effect-cf 0.34.0 → 0.36.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.36.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#132](https://github.com/danieljvdm/effect-cf/pull/132) [`d2e53a9`](https://github.com/danieljvdm/effect-cf/commit/d2e53a9fab38432a472c9f46a74b87cc89474103) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add the `effect-cf/mcp` entry point for serving an Effect AI `Toolkit` over MCP from a Worker fetch handler. `Mcp.fromToolkit(toolkit, { name, version, route })` wraps the MCP SDK's stateless Streamable HTTP handler (`createMcpHandler` from `@modelcontextprotocol/server`) together with the Worker edge concerns remote MCP deployments expect (CORS plus `Host` and `Origin` validation with localhost/`workers.dev` defaults): requests on the configured route (default `/mcp`) are answered by a fresh MCP server exposing every toolkit tool, and other requests fall through with `Option.none()` so the rest of the fetch handler (for example an `HttpApi` router) keeps serving them. Tool input/output schemas are derived from each tool's Effect `Schema`, tool calls run through the Worker's Effect runtime with the toolkit handler layer, declared tool failures become `isError` tool results, and invalid arguments are rejected by the tool's parameters schema. The `@modelcontextprotocol/server` package is a new optional peer dependency, needed only when importing `effect-cf/mcp`.
8
+
9
+ ## 0.35.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#130](https://github.com/danieljvdm/effect-cf/pull/130) [`6139dc2`](https://github.com/danieljvdm/effect-cf/commit/6139dc2507043abfd81b6abeeff87bfd5b8bb31a) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Keep Worker event-layer resources alive through Effect HTTP response streams, give background work and Workflow steps their own resource scopes, and join interrupted native callbacks before returning. Durable Object transactions now await rollback and cleanup on interruption, including accidental asynchronous work in synchronous transactions. Queue handlers also schedule telemetry flushes after success or failure.
14
+
15
+ Propagate fetch cancellation to service bindings and Durable Objects. Report scoped RPC failures, malformed embedding responses, and invalid Analytics Engine URLs through tagged errors. Computer workspace acquisition now exposes `WorkspaceAcquireError` in its layer error channel. Typed WebSocket attachments enforce their declared shape, and fresh RPC connections replace reserved metadata without losing application fields.
16
+
17
+ Support modern WebTransport datagram writers and buffered-datagram options while retaining legacy compatibility. Release stream locks after cleanup and datagram writer locks when either peer closes. Failed or interrupted fallback candidates release their resources before selection continues.
18
+
3
19
  ## 0.34.0
4
20
 
5
21
  ### Minor Changes
@@ -1,5 +1,5 @@
1
1
  import { ComputerArtifactsClient } from "./ComputerArtifacts.mjs";
2
- import { t as DurableObjectState } from "./DurableObjectState-CrEunfQ-.mjs";
2
+ import { t as DurableObjectState } from "./DurableObjectState-CpgAmFqo.mjs";
3
3
  import { Context, Effect, Layer, Schema, Scope, Stream } from "effect";
4
4
  import { CommitView, GitClient, GitCloneOptions, GitCommitOptions, GitDiffOptions, GitFetchOptions, GitLogOptions, GitUpdateRefOptions } from "@cloudflare/computer/git";
5
5
  import { ExecSyncResult, RuntimeExecOptions, RuntimeGetOptions, RuntimeKillOptions, ShellValue, SkippedEntry, ThinkWorkspaceCompatibility, WorkspaceClient, WorkspaceRuntimeResult, WorkspaceRuntimeValue as WorkspaceRuntimeValue$1 } from "@cloudflare/computer";
@@ -56,6 +56,14 @@ declare const WorkspaceAssetsError_base: Schema.Class<WorkspaceAssetsError, Sche
56
56
  }>, import("effect/Cause").YieldableError>;
57
57
  /** Schema-serializable error for a workspace Assets operation. */
58
58
  declare class WorkspaceAssetsError extends WorkspaceAssetsError_base {}
59
+ declare const WorkspaceAcquireError_base: Schema.Class<WorkspaceAcquireError, Schema.TaggedStruct<"WorkspaceAcquireError", {
60
+ readonly operation: Schema.String;
61
+ readonly message: Schema.String;
62
+ readonly code: Schema.optionalKey<Schema.String>;
63
+ readonly cause: Schema.optionalKey<Schema.Defect>;
64
+ }>, import("effect/Cause").YieldableError>;
65
+ /** Schema-serializable error while acquiring the Computer workspace client. */
66
+ declare class WorkspaceAcquireError extends WorkspaceAcquireError_base {}
59
67
  type WorkspaceReadFileOptions = Omit<CloudflareReadFileOptions, "encoding">;
60
68
  type ReadTextFileOptions = WorkspaceReadFileOptions;
61
69
  type ListDirectoryOptions = CloudflareReaddirOptions;
@@ -264,7 +272,7 @@ declare class ComputerWorkspace extends ComputerWorkspace_base {
264
272
  * Acquires the single workspace registered by `withComputerWorkspace` and
265
273
  * releases its RPC client when the layer scope closes.
266
274
  */
267
- static readonly layer: Layer.Layer<ComputerWorkspace, never, DurableObjectState>;
275
+ static readonly layer: Layer.Layer<ComputerWorkspace, WorkspaceAcquireError, DurableObjectState>;
268
276
  }
269
277
  //#endregion
