@opencode/codemode 0.0.0-beta-19500 → 0.0.0-dev-19274
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/README.md +0 -6
- package/dist/codemode.d.ts +12 -9
- package/dist/codemode.js +9 -5
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/interpreter/errors.d.ts +6 -4
- package/dist/interpreter/errors.js +10 -25
- package/dist/interpreter/execute.d.ts +3 -4
- package/dist/interpreter/execute.js +18 -11
- package/dist/interpreter/iterator.d.ts +13 -0
- package/dist/interpreter/iterator.js +4 -0
- package/dist/interpreter/methods.d.ts +16 -3
- package/dist/interpreter/methods.js +290 -101
- package/dist/interpreter/model.d.ts +79 -10
- package/dist/interpreter/model.js +102 -2
- package/dist/interpreter/promises.d.ts +13 -15
- package/dist/interpreter/promises.js +52 -70
- package/dist/interpreter/references.d.ts +0 -1
- package/dist/interpreter/references.js +77 -57
- package/dist/interpreter/runtime.d.ts +95 -16
- package/dist/interpreter/runtime.js +960 -549
- package/dist/openapi/spec.js +6 -3
- package/dist/stdlib/collections.d.ts +1 -6
- package/dist/stdlib/collections.js +1 -117
- package/dist/stdlib/console.d.ts +2 -3
- package/dist/stdlib/console.js +28 -39
- package/dist/stdlib/date.d.ts +4 -5
- package/dist/stdlib/date.js +12 -34
- package/dist/stdlib/json.d.ts +6 -3
- package/dist/stdlib/json.js +63 -40
- package/dist/stdlib/math.d.ts +7 -3
- package/dist/stdlib/math.js +153 -85
- package/dist/stdlib/number.d.ts +4 -2
- package/dist/stdlib/number.js +37 -30
- package/dist/stdlib/object.d.ts +6 -6
- package/dist/stdlib/object.js +87 -84
- package/dist/stdlib/promise.d.ts +2 -0
- package/dist/stdlib/promise.js +1 -0
- package/dist/stdlib/regexp.d.ts +7 -6
- package/dist/stdlib/regexp.js +34 -48
- package/dist/stdlib/string.d.ts +3 -1
- package/dist/stdlib/string.js +17 -20
- package/dist/stdlib/url.d.ts +6 -10
- package/dist/stdlib/url.js +25 -102
- package/dist/stdlib/value.d.ts +7 -8
- package/dist/stdlib/value.js +56 -56
- package/dist/tool-runtime.d.ts +15 -16
- package/dist/tool-runtime.js +150 -13
- package/dist/values.d.ts +16 -22
- package/dist/values.js +17 -23
- package/package.json +1 -1
- package/dist/data.d.ts +0 -25
- package/dist/data.js +0 -153
- package/dist/interpreter/globals.d.ts +0 -13
- package/dist/interpreter/globals.js +0 -63
- package/dist/interpreter/host.d.ts +0 -41
- package/dist/interpreter/host.js +0 -44
- package/dist/interpreter/objects.d.ts +0 -37
- package/dist/interpreter/objects.js +0 -151
- package/dist/interpreter/runner.d.ts +0 -24
- package/dist/interpreter/runner.js +0 -45
- package/dist/stdlib/array.d.ts +0 -3
- package/dist/stdlib/array.js +0 -68
- package/dist/stdlib/web.d.ts +0 -4
- package/dist/stdlib/web.js +0 -20
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
const
|
|
2
|
+
import { CodeModeFunction, CodeModeGenerator, CoercionFunction, ErrorConstructorReference, GlobalMethodReference, GlobalNamespace, IntrinsicReference, InterpreterRuntimeError, JsonMethodReference, PromiseCapabilityFunction, PromiseNamespace, UriFunction, } from "./model.js";
|
|
3
|
+
import { containsOpaqueReference, isRuntimeReference, rejectCircularInsertion, typeofValue } from "./references.js";
|
|
4
|
+
import { isBlockedMember } from "../tool-runtime.js";
|
|
5
|
+
import { CodeModeDate, CodeModeMap, CodeModePromise, CodeModeRegExp, CodeModeSet, CodeModeURL, CodeModeURLSearchParams, isCodeModeValue, } from "../values.js";
|
|
6
|
+
import { dateSetterArgumentCount, invokeDateMethod, invokeDateStatic } from "../stdlib/date.js";
|
|
7
|
+
import { invokeMathMethod } from "../stdlib/math.js";
|
|
8
|
+
import { invokeNumberMethod, invokeNumberStatic } from "../stdlib/number.js";
|
|
9
|
+
import { invokeObjectMethod } from "../stdlib/object.js";
|
|
10
|
+
import { invokeRegExpMethod, invokeRegExpStatic, matchToValue, toHostRegex } from "../stdlib/regexp.js";
|
|
11
|
+
import { invokeStringStatic } from "../stdlib/string.js";
|
|
12
|
+
import { invokeURLMethod, invokeURLStatic, uriArgument } from "../stdlib/url.js";
|
|
13
|
+
import { boundedData, coerceToNumber, coerceToString, errorBrandName } from "../stdlib/value.js";
|
|
14
|
+
import { preserveConsumerError } from "./iterator.js";
|
|
15
|
+
export const isSupportedCallback = (value) => value instanceof CodeModeFunction ||
|
|
16
|
+
value instanceof CoercionFunction ||
|
|
17
|
+
value instanceof UriFunction ||
|
|
18
|
+
value instanceof PromiseCapabilityFunction ||
|
|
19
|
+
value instanceof GlobalMethodReference ||
|
|
20
|
+
value instanceof JsonMethodReference ||
|
|
21
|
+
value instanceof IntrinsicReference ||
|
|
22
|
+
value instanceof ErrorConstructorReference ||
|
|
23
|
+
// Callable namespaces dispatch like JS: Array/Object/Date/RegExp construct,
|
|
24
|
+
// new-requiring constructors throw a TypeError. Math/JSON/console stay non-callable.
|
|
25
|
+
(value instanceof GlobalNamespace && typeofValue(value) === "function") ||
|
|
26
|
+
value instanceof PromiseNamespace;
|
|
27
|
+
export const invokeIntrinsic = (runner, ref, args, node) => {
|
|
16
28
|
if (typeof ref.receiver === "string") {
|
|
17
29
|
if (ref.name === "replace" || ref.name === "replaceAll") {
|
|
18
30
|
if (isSupportedCallback(args[1]))
|
|
@@ -26,10 +38,10 @@ const invoke = (runner, ref, args, node) => {
|
|
|
26
38
|
if (typeof ref.receiver === "number") {
|
|
27
39
|
return Effect.succeed(invokeNumberMethod(ref.receiver, ref.name, args, node));
|
|
28
40
|
}
|
|
29
|
-
if (ref.receiver
|
|
41
|
+
if (Array.isArray(ref.receiver)) {
|
|
30
42
|
return invokeArrayMethod(runner, ref.receiver, ref.name, args, node);
|
|
31
43
|
}
|
|
32
|
-
if (ref.receiver instanceof
|
|
44
|
+
if (ref.receiver instanceof CodeModeDate) {
|
|
33
45
|
const target = ref.receiver;
|
|
34
46
|
const argumentCount = dateSetterArgumentCount(ref.name);
|
|
35
47
|
if (argumentCount === undefined)
|
|
@@ -40,31 +52,70 @@ const invoke = (runner, ref, args, node) => {
|
|
|
40
52
|
concurrency: 1,
|
|
41
53
|
}), (values) => invokeDateMethod(target, ref.name, values, node, initialTime));
|
|
42
54
|
}
|
|
43
|
-
if (ref.receiver instanceof
|
|
55
|
+
if (ref.receiver instanceof CodeModeRegExp) {
|
|
44
56
|
return Effect.succeed(invokeRegExpMethod(ref.receiver, ref.name, args, node));
|
|
45
57
|
}
|
|
46
|
-
if (ref.receiver instanceof
|
|
58
|
+
if (ref.receiver instanceof CodeModeMap) {
|
|
47
59
|
return invokeMapMethod(runner, ref.receiver, ref.name, args, node);
|
|
48
60
|
}
|
|
49
|
-
if (ref.receiver instanceof
|
|
61
|
+
if (ref.receiver instanceof CodeModeSet) {
|
|
50
62
|
return invokeSetMethod(runner, ref.receiver, ref.name, args, node);
|
|
51
63
|
}
|
|
52
|
-
if (ref.receiver instanceof
|
|
64
|
+
if (ref.receiver instanceof CodeModeURL) {
|
|
53
65
|
return Effect.succeed(invokeURLMethod(ref.receiver, ref.name, node));
|
|
54
66
|
}
|
|
55
|
-
if (ref.receiver instanceof
|
|
67
|
+
if (ref.receiver instanceof CodeModeURLSearchParams) {
|
|
56
68
|
return invokeURLSearchParamsMethod(runner, ref.receiver, ref.name, args, node);
|
|
57
69
|
}
|
|
58
70
|
throw new InterpreterRuntimeError(`Method '${ref.name}' is not available.`, node);
|
|
59
71
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
const coerceNumericArgument = (runner, value, node) => {
|
|
73
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || isCodeModeValue(value)) {
|
|
74
|
+
return Effect.succeed(coerceToNumber(value));
|
|
75
|
+
}
|
|
76
|
+
const object = value;
|
|
77
|
+
return Effect.gen(function* () {
|
|
78
|
+
if (Object.hasOwn(object, "valueOf") && typeofValue(object.valueOf) === "function") {
|
|
79
|
+
const result = yield* runner.invokeCallable(object.valueOf, [], node);
|
|
80
|
+
if (result === null || (typeof result !== "object" && typeof result !== "function")) {
|
|
81
|
+
return coerceToNumber(result);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!Object.hasOwn(object, "toString"))
|
|
85
|
+
return coerceToNumber(value);
|
|
86
|
+
if (typeofValue(object.toString) === "function") {
|
|
87
|
+
const result = yield* runner.invokeCallable(object.toString, [], node);
|
|
88
|
+
if (result === null || (typeof result !== "object" && typeof result !== "function")) {
|
|
89
|
+
return coerceToNumber(result);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError");
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
export const invokeGlobalMethod = (ref, args, node) => {
|
|
96
|
+
if (ref.namespace === "console")
|
|
97
|
+
throw new InterpreterRuntimeError(`console.${ref.name} is not available.`, node);
|
|
98
|
+
if (ref.namespace === "Object")
|
|
99
|
+
return invokeObjectMethod(ref.name, args, node);
|
|
100
|
+
if (ref.namespace === "Math")
|
|
101
|
+
return invokeMathMethod(ref.name, args, node);
|
|
102
|
+
if (ref.namespace === "Array")
|
|
103
|
+
return invokeArrayStatic(ref.name, args, node);
|
|
104
|
+
if (ref.namespace === "Number")
|
|
105
|
+
return invokeNumberStatic(ref.name, args, node);
|
|
106
|
+
if (ref.namespace === "String")
|
|
107
|
+
return invokeStringStatic(ref.name, args, node);
|
|
108
|
+
if (ref.namespace === "URL")
|
|
109
|
+
return invokeURLStatic(ref.name, args, node);
|
|
110
|
+
if (ref.namespace === "Date")
|
|
111
|
+
return invokeDateStatic(ref.name, args, node);
|
|
112
|
+
if (ref.namespace === "RegExp")
|
|
113
|
+
return invokeRegExpStatic(ref.name, args, node);
|
|
114
|
+
if (ref.namespace === "Map" || ref.namespace === "Set" || ref.namespace === "URLSearchParams") {
|
|
115
|
+
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available.`, node);
|
|
116
|
+
}
|
|
117
|
+
throw new InterpreterRuntimeError(`${ref.namespace}.${ref.name} is not available.`, node);
|
|
118
|
+
};
|
|
68
119
|
const requireDataArgument = (name, index, arg, node) => {
|
|
69
120
|
if (containsOpaqueReference(arg)) {
|
|
70
121
|
throw new InterpreterRuntimeError(`String.${name} expects argument ${index + 1} to be a data value.`, node, "InvalidDataValue");
|
|
@@ -78,7 +129,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
78
129
|
const optNum = (index) => (args[index] === undefined ? undefined : num(index));
|
|
79
130
|
const optStr = (index) => (args[index] === undefined ? undefined : str(index));
|
|
80
131
|
const rejectRegex = () => {
|
|
81
|
-
if (args[0] instanceof
|
|
132
|
+
if (args[0] instanceof CodeModeRegExp) {
|
|
82
133
|
throw new InterpreterRuntimeError(`String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`, node).as("TypeError");
|
|
83
134
|
}
|
|
84
135
|
};
|
|
@@ -94,11 +145,9 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
94
145
|
result = value.trim();
|
|
95
146
|
break;
|
|
96
147
|
case "trimStart":
|
|
97
|
-
case "trimLeft":
|
|
98
148
|
result = value.trimStart();
|
|
99
149
|
break;
|
|
100
150
|
case "trimEnd":
|
|
101
|
-
case "trimRight":
|
|
102
151
|
result = value.trimEnd();
|
|
103
152
|
break;
|
|
104
153
|
// Locale/options are deliberately unsupported; comparison uses the host default locale.
|
|
@@ -123,7 +172,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
123
172
|
result = requestedLimit !== undefined && requestedLimit >>> 0 === 0 ? [] : [value];
|
|
124
173
|
break;
|
|
125
174
|
}
|
|
126
|
-
if (args[0] instanceof
|
|
175
|
+
if (args[0] instanceof CodeModeRegExp) {
|
|
127
176
|
result = value.split(args[0].regex, optNum(1));
|
|
128
177
|
break;
|
|
129
178
|
}
|
|
@@ -154,7 +203,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
154
203
|
break;
|
|
155
204
|
case "replace":
|
|
156
205
|
case "replaceAll": {
|
|
157
|
-
if (args[0] instanceof
|
|
206
|
+
if (args[0] instanceof CodeModeRegExp) {
|
|
158
207
|
const pattern = args[0].regex;
|
|
159
208
|
const replacement = str(1);
|
|
160
209
|
if (name === "replaceAll" && !pattern.global) {
|
|
@@ -177,7 +226,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
177
226
|
return null;
|
|
178
227
|
// Preserve the own `index` and `groups` properties on non-global matches.
|
|
179
228
|
if (pattern.global)
|
|
180
|
-
return
|
|
229
|
+
return boundedData(matched, "String.match result");
|
|
181
230
|
return matchToValue(matched);
|
|
182
231
|
}
|
|
183
232
|
case "matchAll": {
|
|
@@ -185,7 +234,7 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
185
234
|
if (!pattern.global) {
|
|
186
235
|
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);
|
|
187
236
|
}
|
|
188
|
-
return
|
|
237
|
+
return Array.from(value.matchAll(pattern), matchToValue);
|
|
189
238
|
}
|
|
190
239
|
case "search": {
|
|
191
240
|
result = value.search(toHostRegex(args[0], name, node));
|
|
@@ -213,15 +262,6 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
213
262
|
case "substring":
|
|
214
263
|
result = value.substring(optNum(0) ?? 0, optNum(1));
|
|
215
264
|
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;
|
|
225
265
|
case "charCodeAt":
|
|
226
266
|
result = value.charCodeAt(optNum(0) ?? 0);
|
|
227
267
|
break;
|
|
@@ -238,7 +278,138 @@ const invokeStringMethod = (value, name, args, node) => {
|
|
|
238
278
|
default:
|
|
239
279
|
throw new InterpreterRuntimeError(`String method '${name}' is not available.`, node);
|
|
240
280
|
}
|
|
241
|
-
return
|
|
281
|
+
return boundedData(result, `String.${name} result`);
|
|
282
|
+
};
|
|
283
|
+
export const arrayStatics = new Set(["isArray", "of", "from"]);
|
|
284
|
+
const invokeArrayStatic = (name, args, node) => {
|
|
285
|
+
switch (name) {
|
|
286
|
+
case "isArray":
|
|
287
|
+
return Array.isArray(args[0]);
|
|
288
|
+
case "of":
|
|
289
|
+
return [...args];
|
|
290
|
+
default:
|
|
291
|
+
throw new InterpreterRuntimeError(`Array.${name} is not available.`, node);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const arrayLikeSource = (source, node) => {
|
|
295
|
+
if (source instanceof CodeModePromise) {
|
|
296
|
+
throw new InterpreterRuntimeError("Array.from received an un-awaited Promise; await it before creating the array.", node, "InvalidDataValue");
|
|
297
|
+
}
|
|
298
|
+
if (source !== null &&
|
|
299
|
+
typeof source === "object" &&
|
|
300
|
+
(Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
|
|
301
|
+
typeof source.length === "number") {
|
|
302
|
+
const length = source.length;
|
|
303
|
+
const normalized = Number.isNaN(length) || length <= 0 ? 0 : Math.trunc(length);
|
|
304
|
+
if (normalized > 4_294_967_295)
|
|
305
|
+
throw new RangeError("Invalid array length");
|
|
306
|
+
return { length: normalized, source };
|
|
307
|
+
}
|
|
308
|
+
throw new InterpreterRuntimeError("Array.from expects an array, string, Map, Set, or array-like value.", node, "InvalidDataValue");
|
|
309
|
+
};
|
|
310
|
+
export const invokeArrayFrom = (runner, args, node) => {
|
|
311
|
+
const source = args[0];
|
|
312
|
+
const apply = args.length < 2 || args[1] === undefined ? undefined : applyCollectionCallback(runner, args[1], "Array.from", node);
|
|
313
|
+
return Effect.gen(function* () {
|
|
314
|
+
const cursor = yield* runner.syncIterator(source, node);
|
|
315
|
+
if (cursor === undefined) {
|
|
316
|
+
if (source instanceof CodeModeGenerator) {
|
|
317
|
+
throw new InterpreterRuntimeError("Array.from expects a synchronous iterable or array-like value.", node).as("TypeError");
|
|
318
|
+
}
|
|
319
|
+
const arrayLike = arrayLikeSource(source, node);
|
|
320
|
+
const values = [];
|
|
321
|
+
for (let index = 0; index < arrayLike.length; index += 1) {
|
|
322
|
+
const item = Reflect.get(arrayLike.source, index);
|
|
323
|
+
values.push(apply === undefined ? item : yield* apply([item, index]));
|
|
324
|
+
}
|
|
325
|
+
return values;
|
|
326
|
+
}
|
|
327
|
+
const values = [];
|
|
328
|
+
let index = 0;
|
|
329
|
+
while (true) {
|
|
330
|
+
const step = yield* cursor.next;
|
|
331
|
+
if (step.done)
|
|
332
|
+
return values;
|
|
333
|
+
values.push(apply === undefined ? step.value : yield* preserveConsumerError(cursor, apply([step.value, index])));
|
|
334
|
+
index += 1;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
export const invokeGroupBy = (runner, namespace, args, node) => {
|
|
339
|
+
const source = args[0];
|
|
340
|
+
if (source === null || source === undefined) {
|
|
341
|
+
throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as("TypeError");
|
|
342
|
+
}
|
|
343
|
+
const apply = applyCollectionCallback(runner, args[1], `${namespace}.groupBy`, node);
|
|
344
|
+
return Effect.gen(function* () {
|
|
345
|
+
const cursor = yield* runner.syncIterator(source, node);
|
|
346
|
+
if (cursor === undefined) {
|
|
347
|
+
throw new InterpreterRuntimeError(`${namespace}.groupBy expects an iterable collection.`, node).as("TypeError");
|
|
348
|
+
}
|
|
349
|
+
if (namespace === "Map") {
|
|
350
|
+
const result = new CodeModeMap();
|
|
351
|
+
let index = 0;
|
|
352
|
+
while (true) {
|
|
353
|
+
const step = yield* cursor.next;
|
|
354
|
+
if (step.done)
|
|
355
|
+
return result;
|
|
356
|
+
const item = step.value;
|
|
357
|
+
const key = yield* preserveConsumerError(cursor, apply([item, index]));
|
|
358
|
+
const group = result.map.get(key);
|
|
359
|
+
if (group === undefined)
|
|
360
|
+
result.map.set(key, [item]);
|
|
361
|
+
else
|
|
362
|
+
group.push(item);
|
|
363
|
+
index += 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const result = Object.create(null);
|
|
367
|
+
let index = 0;
|
|
368
|
+
while (true) {
|
|
369
|
+
const step = yield* cursor.next;
|
|
370
|
+
if (step.done)
|
|
371
|
+
return result;
|
|
372
|
+
const item = step.value;
|
|
373
|
+
const key = yield* preserveConsumerError(cursor, Effect.flatMap(apply([item, index]), (value) => coerceGroupByPropertyKey(runner, value, node)));
|
|
374
|
+
if (isBlockedMember(key)) {
|
|
375
|
+
return yield* preserveConsumerError(cursor, Effect.fail(new InterpreterRuntimeError(`Property '${key}' is not available.`, node)));
|
|
376
|
+
}
|
|
377
|
+
const group = result[key];
|
|
378
|
+
if (group === undefined)
|
|
379
|
+
result[key] = [item];
|
|
380
|
+
else
|
|
381
|
+
group.push(item);
|
|
382
|
+
index += 1;
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
const coerceGroupByPropertyKey = (runner, value, node) => {
|
|
387
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || isCodeModeValue(value)) {
|
|
388
|
+
return Effect.succeed(coerceToString(value));
|
|
389
|
+
}
|
|
390
|
+
if (value instanceof CodeModePromise)
|
|
391
|
+
return Effect.succeed("[object Promise]");
|
|
392
|
+
if (isRuntimeReference(value)) {
|
|
393
|
+
throw new InterpreterRuntimeError("Object.groupBy callback must return a data value.", node, "InvalidDataValue");
|
|
394
|
+
}
|
|
395
|
+
const object = value;
|
|
396
|
+
if (!Object.hasOwn(object, "toString"))
|
|
397
|
+
return Effect.succeed(coerceToString(value));
|
|
398
|
+
return Effect.gen(function* () {
|
|
399
|
+
if (typeofValue(object.toString) === "function") {
|
|
400
|
+
const result = yield* runner.invokeCallable(object.toString, [], node);
|
|
401
|
+
if (result === null || (typeof result !== "object" && typeof result !== "function")) {
|
|
402
|
+
return coerceToString(result);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (Object.hasOwn(object, "valueOf") && typeofValue(object.valueOf) === "function") {
|
|
406
|
+
const result = yield* runner.invokeCallable(object.valueOf, [], node);
|
|
407
|
+
if (result === null || (typeof result !== "object" && typeof result !== "function")) {
|
|
408
|
+
return coerceToString(result);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
throw new InterpreterRuntimeError("Cannot convert object to primitive value.", node).as("TypeError");
|
|
412
|
+
});
|
|
242
413
|
};
|
|
243
414
|
const invokeStringReplacer = (runner, value, name, args, node) => {
|
|
244
415
|
const apply = applyCollectionCallback(runner, args[1], `String.${name}`, node);
|
|
@@ -251,13 +422,19 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
|
|
|
251
422
|
if (typeof match !== "string" || typeof offset !== "number") {
|
|
252
423
|
throw new InterpreterRuntimeError(`String.${name} produced an invalid replacement match.`, node);
|
|
253
424
|
}
|
|
254
|
-
if (hasGroups)
|
|
255
|
-
|
|
425
|
+
if (hasGroups) {
|
|
426
|
+
const safeGroups = Object.create(null);
|
|
427
|
+
for (const [key, group] of Object.entries(groups)) {
|
|
428
|
+
if (!isBlockedMember(key))
|
|
429
|
+
safeGroups[key] = group;
|
|
430
|
+
}
|
|
431
|
+
callbackArgs[callbackArgs.length - 1] = safeGroups;
|
|
432
|
+
}
|
|
256
433
|
matches.push({ match, offset, args: callbackArgs });
|
|
257
434
|
return match;
|
|
258
435
|
};
|
|
259
436
|
const pattern = args[0];
|
|
260
|
-
if (pattern instanceof
|
|
437
|
+
if (pattern instanceof CodeModeRegExp) {
|
|
261
438
|
if (name === "replaceAll" && !pattern.regex.global) {
|
|
262
439
|
throw new InterpreterRuntimeError(`String.replaceAll requires a regular expression with the global (g) flag: write /${pattern.regex.source}/${pattern.regex.flags}g, or use String.replace to replace only the first match.`, node);
|
|
263
440
|
}
|
|
@@ -278,15 +455,27 @@ const invokeStringReplacer = (runner, value, name, args, node) => {
|
|
|
278
455
|
let end = 0;
|
|
279
456
|
for (const match of matches) {
|
|
280
457
|
const replacement = yield* apply(match.args);
|
|
281
|
-
|
|
458
|
+
// Error values are branded plain objects; boundedData would strip the brand before coercion.
|
|
459
|
+
output.push(value.slice(end, match.offset), replacement instanceof CodeModePromise
|
|
282
460
|
? "[object Promise]"
|
|
283
|
-
:
|
|
461
|
+
: errorBrandName(replacement)
|
|
462
|
+
? coerceToString(replacement)
|
|
463
|
+
: coerceToString(boundedData(replacement, `String.${name} replacer result`)));
|
|
284
464
|
end = match.offset + match.match.length;
|
|
285
465
|
}
|
|
286
466
|
output.push(value.slice(end));
|
|
287
|
-
return
|
|
467
|
+
return boundedData(output.join(""), `String.${name} result`);
|
|
288
468
|
});
|
|
289
469
|
};
|
|
470
|
+
export const applyCollectionCallback = (runner, callback, name, node) => {
|
|
471
|
+
if (!isSupportedCallback(callback)) {
|
|
472
|
+
if (typeofValue(callback) === "function") {
|
|
473
|
+
throw new InterpreterRuntimeError(`${name} cannot use this callable as a callback; wrap it in an arrow function, e.g. (value) => tools.ns.tool(value).`, node);
|
|
474
|
+
}
|
|
475
|
+
throw new InterpreterRuntimeError(`${name} expects a function callback.`, node).as("TypeError");
|
|
476
|
+
}
|
|
477
|
+
return (callbackArgs) => runner.invokeCallable(callback, callbackArgs, node);
|
|
478
|
+
};
|
|
290
479
|
const invokeMapMethod = (runner, target, name, args, node) => {
|
|
291
480
|
switch (name) {
|
|
292
481
|
case "get":
|
|
@@ -310,7 +499,7 @@ const invokeMapMethod = (runner, target, name, args, node) => {
|
|
|
310
499
|
case "values":
|
|
311
500
|
return Effect.sync(() => Array.from(target.map.values()));
|
|
312
501
|
case "entries":
|
|
313
|
-
return Effect.sync(() => Array.from(target.map.entries(), ([key, item]) =>
|
|
502
|
+
return Effect.sync(() => Array.from(target.map.entries(), ([key, item]) => [key, item]));
|
|
314
503
|
case "forEach": {
|
|
315
504
|
const apply = applyCollectionCallback(runner, args[0], "Map.forEach", node);
|
|
316
505
|
return Effect.gen(function* () {
|
|
@@ -343,7 +532,7 @@ const invokeSetMethod = (runner, target, name, args, node) => {
|
|
|
343
532
|
case "values":
|
|
344
533
|
return Effect.sync(() => Array.from(target.set.values()));
|
|
345
534
|
case "entries":
|
|
346
|
-
return Effect.sync(() => Array.from(target.set.values(), (item) =>
|
|
535
|
+
return Effect.sync(() => Array.from(target.set.values(), (item) => [item, item]));
|
|
347
536
|
case "forEach": {
|
|
348
537
|
const apply = applyCollectionCallback(runner, args[0], "Set.forEach", node);
|
|
349
538
|
return Effect.gen(function* () {
|
|
@@ -373,7 +562,7 @@ const invokeSetOperation = (runner, target, name, source, node) => Effect.gen(fu
|
|
|
373
562
|
return result;
|
|
374
563
|
}
|
|
375
564
|
if (name === "intersection") {
|
|
376
|
-
const result = new
|
|
565
|
+
const result = new CodeModeSet();
|
|
377
566
|
if (target.set.size <= other.size) {
|
|
378
567
|
for (const item of target.set.values()) {
|
|
379
568
|
if (yield* other.has(item))
|
|
@@ -442,45 +631,46 @@ const invokeSetOperation = (runner, target, name, source, node) => Effect.gen(fu
|
|
|
442
631
|
return true;
|
|
443
632
|
});
|
|
444
633
|
const copySet = (source) => {
|
|
445
|
-
const result = new
|
|
634
|
+
const result = new CodeModeSet();
|
|
446
635
|
for (const item of source.set.values())
|
|
447
636
|
result.set.add(item);
|
|
448
637
|
return result;
|
|
449
638
|
};
|
|
450
639
|
const loadSetRecord = (runner, source, name, node) => {
|
|
451
|
-
if (source instanceof
|
|
640
|
+
if (source instanceof CodeModeSet) {
|
|
452
641
|
return Effect.succeed({
|
|
453
642
|
size: source.set.size,
|
|
454
643
|
has: (item) => Effect.succeed(source.set.has(item)),
|
|
455
644
|
keys: () => Effect.succeed(source.set.values()),
|
|
456
645
|
});
|
|
457
646
|
}
|
|
458
|
-
if (source instanceof
|
|
647
|
+
if (source instanceof CodeModeMap) {
|
|
459
648
|
return Effect.succeed({
|
|
460
649
|
size: source.map.size,
|
|
461
650
|
has: (item) => Effect.succeed(source.map.has(item)),
|
|
462
651
|
keys: () => Effect.succeed(source.map.keys()),
|
|
463
652
|
});
|
|
464
653
|
}
|
|
465
|
-
if (
|
|
654
|
+
if (source === null || typeof source !== "object" || isCodeModeValue(source)) {
|
|
466
655
|
throw new InterpreterRuntimeError(`Set.${name} expects a Set-like object.`, node).as("TypeError");
|
|
467
656
|
}
|
|
657
|
+
const object = source;
|
|
468
658
|
return Effect.gen(function* () {
|
|
469
|
-
const size = yield* coerceNumericArgument(runner,
|
|
659
|
+
const size = yield* coerceNumericArgument(runner, object.size, node);
|
|
470
660
|
if (Number.isNaN(size)) {
|
|
471
661
|
throw new InterpreterRuntimeError(`Set.${name} received a Set-like object with an invalid size.`, node).as("TypeError");
|
|
472
662
|
}
|
|
473
|
-
|
|
474
|
-
const keys = get(source, "keys");
|
|
475
|
-
if (!isSupportedCallback(has) || !isSupportedCallback(keys)) {
|
|
663
|
+
if (!isSupportedCallback(object.has) || !isSupportedCallback(object.keys)) {
|
|
476
664
|
throw new InterpreterRuntimeError(`Set.${name} expects callable 'has' and 'keys' methods.`, node).as("TypeError");
|
|
477
665
|
}
|
|
666
|
+
const has = object.has;
|
|
667
|
+
const keys = object.keys;
|
|
478
668
|
return {
|
|
479
669
|
size: Math.max(Math.trunc(size), 0),
|
|
480
670
|
has: (item) => Effect.map(runner.invokeCallable(has, [item], node), Boolean),
|
|
481
671
|
keys: () => Effect.flatMap(runner.invokeCallable(keys, [], node), (result) => {
|
|
482
|
-
if (result
|
|
483
|
-
return Effect.succeed(result
|
|
672
|
+
if (Array.isArray(result))
|
|
673
|
+
return Effect.succeed(result);
|
|
484
674
|
throw new InterpreterRuntimeError(`Set.${name} expected 'keys' to return an iterator.`, node).as("TypeError");
|
|
485
675
|
}),
|
|
486
676
|
};
|
|
@@ -537,7 +727,7 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
|
|
|
537
727
|
case "values":
|
|
538
728
|
return Effect.sync(() => Array.from(target.params.values()));
|
|
539
729
|
case "entries":
|
|
540
|
-
return Effect.sync(() => Array.from(target.params.entries(), ([key, value]) =>
|
|
730
|
+
return Effect.sync(() => Array.from(target.params.entries(), ([key, value]) => [key, value]));
|
|
541
731
|
case "toString":
|
|
542
732
|
return Effect.sync(() => target.params.toString());
|
|
543
733
|
case "forEach": {
|
|
@@ -553,8 +743,7 @@ const invokeURLSearchParamsMethod = (runner, target, name, args, node) => {
|
|
|
553
743
|
throw new InterpreterRuntimeError(`URLSearchParams method '${name}' is not available.`, node);
|
|
554
744
|
}
|
|
555
745
|
};
|
|
556
|
-
const invokeArrayMethod = (runner,
|
|
557
|
-
const target = receiver.items;
|
|
746
|
+
const invokeArrayMethod = (runner, target, name, args, node) => {
|
|
558
747
|
const optNumber = (value, label) => {
|
|
559
748
|
if (value === undefined)
|
|
560
749
|
return undefined;
|
|
@@ -567,7 +756,8 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
567
756
|
if (args.length > 1 || (args.length === 1 && typeof args[0] !== "string")) {
|
|
568
757
|
throw new InterpreterRuntimeError("Array.join expects zero arguments or one string separator.", node);
|
|
569
758
|
}
|
|
570
|
-
|
|
759
|
+
const input = boundedData(target, "Array.join input");
|
|
760
|
+
return Effect.succeed(input.map((item) => coerceToString(item ?? "")).join(args.length === 0 ? "," : args[0]));
|
|
571
761
|
}
|
|
572
762
|
case "includes":
|
|
573
763
|
if (args.length === 0 || args.length > 2)
|
|
@@ -584,14 +774,11 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
584
774
|
case "slice":
|
|
585
775
|
return Effect.succeed(target.slice(optNumber(args[0], "start"), optNumber(args[1], "end")));
|
|
586
776
|
case "concat":
|
|
587
|
-
return Effect.succeed(target.concat(...args
|
|
588
|
-
case "flat":
|
|
589
|
-
|
|
590
|
-
return Effect.succeed(flatten(target, optNumber(args[0], "depth") ?? 1));
|
|
591
|
-
}
|
|
777
|
+
return Effect.succeed(target.concat(...args));
|
|
778
|
+
case "flat":
|
|
779
|
+
return Effect.succeed(target.flat(optNumber(args[0], "depth") ?? 1));
|
|
592
780
|
case "reverse":
|
|
593
|
-
target.reverse();
|
|
594
|
-
return Effect.succeed(receiver);
|
|
781
|
+
return Effect.succeed(target.reverse());
|
|
595
782
|
case "sort": {
|
|
596
783
|
const length = target.length;
|
|
597
784
|
const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(target, index)).filter((own) => !own).length;
|
|
@@ -603,7 +790,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
603
790
|
Array.from({ length: holeCount }, (_, index) => itemCount + index).forEach((index) => {
|
|
604
791
|
Reflect.deleteProperty(target, index);
|
|
605
792
|
});
|
|
606
|
-
return
|
|
793
|
+
return target;
|
|
607
794
|
});
|
|
608
795
|
}
|
|
609
796
|
case "toSorted":
|
|
@@ -623,13 +810,13 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
623
810
|
case "push": {
|
|
624
811
|
// Validate all insertions before mutating to avoid partial cyclic updates.
|
|
625
812
|
for (const item of args)
|
|
626
|
-
rejectCircularInsertion(
|
|
813
|
+
rejectCircularInsertion(target, item, "Array.push result", node);
|
|
627
814
|
target.push(...args);
|
|
628
815
|
return Effect.succeed(target.length);
|
|
629
816
|
}
|
|
630
817
|
case "unshift": {
|
|
631
818
|
for (const item of args)
|
|
632
|
-
rejectCircularInsertion(
|
|
819
|
+
rejectCircularInsertion(target, item, "Array.unshift result", node);
|
|
633
820
|
target.unshift(...args);
|
|
634
821
|
return Effect.succeed(target.length);
|
|
635
822
|
}
|
|
@@ -646,7 +833,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
646
833
|
const deleteCount = optNumber(args[1], "delete count") ?? 0;
|
|
647
834
|
const inserted = args.slice(2);
|
|
648
835
|
for (const item of inserted)
|
|
649
|
-
rejectCircularInsertion(
|
|
836
|
+
rejectCircularInsertion(target, item, "Array.splice result", node);
|
|
650
837
|
return Effect.succeed(target.splice(start, deleteCount, ...inserted));
|
|
651
838
|
}
|
|
652
839
|
case "toSpliced": {
|
|
@@ -664,19 +851,17 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
664
851
|
return Effect.succeed(copied);
|
|
665
852
|
}
|
|
666
853
|
case "fill": {
|
|
667
|
-
rejectCircularInsertion(
|
|
668
|
-
target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end"));
|
|
669
|
-
return Effect.succeed(receiver);
|
|
854
|
+
rejectCircularInsertion(target, args[0], "Array.fill result", node);
|
|
855
|
+
return Effect.succeed(target.fill(args[0], optNumber(args[1], "start"), optNumber(args[2], "end")));
|
|
670
856
|
}
|
|
671
857
|
case "copyWithin":
|
|
672
|
-
target.copyWithin(optNumber(args[0], "target index") ?? 0, optNumber(args[1], "start") ?? 0, optNumber(args[2], "end"));
|
|
673
|
-
return Effect.succeed(receiver);
|
|
858
|
+
return Effect.succeed(target.copyWithin(optNumber(args[0], "target index") ?? 0, optNumber(args[1], "start") ?? 0, optNumber(args[2], "end")));
|
|
674
859
|
case "keys":
|
|
675
860
|
return Effect.succeed(Array.from(target.keys()));
|
|
676
861
|
case "values":
|
|
677
862
|
return Effect.succeed([...target]);
|
|
678
863
|
case "entries":
|
|
679
|
-
return Effect.succeed(Array.from(target.entries(), ([index, item]) =>
|
|
864
|
+
return Effect.succeed(Array.from(target.entries(), ([index, item]) => [index, item]));
|
|
680
865
|
}
|
|
681
866
|
const apply = applyCollectionCallback(runner, args[0], `Array.${name}`, node);
|
|
682
867
|
return Effect.gen(function* () {
|
|
@@ -689,7 +874,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
689
874
|
for (let index = 0; index < length; index += 1) {
|
|
690
875
|
if (!(index in target))
|
|
691
876
|
continue;
|
|
692
|
-
values[index] = yield* apply([target[index], index,
|
|
877
|
+
values[index] = yield* apply([target[index], index, target]);
|
|
693
878
|
}
|
|
694
879
|
return values;
|
|
695
880
|
}
|
|
@@ -698,9 +883,9 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
698
883
|
for (let index = 0; index < length; index += 1) {
|
|
699
884
|
if (!(index in target))
|
|
700
885
|
continue;
|
|
701
|
-
const mapped = yield* apply([target[index], index,
|
|
702
|
-
if (mapped
|
|
703
|
-
values.push(...mapped
|
|
886
|
+
const mapped = yield* apply([target[index], index, target]);
|
|
887
|
+
if (Array.isArray(mapped))
|
|
888
|
+
values.push(...mapped);
|
|
704
889
|
else
|
|
705
890
|
values.push(mapped);
|
|
706
891
|
}
|
|
@@ -712,7 +897,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
712
897
|
if (!(index in target))
|
|
713
898
|
continue;
|
|
714
899
|
const item = target[index];
|
|
715
|
-
if (yield* apply([item, index,
|
|
900
|
+
if (yield* apply([item, index, target]))
|
|
716
901
|
values.push(item);
|
|
717
902
|
}
|
|
718
903
|
return values;
|
|
@@ -720,13 +905,13 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
720
905
|
case "find":
|
|
721
906
|
for (let index = 0; index < length; index += 1) {
|
|
722
907
|
const item = target[index];
|
|
723
|
-
if (yield* apply([item, index,
|
|
908
|
+
if (yield* apply([item, index, target]))
|
|
724
909
|
return item;
|
|
725
910
|
}
|
|
726
911
|
return undefined;
|
|
727
912
|
case "findIndex":
|
|
728
913
|
for (let index = 0; index < length; index += 1) {
|
|
729
|
-
if (yield* apply([target[index], index,
|
|
914
|
+
if (yield* apply([target[index], index, target]))
|
|
730
915
|
return index;
|
|
731
916
|
}
|
|
732
917
|
return -1;
|
|
@@ -734,7 +919,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
734
919
|
for (let index = 0; index < length; index += 1) {
|
|
735
920
|
if (!(index in target))
|
|
736
921
|
continue;
|
|
737
|
-
if (yield* apply([target[index], index,
|
|
922
|
+
if (yield* apply([target[index], index, target]))
|
|
738
923
|
return true;
|
|
739
924
|
}
|
|
740
925
|
return false;
|
|
@@ -742,14 +927,14 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
742
927
|
for (let index = 0; index < length; index += 1) {
|
|
743
928
|
if (!(index in target))
|
|
744
929
|
continue;
|
|
745
|
-
if (!(yield* apply([target[index], index,
|
|
930
|
+
if (!(yield* apply([target[index], index, target])))
|
|
746
931
|
return false;
|
|
747
932
|
}
|
|
748
933
|
return true;
|
|
749
934
|
case "forEach":
|
|
750
935
|
for (let index = 0; index < length; index += 1) {
|
|
751
936
|
if (index in target)
|
|
752
|
-
yield* apply([target[index], index,
|
|
937
|
+
yield* apply([target[index], index, target]);
|
|
753
938
|
}
|
|
754
939
|
return undefined;
|
|
755
940
|
case "reduce": {
|
|
@@ -766,7 +951,7 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
766
951
|
for (let index = start; index < length; index += 1) {
|
|
767
952
|
if (!(index in target))
|
|
768
953
|
continue;
|
|
769
|
-
accumulator = yield* apply([accumulator, target[index], index,
|
|
954
|
+
accumulator = yield* apply([accumulator, target[index], index, target]);
|
|
770
955
|
}
|
|
771
956
|
return accumulator;
|
|
772
957
|
}
|
|
@@ -784,20 +969,20 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
784
969
|
for (let index = start; index >= 0; index -= 1) {
|
|
785
970
|
if (!(index in target))
|
|
786
971
|
continue;
|
|
787
|
-
accumulator = yield* apply([accumulator, target[index], index,
|
|
972
|
+
accumulator = yield* apply([accumulator, target[index], index, target]);
|
|
788
973
|
}
|
|
789
974
|
return accumulator;
|
|
790
975
|
}
|
|
791
976
|
case "findLast":
|
|
792
977
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
793
978
|
const item = target[index];
|
|
794
|
-
if (yield* apply([item, index,
|
|
979
|
+
if (yield* apply([item, index, target]))
|
|
795
980
|
return item;
|
|
796
981
|
}
|
|
797
982
|
return undefined;
|
|
798
983
|
case "findLastIndex":
|
|
799
984
|
for (let index = length - 1; index >= 0; index -= 1) {
|
|
800
|
-
if (yield* apply([target[index], index,
|
|
985
|
+
if (yield* apply([target[index], index, target]))
|
|
801
986
|
return index;
|
|
802
987
|
}
|
|
803
988
|
return -1;
|
|
@@ -807,7 +992,11 @@ const invokeArrayMethod = (runner, receiver, name, args, node) => {
|
|
|
807
992
|
};
|
|
808
993
|
const sortArray = (runner, target, comparator, name, node) => {
|
|
809
994
|
if (comparator === undefined) {
|
|
810
|
-
return Effect.sync(() => [...target].sort((a, b) =>
|
|
995
|
+
return Effect.sync(() => [...target].sort((a, b) => {
|
|
996
|
+
const left = coerceToString(a);
|
|
997
|
+
const right = coerceToString(b);
|
|
998
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
999
|
+
}));
|
|
811
1000
|
}
|
|
812
1001
|
const apply = applyCollectionCallback(runner, comparator, name, node);
|
|
813
1002
|
const mergeSort = (items) => {
|