extension 4.1.0 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,7 +41,7 @@ Browser extensions ship with the worst dev experience in modern web. Manifest V3
41
41
 
42
42
  ## Watch it work
43
43
 
44
- [![Extension.js in 60 seconds: one command, a running extension](https://media.extension.land/video/extension-js/sixty-second-demo.gif)](https://extension.js.org/docs)
44
+ [![One command, and the extension is running in a real browser](https://media.extension.land/video/extension-js/sixty-second-demo.gif)](https://extension.js.org/docs)
45
45
 
46
46
  Or skip the install and try a [live template](https://templates.extension.dev) in your browser.
47
47
 
@@ -155,10 +155,12 @@ npm install extension@latest --save-dev
155
155
  }
156
156
  ```
157
157
 
158
- Run `npm run dev` to develop, `npm run build` for production, and `npm run preview` to inspect the production output.
158
+ Run `npm run dev` to develop, and watch the browser open with your extension already loaded:
159
159
 
160
160
  [![extension dev: the browser opens with the extension loaded](https://media.extension.land/video/extension-js/dev-build-preview.gif)](https://extension.js.org/docs/commands/dev)
161
161
 
162
+ Use `npm run build` for production, and `npm run preview` to inspect the production output.
163
+
162
164
  ## Start from a Chrome sample
163
165
 
164
166
  Pull any sample from [Chrome Extension Samples](https://github.com/GoogleChrome/chrome-extensions-samples) and run it directly:
package/dist/browsers.cjs CHANGED
@@ -706,8 +706,10 @@ var __webpack_modules__ = {
706
706
  function safariDefaultBundleIdNote(bundleId) {
707
707
  return `${getLoggingPrefix('warn')} The bundle id ${pintor__rspack_import_8_default().gray(bundleId)} is generated from the app name.\nEvery project built from the same source shares this id, and the first team to register it takes it.\nSet your own with ${pintor__rspack_import_8_default().blue('--bundle-id')} before your first submission.\nAfter a submission, a new id is a new extension with none of your users.`;
708
708
  }
709
- function safariOpenHint(appPath, appName) {
710
- return `${getLoggingPrefix('info')} Launch it once to register with Safari: ${pintor__rspack_import_8_default().blue('open')} ${pintor__rspack_import_8_default().underline(`"${appPath}"`)}\nThen enable ${pintor__rspack_import_8_default().yellow(appName)} via Safari ▸ Develop ▸ ${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} and Safari ▸ Settings ▸ Extensions.`;
709
+ function safariOpenHint(appPath, appName, signed) {
710
+ const launch = `${getLoggingPrefix('info')} Launch it once to register with Safari: ${pintor__rspack_import_8_default().blue('open')} ${pintor__rspack_import_8_default().underline(`"${appPath}"`)}\n`;
711
+ if (signed) return launch + `Then turn on ${pintor__rspack_import_8_default().yellow(appName)} in Safari ▸ Settings ▸ Extensions.`;
712
+ return launch + `Then enable ${pintor__rspack_import_8_default().yellow(appName)} via Safari ▸ Develop ▸ ` + `${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} and Safari ▸ Settings ▸ Extensions.`;
711
713
  }
712
714
  function safariDryRunNotBuilding() {
713
715
  return `${getLoggingPrefix('info')} Dry run: skip the Safari app build.`;
@@ -718,7 +720,8 @@ var __webpack_modules__ = {
718
720
  function safariDryRunXcodebuild(cmd) {
719
721
  return `${getLoggingPrefix('info')} xcodebuild: ${pintor__rspack_import_8_default().gray(cmd)}.`;
720
722
  }
721
- function safariNextSteps(appName) {
723
+ function safariNextSteps(appName, signed) {
724
+ if (signed) return `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_8_default().yellow(appName)} in Safari:\n ${pintor__rspack_import_8_default().gray('1.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_8_default().yellow(appName)}\n ${pintor__rspack_import_8_default().gray('→')} Signed with your team id, so this survives a restart.`;
722
725
  return `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_8_default().yellow(appName)} in Safari:\n ${pintor__rspack_import_8_default().gray('1.')} Safari ▸ Settings ▸ Advanced ▸ check ${pintor__rspack_import_8_default().yellow('“Show features for web developers”')}\n ${pintor__rspack_import_8_default().gray('2.')} Safari ▸ Develop ▸ ${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} ${pintor__rspack_import_8_default().gray('(resets each launch)')}\n ${pintor__rspack_import_8_default().gray('3.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_8_default().yellow(appName)}\n ${pintor__rspack_import_8_default().gray('→')} The app window that just opened can also take you there.`;
723
726
  }
724
727
  function safariRegistered(appName) {
@@ -780,8 +783,6 @@ var __webpack_modules__ = {
780
783
  }
781
784
  function binaryProvenanceNote(provenance) {
782
785
  if ('pinned' === provenance) return '(pinned with --chromium-binary)';
783
- if ('system' === provenance) return '(system, not the managed default)';
784
- if ('snapshot' === provenance) return '(cached snapshot)';
785
786
  return '';
786
787
  }
787
788
  function runningInDevelopment(manifest, browser, message, browserVersionLine, updateSuffix, opts) {
@@ -820,7 +821,6 @@ var __webpack_modules__ = {
820
821
  }));
821
822
  const provenanceNote = binaryProvenanceNote(opts?.binaryProvenance);
822
823
  const browserLabel = provenanceNote ? `${baseBrowserLabel} ${provenanceNote}` : baseBrowserLabel;
823
- const binaryRowValue = provenanceNote ? collapseHomeDirInCardValue(String(opts?.binaryPath || '').trim()) : '';
824
824
  const cleanId = String(id || '').trim();
825
825
  const includeExtensionId = opts?.includeExtensionId !== false;
826
826
  const updateNotice = updateSuffix ? ` ${updateSuffix}` : '';
@@ -834,10 +834,6 @@ var __webpack_modules__ = {
834
834
  label: 'Browser',
835
835
  value: browserLabel
836
836
  },
837
- {
838
- label: 'Binary',
839
- value: binaryRowValue
840
- },
841
837
  {
842
838
  label: 'Extension',
843
839
  value: displayVersion ? `${displayName} ${displayVersion}` : displayName
@@ -1357,6 +1353,10 @@ var __webpack_modules__ = {
1357
1353
  if ('number' == typeof details?.browserPid && Number.isFinite(details.browserPid)) ready.browserPid = details.browserPid;
1358
1354
  const extensionId = String(details?.extensionId || '').trim();
1359
1355
  if (extensionId) ready.extensionId = extensionId;
1356
+ const binary = String(details?.binary || '').trim();
1357
+ if (binary) ready.binary = binary;
1358
+ const provenance = String(details?.binaryProvenance || '').trim();
1359
+ if (provenance) ready.binaryProvenance = provenance;
1360
1360
  node_fs__rspack_import_0.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
1361
1361
  } catch {}
1362
1362
  }
@@ -3240,6 +3240,8 @@ var __webpack_modules__ = {
3240
3240
  (0, ready_stamp.M)(launchOutputPath, {
3241
3241
  profilePath: chromiumConfig.find((flag)=>flag.startsWith('--user-data-dir='))?.slice('--user-data-dir='.length).replace(/^"|"$/g, ''),
3242
3242
  browserPid: child?.pid,
3243
+ binary: browserBinaryLocation ?? void 0,
3244
+ binaryProvenance,
3243
3245
  extensionId: launchOutputPath ? (0, banner.vy)(launchOutputPath) : void 0
3244
3246
  });
3245
3247
  const pipeStreams = usePipe && child?.stdio?.[3] && child?.stdio?.[4] ? {
@@ -5403,6 +5405,7 @@ var __webpack_modules__ = {
5403
5405
  var banner = __webpack_require__("./browsers/browsers-lib/banner.ts");
5404
5406
  var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
5405
5407
  var ready_message = __webpack_require__("./browsers/browsers-lib/ready-message.ts");
5408
+ var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
5406
5409
  function logSafariDryRun(converterCmd, xcodebuildCmd) {
5407
5410
  if ((0, messaging._o)()) (0, messaging._w)(messages.KP());
5408
5411
  (0, messaging._w)(messages.yc());
@@ -5568,6 +5571,26 @@ var __webpack_modules__ = {
5568
5571
  severity: 'ok'
5569
5572
  };
5570
5573
  }
5574
+ async function resolvePidForBundle(bundleId) {
5575
+ for(let attempt = 0; attempt < 10; attempt += 1){
5576
+ const pid = await new Promise((resolve)=>{
5577
+ const child = (0, external_node_child_process_.spawn)("osascript", [
5578
+ '-e',
5579
+ `tell application "System Events" to get unix id of first process whose bundle identifier is "${bundleId}"`
5580
+ ]);
5581
+ let out = '';
5582
+ child.stdout.on('data', (d)=>out += String(d));
5583
+ child.on('error', ()=>resolve(null));
5584
+ child.on('close', ()=>{
5585
+ const n = Number(String(out).trim());
5586
+ resolve(Number.isFinite(n) && n > 0 ? n : null);
5587
+ });
5588
+ });
5589
+ if (pid) return pid;
5590
+ await new Promise((r)=>setTimeout(r, 500));
5591
+ }
5592
+ return null;
5593
+ }
5571
5594
  async function announceSafariDevSession(host, config, appPath) {
5572
5595
  try {
5573
5596
  await (0, banner.ai)({
@@ -5586,6 +5609,7 @@ var __webpack_modules__ = {
5586
5609
  const config = (0, safari_config.F7)(compilation, host);
5587
5610
  const converterArgs = (0, safari_config.w7)(config);
5588
5611
  const xcodebuildArgs = (0, safari_config.vx)(config);
5612
+ const isSigned = Boolean(config.developmentTeam);
5589
5613
  if ('full' === mode && config.bundleIdDerived) logger.warn?.(messages.$q(config.bundleIdentifier));
5590
5614
  if (host.dryRun || isTestEnv()) {
5591
5615
  logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
@@ -5645,7 +5669,7 @@ var __webpack_modules__ = {
5645
5669
  }
5646
5670
  logger.info?.(messages.Rl(appPath));
5647
5671
  if (!config.open) {
5648
- logger.info?.(messages.aO(appPath, config.appName));
5672
+ logger.info?.(messages.aO(appPath, config.appName, isSigned));
5649
5673
  if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
5650
5674
  return describePackage(config);
5651
5675
  }
@@ -5658,7 +5682,15 @@ var __webpack_modules__ = {
5658
5682
  '-a',
5659
5683
  config.safariBinary
5660
5684
  ]);
5661
- logger.info?.(messages.fO(config.appName));
5685
+ const raisedBundleId = config.safariBinary ? 'com.apple.Safari' : config.bundleIdentifier;
5686
+ const browserPid = await resolvePidForBundle(raisedBundleId);
5687
+ if (browserPid) (0, ready_stamp.M)(config.extensionDir, {
5688
+ browserPid,
5689
+ binary: config.safariBinary || appPath,
5690
+ binaryProvenance: config.safariBinary ? 'pinned' : 'system',
5691
+ extensionId: `${config.bundleIdentifier}.Extension`
5692
+ });
5693
+ logger.info?.(messages.fO(config.appName, isSigned));
5662
5694
  if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
5663
5695
  else logger.info?.(messages.qY(config.appName));
5664
5696
  if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
@@ -5718,7 +5750,8 @@ var __webpack_modules__ = {
5718
5750
  macOsOnly: false !== host.macOsOnly,
5719
5751
  language: 'swift',
5720
5752
  open: !host.noOpen,
5721
- safariBinary: host.safariBinary
5753
+ safariBinary: host.safariBinary,
5754
+ developmentTeam: String(host.developmentTeam || '').trim() || void 0
5722
5755
  };
5723
5756
  }
5724
5757
  function composeConverterArgs(config) {
@@ -5752,7 +5785,7 @@ var __webpack_modules__ = {
5752
5785
  return node_path__rspack_import_1.join(derivedDataPath(config), 'Build', 'Products', 'Release', `${config.appName}.app`);
5753
5786
  }
5754
5787
  function composeXcodebuildArgs(config) {
5755
- return [
5788
+ const base = [
5756
5789
  '-project',
5757
5790
  xcodeProjectPath(config),
5758
5791
  '-scheme',
@@ -5760,7 +5793,18 @@ var __webpack_modules__ = {
5760
5793
  '-configuration',
5761
5794
  'Release',
5762
5795
  '-derivedDataPath',
5763
- derivedDataPath(config),
5796
+ derivedDataPath(config)
5797
+ ];
5798
+ const team = String(config.developmentTeam || '').trim();
5799
+ if (team) return [
5800
+ ...base,
5801
+ `DEVELOPMENT_TEAM=${team}`,
5802
+ 'CODE_SIGN_STYLE=Automatic',
5803
+ '-allowProvisioningUpdates',
5804
+ 'build'
5805
+ ];
5806
+ return [
5807
+ ...base,
5764
5808
  'CODE_SIGN_IDENTITY=-',
5765
5809
  'CODE_SIGNING_REQUIRED=NO',
5766
5810
  'CODE_SIGNING_ALLOWED=YES',
package/dist/cli.cjs CHANGED
@@ -707,8 +707,10 @@ var __webpack_modules__ = {
707
707
  function safariDefaultBundleIdNote(bundleId) {
708
708
  return `${getLoggingPrefix('warn')} The bundle id ${pintor__rspack_import_8_default().gray(bundleId)} is generated from the app name.\nEvery project built from the same source shares this id, and the first team to register it takes it.\nSet your own with ${pintor__rspack_import_8_default().blue('--bundle-id')} before your first submission.\nAfter a submission, a new id is a new extension with none of your users.`;
709
709
  }
710
- function safariOpenHint(appPath, appName) {
711
- return `${getLoggingPrefix('info')} Launch it once to register with Safari: ${pintor__rspack_import_8_default().blue('open')} ${pintor__rspack_import_8_default().underline(`"${appPath}"`)}\nThen enable ${pintor__rspack_import_8_default().yellow(appName)} via Safari ▸ Develop ▸ ${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} and Safari ▸ Settings ▸ Extensions.`;
710
+ function safariOpenHint(appPath, appName, signed) {
711
+ const launch = `${getLoggingPrefix('info')} Launch it once to register with Safari: ${pintor__rspack_import_8_default().blue('open')} ${pintor__rspack_import_8_default().underline(`"${appPath}"`)}\n`;
712
+ if (signed) return launch + `Then turn on ${pintor__rspack_import_8_default().yellow(appName)} in Safari ▸ Settings ▸ Extensions.`;
713
+ return launch + `Then enable ${pintor__rspack_import_8_default().yellow(appName)} via Safari ▸ Develop ▸ ` + `${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} and Safari ▸ Settings ▸ Extensions.`;
712
714
  }
713
715
  function safariDryRunNotBuilding() {
714
716
  return `${getLoggingPrefix('info')} Dry run: skip the Safari app build.`;
@@ -719,7 +721,8 @@ var __webpack_modules__ = {
719
721
  function safariDryRunXcodebuild(cmd) {
720
722
  return `${getLoggingPrefix('info')} xcodebuild: ${pintor__rspack_import_8_default().gray(cmd)}.`;
721
723
  }
722
- function safariNextSteps(appName) {
724
+ function safariNextSteps(appName, signed) {
725
+ if (signed) return `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_8_default().yellow(appName)} in Safari:\n ${pintor__rspack_import_8_default().gray('1.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_8_default().yellow(appName)}\n ${pintor__rspack_import_8_default().gray('→')} Signed with your team id, so this survives a restart.`;
723
726
  return `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_8_default().yellow(appName)} in Safari:\n ${pintor__rspack_import_8_default().gray('1.')} Safari ▸ Settings ▸ Advanced ▸ check ${pintor__rspack_import_8_default().yellow('“Show features for web developers”')}\n ${pintor__rspack_import_8_default().gray('2.')} Safari ▸ Develop ▸ ${pintor__rspack_import_8_default().yellow('Allow Unsigned Extensions')} ${pintor__rspack_import_8_default().gray('(resets each launch)')}\n ${pintor__rspack_import_8_default().gray('3.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_8_default().yellow(appName)}\n ${pintor__rspack_import_8_default().gray('→')} The app window that just opened can also take you there.`;
724
727
  }
725
728
  function safariRegistered(appName) {
@@ -781,8 +784,6 @@ var __webpack_modules__ = {
781
784
  }
782
785
  function binaryProvenanceNote(provenance) {
783
786
  if ('pinned' === provenance) return '(pinned with --chromium-binary)';
784
- if ('system' === provenance) return '(system, not the managed default)';
785
- if ('snapshot' === provenance) return '(cached snapshot)';
786
787
  return '';
787
788
  }
788
789
  function runningInDevelopment(manifest, browser, message, browserVersionLine, updateSuffix, opts) {
@@ -821,7 +822,6 @@ var __webpack_modules__ = {
821
822
  }));
822
823
  const provenanceNote = binaryProvenanceNote(opts?.binaryProvenance);
823
824
  const browserLabel = provenanceNote ? `${baseBrowserLabel} ${provenanceNote}` : baseBrowserLabel;
824
- const binaryRowValue = provenanceNote ? collapseHomeDirInCardValue(String(opts?.binaryPath || '').trim()) : '';
825
825
  const cleanId = String(id || '').trim();
826
826
  const includeExtensionId = opts?.includeExtensionId !== false;
827
827
  const updateNotice = updateSuffix ? ` ${updateSuffix}` : '';
@@ -835,10 +835,6 @@ var __webpack_modules__ = {
835
835
  label: 'Browser',
836
836
  value: browserLabel
837
837
  },
838
- {
839
- label: 'Binary',
840
- value: binaryRowValue
841
- },
842
838
  {
843
839
  label: 'Extension',
844
840
  value: displayVersion ? `${displayName} ${displayVersion}` : displayName
@@ -1358,6 +1354,10 @@ var __webpack_modules__ = {
1358
1354
  if ('number' == typeof details?.browserPid && Number.isFinite(details.browserPid)) ready.browserPid = details.browserPid;
1359
1355
  const extensionId = String(details?.extensionId || '').trim();
1360
1356
  if (extensionId) ready.extensionId = extensionId;
1357
+ const binary = String(details?.binary || '').trim();
1358
+ if (binary) ready.binary = binary;
1359
+ const provenance = String(details?.binaryProvenance || '').trim();
1360
+ if (provenance) ready.binaryProvenance = provenance;
1361
1361
  node_fs__rspack_import_0.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
1362
1362
  } catch {}
1363
1363
  }
@@ -3231,6 +3231,8 @@ var __webpack_modules__ = {
3231
3231
  (0, ready_stamp.M)(launchOutputPath, {
3232
3232
  profilePath: chromiumConfig.find((flag)=>flag.startsWith('--user-data-dir='))?.slice('--user-data-dir='.length).replace(/^"|"$/g, ''),
3233
3233
  browserPid: child?.pid,
3234
+ binary: browserBinaryLocation ?? void 0,
3235
+ binaryProvenance,
3234
3236
  extensionId: launchOutputPath ? (0, banner.vy)(launchOutputPath) : void 0
3235
3237
  });
3236
3238
  const pipeStreams = usePipe && child?.stdio?.[3] && child?.stdio?.[4] ? {
@@ -5394,6 +5396,7 @@ var __webpack_modules__ = {
5394
5396
  var banner = __webpack_require__("./browsers/browsers-lib/banner.ts");
5395
5397
  var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
5396
5398
  var ready_message = __webpack_require__("./browsers/browsers-lib/ready-message.ts");
5399
+ var ready_stamp = __webpack_require__("./browsers/browsers-lib/ready-stamp.ts");
5397
5400
  function logSafariDryRun(converterCmd, xcodebuildCmd) {
5398
5401
  if ((0, messaging._o)()) (0, messaging._w)(messages.KP());
5399
5402
  (0, messaging._w)(messages.yc());
@@ -5559,6 +5562,26 @@ var __webpack_modules__ = {
5559
5562
  severity: 'ok'
5560
5563
  };
5561
5564
  }
5565
+ async function resolvePidForBundle(bundleId) {
5566
+ for(let attempt = 0; attempt < 10; attempt += 1){
5567
+ const pid = await new Promise((resolve)=>{
5568
+ const child = (0, external_node_child_process_.spawn)("osascript", [
5569
+ '-e',
5570
+ `tell application "System Events" to get unix id of first process whose bundle identifier is "${bundleId}"`
5571
+ ]);
5572
+ let out = '';
5573
+ child.stdout.on('data', (d)=>out += String(d));
5574
+ child.on('error', ()=>resolve(null));
5575
+ child.on('close', ()=>{
5576
+ const n = Number(String(out).trim());
5577
+ resolve(Number.isFinite(n) && n > 0 ? n : null);
5578
+ });
5579
+ });
5580
+ if (pid) return pid;
5581
+ await new Promise((r)=>setTimeout(r, 500));
5582
+ }
5583
+ return null;
5584
+ }
5562
5585
  async function announceSafariDevSession(host, config, appPath) {
5563
5586
  try {
5564
5587
  await (0, banner.ai)({
@@ -5577,6 +5600,7 @@ var __webpack_modules__ = {
5577
5600
  const config = (0, safari_config.F7)(compilation, host);
5578
5601
  const converterArgs = (0, safari_config.w7)(config);
5579
5602
  const xcodebuildArgs = (0, safari_config.vx)(config);
5603
+ const isSigned = Boolean(config.developmentTeam);
5580
5604
  if ('full' === mode && config.bundleIdDerived) logger.warn?.(messages.$q(config.bundleIdentifier));
5581
5605
  if (host.dryRun || isTestEnv()) {
5582
5606
  logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
@@ -5636,7 +5660,7 @@ var __webpack_modules__ = {
5636
5660
  }
5637
5661
  logger.info?.(messages.Rl(appPath));
5638
5662
  if (!config.open) {
5639
- logger.info?.(messages.aO(appPath, config.appName));
5663
+ logger.info?.(messages.aO(appPath, config.appName, isSigned));
5640
5664
  if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
5641
5665
  return describePackage(config);
5642
5666
  }
@@ -5649,7 +5673,15 @@ var __webpack_modules__ = {
5649
5673
  '-a',
5650
5674
  config.safariBinary
5651
5675
  ]);
5652
- logger.info?.(messages.fO(config.appName));
5676
+ const raisedBundleId = config.safariBinary ? 'com.apple.Safari' : config.bundleIdentifier;
5677
+ const browserPid = await resolvePidForBundle(raisedBundleId);
5678
+ if (browserPid) (0, ready_stamp.M)(config.extensionDir, {
5679
+ browserPid,
5680
+ binary: config.safariBinary || appPath,
5681
+ binaryProvenance: config.safariBinary ? 'pinned' : 'system',
5682
+ extensionId: `${config.bundleIdentifier}.Extension`
5683
+ });
5684
+ logger.info?.(messages.fO(config.appName, isSigned));
5653
5685
  if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fd(config.appName));
5654
5686
  else logger.info?.(messages.qY(config.appName));
5655
5687
  if (host.announceDevReady) await announceSafariDevSession(host, config, appPath);
@@ -5709,7 +5741,8 @@ var __webpack_modules__ = {
5709
5741
  macOsOnly: false !== host.macOsOnly,
5710
5742
  language: 'swift',
5711
5743
  open: !host.noOpen,
5712
- safariBinary: host.safariBinary
5744
+ safariBinary: host.safariBinary,
5745
+ developmentTeam: String(host.developmentTeam || '').trim() || void 0
5713
5746
  };
5714
5747
  }
5715
5748
  function composeConverterArgs(config) {
@@ -5743,7 +5776,7 @@ var __webpack_modules__ = {
5743
5776
  return node_path__rspack_import_1.join(derivedDataPath(config), 'Build', 'Products', 'Release', `${config.appName}.app`);
5744
5777
  }
5745
5778
  function composeXcodebuildArgs(config) {
5746
- return [
5779
+ const base = [
5747
5780
  '-project',
5748
5781
  xcodeProjectPath(config),
5749
5782
  '-scheme',
@@ -5751,7 +5784,18 @@ var __webpack_modules__ = {
5751
5784
  '-configuration',
5752
5785
  'Release',
5753
5786
  '-derivedDataPath',
5754
- derivedDataPath(config),
5787
+ derivedDataPath(config)
5788
+ ];
5789
+ const team = String(config.developmentTeam || '').trim();
5790
+ if (team) return [
5791
+ ...base,
5792
+ `DEVELOPMENT_TEAM=${team}`,
5793
+ 'CODE_SIGN_STYLE=Automatic',
5794
+ '-allowProvisioningUpdates',
5795
+ 'build'
5796
+ ];
5797
+ return [
5798
+ ...base,
5755
5799
  'CODE_SIGN_IDENTITY=-',
5756
5800
  'CODE_SIGNING_REQUIRED=NO',
5757
5801
  'CODE_SIGNING_ALLOWED=YES',
@@ -8354,7 +8398,7 @@ Cross-browser compatibility
8354
8398
  return null;
8355
8399
  }
8356
8400
  function registerBuildCommand(program) {
8357
- program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option(`--browser <${BROWSER_TARGETS_HELP}>`, '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 })=>{
8401
+ program.command('build').arguments('[project-name]').usage('[path-to-remote-extension] [options]').description(commandDescriptions.build).option(`--browser <${BROWSER_TARGETS_HELP}>`, '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('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari treats as unsigned: the extension then needs Develop \u25b8 Allow Unsigned Extensions re-ticked on every launch. A signed build is listed and stays enabled across restarts').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--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 })=>{
8358
8402
  if (buildOptions.debug || buildOptions.author || buildOptions.authorMode) {
8359
8403
  process.env.EXTENSION_DEBUG = '1';
8360
8404
  process.env.EXTENSION_AUTHOR_MODE = 'true';
@@ -8387,6 +8431,10 @@ Cross-browser compatibility
8387
8431
  '--bundle-id',
8388
8432
  buildOptions.bundleId
8389
8433
  ],
8434
+ [
8435
+ '--development-team',
8436
+ buildOptions.developmentTeam
8437
+ ],
8390
8438
  [
8391
8439
  '--force-regenerate',
8392
8440
  buildOptions.forceRegenerate
@@ -8432,6 +8480,7 @@ Cross-browser compatibility
8432
8480
  mode,
8433
8481
  appName: buildOptions.appName,
8434
8482
  bundleId: buildOptions.bundleId,
8483
+ developmentTeam: buildOptions.developmentTeam,
8435
8484
  macOsOnly: buildOptions.macosOnly,
8436
8485
  forceRegenerate: buildOptions.forceRegenerate,
8437
8486
  safariPackager: safariPackagingEnabled ? (0, safari_packager.P)({
@@ -8941,7 +8990,7 @@ Cross-browser compatibility
8941
8990
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 8080;
8942
8991
  }
8943
8992
  function registerDevCommand(program) {
8944
- program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --no-reload emit a dev-mode dist without the content-script reload runtime; tabs need manual reload to see changes\n --wait wait for ready contract and exit; pair with --output json for machine output\n").option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only; no live reload)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').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` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
8993
+ program.command('dev').arguments('[project-path|remote-url]').usage('[project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --no-reload emit a dev-mode dist without the content-script reload runtime; tabs need manual reload to see changes\n --wait wait for ready contract and exit; pair with --output json for machine output\n").option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option(`-b, --browser <${BROWSER_TARGETS_HELP}>`, 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only; no live reload)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').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('--development-team <id>', 'sign the Safari app with an Apple Developer team id (safari targets only). Without it the build is ad-hoc signed, which Safari treats as unsigned: the extension then needs Develop \u25b8 Allow Unsigned Extensions re-ticked on every launch. A signed build is listed and stays enabled across restarts').option('--macos-only [boolean]', 'generate a macOS-only Safari Xcode project (safari targets only). Pass `false` for a universal macOS + iOS project. Defaults to `true`', parseOptionalBoolean).option('--force-regenerate', 'regenerate the Safari Xcode project even when up to date (safari targets only)').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`', parseOptionalBoolean).option('--no-polyfill', 'disable the cross-browser polyfill').option('--no-open', 'do not open the browser automatically (default: open)').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--host <host>', 'specify the host to bind the dev server to. Use 0.0.0.0 for Docker/devcontainers. Defaults to `127.0.0.1`').option('--public-host <host>', 'connectable host the browser (HMR + reload bridge) dials when it differs from the bind host (e.g. a remote/devcontainer). Defaults to the bind host, or 127.0.0.1 when bound to 0.0.0.0').option('--log-context <list>', '[experimental] comma-separated contexts to include (background,content,page,sidebar,popup,options,devtools). Use `all` to include all contexts (default)').option('--logs <off|error|warn|info|debug|trace|all>', '[experimental] minimum centralized logger level to display in terminal (default: off)').option('--log-format <pretty|json|ndjson>', '[experimental] output format for logger events. Defaults to `pretty`').option('--no-log-timestamps', 'disable ISO timestamps in pretty output').option('--no-log-color', 'disable color in pretty output').option('--log-url <pattern>', '[experimental] only show logs where event.url matches this substring or regex (/re/i)').option('--log-tab <id>', 'only show logs for a specific tabId (number)').option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--output <pretty|json>', 'result format. Use json for a schema-1 envelope on stdout').addOption(new external_commander_namespaceObject.Option('--wait-format <pretty|json>').hideHelp()).addOption(new external_commander_namespaceObject.Option('--debug', 'print maintainer diagnostics alongside normal output')).addOption(new external_commander_namespaceObject.Option('--author, --author-mode', 'deprecated alias for --debug').hideHelp()).option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (implies --allow-control, runs arbitrary code in a context, writes a 0600 session token)').option('--parent-pid <pid>', 'exit when the given process dies. For wrappers that spawn `extension dev`, so a leaked dev server can never outlive its owner').action(async (pathOrRemoteUrl, options, command)=>{
8945
8994
  const { browser = 'chromium', ...devOptions } = options;
8946
8995
  if (devOptions.debug || devOptions.author || devOptions.authorMode) {
8947
8996
  process.env.EXTENSION_DEBUG = '1';
@@ -8985,6 +9034,10 @@ Cross-browser compatibility
8985
9034
  '--bundle-id',
8986
9035
  opts.bundleId
8987
9036
  ],
9037
+ [
9038
+ '--development-team',
9039
+ opts.developmentTeam
9040
+ ],
8988
9041
  [
8989
9042
  '--force-regenerate',
8990
9043
  opts.forceRegenerate
@@ -9108,6 +9161,14 @@ Cross-browser compatibility
9108
9161
  }
9109
9162
  });
9110
9163
  }
9164
+ function binaryDetailSuffix(ready) {
9165
+ const binary = String(ready?.binary || '').trim();
9166
+ if (!binary) return '';
9167
+ const home = external_node_os_.homedir();
9168
+ const shown = home && binary.startsWith(home + external_node_path_default().sep) ? `~${binary.slice(home.length)}` : binary;
9169
+ const provenance = String(ready?.binaryProvenance || '').trim();
9170
+ return provenance ? `, ${shown} (${provenance})` : `, ${shown}`;
9171
+ }
9111
9172
  const CONNECT_TIMEOUT_MS = 5000;
9112
9173
  const PROBE_TIMEOUT_MS = 3000;
9113
9174
  const EXECUTOR_ATTACH_GRACE_MS = 10000;
@@ -9359,18 +9420,18 @@ Cross-browser compatibility
9359
9420
  if (ready.browserExitedAt) results.push({
9360
9421
  check: 'browser',
9361
9422
  status: 'fail',
9362
- detail: `browser exited at ${ready.browserExitedAt}${null != ready.browserExitCode ? ` (code ${ready.browserExitCode})` : ''} while the dev server kept running`,
9423
+ detail: `browser exited at ${ready.browserExitedAt}${null != ready.browserExitCode ? ` (code ${ready.browserExitCode})` : ''} while the dev server kept running${binaryDetailSuffix(ready)}`,
9363
9424
  remediation: 'Restart the dev session to relaunch the browser'
9364
9425
  });
9365
9426
  else if (null != ready.cdpPort) results.push({
9366
9427
  check: 'browser',
9367
9428
  status: 'pass',
9368
- detail: `browser running (cdpPort ${ready.cdpPort})`
9429
+ detail: `browser running (cdpPort ${ready.cdpPort})${binaryDetailSuffix(ready)}`
9369
9430
  });
9370
9431
  else results.push({
9371
9432
  check: 'browser',
9372
9433
  status: 'skip',
9373
- detail: 'browser liveness unknown (no cdpPort stamped yet and no exit recorded)',
9434
+ detail: `browser liveness unknown (no cdpPort stamped yet and no exit recorded)${binaryDetailSuffix(ready)}`,
9374
9435
  remediation: 'If the browser should be up, wait for launch to finish or restart the dev session'
9375
9436
  });
9376
9437
  return results;
@@ -72,11 +72,11 @@ export declare function safariOpening(target: string): string;
72
72
  export declare function safariToolFailed(tool: string, exitCode: number | null, outputTail: string): string;
73
73
  export declare function safariConverterWarnings(warnings: string[]): string;
74
74
  export declare function safariDefaultBundleIdNote(bundleId: string): string;
75
- export declare function safariOpenHint(appPath: string, appName: string): string;
75
+ export declare function safariOpenHint(appPath: string, appName: string, signed?: boolean): string;
76
76
  export declare function safariDryRunNotBuilding(): string;
77
77
  export declare function safariDryRunConverter(cmd: string): string;
78
78
  export declare function safariDryRunXcodebuild(cmd: string): string;
79
- export declare function safariNextSteps(appName: string): string;
79
+ export declare function safariNextSteps(appName: string, signed?: boolean): string;
80
80
  export declare function safariRegistered(appName: string): string;
81
81
  export declare function safariNotYetRegistered(appName: string): string;
82
82
  export declare function safariRebuilt(appName: string): string;
@@ -3,6 +3,8 @@ export declare function stampReadyBrowserLaunch(extensionOutputPath: string | un
3
3
  profilePath?: string;
4
4
  browserPid?: number;
5
5
  extensionId?: string;
6
+ binary?: string;
7
+ binaryProvenance?: 'managed' | 'pinned' | 'system' | 'snapshot';
6
8
  }): void;
7
9
  export declare function stampReadyExtensionId(extensionOutputPath: string | undefined, extensionId: string | undefined): void;
8
10
  export declare function stampReadyExtensionLoadRefused(extensionOutputPath: string | undefined, reason: string): void;
@@ -2,6 +2,7 @@ import type { PluginInterface } from '../browsers-types';
2
2
  export type SafariPluginLike = Pick<PluginInterface, 'extension' | 'noOpen' | 'instanceId' | 'dryRun'> & {
3
3
  browser: PluginInterface['browser'];
4
4
  safariBinary?: string;
5
+ developmentTeam?: string;
5
6
  appName?: string;
6
7
  bundleId?: string;
7
8
  macOsOnly?: boolean;
@@ -19,4 +20,5 @@ export interface SafariBuildConfig {
19
20
  language: 'swift' | 'objc';
20
21
  open: boolean;
21
22
  safariBinary?: string;
23
+ developmentTeam?: string;
22
24
  }
package/package.json CHANGED
@@ -38,7 +38,7 @@
38
38
  "extension": "./bin/extension.cjs"
39
39
  },
40
40
  "name": "extension",
41
- "version": "4.1.0",
41
+ "version": "4.1.2",
42
42
  "description": "The cross-browser extension framework. Build Chrome, Edge, Firefox, and Safari extensions with no build configuration.",
43
43
  "homepage": "https://extension.js.org/",
44
44
  "bugs": {
@@ -106,9 +106,9 @@
106
106
  "vivaldi-location2": "2.1.1",
107
107
  "waterfox-location": "2.1.1",
108
108
  "yandex-location": "2.1.1",
109
- "extension-create": "4.1.0",
110
- "extension-develop": "4.1.0",
111
- "extension-install": "4.1.0",
109
+ "extension-create": "4.1.2",
110
+ "extension-develop": "4.1.2",
111
+ "extension-install": "4.1.2",
112
112
  "commander": "^15.0.0",
113
113
  "pintor": "0.3.0",
114
114
  "semver": "^7.7.3",