@opencode/codemode 0.0.0-dev-20148 → 0.0.0-dev-20150

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.
@@ -16,9 +16,9 @@ export declare const invoke: <R>(ctx: Interpreter<R>, value: Value, name: string
16
16
  export declare const toPrimitiveString: <R>(ctx: Interpreter<R>, value: Value) => Effect.Effect<string, unknown, R>;
17
17
  export declare const toPrimitiveNumber: <R>(ctx: Interpreter<R>, value: Value) => Effect.Effect<number, unknown, R>;
18
18
  /**
19
- * Runs a synchronous native body on its arguments after ToPrimitive, in order, with one hint for all positions or
20
- * one per position. Primitive arguments skip the Effect entirely.
19
+ * Runs a native body on its arguments after ToPrimitive, in order, with one hint for all positions or one per
20
+ * position. Primitive arguments skip the Effect entirely.
21
21
  */
22
- export declare const withPrimitives: <R>(ctx: Interpreter<R>, hints: Hint | ReadonlyArray<Hint>, values: Array<Value>, body: (primitives: Array<Value>) => Value) => Value | Effect.Effect<Value, unknown, R>;
22
+ export declare const withPrimitives: <R>(ctx: Interpreter<R>, hints: Hint | ReadonlyArray<Hint>, values: Array<Value>, body: (primitives: Array<Value>) => Value | Effect.Effect<Value, unknown, R>) => Value | Effect.Effect<Value, unknown, R>;
23
23
  export declare const isSupportedCallback: (value: Value) => value is Callable;
24
24
  export declare const applyCollectionCallback: <R>(ctx: Interpreter<R>, callback: Value, name: string) => ((args: Array<Value>, thisValue?: Value) => Effect.Effect<Value, unknown, R>);
@@ -47,13 +47,16 @@ export const invoke = (ctx, value, name, label) => {
47
47
  export const toPrimitiveString = (ctx, value) => Effect.map(toPrimitive(ctx, value, "string"), coerceToString);
48
48
  export const toPrimitiveNumber = (ctx, value) => Effect.map(toPrimitive(ctx, value, "number"), coerceToNumber);
49
49
  /**
50
- * Runs a synchronous native body on its arguments after ToPrimitive, in order, with one hint for all positions or
51
- * one per position. Primitive arguments skip the Effect entirely.
50
+ * Runs a native body on its arguments after ToPrimitive, in order, with one hint for all positions or one per
51
+ * position. Primitive arguments skip the Effect entirely.
52
52
  */
53
53
  export const withPrimitives = (ctx, hints, values, body) => {
54
54
  if (!values.some((value) => value instanceof Obj))
55
55
  return body(values);
56
- return Effect.map(Effect.forEach(values, (value, index) => toPrimitive(ctx, value, typeof hints === "string" ? hints : hints[index])), body);
56
+ return Effect.flatMap(Effect.forEach(values, (value, index) => toPrimitive(ctx, value, typeof hints === "string" ? hints : hints[index])), (primitives) => {
57
+ const result = body(primitives);
58
+ return Effect.isEffect(result) ? result : Effect.succeed(result);
59
+ });
57
60
  };
58
61
  // The single acceptance list for callbacks: collections, sort, string replacers,
59
62
  // Array.from mappers, and promise reactions all admit exactly these callables.
@@ -33,6 +33,8 @@ const primitiveOperators = new Set([
33
33
  ">>",
34
34
  ">>>",
35
35
  ]);
36
+ /** ToPropertyKey on a primitive (or an opaque value, which keeps its built-in string form). */
37
+ const propertyKey = (value) => typeof value === "string" || typeof value === "number" || typeof value === "symbol" ? value : coerceToString(value);
36
38
  // What a loop does with its body's result: exit with a StatementResult, or undefined to keep iterating.
37
39
  // Unlabelled break ends this loop; a label the loop does not carry propagates outward.
38
40
  const loopExit = (result, labels) => {
@@ -999,7 +1001,7 @@ class Frame {
999
1001
  }
1000
1002
  const keyNode = property.key;
1001
1003
  if (property.computed) {
1002
- return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value));
1004
+ return Effect.flatMap(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value, keyNode));
1003
1005
  }
