@secure-exec/browser 0.0.0-nathan-docs-sdk-expansion.c9c2e4e

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/worker.js ADDED
@@ -0,0 +1,1399 @@
1
+ import { transform } from "sucrase";
2
+ import { createBrowserNetworkAdapter } from "./driver.js";
3
+ import { validatePermissionSource } from "./permission-validation.js";
4
+ import { createCommandExecutorStub, createNetworkStub, exposeCustomGlobal, exposeMutableRuntimeStateGlobal, filterEnv, getIsolateRuntimeSource, getRequireSetupCode, isESM, POLYFILL_CODE_MAP, transformDynamicImport, wrapNetworkAdapter, } from "./runtime.js";
5
+ import { assertBrowserSyncBridgeSupport, SYNC_BRIDGE_KIND_BINARY, SYNC_BRIDGE_KIND_JSON, SYNC_BRIDGE_KIND_NONE, SYNC_BRIDGE_KIND_TEXT, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_ERROR, } from "./sync-bridge.js";
6
+ let networkAdapter = null;
7
+ let commandExecutor = null;
8
+ let permissions;
9
+ let initialized = false;
10
+ let controlToken = null;
11
+ let runtimeTimingMitigation = "freeze";
12
+ let runtimeProcessConfig = null;
13
+ let activeProcessRequestId = null;
14
+ const dynamicImportCache = new Map();
15
+ const MAX_ERROR_MESSAGE_CHARS = 8192;
16
+ const MAX_STDIO_MESSAGE_CHARS = 8192;
17
+ const MAX_STDIO_DEPTH = 6;
18
+ const MAX_STDIO_OBJECT_KEYS = 60;
19
+ const MAX_STDIO_ARRAY_ITEMS = 120;
20
+ // Payload size defaults matching the Node runtime path
21
+ const DEFAULT_BASE64_TRANSFER_BYTES = 16 * 1024 * 1024;
22
+ const DEFAULT_JSON_PAYLOAD_BYTES = 4 * 1024 * 1024;
23
+ const PAYLOAD_LIMIT_ERROR_CODE = "ERR_SANDBOX_PAYLOAD_TOO_LARGE";
24
+ let base64TransferLimitBytes = DEFAULT_BASE64_TRANSFER_BYTES;
25
+ let jsonPayloadLimitBytes = DEFAULT_JSON_PAYLOAD_BYTES;
26
+ const encoder = new TextEncoder();
27
+ const decoder = new TextDecoder();
28
+ // biome-ignore lint/security/noGlobalEval: the browser worker intentionally evaluates isolated runtime source strings.
29
+ const globalEval = eval;
30
+ const SHARED_ARRAY_BUFFER_FREEZE_KEYS = [
31
+ "byteLength",
32
+ "slice",
33
+ "grow",
34
+ "maxByteLength",
35
+ "growable",
36
+ ];
37
+ const timingGlobals = {
38
+ captured: false,
39
+ sharedArrayBufferPrototypeDescriptors: new Map(),
40
+ };
41
+ function getUtf8ByteLength(text) {
42
+ return encoder.encode(text).byteLength;
43
+ }
44
+ function getRequiredControlToken() {
45
+ if (!controlToken) {
46
+ throw new Error("Browser runtime worker control channel is not initialized");
47
+ }
48
+ return controlToken;
49
+ }
50
+ function captureTimingGlobals() {
51
+ if (timingGlobals.captured) {
52
+ return;
53
+ }
54
+ timingGlobals.captured = true;
55
+ timingGlobals.dateDescriptor = Object.getOwnPropertyDescriptor(globalThis, "Date");
56
+ timingGlobals.dateValue = globalThis.Date;
57
+ timingGlobals.performanceDescriptor = Object.getOwnPropertyDescriptor(globalThis, "performance");
58
+ timingGlobals.performanceValue = globalThis.performance;
59
+ timingGlobals.sharedArrayBufferDescriptor = Object.getOwnPropertyDescriptor(globalThis, "SharedArrayBuffer");
60
+ timingGlobals.sharedArrayBufferValue = globalThis.SharedArrayBuffer;
61
+ const sharedArrayBufferCtor = globalThis.SharedArrayBuffer;
62
+ if (typeof sharedArrayBufferCtor !== "function") {
63
+ return;
64
+ }
65
+ const prototype = sharedArrayBufferCtor.prototype;
66
+ for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) {
67
+ timingGlobals.sharedArrayBufferPrototypeDescriptors.set(key, Object.getOwnPropertyDescriptor(prototype, key));
68
+ }
69
+ }
70
+ function restoreGlobalProperty(name, descriptor) {
71
+ if (descriptor) {
72
+ try {
73
+ Object.defineProperty(globalThis, name, descriptor);
74
+ return;
75
+ }
76
+ catch {
77
+ if ("value" in descriptor) {
78
+ globalThis[name] = descriptor.value;
79
+ return;
80
+ }
81
+ }
82
+ }
83
+ Reflect.deleteProperty(globalThis, name);
84
+ }
85
+ function restoreSharedArrayBufferPrototype() {
86
+ const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue;
87
+ if (typeof sharedArrayBufferCtor !== "function") {
88
+ return;
89
+ }
90
+ const prototype = sharedArrayBufferCtor.prototype;
91
+ for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) {
92
+ const descriptor = timingGlobals.sharedArrayBufferPrototypeDescriptors.get(key);
93
+ try {
94
+ if (descriptor) {
95
+ Object.defineProperty(prototype, key, descriptor);
96
+ }
97
+ else {
98
+ delete prototype[key];
99
+ }
100
+ }
101
+ catch {
102
+ // Ignore non-configurable SharedArrayBuffer prototype properties.
103
+ }
104
+ }
105
+ }
106
+ function restoreTimingMitigationOff() {
107
+ captureTimingGlobals();
108
+ restoreGlobalProperty("Date", timingGlobals.dateDescriptor);
109
+ restoreGlobalProperty("performance", timingGlobals.performanceDescriptor);
110
+ restoreSharedArrayBufferPrototype();
111
+ restoreGlobalProperty("SharedArrayBuffer", timingGlobals.sharedArrayBufferDescriptor);
112
+ if (typeof globalThis.performance === "undefined" ||
113
+ globalThis.performance === null) {
114
+ Object.defineProperty(globalThis, "performance", {
115
+ value: {
116
+ now: () => Date.now(),
117
+ },
118
+ configurable: true,
119
+ writable: true,
120
+ });
121
+ }
122
+ }
123
+ function applyTimingMitigation(timingMitigation, frozenTimeMs) {
124
+ captureTimingGlobals();
125
+ restoreTimingMitigationOff();
126
+ if (timingMitigation !== "freeze") {
127
+ return undefined;
128
+ }
129
+ const frozenTimeValue = typeof frozenTimeMs === "number" && Number.isFinite(frozenTimeMs)
130
+ ? Math.trunc(frozenTimeMs)
131
+ : Date.now();
132
+ const originalDate = timingGlobals.dateValue ?? timingGlobals.dateDescriptor?.value ?? Date;
133
+ const frozenDateNow = () => frozenTimeValue;
134
+ const FrozenDate = function (...args) {
135
+ if (new.target) {
136
+ if (args.length === 0) {
137
+ return new originalDate(frozenTimeValue);
138
+ }
139
+ return new originalDate(...args);
140
+ }
141
+ return originalDate();
142
+ };
143
+ Object.defineProperty(FrozenDate, "prototype", {
144
+ value: originalDate.prototype,
145
+ writable: false,
146
+ configurable: false,
147
+ });
148
+ Object.defineProperty(FrozenDate, "now", {
149
+ value: frozenDateNow,
150
+ configurable: true,
151
+ writable: false,
152
+ });
153
+ FrozenDate.parse = originalDate.parse;
154
+ FrozenDate.UTC = originalDate.UTC;
155
+ try {
156
+ Object.defineProperty(globalThis, "Date", {
157
+ value: FrozenDate,
158
+ configurable: true,
159
+ writable: false,
160
+ });
161
+ }
162
+ catch {
163
+ globalThis.Date = FrozenDate;
164
+ }
165
+ const frozenPerformance = Object.create(null);
166
+ const originalPerformance = timingGlobals.performanceValue;
167
+ if (typeof originalPerformance !== "undefined" &&
168
+ originalPerformance !== null) {
169
+ const source = originalPerformance;
170
+ for (const key of Object.getOwnPropertyNames(Object.getPrototypeOf(originalPerformance) ?? originalPerformance)) {
171
+ if (key === "now") {
172
+ continue;
173
+ }
174
+ try {
175
+ const value = source[key];
176
+ frozenPerformance[key] =
177
+ typeof value === "function" ? value.bind(originalPerformance) : value;
178
+ }
179
+ catch {
180
+ // Ignore performance accessors that throw in this host.
181
+ }
182
+ }
183
+ }
184
+ Object.defineProperty(frozenPerformance, "now", {
185
+ value: () => 0,
186
+ configurable: true,
187
+ writable: false,
188
+ });
189
+ Object.freeze(frozenPerformance);
190
+ try {
191
+ Object.defineProperty(globalThis, "performance", {
192
+ value: frozenPerformance,
193
+ configurable: true,
194
+ writable: false,
195
+ });
196
+ }
197
+ catch {
198
+ globalThis.performance = frozenPerformance;
199
+ }
200
+ const sharedArrayBufferCtor = timingGlobals.sharedArrayBufferValue;
201
+ if (typeof sharedArrayBufferCtor === "function") {
202
+ const prototype = sharedArrayBufferCtor.prototype;
203
+ for (const key of SHARED_ARRAY_BUFFER_FREEZE_KEYS) {
204
+ try {
205
+ Object.defineProperty(prototype, key, {
206
+ get() {
207
+ throw new TypeError("SharedArrayBuffer is not available in sandbox");
208
+ },
209
+ configurable: true,
210
+ });
211
+ }
212
+ catch {
213
+ // Ignore non-configurable SharedArrayBuffer prototype properties.
214
+ }
215
+ }
216
+ }
217
+ try {
218
+ Object.defineProperty(globalThis, "SharedArrayBuffer", {
219
+ value: undefined,
220
+ configurable: true,
221
+ writable: false,
222
+ enumerable: false,
223
+ });
224
+ }
225
+ catch {
226
+ Reflect.deleteProperty(globalThis, "SharedArrayBuffer");
227
+ }
228
+ return frozenTimeValue;
229
+ }
230
+ function assertPayloadByteLength(payloadLabel, actualBytes, maxBytes) {
231
+ if (actualBytes <= maxBytes)
232
+ return;
233
+ const error = new Error(`[${PAYLOAD_LIMIT_ERROR_CODE}] ${payloadLabel}: payload is ${actualBytes} bytes, limit is ${maxBytes} bytes`);
234
+ error.code = PAYLOAD_LIMIT_ERROR_CODE;
235
+ throw error;
236
+ }
237
+ function assertTextPayloadSize(payloadLabel, text, maxBytes) {
238
+ assertPayloadByteLength(payloadLabel, getUtf8ByteLength(text), maxBytes);
239
+ }
240
+ function boundErrorMessage(message) {
241
+ if (message.length <= MAX_ERROR_MESSAGE_CHARS) {
242
+ return message;
243
+ }
244
+ return `${message.slice(0, MAX_ERROR_MESSAGE_CHARS)}...[Truncated]`;
245
+ }
246
+ function boundStdioMessage(message) {
247
+ if (message.length <= MAX_STDIO_MESSAGE_CHARS) {
248
+ return message;
249
+ }
250
+ return `${message.slice(0, MAX_STDIO_MESSAGE_CHARS)}...[Truncated]`;
251
+ }
252
+ function revivePermission(source) {
253
+ if (!source)
254
+ return undefined;
255
+ // Validate source before eval to prevent code injection
256
+ if (!validatePermissionSource(source))
257
+ return undefined;
258
+ try {
259
+ const fn = new Function(`return (${source});`)();
260
+ if (typeof fn === "function")
261
+ return fn;
262
+ return undefined;
263
+ }
264
+ catch {
265
+ return undefined;
266
+ }
267
+ }
268
+ /** Deserialize permission callbacks that were stringified for transfer across the Worker boundary. */
269
+ function revivePermissions(serialized) {
270
+ if (!serialized)
271
+ return undefined;
272
+ const perms = {};
273
+ perms.fs = revivePermission(serialized.fs);
274
+ perms.network = revivePermission(serialized.network);
275
+ perms.childProcess = revivePermission(serialized.childProcess);
276
+ perms.env = revivePermission(serialized.env);
277
+ return perms;
278
+ }
279
+ /**
280
+ * Wrap a sync function in the bridge calling convention (`applySync`) so
281
+ * bridge code can call it the same way it calls bridge References.
282
+ */
283
+ function makeApplySync(fn) {
284
+ const applySync = (_ctx, args) => fn(...args);
285
+ return {
286
+ applySync,
287
+ applySyncPromise: applySync,
288
+ };
289
+ }
290
+ function makeApplySyncPromise(fn) {
291
+ return {
292
+ applySyncPromise(_ctx, args) {
293
+ return fn(...args);
294
+ },
295
+ };
296
+ }
297
+ function makeApplyPromise(fn) {
298
+ return {
299
+ apply(_ctx, args) {
300
+ return fn(...args);
301
+ },
302
+ };
303
+ }
304
+ function normalizeTextEncoding(options) {
305
+ if (typeof options === "string") {
306
+ return options;
307
+ }
308
+ if (options && typeof options === "object" && "encoding" in options) {
309
+ const encoding = options.encoding;
310
+ return typeof encoding === "string" ? encoding : null;
311
+ }
312
+ return null;
313
+ }
314
+ function toBinaryView(data) {
315
+ if (data instanceof Uint8Array) {
316
+ return data;
317
+ }
318
+ if (ArrayBuffer.isView(data)) {
319
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
320
+ }
321
+ if (data instanceof ArrayBuffer) {
322
+ return new Uint8Array(data);
323
+ }
324
+ return new TextEncoder().encode(String(data));
325
+ }
326
+ function toNodeBuffer(bytes) {
327
+ if (typeof Buffer === "function") {
328
+ return Buffer.from(bytes);
329
+ }
330
+ return bytes;
331
+ }
332
+ function createStats(stat) {
333
+ return {
334
+ ...stat,
335
+ isFile: () => !stat.isDirectory && !stat.isSymbolicLink,
336
+ isDirectory: () => stat.isDirectory,
337
+ isSymbolicLink: () => stat.isSymbolicLink,
338
+ };
339
+ }
340
+ function createDirent(entry) {
341
+ return {
342
+ name: entry.name,
343
+ isFile: () => !entry.isDirectory && !entry.isSymbolicLink,
344
+ isDirectory: () => entry.isDirectory,
345
+ isSymbolicLink: () => Boolean(entry.isSymbolicLink),
346
+ };
347
+ }
348
+ function createFsModule(syncBridge) {
349
+ const readFileSync = (path, options) => {
350
+ const encoding = normalizeTextEncoding(options);
351
+ if (encoding) {
352
+ return syncBridge.requestText("fs.readFile", [path]);
353
+ }
354
+ return toNodeBuffer(syncBridge.requestBinary("fs.readFileBinary", [path]));
355
+ };
356
+ const writeFileSync = (path, content) => {
357
+ if (typeof content === "string") {
358
+ syncBridge.requestVoid("fs.writeFile", [path, content]);
359
+ return;
360
+ }
361
+ syncBridge.requestVoid("fs.writeFileBinary", [path, toBinaryView(content)]);
362
+ };
363
+ const mkdirSync = (path, options) => {
364
+ const recursive = typeof options === "boolean" ? options : (options?.recursive ?? true);
365
+ if (recursive) {
366
+ syncBridge.requestVoid("fs.mkdir", [path]);
367
+ return;
368
+ }
369
+ syncBridge.requestVoid("fs.createDir", [path]);
370
+ };
371
+ const readdirSync = (path, options) => {
372
+ const entries = syncBridge.requestJson("fs.readDir", [
373
+ path,
374
+ ]);
375
+ if (options?.withFileTypes) {
376
+ return entries.map((entry) => createDirent(entry));
377
+ }
378
+ return entries.map((entry) => entry.name);
379
+ };
380
+ const statSync = (path) => createStats(syncBridge.requestJson("fs.stat", [path]));
381
+ const lstatSync = (path) => createStats(syncBridge.requestJson("fs.lstat", [path]));
382
+ const promises = {
383
+ readFile(path, options) {
384
+ return Promise.resolve(readFileSync(path, options));
385
+ },
386
+ writeFile(path, content) {
387
+ writeFileSync(path, content);
388
+ return Promise.resolve();
389
+ },
390
+ mkdir(path, options) {
391
+ mkdirSync(path, options);
392
+ return Promise.resolve();
393
+ },
394
+ readdir(path, options) {
395
+ return Promise.resolve(readdirSync(path, options));
396
+ },
397
+ stat(path) {
398
+ return Promise.resolve(statSync(path));
399
+ },
400
+ lstat(path) {
401
+ return Promise.resolve(lstatSync(path));
402
+ },
403
+ unlink(path) {
404
+ syncBridge.requestVoid("fs.unlink", [path]);
405
+ return Promise.resolve();
406
+ },
407
+ rmdir(path) {
408
+ syncBridge.requestVoid("fs.rmdir", [path]);
409
+ return Promise.resolve();
410
+ },
411
+ rm(path) {
412
+ syncBridge.requestVoid("fs.unlink", [path]);
413
+ return Promise.resolve();
414
+ },
415
+ rename(oldPath, newPath) {
416
+ syncBridge.requestVoid("fs.rename", [oldPath, newPath]);
417
+ return Promise.resolve();
418
+ },
419
+ realpath(path) {
420
+ return Promise.resolve(syncBridge.requestText("fs.realpath", [path]));
421
+ },
422
+ readlink(path) {
423
+ return Promise.resolve(syncBridge.requestText("fs.readlink", [path]));
424
+ },
425
+ symlink(target, path) {
426
+ syncBridge.requestVoid("fs.symlink", [target, path]);
427
+ return Promise.resolve();
428
+ },
429
+ link(existingPath, newPath) {
430
+ syncBridge.requestVoid("fs.link", [existingPath, newPath]);
431
+ return Promise.resolve();
432
+ },
433
+ chmod(path, mode) {
434
+ syncBridge.requestVoid("fs.chmod", [path, mode]);
435
+ return Promise.resolve();
436
+ },
437
+ truncate(path, length = 0) {
438
+ syncBridge.requestVoid("fs.truncate", [path, length]);
439
+ return Promise.resolve();
440
+ },
441
+ };
442
+ return {
443
+ readFileSync,
444
+ writeFileSync,
445
+ mkdirSync,
446
+ readdirSync,
447
+ existsSync(path) {
448
+ return syncBridge.requestJson("fs.exists", [path]);
449
+ },
450
+ statSync,
451
+ lstatSync,
452
+ unlinkSync(path) {
453
+ syncBridge.requestVoid("fs.unlink", [path]);
454
+ },
455
+ rmdirSync(path) {
456
+ syncBridge.requestVoid("fs.rmdir", [path]);
457
+ },
458
+ rmSync(path) {
459
+ syncBridge.requestVoid("fs.unlink", [path]);
460
+ },
461
+ renameSync(oldPath, newPath) {
462
+ syncBridge.requestVoid("fs.rename", [oldPath, newPath]);
463
+ },
464
+ realpathSync(path) {
465
+ return syncBridge.requestText("fs.realpath", [path]);
466
+ },
467
+ readlinkSync(path) {
468
+ return syncBridge.requestText("fs.readlink", [path]);
469
+ },
470
+ symlinkSync(target, path) {
471
+ syncBridge.requestVoid("fs.symlink", [target, path]);
472
+ },
473
+ linkSync(existingPath, newPath) {
474
+ syncBridge.requestVoid("fs.link", [existingPath, newPath]);
475
+ },
476
+ chmodSync(path, mode) {
477
+ syncBridge.requestVoid("fs.chmod", [path, mode]);
478
+ },
479
+ truncateSync(path, length = 0) {
480
+ syncBridge.requestVoid("fs.truncate", [path, length]);
481
+ },
482
+ promises,
483
+ };
484
+ }
485
+ // Save real postMessage before sandbox code can replace it
486
+ const _realPostMessage = self.postMessage.bind(self);
487
+ function postResponse(message) {
488
+ _realPostMessage({
489
+ controlToken: getRequiredControlToken(),
490
+ ...message,
491
+ });
492
+ }
493
+ function postSyncRequest(message) {
494
+ _realPostMessage({
495
+ controlToken: getRequiredControlToken(),
496
+ ...message,
497
+ });
498
+ }
499
+ function postStdio(requestId, channel, message) {
500
+ const payload = {
501
+ controlToken: getRequiredControlToken(),
502
+ type: "stdio",
503
+ requestId,
504
+ channel,
505
+ message,
506
+ };
507
+ _realPostMessage(payload);
508
+ }
509
+ function formatConsoleValue(value, seen = new WeakSet(), depth = 0) {
510
+ if (value === null) {
511
+ return "null";
512
+ }
513
+ if (value === undefined) {
514
+ return "undefined";
515
+ }
516
+ if (typeof value === "string") {
517
+ return value;
518
+ }
519
+ if (typeof value === "number" || typeof value === "boolean") {
520
+ return String(value);
521
+ }
522
+ if (typeof value === "bigint") {
523
+ return `${value.toString()}n`;
524
+ }
525
+ if (typeof value === "symbol") {
526
+ return value.toString();
527
+ }
528
+ if (typeof value === "function") {
529
+ return `[Function ${value.name || "anonymous"}]`;
530
+ }
531
+ if (typeof value !== "object") {
532
+ return String(value);
533
+ }
534
+ if (seen.has(value)) {
535
+ return "[Circular]";
536
+ }
537
+ if (depth >= MAX_STDIO_DEPTH) {
538
+ return "[MaxDepth]";
539
+ }
540
+ seen.add(value);
541
+ try {
542
+ if (Array.isArray(value)) {
543
+ const out = value
544
+ .slice(0, MAX_STDIO_ARRAY_ITEMS)
545
+ .map((item) => formatConsoleValue(item, seen, depth + 1));
546
+ if (value.length > MAX_STDIO_ARRAY_ITEMS) {
547
+ out.push('"[Truncated]"');
548
+ }
549
+ return `[${out.join(", ")}]`;
550
+ }
551
+ const entries = [];
552
+ for (const key of Object.keys(value).slice(0, MAX_STDIO_OBJECT_KEYS)) {
553
+ entries.push(`${key}: ${formatConsoleValue(value[key], seen, depth + 1)}`);
554
+ }
555
+ if (Object.keys(value).length > MAX_STDIO_OBJECT_KEYS) {
556
+ entries.push('"[Truncated]"');
557
+ }
558
+ return `{ ${entries.join(", ")} }`;
559
+ }
560
+ catch {
561
+ return "[Unserializable]";
562
+ }
563
+ finally {
564
+ seen.delete(value);
565
+ }
566
+ }
567
+ function emitStdio(requestId, channel, args) {
568
+ const message = boundStdioMessage(args.map((arg) => formatConsoleValue(arg)).join(" "));
569
+ postStdio(requestId, channel, message);
570
+ }
571
+ function createSyncBridgeClient(payload) {
572
+ const signal = new Int32Array(payload.signalBuffer);
573
+ const data = new Uint8Array(payload.dataBuffer);
574
+ let nextRequestId = 1;
575
+ const timeoutMs = payload.timeoutMs ?? 30_000;
576
+ function readBytes(length) {
577
+ if (length <= 0) {
578
+ return new Uint8Array(0);
579
+ }
580
+ return data.slice(0, length);
581
+ }
582
+ function requestRaw(operation, args) {
583
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE);
584
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, 0);
585
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_KIND_NONE);
586
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, 0);
587
+ postSyncRequest({
588
+ type: "sync-request",
589
+ requestId: nextRequestId++,
590
+ operation,
591
+ args,
592
+ });
593
+ while (true) {
594
+ const result = Atomics.wait(signal, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE, timeoutMs);
595
+ if (result !== "timed-out") {
596
+ break;
597
+ }
598
+ throw new Error(`Browser runtime sync bridge timed out while handling ${operation}`);
599
+ }
600
+ const status = Atomics.load(signal, SYNC_BRIDGE_SIGNAL_STATUS_INDEX);
601
+ const kind = Atomics.load(signal, SYNC_BRIDGE_SIGNAL_KIND_INDEX);
602
+ const length = Atomics.load(signal, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX);
603
+ const bytes = readBytes(length);
604
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATE_IDLE);
605
+ if (status === SYNC_BRIDGE_STATUS_ERROR) {
606
+ const errorPayload = JSON.parse(decoder.decode(bytes));
607
+ const error = new Error(errorPayload.message);
608
+ if (errorPayload.code) {
609
+ error.code = errorPayload.code;
610
+ }
611
+ throw error;
612
+ }
613
+ return { kind, bytes };
614
+ }
615
+ return {
616
+ requestVoid(operation, args) {
617
+ requestRaw(operation, args);
618
+ },
619
+ requestText(operation, args) {
620
+ const result = requestRaw(operation, args);
621
+ if (result.kind !== SYNC_BRIDGE_KIND_TEXT) {
622
+ throw new Error(`Expected text response from ${operation}, received kind ${result.kind}`);
623
+ }
624
+ return decoder.decode(result.bytes);
625
+ },
626
+ requestNullableText(operation, args) {
627
+ const result = requestRaw(operation, args);
628
+ if (result.kind === SYNC_BRIDGE_KIND_NONE) {
629
+ return null;
630
+ }
631
+ if (result.kind !== SYNC_BRIDGE_KIND_TEXT) {
632
+ throw new Error(`Expected text response from ${operation}, received kind ${result.kind}`);
633
+ }
634
+ return decoder.decode(result.bytes);
635
+ },
636
+ requestBinary(operation, args) {
637
+ const result = requestRaw(operation, args);
638
+ if (result.kind !== SYNC_BRIDGE_KIND_BINARY) {
639
+ throw new Error(`Expected binary response from ${operation}, received kind ${result.kind}`);
640
+ }
641
+ return result.bytes;
642
+ },
643
+ requestJson(operation, args) {
644
+ const result = requestRaw(operation, args);
645
+ if (result.kind !== SYNC_BRIDGE_KIND_JSON) {
646
+ throw new Error(`Expected JSON response from ${operation}, received kind ${result.kind}`);
647
+ }
648
+ return JSON.parse(decoder.decode(result.bytes));
649
+ },
650
+ };
651
+ }
652
+ /**
653
+ * Initialize the worker-side runtime: set up filesystem, network, bridge
654
+ * globals, and load the bridge bundle. Called once before any exec/run.
655
+ */
656
+ async function initRuntime(payload) {
657
+ if (initialized)
658
+ return;
659
+ assertBrowserSyncBridgeSupport();
660
+ captureTimingGlobals();
661
+ if (!payload.syncBridge) {
662
+ throw new Error("Browser runtime sync bridge is required for filesystem and module loading parity");
663
+ }
664
+ permissions = revivePermissions(payload.permissions);
665
+ const syncBridge = createSyncBridgeClient(payload.syncBridge);
666
+ // Apply payload limits (use defaults if not configured)
667
+ base64TransferLimitBytes =
668
+ payload.payloadLimits?.base64TransferBytes ?? DEFAULT_BASE64_TRANSFER_BYTES;
669
+ jsonPayloadLimitBytes =
670
+ payload.payloadLimits?.jsonPayloadBytes ?? DEFAULT_JSON_PAYLOAD_BYTES;
671
+ if (payload.networkEnabled) {
672
+ networkAdapter = wrapNetworkAdapter(createBrowserNetworkAdapter(), permissions);
673
+ }
674
+ else {
675
+ networkAdapter = createNetworkStub();
676
+ }
677
+ commandExecutor = createCommandExecutorStub();
678
+ const processConfig = payload.processConfig ?? {};
679
+ runtimeProcessConfig = processConfig;
680
+ runtimeTimingMitigation =
681
+ payload.timingMitigation ??
682
+ processConfig.timingMitigation ??
683
+ runtimeTimingMitigation;
684
+ processConfig.env = filterEnv(processConfig.env, permissions);
685
+ processConfig.timingMitigation = runtimeTimingMitigation;
686
+ delete processConfig.frozenTimeMs;
687
+ exposeCustomGlobal("_processConfig", processConfig);
688
+ exposeCustomGlobal("_osConfig", payload.osConfig ?? {});
689
+ // Set up filesystem bridge globals before loading runtime shims.
690
+ const readFileRef = makeApplySync((path) => {
691
+ const text = syncBridge.requestText("fs.readFile", [path]);
692
+ assertTextPayloadSize(`fs.readFile ${path}`, text, jsonPayloadLimitBytes);
693
+ return text;
694
+ });
695
+ const writeFileRef = makeApplySync((path, content) => {
696
+ assertTextPayloadSize(`fs.writeFile ${path}`, content, jsonPayloadLimitBytes);
697
+ syncBridge.requestVoid("fs.writeFile", [path, content]);
698
+ });
699
+ const readFileBinaryRef = makeApplySync((path) => {
700
+ const data = syncBridge.requestBinary("fs.readFileBinary", [path]);
701
+ assertPayloadByteLength(`fs.readFileBinary ${path}`, data.byteLength, base64TransferLimitBytes);
702
+ return data;
703
+ });
704
+ const writeFileBinaryRef = makeApplySync((path, binaryContent) => {
705
+ assertPayloadByteLength(`fs.writeFileBinary ${path}`, binaryContent.byteLength, base64TransferLimitBytes);
706
+ syncBridge.requestVoid("fs.writeFileBinary", [path, binaryContent]);
707
+ });
708
+ const readDirRef = makeApplySync((path) => {
709
+ const json = JSON.stringify(syncBridge.requestJson("fs.readDir", [path]));
710
+ assertTextPayloadSize(`fs.readDir ${path}`, json, jsonPayloadLimitBytes);
711
+ return json;
712
+ });
713
+ const mkdirRef = makeApplySync((path) => {
714
+ syncBridge.requestVoid("fs.mkdir", [path]);
715
+ });
716
+ const rmdirRef = makeApplySync((path) => {
717
+ syncBridge.requestVoid("fs.rmdir", [path]);
718
+ });
719
+ const existsRef = makeApplySync((path) => {
720
+ return syncBridge.requestJson("fs.exists", [path]);
721
+ });
722
+ const statRef = makeApplySync((path) => {
723
+ return JSON.stringify(syncBridge.requestJson("fs.stat", [path]));
724
+ });
725
+ const unlinkRef = makeApplySync((path) => {
726
+ syncBridge.requestVoid("fs.unlink", [path]);
727
+ });
728
+ const renameRef = makeApplySync((oldPath, newPath) => {
729
+ syncBridge.requestVoid("fs.rename", [oldPath, newPath]);
730
+ });
731
+ exposeCustomGlobal("_fs", {
732
+ readFile: readFileRef,
733
+ writeFile: writeFileRef,
734
+ readFileBinary: readFileBinaryRef,
735
+ writeFileBinary: writeFileBinaryRef,
736
+ readDir: readDirRef,
737
+ mkdir: mkdirRef,
738
+ rmdir: rmdirRef,
739
+ exists: existsRef,
740
+ stat: statRef,
741
+ unlink: unlinkRef,
742
+ rename: renameRef,
743
+ });
744
+ exposeCustomGlobal("_loadPolyfill", (moduleName) => {
745
+ const name = moduleName.replace(/^node:/, "");
746
+ const polyfillMap = POLYFILL_CODE_MAP;
747
+ return polyfillMap[name] ?? null;
748
+ });
749
+ const resolveModuleSync = (request, fromDir, mode) => {
750
+ return syncBridge.requestNullableText("module.resolve", [
751
+ request,
752
+ fromDir,
753
+ mode ?? "require",
754
+ ]);
755
+ };
756
+ const loadFileSync = (path, _mode) => {
757
+ const source = syncBridge.requestNullableText("module.loadFile", [path]);
758
+ if (source === null) {
759
+ return null;
760
+ }
761
+ let code = source;
762
+ if (isESM(source, path)) {
763
+ code = transform(code, { transforms: ["imports"] }).code;
764
+ }
765
+ return transformDynamicImport(code);
766
+ };
767
+ exposeCustomGlobal("_resolveModuleSync", resolveModuleSync);
768
+ exposeCustomGlobal("_loadFileSync", loadFileSync);
769
+ exposeCustomGlobal("_resolveModule", resolveModuleSync);
770
+ exposeCustomGlobal("_loadFile", loadFileSync);
771
+ exposeCustomGlobal("_scheduleTimer", {
772
+ apply(_ctx, args) {
773
+ return new Promise((resolve) => {
774
+ setTimeout(resolve, args[0]);
775
+ });
776
+ },
777
+ });
778
+ const netAdapter = networkAdapter ?? createNetworkStub();
779
+ exposeCustomGlobal("_networkFetchRaw", makeApplyPromise(async (url, optionsJson) => {
780
+ const options = JSON.parse(optionsJson);
781
+ const result = await netAdapter.fetch(url, options);
782
+ return JSON.stringify(result);
783
+ }));
784
+ exposeCustomGlobal("_networkDnsLookupRaw", makeApplyPromise(async (hostname) => {
785
+ const result = await netAdapter.dnsLookup(hostname);
786
+ return JSON.stringify(result);
787
+ }));
788
+ const execAdapter = commandExecutor ?? createCommandExecutorStub();
789
+ let nextSessionId = 1;
790
+ const sessions = new Map();
791
+ const getDispatch = () => globalThis._childProcessDispatch;
792
+ exposeCustomGlobal("_childProcessSpawnStart", makeApplySync((command, argsJson, optionsJson) => {
793
+ const args = JSON.parse(argsJson);
794
+ const options = JSON.parse(optionsJson);
795
+ const sessionId = nextSessionId++;
796
+ const proc = execAdapter.spawn(command, args, {
797
+ cwd: options.cwd,
798
+ env: options.env,
799
+ onStdout: (data) => {
800
+ getDispatch()?.(sessionId, "stdout", data);
801
+ },
802
+ onStderr: (data) => {
803
+ getDispatch()?.(sessionId, "stderr", data);
804
+ },
805
+ });
806
+ void proc.wait().then((code) => {
807
+ getDispatch()?.(sessionId, "exit", code);
808
+ sessions.delete(sessionId);
809
+ });
810
+ sessions.set(sessionId, proc);
811
+ return sessionId;
812
+ }));
813
+ exposeCustomGlobal("_childProcessStdinWrite", makeApplySync((sessionId, data) => {
814
+ sessions.get(sessionId)?.writeStdin(data);
815
+ }));
816
+ exposeCustomGlobal("_childProcessStdinClose", makeApplySync((sessionId) => {
817
+ sessions.get(sessionId)?.closeStdin();
818
+ }));
819
+ exposeCustomGlobal("_childProcessKill", makeApplySync((sessionId, signal) => {
820
+ sessions.get(sessionId)?.kill(signal);
821
+ }));
822
+ exposeCustomGlobal("_childProcessSpawnSync", makeApplySyncPromise(async (command, argsJson, optionsJson) => {
823
+ const args = JSON.parse(argsJson);
824
+ const options = JSON.parse(optionsJson);
825
+ const stdoutChunks = [];
826
+ const stderrChunks = [];
827
+ const proc = execAdapter.spawn(command, args, {
828
+ cwd: options.cwd,
829
+ env: options.env,
830
+ onStdout: (data) => stdoutChunks.push(data),
831
+ onStderr: (data) => stderrChunks.push(data),
832
+ });
833
+ const exitCode = await proc.wait();
834
+ const decoder = new TextDecoder();
835
+ const stdout = stdoutChunks.map((c) => decoder.decode(c)).join("");
836
+ const stderr = stderrChunks.map((c) => decoder.decode(c)).join("");
837
+ return JSON.stringify({ stdout, stderr, code: exitCode });
838
+ }));
839
+ exposeCustomGlobal("_fsModule", createFsModule(syncBridge));
840
+ exposeMutableRuntimeStateGlobal("_moduleCache", {});
841
+ exposeMutableRuntimeStateGlobal("_pendingModules", {});
842
+ exposeMutableRuntimeStateGlobal("_currentModule", { dirname: "/" });
843
+ globalEval(getRequireSetupCode());
844
+ ensureProcessGlobal();
845
+ // Block dangerous Web APIs that bypass bridge permission checks
846
+ const dangerousApis = [
847
+ "XMLHttpRequest",
848
+ "WebSocket",
849
+ "importScripts",
850
+ "indexedDB",
851
+ "caches",
852
+ "BroadcastChannel",
853
+ ];
854
+ for (const api of dangerousApis) {
855
+ try {
856
+ delete self[api];
857
+ }
858
+ catch {
859
+ // May not exist or may be non-configurable
860
+ }
861
+ Object.defineProperty(self, api, {
862
+ get() {
863
+ throw new ReferenceError(`${api} is not available in sandbox`);
864
+ },
865
+ configurable: false,
866
+ });
867
+ }
868
+ // Lock down self.onmessage so sandbox code cannot hijack the control channel
869
+ const currentHandler = self.onmessage;
870
+ Object.defineProperty(self, "onmessage", {
871
+ value: currentHandler,
872
+ writable: false,
873
+ configurable: false,
874
+ });
875
+ // Block self.postMessage so sandbox code cannot forge responses to host
876
+ Object.defineProperty(self, "postMessage", {
877
+ get() {
878
+ throw new TypeError("postMessage is not available in sandbox");
879
+ },
880
+ configurable: false,
881
+ });
882
+ initialized = true;
883
+ }
884
+ function resetModuleState(cwd) {
885
+ exposeMutableRuntimeStateGlobal("_moduleCache", {});
886
+ exposeMutableRuntimeStateGlobal("_pendingModules", {});
887
+ exposeMutableRuntimeStateGlobal("_currentModule", { dirname: cwd });
888
+ }
889
+ function setDynamicImportFallback() {
890
+ exposeMutableRuntimeStateGlobal("__dynamicImport", (specifier) => {
891
+ const cached = dynamicImportCache.get(specifier);
892
+ if (cached)
893
+ return Promise.resolve(cached);
894
+ try {
895
+ const runtimeRequire = globalThis.require;
896
+ if (typeof runtimeRequire !== "function") {
897
+ throw new Error("require is not available in browser runtime");
898
+ }
899
+ const mod = runtimeRequire(specifier);
900
+ return Promise.resolve({
901
+ default: mod,
902
+ ...mod,
903
+ });
904
+ }
905
+ catch (e) {
906
+ return Promise.reject(new Error(`Cannot dynamically import '${specifier}': ${String(e)}`));
907
+ }
908
+ });
909
+ }
910
+ function toProcessChunk(value, encoding) {
911
+ if (encoding) {
912
+ return value;
913
+ }
914
+ return encoder.encode(value);
915
+ }
916
+ function normalizeProcessOutputChunk(chunk) {
917
+ if (typeof chunk === "string") {
918
+ return chunk;
919
+ }
920
+ if (chunk instanceof Uint8Array) {
921
+ return decoder.decode(chunk);
922
+ }
923
+ if (ArrayBuffer.isView(chunk)) {
924
+ return decoder.decode(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
925
+ }
926
+ if (chunk instanceof ArrayBuffer) {
927
+ return decoder.decode(new Uint8Array(chunk));
928
+ }
929
+ return String(chunk);
930
+ }
931
+ function emitProcessStdio(channel, chunk) {
932
+ if (activeProcessRequestId === null) {
933
+ return true;
934
+ }
935
+ emitStdio(activeProcessRequestId, channel, [
936
+ normalizeProcessOutputChunk(chunk),
937
+ ]);
938
+ return true;
939
+ }
940
+ function createBrowserProcess() {
941
+ let cwd = "/";
942
+ let stdinData = "";
943
+ let stdinPosition = 0;
944
+ let stdinEnded = false;
945
+ let stdinFlushQueued = false;
946
+ const stdinListeners = Object.create(null);
947
+ const stdinOnceListeners = Object.create(null);
948
+ const emitStdinListeners = (event, value) => {
949
+ const listeners = [
950
+ ...(stdinListeners[event] ?? []),
951
+ ...(stdinOnceListeners[event] ?? []),
952
+ ];
953
+ stdinOnceListeners[event] = [];
954
+ for (const listener of listeners) {
955
+ listener(value);
956
+ }
957
+ return listeners.length > 0;
958
+ };
959
+ const clearStdinListeners = () => {
960
+ for (const key of Object.keys(stdinListeners)) {
961
+ stdinListeners[key] = [];
962
+ }
963
+ for (const key of Object.keys(stdinOnceListeners)) {
964
+ stdinOnceListeners[key] = [];
965
+ }
966
+ };
967
+ const flushStdin = () => {
968
+ stdinFlushQueued = false;
969
+ if (stdin.paused || stdinEnded) {
970
+ return;
971
+ }
972
+ if (stdinPosition < stdinData.length) {
973
+ const chunk = stdinData.slice(stdinPosition);
974
+ stdinPosition = stdinData.length;
975
+ emitStdinListeners("data", toProcessChunk(chunk, stdin.encoding));
976
+ }
977
+ if (!stdinEnded) {
978
+ stdinEnded = true;
979
+ emitStdinListeners("end");
980
+ emitStdinListeners("close");
981
+ }
982
+ };
983
+ const scheduleStdinFlush = () => {
984
+ if (stdinFlushQueued) {
985
+ return;
986
+ }
987
+ stdinFlushQueued = true;
988
+ queueMicrotask(flushStdin);
989
+ };
990
+ const stdin = {
991
+ readable: true,
992
+ paused: true,
993
+ encoding: null,
994
+ isRaw: false,
995
+ read(size) {
996
+ if (stdinPosition >= stdinData.length) {
997
+ return null;
998
+ }
999
+ const chunk = size
1000
+ ? stdinData.slice(stdinPosition, stdinPosition + size)
1001
+ : stdinData.slice(stdinPosition);
1002
+ stdinPosition += chunk.length;
1003
+ return toProcessChunk(chunk, stdin.encoding);
1004
+ },
1005
+ on(event, listener) {
1006
+ if (!stdinListeners[event]) {
1007
+ stdinListeners[event] = [];
1008
+ }
1009
+ stdinListeners[event].push(listener);
1010
+ if (event === "data" && stdin.paused) {
1011
+ stdin.resume();
1012
+ }
1013
+ return stdin;
1014
+ },
1015
+ once(event, listener) {
1016
+ if (!stdinOnceListeners[event]) {
1017
+ stdinOnceListeners[event] = [];
1018
+ }
1019
+ stdinOnceListeners[event].push(listener);
1020
+ if (event === "data" && stdin.paused) {
1021
+ stdin.resume();
1022
+ }
1023
+ return stdin;
1024
+ },
1025
+ off(event, listener) {
1026
+ if (!stdinListeners[event]) {
1027
+ return stdin;
1028
+ }
1029
+ stdinListeners[event] = stdinListeners[event].filter((candidate) => candidate !== listener);
1030
+ return stdin;
1031
+ },
1032
+ removeListener(event, listener) {
1033
+ return stdin.off(event, listener);
1034
+ },
1035
+ emit(event, value) {
1036
+ return emitStdinListeners(event, value);
1037
+ },
1038
+ pause() {
1039
+ stdin.paused = true;
1040
+ return stdin;
1041
+ },
1042
+ resume() {
1043
+ stdin.paused = false;
1044
+ scheduleStdinFlush();
1045
+ return stdin;
1046
+ },
1047
+ setEncoding(encoding) {
1048
+ stdin.encoding = encoding;
1049
+ return stdin;
1050
+ },
1051
+ setRawMode(mode) {
1052
+ stdin.isRaw = mode;
1053
+ return stdin;
1054
+ },
1055
+ get isTTY() {
1056
+ return false;
1057
+ },
1058
+ async *[Symbol.asyncIterator]() {
1059
+ const remaining = stdinData.slice(stdinPosition);
1060
+ for (const line of remaining.split("\n")) {
1061
+ if (line.length > 0) {
1062
+ yield line;
1063
+ }
1064
+ }
1065
+ },
1066
+ };
1067
+ const processBridge = {
1068
+ browser: true,
1069
+ env: {},
1070
+ argv: ["node"],
1071
+ argv0: "node",
1072
+ pid: 1,
1073
+ ppid: 0,
1074
+ platform: "browser",
1075
+ version: "v22.0.0",
1076
+ versions: {
1077
+ node: "22.0.0",
1078
+ },
1079
+ stdin,
1080
+ stdout: {
1081
+ isTTY: false,
1082
+ write(chunk) {
1083
+ return emitProcessStdio("stdout", chunk);
1084
+ },
1085
+ },
1086
+ stderr: {
1087
+ isTTY: false,
1088
+ write(chunk) {
1089
+ return emitProcessStdio("stderr", chunk);
1090
+ },
1091
+ },
1092
+ exitCode: 0,
1093
+ cwd: () => cwd,
1094
+ chdir: (nextCwd) => {
1095
+ cwd = String(nextCwd);
1096
+ },
1097
+ nextTick: (callback, ...args) => {
1098
+ queueMicrotask(() => callback(...args));
1099
+ },
1100
+ exit(code) {
1101
+ const exitCode = typeof code === "number" ? code : (processBridge.exitCode ?? 0);
1102
+ processBridge.exitCode = exitCode;
1103
+ throw new Error(`process.exit(${exitCode})`);
1104
+ },
1105
+ on() {
1106
+ return processBridge;
1107
+ },
1108
+ once() {
1109
+ return processBridge;
1110
+ },
1111
+ off() {
1112
+ return processBridge;
1113
+ },
1114
+ removeListener() {
1115
+ return processBridge;
1116
+ },
1117
+ emit() {
1118
+ return false;
1119
+ },
1120
+ __secureExecRefreshProcess(nextConfig) {
1121
+ clearStdinListeners();
1122
+ stdinData = typeof nextConfig?.stdin === "string" ? nextConfig.stdin : "";
1123
+ stdinPosition = 0;
1124
+ stdinEnded = false;
1125
+ stdinFlushQueued = false;
1126
+ stdin.paused = true;
1127
+ stdin.encoding = null;
1128
+ stdin.isRaw = false;
1129
+ processBridge.exitCode = 0;
1130
+ processBridge.env =
1131
+ nextConfig?.env && typeof nextConfig.env === "object"
1132
+ ? { ...nextConfig.env }
1133
+ : {};
1134
+ if (typeof nextConfig?.cwd === "string") {
1135
+ cwd = nextConfig.cwd;
1136
+ }
1137
+ processBridge.argv = Array.isArray(nextConfig?.argv)
1138
+ ? nextConfig.argv.map((value) => String(value))
1139
+ : ["node"];
1140
+ processBridge.argv0 = processBridge.argv[0] ?? "node";
1141
+ if (typeof nextConfig?.platform === "string") {
1142
+ processBridge.platform = nextConfig.platform;
1143
+ }
1144
+ if (typeof nextConfig?.version === "string") {
1145
+ processBridge.version = nextConfig.version;
1146
+ processBridge.versions.node = nextConfig.version.replace(/^v/, "");
1147
+ }
1148
+ if (typeof nextConfig?.pid === "number") {
1149
+ processBridge.pid = nextConfig.pid;
1150
+ }
1151
+ if (typeof nextConfig?.ppid === "number") {
1152
+ processBridge.ppid = nextConfig.ppid;
1153
+ }
1154
+ },
1155
+ };
1156
+ return processBridge;
1157
+ }
1158
+ function getRuntimeProcess() {
1159
+ const proc = globalThis.process;
1160
+ if (!proc || typeof proc !== "object") {
1161
+ return undefined;
1162
+ }
1163
+ return proc;
1164
+ }
1165
+ function refreshRuntimeProcess() {
1166
+ const proc = getRuntimeProcess();
1167
+ const refresh = proc?.__secureExecRefreshProcess;
1168
+ if (typeof refresh === "function") {
1169
+ refresh(runtimeProcessConfig);
1170
+ }
1171
+ }
1172
+ function ensureProcessGlobal() {
1173
+ if (getRuntimeProcess()) {
1174
+ refreshRuntimeProcess();
1175
+ return;
1176
+ }
1177
+ exposeMutableRuntimeStateGlobal("process", createBrowserProcess());
1178
+ refreshRuntimeProcess();
1179
+ }
1180
+ function captureConsole(requestId, captureStdio) {
1181
+ const original = console;
1182
+ if (!captureStdio) {
1183
+ const sandboxConsole = {
1184
+ log: () => undefined,
1185
+ info: () => undefined,
1186
+ warn: () => undefined,
1187
+ error: () => undefined,
1188
+ };
1189
+ globalThis.console = sandboxConsole;
1190
+ return {
1191
+ restore: () => {
1192
+ globalThis.console = original;
1193
+ },
1194
+ };
1195
+ }
1196
+ const sandboxConsole = {
1197
+ log: (...args) => emitStdio(requestId, "stdout", args),
1198
+ info: (...args) => emitStdio(requestId, "stdout", args),
1199
+ warn: (...args) => emitStdio(requestId, "stderr", args),
1200
+ error: (...args) => emitStdio(requestId, "stderr", args),
1201
+ };
1202
+ globalThis.console = sandboxConsole;
1203
+ return {
1204
+ restore: () => {
1205
+ globalThis.console = original;
1206
+ },
1207
+ };
1208
+ }
1209
+ function updateProcessConfig(options, timingMitigation, frozenTimeMs) {
1210
+ if (runtimeProcessConfig) {
1211
+ runtimeProcessConfig.timingMitigation = timingMitigation;
1212
+ if (frozenTimeMs === undefined) {
1213
+ delete runtimeProcessConfig.frozenTimeMs;
1214
+ }
1215
+ else {
1216
+ runtimeProcessConfig.frozenTimeMs = frozenTimeMs;
1217
+ }
1218
+ runtimeProcessConfig.stdin = options?.stdin ?? "";
1219
+ if (options?.env) {
1220
+ const filtered = filterEnv(options.env, permissions);
1221
+ const currentEnv = runtimeProcessConfig.env && typeof runtimeProcessConfig.env === "object"
1222
+ ? runtimeProcessConfig.env
1223
+ : {};
1224
+ runtimeProcessConfig.env = { ...currentEnv, ...filtered };
1225
+ }
1226
+ }
1227
+ refreshRuntimeProcess();
1228
+ const proc = getRuntimeProcess();
1229
+ if (!proc)
1230
+ return;
1231
+ proc.exitCode = 0;
1232
+ proc.timingMitigation = timingMitigation;
1233
+ if (frozenTimeMs === undefined) {
1234
+ delete proc.frozenTimeMs;
1235
+ }
1236
+ else {
1237
+ proc.frozenTimeMs = frozenTimeMs;
1238
+ }
1239
+ if (options?.cwd && typeof proc.chdir === "function") {
1240
+ exposeMutableRuntimeStateGlobal("__runtimeProcessCwdOverride", options.cwd);
1241
+ globalEval(getIsolateRuntimeSource("overrideProcessCwd"));
1242
+ try {
1243
+ proc.chdir(options.cwd);
1244
+ }
1245
+ catch (error) {
1246
+ if (!(error instanceof Error &&
1247
+ error.message.includes("process.chdir() is not supported in workers"))) {
1248
+ throw error;
1249
+ }
1250
+ }
1251
+ }
1252
+ }
1253
+ /**
1254
+ * Execute user code as a script (process-style). Transforms ESM/dynamic
1255
+ * imports, sets up module/exports globals, and waits for active handles.
1256
+ */
1257
+ async function execScript(requestId, code, options, captureStdio = false) {
1258
+ resetModuleState(options?.cwd ?? "/");
1259
+ const timingMitigation = options?.timingMitigation ?? runtimeTimingMitigation;
1260
+ const frozenTimeMs = applyTimingMitigation(timingMitigation);
1261
+ updateProcessConfig(options, timingMitigation, frozenTimeMs);
1262
+ setDynamicImportFallback();
1263
+ const previousProcessRequestId = activeProcessRequestId;
1264
+ activeProcessRequestId = captureStdio ? requestId : null;
1265
+ const { restore } = captureConsole(requestId, captureStdio);
1266
+ try {
1267
+ let transformed = code;
1268
+ if (isESM(code, options?.filePath)) {
1269
+ transformed = transform(transformed, { transforms: ["imports"] }).code;
1270
+ }
1271
+ transformed = transformDynamicImport(transformed);
1272
+ exposeMutableRuntimeStateGlobal("module", { exports: {} });
1273
+ const moduleRef = globalThis.module;
1274
+ exposeMutableRuntimeStateGlobal("exports", moduleRef.exports);
1275
+ if (options?.filePath) {
1276
+ const dirname = options.filePath.includes("/")
1277
+ ? options.filePath.substring(0, options.filePath.lastIndexOf("/")) ||
1278
+ "/"
1279
+ : "/";
1280
+ exposeMutableRuntimeStateGlobal("__filename", options.filePath);
1281
+ exposeMutableRuntimeStateGlobal("__dirname", dirname);
1282
+ exposeMutableRuntimeStateGlobal("_currentModule", {
1283
+ dirname,
1284
+ filename: options.filePath,
1285
+ });
1286
+ }
1287
+ // Await the eval result so async IIFEs / top-level promise expressions
1288
+ // resolve before we check for active handles.
1289
+ const evalResult = globalEval(transformed);
1290
+ if (evalResult &&
1291
+ typeof evalResult === "object" &&
1292
+ typeof evalResult.then === "function") {
1293
+ await evalResult;
1294
+ }
1295
+ await Promise.resolve();
1296
+ const waitForActiveHandles = globalThis
1297
+ ._waitForActiveHandles;
1298
+ if (typeof waitForActiveHandles === "function") {
1299
+ await waitForActiveHandles();
1300
+ }
1301
+ const exitCode = globalThis.process
1302
+ ?.exitCode ?? 0;
1303
+ return {
1304
+ code: exitCode,
1305
+ };
1306
+ }
1307
+ catch (err) {
1308
+ const message = err instanceof Error ? err.message : String(err);
1309
+ const exitMatch = message.match(/process\.exit\((\d+)\)/);
1310
+ if (exitMatch) {
1311
+ const exitCode = Number.parseInt(exitMatch[1], 10);
1312
+ return {
1313
+ code: exitCode,
1314
+ };
1315
+ }
1316
+ return {
1317
+ code: 1,
1318
+ errorMessage: boundErrorMessage(message),
1319
+ };
1320
+ }
1321
+ finally {
1322
+ activeProcessRequestId = previousProcessRequestId;
1323
+ restore();
1324
+ }
1325
+ }
1326
+ async function runScript(requestId, code, filePath, captureStdio = false) {
1327
+ const execResult = await execScript(requestId, code, { filePath }, captureStdio);
1328
+ const moduleObj = globalThis.module;
1329
+ return {
1330
+ ...execResult,
1331
+ exports: moduleObj?.exports,
1332
+ };
1333
+ }
1334
+ self.onmessage = async (event) => {
1335
+ const message = event.data;
1336
+ try {
1337
+ if (message.type === "init") {
1338
+ if (typeof message.controlToken !== "string" ||
1339
+ message.controlToken.length === 0) {
1340
+ return;
1341
+ }
1342
+ if (controlToken && message.controlToken !== controlToken) {
1343
+ return;
1344
+ }
1345
+ controlToken = message.controlToken;
1346
+ await initRuntime(message.payload);
1347
+ postResponse({
1348
+ type: "response",
1349
+ id: message.id,
1350
+ ok: true,
1351
+ result: true,
1352
+ });
1353
+ return;
1354
+ }
1355
+ if (!controlToken || message.controlToken !== controlToken) {
1356
+ return;
1357
+ }
1358
+ if (!initialized) {
1359
+ throw new Error("Sandbox worker not initialized");
1360
+ }
1361
+ if (message.type === "exec") {
1362
+ const result = await execScript(message.id, message.payload.code, message.payload.options, message.payload.captureStdio);
1363
+ postResponse({ type: "response", id: message.id, ok: true, result });
1364
+ return;
1365
+ }
1366
+ if (message.type === "run") {
1367
+ const result = await runScript(message.id, message.payload.code, message.payload.filePath, message.payload.captureStdio);
1368
+ postResponse({ type: "response", id: message.id, ok: true, result });
1369
+ return;
1370
+ }
1371
+ if (message.type === "extension") {
1372
+ const error = new Error(`Browser worker extension dispatch is not implemented for namespace ${message.payload.namespace}`);
1373
+ error.code = "ERR_SECURE_EXEC_BROWSER_EXTENSION_UNSUPPORTED";
1374
+ throw error;
1375
+ }
1376
+ if (message.type === "dispose") {
1377
+ postResponse({
1378
+ type: "response",
1379
+ id: message.id,
1380
+ ok: true,
1381
+ result: true,
1382
+ });
1383
+ close();
1384
+ }
1385
+ }
1386
+ catch (err) {
1387
+ const error = err;
1388
+ postResponse({
1389
+ type: "response",
1390
+ id: message.id,
1391
+ ok: false,
1392
+ error: {
1393
+ message: error?.message ?? String(err),
1394
+ stack: error?.stack,
1395
+ code: error?.code,
1396
+ },
1397
+ });
1398
+ }
1399
+ };