@opencode/codemode 2.0.15 → 2.0.17

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.
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { constructor, fn, methods, prototypeFrom, receiver, requiresNew } from "../interpreter/native.js";
3
3
  import { invalidData, IteratorSymbol, typeError } from "../interpreter/model.js";
4
- import { define, defineAccessor, get, getOwn, hidden, Arr, coerceToString, hostCursor, hostIterator, MapObj, Obj, PromiseObj, SetObj, } from "../interpreter/objects.js";
4
+ import { define, defineAccessor, get, getOwn, hidden, Arr, coerceToString, hostCursor, hostIterator, MapObj, Obj, PromiseObj, SetObj, WeakMapObj, WeakSetObj, } from "../interpreter/objects.js";
5
5
  import { describeValue, isOpaque } from "../interpreter/references.js";
6
6
  import { applyCollectionCallback, isSupportedCallback, preserveConsumerError, toPrimitiveNumber, toPrimitiveString, } from "../interpreter/callback.js";
7
7
  const coerceGroupByPropertyKey = (ctx, value) => {
@@ -167,7 +167,7 @@ export const mapGlobal = (ctx) => {
167
167
  const apply = applyCollectionCallback(ctx, args[0], "Map.forEach");
168
168
  return Effect.gen(function* () {
169
169
  for (const [key, item] of target.map.entries())
170
- yield* apply([item, key, target]);
170
+ yield* apply([item, key, target], args[1]);
171
171
  return undefined;
172
172
  });
173
173
  },
@@ -357,7 +357,7 @@ export const setGlobal = (ctx) => {
357
357
  const apply = applyCollectionCallback(ctx, args[0], "Set.forEach");
358
358
  return Effect.gen(function* () {
359
359
  for (const item of target.set.values())
360
- yield* apply([item, item, target]);
360
+ yield* apply([item, item, target], args[1]);
361
361
  return undefined;
362
362
  });
363
363
  },
@@ -373,3 +373,130 @@ export const setGlobal = (ctx) => {
373
373
  define(proto, IteratorSymbol, get(proto, "values"), hidden);
374
374
  return set;
375
375
  };
376
+ // CanBeHeldWeakly: only program objects; tool references are rebuilt on every access, so they could never be found again.
377
+ const weakKey = (value, label) => {
378
+ if (value instanceof Obj)
379
+ return value;
380
+ throw typeError(`Invalid value used ${label}: ${describeValue(value)} cannot be held weakly.`);
381
+ };
382
+ export const weakMapGlobal = (ctx) => {
383
+ const builtins = ctx.builtins;
384
+ const proto = builtins.WeakMap;
385
+ const weakMap = constructor(builtins, proto, {
386
+ name: "WeakMap",
387
+ call: requiresNew("WeakMap"),
388
+ construct: (args, newTarget) => {
389
+ const target = new WeakMapObj(prototypeFrom(newTarget, proto));
390
+ if (args[0] === undefined || args[0] === null)
391
+ return Effect.succeed(target);
392
+ return Effect.gen(function* () {
393
+ const cursor = yield* ctx.iterate(args[0]);
394
+ if (cursor === undefined) {
395
+ throw typeError(`new WeakMap(...) expects an iterable of [key, value] pairs, received ${describeValue(args[0])}.`);
396
+ }
397
+ while (true) {
398
+ const step = yield* cursor.next;
399
+ if (step.done)
400
+ return target;
401
+ yield* preserveConsumerError(cursor.close, Effect.sync(() => {
402
+ if (!(step.value instanceof Obj)) {
403
+ throw typeError("new WeakMap(...) expects [key, value] pairs as entry objects.");
404
+ }
405
+ target.map.set(weakKey(getOwn(step.value, 0), "as weak map key"), getOwn(step.value, 1));
406
+ }));
407
+ }
408
+ });
409
+ },
410
+ });
411
+ const self = (thisValue, name) => receiver(WeakMapObj, thisValue, `WeakMap.prototype.${name}`).map;
412
+ // Lookups pass any key through: the host collection answers false for a non-object, as the spec requires.
413
+ const key = (value) => weakKey(value, "as weak map key");
414
+ methods(builtins, proto, [
415
+ [
416
+ "get",
417
+ 1,
418
+ (thisValue, args) => {
419
+ const target = self(thisValue, "get");
420
+ return args[0] instanceof Obj ? target.get(args[0]) : undefined;
421
+ },
422
+ ],
423
+ ["has", 1, (thisValue, args) => self(thisValue, "has").has(args[0])],
424
+ ["delete", 1, (thisValue, args) => self(thisValue, "delete").delete(args[0])],
425
+ [
426
+ "set",
427
+ 2,
428
+ (thisValue, args) => {
429
+ self(thisValue, "set").set(key(args[0]), args[1]);
430
+ return thisValue;
431
+ },
432
+ ],
433
+ [
434
+ "getOrInsert",
435
+ 2,
436
+ (thisValue, args) => {
437
+ const target = self(thisValue, "getOrInsert");
438
+ const k = key(args[0]);
439
+ if (!target.has(k))
440
+ target.set(k, args[1]);
441
+ return target.get(k);
442
+ },
443
+ ],
444
+ [
445
+ "getOrInsertComputed",
446
+ 2,
447
+ (thisValue, args) => {
448
+ const target = self(thisValue, "getOrInsertComputed");
449
+ const k = key(args[0]);
450
+ const apply = applyCollectionCallback(ctx, args[1], "WeakMap.getOrInsertComputed");
451
+ if (target.has(k))
452
+ return target.get(k);
453
+ return Effect.map(apply([k]), (value) => {
454
+ target.set(k, value);
455
+ return value;
456
+ });
457
+ },
458
+ ],
459
+ ]);
460
+ return weakMap;
461
+ };
462
+ export const weakSetGlobal = (ctx) => {
463
+ const builtins = ctx.builtins;
464
+ const proto = builtins.WeakSet;
465
+ const weakSet = constructor(builtins, proto, {
466
+ name: "WeakSet",
467
+ call: requiresNew("WeakSet"),
468
+ construct: (args, newTarget) => {
469
+ const target = new WeakSetObj(prototypeFrom(newTarget, proto));
470
+ if (args[0] === undefined || args[0] === null)
471
+ return Effect.succeed(target);
472
+ return Effect.gen(function* () {
473
+ const cursor = yield* ctx.iterate(args[0]);
474
+ if (cursor === undefined) {
475
+ throw typeError(`new WeakSet(...) expects a synchronous iterable, received ${describeValue(args[0])}.`);
476
+ }
477
+ while (true) {
478
+ const step = yield* cursor.next;
479
+ if (step.done)
480
+ return target;
481
+ yield* preserveConsumerError(cursor.close, Effect.sync(() => {
482
+ target.set.add(weakKey(step.value, "in weak set"));
483
+ }));
484
+ }
485
+ });
486
+ },
487
+ });
488
+ const self = (thisValue, name) => receiver(WeakSetObj, thisValue, `WeakSet.prototype.${name}`).set;
489
+ methods(builtins, proto, [
490
+ ["has", 1, (thisValue, args) => self(thisValue, "has").has(args[0])],
491
+ ["delete", 1, (thisValue, args) => self(thisValue, "delete").delete(args[0])],
492
+ [
493
+ "add",
494
+ 1,
495
+ (thisValue, args) => {
496
+ self(thisValue, "add").add(weakKey(args[0], "in weak set"));
497
+ return thisValue;
498
+ },
499
+ ],
500
+ ]);
501
+ return weakSet;
502
+ };
@@ -14,8 +14,8 @@ const constructDate = (ctx, args, proto) => {
14
14
  ? new DateObj(proto, Date.parse(value))
15
15
  : new DateObj(proto, new Date(coerceToNumber(value)).getTime()));
16
16
  }
