@nestjs/workflows 0.0.1

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 (265) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +50 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +8 -0
  5. package/dist/core/codecs/aes-gcm-payload.codec.d.ts +42 -0
  6. package/dist/core/codecs/aes-gcm-payload.codec.js +119 -0
  7. package/dist/core/codecs/index.d.ts +2 -0
  8. package/dist/core/codecs/index.js +2 -0
  9. package/dist/core/codecs/payload-codecs.d.ts +59 -0
  10. package/dist/core/codecs/payload-codecs.js +130 -0
  11. package/dist/core/index.d.ts +16 -0
  12. package/dist/core/index.js +21 -0
  13. package/dist/core/interfaces/clock.interface.d.ts +18 -0
  14. package/dist/core/interfaces/clock.interface.js +1 -0
  15. package/dist/core/interfaces/index.d.ts +9 -0
  16. package/dist/core/interfaces/index.js +9 -0
  17. package/dist/core/interfaces/leased-worker-options.interface.d.ts +94 -0
  18. package/dist/core/interfaces/leased-worker-options.interface.js +1 -0
  19. package/dist/core/interfaces/limits.interface.d.ts +92 -0
  20. package/dist/core/interfaces/limits.interface.js +1 -0
  21. package/dist/core/interfaces/payload-codec.interface.d.ts +64 -0
  22. package/dist/core/interfaces/payload-codec.interface.js +1 -0
  23. package/dist/core/interfaces/result-waiter-options.interface.d.ts +52 -0
  24. package/dist/core/interfaces/result-waiter-options.interface.js +1 -0
  25. package/dist/core/interfaces/retry-settings.interface.d.ts +71 -0
  26. package/dist/core/interfaces/retry-settings.interface.js +1 -0
  27. package/dist/core/interfaces/schedule-store.interface.d.ts +143 -0
  28. package/dist/core/interfaces/schedule-store.interface.js +1 -0
  29. package/dist/core/interfaces/schedule.interface.d.ts +106 -0
  30. package/dist/core/interfaces/schedule.interface.js +1 -0
  31. package/dist/core/interfaces/scheduler-options.interface.d.ts +152 -0
  32. package/dist/core/interfaces/scheduler-options.interface.js +1 -0
  33. package/dist/core/limits/index.d.ts +1 -0
  34. package/dist/core/limits/index.js +1 -0
  35. package/dist/core/limits/limits.d.ts +61 -0
  36. package/dist/core/limits/limits.js +130 -0
  37. package/dist/core/results/index.d.ts +1 -0
  38. package/dist/core/results/index.js +1 -0
  39. package/dist/core/results/result-waiter.d.ts +54 -0
  40. package/dist/core/results/result-waiter.js +140 -0
  41. package/dist/core/retries/index.d.ts +1 -0
  42. package/dist/core/retries/index.js +1 -0
  43. package/dist/core/retries/retry.d.ts +60 -0
  44. package/dist/core/retries/retry.js +107 -0
  45. package/dist/core/scheduling/cron.util.d.ts +52 -0
  46. package/dist/core/scheduling/cron.util.js +262 -0
  47. package/dist/core/scheduling/encoded-schedule.store.d.ts +26 -0
  48. package/dist/core/scheduling/encoded-schedule.store.js +54 -0
  49. package/dist/core/scheduling/in-memory-schedule.store.d.ts +20 -0
  50. package/dist/core/scheduling/in-memory-schedule.store.js +90 -0
  51. package/dist/core/scheduling/index.d.ts +3 -0
  52. package/dist/core/scheduling/index.js +3 -0
  53. package/dist/core/scheduling/rrule.util.d.ts +55 -0
  54. package/dist/core/scheduling/rrule.util.js +371 -0
  55. package/dist/core/scheduling/schedule-spec.d.ts +57 -0
  56. package/dist/core/scheduling/schedule-spec.js +233 -0
  57. package/dist/core/scheduling/scheduler.d.ts +134 -0
  58. package/dist/core/scheduling/scheduler.js +450 -0
  59. package/dist/core/scheduling/time-zone.util.d.ts +18 -0
  60. package/dist/core/scheduling/time-zone.util.js +88 -0
  61. package/dist/core/time/clock.d.ts +28 -0
  62. package/dist/core/time/clock.js +35 -0
  63. package/dist/core/time/duration.d.ts +21 -0
  64. package/dist/core/time/duration.js +30 -0
  65. package/dist/core/time/index.d.ts +2 -0
  66. package/dist/core/time/index.js +2 -0
  67. package/dist/core/utils/canonical.util.d.ts +2 -0
  68. package/dist/core/utils/canonical.util.js +4 -0
  69. package/dist/core/utils/maybe.util.d.ts +8 -0
  70. package/dist/core/utils/maybe.util.js +14 -0
  71. package/dist/core/utils/payload-place.util.d.ts +10 -0
  72. package/dist/core/utils/payload-place.util.js +32 -0
  73. package/dist/core/workers/index.d.ts +1 -0
  74. package/dist/core/workers/index.js +1 -0
  75. package/dist/core/workers/leased-worker.d.ts +86 -0
  76. package/dist/core/workers/leased-worker.js +280 -0
  77. package/dist/cqrs/decorators/index.d.ts +2 -0
  78. package/dist/cqrs/decorators/index.js +2 -0
  79. package/dist/cqrs/decorators/signal-on.decorator.d.ts +18 -0
  80. package/dist/cqrs/decorators/signal-on.decorator.js +34 -0
  81. package/dist/cqrs/decorators/start-on.decorator.d.ts +18 -0
  82. package/dist/cqrs/decorators/start-on.decorator.js +49 -0
  83. package/dist/cqrs/index.d.ts +3 -0
  84. package/dist/cqrs/index.js +5 -0
  85. package/dist/cqrs/interfaces/index.d.ts +4 -0
  86. package/dist/cqrs/interfaces/index.js +4 -0
  87. package/dist/cqrs/interfaces/signal-on-options.interface.d.ts +22 -0
  88. package/dist/cqrs/interfaces/signal-on-options.interface.js +1 -0
  89. package/dist/cqrs/interfaces/start-on-options.interface.d.ts +23 -0
  90. package/dist/cqrs/interfaces/start-on-options.interface.js +1 -0
  91. package/dist/cqrs/interfaces/workflow-dispatcher-context.interface.d.ts +10 -0
  92. package/dist/cqrs/interfaces/workflow-dispatcher-context.interface.js +1 -0
  93. package/dist/cqrs/interfaces/workflow-event-route.interface.d.ts +20 -0
  94. package/dist/cqrs/interfaces/workflow-event-route.interface.js +1 -0
  95. package/dist/cqrs/utils/event-routes.util.d.ts +7 -0
  96. package/dist/cqrs/utils/event-routes.util.js +22 -0
  97. package/dist/cqrs/workflow-event.publisher.d.ts +52 -0
  98. package/dist/cqrs/workflow-event.publisher.js +257 -0
  99. package/dist/cqrs/workflow-events.explorer.d.ts +27 -0
  100. package/dist/cqrs/workflow-events.explorer.js +92 -0
  101. package/dist/cqrs/workflows-cqrs.module.d.ts +10 -0
  102. package/dist/cqrs/workflows-cqrs.module.js +27 -0
  103. package/dist/decorators/index.d.ts +1 -0
  104. package/dist/decorators/index.js +1 -0
  105. package/dist/decorators/workflow.decorator.d.ts +7 -0
  106. package/dist/decorators/workflow.decorator.js +52 -0
  107. package/dist/errors/child-workflow-failed.error.d.ts +13 -0
  108. package/dist/errors/child-workflow-failed.error.js +14 -0
  109. package/dist/errors/index.d.ts +13 -0
  110. package/dist/errors/index.js +13 -0
  111. package/dist/errors/non-retryable-step.error.d.ts +4 -0
  112. package/dist/errors/non-retryable-step.error.js +7 -0
  113. package/dist/errors/step-failed.error.d.ts +15 -0
  114. package/dist/errors/step-failed.error.js +16 -0
  115. package/dist/errors/step-timeout.error.d.ts +5 -0
  116. package/dist/errors/step-timeout.error.js +8 -0
  117. package/dist/errors/workflow-definition.error.d.ts +10 -0
  118. package/dist/errors/workflow-definition.error.js +13 -0
  119. package/dist/errors/workflow-failed.error.d.ts +20 -0
  120. package/dist/errors/workflow-failed.error.js +17 -0
  121. package/dist/errors/workflow-id-conflict.error.d.ts +6 -0
  122. package/dist/errors/workflow-id-conflict.error.js +9 -0
  123. package/dist/errors/workflow-interrupt.error.d.ts +32 -0
  124. package/dist/errors/workflow-interrupt.error.js +19 -0
  125. package/dist/errors/workflow-non-determinism.error.d.ts +4 -0
  126. package/dist/errors/workflow-non-determinism.error.js +7 -0
  127. package/dist/errors/workflow-not-found.error.d.ts +10 -0
  128. package/dist/errors/workflow-not-found.error.js +13 -0
  129. package/dist/errors/workflow-result-timeout.error.d.ts +11 -0
  130. package/dist/errors/workflow-result-timeout.error.js +13 -0
  131. package/dist/errors/workflow-state.error.d.ts +11 -0
  132. package/dist/errors/workflow-state.error.js +14 -0
  133. package/dist/errors/workflow.error.d.ts +7 -0
  134. package/dist/errors/workflow.error.js +7 -0
  135. package/dist/events/index.d.ts +3 -0
  136. package/dist/events/index.js +3 -0
  137. package/dist/events/workflow-events.interface.d.ts +145 -0
  138. package/dist/events/workflow-events.interface.js +1 -0
  139. package/dist/events/workflow-events.service.d.ts +17 -0
  140. package/dist/events/workflow-events.service.js +38 -0
  141. package/dist/events/workflows.channels.d.ts +3 -0
  142. package/dist/events/workflows.channels.js +9 -0
  143. package/dist/index.d.ts +20 -0
  144. package/dist/index.js +23 -0
  145. package/dist/interfaces/index.d.ts +13 -0
  146. package/dist/interfaces/index.js +13 -0
  147. package/dist/interfaces/serialized-workflow-error.interface.d.ts +6 -0
  148. package/dist/interfaces/serialized-workflow-error.interface.js +1 -0
  149. package/dist/interfaces/workflow-client.interface.d.ts +156 -0
  150. package/dist/interfaces/workflow-client.interface.js +1 -0
  151. package/dist/interfaces/workflow-clock.interface.d.ts +7 -0
  152. package/dist/interfaces/workflow-clock.interface.js +1 -0
  153. package/dist/interfaces/workflow-context.interface.d.ts +241 -0
  154. package/dist/interfaces/workflow-context.interface.js +1 -0
  155. package/dist/interfaces/workflow-decorator-options.interface.d.ts +101 -0
  156. package/dist/interfaces/workflow-decorator-options.interface.js +1 -0
  157. package/dist/interfaces/workflow-instance.interface.d.ts +83 -0
  158. package/dist/interfaces/workflow-instance.interface.js +1 -0
  159. package/dist/interfaces/workflow-payload-codec.interface.d.ts +32 -0
  160. package/dist/interfaces/workflow-payload-codec.interface.js +1 -0
  161. package/dist/interfaces/workflow-retry-options.interface.d.ts +9 -0
  162. package/dist/interfaces/workflow-retry-options.interface.js +1 -0
  163. package/dist/interfaces/workflow-runner.interface.d.ts +11 -0
  164. package/dist/interfaces/workflow-runner.interface.js +1 -0
  165. package/dist/interfaces/workflow-schedule.interface.d.ts +104 -0
  166. package/dist/interfaces/workflow-schedule.interface.js +1 -0
  167. package/dist/interfaces/workflow-storage.interface.d.ts +4 -0
  168. package/dist/interfaces/workflow-storage.interface.js +1 -0
  169. package/dist/interfaces/workflow-store.interface.d.ts +488 -0
  170. package/dist/interfaces/workflow-store.interface.js +1 -0
  171. package/dist/interfaces/workflows-module-options.interface.d.ts +80 -0
  172. package/dist/interfaces/workflows-module-options.interface.js +1 -0
  173. package/dist/mysql/index.d.ts +5 -0
  174. package/dist/mysql/index.js +8 -0
  175. package/dist/mysql/interfaces/index.d.ts +1 -0
  176. package/dist/mysql/interfaces/index.js +1 -0
  177. package/dist/mysql/interfaces/mysql-workflow-store-options.interface.d.ts +25 -0
  178. package/dist/mysql/interfaces/mysql-workflow-store-options.interface.js +1 -0
  179. package/dist/mysql/key-limits.d.ts +30 -0
  180. package/dist/mysql/key-limits.js +41 -0
  181. package/dist/mysql/migrations/index.d.ts +7 -0
  182. package/dist/mysql/migrations/index.js +16 -0
  183. package/dist/mysql/migrations/initial.migration.d.ts +9 -0
  184. package/dist/mysql/migrations/initial.migration.js +104 -0
  185. package/dist/mysql/mysql-workflow.store.d.ts +197 -0
  186. package/dist/mysql/mysql-workflow.store.js +828 -0
  187. package/dist/postgres/errors/index.d.ts +1 -0
  188. package/dist/postgres/errors/index.js +2 -0
  189. package/dist/postgres/index.d.ts +5 -0
  190. package/dist/postgres/index.js +8 -0
  191. package/dist/postgres/interfaces/index.d.ts +1 -0
  192. package/dist/postgres/interfaces/index.js +1 -0
  193. package/dist/postgres/interfaces/postgres-workflow-store-options.interface.d.ts +22 -0
  194. package/dist/postgres/interfaces/postgres-workflow-store-options.interface.js +1 -0
  195. package/dist/postgres/migrations/index.d.ts +6 -0
  196. package/dist/postgres/migrations/index.js +15 -0
  197. package/dist/postgres/migrations/initial.migration.d.ts +6 -0
  198. package/dist/postgres/migrations/initial.migration.js +106 -0
  199. package/dist/postgres/postgres-workflow.store.d.ts +132 -0
  200. package/dist/postgres/postgres-workflow.store.js +586 -0
  201. package/dist/services/index.d.ts +6 -0
  202. package/dist/services/index.js +6 -0
  203. package/dist/services/workflow-client.service.d.ts +146 -0
  204. package/dist/services/workflow-client.service.js +436 -0
  205. package/dist/services/workflow-execution.service.d.ts +267 -0
  206. package/dist/services/workflow-execution.service.js +1076 -0
  207. package/dist/services/workflow-registry.service.d.ts +56 -0
  208. package/dist/services/workflow-registry.service.js +172 -0
  209. package/dist/services/workflow-scheduler.service.d.ts +54 -0
  210. package/dist/services/workflow-scheduler.service.js +173 -0
  211. package/dist/services/workflow-schedules.service.d.ts +60 -0
  212. package/dist/services/workflow-schedules.service.js +193 -0
  213. package/dist/services/workflow-worker.service.d.ts +92 -0
  214. package/dist/services/workflow-worker.service.js +468 -0
  215. package/dist/signals/index.d.ts +1 -0
  216. package/dist/signals/index.js +1 -0
  217. package/dist/signals/workflow.signal.d.ts +17 -0
  218. package/dist/signals/workflow.signal.js +28 -0
  219. package/dist/sql/rate-windows.d.ts +41 -0
  220. package/dist/sql/rate-windows.js +53 -0
  221. package/dist/sql/workflow-rows.d.ts +16 -0
  222. package/dist/sql/workflow-rows.js +102 -0
  223. package/dist/sql/workflow-schema.error.d.ts +21 -0
  224. package/dist/sql/workflow-schema.error.js +15 -0
  225. package/dist/storage/encoded-workflow.store.d.ts +71 -0
  226. package/dist/storage/encoded-workflow.store.js +201 -0
  227. package/dist/storage/index.d.ts +1 -0
  228. package/dist/storage/index.js +1 -0
  229. package/dist/storage/workflow.storage.d.ts +55 -0
  230. package/dist/storage/workflow.storage.js +159 -0
  231. package/dist/stores/in-memory-workflow.store.d.ts +75 -0
  232. package/dist/stores/in-memory-workflow.store.js +380 -0
  233. package/dist/stores/index.d.ts +1 -0
  234. package/dist/stores/index.js +1 -0
  235. package/dist/testing/assertions.d.ts +10 -0
  236. package/dist/testing/assertions.js +72 -0
  237. package/dist/testing/index.d.ts +38 -0
  238. package/dist/testing/index.js +1089 -0
  239. package/dist/testing/schedule-store.contract.d.ts +33 -0
  240. package/dist/testing/schedule-store.contract.js +161 -0
  241. package/dist/utils/index.d.ts +7 -0
  242. package/dist/utils/index.js +7 -0
  243. package/dist/utils/journal-limits.util.d.ts +6 -0
  244. package/dist/utils/journal-limits.util.js +25 -0
  245. package/dist/utils/new-instance.util.d.ts +26 -0
  246. package/dist/utils/new-instance.util.js +87 -0
  247. package/dist/utils/normalize.util.d.ts +5 -0
  248. package/dist/utils/normalize.util.js +11 -0
  249. package/dist/utils/run-timeout.util.d.ts +3 -0
  250. package/dist/utils/run-timeout.util.js +15 -0
  251. package/dist/utils/schedule-spec.util.d.ts +16 -0
  252. package/dist/utils/schedule-spec.util.js +9 -0
  253. package/dist/utils/schedule-store.util.d.ts +15 -0
  254. package/dist/utils/schedule-store.util.js +44 -0
  255. package/dist/utils/serialize-error.util.d.ts +7 -0
  256. package/dist/utils/serialize-error.util.js +22 -0
  257. package/dist/utils/step-scope.util.d.ts +16 -0
  258. package/dist/utils/step-scope.util.js +38 -0
  259. package/dist/workflows.constants.d.ts +10 -0
  260. package/dist/workflows.constants.js +10 -0
  261. package/dist/workflows.module-definition.d.ts +4 -0
  262. package/dist/workflows.module-definition.js +6 -0
  263. package/dist/workflows.module.d.ts +24 -0
  264. package/dist/workflows.module.js +68 -0
  265. package/package.json +123 -0
