@fluojs/cqrs 1.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.ko.md +29 -9
  2. package/README.md +29 -9
  3. package/dist/buses/command-bus.d.ts +19 -2
  4. package/dist/buses/command-bus.d.ts.map +1 -1
  5. package/dist/buses/command-bus.js +68 -24
  6. package/dist/buses/event-bus.d.ts +16 -8
  7. package/dist/buses/event-bus.d.ts.map +1 -1
  8. package/dist/buses/event-bus.js +84 -74
  9. package/dist/buses/event-handler-discovery.d.ts +12 -0
  10. package/dist/buses/event-handler-discovery.d.ts.map +1 -0
  11. package/dist/buses/event-handler-discovery.js +35 -0
  12. package/dist/buses/publish-drain-tracker.d.ts +38 -0
  13. package/dist/buses/publish-drain-tracker.d.ts.map +1 -0
  14. package/dist/buses/publish-drain-tracker.js +89 -0
  15. package/dist/buses/query-bus.d.ts +19 -2
  16. package/dist/buses/query-bus.d.ts.map +1 -1
  17. package/dist/buses/query-bus.js +68 -24
  18. package/dist/buses/saga-bus.d.ts +21 -9
  19. package/dist/buses/saga-bus.d.ts.map +1 -1
  20. package/dist/buses/saga-bus.js +113 -112
  21. package/dist/buses/saga-continuation.d.ts +14 -0
  22. package/dist/buses/saga-continuation.d.ts.map +1 -0
  23. package/dist/buses/saga-continuation.js +24 -0
  24. package/dist/buses/saga-discovery.d.ts +12 -0
  25. package/dist/buses/saga-discovery.d.ts.map +1 -0
  26. package/dist/buses/saga-discovery.js +39 -0
  27. package/dist/buses/saga-drain.d.ts +9 -0
  28. package/dist/buses/saga-drain.d.ts.map +1 -0
  29. package/dist/buses/saga-drain.js +34 -0
  30. package/dist/buses/saga-topology.d.ts +18 -0
  31. package/dist/buses/saga-topology.d.ts.map +1 -0
  32. package/dist/buses/saga-topology.js +46 -0
  33. package/dist/buses/shutdown-deadline.d.ts +23 -0
  34. package/dist/buses/shutdown-deadline.d.ts.map +1 -0
  35. package/dist/buses/shutdown-deadline.js +31 -0
  36. package/dist/decorators.d.ts.map +1 -1
  37. package/dist/decorators.js +15 -1
  38. package/dist/discovery.d.ts +18 -0
  39. package/dist/discovery.d.ts.map +1 -1
  40. package/dist/discovery.js +95 -12
  41. package/dist/dispatch-context.d.ts +35 -0
  42. package/dist/dispatch-context.d.ts.map +1 -0
  43. package/dist/dispatch-context.js +43 -0
  44. package/dist/errors.d.ts +4 -2
  45. package/dist/errors.d.ts.map +1 -1
  46. package/dist/errors.js +4 -2
  47. package/dist/module.d.ts +4 -1
  48. package/dist/module.d.ts.map +1 -1
  49. package/dist/module.js +24 -4
  50. package/dist/status.d.ts +4 -0
  51. package/dist/status.d.ts.map +1 -1
  52. package/dist/status.js +10 -6
  53. package/dist/test-setup.d.ts +2 -0
  54. package/dist/test-setup.d.ts.map +1 -0
  55. package/dist/test-setup.js +4 -0
  56. package/dist/types.d.ts +3 -1
  57. package/dist/types.d.ts.map +1 -1
  58. package/package.json +7 -7
@@ -1,10 +1,11 @@
1
- import type { OnApplicationBootstrap, OnApplicationShutdown } from '@fluojs/runtime';
1
+ import type { OnApplicationBootstrap, OnApplicationShutdown, RuntimeCleanupRegistration } from '@fluojs/runtime';
2
2
  import { CqrsBusBase } from '../discovery.js';
3
3
  import type { CqrsModuleOptions } from '../module.js';
4
4
  import type { CqrsDispatchContext, IEvent } from '../types.js';
5
- interface SagaDispatchOptions {
6
- readonly allowDuringShutdown?: boolean;
7
- }
5
+ import { type SagaDispatchOptions } from './saga-continuation.js';
6
+ import { CqrsShutdownDeadline } from './shutdown-deadline.js';
7
+ /** Private capability authorizing saga dispatch from an already active publish drain. */
8
+ export declare const CQRS_SAGA_DRAIN_AUTHORIZATION: unique symbol;
8
9
  /**
9
10
  * Runtime saga coordinator that discovers `@Saga()` providers and serializes execution per saga token.
10
11
  *
@@ -13,14 +14,17 @@ interface SagaDispatchOptions {
13
14
  */
