@opencode/codemode 2.0.11 → 2.0.13

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 (50) hide show
  1. package/dist/data.d.ts +5 -4
  2. package/dist/interpreter/callback.d.ts +11 -14
  3. package/dist/interpreter/callback.js +23 -7
  4. package/dist/interpreter/errors.d.ts +3 -3
  5. package/dist/interpreter/errors.js +2 -2
  6. package/dist/interpreter/execute.d.ts +2 -1
  7. package/dist/interpreter/extensions.d.ts +2 -1
  8. package/dist/interpreter/extensions.js +7 -35
  9. package/dist/interpreter/globals.d.ts +2 -1
  10. package/dist/interpreter/globals.js +1 -1
  11. package/dist/interpreter/interpreter.d.ts +9 -20
  12. package/dist/interpreter/interpreter.js +124 -122
  13. package/dist/interpreter/intrinsics.d.ts +4 -2
  14. package/dist/interpreter/intrinsics.js +12 -0
  15. package/dist/interpreter/model.d.ts +7 -8
  16. package/dist/interpreter/model.js +3 -3
  17. package/dist/interpreter/native.d.ts +5 -5
  18. package/dist/interpreter/objects.d.ts +148 -47
  19. package/dist/interpreter/objects.js +224 -26
  20. package/dist/interpreter/promises.d.ts +7 -7
  21. package/dist/interpreter/promises.js +32 -11
  22. package/dist/interpreter/references.d.ts +8 -7
  23. package/dist/interpreter/references.js +10 -46
  24. package/dist/interpreter/scope.d.ts +5 -4
  25. package/dist/stdlib/array.d.ts +2 -1
  26. package/dist/stdlib/array.js +37 -52
  27. package/dist/stdlib/bytes.js +14 -24
  28. package/dist/stdlib/collections.js +52 -30
  29. package/dist/stdlib/console.d.ts +2 -2
  30. package/dist/stdlib/console.js +6 -50
  31. package/dist/stdlib/date.js +6 -2
  32. package/dist/stdlib/headers.js +10 -22
  33. package/dist/stdlib/iterator.d.ts +1 -1
  34. package/dist/stdlib/iterator.js +294 -6
  35. package/dist/stdlib/json.js +3 -5
  36. package/dist/stdlib/math.js +5 -17
  37. package/dist/stdlib/number.js +9 -21
  38. package/dist/stdlib/object.d.ts +3 -4
  39. package/dist/stdlib/object.js +7 -18
  40. package/dist/stdlib/regexp.d.ts +3 -3
  41. package/dist/stdlib/regexp.js +1 -2
  42. package/dist/stdlib/string.js +12 -20
  43. package/dist/stdlib/url.d.ts +2 -1
  44. package/dist/stdlib/url.js +6 -16
  45. package/dist/stdlib/value.d.ts +0 -3
  46. package/dist/stdlib/value.js +2 -74
  47. package/dist/stdlib/web.js +1 -2
  48. package/dist/tool-runtime.d.ts +2 -0
  49. package/dist/tool-runtime.js +39 -27
  50. package/package.json +1 -1
@@ -4,15 +4,16 @@ import { ToolReference } from "../tool-runtime.js";
4
4
  import { AsyncIteratorSymbol, CallSite, GeneratorReturn, IteratorSymbol, OptionalShortCircuit, invalidData, Throw, rangeError, typeError, unsupportedSyntax, } from "./model.js";
5
5
  import { checkStringLength } from "./limits.js";
6
6
  import { locate, materialize } from "./errors.js";
7
+ import { primitivePrototype } from "./intrinsics.js";
7
8
  import { globals } from "./globals.js";
8
- import { assign, Callable, define, get, has, hasPrototype, keys, Native, parseArrayIndex, Arr, Bytes, DateObj, Fn, GeneratorObj, IteratorObj, MapObj, Obj, PromiseObj, SetObj, URLSearchParamsObj, HeadersObj, record, remove, set, } from "./objects.js";
9
+ import { assign, Callable, define, get, hostCursor, IteratorObj, has, hidden, hasPrototype, keys, Native, parseArrayIndex, Arr, Fn, GeneratorObj, Obj, PromiseObj, record, remove, set, coerceToNumber, coerceToString, } from "./objects.js";
9
10
  import { preserveConsumerError } from "./callback.js";
10
11
  import { Pending, resolvePromise, resolvePromiseValue } from "./promises.js";
11
12
  import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js";
12
13
  import { ScopeStack } from "./scope.js";
13
14
  import { constructRegExp } from "../stdlib/regexp.js";
