@opencode/codemode 0.0.0-beta-19425 → 0.0.0-beta-19507
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/codemode.d.ts +1 -1
- package/dist/codemode.js +1 -1
- package/dist/data.d.ts +4 -6
- package/dist/data.js +44 -18
- package/dist/interpreter/errors.js +3 -4
- package/dist/interpreter/execute.d.ts +2 -1
- package/dist/interpreter/execute.js +2 -2
- package/dist/interpreter/globals.js +4 -0
- package/dist/interpreter/methods.js +64 -55
- package/dist/interpreter/model.d.ts +3 -11
- package/dist/interpreter/model.js +0 -14
- package/dist/interpreter/objects.d.ts +37 -0
- package/dist/interpreter/objects.js +151 -0
- package/dist/interpreter/promises.d.ts +3 -0
- package/dist/interpreter/promises.js +22 -27
- package/dist/interpreter/references.js +9 -11
- package/dist/interpreter/runner.d.ts +4 -3
- package/dist/interpreter/runner.js +9 -6
- package/dist/interpreter/runtime.d.ts +2 -1
- package/dist/interpreter/runtime.js +234 -241
- package/dist/stdlib/array.js +12 -14
- package/dist/stdlib/collections.js +10 -9
- package/dist/stdlib/console.js +16 -14
- package/dist/stdlib/date.js +6 -0
- package/dist/stdlib/json.js +21 -36
- package/dist/stdlib/object.d.ts +2 -0
- package/dist/stdlib/object.js +38 -62
- package/dist/stdlib/regexp.d.ts +2 -1
- package/dist/stdlib/regexp.js +14 -20
- package/dist/stdlib/string.js +5 -0
- package/dist/stdlib/url.js +3 -3
- package/dist/stdlib/value.d.ts +3 -3
- package/dist/stdlib/value.js +25 -26
- package/dist/stdlib/web.d.ts +4 -0
- package/dist/stdlib/web.js +20 -0
- package/dist/tool-runtime.d.ts +1 -1
- package/package.json +1 -1
package/dist/codemode.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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
|
|
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, 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
|
|
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,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 ?
|
|
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
|
-
|
|
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") {
|
|
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 (
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
124
|
-
//
|
|
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
|
|
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 {
|
|
@@ -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
|
-
|
|
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
|
|
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) {
|
|
@@ -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
|
-
|
|
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
|
-
:
|
|
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
|
|
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,
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
661
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
693
|
-
if (
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 {
|
|
1
|
+
import type { Node } 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,17 +23,9 @@ 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
|
-
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;
|