effect-cf 0.33.0 → 0.35.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 +22 -0
- package/README.md +31 -1079
- package/dist/{Artifacts-DnHQA-Ax.d.mts → Artifacts-B3r3RuTB.d.mts} +1 -48
- package/dist/{Artifacts-CSIk1Oml.mjs → Artifacts-CBxWGeZB.mjs} +2 -7
- package/dist/{Binding-pfilNJmP.mjs → Binding-C82jRhdr.mjs} +1 -2
- package/dist/{Binding-xJJJPmiD.d.mts → Binding-DKcLrQCM.d.mts} +0 -45
- package/dist/ComputerArtifacts.d.mts +1 -3
- package/dist/ComputerArtifacts.mjs +1 -2
- package/dist/ComputerWorkspace.d.mts +11 -12
- package/dist/ComputerWorkspace.mjs +31 -10
- package/dist/ComputerWorkspaceHost.d.mts +1 -9
- package/dist/{ContainerNamespace-CFjiLw8b.d.mts → ContainerNamespace-DbLaeFhN.d.mts} +1 -14
- package/dist/{DurableObjectState-CVLLse69.mjs → DurableObjectState-DcqElJ4e.mjs} +82 -89
- package/dist/{DurableObjectState-BpRjz6-q.d.mts → DurableObjectState-DpYdNZgT.d.mts} +102 -266
- package/dist/{ErrorMessage-BM0K-Nik.mjs → ErrorMessage-Byx2OgNd.mjs} +0 -1
- package/dist/{Hyperdrive-z-9fFz15.d.mts → Hyperdrive-HrwFlZ6x.d.mts} +1 -4
- package/dist/HyperdrivePg.d.mts +2 -11
- package/dist/HyperdrivePg.mjs +0 -8
- package/dist/Sandbox.d.mts +2 -32
- package/dist/Sandbox.mjs +2 -11
- package/dist/Vitest.d.mts +3 -31
- package/dist/Vitest.mjs +8 -26
- package/dist/{WorkflowBinding-lBR4_-op.d.mts → WorkflowBinding-Cn3AuUyP.d.mts} +2 -5
- package/dist/index.d.mts +42 -480
- package/dist/index.mjs +267 -380
- package/package.json +1 -1
- package/src/AiGateway.ts +0 -6
- package/src/AnalyticsEngine.ts +8 -10
- package/src/Artifacts.ts +0 -47
- package/src/Binding.ts +0 -8
- package/src/BrowserRendering.ts +0 -4
- package/src/Cache.ts +0 -19
- package/src/CloudflareOtlp.ts +6 -39
- package/src/ComputerArtifacts.ts +0 -2
- package/src/ComputerWorkspace.ts +53 -35
- package/src/ComputerWorkspaceHost.ts +0 -8
- package/src/ContainerNamespace.ts +0 -13
- package/src/D1.ts +0 -22
- package/src/DurableObject.ts +51 -32
- package/src/DurableObjectAlarm.ts +6 -210
- package/src/DurableObjectDefinition.ts +8 -20
- package/src/DurableObjectNamespace.ts +88 -115
- package/src/DurableObjectRpcWebSocket.ts +5 -46
- package/src/DurableObjectSqlite.ts +0 -12
- package/src/DurableObjectState.ts +18 -38
- package/src/DurableObjectStorage.ts +55 -70
- package/src/DurableObjectWebSocket.ts +23 -72
- package/src/Email.ts +0 -12
- package/src/Environment.ts +0 -37
- package/src/Hyperdrive.ts +0 -3
- package/src/HyperdrivePg.ts +0 -9
- package/src/Images.ts +0 -5
- package/src/Kv.ts +0 -15
- package/src/QueueBinding.ts +0 -2
- package/src/R2.ts +0 -5
- package/src/RpcDefinition.ts +5 -17
- package/src/RpcTracing.ts +133 -0
- package/src/Sandbox.ts +0 -30
- package/src/ServiceBinding.ts +83 -128
- package/src/Vectorize.ts +0 -5
- package/src/Vitest.ts +18 -43
- package/src/WebTransport.ts +0 -15
- package/src/Worker.ts +140 -108
- package/src/WorkerDefinition.ts +5 -14
- package/src/WorkersAi.ts +21 -18
- package/src/Workflow.ts +45 -54
- package/src/WorkflowBinding.ts +0 -3
- package/src/index.ts +1 -0
- package/src/internal/Entrypoint.ts +23 -4
- package/src/internal/ErrorMessage.ts +0 -1
- package/src/internal/NativeCallback.ts +55 -0
- package/src/internal/RpcInvocation.ts +18 -1
- package/src/internal/Runtime.ts +12 -7
- package/src/internal/WorkerContext.ts +5 -14
package/src/ComputerWorkspace.ts
CHANGED
|
@@ -100,6 +100,17 @@ export class WorkspaceAssetsError extends Schema.TaggedError<WorkspaceAssetsErro
|
|
|
100
100
|
},
|
|
101
101
|
) {}
|
|
102
102
|
|
|
103
|
+
/** Schema-serializable error while acquiring the Computer workspace client. */
|
|
104
|
+
export class WorkspaceAcquireError extends Schema.TaggedError<WorkspaceAcquireError>()(
|
|
105
|
+
"WorkspaceAcquireError",
|
|
106
|
+
{
|
|
107
|
+
operation: Schema.String,
|
|
108
|
+
message: Schema.String,
|
|
109
|
+
code: Schema.optionalKey(Schema.String),
|
|
110
|
+
cause: Schema.optionalKey(Schema.Defect({ includeStack: true })),
|
|
111
|
+
},
|
|
112
|
+
) {}
|
|
113
|
+
|
|
103
114
|
export type WorkspaceReadFileOptions = Omit<CloudflareReadFileOptions, "encoding">;
|
|
104
115
|
export type ReadTextFileOptions = WorkspaceReadFileOptions;
|
|
105
116
|
export type ListDirectoryOptions = CloudflareReaddirOptions;
|
|
@@ -128,7 +139,6 @@ export interface WorkspaceReadFile {
|
|
|
128
139
|
): Effect.Effect<ReadableStream<Uint8Array>, WorkspaceFsError>;
|
|
129
140
|
}
|
|
130
141
|
|
|
131
|
-
/** Complete Effect wrapper of `WorkspaceClient.fs`. */
|
|
132
142
|
export interface ComputerWorkspaceFilesystem {
|
|
133
143
|
readonly readFile: WorkspaceReadFile;
|
|
134
144
|
readonly stat: (path: string) => Effect.Effect<WorkspaceEntryStat, WorkspaceFsError>;
|
|
@@ -173,7 +183,6 @@ export type WorkspaceGitDiffOptions = CloudflareGitDiffOptions;
|
|
|
173
183
|
export type WorkspaceGitCommitOptions = CloudflareGitCommitOptions;
|
|
174
184
|
|
|
175
185
|
export interface WorkspaceGitLogOptions extends Omit<CloudflareGitLogOptions, "depth"> {
|
|
176
|
-
/** Preferred upstream name for the history bound. */
|
|
177
186
|
readonly depth?: number;
|
|
178
187
|
/** Consumer-port compatibility alias for `depth`. */
|
|
179
188
|
readonly maxCount?: number;
|
|
@@ -235,7 +244,6 @@ export type WorkspaceExecOptions<Encoding extends WorkspaceExecEncoding = "utf8"
|
|
|
235
244
|
"encoding" | "timeoutMs"
|
|
236
245
|
> & {
|
|
237
246
|
readonly encoding?: Encoding;
|
|
238
|
-
/** Upstream runtime option name. */
|
|
239
247
|
readonly timeoutMs?: number;
|
|
240
248
|
/** Consumer-port compatibility alias for `timeoutMs`. */
|
|
241
249
|
readonly timeoutMillis?: number;
|
|
@@ -334,7 +342,6 @@ export interface WorkspaceExecCollect {
|
|
|
334
342
|
): Effect.Effect<WorkspaceExecResult<"binary">, WorkspaceExecError>;
|
|
335
343
|
}
|
|
336
344
|
|
|
337
|
-
/** Complete Effect wrapper of `WorkspaceClient.runtime`. */
|
|
338
345
|
export interface ComputerWorkspaceRuntime {
|
|
339
346
|
readonly exec: WorkspaceExec;
|
|
340
347
|
readonly execCollect: WorkspaceExecCollect;
|
|
@@ -349,7 +356,6 @@ export interface ComputerWorkspaceRuntime {
|
|
|
349
356
|
) => Effect.Effect<void, WorkspaceExecError>;
|
|
350
357
|
}
|
|
351
358
|
|
|
352
|
-
/** Effect wrapper over a configured local `Workspace.assets` client. */
|
|
353
359
|
export interface ComputerWorkspaceAssets {
|
|
354
360
|
readonly share: (
|
|
355
361
|
path: string,
|
|
@@ -368,7 +374,6 @@ export type ComputerWorkspaceThink = {
|
|
|
368
374
|
>;
|
|
369
375
|
};
|
|
370
376
|
|
|
371
|
-
/** Effect service over every client surface exposed by Cloudflare Computer. */
|
|
372
377
|
export interface ComputerWorkspaceService {
|
|
373
378
|
readonly fs: ComputerWorkspaceFilesystem;
|
|
374
379
|
readonly git: ComputerWorkspaceGit;
|
|
@@ -377,7 +382,6 @@ export interface ComputerWorkspaceService {
|
|
|
377
382
|
readonly assets: ComputerWorkspaceAssets | undefined;
|
|
378
383
|
readonly think: ComputerWorkspaceThink | undefined;
|
|
379
384
|
|
|
380
|
-
/** Flat filesystem aliases retained for mechanical consumer migration. */
|
|
381
385
|
readonly readFile: WorkspaceReadFile;
|
|
382
386
|
readonly stat: ComputerWorkspaceFilesystem["stat"];
|
|
383
387
|
readonly lstat: ComputerWorkspaceFilesystem["lstat"];
|
|
@@ -395,7 +399,6 @@ export interface ComputerWorkspaceService {
|
|
|
395
399
|
readonly chmod: ComputerWorkspaceFilesystem["chmod"];
|
|
396
400
|
readonly symlink: ComputerWorkspaceFilesystem["symlink"];
|
|
397
401
|
|
|
398
|
-
/** Flat runtime aliases retained for mechanical consumer migration. */
|
|
399
402
|
readonly exec: WorkspaceExec;
|
|
400
403
|
readonly execCollect: WorkspaceExecCollect;
|
|
401
404
|
readonly getExec: WorkspaceGetExec;
|
|
@@ -477,6 +480,21 @@ const execError = (operation: string, cause: unknown) => {
|
|
|
477
480
|
return WorkspaceExecError.make(properties);
|
|
478
481
|
};
|
|
479
482
|
|
|
483
|
+
const acquireError = (operation: string, cause: unknown) => {
|
|
484
|
+
const properties: WorkspaceOperationErrorProperties = {
|
|
485
|
+
operation,
|
|
486
|
+
message: errorMessage(cause),
|
|
487
|
+
cause,
|
|
488
|
+
};
|
|
489
|
+
const code = errorCode(cause);
|
|
490
|
+
|
|
491
|
+
if (code !== undefined) {
|
|
492
|
+
properties.code = code;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return WorkspaceAcquireError.make(properties);
|
|
496
|
+
};
|
|
497
|
+
|
|
480
498
|
const assetsError = (operation: string, path: string, cause: unknown) => {
|
|
481
499
|
const properties: WorkspaceFsErrorProperties = {
|
|
482
500
|
operation,
|
|
@@ -895,9 +913,6 @@ const wrapRun = <Encoding extends WorkspaceExecEncoding>(
|
|
|
895
913
|
return { id: handle.id, backend: handle.backend, events, result, kill };
|
|
896
914
|
};
|
|
897
915
|
|
|
898
|
-
const releaseRun = (handle: CloudflareWorkspaceRuntimeExecHandle<"utf8" | undefined>) =>
|
|
899
|
-
Effect.sync(() => handle[Symbol.dispose]());
|
|
900
|
-
|
|
901
916
|
const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntime => {
|
|
902
917
|
const execImplementation = Effect.fn("ComputerWorkspace.runtime.exec", {
|
|
903
918
|
attributes: { family: "runtime", operation: "exec" },
|
|
@@ -913,7 +928,7 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
|
|
|
913
928
|
: undefined;
|
|
914
929
|
|
|
915
930
|
yield* Effect.annotateCurrentSpan({ backend: options?.backend, runId: options?.id });
|
|
916
|
-
const handle = yield* Effect.
|
|
931
|
+
const handle = yield* Effect.acquireDisposable(
|
|
917
932
|
Effect.tryPromise({
|
|
918
933
|
try: () => {
|
|
919
934
|
if (Predicate.isString(source)) {
|
|
@@ -934,7 +949,6 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
|
|
|
934
949
|
},
|
|
935
950
|
catch: (cause) => execError("exec", cause),
|
|
936
951
|
}),
|
|
937
|
-
releaseRun,
|
|
938
952
|
);
|
|
939
953
|
|
|
940
954
|
// SAFETY: runtimeExecOptions carries Encoding to Cloudflare's corresponding handle overload.
|
|
@@ -953,12 +967,11 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
|
|
|
953
967
|
options?: WorkspaceGetExecOptions<Encoding>,
|
|
954
968
|
) {
|
|
955
969
|
yield* Effect.annotateCurrentSpan({ backend: options?.backend, runId: id });
|
|
956
|
-
const handle = yield* Effect.
|
|
970
|
+
const handle = yield* Effect.acquireDisposable(
|
|
957
971
|
Effect.tryPromise({
|
|
958
972
|
try: () => client.runtime.getExec(id, runtimeGetOptions(options)),
|
|
959
973
|
catch: (cause) => execError("getExec", cause),
|
|
960
974
|
}),
|
|
961
|
-
releaseRun,
|
|
962
975
|
);
|
|
963
976
|
|
|
964
977
|
// SAFETY: runtimeGetOptions carries Encoding to Cloudflare's corresponding handle overload.
|
|
@@ -1169,7 +1182,6 @@ export const fromWorkspaceClient = (
|
|
|
1169
1182
|
};
|
|
1170
1183
|
};
|
|
1171
1184
|
|
|
1172
|
-
/** Effect service for the workspace owned by the enclosing Durable Object. */
|
|
1173
1185
|
export class ComputerWorkspace extends Context.Service<
|
|
1174
1186
|
ComputerWorkspace,
|
|
1175
1187
|
ComputerWorkspaceService
|
|
@@ -1178,25 +1190,31 @@ export class ComputerWorkspace extends Context.Service<
|
|
|
1178
1190
|
* Acquires the single workspace registered by `withComputerWorkspace` and
|
|
1179
1191
|
* releases its RPC client when the layer scope closes.
|
|
1180
1192
|
*/
|
|
1181
|
-
static readonly layer: Layer.Layer<ComputerWorkspace,
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1193
|
+
static readonly layer: Layer.Layer<ComputerWorkspace, WorkspaceAcquireError, DurableObjectState> =
|
|
1194
|
+
Layer.effect(
|
|
1195
|
+
ComputerWorkspace,
|
|
1196
|
+
Effect.gen(function* () {
|
|
1197
|
+
const state = yield* DurableObjectState;
|
|
1198
|
+
const host = HostRegistry.lookup(state.raw);
|
|
1199
|
+
|
|
1200
|
+
if (host === undefined) {
|
|
1201
|
+
return yield* Effect.die(
|
|
1202
|
+
"ComputerWorkspace.layer requires a Durable Object class built with withComputerWorkspace",
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const computer = yield* Effect.tryPromise({
|
|
1207
|
+
try: () => import("@cloudflare/computer"),
|
|
1208
|
+
catch: (cause) => acquireError("import", cause),
|
|
1209
|
+
});
|
|
1210
|
+
const client = yield* Effect.acquireDisposable(
|
|
1211
|
+
Effect.tryPromise({
|
|
1212
|
+
try: () => computer.getWorkspace(host),
|
|
1213
|
+
catch: (cause) => acquireError("getWorkspace", cause),
|
|
1214
|
+
}),
|
|
1190
1215
|
);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
const computer = yield* Effect.promise(() => import("@cloudflare/computer"));
|
|
1194
|
-
const client = yield* Effect.acquireRelease(
|
|
1195
|
-
Effect.promise(() => computer.getWorkspace(host)),
|
|
1196
|
-
(workspace) => Effect.sync(() => workspace[Symbol.dispose]()),
|
|
1197
|
-
);
|
|
1198
1216
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1217
|
+
return ComputerWorkspace.of(fromWorkspaceClient(client));
|
|
1218
|
+
}),
|
|
1219
|
+
);
|
|
1202
1220
|
}
|
|
@@ -47,7 +47,6 @@ export type {
|
|
|
47
47
|
WorkspaceSpan,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
/** The value of a `worker_loaders` binding. */
|
|
51
50
|
export type DynamicWorkerLoader = WorkerShellLoader;
|
|
52
51
|
|
|
53
52
|
/** Default author and committer identity for workspace Git operations. */
|
|
@@ -75,21 +74,14 @@ export interface ComputerWorkspaceShellOptions extends Omit<
|
|
|
75
74
|
"source" | "loader" | "workspace" | "ctx" | "egress"
|
|
76
75
|
> {
|
|
77
76
|
readonly loader: DynamicWorkerLoader;
|
|
78
|
-
/** Durable Object namespace binding that points back to the wrapped class. */
|
|
79
77
|
readonly hostBinding: string;
|
|
80
|
-
/** Current Durable Object id, normally `ctx.id.toString()`. */
|
|
81
78
|
readonly hostId: string;
|
|
82
|
-
/** Current Durable Object execution context, normally `ctx`. */
|
|
83
79
|
readonly executionContext: unknown;
|
|
84
|
-
/** Dynamic Worker egress policy. Defaults to `{ mode: "none" }`. */
|
|
85
80
|
readonly egress?: WorkspaceEgressPolicy;
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
/** Configuration accepted by {@link withComputerWorkspace}. */
|
|
89
83
|
export interface ComputerWorkspaceHostConfig {
|
|
90
|
-
/** Native SQLite-backed Durable Object storage (`ctx.storage`). */
|
|
91
84
|
readonly storage: globalThis.DurableObjectStorage | DurableObjectStorageLike;
|
|
92
|
-
/** Stable workspace id used by mounts and as the default Artifacts session. */
|
|
93
85
|
readonly sessionId?: string;
|
|
94
86
|
readonly gitIdentity?: ComputerWorkspaceGitIdentity;
|
|
95
87
|
/** Override the default `createGitClient()` factory. */
|
|
@@ -6,13 +6,10 @@ import * as ErrorMessage from "./internal/ErrorMessage";
|
|
|
6
6
|
|
|
7
7
|
const expectedContainerNamespace = "Container namespace binding with getByName()";
|
|
8
8
|
|
|
9
|
-
/** Named signal accepted by Cloudflare Container instances. */
|
|
10
9
|
export type ContainerSignal = "SIGKILL" | "SIGINT" | "SIGTERM";
|
|
11
10
|
|
|
12
|
-
/** Named or numeric signal accepted by Container `stop()`. */
|
|
13
11
|
export type ContainerStopSignal = ContainerSignal | number;
|
|
14
12
|
|
|
15
|
-
/** Per-instance Container startup configuration. */
|
|
16
13
|
export interface ContainerStartOptions {
|
|
17
14
|
readonly envVars?: Record<string, string>;
|
|
18
15
|
readonly entrypoint?: Array<string>;
|
|
@@ -34,7 +31,6 @@ export interface ContainerReadinessOptions {
|
|
|
34
31
|
readonly waitInterval?: number;
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
/** Options for starting a Container and waiting for its ports. */
|
|
38
34
|
export interface ContainerStartAndWaitForPortsOptions {
|
|
39
35
|
readonly startOptions?: ContainerStartOptions;
|
|
40
36
|
readonly ports?: number | Array<number>;
|
|
@@ -77,7 +73,6 @@ export interface ContainerStub {
|
|
|
77
73
|
removeDeniedHost(hostname: string): Promise<void>;
|
|
78
74
|
}
|
|
79
75
|
|
|
80
|
-
/** Native Container namespace binding shape. */
|
|
81
76
|
export interface ContainerNamespaceResource {
|
|
82
77
|
getByName(
|
|
83
78
|
name: string,
|
|
@@ -85,18 +80,14 @@ export interface ContainerNamespaceResource {
|
|
|
85
80
|
): ContainerStub;
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
/** Exact native stub type produced by a Container namespace binding. */
|
|
89
83
|
export type ContainerStubOf<Namespace extends ContainerNamespaceResource> = ReturnType<
|
|
90
84
|
Namespace["getByName"]
|
|
91
85
|
>;
|
|
92
86
|
|
|
93
|
-
/** Container namespace binding metadata. */
|
|
94
87
|
export interface ContainerNamespaceDefinition {
|
|
95
|
-
/** Binding name as configured in `wrangler.jsonc`. */
|
|
96
88
|
readonly binding: string;
|
|
97
89
|
}
|
|
98
90
|
|
|
99
|
-
/** Failure raised when invoking a named Container operation. */
|
|
100
91
|
export class ContainerOperationError extends Data.TaggedError("ContainerOperationError")<{
|
|
101
92
|
readonly binding: string;
|
|
102
93
|
readonly instance: string;
|
|
@@ -108,7 +99,6 @@ export class ContainerOperationError extends Data.TaggedError("ContainerOperatio
|
|
|
108
99
|
}
|
|
109
100
|
}
|
|
110
101
|
|
|
111
|
-
/** Effect-wrapped client for one named Container instance. */
|
|
112
102
|
export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerStub> {
|
|
113
103
|
readonly rawUnsafe: Effect.Effect<Stub, ContainerOperationError>;
|
|
114
104
|
readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
|
|
@@ -140,7 +130,6 @@ export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerS
|
|
|
140
130
|
readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
|
|
141
131
|
}
|
|
142
132
|
|
|
143
|
-
/** Effect client for a Container namespace binding. */
|
|
144
133
|
export interface ContainerNamespaceClient<
|
|
145
134
|
Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
|
|
146
135
|
> {
|
|
@@ -433,14 +422,12 @@ export const make = <Id extends string>(id: Id) => Tag<ContainerNamespaceService
|
|
|
433
422
|
|
|
434
423
|
declare const ContainerNamespaceServiceTypeId: unique symbol;
|
|
435
424
|
|
|
436
|
-
/** Nominal service marker for Container namespaces created with {@link make}. */
|
|
437
425
|
export interface ContainerNamespaceService<Id extends string> {
|
|
438
426
|
readonly [ContainerNamespaceServiceTypeId]: {
|
|
439
427
|
readonly id: Id;
|
|
440
428
|
};
|
|
441
429
|
}
|
|
442
430
|
|
|
443
|
-
/** Creates a typed Effect service for a Cloudflare Container namespace. */
|
|
444
431
|
export const Tag =
|
|
445
432
|
<Self, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource>() =>
|
|
446
433
|
<Id extends string>(id: Id) => {
|
package/src/D1.ts
CHANGED
|
@@ -10,18 +10,14 @@ const TypeId = "~effect-cf/D1" as const;
|
|
|
10
10
|
export type TypeId = typeof TypeId;
|
|
11
11
|
const expectedD1Database = "D1 database binding with prepare(), batch(), and exec()";
|
|
12
12
|
|
|
13
|
-
/** Typed D1 binding definition. */
|
|
14
13
|
export interface D1Definition {
|
|
15
|
-
/** Binding name as configured in `wrangler.jsonc`. */
|
|
16
14
|
readonly binding: string;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
/** Options forwarded to `@effect/sql-d1` when building a SQL client layer. */
|
|
20
17
|
export type D1SqlLayerOptions = Omit<D1Client.D1ClientConfig, "db">;
|
|
21
18
|
|
|
22
19
|
declare const D1ServiceTypeId: unique symbol;
|
|
23
20
|
|
|
24
|
-
/** Nominal service marker for D1 services created with {@link make}. */
|
|
25
21
|
export interface D1Service<Id extends string> {
|
|
26
22
|
readonly [D1ServiceTypeId]: {
|
|
27
23
|
readonly id: Id;
|
|
@@ -36,27 +32,9 @@ const isD1Database = <Candidate>(value: Candidate): value is Candidate & D1Datab
|
|
|
36
32
|
Predicate.hasProperty(value, "exec") &&
|
|
37
33
|
Predicate.isFunction(value.exec);
|
|
38
34
|
|
|
39
|
-
/**
|
|
40
|
-
* Creates a typed D1 service tag plus Effect helpers.
|
|
41
|
-
*/
|
|
42
35
|
export const make = <Id extends string>(id: Id, definition: D1Definition) =>
|
|
43
36
|
Service<D1Service<Id>>()(id, definition);
|
|
44
37
|
|
|
45
|
-
/**
|
|
46
|
-
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
47
|
-
*
|
|
48
|
-
* The returned service exposes the raw `D1Database` binding and `sqlLayer(...)`
|
|
49
|
-
* for providing `effect/unstable/sql` via `@effect/sql-d1`.
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```ts
|
|
53
|
-
* class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
|
|
54
|
-
* binding: "TODO_DB",
|
|
55
|
-
* }) {}
|
|
56
|
-
*
|
|
57
|
-
* const SqlLive = TodoDatabase.sqlLayer();
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
38
|
export const Service =
|
|
61
39
|
<Self>() =>
|
|
62
40
|
<Id extends string>(id: Id, definition: D1Definition) => {
|
package/src/DurableObject.ts
CHANGED
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
|
-
import { Effect, Layer, type ManagedRuntime, type Scope } from "effect";
|
|
2
|
+
import { Effect, Layer, type ManagedRuntime, type Scope, Tracer } from "effect";
|
|
3
3
|
|
|
4
4
|
import { NativeRequest } from "./Worker";
|
|
5
5
|
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
6
|
import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
|
|
7
7
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
8
|
+
import * as RpcDefinition from "./RpcDefinition";
|
|
8
9
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
9
10
|
import * as Runtime from "./internal/Runtime";
|
|
10
11
|
import * as Telemetry from "./internal/Telemetry";
|
|
12
|
+
import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
|
|
11
13
|
|
|
12
|
-
const reservedMethodNames
|
|
13
|
-
"constructor",
|
|
14
|
-
"dup",
|
|
15
|
-
"fetch",
|
|
16
|
-
"alarm",
|
|
17
|
-
"webSocketMessage",
|
|
18
|
-
"webSocketClose",
|
|
19
|
-
"webSocketError",
|
|
20
|
-
]);
|
|
14
|
+
const reservedMethodNames: ReadonlySet<string> = RpcDefinition.reservedMethodNames;
|
|
21
15
|
|
|
22
16
|
type RuntimeContext<ROut> = DurableObjectState | WorkerEnvironment | ROut;
|
|
23
17
|
|
|
24
18
|
type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
|
|
25
19
|
|
|
26
20
|
type FetchContext<ROut> = HandlerContext<ROut> | NativeRequest;
|
|
27
|
-
|
|
21
|
+
/** Metadata available before an event effect or its event layer starts. */
|
|
22
|
+
export interface RunOptions {
|
|
28
23
|
readonly eventLayer?: boolean;
|
|
29
|
-
|
|
24
|
+
readonly event?:
|
|
25
|
+
| "fetch"
|
|
26
|
+
| "rpc"
|
|
27
|
+
| "alarm"
|
|
28
|
+
| "webSocketMessage"
|
|
29
|
+
| "webSocketClose"
|
|
30
|
+
| "webSocketError";
|
|
31
|
+
readonly rpc?: RpcInvocationInfo;
|
|
32
|
+
}
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
/** Override to instrument the whole event, then call super[RunSymbol]. */
|
|
35
|
+
export const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
|
|
32
36
|
|
|
33
37
|
const scheduleTelemetryFlush: Effect.Effect<void, never, DurableObjectState> =
|
|
34
38
|
Telemetry.scheduleTelemetryFlush((flush) =>
|
|
@@ -74,9 +78,6 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
74
78
|
: never;
|
|
75
79
|
};
|
|
76
80
|
|
|
77
|
-
/**
|
|
78
|
-
* Options for creating a Durable Object class backed by Effect handlers.
|
|
79
|
-
*/
|
|
80
81
|
export interface DurableObjectOptions<
|
|
81
82
|
RRuntime,
|
|
82
83
|
REvent = never,
|
|
@@ -113,15 +114,10 @@ export interface DurableObjectOptions<
|
|
|
113
114
|
* does not.
|
|
114
115
|
*/
|
|
115
116
|
readonly rpc?: Rpc;
|
|
116
|
-
/**
|
|
117
|
+
/** Accept live native trace metadata. The client must also enable rpcTracing. */
|
|
118
|
+
readonly rpcTracing?: ReceiverOptions;
|
|
117
119
|
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
|
|
118
|
-
|
|
119
|
-
* Optional logical alarm processing effect.
|
|
120
|
-
*
|
|
121
|
-
* This runs before `alarm` and should be built with helpers such as
|
|
122
|
-
* `DurableObjectAlarm.processDue(...)` so the reusable scheduler stays inside
|
|
123
|
-
* the Durable Object's single managed runtime boundary.
|
|
124
|
-
*/
|
|
120
|
+
|
|
125
121
|
readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<RRuntime | REvent>>;
|
|
126
122
|
/**
|
|
127
123
|
* Optional raw alarm handler.
|
|
@@ -156,10 +152,19 @@ export interface DurableObjectOptions<
|
|
|
156
152
|
export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
|
|
157
153
|
state: globalThis.DurableObjectState,
|
|
158
154
|
env: WorkerEnv,
|
|
159
|
-
) => CloudflareDurableObject<WorkerEnv> &
|
|
155
|
+
) => CloudflareDurableObject<WorkerEnv> &
|
|
156
|
+
DurableObjectRpcApi<Rpc, ROut> & {
|
|
157
|
+
[RunSymbol]<A, E>(
|
|
158
|
+
effect: Effect.Effect<A, E, HandlerContext<ROut>>,
|
|
159
|
+
options?: RunOptions,
|
|
160
|
+
): Promise<A>;
|
|
161
|
+
};
|
|
160
162
|
|
|
161
163
|
/**
|
|
162
|
-
*
|
|
164
|
+
* Builds a Durable Object whose base `layer` lives for the in-memory instance.
|
|
165
|
+
* Cloudflare does not expose an eviction or shutdown hook, so finalizers in
|
|
166
|
+
* that layer are not guaranteed to run. Put resources that require timely
|
|
167
|
+
* release in `eventLayer`, whose scope closes after each handled event.
|
|
163
168
|
*/
|
|
164
169
|
export const make = <
|
|
165
170
|
ROut,
|
|
@@ -195,6 +200,8 @@ export const make = <
|
|
|
195
200
|
runOptions: RunOptions = {},
|
|
196
201
|
): Promise<A> {
|
|
197
202
|
const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
|
|
203
|
+
const parent = runOptions.rpc?.parent;
|
|
204
|
+
const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
|
|
198
205
|
|
|
199
206
|
if (eventLayer === undefined) {
|
|
200
207
|
// SAFETY: event-layer bypass is used only for initialize; otherwise an absent layer means REvent is never.
|
|
@@ -202,6 +209,8 @@ export const make = <
|
|
|
202
209
|
this.runtime,
|
|
203
210
|
// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
|
|
204
211
|
effect as Effect.Effect<A, E, HandlerContext<ROut>>,
|
|
212
|
+
undefined,
|
|
213
|
+
parentSpan,
|
|
205
214
|
);
|
|
206
215
|
}
|
|
207
216
|
|
|
@@ -212,7 +221,7 @@ export const make = <
|
|
|
212
221
|
REvent,
|
|
213
222
|
EventLayerError,
|
|
214
223
|
LayerError
|
|
215
|
-
>(this.runtime, effect, eventLayer);
|
|
224
|
+
>(this.runtime, effect, eventLayer, parentSpan);
|
|
216
225
|
}
|
|
217
226
|
|
|
218
227
|
fetch(request: Request): Promise<Response> {
|
|
@@ -222,7 +231,9 @@ export const make = <
|
|
|
222
231
|
return Promise.resolve(new Response("Not Found", { status: 404 }));
|
|
223
232
|
}
|
|
224
233
|
|
|
225
|
-
return this[RunSymbol](Effect.provideService(fetchHandler, NativeRequest, request)
|
|
234
|
+
return this[RunSymbol](Effect.provideService(fetchHandler, NativeRequest, request), {
|
|
235
|
+
event: "fetch",
|
|
236
|
+
});
|
|
226
237
|
}
|
|
227
238
|
|
|
228
239
|
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void {
|
|
@@ -237,13 +248,17 @@ export const make = <
|
|
|
237
248
|
: (logicalAlarms ?? rawAlarm);
|
|
238
249
|
|
|
239
250
|
if (alarmEffect !== undefined) {
|
|
240
|
-
return this[RunSymbol](alarmEffect.pipe(Effect.onExit(() => scheduleTelemetryFlush))
|
|
251
|
+
return this[RunSymbol](alarmEffect.pipe(Effect.onExit(() => scheduleTelemetryFlush)), {
|
|
252
|
+
event: "alarm",
|
|
253
|
+
});
|
|
241
254
|
}
|
|
242
255
|
}
|
|
243
256
|
|
|
244
257
|
webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void> | void {
|
|
245
258
|
if (options.webSocketMessage !== undefined) {
|
|
246
|
-
return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message)
|
|
259
|
+
return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message), {
|
|
260
|
+
event: "webSocketMessage",
|
|
261
|
+
});
|
|
247
262
|
}
|
|
248
263
|
}
|
|
249
264
|
|
|
@@ -256,13 +271,16 @@ export const make = <
|
|
|
256
271
|
if (options.webSocketClose !== undefined) {
|
|
257
272
|
return this[RunSymbol](
|
|
258
273
|
options.webSocketClose(fromWebSocket(socket), code, reason, wasClean),
|
|
274
|
+
{ event: "webSocketClose" },
|
|
259
275
|
);
|
|
260
276
|
}
|
|
261
277
|
}
|
|
262
278
|
|
|
263
279
|
webSocketError(socket: WebSocket, cause: unknown): Promise<void> | void {
|
|
264
280
|
if (options.webSocketError !== undefined) {
|
|
265
|
-
return this[RunSymbol](options.webSocketError(fromWebSocket(socket), cause)
|
|
281
|
+
return this[RunSymbol](options.webSocketError(fromWebSocket(socket), cause), {
|
|
282
|
+
event: "webSocketError",
|
|
283
|
+
});
|
|
266
284
|
}
|
|
267
285
|
}
|
|
268
286
|
}
|
|
@@ -272,8 +290,9 @@ export const make = <
|
|
|
272
290
|
EffectDurableObject.prototype,
|
|
273
291
|
options.rpc,
|
|
274
292
|
reservedMethodNames,
|
|
275
|
-
(self, effect) => self[RunSymbol](effect),
|
|
293
|
+
(self, effect, rpc) => self[RunSymbol](effect, { event: "rpc", rpc }),
|
|
276
294
|
() => scheduleTelemetryFlush,
|
|
295
|
+
options.rpcTracing,
|
|
277
296
|
);
|
|
278
297
|
|
|
279
298
|
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
|