extension 4.0.18 → 4.0.19

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
@@ -149,7 +149,9 @@ var __webpack_modules__ = {
149
149
  };
150
150
  const updateSuffix = readUpdateSuffixOnce();
151
151
  console.log(_messages__rspack_import_5.io());
152
- console.log(_messages__rspack_import_5.e_(manifest, opts.browser, message, opts.browserVersionLine, updateSuffix || void 0));
152
+ console.log(_messages__rspack_import_5.e_(manifest, opts.browser, message, opts.browserVersionLine, updateSuffix || void 0, {
153
+ profilePath: opts.profilePath
154
+ }));
153
155
  console.log(_messages__rspack_import_5.io());
154
156
  markCardPrinted(k, opts.browser, opts.outPath);
155
157
  return true;
@@ -188,7 +190,8 @@ var __webpack_modules__ = {
188
190
  console.log(_messages__rspack_import_5.io());
189
191
  console.log(_messages__rspack_import_5.e_(manifest, opts.browser, message, browserLabel, updateSuffix || void 0, {
190
192
  includeExtensionId: opts.includeExtensionId,
191
- runLabel
193
+ runLabel,
194
+ profilePath: opts.profilePath
192
195
  }));
193
196
  console.log(_messages__rspack_import_5.io());
194
197
  } else {
@@ -205,7 +208,8 @@ var __webpack_modules__ = {
205
208
  console.log(_messages__rspack_import_5.io());
206
209
  console.log(_messages__rspack_import_5.e_(manifest, opts.browser, message, browserLabel, updateSuffix || void 0, {
207
210
  includeExtensionId: opts.includeExtensionId,
208
- runLabel
211
+ runLabel,
212
+ profilePath: opts.profilePath
209
213
  }));
210
214
  console.log(_messages__rspack_import_5.io());
211
215
  }
@@ -220,6 +224,10 @@ var __webpack_modules__ = {
220
224
  {
221
225
  label: 'Output',
222
226
  value: opts.outPath
227
+ },
228
+ {
229
+ label: 'Profile',
230
+ value: opts.profilePath || ''
223
231
  }
224
232
  ]
225
233
  }));
@@ -796,6 +804,10 @@ var __webpack_modules__ = {
796
804
  label: 'Extension ID',
797
805
  value: includeExtensionId ? cleanId : ''
798
806
  },
