@opencode/codemode 0.0.0-dev-19484 → 0.0.0-dev-19486
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.
- package/dist/data.d.ts +4 -6
- package/dist/data.js +41 -18
- package/dist/interpreter/errors.js +3 -4
- package/dist/interpreter/methods.js +53 -55
- package/dist/interpreter/model.d.ts +2 -2
- package/dist/interpreter/objects.d.ts +25 -0
- package/dist/interpreter/objects.js +122 -0
- package/dist/interpreter/promises.js +7 -9
- package/dist/interpreter/references.d.ts +0 -1
- package/dist/interpreter/references.js +7 -17
- package/dist/interpreter/runner.js +7 -4
- package/dist/interpreter/runtime.js +71 -120
- package/dist/stdlib/array.js +12 -14
- package/dist/stdlib/collections.js +10 -9
- package/dist/stdlib/console.js +16 -14
- package/dist/stdlib/json.js +21 -36
- package/dist/stdlib/object.d.ts +2 -1
- package/dist/stdlib/object.js +21 -42
- package/dist/stdlib/regexp.d.ts +2 -1
- package/dist/stdlib/regexp.js +14 -20
- package/dist/stdlib/url.js +3 -3
- package/dist/stdlib/value.d.ts +3 -3
- package/dist/stdlib/value.js +25 -26
- package/dist/tool-runtime.d.ts +1 -1
- package/package.json +1 -1
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
|
|
12
|
-
* counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects
|
|
13
|
-
*
|
|
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
|
|
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, 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
|
|
16
|
-
* counterparts (Date, RegExp, Map, Set, URL, URLSearchParams) are wrapped, and objects
|
|
17
|
-
*
|
|
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
|
|
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,9 @@ 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
|
+
const plain = mode === "program" || mode === "data";
|
|
48
50
|
if (mode === "program") {
|
|
49
|
-
if (Values.isValue(value))
|
|
51
|
+
if (value instanceof ProgramObject || Values.isValue(value))
|
|
50
52
|
return value;
|
|
51
53
|
if (value instanceof Date)
|
|
52
54
|
return new Values.Date(value.getTime());
|
|
@@ -70,7 +72,6 @@ const copy = (value, label, mode, depth, seen) => {
|
|
|
70
72
|
if (value instanceof URLSearchParams)
|
|
71
73
|
return new Values.URLSearchParams(new URLSearchParams(value));
|
|
72
74
|
}
|
|
73
|
-
const plain = mode === "program" || mode === "data";
|
|
74
75
|
if (value instanceof Values.Date)
|
|
75
76
|
return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null;
|
|
76
77
|
if (value instanceof Date)
|
|
@@ -85,24 +86,39 @@ const copy = (value, label, mode, depth, seen) => {
|
|
|
85
86
|
value instanceof Map ||
|
|
86
87
|
value instanceof Set ||
|
|
87
88
|
value instanceof URLSearchParams) {
|
|
88
|
-
return plain ?
|
|
89
|
+
return plain ? new ProgramObject() : {};
|
|
89
90
|
}
|
|
90
91
|
if (seen.has(value)) {
|
|
91
92
|
throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`);
|
|
92
93
|
}
|
|
93
94
|
seen.add(value);
|
|
95
|
+
if (value instanceof ProgramArray) {
|
|
96
|
+
const copied = Array.from(value.items, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
|
|
97
|
+
seen.delete(value);
|
|
98
|
+
return copied;
|
|
99
|
+
}
|
|
100
|
+
if (value instanceof ProgramObject) {
|
|
101
|
+
const copied = {};
|
|
102
|
+
for (const [key, item] of ownEntries(value)) {
|
|
103
|
+
const next = copy(item, label, mode, depth + 1, seen);
|
|
104
|
+
if (next === undefined && mode === "json")
|
|
105
|
+
continue;
|
|
106
|
+
define(copied, key, next);
|
|
107
|
+
}
|
|
108
|
+
seen.delete(value);
|
|
109
|
+
return copied;
|
|
110
|
+
}
|
|
94
111
|
if (Array.isArray(value)) {
|
|
95
|
-
|
|
96
|
-
|
|
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") {
|
|
112
|
+
if (plain) {
|
|
113
|
+
const copied = new ProgramArray(value.map((item) => copy(item, label, mode, depth + 1, seen)));
|
|
100
114
|
for (const [key, item] of Object.entries(value)) {
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
define(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
115
|
+
if (parseArrayIndex(key) === undefined)
|
|
116
|
+
set(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
104
117
|
}
|
|
118
|
+
seen.delete(value);
|
|
119
|
+
return copied;
|
|
105
120
|
}
|
|
121
|
+
const copied = Array.from(value, (item) => copy(item, label, mode, depth + 1, seen) ?? null);
|
|
106
122
|
seen.delete(value);
|
|
107
123
|
return copied;
|
|
108
124
|
}
|
|
@@ -110,7 +126,14 @@ const copy = (value, label, mode, depth, seen) => {
|
|
|
110
126
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
111
127
|
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`);
|
|
112
128
|
}
|
|
113
|
-
|
|
129
|
+
if (plain) {
|
|
130
|
+
const copied = new ProgramObject();
|
|
131
|
+
for (const [key, item] of Object.entries(value))
|
|
132
|
+
set(copied, key, copy(item, label, mode, depth + 1, seen));
|
|
133
|
+
seen.delete(value);
|
|
134
|
+
return copied;
|
|
135
|
+
}
|
|
136
|
+
const copied = {};
|
|
114
137
|
for (const [key, item] of Object.entries(value)) {
|
|
115
138
|
const next = copy(item, label, mode, depth + 1, seen);
|
|
116
139
|
if (next === undefined && mode === "json")
|
|
@@ -120,8 +143,8 @@ const copy = (value, label, mode, depth, seen) => {
|
|
|
120
143
|
seen.delete(value);
|
|
121
144
|
return copied;
|
|
122
145
|
};
|
|
123
|
-
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object
|
|
124
|
-
//
|
|
146
|
+
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object
|
|
147
|
+
// never reaches the Object.prototype setter.
|
|
125
148
|
const define = (target, key, value) => {
|
|
126
149
|
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
127
150
|
};
|
|
@@ -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
|
|
39
|
-
|
|
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 {
|
|
@@ -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
|
|
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 (
|
|
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) {
|
|
@@ -183,7 +185,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
183
185
|
if (!pattern.global) {
|
|
184
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);
|
|
185
187
|
}
|
|
186
|
-
return Array.from(value.matchAll(pattern), matchToValue);
|
|
188
|
+
return new ProgramArray(Array.from(value.matchAll(pattern), matchToValue));
|
|
187
189
|
}
|
|
188
190
|
case "search": {
|
|
189
191
|
result = value.search(toHostRegex(args[0], name, node));
|
|
@@ -249,13 +251,8 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
|
|
|
249
251
|
if (typeof match !== "string" || typeof offset !== "number") {
|
|
250
252
|
throw new InterpreterRuntimeError(`String.${name} produced an invalid replacement match.`, node);
|
|
251
253
|
}
|
|
252
|
-
if (hasGroups)
|
|
253
|
-
|
|
254
|
-
for (const [key, group] of Object.entries(groups)) {
|
|
255
|
-
safeGroups[key] = group;
|
|
256
|
-
}
|
|
257
|
-
callbackArgs[callbackArgs.length - 1] = safeGroups;
|
|
258
|
-
}
|
|
254
|
+
if (hasGroups)
|
|
255
|
+
callbackArgs[callbackArgs.length - 1] = record(groups);
|
|
259
256
|
matches.push({ match, offset, args: callbackArgs });
|
|
260
257
|
return match;
|
|
261
258
|
};
|
|
@@ -281,12 +278,9 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
|
|
|
281
278
|
let end = 0;
|
|
282
279
|
for (const match of matches) {
|
|
283
280
|
const replacement = yield* apply(match.args);
|
|
284
|
-
// Error values are branded plain objects; toProgram would strip the brand before coercion.
|
|
285
281
|
output.push(value.slice(end, match.offset), replacement instanceof Values.Promise
|
|
286
282
|
? "[object Promise]"
|
|
287
|
-
:
|
|
288
|
-
? coerceToString(replacement)
|
|
289
|
-
: coerceToString(toProgram(replacement, `String.${name} replacer result`)));
|
|
283
|
+
: coerceToString(toProgram(replacement, `String.${name} replacer result`)));
|
|
290
284
|
end = match.offset + match.match.length;
|
|
291
285
|
}
|
|
292
286
|
output.push(value.slice(end));
|
|
@@ -316,7 +310,7 @@ const invokeMapMethod = (runner, target, name, args, node) => {
|
|
|
316
310
|
case "values":
|
|
317
311
|
return Effect.sync(() => Array.from(target.map.values()));
|
|
318
312
|
case "entries":
|
|
319
|
-
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])));
|
|
320
314
|
case "forEach": {
|
|
321
315
|
const apply = applyCollectionCallback(runner, args[0], "Map.forEach", node);
|
|
322
316
|
return Effect.gen(function* () {
|
|
@@ -349,7 +343,7 @@ const invokeSetMethod = (runner, target, name, args, node) => {
|
|
|
349
343
|
case "values":
|
|
350
344
|
return Effect.sync(() => Array.from(target.set.values()));
|
|
351
345
|
case "entries":
|
|
352
|
-
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])));
|
|
353
347
|
case "forEach": {
|
|
354
348
|
const apply = applyCollectionCallback(runner, args[0], "Set.forEach", node);
|
|
355
349
|
return Effect.gen(function* () {
|
|
@@ -468,26 +462,25 @@ const loadSetRecord = (runner, source, name, node) => {
|
|
|
468
462
|
keys: () => Effect.succeed(source.map.keys()),
|
|
469
463
|
});
|
|
470
464
|
}
|
|
471
|
-
if (source
|
|
465
|
+
if (!(source instanceof ProgramObject)) {
|
|
472
466
|
throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError");
|
|
473
467
|
}
|
|
474
|
-
const object = source;
|
|
475
468
|
return Effect.gen(function* () {
|
|
476
|
-
const size = yield* coerceNumericArgument(runner,
|
|
469
|
+
const size = yield* coerceNumericArgument(runner, get(source, "size"), node);
|
|
477
470
|
if (Number.isNaN(size)) {
|
|
478
471
|
throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as("TypeError");
|
|
479
472
|
}
|
|
480
|
-
|
|
473
|
+
const has = get(source, "has");
|
|
474
|
+
const keys = get(source, "keys");
|
|
475
|
+
if (!isSupportedCallback(has) || !isSupportedCallback(keys)) {
|
|
481
476
|
throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError");
|
|
482
477
|
}
|
|
483
|
-
const has = object.has;
|
|
484
|
-
const keys = object.keys;
|
|
485
478
|
return {
|
|
486
479
|
size: Math.max(Math.trunc(size), 0),
|
|
487
480
|
has: (item) => Effect.map(runner.invokeCallable(has, [item], node), Boolean),
|
|
488
481
|
keys: () => Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => {
|
|
489
|
-
if (
|
|
490
|
-
return Effect.succeed(result);
|
|
482
|
+
if (result instanceof ProgramArray)
|
|
483
|
+
return Effect.succeed(result.items);
|
|
491
484
|
throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError");
|
|
492
485
|
}),
|
|
493
486
|
};
|
|
@@ -544,7 +537,7 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
|
|
|
544
537
|
case "values":
|
|
545
538
|
return Effect.sync(() => Array.from(target.params.values()));
|
|
546
539
|
case "entries":
|
|
547
|
-
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])));
|
|
548
541
|
case "toString":
|
|
549
542
|
return Effect.sync(() => target.params.toString());
|
|
550
543
|
case "forEach": {
|
|
@@ -560,7 +553,8 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
|
|
|
560
553
|
throw new InterpreterRuntimeError(`URLSearchParams method '${name}' is not available.`, node);
|
|
561
554
|
}
|
|
562
555
|
};
|
|
563
|
-
const invokeArrayMethod = (runner,
|
|
556
|
+
const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
557
|
+
const target = receiver.items;
|
|
564
558
|
const optNumber = (value, label) => {
|
|
565
559
|
if (value === undefined)
|
|
566
560
|
return undefined;
|
|
@@ -573,8 +567,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
573
567
|
if (args.length > 1 || (args.length === 1 && typeof args[0] !== "string")) {
|
|
574
568
|
throw new InterpreterRuntimeError("Array.join expects zero arguments or one string separator.", node);
|
|
575
569
|
}
|
|
576
|
-
|
|
577
|
-
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]));
|
|
578
571
|
}
|
|
579
572
|
case "includes":
|
|
580
573
|
if (args.length === 0 || args.length > 2)
|
|
@@ -591,11 +584,14 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
591
584
|
case "slice":
|
|
592
585
|
return Effect.succeed(target.slice(optNumber(args[0], "start"), optNumber(args[1], "end")));
|
|
593
586
|
case "concat":
|
|
594
|
-
return Effect.succeed(target.concat(...args));
|
|
595
|
-
case "flat":
|
|
596
|
-
|
|
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
|
+
}
|
|
597
592
|
case "reverse":
|
|
598
|
-
|
|
593
|
+
target.reverse();
|
|
594
|
+
return Effect.succeed(receiver);
|
|
599
595
|
case "sort": {
|
|
600
596
|
const length = target.length;
|
|
601
597
|
const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(target, index)).filter((own) => !own).length;
|
|
@@ -607,7 +603,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
607
603
|
Array.from({ length: holeCount }, (_, index) => itemCount + index).forEach((index) => {
|
|
608
604
|
Reflect.deleteProperty(target, index);
|
|
609
605
|
});
|
|
610
|
-
return
|
|
606
|
+
return receiver;
|
|
611
607
|
});
|
|
612
608
|
}
|
|
613
609
|
case "toSorted":
|
|
@@ -627,13 +623,13 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
627
623
|
case "push": {
|
|
628
624
|
// Validate all insertions before mutating to avoid partial cyclic updates.
|
|
629
625
|
for (const item of args)
|
|
630
|
-
rejectCircularInsertion(
|
|
626
|
+
rejectCircularInsertion(receiver, item, "Array.push result", node);
|
|
631
627
|
target.push(...args);
|
|
632
628
|
return Effect.succeed(target.length);
|
|
633
629
|
}
|
|
634
630
|
case "unshift": {
|
|
635
631
|
for (const item of args)
|
|
636
|
-
rejectCircularInsertion(
|
|
632
|
+
rejectCircularInsertion(receiver, item, "Array.unshift result", node);
|
|
637
633
|
target.unshift(...args);
|
|
638
634
|
return Effect.succeed(target.length);
|
|
639
635
|
}
|
|
@@ -650,7 +646,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
650
646
|
const deleteCount = optNumber(args[1], "delete count") ?? 0;
|
|
651
647
|
const inserted = args.slice(2);
|
|
652
648
|
for (const item of inserted)
|
|
653
|
-
rejectCircularInsertion(
|
|
649
|
+
rejectCircularInsertion(receiver, item, "Array.splice result", node);
|
|
654
650
|
return Effect.succeed(target.splice(start, deleteCount, ...inserted));
|
|
655
651
|
}
|
|
656
652
|
case "toSpliced": {
|
|
@@ -668,17 +664,19 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
668
664
|
return Effect.succeed(copied);
|
|
669
665
|
}
|
|
670
666
|
case "fill": {
|
|
671
|
-
rejectCircularInsertion(
|
|
672
|
-
|
|
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);
|
|
673
670
|
}
|
|
674
671
|
case "copyWithin":
|
|
675
|
-
|
|
672
|
+
target.copyWithin(optNumber(args[0], "target index") ?? 0, optNumber(args[1], "start") ?? 0, optNumber(args[2], "end"));
|
|
673
|
+
return Effect.succeed(receiver);
|
|
676
674
|
case "keys":
|
|
677
675
|
return Effect.succeed(Array.from(target.keys()));
|
|
678
676
|
case "values":
|
|
679
677
|
return Effect.succeed([...target]);
|
|
680
678
|
case "entries":
|
|
681
|
-
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])));
|
|
682
680
|
}
|
|
683
681
|
const apply = applyCollectionCallback(runner, args[0], `Array.${name}`, node);
|
|
684
682
|
return Effect.gen(function* () {
|
|
@@ -691,7 +689,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
691
689
|
for (let index = 0; index < length; index += 1) {
|
|
692
690
|
if (!(index in target))
|
|
693
691
|
continue;
|
|
694
|
-
values[index] = yield* apply([target[index], index,
|
|
692
|
+
values[index] = yield* apply([target[index], index, receiver]);
|
|
695
693
|
}
|
|
696
694
|
return values;
|
|
697
695
|
}
|
|
@@ -700,9 +698,9 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
700
698
|
for (let index = 0; index < length; index += 1) {
|
|
701
699
|
if (!(index in target))
|
|
702
700
|
continue;
|
|
703
|
-
const mapped = yield* apply([target[index], index,
|
|
704
|
-
if (
|
|
705
|
-
values.push(...mapped);
|
|
701
|
+
const mapped = yield* apply([target[index], index, receiver]);
|
|
702
|
+
if (mapped instanceof ProgramArray)
|
|
703
|
+
values.push(...mapped.items);
|
|
706
704
|
else
|
|
707
705
|
values.push(mapped);
|
|
708
706
|
}
|
|
@@ -714,7 +712,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
714
712
|
if (!(index in target))
|
|
715
713
|
continue;
|
|
716
714
|
const item = target[index];
|
|
717
|
-
if (yield* apply([item, index,
|
|
715
|
+
if (yield* apply([item, index, receiver]))
|
|
718
716
|
values.push(item);
|
|
719
717
|
}
|
|
720
718
|
return values;
|
|
@@ -722,13 +720,13 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
722
720
|
case "find":
|
|
723
721
|
for (let index = 0; index < length; index += 1) {
|
|
724
722
|
const item = target[index];
|
|
725
|
-
if (yield* apply([item, index,
|
|
723
|
+
if (yield* apply([item, index, receiver]))
|
|
726
724
|
return item;
|
|
727
725
|
}
|
|
728
726
|
return undefined;
|
|
729
727
|
case "findIndex":
|
|
730
728
|
for (let index = 0; index < length; index += 1) {
|
|
731
|
-
if (yield* apply([target[index], index,
|
|
729
|
+
if (yield* apply([target[index], index, receiver]))
|
|
732
730
|
return index;
|
|
733
731
|
}
|
|
734
732
|
return -1;
|
|
@@ -736,7 +734,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
736
734
|
for (let index = 0; index < length; index += 1) {
|
|
737
735
|
if (!(index in target))
|
|
738
736
|
continue;
|
|
739
|
-
if (yield* apply([target[index], index,
|
|
737
|
+
if (yield* apply([target[index], index, receiver]))
|
|
740
738
|
return true;
|
|
741
739
|
}
|
|
742
740
|
return false;
|
|
@@ -744,14 +742,14 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
744
742
|
for (let index = 0; index < length; index += 1) {
|
|
745
743
|
if (!(index in target))
|
|
746
744
|
continue;
|
|
747
|
-
if (!(yield* apply([target[index], index,
|
|
745
|
+
if (!(yield* apply([target[index], index, receiver])))
|
|
748
746
|
return false;
|
|
749
747
|
}
|
|
750
748
|
return true;
|
|
751
749
|
case "forEach":
|
|
752
750
|
for (let index = 0; index < length; index += 1) {
|
|
753
751
|
if (index in target)
|
|
754
|
-
yield* apply([target[index], index,
|
|
752
|
+
yield* apply([target[index], index, receiver]);
|
|
755
753
|
}
|
|
756
754
|
return undefined;
|
|
757
755
|
case "reduce": {
|
|
@@ -768,7 +766,7 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
768
766
|
for (let index = start; index < length; index += 1) {
|
|
769
767
|
if (!(index in target))
|
|
770
768
|
continue;
|
|
771
|
-
accumulator = yield* apply([accumulator, target[index], index,
|
|
769
|
+
accumulator = yield* apply([accumulator, target[index], index, receiver]);
|
|
772
770
|
}
|
|
773
771
|
return accumulator;
|
|
774
772
|
}
|
|
@@ -786,20 +784,20 @@ const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
|
786
784
|
for (let index = start; index >= 0; index -= 1) {
|
|
787
785
|
if (!(index in target))
|
|
788
786
|
continue;
|
|
789
|
-
accumulator = yield* apply([accumulator, target[index], index,
|
|
787
|
+
accumulator = yield* apply([accumulator, target[index], index, receiver]);
|
|
790
788
|
}
|
|
791
789
|
return accumulator;
|
|
792
790
|
}
|
|
793
791
|
case "findLast":
|
|
794
792
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
795
793
|
const item = target[index];
|
|
796
|
-
if (yield* apply([item, index,
|
|
794
|
+
if (yield* apply([item, index, receiver]))
|
|
797
795
|
return item;
|
|
798
796
|
}
|
|
799
797
|
return undefined;
|
|
800
798
|
case "findLastIndex":
|
|
801
799
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
802
|
-
if (yield* apply([target[index], index,
|
|
800
|
+
if (yield* apply([target[index], index, receiver]))
|
|
803
801
|
return index;
|
|
804
802
|
}
|
|
805
803
|
return -1;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BlockStatement, Expression, Node, Pattern } from "acorn";
|
|
2
2
|
import type { Effect } from "effect";
|
|
3
3
|
import type { DiagnosticKind } from "../codemode.js";
|
|
4
|
-
import type {
|
|
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,7 +23,7 @@ export type StatementResult = {
|
|
|
23
23
|
label?: string;
|
|
24
24
|
};
|
|
25
25
|
export type MemberReference = {
|
|
26
|
-
target:
|
|
26
|
+
target: ProgramObject | Values.RegExp | Values.URL;
|
|
27
27
|
key: PropertyKey;
|
|
28
28
|
};
|
|
29
29
|
export declare class CodeModeFunction {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** An object owned by the program: own properties plus a prototype link. */
|
|
2
|
+
export declare class ProgramObject {
|
|
3
|
+
proto: ProgramObject | null;
|
|
4
|
+
readonly props: Map<PropertyKey, unknown>;
|
|
5
|
+
constructor(proto?: ProgramObject | null);
|
|
6
|
+
}
|
|
7
|
+
export declare class ProgramArray extends ProgramObject {
|
|
8
|
+
readonly items: Array<unknown>;
|
|
9
|
+
constructor(items?: Array<unknown>);
|
|
10
|
+
}
|
|
11
|
+
export declare class ProgramError extends ProgramObject {
|
|
12
|
+
readonly errorName: string;
|
|
13
|
+
constructor(errorName: string);
|
|
14
|
+
}
|
|
15
|
+
export declare const parseArrayIndex: (key: string | number) => number | undefined;
|
|
16
|
+
export declare const hasOwn: (target: ProgramObject, key: PropertyKey) => boolean;
|
|
17
|
+
export declare const getOwn: (target: ProgramObject, key: PropertyKey) => unknown;
|
|
18
|
+
export declare const get: (target: ProgramObject, key: PropertyKey) => unknown;
|
|
19
|
+
export declare const has: (target: ProgramObject, key: PropertyKey) => boolean;
|
|
20
|
+
export declare const set: (target: ProgramObject, key: PropertyKey, value: unknown) => boolean;
|
|
21
|
+
export declare const remove: (target: ProgramObject, key: PropertyKey) => boolean;
|
|
22
|
+
export declare const ownKeys: (target: ProgramObject) => Array<string | symbol>;
|
|
23
|
+
export declare const ownEntries: (target: ProgramObject) => Array<[string, unknown]>;
|
|
24
|
+
export declare const record: (entries: Record<string, unknown>) => ProgramObject;
|
|
25
|
+
export declare const assign: (target: ProgramObject, source: ProgramObject, skip?: ReadonlySet<PropertyKey>) => void;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { AsyncIteratorSymbol, IteratorSymbol } from "./model.js";
|
|
2
|
+
/** An object owned by the program: own properties plus a prototype link. */
|
|
3
|
+
export class ProgramObject {
|
|
4
|
+
proto;
|
|
5
|
+
props = new Map();
|
|
6
|
+
constructor(proto = null) {
|
|
7
|
+
this.proto = proto;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class ProgramArray extends ProgramObject {
|
|
11
|
+
items;
|
|
12
|
+
constructor(items = []) {
|
|
13
|
+
super();
|
|
14
|
+
this.items = items;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class ProgramError extends ProgramObject {
|
|
18
|
+
errorName;
|
|
19
|
+
constructor(errorName) {
|
|
20
|
+
super();
|
|
21
|
+
this.errorName = errorName;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const MAX_ARRAY_LENGTH = 4_294_967_295;
|
|
25
|
+
export const parseArrayIndex = (key) => {
|
|
26
|
+
const property = String(key);
|
|
27
|
+
if (!/^(0|[1-9]\d*)$/.test(property))
|
|
28
|
+
return undefined;
|
|
29
|
+
const index = Number(property);
|
|
30
|
+
return index < MAX_ARRAY_LENGTH ? index : undefined;
|
|
31
|
+
};
|
|
32
|
+
const canonical = (key) => (typeof key === "symbol" ? key : String(key));
|
|
33
|
+
const index = (target, key) => target instanceof ProgramArray && typeof key === "string" ? parseArrayIndex(key) : undefined;
|
|
34
|
+
export const hasOwn = (target, key) => {
|
|
35
|
+
const name = canonical(key);
|
|
36
|
+
const at = index(target, name);
|
|
37
|
+
if (at !== undefined)
|
|
38
|
+
return at in target.items;
|
|
39
|
+
if (name === "length" && target instanceof ProgramArray)
|
|
40
|
+
return true;
|
|
41
|
+
return target.props.has(name);
|
|
42
|
+
};
|
|
43
|
+
export const getOwn = (target, key) => {
|
|
44
|
+
const name = canonical(key);
|
|
45
|
+
const at = index(target, name);
|
|
46
|
+
if (at !== undefined)
|
|
47
|
+
return target.items[at];
|
|
48
|
+
if (name === "length" && target instanceof ProgramArray)
|
|
49
|
+
return target.items.length;
|
|
50
|
+
return target.props.get(name);
|
|
51
|
+
};
|
|
52
|
+
export const get = (target, key) => {
|
|
53
|
+
for (let current = target; current !== null; current = current.proto) {
|
|
54
|
+
if (hasOwn(current, key))
|
|
55
|
+
return getOwn(current, key);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
59
|
+
export const has = (target, key) => {
|
|
60
|
+
for (let current = target; current !== null; current = current.proto) {
|
|
61
|
+
if (hasOwn(current, key))
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
export const set = (target, key, value) => {
|
|
67
|
+
const name = canonical(key);
|
|
68
|
+
const at = index(target, name);
|
|
69
|
+
if (at !== undefined) {
|
|
70
|
+
;
|
|
71
|
+
target.items[at] = value;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (name === "length" && target instanceof ProgramArray) {
|
|
75
|
+
const length = typeof value === "number" ? value : Number(value);
|
|
76
|
+
if (!Number.isInteger(length) || length < 0 || length > 4_294_967_295)
|
|
77
|
+
return false;
|
|
78
|
+
target.items.length = length;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
target.props.set(name, value);
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
export const remove = (target, key) => {
|
|
85
|
+
const name = canonical(key);
|
|
86
|
+
const at = index(target, name);
|
|
87
|
+
if (at !== undefined)
|
|
88
|
+
return delete target.items[at];
|
|
89
|
+
if (name === "length" && target instanceof ProgramArray)
|
|
90
|
+
return false;
|
|
91
|
+
target.props.delete(name);
|
|
92
|
+
return true;
|
|
93
|
+
};
|
|
94
|
+
// JS order: array indexes, integer-like keys ascending, other strings, then symbols.
|
|
95
|
+
export const ownKeys = (target) => {
|
|
96
|
+
const strings = [...target.props.keys()].filter((key) => typeof key === "string");
|
|
97
|
+
const symbols = [...target.props.keys()].filter((key) => typeof key === "symbol");
|
|
98
|
+
return [
|
|
99
|
+
...(target instanceof ProgramArray ? Object.keys(target.items) : []),
|
|
100
|
+
...strings.filter((key) => parseArrayIndex(key) !== undefined).sort((a, b) => Number(a) - Number(b)),
|
|
101
|
+
...strings.filter((key) => parseArrayIndex(key) === undefined),
|
|
102
|
+
...symbols,
|
|
103
|
+
];
|
|
104
|
+
};
|
|
105
|
+
export const ownEntries = (target) => ownKeys(target)
|
|
106
|
+
.filter((key) => typeof key === "string")
|
|
107
|
+
.map((key) => [key, getOwn(target, key)]);
|
|
108
|
+
export const record = (entries) => {
|
|
109
|
+
const target = new ProgramObject();
|
|
110
|
+
for (const [key, value] of Object.entries(entries))
|
|
111
|
+
set(target, key, value);
|
|
112
|
+
return target;
|
|
113
|
+
};
|
|
114
|
+
export const assign = (target, source, skip) => {
|
|
115
|
+
for (const key of ownKeys(source)) {
|
|
116
|
+
if (skip?.has(key))
|
|
117
|
+
continue;
|
|
118
|
+
if (typeof key === "symbol" && key !== IteratorSymbol && key !== AsyncIteratorSymbol)
|
|
119
|
+
continue;
|
|
120
|
+
set(target, key, getOwn(source, key));
|
|
121
|
+
}
|
|
122
|
+
};
|