@opencode/codemode 2.0.0 → 2.0.1

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/dist/data.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export * as Data from "./data.js";
2
- import { ownEntries, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, set, } from "./interpreter/objects.js";
2
+ import { get, ownEntries, parseArrayIndex, ProgramArray, ProgramError, ProgramFunction, ProgramObject, set, } from "./interpreter/objects.js";
3
3
  import { Values } from "./values.js";
4
4
  const MAX_VALUE_DEPTH = 32;
5
5
  export class ToolRuntimeError extends Error {
@@ -102,6 +102,11 @@ const copy = (value, label, mode, depth, seen) => {
102
102
  }
103
103
  if (value instanceof ProgramObject) {
104
104
  const copied = {};
105
+ // Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS.
106
+ if (value instanceof ProgramError) {
107
+ define(copied, "name", copy(get(value, "name"), label, mode, depth + 1, seen));
108
+ define(copied, "message", copy(get(value, "message"), label, mode, depth + 1, seen));
109
+ }
105
110
  for (const [key, item] of ownEntries(value)) {
106
111
  const next = copy(item, label, mode, depth + 1, seen);
107
112
  if (next === undefined && mode === "json")
@@ -1,7 +1,10 @@
1
1
  import type { Diagnostic } from "../codemode.js";
2
2
  import { HostFunction } from "./host.js";
3
+ import { type ErrorType } from "./intrinsics.js";
4
+ import { ProgramError } from "./objects.js";
3
5
  import { type Runner } from "./runner.js";
4
6
  export declare const normalizeError: (error: unknown) => Diagnostic;
5
- export declare const caughtErrorValue: (thrown: unknown) => unknown;
7
+ export declare const caughtErrorValue: <R>(runner: Runner<R>, thrown: unknown) => unknown;
8
+ export declare const createAggregateErrorValue: <R>(runner: Runner<R>, errors: Array<unknown>, message: string) => ProgramError;
6
9
  /** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
7
- export declare const errorGlobal: <R>(name: string, runner: Runner<R>) => HostFunction<R>;
10
+ export declare const errorGlobal: <R>(type: ErrorType, runner: Runner<R>) => HostFunction<R>;
@@ -4,9 +4,10 @@ import { toData, ToolRuntimeError } from "../data.js";
4
4
  import { formatLocation, InterpreterRuntimeError, ProgramThrow, sourceLocation } from "./model.js";
5
5
  import { containsRuntimeReference } from "./references.js";
6
6
  import { HostFunction } from "./host.js";
7
- import { get, ProgramError, ProgramObject } from "./objects.js";
7
+ import { createErrorValue, isErrorType } from "./intrinsics.js";
8
+ import { get, hasPrototype, ProgramArray, ProgramError, ProgramObject, set } from "./objects.js";
8
9
  import {} from "./runner.js";
9
- import { coerceToString, createAggregateErrorValue, createErrorValue, errorBrandName, errorConstructors, } from "../stdlib/value.js";
10
+ import { coerceToString } from "../stdlib/value.js";
10
11
  export const normalizeError = (error) => {
11
12
  if (error instanceof InterpreterRuntimeError) {
12
13
  return {
@@ -66,41 +67,46 @@ export const normalizeError = (error) => {
66
67
  message: String(error),
67
68
  };
68
69
  };
69
- export const caughtErrorValue = (thrown) => {
70
+ export const caughtErrorValue = (runner, thrown) => {
70
71
  if (thrown instanceof ProgramThrow)
71
72
  return thrown.value;
73
+ const prototypes = runner.intrinsics.errors;
72
74
  if (thrown instanceof InterpreterRuntimeError)
73
- return createErrorValue(thrown.errorName, thrown.message);
74
- const name = thrown instanceof Error && errorConstructors.has(thrown.name) ? thrown.name : "Error";
75
- return createErrorValue(name, normalizeError(thrown).message);
75
+ return createErrorValue(prototypes[thrown.type], thrown.message);
76
+ const type = thrown instanceof Error && isErrorType(thrown.name) ? thrown.name : "Error";
77
+ return createErrorValue(prototypes[type], normalizeError(thrown).message);
78
+ };
79
+ export const createAggregateErrorValue = (runner, errors, message) => {
80
+ const value = createErrorValue(runner.intrinsics.errors.AggregateError, message);
81
+ set(value, "errors", new ProgramArray(errors));
82
+ return value;
76
83
  };
77
- const constructErrorValue = (name, args) => createErrorValue(name, args[0] === undefined ? "" : coerceToString(args[0]));
78
84
  const constructAggregateErrorValue = (runner, args, node) => Effect.gen(function* () {
79
85
  const cursor = yield* runner.syncIterator(args[0], node);
80
86
  if (cursor === undefined) {
81
- throw new InterpreterRuntimeError("new AggregateError(...) expects a synchronous iterable of errors.", node).as("TypeError");
87
+ throw new InterpreterRuntimeError("new AggregateError(...) expects a synchronous iterable of errors.", node);
82
88
  }
83
89
  const errors = [];
84
90
  while (true) {
85
91
  const step = yield* cursor.next;
86
92
  if (step.done) {
87
- return createAggregateErrorValue(errors, args[1] === undefined ? "" : coerceToString(args[1]));
93
+ return createAggregateErrorValue(runner, errors, args[1] === undefined ? "" : coerceToString(args[1]));
88
94
  }
89
95
  errors.push(step.value);
90
96
  }
91
97
  });
92
98
  /** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
93
- export const errorGlobal = (name, runner) => {
94
- const construct = (args, node) => name === "AggregateError"
99
+ export const errorGlobal = (type, runner) => {
100
+ const prototype = runner.intrinsics.errors[type];
101
+ const construct = (args, node) => type === "AggregateError"
95
102
  ? constructAggregateErrorValue(runner, args, node)
96
- : Effect.sync(() => constructErrorValue(name, args));
97
- return new HostFunction({
98
- name,
103
+ : Effect.sync(() => createErrorValue(prototype, args[0] === undefined ? undefined : coerceToString(args[0])));
104
+ const fn = new HostFunction({
105
+ name: type,
99
106
  call: construct,
100
107
  construct,
101
- instanceOf: (value) => {
102
- const brand = errorBrandName(value);
103
- return brand !== undefined && (name === "Error" || brand === name);
104
- },
108
+ instanceOf: (value) => hasPrototype(value, prototype),
105
109
  });
110
+ set(prototype, "constructor", fn);
111
+ return fn;
106
112
  };
@@ -10,17 +10,18 @@ import { objectGlobal } from "../stdlib/object.js";
10
10
  import { regexpGlobal } from "../stdlib/regexp.js";
11
11
  import { stringGlobal } from "../stdlib/string.js";
12
12
  import { uriGlobal, urlGlobal, urlSearchParamsGlobal } from "../stdlib/url.js";
13
- import { coercion, errorConstructors } from "../stdlib/value.js";
13
+ import { coercion } from "../stdlib/value.js";
14
14
  import { atobGlobal, btoaGlobal, cryptoGlobal } from "../stdlib/web.js";
15
15
  import { ToolReference } from "../tool-runtime.js";
16
16
  import { errorGlobal } from "./errors.js";
17
+ import { errorTypes } from "./intrinsics.js";
17
18
  import { HostFunction } from "./host.js";
18
19
  import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "./model.js";
19
20
  import { promiseGlobal } from "./promises.js";
20
21
  const symbolGlobal = new HostFunction({
21
22
  name: "Symbol",
22
23
  call: (_, node) => Effect.sync(() => {
23
- throw new InterpreterRuntimeError("Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node).as("TypeError");
24
+ throw new InterpreterRuntimeError("Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node);
24
25
  }),
25
26
  callback: false,
26
27
  members: { asyncIterator: AsyncIteratorSymbol, iterator: IteratorSymbol },
@@ -59,5 +60,5 @@ export const globals = (host) => [
59
60
  ["atob", atobGlobal],
60
61
  ["btoa", btoaGlobal],
61
62
  ["crypto", cryptoGlobal],
62
- ...[...errorConstructors].map((name) => [name, errorGlobal(name, host.runner)]),
63
+ ...errorTypes.map((type) => [type, errorGlobal(type, host.runner)]),
63
64
  ];
@@ -40,5 +40,5 @@ export const syncCall = (impl) => (args, node) => Effect.sync(() => impl(args, n
40
40
  export const sync = (name, impl, options = {}) => new HostFunction({ name, call: syncCall(impl), ...options });
41
41
  /** The `call` of a constructor that JS requires to be invoked with `new`. */
42
42
  export const requiresNew = (name) => (_, node) => Effect.sync(() => {
43
- throw new InterpreterRuntimeError(`Constructor ${name} requires 'new'.`, node).as("TypeError");
43
+ throw new InterpreterRuntimeError(`Constructor ${name} requires 'new'.`, node);
44
44
  });
@@ -0,0 +1,10 @@
1
+ import { ProgramError, ProgramObject } from "./objects.js";
2
+ export declare const errorTypes: readonly ["Error", "TypeError", "RangeError", "SyntaxError", "ReferenceError", "EvalError", "URIError", "AggregateError"];
3
+ export type ErrorType = (typeof errorTypes)[number];
4
+ export declare const isErrorType: (name: string) => name is ErrorType;
5
+ /** The built-in prototype objects of one runtime. Constructors attach themselves as `constructor` when created. */
6
+ export type Intrinsics = {
7
+ readonly errors: Readonly<Record<ErrorType, ProgramObject>>;
8
+ };
9
+ export declare const createErrorValue: (prototype: ProgramObject, message: string | undefined) => ProgramError;
10
+ export declare const createIntrinsics: () => Intrinsics;
@@ -0,0 +1,41 @@
1
+ import { ProgramError, ProgramObject, set } from "./objects.js";
2
+ export const errorTypes = [
3
+ "Error",
4
+ "TypeError",
5
+ "RangeError",
6
+ "SyntaxError",
7
+ "ReferenceError",
8
+ "EvalError",
9
+ "URIError",
10
+ "AggregateError",
11
+ ];
12
+ export const isErrorType = (name) => errorTypes.includes(name);
13
+ export const createErrorValue = (prototype, message) => {
14
+ const value = new ProgramError(prototype);
15
+ if (message !== undefined)
16
+ set(value, "message", message);
17
+ return value;
18
+ };
19
+ export const createIntrinsics = () => {
20
+ const error = new ProgramObject();
21
+ set(error, "name", "Error");
22
+ set(error, "message", "");
23
+ const derived = (type) => {
24
+ const proto = new ProgramObject(error);
25
+ set(proto, "name", type);
26
+ set(proto, "message", "");
27
+ return proto;
28
+ };
29
+ return {
30
+ errors: {
31
+ Error: error,
32
+ TypeError: derived("TypeError"),
33
+ RangeError: derived("RangeError"),
34
+ SyntaxError: derived("SyntaxError"),
35
+ ReferenceError: derived("ReferenceError"),
36
+ EvalError: derived("EvalError"),
37
+ URIError: derived("URIError"),
38
+ AggregateError: derived("AggregateError"),
39
+ },
40
+ };
41
+ };
@@ -7,7 +7,7 @@ import { invokeURLMethod, uriArgument } from "../stdlib/url.js";
7
7
  import { coerceToNumber, coerceToString } from "../stdlib/value.js";
8
8
  import { compareText } from "../tool-runtime.js";
9
9
  import { Values } from "../values.js";
10
- import { IntrinsicReference, InterpreterRuntimeError } from "./model.js";
10
+ import { InterpreterRuntimeError, IntrinsicReference, rangeError } from "./model.js";
11
11
  import { get, ProgramArray, ProgramObject, record } from "./objects.js";
12
12
  import { containsOpaqueReference, rejectCircularInsertion, typeofValue } from "./references.js";
13
13
  import { applyCollectionCallback, isSupportedCallback, toPrimitive } from "./runner.js";
@@ -79,7 +79,7 @@ const invokeStringMethod = (value, name, args, node) => {
79
79
  const optStr = (index) => (args[index] === undefined ? undefined : str(index));
80
80
  const rejectRegex = () => {
81
81
  if (args[0] instanceof Values.RegExp) {
82
- throw new InterpreterRuntimeError(`String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`, node).as("TypeError");
82
+ throw new InterpreterRuntimeError(`String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`, node);
83
83
  }
84
84
  };
85
85
  let result;
@@ -111,7 +111,7 @@ const invokeStringMethod = (value, name, args, node) => {
111
111
  result = value.normalize(form);
112
112
  }
113
113
  catch {
114
- throw new InterpreterRuntimeError(`String.normalize expects the form "NFC", "NFD", "NFKC", or "NFKD" (got ${JSON.stringify(form)}).`, node).as("RangeError");
114
+ throw rangeError(`String.normalize expects the form "NFC", "NFD", "NFKC", or "NFKD" (got ${JSON.stringify(form)}).`, node);
115
115
  }
116
116
  break;
117
117
  }
@@ -194,7 +194,7 @@ const invokeStringMethod = (value, name, args, node) => {
194
194
  case "repeat": {
195
195
  const count = num(0);
196
196
  if (!Number.isFinite(count) || count < 0)
197
- throw new InterpreterRuntimeError("String.repeat expects a finite non-negative count.", node).as("RangeError");
197
+ throw rangeError("String.repeat expects a finite non-negative count.", node);
198
198
  result = value.repeat(count);
199
199
  break;
200
200
  }
@@ -463,17 +463,17 @@ const loadSetRecord = (runner, source, name, node) => {
463
463
  });
464
464
  }
465
465
  if (!(source instanceof ProgramObject)) {
466
- throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError");
466
+ throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node);
467
467
  }
468
468
  return Effect.gen(function* () {
469
469
  const size = yield* coerceNumericArgument(runner, get(source, "size"), node);
470
470
  if (Number.isNaN(size)) {
471
- throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as("TypeError");
471
+ throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node);
472
472
  }
473
473
  const has = get(source, "has");
474
474
  const keys = get(source, "keys");
475
475
  if (!isSupportedCallback(has) || !isSupportedCallback(keys)) {
476
- throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError");
476
+ throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node);
477
477
  }
478
478
  return {
479
479
  size: Math.max(Math.trunc(size), 0),
@@ -481,7 +481,7 @@ const loadSetRecord = (runner, source, name, node) => {
481
481
  keys: () => Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => {
482
482
  if (result instanceof ProgramArray)
483
483
  return Effect.succeed(result.items);
484
- throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError");
484
+ throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node);
485
485
  }),
486
486
  };
