@opencode/codemode 0.0.0-beta-19422 → 0.0.0-beta-19500

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.
@@ -136,7 +136,7 @@ export declare const Result: Schema.Union<readonly [Schema.Struct<{
136
136
  export type Result = typeof Result.Type;
137
137
  /** Reusable confined runtime over explicit tools. */
138
138
  export type Runtime<R = never> = {
139
- readonly catalog: () => ReadonlyArray<ToolDescription>;
139
+ readonly catalog: ReadonlyArray<ToolDescription>;
140
140
  readonly execute: (code: string) => Effect.Effect<Result, never, R>;
141
141
  };
142
142
  /** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
package/dist/codemode.js CHANGED
@@ -60,7 +60,7 @@ export const make = (options = {}) => {
60
60
  const prepared = ToolRuntime.prepare((options.tools ?? {}));
61
61
  const limits = resolveExecutionLimits(options.limits);
62
62
  return {
63
- catalog: () => prepared.catalog,
63
+ catalog: prepared.catalog,
64
64
  execute: (code) => executeProgram(code, prepared, limits, options),
65
65
  };
66
66
  };
package/dist/data.d.ts CHANGED
@@ -1,21 +1,19 @@
1
1
  export * as Data from "./data.js";
2
2
  import type { DiagnosticKind } from "./codemode.js";
3
- /** A null-prototype object owned by the program. */
4
- export type SafeObject = Record<string, unknown>;
5
3
  export declare class ToolRuntimeError extends Error {
6
4
  readonly kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">;
7
5
  readonly suggestions: ReadonlyArray<string>;
8
6
  constructor(kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">, message: string, suggestions?: ReadonlyArray<string>);
9
7
  }
10
8
  /**
11
- * Brings a host-produced runtime value into the program: runtime values pass through, their host
12
- * counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects become
13
- * null-prototype copies. Arrays keep extra enumerable properties such as `index` and `groups`.
9
+ * Brings a host-produced value into the program: program and runtime values pass through, their
10
+ * host counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and host objects
11
+ * and arrays are copied.
14
12
  */
15
13
  export declare const toProgram: (value: unknown, label: string) => unknown;
16
14
  /**
17
15
  * Brings host data into the program: Date and URL become strings, other host collections become
18
- * empty objects, and objects become null-prototype copies. Used for tool results and parsed JSON.
16
+ * empty objects, and objects become program copies. Used for tool results and parsed JSON.
19
17
  */
20
18
  export declare const fromData: (value: unknown, label: string) => unknown;
21
19
  /**
package/dist/data.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * as Data from "./data.js";
2
+ import { ownEntries, parseArrayIndex, ProgramArray, ProgramFunction, ProgramObject, set, } from "./interpreter/objects.js";
2
3
  import { Values } from "./values.js";
3
4
  const MAX_VALUE_DEPTH = 32;
4
5
  export class ToolRuntimeError extends Error {
@@ -12,14 +13,14 @@ export class ToolRuntimeError extends Error {
12
13
  }
13
14
  }
14
15
  /**
15
- * Brings a host-produced runtime value into the program: runtime values pass through, their host
16
- * counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects become
17
- * null-prototype copies. Arrays keep extra enumerable properties such as `index` and `groups`.
16
+ * Brings a host-produced value into the program: program and runtime values pass through, their
17
+ * host counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and host objects
18
+ * and arrays are copied.
18
19
  */
19
20
  export const toProgram = (value, label) => copy(value, label, "program", 0, new Set());
20
21
  /**
21
22
  * Brings host data into the program: Date and URL become strings, other host collections become
22
- * empty objects, and objects become null-prototype copies. Used for tool results and parsed JSON.
23
+ * empty objects, and objects become program copies. Used for tool results and parsed JSON.
23
24
  */
24
25
  export const fromData = (value, label) => copy(value, label, "data", 0, new Set());
25
26
  /**
@@ -45,8 +46,12 @@ const copy = (value, label, mode, depth, seen) => {
45
46
  if (value instanceof Values.Promise) {
46
47
  throw new ToolRuntimeError("InvalidDataValue", `${label} contains an un-awaited Promise; await tool calls (e.g. \`const result = await tools.ns.tool(...)\`) before using their results.`);
47
48
  }
49
+ if (value instanceof ProgramFunction && mode !== "program") {
50
+ throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
51
+ }
52
+ const plain = mode === "program" || mode === "data";
48
53
  if (mode === "program") {
49
- if (Values.isValue(value))
54
+ if (value instanceof ProgramObject || Values.isValue(value))
50
55
  return value;
51
56
  if (value instanceof Date)
52
57
  return new Values.Date(value.getTime());
@@ -70,7 +75,6 @@ const copy = (value, label, mode, depth, seen) => {
70
75
  if (value instanceof URLSearchParams)
71
76
  return new Values.URLSearchParams(new URLSearchParams(value));
72
77
  }
73
- const plain = mode === "program" || mode === "data";
74
78
  if (value instanceof Values.Date)
75
79
  return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null;
76
80
  if (value instanceof Date)
@@ -85,24 +89,39 @@ const copy = (value, label, mode, depth, seen) => {
85
89
  value instanceof Map ||
86
90
  value instanceof Set ||
87
91
  value instanceof URLSearchParams) {
88
- return plain ? Object.create(null) : {};
92
+ return plain ? new ProgramObject() : {};
89
93
  }
90
94
  if (seen.has(value)) {
91
95
  throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`);
92
96
  }
93
97
  seen.add(value);
98
+ if (value instanceof ProgramArray) {
99
+ const copied = Array.from(value.items, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
100
+ seen.delete(value);
101
+ return copied;
102
+ }
103
+ if (value instanceof ProgramObject) {
104
+ const copied = {};
105
+ for (const [key, item] of ownEntries(value)) {
106
+ const next = copy(item, label, mode, depth + 1, seen);
107
+ if (next === undefined && mode === "json")
108
+ continue;
109
+ define(copied, key, next);
110
+ }
111
+ seen.delete(value);
112
+ return copied;
113
+ }
94
114
  if (Array.isArray(value)) {
95
- // Host output densifies holes to null like JSON; program copies keep them.
96
- const copied = plain
97
- ? value.map((item) => copy(item, label, mode, depth + 1, seen))
98
- : Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
99
- if (mode === "program") {
115
+ if (plain) {
116
+ const copied = new ProgramArray(value.map((item) => copy(item, label, mode, depth + 1, seen)));
100
117
  for (const [key, item] of Object.entries(value)) {
101
- if (Object.hasOwn(copied, key))
102
- continue;
103
- define(copied, key, copy(item, label, mode, depth + 1, seen));
118
+ if (parseArrayIndex(key) === undefined)
119
+ set(copied, key, copy(item, label, mode, depth + 1, seen));
104
120
  }
121
+ seen.delete(value);
122
+ return copied;
105
123
  }
124
+ const copied = Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
106
125
  seen.delete(value);
107
126
  return copied;
108
127
  }
@@ -110,7 +129,14 @@ const copy = (value, label, mode, depth, seen) => {
110
129
  if (prototype !== Object.prototype && prototype !== null) {
111
130
  throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`);
112
131
  }
113
- const copied = plain ? Object.create(null) : {};
132
+ if (plain) {
133
+ const copied = new ProgramObject();
134
+ for (const [key, item] of Object.entries(value))
135
+ set(copied, key, copy(item, label, mode, depth + 1, seen));
136
+ seen.delete(value);
137
+ return copied;
138
+ }
139
+ const copied = {};
114
140
  for (const [key, item] of Object.entries(value)) {
115
141
  const next = copy(item, label, mode, depth + 1, seen);
116
142
  if (next === undefined && mode === "json")
@@ -120,8 +146,8 @@ const copy = (value, label, mode, depth, seen) => {
120
146
  seen.delete(value);
121
147
  return copied;
122
148
  };
123
- // Own data property regardless of the target's prototype, so a "__proto__" key on a host object or
124
- // array never reaches the Object.prototype setter.
149
+ // Own data property regardless of the target's prototype, so a "__proto__" key on a host object
150
+ // never reaches the Object.prototype setter.
125
151
  const define = (target, key, value) => {
126
152
  Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
127
153
  };
@@ -4,6 +4,7 @@ import { toData, ToolRuntimeError } from "../data.js";
4
4
  import { formatLocation, InterpreterRuntimeError, ProgramThrow, sourceLocation } from "./model.js";
5
5
  import { containsRuntimeReference } from "./references.js";
6
6
  import { HostFunction } from "./host.js";
7
+ import { get, ProgramError, ProgramObject } from "./objects.js";
7
8
  import {} from "./runner.js";
8
9
  import { coerceToString, createAggregateErrorValue, createErrorValue, errorBrandName, errorConstructors, } from "../stdlib/value.js";
9
10
  export const normalizeError = (error) => {
@@ -35,10 +36,8 @@ export const normalizeError = (error) => {
35
36
  else if (typeof value === "string") {
36
37
  message = value;
37
38
  }
38
- else if (value !== null &&
39
- typeof value === "object" &&
40
- typeof value.message === "string") {
41
- message = value.message;
39
+ else if (value instanceof ProgramObject && typeof get(value, "message") === "string") {
40
+ message = get(value, "message");
42
41
  }
43
42
  else {
44
43
  try {
@@ -1,4 +1,5 @@
1
1
  import { Effect } from "effect";
2
2
  import type { ResolvedExecutionLimits, Result } from "../codemode.js";
3
3
  import { ToolRuntime } from "../tool-runtime.js";
4
- export declare const executeProgram: <R>(code: string, prepared: ToolRuntime.Prepared<R>, limits: ResolvedExecutionLimits, hooks: ToolRuntime.ToolCallHooks<R>) => Effect.Effect<Result, never, R>;
4
+ import type { Host } from "./globals.js";
5
+ export declare const executeProgram: <R>(code: string, prepared: ToolRuntime.Prepared<R>, limits: ResolvedExecutionLimits, hooks: ToolRuntime.ToolCallHooks<R>, extraGlobals?: (host: Host<R>) => ReadonlyArray<readonly [string, unknown]>) => Effect.Effect<Result, never, R>;
@@ -9,7 +9,7 @@ import { normalizeError } from "./errors.js";
9
9
  import { InterpreterRuntimeError } from "./model.js";
10
10
  import { PromiseRuntime } from "./promises.js";
11
11
  import { Runtime } from "./runtime.js";
12
- export const executeProgram = (code, prepared, limits, hooks) => {
12
+ export const executeProgram = (code, prepared, limits, hooks, extraGlobals) => {
13
13
  if (code.trim().length === 0) {
14
14
  return Effect.succeed({
15
15
  ok: false,
@@ -27,7 +27,7 @@ export const executeProgram = (code, prepared, limits, hooks) => {
27
27
  const base = Effect.acquireUseRelease(Scope.make("parallel"), (scope) => Effect.gen(function* () {
28
28
  const program = parseProgram(code);
29
29
  const promises = new PromiseRuntime(scope);
30
- const value = yield* new Runtime(tools.execute, tools.search, tools.keys, promises, logs).run(program);
30
+ const value = yield* new Runtime(tools.execute, tools.search, tools.keys, promises, logs, extraGlobals).run(program);
31
31
  const result = toData(value, "Execution result", "result");
32
32
  returned = { value: result, promises };
33
33
  const warnings = yield* promises.interrupt();
@@ -11,6 +11,7 @@ import { regexpGlobal } from "../stdlib/regexp.js";
11
11
  import { stringGlobal } from "../stdlib/string.js";
12
12
  import { uriGlobal, urlGlobal, urlSearchParamsGlobal } from "../stdlib/url.js";
13
13
  import { coercion, errorConstructors } from "../stdlib/value.js";
14
+ import { atobGlobal, btoaGlobal, cryptoGlobal } from "../stdlib/web.js";
14
15
  import { ToolReference } from "../tool-runtime.js";
15
16
  import { errorGlobal } from "./errors.js";
16
17
  import { HostFunction } from "./host.js";
@@ -55,5 +56,8 @@ export const globals = (host) => [
55
56
  ["encodeURIComponent", uriGlobal("encodeURIComponent")],
56
57
  ["decodeURI", uriGlobal("decodeURI")],
57
58
  ["decodeURIComponent", uriGlobal("decodeURIComponent")],
59
+ ["atob", atobGlobal],
60
+ ["btoa", btoaGlobal],
61
+ ["crypto", cryptoGlobal],
58
62
  ...[...errorConstructors].map((name) => [name, errorGlobal(name, host.runner)]),
59
63
  ];
@@ -4,13 +4,15 @@ import { dateSetterArgumentCount, invokeDateMethod } from "../stdlib/date.js";
4
4
  import { invokeNumberMethod } from "../stdlib/number.js";
5
5
  import { invokeRegExpMethod, matchToValue, toHostRegex } from "../stdlib/regexp.js";
6
6
  import { invokeURLMethod, uriArgument } from "../stdlib/url.js";
7
- import { coerceToNumber, coerceToString, errorBrandName } from "../stdlib/value.js";
7
+ import { coerceToNumber, coerceToString } from "../stdlib/value.js";
8
8
  import { compareText } from "../tool-runtime.js";
9
9
  import { Values } from "../values.js";
10
10
  import { IntrinsicReference, InterpreterRuntimeError } from "./model.js";
11
+ import { get, ProgramArray, ProgramObject, record } from "./objects.js";
11
12
  import { containsOpaqueReference, rejectCircularInsertion, typeofValue } from "./references.js";
12
13
  import { applyCollectionCallback, isSupportedCallback, toPrimitive } from "./runner.js";
13
- export const invokeIntrinsic = (runner, ref, args, node) => {
14
+ export const invokeIntrinsic = (runner, ref, args, node) => Effect.map(invoke(runner, ref, args, node), (result) => (Array.isArray(result) ? new ProgramArray(result) : result));
15
+ const invoke = (runner, ref, args, node) => {
14
16
  if (typeof ref.receiver === "string") {
15
17
  if (ref.name === "replace" || ref.name === "replaceAll") {
16
18
  if (isSupportedCallback(args[1]))
@@ -24,7 +26,7 @@ export const invokeIntrinsic = (runner, ref, args, node) => {
24
26
  if (typeof ref.receiver === "number") {
25
27
  return Effect.succeed(invokeNumberMethod(ref.receiver, ref.name, args, node));
26
28
  }
27
- if (Array.isArray(ref.receiver)) {
29
+ if (ref.receiver instanceof ProgramArray) {
28
30
  return invokeArrayMethod(runner, ref.receiver, ref.name, args, node);
29
31
  }
30
32
  if (ref.receiver instanceof Values.Date) {
@@ -92,9 +94,11 @@ const invokeStringMethod = (value, name, args, node) => {
92
94
  result = value.trim();
93
95
  break;
94
96
  case "trimStart":
97
+ case "trimLeft":
95
98
  result = value.trimStart();
96
99
  break;
97
100
  case "trimEnd":
101
+ case "trimRight":
98
102
  result = value.trimEnd();
99
103
  break;
100
104
  // Locale/options are deliberately unsupported; comparison uses the host default locale.
@@ -181,7 +185,7 @@ const invokeStringMethod = (value, name, args, node) => {
181
185
  if (!pattern.global) {
182
186
  throw new InterpreterRuntimeError(`String.matchAll requires a regular expression with the global (g) flag: write /${pattern.source}/${pattern.flags}g, or use String.match for a single match.`, node);
183
187
  }
184
- return Array.from(value.matchAll(pattern), matchToValue);
188
+ return new ProgramArray(Array.from(value.matchAll(pattern), matchToValue));
185
189
  }
186
190
  case "search": {
187
191
  result = value.search(toHostRegex(args[0], name, node));
@@ -209,6 +213,15 @@ const invokeStringMethod = (value, name, args, node) => {
209
213
  case "substring":
210
214
  result = value.substring(optNum(0) ?? 0, optNum(1));
211
215
  break;
216
+ case "substr":
217
+ result = value.substr(optNum(0) ?? 0, optNum(1));
218
+ break;
219
+ case "isWellFormed":
220
+ result = value.isWellFormed();
221
+ break;
222
+ case "toWellFormed":
223
+ result = value.toWellFormed();
224
+ break;
212
225
  case "charCodeAt":
213
226
  result = value.charCodeAt(optNum(0) ?? 0);
214
227
  break;
@@ -238,13 +251,8 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
238
251
  if (typeof match !== "string" || typeof offset !== "number") {
239
252
  throw new InterpreterRuntimeError(`String.${name} produced an invalid replacement match.`, node);
240
253
  }
241
- if (hasGroups) {
242
- const safeGroups = Object.create(null);
243
- for (const [key, group] of Object.entries(groups)) {
244
- safeGroups[key] = group;
245
- }
246
- callbackArgs[callbackArgs.length - 1] = safeGroups;
247
- }
254
+ if (hasGroups)
255
+ callbackArgs[callbackArgs.length - 1] = record(groups);
248
256
  matches.push({ match, offset, args: callbackArgs });
249
257
  return match;
250
258
  };
@@ -270,12 +278,9 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
270
278
  let end = 0;
271
279
  for (const match of matches) {
272
280
  const replacement = yield* apply(match.args);
273
- // Error values are branded plain objects; toProgram would strip the brand before coercion.
274
281
  output.push(value.slice(end, match.offset), replacement instanceof Values.Promise
275
282
  ? "[object Promise]"
276
- : errorBrandName(replacement)
277
- ? coerceToString(replacement)
278
- : coerceToString(toProgram(replacement, `String.${name} replacer result`)));
283
+ : coerceToString(toProgram(replacement, `String.${name} replacer result`)));
279
284
  end = match.offset + match.match.length;
280
285
  }
281
286
  output.push(value.slice(end));
@@ -305,7 +310,7 @@ const invokeMapMethod = (runner, target, name, args, node) => {
305
310
  case "values":
306
311
  return Effect.sync(() => Array.from(target.map.values()));
307
312
  case "entries":
308
- return Effect.sync(() => Array.from(target.map.entries(), ([key, item]) => [key, item]));
313
+ return Effect.sync(() => Array.from(target.map.entries(), ([key, item]) => new ProgramArray([key, item])));
309
314
  case "forEach": {
310
315
  const apply = applyCollectionCallback(runner, args[0], "Map.forEach", node);
311
316
  return Effect.gen(function* () {
@@ -338,7 +343,7 @@ const invokeSetMethod = (runner, target, name, args, node) => {
338
343
  case "values":
339
344
  return Effect.sync(() => Array.from(target.set.values()));
340
345
  case "entries":
341
- return Effect.sync(() => Array.from(target.set.values(), (item) => [item, item]));
346
+ return Effect.sync(() => Array.from(target.set.values(), (item) => new ProgramArray([item, item])));
342
347
  case "forEach": {
343
348
  const apply = applyCollectionCallback(runner, args[0], "Set.forEach", node);
344
349
  return Effect.gen(function* () {
@@ -457,26 +462,25 @@ const loadSetRecord = (runner, source, name, node) => {
457
462
  keys: () => Effect.succeed(source.map.keys()),
458
463
  });
459
464
  }
460
- if (source === null || typeof source !== "object" || Values.isValue(source)) {
465
+ if (!(source instanceof ProgramObject)) {
461
466
  throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError");
462
467
  }
463
- const object = source;
464
468
  return Effect.gen(function* () {
465
- const size = yield* coerceNumericArgument(runner, object.size, node);
469
+ const size = yield* coerceNumericArgument(runner, get(source, "size"), node);
466
470
  if (Number.isNaN(size)) {
467
471
  throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as("TypeError");
468
472
  }
469
- if (!isSupportedCallback(object.has) || !isSupportedCallback(object.keys)) {
473
+ const has = get(source, "has");
474
+ const keys = get(source, "keys");
475
+ if (!isSupportedCallback(has) || !isSupportedCallback(keys)) {
470
476
  throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError");
471
477
  }
472
- const has = object.has;
473
- const keys = object.keys;
474
478
  return {
475
479
  size: Math.max(Math.trunc(size), 0),
476
480
  has: (item) => Effect.map(runner.invokeCallable(has, [item], node), Boolean),
477
481
  keys: () => Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => {
478
- if (Array.isArray(result))
479
- return Effect.succeed(result);
482
+ if (result instanceof ProgramArray)
483
+ return Effect.succeed(result.items);
480
484
  throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError");
481
485
  }),
482
486
  };
@@ -533,7 +537,7 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
533
537
  case "values":
534
538
  return Effect.sync(() => Array.from(target.params.values()));
535
539
  case "entries":
536
- return Effect.sync(() => Array.from(target.params.entries(), ([key, value]) => [key, value]));
540
+ return Effect.sync(() => Array.from(target.params.entries(), ([key, value]) => new ProgramArray([key, value])));
537
541
  case "toString":
538
542
  return Effect.sync(() => target.params.toString());
539
543
  case "forEach": {
@@ -549,7 +553,8 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
549
553
  throw new InterpreterRuntimeError(`URLSearchParams method '${name}' is not available.`, node);
550
554
  }
551
555
  };
552
- const invokeArrayMethod = (runner, target, name, args, node) => {
556
+ const invokeArrayMethod = (runner, receiver, name, args, node) => {
557
+ const target = receiver.items;
553
558
  const optNumber = (value, label) => {
554
559
  if (value === undefined)
555
560
  return undefined;
@@ -562,8 +567,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
562
567
  if (args.length > 1 || (args.length === 1 && typeof args[0] !== "string")) {
563
568
  throw new InterpreterRuntimeError("Array.join expects zero arguments or one string separator.", node);
564
569
  }
565
- const input = toProgram(target, "Array.join input");
566
- return Effect.succeed(input.map((item) => coerceToString(item ?? "")).join(args.length === 0 ? "," : args[0]));
570
+ return Effect.succeed(target.map((item) => coerceToString(item ?? "")).join(args.length === 0 ? "," : args[0]));
567
571
  }
568
572
  case "includes":
569
573
  if (args.length === 0 || args.length > 2)
@@ -580,11 +584,14 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
580
584
  case "slice":
581
585
  return Effect.succeed(target.slice(optNumber(args[0], "start"), optNumber(args[1], "end")));
582
586
  case "concat":
583
- return Effect.succeed(target.concat(...args));
584
- case "flat":
585
- return Effect.succeed(target.flat(optNumber(args[0], "depth") ?? 1));
587
+ return Effect.succeed(target.concat(...args.map((item) => (item instanceof ProgramArray ? item.items : item))));
588
+ case "flat": {
589
+ const flatten = (items, depth) => items.flatMap((item) => (item instanceof ProgramArray && depth > 0 ? flatten(item.items, depth - 1) : [item]));
590
+ return Effect.succeed(flatten(target, optNumber(args[0], "depth") ?? 1));
591
+ }
586
592
  case "reverse":
587
- return Effect.succeed(target.reverse());
593
+ target.reverse();
594
+ return Effect.succeed(receiver);
588
595
  case "sort": {
589
596
  const length = target.length;
590
597
  const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(target, index)).filter((own) => !own).length;
@@ -596,7 +603,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
596
603
  Array.from({ length: holeCount }, (_, index) => itemCount + index).forEach((index) => {
597
604
  Reflect.deleteProperty(target, index);
598
605
  });
599
- return target;
606
+ return receiver;
600
607
  });
601
608
  }
602
609
  case "toSorted":
@@ -616,13 +623,13 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
616
623
  case "push": {
617
624
  // Validate all insertions before mutating to avoid partial cyclic updates.
618
625
  for (const item of args)
619
- rejectCircularInsertion(target, item, "Array.push result", node);
626
+ rejectCircularInsertion(receiver, item, "Array.push result", node);
620
627
  target.push(...args);
621
628
  return Effect.succeed(target.length);
622
629
  }
623
630
  case "unshift": {
624
631
  for (const item of args)
625
- rejectCircularInsertion(target, item, "Array.unshift result", node);
632
+ rejectCircularInsertion(receiver, item, "Array.unshift result", node);
626
633
  target.unshift(...args);
627
634
  return Effect.succeed(target.length);
628
635
  }
@@ -639,7 +646,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
639
646
  const deleteCount = optNumber(args[1], "delete count") ?? 0;
640
647
  const inserted = args.slice(2);
641
648
  for (const item of inserted)
642
- rejectCircularInsertion(target, item, "Array.splice result", node);
649
+ rejectCircularInsertion(receiver, item, "Array.splice result", node);
643
650
  return Effect.succeed(target.splice(start, deleteCount, ...inserted));
644
651
  }
645
652
  case "toSpliced": {
@@ -657,17 +664,19 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
657
664
  return Effect.succeed(copied);
658
665
  }
659
666
  case "fill": {
660
- rejectCircularInsertion(target, args[0], "Array.fill result", node);
661
- return Effect.succeed(target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end")));
667
+ rejectCircularInsertion(receiver, args[0], "Array.fill result", node);
668
+ target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end"));
669
+ return Effect.succeed(receiver);
662
670
  }
663
671
  case "copyWithin":
664
- return Effect.succeed(target.copyWithin(optNumber(args[0], "target index") ?? 0, optNumber(args[1], "start") ?? 0, optNumber(args[2], "end")));
672
+ target.copyWithin(optNumber(args[0], "target index") ?? 0, optNumber(args[1], "start") ?? 0, optNumber(args[2], "end"));
673
+ return Effect.succeed(receiver);
665
674
  case "keys":
666
675
  return Effect.succeed(Array.from(target.keys()));
667
676
  case "values":
668
677
  return Effect.succeed([...target]);
669
678
  case "entries":
670
- return Effect.succeed(Array.from(target.entries(), ([index, item]) => [index, item]));
679
+ return Effect.succeed(Array.from(target.entries(), ([index, item]) => new ProgramArray([index, item])));
671
680
  }
672
681
  const apply = applyCollectionCallback(runner, args[0], `Array.${name}`, node);
673
682
  return Effect.gen(function* () {
@@ -680,7 +689,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
680
689
  for (let index = 0; index < length; index += 1) {
681
690
  if (!(index in target))
682
691
  continue;
683
- values[index] = yield* apply([target[index], index, target]);
692
+ values[index] = yield* apply([target[index], index, receiver]);
684
693
  }
685
694
  return values;
686
695
  }
@@ -689,9 +698,9 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
689
698
  for (let index = 0; index < length; index += 1) {
690
699
  if (!(index in target))
691
700
  continue;
692
- const mapped = yield* apply([target[index], index, target]);
693
- if (Array.isArray(mapped))
694
- values.push(...mapped);
701
+ const mapped = yield* apply([target[index], index, receiver]);
702
+ if (mapped instanceof ProgramArray)
703
+ values.push(...mapped.items);
695
704
  else
696
705
  values.push(mapped);
697
706
  }
@@ -703,7 +712,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
703
712
  if (!(index in target))
704
713
  continue;
705
714
  const item = target[index];
706
- if (yield* apply([item, index, target]))
715
+ if (yield* apply([item, index, receiver]))
707
716
  values.push(item);
708
717
  }
709
718
  return values;
@@ -711,13 +720,13 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
711
720
  case "find":
712
721
  for (let index = 0; index < length; index += 1) {
713
722
  const item = target[index];
714
- if (yield* apply([item, index, target]))
723
+ if (yield* apply([item, index, receiver]))
715
724
  return item;
716
725
  }
717
726
  return undefined;
718
727
  case "findIndex":
719
728
  for (let index = 0; index < length; index += 1) {
720
- if (yield* apply([target[index], index, target]))
729
+ if (yield* apply([target[index], index, receiver]))
721
730
  return index;
722
731
  }
723
732
  return -1;
@@ -725,7 +734,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
725
734
  for (let index = 0; index < length; index += 1) {
726
735
  if (!(index in target))
727
736
  continue;
728
- if (yield* apply([target[index], index, target]))
737
+ if (yield* apply([target[index], index, receiver]))
729
738
  return true;
730
739
  }
731
740
  return false;
@@ -733,14 +742,14 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
733
742
  for (let index = 0; index < length; index += 1) {
734
743
  if (!(index in target))
735
744
  continue;
736
- if (!(yield* apply([target[index], index, target])))
745
+ if (!(yield* apply([target[index], index, receiver])))
737
746
  return false;
738
747
  }
739
748
  return true;
740
749
  case "forEach":
741
750
  for (let index = 0; index < length; index += 1) {
742
751
  if (index in target)
743
- yield* apply([target[index], index, target]);
752
+ yield* apply([target[index], index, receiver]);
744
753
  }
745
754
  return undefined;
746
755
  case "reduce": {
@@ -757,7 +766,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
757
766
  for (let index = start; index < length; index += 1) {
758
767
  if (!(index in target))
759
768
  continue;
760
- accumulator = yield* apply([accumulator, target[index], index, target]);
769
+ accumulator = yield* apply([accumulator, target[index], index, receiver]);
761
770
  }
762
771
  return accumulator;
763
772
  }
@@ -775,20 +784,20 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
775
784
  for (let index = start; index >= 0; index -= 1) {
776
785
  if (!(index in target))
777
786
  continue;
778
- accumulator = yield* apply([accumulator, target[index], index, target]);
787
+ accumulator = yield* apply([accumulator, target[index], index, receiver]);
779
788
  }
780
789
  return accumulator;
781
790
  }
782
791
  case "findLast":
783
792
  for (let index = length - 1; index >= 0; index -= 1) {
784
793
  const item = target[index];
785
- if (yield* apply([item, index, target]))
794
+ if (yield* apply([item, index, receiver]))
786
795
  return item;
787
796
  }
788
797
  return undefined;
789
798
  case "findLastIndex":
790
799
  for (let index = length - 1; index >= 0; index -= 1) {
791
- if (yield* apply([target[index], index, target]))
800
+ if (yield* apply([target[index], index, receiver]))
792
801
  return index;
793
802
  }
794
803
  return -1;
@@ -1,7 +1,7 @@
1
- import type { BlockStatement, Expression, Node, Pattern } from "acorn";
1
+ import type { Node } from "acorn";
2
2
  import type { Effect } from "effect";
3
3
  import type { DiagnosticKind } from "../codemode.js";
4
- import type { SafeObject } from "../data.js";
4
+ import type { ProgramObject } from "./objects.js";
5
5
  import type { Values } from "../values.js";
6
6
  /** Any parsed node; the interpreter narrows on `type` and reads `loc` for diagnostics. */
7
7
  export type AstNode = Node;
@@ -23,17 +23,9 @@ export type StatementResult = {
23
23
  label?: string;
24
24
  };
25
25
  export type MemberReference = {
26
- target: SafeObject | Array<unknown> | Values.RegExp | Values.URL;
26
+ target: ProgramObject | Values.RegExp | Values.URL;
27
27
  key: PropertyKey;
28
28
  };
29
- export declare class CodeModeFunction {
30
- readonly parameters: ReadonlyArray<Pattern>;
31
- readonly body: BlockStatement | Expression;
32
- readonly capturedScopes: ReadonlyArray<Map<string, Binding>>;
33
- readonly async: boolean;
34
- readonly generator: boolean;
35
- constructor(parameters: ReadonlyArray<Pattern>, body: BlockStatement | Expression, capturedScopes: ReadonlyArray<Map<string, Binding>>, async: boolean, generator: boolean);
36
- }
37
29
  export type GeneratorRequestKind = "next" | "return" | "throw";
38
30
  export declare class CodeModeGenerator {
39
31
  readonly asynchronous: boolean;
@@ -1,17 +1,3 @@
1
- export class CodeModeFunction {
2
- parameters;
3
- body;
4
- capturedScopes;
5
- async;
6
- generator;
7
- constructor(parameters, body, capturedScopes, async, generator) {
8
- this.parameters = parameters;
9
- this.body = body;
10
- this.capturedScopes = capturedScopes;
11
- this.async = async;
12
- this.generator = generator;
13
- }
14
- }
15
1
  export class CodeModeGenerator {
16
2
  asynchronous;
17
3
  request;