extension-develop 4.1.25-canary.408.177007 → 4.1.26

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.
@@ -1,6 +1,56 @@
1
1
  import { createRequire as __extjsCreateRequire } from "node:module"; const require = __extjsCreateRequire(import.meta.url);
2
- import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
3
2
  import * as __rspack_external_node_path_806ed179 from "node:path";
3
+ import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
4
+ __rspack_external_node_path_806ed179.join(process.cwd(), 'node_modules/extension-develop/dist/certs');
5
+ const CHROMIUM_BASED_BROWSERS = [
6
+ 'chrome',
7
+ 'edge',
8
+ 'brave',
9
+ 'opera',
10
+ 'vivaldi',
11
+ 'yandex'
12
+ ];
13
+ const GECKO_BASED_BROWSERS = [
14
+ 'firefox',
15
+ 'waterfox',
16
+ 'librewolf',
17
+ 'zen',
18
+ 'floorp'
19
+ ];
20
+ const WEBKIT_BASED_BROWSERS = [
21
+ 'safari'
22
+ ];
23
+ const CHROMIUM_FAMILY_ALIASES = [
24
+ 'chromium',
25
+ 'chrome',
26
+ 'edge',
27
+ 'chromium-based'
28
+ ];
29
+ const GECKO_FAMILY_ALIASES = [
30
+ 'firefox',
31
+ 'gecko-based'
32
+ ];
33
+ const EMULATOR_BROWSER = 'chromium-emulator';
34
+ function isEmulatorLaneEnabled(env = process.env) {
35
+ const value = String(env.EXTENSION_EXPERIMENTAL_EMULATOR || '').trim().toLowerCase();
36
+ return '1' === value || 'true' === value;
37
+ }
38
+ function isEmulatorBrowser(browser) {
39
+ return browser === EMULATOR_BROWSER;
40
+ }
41
+ [
42
+ ...CHROMIUM_BASED_BROWSERS,
43
+ ...GECKO_BASED_BROWSERS
44
+ ];
45
+ function isChromiumBasedBrowser(browser) {
46
+ return CHROMIUM_BASED_BROWSERS.includes(browser) || String(browser).includes('chromium');
47
+ }
48
+ function isGeckoBasedBrowser(browser) {
49
+ return GECKO_BASED_BROWSERS.includes(browser) || String(browser).includes('gecko') || String(browser).includes('firefox');
50
+ }
51
+ function isWebkitBasedBrowser(browser) {
52
+ return WEBKIT_BASED_BROWSERS.includes(browser) || String(browser).includes('webkit');
53
+ }
4
54
  function stripBom(text) {
5
55
  const raw = 'string' == typeof text ? text : String(text || '');
6
56
  return raw && 0xfeff === raw.charCodeAt(0) ? raw.slice(1) : raw;
@@ -208,4 +258,4 @@ function validateDenoConfig(denoConfigPath) {
208
258
  return false;
209
259
  }
210
260
  }
211
- export { PROJECT_MANIFEST_FILENAMES, findNearestDenoConfigSync, findNearestProjectManifestDirSync, findNearestProjectManifestSync, hasProjectDependency, parseJsonSafe, readProjectDependencies, stripBom, validateDenoConfig };
261
+ export { CHROMIUM_FAMILY_ALIASES, GECKO_FAMILY_ALIASES, PROJECT_MANIFEST_FILENAMES, findNearestDenoConfigSync, findNearestProjectManifestDirSync, findNearestProjectManifestSync, hasProjectDependency, isChromiumBasedBrowser, isEmulatorBrowser, isEmulatorLaneEnabled, isGeckoBasedBrowser, isWebkitBasedBrowser, parseJsonSafe, readProjectDependencies, stripBom, validateDenoConfig };
package/dist/672~0.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  import { createRequire as __extjsCreateRequire } from "node:module"; const require = __extjsCreateRequire(import.meta.url);
2
2
  import pintor from "pintor";
3
- import { isChromiumBasedBrowser, isGeckoBasedBrowser } from "./331~0.mjs";
4
- import { parseJsonSafe } from "./731~0.mjs";
3
+ import { isChromiumBasedBrowser, parseJsonSafe, isGeckoBasedBrowser } from "./474~0.mjs";
5
4
  import { prefix as messaging_prefix } from "./852~0.mjs";
6
5
  import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
