@adonis-agora/durable 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +47 -0
  3. package/dist/commands/main.d.ts +3 -2
  4. package/dist/commands/main.d.ts.map +1 -1
  5. package/dist/commands/main.js +2 -1
  6. package/dist/commands/main.js.map +1 -1
  7. package/dist/commands/make_workflow.d.ts +16 -0
  8. package/dist/commands/make_workflow.d.ts.map +1 -0
  9. package/dist/commands/make_workflow.js +30 -0
  10. package/dist/commands/make_workflow.js.map +1 -0
  11. package/dist/configure.d.ts +5 -2
  12. package/dist/configure.d.ts.map +1 -1
  13. package/dist/configure.js +7 -2
  14. package/dist/configure.js.map +1 -1
  15. package/dist/providers/durable_provider.d.ts +12 -0
  16. package/dist/providers/durable_provider.d.ts.map +1 -1
  17. package/dist/providers/durable_provider.js +50 -2
  18. package/dist/providers/durable_provider.js.map +1 -1
  19. package/dist/src/define_config.d.ts +11 -3
  20. package/dist/src/define_config.d.ts.map +1 -1
  21. package/dist/src/define_config.js.map +1 -1
  22. package/dist/src/hooks/workflows.d.ts +53 -0
  23. package/dist/src/hooks/workflows.d.ts.map +1 -0
  24. package/dist/src/hooks/workflows.js +55 -0
  25. package/dist/src/hooks/workflows.js.map +1 -0
  26. package/dist/src/index.d.ts +3 -1
  27. package/dist/src/index.d.ts.map +1 -1
  28. package/dist/src/index.js +2 -0
  29. package/dist/src/index.js.map +1 -1
  30. package/dist/src/protocol.d.ts.map +1 -1
  31. package/dist/src/protocol.js +50 -18
  32. package/dist/src/protocol.js.map +1 -1
  33. package/dist/src/transports/event-emitter.d.ts +60 -0
  34. package/dist/src/transports/event-emitter.d.ts.map +1 -0
  35. package/dist/src/transports/event-emitter.js +103 -0
  36. package/dist/src/transports/event-emitter.js.map +1 -0
  37. package/dist/src/transports/factory.d.ts +23 -2
  38. package/dist/src/transports/factory.d.ts.map +1 -1
  39. package/dist/src/transports/factory.js +20 -1
  40. package/dist/src/transports/factory.js.map +1 -1
  41. package/dist/src/workflow-discovery.d.ts +45 -0
  42. package/dist/src/workflow-discovery.d.ts.map +1 -0
  43. package/dist/src/workflow-discovery.js +103 -0
  44. package/dist/src/workflow-discovery.js.map +1 -0
  45. package/dist/src/workflow-ref.d.ts +41 -5
  46. package/dist/src/workflow-ref.d.ts.map +1 -1
  47. package/dist/src/workflow-ref.js +37 -6
  48. package/dist/src/workflow-ref.js.map +1 -1
  49. package/dist/stubs/config/durable.stub +6 -1
  50. package/dist/stubs/make/workflow/main.stub +22 -0
  51. package/package.json +11 -2
@@ -1,4 +1,30 @@
1
1
  import { createStepLogger } from './step-logger.js';
2
+ /**
3
+ * The scoped-restore slot `@adonis-agora/context` exposes:
4
+ * `<T>(snapshot: Record<string, unknown> | undefined, fn: () => T) => T`. It runs `fn` INSIDE a
5
+ * freshly-activated context store seeded from `snapshot` (or just `fn()` when snapshot/slot is
6
+ * absent). Read structurally so a worker restores the originating request's userRef/tenant/traceId
7
+ * around a step handler with zero config when context is installed — and a clean no-op (`fn()`) when
8
+ * it is not. Wrapping (rather than the old `:set`, which only populates an already-active store) gives
9
+ * both correct propagation AND per-task isolation on a long-lived worker: each task runs in its own
10
+ * scope, so context never bleeds between tasks. The key is a global-registry symbol so it survives
11
+ * duplicate copies of either package in a dependency tree.
12
+ */
13
+ const CONTEXT_SCOPE = Symbol.for('@agora/context:scope');
14
+ /**
15
+ * Run `fn` inside the originating request's context (userRef/tenant/traceId), restored from the
16
+ * task's snapshot (stamped at dispatch by the originating engine, see {@link RemoteTask.context}),
17
+ * so a step handler sees it with no manual code. When `@adonis-agora/context` is not installed (slot
18
+ * absent) the body runs directly — a clean no-op fallback. Never throws from the propagation path: a
19
+ * non-function slot is ignored; errors inside `fn` propagate as the step's own failure (the scope
20
+ * helper itself re-throws nothing of its own).
21
+ */
22
+ function withRestoredContext(snapshot, fn) {
23
+ const scope = globalThis[CONTEXT_SCOPE];
24
+ if (typeof scope !== 'function')
25
+ return fn();
26
+ return scope(snapshot, fn);
27
+ }
2
28
  /** Canonical step id — the stable identity of a step within a run, used for dedupe and
3
29
  * correlation. The format is part of the cross-language wire contract (Python builds the same). */