@@ -0,0 +1,146 @@
1
+ import { type BeforeApplicationShutdown, type Type } from '@nestjs/common';
2
+ import type { WorkflowInstance, WorkflowJournalEntry } from '../interfaces/workflow-instance.interface.js';
3
+ import type { WorkflowInput, WorkflowOutput } from '../interfaces/workflow-runner.interface.js';
4
+ import type { Journaled } from '../interfaces/workflow-context.interface.js';
5
+ import { type WorkflowSignal } from '../signals/workflow.signal.js';
6
+ import { WorkflowStorage } from '../storage/workflow.storage.js';
7
+ import type { WorkflowInstanceDetails, WorkflowPurgeResult } from '../interfaces/workflow-store.interface.js';
8
+ import { WorkflowRegistry } from './workflow-registry.service.js';
9
+ import { WorkflowSchedules } from './workflow-schedules.service.js';
10
+ import { WorkflowWorker } from './workflow-worker.service.js';
11
+ import type { WorkflowsModuleOptions } from '../interfaces/workflows-module-options.interface.js';
12
+ import type { StartWorkflowOptions, SignalWorkflowOptions, WorkflowStartResult, WorkflowCancelResult, WorkflowListFilter, WorkflowDeleteOptions, WorkflowPurgeOptions, WorkflowResultOptions, WorkflowRetryInstanceOptions, WorkflowSignalSendResult } from '../interfaces/workflow-client.interface.js';
13
+ import { WorkflowEvents } from '../events/workflow-events.service.js';
14
+ /** Starts, signals, inspects and cancels workflow instances. Works with or without a local worker. */
15
+ export declare class WorkflowClient implements BeforeApplicationShutdown {
16
+ private readonly storage;
17
+ private readonly registry;
18
+ private readonly worker;
19
+ private readonly events;
20
+ /** The schedules that start instances: `upsert()`, `get()`, `list()`, `pause()`, `resume()`, `trigger()`... */
21
+ readonly schedules: WorkflowSchedules;
22
+ private readonly clock;
23
+ /** `result()`'s waits: settled by this process's end events, or by reading the store. */
24
+ private readonly results;
25
+ constructor(storage: WorkflowStorage, options: WorkflowsModuleOptions, registry: WorkflowRegistry, worker: WorkflowWorker, events: WorkflowEvents,
26
+ /** The schedules that start instances: `upsert()`, `get()`, `list()`, `pause()`, `resume()`, `trigger()`... */
27
+ schedules: WorkflowSchedules);
28
+ /** Read at each call, never in the constructor: sources register while providers are created. */
29
+ private get store();
30
+ /**
31
+ * Ends the waits of `result()` and `startAndWait()`. Nest closes the HTTP server after this hook, and the server
32
+ * waits for the requests in flight: a route still waiting would hold the shutdown up to its wait's timeout. It runs
33
+ * after `onModuleDestroy`, where the worker drains, so an instance that ends meanwhile answers with its result.
34
+ */
35
+ beforeApplicationShutdown(): Promise<void>;
36
+ /**
37
+ * Creates an instance, or returns the existing one with the same id and
38
+ * input (`created: false`). Throws `WorkflowIdConflictError` for the same id
39
+ * with a different workflow or input. With `transaction`, the instance is
40
+ * created in your transaction and commits or rolls back with it. Inside a
41
+ * workflow step, a start without an `id` gets one derived from the step, so a
42
+ * retried step gets its instance back instead of starting another.
43
+ */
44
+ start<W>(workflow: Type<W> | string, input: WorkflowInput<W>, options?: StartWorkflowOptions): Promise<WorkflowStartResult>;
45
+ /**
46
+ * The instance, with the signals it waits for and, on request, its journal and its children
47
+ * (every instance it started with `ctx.startChild()`, oldest first; `list({ parentId })` pages
48
+ * through them). Its own parent is `parentId`. `null` for an unknown id.
49
+ */
50
+ getStatus(id: string, options: {
51
+ journal: true;
52
+ children?: boolean;
53
+ }): Promise<(WorkflowInstanceDetails & {
54
+ journal: WorkflowJournalEntry[];
55
+ }) | null>;
56
+ getStatus(id: string, options?: {
57
+ journal?: boolean;
58
+ children?: boolean;
59
+ }): Promise<WorkflowInstanceDetails | null>;
60
+ /**
61
+ * Waits for the instance to end and resolves with its output, or rejects with a
62
+ * `WorkflowFailedError` (its `status` is `failed`, `cancelled` or `compensation_failed`, its
63
+ * `cause` the instance's error). Rejects with `WorkflowResultTimeoutError` past `timeout` (the
64
+ * instance keeps running), `WorkflowNotFoundError` for an unknown id or one deleted while
65
+ * waiting, and an `Error` as soon as the application starts shutting down. An instance run by
66
+ * this process's worker is seen the moment it ends, also while the worker drains at shutdown;
67
+ * one run elsewhere, at the next read of the store (every 25ms at first, backing off to every
68
+ * second).
69
+ */
70
+ result<O = unknown>(id: string, options?: WorkflowResultOptions): Promise<Journaled<O>>;
71
+ /** How the stored instance ended, or `null` while it hasn't. */
72
+ private outcomeOf;
73
+ /**
74
+ * `start()`, then `result()`: resolves with the output of the instance (the existing one, for
75
+ * an id that already has one), or rejects as `result()` does. `wait` is `result()`'s options;
76
+ * `options.timeout` stays the instance's run timeout. Not with `{ transaction }`: the instance
77
+ * wouldn't exist before your transaction commits.
78
+ */
79
+ startAndWait<W>(workflow: Type<W> | string, input: WorkflowInput<W>, options?: StartWorkflowOptions, wait?: WorkflowResultOptions): Promise<Journaled<WorkflowOutput<W>>>;
80
+ /** Instances by status, workflow name, version, parent and schedule, oldest first. At most `limit` (default 100). */
81
+ list(filter?: WorkflowListFilter): Promise<WorkflowInstance[]>;
82
+ /**
83
+ * Requests cancellation. A parked instance wakes at once. A running one lets
84
+ * its current step finish and stops at its next `ctx` call: at once when it
85
+ * runs in this process, otherwise once its worker's heartbeat reads the flag
86
+ * (within a third of `leaseDuration`). Then it runs its compensations and
87
+ * ends as `cancelled`. Compensations of steps before a `ctx.commit()` never
88
+ * run. An instance that completes first stays completed, and one that is
89
+ * already compensating is not accepted. Throws `WorkflowNotFoundError` for an
90
+ * unknown id.
91
+ */
92
+ cancel(id: string, reason?: string): Promise<WorkflowCancelResult>;
93
+ /**
94
+ * Stops an instance without running its compensations, for one that can't or mustn't finish
95
+ * on its own: a parked one wakes at once, a running one stops at its next `ctx` call (its
96
+ * current step finishes first), and a compensating one after its current compensation. It
97
+ * ends as `cancelled` with a `WorkflowTerminatedError` whose message is `reason`. Accepted
98
+ * after a `cancel()` too. Throws `WorkflowNotFoundError` for an unknown id.
99
+ */
100
+ terminate(id: string, reason?: string): Promise<WorkflowCancelResult>;
101
+ private stop;
102
+ /**
103
+ * Sends a signal to the instances waiting for it with the same `key`, and
104
+ * wakes the suspended ones. Signals are durable: an instance that reaches
105
+ * `ctx.waitForSignal()` later still gets a signal sent after it started, and
106
+ * each signal is consumed at most once per instance. `woken` counts only the
107
+ * instances parked on a matching wait right now. With `transaction`, the
108
+ * signal and the wake-ups commit or roll back with your transaction. With
109
+ * `id`, or from inside a workflow step, a repeated signal is stored once.
110
+ */
111
+ signal<T>(signal: WorkflowSignal<T> | string, payload: NoInfer<T>, options?: SignalWorkflowOptions): Promise<WorkflowSignalSendResult>;
112
+ /**
113
+ * Retries an instance that needs a person, once its cause is fixed:
114
+ *
115
+ * - `failed`: runs again from its journal. Completed steps return their results, and each
116
+ * step that gave up gets its attempts back. Refused when its compensations ran: the
117
+ * completed steps were undone, and resuming would build on undone work.
118
+ * - `compensation_failed`: runs the compensations that didn't complete again, each with its
119
+ * attempts back, and ends as `failed` or `cancelled` as it would have.
120
+ *
121
+ * Journaled under `$retry:<n>`, and emitted as `workflow-retried`. Throws
122
+ * `WorkflowNotFoundError` for an unknown id, and `WorkflowStateError` for any other status or
123
+ * when another change to the instance races it.
124
+ */
125
+ retry(id: string, options?: WorkflowRetryInstanceOptions): Promise<WorkflowInstance>;
126
+ /**
127
+ * Deletes an instance with its journal, and emits `workflow-deleted`. Only a finished one,
128
+ * unless `force`: then an unfinished one too, without compensating (to remove an instance no
129
+ * worker can run any more, such as one of a version you no longer deploy). A parent waiting
130
+ * for a child deleted that way gets a `ChildWorkflowFailedError` (`cancelled`); a deleted
131
+ * parent's children keep running. Throws
132
+ * `WorkflowNotFoundError` for an unknown id, and `WorkflowStateError` for an unfinished one
133
+ * without `force`.
134
+ */
135
+ delete(id: string, options?: WorkflowDeleteOptions): Promise<void>;
136
+ /**
137
+ * Deletes finished instances older than `olderThan`, with their journals, the signals no
138
+ * unfinished instance can take any more, and the rate-limit windows that ended before it, in
139
+ * batches until none is left. Returns how many of each it deleted. Run it from a scheduled job;
140
+ * concurrent runs are safe, only wasteful.
141
+ */
142
+ purge(options: WorkflowPurgeOptions): Promise<WorkflowPurgeResult>;
143
+ private emit;
144
+ /** The store's method for joining the application's transaction, bound; throws if it has none. */
145
+ private storeMethod;
146
+ }
@@ -0,0 +1,436 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { randomUUID } from 'node:crypto';
14
+ import { Inject, Injectable } from '@nestjs/common';
15
+ import { ResultWaiter } from '../core/results/result-waiter.js';
16
+ import { systemClock } from '../core/time/clock.js';
17
+ import { parseDuration } from '../core/time/duration.js';
18
+ import { runTimeoutMs } from '../utils/run-timeout.util.js';
19
+ import { WorkflowIdConflictError } from '../errors/workflow-id-conflict.error.js';
20
+ import { WorkflowNotFoundError } from '../errors/workflow-not-found.error.js';
21
+ import { WorkflowFailedError } from '../errors/workflow-failed.error.js';
22
+ import { WorkflowResultTimeoutError } from '../errors/workflow-result-timeout.error.js';
23
+ import { normalize } from '../utils/normalize.util.js';
24
+ import { assertSameInstance, newInstance } from '../utils/new-instance.util.js';
25
+ import { signalName } from '../signals/workflow.signal.js';
26
+ import { ENGINE_STORE, WorkflowStorage } from '../storage/workflow.storage.js';
27
+ import { stepSignalId, stepStartId } from '../utils/step-scope.util.js';
28
+ import { WorkflowRegistry } from './workflow-registry.service.js';
29
+ import { WorkflowSchedules } from './workflow-schedules.service.js';
30
+ import { WorkflowWorker } from './workflow-worker.service.js';
31
+ import { WORKFLOWS_MODULE_OPTIONS } from '../workflows.module-definition.js';
32
+ import { CHILD_ENDED_SIGNAL } from '../workflows.constants.js';
33
+ import { WorkflowStateError } from '../errors/workflow-state.error.js';
34
+ import { WorkflowEvents } from '../events/workflow-events.service.js';
35
+ /** Starts, signals, inspects and cancels workflow instances. Works with or without a local worker. */
36
+ let WorkflowClient = class WorkflowClient {
37
+ constructor(storage, options, registry, worker, events,
38
+ /** The schedules that start instances: `upsert()`, `get()`, `list()`, `pause()`, `resume()`, `trigger()`... */
39
+ schedules) {
40
+ this.storage = storage;
41
+ this.registry = registry;
42
+ this.worker = worker;
43
+ this.events = events;
44
+ this.schedules = schedules;
45
+ this.clock = options.clock ?? systemClock;
46
+ this.results = new ResultWaiter({
47
+ read: (id) => this.outcomeOf(id),
48
+ timeoutError: (id, timeoutMs) => new WorkflowResultTimeoutError(id, timeoutMs),
49
+ closedError: (id) => new Error(`The application shut down while waiting for the result of instance "${id}".`),
50
+ });
51
+ // An end event of this process's worker carries what the worker stored: the waits need no read.
52
+ this.events.events$.subscribe((event) => {
53
+ if (isEnded(event)) {
54
+ this.results.settle(event.id, () => outcome(event));
55
+ }
56
+ });
57
+ }
58
+ /** Read at each call, never in the constructor: sources register while providers are created. */
59
+ get store() {
60
+ return this.storage[ENGINE_STORE];
61
+ }
62
+ /**
63
+ * Ends the waits of `result()` and `startAndWait()`. Nest closes the HTTP server after this hook, and the server
64
+ * waits for the requests in flight: a route still waiting would hold the shutdown up to its wait's timeout. It runs
65
+ * after `onModuleDestroy`, where the worker drains, so an instance that ends meanwhile answers with its result.
66
+ */
67
+ async beforeApplicationShutdown() {
68
+ // The routes that stopped waiting answer before it resolves: the server closes only the connections idle by then,
69
+ // and one that answers later stays open until its keep-alive timeout (72 seconds on Fastify), holding the shutdown.
70
+ await this.results.close();
71
+ }
72
+ /**
73
+ * Creates an instance, or returns the existing one with the same id and
74
+ * input (`created: false`). Throws `WorkflowIdConflictError` for the same id
75
+ * with a different workflow or input. With `transaction`, the instance is
76
+ * created in your transaction and commits or rolls back with it. Inside a
77
+ * workflow step, a start without an `id` gets one derived from the step, so a
78
+ * retried step gets its instance back instead of starting another.
79
+ */
80
+ async start(workflow, input, options = {}) {
81
+ const resolved = this.registry.resolve(workflow, options.version);
82
+ const derived = options.id === undefined ? stepStartId(resolved.name) : undefined;
83
+ const id = options.id ?? derived ?? randomUUID();
84
+ const data = newInstance(resolved, id, input, {
85
+ caller: 'start()',
86
+ now: this.clock.now(),
87
+ timeout: options.timeout,
88
+ concurrencyKey: options.concurrencyKey,
89
+ rateLimitKey: options.rateLimitKey,
90
+ priority: options.priority,
91
+ });
92
+ // Nothing is awaited before the store's call: on a driver whose transactions are
93
+ // synchronous, its statements must run before the application's transaction callback returns.
94
+ const { instance, created } = await (options.transaction === undefined
95
+ ? this.store.create(data)
96
+ : this.storeMethod('createInTransaction', 'start')(options.transaction, data));
97
+ if (!created) {
98
+ // A retried step may build its input anew (a timestamp in it, say): with an id derived
99
+ // from the step, the first input wins.
100
+ assertSameInstance(instance, data, { input: derived === undefined, parent: false });
101
+ }
102
+ else {
103
+ this.worker.kick();
104
+ }
105
+ return { id: data.id, workflow: data.workflow, version: instance.version, created, status: instance.status };
106
+ }
107
+ async getStatus(id, options = {}) {
108
+ const details = await this.store.get(id, { journal: options.journal === true });
109
+ if (!details) {
110
+ return null;
111
+ }
112
+ if (!options.journal) {
113
+ delete details.journal;
114
+ }
115
+ if (options.children) {
116
+ details.children = [];
117
+ for (let offset = 0;; offset += 500) {
118
+ const page = await this.store.inner.list({ parentId: id, limit: 500, offset });
119
+ details.children.push(...(await Promise.all(page.map((child) => this.store.readableInstance(child)))));
120
+ if (page.length < 500) {
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ return details;
126
+ }
127
+ /**
128
+ * Waits for the instance to end and resolves with its output, or rejects with a
129
+ * `WorkflowFailedError` (its `status` is `failed`, `cancelled` or `compensation_failed`, its
130
+ * `cause` the instance's error). Rejects with `WorkflowResultTimeoutError` past `timeout` (the
131
+ * instance keeps running), `WorkflowNotFoundError` for an unknown id or one deleted while
132
+ * waiting, and an `Error` as soon as the application starts shutting down. An instance run by
133
+ * this process's worker is seen the moment it ends, also while the worker drains at shutdown;
134
+ * one run elsewhere, at the next read of the store (every 25ms at first, backing off to every
135
+ * second).
136
+ */
137
+ async result(id, options = {}) {
138
+ return (await this.results.wait(id, options));
139
+ }
140
+ /** How the stored instance ended, or `null` while it hasn't. */
141
+ async outcomeOf(id) {
142
+ const instance = await this.store.get(id);
143
+ if (!instance) {
144
+ return { error: new WorkflowNotFoundError(`No workflow instance with id "${id}".`) };
145
+ }
146
+ if (instance.status === 'completed') {
147
+ return { value: instance.output };
148
+ }
149
+ return FINISHED.includes(instance.status) ? { error: failure(instance) } : null;
150
+ }
151
+ /**
152
+ * `start()`, then `result()`: resolves with the output of the instance (the existing one, for
153
+ * an id that already has one), or rejects as `result()` does. `wait` is `result()`'s options;
154
+ * `options.timeout` stays the instance's run timeout. Not with `{ transaction }`: the instance
155
+ * wouldn't exist before your transaction commits.
156
+ */
157
+ async startAndWait(workflow, input, options = {}, wait = {}) {
158
+ if (options.transaction !== undefined) {
159
+ throw new TypeError("startAndWait() can't take { transaction }: the instance only exists once your transaction commits. Call start() in it, and result() after the commit.");
160
+ }
161
+ const { id } = await this.start(workflow, input, options);
162
+ return this.result(id, wait);
163
+ }
164
+ /** Instances by status, workflow name, version, parent and schedule, oldest first. At most `limit` (default 100). */
165
+ async list(filter = {}) {
166
+ const limit = filter.limit ?? 100;
167
+ const offset = filter.offset ?? 0;
168
+ if (!Number.isSafeInteger(limit) || limit < 0 || !Number.isSafeInteger(offset) || offset < 0) {
169
+ throw new TypeError(`list(): limit (${limit}) and offset (${offset}) must be non-negative integers.`);
170
+ }
171
+ const status = filter.status === undefined ? undefined : Array.isArray(filter.status) ? filter.status : [filter.status];
172
+ if (status?.length === 0 || limit === 0) {
173
+ return [];
174
+ }
175
+ // Read as stored, then decoded one by one: one no codec can read any more is listed without its payloads.
176
+ const instances = await this.store.inner.list({
177
+ limit,
178
+ offset,
179
+ ...(status ? { status } : {}),
180
+ ...(filter.workflow !== undefined ? { workflow: filter.workflow } : {}),
181
+ ...(filter.version !== undefined ? { version: filter.version } : {}),
182
+ ...(filter.parentId !== undefined ? { parentId: filter.parentId } : {}),
183
+ ...(filter.scheduleId !== undefined ? { scheduleId: filter.scheduleId } : {}),
184
+ });
185
+ return Promise.all(instances.map((instance) => this.store.readableInstance(instance)));
186
+ }
187
+ /**
188
+ * Requests cancellation. A parked instance wakes at once. A running one lets
189
+ * its current step finish and stops at its next `ctx` call: at once when it
190
+ * runs in this process, otherwise once its worker's heartbeat reads the flag
191
+ * (within a third of `leaseDuration`). Then it runs its compensations and
192
+ * ends as `cancelled`. Compensations of steps before a `ctx.commit()` never
193
+ * run. An instance that completes first stays completed, and one that is
194
+ * already compensating is not accepted. Throws `WorkflowNotFoundError` for an
195
+ * unknown id.
196
+ */
197
+ cancel(id, reason) {
198
+ return this.stop(id, reason, false);
199
+ }
200
+ /**
201
+ * Stops an instance without running its compensations, for one that can't or mustn't finish
202
+ * on its own: a parked one wakes at once, a running one stops at its next `ctx` call (its
203
+ * current step finishes first), and a compensating one after its current compensation. It
204
+ * ends as `cancelled` with a `WorkflowTerminatedError` whose message is `reason`. Accepted
205
+ * after a `cancel()` too. Throws `WorkflowNotFoundError` for an unknown id.
206
+ */
207
+ terminate(id, reason) {
208
+ return this.stop(id, reason, true);
209
+ }
210
+ async stop(id, reason, terminate) {
211
+ const accepted = await this.store.requestCancel(id, { reason: reason ?? null, now: this.clock.now(), terminate });
212
+ // Accepted is accepted: an instance whose payloads no codec can read any more is returned as stored.
213
+ const details = await this.store.get(id).catch(() => this.store.inner.get(id));
214
+ if (!details) {
215
+ throw new WorkflowNotFoundError(`No workflow instance with id "${id}".`);
216
+ }
217
+ if (accepted) {
218
+ this.worker.noticeCancel(id, terminate);
219
+ }
220
+ const { waits: _waits, journal: _journal, ...instance } = details;
221
+ return { ...instance, accepted };
222
+ }
223
+ /**
224
+ * Sends a signal to the instances waiting for it with the same `key`, and
225
+ * wakes the suspended ones. Signals are durable: an instance that reaches
226
+ * `ctx.waitForSignal()` later still gets a signal sent after it started, and
227
+ * each signal is consumed at most once per instance. `woken` counts only the
228
+ * instances parked on a matching wait right now. With `transaction`, the
229
+ * signal and the wake-ups commit or roll back with your transaction. With
230
+ * `id`, or from inside a workflow step, a repeated signal is stored once.
231
+ */
232
+ async signal(signal, payload, options = {}) {
233
+ const name = signalName(signal);
234
+ const key = options.key ?? null;
235
+ if (options.id !== undefined && (typeof options.id !== 'string' || options.id.length === 0)) {
236
+ throw new TypeError(`Invalid signal id ${JSON.stringify(options.id)}. Use a non-empty string, such as the id of the event that causes it.`);
237
+ }
238
+ const dedupeId = options.id ?? stepSignalId(name, key) ?? null;
239
+ const data = { name, key, dedupeId, payload: normalize(payload), now: this.clock.now() };
240
+ // Nothing is awaited before the store's call, as in start().
241
+ const result = await (options.transaction === undefined
242
+ ? this.store.signal(data)
243
+ : this.storeMethod('signalInTransaction', 'signal')(options.transaction, data));
244
+ if (!result.created && result.key !== key) {
245
+ throw new WorkflowIdConflictError(`Signal id "${dedupeId}" of "${name}" was already used with ${describeKey(result.key)}, not ${describeKey(key)}.`);
246
+ }
247
+ if (result.woken > 0) {
248
+ this.worker.kick();
249
+ }
250
+ return { signalId: result.id, woken: result.woken, created: result.created };
251
+ }
252
+ /**
253
+ * Retries an instance that needs a person, once its cause is fixed:
254
+ *
255
+ * - `failed`: runs again from its journal. Completed steps return their results, and each
256
+ * step that gave up gets its attempts back. Refused when its compensations ran: the
257
+ * completed steps were undone, and resuming would build on undone work.
258
+ * - `compensation_failed`: runs the compensations that didn't complete again, each with its
259
+ * attempts back, and ends as `failed` or `cancelled` as it would have.
260
+ *
261
+ * Journaled under `$retry:<n>`, and emitted as `workflow-retried`. Throws
262
+ * `WorkflowNotFoundError` for an unknown id, and `WorkflowStateError` for any other status or
263
+ * when another change to the instance races it.
264
+ */
265
+ async retry(id, options = {}) {
266
+ const details = await this.store.get(id, { journal: true });
267
+ if (!details) {
268
+ throw new WorkflowNotFoundError(`No workflow instance with id "${id}".`);
269
+ }
270
+ const { status, journal = [] } = details;
271
+ const now = this.clock.now();
272
+ const reset = (entry) => ({ ...entry, status: 'pending', attempts: 0, wakeAt: null, updatedAt: now });
273
+ let entries;
274
+ let error;
275
+ if (status === 'failed') {
276
+ const undone = journal.filter((entry) => entry.kind === 'compensation' && entry.status === 'completed').map((entry) => `"${entry.name}"`);
277
+ if (undone.length > 0) {
278
+ throw new WorkflowStateError(`Instance "${id}" failed and its compensations ran (${undone.join(', ')}): its completed steps were undone, so ` +
279
+ 'resuming it would build on undone work. Start a new instance instead.');
280
+ }
281
+ entries = journal.filter((entry) => entry.kind === 'step' && entry.status === 'failed').map(reset);
282
+ error = null;
283
+ }
284
+ else if (status === 'compensation_failed') {
285
+ entries = journal.filter((entry) => entry.kind === 'compensation' && entry.status !== 'completed').map(reset);
286
+ const { compensation: _compensation, ...reason } = details.error ?? { name: 'Error', message: 'Unknown failure.' };
287
+ error = reason;
288
+ }
289
+ else {
290
+ throw new WorkflowStateError(`Instance "${id}" is ${status}: only failed and compensation_failed instances can be retried.` +
291
+ (FINISHED.includes(status) ? '' : ' cancel() stops one that is still running.'));
292
+ }
293
+ let deadline;
294
+ if (options.timeout !== undefined) {
295
+ deadline = options.timeout === false ? null : now + runTimeoutMs(options.timeout, 'retry()');
296
+ }
297
+ else if (status === 'failed' && details.deadline !== null && details.deadline <= now) {
298
+ throw new WorkflowStateError(`Instance "${id}" is past its run timeout, so it would time out again at once. Pass { timeout } with a new one, or false for none.`);
299
+ }
300
+ const retries = journal.filter((entry) => entry.kind === 'retry').length;
301
+ entries.push({ name: `$retry:${retries + 1}`, kind: 'retry', status: 'completed', attempts: 0, data: { from: status, error: details.error ?? null }, updatedAt: now });
302
+ const accepted = await this.store.reopen(id, {
303
+ expect: { status, runs: details.runs },
304
+ status: status === 'failed' ? 'pending' : 'compensating',
305
+ error,
306
+ ...(deadline !== undefined ? { deadline } : {}),
307
+ entries,
308
+ now,
309
+ });
310
+ if (!accepted) {
311
+ throw new WorkflowStateError(`Instance "${id}" changed while it was being retried. Read it again, and retry if it still needs it.`);
312
+ }
313
+ this.emit(details, { type: 'workflow-retried', from: status, error: details.error ?? null });
314
+ this.worker.kick();
315
+ const { waits: _waits, journal: _journal, ...instance } = (await this.store.get(id));
316
+ return instance;
317
+ }
318
+ /**
319
+ * Deletes an instance with its journal, and emits `workflow-deleted`. Only a finished one,
320
+ * unless `force`: then an unfinished one too, without compensating (to remove an instance no
321
+ * worker can run any more, such as one of a version you no longer deploy). A parent waiting
322
+ * for a child deleted that way gets a `ChildWorkflowFailedError` (`cancelled`); a deleted
323
+ * parent's children keep running. Throws
324
+ * `WorkflowNotFoundError` for an unknown id, and `WorkflowStateError` for an unfinished one
325
+ * without `force`.
326
+ */
327
+ async delete(id, options = {}) {
328
+ // Read as stored: deleting needs no payload, so an instance no codec can read any more can go too.
329
+ const details = await this.store.inner.get(id);
330
+ if (!details) {
331
+ throw new WorkflowNotFoundError(`No workflow instance with id "${id}".`);
332
+ }
333
+ const deleted = await this.store.delete(id, options.force ? ALL : FINISHED);
334
+ if (!deleted) {
335
+ const current = await this.store.inner.get(id);
336
+ if (!current) {
337
+ throw new WorkflowNotFoundError(`No workflow instance with id "${id}".`);
338
+ }
339
+ throw new WorkflowStateError(`Instance "${id}" is ${current.status}: delete() removes finished instances. cancel() it first, or pass ` +
340
+ '{ force: true } to delete it without running its compensations.');
341
+ }
342
+ this.emit(details, { type: 'workflow-deleted', status: details.status });
343
+ // A parent waiting for this child would wait forever: it ends for the parent as cancelled.
344
+ if (details.parentId !== null && !FINISHED.includes(details.status)) {
345
+ const ended = { status: 'cancelled', error: { name: 'WorkflowDeletedError', message: `Child instance "${id}" was deleted before it ended.` } };
346
+ await this.store.signal({ name: CHILD_ENDED_SIGNAL, key: id, dedupeId: id, payload: ended, now: this.clock.now() });
347
+ this.worker.kick();
348
+ }
349
+ }
350
+ /**
351
+ * Deletes finished instances older than `olderThan`, with their journals, the signals no
352
+ * unfinished instance can take any more, and the rate-limit windows that ended before it, in
353
+ * batches until none is left. Returns how many of each it deleted. Run it from a scheduled job;
354
+ * concurrent runs are safe, only wasteful.
355
+ */
356
+ async purge(options) {
357
+ const olderThan = parseDuration(options.olderThan);
358
+ const statuses = options.status === undefined ? DEFAULT_PURGE : Array.isArray(options.status) ? options.status : [options.status];
359
+ const unfinished = statuses.filter((status) => !FINISHED.includes(status));
360
+ if (statuses.length === 0 || unfinished.length > 0) {
361
+ throw new TypeError(`purge(): status must list finished statuses (${FINISHED.join(', ')}), not ${unfinished.length ? unfinished.join(', ') : 'none'}. ` +
362
+ 'Cancel an unfinished instance first.');
363
+ }
364
+ const limit = options.batchSize ?? 500;
365
+ if (!Number.isSafeInteger(limit) || limit < 1) {
366
+ throw new TypeError(`purge(): batchSize (${limit}) must be a positive integer.`);
367
+ }
368
+ const before = this.clock.now() - olderThan;
369
+ const total = { instances: 0, signals: 0, rateLimits: 0 };
370
+ for (;;) {
371
+ const batch = await this.store.purge({ statuses, before, limit });
372
+ total.instances += batch.instances;
373
+ total.signals += batch.signals;
374
+ total.rateLimits += batch.rateLimits;
375
+ if (batch.instances < limit && batch.signals < limit && batch.rateLimits < limit) {
376
+ return total;
377
+ }
378
+ }
379
+ }
380
+ emit(instance, body) {
381
+ this.events.emit({ id: instance.id, workflow: instance.workflow, version: instance.version, at: this.clock.now(), ...body });
382
+ }
383
+ /** The store's method for joining the application's transaction, bound; throws if it has none. */
384
+ storeMethod(method, caller) {
385
+ const store = this.store;
386
+ const fn = store[method];
387
+ if (typeof fn !== 'function') {
388
+ throw new TypeError(`${caller}() with { transaction } needs a WorkflowStore on your database that implements ${method}(); ` +
389
+ `${this.storage.source.constructor.name} has none. See https://docs.nestjs.com/reliability/workflows#the-store-contract.`);
390
+ }
391
+ return fn.bind(store);
392
+ }
393
+ };
394
+ WorkflowClient = __decorate([
395
+ Injectable(),
396
+ __param(1, Inject(WORKFLOWS_MODULE_OPTIONS)),
397
+ __metadata("design:paramtypes", [WorkflowStorage, Object, WorkflowRegistry,
398
+ WorkflowWorker,
399
+ WorkflowEvents,
400
+ WorkflowSchedules])
401
+ ], WorkflowClient);
402
+ export { WorkflowClient };
403
+ const FINISHED = ['completed', 'failed', 'cancelled', 'compensation_failed'];
404
+ const FAILURES = {
405
+ 'workflow-failed': 'failed',
406
+ 'workflow-cancelled': 'cancelled',
407
+ 'workflow-compensation-failed': 'compensation_failed',
408
+ };
409
+ function isEnded(event) {
410
+ return event.type === 'workflow-completed' || event.type === 'workflow-deleted' || FAILURES[event.type] !== undefined;
411
+ }
412
+ /** What `result()` settles with on an end event, which carries what the worker stored: no read needed. */
413
+ function outcome(event) {
414
+ if (event.type === 'workflow-completed') {
415
+ return { value: event.output };
416
+ }
417
+ if (event.type === 'workflow-deleted') {
418
+ return { error: new WorkflowNotFoundError(`No workflow instance with id "${event.id}".`) };
419
+ }
420
+ return { error: failure({ id: event.id, workflow: event.workflow, status: FAILURES[event.type], error: event.error }) };
421
+ }
422
+ /** What `result()` rejects with for an instance that ended without completing. */
423
+ function failure(instance) {
424
+ const status = instance.status;
425
+ const cause = instance.error ?? { name: 'Error', message: 'Unknown failure.' };
426
+ return new WorkflowFailedError(`Instance "${instance.id}" of workflow "${instance.workflow}" ${status.replace('_', ' ')}: ${cause.name}: ${cause.message}`, {
427
+ instanceId: instance.id,
428
+ status,
429
+ cause,
430
+ });
431
+ }
432
+ const DEFAULT_PURGE = ['completed', 'failed', 'cancelled'];
433
+ const ALL = ['pending', 'running', 'suspended', 'compensating', ...FINISHED];
434
+ function describeKey(key) {
435
+ return key === null ? 'no key' : `key "${key}"`;
436
+ }