@opencode/codemode 0.0.0-dev-19366 → 0.0.0-dev-19367

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.
Files changed (46) hide show
  1. package/dist/interpreter/errors.d.ts +4 -6
  2. package/dist/interpreter/errors.js +20 -4
  3. package/dist/interpreter/globals.d.ts +13 -0
  4. package/dist/interpreter/globals.js +59 -0
  5. package/dist/interpreter/host.d.ts +41 -0
  6. package/dist/interpreter/host.js +44 -0
  7. package/dist/interpreter/methods.d.ts +3 -23
  8. package/dist/interpreter/methods.js +8 -183
  9. package/dist/interpreter/model.d.ts +0 -41
  10. package/dist/interpreter/model.js +0 -56
  11. package/dist/interpreter/promises.d.ts +7 -8
  12. package/dist/interpreter/promises.js +45 -22
  13. package/dist/interpreter/references.js +11 -28
  14. package/dist/interpreter/runner.d.ts +23 -0
  15. package/dist/interpreter/runner.js +42 -0
  16. package/dist/interpreter/runtime.d.ts +0 -11
  17. package/dist/interpreter/runtime.js +33 -423
  18. package/dist/stdlib/array.d.ts +3 -0
  19. package/dist/stdlib/array.js +73 -0
  20. package/dist/stdlib/collections.d.ts +6 -1
  21. package/dist/stdlib/collections.js +120 -1
  22. package/dist/stdlib/console.d.ts +3 -2
  23. package/dist/stdlib/console.js +11 -2
  24. package/dist/stdlib/date.d.ts +3 -2
  25. package/dist/stdlib/date.js +27 -11
  26. package/dist/stdlib/json.d.ts +4 -4
  27. package/dist/stdlib/json.js +6 -2
  28. package/dist/stdlib/math.d.ts +3 -7
  29. package/dist/stdlib/math.js +85 -153
  30. package/dist/stdlib/number.d.ts +1 -3
  31. package/dist/stdlib/number.js +28 -36
  32. package/dist/stdlib/object.d.ts +4 -6
  33. package/dist/stdlib/object.js +101 -71
  34. package/dist/stdlib/regexp.d.ts +2 -4
  35. package/dist/stdlib/regexp.js +32 -9
  36. package/dist/stdlib/string.d.ts +1 -3
  37. package/dist/stdlib/string.js +14 -16
  38. package/dist/stdlib/url.d.ts +8 -4
  39. package/dist/stdlib/url.js +97 -21
  40. package/dist/stdlib/value.d.ts +5 -3
  41. package/dist/stdlib/value.js +21 -19
  42. package/package.json +1 -1
  43. package/dist/interpreter/iterator.d.ts +0 -13
  44. package/dist/interpreter/iterator.js +0 -4
  45. package/dist/stdlib/promise.d.ts +0 -2
  46. package/dist/stdlib/promise.js +0 -1
@@ -1,36 +1,23 @@
1
1
  import { Cause, Deferred, Effect, Exit } from "effect";
2
2
  import { isBlockedMember, ToolRuntimeError, toProgram } from "../data.js";
3
3
  import { ToolReference } from "../tool-runtime.js";
