@genroc/eval-node 0.0.0-edge.dcfec388 → 0.0.0-edge.df9d1f1d
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 +6 -7
- package/dist/realm.js +1 -4
- package/dist/worker.js +7 -18
- package/package.json +1 -1
- package/realm.ts +1 -4
- package/worker.ts +7 -19
package/README.md
CHANGED
|
@@ -109,20 +109,19 @@ match. Retrying does not help with any of them, because the same code fails the
|
|
|
109
109
|
| code | when |
|
|
110
110
|
|---|---|
|
|
111
111
|
| `threw` | the script threw or rejected |
|
|
112
|
-
| `timeout` | it ran past
|
|
112
|
+
| `timeout` | it ran past `timeout_ms` (default 5000) |
|
|
113
113
|
| `compile_error` | the module does not parse, or has no default-export function |
|
|
114
114
|
| `nonserializable` | the return value cannot be serialized to JSON (a cycle, a `BigInt`) |
|
|
115
115
|
| `exited` | the script called `process.exit()` |
|
|
116
116
|
|
|
117
|
-
`error.data` is `{name, stack?}`. `name`
|
|
118
|
-
|
|
119
|
-
and a thrown non-`Error` is `Thrown`. The stack is mapped back to your source lines.
|
|
117
|
+
`error.data` is `{name, stack?}`. Set `e.name = "LimitExceeded"` in the script to tell your
|
|
118
|
+
own failures apart. The stack is mapped back to your source lines.
|
|
120
119
|
|
|
121
120
|
If the worker itself faults, it releases the task for another worker and reports no error.
|
|
122
121
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
Keep the task's `timeout` above `timeout_ms`. Otherwise a slow script ends with
|
|
123
|
+
`external.timeout`, and you cannot tell it from having no worker at all. The `script-node`
|
|
124
|
+
template already does this.
|
|
126
125
|
|
|
127
126
|
## Limits
|
|
128
127
|
|
package/dist/realm.js
CHANGED
|
@@ -36,10 +36,7 @@ function scriptStack(err) {
|
|
|
36
36
|
}
|
|
37
37
|
function describe(err, kind) {
|
|
38
38
|
if (err instanceof Error) {
|
|
39
|
-
|
|
40
|
-
// would report "Error"; a name the script set still wins.
|
|
41
|
-
const name = err.name === "Error" && err.constructor.name ? err.constructor.name : err.name;
|
|
42
|
-
return { kind, name, message: err.message, stack: scriptStack(err) };
|
|
39
|
+
return { kind, name: err.name, message: err.message, stack: scriptStack(err) };
|
|
43
40
|
}
|
|
44
41
|
// A script may throw a non-Error (`throw {code: "x"}`), so name/message must not assume one.
|
|
45
42
|
return { kind, name: "Thrown", message: safeText(err) };
|
package/dist/worker.js
CHANGED
|
@@ -19,9 +19,6 @@ const POLL_MS = Number(process.env.POLL_MS ?? 250);
|
|
|
19
19
|
// should return its task quickly rather than holding it for the whole budget.
|
|
20
20
|
const LEASE_MS = Number(process.env.LEASE_MS ?? 30_000);
|
|
21
21
|
const RENEW_MS = Math.max(1_000, Math.floor(LEASE_MS / 3));
|
|
22
|
-
// The answer has to land before the task's deadline fires, or a script that overran surfaces as
|
|
23
|
-
// external.timeout ("nobody answered") rather than as its own `timeout`.
|
|
24
|
-
const ANSWER_MARGIN_MS = 250;
|
|
25
22
|
const PROCESS_FILTER = process.env.PROCESS ?? "";
|
|
26
23
|
const TASK_FILTER = process.env.TASK ?? "";
|
|
27
24
|
// Large values arrive as refs; a bundle is one object per definition version, fetched once.
|
|
@@ -97,13 +94,17 @@ async function call(path, body) {
|
|
|
97
94
|
}
|
|
98
95
|
/** A task input that is not an EvalRequest is the definition's fault, reported as compile_error:
|
|
99
96
|
* the nearest permanent kind, since no retry fixes the definition. */
|
|
100
|
-
function asEvalRequest(input
|
|
97
|
+
function asEvalRequest(input) {
|
|
101
98
|
if (typeof input !== "object" || input === null)
|
|
102
99
|
return "the task input is not an object";
|
|
103
100
|
const r = input;
|
|
104
101
|
if (typeof r.code !== "string")
|
|
105
102
|
return "the task input has no `code` string";
|
|
106
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
code: r.code,
|
|
105
|
+
input: r.input,
|
|
106
|
+
timeout_ms: typeof r.timeout_ms === "number" ? r.timeout_ms : undefined,
|
|
107
|
+
};
|
|
107
108
|
}
|
|
108
109
|
const inFlight = new Map();
|
|
109
110
|
let running = true;
|
|
@@ -160,9 +161,6 @@ async function answer(token, outcome) {
|
|
|
160
161
|
await release(token);
|
|
161
162
|
}
|
|
162
163
|
async function run(job, signal) {
|
|
163
|
-
// The task's own timeout is the script's budget. Taken on arrival, because fetching the
|
|
164
|
-
// objects below spends the same deadline.
|
|
165
|
-
const due = job.deadline_in_ms === undefined ? undefined : performance.now() + job.deadline_in_ms;
|
|
166
164
|
let resolved;
|
|
167
165
|
try {
|
|
168
166
|
resolved = await resolveObjects(job);
|
|
@@ -174,16 +172,7 @@ async function run(job, signal) {
|
|
|
174
172
|
await release(job.token);
|
|
175
173
|
return;
|
|
176
174
|
}
|
|
177
|
-
|
|
178
|
-
if (due !== undefined) {
|
|
179
|
-
timeoutMs = Math.floor(due - performance.now() - ANSWER_MARGIN_MS);
|
|
180
|
-
if (timeoutMs <= 0) {
|
|
181
|
-
// Too late to run anything: the deadline fires as external.timeout, which is the truth.
|
|
182
|
-
await release(job.token);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const req = asEvalRequest(resolved, timeoutMs);
|
|
175
|
+
const req = asEvalRequest(resolved);
|
|
187
176
|
if (typeof req === "string") {
|
|
188
177
|
await answer(job.token, {
|
|
189
178
|
error: { code: "compile_error", message: req, data: { name: "BadTaskInput" } },
|
package/package.json
CHANGED
package/realm.ts
CHANGED
|
@@ -35,10 +35,7 @@ function scriptStack(err: unknown): string | undefined {
|
|
|
35
35
|
|
|
36
36
|
function describe(err: unknown, kind: FailureKind): EvalFailure {
|
|
37
37
|
if (err instanceof Error) {
|
|
38
|
-
|
|
39
|
-
// would report "Error"; a name the script set still wins.
|
|
40
|
-
const name = err.name === "Error" && err.constructor.name ? err.constructor.name : err.name;
|
|
41
|
-
return { kind, name, message: err.message, stack: scriptStack(err) };
|
|
38
|
+
return { kind, name: err.name, message: err.message, stack: scriptStack(err) };
|
|
42
39
|
}
|
|
43
40
|
// A script may throw a non-Error (`throw {code: "x"}`), so name/message must not assume one.
|
|
44
41
|
return { kind, name: "Thrown", message: safeText(err) };
|
package/worker.ts
CHANGED
|
@@ -22,9 +22,6 @@ const POLL_MS = Number(process.env.POLL_MS ?? 250);
|
|
|
22
22
|
// should return its task quickly rather than holding it for the whole budget.
|
|
23
23
|
const LEASE_MS = Number(process.env.LEASE_MS ?? 30_000);
|
|
24
24
|
const RENEW_MS = Math.max(1_000, Math.floor(LEASE_MS / 3));
|
|
25
|
-
// The answer has to land before the task's deadline fires, or a script that overran surfaces as
|
|
26
|
-
// external.timeout ("nobody answered") rather than as its own `timeout`.
|
|
27
|
-
const ANSWER_MARGIN_MS = 250;
|
|
28
25
|
const PROCESS_FILTER = process.env.PROCESS ?? "";
|
|
29
26
|
const TASK_FILTER = process.env.TASK ?? "";
|
|
30
27
|
|
|
@@ -37,7 +34,6 @@ type QueueTask = {
|
|
|
37
34
|
external_input: unknown;
|
|
38
35
|
objects?: ObjectEntry[];
|
|
39
36
|
raises?: Record<string, unknown>;
|
|
40
|
-
deadline_in_ms?: number;
|
|
41
37
|
};
|
|
42
38
|
|
|
43
39
|
// Large values arrive as refs; a bundle is one object per definition version, fetched once.
|
|
@@ -110,11 +106,15 @@ async function call(path: string, body: unknown): Promise<{ ok: boolean; status:
|
|
|
110
106
|
|
|
111
107
|
/** A task input that is not an EvalRequest is the definition's fault, reported as compile_error:
|
|
112
108
|
* the nearest permanent kind, since no retry fixes the definition. */
|
|
113
|
-
function asEvalRequest(input: unknown
|
|
109
|
+
function asEvalRequest(input: unknown): EvalRequest | string {
|
|
114
110
|
if (typeof input !== "object" || input === null) return "the task input is not an object";
|
|
115
111
|
const r = input as Record<string, unknown>;
|
|
116
112
|
if (typeof r.code !== "string") return "the task input has no `code` string";
|
|
117
|
-
return {
|
|
113
|
+
return {
|
|
114
|
+
code: r.code,
|
|
115
|
+
input: r.input,
|
|
116
|
+
timeout_ms: typeof r.timeout_ms === "number" ? r.timeout_ms : undefined,
|
|
117
|
+
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** genroc cannot call us, so a cancellation arrives on our own heartbeat and reaches the
|
|
@@ -183,9 +183,6 @@ async function answer(token: string, outcome: Record<string, unknown>): Promise<
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
async function run(job: QueueTask, signal: AbortSignal): Promise<void> {
|
|
186
|
-
// The task's own timeout is the script's budget. Taken on arrival, because fetching the
|
|
187
|
-
// objects below spends the same deadline.
|
|
188
|
-
const due = job.deadline_in_ms === undefined ? undefined : performance.now() + job.deadline_in_ms;
|
|
189
186
|
let resolved: unknown;
|
|
190
187
|
try {
|
|
191
188
|
resolved = await resolveObjects(job);
|
|
@@ -196,16 +193,7 @@ async function run(job: QueueTask, signal: AbortSignal): Promise<void> {
|
|
|
196
193
|
await release(job.token);
|
|
197
194
|
return;
|
|
198
195
|
}
|
|
199
|
-
|
|
200
|
-
if (due !== undefined) {
|
|
201
|
-
timeoutMs = Math.floor(due - performance.now() - ANSWER_MARGIN_MS);
|
|
202
|
-
if (timeoutMs <= 0) {
|
|
203
|
-
// Too late to run anything: the deadline fires as external.timeout, which is the truth.
|
|
204
|
-
await release(job.token);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const req = asEvalRequest(resolved, timeoutMs);
|
|
196
|
+
const req = asEvalRequest(resolved);
|
|
209
197
|
if (typeof req === "string") {
|
|
210
198
|
await answer(job.token, {
|
|
211
199
|
error: { code: "compile_error", message: req, data: { name: "BadTaskInput" } },
|