14
15
  import { enumerableSource } from "../stdlib/object.js";
15
- import { coerceToNumber, coerceToString, compoundOperators } from "../stdlib/value.js";
16
+ import { compoundOperators } from "../stdlib/value.js";
16
17
  // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
17
18
  // Unlabelled break ends this loop; a label the loop does not carry propagates outward.
18
19
  const loopExit = (result, labels) => {
@@ -27,23 +28,37 @@ const loopExit = (result, labels) => {
27
28
  return result;
28
29
  return undefined;
29
30
  };
30
- const calleeDescription = (callee) => {
31
- if (callee?.type === "Identifier")
32
- return callee.name;
33
- if (callee?.type === "MemberExpression") {
34
- const object = callee.object;
35
- const property = callee.property;
36
- const key = !callee.computed && property.type === "Identifier"
37
- ? property.name
38
- : property.type === "Literal" && typeof property.value === "string"
39
- ? property.value
40
- : undefined;
41
- if (object.type === "Identifier" && key !== undefined)
42
- return `${object.name}.${key}`;
43
- }
44
- return "The called value";
31
+ // Native engines name the callee (`search(...).catch is not a function`), including call chains. Returns
32
+ // undefined when a link cannot be named, so a chain is either named completely or not at all.
33
+ const calleeDescription = (node) => {
34
+ if (node?.type === "Identifier")
35
+ return node.name;
36
+ if (node?.type === "CallExpression") {
37
+ const target = calleeDescription(node.callee);
38
+ return target === undefined ? undefined : `${target}(...)`;
39
+ }
40
+ if (node?.type !== "MemberExpression")
41
+ return undefined;
42
+ const property = node.property;
43
+ const key = !node.computed && property.type === "Identifier"
44
+ ? property.name
45
+ : property.type === "Literal" && typeof property.value === "string"
46
+ ? property.value
47
+ : undefined;
48
+ if (key === undefined)
49
+ return undefined;
50
+ const object = calleeDescription(node.object);
51
+ return object === undefined ? undefined : `${object}.${key}`;
45
52
  };
46
53
  // OrdinaryHasInstance: walk the left operand's chain looking for the constructor's `prototype`.
54
+ // acorn types every literal as possibly a BigInt or RegExp; regex literals become RegExp objects before this is asked.
55
+ const literal = (node) => {
56
+ if (typeof node.value === "bigint")
57
+ throw typeError("BigInt literals are not supported.", node);
58
+ if (node.value instanceof RegExp)
59
+ throw unsupportedSyntax("RegExpLiteral", node);
60
+ return node.value;
61
+ };
47
62
  const instanceofValue = (lhs, rhs, node) => {
48
63
  if (!(rhs instanceof Callable)) {
49
64
  throw typeError("The right-hand side of 'instanceof' is not callable.", node);
@@ -173,6 +188,9 @@ export class Interpreter {
173
188
  iterate(value) {
174
189
  return this.root.iterate(value);
175
190
  }
191
+ iterateDirect(value) {
192
+ return this.root.iterateDirect(value);
193
+ }
176
194
  /** Runs one host tool: arguments cross as JSON and the result comes back as program values. */
177
195
  tool(run, args) {
178
196
  const ctx = this;
@@ -300,7 +318,16 @@ class Frame {
300
318
  }).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())));
301
319
  }
302
320
  createFunction(node, name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? "")) {
303
- return new Fn(this.ctx.builtins.Function, name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
321
+ const builtins = this.ctx.builtins;
322
+ const fn = new Fn(builtins.Function, name, node.params, node.body, this.scopes.capture(), node.async, node.generator);
323
+ // Each generator function gets its own prototype, so `g() instanceof g` holds as in JS.
324
+ if (node.generator)
325
+ define(fn, "prototype", new Obj(node.async ? builtins.AsyncGenerator : builtins.Generator), hidden);
326
+ // The body of a named function expression sees its own name, read-only.
327
+ if (node.type === "FunctionExpression" && node.id) {
328
+ fn.capturedScopes.push(new Map([[node.id.name, { mutable: false, value: fn, initialized: true }]]));
329
+ }
330
+ return fn;
304
331
  }
305
332
  // NamedEvaluation: an anonymous function definition takes the name of what it is assigned to.
306
333
  evaluateNamed(node, name) {
@@ -309,11 +336,12 @@ class Frame {
309
336
  }
310
337
  return this.evaluateExpression(node);
311
338
  }
339
+ // Repeated `function` declarations and `var` clashes are legal: the last declaration wins.
312
340
  hoistFunctions(statements) {
313
341
  for (const node of statements) {
314
342
  if (node.type !== "FunctionDeclaration")
315
343
  continue;
316
- this.scopes.declare(node.id.name, this.createFunction(node), true, node);
344
+ this.scopes.current().set(node.id.name, { mutable: true, value: this.createFunction(node), initialized: true });
317
345
  }
318
346
  }
319
347
  // Hoisted `var` bindings start undefined, or copy a same-named parameter. Function bodies hoist
@@ -477,10 +505,10 @@ class Frame {
477
505
  if (declared?.lexical)
478
506
  self.predeclarePattern(declared.pattern, declared.mutable, left);
479
507
  const right = yield* self.evaluateExpression(node.right);
480
- const cursor = self.hostCursor(right);
508
+ const cursor = self.builtinCursor(right);
481
509
  const iterator = cursor === undefined ? yield* self.customIterator(right, node, awaiting) : undefined;
482
510
  if (iterator === undefined && cursor === undefined) {
483
- throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, URLSearchParams, or Headers, or custom iterator value.`, node);
511
+ throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an iterable value, received ${describeValue(right)}.`, node);
484
512
  }
485
513
  const close = () => iterator
486
514
  ? self.closeIterator(iterator, node, awaiting)
@@ -518,10 +546,9 @@ class Frame {
518
546
  const bodyExit = yield* Effect.exit(evaluateBody(step.value));
519
547
  if (!Exit.isSuccess(bodyExit)) {
520
548
  // Process interruption must remain prompt; user cleanup cannot extend a timeout.
521
- if (!Cause.hasInterruptsOnly(bodyExit.cause)) {
522
- yield* Effect.exit(close());
523
- }
524
- return yield* Effect.failCause(bodyExit.cause);
549
+ if (Cause.hasInterruptsOnly(bodyExit.cause))
550
+ return yield* Effect.failCause(bodyExit.cause);
551
+ return yield* preserveConsumerError(close(), Effect.failCause(bodyExit.cause));
525
552
  }
526
553
  const exit = loopExit(bodyExit.value, labels);
527
554
  if (exit !== undefined) {
@@ -550,49 +577,41 @@ class Frame {
550
577
  });
551
578
  }
552
579
  iterate(value, node) {
553
- const cursor = this.hostCursor(value);
580
+ const cursor = this.builtinCursor(value);
554
581
  if (cursor !== undefined)
555
582
  return Effect.succeed(cursor);
556
- const self = this;
557
- return Effect.map(this.customIterator(value, node, false), (iterator) => iterator === undefined
558
- ? undefined
559
- : {
560
- next: self.nextIteratorResult(iterator, node, false),
561
- close: Effect.suspend(() => self.closeIterator(iterator, node, false)),
562
- });
583
+ return Effect.map(this.customIterator(value, node, false), (iterator) => iterator === undefined ? undefined : this.customCursor(iterator, node));
584
+ }
585
+ /** GetIteratorDirect: drive an iterator by its own `next`, without asking for `[Symbol.iterator]`. */
586
+ iterateDirect(value, node) {
587
+ if (value instanceof IteratorObj)
588
+ return value.cursor;
589
+ if (!(value instanceof Obj)) {
590
+ throw typeError(`An iterator must be an object, received ${describeValue(value)}.`, node);
591
+ }
592
+ return this.customCursor({
593
+ iterator: value,
594
+ next: this.requireIteratorMethod(get(value, "next"), "Iterator next", node),
595
+ asynchronous: false,
596
+ }, node);
563
597
  }
564
- hostCursor(value) {
565
- const iterator = value instanceof Arr
566
- ? value.items[Symbol.iterator]()
567
- : typeof value === "string"
568
- ? value[Symbol.iterator]()
569
- : value instanceof MapObj
570
- ? value.map.entries()
571
- : value instanceof SetObj
572
- ? value.set.values()
573
- : value instanceof URLSearchParamsObj
574
- ? value.params.entries()
575
- : value instanceof HeadersObj
576
- ? value.headers.entries()
577
- : value instanceof Bytes
578
- ? value.bytes.values()
579
- : value instanceof IteratorObj
580
- ? value.iterator
581
- : undefined;
582
- if (iterator === undefined)
583
- return undefined;
584
- const proto = this.ctx.builtins.Array;
598
+ customCursor(iterator, node) {
585
599
  return {
586
- next: Effect.sync(() => {
587
- const step = iterator.next();
588
- return {
589
- done: Boolean(step.done),
590
- value: Array.isArray(step.value) ? new Arr(proto, step.value) : step.value,
591
- };
592
- }),
593
- close: Effect.void,
600
+ next: this.nextIteratorResult(iterator, node, false),
601
+ close: Effect.suspend(() => this.closeIterator(iterator, node, false)),
594
602
  };
595
603
  }
604
+ builtinCursor(value) {
605
+ // Natives build their cursors without knowing R, like `lift` in native.ts.
606
+ if (value instanceof IteratorObj)
607
+ return value.cursor;
608
+ const iterator = typeof value === "string"
609
+ ? value[Symbol.iterator]()
610
+ : value instanceof Obj
611
+ ? value.iterator(this.ctx.builtins)
612
+ : undefined;
613
+ return iterator === undefined ? undefined : hostCursor(iterator);
614
+ }
596
615
  customIterator(value, node, allowAsync = true) {
597
616
  if (!(value instanceof Obj))
598
617
  return Effect.undefined;
@@ -934,7 +953,7 @@ class Frame {
934
953
  return;
935
954
  }
936
955
  const consumed = consume(element, step.done ? undefined : step.value, pattern);
937
- yield* step.done ? consumed : preserveConsumerError(cursor, consumed);
956
+ yield* step.done ? consumed : preserveConsumerError(cursor.close, consumed);
938
957
  }
939
958
  if (!done)
940
959
  yield* cursor.close;
@@ -946,7 +965,7 @@ class Frame {
946
965
  }
947
966
  const keyNode = property.key;
948
967
  if (property.computed) {
949
- return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value, keyNode));
968
+ return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value));
950
969
  }