4
- import { AsyncIteratorSymbol, CodeModeFunction, CodeModeGenerator, CoercionFunction, ComputedValue, ErrorConstructorReference, GlobalMethodReference, GlobalNamespace, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, isRecord, IteratorSymbol, IteratorSymbols, JsonMethodReference, OptionalShortCircuit, PromiseCapabilityFunction, PromiseInstanceMethodReference, PromiseMethodReference, PromiseNamespace, ProgramThrow, SearchFunction, SymbolNamespace, unsupportedSyntax, UriFunction, } from "./model.js";
5
- import { caughtErrorValue, constructAggregateErrorValue, constructErrorValue } from "./errors.js";
6
- import { arrayStatics, invokeArrayFrom, invokeGlobalMethod, invokeGroupBy, invokeIntrinsic, toPrimitive, } from "./methods.js";
7
- import { preserveConsumerError } from "./iterator.js";
8
- import { constructPromise, invokePromiseInstanceMethod, invokePromiseMethod, PromiseRuntime, resolvePromise, resolvePromiseValue, } from "./promises.js";
4
+ import { AsyncIteratorSymbol, CodeModeFunction, CodeModeGenerator, ComputedValue, GeneratorMethodReference, GeneratorReturn, IntrinsicReference, InterpreterRuntimeError, isRecord, IteratorSymbol, IteratorSymbols, OptionalShortCircuit, PromiseInstanceMethodReference, ProgramThrow, unsupportedSyntax, } from "./model.js";
5
+ import { caughtErrorValue } from "./errors.js";
6
+ import { globals } from "./globals.js";
7
+ import { HostFunction, HostNamespace } from "./host.js";
8
+ import { invokeIntrinsic } from "./methods.js";
9
+ import { preserveConsumerError } from "./runner.js";
10
+ import { invokePromiseInstanceMethod, PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js";
9
11
  import { containsOpaqueReference, isRuntimeReference, rejectCircularInsertion, typeofValue } from "./references.js";
10
12
  import { ScopeStack } from "./scope.js";
11
- import { arrayMethods, mapMethods, mapStatics, setMethods } from "../stdlib/collections.js";
12
- import { consoleMethods, formatConsoleMessage } from "../stdlib/console.js";
13
- import { dateMethods, dateStatics } from "../stdlib/date.js";
14
- import { invokeJsonMethod, jsonStatics } from "../stdlib/json.js";
15
- import { invokeMathSumPrecise, mathConstants, mathMethods } from "../stdlib/math.js";
16
- import { numberConstants, numberMethods, numberStatics } from "../stdlib/number.js";
17
- import { invokeObjectFromEntries, objectMethodsPreservingIdentity, objectStatics } from "../stdlib/object.js";
18
- import { promiseStatics } from "../stdlib/promise.js";
19
- import { escapeRegexHint, regexpMethods, regexpProperties, regexpStatics, regexFailureReason, } from "../stdlib/regexp.js";
20
- import { stringMethods, stringStatics } from "../stdlib/string.js";
21
- import { urlMethods, urlProperties, urlSearchParamsMethods, urlStatics, urlWritableProperties, invokeUriFunction, uriArgument, urlArgument, } from "../stdlib/url.js";
22
- import { coerceToNumber, coerceToString, compoundOperators, errorBrandName, errorConstructors, invokeCoercion, valueConstructors, } from "../stdlib/value.js";
13
+ import { arrayMethods, mapMethods, setMethods } from "../stdlib/collections.js";
14
+ import { dateMethods } from "../stdlib/date.js";
15
+ import { numberMethods } from "../stdlib/number.js";
16
+ import { constructRegExp, regexpMethods, regexpProperties } from "../stdlib/regexp.js";
17
+ import { stringMethods } from "../stdlib/string.js";
18
+ import { uriArgument, urlMethods, urlProperties, urlSearchParamsMethods, urlWritableProperties } from "../stdlib/url.js";
19
+ import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js";
23
20
  import { Values } from "../values.js";
24
- const globalStaticMembers = {
25
- Object: objectStatics,
26
- Math: mathMethods,
27
- Array: arrayStatics,
28
- console: consoleMethods,
29
- Date: dateStatics,
30
- RegExp: regexpStatics,
31
- Map: mapStatics,
32
- URL: urlStatics,
33
- };
34
21
  const MAX_ARRAY_LENGTH = 4_294_967_295;
35
22
  const parseArrayIndex = (key) => {
36
23
  const property = String(key);
@@ -56,35 +43,8 @@ const calleeDescription = (callee) => {
56
43
  return "The called value";
57
44
  };
58
45
  const instanceofValue = (lhs, rhs, node) => {
59
- if (rhs instanceof ErrorConstructorReference) {
60
- const brand = errorBrandName(lhs);
61
- return brand !== undefined && (rhs.name === "Error" || brand === rhs.name);
62
- }
63
- if (rhs instanceof GlobalNamespace) {
64
- switch (rhs.name) {
65
- case "Date":
66
- return lhs instanceof Values.Date;
67
- case "RegExp":
68
- return lhs instanceof Values.RegExp;
69
- case "Map":
70
- return lhs instanceof Values.Map;
71
- case "Set":
72
- return lhs instanceof Values.Set;
73
- case "URL":
74
- return lhs instanceof Values.URL;
75
- case "URLSearchParams":
76
- return lhs instanceof Values.URLSearchParams;
77
- case "Array":
78
- return Array.isArray(lhs);
79
- case "Object":
80
- return lhs !== null && (typeof lhs === "object" || typeofValue(lhs) === "function");
81
- }
82
- }
83
- if (rhs instanceof PromiseNamespace)
84
- return lhs instanceof Values.Promise;
85
- if (rhs instanceof CoercionFunction && (rhs.name === "Number" || rhs.name === "String" || rhs.name === "Boolean")) {
86
- return false;
87
- }
46
+ if (rhs instanceof HostFunction && rhs.instanceOf !== undefined)
47
+ return rhs.instanceOf(lhs);
88
48
  throw new InterpreterRuntimeError("The right-hand side of 'instanceof' must be a supported constructor: Error (or a specific error type like TypeError), Date, RegExp, Map, Set, URL, URLSearchParams, Array, Object, or Promise.", node);
89
49
  };
90
50
  const collectPatternNames = (pattern, out = []) => {
@@ -127,11 +87,8 @@ const loopDeclaration = (left, statement) => {
127
87
  };
128
88
  };
129
89
  const isOpaqueMemberReference = (value) => value instanceof ToolReference ||
130
- value instanceof PromiseMethodReference ||
131
90
  value instanceof PromiseInstanceMethodReference ||
132
91
  value instanceof IntrinsicReference ||
133
- value instanceof GlobalMethodReference ||
134
- value instanceof JsonMethodReference ||
135
92
  value instanceof GeneratorMethodReference;
136
93
  const copyIteratorSymbols = (source, target, consumed) => {
137
94
  for (const symbol of IteratorSymbols) {
@@ -163,38 +120,9 @@ export class Interpreter {
163
120
  this.toolKeys = toolKeys;
164
121
  this.logs = logs;
165
122
  this.promises = promises;
166
- globalScope.set("tools", { mutable: false, value: new ToolReference([]) });
167
- globalScope.set("search", { mutable: false, value: new SearchFunction() });
168
- globalScope.set("Promise", { mutable: false, value: new PromiseNamespace() });
169
- globalScope.set("Symbol", { mutable: false, value: new SymbolNamespace() });
170
- globalScope.set("undefined", { mutable: false, value: undefined });
171
- globalScope.set("Object", { mutable: false, value: new GlobalNamespace("Object") });
172
- globalScope.set("Math", { mutable: false, value: new GlobalNamespace("Math") });
173
- globalScope.set("JSON", { mutable: false, value: new GlobalNamespace("JSON") });
174
- globalScope.set("Number", { mutable: false, value: new CoercionFunction("Number") });
175
- globalScope.set("String", { mutable: false, value: new CoercionFunction("String") });
176
- globalScope.set("Boolean", { mutable: false, value: new CoercionFunction("Boolean") });
177
- globalScope.set("Array", { mutable: false, value: new GlobalNamespace("Array") });
178
- globalScope.set("console", { mutable: false, value: new GlobalNamespace("console") });
179
- globalScope.set("parseInt", { mutable: false, value: new CoercionFunction("parseInt") });
180
- globalScope.set("parseFloat", { mutable: false, value: new CoercionFunction("parseFloat") });
181
- globalScope.set("isFinite", { mutable: false, value: new CoercionFunction("isFinite") });
182
- globalScope.set("isNaN", { mutable: false, value: new CoercionFunction("isNaN") });
183
- globalScope.set("Date", { mutable: false, value: new GlobalNamespace("Date") });
184
- globalScope.set("RegExp", { mutable: false, value: new GlobalNamespace("RegExp") });
185
- globalScope.set("Map", { mutable: false, value: new GlobalNamespace("Map") });
186
- globalScope.set("Set", { mutable: false, value: new GlobalNamespace("Set") });
187
- globalScope.set("URL", { mutable: false, value: new GlobalNamespace("URL") });
188
- globalScope.set("URLSearchParams", { mutable: false, value: new GlobalNamespace("URLSearchParams") });
189
- globalScope.set("encodeURI", { mutable: false, value: new UriFunction("encodeURI") });
190
- globalScope.set("encodeURIComponent", { mutable: false, value: new UriFunction("encodeURIComponent") });
191
- globalScope.set("decodeURI", { mutable: false, value: new UriFunction("decodeURI") });
192
- globalScope.set("decodeURIComponent", { mutable: false, value: new UriFunction("decodeURIComponent") });
193
- for (const name of errorConstructors) {
194
- globalScope.set(name, { mutable: false, value: new ErrorConstructorReference(name) });
195
- }
196
- globalScope.set("NaN", { mutable: false, value: NaN });
197
- globalScope.set("Infinity", { mutable: false, value: Infinity });
123
+ const host = { runner: this.runner, promises, search: invokeSearch, toolKeys, logs };
124
+ for (const [name, value] of globals(host))
125
+ globalScope.set(name, { mutable: false, value });
198
126
  }
199
127
  run(program) {
200
128
  const self = this;
@@ -1012,7 +940,7 @@ export class Interpreter {
1012
940
  case "Literal": {
1013
941
  const regex = node.regex;
1014
942
  if (regex)
1015
- return Effect.sync(() => this.constructRegExp([regex.pattern, regex.flags], node));
943
+ return Effect.sync(() => constructRegExp([regex.pattern, regex.flags], node));
1016
944
  return Effect.sync(() => toProgram(node.value, "Literal"));
1017
945
  }
1018
946
  case "Identifier":
@@ -1067,208 +995,15 @@ export class Interpreter {
1067
995
  }
1068
996
  }
1069
997
  evaluateNewExpression(node) {
1070
- const callee = node.callee;
1071
- if (callee.type !== "Identifier") {
1072
- throw unsupportedSyntax("NewExpression", node);
1073
- }
1074
- const name = callee.name;
1075
- const argNodes = node.arguments;
1076
- const self = this;
1077
- if (name === "Promise") {
1078
- return Effect.flatMap(this.evaluateCallArguments(argNodes), (args) => constructPromise(self.runner, self.promises, args[0], node));
1079
- }
1080
- if (errorConstructors.has(name)) {
1081
- return Effect.flatMap(this.evaluateCallArguments(argNodes), (args) => name === "AggregateError"
1082
- ? constructAggregateErrorValue(self.runner, args, node)
1083
- : Effect.succeed(constructErrorValue(name, args)));
1084
- }
1085
- // Array and Object construct identically with or without new, like JS.
1086
- if (name === "Array") {
1087
- return Effect.map(this.evaluateCallArguments(argNodes), (args) => self.constructArray(args, node));
1088
- }
1089
- if (name === "Object") {
1090
- return Effect.map(this.evaluateCallArguments(argNodes), (args) => self.constructObject(args, node));
1091
- }
1092
- if (valueConstructors.has(name)) {
1093
- return Effect.gen(function* () {
1094
- const args = yield* self.evaluateCallArguments(argNodes);
1095
- switch (name) {
1096
- case "Date":
1097
- return yield* self.constructDate(args, node);
1098
- case "RegExp":
1099
- return self.constructRegExp(args, node);
1100
- case "Map":
1101
- return yield* self.constructMap(args[0], node);
1102
- case "Set":
1103
- return yield* self.constructSet(args[0], node);
1104
- case "URL":
1105
- return self.constructURL(args, node);
1106
- default:
1107
- return yield* self.constructURLSearchParams(args[0], node);
1108
- }
1109
- });
1110
- }
1111
- throw unsupportedSyntax("NewExpression", node);
1112
- }
1113
- constructArray(args, node) {
1114
- if (args.length !== 1)
1115
- return [...args];
1116
- const first = args[0];
1117
- if (typeof first !== "number")
1118
- return [first];
1119
- if (!Number.isInteger(first) || first < 0 || first > 4294967295) {
1120
- throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError");
1121
- }
1122
- // Sparse like JS: Array(3) has holes, and combinator loops already skip them.
1123
- return new Array(first);
1124
- }
1125
- constructObject(args, node) {
1126
- const first = args[0];
1127
- if (first === null || first === undefined)
1128
- return {};
1129
- if (typeof first === "object")
1130
- return first;
1131
- throw new InterpreterRuntimeError(`Object(${typeof first}) wrapper objects are not supported; use the primitive value directly.`, node);
1132
- }
1133
- constructDate(args, node) {
1134
- if (args.length === 0)
1135
- return Effect.succeed(new Values.Date(Date.now()));
1136
- if (args.length === 1) {
1137
- const arg = args[0];
1138
- if (arg instanceof Values.Date)
1139
- return Effect.succeed(new Values.Date(arg.time));
1140
- return Effect.map(toPrimitive(this.runner, arg, "number", node), (value) => typeof value === "string"
1141
- ? new Values.Date(Date.parse(value))
1142
- : new Values.Date(new Date(coerceToNumber(value)).getTime()));
1143
- }
1144
- const parts = args.map((arg) => coerceToNumber(arg));
1145
- return Effect.succeed(new Values.Date(new Date(...parts).getTime()));
1146
- }
1147
- constructRegExp(args, node) {
1148
- const first = args[0];
1149
- const pattern = first instanceof Values.RegExp ? first.regex.source : first === undefined ? "" : coerceToString(first);
1150
- const flagsArg = args[1];
1151
- if (flagsArg !== undefined && typeof flagsArg !== "string") {
1152
- 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");
1153
- }
1154
- const flags = flagsArg ?? (first instanceof Values.RegExp ? first.regex.flags : "");
1155
- try {
1156
- return new Values.RegExp(pattern, flags);
1157
- }
1158
- catch (error) {
1159
- const reason = regexFailureReason(error);
1160
- throw new InterpreterRuntimeError(/flag/i.test(reason)
1161
- ? `new RegExp(...) received invalid flags ${JSON.stringify(flags)} (${reason}). Valid flags are d, g, i, m, s, u, v, and y.`
1162
- : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node).as("SyntaxError");
1163
- }
1164
- }
1165
- constructMap(init, node) {
1166
- const target = new Values.Map();
1167
- if (init === undefined || init === null)
1168
- return Effect.succeed(target);
1169
- const self = this;
1170
- return Effect.gen(function* () {
1171
- const cursor = yield* self.syncIterator(init, node);
1172
- if (cursor === undefined) {
1173
- throw new InterpreterRuntimeError("new Map(...) expects an iterable of [key, value] pairs or no argument.", node).as("TypeError");
1174
- }
1175
- while (true) {
1176
- const step = yield* cursor.next;
1177
- if (step.done)
1178
- return target;
1179
- yield* preserveConsumerError(cursor, Effect.sync(() => {
1180
- if (!isRecord(step.value) || isRuntimeReference(step.value)) {
1181
- throw new InterpreterRuntimeError("new Map(...) expects [key, value] pairs as entry objects.", node).as("TypeError");
1182
- }
1183
- target.map.set(step.value[0], step.value[1]);
1184
- }));
1185
- }
1186
- });
1187
- }
1188
- constructSet(init, node) {
1189
- const target = new Values.Set();
1190
- if (init === undefined || init === null)
1191
- return Effect.succeed(target);
1192
998
  const self = this;
1193
999
  return Effect.gen(function* () {
1194
- const cursor = yield* self.syncIterator(init, node);
1195
- if (cursor === undefined) {
1196
- throw new InterpreterRuntimeError("new Set(...) expects a synchronous iterable or no argument.", node).as("TypeError");
1197
- }
1198
- while (true) {
1199
- const step = yield* cursor.next;
1200
- if (step.done)
1201
- return target;
1202
- target.set.add(step.value);
1203
- }
1204
- });
1205
- }
1206
- constructURL(args, node) {
1207
- if (args.length === 0) {
1208
- throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as("TypeError");
1209
- }
1210
- const input = urlArgument(args[0], "new URL input");
1211
- const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base");
1212
- try {
1213
- return new Values.URL(new URL(input, base));
1214
- }
1215
- catch {
1216
- throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node).as("TypeError");
1217
- }
1218
- }
1219
- constructURLSearchParams(init, node) {
1220
- if (init === undefined)
1221
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams()));
1222
- if (init instanceof Values.URLSearchParams) {
1223
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init.params)));
1224
- }
1225
- if (typeof init === "string")
1226
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init)));
1227
- if (init === null || typeof init === "number" || typeof init === "boolean") {
1228
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(coerceToString(init))));
1229
- }
1230
- const self = this;
1231
- return Effect.gen(function* () {
1232
- const cursor = yield* self.syncIterator(init, node);
1233
- if (cursor !== undefined) {
1234
- const entries = [];
1235
- while (true) {
1236
- const step = yield* cursor.next;
1237
- if (step.done) {
1238
- if (entries.some((entry) => entry.length !== 2)) {
1239
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
1240
- }
1241
- return new Values.URLSearchParams(new URLSearchParams(entries.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
1242
- }
1243
- entries.push(yield* preserveConsumerError(cursor, self.readURLSearchParamsPair(step.value, node)));
1244
- }
1245
- }
1246
- if (isRuntimeReference(init)) {
1247
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node).as("TypeError");
1248
- }
1249
- if (Values.isValue(init))
1250
- return new Values.URLSearchParams(new URLSearchParams());
1251
- const data = toProgram(init, "new URLSearchParams input");
1252
- if (data === null || typeof data !== "object") {
1253
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node).as("TypeError");
1254
- }
1255
- return new Values.URLSearchParams(new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))));
1256
- });
1257
- }
1258
- readURLSearchParamsPair(value, node) {
1259
- const self = this;
1260
- return Effect.gen(function* () {
1261
- const cursor = yield* self.syncIterator(value, node);
1262
- if (cursor === undefined) {
1263
- throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
1264
- }
1265
- const items = [];
1266
- while (true) {
1267
- const step = yield* cursor.next;
1268
- if (step.done)
1269
- return items;
1270
- items.push(yield* preserveConsumerError(cursor, Effect.sync(() => uriArgument(step.value, "URLSearchParams pair value"))));
1271
- }
1000
+ const callee = yield* self.evaluateExpression(node.callee);
1001
+ // Globals are built with this interpreter's R; `instanceof` cannot recover the type argument.
1002
+ const construct = callee instanceof HostFunction ? callee.construct : undefined;
1003
+ if (construct === undefined)
1004
+ throw unsupportedSyntax("NewExpression", node);
1005
+ const args = yield* self.evaluateCallArguments(node.arguments);
1006
+ return yield* construct(args, node);
1272
1007
  });
1273
1008
  }
