@dudousxd/nestjs-durable 0.25.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,16 +39,41 @@ function getWorkflowMeta(target) {
39
39
  }
40
40
  __name(getWorkflowMeta, "getWorkflowMeta");
41
41
  var DURABLE_STEP_METADATA = Symbol("nestjs-durable:step-handler");
42
- function DurableStep(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
  }
51
- __name(DurableStep, "DurableStep");
75
+ __name(Step, "Step");
76
+ var DurableStep = Step;
52
77
  function getDurableStepMeta(method) {
53
78
  return Reflect.getMetadata(DURABLE_STEP_METADATA, method);
54
79
  }
@@ -87,10 +112,106 @@ function getOnEvents(meta, target) {
87
112
  }
88
113
  __name(getOnEvents, "getOnEvents");
89
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
+
90
210
  // src/durable-worker.module.ts
91
211
  import { DurableWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from "@dudousxd/durable-worker";
92
- import { Inject, Injectable, Module } from "@nestjs/common";
93
- 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";
94
215
 
95
216
  // src/discovery-helpers.ts
96
217
  function scanWorkflows(discovery, register) {
@@ -117,7 +238,12 @@ function scanSteps(discovery, scanner, register) {
117
238
  if (typeof method !== "function") continue;
118
239
  const meta = getDurableStepMeta(method);
119
240
  if (!meta) continue;
120
- 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");
121
247
  register(meta, handler);
122
248
  }
123
249
  }
@@ -125,46 +251,54 @@ function scanSteps(discovery, scanner, register) {
125
251
  __name(scanSteps, "scanSteps");
126
252
 
127
253
  // src/durable-worker.module.ts
128
- function _ts_decorate(decorators, target, key, desc) {
254
+ function _ts_decorate2(decorators, target, key, desc) {
129
255
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
130
256
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
131
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;
132
258
  return c > 3 && r && Object.defineProperty(target, key, r), r;
133
259
  }
134
- __name(_ts_decorate, "_ts_decorate");
135
- function _ts_metadata(k, v) {
260
+ __name(_ts_decorate2, "_ts_decorate");
261
+ function _ts_metadata2(k, v) {
136
262
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
137
263
  }
138
- __name(_ts_metadata, "_ts_metadata");
264
+ __name(_ts_metadata2, "_ts_metadata");
139
265
  function _ts_param(paramIndex, decorator) {
140
266
  return function(target, key) {
141
267
  decorator(target, key, paramIndex);
142
268
  };
143
269
  }
144
270
  __name(_ts_param, "_ts_param");
145
- var DURABLE_WORKER_OPTIONS = Symbol("nestjs-durable:worker-options");
146
271
  var RUN_REDIS_WORKER = Symbol("nestjs-durable:run-redis-worker");
147
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");
148
277
  var ThinWorkflowRegistrar = class {
149
278
  static {
150
279
  __name(this, "ThinWorkflowRegistrar");
151
280
  }
152
281
  discovery;
153
282
  runtime;
154
- constructor(discovery, runtime) {
283
+ options;
284
+ constructor(discovery, runtime, options) {
155
285
  this.discovery = discovery;
156
286
  this.runtime = runtime;
287
+ this.options = options;
157
288
  }
158
289
  onModuleInit() {
290
+ if (!isPureThinWorker(this.options)) return;
159
291
  scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
160
292
  }
161
293
  };
162
- ThinWorkflowRegistrar = _ts_decorate([
163
- Injectable(),
164
- _ts_metadata("design:type", Function),
165
- _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", [
166
299
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
167
- typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime
300
+ typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
301
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
168
302
  ])
169
303
  ], ThinWorkflowRegistrar);
170
304
  var ThinStepRegistrar = class {
@@ -174,22 +308,27 @@ var ThinStepRegistrar = class {
174
308
  discovery;
175
309
  metadataScanner;
176
310
  runtime;
177
- constructor(discovery, metadataScanner, runtime) {
311
+ options;
312
+ constructor(discovery, metadataScanner, runtime, options) {
178
313
  this.discovery = discovery;
179
314
  this.metadataScanner = metadataScanner;
180
315
  this.runtime = runtime;
316
+ this.options = options;
181
317
  }
182
318
  onModuleInit() {
319
+ if (!isPureThinWorker(this.options)) return;
183
320
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
184
321
  }
185
322
  };
186
- ThinStepRegistrar = _ts_decorate([
187
- Injectable(),
188
- _ts_metadata("design:type", Function),
189
- _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", [
190
328
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
191
329
  typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
192
- typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime
330
+ typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
331
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
193
332
  ])
194
333
  ], ThinStepRegistrar);
195
334
  var ThinWorkerBootstrap = class {
@@ -208,113 +347,139 @@ var ThinWorkerBootstrap = class {
208
347
  this.runnersSink = runnersSink;
209
348
  }
210
349
  async onApplicationBootstrap() {
211
- for (const group of this.options.groups) {
212
- const handle = await this.runRedisWorker({
213
- runtime: this.runtime,
214
- group,
215
- connection: this.options.connection,
216
- ...this.options.prefix !== void 0 ? {
217
- prefix: this.options.prefix
218
- } : {},
219
- ...this.options.instanceId !== void 0 ? {
220
- instanceId: this.options.instanceId
221
- } : {},
222
- ...(this.options.concurrencyByGroup?.[group] ?? this.options.concurrency) !== void 0 ? {
223
- concurrency: this.options.concurrencyByGroup?.[group] ?? this.options.concurrency
224
- } : {}
225
- });
226
- this.runners.push(handle);
227
- this.runnersSink.push(handle);
228
- }
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);
229
370
  }
230
371
  async onApplicationShutdown() {
231
372
  await Promise.allSettled(this.runners.map((h) => h.close()));
232
373
  }
233
374
  };
234
- ThinWorkerBootstrap = _ts_decorate([
235
- Injectable(),
236
- _ts_param(1, Inject(DURABLE_WORKER_OPTIONS)),
375
+ ThinWorkerBootstrap = _ts_decorate2([
376
+ Injectable2(),
377
+ _ts_param(1, Inject(DURABLE_OPTIONS_CANONICAL)),
237
378
  _ts_param(2, Inject(RUN_REDIS_WORKER)),
238
379
  _ts_param(3, Inject(DURABLE_WORKER_RUNNERS)),
239
- _ts_metadata("design:type", Function),
240
- _ts_metadata("design:paramtypes", [
380
+ _ts_metadata2("design:type", Function),
381
+ _ts_metadata2("design:paramtypes", [
241
382
  typeof DurableWorkerRuntime === "undefined" ? Object : DurableWorkerRuntime,
242
- typeof DurableWorkerModuleOptions === "undefined" ? Object : DurableWorkerModuleOptions,
383
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
243
384
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
244
385
  Array
245
386
  ])
246
387
  ], ThinWorkerBootstrap);
247
- var DurableWorkerModule = class _DurableWorkerModule {
248
- static {
249
- __name(this, "DurableWorkerModule");
250
- }
251
- static forRoot(options) {
252
- return _DurableWorkerModule.build({
253
- provide: DURABLE_WORKER_OPTIONS,
254
- useValue: options
255
- });
256
- }
257
- static forRootAsync(options) {
258
- return _DurableWorkerModule.build({
259
- provide: DURABLE_WORKER_OPTIONS,
260
- useFactory: options.useFactory,
261
- inject: options.inject ?? []
262
- });
263
- }
264
- static build(optionsProvider) {
265
- return {
266
- module: _DurableWorkerModule,
267
- imports: [
268
- DiscoveryModule
269
- ],
270
- providers: [
271
- optionsProvider,
272
- {
273
- provide: DurableWorkerRuntime,
274
- useFactory: /* @__PURE__ */ __name(() => new DurableWorkerRuntime(), "useFactory")
275
- },
276
- {
277
- provide: RUN_REDIS_WORKER,
278
- useValue: defaultRunRedisWorker
279
- },
280
- {
281
- provide: DURABLE_WORKER_RUNNERS,
282
- useValue: []
283
- },
284
- ThinWorkflowRegistrar,
285
- ThinStepRegistrar,
286
- ThinWorkerBootstrap
287
- ],
288
- exports: [
289
- 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
290
404
  ]
291
- };
292
- }
293
- };
294
- DurableWorkerModule = _ts_decorate([
295
- Module({})
296
- ], 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");
297
450
 
298
451
  // src/durable.module.ts
299
- 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";
300
- import { Module as Module2 } from "@nestjs/common";
301
- 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";
302
455
 
303
456
  // src/durable-step.registrar.ts
304
- import { DURABLE_OPTIONS_CANONICAL, TRANSPORT_CANONICAL } from "@dudousxd/nestjs-durable-core";
305
- 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";
306
459
  import { DiscoveryService as DiscoveryService2, MetadataScanner as MetadataScanner2 } from "@nestjs/core";
307
- 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) {
308
473
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
309
474
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
310
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;
311
476
  return c > 3 && r && Object.defineProperty(target, key, r), r;
312
477
  }
313
- __name(_ts_decorate2, "_ts_decorate");
314
- function _ts_metadata2(k, v) {
478
+ __name(_ts_decorate3, "_ts_decorate");
479
+ function _ts_metadata3(k, v) {
315
480
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
316
481
  }
317
- __name(_ts_metadata2, "_ts_metadata");
482
+ __name(_ts_metadata3, "_ts_metadata");
318
483
  function _ts_param2(paramIndex, decorator) {
319
484
  return function(target, key) {
320
485
  decorator(target, key, paramIndex);
@@ -340,18 +505,18 @@ var DurableStepRegistrar = class {
340
505
  this.options = options;
341
506
  }
342
507
  onModuleInit() {
343
- if (this.options.worker === false) return;
508
+ if (!isDrivingOperator(this.options)) return;
344
509
  if (!supportsHandle(this.transport)) return;
345
510
  const transport = this.transport;
346
511
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => transport.handle(meta.name, handler));
347
512
  }
348
513
  };
349
- DurableStepRegistrar = _ts_decorate2([
350
- Injectable2(),
514
+ DurableStepRegistrar = _ts_decorate3([
515
+ Injectable3(),
351
516
  _ts_param2(2, Inject2(TRANSPORT_CANONICAL)),
352
- _ts_param2(3, Inject2(DURABLE_OPTIONS_CANONICAL)),
353
- _ts_metadata2("design:type", Function),
354
- _ts_metadata2("design:paramtypes", [
517
+ _ts_param2(3, Inject2(DURABLE_OPTIONS_CANONICAL2)),
518
+ _ts_metadata3("design:type", Function),
519
+ _ts_metadata3("design:paramtypes", [
355
520
  typeof DiscoveryService2 === "undefined" ? Object : DiscoveryService2,
356
521
  typeof MetadataScanner2 === "undefined" ? Object : MetadataScanner2,
357
522
  Object,
@@ -361,19 +526,19 @@ DurableStepRegistrar = _ts_decorate2([
361
526
 
362
527
  // src/entity.ts
363
528
  import { WorkflowEngine } from "@dudousxd/nestjs-durable-core";
364
- import { Injectable as Injectable3 } from "@nestjs/common";
529
+ import { Injectable as Injectable4 } from "@nestjs/common";
365
530
  import "reflect-metadata";
366
- function _ts_decorate3(decorators, target, key, desc) {
531
+ function _ts_decorate4(decorators, target, key, desc) {
367
532
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
368
533
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
369
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;
370
535
  return c > 3 && r && Object.defineProperty(target, key, r), r;
371
536
  }
372
- __name(_ts_decorate3, "_ts_decorate");
373
- function _ts_metadata3(k, v) {
537
+ __name(_ts_decorate4, "_ts_decorate");
538
+ function _ts_metadata4(k, v) {
374
539
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
375
540
  }
376
- __name(_ts_metadata3, "_ts_metadata");
541
+ __name(_ts_metadata4, "_ts_metadata");
377
542
  var ENTITY_METADATA = Symbol("nestjs-durable:entity");
378
543
  var ENTITY_ON_METADATA = Symbol("nestjs-durable:entity-on");
379
544
  function Entity(options) {
@@ -430,49 +595,54 @@ var EntityService = class {
430
595
  return this.engine.getEntityState(name, key);
431
596
  }
432
597
  };
433
- EntityService = _ts_decorate3([
434
- Injectable3(),
435
- _ts_metadata3("design:type", Function),
436
- _ts_metadata3("design:paramtypes", [
598
+ EntityService = _ts_decorate4([
599
+ Injectable4(),
600
+ _ts_metadata4("design:type", Function),
601
+ _ts_metadata4("design:paramtypes", [
437
602
  typeof WorkflowEngine === "undefined" ? Object : WorkflowEngine
438
603
  ])
439
604
  ], EntityService);
440
605
 
441
606
  // src/in-app-worker.ts
442
607
  import { DurableWorkerRuntime as DurableWorkerRuntime2, runRedisWorker as defaultRunRedisWorker2 } from "@dudousxd/durable-worker";
443
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL2, RemoteWorkflowExecutor, TRANSPORT_CANONICAL as TRANSPORT_CANONICAL2 } from "@dudousxd/nestjs-durable-core";
444
- 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";
445
610
  import { DiscoveryService as DiscoveryService3, MetadataScanner as MetadataScanner3 } from "@nestjs/core";
446
- function _ts_decorate4(decorators, target, key, desc) {
611
+ function _ts_decorate5(decorators, target, key, desc) {
447
612
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
448
613
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
449
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;
450
615
  return c > 3 && r && Object.defineProperty(target, key, r), r;
451
616
  }
452
- __name(_ts_decorate4, "_ts_decorate");
453
- function _ts_metadata4(k, v) {
617
+ __name(_ts_decorate5, "_ts_decorate");
618
+ function _ts_metadata5(k, v) {
454
619
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
455
620
  }
456
- __name(_ts_metadata4, "_ts_metadata");
621
+ __name(_ts_metadata5, "_ts_metadata");
457
622
  function _ts_param3(paramIndex, decorator) {
458
623
  return function(target, key) {
459
624
  decorator(target, key, paramIndex);
460
625
  };
461
626
  }
462
627
  __name(_ts_param3, "_ts_param");
463
- var IN_APP_WORKER_OPTIONS = Symbol("nestjs-durable:in-app-worker-options");
464
628
  var IN_APP_WORKER_BINDING = Symbol("nestjs-durable:in-app-worker-binding");
465
629
  var IN_APP_WORKER_RUNTIME = Symbol("nestjs-durable:in-app-worker-runtime");
466
630
  var IN_APP_RUN_REDIS_WORKER = Symbol("nestjs-durable:in-app-run-redis-worker");
467
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");
468
636
  function inAppWorkerBinding(transport, options) {
469
- if (!options) return null;
637
+ if (!isCoLocatedWorker(options)) return null;
470
638
  if (!transport?.dispatchWorkflowTask || !transport.onDecision) {
471
- 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.");
472
640
  }
473
641
  return {
474
- group: options.group,
475
- executor: new RemoteWorkflowExecutor(transport, options.group)
642
+ transport,
643
+ ...options.partition !== void 0 ? {
644
+ partition: options.partition
645
+ } : {}
476
646
  };
477
647
  }
478
648
  __name(inAppWorkerBinding, "inAppWorkerBinding");
@@ -496,24 +666,27 @@ var InAppWorkerBootstrap = class {
496
666
  this.runnersSink = runnersSink;
497
667
  }
498
668
  onModuleInit() {
499
- if (!this.options) return;
669
+ if (!isCoLocatedWorker(this.options)) return;
500
670
  scanWorkflows(this.discovery, (meta, instance) => this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)));
501
671
  scanSteps(this.discovery, this.metadataScanner, (meta, handler) => this.runtime.registerStep(meta.name, handler));
502
672
  }
503
673
  async onApplicationBootstrap() {
504
- if (!this.options) return;
674
+ const options = this.options;
675
+ if (!isCoLocatedWorker(options) || options.connection === void 0) return;
505
676
  const handle = await this.runRedisWorker({
506
677
  runtime: this.runtime,
507
- group: this.options.group,
508
- connection: this.options.connection,
509
- ...this.options.prefix !== void 0 ? {
510
- 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
511
684
  } : {},
512
- ...this.options.instanceId !== void 0 ? {
513
- instanceId: this.options.instanceId
685
+ ...options.instanceId !== void 0 ? {
686
+ instanceId: options.instanceId
514
687
  } : {},
515
- ...this.options.concurrency !== void 0 ? {
516
- concurrency: this.options.concurrency
688
+ ...options.concurrency !== void 0 ? {
689
+ concurrency: options.concurrency
517
690
  } : {}
518
691
  });
519
692
  this.runners.push(handle);
@@ -523,17 +696,17 @@ var InAppWorkerBootstrap = class {
523
696
  await Promise.allSettled(this.runners.map((handle) => handle.close()));
524
697
  }
525
698
  };
526
- InAppWorkerBootstrap = _ts_decorate4([
527
- Injectable4(),
528
- _ts_param3(2, Inject3(IN_APP_WORKER_OPTIONS)),
699
+ InAppWorkerBootstrap = _ts_decorate5([
700
+ Injectable5(),
701
+ _ts_param3(2, Inject3(DURABLE_OPTIONS_CANONICAL3)),
529
702
  _ts_param3(3, Inject3(IN_APP_WORKER_RUNTIME)),
530
703
  _ts_param3(4, Inject3(IN_APP_RUN_REDIS_WORKER)),
531
704
  _ts_param3(5, Inject3(IN_APP_WORKER_RUNNERS)),
532
- _ts_metadata4("design:type", Function),
533
- _ts_metadata4("design:paramtypes", [
705
+ _ts_metadata5("design:type", Function),
706
+ _ts_metadata5("design:paramtypes", [
534
707
  typeof DiscoveryService3 === "undefined" ? Object : DiscoveryService3,
535
708
  typeof MetadataScanner3 === "undefined" ? Object : MetadataScanner3,
536
- Object,
709
+ typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
537
710
  typeof DurableWorkerRuntime2 === "undefined" ? Object : DurableWorkerRuntime2,
538
711
  typeof RunRedisWorkerFn === "undefined" ? Object : RunRedisWorkerFn,
539
712
  Array
@@ -543,23 +716,28 @@ function inAppWorkerProviders() {
543
716
  return [
544
717
  {
545
718
  provide: IN_APP_WORKER_BINDING,
546
- useFactory: /* @__PURE__ */ __name((transport, options) => inAppWorkerBinding(transport, options.inAppWorker ?? null), "useFactory"),
719
+ useFactory: /* @__PURE__ */ __name((transport, options) => inAppWorkerBinding(transport, options), "useFactory"),
547
720
  inject: [
548
721
  TRANSPORT_CANONICAL2,
549
- DURABLE_OPTIONS_CANONICAL2
722
+ DURABLE_OPTIONS_CANONICAL3
550
723
  ]
551
724
  },
552
725
  {
553
- provide: IN_APP_WORKER_OPTIONS,
554
- 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"),
555
737
  inject: [
556
- DURABLE_OPTIONS_CANONICAL2
738
+ DURABLE_OPTIONS_CANONICAL3
557
739
  ]
558
740
  },
559
- {
560
- provide: IN_APP_WORKER_RUNTIME,
561
- useFactory: /* @__PURE__ */ __name(() => new DurableWorkerRuntime2(), "useFactory")
562
- },
563
741
  {
564
742
  provide: IN_APP_RUN_REDIS_WORKER,
565
743
  useValue: defaultRunRedisWorker2
@@ -573,20 +751,142 @@ function inAppWorkerProviders() {
573
751
  }
574
752
  __name(inAppWorkerProviders, "inAppWorkerProviders");
575
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
+
576
876
  // src/retention-poller.ts
577
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL3, STATE_STORE_CANONICAL, TERMINAL_RUN_STATUSES, parseDuration } from "@dudousxd/nestjs-durable-core";
578
- import { Inject as Inject4, Injectable as Injectable5 } from "@nestjs/common";
579
- 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) {
580
880
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
581
881
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
582
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;
583
883
  return c > 3 && r && Object.defineProperty(target, key, r), r;
584
884
  }
585
- __name(_ts_decorate5, "_ts_decorate");
586
- function _ts_metadata5(k, v) {
885
+ __name(_ts_decorate7, "_ts_decorate");
886
+ function _ts_metadata7(k, v) {
587
887
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
588
888
  }
589
- __name(_ts_metadata5, "_ts_metadata");
889
+ __name(_ts_metadata7, "_ts_metadata");
590
890
  function _ts_param4(paramIndex, decorator) {
591
891
  return function(target, key) {
592
892
  decorator(target, key, paramIndex);
@@ -630,12 +930,19 @@ var RetentionPoller = class {
630
930
  this.store = store;
631
931
  this.options = options;
632
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
+ }
633
940
  async onApplicationBootstrap() {
634
- if (this.options.worker === false) return;
941
+ if (!isDrivingOperator(this.options)) return;
635
942
  const retention = this.options.retention;
636
943
  if (!retention || retention.policies.length === 0) return;
637
944
  validateRetention(retention);
638
- if (typeof this.store.pruneTerminalRuns !== "function") {
945
+ if (typeof this.requireStore().pruneTerminalRuns !== "function") {
639
946
  console.warn("[nestjs-durable] `retention` is configured but the store adapter does not implement pruneTerminalRuns; retention is disabled.");
640
947
  return;
641
948
  }
@@ -652,7 +959,8 @@ var RetentionPoller = class {
652
959
  async sweep() {
653
960
  if (this.sweeping) return;
654
961
  const retention = this.options.retention;
655
- const prune = this.store.pruneTerminalRuns;
962
+ const store = this.requireStore();
963
+ const prune = store.pruneTerminalRuns;
656
964
  if (!retention || typeof prune !== "function") return;
657
965
  this.sweeping = true;
658
966
  try {
@@ -660,7 +968,7 @@ var RetentionPoller = class {
660
968
  const now = Date.now();
661
969
  for (const policy of retention.policies) {
662
970
  for (let batch = 0; batch < MAX_BATCHES_PER_POLICY; batch++) {
663
- const deleted = await prune.call(this.store, policy, now, batchSize);
971
+ const deleted = await prune.call(store, policy, now, batchSize);
664
972
  if (deleted < batchSize) break;
665
973
  }
666
974
  }
@@ -669,37 +977,244 @@ var RetentionPoller = class {
669
977
  }
670
978
  }
671
979
  };
672
- RetentionPoller = _ts_decorate5([
673
- Injectable5(),
980
+ RetentionPoller = _ts_decorate7([
981
+ Injectable7(),
674
982
  _ts_param4(0, Inject4(STATE_STORE_CANONICAL)),
675
- _ts_param4(1, Inject4(DURABLE_OPTIONS_CANONICAL3)),
676
- _ts_metadata5("design:type", Function),
677
- _ts_metadata5("design:paramtypes", [
678
- 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,
679
987
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
680
988
  ])
681
989
  ], RetentionPoller);
682
990
 
683
- // src/timer-poller.ts
684
- import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL4, WorkflowEngine as WorkflowEngine2, runSchedules } from "@dudousxd/nestjs-durable-core";
685
- import { Inject as Inject5, Injectable as Injectable6 } from "@nestjs/common";
686
- 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) {
687
1086
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
688
1087
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
689
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;
690
1089
  return c > 3 && r && Object.defineProperty(target, key, r), r;
691
1090
  }
692
- __name(_ts_decorate6, "_ts_decorate");
693
- function _ts_metadata6(k, v) {
1091
+ __name(_ts_decorate8, "_ts_decorate");
1092
+ function _ts_metadata8(k, v) {
694
1093
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
695
1094
  }
696
- __name(_ts_metadata6, "_ts_metadata");
1095
+ __name(_ts_metadata8, "_ts_metadata");
697
1096
  function _ts_param5(paramIndex, decorator) {
698
1097
  return function(target, key) {
699
1098
  decorator(target, key, paramIndex);
700
1099
  };
701
1100
  }
702
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");
703
1218
  var TimerPoller = class {
704
1219
  static {
705
1220
  __name(this, "TimerPoller");
@@ -714,7 +1229,7 @@ var TimerPoller = class {
714
1229
  this.options = options;
715
1230
  }
716
1231
  async onApplicationBootstrap() {
717
- if (this.options.worker === false) return;
1232
+ if (!isDrivingOperator(this.options)) return;
718
1233
  this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => void this.engine.runOne(runId));
719
1234
  await this.poll();
720
1235
  const intervalMs = this.options.timerPollMs ?? 1e3;
@@ -744,22 +1259,23 @@ var TimerPoller = class {
744
1259
  }
745
1260
  }
746
1261
  };
747
- TimerPoller = _ts_decorate6([
748
- Injectable6(),
749
- _ts_param5(1, Inject5(DURABLE_OPTIONS_CANONICAL4)),
750
- _ts_metadata6("design:type", Function),
751
- _ts_metadata6("design:paramtypes", [
752
- 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,
753
1268
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions
754
1269
  ])
755
1270
  ], TimerPoller);
756
1271
 
757
1272
  // src/tokens.ts
758
1273
  var CONTEXT_ACCESSOR = Symbol.for("@dudousxd/nestjs-context:accessor");
1274
+ var RUN_GATEWAY = Symbol.for("nestjs-durable:run-gateway");
759
1275
 
760
1276
  // src/workflow.registrar.ts
761
- 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";
762
- 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";
763
1279
  import { DiscoveryService as DiscoveryService4, MetadataScanner as MetadataScanner4 } from "@nestjs/core";
764
1280
 
765
1281
  // src/input-validation.ts
@@ -802,23 +1318,23 @@ function isStepInterceptor(target) {
802
1318
  __name(isStepInterceptor, "isStepInterceptor");
803
1319
 
804
1320
  // src/workflow.registrar.ts
805
- function _ts_decorate7(decorators, target, key, desc) {
1321
+ function _ts_decorate10(decorators, target, key, desc) {
806
1322
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
807
1323
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
808
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;
809
1325
  return c > 3 && r && Object.defineProperty(target, key, r), r;
810
1326
  }
811
- __name(_ts_decorate7, "_ts_decorate");
812
- function _ts_metadata7(k, v) {
1327
+ __name(_ts_decorate10, "_ts_decorate");
1328
+ function _ts_metadata10(k, v) {
813
1329
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
814
1330
  }
815
- __name(_ts_metadata7, "_ts_metadata");
816
- function _ts_param6(paramIndex, decorator) {
1331
+ __name(_ts_metadata10, "_ts_metadata");
1332
+ function _ts_param7(paramIndex, decorator) {
817
1333
  return function(target, key) {
818
1334
  decorator(target, key, paramIndex);
819
1335
  };
820
1336
  }
821
- __name(_ts_param6, "_ts_param");
1337
+ __name(_ts_param7, "_ts_param");
822
1338
  var WorkflowRegistrar = class {
823
1339
  static {
824
1340
  __name(this, "WorkflowRegistrar");
@@ -838,13 +1354,15 @@ var WorkflowRegistrar = class {
838
1354
  this.inAppWorker = inAppWorker;
839
1355
  }
840
1356
  async onApplicationBootstrap() {
841
- if (this.options.worker === false) return;
1357
+ if (!isDrivingOperator(this.options)) return;
842
1358
  await this.engine.recoverIncomplete();
843
1359
  }
844
1360
  /** On deploy/shutdown: stop picking up new runs and wait for in-flight ones to settle, then close
845
1361
  * the transport(s) so the broker workers stop consuming and connections are released. Closing
846
- * 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. */
847
1364
  async onApplicationShutdown() {
1365
+ if (!isOperatorRole(this.options)) return;
848
1366
  await this.engine.drain(this.options.shutdownTimeoutMs);
849
1367
  const transports = [
850
1368
  this.options.transport,
@@ -853,8 +1371,13 @@ var WorkflowRegistrar = class {
853
1371
  await Promise.allSettled(transports.map((t) => t?.close?.()));
854
1372
  }
855
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
+ }
856
1379
  if (this.options.autoSchema !== false) {
857
- await this.store.ensureSchema?.();
1380
+ await store.ensureSchema?.();
858
1381
  }
859
1382
  const deadLetterByWorkflow = /* @__PURE__ */ new Map();
860
1383
  for (const wrapper of this.discovery.getProviders()) {
@@ -888,11 +1411,17 @@ var WorkflowRegistrar = class {
888
1411
  onEvent: getOnEvents(meta, workflowCtor),
889
1412
  eventBatch,
890
1413
  // Uniform dispatch (opt-in): when an in-app worker is configured, register the body GROUP-SERVED
891
- // so the engine dispatches its turns to the app's own group instead of running it inline; the
892
- // 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).
893
1422
  ...this.inAppWorker ? {
894
- group: this.inAppWorker.group,
895
- 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)
896
1425
  } : {}
897
1426
  });
898
1427
  const inline = this.findDeadLetterHandler(workflow);
@@ -904,7 +1433,7 @@ var WorkflowRegistrar = class {
904
1433
  this.engine.register(dlqName, meta.version, inline);
905
1434
  deadLetterByWorkflow.set(meta.name, dlqName);
906
1435
  } else if (meta.deadLetterWorkflow) {
907
- deadLetterByWorkflow.set(meta.name, workflowName(meta.deadLetterWorkflow));
1436
+ deadLetterByWorkflow.set(meta.name, workflowName2(meta.deadLetterWorkflow));
908
1437
  }
909
1438
  });
910
1439
  this.installDeadLetterRouting(deadLetterByWorkflow);
@@ -926,31 +1455,40 @@ var WorkflowRegistrar = class {
926
1455
  * a `dlq:<runId>` id, so re-recovery never double-dispatches.
927
1456
  */
928
1457
  installDeadLetterRouting(byWorkflow) {
929
- const fallback = this.options.deadLetterWorkflow ? workflowName(this.options.deadLetterWorkflow) : void 0;
1458
+ const fallback = this.options.deadLetterWorkflow ? workflowName2(this.options.deadLetterWorkflow) : void 0;
930
1459
  if (byWorkflow.size === 0 && !fallback) return;
931
1460
  this.engine.onDead((run) => {
932
1461
  const target = byWorkflow.get(run.workflow) ?? fallback;
933
1462
  if (!target) return;
934
- void this.engine.start(target, {
935
- deadRunId: run.id,
936
- workflow: run.workflow,
937
- input: run.input,
938
- error: run.error
939
- }, `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);
940
1478
  });
941
1479
  }
942
1480
  };
943
- WorkflowRegistrar = _ts_decorate7([
944
- Injectable7(),
945
- _ts_param6(3, Inject6(STATE_STORE_CANONICAL2)),
946
- _ts_param6(4, Inject6(DURABLE_OPTIONS_CANONICAL5)),
947
- _ts_param6(5, Inject6(IN_APP_WORKER_BINDING)),
948
- _ts_metadata7("design:type", Function),
949
- _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", [
950
1488
  typeof DiscoveryService4 === "undefined" ? Object : DiscoveryService4,
951
1489
  typeof MetadataScanner4 === "undefined" ? Object : MetadataScanner4,
952
- typeof WorkflowEngine3 === "undefined" ? Object : WorkflowEngine3,
953
- typeof StateStore === "undefined" ? Object : StateStore,
1490
+ typeof WorkflowEngine4 === "undefined" ? Object : WorkflowEngine4,
1491
+ Object,
954
1492
  typeof DurableModuleOptions === "undefined" ? Object : DurableModuleOptions,
955
1493
  Object
956
1494
  ])
@@ -958,19 +1496,19 @@ WorkflowRegistrar = _ts_decorate7([
958
1496
 
959
1497
  // src/workflow.service.ts
960
1498
  import { randomUUID } from "node:crypto";
961
- import { WorkflowEngine as WorkflowEngine4 } from "@dudousxd/nestjs-durable-core";
962
- import { Injectable as Injectable8 } from "@nestjs/common";
963
- 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) {
964
1502
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
965
1503
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
966
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;
967
1505
  return c > 3 && r && Object.defineProperty(target, key, r), r;
968
1506
  }
969
- __name(_ts_decorate8, "_ts_decorate");
970
- function _ts_metadata8(k, v) {
1507
+ __name(_ts_decorate11, "_ts_decorate");
1508
+ function _ts_metadata11(k, v) {
971
1509
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
972
1510
  }
973
- __name(_ts_metadata8, "_ts_metadata");
1511
+ __name(_ts_metadata11, "_ts_metadata");
974
1512
  var WorkflowService = class {
975
1513
  static {
976
1514
  __name(this, "WorkflowService");
@@ -1010,22 +1548,32 @@ var WorkflowService = class {
1010
1548
  return this.engine.publishEvent(name, payload, opts);
1011
1549
  }
1012
1550
  };
1013
- WorkflowService = _ts_decorate8([
1014
- Injectable8(),
1015
- _ts_metadata8("design:type", Function),
1016
- _ts_metadata8("design:paramtypes", [
1017
- 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
1018
1556
  ])
1019
1557
  ], WorkflowService);
1020
1558
 
1021
1559
  // src/durable.module.ts
1022
- function _ts_decorate9(decorators, target, key, desc) {
1560
+ function _ts_decorate12(decorators, target, key, desc) {
1023
1561
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1024
1562
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1025
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;
1026
1564
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1027
1565
  }
1028
- __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");
1029
1577
  function resolveAccessor(moduleRef) {
1030
1578
  try {
1031
1579
  return moduleRef.get(CONTEXT_ACCESSOR, {
@@ -1065,20 +1613,103 @@ function isControlPlane(x) {
1065
1613
  return !!x && typeof x.publishControl === "function" && typeof x.onControl === "function";
1066
1614
  }
1067
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);
1068
1694
  var DurableModule = class _DurableModule {
1069
1695
  static {
1070
1696
  __name(this, "DurableModule");
1071
1697
  }
1072
1698
  static forRoot(options) {
1699
+ assertValidRole(options);
1073
1700
  return _DurableModule.build({
1074
- provide: DURABLE_OPTIONS_CANONICAL6,
1701
+ provide: DURABLE_OPTIONS_CANONICAL7,
1075
1702
  useValue: options
1076
1703
  });
1077
1704
  }
1078
1705
  static forRootAsync(options) {
1079
1706
  return _DurableModule.build({
1080
- provide: DURABLE_OPTIONS_CANONICAL6,
1081
- 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"),
1082
1713
  inject: options.inject ?? []
1083
1714
  });
1084
1715
  }
@@ -1087,28 +1718,28 @@ var DurableModule = class _DurableModule {
1087
1718
  module: _DurableModule,
1088
1719
  global: true,
1089
1720
  imports: [
1090
- DiscoveryModule2
1721
+ DiscoveryModule
1091
1722
  ],
1092
1723
  providers: [
1093
1724
  optionsProvider,
1094
1725
  {
1095
- provide: STATE_STORE_CANONICAL3,
1096
- 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"),
1097
1728
  inject: [
1098
- DURABLE_OPTIONS_CANONICAL6
1729
+ DURABLE_OPTIONS_CANONICAL7
1099
1730
  ]
1100
1731
  },
1101
1732
  {
1102
1733
  provide: TRANSPORT_CANONICAL3,
1103
1734
  useFactory: /* @__PURE__ */ __name((options) => options.transport ?? null, "useFactory"),
1104
1735
  inject: [
1105
- DURABLE_OPTIONS_CANONICAL6
1736
+ DURABLE_OPTIONS_CANONICAL7
1106
1737
  ]
1107
1738
  },
1108
1739
  // Legacy back-compat aliases (deprecated tokens still resolve to the same instances):
1109
1740
  {
1110
1741
  provide: STATE_STORE,
1111
- useExisting: STATE_STORE_CANONICAL3
1742
+ useExisting: STATE_STORE_CANONICAL4
1112
1743
  },
1113
1744
  {
1114
1745
  provide: TRANSPORT,
@@ -1116,47 +1747,59 @@ var DurableModule = class _DurableModule {
1116
1747
  },
1117
1748
  {
1118
1749
  provide: DURABLE_OPTIONS,
1119
- useExisting: DURABLE_OPTIONS_CANONICAL6
1750
+ useExisting: DURABLE_OPTIONS_CANONICAL7
1120
1751
  },
1121
1752
  {
1122
- provide: WorkflowEngine5,
1123
- useFactory: /* @__PURE__ */ __name(async (store, transport, opts, moduleRef) => {
1124
- 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;
1125
1765
  const accessor = resolveAccessor(moduleRef);
1126
- const context = opts.context ?? (accessor ? () => carrierFromAccessor(accessor) : void 0);
1766
+ const context = options.context ?? (accessor ? () => carrierFromAccessor(accessor) : void 0);
1127
1767
  const runtime = accessor ? await resolveContextRuntime() : void 0;
1128
1768
  const rehydrate = runtime && ((carrier, fn) => carrier && Object.keys(carrier).length > 0 ? runtime.deserialize(carrier, fn) : fn());
1129
- const engine = new WorkflowEngine5({
1769
+ const engine = new WorkflowEngine6({
1130
1770
  store,
1131
1771
  transport: transport ?? void 0,
1132
- transports: opts.transports,
1133
- controlPlane: opts.controlPlane ?? (isControlPlane(primary) ? primary : void 0),
1134
- leaseMs: opts.leaseMs,
1135
- admission: opts.admission,
1136
- maxRecoveryAttempts: opts.maxRecoveryAttempts,
1137
- remoteAdvanceSilenceMs: opts.remoteAdvanceSilenceMs,
1138
- instanceId: opts.instanceId,
1139
- namespace: opts.namespace,
1140
- webhookUrl: opts.webhookUrl,
1141
- 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,
1142
1782
  context,
1143
1783
  rehydrate: rehydrate || void 0,
1144
- compensationRetries: opts.compensationRetries,
1145
- // A non-worker (API/dashboard) instance must not run workflows: enqueue-only, leaving
1146
- // each `pending` run in the store for a worker's `runPending` poll. Workers use the
1147
- // default in-process dispatcher (execute locally).
1148
- 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 ? {
1149
1792
  dispatch: /* @__PURE__ */ __name(() => {
1150
1793
  }, "dispatch")
1151
1794
  } : void 0
1152
1795
  });
1153
- for (const queue of opts.queues ?? []) engine.registerQueue(queue);
1796
+ for (const queue of options.queues ?? []) engine.registerQueue(queue);
1154
1797
  return engine;
1155
1798
  }, "useFactory"),
1156
1799
  inject: [
1157
- STATE_STORE_CANONICAL3,
1800
+ DURABLE_OPTIONS_CANONICAL7,
1801
+ STATE_STORE_CANONICAL4,
1158
1802
  TRANSPORT_CANONICAL3,
1159
- DURABLE_OPTIONS_CANONICAL6,
1160
1803
  ModuleRef
1161
1804
  ]
1162
1805
  },
@@ -1166,52 +1809,81 @@ var DurableModule = class _DurableModule {
1166
1809
  DurableStepRegistrar,
1167
1810
  TimerPoller,
1168
1811
  RetentionPoller,
1169
- // In-app worker (uniform dispatch, opt-in): inert when `inAppWorker` is unset — the binding
1170
- // resolves to null and the bootstrap no-ops, so a plain DurableModule is unchanged.
1171
- ...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()
1172
1839
  ],
1173
1840
  exports: [
1174
1841
  WorkflowService,
1175
1842
  EntityService,
1176
- WorkflowEngine5,
1843
+ WorkflowEngine6,
1177
1844
  STATE_STORE,
1178
- STATE_STORE_CANONICAL3,
1845
+ STATE_STORE_CANONICAL4,
1179
1846
  TRANSPORT,
1180
1847
  TRANSPORT_CANONICAL3,
1181
- DURABLE_OPTIONS_CANONICAL6
1848
+ DURABLE_OPTIONS_CANONICAL7,
1849
+ RUN_GATEWAY
1182
1850
  ]
1183
1851
  };
1184
1852
  }
1185
1853
  };
1186
- DurableModule = _ts_decorate9([
1187
- Module2({})
1854
+ DurableModule = _ts_decorate12([
1855
+ Module({})
1188
1856
  ], DurableModule);
1189
1857
  export {
1190
1858
  CONTEXT_ACCESSOR,
1191
1859
  DEAD_LETTER_METADATA,
1192
1860
  DURABLE_STEP_METADATA,
1193
- DURABLE_WORKER_OPTIONS,
1194
1861
  DURABLE_WORKER_RUNNERS,
1195
1862
  DeadLetter,
1196
1863
  DurableModule,
1864
+ DurableStartClient,
1197
1865
  DurableStep,
1198
- DurableWorkerModule,
1199
1866
  ENTITY_METADATA,
1200
1867
  ENTITY_ON_METADATA,
1201
1868
  Entity,
1202
1869
  EntityService,
1203
1870
  IN_APP_RUN_REDIS_WORKER,
1204
1871
  IN_APP_WORKER_BINDING,
1205
- IN_APP_WORKER_OPTIONS,
1206
1872
  IN_APP_WORKER_RUNNERS,
1207
1873
  IN_APP_WORKER_RUNTIME,
1208
1874
  InAppWorkerBootstrap,
1209
1875
  ON_EVENT_METADATA,
1210
1876
  On,
1211
1877
  OnEvent,
1878
+ ProxyRunGateway,
1879
+ RUN_GATEWAY,
1212
1880
  RUN_REDIS_WORKER,
1881
+ RunRequestResponder,
1213
1882
  STEP_INTERCEPTOR_METADATA,
1883
+ Step,
1214
1884
  StepInterceptor,
1885
+ StoreRunGateway,
1886
+ TenantEventRepublisher,
1215
1887
  ThinStepRegistrar,
1216
1888
  ThinWorkerBootstrap,
1217
1889
  ThinWorkflowRegistrar,
@@ -1225,6 +1897,10 @@ export {
1225
1897
  getWorkflowMeta,
1226
1898
  inAppWorkerProviders,
1227
1899
  isDeadLetterHandler,
1228
- isStepInterceptor
1900
+ isDrivingOperator,
1901
+ isOperatorRole,
1902
+ isStepInterceptor,
1903
+ thinWorkerProviders,
1904
+ unavailableRunGateway
1229
1905
  };
1230
1906
  //# sourceMappingURL=index.js.map