@opencode/codemode 2.0.1 → 2.0.2

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.
Files changed (61) hide show
  1. package/dist/data.d.ts +6 -5
  2. package/dist/data.js +44 -46
  3. package/dist/index.d.ts +0 -1
  4. package/dist/index.js +0 -1
  5. package/dist/interpreter/errors.d.ts +3 -4
  6. package/dist/interpreter/errors.js +40 -17
  7. package/dist/interpreter/execute.js +5 -3
  8. package/dist/interpreter/generators.d.ts +4 -0
  9. package/dist/interpreter/generators.js +25 -0
  10. package/dist/interpreter/globals.js +67 -46
  11. package/dist/interpreter/intrinsics.d.ts +8 -5
  12. package/dist/interpreter/intrinsics.js +59 -18
  13. package/dist/interpreter/model.d.ts +2 -32
  14. package/dist/interpreter/model.js +4 -38
  15. package/dist/interpreter/native.d.ts +19 -0
  16. package/dist/interpreter/native.js +40 -0
  17. package/dist/interpreter/objects.d.ts +105 -12
  18. package/dist/interpreter/objects.js +190 -66
  19. package/dist/interpreter/promises.d.ts +12 -13
  20. package/dist/interpreter/promises.js +52 -46
  21. package/dist/interpreter/references.d.ts +1 -0
  22. package/dist/interpreter/references.js +20 -33
  23. package/dist/interpreter/runner.d.ts +15 -13
  24. package/dist/interpreter/runner.js +19 -19
  25. package/dist/interpreter/runtime.d.ts +3 -3
  26. package/dist/interpreter/runtime.js +150 -314
  27. package/dist/stdlib/array.d.ts +4 -2
  28. package/dist/stdlib/array.js +423 -31
  29. package/dist/stdlib/collections.d.ts +3 -7
  30. package/dist/stdlib/collections.js +286 -114
  31. package/dist/stdlib/console.d.ts +3 -2
  32. package/dist/stdlib/console.js +35 -30
  33. package/dist/stdlib/date.d.ts +1 -7
  34. package/dist/stdlib/date.js +93 -188
  35. package/dist/stdlib/json.d.ts +2 -2
  36. package/dist/stdlib/json.js +24 -24
  37. package/dist/stdlib/math.d.ts +2 -2
  38. package/dist/stdlib/math.js +96 -76
  39. package/dist/stdlib/number.d.ts +3 -4
  40. package/dist/stdlib/number.js +94 -59
  41. package/dist/stdlib/object.d.ts +4 -4
  42. package/dist/stdlib/object.js +123 -49
  43. package/dist/stdlib/regexp.d.ts +6 -8
  44. package/dist/stdlib/regexp.js +71 -63
  45. package/dist/stdlib/string.d.ts +2 -2
  46. package/dist/stdlib/string.js +213 -50
  47. package/dist/stdlib/url.d.ts +5 -13
  48. package/dist/stdlib/url.js +196 -96
  49. package/dist/stdlib/value.d.ts +5 -4
  50. package/dist/stdlib/value.js +16 -16
  51. package/dist/stdlib/web.d.ts +4 -4
  52. package/dist/stdlib/web.js +8 -7
  53. package/dist/tool-runtime.d.ts +2 -1
  54. package/dist/tool-runtime.js +2 -2
  55. package/package.json +1 -1
  56. package/dist/interpreter/host.d.ts +0 -41
  57. package/dist/interpreter/host.js +0 -44
  58. package/dist/interpreter/methods.d.ts +0 -4
  59. package/dist/interpreter/methods.js +0 -837
  60. package/dist/values.d.ts +0 -37
  61. package/dist/values.js +0 -56