1274
1009
  evaluateBinaryExpression(node) {
@@ -1522,12 +1257,6 @@ export class Interpreter {
1522
1257
  }
1523
1258
  return yield* self.createToolCallPromise(callable.path, args);
1524
1259
  }
1525
- if (callable instanceof PromiseMethodReference) {
1526
- return yield* invokePromiseMethod(self.runner, self.promises, callable, args, node);
1527
- }
1528
- if (callable instanceof PromiseInstanceMethodReference) {
1529
- return yield* invokePromiseInstanceMethod(self.runner, self.promises, callable, args, node);
1530
- }
1531
1260
  if (callable instanceof CodeModeFunction) {
1532
1261
  return yield* self.invokeFunction(callable, args);
1533
1262
  }
@@ -1540,93 +1269,17 @@ export class Interpreter {
1540
1269
  if (callable instanceof IntrinsicReference) {
1541
1270
  return yield* invokeIntrinsic(self.runner, callable, args, node);
1542
1271
  }
1543
- if (callable instanceof GlobalMethodReference) {
1544
- if (callable.namespace === "console")
1545
- return self.invokeConsole(callable.name, args, node);
1546
- if (callable.namespace === "Object" && args[0] instanceof ToolReference) {
1547
- return self.invokeObjectMethodOnTools(callable.name, args[0], node);
1548
- }
1549
- if (callable.namespace === "Object" && objectMethodsPreservingIdentity.has(callable.name)) {
1550
- if (callable.name === "fromEntries")
1551
- return yield* invokeObjectFromEntries(self.runner, args[0], node);
1552
- return invokeGlobalMethod(callable, args, node);
1553
- }
1554
- if (callable.namespace === "Array" && callable.name === "from") {
1555
- return yield* invokeArrayFrom(self.runner, args, node);
1556
- }
1557
- if ((callable.namespace === "Object" || callable.namespace === "Map") && callable.name === "groupBy") {
1558
- return yield* invokeGroupBy(self.runner, callable.namespace, args, node);
1559
- }
1560
- if (callable.namespace === "Math" && callable.name === "sumPrecise") {
1561
- return yield* invokeMathSumPrecise(self.runner, args[0], node);
1562
- }
1563
- if (callable.namespace === "Array" && callable.name === "of") {
1564
- return invokeGlobalMethod(callable, args, node);
1565
- }
1566
- return toProgram(invokeGlobalMethod(callable, args, node), `${callable.namespace}.${callable.name} result`);
1567
- }
1568
- if (callable instanceof JsonMethodReference) {
1569
- return yield* invokeJsonMethod(self.runner, callable.name, args, node);
1570
- }
1571
- if (callable instanceof CoercionFunction) {
1572
- return toProgram(invokeCoercion(callable, args, node), `${callable.name} result`);
1573
- }
1574
- if (callable instanceof UriFunction) {
1575
- return invokeUriFunction(callable, args, node);
1576
- }
1577
- if (callable instanceof SearchFunction) {
1578
- return yield* self.invokeSearch(args);
1579
- }
1580
- if (callable instanceof ErrorConstructorReference) {
1581
- if (callable.name === "AggregateError")
1582
- return yield* constructAggregateErrorValue(self.runner, args, node);
1583
- return constructErrorValue(callable.name, args);
1584
- }
1585
- if (callable instanceof GlobalNamespace) {
1586
- // Real JS permits calling Array, Object, Date, and RegExp without new.
1587
- if (callable.name === "Array")
1588
- return self.constructArray(args, node);
1589
- if (callable.name === "Object")
1590
- return self.constructObject(args, node);
1591
- // ISO instead of the host's locale string: CodeMode date strings are
1592
- // deterministic and must not leak the host timezone.
1593
- if (callable.name === "Date")
1594
- return new Date().toISOString();
1595
- if (callable.name === "RegExp")
1596
- return self.constructRegExp(args, node);
1597
- if (typeofValue(callable) === "function") {
1598
- throw new InterpreterRuntimeError(`Constructor ${callable.name} requires 'new'.`, node).as("TypeError");
1599
- }
1600
- throw new InterpreterRuntimeError(`${callable.name} is not a function.`, node).as("TypeError");
1601
- }
1602
- if (callable instanceof PromiseNamespace) {
1603
- throw new InterpreterRuntimeError("Constructor Promise requires 'new'.", node).as("TypeError");
1604
- }
1605
- if (callable instanceof SymbolNamespace) {
1606
- throw new InterpreterRuntimeError("Symbol is not callable; only Symbol.asyncIterator and Symbol.iterator are available.", node).as("TypeError");
1607
- }
1608
- if (callable instanceof PromiseCapabilityFunction) {
1609
- callable.settle(args[0]);
1610
- return undefined;
1272
+ if (callable instanceof PromiseInstanceMethodReference) {
1273
+ return yield* invokePromiseInstanceMethod(self.runner, self.promises, callable, args, node);
1611
1274
  }
1275
+ if (callable instanceof HostFunction)
1276
+ return yield* callable.call(args, node);
1612
1277
  if (callable === undefined || callable === null) {
1613
1278
  throw new InterpreterRuntimeError(`${calleeDescription(callee)} is not a function.`, callee ?? node).as("TypeError");
1614
1279
  }
1615
1280
  throw new InterpreterRuntimeError("Only tools are callable here.", callee ?? node);
1616
1281
  });
