@hotmeshio/hotmesh 0.7.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 (138) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/README.md +158 -38
  3. package/build/index.d.ts +1 -3
  4. package/build/index.js +1 -5
  5. package/build/modules/utils.js +3 -31
  6. package/build/package.json +63 -79
  7. package/build/services/activities/activity.d.ts +97 -9
  8. package/build/services/activities/activity.js +323 -86
  9. package/build/services/activities/await.d.ts +101 -0
  10. package/build/services/activities/await.js +103 -2
  11. package/build/services/activities/cycle.d.ts +82 -0
  12. package/build/services/activities/cycle.js +86 -8
  13. package/build/services/activities/hook.d.ts +144 -1
  14. package/build/services/activities/hook.js +162 -21
  15. package/build/services/activities/interrupt.d.ts +112 -0
  16. package/build/services/activities/interrupt.js +134 -29
  17. package/build/services/activities/signal.d.ts +111 -4
  18. package/build/services/activities/signal.js +136 -28
  19. package/build/services/activities/trigger.d.ts +56 -4
  20. package/build/services/activities/trigger.js +119 -35
  21. package/build/services/activities/worker.d.ts +107 -0
  22. package/build/services/activities/worker.js +109 -2
  23. package/build/services/collator/index.d.ts +116 -30
  24. package/build/services/collator/index.js +211 -115
  25. package/build/services/connector/factory.d.ts +1 -1
  26. package/build/services/connector/factory.js +1 -11
  27. package/build/services/engine/index.d.ts +22 -6
  28. package/build/services/engine/index.js +49 -18
  29. package/build/services/exporter/index.d.ts +2 -0
  30. package/build/services/exporter/index.js +1 -0
  31. package/build/services/hotmesh/index.d.ts +471 -236
  32. package/build/services/hotmesh/index.js +473 -238
  33. package/build/services/memflow/client.js +2 -2
  34. package/build/services/memflow/handle.js +1 -1
  35. package/build/services/memflow/index.d.ts +1 -1
  36. package/build/services/memflow/index.js +1 -1
  37. package/build/services/memflow/workflow/all.d.ts +28 -3
  38. package/build/services/memflow/workflow/all.js +28 -3
  39. package/build/services/memflow/workflow/context.d.ts +44 -1
  40. package/build/services/memflow/workflow/context.js +44 -1
  41. package/build/services/memflow/workflow/didRun.d.ts +23 -3
  42. package/build/services/memflow/workflow/didRun.js +23 -3
  43. package/build/services/memflow/workflow/emit.d.ts +43 -4
  44. package/build/services/memflow/workflow/emit.js +43 -4
  45. package/build/services/memflow/workflow/enrich.d.ts +32 -4
  46. package/build/services/memflow/workflow/enrich.js +32 -4
  47. package/build/services/memflow/workflow/entityMethods.d.ts +54 -7
  48. package/build/services/memflow/workflow/entityMethods.js +54 -7
  49. package/build/services/memflow/workflow/execChild.d.ts +96 -8
  50. package/build/services/memflow/workflow/execChild.js +96 -8
  51. package/build/services/memflow/workflow/execHook.d.ts +54 -39
  52. package/build/services/memflow/workflow/execHook.js +52 -38
  53. package/build/services/memflow/workflow/execHookBatch.d.ts +82 -29
  54. package/build/services/memflow/workflow/execHookBatch.js +80 -28
  55. package/build/services/memflow/workflow/hook.d.ts +68 -3
  56. package/build/services/memflow/workflow/hook.js +69 -4
  57. package/build/services/memflow/workflow/index.d.ts +65 -10
  58. package/build/services/memflow/workflow/index.js +65 -10
  59. package/build/services/memflow/workflow/interrupt.d.ts +50 -4
  60. package/build/services/memflow/workflow/interrupt.js +50 -4
  61. package/build/services/memflow/workflow/interruption.d.ts +49 -16
  62. package/build/services/memflow/workflow/interruption.js +49 -16
  63. package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +21 -4
  64. package/build/services/memflow/workflow/isSideEffectAllowed.js +21 -4
  65. package/build/services/memflow/workflow/proxyActivities.d.ts +70 -42
  66. package/build/services/memflow/workflow/proxyActivities.js +70 -42
  67. package/build/services/memflow/workflow/random.d.ts +33 -3
  68. package/build/services/memflow/workflow/random.js +33 -3
  69. package/build/services/memflow/workflow/searchMethods.d.ts +49 -2
  70. package/build/services/memflow/workflow/searchMethods.js +49 -2
  71. package/build/services/memflow/workflow/signal.d.ts +51 -22
  72. package/build/services/memflow/workflow/signal.js +52 -23
  73. package/build/services/memflow/workflow/sleepFor.d.ts +57 -18
  74. package/build/services/memflow/workflow/sleepFor.js +57 -18
  75. package/build/services/memflow/workflow/trace.d.ts +39 -6
  76. package/build/services/memflow/workflow/trace.js +39 -6
  77. package/build/services/memflow/workflow/waitFor.d.ts +55 -18
  78. package/build/services/memflow/workflow/waitFor.js +55 -18
  79. package/build/services/router/consumption/index.js +1 -1
  80. package/build/services/search/factory.js +1 -9
  81. package/build/services/store/factory.js +1 -9
  82. package/build/services/store/index.d.ts +6 -1
  83. package/build/services/store/providers/postgres/kvsql.d.ts +4 -0
  84. package/build/services/store/providers/postgres/kvsql.js +4 -0
  85. package/build/services/store/providers/postgres/kvtransaction.d.ts +2 -0
  86. package/build/services/store/providers/postgres/kvtransaction.js +23 -0
  87. package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +51 -0
  88. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +193 -1
  89. package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +4 -0
  90. package/build/services/store/providers/postgres/kvtypes/hash/index.js +6 -0
  91. package/build/services/store/providers/postgres/postgres.d.ts +21 -1
  92. package/build/services/store/providers/postgres/postgres.js +42 -4
  93. package/build/services/stream/factory.js +1 -17
  94. package/build/services/stream/providers/postgres/scout.js +2 -2
  95. package/build/services/sub/factory.js +1 -9
  96. package/build/services/sub/index.d.ts +1 -1
  97. package/build/services/sub/providers/postgres/postgres.d.ts +1 -1
  98. package/build/services/sub/providers/postgres/postgres.js +25 -10
  99. package/build/services/task/index.d.ts +1 -1
  100. package/build/services/task/index.js +2 -6
  101. package/build/services/telemetry/index.js +6 -0
  102. package/build/types/activity.d.ts +1 -1
  103. package/build/types/hotmesh.d.ts +1 -1
  104. package/build/types/index.d.ts +0 -1
  105. package/build/types/index.js +1 -4
  106. package/build/types/job.d.ts +1 -1
  107. package/build/types/memflow.d.ts +1 -1
  108. package/build/types/provider.d.ts +1 -1
  109. package/build/types/quorum.d.ts +2 -2
  110. package/build/vitest.config.d.ts +2 -0
  111. package/build/vitest.config.js +18 -0
  112. package/index.ts +0 -4
  113. package/package.json +63 -79
  114. package/vitest.config.ts +17 -0
  115. package/build/services/connector/providers/ioredis.d.ts +0 -9
  116. package/build/services/connector/providers/ioredis.js +0 -26
  117. package/build/services/connector/providers/redis.d.ts +0 -9
  118. package/build/services/connector/providers/redis.js +0 -38
  119. package/build/services/search/providers/redis/ioredis.d.ts +0 -23
  120. package/build/services/search/providers/redis/ioredis.js +0 -189
  121. package/build/services/search/providers/redis/redis.d.ts +0 -23
  122. package/build/services/search/providers/redis/redis.js +0 -202
  123. package/build/services/store/providers/redis/_base.d.ts +0 -137
  124. package/build/services/store/providers/redis/_base.js +0 -980
  125. package/build/services/store/providers/redis/ioredis.d.ts +0 -20
  126. package/build/services/store/providers/redis/ioredis.js +0 -190
  127. package/build/services/store/providers/redis/redis.d.ts +0 -18
  128. package/build/services/store/providers/redis/redis.js +0 -199
  129. package/build/services/stream/providers/redis/ioredis.d.ts +0 -61
  130. package/build/services/stream/providers/redis/ioredis.js +0 -272
  131. package/build/services/stream/providers/redis/redis.d.ts +0 -61
  132. package/build/services/stream/providers/redis/redis.js +0 -305
  133. package/build/services/sub/providers/redis/ioredis.d.ts +0 -20
  134. package/build/services/sub/providers/redis/ioredis.js +0 -161
  135. package/build/services/sub/providers/redis/redis.d.ts +0 -18
  136. package/build/services/sub/providers/redis/redis.js +0 -148
  137. package/build/types/redis.d.ts +0 -258
  138. package/build/types/redis.js +0 -11