807
+ {
808
+ label: 'Profile',
809
+ value: String(opts?.profilePath || '').trim()
810
+ },
799
811
  {
800
812
  label: 'Run ID',
801
813
  value: opts?.runLabel || ''
@@ -1696,12 +1708,15 @@ var __webpack_modules__ = {
1696
1708
  "./browsers/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1697
1709
  var _browsers_lib_browser_family__rspack_import_0 = __webpack_require__("./browsers/browsers-lib/browser-family.ts");
1698
1710
  var _browsers_lib_output_binaries_resolver__rspack_import_1 = __webpack_require__("./browsers/browsers-lib/output-binaries-resolver.ts");
1699
- var _browsers_lib_runtime_options__rspack_import_7 = __webpack_require__("./browsers/browsers-lib/runtime-options.ts");
1700
- var _run_chromium_chromium_context__rspack_import_5 = __webpack_require__("./browsers/run-chromium/chromium-context/index.ts");
1711
+ var _browsers_lib_runtime_options__rspack_import_10 = __webpack_require__("./browsers/browsers-lib/runtime-options.ts");
1712
+ var _run_chromium_chromium_context__rspack_import_8 = __webpack_require__("./browsers/run-chromium/chromium-context/index.ts");
1701
1713
  var _run_chromium_chromium_launch__rspack_import_2 = __webpack_require__("./browsers/run-chromium/chromium-launch/index.ts");
1702
- var _run_firefox_firefox_context__rspack_import_6 = __webpack_require__("./browsers/run-firefox/firefox-context/index.ts");
1714
+ var _run_firefox_firefox_context__rspack_import_9 = __webpack_require__("./browsers/run-firefox/firefox-context/index.ts");
1703
1715
  var _run_firefox_firefox_launch__rspack_import_3 = __webpack_require__("./browsers/run-firefox/firefox-launch/index.ts");
1704
1716
  __webpack_require__("./browsers/run-only.ts");
1717
+ __webpack_require__("./browsers/run-safari/safari-launch/index.ts");
1718
+ __webpack_require__("./browsers/run-safari/safari-launch/safari-config.ts");
1719
+ __webpack_require__("./browsers/run-safari/safari-packager.ts");
1705
1720
  function createCompilationLike(opts) {
1706
1721
  return {
1707
1722
  options: {
@@ -1750,7 +1765,7 @@ var __webpack_modules__ = {
1750
1765
  logTab: opts.logTab,
1751
1766
  logSink: opts.logSink
1752
1767
  };
1753
- const ctx = (0, _run_chromium_chromium_context__rspack_import_5.g)();
1768
+ const ctx = (0, _run_chromium_chromium_context__rspack_import_8.g)();
1754
1769
  const launcher = new _run_chromium_chromium_launch__rspack_import_2.f(chromiumOpts, ctx);
1755
1770
  const enableCdp = 'development' === mode;
1756
1771
  await launcher.runOnce(compilationLike, {
@@ -1824,9 +1839,9 @@ var __webpack_modules__ = {
1824
1839
  port: opts.port,
1825
1840
  dryRun: opts.dryRun
1826
1841
  };
1827
- const ctx = (0, _run_firefox_firefox_context__rspack_import_6.D)();
1842
+ const ctx = (0, _run_firefox_firefox_context__rspack_import_9.D)();
1828
1843
  const launcher = new _run_firefox_firefox_launch__rspack_import_3.c(firefoxOpts, ctx);
1829
- const launchRequest = (0, _browsers_lib_runtime_options__rspack_import_7.zU)(pluginOptions, mode, {
1844
+ const launchRequest = (0, _browsers_lib_runtime_options__rspack_import_10.zU)(pluginOptions, mode, {
1830
1845
  persistProfile: opts.persistProfile,
1831
1846
  geckoBinary: opts.geckoBinary
1832
1847
  });
@@ -2618,10 +2633,13 @@ var __webpack_modules__ = {
2618
2633
  const loadExtensionFlag = args.chromiumConfig.find((flag)=>flag.startsWith('--load-extension='));
2619
2634
  const extensionOutputPath = (0, extension_output_path.W)(args.compilation, loadExtensionFlag);
2620
2635
  if (!extensionOutputPath) return;
2636
+ const userDataDirFlag = args.chromiumConfig.find((flag)=>flag.startsWith('--user-data-dir='));
2637
+ const profilePath = userDataDirFlag ? userDataDirFlag.replace('--user-data-dir=', '').replace(/^"|"$/g, '') : void 0;
2621
2638
  if ('production' === mode) return void await (0, banner.MK)({
2622
2639
  browser: args.browser,
2623
2640
  outPath: extensionOutputPath,
2624
- browserVersionLine: args.browserVersionLine
2641
+ browserVersionLine: args.browserVersionLine,
2642
+ profilePath
2625
2643
  });
2626
2644
  if ('development' !== mode) return;
2627
2645
  const ready = await (0, manifest_readiness.fR)(extensionOutputPath, {
@@ -2634,7 +2652,8 @@ var __webpack_modules__ = {
2634
2652
  outPath: extensionOutputPath,
2635
2653
  hostPort: args.hostPort,
2636
2654
  getInfo: async ()=>null,
2637
- browserVersionLine: args.browserVersionLine
2655
+ browserVersionLine: args.browserVersionLine,
2656
+ profilePath
2638
2657
  });
2639
2658
  }
2640
2659
  class ChromiumLaunchPlugin {
@@ -5128,1801 +5147,1880 @@ var __webpack_modules__ = {
5128
5147
  E: ()=>runOnlyPreviewBrowser
5129
5148
  });
5130
5149
  },
5131
- "./helpers/messaging.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5132
- var pintor__rspack_import_0 = __webpack_require__("pintor");
5133
- var pintor__rspack_import_0_default = /*#__PURE__*/ __webpack_require__.n(pintor__rspack_import_0);
5134
- const GLYPH = '⏵⏵⏵';
5135
- const DEBUG_GLYPH = '···';
5136
- const DEBUG_VALUES = new Set([
5137
- '1',
5138
- 'true',
5139
- 'yes',
5140
- 'on',
5141
- 'debug',
5142
- 'dev',
5143
- 'development'
5144
- ]);
5145
- function isDebug() {
5146
- const raw = process.env.EXTENSION_DEBUG ?? process.env.EXTENSION_AUTHOR_MODE;
5147
- return DEBUG_VALUES.has(String(raw ?? '').trim().toLowerCase());
5150
+ "./browsers/run-safari/safari-launch/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5151
+ __webpack_require__.d(__webpack_exports__, {
5152
+ Ah: ()=>packageSafariExtension,
5153
+ QW: ()=>safariBuildPreflight,
5154
+ iF: ()=>safariPreflightError
5155
+ });
5156
+ var external_node_child_process_ = __webpack_require__("node:child_process");
5157
+ var external_node_fs_ = __webpack_require__("node:fs");
5158
+ var messaging = __webpack_require__("./helpers/messaging.ts");
5159
+ var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
5160
+ function logSafariDryRun(converterCmd, xcodebuildCmd) {
5161
+ if ((0, messaging._o)()) console.log(messages.KP());
5162
+ console.log(messages.yc());
5163
+ console.log(messages.fL(converterCmd));
5164
+ console.log(messages.Aj(xcodebuildCmd));
5148
5165
  }
5149
- function prefix(type) {
5150
- if ('error' === type) return pintor__rspack_import_0_default().red(GLYPH);
5151
- if ('warn' === type) return pintor__rspack_import_0_default().brightYellow(GLYPH);
5152
- if ('success' === type) return pintor__rspack_import_0_default().green(GLYPH);
5153
- if ('debug' === type) return pintor__rspack_import_0_default().dim(pintor__rspack_import_0_default().gray(DEBUG_GLYPH));
5154
- return pintor__rspack_import_0_default().gray(GLYPH);
5166
+ var safari_config = __webpack_require__("./browsers/run-safari/safari-launch/safari-config.ts");
5167
+ function isMacOS() {
5168
+ return 'darwin' === process.platform;
5155
5169
  }
5156
- const fmt = {
5157
- heading: (title)=>pintor__rspack_import_0_default().underline(pintor__rspack_import_0_default().blue(title)),
5158
- label: (key)=>pintor__rspack_import_0_default().gray(key.toUpperCase()),
5159
- val: (value)=>pintor__rspack_import_0_default().underline(value),
5160
- code: (value)=>pintor__rspack_import_0_default().blue(value),
5161
- bullet: (value)=>`- ${value}`,
5162
- block (title, rows) {
5163
- const head = fmt.heading(title);
5164
- const body = rows.map(([key, value])=>`${fmt.label(key)} ${value}`).join('\n');
5165
- return `${head}\n${body}`;
5166
- },
5167
- truncate (input, max = 800) {
5168
- const s = (()=>{
5169
- try {
5170
- return 'string' == typeof input ? input : JSON.stringify(input);
5171
- } catch {
5172
- return String(input);
5173
- }
5174
- })();
5175
- return s.length > max ? `${s.slice(0, max)}…` : s;
5170
+ function activeDeveloperDir() {
5171
+ try {
5172
+ const result = (0, external_node_child_process_.spawnSync)('xcode-select', [
5173
+ '-p'
5174
+ ], {
5175
+ encoding: 'utf8',
5176
+ timeout: 15000
5177
+ });
5178
+ if (0 !== result.status) return null;
5179
+ const resolved = String(result.stdout || '').trim();
5180
+ return resolved.length > 0 ? resolved : null;
5181
+ } catch {
5182
+ return null;
5176
5183
  }
5177
- };
5178
- const CARD_LABEL_WIDTH = 15;
5179
- function card(input = {}) {
5180
- const version = String(input.version || '').trim();
5181
- const suffix = String(input.suffix || '').trim();
5182
- const head = ` 🧩 ${pintor__rspack_import_0_default().brightBlue('Extension.js')}` + (version ? ` ${pintor__rspack_import_0_default().gray(version)}` : '') + (suffix ? ` ${suffix}` : '');
5183
- const body = (input.rows || []).filter((row)=>String(row.value || '').trim().length > 0).map((row)=>{
5184
- const label = row.label.padEnd(CARD_LABEL_WIDTH);
5185
- return ` ${label}${pintor__rspack_import_0_default().gray(String(row.value).trim())}`;
5184
+ }
5185
+ function findWithXcrun(tool) {
5186
+ try {
5187
+ const result = (0, external_node_child_process_.spawnSync)('xcrun', [
5188
+ '--find',
5189
+ tool
5190
+ ], {
5191
+ encoding: 'utf8',
5192
+ timeout: 15000
5193
+ });
5194
+ if (0 !== result.status) return null;
5195
+ const resolved = String(result.stdout || '').trim();
5196
+ return resolved.length > 0 ? resolved : null;
5197
+ } catch {
5198
+ return null;
5199
+ }
5200
+ }
5201
+ function detectSafariToolchain() {
5202
+ const platformOk = isMacOS();
5203
+ if (!platformOk) return {
5204
+ platformOk: false,
5205
+ developerDir: null,
5206
+ needsFullXcode: false,
5207
+ converter: null,
5208
+ xcodebuild: null,
5209
+ ok: false
5210
+ };
5211
+ const developerDir = activeDeveloperDir();
5212
+ const needsFullXcode = !developerDir || /CommandLineTools/i.test(developerDir);
5213
+ const converter = findWithXcrun('safari-web-extension-converter');
5214
+ const xcodebuild = findWithXcrun('xcodebuild');
5215
+ return {
5216
+ platformOk,
5217
+ developerDir,
5218
+ needsFullXcode,
5219
+ converter,
5220
+ xcodebuild,
5221
+ ok: Boolean(converter && xcodebuild)
5222
+ };
5223
+ }
5224
+ function fallbackLogger() {
5225
+ return {
5226
+ info: (...a)=>console.log(...a),
5227
+ warn: (...a)=>console.warn(...a),
5228
+ error: (...a)=>console.error(...a),
5229
+ debug: (...a)=>console?.debug?.(...a)
5230
+ };
5231
+ }
5232
+ function isTestEnv() {
5233
+ return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);
5234
+ }
5235
+ function delay(ms) {
5236
+ return new Promise((resolve)=>setTimeout(resolve, ms));
5237
+ }
5238
+ const TOOL_TAIL_LINES = 50;
5239
+ const TOOL_TAIL_BYTES = 8192;
5240
+ function toolOutputTail(output) {
5241
+ const lines = output.slice(4 * -TOOL_TAIL_BYTES).split(/\r?\n/).filter((line)=>line.trim().length > 0);
5242
+ const tail = lines.slice(-TOOL_TAIL_LINES).join('\n');
5243
+ return tail.length > TOOL_TAIL_BYTES ? tail.slice(-TOOL_TAIL_BYTES) : tail;
5244
+ }
5245
+ function runTool(bin, args, opts) {
5246
+ const streamOutput = (0, messaging._o)() && !opts?.quiet;
5247
+ return new Promise((resolve)=>{
5248
+ let output = '';
5249
+ const child = (0, external_node_child_process_.spawn)(bin, args, {
5250
+ stdio: [
5251
+ 'ignore',
5252
+ 'pipe',
5253
+ 'pipe'
5254
+ ]
5255
+ });
5256
+ const onChunk = (chunk)=>{
5257
+ const text = String(chunk);
5258
+ output += text;
5259
+ if (output.length > 8 * TOOL_TAIL_BYTES) output = output.slice(4 * -TOOL_TAIL_BYTES);
5260
+ if (streamOutput) process.stdout.write(text);
5261
+ };
5262
+ child.stdout?.on('data', onChunk);
5263
+ child.stderr?.on('data', onChunk);
5264
+ child.on('error', (error)=>resolve({
5265
+ ok: false,
5266
+ code: null,
5267
+ output: `${output}${String(error)}`
5268
+ }));
5269
+ child.on('close', (code)=>resolve({
5270
+ ok: 0 === code,
5271
+ code,
5272
+ output
5273
+ }));
5186
5274
  });
5187
- return [
5188
- head,
5189
- ...body
5190
- ].join('\n');
5191
5275
  }
5192
- const CARD_KEYS_ENV = 'EXTENSION_CLI_CARD_KEYS';
5193
- const CARD_KEYS_SEPARATOR = '\u001f';
5194
- function isCardKeyClaimed(key) {
5195
- const claimed = String(process.env[CARD_KEYS_ENV] || '');
5196
- if (!claimed) return false;
5197
- return claimed.split(CARD_KEYS_SEPARATOR).includes(key);
5276
+ function converterWarnings(output) {
5277
+ return output.split(/\r?\n/).filter((line)=>/warning/i.test(line)).map((line)=>line.trim()).filter((line)=>line.length > 0);
5198
5278
  }
5199
- function claimCardKey(key) {
5200
- if (isCardKeyClaimed(key)) return false;
5201
- const claimed = String(process.env[CARD_KEYS_ENV] || '');
5202
- process.env[CARD_KEYS_ENV] = claimed ? `${claimed}${CARD_KEYS_SEPARATOR}${key}` : key;
5203
- return true;
5279
+ async function confirmRegisteredWithSafari(bundleIdentifier) {
5280
+ const needle = `${bundleIdentifier}.Extension`;
5281
+ for(let attempt = 0; attempt < 6; attempt += 1){
5282
+ const { ok, output } = await runTool('pluginkit', [
5283
+ '-m'
5284
+ ], {
5285
+ quiet: true
5286
+ });
5287
+ if (ok && output.includes(needle)) return true;
5288
+ await delay(800);
5289
+ }
5290
+ return false;
5204
5291
  }
5205
- const GECKO_BROWSERS = new Set([
5206
- 'firefox',
5207
- 'firefox-based',
5208
- 'gecko-based',
5209
- 'librewolf',
5210
- 'waterfox'
5211
- ]);
5212
- function artifactNoun(browser) {
5213
- const name = String(browser || '').trim().toLowerCase();
5214
- if (GECKO_BROWSERS.has(name)) return 'Add-on';
5215
- if (name.includes('gecko') || name.includes('firefox')) return 'Add-on';
5216
- return 'Extension';
5292
+ function describePackage(config) {
5293
+ return {
5294
+ appName: config.appName,
5295
+ bundleId: config.bundleIdentifier,
5296
+ bundleIdDerived: config.bundleIdDerived,
5297
+ appPath: (0, safari_config.fd)(config),
5298
+ xcodeProjectPath: (0, safari_config.Gi)(config),
5299
+ macOsOnly: config.macOsOnly
5300
+ };
5217
5301
  }
5218
- const ENVELOPE_SCHEMA = 1;
5219
- const CODES = {
5220
- E_ARGS: 'E_ARGS',
5221
- E_PROJECT_NOT_FOUND: 'E_PROJECT_NOT_FOUND',
5222
- E_MANIFEST_NOT_FOUND: 'E_MANIFEST_NOT_FOUND',
5223
- E_MANIFEST_INVALID: 'E_MANIFEST_INVALID',
5224
- E_FIRST_COMPILE: 'E_FIRST_COMPILE',
5225
- E_COMPILE: 'E_COMPILE',
5226
- E_BROWSER_NOT_FOUND: 'E_BROWSER_NOT_FOUND',
5227
- E_BROWSER_LAUNCH: 'E_BROWSER_LAUNCH',
5228
- E_PROFILE_LOCKED: 'E_PROFILE_LOCKED',
5229
- E_READY_TIMEOUT: 'E_READY_TIMEOUT',
5230
- E_SESSION_EXISTS: 'E_SESSION_EXISTS',
5231
- E_CONTROL_DENIED: 'E_CONTROL_DENIED',
5232
- E_CONTROL_UNAVAILABLE: 'E_CONTROL_UNAVAILABLE',
5233
- E_TOKEN_MISSING: 'E_TOKEN_MISSING',
5234
- E_NETWORK: 'E_NETWORK',
5235
- E_INTERRUPTED: 'E_INTERRUPTED',
5236
- E_NOT_IMPLEMENTED: 'E_NOT_IMPLEMENTED',
5237
- E_INTERNAL: 'E_INTERNAL',
5238
- E_UNSUPPORTED_BROWSER: 'E_UNSUPPORTED_BROWSER',
5239
- E_INVALID_OPTION: 'E_INVALID_OPTION',
5240
- E_PORT_UNAVAILABLE: 'E_PORT_UNAVAILABLE',
5241
- E_DEPENDENCY_INSTALL: 'E_DEPENDENCY_INSTALL',
5242
- E_TEMPLATE_NOT_FOUND: 'E_TEMPLATE_NOT_FOUND',
5243
- E_DESTINATION_NOT_EMPTY: 'E_DESTINATION_NOT_EMPTY',
5244
- E_DESTINATION_NOT_WRITABLE: 'E_DESTINATION_NOT_WRITABLE',
5245
- E_BROWSER_DOWNLOAD: 'E_BROWSER_DOWNLOAD',
5246
- E_SESSION_NOT_FOUND: 'E_SESSION_NOT_FOUND',
5247
- E_EVAL_REFUSED: 'E_EVAL_REFUSED',
5248
- E_TARGET_NOT_FOUND: 'E_TARGET_NOT_FOUND',
5249
- E_TIMEOUT: 'E_TIMEOUT',
5250
- E_PUBLISH_REJECTED: 'E_PUBLISH_REJECTED',
5251
- E_AUTH_REQUIRED: 'E_AUTH_REQUIRED',
5252
- E_SAFARI_TOOLCHAIN: 'E_SAFARI_TOOLCHAIN',
5253
- E_EVAL: 'E_EVAL',
5254
- E_INSPECT: 'E_INSPECT',
5255
- E_STORAGE: 'E_STORAGE',
5256
- E_LOGS_NOT_FOUND: 'E_LOGS_NOT_FOUND',
5257
- E_READY_ERROR_STATUS: 'E_READY_ERROR_STATUS',
5258
- E_COMMAND_UNSUPPORTED_FOR_TARGET: 'E_COMMAND_UNSUPPORTED_FOR_TARGET',
5259
- E_FLAG_VALUE_INVALID: 'E_FLAG_VALUE_INVALID',
5260
- E_FLAG_NOT_SUPPORTED_HERE: 'E_FLAG_NOT_SUPPORTED_HERE',
5261
- E_DEV_SERVER_START: 'E_DEV_SERVER_START',
5262
- E_DOCTOR_CHECKS_FAILED: 'E_DOCTOR_CHECKS_FAILED',
5263
- E_TELEMETRY_WRITE: 'E_TELEMETRY_WRITE',
5264
- E_PREVIEW_NO_DIST: 'E_PREVIEW_NO_DIST',
5265
- E_BROWSER_UNINSTALL: 'E_BROWSER_UNINSTALL',
5266
- E_NODE_VERSION: 'E_NODE_VERSION',
5267
- E_UNKNOWN_COMMAND: 'E_UNKNOWN_COMMAND',
5268
- E_REMOVED_FLAG: 'E_REMOVED_FLAG',
5269
- E_BROWSER_NOT_INSTALLABLE: 'E_BROWSER_NOT_INSTALLABLE',
5270
- E_PARENT_GONE: 'E_PARENT_GONE',
5271
- E_REMOTE_URL_UNSUPPORTED: 'E_REMOTE_URL_UNSUPPORTED',
5272
- E_CONFIG_LOAD: 'E_CONFIG_LOAD',
5273
- E_MANAGED_DEP_CONFLICT: 'E_MANAGED_DEP_CONFLICT',
5274
- E_TYPES_EMIT: 'E_TYPES_EMIT',
5275
- E_TSCONFIG_MISSING: 'E_TSCONFIG_MISSING',
5276
- E_OPTIONAL_DEP_UNRESOLVED: 'E_OPTIONAL_DEP_UNRESOLVED',
5277
- E_OPTIONAL_DEP_LOAD: 'E_OPTIONAL_DEP_LOAD',
5278
- E_OPTIONAL_DEP_UNKNOWN: 'E_OPTIONAL_DEP_UNKNOWN',
5279
- E_COMPANION_EXTENSION_PATH: 'E_COMPANION_EXTENSION_PATH',
5280
- E_MANIFEST_IN_PUBLIC: 'E_MANIFEST_IN_PUBLIC',
5281
- E_RUNTIME_NOT_FOUND: 'E_RUNTIME_NOT_FOUND',
5282
- E_MANIFEST_SHAPE: 'E_MANIFEST_SHAPE',
5283
- E_MANIFEST_PAGE_MISSING: 'E_MANIFEST_PAGE_MISSING',
5284
- E_MANIFEST_VERSION_UNSUPPORTED: 'E_MANIFEST_VERSION_UNSUPPORTED',
5285
- E_MANIFEST_LOAD_BLOCKERS: 'E_MANIFEST_LOAD_BLOCKERS',
5286
- E_MANIFEST_PERMISSION_MISSING: 'E_MANIFEST_PERMISSION_MISSING',
5287
- E_MANIFEST_MSG_KEY_MISSING: 'E_MANIFEST_MSG_KEY_MISSING',
5288
- E_MANIFEST_EMIT: 'E_MANIFEST_EMIT',
5289
- E_RESTART_REQUIRED: 'E_RESTART_REQUIRED',
5290
- E_COMPILE_FATAL: 'E_COMPILE_FATAL',
5291
- E_MODULE_NOT_FOUND: 'E_MODULE_NOT_FOUND',
5292
- E_ENTRY_NOT_FOUND: 'E_ENTRY_NOT_FOUND',
5293
- E_ASSET_MISSING: 'E_ASSET_MISSING',
5294
- E_SCRIPT_DEP_MISSING: 'E_SCRIPT_DEP_MISSING',
5295
- E_RESERVED_FOLDER: 'E_RESERVED_FOLDER',
5296
- E_CSS_PARSE: 'E_CSS_PARSE',
5297
- E_CSS_PREPROCESSOR_MISSING: 'E_CSS_PREPROCESSOR_MISSING',
5298
- E_CSS_DEAD_REF: 'E_CSS_DEAD_REF',
5299
- E_INTEGRATION_INSTALL: 'E_INTEGRATION_INSTALL',
5300
- E_POLYFILL_NOT_FOUND: 'E_POLYFILL_NOT_FOUND',
5301
- E_LOCALES_LAYOUT: 'E_LOCALES_LAYOUT',
5302
- E_WAR_INVALID: 'E_WAR_INVALID',
5303
- E_MATCH_PATTERN_INVALID: 'E_MATCH_PATTERN_INVALID',
5304
- E_BACKGROUND_REQUIRED: 'E_BACKGROUND_REQUIRED',
5305
- E_CONTENT_SCRIPT_SYNTAX: 'E_CONTENT_SCRIPT_SYNTAX',
5306
- E_NO_ENTRYPOINTS: 'E_NO_ENTRYPOINTS',
5307
- E_REMOTE_RESOURCE_BLOCKED: 'E_REMOTE_RESOURCE_BLOCKED',
5308
- E_PERF_BUDGET: 'E_PERF_BUDGET',
5309
- E_ZIP_SKIPPED: 'E_ZIP_SKIPPED',
5310
- E_ENV_NO_MATCH: 'E_ENV_NO_MATCH',
5311
- E_REMOTE_FETCH_TIMEOUT: 'E_REMOTE_FETCH_TIMEOUT',
5312
- E_REMOTE_DOWNLOAD: 'E_REMOTE_DOWNLOAD',
5313
- E_REMOTE_ZIP_INVALID: 'E_REMOTE_ZIP_INVALID',
5314
- E_LOCAL_ZIP_NOT_FOUND: 'E_LOCAL_ZIP_NOT_FOUND',
5315
- E_PROJECT_DOWNLOAD_EMPTY: 'E_PROJECT_DOWNLOAD_EMPTY',
5316
- E_BROWSER_BINARY_REQUIRED: 'E_BROWSER_BINARY_REQUIRED',
5317
- E_BROWSER_BINARY_INVALID: 'E_BROWSER_BINARY_INVALID',
5318
- E_BROWSER_EXITED: 'E_BROWSER_EXITED',
5319
- E_BROWSER_START_TIMEOUT: 'E_BROWSER_START_TIMEOUT',
5320
- E_LAUNCH_SKIPPED_COMPILE_ERRORS: 'E_LAUNCH_SKIPPED_COMPILE_ERRORS',
5321
- E_INSTANCE_AMBIGUOUS: 'E_INSTANCE_AMBIGUOUS',
5322
- E_WSL_INTEROP: 'E_WSL_INTEROP',
5323
- E_EXTENSION_LOAD_REFUSED: 'E_EXTENSION_LOAD_REFUSED',
5324
- E_ADDON_INSTALL: 'E_ADDON_INSTALL',
5325
- E_BROWSER_CONNECT: 'E_BROWSER_CONNECT',
5326
- E_BROWSER_CONNECTION_CLOSED: 'E_BROWSER_CONNECTION_CLOSED',
5327
- E_CDP_NOT_CONNECTED: 'E_CDP_NOT_CONNECTED',
5328
- E_CDP_TIMEOUT: 'E_CDP_TIMEOUT',
5329
- E_CDP_OP_FAILED: 'E_CDP_OP_FAILED',
5330
- E_EXTENSION_ID_UNKNOWN: 'E_EXTENSION_ID_UNKNOWN',
5331
- E_RDP_PROTOCOL: 'E_RDP_PROTOCOL',
5332
- E_DEV_SERVER_TIMEOUT: 'E_DEV_SERVER_TIMEOUT',
5333
- E_PORT_IN_USE: 'E_PORT_IN_USE',
5334
- E_SESSION_STOPPED: 'E_SESSION_STOPPED',
5335
- E_LOGS_STREAM_GAP: 'E_LOGS_STREAM_GAP',
5336
- E_CREATE_DIR: 'E_CREATE_DIR',
5337
- E_CREATE_WRITE: 'E_CREATE_WRITE',
5338
- E_CREATE_TESTS_SETUP: 'E_CREATE_TESTS_SETUP',
5339
- E_GIT_SKIPPED: 'E_GIT_SKIPPED',
5340
- E_BROWSER_INSTALL_PRIVILEGE: 'E_BROWSER_INSTALL_PRIVILEGE',
5341
- E_UNINSTALL_NOOP: 'E_UNINSTALL_NOOP'
5342
- };
5343
- function withExtras(base, extras) {
5302
+ function safariPreflightError() {
5303
+ const tc = detectSafariToolchain();
5304
+ if (!tc.platformOk) return messages.G3(process.platform);
5305
+ if (!tc.ok) {
5306
+ if (tc.needsFullXcode) return messages.Xi(tc.developerDir);
5307
+ return messages.Cs(tc.converter ? 'xcodebuild' : 'safari-web-extension-converter');
5308
+ }
5309
+ return null;
5310
+ }
5311
+ function safariBuildPreflight() {
5312
+ const tc = detectSafariToolchain();
5313
+ if (!tc.platformOk) return {
5314
+ severity: 'skip',
5315
+ message: messages.UW(process.platform)
5316
+ };
5317
+ if (!tc.ok) return {
5318
+ severity: 'fatal',
5319
+ message: tc.needsFullXcode ? messages.Xi(tc.developerDir) : messages.Cs(tc.converter ? 'xcodebuild' : 'safari-web-extension-converter')
5320
+ };
5344
5321
  return {
5345
- ...base,
5346
- ...extras.hint ? {
5347
- hint: extras.hint
5348
- } : {},
5349
- ...extras.truncated ? {
5350
- truncated: true
5351
- } : {},
5352
- warnings: extras.warnings || []
5322
+ severity: 'ok'
5353
5323
  };
5354
5324
  }
5355
- const ENVELOPE = {
5356
- schema: ENVELOPE_SCHEMA,
5357
- ok (command, status, value, extras = {}) {
5358
- return withExtras({
5359
- schema: ENVELOPE_SCHEMA,
5360
- ok: true,
5361
- command,
5362
- status,
5363
- value,
5364
- error: null,
5365
- warnings: []
5366
- }, extras);
5367
- },
5368
- fail (command, status, error, extras = {}) {
5369
- return withExtras({
5370
- schema: ENVELOPE_SCHEMA,
5371
- ok: false,
5372
- command,
5373
- status,
5374
- value: extras.value ?? null,
5375
- error,
5376
- warnings: []
5377
- }, extras);
5325
+ async function runSafariPipeline(compilation, host, logger, mode) {
5326
+ const config = (0, safari_config.F7)(compilation, host);
5327
+ const converterArgs = (0, safari_config.w7)(config);
5328
+ const xcodebuildArgs = (0, safari_config.vx)(config);
5329
+ if (host.dryRun || isTestEnv()) {
5330
+ logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
5331
+ return describePackage(config);
5378
5332
  }
5379
- };
5380
- __webpack_require__.d(__webpack_exports__, {
5381
- Nr: ()=>card,
5382
- Pl: ()=>prefix,
5383
- T_: ()=>artifactNoun,
5384
- VR: ()=>isCardKeyClaimed,
5385
- _o: ()=>isDebug,
5386
- od: ()=>claimCardKey
5387
- }, {
5388
- Lp: CODES,
5389
- Pr: ENVELOPE,
5390
- Sc: fmt
5391
- });
5333
+ const toolchain = detectSafariToolchain();
5334
+ if (!toolchain.platformOk) {
5335
+ logger.warn?.(messages.G3(process.platform));
5336
+ return describePackage(config);
5337
+ }
5338
+ if (!toolchain.ok) {
5339
+ if (toolchain.needsFullXcode) logger.error?.(messages.Xi(toolchain.developerDir));
5340
+ else {
5341
+ const missing = toolchain.converter ? 'xcodebuild' : 'safari-web-extension-converter';
5342
+ logger.error?.(messages.Cs(missing));
5343
+ }
5344
+ return describePackage(config);
5345
+ }
5346
+ const projectExists = external_node_fs_.existsSync((0, safari_config.Gi)(config));
5347
+ const needsConversion = !projectExists || host.forceRegenerate || (0, safari_config.vk)(config);
5348
+ if (needsConversion) {
5349
+ if (projectExists) {
5350
+ logger.info?.(host.forceRegenerate ? messages.Tq() : messages.jc());
5351
+ logger.warn?.(messages.H1([
5352
+ ...safari_config.X9
5353
+ ]));
5354
+ }
5355
+ const { saved, restore } = (0, safari_config._D)(config);
5356
+ logger.info?.(messages.uK(config.extensionDir));
5357
+ const converted = await runTool('xcrun', converterArgs);
5358
+ if (!converted.ok) {
5359
+ const tail = toolOutputTail(converted.output);
5360
+ logger.error?.(messages.A('safari-web-extension-converter', converted.code, tail));
5361
+ throw new Error(`safari-web-extension-converter failed (exit ${converted.code})\n${tail}`);
5362
+ }
5363
+ const warnings = converterWarnings(converted.output);
5364
+ if (warnings.length > 0) logger.warn?.(messages.y1(warnings));
5365
+ const projFile = (0, safari_config.vh)(config);
5366
+ if (external_node_fs_.existsSync(projFile)) external_node_fs_.writeFileSync(projFile, (0, safari_config.IA)(external_node_fs_.readFileSync(projFile, 'utf8'), config.bundleIdentifier), 'utf8');
5367
+ restore();
5368
+ const preservedKeys = Object.keys(saved);
5369
+ if (preservedKeys.length > 0) logger.info?.(messages.oB(preservedKeys));
5370
+ (0, safari_config.x7)(config);
5371
+ logger.info?.(messages.l_(config.projectLocation));
5372
+ } else logger.info?.(messages.oM());
5373
+ if ('full' === mode) logger.info?.(messages.jR((0, safari_config.aD)(config)));
5374
+ const built = await runTool('xcodebuild', xcodebuildArgs);
5375
+ if (!built.ok) {
5376
+ const tail = toolOutputTail(built.output);
5377
+ logger.error?.(messages.A('xcodebuild', built.code, tail));
5378
+ throw new Error(`xcodebuild failed (exit ${built.code})\n${tail}`);
5379
+ }
5380
+ const appPath = (0, safari_config.fd)(config);
5381
+ if ('resync' === mode) {
5382
+ logger.info?.(messages.cF(config.appName));
5383
+ return describePackage(config);
5384
+ }
5385
+ logger.info?.(messages.Rl(appPath));
5386
+ if (config.bundleIdDerived) logger.info?.(messages.$q(config.bundleIdentifier));
5387
+ if (!config.open) {
5388
+ logger.info?.(messages.aO(appPath, config.appName));
5389
+ return describePackage(config);
5390
+ }
5391
+ const target = external_node_fs_.existsSync(appPath) ? appPath : (0, safari_config.Gi)(config);
5392
+ logger.info?.(messages.fw(target));
5393
+ await runTool('open', [
5394
+ target
5395
+ ]);
5396
+ if (config.safariBinary) await runTool('open', [
5397
+ '-a',
5398
+ config.safariBinary
5399
+ ]);
5400
+ logger.info?.(messages.fO(config.appName));
5401
+ if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
5402
+ else logger.warn?.(messages.qY(config.appName));
5403
+ return describePackage(config);
5404
+ }
5405
+ async function packageSafariExtension(host, outputPath, logger, mode = 'full') {
5406
+ const compilation = {
5407
+ options: {
5408
+ output: {
5409
+ path: outputPath
5410
+ }
5411
+ },
5412
+ outputOptions: {
5413
+ path: outputPath
5414
+ }
5415
+ };
5416
+ return await runSafariPipeline(compilation, host, logger || fallbackLogger(), mode);
5417
+ }
5392
5418
  },
5393
- "./index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
5394
- const MIN_NODE_MAJOR = 22;
5395
- const MIN_NODE_MINOR = 12;
5396
- function isSupportedNodeVersion(version) {
5397
- const [major, minor] = version.split('.').map((part)=>parseInt(part, 10));
5398
- if (!Number.isFinite(major)) return true;
5399
- if (major !== MIN_NODE_MAJOR) return major > MIN_NODE_MAJOR;
5400
- return (Number.isFinite(minor) ? minor : 0) >= MIN_NODE_MINOR;
5419
+ "./browsers/run-safari/safari-launch/safari-config.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5420
+ var node_fs__rspack_import_0 = __webpack_require__("node:fs");
5421
+ var node_path__rspack_import_1 = __webpack_require__("node:path");
5422
+ function sanitizeAppName(value) {
5423
+ const cleaned = String(value || '').replace(/[\\/:*?"<>|]+/g, ' ').replace(/\s+/g, ' ').trim();
5424
+ return cleaned.length > 0 ? cleaned : 'Extension';
5401
5425
  }
5402
- function unsupportedNodeVersionMessage(version) {
5403
- return `[Extension.js] Requires Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} (you are on ${version}). Upgrade Node.js to run the extension CLI.`;
5426
+ function bundleSegment(appName) {
5427
+ return String(appName || '').replace(/[^A-Za-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'extension';
5404
5428
  }
5405
- function enforceSupportedNodeVersion(version = process.versions.node) {
5406
- if (isSupportedNodeVersion(version)) return;
5407
- console.error(unsupportedNodeVersionMessage(version));
5408
- process.exit(1);
5429
+ function deriveBundleId(appName) {
5430
+ return `dev.extensionjs.${bundleSegment(appName)}`;
5409
5431
  }
5410
- enforceSupportedNodeVersion();
5411
- const external_commander_namespaceObject = require("commander");
5412
- var external_node_fs_ = __webpack_require__("node:fs");
5413
- var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_);
5414
- var external_node_path_ = __webpack_require__("node:path");
5415
- var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_);
5416
- async function exitAfterDrain(code) {
5417
- await Promise.all([
5418
- process.stdout,
5419
- process.stderr
5420
- ].map((stream)=>new Promise((resolve)=>{
5421
- if (0 === stream.writableLength) return resolve();
5422
- stream.write('', ()=>resolve());
5423
- })));
5424
- process.exit(code);
5432
+ function isValidBundleId(value) {
5433
+ return /^[A-Za-z][A-Za-z0-9-]*(\.[A-Za-z][A-Za-z0-9-]*)+$/.test(value);
5425
5434
  }
5426
- var external_node_module_ = __webpack_require__("node:module");
5427
- const external_node_url_namespaceObject = require("node:url");
5428
- const extension_develop_runtime_require = (0, external_node_module_.createRequire)(__rslib_import_meta_url__);
5429
- function parseJsonSafe(filePath) {
5430
- return JSON.parse(external_node_fs_default().readFileSync(filePath, 'utf8'));
5431
- }
5432
- function resolveDevelopRootFromDir(dir) {
5433
- try {
5434
- const packageJsonPath = external_node_path_default().join(dir, 'package.json');
5435
- if (!external_node_fs_default().existsSync(packageJsonPath)) return;
5436
- const pkg = parseJsonSafe(packageJsonPath);
5437
- return pkg?.name === 'extension-develop' ? dir : void 0;
5438
- } catch {
5439
- return;
5440
- }
5441
- }
5442
- function resolveWorkspaceDevelopRoot(startDir) {
5443
- let currentDir = external_node_path_default().resolve(startDir);
5444
- if (currentDir.split(external_node_path_default().sep).includes('node_modules')) return;
5445
- for(let depth = 0; depth < 8; depth += 1){
5446
- const candidate = external_node_path_default().join(currentDir, 'programs', 'develop');
5447
- const resolved = resolveDevelopRootFromDir(candidate);
5448
- if (resolved) return resolved;
5449
- const parent = external_node_path_default().dirname(currentDir);
5450
- if (parent === currentDir) break;
5451
- currentDir = parent;
5452
- }
5453
- }
5454
- function resolveInstalledDevelopRoot() {
5435
+ function readManifest(extensionDir) {
5455
5436
  try {
5456
- const packageJsonPath = extension_develop_runtime_require.resolve('extension-develop/package.json');
5457
- return resolveDevelopRootFromDir(external_node_path_default().dirname(packageJsonPath));
5458
- } catch {
5459
- try {
5460
- const entryPath = extension_develop_runtime_require.resolve('extension-develop');
5461
- return resolveDevelopRootFromDir(external_node_path_default().dirname(external_node_path_default().dirname(entryPath)));
5462
- } catch {
5463
- return;
5464
- }
5465
- }
5437
+ const manifestPath = node_path__rspack_import_1.join(extensionDir, 'manifest.json');
5438
+ if (node_fs__rspack_import_0.existsSync(manifestPath)) return JSON.parse(node_fs__rspack_import_0.readFileSync(manifestPath, 'utf8')) || {};
5439
+ } catch {}
5440
+ return {};
5466
5441
  }
5467
- function resolvePreferredDevelopRoot(startDir) {
5468
- const workspaceRoot = resolveWorkspaceDevelopRoot(startDir);
5469
- if (workspaceRoot) return {
5470
- root: workspaceRoot,
5471
- source: 'workspace'
5472
- };
5473
- const envRoot = process.env.EXTENSION_DEVELOP_ROOT ? resolveDevelopRootFromDir(external_node_path_default().resolve(process.env.EXTENSION_DEVELOP_ROOT)) : void 0;
5474
- if (envRoot) return {
5475
- root: envRoot,
5476
- source: 'env'
5477
- };
5478
- const installedRoot = resolveInstalledDevelopRoot();
5479
- if (installedRoot) return {
5480
- root: installedRoot,
5481
- source: 'installed'
5482
- };
5442
+ function resolveSafariBuildConfig(compilation, host) {
5443
+ const extensionDir = String(compilation?.options?.output?.path || '');
5444
+ const manifest = readManifest(extensionDir);
5445
+ const appName = sanitizeAppName(String(host.appName || manifest?.name || 'Extension'));
5446
+ const userBundleId = String(host.bundleId || '').trim();
5447
+ const bundleIdDerived = !userBundleId;
5448
+ const bundleIdentifier = bundleIdDerived ? deriveBundleId(appName) : userBundleId;
5449
+ const projectLocation = `${extensionDir.replace(/[\\/]+$/, '')}-xcode`;
5483
5450
  return {
5484
- root: void 0,
5485
- source: 'missing'
5451
+ extensionDir,
5452
+ projectLocation,
5453
+ appName,
5454
+ bundleIdentifier,
5455
+ bundleIdDerived,
5456
+ macOsOnly: false !== host.macOsOnly,
5457
+ language: 'swift',
5458
+ open: !host.noOpen,
5459
+ safariBinary: host.safariBinary
5486
5460
  };
5487
5461
  }
5488
- function resolveDevelopDistEntry(root, name = 'module') {
5489
- const base = external_node_path_default().join(root, 'dist', name);
5490
- const candidates = [
5491
- `${base}.mjs`,
5492
- `${base}.cjs`,
5493
- `${base}.js`,
5494
- base
5462
+ function composeConverterArgs(config) {
5463
+ const args = [
5464
+ 'safari-web-extension-converter',
5465
+ config.extensionDir,
5466
+ '--project-location',
5467
+ config.projectLocation,
5468
+ '--app-name',
5469
+ config.appName,
5470
+ '--bundle-identifier',
5471
+ config.bundleIdentifier,
5472
+ '--no-prompt',
5473
+ '--no-open',
5474
+ '--force',
5475
+ 'objc' === config.language ? '--objc' : '--swift'
5495
5476
  ];
5496
- for (const candidate of candidates)if (external_node_fs_default().existsSync(candidate)) return candidate;
5477
+ if (config.macOsOnly) args.push('--macos-only');
5478
+ return args;
5497
5479
  }
5498
- function resolveExtensionDevelopRoot(startDir = __dirname) {
5499
- const { root, source } = resolvePreferredDevelopRoot(startDir);
5500
- if (!root) throw new Error('Unable to locate the extension-develop runtime.');
5501
- process.env.EXTENSION_DEVELOP_ROOT = root;
5502
- if ('workspace' === source) process.env.EXTENSION_CREATE_DEVELOP_ROOT = root;
5503
- return root;
5480
+ function macOsSchemeName(config) {
5481
+ return config.macOsOnly ? config.appName : `${config.appName} (macOS)`;
5504
5482
  }
5505
- function resolveExtensionDevelopVersion(startDir = __dirname, fallbackVersion) {
5483
+ function xcodeProjectPath(config) {
5484
+ return node_path__rspack_import_1.join(config.projectLocation, config.appName, `${config.appName}.xcodeproj`);
5485
+ }
5486
+ function derivedDataPath(config) {
5487
+ return node_path__rspack_import_1.join(config.projectLocation, '.derived');
5488
+ }
5489
+ function builtAppPath(config) {
5490
+ return node_path__rspack_import_1.join(derivedDataPath(config), 'Build', 'Products', 'Release', `${config.appName}.app`);
5491
+ }
5492
+ function composeXcodebuildArgs(config) {
5493
+ return [
5494
+ '-project',
5495
+ xcodeProjectPath(config),
5496
+ '-scheme',
5497
+ macOsSchemeName(config),
5498
+ '-configuration',
5499
+ 'Release',
5500
+ '-derivedDataPath',
5501
+ derivedDataPath(config),
5502
+ 'CODE_SIGN_IDENTITY=-',
5503
+ 'CODE_SIGNING_REQUIRED=NO',
5504
+ 'CODE_SIGNING_ALLOWED=YES',
5505
+ 'build'
5506
+ ];
5507
+ }
5508
+ function manifestFingerprintPath(config) {
5509
+ return node_path__rspack_import_1.join(config.projectLocation, '.manifest-fingerprint');
5510
+ }
5511
+ function stableStringify(obj) {
5512
+ if (null == obj) return JSON.stringify(obj);
5513
+ if ('object' != typeof obj) return JSON.stringify(obj);
5514
+ if (Array.isArray(obj)) return `[${obj.map(stableStringify).join(',')}]`;
5515
+ const keys = Object.keys(obj).sort();
5516
+ return `{${keys.map((k)=>`${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
5517
+ }
5518
+ function readManifestRaw(extensionDir) {
5506
5519
  try {
5507
- const root = resolveExtensionDevelopRoot(startDir);
5508
- const packageJsonPath = external_node_path_default().join(root, 'package.json');
5509
- const pkg = parseJsonSafe(packageJsonPath);
5510
- return pkg?.version || fallbackVersion || '0.0.0';
5520
+ return node_fs__rspack_import_0.readFileSync(node_path__rspack_import_1.join(extensionDir, 'manifest.json'), 'utf8');
5511
5521
  } catch {
5512
- return fallbackVersion || '0.0.0';
5522
+ return '';
5513
5523
  }
5514
5524
  }
5515
- async function importModule(filePath) {
5516
- const mod = await import((0, external_node_url_namespaceObject.pathToFileURL)(filePath).href);
5517
- return mod?.default && 'object' == typeof mod.default ? {
5518
- ...mod.default,
5519
- ...mod
5520
- } : mod;
5525
+ function normalizeManifest(raw) {
5526
+ try {
5527
+ return stableStringify(JSON.parse(raw));
5528
+ } catch {
5529
+ return raw;
5530
+ }
5521
5531
  }
5522
- async function loadExtensionDevelopModule(startDir = __dirname) {
5523
- const root = resolveExtensionDevelopRoot(startDir);
5524
- const { source } = resolvePreferredDevelopRoot(startDir);
5525
- const distEntry = resolveDevelopDistEntry(root);
5526
- if (distEntry) return importModule(distEntry);
5527
- if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
5528
- return await import("extension-develop");
5532
+ function composeProjectFingerprint(config) {
5533
+ return JSON.stringify({
5534
+ v: 2,
5535
+ identity: {
5536
+ appName: config.appName,
5537
+ bundleId: config.bundleIdentifier,
5538
+ macOsOnly: config.macOsOnly
5539
+ },
5540
+ manifest: normalizeManifest(readManifestRaw(config.extensionDir))
5541
+ });
5529
5542
  }
5530
- async function loadExtensionDevelopPreviewModule(startDir = __dirname) {
5531
- const root = resolveExtensionDevelopRoot(startDir);
5532
- const { source } = resolvePreferredDevelopRoot(startDir);
5533
- const previewEntry = resolveDevelopDistEntry(root, 'preview');
5534
- if (previewEntry) return importModule(previewEntry);
5535
- const fullEntry = resolveDevelopDistEntry(root);
5536
- if (fullEntry) return importModule(fullEntry);
5537
- if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
5538
- return await import("extension-develop/preview");
5543
+ function saveManifestFingerprint(config) {
5544
+ node_fs__rspack_import_0.mkdirSync(node_path__rspack_import_1.dirname(manifestFingerprintPath(config)), {
5545
+ recursive: true
5546
+ });
5547
+ node_fs__rspack_import_0.writeFileSync(manifestFingerprintPath(config), composeProjectFingerprint(config), 'utf8');
5539
5548
  }
5540
- async function loadExtensionDevelopBridgeModule(startDir = __dirname) {
5541
- const root = resolveExtensionDevelopRoot(startDir);
5542
- const { source } = resolvePreferredDevelopRoot(startDir);
5543
- const bridgeEntry = resolveDevelopDistEntry(root, 'bridge');
5544
- if (bridgeEntry) return importModule(bridgeEntry);
5545
- if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
5546
- const bridgeSpecifier = 'extension-develop/bridge';
5547
- return await import(bridgeSpecifier);
5549
+ function isProjectStale(config) {
5550
+ const fpPath = manifestFingerprintPath(config);
5551
+ if (!node_fs__rspack_import_0.existsSync(fpPath)) return true;
5552
+ const stored = node_fs__rspack_import_0.readFileSync(fpPath, 'utf8');
5553
+ return stored !== composeProjectFingerprint(config);
5548
5554
  }
5549
- var external_pintor_ = __webpack_require__("pintor");
5550
- var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_);
5551
- var messaging = __webpack_require__("./helpers/messaging.ts");
5552
- function getLoggingPrefix(type) {
5553
- return (0, messaging.Pl)(type);
5555
+ function alignBundleIdentifiers(pbxprojContent, bundleId) {
5556
+ return pbxprojContent.replace(/PRODUCT_BUNDLE_IDENTIFIER = ("?)([^;"]+)\1;/g, (_match, _quote, value)=>/\.Extension$/.test(String(value)) ? `PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}.Extension";` : `PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}";`);
5554
5557
  }
5555
- const messages_code = (text)=>external_pintor_default().blue(text);
5556
- const messages_arg = (text)=>external_pintor_default().gray(text);
5557
- function renderArgToken(spec) {
5558
- const token = spec.values ? spec.values.join('|') : spec.label ?? spec.name;
5559
- return spec.required ? `<${token}>` : `[${token}]`;
5558
+ const PRESERVED_SETTINGS = [
5559
+ 'DEVELOPMENT_TEAM',
5560
+ 'CODE_SIGN_STYLE',
5561
+ 'PROVISIONING_PROFILE_SPECIFIER'
5562
+ ];
5563
+ function pbxprojPath(config) {
5564
+ return node_path__rspack_import_1.join(xcodeProjectPath(config), 'project.pbxproj');
5560
5565
  }
5561
- function commandArgSignature(positionals) {
5562
- return positionals.map(renderArgToken).join(' ');
5566
+ function extractXcodeUserSettings(pbxprojContent) {
5567
+ const found = {};
5568
+ for (const key of PRESERVED_SETTINGS){
5569
+ const m = new RegExp(`\\b${key}\\s*=\\s*([^;]+);`).exec(pbxprojContent);
5570
+ if (m) {
5571
+ const val = m[1].trim();
5572
+ if (val && '""' !== val && "''" !== val) found[key] = val;
5573
+ }
5574
+ }
5575
+ return found;
5563
5576
  }
5564
- const COMMAND_TABLE = [
5565
- {
5566
- name: 'create',
5567
- positionals: [
5568
- {
5569
- name: 'project-name|project-path',
5570
- required: true
5571
- }
5572
- ],
5573
- description: 'Create a new extension from a template (React, TypeScript, Vue, Svelte, etc.)',
5574
- supportsSourceInspection: false
5575
- },
5576
- {
5577
- name: 'dev',
5578
- positionals: [
5579
- {
5580
- name: 'project-path|remote-url',
5581
- required: false
5582
- }
5583
- ],
5584
- description: 'Start the development server with hot reloading',
5585
- supportsSourceInspection: true
5586
- },
5587
- {
5588
- name: 'start',
5589
- positionals: [
5590
- {
5591
- name: 'project-path|remote-url',
5592
- required: false
5593
- }
5594
- ],
5595
- description: 'Build and start the extension in production mode',
5596
- supportsSourceInspection: false
5597
- },
5598
- {
5599
- name: 'preview',
5600
- positionals: [
5601
- {
5602
- name: 'project-name',
5603
- required: false,
5604
- label: 'project-path|remote-url'
5605
- }
5606
- ],
5607
- description: 'Preview the extension in production mode without building',
5608
- supportsSourceInspection: false
5609
- },
5610
- {
5611
- name: 'build',
5612
- positionals: [
5613
- {
5614
- name: 'project-name',
5615
- required: false,
5616
- label: 'project-path|remote-url'
5617
- }
5618
- ],
5619
- description: 'Build the extension for packaging and distribution',
5620
- supportsSourceInspection: false
5621
- },
5622
- {
5623
- name: 'logs',
5624
- positionals: [
5625
- {
5626
- name: 'project-path',
5627
- required: false
5628
- }
5629
- ],
5630
- description: 'Print or stream logs from every context of a running dev session',
5631
- supportsSourceInspection: false
5632
- },
5633
- {
5634
- name: 'eval',
5635
- positionals: [
5636
- {
5637
- name: 'expression',
5638
- required: true
5639
- },
5640
- {
5641
- name: 'project-path',
5642
- required: false
5643
- }
5644
- ],
5645
- description: 'Evaluate an expression in a running extension context (requires --allow-eval)',
5646
- supportsSourceInspection: false
5647
- },
5648
- {
5649
- name: 'storage',
5650
- positionals: [
5651
- {
5652
- name: 'action',
5653
- required: true,
5654
- values: [
5655
- 'get',
5656
- 'set'
5657
- ]
5658
- },
5659
- {
5660
- name: 'project-path',
5661
- required: false
5662
- }
5663
- ],
5664
- description: 'Read or write chrome.storage in a running extension (requires --allow-control)',
5665
- supportsSourceInspection: false
5666
- },
5667
- {
5668
- name: 'reload',
5669
- positionals: [
5670
- {
5671
- name: 'project-path',
5672
- required: false
5673
- }
5674
- ],
5675
- description: 'Reload a running extension or tab (requires --allow-control)',
5676
- supportsSourceInspection: false
5677
- },
5678
- {
5679
- name: 'open',
5680
- positionals: [
5681
- {
5682
- name: 'surface',
5683
- required: true,
5684
- values: [
5685
- 'popup',
5686
- 'options',
5687
- 'sidebar',
5688
- 'action',
5689
- 'command'
5690
- ]
5691
- },
5692
- {
5693
- name: 'project-path',
5694
- required: false
5695
- }
5696
- ],
5697
- description: 'Open an extension surface: popup, options, sidebar, action, or command (requires --allow-control)',
5698
- supportsSourceInspection: false
5699
- },
5700
- {
5701
- name: 'inspect',
5702
- positionals: [
5703
- {
5704
- name: 'project-path',
5705
- required: false
5706
- }
5707
- ],
5708
- description: 'Inspect a page or content DOM through the agent bridge (CDP-free; requires --allow-control)',
5709
- supportsSourceInspection: true
5710
- },
5711
- {
5712
- name: 'publish',
5713
- positionals: [
5714
- {
5715
- name: 'project-path',
5716
- required: false
5717
- }
5718
- ],
5719
- description: 'Publish to extension.dev and print a shareable URL (requires EXTENSION_DEV_TOKEN)',
5720
- supportsSourceInspection: false
5721
- },
5722
- {
5723
- name: 'install',
5724
- positionals: [
5725
- {
5726
- name: 'browser-name',
5727
- required: false
5728
- }
5729
- ],
5730
- description: 'Install a managed browser binary into the Extension.js cache',
5731
- detail: 'Defaults to chromium when no browser is named.',
5732
- supportsSourceInspection: false,
5733
- notes: [
5734
- {
5735
- usage: '--browser <chrome|chromium|edge|firefox|chromium-based|gecko-based|firefox-based|all>',
5736
- description: 'Install multiple browsers, browser families, or all'
5737
- },
5738
- {
5739
- usage: '--where',
5740
- description: 'Print the managed browser cache root (or browser install path(s) when a browser name or --browser is provided)'
5741
- }
5742
- ]
5743
- },
5744
- {
5745
- name: 'uninstall',
5746
- positionals: [
5747
- {
5748
- name: 'browser-name',
5749
- required: false
5750
- }
5751
- ],
5752
- description: 'Remove managed browser binaries from the Extension.js cache',
5753
- supportsSourceInspection: false,
5754
- notes: [
5755
- {
5756
- usage: '--all',
5757
- description: 'Remove every managed browser binary'
5758
- },
5759
- {
5760
- usage: '--where',
5761
- description: 'Print the managed browser cache root (or browser install path(s) when --browser/--all is provided)'
5762
- }
5763
- ]
5764
- },
5765
- {
5766
- name: 'telemetry',
5767
- positionals: [
5768
- {
5769
- name: 'action',
5770
- required: false,
5771
- values: [
5772
- 'enable',
5773
- 'disable',
5774
- 'status'
5775
- ]
5776
- }
5777
- ],
5778
- description: 'Manage anonymous telemetry consent (enable, disable, or show status)',
5779
- supportsSourceInspection: false
5780
- },
5781
- {
5782
- name: 'doctor',
5783
- positionals: [
5784
- {
5785
- name: 'project-path',
5786
- required: false
5787
- }
5788
- ],
5789
- description: 'Diagnose a dev session: ready contract, control channel, token, executor, browser',
5790
- supportsSourceInspection: false
5577
+ function applyXcodeUserSettings(pbxprojContent, settings) {
5578
+ let result = pbxprojContent;
5579
+ for (const [key, value] of Object.entries(settings)){
5580
+ const existing = new RegExp(`\\b${key}\\s*=\\s*[^;]+;`, 'g');
5581
+ result = existing.test(result) ? result.replace(existing, `${key} = ${value};`) : result.replace(/buildSettings\s*=\s*\{/g, `buildSettings = {\n\t\t\t\t${key} = ${value};`);
5791
5582
  }
5792
- ];
5793
- const COMMANDS = COMMAND_TABLE.map((entry)=>({
5794
- ...entry,
5795
- args: commandArgSignature(entry.positionals)
5796
- }));
5797
- const commandDescriptions = Object.fromEntries(COMMANDS.map((spec)=>[
5798
- spec.name,
5799
- spec.description
5800
- ]));
5801
- function commandHelpEntry(spec) {
5802
- const signature = spec.args ? ` ${messages_arg(spec.args)}` : '';
5803
- const lines = [
5804
- `- ${messages_code(`extension ${spec.name}`)}${signature}`,
5805
- ` ${spec.description}`
5806
- ];
5807
- if (spec.detail) lines.push(` ${spec.detail}`);
5808
- for (const note of spec.notes ?? [])lines.push('', `- ${messages_code(`extension ${spec.name}`)} ${messages_arg(note.usage)}`, ` ${note.description}`);
5809
- return lines.join('\n');
5810
- }
5811
- function availableCommandsBlock() {
5812
- return COMMANDS.map(commandHelpEntry).join('\n\n');
5813
- }
5814
- function unhandledError(err) {
5815
- const message = err instanceof Error ? err.stack || err.message : 'string' == typeof err ? err : messaging.Sc.truncate(err);
5816
- return `${getLoggingPrefix('error')} ${external_pintor_default().red(String(message || 'Unknown error'))}`;
5817
- }
5818
- function updateFailed(err) {
5819
- return `${getLoggingPrefix('warn')} Couldn't check for updates.\n${external_pintor_default().yellow('The command continues without the update check.')}\n${external_pintor_default().yellow(String(err?.message || err))}`;
5583
+ return result;
5820
5584
  }
5821
- function checkUpdates(packageJson, update) {
5822
- const latest = String(update.latest);
5823
- const releaseNotesUrl = `https://github.com/extension-js/extension.js/releases/tag/v${latest}`;
5824
- const suffix = external_pintor_default().gray(`(version ${latest} is available!)`);
5825
- const message = `${getLoggingPrefix('info')} 🧩 An ${external_pintor_default().blue('Extension.js')} update is available.\n\nYou are on version ${external_pintor_default().red(String(packageJson.version))}.\nThe latest stable is ${external_pintor_default().green(latest)}.\nSee what's new: ${external_pintor_default().underline(releaseNotesUrl)}\nUpdate to the latest stable to get fixes and new features.`;
5585
+ function backupAndRestoreXcodeSettings(config) {
5586
+ const projFile = pbxprojPath(config);
5587
+ let saved = {};
5588
+ if (node_fs__rspack_import_0.existsSync(projFile)) saved = extractXcodeUserSettings(node_fs__rspack_import_0.readFileSync(projFile, 'utf8'));
5826
5589
  return {
5827
- suffix,
5828
- message
5590
+ saved,
5591
+ restore () {
5592
+ if (0 === Object.keys(saved).length) return;
5593
+ if (!node_fs__rspack_import_0.existsSync(projFile)) return;
5594
+ const content = node_fs__rspack_import_0.readFileSync(projFile, 'utf8');
5595
+ node_fs__rspack_import_0.writeFileSync(projFile, applyXcodeUserSettings(content, saved), 'utf8');
5596
+ }
5829
5597
  };
5830
5598
  }
5831
- function programUserHelp() {
5832
- return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
5833
-
5834
- Usage: extension [command] [options]
5835
-
5836
- Notes
5837
- - All high-level commands offer their own \`--help\` with usage and flag lists.
5838
- - Telemetry is anonymous and privacy-safe by default; see ${messages_code('docs/TELEMETRY.md')} for the full contract.
5839
-
5840
- Example
5841
- - ${messages_code('extension create --help')} outputs information about the "create" command.
5842
-
5843
- Available Commands
5844
- ${availableCommandsBlock()}
5845
-
5846
- Common Options
5847
- - ${messages_code('--browser')} ${messages_arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chromium)
5848
- - ${messages_code('--profile')} ${messages_arg('<path|boolean>')} Browser profile configuration
5849
- - ${messages_code('--polyfill')} ${messages_arg('[boolean]')} Enable/disable cross-browser polyfill
5850
- - ${messages_code('--no-telemetry')} Disable anonymous telemetry for this run (persistent toggle: ${messages_code('extension telemetry disable')}, or ${messages_code('EXTENSION_TELEMETRY=0')})
5851
- - ${messages_code('--ai-help')} Show AI-assistant oriented help and tips
5852
- - ${messages_code('--output')} ${messages_arg('<pretty|json>')} Result format for ${messages_code('--ai-help')} and machine-readable command results (default: pretty)
5853
- ${messages_code('--format')} and ${messages_code('--wait-format')} still work as deprecated aliases of ${messages_code('--output')}
5854
- - ${messages_code('--help')} Show help output
5855
- - ${messages_code('--port')} ${messages_arg('<number>')} Development server port (default: 8080; use 0 for OS-assigned)
5856
- - ${messages_code('--host')} ${messages_arg('<address>')} Dev server host (default: 127.0.0.1; use 0.0.0.0 for Docker/devcontainers)
5857
- - ${messages_code('--public-host')} ${messages_arg('<address>')} Connectable host the browser dials for HMR + reload bridge when it differs from ${messages_code('--host')} (remote/devcontainer; default: the bind host, or 127.0.0.1 when bound to 0.0.0.0)
5858
- - ${messages_code('--starting-url')} ${messages_arg('<url>')} Initial URL to load in browser
5859
- - ${messages_code('--silent')} ${messages_arg('[boolean]')} Suppress console output during build
5860
-
5861
- Browser-Specific Options
5862
- - ${messages_code('--chromium-binary')} ${messages_arg('<path>')} Custom Chromium binary path
5863
- - ${messages_code('--gecko-binary')}/${messages_code('--firefox-binary')} ${messages_arg('<path>')} Custom Firefox/Gecko binary path
5864
- Use ${messages_code('flatpak:org.mozilla.firefox')} as the path to launch a Flatpak-installed Firefox
5865
-
5866
- Build Options
5867
- - ${messages_code('--zip')} ${messages_arg('[boolean]')} Create ZIP archive of built extension
5868
- - ${messages_code('--zip-source')} ${messages_arg('[boolean]')} Include source files in ZIP
5869
- - ${messages_code('--zip-filename')} ${messages_arg('<name>')} Custom ZIP filename
5870
-
5871
- ${external_pintor_default().underline('Centralized Logger (terminal output)')}
5872
- - The manager extension embeds a centralized logger that streams events to the CLI.
5873
- - Enable and filter logs directly via ${messages_code('extension dev')} flags:
5874
- - ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level (default: off)
5875
- - ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
5876
- - ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Output format (default: pretty)
5877
- - ${messages_code('--no-log-timestamps')} Hide ISO timestamps in pretty output
5878
- - ${messages_code('--no-log-color')} Disable color in pretty output
5879
- - ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by event.url
5880
- - ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
5881
- - Example: ${messages_code('extension dev ./my-ext --logs=debug --log-context=all --log-format=pretty')}
5882
-
5883
- ${messages_code('extension --help')}
5884
- This command outputs a help file with key command options.
5885
-
5886
- ${external_pintor_default().underline('Path Resolution (important)')}
5887
- - Leading ${messages_code('/')} in manifest/HTML means extension root (the directory containing ${messages_code('manifest.json')}).
5888
- - Relative paths resolve from the ${messages_code('manifest.json')} directory.
5889
- - Absolute OS paths are used as-is.
5890
-
5891
- AI Assistants
5892
- - For AI-oriented guidance and deeper tips, run ${messages_code('extension --ai-help')}
5893
- - For machine-readable AI guidance, run ${messages_code('extension --ai-help --output json')}
5894
-
5895
- Report issues
5896
- - ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
5599
+ __webpack_require__.d(__webpack_exports__, {
5600
+ F7: ()=>resolveSafariBuildConfig,
5601
+ Gi: ()=>xcodeProjectPath,
5602
+ IA: ()=>alignBundleIdentifiers,
5603
+ OA: ()=>isValidBundleId,
5604
+ _D: ()=>backupAndRestoreXcodeSettings,
5605
+ aD: ()=>macOsSchemeName,
5606
+ fd: ()=>builtAppPath,
5607
+ vh: ()=>pbxprojPath,
5608
+ vk: ()=>isProjectStale,
5609
+ vx: ()=>composeXcodebuildArgs,
5610
+ w7: ()=>composeConverterArgs,
5611
+ x7: ()=>saveManifestFingerprint
5612
+ }, {
5613
+ X9: PRESERVED_SETTINGS
5614
+ });
5615
+ },
5616
+ "./browsers/run-safari/safari-packager.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5617
+ var _safari_launch__rspack_import_0 = __webpack_require__("./browsers/run-safari/safari-launch/index.ts");
5618
+ function withoutUndefined(input) {
5619
+ if (!input) return {};
5620
+ return Object.fromEntries(Object.entries(input).filter(([, value])=>void 0 !== value));
5621
+ }
5622
+ function createSafariPackager(options = {}) {
5623
+ const { browser = 'safari', noOpen = true, dryRun = false, logger, ...identity } = options;
5624
+ return async (distPath, mode = 'full', overrides)=>await (0, _safari_launch__rspack_import_0.Ah)({
5625
+ extension: [
5626
+ distPath
5627
+ ],
5628
+ browser,
5629
+ noOpen,
5630
+ dryRun,
5631
+ ...withoutUndefined(identity),
5632
+ ...withoutUndefined(overrides)
5633
+ }, distPath, logger, mode);
5897
5634
  }
5898
- function unsupportedBrowserFlag(value, supported) {
5899
- return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}.\n${external_pintor_default().red('Choose one of:')} ${supported.join(', ')}${external_pintor_default().red('.')}`;
5635
+ __webpack_require__.d(__webpack_exports__, {
5636
+ P: ()=>createSafariPackager
5637
+ });
5638
+ },
5639
+ "./helpers/messaging.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
5640
+ var pintor__rspack_import_0 = __webpack_require__("pintor");
5641
+ var pintor__rspack_import_0_default = /*#__PURE__*/ __webpack_require__.n(pintor__rspack_import_0);
5642
+ const GLYPH = '⏵⏵⏵';
5643
+ const DEBUG_GLYPH = '···';
5644
+ const DEBUG_VALUES = new Set([
5645
+ '1',
5646
+ 'true',
5647
+ 'yes',
5648
+ 'on',
5649
+ 'debug',
5650
+ 'dev',
5651
+ 'development'
5652
+ ]);
5653
+ function isDebug() {
5654
+ const raw = process.env.EXTENSION_DEBUG ?? process.env.EXTENSION_AUTHOR_MODE;
5655
+ return DEBUG_VALUES.has(String(raw ?? '').trim().toLowerCase());
5900
5656
  }
5901
- function safariOnlyOption(flags) {
5902
- return `${getLoggingPrefix('error')} ${flags.map(messages_code).join(', ')} only appl${1 === flags.length ? 'ies' : 'y'} to Safari targets.\nAdd ${messages_code('--browser safari')} (or ${messages_code('webkit-based')}).`;
5657
+ function prefix(type) {
5658
+ if ('error' === type) return pintor__rspack_import_0_default().red(GLYPH);
5659
+ if ('warn' === type) return pintor__rspack_import_0_default().brightYellow(GLYPH);
5660
+ if ('success' === type) return pintor__rspack_import_0_default().green(GLYPH);
5661
+ if ('debug' === type) return pintor__rspack_import_0_default().dim(pintor__rspack_import_0_default().gray(DEBUG_GLYPH));
5662
+ return pintor__rspack_import_0_default().gray(GLYPH);
5903
5663
  }
5904
- function safariInvalidBundleId(bundleId) {
5905
- return `${getLoggingPrefix('error')} Invalid bundle identifier: ${messages_code(bundleId)}.\nUse reverse-DNS form: dot-separated segments of letters, digits and hyphens, each starting with a letter (e.g. ${messages_code('com.example.my-extension')}).`;
5664
+ const fmt = {
5665
+ heading: (title)=>pintor__rspack_import_0_default().underline(pintor__rspack_import_0_default().blue(title)),
5666
+ label: (key)=>pintor__rspack_import_0_default().gray(key.toUpperCase()),
5667
+ val: (value)=>pintor__rspack_import_0_default().underline(value),
5668
+ code: (value)=>pintor__rspack_import_0_default().blue(value),
5669
+ bullet: (value)=>`- ${value}`,
5670
+ block (title, rows) {
5671
+ const head = fmt.heading(title);
5672
+ const body = rows.map(([key, value])=>`${fmt.label(key)} ${value}`).join('\n');
5673
+ return `${head}\n${body}`;
5674
+ },
5675
+ truncate (input, max = 800) {
5676
+ const s = (()=>{
5677
+ try {
5678
+ return 'string' == typeof input ? input : JSON.stringify(input);
5679
+ } catch {
5680
+ return String(input);
5681
+ }
5682
+ })();
5683
+ return s.length > max ? `${s.slice(0, max)}…` : s;
5684
+ }
5685
+ };
5686
+ const CARD_LABEL_WIDTH = 15;
5687
+ function card(input = {}) {
5688
+ const version = String(input.version || '').trim();
5689
+ const suffix = String(input.suffix || '').trim();
5690
+ const head = ` 🧩 ${pintor__rspack_import_0_default().brightBlue('Extension.js')}` + (version ? ` ${pintor__rspack_import_0_default().gray(version)}` : '') + (suffix ? ` ${suffix}` : '');
5691
+ const body = (input.rows || []).filter((row)=>String(row.value || '').trim().length > 0).map((row)=>{
5692
+ const label = row.label.padEnd(CARD_LABEL_WIDTH);
5693
+ return ` ${label}${pintor__rspack_import_0_default().gray(String(row.value).trim())}`;
5694
+ });
5695
+ return [
5696
+ head,
5697
+ ...body
5698
+ ].join('\n');
5906
5699
  }
5907
- function safariCommandNotSupported(command) {
5908
- return `${getLoggingPrefix('error')} ${messages_code(command)} can't load an extension into Safari automatically.\nSafari extensions ship inside a signed app and are enabled by hand, so there's no live browser session to load into, unlike Chromium and Firefox.\nBuild the Safari app instead: ${messages_code('extension build --browser safari')}\nThen open the generated app and enable it in Safari → Settings → Extensions.`;
5700
+ const CARD_KEYS_ENV = 'EXTENSION_CLI_CARD_KEYS';
5701
+ const CARD_KEYS_SEPARATOR = '\u001f';
5702
+ function isCardKeyClaimed(key) {
5703
+ const claimed = String(process.env[CARD_KEYS_ENV] || '');
5704
+ if (!claimed) return false;
5705
+ return claimed.split(CARD_KEYS_SEPARATOR).includes(key);
5909
5706
  }
5910
- function programAIHelp() {
5911
- return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
5912
-
5913
- Browser-Specific Configuration
5914
- - Use browser prefixes in manifest.json for browser-specific fields:
5915
- ${messages_code('{"firefox:manifest": 2, "chrome:manifest": 3}')}
5916
- This applies manifest v2 to Firefox only, v3 to Chrome/Edge.
5917
-
5918
- Centralized Logger (for AI & CI)
5919
- - Logs from all contexts are centralized by the manager extension and streamed to the CLI.
5920
- - Prefer these flags to control terminal logs during ${messages_code('extension dev')}:
5921
- - ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level
5922
- - ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts to include
5923
- - ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Pretty for humans; JSON for machines/NDJSON pipelines
5924
- - ${messages_code('--no-log-timestamps')} ${messages_arg(' ')} Disable timestamps (pretty)
5925
- - ${messages_code('--no-log-color')} ${messages_arg(' ')} Disable ANSI colors (pretty)
5926
- - ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by URL
5927
- - ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
5928
- - Good CI pattern: ${messages_code('EXTENSION_DEBUG=1 EXTENSION_AUTO_EXIT_MS=6000 extension dev ./ext --logs=info --log-format=json')}
5929
-
5930
- Special Folders for Entrypoints
5931
- - Use special folders to handle entrypoints and assets not declared in manifest.json:
5932
- - ${external_pintor_default().underline(messages_code('public/'))} - Static assets automatically copied to build (resolves to output root)
5933
- - ${external_pintor_default().underline(messages_code('pages/'))} - HTML files not declared in manifest (e.g., welcome pages)
5934
- - ${external_pintor_default().underline(messages_code("scripts/"))} - JavaScript files not declared in manifest (e.g., executable scripts)
5935
-
5936
- Predictable Output Paths
5937
- - Core HTML destinations are standardized across browsers so you can reference them safely in code/tests:
5938
- - ${messages_code('devtools_page')} → ${messages_code('devtools/index.html')}
5939
- - ${messages_code('sidebar_action.default_panel')} (MV2) and ${messages_code('side_panel.default_path')} (MV3) → ${messages_code('sidebar/index.html')}
5940
- - ${messages_code('options_ui.page')} and ${messages_code('options_page')} → ${messages_code('options/index.html')}
5941
- - ${messages_code('background.page')} → ${messages_code('background/index.html')}
5942
- - ${messages_code('action.default_popup')}, ${messages_code('browser_action.default_popup')}, ${messages_code('page_action.default_popup')} → ${messages_code('action/index.html')}
5943
- - Other predictable outputs:
5944
- - ${messages_code('chrome_url_overrides.*')} → ${messages_code('chrome_url_overrides/<key>.html')}
5945
- - ${messages_code("content_scripts[n].js/css")} → ${messages_code("content_scripts/content-<n>.{js,css}")}
5946
- - ${messages_code('sandbox.pages[]')} → ${messages_code('sandbox/page-<n>.html')}
5947
- - ${messages_code("user_scripts.api_script")} → ${messages_code("user_scripts/api_script.js")}
5948
- - ${messages_code('icons/*')} → ${messages_code('icons/')} (feature-specific icon folders preserved where applicable)
5949
-
5950
- Public & Special Folders (Output Behavior)
5951
- - ${external_pintor_default().underline(messages_code('public/'))} is the web root in output. Authors can use ${messages_code('/foo')}, ${messages_code('/public/foo')}, ${messages_code('public/foo')}, or ${messages_code('./public/foo')} and they all emit as ${messages_code('dist/<browser>/foo')}.
5952
- - ${external_pintor_default().underline(messages_code('pages/'))} files emit as ${messages_code('pages/<name>.html')}. Relative assets referenced inside page HTML are emitted under ${messages_code('assets/')} preserving relative structure; public-root URLs are preserved.
5953
- - ${external_pintor_default().underline(messages_code("scripts/"))} files emit as ${messages_code("scripts/<name>.js")} with extracted CSS when applicable.
5954
-
5955
- Shadow DOM for Content Scripts
5956
- - Add ${messages_code('use shadow-dom')} directive to content scripts for style isolation
5957
- - Automatically creates ${messages_code('#extension-root')} element with shadow DOM
5958
- - All CSS imports are automatically injected into shadow DOM
5959
- - Prevents style conflicts with host page
5960
-
5961
- Environment Variables
5962
- - Use ${messages_code(messages_arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
5963
- - Supported in both ${messages_code('process.env')} and ${messages_code('import.meta.env')}
5964
- - Environment file priority: ${external_pintor_default().underline(messages_code(messages_arg('.env.{browser}.{mode}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env.{browser}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env.{mode}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env')))}
5965
- - Example: ${messages_code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
5966
-
5967
- Available Templates
5968
- - ${external_pintor_default().green('Frameworks')}: ${messages_code(messages_arg('react'))}, ${messages_code(messages_arg('preact'))}, ${messages_code(messages_arg('vue'))}, ${messages_code(messages_arg('svelte'))}
5969
- - ${external_pintor_default().green('Languages')}: ${messages_code(messages_arg("javascript"))}, ${messages_code(messages_arg("typescript"))}
5970
- - ${external_pintor_default().green('Contexts')}: ${messages_code(messages_arg('content'))} (content scripts), ${messages_code(messages_arg('new'))} (new tab), ${messages_code(messages_arg('action'))} (popup)
5971
- - ${external_pintor_default().green('Styling')}: ${messages_code(messages_arg('tailwind'))}, ${messages_code(messages_arg('sass'))}, ${messages_code(messages_arg('less'))}
5972
- - ${external_pintor_default().green('Configs')}: ${messages_code(messages_arg('eslint'))}, ${messages_code(messages_arg('prettier'))}, ${messages_code(messages_arg('stylelint'))}
5973
-
5974
- Webpack/Rspack Configuration
5975
- - Create ${external_pintor_default().underline(messages_code(messages_arg('extension.config.js')))} for custom webpack configuration
5976
- - Function receives base config, return modified config
5977
- - Supports all webpack/rspack loaders and plugins
5978
- - Example:
5979
- ${messages_code('export default {')}
5980
- ${messages_code(' config: (config) => {')}
5981
- ${messages_code(" config.module.rules.push({ test: /\\.svg$/, use: ['@svgr/webpack'] })")}
5982
- ${messages_code(' return config')}
5983
- ${messages_code(' }')}
5984
- ${messages_code('}')}
5985
-
5986
- Managed Dependencies (Important)
5987
- - ${external_pintor_default().green('Do not add')} packages that ${external_pintor_default().blue('Extension.js')} already ships in its own toolchain.
5988
- - The guard only triggers when a managed package is declared in your ${messages_code('package.json')} ${external_pintor_default().gray('and')} is imported (as a module specifier) in your ${external_pintor_default().underline(messages_code('extension.config.js'))}.
5989
- - In that case, the program will ${external_pintor_default().red('print an error and abort')} to avoid version conflicts.
5990
- - Remove the duplicate from your project ${messages_code('package.json')} or avoid referencing it in ${external_pintor_default().underline(messages_code('extension.config.js'))} and rely on the built-in version instead.
5991
- - If you truly need a different version, open an issue so we can evaluate a safe upgrade.
5992
-
5993
- Framework-Specific Configuration
5994
- - Create ${external_pintor_default().underline(messages_code(messages_arg('vue.loader.js')))} for Vue-specific loader configuration
5995
- - Create ${external_pintor_default().underline(messages_code(messages_arg('svelte.loader.js')))} for Svelte-specific loader configuration
5996
- - Automatically detected and used by Extension.js
5997
- - Example svelte.loader.js:
5998
- ${messages_code('module.exports = {')}
5999
- ${messages_code(' preprocess: require("svelte-preprocess")({')}
6000
- ${messages_code(" typescript: true")}
6001
- ${messages_code(' })')}
6002
- ${messages_code('}')}
6003
-
6004
- Hot Module Replacement (HMR)
6005
- - Automatically enabled in development mode
6006
- - CSS changes trigger automatic style updates
6007
- - React components fast-refresh; Svelte components hot-update
6008
- - Preact and Vue rebuild with a live reload (component state resets)
6009
- - Content scripts automatically re-inject on changes
6010
- - Service workers, _locales and manifest changes reload the extension
6011
-
6012
- Non-Destructive Testing in CI
6013
- - Prefer ${messages_code('EXTENSION_DEBUG=1')} to copy local templates and avoid network.
6014
- - Reuse Playwright's Chromium via ${messages_code('--chromium-binary')} path when available.
6015
- - Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} and ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
6016
-
6017
- File Watching & HMR Examples
6018
- - Content script JS/TS changes trigger reinjection; CSS changes update styles live.
6019
- - For watch-source HTML prints, update a visible string in ${messages_code("content/scripts.*")} and assert it appears in stdout.
6020
-
6021
- Troubleshooting
6022
- - Use ${messages_code('--silent true')} during builds to reduce noise; logs still surface errors.
6023
- - When ports conflict, pass ${messages_code('--port 0')} to auto-select an available port.
6024
- - In Docker/devcontainers, pass ${messages_code('--host 0.0.0.0')} so the dev server is reachable from the host.
6025
-
6026
- Non-Interactive / Auto Mode (CI)
6027
- - Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
6028
- Useful when ${messages_code('pnpm extension dev')} would otherwise hang under Rspack watch.
6029
- Example: ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --starting-url ${messages_arg('https://example.com')}
6030
- - Optional: ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
6031
-
6032
- Docker / Devcontainers / Codespaces
6033
- - Use ${messages_code('--host 0.0.0.0')} to bind the dev server on all interfaces so HMR is reachable from the host.
6034
- - Use ${messages_code('--no-browser')} inside the container and load the extension manually from ${messages_code('dist/<browser>/')} in your host browser.
6035
- - Chromium sandbox flags (${messages_code('--no-sandbox')}) are added automatically when Docker, Podman, devcontainers, or Codespaces are detected.
6036
- - File watching uses polling by default (1 s interval), which works across bind-mounted volumes.
6037
- - Example: ${messages_code('extension dev ./my-ext --host 0.0.0.0 --no-browser --port 8080')}
6038
-
6039
- Flatpak Firefox
6040
- - Use ${messages_code('--gecko-binary flatpak:org.mozilla.firefox')} (or ${messages_code('--firefox-binary')}) to launch a Flatpak-installed Firefox.
6041
- - Extension.js rewrites the binary path to ${messages_code('flatpak run')} with the correct filesystem grants automatically.
6042
-
6043
- Cross-Browser Compatibility
6044
- - Use ${messages_code('--polyfill')} flag to enable webextension-polyfill
6045
- - Automatically handles browser API differences
6046
- - Supports Chrome, Edge, Firefox with single codebase`;
5707
+ function claimCardKey(key) {
5708
+ if (isCardKeyClaimed(key)) return false;
5709
+ const claimed = String(process.env[CARD_KEYS_ENV] || '');
5710
+ process.env[CARD_KEYS_ENV] = claimed ? `${claimed}${CARD_KEYS_SEPARATOR}${key}` : key;
5711
+ return true;
5712
+ }
5713
+ const GECKO_BROWSERS = new Set([
5714
+ 'firefox',
5715
+ 'firefox-based',
5716
+ 'gecko-based',
5717
+ 'librewolf',
5718
+ 'waterfox'
5719
+ ]);
5720
+ function artifactNoun(browser) {
5721
+ const name = String(browser || '').trim().toLowerCase();
5722
+ if (GECKO_BROWSERS.has(name)) return 'Add-on';
5723
+ if (name.includes('gecko') || name.includes('firefox')) return 'Add-on';
5724
+ return 'Extension';
5725
+ }
5726
+ const ENVELOPE_SCHEMA = 1;
5727
+ const CODES = {
5728
+ E_ARGS: 'E_ARGS',
5729
+ E_PROJECT_NOT_FOUND: 'E_PROJECT_NOT_FOUND',
5730
+ E_MANIFEST_NOT_FOUND: 'E_MANIFEST_NOT_FOUND',
5731
+ E_MANIFEST_INVALID: 'E_MANIFEST_INVALID',
5732
+ E_FIRST_COMPILE: 'E_FIRST_COMPILE',
5733
+ E_COMPILE: 'E_COMPILE',
5734
+ E_BROWSER_NOT_FOUND: 'E_BROWSER_NOT_FOUND',
5735
+ E_BROWSER_LAUNCH: 'E_BROWSER_LAUNCH',
5736
+ E_PROFILE_LOCKED: 'E_PROFILE_LOCKED',
5737
+ E_READY_TIMEOUT: 'E_READY_TIMEOUT',
5738
+ E_SESSION_EXISTS: 'E_SESSION_EXISTS',
5739
+ E_CONTROL_DENIED: 'E_CONTROL_DENIED',
5740
+ E_CONTROL_UNAVAILABLE: 'E_CONTROL_UNAVAILABLE',
5741
+ E_TOKEN_MISSING: 'E_TOKEN_MISSING',
5742
+ E_NETWORK: 'E_NETWORK',
5743
+ E_INTERRUPTED: 'E_INTERRUPTED',
5744
+ E_NOT_IMPLEMENTED: 'E_NOT_IMPLEMENTED',
5745
+ E_INTERNAL: 'E_INTERNAL',
5746
+ E_UNSUPPORTED_BROWSER: 'E_UNSUPPORTED_BROWSER',
5747
+ E_INVALID_OPTION: 'E_INVALID_OPTION',
5748
+ E_PORT_UNAVAILABLE: 'E_PORT_UNAVAILABLE',
5749
+ E_DEPENDENCY_INSTALL: 'E_DEPENDENCY_INSTALL',
5750
+ E_TEMPLATE_NOT_FOUND: 'E_TEMPLATE_NOT_FOUND',
5751
+ E_DESTINATION_NOT_EMPTY: 'E_DESTINATION_NOT_EMPTY',
5752
+ E_DESTINATION_NOT_WRITABLE: 'E_DESTINATION_NOT_WRITABLE',
5753
+ E_BROWSER_DOWNLOAD: 'E_BROWSER_DOWNLOAD',
5754
+ E_SESSION_NOT_FOUND: 'E_SESSION_NOT_FOUND',
5755
+ E_EVAL_REFUSED: 'E_EVAL_REFUSED',
5756
+ E_TARGET_NOT_FOUND: 'E_TARGET_NOT_FOUND',
5757
+ E_TIMEOUT: 'E_TIMEOUT',
5758
+ E_PUBLISH_REJECTED: 'E_PUBLISH_REJECTED',
5759
+ E_AUTH_REQUIRED: 'E_AUTH_REQUIRED',
5760
+ E_SAFARI_TOOLCHAIN: 'E_SAFARI_TOOLCHAIN',
5761
+ E_EVAL: 'E_EVAL',
5762
+ E_INSPECT: 'E_INSPECT',
5763
+ E_STORAGE: 'E_STORAGE',
5764
+ E_LOGS_NOT_FOUND: 'E_LOGS_NOT_FOUND',
5765
+ E_READY_ERROR_STATUS: 'E_READY_ERROR_STATUS',
5766
+ E_COMMAND_UNSUPPORTED_FOR_TARGET: 'E_COMMAND_UNSUPPORTED_FOR_TARGET',
5767
+ E_FLAG_VALUE_INVALID: 'E_FLAG_VALUE_INVALID',
5768
+ E_FLAG_NOT_SUPPORTED_HERE: 'E_FLAG_NOT_SUPPORTED_HERE',
5769
+ E_DEV_SERVER_START: 'E_DEV_SERVER_START',
5770
+ E_DOCTOR_CHECKS_FAILED: 'E_DOCTOR_CHECKS_FAILED',
5771
+ E_TELEMETRY_WRITE: 'E_TELEMETRY_WRITE',
5772
+ E_PREVIEW_NO_DIST: 'E_PREVIEW_NO_DIST',
5773
+ E_BROWSER_UNINSTALL: 'E_BROWSER_UNINSTALL',
5774
+ E_NODE_VERSION: 'E_NODE_VERSION',
5775
+ E_UNKNOWN_COMMAND: 'E_UNKNOWN_COMMAND',
5776
+ E_REMOVED_FLAG: 'E_REMOVED_FLAG',
5777
+ E_BROWSER_NOT_INSTALLABLE: 'E_BROWSER_NOT_INSTALLABLE',
5778
+ E_PARENT_GONE: 'E_PARENT_GONE',
5779
+ E_REMOTE_URL_UNSUPPORTED: 'E_REMOTE_URL_UNSUPPORTED',
5780
+ E_CONFIG_LOAD: 'E_CONFIG_LOAD',
5781
+ E_MANAGED_DEP_CONFLICT: 'E_MANAGED_DEP_CONFLICT',
5782
+ E_TYPES_EMIT: 'E_TYPES_EMIT',
5783
+ E_TSCONFIG_MISSING: 'E_TSCONFIG_MISSING',
5784
+ E_OPTIONAL_DEP_UNRESOLVED: 'E_OPTIONAL_DEP_UNRESOLVED',
5785
+ E_OPTIONAL_DEP_LOAD: 'E_OPTIONAL_DEP_LOAD',
5786
+ E_OPTIONAL_DEP_UNKNOWN: 'E_OPTIONAL_DEP_UNKNOWN',
5787
+ E_COMPANION_EXTENSION_PATH: 'E_COMPANION_EXTENSION_PATH',
5788
+ E_MANIFEST_IN_PUBLIC: 'E_MANIFEST_IN_PUBLIC',
5789
+ E_RUNTIME_NOT_FOUND: 'E_RUNTIME_NOT_FOUND',
5790
+ E_MANIFEST_SHAPE: 'E_MANIFEST_SHAPE',
5791
+ E_MANIFEST_PAGE_MISSING: 'E_MANIFEST_PAGE_MISSING',
5792
+ E_MANIFEST_VERSION_UNSUPPORTED: 'E_MANIFEST_VERSION_UNSUPPORTED',
5793
+ E_MANIFEST_LOAD_BLOCKERS: 'E_MANIFEST_LOAD_BLOCKERS',
5794
+ E_MANIFEST_PERMISSION_MISSING: 'E_MANIFEST_PERMISSION_MISSING',
5795
+ E_MANIFEST_MSG_KEY_MISSING: 'E_MANIFEST_MSG_KEY_MISSING',
5796
+ E_MANIFEST_EMIT: 'E_MANIFEST_EMIT',
5797
+ E_RESTART_REQUIRED: 'E_RESTART_REQUIRED',
5798
+ E_COMPILE_FATAL: 'E_COMPILE_FATAL',
5799
+ E_MODULE_NOT_FOUND: 'E_MODULE_NOT_FOUND',
5800
+ E_ENTRY_NOT_FOUND: 'E_ENTRY_NOT_FOUND',
5801
+ E_ASSET_MISSING: 'E_ASSET_MISSING',
5802
+ E_SCRIPT_DEP_MISSING: 'E_SCRIPT_DEP_MISSING',
5803
+ E_RESERVED_FOLDER: 'E_RESERVED_FOLDER',
5804
+ E_CSS_PARSE: 'E_CSS_PARSE',
5805
+ E_CSS_PREPROCESSOR_MISSING: 'E_CSS_PREPROCESSOR_MISSING',
5806
+ E_CSS_DEAD_REF: 'E_CSS_DEAD_REF',
5807
+ E_INTEGRATION_INSTALL: 'E_INTEGRATION_INSTALL',
5808
+ E_POLYFILL_NOT_FOUND: 'E_POLYFILL_NOT_FOUND',
5809
+ E_LOCALES_LAYOUT: 'E_LOCALES_LAYOUT',
5810
+ E_WAR_INVALID: 'E_WAR_INVALID',
5811
+ E_MATCH_PATTERN_INVALID: 'E_MATCH_PATTERN_INVALID',
5812
+ E_BACKGROUND_REQUIRED: 'E_BACKGROUND_REQUIRED',
5813
+ E_CONTENT_SCRIPT_SYNTAX: 'E_CONTENT_SCRIPT_SYNTAX',
5814
+ E_NO_ENTRYPOINTS: 'E_NO_ENTRYPOINTS',
5815
+ E_REMOTE_RESOURCE_BLOCKED: 'E_REMOTE_RESOURCE_BLOCKED',
5816
+ E_PERF_BUDGET: 'E_PERF_BUDGET',
5817
+ E_ZIP_SKIPPED: 'E_ZIP_SKIPPED',
5818
+ E_ENV_NO_MATCH: 'E_ENV_NO_MATCH',
5819
+ E_REMOTE_FETCH_TIMEOUT: 'E_REMOTE_FETCH_TIMEOUT',
5820
+ E_REMOTE_DOWNLOAD: 'E_REMOTE_DOWNLOAD',
5821
+ E_REMOTE_ZIP_INVALID: 'E_REMOTE_ZIP_INVALID',
5822
+ E_LOCAL_ZIP_NOT_FOUND: 'E_LOCAL_ZIP_NOT_FOUND',
5823
+ E_PROJECT_DOWNLOAD_EMPTY: 'E_PROJECT_DOWNLOAD_EMPTY',
5824
+ E_BROWSER_BINARY_REQUIRED: 'E_BROWSER_BINARY_REQUIRED',
5825
+ E_BROWSER_BINARY_INVALID: 'E_BROWSER_BINARY_INVALID',
5826
+ E_BROWSER_EXITED: 'E_BROWSER_EXITED',
5827
+ E_BROWSER_START_TIMEOUT: 'E_BROWSER_START_TIMEOUT',
5828
+ E_LAUNCH_SKIPPED_COMPILE_ERRORS: 'E_LAUNCH_SKIPPED_COMPILE_ERRORS',
5829
+ E_INSTANCE_AMBIGUOUS: 'E_INSTANCE_AMBIGUOUS',
5830
+ E_WSL_INTEROP: 'E_WSL_INTEROP',
5831
+ E_EXTENSION_LOAD_REFUSED: 'E_EXTENSION_LOAD_REFUSED',
5832
+ E_ADDON_INSTALL: 'E_ADDON_INSTALL',
5833
+ E_BROWSER_CONNECT: 'E_BROWSER_CONNECT',
5834
+ E_BROWSER_CONNECTION_CLOSED: 'E_BROWSER_CONNECTION_CLOSED',
5835
+ E_CDP_NOT_CONNECTED: 'E_CDP_NOT_CONNECTED',
5836
+ E_CDP_TIMEOUT: 'E_CDP_TIMEOUT',
5837
+ E_CDP_OP_FAILED: 'E_CDP_OP_FAILED',
5838
+ E_EXTENSION_ID_UNKNOWN: 'E_EXTENSION_ID_UNKNOWN',
5839
+ E_RDP_PROTOCOL: 'E_RDP_PROTOCOL',
5840
+ E_DEV_SERVER_TIMEOUT: 'E_DEV_SERVER_TIMEOUT',
5841
+ E_PORT_IN_USE: 'E_PORT_IN_USE',
5842
+ E_SESSION_STOPPED: 'E_SESSION_STOPPED',
5843
+ E_LOGS_STREAM_GAP: 'E_LOGS_STREAM_GAP',
5844
+ E_CREATE_DIR: 'E_CREATE_DIR',
5845
+ E_CREATE_WRITE: 'E_CREATE_WRITE',
5846
+ E_CREATE_TESTS_SETUP: 'E_CREATE_TESTS_SETUP',
5847
+ E_GIT_SKIPPED: 'E_GIT_SKIPPED',
5848
+ E_BROWSER_INSTALL_PRIVILEGE: 'E_BROWSER_INSTALL_PRIVILEGE',
5849
+ E_UNINSTALL_NOOP: 'E_UNINSTALL_NOOP'
5850
+ };
5851
+ function withExtras(base, extras) {
5852
+ return {
5853
+ ...base,
5854
+ ...extras.hint ? {
5855
+ hint: extras.hint
5856
+ } : {},
5857
+ ...extras.truncated ? {
5858
+ truncated: true
5859
+ } : {},
5860
+ warnings: extras.warnings || []
5861
+ };
5862
+ }
5863
+ const ENVELOPE = {
5864
+ schema: ENVELOPE_SCHEMA,
5865
+ ok (command, status, value, extras = {}) {
5866
+ return withExtras({
5867
+ schema: ENVELOPE_SCHEMA,
5868
+ ok: true,
5869
+ command,
5870
+ status,
5871
+ value,
5872
+ error: null,
5873
+ warnings: []
5874
+ }, extras);
5875
+ },
5876
+ fail (command, status, error, extras = {}) {
5877
+ return withExtras({
5878
+ schema: ENVELOPE_SCHEMA,
5879
+ ok: false,
5880
+ command,
5881
+ status,
5882
+ value: extras.value ?? null,
5883
+ error,
5884
+ warnings: []
5885
+ }, extras);
5886
+ }
5887
+ };
5888
+ __webpack_require__.d(__webpack_exports__, {
5889
+ Nr: ()=>card,
5890
+ Pl: ()=>prefix,
5891
+ T_: ()=>artifactNoun,
5892
+ VR: ()=>isCardKeyClaimed,
5893
+ _o: ()=>isDebug,
5894
+ od: ()=>claimCardKey
5895
+ }, {
5896
+ Lp: CODES,
5897
+ Pr: ENVELOPE,
5898
+ Sc: fmt
5899
+ });
5900
+ },
5901
+ "./index.ts" (__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
5902
+ const MIN_NODE_MAJOR = 22;
5903
+ const MIN_NODE_MINOR = 12;
5904
+ function isSupportedNodeVersion(version) {
5905
+ const [major, minor] = version.split('.').map((part)=>parseInt(part, 10));
5906
+ if (!Number.isFinite(major)) return true;
5907
+ if (major !== MIN_NODE_MAJOR) return major > MIN_NODE_MAJOR;
5908
+ return (Number.isFinite(minor) ? minor : 0) >= MIN_NODE_MINOR;
5909
+ }
5910
+ function unsupportedNodeVersionMessage(version) {
5911
+ return `[Extension.js] Requires Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR} (you are on ${version}). Upgrade Node.js to run the extension CLI.`;
5912
+ }
5913
+ function enforceSupportedNodeVersion(version = process.versions.node) {
5914
+ if (isSupportedNodeVersion(version)) return;
5915
+ console.error(unsupportedNodeVersionMessage(version));
5916
+ process.exit(1);
5917
+ }
5918
+ enforceSupportedNodeVersion();
5919
+ const external_commander_namespaceObject = require("commander");
5920
+ var external_node_fs_ = __webpack_require__("node:fs");
5921
+ var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_);
5922
+ var external_node_path_ = __webpack_require__("node:path");
5923
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_);
5924
+ async function exitAfterDrain(code) {
5925
+ await Promise.all([
5926
+ process.stdout,
5927
+ process.stderr
5928
+ ].map((stream)=>new Promise((resolve)=>{
5929
+ if (0 === stream.writableLength) return resolve();
5930
+ stream.write('', ()=>resolve());
5931
+ })));
5932
+ process.exit(code);
5933
+ }
5934
+ var external_node_module_ = __webpack_require__("node:module");
5935
+ const external_node_url_namespaceObject = require("node:url");
5936
+ const extension_develop_runtime_require = (0, external_node_module_.createRequire)(__rslib_import_meta_url__);
5937
+ function parseJsonSafe(filePath) {
5938
+ return JSON.parse(external_node_fs_default().readFileSync(filePath, 'utf8'));
5939
+ }
5940
+ function resolveDevelopRootFromDir(dir) {
5941
+ try {
5942
+ const packageJsonPath = external_node_path_default().join(dir, 'package.json');
5943
+ if (!external_node_fs_default().existsSync(packageJsonPath)) return;
5944
+ const pkg = parseJsonSafe(packageJsonPath);
5945
+ return pkg?.name === 'extension-develop' ? dir : void 0;
5946
+ } catch {
5947
+ return;
5948
+ }
6047
5949
  }
6048
- function programAIHelpJSON(version) {
5950
+ function resolveWorkspaceDevelopRoot(startDir) {
5951
+ let currentDir = external_node_path_default().resolve(startDir);
5952
+ if (currentDir.split(external_node_path_default().sep).includes('node_modules')) return;
5953
+ for(let depth = 0; depth < 8; depth += 1){
5954
+ const candidate = external_node_path_default().join(currentDir, 'programs', 'develop');
5955
+ const resolved = resolveDevelopRootFromDir(candidate);
5956
+ if (resolved) return resolved;
5957
+ const parent = external_node_path_default().dirname(currentDir);
5958
+ if (parent === currentDir) break;
5959
+ currentDir = parent;
5960
+ }
5961
+ }
5962
+ function resolveInstalledDevelopRoot() {
5963
+ try {
5964
+ const packageJsonPath = extension_develop_runtime_require.resolve('extension-develop/package.json');
5965
+ return resolveDevelopRootFromDir(external_node_path_default().dirname(packageJsonPath));
5966
+ } catch {
5967
+ try {
5968
+ const entryPath = extension_develop_runtime_require.resolve('extension-develop');
5969
+ return resolveDevelopRootFromDir(external_node_path_default().dirname(external_node_path_default().dirname(entryPath)));
5970
+ } catch {
5971
+ return;
5972
+ }
5973
+ }
5974
+ }
5975
+ function resolvePreferredDevelopRoot(startDir) {
5976
+ const workspaceRoot = resolveWorkspaceDevelopRoot(startDir);
5977
+ if (workspaceRoot) return {
5978
+ root: workspaceRoot,
5979
+ source: 'workspace'
5980
+ };
5981
+ const envRoot = process.env.EXTENSION_DEVELOP_ROOT ? resolveDevelopRootFromDir(external_node_path_default().resolve(process.env.EXTENSION_DEVELOP_ROOT)) : void 0;
5982
+ if (envRoot) return {
5983
+ root: envRoot,
5984
+ source: 'env'
5985
+ };
5986
+ const installedRoot = resolveInstalledDevelopRoot();
5987
+ if (installedRoot) return {
5988
+ root: installedRoot,
5989
+ source: 'installed'
5990
+ };
6049
5991
  return {
6050
- version,
6051
- commands: COMMANDS.map((spec)=>({
6052
- name: spec.name,
6053
- summary: spec.description,
6054
- supportsSourceInspection: spec.supportsSourceInspection
6055
- })),
6056
- globalOptions: [
5992
+ root: void 0,
5993
+ source: 'missing'
5994
+ };
5995
+ }
5996
+ function resolveDevelopDistEntry(root, name = 'module') {
5997
+ const base = external_node_path_default().join(root, 'dist', name);
5998
+ const candidates = [
5999
+ `${base}.mjs`,
6000
+ `${base}.cjs`,
6001
+ `${base}.js`,
6002
+ base
6003
+ ];
6004
+ for (const candidate of candidates)if (external_node_fs_default().existsSync(candidate)) return candidate;
6005
+ }
6006
+ function resolveExtensionDevelopRoot(startDir = __dirname) {
6007
+ const { root, source } = resolvePreferredDevelopRoot(startDir);
6008
+ if (!root) throw new Error('Unable to locate the extension-develop runtime.');
6009
+ process.env.EXTENSION_DEVELOP_ROOT = root;
6010
+ if ('workspace' === source) process.env.EXTENSION_CREATE_DEVELOP_ROOT = root;
6011
+ return root;
6012
+ }
6013
+ function resolveExtensionDevelopVersion(startDir = __dirname, fallbackVersion) {
6014
+ try {
6015
+ const root = resolveExtensionDevelopRoot(startDir);
6016
+ const packageJsonPath = external_node_path_default().join(root, 'package.json');
6017
+ const pkg = parseJsonSafe(packageJsonPath);
6018
+ return pkg?.version || fallbackVersion || '0.0.0';
6019
+ } catch {
6020
+ return fallbackVersion || '0.0.0';
6021
+ }
6022
+ }
6023
+ async function importModule(filePath) {
6024
+ const mod = await import((0, external_node_url_namespaceObject.pathToFileURL)(filePath).href);
6025
+ return mod?.default && 'object' == typeof mod.default ? {
6026
+ ...mod.default,
6027
+ ...mod
6028
+ } : mod;
6029
+ }
6030
+ async function loadExtensionDevelopModule(startDir = __dirname) {
6031
+ const root = resolveExtensionDevelopRoot(startDir);
6032
+ const { source } = resolvePreferredDevelopRoot(startDir);
6033
+ const distEntry = resolveDevelopDistEntry(root);
6034
+ if (distEntry) return importModule(distEntry);
6035
+ if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
6036
+ return await import("extension-develop");
6037
+ }
6038
+ async function loadExtensionDevelopPreviewModule(startDir = __dirname) {
6039
+ const root = resolveExtensionDevelopRoot(startDir);
6040
+ const { source } = resolvePreferredDevelopRoot(startDir);
6041
+ const previewEntry = resolveDevelopDistEntry(root, 'preview');
6042
+ if (previewEntry) return importModule(previewEntry);
6043
+ const fullEntry = resolveDevelopDistEntry(root);
6044
+ if (fullEntry) return importModule(fullEntry);
6045
+ if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
6046
+ return await import("extension-develop/preview");
6047
+ }
6048
+ async function loadExtensionDevelopBridgeModule(startDir = __dirname) {
6049
+ const root = resolveExtensionDevelopRoot(startDir);
6050
+ const { source } = resolvePreferredDevelopRoot(startDir);
6051
+ const bridgeEntry = resolveDevelopDistEntry(root, 'bridge');
6052
+ if (bridgeEntry) return importModule(bridgeEntry);
6053
+ if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_node_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
6054
+ const bridgeSpecifier = 'extension-develop/bridge';
6055
+ return await import(bridgeSpecifier);
6056
+ }
6057
+ var external_pintor_ = __webpack_require__("pintor");
6058
+ var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_);
6059
+ var messaging = __webpack_require__("./helpers/messaging.ts");
6060
+ function getLoggingPrefix(type) {
6061
+ return (0, messaging.Pl)(type);
6062
+ }
6063
+ const messages_code = (text)=>external_pintor_default().blue(text);
6064
+ const messages_arg = (text)=>external_pintor_default().gray(text);
6065
+ function renderArgToken(spec) {
6066
+ const token = spec.values ? spec.values.join('|') : spec.label ?? spec.name;
6067
+ return spec.required ? `<${token}>` : `[${token}]`;
6068
+ }
6069
+ function commandArgSignature(positionals) {
6070
+ return positionals.map(renderArgToken).join(' ');
6071
+ }
6072
+ const COMMAND_TABLE = [
6073
+ {
6074
+ name: 'create',
6075
+ positionals: [
6057
6076
  {
6058
- name: '--ai-help',
6059
- description: 'Show AI-assistant oriented help and tips'
6077
+ name: 'project-name|project-path',
6078
+ required: true
6079
+ }
6080
+ ],
6081
+ description: 'Create a new extension from a template (React, TypeScript, Vue, Svelte, etc.)',
6082
+ supportsSourceInspection: false
6083
+ },
6084
+ {
6085
+ name: 'dev',
6086
+ positionals: [
6087
+ {
6088
+ name: 'project-path|remote-url',
6089
+ required: false
6090
+ }
6091
+ ],
6092
+ description: 'Start the development server with hot reloading',
6093
+ supportsSourceInspection: true
6094
+ },
6095
+ {
6096
+ name: 'start',
6097
+ positionals: [
6098
+ {
6099
+ name: 'project-path|remote-url',
6100
+ required: false
6101
+ }
6102
+ ],
6103
+ description: 'Build and start the extension in production mode',
6104
+ supportsSourceInspection: false
6105
+ },
6106
+ {
6107
+ name: 'preview',
6108
+ positionals: [
6109
+ {
6110
+ name: 'project-name',
6111
+ required: false,
6112
+ label: 'project-path|remote-url'
6113
+ }
6114
+ ],
6115
+ description: 'Preview the extension in production mode without building',
6116
+ supportsSourceInspection: false
6117
+ },
6118
+ {
6119
+ name: 'build',
6120
+ positionals: [
6121
+ {
6122
+ name: 'project-name',
6123
+ required: false,
6124
+ label: 'project-path|remote-url'
6125
+ }
6126
+ ],
6127
+ description: 'Build the extension for packaging and distribution',
6128
+ supportsSourceInspection: false
6129
+ },
6130
+ {
6131
+ name: 'logs',
6132
+ positionals: [
6133
+ {
6134
+ name: 'project-path',
6135
+ required: false
6136
+ }
6137
+ ],
6138
+ description: 'Print or stream logs from every context of a running dev session',
6139
+ supportsSourceInspection: false
6140
+ },
6141
+ {
6142
+ name: 'eval',
6143
+ positionals: [
6144
+ {
6145
+ name: 'expression',
6146
+ required: true
6147
+ },
6148
+ {
6149
+ name: 'project-path',
6150
+ required: false
6151
+ }
6152
+ ],
6153
+ description: 'Evaluate an expression in a running extension context (requires --allow-eval)',
6154
+ supportsSourceInspection: false
6155
+ },
6156
+ {
6157
+ name: 'storage',
6158
+ positionals: [
6159
+ {
6160
+ name: 'action',
6161
+ required: true,
6162
+ values: [
6163
+ 'get',
6164
+ 'set'
6165
+ ]
6166
+ },
6167
+ {
6168
+ name: 'project-path',
6169
+ required: false
6170
+ }
6171
+ ],
6172
+ description: 'Read or write chrome.storage in a running extension (requires --allow-control)',
6173
+ supportsSourceInspection: false
6174
+ },
6175
+ {
6176
+ name: 'reload',
6177
+ positionals: [
6178
+ {
6179
+ name: 'project-path',
6180
+ required: false
6181
+ }
6182
+ ],
6183
+ description: 'Reload a running extension or tab (requires --allow-control)',
6184
+ supportsSourceInspection: false
6185
+ },
6186
+ {
6187
+ name: 'open',
6188
+ positionals: [
6189
+ {
6190
+ name: 'surface',
6191
+ required: true,
6192
+ values: [
6193
+ 'popup',
6194
+ 'options',
6195
+ 'sidebar',
6196
+ 'action',
6197
+ 'command'
6198
+ ]
6060
6199
  },
6061
6200
  {
6062
- name: '--output',
6063
- values: [
6064
- 'pretty',
6065
- 'json'
6066
- ],
6067
- default: 'pretty',
6068
- description: 'Result format for --ai-help (--format is a deprecated alias)'
6069
- },
6201
+ name: 'project-path',
6202
+ required: false
6203
+ }
6204
+ ],
6205
+ description: 'Open an extension surface: popup, options, sidebar, action, or command (requires --allow-control)',
6206
+ supportsSourceInspection: false
6207
+ },
6208
+ {
6209
+ name: 'inspect',
6210
+ positionals: [
6070
6211
  {
6071
- name: '--no-telemetry',
6072
- description: 'Disable anonymous telemetry for this run. Persistent toggle: `extension telemetry disable`. Hard override: `EXTENSION_TELEMETRY=0`.'
6073
- },
6212
+ name: 'project-path',
6213
+ required: false
6214
+ }
6215
+ ],
6216
+ description: 'Inspect a page or content DOM through the agent bridge (CDP-free; requires --allow-control)',
6217
+ supportsSourceInspection: true
6218
+ },
6219
+ {
6220
+ name: 'publish',
6221
+ positionals: [
6074
6222
  {
6075
- name: '--browser',
6076
- values: [
6077
- 'chrome',
6078
- 'edge',
6079
- 'firefox',
6080
- 'chromium',
6081
- 'chromium-based',
6082
- 'gecko-based',
6083
- 'firefox-based'
6084
- ],
6085
- default: 'chromium',
6086
- description: 'Target browser/engine'
6087
- },
6223
+ name: 'project-path',
6224
+ required: false
6225
+ }
6226
+ ],
6227
+ description: 'Publish to extension.dev and print a shareable URL (requires EXTENSION_DEV_TOKEN)',
6228
+ supportsSourceInspection: false
6229
+ },
6230
+ {
6231
+ name: 'install',
6232
+ positionals: [
6088
6233
  {
6089
- name: '--port',
6090
- default: '8080',
6091
- description: 'Development server port (use 0 for OS-assigned available port)'
6092
- },
6234
+ name: 'browser-name',
6235
+ required: false
6236
+ }
6237
+ ],
6238
+ description: 'Install a managed browser binary into the Extension.js cache',
6239
+ detail: 'Defaults to chromium when no browser is named.',
6240
+ supportsSourceInspection: false,
6241
+ notes: [
6093
6242
  {
6094
- name: '--host',
6095
- default: '127.0.0.1',
6096
- description: 'Dev server host address (use 0.0.0.0 for Docker/devcontainers)'
6243
+ usage: '--browser <chrome|chromium|edge|firefox|chromium-based|gecko-based|firefox-based|all>',
6244
+ description: 'Install multiple browsers, browser families, or all'
6097
6245
  },
6098
6246
  {
6099
- name: '--public-host',
6100
- default: 'bind host (127.0.0.1 when bound to 0.0.0.0)',
6101
- description: 'Connectable host the browser dials for HMR + the reload bridge when it differs from the bind host (remote/devcontainer)'
6247
+ usage: '--where',
6248
+ description: 'Print the managed browser cache root (or browser install path(s) when a browser name or --browser is provided)'
6249
+ }
6250
+ ]
6251
+ },
6252
+ {
6253
+ name: 'uninstall',
6254
+ positionals: [
6255
+ {
6256
+ name: 'browser-name',
6257
+ required: false
6102
6258
  }
6103
6259
  ],
6104
- capabilities: {
6105
- logger: {
6106
- levels: [
6107
- 'off',
6108
- 'error',
6109
- 'warn',
6110
- 'info',
6111
- 'debug',
6112
- 'trace',
6113
- 'all'
6114
- ],
6115
- formats: [
6116
- 'pretty',
6117
- 'json',
6118
- 'ndjson'
6119
- ],
6120
- notes: [
6121
- 'centralized logger streams logs from multiple extension contexts',
6122
- '--logs defaults to off unless explicitly enabled'
6123
- ]
6124
- },
6125
- managedDependencies: {
6126
- enforcement: 'guarded',
6127
- trigger: 'when managed packages are declared in package.json and imported as a module specifier in extension.config',
6128
- action: 'print an error and abort'
6129
- },
6130
- readyContract: {
6131
- readyPath: 'dist/extension-js/<browser>/ready.json',
6132
- eventsPath: 'dist/extension-js/<browser>/events.ndjson',
6133
- waitFlag: '--wait blocks until ready.json reports ready (or error) then exits; pair with --output json for machine output (--wait-format is a deprecated alias)',
6134
- statuses: [
6135
- 'starting',
6136
- 'ready',
6137
- 'error'
6138
- ],
6139
- readyFields: [
6140
- 'status',
6141
- 'command',
6142
- 'browser',
6143
- 'runId',
6144
- 'startedAt',
6145
- 'distPath',
6146
- 'manifestPath',
6147
- 'port',
6148
- 'pid',
6149
- 'ts',
6150
- 'compiledAt',
6151
- 'errors'
6152
- ],
6153
- eventTypes: [
6154
- 'compile_start',
6155
- 'compile_success',
6156
- 'compile_error',
6157
- 'shutdown'
6158
- ],
6159
- notes: [
6160
- 'ready.json is written atomically by the build (dev/start) on each compile',
6161
- 'events.ndjson is an append-only build timeline with durationMs and errorCount per entry',
6162
- '--wait requires a local project path (remote URLs are not supported)',
6163
- 'consumers should verify pid liveness and recency before trusting a contract'
6164
- ]
6260
+ description: 'Remove managed browser binaries from the Extension.js cache',
6261
+ supportsSourceInspection: false,
6262
+ notes: [
6263
+ {
6264
+ usage: '--all',
6265
+ description: 'Remove every managed browser binary'
6165
6266
  },
6166
- dockerAndContainers: {
6167
- hostFlag: '--host 0.0.0.0 binds the dev server to all interfaces',
6168
- sandboxDetection: [
6169
- '/.dockerenv',
6170
- '/run/.containerenv',
6171
- 'REMOTE_CONTAINERS=true',
6172
- 'CODESPACES=true',
6173
- 'container env var'
6174
- ],
6175
- notes: [
6176
- 'Use --no-browser inside the container and load dist/<browser>/ in the host browser',
6177
- 'File watching uses polling (1s interval) for bind-mount compatibility',
6178
- '--no-sandbox is added automatically when a container environment is detected'
6179
- ]
6267
+ {
6268
+ usage: '--where',
6269
+ description: 'Print the managed browser cache root (or browser install path(s) when --browser/--all is provided)'
6180
6270
  }
6181
- },
6182
- examples: [
6183
- 'extension --ai-help',
6184
- 'extension --ai-help --output json',
6185
- 'extension dev ./my-ext --logs=info --log-format=json',
6186
- 'extension dev ./my-ext --host 0.0.0.0 --no-browser',
6187
- 'extension dev ./my-ext --wait --browser=chromium --output json',
6188
- 'extension start ./my-ext --wait --browser=chromium --output json',
6189
- 'extension dev ./my-ext --gecko-binary flatpak:org.mozilla.firefox',
6190
- 'extension install chromium',
6191
- 'extension install --where',
6192
- 'extension uninstall --where',
6193
- 'extension uninstall --all'
6194
6271
  ]
6195
- };
6196
- }
6197
- function invalidAIHelpFormat(value) {
6198
- return `${getLoggingPrefix('error')} Invalid value for ${messages_code('--output')}: ${external_pintor_default().red(String(value))}.\n${external_pintor_default().red('Pass')} ${messages_arg('pretty')} ${external_pintor_default().red('or')} ${messages_arg('json')}${external_pintor_default().red(', for example')} ${messages_code('extension --ai-help --output json')}${external_pintor_default().red('.')}`;
6199
- }
6200
- function removedNoRunnerFlag() {
6201
- return `${getLoggingPrefix('error')} ${messages_code('--no-runner')} was removed.\nUse ${messages_code('--no-browser')} instead.`;
6202
- }
6203
- function deprecatedOutputAlias(flag) {
6204
- return `${getLoggingPrefix('warn')} ${messages_code(flag)} is deprecated and maps to ${messages_code('--output')}.\nPass ${messages_code('--output')} ${messages_arg('<pretty|json>')} instead.`;
6205
- }
6206
- function noBrowserNotSupportedForCommand(command) {
6207
- return `${getLoggingPrefix('error')} ${messages_code('--no-browser')} is only supported for ${messages_code('dev')}, ${messages_code('start')}, and ${messages_code('preview')}.\nReceived command: ${messages_code(command || '(none)')}`;
6208
- }
6209
- function readRecentConsole(projectPath, browser, target, limit) {
6210
- const file = external_node_path_default().resolve(projectPath, 'dist', 'extension-js', browser, 'logs.ndjson');
6211
- let lines;
6212
- try {
6213
- lines = external_node_fs_default().readFileSync(file, 'utf-8').split('\n').filter(Boolean);
6214
- } catch {
6215
- return [];
6216
- }
6217
- const out = [];
6218
- for (const line of lines){
6219
- let e;
6220
- try {
6221
- e = JSON.parse(line);
6222
- } catch {
6223
- continue;
6224
- }
6225
- if (e && 'header' !== e.type) {
6226
- if (!target.context || e.context === target.context) {
6227
- if (null == target.tabId || e.tabId === target.tabId) out.push({
6228
- seq: e.seq,
6229
- level: e.level,
6230
- context: e.context,
6231
- messageParts: e.messageParts,
6232
- eventType: e.eventType,
6233
- code: e.code,
6234
- tabId: e.tabId
6235
- });
6272
+ },
6273
+ {
6274
+ name: 'telemetry',
6275
+ positionals: [
6276
+ {
6277
+ name: 'action',
6278
+ required: false,
6279
+ values: [
6280
+ 'enable',
6281
+ 'disable',
6282
+ 'status'
6283
+ ]
6236
6284
  }
6237
- }
6285
+ ],
6286
+ description: 'Manage anonymous telemetry consent (enable, disable, or show status)',
6287
+ supportsSourceInspection: false
6288
+ },
6289
+ {
6290
+ name: 'doctor',
6291
+ positionals: [
6292
+ {
6293
+ name: 'project-path',
6294
+ required: false
6295
+ }
6296
+ ],
6297
+ description: 'Diagnose a dev session: ready contract, control channel, token, executor, browser',
6298
+ supportsSourceInspection: false
6238
6299
  }
6239
- return out.slice(Math.max(0, out.length - limit));
6300
+ ];
6301
+ const COMMANDS = COMMAND_TABLE.map((entry)=>({
6302
+ ...entry,
6303
+ args: commandArgSignature(entry.positionals)
6304
+ }));
6305
+ const commandDescriptions = Object.fromEntries(COMMANDS.map((spec)=>[
6306
+ spec.name,
6307
+ spec.description
6308
+ ]));
6309
+ function commandHelpEntry(spec) {
6310
+ const signature = spec.args ? ` ${messages_arg(spec.args)}` : '';
6311
+ const lines = [
6312
+ `- ${messages_code(`extension ${spec.name}`)}${signature}`,
6313
+ ` ${spec.description}`
6314
+ ];
6315
+ if (spec.detail) lines.push(` ${spec.detail}`);
6316
+ for (const note of spec.notes ?? [])lines.push('', `- ${messages_code(`extension ${spec.name}`)} ${messages_arg(note.usage)}`, ` ${note.description}`);
6317
+ return lines.join('\n');
6240
6318
  }
6241
- function codeForBridgeError(name, message) {
6242
- if ('Timeout' === name) return messaging.Lp.E_TIMEOUT;
6243
- if ('Unavailable' === name) return messaging.Lp.E_CONTROL_UNAVAILABLE;
6244
- if ('Forbidden' === name) return /token missing/i.test(message) ? messaging.Lp.E_TOKEN_MISSING : messaging.Lp.E_EVAL_REFUSED;
6245
- if ('BadRequest' === name) return messaging.Lp.E_ARGS;
6246
- if ('Unsupported' === name) return /needs a --tab id|no injectable frame|is not open/i.test(message) ? messaging.Lp.E_TARGET_NOT_FOUND : messaging.Lp.E_NOT_IMPLEMENTED;
6247
- if ('EvalError' === name) return messaging.Lp.E_EVAL;
6248
- if ('InspectError' === name) return messaging.Lp.E_INSPECT;
6249
- if ('StorageError' === name) return messaging.Lp.E_STORAGE;
6250
- return messaging.Lp.E_INTERNAL;
6319
+ function availableCommandsBlock() {
6320
+ return COMMANDS.map(commandHelpEntry).join('\n\n');
6251
6321
  }
6252
- function statusForCode(code) {
6253
- if (code === messaging.Lp.E_TIMEOUT) return 'timeout';
6254
- if (code === messaging.Lp.E_SESSION_NOT_FOUND || code === messaging.Lp.E_TARGET_NOT_FOUND) return 'not-found';
6255
- if (code === messaging.Lp.E_CONTROL_DENIED || code === messaging.Lp.E_EVAL_REFUSED || code === messaging.Lp.E_TOKEN_MISSING) return 'denied';
6256
- if (code === messaging.Lp.E_ARGS) return 'usage';
6257
- return 'failed';
6322
+ function unhandledError(err) {
6323
+ const message = err instanceof Error ? err.stack || err.message : 'string' == typeof err ? err : messaging.Sc.truncate(err);
6324
+ return `${getLoggingPrefix('error')} ${external_pintor_default().red(String(message || 'Unknown error'))}`;
6258
6325
  }
6259
- function buildActEnvelope(command, result) {
6260
- const { ok, value, truncated, error, ...extras } = result;
6261
- const wasTruncated = true === truncated;
6262
- if (ok) return {
6263
- ...extras,
6264
- ...messaging.Pr.ok(command, 'ok', value ?? null, {
6265
- truncated: wasTruncated
6266
- })
6267
- };
6268
- const raw = error || {};
6269
- const name = 'string' == typeof raw.name ? raw.name : 'Error';
6270
- const message = 'string' == typeof raw.message ? raw.message : 'command failed';
6271
- const code = codeForBridgeError(name, message);
6326
+ function updateFailed(err) {
6327
+ return `${getLoggingPrefix('warn')} Couldn't check for updates.\n${external_pintor_default().yellow('The command continues without the update check.')}\n${external_pintor_default().yellow(String(err?.message || err))}`;
6328
+ }
6329
+ function checkUpdates(packageJson, update) {
6330
+ const latest = String(update.latest);
6331
+ const releaseNotesUrl = `https://github.com/extension-js/extension.js/releases/tag/v${latest}`;
6332
+ const suffix = external_pintor_default().gray(`(version ${latest} is available!)`);
6333
+ const message = `${getLoggingPrefix('info')} 🧩 An ${external_pintor_default().blue('Extension.js')} update is available.\n\nYou are on version ${external_pintor_default().red(String(packageJson.version))}.\nThe latest stable is ${external_pintor_default().green(latest)}.\nSee what's new: ${external_pintor_default().underline(releaseNotesUrl)}\nUpdate to the latest stable to get fixes and new features.`;
6272
6334
  return {
6273
- ...extras,
6274
- ...messaging.Pr.fail(command, statusForCode(code), {
6275
- ...raw,
6276
- code,
6277
- message,
6278
- name
6279
- }, {
6280
- truncated: wasTruncated
6281
- })
6335
+ suffix,
6336
+ message
6282
6337
  };
6283
6338
  }
6284
- function writeFrame(frame) {
6285
- try {
6286
- external_node_fs_default().writeSync(1, `${JSON.stringify(frame)}\n`);
6287
- } catch {
6288
- console.log(JSON.stringify(frame));
6289
- }
6339
+ function programUserHelp() {
6340
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
6341
+
6342
+ Usage: extension [command] [options]
6343
+
6344
+ Notes
6345
+ - All high-level commands offer their own \`--help\` with usage and flag lists.
6346
+ - Telemetry is anonymous and privacy-safe by default; see ${messages_code('docs/TELEMETRY.md')} for the full contract.
6347
+
6348
+ Example
6349
+ - ${messages_code('extension create --help')} outputs information about the "create" command.
6350
+
6351
+ Available Commands
6352
+ ${availableCommandsBlock()}
6353
+
6354
+ Common Options
6355
+ - ${messages_code('--browser')} ${messages_arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chromium)
6356
+ - ${messages_code('--profile')} ${messages_arg('<path|boolean>')} Browser profile configuration
6357
+ - ${messages_code('--polyfill')} ${messages_arg('[boolean]')} Enable/disable cross-browser polyfill
6358
+ - ${messages_code('--no-telemetry')} Disable anonymous telemetry for this run (persistent toggle: ${messages_code('extension telemetry disable')}, or ${messages_code('EXTENSION_TELEMETRY=0')})
6359
+ - ${messages_code('--ai-help')} Show AI-assistant oriented help and tips
6360
+ - ${messages_code('--output')} ${messages_arg('<pretty|json>')} Result format for ${messages_code('--ai-help')} and machine-readable command results (default: pretty)
6361
+ ${messages_code('--format')} and ${messages_code('--wait-format')} still work as deprecated aliases of ${messages_code('--output')}
6362
+ - ${messages_code('--help')} Show help output
6363
+ - ${messages_code('--port')} ${messages_arg('<number>')} Development server port (default: 8080; use 0 for OS-assigned)
6364
+ - ${messages_code('--host')} ${messages_arg('<address>')} Dev server host (default: 127.0.0.1; use 0.0.0.0 for Docker/devcontainers)
6365
+ - ${messages_code('--public-host')} ${messages_arg('<address>')} Connectable host the browser dials for HMR + reload bridge when it differs from ${messages_code('--host')} (remote/devcontainer; default: the bind host, or 127.0.0.1 when bound to 0.0.0.0)
6366
+ - ${messages_code('--starting-url')} ${messages_arg('<url>')} Initial URL to load in browser
6367
+ - ${messages_code('--silent')} ${messages_arg('[boolean]')} Suppress console output during build
6368
+
6369
+ Browser-Specific Options
6370
+ - ${messages_code('--chromium-binary')} ${messages_arg('<path>')} Custom Chromium binary path
6371
+ - ${messages_code('--gecko-binary')}/${messages_code('--firefox-binary')} ${messages_arg('<path>')} Custom Firefox/Gecko binary path
6372
+ Use ${messages_code('flatpak:org.mozilla.firefox')} as the path to launch a Flatpak-installed Firefox
6373
+
6374
+ Build Options
6375
+ - ${messages_code('--zip')} ${messages_arg('[boolean]')} Create ZIP archive of built extension
6376
+ - ${messages_code('--zip-source')} ${messages_arg('[boolean]')} Include source files in ZIP
6377
+ - ${messages_code('--zip-filename')} ${messages_arg('<name>')} Custom ZIP filename
6378
+
6379
+ ${external_pintor_default().underline('Centralized Logger (terminal output)')}
6380
+ - The manager extension embeds a centralized logger that streams events to the CLI.
6381
+ - Enable and filter logs directly via ${messages_code('extension dev')} flags:
6382
+ - ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level (default: off)
6383
+ - ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
6384
+ - ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Output format (default: pretty)
6385
+ - ${messages_code('--no-log-timestamps')} Hide ISO timestamps in pretty output
6386
+ - ${messages_code('--no-log-color')} Disable color in pretty output
6387
+ - ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by event.url
6388
+ - ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
6389
+ - Example: ${messages_code('extension dev ./my-ext --logs=debug --log-context=all --log-format=pretty')}
6390
+
6391
+ ${messages_code('extension --help')}
6392
+ This command outputs a help file with key command options.
6393
+
6394
+ ${external_pintor_default().underline('Path Resolution (important)')}
6395
+ - Leading ${messages_code('/')} in manifest/HTML means extension root (the directory containing ${messages_code('manifest.json')}).
6396
+ - Relative paths resolve from the ${messages_code('manifest.json')} directory.
6397
+ - Absolute OS paths are used as-is.
6398
+
6399
+ AI Assistants
6400
+ - For AI-oriented guidance and deeper tips, run ${messages_code('extension --ai-help')}
6401
+ - For machine-readable AI guidance, run ${messages_code('extension --ai-help --output json')}
6402
+
6403
+ Report issues
6404
+ - ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
6290
6405
  }
6291
- function fail(message, frame) {
6292
- console.error(message);
6293
- if (frame && 'json' === frame.output) writeFrame(messaging.Pr.fail(frame.command, statusForCode(frame.code), {
6294
- code: frame.code,
6295
- message,
6296
- name: 'CliError'
6297
- }));
6298
- process.exit(1);
6406
+ function unsupportedBrowserFlag(value, supported) {
6407
+ return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}.\n${external_pintor_default().red('Choose one of:')} ${supported.join(', ')}${external_pintor_default().red('.')}`;
6299
6408
  }
6300
- function printResult(result, output, command) {
6301
- if ('json' === output) return void console.log(JSON.stringify(buildActEnvelope(command, result)));
6302
- if (result.ok) {
6303
- const value = result.value;
6304
- console.log('string' == typeof value ? value : JSON.stringify(value, null, 2));
6305
- if (result.truncated) console.error('… result truncated (byte cap)');
6306
- return;
6307
- }
6308
- const err = result.error || {
6309
- name: 'Error',
6310
- message: 'command failed'
6311
- };
6312
- console.error(`${err.name}: ${err.message}${err.engine ? ` (engine: ${err.engine})` : ''}`);
6409
+ function safariOnlyOption(flags) {
6410
+ return `${getLoggingPrefix('error')} ${flags.map(messages_code).join(', ')} only appl${1 === flags.length ? 'ies' : 'y'} to Safari targets.\nAdd ${messages_code('--browser safari')} (or ${messages_code('webkit-based')}).`;
6313
6411
  }
6314
- async function runCommand(input) {
6315
- const projectPath = external_node_path_default().resolve(input.projectPathArg || process.cwd());
6316
- const browser = input.opts.browser || 'chromium';
6317
- const { BridgeController, readReadyContract, readControlToken } = await loadExtensionDevelopBridgeModule();
6318
- const outputFrame = {
6319
- command: input.command,
6320
- output: input.opts.output
6321
- };
6322
- const ready = readReadyContract(projectPath, browser);
6323
- if (!ready) fail(`No active control channel found for ${browser}. Run \`extension dev --browser=${browser} --allow-control\` first.`, {
6324
- ...outputFrame,
6325
- code: messaging.Lp.E_SESSION_NOT_FOUND
6326
- });
6327
- const token = input.needsToken ? readControlToken(projectPath, browser) : void 0;
6328
- const controller = new BridgeController({
6329
- controlPort: ready.controlPort,
6330
- instanceId: ready.instanceId,
6331
- token: token ?? void 0
6332
- });
6333
- try {
6334
- await controller.connect();
6335
- } catch (err) {
6336
- controller.close();
6337
- const message = err?.message || 'could not connect to the control channel';
6338
- fail(message, {
6339
- ...outputFrame,
6340
- code: /code 40\d\d/.test(message) ? messaging.Lp.E_CONTROL_DENIED : messaging.Lp.E_CONTROL_UNAVAILABLE
6341
- });
6342
- }
6343
- const timeoutMs = input.opts.timeout ? Number(input.opts.timeout) : 5000;
6344
- let result;
6345
- try {
6346
- result = await controller.command({
6347
- op: input.op,
6348
- target: input.target,
6349
- args: input.args,
6350
- timeoutMs
6351
- });
6352
- } catch (err) {
6353
- controller.close();
6354
- const message = err?.message || 'command failed';
6355
- fail(message, {
6356
- ...outputFrame,
6357
- code: /timed out/i.test(message) ? messaging.Lp.E_TIMEOUT : messaging.Lp.E_CONTROL_UNAVAILABLE
6358
- });
6359
- } finally{
6360
- controller.close();
6361
- }
6362
- if (result?.ok && input.augment) try {
6363
- Object.assign(result, input.augment(projectPath, browser, result));
6364
- } catch {}
6365
- printResult(result, input.opts.output, input.command);
6366
- await exitAfterDrain(result.ok ? 0 : 1);
6412
+ function safariInvalidBundleId(bundleId) {
6413
+ return `${getLoggingPrefix('error')} Invalid bundle identifier: ${messages_code(bundleId)}.\nUse reverse-DNS form: dot-separated segments of letters, digits and hyphens, each starting with a letter (e.g. ${messages_code('com.example.my-extension')}).`;
6367
6414
  }
6368
- function targetFrom(opts, fallback = 'background') {
6369
- const context = opts.context || fallback;
6370
- const target = {
6371
- context
6372
- };
6373
- if (opts.url) target.url = opts.url;
6374
- if (null != opts.tab && '' !== opts.tab) target.tabId = Number(opts.tab);
6375
- return target;
6415
+ function safariCommandNotSupported(command) {
6416
+ return `${getLoggingPrefix('error')} ${messages_code(command)} can't load an extension into Safari automatically.\nSafari extensions ship inside a signed app and are enabled by hand, so there's no live browser session to load into, unlike Chromium and Firefox.\nBuild the Safari app instead: ${messages_code('extension build --browser safari')}\nThen open the generated app and enable it in Safari → Settings → Extensions.`;
6417
+ }
6418
+ function programAIHelp() {
6419
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
6420
+
6421
+ Browser-Specific Configuration
6422
+ - Use browser prefixes in manifest.json for browser-specific fields:
6423
+ ${messages_code('{"firefox:manifest": 2, "chrome:manifest": 3}')}
6424
+ This applies manifest v2 to Firefox only, v3 to Chrome/Edge.
6425
+
6426
+ Centralized Logger (for AI & CI)
6427
+ - Logs from all contexts are centralized by the manager extension and streamed to the CLI.
6428
+ - Prefer these flags to control terminal logs during ${messages_code('extension dev')}:
6429
+ - ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level
6430
+ - ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts to include
6431
+ - ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Pretty for humans; JSON for machines/NDJSON pipelines
6432
+ - ${messages_code('--no-log-timestamps')} ${messages_arg(' ')} Disable timestamps (pretty)
6433
+ - ${messages_code('--no-log-color')} ${messages_arg(' ')} Disable ANSI colors (pretty)
6434
+ - ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by URL
6435
+ - ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
6436
+ - Good CI pattern: ${messages_code('EXTENSION_DEBUG=1 EXTENSION_AUTO_EXIT_MS=6000 extension dev ./ext --logs=info --log-format=json')}
6437
+
6438
+ Special Folders for Entrypoints
6439
+ - Use special folders to handle entrypoints and assets not declared in manifest.json:
6440
+ - ${external_pintor_default().underline(messages_code('public/'))} - Static assets automatically copied to build (resolves to output root)
6441
+ - ${external_pintor_default().underline(messages_code('pages/'))} - HTML files not declared in manifest (e.g., welcome pages)
6442
+ - ${external_pintor_default().underline(messages_code("scripts/"))} - JavaScript files not declared in manifest (e.g., executable scripts)
6443
+
6444
+ Predictable Output Paths
6445
+ - Core HTML destinations are standardized across browsers so you can reference them safely in code/tests:
6446
+ - ${messages_code('devtools_page')} → ${messages_code('devtools/index.html')}
6447
+ - ${messages_code('sidebar_action.default_panel')} (MV2) and ${messages_code('side_panel.default_path')} (MV3) → ${messages_code('sidebar/index.html')}
6448
+ - ${messages_code('options_ui.page')} and ${messages_code('options_page')} → ${messages_code('options/index.html')}
6449
+ - ${messages_code('background.page')} → ${messages_code('background/index.html')}
6450
+ - ${messages_code('action.default_popup')}, ${messages_code('browser_action.default_popup')}, ${messages_code('page_action.default_popup')} → ${messages_code('action/index.html')}
6451
+ - Other predictable outputs:
6452
+ - ${messages_code('chrome_url_overrides.*')} → ${messages_code('chrome_url_overrides/<key>.html')}
6453
+ - ${messages_code("content_scripts[n].js/css")} → ${messages_code("content_scripts/content-<n>.{js,css}")}
6454
+ - ${messages_code('sandbox.pages[]')} → ${messages_code('sandbox/page-<n>.html')}
6455
+ - ${messages_code("user_scripts.api_script")} → ${messages_code("user_scripts/api_script.js")}
6456
+ - ${messages_code('icons/*')} → ${messages_code('icons/')} (feature-specific icon folders preserved where applicable)
6457
+
6458
+ Public & Special Folders (Output Behavior)
6459
+ - ${external_pintor_default().underline(messages_code('public/'))} is the web root in output. Authors can use ${messages_code('/foo')}, ${messages_code('/public/foo')}, ${messages_code('public/foo')}, or ${messages_code('./public/foo')} and they all emit as ${messages_code('dist/<browser>/foo')}.
6460
+ - ${external_pintor_default().underline(messages_code('pages/'))} files emit as ${messages_code('pages/<name>.html')}. Relative assets referenced inside page HTML are emitted under ${messages_code('assets/')} preserving relative structure; public-root URLs are preserved.
6461
+ - ${external_pintor_default().underline(messages_code("scripts/"))} files emit as ${messages_code("scripts/<name>.js")} with extracted CSS when applicable.
6462
+
6463
+ Shadow DOM for Content Scripts
6464
+ - Add ${messages_code('use shadow-dom')} directive to content scripts for style isolation
6465
+ - Automatically creates ${messages_code('#extension-root')} element with shadow DOM
6466
+ - All CSS imports are automatically injected into shadow DOM
6467
+ - Prevents style conflicts with host page
6468
+
6469
+ Environment Variables
6470
+ - Use ${messages_code(messages_arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
6471
+ - Supported in both ${messages_code('process.env')} and ${messages_code('import.meta.env')}
6472
+ - Environment file priority: ${external_pintor_default().underline(messages_code(messages_arg('.env.{browser}.{mode}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env.{browser}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env.{mode}')))} > ${external_pintor_default().underline(messages_code(messages_arg('.env')))}
6473
+ - Example: ${messages_code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
6474
+
6475
+ Available Templates
6476
+ - ${external_pintor_default().green('Frameworks')}: ${messages_code(messages_arg('react'))}, ${messages_code(messages_arg('preact'))}, ${messages_code(messages_arg('vue'))}, ${messages_code(messages_arg('svelte'))}
6477
+ - ${external_pintor_default().green('Languages')}: ${messages_code(messages_arg("javascript"))}, ${messages_code(messages_arg("typescript"))}
6478
+ - ${external_pintor_default().green('Contexts')}: ${messages_code(messages_arg('content'))} (content scripts), ${messages_code(messages_arg('new'))} (new tab), ${messages_code(messages_arg('action'))} (popup)
6479
+ - ${external_pintor_default().green('Styling')}: ${messages_code(messages_arg('tailwind'))}, ${messages_code(messages_arg('sass'))}, ${messages_code(messages_arg('less'))}
6480
+ - ${external_pintor_default().green('Configs')}: ${messages_code(messages_arg('eslint'))}, ${messages_code(messages_arg('prettier'))}, ${messages_code(messages_arg('stylelint'))}
6481
+
6482
+ Webpack/Rspack Configuration
6483
+ - Create ${external_pintor_default().underline(messages_code(messages_arg('extension.config.js')))} for custom webpack configuration
6484
+ - Function receives base config, return modified config
6485
+ - Supports all webpack/rspack loaders and plugins
6486
+ - Example:
6487
+ ${messages_code('export default {')}
6488
+ ${messages_code(' config: (config) => {')}
6489
+ ${messages_code(" config.module.rules.push({ test: /\\.svg$/, use: ['@svgr/webpack'] })")}
6490
+ ${messages_code(' return config')}
6491
+ ${messages_code(' }')}
6492
+ ${messages_code('}')}
6493
+
6494
+ Managed Dependencies (Important)
6495
+ - ${external_pintor_default().green('Do not add')} packages that ${external_pintor_default().blue('Extension.js')} already ships in its own toolchain.
6496
+ - The guard only triggers when a managed package is declared in your ${messages_code('package.json')} ${external_pintor_default().gray('and')} is imported (as a module specifier) in your ${external_pintor_default().underline(messages_code('extension.config.js'))}.
6497
+ - In that case, the program will ${external_pintor_default().red('print an error and abort')} to avoid version conflicts.
6498
+ - Remove the duplicate from your project ${messages_code('package.json')} or avoid referencing it in ${external_pintor_default().underline(messages_code('extension.config.js'))} and rely on the built-in version instead.
6499
+ - If you truly need a different version, open an issue so we can evaluate a safe upgrade.
6500
+
6501
+ Framework-Specific Configuration
6502
+ - Create ${external_pintor_default().underline(messages_code(messages_arg('vue.loader.js')))} for Vue-specific loader configuration
6503
+ - Create ${external_pintor_default().underline(messages_code(messages_arg('svelte.loader.js')))} for Svelte-specific loader configuration
6504
+ - Automatically detected and used by Extension.js
6505
+ - Example svelte.loader.js:
6506
+ ${messages_code('module.exports = {')}
6507
+ ${messages_code(' preprocess: require("svelte-preprocess")({')}
6508
+ ${messages_code(" typescript: true")}
6509
+ ${messages_code(' })')}
6510
+ ${messages_code('}')}
6511
+
6512
+ Hot Module Replacement (HMR)
6513
+ - Automatically enabled in development mode
6514
+ - CSS changes trigger automatic style updates
6515
+ - React components fast-refresh; Svelte components hot-update
6516
+ - Preact and Vue rebuild with a live reload (component state resets)
6517
+ - Content scripts automatically re-inject on changes
6518
+ - Service workers, _locales and manifest changes reload the extension
6519
+
6520
+ Non-Destructive Testing in CI
6521
+ - Prefer ${messages_code('EXTENSION_DEBUG=1')} to copy local templates and avoid network.
6522
+ - Reuse Playwright's Chromium via ${messages_code('--chromium-binary')} path when available.
6523
+ - Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} and ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
6524
+
6525
+ File Watching & HMR Examples
6526
+ - Content script JS/TS changes trigger reinjection; CSS changes update styles live.
6527
+ - For watch-source HTML prints, update a visible string in ${messages_code("content/scripts.*")} and assert it appears in stdout.
6528
+
6529
+ Troubleshooting
6530
+ - Use ${messages_code('--silent true')} during builds to reduce noise; logs still surface errors.
6531
+ - When ports conflict, pass ${messages_code('--port 0')} to auto-select an available port.
6532
+ - In Docker/devcontainers, pass ${messages_code('--host 0.0.0.0')} so the dev server is reachable from the host.
6533
+
6534
+ Non-Interactive / Auto Mode (CI)
6535
+ - Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
6536
+ Useful when ${messages_code('pnpm extension dev')} would otherwise hang under Rspack watch.
6537
+ Example: ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --starting-url ${messages_arg('https://example.com')}
6538
+ - Optional: ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
6539
+
6540
+ Docker / Devcontainers / Codespaces
6541
+ - Use ${messages_code('--host 0.0.0.0')} to bind the dev server on all interfaces so HMR is reachable from the host.
6542
+ - Use ${messages_code('--no-browser')} inside the container and load the extension manually from ${messages_code('dist/<browser>/')} in your host browser.
6543
+ - Chromium sandbox flags (${messages_code('--no-sandbox')}) are added automatically when Docker, Podman, devcontainers, or Codespaces are detected.
6544
+ - File watching uses polling by default (1 s interval), which works across bind-mounted volumes.
6545
+ - Example: ${messages_code('extension dev ./my-ext --host 0.0.0.0 --no-browser --port 8080')}
6546
+
6547
+ Flatpak Firefox
6548
+ - Use ${messages_code('--gecko-binary flatpak:org.mozilla.firefox')} (or ${messages_code('--firefox-binary')}) to launch a Flatpak-installed Firefox.
6549
+ - Extension.js rewrites the binary path to ${messages_code('flatpak run')} with the correct filesystem grants automatically.
6550
+
6551
+ Cross-Browser Compatibility
6552
+ - Use ${messages_code('--polyfill')} flag to enable webextension-polyfill
6553
+ - Automatically handles browser API differences
6554
+ - Supports Chrome, Edge, Firefox with single codebase`;
6376
6555
  }
6377
- const commonOptions = (cmd)=>cmd.option('--browser <chrome | chromium | edge | firefox>', 'which session to target (default chromium)').option('--timeout <ms>', 'command timeout in milliseconds (default 5000)').option('--output <pretty|json>', 'output format (default pretty)');
6378
- function registerActCommands(program) {
6379
- commonOptions(program.command('eval').arguments('<expression> [project-path]').description(commandDescriptions.eval).option('--context <background|popup|options|sidebar|devtools|newtab|history|bookmarks|content|page>', 'target context (default background). Extension pages (popup/options/sidebar/devtools/newtab/history/bookmarks) answer via their own in-page relay and must be open').option('--url <glob|substring>', 'for content/page: document to target (resolved to its tab)').option('--tab <id>', 'for content/page: a specific tab (default: the --url match, else the active tab)')).action(async (expression, projectPathArg, opts)=>{
6380
- await runCommand({
6381
- projectPathArg,
6382
- command: 'eval',
6383
- op: 'eval',
6384
- target: targetFrom(opts),
6385
- args: {
6386
- expression
6556
+ function programAIHelpJSON(version) {
6557
+ return {
6558
+ version,
6559
+ commands: COMMANDS.map((spec)=>({
6560
+ name: spec.name,
6561
+ summary: spec.description,
6562
+ supportsSourceInspection: spec.supportsSourceInspection
6563
+ })),
6564
+ globalOptions: [
6565
+ {
6566
+ name: '--ai-help',
6567
+ description: 'Show AI-assistant oriented help and tips'
6387
6568
  },
6388
- needsToken: true,
6389
- opts
6390
- });
6391
- });
6392
- commonOptions(program.command('storage').arguments('<action> [project-path]').description(commandDescriptions.storage).option('--area <local|sync|session|managed>', 'storage area (default local)').option('--key <key>', 'key to get or set').option('--value <json>', 'JSON value to set (with set)').option('--context <background|popup|options|sidebar|content>', 'target context (default background)')).action(async (action, projectPathArg, opts)=>{
6393
- const area = opts.area || 'local';
6394
- if ('get' === action) return void await runCommand({
6395
- projectPathArg,
6396
- command: 'storage',
6397
- op: 'storage.get',
6398
- target: targetFrom(opts),
6399
- args: opts.key ? {
6400
- area,
6401
- key: opts.key
6402
- } : {
6403
- area
6569
+ {
6570
+ name: '--output',
6571
+ values: [
6572
+ 'pretty',
6573
+ 'json'
6574
+ ],
6575
+ default: 'pretty',
6576
+ description: 'Result format for --ai-help (--format is a deprecated alias)'
6404
6577
  },
6405
- opts
6406
- });
6407
- if ('set' === action) {
6408
- if (!opts.key || null == opts.value) fail('storage set requires --key and --value', {
6409
- command: 'storage',
6410
- code: messaging.Lp.E_ARGS,
6411
- output: opts.output
6412
- });
6413
- let parsed;
6414
- try {
6415
- parsed = JSON.parse(opts.value);
6416
- } catch {
6417
- parsed = opts.value;
6578
+ {
6579
+ name: '--no-telemetry',
6580
+ description: 'Disable anonymous telemetry for this run. Persistent toggle: `extension telemetry disable`. Hard override: `EXTENSION_TELEMETRY=0`.'
6581
+ },
6582
+ {
6583
+ name: '--browser',
6584
+ values: [
6585
+ 'chrome',
6586
+ 'edge',
6587
+ 'firefox',
6588
+ 'chromium',
6589
+ 'chromium-based',
6590
+ 'gecko-based',
6591
+ 'firefox-based'
6592
+ ],
6593
+ default: 'chromium',
6594
+ description: 'Target browser/engine'
6595
+ },
6596
+ {
6597
+ name: '--port',
6598
+ default: '8080',
6599
+ description: 'Development server port (use 0 for OS-assigned available port)'
6600
+ },
6601
+ {
6602
+ name: '--host',
6603
+ default: '127.0.0.1',
6604
+ description: 'Dev server host address (use 0.0.0.0 for Docker/devcontainers)'
6605
+ },
6606
+ {
6607
+ name: '--public-host',
6608
+ default: 'bind host (127.0.0.1 when bound to 0.0.0.0)',
6609
+ description: 'Connectable host the browser dials for HMR + the reload bridge when it differs from the bind host (remote/devcontainer)'
6418
6610
  }
6419
- await runCommand({
6420
- projectPathArg,
6421
- command: 'storage',
6422
- op: 'storage.set',
6423
- target: targetFrom(opts),
6424
- args: {
6425
- area,
6426
- items: {
6427
- [opts.key]: parsed
6428
- }
6429
- },
6430
- opts
6431
- });
6432
- return;
6433
- }
6434
- fail(`unknown storage action: ${action} (use get or set)`, {
6435
- command: 'storage',
6436
- code: messaging.Lp.E_ARGS,
6437
- output: opts.output
6438
- });
6439
- });
6440
- commonOptions(program.command('reload').arguments('[project-path]').description(commandDescriptions.reload).option('--context <background|content|page>', 'target context (default background)').option('--tab <id>', 'for content/page: a specific tab')).action(async (projectPathArg, opts)=>{
6441
- await runCommand({
6442
- projectPathArg,
6443
- command: 'reload',
6444
- op: 'reload',
6445
- target: targetFrom(opts),
6446
- opts
6447
- });
6448
- });
6449
- commonOptions(program.command('inspect').arguments('[project-path]').description(commandDescriptions.inspect).option('--context <content|page|popup|options|sidebar|devtools|newtab|history|bookmarks>', 'what to inspect: content/page or an open surface, including url-override pages (default content)').option('--url <glob|substring>', 'for content/page: document to target (resolved to its tab)').option('--tab <id>', 'for content/page: a specific tab (default: the --url match, else the active tab)').option('--list-tabs', 'list open tabs as {tabId,url,title} and exit').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 (projectPathArg, opts)=>{
6450
- if (opts.listTabs) return void await runCommand({
6451
- projectPathArg,
6452
- command: 'inspect',
6453
- op: 'tabs.query',
6454
- target: {
6455
- context: 'background'
6611
+ ],
6612
+ capabilities: {
6613
+ logger: {
6614
+ levels: [
6615
+ 'off',
6616
+ 'error',
6617
+ 'warn',
6618
+ 'info',
6619
+ 'debug',
6620
+ 'trace',
6621
+ 'all'
6622
+ ],
6623
+ formats: [
6624
+ 'pretty',
6625
+ 'json',
6626
+ 'ndjson'
6627
+ ],
6628
+ notes: [
6629
+ 'centralized logger streams logs from multiple extension contexts',
6630
+ '--logs defaults to off unless explicitly enabled'
6631
+ ]
6456
6632
  },
6457
- args: {},
6458
- opts
6459
- });
6460
- const include = opts.include ? opts.include.split(',').map((s)=>s.trim()).filter(Boolean) : [
6461
- 'summary'
6462
- ];
6463
- const target = targetFrom(opts, 'content');
6464
- await runCommand({
6465
- projectPathArg,
6466
- command: 'inspect',
6467
- op: 'inspect',
6468
- target,
6469
- args: {
6470
- include,
6471
- maxBytes: opts.maxBytes ? Number(opts.maxBytes) : void 0
6633
+ managedDependencies: {
6634
+ enforcement: 'guarded',
6635
+ trigger: 'when managed packages are declared in package.json and imported as a module specifier in extension.config',
6636
+ action: 'print an error and abort'
6472
6637
  },
6473
- opts,
6474
- augment: opts.withConsole ? (projectPath, browser)=>{
6475
- const n = 'string' == typeof opts.withConsole && '' !== opts.withConsole ? Number(opts.withConsole) : 20;
6476
- return {
6477
- console: readRecentConsole(projectPath, browser, target, Number.isFinite(n) && n > 0 ? n : 20)
6478
- };
6479
- } : void 0
6480
- });
6481
- });
6482
- commonOptions(program.command('open').arguments('<surface> [project-path]').description('Open an extension surface: popup, options, sidebar, action, or command (requires --allow-control)').option('--name <command>', 'with `open command`: the chrome.commands name to trigger')).action(async (surface, projectPathArg, opts)=>{
6483
- const allowed = [
6484
- 'popup',
6485
- 'options',
6486
- 'sidebar',
6487
- 'action',
6488
- 'command'
6489
- ];
6490
- if (!allowed.includes(surface)) fail(`unknown surface: ${surface} (use popup, options, sidebar, action, or command)`, {
6491
- command: 'open',
6492
- code: messaging.Lp.E_ARGS,
6493
- output: opts.output
6494
- });
6495
- const inBackground = 'action' === surface || 'command' === surface;
6496
- const context = inBackground ? 'background' : surface;
6497
- const args = {
6498
- surface
6499
- };
6500
- if ('command' === surface && opts.name) args.name = opts.name;
6501
- await runCommand({
6502
- projectPathArg,
6503
- command: 'open',
6504
- op: 'open',
6505
- target: {
6506
- context
6638
+ readyContract: {
6639
+ readyPath: 'dist/extension-js/<browser>/ready.json',
6640
+ eventsPath: 'dist/extension-js/<browser>/events.ndjson',
6641
+ waitFlag: '--wait blocks until ready.json reports ready (or error) then exits; pair with --output json for machine output (--wait-format is a deprecated alias)',
6642
+ statuses: [
6643
+ 'starting',
6644
+ 'ready',
6645
+ 'error'
6646
+ ],
6647
+ readyFields: [
6648
+ 'status',
6649
+ 'command',
6650
+ 'browser',
6651
+ 'runId',
6652
+ 'startedAt',
6653
+ 'distPath',
6654
+ 'manifestPath',
6655
+ 'port',
6656
+ 'pid',
6657
+ 'ts',
6658
+ 'compiledAt',
6659
+ 'errors'
6660
+ ],
6661
+ eventTypes: [
6662
+ 'compile_start',
6663
+ 'compile_success',
6664
+ 'compile_error',
6665
+ 'shutdown'
6666
+ ],
6667
+ notes: [
6668
+ 'ready.json is written atomically by the build (dev/start) on each compile',
6669
+ 'events.ndjson is an append-only build timeline with durationMs and errorCount per entry',
6670
+ '--wait requires a local project path (remote URLs are not supported)',
6671
+ 'consumers should verify pid liveness and recency before trusting a contract'
6672
+ ]
6507
6673
  },
6508
- args,
6509
- opts
6510
- });
6511
- });
6512
- }
6513
- var external_node_child_process_ = __webpack_require__("node:child_process");
6514
- var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
6515
- function logSafariDryRun(converterCmd, xcodebuildCmd) {
6516
- if ((0, messaging._o)()) console.log(messages.KP());
6517
- console.log(messages.yc());
6518
- console.log(messages.fL(converterCmd));
6519
- console.log(messages.Aj(xcodebuildCmd));
6520
- }
6521
- function sanitizeAppName(value) {
6522
- const cleaned = String(value || '').replace(/[\\/:*?"<>|]+/g, ' ').replace(/\s+/g, ' ').trim();
6523
- return cleaned.length > 0 ? cleaned : 'Extension';
6524
- }
6525
- function bundleSegment(appName) {
6526
- return String(appName || '').replace(/[^A-Za-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'extension';
6527
- }
6528
- function deriveBundleId(appName) {
6529
- return `dev.extensionjs.${bundleSegment(appName)}`;
6530
- }
6531
- function isValidBundleId(value) {
6532
- return /^[A-Za-z][A-Za-z0-9-]*(\.[A-Za-z][A-Za-z0-9-]*)+$/.test(value);
6533
- }
6534
- function readManifest(extensionDir) {
6535
- try {
6536
- const manifestPath = external_node_path_.join(extensionDir, 'manifest.json');
6537
- if (external_node_fs_.existsSync(manifestPath)) return JSON.parse(external_node_fs_.readFileSync(manifestPath, 'utf8')) || {};
6538
- } catch {}
6539
- return {};
6540
- }
6541
- function resolveSafariBuildConfig(compilation, host) {
6542
- const extensionDir = String(compilation?.options?.output?.path || '');
6543
- const manifest = readManifest(extensionDir);
6544
- const appName = sanitizeAppName(String(host.appName || manifest?.name || 'Extension'));
6545
- const userBundleId = String(host.bundleId || '').trim();
6546
- const bundleIdDerived = !userBundleId;
6547
- const bundleIdentifier = bundleIdDerived ? deriveBundleId(appName) : userBundleId;
6548
- const projectLocation = `${extensionDir.replace(/[\\/]+$/, '')}-xcode`;
6549
- return {
6550
- extensionDir,
6551
- projectLocation,
6552
- appName,
6553
- bundleIdentifier,
6554
- bundleIdDerived,
6555
- macOsOnly: false !== host.macOsOnly,
6556
- language: 'swift',
6557
- open: !host.noOpen,
6558
- safariBinary: host.safariBinary
6559
- };
6560
- }
6561
- function composeConverterArgs(config) {
6562
- const args = [
6563
- 'safari-web-extension-converter',
6564
- config.extensionDir,
6565
- '--project-location',
6566
- config.projectLocation,
6567
- '--app-name',
6568
- config.appName,
6569
- '--bundle-identifier',
6570
- config.bundleIdentifier,
6571
- '--no-prompt',
6572
- '--no-open',
6573
- '--force',
6574
- 'objc' === config.language ? '--objc' : '--swift'
6575
- ];
6576
- if (config.macOsOnly) args.push('--macos-only');
6577
- return args;
6578
- }
6579
- function macOsSchemeName(config) {
6580
- return config.macOsOnly ? config.appName : `${config.appName} (macOS)`;
6581
- }
6582
- function xcodeProjectPath(config) {
6583
- return external_node_path_.join(config.projectLocation, config.appName, `${config.appName}.xcodeproj`);
6584
- }
6585
- function derivedDataPath(config) {
6586
- return external_node_path_.join(config.projectLocation, '.derived');
6587
- }
6588
- function builtAppPath(config) {
6589
- return external_node_path_.join(derivedDataPath(config), 'Build', 'Products', 'Release', `${config.appName}.app`);
6590
- }
6591
- function composeXcodebuildArgs(config) {
6592
- return [
6593
- '-project',
6594
- xcodeProjectPath(config),
6595
- '-scheme',
6596
- macOsSchemeName(config),
6597
- '-configuration',
6598
- 'Release',
6599
- '-derivedDataPath',
6600
- derivedDataPath(config),
6601
- 'CODE_SIGN_IDENTITY=-',
6602
- 'CODE_SIGNING_REQUIRED=NO',
6603
- 'CODE_SIGNING_ALLOWED=YES',
6604
- 'build'
6605
- ];
6606
- }
6607
- function manifestFingerprintPath(config) {
6608
- return external_node_path_.join(config.projectLocation, '.manifest-fingerprint');
6609
- }
6610
- function stableStringify(obj) {
6611
- if (null == obj) return JSON.stringify(obj);
6612
- if ('object' != typeof obj) return JSON.stringify(obj);
6613
- if (Array.isArray(obj)) return `[${obj.map(stableStringify).join(',')}]`;
6614
- const keys = Object.keys(obj).sort();
6615
- return `{${keys.map((k)=>`${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
6616
- }
6617
- function readManifestRaw(extensionDir) {
6618
- try {
6619
- return external_node_fs_.readFileSync(external_node_path_.join(extensionDir, 'manifest.json'), 'utf8');
6620
- } catch {
6621
- return '';
6622
- }
6623
- }
6624
- function normalizeManifest(raw) {
6625
- try {
6626
- return stableStringify(JSON.parse(raw));
6627
- } catch {
6628
- return raw;
6629
- }
6630
- }
6631
- function composeProjectFingerprint(config) {
6632
- return JSON.stringify({
6633
- v: 2,
6634
- identity: {
6635
- appName: config.appName,
6636
- bundleId: config.bundleIdentifier,
6637
- macOsOnly: config.macOsOnly
6674
+ dockerAndContainers: {
6675
+ hostFlag: '--host 0.0.0.0 binds the dev server to all interfaces',
6676
+ sandboxDetection: [
6677
+ '/.dockerenv',
6678
+ '/run/.containerenv',
6679
+ 'REMOTE_CONTAINERS=true',
6680
+ 'CODESPACES=true',
6681
+ 'container env var'
6682
+ ],
6683
+ notes: [
6684
+ 'Use --no-browser inside the container and load dist/<browser>/ in the host browser',
6685
+ 'File watching uses polling (1s interval) for bind-mount compatibility',
6686
+ '--no-sandbox is added automatically when a container environment is detected'
6687
+ ]
6688
+ }
6638
6689
  },
6639
- manifest: normalizeManifest(readManifestRaw(config.extensionDir))
6640
- });
6641
- }
6642
- function saveManifestFingerprint(config) {
6643
- external_node_fs_.mkdirSync(external_node_path_.dirname(manifestFingerprintPath(config)), {
6644
- recursive: true
6645
- });
6646
- external_node_fs_.writeFileSync(manifestFingerprintPath(config), composeProjectFingerprint(config), 'utf8');
6690
+ examples: [
6691
+ 'extension --ai-help',
6692
+ 'extension --ai-help --output json',
6693
+ 'extension dev ./my-ext --logs=info --log-format=json',
6694
+ 'extension dev ./my-ext --host 0.0.0.0 --no-browser',
6695
+ 'extension dev ./my-ext --wait --browser=chromium --output json',
6696
+ 'extension start ./my-ext --wait --browser=chromium --output json',
6697
+ 'extension dev ./my-ext --gecko-binary flatpak:org.mozilla.firefox',
6698
+ 'extension install chromium',
6699
+ 'extension install --where',
6700
+ 'extension uninstall --where',
6701
+ 'extension uninstall --all'
6702
+ ]
6703
+ };
6647
6704
  }
6648
- function isProjectStale(config) {
6649
- const fpPath = manifestFingerprintPath(config);
6650
- if (!external_node_fs_.existsSync(fpPath)) return true;
6651
- const stored = external_node_fs_.readFileSync(fpPath, 'utf8');
6652
- return stored !== composeProjectFingerprint(config);
6705
+ function invalidAIHelpFormat(value) {
6706
+ return `${getLoggingPrefix('error')} Invalid value for ${messages_code('--output')}: ${external_pintor_default().red(String(value))}.\n${external_pintor_default().red('Pass')} ${messages_arg('pretty')} ${external_pintor_default().red('or')} ${messages_arg('json')}${external_pintor_default().red(', for example')} ${messages_code('extension --ai-help --output json')}${external_pintor_default().red('.')}`;
6653
6707
  }
6654
- function alignBundleIdentifiers(pbxprojContent, bundleId) {
6655
- return pbxprojContent.replace(/PRODUCT_BUNDLE_IDENTIFIER = ("?)([^;"]+)\1;/g, (_match, _quote, value)=>/\.Extension$/.test(String(value)) ? `PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}.Extension";` : `PRODUCT_BUNDLE_IDENTIFIER = "${bundleId}";`);
6708
+ function removedNoRunnerFlag() {
6709
+ return `${getLoggingPrefix('error')} ${messages_code('--no-runner')} was removed.\nUse ${messages_code('--no-browser')} instead.`;
6656
6710
  }
6657
- const PRESERVED_SETTINGS = [
6658
- 'DEVELOPMENT_TEAM',
6659
- 'CODE_SIGN_STYLE',
6660
- 'PROVISIONING_PROFILE_SPECIFIER'
6661
- ];
6662
- function pbxprojPath(config) {
6663
- return external_node_path_.join(xcodeProjectPath(config), 'project.pbxproj');
6711
+ function deprecatedOutputAlias(flag) {
6712
+ return `${getLoggingPrefix('warn')} ${messages_code(flag)} is deprecated and maps to ${messages_code('--output')}.\nPass ${messages_code('--output')} ${messages_arg('<pretty|json>')} instead.`;
6664
6713
  }
6665
- function extractXcodeUserSettings(pbxprojContent) {
6666
- const found = {};
6667
- for (const key of PRESERVED_SETTINGS){
6668
- const m = new RegExp(`\\b${key}\\s*=\\s*([^;]+);`).exec(pbxprojContent);
6669
- if (m) {
6670
- const val = m[1].trim();
6671
- if (val && '""' !== val && "''" !== val) found[key] = val;
6714
+ function noBrowserNotSupportedForCommand(command) {
6715
+ return `${getLoggingPrefix('error')} ${messages_code('--no-browser')} is only supported for ${messages_code('dev')}, ${messages_code('start')}, and ${messages_code('preview')}.\nReceived command: ${messages_code(command || '(none)')}`;
6716
+ }
6717
+ function readRecentConsole(projectPath, browser, target, limit) {
6718
+ const file = external_node_path_default().resolve(projectPath, 'dist', 'extension-js', browser, 'logs.ndjson');
6719
+ let lines;
6720
+ try {
6721
+ lines = external_node_fs_default().readFileSync(file, 'utf-8').split('\n').filter(Boolean);
6722
+ } catch {
6723
+ return [];
6724
+ }
6725
+ const out = [];
6726
+ for (const line of lines){
6727
+ let e;
6728
+ try {
6729
+ e = JSON.parse(line);
6730
+ } catch {
6731
+ continue;
6732
+ }
6733
+ if (e && 'header' !== e.type) {
6734
+ if (!target.context || e.context === target.context) {
6735
+ if (null == target.tabId || e.tabId === target.tabId) out.push({
6736
+ seq: e.seq,
6737
+ level: e.level,
6738
+ context: e.context,
6739
+ messageParts: e.messageParts,
6740
+ eventType: e.eventType,
6741
+ code: e.code,
6742
+ tabId: e.tabId
6743
+ });
6744
+ }
6672
6745
  }
6673
6746
  }
6674
- return found;
6747
+ return out.slice(Math.max(0, out.length - limit));
6675
6748
  }
6676
- function applyXcodeUserSettings(pbxprojContent, settings) {
6677
- let result = pbxprojContent;
6678
- for (const [key, value] of Object.entries(settings)){
6679
- const existing = new RegExp(`\\b${key}\\s*=\\s*[^;]+;`, 'g');
6680
- result = existing.test(result) ? result.replace(existing, `${key} = ${value};`) : result.replace(/buildSettings\s*=\s*\{/g, `buildSettings = {\n\t\t\t\t${key} = ${value};`);
6681
- }
6682
- return result;
6749
+ function codeForBridgeError(name, message) {
6750
+ if ('Timeout' === name) return messaging.Lp.E_TIMEOUT;
6751
+ if ('Unavailable' === name) return messaging.Lp.E_CONTROL_UNAVAILABLE;
6752
+ if ('Forbidden' === name) return /token missing/i.test(message) ? messaging.Lp.E_TOKEN_MISSING : messaging.Lp.E_EVAL_REFUSED;
6753
+ if ('BadRequest' === name) return messaging.Lp.E_ARGS;
6754
+ if ('Unsupported' === name) return /needs a --tab id|no injectable frame|is not open/i.test(message) ? messaging.Lp.E_TARGET_NOT_FOUND : messaging.Lp.E_NOT_IMPLEMENTED;
6755
+ if ('EvalError' === name) return messaging.Lp.E_EVAL;
6756
+ if ('InspectError' === name) return messaging.Lp.E_INSPECT;
6757
+ if ('StorageError' === name) return messaging.Lp.E_STORAGE;
6758
+ return messaging.Lp.E_INTERNAL;
6683
6759
  }
6684
- function backupAndRestoreXcodeSettings(config) {
6685
- const projFile = pbxprojPath(config);
6686
- let saved = {};
6687
- if (external_node_fs_.existsSync(projFile)) saved = extractXcodeUserSettings(external_node_fs_.readFileSync(projFile, 'utf8'));
6760
+ function statusForCode(code) {
6761
+ if (code === messaging.Lp.E_TIMEOUT) return 'timeout';
6762
+ if (code === messaging.Lp.E_SESSION_NOT_FOUND || code === messaging.Lp.E_TARGET_NOT_FOUND) return 'not-found';
6763
+ if (code === messaging.Lp.E_CONTROL_DENIED || code === messaging.Lp.E_EVAL_REFUSED || code === messaging.Lp.E_TOKEN_MISSING) return 'denied';
6764
+ if (code === messaging.Lp.E_ARGS) return 'usage';
6765
+ return 'failed';
6766
+ }
6767
+ function buildActEnvelope(command, result) {
6768
+ const { ok, value, truncated, error, ...extras } = result;
6769
+ const wasTruncated = true === truncated;
6770
+ if (ok) return {
6771
+ ...extras,
6772
+ ...messaging.Pr.ok(command, 'ok', value ?? null, {
6773
+ truncated: wasTruncated
6774
+ })
6775
+ };
6776
+ const raw = error || {};
6777
+ const name = 'string' == typeof raw.name ? raw.name : 'Error';
6778
+ const message = 'string' == typeof raw.message ? raw.message : 'command failed';
6779
+ const code = codeForBridgeError(name, message);
6688
6780
  return {
6689
- saved,
6690
- restore () {
6691
- if (0 === Object.keys(saved).length) return;
6692
- if (!external_node_fs_.existsSync(projFile)) return;
6693
- const content = external_node_fs_.readFileSync(projFile, 'utf8');
6694
- external_node_fs_.writeFileSync(projFile, applyXcodeUserSettings(content, saved), 'utf8');
6695
- }
6781
+ ...extras,
6782
+ ...messaging.Pr.fail(command, statusForCode(code), {
6783
+ ...raw,
6784
+ code,
6785
+ message,
6786
+ name
6787
+ }, {
6788
+ truncated: wasTruncated
6789
+ })
6696
6790
  };
6697
6791
  }
6698
- function isMacOS() {
6699
- return 'darwin' === process.platform;
6700
- }
6701
- function activeDeveloperDir() {
6792
+ function writeFrame(frame) {
6702
6793
  try {
6703
- const result = (0, external_node_child_process_.spawnSync)('xcode-select', [
6704
- '-p'
6705
- ], {
6706
- encoding: 'utf8',
6707
- timeout: 15000
6708
- });
6709
- if (0 !== result.status) return null;
6710
- const resolved = String(result.stdout || '').trim();
6711
- return resolved.length > 0 ? resolved : null;
6794
+ external_node_fs_default().writeSync(1, `${JSON.stringify(frame)}\n`);
6712
6795
  } catch {
6713
- return null;
6796
+ console.log(JSON.stringify(frame));
6714
6797
  }
6715
6798
  }
6716
- function findWithXcrun(tool) {
6717
- try {
6718
- const result = (0, external_node_child_process_.spawnSync)('xcrun', [
6719
- '--find',
6720
- tool
6721
- ], {
6722
- encoding: 'utf8',
6723
- timeout: 15000
6724
- });
6725
- if (0 !== result.status) return null;
6726
- const resolved = String(result.stdout || '').trim();
6727
- return resolved.length > 0 ? resolved : null;
6728
- } catch {
6729
- return null;
6730
- }
6799
+ function fail(message, frame) {
6800
+ console.error(message);
6801
+ if (frame && 'json' === frame.output) writeFrame(messaging.Pr.fail(frame.command, statusForCode(frame.code), {
6802
+ code: frame.code,
6803
+ message,
6804
+ name: 'CliError'
6805
+ }));
6806
+ process.exit(1);
6731
6807
  }
6732
- function detectSafariToolchain() {
6733
- const platformOk = isMacOS();
6734
- if (!platformOk) return {
6735
- platformOk: false,
6736
- developerDir: null,
6737
- needsFullXcode: false,
6738
- converter: null,
6739
- xcodebuild: null,
6740
- ok: false
6741
- };
6742
- const developerDir = activeDeveloperDir();
6743
- const needsFullXcode = !developerDir || /CommandLineTools/i.test(developerDir);
6744
- const converter = findWithXcrun('safari-web-extension-converter');
6745
- const xcodebuild = findWithXcrun('xcodebuild');
6746
- return {
6747
- platformOk,
6748
- developerDir,
6749
- needsFullXcode,
6750
- converter,
6751
- xcodebuild,
6752
- ok: Boolean(converter && xcodebuild)
6808
+ function printResult(result, output, command) {
6809
+ if ('json' === output) return void console.log(JSON.stringify(buildActEnvelope(command, result)));
6810
+ if (result.ok) {
6811
+ const value = result.value;
6812
+ console.log('string' == typeof value ? value : JSON.stringify(value, null, 2));
6813
+ if (result.truncated) console.error('… result truncated (byte cap)');
6814
+ return;
6815
+ }
6816
+ const err = result.error || {
6817
+ name: 'Error',
6818
+ message: 'command failed'
6753
6819
  };
6820
+ console.error(`${err.name}: ${err.message}${err.engine ? ` (engine: ${err.engine})` : ''}`);
6754
6821
  }
6755
- function fallbackLogger() {
6756
- return {
6757
- info: (...a)=>console.log(...a),
6758
- warn: (...a)=>console.warn(...a),
6759
- error: (...a)=>console.error(...a),
6760
- debug: (...a)=>console?.debug?.(...a)
6822
+ async function runCommand(input) {
6823
+ const projectPath = external_node_path_default().resolve(input.projectPathArg || process.cwd());
6824
+ const browser = input.opts.browser || 'chromium';
6825
+ const { BridgeController, readReadyContract, readControlToken } = await loadExtensionDevelopBridgeModule();
6826
+ const outputFrame = {
6827
+ command: input.command,
6828
+ output: input.opts.output
6761
6829
  };
6762
- }
6763
- function isTestEnv() {
6764
- return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);
6765
- }
6766
- function delay(ms) {
6767
- return new Promise((resolve)=>setTimeout(resolve, ms));
6768
- }
6769
- const TOOL_TAIL_LINES = 50;
6770
- const TOOL_TAIL_BYTES = 8192;
6771
- function toolOutputTail(output) {
6772
- const lines = output.slice(4 * -TOOL_TAIL_BYTES).split(/\r?\n/).filter((line)=>line.trim().length > 0);
6773
- const tail = lines.slice(-TOOL_TAIL_LINES).join('\n');
6774
- return tail.length > TOOL_TAIL_BYTES ? tail.slice(-TOOL_TAIL_BYTES) : tail;
6775
- }
6776
- function runTool(bin, args, opts) {
6777
- const streamOutput = (0, messaging._o)() && !opts?.quiet;
6778
- return new Promise((resolve)=>{
6779
- let output = '';
6780
- const child = (0, external_node_child_process_.spawn)(bin, args, {
6781
- stdio: [
6782
- 'ignore',
6783
- 'pipe',
6784
- 'pipe'
6785
- ]
6786
- });
6787
- const onChunk = (chunk)=>{
6788
- const text = String(chunk);
6789
- output += text;
6790
- if (output.length > 8 * TOOL_TAIL_BYTES) output = output.slice(4 * -TOOL_TAIL_BYTES);
6791
- if (streamOutput) process.stdout.write(text);
6792
- };
6793
- child.stdout?.on('data', onChunk);
6794
- child.stderr?.on('data', onChunk);
6795
- child.on('error', (error)=>resolve({
6796
- ok: false,
6797
- code: null,
6798
- output: `${output}${String(error)}`
6799
- }));
6800
- child.on('close', (code)=>resolve({
6801
- ok: 0 === code,
6802
- code,
6803
- output
6804
- }));
6830
+ const ready = readReadyContract(projectPath, browser);
6831
+ if (!ready) fail(`No active control channel found for ${browser}. Run \`extension dev --browser=${browser} --allow-control\` first.`, {
6832
+ ...outputFrame,
6833
+ code: messaging.Lp.E_SESSION_NOT_FOUND
6805
6834
  });
6806
- }
6807
- function converterWarnings(output) {
6808
- return output.split(/\r?\n/).filter((line)=>/warning/i.test(line)).map((line)=>line.trim()).filter((line)=>line.length > 0);
6809
- }
6810
- async function confirmRegisteredWithSafari(bundleIdentifier) {
6811
- const needle = `${bundleIdentifier}.Extension`;
6812
- for(let attempt = 0; attempt < 6; attempt += 1){
6813
- const { ok, output } = await runTool('pluginkit', [
6814
- '-m'
6815
- ], {
6816
- quiet: true
6835
+ const token = input.needsToken ? readControlToken(projectPath, browser) : void 0;
6836
+ const controller = new BridgeController({
6837
+ controlPort: ready.controlPort,
6838
+ instanceId: ready.instanceId,
6839
+ token: token ?? void 0
6840
+ });
6841
+ try {
6842
+ await controller.connect();
6843
+ } catch (err) {
6844
+ controller.close();
6845
+ const message = err?.message || 'could not connect to the control channel';
6846
+ fail(message, {
6847
+ ...outputFrame,
6848
+ code: /code 40\d\d/.test(message) ? messaging.Lp.E_CONTROL_DENIED : messaging.Lp.E_CONTROL_UNAVAILABLE
6817
6849
  });
6818
- if (ok && output.includes(needle)) return true;
6819
- await delay(800);
6820
6850
  }
6821
- return false;
6822
- }
6823
- function safariPreflightError() {
6824
- const tc = detectSafariToolchain();
6825
- if (!tc.platformOk) return messages.G3(process.platform);
6826
- if (!tc.ok) {
6827
- if (tc.needsFullXcode) return messages.Xi(tc.developerDir);
6828
- return messages.Cs(tc.converter ? 'xcodebuild' : 'safari-web-extension-converter');
6851
+ const timeoutMs = input.opts.timeout ? Number(input.opts.timeout) : 5000;
6852
+ let result;
6853
+ try {
6854
+ result = await controller.command({
6855
+ op: input.op,
6856
+ target: input.target,
6857
+ args: input.args,
6858
+ timeoutMs
6859
+ });
6860
+ } catch (err) {
6861
+ controller.close();
6862
+ const message = err?.message || 'command failed';
6863
+ fail(message, {
6864
+ ...outputFrame,
6865
+ code: /timed out/i.test(message) ? messaging.Lp.E_TIMEOUT : messaging.Lp.E_CONTROL_UNAVAILABLE
6866
+ });
6867
+ } finally{
6868
+ controller.close();
6829
6869
  }
6830
- return null;
6870
+ if (result?.ok && input.augment) try {
6871
+ Object.assign(result, input.augment(projectPath, browser, result));
6872
+ } catch {}
6873
+ printResult(result, input.opts.output, input.command);
6874
+ await exitAfterDrain(result.ok ? 0 : 1);
6831
6875
  }
6832
- function safariBuildPreflight() {
6833
- const tc = detectSafariToolchain();
6834
- if (!tc.platformOk) return {
6835
- severity: 'skip',
6836
- message: messages.UW(process.platform)
6837
- };
6838
- if (!tc.ok) return {
6839
- severity: 'fatal',
6840
- message: tc.needsFullXcode ? messages.Xi(tc.developerDir) : messages.Cs(tc.converter ? 'xcodebuild' : 'safari-web-extension-converter')
6841
- };
6842
- return {
6843
- severity: 'ok'
6876
+ function targetFrom(opts, fallback = 'background') {
6877
+ const context = opts.context || fallback;
6878
+ const target = {
6879
+ context
6844
6880
  };
6881
+ if (opts.url) target.url = opts.url;
6882
+ if (null != opts.tab && '' !== opts.tab) target.tabId = Number(opts.tab);
6883
+ return target;
6845
6884
  }
6846
- async function runSafariPipeline(compilation, host, logger, mode) {
6847
- const config = resolveSafariBuildConfig(compilation, host);
6848
- const converterArgs = composeConverterArgs(config);
6849
- const xcodebuildArgs = composeXcodebuildArgs(config);
6850
- if (host.dryRun || isTestEnv()) return void logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
6851
- const toolchain = detectSafariToolchain();
6852
- if (!toolchain.platformOk) return void logger.warn?.(messages.G3(process.platform));
6853
- if (!toolchain.ok) {
6854
- if (toolchain.needsFullXcode) logger.error?.(messages.Xi(toolchain.developerDir));
6855
- else {
6856
- const missing = toolchain.converter ? 'xcodebuild' : 'safari-web-extension-converter';
6857
- logger.error?.(messages.Cs(missing));
6858
- }
6859
- return;
6860
- }
6861
- const projectExists = external_node_fs_.existsSync(xcodeProjectPath(config));
6862
- const needsConversion = !projectExists || host.forceRegenerate || isProjectStale(config);
6863
- if (needsConversion) {
6864
- if (projectExists) {
6865
- logger.info?.(host.forceRegenerate ? messages.Tq() : messages.jc());
6866
- logger.warn?.(messages.H1([
6867
- ...PRESERVED_SETTINGS
6868
- ]));
6869
- }
6870
- const { saved, restore } = backupAndRestoreXcodeSettings(config);
6871
- logger.info?.(messages.uK(config.extensionDir));
6872
- const converted = await runTool('xcrun', converterArgs);
6873
- if (!converted.ok) {
6874
- const tail = toolOutputTail(converted.output);
6875
- logger.error?.(messages.A('safari-web-extension-converter', converted.code, tail));
6876
- throw new Error(`safari-web-extension-converter failed (exit ${converted.code})\n${tail}`);
6877
- }
6878
- const warnings = converterWarnings(converted.output);
6879
- if (warnings.length > 0) logger.warn?.(messages.y1(warnings));
6880
- const projFile = pbxprojPath(config);
6881
- if (external_node_fs_.existsSync(projFile)) external_node_fs_.writeFileSync(projFile, alignBundleIdentifiers(external_node_fs_.readFileSync(projFile, 'utf8'), config.bundleIdentifier), 'utf8');
6882
- restore();
6883
- const preservedKeys = Object.keys(saved);
6884
- if (preservedKeys.length > 0) logger.info?.(messages.oB(preservedKeys));
6885
- saveManifestFingerprint(config);
6886
- logger.info?.(messages.l_(config.projectLocation));
6887
- } else logger.info?.(messages.oM());
6888
- if ('full' === mode) logger.info?.(messages.jR(macOsSchemeName(config)));
6889
- const built = await runTool('xcodebuild', xcodebuildArgs);
6890
- if (!built.ok) {
6891
- const tail = toolOutputTail(built.output);
6892
- logger.error?.(messages.A('xcodebuild', built.code, tail));
6893
- throw new Error(`xcodebuild failed (exit ${built.code})\n${tail}`);
6894
- }
6895
- const appPath = builtAppPath(config);
6896
- if ('resync' === mode) return void logger.info?.(messages.cF(config.appName));
6897
- logger.info?.(messages.Rl(appPath));
6898
- if (config.bundleIdDerived) logger.info?.(messages.$q(config.bundleIdentifier));
6899
- if (!config.open) return void logger.info?.(messages.aO(appPath, config.appName));
6900
- const target = external_node_fs_.existsSync(appPath) ? appPath : xcodeProjectPath(config);
6901
- logger.info?.(messages.fw(target));
6902
- await runTool('open', [
6903
- target
6904
- ]);
6905
- if (config.safariBinary) await runTool('open', [
6906
- '-a',
6907
- config.safariBinary
6908
- ]);
6909
- logger.info?.(messages.fO(config.appName));
6910
- if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
6911
- else logger.warn?.(messages.qY(config.appName));
6912
- }
6913
- async function packageSafariExtension(host, outputPath, logger, mode = 'full') {
6914
- const compilation = {
6915
- options: {
6916
- output: {
6917
- path: outputPath
6885
+ const commonOptions = (cmd)=>cmd.option('--browser <chrome | chromium | edge | firefox>', 'which session to target (default chromium)').option('--timeout <ms>', 'command timeout in milliseconds (default 5000)').option('--output <pretty|json>', 'output format (default pretty)');
6886
+ function registerActCommands(program) {
6887
+ commonOptions(program.command('eval').arguments('<expression> [project-path]').description(commandDescriptions.eval).option('--context <background|popup|options|sidebar|devtools|newtab|history|bookmarks|content|page>', 'target context (default background). Extension pages (popup/options/sidebar/devtools/newtab/history/bookmarks) answer via their own in-page relay and must be open').option('--url <glob|substring>', 'for content/page: document to target (resolved to its tab)').option('--tab <id>', 'for content/page: a specific tab (default: the --url match, else the active tab)')).action(async (expression, projectPathArg, opts)=>{
6888
+ await runCommand({
6889
+ projectPathArg,
6890
+ command: 'eval',
6891
+ op: 'eval',
6892
+ target: targetFrom(opts),
6893
+ args: {
6894
+ expression
6895
+ },
6896
+ needsToken: true,
6897
+ opts
6898
+ });
6899
+ });
6900
+ commonOptions(program.command('storage').arguments('<action> [project-path]').description(commandDescriptions.storage).option('--area <local|sync|session|managed>', 'storage area (default local)').option('--key <key>', 'key to get or set').option('--value <json>', 'JSON value to set (with set)').option('--context <background|popup|options|sidebar|content>', 'target context (default background)')).action(async (action, projectPathArg, opts)=>{
6901
+ const area = opts.area || 'local';
6902
+ if ('get' === action) return void await runCommand({
6903
+ projectPathArg,
6904
+ command: 'storage',
6905
+ op: 'storage.get',
6906
+ target: targetFrom(opts),
6907
+ args: opts.key ? {
6908
+ area,
6909
+ key: opts.key
6910
+ } : {
6911
+ area
6912
+ },
6913
+ opts
6914
+ });
6915
+ if ('set' === action) {
6916
+ if (!opts.key || null == opts.value) fail('storage set requires --key and --value', {
6917
+ command: 'storage',
6918
+ code: messaging.Lp.E_ARGS,
6919
+ output: opts.output
6920
+ });
6921
+ let parsed;
6922
+ try {
6923
+ parsed = JSON.parse(opts.value);
6924
+ } catch {
6925
+ parsed = opts.value;
6918
6926
  }
6919
- },
6920
- outputOptions: {
6921
- path: outputPath
6927
+ await runCommand({
6928
+ projectPathArg,
6929
+ command: 'storage',
6930
+ op: 'storage.set',
6931
+ target: targetFrom(opts),
6932
+ args: {
6933
+ area,
6934
+ items: {
6935
+ [opts.key]: parsed
6936
+ }
6937
+ },
6938
+ opts
6939
+ });
6940
+ return;
6922
6941
  }
6923
- };
6924
- await runSafariPipeline(compilation, host, logger || fallbackLogger(), mode);
6942
+ fail(`unknown storage action: ${action} (use get or set)`, {
6943
+ command: 'storage',
6944
+ code: messaging.Lp.E_ARGS,
6945
+ output: opts.output
6946
+ });
6947
+ });
6948
+ commonOptions(program.command('reload').arguments('[project-path]').description(commandDescriptions.reload).option('--context <background|content|page>', 'target context (default background)').option('--tab <id>', 'for content/page: a specific tab')).action(async (projectPathArg, opts)=>{
6949
+ await runCommand({
6950
+ projectPathArg,
6951
+ command: 'reload',
6952
+ op: 'reload',
6953
+ target: targetFrom(opts),
6954
+ opts
6955
+ });
6956
+ });
6957
+ commonOptions(program.command('inspect').arguments('[project-path]').description(commandDescriptions.inspect).option('--context <content|page|popup|options|sidebar|devtools|newtab|history|bookmarks>', 'what to inspect: content/page or an open surface, including url-override pages (default content)').option('--url <glob|substring>', 'for content/page: document to target (resolved to its tab)').option('--tab <id>', 'for content/page: a specific tab (default: the --url match, else the active tab)').option('--list-tabs', 'list open tabs as {tabId,url,title} and exit').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 (projectPathArg, opts)=>{
6958
+ if (opts.listTabs) return void await runCommand({
6959
+ projectPathArg,
6960
+ command: 'inspect',
6961
+ op: 'tabs.query',
6962
+ target: {
6963
+ context: 'background'
6964
+ },
6965
+ args: {},
6966
+ opts
6967
+ });
6968
+ const include = opts.include ? opts.include.split(',').map((s)=>s.trim()).filter(Boolean) : [
6969
+ 'summary'
6970
+ ];
6971
+ const target = targetFrom(opts, 'content');
6972
+ await runCommand({
6973
+ projectPathArg,
6974
+ command: 'inspect',
6975
+ op: 'inspect',
6976
+ target,
6977
+ args: {
6978
+ include,
6979
+ maxBytes: opts.maxBytes ? Number(opts.maxBytes) : void 0
6980
+ },
6981
+ opts,
6982
+ augment: opts.withConsole ? (projectPath, browser)=>{
6983
+ const n = 'string' == typeof opts.withConsole && '' !== opts.withConsole ? Number(opts.withConsole) : 20;
6984
+ return {
6985
+ console: readRecentConsole(projectPath, browser, target, Number.isFinite(n) && n > 0 ? n : 20)
6986
+ };
6987
+ } : void 0
6988
+ });
6989
+ });
6990
+ commonOptions(program.command('open').arguments('<surface> [project-path]').description('Open an extension surface: popup, options, sidebar, action, or command (requires --allow-control)').option('--name <command>', 'with `open command`: the chrome.commands name to trigger')).action(async (surface, projectPathArg, opts)=>{
6991
+ const allowed = [
6992
+ 'popup',
6993
+ 'options',
6994
+ 'sidebar',
6995
+ 'action',
6996
+ 'command'
6997
+ ];
6998
+ if (!allowed.includes(surface)) fail(`unknown surface: ${surface} (use popup, options, sidebar, action, or command)`, {
6999
+ command: 'open',
7000
+ code: messaging.Lp.E_ARGS,
7001
+ output: opts.output
7002
+ });
7003
+ const inBackground = 'action' === surface || 'command' === surface;
7004
+ const context = inBackground ? 'background' : surface;
7005
+ const args = {
7006
+ surface
7007
+ };
7008
+ if ('command' === surface && opts.name) args.name = opts.name;
7009
+ await runCommand({
7010
+ projectPathArg,
7011
+ command: 'open',
7012
+ op: 'open',
7013
+ target: {
7014
+ context
7015
+ },
7016
+ args,
7017
+ opts
7018
+ });
7019
+ });
6925
7020
  }
7021
+ var safari_launch = __webpack_require__("./browsers/run-safari/safari-launch/index.ts");
7022
+ var safari_config = __webpack_require__("./browsers/run-safari/safari-launch/safari-config.ts");
7023
+ var safari_packager = __webpack_require__("./browsers/run-safari/safari-packager.ts");
6926
7024
  function parseLogContexts(raw) {
6927
7025
  if (!raw || 0 === String(raw).trim().length) return;
6928
7026
  if ('all' === String(raw).trim().toLowerCase()) return;
@@ -6995,7 +7093,7 @@ Cross-Browser Compatibility
6995
7093
  return true;
6996
7094
  }
6997
7095
  function registerBuildCommand(program) {
6998
- 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]', 'suppress the build summary output. 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('--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, { browser = 'chromium', ...buildOptions })=>{
7096
+ 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]', 'suppress the build summary output. 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('--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('--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, { browser = 'chromium', ...buildOptions })=>{
6999
7097
  if (buildOptions.debug || buildOptions.author || buildOptions.authorMode) {
7000
7098
  process.env.EXTENSION_DEBUG = '1';
7001
7099
  process.env.EXTENSION_AUTHOR_MODE = 'true';
@@ -7032,18 +7130,19 @@ Cross-Browser Compatibility
7032
7130
  '--force-regenerate',
7033
7131
  buildOptions.forceRegenerate
7034
7132
  ]
7035
- ].filter(([, value])=>void 0 !== value && false !== value);
7133
+ ].filter(([, value])=>void 0 !== value && false !== value).map(([flag])=>flag);
7134
+ if (void 0 !== buildOptions.macosOnly) safariOnlyFlags.push('--macos-only');
7036
7135
  if (safariOnlyFlags.length > 0 && !list.some(isSafariVendor)) {
7037
- console.error(safariOnlyOption(safariOnlyFlags.map(([flag])=>flag)));
7136
+ console.error(safariOnlyOption(safariOnlyFlags));
7038
7137
  process.exit(1);
7039
7138
  }
7040
- if (buildOptions.bundleId && !isValidBundleId(buildOptions.bundleId)) {
7139
+ if (buildOptions.bundleId && !(0, safari_config.OA)(buildOptions.bundleId)) {
7041
7140
  console.error(safariInvalidBundleId(buildOptions.bundleId));
7042
7141
  process.exit(1);
7043
7142
  }
7044
7143
  let safariPackagingEnabled = true;
7045
7144
  if (list.some(isSafariVendor)) {
7046
- const preflight = safariBuildPreflight();
7145
+ const preflight = (0, safari_launch.QW)();
7047
7146
  if ('fatal' === preflight.severity) {
7048
7147
  console.error(preflight.message);
7049
7148
  process.exit(1);
@@ -7057,8 +7156,9 @@ Cross-Browser Compatibility
7057
7156
  const asJson = 'json' === buildOptions.output;
7058
7157
  if (asJson) process.env.EXTENSION_OUTPUT = 'json';
7059
7158
  const built = [];
7159
+ const summaries = [];
7060
7160
  for (const vendor of list)try {
7061
- await extensionBuild(pathOrRemoteUrl, {
7161
+ const summary = await extensionBuild(pathOrRemoteUrl, {
7062
7162
  browser: vendor,
7063
7163
  exitOnError: !asJson,
7064
7164
  polyfill: buildOptions.polyfill,
@@ -7071,20 +7171,15 @@ Cross-Browser Compatibility
7071
7171
  mode,
7072
7172
  appName: buildOptions.appName,
7073
7173
  bundleId: buildOptions.bundleId,
7174
+ macOsOnly: buildOptions.macosOnly,
7074
7175
  forceRegenerate: buildOptions.forceRegenerate,
7075
- safariPackager: safariPackagingEnabled ? async (distPath, packagerMode, overrides)=>{
7076
- await packageSafariExtension({
7077
- extension: [
7078
- distPath
7079
- ],
7080
- browser: vendor,
7081
- noOpen: true !== buildOptions.open,
7082
- dryRun: false,
7083
- ...overrides
7084
- }, distPath, void 0, packagerMode);
7085
- } : void 0
7176
+ safariPackager: safariPackagingEnabled ? (0, safari_packager.P)({
7177
+ browser: vendor,
7178
+ noOpen: true !== buildOptions.open
7179
+ }) : void 0
7086
7180
  });
7087
7181
  built.push(vendor);
7182
+ if (summary) summaries.push(summary);
7088
7183
  } catch (error) {
7089
7184
  if (!asJson) throw error;
7090
7185
  console.log(JSON.stringify(messaging.Pr.fail('build', 'build-failed', {
@@ -7098,7 +7193,8 @@ Cross-Browser Compatibility
7098
7193
  if (asJson) console.log(JSON.stringify(messaging.Pr.ok('build', 'built', {
7099
7194
  projectPath: pathOrRemoteUrl,
7100
7195
  browsers: built,
7101
- mode
7196
+ mode,
7197
+ summaries
7102
7198
  })));
7103
7199
  });
7104
7200
  }
@@ -7413,7 +7509,7 @@ Cross-Browser Compatibility
7413
7509
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 8080;
7414
7510
  }
7415
7511
  function registerDevCommand(program) {
7416
- 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 <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('--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` (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, { browser = 'chromium', ...devOptions })=>{
7512
+ 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 <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('--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` (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, { browser = 'chromium', ...devOptions })=>{
7417
7513
  if (devOptions.debug || devOptions.author || devOptions.authorMode) {
7418
7514
  process.env.EXTENSION_DEBUG = '1';
7419
7515
  process.env.EXTENSION_AUTHOR_MODE = 'true';
@@ -7460,16 +7556,16 @@ Cross-Browser Compatibility
7460
7556
  '--force-regenerate',
7461
7557
  opts.forceRegenerate
7462
7558
  ]
7463
- ].filter(([, value])=>void 0 !== value && false !== value);
7559
+ ].filter(([, value])=>void 0 !== value && false !== value).map(([flag])=>flag);
7560
+ if (void 0 !== opts.macosOnly) safariOnlyFlags.push('--macos-only');
7464
7561
  if (safariOnlyFlags.length > 0 && !list.some(isSafariVendor)) {
7465
- const flags = safariOnlyFlags.map(([flag])=>flag);
7466
- console.error(safariOnlyOption(flags));
7562
+ console.error(safariOnlyOption(safariOnlyFlags));
7467
7563
  failAndExit(asJson, 'usage', {
7468
7564
  code: messaging.Lp.E_INVALID_OPTION,
7469
- message: `${flags.join(', ')} apply to safari targets only.`
7565
+ message: `${safariOnlyFlags.join(', ')} apply to safari targets only.`
7470
7566
  });
7471
7567
  }
7472
- if (opts.bundleId && !isValidBundleId(opts.bundleId)) {
7568
+ if (opts.bundleId && !(0, safari_config.OA)(opts.bundleId)) {
7473
7569
  console.error(safariInvalidBundleId(opts.bundleId));
7474
7570
  failAndExit(asJson, 'usage', {
7475
7571
  code: messaging.Lp.E_INVALID_OPTION,
@@ -7477,7 +7573,7 @@ Cross-Browser Compatibility
7477
7573
  });
7478
7574
  }
7479
7575
  if (list.some(isSafariVendor)) {
7480
- const issue = safariPreflightError();
7576
+ const issue = (0, safari_launch.iF)();
7481
7577
  if (issue) {
7482
7578
  console.error(issue);
7483
7579
  failAndExit(asJson, 'failed', {
@@ -7541,6 +7637,7 @@ Cross-Browser Compatibility
7541
7637
  geckoBinary: devOptions.geckoBinary,
7542
7638
  polyfill: devOptions.polyfill?.toString() !== 'false',
7543
7639
  noOpen: false === devOptions.open,
7640
+ macOsOnly: opts.macosOnly,
7544
7641
  startingUrl: devOptions.startingUrl,
7545
7642
  install: devOptions.install,
7546
7643
  noBrowser,
@@ -7553,17 +7650,10 @@ Cross-Browser Compatibility
7553
7650
  logUrl: devOptions.logUrl,
7554
7651
  logTab: devOptions.logTab,
7555
7652
  launcher: noBrowser ? void 0 : browsers.launchBrowser,
7556
- safariPackager: async (distPath, mode, overrides)=>{
7557
- await packageSafariExtension({
7558
- extension: [
7559
- distPath
7560
- ],
7561
- browser: vendor,
7562
- noOpen: false === devOptions.open,
7563
- dryRun: false,
7564
- ...overrides
7565
- }, distPath, void 0, mode);
7566
- }
7653
+ safariPackager: (0, safari_packager.P)({
7654
+ browser: vendor,
7655
+ noOpen: false === devOptions.open
7656
+ })
7567
7657
  };
7568
7658
  try {
7569
7659
  await extensionDev(pathOrRemoteUrl, devArgs);