@@ -1,51 +1,214 @@
1
- import { sync } from "../interpreter/host.js";
2
- import { InterpreterRuntimeError } from "../interpreter/model.js";
3
- import { coercion } from "./value.js";
4
- export const stringMethods = new Set([
5
- "toLowerCase",
6
- "toUpperCase",
7
- "trim",
8
- "trimStart",
9
- "trimEnd",
10
- "trimLeft",
11
- "trimRight",
12
- "split",
13
- "slice",
14
- "substring",
15
- "substr",
16
- "includes",
17
- "startsWith",
18
- "endsWith",
19
- "indexOf",
20
- "lastIndexOf",
21
- "replace",
22
- "replaceAll",
23
- "repeat",
24
- "padStart",
25
- "padEnd",
26
- "charAt",
27
- "charCodeAt",
28
- "codePointAt",
29
- "at",
30
- "concat",
31
- "toString",
32
- "match",
33
- "matchAll",
34
- "search",
35
- "localeCompare",
36
- "normalize",
37
- "isWellFormed",
38
- "toWellFormed",
39
- ]);
40
- const codeUnits = (name, op) => sync(`String.${name}`, (args, node) => op(...args.map((arg) => {
41
- if (typeof arg !== "number")
42
- throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node);
1
+ import { Effect } from "effect";
2
+ import { toProgram } from "../data.js";
3
+ import { constructor, methods } from "../interpreter/native.js";
4
+ import { InterpreterRuntimeError, rangeError } from "../interpreter/model.js";
5
+ import { ProgramArray, ProgramPromise, ProgramRegExp, record } from "../interpreter/objects.js";
6
+ import { containsOpaqueReference, typeofValue } from "../interpreter/references.js";
7
+ import { applyCollectionCallback, isSupportedCallback } from "../interpreter/runner.js";
8
+ import { matchToValue, toHostRegex } from "./regexp.js";
9
+ import { coerceToNumber, coerceToString, coercion } from "./value.js";
10
+ // console is intercepted by the interpreter before reaching here.
11
+ const requireDataArgument = (name, index, arg, node) => {
12
+ if (containsOpaqueReference(arg)) {
13
+ throw new InterpreterRuntimeError(`String.${name} expects argument ${index + 1} to be a data value.`, node, "InvalidDataValue");
14
+ }
43
15
  return arg;
44
- })));
45
- export const stringGlobal = coercion("String", {
46
- instanceOf: () => false,
47
- members: {
48
- fromCharCode: codeUnits("fromCharCode", String.fromCharCode),
49
- fromCodePoint: codeUnits("fromCodePoint", String.fromCodePoint),
50
- },
51
- });
16
+ };
17
+ const replaceAllNeedsGlobal = (pattern, node) => {
18
+ if (!pattern.global) {
19
+ throw new InterpreterRuntimeError(`String.replaceAll requires a regular expression with the global (g) flag: write /${pattern.source}/${pattern.flags}g, or use String.replace to replace only the first match.`, node);
20
+ }
21
+ };
22
+ const replaceWithCallback = (runner, value, name, args, node) => {
23
+ const protos = runner.prototypes;
24
+ const apply = applyCollectionCallback(runner, args[1], `String.${name}`, node);
25
+ const matches = [];
26
+ const collect = (...callbackArgs) => {
27
+ const match = callbackArgs[0];
28
+ const groups = callbackArgs[callbackArgs.length - 1];
29
+ const hasGroups = groups !== null && typeof groups === "object";
30
+ const offset = callbackArgs[callbackArgs.length - (hasGroups ? 3 : 2)];
31
+ if (typeof match !== "string" || typeof offset !== "number") {
32
+ throw new InterpreterRuntimeError(`String.${name} produced an invalid replacement match.`, node);
33
+ }
34
+ if (hasGroups)
35
+ callbackArgs[callbackArgs.length - 1] = record(protos.Object, groups);
36
+ matches.push({ match, offset, args: callbackArgs });
37
+ return match;
38
+ };
39
+ const pattern = args[0];
40
+ if (pattern instanceof ProgramRegExp) {
41
+ if (name === "replaceAll")
42
+ replaceAllNeedsGlobal(pattern.regex, node);
43
+ if (name === "replace")
44
+ value.replace(pattern.regex, collect);
45
+ else
46
+ value.replaceAll(pattern.regex, collect);
47
+ }
48
+ else {
49
+ const search = coerceToString(requireDataArgument(name, 0, pattern, node));
50
+ if (name === "replace")
51
+ value.replace(search, collect);
52
+ else
53
+ value.replaceAll(search, collect);
54
+ }
55
+ return Effect.gen(function* () {
56
+ const output = [];
57
+ let end = 0;
58
+ for (const match of matches) {
59
+ const replacement = yield* apply(match.args);
60
+ output.push(value.slice(end, match.offset), replacement instanceof ProgramPromise
61
+ ? "[object Promise]"
62
+ : coerceToString(toProgram(protos, replacement, `String.${name} replacer result`)));
63
+ end = match.offset + match.match.length;
64
+ }
65
+ output.push(value.slice(end));
66
+ return output.join("");
67
+ });
68
+ };
69
+ export const stringGlobal = (runner) => {
70
+ const protos = runner.prototypes;
71
+ const string = constructor(protos, protos.String, {
72
+ name: "String",
73
+ length: 1,
74
+ call: coercion(runner, "String").call,
75
+ });
76
+ const codeUnits = (name, op) => [
77
+ name,
78
+ 1,
79
+ (_, args, node) => op(...args.map((arg) => {
80
+ if (typeof arg !== "number") {
81
+ throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node);
82
+ }
83
+ return arg;
84
+ })),
85
+ ];
86
+ methods(protos, string, [
87
+ codeUnits("fromCharCode", String.fromCharCode),
88
+ codeUnits("fromCodePoint", String.fromCodePoint),
89
+ ]);
90
+ const self = (thisValue, name, node) => {
91
+ if (typeof thisValue === "string")
92
+ return thisValue;
93
+ if (thisValue === null || thisValue === undefined) {
94
+ throw new InterpreterRuntimeError(`String.prototype.${name} called on null or undefined.`, node);
95
+ }
96
+ return coerceToString(thisValue);
97
+ };
98
+ // Coerce arguments like native JS; opaque runtime references still reject.
99
+ const str = (name, args, index, node) => coerceToString(requireDataArgument(name, index, args[index], node));
100
+ const num = (name, args, index, node) => coerceToNumber(requireDataArgument(name, index, args[index], node));
101
+ const optNum = (name, args, index, node) => args[index] === undefined ? undefined : num(name, args, index, node);
102
+ const optStr = (name, args, index, node) => args[index] === undefined ? undefined : str(name, args, index, node);
103
+ const rejectRegex = (name, args, node) => {
104
+ if (args[0] instanceof ProgramRegExp) {
105
+ throw new InterpreterRuntimeError(`String.${name} cannot take a regular expression; use regex.test(string) or String.search instead.`, node);
106
+ }
107
+ };
108
+ const simple = (name, length, op) => [name, length, (thisValue, args, node) => op(self(thisValue, name, node), args, node)];
109
+ const replace = (name) => simple(name, 2, (value, args, node) => {
110
+ if (isSupportedCallback(args[1]))
111
+ return replaceWithCallback(runner, value, name, args, node);
112
+ if (typeofValue(args[1]) === "function") {
113
+ throw new InterpreterRuntimeError(`String.${name} cannot use this callable as a replacer; wrap it in an arrow function, e.g. (match) => tools.ns.tool(match).`, node);
114
+ }
115
+ if (args[0] instanceof ProgramRegExp) {
116
+ const pattern = args[0].regex;
117
+ const replacement = str(name, args, 1, node);
118
+ if (name === "replaceAll")
119
+ replaceAllNeedsGlobal(pattern, node);
120
+ return name === "replace" ? value.replace(pattern, replacement) : value.replaceAll(pattern, replacement);
121
+ }
122
+ if (name === "replace")
123
+ return value.replace(str(name, args, 0, node), str(name, args, 1, node));
124
+ return value.replaceAll(str(name, args, 0, node), str(name, args, 1, node));
125
+ });
126
+ methods(protos, protos.String, [
127
+ simple("toString", 0, (value) => value),
128
+ simple("valueOf", 0, (value) => value),
129
+ simple("toLowerCase", 0, (value) => value.toLowerCase()),
130
+ simple("toUpperCase", 0, (value) => value.toUpperCase()),
131
+ simple("trim", 0, (value) => value.trim()),
132
+ simple("trimStart", 0, (value) => value.trimStart()),
133
+ simple("trimLeft", 0, (value) => value.trimStart()),
134
+ simple("trimEnd", 0, (value) => value.trimEnd()),
135
+ simple("trimRight", 0, (value) => value.trimEnd()),
136
+ // Locale/options are deliberately unsupported; comparison uses the host default locale.
137
+ simple("localeCompare", 1, (value, args, node) => value.localeCompare(str("localeCompare", args, 0, node))),
138
+ simple("normalize", 0, (value, args, node) => {
139
+ const form = optStr("normalize", args, 0, node);
140
+ try {
141
+ return value.normalize(form);
142
+ }
143
+ catch {
144
+ throw rangeError(`String.normalize expects the form "NFC", "NFD", "NFKC", or "NFKD" (got ${JSON.stringify(form)}).`, node);
145
+ }
146
+ }),
147
+ simple("split", 2, (value, args, node) => {
148
+ const wrap = (parts) => new ProgramArray(protos.Array, parts);
149
+ // Native: an undefined separator returns the whole string, not a split on "undefined",
150
+ // unless the limit truncates to zero.
151
+ const requestedLimit = optNum("split", args, 1, node);
152
+ if (args[0] === undefined) {
153
+ return wrap(requestedLimit !== undefined && requestedLimit >>> 0 === 0 ? [] : [value]);
154
+ }
155
+ if (args[0] instanceof ProgramRegExp)
156
+ return wrap(value.split(args[0].regex, requestedLimit));
157
+ return wrap(value.split(str("split", args, 0, node), requestedLimit === undefined ? undefined : requestedLimit >>> 0));
158
+ }),
159
+ simple("slice", 2, (value, args, node) => value.slice(optNum("slice", args, 0, node), optNum("slice", args, 1, node))),
160
+ simple("includes", 1, (value, args, node) => {
161
+ rejectRegex("includes", args, node);
162
+ return value.includes(str("includes", args, 0, node), optNum("includes", args, 1, node));
163
+ }),
164
+ simple("startsWith", 1, (value, args, node) => {
165
+ rejectRegex("startsWith", args, node);
166
+ return value.startsWith(str("startsWith", args, 0, node), optNum("startsWith", args, 1, node));
167
+ }),
168
+ simple("endsWith", 1, (value, args, node) => {
169
+ rejectRegex("endsWith", args, node);
170
+ return value.endsWith(str("endsWith", args, 0, node), optNum("endsWith", args, 1, node));
171
+ }),
172
+ simple("indexOf", 1, (value, args, node) => value.indexOf(str("indexOf", args, 0, node), optNum("indexOf", args, 1, node))),
173
+ simple("lastIndexOf", 1, (value, args, node) => value.lastIndexOf(str("lastIndexOf", args, 0, node), optNum("lastIndexOf", args, 1, node))),
174
+ replace("replace"),
175
+ replace("replaceAll"),
176
+ simple("match", 1, (value, args, node) => {
177
+ const pattern = toHostRegex(args[0], "match", node);
178
+ const matched = value.match(pattern);
179
+ if (matched === null)
180
+ return null;
181
+ // Preserve the own `index` and `groups` properties on non-global matches.
182
+ if (pattern.global)
183
+ return toProgram(protos, matched, "String.match result");
184
+ return matchToValue(protos, matched);
185
+ }),
186
+ simple("matchAll", 1, (value, args, node) => {
187
+ const pattern = toHostRegex(args[0], "matchAll", node, "g");
188
+ if (!pattern.global) {
189
+ 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);
190
+ }
191
+ return new ProgramArray(protos.Array, Array.from(value.matchAll(pattern), (match) => matchToValue(protos, match)));
192
+ }),
193
+ simple("search", 1, (value, args, node) => value.search(toHostRegex(args[0], "search", node))),
194
+ simple("repeat", 1, (value, args, node) => {
195
+ const count = num("repeat", args, 0, node);
196
+ if (!Number.isFinite(count) || count < 0) {
197
+ throw rangeError("String.repeat expects a finite non-negative count.", node);
198
+ }
199
+ return value.repeat(count);
200
+ }),
201
+ simple("padStart", 1, (value, args, node) => value.padStart(num("padStart", args, 0, node), optStr("padStart", args, 1, node))),
202
+ simple("padEnd", 1, (value, args, node) => value.padEnd(num("padEnd", args, 0, node), optStr("padEnd", args, 1, node))),
203
+ simple("charAt", 1, (value, args, node) => value.charAt(optNum("charAt", args, 0, node) ?? 0)),
204
+ simple("at", 1, (value, args, node) => value.at(optNum("at", args, 0, node) ?? 0)),
205
+ simple("substring", 2, (value, args, node) => value.substring(optNum("substring", args, 0, node) ?? 0, optNum("substring", args, 1, node))),
206
+ simple("substr", 2, (value, args, node) => value.substr(optNum("substr", args, 0, node) ?? 0, optNum("substr", args, 1, node))),
207
+ simple("isWellFormed", 0, (value) => value.isWellFormed()),
208
+ simple("toWellFormed", 0, (value) => value.toWellFormed()),
209
+ simple("charCodeAt", 1, (value, args, node) => value.charCodeAt(optNum("charCodeAt", args, 0, node) ?? 0)),
210
+ simple("codePointAt", 1, (value, args, node) => value.codePointAt(optNum("codePointAt", args, 0, node) ?? 0)),
211
+ simple("concat", 1, (value, args, node) => value.concat(...args.map((_, index) => str("concat", args, index, node)))),
212
+ ]);
213
+ return string;
214
+ };
@@ -1,16 +1,8 @@
1
- import { HostFunction } from "../interpreter/host.js";
2
- import { type AstNode } from "../interpreter/model.js";
1
+ import type { Prototypes } from "../interpreter/intrinsics.js";
3
2
  import { type Runner } from "../interpreter/runner.js";
