@volter/tabnode 0.5.25 → 0.5.27
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/assets/{runtime-worker-D6N3DujB.js → runtime-worker-DPvtXRd8.js} +248 -21
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +288 -599
- package/dist/port-bridge-CixAESwA.js +655 -0
- package/dist/port-bridge.d.ts +175 -0
- package/dist/port-bridge.mjs +6 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/server-bridge.d.ts +9 -130
- package/package.json +5 -1
- package/src/index.ts +2 -1
- package/src/port-bridge.ts +791 -0
- package/src/runtime.ts +73 -38
- package/src/server-bridge.ts +22 -664
package/src/runtime.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { forGuestRealm, installGuestRealm, takeFromHost, defineOnHost, heldWork
|
|
|
12
12
|
import type { IRuntime, IExecuteResult, IRuntimeOptions } from './runtime-interface';
|
|
13
13
|
import type { PackageJson } from './types/package-json';
|
|
14
14
|
import { simpleHash } from './utils/hash';
|
|
15
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
16
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
15
17
|
import { uint8ToBase64, uint8ToHex } from './utils/binary-encoding';
|
|
16
18
|
import { createFsShim, FsShim } from './shims/fs';
|
|
17
19
|
import * as pathShim from './shims/path';
|
|
@@ -423,6 +425,66 @@ Object.defineProperty(globalThis, "__substrateEvalSource", {
|
|
|
423
425
|
configurable: true,
|
|
424
426
|
value: (source: unknown): unknown => typeof source === "string" ? __substrateRewriteDynamicImportsInScript(source) : source,
|
|
425
427
|
});
|
|
428
|
+
/** A file's source made a body: shebang stripped, types stripped, ESM lowered, dynamic imports rewritten. */
|
|
429
|
+
function __substratePrepareBody(rawCode: string, resolvedPath: string, format: string | undefined, process: unknown): string {
|
|
430
|
+
let code = rawCode;
|
|
431
|
+
|
|
432
|
+
// Strip shebang line if present (e.g. #!/usr/bin/env node)
|
|
433
|
+
if (code.startsWith('#!')) {
|
|
434
|
+
code = code.slice(code.indexOf('\n') + 1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
code = __substrateModuleTypes(code, resolvedPath, format, process);
|
|
438
|
+
|
|
439
|
+
// Transform ESM to CJS if needed (for .mjs files or ESM that wasn't pre-transformed)
|
|
440
|
+
// transformEsmToCjs uses AST to handle import/export, import.meta, and dynamic imports
|
|
441
|
+
// It also handles already-CJS files safely (AST finds no ESM nodes → no-op)
|
|
442
|
+
const __commonjs = format === 'commonjs' || format === 'commonjs-typescript'
|
|
443
|
+
? true
|
|
444
|
+
: format === 'module' || format === 'module-typescript'
|
|
445
|
+
? false
|
|
446
|
+
: resolvedPath.endsWith('.cjs') || resolvedPath.endsWith('.cts');
|
|
447
|
+
if (!__commonjs) {
|
|
448
|
+
// An ES module is strict wherever it runs; the mark says so to the
|
|
449
|
+
// compile below, which is otherwise sloppy as a CommonJS body is.
|
|
450
|
+
// The directive shares the body's first line, as Node's one-line
|
|
451
|
+
// wrapper does, so a frame of an ES module counts the file's own lines;
|
|
452
|
+
// on a line of its own it put every frame one line below the file's.
|
|
453
|
+
const lowered = transformEsmToCjs(code, resolvedPath);
|
|
454
|
+
code = lowered === code ? code : `${__substrateModuleMarker}"use strict";${lowered}`;
|
|
455
|
+
} else {
|
|
456
|
+
// A `.cjs` module skips the ESM transform, and with it the rewrite of
|
|
457
|
+
// `import(...)` to the engine's dynamic import, so its
|
|
458
|
+
// `import("fs/promises")` reached the browser's own import and failed on
|
|
459
|
+
// the bare specifier. The dynamic-import rewrite applies to `.cjs` too.
|
|
460
|
+
code = transformDynamicImportsRegex(code);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return code;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Module bodies prepared where the image is built. Parsing a package's files
|
|
468
|
+
* for the passes above is most of a `require` in a tab: 36 of 45 s for
|
|
469
|
+
* playwright-core's, measured in a tab, against 0.9 s under Node on the same
|
|
470
|
+
* machine. The image carries each body under the hash of the file it was made
|
|
471
|
+
* from, in this directory of the tab's filesystem, and the loader takes it in
|
|
472
|
+
* place of the passes when the file it read hashes to one.
|
|
473
|
+
*/
|
|
474
|
+
export const PREPARED_MODULES_DIR = '/.tabnode/prepared';
|
|
475
|
+
const PREPARED_MODULES_FORMAT = 'tabnode-prepared-1';
|
|
476
|
+
/** The name a prepared body goes under: the hash of the file as read, and how it is compiled. Undefined for a file no body is prepared for. */
|
|
477
|
+
export function preparedModuleKey(rawCode: string, resolvedPath: string): string | undefined {
|
|
478
|
+
const extension = /\.(js|cjs|mjs)$/u.exec(resolvedPath)?.[1];
|
|
479
|
+
if (!extension) return undefined;
|
|
480
|
+
const kind = extension === 'cjs' ? 'cjs' : 'js';
|
|
481
|
+
return bytesToHex(sha256(new TextEncoder().encode(`${PREPARED_MODULES_FORMAT}|${kind}|${rawCode}`)));
|
|
482
|
+
}
|
|
483
|
+
/** The body the loader would compile for this file, with no load hooks and no type stripping: what the image carries. */
|
|
484
|
+
export function prepareModuleForImage(rawCode: string, resolvedPath: string): string {
|
|
485
|
+
return __substrateScopeGlobalCalls(__substratePrepareBody(rawCode, resolvedPath, undefined, { execArgv: [], env: {} }));
|
|
486
|
+
}
|
|
487
|
+
|
|
426
488
|
function transformEsmToCjs(code: string, filename: string): string {
|
|
427
489
|
// Quick check: does the code have any ESM-like patterns?
|
|
428
490
|
const maybeEsm = __substrateHasEsmSyntax(code);
|
|
@@ -1789,6 +1851,14 @@ function createRequire(
|
|
|
1789
1851
|
const rawCode = source === undefined || source === null ? defaultSource() : String(source);
|
|
1790
1852
|
const dirname = resolvedPath.startsWith('data:') ? currentDir : pathShim.dirname(resolvedPath);
|
|
1791
1853
|
|
|
1854
|
+
if (format === undefined && !transformsTypes(process as { execArgv?: string[]; env?: Record<string, string> }) && vfs.existsSync(PREPARED_MODULES_DIR)) {
|
|
1855
|
+
const key = preparedModuleKey(rawCode, resolvedPath);
|
|
1856
|
+
const prepared = key ? `${PREPARED_MODULES_DIR}/${key}` : undefined;
|
|
1857
|
+
if (prepared && vfs.existsSync(prepared)) {
|
|
1858
|
+
runModuleBody(module, vfs.readFileSync(prepared, 'utf8'), resolvedPath, dirname, true);
|
|
1859
|
+
return module;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1792
1862
|
runModuleBody(module, prepareModuleCode(rawCode, resolvedPath, format), resolvedPath, dirname);
|
|
1793
1863
|
return module;
|
|
1794
1864
|
};
|
|
@@ -1808,45 +1878,10 @@ function createRequire(
|
|
|
1808
1878
|
// Use a simple hash of the content for cache key to handle content changes
|
|
1809
1879
|
const codeCacheKey = `${resolvedPath}|${format ?? ''}|${transformsTypes(process as { execArgv?: string[]; env?: Record<string, string> }) ? 'transform-types|' : ''}${simpleHash(rawCode)}`;
|
|
1810
1880
|
let code = processedCodeCache?.get(codeCacheKey);
|
|
1811
|
-
|
|
1812
1881
|
if (!code) {
|
|
1813
|
-
code = rawCode;
|
|
1814
|
-
|
|
1815
|
-
// Strip shebang line if present (e.g. #!/usr/bin/env node)
|
|
1816
|
-
if (code.startsWith('#!')) {
|
|
1817
|
-
code = code.slice(code.indexOf('\n') + 1);
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
code = __substrateModuleTypes(code, resolvedPath, format, process);
|
|
1821
|
-
|
|
1822
|
-
// Transform ESM to CJS if needed (for .mjs files or ESM that wasn't pre-transformed)
|
|
1823
|
-
// transformEsmToCjs uses AST to handle import/export, import.meta, and dynamic imports
|
|
1824
|
-
// It also handles already-CJS files safely (AST finds no ESM nodes → no-op)
|
|
1825
|
-
const __commonjs = format === 'commonjs' || format === 'commonjs-typescript'
|
|
1826
|
-
? true
|
|
1827
|
-
: format === 'module' || format === 'module-typescript'
|
|
1828
|
-
? false
|
|
1829
|
-
: resolvedPath.endsWith('.cjs') || resolvedPath.endsWith('.cts');
|
|
1830
|
-
if (!__commonjs) {
|
|
1831
|
-
// An ES module is strict wherever it runs; the mark says so to the
|
|
1832
|
-
// compile below, which is otherwise sloppy as a CommonJS body is.
|
|
1833
|
-
// The directive shares the body's first line, as Node's one-line
|
|
1834
|
-
// wrapper does, so a frame of an ES module counts the file's own lines;
|
|
1835
|
-
// on a line of its own it put every frame one line below the file's.
|
|
1836
|
-
const lowered = transformEsmToCjs(code, resolvedPath);
|
|
1837
|
-
code = lowered === code ? code : `${__substrateModuleMarker}"use strict";${lowered}`;
|
|
1838
|
-
} else {
|
|
1839
|
-
// A `.cjs` module skips the ESM transform, and with it the rewrite of
|
|
1840
|
-
// `import(...)` to the engine's dynamic import, so its
|
|
1841
|
-
// `import("fs/promises")` reached the browser's own import and failed on
|
|
1842
|
-
// the bare specifier. The dynamic-import rewrite applies to `.cjs` too.
|
|
1843
|
-
code = transformDynamicImportsRegex(code);
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
// Cache the processed code
|
|
1882
|
+
code = __substratePrepareBody(rawCode, resolvedPath, format, process);
|
|
1847
1883
|
processedCodeCache?.set(codeCacheKey, code);
|
|
1848
1884
|
}
|
|
1849
|
-
|
|
1850
1885
|
return code;
|
|
1851
1886
|
};
|
|
1852
1887
|
|
|
@@ -1858,7 +1893,7 @@ function createRequire(
|
|
|
1858
1893
|
* hands the result to it; the engine's own pipeline is what makes the
|
|
1859
1894
|
* body Node-shaped here.
|
|
1860
1895
|
*/
|
|
1861
|
-
const runModuleBody = (module: Module, prepared: string, resolvedPath: string, dirname: string): void => {
|
|
1896
|
+
const runModuleBody = (module: Module, prepared: string, resolvedPath: string, dirname: string, scoped = false): void => {
|
|
1862
1897
|
let code = prepared;
|
|
1863
1898
|
// Create require for this module
|
|
1864
1899
|
const moduleRequire = createRequire(
|
|
@@ -1884,7 +1919,7 @@ function createRequire(
|
|
|
1884
1919
|
try {
|
|
1885
1920
|
const importMetaUrl = resolvedPath.startsWith('data:') ? resolvedPath : 'file://' + resolvedPath;
|
|
1886
1921
|
const strictBody = code.startsWith(__substrateModuleMarker);
|
|
1887
|
-
code = __substrateScopeGlobalCalls(code);
|
|
1922
|
+
if (!scoped) code = __substrateScopeGlobalCalls(code);
|
|
1888
1923
|
// The wrapper is one line and the body begins on it, as Node's
|
|
1889
1924
|
// `Module.wrap` is one line, so a module's line N is line N of the
|
|
1890
1925
|
// script V8 compiles and a stack names the module's own lines. The
|