extension 4.0.11 → 4.0.12

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/README.md CHANGED
@@ -23,7 +23,7 @@ cd my-extension
23
23
  npm run dev
24
24
  ```
25
25
 
26
- Works with `npm`, `pnpm`, `yarn`, `bun`, and `deno` (run the CLI with `deno run -A npm:extension@latest ...`).
26
+ Works with `npm`, `pnpm`, `yarn`, `bun`, and `deno`.
27
27
 
28
28
  [Documentation](https://extension.js.org) · [Templates](https://templates.extension.dev) · [Examples](https://github.com/extension-js/examples) · [Discord](https://discord.gg/v9h2RgeTSN)
29
29
 
package/dist/browsers.cjs CHANGED
@@ -1219,18 +1219,9 @@ var __webpack_modules__ = {
1219
1219
  extension
1220
1220
  ];
1221
1221
  }
1222
- function publishUserExtensionRoot(extension, setExtensionRoot) {
1223
- try {
1224
- const root = [
1225
- ...toExtensionLoadList(extension)
1226
- ].reverse().find((entry)=>'string' == typeof entry);
1227
- if (root && setExtensionRoot) setExtensionRoot(String(root));
1228
- } catch {}
1229
- }
1230
1222
  __webpack_require__.d(__webpack_exports__, {
1231
1223
  fT: ()=>toExtensionLoadList,
1232
1224
  pA: ()=>pickSharedBrowserRuntimeOptions,
1233
- sl: ()=>publishUserExtensionRoot,
1234
1225
  zU: ()=>buildBrowserLaunchRequest
1235
1226
  });
1236
1227
  },
@@ -1693,45 +1684,10 @@ var __webpack_modules__ = {
1693
1684
  "./browsers/run-chromium/chromium-context/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1694
1685
  function createChromiumContext() {
1695
1686
  let controller;
1696
- let cdpPort;
1697
- let extensionRoot;
1698
- let swRel;
1699
- let swAbs;
1700
- let pendingReason;
1701
- const readyCbs = [];
1702
1687
  return {
1703
1688
  getController: ()=>controller,
1704
- onControllerReady: (cb)=>{
1705
- if (controller && cdpPort) cb(controller, cdpPort);
1706
- else readyCbs.push(cb);
1707
- },
1708
- setController: (c, port)=>{
1689
+ setController: (c)=>{
1709
1690
  controller = c;
1710
- cdpPort = port;
1711
- for (const cb of readyCbs)cb(c, port);
1712
- readyCbs.length = 0;
1713
- },
1714
- getPorts: ()=>({
1715
- cdpPort
1716
- }),
1717
- getExtensionRoot: ()=>extensionRoot,
1718
- setExtensionRoot: (root)=>{
1719
- extensionRoot = root;
1720
- },
1721
- setServiceWorkerPaths: (rel, abs)=>{
1722
- swRel = rel;
1723
- swAbs = abs;
1724
- },
1725
- getServiceWorkerPaths: ()=>({
1726
- relativePath: swRel,
1727
- absolutePath: swAbs
1728
- }),
1729
- setPendingReloadReason: (r)=>{
1730
- pendingReason = r;
1731
- },
1732
- getPendingReloadReason: ()=>pendingReason,
1733
- clearPendingReloadReason: ()=>{
1734
- pendingReason = void 0;
1735
1691
  }
1736
1692
  };
1737
1693
  }
@@ -2755,7 +2711,6 @@ var __webpack_modules__ = {
2755
2711
  } catch {}
2756
2712
  console.log(messages.iL(browser, browserBinaryLocation, browserVersionLine));
2757
2713
  const extensionsToLoad = (0, runtime_options.fT)(this.options.extension);
2758
- (0, runtime_options.sl)(extensionsToLoad, this.ctx.setExtensionRoot);
2759
2714
  const resolvedBrowserVersion = /(\d+(?:\.\d+){0,3})/.exec(browserVersionLine || '')?.[1];
2760
2715
  for (const extPath of extensionsToLoad)try {
2761
2716
  const m = JSON.parse(external_fs_.readFileSync(external_path_.join(String(extPath), 'manifest.json'), 'utf8'));
@@ -2853,7 +2808,7 @@ var __webpack_modules__ = {
2853
2808
  mod.setupCdpAfterLaunch(compilation, cdpConfig, chromiumConfig, pipeStreams),
2854
2809
  new Promise((_, reject)=>setTimeout(()=>reject(new Error(`CDP setup did not complete within ${CDP_SETUP_TIMEOUT_MS / 1000}s. Chrome likely rejected the extension at launch — open chrome://extensions in the dev browser window for the exact error. Common causes: MV3 content_security_policy with 'unsafe-inline', manifest keys Chrome does not support, or manifest references to files missing from the output. Reload/HMR cannot attach until this is fixed.`)), CDP_SETUP_TIMEOUT_MS).unref?.())