487
487
  });
@@ -490,7 +490,7 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
490
490
  const arg = (index) => uriArgument(args[index], `URLSearchParams.${name} argument ${index + 1}`);
491
491
  const requireArgs = (count) => {
492
492
  if (args.length < count) {
493
- throw new InterpreterRuntimeError(`URLSearchParams.${name} requires ${count} argument${count === 1 ? "" : "s"}.`, node).as("TypeError");
493
+ throw new InterpreterRuntimeError(`URLSearchParams.${name} requires ${count} argument${count === 1 ? "" : "s"}.`, node);
494
494
  }
495
495
  };
496
496
  switch (name) {
@@ -614,7 +614,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
614
614
  const index = optNumber(args[0], "index") ?? 0;
615
615
  const resolved = index < 0 ? target.length + index : index;
616
616
  if (resolved < 0 || resolved >= target.length) {
617
- throw new InterpreterRuntimeError("Array.with index is out of range.", node);
617
+ throw rangeError("Array.with index is out of range.", node);
618
618
  }
619
619
  const copied = [...target];
620
620
  copied[resolved] = args[1];
@@ -759,7 +759,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
759
759
  while (start < length && !(start in target))
760
760
  start += 1;
761
761
  if (start === length)
762
- throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node).as("TypeError");
762
+ throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node);
763
763
  accumulator = target[start];
764
764
  start += 1;