4
30
  export function stepId(runId, seq) {
@@ -25,23 +51,29 @@ export async function runStepHandler(task, handler) {
25
51
  }
26
52
  const events = [];
27
53
  const withEvents = (result) => events.length > 0 ? { ...result, events } : result;
28
- try {
29
- const output = await handler(task.input, createStepLogger(events, Date.now));
30
- return withEvents({ ...base, status: 'completed', output });
31
- }
32
- catch (err) {
33
- // Carry `code`/`retryable` off the thrown error if present, so the engine's durable retry can
34
- // honour a worker's "don't retry this" verdict (e.g. a declined card).
35
- const e = err;
36
- return withEvents({
37
- ...base,
38
- status: 'failed',
39
- error: {
40
- message: err instanceof Error ? err.message : String(err),
41
- ...(typeof e?.code === 'string' ? { code: e.code } : {}),
42
- ...(typeof e?.retryable === 'boolean' ? { retryable: e.retryable } : {}),
43
- },
44
- });
45
- }
54
+ // Run the handler INSIDE the originating request's context (userRef/tenant/traceId), restored from
55
+ // the task snapshot, so cross-process propagation is automatic — `ctx.call(remoteStep, input)`
56
+ // carries the caller's context with zero manual serialize/deserialize, and each task runs in its
57
+ // own scope (no cross-task bleed on a long-lived worker). No-op without `@adonis-agora/context`.
58
+ return withRestoredContext(task.context, async () => {
59
+ try {
60
+ const output = await handler(task.input, createStepLogger(events, Date.now));
61
+ return withEvents({ ...base, status: 'completed', output });
62
+ }
63
+ catch (err) {
64
+ // Carry `code`/`retryable` off the thrown error if present, so the engine's durable retry can
65
+ // honour a worker's "don't retry this" verdict (e.g. a declined card).
66
+ const e = err;
67
+ return withEvents({
68
+ ...base,
69
+ status: 'failed',
70
+ error: {
71
+ message: err instanceof Error ? err.message : String(err),
72
+ ...(typeof e?.code === 'string' ? { code: e.code } : {}),
73
+ ...(typeof e?.retryable === 'boolean' ? { retryable: e.retryable } : {}),
74
+ },
75
+ });
76
+ }
77
+ });
46
78
  }
47
79
  //# sourceMappingURL=protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD;oGACoG;AACpG,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,GAAW;IAC/C,OAAO,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC;AAC3B,CAAC;AAED;6FAC6F;AAC7F,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,GAAW,EAAU,EAAE,CAAC,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;AAM5F;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAgB,EAChB,OAAgC;IAEhC,+FAA+F;IAC/F,8FAA8F;IAC9F,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC9F,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,EAAE,OAAO,EAAE,kBAAkB,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SACpE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,CAAC,MAAkB,EAAc,EAAE,CACpD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7E,OAAO,UAAU,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,8FAA8F;QAC9F,uEAAuE;QACvE,MAAM,CAAC,GAAG,GAA+D,CAAC;QAC1E,OAAO,UAAU,CAAC;YAChB,GAAG,IAAI;YACP,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE;gBACL,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACzD,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,GAAG,CAAC,OAAO,CAAC,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzE;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type ControlMessage, type ControlPlane, type Heartbeat, type RemoteTask, type StepResult, type Transport } from '../interfaces.js';
