effect-durable-agent-cloudflare 0.1.0-alpha.4 → 0.1.0-alpha.6

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
@@ -1,6 +1,8 @@
1
1
  # effect-durable-agent-cloudflare
2
2
 
3
- Cloudflare Durable Objects host for [`effect-durable-agent`](../../README.md).
3
+ Cloudflare Durable Objects host for [`effect-durable-agent`](../effect-durable-agent/README.md).
4
+
5
+ Executable examples live in the repository's shared [`examples`](../../examples) directory.
4
6
 
5
7
  ```sh
6
8
  pnpm add effect-durable-agent@alpha effect-durable-agent-cloudflare@alpha
@@ -16,9 +18,17 @@ export class AgentSession extends EDASessionDurableObject<Env> {
16
18
  }
17
19
  ```
18
20
 
19
- The host maps one session to one SQLite-backed Durable Object and owns alarms,
20
- hibernatable WebSockets, RPC decoding, recovery, and sink checkpoints. Register
21
- only the concrete application subclass in `wrangler.jsonc`.
21
+ The adapter maps one session to one SQLite-backed Durable Object. Ownership is
22
+ split deliberately:
23
+
24
+ - `EDASessionDurableObject` maps Cloudflare callbacks and object identity.
25
+ - `EDASessionController` coordinates the session use cases.
26
+ - `EDASessionRuntime` owns the disposable Effect runtime and keep-alive.
27
+ - `EDAWebSocketConnectionManager` owns accepted sockets and hibernation state.
28
+
29
+ Idle sockets use `ctx.acceptWebSocket`, Effect Schema attachments, and
30
+ automatic ping responses. They own no timer or resident subscriber fiber.
31
+ Register only the concrete application subclass in `wrangler.jsonc`.
22
32
 
23
33
  Durable Object RPC uses structured clone. Encode Schema class instances before
24
34
  passing commands or batches across the Worker-to-object boundary with
@@ -26,3 +36,7 @@ passing commands or batches across the Worker-to-object boundary with
26
36
 
27
37
  The core and host packages are published in lockstep and must use the same
28
38
  version.
39
+
40
+ Public subpaths are `/durable-object`, `/session-controller`, `/openai`,
41
+ `/rpc`, and `/storage`. The WebSocket protocol and pure delivery machine
42
+ are exported from `effect-durable-agent/websocket`.
@@ -1,4 +1,4 @@
1
- import { a as EDASessionDurableObjectHostOptions } from "./durable-object-runtime-Dehm7PsO.js";
1
+ import { r as EDASessionControllerOptions } from "./session-controller-BSMHhS_l.js";
2
2
  import { DurableObject } from "cloudflare:workers";
3
3
  import { CommittedCommandTerminalEvent, EDARuntimeConfig } from "effect-durable-agent/services/runtime";
4
4
  import { CommittedDurableEvent } from "effect-durable-agent/services/session-store";
@@ -37,7 +37,7 @@ interface EDASessionBlockOnCommandRpcInput {
37
37
  readonly trace: unknown;
38
38
  }
39
39
  /** Constructor options for concrete app subclasses of the EDA Durable Object base. */
40
- type EDASessionDurableObjectOptions = Omit<EDASessionDurableObjectHostOptions, "background" | "storage">;
40
+ type EDASessionDurableObjectOptions = Omit<EDASessionControllerOptions, "background" | "getWebSockets" | "storage">;
41
41
  /** Resolve a concrete EDA session Durable Object binding by domain session id. */
42
42
  declare const getEDASessionDurableObjectByName: <T extends EDASessionDurableObject<object>>(namespace: DurableObjectNamespace<T>, sessionId: string) => DurableObjectStub<T>;
43
43
  /**
@@ -65,7 +65,6 @@ declare abstract class EDASessionDurableObject<EnvType extends object = object>
65
65
  alarm(): Promise<void>;
66
66
  private parseSessionId;
67
67
  private sessionIdFromObjectName;
68
- private restoreAcceptedEventWebSockets;
69
68
  }
70
69
  /** Convenience config constructor for subclasses that need only provider/model ids. */