@@ -3,31 +3,64 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.didInterrupt = void 0;
4
4
  const errors_1 = require("../../../modules/errors");
5
5
  /**
6
- * Checks if an error is a HotMesh reserved error type that indicates
7
- * a HotMesh interruption rather than a true error condition.
6
+ * Type guard that returns `true` if an error is a MemFlow engine
7
+ * control-flow signal rather than a genuine application error.
8
8
  *
9
- * When this returns true, you can safely return rethrow the error.
10
- * The workflow engine will handle the interruption automatically.
9
+ * MemFlow uses thrown errors internally to suspend workflow execution
10
+ * for durable operations like `sleepFor`, `waitFor`, `proxyActivities`,
11
+ * and `execChild`. These errors must be re-thrown (not swallowed) so
12
+ * the engine can persist state and schedule the next step.
13
+ *
14
+ * **Always use `didInterrupt` in `catch` blocks inside workflow
15
+ * functions** to avoid accidentally swallowing engine signals.
16
+ *
17
+ * ## Recognized Error Types
18
+ *
19
+ * `MemFlowChildError`, `MemFlowFatalError`, `MemFlowMaxedError`,
20
+ * `MemFlowProxyError`, `MemFlowRetryError`, `MemFlowSleepError`,
21
+ * `MemFlowTimeoutError`, `MemFlowWaitForError`, `MemFlowWaitForAllError`
22
+ *
23
+ * ## Examples
11
24
  *
12
- * @example
13
25
  * ```typescript
14
26
  * import { MemFlow } from '@hotmeshio/hotmesh';
15
27
  *
16
- * try {
17
- * await someWorkflowOperation();
18
- * } catch (error) {
19
- * // Check if this is a HotMesh interruption
20
- * if (MemFlow.workflow.didInterrupt(error)) {
21
- * // Rethrow the error
22
- * throw error;
28
+ * export async function safeWorkflow(): Promise<string> {
29
+ * const { riskyOperation } = MemFlow.workflow.proxyActivities<typeof activities>();
30
+ *
31
+ * try {
32
+ * return await riskyOperation();
33
+ * } catch (error) {
34
+ * // CRITICAL: re-throw engine signals
35
+ * if (MemFlow.workflow.didInterrupt(error)) {
36
+ * throw error;
37
+ * }
38
+ * // Handle real application errors
39
+ * return 'fallback-value';
23
40
  * }
24
- * // Handle actual error
25
- * console.error('Workflow failed:', error);
26
41
  * }
27
42
  * ```
28
43
  *
29
- * @param error - The error to check
30
- * @returns true if the error is a HotMesh interruption
44
+ * ```typescript
45
+ * // Common pattern in interceptors
46
+ * const interceptor: WorkflowInterceptor = {
47
+ * async execute(ctx, next) {
48
+ * try {
49
+ * return await next();
50
+ * } catch (error) {
51
+ * if (MemFlow.workflow.didInterrupt(error)) {
52
+ * throw error; // always re-throw engine signals
53
+ * }
54
+ * // Log and re-throw application errors
55
+ * console.error('Workflow failed:', error);
56
+ * throw error;
57
+ * }
58
+ * },
59
+ * };
60
+ * ```
61
+ *
62
+ * @param {Error} error - The error to check.
63
+ * @returns {boolean} `true` if the error is a MemFlow engine interruption signal.
31
64
  */