17
- const parts = args.map((arg) => coerceToNumber(arg));
18
- return Effect.succeed(new DateObj(proto, new Date(...parts).getTime()));
17
+ // The spec converts at most seven components, in order, so extra arguments never run program code.
18
+ return Effect.map(Effect.forEach(args.slice(0, 7), (arg) => toPrimitiveNumber(ctx, arg), { concurrency: 1 }), (parts) => new DateObj(proto, new Date(...parts).getTime()));
19
19
  };
20
20
  const getters = [
21
21
  "getFullYear",
@@ -66,7 +66,11 @@ export const dateGlobal = (ctx) => {
66
66
  methods(builtins, date, [
67
67
  ["now", 0, () => Date.now()],
68
68
  ["parse", 1, (_, args) => Date.parse(coerceToString(args[0]))],
69
- ["UTC", 7, (_, args) => Date.UTC(...args.map((arg) => coerceToNumber(arg)))],
69
+ [
70
+ "UTC",
71
+ 7,
72
+ (_, args) => Effect.map(Effect.forEach(args.slice(0, 7), (arg) => toPrimitiveNumber(ctx, arg), { concurrency: 1 }), (parts) => Date.UTC(...parts)),
73
+ ],
70
74
  ]);
71
75
  const self = (thisValue, name) => receiver(DateObj, thisValue, `Date.prototype.${name}`);
72
76
  const iso = (value) => {
@@ -107,6 +111,9 @@ export const dateGlobal = (ctx) => {
107
111
  return Effect.map(Effect.forEach(args.slice(0, length), (arg) => toPrimitiveNumber(ctx, arg), {
108
112
  concurrency: 1,
109
113
  }), (values) => {
114
+ // Every setter but setTime and setFullYear leaves an invalid Date untouched and answers NaN.
115
+ if (Number.isNaN(hosted.getTime()) && name !== "setTime" && !name.endsWith("FullYear"))
116
+ return NaN;
110
117
  target.time = hosted[name](...values);
111
118
  return target.time;
112
119
  });
@@ -104,7 +104,7 @@ export const headersGlobal = (ctx) => {
104
104
  const apply = applyCollectionCallback(ctx, args[0], "Headers.forEach");
105
105
  return Effect.gen(function* () {
106
106
  for (const [key, value] of Array.from(target.headers.entries()))
107
- yield* apply([value, key, target]);
107
+ yield* apply([value, key, target], args[1]);
108
108
  return undefined;
109
109
  });
110
110
  },
@@ -38,7 +38,7 @@ const parse = (ctx, args) => {
38
38
  set(value, name, revived);
39
39
  }
40
40
  }
41
- return yield* apply([key, value]);
41
+ return yield* apply([key, value], holder);
42
42
  });
43
43
  return visit(record(ctx.builtins.Object, { "": parsed }), "");
44
44
  };