4
- import { Values } from "../values.js";
5
- export declare const urlProperties: Set<string>;
6
- export declare const urlWritableProperties: Set<string>;
7
- export declare const urlMethods: Set<string>;
8
- export declare const urlSearchParamsMethods: Set<string>;
9
- export declare const uriArgument: (value: unknown, label: string) => string;
3
+ export declare const uriArgument: (protos: Prototypes, value: unknown, label: string) => string;
10
4
  type UriFunction = "encodeURI" | "encodeURIComponent" | "decodeURI" | "decodeURIComponent";
11
- export declare const uriGlobal: (name: UriFunction) => HostFunction<never>;
12
- export declare const urlArgument: (value: unknown, label: string) => string;
13
- export declare const urlGlobal: HostFunction<never>;
14
- export declare const urlSearchParamsGlobal: <R>(runner: Runner<R>) => HostFunction<R>;
15
- export declare const invokeURLMethod: (value: Values.URL, name: string, node: AstNode) => string;
5
+ export declare const uriGlobal: <R>(runner: Runner<R>, name: UriFunction) => import("../interpreter/objects.js").NativeFunction<R>;
6
+ export declare const urlGlobal: <R>(runner: Runner<R>) => import("../interpreter/objects.js").NativeFunction<R>;
7
+ export declare const urlSearchParamsGlobal: <R>(runner: Runner<R>) => import("../interpreter/objects.js").NativeFunction<R>;
16
8
  export {};
