@adonis-agora/durable 0.9.1 → 0.11.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 (164) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/assets/dashboard.html +116 -3
  3. package/dist/commands/durable_worker.d.ts +25 -0
  4. package/dist/commands/durable_worker.d.ts.map +1 -0
  5. package/dist/commands/durable_worker.js +113 -0
  6. package/dist/commands/durable_worker.js.map +1 -0
  7. package/dist/commands/main.d.ts +6 -5
  8. package/dist/commands/main.d.ts.map +1 -1
  9. package/dist/commands/main.js +11 -4
  10. package/dist/commands/main.js.map +1 -1
  11. package/dist/providers/dashboard_provider.d.ts +38 -0
  12. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  13. package/dist/providers/dashboard_provider.js +103 -10
  14. package/dist/providers/dashboard_provider.js.map +1 -1
  15. package/dist/providers/durable_provider.d.ts +24 -16
  16. package/dist/providers/durable_provider.d.ts.map +1 -1
  17. package/dist/providers/durable_provider.js +195 -20
  18. package/dist/providers/durable_provider.js.map +1 -1
  19. package/dist/src/config_types.d.ts +92 -0
  20. package/dist/src/config_types.d.ts.map +1 -0
  21. package/dist/src/config_types.js +2 -0
  22. package/dist/src/config_types.js.map +1 -0
  23. package/dist/src/dashboard/compat-view.d.ts +13 -0
  24. package/dist/src/dashboard/compat-view.d.ts.map +1 -0
  25. package/dist/src/dashboard/compat-view.js +32 -0
  26. package/dist/src/dashboard/compat-view.js.map +1 -0
  27. package/dist/src/dashboard/compat.d.ts +76 -0
  28. package/dist/src/dashboard/compat.d.ts.map +1 -0
  29. package/dist/src/dashboard/compat.js +130 -0
  30. package/dist/src/dashboard/compat.js.map +1 -0
  31. package/dist/src/dashboard/diagnostics-recorder.d.ts +59 -0
  32. package/dist/src/dashboard/diagnostics-recorder.d.ts.map +1 -0
  33. package/dist/src/dashboard/diagnostics-recorder.js +75 -0
  34. package/dist/src/dashboard/diagnostics-recorder.js.map +1 -0
  35. package/dist/src/dashboard/gateway-adapter.d.ts +37 -0
  36. package/dist/src/dashboard/gateway-adapter.d.ts.map +1 -0
  37. package/dist/src/dashboard/gateway-adapter.js +49 -0
  38. package/dist/src/dashboard/gateway-adapter.js.map +1 -0
  39. package/dist/src/dashboard/handlers.d.ts +38 -8
  40. package/dist/src/dashboard/handlers.d.ts.map +1 -1
  41. package/dist/src/dashboard/handlers.js +17 -1
  42. package/dist/src/dashboard/handlers.js.map +1 -1
  43. package/dist/src/dashboard/index.d.ts +10 -3
  44. package/dist/src/dashboard/index.d.ts.map +1 -1
  45. package/dist/src/dashboard/index.js +8 -2
  46. package/dist/src/dashboard/index.js.map +1 -1
  47. package/dist/src/define_config.d.ts +31 -5
  48. package/dist/src/define_config.d.ts.map +1 -1
  49. package/dist/src/define_config.js +4 -3
  50. package/dist/src/define_config.js.map +1 -1
  51. package/dist/src/dispatch-routing.d.ts +105 -0
  52. package/dist/src/dispatch-routing.d.ts.map +1 -0
  53. package/dist/src/dispatch-routing.js +114 -0
  54. package/dist/src/dispatch-routing.js.map +1 -0
  55. package/dist/src/engine.d.ts +129 -3
  56. package/dist/src/engine.d.ts.map +1 -1
  57. package/dist/src/engine.js +354 -12
  58. package/dist/src/engine.js.map +1 -1
  59. package/dist/src/handshake/descriptor.d.ts +128 -0
  60. package/dist/src/handshake/descriptor.d.ts.map +1 -0
  61. package/dist/src/handshake/descriptor.js +133 -0
  62. package/dist/src/handshake/descriptor.js.map +1 -0
  63. package/dist/src/handshake/negotiate.d.ts +83 -0
  64. package/dist/src/handshake/negotiate.d.ts.map +1 -0
  65. package/dist/src/handshake/negotiate.js +108 -0
  66. package/dist/src/handshake/negotiate.js.map +1 -0
  67. package/dist/src/handshake/routing.d.ts +51 -0
  68. package/dist/src/handshake/routing.d.ts.map +1 -0
  69. package/dist/src/handshake/routing.js +57 -0
  70. package/dist/src/handshake/routing.js.map +1 -0
  71. package/dist/src/index.d.ts +14 -2
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +12 -1
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/interfaces.d.ts +184 -7
  76. package/dist/src/interfaces.d.ts.map +1 -1
  77. package/dist/src/remote-workflow-executor.d.ts +25 -1
  78. package/dist/src/remote-workflow-executor.d.ts.map +1 -1
  79. package/dist/src/remote-workflow-executor.js +41 -0
  80. package/dist/src/remote-workflow-executor.js.map +1 -1
  81. package/dist/src/role_bindings.d.ts +85 -0
  82. package/dist/src/role_bindings.d.ts.map +1 -0
  83. package/dist/src/role_bindings.js +73 -0
  84. package/dist/src/role_bindings.js.map +1 -0
  85. package/dist/src/run-gateway/interface.d.ts +89 -0
  86. package/dist/src/run-gateway/interface.d.ts.map +1 -0
  87. package/dist/src/run-gateway/interface.js +2 -0
  88. package/dist/src/run-gateway/interface.js.map +1 -0
  89. package/dist/src/run-gateway/proxy-run-gateway.d.ts +84 -0
  90. package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -0
  91. package/dist/src/run-gateway/proxy-run-gateway.js +142 -0
  92. package/dist/src/run-gateway/proxy-run-gateway.js.map +1 -0
  93. package/dist/src/run-gateway/run-request-responder.d.ts +67 -0
  94. package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -0
  95. package/dist/src/run-gateway/run-request-responder.js +228 -0
  96. package/dist/src/run-gateway/run-request-responder.js.map +1 -0
  97. package/dist/src/run-gateway/store-run-gateway.d.ts +90 -0
  98. package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -0
  99. package/dist/src/run-gateway/store-run-gateway.js +80 -0
  100. package/dist/src/run-gateway/store-run-gateway.js.map +1 -0
  101. package/dist/src/run-gateway/tenant-auth.d.ts +19 -0
  102. package/dist/src/run-gateway/tenant-auth.d.ts.map +1 -0
  103. package/dist/src/run-gateway/tenant-auth.js +71 -0
  104. package/dist/src/run-gateway/tenant-auth.js.map +1 -0
  105. package/dist/src/services/main.d.ts +16 -3
  106. package/dist/src/services/main.d.ts.map +1 -1
  107. package/dist/src/services/main.js +23 -4
  108. package/dist/src/services/main.js.map +1 -1
  109. package/dist/src/step-name-symbol.d.ts +3 -0
  110. package/dist/src/step-name-symbol.d.ts.map +1 -1
  111. package/dist/src/step-name-symbol.js.map +1 -1
  112. package/dist/src/step-ref.d.ts +4 -0
  113. package/dist/src/step-ref.d.ts.map +1 -1
  114. package/dist/src/step-ref.js +2 -0
  115. package/dist/src/step-ref.js.map +1 -1
  116. package/dist/src/transports/bullmq/bullmq-transport.d.ts +130 -0
  117. package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -0
  118. package/dist/src/transports/bullmq/bullmq-transport.js +562 -0
  119. package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -0
  120. package/dist/src/transports/bullmq/deps.d.ts +59 -0
  121. package/dist/src/transports/bullmq/deps.d.ts.map +1 -0
  122. package/dist/src/transports/bullmq/deps.js +41 -0
  123. package/dist/src/transports/bullmq/deps.js.map +1 -0
  124. package/dist/src/transports/bullmq/index.d.ts +5 -0
  125. package/dist/src/transports/bullmq/index.d.ts.map +1 -0
  126. package/dist/src/transports/bullmq/index.js +5 -0
  127. package/dist/src/transports/bullmq/index.js.map +1 -0
  128. package/dist/src/transports/bullmq/naming.d.ts +71 -0
  129. package/dist/src/transports/bullmq/naming.d.ts.map +1 -0
  130. package/dist/src/transports/bullmq/naming.js +101 -0
  131. package/dist/src/transports/bullmq/naming.js.map +1 -0
  132. package/dist/src/transports/bullmq/serialization.d.ts +56 -0
  133. package/dist/src/transports/bullmq/serialization.d.ts.map +1 -0
  134. package/dist/src/transports/bullmq/serialization.js +101 -0
  135. package/dist/src/transports/bullmq/serialization.js.map +1 -0
  136. package/dist/src/transports/factory.d.ts +31 -0
  137. package/dist/src/transports/factory.d.ts.map +1 -1
  138. package/dist/src/transports/factory.js +21 -0
  139. package/dist/src/transports/factory.js.map +1 -1
  140. package/dist/src/worker-runtime/index.d.ts +22 -0
  141. package/dist/src/worker-runtime/index.d.ts.map +1 -0
  142. package/dist/src/worker-runtime/index.js +27 -0
  143. package/dist/src/worker-runtime/index.js.map +1 -0
  144. package/dist/src/worker-runtime/naming.d.ts +24 -0
  145. package/dist/src/worker-runtime/naming.d.ts.map +1 -0
  146. package/dist/src/worker-runtime/naming.js +28 -0
  147. package/dist/src/worker-runtime/naming.js.map +1 -0
  148. package/dist/src/worker-runtime/registry.d.ts +81 -0
  149. package/dist/src/worker-runtime/registry.d.ts.map +1 -0
  150. package/dist/src/worker-runtime/registry.js +44 -0
  151. package/dist/src/worker-runtime/registry.js.map +1 -0
  152. package/dist/src/worker-runtime/worker-runtime.d.ts +155 -0
  153. package/dist/src/worker-runtime/worker-runtime.d.ts.map +1 -0
  154. package/dist/src/worker-runtime/worker-runtime.js +265 -0
  155. package/dist/src/worker-runtime/worker-runtime.js.map +1 -0
  156. package/dist/src/workflow-ctx.d.ts +9 -0
  157. package/dist/src/workflow-ctx.d.ts.map +1 -1
  158. package/dist/src/workflow-ctx.js +78 -27
  159. package/dist/src/workflow-ctx.js.map +1 -1
  160. package/dist/src/workflow-turn.d.ts +167 -0
  161. package/dist/src/workflow-turn.d.ts.map +1 -0
  162. package/dist/src/workflow-turn.js +372 -0
  163. package/dist/src/workflow-turn.js.map +1 -0
  164. package/package.json +7 -1
