@hotmeshio/hotmesh 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -22
- package/build/modules/enums.d.ts +60 -5
- package/build/modules/enums.js +62 -7
- package/build/modules/errors.d.ts +15 -2
- package/build/modules/errors.js +17 -1
- package/build/modules/storage.d.ts +1 -0
- package/build/modules/storage.js +2 -1
- package/build/package.json +8 -2
- package/build/services/activities/activity/context.d.ts +22 -0
- package/build/services/activities/activity/context.js +76 -0
- package/build/services/activities/activity/index.d.ts +116 -0
- package/build/services/activities/activity/index.js +299 -0
- package/build/services/activities/activity/mapping.d.ts +12 -0
- package/build/services/activities/activity/mapping.js +63 -0
- package/build/services/activities/activity/process.d.ts +28 -0
- package/build/services/activities/activity/process.js +100 -0
- package/build/services/activities/activity/protocol.d.ts +39 -0
- package/build/services/activities/activity/protocol.js +151 -0
- package/build/services/activities/activity/state.d.ts +40 -0
- package/build/services/activities/activity/state.js +143 -0
- package/build/services/activities/activity/transition.d.ts +23 -0
- package/build/services/activities/activity/transition.js +71 -0
- package/build/services/activities/activity/verify.d.ts +22 -0
- package/build/services/activities/activity/verify.js +85 -0
- package/build/services/activities/await.d.ts +1 -4
- package/build/services/activities/await.js +2 -36
- package/build/services/activities/cycle.d.ts +1 -11
- package/build/services/activities/cycle.js +3 -46
- package/build/services/activities/hook.d.ts +2 -11
- package/build/services/activities/hook.js +30 -50
- package/build/services/activities/interrupt.d.ts +2 -4
- package/build/services/activities/interrupt.js +4 -38
- package/build/services/activities/signal.d.ts +1 -11
- package/build/services/activities/signal.js +3 -48
- package/build/services/activities/trigger.d.ts +1 -3
- package/build/services/activities/trigger.js +0 -3
- package/build/services/activities/worker.d.ts +3 -6
- package/build/services/activities/worker.js +4 -40
- package/build/services/connector/factory.d.ts +6 -0
- package/build/services/connector/factory.js +24 -0
- package/build/services/dba/index.d.ts +14 -4
- package/build/services/dba/index.js +57 -18
- package/build/services/durable/activity.d.ts +30 -0
- package/build/services/durable/activity.js +46 -0
- package/build/services/durable/client.d.ts +26 -31
- package/build/services/durable/client.js +26 -31
- package/build/services/durable/connection.d.ts +13 -7
- package/build/services/durable/connection.js +13 -7
- package/build/services/durable/exporter.d.ts +2 -2
- package/build/services/durable/exporter.js +27 -12
- package/build/services/durable/handle.d.ts +59 -41
- package/build/services/durable/handle.js +61 -41
- package/build/services/durable/index.d.ts +152 -283
- package/build/services/durable/index.js +161 -289
- package/build/services/durable/interceptor.d.ts +43 -33
- package/build/services/durable/interceptor.js +59 -39
- package/build/services/durable/schemas/factory.d.ts +2 -3
- package/build/services/durable/schemas/factory.js +180 -30
- package/build/services/durable/telemetry.d.ts +80 -0
- package/build/services/durable/telemetry.js +137 -0
- package/build/services/durable/worker.d.ts +100 -21
- package/build/services/durable/worker.js +314 -60
- package/build/services/durable/workflow/all.d.ts +1 -1
- package/build/services/durable/workflow/all.js +1 -1
- package/build/services/durable/workflow/cancellationScope.d.ts +104 -0
- package/build/services/durable/workflow/cancellationScope.js +139 -0
- package/build/services/durable/workflow/common.d.ts +5 -4
- package/build/services/durable/workflow/common.js +6 -1
- package/build/services/durable/workflow/{waitFor.d.ts → condition.d.ts} +9 -8
- package/build/services/durable/workflow/{waitFor.js → condition.js} +44 -11
- package/build/services/durable/workflow/continueAsNew.d.ts +65 -0
- package/build/services/durable/workflow/continueAsNew.js +92 -0
- package/build/services/durable/workflow/didRun.d.ts +2 -2
- package/build/services/durable/workflow/didRun.js +4 -4
- package/build/services/durable/workflow/enrich.d.ts +5 -0
- package/build/services/durable/workflow/enrich.js +5 -0
- package/build/services/durable/workflow/entityMethods.d.ts +7 -0
- package/build/services/durable/workflow/entityMethods.js +7 -0
- package/build/services/durable/workflow/execHook.js +3 -3
- package/build/services/durable/workflow/execHookBatch.js +2 -2
- package/build/services/durable/workflow/{execChild.d.ts → executeChild.d.ts} +4 -40
- package/build/services/durable/workflow/{execChild.js → executeChild.js} +36 -45
- package/build/services/durable/workflow/hook.d.ts +1 -1
- package/build/services/durable/workflow/hook.js +4 -3
- package/build/services/durable/workflow/index.d.ts +45 -50
- package/build/services/durable/workflow/index.js +46 -51
- package/build/services/durable/workflow/interruption.d.ts +7 -6
- package/build/services/durable/workflow/interruption.js +11 -7
- package/build/services/durable/workflow/patched.d.ts +72 -0
- package/build/services/durable/workflow/patched.js +110 -0
- package/build/services/durable/workflow/proxyActivities.d.ts +7 -7
- package/build/services/durable/workflow/proxyActivities.js +51 -15
- package/build/services/durable/workflow/searchMethods.d.ts +7 -0
- package/build/services/durable/workflow/searchMethods.js +7 -0
- package/build/services/durable/workflow/signal.d.ts +4 -4
- package/build/services/durable/workflow/signal.js +4 -4
- package/build/services/durable/workflow/{sleepFor.d.ts → sleep.d.ts} +7 -7
- package/build/services/durable/workflow/{sleepFor.js → sleep.js} +39 -10
- package/build/services/durable/workflow/terminate.d.ts +55 -0
- package/build/services/durable/workflow/{interrupt.js → terminate.js} +21 -21
- package/build/services/durable/workflow/trace.js +2 -2
- package/build/services/durable/workflow/uuid4.d.ts +14 -0
- package/build/services/durable/workflow/uuid4.js +39 -0
- package/build/services/durable/workflow/{context.d.ts → workflowInfo.d.ts} +5 -5
- package/build/services/durable/workflow/{context.js → workflowInfo.js} +7 -7
- package/build/services/engine/compiler.d.ts +19 -0
- package/build/services/engine/compiler.js +20 -0
- package/build/services/engine/completion.d.ts +46 -0
- package/build/services/engine/completion.js +145 -0
- package/build/services/engine/dispatch.d.ts +24 -0
- package/build/services/engine/dispatch.js +98 -0
- package/build/services/engine/index.d.ts +49 -81
- package/build/services/engine/index.js +175 -573
- package/build/services/engine/init.d.ts +42 -0
- package/build/services/engine/init.js +74 -0
- package/build/services/engine/pubsub.d.ts +50 -0
- package/build/services/engine/pubsub.js +118 -0
- package/build/services/engine/reporting.d.ts +20 -0
- package/build/services/engine/reporting.js +38 -0
- package/build/services/engine/schema.d.ts +23 -0
- package/build/services/engine/schema.js +62 -0
- package/build/services/engine/signal.d.ts +57 -0
- package/build/services/engine/signal.js +117 -0
- package/build/services/engine/state.d.ts +35 -0
- package/build/services/engine/state.js +61 -0
- package/build/services/engine/version.d.ts +31 -0
- package/build/services/engine/version.js +73 -0
- package/build/services/hotmesh/deployment.d.ts +21 -0
- package/build/services/hotmesh/deployment.js +25 -0
- package/build/services/hotmesh/index.d.ts +142 -533
- package/build/services/hotmesh/index.js +223 -674
- package/build/services/hotmesh/init.d.ts +42 -0
- package/build/services/hotmesh/init.js +93 -0
- package/build/services/hotmesh/jobs.d.ts +67 -0
- package/build/services/hotmesh/jobs.js +99 -0
- package/build/services/hotmesh/pubsub.d.ts +38 -0
- package/build/services/hotmesh/pubsub.js +54 -0
- package/build/services/hotmesh/quorum.d.ts +30 -0
- package/build/services/hotmesh/quorum.js +62 -0
- package/build/services/hotmesh/validation.d.ts +6 -0
- package/build/services/hotmesh/validation.js +28 -0
- package/build/services/quorum/index.js +1 -0
- package/build/services/router/consumption/index.d.ts +11 -5
- package/build/services/router/consumption/index.js +24 -17
- package/build/services/router/error-handling/index.d.ts +2 -2
- package/build/services/router/error-handling/index.js +14 -14
- package/build/services/router/index.d.ts +1 -1
- package/build/services/router/index.js +2 -2
- package/build/services/serializer/index.d.ts +22 -0
- package/build/services/serializer/index.js +39 -1
- package/build/services/store/index.d.ts +1 -0
- package/build/services/store/providers/postgres/exporter-sql.d.ts +2 -2
- package/build/services/store/providers/postgres/exporter-sql.js +4 -4
- package/build/services/store/providers/postgres/kvtables.js +7 -6
- package/build/services/store/providers/postgres/kvtypes/hash/basic.js +67 -52
- package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +87 -72
- package/build/services/store/providers/postgres/kvtypes/hash/udata.js +106 -79
- package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +16 -0
- package/build/services/store/providers/postgres/kvtypes/hash/utils.js +29 -16
- package/build/services/store/providers/postgres/postgres.d.ts +1 -0
- package/build/services/store/providers/postgres/postgres.js +14 -4
- package/build/services/stream/factory.d.ts +3 -1
- package/build/services/stream/factory.js +2 -2
- package/build/services/stream/index.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.js +1 -0
- package/build/services/stream/providers/postgres/credentials.d.ts +56 -0
- package/build/services/stream/providers/postgres/credentials.js +129 -0
- package/build/services/stream/providers/postgres/kvtables.js +18 -0
- package/build/services/stream/providers/postgres/messages.js +7 -7
- package/build/services/stream/providers/postgres/notifications.js +16 -2
- package/build/services/stream/providers/postgres/postgres.d.ts +7 -0
- package/build/services/stream/providers/postgres/postgres.js +35 -4
- package/build/services/stream/providers/postgres/procedures.d.ts +21 -0
- package/build/services/stream/providers/postgres/procedures.js +213 -0
- package/build/services/stream/providers/postgres/secured.d.ts +34 -0
- package/build/services/stream/providers/postgres/secured.js +146 -0
- package/build/services/stream/providers/postgres/stats.d.ts +1 -0
- package/build/services/stream/providers/postgres/stats.js +1 -0
- package/build/services/stream/registry.d.ts +1 -1
- package/build/services/stream/registry.js +5 -2
- package/build/services/telemetry/index.d.ts +10 -1
- package/build/services/telemetry/index.js +40 -7
- package/build/services/worker/credentials.d.ts +51 -0
- package/build/services/worker/credentials.js +87 -0
- package/build/services/worker/index.d.ts +2 -2
- package/build/services/worker/index.js +7 -6
- package/build/types/codec.d.ts +84 -0
- package/build/types/codec.js +2 -0
- package/build/types/dba.d.ts +39 -3
- package/build/types/durable.d.ts +123 -25
- package/build/types/error.d.ts +10 -0
- package/build/types/exporter.d.ts +1 -1
- package/build/types/hotmesh.d.ts +67 -4
- package/build/types/index.d.ts +2 -1
- package/build/types/provider.d.ts +2 -2
- package/build/types/quorum.d.ts +35 -1
- package/build/types/stream.d.ts +12 -6
- package/package.json +8 -2
- package/build/services/activities/activity.d.ts +0 -192
- package/build/services/activities/activity.js +0 -786
- package/build/services/durable/workflow/interrupt.d.ts +0 -55
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when a workflow detects a pending cancellation request.
|
|
3
|
+
* Workflows can catch this error to perform cleanup before terminating.
|
|
4
|
+
*
|
|
5
|
+
* ## Examples
|
|
6
|
+
*
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
9
|
+
*
|
|
10
|
+
* export async function myWorkflow(): Promise<string> {
|
|
11
|
+
* const acts = Durable.workflow.proxyActivities<typeof activities>({ ... });
|
|
12
|
+
*
|
|
13
|
+
* try {
|
|
14
|
+
* await acts.longRunningTask();
|
|
15
|
+
* } catch (err) {
|
|
16
|
+
* if (Durable.workflow.isCancellation(err)) {
|
|
17
|
+
* await CancellationScope.nonCancellable(async () => {
|
|
18
|
+
* await acts.cleanup();
|
|
19
|
+
* });
|
|
20
|
+
* return 'cancelled-with-cleanup';
|
|
21
|
+
* }
|
|
22
|
+
* throw err;
|
|
23
|
+
* }
|
|
24
|
+
* return 'done';
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class CancelledFailure extends Error {
|
|
29
|
+
type: string;
|
|
30
|
+
constructor(message?: string);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Type guard that returns `true` if the error is a {@link CancelledFailure},
|
|
34
|
+
* indicating the workflow was cancelled via `handle.cancel()`.
|
|
35
|
+
*
|
|
36
|
+
* Use this inside `catch` blocks to distinguish cancellation from
|
|
37
|
+
* application errors. Always check with `didInterrupt` first for
|
|
38
|
+
* engine control-flow signals, then check `isCancellation` for
|
|
39
|
+
* cooperative cancellation.
|
|
40
|
+
*
|
|
41
|
+
* @param {any} err - The error to check.
|
|
42
|
+
* @returns {boolean} `true` if the error is a `CancelledFailure`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isCancellation(err: any): err is CancelledFailure;
|
|
45
|
+
/**
|
|
46
|
+
* Controls how cancellation propagates through workflow code.
|
|
47
|
+
*
|
|
48
|
+
* When a workflow is cancelled via `handle.cancel()`, the next durable
|
|
49
|
+
* operation (`sleep`, `proxyActivities`, `executeChild`, `condition`,
|
|
50
|
+
* `continueAsNew`) throws a {@link CancelledFailure}. Use
|
|
51
|
+
* `CancellationScope.nonCancellable(fn)` to shield cleanup code from
|
|
52
|
+
* this behavior — durable operations inside the scope will execute
|
|
53
|
+
* normally even when cancellation is pending.
|
|
54
|
+
*
|
|
55
|
+
* ## Examples
|
|
56
|
+
*
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
59
|
+
* const { CancellationScope, isCancellation } = Durable.workflow;
|
|
60
|
+
*
|
|
61
|
+
* export async function orderWorkflow(orderId: string): Promise<string> {
|
|
62
|
+
* const acts = Durable.workflow.proxyActivities<typeof activities>({ ... });
|
|
63
|
+
*
|
|
64
|
+
* try {
|
|
65
|
+
* await acts.chargePayment(orderId);
|
|
66
|
+
* await Durable.workflow.sleep('7 days');
|
|
67
|
+
* await acts.shipOrder(orderId);
|
|
68
|
+
* } catch (err) {
|
|
69
|
+
* if (isCancellation(err)) {
|
|
70
|
+
* // Shield cleanup from further cancellation
|
|
71
|
+
* await CancellationScope.nonCancellable(async () => {
|
|
72
|
+
* await acts.refundPayment(orderId);
|
|
73
|
+
* await acts.notifyCustomer(orderId, 'Order cancelled');
|
|
74
|
+
* });
|
|
75
|
+
* return 'cancelled';
|
|
76
|
+
* }
|
|
77
|
+
* throw err;
|
|
78
|
+
* }
|
|
79
|
+
* return 'shipped';
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare class CancellationScope {
|
|
84
|
+
/**
|
|
85
|
+
* Execute `fn` with cancellation suppressed. Durable operations
|
|
86
|
+
* inside the callback will not throw `CancelledFailure` even if
|
|
87
|
+
* the workflow has a pending cancellation request. Use this for
|
|
88
|
+
* cleanup logic (refunds, notifications, audit logs) that must
|
|
89
|
+
* complete regardless of cancellation state.
|
|
90
|
+
*
|
|
91
|
+
* @param fn - The async function to execute without cancellation.
|
|
92
|
+
* @returns The return value of `fn`.
|
|
93
|
+
*/
|
|
94
|
+
static nonCancellable<T>(fn: () => Promise<T>): Promise<T>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Checks for a pending cancellation request and throws
|
|
98
|
+
* `CancelledFailure` if one exists and we are not inside
|
|
99
|
+
* a `CancellationScope.nonCancellable()` block.
|
|
100
|
+
*
|
|
101
|
+
* Called at the entry of every durable operation.
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
export declare function checkCancellation(): void;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCancellation = exports.CancellationScope = exports.isCancellation = exports.CancelledFailure = void 0;
|
|
4
|
+
const common_1 = require("./common");
|
|
5
|
+
/**
|
|
6
|
+
* Error thrown when a workflow detects a pending cancellation request.
|
|
7
|
+
* Workflows can catch this error to perform cleanup before terminating.
|
|
8
|
+
*
|
|
9
|
+
* ## Examples
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
13
|
+
*
|
|
14
|
+
* export async function myWorkflow(): Promise<string> {
|
|
15
|
+
* const acts = Durable.workflow.proxyActivities<typeof activities>({ ... });
|
|
16
|
+
*
|
|
17
|
+
* try {
|
|
18
|
+
* await acts.longRunningTask();
|
|
19
|
+
* } catch (err) {
|
|
20
|
+
* if (Durable.workflow.isCancellation(err)) {
|
|
21
|
+
* await CancellationScope.nonCancellable(async () => {
|
|
22
|
+
* await acts.cleanup();
|
|
23
|
+
* });
|
|
24
|
+
* return 'cancelled-with-cleanup';
|
|
25
|
+
* }
|
|
26
|
+
* throw err;
|
|
27
|
+
* }
|
|
28
|
+
* return 'done';
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class CancelledFailure extends Error {
|
|
33
|
+
constructor(message = 'Workflow cancelled') {
|
|
34
|
+
super(message);
|
|
35
|
+
this.type = 'CancelledFailure';
|
|
36
|
+
this.name = 'CancelledFailure';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.CancelledFailure = CancelledFailure;
|
|
40
|
+
/**
|
|
41
|
+
* Type guard that returns `true` if the error is a {@link CancelledFailure},
|
|
42
|
+
* indicating the workflow was cancelled via `handle.cancel()`.
|
|
43
|
+
*
|
|
44
|
+
* Use this inside `catch` blocks to distinguish cancellation from
|
|
45
|
+
* application errors. Always check with `didInterrupt` first for
|
|
46
|
+
* engine control-flow signals, then check `isCancellation` for
|
|
47
|
+
* cooperative cancellation.
|
|
48
|
+
*
|
|
49
|
+
* @param {any} err - The error to check.
|
|
50
|
+
* @returns {boolean} `true` if the error is a `CancelledFailure`.
|
|
51
|
+
*/
|
|
52
|
+
function isCancellation(err) {
|
|
53
|
+
return err instanceof CancelledFailure;
|
|
54
|
+
}
|
|
55
|
+
exports.isCancellation = isCancellation;
|
|
56
|
+
/**
|
|
57
|
+
* Controls how cancellation propagates through workflow code.
|
|
58
|
+
*
|
|
59
|
+
* When a workflow is cancelled via `handle.cancel()`, the next durable
|
|
60
|
+
* operation (`sleep`, `proxyActivities`, `executeChild`, `condition`,
|
|
61
|
+
* `continueAsNew`) throws a {@link CancelledFailure}. Use
|
|
62
|
+
* `CancellationScope.nonCancellable(fn)` to shield cleanup code from
|
|
63
|
+
* this behavior — durable operations inside the scope will execute
|
|
64
|
+
* normally even when cancellation is pending.
|
|
65
|
+
*
|
|
66
|
+
* ## Examples
|
|
67
|
+
*
|
|
68
|
+
* ```typescript
|
|
69
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
70
|
+
* const { CancellationScope, isCancellation } = Durable.workflow;
|
|
71
|
+
*
|
|
72
|
+
* export async function orderWorkflow(orderId: string): Promise<string> {
|
|
73
|
+
* const acts = Durable.workflow.proxyActivities<typeof activities>({ ... });
|
|
74
|
+
*
|
|
75
|
+
* try {
|
|
76
|
+
* await acts.chargePayment(orderId);
|
|
77
|
+
* await Durable.workflow.sleep('7 days');
|
|
78
|
+
* await acts.shipOrder(orderId);
|
|
79
|
+
* } catch (err) {
|
|
80
|
+
* if (isCancellation(err)) {
|
|
81
|
+
* // Shield cleanup from further cancellation
|
|
82
|
+
* await CancellationScope.nonCancellable(async () => {
|
|
83
|
+
* await acts.refundPayment(orderId);
|
|
84
|
+
* await acts.notifyCustomer(orderId, 'Order cancelled');
|
|
85
|
+
* });
|
|
86
|
+
* return 'cancelled';
|
|
87
|
+
* }
|
|
88
|
+
* throw err;
|
|
89
|
+
* }
|
|
90
|
+
* return 'shipped';
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
class CancellationScope {
|
|
95
|
+
/**
|
|
96
|
+
* Execute `fn` with cancellation suppressed. Durable operations
|
|
97
|
+
* inside the callback will not throw `CancelledFailure` even if
|
|
98
|
+
* the workflow has a pending cancellation request. Use this for
|
|
99
|
+
* cleanup logic (refunds, notifications, audit logs) that must
|
|
100
|
+
* complete regardless of cancellation state.
|
|
101
|
+
*
|
|
102
|
+
* @param fn - The async function to execute without cancellation.
|
|
103
|
+
* @returns The return value of `fn`.
|
|
104
|
+
*/
|
|
105
|
+
static async nonCancellable(fn) {
|
|
106
|
+
const store = common_1.asyncLocalStorage.getStore();
|
|
107
|
+
const prev = store.get('nonCancellable');
|
|
108
|
+
store.set('nonCancellable', true);
|
|
109
|
+
try {
|
|
110
|
+
return await fn();
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
store.set('nonCancellable', prev ?? false);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.CancellationScope = CancellationScope;
|
|
118
|
+
/**
|
|
119
|
+
* Checks for a pending cancellation request and throws
|
|
120
|
+
* `CancelledFailure` if one exists and we are not inside
|
|
121
|
+
* a `CancellationScope.nonCancellable()` block.
|
|
122
|
+
*
|
|
123
|
+
* Called at the entry of every durable operation.
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
function checkCancellation() {
|
|
127
|
+
const store = common_1.asyncLocalStorage.getStore();
|
|
128
|
+
if (!store)
|
|
129
|
+
return;
|
|
130
|
+
const replay = store.get('replay');
|
|
131
|
+
if (!replay)
|
|
132
|
+
return;
|
|
133
|
+
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
134
|
+
const cancelKey = `-cancelled${workflowDimension}-`;
|
|
135
|
+
if (cancelKey in replay && !store.get('nonCancellable')) {
|
|
136
|
+
throw new CancelledFailure();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.checkCancellation = checkCancellation;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DurableChildError, DurableFatalError, DurableMaxedError, DurableProxyError, DurableRetryError, DurableSleepError, DurableTimeoutError, DurableWaitForError } from '../../../modules/errors';
|
|
1
|
+
import { DurableChildError, DurableContinueAsNewError, DurableFatalError, DurableMaxedError, DurableProxyError, DurableRetryError, DurableSleepError, DurableTimeoutError, DurableWaitForError } from '../../../modules/errors';
|
|
2
2
|
import { KeyService, KeyType } from '../../../modules/key';
|
|
3
3
|
import { asyncLocalStorage } from '../../../modules/storage';
|
|
4
4
|
import { deterministicRandom, guid, s, sleepImmediate } from '../../../modules/utils';
|
|
@@ -8,13 +8,14 @@ import { ActivityConfig, ChildResponseType, HookOptions, ProxyResponseType, Prox
|
|
|
8
8
|
import { JobInterruptOptions } from '../../../types/job';
|
|
9
9
|
import { StreamCode, StreamStatus } from '../../../types/stream';
|
|
10
10
|
import { StringAnyType, StringScalarType, StringStringType } from '../../../types/serializer';
|
|
11
|
-
import { HMSH_CODE_DURABLE_CHILD, HMSH_CODE_DURABLE_FATAL, HMSH_CODE_DURABLE_MAXED, HMSH_CODE_DURABLE_PROXY, HMSH_CODE_DURABLE_SLEEP, HMSH_CODE_DURABLE_TIMEOUT, HMSH_CODE_DURABLE_WAIT, HMSH_DURABLE_EXP_BACKOFF, HMSH_DURABLE_MAX_ATTEMPTS, HMSH_DURABLE_MAX_INTERVAL } from '../../../modules/enums';
|
|
12
|
-
import { DurableChildErrorType, DurableProxyErrorType } from '../../../types/error';
|
|
11
|
+
import { HMSH_CODE_DURABLE_CHILD, HMSH_CODE_DURABLE_CONTINUE, HMSH_CODE_DURABLE_FATAL, HMSH_CODE_DURABLE_MAXED, HMSH_CODE_DURABLE_PROXY, HMSH_CODE_DURABLE_SLEEP, HMSH_CODE_DURABLE_TIMEOUT, HMSH_CODE_DURABLE_WAIT, HMSH_DURABLE_EXP_BACKOFF, HMSH_DURABLE_INITIAL_INTERVAL, HMSH_DURABLE_MAX_ATTEMPTS, HMSH_DURABLE_MAX_INTERVAL } from '../../../modules/enums';
|
|
12
|
+
import { DurableChildErrorType, DurableContinueAsNewErrorType, DurableProxyErrorType } from '../../../types/error';
|
|
13
13
|
import { TelemetryService } from '../../telemetry';
|
|
14
|
+
import { DurableTelemetryService } from '../telemetry';
|
|
14
15
|
import { QuorumMessage } from '../../../types';
|
|
15
16
|
import { UserMessage } from '../../../types/quorum';
|
|
16
17
|
import { Search } from '../search';
|
|
17
18
|
import { WorkerService } from '../worker';
|
|
18
19
|
import { Entity } from '../entity';
|
|
19
20
|
import { ExecHookOptions } from './execHook';
|
|
20
|
-
export { DurableChildError, DurableFatalError, DurableMaxedError, DurableProxyError, DurableRetryError, DurableSleepError, DurableTimeoutError, DurableWaitForError, KeyService, KeyType, asyncLocalStorage, deterministicRandom, guid, s, sleepImmediate, HotMesh, SerializerService, ActivityConfig, ChildResponseType, HookOptions, ExecHookOptions, ProxyResponseType, ProxyType, WorkflowContext, WorkflowOptions, JobInterruptOptions, StreamCode, StreamStatus, StringAnyType, StringScalarType, StringStringType, HMSH_CODE_DURABLE_CHILD, HMSH_CODE_DURABLE_FATAL, HMSH_CODE_DURABLE_MAXED, HMSH_CODE_DURABLE_PROXY, HMSH_CODE_DURABLE_SLEEP, HMSH_CODE_DURABLE_TIMEOUT, HMSH_CODE_DURABLE_WAIT, HMSH_DURABLE_EXP_BACKOFF, HMSH_DURABLE_MAX_ATTEMPTS, HMSH_DURABLE_MAX_INTERVAL, DurableChildErrorType, DurableProxyErrorType, TelemetryService, QuorumMessage, UserMessage, Search, WorkerService, Entity, };
|
|
21
|
+
export { DurableChildError, DurableContinueAsNewError, DurableFatalError, DurableMaxedError, DurableProxyError, DurableRetryError, DurableSleepError, DurableTimeoutError, DurableWaitForError, KeyService, KeyType, asyncLocalStorage, deterministicRandom, guid, s, sleepImmediate, HotMesh, SerializerService, ActivityConfig, ChildResponseType, HookOptions, ExecHookOptions, ProxyResponseType, ProxyType, WorkflowContext, WorkflowOptions, JobInterruptOptions, StreamCode, StreamStatus, StringAnyType, StringScalarType, StringStringType, HMSH_CODE_DURABLE_CHILD, HMSH_CODE_DURABLE_CONTINUE, HMSH_CODE_DURABLE_FATAL, HMSH_CODE_DURABLE_MAXED, HMSH_CODE_DURABLE_PROXY, HMSH_CODE_DURABLE_SLEEP, HMSH_CODE_DURABLE_TIMEOUT, HMSH_CODE_DURABLE_WAIT, HMSH_DURABLE_EXP_BACKOFF, HMSH_DURABLE_INITIAL_INTERVAL, HMSH_DURABLE_MAX_ATTEMPTS, HMSH_DURABLE_MAX_INTERVAL, DurableChildErrorType, DurableContinueAsNewErrorType, DurableProxyErrorType, TelemetryService, DurableTelemetryService, QuorumMessage, UserMessage, Search, WorkerService, Entity, };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Entity = exports.WorkerService = exports.Search = exports.TelemetryService = exports.HMSH_DURABLE_MAX_INTERVAL = exports.HMSH_DURABLE_MAX_ATTEMPTS = exports.HMSH_DURABLE_EXP_BACKOFF = exports.HMSH_CODE_DURABLE_WAIT = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_SLEEP = exports.HMSH_CODE_DURABLE_PROXY = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_CHILD = exports.StreamStatus = exports.SerializerService = exports.HotMesh = exports.sleepImmediate = exports.s = exports.guid = exports.deterministicRandom = exports.asyncLocalStorage = exports.KeyType = exports.KeyService = exports.DurableWaitForError = exports.DurableTimeoutError = exports.DurableSleepError = exports.DurableRetryError = exports.DurableProxyError = exports.DurableMaxedError = exports.DurableFatalError = exports.DurableChildError = void 0;
|
|
3
|
+
exports.Entity = exports.WorkerService = exports.Search = exports.DurableTelemetryService = exports.TelemetryService = exports.HMSH_DURABLE_MAX_INTERVAL = exports.HMSH_DURABLE_MAX_ATTEMPTS = exports.HMSH_DURABLE_INITIAL_INTERVAL = exports.HMSH_DURABLE_EXP_BACKOFF = exports.HMSH_CODE_DURABLE_WAIT = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_SLEEP = exports.HMSH_CODE_DURABLE_PROXY = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_CONTINUE = exports.HMSH_CODE_DURABLE_CHILD = exports.StreamStatus = exports.SerializerService = exports.HotMesh = exports.sleepImmediate = exports.s = exports.guid = exports.deterministicRandom = exports.asyncLocalStorage = exports.KeyType = exports.KeyService = exports.DurableWaitForError = exports.DurableTimeoutError = exports.DurableSleepError = exports.DurableRetryError = exports.DurableProxyError = exports.DurableMaxedError = exports.DurableFatalError = exports.DurableContinueAsNewError = exports.DurableChildError = void 0;
|
|
4
4
|
const errors_1 = require("../../../modules/errors");
|
|
5
5
|
Object.defineProperty(exports, "DurableChildError", { enumerable: true, get: function () { return errors_1.DurableChildError; } });
|
|
6
|
+
Object.defineProperty(exports, "DurableContinueAsNewError", { enumerable: true, get: function () { return errors_1.DurableContinueAsNewError; } });
|
|
6
7
|
Object.defineProperty(exports, "DurableFatalError", { enumerable: true, get: function () { return errors_1.DurableFatalError; } });
|
|
7
8
|
Object.defineProperty(exports, "DurableMaxedError", { enumerable: true, get: function () { return errors_1.DurableMaxedError; } });
|
|
8
9
|
Object.defineProperty(exports, "DurableProxyError", { enumerable: true, get: function () { return errors_1.DurableProxyError; } });
|
|
@@ -28,6 +29,7 @@ const stream_1 = require("../../../types/stream");
|
|
|
28
29
|
Object.defineProperty(exports, "StreamStatus", { enumerable: true, get: function () { return stream_1.StreamStatus; } });
|
|
29
30
|
const enums_1 = require("../../../modules/enums");
|
|
30
31
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_CHILD", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_CHILD; } });
|
|
32
|
+
Object.defineProperty(exports, "HMSH_CODE_DURABLE_CONTINUE", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_CONTINUE; } });
|
|
31
33
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_FATAL", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_FATAL; } });
|
|
32
34
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_MAXED", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_MAXED; } });
|
|
33
35
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_PROXY", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_PROXY; } });
|
|
@@ -35,10 +37,13 @@ Object.defineProperty(exports, "HMSH_CODE_DURABLE_SLEEP", { enumerable: true, ge
|
|
|
35
37
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_TIMEOUT", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_TIMEOUT; } });
|
|
36
38
|
Object.defineProperty(exports, "HMSH_CODE_DURABLE_WAIT", { enumerable: true, get: function () { return enums_1.HMSH_CODE_DURABLE_WAIT; } });
|
|
37
39
|
Object.defineProperty(exports, "HMSH_DURABLE_EXP_BACKOFF", { enumerable: true, get: function () { return enums_1.HMSH_DURABLE_EXP_BACKOFF; } });
|
|
40
|
+
Object.defineProperty(exports, "HMSH_DURABLE_INITIAL_INTERVAL", { enumerable: true, get: function () { return enums_1.HMSH_DURABLE_INITIAL_INTERVAL; } });
|
|
38
41
|
Object.defineProperty(exports, "HMSH_DURABLE_MAX_ATTEMPTS", { enumerable: true, get: function () { return enums_1.HMSH_DURABLE_MAX_ATTEMPTS; } });
|
|
39
42
|
Object.defineProperty(exports, "HMSH_DURABLE_MAX_INTERVAL", { enumerable: true, get: function () { return enums_1.HMSH_DURABLE_MAX_INTERVAL; } });
|
|
40
43
|
const telemetry_1 = require("../../telemetry");
|
|
41
44
|
Object.defineProperty(exports, "TelemetryService", { enumerable: true, get: function () { return telemetry_1.TelemetryService; } });
|
|
45
|
+
const telemetry_2 = require("../telemetry");
|
|
46
|
+
Object.defineProperty(exports, "DurableTelemetryService", { enumerable: true, get: function () { return telemetry_2.DurableTelemetryService; } });
|
|
42
47
|
const search_1 = require("../search");
|
|
43
48
|
Object.defineProperty(exports, "Search", { enumerable: true, get: function () { return search_1.Search; } });
|
|
44
49
|
const worker_1 = require("../worker");
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* The workflow suspends durably — it survives process restarts and will
|
|
4
4
|
* resume exactly once when the matching `signal()` call delivers data.
|
|
5
5
|
*
|
|
6
|
-
* `
|
|
6
|
+
* `condition` is the **receive** side of the signal coordination pair.
|
|
7
7
|
* The **send** side is `signal()`, which can be called from another
|
|
8
8
|
* workflow, a hook function, or externally via `Durable.Client.workflow.signal()`.
|
|
9
9
|
*
|
|
10
|
-
* On replay, `
|
|
10
|
+
* On replay, `condition` returns the previously stored signal payload
|
|
11
11
|
* immediately (no actual suspension occurs).
|
|
12
12
|
*
|
|
13
13
|
* ## Examples
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* await submitForReview(orderId);
|
|
23
23
|
*
|
|
24
24
|
* // Pause indefinitely until a human approves or rejects
|
|
25
|
-
* const decision = await Durable.workflow.
|
|
25
|
+
* const decision = await Durable.workflow.condition<{ approved: boolean }>('approval');
|
|
26
26
|
*
|
|
27
27
|
* return decision.approved;
|
|
28
28
|
* }
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
* // Fan-in: wait for multiple signals in parallel
|
|
36
36
|
* export async function gatherWorkflow(): Promise<[string, number]> {
|
|
37
37
|
* const [name, score] = await Promise.all([
|
|
38
|
-
* Durable.workflow.
|
|
39
|
-
* Durable.workflow.
|
|
38
|
+
* Durable.workflow.condition<string>('name-signal'),
|
|
39
|
+
* Durable.workflow.condition<number>('score-signal'),
|
|
40
40
|
* ]);
|
|
41
41
|
* return [name, score];
|
|
42
42
|
* }
|
|
@@ -55,12 +55,13 @@
|
|
|
55
55
|
* });
|
|
56
56
|
*
|
|
57
57
|
* // Wait for the hook to signal completion
|
|
58
|
-
* return await Durable.workflow.
|
|
58
|
+
* return await Durable.workflow.condition<string>(signalId);
|
|
59
59
|
* }
|
|
60
60
|
* ```
|
|
61
61
|
*
|
|
62
62
|
* @template T - The type of data expected in the signal payload.
|
|
63
63
|
* @param {string} signalId - A unique signal identifier shared by the sender and receiver.
|
|
64
|
-
* @
|
|
64
|
+
* @param {string} [timeout] - Optional duration (e.g., '30s', '5m', '1h'). Returns `false` on timeout.
|
|
65
|
+
* @returns {Promise<T | false>} The signal data, or `false` if the timeout expired first.
|
|
65
66
|
*/
|
|
66
|
-
export declare function
|
|
67
|
+
export declare function condition<T>(signalId: string, timeout?: string): Promise<T | false>;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.condition = void 0;
|
|
4
4
|
const common_1 = require("./common");
|
|
5
|
+
const cancellationScope_1 = require("./cancellationScope");
|
|
5
6
|
const didRun_1 = require("./didRun");
|
|
6
7
|
/**
|
|
7
8
|
* Pauses the workflow until a signal with the given `signalId` is received.
|
|
8
9
|
* The workflow suspends durably — it survives process restarts and will
|
|
9
10
|
* resume exactly once when the matching `signal()` call delivers data.
|
|
10
11
|
*
|
|
11
|
-
* `
|
|
12
|
+
* `condition` is the **receive** side of the signal coordination pair.
|
|
12
13
|
* The **send** side is `signal()`, which can be called from another
|
|
13
14
|
* workflow, a hook function, or externally via `Durable.Client.workflow.signal()`.
|
|
14
15
|
*
|
|
15
|
-
* On replay, `
|
|
16
|
+
* On replay, `condition` returns the previously stored signal payload
|
|
16
17
|
* immediately (no actual suspension occurs).
|
|
17
18
|
*
|
|
18
19
|
* ## Examples
|
|
@@ -27,7 +28,7 @@ const didRun_1 = require("./didRun");
|
|
|
27
28
|
* await submitForReview(orderId);
|
|
28
29
|
*
|
|
29
30
|
* // Pause indefinitely until a human approves or rejects
|
|
30
|
-
* const decision = await Durable.workflow.
|
|
31
|
+
* const decision = await Durable.workflow.condition<{ approved: boolean }>('approval');
|
|
31
32
|
*
|
|
32
33
|
* return decision.approved;
|
|
33
34
|
* }
|
|
@@ -40,8 +41,8 @@ const didRun_1 = require("./didRun");
|
|
|
40
41
|
* // Fan-in: wait for multiple signals in parallel
|
|
41
42
|
* export async function gatherWorkflow(): Promise<[string, number]> {
|
|
42
43
|
* const [name, score] = await Promise.all([
|
|
43
|
-
* Durable.workflow.
|
|
44
|
-
* Durable.workflow.
|
|
44
|
+
* Durable.workflow.condition<string>('name-signal'),
|
|
45
|
+
* Durable.workflow.condition<number>('score-signal'),
|
|
45
46
|
* ]);
|
|
46
47
|
* return [name, score];
|
|
47
48
|
* }
|
|
@@ -60,20 +61,51 @@ const didRun_1 = require("./didRun");
|
|
|
60
61
|
* });
|
|
61
62
|
*
|
|
62
63
|
* // Wait for the hook to signal completion
|
|
63
|
-
* return await Durable.workflow.
|
|
64
|
+
* return await Durable.workflow.condition<string>(signalId);
|
|
64
65
|
* }
|
|
65
66
|
* ```
|
|
66
67
|
*
|
|
67
68
|
* @template T - The type of data expected in the signal payload.
|
|
68
69
|
* @param {string} signalId - A unique signal identifier shared by the sender and receiver.
|
|
69
|
-
* @
|
|
70
|
+
* @param {string} [timeout] - Optional duration (e.g., '30s', '5m', '1h'). Returns `false` on timeout.
|
|
71
|
+
* @returns {Promise<T | false>} The signal data, or `false` if the timeout expired first.
|
|
70
72
|
*/
|
|
71
|
-
async function
|
|
73
|
+
async function condition(signalId, timeout) {
|
|
72
74
|
const [didRunAlready, execIndex, result] = await (0, didRun_1.didRun)('wait');
|
|
75
|
+
(0, cancellationScope_1.checkCancellation)();
|
|
73
76
|
if (didRunAlready) {
|
|
77
|
+
// Emit RETURN span in debug mode
|
|
78
|
+
if (common_1.DurableTelemetryService.isVerbose() && result) {
|
|
79
|
+
const store = common_1.asyncLocalStorage.getStore();
|
|
80
|
+
const workflowTrace = store.get('workflowTrace');
|
|
81
|
+
const workflowSpan = store.get('workflowSpan');
|
|
82
|
+
if (workflowTrace && workflowSpan && result.ac && result.au) {
|
|
83
|
+
common_1.DurableTelemetryService.emitDurationSpan(workflowTrace, workflowSpan, `RETURN/wait/${signalId}/${execIndex}`, common_1.DurableTelemetryService.parseTimestamp(result.ac), common_1.DurableTelemetryService.parseTimestamp(result.au), {
|
|
84
|
+
'durable.operation.type': 'wait',
|
|
85
|
+
'durable.signal.id': signalId,
|
|
86
|
+
'durable.exec.index': execIndex,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// If the condition timed out (timeout won the race), return false
|
|
91
|
+
if (result?.timedOut) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
74
94
|
return result.data.data;
|
|
75
95
|
}
|
|
76
96
|
const store = common_1.asyncLocalStorage.getStore();
|
|
97
|
+
// Emit DISPATCH span in debug mode
|
|
98
|
+
if (common_1.DurableTelemetryService.isVerbose()) {
|
|
99
|
+
const workflowTrace = store.get('workflowTrace');
|
|
100
|
+
const workflowSpan = store.get('workflowSpan');
|
|
101
|
+
if (workflowTrace && workflowSpan) {
|
|
102
|
+
common_1.DurableTelemetryService.emitPointSpan(workflowTrace, workflowSpan, `DISPATCH/wait/${signalId}/${execIndex}`, {
|
|
103
|
+
'durable.operation.type': 'wait',
|
|
104
|
+
'durable.signal.id': signalId,
|
|
105
|
+
'durable.exec.index': execIndex,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
77
109
|
const interruptionRegistry = store.get('interruptionRegistry');
|
|
78
110
|
const workflowId = store.get('workflowId');
|
|
79
111
|
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
@@ -84,10 +116,11 @@ async function waitFor(signalId) {
|
|
|
84
116
|
workflowDimension,
|
|
85
117
|
type: 'DurableWaitForError',
|
|
86
118
|
code: common_1.HMSH_CODE_DURABLE_WAIT,
|
|
119
|
+
...(timeout ? { duration: (0, common_1.s)(timeout) } : {}),
|
|
87
120
|
};
|
|
88
121
|
interruptionRegistry.push(interruptionMessage);
|
|
89
122
|
await (0, common_1.sleepImmediate)();
|
|
90
|
-
//if you are seeing this error in the logs, you might have forgotten to `await
|
|
123
|
+
//if you are seeing this error in the logs, you might have forgotten to `await condition(...)`
|
|
91
124
|
throw new common_1.DurableWaitForError(interruptionMessage);
|
|
92
125
|
}
|
|
93
|
-
exports.
|
|
126
|
+
exports.condition = condition;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completes the current workflow execution and immediately starts a new
|
|
3
|
+
* execution of the same workflow with the provided arguments. The execution
|
|
4
|
+
* history is reset, preventing unbounded state growth in long-running
|
|
5
|
+
* workflows.
|
|
6
|
+
*
|
|
7
|
+
* `continueAsNew` never returns — it always throws an internal interruption
|
|
8
|
+
* error that the engine catches to orchestrate the restart. Any code after
|
|
9
|
+
* the call is unreachable.
|
|
10
|
+
*
|
|
11
|
+
* ## When to Use
|
|
12
|
+
*
|
|
13
|
+
* Use `continueAsNew` for workflows that would otherwise run indefinitely
|
|
14
|
+
* and accumulate unbounded replay history:
|
|
15
|
+
* - Polling loops
|
|
16
|
+
* - Subscription renewals
|
|
17
|
+
* - Iterative processing with cursors
|
|
18
|
+
* - Long-running agents with periodic state resets
|
|
19
|
+
*
|
|
20
|
+
* ## Examples
|
|
21
|
+
*
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
24
|
+
*
|
|
25
|
+
* // Cursor-based batch processor
|
|
26
|
+
* export async function batchProcessor(cursor: string, totalProcessed = 0): Promise<void> {
|
|
27
|
+
* const { fetchBatch, processBatch } = Durable.workflow.proxyActivities<typeof activities>({
|
|
28
|
+
* activities,
|
|
29
|
+
* retry: { maximumAttempts: 3 },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* const batch = await fetchBatch(cursor);
|
|
33
|
+
* await processBatch(batch.items);
|
|
34
|
+
*
|
|
35
|
+
* if (batch.nextCursor) {
|
|
36
|
+
* // Restart with fresh history, carrying forward the cursor
|
|
37
|
+
* await Durable.workflow.continueAsNew(batch.nextCursor, totalProcessed + batch.items.length);
|
|
38
|
+
* }
|
|
39
|
+
* // No more items — workflow completes naturally
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Periodic polling workflow
|
|
45
|
+
* export async function poller(resourceId: string, attempt = 0): Promise<string> {
|
|
46
|
+
* const { checkStatus } = Durable.workflow.proxyActivities<typeof activities>({
|
|
47
|
+
* activities,
|
|
48
|
+
* retry: { maximumAttempts: 3 },
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* const status = await checkStatus(resourceId);
|
|
52
|
+
* if (status === 'ready') return status;
|
|
53
|
+
*
|
|
54
|
+
* await Durable.workflow.sleep('30 seconds');
|
|
55
|
+
*
|
|
56
|
+
* // Reset history on each poll iteration
|
|
57
|
+
* await Durable.workflow.continueAsNew(resourceId, attempt + 1);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @param {...any[]} args - The arguments to pass to the new workflow execution.
|
|
62
|
+
* These become the workflow function's parameters on restart.
|
|
63
|
+
* @returns {Promise<never>} Never returns — always throws an internal interruption.
|
|
64
|
+
*/
|
|
65
|
+
export declare function continueAsNew(...args: any[]): Promise<never>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.continueAsNew = void 0;
|
|
4
|
+
const common_1 = require("./common");
|
|
5
|
+
const cancellationScope_1 = require("./cancellationScope");
|
|
6
|
+
/**
|
|
7
|
+
* Completes the current workflow execution and immediately starts a new
|
|
8
|
+
* execution of the same workflow with the provided arguments. The execution
|
|
9
|
+
* history is reset, preventing unbounded state growth in long-running
|
|
10
|
+
* workflows.
|
|
11
|
+
*
|
|
12
|
+
* `continueAsNew` never returns — it always throws an internal interruption
|
|
13
|
+
* error that the engine catches to orchestrate the restart. Any code after
|
|
14
|
+
* the call is unreachable.
|
|
15
|
+
*
|
|
16
|
+
* ## When to Use
|
|
17
|
+
*
|
|
18
|
+
* Use `continueAsNew` for workflows that would otherwise run indefinitely
|
|
19
|
+
* and accumulate unbounded replay history:
|
|
20
|
+
* - Polling loops
|
|
21
|
+
* - Subscription renewals
|
|
22
|
+
* - Iterative processing with cursors
|
|
23
|
+
* - Long-running agents with periodic state resets
|
|
24
|
+
*
|
|
25
|
+
* ## Examples
|
|
26
|
+
*
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
29
|
+
*
|
|
30
|
+
* // Cursor-based batch processor
|
|
31
|
+
* export async function batchProcessor(cursor: string, totalProcessed = 0): Promise<void> {
|
|
32
|
+
* const { fetchBatch, processBatch } = Durable.workflow.proxyActivities<typeof activities>({
|
|
33
|
+
* activities,
|
|
34
|
+
* retry: { maximumAttempts: 3 },
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const batch = await fetchBatch(cursor);
|
|
38
|
+
* await processBatch(batch.items);
|
|
39
|
+
*
|
|
40
|
+
* if (batch.nextCursor) {
|
|
41
|
+
* // Restart with fresh history, carrying forward the cursor
|
|
42
|
+
* await Durable.workflow.continueAsNew(batch.nextCursor, totalProcessed + batch.items.length);
|
|
43
|
+
* }
|
|
44
|
+
* // No more items — workflow completes naturally
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Periodic polling workflow
|
|
50
|
+
* export async function poller(resourceId: string, attempt = 0): Promise<string> {
|
|
51
|
+
* const { checkStatus } = Durable.workflow.proxyActivities<typeof activities>({
|
|
52
|
+
* activities,
|
|
53
|
+
* retry: { maximumAttempts: 3 },
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* const status = await checkStatus(resourceId);
|
|
57
|
+
* if (status === 'ready') return status;
|
|
58
|
+
*
|
|
59
|
+
* await Durable.workflow.sleep('30 seconds');
|
|
60
|
+
*
|
|
61
|
+
* // Reset history on each poll iteration
|
|
62
|
+
* await Durable.workflow.continueAsNew(resourceId, attempt + 1);
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param {...any[]} args - The arguments to pass to the new workflow execution.
|
|
67
|
+
* These become the workflow function's parameters on restart.
|
|
68
|
+
* @returns {Promise<never>} Never returns — always throws an internal interruption.
|
|
69
|
+
*/
|
|
70
|
+
async function continueAsNew(...args) {
|
|
71
|
+
const store = common_1.asyncLocalStorage.getStore();
|
|
72
|
+
(0, cancellationScope_1.checkCancellation)();
|
|
73
|
+
const interruptionRegistry = store.get('interruptionRegistry');
|
|
74
|
+
const workflowId = store.get('workflowId');
|
|
75
|
+
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
76
|
+
const counter = store.get('counter');
|
|
77
|
+
const execIndex = (counter.counter = counter.counter + 1);
|
|
78
|
+
const payload = {
|
|
79
|
+
arguments: args,
|
|
80
|
+
index: execIndex,
|
|
81
|
+
workflowDimension,
|
|
82
|
+
workflowId,
|
|
83
|
+
};
|
|
84
|
+
interruptionRegistry.push({
|
|
85
|
+
code: common_1.HMSH_CODE_DURABLE_CONTINUE,
|
|
86
|
+
type: 'DurableContinueAsNewError',
|
|
87
|
+
...payload,
|
|
88
|
+
});
|
|
89
|
+
await (0, common_1.sleepImmediate)();
|
|
90
|
+
throw new common_1.DurableContinueAsNewError(payload);
|
|
91
|
+
}
|
|
92
|
+
exports.continueAsNew = continueAsNew;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The core of the deterministic replay mechanism. Every durable operation
|
|
3
|
-
* (`proxyActivities`, `
|
|
3
|
+
* (`proxyActivities`, `executeChild`, `sleep`, `condition`, etc.) calls
|
|
4
4
|
* `didRun` before executing. It increments a shared counter to produce
|
|
5
5
|
* a unique `sessionId` of the form `-{prefix}{dimension}-{index}-`.
|
|
6
6
|
*
|
|
7
7
|
* If that `sessionId` already exists in the `replay` hash (loaded from
|
|
8
8
|
* the job's stored state), the previously persisted result is
|
|
9
9
|
* deserialized and returned — skipping re-execution entirely. This is
|
|
10
|
-
* analogous to
|
|
10
|
+
* analogous to event history replay in durable workflow engines.
|
|
11
11
|
*
|
|
12
12
|
* ## Session ID Format
|
|
13
13
|
*
|