951
970
  if (keyNode.type === "Identifier")
952
971
  return Effect.succeed(keyNode.name);
@@ -960,9 +979,7 @@ class Frame {
960
979
  const regex = node.regex;
961
980
  if (regex)
962
981
  return Effect.sync(() => constructRegExp(this.ctx.builtins, [regex.pattern, regex.flags]));
963
- if (typeof node.value === "bigint")
964
- throw typeError("BigInt literals are not supported.", node);
965
- return Effect.succeed(node.value);
982
+ return Effect.succeed(literal(node));
966
983
  }
967
984
  case "Identifier":
968
985
  return Effect.sync(() => this.scopes.get(node.name, node));
@@ -1025,7 +1042,7 @@ class Frame {
1025
1042
  // `new` itself is supported, so a non-constructible callee is a TypeError like JS rather than
1026
1043
  // unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
1027
1044
  // otherwise; say `new` is unsupported for them and point at the plain call.
1028
- const name = calleeDescription(node.callee);
1045
+ const name = calleeDescription(node.callee) ?? "The called value";
1029
1046
  const message = callee instanceof Fn
1030
1047
  ? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
1031
1048
  : callee instanceof Native
@@ -1062,14 +1079,9 @@ class Frame {
1062
1079
  if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
1063
1080
  throw invalidData("Binary operators require data values.", node);
1064
1081
  }
1065
- // Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
1066
- // Dates use their default string hint for addition and loose equality, and epoch time elsewhere.
1067
- const coerceOperand = (operand) => {
1068
- if (operand instanceof DateObj) {
1069
- return operator === "+" || operator === "==" || operator === "!=" ? coerceToString(operand) : operand.time;
1070
- }
1071
- return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand;
1072
- };
1082
+ // Addition and loose equality use the default hint; every other operator asks for a number.
1083
+ const hint = operator === "+" || operator === "==" || operator === "!=" ? "default" : "number";
1084
+ const coerceOperand = (operand) => (operand instanceof Obj ? operand.toPrimitive(hint) : operand);
1073
1085
  const bothObjects = lhs !== null && typeof lhs === "object" && rhs !== null && typeof rhs === "object";
1074
1086
  const l = coerceOperand(lhs);
1075
1087
  const r = coerceOperand(rhs);
@@ -1154,11 +1166,7 @@ class Frame {
1154
1166
  if (containsOpaqueReference(value)) {
1155
1167
  throw invalidData("Unary operators require data values.", node);
1156
1168
  }
1157
- const operand = value instanceof DateObj
1158
- ? value.time
1159
- : value !== null && typeof value === "object"
1160
- ? coerceToString(value)
1161
- : value;
1169
+ const operand = value instanceof Obj ? value.toPrimitive("number") : value;
1162
1170
  let result;
1163
1171
  switch (operator) {
1164
1172
  case "+":
@@ -1312,7 +1320,7 @@ class Frame {
1312
1320
  if (callable instanceof Native) {
1313
1321
  return yield* self.native(() => callable.call(thisValue, args), node);
1314
1322
  }
1315
- throw typeError(`${calleeDescription(callee)} is not a function.`, callee ?? node);
1323
+ throw typeError(`${calleeDescription(callee) ?? "The called value"} is not a function.`, callee ?? node);
1316
1324
  });
1317
1325
  }
1318
1326
  // Built-ins throw without a location, synchronously or inside their Effect; the call site supplies it.
@@ -1351,14 +1359,14 @@ class Frame {
1351
1359
  if (depth > MAX_CALL_DEPTH)
1352
1360
  throw rangeError("Maximum call stack size exceeded", node);
1353
1361
  const invocation = new Frame(this.ctx, new ScopeStack([...fn.capturedScopes, new Map()]), depth);
1354
- const run = Effect.gen(function* () {
1355
- // Seed all parameters first so defaults cannot fall through to same-named outer bindings.
1356
- const paramScope = invocation.scopes.current();
1357
- for (const parameter of fn.parameters) {
1358
- for (const name of collectPatternNames(parameter)) {
1359
- paramScope.set(name, { mutable: true, value: undefined, initialized: false });
1360
- }
1362
+ // Seed all parameters first so defaults cannot fall through to same-named outer bindings.
1363
+ const paramScope = invocation.scopes.current();
1364
+ for (const parameter of fn.parameters) {
1365
+ for (const name of collectPatternNames(parameter)) {
1366
+ paramScope.set(name, { mutable: true, value: undefined, initialized: false });
1361
1367
  }
1368
+ }
1369
+ const bind = Effect.gen(function* () {
1362
1370
  for (const [index, parameter] of fn.parameters.entries()) {
1363
1371
  if (parameter.type === "RestElement") {
1364
1372
  yield* invocation.declarePattern(parameter.argument, new Arr(self.ctx.builtins.Array, args.slice(index)), true, parameter, true);
@@ -1366,6 +1374,8 @@ class Frame {
1366
1374
  }
1367
1375
  yield* invocation.declarePattern(parameter, args[index], true, parameter, true);
1368
1376
  }
1377
+ });
1378
+ const body = Effect.gen(function* () {
1369
1379
  if (fn.body.type === "BlockStatement") {
1370
1380
  invocation.scopes.push();
1371
1381
  invocation.hoistVars(fn.body.body, paramScope);
@@ -1374,14 +1384,17 @@ class Frame {
1374
1384
  }
1375
1385
  return yield* invocation.evaluateExpression(fn.body);
1376
1386
  });
1387
+ // Generators bind parameters at the call and defer only the body to the first `next()`, as in JS.
1377
1388
  if (fn.generator)
1378
- return Effect.succeed(this.createGenerator(invocation, run, fn.async));
1389
+ return Effect.map(bind, () => this.createGenerator(invocation, body, fn));
1390
+ const run = Effect.andThen(bind, body);
1379
1391
  if (!fn.async)
1380
1392
  return run;
1381
1393
  return this.ctx.pending.createWithSelf((self) => Effect.flatMap(run, (value) => resolvePromiseValue(invocation.ctx, value, self)));
1382
1394
  });
1383
1395
  }
1384
- createGenerator(invocation, run, asynchronous) {
1396
+ createGenerator(invocation, run, fn) {
1397
+ const asynchronous = fn.async;
1385
1398
  const state = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 };
1386
1399
  invocation.generatorState = state;
1387
1400
  invocation.generatorAsync = asynchronous;
@@ -1440,8 +1453,8 @@ class Frame {
1440
1453
  }
1441
1454
  return Deferred.await(request.response);
1442
1455
  };
1443
- const generator = new GeneratorObj(asynchronous ? builtins.AsyncGenerator : builtins.Generator, asynchronous, request);
1444
- return generator;
1456
+ const proto = get(fn, "prototype");
1457
+ return new GeneratorObj(proto instanceof Obj ? proto : builtins.Generator, asynchronous, request);
1445
1458
  }
1446
1459
  completeGeneratorRequests(state, asynchronous) {
1447
1460
  const self = this;
@@ -1517,16 +1530,8 @@ class Frame {
1517
1530
  delegateYield(value, node) {
1518
1531
  const self = this;
1519
1532
  return Effect.gen(function* () {
1520
- if (value instanceof Arr ||
1521
- typeof value === "string" ||
1522
- value instanceof MapObj ||
1523
- value instanceof SetObj ||
1524
- value instanceof URLSearchParamsObj ||
1525
- value instanceof HeadersObj ||
1526
- value instanceof Bytes) {
1527
- const cursor = yield* self.iterate(value, node);
1528
- if (!cursor)
1529
- throw typeError("Built-in iterator is unavailable.", node);
1533
+ const cursor = self.builtinCursor(value);
1534
+ if (cursor !== undefined) {
1530
1535
  while (true) {
1531
1536
  const step = yield* cursor.next;
1532
1537
  if (step.done)
@@ -1603,13 +1608,13 @@ class Frame {
1603
1608
  const keyNode = property.key;
1604
1609
  let key;
1605
1610
  if (property.computed) {
1606
- key = self.toPropertyKey(yield* self.evaluateExpression(keyNode), keyNode);
1611
+ key = self.toPropertyKey(yield* self.evaluateExpression(keyNode));
1607
1612
  }
1608
1613
  else if (keyNode.type === "Identifier") {
1609
1614
  key = keyNode.name;
1610
1615
  }
1611
1616
  else if (keyNode.type === "Literal") {
1612
- key = self.toPropertyKey(keyNode.value, keyNode);
1617
+ key = self.toPropertyKey(literal(keyNode));
1613
1618
  }
1614
1619
  else {
1615
1620
  throw typeError("Unsupported object property key shape.", keyNode);
@@ -1700,10 +1705,10 @@ class Frame {
1700
1705
  if ((objectValue === null || objectValue === undefined) && node.optional)
1701
1706
  return OptionalShortCircuit;
1702
1707
  const key = node.computed
1703
- ? self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode)
1708
+ ? self.toPropertyKey(yield* self.evaluateExpression(propertyNode))
1704
1709
  : propertyNode.type === "Identifier"
1705
1710
  ? propertyNode.name
1706
- : self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode);
1711
+ : self.toPropertyKey(yield* self.evaluateExpression(propertyNode));
1707
1712
  if (objectValue instanceof ToolReference) {
1708
1713
  if (typeof key !== "string") {
1709
1714
  throw typeError("Tool paths must use string property names.", propertyNode);
@@ -1712,20 +1717,17 @@ class Frame {
1712
1717
  }
1713
1718
  if (objectValue instanceof Obj)
1714
1719
  return { target: objectValue, key, receiver: objectValue };
1715
- // Primitives read through their wrapper prototype without being boxed; strings own length and indexes.
1716
- const builtins = self.ctx.builtins;
1720
+ // Strings own length and indexes; every other primitive property reads through the wrapper prototype.
1717
1721
  if (typeof objectValue === "string") {
1718
1722
  if (key === "length")
1719
1723
  return { value: objectValue.length };
1720
1724
  const index = typeof key === "symbol" ? undefined : parseArrayIndex(key);
1721
1725
  if (index !== undefined)
1722
1726
  return { value: objectValue[index] };
1723
- return { target: builtins.String, key, receiver: objectValue };
1724
1727
  }
1725
- if (typeof objectValue === "number")
1726
- return { target: builtins.Number, key, receiver: objectValue };
1727
- if (typeof objectValue === "boolean")
1728
- return { target: builtins.Boolean, key, receiver: objectValue };
1728
+ const proto = primitivePrototype(self.ctx.builtins, objectValue);
1729
+ if (proto !== undefined)
1730
+ return { target: proto, key, receiver: objectValue };
1729
1731
  if (objectValue === null || objectValue === undefined) {
1730
1732
  throw typeError(`Cannot read properties of ${objectValue} (reading '${String(key)}').`, objectNode);
1731
1733
  }
@@ -1764,9 +1766,11 @@ class Frame {
1764
1766
  }
1765
1767
  evaluateDeleteExpression(argument) {
1766
1768
  const target = argument.type === "ChainExpression" ? argument.expression : argument;
1767
- if (target.type !== "MemberExpression") {
1769
+ if (target.type === "Identifier")
1768
1770
  throw typeError("Only data fields may be deleted.", argument);
1769
- }
1771
+ // `delete <non-reference>` evaluates the operand and is true, as in JS.
1772
+ if (target.type !== "MemberExpression")
1773
+ return Effect.map(this.evaluateExpression(target), () => true);
1770
1774
  return Effect.map(this.getMemberReference(target), (reference) => {
1771
1775
  if (reference === OptionalShortCircuit)
1772
1776
  return true;
@@ -1812,12 +1816,10 @@ class Frame {
1812
1816
  throw rangeError("Invalid array length", node);
1813
1817
  throw typeError(`Cannot assign to read only property '${String(key)}'.`, node);
1814
1818
  }
1815
- toPropertyKey(value, node) {
1816
- if (typeof value === "string" || typeof value === "number") {
1817
- return value;
1818
- }
1819
- if (value === AsyncIteratorSymbol || value === IteratorSymbol)
1819
+ // ToPropertyKey: anything else becomes its string form, so `counts[row.category]` works when the field is null.
1820
+ toPropertyKey(value) {
1821
+ if (typeof value === "string" || typeof value === "number" || typeof value === "symbol")
1820
1822
  return value;
1821
- throw typeError("Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.", node);
1823
+ return coerceToString(value);
1822
1824
  }
1823
1825
  }
@@ -1,13 +1,15 @@
1
- import { ErrorObj, Obj } from "./objects.js";
1
+ import { ErrorObj, Obj, type Value } from "./objects.js";
2
2
  export declare const errorTypes: readonly ["Error", "TypeError", "RangeError", "SyntaxError", "ReferenceError", "EvalError", "URIError", "AggregateError"];
3
3
  export type ErrorType = (typeof errorTypes)[number];
4
4
  export declare const isErrorType: (name: string) => name is ErrorType;
5
- declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Headers", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "AsyncIterator", "Generator", "AsyncGenerator"];
5
+ declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Headers", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "IteratorHelper", "AsyncIterator", "Generator", "AsyncGenerator"];
6
6
  /**
7
7
  * The built-in prototype objects of one runtime, allocated empty in dependency order. The globals populate them
8
8
  * and attach their constructors when the runtime is built.
9
9
  */
10
10
  export type Builtins = Readonly<Record<(typeof builtins)[number] | ErrorType, Obj>>;
11
11
  export declare const createErrorValue: (prototype: Obj, message: string | undefined) => ErrorObj;
12
+ /** The prototype a primitive reads its methods from without being boxed; none for null, undefined, and symbols. */
13
+ export declare const primitivePrototype: (builtins: Builtins, value: Value) => Obj | undefined;
12
14
  export declare const createBuiltins: () => Builtins;
13
15
  export {};
@@ -30,6 +30,7 @@ const builtins = [
30
30
  "TextDecoder",
31
31
  "Promise",
32
32
  "Iterator",
33
+ "IteratorHelper",
33
34
  "AsyncIterator",
34
35
  "Generator",
35
36
  "AsyncGenerator",
@@ -40,6 +41,16 @@ export const createErrorValue = (prototype, message) => {
40
41
  define(value, "message", message, hidden);
41
42
  return value;
42
43
  };
44
+ /** The prototype a primitive reads its methods from without being boxed; none for null, undefined, and symbols. */
45
+ export const primitivePrototype = (builtins, value) => {
46
+ if (typeof value === "string")
47
+ return builtins.String;
48
+ if (typeof value === "number")
49
+ return builtins.Number;
50
+ if (typeof value === "boolean")
51
+ return builtins.Boolean;
52
+ return undefined;
53
+ };
43
54
  export const createBuiltins = () => {
44
55
  const object = new Obj(null);
45
56
  // Function.prototype is itself callable and returns undefined.
@@ -75,6 +86,7 @@ export const createBuiltins = () => {
75
86
  TextDecoder: plain(),
76
87
  Promise: plain(),
77
88
  Iterator: iterator,
89
+ IteratorHelper: new Obj(iterator),
78
90
  AsyncIterator: asyncIterator,
79
91
  Generator: new Obj(iterator),
80
92
  AsyncGenerator: new Obj(asyncIterator),
@@ -2,7 +2,7 @@ import type { Node } from "acorn";
2
2
  import { Context } from "effect";
3
3
  import type { ErrorType } from "./intrinsics.js";
4
4
  import type { DiagnosticKind } from "../codemode.js";
5
- import type { ErrorObj } from "./objects.js";
5
+ import type { ErrorObj, Value } from "./objects.js";
6
6
  /** Any parsed node; the interpreter narrows on `type` and reads `loc` for diagnostics. */
7
7
  export type AstNode = Node;
8
8
  /** The program call a built-in is running under: where to locate failures born inside it, and how deep the stack is there. */
@@ -12,14 +12,14 @@ export declare const CallSite: Context.Reference<{
12
12
  }>;
13
13
  export type Binding = {
14
14
  mutable: boolean;
15
- value: unknown;
15
+ value: Value;
16
16
  initialized?: boolean;
17
17
  };
18
18
  export type StatementResult = {
19
19
  kind: "none";
20
20
  } | {
21
21
  kind: "return";
22
- value: unknown;
22
+ value: Value;
23
23
  } | {
24
24
  kind: "break";
25
25
  label?: string;
@@ -32,12 +32,12 @@ export declare const AsyncIteratorSymbol: unique symbol;
32
32
  export declare const IteratorSymbol: unique symbol;
33
33
  export declare const IteratorSymbols: readonly [typeof AsyncIteratorSymbol, typeof IteratorSymbol];
34
34
  export declare class Throw {
35
- readonly value: unknown;
36
- constructor(value: unknown);
35
+ readonly value: Value;
36
+ constructor(value: Value);
37
37
  }
38
38
  export declare class GeneratorReturn {
39
- readonly value: unknown;
40
- constructor(value: unknown);
39
+ readonly value: Value;
40
+ constructor(value: Value);
41
41
  }
42
42
  export declare const OptionalShortCircuit: unique symbol;
43
43
  /**
@@ -64,7 +64,6 @@ export declare const syntaxError: (message: string, node?: AstNode) => PendingTh
64
64
  export declare const uriError: (message: string, node?: AstNode) => PendingThrow;
65
65
  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.";
66
66
  export declare const unsupportedSyntax: (kind: string, node: AstNode) => PendingThrow;
67
- export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
68
67
  export declare const sourceLocation: (node: AstNode) => {
69
68
  readonly line: number;
70
69
  readonly column: number;
@@ -51,10 +51,10 @@ export const uriError = failure("URIError");
51
51
  // Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
52
52
  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.";
53
53
  export const unsupportedSyntax = (kind, node) => new PendingThrow("SyntaxError", `Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage]);
54
- export const isRecord = (value) => typeof value === "object" && value !== null;
54
+ // Acorn lines are 1-based and its columns are 0-based. Diagnostics use 1-based columns of the submitted source.
55
55
  export const sourceLocation = (node) => ({
56
- line: Math.max(1, (node.loc?.start.line ?? 2) - 1),
57
- column: Math.max(1, (node.loc?.start.column ?? 4) - 3),
56
+ line: node.loc?.start.line ?? 1,
57
+ column: (node.loc?.start.column ?? 0) + 1,
58
58
  });
59
59
  export const formatLocation = (node) => {
60
60
  if (!node?.loc)
@@ -1,13 +1,13 @@
1
1
  import { Effect } from "effect";
2
2
  import type { Builtins } from "./intrinsics.js";
3
- import { type Callable, Native, type NativeOptions, Obj } from "./objects.js";
4
- /** A native function body: a plain value, a thrown `PendingThrow`, or an Effect. */
5
- export type Impl = (thisValue: unknown, args: Array<unknown>) => unknown;
3
+ import { type Callable, Native, type NativeOptions, Obj, type Value } from "./objects.js";
4
+ /** A native function body: a value, a thrown `PendingThrow`, or an Effect of a value. */
5
+ export type Impl = (thisValue: Value, args: Array<Value>) => Value | Effect.Effect<Value, unknown, unknown>;
6
6
  export declare const native: <R>(builtins: Builtins, options: NativeOptions<R>) => Native<R>;
7
7
  export declare const fn: <R>(builtins: Builtins, name: string, length: number, impl: Impl) => Native<R>;
8
8
  export type Method = readonly [name: string, length: number, impl: Impl];
9
9
  export declare const methods: (builtins: Builtins, target: Obj, table: ReadonlyArray<Method>) => void;
10
- export declare const constants: (target: Obj, table: Record<string, unknown>) => void;
10
+ export declare const constants: (target: Obj, table: Record<string, Value>) => void;
11
11
  /** A constructor wired to its prototype: `C.prototype === proto` and `proto.constructor === C`. */
12
12
  export declare const constructor: <R>(builtins: Builtins, proto: Obj, options: NativeOptions<R>) => Native<R>;
13
13
  /** The `call` of a constructor that JS requires to be invoked with `new`. */
@@ -15,4 +15,4 @@ export declare const requiresNew: (name: string) => () => Effect.Effect<never, u
15
15
  /** The instance prototype for `new` via `newTarget.prototype`, falling back to the built-in's own. */
16
16
  export declare const prototypeFrom: (newTarget: Callable, fallback: Obj) => Obj;
17
17
  /** Narrows a method receiver to the built-in it belongs to, or throws the TypeError JS would. */
18
- export declare const receiver: <T extends Obj>(cls: abstract new (...args: never) => T, thisValue: unknown, method: string) => T;
18
+ export declare const receiver: <T extends Obj>(cls: abstract new (...args: never) => T, thisValue: Value, method: string) => T;