@@ -0,0 +1,228 @@
1
+ /** `EngineEvent` types that END a run's lifecycle — no further event for that `runId` follows, so it is
2
+ * the safe point to drop the run from the namespace cache. Cancellation + dead-lettering are both
3
+ * emitted as `run.failed`; `run.suspended` is NOT terminal (a suspended run resumes). */
4
+ function isTerminalRunEvent(event) {
5
+ return event.type === 'run.completed' || event.type === 'run.failed';
6
+ }
7
+ /**
8
+ * Operator-side consumer of a store-less tenant's P4 traffic — the TRUST BOUNDARY of the tenant run
9
+ * gateway (spec §8/§9). It answers each {@link RunRequest}/{@link StartRunMessage} against a
10
+ * store-backed {@link RunGateway} (typically `StoreRunGateway`), and re-publishes the engine's
11
+ * lifecycle events to each tenant's channel.
12
+ *
13
+ * Enforcement, per request, BEFORE any run is touched:
14
+ * 1. **Authenticate + derive tenant.** With a {@link RunRequestResponderOptions.verifyTenant}, the
15
+ * signed token (carried in the wire `tenant` field) is verified and the real tenant is DERIVED from
16
+ * it; the body's claim is ignored. An invalid token short-circuits to an `unauthorized` reply.
17
+ * 2. **`listRuns` is namespace-forced** to the derived tenant — the client's `namespace` is discarded,
18
+ * never merely validated, so a tenant cannot enumerate another's runs.
19
+ * 3. **`workerHealth` is group-scoped** to the tenant's own `@<tenant>` queues.
20
+ * 4. **Every runId-bearing verb** (`getRun`/`getCheckpoints`/`getSearchAttributes`/`signal`/`cancel`/
21
+ * `redispatch`) loads the run FIRST and rejects with `cross-tenant` when `run.namespace` isn't the
22
+ * derived tenant — anti-IDOR, so a tenant can never read OR act on another tenant's run.
23
+ * 5. **`start` forces the run's namespace** to the derived tenant, so a started run is owned by, and
24
+ * later only reachable by, that tenant.
25
+ * 6. **Unknown verbs are rejected** rather than silently ignored.
26
+ *
27
+ * Do not weaken any of these — they are the isolation guarantee of the store-less cluster.
28
+ */
29
+ export class RunRequestResponder {
30
+ #transport;
31
+ #gateway;
32
+ #verifyTenant;
33
+ #subscribeEngineEvents;
34
+ // Per-run namespace memo for tenant-events republishing: read the store at most once per run id (a
35
+ // `step.*` event carries no namespace), dropped on the run's terminal event so it stays bounded to
36
+ // in-flight runs. `null` = "resolved, not a tenant" (distinct from absent = "not yet resolved").
37
+ #runNamespaces = new Map();
38
+ #unsubscribeEvents;
39
+ constructor(transport, gateway, options = {}) {
40
+ this.#transport = transport;
41
+ this.#gateway = gateway;
42
+ this.#verifyTenant = options.verifyTenant;
43
+ this.#subscribeEngineEvents = options.subscribeEngineEvents;
44
+ }
45
+ /**
46
+ * Register the consumers on the transport (idempotent per transport). Each request is answered
47
+ * independently; a handler failure never throws back into the transport — every error is captured
48
+ * into an error reply. If an engine-event source was given, starts re-publishing tenant events.
49
+ */
50
+ start() {
51
+ this.#transport.onRunRequest(async (msg) => {
52
+ const reply = await this.#handleRequest(msg);
53
+ await this.#transport.publishRunReply(reply);
54
+ });
55
+ this.#transport.onStartRun(async (msg) => {
56
+ const reply = await this.#handleStart(msg);
57
+ if (reply)
58
+ await this.#transport.publishRunReply(reply);
59
+ });
60
+ if (this.#subscribeEngineEvents) {
61
+ this.#unsubscribeEvents = this.#subscribeEngineEvents((event) => {
62
+ void this.#republish(event);
63
+ });
64
+ }
65
+ }
66
+ /** Stop re-publishing tenant events (the transport consumers are torn down with the transport). */
67
+ stop() {
68
+ this.#unsubscribeEvents?.();
69
+ this.#unsubscribeEvents = undefined;
70
+ this.#runNamespaces.clear();
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // read / control
74
+ // ---------------------------------------------------------------------------
75
+ async #handleRequest(msg) {
76
+ const tenant = await this.#resolveTenant(msg.tenant);
77
+ if (tenant === null) {
78
+ return reply(msg.requestId, {
79
+ ok: false,
80
+ error: { message: 'invalid or unauthorized tenant token', code: 'unauthorized' },
81
+ });
82
+ }
83
+ const { body } = msg;
84
+ if (body.kind === 'listRuns') {
85
+ // Force the namespace to the verified tenant — the client value is DISCARDED, never validated, so
86
+ // a tenant can't widen its own query into another's namespace.
87
+ const data = await this.#gateway.listRuns({ ...body.query, namespace: tenant });
88
+ return reply(msg.requestId, { ok: true, data });
89
+ }
90
+ if (body.kind === 'workerHealth') {
91
+ // Not runId-bearing. Scope by the group-name convention instead: a tenant's queues are suffixed
92
+ // `<name>@<tenant>`, so keep only groups ending in `@<tenant>` — the operator's own bare groups
93
+ // and every other tenant's are dropped.
94
+ const all = await this.#gateway.workerHealth();
95
+ const data = all.filter((h) => h.group.endsWith(`@${tenant}`));
96
+ return reply(msg.requestId, { ok: true, data });
97
+ }
98
+ // Every remaining verb is runId-bearing. Load the run FIRST — before calling the verb — so a
99
+ // cross-tenant request never reaches the gateway's mutating methods (signal/cancel/redispatch) or
100
+ // leaks another tenant's run via a read (getRun/getCheckpoints/getSearchAttributes).
101
+ const run = await this.#gateway.getRun(body.runId);
102
+ if (run && run.namespace !== tenant) {
103
+ return reply(msg.requestId, {
104
+ ok: false,
105
+ error: { message: 'run belongs to another tenant', code: 'cross-tenant' },
106
+ });
107
+ }
108
+ try {
109
+ const data = await this.#callVerb(body, run);
110
+ return reply(msg.requestId, { ok: true, data });
111
+ }
112
+ catch (err) {
113
+ return reply(msg.requestId, {
114
+ ok: false,
115
+ error: { message: err instanceof Error ? err.message : String(err) },
116
+ });
117
+ }
118
+ }
119
+ /** Dispatch a runId-bearing verb. `run` is the already-loaded run (ownership-checked by the caller) —
120
+ * reused for `getRun` so it isn't fetched twice. An unknown verb is rejected, never silently dropped. */
121
+ #callVerb(body, run) {
122
+ switch (body.kind) {
123
+ case 'getRun':
124
+ return Promise.resolve(run);
125
+ case 'getCheckpoints':
126
+ return this.#gateway.getCheckpoints(body.runId);
127
+ case 'getRunChildren':
128
+ return this.#gateway.getRunChildren(body.runId);
129
+ case 'getSearchAttributes':
130
+ return this.#gateway.getSearchAttributes(body.runId);
131
+ case 'signal':
132
+ return this.#gateway.signal(body.runId, body.signal, body.payload);
133
+ case 'cancel':
134
+ return this.#gateway.cancel(body.runId, body.opts);
135
+ case 'redispatch':
136
+ return this.#gateway.redispatchPending(body.runId);
137
+ default:
138
+ // Exhaustiveness guard: an unrecognised verb (a newer/incompatible client) is rejected loudly.
139
+ return Promise.reject(new Error(`unknown run-request verb: ${body.kind}`));
140
+ }
141
+ }
142
+ // ---------------------------------------------------------------------------
143
+ // start-run
144
+ // ---------------------------------------------------------------------------
145
+ /**
146
+ * Answer a start-run. The reply is correlated by the message's `runId` (the proxy mints/forwards one
147
+ * so the fire-and-forget aviary `StartRunMessage` can carry a correlated answer). The run's namespace
148
+ * is FORCED to the verified tenant, so the started run is owned by that tenant and later reachable
149
+ * only by it. Returns `undefined` (no reply) only when there is no id to correlate on.
150
+ */
151
+ async #handleStart(msg) {
152
+ const correlationId = msg.runId;
153
+ const tenant = await this.#resolveTenant(msg.tenant);
154
+ if (tenant === null) {
155
+ return correlationId === undefined
156
+ ? undefined
157
+ : reply(correlationId, {
158
+ ok: false,
159
+ error: { message: 'invalid or unauthorized tenant token', code: 'unauthorized' },
160
+ });
161
+ }
162
+ try {
163
+ const result = await this.#gateway.start(msg.workflow, msg.input, {
164
+ ...(msg.runId !== undefined ? { runId: msg.runId } : {}),
165
+ ...(msg.tags !== undefined ? { tags: msg.tags } : {}),
166
+ ...(msg.searchAttributes !== undefined ? { searchAttributes: msg.searchAttributes } : {}),
167
+ // Force the run's namespace to the verified tenant — never a body claim (anti-IDOR at the root).
168
+ namespace: tenant,
169
+ });
170
+ return reply(correlationId ?? result.runId, { ok: true, data: result });
171
+ }
172
+ catch (err) {
173
+ return correlationId === undefined
174
+ ? undefined
175
+ : reply(correlationId, {
176
+ ok: false,
177
+ error: { message: err instanceof Error ? err.message : String(err) },
178
+ });
179
+ }
180
+ }
181
+ // ---------------------------------------------------------------------------
182
+ // tenant-events republishing
183
+ // ---------------------------------------------------------------------------
184
+ async #republish(event) {
185
+ const namespace = await this.#namespaceFor(event);
186
+ // A terminal run emits no further events — drop its cache entry now regardless of whether this event
187
+ // was itself re-published, so the slot is reclaimed even for a bare/default run.
188
+ if (isTerminalRunEvent(event))
189
+ this.#runNamespaces.delete(event.runId);
190
+ // Skip bare/default runs — no real tenant to re-publish to.
191
+ if (!namespace || namespace === 'default')
192
+ return;
193
+ await this.#transport.publishTenantEvent({ tenant: namespace, event }).catch(() => undefined);
194
+ }
195
+ async #namespaceFor(event) {
196
+ if (this.#runNamespaces.has(event.runId)) {
197
+ const cached = this.#runNamespaces.get(event.runId);
198
+ return cached === null ? undefined : cached;
199
+ }
200
+ if (event.namespace !== undefined) {
201
+ this.#runNamespaces.set(event.runId, event.namespace === 'default' ? null : event.namespace);
202
+ return event.namespace === 'default' ? undefined : event.namespace;
203
+ }
204
+ const run = await this.#gateway.getRun(event.runId);
205
+ const ns = run?.namespace !== undefined && run.namespace !== 'default' ? run.namespace : null;
206
+ this.#runNamespaces.set(event.runId, ns);
207
+ return ns === null ? undefined : ns;
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // trust boundary
211
+ // ---------------------------------------------------------------------------
212
+ /**
213
+ * Resolve the tenant a request is ALLOWED to act as. With a verifier, the wire `tenant` is treated as
214
+ * the signed token: it is verified and the real tenant DERIVED from it (`null` on reject). Without a
215
+ * verifier, the wire value is trusted verbatim (prefix/network isolation only, aviary-compatible).
216
+ */
217
+ async #resolveTenant(wireTenant) {
218
+ if (!this.#verifyTenant)
219
+ return wireTenant;
220
+ const verified = await this.#verifyTenant({ token: wireTenant, tenant: wireTenant });
221
+ return verified ? verified.tenant : null;
222
+ }
223
+ }
224
+ /** Build a correlated {@link RunReply}. */
225
+ function reply(requestId, result) {
226
+ return { requestId, result };
227
+ }
228
+ //# sourceMappingURL=run-request-responder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-request-responder.js","sourceRoot":"","sources":["../../../src/run-gateway/run-request-responder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { EngineEvent, GroupHealth, RunQuery, RunResult, SearchAttributes, StepCheckpoint, WorkflowRun } from '../interfaces.js';
2
+ import type { WorkflowRef } from '../workflow-ref.js';
3
+ import type { DurableTopology, RunGateway, StartRunOptions } from './interface.js';
4
+ /**
5
+ * The slice of {@link WorkflowEngine} the {@link StoreRunGateway} delegates to. Declared structurally
6
+ * (not by importing the concrete class) so the gateway depends only on the verbs it forwards, and so
7
+ * `redispatchPending` — which the AdonisJS engine does NOT yet implement (the lost-remote-step
8
+ * re-dispatch machinery from aviary's core engine isn't ported) — can be declared OPTIONAL: the
9
+ * gateway calls it when present and degrades to `null` when absent. The real `WorkflowEngine` is
10
+ * structurally assignable to this.
11
+ *
12
+ * TODO(integrator): once the engine grows a `redispatchPending(runId)` method (port of aviary
13
+ * `WorkflowEngine.redispatchPending`), this optional member becomes a hard delegation. Consider
14
+ * exporting a shared engine-facing port type from the package so this local structural mirror can be
15
+ * dropped.
16
+ */
17
+ export interface RunGatewayEngine {
18
+ getRun(runId: string): Promise<WorkflowRun | null>;
19
+ listRuns(query: RunQuery): Promise<WorkflowRun[]>;
20
+ listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
21
+ getRunChildren(parentRunId: string): Promise<string[]>;
22
+ workerHealth(extra?: string[]): Promise<GroupHealth[]>;
23
+ start(workflow: string, input: unknown, runId: string, opts?: StartRunOptions): Promise<RunResult>;
24
+ signal(token: string, payload: unknown): Promise<RunResult | null>;
25
+ cancel(runId: string, opts?: {
26
+ compensate?: boolean;
27
+ }): Promise<RunResult | null>;
28
+ subscribe(listener: (event: EngineEvent) => void): () => void;
29
+ /** Optional — absent on the current AdonisJS engine; see {@link RunGatewayEngine} note. */
30
+ redispatchPending?(runId: string): Promise<(RunResult & {
31
+ redispatched: number;
32
+ }) | null>;
33
+ }
34
+ /** Options for {@link StoreRunGateway}. */
35
+ export interface StoreRunGatewayOptions {
36
+ /**
37
+ * The role reported by {@link StoreRunGateway.topology}. `standalone` for a single-process
38
+ * deployment (the default — this package's first target); `control-plane` when this engine is the
39
+ * store-owning operator that tenant proxies round-trip to. A store-backed gateway is never a
40
+ * `tenant` (that's the proxy's role), so only these two are accepted.
41
+ */
42
+ role?: 'standalone' | 'control-plane' | undefined;
43
+ }
44
+ /**
45
+ * Store-backed {@link RunGateway} — the operator-side implementation. Wraps the local
46
+ * {@link WorkflowEngine} and forwards each read/control/stream verb to the engine's existing method,
47
+ * exactly the surface a `standalone` or `control-plane` deployment exposes. It holds NO state of its
48
+ * own beyond the injected engine and its declared role; all durability lives in the engine/store.
49
+ *
50
+ * Idiomatic AdonisJS: a plain class constructed with its dependency, wired by the package's provider /
51
+ * `defineConfig` (no framework DI decorators). A later `ProxyRunGateway` (Wave 2) will implement the
52
+ * SAME {@link RunGateway} interface over the transport for `tenant` deployments — nothing here needs
53
+ * to change for that.
54
+ */
55
+ export declare class StoreRunGateway implements RunGateway {
56
+ #private;
57
+ constructor(engine: RunGatewayEngine, options?: StoreRunGatewayOptions);
58
+ topology(): DurableTopology;
59
+ getRun(runId: string): Promise<WorkflowRun | null>;
60
+ listRuns(query: RunQuery): Promise<WorkflowRun[]>;
61
+ getCheckpoints(runId: string): Promise<StepCheckpoint[]>;
62
+ getRunChildren(runId: string): Promise<string[]>;
63
+ getSearchAttributes(runId: string): Promise<SearchAttributes | undefined>;
64
+ workerHealth(): Promise<GroupHealth[]>;
65
+ start(workflow: WorkflowRef, input: unknown, opts?: StartRunOptions): Promise<RunResult>;
66
+ /**
67
+ * Deliver `signal` (by name) with `payload` to the addressed run. Confirmed against the engine's
68
+ * waiter convention (design §8): `engine.signal` is GENUINELY GLOBAL-BY-NAME — `ctx.waitForSignal(name)`
69
+ * registers a waiter under the caller-chosen `name` VERBATIM, and `engine.signal(name, payload)` matches
70
+ * by that exact string. There is NO `signal:<runId>:<name>` waiter convention: the run-scoped families
71
+ * (`task:<runId>:<name>`, `update:<runId>:<name>`, `wh:<runId>:<seq>`, `child:<id>`) are the FRAMEWORK
72
+ * composing the token before it hands it to `waitForSignal` — an external, user-named signal never is.
73
+ * Deriving a run-scoped token here would therefore reach NO waiter (a run parked on `waitForSignal('go')`
74
+ * would never see `signal('go')`), so the name is forwarded unchanged.
75
+ *
76
+ * `runId` is NOT dropped from the port on purpose: the `RunRequestResponder` uses it for the tenant
77
+ * ownership check (loads the run, rejects cross-tenant) BEFORE this delegates, and a `ProxyRunGateway`
78
+ * uses it to route to the run's owning control plane. So the signal stays name-addressed while the run
79
+ * boundary is still enforced one layer up.
80
+ */
81
+ signal(_runId: string, signal: string, payload?: unknown): Promise<RunResult | null>;
82
+ cancel(runId: string, opts?: {
83
+ compensate?: boolean;
84
+ }): Promise<RunResult | null>;
85
+ redispatchPending(runId: string): Promise<(RunResult & {
86
+ redispatched: number;
87
+ }) | null>;
88
+ subscribe(runId: string, onEvent: (event: EngineEvent) => void): () => void;
89
+ }
90
+ //# sourceMappingURL=store-run-gateway.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-run-gateway.d.ts","sourceRoot":"","sources":["../../../src/run-gateway/store-run-gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Store-backed {@link RunGateway} — the operator-side implementation. Wraps the local
3
+ * {@link WorkflowEngine} and forwards each read/control/stream verb to the engine's existing method,
4
+ * exactly the surface a `standalone` or `control-plane` deployment exposes. It holds NO state of its
5
+ * own beyond the injected engine and its declared role; all durability lives in the engine/store.
6
+ *
7
+ * Idiomatic AdonisJS: a plain class constructed with its dependency, wired by the package's provider /
8
+ * `defineConfig` (no framework DI decorators). A later `ProxyRunGateway` (Wave 2) will implement the
9
+ * SAME {@link RunGateway} interface over the transport for `tenant` deployments — nothing here needs
10
+ * to change for that.
11
+ */
12
+ export class StoreRunGateway {
13
+ #engine;
14
+ #role;
15
+ constructor(engine, options = {}) {
16
+ this.#engine = engine;
17
+ this.#role = options.role ?? 'standalone';
18
+ }
19
+ topology() {
20
+ return { role: this.#role };
21
+ }
22
+ getRun(runId) {
23
+ return this.#engine.getRun(runId);
24
+ }
25
+ listRuns(query) {
26
+ return this.#engine.listRuns(query);
27
+ }
28
+ getCheckpoints(runId) {
29
+ return this.#engine.listCheckpoints(runId);
30
+ }
31
+ getRunChildren(runId) {
32
+ return this.#engine.getRunChildren(runId);
33
+ }
34
+ async getSearchAttributes(runId) {
35
+ const run = await this.#engine.getRun(runId);
36
+ return run?.searchAttributes;
37
+ }
38
+ workerHealth() {
39
+ return this.#engine.workerHealth();
40
+ }
41
+ async start(workflow, input, opts) {
42
+ const runId = opts?.runId ?? globalThis.crypto.randomUUID();
43
+ // `start` is overloaded per ref kind (class | string) on the engine; a `WorkflowRef` union fits
44
+ // neither overload, so resolve to the string overload (the engine handles both at runtime).
45
+ return this.#engine.start(workflow, input, runId, opts);
46
+ }
47
+ /**
48
+ * Deliver `signal` (by name) with `payload` to the addressed run. Confirmed against the engine's
49
+ * waiter convention (design §8): `engine.signal` is GENUINELY GLOBAL-BY-NAME — `ctx.waitForSignal(name)`
50
+ * registers a waiter under the caller-chosen `name` VERBATIM, and `engine.signal(name, payload)` matches
51
+ * by that exact string. There is NO `signal:<runId>:<name>` waiter convention: the run-scoped families
52
+ * (`task:<runId>:<name>`, `update:<runId>:<name>`, `wh:<runId>:<seq>`, `child:<id>`) are the FRAMEWORK
53
+ * composing the token before it hands it to `waitForSignal` — an external, user-named signal never is.
54
+ * Deriving a run-scoped token here would therefore reach NO waiter (a run parked on `waitForSignal('go')`
55
+ * would never see `signal('go')`), so the name is forwarded unchanged.
56
+ *
57
+ * `runId` is NOT dropped from the port on purpose: the `RunRequestResponder` uses it for the tenant
58
+ * ownership check (loads the run, rejects cross-tenant) BEFORE this delegates, and a `ProxyRunGateway`
59
+ * uses it to route to the run's owning control plane. So the signal stays name-addressed while the run
60
+ * boundary is still enforced one layer up.
61
+ */
62
+ signal(_runId, signal, payload) {
63
+ return this.#engine.signal(signal, payload);
64
+ }
65
+ cancel(runId, opts) {
66
+ return this.#engine.cancel(runId, opts);
67
+ }
68
+ redispatchPending(runId) {
69
+ // The AdonisJS engine doesn't yet expose per-step re-dispatch; degrade to null when absent so the
70
+ // verb stays byte-compatible for a proxy without inventing engine behaviour. See RunGatewayEngine.
71
+ return this.#engine.redispatchPending?.(runId) ?? Promise.resolve(null);
72
+ }
73
+ subscribe(runId, onEvent) {
74
+ return this.#engine.subscribe((event) => {
75
+ if (event.runId === runId)
76
+ onEvent(event);
77
+ });
78
+ }
79
+ }
80
+ //# sourceMappingURL=store-run-gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-run-gateway.js","sourceRoot":"","sources":["../../../src/run-gateway/store-run-gateway.ts"],"names":[],"mappings":"AAuDA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,eAAe;IACjB,OAAO,CAAmB;IAC1B,KAAK,CAAiC;IAE/C,YAAY,MAAwB,EAAE,UAAkC,EAAE;QACxE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,IAAI,YAAY,CAAC;IAC5C,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,QAAQ,CAAC,KAAe;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAa;QACrC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,OAAO,GAAG,EAAE,gBAAgB,CAAC;IAC/B,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAqB,EAAE,KAAc,EAAE,IAAsB;QACvE,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC5D,gGAAgG;QAChG,4FAA4F;QAC5F,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,OAAiB;QACtD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,IAA+B;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,iBAAiB,CAAC,KAAa;QAC7B,kGAAkG;QAClG,mGAAmG;QACnG,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,CAAC;IAED,SAAS,CAAC,KAAa,EAAE,OAAqC;QAC5D,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACtC,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;gBAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { TenantVerifier } from '../config_types.js';
2
+ /**
3
+ * Mint the signed token a tenant pod presents: `` `${tenant}.${base64url(HMAC-SHA256(secret, tenant))}` ``.
4
+ * Put it on the pod's `config/durable.ts` as `tenant.token`; the pod carries only the token (never the
5
+ * secret), and the control plane verifies it with the SAME `secret` via {@link hmacTenantVerifier}.
6
+ */
7
+ export declare function signTenantToken(tenant: string, secret: string): string;
8
+ /**
9
+ * Build the control-plane-side {@link TenantVerifier} for {@link signTenantToken}-minted tokens under
10
+ * `secret`. It parses the token, recomputes the HMAC over the embedded tenant, and compares it in
11
+ * CONSTANT TIME (`timingSafeEqual`) — so a tampered tenant, a forged/absent signature, or a token
12
+ * signed with a different secret all return `null` (rejected). The verified tenant is DERIVED from the
13
+ * token's own claim, and the advisory `tenant` on the request body is ignored (spec §9).
14
+ *
15
+ * `capabilities`, if given, is a static grant stamped onto every {@link VerifiedTenant} this verifier
16
+ * authenticates (the HMAC token carries no scoped claims of its own); omit for none.
17
+ */
18
+ export declare function hmacTenantVerifier(secret: string, capabilities?: string[]): TenantVerifier;
19
+ //# sourceMappingURL=tenant-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-auth.d.ts","sourceRoot":"","sources":["../../../src/run-gateway/tenant-auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,oBAAoB,CAAC;AA8BzE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAY1F"}
@@ -0,0 +1,71 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ /**
3
+ * Layered tenant auth (spec §9). On a store-less pod the `tenant` on a `RunRequest`/`StartRunMessage`
4
+ * is only a CLAIM; without a signed proof the isolation boundary is meaningless. This module provides
5
+ * the default defense-in-depth on top of prefix/network isolation: a symmetric HMAC token a tenant pod
6
+ * carries and presents in the `tenant` wire field, which the control-plane {@link TenantVerifier}
7
+ * checks and DERIVES the real tenant from — never trusting the body.
8
+ *
9
+ * The token is deliberately tiny and self-describing (`<tenant>.<base64url-hmac>`), so it fits the
10
+ * existing byte-compatible `tenant: string` wire field with NO envelope change: an aviary/Python
11
+ * control plane that doesn't verify simply treats the whole string as the tenant name (and finds no
12
+ * runs — a safe failure), while an Adonis control plane configured with {@link hmacTenantVerifier}
13
+ * verifies the signature and scopes to the embedded tenant.
14
+ *
15
+ * HMAC (shared secret) is the zero-infra default; a deployment that prefers asymmetric / issued tokens
16
+ * supplies its own {@link TenantVerifier} in `config/durable.ts` instead. Prefix/network segmentation
17
+ * is a deployment concern (Redis ACLs, per-tenant prefixes) and lives outside this code.
18
+ */
19
+ /** Separator between the tenant claim and its signature in a token. `.` is URL/JSON-safe and never
20
+ * appears in the base64url signature, so the LAST `.` unambiguously splits the two halves — a tenant
21
+ * name may itself contain dots. */
22
+ const TOKEN_SEP = '.';
23
+ /** The HMAC digest, byte-for-byte, that authenticates `tenant` under `secret`. */
24
+ function sign(tenant, secret) {
25
+ return createHmac('sha256', secret).update(tenant).digest('base64url');
26
+ }
27
+ /**
28
+ * Mint the signed token a tenant pod presents: `` `${tenant}.${base64url(HMAC-SHA256(secret, tenant))}` ``.
29
+ * Put it on the pod's `config/durable.ts` as `tenant.token`; the pod carries only the token (never the
30
+ * secret), and the control plane verifies it with the SAME `secret` via {@link hmacTenantVerifier}.
31
+ */
32
+ export function signTenantToken(tenant, secret) {
33
+ return `${tenant}${TOKEN_SEP}${sign(tenant, secret)}`;
34
+ }
35
+ /**
36
+ * Build the control-plane-side {@link TenantVerifier} for {@link signTenantToken}-minted tokens under
37
+ * `secret`. It parses the token, recomputes the HMAC over the embedded tenant, and compares it in
38
+ * CONSTANT TIME (`timingSafeEqual`) — so a tampered tenant, a forged/absent signature, or a token
39
+ * signed with a different secret all return `null` (rejected). The verified tenant is DERIVED from the
40
+ * token's own claim, and the advisory `tenant` on the request body is ignored (spec §9).
41
+ *
42
+ * `capabilities`, if given, is a static grant stamped onto every {@link VerifiedTenant} this verifier
43
+ * authenticates (the HMAC token carries no scoped claims of its own); omit for none.
44
+ */
45
+ export function hmacTenantVerifier(secret, capabilities) {
46
+ return ({ token }) => {
47
+ // The signed token travels in the request's `tenant` field, so the responder passes it as `token`.
48
+ if (typeof token !== 'string' || token.length === 0)
49
+ return null;
50
+ const sep = token.lastIndexOf(TOKEN_SEP);
51
+ if (sep <= 0 || sep === token.length - 1)
52
+ return null; // no claim or no signature half
53
+ const tenant = token.slice(0, sep);
54
+ const presented = token.slice(sep + 1);
55
+ const expected = sign(tenant, secret);
56
+ if (!constantTimeEquals(presented, expected))
57
+ return null;
58
+ return capabilities !== undefined ? { tenant, capabilities } : { tenant };
59
+ };
60
+ }
61
+ /** Constant-time string compare that never throws on a length mismatch (`timingSafeEqual` requires
62
+ * equal-length buffers) and never short-circuits on the first differing byte — so a rejected token
63
+ * leaks nothing about how much of the signature matched. */
64
+ function constantTimeEquals(a, b) {
65
+ const ab = Buffer.from(a);
66
+ const bb = Buffer.from(b);
67
+ if (ab.length !== bb.length)
68
+ return false;
69
+ return timingSafeEqual(ab, bb);
70
+ }
71
+ //# sourceMappingURL=tenant-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-auth.js","sourceRoot":"","sources":["../../../src/run-gateway/tenant-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG1D;;;;;;;;;;;;;;;;GAgBG;AAEH;;oCAEoC;AACpC,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,kFAAkF;AAClF,SAAS,IAAI,CAAC,MAAc,EAAE,MAAc;IAC1C,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,MAAc;IAC5D,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,YAAuB;IACxE,OAAO,CAAC,EAAE,KAAK,EAAE,EAAyB,EAAE;QAC1C,mGAAmG;QACnG,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACjE,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,gCAAgC;QACvF,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1D,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5E,CAAC,CAAC;AACJ,CAAC;AAED;;6DAE6D;AAC7D,SAAS,kBAAkB,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -1,13 +1,26 @@
1
1
  import { WorkflowEngine } from '../engine.js';
