@adonis-agora/durable 0.25.0 → 0.26.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/CHANGELOG.md +38 -0
- package/dist/assets/spa/assets/{index-CV232q_B.js → index-ClD9G7pa.js} +10 -10
- package/dist/assets/spa/assets/index-ClD9G7pa.js.map +1 -0
- package/dist/assets/spa/assets/index-Ds6dCWMa.css +1 -0
- package/dist/assets/spa/index.html +2 -2
- package/dist/providers/durable_provider.d.ts.map +1 -1
- package/dist/providers/durable_provider.js +25 -0
- package/dist/providers/durable_provider.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +1 -1
- package/dist/src/dashboard/index.js +1 -1
- package/dist/src/engine.d.ts +7 -0
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +11 -0
- package/dist/src/engine.js.map +1 -1
- package/dist/src/event-trigger-bridge.d.ts +32 -0
- package/dist/src/event-trigger-bridge.d.ts.map +1 -0
- package/dist/src/event-trigger-bridge.js +122 -0
- package/dist/src/event-trigger-bridge.js.map +1 -0
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/workflow-discovery.d.ts.map +1 -1
- package/dist/src/workflow-discovery.js +13 -1
- package/dist/src/workflow-discovery.js.map +1 -1
- package/dist/src/workflow-events.d.ts +85 -0
- package/dist/src/workflow-events.d.ts.map +1 -0
- package/dist/src/workflow-events.js +58 -0
- package/dist/src/workflow-events.js.map +1 -0
- package/package.json +5 -2
- package/skills/durable-cluster/SKILL.md +308 -0
- package/skills/durable-determinism/SKILL.md +260 -0
- package/skills/durable-reliability/SKILL.md +352 -0
- package/skills/durable-setup/SKILL.md +293 -0
- package/skills/durable-transports-stores/SKILL.md +296 -0
- package/skills/durable-workflows/SKILL.md +399 -0
- package/dist/assets/spa/assets/index-CTsYsi6p.css +0 -1
- package/dist/assets/spa/assets/index-CV232q_B.js.map +0 -1
package/dist/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.26.0';
|
|
3
3
|
// --- engine + core primitives -----------------------------------------------
|
|
4
4
|
export * from './admission.js';
|
|
5
5
|
export * from './control-flow-signal.js';
|
|
@@ -27,6 +27,8 @@ export * from './scheduler.js';
|
|
|
27
27
|
export * from './search-attributes.js';
|
|
28
28
|
export * from './tokens.js';
|
|
29
29
|
export * from './workflow-ref.js';
|
|
30
|
+
export * from './workflow-events.js';
|
|
31
|
+
export * from './event-trigger-bridge.js';
|
|
30
32
|
export * from './workflow-discovery.js';
|
|
31
33
|
export { BaseWorkflow, setWorkflowEngineResolver, } from './base-workflow.js';
|
|
32
34
|
export { getCurrentWorkflowCtx, workflowAls } from './workflow-als.js';
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-discovery.d.ts","sourceRoot":"","sources":["../../src/workflow-discovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-discovery.d.ts","sourceRoot":"","sources":["../../src/workflow-discovery.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,KAAK,YAAY,EAAmC,MAAM,mBAAmB,CAAC;AAEvF;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAAG,UAAU;IAC9C,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC/D,CAAC;AAEF;2CAC2C;AAC3C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,uBAAuB,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,IAAI,EAAE,uBAAuB,KAC1B,uBAAuB,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,uBAAuB,CAAC,WAAW,CAAC,CAAC,CAAC;AAQ1F;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,oBAA6C,GACrD,OAAO,CAAC,OAAO,CAAC,CAgClB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAY9E;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAyBlF;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,YAAY,EAAE,CAAC,CAIzB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAErF;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,YAAY,EAAE,CAAC,CAezB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir } from 'node:fs/promises';
|
|
2
2
|
import { extname, join } from 'node:path';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { eventTriggerCanonicalName, workflowEvents } from './workflow-events.js';
|
|
4
5
|
import { workflowMeta, workflowSchedules } from './workflow-ref.js';
|
|
5
6
|
function defaultWorkflowFactory(ctor) {
|
|
6
7
|
return new ctor();
|
|
@@ -22,10 +23,21 @@ export async function registerWorkflowClass(engine, cls, factory = defaultWorkfl
|
|
|
22
23
|
return false;
|
|
23
24
|
const Ctor = cls;
|
|
24
25
|
const instance = await factory(Ctor);
|
|
26
|
+
// Colocated `static on` event triggers: exact-string ones register under the engine's
|
|
27
|
+
// `onEvent` (the bridge publishes the canonical name); regex/any ones are started
|
|
28
|
+
// directly by the bridge. Both are recorded so the provider can attach the bridge.
|
|
29
|
+
const events = workflowEvents(cls);
|
|
30
|
+
if (events.length > 0)
|
|
31
|
+
engine.registerEventTriggers(events);
|
|
32
|
+
const exactNames = events
|
|
33
|
+
.map(eventTriggerCanonicalName)
|
|
34
|
+
.filter((name) => name !== null);
|
|
25
35
|
engine.register(meta.name, meta.version, (ctx, input) => Promise.resolve(instance.run(ctx, input)), {
|
|
26
36
|
...(meta.tags ? { tags: meta.tags } : {}),
|
|
27
37
|
...(meta.executionTimeout !== undefined ? { executionTimeout: meta.executionTimeout } : {}),
|
|
28
|
-
...(meta.onEvent
|
|
38
|
+
...(meta.onEvent || exactNames.length > 0
|
|
39
|
+
? { onEvent: [...(meta.onEvent ?? []), ...exactNames] }
|
|
40
|
+
: {}),
|
|
29
41
|
...(meta.singleton ? { singleton: meta.singleton } : {}),
|
|
30
42
|
});
|
|
31
43
|
// Collect any colocated `static schedule` on the class so the worker loop can fire it alongside the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-discovery.js","sourceRoot":"","sources":["../../src/workflow-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAqB,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAgCvF,SAAS,sBAAsB,CAC7B,IAA6B;IAE7B,OAAO,IAAI,IAAI,EAAE,CAAC;AACpB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAsB,EACtB,GAAY,EACZ,UAAgC,sBAAsB;IAEtD,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,IAAI,GAAG,GAA8B,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,CAAC,QAAQ,CACb,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,OAAO,EACZ,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,EACzD;QACE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3F,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-discovery.js","sourceRoot":"","sources":["../../src/workflow-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type WorkflowClass } from './workflow-ref.js';
|
|
2
|
+
/**
|
|
3
|
+
* Event-triggered workflows: a workflow class declares which **external** events start a
|
|
4
|
+
* run — the Adonis emitter (`@OnEvent`) and/or the `@adonis-agora/diagnostics` bus
|
|
5
|
+
* (`@OnDiagnostic`). The durable provider bridges those buses into the engine (a fresh
|
|
6
|
+
* run per matching event, payload as input), mirroring how `static schedule`/`@Scheduled`
|
|
7
|
+
* bridge time into the engine.
|
|
8
|
+
*
|
|
9
|
+
* Two authoring forms, exactly like schedules:
|
|
10
|
+
* - `@OnEvent(...)` / `@OnDiagnostic(...)` decorators (named params), and
|
|
11
|
+
* - a colocated `static on = [...]` literal — the decorators only stamp `static on`.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* @OnDiagnostic({ lib: 'payments', event: 'payment.succeeded' })
|
|
15
|
+
* @OnEvent({ event: 'agora:payments:payment.succeeded' })
|
|
16
|
+
* export class ProcessPaymentWorkflow extends BaseWorkflow {
|
|
17
|
+
* static workflow = { name: 'process-payment' }
|
|
18
|
+
* async run(ctx, input) { /* input = the event payload *\/ }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/** A trigger fed by the AdonisJS emitter. Exact event names only (the emitter has no wildcard). */
|
|
23
|
+
export interface EmitterTriggerConfig {
|
|
24
|
+
/** The emitter event name, e.g. `'agora:payments:payment.succeeded'`. */
|
|
25
|
+
event: string;
|
|
26
|
+
}
|
|
27
|
+
/** A trigger fed by the `@adonis-agora/diagnostics` bus (`agora:<lib>:<event>` channels). */
|
|
28
|
+
export interface DiagnosticsTriggerConfig {
|
|
29
|
+
/** The diagnostics lib to listen to, e.g. `'payments'`. */
|
|
30
|
+
lib: string;
|
|
31
|
+
/**
|
|
32
|
+
* Exact event within the lib (e.g. `'payment.succeeded'`), a regex over events, or omit
|
|
33
|
+
* to match every event of the lib.
|
|
34
|
+
*/
|
|
35
|
+
event?: string | RegExp;
|
|
36
|
+
}
|
|
37
|
+
/** One colocated event trigger (the `static on` entry), discriminated by source. */
|
|
38
|
+
export type WorkflowEventTrigger = ({
|
|
39
|
+
source: 'emitter';
|
|
40
|
+
} & EmitterTriggerConfig) | ({
|
|
41
|
+
source: 'diagnostics';
|
|
42
|
+
} & DiagnosticsTriggerConfig);
|
|
43
|
+
/** A normalized trigger: the workflow name filled in, ready for the bridge/engine. */
|
|
44
|
+
export type NormalizedEventTrigger = ({
|
|
45
|
+
source: 'emitter';
|
|
46
|
+
} & EmitterTriggerConfig & {
|
|
47
|
+
workflow: string;
|
|
48
|
+
}) | ({
|
|
49
|
+
source: 'diagnostics';
|
|
50
|
+
} & DiagnosticsTriggerConfig & {
|
|
51
|
+
workflow: string;
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* The canonical engine event name an EXACT trigger maps to — the name the workflow
|
|
55
|
+
* registers under `onEvent` and the bridge publishes with:
|
|
56
|
+
* - emitter: the literal event name,
|
|
57
|
+
* - diagnostics: the channel name `agora:<lib>:<event>`.
|
|
58
|
+
* Returns `null` for regex/any triggers (the bridge starts those directly).
|
|
59
|
+
*/
|
|
60
|
+
export declare function eventTriggerCanonicalName(trigger: NormalizedEventTrigger): string | null;
|
|
61
|
+
/** Read a class's colocated event triggers (`static on`) and fill the workflow name. */
|
|
62
|
+
export declare function workflowEvents(target: unknown): NormalizedEventTrigger[];
|
|
63
|
+
/**
|
|
64
|
+
* Listen to an exact event on the **AdonisJS emitter** and start this workflow with the
|
|
65
|
+
* event payload. Named params:
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* @OnEvent({ event: 'agora:payments:payment.succeeded' })
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function OnEvent(config: EmitterTriggerConfig): ClassDecorator;
|
|
72
|
+
/**
|
|
73
|
+
* Listen to an event on the **`@adonis-agora/diagnostics` bus** (`agora:<lib>:<event>`
|
|
74
|
+
* channel) and start this workflow with the event payload. Named params:
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* @OnDiagnostic({ lib: 'payments', event: 'payment.succeeded' }) // exact
|
|
78
|
+
* @OnDiagnostic({ lib: 'payments', event: /^payment\./ }) // regex over events
|
|
79
|
+
* @OnDiagnostic({ lib: 'payments' }) // every event of the lib
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function OnDiagnostic(config: DiagnosticsTriggerConfig): ClassDecorator;
|
|
83
|
+
/** Workflow-class shape used by the bridge to start a run on a matching event. */
|
|
84
|
+
export type EventTriggeredWorkflow = WorkflowClass;
|
|
85
|
+
//# sourceMappingURL=workflow-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-events.d.ts","sourceRoot":"","sources":["../../src/workflow-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAgB,MAAM,mBAAmB,CAAC;AAErE;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,mGAAmG;AACnG,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,6FAA6F;AAC7F,MAAM,WAAW,wBAAwB;IACvC,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,oFAAoF;AACpF,MAAM,MAAM,oBAAoB,GAC5B,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,oBAAoB,CAAC,GAC9C,CAAC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,wBAAwB,CAAC,CAAC;AAE3D,sFAAsF;AACtF,MAAM,MAAM,sBAAsB,GAC9B,CAAC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,oBAAoB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GACrE,CAAC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAIxF;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,sBAAsB,EAAE,CAOxE;AAUD;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,wBAAwB,GAAG,cAAc,CAE7E;AAED,kFAAkF;AAClF,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { workflowMeta } from './workflow-ref.js';
|
|
2
|
+
/**
|
|
3
|
+
* The canonical engine event name an EXACT trigger maps to — the name the workflow
|
|
4
|
+
* registers under `onEvent` and the bridge publishes with:
|
|
5
|
+
* - emitter: the literal event name,
|
|
6
|
+
* - diagnostics: the channel name `agora:<lib>:<event>`.
|
|
7
|
+
* Returns `null` for regex/any triggers (the bridge starts those directly).
|
|
8
|
+
*/
|
|
9
|
+
export function eventTriggerCanonicalName(trigger) {
|
|
10
|
+
if (trigger.source === 'emitter')
|
|
11
|
+
return trigger.event;
|
|
12
|
+
if (trigger.event === undefined)
|
|
13
|
+
return null;
|
|
14
|
+
return typeof trigger.event === 'string' ? `agora:${trigger.lib}:${trigger.event}` : null;
|
|
15
|
+
}
|
|
16
|
+
/** Read a class's colocated event triggers (`static on`) and fill the workflow name. */
|
|
17
|
+
export function workflowEvents(target) {
|
|
18
|
+
const meta = workflowMeta(target);
|
|
19
|
+
if (!meta)
|
|
20
|
+
return [];
|
|
21
|
+
const raw = target.on;
|
|
22
|
+
if (!raw)
|
|
23
|
+
return [];
|
|
24
|
+
const arr = Array.isArray(raw) ? raw : [raw];
|
|
25
|
+
return arr.map((t) => ({ ...t, workflow: meta.name }));
|
|
26
|
+
}
|
|
27
|
+
/** Stamp `static on` (prepend, mirroring {@link Scheduled}). */
|
|
28
|
+
function stampOn(target, config) {
|
|
29
|
+
const cls = target;
|
|
30
|
+
const existing = cls.on === undefined ? [] : Array.isArray(cls.on) ? cls.on : [cls.on];
|
|
31
|
+
const added = Array.isArray(config) ? config : [config];
|
|
32
|
+
cls.on = [...added, ...existing];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Listen to an exact event on the **AdonisJS emitter** and start this workflow with the
|
|
36
|
+
* event payload. Named params:
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* @OnEvent({ event: 'agora:payments:payment.succeeded' })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function OnEvent(config) {
|
|
43
|
+
return (target) => stampOn(target, { source: 'emitter', ...config });
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Listen to an event on the **`@adonis-agora/diagnostics` bus** (`agora:<lib>:<event>`
|
|
47
|
+
* channel) and start this workflow with the event payload. Named params:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* @OnDiagnostic({ lib: 'payments', event: 'payment.succeeded' }) // exact
|
|
51
|
+
* @OnDiagnostic({ lib: 'payments', event: /^payment\./ }) // regex over events
|
|
52
|
+
* @OnDiagnostic({ lib: 'payments' }) // every event of the lib
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function OnDiagnostic(config) {
|
|
56
|
+
return (target) => stampOn(target, { source: 'diagnostics', ...config });
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=workflow-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-events.js","sourceRoot":"","sources":["../../src/workflow-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAkDrE;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA+B;IACvE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5F,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,GAAG,GAAI,MAAiE,CAAC,EAAE,CAAC;IAClF,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,SAAS,OAAO,CAAC,MAAe,EAAE,MAAqD;IACrF,MAAM,GAAG,GAAG,MAAgE,CAAC;IAC7E,MAAM,QAAQ,GAAG,GAAG,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,MAA4B;IAClD,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AACvE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,MAAgC;IAC3D,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AAC3E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/durable",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Durable cross-app workflows for AdonisJS — deterministic replay engine, AdonisJS binding, ace commands, dashboard, OpenTelemetry, Telescope, testing harness and Redis admission, all in one package. Part of the Agora ecosystem.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
},
|
|
102
102
|
"files": [
|
|
103
103
|
"dist/",
|
|
104
|
+
"skills",
|
|
104
105
|
"README.md",
|
|
105
106
|
"CHANGELOG.md"
|
|
106
107
|
],
|
|
@@ -164,10 +165,11 @@
|
|
|
164
165
|
"bullmq": "5.81.3",
|
|
165
166
|
"cron-parser": "4.9.0",
|
|
166
167
|
"ioredis": "5.11.1",
|
|
168
|
+
"@tanstack/intent": "^0.3.2",
|
|
167
169
|
"typescript": "5.9.3",
|
|
168
170
|
"vitest": "3.2.7",
|
|
169
171
|
"zod": "3.25.76",
|
|
170
|
-
"@adonis-agora/durable-dashboard": "0.2.
|
|
172
|
+
"@adonis-agora/durable-dashboard": "0.2.2"
|
|
171
173
|
},
|
|
172
174
|
"engines": {
|
|
173
175
|
"node": ">=20.6.0"
|
|
@@ -183,6 +185,7 @@
|
|
|
183
185
|
"opentelemetry",
|
|
184
186
|
"telescope",
|
|
185
187
|
"testing",
|
|
188
|
+
"tanstack-intent",
|
|
186
189
|
"agora"
|
|
187
190
|
],
|
|
188
191
|
"scripts": {
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: durable-cluster
|
|
3
|
+
description: >-
|
|
4
|
+
Scale @adonis-agora/durable across processes and runtimes: the
|
|
5
|
+
role-discriminated config (standalone, control-plane, store-less tenant with
|
|
6
|
+
compile-time store exclusion), durable:work vs durable:worker entrypoints,
|
|
7
|
+
WorkerRuntime + RedisWorkerRegistry descriptors, handshake negotiation
|
|
8
|
+
(compatible/degraded/incompatible) and capability routing (requires → blocked
|
|
9
|
+
runs), layered tenant auth (signTenantToken/hmacTenantVerifier/verifyTenant),
|
|
10
|
+
runGateway as the role-portable read/control surface, namespace vs partition,
|
|
11
|
+
and Python (durable-worker) / NestJS interop over the bullmq wire. Use when
|
|
12
|
+
splitting web from workers, isolating tenants, or adding polyglot workers.
|
|
13
|
+
license: MIT
|
|
14
|
+
metadata:
|
|
15
|
+
type: core
|
|
16
|
+
library: "@adonis-agora/durable"
|
|
17
|
+
library_version: "0.25.0"
|
|
18
|
+
framework: adonisjs
|
|
19
|
+
sources:
|
|
20
|
+
- 'DavideCarvalho/adonis-durable:docs/cluster/index.mdx'
|
|
21
|
+
- 'DavideCarvalho/adonis-durable:docs/cluster/roles-and-config.mdx'
|
|
22
|
+
- 'DavideCarvalho/adonis-durable:docs/cluster/thin-workers.mdx'
|
|
23
|
+
- 'DavideCarvalho/adonis-durable:docs/cluster/handshake.mdx'
|
|
24
|
+
- 'DavideCarvalho/adonis-durable:docs/cluster/interop.mdx'
|
|
25
|
+
- 'DavideCarvalho/adonis-durable:docs/python.mdx'
|
|
26
|
+
- 'DavideCarvalho/adonis-durable:docs/concepts/tenancy.mdx'
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# Cluster topologies & interop
|
|
30
|
+
|
|
31
|
+
`config/durable.ts` is a **role-discriminated union**: one `role` field picks
|
|
32
|
+
the topology and TypeScript narrows the rest. Workflows (`app/workflows`) and
|
|
33
|
+
steps (`app/steps`) are byte-identical across every role — only where bodies
|
|
34
|
+
execute changes.
|
|
35
|
+
|
|
36
|
+
| Role | Owns the store | Executes bodies | Serves HTTP |
|
|
37
|
+
|------|:---:|:---:|:---:|
|
|
38
|
+
| `standalone` (default — omit `role`) | ✅ | ✅ embedded | optional |
|
|
39
|
+
| `control-plane` | ✅ required | ❌ | optional |
|
|
40
|
+
| `tenant` | ❌ typed `never` | worker pods ✅ | api/dashboard pod |
|
|
41
|
+
|
|
42
|
+
## Setup
|
|
43
|
+
|
|
44
|
+
Split fleet: a pure coordinator plus store-less tenant workers.
|
|
45
|
+
|
|
46
|
+
```ts title="config/durable.ts (control plane)"
|
|
47
|
+
import { defineConfig, stores, transports } from '@adonis-agora/durable'
|
|
48
|
+
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
role: 'control-plane',
|
|
51
|
+
transport: 'bullmq', // cross-ecosystem wire; queue/db work for Adonis-only splits
|
|
52
|
+
transports: { bullmq: transports.bullmq({ connection: { host: '127.0.0.1', port: 6379 } }) },
|
|
53
|
+
store: 'lucid',
|
|
54
|
+
stores: { lucid: stores.lucid({ connection: 'pg' }) },
|
|
55
|
+
verifyTenant: hmacTenantVerifier(process.env.DURABLE_TENANT_SECRET!),
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ts title="config/durable.ts (tenant worker pod)"
|
|
60
|
+
import { defineConfig, transports } from '@adonis-agora/durable'
|
|
61
|
+
|
|
62
|
+
export default defineConfig({
|
|
63
|
+
role: 'tenant',
|
|
64
|
+
transport: 'bullmq',
|
|
65
|
+
transports: { bullmq: transports.bullmq({ connection: process.env.REDIS_URL }) },
|
|
66
|
+
partition: 'acme-corp', // which pool this pod serves (required)
|
|
67
|
+
tenant: { token: process.env.DURABLE_TENANT_TOKEN }, // its signed claim
|
|
68
|
+
requestTimeoutMs: 10_000,
|
|
69
|
+
// store: 'lucid' // ❌ COMPILE ERROR — a tenant config cannot name a store
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```bash title="entrypoints"
|
|
74
|
+
node ace durable:work # store-backed loop: pending, recovery, timers, sweeps, schedules
|
|
75
|
+
node ace durable:worker # store-less tenant loop: serves app/steps, heartbeats, drains
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Isolation is three-layered: type (`store` is `never` on tenant), container (no
|
|
79
|
+
store binding registered), object (`WorkerRuntime`/`ProxyRunGateway` have no
|
|
80
|
+
store field).
|
|
81
|
+
|
|
82
|
+
## Core patterns
|
|
83
|
+
|
|
84
|
+
### Pattern 1 — role-portable code via `runGateway`
|
|
85
|
+
|
|
86
|
+
Every role exposes the same read/control surface; identical controller code runs
|
|
87
|
+
on a store pod (direct reads) or a store-less pod (wire requests answered by the
|
|
88
|
+
control plane's `RunRequestResponder`):
|
|
89
|
+
|
|
90
|
+
```ts title="app/controllers/runs_controller.ts"
|
|
91
|
+
import { runGateway } from '@adonis-agora/durable/services/main'
|
|
92
|
+
|
|
93
|
+
export default class RunsController {
|
|
94
|
+
async show({ params, response }: HttpContext) {
|
|
95
|
+
const run = await runGateway.getRun(params.id)
|
|
96
|
+
if (!run) return response.notFound()
|
|
97
|
+
return response.ok({ run, timeline: await runGateway.getCheckpoints(params.id) })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async approve({ params, response }: HttpContext) {
|
|
101
|
+
await runGateway.signal(params.id, `approve:${params.id}`, { by: 'ops' })
|
|
102
|
+
return response.accepted({})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Verbs: `getRun`, `listRuns`, `getCheckpoints`, `getRunChildren`,
|
|
108
|
+
`getSearchAttributes`, `start`, `signal`, `cancel`, `redispatchPending`,
|
|
109
|
+
`workerHealth`, `subscribe`, `topology()`. Never branch on `role`.
|
|
110
|
+
Source: `docs/cluster/roles-and-config.mdx`.
|
|
111
|
+
|
|
112
|
+
### Pattern 2 — capability-aware routing and blocked runs
|
|
113
|
+
|
|
114
|
+
A step can require capabilities; dispatch goes only to workers advertising them.
|
|
115
|
+
No capable live worker → the run parks **blocked** (first-class status, visible
|
|
116
|
+
in the dashboard, auto-resumes when a capable worker registers):
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
export const settlePayment = defineStep(
|
|
120
|
+
'settle-payment',
|
|
121
|
+
async (input: { orderId: number }) => ({ /* … */ }),
|
|
122
|
+
{ requires: ['saga', 'search-attributes'] },
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Workers advertise a `WorkerDescriptor` (runtime, SDK version, protocol range,
|
|
127
|
+
capabilities, steps/workflows served, partition); the control plane negotiates
|
|
128
|
+
protocol-major overlap + capability intersection into compatible / degraded /
|
|
129
|
+
incompatible. An incompatible worker is never handed a task — structured
|
|
130
|
+
`protocol.incompatible` / `capability.unavailable` diagnostics carry both
|
|
131
|
+
descriptors and the delta. Source: `docs/cluster/handshake.mdx`.
|
|
132
|
+
|
|
133
|
+
### Pattern 3 — building a thin worker by hand
|
|
134
|
+
|
|
135
|
+
Outside an Adonis app (standalone Node service), use the `/worker` subpath — it
|
|
136
|
+
imports zero store code:
|
|
137
|
+
|
|
138
|
+
```ts title="worker.ts"
|
|
139
|
+
import { RedisWorkerRegistry, WorkerRuntime } from '@adonis-agora/durable/worker'
|
|
140
|
+
import { transports } from '@adonis-agora/durable'
|
|
141
|
+
import { Redis } from 'ioredis'
|
|
142
|
+
|
|
143
|
+
const transport = await transports.bullmq({ connection: process.env.REDIS_URL })({ app })
|
|
144
|
+
const runtime = new WorkerRuntime({
|
|
145
|
+
transport,
|
|
146
|
+
partition: 'acme-corp',
|
|
147
|
+
registry: new RedisWorkerRegistry(new Redis(process.env.REDIS_URL!), { ownsConnection: true }),
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
runtime.registerStep('billing.charge', async (input, log) => {
|
|
151
|
+
log.info('charging', input)
|
|
152
|
+
return { chargeId: await stripe.charge(input as ChargeInput) }
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
await runtime.start()
|
|
156
|
+
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
|
157
|
+
process.once(signal, () => void runtime.stop())
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
A worker that must execute workflow turns registers their BODIES explicitly —
|
|
162
|
+
synchronous replay functions returning commands. Source:
|
|
163
|
+
`docs/cluster/thin-workers.mdx`.
|
|
164
|
+
|
|
165
|
+
### Pattern 4 — Python workers on the same control plane
|
|
166
|
+
|
|
167
|
+
The BullMQ transport speaks the aviary wire byte-for-byte, so a Python worker
|
|
168
|
+
can serve steps (or whole workflows) for an AdoniJS control plane over the same
|
|
169
|
+
Redis:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pip install "durable-worker[redis]"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```python title="worker.py"
|
|
176
|
+
import asyncio
|
|
177
|
+
from durable_worker import Worker
|
|
178
|
+
from durable_worker.redis_runner import run_redis_worker
|
|
179
|
+
|
|
180
|
+
worker = Worker()
|
|
181
|
+
|
|
182
|
+
@worker.step("payments.charge-card")
|
|
183
|
+
async def charge(data):
|
|
184
|
+
res = await stripe.charge(data["orderId"], data["amountCents"])
|
|
185
|
+
return {"chargeId": res.id}
|
|
186
|
+
|
|
187
|
+
async def main():
|
|
188
|
+
await run_redis_worker(worker, connection="redis://localhost:6379")
|
|
189
|
+
await asyncio.Event().wait()
|
|
190
|
+
|
|
191
|
+
asyncio.run(main())
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Connection and prefix must match on both sides; sharing a broker across
|
|
195
|
+
deployments means setting the same `partition` on both sides (queues become
|
|
196
|
+
`<name>@<partition>`). Start Python workflows from Adonis by name:
|
|
197
|
+
`engine.start('pipeline', input, id)` — if no worker serves the name, the start
|
|
198
|
+
fails fast rather than hanging. Source: `docs/python.mdx`,
|
|
199
|
+
`docs/cluster/interop.mdx`.
|
|
200
|
+
|
|
201
|
+
## Common mistakes
|
|
202
|
+
|
|
203
|
+
### HIGH launching `durable:worker` on a store-backed role
|
|
204
|
+
|
|
205
|
+
Wrong:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
node ace durable:worker # on a standalone/control-plane pod
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Correct:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
node ace durable:work # the store-backed dispatch/recovery/timers/schedules loop
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Mechanism: `durable:worker` boots the store-less `WorkerRuntime`; on a role that
|
|
218
|
+
owns a store it logs a warning and refuses — the two commands are different
|
|
219
|
+
topologies, not aliases. Source: `docs/cli.mdx`.
|
|
220
|
+
|
|
221
|
+
### HIGH assuming `durable:worker` pods carry workflow bodies
|
|
222
|
+
|
|
223
|
+
Wrong:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# expecting a tenant pod to execute workflow turns because app/workflows exists
|
|
227
|
+
node ace durable:worker
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Correct:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
runtime.registerWorkflow('checkout', (ctx, input) => {
|
|
234
|
+
const paid = ctx.step('billing.charge', input)
|
|
235
|
+
return { paid }
|
|
236
|
+
})
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Mechanism: the command registers workflow NAMES from `app/workflows` (routing +
|
|
240
|
+
descriptor) but not their bodies; turn execution needs explicit registration,
|
|
241
|
+
and turns are synchronous replay functions by design. Source:
|
|
242
|
+
`docs/cluster/thin-workers.mdx` (warning callout).
|
|
243
|
+
|
|
244
|
+
### MEDIUM leaving a hand-built WorkerRuntime on the default NoopWorkerRegistry
|
|
245
|
+
|
|
246
|
+
Wrong:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const runtime = new WorkerRuntime({ transport, partition: 'acme' }) // registry defaults to noop
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Correct:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const runtime = new WorkerRuntime({
|
|
256
|
+
transport,
|
|
257
|
+
partition: 'acme',
|
|
258
|
+
registry: new RedisWorkerRegistry(redis, { ownsConnection: true }),
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Mechanism: without a registry the descriptor/heartbeat go nowhere — the worker
|
|
263
|
+
executes fine but is invisible to capability-aware routing, so any step with
|
|
264
|
+
`requires` will never be dispatched to it. Source:
|
|
265
|
+
`docs/cluster/thin-workers.mdx` options table.
|
|
266
|
+
|
|
267
|
+
### MEDIUM letting a partition-less local process join a shared broker
|
|
268
|
+
|
|
269
|
+
Wrong:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
// laptop dev against the production Redis:
|
|
273
|
+
transports.bullmq({ connection: REDIS_URL })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Correct:
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
transports.bullmq({ connection: REDIS_URL, partition: 'david-dev' })
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Mechanism: a worker on no partition serves the bare `<name>` tokens — exactly
|
|
283
|
+
the queues the deployed fleet consumes — so the local process steals production
|
|
284
|
+
tasks; docs call this out under Tenancy pitfalls ("competes for, and steals, the
|
|
285
|
+
deployed fleet's tasks"). Source: `docs/concepts/tenancy.mdx`.
|
|
286
|
+
|
|
287
|
+
### MEDIUM stamping runs into a namespace nothing polls
|
|
288
|
+
|
|
289
|
+
Wrong:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
await engine.start('checkout', order, id, { namespace: 'eu-west' }) // no engine/operator polls eu-west
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Correct:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
// Run at least one engine on 'eu-west' (or an operator above it) first:
|
|
299
|
+
await engine.start('checkout', order, id, { namespace: 'eu-west' })
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Mechanism: between namespaced engines there is no fallback owner; a namespace
|
|
303
|
+
with neither an engine of its own nor an operator above it is a dead end where
|
|
304
|
+
runs sit `pending` forever. Source: `docs/concepts/tenancy.mdx` (pitfalls).
|
|
305
|
+
|
|
306
|
+
See also: `durable-setup` — the standalone execution model this scales out of.
|
|
307
|
+
See also: `durable-transports-stores` — bullmq/queue/db driver details,
|
|
308
|
+
namespace mechanics.
|