extension 3.5.1 → 3.6.1

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.
@@ -13,7 +13,6 @@ export declare const commandDescriptions: {
13
13
  readonly start: "Builds and starts the extension in production mode";
14
14
  readonly preview: "Previews the extension in production mode without building";
15
15
  readonly build: "Builds the extension for packaging/distribution";
16
- readonly cleanup: "Cleans up orphaned instances and frees unused ports";
17
16
  };
18
17
  export declare function unhandledError(err: unknown): string;
19
18
  export declare function updateFailed(err: any): string;
@@ -31,3 +30,38 @@ export declare function unsupportedBrowserFlag(value: string, supported: string[
31
30
  export declare function invalidLogsOptionPipe(value: string): string;
32
31
  export declare function invalidLogsOptionValue(value: string, allowed: string[]): string;
33
32
  export declare function programAIHelp(): string;
33
+ export type ProgramAIHelpJSON = {
34
+ version: string;
35
+ commands: Array<{
36
+ name: 'create' | 'dev' | 'start' | 'preview' | 'build';
37
+ summary: string;
38
+ supportsSourceInspection: boolean;
39
+ }>;
40
+ globalOptions: Array<{
41
+ name: '--ai-help' | '--format' | '--no-telemetry';
42
+ values?: string[];
43
+ default?: string;
44
+ description: string;
45
+ }>;
46
+ capabilities: {
47
+ sourceInspection: {
48
+ supportedIn: string[];
49
+ unsupportedIn: string[];
50
+ notes: string[];
51
+ };
52
+ logger: {
53
+ levels: string[];
54
+ formats: string[];
55
+ notes: string[];
56
+ };
57
+ managedDependencies: {
58
+ enforcement: string;
59
+ trigger: string;
60
+ action: string;
61
+ };
62
+ };
63
+ examples: string[];
64
+ };
65
+ export declare function programAIHelpJSON(version: string): ProgramAIHelpJSON;
66
+ export declare function invalidAIHelpFormat(value: string): string;
67
+ export declare function sourceInspectionNotSupported(command: 'start' | 'preview'): string;
package/dist/cli.cjs CHANGED
@@ -57,7 +57,7 @@ var __webpack_exports__ = {};
57
57
  return external_pintor_default().green('►►►');
58
58
  }
59
59
  const code = (text)=>external_pintor_default().blue(text);
60
- const arg = (text)=>external_pintor_default().gray(text);
60
+ const messages_arg = (text)=>external_pintor_default().gray(text);
61
61
  const fmt = {
62
62
  heading: (title)=>external_pintor_default().underline(external_pintor_default().blue(title)),
63
63
  label: (k)=>external_pintor_default().gray(k.toUpperCase()),
@@ -85,8 +85,7 @@ var __webpack_exports__ = {};
85
85
  dev: 'Starts the development server with hot reloading',
86
86
  start: 'Builds and starts the extension in production mode',
87
87
  preview: 'Previews the extension in production mode without building',
88
- build: 'Builds the extension for packaging/distribution',
89
- cleanup: 'Cleans up orphaned instances and frees unused ports'
88
+ build: 'Builds the extension for packaging/distribution'
90
89
  };
91
90
  function unhandledError(err) {
92
91
  const message = err instanceof Error ? err.stack || err.message : 'string' == typeof err ? err : fmt.truncate(err);
@@ -115,57 +114,70 @@ Example
115
114
  - ${code('extension create --help')} outputs information about the "create" command.
116
115
 
117
116
  Available Commands
118
- - ${code('extension create ' + arg('<project-name|project-path>'))}
117
+ - ${code('extension create ' + messages_arg('<project-name|project-path>'))}
119
118
  ${commandDescriptions.create}
120
119
 
121
- - ${code('extension dev ' + arg('[project-path|remote-url]'))}
120
+ - ${code('extension dev ' + messages_arg('[project-path|remote-url]'))}
122
121
  ${commandDescriptions.dev}
123
122
 
124
- - ${code('extension start ' + arg('[project-path|remote-url]'))}
123
+ - ${code('extension start ' + messages_arg('[project-path|remote-url]'))}
125
124
  ${commandDescriptions.start}
126
125
 
127
- - ${code('extension preview ' + arg('[project-path|remote-url]'))}
126
+ - ${code('extension preview ' + messages_arg('[project-path|remote-url]'))}
128
127
  ${commandDescriptions.preview}
129
128
 
130
- - ${code('extension build ' + arg('[project-path|remote-url]'))}
129
+ - ${code('extension build ' + messages_arg('[project-path|remote-url]'))}
131
130
  ${commandDescriptions.build}
132
131
 
133
- - ${code('extension cleanup')}
134
- ${commandDescriptions.cleanup}
135
-
136
132
  Common Options
137
- - ${code('--browser')} ${arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chrome)
138
- - ${code('--profile')} ${arg('<path|boolean>')} Browser profile configuration
139
- - ${code('--polyfill')} ${arg('[boolean]')} Enable/disable cross-browser polyfill
133
+ - ${code('--browser')} ${messages_arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chromium)
134
+ - ${code('--profile')} ${messages_arg('<path|boolean>')} Browser profile configuration
135
+ - ${code('--polyfill')} ${messages_arg('[boolean]')} Enable/disable cross-browser polyfill
140
136
  - ${code('--no-telemetry')} Disable anonymous telemetry for this run
141
- - ${code('--port')} ${arg('<number>')} Development server port (default: 8080)
142
- - ${code('--starting-url')} ${arg('<url>')} Initial URL to load in browser
143
- - ${code('--silent')} ${arg('[boolean]')} Suppress console output during build
137
+ - ${code('--ai-help')} Show AI-assistant oriented help and tips
138
+ - ${code('--format')} ${messages_arg('<pretty|json>')} Output format for ${code('--ai-help')} (default: pretty)
139
+ - ${code('--help')} Show help output
140
+ - ${code('--port')} ${messages_arg('<number>')} Development server port (default: 8080)
141
+ - ${code('--starting-url')} ${messages_arg('<url>')} Initial URL to load in browser
142
+ - ${code('--silent')} ${messages_arg('[boolean]')} Suppress console output during build
144
143
 
145
- Source Inspection
146
- - ${code('--source')} ${arg('<url|boolean>')} Open URL and print HTML after content scripts inject
147
- - When provided without a URL, falls back to ${arg('--starting-url')} or ${arg('https://example.com')}
148
- - Watch mode is enabled by default when ${code('--source')} is present
144
+ Source Inspection (dev command)
145
+ - ${code('--source')} ${messages_arg('<url|boolean>')} Open URL and print HTML after content scripts inject (dev only)
146
+ - When provided without a URL, falls back to ${messages_arg('--starting-url')} or ${messages_arg('https://example.com')}
147
+ - For ${code('extension dev')}, watch mode is enabled by default when ${code('--source')} is present
148
+ - ${messages_arg('Note:')} ${code('extension preview')} and ${code('extension start')} do not run source inspection in run-only preview mode.
149
+ - ${code('--watch-source')} ${messages_arg('[boolean]')} Re-print HTML on rebuilds or file changes
150
+ - ${code('--source-format')} ${messages_arg('<pretty|json|ndjson>')} Output format for page HTML (defaults to ${code('--log-format')} when present)
151
+ - ${code('--source-summary')} ${messages_arg('[boolean]')} Output a compact summary instead of full HTML
152
+ - ${code('--source-meta')} ${messages_arg('[boolean]')} Output page metadata (readyState, viewport, frames)
153
+ - ${code('--source-probe')} ${messages_arg('<selectors>')} Comma-separated CSS selectors to probe
154
+ - ${code('--source-tree')} ${messages_arg('<off|root-only>')} Output a compact extension root tree
155
+ - ${code('--source-console')} ${messages_arg('[boolean]')} Output console summary (best-effort)
156
+ - ${code('--source-dom')} ${messages_arg('[boolean]')} Output DOM snapshots and diffs
157
+ - ${code('--source-max-bytes')} ${messages_arg('<bytes>')} Limit HTML output size (0 disables truncation)
158
+ - ${code('--source-redact')} ${messages_arg('<off|safe|strict>')} Redact sensitive HTML content (default: safe for JSON/NDJSON)
159
+ - ${code('--source-include-shadow')} ${messages_arg('<off|open-only|all>')} Control Shadow DOM inclusion (default: open-only)
160
+ - ${code('--source-diff')} ${messages_arg('[boolean]')} Include diff metadata on watch updates
149
161
 
150
162
  Browser-Specific Options
151
- - ${code('--chromium-binary')} ${arg('<path>')} Custom Chromium binary path
152
- - ${code('--gecko-binary')}/${code('--firefox-binary')} ${arg('<path>')} Custom Firefox/Gecko binary path
163
+ - ${code('--chromium-binary')} ${messages_arg('<path>')} Custom Chromium binary path
164
+ - ${code('--gecko-binary')}/${code('--firefox-binary')} ${messages_arg('<path>')} Custom Firefox/Gecko binary path
153
165
 
154
166
  Build Options
155
- - ${code('--zip')} ${arg('[boolean]')} Create ZIP archive of built extension
156
- - ${code('--zip-source')} ${arg('[boolean]')} Include source files in ZIP
157
- - ${code('--zip-filename')} ${arg('<name>')} Custom ZIP filename
167
+ - ${code('--zip')} ${messages_arg('[boolean]')} Create ZIP archive of built extension
168
+ - ${code('--zip-source')} ${messages_arg('[boolean]')} Include source files in ZIP
169
+ - ${code('--zip-filename')} ${messages_arg('<name>')} Custom ZIP filename
158
170
 
159
171
  ${external_pintor_default().underline('Centralized Logger (terminal output)')}
160
172
  - The manager extension embeds a centralized logger that streams events to the CLI.
161
173
  - Enable and filter logs directly via ${code('extension dev')} flags:
162
- - ${code('--logs')} ${arg('<off|error|warn|info|debug|trace>')} Minimum level (default: info)
163
- - ${code('--log-context')} ${arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
164
- - ${code('--log-format')} ${arg('<pretty|json>')} Output format (default: pretty)
174
+ - ${code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level (default: off)
175
+ - ${code('--log-context')} ${messages_arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
176
+ - ${code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Output format (default: pretty)
165
177
  - ${code('--no-log-timestamps')} Hide ISO timestamps in pretty output
166
178
  - ${code('--no-log-color')} Disable color in pretty output
167
- - ${code('--log-url')} ${arg('<substring|/regex/>')} Filter by event.url
168
- - ${code('--log-tab')} ${arg('<id>')} Filter by tabId
179
+ - ${code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by event.url
180
+ - ${code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
169
181
  - Example: ${code('extension dev ./my-ext --logs=debug --log-context=all --log-format=pretty')}
170
182
 
171
183
  ${code('extension --help')}
@@ -179,6 +191,7 @@ ${external_pintor_default().underline('Path Resolution (important)')}
179
191
 
180
192
  AI Assistants
181
193
  - For AI-oriented guidance and deeper tips, run ${code('extension --ai-help')}
194
+ - For machine-readable AI guidance, run ${code('extension --ai-help --format json')}
182
195
 
183
196
  Report issues
184
197
  - ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
@@ -197,13 +210,13 @@ Browser-Specific Configuration
197
210
  Centralized Logger (for AI & CI)
198
211
  - Logs from all contexts are centralized by the manager extension and streamed to the CLI.
199
212
  - Prefer these flags to control terminal logs during ${code('extension dev')}:
200
- - ${code('--logs')} ${arg('<off|error|warn|info|debug|trace>')} Minimum level
201
- - ${code('--log-context')} ${arg('<list|all>')} Contexts to include
202
- - ${code('--log-format')} ${arg('<pretty|json>')} Pretty for humans; JSON for machines/NDJSON pipelines
203
- - ${code('--no-log-timestamps')} ${arg(' ')} Disable timestamps (pretty)
204
- - ${code('--no-log-color')} ${arg(' ')} Disable ANSI colors (pretty)
205
- - ${code('--log-url')} ${arg('<substring|/regex/>')} Filter by URL
206
- - ${code('--log-tab')} ${arg('<id>')} Filter by tabId
213
+ - ${code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level
214
+ - ${code('--log-context')} ${messages_arg('<list|all>')} Contexts to include
215
+ - ${code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Pretty for humans; JSON for machines/NDJSON pipelines
216
+ - ${code('--no-log-timestamps')} ${messages_arg(' ')} Disable timestamps (pretty)
217
+ - ${code('--no-log-color')} ${messages_arg(' ')} Disable ANSI colors (pretty)
218
+ - ${code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by URL
219
+ - ${code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
207
220
  - Good CI pattern: ${code('EXTENSION_AUTHOR_MODE=development EXTENSION_AUTO_EXIT_MS=6000 extension dev ./ext --logs=info --log-format=json')}
208
221
 
209
222
  Special Folders for Entrypoints
@@ -238,20 +251,20 @@ Shadow DOM for Content Scripts
238
251
  - Prevents style conflicts with host page
239
252
 
240
253
  Environment Variables
241
- - Use ${code(arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
254
+ - Use ${code(messages_arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
242
255
  - Supported in both ${code('process.env')} and ${code('import.meta.env')}
243
- - Environment file priority: ${external_pintor_default().underline(code(arg('.env.{browser}.{mode}')))} > ${external_pintor_default().underline(code(arg('.env.{browser}')))} > ${external_pintor_default().underline(code(arg('.env.{mode}')))} > ${external_pintor_default().underline(code(arg('.env')))}
244
- - Example: ${code(arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
256
+ - Environment file priority: ${external_pintor_default().underline(code(messages_arg('.env.{browser}.{mode}')))} > ${external_pintor_default().underline(code(messages_arg('.env.{browser}')))} > ${external_pintor_default().underline(code(messages_arg('.env.{mode}')))} > ${external_pintor_default().underline(code(messages_arg('.env')))}
257
+ - Example: ${code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
245
258
 
246
259
  Available Templates
247
- - ${external_pintor_default().green('Frameworks')}: ${code(arg('react'))}, ${code(arg('preact'))}, ${code(arg('vue'))}, ${code(arg('svelte'))}
248
- - ${external_pintor_default().green('Languages')}: ${code(arg("javascript"))}, ${code(arg("typescript"))}
249
- - ${external_pintor_default().green('Contexts')}: ${code(arg('content'))} (content scripts), ${code(arg('new'))} (new tab), ${code(arg('action'))} (popup)
250
- - ${external_pintor_default().green('Styling')}: ${code(arg('tailwind'))}, ${code(arg('sass'))}, ${code(arg('less'))}
251
- - ${external_pintor_default().green('Configs')}: ${code(arg('eslint'))}, ${code(arg('prettier'))}, ${code(arg('stylelint'))}
260
+ - ${external_pintor_default().green('Frameworks')}: ${code(messages_arg('react'))}, ${code(messages_arg('preact'))}, ${code(messages_arg('vue'))}, ${code(messages_arg('svelte'))}
261
+ - ${external_pintor_default().green('Languages')}: ${code(messages_arg("javascript"))}, ${code(messages_arg("typescript"))}
262
+ - ${external_pintor_default().green('Contexts')}: ${code(messages_arg('content'))} (content scripts), ${code(messages_arg('new'))} (new tab), ${code(messages_arg('action'))} (popup)
263
+ - ${external_pintor_default().green('Styling')}: ${code(messages_arg('tailwind'))}, ${code(messages_arg('sass'))}, ${code(messages_arg('less'))}
264
+ - ${external_pintor_default().green('Configs')}: ${code(messages_arg('eslint'))}, ${code(messages_arg('prettier'))}, ${code(messages_arg('stylelint'))}
252
265
 
253
266
  Webpack/Rspack Configuration
254
- - Create ${external_pintor_default().underline(code(arg('extension.config.js')))} for custom webpack configuration
267
+ - Create ${external_pintor_default().underline(code(messages_arg('extension.config.js')))} for custom webpack configuration
255
268
  - Function receives base config, return modified config
256
269
  - Supports all webpack/rspack loaders and plugins
257
270
  - Example:
@@ -270,8 +283,8 @@ Managed Dependencies (Important)
270
283
  - If you truly need a different version, open an issue so we can evaluate a safe upgrade.
271
284
 
272
285
  Framework-Specific Configuration
273
- - Create ${external_pintor_default().underline(code(arg('vue.loader.js')))} for Vue-specific loader configuration
274
- - Create ${external_pintor_default().underline(code(arg('svelte.loader.js')))} for Svelte-specific loader configuration
286
+ - Create ${external_pintor_default().underline(code(messages_arg('vue.loader.js')))} for Vue-specific loader configuration
287
+ - Create ${external_pintor_default().underline(code(messages_arg('svelte.loader.js')))} for Svelte-specific loader configuration
275
288
  - Automatically detected and used by Extension.js
276
289
  - Example svelte.loader.js:
277
290
  ${code('module.exports = {')}
@@ -288,18 +301,32 @@ Hot Module Replacement (HMR)
288
301
  - Service workers, _locales and manifest changes reload the extension
289
302
 
290
303
  Source Inspection & Real-Time Monitoring
291
- - Use ${code('--source')} ${arg('<url|boolean>')} to inspect page HTML after content script injection
292
- - When no URL is provided, falls back to ${arg('--starting-url')} or ${arg('https://example.com')}
304
+ - Use ${code('extension dev --source')} ${messages_arg('<url|boolean>')} to inspect page HTML after content script injection
305
+ - When no URL is provided, falls back to ${messages_arg('--starting-url')} or ${messages_arg('https://example.com')}
293
306
  - Watch mode is enabled by default when ${code('--source')} is present
307
+ - Use ${code('--watch-source')} to re-print HTML on rebuilds or file changes
308
+ - Use ${code('--source-format')} ${messages_arg('<pretty|json|ndjson>')} for machine-friendly HTML output
309
+ - Use ${code('--source-summary')} to emit a compact JSON summary instead of full HTML
310
+ - Use ${code('--source-meta')} to emit page metadata (readyState, viewport, frames)
311
+ - Use ${code('--source-probe')} to probe CSS selectors for quick validation
312
+ - Use ${code('--source-tree')} to emit a compact extension root tree
313
+ - Use ${code('--source-console')} to emit a console summary (best-effort)
314
+ - Use ${code('--source-dom')} to emit DOM snapshots and diffs
315
+ - Use ${code('--source-redact')} ${messages_arg('<off|safe|strict>')} to redact sensitive content
316
+ - Use ${code('--source-max-bytes')} ${messages_arg('<bytes>')} to limit output size
317
+ - Use ${code('--source-diff')} ${messages_arg('[boolean]')} to emit diff metadata for watch updates
318
+ - Source events include frame context (frameId/frameUrl), and console summaries include best-effort script URLs.
319
+ - Action timeline events ${code('action_event')} report navigation, injection, rebuilds, snapshots, and reloads.
294
320
  - Automatically enables Chrome remote debugging (port 9222) when source inspection is active
295
321
  - Extracts Shadow DOM content from ${code('#extension-root')} or ${code('[data-extension-root=\"true\"]')} elements
296
322
  - Useful for debugging content script behavior and style injection
297
- - Example: ${code('extension dev --source=' + arg('https://example.com'))}
323
+ - Example: ${code('extension dev --source=' + messages_arg('https://example.com'))}
324
+ - ${messages_arg('Note:')} ${code('preview/start')} run in run-only mode and do not perform source inspection.
298
325
 
299
326
  Non-Destructive Testing in CI
300
327
  - Prefer ${code('EXTENSION_AUTHOR_MODE=development')} to copy local templates and avoid network.
301
328
  - Reuse Playwright's Chromium via ${code('--chromium-binary')} path when available.
302
- - Set ${code(arg('EXTENSION_AUTO_EXIT_MS'))} and ${code(arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
329
+ - Set ${code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} and ${code(messages_arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
303
330
 
304
331
  File Watching & HMR Examples
305
332
  - Content script JS/TS changes trigger reinjection; CSS changes update styles live.
@@ -311,16 +338,121 @@ Troubleshooting
311
338
  - When ports conflict, pass ${code('--port 0')} to auto-select an available port.
312
339
 
313
340
  Non-Interactive / Auto Mode (CI)
314
- - Set ${code(arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
341
+ - Set ${code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
315
342
  Useful when ${code('pnpm extension dev')} would otherwise hang under Rspack watch.
316
- Example: ${code(arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --source ${arg('https://example.com')}
317
- - Optional: ${code(arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
343
+ Example: ${code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --source ${messages_arg('https://example.com')}
344
+ - Optional: ${code(messages_arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
318
345
 
319
346
  Cross-Browser Compatibility
320
347
  - Use ${code('--polyfill')} flag to enable webextension-polyfill
321
348
  - Automatically handles browser API differences
322
349
  - Supports Chrome, Edge, Firefox with single codebase`;
323
350
  }
351
+ function programAIHelpJSON(version) {
352
+ return {
353
+ version,
354
+ commands: [
355
+ {
356
+ name: 'create',
357
+ summary: commandDescriptions.create,
358
+ supportsSourceInspection: false
359
+ },
360
+ {
361
+ name: 'dev',
362
+ summary: commandDescriptions.dev,
363
+ supportsSourceInspection: true
364
+ },
365
+ {
366
+ name: 'start',
367
+ summary: commandDescriptions.start,
368
+ supportsSourceInspection: false
369
+ },
370
+ {
371
+ name: 'preview',
372
+ summary: commandDescriptions.preview,
373
+ supportsSourceInspection: false
374
+ },
375
+ {
376
+ name: 'build',
377
+ summary: commandDescriptions.build,
378
+ supportsSourceInspection: false
379
+ }
380
+ ],
381
+ globalOptions: [
382
+ {
383
+ name: '--ai-help',
384
+ description: 'Show AI-assistant oriented help and tips'
385
+ },
386
+ {
387
+ name: '--format',
388
+ values: [
389
+ 'pretty',
390
+ 'json'
391
+ ],
392
+ default: 'pretty',
393
+ description: 'Output format for --ai-help'
394
+ },
395
+ {
396
+ name: '--no-telemetry',
397
+ description: 'Disable anonymous telemetry for this run'
398
+ }
399
+ ],
400
+ capabilities: {
401
+ sourceInspection: {
402
+ supportedIn: [
403
+ 'dev'
404
+ ],
405
+ unsupportedIn: [
406
+ 'start',
407
+ 'preview',
408
+ 'build',
409
+ 'create'
410
+ ],
411
+ notes: [
412
+ '--source supports URL fallback to --starting-url or https://example.com',
413
+ 'run-only preview mode does not perform source inspection'
414
+ ]
415
+ },
416
+ logger: {
417
+ levels: [
418
+ 'off',
419
+ 'error',
420
+ 'warn',
421
+ 'info',
422
+ 'debug',
423
+ 'trace',
424
+ 'all'
425
+ ],
426
+ formats: [
427
+ 'pretty',
428
+ 'json',
429
+ 'ndjson'
430
+ ],
431
+ notes: [
432
+ 'centralized logger streams logs from multiple extension contexts',
433
+ '--logs defaults to off unless explicitly enabled'
434
+ ]
435
+ },
436
+ managedDependencies: {
437
+ enforcement: 'guarded',
438
+ trigger: 'when managed packages are declared in package.json and referenced in extension.config',
439
+ action: 'print an error and abort'
440
+ }
441
+ },
442
+ examples: [
443
+ 'extension --ai-help',
444
+ 'extension --ai-help --format json',
445
+ 'extension dev ./my-ext --source https://example.com --source-format json',
446
+ 'extension dev ./my-ext --logs=info --log-format=json'
447
+ ]
448
+ };
449
+ }
450
+ function invalidAIHelpFormat(value) {
451
+ return `${getLoggingPrefix('error')} Invalid value for ${code('--format')}: ${external_pintor_default().red(String(value))}\nAllowed values: ${messages_arg('pretty, json')}. Example: ${code('extension --ai-help --format json')}`;
452
+ }
453
+ function sourceInspectionNotSupported(command) {
454
+ return `${getLoggingPrefix('error')} ${code(`extension ${command}`)} currently runs in run-only preview mode and does not support source inspection.\nUse ${code('extension dev --source <url>')} for source inspection features.`;
455
+ }
324
456
  const external_semver_namespaceObject = require("semver");
325
457
  const external_node_fs_namespaceObject = require("node:fs");
326
458
  var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
@@ -453,6 +585,8 @@ Cross-Browser Compatibility
453
585
  const DEFAULT_FLUSH_AT = Number(process.env.EXTENSION_TELEMETRY_FLUSH_AT || 10);
454
586
  const DEFAULT_FLUSH_INTERVAL = Number(process.env.EXTENSION_TELEMETRY_FLUSH_INTERVAL || 2000);
455
587
  const DEFAULT_TIMEOUT_MS = Number(process.env.EXTENSION_TELEMETRY_TIMEOUT_MS || 200);
588
+ const DEFAULT_POSTHOG_KEY = 'phc_Np5x3Jg3h2V7kTFtNch2uz6QBaWDycQpIidzX5PetaN';
589
+ const DEFAULT_POSTHOG_HOST = 'https://us.i.posthog.com';
456
590
  class Telemetry {
457
591
  track(event, props = {}) {
458
592
  if (this.disabled || !this.storage) return;
@@ -473,6 +607,7 @@ Cross-Browser Compatibility
473
607
  }
474
608
  if (this.debug) console.error('[telemetry]', JSON.stringify(payload));
475
609
  if (!this.apiKey || !this.host) return;
610
+ if ('cli_shutdown' === event) return void this.buffer.push(payload);
476
611
  this.buffer.push(payload);
477
612
  if (this.buffer.length >= DEFAULT_FLUSH_AT) return void this.flush();
478
613
  if (!this.timer) this.timer = setTimeout(()=>{
@@ -535,8 +670,8 @@ Cross-Browser Compatibility
535
670
  is_ci: isCI(),
536
671
  schema_version: 1
537
672
  };
538
- this.apiKey = init.apiKey || process.env.EXTENSION_PUBLIC_POSTHOG_KEY;
539
- this.host = init.host || process.env.EXTENSION_PUBLIC_POSTHOG_HOST;
673
+ this.apiKey = init.apiKey || DEFAULT_POSTHOG_KEY;
674
+ this.host = init.host || DEFAULT_POSTHOG_HOST;
540
675
  if (!this.disabled && this.storage) {
541
676
  const consentPath = this.storage.consentFile;
542
677
  try {
@@ -614,6 +749,7 @@ Cross-Browser Compatibility
614
749
  });
615
750
  if (!telemetryDisabled) {
616
751
  const startedAt = Date.now();
752
+ let shutdownTracked = false;
617
753
  const known = new Set([
618
754
  'create',
619
755
  'dev',
@@ -634,6 +770,8 @@ Cross-Browser Compatibility
634
770
  telemetry_cli_telemetry.track('manifest_summary', summary);
635
771
  }
636
772
  process.on('beforeExit', async function() {
773
+ if (shutdownTracked) return;
774
+ shutdownTracked = true;
637
775
  telemetry_cli_telemetry.track('cli_shutdown', {
638
776
  command_guess: invoked,
639
777
  duration_ms: Date.now() - startedAt,
@@ -733,6 +871,84 @@ Cross-Browser Compatibility
733
871
  if (!hasExplicitSourceString) return hasStartingUrl ? String(startingUrl) : 'https://example.com';
734
872
  return String(source);
735
873
  }
874
+ function normalizeEnum(value, allowed) {
875
+ if (null == value) return;
876
+ const normalized = String(value).trim().toLowerCase();
877
+ if (!normalized) return;
878
+ return allowed.includes(normalized) ? normalized : void 0;
879
+ }
880
+ function normalizeSourceFormatOption(params) {
881
+ const allowed = [
882
+ 'pretty',
883
+ 'json',
884
+ 'ndjson'
885
+ ];
886
+ const sourceFormat = normalizeEnum(params.sourceFormat, allowed);
887
+ if (sourceFormat) return sourceFormat;
888
+ const logFormat = normalizeEnum(params.logFormat, allowed);
889
+ if (logFormat) return logFormat;
890
+ if (params.sourceEnabled) return 'json';
891
+ }
892
+ function normalizeSourceRedactOption(sourceRedact, sourceFormat) {
893
+ const allowed = [
894
+ 'off',
895
+ 'safe',
896
+ 'strict'
897
+ ];
898
+ const normalized = normalizeEnum(sourceRedact, allowed);
899
+ if (normalized) return normalized;
900
+ if (sourceFormat && 'pretty' !== sourceFormat) return 'safe';
901
+ return 'off';
902
+ }
903
+ function normalizeSourceMaxBytesOption(value) {
904
+ if (null == value) return;
905
+ const parsed = 'number' == typeof value ? value : Number(String(value).trim());
906
+ if (!Number.isFinite(parsed) || parsed < 0) return;
907
+ return Math.floor(parsed);
908
+ }
909
+ function normalizeSourceIncludeShadowOption(value, sourceEnabled) {
910
+ const allowed = [
911
+ 'off',
912
+ 'open-only',
913
+ 'all'
914
+ ];
915
+ const normalized = normalizeEnum(value, allowed);
916
+ if (normalized) return normalized;
917
+ if (sourceEnabled) return 'open-only';
918
+ }
919
+ function normalizeSourceMetaOption(value, sourceEnabled) {
920
+ if (void 0 !== value) return 0 === String(value).trim().length ? true : Boolean(value);
921
+ if (sourceEnabled) return true;
922
+ }
923
+ function normalizeSourceTreeOption(value, sourceEnabled) {
924
+ const allowed = [
925
+ 'off',
926
+ 'root-only'
927
+ ];
928
+ const normalized = normalizeEnum(value, allowed);
929
+ if (normalized) return normalized;
930
+ }
931
+ function normalizeSourceConsoleOption(value, sourceEnabled) {
932
+ if (void 0 !== value) return 0 === String(value).trim().length ? true : Boolean(value);
933
+ }
934
+ function normalizeSourceDomOption(value, watchSource) {
935
+ if (void 0 !== value) return 0 === String(value).trim().length ? true : Boolean(value);
936
+ if (watchSource) return true;
937
+ }
938
+ function normalizeSourceProbeOption(raw) {
939
+ if (null == raw) return;
940
+ if (Array.isArray(raw)) {
941
+ const values = raw.map((value)=>String(value).trim()).filter((value)=>value.length > 0);
942
+ return values.length > 0 ? values : void 0;
943
+ }
944
+ if (0 === String(raw).trim().length) return;
945
+ const values = String(raw).split(',').map((value)=>value.trim()).filter((value)=>value.length > 0);
946
+ return values.length > 0 ? values : void 0;
947
+ }
948
+ function normalizeSourceDiffOption(value, watchSource) {
949
+ if (void 0 !== value) return 0 === String(value).trim().length ? true : Boolean(value);
950
+ if (watchSource) return true;
951
+ }
736
952
  function parseLogContexts(raw) {
737
953
  if (!raw || 0 === String(raw).trim().length) return;
738
954
  if ('all' === String(raw).trim().toLowerCase()) return;
@@ -748,9 +964,14 @@ Cross-Browser Compatibility
748
964
  const values = String(raw).split(',').map((s)=>s.trim()).filter((s)=>s.length > 0).filter((c)=>allowed.includes(c));
749
965
  return values.length > 0 ? values : void 0;
750
966
  }
967
+ function parseExtensionsList(raw) {
968
+ if (!raw || 0 === String(raw).trim().length) return;
969
+ const values = String(raw).split(',').map((value)=>value.trim()).filter((value)=>value.length > 0);
970
+ return values.length > 0 ? values : void 0;
971
+ }
751
972
  const dev_require = (0, external_module_namespaceObject.createRequire)(__rslib_import_meta_url__);
752
973
  function registerDevCommand(program, telemetry) {
753
- program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--no-open', 'do not open the browser automatically (default: open)').option('--no-runner', 'do not launch the browser runner (dev server still starts)').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
974
+ program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--no-open', 'do not open the browser automatically (default: open)').option('--no-runner', 'do not launch the browser runner (dev server still starts)').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source [boolean]', '[experimental] re-print HTML on rebuilds or file changes (defaults to true when --source is present)', parseOptionalBoolean).option('--source-format <pretty|json|ndjson>', '[experimental] output format for source HTML (defaults to --log-format when present, otherwise JSON when --source is used)').option('--source-summary [boolean]', '[experimental] output a compact summary instead of full HTML', parseOptionalBoolean).option('--source-meta [boolean]', '[experimental] output page metadata (readyState, viewport, frames)', parseOptionalBoolean).option('--source-probe <selectors>', '[experimental] comma-separated CSS selectors to probe').option('--source-tree <off|root-only>', '[experimental] output a compact extension root tree').option('--source-console [boolean]', '[experimental] output console summary (best-effort)', parseOptionalBoolean).option('--source-dom [boolean]', '[experimental] output DOM snapshots and diffs (default: true when watch is enabled)', parseOptionalBoolean).option('--source-max-bytes <bytes>', '[experimental] limit HTML output size in bytes (0 disables truncation)').option('--source-redact <off|safe|strict>', '[experimental] redact sensitive content in HTML output (default: safe for JSON/NDJSON)').option('--source-include-shadow <off|open-only|all>', '[experimental] control Shadow DOM inclusion in HTML output (default: open-only)').option('--source-diff [boolean]', '[experimental] include diff metadata on watch updates (default: true when watch is enabled)', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
754
975
  if (devOptions.author || devOptions['authorMode']) {
755
976
  process.env.EXTENSION_AUTHOR_MODE = 'true';
756
977
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -771,8 +992,25 @@ Cross-Browser Compatibility
771
992
  const normalizedSource = normalizeSourceOption(devOptions.source, devOptions.startingUrl);
772
993
  if (normalizedSource) {
773
994
  devOptions.source = normalizedSource;
774
- devOptions.watchSource = true;
995
+ if (void 0 === devOptions.watchSource) devOptions.watchSource = true;
775
996
  }
997
+ const sourceEnabled = Boolean(devOptions.source || devOptions.watchSource);
998
+ const normalizedSourceFormat = normalizeSourceFormatOption({
999
+ sourceFormat: devOptions.sourceFormat,
1000
+ logFormat: devOptions.logFormat,
1001
+ sourceEnabled
1002
+ });
1003
+ devOptions.sourceFormat = normalizedSourceFormat;
1004
+ if (sourceEnabled && normalizedSourceFormat) process.env.EXTENSION_SOURCE_FORMAT = normalizedSourceFormat;
1005
+ devOptions.sourceRedact = normalizeSourceRedactOption(devOptions.sourceRedact, normalizedSourceFormat);
1006
+ devOptions.sourceMeta = normalizeSourceMetaOption(devOptions.sourceMeta, sourceEnabled);
1007
+ devOptions.sourceProbe = normalizeSourceProbeOption(devOptions.sourceProbe);
1008
+ devOptions.sourceTree = normalizeSourceTreeOption(devOptions.sourceTree, sourceEnabled);
1009
+ devOptions.sourceConsole = normalizeSourceConsoleOption(devOptions.sourceConsole, sourceEnabled);
1010
+ devOptions.sourceDom = normalizeSourceDomOption(devOptions.sourceDom, devOptions.watchSource);
1011
+ devOptions.sourceMaxBytes = normalizeSourceMaxBytesOption(devOptions.sourceMaxBytes);
1012
+ devOptions.sourceIncludeShadow = normalizeSourceIncludeShadowOption(devOptions.sourceIncludeShadow, sourceEnabled);
1013
+ devOptions.sourceDiff = normalizeSourceDiffOption(devOptions.sourceDiff, devOptions.watchSource);
776
1014
  const { extensionDev } = dev_require('extension-develop');
777
1015
  for (const vendor of list){
778
1016
  const vendorStart = Date.now();
@@ -793,8 +1031,20 @@ Cross-Browser Compatibility
793
1031
  startingUrl: devOptions.startingUrl,
794
1032
  source: devOptions.source,
795
1033
  watchSource: devOptions.watchSource,
1034
+ sourceFormat: devOptions.sourceFormat,
1035
+ sourceSummary: devOptions.sourceSummary,
1036
+ sourceMeta: devOptions.sourceMeta,
1037
+ sourceProbe: devOptions.sourceProbe,
1038
+ sourceTree: devOptions.sourceTree,
1039
+ sourceConsole: devOptions.sourceConsole,
1040
+ sourceDom: devOptions.sourceDom,
1041
+ sourceMaxBytes: devOptions.sourceMaxBytes,
1042
+ sourceRedact: devOptions.sourceRedact,
1043
+ sourceIncludeShadow: devOptions.sourceIncludeShadow,
1044
+ sourceDiff: devOptions.sourceDiff,
796
1045
  install: devOptions.install,
797
1046
  noRunner: false === devOptions.runner,
1047
+ extensions: parseExtensionsList(devOptions.extensions),
798
1048
  logLevel: logsOption || devOptions.logLevel || 'off',
799
1049
  logContexts: parseLogContexts(logContextOption),
800
1050
  logFormat: devOptions.logFormat || 'pretty',
@@ -820,7 +1070,12 @@ Cross-Browser Compatibility
820
1070
  }
821
1071
  const start_require = (0, external_module_namespaceObject.createRequire)(__rslib_import_meta_url__);
822
1072
  function registerStartCommand(program, telemetry) {
823
- program.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description(commandDescriptions.start).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--no-runner', 'do not launch the browser runner (build still runs)').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--install [boolean]', '[experimental] install project dependencies when missing', parseOptionalBoolean).option('--author, --author-mode', '[experimental] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
1073
+ program.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description(commandDescriptions.start).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--no-runner', 'do not launch the browser runner (build still runs)').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source [boolean]', '[experimental] re-print HTML on rebuilds or file changes', parseOptionalBoolean).option('--source-format <pretty|json|ndjson>', '[experimental] output format for source HTML (defaults to --log-format when present, otherwise JSON when --source is used)').option('--source-summary [boolean]', '[experimental] output a compact summary instead of full HTML', parseOptionalBoolean).option('--source-meta [boolean]', '[experimental] output page metadata (readyState, viewport, frames)', parseOptionalBoolean).option('--source-probe <selectors>', '[experimental] comma-separated CSS selectors to probe').option('--source-tree <off|root-only>', '[experimental] output a compact extension root tree').option('--source-console [boolean]', '[experimental] output console summary (best-effort)', parseOptionalBoolean).option('--source-dom [boolean]', '[experimental] output DOM snapshots and diffs (default: true when watch is enabled)', parseOptionalBoolean).option('--source-max-bytes <bytes>', '[experimental] limit HTML output size in bytes (0 disables truncation)').option('--source-redact <off|safe|strict>', '[experimental] redact sensitive content in HTML output (default: safe for JSON/NDJSON)').option('--source-include-shadow <off|open-only|all>', '[experimental] control Shadow DOM inclusion in HTML output (default: open-only)').option('--source-diff [boolean]', '[experimental] include diff metadata on watch updates (default: true when watch is enabled)', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[experimental] install project dependencies when missing', parseOptionalBoolean).option('--author, --author-mode', '[experimental] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
1074
+ const hasSourceInspectionFlags = void 0 !== startOptions.source || void 0 !== startOptions.watchSource || void 0 !== startOptions.sourceFormat || void 0 !== startOptions.sourceSummary || void 0 !== startOptions.sourceMeta || void 0 !== startOptions.sourceProbe || void 0 !== startOptions.sourceTree || void 0 !== startOptions.sourceConsole || void 0 !== startOptions.sourceDom || void 0 !== startOptions.sourceMaxBytes || void 0 !== startOptions.sourceRedact || void 0 !== startOptions.sourceIncludeShadow || void 0 !== startOptions.sourceDiff;
1075
+ if (hasSourceInspectionFlags) {
1076
+ console.error(sourceInspectionNotSupported('start'));
1077
+ process.exit(1);
1078
+ }
824
1079
  if (startOptions.author || startOptions.authorMode) {
825
1080
  process.env.EXTENSION_AUTHOR_MODE = 'true';
826
1081
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -835,6 +1090,25 @@ Cross-Browser Compatibility
835
1090
  validateVendorsOrExit(list, (invalid, supported)=>{
836
1091
  console.error(unsupportedBrowserFlag(invalid, supported));
837
1092
  });
1093
+ const normalizedSource = normalizeSourceOption(startOptions.source, startOptions.startingUrl);
1094
+ if (normalizedSource) startOptions.source = normalizedSource;
1095
+ const sourceEnabled = Boolean(startOptions.source || startOptions.watchSource);
1096
+ const normalizedSourceFormat = normalizeSourceFormatOption({
1097
+ sourceFormat: startOptions.sourceFormat,
1098
+ logFormat: startOptions.logFormat,
1099
+ sourceEnabled
1100
+ });
1101
+ startOptions.sourceFormat = normalizedSourceFormat;
1102
+ if (sourceEnabled && normalizedSourceFormat) process.env.EXTENSION_SOURCE_FORMAT = normalizedSourceFormat;
1103
+ startOptions.sourceRedact = normalizeSourceRedactOption(startOptions.sourceRedact, normalizedSourceFormat);
1104
+ startOptions.sourceMeta = normalizeSourceMetaOption(startOptions.sourceMeta, sourceEnabled);
1105
+ startOptions.sourceProbe = normalizeSourceProbeOption(startOptions.sourceProbe);
1106
+ startOptions.sourceTree = normalizeSourceTreeOption(startOptions.sourceTree, sourceEnabled);
1107
+ startOptions.sourceConsole = normalizeSourceConsoleOption(startOptions.sourceConsole, sourceEnabled);
1108
+ startOptions.sourceDom = normalizeSourceDomOption(startOptions.sourceDom, startOptions.watchSource);
1109
+ startOptions.sourceMaxBytes = normalizeSourceMaxBytesOption(startOptions.sourceMaxBytes);
1110
+ startOptions.sourceIncludeShadow = normalizeSourceIncludeShadowOption(startOptions.sourceIncludeShadow, sourceEnabled);
1111
+ startOptions.sourceDiff = normalizeSourceDiffOption(startOptions.sourceDiff, startOptions.watchSource);
838
1112
  const { extensionStart } = start_require('extension-develop');
839
1113
  for (const vendor of list){
840
1114
  const vendorStart = Date.now();
@@ -855,8 +1129,20 @@ Cross-Browser Compatibility
855
1129
  port: startOptions.port,
856
1130
  install: startOptions.install,
857
1131
  noRunner: false === startOptions.runner,
1132
+ extensions: parseExtensionsList(startOptions.extensions),
858
1133
  source: 'string' == typeof startOptions.source ? startOptions.source : startOptions.source,
859
1134
  watchSource: startOptions.watchSource,
1135
+ sourceFormat: startOptions.sourceFormat,
1136
+ sourceSummary: startOptions.sourceSummary,
1137
+ sourceMeta: startOptions.sourceMeta,
1138
+ sourceProbe: startOptions.sourceProbe,
1139
+ sourceTree: startOptions.sourceTree,
1140
+ sourceConsole: startOptions.sourceConsole,
1141
+ sourceDom: startOptions.sourceDom,
1142
+ sourceMaxBytes: startOptions.sourceMaxBytes,
1143
+ sourceRedact: startOptions.sourceRedact,
1144
+ sourceIncludeShadow: startOptions.sourceIncludeShadow,
1145
+ sourceDiff: startOptions.sourceDiff,
860
1146
  logLevel: logsOption || startOptions.logLevel || 'off',
861
1147
  logContexts,
862
1148
  logFormat: startOptions.logFormat || 'pretty',
@@ -881,7 +1167,12 @@ Cross-Browser Compatibility
881
1167
  }
882
1168
  const preview_require = (0, external_module_namespaceObject.createRequire)(__rslib_import_meta_url__);
883
1169
  function registerPreviewCommand(program, telemetry) {
884
- program.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description(commandDescriptions.preview).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--no-runner', 'do not launch the browser runner').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
1170
+ program.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description(commandDescriptions.preview).option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--no-runner', 'do not launch the browser runner').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source [boolean]', '[experimental] re-print HTML on rebuilds or file changes', parseOptionalBoolean).option('--source-format <pretty|json|ndjson>', '[experimental] output format for source HTML (defaults to --log-format when present, otherwise JSON when --source is used)').option('--source-summary [boolean]', '[experimental] output a compact summary instead of full HTML', parseOptionalBoolean).option('--source-meta [boolean]', '[experimental] output page metadata (readyState, viewport, frames)', parseOptionalBoolean).option('--source-probe <selectors>', '[experimental] comma-separated CSS selectors to probe').option('--source-tree <off|root-only>', '[experimental] output a compact extension root tree').option('--source-console [boolean]', '[experimental] output console summary (best-effort)', parseOptionalBoolean).option('--source-dom [boolean]', '[experimental] output DOM snapshots and diffs (default: true when watch is enabled)', parseOptionalBoolean).option('--source-max-bytes <bytes>', '[experimental] limit HTML output size in bytes (0 disables truncation)').option('--source-redact <off|safe|strict>', '[experimental] redact sensitive content in HTML output (default: safe for JSON/NDJSON)').option('--source-include-shadow <off|open-only|all>', '[experimental] control Shadow DOM inclusion in HTML output (default: open-only)').option('--source-diff [boolean]', '[experimental] include diff metadata on watch updates (default: true when watch is enabled)', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...previewOptions }) {
1171
+ const hasSourceInspectionFlags = void 0 !== previewOptions.source || void 0 !== previewOptions.watchSource || void 0 !== previewOptions.sourceFormat || void 0 !== previewOptions.sourceSummary || void 0 !== previewOptions.sourceMeta || void 0 !== previewOptions.sourceProbe || void 0 !== previewOptions.sourceTree || void 0 !== previewOptions.sourceConsole || void 0 !== previewOptions.sourceDom || void 0 !== previewOptions.sourceMaxBytes || void 0 !== previewOptions.sourceRedact || void 0 !== previewOptions.sourceIncludeShadow || void 0 !== previewOptions.sourceDiff;
1172
+ if (hasSourceInspectionFlags) {
1173
+ console.error(sourceInspectionNotSupported('preview'));
1174
+ process.exit(1);
1175
+ }
885
1176
  if (previewOptions.author || previewOptions['authorMode']) {
886
1177
  process.env.EXTENSION_AUTHOR_MODE = 'true';
887
1178
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -899,6 +1190,25 @@ Cross-Browser Compatibility
899
1190
  const isRemote = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
900
1191
  if (isRemote) process.env.EXTJS_LIGHT = '1';
901
1192
  }
1193
+ const normalizedSource = normalizeSourceOption(previewOptions.source, previewOptions.startingUrl);
1194
+ if (normalizedSource) previewOptions.source = normalizedSource;
1195
+ const sourceEnabled = Boolean(previewOptions.source || previewOptions.watchSource);
1196
+ const normalizedSourceFormat = normalizeSourceFormatOption({
1197
+ sourceFormat: previewOptions.sourceFormat,
1198
+ logFormat: previewOptions.logFormat,
1199
+ sourceEnabled
1200
+ });
1201
+ previewOptions.sourceFormat = normalizedSourceFormat;
1202
+ if (sourceEnabled && normalizedSourceFormat) process.env.EXTENSION_SOURCE_FORMAT = normalizedSourceFormat;
1203
+ previewOptions.sourceRedact = normalizeSourceRedactOption(previewOptions.sourceRedact, normalizedSourceFormat);
1204
+ previewOptions.sourceMeta = normalizeSourceMetaOption(previewOptions.sourceMeta, sourceEnabled);
1205
+ previewOptions.sourceProbe = normalizeSourceProbeOption(previewOptions.sourceProbe);
1206
+ previewOptions.sourceTree = normalizeSourceTreeOption(previewOptions.sourceTree, sourceEnabled);
1207
+ previewOptions.sourceConsole = normalizeSourceConsoleOption(previewOptions.sourceConsole, sourceEnabled);
1208
+ previewOptions.sourceDom = normalizeSourceDomOption(previewOptions.sourceDom, previewOptions.watchSource);
1209
+ previewOptions.sourceMaxBytes = normalizeSourceMaxBytesOption(previewOptions.sourceMaxBytes);
1210
+ previewOptions.sourceIncludeShadow = normalizeSourceIncludeShadowOption(previewOptions.sourceIncludeShadow, sourceEnabled);
1211
+ previewOptions.sourceDiff = normalizeSourceDiffOption(previewOptions.sourceDiff, previewOptions.watchSource);
902
1212
  const { extensionPreview } = preview_require('extension-develop');
903
1213
  for (const vendor of list){
904
1214
  const vendorStart = Date.now();
@@ -918,8 +1228,20 @@ Cross-Browser Compatibility
918
1228
  startingUrl: previewOptions.startingUrl,
919
1229
  port: previewOptions.port,
920
1230
  noRunner: false === previewOptions.runner,
1231
+ extensions: parseExtensionsList(previewOptions.extensions),
921
1232
  source: 'string' == typeof previewOptions.source ? previewOptions.source : previewOptions.source,
922
1233
  watchSource: previewOptions.watchSource,
1234
+ sourceFormat: previewOptions.sourceFormat,
1235
+ sourceSummary: previewOptions.sourceSummary,
1236
+ sourceMeta: previewOptions.sourceMeta,
1237
+ sourceProbe: previewOptions.sourceProbe,
1238
+ sourceTree: previewOptions.sourceTree,
1239
+ sourceConsole: previewOptions.sourceConsole,
1240
+ sourceDom: previewOptions.sourceDom,
1241
+ sourceMaxBytes: previewOptions.sourceMaxBytes,
1242
+ sourceRedact: previewOptions.sourceRedact,
1243
+ sourceIncludeShadow: previewOptions.sourceIncludeShadow,
1244
+ sourceDiff: previewOptions.sourceDiff,
923
1245
  logLevel: logsOption || previewOptions.logLevel || 'off',
924
1246
  logContexts,
925
1247
  logFormat: previewOptions.logFormat || 'pretty',
@@ -943,7 +1265,7 @@ Cross-Browser Compatibility
943
1265
  });
944
1266
  }
945
1267
  function registerBuildCommand(program, telemetry) {
946
- program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
1268
+ program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
947
1269
  if (buildOptions.author || buildOptions['authorMode']) {
948
1270
  process.env.EXTENSION_AUTHOR_MODE = 'true';
949
1271
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -974,7 +1296,8 @@ Cross-Browser Compatibility
974
1296
  zipSource: buildOptions.zipSource,
975
1297
  zipFilename: buildOptions.zipFilename,
976
1298
  silent: buildOptions.silent,
977
- install: buildOptions.install
1299
+ install: buildOptions.install,
1300
+ extensions: parseExtensionsList(buildOptions.extensions)
978
1301
  });
979
1302
  telemetry.track('cli_build_summary', {
980
1303
  ...buildSummary
@@ -1003,19 +1326,35 @@ Cross-Browser Compatibility
1003
1326
  }
1004
1327
  }
1005
1328
  process.env.EXTENSION_DEVELOP_VERSION = developVersion();
1329
+ function resolveAIHelpFormatFromArgv(argv) {
1330
+ const equalArg = argv.find((arg)=>arg.startsWith('--format='));
1331
+ if (equalArg) return equalArg.slice(9);
1332
+ const formatIndex = argv.indexOf('--format');
1333
+ if (formatIndex >= 0) return argv[formatIndex + 1] || '';
1334
+ return 'pretty';
1335
+ }
1006
1336
  check_updates_checkUpdates().then((updateMessage)=>{
1007
1337
  if (!updateMessage) return;
1008
1338
  if ('true' === process.env.EXTENSION_CLI_BANNER_PRINTED) return void console.log(updateMessage.message);
1009
1339
  process.env.EXTENSION_CLI_UPDATE_SUFFIX = updateMessage.suffix;
1010
1340
  });
1011
1341
  const extensionJs = external_commander_namespaceObject.program;
1012
- extensionJs.name(cliPackageJson.name).description(cliPackageJson.description).version(cliPackageJson.version).option('--no-telemetry', 'disable anonymous telemetry for this run').option('--ai-help', 'show AI-assistant oriented help and tips').addHelpText('after', programUserHelp()).showHelpAfterError(true).showSuggestionAfterError(true);
1342
+ extensionJs.name(cliPackageJson.name).description(cliPackageJson.description).version(cliPackageJson.version).option('--no-telemetry', 'disable anonymous telemetry for this run').option('--ai-help', 'show AI-assistant oriented help and tips').option('--format <pretty|json>', 'output format for --ai-help', 'pretty').addHelpText('after', programUserHelp()).showHelpAfterError(true).showSuggestionAfterError(true);
1013
1343
  registerCreateCommand(extensionJs, telemetry_cli_telemetry);
1014
1344
  registerDevCommand(extensionJs, telemetry_cli_telemetry);
1015
1345
  registerStartCommand(extensionJs, telemetry_cli_telemetry);
1016
1346
  registerPreviewCommand(extensionJs, telemetry_cli_telemetry);
1017
1347
  registerBuildCommand(extensionJs, telemetry_cli_telemetry);
1018
1348
  extensionJs.on('option:ai-help', function() {
1349
+ const format = resolveAIHelpFormatFromArgv(process.argv).trim().toLowerCase();
1350
+ if ('json' === format) {
1351
+ console.log(JSON.stringify(programAIHelpJSON(cliPackageJson.version), null, 2));
1352
+ process.exit(0);
1353
+ }
1354
+ if ('pretty' !== format) {
1355
+ console.error(invalidAIHelpFormat(format));
1356
+ process.exit(1);
1357
+ }
1019
1358
  console.log(programAIHelp());
1020
1359
  process.exit(0);
1021
1360
  });
@@ -1,2 +1,22 @@
1
1
  export declare function normalizeSourceOption(source: boolean | string | undefined, startingUrl?: string): string | undefined;
2
+ export type SourceFormat = 'pretty' | 'json' | 'ndjson';
3
+ export type SourceRedact = 'off' | 'safe' | 'strict';
4
+ export type SourceIncludeShadow = 'off' | 'open-only' | 'all';
5
+ export type SourceTreeMode = 'off' | 'root-only';
6
+ export type SourceDomMode = 'off' | 'on';
7
+ export declare function normalizeSourceFormatOption(params: {
8
+ sourceFormat?: string;
9
+ logFormat?: string;
10
+ sourceEnabled?: boolean;
11
+ }): SourceFormat | undefined;
12
+ export declare function normalizeSourceRedactOption(sourceRedact: string | undefined, sourceFormat: SourceFormat | undefined): SourceRedact;
13
+ export declare function normalizeSourceMaxBytesOption(value: string | number | undefined): number | undefined;
14
+ export declare function normalizeSourceIncludeShadowOption(value: string | undefined, sourceEnabled: boolean): SourceIncludeShadow | undefined;
15
+ export declare function normalizeSourceMetaOption(value: boolean | string | undefined, sourceEnabled: boolean): boolean | undefined;
16
+ export declare function normalizeSourceTreeOption(value: string | undefined, sourceEnabled: boolean): SourceTreeMode | undefined;
17
+ export declare function normalizeSourceConsoleOption(value: boolean | string | undefined, sourceEnabled: boolean): boolean | undefined;
18
+ export declare function normalizeSourceDomOption(value: boolean | string | undefined, watchSource: boolean | undefined): boolean | undefined;
19
+ export declare function normalizeSourceProbeOption(raw: string | string[] | undefined): string[] | undefined;
20
+ export declare function normalizeSourceDiffOption(value: boolean | string | undefined, watchSource: boolean | undefined): boolean | undefined;
2
21
  export declare function parseLogContexts(raw: string | undefined): Array<'background' | 'content' | 'page' | 'sidebar' | 'popup' | 'options' | 'devtools'> | undefined;
22
+ export declare function parseExtensionsList(raw: string | undefined): string[] | undefined;
package/package.json CHANGED
@@ -33,7 +33,7 @@
33
33
  "extension": "./bin/extension.cjs"
34
34
  },
35
35
  "name": "extension",
36
- "version": "3.5.1",
36
+ "version": "3.6.1",
37
37
  "description": "Create cross-browser extensions with no build configuration.",
38
38
  "homepage": "https://extension.js.org/",
39
39
  "bugs": {
@@ -90,8 +90,8 @@
90
90
  "@types/chrome": "^0.1.33",
91
91
  "@types/node": "^25.2.0",
92
92
  "@types/webextension-polyfill": "0.12.4",
93
- "extension-create": "^3.5.1",
94
- "extension-develop": "^3.5.1",
93
+ "extension-create": "^3.6.1",
94
+ "extension-develop": "^3.6.1",
95
95
  "commander": "^14.0.3",
96
96
  "pintor": "0.3.0",
97
97
  "semver": "^7.7.3",