7
6
  const CHROMIUM_VENDOR_PREFIXES = [
package/dist/832~0.mjs CHANGED
@@ -9,9 +9,8 @@ import node_path from "node:path";
9
9
  import { getSpecialFoldersData } from "browser-extension-manifest-fields";
10
10
  import pintor from "pintor";
11
11
  import { devtoolsEngineFor, managedDependencyConflict, resolveProjectStructureSync, getDirs, computePreviewOutputPath, eventsPath as session_paths_eventsPath, anotherDevSessionActive, previewingSourceFallback, asAbsolute, displayPath, previewing, starting, debugPreviewOutput, ensureSessionArtifactsIgnoreFile, debugDirs, debugBrowser, readyContractPath, configLoadingError, normalizeBrowser, collapseHomeDir, previewingCustomOutput, browserArtifactsDir, getProjectStructure, isUsingExperimentalConfig as messages_isUsingExperimentalConfig, getDistPath, configBrowserOrThrow } from "./950~0.mjs";
12
- import { isChromiumBasedBrowser, isWebkitBasedBrowser, isGeckoBasedBrowser } from "./331~0.mjs";
12
+ import { isChromiumBasedBrowser, isEmulatorBrowser, isGeckoBasedBrowser, readProjectDependencies, stripBom, parseJsonSafe, isWebkitBasedBrowser } from "./474~0.mjs";
13
13
  import { humanWarn, isDebug, browserRowValue, artifactNoun, prefix as messaging_prefix, card, humanLine } from "./852~0.mjs";
14
- import { stripBom, parseJsonSafe, readProjectDependencies } from "./731~0.mjs";
15
14
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
16
15
  import { dirname as __rspack_dirname } from "node:path";
17
16
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
@@ -19,7 +18,7 @@ import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
19
18
  import * as __rspack_external_node_os_4f3c9d58 from "node:os";
20
19
  import * as __rspack_external_node_path_806ed179 from "node:path";
21
20
  import * as __rspack_external_node_vm_ef240610 from "node:vm";
22
- var package_namespaceObject = /*#__PURE__*/ JSON.parse('{"rE":"4.1.25-canary.408.0177007","El":{"@prefresh/core":"1.5.9","@prefresh/utils":"1.2.1","@rspack/core":"2.2.3","@rspack/dev-server":"2.1.0","@rspack/plugin-preact-refresh":"2.0.1","@rspack/plugin-react-refresh":"2.0.2","@vue/compiler-sfc":"3.5.26","acorn":"^8.16.0","browser-extension-manifest-fields":"^3.0.1","case-sensitive-paths-webpack-plugin":"^2.4.0","content-security-policy-parser":"^0.6.0","cross-spawn":"^7.0.6","dotenv":"^17.2.3","es-module-lexer":"^3.0.2","extension-from-store":"^0.2.5","fflate":"^0.8.3","go-git-it":"^5.1.5","ignore":"^7.0.5","less":"4.9.1","less-loader":"13.0.0","parse5-utilities":"^1.0.0","pintor":"0.3.0","postcss":"8.5.23","postcss-loader":"8.2.1","postcss-preset-env":"11.1.1","postcss-scss":"4.0.9","preact":"10.27.3","prefers-yarn":"2.0.1","react-refresh":"0.18.0","sass-loader":"17.0.0","schema-utils":"^4.3.3","svelte-loader":"3.2.4","tiny-glob":"^0.2.9","vue":"3.5.26","vue-loader":"17.4.2","webextension-polyfill":"^0.12.0","webpack-merge":"^6.0.1","webpack-target-webextension":"^2.1.3","ws":"^8.20.1"}}');
21
+ var package_namespaceObject = /*#__PURE__*/ JSON.parse('{"rE":"4.1.26","El":{"@prefresh/core":"1.5.9","@prefresh/utils":"1.2.1","@rspack/core":"2.2.3","@rspack/dev-server":"2.1.0","@rspack/plugin-preact-refresh":"2.0.1","@rspack/plugin-react-refresh":"2.0.2","@vue/compiler-sfc":"3.5.26","acorn":"^8.16.0","browser-extension-manifest-fields":"^3.0.1","case-sensitive-paths-webpack-plugin":"^2.4.0","content-security-policy-parser":"^0.6.0","cross-spawn":"^7.0.6","dotenv":"^17.2.3","es-module-lexer":"^3.0.2","extension-from-store":"^0.2.5","fflate":"^0.8.3","go-git-it":"^5.1.5","ignore":"^7.0.5","less":"4.9.1","less-loader":"13.0.0","parse5-utilities":"^1.0.0","pintor":"0.3.0","postcss":"8.5.23","postcss-loader":"8.2.1","postcss-preset-env":"11.1.1","postcss-scss":"4.0.9","preact":"10.27.3","prefers-yarn":"2.0.1","react-refresh":"0.18.0","sass-loader":"17.0.0","schema-utils":"^4.3.3","svelte-loader":"3.2.4","tiny-glob":"^0.2.9","vue":"3.5.26","vue-loader":"17.4.2","webextension-polyfill":"^0.12.0","webpack-merge":"^6.0.1","webpack-target-webextension":"^2.1.3","ws":"^8.20.1"}}');
23
22
  const preloadedEnvKeys = new Set();
24
23
  function getPreloadedEnvKeys() {
25
24
  return preloadedEnvKeys;
@@ -526,7 +525,7 @@ function getSessionRunId(packageJsonDir, browser) {
526
525
  }
527
526
  function deriveDistExtensionId(browser, distPath) {
528
527
  try {
529
- if (isWebkitBasedBrowser(browser)) return;
528
+ if (isWebkitBasedBrowser(browser) || isEmulatorBrowser(browser)) return;
530
529
  if (!__rspack_external_node_fs_1b05aee1.existsSync(__rspack_external_node_path_806ed179.join(distPath, 'manifest.json'))) return;
531
530
  const id = isGeckoBasedBrowser(browser) ? geckoExtensionId(distPath) : chromiumExtensionId(distPath);
532
531
  return id || void 0;
@@ -630,7 +629,7 @@ function createPlaywrightMetadataWriter(options) {
630
629
  instanceId: options.instanceId,
631
630
  instanceExplicit: options.instanceExplicit
632
631
  })) humanWarn(anotherDevSessionActive(options.browser, liveOwner.pid, liveOwner.runId));
633
- const toManagedRecords = (dirs)=>Array.isArray(dirs) && dirs.length > 0 ? managedExtensionRecords(options.browser, dirs) : void 0;
632
+ const toManagedRecords = (dirs)=>Array.isArray(dirs) && dirs.length > 0 && !isEmulatorBrowser(options.browser) ? managedExtensionRecords(options.browser, dirs) : void 0;
634
633
  let managedExtensionsExplicit = void 0 !== options.managedExtensionDirs;
635
634
  let managedExtensions = toManagedRecords(options.managedExtensionDirs);
636
635
  const base = {
@@ -652,7 +651,11 @@ function createPlaywrightMetadataWriter(options) {
652
651
  controlPath: options.controlPath,
653
652
  logsPath: options.logsPath,
654
653
  toolchainVersion: package_namespaceObject.rE,
655
- ...readManifestProvenance(options.manifestPath)
654
+ ...readManifestProvenance(options.manifestPath),
655
+ ...isEmulatorBrowser(options.browser) ? {
656
+ engine: 'emulator',
657
+ browserPid: null
658
+ } : {}
656
659
  };
657
660
  function writeReady(status, extra) {
658
661
  if (foreignLiveDevSession) return;
@@ -1512,6 +1515,7 @@ function readJsonRecord(filePath) {
1512
1515
  }
1513
1516
  }
1514
1517
  function capitalizeToken(value) {
1518
+ if ('chromium-emulator' === value) return 'Emulated Chromium';
1515
1519
  return value.split('-').filter(Boolean).map((token)=>token.charAt(0).toUpperCase() + token.slice(1)).join('-');
1516
1520
  }
1517
1521
  function getExtensionVersion() {
@@ -1563,6 +1567,9 @@ function shouldWarnPortConflict(requested, actual) {
1563
1567
  const requestedPort = Number(requested);
1564
1568
  return Number.isFinite(requestedPort) && 0 !== requestedPort && requestedPort !== actual;
1565
1569
  }
1570
+ function emulatorViewerUrl(url) {
1571
+ return `${getLoggingPrefix('info')} Emulated Chromium runs in a web page. Open this address to load your extension:\n${pintor.underline(url)}`;
1572
+ }
1566
1573
  function portInUse(requestedPort, newPort) {
1567
1574
  return `${getLoggingPrefix('warn')} Port ${requestedPort} is in use.\nThe dev server listens on port ${newPort} instead.`;
1568
1575
  }
@@ -1878,4 +1885,4 @@ async function extensionPreview(pathOrRemoteUrl, previewOptions, browserLauncher
1878
1885
  metadata.writeShutdown(`the ${metadataCommand} session ended`);
1879
1886
  });
1880
1887
  }
1881
- export { BUILD_COMMAND_DEFAULTS, DEV_COMMAND_DEFAULTS, PlaywrightPlugin, START_BUILD_DEFAULTS, assertNoManagedDependencyConflicts, autoExitForceKill, autoExitModeEnabled, autoExitTriggered, browserRunnerDisabled, bundlerFatalError, bundlerRecompiling, computeExtensionsToLoad, createPlaywrightMetadataWriter, devServerRestarting, devServerStartTimeout, extensionJsRunnerError, extensionPreview, getPreloadedEnvKeys, getSessionRunId, getSpecialFoldersDataForCompiler, getSpecialFoldersDataForProjectRoot, loadBrowserConfig, loadCommandConfig, loadCustomConfig, loadProjectConfigDefaults, mergeOptionLayers, messages_ready, noEntrypointsDetected, package_namespaceObject, portInUse, publicFolderShadowed, publicMustBeAtProjectRoot, resolveCompanionExtensionDirs, resolveCompanionExtensionsConfig, runnerCleanupError, runnerUncaughtException, runnerUnhandledRejection, sanitize, serverRestartRequiredFromSpecialFolderMessageOnly, shouldWarnPortConflict, spacerLine, specialFolderChangeDetected, specialFoldersSetupSummary, stampReadyDistExtensionId, stampReadyKnownExtensionId, withDarkMode };
1888
+ export { BUILD_COMMAND_DEFAULTS, DEV_COMMAND_DEFAULTS, PlaywrightPlugin, START_BUILD_DEFAULTS, assertNoManagedDependencyConflicts, autoExitForceKill, autoExitModeEnabled, autoExitTriggered, browserRunnerDisabled, bundlerFatalError, bundlerRecompiling, computeExtensionsToLoad, createPlaywrightMetadataWriter, devServerRestarting, devServerStartTimeout, emulatorViewerUrl, extensionJsRunnerError, extensionPreview, getPreloadedEnvKeys, getSessionRunId, getSpecialFoldersDataForCompiler, getSpecialFoldersDataForProjectRoot, loadBrowserConfig, loadCommandConfig, loadCustomConfig, loadProjectConfigDefaults, mergeOptionLayers, messages_ready, noEntrypointsDetected, package_namespaceObject, portInUse, publicFolderShadowed, publicMustBeAtProjectRoot, resolveCompanionExtensionDirs, resolveCompanionExtensionsConfig, runnerCleanupError, runnerUncaughtException, runnerUnhandledRejection, sanitize, serverRestartRequiredFromSpecialFolderMessageOnly, shouldWarnPortConflict, spacerLine, specialFolderChangeDetected, specialFoldersSetupSummary, stampReadyDistExtensionId, stampReadyKnownExtensionId, withDarkMode };
package/dist/840~0.mjs CHANGED
@@ -9,8 +9,7 @@ import { pathToFileURL } from "node:url";
9
9
  import { EventEmitter } from "node:events";
10
10
  import { humanWarn, browserRowValue, CODES, ENVELOPE, prefix as messaging_prefix, card, isDebug, claimCardKey, isMachineOutput } from "./852~0.mjs";
11
11
  import { package_namespaceObject, loadCommandConfig, DEV_COMMAND_DEFAULTS, stampReadyDistExtensionId, loadProjectConfigDefaults, mergeOptionLayers, stampReadyKnownExtensionId, assertNoManagedDependencyConflicts, loadCustomConfig, START_BUILD_DEFAULTS, loadBrowserConfig, resolveCompanionExtensionsConfig, BUILD_COMMAND_DEFAULTS, getSpecialFoldersDataForProjectRoot, withDarkMode } from "./832~0.mjs";
12
- import { stripBom, hasProjectDependency, parseJsonSafe, findNearestProjectManifestDirSync } from "./731~0.mjs";
13
- import { isGeckoBasedBrowser } from "./331~0.mjs";
12
+ import { stripBom, hasProjectDependency, isGeckoBasedBrowser, parseJsonSafe, findNearestProjectManifestDirSync } from "./474~0.mjs";
14
13
  import { buildWarningsDetails, buildFailed, getDirs, zipArtifactReady, projectInstallFallbackToNpm, debugDirs, addonLintDependencyAttribution, browserLaunchFailed, collapseHomeDir, addonLintMore, extensionLoadStillRefused, extensionLoadRecovered, writingTypeDefinitionsError, operaBuildUnminified, projectInstallScriptsDisabled, authorInstallNotice, addonLintFailed, buildAssetsTree, addonLintSummary, addonLintFinding, relativeToDir, configBrowserOrThrow, needsInstall, projectInstallInWorkspaceRoot, buildCommandFailed, addonLintNotInstalled, writingTypeDefinitions, buildComplete, buildSummaryPath, displayPath, ensureSessionStateInProjectGitignore, ensureSessionArtifactsIgnoreFile, debugBrowser, normalizeBrowser, debugOutputPath, devCommandFailed, buildShareHint, getDistPath, getProjectStructure, debugSplitChunksNarrowed } from "./950~0.mjs";
15
14
  import { publicRootsFor } from "./105~0.mjs";
16
15
  import { getCanonicalContentScriptEntryName } from "./640~0.mjs";
@@ -2995,6 +2994,7 @@ class BrowsersPlugin {
2995
2994
  controller;
2996
2995
  reloadBroker;
2997
2996
  logSink;
2997
+ emulatorViewerUrl;
2998
2998
  lastReportedRefusal;
2999
2999
  constructor(options){
3000
3000
  this.options = options;
@@ -3005,6 +3005,9 @@ class BrowsersPlugin {
3005
3005
  setLogSink(sink) {
3006
3006
  this.logSink = sink;
3007
3007
  }
3008
+ setEmulatorViewerUrl(url) {
3009
+ this.emulatorViewerUrl = url;
3010
+ }
3008
3011
  async retryRefusedExtensionLoad() {
3009
3012
  const controller = this.controller;
3010
3013
  if (!controller?.retryExtensionLoad || !controller.getExtensionLoadRefusal) return false;
@@ -3055,7 +3058,10 @@ class BrowsersPlugin {
3055
3058
  outputPath,
3056
3059
  contextDir,
3057
3060
  extensionsToLoad: this.extensionsToLoad,
3058
- logSink: this.logSink
3061
+ logSink: this.logSink,
3062
+ ...this.emulatorViewerUrl ? {
3063
+ emulatorViewerUrl: this.emulatorViewerUrl
3064
+ } : {}
3059
3065
  });
3060
3066
  const logLevel = this.options.browserOptions.logLevel || 'off';
3061
3067
  if ('off' !== logLevel && this.controller) await this.controller.enableUnifiedLogging({
package/dist/950~0.mjs CHANGED
@@ -2,7 +2,7 @@ import { createRequire as __extjsCreateRequire } from "node:module"; const requi
2
2
  import pintor from "pintor";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { fmt, isDebug, artifactNoun, prefix as messaging_prefix, hasChannelPrefix } from "./852~0.mjs";
5
- import { findNearestDenoConfigSync, validateDenoConfig, PROJECT_MANIFEST_FILENAMES, parseJsonSafe, readProjectDependencies } from "./731~0.mjs";
5
+ import { findNearestDenoConfigSync, isEmulatorLaneEnabled, validateDenoConfig, isEmulatorBrowser, PROJECT_MANIFEST_FILENAMES, parseJsonSafe, readProjectDependencies } from "./474~0.mjs";
6
6
  import * as __rspack_external_node_path_806ed179 from "node:path";
7
7
  import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
8
8
  import * as __rspack_external_node_os_4f3c9d58 from "node:os";
@@ -821,7 +821,9 @@ const KNOWN_BROWSER_NAMES = new Set([
821
821
  'webkit-based'
822
822
  ]);
823
823
  function isKnownBrowserName(name) {
824
- return 'string' == typeof name && KNOWN_BROWSER_NAMES.has(name);
824
+ if ('string' != typeof name) return false;
825
+ if (isEmulatorBrowser(name)) return isEmulatorLaneEnabled();
826
+ return KNOWN_BROWSER_NAMES.has(name);
825
827
  }
826
828
  function configBrowserOrThrow(configBrowser, command) {
827
829
  if (null == configBrowser) return;
@@ -878,6 +880,9 @@ function normalizeBrowser(browser, chromiumBinary, geckoBinary, safariBinary) {
878
880
  return 'safari';
879
881
  case 'webkit-based':
880
882
  return 'webkit-based';
883
+ case 'chromium-emulator':
884
+ if (isEmulatorLaneEnabled()) return 'chromium-emulator';
885
+ return 'chrome';
881
886
  default:
882
887
  return 'chrome';
883
888
  }
@@ -904,6 +909,7 @@ function devtoolsEngineFor(browser) {
904
909
  return 'edge';
905
910
  case 'chromium':
906
911
  case 'chromium-based':
912
+ case 'chromium-emulator':
907
913
  case 'brave':
908
914
  case 'opera':
909
915
  case 'vivaldi':
package/dist/bridge.mjs CHANGED
@@ -27,6 +27,7 @@ function readReadyContract(projectPath, browser = 'chrome') {
27
27
  schema: 'number' == typeof c.schema ? c.schema : void 0,
28
28
  logsPath: c.logsPath,
29
29
  status: c.status,
30
+ engine: 'string' == typeof c.engine ? c.engine : void 0,
30
31
  pid: 'number' == typeof c.pid ? c.pid : void 0,
31
32
  cdpPort: 'number' == typeof c.cdpPort ? c.cdpPort : void 0,
32
33
  browserExitedAt: 'string' == typeof c.browserExitedAt ? c.browserExitedAt : void 0,
@@ -1,16 +1,18 @@
1
1
  import { type ActionsSink } from './actions-file.js';
2
- import { type AnyFrame, type CommandFrame, type DevReloadKind, type IncomingLogEvent, type ServerFrame } from './contracts.js';
2
+ import { type AnyFrame, type BridgeEngine, type CommandFrame, type DevReloadKind, type IncomingLogEvent, type ServerFrame } from './contracts.js';
3
3
  import type { LogsFileWriter } from './logs-file.js';
4
4
  import { LogRingBuffer } from './ring-buffer.js';
5
5
  export interface BridgeConnection {
6
6
  readonly id: string;
7
+ readonly origin?: string;
7
8
  send(frame: ServerFrame | CommandFrame): void;
8
9
  close(code?: number, reason?: string): void;
9
10
  }
10
11
  export interface BridgeBrokerOptions {
11
12
  instanceId: string;
12
13
  runId: string;
13
- engine?: 'chromium' | 'firefox' | 'webkit';
14
+ engine?: BridgeEngine;
15
+ viewerOrigin?: string | null;
14
16
  ring?: LogRingBuffer;
15
17
  file?: LogsFileWriter;
16
18
  allowControl?: boolean;
@@ -31,6 +33,8 @@ export declare class BridgeBroker {
31
33
  private readonly instanceId;
32
34
  private readonly runId;
33
35
  private readonly engine?;
36
+ private readonly viewerOrigin;
37
+ private viewerEverConnected;
34
38
  private readonly ring;
35
39
  private readonly file?;
36
40
  private readonly roles;
@@ -57,6 +61,7 @@ export declare class BridgeBroker {
57
61
  get consumerCount(): number;
58
62
  get producerCount(): number;
59
63
  get controllerCount(): number;
64
+ get viewerCount(): number;
60
65
  get pendingCount(): number;
61
66
  onFrame(conn: BridgeConnection, frame: AnyFrame): void;
62
67
  onClose(conn: BridgeConnection): void;
@@ -83,5 +88,6 @@ export declare class BridgeBroker {
83
88
  private firstExecutor;
84
89
  private audit;
85
90
  private fanOut;
91
+ private isReloadReceiver;
86
92
  private countRole;
87
93
  }
@@ -10,6 +10,7 @@ export interface ReadyContractInfo {
10
10
  schema?: number;
11
11
  logsPath?: string;
12
12
  status?: string;
13
+ engine?: string;
13
14
  /** Dev-server pid; absent in pre-4.1 contracts. */
14
15
  pid?: number;
15
16
  /** Browser CDP port, stamped post-launch, may lag `status: 'ready'`. */
@@ -33,7 +33,8 @@ export interface LogEvent {
33
33
  export type IncomingLogEvent = Omit<LogEvent, 'seq'> & {
34
34
  seq?: number;
35
35
  };
36
- export type BridgeRole = 'producer' | 'consumer' | 'controller';
36
+ export type BridgeRole = 'producer' | 'consumer' | 'controller' | 'viewer';
37
+ export type BridgeEngine = 'chromium' | 'firefox' | 'webkit' | 'emulator';
37
38
  export interface BridgeTarget {
38
39
  context: 'background' | 'popup' | 'options' | 'sidebar' | 'devtools' | 'content' | 'page';
39
40
  url?: string;
@@ -68,7 +69,7 @@ export interface ReadyFrame {
68
69
  type: 'ready';
69
70
  runId: string;
70
71
  bufferedFrom?: number;
71
- engine?: 'chromium' | 'firefox' | 'webkit';
72
+ engine?: BridgeEngine;
72
73
  capabilities?: {
73
74
  eval?: boolean;
74
75
  storage?: boolean;
@@ -9,6 +9,8 @@ export interface StartControlServerOptions {
9
9
  host?: string;
10
10
  port?: number;
11
11
  path?: string;
12
+ viewerOrigin?: string | null;
12
13
  }
13
14
  export declare function isWebOrigin(origin: string | undefined): boolean;
15
+ export declare function isAdmittedOrigin(origin: string | undefined, viewerOrigin?: string | null): boolean;
14
16
  export declare function startControlServer(options: StartControlServerOptions): Promise<ControlServer>;
@@ -0,0 +1,16 @@
1
+ import type { ReadStream } from 'node:fs';
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+ export interface OutputFileSystemLike {
4
+ readFileSync?: (filePath: string) => Buffer;
5
+ }
6
+ export interface ActiveCompilerRef {
7
+ outputPath(): string | undefined;
8
+ outputFileSystem(): OutputFileSystemLike | undefined;
9
+ }
10
+ type ResponseData = Buffer | ReadStream;
11
+ interface ModifiedResponseData {
12
+ data: ResponseData;
13
+ byteLength: number;
14
+ }
15
+ export declare function createResponseDataRepair(ref: ActiveCompilerRef): (req: IncomingMessage, res: ServerResponse, data: ResponseData, byteLength: number) => ModifiedResponseData;
16
+ export {};
@@ -0,0 +1,56 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ export declare const EMULATOR_FILES_PATH = "/__extjs-emulator/files.json";
3
+ export declare const EMULATOR_FILES_VERSION: 1;
4
+ export declare const EMULATOR_WIRE_VERSION = "1";
5
+ export declare const DEFAULT_EMULATOR_ORIGIN = "https://browsers.extension.land";
6
+ export interface EmulatorFileEntry {
7
+ path: string;
8
+ size: number;
9
+ sha256: string;
10
+ }
11
+ export interface EmulatorFileIndex {
12
+ version: typeof EMULATOR_FILES_VERSION;
13
+ instanceId: string;
14
+ root: '/';
15
+ livereload?: {
16
+ path: string;
17
+ };
18
+ files: EmulatorFileEntry[];
19
+ }
20
+ export declare function normalizeWebOrigin(value: unknown): string | null;
21
+ export declare function resolveEmulatorOrigin(env?: NodeJS.ProcessEnv): string;
22
+ export declare function isSameWebOrigin(candidate: unknown, expected: string | null | undefined): boolean;
23
+ export declare function buildEmulatorViewerUrl(options: {
24
+ origin: string;
25
+ host: string;
26
+ port: number;
27
+ controlPort: number | null;
28
+ controlPath: string;
29
+ instanceId: string;
30
+ }): string;
31
+ export interface EmulatorAssetLike {
32
+ name: string;
33
+ source: {
34
+ buffer(): Buffer;
35
+ };
36
+ info?: {
37
+ hotModuleReplacement?: boolean;
38
+ related?: Record<string, string | string[] | undefined>;
39
+ };
40
+ }
41
+ export declare function buildEmulatorFileIndex(assets: Iterable<EmulatorAssetLike>, instanceId: string): EmulatorFileIndex;
42
+ export declare function resolveLivereloadPath(webSocketServer: unknown): string | null;
43
+ export interface EmulatorFileIndexHolder {
44
+ publish(assets: Iterable<EmulatorAssetLike>): EmulatorFileIndex;
45
+ reset(): void;
46
+ setLivereloadPath(socketPath: string | null): void;
47
+ current(): Promise<EmulatorFileIndex>;
48
+ }
49
+ export declare function createEmulatorFileIndexHolder(instanceId: string): EmulatorFileIndexHolder;
50
+ export declare function attachEmulatorFileIndex(compiler: unknown, holder: EmulatorFileIndexHolder): void;
51
+ export declare function createEmulatorFilesMiddlewareEntry(holder: EmulatorFileIndexHolder, devServer: unknown): {
52
+ name: string;
53
+ path: string;
54
+ middleware: (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => Promise<void>;
55
+ };
56
+ export declare function createEmulatorFilesMiddleware(holder: EmulatorFileIndexHolder): (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => Promise<void>;
@@ -6,6 +6,7 @@ export declare function browserRunnerDisabled(args: {
6
6
  distPath?: string;
7
7
  }): string;
8
8
  export declare function shouldWarnPortConflict(requested: unknown, actual: number): boolean;
9
+ export declare function emulatorViewerUrl(url: string): string;
9
10
  export declare function portInUse(requestedPort: number, newPort: number): string;
10
11
  export declare function extensionJsRunnerError(error: unknown): string;
11
12
  export declare function autoExitModeEnabled(ms: number): string;
@@ -1,5 +1,5 @@
1
1
  import { createRequire as __extjsCreateRequire } from "node:module"; const require = __extjsCreateRequire(import.meta.url);
2
- import { randomUUID } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  import node_fs from "node:fs";
4
4
  import { Writable } from "node:stream";
5
5
  import { rspack } from "@rspack/core";
@@ -7,14 +7,13 @@ import { RspackDevServer } from "@rspack/dev-server";
7
7
  import { merge } from "webpack-merge";
8
8
  import { WebSocket, WebSocketServer } from "ws";
9
9
  import { classifyReloadFromSources, attachLifecycleStream, readContentScriptCount, humanLine, createChangedSourcesTracker, buildSourceFeatureIndex, applySplitChunksGuard, createLifecycleStream, dispatchReload } from "./840~0.mjs";
10
- import { runnerCleanupError, autoExitForceKill, loadCommandConfig, sanitize, extensionJsRunnerError, portInUse, devServerStartTimeout, autoExitTriggered, loadProjectConfigDefaults, runnerUncaughtException, shouldWarnPortConflict, devServerRestarting, DEV_COMMAND_DEFAULTS, mergeOptionLayers, runnerUnhandledRejection, createPlaywrightMetadataWriter, autoExitModeEnabled, getSessionRunId, loadCustomConfig, loadBrowserConfig, resolveCompanionExtensionsConfig, getSpecialFoldersDataForProjectRoot } from "./832~0.mjs";
10
+ import { runnerCleanupError, autoExitForceKill, loadCommandConfig, sanitize, extensionJsRunnerError, portInUse, devServerStartTimeout, autoExitTriggered, loadProjectConfigDefaults, runnerUncaughtException, shouldWarnPortConflict, devServerRestarting, DEV_COMMAND_DEFAULTS, mergeOptionLayers, runnerUnhandledRejection, createPlaywrightMetadataWriter, autoExitModeEnabled, emulatorViewerUrl, getSessionRunId, loadCustomConfig, loadBrowserConfig, resolveCompanionExtensionsConfig, getSpecialFoldersDataForProjectRoot } from "./832~0.mjs";
11
11
  import { CONTROL_WS_PATH, writePersistedControlPort, writeControlToken, clearControlToken, readPersistedControlPort } from "./616~0.mjs";
12
- import { hasProjectDependency, findNearestProjectManifestDirSync } from "./731~0.mjs";
12
+ import { isGeckoBasedBrowser, hasProjectDependency, isEmulatorBrowser, findNearestProjectManifestDirSync, isWebkitBasedBrowser } from "./474~0.mjs";
13
13
  import { publicFolderOrDefault } from "./105~0.mjs";
14
14
  import { setupNoBrowserBannerOnFirstDone, DevSessionRestartScheduler, resolveConnectableHost, default as webpackConfig, bindDevSessionRestart, unbindDevSessionRestart, setupCompilerLifecycleHooks } from "./rspack-config~0.mjs";
15
15
  import { logsPath, controlPortFilePath, legacyControlPortFilePath, asAbsolute, getDistPath, ensureSessionStateInProjectGitignore, actionsPath, ensureSessionArtifactsIgnoreFile } from "./950~0.mjs";
16
16
  import { isDebug } from "./852~0.mjs";
17
- import { isGeckoBasedBrowser, isWebkitBasedBrowser } from "./331~0.mjs";
18
17
  import * as __rspack_external_node_os_4f3c9d58 from "node:os";
19
18
  import * as __rspack_external_node_path_806ed179 from "node:path";
20
19
  import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
@@ -181,6 +180,173 @@ function fnv1aHex(input) {
181
180
  }
182
181
  return (hash >>> 0).toString(16).padStart(8, '0');
183
182
  }
183
+ const EMULATOR_FILES_PATH = '/__extjs-emulator/files.json';
184
+ const EMULATOR_FILES_VERSION = 1;
185
+ const EMULATOR_WIRE_VERSION = '1';
186
+ const DEFAULT_EMULATOR_ORIGIN = 'https://browsers.extension.land';
187
+ function normalizeWebOrigin(value) {
188
+ const raw = String(value ?? '').trim();
189
+ if (!raw) return null;
190
+ let url;
191
+ try {
192
+ url = new URL(raw);
193
+ } catch {
194
+ return null;
195
+ }
196
+ if ('http:' !== url.protocol && 'https:' !== url.protocol) return null;
197
+ if (url.username || url.password) return null;
198
+ return url.origin;
199
+ }
200
+ function resolveEmulatorOrigin(env = process.env) {
201
+ const configured = String(env.EXTENSION_EMULATOR_ORIGIN || '').trim();
202
+ if (!configured) return DEFAULT_EMULATOR_ORIGIN;
203
+ const origin = normalizeWebOrigin(configured);
204
+ if (!origin) throw new Error(`EXTENSION_EMULATOR_ORIGIN must be an http or https origin, got: ${configured}`);
205
+ return origin;
206
+ }
207
+ function isSameWebOrigin(candidate, expected) {
208
+ if (!expected) return false;
209
+ const left = normalizeWebOrigin(candidate);
210
+ const right = normalizeWebOrigin(expected);
211
+ return null !== left && left === right;
212
+ }
213
+ function buildEmulatorViewerUrl(options) {
214
+ const host = options.host.includes(':') ? `[${options.host}]` : options.host;
215
+ const params = new URLSearchParams();
216
+ params.set('files', `http://${host}:${options.port}${EMULATOR_FILES_PATH}`);
217
+ if (null != options.controlPort) params.set('control', `ws://${host}:${options.controlPort}${options.controlPath}`);
218
+ params.set('instance', options.instanceId);
219
+ params.set('v', EMULATOR_WIRE_VERSION);
220
+ return `${options.origin}/chromium/#${params.toString()}`;
221
+ }
222
+ function hotModuleReplacementNames(assets) {
223
+ const names = new Set();
224
+ for (const asset of assets)if (asset.info?.hotModuleReplacement) {
225
+ names.add(asset.name);
226
+ for (const related of Object.values(asset.info.related || {}))for (const name of [].concat(related))if (name) names.add(name);
227
+ }
228
+ return names;
229
+ }
230
+ function isServableAssetName(name) {
231
+ if (!name || name.startsWith('/') || name.includes('\\')) return false;
232
+ return !/(?:^|\/)\.\.(?:\/|$)/.test(name);
233
+ }
234
+ function servedAssetPath(name) {
235
+ return name.split('/').filter((segment, index)=>'' !== segment && !('.' === segment && index > 0)).join('/');
236
+ }
237
+ function buildEmulatorFileIndex(assets, instanceId) {
238
+ const list = Array.from(assets);
239
+ const hotNames = hotModuleReplacementNames(list);
240
+ const files = [];
241
+ const listed = new Set();
242
+ for (const asset of list){
243
+ const name = String(asset.name || '');
244
+ if (!isServableAssetName(name) || hotNames.has(name)) continue;
245
+ const servedPath = servedAssetPath(name);
246
+ if (!servedPath || listed.has(servedPath)) continue;
247
+ listed.add(servedPath);
248
+ const bytes = asset.source.buffer();
249
+ files.push({
250
+ path: servedPath,
251
+ size: bytes.length,
252
+ sha256: createHash('sha256').update(bytes).digest('hex')
253
+ });
254
+ }
255
+ files.sort((a, b)=>a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
256
+ return {
257
+ version: EMULATOR_FILES_VERSION,
258
+ instanceId,
259
+ root: '/',
260
+ files
261
+ };
262
+ }
263
+ function resolveLivereloadPath(webSocketServer) {
264
+ if (!webSocketServer || 'object' != typeof webSocketServer) return null;
265
+ const options = webSocketServer.options;
266
+ const socketPath = options?.path;
267
+ return 'string' == typeof socketPath && socketPath.startsWith('/') ? socketPath : null;
268
+ }
269
+ function createEmulatorFileIndexHolder(instanceId) {
270
+ let latest = null;
271
+ let livereloadPath = null;
272
+ let waiters = [];
273
+ const withLivereload = (index)=>livereloadPath ? {
274
+ version: index.version,
275
+ instanceId: index.instanceId,
276
+ root: index.root,
277
+ livereload: {
278
+ path: livereloadPath
279
+ },
280
+ files: index.files
281
+ } : index;
282
+ return {
283
+ publish (assets) {
284
+ latest = buildEmulatorFileIndex(assets, instanceId);
285
+ const ready = waiters;
286
+ waiters = [];
287
+ for (const resolve of ready)resolve(withLivereload(latest));
288
+ return withLivereload(latest);
289
+ },
290
+ reset () {
291
+ latest = null;
292
+ },
293
+ setLivereloadPath (socketPath) {
294
+ livereloadPath = socketPath;
295
+ },
296
+ current () {
297
+ if (latest) return Promise.resolve(withLivereload(latest));
298
+ return new Promise((resolve)=>{
299
+ waiters.push(resolve);
300
+ });
301
+ }
302
+ };
303
+ }
304
+ function attachEmulatorFileIndex(compiler, holder) {
305
+ const done = compiler?.hooks?.done;
306
+ if (!done) return;
307
+ holder.reset();
308
+ done.tap({
309
+ name: 'extjs-emulator-files',
310
+ stage: -100
311
+ }, (stats)=>{
312
+ if (stats.compilation.errors?.length) return;
313
+ holder.publish(stats.compilation.getAssets());
314
+ });
315
+ }
316
+ function createEmulatorFilesMiddlewareEntry(holder, devServer) {
317
+ const options = devServer?.options;
318
+ holder.setLivereloadPath(resolveLivereloadPath(options?.webSocketServer));
319
+ return {
320
+ name: 'extjs-emulator-files',
321
+ path: EMULATOR_FILES_PATH,
322
+ middleware: createEmulatorFilesMiddleware(holder)
323
+ };
324
+ }
325
+ function createEmulatorFilesMiddleware(holder) {
326
+ return async (req, res, next)=>{
327
+ const method = String(req.method || 'GET').toUpperCase();
328
+ res.setHeader('Access-Control-Allow-Origin', '*');
329
+ res.setHeader('Access-Control-Allow-Private-Network', 'true');
330
+ res.setHeader('Cache-Control', 'no-store');
331
+ if ('OPTIONS' === method) {
332
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
333
+ res.statusCode = 204;
334
+ res.end();
335
+ return;
336
+ }
337
+ if ('GET' !== method && 'HEAD' !== method) return void next();
338
+ try {
339
+ const index = await holder.current();
340
+ const body = JSON.stringify(index);
341
+ res.statusCode = 200;
342
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
343
+ res.setHeader('Content-Length', Buffer.byteLength(body));
344
+ res.end('HEAD' === method ? void 0 : body);
345
+ } catch (error) {
346
+ next(error);
347
+ }
348
+ };
349
+ }
184
350
  const DEFAULT_RING_CAPACITY = 5000;
185
351
  class LogRingBuffer {
186
352
  capacity;
@@ -263,10 +429,16 @@ const UNDELIVERED_RELOAD_WARN = {
263
429
  'recently-disconnected': "SW not attached, your edit compiled but no page received it. The extension's service worker disconnected (MV3 workers idle out, or the browser may have exited). Reloads resume automatically once it reconnects."
264
430
  };
265
431
  const UNDELIVERED_RELOAD_WARN_WEBKIT_NEVER_CONNECTED = "Extension not connected, your edit compiled but no page received it. The extension has not connected to the dev server this session. Open Safari > Settings > Extensions and turn it on. Safari restarts the extension itself once it is enabled, and reloads resume automatically.";
432
+ const UNDELIVERED_RELOAD_WARN_EMULATOR = {
433
+ 'never-connected': "No emulated Chromium page is connected, your edit compiled but no page received it. Open the emulated Chromium URL the dev server printed; reloads resume automatically once the page connects.",
434
+ 'recently-disconnected': "The emulated Chromium page disconnected, your edit compiled but no page received it. Reopen the emulated Chromium URL the dev server printed; reloads resume automatically once the page reconnects."
435
+ };
266
436
  class BridgeBroker {
267
437
  instanceId;
268
438
  runId;
269
439
  engine;
440
+ viewerOrigin;
441
+ viewerEverConnected = false;
270
442
  ring;
271
443
  file;
272
444
  roles = new Map();
@@ -293,6 +465,7 @@ class BridgeBroker {
293
465
  this.instanceId = options.instanceId;
294
466
  this.runId = options.runId;
295
467
  this.engine = options.engine;
468
+ this.viewerOrigin = normalizeWebOrigin(options.viewerOrigin);
296
469
  this.ring = options.ring ?? new LogRingBuffer();
297
470
  this.file = options.file;
298
471
  this.allowEval = options.allowEval ?? false;
@@ -316,6 +489,9 @@ class BridgeBroker {
316
489
  get controllerCount() {
317
490
  return this.countRole('controller');
318
491
  }
492
+ get viewerCount() {
493
+ return this.countRole('viewer');
494
+ }
319
495
  get pendingCount() {
320
496
  return this.pending.size;
321
497
  }
@@ -334,7 +510,7 @@ class BridgeBroker {
334
510
  if ('producer' === this.roles.get(conn)) this.onResult(frame);
335
511
  return;
336
512
  case 'reload-ack':
337
- if ('producer' === this.roles.get(conn) && this.pendingReload?.reloadType === frame.reloadType) this.pendingReload = void 0;
513
+ if (this.isReloadReceiver(this.roles.get(conn)) && this.pendingReload?.reloadType === frame.reloadType) this.pendingReload = void 0;
338
514
  return;
339
515
  default:
340
516
  return;
@@ -368,7 +544,7 @@ class BridgeBroker {
368
544
  } : {}
369
545
  };
370
546
  let notified = 0;
371
- for (const [conn, role] of this.roles)if ('producer' === role) try {
547
+ for (const [conn, role] of this.roles)if (this.isReloadReceiver(role)) try {
372
548
  conn.send(frame);
373
549
  notified++;
374
550
  } catch {}
@@ -379,11 +555,12 @@ class BridgeBroker {
379
555
  const now = this.now();
380
556
  if (now - this.sessionStartedAt < UNDELIVERED_RELOAD_GRACE_MS) return null;
381
557
  if (null != this.lastStaleHelloAt && now - this.lastStaleHelloAt < STALE_RESYNC_HINT_MS) return null;
382
- const kind = this.producerEverConnected ? 'recently-disconnected' : 'never-connected';
558
+ const kind = this.producerEverConnected || this.viewerEverConnected ? 'recently-disconnected' : 'never-connected';
383
559
  if (options?.producerRestartExpected && 'recently-disconnected' === kind) return null;
384
560
  if (this.lastUndeliveredWarnKind === kind) return null;
385
561
  this.lastUndeliveredWarnKind = kind;
386
562
  if ('never-connected' === kind && 'webkit' === this.engine) return UNDELIVERED_RELOAD_WARN_WEBKIT_NEVER_CONNECTED;
563
+ if ('emulator' === this.engine) return UNDELIVERED_RELOAD_WARN_EMULATOR[kind];
387
564
  return UNDELIVERED_RELOAD_WARN[kind];
388
565
  }
389
566
  pingProducers() {
@@ -423,6 +600,26 @@ class BridgeBroker {
423
600
  }
424
601
  onHello(conn, hello) {
425
602
  if (1 !== hello.v) return void conn.close(4002, 'unsupported envelope version');
603
+ const fromViewerOrigin = isSameWebOrigin(conn.origin, this.viewerOrigin);
604
+ if ('viewer' === hello.role && !fromViewerOrigin) return void conn.close(4002, 'viewer not allowed from this origin');
605
+ if (fromViewerOrigin && 'viewer' !== hello.role) return void conn.close(4002, 'only the viewer role is allowed from a web origin');
606
+ if ('viewer' === hello.role) {
607
+ if (hello.instanceId !== this.instanceId) return void conn.close(4001, 'instanceId mismatch');
608
+ this.roles.set(conn, 'viewer');
609
+ this.viewerEverConnected = true;
610
+ this.lastUndeliveredWarnKind = null;
611
+ conn.send({
612
+ type: 'ready',
613
+ runId: this.runId,
614
+ engine: this.engine
615
+ });
616
+ if (this.pendingReload) {
617
+ const frame = this.pendingReload;
618
+ this.pendingReload = void 0;
619
+ conn.send(frame);
620
+ }
621
+ return;
622
+ }
426
623
  if (hello.instanceId !== this.instanceId) {
427
624
  if ('producer' === hello.role) {
428
625
  this.lastStaleHelloAt = this.now();
@@ -632,6 +829,9 @@ class BridgeBroker {
632
829
  conn.send(frame);
633
830
  } catch {}
634
831
  }
832
+ isReloadReceiver(role) {
833
+ return 'producer' === role || 'viewer' === role;
834
+ }
635
835
  countRole(role) {
636
836
  let n = 0;
637
837
  for (const r of this.roles.values())if (r === role) n++;
@@ -766,6 +966,10 @@ function isWebOrigin(origin) {
766
966
  const value = String(origin || '').trim();
767
967
  return 'null' === value.toLowerCase() || /^https?:\/\//i.test(value);
768
968
  }
969
+ function isAdmittedOrigin(origin, viewerOrigin) {
970
+ if (!isWebOrigin(origin)) return true;
971
+ return isSameWebOrigin(origin, viewerOrigin);
972
+ }
769
973
  function startControlServer(options) {
770
974
  const { broker } = options;
771
975
  const host = options.host ?? '127.0.0.1';
@@ -775,12 +979,14 @@ function startControlServer(options) {
775
979
  host,
776
980
  port: options.port ?? 0,
777
981
  path,
778
- verifyClient: (info)=>!isWebOrigin(info.origin)
982
+ verifyClient: (info)=>isAdmittedOrigin(info.origin, options.viewerOrigin)
779
983
  });
780
984
  wss.on('error', reject);
781
- wss.on('connection', (socket)=>{
985
+ wss.on('connection', (socket, request)=>{
986
+ const origin = request?.headers?.origin;
782
987
  const conn = {
783
988
  id: `c${++connSeq}`,
989
+ origin: 'string' == typeof origin ? origin : void 0,
784
990
  send (frame) {
785
991
  if (socket.readyState !== WebSocket.OPEN) return;
786
992
  if (socket.bufferedAmount > SLOW_CONSUMER_BYTES) {
@@ -909,6 +1115,35 @@ class PortManager {
909
1115
  this.currentInstance = null;
910
1116
  }
911
1117
  }
1118
+ function createResponseDataRepair(ref) {
1119
+ return (req, res, data, byteLength)=>{
1120
+ const untouched = {
1121
+ data,
1122
+ byteLength
1123
+ };
1124
+ if (0 !== byteLength) return untouched;
1125
+ if (!data || Buffer.isBuffer(data)) return untouched;
1126
+ if (req.headers.range || 206 === res.statusCode) return untouched;
1127
+ const outputPath = ref.outputPath();
1128
+ const outputFileSystem = ref.outputFileSystem();
1129
+ if (!outputPath || 'function' != typeof outputFileSystem?.readFileSync) return untouched;
1130
+ const pathname = decodeURIComponent(String(req.url || '').split('?')[0].split('#')[0]);
1131
+ const filename = __rspack_external_node_path_806ed179.join(outputPath, pathname);
1132
+ if (!filename.startsWith(__rspack_external_node_path_806ed179.join(outputPath, __rspack_external_node_path_806ed179.sep))) return untouched;
1133
+ let buffer;
1134
+ try {
1135
+ buffer = outputFileSystem.readFileSync(filename);
1136
+ } catch {
1137
+ return untouched;
1138
+ }
1139
+ if (0 === buffer.length) return untouched;
1140
+ if ('function' == typeof data.destroy) data.destroy();
1141
+ return {
1142
+ data: buffer,
1143
+ byteLength: buffer.length
1144
+ };
1145
+ };
1146
+ }
912
1147
  function shouldWriteAssetToDisk(filePath) {
913
1148
  if (/(?:^|[/\\])\.\.(?:[/\\]|$)/.test(filePath)) return false;
914
1149
  return !/(?:^|[/\\])manifest\.json$/i.test(filePath);
@@ -1124,6 +1359,8 @@ async function dev_server_devServer(projectStructure, devOptions) {
1124
1359
  process.env.EXTENSION_DEV_SERVER_PORT = String(port);
1125
1360
  process.env.EXTENSION_DEV_SERVER_PATH = '/ws';
1126
1361
  const browserName = String(devOptions.browser || 'chromium');
1362
+ const emulatorLane = isEmulatorBrowser(browserName);
1363
+ const emulatorOrigin = emulatorLane ? resolveEmulatorOrigin() : null;
1127
1364
  const primaryDistPath = getDistPath(asAbsolute(packageJsonDir), browserName);
1128
1365
  const bridgeLogsAbsPath = logsPath(packageJsonDir, browserName);
1129
1366
  ensureSessionArtifactsIgnoreFile(packageJsonDir);
@@ -1146,7 +1383,8 @@ async function dev_server_devServer(projectStructure, devOptions) {
1146
1383
  const bridgeBroker = new BridgeBroker({
1147
1384
  instanceId: currentInstance.instanceId,
1148
1385
  runId: sessionRunId,
1149
- engine: isGeckoBasedBrowser(browserName) ? 'firefox' : isWebkitBasedBrowser(browserName) ? 'webkit' : 'chromium',
1386
+ engine: emulatorLane ? 'emulator' : isGeckoBasedBrowser(browserName) ? 'firefox' : isWebkitBasedBrowser(browserName) ? 'webkit' : 'chromium',
1387
+ viewerOrigin: emulatorOrigin,
1150
1388
  ring: new LogRingBuffer(),
1151
1389
  file: bridgeLogFile,
1152
1390
  allowControl,
@@ -1195,13 +1433,15 @@ async function dev_server_devServer(projectStructure, devOptions) {
1195
1433
  controlServer = await startControlServer({
1196
1434
  broker: bridgeBroker,
1197
1435
  host: devServerHost,
1198
- port: preferredControlPort ?? 0
1436
+ port: preferredControlPort ?? 0,
1437
+ viewerOrigin: emulatorOrigin
1199
1438
  });
1200
1439
  } catch (error) {
1201
1440
  if (null == preferredControlPort) throw error;
1202
1441
  controlServer = await startControlServer({
1203
1442
  broker: bridgeBroker,
1204
- host: devServerHost
1443
+ host: devServerHost,
1444
+ viewerOrigin: emulatorOrigin
1205
1445
  });
1206
1446
  }
1207
1447
  bridgeControlPort = controlServer.port;
@@ -1213,6 +1453,16 @@ async function dev_server_devServer(projectStructure, devOptions) {
1213
1453
  }
1214
1454
  process.env.EXTENSION_INSTANCE_ID = currentInstance.instanceId;
1215
1455
  process.env.EXTENSION_CONTROL_PORT = null != bridgeControlPort ? String(bridgeControlPort) : '';
1456
+ const emulatorFiles = emulatorLane ? createEmulatorFileIndexHolder(currentInstance.instanceId) : null;
1457
+ const emulatorViewer = emulatorLane && emulatorOrigin ? buildEmulatorViewerUrl({
1458
+ origin: emulatorOrigin,
1459
+ host: connectableHost,
1460
+ port,
1461
+ controlPort: bridgeControlPort,
1462
+ controlPath: CONTROL_WS_PATH,
1463
+ instanceId: currentInstance.instanceId
1464
+ }) : null;
1465
+ if (emulatorViewer && launchedPlugin && 'function' == typeof launchedPlugin.setEmulatorViewerUrl) launchedPlugin.setEmulatorViewerUrl(emulatorViewer);
1216
1466
  process.once('exit', ()=>{
1217
1467
  try {
1218
1468
  bridgeLogFile.close();
@@ -1281,6 +1531,7 @@ async function dev_server_devServer(projectStructure, devOptions) {
1281
1531
  port
1282
1532
  });
1283
1533
  if (shouldWarnPortConflict(devOptions.port, port)) humanLine(portInUse(Number(devOptions.port), port));
1534
+ let activeCompiler = null;
1284
1535
  const serverConfig = {
1285
1536
  host: devServerHost,
1286
1537
  allowedHosts: 'all',
@@ -1293,7 +1544,11 @@ async function dev_server_devServer(projectStructure, devOptions) {
1293
1544
  writeToDisk: shouldWriteAssetToDisk,
1294
1545
  stats: {
1295
1546
  all: false
1296
- }
1547
+ },
1548
+ modifyResponseData: createResponseDataRepair({
1549
+ outputPath: ()=>activeCompiler?.options?.output?.path,
1550
+ outputFileSystem: ()=>activeCompiler?.outputFileSystem
1551
+ })
1297
1552
  },
1298
1553
  watchFiles: {
1299
1554
  paths: [
@@ -1317,6 +1572,12 @@ async function dev_server_devServer(projectStructure, devOptions) {
1317
1572
  headers: {
1318
1573
  'Access-Control-Allow-Origin': '*'
1319
1574
  },
1575
+ ...emulatorFiles ? {
1576
+ setupMiddlewares: (...args)=>[
1577
+ createEmulatorFilesMiddlewareEntry(emulatorFiles, args[1]),
1578
+ ...args[0]
1579
+ ]
1580
+ } : {},
1320
1581
  port,
1321
1582
  hot: true,
1322
1583
  liveReload: true
@@ -1327,6 +1588,7 @@ async function dev_server_devServer(projectStructure, devOptions) {
1327
1588
  const customWebpackConfig = await loadCustomConfig(packageJsonDir);
1328
1589
  const compilerConfig = applySplitChunksGuard(merge(customWebpackConfig(baseConfig), {}));
1329
1590
  const compiler = rspack(compilerConfig);
1591
+ activeCompiler = compiler;
1330
1592
  const uninstallManifestGuard = installManifestDiskWriteGuard(manifestOutputPath);
1331
1593
  const releaseOutputFsGuard = suppressManifestOutputWrites(compiler, manifestOutputPath);
1332
1594
  if (!extendedOptions.browsersPlugin && compiler?.hooks?.watchRun) {
@@ -1354,6 +1616,15 @@ async function dev_server_devServer(projectStructure, devOptions) {
1354
1616
  });
1355
1617
  });
1356
1618
  }
1619
+ if (emulatorFiles) attachEmulatorFileIndex(compiler, emulatorFiles);
1620
+ if (emulatorViewer && !opts.isRestart && devOptions.noBrowser) {
1621
+ let printedViewer = false;
1622
+ compiler.hooks.done.tap('extjs-emulator-viewer-url', (stats)=>{
1623
+ if (printedViewer || stats?.hasErrors?.()) return;
1624
+ printedViewer = true;
1625
+ humanLine(emulatorViewerUrl(emulatorViewer));
1626
+ });
1627
+ }
1357
1628
  attachLifecycleStream(compiler, lifecycle);
1358
1629
  setupCompilerLifecycleHooks(compiler);
1359
1630
  if (!opts.isRestart && devOptions.noBrowser) setupNoBrowserBannerOnFirstDone({
@@ -4,7 +4,7 @@ import node_path from "node:path";
4
4
  import { parse } from "es-module-lexer";
5
5
  import { validate } from "schema-utils";
6
6
  import { filterKeysForThisBrowser, reservedScriptsFolder } from "./672~0.mjs";
7
- import { stripBom, findNearestProjectManifestSync } from "./731~0.mjs";
7
+ import { stripBom, findNearestProjectManifestSync } from "./474~0.mjs";
8
8
  import { canonicalizeDir, canonicalizeResourcePath } from "./938~0.mjs";
9
9
  import { getCanonicalContentScriptEntryName, CANONICAL_CONTENT_SCRIPT_ENTRY_PREFIX, getContentScriptCssProbeMarker } from "./640~0.mjs";
10
10
  import { EXTENSION_ROOT_PLACEHOLDER } from "./747~0.mjs";
@@ -4,7 +4,7 @@ import node_path from "node:path";
4
4
  import { parse } from "es-module-lexer";
5
5
  import { validate } from "schema-utils";
6
6
  import { filterKeysForThisBrowser, reservedScriptsFolder } from "./672~0.mjs";
7
- import { stripBom, findNearestProjectManifestSync } from "./731~0.mjs";
7
+ import { stripBom, findNearestProjectManifestSync } from "./474~0.mjs";
8
8
  import { canonicalizeDir, canonicalizeResourcePath } from "./938~0.mjs";
9
9
  import { getCanonicalContentScriptEntryName, CANONICAL_CONTENT_SCRIPT_ENTRY_PREFIX, getContentScriptCssProbeMarker } from "./640~0.mjs";
10
10
  import { EXTENSION_ROOT_PLACEHOLDER } from "./747~0.mjs";
@@ -4,6 +4,9 @@ export declare const GECKO_BASED_BROWSERS: string[];
4
4
  export declare const WEBKIT_BASED_BROWSERS: string[];
5
5
  export declare const CHROMIUM_FAMILY_ALIASES: string[];
6
6
  export declare const GECKO_FAMILY_ALIASES: string[];
7
+ export declare const EMULATOR_BROWSER = "chromium-emulator";
8
+ export declare function isEmulatorLaneEnabled(env?: NodeJS.ProcessEnv): boolean;
9
+ export declare function isEmulatorBrowser(browser: unknown): boolean;
7
10
  export declare const SUPPORTED_BROWSERS: string[];
8
11
  export declare const SUPPORTED_PACKAGE_MANAGERS: readonly ['npm', 'pnpm', 'yarn', 'bun', 'deno'];
9
12
  export declare const SUPPORTED_RUNTIMES: readonly ['node', 'deno', 'bun'];
@@ -2,8 +2,8 @@ import type { ProjectStructure } from './project.js';
2
2
  export type AbsolutePath = string & {
3
3
  readonly __brand: 'AbsolutePath';
4
4
  };
5
- export type BrowserInput = 'chrome' | 'edge' | 'chromium' | 'firefox' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'firefox-based' | 'safari' | 'webkit-based' | undefined;
6
- export type NormalizedBrowser = 'chrome' | 'edge' | 'chromium' | 'firefox' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'safari' | 'webkit-based';
5
+ export type BrowserInput = 'chrome' | 'edge' | 'chromium' | 'firefox' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'firefox-based' | 'safari' | 'webkit-based' | 'chromium-emulator' | undefined;
6
+ export type NormalizedBrowser = 'chrome' | 'edge' | 'chromium' | 'firefox' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'safari' | 'webkit-based' | 'chromium-emulator';
7
7
  export declare function asAbsolute(p: string): AbsolutePath;
8
8
  export declare function toPosixPath(p: string): string;
9
9
  export declare function collapseHomeDir(value: string): string;
@@ -47,6 +47,7 @@ export interface BrowserLaunchOptions {
47
47
  logColor?: boolean;
48
48
  logUrl?: string;
49
49
  logTab?: number | string;
50
+ emulatorViewerUrl?: string;
50
51
  logSink?: BrowserLogSink;
51
52
  }
52
53
  /** Browser-generated log entry normalized by the launcher's CDP controller. */
@@ -118,6 +119,7 @@ export declare class BrowsersPlugin implements RunnerPlugin {
118
119
  private controller;
119
120
  private reloadBroker;
120
121
  private logSink;
122
+ private emulatorViewerUrl;
121
123
  private lastReportedRefusal;
122
124
  constructor(options: BrowsersPluginOptions);
123
125
  /**
@@ -127,6 +129,7 @@ export declare class BrowsersPlugin implements RunnerPlugin {
127
129
  */
128
130
  setReloadBroker(broker: ReloadBroker): void;
129
131
  setLogSink(sink: BrowserLogSink): void;
132
+ setEmulatorViewerUrl(url: string): void;
130
133
  private retryRefusedExtensionLoad;
131
134
  apply(compiler: Compiler): void;
132
135
  }
@@ -29,7 +29,8 @@ export type ReadyMetadata = {
29
29
  cdpPort?: number;
30
30
  rdpPort?: number;
31
31
  profilePath?: string;
32
- browserPid?: number;
32
+ browserPid?: number | null;
33
+ engine?: 'emulator';
33
34
  launcherPid?: number;
34
35
  toolchainVersion: string;
35
36
  extensionName?: string;
@@ -0,0 +1,2 @@
1
+ import type { FilepathList } from '../../types.js';
2
+ export declare function omitOwnedPages(pages: FilepathList | undefined, owned: FilepathList): FilepathList;
@@ -13,11 +13,10 @@ import node_fs from "node:fs";
13
13
  import node_path from "node:path";
14
14
  import { utils_isUrl, getBaseNode, parseHtml, manifestJsonOutputTarget, getExtname, getBaseHref, isResourceUnderDirs, getAssetsFromHtml, normalizeManifestOutputPath, manifestPageOutputTarget, canonicalizeDir, iconOutputPath, isFromIncludeList, htmlStaticAssetOutputName, isSpecialScheme, resolveStaticAttributeName, getHtmlPageDeclaredAssetPath, themeImageOutputPath, themeIconOutputPath, applyRewrittenStaticUrl, toResourceKey, cleanLeading, getFilePath, resolveAbsoluteFsPath, cleanAssetUrl, isHttpLike, resolveHtmlRefPath } from "./938~0.mjs";
15
15
  import { prefix as messaging_prefix, isDebug } from "./852~0.mjs";
16
- import { isChromiumBasedBrowser, CHROMIUM_FAMILY_ALIASES, GECKO_FAMILY_ALIASES, isWebkitBasedBrowser, isGeckoBasedBrowser } from "./331~0.mjs";
16
+ import { GECKO_FAMILY_ALIASES, isEmulatorBrowser, isGeckoBasedBrowser, findNearestProjectManifestSync, readProjectDependencies, isChromiumBasedBrowser, CHROMIUM_FAMILY_ALIASES, stripBom, PROJECT_MANIFEST_FILENAMES, parseJsonSafe, isWebkitBasedBrowser } from "./474~0.mjs";
17
17
  import { maybeUsePreact, isUsingTypeScript, jsFrameworksIntegrationsEnabled, maybeUseReact, isUsingVue, classifyEntrySurface, ensureTypeScriptConfig, resolveDevelopInstallRoot, loadLoaderOptions, getUserTypeScriptConfigFile, svelteCompilerRuntimeMismatch, isUsingIntegration as messages_isUsingIntegration, maybeUseVue, maybeUseSolid, jsFrameworksHmrSummary, humanLine, ensureOptionalContractPackageResolved, jsFrameworksConfigsDetected, recordZipArtifact, hasDependency, resolveDevelopDistFile, isUsingJsxFramework, getJsxImportSource, swcParserForFile, isUsingReact, defaultSplitChunks } from "./840~0.mjs";
18
18
  import { bundlerFatalError, bundlerRecompiling, browserRunnerDisabled, PlaywrightPlugin, publicFolderShadowed, resolveCompanionExtensionDirs, specialFoldersSetupSummary, noEntrypointsDetected, serverRestartRequiredFromSpecialFolderMessageOnly, specialFolderChangeDetected, spacerLine, messages_ready, getPreloadedEnvKeys, publicMustBeAtProjectRoot, computeExtensionsToLoad, getSpecialFoldersDataForCompiler } from "./832~0.mjs";
19
19
  import { renderStatsBlocks, makeSanitizedConsole } from "./stats-handler~0.mjs";
20
- import { stripBom, findNearestProjectManifestSync, PROJECT_MANIFEST_FILENAMES, parseJsonSafe, readProjectDependencies } from "./731~0.mjs";
21
20
  import { EXTENSIONJS_CONTENT_SCRIPT_LAYER, getCanonicalContentScriptEntryName, parseCanonicalContentScriptAsset, CONTENT_SCRIPT_CSS_PROBE_MARKER_PREFIX, createContentScriptCssProbeMarkerPattern, getCanonicalContentScriptJsAssetName, getCanonicalContentScriptCssAssetName, isCanonicalContentScriptAsset } from "./640~0.mjs";
22
21
  import { getURLDependencyMissing, findDroppedVendorKeys, isStaticThemeSource, workerStartedFromPageContext, importScriptsDependencyMissing, filterKeysForThisBrowser, findPrefixedManifestVersionKeys, injectedCompiledSourceLiteral, staticImportDependencyMissing, entrySplitAcrossInitialFiles, fetchedFileDependencyMissing, isStaticTheme, compiledSourceSpelling, injectedFileDependencyMissing, isValidManifestVersion, runtimeSetSurfaceDependencyMissing } from "./672~0.mjs";
23
22
  import { packagingSourceFiles, debugBrowser, debugExtensionsToLoad, treeWithSourceFiles, debugOutputPath, manifestInvalidJson, getDirs, asAbsolute, treeWithDistFilesBrowser, packagingDistributionFiles, treeWithSourceAndDistFiles, toPosixPath as paths_toPosixPath, debugContextPath, noCompanionExtensionsResolved } from "./950~0.mjs";
@@ -7041,9 +7040,11 @@ class ReloadPlugin {
7041
7040
  browser: this.browser
7042
7041
  }).apply(compiler);
7043
7042
  new InjectScriptsReplayShim().apply(compiler);
7044
- if (isChromiumBasedBrowser(String(this.browser))) new SetupDevContentScripts().apply(compiler);
7045
- new InjectBridgeProducer().apply(compiler);
7046
- new InjectBridgeRelay().apply(compiler);
7043
+ if (isChromiumBasedBrowser(String(this.browser)) && !isEmulatorBrowser(this.browser)) new SetupDevContentScripts().apply(compiler);
7044
+ if (!isEmulatorBrowser(this.browser)) {
7045
+ new InjectBridgeProducer().apply(compiler);
7046
+ new InjectBridgeRelay().apply(compiler);
7047
+ }
7047
7048
  new PruneStaleHotUpdates().apply(compiler);
7048
7049
  }
7049
7050
  }
@@ -8786,6 +8787,21 @@ class WasmPlugin {
8786
8787
  this.ensureWasmResolution(compiler, projectRoot);
8787
8788
  }
8788
8789
  }
8790
+ function filesOf(value) {
8791
+ if (Array.isArray(value)) return value;
8792
+ return value ? [
8793
+ value
8794
+ ] : [];
8795
+ }
8796
+ function omitOwnedPages(pages, owned) {
8797
+ const ownedFiles = new Set(Object.values(owned).flatMap(filesOf).map((file)=>__rspack_external_node_path_806ed179.resolve(file)));
8798
+ const next = {};
8799
+ for (const [name, value] of Object.entries(pages || {})){
8800
+ const files = filesOf(value);
8801
+ if (!(files.length > 0 && files.every((file)=>ownedFiles.has(__rspack_external_node_path_806ed179.resolve(file))))) next[name] = value;
8802
+ }
8803
+ return next;
8804
+ }
8789
8805
  const COMPILED_SIBLING_EXTENSIONS = [
8790
8806
  '.ts',
8791
8807
  '.mts',
@@ -15129,15 +15145,18 @@ class plugin_web_extension_WebExtensionPlugin {
15129
15145
  ...manifestFieldsData.scripts
15130
15146
  }
15131
15147
  }).apply(compiler);
15148
+ const ownedPages = {
15149
+ ...manifestFieldsData.html,
15150
+ ...discoverDevtoolsPanelPages(manifestPath, this.browser),
15151
+ ...settingsOverridesStartupPages(manifestPath, this.browser)
15152
+ };
15132
15153
  new HtmlPlugin({
15133
15154
  devSession: this.devSession,
15134
15155
  manifestPath,
15135
15156
  browser: this.browser,
15136
15157
  includeList: {
15137
- ...manifestFieldsData.html,
15138
- ...discoverDevtoolsPanelPages(manifestPath, this.browser),
15139
- ...settingsOverridesStartupPages(manifestPath, this.browser),
15140
- ...specialFoldersData.pages
15158
+ ...ownedPages,
15159
+ ...omitOwnedPages(specialFoldersData.pages, ownedPages)
15141
15160
  }
15142
15161
  }).apply(compiler);
15143
15162
  new ScriptsPlugin({
package/dist/types.d.ts CHANGED
@@ -65,7 +65,7 @@ export interface JsFramework {
65
65
  import type { Configuration } from '@rspack/core';
66
66
  import type { SafariPackageSummary } from './lib/build-summary.js';
67
67
  import type { CompanionExtensionsConfig } from './plugin-special-folders/folder-extensions/types.js';
68
- export type BrowserType = 'chrome' | 'edge' | 'firefox' | 'chromium' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'firefox-based' | 'safari' | 'webkit-based';
68
+ export type BrowserType = 'chrome' | 'edge' | 'firefox' | 'chromium' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | 'zen' | 'floorp' | 'chromium-based' | 'gecko-based' | 'firefox-based' | 'safari' | 'webkit-based' | 'chromium-emulator';
69
69
  export interface BrowserOptionsBase {
70
70
  noOpen?: boolean;
71
71
  profile?: string | false;
package/package.json CHANGED
@@ -43,7 +43,7 @@
43
43
  "runtime"
44
44
  ],
45
45
  "name": "extension-develop",
46
- "version": "4.1.25-canary.408.0177007",
46
+ "version": "4.1.26",
47
47
  "description": "Develop, build, preview, and package Extension.js projects.",
48
48
  "author": {
49
49
  "name": "Cezar Augusto",
package/dist/331~0.mjs DELETED
@@ -1,45 +0,0 @@
1
- import { createRequire as __extjsCreateRequire } from "node:module"; const require = __extjsCreateRequire(import.meta.url);
2
- import * as __rspack_external_node_path_806ed179 from "node:path";
3
- __rspack_external_node_path_806ed179.join(process.cwd(), 'node_modules/extension-develop/dist/certs');
4
- const CHROMIUM_BASED_BROWSERS = [
5
- 'chrome',
6
- 'edge',
7
- 'brave',
8
- 'opera',
9
- 'vivaldi',
10
- 'yandex'
11
- ];
12
- const GECKO_BASED_BROWSERS = [
13
- 'firefox',
14
- 'waterfox',
15
- 'librewolf',
16
- 'zen',
17
- 'floorp'
18
- ];
19
- const WEBKIT_BASED_BROWSERS = [
20
- 'safari'
21
- ];
22
- const CHROMIUM_FAMILY_ALIASES = [
23
- 'chromium',
24
- 'chrome',
25
- 'edge',
26
- 'chromium-based'
27
- ];
28
- const GECKO_FAMILY_ALIASES = [
29
- 'firefox',
30
- 'gecko-based'
31
- ];
32
- [
33
- ...CHROMIUM_BASED_BROWSERS,
34
- ...GECKO_BASED_BROWSERS
35
- ];
36
- function isChromiumBasedBrowser(browser) {
37
- return CHROMIUM_BASED_BROWSERS.includes(browser) || String(browser).includes('chromium');
38
- }
39
- function isGeckoBasedBrowser(browser) {
40
- return GECKO_BASED_BROWSERS.includes(browser) || String(browser).includes('gecko') || String(browser).includes('firefox');
41
- }
42
- function isWebkitBasedBrowser(browser) {
43
- return WEBKIT_BASED_BROWSERS.includes(browser) || String(browser).includes('webkit');
44
- }
45
- export { CHROMIUM_FAMILY_ALIASES, GECKO_FAMILY_ALIASES, isChromiumBasedBrowser, isGeckoBasedBrowser, isWebkitBasedBrowser };