@opencode/codemode 0.0.0-dev-19607 → 0.0.0-dev-19611
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/interpreter/limits.d.ts +8 -0
- package/dist/interpreter/limits.js +17 -0
- package/dist/interpreter/objects.js +5 -3
- package/dist/interpreter/promises.js +5 -1
- package/dist/interpreter/runtime.js +9 -2
- package/dist/stdlib/array.js +16 -7
- package/dist/stdlib/json.js +5 -1
- package/dist/stdlib/string.js +28 -7
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Longest string a built-in or operator may produce. */
|
|
2
|
+
export declare const MAX_STRING_LENGTH: number;
|
|
3
|
+
/** Longest array a built-in may create or grow to. */
|
|
4
|
+
export declare const MAX_ARRAY_LENGTH = 10000000;
|
|
5
|
+
/** Most promises that may be pending at once. */
|
|
6
|
+
export declare const MAX_PENDING_PROMISES = 10000;
|
|
7
|
+
export declare const checkStringLength: (length: number) => void;
|
|
8
|
+
export declare const checkArrayLength: (length: number) => void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { rangeError } from "./model.js";
|
|
2
|
+
// The timeout only fires between interpreter steps, so a single built-in must not be able to materialize an
|
|
3
|
+
// unbounded value. These bound what one call may build; programs cannot reach such sizes any other way.
|
|
4
|
+
/** Longest string a built-in or operator may produce. */
|
|
5
|
+
export const MAX_STRING_LENGTH = 1 << 24;
|
|
6
|
+
/** Longest array a built-in may create or grow to. */
|
|
7
|
+
export const MAX_ARRAY_LENGTH = 10_000_000;
|
|
8
|
+
/** Most promises that may be pending at once. */
|
|
9
|
+
export const MAX_PENDING_PROMISES = 10_000;
|
|
10
|
+
export const checkStringLength = (length) => {
|
|
11
|
+
if (length > MAX_STRING_LENGTH)
|
|
12
|
+
throw rangeError("Invalid string length");
|
|
13
|
+
};
|
|
14
|
+
export const checkArrayLength = (length) => {
|
|
15
|
+
if (length > MAX_ARRAY_LENGTH)
|
|
16
|
+
throw rangeError("Invalid array length");
|
|
17
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { checkArrayLength } from "./limits.js";
|
|
1
2
|
import { AsyncIteratorSymbol, IteratorSymbol, } from "./model.js";
|
|
2
3
|
/** Ordinary assignment: writable, enumerable, configurable. */
|
|
3
4
|
export const data = { writable: true, enumerable: true, configurable: true };
|
|
@@ -129,13 +130,13 @@ export const isWrapper = (value) => value instanceof ProgramDate ||
|
|
|
129
130
|
value instanceof ProgramSet ||
|
|
130
131
|
value instanceof ProgramURL ||
|
|
131
132
|
value instanceof ProgramURLSearchParams;
|
|
132
|
-
const
|
|
133
|
+
const MAX_ARRAY_INDEX = 4_294_967_295;
|
|
133
134
|
export const parseArrayIndex = (key) => {
|
|
134
135
|
const property = String(key);
|
|
135
136
|
if (!/^(0|[1-9]\d*)$/.test(property))
|
|
136
137
|
return undefined;
|
|
137
138
|
const index = Number(property);
|
|
138
|
-
return index <
|
|
139
|
+
return index < MAX_ARRAY_INDEX ? index : undefined;
|
|
139
140
|
};
|
|
140
141
|
const canonical = (key) => (typeof key === "symbol" ? key : String(key));
|
|
141
142
|
const index = (target, key) => target instanceof ProgramArray && typeof key === "string" ? parseArrayIndex(key) : undefined;
|
|
@@ -190,8 +191,9 @@ const writeArray = (target, name, value) => {
|
|
|
190
191
|
if (name !== "length")
|
|
191
192
|
return undefined;
|
|
192
193
|
const length = typeof value === "number" ? value : Number(value);
|
|
193
|
-
if (!Number.isInteger(length) || length < 0
|
|
194
|
+
if (!Number.isInteger(length) || length < 0)
|
|
194
195
|
return false;
|
|
196
|
+
checkArrayLength(length);
|
|
195
197
|
target.items.length = length;
|
|
196
198
|
return true;
|
|
197
199
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Cause, Deferred, Effect, Exit, Fiber, Scope } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import { MAX_PENDING_PROMISES } from "./limits.js";
|
|
3
|
+
import { CallSite, ProgramThrow, rangeError, typeError } from "./model.js";
|
|
3
4
|
import { Callable, define, get, hidden, ProgramArray, ProgramFunction, ProgramObject, ProgramPromise, record, } from "./objects.js";
|
|
4
5
|
import { constructor, fn, methods, native, receiver, requiresNew } from "./native.js";
|
|
5
6
|
import { createAggregateErrorValue, locate, materialize, normalizeError } from "./errors.js";
|
|
@@ -33,6 +34,9 @@ export class PromiseRuntime {
|
|
|
33
34
|
}
|
|
34
35
|
create(effect) {
|
|
35
36
|
return Effect.flatMap(CallSite, (site) => {
|
|
37
|
+
if (this.active.size >= MAX_PENDING_PROMISES) {
|
|
38
|
+
throw rangeError(`Too many pending promises (limit ${MAX_PENDING_PROMISES}); await promises before creating more.`);
|
|
39
|
+
}
|
|
36
40
|
// Allocate before forking so reruns get distinct IDs and diagnostics retain creation order.
|
|
37
41
|
const id = this.nextID++;
|
|
38
42
|
const body = Effect.catchDefect(effect, (defect) => Effect.die(locate(defect, site.node)));
|
|
@@ -2,6 +2,7 @@ import { Cause, Deferred, Effect, Exit } from "effect";
|
|
|
2
2
|
import { toProgram } from "../data.js";
|
|
3
3
|
import { ToolReference } from "../tool-runtime.js";
|
|
4
4
|
import { AsyncIteratorSymbol, CallSite, GeneratorReturn, IteratorSymbol, OptionalShortCircuit, invalidData, ProgramThrow, rangeError, typeError, unsupportedSyntax, } from "./model.js";
|
|
5
|
+
import { checkStringLength } from "./limits.js";
|
|
5
6
|
import { locate, materialize } from "./errors.js";
|
|
6
7
|
import { globals } from "./globals.js";
|
|
7
8
|
import { assign, Callable, define, get, has, hasPrototype, keys, NativeFunction, parseArrayIndex, ProgramArray, ProgramDate, ProgramFunction, ProgramGenerator, ProgramMap, ProgramObject, ProgramPromise, ProgramSet, ProgramURLSearchParams, record, remove, set, } from "./objects.js";
|
|
@@ -1054,8 +1055,12 @@ class Frame {
|
|
|
1054
1055
|
const l = coerceOperand(lhs);
|
|
1055
1056
|
const r = coerceOperand(rhs);
|
|
1056
1057
|
switch (operator) {
|
|
1057
|
-
case "+":
|
|
1058
|
-
|
|
1058
|
+
case "+": {
|
|
1059
|
+
const sum = l + r;
|
|
1060
|
+
if (typeof sum === "string")
|
|
1061
|
+
checkStringLength(sum.length);
|
|
1062
|
+
return sum;
|
|
1063
|
+
}
|
|
1059
1064
|
case "-":
|
|
1060
1065
|
return l - r;
|
|
1061
1066
|
case "*":
|
|
@@ -1641,9 +1646,11 @@ class Frame {
|
|
|
1641
1646
|
throw typeError("Invalid template literal quasi.", quasi);
|
|
1642
1647
|
}
|
|
1643
1648
|
output += quasi.value.cooked;
|
|
1649
|
+
checkStringLength(output.length);
|
|
1644
1650
|
if (index < expressions.length) {
|
|
1645
1651
|
const raw = yield* self.evaluateExpression(expressions[index]);
|
|
1646
1652
|
output += coerceToString(toProgram(self.runtime.prototypes, raw, "Template interpolation"));
|
|
1653
|
+
checkStringLength(output.length);
|
|
1647
1654
|
}
|
|
1648
1655
|
}
|
|
1649
1656
|
return output;
|
package/dist/stdlib/array.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { constructor, methods, prototypeFrom, receiver } from "../interpreter/native.js";
|
|
3
|
+
import { checkArrayLength, checkStringLength, MAX_ARRAY_LENGTH } from "../interpreter/limits.js";
|
|
3
4
|
import { invalidData, rangeError, typeError } from "../interpreter/model.js";
|
|
4
5
|
import { get, ProgramArray, ProgramGenerator, ProgramObject } from "../interpreter/objects.js";
|
|
5
6
|
import { describeValue, rejectCircularInsertion } from "../interpreter/references.js";
|
|
6
7
|
import { applyCollectionCallback, preserveConsumerError } from "../interpreter/runner.js";
|
|
7
8
|
import { compareText } from "../tool-runtime.js";
|
|
8
9
|
import { coerceToNumber, coerceToString } from "./value.js";
|
|
9
|
-
const MAX_LENGTH = 4_294_967_295;
|
|
10
10
|
const arrayLikeSource = (source) => {
|
|
11
11
|
if (source instanceof ProgramObject && typeof get(source, "length") === "number") {
|
|
12
12
|
const length = get(source, "length");
|
|
13
13
|
const normalized = Number.isNaN(length) || length <= 0 ? 0 : Math.trunc(length);
|
|
14
|
-
|
|
15
|
-
throw new RangeError("Invalid array length");
|
|
14
|
+
checkArrayLength(normalized);
|
|
16
15
|
return { length: normalized, source };
|
|
17
16
|
}
|
|
18
17
|
throw invalidData(`Array.from expects an array, string, Map, Set, or array-like value, received ${describeValue(source)}.`);
|
|
@@ -87,7 +86,7 @@ export const arrayGlobal = (runner) => {
|
|
|
87
86
|
const first = args[0];
|
|
88
87
|
if (typeof first !== "number")
|
|
89
88
|
return new ProgramArray(into, [first]);
|
|
90
|
-
if (!Number.isInteger(first) || first < 0 || first >
|
|
89
|
+
if (!Number.isInteger(first) || first < 0 || first > MAX_ARRAY_LENGTH)
|
|
91
90
|
throw rangeError("Invalid array length.");
|
|
92
91
|
// Sparse like JS: Array(3) has holes, and combinator loops already skip them.
|
|
93
92
|
return new ProgramArray(into, new Array(first));
|
|
@@ -130,7 +129,11 @@ export const arrayGlobal = (runner) => {
|
|
|
130
129
|
if (args.length > 1 || (args.length === 1 && typeof args[0] !== "string")) {
|
|
131
130
|
throw typeError("Array.join expects zero arguments or one string separator.");
|
|
132
131
|
}
|
|
133
|
-
|
|
132
|
+
const joined = target
|
|
133
|
+
.map((item) => coerceToString(item ?? ""))
|
|
134
|
+
.join(args.length === 0 ? "," : args[0]);
|
|
135
|
+
checkStringLength(joined.length);
|
|
136
|
+
return joined;
|
|
134
137
|
},
|
|
135
138
|
],
|
|
136
139
|
[
|
|
@@ -175,14 +178,20 @@ export const arrayGlobal = (runner) => {
|
|
|
175
178
|
[
|
|
176
179
|
"concat",
|
|
177
180
|
1,
|
|
178
|
-
(thisValue, args) =>
|
|
181
|
+
(thisValue, args) => {
|
|
182
|
+
const joined = self(thisValue, "concat").items.concat(...args.map((item) => (item instanceof ProgramArray ? item.items : item)));
|
|
183
|
+
checkArrayLength(joined.length);
|
|
184
|
+
return wrap(joined);
|
|
185
|
+
},
|
|
179
186
|
],
|
|
180
187
|
[
|
|
181
188
|
"flat",
|
|
182
189
|
0,
|
|
183
190
|
(thisValue, args) => {
|
|
184
191
|
const flatten = (items, depth) => items.flatMap((item) => (item instanceof ProgramArray && depth > 0 ? flatten(item.items, depth - 1) : [item]));
|
|
185
|
-
|
|
192
|
+
const flattened = flatten(self(thisValue, "flat").items, optNumber("flat", args[0], "depth") ?? 1);
|
|
193
|
+
checkArrayLength(flattened.length);
|
|
194
|
+
return wrap(flattened);
|
|
186
195
|
},
|
|
187
196
|
],
|
|
188
197
|
[
|
package/dist/stdlib/json.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { methods } from "../interpreter/native.js";
|
|
3
3
|
import { applyCollectionCallback } from "../interpreter/runner.js";
|
|
4
|
+
import { checkStringLength } from "../interpreter/limits.js";
|
|
4
5
|
import { syntaxError, typeError } from "../interpreter/model.js";
|
|
5
6
|
import { typeofValue } from "../interpreter/references.js";
|
|
6
7
|
import { fromData, toData, toProgram } from "../data.js";
|
|
@@ -54,7 +55,10 @@ const stringify = (runner, args) => {
|
|
|
54
55
|
.map(String)
|
|
55
56
|
: null;
|
|
56
57
|
// A string cannot pollute, so __proto__ stays: JSON.stringify includes own __proto__ keys, like JS.
|
|
57
|
-
|
|
58
|
+
const text = JSON.stringify(toData(args[0], "JSON.stringify value", "json", false), properties, indent);
|
|
59
|
+
if (text !== undefined)
|
|
60
|
+
checkStringLength(text.length);
|
|
61
|
+
return Effect.succeed(text);
|
|
58
62
|
}
|
|
59
63
|
// Validate up front; the replacer walk below reads the original value.
|
|
60
64
|
toProgram(runner.prototypes, args[0], "JSON.stringify value");
|
package/dist/stdlib/string.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { toProgram } from "../data.js";
|
|
3
3
|
import { constructor, methods } from "../interpreter/native.js";
|
|
4
|
+
import { checkArrayLength, checkStringLength } from "../interpreter/limits.js";
|
|
4
5
|
import { invalidData, rangeError, typeError } from "../interpreter/model.js";
|
|
5
6
|
import { ProgramArray, ProgramPromise, ProgramRegExp, record } from "../interpreter/objects.js";
|
|
6
7
|
import { containsOpaqueReference, typeofValue } from "../interpreter/references.js";
|
|
@@ -156,9 +157,11 @@ export const stringGlobal = (runner) => {
|
|
|
156
157
|
if (args[0] === undefined) {
|
|
157
158
|
return wrap(requestedLimit !== undefined && requestedLimit >>> 0 === 0 ? [] : [value]);
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
const parts = args[0] instanceof ProgramRegExp
|
|
161
|
+
? value.split(args[0].regex, requestedLimit)
|
|
162
|
+
: value.split(str("split", args, 0), requestedLimit === undefined ? undefined : requestedLimit >>> 0);
|
|
163
|
+
checkArrayLength(parts.length);
|
|
164
|
+
return wrap(parts);
|
|
162
165
|
}),
|
|
163
166
|
simple("slice", 2, (value, args) => value.slice(optNum("slice", args, 0), optNum("slice", args, 1))),
|
|
164
167
|
simple("includes", 1, (value, args) => {
|
|
@@ -192,7 +195,12 @@ export const stringGlobal = (runner) => {
|
|
|
192
195
|
if (!pattern.global) {
|
|
193
196
|
throw typeError(`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.`);
|
|
194
197
|
}
|
|
195
|
-
|
|
198
|
+
const matches = [];
|
|
199
|
+
for (const match of value.matchAll(pattern)) {
|
|
200
|
+
checkArrayLength(matches.length + 1);
|
|
201
|
+
matches.push(matchToValue(protos, match));
|
|
202
|
+
}
|
|
203
|
+
return new ProgramArray(protos.Array, matches);
|
|
196
204
|
}),
|
|
197
205
|
simple("search", 1, (value, args) => value.search(toHostRegex(args[0], "search"))),
|
|
198
206
|
simple("repeat", 1, (value, args) => {
|
|
@@ -200,10 +208,19 @@ export const stringGlobal = (runner) => {
|
|
|
200
208
|
if (!Number.isFinite(count) || count < 0) {
|
|
201
209
|
throw rangeError("String.repeat expects a finite non-negative count.");
|
|
202
210
|
}
|
|
211
|
+
checkStringLength(value.length * count);
|
|
203
212
|
return value.repeat(count);
|
|
204
213
|
}),
|
|
205
|
-
simple("padStart", 1, (value, args) =>
|
|
206
|
-
|
|
214
|
+
simple("padStart", 1, (value, args) => {
|
|
215
|
+
const length = num("padStart", args, 0);
|
|
216
|
+
checkStringLength(length);
|
|
217
|
+
return value.padStart(length, optStr("padStart", args, 1));
|
|
218
|
+
}),
|
|
219
|
+
simple("padEnd", 1, (value, args) => {
|
|
220
|
+
const length = num("padEnd", args, 0);
|
|
221
|
+
checkStringLength(length);
|
|
222
|
+
return value.padEnd(length, optStr("padEnd", args, 1));
|
|
223
|
+
}),
|
|
207
224
|
simple("charAt", 1, (value, args) => value.charAt(optNum("charAt", args, 0) ?? 0)),
|
|
208
225
|
simple("at", 1, (value, args) => value.at(optNum("at", args, 0) ?? 0)),
|
|
209
226
|
simple("substring", 2, (value, args) => value.substring(optNum("substring", args, 0) ?? 0, optNum("substring", args, 1))),
|
|
@@ -212,7 +229,11 @@ export const stringGlobal = (runner) => {
|
|
|
212
229
|
simple("toWellFormed", 0, (value) => value.toWellFormed()),
|
|
213
230
|
simple("charCodeAt", 1, (value, args) => value.charCodeAt(optNum("charCodeAt", args, 0) ?? 0)),
|
|
214
231
|
simple("codePointAt", 1, (value, args) => value.codePointAt(optNum("codePointAt", args, 0) ?? 0)),
|
|
215
|
-
simple("concat", 1, (value, args) =>
|
|
232
|
+
simple("concat", 1, (value, args) => {
|
|
233
|
+
const joined = value.concat(...args.map((_, index) => str("concat", args, index)));
|
|
234
|
+
checkStringLength(joined.length);
|
|
235
|
+
return joined;
|
|
236
|
+
}),
|
|
216
237
|
]);
|
|
217
238
|
return string;
|
|
218
239
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@opencode/codemode",
|
|
4
|
-
"version": "0.0.0-dev-
|
|
4
|
+
"version": "0.0.0-dev-19611",
|
|
5
5
|
"description": "Effect-native confined code execution over schema-described tools",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|