@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.
Files changed (86) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/README.md +158 -38
  3. package/build/package.json +62 -67
  4. package/build/services/activities/activity.d.ts +58 -7
  5. package/build/services/activities/activity.js +66 -37
  6. package/build/services/activities/await.d.ts +101 -0
  7. package/build/services/activities/await.js +101 -0
  8. package/build/services/activities/cycle.d.ts +82 -0
  9. package/build/services/activities/cycle.js +86 -8
  10. package/build/services/activities/hook.d.ts +139 -1
  11. package/build/services/activities/hook.js +140 -2
  12. package/build/services/activities/interrupt.d.ts +112 -0
  13. package/build/services/activities/interrupt.js +118 -5
  14. package/build/services/activities/signal.d.ts +108 -3
  15. package/build/services/activities/signal.js +113 -8
  16. package/build/services/activities/trigger.d.ts +56 -4
  17. package/build/services/activities/trigger.js +119 -35
  18. package/build/services/activities/worker.d.ts +107 -0
  19. package/build/services/activities/worker.js +107 -0
  20. package/build/services/collator/index.d.ts +3 -15
  21. package/build/services/collator/index.js +7 -34
  22. package/build/services/engine/index.d.ts +18 -2
  23. package/build/services/engine/index.js +14 -4
  24. package/build/services/exporter/index.d.ts +2 -0
  25. package/build/services/exporter/index.js +1 -0
  26. package/build/services/hotmesh/index.d.ts +471 -236
  27. package/build/services/hotmesh/index.js +473 -238
  28. package/build/services/memflow/client.js +2 -2
  29. package/build/services/memflow/handle.js +1 -1
  30. package/build/services/memflow/index.d.ts +1 -1
  31. package/build/services/memflow/index.js +1 -1
  32. package/build/services/memflow/workflow/all.d.ts +28 -3
  33. package/build/services/memflow/workflow/all.js +28 -3
  34. package/build/services/memflow/workflow/context.d.ts +44 -1
  35. package/build/services/memflow/workflow/context.js +44 -1
  36. package/build/services/memflow/workflow/didRun.d.ts +23 -3
  37. package/build/services/memflow/workflow/didRun.js +23 -3
  38. package/build/services/memflow/workflow/emit.d.ts +43 -4
  39. package/build/services/memflow/workflow/emit.js +43 -4
  40. package/build/services/memflow/workflow/enrich.d.ts +32 -4
  41. package/build/services/memflow/workflow/enrich.js +32 -4
  42. package/build/services/memflow/workflow/entityMethods.d.ts +54 -7
  43. package/build/services/memflow/workflow/entityMethods.js +54 -7
  44. package/build/services/memflow/workflow/execChild.d.ts +96 -8
  45. package/build/services/memflow/workflow/execChild.js +96 -8
  46. package/build/services/memflow/workflow/execHook.d.ts +54 -39
  47. package/build/services/memflow/workflow/execHook.js +52 -38
  48. package/build/services/memflow/workflow/execHookBatch.d.ts +82 -29
  49. package/build/services/memflow/workflow/execHookBatch.js +80 -28
  50. package/build/services/memflow/workflow/hook.d.ts +68 -3
  51. package/build/services/memflow/workflow/hook.js +69 -4
  52. package/build/services/memflow/workflow/index.d.ts +65 -10
  53. package/build/services/memflow/workflow/index.js +65 -10
  54. package/build/services/memflow/workflow/interrupt.d.ts +50 -4
  55. package/build/services/memflow/workflow/interrupt.js +50 -4
  56. package/build/services/memflow/workflow/interruption.d.ts +49 -16
  57. package/build/services/memflow/workflow/interruption.js +49 -16
  58. package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +21 -4
  59. package/build/services/memflow/workflow/isSideEffectAllowed.js +21 -4
  60. package/build/services/memflow/workflow/proxyActivities.d.ts +70 -42
  61. package/build/services/memflow/workflow/proxyActivities.js +70 -42
  62. package/build/services/memflow/workflow/random.d.ts +33 -3
  63. package/build/services/memflow/workflow/random.js +33 -3
  64. package/build/services/memflow/workflow/searchMethods.d.ts +49 -2
  65. package/build/services/memflow/workflow/searchMethods.js +49 -2
  66. package/build/services/memflow/workflow/signal.d.ts +51 -22
  67. package/build/services/memflow/workflow/signal.js +52 -23
  68. package/build/services/memflow/workflow/sleepFor.d.ts +57 -18
  69. package/build/services/memflow/workflow/sleepFor.js +57 -18
  70. package/build/services/memflow/workflow/trace.d.ts +39 -6
  71. package/build/services/memflow/workflow/trace.js +39 -6
  72. package/build/services/memflow/workflow/waitFor.d.ts +55 -18
  73. package/build/services/memflow/workflow/waitFor.js +55 -18
  74. package/build/services/store/index.d.ts +1 -1
  75. package/build/services/store/providers/postgres/postgres.d.ts +1 -1
  76. package/build/services/store/providers/postgres/postgres.js +4 -3
  77. package/build/services/telemetry/index.js +6 -0
  78. package/build/types/activity.d.ts +1 -1
  79. package/build/types/hotmesh.d.ts +1 -1
  80. package/build/types/job.d.ts +1 -1
  81. package/build/types/memflow.d.ts +1 -1
  82. package/build/types/quorum.d.ts +2 -2
  83. package/build/vitest.config.d.ts +2 -0
  84. package/build/vitest.config.js +18 -0
  85. package/package.json +62 -67
  86. package/vitest.config.ts +17 -0