2
+ import type { RunGateway } from '../run-gateway/interface.js';
2
3
  /**
3
- * Returns the singleton {@link WorkflowEngine} resolved from the container.
4
+ * The ACTIVE role's {@link RunGateway} the store-less-cluster read/control/start surface (design §8).
5
+ * On `standalone`/`control-plane` it is the store-backed `StoreRunGateway`; on `tenant` it is the
6
+ * `ProxyRunGateway` that round-trips over the wire. App/dashboard/CLI code reads the SAME object either
7
+ * way, so store presence is invisible above this line:
4
8
  *
5
- * Import it directly instead of resolving the engine yourself:
9
+ * ```ts
10
+ * import { runGateway } from '@adonis-agora/durable/services/main'
11
+ * const run = await runGateway.getRun(runId)
12
+ * ```
13
+ */
14
+ declare let runGateway: RunGateway;
15
+ /**
16
+ * The singleton {@link WorkflowEngine} — bound ONLY on the store roles (`standalone`/`control-plane`).
17
+ * A `tenant` pod owns no engine (structural isolation), so this stays `undefined` there; use
18
+ * {@link runGateway} for role-agnostic access.
6
19
  *
7
20
  * ```ts
8
21
  * import engine from '@adonis-agora/durable/services/main'
9
22
  * ```
10
23
  */
