@genroc/eval-node 0.0.0-edge.81cd55a → 0.0.0-edge.84a2723
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.md +1 -1
- package/dist/import.js +19 -6
- package/import.ts +73 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -169,7 +169,7 @@ no retry policy to write.
|
|
|
169
169
|
|
|
170
170
|
- id: script_failed
|
|
171
171
|
switch:
|
|
172
|
-
- case: '
|
|
172
|
+
- case: 'last_error.data.name == "LimitExceeded"'
|
|
173
173
|
raise: { code: limit_exceeded, message: "the script rejected the amount" }
|
|
174
174
|
- raise: { code: script_failed, message: "the script failed" }
|
|
175
175
|
```
|
package/dist/import.js
CHANGED
|
@@ -86,7 +86,9 @@ function objectType(s, used) {
|
|
|
86
86
|
const required = new Set(s.required ?? []);
|
|
87
87
|
const lines = [];
|
|
88
88
|
for (const [key, sub] of Object.entries(props)) {
|
|
89
|
-
const doc = typeof sub.description === "string"
|
|
89
|
+
const doc = typeof sub.description === "string"
|
|
90
|
+
? ` /** ${sub.description} */\n`
|
|
91
|
+
: "";
|
|
90
92
|
lines.push(`${doc} ${propKey(key)}${required.has(key) ? "" : "?"}: ${tsType(sub, used)};`);
|
|
91
93
|
}
|
|
92
94
|
if (s.additionalProperties && typeof s.additionalProperties === "object") {
|
|
@@ -102,7 +104,7 @@ function union(parts) {
|
|
|
102
104
|
}
|
|
103
105
|
const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
104
106
|
const propKey = (k) => (IDENT.test(k) ? k : JSON.stringify(k));
|
|
105
|
-
const identifier = (n) =>
|
|
107
|
+
const identifier = (n) => IDENT.test(n) ? n : `Def_${n.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
106
108
|
function deref(s, defs) {
|
|
107
109
|
let cur = s;
|
|
108
110
|
for (let i = 0; cur && typeof cur.$ref === "string" && i < 16; i++) {
|
|
@@ -200,7 +202,10 @@ async function typecheck(root, sites) {
|
|
|
200
202
|
// base config there is nothing to opt in with, so the default stays none.
|
|
201
203
|
...(base ? {} : { types: [] }),
|
|
202
204
|
},
|
|
203
|
-
files: group.flatMap((s) => [
|
|
205
|
+
files: group.flatMap((s) => [
|
|
206
|
+
relative(dir, s.path),
|
|
207
|
+
relative(dir, typesPathFor(s.path)),
|
|
208
|
+
]),
|
|
204
209
|
// `files` overrides the base's, but a base `include` survives beside it and would
|
|
205
210
|
// drag the author's whole tree in, to be checked under the worker lib.
|
|
206
211
|
include: [],
|
|
@@ -250,7 +255,10 @@ const transpile = {
|
|
|
250
255
|
return { code: out.outputText, map: out.sourceMapText ?? null };
|
|
251
256
|
},
|
|
252
257
|
};
|
|
253
|
-
const BUILTIN = new Set([
|
|
258
|
+
const BUILTIN = new Set([
|
|
259
|
+
...builtinModules,
|
|
260
|
+
...builtinModules.map((m) => `node:${m}`),
|
|
261
|
+
]);
|
|
254
262
|
/** Resolves imports through TYPESCRIPT, using the same config the typecheck ran under, so a
|
|
255
263
|
* `paths` alias that compiles also bundles. Reimplementing `paths` here would be a second
|
|
256
264
|
* resolver to keep in agreement with tsc; this one cannot disagree.
|
|
@@ -270,7 +278,9 @@ function tsResolve(configPath) {
|
|
|
270
278
|
const { resolvedModule } = ts.resolveModuleName(source, importer, options, ts.sys);
|
|
271
279
|
if (!resolvedModule || resolvedModule.isExternalLibraryImport)
|
|
272
280
|
return null;
|
|
273
|
-
return resolvedModule.resolvedFileName.endsWith(".d.ts")
|
|
281
|
+
return resolvedModule.resolvedFileName.endsWith(".d.ts")
|
|
282
|
+
? null
|
|
283
|
+
: resolvedModule.resolvedFileName;
|
|
274
284
|
},
|
|
275
285
|
};
|
|
276
286
|
}
|
|
@@ -299,7 +309,10 @@ async function bundle(site, root) {
|
|
|
299
309
|
}
|
|
300
310
|
},
|
|
301
311
|
}).catch((e) => die(`${site.path}: ${e instanceof Error ? e.message : String(e)}`));
|
|
302
|
-
const { output } = await built.generate({
|
|
312
|
+
const { output } = await built.generate({
|
|
313
|
+
format: "es",
|
|
314
|
+
inlineDynamicImports: true,
|
|
315
|
+
});
|
|
303
316
|
await built.close();
|
|
304
317
|
// Refused here rather than in the realm: the evaluator can only report it against a running
|
|
305
318
|
// instance, and the file it names is on this machine.
|
package/import.ts
CHANGED
|
@@ -72,13 +72,16 @@ function tsType(s: Schema | undefined, used: Set<string>): string {
|
|
|
72
72
|
if (Array.isArray(s.enum)) {
|
|
73
73
|
return s.enum.map((v: unknown) => JSON.stringify(v)).join(" | ") || "never";
|
|
74
74
|
}
|
|
75
|
-
if (Array.isArray(s.anyOf))
|
|
76
|
-
|
|
75
|
+
if (Array.isArray(s.anyOf))
|
|
76
|
+
return union(s.anyOf.map((a: Schema) => tsType(a, used)));
|
|
77
|
+
if (Array.isArray(s.oneOf))
|
|
78
|
+
return union(s.oneOf.map((a: Schema) => tsType(a, used)));
|
|
77
79
|
if (Array.isArray(s.allOf)) {
|
|
78
80
|
return s.allOf.map((a: Schema) => tsType(a, used)).join(" & ") || "unknown";
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
const types: string[] =
|
|
83
|
+
const types: string[] =
|
|
84
|
+
s.type === undefined ? [] : Array.isArray(s.type) ? s.type : [s.type];
|
|
82
85
|
if (types.length === 0) {
|
|
83
86
|
// The top type: `{}` means unknown, not "an empty object". specs/unknown-type.md.
|
|
84
87
|
return s.properties ? objectType(s, used) : "unknown";
|
|
@@ -111,8 +114,13 @@ function objectType(s: Schema, used: Set<string>): string {
|
|
|
111
114
|
const required = new Set<string>(s.required ?? []);
|
|
112
115
|
const lines: string[] = [];
|
|
113
116
|
for (const [key, sub] of Object.entries(props)) {
|
|
114
|
-
const doc =
|
|
115
|
-
|
|
117
|
+
const doc =
|
|
118
|
+
typeof sub.description === "string"
|
|
119
|
+
? ` /** ${sub.description} */\n`
|
|
120
|
+
: "";
|
|
121
|
+
lines.push(
|
|
122
|
+
`${doc} ${propKey(key)}${required.has(key) ? "" : "?"}: ${tsType(sub, used)};`,
|
|
123
|
+
);
|
|
116
124
|
}
|
|
117
125
|
if (s.additionalProperties && typeof s.additionalProperties === "object") {
|
|
118
126
|
lines.push(` [key: string]: ${tsType(s.additionalProperties, used)};`);
|
|
@@ -128,9 +136,13 @@ function union(parts: string[]): string {
|
|
|
128
136
|
|
|
129
137
|
const IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
130
138
|
const propKey = (k: string) => (IDENT.test(k) ? k : JSON.stringify(k));
|
|
131
|
-
const identifier = (n: string) =>
|
|
139
|
+
const identifier = (n: string) =>
|
|
140
|
+
IDENT.test(n) ? n : `Def_${n.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
132
141
|
|
|
133
|
-
function deref(
|
|
142
|
+
function deref(
|
|
143
|
+
s: Schema | undefined,
|
|
144
|
+
defs: Record<string, Schema>,
|
|
145
|
+
): Schema | undefined {
|
|
134
146
|
let cur = s;
|
|
135
147
|
for (let i = 0; cur && typeof cur.$ref === "string" && i < 16; i++) {
|
|
136
148
|
cur = defs[cur.$ref.replace(/^#\/\$defs\//, "")];
|
|
@@ -142,7 +154,10 @@ function deref(s: Schema | undefined, defs: Record<string, Schema>): Schema | un
|
|
|
142
154
|
* `{code, input, timeout_ms, …}`, and only its `input` field is bound as the script's
|
|
143
155
|
* parameter. genroc cannot know that; this file owns the evaluator's wire contract, so
|
|
144
156
|
* the navigation belongs here rather than in genctl. */
|
|
145
|
-
function scriptInput(
|
|
157
|
+
function scriptInput(
|
|
158
|
+
site: Site,
|
|
159
|
+
defs: Record<string, Schema>,
|
|
160
|
+
): Schema | undefined {
|
|
146
161
|
const action = deref(site.input, defs);
|
|
147
162
|
const bound = action?.properties?.input;
|
|
148
163
|
return bound ?? site.input;
|
|
@@ -189,7 +204,10 @@ function typesPathFor(scriptPath: string): string {
|
|
|
189
204
|
/** The nearest tsconfig above the script — the one the author's editor already reads. Two
|
|
190
205
|
* different configs mean a red editor over a clean apply, or the reverse. The walk stops at
|
|
191
206
|
* the project root: above it is not this project. */
|
|
192
|
-
async function nearestTsconfig(
|
|
207
|
+
async function nearestTsconfig(
|
|
208
|
+
from: string,
|
|
209
|
+
root: string,
|
|
210
|
+
): Promise<string | null> {
|
|
193
211
|
for (let dir = from; ; dir = dirname(dir)) {
|
|
194
212
|
const candidate = join(dir, "tsconfig.json");
|
|
195
213
|
if (await exists(candidate)) return candidate;
|
|
@@ -232,12 +250,18 @@ async function typecheck(root: string, sites: Site[]): Promise<void> {
|
|
|
232
250
|
// base config there is nothing to opt in with, so the default stays none.
|
|
233
251
|
...(base ? {} : { types: [] }),
|
|
234
252
|
},
|
|
235
|
-
files: group.flatMap((s) => [
|
|
253
|
+
files: group.flatMap((s) => [
|
|
254
|
+
relative(dir, s.path),
|
|
255
|
+
relative(dir, typesPathFor(s.path)),
|
|
256
|
+
]),
|
|
236
257
|
// `files` overrides the base's, but a base `include` survives beside it and would
|
|
237
258
|
// drag the author's whole tree in, to be checked under the worker lib.
|
|
238
259
|
include: [],
|
|
239
260
|
};
|
|
240
|
-
const configPath = join(
|
|
261
|
+
const configPath = join(
|
|
262
|
+
dir,
|
|
263
|
+
groups.size === 1 ? "tsconfig.json" : `tsconfig.${n++}.json`,
|
|
264
|
+
);
|
|
241
265
|
await write(configPath, JSON.stringify(config, null, 2));
|
|
242
266
|
await runTsc(root, configPath);
|
|
243
267
|
}
|
|
@@ -285,7 +309,10 @@ const transpile: Plugin = {
|
|
|
285
309
|
},
|
|
286
310
|
};
|
|
287
311
|
|
|
288
|
-
const BUILTIN = new Set([
|
|
312
|
+
const BUILTIN = new Set([
|
|
313
|
+
...builtinModules,
|
|
314
|
+
...builtinModules.map((m) => `node:${m}`),
|
|
315
|
+
]);
|
|
289
316
|
|
|
290
317
|
/** Resolves imports through TYPESCRIPT, using the same config the typecheck ran under, so a
|
|
291
318
|
* `paths` alias that compiles also bundles. Reimplementing `paths` here would be a second
|
|
@@ -296,15 +323,27 @@ function tsResolve(configPath: string | null): Plugin {
|
|
|
296
323
|
let options: ts.CompilerOptions = {};
|
|
297
324
|
if (configPath) {
|
|
298
325
|
const read = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
299
|
-
options = ts.parseJsonConfigFileContent(
|
|
326
|
+
options = ts.parseJsonConfigFileContent(
|
|
327
|
+
read.config ?? {},
|
|
328
|
+
ts.sys,
|
|
329
|
+
dirname(configPath),
|
|
330
|
+
).options;
|
|
300
331
|
}
|
|
301
332
|
return {
|
|
302
333
|
name: "genroc-ts-resolve",
|
|
303
334
|
resolveId(source, importer) {
|
|
304
335
|
if (!importer || BUILTIN.has(source)) return null;
|
|
305
|
-
const { resolvedModule } = ts.resolveModuleName(
|
|
306
|
-
|
|
307
|
-
|
|
336
|
+
const { resolvedModule } = ts.resolveModuleName(
|
|
337
|
+
source,
|
|
338
|
+
importer,
|
|
339
|
+
options,
|
|
340
|
+
ts.sys,
|
|
341
|
+
);
|
|
342
|
+
if (!resolvedModule || resolvedModule.isExternalLibraryImport)
|
|
343
|
+
return null;
|
|
344
|
+
return resolvedModule.resolvedFileName.endsWith(".d.ts")
|
|
345
|
+
? null
|
|
346
|
+
: resolvedModule.resolvedFileName;
|
|
308
347
|
},
|
|
309
348
|
};
|
|
310
349
|
}
|
|
@@ -330,12 +369,19 @@ async function bundle(site: Site, root: string): Promise<string> {
|
|
|
330
369
|
],
|
|
331
370
|
onwarn(warning) {
|
|
332
371
|
if (warning.code === "UNRESOLVED_IMPORT") {
|
|
333
|
-
die(
|
|
372
|
+
die(
|
|
373
|
+
`${site.path}: cannot resolve ${warning.exporter ?? "an import"} — is it installed?`,
|
|
374
|
+
);
|
|
334
375
|
}
|
|
335
376
|
},
|
|
336
|
-
}).catch((e: unknown) =>
|
|
377
|
+
}).catch((e: unknown) =>
|
|
378
|
+
die(`${site.path}: ${e instanceof Error ? e.message : String(e)}`),
|
|
379
|
+
);
|
|
337
380
|
|
|
338
|
-
const { output } = await built.generate({
|
|
381
|
+
const { output } = await built.generate({
|
|
382
|
+
format: "es",
|
|
383
|
+
inlineDynamicImports: true,
|
|
384
|
+
});
|
|
339
385
|
await built.close();
|
|
340
386
|
// Refused here rather than in the realm: the evaluator can only report it against a running
|
|
341
387
|
// instance, and the file it names is on this machine.
|
|
@@ -355,14 +401,16 @@ const stdin: string = await new Promise((resolve, reject) => {
|
|
|
355
401
|
process.stdin.on("error", reject);
|
|
356
402
|
});
|
|
357
403
|
const manifest = JSON.parse(stdin) as Manifest;
|
|
358
|
-
if (!manifest || !Array.isArray(manifest.sites))
|
|
404
|
+
if (!manifest || !Array.isArray(manifest.sites))
|
|
405
|
+
die("stdin is not a genroc resolver manifest");
|
|
359
406
|
|
|
360
407
|
// One script at two sites with different input types is a refusal, not a union: the union
|
|
361
408
|
// is sound and would typecheck a body that is wrong at one of the sites.
|
|
362
409
|
const byPath = new Map<string, Site>();
|
|
363
410
|
for (const site of manifest.sites) {
|
|
364
411
|
const seen = byPath.get(site.path);
|
|
365
|
-
const defsOf = (x: Site) =>
|
|
412
|
+
const defsOf = (x: Site) =>
|
|
413
|
+
(manifest.schemas[x.process]?.$defs ?? {}) as Record<string, Schema>;
|
|
366
414
|
if (
|
|
367
415
|
seen &&
|
|
368
416
|
JSON.stringify(scriptInput(seen, defsOf(seen))) !==
|
|
@@ -377,7 +425,10 @@ for (const site of manifest.sites) {
|
|
|
377
425
|
}
|
|
378
426
|
|
|
379
427
|
for (const site of byPath.values()) {
|
|
380
|
-
const defs = (manifest.schemas[site.process]?.$defs ?? {}) as Record<
|
|
428
|
+
const defs = (manifest.schemas[site.process]?.$defs ?? {}) as Record<
|
|
429
|
+
string,
|
|
430
|
+
Schema
|
|
431
|
+
>;
|
|
381
432
|
await write(typesPathFor(site.path), declarations(site, defs));
|
|
382
433
|
}
|
|
383
434
|
|