@hotmeshio/hotmesh 0.8.0 → 0.9.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/.claude/settings.local.json +2 -1
- package/README.md +158 -38
- package/build/package.json +62 -67
- package/build/services/activities/activity.d.ts +58 -7
- package/build/services/activities/activity.js +66 -37
- package/build/services/activities/await.d.ts +101 -0
- package/build/services/activities/await.js +101 -0
- package/build/services/activities/cycle.d.ts +82 -0
- package/build/services/activities/cycle.js +86 -8
- package/build/services/activities/hook.d.ts +139 -1
- package/build/services/activities/hook.js +140 -2
- package/build/services/activities/interrupt.d.ts +112 -0
- package/build/services/activities/interrupt.js +118 -5
- package/build/services/activities/signal.d.ts +108 -3
- package/build/services/activities/signal.js +113 -8
- package/build/services/activities/trigger.d.ts +56 -4
- package/build/services/activities/trigger.js +119 -35
- package/build/services/activities/worker.d.ts +107 -0
- package/build/services/activities/worker.js +107 -0
- package/build/services/collator/index.d.ts +3 -15
- package/build/services/collator/index.js +7 -34
- package/build/services/engine/index.d.ts +18 -2
- package/build/services/engine/index.js +14 -4
- package/build/services/exporter/index.d.ts +2 -0
- package/build/services/exporter/index.js +1 -0
- package/build/services/hotmesh/index.d.ts +471 -236
- package/build/services/hotmesh/index.js +473 -238
- package/build/services/memflow/client.js +2 -2
- package/build/services/memflow/handle.js +1 -1
- package/build/services/memflow/index.d.ts +1 -1
- package/build/services/memflow/index.js +1 -1
- package/build/services/memflow/workflow/all.d.ts +28 -3
- package/build/services/memflow/workflow/all.js +28 -3
- package/build/services/memflow/workflow/context.d.ts +44 -1
- package/build/services/memflow/workflow/context.js +44 -1
- package/build/services/memflow/workflow/didRun.d.ts +23 -3
- package/build/services/memflow/workflow/didRun.js +23 -3
- package/build/services/memflow/workflow/emit.d.ts +43 -4
- package/build/services/memflow/workflow/emit.js +43 -4
- package/build/services/memflow/workflow/enrich.d.ts +32 -4
- package/build/services/memflow/workflow/enrich.js +32 -4
- package/build/services/memflow/workflow/entityMethods.d.ts +54 -7
- package/build/services/memflow/workflow/entityMethods.js +54 -7
- package/build/services/memflow/workflow/execChild.d.ts +96 -8
- package/build/services/memflow/workflow/execChild.js +96 -8
- package/build/services/memflow/workflow/execHook.d.ts +54 -39
- package/build/services/memflow/workflow/execHook.js +52 -38
- package/build/services/memflow/workflow/execHookBatch.d.ts +82 -29
- package/build/services/memflow/workflow/execHookBatch.js +80 -28
- package/build/services/memflow/workflow/hook.d.ts +68 -3
- package/build/services/memflow/workflow/hook.js +69 -4
- package/build/services/memflow/workflow/index.d.ts +65 -10
- package/build/services/memflow/workflow/index.js +65 -10
- package/build/services/memflow/workflow/interrupt.d.ts +50 -4
- package/build/services/memflow/workflow/interrupt.js +50 -4
- package/build/services/memflow/workflow/interruption.d.ts +49 -16
- package/build/services/memflow/workflow/interruption.js +49 -16
- package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +21 -4
- package/build/services/memflow/workflow/isSideEffectAllowed.js +21 -4
- package/build/services/memflow/workflow/proxyActivities.d.ts +70 -42
- package/build/services/memflow/workflow/proxyActivities.js +70 -42
- package/build/services/memflow/workflow/random.d.ts +33 -3
- package/build/services/memflow/workflow/random.js +33 -3
- package/build/services/memflow/workflow/searchMethods.d.ts +49 -2
- package/build/services/memflow/workflow/searchMethods.js +49 -2
- package/build/services/memflow/workflow/signal.d.ts +51 -22
- package/build/services/memflow/workflow/signal.js +52 -23
- package/build/services/memflow/workflow/sleepFor.d.ts +57 -18
- package/build/services/memflow/workflow/sleepFor.js +57 -18
- package/build/services/memflow/workflow/trace.d.ts +39 -6
- package/build/services/memflow/workflow/trace.js +39 -6
- package/build/services/memflow/workflow/waitFor.d.ts +55 -18
- package/build/services/memflow/workflow/waitFor.js +55 -18
- package/build/services/store/index.d.ts +1 -1
- package/build/services/store/providers/postgres/postgres.d.ts +1 -1
- package/build/services/store/providers/postgres/postgres.js +4 -3
- package/build/services/telemetry/index.js +6 -0
- package/build/types/activity.d.ts +1 -1
- package/build/types/hotmesh.d.ts +1 -1
- package/build/types/job.d.ts +1 -1
- package/build/types/memflow.d.ts +1 -1
- package/build/types/quorum.d.ts +2 -2
- package/build/vitest.config.d.ts +2 -0
- package/build/vitest.config.js +18 -0
- package/package.json +62 -67
- package/vitest.config.ts +17 -0
|
@@ -1,29 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sends a signal payload to
|
|
3
|
-
*
|
|
4
|
-
* and
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
2
|
+
* Sends a signal payload to a paused workflow thread that is awaiting this
|
|
3
|
+
* `signalId` via `waitFor()`. Signals are the primary mechanism for
|
|
4
|
+
* inter-workflow communication and for delivering results from hook
|
|
5
|
+
* functions back to the orchestrating workflow.
|
|
6
|
+
*
|
|
7
|
+
* `signal` is the **send** side of the coordination pair. The **receive**
|
|
8
|
+
* side is `waitFor()`. A signal can be sent from:
|
|
9
|
+
* - Another workflow function
|
|
10
|
+
* - A hook function (most common pattern with `execHook`)
|
|
11
|
+
* - An external client via `MemFlow.Client.workflow.signal()`
|
|
12
|
+
*
|
|
13
|
+
* Signals fire exactly once per workflow execution — the `isSideEffectAllowed`
|
|
14
|
+
* guard ensures they are not re-sent on replay.
|
|
15
|
+
*
|
|
16
|
+
* ## Examples
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
20
|
+
*
|
|
21
|
+
* // Hook function that signals completion back to the parent workflow
|
|
22
|
+
* export async function processOrder(
|
|
23
|
+
* orderId: string,
|
|
24
|
+
* signalInfo?: { signal: string; $memflow: boolean },
|
|
25
|
+
* ): Promise<{ total: number }> {
|
|
26
|
+
* const { calculateTotal } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
27
|
+
* const total = await calculateTotal(orderId);
|
|
28
|
+
*
|
|
29
|
+
* // Signal the waiting workflow with the result
|
|
30
|
+
* if (signalInfo?.signal) {
|
|
31
|
+
* await MemFlow.workflow.signal(signalInfo.signal, { total });
|
|
32
|
+
* }
|
|
33
|
+
* return { total };
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Cross-workflow coordination: workflow A signals workflow B
|
|
39
|
+
* export async function coordinatorWorkflow(): Promise<void> {
|
|
40
|
+
* const { prepareData } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
41
|
+
* const data = await prepareData();
|
|
42
|
+
*
|
|
43
|
+
* // Signal another workflow that is paused on waitFor('data-ready')
|
|
44
|
+
* await MemFlow.workflow.signal('data-ready', { payload: data });
|
|
15
45
|
* }
|
|
46
|
+
* ```
|
|
16
47
|
*
|
|
17
|
-
*
|
|
18
|
-
* //
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* timestamp: new Date().toISOString()
|
|
23
|
-
* });
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // External signal from an API handler (outside a workflow)
|
|
50
|
+
* const client = new MemFlow.Client({ connection });
|
|
51
|
+
* await client.workflow.signal('approval-signal', { approved: true });
|
|
52
|
+
* ```
|
|
24
53
|
*
|
|
25
|
-
* @param {string} signalId - Unique signal identifier that matches a waitFor() call.
|
|
26
|
-
* @param {Record<any, any>} data - The payload to
|
|
54
|
+
* @param {string} signalId - Unique signal identifier that matches a `waitFor()` call.
|
|
55
|
+
* @param {Record<any, any>} data - The payload to deliver to the waiting workflow.
|
|
27
56
|
* @returns {Promise<string>} The resulting hook/stream ID.
|
|
28
57
|
*/
|
|
29
58
|
export declare function signal(signalId: string, data: Record<any, any>): Promise<string>;
|
|
@@ -4,31 +4,60 @@ exports.signal = void 0;
|
|
|
4
4
|
const common_1 = require("./common");
|
|
5
5
|
const isSideEffectAllowed_1 = require("./isSideEffectAllowed");
|
|
6
6
|
/**
|
|
7
|
-
* Sends a signal payload to
|
|
8
|
-
*
|
|
9
|
-
* and
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
7
|
+
* Sends a signal payload to a paused workflow thread that is awaiting this
|
|
8
|
+
* `signalId` via `waitFor()`. Signals are the primary mechanism for
|
|
9
|
+
* inter-workflow communication and for delivering results from hook
|
|
10
|
+
* functions back to the orchestrating workflow.
|
|
11
|
+
*
|
|
12
|
+
* `signal` is the **send** side of the coordination pair. The **receive**
|
|
13
|
+
* side is `waitFor()`. A signal can be sent from:
|
|
14
|
+
* - Another workflow function
|
|
15
|
+
* - A hook function (most common pattern with `execHook`)
|
|
16
|
+
* - An external client via `MemFlow.Client.workflow.signal()`
|
|
17
|
+
*
|
|
18
|
+
* Signals fire exactly once per workflow execution — the `isSideEffectAllowed`
|
|
19
|
+
* guard ensures they are not re-sent on replay.
|
|
20
|
+
*
|
|
21
|
+
* ## Examples
|
|
22
|
+
*
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
25
|
+
*
|
|
26
|
+
* // Hook function that signals completion back to the parent workflow
|
|
27
|
+
* export async function processOrder(
|
|
28
|
+
* orderId: string,
|
|
29
|
+
* signalInfo?: { signal: string; $memflow: boolean },
|
|
30
|
+
* ): Promise<{ total: number }> {
|
|
31
|
+
* const { calculateTotal } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
32
|
+
* const total = await calculateTotal(orderId);
|
|
33
|
+
*
|
|
34
|
+
* // Signal the waiting workflow with the result
|
|
35
|
+
* if (signalInfo?.signal) {
|
|
36
|
+
* await MemFlow.workflow.signal(signalInfo.signal, { total });
|
|
37
|
+
* }
|
|
38
|
+
* return { total };
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // Cross-workflow coordination: workflow A signals workflow B
|
|
44
|
+
* export async function coordinatorWorkflow(): Promise<void> {
|
|
45
|
+
* const { prepareData } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
46
|
+
* const data = await prepareData();
|
|
47
|
+
*
|
|
48
|
+
* // Signal another workflow that is paused on waitFor('data-ready')
|
|
49
|
+
* await MemFlow.workflow.signal('data-ready', { payload: data });
|
|
20
50
|
* }
|
|
51
|
+
* ```
|
|
21
52
|
*
|
|
22
|
-
*
|
|
23
|
-
* //
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* timestamp: new Date().toISOString()
|
|
28
|
-
* });
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // External signal from an API handler (outside a workflow)
|
|
55
|
+
* const client = new MemFlow.Client({ connection });
|
|
56
|
+
* await client.workflow.signal('approval-signal', { approved: true });
|
|
57
|
+
* ```
|
|
29
58
|
*
|
|
30
|
-
* @param {string} signalId - Unique signal identifier that matches a waitFor() call.
|
|
31
|
-
* @param {Record<any, any>} data - The payload to
|
|
59
|
+
* @param {string} signalId - Unique signal identifier that matches a `waitFor()` call.
|
|
60
|
+
* @param {Record<any, any>} data - The payload to deliver to the waiting workflow.
|
|
32
61
|
* @returns {Promise<string>} The resulting hook/stream ID.
|
|
33
62
|
*/
|
|
34
63
|
async function signal(signalId, data) {
|
|
@@ -41,7 +70,7 @@ async function signal(signalId, data) {
|
|
|
41
70
|
namespace,
|
|
42
71
|
});
|
|
43
72
|
if (await (0, isSideEffectAllowed_1.isSideEffectAllowed)(hotMeshClient, 'signal')) {
|
|
44
|
-
return await hotMeshClient.
|
|
73
|
+
return await hotMeshClient.signal(`${namespace}.wfs.signal`, {
|
|
45
74
|
id: signalId,
|
|
46
75
|
data,
|
|
47
76
|
});
|
|
@@ -1,24 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
2
|
+
* Suspends workflow execution for a durable, crash-safe duration. Unlike
|
|
3
|
+
* `setTimeout`, this sleep survives process restarts — the engine persists
|
|
4
|
+
* the wake-up time and resumes the workflow when the timer expires.
|
|
5
|
+
*
|
|
6
|
+
* On replay, `sleepFor` returns immediately with the stored duration
|
|
7
|
+
* (no actual waiting occurs). This makes it safe for deterministic
|
|
8
|
+
* re-execution.
|
|
9
|
+
*
|
|
10
|
+
* ## Duration Formats
|
|
11
|
+
*
|
|
12
|
+
* Accepts any human-readable duration string parsed by the `ms` module:
|
|
13
|
+
* `'5 seconds'`, `'30s'`, `'2 minutes'`, `'1m'`, `'1 hour'`, `'2h'`,
|
|
14
|
+
* `'1 day'`, `'7d'`.
|
|
15
|
+
*
|
|
16
|
+
* ## Examples
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
20
|
+
*
|
|
21
|
+
* // Simple delay before continuing
|
|
22
|
+
* export async function reminderWorkflow(userId: string): Promise<void> {
|
|
23
|
+
* const { sendReminder } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
15
24
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
25
|
+
* // Wait 24 hours (survives server restarts)
|
|
26
|
+
* await MemFlow.workflow.sleepFor('24 hours');
|
|
27
|
+
* await sendReminder(userId, 'Your trial expires tomorrow');
|
|
28
|
+
*
|
|
29
|
+
* // Wait another 6 days
|
|
30
|
+
* await MemFlow.workflow.sleepFor('6 days');
|
|
31
|
+
* await sendReminder(userId, 'Your trial has expired');
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Exponential backoff with retry loop
|
|
37
|
+
* export async function pollingWorkflow(resourceId: string): Promise<string> {
|
|
38
|
+
* const { checkStatus } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
39
|
+
*
|
|
40
|
+
* for (let attempt = 0; attempt < 10; attempt++) {
|
|
41
|
+
* const status = await checkStatus(resourceId);
|
|
42
|
+
* if (status === 'ready') return status;
|
|
43
|
+
*
|
|
44
|
+
* // Exponential backoff: 1s, 2s, 4s, 8s, ...
|
|
45
|
+
* const delay = Math.pow(2, attempt);
|
|
46
|
+
* await MemFlow.workflow.sleepFor(`${delay} seconds`);
|
|
47
|
+
* }
|
|
48
|
+
* return 'timeout';
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Race a sleep against an activity
|
|
54
|
+
* const [result, _] = await Promise.all([
|
|
55
|
+
* activities.fetchData(id),
|
|
56
|
+
* MemFlow.workflow.sleepFor('30 seconds'),
|
|
57
|
+
* ]);
|
|
58
|
+
* ```
|
|
20
59
|
*
|
|
21
|
-
* @param {string} duration - A human-readable duration string
|
|
60
|
+
* @param {string} duration - A human-readable duration string.
|
|
22
61
|
* @returns {Promise<number>} The resolved duration in seconds.
|
|
23
62
|
*/
|
|
24
63
|
export declare function sleepFor(duration: string): Promise<number>;
|
|
@@ -4,26 +4,65 @@ exports.sleepFor = void 0;
|
|
|
4
4
|
const common_1 = require("./common");
|
|
5
5
|
const didRun_1 = require("./didRun");
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
7
|
+
* Suspends workflow execution for a durable, crash-safe duration. Unlike
|
|
8
|
+
* `setTimeout`, this sleep survives process restarts — the engine persists
|
|
9
|
+
* the wake-up time and resumes the workflow when the timer expires.
|
|
10
|
+
*
|
|
11
|
+
* On replay, `sleepFor` returns immediately with the stored duration
|
|
12
|
+
* (no actual waiting occurs). This makes it safe for deterministic
|
|
13
|
+
* re-execution.
|
|
14
|
+
*
|
|
15
|
+
* ## Duration Formats
|
|
16
|
+
*
|
|
17
|
+
* Accepts any human-readable duration string parsed by the `ms` module:
|
|
18
|
+
* `'5 seconds'`, `'30s'`, `'2 minutes'`, `'1m'`, `'1 hour'`, `'2h'`,
|
|
19
|
+
* `'1 day'`, `'7d'`.
|
|
20
|
+
*
|
|
21
|
+
* ## Examples
|
|
22
|
+
*
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
25
|
+
*
|
|
26
|
+
* // Simple delay before continuing
|
|
27
|
+
* export async function reminderWorkflow(userId: string): Promise<void> {
|
|
28
|
+
* const { sendReminder } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
20
29
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
30
|
+
* // Wait 24 hours (survives server restarts)
|
|
31
|
+
* await MemFlow.workflow.sleepFor('24 hours');
|
|
32
|
+
* await sendReminder(userId, 'Your trial expires tomorrow');
|
|
33
|
+
*
|
|
34
|
+
* // Wait another 6 days
|
|
35
|
+
* await MemFlow.workflow.sleepFor('6 days');
|
|
36
|
+
* await sendReminder(userId, 'Your trial has expired');
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Exponential backoff with retry loop
|
|
42
|
+
* export async function pollingWorkflow(resourceId: string): Promise<string> {
|
|
43
|
+
* const { checkStatus } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
44
|
+
*
|
|
45
|
+
* for (let attempt = 0; attempt < 10; attempt++) {
|
|
46
|
+
* const status = await checkStatus(resourceId);
|
|
47
|
+
* if (status === 'ready') return status;
|
|
48
|
+
*
|
|
49
|
+
* // Exponential backoff: 1s, 2s, 4s, 8s, ...
|
|
50
|
+
* const delay = Math.pow(2, attempt);
|
|
51
|
+
* await MemFlow.workflow.sleepFor(`${delay} seconds`);
|
|
52
|
+
* }
|
|
53
|
+
* return 'timeout';
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* ```typescript
|
|
58
|
+
* // Race a sleep against an activity
|
|
59
|
+
* const [result, _] = await Promise.all([
|
|
60
|
+
* activities.fetchData(id),
|
|
61
|
+
* MemFlow.workflow.sleepFor('30 seconds'),
|
|
62
|
+
* ]);
|
|
63
|
+
* ```
|
|
25
64
|
*
|
|
26
|
-
* @param {string} duration - A human-readable duration string
|
|
65
|
+
* @param {string} duration - A human-readable duration string.
|
|
27
66
|
* @returns {Promise<number>} The resolved duration in seconds.
|
|
28
67
|
*/
|
|
29
68
|
async function sleepFor(duration) {
|
|
@@ -1,13 +1,46 @@
|
|
|
1
1
|
import { StringScalarType } from './common';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Emits a distributed trace span to the configured telemetry sink
|
|
4
|
+
* (e.g., OpenTelemetry). The span is linked to the workflow's trace
|
|
5
|
+
* context (`traceId`, `spanId`) for end-to-end observability across
|
|
6
|
+
* workflow executions, activities, and child workflows.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
8
|
+
* By default (`config.once = true`), the trace is emitted exactly once
|
|
9
|
+
* per workflow execution — it will not re-fire on replay.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* ## Examples
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
15
|
+
*
|
|
16
|
+
* // Emit trace spans at key workflow milestones
|
|
17
|
+
* export async function orderWorkflow(orderId: string): Promise<void> {
|
|
18
|
+
* await MemFlow.workflow.trace({
|
|
19
|
+
* 'order.id': orderId,
|
|
20
|
+
* 'order.stage': 'started',
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const { processPayment } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
24
|
+
* await processPayment(orderId);
|
|
25
|
+
*
|
|
26
|
+
* await MemFlow.workflow.trace({
|
|
27
|
+
* 'order.id': orderId,
|
|
28
|
+
* 'order.stage': 'payment_completed',
|
|
29
|
+
* });
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Trace on every re-execution (for debugging replay behavior)
|
|
35
|
+
* await MemFlow.workflow.trace(
|
|
36
|
+
* { 'debug.counter': counter, 'debug.phase': 'retry' },
|
|
37
|
+
* { once: false },
|
|
38
|
+
* );
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @param {StringScalarType} attributes - Key-value attributes to attach to the trace span.
|
|
42
|
+
* @param {{ once: boolean }} [config={ once: true }] - If `true`, trace fires only once (idempotent).
|
|
43
|
+
* @returns {Promise<boolean>} `true` if tracing succeeded.
|
|
11
44
|
*/
|
|
12
45
|
export declare function trace(attributes: StringScalarType, config?: {
|
|
13
46
|
once: boolean;
|
|
@@ -5,14 +5,47 @@ const common_1 = require("./common");
|
|
|
5
5
|
const context_1 = require("./context");
|
|
6
6
|
const isSideEffectAllowed_1 = require("./isSideEffectAllowed");
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Emits a distributed trace span to the configured telemetry sink
|
|
9
|
+
* (e.g., OpenTelemetry). The span is linked to the workflow's trace
|
|
10
|
+
* context (`traceId`, `spanId`) for end-to-end observability across
|
|
11
|
+
* workflow executions, activities, and child workflows.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
13
|
+
* By default (`config.once = true`), the trace is emitted exactly once
|
|
14
|
+
* per workflow execution — it will not re-fire on replay.
|
|
12
15
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* ## Examples
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
20
|
+
*
|
|
21
|
+
* // Emit trace spans at key workflow milestones
|
|
22
|
+
* export async function orderWorkflow(orderId: string): Promise<void> {
|
|
23
|
+
* await MemFlow.workflow.trace({
|
|
24
|
+
* 'order.id': orderId,
|
|
25
|
+
* 'order.stage': 'started',
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const { processPayment } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
29
|
+
* await processPayment(orderId);
|
|
30
|
+
*
|
|
31
|
+
* await MemFlow.workflow.trace({
|
|
32
|
+
* 'order.id': orderId,
|
|
33
|
+
* 'order.stage': 'payment_completed',
|
|
34
|
+
* });
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* // Trace on every re-execution (for debugging replay behavior)
|
|
40
|
+
* await MemFlow.workflow.trace(
|
|
41
|
+
* { 'debug.counter': counter, 'debug.phase': 'retry' },
|
|
42
|
+
* { once: false },
|
|
43
|
+
* );
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @param {StringScalarType} attributes - Key-value attributes to attach to the trace span.
|
|
47
|
+
* @param {{ once: boolean }} [config={ once: true }] - If `true`, trace fires only once (idempotent).
|
|
48
|
+
* @returns {Promise<boolean>} `true` if tracing succeeded.
|
|
16
49
|
*/
|
|
17
50
|
async function trace(attributes, config = { once: true }) {
|
|
18
51
|
const store = common_1.asyncLocalStorage.getStore();
|
|
@@ -1,28 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pauses the workflow until a signal with the given `signalId` is received.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* The workflow suspends durably — it survives process restarts and will
|
|
4
|
+
* resume exactly once when the matching `signal()` call delivers data.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* `waitFor` is the **receive** side of the signal coordination pair.
|
|
7
|
+
* The **send** side is `signal()`, which can be called from another
|
|
8
|
+
* workflow, a hook function, or externally via `MemFlow.Client.workflow.signal()`.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* const [signal1, signal2] = await Promise.all([
|
|
13
|
-
* MemFlow.workflow.waitFor<Record<string, any>>('signal1'),
|
|
14
|
-
* MemFlow.workflow.waitFor<Record<string, any>>('signal2')
|
|
15
|
-
* ]);
|
|
10
|
+
* On replay, `waitFor` returns the previously stored signal payload
|
|
11
|
+
* immediately (no actual suspension occurs).
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
* // Typical pattern with hook functions
|
|
19
|
-
* // In main workflow:
|
|
20
|
-
* await MemFlow.workflow.waitFor<ResponseType>('hook-complete');
|
|
13
|
+
* ## Examples
|
|
21
14
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
24
17
|
*
|
|
25
|
-
*
|
|
18
|
+
* // Human-in-the-loop approval pattern
|
|
19
|
+
* export async function approvalWorkflow(orderId: string): Promise<boolean> {
|
|
20
|
+
* const { submitForReview } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
21
|
+
*
|
|
22
|
+
* await submitForReview(orderId);
|
|
23
|
+
*
|
|
24
|
+
* // Pause indefinitely until a human approves or rejects
|
|
25
|
+
* const decision = await MemFlow.workflow.waitFor<{ approved: boolean }>('approval');
|
|
26
|
+
*
|
|
27
|
+
* return decision.approved;
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // Later, from outside the workflow (e.g., an API handler):
|
|
31
|
+
* await client.workflow.signal('approval', { approved: true });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // Fan-in: wait for multiple signals in parallel
|
|
36
|
+
* export async function gatherWorkflow(): Promise<[string, number]> {
|
|
37
|
+
* const [name, score] = await Promise.all([
|
|
38
|
+
* MemFlow.workflow.waitFor<string>('name-signal'),
|
|
39
|
+
* MemFlow.workflow.waitFor<number>('score-signal'),
|
|
40
|
+
* ]);
|
|
41
|
+
* return [name, score];
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Paired with hook: spawn work and wait for the result
|
|
47
|
+
* export async function orchestrator(input: string): Promise<string> {
|
|
48
|
+
* const signalId = `result-${MemFlow.workflow.random()}`;
|
|
49
|
+
*
|
|
50
|
+
* // Spawn a hook that will signal back when done
|
|
51
|
+
* await MemFlow.workflow.hook({
|
|
52
|
+
* taskQueue: 'processors',
|
|
53
|
+
* workflowName: 'processItem',
|
|
54
|
+
* args: [input, signalId],
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // Wait for the hook to signal completion
|
|
58
|
+
* return await MemFlow.workflow.waitFor<string>(signalId);
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @template T - The type of data expected in the signal payload.
|
|
26
63
|
* @param {string} signalId - A unique signal identifier shared by the sender and receiver.
|
|
27
64
|
* @returns {Promise<T>} The data payload associated with the received signal.
|
|
28
65
|
*/
|
|
@@ -5,29 +5,66 @@ const common_1 = require("./common");
|
|
|
5
5
|
const didRun_1 = require("./didRun");
|
|
6
6
|
/**
|
|
7
7
|
* Pauses the workflow until a signal with the given `signalId` is received.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* The workflow suspends durably — it survives process restarts and will
|
|
9
|
+
* resume exactly once when the matching `signal()` call delivers data.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `waitFor` is the **receive** side of the signal coordination pair.
|
|
12
|
+
* The **send** side is `signal()`, which can be called from another
|
|
13
|
+
* workflow, a hook function, or externally via `MemFlow.Client.workflow.signal()`.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* const [signal1, signal2] = await Promise.all([
|
|
18
|
-
* MemFlow.workflow.waitFor<Record<string, any>>('signal1'),
|
|
19
|
-
* MemFlow.workflow.waitFor<Record<string, any>>('signal2')
|
|
20
|
-
* ]);
|
|
15
|
+
* On replay, `waitFor` returns the previously stored signal payload
|
|
16
|
+
* immediately (no actual suspension occurs).
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
* // Typical pattern with hook functions
|
|
24
|
-
* // In main workflow:
|
|
25
|
-
* await MemFlow.workflow.waitFor<ResponseType>('hook-complete');
|
|
18
|
+
* ## Examples
|
|
26
19
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { MemFlow } from '@hotmeshio/hotmesh';
|
|
29
22
|
*
|
|
30
|
-
*
|
|
23
|
+
* // Human-in-the-loop approval pattern
|
|
24
|
+
* export async function approvalWorkflow(orderId: string): Promise<boolean> {
|
|
25
|
+
* const { submitForReview } = MemFlow.workflow.proxyActivities<typeof activities>();
|
|
26
|
+
*
|
|
27
|
+
* await submitForReview(orderId);
|
|
28
|
+
*
|
|
29
|
+
* // Pause indefinitely until a human approves or rejects
|
|
30
|
+
* const decision = await MemFlow.workflow.waitFor<{ approved: boolean }>('approval');
|
|
31
|
+
*
|
|
32
|
+
* return decision.approved;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Later, from outside the workflow (e.g., an API handler):
|
|
36
|
+
* await client.workflow.signal('approval', { approved: true });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Fan-in: wait for multiple signals in parallel
|
|
41
|
+
* export async function gatherWorkflow(): Promise<[string, number]> {
|
|
42
|
+
* const [name, score] = await Promise.all([
|
|
43
|
+
* MemFlow.workflow.waitFor<string>('name-signal'),
|
|
44
|
+
* MemFlow.workflow.waitFor<number>('score-signal'),
|
|
45
|
+
* ]);
|
|
46
|
+
* return [name, score];
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // Paired with hook: spawn work and wait for the result
|
|
52
|
+
* export async function orchestrator(input: string): Promise<string> {
|
|
53
|
+
* const signalId = `result-${MemFlow.workflow.random()}`;
|
|
54
|
+
*
|
|
55
|
+
* // Spawn a hook that will signal back when done
|
|
56
|
+
* await MemFlow.workflow.hook({
|
|
57
|
+
* taskQueue: 'processors',
|
|
58
|
+
* workflowName: 'processItem',
|
|
59
|
+
* args: [input, signalId],
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* // Wait for the hook to signal completion
|
|
63
|
+
* return await MemFlow.workflow.waitFor<string>(signalId);
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @template T - The type of data expected in the signal payload.
|
|
31
68
|
* @param {string} signalId - A unique signal identifier shared by the sender and receiver.
|
|
32
69
|
* @returns {Promise<T>} The data payload associated with the received signal.
|
|
33
70
|
*/
|
|
@@ -45,7 +45,7 @@ declare abstract class StoreService<Provider extends ProviderClient, Transaction
|
|
|
45
45
|
jobId: string, appId: string, guidField: string, //
|
|
46
46
|
guidWeight: number, transaction?: ProviderTransaction): Promise<any>;
|
|
47
47
|
abstract getStatus(jobId: string, appId: string): Promise<number>;
|
|
48
|
-
abstract setStateNX(jobId: string, appId: string, status?: number, entity?: string): Promise<boolean>;
|
|
48
|
+
abstract setStateNX(jobId: string, appId: string, status?: number, entity?: string, transaction?: ProviderTransaction): Promise<boolean>;
|
|
49
49
|
abstract setState(state: StringAnyType, status: number | null, jobId: string, symbolNames: string[], dIds: StringStringType, transaction?: TransactionProvider): Promise<string>;
|
|
50
50
|
abstract getQueryState(jobId: string, fields: string[]): Promise<StringAnyType>;
|
|
51
51
|
abstract getState(jobId: string, consumes: Consumes, dIds: StringStringType): Promise<[StringAnyType, number] | undefined>;
|
|
@@ -103,7 +103,7 @@ declare class PostgresStoreService extends StoreService<ProviderClient, Provider
|
|
|
103
103
|
* `purposeful re-entry`.
|
|
104
104
|
*/
|
|
105
105
|
collateSynthetic(jobId: string, guid: string, amount: number, transaction?: ProviderTransaction): Promise<number>;
|
|
106
|
-
setStateNX(jobId: string, appId: string, status?: number, entity?: string): Promise<boolean>;
|
|
106
|
+
setStateNX(jobId: string, appId: string, status?: number, entity?: string, transaction?: ProviderTransaction): Promise<boolean>;
|
|
107
107
|
getSchema(activityId: string, appVersion: AppVID): Promise<ActivityType>;
|
|
108
108
|
getSchemas(appVersion: AppVID): Promise<Record<string, ActivityType>>;
|
|
109
109
|
setSchemas(schemas: Record<string, ActivityType>, appVersion: AppVID): Promise<any>;
|