71
70
  declare const edaRuntimeConfig: (input: {
@@ -1,2 +1,180 @@
1
- import { a as getEDASessionDurableObjectByName, i as encodeEdaRpcSessionSnapshot, n as edaRuntimeConfig, r as encodeEdaRpcCommittedDurableEvent, t as EDASessionDurableObject } from "./durable-object-CjWWNyek.js";
1
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcDurableEvent } from "./rpc.js";
2
+ import { t as EDASessionController } from "./session-controller-Bo7DyydT.js";
3
+ import { DurableObject } from "cloudflare:workers";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Schema from "effect/Schema";
6
+ import { EDACommand } from "effect-durable-agent/types/commands";
7
+ import { CommandId, SequenceNumber, SessionId } from "effect-durable-agent/types/core";
8
+ import { EDATraceMetadata, makeEDATraceMetadataFromParent, makeRootEDATraceMetadata, parseEDATraceparent } from "effect-durable-agent/types/tracing";
9
+ import { EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE } from "effect-durable-agent/websocket";
10
+ //#region src/durable-object.ts
11
+ /** Resolve a concrete EDA session Durable Object binding by domain session id. */
12
+ const getEDASessionDurableObjectByName = (namespace, sessionId) => namespace.getByName(sessionId);
13
+ /**
14
+ * Base class for raw Cloudflare Durable Object hosts of one EDA session.
15
+ *
16
+ * Concrete products should extend this class, pass provider/tool layers from
17
+ * their own constructor, and register only that subclass in Wrangler. The base
18
+ * class is deliberately not exported from `workers/app.ts` and owns no binding
19
+ * name or routing convention.
20
+ */
21
+ var EDASessionDurableObject = class extends DurableObject {
22
+ #controller;
23
+ constructor(ctx, env, options) {
24
+ super(ctx, env);
25
+ const storage = toEDASessionStorage(this.ctx.storage);
26
+ this.#controller = new EDASessionController({
27
+ ...options,
28
+ background: this.ctx,
29
+ getWebSockets: () => this.ctx.getWebSockets(),
30
+ storage
31
+ });
32
+ this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair(EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE));
33
+ this.ctx.blockConcurrencyWhile(async () => {
34
+ await Effect.runPromise(EDASessionController.migrate(storage));
35
+ });
36
+ }
37
+ async fetch(request) {
38
+ if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") return new Response("Expected WebSocket upgrade.", { status: 426 });
39
+ const url = new URL(request.url);
40
+ const sessionIdRaw = url.searchParams.get("sessionId");
41
+ if (sessionIdRaw === null) return new Response("Missing sessionId.", { status: 400 });
42
+ const afterSeq = Number(url.searchParams.get("afterSeq") ?? "0");
43
+ if (!Number.isInteger(afterSeq) || afterSeq < 0) return new Response("Invalid afterSeq.", { status: 400 });
44
+ const pair = new WebSocketPair();
45
+ const client = pair[0];
46
+ const server = pair[1];
47
+ this.ctx.acceptWebSocket(server);
48
+ await this.#controller.acceptEventWebSocket({
49
+ afterSeq: SequenceNumber.make(afterSeq),
50
+ sessionId: this.parseSessionId(sessionIdRaw),
51
+ trace: traceMetadataFromRequest(request),
52
+ webSocket: server
53
+ });
54
+ return new Response(null, {
55
+ status: 101,
56
+ webSocket: client
57
+ });
58
+ }
59
+ async submit(input) {
60
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.submit({
61
+ command: await decodeCommand(input.command),
62
+ sessionId: this.parseSessionId(input.sessionId),
63
+ trace: decodeTraceMetadata(input.trace)
64
+ }));
65
+ }
66
+ async submitBatch(input) {
67
+ return (await this.#controller.submitBatch({
68
+ items: await decodeSubmittables(input.items),
69
+ sessionId: this.parseSessionId(input.sessionId),
70
+ trace: decodeTraceMetadata(input.trace)
71
+ })).map(encodeEdaRpcCommittedDurableEvent);
72
+ }
73
+ async submitAndBlock(input) {
74
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.submitAndBlock({
75
+ command: await decodeCommand(input.command),
76
+ sessionId: this.parseSessionId(input.sessionId),
77
+ trace: decodeTraceMetadata(input.trace)
78
+ }));
79
+ }
80
+ async blockOnCommand(input) {
81
+ return encodeEdaRpcCommittedDurableEvent(await this.#controller.blockOnCommand({
82
+ ...input.afterSeq === void 0 ? {} : { afterSeq: SequenceNumber.make(input.afterSeq) },
83
+ commandId: CommandId.make(input.commandId),
84
+ sessionId: this.parseSessionId(input.sessionId),
85
+ trace: decodeTraceMetadata(input.trace)
86
+ }));
87
+ }
88
+ async snapshot(input) {
89
+ return encodeEdaRpcSessionSnapshot(await this.#controller.snapshot({
90
+ sessionId: this.parseSessionId(input.sessionId),
91
+ trace: decodeTraceMetadata(input.trace)
92
+ }));
93
+ }
94
+ async messages(input) {
95
+ return await this.#controller.messages({
96
+ sessionId: this.parseSessionId(input.sessionId),
97
+ trace: decodeTraceMetadata(input.trace)
98
+ });
99
+ }
100
+ async webSocketMessage(webSocket, message) {
101
+ await this.#controller.webSocketMessage(webSocket, message);
102
+ }
103
+ async webSocketClose(webSocket, _code, _reason, _wasClean) {
104
+ this.#controller.webSocketClose(webSocket);
105
+ }
106
+ async webSocketError(webSocket, _error) {
107
+ this.#controller.webSocketError(webSocket);
108
+ }
109
+ async destroySession(input) {
110
+ await this.#controller.destroy({
111
+ sessionId: this.parseSessionId(input.sessionId),
112
+ trace: decodeTraceMetadata(input.trace)
113
+ });
114
+ await this.ctx.storage.deleteAlarm();
115
+ await this.ctx.storage.deleteAll();
116
+ }
117
+ async alarm() {
118
+ const sessionId = this.sessionIdFromObjectName();
119
+ await this.#controller.alarm(sessionId === void 0 ? void 0 : {
120
+ sessionId,
121
+ trace: makeRootEDATraceMetadata()
122
+ });
123
+ }
124
+ parseSessionId(input) {
125
+ const sessionId = SessionId.make(input);
126
+ const objectName = this.ctx.id.name;
127
+ if (objectName !== void 0 && objectName !== sessionId) throw new Error(`EDA Durable Object name ${objectName} cannot serve session ${sessionId}`);
128
+ return sessionId;
129
+ }
130
+ sessionIdFromObjectName() {
131
+ return this.ctx.id.name === void 0 ? void 0 : SessionId.make(this.ctx.id.name);
132
+ }
133
+ };
134
+ const toEDASessionStorage = (storage) => {
135
+ return storage;
136
+ };
137
+ const decodeTraceMetadata = (input) => Schema.decodeUnknownSync(EDATraceMetadata)(input);
138
+ const traceMetadataFromRequest = (request) => {
139
+ const parent = parseEDATraceparent(request.headers.get("traceparent"), request.headers.get("tracestate"));
140
+ return parent === null ? makeRootEDATraceMetadata() : makeEDATraceMetadataFromParent(parent);
141
+ };
142
+ /** Convenience config constructor for subclasses that need only provider/model ids. */
143
+ const edaRuntimeConfig = (input) => ({
144
+ modelSelection: {
145
+ modelId: input.modelId,
146
+ provider: input.provider
147
+ },
148
+ ...input.maxToolCallsPerRun === void 0 ? {} : { maxToolCallsPerRun: input.maxToolCallsPerRun },
149
+ ...input.systemPrompt === void 0 ? {} : { systemPrompt: input.systemPrompt }
150
+ });
151
+ /** Encode committed events into structured-clone-safe Durable Object RPC payloads. */
152
+ const encodeEdaRpcCommittedDurableEvent = (event) => ({
153
+ position: event.position,
154
+ event: encodeEdaRpcDurableEvent(event.event)
155
+ });
156
+ /** Encode runtime snapshots into structured-clone-safe Durable Object RPC payloads. */
157
+ const encodeEdaRpcSessionSnapshot = (snapshot) => ({
158
+ ...snapshot,
159
+ reducerStates: new Map(snapshot.reducerStates),
160
+ state: {
161
+ ...snapshot.state,
162
+ commands: new Map(Array.from(snapshot.state.commands, ([commandId, record]) => [commandId, encodeCommandCarrierForRpc(record)])),
163
+ commandQueues: {
164
+ ...snapshot.state.commandQueues,
165
+ activeControlCommands: snapshot.state.commandQueues.activeControlCommands.map(encodeCommandCarrierForRpc),
166
+ pendingCommands: snapshot.state.commandQueues.pendingCommands.map(encodeCommandCarrierForRpc),
167
+ queuedCommands: snapshot.state.commandQueues.queuedCommands.map(encodeCommandCarrierForRpc)
168
+ }
169
+ }
170
+ });
171
+ const encodeCommandCarrierForRpc = (value) => value.command === void 0 ? value : {
172
+ ...value,
173
+ command: Schema.encodeSync(EDACommand)(value.command)
174
+ };
175
+ const decodeCommand = (input) => Effect.runPromise(decodeEdaRpcCommand(input));
176
+ const decodeSubmittables = (input) => Effect.runPromise(decodeEdaRpcSubmittables(input));
177
+ //#endregion
2
178
  export { EDASessionDurableObject, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName };
