extension 4.1.20 → 4.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +18 -16
  2. package/dist/{84.cjs → 597.cjs} +80 -2
  3. package/dist/browsers.cjs +408 -103
  4. package/dist/cli.cjs +558 -148
  5. package/dist/extension/browsers/__spec__/safari-fake-tools.d.ts +27 -0
  6. package/dist/extension/browsers/browsers-lib/messages.d.ts +2 -1
  7. package/dist/extension/browsers/browsers-lib/process-teardown.d.ts +3 -0
  8. package/dist/extension/browsers/browsers-lib/ready-stamp.d.ts +1 -0
  9. package/dist/extension/browsers/browsers-lib/resolve-live-pid.d.ts +25 -0
  10. package/dist/extension/browsers/browsers-lib/write-json-atomic.d.ts +1 -0
  11. package/dist/extension/browsers/browsers-types.d.ts +1 -1
  12. package/dist/extension/browsers/index.d.ts +1 -1
  13. package/dist/extension/browsers/run-chromium/cdp/cdp-extension-controller/index.d.ts +2 -0
  14. package/dist/extension/browsers/run-chromium/cdp/ensure-developer-mode.d.ts +12 -0
  15. package/dist/extension/browsers/run-firefox/firefox-launch/index.d.ts +9 -0
  16. package/dist/extension/browsers/run-firefox/firefox-launch/process-handlers.d.ts +1 -1
  17. package/dist/extension/browsers/run-safari/safari-launch/index.d.ts +1 -1
  18. package/dist/extension/browsers/run-safari/safari-launch/tools.d.ts +17 -0
  19. package/dist/extension/browsers/run-safari/safari-packager.d.ts +2 -1
  20. package/dist/extension/browsers/run-safari/safari-types.d.ts +2 -0
  21. package/dist/extension/config-types.d.ts +5 -2
  22. package/dist/extension/helpers/messages.d.ts +7 -0
  23. package/dist/extension/helpers/messaging.d.ts +1 -0
  24. package/dist/extension/helpers/node-version-guard.d.ts +3 -1
  25. package/dist/extension/helpers/template-catalog.d.ts +1 -1
  26. package/dist/extension/helpers/template-corpus.generated.d.ts +1 -1
  27. package/package.json +5 -7
package/README.md CHANGED
@@ -6,10 +6,10 @@
6
6
  [action-url]: https://github.com/extension-js/extension.js/actions