1004
1006
  if (keyNode.type === "Identifier")
1005
1007
  return Effect.succeed(keyNode.name);
@@ -1120,6 +1122,10 @@ class Frame {
1120
1122
  // IsLooselyEqual converts only an object facing a non-nullish primitive; two objects (including tool
1121
1123
  // references, which are not Obj) compare by identity.
1122
1124
  const equality = operator === "==" || operator === "!=";
1125
+ // `in` checks the right operand before ToPropertyKey on the left, so a bad right side wins over a bad key.
1126
+ if (operator === "in" && lhs instanceof Obj && !isOpaque(lhs) && rhs instanceof Obj) {
1127
+ return Effect.map(this.toPropertyKey(lhs, node), (key) => has(rhs, key));
1128
+ }
1123
1129
  const other = lhs instanceof Obj ? rhs : lhs;
1124
1130
  const converts = primitiveOperators.has(operator) ||
1125
1131
  (equality && other !== null && other !== undefined && typeof other !== "object");
@@ -1142,9 +1148,8 @@ class Frame {
1142
1148
  return this.looselyEqual(lhs, rhs, node);
1143
1149
  if (operator === "!=")
1144
1150
  return !this.looselyEqual(lhs, rhs, node);
1145
- if (operator === "in" && rhs instanceof Obj && !isOpaque(lhs)) {
1146
- return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : lhs);
1147
- }
1151
+ if (operator === "in" && rhs instanceof Obj && !isOpaque(lhs))
1152
+ return has(rhs, propertyKey(lhs));
1148
1153
  if (isOpaque(lhs) || isOpaque(rhs)) {
1149
1154
  throw invalidData("Binary operators require data values.", node);
1150
1155
  }
@@ -1709,13 +1714,13 @@ class Frame {
1709
1714
  const keyNode = property.key;
1710
1715
  let key;
1711
1716
  if (property.computed) {
1712
- key = self.toPropertyKey(yield* self.evaluateExpression(keyNode));
1717
+ key = yield* self.toPropertyKey(yield* self.evaluateExpression(keyNode), keyNode);
1713
1718
  }
1714
1719
  else if (keyNode.type === "Identifier") {
1715
1720
  key = keyNode.name;
1716
1721
  }
1717
1722
  else if (keyNode.type === "Literal") {
1718
- key = self.toPropertyKey(literal(keyNode));
1723
+ key = propertyKey(literal(keyNode));
1719
1724
  }
1720
1725
  else {
1721
1726
  throw typeError("Unsupported object property key shape.", keyNode);
@@ -1820,11 +1825,15 @@ class Frame {
1820
1825
  return OptionalShortCircuit;
1821
1826
  if ((objectValue === null || objectValue === undefined) && node.optional)
1822
1827
  return OptionalShortCircuit;
1823
- const key = node.computed
1824
- ? self.toPropertyKey(yield* self.evaluateExpression(propertyNode))
1825
- : propertyNode.type === "Identifier"
1826
- ? propertyNode.name
1827
- : self.toPropertyKey(yield* self.evaluateExpression(propertyNode));
1828
+ const keyValue = !node.computed && propertyNode.type === "Identifier"
1829
+ ? propertyNode.name
1830
+ : yield* self.evaluateExpression(propertyNode);
1831
+ // GetValue applies ToObject to the base before ToPropertyKey, so a nullish base throws before the key's own
1832
+ // toString runs.
1833
+ if (objectValue === null || objectValue === undefined) {
1834
+ throw typeError(`Cannot read properties of ${objectValue} (reading '${coerceToString(keyValue)}').`, objectNode);
1835
+ }
1836
+ const key = yield* self.toPropertyKey(keyValue, propertyNode);
1828
1837
  return self.resolveProperty(objectValue, key, objectNode, propertyNode);
1829
1838
  });
1830
1839
  }
@@ -1942,10 +1951,11 @@ class Frame {
1942
1951
  return;
1943
1952
  throw typeError(`Cannot assign to read only property '${String(key)}'.`, node);
1944
1953
  }
1945
- // ToPropertyKey: anything else becomes its string form, so `counts[row.category]` works when the field is null.
1946
- toPropertyKey(value) {
1947
- if (typeof value === "string" || typeof value === "number" || typeof value === "symbol")
1948
- return value;
1949
- return coerceToString(value);
1954
+ // ToPropertyKey: a data object converts through its own `toString`/`valueOf` first; anything else becomes its
1955
+ // string form synchronously, so `counts[row.category]` works when the field is null.
1956
+ toPropertyKey(value, node) {
1957
+ if (!(value instanceof Obj))
1958
+ return Effect.succeed(propertyKey(value));
1959
+ return Effect.map(this.toPrimitive(value, "string", node), propertyKey);
1950
1960
  }
1951
1961
  }
@@ -29,7 +29,7 @@ const arrayFrom = (ctx, args) => {
29
29
  const values = [];
30
30
  for (let index = 0; index < arrayLike.length; index += 1) {
31
31
  const item = get(arrayLike.source, index);
32
- values.push(apply === undefined ? item : yield* apply([item, index]));
32
+ values.push(apply === undefined ? item : yield* apply([item, index], args[2]));
33
33
  }
34
34
  return new Arr(proto, values);
35
35
  }
@@ -39,7 +39,9 @@ const arrayFrom = (ctx, args) => {
39
39
  const step = yield* cursor.next;
40
40
  if (step.done)
41
41
  return new Arr(proto, values);
42
- values.push(apply === undefined ? step.value : yield* preserveConsumerError(cursor.close, apply([step.value, index])));
42
+ values.push(apply === undefined
43
+ ? step.value
44
+ : yield* preserveConsumerError(cursor.close, apply([step.value, index], args[2])));
43
45
  index += 1;
44
46
  }
45
47
  });
