@opencode/codemode 0.0.0-dev-19862 → 0.0.0-dev-19868

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,8 +13,8 @@ The idea of code mode was originally introduced by Cloudflare. See
13
13
 
14
14
  ## How it differs from JavaScript
15
15
 
16
- - **Only supported APIs are available.** Programs can use the provided tools and supported JavaScript built-ins. APIs
17
- such as `fetch`, timers, `process`, filesystem access, imports, and modules are unavailable.
16
+ - **Only supported APIs are available.** Programs can use the provided tools, supported JavaScript built-ins, and the
17
+ globals the host adds through extensions. Timers, `process`, filesystem access, imports, and modules are unavailable.
18
18
  - **Unfinished work is interrupted.** Tool calls and async functions start when called. When the program finishes,
19
19
  anything still running is interrupted. Unhandled rejections from un-awaited promises are returned as warnings.
20
20
  - **REPL-style results.** Without an explicit `return`, the final top-level expression becomes the result. `undefined`
@@ -94,11 +94,19 @@ receive `{ extension, name, args }`. An `after` hook also receives how the call
94
94
  `failure` with its error, or `interrupted`). A failing `before` hook denies the call, and the program catches the
95
95
  failure as a thrown error.
96
96
 
97
- ### `Values`
97
+ ### `Extension.make`
98
98
 
99
- `Values` exports the runtime's non-JSON value classes: `Values.URL`, `Values.URLSearchParams`, `Values.Date`,
100
- `Values.RegExp`, `Values.Map`, `Values.Set`, and `Values.Promise`. The interpreter recognizes these by class; a
101
- program's `new URL(...)` is a `Values.URL` wrapping the host `URL`. `Values.isValue` narrows to the data-like kinds.
99
+ Extensions are host functions a program calls directly as globals, such as `fetch`. Unlike tools they are not in the
100
+ catalog, not counted against `maxToolCalls`, and not described to the model; the host decides what they mean.
101
+
102
+ ```ts
103
+ const web = Extension.make({ name: "web", globals: { fetch: (url: string) => globalThis.fetch(url) } })
104
+ const runtime = CodeMode.make({ tools, extensions: [web] })
105
+ ```
106
+
107
+ Every value crossing in either direction is converted, never shared: arguments come in as copies, results go out as
108
+ copies, and a function inside a result is callable the same way. A global that shadows a built-in or another
109
+ extension throws at `CodeMode.make`.
102
110
 
103
111
  ### OpenAPI tools
104
112
 
package/dist/data.js CHANGED
@@ -2,7 +2,7 @@ export * as Data from "./data.js";
2
2
  import { Effect } from "effect";
3
3
  import { MAX_VALUE_DEPTH } from "./interpreter/limits.js";
4
4
  import { invalidData, rangeError, typeError } from "./interpreter/model.js";
5
- import { Arr, Bytes, Callable, define, ErrorObj, get, keys, Obj, PromiseObj, record, SetObj, URLSearchParamsObj, } from "./interpreter/objects.js";
5
+ import { Arr, Bytes, Callable, define, ErrorObj, get, keys, Obj, PromiseObj, record, SetObj, URLSearchParamsObj, HeadersObj, } from "./interpreter/objects.js";
6
6
  import { typeofValue } from "./interpreter/references.js";
