@effect/platform-cloudflare 0.0.0-stage → 4.1.3
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/AGENTS.md +434 -0
- package/CLAUDE.md +434 -0
- package/LICENSE +21 -0
- package/README.md +505 -2
- package/ai-docs/README.md +44 -0
- package/ai-docs/package.json +36 -0
- package/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
- package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +52 -0
- package/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
- package/ai-docs/src/01_effect/01_basics/index.md +7 -0
- package/ai-docs/src/01_effect/02_schema/10_schema-basics.ts +43 -0
- package/ai-docs/src/01_effect/02_schema/index.md +7 -0
- package/ai-docs/src/01_effect/03_services/01_service.ts +45 -0
- package/ai-docs/src/01_effect/03_services/10_reference.ts +10 -0
- package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +70 -0
- package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +66 -0
- package/ai-docs/src/01_effect/03_services/index.md +5 -0
- package/ai-docs/src/01_effect/04_errors/01_error-handling.ts +30 -0
- package/ai-docs/src/01_effect/04_errors/10_catch-tags.ts +24 -0
- package/ai-docs/src/01_effect/04_errors/20_reason-errors.ts +64 -0
- package/ai-docs/src/01_effect/04_errors/index.md +1 -0
- package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +105 -0
- package/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts +31 -0
- package/ai-docs/src/01_effect/05_resources/30_layer-map.ts +86 -0
- package/ai-docs/src/01_effect/05_resources/index.md +3 -0
- package/ai-docs/src/01_effect/06_running/10_run-main.ts +30 -0
- package/ai-docs/src/01_effect/06_running/20_layer-launch.ts +27 -0
- package/ai-docs/src/01_effect/06_running/index.md +1 -0
- package/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts +56 -0
- package/ai-docs/src/01_effect/07_pubsub/index.md +3 -0
- package/ai-docs/src/03_stream/10_creating-streams.ts +103 -0
- package/ai-docs/src/03_stream/20_consuming-streams.ts +137 -0
- package/ai-docs/src/03_stream/30_encoding.ts +163 -0
- package/ai-docs/src/03_stream/index.md +4 -0
- package/ai-docs/src/04_integration/10_managed-runtime.ts +129 -0
- package/ai-docs/src/04_integration/index.md +5 -0
- package/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
- package/ai-docs/src/05_batching/index.md +3 -0
- package/ai-docs/src/06_schedule/10_schedules.ts +111 -0
- package/ai-docs/src/06_schedule/index.md +3 -0
- package/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
- package/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
- package/ai-docs/src/07_datetime/index.md +5 -0
- package/ai-docs/src/08_observability/10_logging.ts +66 -0
- package/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
- package/ai-docs/src/08_observability/index.md +7 -0
- package/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
- package/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
- package/ai-docs/src/09_testing/index.md +1 -0
- package/ai-docs/src/10_predicate/01_basics.ts +14 -0
- package/ai-docs/src/10_predicate/index.md +9 -0
- package/ai-docs/src/40_sql/10_basics.ts +175 -0
- package/ai-docs/src/40_sql/index.md +6 -0
- package/ai-docs/src/50_http-client/10_basics.ts +102 -0
- package/ai-docs/src/50_http-client/index.md +3 -0
- package/ai-docs/src/51_http-server/10_basics.ts +118 -0
- package/ai-docs/src/51_http-server/20_testing.ts +100 -0
- package/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
- package/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
- package/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
- package/ai-docs/src/51_http-server/fixtures/api/Users.ts +107 -0
- package/ai-docs/src/51_http-server/fixtures/domain/User.ts +21 -0
- package/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
- package/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +37 -0
- package/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +80 -0
- package/ai-docs/src/51_http-server/fixtures/server/Users.ts +183 -0
- package/ai-docs/src/51_http-server/index.md +3 -0
- package/ai-docs/src/52_graphql/10_basics.ts +117 -0
- package/ai-docs/src/52_graphql/20_testing.ts +45 -0
- package/ai-docs/src/52_graphql/30_batching.ts +118 -0
- package/ai-docs/src/52_graphql/40_aliased-batching.ts +124 -0
- package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
- package/ai-docs/src/52_graphql/index.md +6 -0
- package/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
- package/ai-docs/src/60_child-process/index.md +3 -0
- package/ai-docs/src/70_cli/10_basics.ts +162 -0
- package/ai-docs/src/70_cli/index.md +5 -0
- package/ai-docs/src/71_ai/10_language-model.ts +156 -0
- package/ai-docs/src/71_ai/20_tools.ts +226 -0
- package/ai-docs/src/71_ai/30_chat.ts +158 -0
- package/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
- package/ai-docs/src/71_ai/index.md +5 -0
- package/ai-docs/src/80_cluster/10_entities.ts +97 -0
- package/ai-docs/src/80_cluster/index.md +4 -0
- package/ai-docs/src/index.md +10 -0
- package/ai-docs/tsconfig.json +28 -0
- package/dist/AlchemyCloudflareCluster.d.ts +134 -0
- package/dist/AlchemyCloudflareCluster.d.ts.map +1 -0
- package/dist/AlchemyCloudflareCluster.js +111 -0
- package/dist/AlchemyCloudflareCluster.js.map +1 -0
- package/dist/CloudflareCluster.d.ts +196 -0
- package/dist/CloudflareCluster.d.ts.map +1 -0
- package/dist/CloudflareCluster.js +533 -0
- package/dist/CloudflareCluster.js.map +1 -0
- package/dist/CloudflareDurableObjectPrograms.d.ts +279 -0
- package/dist/CloudflareDurableObjectPrograms.d.ts.map +1 -0
- package/dist/CloudflareDurableObjectPrograms.js +295 -0
- package/dist/CloudflareDurableObjectPrograms.js.map +1 -0
- package/dist/CloudflareDurableObjects.d.ts +149 -0
- package/dist/CloudflareDurableObjects.d.ts.map +1 -0
- package/dist/CloudflareDurableObjects.js +337 -0
- package/dist/CloudflareDurableObjects.js.map +1 -0
- package/dist/CloudflarePersistedQueue.d.ts +36 -0
- package/dist/CloudflarePersistedQueue.d.ts.map +1 -0
- package/dist/CloudflarePersistedQueue.js +122 -0
- package/dist/CloudflarePersistedQueue.js.map +1 -0
- package/dist/CloudflareWorkflowEngine.d.ts +72 -0
- package/dist/CloudflareWorkflowEngine.d.ts.map +1 -0
- package/dist/CloudflareWorkflowEngine.js +187 -0
- package/dist/CloudflareWorkflowEngine.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/alchemyCluster.d.ts +2 -0
- package/dist/internal/alchemyCluster.d.ts.map +1 -0
- package/dist/internal/alchemyCluster.js +75 -0
- package/dist/internal/alchemyCluster.js.map +1 -0
- package/dist/internal/boundedMap.d.ts +3 -0
- package/dist/internal/boundedMap.d.ts.map +1 -0
- package/dist/internal/boundedMap.js +15 -0
- package/dist/internal/boundedMap.js.map +1 -0
- package/dist/internal/cluster.d.ts +57 -0
- package/dist/internal/cluster.d.ts.map +1 -0
- package/dist/internal/cluster.js +2 -0
- package/dist/internal/cluster.js.map +1 -0
- package/dist/internal/clusterName.d.ts +2 -0
- package/dist/internal/clusterName.d.ts.map +1 -0
- package/dist/internal/clusterName.js +16 -0
- package/dist/internal/clusterName.js.map +1 -0
- package/dist/internal/entityKeepAlive.d.ts +2 -0
- package/dist/internal/entityKeepAlive.d.ts.map +1 -0
- package/dist/internal/entityKeepAlive.js +73 -0
- package/dist/internal/entityKeepAlive.js.map +1 -0
- package/dist/internal/entityMailbox.d.ts +2 -0
- package/dist/internal/entityMailbox.d.ts.map +1 -0
- package/dist/internal/entityMailbox.js +160 -0
- package/dist/internal/entityMailbox.js.map +1 -0
- package/dist/internal/entityRegistry.d.ts +16 -0
- package/dist/internal/entityRegistry.d.ts.map +1 -0
- package/dist/internal/entityRegistry.js +22 -0
- package/dist/internal/entityRegistry.js.map +1 -0
- package/dist/internal/entityReply.d.ts +2 -0
- package/dist/internal/entityReply.d.ts.map +1 -0
- package/dist/internal/entityReply.js +35 -0
- package/dist/internal/entityReply.js.map +1 -0
- package/dist/internal/entityRuntime.d.ts +2 -0
- package/dist/internal/entityRuntime.d.ts.map +1 -0
- package/dist/internal/entityRuntime.js +823 -0
- package/dist/internal/entityRuntime.js.map +1 -0
- package/dist/internal/entitySql.d.ts +2 -0
- package/dist/internal/entitySql.d.ts.map +1 -0
- package/dist/internal/entitySql.js +29 -0
- package/dist/internal/entitySql.js.map +1 -0
- package/dist/internal/entityStorage.d.ts +2 -0
- package/dist/internal/entityStorage.d.ts.map +1 -0
- package/dist/internal/entityStorage.js +98 -0
- package/dist/internal/entityStorage.js.map +1 -0
- package/dist/internal/entityWire.d.ts +2 -0
- package/dist/internal/entityWire.d.ts.map +1 -0
- package/dist/internal/entityWire.js +146 -0
- package/dist/internal/entityWire.js.map +1 -0
- package/dist/internal/queueRuntime.d.ts +2 -0
- package/dist/internal/queueRuntime.d.ts.map +1 -0
- package/dist/internal/queueRuntime.js +88 -0
- package/dist/internal/queueRuntime.js.map +1 -0
- package/dist/internal/queueStorage.d.ts +2 -0
- package/dist/internal/queueStorage.d.ts.map +1 -0
- package/dist/internal/queueStorage.js +129 -0
- package/dist/internal/queueStorage.js.map +1 -0
- package/dist/internal/registry.d.ts +10 -0
- package/dist/internal/registry.d.ts.map +1 -0
- package/dist/internal/registry.js +24 -0
- package/dist/internal/registry.js.map +1 -0
- package/dist/internal/singletonRegistry.d.ts +2 -0
- package/dist/internal/singletonRegistry.d.ts.map +1 -0
- package/dist/internal/singletonRegistry.js +9 -0
- package/dist/internal/singletonRegistry.js.map +1 -0
- package/dist/internal/singletonRuntime.d.ts +2 -0
- package/dist/internal/singletonRuntime.d.ts.map +1 -0
- package/dist/internal/singletonRuntime.js +42 -0
- package/dist/internal/singletonRuntime.js.map +1 -0
- package/dist/internal/singletonStorage.d.ts +2 -0
- package/dist/internal/singletonStorage.d.ts.map +1 -0
- package/dist/internal/singletonStorage.js +34 -0
- package/dist/internal/singletonStorage.js.map +1 -0
- package/dist/internal/transport.d.ts +2 -0
- package/dist/internal/transport.d.ts.map +1 -0
- package/dist/internal/transport.js +49 -0
- package/dist/internal/transport.js.map +1 -0
- package/dist/internal/workflowRegistry.d.ts +2 -0
- package/dist/internal/workflowRegistry.d.ts.map +1 -0
- package/dist/internal/workflowRegistry.js +29 -0
- package/dist/internal/workflowRegistry.js.map +1 -0
- package/dist/internal/workflowRuntime.d.ts +2 -0
- package/dist/internal/workflowRuntime.d.ts.map +1 -0
- package/dist/internal/workflowRuntime.js +322 -0
- package/dist/internal/workflowRuntime.js.map +1 -0
- package/dist/internal/workflowStorage.d.ts +2 -0
- package/dist/internal/workflowStorage.d.ts.map +1 -0
- package/dist/internal/workflowStorage.js +113 -0
- package/dist/internal/workflowStorage.js.map +1 -0
- package/dist/internal/workflowWire.d.ts +2 -0
- package/dist/internal/workflowWire.d.ts.map +1 -0
- package/dist/internal/workflowWire.js +52 -0
- package/dist/internal/workflowWire.js.map +1 -0
- package/package.json +74 -3
- package/src/AlchemyCloudflareCluster.ts +200 -0
- package/src/CloudflareCluster.ts +704 -0
- package/src/CloudflareDurableObjectPrograms.ts +543 -0
- package/src/CloudflareDurableObjects.ts +430 -0
- package/src/CloudflarePersistedQueue.ts +189 -0
- package/src/CloudflareWorkflowEngine.ts +220 -0
- package/src/index.ts +38 -0
- package/src/internal/alchemyCluster.ts +120 -0
- package/src/internal/boundedMap.ts +15 -0
- package/src/internal/cluster.ts +57 -0
- package/src/internal/clusterName.ts +19 -0
- package/src/internal/entityKeepAlive.ts +121 -0
- package/src/internal/entityMailbox.ts +324 -0
- package/src/internal/entityRegistry.ts +46 -0
- package/src/internal/entityReply.ts +54 -0
- package/src/internal/entityRuntime.ts +1318 -0
- package/src/internal/entitySql.ts +60 -0
- package/src/internal/entityStorage.ts +168 -0
- package/src/internal/entityWire.ts +204 -0
- package/src/internal/queueRuntime.ts +174 -0
- package/src/internal/queueStorage.ts +255 -0
- package/src/internal/registry.ts +31 -0
- package/src/internal/singletonRegistry.ts +21 -0
- package/src/internal/singletonRuntime.ts +88 -0
- package/src/internal/singletonStorage.ts +64 -0
- package/src/internal/transport.ts +67 -0
- package/src/internal/workflowRegistry.ts +78 -0
- package/src/internal/workflowRuntime.ts +443 -0
- package/src/internal/workflowStorage.ts +234 -0
- package/src/internal/workflowWire.ts +123 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as Option from "effect/Option";
|
|
4
|
+
import * as Schedule from "effect/Schedule";
|
|
5
|
+
import * as Workflow from "effect/workflow/Workflow";
|
|
6
|
+
import * as WorkflowEngine from "effect/workflow/WorkflowEngine";
|
|
7
|
+
/**
|
|
8
|
+
* The workflow Durable Object namespace binding the engine is built from.
|
|
9
|
+
*
|
|
10
|
+
* @stability experimental
|
|
11
|
+
* @category layers
|
|
12
|
+
* @since 4.1.3
|
|
13
|
+
*/
|
|
14
|
+
export interface LayerOptions {
|
|
15
|
+
readonly workflowNamespace: DurableObjectNamespace;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates the `WorkflowEngine` service backed by the workflow Durable Object
|
|
19
|
+
* namespace binding.
|
|
20
|
+
*
|
|
21
|
+
* **Details**
|
|
22
|
+
*
|
|
23
|
+
* Inside a workflow Durable Object the engine operates on the local execution
|
|
24
|
+
* handle, so activities and deferred reads never leave the object. Everywhere
|
|
25
|
+
* else it resolves the target execution's object with `getByName` and drives
|
|
26
|
+
* it over the same-Worker binding.
|
|
27
|
+
*
|
|
28
|
+
* @stability experimental
|
|
29
|
+
* @category constructors
|
|
30
|
+
* @since 4.1.3
|
|
31
|
+
*/
|
|
32
|
+
export declare const make: (options: LayerOptions) => Effect.Effect<{
|
|
33
|
+
readonly register: <Name extends string, Payload extends Workflow.AnyStructSchema, Success extends import("effect/Schema").Top, Error extends import("effect/Schema").Top, R>(workflow: Workflow.Workflow<Name, Payload, Success, Error>, execute: (payload: Payload["Type"], executionId: string) => Effect.Effect<Success["Type"], Error["Type"], R>) => Effect.Effect<void, never, import("effect/Scope").Scope | Exclude<R, WorkflowEngine.WorkflowEngine | WorkflowEngine.WorkflowInstance | Workflow.Execution<Name> | import("effect/Scope").Scope> | Payload["DecodingServices"] | Payload["EncodingServices"] | Success["DecodingServices"] | Success["EncodingServices"] | Error["DecodingServices"] | Error["EncodingServices"]>;
|
|
34
|
+
readonly execute: <Name extends string, Payload extends Workflow.AnyStructSchema, Success extends import("effect/Schema").Top, Error extends import("effect/Schema").Top, const Discard extends boolean = false>(workflow: Workflow.Workflow<Name, Payload, Success, Error>, options: {
|
|
35
|
+
readonly executionId: string;
|
|
36
|
+
readonly payload: Payload["Type"];
|
|
37
|
+
readonly discard?: Discard | undefined;
|
|
38
|
+
readonly detached?: boolean | undefined;
|
|
39
|
+
readonly suspendedRetrySchedule?: Schedule.Schedule<any, unknown> | undefined;
|
|
40
|
+
}) => Effect.Effect<Discard extends true ? string : Success["Type"], Error["Type"], Payload["EncodingServices"] | Success["DecodingServices"] | Error["DecodingServices"]>;
|
|
41
|
+
readonly poll: <Name extends string, Payload extends Workflow.AnyStructSchema, Success extends import("effect/Schema").Top, Error extends import("effect/Schema").Top>(workflow: Workflow.Workflow<Name, Payload, Success, Error>, executionId: string) => Effect.Effect<Option.Option<Workflow.Result<Success["Type"], Error["Type"]>>, never, Success["DecodingServices"] | Error["DecodingServices"]>;
|
|
42
|
+
readonly interrupt: (workflow: Workflow.Any, executionId: string) => Effect.Effect<void>;
|
|
43
|
+
readonly interruptUnsafe: (workflow: Workflow.Any, executionId: string) => Effect.Effect<void>;
|
|
44
|
+
readonly resume: (workflow: Workflow.Any, executionId: string) => Effect.Effect<void>;
|
|
45
|
+
readonly activityExecute: <Success extends import("effect/Schema").Constraint, Error extends import("effect/Schema").Constraint, R>(activity: import("effect/workflow/Activity").Activity<Success, Error, R>, attempt: number) => Effect.Effect<Workflow.Result<Success["Type"], Error["Type"]>, never, Success["DecodingServices"] | Error["DecodingServices"] | R | WorkflowEngine.WorkflowInstance>;
|
|
46
|
+
readonly deferredResult: <Success extends import("effect/Schema").Constraint, Error extends import("effect/Schema").Constraint>(deferred: import("effect/workflow/DurableDeferred").DurableDeferred<Success, Error>) => Effect.Effect<Option.Option<import("effect/Exit").Exit<Success["Type"], Error["Type"]>>, never, WorkflowEngine.WorkflowInstance>;
|
|
47
|
+
readonly deferredDone: <Success extends import("effect/Schema").Constraint, Error extends import("effect/Schema").Constraint>(deferred: import("effect/workflow/DurableDeferred").DurableDeferred<Success, Error>, options: {
|
|
48
|
+
readonly workflowName: string;
|
|
49
|
+
readonly executionId: string;
|
|
50
|
+
readonly deferredName: string;
|
|
51
|
+
readonly exit: import("effect/Exit").Exit<Success["Type"], Error["Type"]>;
|
|
52
|
+
}) => Effect.Effect<void, never, Success["EncodingServices"] | Error["EncodingServices"]>;
|
|
53
|
+
readonly scheduleClock: (workflow: Workflow.Any, options: {
|
|
54
|
+
readonly executionId: string;
|
|
55
|
+
readonly clock: import("effect/workflow/DurableClock").DurableClock;
|
|
56
|
+
}) => Effect.Effect<void>;
|
|
57
|
+
}, never, never>;
|
|
58
|
+
/**
|
|
59
|
+
* Layer that provides the `WorkflowEngine` backed by the workflow Durable
|
|
60
|
+
* Object namespace binding.
|
|
61
|
+
*
|
|
62
|
+
* **Details**
|
|
63
|
+
*
|
|
64
|
+
* `CloudflareCluster.layer` already includes this layer; use it directly only
|
|
65
|
+
* when the workflow engine is needed without the rest of the cluster.
|
|
66
|
+
*
|
|
67
|
+
* @stability experimental
|
|
68
|
+
* @category layers
|
|
69
|
+
* @since 4.1.3
|
|
70
|
+
*/
|
|
71
|
+
export declare const layer: (options: LayerOptions) => Layer.Layer<WorkflowEngine.WorkflowEngine>;
|
|
72
|
+
//# sourceMappingURL=CloudflareWorkflowEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareWorkflowEngine.d.ts","sourceRoot":"","sources":["../src/CloudflareWorkflowEngine.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,QAAQ,MAAM,0BAA0B,CAAA;AACpD,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAA;AAahE;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,CAAA;CACnD;AAOD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;gBAsIf,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,YAAa,YAAY,KAAG,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAC3B,CAAA"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs durable workflows on the dedicated workflow Durable Object class.
|
|
3
|
+
*
|
|
4
|
+
* On this path one workflow execution is one Durable Object: the engine
|
|
5
|
+
* encodes `(workflowName, executionId)` into a Durable Object name with the
|
|
6
|
+
* same length-prefix scheme as entities and resolves the object through the
|
|
7
|
+
* workflow namespace binding. Execution state, activity results keyed
|
|
8
|
+
* `${name}/${attempt}`, durable deferred exits, and the clock due table all
|
|
9
|
+
* live on the object's SQLite storage behind its single alarm.
|
|
10
|
+
*
|
|
11
|
+
* Every `DurableClock` is durable on this engine: the in-memory short-sleep
|
|
12
|
+
* path is disabled, so even sub-minute sleeps persist a due row and arm the
|
|
13
|
+
* alarm.
|
|
14
|
+
*
|
|
15
|
+
* @stability experimental
|
|
16
|
+
* @since 4.1.3
|
|
17
|
+
*/
|
|
18
|
+
import { Clock } from "effect/Clock";
|
|
19
|
+
import * as Context from "effect/Context";
|
|
20
|
+
import * as Duration from "effect/Duration";
|
|
21
|
+
import * as Effect from "effect/Effect";
|
|
22
|
+
import * as Layer from "effect/Layer";
|
|
23
|
+
import * as Option from "effect/Option";
|
|
24
|
+
import * as Schedule from "effect/Schedule";
|
|
25
|
+
import * as Workflow from "effect/workflow/Workflow";
|
|
26
|
+
import * as WorkflowEngine from "effect/workflow/WorkflowEngine";
|
|
27
|
+
import { encodeName } from "./internal/clusterName.js";
|
|
28
|
+
import { callDurableObject } from "./internal/transport.js";
|
|
29
|
+
import { CurrentExecutionHandle, registerWorkflow, unregisterWorkflow } from "./internal/workflowRegistry.js";
|
|
30
|
+
import { InterruptSignalName } from "./internal/workflowRuntime.js";
|
|
31
|
+
import { decodeExit, decodeResult, encodeExit, encodePayload } from "./internal/workflowWire.js";
|
|
32
|
+
const retryPolicy = /*#__PURE__*/Schedule.min([/*#__PURE__*/Schedule.exponential(200, 1.5), /*#__PURE__*/Schedule.spaced("1 minute")]);
|
|
33
|
+
/**
|
|
34
|
+
* Creates the `WorkflowEngine` service backed by the workflow Durable Object
|
|
35
|
+
* namespace binding.
|
|
36
|
+
*
|
|
37
|
+
* **Details**
|
|
38
|
+
*
|
|
39
|
+
* Inside a workflow Durable Object the engine operates on the local execution
|
|
40
|
+
* handle, so activities and deferred reads never leave the object. Everywhere
|
|
41
|
+
* else it resolves the target execution's object with `getByName` and drives
|
|
42
|
+
* it over the same-Worker binding.
|
|
43
|
+
*
|
|
44
|
+
* @stability experimental
|
|
45
|
+
* @category constructors
|
|
46
|
+
* @since 4.1.3
|
|
47
|
+
*/
|
|
48
|
+
export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
49
|
+
const clock = yield* Clock;
|
|
50
|
+
// Inside a run the execution's own handle avoids a self-RPC; every other
|
|
51
|
+
// target resolves to its Durable Object stub through the namespace binding.
|
|
52
|
+
//
|
|
53
|
+
// As in `ClusterWorkflowEngine`, a transport failure is retried on an
|
|
54
|
+
// exponential schedule capped at one minute and only then becomes a defect:
|
|
55
|
+
// every call here is idempotent by key, and a permanently unreachable object
|
|
56
|
+
// stalls the caller instead of failing a workflow for good. The object never
|
|
57
|
+
// rejects for application reasons, so a child's own defect arrives as its
|
|
58
|
+
// stored `Complete` result and is never retried.
|
|
59
|
+
const callExecution = (workflowName, executionId, call) => Effect.flatMap(Effect.serviceOption(CurrentExecutionHandle), handle => {
|
|
60
|
+
if (Option.isSome(handle) && handle.value.executionId === executionId) {
|
|
61
|
+
const local = handle.value;
|
|
62
|
+
return Effect.promise(() => call(local));
|
|
63
|
+
}
|
|
64
|
+
const name = encodeName(workflowName, executionId);
|
|
65
|
+
return callDurableObject({
|
|
66
|
+
resolve: () => options.workflowNamespace.getByName(name),
|
|
67
|
+
call,
|
|
68
|
+
retry: retryPolicy
|
|
69
|
+
}).pipe(Effect.orDie);
|
|
70
|
+
});
|
|
71
|
+
const localHandle = Effect.fnUntraced(function* (operation) {
|
|
72
|
+
const instance = yield* WorkflowEngine.WorkflowInstance;
|
|
73
|
+
const handle = yield* Effect.serviceOption(CurrentExecutionHandle);
|
|
74
|
+
if (Option.isNone(handle)) {
|
|
75
|
+
return yield* Effect.die(`CloudflareWorkflowEngine: ${operation} is only available inside a workflow Durable Object execution`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
instance,
|
|
79
|
+
handle: handle.value
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
return WorkflowEngine.makeUnsafe({
|
|
83
|
+
register: Effect.fnUntraced(function* (workflow, execute) {
|
|
84
|
+
const context = yield* Effect.context();
|
|
85
|
+
const registration = {
|
|
86
|
+
workflow,
|
|
87
|
+
execute,
|
|
88
|
+
context
|
|
89
|
+
};
|
|
90
|
+
if (!registerWorkflow(workflow._tag, registration)) return;
|
|
91
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
92
|
+
unregisterWorkflow(workflow._tag, registration);
|
|
93
|
+
}));
|
|
94
|
+
}),
|
|
95
|
+
execute: Effect.fnUntraced(function* (workflow, opts) {
|
|
96
|
+
const context = yield* Effect.context();
|
|
97
|
+
const payload = yield* encodePayload(workflow, opts.payload, context);
|
|
98
|
+
// A detached child keeps no parent, so neither its completion nor a
|
|
99
|
+
// failed run wakes the parent.
|
|
100
|
+
const parent = opts.parent === undefined || opts.detached ? undefined : {
|
|
101
|
+
workflowName: opts.parent.workflow._tag,
|
|
102
|
+
executionId: opts.parent.executionId
|
|
103
|
+
};
|
|
104
|
+
const text = yield* callExecution(workflow._tag, opts.executionId, stub => stub.run(payload, {
|
|
105
|
+
discard: opts.discard,
|
|
106
|
+
parent
|
|
107
|
+
}));
|
|
108
|
+
if (opts.discard) return undefined;
|
|
109
|
+
return yield* decodeResult(workflow, text, context);
|
|
110
|
+
}),
|
|
111
|
+
poll: Effect.fnUntraced(function* (workflow, executionId) {
|
|
112
|
+
const context = yield* Effect.context();
|
|
113
|
+
const text = yield* callExecution(workflow._tag, executionId, stub => stub.poll());
|
|
114
|
+
if (text === undefined) return Option.none();
|
|
115
|
+
return Option.some(yield* decodeResult(workflow, text, context));
|
|
116
|
+
}),
|
|
117
|
+
interrupt: (workflow, executionId) => callExecution(workflow._tag, executionId, stub => stub.interrupt()),
|
|
118
|
+
interruptUnsafe: (workflow, executionId) => callExecution(workflow._tag, executionId, stub => stub.interruptUnsafe()),
|
|
119
|
+
resume: (workflow, executionId) => callExecution(workflow._tag, executionId, stub => stub.resume()),
|
|
120
|
+
activityExecute: Effect.fnUntraced(function* (activity, attempt) {
|
|
121
|
+
const {
|
|
122
|
+
handle,
|
|
123
|
+
instance
|
|
124
|
+
} = yield* localHandle("Activity execution");
|
|
125
|
+
const key = `${activity.name}/${attempt}`;
|
|
126
|
+
const stored = handle.loadActivity(key);
|
|
127
|
+
if (stored !== undefined) {
|
|
128
|
+
return new Workflow.Complete({
|
|
129
|
+
exit: yield* decodeExit(stored, Context.empty())
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// A pending interrupt replays only to rebuild compensations, so it must
|
|
133
|
+
// not start new activities. Suspending lets the run apply the interrupt.
|
|
134
|
+
if (!instance.interrupted && handle.loadDeferred(InterruptSignalName) !== undefined) {
|
|
135
|
+
instance.suspended = true;
|
|
136
|
+
return new Workflow.Suspended({});
|
|
137
|
+
}
|
|
138
|
+
const activityInstance = WorkflowEngine.WorkflowInstance.initial(instance.workflow, instance.executionId);
|
|
139
|
+
activityInstance.interrupted = instance.interrupted;
|
|
140
|
+
const result = yield* activity.executeEncoded.pipe(Workflow.intoResult, Effect.provideService(WorkflowEngine.WorkflowInstance, activityInstance));
|
|
141
|
+
if (result._tag === "Complete") {
|
|
142
|
+
handle.saveActivity(key, yield* encodeExit(result.exit, Context.empty()));
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}),
|
|
146
|
+
deferredResult: Effect.fnUntraced(function* (deferred) {
|
|
147
|
+
const {
|
|
148
|
+
handle
|
|
149
|
+
} = yield* localHandle("DurableDeferred reads");
|
|
150
|
+
const stored = handle.loadDeferred(deferred.name);
|
|
151
|
+
if (stored === undefined) return Option.none();
|
|
152
|
+
return Option.some(yield* decodeExit(stored, Context.empty()));
|
|
153
|
+
}),
|
|
154
|
+
deferredDone: Effect.fnUntraced(function* ({
|
|
155
|
+
deferredName,
|
|
156
|
+
executionId,
|
|
157
|
+
exit,
|
|
158
|
+
workflowName
|
|
159
|
+
}) {
|
|
160
|
+
const text = yield* encodeExit(exit, Context.empty());
|
|
161
|
+
const handle = yield* Effect.serviceOption(CurrentExecutionHandle);
|
|
162
|
+
if (Option.isSome(handle) && handle.value.executionId === executionId) {
|
|
163
|
+
return yield* handle.value.completeOwnDeferred(deferredName, text);
|
|
164
|
+
}
|
|
165
|
+
yield* callExecution(workflowName, executionId, stub => stub.deferredDone(deferredName, text));
|
|
166
|
+
}),
|
|
167
|
+
scheduleClock: Effect.fnUntraced(function* (workflow, opts) {
|
|
168
|
+
const wakeUp = clock.currentTimeMillisUnsafe() + Math.ceil(Duration.toMillis(opts.clock.duration));
|
|
169
|
+
yield* callExecution(workflow._tag, opts.executionId, stub => stub.scheduleClock(opts.clock.name, opts.clock.deferred.name, wakeUp));
|
|
170
|
+
})
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
/**
|
|
174
|
+
* Layer that provides the `WorkflowEngine` backed by the workflow Durable
|
|
175
|
+
* Object namespace binding.
|
|
176
|
+
*
|
|
177
|
+
* **Details**
|
|
178
|
+
*
|
|
179
|
+
* `CloudflareCluster.layer` already includes this layer; use it directly only
|
|
180
|
+
* when the workflow engine is needed without the rest of the cluster.
|
|
181
|
+
*
|
|
182
|
+
* @stability experimental
|
|
183
|
+
* @category layers
|
|
184
|
+
* @since 4.1.3
|
|
185
|
+
*/
|
|
186
|
+
export const layer = options => Layer.effect(WorkflowEngine.WorkflowEngine)(make(options));
|
|
187
|
+
//# sourceMappingURL=CloudflareWorkflowEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudflareWorkflowEngine.js","names":[],"sources":["../src/CloudflareWorkflowEngine.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 4.1.3
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @stability experimental
|
|
6
|
+
* @since 4.1.3
|
|
7
|
+
*/
|
|
8
|
+
export * as CloudflareCluster from "./CloudflareCluster.ts";
|
|
9
|
+
/**
|
|
10
|
+
* @stability experimental
|
|
11
|
+
* @since 4.1.3
|
|
12
|
+
*/
|
|
13
|
+
export * as CloudflareDurableObjectPrograms from "./CloudflareDurableObjectPrograms.ts";
|
|
14
|
+
/**
|
|
15
|
+
* @stability experimental
|
|
16
|
+
* @since 4.1.3
|
|
17
|
+
*/
|
|
18
|
+
export * as CloudflareDurableObjects from "./CloudflareDurableObjects.ts";
|
|
19
|
+
/**
|
|
20
|
+
* @stability experimental
|
|
21
|
+
* @since 4.1.3
|
|
22
|
+
*/
|
|
23
|
+
export * as CloudflarePersistedQueue from "./CloudflarePersistedQueue.ts";
|
|
24
|
+
/**
|
|
25
|
+
* @stability experimental
|
|
26
|
+
* @since 4.1.3
|
|
27
|
+
*/
|
|
28
|
+
export * as CloudflareWorkflowEngine from "./CloudflareWorkflowEngine.ts";
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;;GAGG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;GAGG;AACH,OAAO,KAAK,+BAA+B,MAAM,sCAAsC,CAAA;AAEvF;;;GAGG;AACH,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAA;AAEzE;;;GAGG;AACH,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAA;AAEzE;;;GAGG;AACH,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 4.1.3
|
|
3
|
+
*/
|
|
4
|
+
// The index stays free of the optional `alchemy` peer dependency, so the
|
|
5
|
+
// Alchemy integration is exposed only through its own entrypoint:
|
|
6
|
+
// `@effect/platform-cloudflare/AlchemyCloudflareCluster`.
|
|
7
|
+
// @barrel(Cloudflare*.ts): Auto-generated exports. Do not edit manually.
|
|
8
|
+
/**
|
|
9
|
+
* @stability experimental
|
|
10
|
+
* @since 4.1.3
|
|
11
|
+
*/
|
|
12
|
+
export * as CloudflareCluster from "./CloudflareCluster.js";
|
|
13
|
+
/**
|
|
14
|
+
* @stability experimental
|
|
15
|
+
* @since 4.1.3
|
|
16
|
+
*/
|
|
17
|
+
export * as CloudflareDurableObjectPrograms from "./CloudflareDurableObjectPrograms.js";
|
|
18
|
+
/**
|
|
19
|
+
* @stability experimental
|
|
20
|
+
* @since 4.1.3
|
|
21
|
+
*/
|
|
22
|
+
export * as CloudflareDurableObjects from "./CloudflareDurableObjects.js";
|
|
23
|
+
/**
|
|
24
|
+
* @stability experimental
|
|
25
|
+
* @since 4.1.3
|
|
26
|
+
*/
|
|
27
|
+
export * as CloudflarePersistedQueue from "./CloudflarePersistedQueue.js";
|
|
28
|
+
/**
|
|
29
|
+
* @stability experimental
|
|
30
|
+
* @since 4.1.3
|
|
31
|
+
*/
|
|
32
|
+
export * as CloudflareWorkflowEngine from "./CloudflareWorkflowEngine.js";
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;AACA;AACA;AACA;AAEA;;;;AAIA,OAAO,KAAK,iBAAiB,MAAM,wBAAwB;AAE3D;;;;AAIA,OAAO,KAAK,+BAA+B,MAAM,sCAAsC;AAEvF;;;;AAIA,OAAO,KAAK,wBAAwB,MAAM,+BAA+B;AAEzE;;;;AAIA,OAAO,KAAK,wBAAwB,MAAM,+BAA+B;AAEzE;;;;AAIA,OAAO,KAAK,wBAAwB,MAAM,+BAA+B","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alchemyCluster.d.ts","sourceRoot":"","sources":["../../src/internal/alchemyCluster.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Scope from "effect/Scope";
|
|
5
|
+
import * as CloudflareCluster from "../CloudflareCluster.js";
|
|
6
|
+
import { boundedRetry, callDurableObject } from "./transport.js";
|
|
7
|
+
/** @internal */
|
|
8
|
+
export const makeClusterHandle = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
9
|
+
const entityNamespace = options.env.ClusterEntity;
|
|
10
|
+
const workflowNamespace = options.env.ClusterWorkflow;
|
|
11
|
+
const queueNamespace = options.env.ClusterDurableQueue;
|
|
12
|
+
const singletonNamespace = options.env.ClusterSingleton;
|
|
13
|
+
// The isolate-lifetime scope. workerd has no isolate-teardown hook, so it
|
|
14
|
+
// is never closed; the built services live for the isolate, exactly like
|
|
15
|
+
// the Wrangler path's `setInitializer` build.
|
|
16
|
+
const scope = Scope.makeUnsafe();
|
|
17
|
+
const context = yield* Layer.buildWithScope(options.layer.pipe(Layer.provideMerge(CloudflareCluster.layer({
|
|
18
|
+
entities: options.entities,
|
|
19
|
+
entityNamespace,
|
|
20
|
+
workflowNamespace,
|
|
21
|
+
queueNamespace,
|
|
22
|
+
singletonNamespace,
|
|
23
|
+
keepAliveHeartbeat: options.keepAliveHeartbeat
|
|
24
|
+
}))), scope).pipe(
|
|
25
|
+
// Drop the build's memo map so a Layer the user provides inside a
|
|
26
|
+
// handler builds per event instead of sharing one instance pinned to
|
|
27
|
+
// the first request's IoContext.
|
|
28
|
+
Effect.map(Context.omit(Layer.CurrentMemoMap)));
|
|
29
|
+
const handle = {
|
|
30
|
+
provide: effect => Effect.provideContext(effect, context),
|
|
31
|
+
// Only a transport failure rejects `wake()`, so retrying never reruns a
|
|
32
|
+
// failed singleton. A failed run still fails the Cron Trigger.
|
|
33
|
+
wake: name => () => callDurableObject({
|
|
34
|
+
resolve: () => singletonNamespace.getByName(`Singleton/${name}`),
|
|
35
|
+
call: stub => stub.wake(),
|
|
36
|
+
retry: boundedRetry
|
|
37
|
+
}).pipe(Effect.orDie, Effect.flatMap(result => result?._tag === "Failure" ? Effect.die(new Error(`Singleton "${name}" failed: ${result.message}`)) : Effect.void)),
|
|
38
|
+
entityNamespace,
|
|
39
|
+
workflowNamespace,
|
|
40
|
+
queueNamespace,
|
|
41
|
+
singletonNamespace,
|
|
42
|
+
context
|
|
43
|
+
};
|
|
44
|
+
return handle;
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* The plan-time handle: the deploy-machine evaluation only discovers the
|
|
48
|
+
* Durable Object declarations, and nothing returned from the Worker init
|
|
49
|
+
* program runs outside the deployed isolate.
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export const inertClusterHandle = () => ({
|
|
54
|
+
provide: effect => effect,
|
|
55
|
+
wake: () => () => Effect.void,
|
|
56
|
+
get entityNamespace() {
|
|
57
|
+
return planTimeOnly("entityNamespace");
|
|
58
|
+
},
|
|
59
|
+
get workflowNamespace() {
|
|
60
|
+
return planTimeOnly("workflowNamespace");
|
|
61
|
+
},
|
|
62
|
+
get queueNamespace() {
|
|
63
|
+
return planTimeOnly("queueNamespace");
|
|
64
|
+
},
|
|
65
|
+
get singletonNamespace() {
|
|
66
|
+
return planTimeOnly("singletonNamespace");
|
|
67
|
+
},
|
|
68
|
+
get context() {
|
|
69
|
+
return planTimeOnly("context");
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const planTimeOnly = field => {
|
|
73
|
+
throw new Error(`AlchemyCloudflareCluster: '${field}' is only available at runtime, not during plan evaluation`);
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=alchemyCluster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alchemyCluster.js","names":[],"sources":["../../src/internal/alchemyCluster.ts"],"sourcesContent":[null],"mappings":"AASA,OAAO,KAAK,OAAO,MAAM,gBAAgB;AAEzC,OAAO,KAAK,MAAM,MAAM,eAAe;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc;AACrC,OAAO,KAAK,KAAK,MAAM,cAAc;AACrC,OAAO,KAAK,iBAAiB,MAAM,yBAAyB;AAG5D,SAAS,YAAY,EAAE,iBAAiB,QAAQ,gBAAgB;AAehE;AACA,OAAO,MAAM,iBAAiB,gBAAG,MAAM,CAAC,UAAU,CAAC,WAAU,OAA6B;EACxF,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,aAAuC;EAC3E,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,eAAyC;EAC/E,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,mBAA6C;EAChF,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAA0C;EAEjF;EACA;EACA;EACA,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE;EAChC,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,cAAc,CACzC,OAAO,CAAC,KAAK,CAAC,IAAI,CAChB,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,KAAK,CAAC;IACzC,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,eAAe;IACf,iBAAiB;IACjB,cAAc;IACd,kBAAkB;IAClB,kBAAkB,EAAE,OAAO,CAAC;GAC7B,CAAC,CAAC,CACJ,EACD,KAAK,CACN,CAAC,IAAI;EACJ;EACA;EACA;EACA,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAC/C;EAED,MAAM,MAAM,GAAmB;IAC7B,OAAO,EAAG,MAAM,IAAK,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3D;IACA;IACA,IAAI,EAAG,IAAI,IAAK,MACd,iBAAiB,CAAC;MAChB,OAAO,EAAE,MAAM,kBAAkB,CAAC,SAAS,CAAC,aAAa,IAAI,EAAE,CAA6B;MAC5F,IAAI,EAAG,IAAI,IAAK,IAAI,CAAC,IAAI,EAAE;MAC3B,KAAK,EAAE;KACR,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,KAAK,EACZ,MAAM,CAAC,OAAO,CAAE,MAAM,IACpB,MAAM,EAAE,IAAI,KAAK,SAAS,GACtB,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,cAAc,IAAI,aAAa,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GACtE,MAAM,CAAC,IAAI,CAChB,CACF;IACH,eAAe;IACf,iBAAiB;IACjB,cAAc;IACd,kBAAkB;IAClB;GACD;EACD,OAAO,MAAM;AACf,CAAC,CAAC;AAEF;;;;;;;AAOA,OAAO,MAAM,kBAAkB,GAAG,OAAuB;EACvD,OAAO,EAAG,MAAM,IAAK,MAAM;EAC3B,IAAI,EAAE,MAAM,MAAM,MAAM,CAAC,IAAI;EAC7B,IAAI,eAAe;IACjB,OAAO,YAAY,CAAC,iBAAiB,CAAC;EACxC,CAAC;EACD,IAAI,iBAAiB;IACnB,OAAO,YAAY,CAAC,mBAAmB,CAAC;EAC1C,CAAC;EACD,IAAI,cAAc;IAChB,OAAO,YAAY,CAAC,gBAAgB,CAAC;EACvC,CAAC;EACD,IAAI,kBAAkB;IACpB,OAAO,YAAY,CAAC,oBAAoB,CAAC;EAC3C,CAAC;EACD,IAAI,OAAO;IACT,OAAO,YAAY,CAAC,SAAS,CAAC;EAChC;CACD,CAAC;AAEF,MAAM,YAAY,GAAI,KAAa,IAAW;EAC5C,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,4DAA4D,CAChG;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedMap.d.ts","sourceRoot":"","sources":["../../src/internal/boundedMap.ts"],"names":[],"mappings":"AAAA,gBAAgB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
/**
|
|
3
|
+
* Inserts refreshing the key's recency, then evicts the oldest entry once the
|
|
4
|
+
* map exceeds `capacity`.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export const setWithEviction = (map, key, value, capacity) => {
|
|
9
|
+
map.delete(key);
|
|
10
|
+
map.set(key, value);
|
|
11
|
+
if (map.size <= capacity) return;
|
|
12
|
+
const oldest = map.keys().next().value;
|
|
13
|
+
if (oldest !== undefined) map.delete(oldest);
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=boundedMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundedMap.js","names":[],"sources":["../../src/internal/boundedMap.ts"],"sourcesContent":[null],"mappings":"AAAA;AAEA;;;;;;AAMA,OAAO,MAAM,eAAe,GAAG,CAAO,GAAc,EAAE,GAAM,EAAE,KAAQ,EAAE,QAAgB,KAAU;EAChG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;EACf,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;EACnB,IAAI,GAAG,CAAC,IAAI,IAAI,QAAQ,EAAE;EAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;EACtC,IAAI,MAAM,KAAK,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;AAC9C,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared cluster handle types without the Alchemy dependency graph.
|
|
3
|
+
*
|
|
4
|
+
* @since 4.1.3
|
|
5
|
+
*/
|
|
6
|
+
import type * as Context from "effect/Context";
|
|
7
|
+
import type * as Effect from "effect/Effect";
|
|
8
|
+
/**
|
|
9
|
+
* The handle returned by `AlchemyCloudflareCluster.make`.
|
|
10
|
+
*
|
|
11
|
+
* **Gotchas**
|
|
12
|
+
*
|
|
13
|
+
* Namespace bindings and `context` are available only in the deployed runtime.
|
|
14
|
+
* Reading them during plan evaluation throws a diagnostic; `provide` and
|
|
15
|
+
* `wake` remain inert during planning.
|
|
16
|
+
*
|
|
17
|
+
* @category models
|
|
18
|
+
* @since 4.1.3
|
|
19
|
+
*/
|
|
20
|
+
export interface Cluster<in R = never> {
|
|
21
|
+
/**
|
|
22
|
+
* Attaches the built cluster context to a user Effect, for the Worker's
|
|
23
|
+
* `fetch` handler and other entrypoints. Only provides the already-built
|
|
24
|
+
* Context; it does not open a new Scope.
|
|
25
|
+
*/
|
|
26
|
+
readonly provide: <A, E, R2>(effect: Effect.Effect<A, E, R2>) => Effect.Effect<A, E, Exclude<R2, R>>;
|
|
27
|
+
/**
|
|
28
|
+
* A handler that wakes the named singleton, for `Cloudflare.Workers.cron`.
|
|
29
|
+
* The name is the singleton name without the `Singleton/` prefix, and the
|
|
30
|
+
* returned handler is already context-provided. A `ClusterCron` fires only
|
|
31
|
+
* once its seed singleton is woken; get its name from
|
|
32
|
+
* `CloudflareCluster.clusterCronSingletonName`.
|
|
33
|
+
*/
|
|
34
|
+
readonly wake: (name: string) => () => Effect.Effect<void>;
|
|
35
|
+
/**
|
|
36
|
+
* The native namespace binding of the cluster entity class.
|
|
37
|
+
*/
|
|
38
|
+
readonly entityNamespace: DurableObjectNamespace;
|
|
39
|
+
/**
|
|
40
|
+
* The native namespace binding of the workflow class.
|
|
41
|
+
*/
|
|
42
|
+
readonly workflowNamespace: DurableObjectNamespace;
|
|
43
|
+
/**
|
|
44
|
+
* The native namespace binding of the durable queue class.
|
|
45
|
+
*/
|
|
46
|
+
readonly queueNamespace: DurableObjectNamespace;
|
|
47
|
+
/**
|
|
48
|
+
* The native namespace binding of the singleton class.
|
|
49
|
+
*/
|
|
50
|
+
readonly singletonNamespace: DurableObjectNamespace;
|
|
51
|
+
/**
|
|
52
|
+
* The Context built from the cluster layer and the user layer into the
|
|
53
|
+
* isolate-lifetime Scope.
|
|
54
|
+
*/
|
|
55
|
+
readonly context: Context.Context<R>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=cluster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/internal/cluster.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAE5C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK;IACnC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IACpG;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC1D;;OAEG;IACH,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAA;IAChD;;OAEG;IACH,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,CAAA;IAClD;;OAEG;IACH,QAAQ,CAAC,cAAc,EAAE,sBAAsB,CAAA;IAC/C;;OAEG;IACH,QAAQ,CAAC,kBAAkB,EAAE,sBAAsB,CAAA;IACnD;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;CACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cluster.js","names":[],"sources":["../../src/internal/cluster.ts"],"sourcesContent":[null],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clusterName.d.ts","sourceRoot":"","sources":["../../src/internal/clusterName.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
export const encodeName = (type, id) => `${type.length}:${type}${id}`;
|
|
3
|
+
const lengthPrefix = /^([1-9]\d*):/;
|
|
4
|
+
/** @internal */
|
|
5
|
+
export const decodeName = name => {
|
|
6
|
+
const match = lengthPrefix.exec(name);
|
|
7
|
+
if (match === null) return undefined;
|
|
8
|
+
const typeLength = Number(match[1]);
|
|
9
|
+
const payload = name.slice(match[0].length);
|
|
10
|
+
if (typeLength > payload.length) return undefined;
|
|
11
|
+
return {
|
|
12
|
+
type: payload.slice(0, typeLength),
|
|
13
|
+
id: payload.slice(typeLength)
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=clusterName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clusterName.js","names":[],"sources":["../../src/internal/clusterName.ts"],"sourcesContent":[null],"mappings":"AAEA;AACA,OAAO,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAU,KAAa,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,GAAG,EAAE,EAAE;AAE7F,MAAM,YAAY,GAAG,cAAc;AAEnC;AACA,OAAO,MAAM,UAAU,GAAI,IAAY,IAA6B;EAClE,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;EACrC,IAAI,KAAK,KAAK,IAAI,EAAE,OAAO,SAAS;EACpC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;EACnC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;EAC3C,IAAI,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS;EACjD,OAAO;IACL,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;IAClC,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU;GAC7B;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entityKeepAlive.d.ts","sourceRoot":"","sources":["../../src/internal/entityKeepAlive.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep-alive for one entity Durable Object, in three separate pieces of
|
|
3
|
+
* state:
|
|
4
|
+
*
|
|
5
|
+
* - `wanted` mirrors the persisted `keep_alive` flag that a restart restores.
|
|
6
|
+
* - `hold` is the fiber running the self `hold()` RPC while one is in flight.
|
|
7
|
+
* - The holder count belongs to the current handler build only. Persisting it
|
|
8
|
+
* would break on restart: an `EntityResource` re-acquired by the rebuild
|
|
9
|
+
* adds a holder on top of the stored count, which then never returns to 0.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
import * as Duration from "effect/Duration";
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import * as Latch from "effect/Latch";
|
|
16
|
+
import * as Schedule from "effect/Schedule";
|
|
17
|
+
/** @internal */
|
|
18
|
+
export const makeEntityKeepAlive = options => {
|
|
19
|
+
const latch = Latch.makeUnsafe(true);
|
|
20
|
+
let holders = 0;
|
|
21
|
+
let wanted = options.wanted;
|
|
22
|
+
let hold;
|
|
23
|
+
// A safeguard only: production probes never saw a rejected hold.
|
|
24
|
+
const runHold = Effect.tryPromise(() => options.startHold()).pipe(Effect.tapError(error => Effect.logWarning("Entity keep-alive hold failed, retrying", error)), Effect.retry(Schedule.exponential(100).pipe(Schedule.modifyDelay(({
|
|
25
|
+
duration
|
|
26
|
+
}) => Effect.succeed(Duration.min(duration, Duration.millis(options.retryCapMillis())))), Schedule.jittered)));
|
|
27
|
+
const restore = () => {
|
|
28
|
+
if (!wanted || hold !== undefined) return;
|
|
29
|
+
latch.closeUnsafe();
|
|
30
|
+
const fiber = Effect.runFork(runHold);
|
|
31
|
+
hold = fiber;
|
|
32
|
+
fiber.addObserver(() => {
|
|
33
|
+
if (hold === fiber) hold = undefined;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const release = () => {
|
|
37
|
+
if (hold === undefined) return;
|
|
38
|
+
hold.interruptUnsafe();
|
|
39
|
+
hold = undefined;
|
|
40
|
+
latch.openUnsafe();
|
|
41
|
+
};
|
|
42
|
+
const setWanted = next => {
|
|
43
|
+
if (wanted === next) return;
|
|
44
|
+
wanted = next;
|
|
45
|
+
// Mailbox writes wait for an open user transaction, so writing inline
|
|
46
|
+
// would deadlock `Entity.keepAlive` called inside `withTransaction`.
|
|
47
|
+
Effect.runFork(Effect.suspend(() => options.persist(next)).pipe(Effect.andThen(Effect.sync(() => {
|
|
48
|
+
if (!next && !wanted) release();
|
|
49
|
+
})), Effect.catchCause(cause => Effect.logError("Entity keep-alive persistence failed", cause))));
|
|
50
|
+
};
|
|
51
|
+
const update = enabled => Effect.sync(() => {
|
|
52
|
+
if (enabled) {
|
|
53
|
+
holders++;
|
|
54
|
+
setWanted(true);
|
|
55
|
+
// Also restarts a hold that ended without a release.
|
|
56
|
+
restore();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// With no holders in memory the flag may still be set: a fresh object
|
|
60
|
+
// after a restart has count 0 while its stored row says 1.
|
|
61
|
+
if (holders > 0) holders--;
|
|
62
|
+
if (holders === 0) setWanted(false);
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
update,
|
|
66
|
+
await: latch.await,
|
|
67
|
+
resetHolders: () => {
|
|
68
|
+
holders = 0;
|
|
69
|
+
},
|
|
70
|
+
restore
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=entityKeepAlive.js.map
|