@@ -332,7 +334,7 @@ export const arrayGlobal = (ctx) => {
332
334
  for (let index = 0; index < length; index += 1) {
333
335
  if (!(index in target.items))
334
336
  continue;
335
- const mapped = yield* apply([target.items[index], index, target]);
337
+ const mapped = yield* apply([target.items[index], index, target], args[1]);
336
338
  if (mapped instanceof Arr)
337
339
  values.push(...mapped.items);
338
340
  else
@@ -357,7 +359,10 @@ export const callbackMethods = (ctx, label, self, elements, wrap) => {
357
359
  length,
358
360
  (thisValue, args) => {
359
361
  const target = self(thisValue, name);
360
- return body(elements(target), target, applyCollectionCallback(ctx, args[0], `${label}.${name}`), args);
362
+ const call = applyCollectionCallback(ctx, args[0], `${label}.${name}`);
363
+ // reduce and reduceRight take an initial value where the others take a thisArg.
364
+ const thisArg = name.startsWith("reduce") ? undefined : args[1];
365
+ return body(elements(target), target, (callbackArgs) => call(callbackArgs, thisArg), args);
361
366
  },
362
367
  ];
363
368
  return [
@@ -167,7 +167,7 @@ export const mapGlobal = (ctx) => {
167
167
  const apply = applyCollectionCallback(ctx, args[0], "Map.forEach");
168
168
  return Effect.gen(function* () {
169
169
  for (const [key, item] of target.map.entries())
170
- yield* apply([item, key, target]);
170
+ yield* apply([item, key, target], args[1]);
171
171
  return undefined;
172
172
  });
173
173
  },
@@ -357,7 +357,7 @@ export const setGlobal = (ctx) => {
357
357
  const apply = applyCollectionCallback(ctx, args[0], "Set.forEach");
358
358
  return Effect.gen(function* () {
359
359
  for (const item of target.set.values())
360
- yield* apply([item, item, target]);
360
+ yield* apply([item, item, target], args[1]);
361
361
  return undefined;
362
362
  });
363
363
  },
@@ -104,7 +104,7 @@ export const headersGlobal = (ctx) => {
104
104
  const apply = applyCollectionCallback(ctx, args[0], "Headers.forEach");
105
105
  return Effect.gen(function* () {
106
106
  for (const [key, value] of Array.from(target.headers.entries()))
107
- yield* apply([value, key, target]);
107
+ yield* apply([value, key, target], args[1]);
108
108
  return undefined;
109
109
  });
110
110
  },
@@ -1,6 +1,7 @@
1
1
  import { constructor, constants, methods } from "../interpreter/native.js";
2
2
  import { coerceToNumber } from "../interpreter/objects.js";
3
3
  import { rangeError, typeError } from "../interpreter/model.js";
4
+ import { withPrimitives } from "../interpreter/callback.js";
4
5
  import { coerce, coercion } from "./value.js";
5
6
  export const numberGlobal = (ctx) => {
6
7
  const builtins = ctx.builtins;
@@ -32,32 +33,26 @@ export const numberGlobal = (ctx) => {
32
33
  return thisValue;
33
34
  throw typeError(`Number.prototype.${name} requires that 'this' be a Number.`);
34
35
  };
35
- const optNum = (arg) => (arg === undefined ? undefined : coerceToNumber(arg));
36
+ // The receiver is checked first, then the one argument converts through ToPrimitive with the number hint.
37
+ const formatting = (name, op) => [
38
+ name,
39
+ 1,
40
+ (thisValue, args) => {
41
+ const value = self(thisValue, name);
42
+ return withPrimitives(ctx, "number", [args[0]], ([digits]) => op(value, digits === undefined ? undefined : coerceToNumber(digits)));
43
+ },
44
+ ];
36
45
  methods(builtins, builtins.Number, [
37
- ["toFixed", 1, (thisValue, args) => self(thisValue, "toFixed").toFixed(optNum(args[0]))],
46
+ formatting("toFixed", (value, digits) => value.toFixed(digits)),
38
47
  ["toLocaleString", 0, (thisValue) => self(thisValue, "toLocaleString").toLocaleString("en-US")],
39
- ["toExponential", 1, (thisValue, args) => self(thisValue, "toExponential").toExponential(optNum(args[0]))],
40
- [
41
- "toPrecision",
42
- 1,
43
- (thisValue, args) => {
44
- const value = self(thisValue, "toPrecision");
45
- const digits = optNum(args[0]);
46
- return digits === undefined ? value.toString() : value.toPrecision(digits);
47
- },
48
- ],
49
- [
50
- "toString",
51
- 1,
52
- (thisValue, args) => {
53
- const value = self(thisValue, "toString");
54
- const radix = optNum(args[0]);
55
- if (radix !== undefined && (radix < 2 || radix > 36)) {
56
- throw rangeError("Number.toString radix must be between 2 and 36.");
57
- }
58
- return value.toString(radix);
59
- },
60
- ],
48
+ formatting("toExponential", (value, digits) => value.toExponential(digits)),
49
+ formatting("toPrecision", (value, digits) => (digits === undefined ? value.toString() : value.toPrecision(digits))),
50
+ formatting("toString", (value, radix) => {
51
+ if (radix !== undefined && (radix < 2 || radix > 36)) {
52
+ throw rangeError("Number.toString radix must be between 2 and 36.");
53
+ }
54
+ return value.toString(radix);
55
+ }),
61
56
  ["valueOf", 0, (thisValue) => self(thisValue, "valueOf")],
62
57
  ]);
63
58
  return number;
@@ -4,7 +4,7 @@ import { checkArrayLength, checkStringLength } from "../interpreter/limits.js";
4
4
  import { invalidData, IteratorSymbol, rangeError, typeError } from "../interpreter/model.js";
5
5
  import { define, get, hidden, Arr, hostIterator, Obj, RegExpObj, record, coerceToInteger, coerceToNumber, coerceToString, } from "../interpreter/objects.js";
6
6
  import { containsOpaqueReference, typeofValue } from "../interpreter/references.js";
7
- import { applyCollectionCallback, isSupportedCallback, toPrimitiveString } from "../interpreter/callback.js";
7
+ import { applyCollectionCallback, isSupportedCallback, toPrimitiveString, withPrimitives, } from "../interpreter/callback.js";
8
8
  import { matchToValue, toHostRegex } from "./regexp.js";
9
9
  import { coercion } from "./value.js";
10
10
  // console is intercepted by the interpreter before reaching here.
@@ -111,28 +111,50 @@ export const stringGlobal = (ctx) => {
111
111
  const num = (name, args, index) => coerceToNumber(requireDataArgument(name, index, args[index]));
112
112
  const optNum = (name, args, index) => args[index] === undefined ? undefined : num(name, args, index);
113
113
  const optStr = (name, args, index) => args[index] === undefined ? undefined : str(name, args, index);
114
- const rejectRegex = (name, args) => {
114
+ // ToPrimitive in spec order: the receiver, then the arguments the method consumes, one hint per position; the
115
+ // rest pass through as they are.
116
+ const simple = (name, length, op, hints = []) => [
117
+ name,
118
+ length,
119
+ (thisValue, args) => {
120
+ if (thisValue === null || thisValue === undefined) {
121
+ throw typeError(`String.prototype.${name} called on null or undefined.`);
122
+ }
123
+ return withPrimitives(ctx, ["string", ...hints], [thisValue, ...args.slice(0, hints.length)], ([value, ...primitives]) => op(coerceToString(value), [...primitives, ...args.slice(hints.length)]));
124
+ },
125
+ ];
126
+ // includes, startsWith, and endsWith reject a RegExp before converting their search string and position.
127
+ const searching = (name, op) => simple(name, 1, (value, args) => {
115
128
  if (args[0] instanceof RegExpObj) {
116
129
  throw typeError(`String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`);
117
130
  }
118
- };
119
- const simple = (name, length, op) => [name, length, (thisValue, args) => op(self(thisValue, name), args)];
131
+ return withPrimitives(ctx, ["string", "number"], args.slice(0, 2), (primitives) => op(value, str(name, primitives, 0), optNum(name, primitives, 1)));
132
+ });
133
+ // match, matchAll, and search read a RegExp as is and convert anything else to its pattern text.
134
+ const withPattern = (args, op) => args[0] instanceof RegExpObj ? op(args[0]) : withPrimitives(ctx, "string", [args[0]], ([text]) => op(text));
120
135
  const replace = (name) => simple(name, 2, (value, args) => {
121
- if (isSupportedCallback(args[1]))
122
- return replaceWithCallback(ctx, value, name, args);
123
- if (typeofValue(args[1]) === "function") {
124
- throw typeError(`String.${name} cannot use this callable as a replacer; wrap it in an arrow function, e.g. (match) => tools.ns.tool(match).`);
125
- }
126
- if (args[0] instanceof RegExpObj) {
127
- const pattern = args[0].regex;
128
- const replacement = str(name, args, 1);
129
- if (name === "replaceAll")
130
- replaceAllNeedsGlobal(pattern);
131
- return name === "replace" ? value.replace(pattern, replacement) : value.replaceAll(pattern, replacement);
132
- }
133
- if (name === "replace")
134
- return value.replace(str(name, args, 0), str(name, args, 1));
135
- return value.replaceAll(str(name, args, 0), str(name, args, 1));
136
+ const pattern = args[0];
137
+ const replacer = args[1];
138
+ // A RegExp pattern is used as is; a plain one converts to its search string, then a non-callable replacement.
139
+ return withPrimitives(ctx, "string", [pattern instanceof RegExpObj ? undefined : pattern, isSupportedCallback(replacer) ? undefined : replacer], ([search, replacement]) => {
140
+ if (isSupportedCallback(replacer)) {
141
+ return replaceWithCallback(ctx, value, name, [pattern instanceof RegExpObj ? pattern : search, replacer]);
142
+ }
143
+ if (typeofValue(replacer) === "function") {
144
+ throw typeError(`String.${name} cannot use this callable as a replacer; wrap it in an arrow function, e.g. (match) => tools.ns.tool(match).`);
145
+ }
146
+ const primitives = [search, replacement];
147
+ if (pattern instanceof RegExpObj) {
148
+ const regex = pattern.regex;
149
+ const text = str(name, primitives, 1);
150
+ if (name === "replaceAll")
151
+ replaceAllNeedsGlobal(regex);
152
+ return name === "replace" ? value.replace(regex, text) : value.replaceAll(regex, text);
153
+ }
154
+ if (name === "replace")
155
+ return value.replace(str(name, primitives, 0), str(name, primitives, 1));
156
+ return value.replaceAll(str(name, primitives, 0), str(name, primitives, 1));
157
+ });
136
158
  });
137
159
  methods(builtins, builtins.String, [
138
160
  simple("toString", 0, (value) => value),
@@ -147,7 +169,7 @@ export const stringGlobal = (ctx) => {
147
169
  simple("trimEnd", 0, (value) => value.trimEnd()),
148
170
  simple("trimRight", 0, (value) => value.trimEnd()),
149
171
  // Locale/options are deliberately unsupported; comparison uses the host default locale.
150
- simple("localeCompare", 1, (value, args) => value.localeCompare(str("localeCompare", args, 0))),
172
+ simple("localeCompare", 1, (value, args) => value.localeCompare(str("localeCompare", args, 0)), ["string"]),
151
173
  simple("normalize", 0, (value, args) => {
152
174
  const form = optStr("normalize", args, 0);
153
175
  try {
@@ -156,61 +178,62 @@ export const stringGlobal = (ctx) => {
156
178
  catch {
157
179
  throw rangeError(`String.normalize expects the form "NFC", "NFD", "NFKC", or "NFKD" (got ${JSON.stringify(form)}).`);
158
180
  }
159
- }),
181
+ }, ["string"]),
160
182
  simple("split", 2, (value, args) => {
161
- const wrap = (parts) => new Arr(builtins.Array, parts);
162
- // Native: an undefined separator returns the whole string, not a split on "undefined",
163
- // unless the limit truncates to zero.
164
- const requestedLimit = optNum("split", args, 1);
165
- if (args[0] === undefined) {
166
- return wrap(requestedLimit !== undefined && requestedLimit >>> 0 === 0 ? [] : [value]);
167
- }
168
- const parts = args[0] instanceof RegExpObj
169
- ? value.split(args[0].regex, requestedLimit)
170
- : value.split(str("split", args, 0), requestedLimit === undefined ? undefined : requestedLimit >>> 0);
171
- checkArrayLength(parts.length);
172
- return wrap(parts);
173
- }),
174
- simple("slice", 2, (value, args) => value.slice(optNum("slice", args, 0), optNum("slice", args, 1))),
175
- simple("includes", 1, (value, args) => {
176
- rejectRegex("includes", args);
177
- return value.includes(str("includes", args, 0), optNum("includes", args, 1));
183
+ const separator = args[0];
184
+ // A RegExp separator is used as is; the limit converts before a plain separator does, as in the spec.
185
+ return withPrimitives(ctx, ["number", "string"], [args[1], separator instanceof RegExpObj ? undefined : separator], ([limit, pattern]) => {
186
+ const wrap = (parts) => new Arr(builtins.Array, parts);
187
+ // Native: an undefined separator returns the whole string, not a split on "undefined",
188
+ // unless the limit truncates to zero.
189
+ const requestedLimit = args[1] === undefined ? undefined : num("split", [pattern, limit], 1);
190
+ if (separator === undefined) {
191
+ return wrap(requestedLimit !== undefined && requestedLimit >>> 0 === 0 ? [] : [value]);
192
+ }
193
+ const parts = separator instanceof RegExpObj
194
+ ? value.split(separator.regex, requestedLimit)
195
+ : value.split(str("split", [pattern], 0), requestedLimit === undefined ? undefined : requestedLimit >>> 0);
196
+ checkArrayLength(parts.length);
197
+ return wrap(parts);
198
+ });
178
199
  }),
179
- simple("startsWith", 1, (value, args) => {
180
- rejectRegex("startsWith", args);
181
- return value.startsWith(str("startsWith", args, 0), optNum("startsWith", args, 1));
182
- }),
183
- simple("endsWith", 1, (value, args) => {
184
- rejectRegex("endsWith", args);
185
- return value.endsWith(str("endsWith", args, 0), optNum("endsWith", args, 1));
186
- }),
187
- simple("indexOf", 1, (value, args) => value.indexOf(str("indexOf", args, 0), optNum("indexOf", args, 1))),
188
- simple("lastIndexOf", 1, (value, args) => value.lastIndexOf(str("lastIndexOf", args, 0), optNum("lastIndexOf", args, 1))),
200
+ simple("slice", 2, (value, args) => value.slice(optNum("slice", args, 0), optNum("slice", args, 1)), [
201
+ "number",
202
+ "number",
203
+ ]),
204
+ searching("includes", (value, search, position) => value.includes(search, position)),
205
+ searching("startsWith", (value, search, position) => value.startsWith(search, position)),
206
+ searching("endsWith", (value, search, position) => value.endsWith(search, position)),
207
+ simple("indexOf", 1, (value, args) => value.indexOf(str("indexOf", args, 0), optNum("indexOf", args, 1)), [
208
+ "string",
209
+ "number",
210
+ ]),
211
+ simple("lastIndexOf", 1, (value, args) => value.lastIndexOf(str("lastIndexOf", args, 0), optNum("lastIndexOf", args, 1)), ["string", "number"]),
189
212
  replace("replace"),
190
213
  replace("replaceAll"),
191
- simple("match", 1, (value, args) => {
192
- const pattern = toHostRegex(args[0], "match");
193
- const matched = value.match(pattern);
214
+ simple("match", 1, (value, args) => withPattern(args, (arg) => {
215
+ const regex = toHostRegex(arg, "match");
216
+ const matched = value.match(regex);
194
217
  if (matched === null)
195
218
  return null;
196
219
  // Preserve the own `index` and `groups` properties on non-global matches.
197
- if (pattern.global)
220
+ if (regex.global)
198
221
  return new Arr(builtins.Array, [...matched]);
199
222
  return matchToValue(builtins, matched);
200
- }),
201
- simple("matchAll", 1, (value, args) => {
202
- const pattern = toHostRegex(args[0], "matchAll", "g");
203
- if (!pattern.global) {
204
- 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.`);
223
+ })),
224
+ simple("matchAll", 1, (value, args) => withPattern(args, (arg) => {
225
+ const regex = toHostRegex(arg, "matchAll", "g");
226
+ if (!regex.global) {
227
+ throw typeError(`String.matchAll requires a regular expression with the global (g) flag: write /${regex.source}/${regex.flags}g, or use String.match for a single match.`);
205
228
  }
206
229
  const matches = [];
207
- for (const match of value.matchAll(pattern)) {
230
+ for (const match of value.matchAll(regex)) {
208
231
  checkArrayLength(matches.length + 1);
209
232
  matches.push(matchToValue(builtins, match));
210
233
  }
211
234
  return new Arr(builtins.Array, matches);
212
- }),
213
- simple("search", 1, (value, args) => value.search(toHostRegex(args[0], "search"))),
235
+ })),
236
+ simple("search", 1, (value, args) => withPattern(args, (arg) => value.search(toHostRegex(arg, "search")))),
214
237
  simple("repeat", 1, (value, args) => {
215
238
  const count = num("repeat", args, 0);
216
239
  if (!Number.isFinite(count) || count < 0) {
@@ -218,30 +241,33 @@ export const stringGlobal = (ctx) => {
218
241
  }
219
242
  checkStringLength(value.length * count);
220
243
  return value.repeat(count);
221
- }),
244
+ }, ["number"]),
222
245
  simple("padStart", 1, (value, args) => {
223
246
  const length = num("padStart", args, 0);
224
247
  checkStringLength(length);
225
248
  return value.padStart(length, optStr("padStart", args, 1));
226
- }),
249
+ }, ["number", "string"]),
227
250
  simple("padEnd", 1, (value, args) => {
228
251
  const length = num("padEnd", args, 0);
229
252
  checkStringLength(length);
230
253
  return value.padEnd(length, optStr("padEnd", args, 1));
231
- }),
232
- simple("charAt", 1, (value, args) => value.charAt(optNum("charAt", args, 0) ?? 0)),
233
- simple("at", 1, (value, args) => value.at(optNum("at", args, 0) ?? 0)),
234
- simple("substring", 2, (value, args) => value.substring(optNum("substring", args, 0) ?? 0, optNum("substring", args, 1))),
235
- simple("substr", 2, (value, args) => value.substr(optNum("substr", args, 0) ?? 0, optNum("substr", args, 1))),
254
+ }, ["number", "string"]),
255
+ simple("charAt", 1, (value, args) => value.charAt(optNum("charAt", args, 0) ?? 0), ["number"]),
256
+ simple("at", 1, (value, args) => value.at(optNum("at", args, 0) ?? 0), ["number"]),
257
+ simple("substring", 2, (value, args) => value.substring(optNum("substring", args, 0) ?? 0, optNum("substring", args, 1)), ["number", "number"]),
258
+ simple("substr", 2, (value, args) => value.substr(optNum("substr", args, 0) ?? 0, optNum("substr", args, 1)), [
259
+ "number",
260
+ "number",
261
+ ]),
236
262
  simple("isWellFormed", 0, (value) => value.isWellFormed()),
237
263
  simple("toWellFormed", 0, (value) => value.toWellFormed()),
238
- simple("charCodeAt", 1, (value, args) => value.charCodeAt(optNum("charCodeAt", args, 0) ?? 0)),
239
- simple("codePointAt", 1, (value, args) => value.codePointAt(optNum("codePointAt", args, 0) ?? 0)),
240
- simple("concat", 1, (value, args) => {
241
- const joined = value.concat(...args.map((_, index) => str("concat", args, index)));
264
+ simple("charCodeAt", 1, (value, args) => value.charCodeAt(optNum("charCodeAt", args, 0) ?? 0), ["number"]),
265
+ simple("codePointAt", 1, (value, args) => value.codePointAt(optNum("codePointAt", args, 0) ?? 0), ["number"]),
266
+ simple("concat", 1, (value, args) => withPrimitives(ctx, "string", args, (parts) => {
267
+ const joined = value.concat(...parts.map((_, index) => str("concat", parts, index)));
242
268
  checkStringLength(joined.length);
243
269
  return joined;
244
- }),
270
+ })),
245
271
  ]);
246
272
  define(builtins.String, IteratorSymbol, fn(builtins, "[Symbol.iterator]", 0, (thisValue) => hostIterator(builtins, self(thisValue, "[Symbol.iterator]")[Symbol.iterator]())), hidden);
247
273
  return string;
@@ -246,7 +246,7 @@ export const urlSearchParamsGlobal = (ctx) => {
246
246
  const apply = applyCollectionCallback(ctx, args[0], "URLSearchParams.forEach");
247
247
  return Effect.gen(function* () {
248
248
  for (const [key, value] of Array.from(target.params.entries()))
249
- yield* apply([value, key, target]);
249
+ yield* apply([value, key, target], args[1]);
250
250
  return undefined;
251
251
  });
252
252
  },
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-20148",
4
+ "version": "0.0.0-dev-20150",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",