@hotmeshio/hotmesh 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -43
- package/build/index.d.ts +12 -11
- package/build/index.js +15 -13
- package/build/modules/enums.d.ts +23 -34
- package/build/modules/enums.js +26 -38
- package/build/modules/errors.d.ts +16 -16
- package/build/modules/errors.js +37 -37
- package/build/package.json +63 -67
- package/build/services/activities/activity.d.ts +58 -7
- package/build/services/activities/activity.js +67 -38
- package/build/services/activities/await.d.ts +101 -0
- package/build/services/activities/await.js +101 -0
- package/build/services/activities/cycle.d.ts +82 -0
- package/build/services/activities/cycle.js +86 -8
- package/build/services/activities/hook.d.ts +139 -1
- package/build/services/activities/hook.js +140 -2
- package/build/services/activities/interrupt.d.ts +112 -0
- package/build/services/activities/interrupt.js +118 -5
- package/build/services/activities/signal.d.ts +108 -3
- package/build/services/activities/signal.js +113 -8
- package/build/services/activities/trigger.d.ts +56 -4
- package/build/services/activities/trigger.js +119 -35
- package/build/services/activities/worker.d.ts +107 -0
- package/build/services/activities/worker.js +107 -0
- package/build/services/collator/index.d.ts +3 -15
- package/build/services/collator/index.js +7 -34
- package/build/services/dba/index.d.ts +171 -0
- package/build/services/dba/index.js +280 -0
- package/build/services/{memflow → durable}/client.d.ts +3 -3
- package/build/services/{memflow → durable}/client.js +15 -15
- package/build/services/{memflow → durable}/connection.d.ts +2 -2
- package/build/services/{memflow → durable}/connection.js +1 -1
- package/build/services/{memflow → durable}/exporter.d.ts +6 -6
- package/build/services/{memflow → durable}/exporter.js +2 -2
- package/build/services/{memflow → durable}/handle.d.ts +4 -4
- package/build/services/{memflow → durable}/handle.js +3 -3
- package/build/services/{memflow → durable}/index.d.ts +126 -34
- package/build/services/{memflow → durable}/index.js +146 -50
- package/build/services/{memflow → durable}/interceptor.d.ts +45 -22
- package/build/services/{memflow → durable}/interceptor.js +54 -21
- package/build/services/{memflow → durable}/schemas/factory.d.ts +4 -4
- package/build/services/{memflow → durable}/schemas/factory.js +5 -5
- package/build/services/{memflow → durable}/search.d.ts +1 -1
- package/build/services/{memflow → durable}/search.js +4 -4
- package/build/services/{memflow → durable}/worker.d.ts +11 -11
- package/build/services/{memflow → durable}/worker.js +61 -60
- package/build/services/durable/workflow/all.d.ts +32 -0
- package/build/services/durable/workflow/all.js +40 -0
- package/build/services/{memflow → durable}/workflow/common.d.ts +5 -5
- package/build/services/durable/workflow/common.js +47 -0
- package/build/services/durable/workflow/context.d.ts +49 -0
- package/build/services/durable/workflow/context.js +88 -0
- package/build/services/durable/workflow/didRun.d.ts +27 -0
- package/build/services/durable/workflow/didRun.js +42 -0
- package/build/services/durable/workflow/emit.d.ts +50 -0
- package/build/services/durable/workflow/emit.js +68 -0
- package/build/services/durable/workflow/enrich.d.ts +37 -0
- package/build/services/durable/workflow/enrich.js +45 -0
- package/build/services/durable/workflow/entityMethods.d.ts +61 -0
- package/build/services/durable/workflow/entityMethods.js +80 -0
- package/build/services/durable/workflow/execChild.d.ts +106 -0
- package/build/services/durable/workflow/execChild.js +194 -0
- package/build/services/durable/workflow/execHook.d.ts +80 -0
- package/build/services/durable/workflow/execHook.js +97 -0
- package/build/services/durable/workflow/execHookBatch.d.ts +107 -0
- package/build/services/durable/workflow/execHookBatch.js +129 -0
- package/build/services/durable/workflow/hook.d.ts +74 -0
- package/build/services/durable/workflow/hook.js +123 -0
- package/build/services/durable/workflow/index.d.ts +129 -0
- package/build/services/{memflow → durable}/workflow/index.js +66 -11
- package/build/services/durable/workflow/interrupt.d.ts +55 -0
- package/build/services/durable/workflow/interrupt.js +70 -0
- package/build/services/durable/workflow/interruption.d.ts +61 -0
- package/build/services/durable/workflow/interruption.js +76 -0
- package/build/services/durable/workflow/isSideEffectAllowed.d.ts +27 -0
- package/build/services/{memflow → durable}/workflow/isSideEffectAllowed.js +21 -4
- package/build/services/durable/workflow/proxyActivities.d.ts +119 -0
- package/build/services/durable/workflow/proxyActivities.js +214 -0
- package/build/services/durable/workflow/random.d.ts +36 -0
- package/build/services/durable/workflow/random.js +46 -0
- package/build/services/durable/workflow/searchMethods.d.ts +53 -0
- package/build/services/durable/workflow/searchMethods.js +72 -0
- package/build/services/durable/workflow/signal.d.ts +58 -0
- package/build/services/durable/workflow/signal.js +79 -0
- package/build/services/durable/workflow/sleepFor.d.ts +63 -0
- package/build/services/durable/workflow/sleepFor.js +91 -0
- package/build/services/durable/workflow/trace.d.ts +47 -0
- package/build/services/durable/workflow/trace.js +66 -0
- package/build/services/durable/workflow/waitFor.d.ts +66 -0
- package/build/services/durable/workflow/waitFor.js +93 -0
- package/build/services/engine/index.d.ts +18 -2
- package/build/services/engine/index.js +14 -4
- package/build/services/exporter/index.d.ts +2 -0
- package/build/services/exporter/index.js +1 -0
- package/build/services/hotmesh/index.d.ts +471 -236
- package/build/services/hotmesh/index.js +473 -238
- package/build/services/store/index.d.ts +1 -1
- package/build/services/store/providers/postgres/postgres.d.ts +1 -1
- package/build/services/store/providers/postgres/postgres.js +4 -3
- package/build/services/telemetry/index.js +6 -0
- package/build/services/{meshcall → virtual}/index.d.ts +29 -29
- package/build/services/{meshcall → virtual}/index.js +49 -49
- package/build/services/{meshcall → virtual}/schemas/factory.d.ts +1 -1
- package/build/services/{meshcall → virtual}/schemas/factory.js +1 -1
- package/build/types/activity.d.ts +1 -1
- package/build/types/dba.d.ts +64 -0
- package/build/types/{memflow.d.ts → durable.d.ts} +75 -19
- package/build/types/error.d.ts +5 -5
- package/build/types/exporter.d.ts +1 -1
- package/build/types/hotmesh.d.ts +1 -1
- package/build/types/index.d.ts +5 -4
- package/build/types/job.d.ts +1 -1
- package/build/types/quorum.d.ts +2 -2
- package/build/types/{meshcall.d.ts → virtual.d.ts} +15 -15
- package/build/types/virtual.js +2 -0
- package/index.ts +15 -13
- package/package.json +63 -67
- package/vitest.config.ts +17 -0
- package/.claude/settings.local.json +0 -7
- package/build/services/memflow/workflow/all.d.ts +0 -7
- package/build/services/memflow/workflow/all.js +0 -15
- package/build/services/memflow/workflow/common.js +0 -47
- package/build/services/memflow/workflow/context.d.ts +0 -6
- package/build/services/memflow/workflow/context.js +0 -45
- package/build/services/memflow/workflow/didRun.d.ts +0 -7
- package/build/services/memflow/workflow/didRun.js +0 -22
- package/build/services/memflow/workflow/emit.d.ts +0 -11
- package/build/services/memflow/workflow/emit.js +0 -29
- package/build/services/memflow/workflow/enrich.d.ts +0 -9
- package/build/services/memflow/workflow/enrich.js +0 -17
- package/build/services/memflow/workflow/entityMethods.d.ts +0 -14
- package/build/services/memflow/workflow/entityMethods.js +0 -33
- package/build/services/memflow/workflow/execChild.d.ts +0 -18
- package/build/services/memflow/workflow/execChild.js +0 -106
- package/build/services/memflow/workflow/execHook.d.ts +0 -65
- package/build/services/memflow/workflow/execHook.js +0 -83
- package/build/services/memflow/workflow/execHookBatch.d.ts +0 -54
- package/build/services/memflow/workflow/execHookBatch.js +0 -77
- package/build/services/memflow/workflow/hook.d.ts +0 -9
- package/build/services/memflow/workflow/hook.js +0 -58
- package/build/services/memflow/workflow/index.d.ts +0 -74
- package/build/services/memflow/workflow/interrupt.d.ts +0 -9
- package/build/services/memflow/workflow/interrupt.js +0 -24
- package/build/services/memflow/workflow/interruption.d.ts +0 -28
- package/build/services/memflow/workflow/interruption.js +0 -43
- package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +0 -10
- package/build/services/memflow/workflow/proxyActivities.d.ts +0 -91
- package/build/services/memflow/workflow/proxyActivities.js +0 -176
- package/build/services/memflow/workflow/random.d.ts +0 -6
- package/build/services/memflow/workflow/random.js +0 -16
- package/build/services/memflow/workflow/searchMethods.d.ts +0 -6
- package/build/services/memflow/workflow/searchMethods.js +0 -25
- package/build/services/memflow/workflow/signal.d.ts +0 -29
- package/build/services/memflow/workflow/signal.js +0 -50
- package/build/services/memflow/workflow/sleepFor.d.ts +0 -24
- package/build/services/memflow/workflow/sleepFor.js +0 -52
- package/build/services/memflow/workflow/trace.d.ts +0 -14
- package/build/services/memflow/workflow/trace.js +0 -33
- package/build/services/memflow/workflow/waitFor.d.ts +0 -29
- package/build/services/memflow/workflow/waitFor.js +0 -56
- /package/build/services/{memflow → durable}/entity.d.ts +0 -0
- /package/build/services/{memflow → durable}/entity.js +0 -0
- /package/build/types/{memflow.js → dba.js} +0 -0
- /package/build/types/{meshcall.js → durable.js} +0 -0
|
@@ -11,7 +11,7 @@ const search_1 = require("./search");
|
|
|
11
11
|
const handle_1 = require("./handle");
|
|
12
12
|
const factory_1 = require("./schemas/factory");
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
14
|
+
* The Durable `Client` service is functionally
|
|
15
15
|
* equivalent to the Temporal `Client` service.
|
|
16
16
|
* Start a new workflow execution by calling
|
|
17
17
|
* `workflow.start`. Note the direct connection to
|
|
@@ -83,7 +83,7 @@ class ClientService {
|
|
|
83
83
|
//resolve, activate, and return the client
|
|
84
84
|
const resolvedClient = await hotMeshClient;
|
|
85
85
|
if (!readonly) {
|
|
86
|
-
resolvedClient.engine.logger.info('
|
|
86
|
+
resolvedClient.engine.logger.info('durable-readonly-client', {
|
|
87
87
|
guid: resolvedClient.engine.guid,
|
|
88
88
|
appId: targetNS,
|
|
89
89
|
});
|
|
@@ -114,7 +114,7 @@ class ClientService {
|
|
|
114
114
|
return await searchClient.sendIndexedQuery(index, query);
|
|
115
115
|
};
|
|
116
116
|
/**
|
|
117
|
-
* The
|
|
117
|
+
* The Durable `Client` service is functionally
|
|
118
118
|
* equivalent to the Temporal `Client` service.
|
|
119
119
|
* Starting a workflow is the primary use case and
|
|
120
120
|
* is accessed by calling workflow.start().
|
|
@@ -145,9 +145,9 @@ class ClientService {
|
|
|
145
145
|
parentWorkflowId: options.parentWorkflowId,
|
|
146
146
|
workflowId: options.workflowId || hotmesh_1.HotMesh.guid(),
|
|
147
147
|
workflowTopic: workflowTopic,
|
|
148
|
-
backoffCoefficient: options.config?.backoffCoefficient || enums_1.
|
|
149
|
-
maximumAttempts: options.config?.maximumAttempts || enums_1.
|
|
150
|
-
maximumInterval: (0, utils_1.s)(options.config?.maximumInterval || enums_1.
|
|
148
|
+
backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_DURABLE_EXP_BACKOFF,
|
|
149
|
+
maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_DURABLE_MAX_ATTEMPTS,
|
|
150
|
+
maximumInterval: (0, utils_1.s)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL),
|
|
151
151
|
};
|
|
152
152
|
const context = { metadata: { trc, spn }, data: {} };
|
|
153
153
|
const jobId = await hotMeshClient.pub(`${options.namespace ?? factory_1.APP_ID}.execute`, payload, context, {
|
|
@@ -163,7 +163,7 @@ class ClientService {
|
|
|
163
163
|
*/
|
|
164
164
|
signal: async (signalId, data, namespace) => {
|
|
165
165
|
const topic = `${namespace ?? factory_1.APP_ID}.wfs.signal`;
|
|
166
|
-
return await (await this.getHotMeshClient(topic, namespace)).
|
|
166
|
+
return await (await this.getHotMeshClient(topic, namespace)).signal(topic, { id: signalId, data });
|
|
167
167
|
},
|
|
168
168
|
/**
|
|
169
169
|
* Spawns an a new, isolated execution cycle within the same job.
|
|
@@ -191,13 +191,13 @@ class ClientService {
|
|
|
191
191
|
arguments: [...options.args],
|
|
192
192
|
id: options.workflowId,
|
|
193
193
|
workflowTopic,
|
|
194
|
-
backoffCoefficient: options.config?.backoffCoefficient || enums_1.
|
|
195
|
-
maximumAttempts: options.config?.maximumAttempts || enums_1.
|
|
196
|
-
maximumInterval: (0, utils_1.s)(options.config?.maximumInterval || enums_1.
|
|
194
|
+
backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_DURABLE_EXP_BACKOFF,
|
|
195
|
+
maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_DURABLE_MAX_ATTEMPTS,
|
|
196
|
+
maximumInterval: (0, utils_1.s)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL),
|
|
197
197
|
};
|
|
198
198
|
//seed search data before entering
|
|
199
199
|
const hotMeshClient = await this.getHotMeshClient(taskQueue, options.namespace);
|
|
200
|
-
const msgId = await hotMeshClient.
|
|
200
|
+
const msgId = await hotMeshClient.signal(`${hotMeshClient.appId}.flow.signal`, payload, types_1.StreamStatus.PENDING, 202);
|
|
201
201
|
//todo: commit search data BEFORE enqueuing hook
|
|
202
202
|
if (options.search?.data) {
|
|
203
203
|
const searchSessionId = `-search-${hotmesh_1.HotMesh.guid()}-0`;
|
|
@@ -237,7 +237,7 @@ class ClientService {
|
|
|
237
237
|
* await client.workflow.search(
|
|
238
238
|
* 'someTaskQueue'
|
|
239
239
|
* 'someWorkflowName',
|
|
240
|
-
* '
|
|
240
|
+
* 'durable',
|
|
241
241
|
* 'user',
|
|
242
242
|
* ...args,
|
|
243
243
|
* );
|
|
@@ -251,7 +251,7 @@ class ClientService {
|
|
|
251
251
|
return await this.search(hotMeshClient, index, query);
|
|
252
252
|
}
|
|
253
253
|
catch (error) {
|
|
254
|
-
hotMeshClient.engine.logger.error('
|
|
254
|
+
hotMeshClient.engine.logger.error('durable-client-search-err', {
|
|
255
255
|
error,
|
|
256
256
|
});
|
|
257
257
|
throw error;
|
|
@@ -314,7 +314,7 @@ class ClientService {
|
|
|
314
314
|
await hotMesh.activate(version);
|
|
315
315
|
}
|
|
316
316
|
catch (error) {
|
|
317
|
-
hotMesh.engine.logger.error('
|
|
317
|
+
hotMesh.engine.logger.error('durable-client-activate-err', {
|
|
318
318
|
error,
|
|
319
319
|
});
|
|
320
320
|
throw error;
|
|
@@ -326,7 +326,7 @@ class ClientService {
|
|
|
326
326
|
await hotMesh.activate(version);
|
|
327
327
|
}
|
|
328
328
|
catch (error) {
|
|
329
|
-
hotMesh.engine.logger.error('
|
|
329
|
+
hotMesh.engine.logger.error('durable-client-deploy-activate-err', {
|
|
330
330
|
error,
|
|
331
331
|
});
|
|
332
332
|
throw error;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Connection } from '../../types/
|
|
1
|
+
import { Connection } from '../../types/durable';
|
|
2
2
|
import { ProviderConfig, ProvidersConfig } from '../../types/provider';
|
|
3
3
|
/**
|
|
4
4
|
* The Connection service is used to declare the class
|
|
5
5
|
* and connection options but does not connect quite yet. Connection
|
|
6
6
|
* happens at a later lifecycle stage when a workflow
|
|
7
|
-
* is started by the
|
|
7
|
+
* is started by the Durable Client module (`(new Durable.Client())).start()`).
|
|
8
8
|
*
|
|
9
9
|
* The config options optionall support a multi-connection setup
|
|
10
10
|
* where the `store` connection explicitly defined along with `stream`, `sub`, etc.
|
|
@@ -5,7 +5,7 @@ exports.ConnectionService = void 0;
|
|
|
5
5
|
* The Connection service is used to declare the class
|
|
6
6
|
* and connection options but does not connect quite yet. Connection
|
|
7
7
|
* happens at a later lifecycle stage when a workflow
|
|
8
|
-
* is started by the
|
|
8
|
+
* is started by the Durable Client module (`(new Durable.Client())).start()`).
|
|
9
9
|
*
|
|
10
10
|
* The config options optionall support a multi-connection setup
|
|
11
11
|
* where the `store` connection explicitly defined along with `stream`, `sub`, etc.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ILogger } from '../logger';
|
|
2
2
|
import { StoreService } from '../store';
|
|
3
|
-
import { ExportOptions,
|
|
3
|
+
import { ExportOptions, DurableJobExport, TimelineType, TransitionType, ExportFields } from '../../types/exporter';
|
|
4
4
|
import { ProviderClient, ProviderTransaction } from '../../types/provider';
|
|
5
5
|
import { StringStringType, Symbols } from '../../types/serializer';
|
|
6
6
|
declare class ExporterService {
|
|
@@ -11,17 +11,17 @@ declare class ExporterService {
|
|
|
11
11
|
private static symbols;
|
|
12
12
|
constructor(appId: string, store: StoreService<ProviderClient, ProviderTransaction>, logger: ILogger);
|
|
13
13
|
/**
|
|
14
|
-
* Convert the job hash from its compiles format into a
|
|
14
|
+
* Convert the job hash from its compiles format into a DurableJobExport object with
|
|
15
15
|
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
16
16
|
*/
|
|
17
|
-
export(jobId: string, options?: ExportOptions): Promise<
|
|
17
|
+
export(jobId: string, options?: ExportOptions): Promise<DurableJobExport>;
|
|
18
18
|
/**
|
|
19
|
-
* Inflates the job data into a
|
|
19
|
+
* Inflates the job data into a DurableJobExport object
|
|
20
20
|
* @param jobHash - the job data
|
|
21
21
|
* @param dependencyList - the list of dependencies for the job
|
|
22
22
|
* @returns - the inflated job data
|
|
23
23
|
*/
|
|
24
|
-
inflate(jobHash: StringStringType, options: ExportOptions):
|
|
24
|
+
inflate(jobHash: StringStringType, options: ExportOptions): DurableJobExport;
|
|
25
25
|
resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null;
|
|
26
26
|
/**
|
|
27
27
|
* Inflates the key
|
|
@@ -30,7 +30,7 @@ declare class ExporterService {
|
|
|
30
30
|
* @private
|
|
31
31
|
*/
|
|
32
32
|
inflateKey(key: string): string;
|
|
33
|
-
filterFields(fullObject:
|
|
33
|
+
filterFields(fullObject: DurableJobExport, block?: ExportFields[], allow?: ExportFields[]): Partial<DurableJobExport>;
|
|
34
34
|
inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>): void;
|
|
35
35
|
sortEntriesByCreated(obj: {
|
|
36
36
|
[key: string]: TransitionType;
|
|
@@ -10,7 +10,7 @@ class ExporterService {
|
|
|
10
10
|
this.store = store;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Convert the job hash from its compiles format into a
|
|
13
|
+
* Convert the job hash from its compiles format into a DurableJobExport object with
|
|
14
14
|
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
15
15
|
*/
|
|
16
16
|
async export(jobId, options = {}) {
|
|
@@ -23,7 +23,7 @@ class ExporterService {
|
|
|
23
23
|
return jobExport;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Inflates the job data into a
|
|
26
|
+
* Inflates the job data into a DurableJobExport object
|
|
27
27
|
* @param jobHash - the job data
|
|
28
28
|
* @param dependencyList - the list of dependencies for the job
|
|
29
29
|
* @returns - the inflated job data
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HotMesh } from '../hotmesh';
|
|
2
|
-
import {
|
|
2
|
+
import { DurableJobExport, ExportOptions } from '../../types/exporter';
|
|
3
3
|
import { JobInterruptOptions } from '../../types/job';
|
|
4
4
|
import { StreamError } from '../../types/stream';
|
|
5
5
|
import { ExporterService } from './exporter';
|
|
@@ -7,7 +7,7 @@ import { ExporterService } from './exporter';
|
|
|
7
7
|
* The WorkflowHandleService provides methods to interact with a running
|
|
8
8
|
* workflow. This includes exporting the workflow, sending signals, and
|
|
9
9
|
* querying the state of the workflow. It is instanced/accessed via the
|
|
10
|
-
*
|
|
10
|
+
* Durable.Client class.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
@@ -43,11 +43,11 @@ export declare class WorkflowHandleService {
|
|
|
43
43
|
/**
|
|
44
44
|
* Exports the workflow state to a JSON object.
|
|
45
45
|
*/
|
|
46
|
-
export(options?: ExportOptions): Promise<
|
|
46
|
+
export(options?: ExportOptions): Promise<DurableJobExport>;
|
|
47
47
|
/**
|
|
48
48
|
* Sends a signal to the workflow. This is a way to send
|
|
49
49
|
* a message to a workflow that is paused due to having
|
|
50
|
-
* executed `
|
|
50
|
+
* executed `Durable.workflow.waitFor`. The workflow
|
|
51
51
|
* will awaken if no other signals are pending.
|
|
52
52
|
*/
|
|
53
53
|
signal(signalId: string, data: Record<any, any>): Promise<void>;
|
|
@@ -6,7 +6,7 @@ const exporter_1 = require("./exporter");
|
|
|
6
6
|
* The WorkflowHandleService provides methods to interact with a running
|
|
7
7
|
* workflow. This includes exporting the workflow, sending signals, and
|
|
8
8
|
* querying the state of the workflow. It is instanced/accessed via the
|
|
9
|
-
*
|
|
9
|
+
* Durable.Client class.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
12
|
* ```typescript
|
|
@@ -46,11 +46,11 @@ class WorkflowHandleService {
|
|
|
46
46
|
/**
|
|
47
47
|
* Sends a signal to the workflow. This is a way to send
|
|
48
48
|
* a message to a workflow that is paused due to having
|
|
49
|
-
* executed `
|
|
49
|
+
* executed `Durable.workflow.waitFor`. The workflow
|
|
50
50
|
* will awaken if no other signals are pending.
|
|
51
51
|
*/
|
|
52
52
|
async signal(signalId, data) {
|
|
53
|
-
await this.hotMesh.
|
|
53
|
+
await this.hotMesh.signal(`${this.hotMesh.appId}.wfs.signal`, {
|
|
54
54
|
id: signalId,
|
|
55
55
|
data,
|
|
56
56
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContextType, WorkflowInterceptor } from '../../types/
|
|
1
|
+
import { ContextType, WorkflowInterceptor, ActivityInterceptor } from '../../types/durable';
|
|
2
2
|
import { guid } from '../../modules/utils';
|
|
3
3
|
import { ClientService } from './client';
|
|
4
4
|
import { ConnectionService } from './connection';
|
|
@@ -9,9 +9,9 @@ import { WorkflowService } from './workflow';
|
|
|
9
9
|
import { WorkflowHandleService } from './handle';
|
|
10
10
|
import { didInterrupt } from './workflow/interruption';
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
13
|
-
* Postgres. It offers
|
|
14
|
-
*
|
|
12
|
+
* The Durable service provides a Temporal-compatible workflow framework backed
|
|
13
|
+
* by Postgres. It offers entity-based memory management and composable,
|
|
14
|
+
* fault-tolerant workflows.
|
|
15
15
|
*
|
|
16
16
|
* ## Core Features
|
|
17
17
|
*
|
|
@@ -19,7 +19,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
19
19
|
* Each workflow has a durable JSONB entity that serves as its memory:
|
|
20
20
|
* ```typescript
|
|
21
21
|
* export async function researchAgent(query: string) {
|
|
22
|
-
* const agent = await
|
|
22
|
+
* const agent = await Durable.workflow.entity();
|
|
23
23
|
*
|
|
24
24
|
* // Initialize entity state
|
|
25
25
|
* await agent.set({
|
|
@@ -38,14 +38,14 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
38
38
|
* Spawn and coordinate multiple perspectives/phases:
|
|
39
39
|
* ```typescript
|
|
40
40
|
* // Launch parallel research perspectives
|
|
41
|
-
* await
|
|
41
|
+
* await Durable.workflow.execHook({
|
|
42
42
|
* taskQueue: 'research',
|
|
43
43
|
* workflowName: 'optimisticView',
|
|
44
44
|
* args: [query],
|
|
45
45
|
* signalId: 'optimistic-complete'
|
|
46
46
|
* });
|
|
47
47
|
*
|
|
48
|
-
* await
|
|
48
|
+
* await Durable.workflow.execHook({
|
|
49
49
|
* taskQueue: 'research',
|
|
50
50
|
* workflowName: 'skepticalView',
|
|
51
51
|
* args: [query],
|
|
@@ -54,8 +54,8 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
54
54
|
*
|
|
55
55
|
* // Wait for both perspectives
|
|
56
56
|
* await Promise.all([
|
|
57
|
-
*
|
|
58
|
-
*
|
|
57
|
+
* Durable.workflow.waitFor('optimistic-complete'),
|
|
58
|
+
* Durable.workflow.waitFor('skeptical-complete')
|
|
59
59
|
* ]);
|
|
60
60
|
* ```
|
|
61
61
|
*
|
|
@@ -63,7 +63,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
63
63
|
* Define and execute durable activities with automatic retry:
|
|
64
64
|
* ```typescript
|
|
65
65
|
* // Default: activities use workflow's task queue
|
|
66
|
-
* const activities =
|
|
66
|
+
* const activities = Durable.workflow.proxyActivities<{
|
|
67
67
|
* analyzeDocument: typeof analyzeDocument;
|
|
68
68
|
* validateFindings: typeof validateFindings;
|
|
69
69
|
* }>({
|
|
@@ -83,7 +83,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
83
83
|
* Register activity workers explicitly before workflows start:
|
|
84
84
|
* ```typescript
|
|
85
85
|
* // Register shared activity pool for interceptors
|
|
86
|
-
* await
|
|
86
|
+
* await Durable.registerActivityWorker({
|
|
87
87
|
* connection: {
|
|
88
88
|
* class: Postgres,
|
|
89
89
|
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
@@ -92,7 +92,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
92
92
|
* }, sharedActivities, 'shared-activities');
|
|
93
93
|
*
|
|
94
94
|
* // Register custom activity pool for specific use cases
|
|
95
|
-
* await
|
|
95
|
+
* await Durable.registerActivityWorker({
|
|
96
96
|
* connection: {
|
|
97
97
|
* class: Postgres,
|
|
98
98
|
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
@@ -105,7 +105,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
105
105
|
* Build complex workflows through composition:
|
|
106
106
|
* ```typescript
|
|
107
107
|
* // Start a child workflow
|
|
108
|
-
* const childResult = await
|
|
108
|
+
* const childResult = await Durable.workflow.execChild({
|
|
109
109
|
* taskQueue: 'analysis',
|
|
110
110
|
* workflowName: 'detailedAnalysis',
|
|
111
111
|
* args: [data],
|
|
@@ -117,7 +117,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
117
117
|
* });
|
|
118
118
|
*
|
|
119
119
|
* // Fire-and-forget child workflow
|
|
120
|
-
* await
|
|
120
|
+
* await Durable.workflow.startChild({
|
|
121
121
|
* taskQueue: 'notifications',
|
|
122
122
|
* workflowName: 'sendUpdates',
|
|
123
123
|
* args: [updates]
|
|
@@ -128,7 +128,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
128
128
|
* Add cross-cutting concerns through interceptors that run as durable functions:
|
|
129
129
|
* ```typescript
|
|
130
130
|
* // First register shared activity worker for interceptors
|
|
131
|
-
* await
|
|
131
|
+
* await Durable.registerActivityWorker({
|
|
132
132
|
* connection: {
|
|
133
133
|
* class: Postgres,
|
|
134
134
|
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
@@ -137,11 +137,11 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
137
137
|
* }, { auditLog }, 'interceptor-activities');
|
|
138
138
|
*
|
|
139
139
|
* // Add audit interceptor that uses activities with explicit taskQueue
|
|
140
|
-
*
|
|
140
|
+
* Durable.registerInterceptor({
|
|
141
141
|
* async execute(ctx, next) {
|
|
142
142
|
* try {
|
|
143
143
|
* // Interceptors use explicit taskQueue to prevent per-workflow queues
|
|
144
|
-
* const { auditLog } =
|
|
144
|
+
* const { auditLog } = Durable.workflow.proxyActivities<typeof activities>({
|
|
145
145
|
* activities: { auditLog },
|
|
146
146
|
* taskQueue: 'interceptor-activities', // Explicit shared queue
|
|
147
147
|
* retryPolicy: { maximumAttempts: 3 }
|
|
@@ -156,7 +156,7 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
156
156
|
* return result;
|
|
157
157
|
* } catch (err) {
|
|
158
158
|
* // CRITICAL: Always check for HotMesh interruptions
|
|
159
|
-
* if (
|
|
159
|
+
* if (Durable.didInterrupt(err)) {
|
|
160
160
|
* throw err; // Rethrow for replay system
|
|
161
161
|
* }
|
|
162
162
|
* throw err;
|
|
@@ -165,15 +165,71 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
165
165
|
* });
|
|
166
166
|
* ```
|
|
167
167
|
*
|
|
168
|
+
* ### 7. Activity Interceptors
|
|
169
|
+
* Wrap individual proxied activity calls with cross-cutting logic.
|
|
170
|
+
* Unlike workflow interceptors (which wrap the entire workflow), activity
|
|
171
|
+
* interceptors execute around each `proxyActivities` call. They run inside
|
|
172
|
+
* the workflow's execution context and have access to all Durable workflow
|
|
173
|
+
* methods (`proxyActivities`, `sleepFor`, `waitFor`, `execChild`, etc.).
|
|
174
|
+
* Multiple activity interceptors execute in onion order (first registered
|
|
175
|
+
* is outermost).
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // Simple logging interceptor
|
|
178
|
+
* Durable.registerActivityInterceptor({
|
|
179
|
+
* async execute(activityCtx, workflowCtx, next) {
|
|
180
|
+
* console.log(`Activity ${activityCtx.activityName} starting`);
|
|
181
|
+
* try {
|
|
182
|
+
* const result = await next();
|
|
183
|
+
* console.log(`Activity ${activityCtx.activityName} completed`);
|
|
184
|
+
* return result;
|
|
185
|
+
* } catch (err) {
|
|
186
|
+
* if (Durable.didInterrupt(err)) throw err;
|
|
187
|
+
* console.error(`Activity ${activityCtx.activityName} failed`);
|
|
188
|
+
* throw err;
|
|
189
|
+
* }
|
|
190
|
+
* }
|
|
191
|
+
* });
|
|
192
|
+
*
|
|
193
|
+
* // Interceptor that calls its own proxy activities
|
|
194
|
+
* await Durable.registerActivityWorker({
|
|
195
|
+
* connection: {
|
|
196
|
+
* class: Postgres,
|
|
197
|
+
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
198
|
+
* },
|
|
199
|
+
* taskQueue: 'audit-activities'
|
|
200
|
+
* }, { auditLog }, 'audit-activities');
|
|
201
|
+
*
|
|
202
|
+
* Durable.registerActivityInterceptor({
|
|
203
|
+
* async execute(activityCtx, workflowCtx, next) {
|
|
204
|
+
* try {
|
|
205
|
+
* const { auditLog } = Durable.workflow.proxyActivities<{
|
|
206
|
+
* auditLog: (id: string, action: string) => Promise<void>;
|
|
207
|
+
* }>({
|
|
208
|
+
* taskQueue: 'audit-activities',
|
|
209
|
+
* retryPolicy: { maximumAttempts: 3 }
|
|
210
|
+
* });
|
|
211
|
+
*
|
|
212
|
+
* await auditLog(workflowCtx.get('workflowId'), `before:${activityCtx.activityName}`);
|
|
213
|
+
* const result = await next();
|
|
214
|
+
* await auditLog(workflowCtx.get('workflowId'), `after:${activityCtx.activityName}`);
|
|
215
|
+
* return result;
|
|
216
|
+
* } catch (err) {
|
|
217
|
+
* if (Durable.didInterrupt(err)) throw err;
|
|
218
|
+
* throw err;
|
|
219
|
+
* }
|
|
220
|
+
* }
|
|
221
|
+
* });
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
168
224
|
* ## Basic Usage Example
|
|
169
225
|
*
|
|
170
226
|
* ```typescript
|
|
171
|
-
* import { Client, Worker,
|
|
227
|
+
* import { Client, Worker, Durable } from '@hotmeshio/hotmesh';
|
|
172
228
|
* import { Client as Postgres } from 'pg';
|
|
173
229
|
* import * as activities from './activities';
|
|
174
230
|
*
|
|
175
231
|
* // (Optional) Register shared activity workers for interceptors
|
|
176
|
-
* await
|
|
232
|
+
* await Durable.registerActivityWorker({
|
|
177
233
|
* connection: {
|
|
178
234
|
* class: Postgres,
|
|
179
235
|
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
@@ -204,28 +260,28 @@ import { didInterrupt } from './workflow/interruption';
|
|
|
204
260
|
* args: ['input data'],
|
|
205
261
|
* taskQueue: 'default',
|
|
206
262
|
* workflowName: 'example',
|
|
207
|
-
* workflowId:
|
|
263
|
+
* workflowId: Durable.guid()
|
|
208
264
|
* });
|
|
209
265
|
*
|
|
210
266
|
* // Get result
|
|
211
267
|
* const result = await handle.result();
|
|
212
268
|
*
|
|
213
269
|
* // Cleanup
|
|
214
|
-
* await
|
|
270
|
+
* await Durable.shutdown();
|
|
215
271
|
* ```
|
|
216
272
|
*/
|
|
217
|
-
declare class
|
|
273
|
+
declare class DurableClass {
|
|
218
274
|
/**
|
|
219
275
|
* @private
|
|
220
276
|
*/
|
|
221
277
|
constructor();
|
|
222
278
|
/**
|
|
223
|
-
* The
|
|
279
|
+
* The Durable `Client` service is functionally
|
|
224
280
|
* equivalent to the Temporal `Client` service.
|
|
225
281
|
*/
|
|
226
282
|
static Client: typeof ClientService;
|
|
227
283
|
/**
|
|
228
|
-
* The
|
|
284
|
+
* The Durable `Connection` service is functionally
|
|
229
285
|
* equivalent to the Temporal `Connection` service.
|
|
230
286
|
*/
|
|
231
287
|
static Connection: typeof ConnectionService;
|
|
@@ -241,11 +297,11 @@ declare class MemFlowClass {
|
|
|
241
297
|
* The Handle provides methods to interact with a running
|
|
242
298
|
* workflow. This includes exporting the workflow, sending signals, and
|
|
243
299
|
* querying the state of the workflow. An instance of the Handle service
|
|
244
|
-
* is typically accessed via the
|
|
300
|
+
* is typically accessed via the Durable.Client class (workflow.getHandle).
|
|
245
301
|
*/
|
|
246
302
|
static Handle: typeof WorkflowHandleService;
|
|
247
303
|
/**
|
|
248
|
-
* The
|
|
304
|
+
* The Durable `Worker` service is functionally
|
|
249
305
|
* equivalent to the Temporal `Worker` service.
|
|
250
306
|
*/
|
|
251
307
|
static Worker: typeof WorkerService;
|
|
@@ -261,7 +317,7 @@ declare class MemFlowClass {
|
|
|
261
317
|
* async sendEmail(to: string, msg: string) { /* ... *\/ }
|
|
262
318
|
* };
|
|
263
319
|
*
|
|
264
|
-
* await
|
|
320
|
+
* await Durable.registerActivityWorker({
|
|
265
321
|
* connection: {
|
|
266
322
|
* class: Postgres,
|
|
267
323
|
* options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
|
|
@@ -271,7 +327,7 @@ declare class MemFlowClass {
|
|
|
271
327
|
*
|
|
272
328
|
* // Workflow worker (can be on different server)
|
|
273
329
|
* async function orderWorkflow(amount: number) {
|
|
274
|
-
* const { processPayment, sendEmail } =
|
|
330
|
+
* const { processPayment, sendEmail } = Durable.workflow.proxyActivities<{
|
|
275
331
|
* processPayment: (amount: number) => Promise<string>;
|
|
276
332
|
* sendEmail: (to: string, msg: string) => Promise<void>;
|
|
277
333
|
* }>({
|
|
@@ -284,7 +340,7 @@ declare class MemFlowClass {
|
|
|
284
340
|
* return result;
|
|
285
341
|
* }
|
|
286
342
|
*
|
|
287
|
-
* await
|
|
343
|
+
* await Durable.Worker.create({
|
|
288
344
|
* connection: { class: Postgres, options: { connectionString: '...' } },
|
|
289
345
|
* taskQueue: 'orders',
|
|
290
346
|
* workflow: orderWorkflow
|
|
@@ -293,7 +349,7 @@ declare class MemFlowClass {
|
|
|
293
349
|
*/
|
|
294
350
|
static registerActivityWorker: typeof WorkerService.registerActivityWorker;
|
|
295
351
|
/**
|
|
296
|
-
* The
|
|
352
|
+
* The Durable `workflow` service is functionally
|
|
297
353
|
* equivalent to the Temporal `Workflow` service
|
|
298
354
|
* with additional methods for managing workflows,
|
|
299
355
|
* including: `execChild`, `waitFor`, `sleep`, etc
|
|
@@ -303,7 +359,7 @@ declare class MemFlowClass {
|
|
|
303
359
|
* Checks if an error is a HotMesh reserved error type that indicates
|
|
304
360
|
* a workflow interruption rather than a true error condition.
|
|
305
361
|
*
|
|
306
|
-
* @see {@link
|
|
362
|
+
* @see {@link didInterrupt} for detailed documentation
|
|
307
363
|
*/
|
|
308
364
|
static didInterrupt: typeof didInterrupt;
|
|
309
365
|
private static interceptorService;
|
|
@@ -313,9 +369,45 @@ declare class MemFlowClass {
|
|
|
313
369
|
*/
|
|
314
370
|
static registerInterceptor(interceptor: WorkflowInterceptor): void;
|
|
315
371
|
/**
|
|
316
|
-
* Clear all registered workflow
|
|
372
|
+
* Clear all registered interceptors (both workflow and activity)
|
|
317
373
|
*/
|
|
318
374
|
static clearInterceptors(): void;
|
|
375
|
+
/**
|
|
376
|
+
* Register an activity interceptor that wraps individual proxied
|
|
377
|
+
* activity calls within workflows. Interceptors execute in registration
|
|
378
|
+
* order (first registered is outermost) using the onion pattern.
|
|
379
|
+
*
|
|
380
|
+
* Activity interceptors run inside the workflow's execution context
|
|
381
|
+
* and have access to all Durable workflow methods (`proxyActivities`,
|
|
382
|
+
* `sleepFor`, `waitFor`, `execChild`, etc.). The `activityCtx` parameter
|
|
383
|
+
* provides `activityName`, `args`, and `options` for the call being
|
|
384
|
+
* intercepted. The `workflowCtx` map provides workflow metadata
|
|
385
|
+
* (`workflowId`, `workflowName`, `namespace`, etc.).
|
|
386
|
+
*
|
|
387
|
+
* @param interceptor The activity interceptor to register
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* Durable.registerActivityInterceptor({
|
|
392
|
+
* async execute(activityCtx, workflowCtx, next) {
|
|
393
|
+
* const start = Date.now();
|
|
394
|
+
* try {
|
|
395
|
+
* const result = await next();
|
|
396
|
+
* console.log(`${activityCtx.activityName} took ${Date.now() - start}ms`);
|
|
397
|
+
* return result;
|
|
398
|
+
* } catch (err) {
|
|
399
|
+
* if (Durable.didInterrupt(err)) throw err;
|
|
400
|
+
* throw err;
|
|
401
|
+
* }
|
|
402
|
+
* }
|
|
403
|
+
* });
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
static registerActivityInterceptor(interceptor: ActivityInterceptor): void;
|
|
407
|
+
/**
|
|
408
|
+
* Clear all registered activity interceptors
|
|
409
|
+
*/
|
|
410
|
+
static clearActivityInterceptors(): void;
|
|
319
411
|
/**
|
|
320
412
|
* Generate a unique identifier for workflow IDs
|
|
321
413
|
*/
|
|
@@ -326,5 +418,5 @@ declare class MemFlowClass {
|
|
|
326
418
|
*/
|
|
327
419
|
static shutdown(): Promise<void>;
|
|
328
420
|
}
|
|
329
|
-
export {
|
|
421
|
+
export { DurableClass as Durable };
|
|
330
422
|
export type { ContextType };
|