@opencode/codemode 0.0.0-reserved → 2.0.1

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 (86) hide show
  1. package/README.md +184 -2
  2. package/dist/codemode.d.ts +145 -0
  3. package/dist/codemode.js +66 -0
  4. package/dist/data.d.ts +25 -0
  5. package/dist/data.js +158 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +7 -0
  8. package/dist/interpreter/errors.d.ts +10 -0
  9. package/dist/interpreter/errors.js +112 -0
  10. package/dist/interpreter/execute.d.ts +5 -0
  11. package/dist/interpreter/execute.js +171 -0
  12. package/dist/interpreter/globals.d.ts +13 -0
  13. package/dist/interpreter/globals.js +64 -0
  14. package/dist/interpreter/host.d.ts +41 -0
  15. package/dist/interpreter/host.js +44 -0
  16. package/dist/interpreter/intrinsics.d.ts +10 -0
  17. package/dist/interpreter/intrinsics.js +41 -0
  18. package/dist/interpreter/methods.d.ts +4 -0
  19. package/dist/interpreter/methods.js +837 -0
  20. package/dist/interpreter/model.d.ts +89 -0
  21. package/dist/interpreter/model.js +90 -0
  22. package/dist/interpreter/objects.d.ts +37 -0
  23. package/dist/interpreter/objects.js +154 -0
  24. package/dist/interpreter/promises.d.ts +31 -0
  25. package/dist/interpreter/promises.js +270 -0
  26. package/dist/interpreter/references.d.ts +7 -0
  27. package/dist/interpreter/references.js +93 -0
  28. package/dist/interpreter/runner.d.ts +26 -0
  29. package/dist/interpreter/runner.js +45 -0
  30. package/dist/interpreter/runtime.d.ts +19 -0
  31. package/dist/interpreter/runtime.js +1942 -0
  32. package/dist/interpreter/scope.d.ts +15 -0
  33. package/dist/interpreter/scope.js +79 -0
  34. package/dist/interpreter/transpile.node.d.ts +5 -0
  35. package/dist/interpreter/transpile.node.js +19 -0
  36. package/dist/interpreter/transpile.workerd.d.ts +5 -0
  37. package/dist/interpreter/transpile.workerd.js +6 -0
  38. package/dist/namespace.d.ts +15 -0
  39. package/dist/namespace.js +7 -0
  40. package/dist/openapi/index.d.ts +7 -0
  41. package/dist/openapi/index.js +101 -0
  42. package/dist/openapi/runtime.d.ts +4 -0
  43. package/dist/openapi/runtime.js +283 -0
  44. package/dist/openapi/spec.d.ts +20 -0
  45. package/dist/openapi/spec.js +583 -0
  46. package/dist/openapi/types.d.ts +122 -0
  47. package/dist/openapi/types.js +2 -0
  48. package/dist/stdlib/array.d.ts +3 -0
  49. package/dist/stdlib/array.js +68 -0
  50. package/dist/stdlib/collections.d.ts +9 -0
  51. package/dist/stdlib/collections.js +173 -0
  52. package/dist/stdlib/console.d.ts +3 -0
  53. package/dist/stdlib/console.js +137 -0
  54. package/dist/stdlib/date.d.ts +8 -0
  55. package/dist/stdlib/date.js +208 -0
  56. package/dist/stdlib/json.d.ts +3 -0
  57. package/dist/stdlib/json.js +101 -0
  58. package/dist/stdlib/math.d.ts +8 -0
  59. package/dist/stdlib/math.js +89 -0
  60. package/dist/stdlib/number.d.ts +4 -0
  61. package/dist/stdlib/number.js +69 -0
  62. package/dist/stdlib/object.d.ts +7 -0
  63. package/dist/stdlib/object.js +106 -0
  64. package/dist/stdlib/regexp.d.ts +10 -0
  65. package/dist/stdlib/regexp.js +120 -0
  66. package/dist/stdlib/string.d.ts +2 -0
  67. package/dist/stdlib/string.js +51 -0
  68. package/dist/stdlib/url.d.ts +16 -0
  69. package/dist/stdlib/url.js +161 -0
  70. package/dist/stdlib/value.d.ts +8 -0
  71. package/dist/stdlib/value.js +98 -0
  72. package/dist/stdlib/web.d.ts +4 -0
  73. package/dist/stdlib/web.js +21 -0
  74. package/dist/tool-error.d.ts +11 -0
  75. package/dist/tool-error.js +9 -0
  76. package/dist/tool-runtime.d.ts +69 -0
  77. package/dist/tool-runtime.js +254 -0
  78. package/dist/tool-schema.d.ts +16 -0
  79. package/dist/tool-schema.js +263 -0
  80. package/dist/tool.d.ts +66 -0
  81. package/dist/tool.js +16 -0
  82. package/dist/tools.d.ts +5 -0
  83. package/dist/tools.js +1 -0
  84. package/dist/values.d.ts +37 -0
  85. package/dist/values.js +56 -0
  86. package/package.json +37 -6
