playwriter 0.0.25 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin.js +1 -1
- package/dist/bippy.js +966 -0
- package/dist/{extension/cdp-relay.d.ts → cdp-relay.d.ts} +3 -2
- package/dist/cdp-relay.d.ts.map +1 -0
- package/dist/{extension/cdp-relay.js → cdp-relay.js} +101 -3
- package/dist/cdp-relay.js.map +1 -0
- package/dist/cdp-session.d.ts +1 -1
- package/dist/cdp-session.d.ts.map +1 -1
- package/dist/cdp-session.js +4 -4
- package/dist/cdp-session.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +71 -0
- package/dist/cli.js.map +1 -0
- package/dist/create-logger.d.ts.map +1 -1
- package/dist/create-logger.js +2 -1
- package/dist/create-logger.js.map +1 -1
- package/dist/debugger-examples-types.d.ts +18 -0
- package/dist/debugger-examples-types.d.ts.map +1 -0
- package/dist/debugger-examples-types.js +2 -0
- package/dist/debugger-examples-types.js.map +1 -0
- package/dist/debugger-examples.d.ts +6 -0
- package/dist/debugger-examples.d.ts.map +1 -0
- package/dist/debugger-examples.js +53 -0
- package/dist/debugger-examples.js.map +1 -0
- package/dist/debugger-examples.ts +66 -0
- package/dist/debugger.d.ts +380 -0
- package/dist/debugger.d.ts.map +1 -0
- package/dist/debugger.js +631 -0
- package/dist/debugger.js.map +1 -0
- package/dist/editor-examples.d.ts +11 -0
- package/dist/editor-examples.d.ts.map +1 -0
- package/dist/editor-examples.js +124 -0
- package/dist/editor-examples.js.map +1 -0
- package/dist/editor.d.ts +203 -0
- package/dist/editor.d.ts.map +1 -0
- package/dist/editor.js +335 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-client.d.ts +5 -1
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +13 -9
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp.d.ts +4 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +170 -27
- package/dist/mcp.js.map +1 -1
- package/dist/mcp.test.d.ts.map +1 -1
- package/dist/mcp.test.js +886 -182
- package/dist/mcp.test.js.map +1 -1
- package/dist/prompt.md +86 -6
- package/dist/{extension/protocol.d.ts → protocol.d.ts} +1 -1
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js.map +1 -0
- package/dist/react-source.d.ts +13 -0
- package/dist/react-source.d.ts.map +1 -0
- package/dist/react-source.js +66 -0
- package/dist/react-source.js.map +1 -0
- package/dist/selector-generator.js +7065 -18
- package/dist/start-relay-server.d.ts +4 -2
- package/dist/start-relay-server.d.ts.map +1 -1
- package/dist/start-relay-server.js +3 -3
- package/dist/start-relay-server.js.map +1 -1
- package/dist/styles.d.ts +27 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +232 -0
- package/dist/styles.js.map +1 -0
- package/dist/utils.d.ts +3 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +7 -3
- package/dist/utils.js.map +1 -1
- package/dist/wait-for-page-load.d.ts.map +1 -1
- package/dist/wait-for-page-load.js +3 -2
- package/dist/wait-for-page-load.js.map +1 -1
- package/package.json +5 -2
- package/src/{extension/cdp-relay.ts → cdp-relay.ts} +109 -5
- package/src/cdp-session.ts +4 -4
- package/src/cdp-timing.md +128 -0
- package/src/cli.ts +85 -0
- package/src/create-logger.ts +2 -1
- package/src/debugger-examples-types.ts +10 -0
- package/src/debugger-examples.ts +66 -0
- package/src/debugger.ts +711 -0
- package/src/editor-examples.ts +148 -0
- package/src/editor.ts +389 -0
- package/src/index.ts +1 -1
- package/src/mcp-client.ts +14 -9
- package/src/mcp.test.ts +1053 -196
- package/src/mcp.ts +195 -30
- package/src/prompt.md +86 -6
- package/src/{extension/protocol.ts → protocol.ts} +1 -1
- package/src/react-source.ts +92 -0
- package/src/snapshots/shadcn-ui-accessibility.md +57 -57
- package/src/start-relay-server.ts +3 -3
- package/src/styles.ts +343 -0
- package/src/utils.ts +8 -3
- package/src/wait-for-page-load.ts +3 -2
- package/dist/extension/cdp-relay.d.ts.map +0 -1
- package/dist/extension/cdp-relay.js.map +0 -1
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js.map +0 -1
- /package/dist/{extension/protocol.js → protocol.js} +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { page, getCDPSession, createEditor, console } from './debugger-examples-types.js';
|
|
2
|
+
// Example: List available scripts
|
|
3
|
+
async function listScripts() {
|
|
4
|
+
const cdp = await getCDPSession({ page });
|
|
5
|
+
const editor = createEditor({ cdp });
|
|
6
|
+
await editor.enable();
|
|
7
|
+
const scripts = editor.list({ pattern: /app/ });
|
|
8
|
+
console.log(scripts);
|
|
9
|
+
}
|
|
10
|
+
// Example: Read a script with line numbers
|
|
11
|
+
async function readScript() {
|
|
12
|
+
const cdp = await getCDPSession({ page });
|
|
13
|
+
const editor = createEditor({ cdp });
|
|
14
|
+
await editor.enable();
|
|
15
|
+
const { content, totalLines } = await editor.read({
|
|
16
|
+
url: 'https://example.com/app.js',
|
|
17
|
+
});
|
|
18
|
+
console.log('Total lines:', totalLines);
|
|
19
|
+
console.log(content);
|
|
20
|
+
const { content: partial } = await editor.read({
|
|
21
|
+
url: 'https://example.com/app.js',
|
|
22
|
+
offset: 100,
|
|
23
|
+
limit: 50,
|
|
24
|
+
});
|
|
25
|
+
console.log(partial);
|
|
26
|
+
}
|
|
27
|
+
// Example: Edit a script (exact string replacement)
|
|
28
|
+
async function editScript() {
|
|
29
|
+
const cdp = await getCDPSession({ page });
|
|
30
|
+
const editor = createEditor({ cdp });
|
|
31
|
+
await editor.enable();
|
|
32
|
+
await editor.edit({
|
|
33
|
+
url: 'https://example.com/app.js',
|
|
34
|
+
oldString: 'const DEBUG = false',
|
|
35
|
+
newString: 'const DEBUG = true',
|
|
36
|
+
});
|
|
37
|
+
const dryRunResult = await editor.edit({
|
|
38
|
+
url: 'https://example.com/app.js',
|
|
39
|
+
oldString: 'old code',
|
|
40
|
+
newString: 'new code',
|
|
41
|
+
dryRun: true,
|
|
42
|
+
});
|
|
43
|
+
console.log('Dry run result:', dryRunResult);
|
|
44
|
+
}
|
|
45
|
+
// Example: Search across all scripts
|
|
46
|
+
async function searchScripts() {
|
|
47
|
+
const cdp = await getCDPSession({ page });
|
|
48
|
+
const editor = createEditor({ cdp });
|
|
49
|
+
await editor.enable();
|
|
50
|
+
const matches = await editor.grep({ regex: /console\.log/ });
|
|
51
|
+
console.log(matches);
|
|
52
|
+
const todoMatches = await editor.grep({
|
|
53
|
+
regex: /TODO|FIXME/i,
|
|
54
|
+
pattern: /app/,
|
|
55
|
+
});
|
|
56
|
+
console.log(todoMatches);
|
|
57
|
+
}
|
|
58
|
+
// Example: Write entire script content
|
|
59
|
+
async function writeScript() {
|
|
60
|
+
const cdp = await getCDPSession({ page });
|
|
61
|
+
const editor = createEditor({ cdp });
|
|
62
|
+
await editor.enable();
|
|
63
|
+
const { content } = await editor.read({ url: 'https://example.com/app.js' });
|
|
64
|
+
const newContent = content.replace(/console\.log/g, 'console.debug');
|
|
65
|
+
await editor.write({
|
|
66
|
+
url: 'https://example.com/app.js',
|
|
67
|
+
content: newContent,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Example: Edit an inline script (scripts without URL get inline://{id} URLs)
|
|
71
|
+
async function editInlineScript() {
|
|
72
|
+
const cdp = await getCDPSession({ page });
|
|
73
|
+
const editor = createEditor({ cdp });
|
|
74
|
+
await editor.enable();
|
|
75
|
+
const matches = await editor.grep({ regex: /myFunction/ });
|
|
76
|
+
if (matches.length > 0) {
|
|
77
|
+
const { url } = matches[0];
|
|
78
|
+
console.log('Found in:', url);
|
|
79
|
+
await editor.edit({
|
|
80
|
+
url,
|
|
81
|
+
oldString: 'return false',
|
|
82
|
+
newString: 'return true',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Example: List and read CSS stylesheets
|
|
87
|
+
async function readStylesheet() {
|
|
88
|
+
const cdp = await getCDPSession({ page });
|
|
89
|
+
const editor = createEditor({ cdp });
|
|
90
|
+
await editor.enable();
|
|
91
|
+
const stylesheets = await editor.list({ pattern: /\.css/ });
|
|
92
|
+
console.log('Stylesheets:', stylesheets);
|
|
93
|
+
if (stylesheets.length > 0) {
|
|
94
|
+
const { content, totalLines } = await editor.read({
|
|
95
|
+
url: stylesheets[0],
|
|
96
|
+
});
|
|
97
|
+
console.log('Total lines:', totalLines);
|
|
98
|
+
console.log(content);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Example: Edit a CSS stylesheet
|
|
102
|
+
async function editStylesheet() {
|
|
103
|
+
const cdp = await getCDPSession({ page });
|
|
104
|
+
const editor = createEditor({ cdp });
|
|
105
|
+
await editor.enable();
|
|
106
|
+
await editor.edit({
|
|
107
|
+
url: 'https://example.com/styles.css',
|
|
108
|
+
oldString: 'color: red',
|
|
109
|
+
newString: 'color: blue',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Example: Search CSS for specific properties
|
|
113
|
+
async function searchStyles() {
|
|
114
|
+
const cdp = await getCDPSession({ page });
|
|
115
|
+
const editor = createEditor({ cdp });
|
|
116
|
+
await editor.enable();
|
|
117
|
+
const matches = await editor.grep({
|
|
118
|
+
regex: /background-color/,
|
|
119
|
+
pattern: /\.css/,
|
|
120
|
+
});
|
|
121
|
+
console.log(matches);
|
|
122
|
+
}
|
|
123
|
+
export { listScripts, readScript, editScript, searchScripts, writeScript, editInlineScript, readStylesheet, editStylesheet, searchStyles };
|
|
124
|
+
//# sourceMappingURL=editor-examples.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor-examples.js","sourceRoot":"","sources":["../src/editor-examples.ts"],"names":[],"mappings":"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"}
|
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { CDPSession } from './cdp-session.js';
|
|
2
|
+
export interface ReadResult {
|
|
3
|
+
content: string;
|
|
4
|
+
totalLines: number;
|
|
5
|
+
startLine: number;
|
|
6
|
+
endLine: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SearchMatch {
|
|
9
|
+
url: string;
|
|
10
|
+
lineNumber: number;
|
|
11
|
+
lineContent: string;
|
|
12
|
+
}
|
|
13
|
+
export interface EditResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
stackChanged?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A class for viewing and editing web page scripts via Chrome DevTools Protocol.
|
|
19
|
+
* Provides a Claude Code-like interface: list, read, edit, grep.
|
|
20
|
+
*
|
|
21
|
+
* Edits are in-memory only and persist until page reload. They modify the running
|
|
22
|
+
* V8 instance but are not saved to disk or server.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const cdp = await getCDPSession({ page })
|
|
27
|
+
* const editor = new Editor({ cdp })
|
|
28
|
+
* await editor.enable()
|
|
29
|
+
*
|
|
30
|
+
* // List available scripts
|
|
31
|
+
* const scripts = editor.list({ search: 'app' })
|
|
32
|
+
*
|
|
33
|
+
* // Read a script
|
|
34
|
+
* const { content } = await editor.read({ url: 'https://example.com/app.js' })
|
|
35
|
+
*
|
|
36
|
+
* // Edit a script
|
|
37
|
+
* await editor.edit({
|
|
38
|
+
* url: 'https://example.com/app.js',
|
|
39
|
+
* oldString: 'console.log("old")',
|
|
40
|
+
* newString: 'console.log("new")'
|
|
41
|
+
* })
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare class Editor {
|
|
45
|
+
private cdp;
|
|
46
|
+
private enabled;
|
|
47
|
+
private scripts;
|
|
48
|
+
private stylesheets;
|
|
49
|
+
private sourceCache;
|
|
50
|
+
constructor({ cdp }: {
|
|
51
|
+
cdp: CDPSession;
|
|
52
|
+
});
|
|
53
|
+
private setupEventListeners;
|
|
54
|
+
/**
|
|
55
|
+
* Enables the editor. Must be called before other methods.
|
|
56
|
+
* Scripts are collected from Debugger.scriptParsed events.
|
|
57
|
+
* Reload the page after enabling to capture all scripts.
|
|
58
|
+
*/
|
|
59
|
+
enable(): Promise<void>;
|
|
60
|
+
private getIdByUrl;
|
|
61
|
+
/**
|
|
62
|
+
* Lists available script and stylesheet URLs. Use pattern to filter by regex.
|
|
63
|
+
* Automatically enables the editor if not already enabled.
|
|
64
|
+
*
|
|
65
|
+
* @param options - Options
|
|
66
|
+
* @param options.pattern - Optional regex to filter URLs
|
|
67
|
+
* @returns Array of URLs
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // List all scripts and stylesheets
|
|
72
|
+
* const urls = await editor.list()
|
|
73
|
+
*
|
|
74
|
+
* // List only JS files
|
|
75
|
+
* const jsFiles = await editor.list({ pattern: /\.js/ })
|
|
76
|
+
*
|
|
77
|
+
* // List only CSS files
|
|
78
|
+
* const cssFiles = await editor.list({ pattern: /\.css/ })
|
|
79
|
+
*
|
|
80
|
+
* // Search for specific scripts
|
|
81
|
+
* const appScripts = await editor.list({ pattern: /app/ })
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
list({ pattern }?: {
|
|
85
|
+
pattern?: RegExp;
|
|
86
|
+
}): Promise<string[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Reads a script or stylesheet's source code by URL.
|
|
89
|
+
* Returns line-numbered content like Claude Code's Read tool.
|
|
90
|
+
* For inline scripts, use the `inline://` URL from list() or grep().
|
|
91
|
+
*
|
|
92
|
+
* @param options - Options
|
|
93
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
94
|
+
* @param options.offset - Line number to start from (0-based, default 0)
|
|
95
|
+
* @param options.limit - Number of lines to return (default 2000)
|
|
96
|
+
* @returns Content with line numbers, total lines, and range info
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // Read by URL
|
|
101
|
+
* const { content, totalLines } = await editor.read({
|
|
102
|
+
* url: 'https://example.com/app.js'
|
|
103
|
+
* })
|
|
104
|
+
*
|
|
105
|
+
* // Read a CSS file
|
|
106
|
+
* const { content } = await editor.read({ url: 'https://example.com/styles.css' })
|
|
107
|
+
*
|
|
108
|
+
* // Read lines 100-200
|
|
109
|
+
* const { content } = await editor.read({
|
|
110
|
+
* url: 'https://example.com/app.js',
|
|
111
|
+
* offset: 100,
|
|
112
|
+
* limit: 100
|
|
113
|
+
* })
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
read({ url, offset, limit }: {
|
|
117
|
+
url: string;
|
|
118
|
+
offset?: number;
|
|
119
|
+
limit?: number;
|
|
120
|
+
}): Promise<ReadResult>;
|
|
121
|
+
private getSource;
|
|
122
|
+
/**
|
|
123
|
+
* Edits a script or stylesheet by replacing oldString with newString.
|
|
124
|
+
* Like Claude Code's Edit tool - performs exact string replacement.
|
|
125
|
+
*
|
|
126
|
+
* @param options - Options
|
|
127
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
128
|
+
* @param options.oldString - Exact string to find and replace
|
|
129
|
+
* @param options.newString - Replacement string
|
|
130
|
+
* @param options.dryRun - If true, validate without applying (default false)
|
|
131
|
+
* @returns Result with success status
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* // Replace a string in JS
|
|
136
|
+
* await editor.edit({
|
|
137
|
+
* url: 'https://example.com/app.js',
|
|
138
|
+
* oldString: 'const DEBUG = false',
|
|
139
|
+
* newString: 'const DEBUG = true'
|
|
140
|
+
* })
|
|
141
|
+
*
|
|
142
|
+
* // Edit CSS
|
|
143
|
+
* await editor.edit({
|
|
144
|
+
* url: 'https://example.com/styles.css',
|
|
145
|
+
* oldString: 'color: red',
|
|
146
|
+
* newString: 'color: blue'
|
|
147
|
+
* })
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
edit({ url, oldString, newString, dryRun, }: {
|
|
151
|
+
url: string;
|
|
152
|
+
oldString: string;
|
|
153
|
+
newString: string;
|
|
154
|
+
dryRun?: boolean;
|
|
155
|
+
}): Promise<EditResult>;
|
|
156
|
+
private setSource;
|
|
157
|
+
/**
|
|
158
|
+
* Searches for a regex across all scripts and stylesheets.
|
|
159
|
+
* Like Claude Code's Grep tool - returns matching lines with context.
|
|
160
|
+
*
|
|
161
|
+
* @param options - Options
|
|
162
|
+
* @param options.regex - Regular expression to search for in file contents
|
|
163
|
+
* @param options.pattern - Optional regex to filter which URLs to search
|
|
164
|
+
* @returns Array of matches with url, line number, and line content
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* // Search all scripts and stylesheets for "color"
|
|
169
|
+
* const matches = await editor.grep({ regex: /color/ })
|
|
170
|
+
*
|
|
171
|
+
* // Search only CSS files
|
|
172
|
+
* const matches = await editor.grep({
|
|
173
|
+
* regex: /background-color/,
|
|
174
|
+
* pattern: /\.css/
|
|
175
|
+
* })
|
|
176
|
+
*
|
|
177
|
+
* // Regex search for console methods in JS
|
|
178
|
+
* const matches = await editor.grep({
|
|
179
|
+
* regex: /console\.(log|error|warn)/,
|
|
180
|
+
* pattern: /\.js/
|
|
181
|
+
* })
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
grep({ regex, pattern }: {
|
|
185
|
+
regex: RegExp;
|
|
186
|
+
pattern?: RegExp;
|
|
187
|
+
}): Promise<SearchMatch[]>;
|
|
188
|
+
/**
|
|
189
|
+
* Writes entire content to a script or stylesheet, replacing all existing code.
|
|
190
|
+
* Use with caution - prefer edit() for targeted changes.
|
|
191
|
+
*
|
|
192
|
+
* @param options - Options
|
|
193
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
194
|
+
* @param options.content - New content
|
|
195
|
+
* @param options.dryRun - If true, validate without applying (default false, only works for JS)
|
|
196
|
+
*/
|
|
197
|
+
write({ url, content, dryRun }: {
|
|
198
|
+
url: string;
|
|
199
|
+
content: string;
|
|
200
|
+
dryRun?: boolean;
|
|
201
|
+
}): Promise<EditResult>;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;gBAEnC,EAAE,GAAG,EAAE,EAAE;QAAE,GAAG,EAAE,UAAU,CAAA;KAAE;IAKxC,OAAO,CAAC,mBAAmB;IAoB3B;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC7B,OAAO,CAAC,UAAU;IAclB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,IAAI,CAAC,EAAE,OAAO,EAAE,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAcrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACG,IAAI,CAAC,EAAE,GAAG,EAAE,MAAU,EAAE,KAAY,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;YAqBtG,SAAS;IAmBvB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,IAAI,CAAC,EACT,GAAG,EACH,SAAS,EACT,SAAS,EACT,MAAc,GACf,EAAE;QACD,GAAG,EAAE,MAAM,CAAA;QACX,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,OAAO,CAAA;KACjB,GAAG,OAAO,CAAC,UAAU,CAAC;YAiBT,SAAS;IAuBvB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA+B3F;;;;;;;;OAQG;IACG,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,MAAc,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;CAKvH"}
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A class for viewing and editing web page scripts via Chrome DevTools Protocol.
|
|
3
|
+
* Provides a Claude Code-like interface: list, read, edit, grep.
|
|
4
|
+
*
|
|
5
|
+
* Edits are in-memory only and persist until page reload. They modify the running
|
|
6
|
+
* V8 instance but are not saved to disk or server.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const cdp = await getCDPSession({ page })
|
|
11
|
+
* const editor = new Editor({ cdp })
|
|
12
|
+
* await editor.enable()
|
|
13
|
+
*
|
|
14
|
+
* // List available scripts
|
|
15
|
+
* const scripts = editor.list({ search: 'app' })
|
|
16
|
+
*
|
|
17
|
+
* // Read a script
|
|
18
|
+
* const { content } = await editor.read({ url: 'https://example.com/app.js' })
|
|
19
|
+
*
|
|
20
|
+
* // Edit a script
|
|
21
|
+
* await editor.edit({
|
|
22
|
+
* url: 'https://example.com/app.js',
|
|
23
|
+
* oldString: 'console.log("old")',
|
|
24
|
+
* newString: 'console.log("new")'
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class Editor {
|
|
29
|
+
cdp;
|
|
30
|
+
enabled = false;
|
|
31
|
+
scripts = new Map();
|
|
32
|
+
stylesheets = new Map();
|
|
33
|
+
sourceCache = new Map();
|
|
34
|
+
constructor({ cdp }) {
|
|
35
|
+
this.cdp = cdp;
|
|
36
|
+
this.setupEventListeners();
|
|
37
|
+
}
|
|
38
|
+
setupEventListeners() {
|
|
39
|
+
this.cdp.on('Debugger.scriptParsed', (params) => {
|
|
40
|
+
if (!params.url.startsWith('chrome') && !params.url.startsWith('devtools')) {
|
|
41
|
+
const url = params.url || `inline://${params.scriptId}`;
|
|
42
|
+
this.scripts.set(url, params.scriptId);
|
|
43
|
+
this.sourceCache.delete(params.scriptId);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
this.cdp.on('CSS.styleSheetAdded', (params) => {
|
|
47
|
+
const header = params.header;
|
|
48
|
+
if (header.sourceURL?.startsWith('chrome') || header.sourceURL?.startsWith('devtools')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const url = header.sourceURL || `inline-css://${header.styleSheetId}`;
|
|
52
|
+
this.stylesheets.set(url, header.styleSheetId);
|
|
53
|
+
this.sourceCache.delete(header.styleSheetId);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Enables the editor. Must be called before other methods.
|
|
58
|
+
* Scripts are collected from Debugger.scriptParsed events.
|
|
59
|
+
* Reload the page after enabling to capture all scripts.
|
|
60
|
+
*/
|
|
61
|
+
async enable() {
|
|
62
|
+
if (this.enabled) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await this.cdp.send('Debugger.disable');
|
|
66
|
+
await this.cdp.send('CSS.disable');
|
|
67
|
+
this.scripts.clear();
|
|
68
|
+
this.stylesheets.clear();
|
|
69
|
+
this.sourceCache.clear();
|
|
70
|
+
const resourcesReady = new Promise((resolve) => {
|
|
71
|
+
let timeout;
|
|
72
|
+
const listener = () => {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
timeout = setTimeout(() => {
|
|
75
|
+
this.cdp.off('Debugger.scriptParsed', listener);
|
|
76
|
+
this.cdp.off('CSS.styleSheetAdded', listener);
|
|
77
|
+
resolve();
|
|
78
|
+
}, 100);
|
|
79
|
+
};
|
|
80
|
+
this.cdp.on('Debugger.scriptParsed', listener);
|
|
81
|
+
this.cdp.on('CSS.styleSheetAdded', listener);
|
|
82
|
+
timeout = setTimeout(() => {
|
|
83
|
+
this.cdp.off('Debugger.scriptParsed', listener);
|
|
84
|
+
this.cdp.off('CSS.styleSheetAdded', listener);
|
|
85
|
+
resolve();
|
|
86
|
+
}, 100);
|
|
87
|
+
});
|
|
88
|
+
await this.cdp.send('Debugger.enable');
|
|
89
|
+
await this.cdp.send('DOM.enable');
|
|
90
|
+
await this.cdp.send('CSS.enable');
|
|
91
|
+
await resourcesReady;
|
|
92
|
+
this.enabled = true;
|
|
93
|
+
}
|
|
94
|
+
getIdByUrl(url) {
|
|
95
|
+
const scriptId = this.scripts.get(url);
|
|
96
|
+
if (scriptId) {
|
|
97
|
+
return { scriptId };
|
|
98
|
+
}
|
|
99
|
+
const styleSheetId = this.stylesheets.get(url);
|
|
100
|
+
if (styleSheetId) {
|
|
101
|
+
return { styleSheetId };
|
|
102
|
+
}
|
|
103
|
+
const allUrls = [...Array.from(this.scripts.keys()), ...Array.from(this.stylesheets.keys())];
|
|
104
|
+
const available = allUrls.slice(0, 5);
|
|
105
|
+
throw new Error(`Resource not found: ${url}\nAvailable: ${available.join(', ')}${allUrls.length > 5 ? '...' : ''}`);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Lists available script and stylesheet URLs. Use pattern to filter by regex.
|
|
109
|
+
* Automatically enables the editor if not already enabled.
|
|
110
|
+
*
|
|
111
|
+
* @param options - Options
|
|
112
|
+
* @param options.pattern - Optional regex to filter URLs
|
|
113
|
+
* @returns Array of URLs
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* // List all scripts and stylesheets
|
|
118
|
+
* const urls = await editor.list()
|
|
119
|
+
*
|
|
120
|
+
* // List only JS files
|
|
121
|
+
* const jsFiles = await editor.list({ pattern: /\.js/ })
|
|
122
|
+
*
|
|
123
|
+
* // List only CSS files
|
|
124
|
+
* const cssFiles = await editor.list({ pattern: /\.css/ })
|
|
125
|
+
*
|
|
126
|
+
* // Search for specific scripts
|
|
127
|
+
* const appScripts = await editor.list({ pattern: /app/ })
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
async list({ pattern } = {}) {
|
|
131
|
+
await this.enable();
|
|
132
|
+
const urls = [...Array.from(this.scripts.keys()), ...Array.from(this.stylesheets.keys())];
|
|
133
|
+
if (!pattern) {
|
|
134
|
+
return urls;
|
|
135
|
+
}
|
|
136
|
+
return urls.filter((url) => {
|
|
137
|
+
const matches = pattern.test(url);
|
|
138
|
+
pattern.lastIndex = 0;
|
|
139
|
+
return matches;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Reads a script or stylesheet's source code by URL.
|
|
144
|
+
* Returns line-numbered content like Claude Code's Read tool.
|
|
145
|
+
* For inline scripts, use the `inline://` URL from list() or grep().
|
|
146
|
+
*
|
|
147
|
+
* @param options - Options
|
|
148
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
149
|
+
* @param options.offset - Line number to start from (0-based, default 0)
|
|
150
|
+
* @param options.limit - Number of lines to return (default 2000)
|
|
151
|
+
* @returns Content with line numbers, total lines, and range info
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // Read by URL
|
|
156
|
+
* const { content, totalLines } = await editor.read({
|
|
157
|
+
* url: 'https://example.com/app.js'
|
|
158
|
+
* })
|
|
159
|
+
*
|
|
160
|
+
* // Read a CSS file
|
|
161
|
+
* const { content } = await editor.read({ url: 'https://example.com/styles.css' })
|
|
162
|
+
*
|
|
163
|
+
* // Read lines 100-200
|
|
164
|
+
* const { content } = await editor.read({
|
|
165
|
+
* url: 'https://example.com/app.js',
|
|
166
|
+
* offset: 100,
|
|
167
|
+
* limit: 100
|
|
168
|
+
* })
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
async read({ url, offset = 0, limit = 2000 }) {
|
|
172
|
+
await this.enable();
|
|
173
|
+
const id = this.getIdByUrl(url);
|
|
174
|
+
const source = await this.getSource(id);
|
|
175
|
+
const lines = source.split('\n');
|
|
176
|
+
const totalLines = lines.length;
|
|
177
|
+
const startLine = Math.min(offset, totalLines);
|
|
178
|
+
const endLine = Math.min(offset + limit, totalLines);
|
|
179
|
+
const selectedLines = lines.slice(startLine, endLine);
|
|
180
|
+
const content = selectedLines.map((line, i) => `${String(startLine + i + 1).padStart(5)}| ${line}`).join('\n');
|
|
181
|
+
return {
|
|
182
|
+
content,
|
|
183
|
+
totalLines,
|
|
184
|
+
startLine: startLine + 1,
|
|
185
|
+
endLine,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async getSource(id) {
|
|
189
|
+
if ('styleSheetId' in id) {
|
|
190
|
+
const cached = this.sourceCache.get(id.styleSheetId);
|
|
191
|
+
if (cached) {
|
|
192
|
+
return cached;
|
|
193
|
+
}
|
|
194
|
+
const response = await this.cdp.send('CSS.getStyleSheetText', { styleSheetId: id.styleSheetId });
|
|
195
|
+
this.sourceCache.set(id.styleSheetId, response.text);
|
|
196
|
+
return response.text;
|
|
197
|
+
}
|
|
198
|
+
const cached = this.sourceCache.get(id.scriptId);
|
|
199
|
+
if (cached) {
|
|
200
|
+
return cached;
|
|
201
|
+
}
|
|
202
|
+
const response = await this.cdp.send('Debugger.getScriptSource', { scriptId: id.scriptId });
|
|
203
|
+
this.sourceCache.set(id.scriptId, response.scriptSource);
|
|
204
|
+
return response.scriptSource;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Edits a script or stylesheet by replacing oldString with newString.
|
|
208
|
+
* Like Claude Code's Edit tool - performs exact string replacement.
|
|
209
|
+
*
|
|
210
|
+
* @param options - Options
|
|
211
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
212
|
+
* @param options.oldString - Exact string to find and replace
|
|
213
|
+
* @param options.newString - Replacement string
|
|
214
|
+
* @param options.dryRun - If true, validate without applying (default false)
|
|
215
|
+
* @returns Result with success status
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* // Replace a string in JS
|
|
220
|
+
* await editor.edit({
|
|
221
|
+
* url: 'https://example.com/app.js',
|
|
222
|
+
* oldString: 'const DEBUG = false',
|
|
223
|
+
* newString: 'const DEBUG = true'
|
|
224
|
+
* })
|
|
225
|
+
*
|
|
226
|
+
* // Edit CSS
|
|
227
|
+
* await editor.edit({
|
|
228
|
+
* url: 'https://example.com/styles.css',
|
|
229
|
+
* oldString: 'color: red',
|
|
230
|
+
* newString: 'color: blue'
|
|
231
|
+
* })
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
async edit({ url, oldString, newString, dryRun = false, }) {
|
|
235
|
+
await this.enable();
|
|
236
|
+
const id = this.getIdByUrl(url);
|
|
237
|
+
const source = await this.getSource(id);
|
|
238
|
+
const matchCount = source.split(oldString).length - 1;
|
|
239
|
+
if (matchCount === 0) {
|
|
240
|
+
throw new Error(`oldString not found in ${url}`);
|
|
241
|
+
}
|
|
242
|
+
if (matchCount > 1) {
|
|
243
|
+
throw new Error(`oldString found ${matchCount} times in ${url}. Provide more context to make it unique.`);
|
|
244
|
+
}
|
|
245
|
+
const newSource = source.replace(oldString, newString);
|
|
246
|
+
return this.setSource(id, newSource, dryRun);
|
|
247
|
+
}
|
|
248
|
+
async setSource(id, content, dryRun = false) {
|
|
249
|
+
if ('styleSheetId' in id) {
|
|
250
|
+
await this.cdp.send('CSS.setStyleSheetText', { styleSheetId: id.styleSheetId, text: content });
|
|
251
|
+
if (!dryRun) {
|
|
252
|
+
this.sourceCache.set(id.styleSheetId, content);
|
|
253
|
+
}
|
|
254
|
+
return { success: true };
|
|
255
|
+
}
|
|
256
|
+
const response = await this.cdp.send('Debugger.setScriptSource', {
|
|
257
|
+
scriptId: id.scriptId,
|
|
258
|
+
scriptSource: content,
|
|
259
|
+
dryRun,
|
|
260
|
+
});
|
|
261
|
+
if (!dryRun) {
|
|
262
|
+
this.sourceCache.set(id.scriptId, content);
|
|
263
|
+
}
|
|
264
|
+
return { success: true, stackChanged: response.stackChanged };
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Searches for a regex across all scripts and stylesheets.
|
|
268
|
+
* Like Claude Code's Grep tool - returns matching lines with context.
|
|
269
|
+
*
|
|
270
|
+
* @param options - Options
|
|
271
|
+
* @param options.regex - Regular expression to search for in file contents
|
|
272
|
+
* @param options.pattern - Optional regex to filter which URLs to search
|
|
273
|
+
* @returns Array of matches with url, line number, and line content
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* // Search all scripts and stylesheets for "color"
|
|
278
|
+
* const matches = await editor.grep({ regex: /color/ })
|
|
279
|
+
*
|
|
280
|
+
* // Search only CSS files
|
|
281
|
+
* const matches = await editor.grep({
|
|
282
|
+
* regex: /background-color/,
|
|
283
|
+
* pattern: /\.css/
|
|
284
|
+
* })
|
|
285
|
+
*
|
|
286
|
+
* // Regex search for console methods in JS
|
|
287
|
+
* const matches = await editor.grep({
|
|
288
|
+
* regex: /console\.(log|error|warn)/,
|
|
289
|
+
* pattern: /\.js/
|
|
290
|
+
* })
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
async grep({ regex, pattern }) {
|
|
294
|
+
await this.enable();
|
|
295
|
+
const matches = [];
|
|
296
|
+
const urls = await this.list({ pattern });
|
|
297
|
+
for (const url of urls) {
|
|
298
|
+
let source;
|
|
299
|
+
try {
|
|
300
|
+
const id = this.getIdByUrl(url);
|
|
301
|
+
source = await this.getSource(id);
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const lines = source.split('\n');
|
|
307
|
+
for (let i = 0; i < lines.length; i++) {
|
|
308
|
+
if (regex.test(lines[i])) {
|
|
309
|
+
matches.push({
|
|
310
|
+
url,
|
|
311
|
+
lineNumber: i + 1,
|
|
312
|
+
lineContent: lines[i].trim().slice(0, 200),
|
|
313
|
+
});
|
|
314
|
+
regex.lastIndex = 0;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return matches;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Writes entire content to a script or stylesheet, replacing all existing code.
|
|
322
|
+
* Use with caution - prefer edit() for targeted changes.
|
|
323
|
+
*
|
|
324
|
+
* @param options - Options
|
|
325
|
+
* @param options.url - Script or stylesheet URL (inline scripts have `inline://{id}` URLs)
|
|
326
|
+
* @param options.content - New content
|
|
327
|
+
* @param options.dryRun - If true, validate without applying (default false, only works for JS)
|
|
328
|
+
*/
|
|
329
|
+
async write({ url, content, dryRun = false }) {
|
|
330
|
+
await this.enable();
|
|
331
|
+
const id = this.getIdByUrl(url);
|
|
332
|
+
return this.setSource(id, content, dryRun);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=editor.js.map
|