11
24
  declare let engine: WorkflowEngine;
12
- export { engine as default };
25
+ export { engine as default, runGateway };
13
26
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;GAQG;AACH,QAAA,IAAI,MAAM,EAAE,cAAc,CAAC;AAM3B,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;;;;;;GAUG;AACH,QAAA,IAAI,UAAU,EAAE,UAAU,CAAC;AAE3B;;;;;;;;GAQG;AACH,QAAA,IAAI,MAAM,EAAE,cAAc,CAAC;AAY3B,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -1,9 +1,22 @@
1
1
  import app from '@adonisjs/core/services/app';
2
2
  import { WorkflowEngine } from '../engine.js';
3
+ import { DURABLE_RUN_GATEWAY } from '../role_bindings.js';
3
4
  /**
4
- * Returns the singleton {@link WorkflowEngine} resolved from the container.
5
+ * The ACTIVE role's {@link RunGateway} the store-less-cluster read/control/start surface (design §8).
6
+ * On `standalone`/`control-plane` it is the store-backed `StoreRunGateway`; on `tenant` it is the
7
+ * `ProxyRunGateway` that round-trips over the wire. App/dashboard/CLI code reads the SAME object either
8
+ * way, so store presence is invisible above this line:
5
9
  *
6
- * Import it directly instead of resolving the engine yourself:
10
+ * ```ts
11
+ * import { runGateway } from '@adonis-agora/durable/services/main'
12
+ * const run = await runGateway.getRun(runId)
13
+ * ```
14
+ */
15
+ let runGateway;
16
+ /**
17
+ * The singleton {@link WorkflowEngine} — bound ONLY on the store roles (`standalone`/`control-plane`).
18
+ * A `tenant` pod owns no engine (structural isolation), so this stays `undefined` there; use
19
+ * {@link runGateway} for role-agnostic access.
7
20
  *
8
21
  * ```ts
9
22
  * import engine from '@adonis-agora/durable/services/main'
@@ -11,7 +24,13 @@ import { WorkflowEngine } from '../engine.js';
11
24
  */
