@opencode-ai/codemode 0.0.0-next-17250
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 +168 -0
- package/dist/codemode.d.ts +148 -0
- package/dist/codemode.js +70 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/interpreter/errors.d.ts +9 -0
- package/dist/interpreter/errors.js +91 -0
- package/dist/interpreter/execute.d.ts +4 -0
- package/dist/interpreter/execute.js +183 -0
- package/dist/interpreter/iterator.d.ts +13 -0
- package/dist/interpreter/iterator.js +4 -0
- package/dist/interpreter/methods.d.ts +17 -0
- package/dist/interpreter/methods.js +1026 -0
- package/dist/interpreter/model.d.ts +151 -0
- package/dist/interpreter/model.js +186 -0
- package/dist/interpreter/promises.d.ts +29 -0
- package/dist/interpreter/promises.js +253 -0
- package/dist/interpreter/references.d.ts +6 -0
- package/dist/interpreter/references.js +114 -0
- package/dist/interpreter/runtime.d.ts +98 -0
- package/dist/interpreter/runtime.js +2351 -0
- package/dist/interpreter/scope.d.ts +15 -0
- package/dist/interpreter/scope.js +79 -0
- package/dist/openapi/index.d.ts +7 -0
- package/dist/openapi/index.js +101 -0
- package/dist/openapi/runtime.d.ts +4 -0
- package/dist/openapi/runtime.js +283 -0
- package/dist/openapi/spec.d.ts +20 -0
- package/dist/openapi/spec.js +588 -0
- package/dist/openapi/types.d.ts +122 -0
- package/dist/openapi/types.js +2 -0
- package/dist/stdlib/collections.d.ts +4 -0
- package/dist/stdlib/collections.js +57 -0
- package/dist/stdlib/console.d.ts +2 -0
- package/dist/stdlib/console.js +126 -0
- package/dist/stdlib/date.d.ts +7 -0
- package/dist/stdlib/date.js +186 -0
- package/dist/stdlib/json.d.ts +6 -0
- package/dist/stdlib/json.js +124 -0
- package/dist/stdlib/math.d.ts +12 -0
- package/dist/stdlib/math.js +157 -0
- package/dist/stdlib/number.d.ts +6 -0
- package/dist/stdlib/number.js +76 -0
- package/dist/stdlib/object.d.ts +7 -0
- package/dist/stdlib/object.js +100 -0
- package/dist/stdlib/promise.d.ts +2 -0
- package/dist/stdlib/promise.js +1 -0
- package/dist/stdlib/regexp.d.ts +11 -0
- package/dist/stdlib/regexp.js +106 -0
- package/dist/stdlib/string.d.ts +4 -0
- package/dist/stdlib/string.js +48 -0
- package/dist/stdlib/url.d.ts +12 -0
- package/dist/stdlib/url.js +84 -0
- package/dist/stdlib/value.d.ts +12 -0
- package/dist/stdlib/value.js +120 -0
- package/dist/tool-error.d.ts +11 -0
- package/dist/tool-error.js +9 -0
- package/dist/tool-runtime.d.ts +68 -0
- package/dist/tool-runtime.js +390 -0
- package/dist/tool-schema.d.ts +15 -0
- package/dist/tool-schema.js +213 -0
- package/dist/tool.d.ts +55 -0
- package/dist/tool.js +21 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.js +1 -0
- package/dist/values.d.ts +31 -0
- package/dist/values.js +50 -0
- package/package.json +40 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { preserveConsumerError } from "../interpreter/iterator.js";
|
|
3
|
+
import { InterpreterRuntimeError } from "../interpreter/model.js";
|
|
4
|
+
export const mathConstants = new Set(["PI", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT2", "SQRT1_2"]);
|
|
5
|
+
export const mathMethods = new Set([
|
|
6
|
+
"random",
|
|
7
|
+
"max",
|
|
8
|
+
"min",
|
|
9
|
+
"abs",
|
|
10
|
+
"acos",
|
|
11
|
+
"acosh",
|
|
12
|
+
"asin",
|
|
13
|
+
"asinh",
|
|
14
|
+
"atan",
|
|
15
|
+
"atan2",
|
|
16
|
+
"atanh",
|
|
17
|
+
"floor",
|
|
18
|
+
"ceil",
|
|
19
|
+
"round",
|
|
20
|
+
"trunc",
|
|
21
|
+
"sign",
|
|
22
|
+
"sqrt",
|
|
23
|
+
"cbrt",
|
|
24
|
+
"pow",
|
|
25
|
+
"hypot",
|
|
26
|
+
"cos",
|
|
27
|
+
"cosh",
|
|
28
|
+
"sin",
|
|
29
|
+
"sinh",
|
|
30
|
+
"tan",
|
|
31
|
+
"tanh",
|
|
32
|
+
"log",
|
|
33
|
+
"log2",
|
|
34
|
+
"log10",
|
|
35
|
+
"log1p",
|
|
36
|
+
"exp",
|
|
37
|
+
"expm1",
|
|
38
|
+
"f16round",
|
|
39
|
+
"fround",
|
|
40
|
+
"clz32",
|
|
41
|
+
"imul",
|
|
42
|
+
"sumPrecise",
|
|
43
|
+
]);
|
|
44
|
+
export const invokeMathMethod = (name, args, node) => {
|
|
45
|
+
if (!mathMethods.has(name))
|
|
46
|
+
throw new InterpreterRuntimeError(`Math.${name} is not available.`, node);
|
|
47
|
+
if (name === "random")
|
|
48
|
+
return Math.random();
|
|
49
|
+
// Validate only the arguments the method consumes; like JS, extras are ignored
|
|
50
|
+
// (so built-ins work as callbacks receiving (element, index, array)).
|
|
51
|
+
const num = (index) => {
|
|
52
|
+
if (index >= args.length)
|
|
53
|
+
return Number.NaN;
|
|
54
|
+
const arg = args[index];
|
|
55
|
+
if (typeof arg !== "number")
|
|
56
|
+
throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node);
|
|
57
|
+
return arg;
|
|
58
|
+
};
|
|
59
|
+
const nums = () => args.map((arg) => {
|
|
60
|
+
if (typeof arg !== "number")
|
|
61
|
+
throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node);
|
|
62
|
+
return arg;
|
|
63
|
+
});
|
|
64
|
+
const a = num(0);
|
|
65
|
+
const b = () => num(1);
|
|
66
|
+
switch (name) {
|
|
67
|
+
case "max":
|
|
68
|
+
return Math.max(...nums());
|
|
69
|
+
case "min":
|
|
70
|
+
return Math.min(...nums());
|
|
71
|
+
case "abs":
|
|
72
|
+
return Math.abs(a);
|
|
73
|
+
case "acos":
|
|
74
|
+
return Math.acos(a);
|
|
75
|
+
case "acosh":
|
|
76
|
+
return Math.acosh(a);
|
|
77
|
+
case "asin":
|
|
78
|
+
return Math.asin(a);
|
|
79
|
+
case "asinh":
|
|
80
|
+
return Math.asinh(a);
|
|
81
|
+
case "atan":
|
|
82
|
+
return Math.atan(a);
|
|
83
|
+
case "atan2":
|
|
84
|
+
return Math.atan2(a, b());
|
|
85
|
+
case "atanh":
|
|
86
|
+
return Math.atanh(a);
|
|
87
|
+
case "floor":
|
|
88
|
+
return Math.floor(a);
|
|
89
|
+
case "ceil":
|
|
90
|
+
return Math.ceil(a);
|
|
91
|
+
case "round":
|
|
92
|
+
return Math.round(a);
|
|
93
|
+
case "trunc":
|
|
94
|
+
return Math.trunc(a);
|
|
95
|
+
case "sign":
|
|
96
|
+
return Math.sign(a);
|
|
97
|
+
case "sqrt":
|
|
98
|
+
return Math.sqrt(a);
|
|
99
|
+
case "cbrt":
|
|
100
|
+
return Math.cbrt(a);
|
|
101
|
+
case "pow":
|
|
102
|
+
return Math.pow(a, b());
|
|
103
|
+
case "hypot":
|
|
104
|
+
return Math.hypot(...nums());
|
|
105
|
+
case "cos":
|
|
106
|
+
return Math.cos(a);
|
|
107
|
+
case "cosh":
|
|
108
|
+
return Math.cosh(a);
|
|
109
|
+
case "sin":
|
|
110
|
+
return Math.sin(a);
|
|
111
|
+
case "sinh":
|
|
112
|
+
return Math.sinh(a);
|
|
113
|
+
case "tan":
|
|
114
|
+
return Math.tan(a);
|
|
115
|
+
case "tanh":
|
|
116
|
+
return Math.tanh(a);
|
|
117
|
+
case "log":
|
|
118
|
+
return Math.log(a);
|
|
119
|
+
case "log2":
|
|
120
|
+
return Math.log2(a);
|
|
121
|
+
case "log10":
|
|
122
|
+
return Math.log10(a);
|
|
123
|
+
case "log1p":
|
|
124
|
+
return Math.log1p(a);
|
|
125
|
+
case "exp":
|
|
126
|
+
return Math.exp(a);
|
|
127
|
+
case "expm1":
|
|
128
|
+
return Math.expm1(a);
|
|
129
|
+
case "f16round":
|
|
130
|
+
return Math.f16round(a);
|
|
131
|
+
case "fround":
|
|
132
|
+
return Math.fround(a);
|
|
133
|
+
case "clz32":
|
|
134
|
+
return Math.clz32(a);
|
|
135
|
+
case "imul":
|
|
136
|
+
return Math.imul(a, b());
|
|
137
|
+
}
|
|
138
|
+
throw new InterpreterRuntimeError(`Math.${name} is not available.`, node);
|
|
139
|
+
};
|
|
140
|
+
export const invokeMathSumPrecise = (runner, source, node) => Effect.gen(function* () {
|
|
141
|
+
const cursor = yield* runner.syncIterator(source, node);
|
|
142
|
+
if (cursor === undefined) {
|
|
143
|
+
throw new InterpreterRuntimeError("Math.sumPrecise expects a synchronous iterable.", node).as("TypeError");
|
|
144
|
+
}
|
|
145
|
+
const numbers = [];
|
|
146
|
+
while (true) {
|
|
147
|
+
const step = yield* cursor.next;
|
|
148
|
+
if (step.done)
|
|
149
|
+
return Math.sumPrecise(numbers);
|
|
150
|
+
yield* preserveConsumerError(cursor, Effect.sync(() => {
|
|
151
|
+
if (typeof step.value !== "number") {
|
|
152
|
+
throw new InterpreterRuntimeError("Math.sumPrecise expects an iterable of numbers.", node).as("TypeError");
|
|
153
|
+
}
|
|
154
|
+
numbers.push(step.value);
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const numberMethods: Set<string>;
|
|
2
|
+
export declare const numberConstants: Set<string>;
|
|
3
|
+
export declare const numberStatics: Set<string>;
|
|
4
|
+
export declare const invokeNumberMethod: (value: number, name: string, args: Array<unknown>, node: AstNode) => unknown;
|
|
5
|
+
export declare const invokeNumberStatic: (name: string, args: Array<unknown>, node: AstNode) => unknown;
|
|
6
|
+
import { type AstNode } from "../interpreter/model.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"]);
|
|
2
|
+
export const numberConstants = new Set([
|
|
3
|
+
"MAX_SAFE_INTEGER",
|
|
4
|
+
"MIN_SAFE_INTEGER",
|
|
5
|
+
"MAX_VALUE",
|
|
6
|
+
"MIN_VALUE",
|
|
7
|
+
"EPSILON",
|
|
8
|
+
"NaN",
|
|
9
|
+
"POSITIVE_INFINITY",
|
|
10
|
+
"NEGATIVE_INFINITY",
|
|
11
|
+
]);
|
|
12
|
+
export const numberStatics = new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"]);
|
|
13
|
+
export const invokeNumberMethod = (value, name, args, node) => {
|
|
14
|
+
const optNum = (index) => {
|
|
15
|
+
const arg = args[index];
|
|
16
|
+
if (arg === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (typeof arg !== "number")
|
|
19
|
+
throw new InterpreterRuntimeError(`Number.${name} expects a number argument.`, node);
|
|
20
|
+
return arg;
|
|
21
|
+
};
|
|
22
|
+
let result;
|
|
23
|
+
switch (name) {
|
|
24
|
+
case "toFixed":
|
|
25
|
+
result = value.toFixed(optNum(0));
|
|
26
|
+
break;
|
|
27
|
+
case "toExponential":
|
|
28
|
+
result = value.toExponential(optNum(0));
|
|
29
|
+
break;
|
|
30
|
+
case "toPrecision": {
|
|
31
|
+
const digits = optNum(0);
|
|
32
|
+
result = digits === undefined ? value.toString() : value.toPrecision(digits);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case "toString": {
|
|
36
|
+
const radix = optNum(0);
|
|
37
|
+
if (radix !== undefined && (radix < 2 || radix > 36)) {
|
|
38
|
+
throw new InterpreterRuntimeError("Number.toString radix must be between 2 and 36.", node);
|
|
39
|
+
}
|
|
40
|
+
result = value.toString(radix);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case "valueOf":
|
|
44
|
+
result = value;
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
throw new InterpreterRuntimeError(`Number method '${name}' is not available.`, node);
|
|
48
|
+
}
|
|
49
|
+
return boundedData(result, `Number.${name} result`);
|
|
50
|
+
};
|
|
51
|
+
export const invokeNumberStatic = (name, args, node) => {
|
|
52
|
+
const value = args[0];
|
|
53
|
+
switch (name) {
|
|
54
|
+
case "isInteger":
|
|
55
|
+
return Number.isInteger(value);
|
|
56
|
+
case "isFinite":
|
|
57
|
+
return Number.isFinite(value);
|
|
58
|
+
case "isNaN":
|
|
59
|
+
return Number.isNaN(value);
|
|
60
|
+
case "isSafeInteger":
|
|
61
|
+
return Number.isSafeInteger(value);
|
|
62
|
+
case "parseInt": {
|
|
63
|
+
const radix = args[1];
|
|
64
|
+
if (radix !== undefined && typeof radix !== "number") {
|
|
65
|
+
throw new InterpreterRuntimeError("Number.parseInt expects a numeric radix.", node);
|
|
66
|
+
}
|
|
67
|
+
return parseInt(coerceToString(value), radix);
|
|
68
|
+
}
|
|
69
|
+
case "parseFloat":
|
|
70
|
+
return parseFloat(coerceToString(value));
|
|
71
|
+
default:
|
|
72
|
+
throw new InterpreterRuntimeError(`Number.${name} is not available.`, node);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
import { InterpreterRuntimeError } from "../interpreter/model.js";
|
|
76
|
+
import { boundedData, coerceToString } from "./value.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type AstNode } from "../interpreter/model.js";
|
|
3
|
+
import { type SyncIteratorRunner } from "../interpreter/iterator.js";
|
|
4
|
+
export declare const objectMethodsPreservingIdentity: Set<string>;
|
|
5
|
+
export declare const objectStatics: Set<string>;
|
|
6
|
+
export declare const invokeObjectMethod: (name: string, args: Array<unknown>, node: AstNode) => unknown;
|
|
7
|
+
export declare const invokeObjectFromEntries: <R>(runner: SyncIteratorRunner<R>, source: unknown, node: AstNode) => Effect.Effect<Record<string, unknown>, unknown, R>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol, IteratorSymbols, } from "../interpreter/model.js";
|
|
3
|
+
import { containsOpaqueReference } from "../interpreter/references.js";
|
|
4
|
+
import { isBlockedMember } from "../tool-runtime.js";
|
|
5
|
+
import { isCodeModeValue, CodeModePromise } from "../values.js";
|
|
6
|
+
import { boundedData, coerceToString } from "./value.js";
|
|
7
|
+
import { preserveConsumerError } from "../interpreter/iterator.js";
|
|
8
|
+
export const objectMethodsPreservingIdentity = new Set(["assign", "values", "entries", "fromEntries"]);
|
|
9
|
+
export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "is", "assign", "fromEntries", "groupBy"]);
|
|
10
|
+
export const invokeObjectMethod = (name, args, node) => {
|
|
11
|
+
const requireObject = () => {
|
|
12
|
+
const input = args[0];
|
|
13
|
+
if (Array.isArray(input))
|
|
14
|
+
return input;
|
|
15
|
+
if (isCodeModeValue(input))
|
|
16
|
+
return {};
|
|
17
|
+
if (input instanceof CodeModePromise) {
|
|
18
|
+
throw new InterpreterRuntimeError(`Object.${name} received an un-awaited Promise; await it before inspecting the result.`, node, "InvalidDataValue");
|
|
19
|
+
}
|
|
20
|
+
if (input === null || typeof input !== "object") {
|
|
21
|
+
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
|
|
22
|
+
}
|
|
23
|
+
const prototype = Object.getPrototypeOf(input);
|
|
24
|
+
if (prototype !== null && prototype !== Object.prototype) {
|
|
25
|
+
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
|
|
26
|
+
}
|
|
27
|
+
return input;
|
|
28
|
+
};
|
|
29
|
+
const guardedSet = (out, key, item) => {
|
|
30
|
+
if (isBlockedMember(key))
|
|
31
|
+
throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node);
|
|
32
|
+
out[key] = item;
|
|
33
|
+
};
|
|
34
|
+
switch (name) {
|
|
35
|
+
case "keys":
|
|
36
|
+
return Object.keys(requireObject());
|
|
37
|
+
case "values":
|
|
38
|
+
return Object.values(requireObject());
|
|
39
|
+
case "entries":
|
|
40
|
+
return Object.entries(requireObject()).map(([key, item]) => [key, item]);
|
|
41
|
+
case "hasOwn":
|
|
42
|
+
return Object.hasOwn(requireObject(), args[1] === AsyncIteratorSymbol || args[1] === IteratorSymbol ? args[1] : String(args[1]));
|
|
43
|
+
case "is":
|
|
44
|
+
if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
|
|
45
|
+
throw new InterpreterRuntimeError("Object.is requires data values.", node, "InvalidDataValue");
|
|
46
|
+
}
|
|
47
|
+
return Object.is(args[0], args[1]);
|
|
48
|
+
case "assign": {
|
|
49
|
+
const target = args[0];
|
|
50
|
+
if (target === null || typeof target !== "object" || Array.isArray(target) || isCodeModeValue(target)) {
|
|
51
|
+
throw new InterpreterRuntimeError("Object.assign expects a data object target.", node);
|
|
52
|
+
}
|
|
53
|
+
const out = target;
|
|
54
|
+
for (const source of args.slice(1)) {
|
|
55
|
+
if (source === null || source === undefined || isCodeModeValue(source))
|
|
56
|
+
continue;
|
|
57
|
+
if (typeof source !== "object" || Array.isArray(source)) {
|
|
58
|
+
throw new InterpreterRuntimeError("Object.assign expects data objects.", node);
|
|
59
|
+
}
|
|
60
|
+
for (const [key, item] of Object.entries(source))
|
|
61
|
+
guardedSet(out, key, item);
|
|
62
|
+
for (const symbol of IteratorSymbols) {
|
|
63
|
+
if (Object.hasOwn(source, symbol))
|
|
64
|
+
Reflect.set(out, symbol, Reflect.get(source, symbol));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new InterpreterRuntimeError(`Object.${name} is not available.`, node);
|
|
71
|
+
};
|
|
72
|
+
export const invokeObjectFromEntries = (runner, source, node) => {
|
|
73
|
+
const out = Object.create(null);
|
|
74
|
+
return Effect.gen(function* () {
|
|
75
|
+
const cursor = yield* runner.syncIterator(source, node);
|
|
76
|
+
if (cursor === undefined) {
|
|
77
|
+
throw new InterpreterRuntimeError("Object.fromEntries expects a synchronous iterable of entries.", node).as("TypeError");
|
|
78
|
+
}
|
|
79
|
+
while (true) {
|
|
80
|
+
const step = yield* cursor.next;
|
|
81
|
+
if (step.done)
|
|
82
|
+
return out;
|
|
83
|
+
yield* preserveConsumerError(cursor, Effect.sync(() => {
|
|
84
|
+
if (step.value === null ||
|
|
85
|
+
typeof step.value !== "object" ||
|
|
86
|
+
isCodeModeValue(step.value) ||
|
|
87
|
+
containsOpaqueReference(step.value)) {
|
|
88
|
+
throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node).as("TypeError");
|
|
89
|
+
}
|
|
90
|
+
const entry = step.value;
|
|
91
|
+
boundedData(entry[0], "Object.fromEntries key");
|
|
92
|
+
boundedData(entry[1], "Object.fromEntries value");
|
|
93
|
+
const key = coerceToString(entry[0]);
|
|
94
|
+
if (isBlockedMember(key))
|
|
95
|
+
throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node);
|
|
96
|
+
out[key] = entry[1];
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const promiseStatics = new Set(["all", "allSettled", "race", "any", "resolve", "reject"]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type AstNode } from "../interpreter/model.js";
|
|
2
|
+
import { CodeModeRegExp } from "../values.js";
|
|
3
|
+
export declare const regexpMethods: Set<string>;
|
|
4
|
+
export declare const regexpStatics: Set<string>;
|
|
5
|
+
export declare const regexpProperties: Set<string>;
|
|
6
|
+
export declare const regexFailureReason: (error: unknown) => string;
|
|
7
|
+
export declare const escapeRegexHint = "To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. \"\\\\(\") or test for them with String.includes instead.";
|
|
8
|
+
export declare const toHostRegex: (arg: unknown, method: string, node: AstNode, extraFlags?: string) => RegExp;
|
|
9
|
+
export declare const matchToValue: (match: RegExpMatchArray) => Array<unknown>;
|
|
10
|
+
export declare const invokeRegExpStatic: (name: string, args: Array<unknown>, node: AstNode) => string;
|
|
11
|
+
export declare const invokeRegExpMethod: (value: CodeModeRegExp, name: string, args: Array<unknown>, node: AstNode) => unknown;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { InterpreterRuntimeError } from "../interpreter/model.js";
|
|
2
|
+
import { isBlockedMember } from "../tool-runtime.js";
|
|
3
|
+
import { CodeModeRegExp } from "../values.js";
|
|
4
|
+
import { coerceToNumber, coerceToString } from "./value.js";
|
|
5
|
+
export const regexpMethods = new Set(["test", "exec", "toString"]);
|
|
6
|
+
export const regexpStatics = new Set(["escape"]);
|
|
7
|
+
export const regexpProperties = new Set([
|
|
8
|
+
"source",
|
|
9
|
+
"flags",
|
|
10
|
+
"lastIndex",
|
|
11
|
+
"hasIndices",
|
|
12
|
+
"global",
|
|
13
|
+
"ignoreCase",
|
|
14
|
+
"multiline",
|
|
15
|
+
"sticky",
|
|
16
|
+
"unicode",
|
|
17
|
+
"unicodeSets",
|
|
18
|
+
"dotAll",
|
|
19
|
+
]);
|
|
20
|
+
export const regexFailureReason = (error) => (error instanceof Error ? error.message : String(error)).replace(/^Invalid regular expression:\s*/i, "");
|
|
21
|
+
export const escapeRegexHint = 'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.';
|
|
22
|
+
export const toHostRegex = (arg, method, node, extraFlags = "") => {
|
|
23
|
+
// Native parity: an undefined pattern behaves as an empty pattern.
|
|
24
|
+
if (arg === undefined)
|
|
25
|
+
return new RegExp("", extraFlags);
|
|
26
|
+
if (arg instanceof CodeModeRegExp)
|
|
27
|
+
return arg.regex;
|
|
28
|
+
if (typeof arg === "string") {
|
|
29
|
+
try {
|
|
30
|
+
return new RegExp(arg, extraFlags);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new InterpreterRuntimeError(`String.${method} received the string ${JSON.stringify(arg)}, which is not a valid regular expression pattern (${regexFailureReason(error)}). ${escapeRegexHint}`, node).as("SyntaxError");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw new InterpreterRuntimeError(`String.${method} expects a regular expression (a /pattern/flags literal or new RegExp(...)) or a string pattern, not ${arg === null ? "null" : typeof arg}.`, node);
|
|
37
|
+
};
|
|
38
|
+
export const matchToValue = (match) => {
|
|
39
|
+
const result = Array.from(match, (group) => group);
|
|
40
|
+
if (match.index !== undefined)
|
|
41
|
+
result.index = match.index;
|
|
42
|
+
if (match.groups) {
|
|
43
|
+
const groups = Object.create(null);
|
|
44
|
+
for (const [key, group] of Object.entries(match.groups)) {
|
|
45
|
+
if (!isBlockedMember(key))
|
|
46
|
+
groups[key] = group;
|
|
47
|
+
}
|
|
48
|
+
result.groups = groups;
|
|
49
|
+
}
|
|
50
|
+
if (match.indices)
|
|
51
|
+
result.indices = indicesToValue(match.indices);
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
export const invokeRegExpStatic = (name, args, node) => {
|
|
55
|
+
if (name !== "escape")
|
|
56
|
+
throw new InterpreterRuntimeError(`RegExp.${name} is not available.`, node);
|
|
57
|
+
if (typeof args[0] !== "string") {
|
|
58
|
+
throw new InterpreterRuntimeError("RegExp.escape expects a string.", node).as("TypeError");
|
|
59
|
+
}
|
|
60
|
+
return RegExp.escape(args[0]);
|
|
61
|
+
};
|
|
62
|
+
export const invokeRegExpMethod = (value, name, args, node) => {
|
|
63
|
+
switch (name) {
|
|
64
|
+
case "test":
|
|
65
|
+
case "exec": {
|
|
66
|
+
const input = coerceToString(args[0]);
|
|
67
|
+
const lastIndex = value.lastIndex;
|
|
68
|
+
const stateful = value.regex.global || value.regex.sticky;
|
|
69
|
+
value.regex.lastIndex = toLength(lastIndex);
|
|
70
|
+
if (name === "test") {
|
|
71
|
+
const matched = value.regex.test(input);
|
|
72
|
+
if (!stateful)
|
|
73
|
+
value.lastIndex = lastIndex;
|
|
74
|
+
return matched;
|
|
75
|
+
}
|
|
76
|
+
const matched = value.regex.exec(input);
|
|
77
|
+
if (!stateful)
|
|
78
|
+
value.lastIndex = lastIndex;
|
|
79
|
+
return matched === null ? null : matchToValue(matched);
|
|
80
|
+
}
|
|
81
|
+
case "toString":
|
|
82
|
+
return coerceToString(value);
|
|
83
|
+
default:
|
|
84
|
+
throw new InterpreterRuntimeError(`RegExp method '${name}' is not available.`, node);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const toLength = (value) => {
|
|
88
|
+
const number = coerceToNumber(value);
|
|
89
|
+
if (Number.isNaN(number) || number <= 0)
|
|
90
|
+
return 0;
|
|
91
|
+
return Math.min(Math.floor(number), Number.MAX_SAFE_INTEGER);
|
|
92
|
+
};
|
|
93
|
+
const indicesToValue = (indices) => {
|
|
94
|
+
const result = Array.from(indices, (range) => (range === undefined ? undefined : [...range]));
|
|
95
|
+
if (indices.groups) {
|
|
96
|
+
const groups = Object.create(null);
|
|
97
|
+
for (const [key, range] of Object.entries(indices.groups)) {
|
|
98
|
+
if (!isBlockedMember(key))
|
|
99
|
+
groups[key] = range === undefined ? undefined : [...range];
|
|
100
|
+
}
|
|
101
|
+
result.groups = groups;
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
result.groups = undefined;
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const stringMethods = new Set([
|
|
2
|
+
"toLowerCase",
|
|
3
|
+
"toUpperCase",
|
|
4
|
+
"trim",
|
|
5
|
+
"trimStart",
|
|
6
|
+
"trimEnd",
|
|
7
|
+
"split",
|
|
8
|
+
"slice",
|
|
9
|
+
"substring",
|
|
10
|
+
"includes",
|
|
11
|
+
"startsWith",
|
|
12
|
+
"endsWith",
|
|
13
|
+
"indexOf",
|
|
14
|
+
"lastIndexOf",
|
|
15
|
+
"replace",
|
|
16
|
+
"replaceAll",
|
|
17
|
+
"repeat",
|
|
18
|
+
"padStart",
|
|
19
|
+
"padEnd",
|
|
20
|
+
"charAt",
|
|
21
|
+
"charCodeAt",
|
|
22
|
+
"codePointAt",
|
|
23
|
+
"at",
|
|
24
|
+
"concat",
|
|
25
|
+
"toString",
|
|
26
|
+
"match",
|
|
27
|
+
"matchAll",
|
|
28
|
+
"search",
|
|
29
|
+
"localeCompare",
|
|
30
|
+
"normalize",
|
|
31
|
+
]);
|
|
32
|
+
export const stringStatics = new Set(["fromCharCode", "fromCodePoint"]);
|
|
33
|
+
export const invokeStringStatic = (name, args, node) => {
|
|
34
|
+
const codes = args.map((arg) => {
|
|
35
|
+
if (typeof arg !== "number")
|
|
36
|
+
throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node);
|
|
37
|
+
return arg;
|
|
38
|
+
});
|
|
39
|
+
switch (name) {
|
|
40
|
+
case "fromCharCode":
|
|
41
|
+
return String.fromCharCode(...codes);
|
|
42
|
+
case "fromCodePoint":
|
|
43
|
+
return String.fromCodePoint(...codes);
|
|
44
|
+
default:
|
|
45
|
+
throw new InterpreterRuntimeError(`String.${name} is not available.`, node);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
import { InterpreterRuntimeError } from "../interpreter/model.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const urlProperties: Set<string>;
|
|
2
|
+
export declare const urlWritableProperties: Set<string>;
|
|
3
|
+
export declare const urlMethods: Set<string>;
|
|
4
|
+
export declare const urlStatics: Set<string>;
|
|
5
|
+
export declare const urlSearchParamsMethods: Set<string>;
|
|
6
|
+
export declare const uriArgument: (value: unknown, label: string) => string;
|
|
7
|
+
export declare const invokeUriFunction: (ref: UriFunction, args: Array<unknown>, node: AstNode) => string;
|
|
8
|
+
export declare const urlArgument: (value: unknown, label: string) => string;
|
|
9
|
+
export declare const invokeURLStatic: (name: string, args: Array<unknown>, node: AstNode) => unknown;
|
|
10
|
+
export declare const invokeURLMethod: (value: CodeModeURL, name: string, node: AstNode) => string;
|
|
11
|
+
import { type AstNode, UriFunction } from "../interpreter/model.js";
|
|
12
|
+
import { CodeModeURL } from "../values.js";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const urlProperties = new Set([
|
|
2
|
+
"href",
|
|
3
|
+
"origin",
|
|
4
|
+
"protocol",
|
|
5
|
+
"username",
|
|
6
|
+
"password",
|
|
7
|
+
"host",
|
|
8
|
+
"hostname",
|
|
9
|
+
"port",
|
|
10
|
+
"pathname",
|
|
11
|
+
"search",
|
|
12
|
+
"hash",
|
|
13
|
+
]);
|
|
14
|
+
export const urlWritableProperties = new Set([
|
|
15
|
+
"href",
|
|
16
|
+
"protocol",
|
|
17
|
+
"username",
|
|
18
|
+
"password",
|
|
19
|
+
"host",
|
|
20
|
+
"hostname",
|
|
21
|
+
"port",
|
|
22
|
+
"pathname",
|
|
23
|
+
"search",
|
|
24
|
+
"hash",
|
|
25
|
+
]);
|
|
26
|
+
export const urlMethods = new Set(["toString", "toJSON"]);
|
|
27
|
+
export const urlStatics = new Set(["canParse", "parse"]);
|
|
28
|
+
export const urlSearchParamsMethods = new Set([
|
|
29
|
+
"append",
|
|
30
|
+
"delete",
|
|
31
|
+
"get",
|
|
32
|
+
"getAll",
|
|
33
|
+
"has",
|
|
34
|
+
"set",
|
|
35
|
+
"sort",
|
|
36
|
+
"forEach",
|
|
37
|
+
"keys",
|
|
38
|
+
"values",
|
|
39
|
+
"entries",
|
|
40
|
+
"toString",
|
|
41
|
+
]);
|
|
42
|
+
export const uriArgument = (value, label) => coerceToString(boundedData(value, label));
|
|
43
|
+
export const invokeUriFunction = (ref, args, node) => {
|
|
44
|
+
const value = uriArgument(args[0], `${ref.name} input`);
|
|
45
|
+
try {
|
|
46
|
+
switch (ref.name) {
|
|
47
|
+
case "encodeURI":
|
|
48
|
+
return encodeURI(value);
|
|
49
|
+
case "encodeURIComponent":
|
|
50
|
+
return encodeURIComponent(value);
|
|
51
|
+
case "decodeURI":
|
|
52
|
+
return decodeURI(value);
|
|
53
|
+
case "decodeURIComponent":
|
|
54
|
+
return decodeURIComponent(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new InterpreterRuntimeError(`${ref.name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node).as("URIError");
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
export const urlArgument = (value, label) => value instanceof CodeModeURL ? value.url.href : uriArgument(value, label);
|
|
62
|
+
export const invokeURLStatic = (name, args, node) => {
|
|
63
|
+
if (!urlStatics.has(name))
|
|
64
|
+
throw new InterpreterRuntimeError(`URL.${name} is not available.`, node);
|
|
65
|
+
if (args.length === 0)
|
|
66
|
+
throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError");
|
|
67
|
+
const input = urlArgument(args[0], `URL.${name} input`);
|
|
68
|
+
const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`);
|
|
69
|
+
try {
|
|
70
|
+
const url = new URL(input, base);
|
|
71
|
+
return name === "canParse" ? true : new CodeModeURL(url);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return name === "canParse" ? false : null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
export const invokeURLMethod = (value, name, node) => {
|
|
78
|
+
if (name === "toString" || name === "toJSON")
|
|
79
|
+
return value.url.href;
|
|
80
|
+
throw new InterpreterRuntimeError(`URL method '${name}' is not available.`, node);
|
|
81
|
+
};
|
|
82
|
+
import { InterpreterRuntimeError, UriFunction } from "../interpreter/model.js";
|
|
83
|
+
import { CodeModeURL } from "../values.js";
|
|
84
|
+
import { boundedData, coerceToString } from "./value.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const errorConstructors: Set<string>;
|
|
2
|
+
export declare const valueConstructors: Set<string>;
|
|
3
|
+
export declare const compoundOperators: Set<string>;
|
|
4
|
+
export declare const createErrorValue: (name: string, message: string) => SafeObject;
|
|
5
|
+
export declare const createAggregateErrorValue: (errors: Array<unknown>, message: string) => SafeObject;
|
|
6
|
+
export declare const errorBrandName: (value: unknown) => string | undefined;
|
|
7
|
+
export declare const boundedData: (value: unknown, label: string) => unknown;
|
|
8
|
+
export declare const coerceToString: (value: unknown) => string;
|
|
9
|
+
export declare const coerceToNumber: (value: unknown) => number;
|
|
10
|
+
export declare const invokeCoercion: (ref: CoercionFunction, args: Array<unknown>, node: AstNode) => unknown;
|
|
11
|
+
import { type AstNode, CoercionFunction } from "../interpreter/model.js";
|
|
12
|
+
import { type SafeObject } from "../tool-runtime.js";
|