From 46c1611621f1e655741760f4f60a8ed4e575435f Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Sun, 15 Jun 2025 07:43:37 -0400 Subject: [PATCH 01/15] Fixed typos Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 74d4f0d..dceab63 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ There are also some additional security features we have implemented: - Content scripts are injected in an isolated world and never modify the DOM. This prevents detection while still allowing access to the DOM. -- Instead of EC6 classes, we use nested functions to create truly +- Instead of ES6 classes, we use nested functions to create truly private methods in our content script. I.e: ```ts @@ -67,12 +67,10 @@ There are also some additional security features we have implemented: })(); ``` - +--> Finally, for transparency's sake, yes, the initial commit added _a lot_. That's because this project had been in the works for a while. This was a rich From d2b0520e99ab02594df24aea7a14ae5900d534f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 08:20:39 +0000 Subject: [PATCH 02/15] Bump eslint-plugin-react-hooks from 4.6.0 to 5.2.0 Bumps [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) from 4.6.0 to 5.2.0. - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/HEAD/packages/eslint-plugin-react-hooks) --- updated-dependencies: - dependency-name: eslint-plugin-react-hooks dependency-version: 5.2.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 11 ++++++----- package.json | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0abc475..47357a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "css-loader": "^7.1.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.0", @@ -3293,15 +3293,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { diff --git a/package.json b/package.json index e65cc6f..5cd6e0f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "css-loader": "^7.1.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.0", From dc03578c7ad8d6169e9096f3d48a5e43d77a2a82 Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:38:28 -0400 Subject: [PATCH 03/15] Removed assert function in favour of node:assert Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- src/pages/shared/index.ts | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/pages/shared/index.ts b/src/pages/shared/index.ts index ef5ac9f..274b671 100644 --- a/src/pages/shared/index.ts +++ b/src/pages/shared/index.ts @@ -3,33 +3,6 @@ */ export const IS_PRODUCTION = process.env.NODE_ENV === 'production'; -/** - * Raised when an assertion fails - * @see {@link assert} - */ -export class AssertionError extends Error { - /** - * @param msg The error message - */ - constructor(msg?: Readonly) { - super(msg ?? 'Assertion failed'); - } -} - -/** - * Throws an {@link AssertionError} if the given expression is falsey - * @param expr - * @param msg - */ -export function assert( - expr: Readonly, - msg?: Readonly -): asserts expr is true { - if (!expr) { - throw new AssertionError(msg); - } -} - /** * Returns a promise that resolves when the given time has passed * @param sec The time to sleep in milliseconds From 265122ee904d535a2dba8323a0fc221086d4fede Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:39:17 -0400 Subject: [PATCH 04/15] Fixed doctype rendering bug Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- src/pages/popup/components/doctype.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/popup/components/doctype.tsx b/src/pages/popup/components/doctype.tsx index d3574a6..decd4fe 100644 --- a/src/pages/popup/components/doctype.tsx +++ b/src/pages/popup/components/doctype.tsx @@ -26,8 +26,8 @@ export function VirtualDoctype(props: Readonly) { if (props.publicId !== '') { xmlId += ` PUBLIC "${props.publicId}"`; - if (props.systemId === '') { - xmlId += ` ${props.systemId}`; + if (props.systemId !== '') { + xmlId += ` "${props.systemId}"`; } } else if (props.systemId !== '') { xmlId += ` SYSTEM "${props.systemId}"`; From 14abe9a4d76062ed895ecfa47db424d2aa542018 Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:06:27 -0400 Subject: [PATCH 05/15] Implemented attribute notes Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- .hintrc | 2 +- README.md | 8 +- TODO.md | 18 ++- src/globals.d.ts | 2 + src/pages/popup/base.tsx | 40 +---- src/pages/popup/childManager.tsx | 74 +++++++-- src/pages/popup/components/attribute.tsx | 29 ++-- src/pages/popup/components/cdataSection.tsx | 9 +- src/pages/popup/components/comment.tsx | 13 +- src/pages/popup/components/doctype.tsx | 9 +- src/pages/popup/components/document.tsx | 3 +- src/pages/popup/components/element.tsx | 19 ++- src/pages/popup/components/text.tsx | 18 +-- src/pages/popup/docListener.ts | 162 +++++++++++++++---- src/pages/popup/msgs.ts | 8 +- src/pages/popup/popup.tsx | 166 ++++++++++++++++---- 16 files changed, 410 insertions(+), 170 deletions(-) diff --git a/.hintrc b/.hintrc index 8f3ae3d..b222dc4 100644 --- a/.hintrc +++ b/.hintrc @@ -1,3 +1,3 @@ { "extends": ["development"] -} \ No newline at end of file +} diff --git a/README.md b/README.md index dceab63..8d3e4b0 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ There are also some additional security features we have implemented: ```ts (async () => { - function _privateMethod() { + function _privateMethod(): void { // Super secret internals } - function publicMethod() { + function publicMethod(): void { // Public API } @@ -171,6 +171,10 @@ the page, such as with ad blocking. ## Caveats +- We can't currently catch every attribute event. Because `MutationObserver`s + run at the microtask level, and because attribute `MutationRecord`s don't + include the new attribute value, we don't yet have a way to get the values + of attributes every time they're updated, only most times. - This extension is subject to the same restrictions as any extension. That means that protected URLs, such as `chrome://`, `edge://`, `chrome-extension://` and `about://`, cannot be inspected. Ironically, this diff --git a/TODO.md b/TODO.md index 07915f3..6172552 100644 --- a/TODO.md +++ b/TODO.md @@ -7,9 +7,12 @@ - Add dropdown functionality to all nodes with children - For accessibility purposes, add the ability to select and navigate between elements in the inspector -- When elements are added, children should be processed -- Fix adding nodes with previous node IDs, as they seem to be rendered - out of order when, e.g, removing a node in DevTools and undoing the action +- When elements are added, children and text should be processed +- Fix duplicate attribute names when adding a second or more attributes + to an element using DevTool's "Edit as HTML" function + (possible race condition) +- Fix adding nodes with previous node IDs + (e.g, removing a node in DevTools and then undoing the action) - Render XML attributes in an XHTML document, e.g: ```xhtml @@ -23,9 +26,6 @@ [docs](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/security/compromised-renderers.md#Messaging)) - Tell the user when a protected page cannot be inspected - Add documentation where it's missing or necessary (e.g, @param or @returns) -- Support attribute mutations - - Have attributes and text referenced through an ID in virtual nodes instead - of storing key-value pairs and regenerating keys by a fixed pattern - Support character data mutations - Finish supporting all applicable node types - Test nested document nodes (see: [SO](https://stackoverflow.com/questions/26010355/is-there-a-way-to-uniquely-identify-an-iframe-that-the-content-script-runs-in-fo)) @@ -34,6 +34,8 @@ ## Technical Priorities +- Add an [error boundary](https://react.dev/link/error-boundaries) + for improved debugging UX - StateManager needs to process multiple messages per render or defer rendering to avoid `Uncaught Error: Too many re-renders. React limits the number of renders` @@ -59,8 +61,8 @@ ## Future Priorities -- Replace shared assert function with node:assert.assert -- Remove redundant information from messages and props +- Configure React source map generation + (`React.createElement` statements are ugly) - Create and add donation medium - Modify ESLint config: diff --git a/src/globals.d.ts b/src/globals.d.ts index 8d28773..1b86d41 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -8,6 +8,8 @@ declare global { // Extends existing type interface Attr { nodeType: Node['ATTRIBUTE_NODE']; + nodeValue: string; + previousSibling: null; } // Extends existing type diff --git a/src/pages/popup/base.tsx b/src/pages/popup/base.tsx index 6991a48..b9b321c 100644 --- a/src/pages/popup/base.tsx +++ b/src/pages/popup/base.tsx @@ -1,52 +1,18 @@ -import React, { ReactNode } from 'react'; -import { VirtualInlineText, VirtualAttribute } from './components'; +import { ReactNode } from 'react'; export interface StoredVirtualNodeProps { nodeType: number; nodeName: string; nodeValue: string | null; - attributes: Record; childNodeIds: string[]; parentId?: string; } -export type NonStoredProps

= VirtualNodeProps & Omit; +export type NonStoredProps

= VirtualNodeProps & + Omit; export interface VirtualNodeProps extends Omit { id: string; children?: ReactNode[]; } - -export function VirtualNode(props: Readonly) { - const attrs = - props.attributes == null - ? '' - : Object.entries(props.attributes).map(([name, value]) => { - const id = `${props.id}-attr-${name}`; - - return ( - - ); - }); - - return ( -

-            {`<${props.nodeName}`}
-            {attrs}>
-            
-            {props.children}
-            {``}
-        
- ); -} diff --git a/src/pages/popup/childManager.tsx b/src/pages/popup/childManager.tsx index 9dfc6d1..9d00005 100644 --- a/src/pages/popup/childManager.tsx +++ b/src/pages/popup/childManager.tsx @@ -1,17 +1,20 @@ import React, { ReactElement, ReactNode } from 'react'; import { StoredVirtualNodeProps } from './base'; import { + StoredVirtualAttributeProps, StoredVirtualCdataSectionProps, StoredVirtualCommentProps, StoredVirtualDoctypeProps, StoredVirtualDocumentProps, StoredVirtualElementProps, StoredVirtualTextProps, + VirtualAttribute, VirtualCdataSection, VirtualComment, VirtualDoctype, VirtualDocument, VirtualElement, + VirtualInlineText, VirtualText } from './components'; @@ -43,6 +46,39 @@ function renderElement( props: Readonly, node: Readonly ): ReactElement { + const attrs = node.attributeIds.keys().map((id) => { + const attrNode = props.nodes[id] as StoredVirtualAttributeProps; + + return ( + + ); + }); + + let renderingChildren: ReactNode[]; + + if (node.childNodeIds.length < 1 && node.nodeValue == null) { + renderingChildren = [...attrs, ' />']; + } else { + renderingChildren = [ + ...attrs, + '>', + , + ...renderChildren(node, props.nodes), + `` + ]; + } + return ( <> {renderDebug(props.id)} @@ -51,14 +87,33 @@ function renderElement( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={node.attributes} > - {renderChildren(node, props.nodes)} + {renderingChildren} ); } +function renderAttribute( + props: Readonly, + node: Readonly +): ReactElement { + return ( + <> + {renderDebug(props.id)} + + {renderChildren(node, props.nodes)} + + + ); +} + function renderText( props: Readonly, node: Readonly @@ -71,7 +126,6 @@ function renderText( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={node.attributes} parentId={node.parentId} prevSiblingId={node.prevSiblingId} /> @@ -91,7 +145,6 @@ function renderCdataSection( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={{}} parentId={node.parentId} prevSiblingId={node.prevSiblingId} /> @@ -111,7 +164,6 @@ function renderComment( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={node.attributes} parentId={node.parentId} prevSiblingId={node.prevSiblingId} /> @@ -131,7 +183,6 @@ function renderDocument( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={node.attributes} documentURI={node.documentURI} > {renderChildren(node, props.nodes)} @@ -152,7 +203,6 @@ function renderDoctype( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - attributes={node.attributes} publicId={node.publicId} systemId={node.systemId} parentId={node.parentId} @@ -161,13 +211,16 @@ function renderDoctype( ); } -export function ChildManager(props: Readonly) { +export function ChildManager(props: Readonly): ReactElement { const node = props.nodes[props.id]; switch (node.nodeType) { case Node.ELEMENT_NODE: { return renderElement(props, node as StoredVirtualElementProps); } + case Node.ATTRIBUTE_NODE: { + return renderAttribute(props, node as StoredVirtualAttributeProps); + } case Node.TEXT_NODE: { return renderText(props, node as StoredVirtualTextProps); } @@ -186,7 +239,6 @@ export function ChildManager(props: Readonly) { case Node.DOCUMENT_TYPE_NODE: { return renderDoctype(props, node as StoredVirtualDoctypeProps); } - case Node.ATTRIBUTE_NODE: case Node.ENTITY_REFERENCE_NODE: case Node.ENTITY_NODE: case Node.PROCESSING_INSTRUCTION_NODE: @@ -196,9 +248,7 @@ export function ChildManager(props: Readonly) { return ( <> {renderDebug(props.id)} -
-                        {`Unsupported node type: ${node.nodeType}`}
-                    
+
{`Unsupported node type: ${node.nodeType}`}
); } diff --git a/src/pages/popup/components/attribute.tsx b/src/pages/popup/components/attribute.tsx index 0caf4d1..011b67d 100644 --- a/src/pages/popup/components/attribute.tsx +++ b/src/pages/popup/components/attribute.tsx @@ -1,14 +1,22 @@ -import React, { Fragment, ReactElement } from 'react'; +import React, { ReactElement } from 'react'; +import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { BaseUpdateMsg } from '../msgs'; -export interface VirtualAttributeProps { +type SharedValues = { id: string; + parentId: string; nodeType: Node['ATTRIBUTE_NODE']; nodeName: string; nodeValue: string | null; - attributes: Record; - childNodeIds?: never[]; - parentId?: string; -} + childNodeIds: never[]; + prevSiblingId?: undefined; +}; + +export type UpdateAttributeMsg = BaseUpdateMsg & SharedValues; + +export type StoredVirtualAttributeProps = StoredVirtualNodeProps & SharedValues; + +export type VirtualAttributeProps = NonStoredProps; /** * The document source virtual element attribute component @@ -17,15 +25,14 @@ export function VirtualAttribute( props: Readonly ): ReactElement { return ( - + <>
{' ' + props.nodeName}
{props.nodeValue == null ? undefined : ( <> -
="
-
{props.nodeValue}
-
"
+ =" +
{props.nodeValue}
" )} -
+ ); } diff --git a/src/pages/popup/components/cdataSection.tsx b/src/pages/popup/components/cdataSection.tsx index d79d2f1..423e367 100644 --- a/src/pages/popup/components/cdataSection.tsx +++ b/src/pages/popup/components/cdataSection.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; @@ -7,7 +7,6 @@ interface SharedValues { nodeType: Node['CDATA_SECTION_NODE']; nodeName: '#cdata-section'; nodeValue: string; - attributes: Record; prevSiblingId?: string; children?: never[]; } @@ -20,9 +19,11 @@ export type StoredVirtualCdataSectionProps = StoredVirtualNodeProps & export type VirtualCdataSectionProps = NonStoredProps; -export function VirtualCdataSection(props: Readonly) { +export function VirtualCdataSection( + props: Readonly +): ReactElement { return ( -
+        
             

{``}

); diff --git a/src/pages/popup/components/comment.tsx b/src/pages/popup/components/comment.tsx index f2bd86c..555f65e 100644 --- a/src/pages/popup/components/comment.tsx +++ b/src/pages/popup/components/comment.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; @@ -7,7 +7,6 @@ interface SharedValues { nodeType: Node['COMMENT_NODE']; nodeName: '#comment'; nodeValue: string; - attributes: Record; prevSiblingId?: string; children?: never[]; } @@ -18,10 +17,8 @@ export type StoredVirtualCommentProps = StoredVirtualNodeProps & SharedValues; export type VirtualCommentProps = NonStoredProps; -export function VirtualComment(props: Readonly) { - return ( -
-            {``}
-        
- ); +export function VirtualComment( + props: Readonly +): ReactElement { + return
{``}
; } diff --git a/src/pages/popup/components/doctype.tsx b/src/pages/popup/components/doctype.tsx index decd4fe..8e44e43 100644 --- a/src/pages/popup/components/doctype.tsx +++ b/src/pages/popup/components/doctype.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactElement } from 'react'; import { NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; @@ -7,7 +7,6 @@ interface SharedValues { nodeType: Node['DOCUMENT_TYPE_NODE']; nodeName: string; nodeValue: null; - attributes: Record; publicId: string; systemId: string; children?: never[]; @@ -20,7 +19,9 @@ export type StoredVirtualDoctypeProps = StoredVirtualNodeProps & export type VirtualDoctypeProps = NonStoredProps; -export function VirtualDoctype(props: Readonly) { +export function VirtualDoctype( + props: Readonly +): ReactElement { let xmlId = ''; if (props.publicId !== '') { @@ -34,7 +35,7 @@ export function VirtualDoctype(props: Readonly) { } return ( -
+        
             {``}
         
); diff --git a/src/pages/popup/components/document.tsx b/src/pages/popup/components/document.tsx index 46339f4..99119e0 100644 --- a/src/pages/popup/components/document.tsx +++ b/src/pages/popup/components/document.tsx @@ -6,7 +6,6 @@ interface SharedValues { nodeType: Node['DOCUMENT_NODE']; nodeName: '#document'; nodeValue: null; - attributes: Record; prevSiblingId?: undefined; documentURI: string; } @@ -21,7 +20,7 @@ export function VirtualDocument(props: VirtualDocumentProps): ReactElement { // For security, don't change the rel attribute // See: https://stackoverflow.com/a/17711167/8387760 return ( -
+        
             {`${props.nodeName} (`}
             ;
 }
 
 export type UpdateElementMsg = BaseUpdateMsg & SharedValues;
 
-export type StoredVirtualElementProps = StoredVirtualNodeProps & SharedValues;
+export type StoredVirtualElementProps = StoredVirtualNodeProps &
+    SharedValues & { attributeIds: Set };
 
 export type VirtualElementProps = NonStoredProps;
 
-export { VirtualNode as VirtualElement };
+export function VirtualElement(
+    props: Readonly
+): ReactElement {
+    // Parts of this component's rendering are handled by the child manager
+    return (
+        
+            {`<${props.nodeName}`}
+            {props.children}
+        
+ ); +} diff --git a/src/pages/popup/components/text.tsx b/src/pages/popup/components/text.tsx index 4de5210..8816d7a 100644 --- a/src/pages/popup/components/text.tsx +++ b/src/pages/popup/components/text.tsx @@ -7,7 +7,6 @@ interface SharedValues { nodeType: Node['TEXT_NODE']; nodeName: '#text'; nodeValue: string; - attributes: Record; prevSiblingId?: string; children?: never[]; } @@ -26,11 +25,7 @@ export function VirtualText(props: Readonly): ReactElement { const hidden = props.nodeValue === ''; return ( -
); } diff --git a/src/pages/popup/components/element.tsx b/src/pages/popup/components/element.tsx index 3a2b07e..9640a3f 100644 --- a/src/pages/popup/components/element.tsx +++ b/src/pages/popup/components/element.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from 'react'; +import React, { ReactNode } from 'react'; import { NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; @@ -15,12 +15,12 @@ export type VirtualElementProps = NonStoredProps; export function VirtualElement( props: Readonly -): ReactElement { +): ReactNode { // Parts of this component's rendering are handled by the child manager return ( -
+        
{`<${props.nodeName}`} {props.children} -
+ ); } diff --git a/src/pages/popup/components/index.tsx b/src/pages/popup/components/index.tsx index ea3aed8..579ddf2 100644 --- a/src/pages/popup/components/index.tsx +++ b/src/pages/popup/components/index.tsx @@ -5,3 +5,4 @@ export * from './doctype'; export * from './document'; export * from './element'; export * from './text'; +export * from './processingInstruction'; diff --git a/src/pages/popup/components/processingInstruction.tsx b/src/pages/popup/components/processingInstruction.tsx new file mode 100644 index 0000000..d950fdb --- /dev/null +++ b/src/pages/popup/components/processingInstruction.tsx @@ -0,0 +1,32 @@ +import React, { ReactNode } from 'react'; +import { BaseUpdateMsg } from '../msgs'; +import { VirtualInlineText } from './text'; +import { NonStoredProps, StoredVirtualNodeProps } from '../base'; + +export interface SharedValues { + parentId: string; + nodeType: Node['PROCESSING_INSTRUCTION_NODE']; + nodeValue: string; + prevSiblingId?: string; + children?: never[]; +} + +export type UpdateProcessingInstructionMsg = BaseUpdateMsg & SharedValues; + +export type StoredVirtualProcessingInstructionProps = StoredVirtualNodeProps & + SharedValues & { childNodeIds: never[] }; + +export type VirtualProcessingInstructionProps = + NonStoredProps; + +export function VirtualProcessingInstruction( + props: VirtualProcessingInstructionProps +): ReactNode { + const data = props.nodeValue === '' ? '' : ' ' + props.nodeValue; + + return ( +
+ `} /> +
+ ); +} diff --git a/src/pages/popup/components/text.tsx b/src/pages/popup/components/text.tsx index 8816d7a..704ed9d 100644 --- a/src/pages/popup/components/text.tsx +++ b/src/pages/popup/components/text.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from 'react'; +import React, { ReactNode } from 'react'; import { NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; @@ -21,22 +21,19 @@ export type VirtualTextProps = NonStoredProps; /** * The document source block virtual text node component */ -export function VirtualText(props: Readonly): ReactElement { +export function VirtualText(props: Readonly): ReactNode { const hidden = props.nodeValue === ''; return ( ); } -// Never persisted, so no ID is necessary +// Key is handled by parent, so no parent ID is necessary +// Node is never persisted, so no ID is necessary export interface VirtualInlineTextProps { - parentId: string; nodeValue: string; } @@ -45,6 +42,6 @@ export interface VirtualInlineTextProps { */ export function VirtualInlineText( props: Readonly -): ReactElement { - return
{props.nodeValue}
; +): ReactNode { + return
{props.nodeValue}
; } diff --git a/src/pages/popup/docListener.ts b/src/pages/popup/docListener.ts index 61e8854..9d91dfe 100644 --- a/src/pages/popup/docListener.ts +++ b/src/pages/popup/docListener.ts @@ -1,6 +1,5 @@ -import { v4 as uuid } from 'uuid'; -import { TIMEOUT_MS } from './background'; -import { +import { UpdateProcessingInstructionMsg } from './components'; +import type { ConnectMsg, PopupMsg, RemoveMsg, @@ -13,56 +12,59 @@ import { UpdateTextMsg } from './msgs'; -interface PartialNodeMutationRecord { - readonly type: 'childList'; - readonly target?: Node | null; - readonly addedNodes: NodeList | Node[]; - readonly removedNodes: NodeList | Node[]; - readonly previousSibling?: Node | null; -} - -interface PartialAttributeMutationRecord { - readonly type: 'attributes'; - readonly target: Node; - readonly attributeName: string; -} - -interface PartialCharacterDataMutationRecord { - readonly type: 'characterData'; - readonly target: Node; -} - -type PartialMutationRecord = - | PartialNodeMutationRecord - | PartialAttributeMutationRecord - | PartialCharacterDataMutationRecord; - -type KnownNode = - | Element - | Attr - | Text - | CDATASection - | EntityReference - | Entity - | ProcessingInstruction - | Comment - | Document - | DocumentType - | DocumentFragment - | NotationNode; - /** * The document source inspector. Communicates document * updates to a popup without invoking DevTools, thereby * circumventing debugger detection. */ (async () => { + const { v4: uuid } = require('uuid'); + const { TIMEOUT_MS } = require('./background'); + + interface PartialNodeMutationRecord { + readonly type: 'childList'; + readonly target?: Node | null; + readonly addedNodes: NodeList | Node[]; + readonly removedNodes: NodeList | Node[]; + readonly previousSibling?: Node | null; + } + + interface PartialAttributeMutationRecord { + readonly type: 'attributes'; + readonly target: Node; + readonly attributeName: string; + } + + interface PartialCharacterDataMutationRecord { + readonly type: 'characterData'; + readonly target: Node; + } + + type PartialMutationRecord = + | PartialNodeMutationRecord + | PartialAttributeMutationRecord + | PartialCharacterDataMutationRecord; + + type KnownNode = + | Element + | Attr + | Text + | CDATASection + | EntityReference + | Entity + | ProcessingInstruction + | Comment + | Document + | DocumentType + | DocumentFragment + | NotationNode; + const ADD_NODE_SUPPORTED_TYPES = new Set>([ Node['ELEMENT_NODE'], // Node['ATTRIBUTE_NODE'], // Should never encounter Node['TEXT_NODE'], Node['CDATA_SECTION_NODE'], - // Node['PROCESSING_INSTRUCTION_NODE'], + Node['PROCESSING_INSTRUCTION_NODE'], Node['COMMENT_NODE'], Node['DOCUMENT_NODE'], Node['DOCUMENT_TYPE_NODE'] @@ -83,8 +85,7 @@ type KnownNode = >(); let _initialDomConstructed = false; const _mutationCache = new Array>(); - let _asyncIndex = 0; - let msgCount = 0; + let _msgIndex = 0; /** * Passes a message to the popup without waiting for a response. @@ -194,7 +195,7 @@ type KnownNode = const id = _getAttrId(ownerElement, attr, attrName); const msg: UpdateAttributeMsg = { type: 'update', - asyncIndex: _asyncIndex++, + msgIndex: _msgIndex++, id, parentId, nodeType: Node.ATTRIBUTE_NODE, @@ -216,7 +217,8 @@ type KnownNode = console.warn( 'Encountered attribute in node removal method:', - attr + attr, + 'Proceed with caution.' ); if (attr.ownerElement != null) { @@ -232,7 +234,7 @@ type KnownNode = const msg: RemoveMsg = { type: 'remove', id, - asyncIndex: _asyncIndex++ + msgIndex: _msgIndex++ }; _sendMessage(msg); @@ -267,9 +269,9 @@ type KnownNode = switch (cNode.nodeType) { case Node.ELEMENT_NODE: { const parentId = _getId(parentNode); - const msg: UpdateElementMsg | UpdateTextMsg = { + const msg: UpdateElementMsg = { type: 'update', - asyncIndex: _asyncIndex++, + msgIndex: _msgIndex++, id, parentId, prevSiblingId, @@ -286,7 +288,7 @@ type KnownNode = parentNode == null ? undefined : _getId(parentNode); const msg: UpdateDocumentMsg = { type: 'update', - asyncIndex: _asyncIndex++, + msgIndex: _msgIndex++, id, parentId, nodeType: cNode.nodeType, @@ -302,7 +304,7 @@ type KnownNode = const parentId = _getId(parentNode); const msg: UpdateDoctypeMsg = { type: 'update', - asyncIndex: _asyncIndex++, + msgIndex: _msgIndex++, id, parentId, prevSiblingId, @@ -318,20 +320,25 @@ type KnownNode = } case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: + case Node.PROCESSING_INSTRUCTION_NODE: case Node.COMMENT_NODE: { // Setting nodeValue to null actually sets it to an empty string const nodeValue = cNode.nodeValue ?? ''; const parentId = _getId(parentNode); const msg = { type: 'update', - asyncIndex: _asyncIndex++, + msgIndex: _msgIndex++, id, parentId, prevSiblingId, nodeType: cNode.nodeType, nodeName: cNode.nodeName, nodeValue - } as UpdateTextMsg | UpdateCdataSectionMsg | UpdateCommentMsg; + } as + | UpdateTextMsg + | UpdateCdataSectionMsg + | UpdateCommentMsg + | UpdateProcessingInstructionMsg; _sendMessage(msg); break; @@ -340,7 +347,9 @@ type KnownNode = // Should never happen console.error( `Skipping addition of unimplemented node ` + - `of type ${node.nodeType}` + `of type ${node.nodeType}; ` + + `any anomalous parent updates associated with id ` + + `${id} are a result of this.` ); } } diff --git a/src/pages/popup/msgs.ts b/src/pages/popup/msgs.ts index 9e642d1..e0927d6 100644 --- a/src/pages/popup/msgs.ts +++ b/src/pages/popup/msgs.ts @@ -5,6 +5,7 @@ import { UpdateDoctypeMsg, UpdateDocumentMsg, UpdateElementMsg, + UpdateProcessingInstructionMsg, UpdateTextMsg } from './components'; @@ -22,10 +23,10 @@ export type PopupMsg = RemoveMsg | UpdateMsg; interface Msg { type: string; - asyncIndex: number; + msgIndex: number; } -export interface ConnectMsg extends Omit { +export interface ConnectMsg extends Omit { type: 'connection'; tabId: number; } @@ -42,7 +43,7 @@ export type UpdateMsg = | UpdateCdataSectionMsg | UpdateEntityRefMsg | UpdateEntityMsg - | UpdateInstructionMsg + | UpdateProcessingInstructionMsg | UpdateCommentMsg | UpdateDocumentMsg | UpdateDoctypeMsg @@ -67,10 +68,6 @@ export interface UpdateEntityMsg extends BaseUpdateMsg { nodeType: Node['ENTITY_NODE']; } -export interface UpdateInstructionMsg extends BaseUpdateMsg { - nodeType: Node['PROCESSING_INSTRUCTION_NODE']; -} - export interface UpdateDocFragmentMsg extends BaseUpdateMsg { nodeType: Node['DOCUMENT_FRAGMENT_NODE']; } diff --git a/src/pages/popup/popup.tsx b/src/pages/popup/popup.tsx index 5eac19e..dbecaac 100644 --- a/src/pages/popup/popup.tsx +++ b/src/pages/popup/popup.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useEffect, useState } from 'react'; +import React, { ReactNode, useEffect, useState } from 'react'; import { StoredVirtualNodeProps } from './base'; import { ChildManager, NodeState } from './childManager'; import { @@ -8,6 +8,7 @@ import { StoredVirtualDoctypeProps, StoredVirtualDocumentProps, StoredVirtualElementProps, + StoredVirtualProcessingInstructionProps, StoredVirtualTextProps } from './components'; import { ConnectMsg, PopupMsg, UpdateMsg } from './msgs'; @@ -42,7 +43,7 @@ function insertMsg( msg: Readonly ): PopupMsg[] { return queue.toSpliced( - binarySearch(queue, msg, (a, b) => b.asyncIndex - a.asyncIndex), + binarySearch(queue, msg, (a, b) => b.msgIndex - a.msgIndex), 0, msg ); @@ -86,7 +87,7 @@ function insertAfterSibling( * * @returns The document source inspection UI component */ -export default function StateManager(): ReactElement { +export default function StateManager(): ReactNode { const [firstRun, setFirstRun] = useState(true); const [tabId, setTabId] = useState(); const [root, setRoot] = useState(); @@ -100,7 +101,7 @@ export default function StateManager(): ReactElement { chrome.runtime.onMessage.addListener(generateDocument); chrome.runtime.sendMessage({} as any); console.log('Popup ready to connect!'); - } else if (queue[0]?.asyncIndex === queueIndex) { + } else if (queue[0]?.msgIndex === queueIndex) { // Sending queued messages // Relies on state 'nodes', so only one message is processed per render const msg = queue[0]; @@ -362,6 +363,7 @@ export default function StateManager(): ReactElement { break; } case Node.CDATA_SECTION_NODE: + case Node.PROCESSING_INSTRUCTION_NODE: case Node.COMMENT_NODE: case Node.DOCUMENT_TYPE_NODE: { const parentId = msg.parentId; @@ -373,6 +375,7 @@ export default function StateManager(): ReactElement { const state: | StoredVirtualCdataSectionProps + | StoredVirtualProcessingInstructionProps | StoredVirtualCommentProps | StoredVirtualDoctypeProps = { ...msg, @@ -384,7 +387,6 @@ export default function StateManager(): ReactElement { } case Node.ENTITY_REFERENCE_NODE: case Node.ENTITY_NODE: - case Node.PROCESSING_INSTRUCTION_NODE: case Node.DOCUMENT_FRAGMENT_NODE: case Node.NOTATION_NODE: default: { From e73e073c3345552a752d805c8f22cfeec96dd41d Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:16:43 -0400 Subject: [PATCH 07/15] Clarified TODOs Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- TODO.md | 31 ++++++++++++------------------- src/pages/popup/index.css | 4 ++++ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/TODO.md b/TODO.md index 4322342..2065151 100644 --- a/TODO.md +++ b/TODO.md @@ -2,24 +2,26 @@ ## Functional Priorities +- StateManager needs to process multiple messages per render + or defer rendering to avoid + `Uncaught Error: Too many re-renders. React limits the number of renders` + `to prevent an infinite loop` on complex websites +- Add Firefox extension support, + see: [signing](https://extensionworkshop.com/documentation/publish/) - Write `README.md` - How it Works section - Add dropdown functionality to all nodes with children -- For accessibility purposes, add the ability - to select and navigate between elements in the inspector - When elements are added, children and text should be processed - Fix duplicate attribute names when adding a second or more attributes to an element using DevTool's "Edit as HTML" function (possible race condition) - Fix adding nodes with previous node IDs (e.g, removing a node in DevTools and then undoing the action) - - For security reasons, validate all messages to ensure no compromisation has occurred within any untrustworthy part of the extension (see: the [issue tracker](https://issuetracker.google.com/issues/311491887) and the referenced [docs](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/security/compromised-renderers.md#Messaging)) -- Tell the user when a protected page cannot be inspected - Add documentation where it's missing or necessary (e.g, @param or @returns) - Support character data mutations - Finish supporting all applicable node types @@ -30,33 +32,25 @@ ## Technical Priorities +- For accessibility purposes, add the ability + to select and navigate between elements in the inspector - Add an [error boundary](https://react.dev/link/error-boundaries) for improved debugging UX -- StateManager needs to process multiple messages per render - or defer rendering to avoid - `Uncaught Error: Too many re-renders. React limits the number of renders` - `to prevent an infinite loop` on complex websites - Move top-level configuration files to their own folder - Migrate ChildManager to a [reducer](https://react.dev/learn/extracting-state-logic-into-a-reducer) for increased maintainability - ChildManager prop drills `nodes`: use a shared context and provider (in ChildManager) to manage deep node state -- Format VS Code editor on save with Prettier - Configure ESLint: - Ensure ESLint config file path is correctly recognized and interpreted + - Replace Prettier formatting with ESLint to resolve rule conflicts - Add chromium-extension-boilerplate as an upstream repo dependency -- Add a light mode: +- Add a light mode toggle - ```css - :root { - color-scheme: light dark; - color: light-dark(); - } - ``` - -## Future Priorities +## Backlog Priorities +- Tell the user when a protected page cannot be inspected - Configure React source map generation (`React.createElement` statements are ugly) - Create and add donation medium @@ -85,7 +79,6 @@ (i.e, script injection or manifest registering) - Support Webpack chunking - Add unit tests and end-to-end tests -- Add Firefox extension support (see: [signing](https://extensionworkshop.com/documentation/publish/)) - [Internationalize](https://developer.chrome.com/docs/extensions/reference/api/i18n) - If project scope allows it, add an options page to allow the content script to be automatically reinjected on page or tab (re)load diff --git a/src/pages/popup/index.css b/src/pages/popup/index.css index a8318f0..72021fa 100644 --- a/src/pages/popup/index.css +++ b/src/pages/popup/index.css @@ -7,6 +7,10 @@ :root { color-scheme: dark; + /* + color-scheme: light dark; + color: light-dark(); + */ overflow: scroll; } From bc91f1c3e8da8e766d955015a05cc1de209489cf Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:44:27 -0400 Subject: [PATCH 08/15] Simplified types Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- src/pages/popup/base.tsx | 15 +++++++++------ src/pages/popup/childManager.tsx | 4 +--- src/pages/popup/components/attribute.tsx | 7 ++++--- src/pages/popup/components/cdataSection.tsx | 6 +++--- src/pages/popup/components/comment.tsx | 7 ++++--- src/pages/popup/components/doctype.tsx | 6 +++--- .../popup/components/processingInstruction.tsx | 6 +++--- src/pages/popup/components/text.tsx | 6 +++--- src/pages/popup/docListener.ts | 3 +-- 9 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/pages/popup/base.tsx b/src/pages/popup/base.tsx index b9b321c..6059c6f 100644 --- a/src/pages/popup/base.tsx +++ b/src/pages/popup/base.tsx @@ -8,11 +8,14 @@ export interface StoredVirtualNodeProps { parentId?: string; } -export type NonStoredProps

= VirtualNodeProps & - Omit; +export interface NoChildren { + childNodeIds: never[]; +} -export interface VirtualNodeProps - extends Omit { +export type NonStoredProps

= Omit< + StoredVirtualNodeProps & P, + 'childNodeIds' | 'attributeIds' +> & { id: string; - children?: ReactNode[]; -} + children?: P extends NoChildren ? never[] : ReactNode[]; +}; diff --git a/src/pages/popup/childManager.tsx b/src/pages/popup/childManager.tsx index 9822f87..73ab07a 100644 --- a/src/pages/popup/childManager.tsx +++ b/src/pages/popup/childManager.tsx @@ -108,9 +108,7 @@ function renderAttribute( nodeType={node.nodeType} nodeName={node.nodeName} nodeValue={node.nodeValue} - > - {renderChildren(node, props.nodes)} - + /> ); } diff --git a/src/pages/popup/components/attribute.tsx b/src/pages/popup/components/attribute.tsx index 7c7778d..9665724 100644 --- a/src/pages/popup/components/attribute.tsx +++ b/src/pages/popup/components/attribute.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; type SharedValues = { @@ -8,13 +8,14 @@ type SharedValues = { nodeType: Node['ATTRIBUTE_NODE']; nodeName: string; nodeValue: string | null; - childNodeIds: never[]; prevSiblingId?: undefined; }; export type UpdateAttributeMsg = BaseUpdateMsg & SharedValues; -export type StoredVirtualAttributeProps = StoredVirtualNodeProps & SharedValues; +export type StoredVirtualAttributeProps = StoredVirtualNodeProps & + SharedValues & + NoChildren; export type VirtualAttributeProps = NonStoredProps; diff --git a/src/pages/popup/components/cdataSection.tsx b/src/pages/popup/components/cdataSection.tsx index ca02935..815adee 100644 --- a/src/pages/popup/components/cdataSection.tsx +++ b/src/pages/popup/components/cdataSection.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; import { VirtualInlineText } from './text'; @@ -9,13 +9,13 @@ interface SharedValues { nodeName: '#cdata-section'; nodeValue: string; prevSiblingId?: string; - children?: never[]; } export type UpdateCdataSectionMsg = BaseUpdateMsg & SharedValues; export type StoredVirtualCdataSectionProps = StoredVirtualNodeProps & - SharedValues; + SharedValues & + NoChildren; export type VirtualCdataSectionProps = NonStoredProps; diff --git a/src/pages/popup/components/comment.tsx b/src/pages/popup/components/comment.tsx index a56025b..b42f8c2 100644 --- a/src/pages/popup/components/comment.tsx +++ b/src/pages/popup/components/comment.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; interface SharedValues { @@ -8,12 +8,13 @@ interface SharedValues { nodeName: '#comment'; nodeValue: string; prevSiblingId?: string; - children?: never[]; } export type UpdateCommentMsg = BaseUpdateMsg & SharedValues; -export type StoredVirtualCommentProps = StoredVirtualNodeProps & SharedValues; +export type StoredVirtualCommentProps = StoredVirtualNodeProps & + SharedValues & + NoChildren; export type VirtualCommentProps = NonStoredProps; diff --git a/src/pages/popup/components/doctype.tsx b/src/pages/popup/components/doctype.tsx index c2a4965..71c3244 100644 --- a/src/pages/popup/components/doctype.tsx +++ b/src/pages/popup/components/doctype.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; interface SharedValues { @@ -9,13 +9,13 @@ interface SharedValues { nodeValue: null; publicId: string; systemId: string; - children?: never[]; } export type UpdateDoctypeMsg = BaseUpdateMsg & SharedValues; export type StoredVirtualDoctypeProps = StoredVirtualNodeProps & - SharedValues & { childNodeIds: never[] }; + SharedValues & + NoChildren; export type VirtualDoctypeProps = NonStoredProps; diff --git a/src/pages/popup/components/processingInstruction.tsx b/src/pages/popup/components/processingInstruction.tsx index d950fdb..187277d 100644 --- a/src/pages/popup/components/processingInstruction.tsx +++ b/src/pages/popup/components/processingInstruction.tsx @@ -1,20 +1,20 @@ import React, { ReactNode } from 'react'; import { BaseUpdateMsg } from '../msgs'; import { VirtualInlineText } from './text'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; export interface SharedValues { parentId: string; nodeType: Node['PROCESSING_INSTRUCTION_NODE']; nodeValue: string; prevSiblingId?: string; - children?: never[]; } export type UpdateProcessingInstructionMsg = BaseUpdateMsg & SharedValues; export type StoredVirtualProcessingInstructionProps = StoredVirtualNodeProps & - SharedValues & { childNodeIds: never[] }; + SharedValues & + NoChildren; export type VirtualProcessingInstructionProps = NonStoredProps; diff --git a/src/pages/popup/components/text.tsx b/src/pages/popup/components/text.tsx index 704ed9d..5f1ac8c 100644 --- a/src/pages/popup/components/text.tsx +++ b/src/pages/popup/components/text.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { NonStoredProps, StoredVirtualNodeProps } from '../base'; +import { NoChildren, NonStoredProps, StoredVirtualNodeProps } from '../base'; import { BaseUpdateMsg } from '../msgs'; interface SharedValues { @@ -8,13 +8,13 @@ interface SharedValues { nodeName: '#text'; nodeValue: string; prevSiblingId?: string; - children?: never[]; } export type UpdateTextMsg = BaseUpdateMsg & SharedValues; export type StoredVirtualTextProps = StoredVirtualNodeProps & - SharedValues & { childNodeIds: never[] }; + SharedValues & + NoChildren; export type VirtualTextProps = NonStoredProps; diff --git a/src/pages/popup/docListener.ts b/src/pages/popup/docListener.ts index 9d91dfe..61e9684 100644 --- a/src/pages/popup/docListener.ts +++ b/src/pages/popup/docListener.ts @@ -200,8 +200,7 @@ import type { parentId, nodeType: Node.ATTRIBUTE_NODE, nodeName: attrName.toLowerCase(), - nodeValue: attr?.nodeValue ?? null, - childNodeIds: [] + nodeValue: attr?.nodeValue ?? null }; _sendMessage(msg); From 1f23c5aa10006590c677116a91a4ba7a94283f7a Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:50:41 -0400 Subject: [PATCH 09/15] Migrated away from prop drilling and towards context Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- TODO.md | 3 +- src/pages/popup/childManager.tsx | 109 ++++++++++++++----------------- src/pages/popup/index.tsx | 4 +- src/pages/popup/popup.tsx | 20 ++++-- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/TODO.md b/TODO.md index 2065151..d522036 100644 --- a/TODO.md +++ b/TODO.md @@ -22,7 +22,7 @@ (see: the [issue tracker](https://issuetracker.google.com/issues/311491887) and the referenced [docs](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/security/compromised-renderers.md#Messaging)) -- Add documentation where it's missing or necessary (e.g, @param or @returns) +- Add documentation where it's missing or necessary (e.g, @param or @template) - Support character data mutations - Finish supporting all applicable node types - Test nested document nodes (see: [SO](https://stackoverflow.com/questions/26010355/is-there-a-way-to-uniquely-identify-an-iframe-that-the-content-script-runs-in-fo)) @@ -45,7 +45,6 @@ - Configure ESLint: - Ensure ESLint config file path is correctly recognized and interpreted - Replace Prettier formatting with ESLint to resolve rule conflicts -- Add chromium-extension-boilerplate as an upstream repo dependency - Add a light mode toggle ## Backlog Priorities diff --git a/src/pages/popup/childManager.tsx b/src/pages/popup/childManager.tsx index 73ab07a..e616508 100644 --- a/src/pages/popup/childManager.tsx +++ b/src/pages/popup/childManager.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, useContext } from 'react'; import { StoredVirtualNodeProps } from './base'; import { StoredVirtualAttributeProps, @@ -19,13 +19,7 @@ import { VirtualProcessingInstruction, VirtualText } from './components'; - -export type NodeState = { [id: string]: StoredVirtualNodeProps }; - -interface ChildManagerProps { - id: string; - nodes: NodeState; -} +import { NodeContext, NodeState } from './popup'; function renderDebug(id: Readonly): ReactNode { return ( @@ -35,27 +29,23 @@ function renderDebug(id: Readonly): ReactNode { ); } -function renderChildren( - node: Readonly, - nodes: Readonly -): ReactNode[] { - return node.childNodeIds.map((id) => ( - - )); +function renderChildren(node: Readonly): ReactNode[] { + return node.childNodeIds.map((id) => ); } function renderElement( - props: Readonly, - node: Readonly + id: Readonly, + node: Readonly, + nodes: Readonly ): ReactNode { - const attrs = node.attributeIds.keys().map((id) => { - const attrNode = props.nodes[id] as StoredVirtualAttributeProps; + const attrs = node.attributeIds.keys().map((attrId) => { + const attrNode = nodes[attrId] as StoredVirtualAttributeProps; return ( ', , - ...renderChildren(node, props.nodes), + ...renderChildren(node), `` ]; } return ( <> - {renderDebug(props.id)} + {renderDebug(id)} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {/* {renderDebug(id)} */} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} , + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} - {renderChildren(node, props.nodes)} + {renderChildren(node)} ); } function renderDoctype( - props: Readonly, + id: Readonly, node: Readonly ): ReactNode { return ( <> - {renderDebug(props.id)} + {renderDebug(id)} ): ReactNode { - const node = props.nodes[props.id]; +export function ChildManager({ id }: { readonly id: string }): ReactNode { + const nodes = useContext(NodeContext); + const node = nodes[id]; switch (node.nodeType) { case Node.ELEMENT_NODE: { - return renderElement(props, node as StoredVirtualElementProps); + return renderElement(id, node as StoredVirtualElementProps, nodes); } case Node.ATTRIBUTE_NODE: { - return renderAttribute(props, node as StoredVirtualAttributeProps); + return renderAttribute(id, node as StoredVirtualAttributeProps); } case Node.TEXT_NODE: { - return renderText(props, node as StoredVirtualTextProps); + return renderText(id, node as StoredVirtualTextProps); } case Node.CDATA_SECTION_NODE: { return renderCdataSection( - props, + id, node as StoredVirtualCdataSectionProps ); } case Node.PROCESSING_INSTRUCTION_NODE: { return renderProcessingInstruction( - props, + id, node as StoredVirtualProcessingInstructionProps ); } case Node.COMMENT_NODE: { - return renderComment(props, node as StoredVirtualCommentProps); + return renderComment(id, node as StoredVirtualCommentProps); } case Node.DOCUMENT_NODE: { - return renderDocument(props, node as StoredVirtualDocumentProps); + return renderDocument(id, node as StoredVirtualDocumentProps); } case Node.DOCUMENT_TYPE_NODE: { - return renderDoctype(props, node as StoredVirtualDoctypeProps); + return renderDoctype(id, node as StoredVirtualDoctypeProps); } case Node.ENTITY_REFERENCE_NODE: case Node.ENTITY_NODE: @@ -270,7 +261,7 @@ export function ChildManager(props: Readonly): ReactNode { default: { return ( <> - {renderDebug(props.id)} + {renderDebug(id)}

{`Unsupported node type: ${node.nodeType}`}
diff --git a/src/pages/popup/index.tsx b/src/pages/popup/index.tsx index 9560add..7100cc9 100644 --- a/src/pages/popup/index.tsx +++ b/src/pages/popup/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; -import App from './popup'; +import Popup from './popup'; // CSS import './index.css'; @@ -10,5 +10,5 @@ const container = document.getElementById('app-container'); if (container != null) { const root = createRoot(container); - root.render(); + root.render(); } diff --git a/src/pages/popup/popup.tsx b/src/pages/popup/popup.tsx index dbecaac..b251241 100644 --- a/src/pages/popup/popup.tsx +++ b/src/pages/popup/popup.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode, useEffect, useState } from 'react'; +import React, { createContext, ReactNode, useEffect, useState } from 'react'; import { StoredVirtualNodeProps } from './base'; -import { ChildManager, NodeState } from './childManager'; +import { ChildManager } from './childManager'; import { StoredVirtualAttributeProps, StoredVirtualCdataSectionProps, @@ -13,6 +13,10 @@ import { } from './components'; import { ConnectMsg, PopupMsg, UpdateMsg } from './msgs'; +export type NodeState = { [id: string]: StoredVirtualNodeProps }; + +export const NodeContext = createContext({}); + function binarySearch( arr: Readonly, e: Readonly, @@ -203,7 +207,6 @@ export default function StateManager(): ReactNode { id: Readonly, parentId: Readonly ): void { - // If the attribute already exists, it's updated accordingly setNodes((prevNodes) => { const prevParentState = prevNodes[ parentId @@ -404,7 +407,7 @@ export default function StateManager(): ReactNode { * @param msg */ function queueMessage(msg: Readonly): void { - setQueue((queue) => insertMsg(queue, msg)); + setQueue((q) => insertMsg(q, msg)); } /** @@ -457,8 +460,11 @@ export default function StateManager(): ReactNode { } // Rendering popup - const childNodes = - root == null ? undefined : ; + const childNodes = root == null ? undefined : ; - return
{childNodes}
; + return ( +
+ {childNodes} +
+ ); } From 6b30dfaef088023612378064e7572a24ebd7baa3 Mon Sep 17 00:00:00 2001 From: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:36:49 -0400 Subject: [PATCH 10/15] Updated TODOs Signed-off-by: Anonymous-Humanoid <68610963+Anonymous-Humanoid@users.noreply.github.com> --- TODO.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/TODO.md b/TODO.md index d522036..58b352d 100644 --- a/TODO.md +++ b/TODO.md @@ -2,8 +2,8 @@ ## Functional Priorities -- StateManager needs to process multiple messages per render - or defer rendering to avoid +- StateManager needs to process multiple messages per render, defer rendering, + or decouple the renderer (`setState`) to avoid `Uncaught Error: Too many re-renders. React limits the number of renders` `to prevent an infinite loop` on complex websites - Add Firefox extension support, @@ -28,11 +28,13 @@ - Test nested document nodes (see: [SO](https://stackoverflow.com/questions/26010355/is-there-a-way-to-uniquely-identify-an-iframe-that-the-content-script-runs-in-fo)) - Shadow roots can be accessed using [`chrome.dom.openOrClosedShadowRoot`](https://developer.chrome.com/docs/extensions/reference/api/dom?hl=en#method-openOrClosedShadowRoot) -- [Validate](https://validator.w3.org) all (X)HTML pages conform to standards +- [Validate](https://validator.w3.org) sample inspector pages + conform to a11y standards ## Technical Priorities -- For accessibility purposes, add the ability +- Rethink debug view (possibly a test-id field?) +- For a11y purposes, add the ability to select and navigate between elements in the inspector - Add an [error boundary](https://react.dev/link/error-boundaries) for improved debugging UX @@ -40,8 +42,6 @@ - Migrate ChildManager to a [reducer](https://react.dev/learn/extracting-state-logic-into-a-reducer) for increased maintainability -- ChildManager prop drills `nodes`: - use a shared context and provider (in ChildManager) to manage deep node state - Configure ESLint: - Ensure ESLint config file path is correctly recognized and interpreted - Replace Prettier formatting with ESLint to resolve rule conflicts @@ -82,6 +82,5 @@ - If project scope allows it, add an options page to allow the content script to be automatically reinjected on page or tab (re)load - If project scope allows it, add a network request viewer -- If the project scope allows it, install the dependencies necessary for Webhint - and its .hintrc config file and resolve any vulnerabilities -- If the project scope allows it, add more accessibility customization +- If the project scope allows it, install the dependencies necessary + for Webhint and its .hintrc config file and resolve any vulnerabilities From c22310630611bdbf389c1ee94fc32e5c1a36379c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:08:52 +0000 Subject: [PATCH 11/15] Bump on-headers and compression Bumps [on-headers](https://github.com/jshttp/on-headers) and [compression](https://github.com/expressjs/compression). These dependencies needed to be updated together. Updates `on-headers` from 1.0.2 to 1.1.0 - [Release notes](https://github.com/jshttp/on-headers/releases) - [Changelog](https://github.com/jshttp/on-headers/blob/master/HISTORY.md) - [Commits](https://github.com/jshttp/on-headers/compare/v1.0.2...v1.1.0) Updates `compression` from 1.7.4 to 1.8.1 - [Release notes](https://github.com/expressjs/compression/releases) - [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/compression/compare/1.7.4...v1.8.1) --- updated-dependencies: - dependency-name: on-headers dependency-version: 1.1.0 dependency-type: indirect - dependency-name: compression dependency-version: 1.8.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 65 +++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0abc475..3764ea4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1515,16 +1515,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1659,10 +1649,11 @@ } }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2123,17 +2114,18 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { @@ -2155,11 +2147,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -6960,10 +6956,11 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7674,16 +7671,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", From ead6200da1dc429e2be5d96c9abf9a929dadec9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:00:42 +0000 Subject: [PATCH 12/15] Bump form-data from 4.0.3 to 4.0.4 Bumps [form-data](https://github.com/form-data/form-data) from 4.0.3 to 4.0.4. - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v4.0.3...v4.0.4) --- updated-dependencies: - dependency-name: form-data dependency-version: 4.0.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3764ea4..d983b71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "async-mutex": "^0.5.0", - "form-data": "^4.0.3", + "form-data": "^4.0.4", "https": "^1.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -3886,9 +3886,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", diff --git a/package.json b/package.json index e65cc6f..478c8a7 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "async-mutex": "^0.5.0", - "form-data": "^4.0.3", + "form-data": "^4.0.4", "https": "^1.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", From b4f9a21094fea789537c35fe60d7a51d06753964 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 07:49:31 +0000 Subject: [PATCH 13/15] Bump html-webpack-plugin from 5.6.0 to 5.6.4 Bumps [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) from 5.6.0 to 5.6.4. - [Release notes](https://github.com/jantimon/html-webpack-plugin/releases) - [Changelog](https://github.com/jantimon/html-webpack-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/jantimon/html-webpack-plugin/compare/v5.6.0...v5.6.4) --- updated-dependencies: - dependency-name: html-webpack-plugin dependency-version: 5.6.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d983b71..95ad5e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", - "html-webpack-plugin": "^5.5.0", + "html-webpack-plugin": "^5.6.4", "nodemon": "^3.1.9", "prettier": "^2.8.3", "sass": "^1.57.1", @@ -4556,10 +4556,11 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.4.tgz", + "integrity": "sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==", "dev": true, + "license": "MIT", "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", diff --git a/package.json b/package.json index 478c8a7..0a35028 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", - "html-webpack-plugin": "^5.5.0", + "html-webpack-plugin": "^5.6.4", "nodemon": "^3.1.9", "prettier": "^2.8.3", "sass": "^1.57.1", From ba09550469c0cd28eb721289b75b4fd7362ab8ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 07:19:38 +0000 Subject: [PATCH 14/15] Bump type-fest from 3.5.2 to 5.0.1 Bumps [type-fest](https://github.com/sindresorhus/type-fest) from 3.5.2 to 5.0.1. - [Release notes](https://github.com/sindresorhus/type-fest/releases) - [Commits](https://github.com/sindresorhus/type-fest/compare/v3.5.2...v5.0.1) --- updated-dependencies: - dependency-name: type-fest dependency-version: 5.0.1 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 27 ++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95ad5e8..15c3fa1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "terser-webpack-plugin": "^5.3.14", "ts-loader": "^9.5.2", "ts-node": "^10.9.2", - "type-fest": "^3.5.2", + "type-fest": "^5.0.1", "typescript": "^5.8.3", "webpack": "^5.98.0", "webpack-cli": "^4.10.0", @@ -9287,6 +9287,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -9730,12 +9743,16 @@ } }, "node_modules/type-fest": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", - "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.0.1.tgz", + "integrity": "sha512-9MpwAI52m8H6ssA542UxSLnSiSD2dsC3/L85g6hVubLSXd82wdI80eZwTWhdOfN67NlA+D+oipAs1MlcTcu3KA==", "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, "engines": { - "node": ">=14.16" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 0a35028..f3b0e3f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "terser-webpack-plugin": "^5.3.14", "ts-loader": "^9.5.2", "ts-node": "^10.9.2", - "type-fest": "^3.5.2", + "type-fest": "^5.0.1", "typescript": "^5.8.3", "webpack": "^5.98.0", "webpack-cli": "^4.10.0", From 46ead1fb69fd0734ce1e3807a9f957a5298c5942 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 08:20:39 +0000 Subject: [PATCH 15/15] Bump eslint-plugin-react-hooks from 4.6.0 to 5.2.0 Bumps [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) from 4.6.0 to 5.2.0. - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/HEAD/packages/eslint-plugin-react-hooks) --- updated-dependencies: - dependency-name: eslint-plugin-react-hooks dependency-version: 5.2.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 11 ++++++----- package.json | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15c3fa1..baa7ffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "css-loader": "^7.1.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.6.4", @@ -3289,15 +3289,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { diff --git a/package.json b/package.json index f3b0e3f..d233403 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "css-loader": "^7.1.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "generate-json-webpack-plugin": "^2.0.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.6.4",