@hotmeshio/hotmesh 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/package.json +5 -4
- package/build/services/dba/index.d.ts +52 -18
- package/build/services/dba/index.js +118 -29
- package/build/services/durable/exporter.d.ts +60 -3
- package/build/services/durable/exporter.js +430 -2
- package/build/services/durable/handle.d.ts +12 -1
- package/build/services/durable/handle.js +13 -0
- package/build/services/durable/index.d.ts +31 -2
- package/build/services/durable/index.js +31 -2
- package/build/services/durable/interceptor.d.ts +73 -0
- package/build/services/durable/interceptor.js +73 -0
- package/build/services/durable/workflow/proxyActivities.js +33 -29
- package/build/services/store/providers/postgres/kvtables.js +6 -1
- package/build/types/dba.d.ts +31 -5
- package/build/types/durable.d.ts +30 -12
- package/build/types/exporter.d.ts +127 -0
- package/build/types/index.d.ts +1 -1
- package/package.json +5 -4
- /package/{vitest.config.ts → vitest.config.mts} +0 -0
|
@@ -183,6 +183,79 @@ exports.InterceptorService = void 0;
|
|
|
183
183
|
* }
|
|
184
184
|
* };
|
|
185
185
|
* ```
|
|
186
|
+
*
|
|
187
|
+
* ## Activity Interceptors
|
|
188
|
+
*
|
|
189
|
+
* Activity interceptors wrap individual proxied activity calls, supporting
|
|
190
|
+
* both **before** and **after** phases. The before phase receives the activity
|
|
191
|
+
* input (and can modify `activityCtx.args`). The after phase receives the
|
|
192
|
+
* activity output as the return value of `next()`.
|
|
193
|
+
*
|
|
194
|
+
* This enables patterns like publishing activity results to an external
|
|
195
|
+
* system (e.g., SNS, audit log) without modifying the workflow itself.
|
|
196
|
+
*
|
|
197
|
+
* **Important:** The after-phase proxy activity calls go through the same
|
|
198
|
+
* interceptor chain. Guard against recursion by checking `activityCtx.activityName`
|
|
199
|
+
* to skip the interceptor's own calls.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* import { Durable } from '@hotmeshio/hotmesh';
|
|
204
|
+
* import type { ActivityInterceptor } from '@hotmeshio/hotmesh/types/durable';
|
|
205
|
+
* import * as activities from './activities';
|
|
206
|
+
*
|
|
207
|
+
* // Activity interceptor that publishes results via a proxy activity
|
|
208
|
+
* const publishResultInterceptor: ActivityInterceptor = {
|
|
209
|
+
* async execute(activityCtx, workflowCtx, next) {
|
|
210
|
+
* try {
|
|
211
|
+
* // BEFORE: inspect or modify the activity input
|
|
212
|
+
* console.log(`Calling ${activityCtx.activityName}`, activityCtx.args);
|
|
213
|
+
*
|
|
214
|
+
* // Execute the activity (returns stored result on replay)
|
|
215
|
+
* const result = await next();
|
|
216
|
+
*
|
|
217
|
+
* // AFTER: use the activity output (only runs on replay,
|
|
218
|
+
* // once the result is available)
|
|
219
|
+
*
|
|
220
|
+
* // Guard: skip for the interceptor's own proxy calls
|
|
221
|
+
* if (activityCtx.activityName !== 'publishToSNS') {
|
|
222
|
+
* const { publishToSNS } = Durable.workflow.proxyActivities<{
|
|
223
|
+
* publishToSNS: (topic: string, payload: any) => Promise<void>;
|
|
224
|
+
* }>({
|
|
225
|
+
* taskQueue: 'shared-notifications',
|
|
226
|
+
* retryPolicy: { maximumAttempts: 3, throwOnError: true },
|
|
227
|
+
* });
|
|
228
|
+
*
|
|
229
|
+
* await publishToSNS('activity-results', {
|
|
230
|
+
* workflowId: workflowCtx.get('workflowId'),
|
|
231
|
+
* activityName: activityCtx.activityName,
|
|
232
|
+
* input: activityCtx.args,
|
|
233
|
+
* output: result,
|
|
234
|
+
* });
|
|
235
|
+
* }
|
|
236
|
+
*
|
|
237
|
+
* return result;
|
|
238
|
+
* } catch (err) {
|
|
239
|
+
* if (Durable.didInterrupt(err)) throw err;
|
|
240
|
+
* throw err;
|
|
241
|
+
* }
|
|
242
|
+
* },
|
|
243
|
+
* };
|
|
244
|
+
*
|
|
245
|
+
* Durable.registerActivityInterceptor(publishResultInterceptor);
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* ## Activity Interceptor Replay Pattern
|
|
249
|
+
*
|
|
250
|
+
* Activity interceptors participate in the interruption/replay cycle:
|
|
251
|
+
*
|
|
252
|
+
* 1. **First execution**: Before-phase runs → `next()` registers the activity
|
|
253
|
+
* interruption and throws `DurableProxyError` → workflow pauses
|
|
254
|
+
* 2. **Second execution**: Before-phase replays → `next()` returns the stored
|
|
255
|
+
* activity result → after-phase runs → after-phase proxy call (e.g.,
|
|
256
|
+
* `publishToSNS`) registers its own interruption → workflow pauses
|
|
257
|
+
* 3. **Third execution**: Everything replays → after-phase proxy call returns
|
|
258
|
+
* its stored result → interceptor returns → workflow continues
|
|
186
259
|
*/
|
|
187
260
|
class InterceptorService {
|
|
188
261
|
constructor() {
|
|
@@ -42,36 +42,40 @@ exports.getProxyInterruptPayload = getProxyInterruptPayload;
|
|
|
42
42
|
*/
|
|
43
43
|
function wrapActivity(activityName, options) {
|
|
44
44
|
return async function (...args) {
|
|
45
|
+
// Increment counter first for deterministic replay ordering
|
|
45
46
|
const [didRunAlready, execIndex, result] = await (0, didRun_1.didRun)('proxy');
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
const context = (0, context_1.getContext)();
|
|
48
|
+
const { interruptionRegistry } = context;
|
|
49
|
+
// Build activityCtx so interceptors can inspect/modify args
|
|
50
|
+
const activityCtx = { activityName, args, options };
|
|
51
|
+
// Core function: returns stored result on replay, or registers
|
|
52
|
+
// the interruption and throws on first execution. Reads args
|
|
53
|
+
// from activityCtx so "before" interceptors can modify them.
|
|
54
|
+
const coreFunction = async () => {
|
|
55
|
+
if (didRunAlready) {
|
|
56
|
+
if (result?.$error) {
|
|
57
|
+
if (options?.retryPolicy?.throwOnError !== false) {
|
|
58
|
+
const code = result.$error.code;
|
|
59
|
+
const message = result.$error.message;
|
|
60
|
+
const stack = result.$error.stack;
|
|
61
|
+
if (code === common_1.HMSH_CODE_DURABLE_FATAL) {
|
|
62
|
+
throw new common_1.DurableFatalError(message, stack);
|
|
63
|
+
}
|
|
64
|
+
else if (code === common_1.HMSH_CODE_DURABLE_MAXED) {
|
|
65
|
+
throw new common_1.DurableMaxedError(message, stack);
|
|
66
|
+
}
|
|
67
|
+
else if (code === common_1.HMSH_CODE_DURABLE_TIMEOUT) {
|
|
68
|
+
throw new common_1.DurableTimeoutError(message, stack);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new common_1.DurableFatalError(message, stack);
|
|
72
|
+
}
|
|
64
73
|
}
|
|
74
|
+
return result.$error;
|
|
65
75
|
}
|
|
66
|
-
return result
|
|
76
|
+
return result.data;
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
const context = (0, context_1.getContext)();
|
|
71
|
-
const { interruptionRegistry } = context;
|
|
72
|
-
// Core activity registration logic
|
|
73
|
-
const executeActivity = async () => {
|
|
74
|
-
const interruptionMessage = getProxyInterruptPayload(context, activityName, execIndex, args, options);
|
|
78
|
+
const interruptionMessage = getProxyInterruptPayload(context, activityName, execIndex, activityCtx.args, options);
|
|
75
79
|
interruptionRegistry.push({
|
|
76
80
|
code: common_1.HMSH_CODE_DURABLE_PROXY,
|
|
77
81
|
type: 'DurableProxyError',
|
|
@@ -80,13 +84,13 @@ function wrapActivity(activityName, options) {
|
|
|
80
84
|
await (0, common_1.sleepImmediate)();
|
|
81
85
|
throw new common_1.DurableProxyError(interruptionMessage);
|
|
82
86
|
};
|
|
83
|
-
//
|
|
87
|
+
// Run through interceptor chain if interceptors exist
|
|
84
88
|
const store = common_1.asyncLocalStorage.getStore();
|
|
85
89
|
const interceptorService = store?.get('activityInterceptorService');
|
|
86
90
|
if (interceptorService?.activityInterceptors?.length > 0) {
|
|
87
|
-
return await interceptorService.executeActivityChain(
|
|
91
|
+
return await interceptorService.executeActivityChain(activityCtx, store, coreFunction);
|
|
88
92
|
}
|
|
89
|
-
return await
|
|
93
|
+
return await coreFunction();
|
|
90
94
|
};
|
|
91
95
|
}
|
|
92
96
|
exports.wrapActivity = wrapActivity;
|
|
@@ -182,6 +182,7 @@ const KVTables = (context) => ({
|
|
|
182
182
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
183
183
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
184
184
|
expired_at TIMESTAMP WITH TIME ZONE,
|
|
185
|
+
pruned_at TIMESTAMP WITH TIME ZONE,
|
|
185
186
|
is_live BOOLEAN DEFAULT TRUE,
|
|
186
187
|
PRIMARY KEY (id)
|
|
187
188
|
) PARTITION BY HASH (id);
|
|
@@ -214,8 +215,12 @@ const KVTables = (context) => ({
|
|
|
214
215
|
ON ${fullTableName} (entity, status);
|
|
215
216
|
`);
|
|
216
217
|
await client.query(`
|
|
217
|
-
CREATE INDEX IF NOT EXISTS idx_${tableDef.name}_expired_at
|
|
218
|
+
CREATE INDEX IF NOT EXISTS idx_${tableDef.name}_expired_at
|
|
218
219
|
ON ${fullTableName} (expired_at);
|
|
220
|
+
`);
|
|
221
|
+
await client.query(`
|
|
222
|
+
CREATE INDEX IF NOT EXISTS idx_${tableDef.name}_pruned_at
|
|
223
|
+
ON ${fullTableName} (pruned_at) WHERE pruned_at IS NULL;
|
|
219
224
|
`);
|
|
220
225
|
// Create function to update is_live flag in the schema
|
|
221
226
|
await client.query(`
|
package/build/types/dba.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface PruneOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* If true, hard-deletes expired jobs older than the retention window.
|
|
33
33
|
* FK CASCADE on `jobs_attributes` automatically removes associated
|
|
34
|
-
* attribute rows.
|
|
34
|
+
* attribute rows. When `entities` is set, only matching jobs are deleted.
|
|
35
35
|
* @default true
|
|
36
36
|
*/
|
|
37
37
|
jobs?: boolean;
|
|
@@ -42,13 +42,35 @@ export interface PruneOptions {
|
|
|
42
42
|
*/
|
|
43
43
|
streams?: boolean;
|
|
44
44
|
/**
|
|
45
|
-
* If true, strips execution-artifact attributes
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
45
|
+
* If true, strips execution-artifact attributes from completed,
|
|
46
|
+
* un-pruned jobs. Preserves `jdata` (return data), `udata`
|
|
47
|
+
* (searchable data), and `jmark` (timeline/event history for
|
|
48
|
+
* Temporal-compatible export). See `keepHmark` for `hmark`.
|
|
49
49
|
* @default false
|
|
50
50
|
*/
|
|
51
51
|
attributes?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Entity allowlist. When provided, only jobs whose `entity` column
|
|
54
|
+
* matches one of these values are eligible for pruning/stripping.
|
|
55
|
+
* Jobs with `entity IS NULL` are excluded unless `pruneTransient`
|
|
56
|
+
* is also true.
|
|
57
|
+
* @default undefined (all entities)
|
|
58
|
+
*/
|
|
59
|
+
entities?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* If true, hard-deletes expired jobs where `entity IS NULL`
|
|
62
|
+
* (transient workflow runs). Must also satisfy the retention
|
|
63
|
+
* window (`expire`).
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
pruneTransient?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* If true, `hmark` attributes are preserved during stripping
|
|
69
|
+
* (along with `jdata`, `udata`, and `jmark`). If false, `hmark`
|
|
70
|
+
* rows are stripped.
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
keepHmark?: boolean;
|
|
52
74
|
}
|
|
53
75
|
/**
|
|
54
76
|
* Result returned by `DBA.prune()`, providing deletion
|
|
@@ -61,4 +83,8 @@ export interface PruneResult {
|
|
|
61
83
|
streams: number;
|
|
62
84
|
/** Number of execution-artifact attribute rows stripped from completed jobs */
|
|
63
85
|
attributes: number;
|
|
86
|
+
/** Number of transient (entity IS NULL) job rows hard-deleted */
|
|
87
|
+
transient: number;
|
|
88
|
+
/** Number of jobs marked as pruned (pruned_at set) */
|
|
89
|
+
marked: number;
|
|
64
90
|
}
|
package/build/types/durable.d.ts
CHANGED
|
@@ -596,15 +596,21 @@ export interface WorkflowInterceptor {
|
|
|
596
596
|
execute(ctx: Map<string, any>, next: () => Promise<any>): Promise<any>;
|
|
597
597
|
}
|
|
598
598
|
/**
|
|
599
|
-
* Registry for workflow interceptors that are executed in order
|
|
600
|
-
* for each workflow execution
|
|
599
|
+
* Registry for workflow and activity interceptors that are executed in order
|
|
600
|
+
* for each workflow or activity execution.
|
|
601
601
|
*/
|
|
602
602
|
export interface InterceptorRegistry {
|
|
603
603
|
/**
|
|
604
|
-
* Array of registered interceptors that will wrap workflow execution
|
|
605
|
-
* in the order they were registered (first registered = outermost wrapper)
|
|
604
|
+
* Array of registered workflow interceptors that will wrap workflow execution
|
|
605
|
+
* in the order they were registered (first registered = outermost wrapper).
|
|
606
606
|
*/
|
|
607
607
|
interceptors: WorkflowInterceptor[];
|
|
608
|
+
/**
|
|
609
|
+
* Array of registered activity interceptors that will wrap individual
|
|
610
|
+
* proxied activity calls in the order they were registered
|
|
611
|
+
* (first registered = outermost wrapper).
|
|
612
|
+
*/
|
|
613
|
+
activityInterceptors: ActivityInterceptor[];
|
|
608
614
|
}
|
|
609
615
|
/**
|
|
610
616
|
* Context provided to an activity interceptor, containing metadata
|
|
@@ -624,11 +630,21 @@ export interface ActivityInterceptorContext {
|
|
|
624
630
|
* workflow methods (proxyActivities, sleepFor, waitFor, execChild, etc.)
|
|
625
631
|
* are available.
|
|
626
632
|
*
|
|
627
|
-
* Activity interceptors
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
633
|
+
* Activity interceptors wrap proxied activity calls in an onion pattern,
|
|
634
|
+
* supporting both **before** and **after** phases:
|
|
635
|
+
*
|
|
636
|
+
* - **Before phase** (code before `await next()`): Runs before the activity
|
|
637
|
+
* executes. The interceptor can inspect or modify `activityCtx.args` to
|
|
638
|
+
* transform the activity input before it is sent.
|
|
639
|
+
*
|
|
640
|
+
* - **After phase** (code after `await next()`): Runs on replay once the
|
|
641
|
+
* activity result is available. The interceptor receives the activity
|
|
642
|
+
* output as the return value of `next()` and can inspect or transform it.
|
|
643
|
+
*
|
|
644
|
+
* On first execution, `next()` registers the activity with the interruption
|
|
645
|
+
* system and throws (the activity has not completed yet). On replay, `next()`
|
|
646
|
+
* returns the stored result and the after-phase code executes. This follows
|
|
647
|
+
* the same deterministic replay pattern as workflow interceptors.
|
|
632
648
|
*
|
|
633
649
|
* @example
|
|
634
650
|
* ```typescript
|
|
@@ -653,11 +669,13 @@ export interface ActivityInterceptorContext {
|
|
|
653
669
|
*/
|
|
654
670
|
export interface ActivityInterceptor {
|
|
655
671
|
/**
|
|
656
|
-
* Called
|
|
672
|
+
* Called around each proxied activity invocation. Code before `next()`
|
|
673
|
+
* runs in the before phase; code after `next()` runs in the after phase
|
|
674
|
+
* once the activity result is available on replay.
|
|
657
675
|
*
|
|
658
|
-
* @param activityCtx - Metadata about the activity being called
|
|
676
|
+
* @param activityCtx - Metadata about the activity being called (args may be modified)
|
|
659
677
|
* @param workflowCtx - The workflow context map (same as WorkflowInterceptor receives)
|
|
660
|
-
* @param next - Call to proceed to the next interceptor or the
|
|
678
|
+
* @param next - Call to proceed to the next interceptor or the core activity function
|
|
661
679
|
* @returns The activity result (from replay or after interruption/re-execution)
|
|
662
680
|
*/
|
|
663
681
|
execute(activityCtx: ActivityInterceptorContext, workflowCtx: Map<string, any>, next: () => Promise<any>): Promise<any>;
|
|
@@ -79,3 +79,130 @@ export interface JobExport {
|
|
|
79
79
|
process: StringAnyType;
|
|
80
80
|
status: string;
|
|
81
81
|
}
|
|
82
|
+
export type ExportMode = 'sparse' | 'verbose';
|
|
83
|
+
export type WorkflowEventType = 'workflow_execution_started' | 'workflow_execution_completed' | 'workflow_execution_failed' | 'activity_task_scheduled' | 'activity_task_completed' | 'activity_task_failed' | 'child_workflow_execution_started' | 'child_workflow_execution_completed' | 'child_workflow_execution_failed' | 'timer_started' | 'timer_fired' | 'workflow_execution_signaled';
|
|
84
|
+
export type WorkflowEventCategory = 'workflow' | 'activity' | 'child_workflow' | 'timer' | 'signal';
|
|
85
|
+
export interface WorkflowExecutionStartedAttributes {
|
|
86
|
+
kind: 'workflow_execution_started';
|
|
87
|
+
workflow_type: string;
|
|
88
|
+
task_queue: string;
|
|
89
|
+
input?: any;
|
|
90
|
+
}
|
|
91
|
+
export interface WorkflowExecutionCompletedAttributes {
|
|
92
|
+
kind: 'workflow_execution_completed';
|
|
93
|
+
result?: any;
|
|
94
|
+
}
|
|
95
|
+
export interface WorkflowExecutionFailedAttributes {
|
|
96
|
+
kind: 'workflow_execution_failed';
|
|
97
|
+
failure?: string;
|
|
98
|
+
}
|
|
99
|
+
export interface ActivityTaskScheduledAttributes {
|
|
100
|
+
kind: 'activity_task_scheduled';
|
|
101
|
+
activity_type: string;
|
|
102
|
+
timeline_key: string;
|
|
103
|
+
execution_index: number;
|
|
104
|
+
}
|
|
105
|
+
export interface ActivityTaskCompletedAttributes {
|
|
106
|
+
kind: 'activity_task_completed';
|
|
107
|
+
activity_type: string;
|
|
108
|
+
result?: any;
|
|
109
|
+
scheduled_event_id?: number;
|
|
110
|
+
timeline_key: string;
|
|
111
|
+
execution_index: number;
|
|
112
|
+
}
|
|
113
|
+
export interface ActivityTaskFailedAttributes {
|
|
114
|
+
kind: 'activity_task_failed';
|
|
115
|
+
activity_type: string;
|
|
116
|
+
failure?: any;
|
|
117
|
+
scheduled_event_id?: number;
|
|
118
|
+
timeline_key: string;
|
|
119
|
+
execution_index: number;
|
|
120
|
+
}
|
|
121
|
+
export interface ChildWorkflowExecutionStartedAttributes {
|
|
122
|
+
kind: 'child_workflow_execution_started';
|
|
123
|
+
child_workflow_id: string;
|
|
124
|
+
awaited: boolean;
|
|
125
|
+
timeline_key: string;
|
|
126
|
+
execution_index: number;
|
|
127
|
+
}
|
|
128
|
+
export interface ChildWorkflowExecutionCompletedAttributes {
|
|
129
|
+
kind: 'child_workflow_execution_completed';
|
|
130
|
+
child_workflow_id: string;
|
|
131
|
+
result?: any;
|
|
132
|
+
initiated_event_id?: number;
|
|
133
|
+
timeline_key: string;
|
|
134
|
+
execution_index: number;
|
|
135
|
+
}
|
|
136
|
+
export interface ChildWorkflowExecutionFailedAttributes {
|
|
137
|
+
kind: 'child_workflow_execution_failed';
|
|
138
|
+
child_workflow_id: string;
|
|
139
|
+
failure?: any;
|
|
140
|
+
initiated_event_id?: number;
|
|
141
|
+
timeline_key: string;
|
|
142
|
+
execution_index: number;
|
|
143
|
+
}
|
|
144
|
+
export interface TimerStartedAttributes {
|
|
145
|
+
kind: 'timer_started';
|
|
146
|
+
duration_ms?: number;
|
|
147
|
+
timeline_key: string;
|
|
148
|
+
execution_index: number;
|
|
149
|
+
}
|
|
150
|
+
export interface TimerFiredAttributes {
|
|
151
|
+
kind: 'timer_fired';
|
|
152
|
+
timeline_key: string;
|
|
153
|
+
execution_index: number;
|
|
154
|
+
}
|
|
155
|
+
export interface WorkflowExecutionSignaledAttributes {
|
|
156
|
+
kind: 'workflow_execution_signaled';
|
|
157
|
+
signal_name: string;
|
|
158
|
+
input?: any;
|
|
159
|
+
timeline_key: string;
|
|
160
|
+
execution_index: number;
|
|
161
|
+
}
|
|
162
|
+
export type WorkflowEventAttributes = WorkflowExecutionStartedAttributes | WorkflowExecutionCompletedAttributes | WorkflowExecutionFailedAttributes | ActivityTaskScheduledAttributes | ActivityTaskCompletedAttributes | ActivityTaskFailedAttributes | ChildWorkflowExecutionStartedAttributes | ChildWorkflowExecutionCompletedAttributes | ChildWorkflowExecutionFailedAttributes | TimerStartedAttributes | TimerFiredAttributes | WorkflowExecutionSignaledAttributes;
|
|
163
|
+
export interface WorkflowExecutionEvent {
|
|
164
|
+
event_id: number;
|
|
165
|
+
event_type: WorkflowEventType;
|
|
166
|
+
category: WorkflowEventCategory;
|
|
167
|
+
event_time: string;
|
|
168
|
+
duration_ms: number | null;
|
|
169
|
+
is_system: boolean;
|
|
170
|
+
attributes: WorkflowEventAttributes;
|
|
171
|
+
}
|
|
172
|
+
export interface WorkflowExecutionSummary {
|
|
173
|
+
total_events: number;
|
|
174
|
+
activities: {
|
|
175
|
+
total: number;
|
|
176
|
+
completed: number;
|
|
177
|
+
failed: number;
|
|
178
|
+
system: number;
|
|
179
|
+
user: number;
|
|
180
|
+
};
|
|
181
|
+
child_workflows: {
|
|
182
|
+
total: number;
|
|
183
|
+
completed: number;
|
|
184
|
+
failed: number;
|
|
185
|
+
};
|
|
186
|
+
timers: number;
|
|
187
|
+
signals: number;
|
|
188
|
+
}
|
|
189
|
+
export type WorkflowExecutionStatus = 'running' | 'completed' | 'failed';
|
|
190
|
+
export interface WorkflowExecution {
|
|
191
|
+
workflow_id: string;
|
|
192
|
+
workflow_type: string;
|
|
193
|
+
task_queue: string;
|
|
194
|
+
status: WorkflowExecutionStatus;
|
|
195
|
+
start_time: string | null;
|
|
196
|
+
close_time: string | null;
|
|
197
|
+
duration_ms: number | null;
|
|
198
|
+
result: any;
|
|
199
|
+
events: WorkflowExecutionEvent[];
|
|
200
|
+
summary: WorkflowExecutionSummary;
|
|
201
|
+
children?: WorkflowExecution[];
|
|
202
|
+
}
|
|
203
|
+
export interface ExecutionExportOptions {
|
|
204
|
+
mode?: ExportMode;
|
|
205
|
+
exclude_system?: boolean;
|
|
206
|
+
omit_results?: boolean;
|
|
207
|
+
max_depth?: number;
|
|
208
|
+
}
|
package/build/types/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { CollationFaultType, CollationStage } from './collator';
|
|
|
6
6
|
export { ActivityConfig, ActivityInterceptor, ActivityInterceptorContext, ActivityWorkflowDataType, ChildResponseType, ClientConfig, ClientWorkflow, ContextType, Connection, ProxyResponseType, ProxyType, Registry, SignalOptions, FindJobsOptions, FindOptions, FindWhereOptions, FindWhereQuery, HookOptions, SearchResults, WorkflowConfig, WorkerConfig, WorkerOptions, WorkflowContext, WorkflowSearchOptions, WorkflowSearchSchema, WorkflowDataType, WorkflowOptions, WorkflowInterceptor, InterceptorRegistry, } from './durable';
|
|
7
7
|
export { PruneOptions, PruneResult, } from './dba';
|
|
8
8
|
export { DurableChildErrorType, DurableProxyErrorType, DurableSleepErrorType, DurableWaitForAllErrorType, DurableWaitForErrorType, } from './error';
|
|
9
|
-
export { ActivityAction, DependencyExport, DurableJobExport, ExportCycles, ExportItem, ExportOptions, ExportTransitions, JobAction, JobExport, JobActionExport, JobTimeline, } from './exporter';
|
|
9
|
+
export { ActivityAction, DependencyExport, DurableJobExport, ExecutionExportOptions, ExportCycles, ExportItem, ExportMode, ExportOptions, ExportTransitions, JobAction, JobExport, JobActionExport, JobTimeline, WorkflowEventAttributes, WorkflowEventCategory, WorkflowEventType, WorkflowExecution, WorkflowExecutionEvent, WorkflowExecutionStatus, WorkflowExecutionSummary, } from './exporter';
|
|
10
10
|
export { HookCondition, HookConditions, HookGate, HookInterface, HookRule, HookRules, HookSignal, } from './hook';
|
|
11
11
|
export { HotMesh, HotMeshEngine, HotMeshWorker, HotMeshSettings, HotMeshApp, HotMeshApps, HotMeshConfig, HotMeshManifest, HotMeshGraph, KeyType, KeyStoreParams, ScoutType, } from './hotmesh';
|
|
12
12
|
export { ILogger, LogLevel } from './logger';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Permanent-Memory Workflows & AI Agents",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
"test:durable:sleep": "vitest run tests/durable/sleep/postgres.test.ts",
|
|
47
47
|
"test:durable:signal": "vitest run tests/durable/signal/postgres.test.ts",
|
|
48
48
|
"test:durable:unknown": "vitest run tests/durable/unknown/postgres.test.ts",
|
|
49
|
+
"test:durable:exporter": "vitest run tests/durable/exporter/exporter.test.ts",
|
|
50
|
+
"test:durable:exporter:debug": "EXPORT_DEBUG=1 HMSH_LOGLEVEL=error vitest run tests/durable/basic/postgres.test.ts",
|
|
49
51
|
"test:dba": "vitest run tests/dba",
|
|
50
52
|
"test:cycle": "vitest run tests/functional/cycle",
|
|
51
53
|
"test:functional": "vitest run tests/functional",
|
|
@@ -110,13 +112,12 @@
|
|
|
110
112
|
"nats": "^2.28.0",
|
|
111
113
|
"openai": "^5.9.0",
|
|
112
114
|
"pg": "^8.10.0",
|
|
113
|
-
"rimraf": "^
|
|
115
|
+
"rimraf": "^6.1.3",
|
|
114
116
|
"terser": "^5.37.0",
|
|
115
117
|
"ts-node": "^10.9.1",
|
|
116
|
-
"ts-node-dev": "^2.0.0",
|
|
117
118
|
"typedoc": "^0.26.4",
|
|
118
119
|
"typescript": "^5.0.4",
|
|
119
|
-
"vitest": "^
|
|
120
|
+
"vitest": "^4.0.18"
|
|
120
121
|
},
|
|
121
122
|
"peerDependencies": {
|
|
122
123
|
"nats": "^2.0.0",
|
|
File without changes
|