@@ -1,13 +1,12 @@
1
1
  import { Effect } from "effect";
2
- import { toProgram } from "../data.js";
3
- import { HostFunction, requiresNew, sync, syncCall } from "../interpreter/host.js";
2
+ import { toProgram, ToolRuntimeError } from "../data.js";
3
+ import { constructor, fn, methods, prototypeFrom, receiver, requiresNew } from "../interpreter/native.js";
4
4
  import { InterpreterRuntimeError, uriError } from "../interpreter/model.js";
5
- import { ownEntries, ProgramObject } from "../interpreter/objects.js";
5
+ import { defineAccessor, entries, isWrapper, ProgramArray, ProgramObject, ProgramURL, ProgramURLSearchParams, } from "../interpreter/objects.js";
6
6
  import { isRuntimeReference } from "../interpreter/references.js";
7
- import { preserveConsumerError } from "../interpreter/runner.js";
8
- import { Values } from "../values.js";
7
+ import { applyCollectionCallback, preserveConsumerError } from "../interpreter/runner.js";
9
8
  import { coerceToString } from "./value.js";
10
- export const urlProperties = new Set([
9
+ const urlProperties = [
11
10
  "href",
12
11
  "origin",
13
12
  "protocol",
@@ -19,43 +18,16 @@ export const urlProperties = new Set([
19
18
  "pathname",
20
19
  "search",
21
20
  "hash",
22
- ]);
23
- export const urlWritableProperties = new Set([
24
- "href",
25
- "protocol",
26
- "username",
27
- "password",
28
- "host",
29
- "hostname",
30
- "port",
31
- "pathname",
32
- "search",
33
- "hash",
34
- ]);
35
- export const urlMethods = new Set(["toString", "toJSON"]);
36
- export const urlSearchParamsMethods = new Set([
37
- "append",
38
- "delete",
39
- "get",
40
- "getAll",
41
- "has",
42
- "set",
43
- "sort",
44
- "forEach",
45
- "keys",
46
- "values",
47
- "entries",
48
- "toString",
49
- ]);
50
- export const uriArgument = (value, label) => coerceToString(toProgram(value, label));
21
+ ];
22
+ export const uriArgument = (protos, value, label) => coerceToString(toProgram(protos, value, label));
51
23
  const uriFunctions = {
52
24
  encodeURI,
53
25
  encodeURIComponent,
54
26
  decodeURI,
55
27
  decodeURIComponent,
56
28
  };
57
- export const uriGlobal = (name) => sync(name, (args, node) => {
58
- const value = uriArgument(args[0], `${name} input`);
29
+ export const uriGlobal = (runner, name) => fn(runner.prototypes, name, 1, (_, args, node) => {
30
+ const value = uriArgument(runner.prototypes, args[0], `${name} input`);
59
31
  try {
60
32
  return uriFunctions[name](value);
61
33
  }
@@ -63,42 +35,72 @@ export const uriGlobal = (name) => sync(name, (args, node) => {
63
35
  throw uriError(`${name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node);
64
36
  }
65
37
  });
66
- export const urlArgument = (value, label) => value instanceof Values.URL ? value.url.href : uriArgument(value, label);
67
- const urlStatic = (name) => sync(`URL.${name}`, (args, node) => {
68
- if (args.length === 0) {
69
- throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node);
70
- }
71
- const input = urlArgument(args[0], `URL.${name} input`);
72
- const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`);
73
- try {
74
- const url = new URL(input, base);
75
- return name === "canParse" ? true : new Values.URL(url);
76
- }
77
- catch {
78
- return name === "canParse" ? false : null;
79
- }
80
- });
81
- const constructURL = (args, node) => {
82
- if (args.length === 0) {
83
- throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node);
84
- }
85
- const input = urlArgument(args[0], "new URL input");
86
- const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base");
87
- try {
88
- return new Values.URL(new URL(input, base));
89
- }
90
- catch {
91
- throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node);
38
+ const urlArgument = (protos, value, label) => value instanceof ProgramURL ? value.url.href : uriArgument(protos, value, label);
39
+ export const urlGlobal = (runner) => {
40
+ const protos = runner.prototypes;
41
+ const proto = protos.URL;
42
+ const construct = (args, into, node) => {
43
+ if (args.length === 0) {
44
+ throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node);
45
+ }
46
+ const input = urlArgument(protos, args[0], "new URL input");
47
+ const base = args[1] === undefined ? undefined : urlArgument(protos, args[1], "new URL base");
48
+ try {
49
+ return new ProgramURL(into, protos.URLSearchParams, new URL(input, base));
50
+ }
51
+ catch {
52
+ throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node);
53
+ }
54
+ };
55
+ const url = constructor(protos, proto, {
56
+ name: "URL",
57
+ length: 1,
58
+ call: requiresNew("URL"),
59
+ construct: (args, newTarget, node) => Effect.sync(() => construct(args, prototypeFrom(newTarget, proto), node)),
60
+ });
61
+ const parse = (name) => [
62
+ name,
63
+ 1,
64
+ (_, args, node) => {
65
+ if (args.length === 0)
66
+ throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node);
67
+ const input = urlArgument(protos, args[0], `URL.${name} input`);
68
+ const base = args[1] === undefined ? undefined : urlArgument(protos, args[1], `URL.${name} base`);
69
+ try {
70
+ const parsed = new URL(input, base);
71
+ return name === "canParse" ? true : new ProgramURL(proto, protos.URLSearchParams, parsed);
72
+ }
73
+ catch {
74
+ return name === "canParse" ? false : null;
75
+ }
76
+ },
77
+ ];
78
+ methods(protos, url, [parse("canParse"), parse("parse")]);
79
+ const self = (thisValue, name, node) => receiver(ProgramURL, thisValue, `URL.prototype.${name}`, node);
80
+ for (const name of urlProperties) {
81
+ defineAccessor(proto, name, (thisValue) => self(thisValue, name).url[name], name === "origin"
82
+ ? undefined
83
+ : (thisValue, value) => {
84
+ const target = self(thisValue, name);
85
+ try {
86
+ ;
87
+ target.url[name] = uriArgument(protos, value, `URL.${name} value`);
88
+ }
89
+ catch (error) {
90
+ if (error instanceof InterpreterRuntimeError || error instanceof ToolRuntimeError)
91
+ throw error;
92
+ throw new InterpreterRuntimeError(`URL.${name} received an invalid value.`);
93
+ }
94
+ });
92
95
  }
96
+ defineAccessor(proto, "searchParams", (thisValue) => self(thisValue, "searchParams").searchParams);
97
+ methods(protos, proto, [
98
+ ["toString", 0, (thisValue, _, node) => self(thisValue, "toString", node).url.href],
99
+ ["toJSON", 0, (thisValue, _, node) => self(thisValue, "toJSON", node).url.href],
100
+ ]);
101
+ return url;
93
102
  };
94
- export const urlGlobal = new HostFunction({
95
- name: "URL",
96
- call: requiresNew("URL"),
97
- construct: syncCall(constructURL),
98
- instanceOf: (value) => value instanceof Values.URL,
99
- members: { canParse: urlStatic("canParse"), parse: urlStatic("parse") },
100
- });
101
- const readURLSearchParamsPair = (runner, value, node) => Effect.gen(function* () {
103
+ const readPair = (runner, value, node) => Effect.gen(function* () {
102
104
  const cursor = yield* runner.syncIterator(value, node);
103
105
  if (cursor === undefined) {
104
106
  throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node);
@@ -108,54 +110,152 @@ const readURLSearchParamsPair = (runner, value, node) => Effect.gen(function* ()
108
110
  const step = yield* cursor.next;
109
111
  if (step.done)
110
112
  return items;
111
- items.push(yield* preserveConsumerError(cursor, Effect.sync(() => uriArgument(step.value, "URLSearchParams pair value"))));
113
+ items.push(yield* preserveConsumerError(cursor, Effect.sync(() => uriArgument(runner.prototypes, step.value, "URLSearchParams pair value"))));
112
114
  }
113
115
  });
114
- const constructURLSearchParams = (runner, init, node) => {
116
+ const constructURLSearchParams = (runner, init, proto, node) => {
117
+ const wrap = (params) => new ProgramURLSearchParams(proto, params);
115
118
  if (init === undefined)
116
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams()));
117
- if (init instanceof Values.URLSearchParams) {
118
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init.params)));
119
- }
119
+ return Effect.succeed(wrap(new URLSearchParams()));
120
+ if (init instanceof ProgramURLSearchParams)
121
+ return Effect.succeed(wrap(new URLSearchParams(init.params)));
120
122
  if (typeof init === "string")
121
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init)));
123
+ return Effect.succeed(wrap(new URLSearchParams(init)));
122
124
  if (init === null || typeof init === "number" || typeof init === "boolean") {
123
- return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(coerceToString(init))));
125
+ return Effect.succeed(wrap(new URLSearchParams(coerceToString(init))));
124
126
  }
