extension-develop 4.1.25 → 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.
- package/dist/{731~0.mjs → 474~0.mjs} +52 -2
- package/dist/672~0.mjs +1 -2
- package/dist/832~0.mjs +31 -10
- package/dist/840~0.mjs +12 -4
- package/dist/852~0.mjs +2 -0
- package/dist/950~0.mjs +20 -3
- package/dist/bridge.mjs +1 -0
- package/dist/dev-server/control-bridge/broker.d.ts +8 -2
- package/dist/dev-server/control-bridge/consumer-client.d.ts +1 -0
- package/dist/dev-server/control-bridge/contracts.d.ts +3 -2
- package/dist/dev-server/control-bridge/ws-control-server.d.ts +2 -0
- package/dist/dev-server/dev-middleware-body.d.ts +16 -0
- package/dist/dev-server/emulator-lane.d.ts +56 -0
- package/dist/dev-server/messages.d.ts +1 -0
- package/dist/dev-server~0.mjs +284 -13
- package/dist/feature-scripts-content-script-wrapper.js +1 -1
- package/dist/feature-scripts-content-script-wrapper.mjs +1 -1
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/extension-id.d.ts +1 -0
- package/dist/lib/messages.d.ts +1 -0
- package/dist/lib/paths.d.ts +2 -2
- package/dist/plugin-browsers/index.d.ts +3 -0
- package/dist/plugin-playwright/index.d.ts +2 -1
- package/dist/plugin-web-extension/feature-scripts/steps/trace-runtime-loaded-files.d.ts +1 -0
- package/dist/plugin-web-extension/shared/omit-owned-pages.d.ts +2 -0
- package/dist/rspack-config~0.mjs +201 -43
- package/dist/types.d.ts +7 -2
- package/package.json +2 -1
- package/dist/331~0.mjs +0 -43
package/dist/dev-server~0.mjs
CHANGED
|
@@ -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 "./
|
|
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 (
|
|
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 (
|
|
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)
|
|
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 "./
|
|
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 "./
|
|
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";
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -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'];
|
|
@@ -3,6 +3,7 @@ export interface ManagedExtensionRecord {
|
|
|
3
3
|
id?: string;
|
|
4
4
|
}
|
|
5
5
|
export declare function chromiumExtensionIdFromKey(manifestKey: string): string;
|
|
6
|
+
export declare function chromiumExtensionIdFromRegisteredPath(registeredPath: string, platform?: NodeJS.Platform): string;
|
|
6
7
|
export declare function chromiumExtensionIdFromPath(extensionDir: string): string;
|
|
7
8
|
export declare function chromiumExtensionId(extensionDir: string): string;
|
|
8
9
|
export declare function geckoExtensionId(extensionDir: string): string | undefined;
|
package/dist/lib/messages.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export declare function projectInstallScriptsDisabled(pmName: string): string;
|
|
|
20
20
|
export declare function anotherDevSessionActive(browser: string, pid: number, runId: string): string;
|
|
21
21
|
export declare function buildAssetsTree(stats: Stats | undefined): string;
|
|
22
22
|
export declare function buildComplete(browser: DevOptions['browser'], distDisplayPath: string, totalBytes?: number, mode?: 'development' | 'production' | 'none'): string;
|
|
23
|
+
export declare function operaBuildUnminified(): string;
|
|
23
24
|
export declare function addonLintSummary(errorCount: number, warningCount: number, distDisplay: string): string;
|
|
24
25
|
export declare function addonLintFinding(level: 'error' | 'warning', code: string, message: string, location: string, attribution?: string): string;
|
|
25
26
|
export declare function addonLintDependencyAttribution(packages: string[], onlyDependencies: boolean): string;
|
package/dist/lib/paths.d.ts
CHANGED
|
@@ -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' | 'chromium-based' | 'gecko-based' | 'firefox-based' | 'safari' | 'webkit-based' | undefined;
|
|
6
|
-
export type NormalizedBrowser = 'chrome' | 'edge' | 'chromium' | 'firefox' | 'brave' | 'opera' | 'vivaldi' | 'yandex' | 'waterfox' | 'librewolf' | '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
|
}
|