@deepseek-ai/dsh-experimental-webworker-runtime 0.2.1-alpha.2 → 0.2.1-alpha.4
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/README.i18n.yaml +4 -4
- package/README.md +11 -5
- package/README.zh.md +11 -5
- package/lib/index.js +450 -104
- package/lib/types/image-layout.d.ts +13 -4
- package/lib/types/image-manifest.d.ts +42 -0
- package/lib/types/index.d.ts +3 -2
- package/lib/types/module-system/module-loader.d.ts +20 -6
- package/lib/types/node/builtin_modules/implemented/path.d.ts +8 -2
- package/lib/types/node/builtin_modules/implemented/util.d.ts +7 -0
- package/lib/types/node/external_packages/node-addon-require-builtin.d.ts +8 -0
- package/lib/types/node/globals/process.d.ts +3 -3
- package/lib/types/node/internal_modules/esm-loader.d.ts +50 -0
- package/lib/types/node/internal_modules/module-map.d.ts +59 -0
- package/lib/types/node/internal_modules/package-surfaces.d.ts +29 -0
- package/lib/types/node/notImplementedFail.d.ts +7 -3
- package/lib/types/storage/memory.d.ts +23 -2
- package/lib/types/storage/paths.d.ts +1 -1
- package/lib/types/storage/types.d.ts +33 -0
- package/lib/types/worker-host.d.ts +8 -9
- package/lib/worker.js +689 -163
- package/package.json +25 -25
package/lib/worker.js
CHANGED
|
@@ -101,6 +101,202 @@ function createAlsRuntime(causality) {
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/node/notImplementedFail.ts
|
|
106
|
+
/**
|
|
107
|
+
* Structural not-implemented stubs: a replaced module must expose every symbol
|
|
108
|
+
* its importers name (a missing CommonJS symbol degrades to `undefined` at call
|
|
109
|
+
* time instead of failing at link time), and every one of those symbols must
|
|
110
|
+
* report exactly what is unavailable when it is finally called. The refusal
|
|
111
|
+
* carries Node's `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` code, so a caller that
|
|
112
|
+
* can run without the feature distinguishes it from other failures.
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Build a function that throws naming its module and symbol. The refusal is
|
|
116
|
+
* also written to the console before it propagates: callers routinely swallow
|
|
117
|
+
* these errors far from their cause, and the console line is what places the
|
|
118
|
+
* failure while debugging a worker session.
|
|
119
|
+
*
|
|
120
|
+
* `Face` is the Node declaration this stub stands in for, so the replaced module
|
|
121
|
+
* publishes the type its importers compile against. The value is one throwing
|
|
122
|
+
* function whatever that declaration says: a caller reaches the throw before any
|
|
123
|
+
* declared parameter, return value, or `new` result exists, so the assertion
|
|
124
|
+
* below cannot be observed as a lie. It is a function expression rather than an
|
|
125
|
+
* arrow because a stub standing in for a class must refuse under `new` too, and
|
|
126
|
+
* an arrow has no construct behavior to reach.
|
|
127
|
+
* @param module - module specifier being stubbed.
|
|
128
|
+
* @param symbol - exported symbol name.
|
|
129
|
+
* @returns the throwing stand-in, typed as the member it replaces.
|
|
130
|
+
*/
|
|
131
|
+
function notImplementedFail(module, symbol) {
|
|
132
|
+
return (function refuse() {
|
|
133
|
+
throw notAvailableError(module, symbol);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/** Node's standard code for a feature the running platform cannot provide. */
|
|
137
|
+
const FEATURE_UNAVAILABLE_ON_PLATFORM = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
138
|
+
/**
|
|
139
|
+
* Build the refusal error and write it to the console first, for stubs that
|
|
140
|
+
* cannot be a plain throwing function (constructors, methods on structural
|
|
141
|
+
* fakes).
|
|
142
|
+
* @param module - module specifier being stubbed.
|
|
143
|
+
* @param symbol - unavailable member, named as the importer sees it.
|
|
144
|
+
* @returns the error to throw, with `code` set to Node's `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM`.
|
|
145
|
+
*/
|
|
146
|
+
function notAvailableError(module, symbol) {
|
|
147
|
+
const message = `web-preview: ${module}.${symbol} is not available in the worker host`;
|
|
148
|
+
console.error(message);
|
|
149
|
+
return Object.assign(new Error(message), { code: FEATURE_UNAVAILABLE_ON_PLATFORM });
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/node/internal_modules/module-map.ts
|
|
153
|
+
/**
|
|
154
|
+
* `internal/modules/esm/module_map` for the Worker, following Node v24.18.0. The Worker evaluates no ES module
|
|
155
|
+
* jobs, so a {@link LoadCache} stays empty and refuses to store one.
|
|
156
|
+
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/internal_modules/module-map
|
|
157
|
+
*/
|
|
158
|
+
/** Job slot Node uses when an import names no `type` attribute. */
|
|
159
|
+
const IMPLICIT_TYPE_ATTRIBUTE = "javascript";
|
|
160
|
+
/**
|
|
161
|
+
* Node's `validateString` refusal.
|
|
162
|
+
* @param value - Argument to check.
|
|
163
|
+
* @param name - Argument name in the message.
|
|
164
|
+
*/
|
|
165
|
+
function validateString(value, name) {
|
|
166
|
+
if (typeof value === "string") return;
|
|
167
|
+
throw Object.assign(/* @__PURE__ */ new TypeError(`The "${name}" argument must be of type string. Received ${typeof value}`), { code: "ERR_INVALID_ARG_TYPE" });
|
|
168
|
+
}
|
|
169
|
+
/** Resolutions keyed by parent URL, then by serialized module request. */
|
|
170
|
+
var ResolveCache = class extends Map {
|
|
171
|
+
/**
|
|
172
|
+
* Serialize a module request: the specifier and its import attributes sorted by key.
|
|
173
|
+
* @param specifier - Requested module.
|
|
174
|
+
* @param importAttributes - Import attributes of the request.
|
|
175
|
+
* @returns The cache key Node uses for this request.
|
|
176
|
+
*/
|
|
177
|
+
serializeKey(specifier, importAttributes) {
|
|
178
|
+
const keys = Object.keys(importAttributes);
|
|
179
|
+
if (keys.length === 0) return `${specifier}::`;
|
|
180
|
+
return `${specifier}::${keys.sort().map((key) => JSON.stringify(key) + JSON.stringify(importAttributes[key])).join(",")}`;
|
|
181
|
+
}
|
|
182
|
+
#imports(parentURL) {
|
|
183
|
+
const existing = super.get(parentURL);
|
|
184
|
+
if (existing !== void 0) return existing;
|
|
185
|
+
const created = Object.create(null);
|
|
186
|
+
super.set(parentURL, created);
|
|
187
|
+
return created;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Read one cached resolution.
|
|
191
|
+
* @param serializedKey - Key from {@link serializeKey}.
|
|
192
|
+
* @param parentURL - URL of the importing module.
|
|
193
|
+
* @returns The cached resolution, if any.
|
|
194
|
+
*/
|
|
195
|
+
get(serializedKey, parentURL) {
|
|
196
|
+
return this.#imports(parentURL)[serializedKey];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Store one resolution.
|
|
200
|
+
* @param serializedKey - Key from {@link serializeKey}.
|
|
201
|
+
* @param parentURL - URL of the importing module.
|
|
202
|
+
* @param result - Resolution to cache.
|
|
203
|
+
* @returns This cache.
|
|
204
|
+
*/
|
|
205
|
+
set(serializedKey, parentURL, result) {
|
|
206
|
+
this.#imports(parentURL)[serializedKey] = result;
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check for one cached resolution.
|
|
211
|
+
* @param serializedKey - Key from {@link serializeKey}.
|
|
212
|
+
* @param parentURL - URL of the importing module.
|
|
213
|
+
* @returns Whether the request has a cached resolution.
|
|
214
|
+
*/
|
|
215
|
+
has(serializedKey, parentURL) {
|
|
216
|
+
return serializedKey in this.#imports(parentURL);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
/** Module jobs keyed by URL, then by import `type` attribute; always empty in the Worker. */
|
|
220
|
+
var LoadCache = class extends Map {
|
|
221
|
+
/**
|
|
222
|
+
* Read the job for one URL and type.
|
|
223
|
+
* @param url - Module URL.
|
|
224
|
+
* @param type - Import `type` attribute.
|
|
225
|
+
* @returns The cached job, if any.
|
|
226
|
+
*/
|
|
227
|
+
get(url, type = IMPLICIT_TYPE_ATTRIBUTE) {
|
|
228
|
+
validateString(url, "url");
|
|
229
|
+
validateString(type, "type");
|
|
230
|
+
return super.get(url)?.[type];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Refuse to store a job: the Worker creates no ES module jobs.
|
|
234
|
+
* @param url - Module URL.
|
|
235
|
+
* @param type - Import `type` attribute.
|
|
236
|
+
* @param _job - Job Node would store.
|
|
237
|
+
* @returns Never; always throws.
|
|
238
|
+
*/
|
|
239
|
+
set(url, type = IMPLICIT_TYPE_ATTRIBUTE, _job) {
|
|
240
|
+
validateString(url, "url");
|
|
241
|
+
validateString(type, "type");
|
|
242
|
+
throw notAvailableError("internal/modules/esm/module_map", "LoadCache.set");
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Check for the job of one URL and type.
|
|
246
|
+
* @param url - Module URL.
|
|
247
|
+
* @param type - Import `type` attribute.
|
|
248
|
+
* @returns Whether a job is cached.
|
|
249
|
+
*/
|
|
250
|
+
has(url, type = IMPLICIT_TYPE_ATTRIBUTE) {
|
|
251
|
+
validateString(url, "url");
|
|
252
|
+
validateString(type, "type");
|
|
253
|
+
return super.get(url)?.[type] !== void 0;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/node/internal_modules/esm-loader.ts
|
|
258
|
+
/**
|
|
259
|
+
* `internal/modules/esm/loader` cascaded loader for the Worker. {@link createCascadedLoader} builds the object with
|
|
260
|
+
* the Node v24.18.0 (v2) loader members this repository calls: `resolveSync` and `import` over one Worker module
|
|
261
|
+
* loader, and an empty `loadCache`. ES module jobs are unavailable on this platform.
|
|
262
|
+
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/internal_modules/esm-loader
|
|
263
|
+
*/
|
|
264
|
+
/**
|
|
265
|
+
* Translate a Worker resolution failure into the code Node's ESM resolver reports.
|
|
266
|
+
* @param error - Failure thrown by the Worker module loader.
|
|
267
|
+
* @returns The same failure with an ESM resolution code.
|
|
268
|
+
*/
|
|
269
|
+
function esmResolutionError(error) {
|
|
270
|
+
if (error instanceof Error && error.code === "MODULE_NOT_FOUND") return Object.assign(error, { code: "ERR_MODULE_NOT_FOUND" });
|
|
271
|
+
return error;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Build the cascaded loader for one Worker module loader.
|
|
275
|
+
* @param source - The Worker module loader's resolution and loading operations.
|
|
276
|
+
* @returns A cascaded loader delegating to `source`.
|
|
277
|
+
*/
|
|
278
|
+
function createCascadedLoader(source) {
|
|
279
|
+
return {
|
|
280
|
+
loadCache: new LoadCache(),
|
|
281
|
+
resolveSync(parentURL, request) {
|
|
282
|
+
try {
|
|
283
|
+
const { url, format } = source.resolveSync(request.specifier, parentURL);
|
|
284
|
+
return {
|
|
285
|
+
url,
|
|
286
|
+
format
|
|
287
|
+
};
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw esmResolutionError(error);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
import(specifier, parentURL) {
|
|
293
|
+
return source.import(specifier, parentURL);
|
|
294
|
+
},
|
|
295
|
+
getOrCreateModuleJob() {
|
|
296
|
+
throw notAvailableError("internal/modules/esm/loader", "getOrCreateModuleJob");
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
104
300
|
/**
|
|
105
301
|
* Collapse `.` and `..` segments.
|
|
106
302
|
* @param path - Path with any number of separators.
|
|
@@ -236,10 +432,11 @@ const WRAPPER_PARAMS = [
|
|
|
236
432
|
//#endregion
|
|
237
433
|
//#region src/module-system/module-loader.ts
|
|
238
434
|
/**
|
|
239
|
-
* CommonJS module loader over the worker VFS. It
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
435
|
+
* CommonJS module loader over the worker VFS. It backs the Node internal ESM
|
|
436
|
+
* loader that `node-addon-require-builtin` answers, which Cordis's Loader uses
|
|
437
|
+
* for every entry import, and the `node:module` `createRequire` proxy that
|
|
438
|
+
* `typert-loader`, `client-modules`, and the plugin package inventory resolve
|
|
439
|
+
* package metadata through.
|
|
243
440
|
*
|
|
244
441
|
* Resolution is a narrowed Node `require` algorithm: `exports` walk with a
|
|
245
442
|
* fixed condition order, extension probing, and one cache keyed by resolved
|
|
@@ -275,9 +472,11 @@ var WorkerModuleLoader = class {
|
|
|
275
472
|
modules = /* @__PURE__ */ new Map();
|
|
276
473
|
manifests = /* @__PURE__ */ new Map();
|
|
277
474
|
stack = [];
|
|
475
|
+
cascaded;
|
|
278
476
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
477
|
+
* Resolution and loading over this mount, in Node's v1 argument order. The cascaded ESM loader
|
|
478
|
+
* ({@link cascadedLoader}) and the packer call it; `parentURL` positions relative specifiers and import attributes
|
|
479
|
+
* are ignored.
|
|
281
480
|
*/
|
|
282
481
|
internal;
|
|
283
482
|
constructor(options) {
|
|
@@ -309,9 +508,22 @@ var WorkerModuleLoader = class {
|
|
|
309
508
|
resolveSync: resolveInternal
|
|
310
509
|
};
|
|
311
510
|
}
|
|
312
|
-
|
|
511
|
+
/**
|
|
512
|
+
* The `internal/modules/esm/loader` cascaded loader over this mount, created on first use.
|
|
513
|
+
* @returns This loader's one cascaded loader.
|
|
514
|
+
*/
|
|
515
|
+
cascadedLoader() {
|
|
516
|
+
return this.cascaded ??= createCascadedLoader(this.internal);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Throw this loader's diagnostic.
|
|
520
|
+
* @param detail - What failed.
|
|
521
|
+
* @param code - Node error code for a resolution failure Node also reports by code.
|
|
522
|
+
*/
|
|
523
|
+
fail(detail, code) {
|
|
313
524
|
const chain = this.stack.length === 0 ? "" : ` (importer chain: ${this.stack.join(" -> ")})`;
|
|
314
|
-
|
|
525
|
+
const error = /* @__PURE__ */ new Error(`webworker modules: ${detail}${chain}`);
|
|
526
|
+
throw code === void 0 ? error : Object.assign(error, { code });
|
|
315
527
|
}
|
|
316
528
|
/** @returns Directory a base path or URL resolves specifiers from. */
|
|
317
529
|
baseDirectoryOf(base) {
|
|
@@ -392,7 +604,7 @@ var WorkerModuleLoader = class {
|
|
|
392
604
|
}
|
|
393
605
|
return this.probe(join$1(path, "index"), specifier);
|
|
394
606
|
}
|
|
395
|
-
return this.fail(`cannot resolve "${specifier}": no file at ${candidates.join(", ")}
|
|
607
|
+
return this.fail(`cannot resolve "${specifier}": no file at ${candidates.join(", ")}`, "MODULE_NOT_FOUND");
|
|
396
608
|
}
|
|
397
609
|
/** @returns The Worker-provided implementation of a static specifier. */
|
|
398
610
|
staticModule(specifier) {
|
|
@@ -431,12 +643,12 @@ var WorkerModuleLoader = class {
|
|
|
431
643
|
const packageName = specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0] ?? specifier;
|
|
432
644
|
const rest = specifier.slice(packageName.length).replace(/^\//, "");
|
|
433
645
|
const packageDirectory = join$1(this.root, "node_modules", packageName);
|
|
434
|
-
if (!this.vfs.existsSync(join$1(packageDirectory, "package.json"))) return this.fail(`cannot resolve "${specifier}": ${packageDirectory}/package.json is not in the image
|
|
646
|
+
if (!this.vfs.existsSync(join$1(packageDirectory, "package.json"))) return this.fail(`cannot resolve "${specifier}": ${packageDirectory}/package.json is not in the image`, "MODULE_NOT_FOUND");
|
|
435
647
|
const manifest = this.manifestOf(packageDirectory);
|
|
436
648
|
const subpath = rest === "" ? "." : `./${rest}`;
|
|
437
649
|
if (manifest.exports !== void 0) {
|
|
438
650
|
const target = this.selectExport(manifest.exports, subpath, packageName);
|
|
439
|
-
if (target === void 0) return this.fail(`"${packageName}" does not export "${subpath}" under conditions [${[...this.conditions].join(", ")}]
|
|
651
|
+
if (target === void 0) return this.fail(`"${packageName}" does not export "${subpath}" under conditions [${[...this.conditions].join(", ")}]`, "ERR_PACKAGE_PATH_NOT_EXPORTED");
|
|
440
652
|
return {
|
|
441
653
|
kind: "file",
|
|
442
654
|
path: this.probe(join$1(packageDirectory, target), specifier)
|
|
@@ -614,19 +826,21 @@ function signalProcess(pid, signal) {
|
|
|
614
826
|
/**
|
|
615
827
|
* The `process` global the worker needs before any VFS module runs. Cordis
|
|
616
828
|
* reads `process.env` and `process.versions.node` while the Loader is
|
|
617
|
-
* constructed, and
|
|
618
|
-
* configuration bytes stay identical to the Node deployment. Third-party Node
|
|
829
|
+
* constructed, and bundle patch rows keep their `!!js process.*` expressions, so
|
|
830
|
+
* the configuration bytes stay identical to the Node deployment. Third-party Node
|
|
619
831
|
* packages use the presence of `process.title` to avoid browser-only globals.
|
|
620
832
|
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/globals/process
|
|
621
833
|
*/
|
|
622
834
|
/** Virtual executable identity; the worker has no Node binary behind it. */
|
|
623
835
|
const EXEC_PATH = "/dsh/bin/node";
|
|
836
|
+
/** Node release whose internal-module members `node-addon-require-builtin` follows in the Worker. */
|
|
837
|
+
const NODE_VERSION = "24.18.0";
|
|
624
838
|
/**
|
|
625
839
|
* Publish `globalThis.process`.
|
|
626
840
|
*
|
|
627
|
-
* `versions.node`
|
|
628
|
-
* `ModuleLoader.fromInternal()`
|
|
629
|
-
* internals
|
|
841
|
+
* `versions.node` names the Node release the Worker's `node-addon-require-builtin` internals follow, so Cordis's
|
|
842
|
+
* `ModuleLoader.fromInternal()` reaches them the way it reaches Node's; `execArgv` stays empty, without
|
|
843
|
+
* `--expose-internals`.
|
|
630
844
|
* @param options - Root, environment, and argument vector.
|
|
631
845
|
* @returns The published object, for the module proxy table.
|
|
632
846
|
*/
|
|
@@ -645,8 +859,8 @@ function installProcessGlobal(options) {
|
|
|
645
859
|
platform: "linux",
|
|
646
860
|
arch: "x64",
|
|
647
861
|
pid: 1,
|
|
648
|
-
version:
|
|
649
|
-
versions: { node:
|
|
862
|
+
version: `v${NODE_VERSION}`,
|
|
863
|
+
versions: { node: NODE_VERSION },
|
|
650
864
|
cwd: () => options.cwd,
|
|
651
865
|
getBuiltinModule: (id) => {
|
|
652
866
|
let resolution;
|
|
@@ -1650,6 +1864,8 @@ function resize(bytes, length) {
|
|
|
1650
1864
|
*/
|
|
1651
1865
|
var MemoryVfs = class {
|
|
1652
1866
|
files = /* @__PURE__ */ new Map();
|
|
1867
|
+
/** Files a lazy archive supplies on their first asynchronous read. */
|
|
1868
|
+
deferred = /* @__PURE__ */ new Map();
|
|
1653
1869
|
directories = new Set(["/"]);
|
|
1654
1870
|
/** Directory permission bits; absence means {@link DEFAULT_DIRECTORY_MODE}. */
|
|
1655
1871
|
directoryModes = /* @__PURE__ */ new Map();
|
|
@@ -1698,7 +1914,10 @@ var MemoryVfs = class {
|
|
|
1698
1914
|
}
|
|
1699
1915
|
/** Promise face mirroring `node:fs/promises` for the methods the roster uses. */
|
|
1700
1916
|
promises = {
|
|
1701
|
-
readFile: async (path, options) =>
|
|
1917
|
+
readFile: async (path, options) => {
|
|
1918
|
+
await this.whenReadable(path);
|
|
1919
|
+
return this.readFileSync(path, options);
|
|
1920
|
+
},
|
|
1702
1921
|
writeFile: async (path, data, options) => {
|
|
1703
1922
|
this.writeFileSync(path, data, options);
|
|
1704
1923
|
},
|
|
@@ -1730,11 +1949,13 @@ var MemoryVfs = class {
|
|
|
1730
1949
|
this.chmodSync(path, mode);
|
|
1731
1950
|
},
|
|
1732
1951
|
opendir: async (path) => this.opendir(path),
|
|
1733
|
-
open: async (path, flags, mode) =>
|
|
1952
|
+
open: async (path, flags, mode) => {
|
|
1953
|
+
await this.whenReadable(path);
|
|
1954
|
+
return this.open(path, flags, mode);
|
|
1955
|
+
},
|
|
1734
1956
|
/** Resolves for any existing path: the VFS grants read and write to everything it holds. */
|
|
1735
1957
|
access: async (path) => {
|
|
1736
|
-
|
|
1737
|
-
if (!this.files.has(target) && !this.directories.has(target)) fail("ENOENT", "access", target);
|
|
1958
|
+
if (!this.existsSync(path)) fail("ENOENT", "access", normalize$1(resolve$2(path)));
|
|
1738
1959
|
}
|
|
1739
1960
|
};
|
|
1740
1961
|
/** @returns Absolute path with no trailing separator. */
|
|
@@ -1743,6 +1964,101 @@ var MemoryVfs = class {
|
|
|
1743
1964
|
return absolute.length > 1 && absolute.endsWith("/") ? absolute.slice(0, -1) : absolute;
|
|
1744
1965
|
}
|
|
1745
1966
|
/**
|
|
1967
|
+
* Register files whose bytes arrive with one archive on the first
|
|
1968
|
+
* asynchronous read of any of them. Their parent directories are created now.
|
|
1969
|
+
* @param archive - Archive name, mount root, registered files, and fetch.
|
|
1970
|
+
* @throws When a registered path already holds a file, directory, or another registration.
|
|
1971
|
+
*/
|
|
1972
|
+
deferArchive(archive) {
|
|
1973
|
+
const root = this.key(archive.root);
|
|
1974
|
+
const files = /* @__PURE__ */ new Map();
|
|
1975
|
+
for (const file of archive.files) {
|
|
1976
|
+
const target = this.key(join$1(root, file.path));
|
|
1977
|
+
if (this.files.has(target) || this.directories.has(target) || this.deferred.has(target) || files.has(target)) throw new Error(`webworker vfs: ${target} is already registered, so lazy archive ${archive.name} cannot supply it`);
|
|
1978
|
+
files.set(target, file.size);
|
|
1979
|
+
}
|
|
1980
|
+
const state = {
|
|
1981
|
+
name: archive.name,
|
|
1982
|
+
root,
|
|
1983
|
+
load: () => archive.load(),
|
|
1984
|
+
files,
|
|
1985
|
+
fetching: void 0
|
|
1986
|
+
};
|
|
1987
|
+
const mtimeMs = Date.now();
|
|
1988
|
+
for (const [target, size] of files) {
|
|
1989
|
+
this.seedDirectory(dirname$1(target));
|
|
1990
|
+
this.deferred.set(target, {
|
|
1991
|
+
size,
|
|
1992
|
+
mode: DEFAULT_FILE_MODE,
|
|
1993
|
+
mtimeMs,
|
|
1994
|
+
archive: state
|
|
1995
|
+
});
|
|
1996
|
+
this.touchDirectory(dirname$1(target));
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Fetch the lazy archive that supplies a registered path. Concurrent readers
|
|
2001
|
+
* of one archive share one fetch; a failed fetch leaves its files registered,
|
|
2002
|
+
* so the next read fetches again.
|
|
2003
|
+
* @param path - Path about to be read asynchronously.
|
|
2004
|
+
* @returns A promise that settles once the archive arrived, or undefined when the path needs no fetch.
|
|
2005
|
+
*/
|
|
2006
|
+
whenReadable(path) {
|
|
2007
|
+
const file = this.deferred.get(this.key(path));
|
|
2008
|
+
if (file === void 0) return void 0;
|
|
2009
|
+
file.archive.fetching ??= this.fetchArchive(file.archive);
|
|
2010
|
+
return file.archive.fetching;
|
|
2011
|
+
}
|
|
2012
|
+
/** Fetch, validate against the registration, and mount one archive's files. */
|
|
2013
|
+
async fetchArchive(state) {
|
|
2014
|
+
try {
|
|
2015
|
+
let archive;
|
|
2016
|
+
try {
|
|
2017
|
+
archive = await state.load();
|
|
2018
|
+
} catch (error) {
|
|
2019
|
+
throw new Error(`webworker vfs: lazy archive ${state.name} could not be fetched`, { cause: error });
|
|
2020
|
+
}
|
|
2021
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2022
|
+
for (const entry of parseTar(archive)) {
|
|
2023
|
+
if (entry.directory) continue;
|
|
2024
|
+
const relativeName = entry.name.startsWith("./") ? entry.name.slice(2) : entry.name;
|
|
2025
|
+
const target = this.key(join$1(state.root, relativeName));
|
|
2026
|
+
const size = state.files.get(target);
|
|
2027
|
+
if (size === void 0) throw new Error(`webworker vfs: lazy archive ${state.name} carries unregistered entry "${relativeName}"`);
|
|
2028
|
+
if (entry.bytes.length !== size) throw new Error(`webworker vfs: lazy archive ${state.name} holds ${String(entry.bytes.length)} bytes for ${target}, registered as ${String(size)}`);
|
|
2029
|
+
entries.set(target, entry.bytes);
|
|
2030
|
+
}
|
|
2031
|
+
for (const target of state.files.keys()) if (!entries.has(target)) throw new Error(`webworker vfs: lazy archive ${state.name} lacks registered file ${target}`);
|
|
2032
|
+
for (const [target, bytes] of entries) {
|
|
2033
|
+
const file = this.deferred.get(target);
|
|
2034
|
+
if (file?.archive !== state) continue;
|
|
2035
|
+
this.deferred.delete(target);
|
|
2036
|
+
const identity = this.identities.get(target);
|
|
2037
|
+
this.identities.delete(target);
|
|
2038
|
+
this.setFile(target, {
|
|
2039
|
+
bytes,
|
|
2040
|
+
mtimeMs: file.mtimeMs,
|
|
2041
|
+
mode: file.mode,
|
|
2042
|
+
paths: void 0,
|
|
2043
|
+
...identity === void 0 ? {} : { identity }
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
} catch (error) {
|
|
2047
|
+
state.fetching = void 0;
|
|
2048
|
+
throw error;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
/** Refuse a synchronous operation on a registered file whose bytes have not arrived. */
|
|
2052
|
+
requireArrived(syscall, target) {
|
|
2053
|
+
const file = this.deferred.get(target);
|
|
2054
|
+
if (file === void 0) return;
|
|
2055
|
+
const error = /* @__PURE__ */ new Error(`ERR_DSH_VFS_DEFERRED: ${syscall} cannot wait for '${target}', which lazy archive ${file.archive.name} supplies on its first asynchronous read`);
|
|
2056
|
+
error.code = "ERR_DSH_VFS_DEFERRED";
|
|
2057
|
+
error.path = target;
|
|
2058
|
+
error.syscall = syscall;
|
|
2059
|
+
throw error;
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
1746
2062
|
* Read a file.
|
|
1747
2063
|
* @param path - File path.
|
|
1748
2064
|
* @param options - `'utf8'` or `{encoding}` for text; omitted for bytes.
|
|
@@ -1750,6 +2066,7 @@ var MemoryVfs = class {
|
|
|
1750
2066
|
*/
|
|
1751
2067
|
readFileSync(path, options) {
|
|
1752
2068
|
const target = this.key(path);
|
|
2069
|
+
this.requireArrived("open", target);
|
|
1753
2070
|
const node = this.files.get(target);
|
|
1754
2071
|
if (node === void 0) {
|
|
1755
2072
|
if (this.directories.has(target)) fail("EISDIR", "read", target);
|
|
@@ -1764,7 +2081,7 @@ var MemoryVfs = class {
|
|
|
1764
2081
|
*/
|
|
1765
2082
|
existsSync(path) {
|
|
1766
2083
|
const target = this.key(path);
|
|
1767
|
-
return this.files.has(target) || this.directories.has(target);
|
|
2084
|
+
return this.files.has(target) || this.directories.has(target) || this.deferred.has(target);
|
|
1768
2085
|
}
|
|
1769
2086
|
/**
|
|
1770
2087
|
* Stat a path.
|
|
@@ -1775,11 +2092,17 @@ var MemoryVfs = class {
|
|
|
1775
2092
|
statSync(path, options) {
|
|
1776
2093
|
const target = this.key(path);
|
|
1777
2094
|
const node = this.files.get(target);
|
|
2095
|
+
const deferred = this.deferred.get(target);
|
|
1778
2096
|
const [size, mtimeMs, directory, mode] = node !== void 0 ? [
|
|
1779
2097
|
node.bytes.length,
|
|
1780
2098
|
node.mtimeMs,
|
|
1781
2099
|
false,
|
|
1782
2100
|
node.mode
|
|
2101
|
+
] : deferred !== void 0 ? [
|
|
2102
|
+
deferred.size,
|
|
2103
|
+
deferred.mtimeMs,
|
|
2104
|
+
false,
|
|
2105
|
+
deferred.mode
|
|
1783
2106
|
] : this.directories.has(target) ? [
|
|
1784
2107
|
0,
|
|
1785
2108
|
this.directoryMtimes.get(target) ?? 0,
|
|
@@ -1949,12 +2272,16 @@ var MemoryVfs = class {
|
|
|
1949
2272
|
readdirSync(path, options) {
|
|
1950
2273
|
const target = this.key(path);
|
|
1951
2274
|
if (!this.directories.has(target)) {
|
|
1952
|
-
if (this.files.has(target)) fail("ENOTDIR", "scandir", target);
|
|
2275
|
+
if (this.files.has(target) || this.deferred.has(target)) fail("ENOTDIR", "scandir", target);
|
|
1953
2276
|
fail("ENOENT", "scandir", target);
|
|
1954
2277
|
}
|
|
1955
2278
|
const prefix = target === "/" ? "/" : `${target}/`;
|
|
1956
2279
|
const names = /* @__PURE__ */ new Set();
|
|
1957
|
-
for (const candidate of [
|
|
2280
|
+
for (const candidate of [
|
|
2281
|
+
...this.files.keys(),
|
|
2282
|
+
...this.deferred.keys(),
|
|
2283
|
+
...this.directories
|
|
2284
|
+
]) {
|
|
1958
2285
|
if (!candidate.startsWith(prefix) || candidate === target) continue;
|
|
1959
2286
|
const rest = candidate.slice(prefix.length);
|
|
1960
2287
|
if (rest === "") continue;
|
|
@@ -1994,7 +2321,7 @@ var MemoryVfs = class {
|
|
|
1994
2321
|
*/
|
|
1995
2322
|
mkdirSync(path, options) {
|
|
1996
2323
|
const target = this.key(path);
|
|
1997
|
-
if (this.files.has(target)) fail("EEXIST", "mkdir", target);
|
|
2324
|
+
if (this.files.has(target) || this.deferred.has(target)) fail("EEXIST", "mkdir", target);
|
|
1998
2325
|
if (this.directories.has(target)) {
|
|
1999
2326
|
if (options?.recursive === true) return void 0;
|
|
2000
2327
|
fail("EEXIST", "mkdir", target);
|
|
@@ -2024,6 +2351,7 @@ var MemoryVfs = class {
|
|
|
2024
2351
|
*/
|
|
2025
2352
|
writeFileSync(path, data, options) {
|
|
2026
2353
|
const target = this.key(path);
|
|
2354
|
+
this.requireArrived("open", target);
|
|
2027
2355
|
if (this.directories.has(target)) fail("EISDIR", "open", target);
|
|
2028
2356
|
if (!this.directories.has(dirname$1(target))) fail("ENOENT", "open", target);
|
|
2029
2357
|
const flag = options?.flag ?? "w";
|
|
@@ -2154,6 +2482,7 @@ var MemoryVfs = class {
|
|
|
2154
2482
|
*/
|
|
2155
2483
|
openFileSync(path, flags = "r", mode) {
|
|
2156
2484
|
const target = this.key(path);
|
|
2485
|
+
this.requireArrived("open", target);
|
|
2157
2486
|
const access = openMode(flags);
|
|
2158
2487
|
const existing = this.files.get(target);
|
|
2159
2488
|
if (this.directories.has(target)) fail("EISDIR", "open", target);
|
|
@@ -2215,6 +2544,7 @@ var MemoryVfs = class {
|
|
|
2215
2544
|
*/
|
|
2216
2545
|
appendFileSync(path, data) {
|
|
2217
2546
|
const target = this.key(path);
|
|
2547
|
+
this.requireArrived("open", target);
|
|
2218
2548
|
const existing = this.files.get(target);
|
|
2219
2549
|
const addition = typeof data === "string" ? encoder$1.encode(data) : data;
|
|
2220
2550
|
if (existing === void 0) {
|
|
@@ -2231,6 +2561,8 @@ var MemoryVfs = class {
|
|
|
2231
2561
|
renameSync(from, to) {
|
|
2232
2562
|
const source = this.key(from);
|
|
2233
2563
|
const destination = this.key(to);
|
|
2564
|
+
this.requireArrived("rename", source);
|
|
2565
|
+
this.requireArrived("rename", destination);
|
|
2234
2566
|
if (source === destination) return;
|
|
2235
2567
|
const node = this.files.get(source);
|
|
2236
2568
|
if (node !== void 0) {
|
|
@@ -2259,13 +2591,14 @@ var MemoryVfs = class {
|
|
|
2259
2591
|
if (!this.directories.has(source)) fail("ENOENT", "rename", source);
|
|
2260
2592
|
if (this.files.has(destination)) fail("ENOTDIR", "rename", destination);
|
|
2261
2593
|
if (!this.directories.has(dirname$1(destination))) fail("ENOENT", "rename", destination);
|
|
2594
|
+
const prefix = `${source}/`;
|
|
2595
|
+
for (const registered of this.deferred.keys()) if (registered.startsWith(prefix)) this.requireArrived("rename", registered);
|
|
2262
2596
|
if (this.directories.has(destination)) {
|
|
2263
2597
|
if (this.readdirSync(destination).length > 0) fail("ENOTEMPTY", "rename", destination);
|
|
2264
2598
|
this.directories.delete(destination);
|
|
2265
2599
|
this.directoryModes.delete(destination);
|
|
2266
2600
|
this.directoryMtimes.delete(destination);
|
|
2267
2601
|
}
|
|
2268
|
-
const prefix = `${source}/`;
|
|
2269
2602
|
const movedFiles = [];
|
|
2270
2603
|
for (const [candidate, value] of [...this.files]) {
|
|
2271
2604
|
if (!candidate.startsWith(prefix)) continue;
|
|
@@ -2327,9 +2660,10 @@ var MemoryVfs = class {
|
|
|
2327
2660
|
linkSync(existing, next) {
|
|
2328
2661
|
const source = this.key(existing);
|
|
2329
2662
|
const target = this.key(next);
|
|
2663
|
+
this.requireArrived("link", source);
|
|
2330
2664
|
const node = this.files.get(source);
|
|
2331
2665
|
if (node === void 0) fail("ENOENT", "link", source);
|
|
2332
|
-
if (this.
|
|
2666
|
+
if (this.existsSync(target)) fail("EEXIST", "link", target);
|
|
2333
2667
|
if (!this.directories.has(dirname$1(target))) fail("ENOENT", "link", target);
|
|
2334
2668
|
this.setFile(target, node);
|
|
2335
2669
|
this.touchDirectory(dirname$1(target));
|
|
@@ -2348,6 +2682,7 @@ var MemoryVfs = class {
|
|
|
2348
2682
|
*/
|
|
2349
2683
|
truncateSync(path, length = 0) {
|
|
2350
2684
|
const target = this.key(path);
|
|
2685
|
+
this.requireArrived("truncate", target);
|
|
2351
2686
|
const node = this.files.get(target);
|
|
2352
2687
|
if (node === void 0) fail("ENOENT", "truncate", target);
|
|
2353
2688
|
this.truncateFile(node, length);
|
|
@@ -2359,6 +2694,7 @@ var MemoryVfs = class {
|
|
|
2359
2694
|
*/
|
|
2360
2695
|
chmodSync(path, mode) {
|
|
2361
2696
|
const target = this.key(path);
|
|
2697
|
+
this.requireArrived("chmod", target);
|
|
2362
2698
|
const node = this.files.get(target);
|
|
2363
2699
|
if (node !== void 0) {
|
|
2364
2700
|
this.chmodFile(node, mode);
|
|
@@ -2382,7 +2718,7 @@ var MemoryVfs = class {
|
|
|
2382
2718
|
*/
|
|
2383
2719
|
unlinkSync(path) {
|
|
2384
2720
|
const target = this.key(path);
|
|
2385
|
-
if (this.deleteFile(target) === void 0) fail("ENOENT", "unlink", target);
|
|
2721
|
+
if (this.deleteFile(target) === void 0 && !this.deferred.delete(target)) fail("ENOENT", "unlink", target);
|
|
2386
2722
|
this.forgetIdentity(target);
|
|
2387
2723
|
this.touchDirectory(dirname$1(target));
|
|
2388
2724
|
this.publish({
|
|
@@ -2397,7 +2733,7 @@ var MemoryVfs = class {
|
|
|
2397
2733
|
*/
|
|
2398
2734
|
rmSync(path, options) {
|
|
2399
2735
|
const target = this.key(path);
|
|
2400
|
-
if (this.deleteFile(target) !== void 0) {
|
|
2736
|
+
if (this.deleteFile(target) !== void 0 || this.deferred.delete(target)) {
|
|
2401
2737
|
this.forgetIdentity(target);
|
|
2402
2738
|
this.touchDirectory(dirname$1(target));
|
|
2403
2739
|
this.publish({
|
|
@@ -2410,6 +2746,7 @@ var MemoryVfs = class {
|
|
|
2410
2746
|
if (options?.recursive !== true) fail("ERR_FS_EISDIR", "rm", target);
|
|
2411
2747
|
const prefix = `${target}/`;
|
|
2412
2748
|
for (const candidate of [...this.files.keys()]) if (candidate.startsWith(prefix)) this.deleteFile(candidate);
|
|
2749
|
+
for (const candidate of [...this.deferred.keys()]) if (candidate.startsWith(prefix)) this.deferred.delete(candidate);
|
|
2413
2750
|
for (const candidate of [...this.directories]) {
|
|
2414
2751
|
if (!candidate.startsWith(prefix)) continue;
|
|
2415
2752
|
this.directories.delete(candidate);
|
|
@@ -2449,6 +2786,7 @@ var MemoryVfs = class {
|
|
|
2449
2786
|
seed(path, data, options = {}) {
|
|
2450
2787
|
const target = this.key(path);
|
|
2451
2788
|
this.seedDirectory(dirname$1(target));
|
|
2789
|
+
this.deferred.delete(target);
|
|
2452
2790
|
this.setFile(target, {
|
|
2453
2791
|
bytes: typeof data === "string" ? encoder$1.encode(data) : data,
|
|
2454
2792
|
mtimeMs: options.mtimeMs ?? this.touch(target),
|
|
@@ -2467,7 +2805,7 @@ var MemoryVfs = class {
|
|
|
2467
2805
|
if (!this.directories.has(target)) {
|
|
2468
2806
|
const parent = dirname$1(target);
|
|
2469
2807
|
if (parent !== target) this.seedDirectory(parent);
|
|
2470
|
-
if (this.files.has(target)) fail("EEXIST", "mkdir", target);
|
|
2808
|
+
if (this.files.has(target) || this.deferred.has(target)) fail("EEXIST", "mkdir", target);
|
|
2471
2809
|
this.directories.add(target);
|
|
2472
2810
|
this.directoryMtimes.set(target, options.mtimeMs ?? Date.now());
|
|
2473
2811
|
this.touchDirectory(parent);
|
|
@@ -2492,7 +2830,7 @@ var MemoryVfs = class {
|
|
|
2492
2830
|
/**
|
|
2493
2831
|
* Mount a tar image produced by the build-time collector.
|
|
2494
2832
|
*
|
|
2495
|
-
* Entry names are relative to `root` (`node_modules/...`, `config/
|
|
2833
|
+
* Entry names are relative to `root` (`node_modules/...`, `config/vfs-manifest.json`);
|
|
2496
2834
|
* an absolute entry name is a collector defect and fails loud. File contents
|
|
2497
2835
|
* stay views into `image` — nothing is copied at mount time.
|
|
2498
2836
|
* @param image - The ustar archive, as `inflateImage` produces it from the fetched image.
|
|
@@ -2604,6 +2942,72 @@ const XDG_OPEN_EXECUTABLE = {
|
|
|
2604
2942
|
return { kind: "asynchronous" };
|
|
2605
2943
|
}
|
|
2606
2944
|
};
|
|
2945
|
+
//#endregion
|
|
2946
|
+
//#region src/image-manifest.ts
|
|
2947
|
+
/**
|
|
2948
|
+
* The base image's manifest: what the packer records about one image and what
|
|
2949
|
+
* the worker host requires before it wraps a module or boots the tree. Both
|
|
2950
|
+
* sides use these types; the worker validates the fetched file with
|
|
2951
|
+
* {@link parseImageManifest}.
|
|
2952
|
+
*/
|
|
2953
|
+
function recordOf(value) {
|
|
2954
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
2955
|
+
}
|
|
2956
|
+
/** A relative path made of non-empty segments without traversal. */
|
|
2957
|
+
function isRelativePath(value) {
|
|
2958
|
+
return typeof value === "string" && value !== "" && !value.startsWith("/") && value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
|
|
2959
|
+
}
|
|
2960
|
+
function isCount(value) {
|
|
2961
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
2962
|
+
}
|
|
2963
|
+
/**
|
|
2964
|
+
* Validate a fetched base-image manifest.
|
|
2965
|
+
* @param value - Parsed manifest JSON.
|
|
2966
|
+
* @param path - Manifest path inside the image, named in refusals.
|
|
2967
|
+
* @returns The detached manifest.
|
|
2968
|
+
* @throws When the manifest is malformed, or its bodies were lowered against another wrapper contract.
|
|
2969
|
+
*/
|
|
2970
|
+
function parseImageManifest(value, path) {
|
|
2971
|
+
const record = recordOf(value);
|
|
2972
|
+
if (record === void 0) throw new Error(`webworker host: ${path} does not hold an object`);
|
|
2973
|
+
if (record.lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(record.lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
|
|
2974
|
+
const { root, profile, installation, javascriptEntries, visitedEntries, rewrittenEntries, lazy } = record;
|
|
2975
|
+
if (typeof root !== "string" || typeof profile !== "string" || profile === "" || installation !== void 0 && (typeof installation !== "string" || installation === "") || !isCount(javascriptEntries) || !isCount(visitedEntries) || !isCount(rewrittenEntries) || !Array.isArray(lazy)) throw new Error(`webworker host: ${path} lacks a valid root, profile, installation, entry count, or lazy archive list`);
|
|
2976
|
+
const archives = [];
|
|
2977
|
+
const archiveNames = /* @__PURE__ */ new Set();
|
|
2978
|
+
const filePaths = /* @__PURE__ */ new Set();
|
|
2979
|
+
for (const entry of lazy) {
|
|
2980
|
+
const archive = recordOf(entry);
|
|
2981
|
+
const name = archive?.archive;
|
|
2982
|
+
const files = archive?.files;
|
|
2983
|
+
if (!isRelativePath(name) || archiveNames.has(name) || !Array.isArray(files) || files.length === 0) throw new Error(`webworker host: ${path} lists an invalid or repeated lazy archive`);
|
|
2984
|
+
archiveNames.add(name);
|
|
2985
|
+
const parsed = [];
|
|
2986
|
+
for (const candidate of files) {
|
|
2987
|
+
const file = recordOf(candidate);
|
|
2988
|
+
if (file === void 0 || !isRelativePath(file.path) || !isCount(file.size) || filePaths.has(file.path)) throw new Error(`webworker host: ${path} lists an invalid or repeated file in lazy archive ${name}`);
|
|
2989
|
+
filePaths.add(file.path);
|
|
2990
|
+
parsed.push({
|
|
2991
|
+
path: file.path,
|
|
2992
|
+
size: file.size
|
|
2993
|
+
});
|
|
2994
|
+
}
|
|
2995
|
+
archives.push({
|
|
2996
|
+
archive: name,
|
|
2997
|
+
files: parsed
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
return {
|
|
3001
|
+
root,
|
|
3002
|
+
profile,
|
|
3003
|
+
...installation === void 0 ? {} : { installation },
|
|
3004
|
+
lowered: LOWERING_VERSION,
|
|
3005
|
+
javascriptEntries,
|
|
3006
|
+
visitedEntries,
|
|
3007
|
+
rewrittenEntries,
|
|
3008
|
+
lazy: archives
|
|
3009
|
+
};
|
|
3010
|
+
}
|
|
2607
3011
|
function requireGlobalPort(channel) {
|
|
2608
3012
|
if (channel !== void 0) return channel;
|
|
2609
3013
|
const post = globalThis.postMessage;
|
|
@@ -2626,7 +3030,6 @@ async function readImage(image) {
|
|
|
2626
3030
|
*/
|
|
2627
3031
|
function createWorkerHost(options) {
|
|
2628
3032
|
const root = options.root ?? "/dsh";
|
|
2629
|
-
const configPath = options.configPath ?? join$1(root, "config/cordis.yml");
|
|
2630
3033
|
const port = options.port ?? 3080;
|
|
2631
3034
|
const tunnel = new TunnelServer({
|
|
2632
3035
|
port: requireGlobalPort(options.channel),
|
|
@@ -2643,7 +3046,7 @@ function createWorkerHost(options) {
|
|
|
2643
3046
|
const start = async () => {
|
|
2644
3047
|
try {
|
|
2645
3048
|
const home = join$1(root, IMAGE_HOME_DIRECTORY);
|
|
2646
|
-
installProcessGlobal({
|
|
3049
|
+
const processShim = installProcessGlobal({
|
|
2647
3050
|
cwd: root,
|
|
2648
3051
|
env: {
|
|
2649
3052
|
DSH_HOME: home,
|
|
@@ -2657,7 +3060,8 @@ function createWorkerHost(options) {
|
|
|
2657
3060
|
for (const directory of IMAGE_EMPTY_DIRECTORIES) mounted.seedDirectory(join$1(root, directory.replace(/\/$/, "")));
|
|
2658
3061
|
setActiveVfs(mounted);
|
|
2659
3062
|
vfs = mounted;
|
|
2660
|
-
|
|
3063
|
+
const manifest = readImageManifest(mounted, options.manifestPath ?? join$1(root, "config/vfs-manifest.json"));
|
|
3064
|
+
deferLazyArchives(mounted, manifest, root, options.image);
|
|
2661
3065
|
const staticModules = { ...options.staticModules };
|
|
2662
3066
|
for (const key of ["node:process", "process"]) staticModules[key] ??= () => globalThis.process;
|
|
2663
3067
|
const loader = new WorkerModuleLoader({
|
|
@@ -2669,16 +3073,18 @@ function createWorkerHost(options) {
|
|
|
2669
3073
|
});
|
|
2670
3074
|
setActiveModuleLoader(loader);
|
|
2671
3075
|
modules = loader;
|
|
2672
|
-
const require = loader.requireFrom(
|
|
3076
|
+
const require = loader.requireFrom(root);
|
|
2673
3077
|
const appBoot = require("@deepseek-ai/dsh-app-boot");
|
|
3078
|
+
const { DSH_LAUNCH_ENVIRONMENT_KEY } = require("@deepseek-ai/dsh-launch-environment");
|
|
2674
3079
|
const cmdline = require("@deepseek-ai/dsh-cmdline");
|
|
2675
3080
|
const started = Promise.withResolvers();
|
|
2676
|
-
const { patches, profile } =
|
|
3081
|
+
const { patches, profile, environment } = prepareProfile(appBoot, mounted, manifest, root, processShim.env);
|
|
2677
3082
|
const profileConfig = join$1(profile.dir, "cordis.yml");
|
|
2678
|
-
const ctx = await appBoot.boot(
|
|
3083
|
+
const ctx = await appBoot.boot(BIN_NAME, profileConfig, patches, async (hostCtx) => {
|
|
2679
3084
|
hostCtx.provide("profileContext", profile);
|
|
2680
|
-
hostCtx.
|
|
3085
|
+
hostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, environment);
|
|
2681
3086
|
installLogSink(hostCtx, require);
|
|
3087
|
+
await hostCtx.plugin(appBoot.PluginPackages, {});
|
|
2682
3088
|
cmdline.provideCmdline(hostCtx, {
|
|
2683
3089
|
args: [...options.cmdlineArgs ?? [
|
|
2684
3090
|
"--host",
|
|
@@ -2708,11 +3114,12 @@ function createWorkerHost(options) {
|
|
|
2708
3114
|
if (typertGateway === void 0) throw new Error("webworker host: the tree activated without a typertGateway service");
|
|
2709
3115
|
const handler = connection.createSharedFetchHandler("/api");
|
|
2710
3116
|
const usage = loader.usage();
|
|
2711
|
-
console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
|
|
3117
|
+
console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, lazy archives=${String(manifest.lazy.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
|
|
3118
|
+
const peer = connection.browserPeer;
|
|
2712
3119
|
tunnel.serve({
|
|
2713
|
-
directFetch: (request) => handler.fetch(request),
|
|
3120
|
+
directFetch: (request) => handler.fetch(request, peer),
|
|
2714
3121
|
bootPayload: () => readBootPayload(ctx),
|
|
2715
|
-
openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink,
|
|
3122
|
+
openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, peer, signal),
|
|
2716
3123
|
streamFailure: typertGateway.wireStream.failure
|
|
2717
3124
|
});
|
|
2718
3125
|
started.resolve();
|
|
@@ -2767,76 +3174,84 @@ function installLogSink(ctx, require) {
|
|
|
2767
3174
|
};
|
|
2768
3175
|
ctx.logger.exporter(exporter);
|
|
2769
3176
|
}
|
|
3177
|
+
/** Diagnostic prefix the launcher passes to app-boot; the Worker boots the same profile code. */
|
|
3178
|
+
const BIN_NAME = "dsh";
|
|
2770
3179
|
/**
|
|
2771
|
-
*
|
|
3180
|
+
* Read and validate the mounted image's manifest.
|
|
2772
3181
|
*
|
|
2773
3182
|
* The manifest the packer writes is the single source of truth: the worker holds
|
|
2774
3183
|
* no transform, so an image that was never lowered — or was lowered against
|
|
2775
3184
|
* different wrapper semantics — cannot be recovered at load and must be rebuilt.
|
|
2776
3185
|
* @param vfs - Mounted filesystem.
|
|
2777
3186
|
* @param path - Manifest path inside the image.
|
|
2778
|
-
* @
|
|
3187
|
+
* @returns The validated manifest.
|
|
3188
|
+
* @throws When the manifest is missing, malformed, or names another contract.
|
|
2779
3189
|
*/
|
|
2780
|
-
function
|
|
3190
|
+
function readImageManifest(vfs, path) {
|
|
2781
3191
|
if (!vfs.existsSync(path)) throw new Error(`webworker host: ${path} is missing, so the image records no lowering; rebuild the image`);
|
|
2782
|
-
|
|
2783
|
-
if (typeof parsed !== "object" || parsed === null) throw new Error(`webworker host: ${path} does not hold an object`);
|
|
2784
|
-
const lowered = parsed.lowered;
|
|
2785
|
-
if (lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
|
|
3192
|
+
return parseImageManifest(JSON.parse(vfs.readFileSync(path, "utf8")), path);
|
|
2786
3193
|
}
|
|
2787
3194
|
/**
|
|
2788
|
-
*
|
|
2789
|
-
*
|
|
2790
|
-
*
|
|
2791
|
-
* @param
|
|
2792
|
-
* @param
|
|
2793
|
-
* @param
|
|
2794
|
-
* @
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
const
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
3195
|
+
* Register the manifest's lazy archives; each is fetched beside the image on
|
|
3196
|
+
* the first asynchronous read of a file it carries.
|
|
3197
|
+
* @param vfs - Mounted filesystem.
|
|
3198
|
+
* @param manifest - Validated image manifest.
|
|
3199
|
+
* @param root - Virtual root the archives' entries mount under.
|
|
3200
|
+
* @param image - The image bytes or URL; archive names resolve against a URL.
|
|
3201
|
+
* @throws When the image registers lazy archives but arrived as bytes, leaving them unlocatable.
|
|
3202
|
+
*/
|
|
3203
|
+
function deferLazyArchives(vfs, manifest, root, image) {
|
|
3204
|
+
if (manifest.lazy.length === 0) return;
|
|
3205
|
+
if (typeof image !== "string") throw new Error("webworker host: the image registers lazy archives, which only an image fetched from a URL can locate");
|
|
3206
|
+
const base = new URL(image, globalThis.location?.href);
|
|
3207
|
+
for (const archive of manifest.lazy) {
|
|
3208
|
+
const url = new URL(archive.archive, base).href;
|
|
3209
|
+
vfs.deferArchive({
|
|
3210
|
+
name: archive.archive,
|
|
3211
|
+
root,
|
|
3212
|
+
files: archive.files,
|
|
3213
|
+
load: () => readImage(url)
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
/**
|
|
3218
|
+
* Prepare the profile the served launcher would boot: the image's app-boot
|
|
3219
|
+
* initializes the manifest's shipped profile template under the Worker home
|
|
3220
|
+
* and composes its bundle layers, user layer, and home layer. The profile
|
|
3221
|
+
* context carries the launcher's facts with no command-line overlay; the
|
|
3222
|
+
* image's home patch, written by the packer, carries the Worker's required
|
|
3223
|
+
* row config (`PLATFORM_ENTRY_CONFIG` in `image-layout.ts`).
|
|
3224
|
+
* @param appBoot - The image's app-boot.
|
|
3225
|
+
* @param vfs - Filesystem holding the profile.
|
|
3226
|
+
* @param manifest - Validated image manifest naming the profile and installation.
|
|
3227
|
+
* @param root - Virtual root, the process working directory.
|
|
3228
|
+
* @param env - The process shim's environment, after the launch layers applied.
|
|
3229
|
+
* @returns Effective boot patches, the profile context, and the launch environment snapshot.
|
|
3230
|
+
* @throws When the image names no installation manifest to anchor bundle resolution.
|
|
3231
|
+
*/
|
|
3232
|
+
function prepareProfile(appBoot, vfs, manifest, root, env) {
|
|
3233
|
+
if (manifest.installation === void 0) throw new Error("webworker host: the image names no installation manifest, so no profile bundle can resolve; rebuild the image");
|
|
3234
|
+
const home = join$1(root, IMAGE_HOME_DIRECTORY);
|
|
3235
|
+
const installAnchor = join$1(root, "node_modules", manifest.installation, "package.json");
|
|
3236
|
+
const environment = appBoot.loadLayeredEnv(BIN_NAME, root);
|
|
3237
|
+
const loaded = appBoot.loadProfile(BIN_NAME, manifest.profile, installAnchor, home);
|
|
3238
|
+
appBoot.reportSkippedBundles(BIN_NAME, loaded);
|
|
3239
|
+
vfs.writeFileSync(join$1(loaded.dir, "cordis.yml"), "[]\n");
|
|
2823
3240
|
const profile = {
|
|
2824
|
-
name:
|
|
2825
|
-
dir,
|
|
2826
|
-
patchPath:
|
|
2827
|
-
installAnchor
|
|
3241
|
+
name: loaded.name,
|
|
3242
|
+
dir: loaded.dir,
|
|
3243
|
+
patchPath: loaded.patchPath,
|
|
3244
|
+
installAnchor,
|
|
2828
3245
|
cwd: root,
|
|
2829
|
-
home
|
|
2830
|
-
startedBundles:
|
|
2831
|
-
overlays:
|
|
2832
|
-
telemetryDisabledEnv:
|
|
2833
|
-
};
|
|
2834
|
-
vfs.seed(join$1(dir, "package.json"), "{\"private\":true,\"dsh\":{\"profile\":{\"bundles\":[]}}}\n");
|
|
2835
|
-
vfs.seed(join$1(dir, "cordis.yml"), "[]\n");
|
|
2836
|
-
if (!vfs.existsSync(profile.patchPath)) vfs.seed(profile.patchPath, yaml.dump([{ insert: rows }], { schema: include.entryListSchema }));
|
|
3246
|
+
home,
|
|
3247
|
+
startedBundles: loaded.layers.map((layer) => layer.packageName),
|
|
3248
|
+
overlays: [],
|
|
3249
|
+
telemetryDisabledEnv: env.DSH_TELEMETRY_DISABLED
|
|
3250
|
+
};
|
|
2837
3251
|
return {
|
|
2838
|
-
patches:
|
|
2839
|
-
profile
|
|
3252
|
+
patches: appBoot.readProfilePatches(BIN_NAME, profile, loaded),
|
|
3253
|
+
profile,
|
|
3254
|
+
environment
|
|
2840
3255
|
};
|
|
2841
3256
|
}
|
|
2842
3257
|
/**
|
|
@@ -4330,49 +4745,6 @@ var buffer_default = {
|
|
|
4330
4745
|
kMaxLength: import_buffer.kMaxLength
|
|
4331
4746
|
};
|
|
4332
4747
|
//#endregion
|
|
4333
|
-
//#region src/node/notImplementedFail.ts
|
|
4334
|
-
/**
|
|
4335
|
-
* Structural not-implemented stubs: a replaced module must expose every symbol
|
|
4336
|
-
* its importers name (a missing CommonJS symbol degrades to `undefined` at call
|
|
4337
|
-
* time instead of failing at link time), and every one of those symbols must
|
|
4338
|
-
* report exactly what is unavailable when it is finally called.
|
|
4339
|
-
*/
|
|
4340
|
-
/**
|
|
4341
|
-
* Build a function that throws naming its module and symbol. The refusal is
|
|
4342
|
-
* also written to the console before it propagates: callers routinely swallow
|
|
4343
|
-
* these errors far from their cause, and the console line is what places the
|
|
4344
|
-
* failure while debugging a worker session.
|
|
4345
|
-
*
|
|
4346
|
-
* `Face` is the Node declaration this stub stands in for, so the replaced module
|
|
4347
|
-
* publishes the type its importers compile against. The value is one throwing
|
|
4348
|
-
* function whatever that declaration says: a caller reaches the throw before any
|
|
4349
|
-
* declared parameter, return value, or `new` result exists, so the assertion
|
|
4350
|
-
* below cannot be observed as a lie. It is a function expression rather than an
|
|
4351
|
-
* arrow because a stub standing in for a class must refuse under `new` too, and
|
|
4352
|
-
* an arrow has no construct behavior to reach.
|
|
4353
|
-
* @param module - module specifier being stubbed.
|
|
4354
|
-
* @param symbol - exported symbol name.
|
|
4355
|
-
* @returns the throwing stand-in, typed as the member it replaces.
|
|
4356
|
-
*/
|
|
4357
|
-
function notImplementedFail(module, symbol) {
|
|
4358
|
-
return (function refuse() {
|
|
4359
|
-
throw notAvailableError(module, symbol);
|
|
4360
|
-
});
|
|
4361
|
-
}
|
|
4362
|
-
/**
|
|
4363
|
-
* Build the refusal error and write it to the console first, for stubs that
|
|
4364
|
-
* cannot be a plain throwing function (constructors, methods on structural
|
|
4365
|
-
* fakes).
|
|
4366
|
-
* @param module - module specifier being stubbed.
|
|
4367
|
-
* @param symbol - unavailable member, named as the importer sees it.
|
|
4368
|
-
* @returns the error to throw.
|
|
4369
|
-
*/
|
|
4370
|
-
function notAvailableError(module, symbol) {
|
|
4371
|
-
const message = `web-preview: ${module}.${symbol} is not available in the worker host`;
|
|
4372
|
-
console.error(message);
|
|
4373
|
-
return new Error(message);
|
|
4374
|
-
}
|
|
4375
|
-
//#endregion
|
|
4376
4748
|
//#region src/node/builtin_modules/implemented/async_hooks.ts
|
|
4377
4749
|
/**
|
|
4378
4750
|
* `node:async_hooks` for the worker: `AsyncLocalStorage` over an EXPLICIT-SWITCH
|
|
@@ -11280,8 +11652,10 @@ const DSH_TMP = `${DSH_ROOT}/tmp`;
|
|
|
11280
11652
|
* Node). `../../../../tests/node/path-diff.spec.ts` pins the port below to
|
|
11281
11653
|
* Node's answers.
|
|
11282
11654
|
* A `node:` proxy has to answer what Node answers, since VFS paths were built with
|
|
11283
|
-
* Node semantics. `win32` members throw
|
|
11284
|
-
*
|
|
11655
|
+
* Node semantics. `win32` members throw, except `isAbsolute`, which
|
|
11656
|
+
* cross-platform callers use to reject Windows absolute paths on any platform:
|
|
11657
|
+
* the worker host reports `process.platform === 'linux'`, so any other Windows
|
|
11658
|
+
* branch means a bug.
|
|
11285
11659
|
*/
|
|
11286
11660
|
var path_exports = /* @__PURE__ */ __exportAll({
|
|
11287
11661
|
__esModule: () => true,
|
|
@@ -11613,11 +11987,31 @@ const posix = {
|
|
|
11613
11987
|
const win32Member = (name) => () => {
|
|
11614
11988
|
throw new Error(`web-preview: node:path.win32.${name} is unreachable — the worker host reports platform "linux"`);
|
|
11615
11989
|
};
|
|
11616
|
-
|
|
11990
|
+
const CHAR_BACKWARD_SLASH = 92;
|
|
11991
|
+
const CHAR_COLON = 58;
|
|
11992
|
+
/** Node's win32 separator test: `/` or `\`. */
|
|
11993
|
+
function isWin32Separator(code) {
|
|
11994
|
+
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
|
11995
|
+
}
|
|
11996
|
+
/** Node's drive-letter test: `A`–`Z` or `a`–`z`. */
|
|
11997
|
+
function isWindowsDeviceRoot(code) {
|
|
11998
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
11999
|
+
}
|
|
12000
|
+
/**
|
|
12001
|
+
* Node's `path.win32.isAbsolute`, a string check cross-platform callers make on every platform.
|
|
12002
|
+
* @param path - Path to classify.
|
|
12003
|
+
* @returns Whether a Windows host would treat the path as absolute.
|
|
12004
|
+
*/
|
|
12005
|
+
function win32IsAbsolute(path) {
|
|
12006
|
+
if (path.length === 0) return false;
|
|
12007
|
+
const code = path.charCodeAt(0);
|
|
12008
|
+
return isWin32Separator(code) || path.length > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isWin32Separator(path.charCodeAt(2));
|
|
12009
|
+
}
|
|
12010
|
+
/** Windows member set: reaching a member other than `isAbsolute` means a platform branch went the wrong way. */
|
|
11617
12011
|
const win32 = {
|
|
11618
12012
|
resolve: win32Member("resolve"),
|
|
11619
12013
|
normalize: win32Member("normalize"),
|
|
11620
|
-
isAbsolute:
|
|
12014
|
+
isAbsolute: win32IsAbsolute,
|
|
11621
12015
|
join: win32Member("join"),
|
|
11622
12016
|
relative: win32Member("relative"),
|
|
11623
12017
|
dirname: win32Member("dirname"),
|
|
@@ -11691,6 +12085,18 @@ const statOrMissing = (path, bigint) => {
|
|
|
11691
12085
|
const statsChanged = (left, right) => left.size !== right.size || left.mtimeMs !== right.mtimeMs || left.mode !== right.mode || left.ino !== right.ino || left.isFile() !== right.isFile() || left.isDirectory() !== right.isDirectory();
|
|
11692
12086
|
const contains$1 = (parent, child) => parent === "/" || child === parent || child.startsWith(`${parent}/`);
|
|
11693
12087
|
const overlaps = (left, right) => contains$1(left, right) || contains$1(right, left);
|
|
12088
|
+
/**
|
|
12089
|
+
* Run a watcher callback in the async context where its watcher was created, as Node delivers watch events from the
|
|
12090
|
+
* watch handle: stores captured at creation apply, and every other store reads `undefined` instead of the store of the
|
|
12091
|
+
* code whose write produced the event.
|
|
12092
|
+
* @param context - Context captured when the watcher was created.
|
|
12093
|
+
* @param callback - Event dispatch.
|
|
12094
|
+
*/
|
|
12095
|
+
const dispatchInWatcherContext = (context, callback) => {
|
|
12096
|
+
runAtAsyncContextRoot(() => {
|
|
12097
|
+
runWithAsyncContext(context, callback);
|
|
12098
|
+
});
|
|
12099
|
+
};
|
|
11694
12100
|
/** `fs.FSWatcher` over VFS mutations. */
|
|
11695
12101
|
var FSWatcher = class extends EventEmitter {
|
|
11696
12102
|
target;
|
|
@@ -11715,7 +12121,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
11715
12121
|
const filename = this.filename(mutation.path);
|
|
11716
12122
|
queueMicrotask(() => {
|
|
11717
12123
|
if (this.closed) return;
|
|
11718
|
-
|
|
12124
|
+
dispatchInWatcherContext(context, () => {
|
|
11719
12125
|
this.emit("change", eventType, filename);
|
|
11720
12126
|
});
|
|
11721
12127
|
});
|
|
@@ -11822,7 +12228,7 @@ var StatWatcher = class extends EventEmitter {
|
|
|
11822
12228
|
const current = statOrMissing(this.path, this.bigint);
|
|
11823
12229
|
const previous = this.previous;
|
|
11824
12230
|
this.previous = current;
|
|
11825
|
-
if (initialMissing || statsChanged(current, previous))
|
|
12231
|
+
if (initialMissing || statsChanged(current, previous)) dispatchInWatcherContext(this.context, () => {
|
|
11826
12232
|
this.emit("change", current, previous);
|
|
11827
12233
|
});
|
|
11828
12234
|
}, this.interval);
|
|
@@ -12467,6 +12873,21 @@ function closeFileStream(stream, callback) {
|
|
|
12467
12873
|
});
|
|
12468
12874
|
stream.destroy();
|
|
12469
12875
|
}
|
|
12876
|
+
/** Open a read stream's descriptor and announce it, completing `_construct`. */
|
|
12877
|
+
function openReadDescriptor(stream, path, flags, callback) {
|
|
12878
|
+
let fd;
|
|
12879
|
+
try {
|
|
12880
|
+
fd = openSync(path, flags);
|
|
12881
|
+
} catch (error) {
|
|
12882
|
+
callback(error);
|
|
12883
|
+
return;
|
|
12884
|
+
}
|
|
12885
|
+
stream.fd = fd;
|
|
12886
|
+
stream.pending = false;
|
|
12887
|
+
callback();
|
|
12888
|
+
stream.emit("open", fd);
|
|
12889
|
+
stream.emit("ready");
|
|
12890
|
+
}
|
|
12470
12891
|
/** Read stream over one VFS file. */
|
|
12471
12892
|
var ReadStream = class extends Readable {
|
|
12472
12893
|
/** Resolved path opened by this stream. */
|
|
@@ -12510,18 +12931,16 @@ var ReadStream = class extends Readable {
|
|
|
12510
12931
|
callback(abortError$1(this.signal.reason));
|
|
12511
12932
|
return;
|
|
12512
12933
|
}
|
|
12513
|
-
|
|
12514
|
-
|
|
12515
|
-
|
|
12516
|
-
} catch (error) {
|
|
12517
|
-
callback(error);
|
|
12934
|
+
const arrival = vfs().whenReadable(this.path);
|
|
12935
|
+
if (arrival === void 0) {
|
|
12936
|
+
openReadDescriptor(this, this.path, this.flags, callback);
|
|
12518
12937
|
return;
|
|
12519
12938
|
}
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12939
|
+
arrival.then(() => {
|
|
12940
|
+
openReadDescriptor(this, this.path, this.flags, callback);
|
|
12941
|
+
}, (error) => {
|
|
12942
|
+
callback(error);
|
|
12943
|
+
});
|
|
12525
12944
|
}
|
|
12526
12945
|
_read(size) {
|
|
12527
12946
|
if (this.fd === null) return;
|
|
@@ -12685,7 +13104,10 @@ function opendirSync(path) {
|
|
|
12685
13104
|
* check here is that every name is a real `node:fs/promises` export.
|
|
12686
13105
|
*/
|
|
12687
13106
|
const promises = {
|
|
12688
|
-
readFile: async (path, options) =>
|
|
13107
|
+
readFile: async (path, options) => {
|
|
13108
|
+
await vfs().whenReadable(asPath(path));
|
|
13109
|
+
return readFileSync(path, options);
|
|
13110
|
+
},
|
|
12689
13111
|
writeFile: async (path, data, options) => {
|
|
12690
13112
|
const flag = typeof options === "object" && options !== null ? options.flag : void 0;
|
|
12691
13113
|
const mode = typeof options === "object" && options !== null ? options.mode : void 0;
|
|
@@ -12733,12 +13155,16 @@ const promises = {
|
|
|
12733
13155
|
return;
|
|
12734
13156
|
}
|
|
12735
13157
|
mkdirSync(dirname(target), { recursive: true });
|
|
13158
|
+
await vfs().whenReadable(source);
|
|
12736
13159
|
writeFileSync(target, bytesOf(source));
|
|
12737
13160
|
},
|
|
12738
13161
|
link: async (from, to) => {
|
|
12739
13162
|
linkSync(from, to);
|
|
12740
13163
|
},
|
|
12741
|
-
open: async (path, flags, mode) =>
|
|
13164
|
+
open: async (path, flags, mode) => {
|
|
13165
|
+
await vfs().whenReadable(asPath(path));
|
|
13166
|
+
return openHandleSync(path, flags, mode);
|
|
13167
|
+
},
|
|
12742
13168
|
opendir: async (path) => opendirSync(path),
|
|
12743
13169
|
truncate: async (path, length = 0) => {
|
|
12744
13170
|
vfs().truncateSync(asPath(path), length);
|
|
@@ -13396,6 +13822,7 @@ var util_exports = /* @__PURE__ */ __exportAll({
|
|
|
13396
13822
|
default: () => util_default,
|
|
13397
13823
|
deprecate: () => deprecate,
|
|
13398
13824
|
format: () => format,
|
|
13825
|
+
inherits: () => inherits,
|
|
13399
13826
|
inspect: () => inspect,
|
|
13400
13827
|
isDeepStrictEqual: () => isDeepStrictEqual,
|
|
13401
13828
|
parseArgs: () => parseArgs,
|
|
@@ -13508,6 +13935,33 @@ function parseArgs() {
|
|
|
13508
13935
|
function deprecate(fn) {
|
|
13509
13936
|
return fn;
|
|
13510
13937
|
}
|
|
13938
|
+
/**
|
|
13939
|
+
* Node's `ERR_INVALID_ARG_TYPE` refusal for {@link inherits}.
|
|
13940
|
+
* @param name - Argument name.
|
|
13941
|
+
* @param expected - Expected type.
|
|
13942
|
+
* @param actual - Received value.
|
|
13943
|
+
* @returns The error to throw.
|
|
13944
|
+
*/
|
|
13945
|
+
function invalidArgType(name, expected, actual) {
|
|
13946
|
+
return Object.assign(/* @__PURE__ */ new TypeError(`The "${name}" argument must be of type ${expected}. Received ${String(actual)}`), { code: "ERR_INVALID_ARG_TYPE" });
|
|
13947
|
+
}
|
|
13948
|
+
/**
|
|
13949
|
+
* Node's `util.inherits`: link `ctor.prototype` to `superCtor.prototype` and record `ctor.super_`.
|
|
13950
|
+
* @param ctor - Constructor that inherits.
|
|
13951
|
+
* @param superCtor - Constructor inherited from.
|
|
13952
|
+
*/
|
|
13953
|
+
function inherits(ctor, superCtor) {
|
|
13954
|
+
if (ctor === void 0 || ctor === null) throw invalidArgType("ctor", "Function", ctor);
|
|
13955
|
+
if (superCtor === void 0 || superCtor === null) throw invalidArgType("superCtor", "Function", superCtor);
|
|
13956
|
+
const parent = superCtor.prototype;
|
|
13957
|
+
if (parent === void 0) throw invalidArgType("superCtor.prototype", "Object", parent);
|
|
13958
|
+
Object.defineProperty(ctor, "super_", {
|
|
13959
|
+
value: superCtor,
|
|
13960
|
+
writable: true,
|
|
13961
|
+
configurable: true
|
|
13962
|
+
});
|
|
13963
|
+
Object.setPrototypeOf(ctor.prototype, parent);
|
|
13964
|
+
}
|
|
13511
13965
|
/** Text decoder class, as `node:util` re-exports it. */
|
|
13512
13966
|
const TextDecoderClass = globalThis.TextDecoder;
|
|
13513
13967
|
/** Text encoder class, as `node:util` re-exports it. */
|
|
@@ -13522,6 +13976,7 @@ var util_default = {
|
|
|
13522
13976
|
types: types$1,
|
|
13523
13977
|
parseArgs,
|
|
13524
13978
|
deprecate,
|
|
13979
|
+
inherits,
|
|
13525
13980
|
TextDecoder: TextDecoderClass,
|
|
13526
13981
|
TextEncoder: TextEncoderClass
|
|
13527
13982
|
};
|
|
@@ -21707,6 +22162,76 @@ var got_exports = /* @__PURE__ */ __exportAll({
|
|
|
21707
22162
|
/** Reject product telemetry HTTP requests without opening a connection. */
|
|
21708
22163
|
const got = { post: notImplementedFail("got", "post") };
|
|
21709
22164
|
//#endregion
|
|
22165
|
+
//#region src/node/internal_modules/package-surfaces.ts
|
|
22166
|
+
/**
|
|
22167
|
+
* Node internal members HMR reads and replaces while expiring package configuration:
|
|
22168
|
+
* `internal/modules/cjs/loader`'s `Module` caches and loaders, `internal/modules/package_json_reader`, and the
|
|
22169
|
+
* `modules` binding from `internal/bootstrap/realm`. The Worker's own `require` does not route through them, so
|
|
22170
|
+
* they keep Node's member names for replacement and refuse when called.
|
|
22171
|
+
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/internal_modules/package-surfaces
|
|
22172
|
+
*/
|
|
22173
|
+
const CJS_LOADER = "internal/modules/cjs/loader";
|
|
22174
|
+
/** `Module` with the static members HMR reads and replaces. */
|
|
22175
|
+
const Module = Object.assign(function Module() {
|
|
22176
|
+
throw notAvailableError(CJS_LOADER, "Module");
|
|
22177
|
+
}, {
|
|
22178
|
+
/** Empty: the Worker loader keeps its own resolution cache. */
|
|
22179
|
+
_pathCache: Object.create(null),
|
|
22180
|
+
_resolveFilename: notImplementedFail(CJS_LOADER, "Module._resolveFilename"),
|
|
22181
|
+
_load: notImplementedFail(CJS_LOADER, "Module._load")
|
|
22182
|
+
});
|
|
22183
|
+
/** `internal/modules/package_json_reader` with the member HMR replaces. */
|
|
22184
|
+
const packageJsonReader = { getNearestParentPackageJSON: notImplementedFail("internal/modules/package_json_reader", "getNearestParentPackageJSON") };
|
|
22185
|
+
/** `internalBinding('modules')` with the members HMR replaces. */
|
|
22186
|
+
const modulesBinding = {
|
|
22187
|
+
readPackageJSON: notImplementedFail("internalBinding(\"modules\")", "readPackageJSON"),
|
|
22188
|
+
getPackageScopeConfig: notImplementedFail("internalBinding(\"modules\")", "getPackageScopeConfig"),
|
|
22189
|
+
getPackageType: notImplementedFail("internalBinding(\"modules\")", "getPackageType")
|
|
22190
|
+
};
|
|
22191
|
+
/** `internal/bootstrap/realm`: only the `modules` binding is provided. */
|
|
22192
|
+
const realm = {
|
|
22193
|
+
/**
|
|
22194
|
+
* Answer one internal binding.
|
|
22195
|
+
* @param name - Binding name.
|
|
22196
|
+
* @returns The `modules` binding.
|
|
22197
|
+
* @throws Error with `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` for any other binding.
|
|
22198
|
+
*/
|
|
22199
|
+
internalBinding(name) {
|
|
22200
|
+
if (name === "modules") return modulesBinding;
|
|
22201
|
+
throw notAvailableError("internal/bootstrap/realm", `internalBinding(${JSON.stringify(name)})`);
|
|
22202
|
+
} };
|
|
22203
|
+
//#endregion
|
|
22204
|
+
//#region src/node/external_packages/node-addon-require-builtin.ts
|
|
22205
|
+
/**
|
|
22206
|
+
* `node-addon-require-builtin` for the Worker. Without `--expose-internals`, the vendored Loader, app-boot, and HMR
|
|
22207
|
+
* read Node internal modules through this package's `requireBuiltin(id)`; the Worker answers the ids those callers
|
|
22208
|
+
* read with members that follow Node v24.18.0 and refuses every other id with Node's platform code.
|
|
22209
|
+
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/node/external_packages/node-addon-require-builtin
|
|
22210
|
+
*/
|
|
22211
|
+
var node_addon_require_builtin_exports = /* @__PURE__ */ __exportAll({ requireBuiltin: () => requireBuiltin });
|
|
22212
|
+
/** Mutable module objects: HMR replaces members on them and restores the originals. */
|
|
22213
|
+
const INTERNAL_MODULES = new Map([
|
|
22214
|
+
["internal/modules/esm/loader", { getOrInitializeCascadedLoader: () => requireActiveModuleLoader().cascadedLoader() }],
|
|
22215
|
+
["internal/modules/esm/module_map", {
|
|
22216
|
+
LoadCache,
|
|
22217
|
+
ResolveCache
|
|
22218
|
+
}],
|
|
22219
|
+
["internal/modules/cjs/loader", { Module }],
|
|
22220
|
+
["internal/modules/package_json_reader", packageJsonReader],
|
|
22221
|
+
["internal/bootstrap/realm", realm]
|
|
22222
|
+
]);
|
|
22223
|
+
/**
|
|
22224
|
+
* Answer one Node internal module.
|
|
22225
|
+
* @param moduleId - Node internal module id.
|
|
22226
|
+
* @returns The Worker's module object for that id.
|
|
22227
|
+
* @throws Error with `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` for an id the Worker does not provide.
|
|
22228
|
+
*/
|
|
22229
|
+
function requireBuiltin(moduleId) {
|
|
22230
|
+
const module = INTERNAL_MODULES.get(moduleId);
|
|
22231
|
+
if (module === void 0) throw notAvailableError("node-addon-require-builtin", `requireBuiltin(${JSON.stringify(moduleId)})`);
|
|
22232
|
+
return module;
|
|
22233
|
+
}
|
|
22234
|
+
//#endregion
|
|
21710
22235
|
//#region src/node/external_packages/pi-ai.ts
|
|
21711
22236
|
/**
|
|
21712
22237
|
* `@earendil-works/pi-ai` stub, including its `/providers/all` and `/api/*.lazy`
|
|
@@ -21748,15 +22273,14 @@ const isContextOverflow = notImplementedFail(MODULE$1, "isContextOverflow");
|
|
|
21748
22273
|
/** Collection of every builtin provider (unavailable). */
|
|
21749
22274
|
const builtinModels = notImplementedFail(MODULE$1, "builtinModels");
|
|
21750
22275
|
/**
|
|
21751
|
-
*
|
|
21752
|
-
*
|
|
21753
|
-
* added since.
|
|
22276
|
+
* Preview provider ids, in catalog order. This fixed subset must use valid
|
|
22277
|
+
* installed pi-ai identifiers so the adapter can attach authentication metadata.
|
|
21754
22278
|
*/
|
|
21755
22279
|
const BUILTIN_PROVIDER_IDS = [
|
|
21756
22280
|
"amazon-bedrock",
|
|
21757
22281
|
"ant-ling",
|
|
21758
22282
|
"anthropic",
|
|
21759
|
-
"azure
|
|
22283
|
+
"azure",
|
|
21760
22284
|
"baseten",
|
|
21761
22285
|
"cerebras",
|
|
21762
22286
|
"cloudflare-ai-gateway",
|
|
@@ -21945,6 +22469,7 @@ const REPLACED_EXTERNAL_PACKAGES = [
|
|
|
21945
22469
|
"node-pty",
|
|
21946
22470
|
"execa",
|
|
21947
22471
|
"got",
|
|
22472
|
+
"node-addon-require-builtin",
|
|
21948
22473
|
"sharp",
|
|
21949
22474
|
"ws"
|
|
21950
22475
|
];
|
|
@@ -22013,6 +22538,7 @@ const EXTERNALS = {
|
|
|
22013
22538
|
"node-pty": () => node_pty_exports,
|
|
22014
22539
|
"execa": () => execa_exports,
|
|
22015
22540
|
"got": () => got_exports,
|
|
22541
|
+
"node-addon-require-builtin": () => node_addon_require_builtin_exports,
|
|
22016
22542
|
"ws": () => ws_exports,
|
|
22017
22543
|
"@vscode/ripgrep": () => ripgrep_exports,
|
|
22018
22544
|
"@earendil-works/pi-ai": () => pi_ai_exports
|