125
127
  return Effect.gen(function* () {
126
128
  const cursor = yield* runner.syncIterator(init, node);
127
129
  if (cursor !== undefined) {
128
- const entries = [];
130
+ const pairs = [];
129
131
  while (true) {
130
132
  const step = yield* cursor.next;
131
133
  if (step.done) {
132
- if (entries.some((entry) => entry.length !== 2)) {
134
+ if (pairs.some((entry) => entry.length !== 2)) {
133
135
  throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node);
134
136
  }
135
- return new Values.URLSearchParams(new URLSearchParams(entries.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
137
+ return wrap(new URLSearchParams(pairs.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
136
138
  }
137
- entries.push(yield* preserveConsumerError(cursor, readURLSearchParamsPair(runner, step.value, node)));
139
+ pairs.push(yield* preserveConsumerError(cursor, readPair(runner, step.value, node)));
138
140
  }
139
141
  }
140
142
  if (isRuntimeReference(init)) {
141
143
  throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node);
142
144
  }
143
- if (Values.isValue(init))
144
- return new Values.URLSearchParams(new URLSearchParams());
145
+ if (isWrapper(init))
146
+ return wrap(new URLSearchParams());
145
147
  if (!(init instanceof ProgramObject)) {
146
148
  throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node);
147
149
  }
148
- return new Values.URLSearchParams(new URLSearchParams(Object.fromEntries(ownEntries(init).map(([key, value]) => [key, coerceToString(value)]))));
150
+ return wrap(new URLSearchParams(Object.fromEntries(entries(init).map(([key, value]) => [key, coerceToString(value)]))));
149
151
  });
150
152
  };
151
- export const urlSearchParamsGlobal = (runner) => new HostFunction({
152
- name: "URLSearchParams",
153
- call: requiresNew("URLSearchParams"),
154
- construct: (args, node) => constructURLSearchParams(runner, args[0], node),
155
- instanceOf: (value) => value instanceof Values.URLSearchParams,
156
- });
157
- export const invokeURLMethod = (value, name, node) => {
158
- if (name === "toString" || name === "toJSON")
159
- return value.url.href;
160
- throw new InterpreterRuntimeError(`URL method '${name}' is not available.`, node);
153
+ export const urlSearchParamsGlobal = (runner) => {
154
+ const protos = runner.prototypes;
155
+ const proto = protos.URLSearchParams;
156
+ const searchParams = constructor(protos, proto, {
157
+ name: "URLSearchParams",
158
+ call: requiresNew("URLSearchParams"),
159
+ construct: (args, newTarget, node) => constructURLSearchParams(runner, args[0], prototypeFrom(newTarget, proto), node),
160
+ });
161
+ const self = (thisValue, name, node) => receiver(ProgramURLSearchParams, thisValue, `URLSearchParams.prototype.${name}`, node);
162
+ const wrap = (items) => new ProgramArray(protos.Array, items);
163
+ const arg = (name, args, index) => uriArgument(protos, args[index], `URLSearchParams.${name} argument ${index + 1}`);
164
+ const requireArgs = (name, args, count, node) => {
165
+ if (args.length < count) {
166
+ throw new InterpreterRuntimeError(`URLSearchParams.${name} requires ${count} argument${count === 1 ? "" : "s"}.`, node);
167
+ }
168
+ };
169
+ defineAccessor(proto, "size", (thisValue) => self(thisValue, "size").params.size);
170
+ methods(protos, proto, [
171
+ [
172
+ "append",
173
+ 2,
174
+ (thisValue, args, node) => {
175
+ requireArgs("append", args, 2, node);
176
+ self(thisValue, "append", node).params.append(arg("append", args, 0), arg("append", args, 1));
177
+ return undefined;
178
+ },
179
+ ],
180
+ [
181
+ "delete",
182
+ 1,
183
+ (thisValue, args, node) => {
184
+ requireArgs("delete", args, 1, node);
185
+ const params = self(thisValue, "delete", node).params;
186
+ if (args[1] !== undefined)
187
+ params.delete(arg("delete", args, 0), arg("delete", args, 1));
188
+ else
189
+ params.delete(arg("delete", args, 0));
190
+ return undefined;
191
+ },
192
+ ],
193
+ [
194
+ "get",
195
+ 1,
196
+ (thisValue, args, node) => {
197
+ requireArgs("get", args, 1, node);
198
+ return self(thisValue, "get", node).params.get(arg("get", args, 0));
199
+ },
200
+ ],
201
+ [
202
+ "getAll",
203
+ 1,
204
+ (thisValue, args, node) => {
205
+ requireArgs("getAll", args, 1, node);
206
+ return wrap(self(thisValue, "getAll", node).params.getAll(arg("getAll", args, 0)));
207
+ },
208
+ ],
209
+ [
210
+ "has",
211
+ 1,
212
+ (thisValue, args, node) => {
213
+ requireArgs("has", args, 1, node);
214
+ const params = self(thisValue, "has", node).params;
215
+ return args[1] !== undefined
216
+ ? params.has(arg("has", args, 0), arg("has", args, 1))
217
+ : params.has(arg("has", args, 0));
218
+ },
219
+ ],
220
+ [
221
+ "set",
222
+ 2,
223
+ (thisValue, args, node) => {
224
+ requireArgs("set", args, 2, node);
225
+ self(thisValue, "set", node).params.set(arg("set", args, 0), arg("set", args, 1));
226
+ return undefined;
227
+ },
228
+ ],
229
+ [
230
+ "sort",
231
+ 0,
232
+ (thisValue, _, node) => {
233
+ self(thisValue, "sort", node).params.sort();
234
+ return undefined;
235
+ },
236
+ ],
237
+ ["keys", 0, (thisValue, _, node) => wrap(Array.from(self(thisValue, "keys", node).params.keys()))],
238
+ ["values", 0, (thisValue, _, node) => wrap(Array.from(self(thisValue, "values", node).params.values()))],
239
+ [
240
+ "entries",
241
+ 0,
242
+ (thisValue, _, node) => wrap(Array.from(self(thisValue, "entries", node).params.entries(), ([key, value]) => wrap([key, value]))),
243
+ ],
244
+ ["toString", 0, (thisValue, _, node) => self(thisValue, "toString", node).params.toString()],
245
+ [
246
+ "forEach",
247
+ 1,
248
+ (thisValue, args, node) => {
249
+ requireArgs("forEach", args, 1, node);
250
+ const target = self(thisValue, "forEach", node);
251
+ const apply = applyCollectionCallback(runner, args[0], "URLSearchParams.forEach", node);
252
+ return Effect.gen(function* () {
253
+ for (const [key, value] of Array.from(target.params.entries()))
254
+ yield* apply([value, key, target]);
255
+ return undefined;
256
+ });
257
+ },
258
+ ],
259
+ ]);
260
+ return searchParams;
161
261
  };
@@ -1,8 +1,9 @@
1
- import { type HostFunction, type SyncOptions } from "../interpreter/host.js";
1
+ import { type NativeFunction } from "../interpreter/objects.js";
2
+ import type { Runner } from "../interpreter/runner.js";
2
3
  export declare const compoundOperators: Set<string>;
4
+ /** The built-in string form of a value, without consulting program-defined `toString` methods. */
3
5
  export declare const coerceToString: (value: unknown) => string;
4
6
  export declare const coerceToNumber: (value: unknown) => number;
5
- type Coercion = "Number" | "String" | "Boolean" | "parseInt" | "parseFloat" | "isFinite" | "isNaN";
7
+ export type Coercion = "Number" | "String" | "Boolean" | "parseInt" | "parseFloat" | "isFinite" | "isNaN";
6
8
  /** A global coercion function such as `Number` or `parseInt`. */
7
- export declare const coercion: (name: Coercion, options?: SyncOptions) => HostFunction;
8
- export {};
9
+ export declare const coercion: <R>(runner: Runner<R>, name: Coercion, length?: number) => NativeFunction<R>;