@@ -2,23 +2,27 @@ import { Effect } from "effect";
2
2
  import { constants, methods } from "../interpreter/native.js";
3
3
  import { typeError } from "../interpreter/model.js";
4
4
  import { Obj, coerceToNumber } from "../interpreter/objects.js";
5
- import { preserveConsumerError } from "../interpreter/callback.js";
6
- // Validate only the arguments a method consumes; like JS, extras are ignored
7
- // (so built-ins work as callbacks receiving (element, index, array)).
8
- const unary = (name, op) => [name, 1, (_, args) => op(coerceToNumber(args[0]))];
9
- const binary = (name, op) => [
10
- name,
11
- 2,
12
- (_, args) => op(coerceToNumber(args[0]), coerceToNumber(args[1])),
13
- ];
14
- const variadic = (name, op) => [
15
- name,
16
- 2,
17
- (_, args) => op(...args.map(coerceToNumber)),
18
- ];
5
+ import { preserveConsumerError, withPrimitives } from "../interpreter/callback.js";
19
6
  export const mathGlobal = (ctx) => {
20
7
  const builtins = ctx.builtins;
21
8
  const math = new Obj(builtins.Object);
9
+ // Convert only the arguments a method consumes; like JS, extras are ignored
10
+ // (so built-ins work as callbacks receiving (element, index, array)).
11
+ const unary = (name, op) => [
12
+ name,
13
+ 1,
14
+ (_, args) => withPrimitives(ctx, "number", [args[0]], ([a]) => op(coerceToNumber(a))),
15
+ ];
16
+ const binary = (name, op) => [
17
+ name,
18
+ 2,
19
+ (_, args) => withPrimitives(ctx, "number", [args[0], args[1]], ([a, b]) => op(coerceToNumber(a), coerceToNumber(b))),
20
+ ];
21
+ const variadic = (name, op) => [
22
+ name,
23
+ 2,
24
+ (_, args) => withPrimitives(ctx, "number", args, (values) => op(...values.map(coerceToNumber))),
25
+ ];
22
26
  constants(math, {
23
27
  PI: Math.PI,
24
28
  E: Math.E,
@@ -1,7 +1,8 @@
1
1
  import { constructor, constants, methods } from "../interpreter/native.js";
2
- import { coerceToNumber, coerceToString } from "../interpreter/objects.js";
2
+ import { coerceToNumber } from "../interpreter/objects.js";
3
3
  import { rangeError, typeError } from "../interpreter/model.js";
4
- import { coercion } from "./value.js";
4
+ import { withPrimitives } from "../interpreter/callback.js";
5
+ import { coerce, coercion } from "./value.js";
5
6
  export const numberGlobal = (ctx) => {
6
7
  const builtins = ctx.builtins;
7
8
  const number = constructor(builtins, builtins.Number, {
@@ -24,46 +25,34 @@ export const numberGlobal = (ctx) => {
24
25
  ["isFinite", 1, (_, args) => Number.isFinite(args[0])],
25
26
  ["isNaN", 1, (_, args) => Number.isNaN(args[0])],
26
27
  ["isSafeInteger", 1, (_, args) => Number.isSafeInteger(args[0])],
27
- [
28
- "parseInt",
29
- 2,
30
- (_, args) => {
31
- return parseInt(coerceToString(args[0]), coerceToNumber(args[1]));
32
- },
33
- ],
34
- ["parseFloat", 1, (_, args) => parseFloat(coerceToString(args[0]))],
28
+ ["parseInt", 2, (_, args) => coerce(ctx, "parseInt", args)],
29
+ ["parseFloat", 1, (_, args) => coerce(ctx, "parseFloat", args)],
35
30
  ]);
36
31
  const self = (thisValue, name) => {
37
32
  if (typeof thisValue === "number")
38
33
  return thisValue;
39
34
  throw typeError(`Number.prototype.${name} requires that 'this' be a Number.`);
40
35
  };
41
- const optNum = (arg) => (arg === undefined ? undefined : coerceToNumber(arg));
36
+ // The receiver is checked first, then the one argument converts through ToPrimitive with the number hint.
37
+ const formatting = (name, op) => [
38
+ name,
39
+ 1,
40
+ (thisValue, args) => {
41
+ const value = self(thisValue, name);
42
+ return withPrimitives(ctx, "number", [args[0]], ([digits]) => op(value, digits === undefined ? undefined : coerceToNumber(digits)));
43
+ },
44
+ ];
42
45
  methods(builtins, builtins.Number, [
43
- ["toFixed", 1, (thisValue, args) => self(thisValue, "toFixed").toFixed(optNum(args[0]))],
46
+ formatting("toFixed", (value, digits) => value.toFixed(digits)),
44
47
  ["toLocaleString", 0, (thisValue) => self(thisValue, "toLocaleString").toLocaleString("en-US")],
45
- ["toExponential", 1, (thisValue, args) => self(thisValue, "toExponential").toExponential(optNum(args[0]))],
46
- [
47
- "toPrecision",
48
- 1,
49
- (thisValue, args) => {
50
- const value = self(thisValue, "toPrecision");
51
- const digits = optNum(args[0]);
52
- return digits === undefined ? value.toString() : value.toPrecision(digits);
53
- },
54
- ],
55
- [
56
- "toString",
57
- 1,
58
- (thisValue, args) => {
59
- const value = self(thisValue, "toString");
60
- const radix = optNum(args[0]);
61
- if (radix !== undefined && (radix < 2 || radix > 36)) {
62
- throw rangeError("Number.toString radix must be between 2 and 36.");
63
- }
64
- return value.toString(radix);
65
- },
66
- ],
48
+ formatting("toExponential", (value, digits) => value.toExponential(digits)),
49
+ formatting("toPrecision", (value, digits) => (digits === undefined ? value.toString() : value.toPrecision(digits))),
50
+ formatting("toString", (value, radix) => {
51
+ if (radix !== undefined && (radix < 2 || radix > 36)) {
52
+ throw rangeError("Number.toString radix must be between 2 and 36.");
53
+ }
54
+ return value.toString(radix);
55
+ }),
67
56
  ["valueOf", 0, (thisValue) => self(thisValue, "valueOf")],
68
57
  ]);
69
58
  return number;
@@ -1,7 +1,8 @@
1
1
  import { Effect } from "effect";
2
2
  import { constructor, methods, receiver } from "../interpreter/native.js";
3
- import { AsyncIteratorSymbol, invalidData, IteratorSymbol, rangeError, typeError, } from "../interpreter/model.js";
4
- import { define, entries, enumerableKeys, getOwn, hasOwn, hasPrototype, hidden, keys, own, Arr, Obj, PromiseObj, set, coerceToString, } from "../interpreter/objects.js";
3
+ import { AsyncIteratorSymbol, invalidData, IteratorSymbol, typeError } from "../interpreter/model.js";
4
+ import { define, entries, enumerableKeys, getOwn, hasOwn, hasPrototype, hidden, keys, own, ownKeys, Arr, Bytes, Obj, PromiseObj, set, coerceToString, } from "../interpreter/objects.js";
5
+ import { primitivePrototype } from "../interpreter/intrinsics.js";
5
6
  import { containsOpaqueReference, describeValue, rejectCircularInsertion } from "../interpreter/references.js";
6
7
  import { invoke, preserveConsumerError } from "../interpreter/callback.js";
7
8
  import { ToolReference } from "../tool-runtime.js";
@@ -37,8 +38,6 @@ export const objectAssign = (ctx, args) => {
37
38
  for (const key of enumerableKeys(from)) {
38
39
  rejectCircularInsertion(target, getOwn(from, key), "Object.assign result", seen);
39
40
  if (!set(target, key, getOwn(from, key))) {
40
- if (target instanceof Arr && key === "length")
41
- throw rangeError("Invalid array length");
42
41
  throw typeError(`Cannot assign to read only property '${String(key)}'.`);
43
42
  }
44
43
  }
@@ -81,6 +80,38 @@ const classTag = (value) => {
81
80
  return "Object";
82
81
  };
83
82
  const propertyKey = (value) => value === AsyncIteratorSymbol || value === IteratorSymbol ? value : coerceToString(value);
83
+ // SetIntegrityLevel: primitives pass through. A typed array's bytes cannot carry attributes, so JS throws after
84
+ // already making it non-extensible.
85
+ const restrict = (level, value) => {
86
+ if (!(value instanceof Obj))
87
+ return value;
88
+ value.extensible = false;
89
+ if (level === "preventExtensions")
90
+ return value;
91
+ if (value instanceof Bytes && value.bytes.length > 0) {
92
+ throw typeError(`Cannot ${level} array buffer views with elements.`);
93
+ }
94
+ for (const slot of value.props.values()) {
95
+ slot.configurable = false;
96
+ if (level === "freeze" && "value" in slot)
97
+ slot.writable = false;
98
+ }
99
+ if (value instanceof Arr)
100
+ value.elements = { writable: level === "seal", enumerable: true, configurable: false };
101
+ return value;
102
+ };
103
+ // TestIntegrityLevel: array elements and `length` answer through `own`, so a non-extensible empty array is sealed
104
+ // but not frozen, as in JS.
105
+ const integrity = (value, frozen) => {
106
+ if (!(value instanceof Obj))
107
+ return true;
108
+ if (value.extensible)
109
+ return false;
110
+ return ownKeys(value).every((key) => {
111
+ const slot = own(value, key);
112
+ return slot !== undefined && !slot.configurable && !(frozen && "value" in slot && slot.writable);
113
+ });
114
+ };
84
115
  // Object constructs identically with or without new, like JS. Only `keys` copies its result into the
85
116
  // program; `values`, `entries`, `assign`, and `fromEntries` hand back the program's own values.
86
117
  export const objectGlobal = (ctx) => {
@@ -118,18 +149,41 @@ export const objectGlobal = (ctx) => {
118
149
  (_, args) => new Arr(builtins.Array, entries(enumerableSource(ctx, "Object.entries(...)", args[0])).map((entry) => new Arr(builtins.Array, entry))),
119
150
  ],
120
151
  ["hasOwn", 2, (_, args) => hasOwn(enumerableSource(ctx, "Object.hasOwn(...)", args[0]), propertyKey(args[1]))],
152
+ ["is", 2, (_, args) => Object.is(args[0], args[1])],
153
+ ["assign", 2, (_, args) => objectAssign(ctx, args)],
154
+ ["fromEntries", 1, (_, args) => objectFromEntries(ctx, args[0])],
155
+ ["freeze", 1, (_, args) => restrict("freeze", args[0])],
156
+ ["seal", 1, (_, args) => restrict("seal", args[0])],
157
+ ["preventExtensions", 1, (_, args) => restrict("preventExtensions", args[0])],
158
+ ["isFrozen", 1, (_, args) => integrity(args[0], true)],
159
+ ["isSealed", 1, (_, args) => integrity(args[0], false)],
160
+ ["isExtensible", 1, (_, args) => args[0] instanceof Obj && args[0].extensible],
121
161
  [
122
- "is",
162
+ "getPrototypeOf",
163
+ 1,
164
+ (_, args) => {
165
+ if (args[0] instanceof Obj)
166
+ return args[0].proto;
167
+ const proto = primitivePrototype(builtins, args[0]);
168
+ if (proto === undefined) {
169
+ throw typeError(`Object.getPrototypeOf cannot convert ${describeValue(args[0])} to an object.`);
170
+ }
171
+ return proto;
172
+ },
173
+ ],
174
+ [
175
+ "create",
123
176
  2,
124
177
  (_, args) => {
125
- if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
126
- throw invalidData("Object.is requires data values.");
178
+ if (args[0] !== null && !(args[0] instanceof Obj)) {
179
+ throw typeError("Object prototype may only be an Object or null.");
180
+ }
181
+ if (args[1] !== undefined) {
182
+ throw typeError("Object.create property descriptors are not supported; assign the fields after creating the object.");
127
183
  }
128
- return Object.is(args[0], args[1]);
184
+ return new Obj(args[0]);
129
185
  },
130
186
  ],
131
- ["assign", 2, (_, args) => objectAssign(ctx, args)],
132
- ["fromEntries", 1, (_, args) => objectFromEntries(ctx, args[0])],
133
187
  ]);
134
188
  define(object, "groupBy", groupBy(ctx, "Object"), hidden);
135
189
  methods(builtins, builtins.Object, [