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