@dudousxd/nestjs-durable 0.26.0 → 0.28.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/dist/index.js CHANGED
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
  });
9
9
 
10
10
  // src/decorators.ts
11
- import { WORKFLOW_NAME_KEY } from "@dudousxd/nestjs-durable-core";
11
+ import { DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, WORKFLOW_NAME_KEY } from "@dudousxd/nestjs-durable-core";
12
12
  import "reflect-metadata";
13
13
  var WORKFLOW_METADATA = Symbol("nestjs-durable:workflow");
14
14
  function Workflow(options) {
@@ -39,12 +39,36 @@ function getWorkflowMeta(target) {
39
39
  }
40
40
  __name(getWorkflowMeta, "getWorkflowMeta");
41
41
  var DURABLE_STEP_METADATA = Symbol("nestjs-durable:step-handler");
42
- function Step(name) {
43
- return (_target, _propertyKey, descriptor) => {
42
+ function stepConfigFrom(options) {
43
+ const config = {
44
+ retries: options.retries,
45
+ backoff: options.backoff,
46
+ backoffMs: options.backoffMs,
47
+ backoffMaxMs: options.backoffMaxMs,
48
+ jitter: options.jitter,
49
+ timeoutMs: options.timeoutMs
50
+ };
51
+ const hasAnyField = Object.values(config).some((value) => value !== void 0);
52
+ return hasAnyField ? config : void 0;
53
+ }
54
+ __name(stepConfigFrom, "stepConfigFrom");
55
+ function Step(nameOrOptions) {
56
+ return (target, propertyKey, descriptor) => {
57
+ const options = typeof nameOrOptions === "string" ? {
58
+ name: nameOrOptions
59
+ } : nameOrOptions ?? {};
60
+ const derivedName = options.name ?? `${target.constructor.name}.${String(propertyKey)}`;
44
61
  const meta = {
45
- name
62
+ name: derivedName,
63
+ input: options.input,
64
+ output: options.output
46
65
  };
47
66
  Reflect.defineMetadata(DURABLE_STEP_METADATA, meta, descriptor.value);
67
+ descriptor.value[DURABLE_STEP_NAME] = derivedName;
68
+ const config = stepConfigFrom(options);
69
+ if (config !== void 0) {
70
+ descriptor.value[DURABLE_STEP_CONFIG] = config;
71
+ }
48
72
  return descriptor;
49
73
  };
50
74
  }
@@ -67,7 +91,7 @@ function isDeadLetterHandler(method) {
67
91
  }
68
92
  __name(isDeadLetterHandler, "isDeadLetterHandler");
69
93
  var ON_EVENT_METADATA = Symbol("nestjs-durable:on-event");
70
- function OnEvent(...events) {
94
+ function OnDurableEvent(...events) {
71
95
  return (target) => {
72
96
  const existing = Reflect.getMetadata(ON_EVENT_METADATA, target) ?? [];
73
97
  Reflect.defineMetadata(ON_EVENT_METADATA, [
@@ -76,7 +100,8 @@ function OnEvent(...events) {
76
100
  ], target);
77
101
  };
78
102
  }
79
- __name(OnEvent, "OnEvent");
103
+ __name(OnDurableEvent, "OnDurableEvent");
104
+ var OnEvent = OnDurableEvent;
80
105
  function getOnEvents(meta, target) {
81
106
  const fromDecorator = Reflect.getMetadata(ON_EVENT_METADATA, target) ?? [];
82
107
  return [
@@ -88,10 +113,106 @@ function getOnEvents(meta, target) {
88
113
  }
89
114
  __name(getOnEvents, "getOnEvents");
90
115
 
116
+ // src/durable-start-client.ts
117
+ import { startRun } from "@dudousxd/durable-worker";
118
+ import { workflowName } from "@dudousxd/nestjs-durable-core";
119
+ import { Injectable } from "@nestjs/common";
120
+ function _ts_decorate(decorators, target, key, desc) {
121
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
122
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
123
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
124
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
125
+ }
126
+ __name(_ts_decorate, "_ts_decorate");
127
+ function _ts_metadata(k, v) {
128
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
129
+ }
130
+ __name(_ts_metadata, "_ts_metadata");
131
+ var DurableStartClient = class {
132
+ static {
133
+ __name(this, "DurableStartClient");
134
+ }
135
+ options;
136
+ deps;
137
+ tenant;
138
+ constructor(options, deps) {
139
+ this.options = options;
140
+ this.deps = deps;
141
+ this.tenant = options.partition ?? "default";
142
+ }
143
+ async start(workflow, input, runId = globalThis.crypto.randomUUID(), opts) {
144
+ const name = workflowName(workflow);
145
+ await startRun(this.options.connection, {
146
+ tenant: this.tenant,
147
+ workflow: name,
148
+ input,
149
+ runId,
150
+ // Option B: DO NOT pass `namespace` — the start-run queue stays the shared
151
+ // `durable-start-run` the operator consumes; tenant rides only as message data.
152
+ ...this.options.prefix !== void 0 ? {
153
+ prefix: this.options.prefix
154
+ } : {},
155
+ ...opts?.tags !== void 0 ? {
156
+ tags: opts.tags
157
+ } : {},
158
+ ...opts?.searchAttributes !== void 0 ? {
159
+ searchAttributes: opts.searchAttributes
160
+ } : {},
161
+ ...this.deps !== void 0 ? {
162
+ deps: this.deps
163
+ } : {}
164
+ });
165
+ return {
166
+ runId,
167
+ status: "pending"
168
+ };
169
+ }
170
+ cancel(_runId) {
171
+ return tenantUnsupported("cancel");
172
+ }
173
+ deleteRun(_runId) {
174
+ return tenantUnsupported("deleteRun");
175
+ }
176
+ // — everything below rejects: a tenant worker holds no store/driver, only the start channel. —
177
+ // The remaining WorkflowEngine surface WorkflowService delegates to (resume/waitForRun/signal/
178
+ // signalWithStart/publishEvent) all need the store or driver a tenant does not have. A tenant only
179
+ // ever calls `start`; these exist so a mistaken call fails with a CLEAR, named tenant error instead
180
+ // of a cryptic `this.engine.X is not a function` — the facade is honest about what it cannot do.
181
+ // Params mirror the WorkflowEngine surface being faced; every method rejects without reading them.
182
+ resume(_runId) {
183
+ return tenantUnsupported("resume");
184
+ }
185
+ waitForRun(_runId, _opts) {
186
+ return tenantUnsupported("waitForRun");
187
+ }
188
+ signal(_token, _payload) {
189
+ return tenantUnsupported("signal");
190
+ }
191
+ signalWithStart(_workflow, _input, _runId, _signal, _opts) {
192
+ return tenantUnsupported("signalWithStart");
193
+ }
194
+ publishEvent(_name, _payload, _opts) {
195
+ return tenantUnsupported("publishEvent");
196
+ }
197
+ };
198
+ DurableStartClient = _ts_decorate([
199
+ Injectable(),
200
+ _ts_metadata("design:type", Function),
201
+ _ts_metadata("design:paramtypes", [
202
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
203
+ typeof StartRunDeps === "undefined" ? Object : StartRunDeps
204
+ ])
205
+ ], DurableStartClient);
206
+ function tenantUnsupported(method) {
207
+ return Promise.reject(new Error(`${method}() is not available on a tenant worker (no store). Use the control plane for it.`));
208
+ }
209
+ __name(tenantUnsupported, "tenantUnsupported");
210
+
91
211
  // src/durable-worker.module.ts
92
212
  import { DurableWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from "@dudousxd/durable-worker";
93
- import { Inject, Injectable, Module } from "@nestjs/common";
94
- import { DiscoveryModule, DiscoveryService, MetadataScanner } from "@nestjs/core";
213
+ import { DURABLE_OPTIONS_CANONICAL } from "@dudousxd/nestjs-durable-core";
214
+ import { Inject, Injectable as Injectable2 } from "@nestjs/common";
215
+ import { DiscoveryService, MetadataScanner } from "@nestjs/core";
95
216
 
96
217
  // src/discovery-helpers.ts
97
218
  function scanWorkflows(discovery, register) {
@@ -118,7 +239,12 @@ function scanSteps(discovery, scanner, register) {
118
239
  if (typeof method !== "function") continue;
119
240
  const meta = getDurableStepMeta(method);
120
241
  if (!meta) continue;
121
- const handler = /* @__PURE__ */ __name((input, log) => method.call(instance, input, log), "handler");
242
+ const boundMethod = method;
243
+ const handler = /* @__PURE__ */ __name(async (input, log) => {
244
+ const validInput = meta.input ? meta.input.parse(input) : input;
245
+ const output = await boundMethod.call(instance, validInput, log);
246
+ return meta.output ? meta.output.parse(output) : output;
247
+ }, "handler");
122
248
  register(meta, handler);
123
249
  }
124
250
  }
@@ -126,46 +252,54 @@ function scanSteps(discovery, scanner, register) {
126
252
  __name(scanSteps, "scanSteps");
127
253
 
128
254
  // src/durable-worker.module.ts
129
- function _ts_decorate(decorators, target, key, desc) {
255
+ function _ts_decorate2(decorators, target, key, desc) {
130
256
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
131
257
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
132
258
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
133
259
  return c > 3 && r && Object.defineProperty(target, key, r), r;
134
260
  }
135
- __name(_ts_decorate, "_ts_decorate");
136
- function _ts_metadata(k, v) {
261
+ __name(_ts_decorate2, "_ts_decorate");
262
+ function _ts_metadata2(k, v) {
137
263
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
138
264
  }
139
- __name(_ts_metadata, "_ts_metadata");
265
+ __name(_ts_metadata2, "_ts_metadata");
140
266
  function _ts_param(paramIndex, decorator) {
141
267
  return function(target, key) {
142
268
  decorator(target, key, paramIndex);
143
269
  };
144
270
  }
145
271
  __name(_ts_param, "_ts_param");
146
- var DURABLE_WORKER_OPTIONS = Symbol("nestjs-durable:worker-options");
147
272
  var RUN_REDIS_WORKER = Symbol("nestjs-durable:run-redis-worker");
148
273
  var DURABLE_WORKER_RUNNERS = Symbol("nestjs-durable:worker-runners");
274
+ function isPureThinWorker(options) {
275
+ return options.store === void 0 && options.connection !== void 0;
276
+ }
277
+ __name(isPureThinWorker, "isPureThinWorker");
149
278
  var ThinWorkflowRegistrar = class {
150
279
  static {
151
280
  __name(this, "ThinWorkflowRegistrar");
152
281
  }
153
282
  discovery;
154
283
  runtime;
155
- constructor(discovery, runtime) {
284
+ options;
285
+ constructor(discovery, runtime, options) {
156
286
  this.discovery = discovery;
157
287
  this.runtime = runtime;
288
+ this.options = options;
158
289
  }
159
290
  onModuleInit() {
291
+ if (!isPureThinWorker(this.options)) return;
160
292
  scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
161
293
  }
162
294
  };
163
- ThinWorkflowRegistrar = _ts_decorate([
164
- Injectable(),
165
- _ts_metadata("design:type", Function),
166
- _ts_metadata("design:paramtypes", [
295
+ ThinWorkflowRegistrar = _ts_decorate2([
296
+ Injectable2(),
297
+ _ts_param(2, Inject(DURABLE_OPTIONS_CANONICAL)),
298
+ _ts_metadata2("design:type", Function),
299
+ _ts_metadata2("design:paramtypes", [
167
300
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
168
- typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime
301
+ typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
302
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
169
303
  ])
170
304
  ], ThinWorkflowRegistrar);
171
305
  var ThinStepRegistrar = class {
@@ -175,22 +309,27 @@ var ThinStepRegistrar = class {
175
309
  discovery;
176
310
  metadataScanner;
177
311
  runtime;
178
- constructor(discovery, metadataScanner, runtime) {
312
+ options;
313
+ constructor(discovery, metadataScanner, runtime, options) {
179
314
  this.discovery = discovery;
180
315
  this.metadataScanner = metadataScanner;
181
316
  this.runtime = runtime;
317
+ this.options = options;
182
318
  }
183
319
  onModuleInit() {
320
+ if (!isPureThinWorker(this.options)) return;
184
321
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
185
322
  }
186
323
  };
187
- ThinStepRegistrar = _ts_decorate([
188
- Injectable(),
189
- _ts_metadata("design:type", Function),
190
- _ts_metadata("design:paramtypes", [
324
+ ThinStepRegistrar = _ts_decorate2([
325
+ Injectable2(),
326
+ _ts_param(3, Inject(DURABLE_OPTIONS_CANONICAL)),
327
+ _ts_metadata2("design:type", Function),
328
+ _ts_metadata2("design:paramtypes", [
191
329
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
192
330
  typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
193
- typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime
331
+ typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
332
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
194
333
  ])
195
334
  ], ThinStepRegistrar);
196
335
  var ThinWorkerBootstrap = class {
@@ -209,113 +348,139 @@ var ThinWorkerBootstrap = class {
209
348
  this.runnersSink = runnersSink;
210
349
  }
211
350
  async onApplicationBootstrap() {
212
- for (const group of this.options.groups) {
213
- const handle = await this.runRedisWorker({
214
- runtime: this.runtime,
215
- group,
216
- connection: this.options.connection,
217
- ...this.options.prefix !== void 0 ? {
218
- prefix: this.options.prefix
219
- } : {},
220
- ...this.options.instanceId !== void 0 ? {
221
- instanceId: this.options.instanceId
222
- } : {},
223
- ...(this.options.concurrencyByGroup?.[group] ?? this.options.concurrency) !== void 0 ? {
224
- concurrency: this.options.concurrencyByGroup?.[group] ?? this.options.concurrency
225
- } : {}
226
- });
227
- this.runners.push(handle);
228
- this.runnersSink.push(handle);
229
- }
351
+ const options = this.options;
352
+ if (!isPureThinWorker(options) || options.connection === void 0) return;
353
+ const handle = await this.runRedisWorker({
354
+ runtime: this.runtime,
355
+ connection: options.connection,
356
+ ...options.partition !== void 0 ? {
357
+ partition: options.partition
358
+ } : {},
359
+ ...options.prefix !== void 0 ? {
360
+ prefix: options.prefix
361
+ } : {},
362
+ ...options.instanceId !== void 0 ? {
363
+ instanceId: options.instanceId
364
+ } : {},
365
+ ...options.concurrency !== void 0 ? {
366
+ concurrency: options.concurrency
367
+ } : {}
368
+ });
369
+ this.runners.push(handle);
370
+ this.runnersSink.push(handle);
230
371
  }
231
372
  async onApplicationShutdown() {
232
373
  await Promise.allSettled(this.runners.map((h) => h.close()));
233
374
  }
234
375
  };
235
- ThinWorkerBootstrap = _ts_decorate([
236
- Injectable(),
237
- _ts_param(1, Inject(DURABLE_WORKER_OPTIONS)),
376
+ ThinWorkerBootstrap = _ts_decorate2([
377
+ Injectable2(),
378
+ _ts_param(1, Inject(DURABLE_OPTIONS_CANONICAL)),
238
379
  _ts_param(2, Inject(RUN_REDIS_WORKER)),
239
380
  _ts_param(3, Inject(DURABLE_WORKER_RUNNERS)),
240
- _ts_metadata("design:type", Function),
241
- _ts_metadata("design:paramtypes", [
381
+ _ts_metadata2("design:type", Function),
382
+ _ts_metadata2("design:paramtypes", [
242
383
  typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
243
- typeof DurableWorkerModuleOptions === "undefined" ? Object : DurableWorkerModuleOptions,
384
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
244
385
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
245
386
  Array
246
387
  ])
247
388
  ], ThinWorkerBootstrap);
248
- var DurableWorkerModule = class _DurableWorkerModule {
249
- static {
250
- __name(this, "DurableWorkerModule");
251
- }
252
- static forRoot(options) {
253
- return _DurableWorkerModule.build({
254
- provide: DURABLE_WORKER_OPTIONS,
255
- useValue: options
256
- });
257
- }
258
- static forRootAsync(options) {
259
- return _DurableWorkerModule.build({
260
- provide: DURABLE_WORKER_OPTIONS,
261
- useFactory: options.useFactory,
262
- inject: options.inject ?? []
263
- });
264
- }
265
- static build(optionsProvider) {
266
- return {
267
- module: _DurableWorkerModule,
268
- imports: [
269
- DiscoveryModule
270
- ],
271
- providers: [
272
- optionsProvider,
273
- {
274
- provide: DurableWorkerRuntime,
275
- useFactory: /* @__PURE__ */ __name(() => new DurableWorkerRuntime(), "useFactory")
276
- },
277
- {
278
- provide: RUN_REDIS_WORKER,
279
- useValue: defaultRunRedisWorker
280
- },
281
- {
282
- provide: DURABLE_WORKER_RUNNERS,
283
- useValue: []
284
- },
285
- ThinWorkflowRegistrar,
286
- ThinStepRegistrar,
287
- ThinWorkerBootstrap
288
- ],
289
- exports: [
290
- DurableWorkerRuntime
389
+ function thinWorkerProviders() {
390
+ return [
391
+ {
392
+ provide: DurableWorkerRuntime,
393
+ // The runtime's `WorkflowWorker` falls back to its own `group` ctor param as the WORKFLOW's
394
+ // `workflowPartition` for any `ctx.step` call (see `workflow-context.ts`'s `resolveCallGroup`)
395
+ // — that fallback MUST equal this module's own `partition`, or a dispatched step's decision
396
+ // carries a mismatched token and the engine dispatches it to a queue nothing here subscribes
397
+ // to. Explicit `''` (never
398
+ // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`
399
+ // default parameter.
400
+ useFactory: /* @__PURE__ */ __name((options) => new DurableWorkerRuntime({
401
+ workflowGroup: options.partition ?? ""
402
+ }), "useFactory"),
403
+ inject: [
404
+ DURABLE_OPTIONS_CANONICAL
291
405
  ]
292
- };
293
- }
294
- };
295
- DurableWorkerModule = _ts_decorate([
296
- Module({})
297
- ], DurableWorkerModule);
406
+ },
407
+ {
408
+ provide: RUN_REDIS_WORKER,
409
+ useValue: defaultRunRedisWorker
410
+ },
411
+ {
412
+ provide: DURABLE_WORKER_RUNNERS,
413
+ useValue: []
414
+ },
415
+ ThinWorkflowRegistrar,
416
+ ThinStepRegistrar,
417
+ ThinWorkerBootstrap
418
+ ];
419
+ }
420
+ __name(thinWorkerProviders, "thinWorkerProviders");
421
+ function tenantGatewayUnavailable(method) {
422
+ return Promise.reject(new Error(`RunGateway.${method}() is not available \u2014 pass \`transport\` to use RunGateway.`));
423
+ }
424
+ __name(tenantGatewayUnavailable, "tenantGatewayUnavailable");
425
+ function unavailableRunGateway() {
426
+ return {
427
+ getRunDetail(_runId) {
428
+ return tenantGatewayUnavailable("getRunDetail");
429
+ },
430
+ listRuns(_query) {
431
+ return tenantGatewayUnavailable("listRuns");
432
+ },
433
+ cancel(_runId) {
434
+ return tenantGatewayUnavailable("cancel");
435
+ },
436
+ retry(_runId) {
437
+ return tenantGatewayUnavailable("retry");
438
+ },
439
+ continue(_runId) {
440
+ return tenantGatewayUnavailable("continue");
441
+ },
442
+ retryWithInput(_runId, _input) {
443
+ return tenantGatewayUnavailable("retryWithInput");
444
+ },
445
+ subscribe(_runId, _onEvent) {
446
+ throw new Error("RunGateway.subscribe() is not available \u2014 pass `transport` to use RunGateway.");
447
+ }
448
+ };
449
+ }
450
+ __name(unavailableRunGateway, "unavailableRunGateway");
298
451
 
299
452
  // src/durable.module.ts
300
- import { DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL6, STATE_STORE, STATE_STORE_CANONICAL as STATE_STORE_CANONICAL3, TRANSPORT, TRANSPORT_CANONICAL as TRANSPORT_CANONICAL3, WorkflowEngine as WorkflowEngine5 } from "@dudousxd/nestjs-durable-core";
301
- import { Module as Module2 } from "@nestjs/common";
302
- import { DiscoveryModule as DiscoveryModule2, ModuleRef } from "@nestjs/core";
453
+ import { DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL7, STATE_STORE, STATE_STORE_CANONICAL as STATE_STORE_CANONICAL4, TRANSPORT, TRANSPORT_CANONICAL as TRANSPORT_CANONICAL3, WorkflowEngine as WorkflowEngine6 } from "@dudousxd/nestjs-durable-core";
454
+ import { Inject as Inject8, Injectable as Injectable12, Module } from "@nestjs/common";
455
+ import { DiscoveryModule, ModuleRef } from "@nestjs/core";
303
456
 
304
457
  // src/durable-step.registrar.ts
305
- import { DURABLE_OPTIONS_CANONICAL, TRANSPORT_CANONICAL } from "@dudousxd/nestjs-durable-core";
306
- import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
458
+ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL2, TRANSPORT_CANONICAL } from "@dudousxd/nestjs-durable-core";
459
+ import { Inject as Inject2, Injectable as Injectable3 } from "@nestjs/common";
307
460
  import { DiscoveryService as DiscoveryService2, MetadataScanner as MetadataScanner2 } from "@nestjs/core";
308
- function _ts_decorate2(decorators, target, key, desc) {
461
+
462
+ // src/role.ts
463
+ function isOperatorRole(options) {
464
+ return options.store !== void 0;
465
+ }
466
+ __name(isOperatorRole, "isOperatorRole");
467
+ function isDrivingOperator(options) {
468
+ return isOperatorRole(options) && options.drive !== false;
469
+ }
470
+ __name(isDrivingOperator, "isDrivingOperator");
471
+
472
+ // src/durable-step.registrar.ts
473
+ function _ts_decorate3(decorators, target, key, desc) {
309
474
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
310
475
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
311
476
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
312
477
  return c > 3 && r && Object.defineProperty(target, key, r), r;
313
478
  }
314
- __name(_ts_decorate2, "_ts_decorate");
315
- function _ts_metadata2(k, v) {
479
+ __name(_ts_decorate3, "_ts_decorate");
480
+ function _ts_metadata3(k, v) {
316
481
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
317
482
  }
318
- __name(_ts_metadata2, "_ts_metadata");
483
+ __name(_ts_metadata3, "_ts_metadata");
319
484
  function _ts_param2(paramIndex, decorator) {
320
485
  return function(target, key) {
321
486
  decorator(target, key, paramIndex);
@@ -341,18 +506,18 @@ var DurableStepRegistrar = class {
341
506
  this.options = options;
342
507
  }
343
508
  onModuleInit() {
344
- if (this.options.worker === false) return;
509
+ if (!isDrivingOperator(this.options)) return;
345
510
  if (!supportsHandle(this.transport)) return;
346
511
  const transport = this.transport;
347
512
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => transport.handle(meta.name, handler));
348
513
  }
349
514
  };
350
- DurableStepRegistrar = _ts_decorate2([
351
- Injectable2(),
515
+ DurableStepRegistrar = _ts_decorate3([
516
+ Injectable3(),
352
517
  _ts_param2(2, Inject2(TRANSPORT_CANONICAL)),
353
- _ts_param2(3, Inject2(DURABLE_OPTIONS_CANONICAL)),
354
- _ts_metadata2("design:type", Function),
355
- _ts_metadata2("design:paramtypes", [
518
+ _ts_param2(3, Inject2(DURABLE_OPTIONS_CANONICAL2)),
519
+ _ts_metadata3("design:type", Function),
520
+ _ts_metadata3("design:paramtypes", [
356
521
  typeof DiscoveryService2 === "undefined" ? Object : DiscoveryService2,
357
522
  typeof MetadataScanner2 === "undefined" ? Object : MetadataScanner2,
358
523
  Object,
@@ -362,19 +527,19 @@ DurableStepRegistrar = _ts_decorate2([
362
527
 
363
528
  // src/entity.ts
364
529
  import { WorkflowEngine } from "@dudousxd/nestjs-durable-core";
365
- import { Injectable as Injectable3 } from "@nestjs/common";
530
+ import { Injectable as Injectable4 } from "@nestjs/common";
366
531
  import "reflect-metadata";
367
- function _ts_decorate3(decorators, target, key, desc) {
532
+ function _ts_decorate4(decorators, target, key, desc) {
368
533
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
369
534
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
370
535
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
371
536
  return c > 3 && r && Object.defineProperty(target, key, r), r;
372
537
  }
373
- __name(_ts_decorate3, "_ts_decorate");
374
- function _ts_metadata3(k, v) {
538
+ __name(_ts_decorate4, "_ts_decorate");
539
+ function _ts_metadata4(k, v) {
375
540
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
376
541
  }
377
- __name(_ts_metadata3, "_ts_metadata");
542
+ __name(_ts_metadata4, "_ts_metadata");
378
543
  var ENTITY_METADATA = Symbol("nestjs-durable:entity");
379
544
  var ENTITY_ON_METADATA = Symbol("nestjs-durable:entity-on");
380
545
  function Entity(options) {
@@ -431,49 +596,54 @@ var EntityService = class {
431
596
  return this.engine.getEntityState(name, key);
432
597
  }
433
598
  };
434
- EntityService = _ts_decorate3([
435
- Injectable3(),
436
- _ts_metadata3("design:type", Function),
437
- _ts_metadata3("design:paramtypes", [
599
+ EntityService = _ts_decorate4([
600
+ Injectable4(),
601
+ _ts_metadata4("design:type", Function),
602
+ _ts_metadata4("design:paramtypes", [
438
603
  typeof WorkflowEngine === "undefined" ? Object : WorkflowEngine
439
604
  ])
440
605
  ], EntityService);
441
606
 
442
607
  // src/in-app-worker.ts
443
608
  import { DurableWorkerRuntime as DurableWorkerRuntime2, runRedisWorker as defaultRunRedisWorker2 } from "@dudousxd/durable-worker";
444
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL2, RemoteWorkflowExecutor, TRANSPORT_CANONICAL as TRANSPORT_CANONICAL2 } from "@dudousxd/nestjs-durable-core";
445
- import { Inject as Inject3, Injectable as Injectable4 } from "@nestjs/common";
609
+ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL3, TRANSPORT_CANONICAL as TRANSPORT_CANONICAL2 } from "@dudousxd/nestjs-durable-core";
610
+ import { Inject as Inject3, Injectable as Injectable5 } from "@nestjs/common";
446
611
  import { DiscoveryService as DiscoveryService3, MetadataScanner as MetadataScanner3 } from "@nestjs/core";
447
- function _ts_decorate4(decorators, target, key, desc) {
612
+ function _ts_decorate5(decorators, target, key, desc) {
448
613
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
449
614
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
450
615
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
451
616
  return c > 3 && r && Object.defineProperty(target, key, r), r;
452
617
  }
453
- __name(_ts_decorate4, "_ts_decorate");
454
- function _ts_metadata4(k, v) {
618
+ __name(_ts_decorate5, "_ts_decorate");
619
+ function _ts_metadata5(k, v) {
455
620
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
456
621
  }
457
- __name(_ts_metadata4, "_ts_metadata");
622
+ __name(_ts_metadata5, "_ts_metadata");
458
623
  function _ts_param3(paramIndex, decorator) {
459
624
  return function(target, key) {
460
625
  decorator(target, key, paramIndex);
461
626
  };
462
627
  }
463
628
  __name(_ts_param3, "_ts_param");
464
- var IN_APP_WORKER_OPTIONS = Symbol("nestjs-durable:in-app-worker-options");
465
629
  var IN_APP_WORKER_BINDING = Symbol("nestjs-durable:in-app-worker-binding");
466
630
  var IN_APP_WORKER_RUNTIME = Symbol("nestjs-durable:in-app-worker-runtime");
467
631
  var IN_APP_RUN_REDIS_WORKER = Symbol("nestjs-durable:in-app-run-redis-worker");
468
632
  var IN_APP_WORKER_RUNNERS = Symbol("nestjs-durable:in-app-worker-runners");
633
+ function isCoLocatedWorker(options) {
634
+ return options.store !== void 0 && options.connection !== void 0;
635
+ }
636
+ __name(isCoLocatedWorker, "isCoLocatedWorker");
469
637
  function inAppWorkerBinding(transport, options) {
470
- if (!options) return null;
638
+ if (!isCoLocatedWorker(options)) return null;
471
639
  if (!transport?.dispatchWorkflowTask || !transport.onDecision) {
472
- throw new Error("inAppWorker requires a transport that carries workflow tasks (dispatchWorkflowTask + onDecision), e.g. BullMQTransport. An in-process transport cannot serve a group-served workflow.");
640
+ throw new Error("a co-located worker (store + connection) requires a transport that carries workflow tasks (dispatchWorkflowTask + onDecision), e.g. BullMQTransport. An in-process transport cannot serve a group-served workflow.");
473
641
  }
474
642
  return {
475
- group: options.group,
476
- executor: new RemoteWorkflowExecutor(transport, options.group)
643
+ transport,
644
+ ...options.partition !== void 0 ? {
645
+ partition: options.partition
646
+ } : {}
477
647
  };
478
648
  }
479
649
  __name(inAppWorkerBinding, "inAppWorkerBinding");
@@ -497,24 +667,27 @@ var InAppWorkerBootstrap = class {
497
667
  this.runnersSink = runnersSink;
498
668
  }
499
669
  onModuleInit() {
500
- if (!this.options) return;
670
+ if (!isCoLocatedWorker(this.options)) return;
501
671
  scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
502
672
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
503
673
  }
504
674
  async onApplicationBootstrap() {
505
- if (!this.options) return;
675
+ const options = this.options;
676
+ if (!isCoLocatedWorker(options) || options.connection === void 0) return;
506
677
  const handle = await this.runRedisWorker({
507
678
  runtime: this.runtime,
508
- group: this.options.group,
509
- connection: this.options.connection,
510
- ...this.options.prefix !== void 0 ? {
511
- prefix: this.options.prefix
679
+ connection: options.connection,
680
+ ...options.partition !== void 0 ? {
681
+ partition: options.partition
682
+ } : {},
683
+ ...options.prefix !== void 0 ? {
684
+ prefix: options.prefix
512
685
  } : {},
513
- ...this.options.instanceId !== void 0 ? {
514
- instanceId: this.options.instanceId
686
+ ...options.instanceId !== void 0 ? {
687
+ instanceId: options.instanceId
515
688
  } : {},
516
- ...this.options.concurrency !== void 0 ? {
517
- concurrency: this.options.concurrency
689
+ ...options.concurrency !== void 0 ? {
690
+ concurrency: options.concurrency
518
691
  } : {}
519
692
  });
520
693
  this.runners.push(handle);
@@ -524,17 +697,17 @@ var InAppWorkerBootstrap = class {
524
697
  await Promise.allSettled(this.runners.map((handle) => handle.close()));
525
698
  }
526
699
  };
527
- InAppWorkerBootstrap = _ts_decorate4([
528
- Injectable4(),
529
- _ts_param3(2, Inject3(IN_APP_WORKER_OPTIONS)),
700
+ InAppWorkerBootstrap = _ts_decorate5([
701
+ Injectable5(),
702
+ _ts_param3(2, Inject3(DURABLE_OPTIONS_CANONICAL3)),
530
703
  _ts_param3(3, Inject3(IN_APP_WORKER_RUNTIME)),
531
704
  _ts_param3(4, Inject3(IN_APP_RUN_REDIS_WORKER)),
532
705
  _ts_param3(5, Inject3(IN_APP_WORKER_RUNNERS)),
533
- _ts_metadata4("design:type", Function),
534
- _ts_metadata4("design:paramtypes", [
706
+ _ts_metadata5("design:type", Function),
707
+ _ts_metadata5("design:paramtypes", [
535
708
  typeof DiscoveryService3 === "undefined" ? Object : DiscoveryService3,
536
709
  typeof MetadataScanner3 === "undefined" ? Object : MetadataScanner3,
537
- Object,
710
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
538
711
  typeof DurableWorkerRuntime2 === "undefined" ? Object : DurableWorkerRuntime2,
539
712
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
540
713
  Array
@@ -544,23 +717,28 @@ function inAppWorkerProviders() {
544
717
  return [
545
718
  {
546
719
  provide: IN_APP_WORKER_BINDING,
547
- useFactory: /* @__PURE__ */ __name((transport, options) => inAppWorkerBinding(transport, options.inAppWorker ?? null), "useFactory"),
720
+ useFactory: /* @__PURE__ */ __name((transport, options) => inAppWorkerBinding(transport, options), "useFactory"),
548
721
  inject: [
549
722
  TRANSPORT_CANONICAL2,
550
- DURABLE_OPTIONS_CANONICAL2
723
+ DURABLE_OPTIONS_CANONICAL3
551
724
  ]
552
725
  },
553
726
  {
554
- provide: IN_APP_WORKER_OPTIONS,
555
- useFactory: /* @__PURE__ */ __name((options) => options.inAppWorker ?? null, "useFactory"),
727
+ provide: IN_APP_WORKER_RUNTIME,
728
+ // The runtime's `WorkflowWorker` uses its `group` ctor param as the WORKFLOW's
729
+ // `workflowPartition` for any `ctx.step` call (see `workflow-context.ts`'s `resolveCallGroup`)
730
+ // — that fallback MUST equal this app's own `partition`, or a dispatched step's decision
731
+ // carries a mismatched token and the engine dispatches it to a queue nothing here subscribes
732
+ // to. Explicit `''` (never
733
+ // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`
734
+ // default parameter.
735
+ useFactory: /* @__PURE__ */ __name((options) => new DurableWorkerRuntime2({
736
+ workflowGroup: options.partition ?? ""
737
+ }), "useFactory"),
556
738
  inject: [
557
- DURABLE_OPTIONS_CANONICAL2
739
+ DURABLE_OPTIONS_CANONICAL3
558
740
  ]
559
741
  },
560
- {
561
- provide: IN_APP_WORKER_RUNTIME,
562
- useFactory: /* @__PURE__ */ __name(() => new DurableWorkerRuntime2(), "useFactory")
563
- },
564
742
  {
565
743
  provide: IN_APP_RUN_REDIS_WORKER,
566
744
  useValue: defaultRunRedisWorker2
@@ -574,20 +752,142 @@ function inAppWorkerProviders() {
574
752
  }
575
753
  __name(inAppWorkerProviders, "inAppWorkerProviders");
576
754
 
755
+ // src/proxy-run-gateway.ts
756
+ import { Injectable as Injectable6 } from "@nestjs/common";
757
+ function _ts_decorate6(decorators, target, key, desc) {
758
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
759
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
760
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
761
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
762
+ }
763
+ __name(_ts_decorate6, "_ts_decorate");
764
+ function _ts_metadata6(k, v) {
765
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
766
+ }
767
+ __name(_ts_metadata6, "_ts_metadata");
768
+ var ProxyRunGateway = class {
769
+ static {
770
+ __name(this, "ProxyRunGateway");
771
+ }
772
+ transport;
773
+ tenant;
774
+ timeoutMs;
775
+ pending = /* @__PURE__ */ new Map();
776
+ constructor(transport, tenant, timeoutMs = 1e4) {
777
+ this.transport = transport;
778
+ this.tenant = tenant;
779
+ this.timeoutMs = timeoutMs;
780
+ this.transport.onRunReply?.((reply) => this.handleReply(reply));
781
+ }
782
+ handleReply(reply) {
783
+ const pending = this.pending.get(reply.requestId);
784
+ if (!pending) return;
785
+ clearTimeout(pending.timer);
786
+ this.pending.delete(reply.requestId);
787
+ if (reply.result.ok) {
788
+ pending.resolve(reply.result.data);
789
+ } else {
790
+ pending.reject(new Error(reply.result.error.message));
791
+ }
792
+ }
793
+ request(body) {
794
+ return new Promise((resolve, reject) => {
795
+ const requestId = globalThis.crypto.randomUUID();
796
+ const timer = setTimeout(() => {
797
+ this.pending.delete(requestId);
798
+ reject(new Error(`control plane did not respond to ${body.kind} within ${this.timeoutMs}ms`));
799
+ }, this.timeoutMs);
800
+ this.pending.set(requestId, {
801
+ resolve,
802
+ reject,
803
+ timer
804
+ });
805
+ this.transport.dispatchRunRequest?.({
806
+ requestId,
807
+ tenant: this.tenant,
808
+ body
809
+ }).catch((error) => {
810
+ const stillPending = this.pending.get(requestId);
811
+ if (!stillPending) return;
812
+ clearTimeout(stillPending.timer);
813
+ this.pending.delete(requestId);
814
+ stillPending.reject(error instanceof Error ? error : new Error(String(error)));
815
+ });
816
+ });
817
+ }
818
+ getRunDetail(runId) {
819
+ return this.request({
820
+ kind: "getRunDetail",
821
+ runId
822
+ });
823
+ }
824
+ listRuns(query) {
825
+ return this.request({
826
+ kind: "listRuns",
827
+ query
828
+ });
829
+ }
830
+ cancel(runId, opts) {
831
+ return this.request(opts === void 0 ? {
832
+ kind: "cancel",
833
+ runId
834
+ } : {
835
+ kind: "cancel",
836
+ runId,
837
+ opts
838
+ });
839
+ }
840
+ retry(runId) {
841
+ return this.request({
842
+ kind: "retry",
843
+ runId
844
+ });
845
+ }
846
+ continue(runId) {
847
+ return this.request({
848
+ kind: "continue",
849
+ runId
850
+ });
851
+ }
852
+ retryWithInput(runId, input) {
853
+ return this.request({
854
+ kind: "retryWithInput",
855
+ runId,
856
+ input
857
+ });
858
+ }
859
+ subscribe(runId, onEvent) {
860
+ const unsubscribe = this.transport.onTenantEvent?.(this.tenant, (evt) => {
861
+ if (evt.event.runId === runId) onEvent(evt.event);
862
+ });
863
+ return unsubscribe ?? (() => {
864
+ });
865
+ }
866
+ };
867
+ ProxyRunGateway = _ts_decorate6([
868
+ Injectable6(),
869
+ _ts_metadata6("design:type", Function),
870
+ _ts_metadata6("design:paramtypes", [
871
+ typeof Transport === "undefined" ? Object : Transport,
872
+ String,
873
+ void 0
874
+ ])
875
+ ], ProxyRunGateway);
876
+
577
877
  // src/retention-poller.ts
578
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL3, STATE_STORE_CANONICAL, TERMINAL_RUN_STATUSES, parseDuration } from "@dudousxd/nestjs-durable-core";
579
- import { Inject as Inject4, Injectable as Injectable5 } from "@nestjs/common";
580
- function _ts_decorate5(decorators, target, key, desc) {
878
+ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL4, STATE_STORE_CANONICAL, TERMINAL_RUN_STATUSES, parseDuration } from "@dudousxd/nestjs-durable-core";
879
+ import { Inject as Inject4, Injectable as Injectable7 } from "@nestjs/common";
880
+ function _ts_decorate7(decorators, target, key, desc) {
581
881
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
582
882
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
583
883
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
584
884
  return c > 3 && r && Object.defineProperty(target, key, r), r;
585
885
  }
586
- __name(_ts_decorate5, "_ts_decorate");
587
- function _ts_metadata5(k, v) {
886
+ __name(_ts_decorate7, "_ts_decorate");
887
+ function _ts_metadata7(k, v) {
588
888
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
589
889
  }
590
- __name(_ts_metadata5, "_ts_metadata");
890
+ __name(_ts_metadata7, "_ts_metadata");
591
891
  function _ts_param4(paramIndex, decorator) {
592
892
  return function(target, key) {
593
893
  decorator(target, key, paramIndex);
@@ -631,12 +931,19 @@ var RetentionPoller = class {
631
931
  this.store = store;
632
932
  this.options = options;
633
933
  }
934
+ /** Non-null once past the `isDrivingOperator` guard (which requires `options.store` to be set). */
935
+ requireStore() {
936
+ if (!this.store) {
937
+ throw new Error("unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set");
938
+ }
939
+ return this.store;
940
+ }
634
941
  async onApplicationBootstrap() {
635
- if (this.options.worker === false) return;
942
+ if (!isDrivingOperator(this.options)) return;
636
943
  const retention = this.options.retention;
637
944
  if (!retention || retention.policies.length === 0) return;
638
945
  validateRetention(retention);
639
- if (typeof this.store.pruneTerminalRuns !== "function") {
946
+ if (typeof this.requireStore().pruneTerminalRuns !== "function") {
640
947
  console.warn("[nestjs-durable] `retention` is configured but the store adapter does not implement pruneTerminalRuns; retention is disabled.");
641
948
  return;
642
949
  }
@@ -653,7 +960,8 @@ var RetentionPoller = class {
653
960
  async sweep() {
654
961
  if (this.sweeping) return;
655
962
  const retention = this.options.retention;
656
- const prune = this.store.pruneTerminalRuns;
963
+ const store = this.requireStore();
964
+ const prune = store.pruneTerminalRuns;
657
965
  if (!retention || typeof prune !== "function") return;
658
966
  this.sweeping = true;
659
967
  try {
@@ -661,7 +969,7 @@ var RetentionPoller = class {
661
969
  const now = Date.now();
662
970
  for (const policy of retention.policies) {
663
971
  for (let batch = 0; batch < MAX_BATCHES_PER_POLICY; batch++) {
664
- const deleted = await prune.call(this.store, policy, now, batchSize);
972
+ const deleted = await prune.call(store, policy, now, batchSize);
665
973
  if (deleted < batchSize) break;
666
974
  }
667
975
  }
@@ -670,37 +978,244 @@ var RetentionPoller = class {
670
978
  }
671
979
  }
672
980
  };
673
- RetentionPoller = _ts_decorate5([
674
- Injectable5(),
981
+ RetentionPoller = _ts_decorate7([
982
+ Injectable7(),
675
983
  _ts_param4(0, Inject4(STATE_STORE_CANONICAL)),
676
- _ts_param4(1, Inject4(DURABLE_OPTIONS_CANONICAL3)),
677
- _ts_metadata5("design:type", Function),
678
- _ts_metadata5("design:paramtypes", [
679
- typeof StateStore === "undefined" ? Object : StateStore,
984
+ _ts_param4(1, Inject4(DURABLE_OPTIONS_CANONICAL4)),
985
+ _ts_metadata7("design:type", Function),
986
+ _ts_metadata7("design:paramtypes", [
987
+ Object,
680
988
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
681
989
  ])
682
990
  ], RetentionPoller);
683
991
 
684
- // src/timer-poller.ts
685
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL4, WorkflowEngine as WorkflowEngine2, runSchedules } from "@dudousxd/nestjs-durable-core";
686
- import { Inject as Inject5, Injectable as Injectable6 } from "@nestjs/common";
687
- function _ts_decorate6(decorators, target, key, desc) {
992
+ // src/run-request-responder.ts
993
+ var RunRequestResponder = class {
994
+ static {
995
+ __name(this, "RunRequestResponder");
996
+ }
997
+ transport;
998
+ gateway;
999
+ constructor(transport, gateway) {
1000
+ this.transport = transport;
1001
+ this.gateway = gateway;
1002
+ }
1003
+ /** Register the consumer on the transport. Each request is answered independently; a handler
1004
+ * failure never throws back into the transport (errors are captured into an error reply). */
1005
+ start() {
1006
+ this.transport.onRunRequest(async (msg) => {
1007
+ const reply = await this.handle(msg);
1008
+ await this.transport.publishRunReply(reply);
1009
+ });
1010
+ }
1011
+ async handle(msg) {
1012
+ const { body } = msg;
1013
+ if (body.kind === "listRuns") {
1014
+ const data = await this.gateway.listRuns({
1015
+ ...body.query,
1016
+ namespace: msg.tenant
1017
+ });
1018
+ return {
1019
+ requestId: msg.requestId,
1020
+ result: {
1021
+ ok: true,
1022
+ data
1023
+ }
1024
+ };
1025
+ }
1026
+ const detail = await this.gateway.getRunDetail(body.runId);
1027
+ if (detail && detail.run.namespace !== msg.tenant) {
1028
+ return {
1029
+ requestId: msg.requestId,
1030
+ result: {
1031
+ ok: false,
1032
+ error: {
1033
+ message: "run belongs to another tenant",
1034
+ code: "cross-tenant"
1035
+ }
1036
+ }
1037
+ };
1038
+ }
1039
+ if (body.kind === "getRunDetail") {
1040
+ return {
1041
+ requestId: msg.requestId,
1042
+ result: {
1043
+ ok: true,
1044
+ data: detail
1045
+ }
1046
+ };
1047
+ }
1048
+ try {
1049
+ const data = await this.callVerb(body);
1050
+ return {
1051
+ requestId: msg.requestId,
1052
+ result: {
1053
+ ok: true,
1054
+ data
1055
+ }
1056
+ };
1057
+ } catch (err) {
1058
+ return {
1059
+ requestId: msg.requestId,
1060
+ result: {
1061
+ ok: false,
1062
+ error: {
1063
+ message: err instanceof Error ? err.message : String(err)
1064
+ }
1065
+ }
1066
+ };
1067
+ }
1068
+ }
1069
+ callVerb(body) {
1070
+ switch (body.kind) {
1071
+ case "cancel":
1072
+ return this.gateway.cancel(body.runId, body.opts);
1073
+ case "retry":
1074
+ return this.gateway.retry(body.runId);
1075
+ case "continue":
1076
+ return this.gateway.continue(body.runId);
1077
+ case "retryWithInput":
1078
+ return this.gateway.retryWithInput(body.runId, body.input);
1079
+ }
1080
+ }
1081
+ };
1082
+
1083
+ // src/store-run-gateway.ts
1084
+ import { STATE_STORE_CANONICAL as STATE_STORE_CANONICAL2, WorkflowEngine as WorkflowEngine2 } from "@dudousxd/nestjs-durable-core";
1085
+ import { Inject as Inject5, Injectable as Injectable8 } from "@nestjs/common";
1086
+ function _ts_decorate8(decorators, target, key, desc) {
688
1087
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
689
1088
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
690
1089
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
691
1090
  return c > 3 && r && Object.defineProperty(target, key, r), r;
692
1091
  }
693
- __name(_ts_decorate6, "_ts_decorate");
694
- function _ts_metadata6(k, v) {
1092
+ __name(_ts_decorate8, "_ts_decorate");
1093
+ function _ts_metadata8(k, v) {
695
1094
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
696
1095
  }
697
- __name(_ts_metadata6, "_ts_metadata");
1096
+ __name(_ts_metadata8, "_ts_metadata");
698
1097
  function _ts_param5(paramIndex, decorator) {
699
1098
  return function(target, key) {
700
1099
  decorator(target, key, paramIndex);
701
1100
  };
702
1101
  }
703
1102
  __name(_ts_param5, "_ts_param");
1103
+ var StoreRunGateway = class {
1104
+ static {
1105
+ __name(this, "StoreRunGateway");
1106
+ }
1107
+ store;
1108
+ engine;
1109
+ constructor(store, engine) {
1110
+ this.store = store;
1111
+ this.engine = engine;
1112
+ }
1113
+ async getRunDetail(runId) {
1114
+ const run = await this.store.getRun(runId);
1115
+ if (!run) return null;
1116
+ const [timeline, children] = await Promise.all([
1117
+ this.store.listCheckpoints(runId),
1118
+ this.engine.getRunChildren(runId)
1119
+ ]);
1120
+ return {
1121
+ run,
1122
+ timeline,
1123
+ children
1124
+ };
1125
+ }
1126
+ listRuns(query) {
1127
+ return this.store.listRuns(query);
1128
+ }
1129
+ cancel(runId, opts) {
1130
+ return this.engine.cancel(runId, opts);
1131
+ }
1132
+ /** Re-enqueue (dispatch model) instead of resuming inline — a worker picks the run up and replays it. */
1133
+ retry(runId) {
1134
+ return this.engine.requeue(runId);
1135
+ }
1136
+ continue(runId) {
1137
+ return this.engine.continue(runId);
1138
+ }
1139
+ retryWithInput(runId, input) {
1140
+ return this.engine.retryWithInput(runId, input);
1141
+ }
1142
+ subscribe(runId, onEvent) {
1143
+ return this.engine.subscribe((event) => {
1144
+ if (event.runId === runId) onEvent(event);
1145
+ });
1146
+ }
1147
+ };
1148
+ StoreRunGateway = _ts_decorate8([
1149
+ Injectable8(),
1150
+ _ts_param5(0, Inject5(STATE_STORE_CANONICAL2)),
1151
+ _ts_metadata8("design:type", Function),
1152
+ _ts_metadata8("design:paramtypes", [
1153
+ typeof StateStore === "undefined" ? Object : StateStore,
1154
+ typeof WorkflowEngine2 === "undefined" ? Object : WorkflowEngine2
1155
+ ])
1156
+ ], StoreRunGateway);
1157
+
1158
+ // src/tenant-event-republisher.ts
1159
+ function isTerminalRunEvent(event) {
1160
+ return event.type === "run.completed" || event.type === "run.failed";
1161
+ }
1162
+ __name(isTerminalRunEvent, "isTerminalRunEvent");
1163
+ var TenantEventRepublisher = class {
1164
+ static {
1165
+ __name(this, "TenantEventRepublisher");
1166
+ }
1167
+ store;
1168
+ publish;
1169
+ runNamespaces = /* @__PURE__ */ new Map();
1170
+ constructor(store, publish) {
1171
+ this.store = store;
1172
+ this.publish = publish;
1173
+ }
1174
+ async handle(event) {
1175
+ const namespace = await this.namespaceFor(event);
1176
+ if (isTerminalRunEvent(event)) this.runNamespaces.delete(event.runId);
1177
+ if (!namespace || namespace === "default") return;
1178
+ await this.publish({
1179
+ tenant: namespace,
1180
+ event
1181
+ }).catch(() => void 0);
1182
+ }
1183
+ async namespaceFor(event) {
1184
+ if (this.runNamespaces.has(event.runId)) {
1185
+ const cached = this.runNamespaces.get(event.runId);
1186
+ return cached === null ? void 0 : cached;
1187
+ }
1188
+ if (event.namespace !== void 0) {
1189
+ this.runNamespaces.set(event.runId, event.namespace === "default" ? null : event.namespace);
1190
+ return event.namespace;
1191
+ }
1192
+ const run = await this.store.getRun(event.runId);
1193
+ const tenantNamespace = run?.namespace !== void 0 && run.namespace !== "default" ? run.namespace : null;
1194
+ this.runNamespaces.set(event.runId, tenantNamespace);
1195
+ return tenantNamespace === null ? void 0 : tenantNamespace;
1196
+ }
1197
+ };
1198
+
1199
+ // src/timer-poller.ts
1200
+ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL5, WorkflowEngine as WorkflowEngine3, runSchedules } from "@dudousxd/nestjs-durable-core";
1201
+ import { Inject as Inject6, Injectable as Injectable9 } from "@nestjs/common";
1202
+ function _ts_decorate9(decorators, target, key, desc) {
1203
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1204
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1205
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1206
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1207
+ }
1208
+ __name(_ts_decorate9, "_ts_decorate");
1209
+ function _ts_metadata9(k, v) {
1210
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1211
+ }
1212
+ __name(_ts_metadata9, "_ts_metadata");
1213
+ function _ts_param6(paramIndex, decorator) {
1214
+ return function(target, key) {
1215
+ decorator(target, key, paramIndex);
1216
+ };
1217
+ }
1218
+ __name(_ts_param6, "_ts_param");
704
1219
  var TimerPoller = class {
705
1220
  static {
706
1221
  __name(this, "TimerPoller");
@@ -715,7 +1230,7 @@ var TimerPoller = class {
715
1230
  this.options = options;
716
1231
  }
717
1232
  async onApplicationBootstrap() {
718
- if (this.options.worker === false) return;
1233
+ if (!isDrivingOperator(this.options)) return;
719
1234
  this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => void this.engine.runOne(runId));
720
1235
  await this.poll();
721
1236
  const intervalMs = this.options.timerPollMs ?? 1e3;
@@ -745,22 +1260,23 @@ var TimerPoller = class {
745
1260
  }
746
1261
  }
747
1262
  };
748
- TimerPoller = _ts_decorate6([
749
- Injectable6(),
750
- _ts_param5(1, Inject5(DURABLE_OPTIONS_CANONICAL4)),
751
- _ts_metadata6("design:type", Function),
752
- _ts_metadata6("design:paramtypes", [
753
- typeof WorkflowEngine2 === "undefined" ? Object : WorkflowEngine2,
1263
+ TimerPoller = _ts_decorate9([
1264
+ Injectable9(),
1265
+ _ts_param6(1, Inject6(DURABLE_OPTIONS_CANONICAL5)),
1266
+ _ts_metadata9("design:type", Function),
1267
+ _ts_metadata9("design:paramtypes", [
1268
+ typeof WorkflowEngine3 === "undefined" ? Object : WorkflowEngine3,
754
1269
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
755
1270
  ])
756
1271
  ], TimerPoller);
757
1272
 
758
1273
  // src/tokens.ts
759
1274
  var CONTEXT_ACCESSOR = Symbol.for("@dudousxd/nestjs-context:accessor");
1275
+ var RUN_GATEWAY = Symbol.for("nestjs-durable:run-gateway");
760
1276
 
761
1277
  // src/workflow.registrar.ts
762
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL5, STATE_STORE_CANONICAL as STATE_STORE_CANONICAL2, WorkflowEngine as WorkflowEngine3, parseDuration as parseDuration2, workflowName } from "@dudousxd/nestjs-durable-core";
763
- import { Inject as Inject6, Injectable as Injectable7 } from "@nestjs/common";
1278
+ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL6, RemoteWorkflowExecutor, STATE_STORE_CANONICAL as STATE_STORE_CANONICAL3, WorkflowEngine as WorkflowEngine4, bindWorkflowClass, parseDuration as parseDuration2, sanitizeQueueToken, tenantGroup, workflowName as workflowName2 } from "@dudousxd/nestjs-durable-core";
1279
+ import { Inject as Inject7, Injectable as Injectable10 } from "@nestjs/common";
764
1280
  import { DiscoveryService as DiscoveryService4, MetadataScanner as MetadataScanner4 } from "@nestjs/core";
765
1281
 
766
1282
  // src/input-validation.ts
@@ -803,23 +1319,23 @@ function isStepInterceptor(target) {
803
1319
  __name(isStepInterceptor, "isStepInterceptor");
804
1320
 
805
1321
  // src/workflow.registrar.ts
806
- function _ts_decorate7(decorators, target, key, desc) {
1322
+ function _ts_decorate10(decorators, target, key, desc) {
807
1323
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
808
1324
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
809
1325
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
810
1326
  return c > 3 && r && Object.defineProperty(target, key, r), r;
811
1327
  }
812
- __name(_ts_decorate7, "_ts_decorate");
813
- function _ts_metadata7(k, v) {
1328
+ __name(_ts_decorate10, "_ts_decorate");
1329
+ function _ts_metadata10(k, v) {
814
1330
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
815
1331
  }
816
- __name(_ts_metadata7, "_ts_metadata");
817
- function _ts_param6(paramIndex, decorator) {
1332
+ __name(_ts_metadata10, "_ts_metadata");
1333
+ function _ts_param7(paramIndex, decorator) {
818
1334
  return function(target, key) {
819
1335
  decorator(target, key, paramIndex);
820
1336
  };
821
1337
  }
822
- __name(_ts_param6, "_ts_param");
1338
+ __name(_ts_param7, "_ts_param");
823
1339
  var WorkflowRegistrar = class {
824
1340
  static {
825
1341
  __name(this, "WorkflowRegistrar");
@@ -839,13 +1355,15 @@ var WorkflowRegistrar = class {
839
1355
  this.inAppWorker = inAppWorker;
840
1356
  }
841
1357
  async onApplicationBootstrap() {
842
- if (this.options.worker === false) return;
1358
+ if (!isDrivingOperator(this.options)) return;
843
1359
  await this.engine.recoverIncomplete();
844
1360
  }
845
1361
  /** On deploy/shutdown: stop picking up new runs and wait for in-flight ones to settle, then close
846
1362
  * the transport(s) so the broker workers stop consuming and connections are released. Closing
847
- * AFTER the drain keeps the transport alive while in-flight runs dispatch/await their remote steps. */
1363
+ * AFTER the drain keeps the transport alive while in-flight runs dispatch/await their remote steps.
1364
+ * Operator only — a thin worker (no `store`) holds no engine to drain. */
848
1365
  async onApplicationShutdown() {
1366
+ if (!isOperatorRole(this.options)) return;
849
1367
  await this.engine.drain(this.options.shutdownTimeoutMs);
850
1368
  const transports = [
851
1369
  this.options.transport,
@@ -854,8 +1372,13 @@ var WorkflowRegistrar = class {
854
1372
  await Promise.allSettled(transports.map((t) => t?.close?.()));
855
1373
  }
856
1374
  async onModuleInit() {
1375
+ if (!isOperatorRole(this.options)) return;
1376
+ const store = this.store;
1377
+ if (!store) {
1378
+ throw new Error("unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set");
1379
+ }
857
1380
  if (this.options.autoSchema !== false) {
858
- await this.store.ensureSchema?.();
1381
+ await store.ensureSchema?.();
859
1382
  }
860
1383
  const deadLetterByWorkflow = /* @__PURE__ */ new Map();
861
1384
  for (const wrapper of this.discovery.getProviders()) {
@@ -889,13 +1412,23 @@ var WorkflowRegistrar = class {
889
1412
  onEvent: getOnEvents(meta, workflowCtor),
890
1413
  eventBatch,
891
1414
  // Uniform dispatch (opt-in): when an in-app worker is configured, register the body GROUP-SERVED
892
- // so the engine dispatches its turns to the app's own group instead of running it inline; the
893
- // co-located worker consumer replays the same body. Absent → the inline fast path (unchanged).
1415
+ // so the engine dispatches its turns over the transport instead of running it inline; the
1416
+ // co-located worker consumer (Task 5: one queue PER REGISTERED NAME) replays the same body.
1417
+ // The routing token — and the executor that dispatches under it — MUST be keyed by THIS
1418
+ // workflow's own name (`tenantGroup(sanitizeQueueToken(meta.name), partition)`), not a single
1419
+ // group shared across every discovered `@Workflow`: a fixed shared token would dispatch every
1420
+ // workflow's turns to one queue while the co-located worker subscribes one queue per name,
1421
+ // so a turn dispatched under the wrong token would never be consumed. Absent → the inline fast
1422
+ // path (unchanged).
894
1423
  ...this.inAppWorker ? {
895
- group: this.inAppWorker.group,
896
- executor: this.inAppWorker.executor
1424
+ group: tenantGroup(sanitizeQueueToken(meta.name), this.inAppWorker.partition),
1425
+ executor: new RemoteWorkflowExecutor(this.inAppWorker.transport, meta.name, this.inAppWorker.partition)
897
1426
  } : {}
898
1427
  });
1428
+ bindWorkflowClass(workflowCtor, {
1429
+ start: /* @__PURE__ */ __name((name, input, runId, opts) => this.engine.start(name, input, runId, opts), "start"),
1430
+ waitForRun: /* @__PURE__ */ __name((runId, opts) => this.engine.waitForRun(runId, opts), "waitForRun")
1431
+ });
899
1432
  const inline = this.findDeadLetterHandler(workflow);
900
1433
  if (inline && meta.deadLetterWorkflow) {
901
1434
  throw new Error(`@Workflow ${meta.name} declares both an inline @DeadLetter() method and a deadLetterWorkflow option. Use one: the inline handler, or the reference.`);
@@ -905,7 +1438,7 @@ var WorkflowRegistrar = class {
905
1438
  this.engine.register(dlqName, meta.version, inline);
906
1439
  deadLetterByWorkflow.set(meta.name, dlqName);
907
1440
  } else if (meta.deadLetterWorkflow) {
908
- deadLetterByWorkflow.set(meta.name, workflowName(meta.deadLetterWorkflow));
1441
+ deadLetterByWorkflow.set(meta.name, workflowName2(meta.deadLetterWorkflow));
909
1442
  }
910
1443
  });
911
1444
  this.installDeadLetterRouting(deadLetterByWorkflow);
@@ -927,31 +1460,40 @@ var WorkflowRegistrar = class {
927
1460
  * a `dlq:<runId>` id, so re-recovery never double-dispatches.
928
1461
  */
929
1462
  installDeadLetterRouting(byWorkflow) {
930
- const fallback = this.options.deadLetterWorkflow ? workflowName(this.options.deadLetterWorkflow) : void 0;
1463
+ const fallback = this.options.deadLetterWorkflow ? workflowName2(this.options.deadLetterWorkflow) : void 0;
931
1464
  if (byWorkflow.size === 0 && !fallback) return;
932
1465
  this.engine.onDead((run) => {
933
1466
  const target = byWorkflow.get(run.workflow) ?? fallback;
934
1467
  if (!target) return;
935
- void this.engine.start(target, {
936
- deadRunId: run.id,
937
- workflow: run.workflow,
938
- input: run.input,
939
- error: run.error
940
- }, `dlq:${run.id}`).catch(() => void 0);
1468
+ void this.engine.start(
1469
+ target,
1470
+ {
1471
+ deadRunId: run.id,
1472
+ workflow: run.workflow,
1473
+ input: run.input,
1474
+ error: run.error
1475
+ },
1476
+ `dlq:${run.id}`,
1477
+ // Route the dead-letter handler to the dead run's OWN tenant so an operator dispatches it to
1478
+ // that tenant's worker group (`target@<tenant>`), not the bare group.
1479
+ {
1480
+ namespace: run.namespace
1481
+ }
1482
+ ).catch(() => void 0);
941
1483
  });
942
1484
  }
943
1485
  };
944
- WorkflowRegistrar = _ts_decorate7([
945
- Injectable7(),
946
- _ts_param6(3, Inject6(STATE_STORE_CANONICAL2)),
947
- _ts_param6(4, Inject6(DURABLE_OPTIONS_CANONICAL5)),
948
- _ts_param6(5, Inject6(IN_APP_WORKER_BINDING)),
949
- _ts_metadata7("design:type", Function),
950
- _ts_metadata7("design:paramtypes", [
1486
+ WorkflowRegistrar = _ts_decorate10([
1487
+ Injectable10(),
1488
+ _ts_param7(3, Inject7(STATE_STORE_CANONICAL3)),
1489
+ _ts_param7(4, Inject7(DURABLE_OPTIONS_CANONICAL6)),
1490
+ _ts_param7(5, Inject7(IN_APP_WORKER_BINDING)),
1491
+ _ts_metadata10("design:type", Function),
1492
+ _ts_metadata10("design:paramtypes", [
951
1493
  typeof DiscoveryService4 === "undefined" ? Object : DiscoveryService4,
952
1494
  typeof MetadataScanner4 === "undefined" ? Object : MetadataScanner4,
953
- typeof WorkflowEngine3 === "undefined" ? Object : WorkflowEngine3,
954
- typeof StateStore === "undefined" ? Object : StateStore,
1495
+ typeof WorkflowEngine4 === "undefined" ? Object : WorkflowEngine4,
1496
+ Object,
955
1497
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
956
1498
  Object
957
1499
  ])
@@ -959,19 +1501,19 @@ WorkflowRegistrar = _ts_decorate7([
959
1501
 
960
1502
  // src/workflow.service.ts
961
1503
  import { randomUUID } from "node:crypto";
962
- import { WorkflowEngine as WorkflowEngine4 } from "@dudousxd/nestjs-durable-core";
963
- import { Injectable as Injectable8 } from "@nestjs/common";
964
- function _ts_decorate8(decorators, target, key, desc) {
1504
+ import { WorkflowEngine as WorkflowEngine5 } from "@dudousxd/nestjs-durable-core";
1505
+ import { Injectable as Injectable11 } from "@nestjs/common";
1506
+ function _ts_decorate11(decorators, target, key, desc) {
965
1507
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
966
1508
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
967
1509
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
968
1510
  return c > 3 && r && Object.defineProperty(target, key, r), r;
969
1511
  }
970
- __name(_ts_decorate8, "_ts_decorate");
971
- function _ts_metadata8(k, v) {
1512
+ __name(_ts_decorate11, "_ts_decorate");
1513
+ function _ts_metadata11(k, v) {
972
1514
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
973
1515
  }
974
- __name(_ts_metadata8, "_ts_metadata");
1516
+ __name(_ts_metadata11, "_ts_metadata");
975
1517
  var WorkflowService = class {
976
1518
  static {
977
1519
  __name(this, "WorkflowService");
@@ -1003,7 +1545,7 @@ var WorkflowService = class {
1003
1545
  }
1004
1546
  /**
1005
1547
  * Publish a named event. Resumes runs waiting on it via `ctx.waitForEvent(name, { match })` and
1006
- * starts a fresh run of every workflow subscribed via `@Workflow({ onEvent })` / `@OnEvent` (the
1548
+ * starts a fresh run of every workflow subscribed via `@Workflow({ onEvent })` / `@OnDurableEvent` (the
1007
1549
  * payload becomes its input). Pass `opts.id` to dedupe redeliveries. Returns how many runs it
1008
1550
  * touched (resumed + started).
1009
1551
  */
@@ -1011,22 +1553,32 @@ var WorkflowService = class {
1011
1553
  return this.engine.publishEvent(name, payload, opts);
1012
1554
  }
1013
1555
  };
1014
- WorkflowService = _ts_decorate8([
1015
- Injectable8(),
1016
- _ts_metadata8("design:type", Function),
1017
- _ts_metadata8("design:paramtypes", [
1018
- typeof WorkflowEngine4 === "undefined" ? Object : WorkflowEngine4
1556
+ WorkflowService = _ts_decorate11([
1557
+ Injectable11(),
1558
+ _ts_metadata11("design:type", Function),
1559
+ _ts_metadata11("design:paramtypes", [
1560
+ typeof WorkflowEngine5 === "undefined" ? Object : WorkflowEngine5
1019
1561
  ])
1020
1562
  ], WorkflowService);
1021
1563
 
1022
1564
  // src/durable.module.ts
1023
- function _ts_decorate9(decorators, target, key, desc) {
1565
+ function _ts_decorate12(decorators, target, key, desc) {
1024
1566
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1025
1567
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1026
1568
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1027
1569
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1028
1570
  }
1029
- __name(_ts_decorate9, "_ts_decorate");
1571
+ __name(_ts_decorate12, "_ts_decorate");
1572
+ function _ts_metadata12(k, v) {
1573
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1574
+ }
1575
+ __name(_ts_metadata12, "_ts_metadata");
1576
+ function _ts_param8(paramIndex, decorator) {
1577
+ return function(target, key) {
1578
+ decorator(target, key, paramIndex);
1579
+ };
1580
+ }
1581
+ __name(_ts_param8, "_ts_param");
1030
1582
  function resolveAccessor(moduleRef) {
1031
1583
  try {
1032
1584
  return moduleRef.get(CONTEXT_ACCESSOR, {
@@ -1066,20 +1618,103 @@ function isControlPlane(x) {
1066
1618
  return !!x && typeof x.publishControl === "function" && typeof x.onControl === "function";
1067
1619
  }
1068
1620
  __name(isControlPlane, "isControlPlane");
1621
+ function isScopeableStore(store) {
1622
+ return typeof store.withScope === "function";
1623
+ }
1624
+ __name(isScopeableStore, "isScopeableStore");
1625
+ function scopedStore(store, options) {
1626
+ if (options.scopeReads !== true || options.namespace === void 0) return store;
1627
+ if (!isScopeableStore(store)) return store;
1628
+ return store.withScope({
1629
+ namespace: options.namespace
1630
+ });
1631
+ }
1632
+ __name(scopedStore, "scopedStore");
1633
+ function assertValidRole(options) {
1634
+ const hasStore = options.store !== void 0;
1635
+ const hasConnection = options.connection !== void 0;
1636
+ if (!hasStore && !hasConnection) {
1637
+ throw new Error("a durable module needs either a `store` (operator) or a `connection` (worker)");
1638
+ }
1639
+ if (hasStore && options.transport === void 0) {
1640
+ throw new Error("an operator (`store`) needs a `transport`");
1641
+ }
1642
+ }
1643
+ __name(assertValidRole, "assertValidRole");
1644
+ var RunGatewayBootstrap = class RunGatewayBootstrap2 {
1645
+ static {
1646
+ __name(this, "RunGatewayBootstrap");
1647
+ }
1648
+ engine;
1649
+ transport;
1650
+ gateway;
1651
+ store;
1652
+ options;
1653
+ unsubscribe;
1654
+ constructor(engine, transport, gateway, store, options) {
1655
+ this.engine = engine;
1656
+ this.transport = transport;
1657
+ this.gateway = gateway;
1658
+ this.store = store;
1659
+ this.options = options;
1660
+ }
1661
+ onApplicationBootstrap() {
1662
+ if (!isDrivingOperator(this.options) || !this.transport) return;
1663
+ const store = this.store;
1664
+ if (!store) return;
1665
+ const { onRunRequest, publishRunReply, publishTenantEvent } = this.transport;
1666
+ if (typeof onRunRequest === "function" && typeof publishRunReply === "function") {
1667
+ const runRequestTransport = {
1668
+ onRunRequest: onRunRequest.bind(this.transport),
1669
+ publishRunReply: publishRunReply.bind(this.transport)
1670
+ };
1671
+ new RunRequestResponder(runRequestTransport, this.gateway).start();
1672
+ }
1673
+ if (typeof publishTenantEvent === "function") {
1674
+ const republisher = new TenantEventRepublisher(store, publishTenantEvent.bind(this.transport));
1675
+ this.unsubscribe = this.engine.subscribe((event) => {
1676
+ void republisher.handle(event);
1677
+ });
1678
+ }
1679
+ }
1680
+ onModuleDestroy() {
1681
+ this.unsubscribe?.();
1682
+ }
1683
+ };
1684
+ RunGatewayBootstrap = _ts_decorate12([
1685
+ Injectable12(),
1686
+ _ts_param8(1, Inject8(TRANSPORT_CANONICAL3)),
1687
+ _ts_param8(2, Inject8(RUN_GATEWAY)),
1688
+ _ts_param8(3, Inject8(STATE_STORE_CANONICAL4)),
1689
+ _ts_param8(4, Inject8(DURABLE_OPTIONS_CANONICAL7)),
1690
+ _ts_metadata12("design:type", Function),
1691
+ _ts_metadata12("design:paramtypes", [
1692
+ typeof WorkflowEngine6 === "undefined" ? Object : WorkflowEngine6,
1693
+ Object,
1694
+ typeof RunGateway === "undefined" ? Object : RunGateway,
1695
+ Object,
1696
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
1697
+ ])
1698
+ ], RunGatewayBootstrap);
1069
1699
  var DurableModule = class _DurableModule {
1070
1700
  static {
1071
1701
  __name(this, "DurableModule");
1072
1702
  }
1073
1703
  static forRoot(options) {
1704
+ assertValidRole(options);
1074
1705
  return _DurableModule.build({
1075
- provide: DURABLE_OPTIONS_CANONICAL6,
1706
+ provide: DURABLE_OPTIONS_CANONICAL7,
1076
1707
  useValue: options
1077
1708
  });
1078
1709
  }
1079
1710
  static forRootAsync(options) {
1080
1711
  return _DurableModule.build({
1081
- provide: DURABLE_OPTIONS_CANONICAL6,
1082
- useFactory: options.useFactory,
1712
+ provide: DURABLE_OPTIONS_CANONICAL7,
1713
+ useFactory: /* @__PURE__ */ __name(async (...args) => {
1714
+ const resolved = await options.useFactory(...args);
1715
+ assertValidRole(resolved);
1716
+ return resolved;
1717
+ }, "useFactory"),
1083
1718
  inject: options.inject ?? []
1084
1719
  });
1085
1720
  }
@@ -1088,28 +1723,28 @@ var DurableModule = class _DurableModule {
1088
1723
  module: _DurableModule,
1089
1724
  global: true,
1090
1725
  imports: [
1091
- DiscoveryModule2
1726
+ DiscoveryModule
1092
1727
  ],
1093
1728
  providers: [
1094
1729
  optionsProvider,
1095
1730
  {
1096
- provide: STATE_STORE_CANONICAL3,
1097
- useFactory: /* @__PURE__ */ __name((options) => options.store, "useFactory"),
1731
+ provide: STATE_STORE_CANONICAL4,
1732
+ useFactory: /* @__PURE__ */ __name((options) => options.store !== void 0 ? scopedStore(options.store, options) : null, "useFactory"),
1098
1733
  inject: [
1099
- DURABLE_OPTIONS_CANONICAL6
1734
+ DURABLE_OPTIONS_CANONICAL7
1100
1735
  ]
1101
1736
  },
1102
1737
  {
1103
1738
  provide: TRANSPORT_CANONICAL3,
1104
1739
  useFactory: /* @__PURE__ */ __name((options) => options.transport ?? null, "useFactory"),
1105
1740
  inject: [
1106
- DURABLE_OPTIONS_CANONICAL6
1741
+ DURABLE_OPTIONS_CANONICAL7
1107
1742
  ]
1108
1743
  },
1109
1744
  // Legacy back-compat aliases (deprecated tokens still resolve to the same instances):
1110
1745
  {
1111
1746
  provide: STATE_STORE,
1112
- useExisting: STATE_STORE_CANONICAL3
1747
+ useExisting: STATE_STORE_CANONICAL4
1113
1748
  },
1114
1749
  {
1115
1750
  provide: TRANSPORT,
@@ -1117,47 +1752,59 @@ var DurableModule = class _DurableModule {
1117
1752
  },
1118
1753
  {
1119
1754
  provide: DURABLE_OPTIONS,
1120
- useExisting: DURABLE_OPTIONS_CANONICAL6
1755
+ useExisting: DURABLE_OPTIONS_CANONICAL7
1121
1756
  },
1122
1757
  {
1123
- provide: WorkflowEngine5,
1124
- useFactory: /* @__PURE__ */ __name(async (store, transport, opts, moduleRef) => {
1125
- const primary = transport ?? opts.transports?.[0]?.transport;
1758
+ // Shared token, bound EXACTLY once: a real engine for the operator role, or a store-less
1759
+ // `DurableStartClient` facade for a thin worker (no `store`) — either way, `WorkflowService`
1760
+ // and app code call `engine.start(...)` unchanged.
1761
+ provide: WorkflowEngine6,
1762
+ useFactory: /* @__PURE__ */ __name(async (options, store, transport, moduleRef) => {
1763
+ if (options.store === void 0) {
1764
+ return new DurableStartClient(options);
1765
+ }
1766
+ if (!store) {
1767
+ throw new Error("unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set");
1768
+ }
1769
+ const primary = transport ?? options.transports?.[0]?.transport;
1126
1770
  const accessor = resolveAccessor(moduleRef);
1127
- const context = opts.context ?? (accessor ? () => carrierFromAccessor(accessor) : void 0);
1771
+ const context = options.context ?? (accessor ? () => carrierFromAccessor(accessor) : void 0);
1128
1772
  const runtime = accessor ? await resolveContextRuntime() : void 0;
1129
1773
  const rehydrate = runtime && ((carrier, fn) => carrier && Object.keys(carrier).length > 0 ? runtime.deserialize(carrier, fn) : fn());
1130
- const engine = new WorkflowEngine5({
1774
+ const engine = new WorkflowEngine6({
1131
1775
  store,
1132
1776
  transport: transport ?? void 0,
1133
- transports: opts.transports,
1134
- controlPlane: opts.controlPlane ?? (isControlPlane(primary) ? primary : void 0),
1135
- leaseMs: opts.leaseMs,
1136
- admission: opts.admission,
1137
- maxRecoveryAttempts: opts.maxRecoveryAttempts,
1138
- remoteAdvanceSilenceMs: opts.remoteAdvanceSilenceMs,
1139
- instanceId: opts.instanceId,
1140
- namespace: opts.namespace,
1141
- webhookUrl: opts.webhookUrl,
1142
- traceparent: opts.traceparent,
1777
+ transports: options.transports,
1778
+ controlPlane: options.controlPlane ?? (isControlPlane(primary) ? primary : void 0),
1779
+ leaseMs: options.leaseMs,
1780
+ admission: options.admission,
1781
+ maxRecoveryAttempts: options.maxRecoveryAttempts,
1782
+ remoteAdvanceSilenceMs: options.remoteAdvanceSilenceMs,
1783
+ instanceId: options.instanceId,
1784
+ namespace: options.namespace,
1785
+ webhookUrl: options.webhookUrl,
1786
+ traceparent: options.traceparent,
1143
1787
  context,
1144
1788
  rehydrate: rehydrate || void 0,
1145
- compensationRetries: opts.compensationRetries,
1146
- // A non-worker (API/dashboard) instance must not run workflows: enqueue-only, leaving
1147
- // each `pending` run in the store for a worker's `runPending` poll. Workers use the
1148
- // default in-process dispatcher (execute locally).
1149
- runDispatcher: opts.worker === false ? {
1789
+ compensationRetries: options.compensationRetries,
1790
+ // A non-driving (dashboard/API) operator must not run workflows: enqueue-only, leaving
1791
+ // each `pending` run in the store for a DRIVING instance's poll. A driving operator gets
1792
+ // the engine's default in-process dispatcher: for a body registered inline, it runs
1793
+ // locally; for one registered GROUP-SERVED (co-located worker) or left unregistered, the
1794
+ // SAME default dispatcher routes it out remotely (group-served executor, or convention
1795
+ // dispatch — always on) instead.
1796
+ runDispatcher: options.drive === false ? {
1150
1797
  dispatch: /* @__PURE__ */ __name(() => {
1151
1798
  }, "dispatch")
1152
1799
  } : void 0
1153
1800
  });
1154
- for (const queue of opts.queues ?? []) engine.registerQueue(queue);
1801
+ for (const queue of options.queues ?? []) engine.registerQueue(queue);
1155
1802
  return engine;
1156
1803
  }, "useFactory"),
1157
1804
  inject: [
1158
- STATE_STORE_CANONICAL3,
1805
+ DURABLE_OPTIONS_CANONICAL7,
1806
+ STATE_STORE_CANONICAL4,
1159
1807
  TRANSPORT_CANONICAL3,
1160
- DURABLE_OPTIONS_CANONICAL6,
1161
1808
  ModuleRef
1162
1809
  ]
1163
1810
  },
@@ -1167,53 +1814,82 @@ var DurableModule = class _DurableModule {
1167
1814
  DurableStepRegistrar,
1168
1815
  TimerPoller,
1169
1816
  RetentionPoller,
1170
- // In-app worker (uniform dispatch, opt-in): inert when `inAppWorker` is unset — the binding
1171
- // resolves to null and the bootstrap no-ops, so a plain DurableModule is unchanged.
1172
- ...inAppWorkerProviders()
1817
+ // Tenant run gateway: bound EXACTLY once — the store-backed `StoreRunGateway` for an operator,
1818
+ // or (for a thin worker, no `store`) a `ProxyRunGateway` over `transport` when given, else a
1819
+ // gateway whose every method rejects with a clear tenant error.
1820
+ {
1821
+ provide: RUN_GATEWAY,
1822
+ useFactory: /* @__PURE__ */ __name((options, store, engine) => {
1823
+ if (options.store !== void 0) {
1824
+ if (!store) {
1825
+ throw new Error("unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set");
1826
+ }
1827
+ return new StoreRunGateway(store, engine);
1828
+ }
1829
+ return options.transport ? new ProxyRunGateway(options.transport, options.partition ?? "default", options.runGatewayTimeoutMs) : unavailableRunGateway();
1830
+ }, "useFactory"),
1831
+ inject: [
1832
+ DURABLE_OPTIONS_CANONICAL7,
1833
+ STATE_STORE_CANONICAL4,
1834
+ WorkflowEngine6
1835
+ ]
1836
+ },
1837
+ RunGatewayBootstrap,
1838
+ // Co-located in-app worker (uniform dispatch): inert unless BOTH `store` and `connection` are
1839
+ // set — see `in-app-worker.ts`.
1840
+ ...inAppWorkerProviders(),
1841
+ // Pure thin-worker consumer: inert unless `connection` is set WITHOUT `store` — see
1842
+ // `durable-worker.module.ts`.
1843
+ ...thinWorkerProviders()
1173
1844
  ],
1174
1845
  exports: [
1175
1846
  WorkflowService,
1176
1847
  EntityService,
1177
- WorkflowEngine5,
1848
+ WorkflowEngine6,
1178
1849
  STATE_STORE,
1179
- STATE_STORE_CANONICAL3,
1850
+ STATE_STORE_CANONICAL4,
1180
1851
  TRANSPORT,
1181
1852
  TRANSPORT_CANONICAL3,
1182
- DURABLE_OPTIONS_CANONICAL6
1853
+ DURABLE_OPTIONS_CANONICAL7,
1854
+ RUN_GATEWAY
1183
1855
  ]
1184
1856
  };
1185
1857
  }
1186
1858
  };
1187
- DurableModule = _ts_decorate9([
1188
- Module2({})
1859
+ DurableModule = _ts_decorate12([
1860
+ Module({})
1189
1861
  ], DurableModule);
1190
1862
  export {
1191
1863
  CONTEXT_ACCESSOR,
1192
1864
  DEAD_LETTER_METADATA,
1193
1865
  DURABLE_STEP_METADATA,
1194
- DURABLE_WORKER_OPTIONS,
1195
1866
  DURABLE_WORKER_RUNNERS,
1196
1867
  DeadLetter,
1197
1868
  DurableModule,
1869
+ DurableStartClient,
1198
1870
  DurableStep,
1199
- DurableWorkerModule,
1200
1871
  ENTITY_METADATA,
1201
1872
  ENTITY_ON_METADATA,
1202
1873
  Entity,
1203
1874
  EntityService,
1204
1875
  IN_APP_RUN_REDIS_WORKER,
1205
1876
  IN_APP_WORKER_BINDING,
1206
- IN_APP_WORKER_OPTIONS,
1207
1877
  IN_APP_WORKER_RUNNERS,
1208
1878
  IN_APP_WORKER_RUNTIME,
1209
1879
  InAppWorkerBootstrap,
1210
1880
  ON_EVENT_METADATA,
1211
1881
  On,
1882
+ OnDurableEvent,
1212
1883
  OnEvent,
1884
+ ProxyRunGateway,
1885
+ RUN_GATEWAY,
1213
1886
  RUN_REDIS_WORKER,
1887
+ RunRequestResponder,
1214
1888
  STEP_INTERCEPTOR_METADATA,
1215
1889
  Step,
1216
1890
  StepInterceptor,
1891
+ StoreRunGateway,
1892
+ TenantEventRepublisher,
1217
1893
  ThinStepRegistrar,
1218
1894
  ThinWorkerBootstrap,
1219
1895
  ThinWorkflowRegistrar,
@@ -1227,6 +1903,10 @@ export {
1227
1903
  getWorkflowMeta,
1228
1904
  inAppWorkerProviders,
1229
1905
  isDeadLetterHandler,
1230
- isStepInterceptor
1906
+ isDrivingOperator,
1907
+ isOperatorRole,
1908
+ isStepInterceptor,
1909
+ thinWorkerProviders,
1910
+ unavailableRunGateway
1231
1911
  };
1232
1912
  //# sourceMappingURL=index.js.map