2855
2810
  ]);
2856
- if (cdpConfig.cdpController) this.ctx.setController(cdpConfig.cdpController, selectedPort);
2811
+ if (cdpConfig.cdpController) this.ctx.setController(cdpConfig.cdpController);
2857
2812
  }
2858
2813
  } catch (error) {
2859
2814
  const message = String(error && error.message);
@@ -3094,45 +3049,10 @@ var __webpack_modules__ = {
3094
3049
  "./browsers/run-firefox/firefox-context/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3095
3050
  function createFirefoxContext() {
3096
3051
  let controller;
3097
- let rdpPort;
3098
- const readyCbs = [];
3099
- let extensionRoot;
3100
- let swRel;
3101
- let swAbs;
3102
- let pendingReason;
3103
3052
  return {
3104
3053
  getController: ()=>controller,
3105
- onControllerReady: (cb)=>{
3106
- if (controller && rdpPort) cb(controller, rdpPort);
3107
- else readyCbs.push(cb);
3108
- },
3109
- setController: (c, port)=>{
3054
+ setController: (c)=>{
3110
3055
  controller = c;
3111
- rdpPort = port;
3112
- for (const cb of readyCbs)cb(c, port);
3113
- readyCbs.length = 0;
3114
- },
3115
- getPorts: ()=>({
3116
- rdpPort
3117
- }),
3118
- setExtensionRoot: (root)=>{
3119
- extensionRoot = root;
3120
- },
3121
- getExtensionRoot: ()=>extensionRoot,
3122
- setServiceWorkerPaths: (rel, abs)=>{
3123
- swRel = rel;
3124
- swAbs = abs;
3125
- },
3126
- getServiceWorkerPaths: ()=>({
3127
- relativePath: swRel,
3128
- absolutePath: swAbs
3129
- }),
3130
- setPendingReloadReason: (r)=>{
3131
- pendingReason = r;
3132
- },
3133
- getPendingReloadReason: ()=>pendingReason,
3134
- clearPendingReloadReason: ()=>{
3135
- pendingReason = void 0;
3136
3056
  },
3137
3057
  logger: void 0,
3138
3058
  didLaunch: false
@@ -4150,7 +4070,6 @@ var __webpack_modules__ = {
4150
4070
  }
4151
4071
  invalidateConnectionScopedCaches() {
4152
4072
  this.cachedAddonsActor = void 0;
4153
- this.cachedSupportsReload = null;
4154
4073
  }
4155
4074
  async installAddons(compilation) {
4156
4075
  const { devtools } = this.options;
@@ -4237,7 +4156,6 @@ var __webpack_modules__ = {
4237
4156
  remote_firefox_define_property(this, "client", null);
4238
4157
  remote_firefox_define_property(this, "loggingAttached", false);
4239
4158
  remote_firefox_define_property(this, "cachedAddonsActor", void 0);
4240
- remote_firefox_define_property(this, "cachedSupportsReload", null);
4241
4159
  remote_firefox_define_property(this, "lastInstalledAddonPath", void 0);
4242
4160
  remote_firefox_define_property(this, "derivedExtensionId", void 0);
4243
4161
  this.options = configOptions;
@@ -4533,8 +4451,7 @@ var __webpack_modules__ = {
4533
4451
  allowExec: true
4534
4452
  }) || '';
4535
4453
  } catch {}
4536
- const extensionsToLoad = (0, runtime_options.fT)(this.host.extension);
4537
- (0, runtime_options.sl)(extensionsToLoad, 'function' == typeof this.ctx.setExtensionRoot ? this.ctx.setExtensionRoot : void 0);
4454
+ (0, runtime_options.fT)(this.host.extension);
4538
4455
  const desiredDebugPort = (0, shared_utils.jl)(this.host.port, this.host.instanceId);
4539
4456
  const debugPort = await (0, shared_utils.aY)(desiredDebugPort);
4540
4457
  (0, instance_registry.UF)(this.host.instanceId, {
@@ -4567,7 +4484,7 @@ var __webpack_modules__ = {
4567
4484
  this.wireChildLifecycle();
4568
4485
  const ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
4569
4486
  this.host.rdpController = ctrl;
4570
- this.ctx.setController(ctrl, debugPort);
4487
+ this.ctx.setController(ctrl);
4571
4488
  this.scheduleWatchTimeout();
4572
4489
  try {
4573
4490
  if ('true' === process.env.EXTENSION_AUTHOR_MODE) {
package/dist/cli.cjs CHANGED
@@ -1332,18 +1332,9 @@ var __webpack_modules__ = {
1332
1332
  extension
1333
1333
  ];
1334
1334
  }
1335
- function publishUserExtensionRoot(extension, setExtensionRoot) {
1336
- try {
1337
- const root = [
1338
- ...toExtensionLoadList(extension)
1339
- ].reverse().find((entry)=>'string' == typeof entry);
1340
- if (root && setExtensionRoot) setExtensionRoot(String(root));
1341
- } catch {}
1342
- }
1343
1335
  __webpack_require__.d(__webpack_exports__, {
1344
1336
  fT: ()=>toExtensionLoadList,
1345
1337
  pA: ()=>pickSharedBrowserRuntimeOptions,
1346
- sl: ()=>publishUserExtensionRoot,
1347
1338
  zU: ()=>buildBrowserLaunchRequest
1348
1339
  });
1349
1340
  },
@@ -1804,45 +1795,10 @@ var __webpack_modules__ = {
1804
1795
  "./browsers/run-chromium/chromium-context/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1805
1796
  function createChromiumContext() {
1806
1797
  let controller;
1807
- let cdpPort;
1808
- let extensionRoot;
1809
- let swRel;
1810
- let swAbs;
1811
- let pendingReason;
1812
- const readyCbs = [];
1813
1798
  return {
1814
1799
  getController: ()=>controller,
1815
- onControllerReady: (cb)=>{
1816
- if (controller && cdpPort) cb(controller, cdpPort);
1817
- else readyCbs.push(cb);
1818
- },
1819
- setController: (c, port)=>{
1800
+ setController: (c)=>{
1820
1801
  controller = c;
1821
- cdpPort = port;
1822
- for (const cb of readyCbs)cb(c, port);
1823
- readyCbs.length = 0;
1824
- },
1825
- getPorts: ()=>({
1826
- cdpPort
1827
- }),
1828
- getExtensionRoot: ()=>extensionRoot,
1829
- setExtensionRoot: (root)=>{
1830
- extensionRoot = root;
1831
- },
1832
- setServiceWorkerPaths: (rel, abs)=>{
1833
- swRel = rel;
1834
- swAbs = abs;
1835
- },
1836
- getServiceWorkerPaths: ()=>({
1837
- relativePath: swRel,
1838
- absolutePath: swAbs
1839
- }),
1840
- setPendingReloadReason: (r)=>{
1841
- pendingReason = r;
1842
- },
1843
- getPendingReloadReason: ()=>pendingReason,
1844
- clearPendingReloadReason: ()=>{
1845
- pendingReason = void 0;
1846
1802
  }
1847
1803
  };
1848
1804
  }
@@ -2866,7 +2822,6 @@ var __webpack_modules__ = {
2866
2822
  } catch {}
2867
2823
  console.log(messages.iL(browser, browserBinaryLocation, browserVersionLine));
2868
2824
  const extensionsToLoad = (0, runtime_options.fT)(this.options.extension);
2869
- (0, runtime_options.sl)(extensionsToLoad, this.ctx.setExtensionRoot);
2870
2825
  const resolvedBrowserVersion = /(\d+(?:\.\d+){0,3})/.exec(browserVersionLine || '')?.[1];
2871
2826
  for (const extPath of extensionsToLoad)try {
2872
2827
  const m = JSON.parse(external_fs_.readFileSync(external_path_.join(String(extPath), 'manifest.json'), 'utf8'));
@@ -2964,7 +2919,7 @@ var __webpack_modules__ = {
2964
2919
  mod.setupCdpAfterLaunch(compilation, cdpConfig, chromiumConfig, pipeStreams),
2965
2920
  new Promise((_, reject)=>setTimeout(()=>reject(new Error(`CDP setup did not complete within ${CDP_SETUP_TIMEOUT_MS / 1000}s. Chrome likely rejected the extension at launch — open chrome://extensions in the dev browser window for the exact error. Common causes: MV3 content_security_policy with 'unsafe-inline', manifest keys Chrome does not support, or manifest references to files missing from the output. Reload/HMR cannot attach until this is fixed.`)), CDP_SETUP_TIMEOUT_MS).unref?.())
2966
2921
  ]);
2967
- if (cdpConfig.cdpController) this.ctx.setController(cdpConfig.cdpController, selectedPort);
2922
+ if (cdpConfig.cdpController) this.ctx.setController(cdpConfig.cdpController);
2968
2923
  }
2969
2924
  } catch (error) {
2970
2925
  const message = String(error && error.message);
@@ -3205,45 +3160,10 @@ var __webpack_modules__ = {
3205
3160
  "./browsers/run-firefox/firefox-context/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
3206
3161
  function createFirefoxContext() {
3207
3162
  let controller;
3208
- let rdpPort;
3209
- const readyCbs = [];
3210
- let extensionRoot;
3211
- let swRel;
3212
- let swAbs;
3213
- let pendingReason;
3214
3163
  return {
3215
3164
  getController: ()=>controller,
3216
- onControllerReady: (cb)=>{
3217
- if (controller && rdpPort) cb(controller, rdpPort);
3218
- else readyCbs.push(cb);
3219
- },
3220
- setController: (c, port)=>{
3165
+ setController: (c)=>{
3221
3166
  controller = c;
3222
- rdpPort = port;
3223
- for (const cb of readyCbs)cb(c, port);
3224
- readyCbs.length = 0;
3225
- },
3226
- getPorts: ()=>({
3227
- rdpPort
3228
- }),
3229
- setExtensionRoot: (root)=>{
3230
- extensionRoot = root;
3231
- },
3232
- getExtensionRoot: ()=>extensionRoot,
3233
- setServiceWorkerPaths: (rel, abs)=>{
3234
- swRel = rel;
3235
- swAbs = abs;
3236
- },
3237
- getServiceWorkerPaths: ()=>({
3238
- relativePath: swRel,
3239
- absolutePath: swAbs
3240
- }),
3241
- setPendingReloadReason: (r)=>{
3242
- pendingReason = r;
3243
- },
3244
- getPendingReloadReason: ()=>pendingReason,
3245
- clearPendingReloadReason: ()=>{
3246
- pendingReason = void 0;
3247
3167
  },
3248
3168
  logger: void 0,
3249
3169
  didLaunch: false
@@ -4261,7 +4181,6 @@ var __webpack_modules__ = {
4261
4181
  }
4262
4182
  invalidateConnectionScopedCaches() {
4263
4183
  this.cachedAddonsActor = void 0;
4264
- this.cachedSupportsReload = null;
4265
4184
  }
4266
4185
  async installAddons(compilation) {
4267
4186
  const { devtools } = this.options;
@@ -4348,7 +4267,6 @@ var __webpack_modules__ = {
4348
4267
  remote_firefox_define_property(this, "client", null);
4349
4268
  remote_firefox_define_property(this, "loggingAttached", false);
4350
4269
  remote_firefox_define_property(this, "cachedAddonsActor", void 0);
4351
- remote_firefox_define_property(this, "cachedSupportsReload", null);
4352
4270
  remote_firefox_define_property(this, "lastInstalledAddonPath", void 0);
4353
4271
  remote_firefox_define_property(this, "derivedExtensionId", void 0);
4354
4272
  this.options = configOptions;
@@ -4644,8 +4562,7 @@ var __webpack_modules__ = {
4644
4562
  allowExec: true
4645
4563
  }) || '';
4646
4564
  } catch {}
4647
- const extensionsToLoad = (0, runtime_options.fT)(this.host.extension);
4648
- (0, runtime_options.sl)(extensionsToLoad, 'function' == typeof this.ctx.setExtensionRoot ? this.ctx.setExtensionRoot : void 0);
4565
+ (0, runtime_options.fT)(this.host.extension);
4649
4566
  const desiredDebugPort = (0, shared_utils.jl)(this.host.port, this.host.instanceId);
4650
4567
  const debugPort = await (0, shared_utils.aY)(desiredDebugPort);
4651
4568
  (0, instance_registry.UF)(this.host.instanceId, {
@@ -4678,7 +4595,7 @@ var __webpack_modules__ = {
4678
4595
  this.wireChildLifecycle();
4679
4596
  const ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
4680
4597
  this.host.rdpController = ctrl;
4681
- this.ctx.setController(ctrl, debugPort);
4598
+ this.ctx.setController(ctrl);
4682
4599
  this.scheduleWatchTimeout();
4683
4600
  try {
4684
4601
  if ('true' === process.env.EXTENSION_AUTHOR_MODE) {
@@ -5244,7 +5161,7 @@ Troubleshooting
5244
5161
  Non-Interactive / Auto Mode (CI)
5245
5162
  - Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
5246
5163
  Useful when ${messages_code('pnpm extension dev')} would otherwise hang under Rspack watch.
5247
- Example: ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --source ${messages_arg('https://example.com')}
5164
+ Example: ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --starting-url ${messages_arg('https://example.com')}
5248
5165
  - Optional: ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
5249
5166
 
5250
5167
  Docker / Devcontainers / Codespaces
@@ -6669,12 +6586,64 @@ Cross-Browser Compatibility
6669
6586
  const values = String(raw).split(',').map((value)=>value.trim()).filter((value)=>value.length > 0);
6670
6587
  return values.length > 0 ? values : void 0;
6671
6588
  }
6589
+ const POLL_INTERVAL_MS = 2000;
6590
+ const HARD_EXIT_GRACE_MS = 5000;
6591
+ function isProcessAlive(pid) {
6592
+ try {
6593
+ process.kill(pid, 0);
6594
+ return true;
6595
+ } catch (error) {
6596
+ return error?.code === 'EPERM';
6597
+ }
6598
+ }
6599
+ function setupParentWatchdog(parentPid, options) {
6600
+ const log = options?.log ?? console.error;
6601
+ const pollIntervalMs = options?.pollIntervalMs ?? POLL_INTERVAL_MS;
6602
+ const shutdown = options?.onDeath ?? (()=>{
6603
+ const hardExit = setTimeout(()=>process.exit(1), HARD_EXIT_GRACE_MS);
6604
+ hardExit.unref?.();
6605
+ try {
6606
+ process.kill(process.pid, 'SIGTERM');
6607
+ } catch {
6608
+ process.exit(1);
6609
+ }
6610
+ });
6611
+ const onParentDeath = ()=>{
6612
+ log(`[Extension.js] Parent process ${parentPid} is gone (--parent-pid). Shutting down.`);
6613
+ shutdown();
6614
+ };
6615
+ if (!isProcessAlive(parentPid)) {
6616
+ onParentDeath();
6617
+ return ()=>{};
6618
+ }
6619
+ const timer = setInterval(()=>{
6620
+ if (isProcessAlive(parentPid)) return;
6621
+ clearInterval(timer);
6622
+ onParentDeath();
6623
+ }, pollIntervalMs);
6624
+ timer.unref?.();
6625
+ return ()=>clearInterval(timer);
6626
+ }
6627
+ function parseParentPid(value) {
6628
+ if (null == value || '' === value) return;
6629
+ const pid = Number.parseInt(String(value), 10);
6630
+ if (!Number.isInteger(pid) || pid <= 0) return;
6631
+ return pid;
6632
+ }
6672
6633
  function registerDevCommand(program) {
6673
- 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\n --wait-format pretty|json output for wait mode\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 <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | safari | webkit-based>', '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').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('--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('--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-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('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').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` (runs arbitrary code in a context; writes a 0600 session token)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
6634
+ 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\n --wait-format pretty|json output for wait mode\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 <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | safari | webkit-based>', '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').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('--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('--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('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').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` (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 function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
6674
6635
  if (devOptions.author || devOptions['authorMode']) {
6675
6636
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6676
6637
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
6677
6638
  }
6639
+ if (void 0 !== devOptions.parentPid) {
6640
+ const parentPid = parseParentPid(devOptions.parentPid);
6641
+ if (void 0 === parentPid) {
6642
+ console.error(unhandledError(`--parent-pid expects a positive integer pid, got: ${devOptions.parentPid}`));
6643
+ process.exit(1);
6644
+ }
6645
+ setupParentWatchdog(parentPid);
6646
+ }
6678
6647
  const list = vendors(browser);
6679
6648
  validateVendorsOrExit(list, (invalid, supported)=>{
6680
6649
  console.error(unsupportedBrowserFlag(invalid, supported));
@@ -6775,7 +6744,7 @@ Cross-Browser Compatibility
6775
6744
  }
6776
6745
  var run_only = __webpack_require__("./browsers/run-only.ts");
6777
6746
  function registerStartCommand(program) {
6778
- 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\n --wait-format pretty|json output for wait mode\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 <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`', parseOptionalBoolean).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('--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]', '[experimental] 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('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[experimental] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
6747
+ 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\n --wait-format pretty|json output for wait mode\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 <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`', 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').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('--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]', '[experimental] 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('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[experimental] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...startOptions }) {
6779
6748
  if (startOptions.author || startOptions.authorMode) {
6780
6749
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6781
6750
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -6814,6 +6783,7 @@ Cross-Browser Compatibility
6814
6783
  await extensionBuild(pathOrRemoteUrl, {
6815
6784
  browser: vendor,
6816
6785
  exitOnError: true,
6786
+ metadataCommand: 'start',
6817
6787
  polyfill: startOptions.polyfill?.toString() !== 'false',
6818
6788
  install: startOptions.install,
6819
6789
  extensions: parseExtensionsList(startOptions.extensions),
@@ -6890,7 +6860,7 @@ Cross-Browser Compatibility
6890
6860
  });
6891
6861
  }
6892
6862
  function registerBuildCommand(program) {
6893
- program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | safari | webkit-based>', 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds a Safari app via Xcode (macOS only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`', parseOptionalBoolean).option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`', parseOptionalBoolean).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`', parseOptionalBoolean).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('--mode <development|production|none>', 'bundler mode override (also sets NODE_ENV). Defaults to `production`').option('--open [boolean]', 'open the built Safari app after packaging (safari targets only). Defaults to `false`', parseOptionalBoolean).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('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
6863
+ program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | safari | webkit-based>', 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds a Safari app via Xcode (macOS only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`', parseOptionalBoolean).option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`', parseOptionalBoolean).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`', parseOptionalBoolean).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('--mode <development|production|none>', 'bundler mode override (also sets NODE_ENV). Defaults to `production`').option('--open [boolean]', 'open the built Safari app after packaging (safari targets only). Defaults to `false`', parseOptionalBoolean).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('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
6894
6864
  if (buildOptions.author || buildOptions['authorMode']) {
6895
6865
  process.env.EXTENSION_AUTHOR_MODE = 'true';
6896
6866
  if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
@@ -7247,7 +7217,7 @@ Cross-Browser Compatibility
7247
7217
  opts
7248
7218
  });
7249
7219
  });
7250
- commonOptions(program.command('inspect').arguments('[project-path]').description('Inspect a page/content DOM via the agent bridge (CDP-free; requires --allow-control). For closed shadow roots use dev --source --deep-dom.').option('--context <content|page|popup|options|sidebar|devtools>', 'what to inspect: content/page (needs --tab) or an open surface (default content)').option('--tab <id>', 'tab id to inspect (required for content/page)').option('--include <list>', 'comma-separated: html,summary (default summary)').option('--max-bytes <n>', 'cap on returned HTML bytes (default 262144)').option('--with-console [n]', 'also include the last n console lines for the target (default 20)')).action(async function(projectPathArg, opts) {
7220
+ commonOptions(program.command('inspect').arguments('[project-path]').description('Inspect a page/content DOM via the agent bridge (CDP-free; requires --allow-control). For closed shadow roots use CDP-based inspection against the ready.json cdpPort.').option('--context <content|page|popup|options|sidebar|devtools>', 'what to inspect: content/page (needs --tab) or an open surface (default content)').option('--tab <id>', 'tab id to inspect (required for content/page)').option('--include <list>', 'comma-separated: html,summary (default summary)').option('--max-bytes <n>', 'cap on returned HTML bytes (default 262144)').option('--with-console [n]', 'also include the last n console lines for the target (default 20)')).action(async function(projectPathArg, opts) {
7251
7221
  const include = opts.include ? opts.include.split(',').map((s)=>s.trim()).filter(Boolean) : [
7252
7222
  'summary'
7253
7223
  ];
@@ -91,7 +91,6 @@ export declare function cdpClientConnectionError(error: string): string;
91
91
  export declare function cdpClientBrowserConnectionEstablished(): string;
92
92
  export declare function cdpClientConnectionClosed(): string;
93
93
  export declare function cdpClientLoadEventTimeout(): string;
94
- export declare function cdpClientExtensionReloadFailed(extensionId: string, error: string): string;
95
94
  export declare function cdpClientExtensionUnloadFailed(extensionId: string, error: string): string;
96
95
  export declare function cdpClientExtensionInfoFailed(extensionId: string, error: string): string;
97
96
  export declare function cdpClientExtensionLoadFailed(path: string, error: string): string;
@@ -9,4 +9,3 @@ export type BrowserLaunchRequest = Pick<PluginInterface & BrowserConfig, 'browse
9
9
  };
10
10
  export declare function buildBrowserLaunchRequest<T extends object = {}>(options: BrowserLaunchRequestSource, mode: 'development' | 'production' | 'none', extras?: T): BrowserLaunchRequest & T;
11
11
  export declare function toExtensionLoadList(extension: PluginInterface['extension']): string[];
12
- export declare function publishUserExtensionRoot(extension: PluginInterface['extension'], setExtensionRoot?: (root?: string) => void): void;
@@ -57,7 +57,7 @@ export interface BrowserController {
57
57
  /**
58
58
  * Launch a browser with the given extension(s) loaded.
59
59
  *
60
- * Returns a `BrowserController` that provides reload and unified-logging control.
60
+ * Returns a `BrowserController` that provides unified-logging control.
61
61
  * This is the primary entry point for the CLI orchestration layer — it replaces
62
62
  * the old BrowsersPlugin that lived inside the bundler.
63
63
  */
@@ -1,20 +1,6 @@
1
1
  import type { CDPExtensionController } from '../cdp/cdp-extension-controller';
2
2
  export type ChromiumContext = {
3
3
  getController(): CDPExtensionController | undefined;
4
- onControllerReady(cb: (c: CDPExtensionController, port: number) => void): void;
5
- setController(c: CDPExtensionController, port: number): void;
6
- getPorts(): {
7
- cdpPort?: number;
8
- };
9
- getExtensionRoot(): string | undefined;
10
- setExtensionRoot(root?: string): void;
11
- setServiceWorkerPaths(rel?: string, abs?: string): void;
12
- getServiceWorkerPaths(): {
13
- relativePath?: string;
14
- absolutePath?: string;
15
- };
16
- setPendingReloadReason(reason?: 'manifest' | 'locales' | 'sw'): void;
17
- getPendingReloadReason(): 'manifest' | 'locales' | 'sw' | undefined;
18
- clearPendingReloadReason(): void;
4
+ setController(c: CDPExtensionController): void;
19
5
  };
20
6
  export declare function createChromiumContext(): ChromiumContext;
@@ -33,10 +33,9 @@ export type CdpEvent = ConsoleAPICalledEvent | LogEntryAddedEvent | {
33
33
  method: string;
34
34
  params?: unknown;
35
35
  };
36
- export type PendingReason = 'manifest' | 'locales' | 'sw';
37
36
  export type Logger = BrowserLogger;
38
37
  /**
39
- * Options consumed by Chromium plugins (launch/logger/reload/inspection).
38
+ * Options consumed by Chromium plugins (launch/logger/inspection).
40
39
  * Narrowed from PluginInterface and specialized for Chromium path.
41
40
  */
42
41
  export interface ChromiumLaunchOptions extends Pick<PluginInterface, 'extension' | 'browser' | 'noOpen' | 'browserFlags' | 'excludeBrowserFlags' | 'profile' | 'persistProfile' | 'keepProfileChanges' | 'copyFromProfile' | 'preferences' | 'startingUrl' | 'chromiumBinary' | 'instanceId' | 'port' | 'dryRun' | 'logLevel' | 'logContexts' | 'logFormat' | 'logTimestamps' | 'logColor' | 'logUrl' | 'logTab'> {
@@ -190,11 +189,3 @@ export type ExtensionRootTreeResult = {
190
189
  truncated: boolean;
191
190
  tree: unknown;
192
191
  };
193
- declare global {
194
- /**
195
- * Dev-only hooks the Chromium inspection flow installs on `globalThis` to
196
- * coordinate content-reload snapshots across the watch loop.
197
- */
198
- var __EXTJS_ON_CHROMIUM_CONTENT_RELOADED__: (() => Promise<void> | void) | undefined;
199
- var __EXTJS_PENDING_CHROMIUM_CONTENT_RELOAD__: unknown;
200
- }
@@ -1,23 +1,8 @@
1
1
  import type { BrowserLogger } from '../../browsers-types';
2
2
  import type { FirefoxRDPController } from '../rdp/rdp-extension-controller';
3
- export type PendingReason = 'manifest' | 'locales' | 'sw';
4
3
  export type FirefoxContext = {
5
4
  getController(): FirefoxRDPController | undefined;
6
- onControllerReady(cb: (c: FirefoxRDPController, port: number) => void): void;
7
- setController(c: FirefoxRDPController, port: number): void;
8
- getPorts(): {
9
- rdpPort?: number;
10
- };
11
- setExtensionRoot(root?: string): void;
12
- getExtensionRoot(): string | undefined;
13
- setServiceWorkerPaths(rel?: string, abs?: string): void;
14
- getServiceWorkerPaths(): {
15
- relativePath?: string;
16
- absolutePath?: string;
17
- };
18
- setPendingReloadReason(reason?: PendingReason): void;
19
- getPendingReloadReason(): PendingReason | undefined;
20
- clearPendingReloadReason(): void;
5
+ setController(c: FirefoxRDPController): void;
21
6
  logger?: BrowserLogger;
22
7
  didLaunch?: boolean;
23
8
  };
@@ -5,7 +5,6 @@ export declare class RemoteFirefox {
5
5
  private client;
6
6
  private loggingAttached;
7
7
  private cachedAddonsActor;
8
- private cachedSupportsReload;
9
8
  private lastInstalledAddonPath;
10
9
  private derivedExtensionId;
11
10
  private selectPrimaryAddonPath;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Exit this process when the given parent pid dies (`--parent-pid`).
3
+ *
4
+ * Wrappers that spawn `extension dev` otherwise have to own the whole
5
+ * process group to guarantee teardown; when a wrapper dies HARD (SIGKILL,
6
+ * crash, CI reaper), the orphaned dev keeps watching and compiling, and the
7
+ * next session's builds race it inside one dist. Watching the parent pid
8
+ * directly closes that hole: on death we deliver SIGTERM to ourselves so the
9
+ * regular cleanup handlers (dev server stop, port manager, browser plugins)
10
+ * run, with a hard exit as backstop.
11
+ *
12
+ * Returns a cancel function.
13
+ */
14
+ export declare function setupParentWatchdog(parentPid: number, options?: {
15
+ log?: (message: string) => void;
16
+ onDeath?: () => void;
17
+ pollIntervalMs?: number;
18
+ }): () => void;
19
+ export declare function parseParentPid(value: unknown): number | undefined;
package/package.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "extension": "./bin/extension.cjs"
39
39
  },
40
40
  "name": "extension",
41
- "version": "4.0.11",
41
+ "version": "4.0.12",
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": {
@@ -109,9 +109,9 @@
109
109
  "vivaldi-location2": "2.1.0",
110
110
  "waterfox-location": "2.1.0",
111
111
  "yandex-location": "2.1.0",
112
- "extension-create": "4.0.11",
113
- "extension-develop": "4.0.11",
114
- "extension-install": "4.0.11",
112
+ "extension-create": "4.0.12",
113
+ "extension-develop": "4.0.12",
114
+ "extension-install": "4.0.12",
115
115
  "commander": "^15.0.0",
116
116
  "pintor": "0.3.0",
117
117
  "semver": "^7.7.3",