1617
1282
  }
1618
- invokeObjectMethodOnTools(name, ref, node) {
1619
- if (name === "keys") {
1620
- return toProgram(this.enumerableKeys(ref), "Object.keys result");
1621
- }
1622
- throw new InterpreterRuntimeError(`Object.${name}(...) cannot read tool references: they are not plain data. Use Object.keys(tools) for names, or search({ query }) for signatures.`, node, "InvalidDataValue");
1623
- }
1624
- invokeConsole(name, args, node) {
1625
- if (!consoleMethods.has(name))
1626
- throw new InterpreterRuntimeError(`console.${name} is not available.`, node);
1627
- this.logs.push(formatConsoleMessage(name, args));
1628
- return undefined;
1629
- }
1630
1283
  evaluateCallArguments(argNodes) {
1631
1284
  const self = this;
1632
1285
  return Effect.gen(function* () {
@@ -2018,38 +1671,12 @@ export class Interpreter {
2018
1671
  }
2019
1672
  return new ToolReference([...objectValue.path, key]);
2020
1673
  }
2021
- if (objectValue instanceof PromiseNamespace) {
2022
- if (typeof key === "string" && promiseStatics.has(key)) {
2023
- return new PromiseMethodReference(key);
2024
- }
2025
- throw new InterpreterRuntimeError(`Promise.${String(key)} is not available. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`, propertyNode);
2026
- }
2027
- if (objectValue instanceof SymbolNamespace) {
2028
- if (key === "asyncIterator")
2029
- return new ComputedValue(AsyncIteratorSymbol);
2030
- if (key === "iterator")
2031
- return new ComputedValue(IteratorSymbol);
2032
- return new ComputedValue(undefined);
2033
- }
2034
- if (objectValue instanceof GlobalNamespace) {
1674
+ if (objectValue instanceof HostFunction || objectValue instanceof HostNamespace) {
2035
1675
  if (typeof key === "string" && isBlockedMember(key)) {
2036
1676
  throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode);
2037
1677
  }
2038
- if (typeof key !== "string")
2039
- return new ComputedValue(undefined);
2040
- if (objectValue.name === "Math" && mathConstants.has(key)) {
2041
- return new ComputedValue(Math[key]);
2042
- }
2043
- if (objectValue.name === "JSON") {
2044
- if (jsonStatics.has(key))
2045
- return new JsonMethodReference(key);
2046
- return new ComputedValue(undefined);
2047
- }
2048
- if (globalStaticMembers[objectValue.name]?.has(key)) {
2049
- return new GlobalMethodReference(objectValue.name, key);
2050
- }
2051
1678
  // Unknown static members read as undefined so feature detection works like native JS.
2052
- return new ComputedValue(undefined);
1679
+ return new ComputedValue(objectValue.member(key, propertyNode));
2053
1680
  }
2054
1681
  if (typeof objectValue === "string") {
2055
1682
  if (key === "length")
@@ -2066,23 +1693,6 @@ export class Interpreter {
2066
1693
  return new IntrinsicReference(objectValue, key);
2067
1694
  return new ComputedValue(undefined);
2068
1695
  }
2069
- if (objectValue instanceof CoercionFunction) {
2070
- if (typeof key === "string" && isBlockedMember(key)) {
2071
- throw new InterpreterRuntimeError(`${objectValue.name}.${key} is not available.`, propertyNode);
2072
- }
2073
- if (typeof key !== "string")
2074
- return new ComputedValue(undefined);
2075
- if (objectValue.name === "Number" && numberConstants.has(key)) {
2076
- return new ComputedValue(Number[key]);
2077
- }
2078
- if (objectValue.name === "Number" && numberStatics.has(key)) {
2079
- return new GlobalMethodReference("Number", key);
2080
- }
2081
- if (objectValue.name === "String" && stringStatics.has(key)) {
2082
- return new GlobalMethodReference("String", key);
2083
- }
2084
- return new ComputedValue(undefined);
2085
- }
2086
1696
  if (objectValue instanceof Values.Date) {
2087
1697
  if (typeof key === "string" && dateMethods.has(key))
2088
1698
  return new IntrinsicReference(objectValue, key);
@@ -0,0 +1,3 @@
1
+ import { HostFunction } from "../interpreter/host.js";
2
+ import { type Runner } from "../interpreter/runner.js";
3
+ export declare const arrayGlobal: <R>(runner: Runner<R>) => HostFunction<R>;
@@ -0,0 +1,73 @@
1
+ import { Effect } from "effect";
2
+ import { HostFunction, sync, syncCall } from "../interpreter/host.js";
3
+ import { CodeModeGenerator, InterpreterRuntimeError } from "../interpreter/model.js";
4
+ import { applyCollectionCallback, preserveConsumerError } from "../interpreter/runner.js";
5
+ import { Values } from "../values.js";
6
+ const constructArray = (args, node) => {
7
+ if (args.length !== 1)
8
+ return [...args];
9
+ const first = args[0];
10
+ if (typeof first !== "number")
11
+ return [first];
12
+ if (!Number.isInteger(first) || first < 0 || first > 4294967295) {
13
+ throw new InterpreterRuntimeError("Invalid array length.", node).as("RangeError");
14
+ }
15
+ // Sparse like JS: Array(3) has holes, and combinator loops already skip them.
16
+ return new Array(first);
17
+ };
18
+ const arrayLikeSource = (source, node) => {
19
+ if (source instanceof Values.Promise) {
20
+ throw new InterpreterRuntimeError("Array.from received an un-awaited Promise; await it before creating the array.", node, "InvalidDataValue");
21
+ }
22
+ if (source !== null &&
23
+ typeof source === "object" &&
24
+ (Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
25
+ typeof source.length === "number") {
26
+ const length = source.length;
27
+ const normalized = Number.isNaN(length) || length <= 0 ? 0 : Math.trunc(length);
28
+ if (normalized > 4_294_967_295)
29
+ throw new RangeError("Invalid array length");
30
+ return { length: normalized, source };
31
+ }
32
+ throw new InterpreterRuntimeError("Array.from expects an array, string, Map, Set, or array-like value.", node, "InvalidDataValue");
33
+ };
34
+ const arrayFrom = (runner, args, node) => {
35
+ const source = args[0];
36
+ const apply = args.length < 2 || args[1] === undefined ? undefined : applyCollectionCallback(runner, args[1], "Array.from", node);
37
+ return Effect.gen(function* () {
38
+ const cursor = yield* runner.syncIterator(source, node);
39
+ if (cursor === undefined) {
40
+ if (source instanceof CodeModeGenerator) {
41
+ throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node).as("TypeError");
42
+ }
43
+ const arrayLike = arrayLikeSource(source, node);
44
+ const values = [];
45
+ for (let index = 0; index < arrayLike.length; index += 1) {
46
+ const item = Reflect.get(arrayLike.source, index);
47
+ values.push(apply === undefined ? item : yield* apply([item, index]));
48
+ }
49
+ return values;
50
+ }
51
+ const values = [];
52
+ let index = 0;
53
+ while (true) {
54
+ const step = yield* cursor.next;
55
+ if (step.done)
56
+ return values;
57
+ values.push(apply === undefined ? step.value : yield* preserveConsumerError(cursor, apply([step.value, index])));
58
+ index += 1;
59
+ }
60
+ });
61
+ };
62
+ // Array constructs identically with or without new, like JS.
63
+ export const arrayGlobal = (runner) => new HostFunction({
64
+ name: "Array",
65
+ call: syncCall(constructArray),
66
+ construct: syncCall(constructArray),
67
+ instanceOf: (value) => Array.isArray(value),
68
+ members: {
69
+ isArray: sync("Array.isArray", (args) => Array.isArray(args[0])),
70
+ of: sync("Array.of", (args) => [...args]),
71
+ from: new HostFunction({ name: "Array.from", call: (args, node) => arrayFrom(runner, args, node) }),
72
+ },
73
+ });
@@ -1,4 +1,9 @@
1
+ import { HostFunction } from "../interpreter/host.js";
2
+ import { type Runner } from "../interpreter/runner.js";
1
3
  export declare const arrayMethods: Set<string>;
2
4
  export declare const mapMethods: Set<string>;
3
- export declare const mapStatics: Set<string>;
4
5
  export declare const setMethods: Set<string>;
6
+ /** `Map.groupBy` and `Object.groupBy`: the same iteration, keyed into a Map or a data object. */
7
+ export declare const groupBy: <R>(runner: Runner<R>, namespace: "Map" | "Object") => HostFunction<R>;
8
+ export declare const mapGlobal: <R>(runner: Runner<R>) => HostFunction<R>;
9
+ export declare const setGlobal: <R>(runner: Runner<R>) => HostFunction<R>;