765
765
  }
@@ -777,7 +777,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
777
777
  while (start >= 0 && !(start in target))
778
778
  start -= 1;
779
779
  if (start < 0)
780
- throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node).as("TypeError");
780
+ throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node);
781
781
  accumulator = target[start];
782
782
  start -= 1;
783
783
  }
@@ -1,4 +1,5 @@
1
1
  import type { Node } from "acorn";
2
+ import type { ErrorType } from "./intrinsics.js";
2
3
  import type { Effect } from "effect";
3
4
  import type { DiagnosticKind } from "../codemode.js";
4
5
  import type { ProgramObject } from "./objects.js";
@@ -67,11 +68,17 @@ export declare const OptionalShortCircuit: unique symbol;
67
68
  export declare class InterpreterRuntimeError extends Error {
68
69
  readonly kind: DiagnosticKind;
69
70
  readonly suggestions?: ReadonlyArray<string> | undefined;
71
+ /** The JS error class a program sees when it catches this failure. */
72
+ readonly type: ErrorType;
70
73
  readonly node?: AstNode;
71
- errorName: string;
72
- constructor(message: string, node?: AstNode, kind?: DiagnosticKind, suggestions?: ReadonlyArray<string> | undefined);
73
- as(errorName: string): this;
74
+ constructor(message: string, node?: AstNode, kind?: DiagnosticKind, suggestions?: ReadonlyArray<string> | undefined,
75
+ /** The JS error class a program sees when it catches this failure. */
76
+ type?: ErrorType);
74
77
  }
78
+ export declare const rangeError: (message: string, node?: AstNode) => InterpreterRuntimeError;
79
+ export declare const referenceError: (message: string, node?: AstNode) => InterpreterRuntimeError;
80
+ export declare const syntaxError: (message: string, node?: AstNode) => InterpreterRuntimeError;
81
+ export declare const uriError: (message: string, node?: AstNode) => InterpreterRuntimeError;
75
82
  export declare const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
76
83
  export declare const unsupportedSyntax: (kind: string, node: AstNode) => InterpreterRuntimeError;
77
84
  export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
@@ -55,24 +55,28 @@ export const OptionalShortCircuit = Symbol("codemode.optional-short-circuit");
55
55
  export class InterpreterRuntimeError extends Error {
56
56
  kind;
57
57
  suggestions;
58
+ type;
58
59
  node;
59
- errorName = "Error";
60
- constructor(message, node, kind = "ExecutionFailure", suggestions) {
60
+ constructor(message, node, kind = "ExecutionFailure", suggestions,
61
+ /** The JS error class a program sees when it catches this failure. */
62
+ type = "TypeError") {
61
63
  super(message);
62
64
  this.kind = kind;
63
65
  this.suggestions = suggestions;
66
+ this.type = type;
64
67
  this.name = "InterpreterRuntimeError";
65
68
  if (node)
66
69
  this.node = node;
67
70
  }
68
- as(errorName) {
69
- this.errorName = errorName;
70
- return this;
71
- }
72
71
  }
72
+ const failure = (type) => (message, node) => new InterpreterRuntimeError(message, node, "ExecutionFailure", undefined, type);
73
+ export const rangeError = failure("RangeError");
74
+ export const referenceError = failure("ReferenceError");
75
+ export const syntaxError = failure("SyntaxError");
76
+ export const uriError = failure("URIError");
73
77
  // Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
74
78
  export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
75
- export const unsupportedSyntax = (kind, node) => new InterpreterRuntimeError(`Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage]);
79
+ export const unsupportedSyntax = (kind, node) => new InterpreterRuntimeError(`Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage], "SyntaxError");
76
80
  export const isRecord = (value) => typeof value === "object" && value !== null;
77
81
  export const sourceLocation = (node) => ({
78
82
  line: Math.max(1, (node.loc?.start.line ?? 2) - 1),
@@ -10,9 +10,8 @@ export declare class ProgramArray extends ProgramObject {
10
10
  readonly items: Array<unknown>;
11
11
  constructor(items?: Array<unknown>);
12
12
  }
13
+ /** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */
13
14
  export declare class ProgramError extends ProgramObject {
14
- readonly errorName: string;
15
- constructor(errorName: string);
16
15
  }
17
16
  export declare class ProgramFunction extends ProgramObject {
18
17
  readonly name: string;
@@ -28,6 +27,7 @@ export declare const parseArrayIndex: (key: string | number) => number | undefin
28
27
  export declare const hasOwn: (target: ProgramObject, key: PropertyKey) => boolean;
29
28
  export declare const getOwn: (target: ProgramObject, key: PropertyKey) => unknown;
30
29
  export declare const get: (target: ProgramObject, key: PropertyKey) => unknown;
30
+ export declare const hasPrototype: (value: unknown, proto: ProgramObject) => boolean;
31
31
  export declare const has: (target: ProgramObject, key: PropertyKey) => boolean;
32
32
  export declare const set: (target: ProgramObject, key: PropertyKey, value: unknown) => boolean;
33
33
  export declare const remove: (target: ProgramObject, key: PropertyKey) => boolean;
@@ -14,12 +14,8 @@ export class ProgramArray extends ProgramObject {
14
14
  this.items = items;
15
15
  }
16
16
  }
17
+ /** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */
17
18
  export class ProgramError extends ProgramObject {
18
- errorName;
19
- constructor(errorName) {
20
- super();
21
- this.errorName = errorName;
22
- }
23
19
  }
24
20
  export class ProgramFunction extends ProgramObject {
25
21
  name;
@@ -81,6 +77,13 @@ export const get = (target, key) => {
81
77
  }
82
78
  return undefined;
83
79
  };
80
+ export const hasPrototype = (value, proto) => {
81
+ for (let current = value instanceof ProgramObject ? value.proto : null; current !== null; current = current.proto) {
82
+ if (current === proto)
83
+ return true;
84
+ }
85
+ return false;
86
+ };
84
87
  export const has = (target, key) => {
85
88
  for (let current = target; current !== null; current = current.proto) {
86
89
  if (hasOwn(current, key))
@@ -2,9 +2,8 @@ import { Cause, Deferred, Effect, Exit, Fiber, Scope } from "effect";
2
2
  import { InterpreterRuntimeError, ProgramThrow, PromiseInstanceMethodReference } from "./model.js";
3
3
  import { get, ProgramArray, ProgramFunction, ProgramObject, record } from "./objects.js";
4
4
  import { HostFunction, requiresNew, sync } from "./host.js";
5
- import { caughtErrorValue, normalizeError } from "./errors.js";
5
+ import { caughtErrorValue, createAggregateErrorValue, normalizeError } from "./errors.js";
6
6
  import { typeofValue } from "./references.js";
7
- import { createAggregateErrorValue } from "../stdlib/value.js";
8
7
  import { Values } from "../values.js";
9
8
  import { applyCollectionCallback, isSupportedCallback } from "./runner.js";
10
9
  // A `resolve`/`reject` handed to an executor or thenable: calling it settles the capability.
@@ -83,7 +82,7 @@ export class PromiseRuntime {
83
82
  });
84
83
  }
85
84
  }
86
- export const selfResolutionError = (node) => new InterpreterRuntimeError("Chaining cycle detected: a promise cannot resolve with itself.", node).as("TypeError");
85
+ export const selfResolutionError = (node) => new InterpreterRuntimeError("Chaining cycle detected: a promise cannot resolve with itself.", node);
87
86
  export const resolvePromiseValue = (runner, value, node, own) => {
88
87
  if (own?.promise !== undefined && value === own.promise)
89
88
  return Effect.fail(selfResolutionError(node));
@@ -125,7 +124,7 @@ const invokePromiseMethod = (runner, promises, name, args, node) => {
125
124
  return promises.create(Effect.gen(function* () {
126
125
  const cursor = yield* runner.syncIterator(args[0], node);
127
126
  if (cursor === undefined) {
128
- throw new InterpreterRuntimeError(`Promise.${name} expects an array or other synchronous iterable.`, node).as("TypeError");
127
+ throw new InterpreterRuntimeError(`Promise.${name} expects an array or other synchronous iterable.`, node);
129
128
  }
130
129
  const items = [];
131
130
  while (true) {
@@ -149,7 +148,7 @@ const invokePromiseMethod = (runner, promises, name, args, node) => {
149
148
  }
150
149
  if (Cause.hasInterruptsOnly(exit.cause))
151
150
  return yield* Effect.failCause(exit.cause);
152
- outcomes.push(record({ status: "rejected", reason: caughtErrorValue(Cause.squash(exit.cause)) }));
151
+ outcomes.push(record({ status: "rejected", reason: caughtErrorValue(runner, Cause.squash(exit.cause)) }));
153
152
  }
154
153
  yield* Effect.yieldNow;
155
154
  return new ProgramArray(outcomes);
@@ -165,9 +164,9 @@ const invokePromiseMethod = (runner, promises, name, args, node) => {
165
164
  return Effect.fail(new PromiseAnyFulfilled(exit.value));
166
165
  if (Cause.hasInterruptsOnly(exit.cause))
167
166
  return Effect.failCause(exit.cause);
168
- return Effect.succeed(caughtErrorValue(Cause.squash(exit.cause)));
167
+ return Effect.succeed(caughtErrorValue(runner, Cause.squash(exit.cause)));
169
168
  }));
170
- return yield* settleAfterTurn(Effect.all(flipped, { concurrency: "unbounded" }).pipe(Effect.flatMap((reasons) => Effect.fail(new ProgramThrow(createAggregateErrorValue(reasons, "All promises were rejected")))), Effect.catch((error) => error instanceof PromiseAnyFulfilled ? Effect.succeed(error.value) : Effect.fail(error))));
169
+ return yield* settleAfterTurn(Effect.all(flipped, { concurrency: "unbounded" }).pipe(Effect.flatMap((reasons) => Effect.fail(new ProgramThrow(createAggregateErrorValue(runner, reasons, "All promises were rejected")))), Effect.catch((error) => error instanceof PromiseAnyFulfilled ? Effect.succeed(error.value) : Effect.fail(error))));
171
170
  }));
172
171
  };