3
+ import { type StepHandler } from '../protocol.js';
4
+ /** Event names the transport multiplexes over a single in-process emitter. */
5
+ export declare const TASK_EVENT = "durable.task";
6
+ export declare const RESULT_EVENT = "durable.result";
7
+ export declare const HEARTBEAT_EVENT = "durable.heartbeat";
8
+ export declare const CONTROL_EVENT = "durable.control";
9
+ export interface EventEmitterTransportOptions {
10
+ /**
11
+ * The in-process emitter to multiplex over. Defaults to a fresh Node `EventEmitter` (listener cap
12
+ * lifted, since the engine wires several long-lived listeners). Pass `@adonisjs/core`'s `emitter`
13
+ * service if you'd rather ride the app's bus — it is API-compatible for `on`/`emit`.
14
+ */
15
+ emitter?: EventEmitter;
16
+ /**
17
+ * The worker group this instance serves. Unused for routing (handlers are matched by step name in
18
+ * this same process) — accepted for parity with the broker transports, and stamped nowhere.
19
+ */
20
+ group?: string;
21
+ /** Stable id for this process (stamped on control `from` when a publisher leaves it unset). Default random. */
22
+ instanceId?: string;
23
+ }
24
+ /**
25
+ * A production **in-process** {@link Transport} (and {@link ControlPlane}) backed by a single Node
26
+ * `EventEmitter`. Zero external infrastructure (no DB, no Redis, no broker): step handlers run in the
27
+ * same process, fully decoupled from the workflow that dispatched them, so a single-process app runs
28
+ * real durable workflows with nothing else to deploy.
29
+ *
30
+ * Distinct from the test-only {@link import('../testing/in-memory-transport.js').InMemoryTransport}:
31
+ * that one drives `dispatch` straight into the handler (synchronous-ish, for deterministic tests);
32
+ * this one decouples both directions through the emitter's event loop, mirroring how a real broker
33
+ * fans dispatch → worker → result back. Both funnel every step through {@link runStepHandler}, so the
34
+ * scoped context restore (the `@agora/context:scope` slot) works identically here.
35
+ *
36
+ * Swap to {@link import('./db.js').DbTransport} / {@link import('./queue.js').QueueTransport} for
37
+ * true cross-process or cross-language steps. The {@link ControlPlane} here broadcasts locally (every
38
+ * subscriber in this process), correct for single-instance; it does NOT fan out across pods.
39
+ *
40
+ * Usually you don't construct this directly: `config/durable.ts` selects it via
41
+ * `transports.eventEmitter()` (alias `transports.memory()` from the factory points at the test
42
+ * transport) and the provider builds it for you.
43
+ */
44
+ export declare class EventEmitterTransport implements Transport, ControlPlane {
45
+ #private;
46
+ constructor(options?: EventEmitterTransportOptions);
47
+ /** Stable id stamped on control `from` when a publisher leaves it unset. */
48
+ get instanceId(): string;
49
+ dispatch(task: RemoteTask): Promise<void>;
50
+ /** Register a step handler by name (the worker side, in this same process). */
51
+ handle(name: string, fn: StepHandler): void;
52
+ /** Worker side: a liveness heartbeat. In-process handlers run synchronously, so emit it straight
53
+ * through for symmetry — an engine that wired `onHeartbeat` still observes it. */
54
+ heartbeat(beat: Heartbeat): Promise<void>;
55
+ onResult(handler: (result: StepResult) => Promise<void>): void;
56
+ onHeartbeat(handler: (beat: Heartbeat) => Promise<void>): void;
57
+ publishControl(msg: ControlMessage): Promise<void>;
58
+ onControl(handler: (msg: ControlMessage) => void): void;
59
+ }
60
+ //# sourceMappingURL=event-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../../src/transports/event-emitter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,WAAW,EAAkB,MAAM,gBAAgB,CAAC;AAElE,8EAA8E;AAC9E,eAAO,MAAM,UAAU,iBAAiB,CAAC;AACzC,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAC7C,eAAO,MAAM,eAAe,sBAAsB,CAAC;AACnD,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+GAA+G;IAC/G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,qBAAsB,YAAW,SAAS,EAAE,YAAY;;gBAKvD,OAAO,GAAE,4BAAiC;IAatD,4EAA4E;IAC5E,IAAI,UAAU,IAAI,MAAM,CAEvB;IAMK,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/C,+EAA+E;IAC/E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI;IAI3C;uFACmF;IAC7E,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB/C,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAM9D,WAAW,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAUxD,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxD,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,IAAI;CAGxD"}
@@ -0,0 +1,103 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+ import { runStepHandler } from '../protocol.js';
4
+ /** Event names the transport multiplexes over a single in-process emitter. */
5
+ export const TASK_EVENT = 'durable.task';
6
+ export const RESULT_EVENT = 'durable.result';
7
+ export const HEARTBEAT_EVENT = 'durable.heartbeat';
8
+ export const CONTROL_EVENT = 'durable.control';
9
+ /**
10
+ * A production **in-process** {@link Transport} (and {@link ControlPlane}) backed by a single Node
11
+ * `EventEmitter`. Zero external infrastructure (no DB, no Redis, no broker): step handlers run in the
12
+ * same process, fully decoupled from the workflow that dispatched them, so a single-process app runs
13
+ * real durable workflows with nothing else to deploy.
14
+ *
15
+ * Distinct from the test-only {@link import('../testing/in-memory-transport.js').InMemoryTransport}:
16
+ * that one drives `dispatch` straight into the handler (synchronous-ish, for deterministic tests);
17
+ * this one decouples both directions through the emitter's event loop, mirroring how a real broker
18
+ * fans dispatch → worker → result back. Both funnel every step through {@link runStepHandler}, so the
19
+ * scoped context restore (the `@agora/context:scope` slot) works identically here.
20
+ *
21
+ * Swap to {@link import('./db.js').DbTransport} / {@link import('./queue.js').QueueTransport} for
22
+ * true cross-process or cross-language steps. The {@link ControlPlane} here broadcasts locally (every
23
+ * subscriber in this process), correct for single-instance; it does NOT fan out across pods.
24
+ *
25
+ * Usually you don't construct this directly: `config/durable.ts` selects it via
26
+ * `transports.eventEmitter()` (alias `transports.memory()` from the factory points at the test
27
+ * transport) and the provider builds it for you.
28
+ */
29
+ export class EventEmitterTransport {
30
+ #emitter;
31
+ #instanceId;
32
+ #handlers = new Map();
33
+ constructor(options = {}) {
34
+ // A long-lived single emitter carries several engine listeners; lift Node's default cap so the
35
+ // engine wiring (result + heartbeat + control + task) never trips a MaxListenersExceededWarning.
36
+ const emitter = options.emitter ?? new EventEmitter();
37
+ if (emitter instanceof EventEmitter)
38
+ emitter.setMaxListeners(0);
39
+ this.#emitter = emitter;
40
+ this.#instanceId = options.instanceId ?? randomUUID();
41
+ // The worker side listens for every dispatched task and runs it if it owns the step name.
42
+ this.#emitter.on(TASK_EVENT, (task) => {
43
+ void this.#process(task);
44
+ });
45
+ }
46
+ /** Stable id stamped on control `from` when a publisher leaves it unset. */
47
+ get instanceId() {
48
+ return this.#instanceId;
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // engine → worker
52
+ // ---------------------------------------------------------------------------
53
+ async dispatch(task) {
54
+ this.#emitter.emit(TASK_EVENT, task);
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // worker side — register a step handler, run it, emit the result back
58
+ // ---------------------------------------------------------------------------
59
+ /** Register a step handler by name (the worker side, in this same process). */
60
+ handle(name, fn) {
61
+ this.#handlers.set(name, fn);
62
+ }
63
+ /** Worker side: a liveness heartbeat. In-process handlers run synchronously, so emit it straight
64
+ * through for symmetry — an engine that wired `onHeartbeat` still observes it. */
65
+ async heartbeat(beat) {
66
+ this.#emitter.emit(HEARTBEAT_EVENT, beat);
67
+ }
68
+ async #process(task) {
69
+ const handler = this.#handlers.get(task.name);
70
+ // Another subscriber may own this step name — stay silent, don't synthesize a "no handler" failure.
71
+ if (!handler)
72
+ return;
73
+ const result = await runStepHandler(task, handler);
74
+ // Emit the result on a LATER tick: a durable `ctx.call` suspends the run right after dispatch, so
75
+ // the result must land AFTER that unwinds (else the resume re-enters mid-suspend). Real brokers
76
+ // deliver asynchronously; this mirrors them.
77
+ setImmediate(() => this.#emitter.emit(RESULT_EVENT, result));
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // worker → engine — the engine consumes results + heartbeats
81
+ // ---------------------------------------------------------------------------
82
+ onResult(handler) {
83
+ this.#emitter.on(RESULT_EVENT, (result) => {
84
+ void handler(result);
85
+ });
86
+ }
87
+ onHeartbeat(handler) {
88
+ this.#emitter.on(HEARTBEAT_EVENT, (beat) => {
89
+ void handler(beat);
90
+ });
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // control plane (broadcast within this process)
94
+ // ---------------------------------------------------------------------------
95
+ async publishControl(msg) {
96
+ const stamped = msg.from ? msg : { ...msg, from: this.#instanceId };
97
+ this.#emitter.emit(CONTROL_EVENT, stamped);
98
+ }
99
+ onControl(handler) {
100
+ this.#emitter.on(CONTROL_EVENT, (msg) => handler(msg));
101
+ }
102
+ }
103
+ //# sourceMappingURL=event-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.js","sourceRoot":"","sources":["../../../src/transports/event-emitter.ts"],"names":[],"mappings":"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"}
@@ -24,9 +24,19 @@ export interface TransportContext {
24
24
  * actually selected — keeping those packages optional.
25
25
  */
26
26
  export type TransportFactory = (ctx: TransportContext) => Promise<Transport & Partial<ControlPlane>>;
27
- /** Options for the in-memory transport (no peer dependency). */
27
+ /** Options for the in-memory (test-only) transport (no peer dependency). */
28
28
  export interface MemoryTransportConfig {
29
29
  }
30
+ /** Options for the production in-process EventEmitter transport (no peer dependency). */
31
+ export interface EventEmitterTransportConfig {
32
+ /**
33
+ * The worker group this instance serves. Accepted for parity with the broker transports; handlers
34
+ * are matched by step name in-process, so it does not affect routing.
35
+ */
36
+ group?: string;
37
+ /** Stable id for this process (stamped on control `from` when a publisher leaves it unset). Default random. */
38
+ instanceId?: string;
39
+ }
30
40
  /** Options for the `@adonisjs/queue` transport. */
31
41
  export interface QueueTransportConfig {
32
42
  /**
@@ -69,8 +79,19 @@ export interface DbTransportConfig {
69
79
  instanceId?: string;
70
80
  }
71
81
  export declare const transports: {
72
- /** In-process transport + control plane (single-process, no extra infra). The default. */
82
+ /**
83
+ * The test-only in-process transport + control plane (the engine's default when no `transport` is
84
+ * named). Drives `dispatch` straight into the handler for deterministic tests — for a real
85
+ * single-process production app, prefer {@link transports.eventEmitter}.
86
+ */
73
87
  memory(_config?: MemoryTransportConfig): TransportFactory;
88
+ /**
89
+ * Production **in-process** transport + control plane backed by a single Node `EventEmitter`. Zero
90
+ * external infrastructure (no DB, no Redis, no broker) — a single-process app runs real durable
91
+ * workflows with nothing else to deploy. Decouples dispatch → worker → result over the event loop
92
+ * (mirroring a real broker), unlike the test-only {@link transports.memory}.
93
+ */
94
+ eventEmitter(config?: EventEmitterTransportConfig): TransportFactory;
74
95
  /** Run remote steps cross-process over `@adonisjs/queue`, using a connection from `config/queue.ts`. */
75
96
  queue(config?: QueueTransportConfig): TransportFactory;
76
97
  /** Run remote steps cross-process over the app's database, using `@adonisjs/lucid` — no broker. */
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/transports/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGhE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,GAAG,EAAE;QACH,SAAS,EAAE;YAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAC;QACxD,MAAM,EAAE;YAAE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;SAAE,CAAC;KACtD,CAAC;CACH;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AAEhD,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;CAErC;AAED,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oGAAoG;IACpG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4GAA4G;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyDD,eAAO,MAAM,UAAU;IACrB,0FAA0F;qBAC1E,qBAAqB,GAAQ,gBAAgB;IAI7D,wGAAwG;mBAC1F,oBAAoB,GAAQ,gBAAgB;IAc1D,mGAAmG;gBACxF,iBAAiB,GAAQ,gBAAgB;CAgBrD,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/transports/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGhE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,GAAG,EAAE;QACH,SAAS,EAAE;YAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAC;QACxD,MAAM,EAAE;YAAE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;SAAE,CAAC;KACtD,CAAC;CACH;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AAEhD,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;CAErC;AAED,yFAAyF;AACzF,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+GAA+G;IAC/G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oGAAoG;IACpG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4GAA4G;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0GAA0G;IAC1G,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyDD,eAAO,MAAM,UAAU;IACrB;;;;OAIG;qBACa,qBAAqB,GAAQ,gBAAgB;IAI7D;;;;;OAKG;0BACkB,2BAA2B,GAAQ,gBAAgB;IAUxE,wGAAwG;mBAC1F,oBAAoB,GAAQ,gBAAgB;IAc1D,mGAAmG;gBACxF,iBAAiB,GAAQ,gBAAgB;CAgBrD,CAAC"}
@@ -18,10 +18,29 @@ async function resolveQueueAdapter(ctx, connection) {
18
18
  return typeof entry === 'function' ? entry : entry.resolver(ctx.app);
19
19
  }
20
20
  export const transports = {
21
- /** In-process transport + control plane (single-process, no extra infra). The default. */
21
+ /**
22
+ * The test-only in-process transport + control plane (the engine's default when no `transport` is
23
+ * named). Drives `dispatch` straight into the handler for deterministic tests — for a real
24
+ * single-process production app, prefer {@link transports.eventEmitter}.
25
+ */
22
26
  memory(_config = {}) {
23
27
  return async () => new InMemoryTransport();
24
28
  },
29
+ /**
30
+ * Production **in-process** transport + control plane backed by a single Node `EventEmitter`. Zero
31
+ * external infrastructure (no DB, no Redis, no broker) — a single-process app runs real durable
32
+ * workflows with nothing else to deploy. Decouples dispatch → worker → result over the event loop
33
+ * (mirroring a real broker), unlike the test-only {@link transports.memory}.
34
+ */
35
+ eventEmitter(config = {}) {
36
+ return async () => {
37
+ const { EventEmitterTransport } = await import('./event-emitter.js');
38
+ return new EventEmitterTransport({
39
+ ...(config.group !== undefined ? { group: config.group } : {}),
40
+ ...(config.instanceId !== undefined ? { instanceId: config.instanceId } : {}),
41
+ });
42
+ };
43
+ },
25
44
  /** Run remote steps cross-process over `@adonisjs/queue`, using a connection from `config/queue.ts`. */
26
45
  queue(config = {}) {
27
46
  return async (ctx) => {
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/transports/factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/transports/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { WorkflowEngine } from './engine.js';
2
+ import { type WorkflowClass, type WorkflowMeta } from './workflow-ref.js';
3
+ /** A discovered, decorated workflow class plus the metadata its `@Workflow` decorator stamped. */
4
+ export interface DiscoveredWorkflow {
5
+ meta: WorkflowMeta;
6
+ cls: WorkflowClass;
7
+ }
8
+ /**
9
+ * Register a single `@Workflow`-decorated class on the engine: instantiate it once and bind its
10
+ * `run(ctx, input)` as the workflow body via `engine.register`. The low-level
11
+ * `engine.register(name, version, fn)` stays the escape hatch — this is the convenience the
12
+ * `app/workflows` convention builds on. No-op (returns `false`) for an undecorated class.
13
+ */
14
+ export declare function registerWorkflowClass(engine: WorkflowEngine, cls: unknown): boolean;
15
+ /**
16
+ * Scan a directory RECURSIVELY for modules and collect every exported `@Workflow`-decorated class
17
+ * (the default export and any named export are considered) — so nested conventions like
18
+ * `app/workflows/billing/charge_workflow.ts` are found, matching `make:workflow`'s nested-path
19
+ * scaffolding. Only the environment-appropriate extension is imported (see {@link MODULE_EXT}), and
20
+ * each module path is visited once, so a built `.js` and a dev `.ts` of the same module never both
21
+ * register. Missing directory → empty list (the convention is opt-in: no `app/workflows`, nothing to
22
+ * register).
23
+ */
24
+ export declare function discoverWorkflows(dir: string): Promise<DiscoveredWorkflow[]>;
25
+ /**
26
+ * Discover every `@Workflow` class under `dir` and register each on the engine. Returns the
27
+ * registered metadata so the caller can log what was wired. Best-effort over a missing directory.
28
+ */
29
+ export declare function registerWorkflowsFromDir(engine: WorkflowEngine, dir: string): Promise<WorkflowMeta[]>;
30
+ /**
31
+ * The shape of the build-time barrel generated by the Assembler `init` hook
32
+ * (`@adonis-agora/durable/hooks/workflows`): a map of stable key → lazy module import, e.g.
33
+ * `{ Charge: () => import('#workflows/charge_workflow') }`. The exact key is irrelevant here — we
34
+ * register every `@Workflow`-decorated export of every module, identical to the runtime scan.
35
+ */
36
+ export type WorkflowsBarrel = Record<string, () => Promise<Record<string, unknown>>>;
37
+ /**
38
+ * Register every `@Workflow` class reachable from a generated {@link WorkflowsBarrel}, by awaiting
39
+ * each lazy module import and registering each decorated export — the build-time equivalent of
40
+ * {@link registerWorkflowsFromDir}, with no runtime `readdir`. Each module is imported once and each
41
+ * decorated class registered once (deduped), so a class re-exported from several modules is safe.
42
+ * Returns the registered metadata so the caller can log what was wired.
43
+ */
44
+ export declare function registerWorkflowsFromBarrel(engine: WorkflowEngine, barrel: WorkflowsBarrel): Promise<WorkflowMeta[]>;
45
+ //# sourceMappingURL=workflow-discovery.d.ts.map
@@ -0,0 +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;AAClD,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAgB,MAAM,mBAAmB,CAAC;AAExF,kGAAkG;AAClG,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,aAAa,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAgBnF;AAUD;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAuBlF;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,YAAY,EAAE,CAAC,CAIzB;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC,CAezB"}
@@ -0,0 +1,103 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import { extname, join } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { workflowMeta } from './workflow-ref.js';
5
+ /**
6
+ * Register a single `@Workflow`-decorated class on the engine: instantiate it once and bind its
7
+ * `run(ctx, input)` as the workflow body via `engine.register`. The low-level
8
+ * `engine.register(name, version, fn)` stays the escape hatch — this is the convenience the
9
+ * `app/workflows` convention builds on. No-op (returns `false`) for an undecorated class.
10
+ */
11
+ export function registerWorkflowClass(engine, cls) {
12
+ const meta = workflowMeta(cls);
13
+ if (!meta)
14
+ return false;
15
+ const Ctor = cls;
16
+ const instance = new Ctor();
17
+ engine.register(meta.name, meta.version, (ctx, input) => Promise.resolve(instance.run(ctx, input)), {
18
+ ...(meta.tags ? { tags: meta.tags } : {}),
19
+ ...(meta.executionTimeout !== undefined ? { executionTimeout: meta.executionTimeout } : {}),
20
+ ...(meta.onEvent ? { onEvent: meta.onEvent } : {}),
21
+ });
22
+ return true;
23
+ }
24
+ /**
25
+ * Pick the module extension for the running environment so a built app (`.js`) and a dev/ts app
26
+ * (`.ts`, run under a loader) never double-register the same workflow. We import only files whose
27
+ * extension matches `import.meta.url`'s own (`.ts` when running from source, `.js` from `dist`),
28
+ * which keeps scanner and `make:workflow` (it scaffolds `.ts`) in agreement under both setups.
29
+ */
30
+ const MODULE_EXT = extname(import.meta.url || '') === '.ts' ? '.ts' : '.js';
31
+ /**
32
+ * Scan a directory RECURSIVELY for modules and collect every exported `@Workflow`-decorated class
33
+ * (the default export and any named export are considered) — so nested conventions like
34
+ * `app/workflows/billing/charge_workflow.ts` are found, matching `make:workflow`'s nested-path
35
+ * scaffolding. Only the environment-appropriate extension is imported (see {@link MODULE_EXT}), and
36
+ * each module path is visited once, so a built `.js` and a dev `.ts` of the same module never both
37
+ * register. Missing directory → empty list (the convention is opt-in: no `app/workflows`, nothing to
38
+ * register).
39
+ */
40
+ export async function discoverWorkflows(dir) {
41
+ let entries;
42
+ try {
43
+ entries = await readdir(dir, { recursive: true });
44
+ }
45
+ catch (err) {
46
+ if (err.code === 'ENOENT')
47
+ return [];
48
+ throw err;
49
+ }
50
+ const found = [];
51
+ const seen = new Set();
52
+ for (const entry of entries.sort()) {
53
+ if (extname(entry) !== MODULE_EXT || entry.endsWith(`.d${MODULE_EXT}`))
54
+ continue;
55
+ const mod = (await import(pathToFileURL(join(dir, entry)).href));
56
+ for (const exported of Object.values(mod)) {
57
+ if (seen.has(exported))
58
+ continue;
59
+ const meta = workflowMeta(exported);
60
+ if (!meta)
61
+ continue;
62
+ seen.add(exported);
63
+ found.push({ meta, cls: exported });
64
+ }
65
+ }
66
+ return found;
67
+ }
68
+ /**
69
+ * Discover every `@Workflow` class under `dir` and register each on the engine. Returns the
70
+ * registered metadata so the caller can log what was wired. Best-effort over a missing directory.
71
+ */
72
+ export async function registerWorkflowsFromDir(engine, dir) {
73
+ const discovered = await discoverWorkflows(dir);
74
+ for (const { cls } of discovered)
75
+ registerWorkflowClass(engine, cls);
76
+ return discovered.map((d) => d.meta);
77
+ }
78
+ /**
79
+ * Register every `@Workflow` class reachable from a generated {@link WorkflowsBarrel}, by awaiting
80
+ * each lazy module import and registering each decorated export — the build-time equivalent of
81
+ * {@link registerWorkflowsFromDir}, with no runtime `readdir`. Each module is imported once and each
82
+ * decorated class registered once (deduped), so a class re-exported from several modules is safe.
83
+ * Returns the registered metadata so the caller can log what was wired.
84
+ */
85
+ export async function registerWorkflowsFromBarrel(engine, barrel) {
86
+ const registered = [];
87
+ const seen = new Set();
88
+ for (const load of Object.values(barrel)) {
89
+ const mod = await load();
90
+ for (const exported of Object.values(mod)) {
91
+ if (seen.has(exported))
92
+ continue;
93
+ const meta = workflowMeta(exported);
94
+ if (!meta)
95
+ continue;
96
+ seen.add(exported);
97
+ registerWorkflowClass(engine, exported);
98
+ registered.push(meta);
99
+ }
100
+ }
101
+ return registered;
102
+ }
103
+ //# sourceMappingURL=workflow-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-discovery.js","sourceRoot":"","sources":["../../src/workflow-discovery.ts"],"names":[],"mappings":"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"}
@@ -6,11 +6,47 @@
6
6
  * string stays available for the cross-runtime case.
7
7
  */
8
8
  /**
9
- * The symbol the `@Workflow` decorator stamps a workflow's registered name onto, so a class ref can
10
- * be resolved back to its name. A global-registry symbol (`Symbol.for`) so it survives duplicate
9
+ * The symbol the `@Workflow` decorator stamps the full options onto (name + version + tags …), so
10
+ * auto-discovery can register the class against the engine and a class ref can be resolved back to
11
+ * its name via {@link workflowName}. A global-registry symbol (`Symbol.for`) so it survives duplicate
11
12
  * copies of this package in a dependency tree.
12
13
  */
13
- export declare const WORKFLOW_NAME_KEY: unique symbol;
14
+ export declare const WORKFLOW_META_KEY: unique symbol;
15
+ /** Options passed to `@Workflow({ name, version, … })`. */
16
+ export interface WorkflowOptions {
17
+ /** The registered workflow name (the cross-runtime identity, e.g. `order`). */
18
+ name: string;
19
+ /** Workflow version. Defaults to `'1'`. Register a new version for a breaking change. */
20
+ version?: string;
21
+ /** Searchable labels merged onto every run of this workflow. */
22
+ tags?: string[];
23
+ /** Wall-clock budget for the whole run (a duration string like `'5m'`, or ms). */
24
+ executionTimeout?: string | number;
25
+ /** Event names that start a run of this workflow when published (see `onEvent`). */
26
+ onEvent?: string[];
27
+ }
28
+ /** The metadata the `@Workflow` decorator stamps onto a class for discovery + registration. */
29
+ export interface WorkflowMeta extends WorkflowOptions {
30
+ version: string;
31
+ }
32
+ /**
33
+ * Class decorator marking a class as a durable workflow. Stamps the full options (name + version +
34
+ * tags …) so a class ref resolves via {@link workflowName} and the provider's `app/workflows`
35
+ * auto-discovery can register it on the engine — no manual `engine.register(...)`. The class must
36
+ * expose `run(ctx, input)`; that method becomes the workflow body.
37
+ *
38
+ * ```ts
39
+ * @Workflow({ name: 'order', version: '1' })
40
+ * export default class OrderWorkflow {
41
+ * async run(ctx: WorkflowCtx, input: { id: string }) { ... }
42
+ * }
43
+ * ```
44
+ */
45
+ export declare function Workflow(options: WorkflowOptions): <T extends abstract new (...args: never[]) => {
46
+ run(ctx: never, input: never): unknown;
47
+ }>(target: T) => T;
48
+ /** Read the {@link WorkflowMeta} a `@Workflow` decorator stamped on a class, or `undefined`. */
49
+ export declare function workflowMeta(target: unknown): WorkflowMeta | undefined;
14
50
  /** Structural shape of a `@Workflow` class — its `run(ctx, input)` carries the input/output types. */
15
51
  export type WorkflowClass<TInput = unknown, TOutput = unknown> = abstract new (...args: never[]) => {
16
52
  run(ctx: never, input: TInput): Promise<TOutput> | TOutput;
@@ -27,8 +63,8 @@ export type WorkflowOutputOf<C> = C extends abstract new (...args: never[]) => {
27
63
  } ? Awaited<R> : unknown;
28
64
  /**
29
65
  * Resolve a {@link WorkflowRef} to its registered workflow name: a string is returned as-is; a
30
- * `@Workflow` class is resolved via the name the decorator stamped on it. Throws if a class was
31
- * never decorated (so it carries no registered name).
66
+ * `@Workflow` class is resolved via the name the decorator stamped in its metadata. Throws if a
67
+ * class was never decorated (so it carries no registered name).
32
68
  */
33
69
  export declare function workflowName(ref: WorkflowRef): string;
34
70
  //# sourceMappingURL=workflow-ref.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-ref.d.ts","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAAmD,CAAC;AAE3F,sGAAsG;AACtG,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,QAAQ,MACvE,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC5D,CAAC;AAEF,yGAAyG;AACzG,MAAM,MAAM,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IACvD,MAAM,GACN,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,uDAAuD;AACvD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MACjD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;CAC1C,GACG,CAAC,GACD,OAAO,CAAC;AAEZ,gFAAgF;AAChF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MAClD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;CACxC,GACG,OAAO,CAAC,CAAC,CAAC,GACV,OAAO,CAAC;AAEZ;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CASrD"}
1
+ {"version":3,"file":"workflow-ref.d.ts","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAAmD,CAAC;AAE3F,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,+FAA+F;AAC/F,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,IACvC,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK;IAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;CAAE,EAC7F,QAAQ,CAAC,KACR,CAAC,CASL;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAGtE;AAED,sGAAsG;AACtG,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,QAAQ,MACvE,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC5D,CAAC;AAEF,yGAAyG;AACzG,MAAM,MAAM,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IACvD,MAAM,GACN,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,uDAAuD;AACvD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MACjD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;CAC1C,GACG,CAAC,GACD,OAAO,CAAC;AAEZ,gFAAgF;AAChF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MAClD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;CACxC,GACG,OAAO,CAAC,CAAC,CAAC,GACV,OAAO,CAAC;AAEZ;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CASrD"}