@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 @@
|
|
|
1
|
+
{"version":3,"file":"queueStorage.js","names":[],"sources":["../../src/internal/queueStorage.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A module-level registration map shared between the Worker layer and the
|
|
3
|
+
* Durable Object instances of the same isolate. Unregistering only removes
|
|
4
|
+
* the exact registration that was added, so a finalizer racing a re-register
|
|
5
|
+
* cannot drop the replacement.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/internal/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A module-level registration map shared between the Worker layer and the
|
|
3
|
+
* Durable Object instances of the same isolate. Unregistering only removes
|
|
4
|
+
* the exact registration that was added, so a finalizer racing a re-register
|
|
5
|
+
* cannot drop the replacement.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
/** @internal */
|
|
10
|
+
export const makeRegistry = () => {
|
|
11
|
+
const registrations = new Map();
|
|
12
|
+
return {
|
|
13
|
+
get: key => registrations.get(key),
|
|
14
|
+
register: (key, value) => {
|
|
15
|
+
if (registrations.has(key)) return false;
|
|
16
|
+
registrations.set(key, value);
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
unregister: (key, value) => {
|
|
20
|
+
if (registrations.get(key) === value) registrations.delete(key);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/internal/registry.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;AAgBA;AACA,OAAO,MAAM,YAAY,GAAG,MAAqB;EAC/C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAa;EAC1C,OAAO;IACL,GAAG,EAAG,GAAG,IAAK,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;IACpC,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAI;MACvB,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK;MACxC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC;MAC7B,OAAO,IAAI;IACb,CAAC;IACD,UAAU,EAAE,CAAC,GAAG,EAAE,KAAK,KAAI;MACzB,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;IACjE;GACD;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRegistry.d.ts","sourceRoot":"","sources":["../../src/internal/singletonRegistry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { makeRegistry } from "./registry.js";
|
|
2
|
+
const registry = /*#__PURE__*/makeRegistry();
|
|
3
|
+
/** @internal */
|
|
4
|
+
export const getSingletonRegistration = registry.get;
|
|
5
|
+
/** @internal */
|
|
6
|
+
export const registerSingleton = registry.register;
|
|
7
|
+
/** @internal */
|
|
8
|
+
export const unregisterSingleton = registry.unregister;
|
|
9
|
+
//# sourceMappingURL=singletonRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRegistry.js","names":[],"sources":["../../src/internal/singletonRegistry.ts"],"sourcesContent":[null],"mappings":"AAGA,SAAS,YAAY,QAAQ,eAAe;AAQ5C,MAAM,QAAQ,gBAAG,YAAY,EAAyB;AAEtD;AACA,OAAO,MAAM,wBAAwB,GAAwD,QAAQ,CAAC,GAAG;AAEzG;AACA,OAAO,MAAM,iBAAiB,GAAmE,QAAQ,CAAC,QAAQ;AAElH;AACA,OAAO,MAAM,mBAAmB,GAAgE,QAAQ,CAAC,UAAU","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRuntime.d.ts","sourceRoot":"","sources":["../../src/internal/singletonRuntime.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { armAlarm } from "./entityStorage.js";
|
|
3
|
+
import { beginSingletonWake, completeSingletonWake, ensureSingletonStorage, loadSingletonState } from "./singletonStorage.js";
|
|
4
|
+
/**
|
|
5
|
+
* How far ahead the watchdog alarm is armed while a wake is running.
|
|
6
|
+
*/
|
|
7
|
+
const watchdogMillis = 60_000;
|
|
8
|
+
/** @internal */
|
|
9
|
+
export const makeSingletonRuntime = options => {
|
|
10
|
+
ensureSingletonStorage(options.sql);
|
|
11
|
+
let inFlight;
|
|
12
|
+
const armWatchdog = () => Effect.suspend(() => armAlarm(options.alarm, options.now() + watchdogMillis));
|
|
13
|
+
const runPending = arm => {
|
|
14
|
+
const run = options.run.pipe(Effect.ensuring(Effect.promise(() => options.alarm.deleteAlarm()).pipe(Effect.ensuring(Effect.sync(() => completeSingletonWake(options.sql))))));
|
|
15
|
+
const operation = Effect.runPromise(arm ? Effect.andThen(armWatchdog(), run) : run);
|
|
16
|
+
inFlight = operation;
|
|
17
|
+
void operation.then(() => {
|
|
18
|
+
inFlight = undefined;
|
|
19
|
+
}, () => {
|
|
20
|
+
inFlight = undefined;
|
|
21
|
+
});
|
|
22
|
+
return operation;
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
wake: () => {
|
|
26
|
+
if (inFlight !== undefined) return Promise.resolve();
|
|
27
|
+
const pending = loadSingletonState(options.sql).wakeAt;
|
|
28
|
+
if (pending !== undefined) return runPending(false);
|
|
29
|
+
const now = options.now();
|
|
30
|
+
if (!beginSingletonWake(options.sql, now)) return Promise.resolve();
|
|
31
|
+
return runPending(true);
|
|
32
|
+
},
|
|
33
|
+
// Cloudflare consumes the alarm before invoking this handler, so an alarm
|
|
34
|
+
// that fires during a run must arm the next watchdog before returning.
|
|
35
|
+
runAlarm: () => {
|
|
36
|
+
if (loadSingletonState(options.sql).wakeAt === undefined) return Promise.resolve();
|
|
37
|
+
if (inFlight !== undefined) return Effect.runPromise(armWatchdog());
|
|
38
|
+
return runPending(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=singletonRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonRuntime.js","names":[],"sources":["../../src/internal/singletonRuntime.ts"],"sourcesContent":[null],"mappings":"AAYA,OAAO,KAAK,MAAM,MAAM,eAAe;AACvC,SAAS,QAAQ,QAAQ,oBAAoB;AAC7C,SACE,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,QACb,uBAAuB;AAE9B;;;AAGA,MAAM,cAAc,GAAG,MAAM;AAgB7B;AACA,OAAO,MAAM,oBAAoB,GAAI,OAAgC,IAAsB;EACzF,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC;EACnC,IAAI,QAAmC;EAEvC,MAAM,WAAW,GAAG,MAClB,MAAM,CAAC,OAAO,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC;EAE/E,MAAM,UAAU,GAAI,GAAY,IAAmB;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAC1B,MAAM,CAAC,QAAQ,CACb,MAAM,CAAC,OAAO,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CACpD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CACvE,CACF,CACF;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC;IACnF,QAAQ,GAAG,SAAS;IACpB,KAAK,SAAS,CAAC,IAAI,CACjB,MAAK;MACH,QAAQ,GAAG,SAAS;IACtB,CAAC,EACD,MAAK;MACH,QAAQ,GAAG,SAAS;IACtB,CAAC,CACF;IACD,OAAO,SAAS;EAClB,CAAC;EAED,OAAO;IACL,IAAI,EAAE,MAAK;MACT,IAAI,QAAQ,KAAK,SAAS,EAAE,OAAO,OAAO,CAAC,OAAO,EAAE;MACpD,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM;MACtD,IAAI,OAAO,KAAK,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;MACnD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;MACzB,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,OAAO,CAAC,OAAO,EAAE;MACnE,OAAO,UAAU,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;IACA;IACA,QAAQ,EAAE,MAAK;MACb,IAAI,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,OAAO,OAAO,CAAC,OAAO,EAAE;MAClF,IAAI,QAAQ,KAAK,SAAS,EAAE,OAAO,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;MACnE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC1B;GACD;AACH,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonStorage.d.ts","sourceRoot":"","sources":["../../src/internal/singletonStorage.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const ddl = `CREATE TABLE IF NOT EXISTS singleton_state (
|
|
2
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
3
|
+
name TEXT,
|
|
4
|
+
wake_at INTEGER
|
|
5
|
+
)`;
|
|
6
|
+
/** @internal */
|
|
7
|
+
export const ensureSingletonStorage = sql => {
|
|
8
|
+
sql.exec(ddl);
|
|
9
|
+
};
|
|
10
|
+
/** @internal */
|
|
11
|
+
export const loadSingletonState = sql => {
|
|
12
|
+
const row = sql.exec("SELECT name, wake_at FROM singleton_state WHERE id = 1").toArray()[0];
|
|
13
|
+
return {
|
|
14
|
+
name: row?.name ?? undefined,
|
|
15
|
+
wakeAt: row?.wake_at ?? undefined
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/** @internal */
|
|
19
|
+
export const rememberSingletonName = (sql, name) => {
|
|
20
|
+
sql.exec(`INSERT INTO singleton_state (id, name) VALUES (1, ?)
|
|
21
|
+
ON CONFLICT(id) DO UPDATE SET name = excluded.name`, name);
|
|
22
|
+
};
|
|
23
|
+
/** @internal */
|
|
24
|
+
export const beginSingletonWake = (sql, wakeAt) => {
|
|
25
|
+
if (loadSingletonState(sql).wakeAt !== undefined) return false;
|
|
26
|
+
sql.exec(`INSERT INTO singleton_state (id, wake_at) VALUES (1, ?)
|
|
27
|
+
ON CONFLICT(id) DO UPDATE SET wake_at = excluded.wake_at`, wakeAt);
|
|
28
|
+
return true;
|
|
29
|
+
};
|
|
30
|
+
/** @internal */
|
|
31
|
+
export const completeSingletonWake = sql => {
|
|
32
|
+
sql.exec("UPDATE singleton_state SET wake_at = NULL WHERE id = 1");
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=singletonStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singletonStorage.js","names":[],"sources":["../../src/internal/singletonStorage.ts"],"sourcesContent":[null],"mappings":"AASA,MAAM,GAAG,GAAG;;;;EAIV;AAEF;AACA,OAAO,MAAM,sBAAsB,GAAI,GAAe,IAAU;EAC9D,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AACf,CAAC;AAaD;AACA,OAAO,MAAM,kBAAkB,GAAI,GAAe,IAAoB;EACpE,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAoB,wDAAwD,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;EAC9G,OAAO;IACL,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,SAAS;IAC5B,MAAM,EAAE,GAAG,EAAE,OAAO,IAAI;GACzB;AACH,CAAC;AAED;AACA,OAAO,MAAM,qBAAqB,GAAG,CAAC,GAAe,EAAE,IAAY,KAAU;EAC3E,GAAG,CAAC,IAAI,CACN;wDACoD,EACpD,IAAI,CACL;AACH,CAAC;AAED;AACA,OAAO,MAAM,kBAAkB,GAAG,CAAC,GAAe,EAAE,MAAc,KAAa;EAC7E,IAAI,kBAAkB,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,OAAO,KAAK;EAC9D,GAAG,CAAC,IAAI,CACN;8DAC0D,EAC1D,MAAM,CACP;EACD,OAAO,IAAI;AACb,CAAC;AAED;AACA,OAAO,MAAM,qBAAqB,GAAI,GAAe,IAAU;EAC7D,GAAG,CAAC,IAAI,CAAC,wDAAwD,CAAC;AACpE,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/internal/transport.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** @internal */
|
|
2
|
+
import { PersistenceError } from "effect/cluster/ClusterError";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Schedule from "effect/Schedule";
|
|
5
|
+
/**
|
|
6
|
+
* Whether a failed Durable Object call may be retried. Cloudflare marks
|
|
7
|
+
* transient failures `retryable`, and an `overloaded` object must not be
|
|
8
|
+
* retried, as retrying only adds load.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export const isRetryable = error => {
|
|
13
|
+
const cause = error.cause;
|
|
14
|
+
return typeof cause === "object" && cause !== null && cause.retryable === true && cause.overloaded !== true;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The bounded retry policy for idempotent calls outside a workflow: a few
|
|
18
|
+
* quick attempts, then the caller sees the typed error.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export const boundedRetry = /*#__PURE__*/Schedule.max([/*#__PURE__*/Schedule.exponential(100, 2), /*#__PURE__*/Schedule.recurs(4)]);
|
|
23
|
+
/**
|
|
24
|
+
* Makes one Worker-to-Durable Object stub call.
|
|
25
|
+
*
|
|
26
|
+
* **Details**
|
|
27
|
+
*
|
|
28
|
+
* A rejection becomes a `PersistenceError` whose `cause` is the rejected
|
|
29
|
+
* value, so Cloudflare's `retryable`, `overloaded` and `remote` flags stay
|
|
30
|
+
* readable. Each attempt resolves a fresh stub with `resolve`, because
|
|
31
|
+
* Cloudflare advises against reusing a stub after it throws. With `retry`, the
|
|
32
|
+
* call is retried on that schedule while the error is retryable; only pass it
|
|
33
|
+
* for idempotent calls.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export const callDurableObject = options => {
|
|
38
|
+
const attempt = Effect.tryPromise({
|
|
39
|
+
try: () => options.call(options.resolve()),
|
|
40
|
+
catch: cause => new PersistenceError({
|
|
41
|
+
cause
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
return options.retry === undefined ? attempt : Effect.retry(attempt, {
|
|
45
|
+
schedule: options.retry,
|
|
46
|
+
while: isRetryable
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","names":[],"sources":["../../src/internal/transport.ts"],"sourcesContent":[null],"mappings":"AAAA;AACA,SAAS,gBAAgB,QAAQ,6BAA6B;AAC9D,OAAO,KAAK,MAAM,MAAM,eAAe;AACvC,OAAO,KAAK,QAAQ,MAAM,iBAAiB;AAc3C;;;;;;;AAOA,OAAO,MAAM,WAAW,GAAI,KAAuB,IAAa;EAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,KAA+C;EACnE,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;AAC7G,CAAC;AAED;;;;;;AAMA,OAAO,MAAM,YAAY,gBAA+B,QAAQ,CAAC,GAAG,CAAC,cACnE,QAAQ,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,eAC5B,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CACnB,CAAC;AAEF;;;;;;;;;;;;;;AAcA,OAAO,MAAM,iBAAiB,GAAa,OAI1C,IAAwC;EACvC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC;IAChC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1C,KAAK,EAAG,KAAK,IAAK,IAAI,gBAAgB,CAAC;MAAE;IAAK,CAAE;GACjD,CAAC;EACF,OAAO,OAAO,CAAC,KAAK,KAAK,SAAS,GAC9B,OAAO,GACP,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAC,KAAK;IAAE,KAAK,EAAE;EAAW,CAAE,CAAC;AAC5E,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowRegistry.d.ts","sourceRoot":"","sources":["../../src/internal/workflowRegistry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level workflow state shared between the Worker layer and the workflow
|
|
3
|
+
* Durable Object instances of the same isolate. Registrations are recorded at
|
|
4
|
+
* Worker init; a running execution provides its own handle through
|
|
5
|
+
* `CurrentExecutionHandle` so engine operations inside the run hit local
|
|
6
|
+
* SQLite instead of a self-RPC.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
import * as Context from "effect/Context";
|
|
11
|
+
import * as WorkflowEngine from "effect/workflow/WorkflowEngine";
|
|
12
|
+
import { makeRegistry } from "./registry.js";
|
|
13
|
+
const registry = /*#__PURE__*/makeRegistry();
|
|
14
|
+
/** @internal */
|
|
15
|
+
export const getWorkflowRegistration = registry.get;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const registerWorkflow = registry.register;
|
|
18
|
+
/** @internal */
|
|
19
|
+
export const unregisterWorkflow = registry.unregister;
|
|
20
|
+
/**
|
|
21
|
+
* The handle of the workflow execution currently running in this fiber,
|
|
22
|
+
* provided by the workflow Durable Object runtime around each run attempt.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export class CurrentExecutionHandle extends /*#__PURE__*/Context.Service()("@effect/platform-cloudflare/CloudflareWorkflowEngine/CurrentExecutionHandle") {}
|
|
27
|
+
/** @internal */
|
|
28
|
+
export const deferredState = /*#__PURE__*/WorkflowEngine.makeDeferredState();
|
|
29
|
+
//# sourceMappingURL=workflowRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowRegistry.js","names":[],"sources":["../../src/internal/workflowRegistry.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;AASA,OAAO,KAAK,OAAO,MAAM,gBAAgB;AAGzC,OAAO,KAAK,cAAc,MAAM,gCAAgC;AAEhE,SAAS,YAAY,QAAQ,eAAe;AAY5C,MAAM,QAAQ,gBAAG,YAAY,EAAwB;AAErD;AACA,OAAO,MAAM,uBAAuB,GAAuD,QAAQ,CAAC,GAAG;AAEvG;AACA,OAAO,MAAM,gBAAgB,GAAkE,QAAQ,CAAC,QAAQ;AAEhH;AACA,OAAO,MAAM,kBAAkB,GAA+D,QAAQ,CAAC,UAAU;AA+BjH;;;;;;AAMA,OAAM,MAAO,sBAAuB,sBAAQ,OAAO,CAAC,OAAO,EAAmD,CAC5G,6EAA6E,CAC9E;AAED;AACA,OAAO,MAAM,aAAa,gBAAG,cAAc,CAAC,iBAAiB,EAAE","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowRuntime.d.ts","sourceRoot":"","sources":["../../src/internal/workflowRuntime.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Context from "effect/Context";
|
|
3
|
+
import * as Duration from "effect/Duration";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Exit from "effect/Exit";
|
|
6
|
+
import * as Fiber from "effect/Fiber";
|
|
7
|
+
import * as Schedule from "effect/Schedule";
|
|
8
|
+
import * as Scope from "effect/Scope";
|
|
9
|
+
import * as DurableClock from "effect/workflow/DurableClock";
|
|
10
|
+
import * as Workflow from "effect/workflow/Workflow";
|
|
11
|
+
import * as WorkflowEngine from "effect/workflow/WorkflowEngine";
|
|
12
|
+
import { decodeName, encodeName } from "./clusterName.js";
|
|
13
|
+
import { armAlarm } from "./entityStorage.js";
|
|
14
|
+
import { CurrentExecutionHandle, deferredState, getWorkflowRegistration } from "./workflowRegistry.js";
|
|
15
|
+
import * as WorkflowStorage from "./workflowStorage.js";
|
|
16
|
+
import { decodePayload, encodeDefectResult, encodeExit, encodeResult } from "./workflowWire.js";
|
|
17
|
+
/** @internal */
|
|
18
|
+
export const InterruptSignalName = "Workflow/InterruptSignal";
|
|
19
|
+
const resumeGuardMillis = 60_000;
|
|
20
|
+
let voidExitCache;
|
|
21
|
+
const voidExitText = () => voidExitCache ??= Effect.runPromise(encodeExit(Exit.void, Context.empty()));
|
|
22
|
+
const mergeWake = (current, mode) => current === "explicit" ? current : mode;
|
|
23
|
+
/** @internal */
|
|
24
|
+
export const makeWorkflowRuntime = options => {
|
|
25
|
+
const name = decodeName(options.name);
|
|
26
|
+
if (name === undefined) {
|
|
27
|
+
throw new Error("ClusterWorkflow requires a canonical workflow Durable Object name");
|
|
28
|
+
}
|
|
29
|
+
const workflowName = name.type;
|
|
30
|
+
const executionId = name.id;
|
|
31
|
+
const sql = options.sql;
|
|
32
|
+
WorkflowStorage.ensureWorkflowStorage(sql);
|
|
33
|
+
let inflight;
|
|
34
|
+
let resumeRequested;
|
|
35
|
+
// Parent of every attempt's scope. As in the memory engine, scopes of
|
|
36
|
+
// attempts that suspended stay open under it, so their finalizers and
|
|
37
|
+
// resources last until the execution completes or an attempt defects.
|
|
38
|
+
let executionScope = Scope.makeUnsafe();
|
|
39
|
+
// The final attempt's scope has already closed with the real exit. Closing
|
|
40
|
+
// the parent closes earlier attempts' scopes in reverse order with
|
|
41
|
+
// `Exit.void`, so their superseded compensations are skipped. A fresh
|
|
42
|
+
// parent is swapped in first: a later replay must not fork from a closed
|
|
43
|
+
// scope, which would run its finalizers as soon as they are registered.
|
|
44
|
+
const closeExecutionScope = Effect.suspend(() => {
|
|
45
|
+
const closing = executionScope;
|
|
46
|
+
executionScope = Scope.makeUnsafe();
|
|
47
|
+
return Scope.close(closing, Exit.void);
|
|
48
|
+
});
|
|
49
|
+
const detach = promise => {
|
|
50
|
+
options.waitUntil(promise.then(() => undefined, () => undefined));
|
|
51
|
+
};
|
|
52
|
+
const parseResult = result => JSON.parse(result);
|
|
53
|
+
const isComplete = result => result !== undefined && parseResult(result)._tag === "Complete";
|
|
54
|
+
// As in the memory engine, only an explicit wake replays a failure suspension.
|
|
55
|
+
const isUnwakeable = (result, mode) => {
|
|
56
|
+
if (result === undefined) return false;
|
|
57
|
+
const parsed = parseResult(result);
|
|
58
|
+
return parsed._tag === "Complete" || mode === "automatic" && parsed.failed === true;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Records a wake durably. A running attempt queues it and decides when it
|
|
62
|
+
* settles, since its result may still become a failure suspension. Returns
|
|
63
|
+
* whether a replay is now pending.
|
|
64
|
+
*/
|
|
65
|
+
const requestWake = mode => {
|
|
66
|
+
const row = WorkflowStorage.loadExecution(sql);
|
|
67
|
+
if (row === undefined || isComplete(row.result)) return false;
|
|
68
|
+
if (inflight === undefined && isUnwakeable(row.result, mode)) return false;
|
|
69
|
+
const pending = mergeWake(row.resumePending, mode);
|
|
70
|
+
if (pending !== row.resumePending) WorkflowStorage.setResumePending(sql, pending);
|
|
71
|
+
if (inflight !== undefined) resumeRequested = mergeWake(resumeRequested, mode);
|
|
72
|
+
return true;
|
|
73
|
+
};
|
|
74
|
+
const armResumeGuard = Effect.suspend(() => armAlarm(options.alarm, options.now() + resumeGuardMillis));
|
|
75
|
+
const resumeParent = parent => Effect.promise(() => options.getStub(encodeName(parent.workflowName, parent.executionId)).resume("automatic")).pipe(Effect.sandbox, Effect.retry({
|
|
76
|
+
times: 5,
|
|
77
|
+
schedule: Schedule.exponential(200)
|
|
78
|
+
}));
|
|
79
|
+
const logResumeParentFailure = cause => Effect.logError("Workflow parent resume failed", cause);
|
|
80
|
+
const deliverParentWake = Effect.suspend(() => {
|
|
81
|
+
const row = WorkflowStorage.loadExecution(sql);
|
|
82
|
+
if (row === undefined || row.resumePending === undefined || !isComplete(row.result) || row.parent === undefined) {
|
|
83
|
+
return Effect.void;
|
|
84
|
+
}
|
|
85
|
+
// Re-arm before sending so a crash or exhausted retries leave another attempt.
|
|
86
|
+
return armResumeGuard.pipe(Effect.andThen(resumeParent(row.parent).pipe(Effect.tap(() => Effect.sync(() => WorkflowStorage.setResumePending(sql, undefined))), Effect.catchCause(logResumeParentFailure))));
|
|
87
|
+
});
|
|
88
|
+
// An attempt the machinery cannot finish (a defect with `CaptureDefects`
|
|
89
|
+
// off, a payload that no longer decodes, a result that fails to encode, a
|
|
90
|
+
// dying finalizer, a missing registration) completes the execution with the
|
|
91
|
+
// defect, as `ClusterWorkflowEngine` stores an exhausted defect retry. The
|
|
92
|
+
// object never rejects `run` for such a reason, so a caller can tell the
|
|
93
|
+
// child's own defect from a transport failure, and a parent replaying a
|
|
94
|
+
// discarded child finds the result instead of restarting it.
|
|
95
|
+
const failAttempt = cause => Effect.runPromise(Effect.gen(function* () {
|
|
96
|
+
yield* Effect.logError("Workflow execution failed", cause);
|
|
97
|
+
const text = yield* encodeDefectResult(Cause.squash(cause));
|
|
98
|
+
const parent = WorkflowStorage.loadExecution(sql)?.parent;
|
|
99
|
+
if (parent !== undefined) yield* armResumeGuard;
|
|
100
|
+
WorkflowStorage.saveCompletion(sql, text, parent !== undefined);
|
|
101
|
+
inflight = undefined;
|
|
102
|
+
resumeRequested = undefined;
|
|
103
|
+
if (parent !== undefined) options.waitUntil(Effect.runPromise(deliverParentWake));
|
|
104
|
+
return text;
|
|
105
|
+
}));
|
|
106
|
+
const startAttempt = row => {
|
|
107
|
+
const registration = getWorkflowRegistration(workflowName);
|
|
108
|
+
if (registration === undefined) {
|
|
109
|
+
return failAttempt(Cause.die(new Error(`No workflow registered for name: ${workflowName}`)));
|
|
110
|
+
}
|
|
111
|
+
const workflow = registration.workflow;
|
|
112
|
+
const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId, {
|
|
113
|
+
scope: Scope.forkUnsafe(executionScope)
|
|
114
|
+
});
|
|
115
|
+
const execute = decodePayload(workflow, row.payload, registration.context).pipe(Effect.flatMap(payload => registration.execute(payload, executionId)), Effect.onExit(exit => {
|
|
116
|
+
const suspendOnFailure = Context.get(workflow.annotations, Workflow.SuspendOnFailure);
|
|
117
|
+
if (!instance.suspended && !(suspendOnFailure && exit._tag === "Failure")) {
|
|
118
|
+
return Effect.void;
|
|
119
|
+
}
|
|
120
|
+
if (WorkflowStorage.loadDeferred(sql, InterruptSignalName) === undefined) {
|
|
121
|
+
return Effect.void;
|
|
122
|
+
}
|
|
123
|
+
instance.suspended = false;
|
|
124
|
+
instance.interrupted = true;
|
|
125
|
+
return Effect.withFiber(fiber => Effect.interruptible(Fiber.interrupt(fiber)));
|
|
126
|
+
}), Workflow.intoResult, effect => deferredState.trackRun(instance, effect), Effect.flatMap(result => Effect.map(encodeResult(workflow, result, registration.context), text => ({
|
|
127
|
+
result,
|
|
128
|
+
text
|
|
129
|
+
}))), Effect.provideService(DurableClock.InMemoryThreshold, Duration.zero), Effect.provideService(CurrentExecutionHandle, runtime));
|
|
130
|
+
const persist = ({
|
|
131
|
+
result,
|
|
132
|
+
text
|
|
133
|
+
}) => Effect.runPromise(Effect.gen(function* () {
|
|
134
|
+
const parent = WorkflowStorage.loadExecution(sql)?.parent;
|
|
135
|
+
if (result._tag === "Complete" && parent !== undefined) {
|
|
136
|
+
// Arm before the atomic result/pending save so a persisted
|
|
137
|
+
// completion always has a delivery wake.
|
|
138
|
+
yield* armResumeGuard;
|
|
139
|
+
}
|
|
140
|
+
// Stay in flight until the result is saved so a concurrent resume
|
|
141
|
+
// queues behind this attempt instead of replaying alongside it.
|
|
142
|
+
if (result._tag === "Complete") {
|
|
143
|
+
WorkflowStorage.saveCompletion(sql, text, parent !== undefined);
|
|
144
|
+
inflight = undefined;
|
|
145
|
+
resumeRequested = undefined;
|
|
146
|
+
if (parent !== undefined) options.waitUntil(Effect.runPromise(deliverParentWake));
|
|
147
|
+
return text;
|
|
148
|
+
}
|
|
149
|
+
const replay = resumeRequested !== undefined && !isUnwakeable(text, resumeRequested) ? resumeRequested : undefined;
|
|
150
|
+
WorkflowStorage.saveSuspension(sql, text, replay);
|
|
151
|
+
inflight = undefined;
|
|
152
|
+
resumeRequested = undefined;
|
|
153
|
+
if (replay !== undefined) detach(startAttempt(row));
|
|
154
|
+
return text;
|
|
155
|
+
}).pipe(Effect.tapCause(() => Effect.sync(() => {
|
|
156
|
+
inflight = undefined;
|
|
157
|
+
}))));
|
|
158
|
+
const finish = attempt => {
|
|
159
|
+
if (attempt.result._tag !== "Complete") return persist(attempt);
|
|
160
|
+
// Close retained scopes before saving, so a durable completion never
|
|
161
|
+
// has unrun finalizers. A dying finalizer fails the attempt instead.
|
|
162
|
+
return Effect.runPromise(Effect.exit(closeExecutionScope)).then(closed => Exit.isSuccess(closed) ? persist(attempt) : failAttempt(closed.cause));
|
|
163
|
+
};
|
|
164
|
+
const fiber = Effect.runFork(execute);
|
|
165
|
+
const promise = Effect.runPromise(Fiber.await(fiber)).then(exit => {
|
|
166
|
+
if (Exit.isSuccess(exit)) return finish(exit.value);
|
|
167
|
+
if (Cause.hasInterruptsOnly(exit.cause) && (instance.interrupted || instance.suspended)) {
|
|
168
|
+
// A hard interrupt (or a completion preempting the fiber after it
|
|
169
|
+
// already produced its result) kills the fiber before it can encode;
|
|
170
|
+
// synthesize the result from the instance state so it still persists.
|
|
171
|
+
const result = instance.interrupted ? new Workflow.Complete({
|
|
172
|
+
exit: exit
|
|
173
|
+
}) : new Workflow.Suspended({});
|
|
174
|
+
return Effect.runPromise(encodeResult(workflow, result, registration.context)).then(text => finish({
|
|
175
|
+
result,
|
|
176
|
+
text
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
// A defect ends every retained attempt; a replay starts in fresh scopes.
|
|
180
|
+
// Stay in flight while closing so cleanup precedes any replay.
|
|
181
|
+
return Effect.runPromise(Effect.exit(closeExecutionScope)).then(closed => failAttempt(Exit.isSuccess(closed) ? exit.cause : Cause.combine(exit.cause, closed.cause)));
|
|
182
|
+
});
|
|
183
|
+
inflight = {
|
|
184
|
+
instance,
|
|
185
|
+
fiber,
|
|
186
|
+
promise
|
|
187
|
+
};
|
|
188
|
+
return promise;
|
|
189
|
+
};
|
|
190
|
+
const run = (payload, opts) => {
|
|
191
|
+
let row = WorkflowStorage.loadExecution(sql);
|
|
192
|
+
if (row === undefined) {
|
|
193
|
+
WorkflowStorage.createExecution(sql, workflowName, executionId, payload, opts.parent);
|
|
194
|
+
row = {
|
|
195
|
+
workflowName,
|
|
196
|
+
executionId,
|
|
197
|
+
payload,
|
|
198
|
+
parent: opts.parent,
|
|
199
|
+
result: undefined,
|
|
200
|
+
resumePending: undefined
|
|
201
|
+
};
|
|
202
|
+
} else if (opts.parent !== undefined && row.parent === undefined) {
|
|
203
|
+
// An execution started standalone can gain a parent later; keep the
|
|
204
|
+
// first parent so its completion still wakes that parent.
|
|
205
|
+
WorkflowStorage.setParent(sql, opts.parent);
|
|
206
|
+
row = {
|
|
207
|
+
...row,
|
|
208
|
+
parent: opts.parent
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (inflight !== undefined) return opts.discard ? Promise.resolve("") : inflight.promise;
|
|
212
|
+
if (row.result !== undefined) return Promise.resolve(opts.discard ? "" : row.result);
|
|
213
|
+
// The attempt stays pending under the guard alarm until it persists a
|
|
214
|
+
// result, so an attempt lost with this object is replayed even when no
|
|
215
|
+
// caller awaits it.
|
|
216
|
+
WorkflowStorage.setResumePending(sql, mergeWake(row.resumePending, "automatic"));
|
|
217
|
+
const attempt = startAttempt(row);
|
|
218
|
+
const armed = Effect.runPromise(armResumeGuard);
|
|
219
|
+
if (opts.discard) {
|
|
220
|
+
detach(attempt);
|
|
221
|
+
return armed.then(() => "");
|
|
222
|
+
}
|
|
223
|
+
return Promise.all([attempt, armed]).then(([text]) => text);
|
|
224
|
+
};
|
|
225
|
+
const startPending = () => {
|
|
226
|
+
const row = WorkflowStorage.loadExecution(sql);
|
|
227
|
+
if (row === undefined || row.resumePending === undefined || inflight !== undefined) return;
|
|
228
|
+
if (isUnwakeable(row.result, row.resumePending)) return;
|
|
229
|
+
detach(startAttempt(row));
|
|
230
|
+
};
|
|
231
|
+
const resume = (mode = "explicit") => {
|
|
232
|
+
if (!requestWake(mode)) return Promise.resolve();
|
|
233
|
+
// Persist and guard the resume before acknowledging it, so an abort after
|
|
234
|
+
// the RPC returns is still recovered.
|
|
235
|
+
return Effect.runPromise(armResumeGuard).then(startPending);
|
|
236
|
+
};
|
|
237
|
+
const completeDeferred = (deferredName, exitText, mode) => {
|
|
238
|
+
if (!WorkflowStorage.saveDeferred(sql, deferredName, exitText)) return Promise.resolve();
|
|
239
|
+
// Persisted with the exit in the same write batch: if this wake is lost
|
|
240
|
+
// before the replay finishes, the next wake (or the guard alarm) replays
|
|
241
|
+
// the execution. The in-flight request is set synchronously so a settling
|
|
242
|
+
// attempt cannot clear the pending resume without a restart.
|
|
243
|
+
if (!requestWake(mode)) return Promise.resolve();
|
|
244
|
+
return Effect.runPromise(armResumeGuard.pipe(Effect.andThen(deferredState.deferredDone(executionId, deferredName)))).then(() => resume(mode));
|
|
245
|
+
};
|
|
246
|
+
const deferredDone = (deferredName, exitText) => completeDeferred(deferredName, exitText, "automatic");
|
|
247
|
+
// The caller belongs to the run this wake interrupts, so awaiting that run
|
|
248
|
+
// would wait on the caller's own cleanup. As in the memory engine, the wake
|
|
249
|
+
// starts on a separate fiber after the caller continues.
|
|
250
|
+
const completeOwnDeferred = (deferredName, exitText) => Effect.suspend(() => {
|
|
251
|
+
if (!WorkflowStorage.saveDeferred(sql, deferredName, exitText) || !requestWake("automatic")) {
|
|
252
|
+
return Effect.void;
|
|
253
|
+
}
|
|
254
|
+
const wake = deferredState.deferredDone(executionId, deferredName).pipe(Effect.andThen(Effect.suspend(() => inflight === undefined ? Effect.promise(() => resume("automatic")) : Effect.void)));
|
|
255
|
+
return armResumeGuard.pipe(Effect.andThen(Effect.forkDetach(wake)), Effect.flatMap(fiber => Effect.sync(() => detach(Effect.runPromise(Fiber.await(fiber))))));
|
|
256
|
+
});
|
|
257
|
+
const interrupt = () => {
|
|
258
|
+
const row = WorkflowStorage.loadExecution(sql);
|
|
259
|
+
if (row === undefined || isComplete(row.result)) return Promise.resolve();
|
|
260
|
+
return voidExitText().then(exitText => completeDeferred(InterruptSignalName, exitText, "explicit"));
|
|
261
|
+
};
|
|
262
|
+
const interruptUnsafe = () => {
|
|
263
|
+
const current = inflight;
|
|
264
|
+
const signalled = interrupt();
|
|
265
|
+
if (current === undefined) return signalled;
|
|
266
|
+
current.instance.interrupted = true;
|
|
267
|
+
return signalled.then(() => Effect.runPromise(Fiber.interrupt(current.fiber))).then(() => undefined);
|
|
268
|
+
};
|
|
269
|
+
const scheduleClock = (clockName, deferredName, wakeUp) => {
|
|
270
|
+
WorkflowStorage.saveClock(sql, clockName, deferredName, wakeUp);
|
|
271
|
+
const earliest = WorkflowStorage.earliestClockWakeUp(sql);
|
|
272
|
+
return earliest === undefined ? Promise.resolve() : Effect.runPromise(armAlarm(options.alarm, earliest));
|
|
273
|
+
};
|
|
274
|
+
const runAlarm = () => voidExitText().then(voidExit => {
|
|
275
|
+
const completed = WorkflowStorage.dueClocks(sql, options.now()).filter(clock => {
|
|
276
|
+
WorkflowStorage.markClockFired(sql, clock.name);
|
|
277
|
+
return WorkflowStorage.saveDeferred(sql, clock.deferredName, voidExit);
|
|
278
|
+
});
|
|
279
|
+
if (completed.length > 0) requestWake("automatic");
|
|
280
|
+
return Effect.runPromise(Effect.forEach(completed, clock => deferredState.deferredDone(executionId, clock.deferredName), {
|
|
281
|
+
discard: true
|
|
282
|
+
})).then(() => {
|
|
283
|
+
const row = WorkflowStorage.loadExecution(sql);
|
|
284
|
+
const mode = row?.resumePending;
|
|
285
|
+
const pending = mode !== undefined && !isUnwakeable(row?.result, mode);
|
|
286
|
+
// A live attempt is not lost: only re-arm the guard. Requesting a wake
|
|
287
|
+
// would replay the attempt once it suspends, for no reason.
|
|
288
|
+
return (pending && inflight === undefined ? resume(mode) : Promise.resolve()).then(() => {
|
|
289
|
+
// While a resume is pending a guard alarm stays armed, so a replay
|
|
290
|
+
// lost with this isolate is retried instead of sleeping forever.
|
|
291
|
+
const targets = [WorkflowStorage.earliestClockWakeUp(sql), pending ? options.now() + resumeGuardMillis : undefined].filter(target => target !== undefined);
|
|
292
|
+
if (targets.length === 0) return undefined;
|
|
293
|
+
// The alarm that fired is spent, so set the next one outright:
|
|
294
|
+
// `armAlarm` only ever moves an alarm earlier.
|
|
295
|
+
return options.alarm.setAlarm(Math.min(...targets));
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}).then(() => Effect.runPromise(deliverParentWake)).then(() => undefined);
|
|
299
|
+
const runtime = {
|
|
300
|
+
executionId,
|
|
301
|
+
run,
|
|
302
|
+
poll: () => Promise.resolve(WorkflowStorage.loadExecution(sql)?.result),
|
|
303
|
+
resume,
|
|
304
|
+
interrupt,
|
|
305
|
+
interruptUnsafe,
|
|
306
|
+
deferredDone,
|
|
307
|
+
scheduleClock,
|
|
308
|
+
runAlarm,
|
|
309
|
+
loadActivity: key => WorkflowStorage.loadActivity(sql, key),
|
|
310
|
+
saveActivity: (key, exit) => WorkflowStorage.saveActivity(sql, key, exit),
|
|
311
|
+
loadDeferred: deferredName => WorkflowStorage.loadDeferred(sql, deferredName),
|
|
312
|
+
completeOwnDeferred
|
|
313
|
+
};
|
|
314
|
+
// Self-heal on wake: a resume recorded by deferredDone but lost with the
|
|
315
|
+
// previous isolate replays now instead of waiting for external contact.
|
|
316
|
+
const pending = WorkflowStorage.loadExecution(sql)?.resumePending;
|
|
317
|
+
if (pending !== undefined) {
|
|
318
|
+
void resume(pending);
|
|
319
|
+
}
|
|
320
|
+
return runtime;
|
|
321
|
+
};
|
|
322
|
+
//# sourceMappingURL=workflowRuntime.js.map
|