@@ -1,29 +1,58 @@
1
1
  /**
2
- * Sends a signal payload to any paused workflow thread awaiting this signal.
3
- * This method is commonly used to coordinate between workflows, hook functions,
4
- * and external events.
5
- *
6
- * @example
7
- * // Basic usage - send a simple signal with data
8
- * await MemFlow.workflow.signal('signal-id', { name: 'WarmMash' });
9
- *
10
- * @example
11
- * // Hook function signaling completion
12
- * export async function exampleHook(name: string): Promise<void> {
13
- * const result = await processData(name);
14
- * await MemFlow.workflow.signal('hook-complete', { data: result });
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
- * @example
18
- * // Signal with complex data structure
19
- * await MemFlow.workflow.signal('process-complete', {
20
- * status: 'success',
21
- * data: { id: 123, name: 'test' },
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 send with the signal.
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 any paused workflow thread awaiting this signal.
8
- * This method is commonly used to coordinate between workflows, hook functions,
9
- * and external events.
10
- *
11
- * @example
12
- * // Basic usage - send a simple signal with data
13
- * await MemFlow.workflow.signal('signal-id', { name: 'WarmMash' });
14
- *
15
- * @example
16
- * // Hook function signaling completion
17
- * export async function exampleHook(name: string): Promise<void> {
18
- * const result = await processData(name);
19
- * await MemFlow.workflow.signal('hook-complete', { data: result });
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
- * @example
23
- * // Signal with complex data structure
24
- * await MemFlow.workflow.signal('process-complete', {
25
- * status: 'success',
26
- * data: { id: 123, name: 'test' },
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 send with the signal.
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.hook(`${namespace}.wfs.signal`, {
73
+ return await hotMeshClient.signal(`${namespace}.wfs.signal`, {
45
74
  id: signalId,
46
75
  data,
47
76
  });
@@ -1,24 +1,63 @@
1
1
  /**
2
- * Sleeps the workflow for a specified duration, deterministically.
3
- * On replay, it will not actually sleep again, but resume after sleep.
4
- *
5
- * @example
6
- * // Basic usage - sleep for a specific duration
7
- * await MemFlow.workflow.sleepFor('2 seconds');
8
- *
9
- * @example
10
- * // Using with Promise.all for parallel operations
11
- * const [greeting, timeInSeconds] = await Promise.all([
12
- * someActivity(name),
13
- * MemFlow.workflow.sleepFor('1 second')
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
- * @example
17
- * // Multiple sequential sleeps
18
- * await MemFlow.workflow.sleepFor('1 seconds'); // First pause
19
- * await MemFlow.workflow.sleepFor('2 seconds'); // Second pause
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 (e.g., '1m', '2 hours', '30 seconds').
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
- * Sleeps the workflow for a specified duration, deterministically.
8
- * On replay, it will not actually sleep again, but resume after sleep.
9
- *
10
- * @example
11
- * // Basic usage - sleep for a specific duration
12
- * await MemFlow.workflow.sleepFor('2 seconds');
13
- *
14
- * @example
15
- * // Using with Promise.all for parallel operations
16
- * const [greeting, timeInSeconds] = await Promise.all([
17
- * someActivity(name),
18
- * MemFlow.workflow.sleepFor('1 second')
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
- * @example
22
- * // Multiple sequential sleeps
23
- * await MemFlow.workflow.sleepFor('1 seconds'); // First pause
24
- * await MemFlow.workflow.sleepFor('2 seconds'); // Second pause
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 (e.g., '1m', '2 hours', '30 seconds').
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
- * Executes a distributed trace, outputting the provided attributes
4
- * to the telemetry sink (e.g. OpenTelemetry).
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
- * This trace will only run once per workflow execution by default.
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
- * @param {StringScalarType} attributes - Key-value attributes to attach to the trace.
9
- * @param {{ once: boolean }} [config={ once: true }] - If `once` is true, trace only runs once.
10
- * @returns {Promise<boolean>} True if tracing succeeded, otherwise false.
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
- * Executes a distributed trace, outputting the provided attributes
9
- * to the telemetry sink (e.g. OpenTelemetry).
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
- * This trace will only run once per workflow execution by default.
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
- * @param {StringScalarType} attributes - Key-value attributes to attach to the trace.
14
- * @param {{ once: boolean }} [config={ once: true }] - If `once` is true, trace only runs once.
15
- * @returns {Promise<boolean>} True if tracing succeeded, otherwise false.
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
- * This method is commonly used to coordinate between the main workflow and hook functions,
4
- * or to wait for external events.
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
- * @example
7
- * // Basic usage - wait for a single signal
8
- * const payload = await MemFlow.workflow.waitFor<PayloadType>('abcdefg');
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
- * @example
11
- * // Wait for multiple signals in parallel
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
- * @example
18
- * // Typical pattern with hook functions
19
- * // In main workflow:
20
- * await MemFlow.workflow.waitFor<ResponseType>('hook-complete');
13
+ * ## Examples
21
14
  *
22
- * // In hook function:
23
- * await MemFlow.workflow.signal('hook-complete', { data: result });
15
+ * ```typescript
16
+ * import { MemFlow } from '@hotmeshio/hotmesh';
24
17
  *
25
- * @template T - The type of data expected in the signal payload
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
- * This method is commonly used to coordinate between the main workflow and hook functions,
9
- * or to wait for external events.
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
- * @example
12
- * // Basic usage - wait for a single signal
13
- * const payload = await MemFlow.workflow.waitFor<PayloadType>('abcdefg');
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
- * @example
16
- * // Wait for multiple signals in parallel
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
- * @example
23
- * // Typical pattern with hook functions
24
- * // In main workflow:
25
- * await MemFlow.workflow.waitFor<ResponseType>('hook-complete');
18
+ * ## Examples
26
19
  *
27
- * // In hook function:
28
- * await MemFlow.workflow.signal('hook-complete', { data: result });
20
+ * ```typescript
21
+ * import { MemFlow } from '@hotmeshio/hotmesh';
29
22
  *
30
- * @template T - The type of data expected in the signal payload
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>;