12
25
  let engine;
13
26
  await app.booted(async () => {
14
- engine = await app.container.make(WorkflowEngine);
27
+ const config = app.config.get('durable', {});
28
+ const role = config.role ?? 'standalone';
29
+ runGateway = await app.container.make(DURABLE_RUN_GATEWAY);
30
+ // Expose the engine only for store roles — a tenant pod has no engine binding to resolve.
31
+ if (role !== 'tenant') {
32
+ engine = await app.container.make(WorkflowEngine);
33
+ }
15
34
  });
16
- export { engine as default };
35
+ export { engine as default, runGateway };
17
36
  //# sourceMappingURL=main.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,6BAA6B,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;GAQG;AACH,IAAI,MAAsB,CAAC;AAE3B,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IAC1B,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/services/main.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,6BAA6B,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D;;;;;;;;;;GAUG;AACH,IAAI,UAAsB,CAAC;AAE3B;;;;;;;;GAQG;AACH,IAAI,MAAsB,CAAC;AAE3B,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAgB,SAAS,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,YAAY,CAAC;IACzC,UAAU,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC3D,0FAA0F;IAC1F,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -40,6 +40,9 @@ export interface StepConfig {
40
40
  * worker dead and fails the dispatch with a `RemoteStepTimeout` (retryable — re-dispatches per
41
41
  * `retries`). Omit to wait indefinitely. */