173
172
  export const invokePromiseInstanceMethod = (runner, promises, ref, args, node) => {
@@ -182,7 +181,7 @@ export const invokePromiseInstanceMethod = (runner, promises, ref, args, node) =
182
181
  };
183
182
  const constructPromise = (runner, promises, executor, node) => {
184
183
  if (!(executor instanceof ProgramFunction)) {
185
- throw new InterpreterRuntimeError("new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).", node).as("TypeError");
184
+ throw new InterpreterRuntimeError("new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).", node);
186
185
  }
187
186
  return Effect.gen(function* () {
188
187
  const deferred = Deferred.makeUnsafe();
@@ -228,7 +227,7 @@ const chainReaction = (runner, promises, source, onFulfilled, onRejected, method
228
227
  const handler = Exit.isSuccess(exit) ? onFulfilled : onRejected;
229
228
  if (handler === undefined)
230
229
  return yield* exit;
231
- const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(Cause.squash(exit.cause));
230
+ const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(runner, Cause.squash(exit.cause));
232
231
  const result = yield* applyCollectionCallback(runner, handler, method, node)([input]);
233
232
  return yield* resolvePromiseValue(runner, result, node, self);
234
233
  }));
@@ -2,6 +2,7 @@ import { Effect } from "effect";
2
2
  import { Values } from "../values.js";
3
3
  import { HostFunction } from "./host.js";
4
4
  import { type AstNode, IntrinsicReference } from "./model.js";
5
+ import type { Intrinsics } from "./intrinsics.js";
5
6
  import { ProgramFunction } from "./objects.js";
6
7
  export type IteratorCursor<R> = {
7
8
  readonly next: Effect.Effect<{
@@ -10,12 +11,13 @@ export type IteratorCursor<R> = {
10
11
  }, unknown, R>;
11
12
  readonly close: Effect.Effect<void, unknown, R>;
12
13
  };
13
- /** Everything a host function needs to call back into the program. */
14
+ /** Everything a host function needs from the realm: calling back into the program and its intrinsic objects. */
14
15
  export type Runner<R> = {
15
16
  readonly invokeFunction: (fn: ProgramFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>;
16
17
  readonly invokeCallable: (callable: unknown, args: Array<unknown>, node: AstNode) => Effect.Effect<unknown, unknown, R>;
17
18
  readonly settlePromise: (promise: Values.Promise) => Effect.Effect<unknown, unknown, never>;
18
19
  readonly syncIterator: (value: unknown, node: AstNode) => Effect.Effect<IteratorCursor<R> | undefined, unknown, R>;
20
+ readonly intrinsics: Intrinsics;
19
21
  };
20
22
  export declare const preserveConsumerError: <A, R>(cursor: IteratorCursor<R>, effect: Effect.Effect<A, unknown, R>) => Effect.Effect<A, unknown, R>;
21
23
  export declare const toPrimitive: <R>(runner: Runner<R>, value: unknown, hint: "number" | "string", node: AstNode) => Effect.Effect<unknown, unknown, R>;
@@ -28,7 +28,7 @@ export const toPrimitive = (runner, value, hint, node) => {
28
28
  if (result === null || (typeof result !== "object" && typeof result !== "function"))
29
29
  return result;
30
30
  }
31
- throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError");
31
+ throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node);
32
32
  });
33
33
  };
34
34
  export const isSupportedCallback = (value) => value instanceof ProgramFunction ||
@@ -39,7 +39,7 @@ export const applyCollectionCallback = (runner, callback, name, node) => {
39
39
  if (typeofValue(callback) === "function") {
40
40
  throw new InterpreterRuntimeError(`${name} cannot use this callable as a callback; wrap it in an arrow function, e.g. (value) => tools.ns.tool(value).`, node);
41
41
  }
42
- throw new InterpreterRuntimeError(`${name} expects a function callback.`, node).as("TypeError");
42
+ throw new InterpreterRuntimeError(`${name} expects a function callback.`, node);
43
43
  }
44
44
  return (callbackArgs) => runner.invokeCallable(callback, callbackArgs, node);
45
45
  };
@@ -1,8 +1,9 @@
1
1
  import { Cause, Deferred, Effect, Exit } from "effect";
2
2
  import { ToolRuntimeError, toProgram } from "../data.js";
3
3
  import { ToolReference } from "../tool-runtime.js";
