@deepseek-ai/dsh-experimental-webworker-runtime 0.2.1-rc.5 → 0.2.2-alpha.3
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 +7 -4
- package/README.zh.md +7 -4
- package/lib/client.js +104 -9
- package/lib/index.js +338 -31
- package/lib/types/client/client.d.ts +5 -0
- package/lib/types/node/builtin_modules/implemented/async_hooks.d.ts +3 -3
- package/lib/types/node/builtin_modules/implemented/buffer.d.ts +7 -0
- package/lib/types/node/builtin_modules/implemented/string_decoder.d.ts +12 -0
- package/lib/types/node/builtin_modules/mock/repl.d.ts +10 -0
- package/lib/types/node/external_packages/pi-coding-agent.d.ts +64 -0
- package/lib/types/node/globals/process.d.ts +1 -1
- package/lib/types/package-assets.d.ts +17 -0
- package/lib/types/string-decoder-types.d.ts +5 -0
- package/lib/types/transport/tunnel.d.ts +5 -0
- package/lib/worker.js +363 -87
- package/package.json +28 -24
package/lib/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parse } from "acorn";
|
|
2
|
+
import { analyze } from "eslint-scope";
|
|
3
|
+
import { KEYS } from "eslint-visitor-keys";
|
|
2
4
|
//#region \0rolldown/runtime.js
|
|
3
5
|
var __defProp = Object.defineProperty;
|
|
4
6
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -878,9 +880,11 @@ const MODULE_PROXIES = {
|
|
|
878
880
|
"dns/promises": "./node/builtin_modules/mock/dns/promises.ts",
|
|
879
881
|
"node:net": "./node/builtin_modules/mock/net.ts",
|
|
880
882
|
"node:stream": "./node/builtin_modules/implemented/stream.ts",
|
|
883
|
+
"node:string_decoder": "./node/builtin_modules/implemented/string_decoder.ts",
|
|
881
884
|
"node:stream/promises": "./node/builtin_modules/implemented/stream/promises.ts",
|
|
882
885
|
"stream/promises": "./node/builtin_modules/implemented/stream/promises.ts",
|
|
883
886
|
"node:vm": "./node/builtin_modules/mock/vm.ts",
|
|
887
|
+
"node:repl": "./node/builtin_modules/mock/repl.ts",
|
|
884
888
|
"node:worker_threads": "./node/builtin_modules/mock/worker_threads.ts",
|
|
885
889
|
"node:sqlite": "./node/builtin_modules/mock/sqlite.ts",
|
|
886
890
|
"@deepseek-ai/libreoffice-kit": "./node/external_packages/libreoffice-kit.ts",
|
|
@@ -892,6 +896,7 @@ const MODULE_PROXIES = {
|
|
|
892
896
|
"got": "./node/external_packages/got.ts",
|
|
893
897
|
"@vscode/ripgrep": "./node/external_packages/ripgrep.ts",
|
|
894
898
|
"@earendil-works/pi-ai": "./node/external_packages/pi-ai.ts",
|
|
899
|
+
"@earendil-works/pi-coding-agent": "./node/external_packages/pi-coding-agent.ts",
|
|
895
900
|
"node-addon-require-builtin": "./node/external_packages/node-addon-require-builtin.ts",
|
|
896
901
|
"ws": "./node/external_packages/ws.ts"
|
|
897
902
|
};
|
|
@@ -908,6 +913,7 @@ const MODULE_PROXY_PREFIXES = { "@earendil-works/pi-ai/": "./node/external_packa
|
|
|
908
913
|
/** Package or subpath specifiers served from the worker bundle instead of the VFS. */
|
|
909
914
|
const REPLACED_EXTERNAL_PACKAGES = [
|
|
910
915
|
"@earendil-works/pi-ai",
|
|
916
|
+
"@earendil-works/pi-coding-agent",
|
|
911
917
|
"@vscode/ripgrep",
|
|
912
918
|
"@deepseek-ai/libreoffice-kit",
|
|
913
919
|
"@deepseek-ai/node-addon-system/flock",
|
|
@@ -1052,6 +1058,14 @@ function createSyntheticExchange(frame, sink) {
|
|
|
1052
1058
|
* so **line numbers survive**: a stack frame in a transformed module points at
|
|
1053
1059
|
* the same line as the built artifact it came from.
|
|
1054
1060
|
*
|
|
1061
|
+
* Named and default imports stay live: every reference reads the required
|
|
1062
|
+
* module object when it runs (`__dsh$m1["x"]`), never a copy taken at the
|
|
1063
|
+
* import statement, so a module in an import cycle sees a binding once the
|
|
1064
|
+
* exporting module has initialized it, as ES modules do. Scope analysis tells
|
|
1065
|
+
* those references apart from same-named locals that shadow them; it runs only
|
|
1066
|
+
* for a module that declares or assigns an import's name somewhere, because
|
|
1067
|
+
* elsewhere every read of the name is the import.
|
|
1068
|
+
*
|
|
1055
1069
|
* The image packer is this transform's only caller: it lowers every JavaScript
|
|
1056
1070
|
* entry it packs and records `LOWERING_VERSION` in the image manifest, so the
|
|
1057
1071
|
* worker wraps those bodies without carrying a compiler of its own.
|
|
@@ -1059,7 +1073,7 @@ function createSyntheticExchange(frame, sink) {
|
|
|
1059
1073
|
*/
|
|
1060
1074
|
const HELPER_SOURCE = {
|
|
1061
1075
|
def: "const __dsh$def=(t,k,get)=>Object.defineProperty(t,k,{enumerable:true,configurable:true,get});",
|
|
1062
|
-
default: "const __dsh$default=(m)=>(m&&m.__esModule?m.default:m);",
|
|
1076
|
+
default: "const __dsh$default=(m)=>(m&&m.__esModule&&Object.prototype.hasOwnProperty.call(m,\"default\")?m.default:m);",
|
|
1063
1077
|
ns: "const __dsh$ns=(m)=>(m&&m.__esModule?m:Object.assign({},m,{default:m}));",
|
|
1064
1078
|
exportAll: "const __dsh$exportAll=(t,m)=>{for(const k of Object.keys(m))if(k!==\"default\"&&!(k in t))__dsh$def(t,k,()=>m[k]);};",
|
|
1065
1079
|
dynImport: "const __dsh$dynImport=(s)=>Promise.resolve().then(()=>__dsh$ns(require(s)));"
|
|
@@ -1076,6 +1090,8 @@ function countNewlines(text) {
|
|
|
1076
1090
|
for (let index = text.indexOf("\n"); index >= 0; index = text.indexOf("\n", index + 1)) count += 1;
|
|
1077
1091
|
return count;
|
|
1078
1092
|
}
|
|
1093
|
+
/** Prefix of the identifiers this transform emits; a module declaring one would capture them. */
|
|
1094
|
+
const RESERVED_PREFIX = "__dsh$";
|
|
1079
1095
|
var Transformer = class {
|
|
1080
1096
|
path;
|
|
1081
1097
|
edits = [];
|
|
@@ -1088,6 +1104,18 @@ var Transformer = class {
|
|
|
1088
1104
|
moduleRequests = /* @__PURE__ */ new Set();
|
|
1089
1105
|
metaResolveRequests = /* @__PURE__ */ new Set();
|
|
1090
1106
|
createRequireBindings = /* @__PURE__ */ new Set();
|
|
1107
|
+
/** Module temporary per import declaration with specifiers, assigned before traversal. */
|
|
1108
|
+
importModules = /* @__PURE__ */ new Map();
|
|
1109
|
+
/** Named and default imports by local name. */
|
|
1110
|
+
importedBindings = /* @__PURE__ */ new Map();
|
|
1111
|
+
/** Read-position identifiers spelling an import's name, rewritten after traversal. */
|
|
1112
|
+
importCandidates = [];
|
|
1113
|
+
/**
|
|
1114
|
+
* Set when an import name (or the reserved prefix) is declared or assigned.
|
|
1115
|
+
* Only then can a candidate be a shadowing local, so only then does scope
|
|
1116
|
+
* analysis decide which candidates read the import.
|
|
1117
|
+
*/
|
|
1118
|
+
scopeAnalysisNeeded = false;
|
|
1091
1119
|
constructor(source, path) {
|
|
1092
1120
|
this.path = path;
|
|
1093
1121
|
this.source = source.startsWith("#!") ? `//${source.slice(2)}` : source;
|
|
@@ -1160,29 +1188,69 @@ var Transformer = class {
|
|
|
1160
1188
|
if (Array.isArray(node.attributes) && node.attributes.length > 0) this.fail("import attributes are not supported", node.start);
|
|
1161
1189
|
const source = node.source;
|
|
1162
1190
|
const request = `require(${this.literal(source)})`;
|
|
1163
|
-
const
|
|
1164
|
-
if (
|
|
1191
|
+
const held = this.importModules.get(node);
|
|
1192
|
+
if (held === void 0) {
|
|
1165
1193
|
this.replace(node.start, node.end, `${request};`);
|
|
1166
1194
|
return;
|
|
1167
1195
|
}
|
|
1168
|
-
const held = this.moduleTemp();
|
|
1169
1196
|
const lines = [`const ${held}=${request};`];
|
|
1170
|
-
for (const specifier of specifiers) {
|
|
1171
|
-
const local = specifier.local.name;
|
|
1172
|
-
if (specifier.type === "ImportDefaultSpecifier") {
|
|
1173
|
-
lines.push(`const ${local}=${this.helper("default")}(${held});`);
|
|
1174
|
-
continue;
|
|
1175
|
-
}
|
|
1176
|
-
if (specifier.type === "ImportNamespaceSpecifier") {
|
|
1177
|
-
lines.push(`const ${local}=${this.helper("ns")}(${held});`);
|
|
1178
|
-
continue;
|
|
1179
|
-
}
|
|
1180
|
-
const imported = specifier.imported;
|
|
1181
|
-
const name = imported.type === "Identifier" ? imported.name : imported.value;
|
|
1182
|
-
lines.push(`const ${local}=${held}[${JSON.stringify(name)}];`);
|
|
1183
|
-
}
|
|
1197
|
+
for (const specifier of node.specifiers) if (specifier.type === "ImportNamespaceSpecifier") lines.push(`const ${nameOf(specifier.local)}=${this.helper("ns")}(${held});`);
|
|
1184
1198
|
this.replace(node.start, node.end, lines.join(""));
|
|
1185
1199
|
}
|
|
1200
|
+
/**
|
|
1201
|
+
* The expression that reads an import when the reference runs. The default
|
|
1202
|
+
* form is parenthesized so it stays one operand in any position, including
|
|
1203
|
+
* `new` callees and member objects.
|
|
1204
|
+
*/
|
|
1205
|
+
importAccess(binding) {
|
|
1206
|
+
return binding.kind === "default" ? `(${this.helper("default")}(${binding.held}))` : `${binding.held}[${JSON.stringify(binding.imported)}]`;
|
|
1207
|
+
}
|
|
1208
|
+
/** @returns How a local export reads its binding: imports through their module object. */
|
|
1209
|
+
bindingAccess(local) {
|
|
1210
|
+
const imported = this.importedBindings.get(local);
|
|
1211
|
+
return imported === void 0 ? local : this.importAccess(imported);
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Record an identifier that spells an import's name in a read position, and
|
|
1215
|
+
* flag one in a declaration or assignment position (or one using the
|
|
1216
|
+
* reserved prefix there), which makes scope analysis decide the candidates.
|
|
1217
|
+
* @param node - The identifier.
|
|
1218
|
+
* @param parent - Its parent node.
|
|
1219
|
+
* @param key - The parent property holding it.
|
|
1220
|
+
* @param statementStart - Start of the enclosing expression statement when that statement sits in a statement list.
|
|
1221
|
+
*/
|
|
1222
|
+
identifier(node, parent, key, statementStart) {
|
|
1223
|
+
if (parent === void 0 || this.importedBindings.size === 0) return;
|
|
1224
|
+
const name = node.name;
|
|
1225
|
+
const binding = this.importedBindings.get(name);
|
|
1226
|
+
if (binding === void 0 && !name.startsWith(RESERVED_PREFIX)) return;
|
|
1227
|
+
if (isBindingPosition(parent, key)) {
|
|
1228
|
+
this.scopeAnalysisNeeded = true;
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (binding !== void 0 && isReadPosition(parent, key)) this.importCandidates.push({
|
|
1232
|
+
node,
|
|
1233
|
+
parent,
|
|
1234
|
+
key,
|
|
1235
|
+
statementStart,
|
|
1236
|
+
binding
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
/** Flag a declared or assigned name that could shadow an import or capture an emitted temporary. */
|
|
1240
|
+
bindingOccurrence(name) {
|
|
1241
|
+
if (this.importedBindings.size === 0) return;
|
|
1242
|
+
if (this.importedBindings.has(name) || name.startsWith(RESERVED_PREFIX)) this.scopeAnalysisNeeded = true;
|
|
1243
|
+
}
|
|
1244
|
+
/** Rewrite one identifier that reads an import. */
|
|
1245
|
+
importReference({ node, parent, key, statementStart, binding }) {
|
|
1246
|
+
const access = this.importAccess(binding);
|
|
1247
|
+
if (parent.type === "Property" && parent.shorthand === true) {
|
|
1248
|
+
this.replace(node.start, node.end, `${nameOf(node)}:${access}`);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
const text = (parent.type === "CallExpression" && key === "callee" || parent.type === "TaggedTemplateExpression" && key === "tag") && binding.kind === "named" ? `(0,${access})` : access;
|
|
1252
|
+
this.replace(node.start, node.end, text.startsWith("(") && node.start === statementStart ? `;${text}` : text);
|
|
1253
|
+
}
|
|
1186
1254
|
exportNamed(node) {
|
|
1187
1255
|
this.moduleSyntax = true;
|
|
1188
1256
|
const declaration = node.declaration;
|
|
@@ -1295,16 +1363,25 @@ var Transformer = class {
|
|
|
1295
1363
|
`finally{if(!${exhausted})${ALS}.resume(await ${ALS}.pause(${ALS}.close(${iterator})))}}`
|
|
1296
1364
|
].join(""));
|
|
1297
1365
|
}
|
|
1298
|
-
visit(node, context) {
|
|
1366
|
+
visit(node, context, parent, key) {
|
|
1299
1367
|
if (node === null || typeof node !== "object") return;
|
|
1300
1368
|
if (Array.isArray(node)) {
|
|
1301
|
-
for (const child of node) this.visit(child, context);
|
|
1369
|
+
for (const child of node) this.visit(child, context, parent, key);
|
|
1302
1370
|
return;
|
|
1303
1371
|
}
|
|
1304
1372
|
const record = node;
|
|
1305
1373
|
if (typeof record.type !== "string") return;
|
|
1306
1374
|
let next = context;
|
|
1307
1375
|
switch (record.type) {
|
|
1376
|
+
case "Identifier":
|
|
1377
|
+
this.identifier(record, parent, key, context.statementStart);
|
|
1378
|
+
break;
|
|
1379
|
+
case "ObjectPattern":
|
|
1380
|
+
for (const property of record.properties) {
|
|
1381
|
+
const value = property.value;
|
|
1382
|
+
if (value?.type === "Identifier") this.bindingOccurrence(value.name);
|
|
1383
|
+
}
|
|
1384
|
+
break;
|
|
1308
1385
|
case "ImportDeclaration":
|
|
1309
1386
|
this.importDeclaration(record);
|
|
1310
1387
|
break;
|
|
@@ -1328,6 +1405,7 @@ var Transformer = class {
|
|
|
1328
1405
|
case "CallExpression": {
|
|
1329
1406
|
const callee = record.callee;
|
|
1330
1407
|
const callArguments = record.arguments;
|
|
1408
|
+
if (callee.type === "Identifier" && callee.name === "eval" && record.optional !== true && this.importedBindings.size > 0) this.fail("direct eval cannot see imports once they are read through their module", record.start);
|
|
1331
1409
|
if (this.isRequireCall(callee, context.moduleScope) && callArguments.length === 1 && typeof callArguments[0]?.value === "string") this.moduleRequests.add(callArguments[0].value);
|
|
1332
1410
|
if (callee.type === "MemberExpression") {
|
|
1333
1411
|
const object = callee.object;
|
|
@@ -1388,11 +1466,12 @@ var Transformer = class {
|
|
|
1388
1466
|
}
|
|
1389
1467
|
if (record.type === "ExpressionStatement") next = {
|
|
1390
1468
|
...next,
|
|
1391
|
-
statement: record
|
|
1469
|
+
statement: record,
|
|
1470
|
+
statementStart: inStatementList(parent, key) ? record.start : void 0
|
|
1392
1471
|
};
|
|
1393
|
-
for (const [
|
|
1394
|
-
if (
|
|
1395
|
-
this.visit(value, next);
|
|
1472
|
+
for (const [childKey, value] of Object.entries(record)) {
|
|
1473
|
+
if (childKey === "type" || childKey === "start" || childKey === "end") continue;
|
|
1474
|
+
this.visit(value, next, record, childKey);
|
|
1396
1475
|
}
|
|
1397
1476
|
}
|
|
1398
1477
|
isCreateRequireCall(node) {
|
|
@@ -1415,10 +1494,73 @@ var Transformer = class {
|
|
|
1415
1494
|
const source = statement.source;
|
|
1416
1495
|
if (source.value !== "node:module" && source.value !== "module") continue;
|
|
1417
1496
|
for (const specifier of statement.specifiers) {
|
|
1418
|
-
if (specifier.type !== "ImportSpecifier" ||
|
|
1419
|
-
this.createRequireBindings.add(
|
|
1497
|
+
if (specifier.type !== "ImportSpecifier" || specifierName(specifier.imported) !== "createRequire") continue;
|
|
1498
|
+
this.createRequireBindings.add(specifier.local.name);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Give every import declaration its module temporary and record its named and
|
|
1504
|
+
* default bindings before traversal, because references may precede the
|
|
1505
|
+
* declaration in source order.
|
|
1506
|
+
*/
|
|
1507
|
+
indexImports(program) {
|
|
1508
|
+
for (const statement of program.body) {
|
|
1509
|
+
if (statement.type !== "ImportDeclaration" || statement.specifiers.length === 0) continue;
|
|
1510
|
+
const held = this.moduleTemp();
|
|
1511
|
+
this.importModules.set(statement, held);
|
|
1512
|
+
for (const specifier of statement.specifiers) if (specifier.type === "ImportDefaultSpecifier") this.importedBindings.set(specifier.local.name, {
|
|
1513
|
+
held,
|
|
1514
|
+
kind: "default",
|
|
1515
|
+
imported: "default"
|
|
1516
|
+
});
|
|
1517
|
+
else if (specifier.type === "ImportSpecifier") this.importedBindings.set(specifier.local.name, {
|
|
1518
|
+
held,
|
|
1519
|
+
kind: "named",
|
|
1520
|
+
imported: specifierName(specifier.imported)
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Decide with scope analysis which candidates read an import.
|
|
1526
|
+
*
|
|
1527
|
+
* Refuses an assignment to an import, which live reads cannot express, and a
|
|
1528
|
+
* declaration using the reserved prefix, which would capture an emitted
|
|
1529
|
+
* module temporary. Direct `eval` is refused during traversal.
|
|
1530
|
+
* @param program - The parsed module, which gains node ranges here.
|
|
1531
|
+
* @returns The identifier nodes that read a named or default import.
|
|
1532
|
+
*/
|
|
1533
|
+
resolveImportReads(program) {
|
|
1534
|
+
assignRanges(program);
|
|
1535
|
+
const manager = analyze(program, {
|
|
1536
|
+
ecmaVersion: 2022,
|
|
1537
|
+
sourceType: "module",
|
|
1538
|
+
childVisitorKeys: KEYS
|
|
1539
|
+
});
|
|
1540
|
+
for (const scope of manager.scopes) for (const variable of scope.variables) {
|
|
1541
|
+
const definition = variable.defs[0];
|
|
1542
|
+
if (definition === void 0 || !variable.name.startsWith(RESERVED_PREFIX)) continue;
|
|
1543
|
+
this.fail(`${variable.name} uses the transform's reserved ${RESERVED_PREFIX} prefix`, this.rangeStart(definition.name));
|
|
1544
|
+
}
|
|
1545
|
+
const reads = /* @__PURE__ */ new Set();
|
|
1546
|
+
const moduleScope = manager.scopes.find((scope) => scope.type === "module");
|
|
1547
|
+
for (const variable of moduleScope?.variables ?? []) {
|
|
1548
|
+
if (!this.importedBindings.has(variable.name) || variable.defs[0]?.type !== "ImportBinding") continue;
|
|
1549
|
+
for (const reference of variable.references) {
|
|
1550
|
+
if (reference.isWrite()) this.fail(`the import ${variable.name} cannot be assigned`, this.rangeStart(reference.identifier));
|
|
1551
|
+
reads.add(reference.identifier);
|
|
1420
1552
|
}
|
|
1421
1553
|
}
|
|
1554
|
+
return reads;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* @param node - A node scope analysis reports.
|
|
1558
|
+
* @returns Its source offset; `assignRanges` gave every node a range.
|
|
1559
|
+
*/
|
|
1560
|
+
rangeStart(node) {
|
|
1561
|
+
const start = node.range?.[0];
|
|
1562
|
+
if (start === void 0) this.fail("scope analysis reported a node without a source range", 0);
|
|
1563
|
+
return start;
|
|
1422
1564
|
}
|
|
1423
1565
|
run() {
|
|
1424
1566
|
if (this.source.includes(`${ALS}.pause(`) || this.source.includes("__als$")) this.fail("the module is already lowered; check the image manifest wiring", 0);
|
|
@@ -1433,17 +1575,21 @@ var Transformer = class {
|
|
|
1433
1575
|
this.fail(`parse failed: ${reason.message}`, 0);
|
|
1434
1576
|
}
|
|
1435
1577
|
this.indexCreateRequireImports(program);
|
|
1578
|
+
this.indexImports(program);
|
|
1436
1579
|
this.visit(program, {
|
|
1437
1580
|
asyncGenerator: false,
|
|
1438
1581
|
functionDepth: 0,
|
|
1439
1582
|
moduleScope: true
|
|
1440
1583
|
});
|
|
1584
|
+
const reads = this.scopeAnalysisNeeded ? this.resolveImportReads(program) : void 0;
|
|
1585
|
+
for (const candidate of this.importCandidates) if (reads === void 0 || reads.has(candidate.node)) this.importReference(candidate);
|
|
1441
1586
|
if (this.edits.length === 0 && !this.moduleSyntax) return this.source;
|
|
1442
1587
|
const prologue = [];
|
|
1443
1588
|
if (this.moduleSyntax) prologue.push("\"use strict\";Object.defineProperty(exports,\"__esModule\",{value:true});");
|
|
1589
|
+
const getters = this.bindings.map(({ exported, local }) => `__dsh$def(exports,${JSON.stringify(exported)},()=>${this.bindingAccess(local)});`);
|
|
1444
1590
|
if (this.bindings.length > 0) this.helper("def");
|
|
1445
1591
|
for (const [name, source] of Object.entries(HELPER_SOURCE)) if (this.helpers.has(name)) prologue.push(source);
|
|
1446
|
-
|
|
1592
|
+
prologue.push(...getters);
|
|
1447
1593
|
const sorted = [...this.edits].sort((left, right) => left.start - right.start || left.end - right.end);
|
|
1448
1594
|
const render = (from, to) => {
|
|
1449
1595
|
let cursor = from;
|
|
@@ -1472,6 +1618,96 @@ var Transformer = class {
|
|
|
1472
1618
|
function nameOf(node) {
|
|
1473
1619
|
return node.type === "Identifier" ? node.name : String(node.value);
|
|
1474
1620
|
}
|
|
1621
|
+
/** @returns The name an import or export specifier spells, as an identifier or a string literal. */
|
|
1622
|
+
function specifierName(node) {
|
|
1623
|
+
return node.type === "Identifier" ? node.name : String(node.value);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Give every node the `range` that eslint-scope reads. The parse leaves ranges
|
|
1627
|
+
* out because only modules that need scope analysis use them.
|
|
1628
|
+
* @param node - A node or node array of the parsed module.
|
|
1629
|
+
*/
|
|
1630
|
+
function assignRanges(node) {
|
|
1631
|
+
if (node === null || typeof node !== "object") return;
|
|
1632
|
+
if (Array.isArray(node)) {
|
|
1633
|
+
for (const child of node) assignRanges(child);
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const record = node;
|
|
1637
|
+
if (typeof record.type !== "string") return;
|
|
1638
|
+
record.range = [record.start, record.end];
|
|
1639
|
+
for (const value of Object.values(record)) assignRanges(value);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* ESTree positions where an identifier names a declared or assigned binding.
|
|
1643
|
+
* Property values inside an object pattern are checked at the pattern.
|
|
1644
|
+
*
|
|
1645
|
+
* Together with the ObjectPattern check, this must recognize every ESTree
|
|
1646
|
+
* declaration and assignment form: a missed form lets a shadowing local skip
|
|
1647
|
+
* scope analysis and be rewritten as an import read. A parser upgrade that adds
|
|
1648
|
+
* a binding form must extend this list and the shadowing cases in
|
|
1649
|
+
* `transform-imports.spec.ts`.
|
|
1650
|
+
* @param parent - Parent of the identifier.
|
|
1651
|
+
* @param key - The parent property holding it.
|
|
1652
|
+
* @returns Whether the identifier declares or assigns a binding.
|
|
1653
|
+
*/
|
|
1654
|
+
function isBindingPosition(parent, key) {
|
|
1655
|
+
switch (parent.type) {
|
|
1656
|
+
case "VariableDeclarator": return key === "id";
|
|
1657
|
+
case "FunctionDeclaration":
|
|
1658
|
+
case "FunctionExpression":
|
|
1659
|
+
case "ArrowFunctionExpression": return key === "id" || key === "params";
|
|
1660
|
+
case "ClassDeclaration":
|
|
1661
|
+
case "ClassExpression": return key === "id";
|
|
1662
|
+
case "CatchClause": return key === "param";
|
|
1663
|
+
case "ArrayPattern":
|
|
1664
|
+
case "RestElement":
|
|
1665
|
+
case "UpdateExpression": return true;
|
|
1666
|
+
case "AssignmentPattern":
|
|
1667
|
+
case "AssignmentExpression":
|
|
1668
|
+
case "ForInStatement":
|
|
1669
|
+
case "ForOfStatement": return key === "left";
|
|
1670
|
+
default: return false;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* ESTree positions where an identifier, outside a binding position, reads a
|
|
1675
|
+
* binding rather than naming a property, label, specifier, or meta property.
|
|
1676
|
+
* Positions not listed read a binding, so the binding positions must be ruled
|
|
1677
|
+
* out first (`isBindingPosition`).
|
|
1678
|
+
* @param parent - Parent of the identifier.
|
|
1679
|
+
* @param key - The parent property holding it.
|
|
1680
|
+
* @returns Whether the identifier reads a binding.
|
|
1681
|
+
*/
|
|
1682
|
+
function isReadPosition(parent, key) {
|
|
1683
|
+
switch (parent.type) {
|
|
1684
|
+
case "MemberExpression": return key !== "property" || parent.computed === true;
|
|
1685
|
+
case "Property":
|
|
1686
|
+
case "MethodDefinition":
|
|
1687
|
+
case "PropertyDefinition": return key !== "key" || parent.computed === true;
|
|
1688
|
+
case "LabeledStatement":
|
|
1689
|
+
case "BreakStatement":
|
|
1690
|
+
case "ContinueStatement":
|
|
1691
|
+
case "ImportSpecifier":
|
|
1692
|
+
case "ImportDefaultSpecifier":
|
|
1693
|
+
case "ImportNamespaceSpecifier":
|
|
1694
|
+
case "ExportSpecifier":
|
|
1695
|
+
case "ExportAllDeclaration":
|
|
1696
|
+
case "MetaProperty": return false;
|
|
1697
|
+
default: return true;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* @param parent - Parent of a statement.
|
|
1702
|
+
* @param key - The parent property holding it.
|
|
1703
|
+
* @returns Whether the statement sits in a statement list, where it can follow
|
|
1704
|
+
* a statement that ends without a semicolon.
|
|
1705
|
+
*/
|
|
1706
|
+
function inStatementList(parent, key) {
|
|
1707
|
+
if (parent === void 0) return false;
|
|
1708
|
+
if (parent.type === "SwitchCase") return key === "consequent";
|
|
1709
|
+
return key === "body" && (parent.type === "Program" || parent.type === "BlockStatement" || parent.type === "StaticBlock");
|
|
1710
|
+
}
|
|
1475
1711
|
/** Every binding an exported declaration introduces, including patterns. */
|
|
1476
1712
|
function declaredBindings(declaration, fail) {
|
|
1477
1713
|
if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
@@ -1564,6 +1800,8 @@ function lowerModuleSource(options) {
|
|
|
1564
1800
|
*
|
|
1565
1801
|
* - `GET /__boot__` answers from tunnel glue, never from the host API surface,
|
|
1566
1802
|
* because the page needs the boot payload before its Cordis tree exists.
|
|
1803
|
+
* `GET /__assets__` answers the same way with every package asset of the
|
|
1804
|
+
* image, which the page maps before any Client bundle runs.
|
|
1567
1805
|
* - Privileged `/api` methods take that same direct entry. The method set is not
|
|
1568
1806
|
* restated here: a 401 or 403 from the route lane is retried on the direct
|
|
1569
1807
|
* lane because the page owns the worker and needs no network authentication.
|
|
@@ -1965,6 +2203,10 @@ var TunnelServer = class {
|
|
|
1965
2203
|
this.serveBoot(frame, sink);
|
|
1966
2204
|
return;
|
|
1967
2205
|
}
|
|
2206
|
+
if (path === "/__assets__") {
|
|
2207
|
+
this.servePackageAssets(frame, sink);
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
1968
2210
|
if (path.startsWith(`/api/`)) {
|
|
1969
2211
|
await this.serveApi(frame, routed, path, sink);
|
|
1970
2212
|
return;
|
|
@@ -2030,6 +2272,24 @@ var TunnelServer = class {
|
|
|
2030
2272
|
}
|
|
2031
2273
|
buffered.flushTo(sink);
|
|
2032
2274
|
}
|
|
2275
|
+
servePackageAssets(frame, sink) {
|
|
2276
|
+
if (this.seams === void 0) throw new Error("webworker tunnel: package assets requested before the host tree is serving");
|
|
2277
|
+
if (frame.method !== "GET") {
|
|
2278
|
+
sink.end({
|
|
2279
|
+
status: 405,
|
|
2280
|
+
headers: { allow: "GET" }
|
|
2281
|
+
});
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
sink.end({
|
|
2285
|
+
status: 200,
|
|
2286
|
+
headers: {
|
|
2287
|
+
"content-type": "application/x-tar",
|
|
2288
|
+
"cache-control": "no-store"
|
|
2289
|
+
},
|
|
2290
|
+
body: this.seams.packageAssets()
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2033
2293
|
serveBoot(frame, sink) {
|
|
2034
2294
|
if (this.seams === void 0) throw new Error("webworker tunnel: boot payload requested before the host tree is serving");
|
|
2035
2295
|
if (frame.method !== "GET") {
|
|
@@ -2144,7 +2404,7 @@ const EXEC_PATH = "/dsh/bin/node";
|
|
|
2144
2404
|
/** Node release whose internal-module members `node-addon-require-builtin` follows in the Worker. */
|
|
2145
2405
|
const NODE_VERSION = "24.18.0";
|
|
2146
2406
|
/**
|
|
2147
|
-
* Publish `globalThis.process
|
|
2407
|
+
* Publish `globalThis.process` and Node's `global` alias for the worker lifetime.
|
|
2148
2408
|
*
|
|
2149
2409
|
* `versions.node` names the Node release the Worker's `node-addon-require-builtin` internals follow, so Cordis's
|
|
2150
2410
|
* `ModuleLoader.fromInternal()` reaches them the way it reaches Node's; `execArgv` stays empty, without
|
|
@@ -2213,6 +2473,7 @@ function installProcessGlobal(options) {
|
|
|
2213
2473
|
console.warn(`webworker process: exit(${String(code ?? 0)}) requested; the worker keeps running`);
|
|
2214
2474
|
}
|
|
2215
2475
|
};
|
|
2476
|
+
globalThis.global = globalThis;
|
|
2216
2477
|
globalThis.process = shim;
|
|
2217
2478
|
return shim;
|
|
2218
2479
|
}
|
|
@@ -3597,6 +3858,50 @@ function requireActiveVfs() {
|
|
|
3597
3858
|
return active;
|
|
3598
3859
|
}
|
|
3599
3860
|
//#endregion
|
|
3861
|
+
//#region src/package-assets.ts
|
|
3862
|
+
/**
|
|
3863
|
+
* Package assets for the preview page. A Client bundle names each image as a
|
|
3864
|
+
* document-relative reference, `plugins/<package>/assets/<path>`, and resolves
|
|
3865
|
+
* it through the module loader's synchronous `require.asset`. The page of the
|
|
3866
|
+
* Worker preview cannot request such a URL itself, so the Worker hands it every
|
|
3867
|
+
* asset the image holds in one archive and the page maps each reference to a
|
|
3868
|
+
* Blob URL before any bundle runs.
|
|
3869
|
+
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/package-assets
|
|
3870
|
+
*/
|
|
3871
|
+
/** Entries of one directory. */
|
|
3872
|
+
function entriesOf(vfs, directory) {
|
|
3873
|
+
return vfs.readdirSync(directory, { withFileTypes: true });
|
|
3874
|
+
}
|
|
3875
|
+
/** Files under one directory, as `/`-separated paths relative to it. */
|
|
3876
|
+
function filesBelow(vfs, directory, prefix = "") {
|
|
3877
|
+
return entriesOf(vfs, directory).flatMap((entry) => entry.isDirectory() ? filesBelow(vfs, join(directory, entry.name), `${prefix}${entry.name}/`) : entry.isFile() ? [`${prefix}${entry.name}`] : []);
|
|
3878
|
+
}
|
|
3879
|
+
/** Installed package directories under `node_modules`, scoped ones included. */
|
|
3880
|
+
function packageDirectories(vfs, modules) {
|
|
3881
|
+
if (!vfs.existsSync(modules)) return [];
|
|
3882
|
+
return entriesOf(vfs, modules).filter((entry) => entry.isDirectory()).flatMap((entry) => entry.name.startsWith("@") ? entriesOf(vfs, join(modules, entry.name)).filter((scoped) => scoped.isDirectory()).map((scoped) => join(modules, entry.name, scoped.name)) : [join(modules, entry.name)]);
|
|
3883
|
+
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Archive every file a package of the image publishes through `"./assets/*": "./lib/assets/*"`.
|
|
3886
|
+
* @param vfs - Mounted image filesystem.
|
|
3887
|
+
* @param root - Image root holding `node_modules`.
|
|
3888
|
+
* @returns An uncompressed tar whose entries are named `plugins/<package>/assets/<path>` with the path unencoded,
|
|
3889
|
+
* so every name fits the ustar limits the base image already met.
|
|
3890
|
+
*/
|
|
3891
|
+
function packPackageAssets(vfs, root) {
|
|
3892
|
+
const files = {};
|
|
3893
|
+
for (const directory of packageDirectories(vfs, join(root, "node_modules"))) {
|
|
3894
|
+
const manifestPath = join(directory, "package.json");
|
|
3895
|
+
if (!vfs.existsSync(manifestPath)) continue;
|
|
3896
|
+
const manifest = JSON.parse(vfs.readFileSync(manifestPath, "utf8"));
|
|
3897
|
+
if (typeof manifest.name !== "string" || manifest.exports?.["./assets/*"] !== "./lib/assets/*") continue;
|
|
3898
|
+
const assets = join(directory, "lib", "assets");
|
|
3899
|
+
if (!vfs.existsSync(assets)) continue;
|
|
3900
|
+
for (const path of filesBelow(vfs, assets)) files[`plugins/${manifest.name}/assets/${path}`] = vfs.readFileSync(join(assets, path));
|
|
3901
|
+
}
|
|
3902
|
+
return packTar(files);
|
|
3903
|
+
}
|
|
3904
|
+
//#endregion
|
|
3600
3905
|
//#region src/image-manifest.ts
|
|
3601
3906
|
/**
|
|
3602
3907
|
* The base image's manifest: what the packer records about one image and what
|
|
@@ -3769,7 +4074,8 @@ function createWorkerHost(options) {
|
|
|
3769
4074
|
const peer = connection.browserPeer;
|
|
3770
4075
|
tunnel.serve({
|
|
3771
4076
|
directFetch: (request) => handler.fetch(request, peer),
|
|
3772
|
-
bootPayload: () => readBootPayload(ctx),
|
|
4077
|
+
bootPayload: () => readBootPayload(ctx, peer),
|
|
4078
|
+
packageAssets: () => packPackageAssets(mounted, root),
|
|
3773
4079
|
openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, peer, signal),
|
|
3774
4080
|
streamFailure: typertGateway.wireStream.failure
|
|
3775
4081
|
});
|
|
@@ -3911,12 +4217,13 @@ function prepareProfile(appBoot, vfs, manifest, root, env) {
|
|
|
3911
4217
|
* from the in-process webserver service, never from the API surface, because
|
|
3912
4218
|
* the page has no Cordis tree yet.
|
|
3913
4219
|
* @param ctx - Booted host context.
|
|
4220
|
+
* @param peer - the browser Peer the page speaks for.
|
|
3914
4221
|
* @returns Boot payload for `GET /__boot__`.
|
|
3915
4222
|
*/
|
|
3916
|
-
function readBootPayload(ctx) {
|
|
4223
|
+
function readBootPayload(ctx, peer) {
|
|
3917
4224
|
const webServer = ctx.get("webServer");
|
|
3918
4225
|
if (webServer === void 0) throw new Error("webworker host: no webServer service, so the page cannot receive its boot injections");
|
|
3919
|
-
return { injections: webServer.collectIndexInjections() };
|
|
4226
|
+
return { injections: webServer.collectIndexInjections(peer) };
|
|
3920
4227
|
}
|
|
3921
4228
|
//#endregion
|
|
3922
4229
|
//#region src/fixture-manifest.ts
|
|
@@ -72,6 +72,11 @@ export declare class WorkerTunnel {
|
|
|
72
72
|
* @returns The payload the page applies before the client tree loads.
|
|
73
73
|
*/
|
|
74
74
|
bootPayload(): Promise<BootPayload>;
|
|
75
|
+
/**
|
|
76
|
+
* Read every package asset of the image and map each reference to a Blob URL.
|
|
77
|
+
* @returns The synchronous resolver the module system calls for `require.asset`.
|
|
78
|
+
*/
|
|
79
|
+
packageAssets(): Promise<(reference: string) => string>;
|
|
75
80
|
/**
|
|
76
81
|
* `loadBundle` seam: take one client bundle through the tunnel and execute it
|
|
77
82
|
* as a classic script, exactly like the shell's same-origin `<script src>`.
|
|
@@ -51,7 +51,7 @@ export declare class AsyncLocalStorage<T> {
|
|
|
51
51
|
/**
|
|
52
52
|
* Copy every live instance's current store. Not part of the Node face: this is
|
|
53
53
|
* the shim's own mechanism, kept in the class so the overlay stays private.
|
|
54
|
-
* @returns the snapshot, or undefined when no instance has a store.
|
|
54
|
+
* @returns the snapshot, or undefined when no instance has a store or explicit context.
|
|
55
55
|
*/
|
|
56
56
|
static captureContext(): AsyncContextSnapshot | undefined;
|
|
57
57
|
/**
|
|
@@ -90,8 +90,8 @@ export type AsyncContextSnapshot = readonly CapturedStore[];
|
|
|
90
90
|
export type AmbientSnapshot = readonly CapturedStore[];
|
|
91
91
|
/**
|
|
92
92
|
* Copy every live instance's current store.
|
|
93
|
-
* @returns the snapshot, or undefined when no instance has a store
|
|
94
|
-
* layer then wraps nothing and callbacks inherit the stack top).
|
|
93
|
+
* @returns the snapshot, or undefined when no instance has a store or explicit
|
|
94
|
+
* context (the hook layer then wraps nothing and callbacks inherit the stack top).
|
|
95
95
|
*/
|
|
96
96
|
export declare function captureAsyncContext(): AsyncContextSnapshot | undefined;
|
|
97
97
|
/**
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* `node:buffer` for the worker, backed by the `buffer` npm package (feross), and
|
|
3
3
|
* the matching `globalThis.Buffer` install. Node code treats Buffer as ambient,
|
|
4
4
|
* so the global must exist before any host module evaluates.
|
|
5
|
+
*
|
|
6
|
+
* Loading this module replaces `Buffer.from`, `Buffer.prototype.toString`, and
|
|
7
|
+
* `toLocaleString` on the package's class, so every Buffer user in the Worker,
|
|
8
|
+
* including bundled third-party code, gets the same conversions. Whole-buffer
|
|
9
|
+
* UTF-8 conversions use `TextEncoder` and `TextDecoder` with Node's U+FFFD
|
|
10
|
+
* replacement and BOM retention; other encodings and ranged `toString` calls
|
|
11
|
+
* keep the package's implementation.
|
|
5
12
|
*/
|
|
6
13
|
import { Buffer, kMaxLength } from 'buffer';
|
|
7
14
|
export { Buffer, kMaxLength };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Node string decoding backed by the maintained npm implementation used by readable-stream. */
|
|
2
|
+
import '../../../string-decoder-types.ts';
|
|
3
|
+
import { StringDecoder } from 'string_decoder/';
|
|
4
|
+
export { StringDecoder };
|
|
5
|
+
/** CommonJS interop marker consumed by the worker module loader. */
|
|
6
|
+
export declare const __esModule = true;
|
|
7
|
+
/** CommonJS-compatible namespace for default imports. */
|
|
8
|
+
declare const _default: {
|
|
9
|
+
StringDecoder: typeof StringDecoder;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
//# sourceMappingURL=string_decoder.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** REPL construction is unavailable in the browser worker. */
|
|
2
|
+
export declare const start: typeof import('node:repl').start;
|
|
3
|
+
/** CommonJS interop marker consumed by the Worker module loader. */
|
|
4
|
+
export declare const __esModule = true;
|
|
5
|
+
/** CommonJS export for the REPL member used by the Node PTC evaluator. */
|
|
6
|
+
declare const _default: {
|
|
7
|
+
start: typeof import("repl").start;
|
|
8
|
+
};
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=repl.d.ts.map
|