extension 3.17.0 → 3.18.0
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/322.cjs +89 -11
- package/dist/browsers.cjs +263 -206
- package/dist/cli.cjs +1307 -410
- package/dist/extension/browsers/browsers-lib/messages.d.ts +17 -0
- package/dist/extension/browsers/browsers-lib/output-binaries-resolver.d.ts +1 -1
- package/dist/extension/browsers/browsers-lib/shared-utils.d.ts +1 -0
- package/dist/extension/browsers/browsers-lib/wsl-support.d.ts +3 -0
- package/dist/extension/browsers/browsers-types.d.ts +1 -1
- package/dist/extension/browsers/run-chromium/chromium-launch/process-handlers.d.ts +8 -1
- package/dist/extension/browsers/run-chromium/chromium-launch/wsl-support.d.ts +2 -9
- package/dist/extension/browsers/run-chromium/chromium-source-inspection/cdp-client.d.ts +6 -0
- package/dist/extension/browsers/run-chromium/chromium-source-inspection/cdp-extension-controller/logging.d.ts +0 -5
- package/dist/extension/browsers/run-chromium/chromium-source-inspection/index.d.ts +1 -0
- package/dist/extension/browsers/run-firefox/firefox-launch/binary-detector.d.ts +0 -4
- package/dist/extension/browsers/run-firefox/firefox-launch/process-handlers.d.ts +5 -1
- package/dist/extension/browsers/run-firefox/firefox-launch/wsl-support.d.ts +2 -4
- package/dist/extension/browsers/run-firefox/firefox-source-inspection/remote-firefox/evaluate.d.ts +1 -0
- package/dist/extension/browsers/run-safari/safari-launch/dry-run.d.ts +1 -0
- package/dist/extension/browsers/run-safari/safari-launch/index.d.ts +5 -0
- package/dist/extension/browsers/run-safari/safari-launch/safari-config.d.ts +16 -0
- package/dist/extension/browsers/run-safari/safari-launch/toolchain.d.ts +10 -0
- package/dist/extension/browsers/run-safari/safari-types.d.ts +18 -0
- package/dist/extension/commands/act.d.ts +6 -0
- package/dist/extension/commands/dev-wait.d.ts +2 -0
- package/dist/extension/commands/logs.d.ts +2 -0
- package/dist/extension/commands/publish.d.ts +15 -0
- package/dist/extension/helpers/extension-develop-runtime.d.ts +5 -0
- package/dist/extension/helpers/messages.d.ts +18 -1
- package/dist/extension/helpers/vendors.d.ts +2 -1
- package/package.json +5 -5
package/dist/cli.cjs
CHANGED
|
@@ -455,8 +455,10 @@ var __webpack_modules__ = {
|
|
|
455
455
|
$wD: ()=>bestEffortBannerPrintFailed,
|
|
456
456
|
A9Y: ()=>firefoxLaunchCalled,
|
|
457
457
|
AGJ: ()=>enhancedProcessManagementForceKill,
|
|
458
|
+
Ajw: ()=>safariDryRunXcodebuild,
|
|
458
459
|
CYH: ()=>devChromiumDebugPort,
|
|
459
460
|
Cny: ()=>chromeProcessError,
|
|
461
|
+
Csg: ()=>safariToolchainMissing,
|
|
460
462
|
DhR: ()=>invalidGeckoBinaryPath,
|
|
461
463
|
DyD: ()=>firefoxRdpRuntimeReinjectionResult,
|
|
462
464
|
E8B: ()=>chromiumDryRunFlags,
|
|
@@ -465,10 +467,12 @@ var __webpack_modules__ = {
|
|
|
465
467
|
F41: ()=>enhancedProcessManagementUncaughtException,
|
|
466
468
|
FFz: ()=>cdpClientFoundTargets,
|
|
467
469
|
FmF: ()=>connectionClosedError,
|
|
470
|
+
G3E: ()=>safariRequiresMacOS,
|
|
468
471
|
GqE: ()=>locatingBrowser,
|
|
469
472
|
HHw: ()=>messageWithoutSenderError,
|
|
470
473
|
IhA: ()=>browserNotInstalledError,
|
|
471
474
|
KK1: ()=>cdpProtocolEventHandlerError,
|
|
475
|
+
KP6: ()=>safariBuildCalled,
|
|
472
476
|
LDU: ()=>firefoxDryRunBinary,
|
|
473
477
|
M3V: ()=>cdpClientConnected,
|
|
474
478
|
MGf: ()=>prettyPuppeteerInstallGuidance,
|
|
@@ -479,6 +483,8 @@ var __webpack_modules__ = {
|
|
|
479
483
|
Nk3: ()=>chromiumDryRunNotLaunching,
|
|
480
484
|
Q0p: ()=>chromiumDryRunBinary,
|
|
481
485
|
Qiq: ()=>cdpClientLoadEventTimeout,
|
|
486
|
+
Qrh: ()=>safariFailed,
|
|
487
|
+
Rl_: ()=>safariBuilt,
|
|
482
488
|
Spm: ()=>invalidChromiumBinaryPath,
|
|
483
489
|
TFX: ()=>enhancedProcessManagementCleanupError,
|
|
484
490
|
Ter: ()=>cdpClientTargetWebSocketUrlStored,
|
|
@@ -487,43 +493,54 @@ var __webpack_modules__ = {
|
|
|
487
493
|
WVu: ()=>addonInstallError,
|
|
488
494
|
XHr: ()=>enhancedProcessManagementTerminating,
|
|
489
495
|
XOv: ()=>firefoxRdpClientTestingEvaluation,
|
|
496
|
+
Xiv: ()=>safariXcodeRequired,
|
|
490
497
|
Z5i: ()=>cdpClientExtensionInfoFailed,
|
|
491
498
|
_D4: ()=>browserLaunchError,
|
|
492
499
|
aIt: ()=>devChromeProfilePath,
|
|
493
500
|
bvI: ()=>cdpUnifiedExtensionLog,
|
|
494
501
|
cD1: ()=>targetActorHasActiveRequestError,
|
|
502
|
+
cFJ: ()=>safariRebuilt,
|
|
495
503
|
ccn: ()=>cdpClientExtensionReloadFailed,
|
|
496
504
|
dNY: ()=>firefoxNoBinaryArgsFound,
|
|
497
505
|
dkE: ()=>generalBrowserError,
|
|
498
506
|
dwQ: ()=>devFirefoxProfilePath,
|
|
499
507
|
eO_: ()=>firefoxInspectSourceNonFatal,
|
|
500
508
|
e_P: ()=>runningInDevelopment,
|
|
509
|
+
fLj: ()=>safariDryRunConverter,
|
|
510
|
+
fOJ: ()=>safariNextSteps,
|
|
501
511
|
fRy: ()=>cdpClientConnectionError,
|
|
512
|
+
fdc: ()=>safariRegistered,
|
|
513
|
+
fwU: ()=>safariOpening,
|
|
502
514
|
hE8: ()=>cdpClientConnectionClosed,
|
|
503
515
|
io8: ()=>emptyLine,
|
|
504
516
|
ivb: ()=>firefoxBinaryArgsExtracted,
|
|
505
517
|
jPE: ()=>rdpInvalidRequestPayload,
|
|
518
|
+
jRH: ()=>safariBuilding,
|
|
506
519
|
jk4: ()=>requireGeckoBinaryForGeckoBased,
|
|
507
520
|
k6b: ()=>firefoxDryRunConfig,
|
|
508
521
|
l9J: ()=>parsingPacketError,
|
|
509
522
|
lID: ()=>firefoxRdpReloadCapabilitySummary,
|
|
523
|
+
l_B: ()=>safariConverted,
|
|
510
524
|
nek: ()=>requireChromiumBinaryForChromiumBased,
|
|
511
525
|
nnm: ()=>chromeProcessExited,
|
|
512
526
|
p8m: ()=>cdpClientAttachedToTarget,
|
|
513
527
|
pdv: ()=>devFirefoxDebugPort,
|
|
514
528
|
qNy: ()=>messagingClientClosedError,
|
|
529
|
+
qYJ: ()=>safariNotYetRegistered,
|
|
515
530
|
qnG: ()=>firefoxDryRunNotLaunching,
|
|
516
531
|
r0s: ()=>cdpPendingRejectFailed,
|
|
517
532
|
sYQ: ()=>creatingUserProfile,
|
|
518
533
|
sew: ()=>separatorLine,
|
|
519
534
|
tUu: ()=>firefoxRdpClientFailedToGetMainHTML,
|
|
535
|
+
uKN: ()=>safariConverting,
|
|
520
536
|
wXK: ()=>cdpAutoAttachSetupFailed,
|
|
521
537
|
wk1: ()=>chromeFailedToSpawn,
|
|
522
538
|
xlM: ()=>cdpClientExtensionLoadFailed,
|
|
523
539
|
xxG: ()=>cdpFailedToHandleMessage,
|
|
524
540
|
xyq: ()=>chromeInitializingEnhancedReload,
|
|
525
541
|
y7j: ()=>skippingBrowserLaunchDueToCompileErrors,
|
|
526
|
-
yEr: ()=>cdpClientExtensionUnloadFailed
|
|
542
|
+
yEr: ()=>cdpClientExtensionUnloadFailed,
|
|
543
|
+
ycc: ()=>safariDryRunNotBuilding
|
|
527
544
|
});
|
|
528
545
|
var path__rspack_import_0 = __webpack_require__("path");
|
|
529
546
|
var fs__rspack_import_1 = __webpack_require__("fs");
|
|
@@ -787,6 +804,63 @@ var __webpack_modules__ = {
|
|
|
787
804
|
function firefoxDryRunConfig(cfg) {
|
|
788
805
|
return `${getLoggingPrefix('info')} [browser] Config: ${pintor__rspack_import_4_default().gray(cfg)}`;
|
|
789
806
|
}
|
|
807
|
+
function safariBuildCalled() {
|
|
808
|
+
return `${getLoggingPrefix('info')} Safari build requested.`;
|
|
809
|
+
}
|
|
810
|
+
function prettyPlatform(platform) {
|
|
811
|
+
if ('win32' === platform) return 'Windows';
|
|
812
|
+
if ('linux' === platform) return 'Linux';
|
|
813
|
+
return platform;
|
|
814
|
+
}
|
|
815
|
+
function safariRequiresMacOS(platform) {
|
|
816
|
+
return `${getLoggingPrefix('warn')} Safari extensions can only be built on macOS.\nDetected ${pintor__rspack_import_4_default().gray(prettyPlatform(platform))} — skipping Safari packaging. The web-extension build in ${pintor__rspack_import_4_default().yellow('dist/safari')} is still complete and can be packaged later on a Mac with Xcode.`;
|
|
817
|
+
}
|
|
818
|
+
function safariXcodeRequired(developerDir) {
|
|
819
|
+
const current = developerDir ? `${pintor__rspack_import_4_default().gray('Active toolchain:')} ${pintor__rspack_import_4_default().underline(developerDir)}` : `${pintor__rspack_import_4_default().gray('No active developer directory was found.')}`;
|
|
820
|
+
return `${getLoggingPrefix('error')} Safari packaging needs the full Xcode app (not just the Command Line Tools).\n${pintor__rspack_import_4_default().red('NOT FOUND')} ${pintor__rspack_import_4_default().underline('safari-web-extension-converter')}\n${current}\n\nTo enable Safari builds:\n- Install ${pintor__rspack_import_4_default().yellow('Xcode')} from the Mac App Store, then\n- Point the toolchain at it: ${pintor__rspack_import_4_default().blue('sudo xcode-select --switch')} ${pintor__rspack_import_4_default().gray('/Applications/Xcode.app')}\n- Finish setup once: ${pintor__rspack_import_4_default().blue('xcodebuild -runFirstLaunch')}\n\nPrefer to keep building now? Target another browser via ${pintor__rspack_import_4_default().blue('--browser')} ${pintor__rspack_import_4_default().gray('<chrome|edge|firefox>')}.`;
|
|
821
|
+
}
|
|
822
|
+
function safariToolchainMissing(tool) {
|
|
823
|
+
return `${getLoggingPrefix('error')} Safari packaging tool not found: ${pintor__rspack_import_4_default().underline(tool)}\nYour Xcode install looks incomplete. Try ${pintor__rspack_import_4_default().blue('xcodebuild -runFirstLaunch')}, or reinstall Xcode from the Mac App Store.`;
|
|
824
|
+
}
|
|
825
|
+
function safariConverting(extensionDir) {
|
|
826
|
+
return `${getLoggingPrefix('info')} Converting web extension into a Safari app project from ${pintor__rspack_import_4_default().underline(extensionDir)}`;
|
|
827
|
+
}
|
|
828
|
+
function safariConverted(projectDir) {
|
|
829
|
+
return `${getLoggingPrefix('success')} Generated Safari Xcode project at ${pintor__rspack_import_4_default().underline(projectDir)}`;
|
|
830
|
+
}
|
|
831
|
+
function safariBuilding(scheme) {
|
|
832
|
+
return `${getLoggingPrefix('info')} Building Safari app with xcodebuild (scheme: ${pintor__rspack_import_4_default().gray(scheme)})`;
|
|
833
|
+
}
|
|
834
|
+
function safariBuilt(appPath) {
|
|
835
|
+
return `${getLoggingPrefix('success')} Built Safari app at ${pintor__rspack_import_4_default().underline(appPath)}`;
|
|
836
|
+
}
|
|
837
|
+
function safariOpening(target) {
|
|
838
|
+
return `${getLoggingPrefix('info')} Opening ${pintor__rspack_import_4_default().underline(target)}`;
|
|
839
|
+
}
|
|
840
|
+
function safariFailed(error) {
|
|
841
|
+
return `${getLoggingPrefix('error')} Safari build failed:\n${pintor__rspack_import_4_default().red(errorDetail(error))}`;
|
|
842
|
+
}
|
|
843
|
+
function safariDryRunNotBuilding() {
|
|
844
|
+
return `${getLoggingPrefix('info')} [browser] Dry run: not building Safari app`;
|
|
845
|
+
}
|
|
846
|
+
function safariDryRunConverter(cmd) {
|
|
847
|
+
return `${getLoggingPrefix('info')} [browser] Converter: ${pintor__rspack_import_4_default().gray(cmd)}`;
|
|
848
|
+
}
|
|
849
|
+
function safariDryRunXcodebuild(cmd) {
|
|
850
|
+
return `${getLoggingPrefix('info')} [browser] xcodebuild: ${pintor__rspack_import_4_default().gray(cmd)}`;
|
|
851
|
+
}
|
|
852
|
+
function safariNextSteps(appName) {
|
|
853
|
+
return `${getLoggingPrefix('info')} One-time setup to load ${pintor__rspack_import_4_default().brightBlue(appName)} in Safari:\n ${pintor__rspack_import_4_default().gray('1.')} Safari ▸ Settings ▸ Advanced ▸ check ${pintor__rspack_import_4_default().yellow('“Show features for web developers”')}\n ${pintor__rspack_import_4_default().gray('2.')} Safari ▸ Develop ▸ ${pintor__rspack_import_4_default().yellow('Allow Unsigned Extensions')} ${pintor__rspack_import_4_default().gray('(resets each launch)')}\n ${pintor__rspack_import_4_default().gray('3.')} Safari ▸ Settings ▸ Extensions ▸ turn on ${pintor__rspack_import_4_default().yellow(appName)}\n ${pintor__rspack_import_4_default().gray('→')} The app window that just opened can also take you there.`;
|
|
854
|
+
}
|
|
855
|
+
function safariRegistered(appName) {
|
|
856
|
+
return `${getLoggingPrefix('success')} Safari recognizes ${pintor__rspack_import_4_default().brightBlue(appName)} — finish enabling it with the steps above.`;
|
|
857
|
+
}
|
|
858
|
+
function safariNotYetRegistered(appName) {
|
|
859
|
+
return `${getLoggingPrefix('warn')} Safari hasn't picked up ${pintor__rspack_import_4_default().brightBlue(appName)} yet. Open the app once, then check Safari ▸ Settings ▸ Extensions.`;
|
|
860
|
+
}
|
|
861
|
+
function safariRebuilt(appName) {
|
|
862
|
+
return `${getLoggingPrefix('success')} Rebuilt ${pintor__rspack_import_4_default().brightBlue(appName)} — reload the page (or toggle the extension) in Safari to see changes.`;
|
|
863
|
+
}
|
|
790
864
|
function cdpClientFoundTargets(count) {
|
|
791
865
|
return `${getLoggingPrefix('info')} Chrome found ${pintor__rspack_import_4_default().gray(count.toString())} targets`;
|
|
792
866
|
}
|
|
@@ -999,6 +1073,7 @@ var __webpack_modules__ = {
|
|
|
999
1073
|
function managedBrowserCacheEnv(cacheRoot, browser) {
|
|
1000
1074
|
const root = String(cacheRoot || '').trim();
|
|
1001
1075
|
if (!root) return {};
|
|
1076
|
+
if ('safari' === browser || 'webkit-based' === browser) return {};
|
|
1002
1077
|
if ('chrome' === browser) return {
|
|
1003
1078
|
PUPPETEER_CACHE_DIR: path__rspack_import_1.join(root, 'chrome', 'chrome')
|
|
1004
1079
|
};
|
|
@@ -1034,19 +1109,13 @@ var __webpack_modules__ = {
|
|
|
1034
1109
|
const versionDirs = entries.filter((entry)=>entry.isDirectory() && versionDirPattern.test(entry.name)).map((entry)=>path__rspack_import_1.join(root, entry.name));
|
|
1035
1110
|
for (const dir of versionDirs)candidateFiles.push(...buildCandidates(dir, browser));
|
|
1036
1111
|
} catch {}
|
|
1037
|
-
let matched = false;
|
|
1038
1112
|
for (const candidate of candidateFiles)try {
|
|
1039
|
-
if (candidate && fs__rspack_import_0.existsSync(candidate))
|
|
1040
|
-
matched = true;
|
|
1041
|
-
return candidate;
|
|
1042
|
-
}
|
|
1113
|
+
if (candidate && fs__rspack_import_0.existsSync(candidate)) return candidate;
|
|
1043
1114
|
} catch {}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
if (found) return found;
|
|
1049
|
-
}
|
|
1115
|
+
const names = executableNamesFor(browser);
|
|
1116
|
+
for (const root of scanRoots){
|
|
1117
|
+
const found = findExecutableUnder(root, names, 6);
|
|
1118
|
+
if (found) return found;
|
|
1050
1119
|
}
|
|
1051
1120
|
return null;
|
|
1052
1121
|
}
|
|
@@ -1224,7 +1293,8 @@ var __webpack_modules__ = {
|
|
|
1224
1293
|
RE: ()=>markManagedEphemeralProfile,
|
|
1225
1294
|
aY: ()=>findAvailablePortNear,
|
|
1226
1295
|
jl: ()=>deriveDebugPortWithInstance,
|
|
1227
|
-
ov: ()=>filterBrowserFlags
|
|
1296
|
+
ov: ()=>filterBrowserFlags,
|
|
1297
|
+
sW: ()=>removeManagedEphemeralProfile
|
|
1228
1298
|
});
|
|
1229
1299
|
var fs__rspack_import_0 = __webpack_require__("fs");
|
|
1230
1300
|
var os__rspack_import_1 = __webpack_require__("os");
|
|
@@ -1342,6 +1412,18 @@ var __webpack_modules__ = {
|
|
|
1342
1412
|
fs__rspack_import_0.writeFileSync(path__rspack_import_2.join(profilePath, MANAGED_EPHEMERAL_PROFILE_MARKER), 'managed-ephemeral-profile\n', 'utf8');
|
|
1343
1413
|
} catch {}
|
|
1344
1414
|
}
|
|
1415
|
+
function removeManagedEphemeralProfile(profilePath) {
|
|
1416
|
+
try {
|
|
1417
|
+
if (!profilePath) return;
|
|
1418
|
+
if ('dev' === path__rspack_import_2.basename(profilePath)) return;
|
|
1419
|
+
const markerPath = path__rspack_import_2.join(profilePath, MANAGED_EPHEMERAL_PROFILE_MARKER);
|
|
1420
|
+
if (!fs__rspack_import_0.existsSync(markerPath)) return;
|
|
1421
|
+
fs__rspack_import_0.rmSync(profilePath, {
|
|
1422
|
+
recursive: true,
|
|
1423
|
+
force: true
|
|
1424
|
+
});
|
|
1425
|
+
} catch {}
|
|
1426
|
+
}
|
|
1345
1427
|
function cleanupOldTempProfiles(baseDir, excludeBasename, maxAgeHours = 12) {
|
|
1346
1428
|
try {
|
|
1347
1429
|
if (!fs__rspack_import_0.existsSync(baseDir)) return;
|
|
@@ -1372,6 +1454,37 @@ var __webpack_modules__ = {
|
|
|
1372
1454
|
} catch {}
|
|
1373
1455
|
}
|
|
1374
1456
|
},
|
|
1457
|
+
"./browsers/browsers-lib/wsl-support.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1458
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
1459
|
+
EV: ()=>isWslEnv,
|
|
1460
|
+
f7: ()=>normalizeBinaryPathForWsl,
|
|
1461
|
+
qc: ()=>hasGuiDisplay
|
|
1462
|
+
});
|
|
1463
|
+
var os__rspack_import_0 = __webpack_require__("os");
|
|
1464
|
+
function isWslEnv() {
|
|
1465
|
+
if ('linux' !== process.platform) return false;
|
|
1466
|
+
const hasWslEnv = Boolean(String(process.env.WSL_DISTRO_NAME || '').trim() || String(process.env.WSL_INTEROP || '').trim() || String(process.env.WSLENV || '').trim());
|
|
1467
|
+
if (hasWslEnv) return true;
|
|
1468
|
+
return /microsoft/i.test(os__rspack_import_0.release());
|
|
1469
|
+
}
|
|
1470
|
+
function hasGuiDisplay() {
|
|
1471
|
+
const display = String(process.env.DISPLAY || '').trim();
|
|
1472
|
+
const waylandDisplay = String(process.env.WAYLAND_DISPLAY || '').trim();
|
|
1473
|
+
return display.length > 0 || waylandDisplay.length > 0;
|
|
1474
|
+
}
|
|
1475
|
+
function normalizeBinaryPathForWsl(input) {
|
|
1476
|
+
let value = String(input || '').trim();
|
|
1477
|
+
if (!value) return value;
|
|
1478
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
1479
|
+
if (!isWslEnv()) return value;
|
|
1480
|
+
if (/^[a-zA-Z]:[\\/]/.test(value)) {
|
|
1481
|
+
const drive = value[0].toLowerCase();
|
|
1482
|
+
const rest = value.slice(2).replace(/\\/g, '/').replace(/^\/+/, '');
|
|
1483
|
+
return `/mnt/${drive}/${rest}`;
|
|
1484
|
+
}
|
|
1485
|
+
return value;
|
|
1486
|
+
}
|
|
1487
|
+
},
|
|
1375
1488
|
"./browsers/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
1376
1489
|
__webpack_require__.d(__webpack_exports__, {
|
|
1377
1490
|
launchBrowser: ()=>launchBrowser
|
|
@@ -1897,7 +2010,9 @@ var __webpack_modules__ = {
|
|
|
1897
2010
|
'--disable-dev-shm-usage'
|
|
1898
2011
|
];
|
|
1899
2012
|
const baseFlags = [
|
|
1900
|
-
|
|
2013
|
+
...extensionsToLoad.length ? [
|
|
2014
|
+
`--load-extension=${extensionsToLoad.join()}`
|
|
2015
|
+
] : [],
|
|
1901
2016
|
...userProfilePath ? [
|
|
1902
2017
|
`--user-data-dir=${userProfilePath}`
|
|
1903
2018
|
] : [],
|
|
@@ -1914,53 +2029,81 @@ var __webpack_modules__ = {
|
|
|
1914
2029
|
return baseFlags;
|
|
1915
2030
|
}
|
|
1916
2031
|
var external_child_process_ = __webpack_require__("child_process");
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
2032
|
+
const activeInstances = new Set();
|
|
2033
|
+
let globalHandlersInstalled = false;
|
|
2034
|
+
function cleanupOne(instance) {
|
|
2035
|
+
if (instance.isCleaningUp) return;
|
|
2036
|
+
instance.isCleaningUp = true;
|
|
2037
|
+
const { browser, child, cleanupInstance } = instance;
|
|
2038
|
+
try {
|
|
2039
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.VkK(browser));
|
|
2040
|
+
if (child && !child.killed) {
|
|
2041
|
+
if ('win32' === process.platform) try {
|
|
2042
|
+
(0, external_child_process_.spawn)('taskkill', [
|
|
2043
|
+
'/PID',
|
|
2044
|
+
String(child.pid),
|
|
2045
|
+
'/T',
|
|
2046
|
+
'/F'
|
|
2047
|
+
], {
|
|
2048
|
+
stdio: 'ignore',
|
|
2049
|
+
windowsHide: true
|
|
2050
|
+
}).on('error', ()=>{});
|
|
2051
|
+
} catch {}
|
|
2052
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.XHr(browser));
|
|
2053
|
+
child.kill('SIGTERM');
|
|
2054
|
+
const killTimer = setTimeout(()=>{
|
|
2055
|
+
if (child && !child.killed) {
|
|
2056
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.AGJ(browser));
|
|
2057
|
+
child.kill('SIGKILL');
|
|
2058
|
+
}
|
|
2059
|
+
}, 5000);
|
|
2060
|
+
killTimer.unref?.();
|
|
1945
2061
|
}
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2062
|
+
cleanupInstance();
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
console.error(messages.TFX(browser, error));
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
function cleanupAll() {
|
|
2068
|
+
for (const instance of activeInstances)cleanupOne(instance);
|
|
2069
|
+
}
|
|
2070
|
+
function firstBrowserLabel() {
|
|
2071
|
+
for (const instance of activeInstances)return instance.browser;
|
|
2072
|
+
return 'chrome';
|
|
2073
|
+
}
|
|
2074
|
+
function installGlobalHandlersOnce() {
|
|
2075
|
+
if (globalHandlersInstalled) return;
|
|
2076
|
+
globalHandlersInstalled = true;
|
|
2077
|
+
process.on('SIGINT', cleanupAll);
|
|
2078
|
+
process.on('SIGTERM', cleanupAll);
|
|
2079
|
+
process.on('SIGHUP', cleanupAll);
|
|
2080
|
+
process.on('exit', cleanupAll);
|
|
1951
2081
|
process.on('uncaughtException', (error)=>{
|
|
1952
2082
|
if (isBenignSocketTeardown(error)) return;
|
|
1953
|
-
console.error(messages.F41(
|
|
1954
|
-
|
|
2083
|
+
console.error(messages.F41(firstBrowserLabel(), error));
|
|
2084
|
+
cleanupAll();
|
|
1955
2085
|
process.exit(1);
|
|
1956
2086
|
});
|
|
1957
2087
|
process.on('unhandledRejection', (reason)=>{
|
|
1958
2088
|
if (isBenignSocketTeardown(reason)) return;
|
|
1959
|
-
console.error(messages.N4O(
|
|
1960
|
-
|
|
2089
|
+
console.error(messages.N4O(firstBrowserLabel(), reason));
|
|
2090
|
+
cleanupAll();
|
|
1961
2091
|
process.exit(1);
|
|
1962
2092
|
});
|
|
1963
2093
|
}
|
|
2094
|
+
function setupProcessSignalHandlers(browser, child, cleanupInstance) {
|
|
2095
|
+
const instance = {
|
|
2096
|
+
browser,
|
|
2097
|
+
child,
|
|
2098
|
+
cleanupInstance,
|
|
2099
|
+
isCleaningUp: false
|
|
2100
|
+
};
|
|
2101
|
+
activeInstances.add(instance);
|
|
2102
|
+
installGlobalHandlersOnce();
|
|
2103
|
+
return ()=>{
|
|
2104
|
+
activeInstances.delete(instance);
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
1964
2107
|
const BENIGN_SOCKET_ERROR_CODES = new Set([
|
|
1965
2108
|
'ECONNRESET',
|
|
1966
2109
|
'EPIPE',
|
|
@@ -1980,18 +2123,7 @@ var __webpack_modules__ = {
|
|
|
1980
2123
|
}
|
|
1981
2124
|
var extension_output_path = __webpack_require__("./browsers/run-chromium/chromium-launch/extension-output-path.ts");
|
|
1982
2125
|
var manifest_readiness = __webpack_require__("./browsers/run-chromium/manifest-readiness.ts");
|
|
1983
|
-
var
|
|
1984
|
-
function isWslEnv() {
|
|
1985
|
-
if ('linux' !== process.platform) return false;
|
|
1986
|
-
const hasWslEnv = Boolean(String(process.env.WSL_DISTRO_NAME || '').trim() || String(process.env.WSL_INTEROP || '').trim() || String(process.env.WSLENV || '').trim());
|
|
1987
|
-
if (hasWslEnv) return true;
|
|
1988
|
-
return /microsoft/i.test(external_os_.release());
|
|
1989
|
-
}
|
|
1990
|
-
function hasGuiDisplay() {
|
|
1991
|
-
const display = String(process.env.DISPLAY || '').trim();
|
|
1992
|
-
const waylandDisplay = String(process.env.WAYLAND_DISPLAY || '').trim();
|
|
1993
|
-
return display.length > 0 || waylandDisplay.length > 0;
|
|
1994
|
-
}
|
|
2126
|
+
var wsl_support = __webpack_require__("./browsers/browsers-lib/wsl-support.ts");
|
|
1995
2127
|
const LINUX_BROWSER_PATHS = {
|
|
1996
2128
|
chrome: [
|
|
1997
2129
|
'/opt/google/chrome/chrome',
|
|
@@ -2014,7 +2146,7 @@ var __webpack_modules__ = {
|
|
|
2014
2146
|
]
|
|
2015
2147
|
};
|
|
2016
2148
|
function resolveWslLinuxBinary(browser) {
|
|
2017
|
-
if (!
|
|
2149
|
+
if (!(0, wsl_support.EV)() || !(0, wsl_support.qc)()) return null;
|
|
2018
2150
|
const candidates = LINUX_BROWSER_PATHS[browser] || LINUX_BROWSER_PATHS['chrome'];
|
|
2019
2151
|
for (const candidate of candidates)if (external_fs_.existsSync(candidate)) return candidate;
|
|
2020
2152
|
return null;
|
|
@@ -2036,26 +2168,14 @@ var __webpack_modules__ = {
|
|
|
2036
2168
|
}
|
|
2037
2169
|
function preferRealChromeBinary(binary) {
|
|
2038
2170
|
if (!binary) return binary || null;
|
|
2039
|
-
if (!
|
|
2171
|
+
if (!(0, wsl_support.EV)() || !(0, wsl_support.qc)()) return binary;
|
|
2040
2172
|
if (!looksLikeChromeWrapperScript(binary)) return binary;
|
|
2041
2173
|
const realBinary = '/opt/google/chrome/chrome';
|
|
2042
2174
|
if (external_fs_.existsSync(realBinary)) return realBinary;
|
|
2043
2175
|
return binary;
|
|
2044
2176
|
}
|
|
2045
|
-
function normalizeBinaryPathForWsl(input) {
|
|
2046
|
-
let value = String(input || '').trim();
|
|
2047
|
-
if (!value) return value;
|
|
2048
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
2049
|
-
if (!isWslEnv()) return value;
|
|
2050
|
-
if (/^[a-zA-Z]:[\\/]/.test(value)) {
|
|
2051
|
-
const drive = value[0].toLowerCase();
|
|
2052
|
-
const rest = value.slice(2).replace(/\\/g, '/').replace(/^\/+/, '');
|
|
2053
|
-
return `/mnt/${drive}/${rest}`;
|
|
2054
|
-
}
|
|
2055
|
-
return value;
|
|
2056
|
-
}
|
|
2057
2177
|
function resolveWslWindowsBinary(browser) {
|
|
2058
|
-
if (!
|
|
2178
|
+
if (!(0, wsl_support.EV)()) return null;
|
|
2059
2179
|
const chromeCandidates = [
|
|
2060
2180
|
'/mnt/c/Program Files/Google/Chrome/Application/chrome.exe',
|
|
2061
2181
|
'/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'
|
|
@@ -2106,7 +2226,7 @@ var __webpack_modules__ = {
|
|
|
2106
2226
|
try {
|
|
2107
2227
|
return await spawnOnce(binary);
|
|
2108
2228
|
} catch (error) {
|
|
2109
|
-
if (
|
|
2229
|
+
if ((0, wsl_support.EV)()) {
|
|
2110
2230
|
const fallback = resolveWslWindowsBinary(browser);
|
|
2111
2231
|
if (fallback && fallback !== binary) {
|
|
2112
2232
|
logger?.warn?.('[browser] WSL detected: retrying with Windows browser binary.');
|
|
@@ -2185,7 +2305,7 @@ var __webpack_modules__ = {
|
|
|
2185
2305
|
let printedGuidance = false;
|
|
2186
2306
|
const normalizePath = (p)=>{
|
|
2187
2307
|
if (!p) return null;
|
|
2188
|
-
const normalized =
|
|
2308
|
+
const normalized = (0, wsl_support.f7)(p);
|
|
2189
2309
|
return normalized || null;
|
|
2190
2310
|
};
|
|
2191
2311
|
const isUsableBinary = (p)=>Boolean(p && external_fs_.existsSync(p));
|
|
@@ -2214,7 +2334,7 @@ var __webpack_modules__ = {
|
|
|
2214
2334
|
};
|
|
2215
2335
|
browserBinaryLocation = resolveManagedBinary();
|
|
2216
2336
|
let skipDetection = Boolean(browserBinaryLocation);
|
|
2217
|
-
if (!browserBinaryLocation &&
|
|
2337
|
+
if (!browserBinaryLocation && (0, wsl_support.EV)()) {
|
|
2218
2338
|
const linuxFallback = resolveWslLinuxBinary(browser);
|
|
2219
2339
|
if (linuxFallback) {
|
|
2220
2340
|
browserBinaryLocation = linuxFallback;
|
|
@@ -2262,7 +2382,7 @@ var __webpack_modules__ = {
|
|
|
2262
2382
|
if (!located) throw new Error(getInstallGuidanceText('chrome'));
|
|
2263
2383
|
const normalized = normalizePath(located || null);
|
|
2264
2384
|
if (isUsableBinary(normalized)) {
|
|
2265
|
-
if (looksOfficialChromeBinaryPath(normalized) && !
|
|
2385
|
+
if (looksOfficialChromeBinaryPath(normalized) && !(0, wsl_support.EV)()) {
|
|
2266
2386
|
printInstallGuidance(getInstallGuidanceText('chrome'), browser);
|
|
2267
2387
|
return null;
|
|
2268
2388
|
}
|
|
@@ -2276,7 +2396,7 @@ var __webpack_modules__ = {
|
|
|
2276
2396
|
}) || null;
|
|
2277
2397
|
const normalized = normalizePath(candidate || null);
|
|
2278
2398
|
if (normalized) {
|
|
2279
|
-
if (looksOfficialChromeBinaryPath(normalized) && !
|
|
2399
|
+
if (looksOfficialChromeBinaryPath(normalized) && !(0, wsl_support.EV)()) {
|
|
2280
2400
|
printInstallGuidance(getInstallGuidanceText('chrome'), browser);
|
|
2281
2401
|
candidate = null;
|
|
2282
2402
|
}
|
|
@@ -2500,7 +2620,7 @@ var __webpack_modules__ = {
|
|
|
2500
2620
|
'pipe',
|
|
2501
2621
|
'pipe'
|
|
2502
2622
|
] : 'ignore';
|
|
2503
|
-
const normalizedBinary =
|
|
2623
|
+
const normalizedBinary = (0, wsl_support.f7)(binary);
|
|
2504
2624
|
try {
|
|
2505
2625
|
const child = await spawnChromiumProcess({
|
|
2506
2626
|
binary: normalizedBinary,
|
|
@@ -2510,13 +2630,17 @@ var __webpack_modules__ = {
|
|
|
2510
2630
|
logger: this.logger
|
|
2511
2631
|
});
|
|
2512
2632
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.logger.debug?.('[browser] Final Chrome flags:', launchArgs.join(' '));
|
|
2633
|
+
let disposeSignalHandlers;
|
|
2513
2634
|
child.on('close', (code)=>{
|
|
2514
2635
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.logger.info(messages.nnm(code || 0));
|
|
2636
|
+
disposeSignalHandlers?.();
|
|
2637
|
+
const userDataDir = launchArgs.find((arg)=>arg.startsWith('--user-data-dir='))?.slice('--user-data-dir='.length).replace(/^"|"$/g, '');
|
|
2638
|
+
shared_utils.sW(userDataDir);
|
|
2515
2639
|
});
|
|
2516
2640
|
child.on('error', (error)=>{
|
|
2517
2641
|
this.logger.error(messages.Cny(error));
|
|
2518
2642
|
});
|
|
2519
|
-
setupProcessSignalHandlers(this.options?.browser, child, ()=>{});
|
|
2643
|
+
disposeSignalHandlers = setupProcessSignalHandlers(this.options?.browser, child, ()=>{});
|
|
2520
2644
|
return child;
|
|
2521
2645
|
} catch (error) {
|
|
2522
2646
|
this.logger.error(messages.wk1(error));
|
|
@@ -2830,65 +2954,88 @@ var __webpack_modules__ = {
|
|
|
2830
2954
|
var ready_message = __webpack_require__("./browsers/browsers-lib/ready-message.ts");
|
|
2831
2955
|
var runtime_options = __webpack_require__("./browsers/browsers-lib/runtime-options.ts");
|
|
2832
2956
|
var external_child_process_ = __webpack_require__("child_process");
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
|
|
2863
|
-
} catch (error) {
|
|
2864
|
-
console.error(messages.TFX(browser, error));
|
|
2957
|
+
const activeInstances = new Set();
|
|
2958
|
+
let globalHandlersInstalled = false;
|
|
2959
|
+
async function attemptCleanup(instance) {
|
|
2960
|
+
if (instance.isCleaningUp) return;
|
|
2961
|
+
instance.isCleaningUp = true;
|
|
2962
|
+
const { browser, childRef, cleanupInstance } = instance;
|
|
2963
|
+
try {
|
|
2964
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.VkK(browser));
|
|
2965
|
+
const child = childRef();
|
|
2966
|
+
if (child && !child.killed) {
|
|
2967
|
+
if ('win32' === process.platform) try {
|
|
2968
|
+
(0, external_child_process_.spawn)('taskkill', [
|
|
2969
|
+
'/PID',
|
|
2970
|
+
String(child.pid),
|
|
2971
|
+
'/T',
|
|
2972
|
+
'/F'
|
|
2973
|
+
], {
|
|
2974
|
+
stdio: 'ignore',
|
|
2975
|
+
windowsHide: true
|
|
2976
|
+
}).on('error', ()=>{});
|
|
2977
|
+
} catch {}
|
|
2978
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.XHr(browser));
|
|
2979
|
+
child.kill('SIGTERM');
|
|
2980
|
+
const killTimer = setTimeout(()=>{
|
|
2981
|
+
if (child && !child.killed) {
|
|
2982
|
+
if ('true' === process.env.EXTENSION_AUTHOR_MODE) console.log(messages.AGJ(browser));
|
|
2983
|
+
child.kill('SIGKILL');
|
|
2984
|
+
}
|
|
2985
|
+
}, 5000);
|
|
2986
|
+
killTimer.unref?.();
|
|
2865
2987
|
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2988
|
+
await cleanupInstance();
|
|
2989
|
+
} catch (error) {
|
|
2990
|
+
console.error(messages.TFX(browser, error));
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
function cleanupAllInstances() {
|
|
2994
|
+
for (const instance of activeInstances)attemptCleanup(instance);
|
|
2995
|
+
}
|
|
2996
|
+
function firstBrowserLabel() {
|
|
2997
|
+
for (const instance of activeInstances)return instance.browser;
|
|
2998
|
+
return 'firefox';
|
|
2999
|
+
}
|
|
3000
|
+
function installGlobalHandlersOnce() {
|
|
3001
|
+
if (globalHandlersInstalled) return;
|
|
3002
|
+
globalHandlersInstalled = true;
|
|
3003
|
+
process.on('exit', cleanupAllInstances);
|
|
3004
|
+
process.on('SIGINT', cleanupAllInstances);
|
|
3005
|
+
process.on('SIGTERM', cleanupAllInstances);
|
|
3006
|
+
process.on('SIGHUP', cleanupAllInstances);
|
|
3007
|
+
process.on('SIGBREAK', cleanupAllInstances);
|
|
3008
|
+
process.on('beforeExit', cleanupAllInstances);
|
|
2879
3009
|
process.on('uncaughtException', async (error)=>{
|
|
2880
3010
|
if (isBenignSocketTeardown(error)) return;
|
|
2881
|
-
console.error(messages.F41(
|
|
2882
|
-
await
|
|
3011
|
+
console.error(messages.F41(firstBrowserLabel(), error));
|
|
3012
|
+
await Promise.all([
|
|
3013
|
+
...activeInstances
|
|
3014
|
+
].map((i)=>attemptCleanup(i)));
|
|
2883
3015
|
process.exit(1);
|
|
2884
3016
|
});
|
|
2885
3017
|
process.on('unhandledRejection', async (reason)=>{
|
|
2886
3018
|
if (isBenignSocketTeardown(reason)) return;
|
|
2887
|
-
console.error(messages.N4O(
|
|
2888
|
-
await
|
|
3019
|
+
console.error(messages.N4O(firstBrowserLabel(), reason));
|
|
3020
|
+
await Promise.all([
|
|
3021
|
+
...activeInstances
|
|
3022
|
+
].map((i)=>attemptCleanup(i)));
|
|
2889
3023
|
process.exit(1);
|
|
2890
3024
|
});
|
|
2891
3025
|
}
|
|
3026
|
+
function setupFirefoxProcessHandlers(browser, childRef, cleanupInstance) {
|
|
3027
|
+
const instance = {
|
|
3028
|
+
browser,
|
|
3029
|
+
childRef,
|
|
3030
|
+
cleanupInstance,
|
|
3031
|
+
isCleaningUp: false
|
|
3032
|
+
};
|
|
3033
|
+
activeInstances.add(instance);
|
|
3034
|
+
installGlobalHandlersOnce();
|
|
3035
|
+
return ()=>{
|
|
3036
|
+
activeInstances.delete(instance);
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
2892
3039
|
const BENIGN_SOCKET_ERROR_CODES = new Set([
|
|
2893
3040
|
'ECONNRESET',
|
|
2894
3041
|
'EPIPE',
|
|
@@ -2951,6 +3098,10 @@ var __webpack_modules__ = {
|
|
|
2951
3098
|
else obj[key] = value;
|
|
2952
3099
|
return obj;
|
|
2953
3100
|
}
|
|
3101
|
+
function rdpRequestTimeoutMs() {
|
|
3102
|
+
const raw = parseInt(String(process.env.EXTENSION_RDP_REQUEST_TIMEOUT_MS || ''), 10);
|
|
3103
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 30000;
|
|
3104
|
+
}
|
|
2954
3105
|
class RdpTransport extends external_events_default() {
|
|
2955
3106
|
async connect(port, host = '127.0.0.1') {
|
|
2956
3107
|
await new Promise((resolve, reject)=>{
|
|
@@ -2980,7 +3131,10 @@ var __webpack_modules__ = {
|
|
|
2980
3131
|
this.rejectAll(err);
|
|
2981
3132
|
}
|
|
2982
3133
|
rejectAll(error) {
|
|
2983
|
-
for (const
|
|
3134
|
+
for (const entry of this.active.values()){
|
|
3135
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
3136
|
+
entry.deferred.reject(error);
|
|
3137
|
+
}
|
|
2984
3138
|
this.active.clear();
|
|
2985
3139
|
for (const { deferred } of this.pending)deferred.reject(error);
|
|
2986
3140
|
this.pending = [];
|
|
@@ -3006,7 +3160,10 @@ var __webpack_modules__ = {
|
|
|
3006
3160
|
flush() {
|
|
3007
3161
|
this.pending = this.pending.filter(({ to, payload, deferred })=>{
|
|
3008
3162
|
if (this.active.has(to)) return true;
|
|
3009
|
-
if (!this.conn)
|
|
3163
|
+
if (!this.conn) {
|
|
3164
|
+
deferred.reject(new Error(messages.FmF('firefox')));
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3010
3167
|
try {
|
|
3011
3168
|
this.conn.write(buildRdpFrame(payload));
|
|
3012
3169
|
this.expectReply(to, deferred);
|
|
@@ -3018,7 +3175,19 @@ var __webpack_modules__ = {
|
|
|
3018
3175
|
}
|
|
3019
3176
|
expectReply(to, deferred) {
|
|
3020
3177
|
if (this.active.has(to)) throw new Error(messages.cD1('firefox', to));
|
|
3021
|
-
|
|
3178
|
+
const timeoutMs = rdpRequestTimeoutMs();
|
|
3179
|
+
const timer = setTimeout(()=>{
|
|
3180
|
+
const entry = this.active.get(to);
|
|
3181
|
+
if (!entry) return;
|
|
3182
|
+
this.active.delete(to);
|
|
3183
|
+
entry.deferred.reject(new Error(`RDP request to "${to}" timed out after ${timeoutMs}ms`));
|
|
3184
|
+
this.flush();
|
|
3185
|
+
}, timeoutMs);
|
|
3186
|
+
timer.unref?.();
|
|
3187
|
+
this.active.set(to, {
|
|
3188
|
+
deferred,
|
|
3189
|
+
timer
|
|
3190
|
+
});
|
|
3022
3191
|
}
|
|
3023
3192
|
onData(buf) {
|
|
3024
3193
|
this.incoming = Buffer.concat([
|
|
@@ -3041,17 +3210,19 @@ var __webpack_modules__ = {
|
|
|
3041
3210
|
}
|
|
3042
3211
|
handleMessage(message) {
|
|
3043
3212
|
if (!message.from) return void this.emit('error', new Error(messages.HHw('firefox', message)));
|
|
3044
|
-
const
|
|
3045
|
-
if (
|
|
3213
|
+
const entry = this.active.get(message.from);
|
|
3214
|
+
if (entry) {
|
|
3046
3215
|
this.active.delete(message.from);
|
|
3047
|
-
if (
|
|
3048
|
-
|
|
3216
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
3217
|
+
if (message.error) entry.deferred.reject(message);
|
|
3218
|
+
else entry.deferred.resolve(message);
|
|
3049
3219
|
this.flush();
|
|
3050
3220
|
return;
|
|
3051
3221
|
}
|
|
3052
3222
|
this.emit('message', message);
|
|
3053
3223
|
}
|
|
3054
3224
|
onEnd() {
|
|
3225
|
+
this.rejectAll(new Error(messages.qNy('firefox')));
|
|
3055
3226
|
this.emit('end');
|
|
3056
3227
|
}
|
|
3057
3228
|
onTimeout() {
|
|
@@ -3473,6 +3644,7 @@ var __webpack_modules__ = {
|
|
|
3473
3644
|
} catch {}
|
|
3474
3645
|
return merged;
|
|
3475
3646
|
}
|
|
3647
|
+
const GET_PAGE_HTML_WITH_SHADOW_EXPRESSION = "(() => { try { var selector = '#extension-root,[data-extension-root]:not([data-extension-root=\"extension-js-devtools\"])'; var serializeShadowRoot = function(shadowRoot, serializer) { if (!shadowRoot) return ''; var stylesheetCss = Array.from(shadowRoot.styleSheets || []).map(function(sheet) { try { return Array.from(sheet.cssRules || []).map(function(rule) { return String(rule.cssText || ''); }).join('\\n'); } catch (e) { return ''; } }).filter(Boolean).join('\\n'); var adoptedCss = Array.from(shadowRoot.adoptedStyleSheets || []).map(function(sheet) { try { return Array.from(sheet.cssRules || []).map(function(rule) { return String(rule.cssText || ''); }).join('\\n'); } catch (e) { return ''; } }).filter(Boolean).join('\\n'); var stylesheetMarkup = stylesheetCss ? '<style>' + stylesheetCss + '</style>' : ''; var adoptedMarkup = adoptedCss ? '<style>' + adoptedCss + '</style>' : ''; var childMarkup = Array.from(shadowRoot.childNodes || []).map(function(node) { try { if (node && node.nodeType === 1 && String(node.nodeName || '').toLowerCase() === 'style') { return '<style>' + String(node.textContent || '') + '</style>'; } return serializer.serializeToString(node); } catch (e) { return ''; } }).join(''); return stylesheetMarkup + adoptedMarkup + childMarkup; }; var cloned = document.documentElement.cloneNode(true); var clonedHosts = Array.from(cloned.querySelectorAll(selector)); var liveHosts = Array.from(document.querySelectorAll(selector)); if (!clonedHosts.length) { var body = cloned.querySelector('body') || cloned; var newRoot = document.createElement('div'); newRoot.id='extension-root'; body.appendChild(newRoot); clonedHosts = [newRoot]; } var s = new XMLSerializer(); for (var i = 0; i < clonedHosts.length; i++) { var host = clonedHosts[i]; var live = liveHosts[i]; var shadow = ''; try { if (live && live.shadowRoot) { shadow = serializeShadowRoot(live.shadowRoot, s); } } catch (e) {} try { host.innerHTML = shadow; } catch (e) {} } return String('<!DOCTYPE html>' + (cloned.outerHTML || document.documentElement.outerHTML)); } catch(e) { try { return String(document.documentElement.outerHTML); } catch(_) { return '' } } })()";
|
|
3476
3648
|
function messaging_client_define_property(obj, key, value) {
|
|
3477
3649
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
3478
3650
|
value: value,
|
|
@@ -3562,11 +3734,7 @@ var __webpack_modules__ = {
|
|
|
3562
3734
|
}
|
|
3563
3735
|
}
|
|
3564
3736
|
async addTab(url) {
|
|
3565
|
-
|
|
3566
|
-
return await addTab(this.transport, url);
|
|
3567
|
-
} catch (e) {
|
|
3568
|
-
throw e;
|
|
3569
|
-
}
|
|
3737
|
+
return addTab(this.transport, url);
|
|
3570
3738
|
}
|
|
3571
3739
|
async navigateViaScript(consoleActor, url) {
|
|
3572
3740
|
await navigateViaScript(this.transport, consoleActor, url);
|
|
@@ -3624,7 +3792,7 @@ var __webpack_modules__ = {
|
|
|
3624
3792
|
const shadowContent = await this.extractShadowContent(actorToUse);
|
|
3625
3793
|
if (!shadowContent) return mainHTML;
|
|
3626
3794
|
try {
|
|
3627
|
-
const mergedResp = await this.evaluateRaw(actorToUse,
|
|
3795
|
+
const mergedResp = await this.evaluateRaw(actorToUse, GET_PAGE_HTML_WITH_SHADOW_EXPRESSION);
|
|
3628
3796
|
const mergedHtml = await this.coerceResponseToString(actorToUse, mergedResp, {
|
|
3629
3797
|
fallbackToFullDocument: false
|
|
3630
3798
|
});
|
|
@@ -3736,7 +3904,8 @@ var __webpack_modules__ = {
|
|
|
3736
3904
|
console.log(JSON.stringify(event));
|
|
3737
3905
|
}
|
|
3738
3906
|
function resolveAddonDirectory(baseDir, inputPath) {
|
|
3739
|
-
let candidate = inputPath.
|
|
3907
|
+
let candidate = inputPath.trim();
|
|
3908
|
+
if (candidate.startsWith('"') && candidate.endsWith('"') || candidate.startsWith("'") && candidate.endsWith("'")) candidate = candidate.slice(1, -1);
|
|
3740
3909
|
if (!external_path_.isAbsolute(candidate)) candidate = external_path_.resolve(baseDir, candidate);
|
|
3741
3910
|
try {
|
|
3742
3911
|
const stat = external_fs_.statSync(candidate);
|
|
@@ -3978,7 +4147,7 @@ var __webpack_modules__ = {
|
|
|
3978
4147
|
const shadowContent = await client.extractShadowContent?.(descriptorActor);
|
|
3979
4148
|
if (!shadowContent) return mainHTML;
|
|
3980
4149
|
try {
|
|
3981
|
-
const mergedResp = await client.evaluateRaw(descriptorActor,
|
|
4150
|
+
const mergedResp = await client.evaluateRaw(descriptorActor, GET_PAGE_HTML_WITH_SHADOW_EXPRESSION);
|
|
3982
4151
|
const mergedHtml = await client.coerceResponseToString?.(descriptorActor, mergedResp, {
|
|
3983
4152
|
fallbackToFullDocument: false
|
|
3984
4153
|
});
|
|
@@ -4694,8 +4863,6 @@ var __webpack_modules__ = {
|
|
|
4694
4863
|
if (profilePath) parts.splice(1, 0, `--profile="${profilePath}"`);
|
|
4695
4864
|
return parts.join(' ');
|
|
4696
4865
|
}
|
|
4697
|
-
const external_util_namespaceObject = require("util");
|
|
4698
|
-
const execFile = (0, external_util_namespaceObject.promisify)(external_child_process_.execFile);
|
|
4699
4866
|
function parseFlatpakBinary(binary) {
|
|
4700
4867
|
if (!binary || !binary.startsWith('flatpak:')) return null;
|
|
4701
4868
|
const appId = binary.substring(8).trim();
|
|
@@ -4747,57 +4914,20 @@ var __webpack_modules__ = {
|
|
|
4747
4914
|
args
|
|
4748
4915
|
};
|
|
4749
4916
|
}
|
|
4750
|
-
static async validateFirefoxBinary(binaryPath) {
|
|
4751
|
-
try {
|
|
4752
|
-
const { stdout } = await execFile(binaryPath, [
|
|
4753
|
-
'--version'
|
|
4754
|
-
]);
|
|
4755
|
-
const version = stdout.trim();
|
|
4756
|
-
return {
|
|
4757
|
-
version,
|
|
4758
|
-
path: binaryPath
|
|
4759
|
-
};
|
|
4760
|
-
} catch (error) {
|
|
4761
|
-
throw new Error(`Failed to validate Firefox binary: ${error}`);
|
|
4762
|
-
}
|
|
4763
|
-
}
|
|
4764
|
-
}
|
|
4765
|
-
var external_os_ = __webpack_require__("os");
|
|
4766
|
-
function isWslEnv() {
|
|
4767
|
-
if ('linux' !== process.platform) return false;
|
|
4768
|
-
const hasWslEnv = Boolean(String(process.env.WSL_DISTRO_NAME || '').trim() || String(process.env.WSL_INTEROP || '').trim() || String(process.env.WSLENV || '').trim());
|
|
4769
|
-
if (hasWslEnv) return true;
|
|
4770
|
-
return /microsoft/i.test(external_os_.release());
|
|
4771
|
-
}
|
|
4772
|
-
function hasGuiDisplay() {
|
|
4773
|
-
const display = String(process.env.DISPLAY || '').trim();
|
|
4774
|
-
const waylandDisplay = String(process.env.WAYLAND_DISPLAY || '').trim();
|
|
4775
|
-
return display.length > 0 || waylandDisplay.length > 0;
|
|
4776
4917
|
}
|
|
4918
|
+
var wsl_support = __webpack_require__("./browsers/browsers-lib/wsl-support.ts");
|
|
4777
4919
|
const LINUX_FIREFOX_PATHS = [
|
|
4778
4920
|
'/usr/bin/firefox',
|
|
4779
4921
|
'/snap/bin/firefox',
|
|
4780
4922
|
'/opt/firefox/firefox'
|
|
4781
4923
|
];
|
|
4782
4924
|
function resolveWslLinuxBinary() {
|
|
4783
|
-
if (!
|
|
4925
|
+
if (!(0, wsl_support.EV)() || !(0, wsl_support.qc)()) return null;
|
|
4784
4926
|
for (const candidate of LINUX_FIREFOX_PATHS)if (external_fs_.existsSync(candidate)) return candidate;
|
|
4785
4927
|
return null;
|
|
4786
4928
|
}
|
|
4787
|
-
function normalizeBinaryPathForWsl(input) {
|
|
4788
|
-
let value = String(input || '').trim();
|
|
4789
|
-
if (!value) return value;
|
|
4790
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
4791
|
-
if (!isWslEnv()) return value;
|
|
4792
|
-
if (/^[a-zA-Z]:[\\/]/.test(value)) {
|
|
4793
|
-
const drive = value[0].toLowerCase();
|
|
4794
|
-
const rest = value.slice(2).replace(/\\/g, '/').replace(/^\/+/, '');
|
|
4795
|
-
return `/mnt/${drive}/${rest}`;
|
|
4796
|
-
}
|
|
4797
|
-
return value;
|
|
4798
|
-
}
|
|
4799
4929
|
function resolveWslWindowsBinary() {
|
|
4800
|
-
if (!
|
|
4930
|
+
if (!(0, wsl_support.EV)()) return null;
|
|
4801
4931
|
const candidates = [
|
|
4802
4932
|
'/mnt/c/Program Files/Mozilla Firefox/firefox.exe',
|
|
4803
4933
|
'/mnt/c/Program Files (x86)/Mozilla Firefox/firefox.exe'
|
|
@@ -4838,7 +4968,7 @@ var __webpack_modules__ = {
|
|
|
4838
4968
|
try {
|
|
4839
4969
|
return await spawnOnce(binary);
|
|
4840
4970
|
} catch (error) {
|
|
4841
|
-
if (
|
|
4971
|
+
if ((0, wsl_support.EV)() && fallbackBinary && fallbackBinary !== binary) {
|
|
4842
4972
|
logger?.warn?.('[browser] WSL detected: retrying with Windows Firefox binary.');
|
|
4843
4973
|
return await spawnOnce(fallbackBinary);
|
|
4844
4974
|
}
|
|
@@ -4899,7 +5029,7 @@ var __webpack_modules__ = {
|
|
|
4899
5029
|
if (compilationErrors.length > 0) return void this.ctx.logger?.info?.(messages.y7j());
|
|
4900
5030
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.ctx.logger?.info?.(messages.A9Y());
|
|
4901
5031
|
const normalizePath = (value)=>{
|
|
4902
|
-
const normalized = value ?
|
|
5032
|
+
const normalized = value ? (0, wsl_support.f7)(value) : null;
|
|
4903
5033
|
return normalized && external_fs_.existsSync(normalized) ? normalized : null;
|
|
4904
5034
|
};
|
|
4905
5035
|
const resolveManagedBinary = ()=>normalizePath((0, output_binaries_resolver.kI)(compilation, 'firefox') || null);
|
|
@@ -4930,7 +5060,7 @@ var __webpack_modules__ = {
|
|
|
4930
5060
|
let browserBinaryLocation = resolveManagedBinary();
|
|
4931
5061
|
let skipDetection = Boolean(browserBinaryLocation);
|
|
4932
5062
|
const engineBased = 'gecko-based' === this.host.browser || 'firefox-based' === this.host.browser;
|
|
4933
|
-
if (!browserBinaryLocation && !engineBased &&
|
|
5063
|
+
if (!browserBinaryLocation && !engineBased && (0, wsl_support.EV)()) {
|
|
4934
5064
|
const linuxFallback = resolveWslLinuxBinary();
|
|
4935
5065
|
if (linuxFallback) {
|
|
4936
5066
|
browserBinaryLocation = linuxFallback;
|
|
@@ -4987,17 +5117,12 @@ var __webpack_modules__ = {
|
|
|
4987
5117
|
}
|
|
4988
5118
|
}
|
|
4989
5119
|
const binaryPath = browserBinaryLocation;
|
|
4990
|
-
const wslFallbackBinary =
|
|
4991
|
-
if (!isFlatpak) {
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
} catch {}
|
|
4997
|
-
try {
|
|
4998
|
-
await FirefoxBinaryDetector.validateFirefoxBinary(binaryPath);
|
|
4999
|
-
} catch {}
|
|
5000
|
-
}
|
|
5120
|
+
const wslFallbackBinary = (0, wsl_support.EV)() && !engineBased ? resolveWslFallback() : null;
|
|
5121
|
+
if (!isFlatpak) try {
|
|
5122
|
+
this.host.browserVersionLine = (0, external_firefox_location2_.getFirefoxVersion)(binaryPath, {
|
|
5123
|
+
allowExec: true
|
|
5124
|
+
}) || '';
|
|
5125
|
+
} catch {}
|
|
5001
5126
|
const extensionsToLoad = (0, runtime_options.fT)(this.host.extension);
|
|
5002
5127
|
(0, runtime_options.sl)(extensionsToLoad, 'function' == typeof this.ctx.setExtensionRoot ? this.ctx.setExtensionRoot : void 0);
|
|
5003
5128
|
const desiredDebugPort = (0, shared_utils.jl)(this.host.port, this.host.instanceId);
|
|
@@ -5026,6 +5151,9 @@ var __webpack_modules__ = {
|
|
|
5026
5151
|
const profilePath = profileMatch[1];
|
|
5027
5152
|
const { binary, args } = FirefoxBinaryDetector.generateFirefoxArgs(binaryPath, profilePath, debugPort, firefoxArgs);
|
|
5028
5153
|
this.child = await this.spawnFirefoxChild(binary, args, wslFallbackBinary);
|
|
5154
|
+
this.child.on('close', ()=>{
|
|
5155
|
+
(0, shared_utils.sW)(profilePath);
|
|
5156
|
+
});
|
|
5029
5157
|
this.wireChildLifecycle();
|
|
5030
5158
|
const ctrl = await setupRdpAfterLaunch(this.host, compilation, debugPort);
|
|
5031
5159
|
this.host.rdpController = ctrl;
|
|
@@ -5093,6 +5221,7 @@ var __webpack_modules__ = {
|
|
|
5093
5221
|
if (process.env.VITEST || process.env.VITEST_WORKER_ID) throw new Error('Firefox startup timed out');
|
|
5094
5222
|
process.exit(1);
|
|
5095
5223
|
});
|
|
5224
|
+
let disposeProcessHandlers;
|
|
5096
5225
|
child.on('close', (_code)=>{
|
|
5097
5226
|
if (this.watchTimeout) {
|
|
5098
5227
|
clearTimeout(this.watchTimeout);
|
|
@@ -5102,9 +5231,10 @@ var __webpack_modules__ = {
|
|
|
5102
5231
|
this.cleanupInstance().catch((err)=>{
|
|
5103
5232
|
if ('true' === process.env.EXTENSION_AUTHOR_MODE) this.ctx.logger?.error?.(`[browser] Cleanup error on child close: ${err?.message || err}`);
|
|
5104
5233
|
});
|
|
5234
|
+
disposeProcessHandlers?.();
|
|
5105
5235
|
});
|
|
5106
5236
|
this.pipeChildOutput(child);
|
|
5107
|
-
setupFirefoxProcessHandlers(this.host.browser, ()=>this.child, ()=>this.cleanupInstance());
|
|
5237
|
+
disposeProcessHandlers = setupFirefoxProcessHandlers(this.host.browser, ()=>this.child, ()=>this.cleanupInstance());
|
|
5108
5238
|
}
|
|
5109
5239
|
async cleanupInstance() {
|
|
5110
5240
|
try {
|
|
@@ -5112,11 +5242,12 @@ var __webpack_modules__ = {
|
|
|
5112
5242
|
try {
|
|
5113
5243
|
this.child.kill('SIGTERM');
|
|
5114
5244
|
} catch {}
|
|
5115
|
-
setTimeout(()=>{
|
|
5245
|
+
const killTimer = setTimeout(()=>{
|
|
5116
5246
|
try {
|
|
5117
5247
|
if (this.child && !this.child.killed) this.child.kill('SIGKILL');
|
|
5118
5248
|
} catch {}
|
|
5119
5249
|
}, 2000);
|
|
5250
|
+
killTimer.unref?.();
|
|
5120
5251
|
}
|
|
5121
5252
|
} catch {}
|
|
5122
5253
|
}
|
|
@@ -5637,7 +5768,7 @@ var __webpack_modules__ = {
|
|
|
5637
5768
|
if ('info' === type) return external_pintor_default().gray('⏵⏵⏵');
|
|
5638
5769
|
return external_pintor_default().green('⏵⏵⏵');
|
|
5639
5770
|
}
|
|
5640
|
-
const
|
|
5771
|
+
const messages_code = (text)=>external_pintor_default().blue(text);
|
|
5641
5772
|
const messages_arg = (text)=>external_pintor_default().gray(text);
|
|
5642
5773
|
const fmt = {
|
|
5643
5774
|
heading: (title)=>external_pintor_default().underline(external_pintor_default().blue(title)),
|
|
@@ -5667,6 +5798,13 @@ var __webpack_modules__ = {
|
|
|
5667
5798
|
start: 'Builds and starts the extension in production mode',
|
|
5668
5799
|
preview: 'Previews the extension in production mode without building',
|
|
5669
5800
|
build: 'Builds the extension for packaging/distribution',
|
|
5801
|
+
logs: 'Prints or streams logs from every context of a running dev session',
|
|
5802
|
+
eval: 'Evaluates an expression in a running extension context (requires --allow-eval)',
|
|
5803
|
+
storage: 'Reads or writes chrome.storage in a running extension (requires --allow-control)',
|
|
5804
|
+
reload: 'Reloads a running extension or tab (requires --allow-control)',
|
|
5805
|
+
open: 'Opens an extension surface — popup, options, or sidebar (requires --allow-control)',
|
|
5806
|
+
inspect: 'Inspects a page/content DOM via the agent bridge (CDP-free; requires --allow-control)',
|
|
5807
|
+
publish: 'Publishes to extension.dev and prints a shareable URL (requires EXTENSION_DEV_TOKEN)',
|
|
5670
5808
|
install: 'Installs a managed browser binary into Extension.js cache',
|
|
5671
5809
|
uninstall: 'Removes managed browser binaries from Extension.js cache',
|
|
5672
5810
|
telemetry: 'Manage anonymous telemetry consent (enable, disable, or show status)'
|
|
@@ -5693,111 +5831,132 @@ Usage: extension [command] [options]
|
|
|
5693
5831
|
|
|
5694
5832
|
Notes
|
|
5695
5833
|
- All high-level commands offer their own \`--help\` with usage and flag lists.
|
|
5696
|
-
- Telemetry is anonymous and privacy-safe by default; see ${
|
|
5834
|
+
- Telemetry is anonymous and privacy-safe by default; see ${messages_code('docs/TELEMETRY.md')} for the full contract.
|
|
5697
5835
|
|
|
5698
5836
|
Example
|
|
5699
|
-
- ${
|
|
5837
|
+
- ${messages_code('extension create --help')} outputs information about the "create" command.
|
|
5700
5838
|
|
|
5701
5839
|
Available Commands
|
|
5702
|
-
- ${
|
|
5840
|
+
- ${messages_code('extension create ' + messages_arg('<project-name|project-path>'))}
|
|
5703
5841
|
${commandDescriptions.create}
|
|
5704
5842
|
|
|
5705
|
-
- ${
|
|
5843
|
+
- ${messages_code('extension dev ' + messages_arg('[project-path|remote-url]'))}
|
|
5706
5844
|
${commandDescriptions.dev}
|
|
5707
5845
|
|
|
5708
|
-
- ${
|
|
5846
|
+
- ${messages_code('extension start ' + messages_arg('[project-path|remote-url]'))}
|
|
5709
5847
|
${commandDescriptions.start}
|
|
5710
5848
|
|
|
5711
|
-
- ${
|
|
5849
|
+
- ${messages_code('extension preview ' + messages_arg('[project-path|remote-url]'))}
|
|
5712
5850
|
${commandDescriptions.preview}
|
|
5713
5851
|
|
|
5714
|
-
- ${
|
|
5852
|
+
- ${messages_code('extension build ' + messages_arg('[project-path|remote-url]'))}
|
|
5715
5853
|
${commandDescriptions.build}
|
|
5716
5854
|
|
|
5717
|
-
- ${
|
|
5855
|
+
- ${messages_code('extension logs ' + messages_arg('[project-path]'))}
|
|
5856
|
+
${commandDescriptions.logs}
|
|
5857
|
+
|
|
5858
|
+
- ${messages_code('extension eval ' + messages_arg('<expression> [project-path]'))}
|
|
5859
|
+
${commandDescriptions.eval}
|
|
5860
|
+
|
|
5861
|
+
- ${messages_code('extension storage ' + messages_arg('<get|set> [key] [value] [project-path]'))}
|
|
5862
|
+
${commandDescriptions.storage}
|
|
5863
|
+
|
|
5864
|
+
- ${messages_code('extension reload ' + messages_arg('[project-path]'))}
|
|
5865
|
+
${commandDescriptions.reload}
|
|
5866
|
+
|
|
5867
|
+
- ${messages_code('extension open ' + messages_arg('<popup|options|sidebar> [project-path]'))}
|
|
5868
|
+
${commandDescriptions.open}
|
|
5869
|
+
|
|
5870
|
+
- ${messages_code('extension inspect ' + messages_arg('[project-path] --tab <id>'))}
|
|
5871
|
+
${commandDescriptions.inspect}
|
|
5872
|
+
|
|
5873
|
+
- ${messages_code('extension publish ' + messages_arg('[project-path]'))}
|
|
5874
|
+
${commandDescriptions.publish}
|
|
5875
|
+
|
|
5876
|
+
- ${messages_code('extension install ' + messages_arg('<chrome|chromium|edge|firefox>'))}
|
|
5718
5877
|
${commandDescriptions.install}
|
|
5719
5878
|
|
|
5720
|
-
- ${
|
|
5721
|
-
Install multiple browsers, browser families, or ${
|
|
5879
|
+
- ${messages_code('extension install ' + messages_arg('--browser <chrome|chromium|edge|firefox|chromium-based|gecko-based|firefox-based|all>'))}
|
|
5880
|
+
Install multiple browsers, browser families, or ${messages_code('all')}
|
|
5722
5881
|
|
|
5723
|
-
- ${
|
|
5724
|
-
Prints the managed browser cache root (or browser install path(s) when a browser name or ${
|
|
5882
|
+
- ${messages_code('extension install --where')}
|
|
5883
|
+
Prints the managed browser cache root (or browser install path(s) when a browser name or ${messages_code('--browser')} is provided)
|
|
5725
5884
|
|
|
5726
|
-
- ${
|
|
5885
|
+
- ${messages_code('extension uninstall ' + messages_arg('<chrome|chromium|edge|firefox> | --all'))}
|
|
5727
5886
|
${commandDescriptions.uninstall}
|
|
5728
5887
|
|
|
5729
|
-
- ${
|
|
5888
|
+
- ${messages_code('extension uninstall --where')}
|
|
5730
5889
|
Prints the managed browser cache root (or browser install path(s) when --browser/--all is provided)
|
|
5731
5890
|
|
|
5732
|
-
- ${
|
|
5891
|
+
- ${messages_code('extension telemetry ' + messages_arg('<enable|disable|status>'))}
|
|
5733
5892
|
${commandDescriptions.telemetry}
|
|
5734
5893
|
|
|
5735
5894
|
Common Options
|
|
5736
|
-
- ${
|
|
5737
|
-
- ${
|
|
5738
|
-
- ${
|
|
5739
|
-
- ${
|
|
5740
|
-
- ${
|
|
5741
|
-
- ${
|
|
5742
|
-
- ${
|
|
5743
|
-
- ${
|
|
5744
|
-
- ${
|
|
5745
|
-
- ${
|
|
5746
|
-
- ${
|
|
5895
|
+
- ${messages_code('--browser')} ${messages_arg('<chrome|edge|firefox|chromium|chromium-based|gecko-based|firefox-based>')} Target browser/engine (default: chromium)
|
|
5896
|
+
- ${messages_code('--profile')} ${messages_arg('<path|boolean>')} Browser profile configuration
|
|
5897
|
+
- ${messages_code('--polyfill')} ${messages_arg('[boolean]')} Enable/disable cross-browser polyfill
|
|
5898
|
+
- ${messages_code('--no-telemetry')} Disable anonymous telemetry for this run (persistent toggle: ${messages_code('extension telemetry disable')}, or ${messages_code('EXTENSION_TELEMETRY=0')})
|
|
5899
|
+
- ${messages_code('--ai-help')} Show AI-assistant oriented help and tips
|
|
5900
|
+
- ${messages_code('--format')} ${messages_arg('<pretty|json>')} Output format for ${messages_code('--ai-help')} (default: pretty)
|
|
5901
|
+
- ${messages_code('--help')} Show help output
|
|
5902
|
+
- ${messages_code('--port')} ${messages_arg('<number>')} Development server port (default: 8080; use 0 for OS-assigned)
|
|
5903
|
+
- ${messages_code('--host')} ${messages_arg('<address>')} Dev server host (default: 127.0.0.1; use 0.0.0.0 for Docker/devcontainers)
|
|
5904
|
+
- ${messages_code('--starting-url')} ${messages_arg('<url>')} Initial URL to load in browser
|
|
5905
|
+
- ${messages_code('--silent')} ${messages_arg('[boolean]')} Suppress console output during build
|
|
5747
5906
|
|
|
5748
5907
|
Source Inspection (dev command)
|
|
5749
|
-
- ${
|
|
5908
|
+
- ${messages_code('--source')} ${messages_arg('<url|boolean>')} Open URL and print HTML after content scripts inject (dev only)
|
|
5750
5909
|
- When provided without a URL, falls back to ${messages_arg('--starting-url')} or ${messages_arg('https://example.com')}
|
|
5751
|
-
- For ${
|
|
5752
|
-
- ${messages_arg('Note:')} ${
|
|
5753
|
-
- ${messages_arg('Automation sync:')} when using ${
|
|
5754
|
-
- ${
|
|
5755
|
-
- ${
|
|
5756
|
-
- ${
|
|
5757
|
-
- ${
|
|
5758
|
-
- ${
|
|
5759
|
-
- ${
|
|
5760
|
-
- ${
|
|
5761
|
-
- ${
|
|
5762
|
-
- ${
|
|
5763
|
-
- ${
|
|
5764
|
-
- ${
|
|
5765
|
-
- ${
|
|
5910
|
+
- For ${messages_code('extension dev')}, watch mode is enabled by default when ${messages_code('--source')} is present
|
|
5911
|
+
- ${messages_arg('Note:')} ${messages_code('extension preview')} and ${messages_code('extension start')} do not run source inspection in run-only preview mode.
|
|
5912
|
+
- ${messages_arg('Automation sync:')} when using ${messages_code('extension dev --no-browser')} or ${messages_code('extension start --no-browser')}, run ${messages_code('extension <dev|start> --wait --browser=<browser>')} in a second process to gate on ${messages_code('ready.json')} (add ${messages_code('--wait-format=json')} for machine-readable output).
|
|
5913
|
+
- ${messages_code('--watch-source')} ${messages_arg('[boolean]')} Re-print HTML on rebuilds or file changes
|
|
5914
|
+
- ${messages_code('--source-format')} ${messages_arg('<pretty|json|ndjson>')} Output format for page HTML (defaults to ${messages_code('--log-format')} when present)
|
|
5915
|
+
- ${messages_code('--source-summary')} ${messages_arg('[boolean]')} Output a compact summary instead of full HTML
|
|
5916
|
+
- ${messages_code('--source-meta')} ${messages_arg('[boolean]')} Output page metadata (readyState, viewport, frames)
|
|
5917
|
+
- ${messages_code('--source-probe')} ${messages_arg('<selectors>')} Comma-separated CSS selectors to probe
|
|
5918
|
+
- ${messages_code('--source-tree')} ${messages_arg('<off|root-only>')} Output a compact extension root tree
|
|
5919
|
+
- ${messages_code('--source-console')} ${messages_arg('[boolean]')} Output console summary (best-effort)
|
|
5920
|
+
- ${messages_code('--source-dom')} ${messages_arg('[boolean]')} Output DOM snapshots and diffs
|
|
5921
|
+
- ${messages_code('--source-max-bytes')} ${messages_arg('<bytes>')} Limit HTML output size (0 disables truncation)
|
|
5922
|
+
- ${messages_code('--source-redact')} ${messages_arg('<off|safe|strict>')} Redact sensitive HTML content (default: safe for JSON/NDJSON)
|
|
5923
|
+
- ${messages_code('--source-include-shadow')} ${messages_arg('<off|open-only|all>')} Control Shadow DOM inclusion (default: open-only)
|
|
5924
|
+
- ${messages_code('--source-diff')} ${messages_arg('[boolean]')} Include diff metadata on watch updates
|
|
5766
5925
|
|
|
5767
5926
|
Browser-Specific Options
|
|
5768
|
-
- ${
|
|
5769
|
-
- ${
|
|
5770
|
-
Use ${
|
|
5927
|
+
- ${messages_code('--chromium-binary')} ${messages_arg('<path>')} Custom Chromium binary path
|
|
5928
|
+
- ${messages_code('--gecko-binary')}/${messages_code('--firefox-binary')} ${messages_arg('<path>')} Custom Firefox/Gecko binary path
|
|
5929
|
+
Use ${messages_code('flatpak:org.mozilla.firefox')} as the path to launch a Flatpak-installed Firefox
|
|
5771
5930
|
|
|
5772
5931
|
Build Options
|
|
5773
|
-
- ${
|
|
5774
|
-
- ${
|
|
5775
|
-
- ${
|
|
5932
|
+
- ${messages_code('--zip')} ${messages_arg('[boolean]')} Create ZIP archive of built extension
|
|
5933
|
+
- ${messages_code('--zip-source')} ${messages_arg('[boolean]')} Include source files in ZIP
|
|
5934
|
+
- ${messages_code('--zip-filename')} ${messages_arg('<name>')} Custom ZIP filename
|
|
5776
5935
|
|
|
5777
5936
|
${external_pintor_default().underline('Centralized Logger (terminal output)')}
|
|
5778
5937
|
- The manager extension embeds a centralized logger that streams events to the CLI.
|
|
5779
|
-
- Enable and filter logs directly via ${
|
|
5780
|
-
- ${
|
|
5781
|
-
- ${
|
|
5782
|
-
- ${
|
|
5783
|
-
- ${
|
|
5784
|
-
- ${
|
|
5785
|
-
- ${
|
|
5786
|
-
- ${
|
|
5787
|
-
- Example: ${
|
|
5938
|
+
- Enable and filter logs directly via ${messages_code('extension dev')} flags:
|
|
5939
|
+
- ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level (default: off)
|
|
5940
|
+
- ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts: background,content,page,sidebar,popup,options,devtools
|
|
5941
|
+
- ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Output format (default: pretty)
|
|
5942
|
+
- ${messages_code('--no-log-timestamps')} Hide ISO timestamps in pretty output
|
|
5943
|
+
- ${messages_code('--no-log-color')} Disable color in pretty output
|
|
5944
|
+
- ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by event.url
|
|
5945
|
+
- ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
|
|
5946
|
+
- Example: ${messages_code('extension dev ./my-ext --logs=debug --log-context=all --log-format=pretty')}
|
|
5788
5947
|
|
|
5789
|
-
${
|
|
5948
|
+
${messages_code('extension --help')}
|
|
5790
5949
|
This command outputs a help file with key command options.
|
|
5791
5950
|
|
|
5792
5951
|
${external_pintor_default().underline('Path Resolution (important)')}
|
|
5793
|
-
- Leading ${
|
|
5794
|
-
- Relative paths resolve from the ${
|
|
5952
|
+
- Leading ${messages_code('/')} in manifest/HTML means extension root (the directory containing ${messages_code('manifest.json')}).
|
|
5953
|
+
- Relative paths resolve from the ${messages_code('manifest.json')} directory.
|
|
5795
5954
|
- Absolute OS paths are used as-is.
|
|
5796
5955
|
|
|
5797
5956
|
|
|
5798
5957
|
AI Assistants
|
|
5799
|
-
- For AI-oriented guidance and deeper tips, run ${
|
|
5800
|
-
- For machine-readable AI guidance, run ${
|
|
5958
|
+
- For AI-oriented guidance and deeper tips, run ${messages_code('extension --ai-help')}
|
|
5959
|
+
- For machine-readable AI guidance, run ${messages_code('extension --ai-help --format json')}
|
|
5801
5960
|
|
|
5802
5961
|
Report issues
|
|
5803
5962
|
- ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
|
|
@@ -5805,99 +5964,102 @@ AI Assistants
|
|
|
5805
5964
|
function unsupportedBrowserFlag(value, supported) {
|
|
5806
5965
|
return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}. Supported: ${supported.join(', ')}.`;
|
|
5807
5966
|
}
|
|
5967
|
+
function safariCommandNotSupported(command) {
|
|
5968
|
+
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.`;
|
|
5969
|
+
}
|
|
5808
5970
|
function programAIHelp() {
|
|
5809
5971
|
return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
|
|
5810
5972
|
|
|
5811
5973
|
Browser-Specific Configuration
|
|
5812
5974
|
- Use browser prefixes in manifest.json for browser-specific fields:
|
|
5813
|
-
${
|
|
5975
|
+
${messages_code('{"firefox:manifest": 2, "chrome:manifest": 3}')}
|
|
5814
5976
|
This applies manifest v2 to Firefox only, v3 to Chrome/Edge.
|
|
5815
5977
|
|
|
5816
5978
|
Centralized Logger (for AI & CI)
|
|
5817
5979
|
- Logs from all contexts are centralized by the manager extension and streamed to the CLI.
|
|
5818
|
-
- Prefer these flags to control terminal logs during ${
|
|
5819
|
-
- ${
|
|
5820
|
-
- ${
|
|
5821
|
-
- ${
|
|
5822
|
-
- ${
|
|
5823
|
-
- ${
|
|
5824
|
-
- ${
|
|
5825
|
-
- ${
|
|
5826
|
-
- Good CI pattern: ${
|
|
5980
|
+
- Prefer these flags to control terminal logs during ${messages_code('extension dev')}:
|
|
5981
|
+
- ${messages_code('--logs')} ${messages_arg('<off|error|warn|info|debug|trace>')} Minimum level
|
|
5982
|
+
- ${messages_code('--log-context')} ${messages_arg('<list|all>')} Contexts to include
|
|
5983
|
+
- ${messages_code('--log-format')} ${messages_arg('<pretty|json|ndjson>')} Pretty for humans; JSON for machines/NDJSON pipelines
|
|
5984
|
+
- ${messages_code('--no-log-timestamps')} ${messages_arg(' ')} Disable timestamps (pretty)
|
|
5985
|
+
- ${messages_code('--no-log-color')} ${messages_arg(' ')} Disable ANSI colors (pretty)
|
|
5986
|
+
- ${messages_code('--log-url')} ${messages_arg('<substring|/regex/>')} Filter by URL
|
|
5987
|
+
- ${messages_code('--log-tab')} ${messages_arg('<id>')} Filter by tabId
|
|
5988
|
+
- Good CI pattern: ${messages_code('EXTENSION_AUTHOR_MODE=development EXTENSION_AUTO_EXIT_MS=6000 extension dev ./ext --logs=info --log-format=json')}
|
|
5827
5989
|
|
|
5828
5990
|
Special Folders for Entrypoints
|
|
5829
5991
|
- Use special folders to handle entrypoints and assets not declared in manifest.json:
|
|
5830
|
-
- ${external_pintor_default().underline(
|
|
5831
|
-
- ${external_pintor_default().underline(
|
|
5832
|
-
- ${external_pintor_default().underline(
|
|
5992
|
+
- ${external_pintor_default().underline(messages_code('public/'))} - Static assets automatically copied to build (resolves to output root)
|
|
5993
|
+
- ${external_pintor_default().underline(messages_code('pages/'))} - HTML files not declared in manifest (e.g., welcome pages)
|
|
5994
|
+
- ${external_pintor_default().underline(messages_code("scripts/"))} - JavaScript files not declared in manifest (e.g., executable scripts)
|
|
5833
5995
|
|
|
5834
5996
|
Predictable Output Paths
|
|
5835
5997
|
- Core HTML destinations are standardized across browsers so you can reference them safely in code/tests:
|
|
5836
|
-
- ${
|
|
5837
|
-
- ${
|
|
5838
|
-
- ${
|
|
5839
|
-
- ${
|
|
5840
|
-
- ${
|
|
5998
|
+
- ${messages_code('devtools_page')} → ${messages_code('devtools/index.html')}
|
|
5999
|
+
- ${messages_code('sidebar_action.default_panel')} (MV2) and ${messages_code('side_panel.default_path')} (MV3) → ${messages_code('sidebar/index.html')}
|
|
6000
|
+
- ${messages_code('options_ui.page')} and ${messages_code('options_page')} → ${messages_code('options/index.html')}
|
|
6001
|
+
- ${messages_code('background.page')} → ${messages_code('background/index.html')}
|
|
6002
|
+
- ${messages_code('action.default_popup')}, ${messages_code('browser_action.default_popup')}, ${messages_code('page_action.default_popup')} → ${messages_code('action/index.html')}
|
|
5841
6003
|
- Other predictable outputs:
|
|
5842
|
-
- ${
|
|
5843
|
-
- ${
|
|
5844
|
-
- ${
|
|
5845
|
-
- ${
|
|
5846
|
-
- ${
|
|
6004
|
+
- ${messages_code('chrome_url_overrides.*')} → ${messages_code('chrome_url_overrides/<key>.html')}
|
|
6005
|
+
- ${messages_code("content_scripts[n].js/css")} → ${messages_code("content_scripts/content-<n>.{js,css}")}
|
|
6006
|
+
- ${messages_code('sandbox.pages[]')} → ${messages_code('sandbox/page-<n>.html')}
|
|
6007
|
+
- ${messages_code("user_scripts.api_script")} → ${messages_code("user_scripts/api_script.js")}
|
|
6008
|
+
- ${messages_code('icons/*')} → ${messages_code('icons/')} (feature-specific icon folders preserved where applicable)
|
|
5847
6009
|
|
|
5848
6010
|
Public & Special Folders (Output Behavior)
|
|
5849
|
-
- ${external_pintor_default().underline(
|
|
5850
|
-
- ${external_pintor_default().underline(
|
|
5851
|
-
- ${external_pintor_default().underline(
|
|
6011
|
+
- ${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')}.
|
|
6012
|
+
- ${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.
|
|
6013
|
+
- ${external_pintor_default().underline(messages_code("scripts/"))} files emit as ${messages_code("scripts/<name>.js")} with extracted CSS when applicable.
|
|
5852
6014
|
|
|
5853
6015
|
Shadow DOM for Content Scripts
|
|
5854
|
-
- Add ${
|
|
5855
|
-
- Automatically creates ${
|
|
6016
|
+
- Add ${messages_code('use shadow-dom')} directive to content scripts for style isolation
|
|
6017
|
+
- Automatically creates ${messages_code('#extension-root')} element with shadow DOM
|
|
5856
6018
|
- All CSS imports are automatically injected into shadow DOM
|
|
5857
6019
|
- Prevents style conflicts with host page
|
|
5858
6020
|
|
|
5859
6021
|
Environment Variables
|
|
5860
|
-
- Use ${
|
|
5861
|
-
- Supported in both ${
|
|
5862
|
-
- Environment file priority: ${external_pintor_default().underline(
|
|
5863
|
-
- Example: ${
|
|
6022
|
+
- Use ${messages_code(messages_arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
|
|
6023
|
+
- Supported in both ${messages_code('process.env')} and ${messages_code('import.meta.env')}
|
|
6024
|
+
- 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')))}
|
|
6025
|
+
- Example: ${messages_code(messages_arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
|
|
5864
6026
|
|
|
5865
6027
|
Available Templates
|
|
5866
|
-
- ${external_pintor_default().green('Frameworks')}: ${
|
|
5867
|
-
- ${external_pintor_default().green('Languages')}: ${
|
|
5868
|
-
- ${external_pintor_default().green('Contexts')}: ${
|
|
5869
|
-
- ${external_pintor_default().green('Styling')}: ${
|
|
5870
|
-
- ${external_pintor_default().green('Configs')}: ${
|
|
6028
|
+
- ${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'))}
|
|
6029
|
+
- ${external_pintor_default().green('Languages')}: ${messages_code(messages_arg("javascript"))}, ${messages_code(messages_arg("typescript"))}
|
|
6030
|
+
- ${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)
|
|
6031
|
+
- ${external_pintor_default().green('Styling')}: ${messages_code(messages_arg('tailwind'))}, ${messages_code(messages_arg('sass'))}, ${messages_code(messages_arg('less'))}
|
|
6032
|
+
- ${external_pintor_default().green('Configs')}: ${messages_code(messages_arg('eslint'))}, ${messages_code(messages_arg('prettier'))}, ${messages_code(messages_arg('stylelint'))}
|
|
5871
6033
|
|
|
5872
6034
|
Webpack/Rspack Configuration
|
|
5873
|
-
- Create ${external_pintor_default().underline(
|
|
6035
|
+
- Create ${external_pintor_default().underline(messages_code(messages_arg('extension.config.js')))} for custom webpack configuration
|
|
5874
6036
|
- Function receives base config, return modified config
|
|
5875
6037
|
- Supports all webpack/rspack loaders and plugins
|
|
5876
6038
|
- Example:
|
|
5877
|
-
${
|
|
5878
|
-
${
|
|
5879
|
-
${
|
|
5880
|
-
${
|
|
5881
|
-
${
|
|
5882
|
-
${
|
|
6039
|
+
${messages_code('export default {')}
|
|
6040
|
+
${messages_code(' config: (config) => {')}
|
|
6041
|
+
${messages_code(" config.module.rules.push({ test: /\\.svg$/, use: ['@svgr/webpack'] })")}
|
|
6042
|
+
${messages_code(' return config')}
|
|
6043
|
+
${messages_code(' }')}
|
|
6044
|
+
${messages_code('}')}
|
|
5883
6045
|
|
|
5884
6046
|
Managed Dependencies (Important)
|
|
5885
6047
|
- ${external_pintor_default().green('Do not add')} packages that ${external_pintor_default().blue('Extension.js')} already ships in its own toolchain.
|
|
5886
|
-
- The guard only triggers when a managed package is declared in your ${
|
|
6048
|
+
- 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'))}.
|
|
5887
6049
|
- In that case, the program will ${external_pintor_default().red('print an error and abort')} to avoid version conflicts.
|
|
5888
|
-
- Remove the duplicate from your project ${
|
|
6050
|
+
- 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.
|
|
5889
6051
|
- If you truly need a different version, open an issue so we can evaluate a safe upgrade.
|
|
5890
6052
|
|
|
5891
6053
|
Framework-Specific Configuration
|
|
5892
|
-
- Create ${external_pintor_default().underline(
|
|
5893
|
-
- Create ${external_pintor_default().underline(
|
|
6054
|
+
- Create ${external_pintor_default().underline(messages_code(messages_arg('vue.loader.js')))} for Vue-specific loader configuration
|
|
6055
|
+
- Create ${external_pintor_default().underline(messages_code(messages_arg('svelte.loader.js')))} for Svelte-specific loader configuration
|
|
5894
6056
|
- Automatically detected and used by Extension.js
|
|
5895
6057
|
- Example svelte.loader.js:
|
|
5896
|
-
${
|
|
5897
|
-
${
|
|
5898
|
-
${
|
|
5899
|
-
${
|
|
5900
|
-
${
|
|
6058
|
+
${messages_code('module.exports = {')}
|
|
6059
|
+
${messages_code(' preprocess: require("svelte-preprocess")({')}
|
|
6060
|
+
${messages_code(" typescript: true")}
|
|
6061
|
+
${messages_code(' })')}
|
|
6062
|
+
${messages_code('}')}
|
|
5901
6063
|
|
|
5902
6064
|
Hot Module Replacement (HMR)
|
|
5903
6065
|
- Automatically enabled in development mode
|
|
@@ -5907,63 +6069,63 @@ Hot Module Replacement (HMR)
|
|
|
5907
6069
|
- Service workers, _locales and manifest changes reload the extension
|
|
5908
6070
|
|
|
5909
6071
|
Source Inspection & Real-Time Monitoring
|
|
5910
|
-
- Use ${
|
|
6072
|
+
- Use ${messages_code('extension dev --source')} ${messages_arg('<url|boolean>')} to inspect page HTML after content script injection
|
|
5911
6073
|
- When no URL is provided, falls back to ${messages_arg('--starting-url')} or ${messages_arg('https://example.com')}
|
|
5912
|
-
- Watch mode is enabled by default when ${
|
|
5913
|
-
- Use ${
|
|
5914
|
-
- Use ${
|
|
5915
|
-
- Use ${
|
|
5916
|
-
- Use ${
|
|
5917
|
-
- Use ${
|
|
5918
|
-
- Use ${
|
|
5919
|
-
- Use ${
|
|
5920
|
-
- Use ${
|
|
5921
|
-
- Use ${
|
|
5922
|
-
- Use ${
|
|
5923
|
-
- Use ${
|
|
6074
|
+
- Watch mode is enabled by default when ${messages_code('--source')} is present
|
|
6075
|
+
- Use ${messages_code('--watch-source')} to re-print HTML on rebuilds or file changes
|
|
6076
|
+
- Use ${messages_code('--source-format')} ${messages_arg('<pretty|json|ndjson>')} for machine-friendly HTML output
|
|
6077
|
+
- Use ${messages_code('--source-summary')} to emit a compact JSON summary instead of full HTML
|
|
6078
|
+
- Use ${messages_code('--source-meta')} to emit page metadata (readyState, viewport, frames)
|
|
6079
|
+
- Use ${messages_code('--source-probe')} to probe CSS selectors for quick validation
|
|
6080
|
+
- Use ${messages_code('--source-tree')} to emit a compact extension root tree
|
|
6081
|
+
- Use ${messages_code('--source-console')} to emit a console summary (best-effort)
|
|
6082
|
+
- Use ${messages_code('--source-dom')} to emit DOM snapshots and diffs
|
|
6083
|
+
- Use ${messages_code('--source-redact')} ${messages_arg('<off|safe|strict>')} to redact sensitive content
|
|
6084
|
+
- Use ${messages_code('--source-max-bytes')} ${messages_arg('<bytes>')} to limit output size
|
|
6085
|
+
- Use ${messages_code('--source-diff')} ${messages_arg('[boolean]')} to emit diff metadata for watch updates
|
|
5924
6086
|
- Source events include frame context (frameId/frameUrl), and console summaries include best-effort script URLs.
|
|
5925
|
-
- Action timeline events ${
|
|
6087
|
+
- Action timeline events ${messages_code('action_event')} report navigation, injection, rebuilds, snapshots, and reloads.
|
|
5926
6088
|
- Automatically enables Chrome remote debugging (port 9222) when source inspection is active
|
|
5927
|
-
- Extracts Shadow DOM content from ${
|
|
6089
|
+
- Extracts Shadow DOM content from ${messages_code('#extension-root')} or ${messages_code('[data-extension-root=\"true\"]')} elements
|
|
5928
6090
|
- Useful for debugging content script behavior and style injection
|
|
5929
|
-
- Example: ${
|
|
5930
|
-
- ${messages_arg('Note:')} ${
|
|
5931
|
-
- For machine synchronization with ${
|
|
6091
|
+
- Example: ${messages_code('extension dev --source=' + messages_arg('https://example.com'))}
|
|
6092
|
+
- ${messages_arg('Note:')} ${messages_code('preview/start')} run in run-only mode and do not perform source inspection.
|
|
6093
|
+
- For machine synchronization with ${messages_code('--no-browser')}, use ${messages_code('extension dev --wait --browser=<browser>')} or ${messages_code('extension start --wait --browser=<browser>')} in a second process (use ${messages_code('--wait-format=json')} when a parser consumes stdout).
|
|
5932
6094
|
|
|
5933
6095
|
Non-Destructive Testing in CI
|
|
5934
|
-
- Prefer ${
|
|
5935
|
-
- Reuse Playwright's Chromium via ${
|
|
5936
|
-
- Set ${
|
|
6096
|
+
- Prefer ${messages_code('EXTENSION_AUTHOR_MODE=development')} to copy local templates and avoid network.
|
|
6097
|
+
- Reuse Playwright's Chromium via ${messages_code('--chromium-binary')} path when available.
|
|
6098
|
+
- Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} and ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
|
|
5937
6099
|
|
|
5938
6100
|
File Watching & HMR Examples
|
|
5939
6101
|
- Content script JS/TS changes trigger reinjection; CSS changes update styles live.
|
|
5940
|
-
- For watch-source HTML prints, update a visible string in ${
|
|
6102
|
+
- For watch-source HTML prints, update a visible string in ${messages_code("content/scripts.*")} and assert it appears in stdout.
|
|
5941
6103
|
|
|
5942
6104
|
Troubleshooting
|
|
5943
|
-
- If HTML is not printed, ensure ${
|
|
5944
|
-
- Use ${
|
|
5945
|
-
- When ports conflict, pass ${
|
|
5946
|
-
- In Docker/devcontainers, pass ${
|
|
6105
|
+
- If HTML is not printed, ensure ${messages_code('--source')} is provided and browser launched with debugging port.
|
|
6106
|
+
- Use ${messages_code('--silent true')} during builds to reduce noise; logs still surface errors.
|
|
6107
|
+
- When ports conflict, pass ${messages_code('--port 0')} to auto-select an available port.
|
|
6108
|
+
- In Docker/devcontainers, pass ${messages_code('--host 0.0.0.0')} so the dev server is reachable from the host.
|
|
5947
6109
|
|
|
5948
6110
|
Non-Interactive / Auto Mode (CI)
|
|
5949
|
-
- Set ${
|
|
5950
|
-
Useful when ${
|
|
5951
|
-
Example: ${
|
|
5952
|
-
- Optional: ${
|
|
6111
|
+
- Set ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
|
|
6112
|
+
Useful when ${messages_code('pnpm extension dev')} would otherwise hang under Rspack watch.
|
|
6113
|
+
Example: ${messages_code(messages_arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --source ${messages_arg('https://example.com')}
|
|
6114
|
+
- Optional: ${messages_code(messages_arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
|
|
5953
6115
|
|
|
5954
6116
|
Docker / Devcontainers / Codespaces
|
|
5955
|
-
- Use ${
|
|
5956
|
-
- Use ${
|
|
5957
|
-
- Chromium sandbox flags (${
|
|
6117
|
+
- Use ${messages_code('--host 0.0.0.0')} to bind the dev server on all interfaces so HMR is reachable from the host.
|
|
6118
|
+
- Use ${messages_code('--no-browser')} inside the container and load the extension manually from ${messages_code('dist/<browser>/')} in your host browser.
|
|
6119
|
+
- Chromium sandbox flags (${messages_code('--no-sandbox')}) are added automatically when Docker, Podman, devcontainers, or Codespaces are detected.
|
|
5958
6120
|
- File watching uses polling by default (1 s interval), which works across bind-mounted volumes.
|
|
5959
|
-
- Example: ${
|
|
6121
|
+
- Example: ${messages_code('extension dev ./my-ext --host 0.0.0.0 --no-browser --port 8080')}
|
|
5960
6122
|
|
|
5961
6123
|
Flatpak Firefox
|
|
5962
|
-
- Use ${
|
|
5963
|
-
- Extension.js rewrites the binary path to ${
|
|
6124
|
+
- Use ${messages_code('--gecko-binary flatpak:org.mozilla.firefox')} (or ${messages_code('--firefox-binary')}) to launch a Flatpak-installed Firefox.
|
|
6125
|
+
- Extension.js rewrites the binary path to ${messages_code('flatpak run')} with the correct filesystem grants automatically.
|
|
5964
6126
|
|
|
5965
6127
|
Cross-Browser Compatibility
|
|
5966
|
-
- Use ${
|
|
6128
|
+
- Use ${messages_code('--polyfill')} flag to enable webextension-polyfill
|
|
5967
6129
|
- Automatically handles browser API differences
|
|
5968
6130
|
- Supports Chrome, Edge, Firefox with single codebase`;
|
|
5969
6131
|
}
|
|
@@ -5996,6 +6158,41 @@ Cross-Browser Compatibility
|
|
|
5996
6158
|
summary: commandDescriptions.build,
|
|
5997
6159
|
supportsSourceInspection: false
|
|
5998
6160
|
},
|
|
6161
|
+
{
|
|
6162
|
+
name: 'logs',
|
|
6163
|
+
summary: commandDescriptions.logs,
|
|
6164
|
+
supportsSourceInspection: false
|
|
6165
|
+
},
|
|
6166
|
+
{
|
|
6167
|
+
name: 'eval',
|
|
6168
|
+
summary: commandDescriptions.eval,
|
|
6169
|
+
supportsSourceInspection: false
|
|
6170
|
+
},
|
|
6171
|
+
{
|
|
6172
|
+
name: 'storage',
|
|
6173
|
+
summary: commandDescriptions.storage,
|
|
6174
|
+
supportsSourceInspection: false
|
|
6175
|
+
},
|
|
6176
|
+
{
|
|
6177
|
+
name: 'reload',
|
|
6178
|
+
summary: commandDescriptions.reload,
|
|
6179
|
+
supportsSourceInspection: false
|
|
6180
|
+
},
|
|
6181
|
+
{
|
|
6182
|
+
name: 'open',
|
|
6183
|
+
summary: commandDescriptions.open,
|
|
6184
|
+
supportsSourceInspection: false
|
|
6185
|
+
},
|
|
6186
|
+
{
|
|
6187
|
+
name: 'inspect',
|
|
6188
|
+
summary: commandDescriptions.inspect,
|
|
6189
|
+
supportsSourceInspection: true
|
|
6190
|
+
},
|
|
6191
|
+
{
|
|
6192
|
+
name: 'publish',
|
|
6193
|
+
summary: commandDescriptions.publish,
|
|
6194
|
+
supportsSourceInspection: false
|
|
6195
|
+
},
|
|
5999
6196
|
{
|
|
6000
6197
|
name: 'install',
|
|
6001
6198
|
summary: commandDescriptions.install,
|
|
@@ -6095,9 +6292,45 @@ Cross-Browser Compatibility
|
|
|
6095
6292
|
},
|
|
6096
6293
|
managedDependencies: {
|
|
6097
6294
|
enforcement: 'guarded',
|
|
6098
|
-
trigger: 'when managed packages are declared in package.json and
|
|
6295
|
+
trigger: 'when managed packages are declared in package.json and imported as a module specifier in extension.config',
|
|
6099
6296
|
action: 'print an error and abort'
|
|
6100
6297
|
},
|
|
6298
|
+
readyContract: {
|
|
6299
|
+
readyPath: 'dist/extension-js/<browser>/ready.json',
|
|
6300
|
+
eventsPath: 'dist/extension-js/<browser>/events.ndjson',
|
|
6301
|
+
waitFlag: '--wait blocks until ready.json reports ready (or error) then exits; pair with --wait-format=json for machine output',
|
|
6302
|
+
statuses: [
|
|
6303
|
+
'starting',
|
|
6304
|
+
'ready',
|
|
6305
|
+
'error'
|
|
6306
|
+
],
|
|
6307
|
+
readyFields: [
|
|
6308
|
+
'status',
|
|
6309
|
+
'command',
|
|
6310
|
+
'browser',
|
|
6311
|
+
'runId',
|
|
6312
|
+
'startedAt',
|
|
6313
|
+
'distPath',
|
|
6314
|
+
'manifestPath',
|
|
6315
|
+
'port',
|
|
6316
|
+
'pid',
|
|
6317
|
+
'ts',
|
|
6318
|
+
'compiledAt',
|
|
6319
|
+
'errors'
|
|
6320
|
+
],
|
|
6321
|
+
eventTypes: [
|
|
6322
|
+
'compile_start',
|
|
6323
|
+
'compile_success',
|
|
6324
|
+
'compile_error',
|
|
6325
|
+
'shutdown'
|
|
6326
|
+
],
|
|
6327
|
+
notes: [
|
|
6328
|
+
'ready.json is written atomically by the build (dev/start) on each compile',
|
|
6329
|
+
'events.ndjson is an append-only build timeline with durationMs and errorCount per entry',
|
|
6330
|
+
'--wait requires a local project path (remote URLs are not supported)',
|
|
6331
|
+
'consumers should verify pid liveness and recency before trusting a contract'
|
|
6332
|
+
]
|
|
6333
|
+
},
|
|
6101
6334
|
dockerAndContainers: {
|
|
6102
6335
|
hostFlag: '--host 0.0.0.0 binds the dev server to all interfaces',
|
|
6103
6336
|
sandboxDetection: [
|
|
@@ -6131,22 +6364,22 @@ Cross-Browser Compatibility
|
|
|
6131
6364
|
};
|
|
6132
6365
|
}
|
|
6133
6366
|
function invalidAIHelpFormat(value) {
|
|
6134
|
-
return `${getLoggingPrefix('error')} Invalid value for ${
|
|
6367
|
+
return `${getLoggingPrefix('error')} Invalid value for ${messages_code('--format')}: ${external_pintor_default().red(String(value))}\nAllowed values: ${messages_arg('pretty, json')}. Example: ${messages_code('extension --ai-help --format json')}`;
|
|
6135
6368
|
}
|
|
6136
6369
|
function sourceInspectionNotSupported(command) {
|
|
6137
|
-
return `${getLoggingPrefix('error')} ${
|
|
6370
|
+
return `${getLoggingPrefix('error')} ${messages_code(`extension ${command}`)} currently runs in run-only preview mode and does not support source inspection.\nUse ${messages_code('extension dev --source <url>')} for source inspection features.`;
|
|
6138
6371
|
}
|
|
6139
6372
|
function removedNoRunnerFlag() {
|
|
6140
|
-
return `${getLoggingPrefix('error')} ${
|
|
6373
|
+
return `${getLoggingPrefix('error')} ${messages_code('--no-runner')} was removed.\nUse ${messages_code('--no-browser')} instead.`;
|
|
6141
6374
|
}
|
|
6142
6375
|
function noBrowserNotSupportedForCommand(command) {
|
|
6143
|
-
return `${getLoggingPrefix('error')} ${
|
|
6376
|
+
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)')}`;
|
|
6144
6377
|
}
|
|
6145
6378
|
function sourceIncompatibleWithWait() {
|
|
6146
|
-
return `${getLoggingPrefix('error')} ${
|
|
6379
|
+
return `${getLoggingPrefix('error')} ${messages_code('--source')} cannot be combined with ${messages_code('--wait')}.\nSource inspection requires a live browser session; ${messages_code('--wait')} exits as soon as the ready contract is satisfied.\nRun them separately: ${messages_code('extension dev --wait')} for the readiness gate, then ${messages_code('extension dev --source <url>')} for inspection.`;
|
|
6147
6380
|
}
|
|
6148
6381
|
function sourceIncompatibleWithNoBrowser() {
|
|
6149
|
-
return `${getLoggingPrefix('error')} ${
|
|
6382
|
+
return `${getLoggingPrefix('error')} ${messages_code('--source')} cannot be combined with ${messages_code('--no-browser')}.\nSource inspection drives a real browser via CDP/RDP and cannot run headlessly against the dev server alone.\nDrop ${messages_code('--no-browser')} or omit the ${messages_code('--source')} flags to continue.`;
|
|
6150
6383
|
}
|
|
6151
6384
|
const external_semver_namespaceObject = require("semver");
|
|
6152
6385
|
const external_node_fs_namespaceObject = require("node:fs");
|
|
@@ -6303,6 +6536,15 @@ Cross-Browser Compatibility
|
|
|
6303
6536
|
if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
|
|
6304
6537
|
return await import("extension-develop/preview");
|
|
6305
6538
|
}
|
|
6539
|
+
async function loadExtensionDevelopBridgeModule(startDir = __dirname) {
|
|
6540
|
+
const root = resolveExtensionDevelopRoot(startDir);
|
|
6541
|
+
const { source } = resolvePreferredDevelopRoot(startDir);
|
|
6542
|
+
const bridgeEntry = resolveDevelopDistEntry(root, 'bridge');
|
|
6543
|
+
if (bridgeEntry) return importModule(bridgeEntry);
|
|
6544
|
+
if ('workspace' === source) throw new Error(`Local extension-develop runtime is not built at ${external_path_default().join(root, 'dist')}. Run \`pnpm --filter extension-develop compile\` before invoking the local CLI.`);
|
|
6545
|
+
const bridgeSpecifier = 'extension-develop/bridge';
|
|
6546
|
+
return await import(bridgeSpecifier);
|
|
6547
|
+
}
|
|
6306
6548
|
const external_node_os_namespaceObject = require("node:os");
|
|
6307
6549
|
var external_node_os_default = /*#__PURE__*/ __webpack_require__.n(external_node_os_namespaceObject);
|
|
6308
6550
|
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
@@ -6645,6 +6887,9 @@ Cross-Browser Compatibility
|
|
|
6645
6887
|
markCommandFailure();
|
|
6646
6888
|
});
|
|
6647
6889
|
}
|
|
6890
|
+
function isSafariVendor(value) {
|
|
6891
|
+
return 'safari' === value || 'webkit-based' === value;
|
|
6892
|
+
}
|
|
6648
6893
|
function parseOptionalBoolean(value) {
|
|
6649
6894
|
if (void 0 === value) return true;
|
|
6650
6895
|
const normalized = String(value).trim().toLowerCase();
|
|
@@ -6671,7 +6916,9 @@ Cross-Browser Compatibility
|
|
|
6671
6916
|
'chromium',
|
|
6672
6917
|
'chromium-based',
|
|
6673
6918
|
'gecko-based',
|
|
6674
|
-
'firefox-based'
|
|
6919
|
+
'firefox-based',
|
|
6920
|
+
'safari',
|
|
6921
|
+
'webkit-based'
|
|
6675
6922
|
];
|
|
6676
6923
|
for (const v of vendorsList)if (!supported.includes(v)) {
|
|
6677
6924
|
onInvalid(v, supported);
|
|
@@ -6702,6 +6949,7 @@ Cross-Browser Compatibility
|
|
|
6702
6949
|
});
|
|
6703
6950
|
});
|
|
6704
6951
|
}
|
|
6952
|
+
const READY_CONTRACT_FRESHNESS_MS = 60000;
|
|
6705
6953
|
function isHttpUrl(value) {
|
|
6706
6954
|
if (!value) return false;
|
|
6707
6955
|
return /^https?:\/\//i.test(value);
|
|
@@ -6731,7 +6979,7 @@ Cross-Browser Compatibility
|
|
|
6731
6979
|
return false;
|
|
6732
6980
|
}
|
|
6733
6981
|
}
|
|
6734
|
-
function isFreshContractPayload(payload
|
|
6982
|
+
function isFreshContractPayload(payload) {
|
|
6735
6983
|
const candidates = [
|
|
6736
6984
|
payload.ts,
|
|
6737
6985
|
payload.compiledAt,
|
|
@@ -6740,7 +6988,7 @@ Cross-Browser Compatibility
|
|
|
6740
6988
|
for (const candidate of candidates){
|
|
6741
6989
|
if (!candidate) continue;
|
|
6742
6990
|
const stamp = Date.parse(candidate);
|
|
6743
|
-
if (Number.isFinite(stamp)) return Date.now() - stamp <=
|
|
6991
|
+
if (Number.isFinite(stamp)) return Date.now() - stamp <= READY_CONTRACT_FRESHNESS_MS;
|
|
6744
6992
|
}
|
|
6745
6993
|
return false;
|
|
6746
6994
|
}
|
|
@@ -6751,7 +6999,7 @@ Cross-Browser Compatibility
|
|
|
6751
6999
|
if (external_node_fs_namespaceObject.existsSync(readyPath)) try {
|
|
6752
7000
|
const payload = JSON.parse(external_node_fs_namespaceObject.readFileSync(readyPath, 'utf8'));
|
|
6753
7001
|
const isLive = isProcessLikelyAlive(payload.pid);
|
|
6754
|
-
const isFresh = isFreshContractPayload(payload
|
|
7002
|
+
const isFresh = isFreshContractPayload(payload);
|
|
6755
7003
|
if (payload.command !== options.command) {
|
|
6756
7004
|
await new Promise((resolve)=>setTimeout(resolve, 250));
|
|
6757
7005
|
continue;
|
|
@@ -6807,6 +7055,272 @@ Cross-Browser Compatibility
|
|
|
6807
7055
|
};
|
|
6808
7056
|
}
|
|
6809
7057
|
var browsers = __webpack_require__("./browsers/index.ts");
|
|
7058
|
+
var external_child_process_ = __webpack_require__("child_process");
|
|
7059
|
+
var messages = __webpack_require__("./browsers/browsers-lib/messages.ts");
|
|
7060
|
+
function isMacOS() {
|
|
7061
|
+
return 'darwin' === process.platform;
|
|
7062
|
+
}
|
|
7063
|
+
function activeDeveloperDir() {
|
|
7064
|
+
try {
|
|
7065
|
+
const result = (0, external_child_process_.spawnSync)('xcode-select', [
|
|
7066
|
+
'-p'
|
|
7067
|
+
], {
|
|
7068
|
+
encoding: 'utf8',
|
|
7069
|
+
timeout: 15000
|
|
7070
|
+
});
|
|
7071
|
+
if (0 !== result.status) return null;
|
|
7072
|
+
const resolved = String(result.stdout || '').trim();
|
|
7073
|
+
return resolved.length > 0 ? resolved : null;
|
|
7074
|
+
} catch {
|
|
7075
|
+
return null;
|
|
7076
|
+
}
|
|
7077
|
+
}
|
|
7078
|
+
function findWithXcrun(tool) {
|
|
7079
|
+
try {
|
|
7080
|
+
const result = (0, external_child_process_.spawnSync)('xcrun', [
|
|
7081
|
+
'--find',
|
|
7082
|
+
tool
|
|
7083
|
+
], {
|
|
7084
|
+
encoding: 'utf8',
|
|
7085
|
+
timeout: 15000
|
|
7086
|
+
});
|
|
7087
|
+
if (0 !== result.status) return null;
|
|
7088
|
+
const resolved = String(result.stdout || '').trim();
|
|
7089
|
+
return resolved.length > 0 ? resolved : null;
|
|
7090
|
+
} catch {
|
|
7091
|
+
return null;
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
function detectSafariToolchain() {
|
|
7095
|
+
const platformOk = isMacOS();
|
|
7096
|
+
if (!platformOk) return {
|
|
7097
|
+
platformOk: false,
|
|
7098
|
+
developerDir: null,
|
|
7099
|
+
needsFullXcode: false,
|
|
7100
|
+
converter: null,
|
|
7101
|
+
xcodebuild: null,
|
|
7102
|
+
ok: false
|
|
7103
|
+
};
|
|
7104
|
+
const developerDir = activeDeveloperDir();
|
|
7105
|
+
const needsFullXcode = !developerDir || /CommandLineTools/i.test(developerDir);
|
|
7106
|
+
const converter = findWithXcrun('safari-web-extension-converter');
|
|
7107
|
+
const xcodebuild = findWithXcrun('xcodebuild');
|
|
7108
|
+
return {
|
|
7109
|
+
platformOk,
|
|
7110
|
+
developerDir,
|
|
7111
|
+
needsFullXcode,
|
|
7112
|
+
converter,
|
|
7113
|
+
xcodebuild,
|
|
7114
|
+
ok: Boolean(converter && xcodebuild)
|
|
7115
|
+
};
|
|
7116
|
+
}
|
|
7117
|
+
function logSafariDryRun(converterCmd, xcodebuildCmd) {
|
|
7118
|
+
console.log(messages.KP6());
|
|
7119
|
+
console.log(messages.ycc());
|
|
7120
|
+
console.log(messages.fLj(converterCmd));
|
|
7121
|
+
console.log(messages.Ajw(xcodebuildCmd));
|
|
7122
|
+
}
|
|
7123
|
+
function sanitizeAppName(value) {
|
|
7124
|
+
const cleaned = String(value || '').replace(/[\\/:*?"<>|]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
7125
|
+
return cleaned.length > 0 ? cleaned : 'Extension';
|
|
7126
|
+
}
|
|
7127
|
+
function bundleSegment(appName) {
|
|
7128
|
+
return String(appName || '').replace(/[^A-Za-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'extension';
|
|
7129
|
+
}
|
|
7130
|
+
function deriveBundleId(appName) {
|
|
7131
|
+
return `dev.extensionjs.${bundleSegment(appName)}`;
|
|
7132
|
+
}
|
|
7133
|
+
function readManifest(extensionDir) {
|
|
7134
|
+
try {
|
|
7135
|
+
const manifestPath = external_path_.join(extensionDir, 'manifest.json');
|
|
7136
|
+
if (external_fs_.existsSync(manifestPath)) return JSON.parse(external_fs_.readFileSync(manifestPath, 'utf8')) || {};
|
|
7137
|
+
} catch {}
|
|
7138
|
+
return {};
|
|
7139
|
+
}
|
|
7140
|
+
function resolveSafariBuildConfig(compilation, host) {
|
|
7141
|
+
const extensionDir = String(compilation?.options?.output?.path || '');
|
|
7142
|
+
const manifest = readManifest(extensionDir);
|
|
7143
|
+
const appName = sanitizeAppName(String(host.appName || manifest?.name || 'Extension'));
|
|
7144
|
+
const bundleIdentifier = deriveBundleId(appName);
|
|
7145
|
+
const projectLocation = `${extensionDir.replace(/[\\/]+$/, '')}-xcode`;
|
|
7146
|
+
return {
|
|
7147
|
+
extensionDir,
|
|
7148
|
+
projectLocation,
|
|
7149
|
+
appName,
|
|
7150
|
+
bundleIdentifier,
|
|
7151
|
+
macOsOnly: false !== host.macOsOnly,
|
|
7152
|
+
language: 'swift',
|
|
7153
|
+
open: !host.noOpen,
|
|
7154
|
+
safariBinary: host.safariBinary
|
|
7155
|
+
};
|
|
7156
|
+
}
|
|
7157
|
+
function composeConverterArgs(config) {
|
|
7158
|
+
const args = [
|
|
7159
|
+
'safari-web-extension-converter',
|
|
7160
|
+
config.extensionDir,
|
|
7161
|
+
'--project-location',
|
|
7162
|
+
config.projectLocation,
|
|
7163
|
+
'--app-name',
|
|
7164
|
+
config.appName,
|
|
7165
|
+
'--bundle-identifier',
|
|
7166
|
+
config.bundleIdentifier,
|
|
7167
|
+
'--no-prompt',
|
|
7168
|
+
'--no-open',
|
|
7169
|
+
'--force',
|
|
7170
|
+
'objc' === config.language ? '--objc' : '--swift'
|
|
7171
|
+
];
|
|
7172
|
+
if (config.macOsOnly) args.push('--macos-only');
|
|
7173
|
+
return args;
|
|
7174
|
+
}
|
|
7175
|
+
function macOsSchemeName(config) {
|
|
7176
|
+
return config.macOsOnly ? config.appName : `${config.appName} (macOS)`;
|
|
7177
|
+
}
|
|
7178
|
+
function xcodeProjectPath(config) {
|
|
7179
|
+
return external_path_.join(config.projectLocation, config.appName, `${config.appName}.xcodeproj`);
|
|
7180
|
+
}
|
|
7181
|
+
function derivedDataPath(config) {
|
|
7182
|
+
return external_path_.join(config.projectLocation, '.derived');
|
|
7183
|
+
}
|
|
7184
|
+
function builtAppPath(config) {
|
|
7185
|
+
return external_path_.join(derivedDataPath(config), 'Build', 'Products', 'Release', `${config.appName}.app`);
|
|
7186
|
+
}
|
|
7187
|
+
function composeXcodebuildArgs(config) {
|
|
7188
|
+
return [
|
|
7189
|
+
'-project',
|
|
7190
|
+
xcodeProjectPath(config),
|
|
7191
|
+
'-scheme',
|
|
7192
|
+
macOsSchemeName(config),
|
|
7193
|
+
'-configuration',
|
|
7194
|
+
'Release',
|
|
7195
|
+
'-derivedDataPath',
|
|
7196
|
+
derivedDataPath(config),
|
|
7197
|
+
'CODE_SIGN_IDENTITY=-',
|
|
7198
|
+
'CODE_SIGNING_REQUIRED=NO',
|
|
7199
|
+
'CODE_SIGNING_ALLOWED=YES',
|
|
7200
|
+
'build'
|
|
7201
|
+
];
|
|
7202
|
+
}
|
|
7203
|
+
function fallbackLogger() {
|
|
7204
|
+
return {
|
|
7205
|
+
info: (...a)=>console.log(...a),
|
|
7206
|
+
warn: (...a)=>console.warn(...a),
|
|
7207
|
+
error: (...a)=>console.error(...a),
|
|
7208
|
+
debug: (...a)=>console?.debug?.(...a)
|
|
7209
|
+
};
|
|
7210
|
+
}
|
|
7211
|
+
function isTestEnv() {
|
|
7212
|
+
return Boolean(process.env.VITEST || process.env.VITEST_WORKER_ID);
|
|
7213
|
+
}
|
|
7214
|
+
function delay(ms) {
|
|
7215
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
7216
|
+
}
|
|
7217
|
+
function runTool(bin, args) {
|
|
7218
|
+
const inheritOutput = 'true' === process.env.EXTENSION_AUTHOR_MODE;
|
|
7219
|
+
return new Promise((resolve)=>{
|
|
7220
|
+
const child = (0, external_child_process_.spawn)(bin, args, {
|
|
7221
|
+
stdio: inheritOutput ? 'inherit' : 'ignore'
|
|
7222
|
+
});
|
|
7223
|
+
child.on('error', ()=>resolve(false));
|
|
7224
|
+
child.on('close', (code)=>resolve(0 === code));
|
|
7225
|
+
});
|
|
7226
|
+
}
|
|
7227
|
+
function runToolCapture(bin, args) {
|
|
7228
|
+
return new Promise((resolve)=>{
|
|
7229
|
+
let stdout = '';
|
|
7230
|
+
const child = (0, external_child_process_.spawn)(bin, args, {
|
|
7231
|
+
stdio: [
|
|
7232
|
+
'ignore',
|
|
7233
|
+
'pipe',
|
|
7234
|
+
'pipe'
|
|
7235
|
+
]
|
|
7236
|
+
});
|
|
7237
|
+
child.stdout?.on('data', (chunk)=>{
|
|
7238
|
+
stdout += String(chunk);
|
|
7239
|
+
});
|
|
7240
|
+
child.on('error', ()=>resolve({
|
|
7241
|
+
ok: false,
|
|
7242
|
+
stdout
|
|
7243
|
+
}));
|
|
7244
|
+
child.on('close', (code)=>resolve({
|
|
7245
|
+
ok: 0 === code,
|
|
7246
|
+
stdout
|
|
7247
|
+
}));
|
|
7248
|
+
});
|
|
7249
|
+
}
|
|
7250
|
+
async function confirmRegisteredWithSafari(bundleIdentifier) {
|
|
7251
|
+
const needle = `${bundleIdentifier}.Extension`;
|
|
7252
|
+
for(let attempt = 0; attempt < 6; attempt += 1){
|
|
7253
|
+
const { ok, stdout } = await runToolCapture('pluginkit', [
|
|
7254
|
+
'-m'
|
|
7255
|
+
]);
|
|
7256
|
+
if (ok && stdout.includes(needle)) return true;
|
|
7257
|
+
await delay(800);
|
|
7258
|
+
}
|
|
7259
|
+
return false;
|
|
7260
|
+
}
|
|
7261
|
+
function safariPreflightError() {
|
|
7262
|
+
const tc = detectSafariToolchain();
|
|
7263
|
+
if (!tc.platformOk) return messages.G3E(process.platform);
|
|
7264
|
+
if (!tc.ok) {
|
|
7265
|
+
if (tc.needsFullXcode) return messages.Xiv(tc.developerDir);
|
|
7266
|
+
return messages.Csg(tc.converter ? 'xcodebuild' : 'safari-web-extension-converter');
|
|
7267
|
+
}
|
|
7268
|
+
return null;
|
|
7269
|
+
}
|
|
7270
|
+
async function runSafariPipeline(compilation, host, logger, mode) {
|
|
7271
|
+
const config = resolveSafariBuildConfig(compilation, host);
|
|
7272
|
+
const converterArgs = composeConverterArgs(config);
|
|
7273
|
+
const xcodebuildArgs = composeXcodebuildArgs(config);
|
|
7274
|
+
if (host.dryRun || isTestEnv()) return void logSafariDryRun(`xcrun ${converterArgs.join(' ')}`, `xcodebuild ${xcodebuildArgs.join(' ')}`);
|
|
7275
|
+
const toolchain = detectSafariToolchain();
|
|
7276
|
+
if (!toolchain.platformOk) return void logger.warn?.(messages.G3E(process.platform));
|
|
7277
|
+
if (!toolchain.ok) {
|
|
7278
|
+
if (toolchain.needsFullXcode) logger.error?.(messages.Xiv(toolchain.developerDir));
|
|
7279
|
+
else {
|
|
7280
|
+
const missing = toolchain.converter ? 'xcodebuild' : 'safari-web-extension-converter';
|
|
7281
|
+
logger.error?.(messages.Csg(missing));
|
|
7282
|
+
}
|
|
7283
|
+
return;
|
|
7284
|
+
}
|
|
7285
|
+
const projectExists = external_fs_.existsSync(xcodeProjectPath(config));
|
|
7286
|
+
if (!projectExists || host.forceRegenerate) {
|
|
7287
|
+
logger.info?.(messages.uKN(config.extensionDir));
|
|
7288
|
+
if (!await runTool('xcrun', converterArgs)) return void logger.error?.(messages.Qrh(new Error('safari-web-extension-converter failed')));
|
|
7289
|
+
logger.info?.(messages.l_B(config.projectLocation));
|
|
7290
|
+
}
|
|
7291
|
+
if ('full' === mode) logger.info?.(messages.jRH(macOsSchemeName(config)));
|
|
7292
|
+
if (!await runTool('xcodebuild', xcodebuildArgs)) return void logger.error?.(messages.Qrh(new Error('xcodebuild failed')));
|
|
7293
|
+
const appPath = builtAppPath(config);
|
|
7294
|
+
if ('resync' === mode) return void logger.info?.(messages.cFJ(config.appName));
|
|
7295
|
+
logger.info?.(messages.Rl_(appPath));
|
|
7296
|
+
if (config.open) {
|
|
7297
|
+
const target = external_fs_.existsSync(appPath) ? appPath : xcodeProjectPath(config);
|
|
7298
|
+
logger.info?.(messages.fwU(target));
|
|
7299
|
+
await runTool('open', [
|
|
7300
|
+
target
|
|
7301
|
+
]);
|
|
7302
|
+
if (config.safariBinary) await runTool('open', [
|
|
7303
|
+
'-a',
|
|
7304
|
+
config.safariBinary
|
|
7305
|
+
]);
|
|
7306
|
+
}
|
|
7307
|
+
logger.info?.(messages.fOJ(config.appName));
|
|
7308
|
+
if (await confirmRegisteredWithSafari(config.bundleIdentifier)) logger.info?.(messages.fdc(config.appName));
|
|
7309
|
+
else logger.warn?.(messages.qYJ(config.appName));
|
|
7310
|
+
}
|
|
7311
|
+
async function packageSafariExtension(host, outputPath, logger, mode = 'full') {
|
|
7312
|
+
const compilation = {
|
|
7313
|
+
options: {
|
|
7314
|
+
output: {
|
|
7315
|
+
path: outputPath
|
|
7316
|
+
}
|
|
7317
|
+
},
|
|
7318
|
+
outputOptions: {
|
|
7319
|
+
path: outputPath
|
|
7320
|
+
}
|
|
7321
|
+
};
|
|
7322
|
+
await runSafariPipeline(compilation, host, logger || fallbackLogger(), mode);
|
|
7323
|
+
}
|
|
6810
7324
|
function normalizeSourceOption(source, startingUrl) {
|
|
6811
7325
|
if (!source) return;
|
|
6812
7326
|
const hasExplicitSourceString = 'string' == typeof source && 'true' !== String(source).trim().toLowerCase();
|
|
@@ -6913,7 +7427,7 @@ Cross-Browser Compatibility
|
|
|
6913
7427
|
return values.length > 0 ? values : void 0;
|
|
6914
7428
|
}
|
|
6915
7429
|
function registerDevCommand(program) {
|
|
6916
|
-
program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --no-reload emit a dev-mode dist without the content-script reload runtime; tabs need manual reload to see changes\n --wait wait for ready contract and exit\n --wait-format pretty|json output for wait mode\n").option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('-b, --browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').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('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `
|
|
7430
|
+
program.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description(commandDescriptions.dev).addHelpText('after', "\nAdditional options:\n --no-browser do not launch the browser (dev server still starts)\n --no-reload emit a dev-mode dist without the content-script reload runtime; tabs need manual reload to see changes\n --wait wait for ready contract and exit\n --wait-format pretty|json output for wait mode\n").option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('-b, --browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | safari | webkit-based>', 'specify a browser/engine to run. Defaults to `chromium`. `safari` builds and opens a Safari app via Xcode (macOS only; no live reload)').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary, --firefox-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').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('--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('--source [url]', "[experimental] opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source [boolean]', '[experimental] re-print HTML on rebuilds or file changes (defaults to true when --source is present)', parseOptionalBoolean).option('--source-format <pretty|json|ndjson>', '[experimental] output format for source HTML (defaults to --log-format when present, otherwise JSON when --source is used)').option('--source-summary [boolean]', '[experimental] output a compact summary instead of full HTML', parseOptionalBoolean).option('--source-meta [boolean]', '[experimental] output page metadata (readyState, viewport, frames)', parseOptionalBoolean).option('--source-probe <selectors>', '[experimental] comma-separated CSS selectors to probe').option('--source-tree <off|root-only>', '[experimental] output a compact extension root tree').option('--source-console [boolean]', '[experimental] output console summary (best-effort)', parseOptionalBoolean).option('--source-dom [boolean]', '[experimental] output DOM snapshots and diffs (default: true when watch is enabled)', parseOptionalBoolean).option('--source-max-bytes <bytes>', '[experimental] limit HTML output size in bytes (0 disables truncation)').option('--source-redact <off|safe|strict>', '[experimental] redact sensitive content in HTML output (default: safe for JSON/NDJSON)').option('--source-include-shadow <off|open-only|all>', '[experimental] control Shadow DOM inclusion in HTML output (default: open-only)').option('--source-diff [boolean]', '[experimental] include diff metadata on watch updates (default: true when watch is enabled)', parseOptionalBoolean).option('--extensions <list>', 'comma-separated list of companion extensions or store URLs to load').option('--install [boolean]', '[internal] install project dependencies when missing', parseOptionalBoolean).option('--wait [boolean]', 'wait for dist/extension-js/<browser>/ready.json and exit', parseOptionalBoolean).option('--wait-timeout <ms>', 'timeout in milliseconds when using --wait (default: 60000)').option('--wait-format <pretty|json>', 'output format for --wait results (default: pretty)').option('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').option('--allow-control', 'enable the agent-bridge control channel for bounded act (storage/reload/open): see `extension reload|storage|open`').option('--allow-eval', 'additionally enable `extension eval` (runs arbitrary code in a context; writes a 0600 session token)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...devOptions }) {
|
|
6917
7431
|
if (devOptions.author || devOptions['authorMode']) {
|
|
6918
7432
|
process.env.EXTENSION_AUTHOR_MODE = 'true';
|
|
6919
7433
|
if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
|
|
@@ -6922,6 +7436,13 @@ Cross-Browser Compatibility
|
|
|
6922
7436
|
validateVendorsOrExit(list, (invalid, supported)=>{
|
|
6923
7437
|
console.error(unsupportedBrowserFlag(invalid, supported));
|
|
6924
7438
|
});
|
|
7439
|
+
if (list.some(isSafariVendor)) {
|
|
7440
|
+
const issue = safariPreflightError();
|
|
7441
|
+
if (issue) {
|
|
7442
|
+
console.error(issue);
|
|
7443
|
+
process.exit(1);
|
|
7444
|
+
}
|
|
7445
|
+
}
|
|
6925
7446
|
if (devOptions.wait) {
|
|
6926
7447
|
const waitResult = await runWaitMode({
|
|
6927
7448
|
command: 'dev',
|
|
@@ -7000,7 +7521,17 @@ Cross-Browser Compatibility
|
|
|
7000
7521
|
logColor: false !== devOptions.logColor,
|
|
7001
7522
|
logUrl: devOptions.logUrl,
|
|
7002
7523
|
logTab: devOptions.logTab,
|
|
7003
|
-
launcher: noBrowser ? void 0 : browsers.launchBrowser
|
|
7524
|
+
launcher: noBrowser ? void 0 : browsers.launchBrowser,
|
|
7525
|
+
safariPackager: async (distPath, mode)=>{
|
|
7526
|
+
await packageSafariExtension({
|
|
7527
|
+
extension: [
|
|
7528
|
+
distPath
|
|
7529
|
+
],
|
|
7530
|
+
browser: vendor,
|
|
7531
|
+
noOpen: false === devOptions.open,
|
|
7532
|
+
dryRun: false
|
|
7533
|
+
}, distPath, void 0, mode);
|
|
7534
|
+
}
|
|
7004
7535
|
};
|
|
7005
7536
|
await extensionDev(pathOrRemoteUrl, devArgs);
|
|
7006
7537
|
}
|
|
@@ -7017,6 +7548,10 @@ Cross-Browser Compatibility
|
|
|
7017
7548
|
validateVendorsOrExit(list, (invalid, supported)=>{
|
|
7018
7549
|
console.error(unsupportedBrowserFlag(invalid, supported));
|
|
7019
7550
|
});
|
|
7551
|
+
if (list.some(isSafariVendor)) {
|
|
7552
|
+
console.error(safariCommandNotSupported('start'));
|
|
7553
|
+
process.exit(1);
|
|
7554
|
+
}
|
|
7020
7555
|
if (startOptions.wait) {
|
|
7021
7556
|
const waitResult = await runWaitMode({
|
|
7022
7557
|
command: 'start',
|
|
@@ -7058,6 +7593,7 @@ Cross-Browser Compatibility
|
|
|
7058
7593
|
geckoBinary: startOptions.geckoBinary,
|
|
7059
7594
|
startingUrl: startOptions.startingUrl,
|
|
7060
7595
|
port: startOptions.port,
|
|
7596
|
+
host: startOptions.host,
|
|
7061
7597
|
noBrowser: false,
|
|
7062
7598
|
extensions: parseExtensionsList(startOptions.extensions),
|
|
7063
7599
|
metadataCommand: 'start',
|
|
@@ -7082,6 +7618,10 @@ Cross-Browser Compatibility
|
|
|
7082
7618
|
validateVendorsOrExit(list, (invalid, supported)=>{
|
|
7083
7619
|
console.error(unsupportedBrowserFlag(invalid, supported));
|
|
7084
7620
|
});
|
|
7621
|
+
if (list.some(isSafariVendor)) {
|
|
7622
|
+
console.error(safariCommandNotSupported('preview'));
|
|
7623
|
+
process.exit(1);
|
|
7624
|
+
}
|
|
7085
7625
|
if (!process.env.EXTJS_LIGHT) {
|
|
7086
7626
|
const isRemote = 'string' == typeof pathOrRemoteUrl && /^https?:/i.test(pathOrRemoteUrl);
|
|
7087
7627
|
if (isRemote) process.env.EXTJS_LIGHT = '1';
|
|
@@ -7113,7 +7653,7 @@ Cross-Browser Compatibility
|
|
|
7113
7653
|
});
|
|
7114
7654
|
}
|
|
7115
7655
|
function registerBuildCommand(program) {
|
|
7116
|
-
program.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description(commandDescriptions.build).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based>', 'specify a browser/engine to run. Defaults to `chromium`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').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('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
|
|
7656
|
+
program.command('build').arguments('[project-name]').usage('build [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`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').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('--author, --author-mode', '[internal] enable maintainer diagnostics (does not affect user runtime logs)').action(async function(pathOrRemoteUrl, { browser = 'chromium', ...buildOptions }) {
|
|
7117
7657
|
if (buildOptions.author || buildOptions['authorMode']) {
|
|
7118
7658
|
process.env.EXTENSION_AUTHOR_MODE = 'true';
|
|
7119
7659
|
if (!process.env.EXTENSION_VERBOSE) process.env.EXTENSION_VERBOSE = '1';
|
|
@@ -7131,6 +7671,13 @@ Cross-Browser Compatibility
|
|
|
7131
7671
|
process.exit(1);
|
|
7132
7672
|
}
|
|
7133
7673
|
}
|
|
7674
|
+
if (list.some(isSafariVendor)) {
|
|
7675
|
+
const issue = safariPreflightError();
|
|
7676
|
+
if (issue) {
|
|
7677
|
+
console.error(issue);
|
|
7678
|
+
process.exit(1);
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7134
7681
|
const { extensionBuild } = await loadExtensionDevelopModule();
|
|
7135
7682
|
for (const vendor of list)await extensionBuild(pathOrRemoteUrl, {
|
|
7136
7683
|
browser: vendor,
|
|
@@ -7141,39 +7688,388 @@ Cross-Browser Compatibility
|
|
|
7141
7688
|
silent: buildOptions.silent,
|
|
7142
7689
|
install: buildOptions.install,
|
|
7143
7690
|
extensions: parseExtensionsList(buildOptions.extensions),
|
|
7144
|
-
mode
|
|
7691
|
+
mode,
|
|
7692
|
+
safariPackager: async (distPath, packagerMode)=>{
|
|
7693
|
+
await packageSafariExtension({
|
|
7694
|
+
extension: [
|
|
7695
|
+
distPath
|
|
7696
|
+
],
|
|
7697
|
+
browser: vendor,
|
|
7698
|
+
noOpen: !!buildOptions.silent,
|
|
7699
|
+
dryRun: false
|
|
7700
|
+
}, distPath, void 0, packagerMode);
|
|
7701
|
+
}
|
|
7145
7702
|
});
|
|
7146
7703
|
});
|
|
7147
7704
|
}
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7705
|
+
const LEVEL_ORDER = [
|
|
7706
|
+
'error',
|
|
7707
|
+
'warn',
|
|
7708
|
+
'info',
|
|
7709
|
+
'debug',
|
|
7710
|
+
'trace'
|
|
7711
|
+
];
|
|
7712
|
+
function levelRank(level) {
|
|
7713
|
+
const l = 'log' === level ? 'info' : level;
|
|
7714
|
+
const i = LEVEL_ORDER.indexOf(l);
|
|
7715
|
+
return -1 === i ? LEVEL_ORDER.length : i;
|
|
7716
|
+
}
|
|
7717
|
+
function makeUrlMatcher(pattern) {
|
|
7718
|
+
const hasGlob = pattern.includes('*');
|
|
7719
|
+
let re = null;
|
|
7720
|
+
if (hasGlob) {
|
|
7721
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
7722
|
+
re = new RegExp(escaped);
|
|
7723
|
+
}
|
|
7724
|
+
return (event)=>{
|
|
7725
|
+
const candidates = [
|
|
7726
|
+
event.url,
|
|
7727
|
+
event.hostname
|
|
7728
|
+
].filter((v)=>'string' == typeof v);
|
|
7729
|
+
if (0 === candidates.length) return false;
|
|
7730
|
+
return candidates.some((c)=>re ? re.test(c) : c.includes(pattern));
|
|
7731
|
+
};
|
|
7732
|
+
}
|
|
7733
|
+
function makeFilter(opts) {
|
|
7734
|
+
const minLevel = String(opts.level || 'all').toLowerCase();
|
|
7735
|
+
const contexts = opts.context && 'all' !== opts.context.toLowerCase() ? new Set(opts.context.split(',').map((c)=>c.trim())) : null;
|
|
7736
|
+
const sinceSeq = null != opts.since ? Number(opts.since) : null;
|
|
7737
|
+
const urlMatches = opts.url ? makeUrlMatcher(opts.url) : null;
|
|
7738
|
+
const tabId = null != opts.tab && '' !== opts.tab ? Number(opts.tab) : null;
|
|
7739
|
+
return (event)=>{
|
|
7740
|
+
if (!event || 'object' != typeof event) return false;
|
|
7741
|
+
if ('header' === event.type) return false;
|
|
7742
|
+
if (opts.signalsOnly && 'dx.signal' !== event.eventType) return false;
|
|
7743
|
+
if (contexts && !contexts.has(event.context)) return false;
|
|
7744
|
+
if ('all' !== minLevel && 'off' !== minLevel) {
|
|
7745
|
+
if (levelRank(event.level) > levelRank(minLevel)) return false;
|
|
7746
|
+
}
|
|
7747
|
+
if (null != sinceSeq && Number.isFinite(sinceSeq) && 'number' == typeof event.seq && event.seq <= sinceSeq) return false;
|
|
7748
|
+
if (urlMatches && !urlMatches(event)) return false;
|
|
7749
|
+
if (null != tabId && Number.isFinite(tabId) && event.tabId !== tabId) return false;
|
|
7750
|
+
return true;
|
|
7751
|
+
};
|
|
7752
|
+
}
|
|
7753
|
+
function resolveFormat(opts) {
|
|
7754
|
+
if (opts.output) return opts.output;
|
|
7755
|
+
return process.stdout.isTTY ? 'pretty' : 'ndjson';
|
|
7756
|
+
}
|
|
7757
|
+
function printEvent(event, format) {
|
|
7758
|
+
if ('ndjson' === format) return void console.log(JSON.stringify(event));
|
|
7759
|
+
if ('json' === format) return void console.log(JSON.stringify(event, null, 2));
|
|
7760
|
+
const parts = Array.isArray(event.messageParts) ? event.messageParts.map((p)=>'string' == typeof p ? p : JSON.stringify(p)).join(' ') : '';
|
|
7761
|
+
const code = event.code ? ` ${event.code}` : '';
|
|
7762
|
+
const remediation = event.remediation ? `\n ↳ ${event.remediation}` : '';
|
|
7763
|
+
console.log(`[${event.seq ?? '-'}] ${String(event.level || 'log').toUpperCase()} (${event.context})${code} ${parts}${remediation}`);
|
|
7764
|
+
}
|
|
7765
|
+
function logsFilePath(projectPath, browser) {
|
|
7766
|
+
return external_path_default().resolve(projectPath, 'dist', 'extension-js', browser, 'logs.ndjson');
|
|
7767
|
+
}
|
|
7768
|
+
function registerLogsCommand(program) {
|
|
7769
|
+
program.command('logs').arguments('[project-path]').usage('logs [project-path] [options]').description('Print or stream logs from every context of a running dev session (agent bridge)').option('--browser <chrome | chromium | edge | firefox>', 'which dist/extension-js/<browser> to read. Defaults to `chromium`').option('--follow', 'stream live via the control channel instead of printing and exiting').option('--context <list>', 'comma-separated contexts (background, content, popup, options, sidebar, devtools, page)').option('--level <off|error|warn|info|debug|trace|all>', 'minimum severity to show. Defaults to `all`').option('--signals-only', 'show only structured dx.signal diagnostics').option('--since <seq|iso>', 'only show events after this sequence number').option('--url <glob|substring>', 'only events whose url/hostname matches (glob with * or plain substring)').option('--tab <id>', 'only events from this tab id').option('--output <pretty|json|ndjson>', 'output format. Defaults to pretty on a TTY, ndjson when piped').action(async function(projectPathArg, options) {
|
|
7770
|
+
const projectPath = external_path_default().resolve(projectPathArg || process.cwd());
|
|
7771
|
+
const browser = options.browser || 'chromium';
|
|
7772
|
+
const format = resolveFormat(options);
|
|
7773
|
+
const matches = makeFilter(options);
|
|
7774
|
+
if (options.follow) return void await followLogs(projectPath, browser, format, matches);
|
|
7775
|
+
const file = logsFilePath(projectPath, browser);
|
|
7776
|
+
if (!external_fs_default().existsSync(file)) {
|
|
7777
|
+
console.error(`No logs found at ${file}. Start a dev session (extension dev) first, or pass --browser to match it.`);
|
|
7778
|
+
process.exit(1);
|
|
7779
|
+
}
|
|
7780
|
+
const lines = external_fs_default().readFileSync(file, 'utf-8').split('\n').filter(Boolean);
|
|
7781
|
+
for (const line of lines){
|
|
7782
|
+
let event;
|
|
7783
|
+
try {
|
|
7784
|
+
event = JSON.parse(line);
|
|
7785
|
+
} catch {
|
|
7786
|
+
continue;
|
|
7787
|
+
}
|
|
7788
|
+
if (matches(event)) printEvent(event, format);
|
|
7789
|
+
}
|
|
7790
|
+
});
|
|
7791
|
+
}
|
|
7792
|
+
async function followLogs(projectPath, browser, format, matches) {
|
|
7793
|
+
const { BridgeConsumer, readReadyContract } = await loadExtensionDevelopBridgeModule();
|
|
7794
|
+
const ready = readReadyContract(projectPath, browser);
|
|
7795
|
+
if (!ready) {
|
|
7796
|
+
console.error(`No active dev session control channel found for ${browser}. Run \`extension dev --browser=${browser}\` first.`);
|
|
7797
|
+
process.exit(1);
|
|
7159
7798
|
}
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7799
|
+
const consumer = new BridgeConsumer({
|
|
7800
|
+
controlPort: ready.controlPort,
|
|
7801
|
+
instanceId: ready.instanceId,
|
|
7802
|
+
reconnect: true,
|
|
7803
|
+
onLog: (event)=>{
|
|
7804
|
+
if (matches(event)) printEvent(event, format);
|
|
7805
|
+
},
|
|
7806
|
+
onGap: (gap)=>{
|
|
7807
|
+
console.error(`… ${gap.dropped} event(s) dropped (${gap.reason}) — stream is behind`);
|
|
7808
|
+
}
|
|
7809
|
+
});
|
|
7810
|
+
const shutdown = ()=>{
|
|
7811
|
+
consumer.close();
|
|
7812
|
+
process.exit(0);
|
|
7813
|
+
};
|
|
7814
|
+
process.on('SIGINT', shutdown);
|
|
7815
|
+
process.on('SIGTERM', shutdown);
|
|
7816
|
+
consumer.start();
|
|
7817
|
+
await new Promise(()=>{});
|
|
7818
|
+
}
|
|
7819
|
+
function readRecentConsole(projectPath, browser, target, limit) {
|
|
7820
|
+
const file = external_path_default().resolve(projectPath, 'dist', 'extension-js', browser, 'logs.ndjson');
|
|
7821
|
+
let lines;
|
|
7822
|
+
try {
|
|
7823
|
+
lines = external_fs_default().readFileSync(file, 'utf-8').split('\n').filter(Boolean);
|
|
7824
|
+
} catch {
|
|
7825
|
+
return [];
|
|
7164
7826
|
}
|
|
7165
|
-
const
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7827
|
+
const out = [];
|
|
7828
|
+
for (const line of lines){
|
|
7829
|
+
let e;
|
|
7830
|
+
try {
|
|
7831
|
+
e = JSON.parse(line);
|
|
7832
|
+
} catch {
|
|
7833
|
+
continue;
|
|
7834
|
+
}
|
|
7835
|
+
if (e && 'header' !== e.type) {
|
|
7836
|
+
if (!target.context || e.context === target.context) {
|
|
7837
|
+
if (null == target.tabId || e.tabId === target.tabId) out.push({
|
|
7838
|
+
seq: e.seq,
|
|
7839
|
+
level: e.level,
|
|
7840
|
+
context: e.context,
|
|
7841
|
+
messageParts: e.messageParts,
|
|
7842
|
+
eventType: e.eventType,
|
|
7843
|
+
code: e.code,
|
|
7844
|
+
tabId: e.tabId
|
|
7845
|
+
});
|
|
7846
|
+
}
|
|
7847
|
+
}
|
|
7848
|
+
}
|
|
7849
|
+
return out.slice(Math.max(0, out.length - limit));
|
|
7850
|
+
}
|
|
7851
|
+
function fail(message) {
|
|
7852
|
+
console.error(message);
|
|
7853
|
+
process.exit(1);
|
|
7854
|
+
}
|
|
7855
|
+
function printResult(result, output) {
|
|
7856
|
+
if ('json' === output) return void console.log(JSON.stringify(result));
|
|
7857
|
+
if (result.ok) {
|
|
7858
|
+
const value = result.value;
|
|
7859
|
+
console.log('string' == typeof value ? value : JSON.stringify(value, null, 2));
|
|
7860
|
+
if (result.truncated) console.error('… result truncated (byte cap)');
|
|
7861
|
+
return;
|
|
7862
|
+
}
|
|
7863
|
+
const err = result.error || {
|
|
7864
|
+
name: 'Error',
|
|
7865
|
+
message: 'command failed'
|
|
7866
|
+
};
|
|
7867
|
+
console.error(`${err.name}: ${err.message}${err.engine ? ` (engine: ${err.engine})` : ''}`);
|
|
7868
|
+
}
|
|
7869
|
+
async function runCommand(input) {
|
|
7870
|
+
const projectPath = external_path_default().resolve(input.projectPathArg || process.cwd());
|
|
7871
|
+
const browser = input.opts.browser || 'chromium';
|
|
7872
|
+
const { BridgeController, readReadyContract, readControlToken } = await loadExtensionDevelopBridgeModule();
|
|
7873
|
+
const ready = readReadyContract(projectPath, browser);
|
|
7874
|
+
if (!ready) fail(`No active control channel found for ${browser}. Run \`extension dev --browser=${browser} --allow-control\` first.`);
|
|
7875
|
+
const token = input.needsToken ? readControlToken(projectPath) : void 0;
|
|
7876
|
+
const controller = new BridgeController({
|
|
7877
|
+
controlPort: ready.controlPort,
|
|
7878
|
+
instanceId: ready.instanceId,
|
|
7879
|
+
token: token ?? void 0
|
|
7880
|
+
});
|
|
7881
|
+
try {
|
|
7882
|
+
await controller.connect();
|
|
7883
|
+
} catch (err) {
|
|
7884
|
+
controller.close();
|
|
7885
|
+
fail(err?.message || 'could not connect to the control channel');
|
|
7886
|
+
}
|
|
7887
|
+
const timeoutMs = input.opts.timeout ? Number(input.opts.timeout) : 5000;
|
|
7888
|
+
let result;
|
|
7889
|
+
try {
|
|
7890
|
+
result = await controller.command({
|
|
7891
|
+
op: input.op,
|
|
7892
|
+
target: input.target,
|
|
7893
|
+
args: input.args,
|
|
7894
|
+
timeoutMs
|
|
7895
|
+
});
|
|
7896
|
+
} catch (err) {
|
|
7897
|
+
controller.close();
|
|
7898
|
+
fail(err?.message || 'command failed');
|
|
7899
|
+
} finally{
|
|
7900
|
+
controller.close();
|
|
7901
|
+
}
|
|
7902
|
+
if (result && result.ok && input.augment) try {
|
|
7903
|
+
Object.assign(result, input.augment(projectPath, browser, result));
|
|
7904
|
+
} catch {}
|
|
7905
|
+
printResult(result, input.opts.output);
|
|
7906
|
+
process.exit(result.ok ? 0 : 1);
|
|
7170
7907
|
}
|
|
7171
|
-
function
|
|
7172
|
-
const
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7908
|
+
function targetFrom(opts, fallback = 'background') {
|
|
7909
|
+
const context = opts.context || fallback;
|
|
7910
|
+
const target = {
|
|
7911
|
+
context
|
|
7912
|
+
};
|
|
7913
|
+
if (opts.url) target.url = opts.url;
|
|
7914
|
+
if (null != opts.tab && '' !== opts.tab) target.tabId = Number(opts.tab);
|
|
7915
|
+
return target;
|
|
7916
|
+
}
|
|
7917
|
+
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)');
|
|
7918
|
+
function registerActCommands(program) {
|
|
7919
|
+
commonOptions(program.command('eval').arguments('<expression> [project-path]').description('Evaluate an expression in a running extension context (requires --allow-eval)').option('--context <background|popup|options|sidebar|devtools|content|page>', 'target context (default background)').option('--url <glob|substring>', 'for content/page: document(s) to target').option('--tab <id>', 'for content/page: a specific tab')).action(async function(expression, projectPathArg, opts) {
|
|
7920
|
+
await runCommand({
|
|
7921
|
+
projectPathArg,
|
|
7922
|
+
op: 'eval',
|
|
7923
|
+
target: targetFrom(opts),
|
|
7924
|
+
args: {
|
|
7925
|
+
expression
|
|
7926
|
+
},
|
|
7927
|
+
needsToken: true,
|
|
7928
|
+
opts
|
|
7929
|
+
});
|
|
7930
|
+
});
|
|
7931
|
+
commonOptions(program.command('storage').arguments('<action> [project-path]').description('Read or write chrome.storage in a running extension (requires --allow-control)').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 function(action, projectPathArg, opts) {
|
|
7932
|
+
const area = opts.area || 'local';
|
|
7933
|
+
if ('get' === action) return void await runCommand({
|
|
7934
|
+
projectPathArg,
|
|
7935
|
+
op: 'storage.get',
|
|
7936
|
+
target: targetFrom(opts),
|
|
7937
|
+
args: opts.key ? {
|
|
7938
|
+
area,
|
|
7939
|
+
key: opts.key
|
|
7940
|
+
} : {
|
|
7941
|
+
area
|
|
7942
|
+
},
|
|
7943
|
+
opts
|
|
7944
|
+
});
|
|
7945
|
+
if ('set' === action) {
|
|
7946
|
+
if (!opts.key || null == opts.value) fail('storage set requires --key and --value');
|
|
7947
|
+
let parsed;
|
|
7948
|
+
try {
|
|
7949
|
+
parsed = JSON.parse(opts.value);
|
|
7950
|
+
} catch {
|
|
7951
|
+
parsed = opts.value;
|
|
7952
|
+
}
|
|
7953
|
+
await runCommand({
|
|
7954
|
+
projectPathArg,
|
|
7955
|
+
op: 'storage.set',
|
|
7956
|
+
target: targetFrom(opts),
|
|
7957
|
+
args: {
|
|
7958
|
+
area,
|
|
7959
|
+
items: {
|
|
7960
|
+
[opts.key]: parsed
|
|
7961
|
+
}
|
|
7962
|
+
},
|
|
7963
|
+
opts
|
|
7964
|
+
});
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
fail(`unknown storage action: ${action} (use get or set)`);
|
|
7968
|
+
});
|
|
7969
|
+
commonOptions(program.command('reload').arguments('[project-path]').description('Reload a running extension or tab (requires --allow-control)').option('--context <background|content|page>', 'target context (default background)').option('--tab <id>', 'for content/page: a specific tab')).action(async function(projectPathArg, opts) {
|
|
7970
|
+
await runCommand({
|
|
7971
|
+
projectPathArg,
|
|
7972
|
+
op: 'reload',
|
|
7973
|
+
target: targetFrom(opts),
|
|
7974
|
+
opts
|
|
7975
|
+
});
|
|
7976
|
+
});
|
|
7977
|
+
commonOptions(program.command('inspect').arguments('[project-path]').description('Inspect a page/content DOM via the agent bridge (CDP-free; requires --allow-control). For closed shadow roots use dev --source --deep-dom.').option('--context <content|page|popup|options|sidebar|devtools>', 'what to inspect: content/page (needs --tab) or an open surface (default content)').option('--tab <id>', 'tab id to inspect (required for content/page)').option('--include <list>', 'comma-separated: html,summary (default summary)').option('--max-bytes <n>', 'cap on returned HTML bytes (default 262144)').option('--with-console [n]', 'also include the last n console lines for the target (default 20)')).action(async function(projectPathArg, opts) {
|
|
7978
|
+
const include = opts.include ? opts.include.split(',').map((s)=>s.trim()).filter(Boolean) : [
|
|
7979
|
+
'summary'
|
|
7980
|
+
];
|
|
7981
|
+
const target = targetFrom(opts, 'content');
|
|
7982
|
+
await runCommand({
|
|
7983
|
+
projectPathArg,
|
|
7984
|
+
op: 'inspect',
|
|
7985
|
+
target,
|
|
7986
|
+
args: {
|
|
7987
|
+
include,
|
|
7988
|
+
maxBytes: opts.maxBytes ? Number(opts.maxBytes) : void 0
|
|
7989
|
+
},
|
|
7990
|
+
opts,
|
|
7991
|
+
augment: opts.withConsole ? (projectPath, browser)=>{
|
|
7992
|
+
const n = 'string' == typeof opts.withConsole && '' !== opts.withConsole ? Number(opts.withConsole) : 20;
|
|
7993
|
+
return {
|
|
7994
|
+
console: readRecentConsole(projectPath, browser, target, Number.isFinite(n) && n > 0 ? n : 20)
|
|
7995
|
+
};
|
|
7996
|
+
} : void 0
|
|
7997
|
+
});
|
|
7998
|
+
});
|
|
7999
|
+
commonOptions(program.command('open').arguments('<surface> [project-path]').description('Open an extension surface — popup, options, or sidebar (requires --allow-control)')).action(async function(surface, projectPathArg, opts) {
|
|
8000
|
+
const allowed = [
|
|
8001
|
+
'popup',
|
|
8002
|
+
'options',
|
|
8003
|
+
'sidebar'
|
|
8004
|
+
];
|
|
8005
|
+
if (!allowed.includes(surface)) fail(`unknown surface: ${surface} (use popup, options, or sidebar)`);
|
|
8006
|
+
await runCommand({
|
|
8007
|
+
projectPathArg,
|
|
8008
|
+
op: 'open',
|
|
8009
|
+
target: {
|
|
8010
|
+
context: surface
|
|
8011
|
+
},
|
|
8012
|
+
args: {
|
|
8013
|
+
surface
|
|
8014
|
+
},
|
|
8015
|
+
opts
|
|
8016
|
+
});
|
|
8017
|
+
});
|
|
8018
|
+
}
|
|
8019
|
+
const DEFAULT_API = 'https://www.extension.dev';
|
|
8020
|
+
function buildPublishRequest(opts) {
|
|
8021
|
+
const token = String(opts.token || process.env.EXTENSION_DEV_TOKEN || '').trim();
|
|
8022
|
+
if (!token) throw new Error("No token. Set EXTENSION_DEV_TOKEN (create one in the extension.dev dashboard, or via the project access-tokens API) or pass --token.");
|
|
8023
|
+
const base = String(opts.api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, '');
|
|
8024
|
+
const body = {};
|
|
8025
|
+
if (null != opts.ttl && '' !== opts.ttl) body.ttlHours = Number(opts.ttl);
|
|
8026
|
+
if (opts.buildSha) body.buildSha = opts.buildSha;
|
|
8027
|
+
return {
|
|
8028
|
+
url: `${base}/api/cli/publish`,
|
|
8029
|
+
headers: {
|
|
8030
|
+
authorization: `Bearer ${token}`,
|
|
8031
|
+
'content-type': 'application/json'
|
|
8032
|
+
},
|
|
8033
|
+
body: JSON.stringify(body)
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
function registerPublishCommand(program) {
|
|
8037
|
+
program.command('publish').arguments('[project-path]').usage('publish [project-path] [options]').description('Publish to extension.dev and print a shareable URL (requires EXTENSION_DEV_TOKEN)').option('--token <token>', 'extension.dev access token (or EXTENSION_DEV_TOKEN)').option('--api <url>', 'platform base URL (or EXTENSION_DEV_API_URL)').option('--ttl <hours>', 'share-link lifetime in hours (1–168, default 24)').option('--build-sha <sha>', 'pin the share URL to a specific build').option('--output <pretty|json>', 'output format (default pretty)').action(async function(_projectPathArg, opts) {
|
|
8038
|
+
let req;
|
|
8039
|
+
try {
|
|
8040
|
+
req = buildPublishRequest(opts);
|
|
8041
|
+
} catch (err) {
|
|
8042
|
+
console.error(err?.message || 'publish failed');
|
|
8043
|
+
process.exit(1);
|
|
8044
|
+
}
|
|
8045
|
+
let res;
|
|
8046
|
+
try {
|
|
8047
|
+
res = await fetch(req.url, {
|
|
8048
|
+
method: 'POST',
|
|
8049
|
+
headers: req.headers,
|
|
8050
|
+
body: req.body
|
|
8051
|
+
});
|
|
8052
|
+
} catch (err) {
|
|
8053
|
+
console.error(`Could not reach ${req.url}: ${err?.message || err}`);
|
|
8054
|
+
process.exit(1);
|
|
8055
|
+
}
|
|
8056
|
+
const text = await res.text();
|
|
8057
|
+
let data;
|
|
8058
|
+
try {
|
|
8059
|
+
data = JSON.parse(text);
|
|
8060
|
+
} catch {
|
|
8061
|
+
data = {
|
|
8062
|
+
message: text
|
|
8063
|
+
};
|
|
8064
|
+
}
|
|
8065
|
+
if (!res.ok) {
|
|
8066
|
+
console.error(`publish failed (${res.status}): ${data?.message || text || 'unknown error'}`);
|
|
8067
|
+
process.exit(1);
|
|
8068
|
+
}
|
|
8069
|
+
if ('json' === opts.output) console.log(JSON.stringify(data));
|
|
8070
|
+
else console.log(data.shareUrl || JSON.stringify(data));
|
|
8071
|
+
process.exit(0);
|
|
8072
|
+
});
|
|
7177
8073
|
}
|
|
7178
8074
|
function registerInstallCommand(program) {
|
|
7179
8075
|
program.command('install').arguments('[browser-name]').usage('[browser-name] [options]').description(commandDescriptions.install).option('--browser <chrome | chromium | edge | firefox | chromium-based | gecko-based | firefox-based | all>', 'override the positional browser name. Supports comma-separated values and `all`.').option('--where', 'print the resolved managed browser cache root').action(async function(browserArg, options) {
|
|
@@ -7182,37 +8078,35 @@ Cross-Browser Compatibility
|
|
|
7182
8078
|
validateVendorsOrExit(browserList, (invalid, supported)=>{
|
|
7183
8079
|
console.error(unsupportedBrowserFlag(invalid, supported));
|
|
7184
8080
|
});
|
|
8081
|
+
const { extensionInstall, getManagedBrowsersCacheRoot, getManagedBrowserInstallDir } = await import("extension-install");
|
|
7185
8082
|
if (options.where) {
|
|
7186
|
-
const
|
|
7187
|
-
|
|
7188
|
-
else console.log(root);
|
|
8083
|
+
if (options.browser || browserArg) for (const browser of browserList)console.log(getManagedBrowserInstallDir(browser));
|
|
8084
|
+
else console.log(getManagedBrowsersCacheRoot());
|
|
7189
8085
|
return;
|
|
7190
8086
|
}
|
|
7191
|
-
const { extensionInstall } = await import("extension-install");
|
|
7192
8087
|
for (const browser of browserList)await extensionInstall({
|
|
7193
8088
|
browser
|
|
7194
8089
|
});
|
|
7195
8090
|
});
|
|
7196
8091
|
program.command('uninstall').usage('uninstall <browser-name> | uninstall --all | uninstall --where').description(commandDescriptions.uninstall).option('--browser <browser-name>', 'browser to uninstall').option('--all', 'remove all managed browser binaries').option('--where', 'print the resolved managed browser cache root').argument('[browser-name]').action(async function(browserArg, { browser, all, where }) {
|
|
7197
8092
|
const target = browserArg || browser;
|
|
8093
|
+
const { extensionUninstall, getManagedBrowsersCacheRoot, getManagedBrowserInstallDir } = await import("extension-install");
|
|
7198
8094
|
if (where) {
|
|
7199
|
-
const root = resolveManagedBrowsersCacheRoot();
|
|
7200
8095
|
if (all) for (const browser of [
|
|
7201
8096
|
'chrome',
|
|
7202
8097
|
'chromium',
|
|
7203
8098
|
'edge',
|
|
7204
8099
|
'firefox'
|
|
7205
|
-
])console.log(
|
|
8100
|
+
])console.log(getManagedBrowserInstallDir(browser));
|
|
7206
8101
|
else if (target) {
|
|
7207
8102
|
const list = vendors(target);
|
|
7208
8103
|
validateVendorsOrExit(list, (invalid, supported)=>{
|
|
7209
8104
|
console.error(unsupportedBrowserFlag(invalid, supported));
|
|
7210
8105
|
});
|
|
7211
|
-
for (const browser of list)console.log(
|
|
7212
|
-
} else console.log(
|
|
8106
|
+
for (const browser of list)console.log(getManagedBrowserInstallDir(browser));
|
|
8107
|
+
} else console.log(getManagedBrowsersCacheRoot());
|
|
7213
8108
|
return;
|
|
7214
8109
|
}
|
|
7215
|
-
const { extensionUninstall } = await import("extension-install");
|
|
7216
8110
|
await extensionUninstall({
|
|
7217
8111
|
browser: target,
|
|
7218
8112
|
all
|
|
@@ -7358,6 +8252,9 @@ Cross-Browser Compatibility
|
|
|
7358
8252
|
registerStartCommand(extensionJs);
|
|
7359
8253
|
registerPreviewCommand(extensionJs);
|
|
7360
8254
|
registerBuildCommand(extensionJs);
|
|
8255
|
+
registerLogsCommand(extensionJs);
|
|
8256
|
+
registerActCommands(extensionJs);
|
|
8257
|
+
registerPublishCommand(extensionJs);
|
|
7361
8258
|
registerInstallCommand(extensionJs);
|
|
7362
8259
|
registerTelemetryCommand(extensionJs);
|
|
7363
8260
|
extensionJs.on('option:ai-help', function() {
|