179
+
180
+ //# sourceMappingURL=durable-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-object.js","names":["#controller"],"sources":["../src/durable-object.ts"],"sourcesContent":["import { DurableObject } from \"cloudflare:workers\";\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\n\nimport type { DurableTranscriptMessage } from \"effect-durable-agent/domain/message-transcript\";\nimport {\n decodeEdaRpcCommand,\n decodeEdaRpcSubmittables,\n encodeEdaRpcDurableEvent,\n} from \"./rpc-codec\";\nimport type { EDASessionSnapshot } from \"effect-durable-agent/services/session-query\";\nimport type { CommittedDurableEvent as CommittedDurableEventValue } from \"effect-durable-agent/services/session-store\";\nimport type {\n CommittedCommandTerminalEvent,\n EDARuntimeConfig,\n} from \"effect-durable-agent/services/runtime\";\nimport {\n EDACommand,\n type EDACommand as EDACommandValue,\n} from \"effect-durable-agent/types/commands\";\nimport type { EDASubmittable } from \"effect-durable-agent/services/session-state\";\nimport { CommandId, SequenceNumber, SessionId } from \"effect-durable-agent/types/core\";\nimport {\n EDATraceMetadata,\n makeEDATraceMetadataFromParent,\n makeRootEDATraceMetadata,\n parseEDATraceparent,\n} from \"effect-durable-agent/types/tracing\";\nimport {\n EDASessionController,\n type EDASessionControllerOptions,\n type EDASessionDurableObjectStorage,\n} from \"./session-controller\";\nimport {\n EDA_WEB_SOCKET_PING_MESSAGE,\n EDA_WEB_SOCKET_PONG_MESSAGE,\n} from \"effect-durable-agent/websocket\";\n\n/** Raw RPC shape decoded before admitting one session command. */\nexport interface EDASessionCommandRpcInput {\n readonly command: unknown;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape decoded before admitting a durable command/app-event batch. */\nexport interface EDASessionSubmitBatchRpcInput {\n readonly items: ReadonlyArray<unknown>;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape for read/destroy operations that only need a session id. */\nexport interface EDASessionScopedRpcInput {\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape for WebSocket event-stream upgrades with a resume cursor. */\nexport interface EDASessionEventsRpcInput {\n readonly afterSeq?: number;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Raw RPC shape decoded before blocking on one command terminal event. */\nexport interface EDASessionBlockOnCommandRpcInput {\n readonly afterSeq?: number;\n readonly commandId: string;\n readonly sessionId: string;\n readonly trace: unknown;\n}\n\n/** Constructor options for concrete app subclasses of the EDA Durable Object base. */\nexport type EDASessionDurableObjectOptions = Omit<\n EDASessionControllerOptions,\n \"background\" | \"getWebSockets\" | \"storage\"\n>;\n\n/** Resolve a concrete EDA session Durable Object binding by domain session id. */\nexport const getEDASessionDurableObjectByName = <T extends EDASessionDurableObject<object>>(\n namespace: DurableObjectNamespace<T>,\n sessionId: string,\n): DurableObjectStub<T> => namespace.getByName(sessionId);\n\n/**\n * Base class for raw Cloudflare Durable Object hosts of one EDA session.\n *\n * Concrete products should extend this class, pass provider/tool layers from\n * their own constructor, and register only that subclass in Wrangler. The base\n * class is deliberately not exported from `workers/app.ts` and owns no binding\n * name or routing convention.\n */\nexport abstract class EDASessionDurableObject<\n EnvType extends object = object,\n> extends DurableObject<EnvType> {\n readonly #controller: EDASessionController;\n\n protected constructor(\n ctx: DurableObjectState,\n env: EnvType,\n options: EDASessionDurableObjectOptions,\n ) {\n super(ctx, env);\n const storage = toEDASessionStorage(this.ctx.storage);\n this.#controller = new EDASessionController({\n ...options,\n background: this.ctx,\n getWebSockets: () => this.ctx.getWebSockets(),\n storage,\n });\n\n // Answer client liveness pings at the runtime layer so idle sockets never\n // wake a hibernated object.\n this.ctx.setWebSocketAutoResponse(\n new WebSocketRequestResponsePair(EDA_WEB_SOCKET_PING_MESSAGE, EDA_WEB_SOCKET_PONG_MESSAGE),\n );\n\n this.ctx.blockConcurrencyWhile(async () => {\n await Effect.runPromise(EDASessionController.migrate(storage));\n });\n }\n\n async fetch(request: Request): Promise<Response> {\n const upgrade = request.headers.get(\"Upgrade\");\n if (upgrade?.toLowerCase() !== \"websocket\") {\n return new Response(\"Expected WebSocket upgrade.\", { status: 426 });\n }\n\n const url = new URL(request.url);\n const sessionIdRaw = url.searchParams.get(\"sessionId\");\n if (sessionIdRaw === null) {\n return new Response(\"Missing sessionId.\", { status: 400 });\n }\n const afterSeq = Number(url.searchParams.get(\"afterSeq\") ?? \"0\");\n if (!Number.isInteger(afterSeq) || afterSeq < 0) {\n return new Response(\"Invalid afterSeq.\", { status: 400 });\n }\n\n const pair = new WebSocketPair();\n const client = pair[0];\n const server = pair[1];\n this.ctx.acceptWebSocket(server);\n await this.#controller.acceptEventWebSocket({\n afterSeq: SequenceNumber.make(afterSeq),\n sessionId: this.parseSessionId(sessionIdRaw),\n trace: traceMetadataFromRequest(request),\n webSocket: server,\n });\n return new Response(null, { status: 101, webSocket: client });\n }\n\n async submit(input: EDASessionCommandRpcInput): Promise<CommittedDurableEventValue> {\n const committed = await this.#controller.submit({\n command: await decodeCommand(input.command),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed);\n }\n\n async submitBatch(\n input: EDASessionSubmitBatchRpcInput,\n ): Promise<ReadonlyArray<CommittedDurableEventValue>> {\n const committed = await this.#controller.submitBatch({\n items: await decodeSubmittables(input.items),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return committed.map(encodeEdaRpcCommittedDurableEvent);\n }\n\n async submitAndBlock(input: EDASessionCommandRpcInput): Promise<CommittedCommandTerminalEvent> {\n const committed = await this.#controller.submitAndBlock({\n command: await decodeCommand(input.command),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed) as CommittedCommandTerminalEvent;\n }\n\n async blockOnCommand(\n input: EDASessionBlockOnCommandRpcInput,\n ): Promise<CommittedCommandTerminalEvent> {\n const committed = await this.#controller.blockOnCommand({\n ...(input.afterSeq === undefined ? {} : { afterSeq: SequenceNumber.make(input.afterSeq) }),\n commandId: CommandId.make(input.commandId),\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcCommittedDurableEvent(committed) as CommittedCommandTerminalEvent;\n }\n\n async snapshot(input: EDASessionScopedRpcInput): Promise<EDASessionSnapshot> {\n const snapshot = await this.#controller.snapshot({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n return encodeEdaRpcSessionSnapshot(snapshot);\n }\n\n async messages(\n input: EDASessionScopedRpcInput,\n ): Promise<ReadonlyArray<DurableTranscriptMessage>> {\n return await this.#controller.messages({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n }\n\n async webSocketMessage(webSocket: WebSocket, message: string | ArrayBuffer): Promise<void> {\n await this.#controller.webSocketMessage(webSocket, message);\n }\n\n async webSocketClose(\n webSocket: WebSocket,\n _code: number,\n _reason: string,\n _wasClean: boolean,\n ): Promise<void> {\n this.#controller.webSocketClose(webSocket);\n }\n\n async webSocketError(webSocket: WebSocket, _error: unknown): Promise<void> {\n this.#controller.webSocketError(webSocket);\n }\n\n async destroySession(input: EDASessionScopedRpcInput): Promise<void> {\n await this.#controller.destroy({\n sessionId: this.parseSessionId(input.sessionId),\n trace: decodeTraceMetadata(input.trace),\n });\n await this.ctx.storage.deleteAlarm();\n await this.ctx.storage.deleteAll();\n }\n\n async alarm(): Promise<void> {\n const sessionId = this.sessionIdFromObjectName();\n await this.#controller.alarm(\n sessionId === undefined ? undefined : { sessionId, trace: makeRootEDATraceMetadata() },\n );\n }\n\n private parseSessionId(input: string): SessionId {\n const sessionId = SessionId.make(input);\n const objectName = this.ctx.id.name;\n if (objectName !== undefined && objectName !== sessionId) {\n throw new Error(`EDA Durable Object name ${objectName} cannot serve session ${sessionId}`);\n }\n return sessionId;\n }\n\n private sessionIdFromObjectName(): SessionId | undefined {\n return this.ctx.id.name === undefined ? undefined : SessionId.make(this.ctx.id.name);\n }\n}\n\nconst toEDASessionStorage = (storage: DurableObjectStorage): EDASessionDurableObjectStorage => {\n // SAFETY: the adapter interface is the exact SQL, transaction, and alarm subset\n // of DurableObjectStorage; its looser row generic also supports test implementations.\n return storage as EDASessionDurableObjectStorage;\n};\n\nconst decodeTraceMetadata = (input: unknown): EDATraceMetadata =>\n Schema.decodeUnknownSync(EDATraceMetadata)(input);\n\nconst traceMetadataFromRequest = (request: Request): EDATraceMetadata => {\n const parent = parseEDATraceparent(\n request.headers.get(\"traceparent\"),\n request.headers.get(\"tracestate\"),\n );\n return parent === null ? makeRootEDATraceMetadata() : makeEDATraceMetadataFromParent(parent);\n};\n\n/** Convenience config constructor for subclasses that need only provider/model ids. */\nexport const edaRuntimeConfig = (input: {\n readonly maxToolCallsPerRun?: number;\n readonly modelId: string;\n readonly provider: string;\n readonly systemPrompt?: string;\n}): EDARuntimeConfig => ({\n modelSelection: { modelId: input.modelId, provider: input.provider },\n ...(input.maxToolCallsPerRun === undefined\n ? {}\n : { maxToolCallsPerRun: input.maxToolCallsPerRun }),\n ...(input.systemPrompt === undefined ? {} : { systemPrompt: input.systemPrompt }),\n});\n\n/** Encode committed events into structured-clone-safe Durable Object RPC payloads. */\nexport const encodeEdaRpcCommittedDurableEvent = (\n event: CommittedDurableEventValue,\n): CommittedDurableEventValue => ({\n position: event.position,\n event: encodeEdaRpcDurableEvent(event.event),\n});\n\n/** Encode runtime snapshots into structured-clone-safe Durable Object RPC payloads. */\nexport const encodeEdaRpcSessionSnapshot = (snapshot: EDASessionSnapshot): EDASessionSnapshot => ({\n ...snapshot,\n reducerStates: new Map(snapshot.reducerStates),\n state: {\n ...snapshot.state,\n commands: new Map(\n Array.from(snapshot.state.commands, ([commandId, record]) => [\n commandId,\n encodeCommandCarrierForRpc(record),\n ]),\n ),\n commandQueues: {\n ...snapshot.state.commandQueues,\n activeControlCommands: snapshot.state.commandQueues.activeControlCommands.map(\n encodeCommandCarrierForRpc,\n ),\n pendingCommands: snapshot.state.commandQueues.pendingCommands.map(encodeCommandCarrierForRpc),\n queuedCommands: snapshot.state.commandQueues.queuedCommands.map(encodeCommandCarrierForRpc),\n },\n },\n});\n\nconst encodeCommandCarrierForRpc = <A extends { readonly command?: EDACommandValue }>(\n value: A,\n): A =>\n value.command === undefined\n ? value\n : {\n ...value,\n command: Schema.encodeSync(EDACommand)(value.command) as EDACommandValue,\n };\n\nconst decodeCommand = (input: unknown): Promise<EDACommandValue> =>\n Effect.runPromise(decodeEdaRpcCommand(input));\n\nconst decodeSubmittables = (\n input: ReadonlyArray<unknown>,\n): Promise<ReadonlyArray<EDASubmittable>> => Effect.runPromise(decodeEdaRpcSubmittables(input));\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,6 @@
1
- import { _ as encodeEdaRpcCommand, a as EDASessionDurableObjectHostOptions, b as makeEDADurableObjectRuntimeLayer, c as EDASessionScopedInput, d as EDAToolRegistryFactory, f as EDAToolkitFactory, g as decodeEdaRpcSubmittables, h as decodeEdaRpcSubmittable, i as EDASessionDurableObjectHost, l as EDASessionSubmitBatchInput, m as decodeEdaRpcCommand, n as EDAOpenAiModelConfig, o as EDASessionDurableObjectStorage, p as EDATracerFactory, r as EDASessionBlockOnCommandInput, s as EDASessionEventsInput, t as EDADurableObjectRuntimeLayerOptions, u as EDASessionSubmitInput, v as encodeEdaRpcSubmittables, y as makeEDADurableObjectOpenAiModelLayer } from "./durable-object-runtime-Dehm7PsO.js";
2
1
  import { i as DurableObjectSqlStorage, n as DurableObjectSessionStoreOptions, r as DurableObjectSqlCursor, t as DurableObjectSessionStorage } from "./durable-object-storage-DCL2IyUe.js";