4
- import { AsyncIteratorSymbol, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, IteratorSymbol, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
4
+ import { AsyncIteratorSymbol, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, InterpreterRuntimeError, IntrinsicReference, IteratorSymbol, OptionalShortCircuit, ProgramThrow, PromiseInstanceMethodReference, rangeError, unsupportedSyntax, } from "./model.js";
5
5
  import { caughtErrorValue } from "./errors.js";
6
+ import { createIntrinsics } from "./intrinsics.js";
6
7
  import { globals } from "./globals.js";
7
8
  import { HostFunction, HostNamespace } from "./host.js";
8
9
  import { invokeIntrinsic } from "./methods.js";
@@ -18,7 +19,7 @@ import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/rege
18
19
  import { stringMethods } from "../stdlib/string.js";
19
20
  import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
20
21
  import { enumerableSource } from "../stdlib/object.js";
21
- import { coerceToNumber, coerceToString, compoundOperators, errorBrandName } from "../stdlib/value.js";
22
+ import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js";
22
23
  import { Values } from "../values.js";
23
24
  // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
24
25
  // Unlabelled break ends this loop; a label the loop does not carry propagates outward.
@@ -75,7 +76,7 @@ const constructorName = (value) => {
75
76
  return "Promise";
76
77
  if (!(value instanceof ProgramObject) || value instanceof ProgramFunction)
77
78
  return undefined;
78
- return errorBrandName(value) ?? "Object";
79
+ return "Object";
79
80
  };
80
81
  const instanceofValue = (lhs, rhs, node) => {
81
82
  if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
@@ -200,6 +201,7 @@ export class Runtime {
200
201
  invokeCallable: (callable, args, node) => this.root.invokeCallable(callable, args, node),
201
202
  settlePromise: (promise) => this.root.settlePromise(promise),
202
203
  syncIterator: (value, node) => this.root.syncIterator(value, node),
204
+ intrinsics: createIntrinsics(),
203
205
  };
204
206
  this.builtins = new Map([...globals(this), ...extraGlobals(this)]);
205
207
  for (const [name, value] of this.builtins)
@@ -505,7 +507,7 @@ class Frame {
505
507
  const iterator = yield* self.customIterator(right, node, awaiting);
506
508
  const cursor = iterator === undefined ? yield* self.syncIterator(right, node) : undefined;
507
509
  if (iterator === undefined && cursor === undefined) {
508
- throw new InterpreterRuntimeError(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node).as("TypeError");
510
+ throw new InterpreterRuntimeError(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node);
509
511
  }
510
512
  const close = () => iterator
511
513
  ? self.closeIterator(iterator, node, awaiting)
@@ -696,7 +698,7 @@ class Frame {
696
698
  requireIteratorObject(value, context, node) {
697
699
  if (value instanceof ProgramObject)
698
700
  return value;
699
- throw new InterpreterRuntimeError(`${context} must be an object.`, node).as("TypeError");
701
+ throw new InterpreterRuntimeError(`${context} must be an object.`, node);
700
702
  }
701
703
  requireIterator(value, node) {
702
704
  return value instanceof CodeModeGenerator
@@ -706,7 +708,7 @@ class Frame {
706
708
  requireIteratorMethod(value, context, node) {
707
709
  if (typeofValue(value) === "function")
708
710
  return value;
709
- throw new InterpreterRuntimeError(`${context} must be a function.`, node).as("TypeError");
711
+ throw new InterpreterRuntimeError(`${context} must be a function.`, node);
710
712
  }
711
713
  // for...in over null/undefined iterates nothing, like JS.
712
714
  enumerableKeys(value, node) {
@@ -802,7 +804,7 @@ class Frame {
802
804
  if (cause.reasons.some(Cause.isInterruptReason) || Cause.squash(cause) instanceof GeneratorReturn || !handler) {
803
805
  return Effect.failCause(cause);
804
806
  }
805
- const caught = caughtErrorValue(Cause.squash(cause));
807
+ const caught = caughtErrorValue(self.runtime.runner, Cause.squash(cause));
806
808
  const parameter = handler.param;
807
809
  self.scopes.push();
808
810
  return Effect.gen(function* () {
@@ -935,7 +937,7 @@ class Frame {
935
937
  return Effect.gen(function* () {
936
938
  const cursor = yield* self.syncIterator(value, pattern);
937
939
  if (cursor === undefined) {
938
- throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern).as("TypeError");
940
+ throw new InterpreterRuntimeError("Array destructuring requires a supported iterable value.", pattern);
939
941
  }
940
942
  let done = false;
941
943
  for (const element of pattern.elements) {
@@ -1069,7 +1071,7 @@ class Frame {
1069
1071
  : callee instanceof HostFunction
1070
1072
  ? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
1071
1073
  : `${name} is not a constructor.`;
1072
- throw new InterpreterRuntimeError(message, node).as("TypeError");
1074
+ throw new InterpreterRuntimeError(message, node);
1073
1075
  }
1074
1076
  const args = yield* self.evaluateCallArguments(node.arguments);
1075
1077
  return yield* construct(args, node);
@@ -1346,7 +1348,7 @@ class Frame {
1346
1348
  if (callable instanceof HostFunction)
1347
1349
  return yield* callable.call(args, node);
1348
1350
  if (callable === undefined || callable === null) {
1349
- throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node).as("TypeError");
1351
+ throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node);
1350
1352
  }
1351
1353
  throw new InterpreterRuntimeError("Only tools are callable here.", callee ?? node);
1352
1354
  });
@@ -1360,7 +1362,7 @@ class Frame {
1360
1362
  const spread = yield* self.evaluateExpression(argNode.argument);
1361
1363
  const cursor = yield* self.syncIterator(spread, argNode);
1362
1364
  if (cursor === undefined)
1363
- throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode).as("TypeError");
1365
+ throw new InterpreterRuntimeError("Spread arguments require a synchronous iterable.", argNode);
1364
1366
  while (true) {
1365
1367
  const step = yield* cursor.next;
1366
1368
  if (step.done)
@@ -1413,7 +1415,7 @@ class Frame {
1413
1415
  const generator = new CodeModeGenerator(asynchronous, (kind, value, node) => {
1414
1416
  const request = { kind, value, response: Deferred.makeUnsafe() };
1415
1417
  if (!asynchronous && state.active) {
1416
- return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node).as("TypeError"));
1418
+ return Effect.fail(new InterpreterRuntimeError("Generator is already running.", node));
1417
1419
  }
1418
1420
  if (asynchronous && (state.completed || (!state.started && kind !== "next"))) {
1419
1421
  state.started = true;
@@ -1561,14 +1563,14 @@ class Frame {
1561
1563
  }
1562
1564
  if (error instanceof ProgramThrow) {
1563
1565
  yield* cursor.close;
1564
- throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as("TypeError");
1566
+ throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node);
1565
1567
  }
1566
1568
  return yield* Effect.failCause(resumed.cause);
1567
1569
  }
1568
1570
  }
1569
1571
  const iterator = yield* self.customIterator(value, node, self.generatorAsync);
1570
1572
  if (!iterator)
1571
- throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node).as("TypeError");
1573
+ throw new InterpreterRuntimeError("yield* requires a compatible iterable value.", node);
1572
1574
  let kind = "next";
1573
1575
  let input = undefined;
1574
1576
  while (true) {
@@ -1581,7 +1583,7 @@ class Frame {
1581
1583
  if (kind === "return")
1582
1584
  return yield* Effect.fail(new GeneratorReturn(input));
1583
1585
  yield* self.closeIterator(iterator, node, self.generatorAsync);
1584
- throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node).as("TypeError");
1586
+ throw new InterpreterRuntimeError("The delegated iterator does not provide a throw() method.", node);
1585
1587
  }
1586
1588
  const called = yield* self.invokeCallable(self.requireIteratorMethod(method, `Iterator ${kind}`, node), [input], node);
1587
1589
  const result = self.requireIteratorObject(iterator.asynchronous ? yield* self.awaitValue(called) : called, `Iterator ${kind}() result`, node);
@@ -1662,7 +1664,7 @@ class Frame {
1662
1664
  const spread = yield* self.evaluateExpression(element.argument);
1663
1665
  const cursor = yield* self.syncIterator(spread, element);
1664
1666
  if (cursor === undefined)
1665
- throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element).as("TypeError");
1667
+ throw new InterpreterRuntimeError("Array spread requires a synchronous iterable.", element);
1666
1668
  while (true) {
1667
1669
  const step = yield* cursor.next;
1668
1670
  if (step.done)
@@ -1904,7 +1906,7 @@ class Frame {
1904
1906
  if (reference.target instanceof Values.URL) {
1905
1907
  const property = key;
1906
1908
  if (!urlWritableProperties.has(property)) {
1907
- throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError");
1909
+ throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node);
1908
1910
  }
1909
1911
  try {
1910
1912
  const url = reference.target.url;
@@ -1914,7 +1916,7 @@ class Frame {
1914
1916
  catch (error) {
1915
1917
  if (error instanceof InterpreterRuntimeError || error instanceof ToolRuntimeError)
1916
1918
  throw error;
1917
- throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node).as("TypeError");
1919
+ throw new InterpreterRuntimeError(`URL.${property} received an invalid value.`, node);
1918
1920
  }
1919
1921
  }
1920
1922
  if (reference.target instanceof Values.RegExp) {
@@ -1926,8 +1928,8 @@ class Frame {
1926
1928
  if (set(target, key, next))
1927
1929
  return;
1928
1930
  if (target instanceof ProgramArray)
1929
- throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError");
1930
- throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node).as("TypeError");
1931
+ throw rangeError("Invalid array length", node);
1932
+ throw new InterpreterRuntimeError(`Cannot assign to read only property '${String(key)}'.`, node);
1931
1933
  }
1932
1934
  toPropertyKey(value, node) {
1933
1935
  if (typeof value === "string" || typeof value === "number") {
@@ -1,4 +1,4 @@
1
- import { InterpreterRuntimeError } from "./model.js";
1
+ import { InterpreterRuntimeError, referenceError } from "./model.js";
2
2
  export class ScopeStack {
3
3
  scopes;
4
4
  constructor(scopes) {
@@ -29,23 +29,23 @@ export class ScopeStack {
29
29
  get(name, node) {
30
30
  const binding = this.resolve(name);
31
31
  if (!binding) {
32
- throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError");
32
+ throw referenceError(`Unknown identifier '${name}'.`, node);
33
33
  }
34
34
  if (binding.initialized === false) {
35
- throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError");
35
+ throw referenceError(`Cannot access '${name}' before initialization.`, node);
36
36
  }
37
37
  return binding.value;
38
38
  }
39
39
  set(name, value, node) {
40
40
  const binding = this.resolve(name);
41
41
  if (!binding) {
42
- throw new InterpreterRuntimeError(`Unknown identifier '${name}'.`, node).as("ReferenceError");
42
+ throw referenceError(`Unknown identifier '${name}'.`, node);
43
43
  }
44
44
  if (binding.initialized === false) {
45
- throw new InterpreterRuntimeError(`Cannot access '${name}' before initialization.`, node).as("ReferenceError");
45
+ throw referenceError(`Cannot access '${name}' before initialization.`, node);
46
46
  }
47
47
  if (!binding.mutable) {
48
- throw new InterpreterRuntimeError(`Cannot assign to constant '${name}'.`, node).as("TypeError");
48
+ throw new InterpreterRuntimeError(`Cannot assign to constant '${name}'.`, node);
49
49
  }
50
50
  binding.value = value;
51
51
  return value;
@@ -1,6 +1,6 @@
1
1
  import { Effect } from "effect";
2
2
  import { HostFunction, sync, syncCall } from "../interpreter/host.js";
3
- import { CodeModeGenerator, InterpreterRuntimeError } from "../interpreter/model.js";
3
+ import { CodeModeGenerator, InterpreterRuntimeError, rangeError } from "../interpreter/model.js";
4
4
  import { get, ProgramArray, ProgramObject } from "../interpreter/objects.js";
5
5
  import { describeValue } from "../interpreter/references.js";
6
6
  import { applyCollectionCallback, preserveConsumerError } from "../interpreter/runner.js";
@@ -11,7 +11,7 @@ const constructArray = (args, node) => {
11
11
  if (typeof first !== "number")
12
12
  return new ProgramArray([first]);
13
13
  if (!Number.isInteger(first) || first < 0 || first > 4294967295) {
14
- throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError");
14
+ throw rangeError("Invalid array length.", node);
15
15
  }
16
16
  // Sparse like JS: Array(3) has holes, and combinator loops already skip them.
17
17
  return new ProgramArray(new Array(first));
@@ -33,7 +33,7 @@ const arrayFrom = (runner, args, node) => {
33
33
  const cursor = yield* runner.syncIterator(source, node);
34
34
  if (cursor === undefined) {
35
35
  if (source instanceof CodeModeGenerator) {
36
- throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node).as("TypeError");
36
+ throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node);
37
37
  }
38
38
  const arrayLike = arrayLikeSource(source, node);
39
39
  const values = [];
@@ -76,13 +76,13 @@ export const groupBy = (runner, namespace) => new HostFunction({
76
76
  call: (args, node) => {
77
77
  const source = args[0];
78
78
  if (source === null || source === undefined) {
79
- throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as("TypeError");
79
+ throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node);
80
80
  }
81
81
  const apply = applyCollectionCallback(runner, args[1], `${namespace}.groupBy`, node);
82
82
  return Effect.gen(function* () {
83
83
  const cursor = yield* runner.syncIterator(source, node);
84
84
  if (cursor === undefined) {
85
- throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as("TypeError");
85
+ throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node);
86
86
  }
87
87
  if (namespace === "Map") {
88
88
  const result = new Values.Map();
@@ -126,7 +126,7 @@ const constructMap = (runner, init, node) => {
126
126
  return Effect.gen(function* () {
127
127
  const cursor = yield* runner.syncIterator(init, node);
128
128
  if (cursor === undefined) {
129
- throw new InterpreterRuntimeError("new Map(...) expects an iterable of [key, value] pairs or no argument.", node).as("TypeError");
129
+ throw new InterpreterRuntimeError("new Map(...) expects an iterable of [key, value] pairs or no argument.", node);
130
130
  }
131
131
  while (true) {
132
132
  const step = yield* cursor.next;
@@ -134,7 +134,7 @@ const constructMap = (runner, init, node) => {
134
134
  return target;
135
135
  yield* preserveConsumerError(cursor, Effect.sync(() => {
136
136
  if (!(step.value instanceof ProgramObject)) {
137
- throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node).as("TypeError");
137
+ throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node);
138
138
  }
139
139
  target.map.set(getOwn(step.value, 0), getOwn(step.value, 1));
140
140
  }));
@@ -148,7 +148,7 @@ const constructSet = (runner, init, node) => {
148
148
  return Effect.gen(function* () {
149
149
  const cursor = yield* runner.syncIterator(init, node);
150
150
  if (cursor === undefined) {
151
- throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node).as("TypeError");
151
+ throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node);
152
152
  }
153
153
  while (true) {
154
154
  const step = yield* cursor.next;
@@ -1,6 +1,6 @@
1
1
  import { Effect } from "effect";
2
2
  import { HostFunction, sync } from "../interpreter/host.js";
3
- import { InterpreterRuntimeError } from "../interpreter/model.js";
3
+ import { InterpreterRuntimeError, rangeError } from "../interpreter/model.js";
4
4
  import { toPrimitive } from "../interpreter/runner.js";
5
5
  import { Values } from "../values.js";
6
6
  import { coerceToNumber, coerceToString } from "./value.js";
@@ -85,7 +85,7 @@ export const invokeDateMethod = (value, name, args, node, initialTime = value.ti
85
85
  return value.time;
86
86
  case "toISOString":
87
87
  if (!Number.isFinite(value.time))
88
- throw new InterpreterRuntimeError("Invalid time value.", node).as("RangeError");
88
+ throw rangeError("Invalid time value.", node);
89
89
  return hosted.toISOString();
90
90
  case "toJSON":
91
91
  return Number.isFinite(value.time) ? hosted.toISOString() : null;
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { HostFunction, HostNamespace } from "../interpreter/host.js";
3
3
  import { applyCollectionCallback } from "../interpreter/runner.js";
4
- import { InterpreterRuntimeError } from "../interpreter/model.js";
4
+ import { InterpreterRuntimeError, syntaxError } from "../interpreter/model.js";
5
5
  import { typeofValue } from "../interpreter/references.js";
6
6
  import { fromData, toData, toProgram } from "../data.js";
7
7
  import { get, ownKeys, ProgramArray, ProgramObject, record, remove, set } from "../interpreter/objects.js";
@@ -19,7 +19,7 @@ const parse = (runner, args, node) => {
19
19
  return fromData(JSON.parse(text), "JSON.parse result");
20
20
  }
21
21
  catch (error) {
22
- throw new InterpreterRuntimeError(`JSON.parse received invalid JSON: ${error instanceof Error ? error.message : String(error)}`, node).as("SyntaxError");
22
+ throw syntaxError(`JSON.parse received invalid JSON: ${error instanceof Error ? error.message : String(error)}`, node);
23
23
  }
24
24
  })();
25
25
  if (typeofValue(args[1]) !== "function")
@@ -68,7 +68,7 @@ const stringify = (runner, args, node) => {
68
68
  if (!(value instanceof ProgramObject))
69
69
  return {};
70
70
  if (stack.has(value))
71
- throw new InterpreterRuntimeError("Converting circular structure to JSON.", node).as("TypeError");
71
+ throw new InterpreterRuntimeError("Converting circular structure to JSON.", node);
72
72
  stack.add(value);
73
73
  if (value instanceof ProgramArray) {
74
74
  const result = [];
@@ -24,7 +24,7 @@ const sumPrecise = (runner) => new HostFunction({
24
24
  call: (args, node) => Effect.gen(function* () {
25
25
  const cursor = yield* runner.syncIterator(args[0], node);
26
26
  if (cursor === undefined) {
27
- throw new InterpreterRuntimeError("Math.sumPrecise expects a synchronous iterable.", node).as("TypeError");
27
+ throw new InterpreterRuntimeError("Math.sumPrecise expects a synchronous iterable.", node);
28
28
  }
29
29
  const numbers = [];
30
30
  while (true) {
@@ -33,7 +33,7 @@ const sumPrecise = (runner) => new HostFunction({
33
33
  return Math.sumPrecise(numbers);
34
34
  yield* preserveConsumerError(cursor, Effect.sync(() => {
35
35
  if (typeof step.value !== "number") {
36
- throw new InterpreterRuntimeError("Math.sumPrecise expects an iterable of numbers.", node).as("TypeError");
36
+ throw new InterpreterRuntimeError("Math.sumPrecise expects an iterable of numbers.", node);
37
37
  }
38
38
  numbers.push(step.value);
39
39
  }));
@@ -1,6 +1,6 @@
1
1
  import { toProgram } from "../data.js";
2
2
  import { sync } from "../interpreter/host.js";
3
- import { InterpreterRuntimeError } from "../interpreter/model.js";
3
+ import { InterpreterRuntimeError, rangeError } from "../interpreter/model.js";
4
4
  import { coercion, coerceToString } from "./value.js";
5
5
  export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"]);
6
6
  export const invokeNumberMethod = (value, name, args, node) => {
@@ -28,7 +28,7 @@ export const invokeNumberMethod = (value, name, args, node) => {
28
28
  case "toString": {
29
29
  const radix = optNum(0);
30
30
  if (radix !== undefined && (radix < 2 || radix > 36)) {
31
- throw new InterpreterRuntimeError("Number.toString radix must be between 2 and 36.", node);
31
+ throw rangeError("Number.toString radix must be between 2 and 36.", node);
32
32
  }
33
33
  result = value.toString(radix);
34
34
  break;
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { toProgram } from "../data.js";
3
3
  import { HostFunction, sync, syncCall } from "../interpreter/host.js";
4
- import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "../interpreter/model.js";
4
+ import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol, rangeError, } from "../interpreter/model.js";
5
5
  import { getOwn, hasOwn, ownEntries, ownKeys, ProgramArray, ProgramObject, set } from "../interpreter/objects.js";
6
6
  import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue, } from "../interpreter/references.js";
7
7
  import { preserveConsumerError } from "../interpreter/runner.js";
@@ -12,7 +12,7 @@ import { coerceToString } from "./value.js";
12
12
  // ToObject for enumeration.
13
13
  export const enumerableSource = (label, value, node) => {
14
14
  if (value === null || value === undefined) {
15
- throw new InterpreterRuntimeError(`${label} cannot convert ${describeValue(value)} to an object.`, node).as("TypeError");
15
+ throw new InterpreterRuntimeError(`${label} cannot convert ${describeValue(value)} to an object.`, node);
16
16
  }
17
17
  if (value instanceof Values.Promise) {
18
18
  throw new InterpreterRuntimeError(`${label} received an un-awaited Promise; await it before inspecting the result.`, node, "InvalidDataValue");
@@ -30,7 +30,7 @@ export const objectAssign = (args, node) => {
30
30
  const target = args[0];
31
31
  // JS would box a primitive target; wrappers and primitives cannot hold fields here.
32
32
  if (!(target instanceof ProgramObject)) {
33
- throw new InterpreterRuntimeError(`Object.assign expects a data object or array target, received ${describeValue(target)}.`, node).as("TypeError");
33
+ throw new InterpreterRuntimeError(`Object.assign expects a data object or array target, received ${describeValue(target)}.`, node);
34
34
  }
35
35
  const seen = new Set();
36
36
  for (const source of args.slice(1)) {
@@ -42,7 +42,7 @@ export const objectAssign = (args, node) => {
42
42
  continue;
43
43
  rejectCircularInsertion(target, getOwn(from, key), "Object.assign result", node, seen);
44
44
  if (!set(target, key, getOwn(from, key))) {
45
- throw new InterpreterRuntimeError("Invalid array length", node).as("RangeError");
45
+ throw rangeError("Invalid array length", node);
46
46
  }
47
47
  }
48
48
  }
@@ -53,7 +53,7 @@ const objectFromEntries = (runner, source, node) => {
53
53
  return Effect.gen(function* () {
54
54
  const cursor = yield* runner.syncIterator(source, node);
55
55
  if (cursor === undefined) {
56
- throw new InterpreterRuntimeError("Object.fromEntries expects a synchronous iterable of entries.", node).as("TypeError");
56
+ throw new InterpreterRuntimeError("Object.fromEntries expects a synchronous iterable of entries.", node);
57
57
  }
58
58
  while (true) {
59
59
  const step = yield* cursor.next;
@@ -61,7 +61,7 @@ const objectFromEntries = (runner, source, node) => {
61
61
  return out;
62
62
  yield* preserveConsumerError(cursor, Effect.sync(() => {
63
63
  if (!(step.value instanceof ProgramObject) || containsOpaqueReference(step.value)) {
64
- throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node).as("TypeError");
64
+ throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node);
65
65
  }
66
66
  set(out, coerceToString(getOwn(step.value, 0)), getOwn(step.value, 1));
67
67
  }));
@@ -1,5 +1,5 @@
1
1
  import { sync, syncCall } from "../interpreter/host.js";
2
- import { InterpreterRuntimeError } from "../interpreter/model.js";
2
+ import { InterpreterRuntimeError, syntaxError } from "../interpreter/model.js";
3
3
  import { ProgramArray, record, set } from "../interpreter/objects.js";
4
4
  import { Values } from "../values.js";
5
5
  import { coerceToNumber, coerceToString } from "./value.js";
@@ -30,7 +30,7 @@ export const toHostRegex = (arg, method, node, extraFlags = "") => {
30
30
  return new RegExp(arg, extraFlags);
31
31
  }
32
32
  catch (error) {
33
- throw new InterpreterRuntimeError(`String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`, node).as("SyntaxError");
33
+ throw syntaxError(`String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`, node);
34
34
  }
35
35
  }
36
36
  throw new InterpreterRuntimeError(`String.${method} expects a regular expression (a /pattern/flags literal or new RegExp(...)) or a string pattern, not ${arg === null ? "null" : typeof arg}.`, node);
@@ -52,7 +52,7 @@ export const constructRegExp = (args, node) => {
52
52
  const pattern = first instanceof Values.RegExp ? first.regex.source : first === undefined ? "" : coerceToString(first);
53
53
  const flagsArg = args[1];
54
54
  if (flagsArg !== undefined && typeof flagsArg !== "string") {
55
- throw new InterpreterRuntimeError(`RegExp flags must be a string of flag characters (e.g. "g", "gi"), not ${flagsArg === null ? "null" : typeof flagsArg}.`, node).as("SyntaxError");
55
+ throw syntaxError(`RegExp flags must be a string of flag characters (e.g. "g", "gi"), not ${flagsArg === null ? "null" : typeof flagsArg}.`, node);
56
56
  }
57
57
  const flags = flagsArg ?? (first instanceof Values.RegExp ? first.regex.flags : "");
58
58
  try {
@@ -60,9 +60,9 @@ export const constructRegExp = (args, node) => {
60
60
  }
61
61
  catch (error) {
62
62
  const reason = regexFailureReason(error);
63
- throw new InterpreterRuntimeError(/flag/i.test(reason)
63
+ throw syntaxError(/flag/i.test(reason)
64
64
  ? `new RegExp(...) received invalid flags ${JSON.stringify(flags)} (${reason}). Valid flags are d, g, i, m, s, u, v, and y.`
65
- : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node).as("SyntaxError");
65
+ : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node);
66
66
  }
67
67
  };
68
68
  // RegExp constructs identically with or without new, like JS.
@@ -72,7 +72,7 @@ export const regexpGlobal = sync("RegExp", constructRegExp, {
72
72
  members: {
73
73
  escape: sync("RegExp.escape", (args, node) => {
74
74
  if (typeof args[0] !== "string") {
75
- throw new InterpreterRuntimeError("RegExp.escape expects a string.", node).as("TypeError");
75
+ throw new InterpreterRuntimeError("RegExp.escape expects a string.", node);
76
76
  }
77
77
  return RegExp.escape(args[0]);
78
78
  }),
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { toProgram } from "../data.js";
3
3
  import { HostFunction, requiresNew, sync, syncCall } from "../interpreter/host.js";
4
- import { InterpreterRuntimeError } from "../interpreter/model.js";
4
+ import { InterpreterRuntimeError, uriError } from "../interpreter/model.js";
5
5
  import { ownEntries, ProgramObject } from "../interpreter/objects.js";
6
6
  import { isRuntimeReference } from "../interpreter/references.js";
7
7
  import { preserveConsumerError } from "../interpreter/runner.js";
@@ -60,13 +60,13 @@ export const uriGlobal = (name) => sync(name, (args, node) => {
60
60
  return uriFunctions[name](value);
61
61
  }
62
62
  catch (error) {
63
- throw new InterpreterRuntimeError(`${name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node).as("URIError");
63
+ throw uriError(`${name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node);
64
64
  }
65
65
  });
66
66
  export const urlArgument = (value, label) => value instanceof Values.URL ? value.url.href : uriArgument(value, label);
67
67
  const urlStatic = (name) => sync(`URL.${name}`, (args, node) => {
68
68
  if (args.length === 0) {
69
- throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError");
69
+ throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node);
70
70
  }
71
71
  const input = urlArgument(args[0], `URL.${name} input`);
72
72
  const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`);
@@ -80,7 +80,7 @@ const urlStatic = (name) => sync(`URL.${name}`, (args, node) => {
80
80
  });
81
81
  const constructURL = (args, node) => {
82
82
  if (args.length === 0) {
83
- throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as("TypeError");
83
+ throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node);
84
84
  }
85
85
  const input = urlArgument(args[0], "new URL input");
86
86
  const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base");
@@ -88,7 +88,7 @@ const constructURL = (args, node) => {
88
88
  return new Values.URL(new URL(input, base));
89
89
  }
90
90
  catch {
91
- throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node).as("TypeError");
91
+ throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node);
92
92
  }
93
93
  };
94
94
  export const urlGlobal = new HostFunction({
@@ -101,7 +101,7 @@ export const urlGlobal = new HostFunction({
101
101
  const readURLSearchParamsPair = (runner, value, node) => Effect.gen(function* () {
102
102
  const cursor = yield* runner.syncIterator(value, node);
103
103
  if (cursor === undefined) {
104
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
104
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node);
105
105
  }
106
106
  const items = [];
107
107
  while (true) {
@@ -130,7 +130,7 @@ const constructURLSearchParams = (runner, init, node) => {
130
130
  const step = yield* cursor.next;
131
131
  if (step.done) {
132
132
  if (entries.some((entry) => entry.length !== 2)) {
133
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
133
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node);
134
134
  }
135
135
  return new Values.URLSearchParams(new URLSearchParams(entries.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
136
136
  }
@@ -138,12 +138,12 @@ const constructURLSearchParams = (runner, init, node) => {
138
138
  }
139
139
  }
140
140
  if (isRuntimeReference(init)) {
141
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node).as("TypeError");
141
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node);
142
142
  }
143
143
  if (Values.isValue(init))
144
144
  return new Values.URLSearchParams(new URLSearchParams());
145
145
  if (!(init instanceof ProgramObject)) {
146
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node).as("TypeError");
146
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node);
147
147
  }
148
148
  return new Values.URLSearchParams(new URLSearchParams(Object.fromEntries(ownEntries(init).map(([key, value]) => [key, coerceToString(value)]))));
149
149
  });
@@ -1,10 +1,5 @@
1
1
  import { type HostFunction, type SyncOptions } from "../interpreter/host.js";
2
- import { ProgramError } from "../interpreter/objects.js";
3
- export declare const errorConstructors: Set<string>;
4
2
  export declare const compoundOperators: Set<string>;
5
- export declare const createErrorValue: (name: string, message: string) => ProgramError;
6
- export declare const createAggregateErrorValue: (errors: Array<unknown>, message: string) => ProgramError;
7
- export declare const errorBrandName: (value: unknown) => string | undefined;
8
3
  export declare const coerceToString: (value: unknown) => string;
9
4
  export declare const coerceToNumber: (value: unknown) => number;
10
5
  type Coercion = "Number" | "String" | "Boolean" | "parseInt" | "parseFloat" | "isFinite" | "isNaN";
@@ -1,31 +1,9 @@
1
1
  import { sync } from "../interpreter/host.js";
2
2
  import { InterpreterRuntimeError } from "../interpreter/model.js";
3
3
  import { toProgram } from "../data.js";
4
- import { get, ProgramArray, ProgramError, set } from "../interpreter/objects.js";
4
+ import { get, ProgramArray, ProgramError } from "../interpreter/objects.js";
5
5
  import { Values } from "../values.js";
6
- export const errorConstructors = new Set([
7
- "Error",
8
- "TypeError",
9
- "RangeError",
10
- "SyntaxError",
11
- "ReferenceError",
12
- "EvalError",
13
- "URIError",
14
- "AggregateError",
15
- ]);
16
6
  export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="]);
17
- export const createErrorValue = (name, message) => {
18
- const value = new ProgramError(name);
19
- set(value, "name", name);
20
- set(value, "message", message);
21
- return value;
22
- };
23
- export const createAggregateErrorValue = (errors, message) => {
24
- const value = createErrorValue("AggregateError", message);
25
- set(value, "errors", new ProgramArray(errors));
26
- return value;
27
- };
28
- export const errorBrandName = (value) => value instanceof ProgramError ? value.errorName : undefined;
29
7
  export const coerceToString = (value) => {
30
8
  if (value === null)
31
9
  return "null";
@@ -1,16 +1,17 @@
1
1
  import { HostNamespace, sync } from "../interpreter/host.js";
2
2
  import { InterpreterRuntimeError } from "../interpreter/model.js";
3
3
  import { coerceToString } from "./value.js";
4
- // WebIDL DOMString conversion: a missing argument is a TypeError, anything else stringifies.
4
+ // WebIDL DOMString conversion: a missing argument is a TypeError, anything else stringifies. Invalid input is a
5
+ // TypeError as well; browsers throw a DOMException named InvalidCharacterError, which CodeMode does not have.
5
6
  const base64 = (name) => sync(name, (args, node) => {
6
7
  if (args.length === 0)
7
- throw new InterpreterRuntimeError(`${name} requires 1 argument (a string)`, node).as("TypeError");
8
+ throw new InterpreterRuntimeError(`${name} requires 1 argument (a string)`, node);
8
9
  const input = coerceToString(args[0]);
9
10
  try {
10
11
  return name === "atob" ? atob(input) : btoa(input);
11
12
  }
12
13
  catch {
13
- throw new InterpreterRuntimeError("The string contains invalid characters.", node).as("InvalidCharacterError");
14
+ throw new InterpreterRuntimeError("The string contains invalid characters.", node);
14
15
  }
15
16
  });
16
17
  export const atobGlobal = base64("atob");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode/codemode",
4
- "version": "2.0.0",
4
+ "version": "2.0.1",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",