270
- export { ComputerWorkspace, ComputerWorkspaceAssets, ComputerWorkspaceFilesystem, ComputerWorkspaceGit, ComputerWorkspaceRuntime, ComputerWorkspaceService, type ComputerWorkspaceService as ComputerWorkspaceShape, ComputerWorkspaceThink, ListDirectoryOptions, ReadTextFileOptions, RemovePathOptions, WorkspaceAssetsError, WorkspaceDirectoryEntry, WorkspaceEntryStat, WorkspaceExec, WorkspaceExecChunk, WorkspaceExecCollect, WorkspaceExecEncoding, WorkspaceExecError, WorkspaceExecEvent, WorkspaceExecOptions, WorkspaceExecResult, WorkspaceExecRun, WorkspaceExecSignal, WorkspaceExecSkippedEntry, WorkspaceExecSyncResult, WorkspaceFindOptions, WorkspaceFoundEntry, WorkspaceFsError, WorkspaceGetExec, WorkspaceGetExecOptions, WorkspaceGitCheckoutOptions, WorkspaceGitCloneOptions, WorkspaceGitCommit, WorkspaceGitCommitOptions, WorkspaceGitDiffOptions, WorkspaceGitError, WorkspaceGitFetchOptions, WorkspaceGitFetchResult, WorkspaceGitLogOptions, WorkspaceGitPerson, WorkspaceGitUpdateRefOptions, WorkspaceGrepMatch, WorkspaceGrepOptions, WorkspaceMkdirOptions, WorkspaceReadEncoding, WorkspaceReadFile, WorkspaceReadFileOptions, WorkspaceRuntimeValue, WorkspaceWriteFileContent, WorkspaceWriteFileOptions, fromWorkspaceClient };
278
+ export { ComputerWorkspace, ComputerWorkspaceAssets, ComputerWorkspaceFilesystem, ComputerWorkspaceGit, ComputerWorkspaceRuntime, ComputerWorkspaceService, type ComputerWorkspaceService as ComputerWorkspaceShape, ComputerWorkspaceThink, ListDirectoryOptions, ReadTextFileOptions, RemovePathOptions, WorkspaceAcquireError, WorkspaceAssetsError, WorkspaceDirectoryEntry, WorkspaceEntryStat, WorkspaceExec, WorkspaceExecChunk, WorkspaceExecCollect, WorkspaceExecEncoding, WorkspaceExecError, WorkspaceExecEvent, WorkspaceExecOptions, WorkspaceExecResult, WorkspaceExecRun, WorkspaceExecSignal, WorkspaceExecSkippedEntry, WorkspaceExecSyncResult, WorkspaceFindOptions, WorkspaceFoundEntry, WorkspaceFsError, WorkspaceGetExec, WorkspaceGetExecOptions, WorkspaceGitCheckoutOptions, WorkspaceGitCloneOptions, WorkspaceGitCommit, WorkspaceGitCommitOptions, WorkspaceGitDiffOptions, WorkspaceGitError, WorkspaceGitFetchOptions, WorkspaceGitFetchResult, WorkspaceGitLogOptions, WorkspaceGitPerson, WorkspaceGitUpdateRefOptions, WorkspaceGrepMatch, WorkspaceGrepOptions, WorkspaceMkdirOptions, WorkspaceReadEncoding, WorkspaceReadFile, WorkspaceReadFileOptions, WorkspaceRuntimeValue, WorkspaceWriteFileContent, WorkspaceWriteFileOptions, fromWorkspaceClient };
@@ -1,4 +1,4 @@
1
- import { t as DurableObjectState } from "./DurableObjectState-De4ZojhY.mjs";
1
+ import { t as DurableObjectState } from "./DurableObjectState-DCh4PZMN.mjs";
2
2
  import { fromClient } from "./ComputerArtifacts.mjs";
3
3
  import { t as lookup } from "./ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs";
4
4
  import { Context, Effect, Layer, Predicate, Schema, Stream } from "effect";
@@ -37,6 +37,13 @@ var WorkspaceAssetsError = class extends Schema.TaggedError()("WorkspaceAssetsEr
37
37
  code: Schema.optionalKey(Schema.String),
38
38
  cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
39
39
  }) {};
40
+ /** Schema-serializable error while acquiring the Computer workspace client. */
41
+ var WorkspaceAcquireError = class extends Schema.TaggedError()("WorkspaceAcquireError", {
42
+ operation: Schema.String,
43
+ message: Schema.String,
44
+ code: Schema.optionalKey(Schema.String),
45
+ cause: Schema.optionalKey(Schema.Defect({ includeStack: true }))
46
+ }) {};
40
47
  const errorMessage = (cause) => cause instanceof Error ? cause.message : String(cause);
