@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 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 the task's `timeout` (5000 ms when the task has none) |
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` is the error's class name, so throwing your own
118
- `class LimitExceeded extends Error {}` tells your failures apart; setting `e.name` overrides it,
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
- The task's `timeout` is the script's budget, waiting in the queue included. The worker stops
124
- the script just before it and answers `timeout`, so `external.timeout` means no worker answered
125
- at all. With `script-node`, set it through the `timeout_ms` input.
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
- // A subclass inherits `name` from Error.prototype, so `class LimitExceeded extends Error {}`
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, timeoutMs) {
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 { code: r.code, input: r.input, timeout_ms: timeoutMs };
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
- let timeoutMs;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genroc/eval-node",
3
- "version": "0.0.0-edge.dcfec388",
3
+ "version": "0.0.0-edge.df9d1f1d",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
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
- // A subclass inherits `name` from Error.prototype, so `class LimitExceeded extends Error {}`
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, timeoutMs: number | undefined): EvalRequest | string {
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 { code: r.code, input: r.input, timeout_ms: timeoutMs };
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
- let timeoutMs: number | undefined;
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" } },