7
7
  [discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=0971fe
8
8
  [discord-url]: https://discord.gg/v9h2RgeTSN
9
- [bestpractices-image]: https://img.shields.io/cii/level/14662?label=OpenSSF%20Best%20Practices&style=flat&color=0971fe
9
+ [bestpractices-image]: https://img.shields.io/cii/level/14662?label=Best%20Practices&style=flat&color=0971fe
10
10
  [bestpractices-url]: https://www.bestpractices.dev/projects/14662
11
11
 
12
- # Extension.js [![Version][npm-version-image]][npm-version-url] [![Stars][stars-image]][stars-url] [![CI][action-image]][action-url] [![Discord][discord-image]][discord-url] [![OpenSSF Best Practices][bestpractices-image]][bestpractices-url]
12
+ # Extension.js [![Version][npm-version-image]][npm-version-url] [![Stars][stars-image]][stars-url] [![CI][action-image]][action-url] [![Discord][discord-image]][discord-url] [![Best Practices][bestpractices-image]][bestpractices-url]
13
13
 
14
14
  > The cross-browser extension framework. Build for Chrome, Edge, Firefox, and Safari with no build config required.
15
15
 
@@ -23,9 +23,9 @@ npm run dev
23
23
 
24
24
  Works with `npm`, `pnpm`, `yarn`, `bun`, and `deno`.
25
25
 
26
- The CLI itself runs on Node.js 22.12+ and Deno 2.5+. Bun installs and launches it, and it runs on Node.js from there.
26
+ The CLI itself runs on Node.js 22.12+, Deno 2.5+, and Bun 1.2+.
27
27
 
28
- [Documentation](https://extension.js.org/docs) · [Templates](https://templates.extension.dev/?utm_source=readme&utm_campaign=nav-templates) · [Showcase](https://extension.js.org/showcase) · [Blog](https://extension.js.org/blog)
28
+ [Documentation](https://extension.js.org/docs) · [Templates](https://templates.extension.dev/?utm_source=readme&utm_campaign=nav-templates) · [YouTube](https://www.youtube.com/@BrowserExtension) · [Showcase](https://extension.js.org/showcase) · [Blog](https://extension.js.org/blog)
29
29
 
30
30
  ## Why Extension.js
31
31
 
@@ -173,23 +173,25 @@ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-sampl
173
173
 
174
174
  ## Package managers
175
175
 
176
- <div align="center">
177
-
178
- | <img alt="npm" src="https://media.extension.land/logos/package-managers/npm.svg" width="70"> | <img alt="pnpm" src="https://media.extension.land/logos/package-managers/pnpm.svg" width="70"> | <img alt="Yarn" src="https://media.extension.land/logos/package-managers/yarn.svg" width="70"> | <img alt="Bun" src="https://media.extension.land/logos/package-managers/bun.svg" width="70"> | <picture><source media="(prefers-color-scheme: dark)" srcset="https://media.extension.land/logos/package-managers/deno-dark.svg"><img alt="Deno" src="https://media.extension.land/logos/package-managers/deno.svg" width="70"></picture> |
179
- | :-: | :-: | :-: | :-: | :-: |
180
- | npm<br>✅ Supported | pnpm<br>✅ Supported | Yarn<br>✅ Supported | Bun<br>✅ Supported | Deno<br>✅ Supported |
176
+ Add Extension.js to a project, or run it once without installing, with the package manager you already use.
181
177
 
182
- </div>
178
+ | Package manager | Add to a project | Run without installing |
179
+ | :-- | :-- | :-- |
180
+ | npm | `npm install extension@latest --save-dev` | `npx extension@latest create my-extension` |
181
+ | pnpm | `pnpm add extension@latest --save-dev` | `pnpm dlx extension@latest create my-extension` |
182
+ | Yarn | `yarn add extension@latest --dev` | `yarn dlx extension@latest create my-extension` |
183
+ | Bun | `bun add --dev extension@latest` | `bunx extension@latest create my-extension` |
184
+ | Deno | `deno add npm:extension@latest` | `deno run -A npm:extension@latest create my-extension` |
183
185
 
184
- Each of these installs Extension.js, launches it, and runs its scripts. Which runtime the CLI then executes on is a separate question.
186
+ Which runtime the CLI then executes on is a separate question, answered below.
185
187
 
186
188
  ## Runtimes
187
189
 
188
- | Runtime | Status | Notes |
189
- | :-- | :-- | :-- |
190
- | Node.js | ✅ 22.12 and newer | The default. `npx`, `pnpm dlx`, `yarn dlx` and `bunx` all land here through the published bin |
191
- | Deno | ✅ 2.5 and newer | `deno run -A npm:extension@latest`. Deno 2.8.0 is refused because it cannot load `node:querystring`, fixed in 2.8.1 |
192
- | Bun | Not a runtime for the CLI | Bun installs and launches Extension.js, which then runs on Node.js. `bunx --bun` and `bun run --bun` are refused |
190
+ | | Runtime | Status | Notes |
191
+ | :-: | :-- | :-- | :-- |
192
+ | <img alt="Node.js" src="https://media.extension.land/logos/package-managers/node.svg" width="28"> | Node.js | ✅ 22.12 and newer | The default. `npx`, `pnpm dlx`, `yarn dlx` and `bunx` all land here through the published bin |
193
+ | <picture><source media="(prefers-color-scheme: dark)" srcset="https://media.extension.land/logos/package-managers/deno-dark.svg"><img alt="Deno" src="https://media.extension.land/logos/package-managers/deno.svg" width="28"></picture> | Deno | ✅ 2.5 and newer | `deno run -A npm:extension@latest`. Deno 2.8.0 is refused because it cannot load `node:querystring`, fixed in 2.8.1 |
194
+ | <img alt="Bun" src="https://media.extension.land/logos/package-managers/bun.svg" width="28"> | Bun | 1.2 and newer | `bunx --bun extension` and `bun run --bun`. Plain `bunx` runs the CLI on Node.js instead, through the published bin |
193
195
 
194
196
  ## Community
195
197
 
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  exports.ids = [
3
- 84
3
+ 597
4
4
  ];
5
5
  exports.modules = {
6
6
  "./browsers/run-chromium/cdp/cdp-extension-controller/ensure.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
@@ -197,6 +197,7 @@ exports.modules = {
197
197
  var newtab_override = __webpack_require__("./browsers/browsers-lib/newtab-override.ts");
198
198
  var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
199
199
  var shared_utils = __webpack_require__("./browsers/browsers-lib/shared-utils.ts");
200
+ var write_json_atomic = __webpack_require__("./browsers/browsers-lib/write-json-atomic.ts");
200
201
  var external_ws_ = __webpack_require__("ws");
201
202
  var external_ws_default = /*#__PURE__*/ __webpack_require__.n(external_ws_);
202
203
  var constants = __webpack_require__("./browsers/browsers-lib/constants.ts");
@@ -482,6 +483,73 @@ exports.modules = {
482
483
  this.host = host;
483
484
  }
484
485
  }
486
+ const EXTENSIONS_PAGE = 'chrome://extensions';
487
+ const READ_DEVELOPER_MODE = "new Promise((resolve) => chrome.developerPrivate.getProfileConfiguration((config) => resolve(!!config.inDeveloperMode)))";
488
+ const ENABLE_DEVELOPER_MODE = "new Promise((resolve) => chrome.developerPrivate.updateProfileConfiguration({inDeveloperMode: true}, () => resolve(!chrome.runtime.lastError)))";
489
+ function developerModeFromProfile(profilePath) {
490
+ try {
491
+ const securePath = external_node_path_.join(profilePath, 'Default', 'Secure Preferences');
492
+ const parsed = JSON.parse(external_node_fs_.readFileSync(securePath, 'utf-8'));
493
+ return parsed?.extensions?.ui?.developer_mode === true;
494
+ } catch {
495
+ return false;
496
+ }
497
+ }
498
+ function developerModeFlipIsSafe(browserArgs) {
499
+ return !browserArgs.some((arg)=>arg.startsWith('--no-startup-window'));
500
+ }
501
+ async function evaluateBoolean(transport, sessionId, expression) {
502
+ try {
503
+ const response = await transport.sendCommand('Runtime.evaluate', {
504
+ expression,
505
+ awaitPromise: true,
506
+ returnByValue: true
507
+ }, sessionId);
508
+ const value = response?.result?.value;
509
+ return 'boolean' == typeof value ? value : void 0;
510
+ } catch {
511
+ return;
512
+ }
513
+ }
514
+ async function ensureDeveloperMode(options) {
515
+ const { transport } = options;
516
+ const attempts = options.attempts ?? 10;
517
+ const delayMs = options.delayMs ?? 200;
518
+ const sleep = options.sleep ?? ((ms)=>new Promise((r)=>setTimeout(r, ms)));
519
+ let targetId = '';
520
+ try {
521
+ const created = await transport.sendCommand('Target.createTarget', {
522
+ url: EXTENSIONS_PAGE,
523
+ background: true
524
+ });
525
+ targetId = created?.targetId ?? '';
526
+ if (!targetId) return 'unavailable';
527
+ const attached = await transport.sendCommand('Target.attachToTarget', {
528
+ targetId,
529
+ flatten: true
530
+ });
531
+ const sessionId = attached?.sessionId ?? '';
532
+ if (!sessionId) return 'unavailable';
533
+ for(let attempt = 0; attempt < attempts; attempt++){
534
+ const current = await evaluateBoolean(transport, sessionId, READ_DEVELOPER_MODE);
535
+ if (true === current) return 'already-on';
536
+ if (false === current) {
537
+ const enabled = await evaluateBoolean(transport, sessionId, ENABLE_DEVELOPER_MODE);
538
+ return true === enabled ? 'enabled' : 'unavailable';
539
+ }
540
+ await sleep(delayMs);
541
+ }
542
+ return 'unavailable';
543
+ } catch {
544
+ return 'unavailable';
545
+ } finally{
546
+ if (targetId) try {
547
+ await transport.sendCommand('Target.closeTarget', {
548
+ targetId
549
+ });
550
+ } catch {}
551
+ }
552
+ }
485
553
  function isRecoverableBootstrapError(error) {
486
554
  const msg = String(error?.message || error || '').toLowerCase();
487
555
  return msg.includes('econnreset') || msg.includes('websocket is not open') || msg.includes('cdp transport is not open') || msg.includes('cdp connection closed') || msg.includes('cdp pipe closed') || msg.includes('socket hang up') || msg.includes('timed out') || msg.includes('no cdp websocket url');
@@ -783,6 +851,12 @@ exports.modules = {
783
851
  if (this.cdp) return;
784
852
  await this.connectFreshClient();
785
853
  }
854
+ async ensureDeveloperMode() {
855
+ if (!this.cdp) return 'unavailable';
856
+ return ensureDeveloperMode({
857
+ transport: this.cdp
858
+ });
859
+ }
786
860
  async openTab(url) {
787
861
  if (!this.cdp) return;
788
862
  await this.cdp.sendCommand('Target.createTarget', {
@@ -1109,6 +1183,10 @@ exports.modules = {
1109
1183
  });
1110
1184
  await cdpExtensionController.connect();
1111
1185
  if ((0, messaging._o)()) (0, messaging._w)(messages.M3('127.0.0.1', chromeRemoteDebugPort));
1186
+ if (userDataDir && developerModeFlipIsSafe(chromiumArgs) && !developerModeFromProfile(userDataDir)) {
1187
+ const developerMode = await cdpExtensionController.ensureDeveloperMode();
1188
+ if ((0, messaging._o)()) (0, messaging._w)(`[CDP] developer mode: ${developerMode}`);
1189
+ }
1112
1190
  try {
1113
1191
  if (extensionOutputPath && Number.isFinite(chromeRemoteDebugPort)) {
1114
1192
  const readyPath = external_node_path_.join(external_node_path_.dirname(extensionOutputPath), 'extension-js', external_node_path_.basename(extensionOutputPath), 'ready.json');
@@ -1116,7 +1194,7 @@ exports.modules = {
1116
1194
  const ready = JSON.parse(external_node_fs_.readFileSync(readyPath, 'utf-8'));
1117
1195
  if (ready.cdpPort !== chromeRemoteDebugPort) {
1118
1196
  ready.cdpPort = chromeRemoteDebugPort;
1119
- external_node_fs_.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
1197
+ (0, write_json_atomic.Z)(readyPath, ready);
1120
1198
  }
1121
1199
  }
1122
1200
  }