2
+ import { a as EDASessionScopedInput, c as EDADurableObjectRuntimeLayerOptions, i as EDASessionEventsInput, l as EDASessionDurableObjectStorage, n as EDASessionController, o as EDASessionSubmitBatchInput, r as EDASessionControllerOptions, s as EDASessionSubmitInput, t as EDASessionBlockOnCommandInput, u as makeEDADurableObjectRuntimeLayer } from "./session-controller-BSMHhS_l.js";
3
3
  import { EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionDurableObject, EDASessionDurableObjectOptions, EDASessionEventsRpcInput, EDASessionScopedRpcInput, EDASessionSubmitBatchRpcInput, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName } from "./durable-object.js";
4
- export * from "effect-durable-agent/host/websocket-protocol";
5
-
6
- //#region src/index.d.ts
7
- declare namespace index_d_exports {
8
- export { DurableObjectSessionStorage, DurableObjectSessionStoreOptions, DurableObjectSqlCursor, DurableObjectSqlStorage, EDADurableObjectRuntimeLayerOptions, EDAOpenAiModelConfig, EDASessionBlockOnCommandInput, EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionDurableObject, EDASessionDurableObjectHost, EDASessionDurableObjectHostOptions, EDASessionDurableObjectOptions, EDASessionDurableObjectStorage, EDASessionEventsInput, EDASessionEventsRpcInput, EDASessionScopedInput, EDASessionScopedRpcInput, EDASessionSubmitBatchInput, EDASessionSubmitBatchRpcInput, EDASessionSubmitInput, EDAToolRegistryFactory, EDAToolkitFactory, EDATracerFactory, decodeEdaRpcCommand, decodeEdaRpcSubmittable, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectRuntimeLayer };
9
- }
10
- //#endregion
11
- export { DurableObjectSessionStorage, DurableObjectSessionStoreOptions, DurableObjectSqlCursor, DurableObjectSqlStorage, EDADurableObjectRuntimeLayerOptions, EDAOpenAiModelConfig, EDASessionBlockOnCommandInput, EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionDurableObject, EDASessionDurableObjectHost, EDASessionDurableObjectHostOptions, EDASessionDurableObjectOptions, EDASessionDurableObjectStorage, EDASessionEventsInput, EDASessionEventsRpcInput, EDASessionScopedInput, EDASessionScopedRpcInput, EDASessionSubmitBatchInput, EDASessionSubmitBatchRpcInput, EDASessionSubmitInput, EDAToolRegistryFactory, EDAToolkitFactory, EDATracerFactory, decodeEdaRpcCommand, decodeEdaRpcSubmittable, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectRuntimeLayer };
12
- //# sourceMappingURL=index.d.ts.map
4
+ import { EDAOpenAiModelConfig, makeEDADurableObjectOpenAiModelLayer } from "./openai.js";
5
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables } from "./rpc.js";
6
+ export { DurableObjectSessionStorage, DurableObjectSessionStoreOptions, DurableObjectSqlCursor, DurableObjectSqlStorage, EDADurableObjectRuntimeLayerOptions, EDAOpenAiModelConfig, EDASessionBlockOnCommandInput, EDASessionBlockOnCommandRpcInput, EDASessionCommandRpcInput, EDASessionController, EDASessionControllerOptions, EDASessionDurableObject, EDASessionDurableObjectOptions, EDASessionDurableObjectStorage, EDASessionEventsInput, EDASessionEventsRpcInput, EDASessionScopedInput, EDASessionScopedRpcInput, EDASessionSubmitBatchInput, EDASessionSubmitBatchRpcInput, EDASessionSubmitInput, decodeEdaRpcCommand, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectRuntimeLayer };
package/dist/index.js CHANGED
@@ -1,25 +1,6 @@
1
- import { n as __reExport, t as __exportAll } from "./chunk-BBjsoOtd.js";
2
- import { a as getEDASessionDurableObjectByName, i as encodeEdaRpcSessionSnapshot, n as edaRuntimeConfig, r as encodeEdaRpcCommittedDurableEvent, t as EDASessionDurableObject } from "./durable-object-CjWWNyek.js";
3
- import { a as encodeEdaRpcCommand, c as makeEDADurableObjectRuntimeLayer, i as decodeEdaRpcSubmittables, n as decodeEdaRpcCommand, o as encodeEdaRpcSubmittables, r as decodeEdaRpcSubmittable, s as makeEDADurableObjectOpenAiModelLayer, t as EDASessionDurableObjectHost } from "./durable-object-runtime-LgOJbsVm.js";
4
- import { t as websocket_protocol_exports } from "./websocket-protocol.js";
5
- import "./durable-object-storage.js";
6
- export * from "effect-durable-agent/host/websocket-protocol";
7
- __reExport(/* @__PURE__ */ __exportAll({
8
- EDASessionDurableObject: () => EDASessionDurableObject,
9
- EDASessionDurableObjectHost: () => EDASessionDurableObjectHost,
10
- decodeEdaRpcCommand: () => decodeEdaRpcCommand,
11
- decodeEdaRpcSubmittable: () => decodeEdaRpcSubmittable,
12
- decodeEdaRpcSubmittables: () => decodeEdaRpcSubmittables,
13
- edaRuntimeConfig: () => edaRuntimeConfig,
14
- encodeEdaRpcCommand: () => encodeEdaRpcCommand,
15
- encodeEdaRpcCommittedDurableEvent: () => encodeEdaRpcCommittedDurableEvent,
16
- encodeEdaRpcSessionSnapshot: () => encodeEdaRpcSessionSnapshot,
17
- encodeEdaRpcSubmittables: () => encodeEdaRpcSubmittables,
18
- getEDASessionDurableObjectByName: () => getEDASessionDurableObjectByName,
19
- makeEDADurableObjectOpenAiModelLayer: () => makeEDADurableObjectOpenAiModelLayer,
20
- makeEDADurableObjectRuntimeLayer: () => makeEDADurableObjectRuntimeLayer
21
- }), websocket_protocol_exports);
22
- //#endregion
23
- export { EDASessionDurableObject, EDASessionDurableObjectHost, decodeEdaRpcCommand, decodeEdaRpcSubmittable, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectRuntimeLayer };
24
-
25
- //# sourceMappingURL=index.js.map
1
+ import { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables } from "./rpc.js";
2
+ import { n as makeEDADurableObjectRuntimeLayer, t as EDASessionController } from "./session-controller-Bo7DyydT.js";
3
+ import { EDASessionDurableObject, edaRuntimeConfig, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcSessionSnapshot, getEDASessionDurableObjectByName } from "./durable-object.js";
4
+ import "./storage.js";
5
+ import { makeEDADurableObjectOpenAiModelLayer } from "./openai.js";
6
+ export { EDASessionController, EDASessionDurableObject, decodeEdaRpcCommand, decodeEdaRpcSubmittables, edaRuntimeConfig, encodeEdaRpcCommand, encodeEdaRpcCommittedDurableEvent, encodeEdaRpcDurableEvent, encodeEdaRpcSessionSnapshot, encodeEdaRpcSubmittables, getEDASessionDurableObjectByName, makeEDADurableObjectOpenAiModelLayer, makeEDADurableObjectRuntimeLayer };
@@ -0,0 +1,18 @@
1
+ import * as Layer from "effect/Layer";
2
+ import { OpenAiLanguageModel } from "@effect/ai-openai";
3
+ import * as LanguageModel from "effect/unstable/ai/LanguageModel";
4
+
5
+ //#region src/providers/openai.d.ts
6
+ /** Provider request defaults accepted by EDA's OpenAI Responses model adapter. */
7
+ type EDAOpenAiModelConfig = Omit<typeof OpenAiLanguageModel.Config.Service, "model">;
8
+ /** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */
9
+ declare const makeEDADurableObjectOpenAiModelLayer: (options: {
10
+ readonly aiGateway?: AiGateway;
11
+ readonly apiKey?: string;
12
+ readonly apiUrl?: string;
13
+ readonly config?: EDAOpenAiModelConfig;
14
+ readonly modelId: string;
15
+ }) => Layer.Layer<LanguageModel.LanguageModel>;
16
+ //#endregion
17
+ export { EDAOpenAiModelConfig, makeEDADurableObjectOpenAiModelLayer };
18
+ //# sourceMappingURL=openai.d.ts.map
package/dist/openai.js ADDED
@@ -0,0 +1,59 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import { OpenAiClient, OpenAiLanguageModel } from "@effect/ai-openai";
4
+ import * as Redacted from "effect/Redacted";
5
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
6
+ import "effect/unstable/http/HttpBody";
7
+ import * as HttpClient from "effect/unstable/http/HttpClient";
8
+ import * as HttpClientError from "effect/unstable/http/HttpClientError";
9
+ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
10
+ //#region src/providers/openai.ts
11
+ /** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */
12
+ const makeEDADurableObjectOpenAiModelLayer = (options) => {
13
+ const apiKey = optionalNonEmptyString(options.apiKey);
14
+ if (apiKey === void 0 && options.aiGateway === void 0) throw new Error("OPENAI_API_KEY is required unless the EDA OpenAI model layer is backed by a Cloudflare AI Gateway binding.");
15
+ const httpClient = options.aiGateway === void 0 ? FetchHttpClient.layer : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));
16
+ const client = OpenAiClient.layer({
17
+ ...apiKey === void 0 ? {} : { apiKey: Redacted.make(apiKey) },
18
+ ...options.apiUrl === void 0 ? {} : { apiUrl: options.apiUrl }
19
+ }).pipe(Layer.provide(httpClient));
20
+ return OpenAiLanguageModel.layer({
21
+ model: options.modelId,
22
+ ...options.config === void 0 ? {} : { config: options.config }
23
+ }).pipe(Layer.provide(client));
24
+ };
25
+ const makeAiGatewayHttpClient = (gateway) => HttpClient.make((request, url, signal) => Effect.tryPromise({
26
+ try: async () => {
27
+ const response = await gateway.run({
28
+ endpoint: endpointFromUrl(url),
29
+ headers: Object.fromEntries(Object.entries(request.headers).filter((entry) => typeof entry[1] === "string" && entry[0].toLowerCase() !== "host")),
30
+ provider: "openai",
31
+ query: requestBody(request.body)
32
+ }, { signal });
33
+ return HttpClientResponse.fromWeb(request, response);
34
+ },
35
+ catch: (cause) => new HttpClientError.HttpClientError({ reason: new HttpClientError.TransportError({
36
+ request,
37
+ cause,
38
+ description: "Cloudflare AI Gateway request failed"
39
+ }) })
40
+ }));
41
+ const endpointFromUrl = (url) => {
42
+ return `${url.pathname.startsWith("/v1/") ? url.pathname.slice(4) : url.pathname.replace(/^\/+/, "")}${url.search}`;
43
+ };
44
+ const requestBody = (body) => {
45
+ switch (body._tag) {
46
+ case "Empty": return;
47
+ case "Raw": return body.body;
48
+ case "Uint8Array": return JSON.parse(new TextDecoder().decode(body.body));
49
+ default: throw new Error(`Unsupported OpenAI request body for Cloudflare AI Gateway: ${body._tag}`);
50
+ }
51
+ };
52
+ const optionalNonEmptyString = (value) => {
53
+ const trimmed = value?.trim();
54
+ return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
55
+ };
56
+ //#endregion
57
+ export { makeEDADurableObjectOpenAiModelLayer };
58
+
59
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","names":[],"sources":["../src/providers/openai.ts"],"sourcesContent":["import { OpenAiClient, OpenAiLanguageModel } from \"@effect/ai-openai\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Redacted from \"effect/Redacted\";\nimport type * as LanguageModel from \"effect/unstable/ai/LanguageModel\";\nimport * as FetchHttpClient from \"effect/unstable/http/FetchHttpClient\";\nimport * as HttpBody from \"effect/unstable/http/HttpBody\";\nimport * as HttpClient from \"effect/unstable/http/HttpClient\";\nimport * as HttpClientError from \"effect/unstable/http/HttpClientError\";\nimport * as HttpClientResponse from \"effect/unstable/http/HttpClientResponse\";\n\n/** Provider request defaults accepted by EDA's OpenAI Responses model adapter. */\nexport type EDAOpenAiModelConfig = Omit<typeof OpenAiLanguageModel.Config.Service, \"model\">;\n\n/** OpenAI-backed Effect AI model layer for a Cloudflare Worker. */\nexport const makeEDADurableObjectOpenAiModelLayer = (options: {\n readonly aiGateway?: AiGateway;\n readonly apiKey?: string;\n readonly apiUrl?: string;\n readonly config?: EDAOpenAiModelConfig;\n readonly modelId: string;\n}): Layer.Layer<LanguageModel.LanguageModel> => {\n const apiKey = optionalNonEmptyString(options.apiKey);\n if (apiKey === undefined && options.aiGateway === undefined) {\n throw new Error(\n \"OPENAI_API_KEY is required unless the EDA OpenAI model layer is backed by a Cloudflare AI Gateway binding.\",\n );\n }\n\n const httpClient =\n options.aiGateway === undefined\n ? FetchHttpClient.layer\n : Layer.succeed(HttpClient.HttpClient, makeAiGatewayHttpClient(options.aiGateway));\n const client = OpenAiClient.layer({\n ...(apiKey === undefined ? {} : { apiKey: Redacted.make(apiKey) }),\n ...(options.apiUrl === undefined ? {} : { apiUrl: options.apiUrl }),\n }).pipe(Layer.provide(httpClient));\n\n return OpenAiLanguageModel.layer({\n model: options.modelId,\n ...(options.config === undefined ? {} : { config: options.config }),\n }).pipe(Layer.provide(client));\n};\n\nconst makeAiGatewayHttpClient = (gateway: AiGateway): HttpClient.HttpClient =>\n HttpClient.make((request, url, signal) =>\n Effect.tryPromise({\n try: async () => {\n const response = await gateway.run(\n {\n endpoint: endpointFromUrl(url),\n headers: Object.fromEntries(\n Object.entries(request.headers).filter(\n (entry): entry is [string, string] =>\n typeof entry[1] === \"string\" && entry[0].toLowerCase() !== \"host\",\n ),\n ),\n provider: \"openai\",\n query: requestBody(request.body),\n },\n { signal },\n );\n return HttpClientResponse.fromWeb(request, response);\n },\n catch: (cause) =>\n new HttpClientError.HttpClientError({\n reason: new HttpClientError.TransportError({\n request,\n cause,\n description: \"Cloudflare AI Gateway request failed\",\n }),\n }),\n }),\n );\n\nconst endpointFromUrl = (url: URL): string => {\n const path = url.pathname.startsWith(\"/v1/\")\n ? url.pathname.slice(\"/v1/\".length)\n : url.pathname.replace(/^\\/+/, \"\");\n return `${path}${url.search}`;\n};\n\nconst requestBody = (body: HttpBody.HttpBody): unknown => {\n switch (body._tag) {\n case \"Empty\":\n return undefined;\n case \"Raw\":\n return body.body;\n case \"Uint8Array\":\n return JSON.parse(new TextDecoder().decode(body.body)) as unknown;\n default:\n throw new Error(`Unsupported OpenAI request body for Cloudflare AI Gateway: ${body._tag}`);\n }\n};\n\nconst optionalNonEmptyString = (value: string | undefined): string | undefined => {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n};\n"],"mappings":";;;;;;;;;;;AAeA,MAAa,wCAAwC,YAML;CAC9C,MAAM,SAAS,uBAAuB,QAAQ,MAAM;CACpD,IAAI,WAAW,KAAA,KAAa,QAAQ,cAAc,KAAA,GAChD,MAAM,IAAI,MACR,4GACF;CAGF,MAAM,aACJ,QAAQ,cAAc,KAAA,IAClB,gBAAgB,QAChB,MAAM,QAAQ,WAAW,YAAY,wBAAwB,QAAQ,SAAS,CAAC;CACrF,MAAM,SAAS,aAAa,MAAM;EAChC,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,SAAS,KAAK,MAAM,EAAE;EAChE,GAAI,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;CACnE,CAAC,EAAE,KAAK,MAAM,QAAQ,UAAU,CAAC;CAEjC,OAAO,oBAAoB,MAAM;EAC/B,OAAO,QAAQ;EACf,GAAI,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO;CACnE,CAAC,EAAE,KAAK,MAAM,QAAQ,MAAM,CAAC;AAC/B;AAEA,MAAM,2BAA2B,YAC/B,WAAW,MAAM,SAAS,KAAK,WAC7B,OAAO,WAAW;CAChB,KAAK,YAAY;EACf,MAAM,WAAW,MAAM,QAAQ,IAC7B;GACE,UAAU,gBAAgB,GAAG;GAC7B,SAAS,OAAO,YACd,OAAO,QAAQ,QAAQ,OAAO,EAAE,QAC7B,UACC,OAAO,MAAM,OAAO,YAAY,MAAM,GAAG,YAAY,MAAM,MAC/D,CACF;GACA,UAAU;GACV,OAAO,YAAY,QAAQ,IAAI;EACjC,GACA,EAAE,OAAO,CACX;EACA,OAAO,mBAAmB,QAAQ,SAAS,QAAQ;CACrD;CACA,QAAQ,UACN,IAAI,gBAAgB,gBAAgB,EAClC,QAAQ,IAAI,gBAAgB,eAAe;EACzC;EACA;EACA,aAAa;CACf,CAAC,EACH,CAAC;AACL,CAAC,CACH;AAEF,MAAM,mBAAmB,QAAqB;CAI5C,OAAO,GAHM,IAAI,SAAS,WAAW,MAAM,IACvC,IAAI,SAAS,MAAM,CAAa,IAChC,IAAI,SAAS,QAAQ,QAAQ,EAAE,IAClB,IAAI;AACvB;AAEA,MAAM,eAAe,SAAqC;CACxD,QAAQ,KAAK,MAAb;EACE,KAAK,SACH;EACF,KAAK,OACH,OAAO,KAAK;EACd,KAAK,cACH,OAAO,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;EACvD,SACE,MAAM,IAAI,MAAM,8DAA8D,KAAK,MAAM;CAC7F;AACF;AAEA,MAAM,0BAA0B,UAAkD;CAChF,MAAM,UAAU,OAAO,KAAK;CAC5B,OAAO,YAAY,KAAA,KAAa,QAAQ,WAAW,IAAI,KAAA,IAAY;AACrE"}
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import { EDACommand } from "effect-durable-agent/types/commands";
4
+ import { DurableEventEnvelope } from "effect-durable-agent/types/events";
5
+ import { EDASubmittable } from "effect-durable-agent/services/session-state";
6
+
7
+ //#region src/rpc-codec.d.ts
8
+ /** Encode one durable event into a structured-clone-safe Durable Object RPC value. */
9
+ declare const encodeEdaRpcDurableEvent: (event: DurableEventEnvelope) => DurableEventEnvelope;
10
+ declare const encodeEdaRpcCommand: (input: EDACommand) => unknown;
11
+ declare const encodeEdaRpcSubmittables: (input: ReadonlyArray<EDASubmittable>) => ReadonlyArray<unknown>;
12
+ declare const decodeEdaRpcCommand: (input: unknown) => Effect.Effect<EDACommand, Schema.SchemaError>;
13
+ declare const decodeEdaRpcSubmittables: (input: ReadonlyArray<unknown>) => Effect.Effect<ReadonlyArray<EDASubmittable>, Schema.SchemaError>;
14
+ //#endregion
15
+ export { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables };
16
+ //# sourceMappingURL=rpc.d.ts.map
package/dist/rpc.js ADDED
@@ -0,0 +1,26 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import * as Exit from "effect/Exit";
4
+ import { EDACommand } from "effect-durable-agent/types/commands";
5
+ import { DurableEventEnvelope, EDADurableEvent, effectDurableAgentNamespace } from "effect-durable-agent/types/events";
6
+ //#region src/rpc-codec.ts
7
+ /** Encode one durable event into a structured-clone-safe Durable Object RPC value. */
8
+ const encodeEdaRpcDurableEvent = (event) => {
9
+ const encoded = event.namespace === effectDurableAgentNamespace ? Schema.encodeSync(EDADurableEvent)(Schema.decodeUnknownSync(EDADurableEvent)(event)) : Schema.encodeSync(DurableEventEnvelope)(event);
10
+ return Schema.decodeUnknownSync(DurableEventEnvelope)(encoded);
11
+ };
12
+ const EDARpcSubmittable = Schema.Union([EDACommand, DurableEventEnvelope]);
13
+ const encodeEdaRpcCommand = (input) => Schema.encodeSync(EDACommand)(input);
14
+ const encodeEdaRpcSubmittables = (input) => input.map((item) => Schema.encodeSync(EDARpcSubmittable)(item));
15
+ const decodeEdaRpcCommand = (input) => Schema.decodeUnknownExit(EDACommand)(input).pipe(Exit.match({
16
+ onFailure: Effect.failCause,
17
+ onSuccess: Effect.succeed
18
+ }));
19
+ const decodeEdaRpcSubmittables = (input) => Effect.forEach(input, (item) => Schema.decodeUnknownExit(EDARpcSubmittable)(item).pipe(Exit.match({
20
+ onFailure: Effect.failCause,
21
+ onSuccess: Effect.succeed
22
+ })));
23
+ //#endregion
24
+ export { decodeEdaRpcCommand, decodeEdaRpcSubmittables, encodeEdaRpcCommand, encodeEdaRpcDurableEvent, encodeEdaRpcSubmittables };
25
+
26
+ //# sourceMappingURL=rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.js","names":[],"sources":["../src/rpc-codec.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Schema from \"effect/Schema\";\n\nimport { EDACommand } from \"effect-durable-agent/types/commands\";\nimport type { EDASubmittable } from \"effect-durable-agent/services/session-state\";\nimport {\n DurableEventEnvelope,\n EDADurableEvent,\n effectDurableAgentNamespace,\n} from \"effect-durable-agent/types/events\";\n\n/** Encode one durable event into a structured-clone-safe Durable Object RPC value. */\nexport const encodeEdaRpcDurableEvent = (event: DurableEventEnvelope): DurableEventEnvelope => {\n const encoded =\n event.namespace === effectDurableAgentNamespace\n ? Schema.encodeSync(EDADurableEvent)(Schema.decodeUnknownSync(EDADurableEvent)(event))\n : Schema.encodeSync(DurableEventEnvelope)(event);\n return Schema.decodeUnknownSync(DurableEventEnvelope)(encoded);\n};\n\nconst EDARpcSubmittable = Schema.Union([EDACommand, DurableEventEnvelope]);\n\nexport const encodeEdaRpcCommand = (input: EDACommand): unknown =>\n Schema.encodeSync(EDACommand)(input);\n\nexport const encodeEdaRpcSubmittables = (\n input: ReadonlyArray<EDASubmittable>,\n): ReadonlyArray<unknown> => input.map((item) => Schema.encodeSync(EDARpcSubmittable)(item));\n\nexport const decodeEdaRpcCommand = (\n input: unknown,\n): Effect.Effect<EDACommand, Schema.SchemaError> =>\n Schema.decodeUnknownExit(EDACommand)(input).pipe(\n Exit.match({ onFailure: Effect.failCause, onSuccess: Effect.succeed }),\n );\n\nexport const decodeEdaRpcSubmittables = (\n input: ReadonlyArray<unknown>,\n): Effect.Effect<ReadonlyArray<EDASubmittable>, Schema.SchemaError> =>\n Effect.forEach(input, (item) =>\n Schema.decodeUnknownExit(EDARpcSubmittable)(item).pipe(\n Exit.match({ onFailure: Effect.failCause, onSuccess: Effect.succeed }),\n ),\n );\n"],"mappings":";;;;;;;AAaA,MAAa,4BAA4B,UAAsD;CAC7F,MAAM,UACJ,MAAM,cAAc,8BAChB,OAAO,WAAW,eAAe,EAAE,OAAO,kBAAkB,eAAe,EAAE,KAAK,CAAC,IACnF,OAAO,WAAW,oBAAoB,EAAE,KAAK;CACnD,OAAO,OAAO,kBAAkB,oBAAoB,EAAE,OAAO;AAC/D;AAEA,MAAM,oBAAoB,OAAO,MAAM,CAAC,YAAY,oBAAoB,CAAC;AAEzE,MAAa,uBAAuB,UAClC,OAAO,WAAW,UAAU,EAAE,KAAK;AAErC,MAAa,4BACX,UAC2B,MAAM,KAAK,SAAS,OAAO,WAAW,iBAAiB,EAAE,IAAI,CAAC;AAE3F,MAAa,uBACX,UAEA,OAAO,kBAAkB,UAAU,EAAE,KAAK,EAAE,KAC1C,KAAK,MAAM;CAAE,WAAW,OAAO;CAAW,WAAW,OAAO;AAAQ,CAAC,CACvE;AAEF,MAAa,4BACX,UAEA,OAAO,QAAQ,QAAQ,SACrB,OAAO,kBAAkB,iBAAiB,EAAE,IAAI,EAAE,KAChD,KAAK,MAAM;CAAE,WAAW,OAAO;CAAW,WAAW,OAAO;AAAQ,CAAC,CACvE,CACF"}