41
48
  const errorCode = (cause) => {
42
49
  if (Predicate.hasProperty(cause, "code") && Predicate.isString(cause.code)) return cause.code;
@@ -72,6 +79,16 @@ const execError = (operation, cause) => {
72
79
  if (code !== void 0) properties.code = code;
73
80
  return WorkspaceExecError.make(properties);
74
81
  };
82
+ const acquireError = (operation, cause) => {
83
+ const properties = {
84
+ operation,
85
+ message: errorMessage(cause),
86
+ cause
87
+ };
88
+ const code = errorCode(cause);
89
+ if (code !== void 0) properties.code = code;
90
+ return WorkspaceAcquireError.make(properties);
91
+ };
75
92
  const assetsError = (operation, path, cause) => {
76
93
  const properties = {
77
94
  operation,
@@ -404,7 +421,6 @@ const wrapRun = (handle) => {
404
421
  kill
405
422
  };
406
423
  };
407
- const releaseRun = (handle) => Effect.sync(() => handle[Symbol.dispose]());
408
424
  const makeRuntime = (client) => {
409
425
  const execImplementation = Effect.fn("ComputerWorkspace.runtime.exec", { attributes: {
410
426
  family: "runtime",
@@ -415,14 +431,14 @@ const makeRuntime = (client) => {
415
431
  backend: options?.backend,
416
432
  runId: options?.id
417
433
  });
418
- const handle = yield* Effect.acquireRelease(Effect.tryPromise({
434
+ const handle = yield* Effect.acquireDisposable(Effect.tryPromise({
419
435
  try: () => {
420
436
  if (Predicate.isString(source)) return client.runtime.exec(source, runtimeExecOptions(options));
421
437
  const values = optionsOrValue === void 0 ? remainingValues : [optionsOrValue, ...remainingValues];
422
438
  return client.runtime.exec(source, ...values);
423
439
  },
424
440
  catch: (cause) => execError("exec", cause)
425
- }), releaseRun);
441
+ }));
426
442
  return wrapRun(handle);
427
443
  });
428
444
  const exec = execImplementation;
@@ -434,10 +450,10 @@ const makeRuntime = (client) => {
434
450
  backend: options?.backend,
435
451
  runId: id
436
452
  });
437
- const handle = yield* Effect.acquireRelease(Effect.tryPromise({
453
+ const handle = yield* Effect.acquireDisposable(Effect.tryPromise({
438
454
  try: () => client.runtime.getExec(id, runtimeGetOptions(options)),
439
455
  catch: (cause) => execError("getExec", cause)
440
- }), releaseRun);
456
+ }));
441
457
  return wrapRun(handle);
442
458
  });
