@opencode/codemode 2.0.3 → 2.0.5
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 +5 -2
- package/dist/codemode.d.ts +7 -2
- package/dist/codemode.js +15 -2
- package/dist/data.d.ts +14 -21
- package/dist/data.js +72 -140
- package/dist/extension.d.ts +11 -0
- package/dist/extension.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interpreter/callback.d.ts +20 -0
- package/dist/interpreter/{runner.js → callback.js} +14 -14
- package/dist/interpreter/errors.d.ts +12 -5
- package/dist/interpreter/errors.js +66 -54
- package/dist/interpreter/execute.d.ts +2 -2
- package/dist/interpreter/execute.js +15 -15
- package/dist/interpreter/extensions.d.ts +8 -0
- package/dist/interpreter/extensions.js +160 -0
- package/dist/interpreter/generators.d.ts +2 -3
- package/dist/interpreter/generators.js +10 -10
- package/dist/interpreter/globals.d.ts +4 -12
- package/dist/interpreter/globals.js +56 -49
- package/dist/interpreter/interpreter.d.ts +34 -0
- package/dist/interpreter/{runtime.js → interpreter.js} +250 -215
- package/dist/interpreter/intrinsics.d.ts +5 -5
- package/dist/interpreter/intrinsics.js +16 -10
- package/dist/interpreter/limits.d.ts +10 -0
- package/dist/interpreter/limits.js +19 -0
- package/dist/interpreter/model.d.ts +27 -14
- package/dist/interpreter/model.js +22 -14
- package/dist/interpreter/native.d.ts +12 -13
- package/dist/interpreter/native.js +17 -16
- package/dist/interpreter/objects.d.ts +55 -48
- package/dist/interpreter/objects.js +64 -44
- package/dist/interpreter/promises.d.ts +13 -15
- package/dist/interpreter/promises.js +89 -85
- package/dist/interpreter/references.d.ts +1 -2
- package/dist/interpreter/references.js +18 -16
- package/dist/interpreter/scope.js +6 -6
- package/dist/openapi/types.d.ts +1 -1
- package/dist/stdlib/array.d.ts +3 -4
- package/dist/stdlib/array.js +115 -110
- package/dist/stdlib/bytes.d.ts +10 -0
- package/dist/stdlib/bytes.js +228 -0
- package/dist/stdlib/collections.d.ts +4 -4
- package/dist/stdlib/collections.js +89 -89
- package/dist/stdlib/console.d.ts +5 -3
- package/dist/stdlib/console.js +30 -37
- package/dist/stdlib/date.d.ts +2 -2
- package/dist/stdlib/date.js +35 -35
- package/dist/stdlib/json.d.ts +3 -3
- package/dist/stdlib/json.js +29 -67
- package/dist/stdlib/math.d.ts +3 -3
- package/dist/stdlib/math.js +17 -21
- package/dist/stdlib/number.d.ts +3 -3
- package/dist/stdlib/number.js +32 -36
- package/dist/stdlib/object.d.ts +5 -5
- package/dist/stdlib/object.js +59 -62
- package/dist/stdlib/regexp.d.ts +7 -8
- package/dist/stdlib/regexp.js +40 -48
- package/dist/stdlib/string.d.ts +2 -2
- package/dist/stdlib/string.js +110 -88
- package/dist/stdlib/url.d.ts +4 -6
- package/dist/stdlib/url.js +86 -90
- package/dist/stdlib/value.d.ts +3 -3
- package/dist/stdlib/value.js +26 -24
- package/dist/stdlib/web.d.ts +4 -4
- package/dist/stdlib/web.js +22 -8
- package/dist/tool-runtime.d.ts +38 -19
- package/dist/tool-runtime.js +70 -48
- package/package.json +1 -1
- package/dist/interpreter/runner.d.ts +0 -28
- package/dist/interpreter/runtime.d.ts +0 -19
package/README.md
CHANGED
|
@@ -88,8 +88,11 @@ runtime.catalog() // structured tool descriptions
|
|
|
88
88
|
runtime.execute(source) // Effect<CodeMode.Result, never, ToolServices>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
The Effect environment is inferred from the supplied tools. `
|
|
92
|
-
|
|
91
|
+
The Effect environment is inferred from the supplied tools. `hooks` surround every call the program makes into the
|
|
92
|
+
host: `tool.before`/`tool.after` receive `{ name, input }` with decoded input, `extension.before`/`extension.after`
|
|
93
|
+
receive `{ extension, name, args }`. An `after` hook also receives how the call ended (`success` with its value,
|
|
94
|
+
`failure` with its error, or `interrupted`). A failing `before` hook denies the call, and the program catches the
|
|
95
|
+
failure as a thrown error.
|
|
93
96
|
|
|
94
97
|
### `Values`
|
|
95
98
|
|
package/dist/codemode.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { Extension } from "./extension.js";
|
|
2
3
|
import { type Services, type ToolDescription, ToolRuntime } from "./tool-runtime.js";
|
|
3
4
|
import type { Tools } from "./tools.js";
|
|
4
5
|
/** A tool call admitted during an execution. */
|
|
5
|
-
export type {
|
|
6
|
+
export type { CallResult, ExtensionInvocation, Hooks, ToolCall, ToolDescription, ToolInvocation, } from "./tool-runtime.js";
|
|
6
7
|
/** Signature-construction helpers for host-owned catalog instructions. */
|
|
7
8
|
export { searchSignature, toolExpression } from "./tool-runtime.js";
|
|
8
9
|
/** Resource budgets enforced independently during each CodeMode program execution. */
|
|
@@ -26,9 +27,13 @@ export type ResolvedExecutionLimits = {
|
|
|
26
27
|
readonly maxOutputBytes: number | undefined;
|
|
27
28
|
};
|
|
28
29
|
/** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
|
|
29
|
-
export type Options<Provided extends Record<string, unknown> = {}> =
|
|
30
|
+
export type Options<Provided extends Record<string, unknown> = {}> = {
|
|
30
31
|
/** Explicit tools exposed to the program as `tools`. */
|
|
31
32
|
tools?: Provided & Tools<Services<Provided>>;
|
|
33
|
+
/** Hooks around every tool and extension call the program makes; see `Hooks`. */
|
|
34
|
+
hooks?: ToolRuntime.Hooks<Services<Provided>>;
|
|
35
|
+
/** Host functions exposed as globals; see `Extension.make`. */
|
|
36
|
+
extensions?: ReadonlyArray<Extension>;
|
|
32
37
|
/** Resource limits enforced on each execution. */
|
|
33
38
|
limits?: ExecutionLimits;
|
|
34
39
|
};
|
package/dist/codemode.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { executeProgram } from "./interpreter/execute.js";
|
|
3
|
+
import { extensionGlobals } from "./interpreter/extensions.js";
|
|
4
|
+
import { globalNames } from "./interpreter/globals.js";
|
|
3
5
|
import { ToolRuntime } from "./tool-runtime.js";
|
|
4
6
|
/** Signature-construction helpers for host-owned catalog instructions. */
|
|
5
7
|
export { searchSignature, toolExpression } from "./tool-runtime.js";
|
|
@@ -59,8 +61,19 @@ export const execute = (options) => make(options).execute(options.code);
|
|
|
59
61
|
export const make = (options = {}) => {
|
|
60
62
|
const prepared = ToolRuntime.prepare((options.tools ?? {}));
|
|
61
63
|
const limits = resolveExecutionLimits(options.limits);
|
|
64
|
+
const extensions = options.extensions ?? [];
|
|
65
|
+
const bound = new Set(globalNames);
|
|
66
|
+
for (const extension of extensions) {
|
|
67
|
+
for (const name of Object.keys(extension.globals)) {
|
|
68
|
+
if (bound.has(name))
|
|
69
|
+
throw new TypeError(`Extension "${extension.name}" global "${name}" is already defined.`);
|
|
70
|
+
bound.add(name);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
62
73
|
return {
|
|
63
|
-
catalog
|
|
64
|
-
|
|
74
|
+
get catalog() {
|
|
75
|
+
return prepared.catalog;
|
|
76
|
+
},
|
|
77
|
+
execute: (code) => executeProgram(code, prepared, limits, options.hooks ?? {}, (ctx) => extensionGlobals(ctx, extensions)),
|
|
65
78
|
};
|
|
66
79
|
};
|
package/dist/data.d.ts
CHANGED
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
export * as Data from "./data.js";
|
|
2
|
-
import type
|
|
3
|
-
import type {
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
readonly suggestions: ReadonlyArray<string>;
|
|
7
|
-
constructor(kind: Extract<DiagnosticKind, "UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded">, message: string, suggestions?: ReadonlyArray<string>);
|
|
8
|
-
}
|
|
2
|
+
import { Effect, type Schema } from "effect";
|
|
3
|
+
import type { Interpreter } from "./interpreter/interpreter.js";
|
|
4
|
+
export type Json = Schema.Json;
|
|
5
|
+
type Replacer<R> = (args: Array<unknown>) => Effect.Effect<unknown, unknown, R>;
|
|
9
6
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* What `JSON.stringify` would serialize for a program value, as host JSON: `toJSON` is honored, functions and
|
|
8
|
+
* `undefined` vanish, non-finite numbers become null, and everything else is copied. Two departures from JS
|
|
9
|
+
* so a mistake is not a silent `{}`: an Error serializes as `{ name, message, ...own }`, and a promise throws.
|
|
13
10
|
*/
|
|
14
|
-
export declare const
|
|
11
|
+
export declare const toJson: <R>(ctx: Interpreter<R>, value: unknown, replacer?: Replacer<R>) => Effect.Effect<Schema.Json | undefined, unknown, R>;
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* The host boundary: `toJson`, plus what a program most likely meant when a value cannot be JSON. A promise is
|
|
14
|
+
* awaited, a Set crosses as an array, a URLSearchParams as its query string, a Uint8Array asks to be encoded as
|
|
15
|
+
* text first, and a `__proto__` key is dropped so host code can never receive one.
|
|
18
16
|
*/
|
|
19
|
-
export declare const
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
* non-finite numbers become null, and array holes become null. `undefined` object properties are
|
|
23
|
-
* dropped ("json") or become null ("result", for program results where the consumer must never see
|
|
24
|
-
* undefined); a bare `undefined` follows the same rule.
|
|
25
|
-
*/
|
|
26
|
-
export declare const toData: (value: unknown, label: string, undefinedAs?: "json" | "result") => unknown;
|
|
17
|
+
export declare const toBoundary: <R>(ctx: Interpreter<R>, value: unknown) => Effect.Effect<Schema.Json | undefined, unknown, R>;
|
|
18
|
+
/** Host JSON as program values: objects and arrays are copied, primitives pass through. */
|
|
19
|
+
export declare const fromJson: <R>(ctx: Interpreter<R>, value: unknown) => unknown;
|
package/dist/data.js
CHANGED
|
@@ -1,156 +1,88 @@
|
|
|
1
1
|
export * as Data from "./data.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
constructor(kind, message, suggestions = []) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.kind = kind;
|
|
10
|
-
this.suggestions = suggestions;
|
|
11
|
-
this.name = "ToolRuntimeError";
|
|
12
|
-
}
|
|
13
|
-
}
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { MAX_VALUE_DEPTH } from "./interpreter/limits.js";
|
|
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";
|
|
6
|
+
import { typeofValue } from "./interpreter/references.js";
|
|
14
7
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
8
|
+
* What `JSON.stringify` would serialize for a program value, as host JSON: `toJSON` is honored, functions and
|
|
9
|
+
* `undefined` vanish, non-finite numbers become null, and everything else is copied. Two departures from JS
|
|
10
|
+
* so a mistake is not a silent `{}`: an Error serializes as `{ name, message, ...own }`, and a promise throws.
|
|
18
11
|
*/
|
|
19
|
-
export const
|
|
12
|
+
export const toJson = (ctx, value, replacer) => walk(ctx, value, replacer, false);
|
|
20
13
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
14
|
+
* The host boundary: `toJson`, plus what a program most likely meant when a value cannot be JSON. A promise is
|
|
15
|
+
* awaited, a Set crosses as an array, a URLSearchParams as its query string, a Uint8Array asks to be encoded as
|
|
16
|
+
* text first, and a `__proto__` key is dropped so host code can never receive one.
|
|
23
17
|
*/
|
|
24
|
-
export const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (typeof value !== "object") {
|
|
44
|
-
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
|
|
45
|
-
}
|
|
46
|
-
if (value instanceof ProgramPromise) {
|
|
47
|
-
throw new ToolRuntimeError("InvalidDataValue", `${label} contains an un-awaited Promise; await tool calls (e.g. \`const result = await tools.ns.tool(...)\`) before using their results.`);
|
|
48
|
-
}
|
|
49
|
-
if ((value instanceof Callable || value instanceof ProgramGenerator) && mode !== "program") {
|
|
50
|
-
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`);
|
|
51
|
-
}
|
|
52
|
-
if (protos !== undefined && mode === "program") {
|
|
53
|
-
if (value instanceof ProgramObject)
|
|
18
|
+
export const toBoundary = (ctx, value) => walk(ctx, value, undefined, true);
|
|
19
|
+
const walk = (ctx, value, replacer, boundary) => {
|
|
20
|
+
const stack = new Set();
|
|
21
|
+
const visit = (holder, key, depth) => Effect.gen(function* () {
|
|
22
|
+
if (depth > MAX_VALUE_DEPTH)
|
|
23
|
+
throw rangeError(`Value exceeds the maximum depth of ${MAX_VALUE_DEPTH}.`);
|
|
24
|
+
const raw = get(holder, key);
|
|
25
|
+
if (raw instanceof PromiseObj && !boundary) {
|
|
26
|
+
throw invalidData("JSON.stringify received an un-awaited Promise; await it first - e.g. `const result = await tools.ns.tool(...)`.");
|
|
27
|
+
}
|
|
28
|
+
const settled = raw instanceof PromiseObj ? yield* ctx.await(raw) : raw;
|
|
29
|
+
const toJSON = settled instanceof Obj ? get(settled, "toJSON") : undefined;
|
|
30
|
+
const own = toJSON instanceof Callable ? yield* ctx.call(toJSON, settled, [key]) : settled;
|
|
31
|
+
const value = replacer === undefined ? own : yield* replacer([key, own]);
|
|
32
|
+
if (value === undefined || typeofValue(value) === "function")
|
|
33
|
+
return undefined;
|
|
34
|
+
if (typeof value === "number")
|
|
35
|
+
return Number.isFinite(value) ? value : null;
|
|
36
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
54
37
|
return value;
|
|
55
|
-
if (value instanceof
|
|
56
|
-
return
|
|
57
|
-
if (value instanceof
|
|
58
|
-
|
|
59
|
-
if (value instanceof Map) {
|
|
60
|
-
const wrapped = new ProgramMap(protos.Map);
|
|
61
|
-
for (const [key, item] of value.entries())
|
|
62
|
-
wrapped.map.set(next(key), next(item));
|
|
63
|
-
return wrapped;
|
|
38
|
+
if (!(value instanceof Obj))
|
|
39
|
+
return {};
|
|
40
|
+
if (boundary && value instanceof Bytes) {
|
|
41
|
+
throw invalidData("A Uint8Array cannot cross to the host; pass text instead, e.g. `new TextDecoder().decode(bytes)` or `bytes.toBase64()`.");
|
|
64
42
|
}
|
|
65
|
-
if (value instanceof
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
if (boundary && value instanceof URLSearchParamsObj)
|
|
44
|
+
return value.params.toString();
|
|
45
|
+
const target = boundary && value instanceof SetObj ? new Arr(ctx.builtins.Array, [...value.set]) : value;
|
|
46
|
+
if (stack.has(target))
|
|
47
|
+
throw typeError("Converting circular structure to JSON.");
|
|
48
|
+
stack.add(target);
|
|
49
|
+
if (target instanceof Arr) {
|
|
50
|
+
const items = [];
|
|
51
|
+
for (let index = 0; index < target.items.length; index += 1) {
|
|
52
|
+
items.push((yield* visit(target, String(index), depth + 1)) ?? null);
|
|
53
|
+
}
|
|
54
|
+
stack.delete(target);
|
|
55
|
+
return items;
|
|
70
56
|
}
|
|
71
|
-
if (value instanceof URL)
|
|
72
|
-
return new ProgramURL(protos.URL, protos.URLSearchParams, new URL(value.href));
|
|
73
|
-
if (value instanceof URLSearchParams)
|
|
74
|
-
return new ProgramURLSearchParams(protos.URLSearchParams, new URLSearchParams(value));
|
|
75
|
-
}
|
|
76
|
-
if (value instanceof ProgramDate)
|
|
77
|
-
return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null;
|
|
78
|
-
if (value instanceof Date)
|
|
79
|
-
return Number.isFinite(value.getTime()) ? value.toISOString() : null;
|
|
80
|
-
if (value instanceof ProgramURL)
|
|
81
|
-
return value.url.href;
|
|
82
|
-
if (value instanceof URL)
|
|
83
|
-
return value.href;
|
|
84
|
-
// Remaining wrappers and their host counterparts serialize as empty objects, like JSON.stringify.
|
|
85
|
-
if (isWrapper(value) ||
|
|
86
|
-
value instanceof RegExp ||
|
|
87
|
-
value instanceof Map ||
|
|
88
|
-
value instanceof Set ||
|
|
89
|
-
value instanceof URLSearchParams) {
|
|
90
|
-
return protos !== undefined ? new ProgramObject(protos.Object) : {};
|
|
91
|
-
}
|
|
92
|
-
if (seen.has(value)) {
|
|
93
|
-
throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`);
|
|
94
|
-
}
|
|
95
|
-
seen.add(value);
|
|
96
|
-
if (value instanceof ProgramArray) {
|
|
97
|
-
const copied = Array.from(value.items, (item) => next(item) ?? null);
|
|
98
|
-
seen.delete(value);
|
|
99
|
-
return copied;
|
|
100
|
-
}
|
|
101
|
-
if (value instanceof ProgramObject) {
|
|
102
57
|
const copied = {};
|
|
58
|
+
// Own data property regardless of the key, so "__proto__" never reaches the Object.prototype setter.
|
|
59
|
+
const put = (name, item) => {
|
|
60
|
+
if (item !== undefined)
|
|
61
|
+
Object.defineProperty(copied, name, { value: item, enumerable: true, writable: true, configurable: true });
|
|
62
|
+
};
|
|
103
63
|
// Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS.
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
64
|
+
if (target instanceof ErrorObj) {
|
|
65
|
+
put("name", yield* visit(target, "name", depth + 1));
|
|
66
|
+
put("message", yield* visit(target, "message", depth + 1));
|
|
107
67
|
}
|
|
108
|
-
for (const
|
|
109
|
-
|
|
110
|
-
if (copiedItem === undefined && mode === "json")
|
|
68
|
+
for (const name of keys(target)) {
|
|
69
|
+
if (boundary && name === "__proto__")
|
|
111
70
|
continue;
|
|
112
|
-
|
|
71
|
+
put(name, yield* visit(target, name, depth + 1));
|
|
113
72
|
}
|
|
114
|
-
|
|
73
|
+
stack.delete(target);
|
|
115
74
|
return copied;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (protos !== undefined) {
|
|
119
|
-
const copied = new ProgramArray(protos.Array, value.map(next));
|
|
120
|
-
for (const [key, item] of Object.entries(value)) {
|
|
121
|
-
if (parseArrayIndex(key) === undefined)
|
|
122
|
-
define(copied, key, next(item));
|
|
123
|
-
}
|
|
124
|
-
seen.delete(value);
|
|
125
|
-
return copied;
|
|
126
|
-
}
|
|
127
|
-
const copied = Array.from(value, (item) => next(item) ?? null);
|
|
128
|
-
seen.delete(value);
|
|
129
|
-
return copied;
|
|
130
|
-
}
|
|
131
|
-
const prototype = Object.getPrototypeOf(value);
|
|
132
|
-
if (prototype !== Object.prototype && prototype !== null) {
|
|
133
|
-
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`);
|
|
134
|
-
}
|
|
135
|
-
if (protos !== undefined) {
|
|
136
|
-
const copied = new ProgramObject(protos.Object);
|
|
137
|
-
for (const [key, item] of Object.entries(value))
|
|
138
|
-
define(copied, key, next(item));
|
|
139
|
-
seen.delete(value);
|
|
140
|
-
return copied;
|
|
141
|
-
}
|
|
142
|
-
const copied = {};
|
|
143
|
-
for (const [key, item] of Object.entries(value)) {
|
|
144
|
-
const copiedItem = next(item);
|
|
145
|
-
if (copiedItem === undefined && mode === "json")
|
|
146
|
-
continue;
|
|
147
|
-
defineHost(copied, key, copiedItem);
|
|
148
|
-
}
|
|
149
|
-
seen.delete(value);
|
|
150
|
-
return copied;
|
|
75
|
+
});
|
|
76
|
+
return visit(record(ctx.builtins.Object, { "": value }), "", 0);
|
|
151
77
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
78
|
+
/** Host JSON as program values: objects and arrays are copied, primitives pass through. */
|
|
79
|
+
export const fromJson = (ctx, value) => {
|
|
80
|
+
if (value === null || typeof value !== "object")
|
|
81
|
+
return value;
|
|
82
|
+
if (Array.isArray(value))
|
|
83
|
+
return new Arr(ctx.builtins.Array, value.map((item) => fromJson(ctx, item)));
|
|
84
|
+
const copied = new Obj(ctx.builtins.Object);
|
|
85
|
+
for (const [key, item] of Object.entries(value))
|
|
86
|
+
define(copied, key, fromJson(ctx, item));
|
|
87
|
+
return copied;
|
|
156
88
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * as Extension from "./extension.js";
|
|
2
|
+
/**
|
|
3
|
+
* Host functions a program calls directly as globals. Values crossing in either direction are converted, never
|
|
4
|
+
* shared: arguments come in as copies, results go out as copies, and a function inside a result is callable the
|
|
5
|
+
* same way. Extension calls are not tool calls.
|
|
6
|
+
*/
|
|
7
|
+
export type Extension = {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly globals: Readonly<Record<string, Function>>;
|
|
10
|
+
};
|
|
11
|
+
export declare const make: (options: Extension) => Extension;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * as Extension from "./extension.js";
|
|
2
|
+
export const make = (options) => {
|
|
3
|
+
for (const [name, value] of Object.entries(options.globals)) {
|
|
4
|
+
if (typeof value !== "function") {
|
|
5
|
+
throw new TypeError(`Extension "${options.name}" global "${name}" must be a function.`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return { name: options.name, globals: { ...options.globals } };
|
|
9
|
+
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { Interpreter } from "./interpreter.js";
|
|
3
|
+
import { Callable } from "./objects.js";
|
|
4
|
+
export type IteratorCursor<R> = {
|
|
5
|
+
readonly next: Effect.Effect<{
|
|
6
|
+
readonly done: boolean;
|
|
7
|
+
readonly value: unknown;
|
|
8
|
+
}, unknown, R>;
|
|
9
|
+
readonly close: Effect.Effect<void, unknown, R>;
|
|
10
|
+
};
|
|
11
|
+
export declare const preserveConsumerError: <A, R>(cursor: IteratorCursor<R>, effect: Effect.Effect<A, unknown, R>) => Effect.Effect<A, unknown, R>;
|
|
12
|
+
/**
|
|
13
|
+
* ToPrimitive: calls `valueOf`/`toString` in hint order and returns the first primitive result. Dates treat the
|
|
14
|
+
* default hint as "string", like their `Symbol.toPrimitive`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const toPrimitive: <R>(ctx: Interpreter<R>, value: unknown, hint: "number" | "string" | "default") => Effect.Effect<unknown, unknown, R>;
|
|
17
|
+
export declare const toPrimitiveString: <R>(ctx: Interpreter<R>, value: unknown) => Effect.Effect<string, unknown, R>;
|
|
18
|
+
export declare const toPrimitiveNumber: <R>(ctx: Interpreter<R>, value: unknown) => Effect.Effect<number, unknown, R>;
|
|
19
|
+
export declare const isSupportedCallback: (value: unknown) => value is Callable;
|
|
20
|
+
export declare const applyCollectionCallback: <R>(ctx: Interpreter<R>, callback: unknown, name: string) => ((args: Array<unknown>) => Effect.Effect<unknown, unknown, R>);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Exit } from "effect";
|
|
2
2
|
import { coerceToNumber, coerceToString } from "../stdlib/value.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Callable, get,
|
|
3
|
+
import { typeError } from "./model.js";
|
|
4
|
+
import { Callable, get, Native, DateObj, Obj } from "./objects.js";
|
|
5
5
|
import { typeofValue } from "./references.js";
|
|
6
6
|
export const preserveConsumerError = (cursor, effect) => Effect.flatMap(Effect.exit(effect), (exit) => Exit.isSuccess(exit)
|
|
7
7
|
? Effect.succeed(exit.value)
|
|
@@ -10,36 +10,36 @@ export const preserveConsumerError = (cursor, effect) => Effect.flatMap(Effect.e
|
|
|
10
10
|
* ToPrimitive: calls `valueOf`/`toString` in hint order and returns the first primitive result. Dates treat the
|
|
11
11
|
* default hint as "string", like their `Symbol.toPrimitive`.
|
|
12
12
|
*/
|
|
13
|
-
export const toPrimitive = (
|
|
14
|
-
if (!(value instanceof
|
|
13
|
+
export const toPrimitive = (ctx, value, hint) => {
|
|
14
|
+
if (!(value instanceof Obj))
|
|
15
15
|
return Effect.succeed(value);
|
|
16
|
-
const asString = hint === "string" || (hint === "default" && value instanceof
|
|
16
|
+
const asString = hint === "string" || (hint === "default" && value instanceof DateObj);
|
|
17
17
|
const order = asString ? ["toString", "valueOf"] : ["valueOf", "toString"];
|
|
18
18
|
return Effect.gen(function* () {
|
|
19
19
|
for (const method of order) {
|
|
20
20
|
const callable = get(value, method);
|
|
21
21
|
if (!(callable instanceof Callable))
|
|
22
22
|
continue;
|
|
23
|
-
const result = yield*
|
|
23
|
+
const result = yield* ctx.call(callable, value, []);
|
|
24
24
|
if (result === null || (typeof result !== "object" && typeof result !== "function"))
|
|
25
25
|
return result;
|
|
26
26
|
}
|
|
27
|
-
throw
|
|
27
|
+
throw typeError("Cannot convert object to primitive value.");
|
|
28
28
|
});
|
|
29
29
|
};
|
|
30
|
-
export const toPrimitiveString = (
|
|
31
|
-
export const toPrimitiveNumber = (
|
|
30
|
+
export const toPrimitiveString = (ctx, value) => Effect.map(toPrimitive(ctx, value, "string"), coerceToString);
|
|
31
|
+
export const toPrimitiveNumber = (ctx, value) => Effect.map(toPrimitive(ctx, value, "number"), coerceToNumber);
|
|
32
32
|
// The single acceptance list for callbacks: collections, sort, string replacers,
|
|
33
33
|
// Array.from mappers, and promise reactions all admit exactly these callables.
|
|
34
34
|
// Admission means dispatchable, not necessarily invocable: new-requiring
|
|
35
35
|
// constructors pass the gate and throw a TypeError on call, like JS.
|
|
36
|
-
export const isSupportedCallback = (value) => value instanceof Callable && !(value instanceof
|
|
37
|
-
export const applyCollectionCallback = (
|
|
36
|
+
export const isSupportedCallback = (value) => value instanceof Callable && !(value instanceof Native && !value.callback);
|
|
37
|
+
export const applyCollectionCallback = (ctx, callback, name) => {
|
|
38
38
|
if (!isSupportedCallback(callback)) {
|
|
39
39
|
if (typeofValue(callback) === "function") {
|
|
40
|
-
throw
|
|
40
|
+
throw typeError(`${name} cannot use this callable as a callback; wrap it in an arrow function, e.g. (value) => tools.ns.tool(value).`);
|
|
41
41
|
}
|
|
42
|
-
throw
|
|
42
|
+
throw typeError(`${name} expects a function callback.`);
|
|
43
43
|
}
|
|
44
|
-
return (callbackArgs) =>
|
|
44
|
+
return (callbackArgs) => ctx.call(callback, undefined, callbackArgs);
|
|
45
45
|
};
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { Diagnostic } from "../codemode.js";
|
|
2
|
+
import { type AstNode } from "./model.js";
|
|
2
3
|
import { type ErrorType } from "./intrinsics.js";
|
|
3
|
-
import { type
|
|
4
|
-
import {
|
|
4
|
+
import { type Native, ErrorObj, Obj } from "./objects.js";
|
|
5
|
+
import type { Interpreter } from "./interpreter.js";
|
|
5
6
|
export declare const normalizeError: (error: unknown) => Diagnostic;
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Gives a failure the source location of the expression that raised it, keeping the first one attached. Host errors
|
|
9
|
+
* that escape a built-in become the equivalent program error here.
|
|
10
|
+
*/
|
|
11
|
+
export declare const locate: (error: unknown, node?: AstNode) => unknown;
|
|
12
|
+
/** The program value a handler receives for a failure; one failure always yields the same value. */
|
|
13
|
+
export declare const materialize: <R>(ctx: Interpreter<R>, thrown: unknown) => unknown;
|
|
14
|
+
export declare const createAggregateErrorValue: <R>(ctx: Interpreter<R>, errors: Array<unknown>, message: string, proto?: Obj) => ErrorObj;
|
|
8
15
|
/** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
|
|
9
|
-
export declare const errorGlobal: <R>(type: ErrorType,
|
|
16
|
+
export declare const errorGlobal: <R>(type: ErrorType, ctx: Interpreter<R>) => Native<R>;
|