7
7
  /**
8
8
  * What `JSON.stringify` would serialize for a program value, as host JSON: `toJSON` is honored, functions and
@@ -42,6 +42,8 @@ const walk = (ctx, value, replacer, boundary) => {
42
42
  }
43
43
  if (boundary && value instanceof URLSearchParamsObj)
44
44
  return value.params.toString();
45
+ if (value instanceof HeadersObj)
46
+ return Object.fromEntries(value.headers);
45
47
  const target = boundary && value instanceof SetObj ? new Arr(ctx.builtins.Array, [...value.set]) : value;
46
48
  if (stack.has(target))
47
49
  throw typeError("Converting circular structure to JSON.");
@@ -5,7 +5,7 @@ import { createErrorValue, isErrorType } from "./intrinsics.js";
5
5
  import { MAX_VALUE_DEPTH } from "./limits.js";
6
6
  import { PendingThrow, Throw, typeError } from "./model.js";
7
7
  import { fn } from "./native.js";
8
- import { Callable, define, entries, get, has, hidden, keys, Arr, Bytes, DateObj, ErrorObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
8
+ import { Callable, define, entries, get, has, hidden, keys, Arr, Bytes, DateObj, ErrorObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, HeadersObj, } from "./objects.js";
9
9
  import { describeValue } from "./references.js";
10
10
  /**
11
11
  * The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data and
@@ -32,6 +32,8 @@ export const extensionGlobals = (ctx, extensions) => {
32
32
  return new URL(value.url.href);
33
33
  if (value instanceof URLSearchParamsObj)
34
34
  return new URLSearchParams(value.params);
35
+ if (value instanceof HeadersObj)
36
+ return new Headers(value.headers);
35
37
  const next = (item) => toHost(item, label, depth + 1, seen);
36
38
  if (value instanceof MapObj)
37
39
  return new Map([...value.map].map(([key, item]) => [next(key), next(item)]));
@@ -114,6 +116,8 @@ export const extensionGlobals = (ctx, extensions) => {
114
116
  if (value instanceof URLSearchParams) {
115
117
  return new URLSearchParamsObj(builtins.URLSearchParams, new URLSearchParams(value));
116
118
  }
119
+ if (value instanceof Headers)
120
+ return new HeadersObj(builtins.Headers, new Headers(value));
117
121
  if (value instanceof Map) {
118
122
  const wrapped = new MapObj(builtins.Map);
119
123
  for (const [key, item] of value)
@@ -11,6 +11,7 @@ import { objectGlobal } from "../stdlib/object.js";
11
11
  import { regexpGlobal } from "../stdlib/regexp.js";
12
12
  import { stringGlobal } from "../stdlib/string.js";
13
13
  import { uriGlobal, urlGlobal, urlSearchParamsGlobal } from "../stdlib/url.js";
14
+ import { headersGlobal } from "../stdlib/headers.js";
14
15
  import { coercion } from "../stdlib/value.js";
15
16
  import { base64Global, cryptoGlobal } from "../stdlib/web.js";
16
17
  import { ToolReference } from "../tool-runtime.js";
@@ -71,6 +72,7 @@ const table = {
71
72
  Set: (ctx) => setGlobal(ctx),
72
73
  URL: (ctx) => urlGlobal(ctx),
73
74
  URLSearchParams: (ctx) => urlSearchParamsGlobal(ctx),
75
+ Headers: (ctx) => headersGlobal(ctx),
74
76
  Uint8Array: (ctx) => uint8ArrayGlobal(ctx),
75
77
  TextEncoder: (ctx) => textEncoderGlobal(ctx),
76
78
  TextDecoder: (ctx) => textDecoderGlobal(ctx),
@@ -23,6 +23,12 @@ export declare class Interpreter<R> {
23
23
  call(callable: unknown, thisValue: unknown, args: Array<unknown>): Effect.Effect<unknown, unknown, R>;
24
24
  await(promise: PromiseObj): Effect.Effect<unknown, unknown, never>;
25
25
  iterate(value: unknown): Effect.Effect<{
26
+ next: Effect.Effect<{
27
+ done: boolean;
28
+ value: any;
29
+ }, never, never>;
30
+ close: Effect.Effect<void, never, never>;
31
+ }, never, never> | Effect.Effect<{
26
32
  next: Effect.Effect<{
27
33
  done: boolean;
28
34
  value: unknown;
@@ -5,7 +5,7 @@ import { AsyncIteratorSymbol, CallSite, GeneratorReturn, IteratorSymbol, Optiona
5
5
  import { checkStringLength } from "./limits.js";
6
6
  import { locate, materialize } from "./errors.js";
7
7
  import { globals } from "./globals.js";
8
- import { assign, Callable, define, get, has, hasPrototype, keys, Native, parseArrayIndex, Arr, Bytes, DateObj, Fn, GeneratorObj, MapObj, Obj, PromiseObj, SetObj, URLSearchParamsObj, record, remove, set, } from "./objects.js";
8
+ import { assign, Callable, define, get, has, hasPrototype, keys, Native, parseArrayIndex, Arr, Bytes, DateObj, Fn, GeneratorObj, MapObj, Obj, PromiseObj, SetObj, URLSearchParamsObj, HeadersObj, record, remove, set, } from "./objects.js";
9
9
  import { preserveConsumerError } from "./callback.js";
10
10
  import { Pending, resolvePromise, resolvePromiseValue } from "./promises.js";
11
11
  import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js";
@@ -480,7 +480,7 @@ class Frame {
480
480
  const iterator = yield* self.customIterator(right, node, awaiting);
481
481
  const cursor = iterator === undefined ? yield* self.iterate(right, node) : undefined;
482
482
  if (iterator === undefined && cursor === undefined) {
483
- throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`, node);
483
+ throw invalidData(`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, URLSearchParams, or Headers, or custom iterator value.`, node);
484
484
  }
485
485
  const close = () => iterator
486
486
  ? self.closeIterator(iterator, node, awaiting)
@@ -560,9 +560,11 @@ class Frame {
560
560
  ? value.set.values()
561
561
  : value instanceof URLSearchParamsObj
562
562
  ? value.params.entries()
563
- : value instanceof Bytes
564
- ? value.bytes.values()
565
- : undefined;
563
+ : value instanceof HeadersObj
564
+ ? value.headers.entries()
565
+ : value instanceof Bytes
566
+ ? value.bytes.values()
567
+ : undefined;
566
568
  if (iterator !== undefined) {
567
569
  const proto = this.ctx.builtins.Array;
568
570
  return Effect.succeed({
@@ -1513,6 +1515,7 @@ class Frame {
1513
1515
  value instanceof MapObj ||
1514
1516
  value instanceof SetObj ||
1515
1517
  value instanceof URLSearchParamsObj ||
1518
+ value instanceof HeadersObj ||
1516
1519
  value instanceof Bytes) {
1517
1520
  const cursor = yield* self.iterate(value, node);
1518
1521
  if (!cursor)
@@ -2,7 +2,7 @@ import { ErrorObj, Obj } from "./objects.js";
2
2
  export declare const errorTypes: readonly ["Error", "TypeError", "RangeError", "SyntaxError", "ReferenceError", "EvalError", "URIError", "AggregateError"];
3
3
  export type ErrorType = (typeof errorTypes)[number];
4
4
  export declare const isErrorType: (name: string) => name is ErrorType;
5
- declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "AsyncIterator", "Generator", "AsyncGenerator"];
5
+ declare const builtins: readonly ["Object", "Function", "Array", "String", "Number", "Boolean", "Date", "RegExp", "Map", "Set", "URL", "URLSearchParams", "Headers", "Uint8Array", "TextEncoder", "TextDecoder", "Promise", "Iterator", "AsyncIterator", "Generator", "AsyncGenerator"];
6
6
  /**
7
7
  * The built-in prototype objects of one runtime, allocated empty in dependency order. The globals populate them
8
8
  * and attach their constructors when the runtime is built.
@@ -24,6 +24,7 @@ const builtins = [
24
24
  "Set",
25
25
  "URL",
26
26
  "URLSearchParams",
27
+ "Headers",
27
28
  "Uint8Array",
28
29
  "TextEncoder",
29
30
  "TextDecoder",
@@ -68,6 +69,7 @@ export const createBuiltins = () => {
68
69
  Set: plain(),
69
70
  URL: plain(),
70
71
  URLSearchParams: plain(),
72
+ Headers: plain(),
71
73
  Uint8Array: plain(),
72
74
  TextEncoder: plain(),
73
75
  TextDecoder: plain(),
@@ -99,6 +99,10 @@ export declare class URLSearchParamsObj extends Obj {
99
99
  readonly params: URLSearchParams;
100
100
  constructor(proto: Obj, params: URLSearchParams);
101
101
  }
102
+ export declare class HeadersObj extends Obj {
103
+ readonly headers: Headers;
104
+ constructor(proto: Obj, headers: Headers);
105
+ }
102
106
  export declare class URLObj extends Obj {
103
107
  readonly url: URL;
104
108
  readonly searchParams: URLSearchParamsObj;
@@ -110,7 +114,7 @@ export declare class Bytes extends Obj {
110
114
  constructor(proto: Obj, bytes: Uint8Array);
111
115
  }
112
116
  /** Built-in objects that wrap a host value; data-like, but never plain data. */