14
15
  export declare class CqrsSagaLifecycleService extends CqrsBusBase implements OnApplicationBootstrap, OnApplicationShutdown {
15
16
  private readonly moduleOptions;
17
+ private readonly shutdownDeadline;
16
18
  private descriptorsByEvent;
17
19
  private discoveryPromise;
18
20
  private discovered;
19
21
  private readonly executionChains;
20
22
  private lifecycleState;
21
23
  private readonly pendingDispatches;
24
+ private authorizedPipelineLeaseCount;
22
25
  private shutdownDrainTimeouts;
23
- constructor(runtimeContainer: ConstructorParameters<typeof CqrsBusBase>[0], compiledModules: ConstructorParameters<typeof CqrsBusBase>[1], logger: ConstructorParameters<typeof CqrsBusBase>[2], moduleOptions?: CqrsModuleOptions);
26
+ private unregisterShutdownStartCleanup;
27
+ constructor(runtimeContainer: ConstructorParameters<typeof CqrsBusBase>[0], compiledModules: ConstructorParameters<typeof CqrsBusBase>[1], logger: ConstructorParameters<typeof CqrsBusBase>[2], moduleOptions?: CqrsModuleOptions, registerRuntimeCleanup?: RuntimeCleanupRegistration, shutdownDeadline?: CqrsShutdownDeadline);
24
28
  onApplicationBootstrap(): Promise<void>;
25
29
  onApplicationShutdown(): Promise<void>;
26
30
  /**
@@ -41,18 +45,26 @@ export declare class CqrsSagaLifecycleService extends CqrsBusBase implements OnA
41
45
  * @param event Event instance that may trigger one or more sagas.
42
46
  * @returns A promise that resolves once all matching saga chains for the event complete.
43
47
  */
44
- dispatch<TEvent extends IEvent>(event: TEvent, context?: CqrsDispatchContext, options?: SagaDispatchOptions): Promise<void>;
48
+ dispatch<TEvent extends IEvent>(event: TEvent, context?: CqrsDispatchContext, options?: SagaDispatchOptions<typeof CQRS_SAGA_DRAIN_AUTHORIZATION>): Promise<void>;
49
+ /**
50
+ * Retains the discovered saga graph for one active CQRS publish pipeline.
51
+ *
52
+ * @returns A release function that must run after the authorized pipeline settles.
53
+ */
54
+ acquireAuthorizedPipelineLease(): () => void;
55
+ private runDispatch;
45
56
  private assertAcceptingNewWork;
57
+ private markApplicationShutdownStarted;
46
58
  private matchSagaDescriptors;
47
59
  private dispatchWithOrdering;
60
+ private trackPendingDispatch;
61
+ private clearStoppedSagaGraphIfQuiescent;
48
62
  private drainActiveSagaWork;
49
- private awaitShutdownDrain;
63
+ private reportShutdownDrainTimeout;
50
64
  private resolveShutdownDrainTimeoutMs;
51
- private createDispatchContext;
52
65
  private invokeSaga;
53
66
  private ensureDiscovered;
54
67
  private discoverHandlers;
55
68
  private discoverSagaDescriptors;
56
69
  }
57
- export {};
58
70
  //# sourceMappingURL=saga-bus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"saga-bus.d.ts","sourceRoot":"","sources":["../../src/buses/saga-bus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGrF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,EAAyB,MAAM,aAAa,CAAC;AAMrG,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC;AAsCD;;;;;GAKG;AACH,qBACa,wBAAyB,SAAQ,WAAY,YAAW,sBAAsB,EAAE,qBAAqB;IAa9G,OAAO,CAAC,QAAQ,CAAC,aAAa;IAZhC,OAAO,CAAC,kBAAkB,CAA8C;IACxE,OAAO,CAAC,gBAAgB,CAA4B;IACpD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmC;IACnE,OAAO,CAAC,cAAc,CAAsF;IAC5G,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA4B;IAC9D,OAAO,CAAC,qBAAqB,CAAK;gBAGhC,gBAAgB,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EAC9D,eAAe,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EAC7D,MAAM,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACnC,aAAa,GAAE,iBAAsB;IAKlD,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5C;;;;OAIG;IACH,kBAAkB,IAAI;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;QACxF,eAAe,EAAE,MAAM,CAAC;QACxB,qBAAqB,EAAE,MAAM,CAAC;KAC/B;IAUD;;;;;OAKG;IACG,QAAQ,CAAC,MAAM,SAAS,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAarI,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,oBAAoB;YAYd,oBAAoB;YA0CpB,mBAAmB;YAmBnB,kBAAkB;IAiBhC,OAAO,CAAC,6BAA6B;IAUrC,OAAO,CAAC,qBAAqB;YAaf,UAAU;YAoBV,gBAAgB;YAchB,gBAAgB;IAiB9B,OAAO,CAAC,uBAAuB;CA+ChC"}