443
459
  const killExec = Effect.fn("ComputerWorkspace.runtime.killExec", { attributes: {
@@ -567,10 +583,16 @@ var ComputerWorkspace = class ComputerWorkspace extends Context.Service()("effec
567
583
  static layer = Layer.effect(ComputerWorkspace, Effect.gen(function* () {
568
584
  const host = lookup((yield* DurableObjectState).raw);
569
585
  if (host === void 0) return yield* Effect.die("ComputerWorkspace.layer requires a Durable Object class built with withComputerWorkspace");
570
- const computer = yield* Effect.promise(() => import("@cloudflare/computer"));
571
- const client = yield* Effect.acquireRelease(Effect.promise(() => computer.getWorkspace(host)), (workspace) => Effect.sync(() => workspace[Symbol.dispose]()));
586
+ const computer = yield* Effect.tryPromise({
587
+ try: () => import("@cloudflare/computer"),
588
+ catch: (cause) => acquireError("import", cause)
589
+ });
590
+ const client = yield* Effect.acquireDisposable(Effect.tryPromise({
591
+ try: () => computer.getWorkspace(host),
592
+ catch: (cause) => acquireError("getWorkspace", cause)
593
+ }));
572
594
  return ComputerWorkspace.of(fromWorkspaceClient(client));
573
595
  }));
574
596
  };
575
597
  //#endregion
576
- export { ComputerWorkspace, WorkspaceAssetsError, WorkspaceExecError, WorkspaceFsError, WorkspaceGitError, fromWorkspaceClient };
598
+ export { ComputerWorkspace, WorkspaceAcquireError, WorkspaceAssetsError, WorkspaceExecError, WorkspaceFsError, WorkspaceGitError, fromWorkspaceClient };
@@ -0,0 +1,244 @@
1
+ import { i as WorkerContextWaitUntilOptions } from "./Worker-Dl6_Pijl.mjs";
2
+ import { Cause, Context, Duration, Effect, Option, Schema } from "effect";
3
+ declare namespace DurableObjectStorage_d_exports {
4
+ export { DurableObjectStorage, DurableObjectTransaction, SchemaBackedSyncKvStorage, SqlCursor, SqlStorage, SqlStorageValue, StorageOperationError, SyncKvDefinition, SyncKvStorage, fromDurableObjectStorage };
5
+ }
6
+ type SqlStorageValue = globalThis.SqlStorageValue;
7
+ declare const StorageOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
8
+ readonly _tag: "StorageOperationError";
9
+ } & Readonly<A>;
10
+ declare class StorageOperationError extends StorageOperationError_base<{
11
+ readonly operation: string;
12
+ readonly cause: unknown;
13
+ }> {
14
+ get message(): string;
15
+ }
16
+ type StorageEffect<A> = Effect.Effect<A, StorageOperationError>;
17
+ interface SqlCursor<T extends Record<string, SqlStorageValue>> {
18
+ next(): StorageEffect<{
19
+ done?: false;
20
+ value: T;
21
+ } | {
22
+ done: true;
23
+ value?: never;
24
+ }>;
25
+ toArray(): StorageEffect<Array<T>>;
26
+ one(): StorageEffect<T>;
27
+ raw<U extends Array<SqlStorageValue>>(): StorageEffect<IterableIterator<U>>;
28
+ readonly columnNames: Array<string>;
29
+ readonly rowsRead: StorageEffect<number>;
30
+ readonly rowsWritten: StorageEffect<number>;
31
+ }
32
+ interface SqlStorage {
33
+ exec<T extends Record<string, SqlStorageValue>>(query: string, ...bindings: Array<SqlStorageValue>): StorageEffect<SqlCursor<T>>;
34
+ readonly databaseSize: number;
35
+ }
36
+ interface SyncKvDefinition<Key, Value, EncodedValue> {
37
+ readonly key: Schema.Codec<Key, string>;
38
+ readonly value: Schema.Codec<Value, EncodedValue>;
39
+ }
40
+ /**
41
+ * Sync KV wrapper that transparently encodes keys/values through Effect Schema.
42
+ */
43
+ interface SchemaBackedSyncKvStorage<Key, Value> {
44
+ get(key: Key): Effect.Effect<Option.Option<Value>, unknown>;
45
+ put(key: Key, value: Value): Effect.Effect<void, unknown>;
46
+ delete(key: Key): Effect.Effect<boolean, unknown>;
47
+ list(options?: globalThis.SyncKvListOptions): Effect.Effect<Array<[Key, Value]>, unknown>;
48
+ }
49
+ interface SyncKvStorage {
50
+ get<T = unknown>(key: string): StorageEffect<T | undefined>;
51
+ put<T>(key: string, value: T): StorageEffect<void>;
52
+ delete(key: string): StorageEffect<boolean>;
53
+ list<T = unknown>(options?: globalThis.SyncKvListOptions): StorageEffect<Array<[string, T]>>;
54
+ schema<Key, Value, EncodedValue>(definition: SyncKvDefinition<Key, Value, EncodedValue>): SchemaBackedSyncKvStorage<Key, Value>;
55
+ }
56
+ /**
57
+ * Effect wrapper around Cloudflare transaction callbacks.
58
+ */
59
+ interface DurableObjectTransaction {
60
+ get<T = unknown>(key: string, options?: globalThis.DurableObjectGetOptions): StorageEffect<T | undefined>;
61
+ get<T = unknown>(keys: Array<string>, options?: globalThis.DurableObjectGetOptions): StorageEffect<Map<string, T>>;
62
+ list<T = unknown>(options?: globalThis.DurableObjectListOptions): StorageEffect<Map<string, T>>;
63
+ put<T>(key: string, value: T, options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
64
+ put<T>(entries: Record<string, T>, options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
65
+ delete(key: string, options?: globalThis.DurableObjectPutOptions): StorageEffect<boolean>;
66
+ delete(keys: Array<string>, options?: globalThis.DurableObjectPutOptions): StorageEffect<number>;
67
+ rollback(): StorageEffect<void>;
68
+ getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
69
+ setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
70
+ setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
71
+ deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
72
+ }
73
+ interface DurableObjectStorage {
74
+ get<T = unknown>(key: string, options?: globalThis.DurableObjectGetOptions): StorageEffect<T | undefined>;
75
+ put<T>(key: string, value: T, options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
76
+ delete(key: string, options?: globalThis.DurableObjectPutOptions): StorageEffect<boolean>;
77
+ /**
78
+ * Deletes all stored data. On compatibility dates before 2026-02-24, Cloudflare
79
+ * documents that active alarms must be deleted separately with `deleteAlarm()`.
80
+ */
81
+ deleteAll(options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
82
+ getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
83
+ setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
84
+ setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
85
+ deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
86
+ /**
87
+ * SQLite-backed Durable Object storage only.
88
+ *
89
+ * The callback Effect must complete synchronously. If it requires asynchronous
90
+ * work, use `transaction` instead so Cloudflare can run the async transaction
91
+ * callback under the platform's transaction contract.
92
+ */
93
+ transactionSync<A, E, R>(closure: () => Effect.Effect<A, E, R>): Effect.Effect<A, E | StorageOperationError, R>;
94
+ /**
95
+ * Runs an async transaction and exposes a typed transaction wrapper.
96
+ */
97
+ transaction<A, E, R>(closure: (txn: DurableObjectTransaction) => Effect.Effect<A, E, R>): Effect.Effect<A, E | StorageOperationError, R>;
98
+ /** Flushes pending writes to disk. */
99
+ sync(): StorageEffect<void>;
100
+ /** Requires SQLite-backed Durable Object storage. */
101
+ getCurrentBookmark(): StorageEffect<string>;
102
+ /** Requires SQLite-backed Durable Object storage. */
103
+ onNextSessionRestoreBookmark(bookmark: string): StorageEffect<string>;
104
+ readonly sql: SqlStorage;
105
+ readonly kv: SyncKvStorage;
106
+ }
107
+ declare const fromDurableObjectStorage: (storage: globalThis.DurableObjectStorage) => DurableObjectStorage;
108
+ declare namespace DurableObjectWebSocket_d_exports {
109
+ export { AcceptUpgradeOptions, AcceptedUpgrade, AttachmentInvalidPolicy, AttachmentRehydrateOptions, DurableWebSocket, DurableWebSocketAttachment, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketHandlers, DurableWebSocketSendData, DurableWebSocketSendError, RehydratedDurableWebSocket, acceptUpgrade, attachment, fromWebSocket, handlers };
110
+ }
111
+ type DurableWebSocketSendData = string | ArrayBuffer | ArrayBufferView;
112
+ declare const DurableWebSocketSendError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
113
+ readonly _tag: "DurableWebSocketSendError";
114
+ } & Readonly<A>;
115
+ declare class DurableWebSocketSendError extends DurableWebSocketSendError_base<{
116
+ readonly cause: unknown;
117
+ }> {
118
+ get message(): string;
119
+ }
120
+ declare const DurableWebSocketCloseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
121
+ readonly _tag: "DurableWebSocketCloseError";
122
+ } & Readonly<A>;
123
+ declare class DurableWebSocketCloseError extends DurableWebSocketCloseError_base<{
124
+ readonly cause: unknown;
125
+ }> {
126
+ get message(): string;
127
+ }
128
+ declare const DurableWebSocketAttachmentError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
129
+ readonly _tag: "DurableWebSocketAttachmentError";
130
+ } & Readonly<A>;
131
+ /** Error raised when serializing or deserializing a websocket attachment fails. */
132
+ declare class DurableWebSocketAttachmentError extends DurableWebSocketAttachmentError_base<{
133
+ readonly operation: "serialize" | "deserialize";
134
+ readonly cause: unknown;
135
+ }> {
136
+ get message(): string;
137
+ }
138
+ interface DurableWebSocket<Attachment = unknown> {
139
+ readonly raw: WebSocket;
140
+ send(data: DurableWebSocketSendData): Effect.Effect<void, DurableWebSocketSendError>;
141
+ close(code?: number, reason?: string): Effect.Effect<void, DurableWebSocketCloseError>;
142
+ /** Serializes hibernation attachment metadata onto the socket. */
143
+ serializeAttachment<A extends Attachment = Attachment>(value: A): Effect.Effect<void, DurableWebSocketAttachmentError>;
144
+ /** Deserializes hibernation attachment metadata from the socket. */
145
+ readonly deserializeAttachment: Effect.Effect<unknown, DurableWebSocketAttachmentError>;
146
+ }
147
+ declare const fromWebSocket: <Attachment = unknown>(raw: WebSocket) => DurableWebSocket<Attachment>;
148
+ interface AcceptUpgradeOptions<Attachment = unknown> {
149
+ readonly tags?: ReadonlyArray<string> | undefined;
150
+ /** Optional attachment serialized onto the server socket. */
151
+ readonly attachment?: Attachment | undefined;
152
+ }
153
+ interface AcceptedUpgrade<Attachment = unknown> {
154
+ readonly client: WebSocket;
155
+ readonly server: DurableWebSocket<Attachment>;
156
+ readonly response: Response;
157
+ }
158
+ declare const acceptUpgrade: <Attachment = unknown>(options?: AcceptUpgradeOptions<Attachment> | undefined) => Effect.Effect<AcceptedUpgrade<Attachment>, DurableWebSocketAttachmentError, DurableObjectState>;
159
+ type AttachmentInvalidPolicy = "ignore" | "ignore-and-close" | "fail";
160
+ interface AttachmentRehydrateOptions {
161
+ readonly tag?: string | undefined;
162
+ readonly onInvalid?: AttachmentInvalidPolicy | undefined;
163
+ }
164
+ interface RehydratedDurableWebSocket<Attachment> {
165
+ readonly socket: DurableWebSocket<Attachment>;
166
+ readonly attachment: Attachment;
167
+ }
168
+ interface DurableWebSocketAttachment<Attachment, Encoded> {
169
+ serialize(socket: DurableWebSocket<unknown>, value: Attachment): Effect.Effect<void, DurableWebSocketAttachmentError>;
170
+ deserialize(socket: DurableWebSocket<unknown>): Effect.Effect<Option.Option<Attachment>, DurableWebSocketAttachmentError>;
171
+ rehydrate(options?: AttachmentRehydrateOptions): Effect.Effect<Array<RehydratedDurableWebSocket<Attachment>>, DurableWebSocketAttachmentError, DurableObjectState>;
172
+ readonly schema: Schema.Codec<Attachment, Encoded, never, never>;
173
+ }
174
+ declare const attachment: <const AttachmentSchema extends Schema.Codec<any, any, never, never>>(schema: AttachmentSchema) => DurableWebSocketAttachment<Schema.Schema.Type<AttachmentSchema>, Schema.Codec.Encoded<AttachmentSchema>>;
175
+ interface DurableWebSocketHandlers<R = never, E = unknown> {
176
+ readonly message?: (socket: DurableWebSocket, message: string | ArrayBuffer) => Effect.Effect<void, E, R>;
177
+ readonly close?: (socket: DurableWebSocket, code: number, reason: string, wasClean: boolean) => Effect.Effect<void, E, R>;
178
+ readonly error?: (socket: DurableWebSocket, cause: unknown) => Effect.Effect<void, E, R>;
179
+ }
180
+ declare const handlers: <R = never, E = unknown>(options: DurableWebSocketHandlers<R, E>) => {
181
+ webSocketMessage: ((socket: DurableWebSocket, message: string | ArrayBuffer) => Effect.Effect<void, E, R>) | undefined;
182
+ webSocketClose: ((socket: DurableWebSocket, code: number, reason: string, wasClean: boolean) => Effect.Effect<void, E, R>) | undefined;
183
+ webSocketError: ((socket: DurableWebSocket, cause: unknown) => Effect.Effect<void, E, R>) | undefined;
184
+ };
185
+ declare namespace DurableObjectState_d_exports {
186
+ export { DurableObjectState, DurableObjectStateService, fromDurableObjectState };
187
+ }
188
+ /**
189
+ * Effect-friendly wrapper around Cloudflare `DurableObjectState`.
190
+ */
191
+ interface DurableObjectStateService {
192
+ readonly raw: globalThis.DurableObjectState;
193
+ readonly id: globalThis.DurableObjectId;
194
+ readonly storage: DurableObjectStorage;
195
+ waitUntil(promise: Promise<unknown>): Effect.Effect<void>;
196
+ /**
197
+ * Runs a background Effect via Cloudflare's `waitUntil` with the caller's
198
+ * Effect context, mirroring `WorkerContext.waitUntil` failure modes:
199
+ * `"observe"` (default) logs or routes failures to `onFailure`, while
200
+ * `"propagate"` also rejects the native `waitUntil` promise.
201
+ */
202
+ waitUntil<A, E, R, R2 = never>(effect: Effect.Effect<A, E, R>, options?: WorkerContextWaitUntilOptions<E, R2>): Effect.Effect<void, never, R | R2>;
203
+ /**
204
+ * Runs an Effect inside Cloudflare's `blockConcurrencyWhile` gate.
205
+ *
206
+ * Cloudflare resets a Durable Object if the callback throws/rejects, or if the
207
+ * callback exceeds the platform timeout (currently documented as 30 seconds).
208
+ * This safe default resolves the callback with the Effect `Exit` for typed
209
+ * Effect failures and resumes that `Exit` afterward, so typed failures remain
210
+ * ordinary Effect failures instead of resetting the Durable Object. Defects
211
+ * and interruptions still reject the callback.
212
+ */
213
+ blockConcurrencyWhile<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>;
214
+ /**
215
+ * Runs an Effect inside Cloudflare's `blockConcurrencyWhile` gate and lets
216
+ * Effect failures reject the callback.
217
+ *
218
+ * Use this only when Cloudflare's reset-on-throw behavior is intentional. The
219
+ * platform also resets the Durable Object if the callback exceeds the
220
+ * documented timeout (currently 30 seconds).
221
+ */
222
+ blockConcurrencyWhileOrReset<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>;
223
+ acceptWebSocket(ws: DurableWebSocket, tags?: Array<string>): Effect.Effect<void>;
224
+ getWebSockets(tag?: string): Effect.Effect<Array<DurableWebSocket>>;
225
+ setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): Effect.Effect<void>;
226
+ getWebSocketAutoResponse: Effect.Effect<WebSocketRequestResponsePair | null>;
227
+ getWebSocketAutoResponseTimestamp(ws: DurableWebSocket): Effect.Effect<Date | null>;
228
+ setHibernatableWebSocketEventTimeout(timeoutMs?: number): Effect.Effect<void>;
229
+ getHibernatableWebSocketEventTimeout: Effect.Effect<number | null>;
230
+ getTags(ws: DurableWebSocket): Effect.Effect<Array<string>>;
231
+ /**
232
+ * Forcibly resets the Durable Object. Cloudflare logs an uncaught Error using
233
+ * the optional reason, and the method is not available in `wrangler dev` local
234
+ * development according to the Durable Object State docs. Set
235
+ * `retryAlarm: false` to prevent the current alarm invocation from retrying;
236
+ * this option has no effect outside an alarm handler.
237
+ */
238
+ abort(reason?: string, options?: globalThis.DurableObjectAbortOptions): Effect.Effect<void>;
239
+ }
240
+ declare const DurableObjectState_base: Context.ServiceClass<DurableObjectState, "effect-cf/DurableObjectState", DurableObjectStateService>;
241
+ declare class DurableObjectState extends DurableObjectState_base {}
242
+ declare const fromDurableObjectState: (state: globalThis.DurableObjectState) => DurableObjectStateService;
243
+ //#endregion
244
+ export { AcceptedUpgrade as a, DurableWebSocketAttachmentError as c, AcceptUpgradeOptions as i, DurableObjectStorage_d_exports as l, DurableObjectStateService as n, DurableObjectWebSocket_d_exports as o, DurableObjectState_d_exports as r, DurableWebSocket as s, DurableObjectState as t, StorageOperationError as u };
@@ -1,6 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { t as causeMessage } from "./ErrorMessage-Byx2OgNd.mjs";
3
- import { Cause, Clock, Context, Data, Duration, Effect, Exit, Option, Predicate, Result, Schema } from "effect";
3
+ import { n as makeWaitUntilScheduler, r as runNativeCallback } from "./WorkerContext-Yobnfw-K.mjs";
4
+ import { Cause, Clock, Context, Data, Duration, Effect, Exit, Fiber, Option, Predicate, Result, Schema } from "effect";
4
5
  //#region src/DurableObjectStorage.ts
5
6
  var DurableObjectStorage_exports = /* @__PURE__ */ __exportAll({
6
7
  StorageOperationError: () => StorageOperationError,
@@ -103,30 +104,35 @@ const fromDurableObjectStorage = (storage) => ({
103
104
  deleteAlarm: (options) => tryStoragePromise("deleteAlarm", () => storage.deleteAlarm(options)),
104
105
  transactionSync: (closure) => Effect.context().pipe(Effect.flatMap((context) => Effect.suspend(() => {
105
106
  let aborted;
107
+ let escapedFiber;
106
108
  try {
107
109
  return Effect.succeed(storage.transactionSync(() => {
108
110
  const exit = Effect.runSyncExitWith(context)(closure());
109
111
  if (Exit.isSuccess(exit)) return exit.value;
112
+ const defect = Cause.findDefect(exit.cause);
113
+ if (Result.isSuccess(defect) && Cause.isAsyncFiberError(defect.success)) {
114
+ escapedFiber = defect.success.fiber;
115
+ escapedFiber.interruptUnsafe();
116
+ }
110
117
  aborted = exit;
111
118
  throw exit;
112
119
  }));
113
120
  } catch (cause) {
114
- if (aborted !== void 0 && cause === aborted && Exit.isFailure(aborted)) return Effect.failCause(aborted.cause);
115
- return Effect.fail(storageError("transactionSync", cause));
121
+ const failure = aborted !== void 0 && cause === aborted && Exit.isFailure(aborted) ? Effect.failCause(aborted.cause) : Effect.fail(storageError("transactionSync", cause));
122
+ const fiber = escapedFiber;
123
+ if (fiber !== void 0) return Fiber.interrupt(fiber).pipe(Effect.andThen(failure));
124
+ return failure;
116
125
  }
117
126
  }))),
118
- transaction: (closure) => Effect.context().pipe(Effect.flatMap((context) => Effect.callback((resume) => {
127
+ transaction: (closure) => Effect.suspend(() => {
119
128
  let aborted;
120
- storage.transaction(async (txn) => {
121
- const exit = await Effect.runPromiseExitWith(context)(closure(fromDurableObjectTransaction(txn)));
129
+ return runNativeCallback((run) => storage.transaction(async (txn) => {
130
+ const exit = await run(Effect.suspend(() => closure(fromDurableObjectTransaction(txn))));
122
131
  if (Exit.isSuccess(exit)) return exit.value;
123
132
  aborted = exit;
124
133
  throw exit;
125
- }).then((value) => resume(Effect.succeed(value)), (cause) => {
126
- if (aborted !== void 0 && cause === aborted && Exit.isFailure(aborted)) resume(Effect.failCause(aborted.cause));
127
- else resume(Effect.fail(storageError("transaction", cause)));
128
- });
129
- }))),
134
+ })).pipe(Effect.catch((cause) => aborted !== void 0 && cause === aborted && Exit.isFailure(aborted) ? Effect.failCause(aborted.cause) : Effect.fail(storageError("transaction", cause))));
135
+ }),
130
136
  sync: () => tryStoragePromise("sync", () => storage.sync()),
131
137
  getCurrentBookmark: () => tryStoragePromise("getCurrentBookmark", () => storage.getCurrentBookmark()),
132
138
  onNextSessionRestoreBookmark: (bookmark) => tryStoragePromise("onNextSessionRestoreBookmark", () => storage.onNextSessionRestoreBookmark(bookmark)),
@@ -160,38 +166,31 @@ var DurableWebSocketAttachmentError = class extends Data.TaggedError("DurableWeb
160
166
  return `Durable Object websocket attachment ${this.operation} failed: ${causeMessage(this.cause)}`;
161
167
  }
162
168
  };
163
- const wrappers = /* @__PURE__ */ new WeakMap();
164
- const fromWebSocket = (raw) => {
165
- const existing = wrappers.get(raw);
166
- if (existing !== void 0) return existing;
167
- const socket = {
168
- raw,
169
- send: (data) => Effect.try({
170
- try: () => raw.send(data),
171
- catch: (cause) => new DurableWebSocketSendError({ cause })
172
- }),
173
- close: (code, reason) => Effect.try({
174
- try: () => raw.close(code, reason),
175
- catch: (cause) => new DurableWebSocketCloseError({ cause })
176
- }),
177
- serializeAttachment: (value) => Effect.try({
178
- try: () => raw.serializeAttachment(value),
179
- catch: (cause) => new DurableWebSocketAttachmentError({
180
- operation: "serialize",
181
- cause
182
- })
183
- }),
184
- deserializeAttachment: Effect.try({
185
- try: () => raw.deserializeAttachment(),
186
- catch: (cause) => new DurableWebSocketAttachmentError({
187
- operation: "deserialize",
188
- cause
189
- })
169
+ const fromWebSocket = (raw) => ({
170
+ raw,
171
+ send: (data) => Effect.try({
172
+ try: () => raw.send(data),
173
+ catch: (cause) => new DurableWebSocketSendError({ cause })
174
+ }),
175
+ close: (code, reason) => Effect.try({
176
+ try: () => raw.close(code, reason),
177
+ catch: (cause) => new DurableWebSocketCloseError({ cause })
178
+ }),
179
+ serializeAttachment: (value) => Effect.try({
180
+ try: () => raw.serializeAttachment(value),
181
+ catch: (cause) => new DurableWebSocketAttachmentError({
182
+ operation: "serialize",
183
+ cause
190
184
  })
191
- };
192
- wrappers.set(raw, socket);
193
- return socket;
194
- };
185
+ }),
186
+ deserializeAttachment: Effect.try({
187
+ try: () => raw.deserializeAttachment(),
188
+ catch: (cause) => new DurableWebSocketAttachmentError({
189
+ operation: "deserialize",
190
+ cause
191
+ })
192
+ })
193
+ });
195
194
  const acceptUpgrade = Effect.fn("DurableObjectWebSocket.acceptUpgrade")(function* (options = {}) {
196
195
  const state = yield* DurableObjectState;
197
196
  const pair = new WebSocketPair();
@@ -252,35 +251,6 @@ const handlers = (options) => ({
252
251
  webSocketError: options.error
253
252
  });
254
253
  //#endregion
255
- //#region src/internal/WorkerContext.ts
256
- const causeError = (cause) => {
257
- const squashed = Cause.squash(cause);
258
- return squashed instanceof Error ? squashed : new Error(Cause.pretty(cause), { cause: squashed });
259
- };
260
- const failureHandler = (label, cause, options) => (options?.onFailure?.(cause) ?? Effect.logError(`${label} failed`, Cause.pretty(cause))).pipe(Effect.catchCause((handlerCause) => Effect.logError(`${label} failure handler failed`, Cause.pretty(cause), Cause.pretty(handlerCause))));
261
- const makeWaitUntilScheduler = (label, register, runPromiseExit = (effect) => Effect.runPromiseExit(effect)) => {
262
- return (effect, options, mode) => Effect.context().pipe(Effect.flatMap((context) => Effect.sync(() => {
263
- const runHandler = (cause) => runPromiseExit(Effect.scoped(Effect.provideContext(failureHandler(label, cause, options), context))).then((exit) => {
264
- if (Exit.isFailure(exit)) console.error(`${label} failure handler failed`, Cause.pretty(exit.cause));
265
- });
266
- register(runPromiseExit(Effect.scoped(Effect.provideContext(effect, context))).then(async (exit) => {
267
- if (Exit.isSuccess(exit)) return;
268
- await runHandler(exit.cause);
269
- if (mode === "propagate") throw causeError(exit.cause);
270
- }));
271
- })));
272
- };
273
- const fromExecutionContext = (ctx, runPromiseExit = (effect) => Effect.runPromiseExit(effect)) => {
274
- const schedule = makeWaitUntilScheduler("WorkerContext.waitUntil", (promise) => ctx.waitUntil(promise), runPromiseExit);
275
- return {
276
- raw: ctx,
277
- waitUntil: (effect, options) => schedule(effect, options, options?.mode ?? "observe"),
278
- waitUntilPropagating: (effect, options) => schedule(effect, options, "propagate"),
279
- passThroughOnException: Effect.sync(() => ctx.passThroughOnException()),
280
- abort: (reason) => Effect.sync(() => ctx.abort(reason))
281
- };
282
- };
283
- //#endregion
284
254
  //#region src/DurableObjectState.ts
285
255
  var DurableObjectState_exports = /* @__PURE__ */ __exportAll({
286
256
  DurableObjectState: () => DurableObjectState,
@@ -295,8 +265,12 @@ const fromDurableObjectState = (state) => {
295
265
  id: state.id,
296
266
  storage: fromDurableObjectStorage(state.storage),
297
267
  waitUntil,
298
- blockConcurrencyWhile: (effect) => Effect.context().pipe(Effect.flatMap((context) => Effect.promise(() => state.blockConcurrencyWhile(() => runPromiseExitPreservingTypedFailures(context, effect)))), Effect.flatten),
299
- blockConcurrencyWhileOrReset: (effect) => Effect.context().pipe(Effect.flatMap((context) => Effect.promise(() => state.blockConcurrencyWhile(() => Effect.runPromiseWith(context)(effect))))),
268
+ blockConcurrencyWhile: (effect) => runNativeCallback((run) => state.blockConcurrencyWhile(() => runExitPreservingTypedFailures(run, effect))).pipe(Effect.orDie, Effect.flatten),
269
+ blockConcurrencyWhileOrReset: (effect) => runNativeCallback((run) => state.blockConcurrencyWhile(async () => {
270
+ const exit = await run(effect);
271
+ if (Exit.isFailure(exit)) throw Cause.squash(exit.cause);
272
+ return exit.value;
273
+ })).pipe(Effect.orDie),
300
274
  acceptWebSocket: (ws, tags) => Effect.sync(() => state.acceptWebSocket(ws.raw, tags)),
301
275
  getWebSockets: (tag) => Effect.sync(() => state.getWebSockets(tag).map((socket) => fromWebSocket(socket))),
302
276
  setWebSocketAutoResponse: (pair) => Effect.sync(() => state.setWebSocketAutoResponse(pair)),
@@ -308,10 +282,10 @@ const fromDurableObjectState = (state) => {
308
282
  abort: (reason, options) => Effect.sync(() => state.abort(reason, options))
309
283
  };
310
284
  };
311
- const runPromiseExitPreservingTypedFailures = async (context, effect) => {
312
- const exit = await Effect.runPromiseExitWith(context)(effect);
285
+ const runExitPreservingTypedFailures = async (run, effect) => {
286
+ const exit = await run(effect);
313
287
  if (Exit.isFailure(exit) && (Cause.hasDies(exit.cause) || Cause.hasInterrupts(exit.cause))) throw Cause.squash(exit.cause);
314
288
  return exit;
315
289
  };
316
290
  //#endregion
317
- export { DurableObjectWebSocket_exports as a, DurableObjectStorage_exports as c, fromExecutionContext as i, DurableObjectState_exports as n, DurableWebSocketAttachmentError as o, fromDurableObjectState as r, fromWebSocket as s, DurableObjectState as t };
291
+ export { DurableWebSocketAttachmentError as a, DurableObjectWebSocket_exports as i, DurableObjectState_exports as n, fromWebSocket as o, fromDurableObjectState as r, DurableObjectStorage_exports as s, DurableObjectState as t };