@@ -0,0 +1,89 @@
1
+ import type { Node } from "acorn";
2
+ import type { ErrorType } from "./intrinsics.js";
3
+ import type { Effect } from "effect";
4
+ import type { DiagnosticKind } from "../codemode.js";
5
+ import type { ProgramObject } from "./objects.js";
6
+ import type { Values } from "../values.js";
7
+ /** Any parsed node; the interpreter narrows on `type` and reads `loc` for diagnostics. */
8
+ export type AstNode = Node;
9
+ export type Binding = {
10
+ mutable: boolean;
11
+ value: unknown;
12
+ initialized?: boolean;
13
+ };
14
+ export type StatementResult = {
15
+ kind: "none";
16
+ } | {
17
+ kind: "return";
18
+ value: unknown;
19
+ } | {
20
+ kind: "break";
21
+ label?: string;
22
+ } | {
23
+ kind: "continue";
24
+ label?: string;
25
+ };
26
+ export type MemberReference = {
27
+ target: ProgramObject | Values.RegExp | Values.URL;
28
+ key: PropertyKey;
29
+ };
30
+ export type GeneratorRequestKind = "next" | "return" | "throw";
31
+ export declare class CodeModeGenerator {
32
+ readonly asynchronous: boolean;
33
+ readonly request: (kind: GeneratorRequestKind, value: unknown, node: AstNode) => Effect.Effect<unknown, unknown, unknown>;
34
+ constructor(asynchronous: boolean, request: (kind: GeneratorRequestKind, value: unknown, node: AstNode) => Effect.Effect<unknown, unknown, unknown>);
35
+ }
36
+ export declare class GeneratorMethodReference {
37
+ readonly generator: CodeModeGenerator;
38
+ readonly kind: GeneratorRequestKind | "iterator";
39
+ constructor(generator: CodeModeGenerator, kind: GeneratorRequestKind | "iterator");
40
+ }
41
+ export declare class IntrinsicReference {
42
+ readonly receiver: unknown;
43
+ readonly name: string;
44
+ constructor(receiver: unknown, name: string);
45
+ }
46
+ export declare class ComputedValue {
47
+ readonly value: unknown;
48
+ constructor(value: unknown);
49
+ }
50
+ export declare const AsyncIteratorSymbol: unique symbol;
51
+ export declare const IteratorSymbol: unique symbol;
52
+ export declare const IteratorSymbols: readonly [typeof AsyncIteratorSymbol, typeof IteratorSymbol];
53
+ export type PromiseInstanceMethodName = "then" | "catch" | "finally";
54
+ export declare class PromiseInstanceMethodReference {
55
+ readonly promise: Values.Promise;
56
+ readonly name: PromiseInstanceMethodName;
57
+ constructor(promise: Values.Promise, name: PromiseInstanceMethodName);
58
+ }
59
+ export declare class ProgramThrow {
60
+ readonly value: unknown;
61
+ constructor(value: unknown);
62
+ }
63
+ export declare class GeneratorReturn {
64
+ readonly value: unknown;
65
+ constructor(value: unknown);
66
+ }
67
+ export declare const OptionalShortCircuit: unique symbol;
68
+ export declare class InterpreterRuntimeError extends Error {
69
+ readonly kind: DiagnosticKind;
70
+ readonly suggestions?: ReadonlyArray<string> | undefined;
71
+ /** The JS error class a program sees when it catches this failure. */
72
+ readonly type: ErrorType;
73
+ readonly node?: AstNode;
74
+ constructor(message: string, node?: AstNode, kind?: DiagnosticKind, suggestions?: ReadonlyArray<string> | undefined,
75
+ /** The JS error class a program sees when it catches this failure. */
76
+ type?: ErrorType);
77
+ }
78
+ export declare const rangeError: (message: string, node?: AstNode) => InterpreterRuntimeError;
79
+ export declare const referenceError: (message: string, node?: AstNode) => InterpreterRuntimeError;
80
+ export declare const syntaxError: (message: string, node?: AstNode) => InterpreterRuntimeError;
81
+ export declare const uriError: (message: string, node?: AstNode) => InterpreterRuntimeError;
82
+ export declare const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
83
+ export declare const unsupportedSyntax: (kind: string, node: AstNode) => InterpreterRuntimeError;
84
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
85
+ export declare const sourceLocation: (node: AstNode) => {
86
+ readonly line: number;
87
+ readonly column: number;
88
+ };
89
+ export declare const formatLocation: (node?: AstNode) => string;
@@ -0,0 +1,90 @@
1
+ export class CodeModeGenerator {
2
+ asynchronous;
3
+ request;
4
+ constructor(asynchronous, request) {
5
+ this.asynchronous = asynchronous;
6
+ this.request = request;
7
+ }
8
+ }
9
+ export class GeneratorMethodReference {
10
+ generator;
11
+ kind;
12
+ constructor(generator, kind) {
13
+ this.generator = generator;
14
+ this.kind = kind;
15
+ }
16
+ }
17
+ export class IntrinsicReference {
18
+ receiver;
19
+ name;
20
+ constructor(receiver, name) {
21
+ this.receiver = receiver;
22
+ this.name = name;
23
+ }
24
+ }
25
+ export class ComputedValue {
26
+ value;
27
+ constructor(value) {
28
+ this.value = value;
29
+ }
30
+ }
31
+ export const AsyncIteratorSymbol = Symbol("codemode.async-iterator");
32
+ export const IteratorSymbol = Symbol("codemode.iterator");
33
+ export const IteratorSymbols = [AsyncIteratorSymbol, IteratorSymbol];
34
+ export class PromiseInstanceMethodReference {
35
+ promise;
36
+ name;
37
+ constructor(promise, name) {
38
+ this.promise = promise;
39
+ this.name = name;
40
+ }
41
+ }
42
+ export class ProgramThrow {
43
+ value;
44
+ constructor(value) {
45
+ this.value = value;
46
+ }
47
+ }
48
+ export class GeneratorReturn {
49
+ value;
50
+ constructor(value) {
51
+ this.value = value;
52
+ }
53
+ }
54
+ export const OptionalShortCircuit = Symbol("codemode.optional-short-circuit");
55
+ export class InterpreterRuntimeError extends Error {
56
+ kind;
57
+ suggestions;
58
+ type;
59
+ node;
60
+ constructor(message, node, kind = "ExecutionFailure", suggestions,
61
+ /** The JS error class a program sees when it catches this failure. */
62
+ type = "TypeError") {
63
+ super(message);
64
+ this.kind = kind;
65
+ this.suggestions = suggestions;
66
+ this.type = type;
67
+ this.name = "InterpreterRuntimeError";
68
+ if (node)
69
+ this.node = node;
70
+ }
71
+ }
72
+ const failure = (type) => (message, node) => new InterpreterRuntimeError(message, node, "ExecutionFailure", undefined, type);
73
+ export const rangeError = failure("RangeError");
74
+ export const referenceError = failure("ReferenceError");
75
+ export const syntaxError = failure("SyntaxError");
76
+ export const uriError = failure("URIError");
77
+ // Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
78
+ export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, this, getters/setters, tagged templates, BigInt, and custom Symbols. Use plain functions and data objects instead.";
79
+ export const unsupportedSyntax = (kind, node) => new InterpreterRuntimeError(`Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage], "SyntaxError");
80
+ export const isRecord = (value) => typeof value === "object" && value !== null;
81
+ export const sourceLocation = (node) => ({
82
+ line: Math.max(1, (node.loc?.start.line ?? 2) - 1),
83
+ column: Math.max(1, (node.loc?.start.column ?? 4) - 3),
84
+ });
85
+ export const formatLocation = (node) => {
86
+ if (!node?.loc)
87
+ return "";
88
+ const location = sourceLocation(node);
89
+ return ` (line ${location.line}, col ${location.column})`;
90
+ };
@@ -0,0 +1,37 @@
1
+ import type { BlockStatement, Expression, Pattern } from "acorn";
2
+ import { type Binding } from "./model.js";
3
+ /** An object owned by the program: own properties plus a prototype link. */
4
+ export declare class ProgramObject {
5
+ proto: ProgramObject | null;
6
+ readonly props: Map<PropertyKey, unknown>;
7
+ constructor(proto?: ProgramObject | null);
8
+ }
9
+ export declare class ProgramArray extends ProgramObject {
10
+ readonly items: Array<unknown>;
11
+ constructor(items?: Array<unknown>);
12
+ }
13
+ /** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */
14
+ export declare class ProgramError extends ProgramObject {
15
+ }
16
+ export declare class ProgramFunction extends ProgramObject {
17
+ readonly name: string;
18
+ readonly parameters: ReadonlyArray<Pattern>;
19
+ readonly body: BlockStatement | Expression;
20
+ readonly capturedScopes: ReadonlyArray<Map<string, Binding>>;
21
+ readonly async: boolean;
22
+ readonly generator: boolean;
23
+ readonly length: number;
24
+ constructor(name: string, parameters: ReadonlyArray<Pattern>, body: BlockStatement | Expression, capturedScopes: ReadonlyArray<Map<string, Binding>>, async: boolean, generator: boolean);
25
+ }
26
+ export declare const parseArrayIndex: (key: string | number) => number | undefined;
27
+ export declare const hasOwn: (target: ProgramObject, key: PropertyKey) => boolean;
28
+ export declare const getOwn: (target: ProgramObject, key: PropertyKey) => unknown;
29
+ export declare const get: (target: ProgramObject, key: PropertyKey) => unknown;
30
+ export declare const hasPrototype: (value: unknown, proto: ProgramObject) => boolean;
31
+ export declare const has: (target: ProgramObject, key: PropertyKey) => boolean;
32
+ export declare const set: (target: ProgramObject, key: PropertyKey, value: unknown) => boolean;
33
+ export declare const remove: (target: ProgramObject, key: PropertyKey) => boolean;
34
+ export declare const ownKeys: (target: ProgramObject) => Array<string | symbol>;
35
+ export declare const ownEntries: (target: ProgramObject) => Array<[string, unknown]>;
36
+ export declare const record: (entries: Record<string, unknown>) => ProgramObject;
37
+ export declare const assign: (target: ProgramObject, source: ProgramObject, skip?: ReadonlySet<PropertyKey>) => void;
@@ -0,0 +1,154 @@
1
+ import { AsyncIteratorSymbol, IteratorSymbol } from "./model.js";
2
+ /** An object owned by the program: own properties plus a prototype link. */
3
+ export class ProgramObject {
4
+ proto;
5
+ props = new Map();
6
+ constructor(proto = null) {
7
+ this.proto = proto;
8
+ }
9
+ }
10
+ export class ProgramArray extends ProgramObject {
11
+ items;
12
+ constructor(items = []) {
13
+ super();
14
+ this.items = items;
15
+ }
16
+ }
17
+ /** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */
18
+ export class ProgramError extends ProgramObject {
19
+ }
20
+ export class ProgramFunction extends ProgramObject {
21
+ name;
22
+ parameters;
23
+ body;
24
+ capturedScopes;
25
+ async;
26
+ generator;
27
+ length;
28
+ constructor(name, parameters, body, capturedScopes, async, generator) {
29
+ super();
30
+ this.name = name;
31
+ this.parameters = parameters;
32
+ this.body = body;
33
+ this.capturedScopes = capturedScopes;
34
+ this.async = async;
35
+ this.generator = generator;
36
+ const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement");
37
+ this.length = optional === -1 ? parameters.length : optional;
38
+ }
39
+ }
40
+ const MAX_ARRAY_LENGTH = 4_294_967_295;
41
+ export const parseArrayIndex = (key) => {
42
+ const property = String(key);
43
+ if (!/^(0|[1-9]\d*)$/.test(property))
44
+ return undefined;
45
+ const index = Number(property);
46
+ return index < MAX_ARRAY_LENGTH ? index : undefined;
47
+ };
48
+ const canonical = (key) => (typeof key === "symbol" ? key : String(key));
49
+ const index = (target, key) => target instanceof ProgramArray && typeof key === "string" ? parseArrayIndex(key) : undefined;
50
+ // Non-enumerable built-in properties: array length, function name and length.
51
+ const builtin = (target, name) => (name === "length" && target instanceof ProgramArray) ||
52
+ ((name === "name" || name === "length") && target instanceof ProgramFunction);
53
+ export const hasOwn = (target, key) => {
54
+ const name = canonical(key);
55
+ const at = index(target, name);
56
+ if (at !== undefined)
57
+ return at in target.items;
58
+ return builtin(target, name) || target.props.has(name);
59
+ };
60
+ export const getOwn = (target, key) => {
61
+ const name = canonical(key);
62
+ const at = index(target, name);
63
+ if (at !== undefined)
64
+ return target.items[at];
65
+ if (target instanceof ProgramArray && name === "length")
66
+ return target.items.length;
67
+ if (target instanceof ProgramFunction && name === "name")
68
+ return target.name;
69
+ if (target instanceof ProgramFunction && name === "length")
70
+ return target.length;
71
+ return target.props.get(name);
72
+ };
73
+ export const get = (target, key) => {
74
+ for (let current = target; current !== null; current = current.proto) {
75
+ if (hasOwn(current, key))
76
+ return getOwn(current, key);
77
+ }
78
+ return undefined;
79
+ };
80
+ export const hasPrototype = (value, proto) => {
81
+ for (let current = value instanceof ProgramObject ? value.proto : null; current !== null; current = current.proto) {
82
+ if (current === proto)
83
+ return true;
84
+ }
85
+ return false;
86
+ };
87
+ export const has = (target, key) => {
88
+ for (let current = target; current !== null; current = current.proto) {
89
+ if (hasOwn(current, key))
90
+ return true;
91
+ }
92
+ return false;
93
+ };
94
+ export const set = (target, key, value) => {
95
+ const name = canonical(key);
96
+ const at = index(target, name);
97
+ if (at !== undefined) {
98
+ ;
99
+ target.items[at] = value;
100
+ return true;
101
+ }
102
+ if (name === "length" && target instanceof ProgramArray) {
103
+ const length = typeof value === "number" ? value : Number(value);
104
+ if (!Number.isInteger(length) || length < 0 || length > 4_294_967_295)
105
+ return false;
106
+ target.items.length = length;
107
+ return true;
108
+ }
109
+ if (builtin(target, name))
110
+ return false;
111
+ target.props.set(name, value);
112
+ return true;
113
+ };
114
+ export const remove = (target, key) => {
115
+ const name = canonical(key);
116
+ const at = index(target, name);
117
+ if (at !== undefined)
118
+ return delete target.items[at];
119
+ if (name === "length" && target instanceof ProgramArray)
120
+ return false;
121
+ if (builtin(target, name))
122
+ return true;
123
+ target.props.delete(name);
124
+ return true;
125
+ };
126
+ // JS order: array indexes, integer-like keys ascending, other strings, then symbols.
127
+ export const ownKeys = (target) => {
128
+ const strings = [...target.props.keys()].filter((key) => typeof key === "string");
129
+ const symbols = [...target.props.keys()].filter((key) => typeof key === "symbol");
130
+ return [
131
+ ...(target instanceof ProgramArray ? Object.keys(target.items) : []),
132
+ ...strings.filter((key) => parseArrayIndex(key) !== undefined).sort((a, b) => Number(a) - Number(b)),
133
+ ...strings.filter((key) => parseArrayIndex(key) === undefined),
134
+ ...symbols,
135
+ ];
136
+ };
137
+ export const ownEntries = (target) => ownKeys(target)
138
+ .filter((key) => typeof key === "string")
139
+ .map((key) => [key, getOwn(target, key)]);
140
+ export const record = (entries) => {
141
+ const target = new ProgramObject();
142
+ for (const [key, value] of Object.entries(entries))
143
+ set(target, key, value);
144
+ return target;
145
+ };
146
+ export const assign = (target, source, skip) => {
147
+ for (const key of ownKeys(source)) {
148
+ if (skip?.has(key))
149
+ continue;
150
+ if (typeof key === "symbol" && key !== IteratorSymbol && key !== AsyncIteratorSymbol)
151
+ continue;
152
+ set(target, key, getOwn(source, key));
153
+ }
154
+ };
@@ -0,0 +1,31 @@
1
+ import { Effect, Exit, Scope } from "effect";
2
+ import type { Diagnostic } from "../codemode.js";
3
+ import { type AstNode, InterpreterRuntimeError, PromiseInstanceMethodReference } from "./model.js";
4
+ import { HostFunction } from "./host.js";
5
+ import { Values } from "../values.js";
6
+ import { type Runner } from "./runner.js";
7
+ export declare class PromiseRuntime<R> {
8
+ private readonly scope;
9
+ private readonly active;
10
+ private readonly ids;
11
+ private readonly observed;
12
+ private readonly failures;
13
+ private nextID;
14
+ constructor(scope: Scope.Scope);
15
+ createWithSelf(body: (self: {
16
+ promise?: Values.Promise;
17
+ }) => Effect.Effect<unknown, unknown, R>): Effect.Effect<Values.Promise, never, R>;
18
+ create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<Values.Promise, never, R>;
19
+ markObserved(promise: Values.Promise): void;
20
+ await(promise: Values.Promise): Effect.Effect<Exit.Exit<unknown, unknown>>;
21
+ fork(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<void, never, R>;
22
+ diagnostics(): Array<Diagnostic>;
23
+ interrupt(): Effect.Effect<Array<Diagnostic>>;
24
+ }
25
+ export declare const selfResolutionError: (node?: AstNode) => InterpreterRuntimeError;
26
+ export declare const resolvePromiseValue: <R>(runner: Runner<R>, value: unknown, node: AstNode, own?: {
27
+ promise?: Values.Promise;
28
+ }) => Effect.Effect<unknown, unknown, R>;
29
+ export declare const resolvePromise: <R>(runner: Runner<R>, promises: PromiseRuntime<R>, value: unknown, node: AstNode) => Effect.Effect<Values.Promise, never, R>;
30
+ export declare const invokePromiseInstanceMethod: <R>(runner: Runner<R>, promises: PromiseRuntime<R>, ref: PromiseInstanceMethodReference, args: Array<unknown>, node: AstNode) => Effect.Effect<Values.Promise, never, R>;
31
+ export declare const promiseGlobal: <R>(runner: Runner<R>, promises: PromiseRuntime<R>) => HostFunction<R>;
@@ -0,0 +1,270 @@
1
+ import { Cause, Deferred, Effect, Exit, Fiber, Scope } from "effect";
2
+ import { InterpreterRuntimeError, ProgramThrow, PromiseInstanceMethodReference } from "./model.js";
3
+ import { get, ProgramArray, ProgramFunction, ProgramObject, record } from "./objects.js";
4
+ import { HostFunction, requiresNew, sync } from "./host.js";
5
+ import { caughtErrorValue, createAggregateErrorValue, normalizeError } from "./errors.js";
6
+ import { typeofValue } from "./references.js";
7
+ import { Values } from "../values.js";
8
+ import { applyCollectionCallback, isSupportedCallback } from "./runner.js";
9
+ // A `resolve`/`reject` handed to an executor or thenable: calling it settles the capability.
10
+ const capability = (name, settle) => sync(name, (args) => {
11
+ settle(args[0]);
12
+ return undefined;
13
+ });
14
+ // Observation only controls rejection reporting; program completion interrupts all promise work.
15
+ export class PromiseRuntime {
16
+ scope;
17
+ active = new Set();
18
+ ids = new WeakMap();
19
+ observed = new WeakSet();
20
+ failures = new Map();
21
+ nextID = 0;
22
+ constructor(scope) {
23
+ this.scope = scope;
24
+ }
25
+ // Resolution bodies need the promise's own identity to reject `resolve(promise)` self-resolution.
26
+ createWithSelf(body) {
27
+ const self = {};
28
+ return Effect.map(this.create(body(self)), (promise) => {
29
+ self.promise = promise;
30
+ return promise;
31
+ });
32
+ }
33
+ create(effect) {
34
+ return Effect.suspend(() => {
35
+ // Allocate before forking so reruns get distinct IDs and diagnostics retain creation order.
36
+ const id = this.nextID++;
37
+ return Effect.map(Effect.forkIn(effect, this.scope, { startImmediately: true }), (fiber) => {
38
+ const promise = new Values.Promise(fiber);
39
+ this.active.add(promise);
40
+ this.ids.set(promise, id);
41
+ fiber.addObserver((exit) => {
42
+ this.active.delete(promise);
43
+ if (Exit.isSuccess(exit) || Cause.hasInterruptsOnly(exit.cause) || this.observed.has(promise)) {
44
+ this.ids.delete(promise);
45
+ return;
46
+ }
47
+ const failure = normalizeError(Cause.squash(exit.cause));
48
+ this.failures.set(id, {
49
+ ...failure,
50
+ message: `Unhandled rejection from an un-awaited promise: ${failure.message}`,
51
+ });
52
+ });
53
+ return promise;
54
+ });
55
+ });
56
+ }
57
+ // Observation must be recorded when responsibility transfers, before the consumer fiber runs.
58
+ markObserved(promise) {
59
+ this.observed.add(promise);
60
+ const id = this.ids.get(promise);
61
+ this.ids.delete(promise);
62
+ if (id !== undefined)
63
+ this.failures.delete(id);
64
+ }
65
+ await(promise) {
66
+ return Fiber.await(promise.fiber);
67
+ }
68
+ fork(effect) {
69
+ return Effect.asVoid(Effect.forkIn(effect, this.scope, { startImmediately: true }));
70
+ }
71
+ diagnostics() {
72
+ return [...this.failures].sort(([left], [right]) => left - right).map(([, failure]) => failure);
73
+ }
74
+ // Re-check because a straggler can create promises before its interruption lands.
75
+ interrupt() {
76
+ const self = this;
77
+ return Effect.gen(function* () {
78
+ while (self.active.size > 0) {
79
+ yield* Fiber.interruptAll([...self.active].map((promise) => promise.fiber));
80
+ }
81
+ return self.diagnostics();
82
+ });
83
+ }
84
+ }
85
+ export const selfResolutionError = (node) => new InterpreterRuntimeError("Chaining cycle detected: a promise cannot resolve with itself.", node);
86
+ export const resolvePromiseValue = (runner, value, node, own) => {
87
+ if (own?.promise !== undefined && value === own.promise)
88
+ return Effect.fail(selfResolutionError(node));
89
+ if (value instanceof Values.Promise)
90
+ return runner.settlePromise(value);
91
+ if (!(value instanceof ProgramObject))
92
+ return Effect.succeed(value);
93
+ const then = get(value, "then");
94
+ if (typeofValue(then) !== "function")
95
+ return Effect.succeed(value);
96
+ return Effect.gen(function* () {
97
+ // Promise resolution invokes a thenable's method in a later job.
98
+ yield* Effect.yieldNow;
99
+ const deferred = Deferred.makeUnsafe();
100
+ const resolve = capability("resolve", (result) => Deferred.doneUnsafe(deferred, Exit.succeed(result)));
101
+ const reject = capability("reject", (reason) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(reason))));
102
+ const executed = yield* Effect.exit(runner.invokeCallable(then, [resolve, reject], node));
103
+ if (!Exit.isSuccess(executed)) {
104
+ if (Cause.hasInterruptsOnly(executed.cause))
105
+ return yield* Effect.failCause(executed.cause);
106
+ Deferred.doneUnsafe(deferred, Exit.fail(Cause.squash(executed.cause)));
107
+ }
108
+ return yield* resolvePromiseValue(runner, yield* Deferred.await(deferred), node, own);
109
+ });
110
+ };
111
+ export const resolvePromise = (runner, promises, value, node) => {
112
+ if (value instanceof Values.Promise)
113
+ return Effect.succeed(value);
114
+ return promises.createWithSelf((self) => resolvePromiseValue(runner, value, node, self));
115
+ };
116
+ const promiseStatics = ["all", "allSettled", "race", "any", "resolve", "reject"];
117
+ const invokePromiseMethod = (runner, promises, name, args, node) => {
118
+ if (name === "resolve") {
119
+ return resolvePromise(runner, promises, args[0], node);
120
+ }
121
+ if (name === "reject") {
122
+ return promises.create(Effect.fail(new ProgramThrow(args[0])));
123
+ }
124
+ return promises.create(Effect.gen(function* () {
125
+ const cursor = yield* runner.syncIterator(args[0], node);
126
+ if (cursor === undefined) {
127
+ throw new InterpreterRuntimeError(`Promise.${name} expects an array or other synchronous iterable.`, node);
128
+ }
129
+ const items = [];
130
+ while (true) {
131
+ const step = yield* cursor.next;
132
+ if (step.done)
133
+ break;
134
+ const item = yield* resolvePromise(runner, promises, step.value, node);
135
+ promises.markObserved(item);
136
+ items.push(item);
137
+ }
138
+ if (name === "all") {
139
+ return new ProgramArray(yield* settleAfterTurn(Effect.all(items.map((item) => Effect.flatten(promises.await(item))), { concurrency: "unbounded" })));
140
+ }
141
+ if (name === "allSettled") {
142
+ const outcomes = [];
143
+ for (const item of items) {
144
+ const exit = yield* promises.await(item);
145
+ if (Exit.isSuccess(exit)) {
146
+ outcomes.push(record({ status: "fulfilled", value: exit.value }));
147
+ continue;
148
+ }
149
+ if (Cause.hasInterruptsOnly(exit.cause))
150
+ return yield* Effect.failCause(exit.cause);
151
+ outcomes.push(record({ status: "rejected", reason: caughtErrorValue(runner, Cause.squash(exit.cause)) }));
152
+ }
153
+ yield* Effect.yieldNow;
154
+ return new ProgramArray(outcomes);
155
+ }
156
+ if (name === "race") {
157
+ if (items.length === 0) {
158
+ throw new InterpreterRuntimeError("Promise.race([]) would never settle; provide at least one promise or value.", node);
159
+ }
160
+ return yield* settleAfterTurn(Effect.flatten(Effect.raceAll(items.map((item) => promises.await(item)))));
161
+ }
162
+ const flipped = items.map((item) => Effect.flatMap(promises.await(item), (exit) => {
163
+ if (Exit.isSuccess(exit))
164
+ return Effect.fail(new PromiseAnyFulfilled(exit.value));
165
+ if (Cause.hasInterruptsOnly(exit.cause))
166
+ return Effect.failCause(exit.cause);
167
+ return Effect.succeed(caughtErrorValue(runner, Cause.squash(exit.cause)));
168
+ }));
169
+ return yield* settleAfterTurn(Effect.all(flipped, { concurrency: "unbounded" }).pipe(Effect.flatMap((reasons) => Effect.fail(new ProgramThrow(createAggregateErrorValue(runner, reasons, "All promises were rejected")))), Effect.catch((error) => error instanceof PromiseAnyFulfilled ? Effect.succeed(error.value) : Effect.fail(error))));
170
+ }));
171
+ };
172
+ export const invokePromiseInstanceMethod = (runner, promises, ref, args, node) => {
173
+ const method = `Promise.prototype.${ref.name}`;
174
+ promises.markObserved(ref.promise);
175
+ if (ref.name === "finally") {
176
+ return chainFinally(runner, promises, ref.promise, reactionHandler(args[0], method, node), method, node);
177
+ }
178
+ const onFulfilled = ref.name === "then" ? reactionHandler(args[0], method, node) : undefined;
179
+ const onRejected = reactionHandler(ref.name === "then" ? args[1] : args[0], method, node);
180
+ return chainReaction(runner, promises, ref.promise, onFulfilled, onRejected, method, node);
181
+ };
182
+ const constructPromise = (runner, promises, executor, node) => {
183
+ if (!(executor instanceof ProgramFunction)) {
184
+ throw new InterpreterRuntimeError("new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).", node);
185
+ }
186
+ return Effect.gen(function* () {
187
+ const deferred = Deferred.makeUnsafe();
188
+ const promise = yield* promises.createWithSelf((self) => Effect.flatMap(Deferred.await(deferred), (value) => resolvePromiseValue(runner, value, node, self)));
189
+ const resolve = capability("resolve", (value) => Deferred.doneUnsafe(deferred, Exit.succeed(value)));
190
+ const reject = capability("reject", (value) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(value))));
191
+ const executed = yield* Effect.exit(runner.invokeFunction(executor, [resolve, reject]));
192
+ if (!Exit.isSuccess(executed)) {
193
+ if (Cause.hasInterruptsOnly(executed.cause))
194
+ return yield* Effect.failCause(executed.cause);
195
+ Deferred.doneUnsafe(deferred, Exit.fail(Cause.squash(executed.cause)));
196
+ }
197
+ return promise;
198
+ });
199
+ };
200
+ // Settle one reaction turn after the deciding member, after its existing reactions.
201
+ const settleAfterTurn = (body) => Effect.flatMap(Effect.exit(body), (exit) => Effect.andThen(Effect.yieldNow, exit));
202
+ class PromiseAnyFulfilled {
203
+ value;
204
+ constructor(value) {
205
+ this.value = value;
206
+ }
207
+ }
208
+ const reactionHandler = (value, method, node) => {
209
+ if (isSupportedCallback(value))
210
+ return value;
211
+ if (typeofValue(value) === "function") {
212
+ throw new InterpreterRuntimeError(`${method} cannot use this callable as a handler; wrap it in an arrow function, e.g. (value) => tools.ns.tool(value).`, node);
213
+ }
214
+ return undefined;
215
+ };
216
+ // Teardown bypasses handlers; settled reactions yield once so handlers never run inline.
217
+ const reactionExit = (promises, source) => Effect.gen(function* () {
218
+ const exit = yield* promises.await(source);
219
+ if (!Exit.isSuccess(exit) && Cause.hasInterruptsOnly(exit.cause))
220
+ return yield* Effect.failCause(exit.cause);
221
+ yield* Effect.yieldNow;
222
+ return exit;
223
+ });
224
+ const chainReaction = (runner, promises, source, onFulfilled, onRejected, method, node) => {
225
+ return promises.createWithSelf((self) => Effect.gen(function* () {
226
+ const exit = yield* reactionExit(promises, source);
227
+ const handler = Exit.isSuccess(exit) ? onFulfilled : onRejected;
228
+ if (handler === undefined)
229
+ return yield* exit;
230
+ const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(runner, Cause.squash(exit.cause));
231
+ const result = yield* applyCollectionCallback(runner, handler, method, node)([input]);
232
+ return yield* resolvePromiseValue(runner, result, node, self);
233
+ }));
234
+ };
235
+ const chainFinally = (runner, promises, source, cleanup, method, node) => promises.create(Effect.gen(function* () {
236
+ const exit = yield* reactionExit(promises, source);
237
+ if (cleanup !== undefined) {
238
+ const result = yield* applyCollectionCallback(runner, cleanup, method, node)([]);
239
+ const intermediate = yield* promises.create(Effect.gen(function* () {
240
+ yield* runner.settlePromise(yield* resolvePromise(runner, promises, result, node));
241
+ return yield* exit;
242
+ }));
243
+ return yield* runner.settlePromise(intermediate);
244
+ }
245
+ return yield* exit;
246
+ }));
247
+ export const promiseGlobal = (runner, promises) => {
248
+ // Combinators are not callbacks: `[p].map(Promise.resolve)` must ask for an arrow function.
249
+ const statics = new Map(promiseStatics.map((name) => [
250
+ name,
251
+ new HostFunction({
252
+ name: `Promise.${name}`,
253
+ call: (args, node) => invokePromiseMethod(runner, promises, name, args, node),
254
+ callback: false,
255
+ }),
256
+ ]));
257
+ return new HostFunction({
258
+ name: "Promise",
259
+ call: requiresNew("Promise"),
260
+ construct: (args, node) => constructPromise(runner, promises, args[0], node),
261
+ instanceOf: (value) => value instanceof Values.Promise,
262
+ // Unknown statics fail loudly so a missing await cannot hide behind `undefined`.
263
+ members: (key, node) => {
264
+ const method = typeof key === "string" ? statics.get(key) : undefined;
265
+ if (method !== undefined)
266
+ return method;
267
+ throw new InterpreterRuntimeError(`Promise.${String(key)} is not available. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`, node);
268
+ },
269
+ });
270
+ };