1
+ {"version":3,"file":"saga-bus.d.ts","sourceRoot":"","sources":["../../src/buses/saga-bus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAGjH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,EAAyB,MAAM,aAAa,CAAC;AACrG,OAAO,EAA8D,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG9H,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAK9D,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,EAAE,OAAO,MAAmD,CAAC;AAkBvG;;;;;GAKG;AACH,qBAQa,wBAAyB,SAAQ,WAAY,YAAW,sBAAsB,EAAE,qBAAqB;IAe9G,OAAO,CAAC,QAAQ,CAAC,aAAa;IAE9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAhBnC,OAAO,CAAC,kBAAkB,CAA8C;IACxE,OAAO,CAAC,gBAAgB,CAA4B;IACpD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmC;IACnE,OAAO,CAAC,cAAc,CAAsF;IAC5G,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA4B;IAC9D,OAAO,CAAC,4BAA4B,CAAK;IACzC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,8BAA8B,CAA2B;gBAG/D,gBAAgB,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EAC9D,eAAe,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EAC7D,MAAM,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,EACnC,aAAa,GAAE,iBAAsB,EACtD,sBAAsB,GAAE,0BAAkD,EACzD,gBAAgB,GAAE,oBAAiD;IAShF,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5C;;;;OAIG;IACH,kBAAkB,IAAI;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;QACxF,eAAe,EAAE,MAAM,CAAC;QACxB,qBAAqB,EAAE,MAAM,CAAC;KAC/B;IAcD;;;;;OAKG;IACG,QAAQ,CAAC,MAAM,SAAS,MAAM,EAClC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,mBAAmB,EAC7B,OAAO,GAAE,mBAAmB,CAAC,OAAO,6BAA6B,CAAM,GACtE,OAAO,CAAC,IAAI,CAAC;IAKhB;;;;OAIG;IACH,8BAA8B,IAAI,MAAM,IAAI;YAe9B,WAAW;IA2CzB,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,8BAA8B;IAQtC,OAAO,CAAC,oBAAoB;YAYd,oBAAoB;YAwBpB,oBAAoB;IAWlC,OAAO,CAAC,gCAAgC;YAgB1B,mBAAmB;IAUjC,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,6BAA6B;YAUvB,UAAU;YAoBV,gBAAgB;YAchB,gBAAgB;IAiB9B,OAAO,CAAC,uBAAuB;CAGhC"}
@@ -5,15 +5,20 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
5
5
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
6
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
7
  import { FluoError, Inject, InvariantError } from '@fluojs/core';
8
- import { APPLICATION_LOGGER, COMPILED_MODULES, RUNTIME_CONTAINER } from '@fluojs/runtime/internal';
8
+ import { APPLICATION_LOGGER, COMPILED_MODULES, RUNTIME_CLEANUP_REGISTRATION, RUNTIME_CONTAINER } from '@fluojs/runtime/internal';
9
9
  import { CqrsBusBase } from '../discovery.js';
10
- import { SagaExecutionError, SagaTopologyError } from '../errors.js';
10
+ import { SagaExecutionError } from '../errors.js';
11
11
  import { createIsolatedEvent } from '../event-clone.js';
12
- import { getSagaMetadata } from '../metadata.js';
13
12
  import { CQRS_MODULE_OPTIONS } from '../tokens.js';
14
- const MAX_NESTED_SAGA_DEPTH = 32;
13
+ import { drainSagaContinuations, runSerializedSagaContinuationTasks } from './saga-continuation.js';
14
+ import { discoverSagaDescriptors } from './saga-discovery.js';
15
+ import { drainPendingSagaDispatches } from './saga-drain.js';
16
+ import { CqrsShutdownDeadline } from './shutdown-deadline.js';
17
+ import { enterSagaTopology } from './saga-topology.js';
15
18
  const DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS = 5000;
16
- const cqrsDispatchContextStateBrand = Symbol('fluo.cqrs.dispatchContextState');
19
+
20
+ /** Private capability authorizing saga dispatch from an already active publish drain. */
21
+ export const CQRS_SAGA_DRAIN_AUTHORIZATION = Symbol('fluo.cqrs.sagaDrainAuthorization');
17
22
  function isSaga(value) {
18
23
  if (typeof value !== 'object' || value === null) {
19
24
  return false;
@@ -26,12 +31,6 @@ function toErrorMessage(error) {
26
31
  }
27
32
  return String(error);
28
33
  }
29
- function isCqrsDispatchContextState(context) {
30
- if (typeof context !== 'object' || context === null) {
31
- return false;
32
- }
33
- return context[cqrsDispatchContextStateBrand] === true;
34
- }
35
34
 
36
35
  /**
37
36
  * Runtime saga coordinator that discovers `@Saga()` providers and serializes execution per saga token.
@@ -42,7 +41,7 @@ function isCqrsDispatchContextState(context) {
42
41
  let _CqrsSagaLifecycleSer;
43
42
  class CqrsSagaLifecycleService extends CqrsBusBase {
44
43
  static {
45
- [_CqrsSagaLifecycleSer, _initClass] = _applyDecs(this, [Inject(RUNTIME_CONTAINER, COMPILED_MODULES, APPLICATION_LOGGER, CQRS_MODULE_OPTIONS)], [], 0, void 0, CqrsBusBase).c;
44
+ [_CqrsSagaLifecycleSer, _initClass] = _applyDecs(this, [Inject(RUNTIME_CONTAINER, COMPILED_MODULES, APPLICATION_LOGGER, CQRS_MODULE_OPTIONS, RUNTIME_CLEANUP_REGISTRATION, CqrsShutdownDeadline)], [], 0, void 0, CqrsBusBase).c;
46
45
  }
47
46
  descriptorsByEvent = new Map();
48
47
  discoveryPromise;
@@ -50,10 +49,16 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
50
49
  executionChains = new Map();
51
50
  lifecycleState = 'created';
52
51
  pendingDispatches = new Set();
52
+ authorizedPipelineLeaseCount = 0;
53
53
  shutdownDrainTimeouts = 0;
54
- constructor(runtimeContainer, compiledModules, logger, moduleOptions = {}) {
54
+ unregisterShutdownStartCleanup;
55
+ constructor(runtimeContainer, compiledModules, logger, moduleOptions = {}, registerRuntimeCleanup = () => () => undefined, shutdownDeadline = new CqrsShutdownDeadline()) {
55
56
  super(runtimeContainer, compiledModules, logger);
56
57
  this.moduleOptions = moduleOptions;
58
+ this.shutdownDeadline = shutdownDeadline;
59
+ this.unregisterShutdownStartCleanup = registerRuntimeCleanup(() => {
60
+ this.markApplicationShutdownStarted();
61
+ });
57
62
  }
58
63
  async onApplicationBootstrap() {
59
64
  this.lifecycleState = 'discovering';
@@ -66,14 +71,12 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
66
71
  }
67
72
  }
68
73
  async onApplicationShutdown() {
69
- this.lifecycleState = 'stopping';
74
+ this.markApplicationShutdownStarted();
75
+ this.unregisterShutdownStartCleanup?.();
76
+ this.unregisterShutdownStartCleanup = undefined;
70
77
  await this.drainActiveSagaWork();
71
- this.executionChains.clear();
72
- this.handlerInstances.clear();
73
- this.descriptorsByEvent.clear();
74
- this.discovered = false;
75
- this.discoveryPromise = undefined;
76
78
  this.lifecycleState = 'stopped';
79
+ this.clearStoppedSagaGraphIfQuiescent();
77
80
  }
78
81
 
79
82
  /**
@@ -82,11 +85,12 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
82
85
  * @returns Current discovery state, in-flight execution count, lifecycle state, and discovered saga count.
83
86
  */
84
87
  getRuntimeSnapshot() {
88
+ const stopped = this.lifecycleState === 'stopped';
85
89
  return {
86
- discovered: this.discovered,
90
+ discovered: stopped ? false : this.discovered,
87
91
  inFlightSagaExecutions: this.pendingDispatches.size,
88
92
  lifecycleState: this.lifecycleState,
89
- sagasDiscovered: new Set(Array.from(this.descriptorsByEvent.values()).flatMap(descriptors => descriptors.map(d => d.token))).size,
93
+ sagasDiscovered: stopped ? 0 : new Set(Array.from(this.descriptorsByEvent.values()).flatMap(descriptors => descriptors.map(d => d.token))).size,
90
94
  shutdownDrainTimeouts: this.shutdownDrainTimeouts
91
95
  };
92
96
  }
@@ -98,22 +102,66 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
98
102
  * @returns A promise that resolves once all matching saga chains for the event complete.
99
103
  */
100
104
  async dispatch(event, context, options = {}) {
101
- this.assertAcceptingNewWork(options);
105
+ this.assertAcceptingNewWork(options.drainAuthorization);
106
+ return this.trackPendingDispatch(this.runDispatch(event, context, options));
107
+ }
108
+
109
+ /**
110
+ * Retains the discovered saga graph for one active CQRS publish pipeline.
111
+ *
112
+ * @returns A release function that must run after the authorized pipeline settles.
113
+ */
114
+ acquireAuthorizedPipelineLease() {
115
+ this.authorizedPipelineLeaseCount += 1;
116
+ let released = false;
117
+ return () => {
118
+ if (released) {
119
+ return;
120
+ }
121
+ released = true;
122
+ this.authorizedPipelineLeaseCount -= 1;
123
+ this.clearStoppedSagaGraphIfQuiescent();
124
+ };
125
+ }
126
+ async runDispatch(event, context, options) {
102
127
  await this.ensureDiscovered();
103
- const descriptors = this.matchSagaDescriptors(event);
104
- if (descriptors.length === 0) {
128
+ const entries = this.matchSagaDescriptors(event).map(descriptor => ({
129
+ descriptor,
130
+ topology: enterSagaTopology(context, descriptor)
131
+ }));
132
+ if (entries.length === 0) {
133
+ await options.afterSagas?.();
105
134
  return;
106
135
  }
107
- await Promise.all(descriptors.map(descriptor => this.dispatchWithOrdering(descriptor, event, context)));
108
- }
109
- assertAcceptingNewWork(options) {
110
- if (options.allowDuringShutdown) {
136
+ const continuationEntry = entries.find(entry => entry.topology.reentrantToken);
137
+ if (continuationEntry) {
138
+ const continuationTasks = entries.map(entry => {
139
+ const scheduledDispatch = entry.topology.reentrantToken ? this.dispatchWithOrdering(entry.descriptor, event, entry.topology) : undefined;
140
+ return {
141
+ run: () => scheduledDispatch ?? this.dispatchWithOrdering(entry.descriptor, event, entry.topology),
142
+ token: entry.descriptor.token
143
+ };
144
+ });
145
+ continuationEntry.topology.continuationScope.queue.push(async () => {
146
+ await runSerializedSagaContinuationTasks(continuationTasks);
147
+ await options.afterSagas?.();
148
+ });
111
149
  return;
112
150
  }
113
- if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
151
+ await Promise.all(entries.map(entry => this.dispatchWithOrdering(entry.descriptor, event, entry.topology)));
152
+ await options.afterSagas?.();
153
+ }
154
+ assertAcceptingNewWork(drainAuthorization) {
155
+ if ((this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') && drainAuthorization !== CQRS_SAGA_DRAIN_AUTHORIZATION) {
114
156
  throw new InvariantError('CQRS saga bus cannot dispatch after shutdown has started.');
115
157
  }
116
158
  }
159
+ markApplicationShutdownStarted() {
160
+ this.shutdownDeadline.start(this.resolveShutdownDrainTimeoutMs());
161
+ if (this.lifecycleState !== 'stopped') {
162
+ this.lifecycleState = 'stopping';
163
+ }
164
+ }
117
165
  matchSagaDescriptors(event) {
118
166
  const descriptors = [];
119
167
  for (const [eventType, eventDescriptors] of this.descriptorsByEvent.entries()) {
@@ -123,58 +171,52 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
123
171
  }
124
172
  return descriptors;
125
173
  }
126
- async dispatchWithOrdering(descriptor, event, activeContext) {
127
- const activeState = isCqrsDispatchContextState(activeContext) ? activeContext : undefined;
128
- const routeLabel = `${descriptor.targetType.name}(${descriptor.eventType.name})`;
129
- const isActiveRoute = activeState?.activeRoutes.some(route => route.token === descriptor.token && route.eventType === descriptor.eventType);
130
- const isActiveToken = activeState?.activeRoutes.some(route => route.token === descriptor.token) ?? false;
131
- if (isActiveRoute) {
132
- throw new SagaTopologyError(`Saga ${descriptor.targetType.name} re-entered an unsafe cycle while handling ${descriptor.eventType.name}. ` + `Active saga path: ${[...(activeState?.path ?? []), routeLabel].join(' -> ')}.`);
133
- }
134
- if ((activeState?.depth ?? 0) >= MAX_NESTED_SAGA_DEPTH) {
135
- throw new SagaTopologyError(`Saga ${descriptor.targetType.name} exceeded the maximum nested saga depth of ${MAX_NESTED_SAGA_DEPTH} while handling ${descriptor.eventType.name}. ` + 'Keep in-process saga graphs acyclic and externally bounded.');
136
- }
137
- if (isActiveToken) {
138
- await this.invokeSaga(descriptor, event, this.createDispatchContext(activeState, descriptor, routeLabel));
139
- return;
140
- }
174
+ async dispatchWithOrdering(descriptor, event, topology) {
141
175
  const previous = this.executionChains.get(descriptor.token) ?? Promise.resolve();
142
- const current = previous.then(async () => {
143
- await this.invokeSaga(descriptor, event, this.createDispatchContext(activeState, descriptor, routeLabel));
144
- });
145
- this.executionChains.set(descriptor.token, current.catch(() => undefined));
146
- this.pendingDispatches.add(current);
176
+ const current = previous.then(async () => this.invokeSaga(descriptor, event, topology.context));
177
+ const settled = current.catch(() => undefined);
178
+ this.executionChains.set(descriptor.token, settled);
147
179
  try {
148
180
  await current;
181
+ if (topology.ownsContinuationScope) {
182
+ await drainSagaContinuations(topology.continuationScope);
183
+ }
149
184
  } finally {
150
- this.pendingDispatches.delete(current);
185
+ if (this.executionChains.get(descriptor.token) === settled) {
186
+ this.executionChains.delete(descriptor.token);
187
+ }
151
188
  }
152
189
  }
153
- async drainActiveSagaWork() {
154
- const activeWork = [...this.pendingDispatches, ...this.executionChains.values()];
155
- if (activeWork.length === 0) {
190
+ async trackPendingDispatch(dispatch) {
191
+ this.pendingDispatches.add(dispatch);
192
+ try {
193
+ await dispatch;
194
+ } finally {
195
+ this.pendingDispatches.delete(dispatch);
196
+ this.clearStoppedSagaGraphIfQuiescent();
197
+ }
198
+ }
199
+ clearStoppedSagaGraphIfQuiescent() {
200
+ if (this.lifecycleState !== 'stopped' || this.pendingDispatches.size > 0 || this.authorizedPipelineLeaseCount > 0) {
156
201
  return;
157
202
  }
158
- const timeoutMs = this.resolveShutdownDrainTimeoutMs();
159
- const drained = await this.awaitShutdownDrain(activeWork, timeoutMs);
203
+ this.executionChains.clear();
204
+ this.handlerInstances.clear();
205
+ this.descriptorsByEvent.clear();
206
+ this.discovered = false;
207
+ this.discoveryPromise = undefined;
208
+ }
209
+ async drainActiveSagaWork() {
210
+ const timeoutMs = this.shutdownDeadline.remainingTimeoutMs() ?? this.resolveShutdownDrainTimeoutMs();
211
+ const activeWorkCount = this.pendingDispatches.size;
212
+ const drained = await drainPendingSagaDispatches(this.pendingDispatches, timeoutMs);
160
213
  if (!drained) {
161
- this.shutdownDrainTimeouts += 1;
162
- this.logger.warn(`CQRS saga shutdown drain exceeded ${String(timeoutMs)}ms with ${String(activeWork.length)} active saga task(s); continuing shutdown.`, 'CqrsSagaLifecycleService');
214
+ this.reportShutdownDrainTimeout(timeoutMs, activeWorkCount);
163
215
  }
164
216
  }
165
- async awaitShutdownDrain(activeWork, timeoutMs) {
166
- let timeoutId;
167
- const timeout = new Promise(resolve => {
168
- timeoutId = setTimeout(() => resolve(false), timeoutMs);
169
- });
170
- const drain = Promise.allSettled(activeWork).then(() => true);
171
- try {
172
- return await Promise.race([drain, timeout]);
173
- } finally {
174
- if (timeoutId) {
175
- clearTimeout(timeoutId);
176
- }
177
- }
217
+ reportShutdownDrainTimeout(timeoutMs, activeWorkCount) {
218
+ this.shutdownDrainTimeouts += 1;
219
+ this.logger.warn(`CQRS saga shutdown drain exceeded ${String(timeoutMs)}ms with ${String(activeWorkCount)} active saga task(s); continuing shutdown.`, 'CqrsSagaLifecycleService');
178
220
  }
179
221
  resolveShutdownDrainTimeoutMs() {
180
222
  const timeoutMs = this.moduleOptions.shutdown?.drainTimeoutMs;
@@ -183,17 +225,6 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
183
225
  }
184
226
  return Math.floor(timeoutMs);
185
227
  }
186
- createDispatchContext(activeContext, descriptor, routeLabel) {
187
- return {
188
- [cqrsDispatchContextStateBrand]: true,
189
- activeRoutes: [...(activeContext?.activeRoutes ?? []), {
190
- eventType: descriptor.eventType,
191
- token: descriptor.token
192
- }],
193
- depth: (activeContext?.depth ?? 0) + 1,
194
- path: [...(activeContext?.path ?? []), routeLabel]
195
- };
196
- }
197
228
  async invokeSaga(descriptor, event, context) {
198
229
  const instance = await this.resolveHandlerInstance(descriptor.token);
199
230
  if (!isSaga(instance)) {
@@ -234,37 +265,7 @@ class CqrsSagaLifecycleService extends CqrsBusBase {
234
265
  }
235
266
  }
236
267
  discoverSagaDescriptors() {
237
- const descriptorsByEvent = new Map();
238
- const seenByTarget = new WeakMap();
239
- for (const candidate of this.discoveryCandidates()) {
240
- const metadata = getSagaMetadata(candidate.targetType);
241
- if (!metadata) {
242
- continue;
243
- }
244
- if (candidate.scope !== 'singleton') {
245
- this.logger.warn(`${candidate.targetType.name} in module ${candidate.moduleName} declares @Saga() but is registered with ${candidate.scope} scope. Sagas are registered only for singleton providers.`, 'CqrsSagaLifecycleService');
246
- continue;
247
- }
248
- const seenEventTypes = seenByTarget.get(candidate.targetType) ?? new Set();
249
- for (const eventType of metadata.eventTypes) {
250
- if (seenEventTypes.has(eventType)) {
251
- continue;
252
- }
253
- seenEventTypes.add(eventType);
254
- const descriptors = descriptorsByEvent.get(eventType) ?? [];
255
- if (!descriptors.some(descriptor => descriptor.targetType === candidate.targetType)) {
256
- descriptors.push({
257
- eventType,
258
- moduleName: candidate.moduleName,
259
- targetType: candidate.targetType,
260
- token: candidate.token
261
- });
262
- descriptorsByEvent.set(eventType, descriptors);
263
- }
264
- }
265
- seenByTarget.set(candidate.targetType, seenEventTypes);
266
- }
267
- return descriptorsByEvent;
268
+ return discoverSagaDescriptors(this.discoveryCandidates(), this.logger);
268
269
  }
269
270
  static {
270
271
  _initClass();
@@ -0,0 +1,14 @@
1
+ export interface SagaContinuationScope {
2
+ readonly queue: Array<() => Promise<void>>;
3
+ }
4
+ export interface SagaDispatchOptions<TDrainAuthorization extends symbol> {
5
+ readonly afterSagas?: () => Promise<void>;
6
+ readonly drainAuthorization?: TDrainAuthorization;
7
+ }
8
+ export interface SagaContinuationTask<TToken> {
9
+ readonly run: () => Promise<void>;
10
+ readonly token: TToken;
11
+ }
12
+ export declare function runSerializedSagaContinuationTasks<TToken>(tasks: readonly SagaContinuationTask<TToken>[]): Promise<void>;
13
+ export declare function drainSagaContinuations(scope: SagaContinuationScope): Promise<void>;
14
+ //# sourceMappingURL=saga-continuation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-continuation.d.ts","sourceRoot":"","sources":["../../src/buses/saga-continuation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB,CAAC,mBAAmB,SAAS,MAAM;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,mBAAmB,CAAC;CACnD;AAED,MAAM,WAAW,oBAAoB,CAAC,MAAM;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,kCAAkC,CAAC,MAAM,EAC7D,KAAK,EAAE,SAAS,oBAAoB,CAAC,MAAM,CAAC,EAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxF"}
@@ -0,0 +1,24 @@
1
+ export async function runSerializedSagaContinuationTasks(tasks) {
2
+ const completions = [];
3
+ const settledChains = new Map();
4
+ for (const task of tasks) {
5
+ const previous = settledChains.get(task.token) ?? Promise.resolve();
6
+ const current = previous.then(task.run);
7
+ completions.push(current);
8
+ settledChains.set(task.token, current.catch(() => undefined));
9
+ }
10
+ await Promise.all(settledChains.values());
11
+ await Promise.all(completions);
12
+ }
13
+ export async function drainSagaContinuations(scope) {
14
+ try {
15
+ while (scope.queue.length > 0) {
16
+ const continuation = scope.queue.shift();
17
+ if (continuation) {
18
+ await continuation();
19
+ }
20
+ }
21
+ } finally {
22
+ scope.queue.length = 0;
23
+ }
24
+ }
@@ -0,0 +1,12 @@
1
+ import type { ApplicationLogger } from '@fluojs/runtime';
2
+ import type { DiscoveryCandidate } from '../discovery.js';
3
+ import type { CqrsEventType, SagaDescriptor } from '../types.js';
4
+ /**
5
+ * Discovers singleton saga registrations by provider-token identity.
6
+ *
7
+ * @param candidates Provider registrations compiled from the application module graph.
8
+ * @param logger Application logger used for non-singleton discovery warnings.
9
+ * @returns Saga descriptors grouped by event type in discovery order.
10
+ */
11
+ export declare function discoverSagaDescriptors(candidates: readonly DiscoveryCandidate[], logger: ApplicationLogger): Map<CqrsEventType, SagaDescriptor[]>;
12
+ //# sourceMappingURL=saga-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-discovery.d.ts","sourceRoot":"","sources":["../../src/buses/saga-discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,MAAM,EAAE,iBAAiB,GACxB,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,CAAC,CAyCtC"}
@@ -0,0 +1,39 @@
1
+ import { getSagaMetadata } from '../metadata.js';
2
+ /**
3
+ * Discovers singleton saga registrations by provider-token identity.
4
+ *
5
+ * @param candidates Provider registrations compiled from the application module graph.
6
+ * @param logger Application logger used for non-singleton discovery warnings.
7
+ * @returns Saga descriptors grouped by event type in discovery order.
8
+ */
9
+ export function discoverSagaDescriptors(candidates, logger) {
10
+ const descriptorsByEvent = new Map();
11
+ const seenEventTypesByToken = new Map();
12
+ for (const candidate of candidates) {
13
+ const metadata = getSagaMetadata(candidate.targetType);
14
+ if (!metadata) {
15
+ continue;
16
+ }
17
+ if (candidate.scope !== 'singleton') {
18
+ logger.warn(`${candidate.targetType.name} in module ${candidate.moduleName} declares @Saga() but is registered with ${candidate.scope} scope. Sagas are registered only for singleton providers.`, 'CqrsSagaLifecycleService');
19
+ continue;
20
+ }
21
+ const seenEventTypes = seenEventTypesByToken.get(candidate.token) ?? new Set();
22
+ for (const eventType of metadata.eventTypes) {
23
+ if (seenEventTypes.has(eventType)) {
24
+ continue;
25
+ }
26
+ seenEventTypes.add(eventType);
27
+ const descriptors = descriptorsByEvent.get(eventType) ?? [];
28
+ descriptors.push({
29
+ eventType,
30
+ moduleName: candidate.moduleName,
31
+ targetType: candidate.targetType,
32
+ token: candidate.token
33
+ });
34
+ descriptorsByEvent.set(eventType, descriptors);
35
+ }
36
+ seenEventTypesByToken.set(candidate.token, seenEventTypes);
37
+ }
38
+ return descriptorsByEvent;
39
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Waits until a mutable set of saga tasks becomes quiescent within one deadline.
3
+ *
4
+ * @param pendingDispatches Live saga task set owned by the lifecycle service.
5
+ * @param timeoutMs Maximum drain duration in milliseconds.
6
+ * @returns `true` when all current and late-added tasks settle before the deadline.
7
+ */
8
+ export declare function drainPendingSagaDispatches(pendingDispatches: ReadonlySet<Promise<void>>, timeoutMs: number): Promise<boolean>;
9
+ //# sourceMappingURL=saga-drain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-drain.d.ts","sourceRoot":"","sources":["../../src/buses/saga-drain.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,iBAAiB,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAgBlB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Waits until a mutable set of saga tasks becomes quiescent within one deadline.
3
+ *
4
+ * @param pendingDispatches Live saga task set owned by the lifecycle service.
5
+ * @param timeoutMs Maximum drain duration in milliseconds.
6
+ * @returns `true` when all current and late-added tasks settle before the deadline.
7
+ */
8
+ export async function drainPendingSagaDispatches(pendingDispatches, timeoutMs) {
9
+ const deadline = Date.now() + timeoutMs;
10
+ while (pendingDispatches.size > 0) {
11
+ const remainingTimeoutMs = deadline - Date.now();
12
+ if (remainingTimeoutMs <= 0) {
13
+ return false;
14
+ }
15
+ if (!(await awaitSagaTasks([...pendingDispatches], remainingTimeoutMs))) {
16
+ return false;
17
+ }
18
+ }
19
+ return true;
20
+ }
21
+ async function awaitSagaTasks(activeWork, timeoutMs) {
22
+ let timeoutId;
23
+ const timeout = new Promise(resolve => {
24
+ timeoutId = setTimeout(() => resolve(false), timeoutMs);
25
+ });
26
+ const drain = Promise.allSettled(activeWork).then(() => true);
27
+ try {
28
+ return await Promise.race([drain, timeout]);
29
+ } finally {
30
+ if (timeoutId) {
31
+ clearTimeout(timeoutId);
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,18 @@
1
+ import type { CqrsDispatchContext, SagaDescriptor } from '../types.js';
2
+ import type { SagaContinuationScope } from './saga-continuation.js';
3
+ /** Result of entering one guarded saga route. */
4
+ export interface SagaTopologyEntry {
5
+ readonly continuationScope: SagaContinuationScope;
6
+ readonly context: CqrsDispatchContext;
7
+ readonly ownsContinuationScope: boolean;
8
+ readonly reentrantToken: boolean;
9
+ }
10
+ /**
11
+ * Validates and enters one saga route using private immutable context state.
12
+ *
13
+ * @param context Opaque context passed through the active CQRS pipeline.
14
+ * @param descriptor Saga route selected for the current event.
15
+ * @returns The next opaque context.
16
+ */
17
+ export declare function enterSagaTopology(context: CqrsDispatchContext | undefined, descriptor: SagaDescriptor): SagaTopologyEntry;
18
+ //# sourceMappingURL=saga-topology.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saga-topology.d.ts","sourceRoot":"","sources":["../../src/buses/saga-topology.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAIpE,iDAAiD;AACjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAAG,SAAS,EACxC,UAAU,EAAE,cAAc,GACzB,iBAAiB,CAuCnB"}
@@ -0,0 +1,46 @@
1
+ import { createInternalCqrsDispatchContext, getInternalCqrsDispatchContextState } from '../dispatch-context.js';
2
+ import { SagaTopologyError } from '../errors.js';
3
+ const MAX_NESTED_SAGA_DEPTH = 32;
4
+
5
+ /** Result of entering one guarded saga route. */
6
+
7
+ /**
8
+ * Validates and enters one saga route using private immutable context state.
9
+ *
10
+ * @param context Opaque context passed through the active CQRS pipeline.
11
+ * @param descriptor Saga route selected for the current event.
12
+ * @returns The next opaque context.
13
+ */
14
+ export function enterSagaTopology(context, descriptor) {
15
+ const internalState = getInternalCqrsDispatchContextState(context);
16
+ const activeTopology = internalState?.sagaTopology;
17
+ const routeLabel = `${descriptor.targetType.name}(${descriptor.eventType.name})`;
18
+ const reenteredRoute = activeTopology?.activeRoutes.some(route => route.token === descriptor.token && route.eventType === descriptor.eventType);
19
+ const reentrantToken = activeTopology?.activeRoutes.some(route => route.token === descriptor.token) ?? false;
20
+ if (reenteredRoute) {
21
+ throw new SagaTopologyError(`Saga ${descriptor.targetType.name} re-entered an unsafe cycle while handling ${descriptor.eventType.name}. ` + `Active saga path: ${[...(activeTopology?.path ?? []), routeLabel].join(' -> ')}.`);
22
+ }
23
+ if ((activeTopology?.depth ?? 0) >= MAX_NESTED_SAGA_DEPTH) {
24
+ throw new SagaTopologyError(`Saga ${descriptor.targetType.name} exceeded the maximum nested saga depth of ${MAX_NESTED_SAGA_DEPTH} while handling ${descriptor.eventType.name}. ` + 'Keep in-process saga graphs acyclic and externally bounded.');
25
+ }
26
+ const continuationScope = internalState?.sagaContinuationScope ?? {
27
+ queue: []
28
+ };
29
+ return {
30
+ continuationScope,
31
+ context: createInternalCqrsDispatchContext({
32
+ publishDrainToken: internalState?.publishDrainToken,
33
+ sagaContinuationScope: continuationScope,
34
+ sagaTopology: {
35
+ activeRoutes: [...(activeTopology?.activeRoutes ?? []), {
36
+ eventType: descriptor.eventType,
37
+ token: descriptor.token
38
+ }],
39
+ depth: (activeTopology?.depth ?? 0) + 1,
40
+ path: [...(activeTopology?.path ?? []), routeLabel]
41
+ }
42
+ }),
43
+ ownsContinuationScope: internalState?.sagaContinuationScope === undefined,
44
+ reentrantToken
45
+ };
46
+ }
@@ -0,0 +1,23 @@
1
+ /** Coordinates one absolute CQRS shutdown deadline across lifecycle drain hooks. */
2
+ export declare class CqrsShutdownDeadline {
3
+ private deadlineMs;
4
+ /**
5
+ * Starts the shared deadline once.
6
+ *
7
+ * @param timeoutMs Configured CQRS shutdown drain duration.
8
+ */
9
+ start(timeoutMs: number): void;
10
+ /**
11
+ * Returns the remaining duration from the shared deadline.
12
+ *
13
+ * @returns Remaining duration in milliseconds, or `undefined` before shutdown starts.
14
+ */
15
+ remainingTimeoutMs(): number | undefined;
16
+ /**
17
+ * Returns the absolute timestamp for the shared deadline.
18
+ *
19
+ * @returns Absolute deadline in milliseconds, or `undefined` before shutdown starts.
20
+ */
21
+ deadlineAtMs(): number | undefined;
22
+ }
23
+ //# sourceMappingURL=shutdown-deadline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown-deadline.d.ts","sourceRoot":"","sources":["../../src/buses/shutdown-deadline.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,UAAU,CAAqB;IAEvC;;;;OAIG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,GAAG,SAAS;IAIxC;;;;OAIG;IACH,YAAY,IAAI,MAAM,GAAG,SAAS;CAGnC"}