113
- export declare const isWrapper: (value: unknown) => value is DateObj | RegExpObj | MapObj | SetObj | URLObj | URLSearchParamsObj | Bytes;
117
+ export declare const isWrapper: (value: unknown) => value is DateObj | RegExpObj | MapObj | SetObj | URLObj | URLSearchParamsObj | HeadersObj | Bytes;
114
118
  export declare const parseArrayIndex: (key: string | number) => number | undefined;
115
119
  /** The own property under `key`, including an array's live indexes and `length`. */
116
120
  export declare const own: (target: Obj, key: PropertyKey) => Slot | undefined;
@@ -105,6 +105,13 @@ export class URLSearchParamsObj extends Obj {
105
105
  this.params = params;
106
106
  }
107
107
  }
108
+ export class HeadersObj extends Obj {
109
+ headers;
110
+ constructor(proto, headers) {
111
+ super(proto);
112
+ this.headers = headers;
113
+ }
114
+ }
108
115
  export class URLObj extends Obj {
109
116
  url;
110
117
  searchParams;
@@ -129,6 +136,7 @@ export const isWrapper = (value) => value instanceof DateObj ||
129
136
  value instanceof SetObj ||
130
137
  value instanceof URLObj ||
131
138
  value instanceof URLSearchParamsObj ||
139
+ value instanceof HeadersObj ||
132
140
  value instanceof Bytes;
133
141
  const MAX_ARRAY_INDEX = 4_294_967_295;
134
142
  export const parseArrayIndex = (key) => {
@@ -1,6 +1,6 @@
1
1
  import { ToolReference } from "../tool-runtime.js";
2
2
  import { invalidData } from "./model.js";
3
- import { Callable, getOwn, isWrapper, ownKeys, Arr, Bytes, DateObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "./objects.js";
3
+ import { Callable, getOwn, isWrapper, ownKeys, Arr, Bytes, DateObj, GeneratorObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, HeadersObj, } from "./objects.js";
4
4
  /** Values that cannot cross the data boundary. */
5
5
  export const isRuntimeReference = (value) => value instanceof Callable ||
6
6
  value instanceof GeneratorObj ||
@@ -63,6 +63,8 @@ export const describeValue = (value) => {
63
63
  return "a URL";
64
64
  if (value instanceof URLSearchParamsObj)
65
65
  return "a URLSearchParams";
66
+ if (value instanceof HeadersObj)
67
+ return "a Headers";
66
68
  if (value instanceof Bytes)
67
69
  return "a Uint8Array";
68
70
  if (value instanceof GeneratorObj)
@@ -1,5 +1,5 @@
1
1
  import { methods } from "../interpreter/native.js";
2
- import { entries, get, Arr, Bytes, DateObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "../interpreter/objects.js";
2
+ import { entries, get, Arr, Bytes, DateObj, MapObj, Obj, PromiseObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, HeadersObj, } from "../interpreter/objects.js";
3
3
  import { containsOpaqueReference, isRuntimeReference } from "../interpreter/references.js";
4
4
  import { coerceToString } from "./value.js";
5
5
  const consoleMethods = ["log", "info", "debug", "warn", "error", "dir", "table"];
@@ -53,6 +53,8 @@ const formatConsoleValue = (value, seen, depth) => {
53
53
  return coerceToString(value);
54
54
  if (value instanceof URLSearchParamsObj)
55
55
  return coerceToString(value);
56
+ if (value instanceof HeadersObj)
57
+ return `Headers ${JSON.stringify(Object.fromEntries(value.headers))}`;
56
58
  if (value instanceof Bytes)
57
59
  return `Uint8Array(${value.bytes.length}) [${value.bytes.join(",")}]`;
58
60
  if (depth > MAX_CONSOLE_DEPTH)
@@ -0,0 +1,2 @@
1
+ import type { Interpreter } from "../interpreter/interpreter.js";
2
+ export declare const headersGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
@@ -0,0 +1,117 @@
1
+ import { Effect } from "effect";
2
+ import { constructor, methods, prototypeFrom, receiver, requiresNew } from "../interpreter/native.js";
3
+ import { typeError } from "../interpreter/model.js";
4
+ import { entries, Arr, HeadersObj, Obj } from "../interpreter/objects.js";
5
+ import { applyCollectionCallback } from "../interpreter/callback.js";
6
+ import { isRuntimeReference } from "../interpreter/references.js";
7
+ import { coerceToString } from "./value.js";
8
+ import { readPairs } from "./url.js";
9
+ // The host validates header names and values and throws its own TypeError; the program gets one of its own.
10
+ const attempt = (run) => {
11
+ try {
12
+ return run();
13
+ }
14
+ catch (error) {
15
+ throw typeError(error instanceof Error ? error.message : String(error));
16
+ }
17
+ };
18
+ const constructHeaders = (ctx, init, proto) => {
19
+ const wrap = (headers) => new HeadersObj(proto, headers);
20
+ if (init === undefined)
21
+ return Effect.succeed(wrap(new Headers()));
22
+ return Effect.gen(function* () {
23
+ const pairs = init instanceof Obj ? yield* readPairs(ctx, init, "new Headers(...)") : undefined;
24
+ if (pairs !== undefined)
25
+ return wrap(attempt(() => new Headers(pairs)));
26
+ if (!(init instanceof Obj) || isRuntimeReference(init)) {
27
+ throw typeError("new Headers(...) expects a record of names to values, iterable [name, value] pairs, or Headers.");
28
+ }
29
+ return wrap(attempt(() => new Headers(Object.fromEntries(entries(init).map(([key, value]) => [key, coerceToString(value)])))));
30
+ });
31
+ };
32
+ export const headersGlobal = (ctx) => {
33
+ const builtins = ctx.builtins;
34
+ const proto = builtins.Headers;
35
+ const headers = constructor(builtins, proto, {
36
+ name: "Headers",
37
+ call: requiresNew("Headers"),
38
+ construct: (args, newTarget) => constructHeaders(ctx, args[0], prototypeFrom(newTarget, proto)),
39
+ });
40
+ const self = (thisValue, name) => receiver(HeadersObj, thisValue, `Headers.prototype.${name}`);
41
+ const wrap = (items) => new Arr(builtins.Array, items);
42
+ const arg = (args, index) => coerceToString(args[index]);
43
+ const requireArgs = (name, args, count) => {
44
+ if (args.length < count)
45
+ throw typeError(`Headers.${name} requires ${count} argument${count === 1 ? "" : "s"}.`);
46
+ };
47
+ methods(builtins, proto, [
48
+ [
49
+ "append",
50
+ 2,
51
+ (thisValue, args) => {
52
+ requireArgs("append", args, 2);
53
+ const target = self(thisValue, "append").headers;
54
+ return attempt(() => target.append(arg(args, 0), arg(args, 1)));
55
+ },
56
+ ],
57
+ [
58
+ "delete",
59
+ 1,
60
+ (thisValue, args) => {
61
+ requireArgs("delete", args, 1);
62
+ const target = self(thisValue, "delete").headers;
63
+ return attempt(() => target.delete(arg(args, 0)));
64
+ },
65
+ ],
66
+ [
67
+ "get",
68
+ 1,
69
+ (thisValue, args) => {
70
+ requireArgs("get", args, 1);
71
+ const target = self(thisValue, "get").headers;
72
+ return attempt(() => target.get(arg(args, 0)));
73
+ },
74
+ ],
75
+ ["getSetCookie", 0, (thisValue) => wrap(self(thisValue, "getSetCookie").headers.getSetCookie())],
76
+ [
77
+ "has",
78
+ 1,
79
+ (thisValue, args) => {
80
+ requireArgs("has", args, 1);
81
+ const target = self(thisValue, "has").headers;
82
+ return attempt(() => target.has(arg(args, 0)));
83
+ },
84
+ ],
85
+ [
86
+ "set",
87
+ 2,
88
+ (thisValue, args) => {
89
+ requireArgs("set", args, 2);
90
+ const target = self(thisValue, "set").headers;
91
+ return attempt(() => target.set(arg(args, 0), arg(args, 1)));
92
+ },
93
+ ],
94
+ ["keys", 0, (thisValue) => wrap(Array.from(self(thisValue, "keys").headers.keys()))],
95
+ ["values", 0, (thisValue) => wrap(Array.from(self(thisValue, "values").headers.values()))],
96
+ [
97
+ "entries",
98
+ 0,
99
+ (thisValue) => wrap(Array.from(self(thisValue, "entries").headers.entries(), ([key, value]) => wrap([key, value]))),
100
+ ],
101
+ [
102
+ "forEach",
103
+ 1,
104
+ (thisValue, args) => {
105
+ requireArgs("forEach", args, 1);
106
+ const target = self(thisValue, "forEach");
107
+ const apply = applyCollectionCallback(ctx, args[0], "Headers.forEach");
108
+ return Effect.gen(function* () {
109
+ for (const [key, value] of Array.from(target.headers.entries()))
110
+ yield* apply([value, key, target]);
111
+ return undefined;
112
+ });
113
+ },
114
+ ],
115
+ ]);
116
+ return headers;
117
+ };
@@ -1,6 +1,12 @@
1
+ import { Effect } from "effect";
1
2
  import type { Interpreter } from "../interpreter/interpreter.js";
2
3
  type UriFunction = "encodeURI" | "encodeURIComponent" | "decodeURI" | "decodeURIComponent";
3
4
  export declare const uriGlobal: <R>(ctx: Interpreter<R>, name: UriFunction) => import("../interpreter/objects.js").Native<R>;
4
5
  export declare const urlGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
6
+ /**
7
+ * Reads a synchronous iterable of `[name, value]` pairs as strings; `undefined` when `init` is not iterable. As in
8
+ * WebIDL, the whole sequence is converted before any pair's length is checked.
9
+ */
10
+ export declare const readPairs: <R>(ctx: Interpreter<R>, init: unknown, label: string) => Effect.Effect<Array<[string, string]> | undefined, unknown, R>;
5
11
  export declare const urlSearchParamsGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
6
12
  export {};
@@ -98,11 +98,10 @@ export const urlGlobal = (ctx) => {
98
98
  ]);
99
99
  return url;
100
100
  };
101
- const readPair = (ctx, value) => Effect.gen(function* () {
101
+ const readPair = (ctx, value, label) => Effect.gen(function* () {
102
102
  const cursor = yield* ctx.iterate(value);
103
- if (cursor === undefined) {
104
- throw typeError("new URLSearchParams(...) expects iterable [name, value] pairs.");
105
- }
103
+ if (cursor === undefined)
104
+ throw typeError(`${label} expects iterable [name, value] pairs.`);
106
105
  const items = [];
107
106
  while (true) {
108
107
  const step = yield* cursor.next;
@@ -111,6 +110,25 @@ const readPair = (ctx, value) => Effect.gen(function* () {
111
110
  items.push(yield* preserveConsumerError(cursor, Effect.sync(() => coerceToString(step.value))));
112
111
  }
113
112
  });
113
+ /**
114
+ * Reads a synchronous iterable of `[name, value]` pairs as strings; `undefined` when `init` is not iterable. As in
115
+ * WebIDL, the whole sequence is converted before any pair's length is checked.
116
+ */
117
+ export const readPairs = (ctx, init, label) => Effect.gen(function* () {
118
+ const cursor = yield* ctx.iterate(init);
119
+ if (cursor === undefined)
120
+ return undefined;
121
+ const pairs = [];
122
+ while (true) {
123
+ const step = yield* cursor.next;
124
+ if (step.done) {
125
+ if (pairs.some((entry) => entry.length !== 2))
126
+ throw typeError(`${label} expects iterable [name, value] pairs.`);
127
+ return pairs;
128
+ }
129
+ pairs.push(yield* preserveConsumerError(cursor, readPair(ctx, step.value, label)));
130
+ }
131
+ });
114
132
  const constructURLSearchParams = (ctx, init, proto) => {
115
133
  const wrap = (params) => new URLSearchParamsObj(proto, params);
116
134
  if (init === undefined)
@@ -123,20 +141,9 @@ const constructURLSearchParams = (ctx, init, proto) => {
123
141
  return Effect.succeed(wrap(new URLSearchParams(coerceToString(init))));
124
142
  }
125
143
  return Effect.gen(function* () {
126
- const cursor = yield* ctx.iterate(init);
127
- if (cursor !== undefined) {
128
- const pairs = [];
129
- while (true) {
130
- const step = yield* cursor.next;
131
- if (step.done) {
132
- if (pairs.some((entry) => entry.length !== 2)) {
133
- throw typeError("new URLSearchParams(...) expects iterable [name, value] pairs.");
134
- }
135
- return wrap(new URLSearchParams(pairs.map((entry) => [entry[0] ?? "", entry[1] ?? ""])));
136
- }
137
- pairs.push(yield* preserveConsumerError(cursor, readPair(ctx, step.value)));
138
- }
139
- }
144
+ const pairs = yield* readPairs(ctx, init, "new URLSearchParams(...)");
145
+ if (pairs !== undefined)
146
+ return wrap(new URLSearchParams(pairs));
140
147
  if (isRuntimeReference(init)) {
141
148
  throw typeError("new URLSearchParams(...) expects a query string, data object, or synchronous iterable pairs.");
142
149
  }
@@ -1,6 +1,6 @@
1
1
  import { fn } from "../interpreter/native.js";
2
2
  import { typeError } from "../interpreter/model.js";
3
- import { get, isWrapper, Arr, Bytes, DateObj, ErrorObj, MapObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, } from "../interpreter/objects.js";
3
+ import { get, isWrapper, Arr, Bytes, DateObj, ErrorObj, MapObj, RegExpObj, SetObj, URLObj, URLSearchParamsObj, HeadersObj, } from "../interpreter/objects.js";
4
4
  export const compoundOperators = new Set(["+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", ">>>="]);
5
5
  /** The built-in string form of a value, without consulting program-defined `toString` methods. */
6
6
  export const coerceToString = (value) => {
@@ -20,6 +20,8 @@ export const coerceToString = (value) => {
20
20
  return value.url.href;
21
21
  if (value instanceof URLSearchParamsObj)
22
22
  return value.params.toString();
23
+ if (value instanceof HeadersObj)
24
+ return "[object Headers]";
23
25
  if (value instanceof Bytes)
24
26
  return value.bytes.join(",");
25
27
  if (value instanceof ErrorObj) {
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-19862",
4
+ "version": "0.0.0-dev-19868",
5
5
  "description": "Effect-native confined code execution over schema-described tools",
6
6
  "type": "module",
7
7
  "license": "MIT",