@opencode/codemode 0.0.0-dev-19366 → 0.0.0-dev-19367

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 (46) hide show
  1. package/dist/interpreter/errors.d.ts +4 -6
  2. package/dist/interpreter/errors.js +20 -4
  3. package/dist/interpreter/globals.d.ts +13 -0
  4. package/dist/interpreter/globals.js +59 -0
  5. package/dist/interpreter/host.d.ts +41 -0
  6. package/dist/interpreter/host.js +44 -0
  7. package/dist/interpreter/methods.d.ts +3 -23
  8. package/dist/interpreter/methods.js +8 -183
  9. package/dist/interpreter/model.d.ts +0 -41
  10. package/dist/interpreter/model.js +0 -56
  11. package/dist/interpreter/promises.d.ts +7 -8
  12. package/dist/interpreter/promises.js +45 -22
  13. package/dist/interpreter/references.js +11 -28
  14. package/dist/interpreter/runner.d.ts +23 -0
  15. package/dist/interpreter/runner.js +42 -0
  16. package/dist/interpreter/runtime.d.ts +0 -11
  17. package/dist/interpreter/runtime.js +33 -423
  18. package/dist/stdlib/array.d.ts +3 -0
  19. package/dist/stdlib/array.js +73 -0
  20. package/dist/stdlib/collections.d.ts +6 -1
  21. package/dist/stdlib/collections.js +120 -1
  22. package/dist/stdlib/console.d.ts +3 -2
  23. package/dist/stdlib/console.js +11 -2
  24. package/dist/stdlib/date.d.ts +3 -2
  25. package/dist/stdlib/date.js +27 -11
  26. package/dist/stdlib/json.d.ts +4 -4
  27. package/dist/stdlib/json.js +6 -2
  28. package/dist/stdlib/math.d.ts +3 -7
  29. package/dist/stdlib/math.js +85 -153
  30. package/dist/stdlib/number.d.ts +1 -3
  31. package/dist/stdlib/number.js +28 -36
  32. package/dist/stdlib/object.d.ts +4 -6
  33. package/dist/stdlib/object.js +101 -71
  34. package/dist/stdlib/regexp.d.ts +2 -4
  35. package/dist/stdlib/regexp.js +32 -9
  36. package/dist/stdlib/string.d.ts +1 -3
  37. package/dist/stdlib/string.js +14 -16
  38. package/dist/stdlib/url.d.ts +8 -4
  39. package/dist/stdlib/url.js +97 -21
  40. package/dist/stdlib/value.d.ts +5 -3
  41. package/dist/stdlib/value.js +21 -19
  42. package/package.json +1 -1
  43. package/dist/interpreter/iterator.d.ts +0 -13
  44. package/dist/interpreter/iterator.js +0 -4
  45. package/dist/stdlib/promise.d.ts +0 -2
  46. package/dist/stdlib/promise.js +0 -1
@@ -1,85 +1,66 @@
1
1
  import { Effect } from "effect";
2
+ import { isBlockedMember, toProgram } from "../data.js";
3
+ import { HostFunction, sync, syncCall } from "../interpreter/host.js";
2
4
  import { AsyncIteratorSymbol, InterpreterRuntimeError, IteratorSymbol } from "../interpreter/model.js";
3
- import { containsOpaqueReference, rejectCircularInsertion } from "../interpreter/references.js";
4
- import { isBlockedMember } from "../data.js";
5
+ import { containsOpaqueReference, rejectCircularInsertion, typeofValue } from "../interpreter/references.js";
6
+ import { preserveConsumerError } from "../interpreter/runner.js";
7
+ import { ToolReference } from "../tool-runtime.js";
5
8
  import { Values } from "../values.js";
6
- import { toProgram } from "../data.js";
9
+ import { groupBy } from "./collections.js";
7
10
  import { coerceToString } from "./value.js";
