extension 4.1.15 → 4.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -6642,6 +6642,15 @@ var __webpack_modules__ = {
6642
6642
  return obj;
6643
6643
  }
6644
6644
  const VERSION_MAX_LENGTH = 64;
6645
+ const CATALOG_CODE = /^E_[A-Z0-9_]{1,48}$/;
6646
+ function catalogCode(value) {
6647
+ if ('string' != typeof value) return;
6648
+ return CATALOG_CODE.test(value) ? value : void 0;
6649
+ }
6650
+ function smallExitCode(value) {
6651
+ if ('number' != typeof value || !Number.isInteger(value)) return;
6652
+ return value >= 0 && value <= 255 ? value : void 0;
6653
+ }
6645
6654
  function sanitizeTag(value) {
6646
6655
  return String(value).trim().replace(/[^a-zA-Z0-9._-]/g, '').slice(0, 64);
6647
6656
  }
@@ -6846,6 +6855,10 @@ var __webpack_modules__ = {
6846
6855
  get isEnabled() {
6847
6856
  return !this.disabled;
6848
6857
  }
6858
+ disable() {
6859
+ this.disabled = true;
6860
+ this.buffer.length = 0;
6861
+ }
6849
6862
  track(event, props) {
6850
6863
  try {
6851
6864
  if (this.disabled) return;
@@ -6862,6 +6875,11 @@ var __webpack_modules__ = {
6862
6875
  };
6863
6876
  if (props.template) enforcedProps.template = sanitizeTag(props.template);
6864
6877
  if (props.source) enforcedProps.source = sanitizeTag(props.source);
6878
+ if ('started' === props.session) enforcedProps.session = 'started';
6879
+ const code = catalogCode(props.code);
6880
+ if (code) enforcedProps.code = code;
6881
+ const exitCode = smallExitCode(props.exit_code);
6882
+ if (void 0 !== exitCode) enforcedProps.exit_code = exitCode;
6865
6883
  const payload = {
6866
6884
  event,
6867
6885
  properties: {
@@ -6873,7 +6891,7 @@ var __webpack_modules__ = {
6873
6891
  distinct_id: this.anonId
6874
6892
  };
6875
6893
  this.writeAudit(payload);
6876
- if ('command_executed' === event && 'create' !== props.command && Math.random() > this.sampleRate) return;
6894
+ if ('command_executed' === event && 'create' !== props.command && 'started' !== props.session && Math.random() > this.sampleRate) return;
6877
6895
  if (!this.apiKey || !this.host) return;
6878
6896
  this.buffer.push(payload);
6879
6897
  this.sent += 1;
@@ -6977,7 +6995,7 @@ var __webpack_modules__ = {
6977
6995
  return Math.min(Math.max(n, min), max);
6978
6996
  }
6979
6997
  const TEMPLATE_CORPUS_REPO = 'extension-js/examples';
6980
- const TEMPLATE_CORPUS_REF = 'cb6a25377bd9516a1e55447a2010537019851ab2';
6998
+ const TEMPLATE_CORPUS_REF = 'd951b735ee5fbe904bba0c47d688527300d10e32';
6981
6999
  const TEMPLATE_CORPUS_SLUGS = [
6982
7000
  'action',
6983
7001
  'action-locales',
@@ -7342,8 +7360,12 @@ var __webpack_modules__ = {
7342
7360
  function getTelemetryConsent() {
7343
7361
  return consent;
7344
7362
  }
7363
+ function invokedCommand() {
7364
+ return invoked;
7365
+ }
7345
7366
  function setTelemetryConsent(value) {
7346
7367
  const ok = writeConsent(value);
7368
+ if ('disabled' === value) telemetry.disable();
7347
7369
  const storage = resolveTelemetryStorage();
7348
7370
  return {
7349
7371
  ok,
@@ -7351,12 +7373,29 @@ var __webpack_modules__ = {
7351
7373
  };
7352
7374
  }
7353
7375
  let tracked = false;
7376
+ let sessionStarted = false;
7354
7377
  function markTracked() {
7355
7378
  if (tracked) return false;
7356
7379
  tracked = true;
7357
7380
  return true;
7358
7381
  }
7382
+ function markCommandSessionStart(command = invoked) {
7383
+ if (tracked || sessionStarted) return;
7384
+ sessionStarted = true;
7385
+ telemetry.track('command_executed', {
7386
+ command,
7387
+ success: true,
7388
+ version: telemetry_cli_version,
7389
+ session: 'started',
7390
+ ...telemetryCommandContext(command)
7391
+ });
7392
+ telemetry.flush();
7393
+ }
7394
+ function hasTrackedSessionStart() {
7395
+ return sessionStarted;
7396
+ }
7359
7397
  function markCommandSuccess(command = invoked) {
7398
+ if (sessionStarted) return;
7360
7399
  if (!markTracked()) return;
7361
7400
  telemetry.track('command_executed', {
7362
7401
  command,
@@ -7414,7 +7453,17 @@ var __webpack_modules__ = {
7414
7453
  });
7415
7454
  }
7416
7455
  const TELEMETRY_FLUSH_TIMEOUT_MS = 500;
7456
+ function markOutcomeForExit(code) {
7457
+ try {
7458
+ if ('unknown' === invokedCommand()) return;
7459
+ if (0 === code) return void markCommandSuccess();
7460
+ markCommandFailure(void 0, {
7461
+ exitCode: code
7462
+ });
7463
+ } catch {}
7464
+ }
7417
7465
  async function exitAfterDrain(code) {
7466
+ markOutcomeForExit(code);
7418
7467
  try {
7419
7468
  await Promise.race([
7420
7469
  telemetry.flush(),
@@ -9473,6 +9522,8 @@ Cross-browser compatibility
9473
9522
  function cliGeckoBinary(opts) {
9474
9523
  return opts.geckoBinary ?? opts.firefoxBinary;
9475
9524
  }
9525
+ const BROWSER_LAUNCH_HELP_FOOTER = "\nTo stop the browser launch, use --no-browser.\nTo launch the browser without opening a tab for your extension, use --no-open.\n";
9526
+ const NO_OPEN_FLAG_DESCRIPTION = 'launch the browser but do not open a tab for your extension. To stop the browser launch itself, use --no-browser';
9476
9527
  async function resolveNoBrowser(projectPath, command) {
9477
9528
  if ('1' === process.env.EXTENSION_CLI_NO_BROWSER) return true;
9478
9529
  try {
@@ -9696,7 +9747,7 @@ Cross-browser compatibility
9696
9747
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 8080;
9697
9748
  }
9698
9749
  function registerDevCommand(program) {
9699
- program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --no-reload emit a dev-mode dist without the content-script reload runtime; tabs need manual reload to see changes\n --wait wait for ready contract and exit; pair with --output json for machine output\n").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(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only; no live reload)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).option('--safari-binary <path-to-binary>', 'specify the Safari binary to open after packaging (safari targets only)').option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari treats as unsigned: the extension then needs Develop \u25b8 Allow Unsigned Extensions re-ticked on every launch. A signed build is listed and stays enabled across restarts').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', 'do not open the browser automatically (default: open)').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('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
9750
+ program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser stop the browser launch, the dev server still starts\n --no-reload emit a dev-mode dist without the content-script reload runtime, tabs need a manual reload to see changes\n --wait wait for ready contract and exit, pair with --output json for machine output\n" + BROWSER_LAUNCH_HELP_FOOTER).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(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only; no live reload)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).option('--safari-binary <path-to-binary>', 'specify the Safari binary to open after packaging (safari targets only)').option('--app-name <name>', 'override the Safari app name (safari targets only). Defaults to the manifest `name`').option('--bundle-id <reverse.dns>', 'set a user-owned Safari bundle identifier (safari targets only). Defaults to a generated dev.extensionjs.* id').option('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari treats as unsigned: the extension then needs Develop \u25b8 Allow Unsigned Extensions re-ticked on every launch. A signed build is listed and stays enabled across restarts').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', NO_OPEN_FLAG_DESCRIPTION).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('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
9700
9751
  const { browser: cliBrowser, ...devOptions } = options;
9701
9752
  const browser = cliBrowser ?? await resolveConfigBrowser(pathOrRemoteUrl || process.cwd(), 'dev') ?? 'chromium';
9702
9753
  if (devOptions.debug || devOptions.author || devOptions.authorMode) {
@@ -9817,6 +9868,7 @@ Cross-browser compatibility
9817
9868
  pid: process.pid,
9818
9869
  noBrowser
9819
9870
  }));
9871
+ markCommandSessionStart('dev');
9820
9872
  const { extensionDev } = await loadExtensionDevelopModule();
9821
9873
  for (const vendor of list){
9822
9874
  const logsOption = devOptions.logs;
@@ -10361,7 +10413,7 @@ Cross-browser compatibility
10361
10413
  'Manifest file not found'
10362
10414
  ];
10363
10415
  function registerPreviewCommand(program) {
10364
- program.command('preview').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.preview).addHelpText('after', '\nAdditional option:\n --no-browser do not launch the browser\n').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 <${NO_SAFARI_BROWSER_TARGETS_HELP}>`, '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').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--output-path <dir>', 'path to an existing unpacked extension directory. Defaults to dist/<browser> when available').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, options, command)=>{
10416
+ program.command('preview').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.preview).addHelpText('after', "\nAdditional option:\n --no-browser stop the browser launch\n" + BROWSER_LAUNCH_HELP_FOOTER).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 <${NO_SAFARI_BROWSER_TARGETS_HELP}>`, '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').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).option('--no-open', NO_OPEN_FLAG_DESCRIPTION).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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--output-path <dir>', 'path to an existing unpacked extension directory. Defaults to dist/<browser> when available').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, options, command)=>{
10365
10417
  const { browser: cliBrowser, ...previewOptions } = options;
10366
10418
  const browser = cliBrowser ?? await resolveConfigBrowser(pathOrRemoteUrl || process.cwd(), 'preview') ?? 'chromium';
10367
10419
  if (previewOptions.debug || previewOptions.author || previewOptions.authorMode) {
@@ -10402,6 +10454,7 @@ Cross-browser compatibility
10402
10454
  const isRemote = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
10403
10455
  if (isRemote) process.env.EXTJS_LIGHT = '1';
10404
10456
  }
10457
+ markCommandSessionStart('preview');
10405
10458
  const { extensionPreview } = await loadExtensionDevelopPreviewModule();
10406
10459
  const previewed = [];
10407
10460
  for (const vendor of list){
@@ -10415,6 +10468,7 @@ Cross-browser compatibility
10415
10468
  browser: vendor,
10416
10469
  chromiumBinary: previewOptions.chromiumBinary,
10417
10470
  geckoBinary: cliGeckoBinary(previewOptions),
10471
+ noOpen: explicitCliValue(command, 'open', false === previewOptions.open),
10418
10472
  startingUrl: previewOptions.startingUrl,
10419
10473
  port: previewOptions.port,
10420
10474
  noBrowser: await resolveNoBrowser(pathOrRemoteUrl || process.cwd(), 'preview'),
@@ -10626,7 +10680,7 @@ Cross-browser compatibility
10626
10680
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 8080;
10627
10681
  }
10628
10682
  function registerStartCommand(program) {
10629
- program.command('start').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.start).addHelpText('after', '\nAdditional options:\n --no-browser do not launch the browser (build still runs)\n --wait wait for ready contract and exit; pair with --output json for machine output\n').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 <${NO_SAFARI_BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).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('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, options, command)=>{
10683
+ program.command('start').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.start).addHelpText('after', "\nAdditional options:\n --no-browser stop the browser launch, the build still runs\n --wait wait for ready contract and exit, pair with --output json for machine output\n" + BROWSER_LAUNCH_HELP_FOOTER).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 <${NO_SAFARI_BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', NO_OPEN_FLAG_DESCRIPTION).option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').addOption(geckoBinaryOption()).addOption(firefoxBinaryAliasOption()).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('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').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('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).action(async (pathOrRemoteUrl, options, command)=>{
10630
10684
  const { browser: cliBrowser, ...startOptions } = options;
10631
10685
  const browser = cliBrowser ?? await resolveConfigBrowser(pathOrRemoteUrl || process.cwd(), 'start') ?? 'chromium';
10632
10686
  if (startOptions.debug || startOptions.author || startOptions.authorMode) {
@@ -10694,6 +10748,7 @@ Cross-browser compatibility
10694
10748
  pid: process.pid,
10695
10749
  noBrowser: await resolveNoBrowser(pathOrRemoteUrl || process.cwd(), 'start')
10696
10750
  }));
10751
+ markCommandSessionStart('start');
10697
10752
  const { extensionBuild } = await loadExtensionDevelopModule();
10698
10753
  for (const vendor of list){
10699
10754
  const logsOption = startOptions.logs;
@@ -10733,6 +10788,7 @@ Cross-browser compatibility
10733
10788
  port: startOptions.port,
10734
10789
  host: startOptions.host,
10735
10790
  noBrowser: false,
10791
+ noOpen: explicitCliValue(command, 'open', false === startOptions.open),
10736
10792
  extensions: parseExtensionsList(startOptions.extensions),
10737
10793
  metadataCommand: 'start',
10738
10794
  logLevel,
@@ -10819,6 +10875,63 @@ Cross-browser compatibility
10819
10875
  }
10820
10876
  return null;
10821
10877
  }
10878
+ const TERMINATION_SIGNALS = [
10879
+ 'SIGINT',
10880
+ 'SIGTERM'
10881
+ ];
10882
+ const FLUSH_DEADLINE_MS = 500;
10883
+ const SIGNAL_NUMBERS = {
10884
+ SIGINT: 2,
10885
+ SIGTERM: 15
10886
+ };
10887
+ function signalExitCode(signal) {
10888
+ return 128 + SIGNAL_NUMBERS[signal];
10889
+ }
10890
+ let handled = false;
10891
+ const ownListeners = new Set();
10892
+ let telemetry_signals_installed = false;
10893
+ async function flushTelemetryWithin(ms) {
10894
+ try {
10895
+ await Promise.race([
10896
+ telemetry.flush(),
10897
+ new Promise((resolve)=>setTimeout(resolve, ms).unref?.())
10898
+ ]);
10899
+ } catch {}
10900
+ }
10901
+ async function handleTerminationSignal(signal, deps) {
10902
+ if (handled) return;
10903
+ handled = true;
10904
+ const counted = deps.sessionStarted();
10905
+ const othersOwn = deps.othersOwnTermination();
10906
+ if (!counted && !othersOwn) deps.markInterrupted(signalExitCode(signal));
10907
+ await deps.flush();
10908
+ if (!othersOwn) await deps.exit(counted ? 0 : signalExitCode(signal));
10909
+ }
10910
+ function defaultDeps() {
10911
+ return {
10912
+ sessionStarted: hasTrackedSessionStart,
10913
+ markInterrupted: (exitCode)=>markCommandFailure(void 0, {
10914
+ code: messaging.Lp.E_INTERRUPTED,
10915
+ exitCode
10916
+ }),
10917
+ flush: ()=>flushTelemetryWithin(FLUSH_DEADLINE_MS),
10918
+ othersOwnTermination: ()=>TERMINATION_SIGNALS.some((signal)=>process.listeners(signal).some((listener)=>!ownListeners.has(listener))),
10919
+ exit: (code)=>exitAfterDrain(code)
10920
+ };
10921
+ }
10922
+ function installTelemetrySignalHandlers() {
10923
+ if (telemetry_signals_installed) return;
10924
+ if (!getTelemetryConsent().enabled) return;
10925
+ telemetry_signals_installed = true;
10926
+ for (const signal of TERMINATION_SIGNALS){
10927
+ const listener = ()=>{
10928
+ handleTerminationSignal(signal, defaultDeps());
10929
+ };
10930
+ ownListeners.add(listener);
10931
+ process.once(signal, listener);
10932
+ }
10933
+ }
10934
+ installTelemetrySignalHandlers();
10822
10935
  const index_cliPackageJson = getCliPackageJson();
10823
10936
  function developVersion() {
10824
10937
  return resolveExtensionDevelopVersion(__dirname, index_cliPackageJson.version);
@@ -1 +1,3 @@
1
+ export declare const BROWSER_LAUNCH_HELP_FOOTER: string;
2
+ export declare const NO_OPEN_FLAG_DESCRIPTION = "launch the browser but do not open a tab for your extension. To stop the browser launch itself, use --no-browser";
1
3
  export declare function resolveNoBrowser(projectPath: string, command: 'dev' | 'start' | 'preview'): Promise<boolean>;
@@ -15,10 +15,13 @@ export declare function getTelemetryConsent(): {
15
15
  enabled: boolean;
16
16
  source: TelemetrySource;
17
17
  };
18
+ export declare function invokedCommand(): string;
18
19
  export declare function setTelemetryConsent(value: 'enabled' | 'disabled'): {
19
20
  ok: boolean;
20
21
  path: string | null;
21
22
  };
23
+ export declare function markCommandSessionStart(command?: KnownCommand): void;
24
+ export declare function hasTrackedSessionStart(): boolean;
22
25
  export declare function markCommandSuccess(command?: KnownCommand): void;
23
26
  export interface CommandFailureDetails {
24
27
  code?: unknown;
@@ -0,0 +1,14 @@
1
+ export type TerminationSignal = 'SIGINT' | 'SIGTERM';
2
+ export declare const TERMINATION_SIGNALS: readonly TerminationSignal[];
3
+ export declare function signalExitCode(signal: TerminationSignal): number;
4
+ export interface TerminationSignalDeps {
5
+ sessionStarted: () => boolean;
6
+ markInterrupted: (exitCode: number) => void;
7
+ flush: () => Promise<void>;
8
+ othersOwnTermination: () => boolean;
9
+ exit: (code: number) => void | Promise<void>;
10
+ }
11
+ export declare function __resetTelemetrySignalsForTest(): void;
12
+ export declare function flushTelemetryWithin(ms: number): Promise<void>;
13
+ export declare function handleTerminationSignal(signal: TerminationSignal, deps: TerminationSignalDeps): Promise<void>;
14
+ export declare function installTelemetrySignalHandlers(): void;
@@ -5,6 +5,9 @@ export type TelemetryProps = {
5
5
  version: string;
6
6
  template?: string;
7
7
  source?: string;
8
+ session?: 'started';
9
+ code?: string;
10
+ exit_code?: number;
8
11
  };
9
12
  export type TelemetrySource = 'env' | 'flag' | 'config' | 'ci' | 'default';
10
13
  type TelemetryInit = {
@@ -53,6 +56,7 @@ export declare class Telemetry {
53
56
  private buffer;
54
57
  constructor(init: TelemetryInit);
55
58
  get isEnabled(): boolean;
59
+ disable(): void;
56
60
  track(event: TelemetryEvent, props: TelemetryProps): void;
57
61
  flush(): Promise<void>;
58
62
  shutdown(): void;
@@ -1,6 +1,6 @@
1
1
  export declare const DEFAULT_TEMPLATE = "typescript";
2
2
  export declare const BUNDLED_TEMPLATES: readonly string[];
3
- export declare const TEMPLATE_CATALOG_URL = "https://github.com/extension-js/examples/tree/cb6a25377bd9516a1e55447a2010537019851ab2/examples";
3
+ export declare const TEMPLATE_CATALOG_URL = "https://github.com/extension-js/examples/tree/d951b735ee5fbe904bba0c47d688527300d10e32/examples";
4
4
  export interface TemplateGroup {
5
5
  title: string;
6
6
  summary: string;
@@ -1,3 +1,3 @@
1
1
  export declare const TEMPLATE_CORPUS_REPO = "extension-js/examples";
2
- export declare const TEMPLATE_CORPUS_REF = "cb6a25377bd9516a1e55447a2010537019851ab2";
2
+ export declare const TEMPLATE_CORPUS_REF = "d951b735ee5fbe904bba0c47d688527300d10e32";
3
3
  export declare const TEMPLATE_CORPUS_SLUGS: readonly string[];
package/package.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "extension": "./bin/extension.cjs"
39
39
  },
40
40
  "name": "extension",
41
- "version": "4.1.15",
41
+ "version": "4.1.16",
42
42
  "description": "The cross-browser extension framework. Build Chrome, Edge, Firefox, and Safari extensions with no build configuration.",
43
43
  "homepage": "https://extension.js.org/",
44
44
  "bugs": {
@@ -106,9 +106,9 @@
106
106
  "vivaldi-location2": "2.1.1",
107
107
  "waterfox-location": "2.1.1",
108
108
  "yandex-location": "2.1.1",
109
- "extension-create": "4.1.15",
110
- "extension-develop": "4.1.15",
111
- "extension-install": "4.1.15",
109
+ "extension-create": "4.1.16",
110
+ "extension-develop": "4.1.16",
111
+ "extension-install": "4.1.16",
112
112
  "commander": "^15.0.0",
113
113
  "pintor": "0.3.0",
114
114
  "semver": "^7.7.3",