@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.
@@ -49,7 +49,7 @@ export const referenceError = failure("ReferenceError");
49
49
  export const syntaxError = failure("SyntaxError");
50
50
  export const uriError = failure("URIError");
51
51
  // Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
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, BigInt, and custom Symbols. Use plain functions and data objects instead.";
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, getters/setters, 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
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) => ({
@@ -38,6 +38,8 @@ export declare const frozen: Attributes;
38
38
  export declare class Obj {
39
39
  proto: Obj | null;
40
40
  readonly props: Map<string | symbol, Slot>;
41
+ /** [[Extensible]]: cleared by `Object.preventExtensions`, `seal`, and `freeze`. */
42
+ extensible: boolean;
41
43
  constructor(proto: Obj | null);
42
44
  /** The class name `Object.prototype.toString` reports: `[object Map]`. */
43
45
  readonly tag: string;
@@ -59,6 +61,8 @@ export declare class Obj {
59
61
  export declare class Arr extends Obj {
60
62
  readonly items: Array<Value>;
61
63
  readonly tag = "Array";
64
+ /** The attributes every live element shares; `seal` and `freeze` narrow them since elements have no slots. */
65
+ elements: Attributes;
62
66
  constructor(proto: Obj, items?: Array<Value>);
63
67
  get describe(): string;
64
68
  toString(): string;
@@ -80,6 +84,7 @@ export declare abstract class Opaque extends Obj {
80
84
  inspect(): string;
81
85
  }
82
86
  export declare abstract class Callable extends Opaque {
87
+ readonly length: number;
83
88
  readonly tag = "Function";
84
89
  constructor(proto: Obj, name: string, length: number);
85
90
  get describe(): string;
@@ -90,7 +95,17 @@ export declare class Fn extends Callable {
90
95
  readonly capturedScopes: Array<Map<string, Binding>>;
91
96
  readonly async: boolean;
92
97
  readonly generator: boolean;
93
- constructor(proto: Obj, name: string, parameters: ReadonlyArray<Pattern>, body: BlockStatement | Expression, capturedScopes: Array<Map<string, Binding>>, async: boolean, generator: boolean);
98
+ /** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
99
+ readonly arrow: boolean;
100
+ constructor(proto: Obj, name: string, parameters: ReadonlyArray<Pattern>, body: BlockStatement | Expression, capturedScopes: Array<Map<string, Binding>>, async: boolean, generator: boolean,
101
+ /** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
102
+ arrow: boolean);
103
+ }
104
+ /** The strict `arguments` object: an ordinary object with indexed own properties and a hidden `length`. */
105
+ export declare class Arguments extends Obj {
106
+ readonly tag = "Arguments";
107
+ constructor(proto: Obj, args: Array<Value>);
108
+ iterator(): ArrayIterator<Value>;
94
109
  }
95
110
  export type NativeCall<R> = (thisValue: Value, args: Array<Value>) => Effect.Effect<Value, unknown, R>;
96
111
  export type NativeConstruct<R> = (args: Array<Value>, newTarget: Callable) => Effect.Effect<Value, unknown, R>;
@@ -177,6 +192,17 @@ export declare class SetObj extends Wrapper {
177
192
  toHost(item: (value: Value) => unknown): Set<unknown>;
178
193
  iterator(): SetIterator<Value>;
179
194
  }
195
+ /** Keys are program objects, so a host WeakMap gives the same lifetime rule as JavaScript without any bookkeeping. */
196
+ export declare class WeakMapObj extends Wrapper {
197
+ readonly tag = "WeakMap";
198
+ readonly map: WeakMap<Obj, Value>;
199
+ inspect(): string;
200
+ }
201
+ export declare class WeakSetObj extends Wrapper {
202
+ readonly tag = "WeakSet";
203
+ readonly set: WeakSet<Obj>;
204
+ inspect(): string;
205
+ }
180
206
  export declare class URLSearchParamsObj extends Wrapper {
181
207
  readonly params: URLSearchParams;
182
208
  readonly tag = "URLSearchParams";
@@ -232,6 +258,8 @@ export declare const has: (target: Obj, key: PropertyKey) => boolean;
232
258
  export declare const hasPrototype: (value: Value, proto: Obj) => boolean;
233
259
  /** [[Set]]: an inherited setter or read-only property decides before an own data property is created. */
234
260
  export declare const set: (target: Obj, key: PropertyKey, value: Value) => boolean;
261
+ /** Creating a property on a non-extensible object is the one [[Set]] failure with its own message. */
262
+ export declare const rejectAddition: (target: Obj, key: string | symbol | number) => void;
235
263
  /** [[DefineOwnProperty]] for a data property, ignoring the chain. */
236
264
  export declare const define: (target: Obj, key: PropertyKey, value: Value, attrs?: Attributes) => void;
237
265
  export declare const defineAccessor: (target: Obj, key: PropertyKey, get: Getter | undefined, set?: Setter) => void;
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { ToolReference } from "../tool-runtime.js";
3
3
  import { checkArrayLength } from "./limits.js";
4
- import { AsyncIteratorSymbol, IteratorSymbol, } from "./model.js";
4
+ import { AsyncIteratorSymbol, IteratorSymbol, rangeError, typeError, } from "./model.js";
5
5
  /** Ordinary assignment: writable, enumerable, configurable. */
6
6
  export const data = { writable: true, enumerable: true, configurable: true };
7
7
  /** Built-in methods and `constructor`: writable and configurable but hidden from enumeration. */
@@ -17,6 +17,8 @@ export const frozen = { writable: false, enumerable: false, configurable: false
17
17
  export class Obj {
18
18
  proto;
19
19
  props = new Map();
20
+ /** [[Extensible]]: cleared by `Object.preventExtensions`, `seal`, and `freeze`. */
21
+ extensible = true;
20
22
  constructor(proto) {
21
23
  this.proto = proto;
22
24
  }
@@ -60,6 +62,8 @@ export class Obj {
60
62
  export class Arr extends Obj {
61
63
  items;
62
64
  tag = "Array";
65
+ /** The attributes every live element shares; `seal` and `freeze` narrow them since elements have no slots. */
66
+ elements = data;
63
67
  constructor(proto, items = []) {
64
68
  super(proto);
65
69
  this.items = items;
@@ -108,9 +112,11 @@ export class Opaque extends Obj {
108
112
  }
109
113
  }
110
114
  export class Callable extends Opaque {
115
+ length;
111
116
  tag = "Function";
112
117
  constructor(proto, name, length) {
113
118
  super(proto);
119
+ this.length = length;
114
120
  define(this, "length", length, readonly);
115
121
  define(this, "name", name, readonly);
116
122
  }
@@ -124,7 +130,10 @@ export class Fn extends Callable {
124
130
  capturedScopes;
125
131
  async;
126
132
  generator;
127
- constructor(proto, name, parameters, body, capturedScopes, async, generator) {
133
+ arrow;
134
+ constructor(proto, name, parameters, body, capturedScopes, async, generator,
135
+ /** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
136
+ arrow) {
128
137
  const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement");
129
138
  super(proto, name, optional === -1 ? parameters.length : optional);
130
139
  this.parameters = parameters;
@@ -132,6 +141,21 @@ export class Fn extends Callable {
132
141
  this.capturedScopes = capturedScopes;
133
142
  this.async = async;
134
143
  this.generator = generator;
144
+ this.arrow = arrow;
145
+ }
146
+ }
147
+ /** The strict `arguments` object: an ordinary object with indexed own properties and a hidden `length`. */
148
+ export class Arguments extends Obj {
149
+ tag = "Arguments";
150
+ constructor(proto, args) {
151
+ super(proto);
152
+ args.forEach((arg, index) => define(this, String(index), arg));
153
+ define(this, "length", args.length, hidden);
154
+ }
155
+ iterator() {
156
+ return keys(this)
157
+ .map((key) => get(this, key))
158
+ .values();
135
159
  }
136
160
  }
137
161
  export class Native extends Callable {
@@ -257,6 +281,21 @@ export class SetObj extends Wrapper {
257
281
  return this.set.values();
258
282
  }
259
283
  }
284
+ /** Keys are program objects, so a host WeakMap gives the same lifetime rule as JavaScript without any bookkeeping. */
285
+ export class WeakMapObj extends Wrapper {
286
+ tag = "WeakMap";
287
+ map = new WeakMap();
288
+ inspect() {
289
+ return "WeakMap { <items unknown> }";
290
+ }
291
+ }
292
+ export class WeakSetObj extends Wrapper {
293
+ tag = "WeakSet";
294
+ set = new WeakSet();
295
+ inspect() {
296
+ return "WeakSet { <items unknown> }";
297
+ }
298
+ }
260
299
  export class URLSearchParamsObj extends Wrapper {
261
300
  params;
262
301
  tag = "URLSearchParams";
@@ -363,10 +402,12 @@ export const own = (target, key) => {
363
402
  const at = index(target, name);
364
403
  if (at !== undefined) {
365
404
  const items = elements(target);
366
- return at in items ? { value: items[at], ...data } : undefined;
405
+ if (!(at in items))
406
+ return undefined;
407
+ return { value: items[at], ...(target instanceof Arr ? target.elements : data) };
367
408
  }
368
409
  if (target instanceof Arr && name === "length") {
369
- return { value: target.items.length, writable: true, enumerable: false, configurable: false };
410
+ return { value: target.items.length, writable: target.elements.writable, enumerable: false, configurable: false };
370
411
  }
371
412
  }
372
413
  return target.props.get(name);
@@ -403,16 +444,22 @@ export const hasPrototype = (value, proto) => {
403
444
  const writeElement = (target, name, value) => {
404
445
  const at = index(target, name);
405
446
  if (at !== undefined) {
406
- if (target instanceof Bytes)
447
+ if (target instanceof Bytes) {
407
448
  target.bytes[at] = typeof value === "number" ? value : Number(value);
408
- else
409
- target.items[at] = value;
449
+ return true;
450
+ }
451
+ if (!(at in target.items))
452
+ rejectAddition(target, at);
453
+ target.items[at] = value;
410
454
  return true;
411
455
  }
412
456
  if (!(target instanceof Arr) || name !== "length")
413
457
  return undefined;
414
458
  const length = typeof value === "number" ? value : Number(value);
415
459
  if (!Number.isInteger(length) || length < 0)
460
+ throw rangeError("Invalid array length");
461
+ // Shrinking deletes elements, which a sealed array forbids.
462
+ if (length < target.items.length && !target.elements.configurable)
416
463
  return false;
417
464
  checkArrayLength(length);
418
465
  target.items.length = length;
@@ -448,9 +495,16 @@ export const set = (target, key, value) => {
448
495
  if (written !== undefined)
449
496
  return written;
450
497
  }
498
+ rejectAddition(target, name);
451
499
  target.props.set(name, { value, ...data });
452
500
  return true;
453
501
  };
502
+ /** Creating a property on a non-extensible object is the one [[Set]] failure with its own message. */
503
+ export const rejectAddition = (target, key) => {
504
+ if (target.extensible)
505
+ return;
506
+ throw typeError(`Cannot add property ${String(key)}, object is not extensible.`);
507
+ };
454
508
  /** [[DefineOwnProperty]] for a data property, ignoring the chain. */
455
509
  export const define = (target, key, value, attrs = data) => {
456
510
  const name = canonical(key);
@@ -465,8 +519,13 @@ export const remove = (target, key) => {
465
519
  const name = canonical(key);
466
520
  if (isIndexed(target)) {
467
521
  const at = index(target, name);
468
- if (at !== undefined)
469
- return target instanceof Bytes ? !(at in target.bytes) : delete target.items[at];
522
+ if (at !== undefined) {
523
+ if (target instanceof Bytes)
524
+ return !(at in target.bytes);
525
+ if (at in target.items && !target.elements.configurable)
526
+ return false;
527
+ return delete target.items[at];
528
+ }
470
529
  if (target instanceof Arr && name === "length")
471
530
  return false;
472
531
  }
@@ -1,5 +1,11 @@
1
1
  import { Effect } from "effect";
2
- import { type Value } from "../interpreter/objects.js";
2
+ import { type Method } from "../interpreter/native.js";
3
+ import { Obj, type Value } from "../interpreter/objects.js";
3
4
  import type { Interpreter } from "../interpreter/interpreter.js";
4
5
  export declare const sortArray: <R>(ctx: Interpreter<R>, target: Array<Value>, comparator: Value, name: string) => Effect.Effect<Array<Value>, unknown, R>;
5
6
  export declare const arrayGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
7
+ /**
8
+ * The callback methods Array and Uint8Array share. They fix the iteration length while reading existing elements
9
+ * live; `wrap` builds the collection `map` and `filter` return.
10
+ */
11
+ export declare const callbackMethods: <R, T extends Obj>(ctx: Interpreter<R>, label: string, self: (thisValue: Value, name: string) => T, elements: (target: T) => ArrayLike<Value>, wrap: (values: Array<Value>) => Value) => Array<Method>;
@@ -2,9 +2,9 @@ import { Effect } from "effect";
2
2
  import { constructor, methods, prototypeFrom, receiver } from "../interpreter/native.js";
3
3
  import { checkArrayLength, checkStringLength, MAX_ARRAY_LENGTH } from "../interpreter/limits.js";
4
4
  import { invalidData, IteratorSymbol, rangeError, typeError } from "../interpreter/model.js";
5
- import { define, get, hidden, Arr, GeneratorObj, hostIterator, Obj, PromiseObj, coerceToInteger, coerceToNumber, coerceToString, } from "../interpreter/objects.js";
5
+ import { define, get, hidden, Arr, GeneratorObj, hostIterator, Obj, PromiseObj, coerceToInteger, coerceToNumber, coerceToString, rejectAddition, } from "../interpreter/objects.js";
6
6
  import { describeValue, rejectCircularInsertion } from "../interpreter/references.js";
7
- import { applyCollectionCallback, invoke, preserveConsumerError } from "../interpreter/callback.js";
7
+ import { applyCollectionCallback, invoke, preserveConsumerError, withPrimitives } from "../interpreter/callback.js";
8
8
  import { compareText } from "../tool-runtime.js";
9
9
  const arrayLikeSource = (source) => {
10
10
  // JS would treat a promise as an empty array-like; that would hide a missing `await`.
@@ -29,7 +29,7 @@ const arrayFrom = (ctx, args) => {
29
29
  const values = [];
30
30
  for (let index = 0; index < arrayLike.length; index += 1) {
31
31
  const item = get(arrayLike.source, index);
32
- values.push(apply === undefined ? item : yield* apply([item, index]));
32
+ values.push(apply === undefined ? item : yield* apply([item, index], args[2]));
33
33
  }
34
34
  return new Arr(proto, values);
35
35
  }
@@ -39,7 +39,9 @@ const arrayFrom = (ctx, args) => {
39
39
  const step = yield* cursor.next;
40
40
  if (step.done)
41
41
  return new Arr(proto, values);
42
- values.push(apply === undefined ? step.value : yield* preserveConsumerError(cursor.close, apply([step.value, index])));
42
+ values.push(apply === undefined
43
+ ? step.value
44
+ : yield* preserveConsumerError(cursor.close, apply([step.value, index], args[2])));
43
45
  index += 1;
44
46
  }
45
47
  });
@@ -102,34 +104,40 @@ export const arrayGlobal = (ctx) => {
102
104
  ["from", 1, (_, args) => arrayFrom(ctx, args)],
103
105
  ]);
104
106
  const self = (thisValue, name) => receiver(Arr, thisValue, `Array.prototype.${name}`);
107
+ // A mutating method fails where its first element write, delete, or `length` write would on a frozen, sealed, or
108
+ // non-extensible array. `growth` is given by the methods that always write `length`, even when it is 0; holes a
109
+ // method would fill on a non-extensible array are not checked.
110
+ const mutable = (target, growth) => {
111
+ const length = target.items.length;
112
+ if ((growth ?? 0) > 0)
113
+ rejectAddition(target, length);
114
+ if ((growth ?? 0) < 0 && length > 0 && !target.elements.configurable) {
115
+ throw typeError(`Cannot delete property '${length - 1}'.`);
116
+ }
117
+ if (!target.elements.writable && (length > 0 || growth !== undefined)) {
118
+ throw typeError(`Cannot assign to read only property '${length > 0 ? 0 : "length"}'.`);
119
+ }
120
+ return target;
121
+ };
105
122
  const optNumber = (value) => (value === undefined ? undefined : coerceToInteger(value));
106
- // Callback methods fix the iteration length while reading existing elements live.
107
- const iterate = (name, length, body) => [
108
- name,
109
- length,
110
- (thisValue, args) => {
111
- const target = self(thisValue, name);
112
- return body(target.items, target, applyCollectionCallback(ctx, args[0], `Array.${name}`), args);
113
- },
114
- ];
115
123
  methods(builtins, proto, [
116
124
  [
117
125
  "join",
118
126
  1,
119
127
  (thisValue, args) => {
120
- const joined = self(thisValue, "join")
121
- .items.map((item) => coerceToString(item ?? ""))
122
- .join(args[0] === undefined ? "," : coerceToString(args[0]));
123
- checkStringLength(joined.length);
124
- return joined;
128
+ // .map would keep holes, which Effect.forEach would then hand to the body as undefined.
129
+ const parts = Array.from(self(thisValue, "join").items, (item) => item ?? "");
130
+ return withPrimitives(ctx, "string", [args[0] === undefined ? "," : args[0], ...parts], ([separator, ...items]) => {
131
+ const joined = items.map(coerceToString).join(coerceToString(separator));
132
+ checkStringLength(joined.length);
133
+ return joined;
134
+ });
125
135
  },
126
136
  ],
127
137
  [
128
138
  "toString",
129
139
  0,
130
- (thisValue) => self(thisValue, "toString")
131
- .items.map((item) => coerceToString(item ?? ""))
132
- .join(","),
140
+ (thisValue) => withPrimitives(ctx, "string", Array.from(self(thisValue, "toString").items, (item) => item ?? ""), (items) => items.map(coerceToString).join(",")),
133
141
  ],
134
142
  [
135
143
  "includes",
@@ -144,7 +152,8 @@ export const arrayGlobal = (ctx) => {
144
152
  1,
145
153
  (thisValue, args) => {
146
154
  const target = self(thisValue, "lastIndexOf").items;
147
- return args[1] === undefined ? target.lastIndexOf(args[0]) : target.lastIndexOf(args[0], optNumber(args[1]));
155
+ // An explicit undefined is a fromIndex of 0, unlike omitting it.
156
+ return args.length < 2 ? target.lastIndexOf(args[0]) : target.lastIndexOf(args[0], optNumber(args[1]));
148
157
  },
149
158
  ],
150
159
  ["at", 1, (thisValue, args) => self(thisValue, "at").items.at(optNumber(args[0]) ?? 0)],
@@ -177,6 +186,9 @@ export const arrayGlobal = (ctx) => {
177
186
  0,
178
187
  (thisValue) => {
179
188
  const target = self(thisValue, "reverse");
189
+ // Fewer than two elements means no writes at all, so a frozen one-element array reverses fine.
190
+ if (target.items.length > 1)
191
+ mutable(target);
180
192
  target.items.reverse();
181
193
  return target;
182
194
  },
@@ -185,7 +197,7 @@ export const arrayGlobal = (ctx) => {
185
197
  "sort",
186
198
  1,
187
199
  (thisValue, args) => {
188
- const target = self(thisValue, "sort");
200
+ const target = mutable(self(thisValue, "sort"));
189
201
  const items = target.items;
190
202
  const length = items.length;
191
203
  const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(items, index)).filter((o) => !o).length;
@@ -225,7 +237,7 @@ export const arrayGlobal = (ctx) => {
225
237
  "push",
226
238
  1,
227
239
  (thisValue, args) => {
228
- const target = self(thisValue, "push");
240
+ const target = mutable(self(thisValue, "push"), args.length);
229
241
  // Validate all insertions before mutating to avoid partial cyclic updates.
230
242
  for (const item of args)
231
243
  rejectCircularInsertion(target, item, "Array.push result");
@@ -236,29 +248,28 @@ export const arrayGlobal = (ctx) => {
236
248
  "unshift",
237
249
  1,
238
250
  (thisValue, args) => {
239
- const target = self(thisValue, "unshift");
251
+ const target = mutable(self(thisValue, "unshift"), args.length);
240
252
  for (const item of args)
241
253
  rejectCircularInsertion(target, item, "Array.unshift result");
242
254
  return target.items.unshift(...args);
243
255
  },
244
256
  ],
245
- ["pop", 0, (thisValue) => self(thisValue, "pop").items.pop()],
246
- ["shift", 0, (thisValue) => self(thisValue, "shift").items.shift()],
257
+ ["pop", 0, (thisValue) => mutable(self(thisValue, "pop"), -1).items.pop()],
258
+ ["shift", 0, (thisValue) => mutable(self(thisValue, "shift"), -1).items.shift()],
247
259
  [
248
260
  "splice",
249
261
  2,
250
262
  (thisValue, args) => {
251
263
  const target = self(thisValue, "splice");
252
- if (args.length === 0)
253
- return wrap(target.items.splice(0, 0));
264
+ const length = target.items.length;
254
265
  const start = optNumber(args[0]) ?? 0;
255
- if (args.length === 1)
256
- return wrap(target.items.splice(start));
257
- const deleteCount = optNumber(args[1]) ?? 0;
266
+ const from = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
267
+ const deleteCount = args.length === 1 ? length - from : Math.min(Math.max(optNumber(args[1]) ?? 0, 0), length - from);
258
268
  const inserted = args.slice(2);
259
269
  for (const item of inserted)
260
270
  rejectCircularInsertion(target, item, "Array.splice result");
261
- return wrap(target.items.splice(start, deleteCount, ...inserted));
271
+ mutable(target, inserted.length - deleteCount);
272
+ return wrap(target.items.splice(from, deleteCount, ...inserted));
262
273
  },
263
274
  ],
264
275
  [
@@ -280,7 +291,7 @@ export const arrayGlobal = (ctx) => {
280
291
  "fill",
281
292
  1,
282
293
  (thisValue, args) => {
283
- const target = self(thisValue, "fill");
294
+ const target = mutable(self(thisValue, "fill"));
284
295
  rejectCircularInsertion(target, args[0], "Array.fill result");
285
296
  target.items.fill(args[0], optNumber(args[1]), optNumber(args[2]));
286
297
  return target;
@@ -290,7 +301,7 @@ export const arrayGlobal = (ctx) => {
290
301
  "copyWithin",
291
302
  2,
292
303
  (thisValue, args) => {
293
- const target = self(thisValue, "copyWithin");
304
+ const target = mutable(self(thisValue, "copyWithin"));
294
305
  target.items.copyWithin(optNumber(args[0]) ?? 0, optNumber(args[1]) ?? 0, optNumber(args[2]));
295
306
  return target;
296
307
  },
@@ -311,6 +322,50 @@ export const arrayGlobal = (ctx) => {
311
322
  .items.entries()
312
323
  .map(([index, item]) => wrap([index, item]))),
313
324
  ],
325
+ [
326
+ "flatMap",
327
+ 1,
328
+ (thisValue, args) => {
329
+ const target = self(thisValue, "flatMap");
330
+ const apply = applyCollectionCallback(ctx, args[0], "Array.flatMap");
331
+ return Effect.gen(function* () {
332
+ const length = target.items.length;
333
+ const values = [];
334
+ for (let index = 0; index < length; index += 1) {
335
+ if (!(index in target.items))
336
+ continue;
337
+ const mapped = yield* apply([target.items[index], index, target], args[1]);
338
+ if (mapped instanceof Arr)
339
+ values.push(...mapped.items);
340
+ else
341
+ values.push(mapped);
342
+ }
343
+ return wrap(values);
344
+ });
345
+ },
346
+ ],
347
+ ...callbackMethods(ctx, "Array", self, (target) => target.items, wrap),
348
+ ]);
349
+ define(proto, IteratorSymbol, get(proto, "values"), hidden);
350
+ return array;
351
+ };
352
+ /**
353
+ * The callback methods Array and Uint8Array share. They fix the iteration length while reading existing elements
354
+ * live; `wrap` builds the collection `map` and `filter` return.
355
+ */
356
+ export const callbackMethods = (ctx, label, self, elements, wrap) => {
357
+ const iterate = (name, length, body) => [
358
+ name,
359
+ length,
360
+ (thisValue, args) => {
361
+ const target = self(thisValue, name);
362
+ const call = applyCollectionCallback(ctx, args[0], `${label}.${name}`);
363
+ // reduce and reduceRight take an initial value where the others take a thisArg.
364
+ const thisArg = name.startsWith("reduce") ? undefined : args[1];
365
+ return body(elements(target), target, (callbackArgs) => call(callbackArgs, thisArg), args);
366
+ },
367
+ ];
368
+ return [
314
369
  iterate("map", 1, (target, receiver, apply) => Effect.gen(function* () {
315
370
  const length = target.length;
316
371
  const values = [];
@@ -322,20 +377,6 @@ export const arrayGlobal = (ctx) => {
322
377
  }
323
378
  return wrap(values);
324
379
  })),
325
- iterate("flatMap", 1, (target, receiver, apply) => Effect.gen(function* () {
326
- const length = target.length;
327
- const values = [];
328
- for (let index = 0; index < length; index += 1) {
329
- if (!(index in target))
330
- continue;
331
- const mapped = yield* apply([target[index], index, receiver]);
332
- if (mapped instanceof Arr)
333
- values.push(...mapped.items);
334
- else
335
- values.push(mapped);
336
- }
337
- return wrap(values);
338
- })),
339
380
  iterate("filter", 1, (target, receiver, apply) => Effect.gen(function* () {
340
381
  const length = target.length;
341
382
  const values = [];
@@ -416,7 +457,7 @@ export const arrayGlobal = (ctx) => {
416
457
  while (start < length && !(start in target))
417
458
  start += 1;
418
459
  if (start === length) {
419
- throw typeError("Array.reduce of an empty array with no initial value.");
460
+ throw typeError(`${label}.reduce of an empty array with no initial value.`);
420
461
  }
421
462
  accumulator = target[start];
422
463
  start += 1;
@@ -435,7 +476,7 @@ export const arrayGlobal = (ctx) => {
435
476
  while (start >= 0 && !(start in target))
436
477
  start -= 1;
437
478
  if (start < 0) {
438
- throw typeError("Array.reduceRight of an empty array with no initial value.");
479
+ throw typeError(`${label}.reduceRight of an empty array with no initial value.`);
439
480
  }
440
481
  accumulator = target[start];
441
482
  start -= 1;
@@ -447,7 +488,5 @@ export const arrayGlobal = (ctx) => {
447
488
  }
448
489
  return accumulator;
449
490
  })),
450
- ]);
451
- define(proto, IteratorSymbol, get(proto, "values"), hidden);
452
- return array;
491
+ ];
453
492
  };
@@ -4,6 +4,7 @@ import { constructor, methods, prototypeFrom, receiver, requiresNew } from "../i
4
4
  import { IteratorSymbol, rangeError, syntaxError, typeError } from "../interpreter/model.js";
5
5
  import { define, defineAccessor, get, hidden, Arr, Bytes, hostIterator, Obj, coerceToInteger, coerceToNumber, coerceToString, } from "../interpreter/objects.js";
6
6
  import { describeValue } from "../interpreter/references.js";
7
+ import { callbackMethods, sortArray } from "./array.js";
7
8
  /** The bytes a Uint8Array, array, or other iterable of numbers describes; the host array clamps each value. */
8
9
  const collectBytes = (ctx, source, name) => {
9
10
  if (source instanceof Bytes)
@@ -110,6 +111,22 @@ export const uint8ArrayGlobal = (ctx) => {
110
111
  return target;
111
112
  },
112
113
  ],
114
+ [
115
+ "sort",
116
+ 1,
117
+ (thisValue, args) => {
118
+ const target = self(thisValue, "sort");
119
+ // Without a comparator, typed arrays sort numerically rather than by string.
120
+ if (args[0] === undefined) {
121
+ target.bytes.sort();
122
+ return target;
123
+ }
124
+ return Effect.map(sortArray(ctx, [...target.bytes], args[0], "Uint8Array.sort"), (sorted) => {
125
+ target.bytes.set(Uint8Array.from(sorted, coerceToNumber));
126
+ return target;
127
+ });
128
+ },
129
+ ],
113
130
  [
114
131
  "indexOf",
115
132
  1,
@@ -120,7 +137,7 @@ export const uint8ArrayGlobal = (ctx) => {
120
137
  1,
121
138
  (thisValue, args) => {
122
139
  const target = self(thisValue, "lastIndexOf").bytes;
123
- return args[1] === undefined
140
+ return args.length < 2
124
141
  ? target.lastIndexOf(coerceToNumber(args[0]))
125
142
  : target.lastIndexOf(coerceToNumber(args[0]), optNumber(args[1]));
126
143
  },
@@ -151,6 +168,7 @@ export const uint8ArrayGlobal = (ctx) => {
151
168
  .bytes.entries()
152
169
  .map(([index, byte]) => wrapAll([index, byte]))),
153
170
  ],
171
+ ...callbackMethods(ctx, "Uint8Array", self, (target) => target.bytes, (values) => wrap(Uint8Array.from(values, coerceToNumber))),
154
172
  ]);
155
173
  define(proto, IteratorSymbol, get(proto, "values"), hidden);
156
174
  return uint8Array;
@@ -3,3 +3,5 @@ import type { Interpreter } from "../interpreter/interpreter.js";
3
3
  export declare const groupBy: <R>(ctx: Interpreter<R>, namespace: "Map" | "Object") => import("../interpreter/objects.js").Native<R>;
4
4
  export declare const mapGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
5
5
  export declare const setGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
6
+ export declare const weakMapGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
7
+ export declare const weakSetGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;