8
- import { preserveConsumerError } from "../interpreter/iterator.js";
9
- export const objectMethodsPreservingIdentity = new Set(["assign", "values", "entries", "fromEntries"]);
10
- export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "is", "assign", "fromEntries", "groupBy"]);
11
- export const invokeObjectMethod = (name, args, node) => {
12
- const requireObject = () => {
13
- const input = args[0];
14
- if (Array.isArray(input))
15
- return input;
16
- if (Values.isValue(input))
17
- return {};
18
- if (input instanceof Values.Promise) {
19
- throw new InterpreterRuntimeError(`Object.${name} received an un-awaited Promise; await it before inspecting the result.`, node, "InvalidDataValue");
20
- }
21
- if (input === null || typeof input !== "object") {
22
- throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
23
- }
24
- const prototype = Object.getPrototypeOf(input);
25
- if (prototype !== null && prototype !== Object.prototype) {
26
- throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
27
- }
11
+ const requireObject = (name, input, node) => {
12
+ if (Array.isArray(input))
28
13
  return input;
14
+ if (Values.isValue(input))
15
+ return {};
16
+ if (input instanceof Values.Promise) {
17
+ throw new InterpreterRuntimeError(`Object.${name} received an un-awaited Promise; await it before inspecting the result.`, node, "InvalidDataValue");
18
+ }
19
+ if (input === null || typeof input !== "object") {
20
+ throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
21
+ }
22
+ const prototype = Object.getPrototypeOf(input);
23
+ if (prototype !== null && prototype !== Object.prototype) {
24
+ throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue");
25
+ }
26
+ return input;
27
+ };
28
+ export const objectAssign = (args, node) => {
29
+ const target = args[0];
30
+ if (target === null || typeof target !== "object" || Array.isArray(target) || Values.isValue(target)) {
31
+ throw new InterpreterRuntimeError("Object.assign expects a data object target.", node);
32
+ }
33
+ const out = target;
34
+ const seen = new Set();
35
+ const guardedSet = (key, item) => {
36
+ if (typeof key === "string" && isBlockedMember(key))
37
+ throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node);
38
+ rejectCircularInsertion(out, item, "Object.assign result", node, seen);
39
+ if (!Reflect.set(out, key, item))
40
+ throw new InterpreterRuntimeError(`Object.assign could not assign property '${String(key)}'.`, node).as("TypeError");
29
41
  };
30
- switch (name) {
31
- case "keys":
32
- return Object.keys(requireObject());
33
- case "values":
34
- return Object.values(requireObject());
35
- case "entries":
36
- return Object.entries(requireObject()).map(([key, item]) => [key, item]);
37
- case "hasOwn":
38
- return Object.hasOwn(requireObject(), args[1] === AsyncIteratorSymbol || args[1] === IteratorSymbol ? args[1] : String(args[1]));
39
- case "is":
40
- if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
41
- throw new InterpreterRuntimeError("Object.is requires data values.", node, "InvalidDataValue");
42
- }
43
- return Object.is(args[0], args[1]);
44
- case "assign": {
45
- const target = args[0];
46
- if (target === null || typeof target !== "object" || Array.isArray(target) || Values.isValue(target)) {
47
- throw new InterpreterRuntimeError("Object.assign expects a data object target.", node);
48
- }
49
- const out = target;
50
- const seen = new Set();
51
- const guardedSet = (key, item) => {
52
- if (typeof key === "string" && isBlockedMember(key))
53
- throw new InterpreterRuntimeError(`Property '${key}' is not available.`, node);
54
- rejectCircularInsertion(out, item, "Object.assign result", node, seen);
55
- if (!Reflect.set(out, key, item))
56
- throw new InterpreterRuntimeError(`Object.assign could not assign property '${String(key)}'.`, node).as("TypeError");
57
- };
58
- for (const source of args.slice(1)) {
59
- if (source === null || source === undefined || Values.isValue(source))
60
- continue;
61
- if (typeof source !== "object" || Array.isArray(source)) {
62
- throw new InterpreterRuntimeError("Object.assign expects data objects.", node);
63
- }
64
- for (const key of Reflect.ownKeys(source)) {
65
- if (typeof key === "string") {
66
- if (Object.prototype.propertyIsEnumerable.call(source, key))
67
- guardedSet(key, Reflect.get(source, key));
68
- continue;
69
- }
70
- if (key !== AsyncIteratorSymbol && key !== IteratorSymbol)
71
- continue;
72
- if (!Object.prototype.propertyIsEnumerable.call(source, key))
73
- continue;
42
+ for (const source of args.slice(1)) {
43
+ if (source === null || source === undefined || Values.isValue(source))
44
+ continue;
45
+ if (typeof source !== "object" || Array.isArray(source)) {
46
+ throw new InterpreterRuntimeError("Object.assign expects data objects.", node);
47
+ }
48
+ for (const key of Reflect.ownKeys(source)) {
49
+ if (typeof key === "string") {
50
+ if (Object.prototype.propertyIsEnumerable.call(source, key))
74
51
  guardedSet(key, Reflect.get(source, key));
75
- }
52
+ continue;
76
53
  }
77
- return out;
54
+ if (key !== AsyncIteratorSymbol && key !== IteratorSymbol)
55
+ continue;
56
+ if (!Object.prototype.propertyIsEnumerable.call(source, key))
57
+ continue;
58
+ guardedSet(key, Reflect.get(source, key));
78
59
  }
79
60
  }
80
- throw new InterpreterRuntimeError(`Object.${name} is not available.`, node);
61
+ return out;
81
62
  };
82
- export const invokeObjectFromEntries = (runner, source, node) => {
63
+ const objectFromEntries = (runner, source, node) => {
83
64
  const out = Object.create(null);
84
65
  return Effect.gen(function* () {
85
66
  const cursor = yield* runner.syncIterator(source, node);
@@ -108,3 +89,52 @@ export const invokeObjectFromEntries = (runner, source, node) => {
108
89
  }
109
90
  });
110
91
  };
92
+ const constructObject = (args, node) => {
93
+ const first = args[0];
94
+ if (first === null || first === undefined)
95
+ return {};
96
+ if (typeof first === "object")
97
+ return first;
98
+ throw new InterpreterRuntimeError(`Object(${typeof first}) wrapper objects are not supported; use the primitive value directly.`, node);
99
+ };
100
+ // Tool references are not data; only Object.keys(tools) reads them, for tool names.
101
+ const rejectTools = (name, args, node) => {
102
+ if (!(args[0] instanceof ToolReference))
103
+ return;
104
+ throw new InterpreterRuntimeError(`Object.${name}(...) cannot read tool references: they are not plain data. Use Object.keys(tools) for names, or search({ query }) for signatures.`, node, "InvalidDataValue");
105
+ };
106
+ const objectStatic = (name, impl) => sync(`Object.${name}`, (args, node) => {
107
+ rejectTools(name, args, node);
108
+ return impl(args, node);
109
+ });
110
+ // Object constructs identically with or without new, like JS. Only `keys` copies its result into the
111
+ // program; `values`, `entries`, `assign`, and `fromEntries` hand back the program's own values.
112
+ export const objectGlobal = (runner, toolKeys) => new HostFunction({
113
+ name: "Object",
114
+ call: syncCall(constructObject),
115
+ construct: syncCall(constructObject),
116
+ instanceOf: (value) => value !== null && (typeof value === "object" || typeofValue(value) === "function"),
117
+ members: {
118
+ keys: sync("Object.keys", (args, node) => toProgram(args[0] instanceof ToolReference
119
+ ? [...toolKeys(args[0].path)]
120
+ : Object.keys(requireObject("keys", args[0], node)), "Object.keys result")),
121
+ values: objectStatic("values", (args, node) => Object.values(requireObject("values", args[0], node))),
122
+ entries: objectStatic("entries", (args, node) => Object.entries(requireObject("entries", args[0], node)).map(([key, item]) => [key, item])),
123
+ hasOwn: objectStatic("hasOwn", (args, node) => Object.hasOwn(requireObject("hasOwn", args[0], node), args[1] === AsyncIteratorSymbol || args[1] === IteratorSymbol ? args[1] : String(args[1]))),
124
+ is: objectStatic("is", (args, node) => {
125
+ if (containsOpaqueReference(args[0]) || containsOpaqueReference(args[1])) {
126
+ throw new InterpreterRuntimeError("Object.is requires data values.", node, "InvalidDataValue");
127
+ }
128
+ return Object.is(args[0], args[1]);
129
+ }),
130
+ assign: objectStatic("assign", objectAssign),
131
+ fromEntries: new HostFunction({
132
+ name: "Object.fromEntries",
133
+ call: (args, node) => Effect.suspend(() => {
134
+ rejectTools("fromEntries", args, node);
135
+ return objectFromEntries(runner, args[0], node);
136
+ }),
137
+ }),
138
+ groupBy: groupBy(runner, "Object"),
139
+ },
140
+ });
@@ -1,11 +1,9 @@
1
1
  import { type AstNode } from "../interpreter/model.js";
2
2
  import { Values } from "../values.js";
3
3
  export declare const regexpMethods: Set<string>;
4
- export declare const regexpStatics: Set<string>;
5
4
  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
5
  export declare const toHostRegex: (arg: unknown, method: string, node: AstNode, extraFlags?: string) => RegExp;
9
6
  export declare const matchToValue: (match: RegExpMatchArray) => Array<unknown>;
10
- export declare const invokeRegExpStatic: (name: string, args: Array<unknown>, node: AstNode) => string;
7
+ export declare const constructRegExp: (args: Array<unknown>, node: AstNode) => Values.RegExp;
8
+ export declare const regexpGlobal: import("../interpreter/host.js").HostFunction<never>;
11
9
  export declare const invokeRegExpMethod: (value: Values.RegExp, name: string, args: Array<unknown>, node: AstNode) => unknown;
@@ -1,9 +1,9 @@
1
+ import { sync, syncCall } from "../interpreter/host.js";
1
2
  import { InterpreterRuntimeError } from "../interpreter/model.js";
2
3
  import { isBlockedMember } from "../data.js";
3
4
  import { Values } from "../values.js";
4
5
  import { coerceToNumber, coerceToString } from "./value.js";
5
6
  export const regexpMethods = new Set(["test", "exec", "toString"]);
6
- export const regexpStatics = new Set(["escape"]);
7
7
  export const regexpProperties = new Set([
8
8
  "source",
9
9
  "flags",
@@ -17,8 +17,8 @@ export const regexpProperties = new Set([
17
17
  "unicodeSets",
18
18
  "dotAll",
19
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.';
20
+ const regexFailureReason = (error) => (error instanceof Error ? error.message : String(error)).replace(/^Invalid regular expression:\s*/i, "");
21
+ const escapeRegexHint = 'To match special characters like ( ) [ ] { } + * ? . literally, escape them with a backslash (e.g. "\\\\(") or test for them with String.includes instead.';
22
22
  export const toHostRegex = (arg, method, node, extraFlags = "") => {
23
23
  // Native parity: an undefined pattern behaves as an empty pattern.
24
24
  if (arg === undefined)
@@ -51,14 +51,37 @@ export const matchToValue = (match) => {
51
51
  result.indices = indicesToValue(match.indices);
52
52
  return result;
53
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");
54
+ export const constructRegExp = (args, node) => {
55
+ const first = args[0];
56
+ const pattern = first instanceof Values.RegExp ? first.regex.source : first === undefined ? "" : coerceToString(first);
57
+ const flagsArg = args[1];
58
+ if (flagsArg !== undefined && typeof flagsArg !== "string") {
59
+ throw new InterpreterRuntimeError(`RegExp flags must be a string of flag characters (e.g. "g", "gi"), not ${flagsArg === null ? "null" : typeof flagsArg}.`, node).as("SyntaxError");
60
+ }
61
+ const flags = flagsArg ?? (first instanceof Values.RegExp ? first.regex.flags : "");
62
+ try {
63
+ return new Values.RegExp(pattern, flags);
64
+ }
65
+ catch (error) {
66
+ const reason = regexFailureReason(error);
67
+ throw new InterpreterRuntimeError(/flag/i.test(reason)
68
+ ? `new RegExp(...) received invalid flags ${JSON.stringify(flags)} (${reason}). Valid flags are d, g, i, m, s, u, v, and y.`
69
+ : `new RegExp(...) received ${JSON.stringify(pattern)}, which is not a valid regular expression pattern (${reason}). ${escapeRegexHint}`, node).as("SyntaxError");
59
70
  }
60
- return RegExp.escape(args[0]);
61
71
  };
72
+ // RegExp constructs identically with or without new, like JS.
73
+ export const regexpGlobal = sync("RegExp", constructRegExp, {
74
+ construct: syncCall(constructRegExp),
75
+ instanceOf: (value) => value instanceof Values.RegExp,
76
+ members: {
77
+ escape: sync("RegExp.escape", (args, node) => {
78
+ if (typeof args[0] !== "string") {
79
+ throw new InterpreterRuntimeError("RegExp.escape expects a string.", node).as("TypeError");
80
+ }
81
+ return RegExp.escape(args[0]);
82
+ }),
83
+ },
84
+ });
62
85
  export const invokeRegExpMethod = (value, name, args, node) => {
63
86
  switch (name) {
64
87
  case "test":
@@ -1,4 +1,2 @@
1
- import { type AstNode } from "../interpreter/model.js";
2
1
  export declare const stringMethods: Set<string>;
3
- export declare const stringStatics: Set<string>;
4
- export declare const invokeStringStatic: (name: string, args: Array<unknown>, node: AstNode) => unknown;
2
+ export declare const stringGlobal: import("../interpreter/host.js").HostFunction<never>;
@@ -1,4 +1,6 @@
1
+ import { sync } from "../interpreter/host.js";
1
2
  import { InterpreterRuntimeError } from "../interpreter/model.js";
3
+ import { coercion } from "./value.js";
2
4
  export const stringMethods = new Set([
3
5
  "toLowerCase",
4
6
  "toUpperCase",
@@ -30,19 +32,15 @@ export const stringMethods = new Set([
30
32
  "localeCompare",
31
33
  "normalize",
32
34
  ]);
33
- export const stringStatics = new Set(["fromCharCode", "fromCodePoint"]);
34
- export const invokeStringStatic = (name, args, node) => {
35
- const codes = args.map((arg) => {
36
- if (typeof arg !== "number")
37
- throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node);
38
- return arg;
39
- });
40
- switch (name) {
41
- case "fromCharCode":
42
- return String.fromCharCode(...codes);
43
- case "fromCodePoint":
44
- return String.fromCodePoint(...codes);
45
- default:
46
- throw new InterpreterRuntimeError(`String.${name} is not available.`, node);
47
- }
48
- };
35
+ const codeUnits = (name, op) => sync(`String.${name}`, (args, node) => op(...args.map((arg) => {
36
+ if (typeof arg !== "number")
37
+ throw new InterpreterRuntimeError(`String.${name} expects number arguments.`, node);
38
+ return arg;
39
+ })));
40
+ export const stringGlobal = coercion("String", {
41
+ instanceOf: () => false,
42
+ members: {
43
+ fromCharCode: codeUnits("fromCharCode", String.fromCharCode),
44
+ fromCodePoint: codeUnits("fromCodePoint", String.fromCodePoint),
45
+ },
46
+ });
@@ -1,12 +1,16 @@
1
- import { type AstNode, UriFunction } from "../interpreter/model.js";
1
+ import { HostFunction } from "../interpreter/host.js";
2
+ import { type AstNode } from "../interpreter/model.js";
3
+ import { type Runner } from "../interpreter/runner.js";
2
4
  import { Values } from "../values.js";
3
5
  export declare const urlProperties: Set<string>;
4
6
  export declare const urlWritableProperties: Set<string>;
5
7
  export declare const urlMethods: Set<string>;
6
- export declare const urlStatics: Set<string>;
7
8
  export declare const urlSearchParamsMethods: Set<string>;
8
9
  export declare const uriArgument: (value: unknown, label: string) => string;
9
- export declare const invokeUriFunction: (ref: UriFunction, args: Array<unknown>, node: AstNode) => string;
10
+ type UriFunction = "encodeURI" | "encodeURIComponent" | "decodeURI" | "decodeURIComponent";
11
+ export declare const uriGlobal: (name: UriFunction) => HostFunction<never>;
10
12
  export declare const urlArgument: (value: unknown, label: string) => string;
11
- export declare const invokeURLStatic: (name: string, args: Array<unknown>, node: AstNode) => unknown;
13
+ export declare const urlGlobal: HostFunction<never>;
14
+ export declare const urlSearchParamsGlobal: <R>(runner: Runner<R>) => HostFunction<R>;
12
15
  export declare const invokeURLMethod: (value: Values.URL, name: string, node: AstNode) => string;
16
+ export {};
@@ -1,6 +1,10 @@
1
- import { InterpreterRuntimeError, UriFunction } from "../interpreter/model.js";
2
- import { Values } from "../values.js";
1
+ import { Effect } from "effect";
3
2
  import { toProgram } from "../data.js";
3
+ import { HostFunction, requiresNew, sync, syncCall } from "../interpreter/host.js";
4
+ import { InterpreterRuntimeError } from "../interpreter/model.js";
5
+ import { isRuntimeReference } from "../interpreter/references.js";
6
+ import { preserveConsumerError } from "../interpreter/runner.js";
7
+ import { Values } from "../values.js";
4
8
  import { coerceToString } from "./value.js";
5
9
  export const urlProperties = new Set([
6
10
  "href",
@@ -28,7 +32,6 @@ export const urlWritableProperties = new Set([
28
32
  "hash",
29
33
  ]);
30
34
  export const urlMethods = new Set(["toString", "toJSON"]);
31
- export const urlStatics = new Set(["canParse", "parse"]);
32
35
  export const urlSearchParamsMethods = new Set([
33
36
  "append",
34
37
  "delete",
@@ -44,30 +47,26 @@ export const urlSearchParamsMethods = new Set([
44
47
  "toString",
45
48
  ]);
46
49
  export const uriArgument = (value, label) => coerceToString(toProgram(value, label));
47
- export const invokeUriFunction = (ref, args, node) => {
48
- const value = uriArgument(args[0], `${ref.name} input`);
50
+ const uriFunctions = {
51
+ encodeURI,
52
+ encodeURIComponent,
53
+ decodeURI,
54
+ decodeURIComponent,
55
+ };
56
+ export const uriGlobal = (name) => sync(name, (args, node) => {
57
+ const value = uriArgument(args[0], `${name} input`);
49
58
  try {
50
- switch (ref.name) {
51
- case "encodeURI":
52
- return encodeURI(value);
53
- case "encodeURIComponent":
54
- return encodeURIComponent(value);
55
- case "decodeURI":
56
- return decodeURI(value);
57
- case "decodeURIComponent":
58
- return decodeURIComponent(value);
59
- }
59
+ return uriFunctions[name](value);
60
60
  }
61
61
  catch (error) {
62
- throw new InterpreterRuntimeError(`${ref.name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node).as("URIError");
62
+ throw new InterpreterRuntimeError(`${name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`, node).as("URIError");
63
63
  }
64
- };
64
+ });
65
65
  export const urlArgument = (value, label) => value instanceof Values.URL ? value.url.href : uriArgument(value, label);
66
- export const invokeURLStatic = (name, args, node) => {
67
- if (!urlStatics.has(name))
68
- throw new InterpreterRuntimeError(`URL.${name} is not available.`, node);
69
- if (args.length === 0)
66
+ const urlStatic = (name) => sync(`URL.${name}`, (args, node) => {
67
+ if (args.length === 0) {
70
68
  throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError");
69
+ }
71
70
  const input = urlArgument(args[0], `URL.${name} input`);
72
71
  const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`);
73
72
  try {
@@ -77,7 +76,84 @@ export const invokeURLStatic = (name, args, node) => {
77
76
  catch {
78
77
  return name === "canParse" ? false : null;
79
78
  }
79
+ });
80
+ const constructURL = (args, node) => {
81
+ if (args.length === 0) {
82
+ throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as("TypeError");
83
+ }
84
+ const input = urlArgument(args[0], "new URL input");
85
+ const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base");
86
+ try {
87
+ return new Values.URL(new URL(input, base));
88
+ }
89
+ catch {
90
+ throw new InterpreterRuntimeError(`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`, node).as("TypeError");
91
+ }
92
+ };
93
+ export const urlGlobal = new HostFunction({
94
+ name: "URL",
95
+ call: requiresNew("URL"),
96
+ construct: syncCall(constructURL),
97
+ instanceOf: (value) => value instanceof Values.URL,
98
+ members: { canParse: urlStatic("canParse"), parse: urlStatic("parse") },
99
+ });
100
+ const readURLSearchParamsPair = (runner, value, node) => Effect.gen(function* () {
101
+ const cursor = yield* runner.syncIterator(value, node);
102
+ if (cursor === undefined) {
103
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
104
+ }
105
+ const items = [];
106
+ while (true) {
107
+ const step = yield* cursor.next;
108
+ if (step.done)
109
+ return items;
110
+ items.push(yield* preserveConsumerError(cursor, Effect.sync(() => uriArgument(step.value, "URLSearchParams pair value"))));
111
+ }
112
+ });
113
+ const constructURLSearchParams = (runner, init, node) => {
114
+ if (init === undefined)
115
+ return Effect.succeed(new Values.URLSearchParams(new URLSearchParams()));
116
+ if (init instanceof Values.URLSearchParams) {
117
+ return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init.params)));
118
+ }
119
+ if (typeof init === "string")
120
+ return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(init)));
121
+ if (init === null || typeof init === "number" || typeof init === "boolean") {
122
+ return Effect.succeed(new Values.URLSearchParams(new URLSearchParams(coerceToString(init))));
123
+ }
124
+ return Effect.gen(function* () {
125
+ const cursor = yield* runner.syncIterator(init, node);
126
+ if (cursor !== undefined) {
127
+ const entries = [];
128
+ while (true) {
129
+ const step = yield* cursor.next;
130
+ if (step.done) {
131
+ if (entries.some((entry) => entry.length !== 2)) {
132
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects iterable [name, value] pairs.", node).as("TypeError");
133
+ }
134
+ return new Values.URLSearchParams(new URLSearchParams(entries.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
135
+ }
136
+ entries.push(yield* preserveConsumerError(cursor, readURLSearchParamsPair(runner, step.value, node)));
137
+ }
138
+ }
139
+ if (isRuntimeReference(init)) {
140
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.", node).as("TypeError");
141
+ }
142
+ if (Values.isValue(init))
143
+ return new Values.URLSearchParams(new URLSearchParams());
144
+ const data = toProgram(init, "new URLSearchParams input");
145
+ if (data === null || typeof data !== "object") {
146
+ throw new InterpreterRuntimeError("new URLSearchParams(...) expects a query string, data object, iterable pairs, or URLSearchParams.", node).as("TypeError");
147
+ }
148
+ return new Values.URLSearchParams(new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))));
149
+ });
80
150
  };
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
+ });
81
157
  export const invokeURLMethod = (value, name, node) => {
82
158
  if (name === "toString" || name === "toJSON")
83
159
  return value.url.href;
@@ -1,11 +1,13 @@
1
- import { type AstNode, CoercionFunction } from "../interpreter/model.js";
1
+ import { type HostFunction, type SyncOptions } from "../interpreter/host.js";
2
2
  import { type SafeObject } from "../data.js";
3
3
  export declare const errorConstructors: Set<string>;
4
- export declare const valueConstructors: Set<string>;
5
4
  export declare const compoundOperators: Set<string>;
6
5
  export declare const createErrorValue: (name: string, message: string) => SafeObject;
7
6
  export declare const createAggregateErrorValue: (errors: Array<unknown>, message: string) => SafeObject;
8
7
  export declare const errorBrandName: (value: unknown) => string | undefined;
9
8
  export declare const coerceToString: (value: unknown) => string;
10
9
  export declare const coerceToNumber: (value: unknown) => number;
11
- export declare const invokeCoercion: (ref: CoercionFunction, args: Array<unknown>, node: AstNode) => unknown;
10
+ type Coercion = "Number" | "String" | "Boolean" | "parseInt" | "parseFloat" | "isFinite" | "isNaN";
11
+ /** A global coercion function such as `Number` or `parseInt`. */
12
+ export declare const coercion: (name: Coercion, options?: SyncOptions) => HostFunction;
13
+ export {};
@@ -1,4 +1,5 @@
1
- import { CoercionFunction, InterpreterRuntimeError } from "../interpreter/model.js";
1
+ import { sync } from "../interpreter/host.js";
2
+ import { InterpreterRuntimeError } from "../interpreter/model.js";
2
3
  import { toProgram } from "../data.js";
3
4
  import { Values } from "../values.js";
4
5
  export const errorConstructors = new Set([
@@ -11,7 +12,6 @@ export const errorConstructors = new Set([
11
12
  "URIError",
12
13
  "AggregateError",
13
14
  ]);
14
- export const valueConstructors = new Set(["Date", "RegExp", "Map", "Set", "URL", "URLSearchParams"]);
15
15
  export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="]);
16
16
  const ErrorBrand = Symbol("codemode.error");
17
17
  export const createErrorValue = (name, message) => {
@@ -69,51 +69,53 @@ export const coerceToNumber = (value) => {
69
69
  return Number(coerceToString(value));
70
70
  return value !== null && typeof value === "object" ? Number.NaN : Number(value);
71
71
  };
72
- export const invokeCoercion = (ref, args, node) => {
72
+ const coerce = (name, args, node) => {
73
73
  // Native: Number() is 0 and String() is "", unlike their undefined-argument forms; the
74
74
  // other coercers match native through the undefined-argument path below.
75
75
  if (args.length === 0) {
76
- if (ref.name === "Number")
76
+ if (name === "Number")
77
77
  return 0;
78
- if (ref.name === "String")
78
+ if (name === "String")
79
79
  return "";
80
80
  }
81
81
  const raw = args[0];
82
82
  // Error values are plain SafeObjects; the toProgram path below would strip their brand.
83
- if (ref.name === "String" && errorBrandName(raw) !== undefined)
83
+ if (name === "String" && errorBrandName(raw) !== undefined)
84
84
  return coerceToString(raw);
85
85
  if (Values.isValue(raw)) {
86
- if (ref.name === "Boolean")
86
+ if (name === "Boolean")
87
87
  return true;
88
- if (ref.name === "Number")
88
+ if (name === "Number")
89
89
  return coerceToNumber(raw);
90
- if (ref.name === "String")
90
+ if (name === "String")
91
91
  return coerceToString(raw);
92
- if (ref.name === "isFinite")
92
+ if (name === "isFinite")
93
93
  return Number.isFinite(coerceToNumber(raw));
94
- if (ref.name === "isNaN")
94
+ if (name === "isNaN")
95
95
  return Number.isNaN(coerceToNumber(raw));
96
- if (ref.name === "parseInt")
96
+ if (name === "parseInt")
97
97
  return parseInt(coerceToString(raw));
98
98
  return parseFloat(coerceToString(raw));
99
99
  }
100
- const value = toProgram(raw, `${ref.name} input`);
101
- if (ref.name === "Number")
100
+ const value = toProgram(raw, `${name} input`);
101
+ if (name === "Number")
102
102
  return coerceToNumber(value);
103
- if (ref.name === "Boolean")
103
+ if (name === "Boolean")
104
104
  return Boolean(value);
105
- if (ref.name === "isFinite")
105
+ if (name === "isFinite")
106
106
  return Number.isFinite(coerceToNumber(value));
107
- if (ref.name === "isNaN")
107
+ if (name === "isNaN")
108
108
  return Number.isNaN(coerceToNumber(value));
109
- if (ref.name === "parseInt") {
109
+ if (name === "parseInt") {
110
110
  const radix = args[1];
111
111
  if (radix !== undefined && typeof radix !== "number") {
112
112
  throw new InterpreterRuntimeError("parseInt expects a numeric radix.", node);
113
113
  }
114
114
  return parseInt(coerceToString(value), radix);
115
115
  }
116
- if (ref.name === "parseFloat")
116
+ if (name === "parseFloat")
117
117
  return parseFloat(coerceToString(value));
118
118
  return coerceToString(value);
119
119
  };
120
+ /** A global coercion function such as `Number` or `parseInt`. */
121
+ export const coercion = (name, options = {}) => sync(name, (args, node) => toProgram(coerce(name, args, node), `${name} result`), options);
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-19366",
4
+ "version": "0.0.0-dev-19367",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -1,13 +0,0 @@
1
- import { Effect } from "effect";
2
- import type { AstNode } from "./model.js";
3
- export type IteratorCursor<R> = {
4
- readonly next: Effect.Effect<{
5
- readonly done: boolean;
6
- readonly value: unknown;
7
- }, unknown, R>;
8
- readonly close: Effect.Effect<void, unknown, R>;
9
- };
10
- export type SyncIteratorRunner<R> = {
11
- readonly syncIterator: (value: unknown, node: AstNode) => Effect.Effect<IteratorCursor<R> | undefined, unknown, R>;
12
- };
13
- export declare const preserveConsumerError: <A, R>(cursor: IteratorCursor<R>, effect: Effect.Effect<A, unknown, R>) => Effect.Effect<A, unknown, R>;
@@ -1,4 +0,0 @@
1
- import { Effect, Exit } from "effect";
2
- export const preserveConsumerError = (cursor, effect) => Effect.flatMap(Effect.exit(effect), (exit) => Exit.isSuccess(exit)
3
- ? Effect.succeed(exit.value)
4
- : Effect.andThen(Effect.exit(cursor.close), Effect.failCause(exit.cause)));
@@ -1,2 +0,0 @@
1
- import type { PromiseMethodName } from "../interpreter/model.js";
2
- export declare const promiseStatics: Set<PromiseMethodName>;
@@ -1 +0,0 @@
1
- export const promiseStatics = new Set(["all", "allSettled", "race", "any", "resolve", "reject"]);