42
42
  timeoutMs?: number | undefined;
43
+ /** Capabilities a live worker must advertise to run this step (design §7.5). Dispatch parks the run
44
+ * `blocked` when no live capable+protocol-compatible worker exists. Absent/empty = runs anywhere. */
45
+ requires?: string[] | undefined;
43
46
  }
44
47
  /**
45
48
  * A method carrying its `@Step`-stamped routing name (and, optionally, its dispatch policy).
@@ -1 +1 @@
1
- {"version":3,"file":"step-name-symbol.d.ts","sourceRoot":"","sources":["../../src/step-name-symbol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAiD,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACtC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B;;iDAE6C;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAMD;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,CAAC,CAC1D,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG;IACjC,CAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,CAAC,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAChD,CAAC;AAEF;;uEAEuE;AACvE,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI3D;AAED;;0EAE0E;AAC1E,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAIjE"}
1
+ {"version":3,"file":"step-name-symbol.d.ts","sourceRoot":"","sources":["../../src/step-name-symbol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,OAAO,MAAiD,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACtC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B;;iDAE6C;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B;0GACsG;IACtG,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACjC;AAMD;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,CAAC,CAC1D,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG;IACjC,CAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,CAAC,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAChD,CAAC;AAEF;;uEAEuE;AACvE,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI3D;AAED;;0EAE0E;AAC1E,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAIjE"}