32
65
  function didInterrupt(error) {
33
66
  return (error instanceof errors_1.MemFlowChildError ||
@@ -1,10 +1,27 @@
1
1
  import { HotMesh } from './common';
2
2
  /**
3
- * Checks if a side-effect is allowed to run. This ensures certain actions
4
- * are executed exactly once.
3
+ * Guards side-effectful operations (`emit`, `signal`, `hook`, `trace`,
4
+ * `interrupt`) against duplicate execution during replay. Unlike
5
+ * `didRun()` (which replays stored results), this guard is for
6
+ * operations that don't produce a return value to cache — they simply
7
+ * must not run twice.
8
+ *
9
+ * ## Mechanism
10
+ *
11
+ * 1. Increments the execution counter to produce a `sessionId`.
12
+ * 2. If that `sessionId` already exists in the `replay` hash, the
13
+ * operation already ran in a previous execution → return `false`.
14
+ * 3. Otherwise, atomically increments a field on the job's backend
15
+ * HASH via `incrementFieldByFloat`. If the result is exactly `1`,
16
+ * this is the first worker to reach this point → return `true`.
17
+ * If `> 1`, a concurrent worker already executed it → return `false`.
18
+ *
19
+ * This provides **distributed idempotency** for side effects across
20
+ * replays and concurrent worker instances.
21
+ *
5
22
  * @private
6
23
  * @param {HotMesh} hotMeshClient - The HotMesh client.
7
- * @param {string} prefix - A unique prefix representing the action (e.g., 'trace', 'emit', etc.)
8
- * @returns {Promise<boolean>} True if the side effect can run, false otherwise.
24
+ * @param {string} prefix - Operation type (`'trace'`, `'emit'`, `'signal'`, `'hook'`, `'interrupt'`).
25
+ * @returns {Promise<boolean>} `true` if the side effect should execute, `false` if it already ran.
9
26
  */
10
27
  export declare function isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean>;
@@ -3,12 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isSideEffectAllowed = void 0;
4
4
  const common_1 = require("./common");
5
5
  /**
6
- * Checks if a side-effect is allowed to run. This ensures certain actions
7
- * are executed exactly once.
6
+ * Guards side-effectful operations (`emit`, `signal`, `hook`, `trace`,
7
+ * `interrupt`) against duplicate execution during replay. Unlike
8
+ * `didRun()` (which replays stored results), this guard is for
9
+ * operations that don't produce a return value to cache — they simply
10
+ * must not run twice.
11
+ *
12
+ * ## Mechanism
13
+ *
14
+ * 1. Increments the execution counter to produce a `sessionId`.
15
+ * 2. If that `sessionId` already exists in the `replay` hash, the
16
+ * operation already ran in a previous execution → return `false`.
17
+ * 3. Otherwise, atomically increments a field on the job's backend
18
+ * HASH via `incrementFieldByFloat`. If the result is exactly `1`,
19
+ * this is the first worker to reach this point → return `true`.
20
+ * If `> 1`, a concurrent worker already executed it → return `false`.
21
+ *
22
+ * This provides **distributed idempotency** for side effects across
23
+ * replays and concurrent worker instances.
24
+ *
8
25
  * @private
9
26
  * @param {HotMesh} hotMeshClient - The HotMesh client.
10
- * @param {string} prefix - A unique prefix representing the action (e.g., 'trace', 'emit', etc.)
11
- * @returns {Promise<boolean>} True if the side effect can run, false otherwise.
27
+ * @param {string} prefix - Operation type (`'trace'`, `'emit'`, `'signal'`, `'hook'`, `'interrupt'`).
28
+ * @returns {Promise<boolean>} `true` if the side effect should execute, `false` if it already ran.
12
29
  */
13
30
  async function isSideEffectAllowed(hotMeshClient, prefix) {
14
31
  const store = common_1.asyncLocalStorage.getStore();
@@ -11,81 +11,109 @@ declare function getProxyInterruptPayload(context: ReturnType<typeof getContext>
11
11
  */
12
12
  declare function wrapActivity<T>(activityName: string, options?: ActivityConfig): T;
13
13
  /**
14
- * Create proxies for activity functions with automatic retry and deterministic replay.
15
- * Activities execute via message queue, so they can run on different servers.
14
+ * Creates a typed proxy for calling activity functions with durable execution,
15
+ * automatic retry, and deterministic replay. This is the primary way to invoke
16
+ * side-effectful code (HTTP calls, database writes, file I/O) from within a
17
+ * workflow function.
16
18
  *
17
- * Without `taskQueue`, activities use the workflow's task queue (e.g., `my-workflow-activity`).
18
- * With `taskQueue`, activities use the specified queue (e.g., `payment-activity`).
19
+ * Activities execute on a **separate worker process** via message queue,
20
+ * isolating side effects from the deterministic workflow function. Each
21
+ * proxied call is assigned a unique execution index, and on replay the
22
+ * stored result is returned without re-executing the activity.
19
23
  *
20
- * The `activities` parameter is optional. If activities are already registered via
21
- * `registerActivityWorker()`, you can reference them by providing just the `taskQueue`
22
- * and a TypeScript interface.
24
+ * ## Routing
23
25
  *
24
- * @template ACT
25
- * @param {ActivityConfig} [options] - Activity configuration
26
- * @param {any} [options.activities] - (Optional) Activity functions to register inline
27
- * @param {string} [options.taskQueue] - (Optional) Task queue name (without `-activity` suffix)
28
- * @param {object} [options.retryPolicy] - Retry configuration
29
- * @returns {ProxyType<ACT>} Proxy for calling activities with durability and retry
26
+ * - **Default**: Activities route to `{workflowTaskQueue}-activity`.
27
+ * - **Explicit `taskQueue`**: Activities route to `{taskQueue}-activity`,
28
+ * enabling shared/global activity worker pools across workflows.
29
+ *
30
+ * ## Retry Policy
31
+ *
32
+ * | Option | Default | Description |
33
+ * |----------------------|---------|-------------|
34
+ * | `maximumAttempts` | 50 | Max retries before the activity is marked as failed |
35
+ * | `backoffCoefficient` | 2 | Exponential backoff multiplier |
36
+ * | `maximumInterval` | `'5m'` | Cap on delay between retries |
37
+ * | `throwOnError` | `true` | Throw on activity failure (set `false` to return the error) |
38
+ *
39
+ * ## Examples
30
40
  *
31
- * @example
32
41
  * ```typescript
33
- * // Inline registration (activities in same codebase)
34
- * const activities = MemFlow.workflow.proxyActivities<typeof activities>({
35
- * activities: { processData, validateData },
36
- * retryPolicy: { maximumAttempts: 3 }
37
- * });
42
+ * import { MemFlow } from '@hotmeshio/hotmesh';
43
+ * import * as activities from './activities';
44
+ *
45
+ * // Standard pattern: register and proxy activities inline
46
+ * export async function orderWorkflow(orderId: string): Promise<string> {
47
+ * const { validateOrder, chargePayment, sendConfirmation } =
48
+ * MemFlow.workflow.proxyActivities<typeof activities>({
49
+ * activities,
50
+ * retryPolicy: {
51
+ * maximumAttempts: 3,
52
+ * backoffCoefficient: 2,
53
+ * maximumInterval: '30s',
54
+ * },
55
+ * });
38
56
  *
39
- * await activities.processData('input');
57
+ * await validateOrder(orderId);
58
+ * const receipt = await chargePayment(orderId);
59
+ * await sendConfirmation(orderId, receipt);
60
+ * return receipt;
61
+ * }
40
62
  * ```
41
63
  *
42
- * @example
43
64
  * ```typescript
44
- * // Reference pre-registered activities (can be on different server)
65
+ * // Remote activities: reference a pre-registered worker pool by taskQueue
45
66
  * interface PaymentActivities {
46
67
  * processPayment: (amount: number) => Promise<string>;
47
- * sendEmail: (to: string, subject: string) => Promise<void>;
68
+ * refundPayment: (txId: string) => Promise<void>;
48
69
  * }
49
70
  *
50
- * const { processPayment, sendEmail } =
51
- * MemFlow.workflow.proxyActivities<PaymentActivities>({
52
- * taskQueue: 'payment',
53
- * retryPolicy: { maximumAttempts: 3 }
54
- * });
71
+ * export async function refundWorkflow(txId: string): Promise<void> {
72
+ * const { refundPayment } =
73
+ * MemFlow.workflow.proxyActivities<PaymentActivities>({
74
+ * taskQueue: 'payments',
75
+ * retryPolicy: { maximumAttempts: 5 },
76
+ * });
55
77
  *
56
- * const result = await processPayment(100.00);
57
- * await sendEmail('user@example.com', 'Payment processed');
78
+ * await refundPayment(txId);
79
+ * }
58
80
  * ```
59
81
  *
60
- * @example
61
82
  * ```typescript
62
- * // Shared activities in interceptor
63
- * const interceptor: WorkflowInterceptor = {
83
+ * // Interceptor with shared activity pool
84
+ * const auditInterceptor: WorkflowInterceptor = {
64
85
  * async execute(ctx, next) {
65
86
  * const { auditLog } = MemFlow.workflow.proxyActivities<{
66
87
  * auditLog: (id: string, action: string) => Promise<void>;
67
88
  * }>({
68
- * taskQueue: 'shared',
69
- * retryPolicy: { maximumAttempts: 3 }
89
+ * taskQueue: 'shared-audit',
90
+ * retryPolicy: { maximumAttempts: 3 },
70
91
  * });
71
92
  *
72
93
  * await auditLog(ctx.get('workflowId'), 'started');
73
94
  * const result = await next();
74
95
  * await auditLog(ctx.get('workflowId'), 'completed');
75
96
  * return result;
76
- * }
97
+ * },
77
98
  * };
78
99
  * ```
79
100
  *
80
- * @example
81
101
  * ```typescript
82
- * // Custom task queue for specific activities
83
- * const highPriority = MemFlow.workflow.proxyActivities<typeof activities>({
84
- * activities: { criticalProcess },
85
- * taskQueue: 'high-priority',
86
- * retryPolicy: { maximumAttempts: 5 }
102
+ * // Graceful error handling (no throw)
103
+ * const { riskyOperation } = MemFlow.workflow.proxyActivities<typeof activities>({
104
+ * activities,
105
+ * retryPolicy: { maximumAttempts: 1, throwOnError: false },
87
106
  * });
107
+ *
108
+ * const result = await riskyOperation();
109
+ * if (result instanceof Error) {
110
+ * // handle gracefully
111
+ * }
88
112
  * ```
113
+ *
114
+ * @template ACT - The activity type map (use `typeof activities` for inline registration).
115
+ * @param {ActivityConfig} [options] - Activity configuration including retry policy and routing.
116
+ * @returns {ProxyType<ACT>} A typed proxy object mapping activity names to their durable wrappers.
89
117
  */
90
118
  export declare function proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT>;
91
119
  export { wrapActivity, getProxyInterruptPayload };
@@ -81,81 +81,109 @@ function wrapActivity(activityName, options) {
81
81
  }
82
82
  exports.wrapActivity = wrapActivity;
83
83
  /**
84
- * Create proxies for activity functions with automatic retry and deterministic replay.
85
- * Activities execute via message queue, so they can run on different servers.
84
+ * Creates a typed proxy for calling activity functions with durable execution,
85
+ * automatic retry, and deterministic replay. This is the primary way to invoke
86
+ * side-effectful code (HTTP calls, database writes, file I/O) from within a
87
+ * workflow function.
86
88
  *
87
- * Without `taskQueue`, activities use the workflow's task queue (e.g., `my-workflow-activity`).
88
- * With `taskQueue`, activities use the specified queue (e.g., `payment-activity`).
89
+ * Activities execute on a **separate worker process** via message queue,
90
+ * isolating side effects from the deterministic workflow function. Each
91
+ * proxied call is assigned a unique execution index, and on replay the
92
+ * stored result is returned without re-executing the activity.
89
93
  *
90
- * The `activities` parameter is optional. If activities are already registered via
91
- * `registerActivityWorker()`, you can reference them by providing just the `taskQueue`
92
- * and a TypeScript interface.
94
+ * ## Routing
93
95
  *
94
- * @template ACT
95
- * @param {ActivityConfig} [options] - Activity configuration
96
- * @param {any} [options.activities] - (Optional) Activity functions to register inline
97
- * @param {string} [options.taskQueue] - (Optional) Task queue name (without `-activity` suffix)
98
- * @param {object} [options.retryPolicy] - Retry configuration
99
- * @returns {ProxyType<ACT>} Proxy for calling activities with durability and retry
96
+ * - **Default**: Activities route to `{workflowTaskQueue}-activity`.
97
+ * - **Explicit `taskQueue`**: Activities route to `{taskQueue}-activity`,
98
+ * enabling shared/global activity worker pools across workflows.
99
+ *
100
+ * ## Retry Policy
101
+ *
102
+ * | Option | Default | Description |
103
+ * |----------------------|---------|-------------|
104
+ * | `maximumAttempts` | 50 | Max retries before the activity is marked as failed |
105
+ * | `backoffCoefficient` | 2 | Exponential backoff multiplier |
106
+ * | `maximumInterval` | `'5m'` | Cap on delay between retries |
107
+ * | `throwOnError` | `true` | Throw on activity failure (set `false` to return the error) |
108
+ *
109
+ * ## Examples
100
110
  *
101
- * @example
102
111
  * ```typescript
103
- * // Inline registration (activities in same codebase)
104
- * const activities = MemFlow.workflow.proxyActivities<typeof activities>({
105
- * activities: { processData, validateData },
106
- * retryPolicy: { maximumAttempts: 3 }
107
- * });
112
+ * import { MemFlow } from '@hotmeshio/hotmesh';
113
+ * import * as activities from './activities';
114
+ *
115
+ * // Standard pattern: register and proxy activities inline
116
+ * export async function orderWorkflow(orderId: string): Promise<string> {
117
+ * const { validateOrder, chargePayment, sendConfirmation } =
118
+ * MemFlow.workflow.proxyActivities<typeof activities>({
119
+ * activities,
120
+ * retryPolicy: {
121
+ * maximumAttempts: 3,
122
+ * backoffCoefficient: 2,
123
+ * maximumInterval: '30s',
124
+ * },
125
+ * });
108
126
  *
109
- * await activities.processData('input');
127
+ * await validateOrder(orderId);
128
+ * const receipt = await chargePayment(orderId);
129
+ * await sendConfirmation(orderId, receipt);
130
+ * return receipt;
131
+ * }
110
132
  * ```
111
133
  *
112
- * @example
113
134
  * ```typescript
114
- * // Reference pre-registered activities (can be on different server)
135
+ * // Remote activities: reference a pre-registered worker pool by taskQueue
115
136
  * interface PaymentActivities {
116
137
  * processPayment: (amount: number) => Promise<string>;
117
- * sendEmail: (to: string, subject: string) => Promise<void>;
138
+ * refundPayment: (txId: string) => Promise<void>;
118
139
  * }
119
140
  *
120
- * const { processPayment, sendEmail } =
121
- * MemFlow.workflow.proxyActivities<PaymentActivities>({
122
- * taskQueue: 'payment',
123
- * retryPolicy: { maximumAttempts: 3 }
124
- * });
141
+ * export async function refundWorkflow(txId: string): Promise<void> {
142
+ * const { refundPayment } =
143
+ * MemFlow.workflow.proxyActivities<PaymentActivities>({
144
+ * taskQueue: 'payments',
145
+ * retryPolicy: { maximumAttempts: 5 },
146
+ * });
125
147
  *
126
- * const result = await processPayment(100.00);
127
- * await sendEmail('user@example.com', 'Payment processed');
148
+ * await refundPayment(txId);
149
+ * }
128
150
  * ```
129
151
  *
130
- * @example
131
152
  * ```typescript
132
- * // Shared activities in interceptor
133
- * const interceptor: WorkflowInterceptor = {
153
+ * // Interceptor with shared activity pool
154
+ * const auditInterceptor: WorkflowInterceptor = {
134
155
  * async execute(ctx, next) {
135
156
  * const { auditLog } = MemFlow.workflow.proxyActivities<{
136
157
  * auditLog: (id: string, action: string) => Promise<void>;
137
158
  * }>({
138
- * taskQueue: 'shared',
139
- * retryPolicy: { maximumAttempts: 3 }
159
+ * taskQueue: 'shared-audit',
160
+ * retryPolicy: { maximumAttempts: 3 },
140
161
  * });
141
162
  *
142
163
  * await auditLog(ctx.get('workflowId'), 'started');
143
164
  * const result = await next();
144
165
  * await auditLog(ctx.get('workflowId'), 'completed');
145
166
  * return result;
146
- * }
167
+ * },
147
168
  * };
148
169
  * ```
149
170
  *
150
- * @example
151
171
  * ```typescript
152
- * // Custom task queue for specific activities
153
- * const highPriority = MemFlow.workflow.proxyActivities<typeof activities>({
154
- * activities: { criticalProcess },
155
- * taskQueue: 'high-priority',
156
- * retryPolicy: { maximumAttempts: 5 }
172
+ * // Graceful error handling (no throw)
173
+ * const { riskyOperation } = MemFlow.workflow.proxyActivities<typeof activities>({
174
+ * activities,
175
+ * retryPolicy: { maximumAttempts: 1, throwOnError: false },
157
176
  * });
177
+ *
178
+ * const result = await riskyOperation();
179
+ * if (result instanceof Error) {
180
+ * // handle gracefully
181
+ * }
158
182
  * ```
183
+ *
184
+ * @template ACT - The activity type map (use `typeof activities` for inline registration).
185
+ * @param {ActivityConfig} [options] - Activity configuration including retry policy and routing.
186
+ * @returns {ProxyType<ACT>} A typed proxy object mapping activity names to their durable wrappers.
159
187
  */
160
188
  function proxyActivities(options) {
161
189
  // Register activities if provided (optional - may already be registered remotely)
@@ -1,6 +1,36 @@
1
1
  /**
2
- * Returns a deterministic random number between 0 and 1. The number is derived from the
3
- * current execution index, ensuring deterministic replay.
4
- * @returns {number} A deterministic pseudo-random number between 0 and 1.
2
+ * Returns a deterministic pseudo-random number between 0 and 1. The value
3
+ * is derived from the current execution counter, so it produces the
4
+ * **same result** on every replay of the workflow at this execution point.
5
+ *
6
+ * Use this instead of `Math.random()` inside workflow functions.
7
+ * `Math.random()` is non-deterministic and would break replay correctness.
8
+ *
9
+ * ## Examples
10
+ *
11
+ * ```typescript
12
+ * import { MemFlow } from '@hotmeshio/hotmesh';
13
+ *
14
+ * // Generate a deterministic unique suffix
15
+ * export async function uniqueWorkflow(): Promise<string> {
16
+ * const suffix = Math.floor(MemFlow.workflow.random() * 10000);
17
+ * return `item-${suffix}`;
18
+ * }
19
+ * ```
20
+ *
21
+ * ```typescript
22
+ * // A/B test routing (deterministic per workflow execution)
23
+ * export async function experimentWorkflow(userId: string): Promise<string> {
24
+ * const { variantA, variantB } = MemFlow.workflow.proxyActivities<typeof activities>();
25
+ *
26
+ * if (MemFlow.workflow.random() < 0.5) {
27
+ * return await variantA(userId);
28
+ * } else {
29
+ * return await variantB(userId);
30
+ * }
31
+ * }
32
+ * ```
33
+ *
34
+ * @returns {number} A deterministic pseudo-random number in [0, 1).
5
35
  */
6
36
  export declare function random(): number;
@@ -3,9 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.random = void 0;
4
4
  const common_1 = require("./common");
5
5
  /**
6
- * Returns a deterministic random number between 0 and 1. The number is derived from the
7
- * current execution index, ensuring deterministic replay.
8
- * @returns {number} A deterministic pseudo-random number between 0 and 1.
6
+ * Returns a deterministic pseudo-random number between 0 and 1. The value
7
+ * is derived from the current execution counter, so it produces the
8
+ * **same result** on every replay of the workflow at this execution point.
9
+ *
10
+ * Use this instead of `Math.random()` inside workflow functions.
11
+ * `Math.random()` is non-deterministic and would break replay correctness.
12
+ *
13
+ * ## Examples
14
+ *
15
+ * ```typescript
16
+ * import { MemFlow } from '@hotmeshio/hotmesh';
17
+ *
18
+ * // Generate a deterministic unique suffix
19
+ * export async function uniqueWorkflow(): Promise<string> {
20
+ * const suffix = Math.floor(MemFlow.workflow.random() * 10000);
21
+ * return `item-${suffix}`;
22
+ * }
23
+ * ```
24
+ *
25
+ * ```typescript
26
+ * // A/B test routing (deterministic per workflow execution)
27
+ * export async function experimentWorkflow(userId: string): Promise<string> {
28
+ * const { variantA, variantB } = MemFlow.workflow.proxyActivities<typeof activities>();
29
+ *
30
+ * if (MemFlow.workflow.random() < 0.5) {
31
+ * return await variantA(userId);
32
+ * } else {
33
+ * return await variantB(userId);
34
+ * }
35
+ * }
36
+ * ```
37
+ *
38
+ * @returns {number} A deterministic pseudo-random number in [0, 1).
9
39
  */
10
40
  function random() {
11
41
  const store = common_1.asyncLocalStorage.getStore();
@@ -1,6 +1,53 @@
1
1
  import { Search } from './common';
2
2
  /**
3
- * Returns a search session handle for interacting with the workflow's HASH storage.
4
- * @returns {Promise<Search>} A search session for workflow data.
3
+ * Returns a `Search` session handle for reading and writing key-value data
4
+ * on the workflow's backend HASH record. Search fields are flat string
5
+ * key-value pairs stored alongside the job state, making them queryable
6
+ * via `MemFlow.Client.workflow.search()` (FT.SEARCH).
7
+ *
8
+ * Each call produces a unique session ID tied to the deterministic
9
+ * execution counter, ensuring correct replay behavior.
10
+ *
11
+ * Use `search()` for flat, indexable key-value data. For structured
12
+ * JSON documents, use `entity()` instead.
13
+ *
14
+ * ## Examples
15
+ *
16
+ * ```typescript
17
+ * import { MemFlow } from '@hotmeshio/hotmesh';
18
+ *
19
+ * export async function orderWorkflow(orderId: string): Promise<void> {
20
+ * const search = await MemFlow.workflow.search();
21
+ *
22
+ * // Write searchable fields
23
+ * await search.set({
24
+ * orderId,
25
+ * status: 'processing',
26
+ * createdAt: new Date().toISOString(),
27
+ * });
28
+ *
29
+ * const { processOrder } = MemFlow.workflow.proxyActivities<typeof activities>();
30
+ * await processOrder(orderId);
31
+ *
32
+ * // Update status
33
+ * await search.set({ status: 'completed' });
34
+ *
35
+ * // Read a field back
36
+ * const status = await search.get('status');
37
+ * }
38
+ * ```
39
+ *
40
+ * ```typescript
41
+ * // Increment a numeric counter
42
+ * export async function counterWorkflow(): Promise<number> {
43
+ * const search = await MemFlow.workflow.search();
44
+ * await search.set({ count: '0' });
45
+ * await search.incr('count', 1);
46
+ * await search.incr('count', 1);
47
+ * return Number(await search.get('count')); // 2
48
+ * }
49
+ * ```
50
+ *
51
+ * @returns {Promise<Search>} A search session scoped to the current workflow job.
5
52
  */
6
53
  export declare function search(): Promise<Search>;
@@ -3,8 +3,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.search = void 0;
4
4
  const common_1 = require("./common");
5
5
  /**
6
- * Returns a search session handle for interacting with the workflow's HASH storage.
7
- * @returns {Promise<Search>} A search session for workflow data.
6
+ * Returns a `Search` session handle for reading and writing key-value data
7
+ * on the workflow's backend HASH record. Search fields are flat string
8
+ * key-value pairs stored alongside the job state, making them queryable
9
+ * via `MemFlow.Client.workflow.search()` (FT.SEARCH).
10
+ *
11
+ * Each call produces a unique session ID tied to the deterministic
12
+ * execution counter, ensuring correct replay behavior.
13
+ *
14
+ * Use `search()` for flat, indexable key-value data. For structured
15
+ * JSON documents, use `entity()` instead.
16
+ *
17
+ * ## Examples
18
+ *
19
+ * ```typescript
20
+ * import { MemFlow } from '@hotmeshio/hotmesh';
21
+ *
22
+ * export async function orderWorkflow(orderId: string): Promise<void> {
23
+ * const search = await MemFlow.workflow.search();
24
+ *
25
+ * // Write searchable fields
26
+ * await search.set({
27
+ * orderId,
28
+ * status: 'processing',
29
+ * createdAt: new Date().toISOString(),
30
+ * });
31
+ *
32
+ * const { processOrder } = MemFlow.workflow.proxyActivities<typeof activities>();
33
+ * await processOrder(orderId);
34
+ *
35
+ * // Update status
36
+ * await search.set({ status: 'completed' });
37
+ *
38
+ * // Read a field back
39
+ * const status = await search.get('status');
40
+ * }
41
+ * ```
42
+ *
43
+ * ```typescript
44
+ * // Increment a numeric counter
45
+ * export async function counterWorkflow(): Promise<number> {
46
+ * const search = await MemFlow.workflow.search();
47
+ * await search.set({ count: '0' });
48
+ * await search.incr('count', 1);
49
+ * await search.incr('count', 1);
50
+ * return Number(await search.get('count')); // 2
51
+ * }
52
+ * ```
53
+ *
54
+ * @returns {Promise<Search>} A search session scoped to the current workflow job.
8
55
  */
9
56
  async function search() {
10
57
  const store = common_1.asyncLocalStorage.getStore();