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.
Files changed (105) hide show
  1. package/bin.js +1 -1
  2. package/dist/bippy.js +966 -0
  3. package/dist/{extension/cdp-relay.d.ts → cdp-relay.d.ts} +3 -2
  4. package/dist/cdp-relay.d.ts.map +1 -0
  5. package/dist/{extension/cdp-relay.js → cdp-relay.js} +101 -3
  6. package/dist/cdp-relay.js.map +1 -0
  7. package/dist/cdp-session.d.ts +1 -1
  8. package/dist/cdp-session.d.ts.map +1 -1
  9. package/dist/cdp-session.js +4 -4
  10. package/dist/cdp-session.js.map +1 -1
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +71 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/create-logger.d.ts.map +1 -1
  16. package/dist/create-logger.js +2 -1
  17. package/dist/create-logger.js.map +1 -1
  18. package/dist/debugger-examples-types.d.ts +18 -0
  19. package/dist/debugger-examples-types.d.ts.map +1 -0
  20. package/dist/debugger-examples-types.js +2 -0
  21. package/dist/debugger-examples-types.js.map +1 -0
  22. package/dist/debugger-examples.d.ts +6 -0
  23. package/dist/debugger-examples.d.ts.map +1 -0
  24. package/dist/debugger-examples.js +53 -0
  25. package/dist/debugger-examples.js.map +1 -0
  26. package/dist/debugger-examples.ts +66 -0
  27. package/dist/debugger.d.ts +380 -0
  28. package/dist/debugger.d.ts.map +1 -0
  29. package/dist/debugger.js +631 -0
  30. package/dist/debugger.js.map +1 -0
  31. package/dist/editor-examples.d.ts +11 -0
  32. package/dist/editor-examples.d.ts.map +1 -0
  33. package/dist/editor-examples.js +124 -0
  34. package/dist/editor-examples.js.map +1 -0
  35. package/dist/editor.d.ts +203 -0
  36. package/dist/editor.d.ts.map +1 -0
  37. package/dist/editor.js +335 -0
  38. package/dist/editor.js.map +1 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/mcp-client.d.ts +5 -1
  44. package/dist/mcp-client.d.ts.map +1 -1
  45. package/dist/mcp-client.js +13 -9
  46. package/dist/mcp-client.js.map +1 -1
  47. package/dist/mcp.d.ts +4 -1
  48. package/dist/mcp.d.ts.map +1 -1
  49. package/dist/mcp.js +170 -27
  50. package/dist/mcp.js.map +1 -1
  51. package/dist/mcp.test.d.ts.map +1 -1
  52. package/dist/mcp.test.js +886 -182
  53. package/dist/mcp.test.js.map +1 -1
  54. package/dist/prompt.md +86 -6
  55. package/dist/{extension/protocol.d.ts → protocol.d.ts} +1 -1
  56. package/dist/protocol.d.ts.map +1 -0
  57. package/dist/protocol.js.map +1 -0
  58. package/dist/react-source.d.ts +13 -0
  59. package/dist/react-source.d.ts.map +1 -0
  60. package/dist/react-source.js +66 -0
  61. package/dist/react-source.js.map +1 -0
  62. package/dist/selector-generator.js +7065 -18
  63. package/dist/start-relay-server.d.ts +4 -2
  64. package/dist/start-relay-server.d.ts.map +1 -1
  65. package/dist/start-relay-server.js +3 -3
  66. package/dist/start-relay-server.js.map +1 -1
  67. package/dist/styles.d.ts +27 -0
  68. package/dist/styles.d.ts.map +1 -0
  69. package/dist/styles.js +232 -0
  70. package/dist/styles.js.map +1 -0
  71. package/dist/utils.d.ts +3 -1
  72. package/dist/utils.d.ts.map +1 -1
  73. package/dist/utils.js +7 -3
  74. package/dist/utils.js.map +1 -1
  75. package/dist/wait-for-page-load.d.ts.map +1 -1
  76. package/dist/wait-for-page-load.js +3 -2
  77. package/dist/wait-for-page-load.js.map +1 -1
  78. package/package.json +5 -2
  79. package/src/{extension/cdp-relay.ts → cdp-relay.ts} +109 -5
  80. package/src/cdp-session.ts +4 -4
  81. package/src/cdp-timing.md +128 -0
  82. package/src/cli.ts +85 -0
  83. package/src/create-logger.ts +2 -1
  84. package/src/debugger-examples-types.ts +10 -0
  85. package/src/debugger-examples.ts +66 -0
  86. package/src/debugger.ts +711 -0
  87. package/src/editor-examples.ts +148 -0
  88. package/src/editor.ts +389 -0
  89. package/src/index.ts +1 -1
  90. package/src/mcp-client.ts +14 -9
  91. package/src/mcp.test.ts +1053 -196
  92. package/src/mcp.ts +195 -30
  93. package/src/prompt.md +86 -6
  94. package/src/{extension/protocol.ts → protocol.ts} +1 -1
  95. package/src/react-source.ts +92 -0
  96. package/src/snapshots/shadcn-ui-accessibility.md +57 -57
  97. package/src/start-relay-server.ts +3 -3
  98. package/src/styles.ts +343 -0
  99. package/src/utils.ts +8 -3
  100. package/src/wait-for-page-load.ts +3 -2
  101. package/dist/extension/cdp-relay.d.ts.map +0 -1
  102. package/dist/extension/cdp-relay.js.map +0 -1
  103. package/dist/extension/protocol.d.ts.map +0 -1
  104. package/dist/extension/protocol.js.map +0 -1
  105. /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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"}
@@ -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