@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,92 @@
1
+ import type { Duration } from '../time/duration.js';
2
+ /**
3
+ * A concurrency limit, in the family's words: at most `limit` run at once across every worker, of the whole (a
4
+ * workflow, a queue), or with `key`, per key. A package takes one, or an array of one without a key and one with,
5
+ * and checks them with `resolveConcurrency()`. A run holds its slot while its lease is live, not while it waits.
6
+ *
7
+ * ```ts
8
+ * const concurrency: ConcurrencyLimit<Order>[] = [{ limit: 20 }, { limit: 1, key: (order) => order.customerId }];
9
+ * ```
10
+ */
11
+ export interface ConcurrencyLimit<I = any> {
12
+ /** A positive integer: how many may run at once, of the whole, or with `key`, per key. */
13
+ limit: number;
14
+ /**
15
+ * Makes `limit` a limit per key: the key of a run, computed from its (JSON) input when it is created, such as
16
+ * `(order) => order.customerId`. `null` or `undefined` leaves it out of every key.
17
+ */
18
+ key?: (input: I) => string | null | undefined;
19
+ }
20
+ /**
21
+ * A rate limit, in the family's words: at most `max` starts per window of `duration`, of the whole, or with `key`,
22
+ * per key. A window opens with the first start after the previous one ended and lasts `duration` (BullMQ's limiter),
23
+ * so up to `2 * max` can start around a window's end. `resolveRateLimit()` checks one, or an array of one without a
24
+ * key and one with.
25
+ *
26
+ * ```ts
27
+ * const rateLimit: RateLimit<Order>[] = [{ max: 100, duration: '1m' }, { max: 5, duration: '1m', key: (order) => order.customerId }];
28
+ * ```
29
+ */
30
+ export interface RateLimit<I = any> {
31
+ /** A positive integer: how many may start per `duration`, of the whole, or with `key`, per key. */
32
+ max: number;
33
+ /** The window, such as `'1m'`. */
34
+ duration: Duration;
35
+ /** Makes the limit a limit per key, as `ConcurrencyLimit.key` does. */
36
+ key?: (input: I) => string | null | undefined;
37
+ }
38
+ /**
39
+ * A rate limit's window, resolved: at most `max` starts per `duration` milliseconds.
40
+ *
41
+ * ```ts
42
+ * const window: RateWindow = { max: 10, duration: 60_000 };
43
+ * ```
44
+ */
45
+ export interface RateWindow {
46
+ max: number;
47
+ duration: number;
48
+ }
49
+ /**
50
+ * Concurrency limits as `resolveConcurrency()` returns them: the whole's limit and the per-key one (`null`: none),
51
+ * and the function that computes a run's key.
52
+ *
53
+ * ```ts
54
+ * const { limit, perKey, key } = resolveConcurrency('queue "emails"', definition.concurrency);
55
+ * ```
56
+ */
57
+ export interface ResolvedConcurrency<I = any> {
58
+ /** At most this many of the whole run at once, or `null`. */
59
+ limit: number | null;
60
+ /** At most this many per key, or `null`. */
61
+ perKey: number | null;
62
+ /** The key of a run, from its input. */
63
+ key?: (input: I) => string | null | undefined;
64
+ }
65
+ /**
66
+ * Rate limits as `resolveRateLimit()` returns them: the whole's window and the per-key one (`null`: none), and the
67
+ * function that computes a run's key.
68
+ *
69
+ * ```ts
70
+ * const { limit, perKey, key } = resolveRateLimit('queue "emails"', definition.rateLimit);
71
+ * ```
72
+ */
73
+ export interface ResolvedRateLimit<I = any> {
74
+ /** The window of the whole, or `null`. */
75
+ limit: RateWindow | null;
76
+ /** Each key's window, or `null`. */
77
+ perKey: RateWindow | null;
78
+ /** The key of a run, from its input. */
79
+ key?: (input: I) => string | null | undefined;
80
+ }
81
+ /**
82
+ * A rate-limit window as a store keeps it: when it ends (epoch milliseconds), and how many starts it holds. A window
83
+ * that ended is the same as none.
84
+ *
85
+ * ```ts
86
+ * const window: RateWindowState = { windowEnd: now + 60_000, count: 1 };
87
+ * ```
88
+ */
89
+ export interface RateWindowState {
90
+ windowEnd: number;
91
+ count: number;
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Where a payload is stored, which a codec receives with it, the same to encode and to decode: `field` names what
3
+ * holds it, and the other keys say where that is (ids). A codec that authenticates the payload (`AesGcmPayloadCodec`
4
+ * does) binds it to its context, and refuses it once it was moved to another place.
5
+ *
6
+ * ```ts
7
+ * const context: PayloadContext = { field: 'data', queue: 'emails', job: 'order-confirmation-42' };
8
+ * ```
9
+ */
10
+ export interface PayloadContext {
11
+ /** The field that holds the payload, such as `'input'` or `'data'`. */
12
+ readonly field: string;
13
+ /** Where that field is: the ids of what holds it, such as `instanceId` and `entry`, or `queue` and `job`. */
14
+ readonly [key: string]: string | undefined;
15
+ }
16
+ /**
17
+ * Encodes what a package stores for the application before its store sees it, to encrypt or compress it, and decodes
18
+ * it on the way back. `PayloadCodecs` wraps what it returns in the family's envelope, with its `id`, so a codec that
19
+ * was replaced keeps decoding what it encoded. `@nestjs/workflows` takes one as `WorkflowPayloadCodec`.
20
+ *
21
+ * ```ts
22
+ * class Base64Codec implements PayloadCodec {
23
+ * readonly id = 'base64';
24
+ *
25
+ * encode(value: unknown): string {
26
+ * return Buffer.from(JSON.stringify(value)).toString('base64');
27
+ * }
28
+ *
29
+ * decode(data: string): unknown {
30
+ * return JSON.parse(Buffer.from(data, 'base64').toString());
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ export interface PayloadCodec<C extends PayloadContext = PayloadContext> {
36
+ /**
37
+ * Stored with every payload the codec encodes, to pick the codec that decodes it: letters, digits, `.`, `_` and
38
+ * `-`, such as `'aes-256-gcm'` (`plain` is the envelope's own). Keep it when you change the codec's options; change
39
+ * it with a codec that can't read what this one wrote.
40
+ */
41
+ readonly id: string;
42
+ /**
43
+ * `value` as a string (it is JSON-safe: what `JSON.parse(JSON.stringify(...))` gives). Synchronous where it can be:
44
+ * an asynchronous codec delays a write in the application's transaction past its first await, which a driver whose
45
+ * transactions run synchronously can't take.
46
+ */
47
+ encode(value: unknown, context: C): string | Promise<string>;
48
+ /** The value `encode()` got (or a promise of it), from what it returned. Throw if `data` isn't what it encoded for `context`. */
49
+ decode(data: string, context: C): unknown;
50
+ }
51
+ /**
52
+ * An error as the family stores it: JSON-safe, its class's `name` kept apart from what may hold the application's
53
+ * data (`message`, `stack`), which `PayloadCodecs.encodeError()` encodes. A nested error (workflows' `compensation`)
54
+ * is another `SerializedError` in a field of its own.
55
+ *
56
+ * ```ts
57
+ * const failure: SerializedError = { name: 'CardDeclinedError', message: 'Card 4242 was declined.' };
58
+ * ```
59
+ */
60
+ export interface SerializedError {
61
+ name: string;
62
+ message: string;
63
+ stack?: string;
64
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import type { Duration } from '../time/duration.js';
2
+ /**
3
+ * How something ended, for `ResultWaiter`: its value, or the error its waits reject with (a failure, or "not
4
+ * found").
5
+ *
6
+ * ```ts
7
+ * const outcome: ResultOutcome<Report> = row.state === 'completed' ? { value: row.result } : { error: new JobFailedError(row) };
8
+ * ```
9
+ */
10
+ export type ResultOutcome<T> = {
11
+ value: T;
12
+ } | {
13
+ error: unknown;
14
+ };
15
+ /**
16
+ * What `ResultWaiter` takes: how to read an outcome from the store, and the errors its waits reject with.
17
+ *
18
+ * ```ts
19
+ * const results = new ResultWaiter<unknown>({
20
+ * read: async (id) => {
21
+ * const job = await store.get(id);
22
+ * return !job ? { error: new JobNotFoundError(id) } : job.state === 'completed' ? { value: job.result } : null;
23
+ * },
24
+ * timeoutError: (id, timeoutMs) => new JobResultTimeoutError(id, timeoutMs),
25
+ * closedError: (id) => new Error(`The application shut down while waiting for the result of job "${id}".`),
26
+ * });
27
+ * ```
28
+ */
29
+ export interface ResultWaiterOptions<T> {
30
+ /**
31
+ * Reads the outcome from the store: `null` while it hasn't ended. Called when a wait starts, then after a backoff
32
+ * (25 ms at first, doubling up to a second), until the wait ends; a rejection rejects the wait.
33
+ */
34
+ read(id: string): Promise<ResultOutcome<T> | null>;
35
+ /** The error a wait rejects with past its `timeout`. Default: an `Error` that names `id`. */
36
+ timeoutError?(id: string, timeoutMs: number): Error;
37
+ /** The error a wait rejects with once the waiter closed (the application shuts down). Default: an `Error` that names `id`. */
38
+ closedError?(id: string): Error;
39
+ }
40
+ /**
41
+ * What `ResultWaiter.wait()` takes.
42
+ *
43
+ * ```ts
44
+ * await results.wait(job.id, { timeout: '30s', signal: request.signal });
45
+ * ```
46
+ */
47
+ export interface ResultWaitOptions {
48
+ /** Stop waiting after this long, in wall-clock time (not a `Clock`'s). Default: no limit. */
49
+ timeout?: Duration;
50
+ /** Stop waiting when it aborts: the wait rejects with its reason. */
51
+ signal?: AbortSignal;
52
+ }
@@ -0,0 +1,71 @@
1
+ import type { Duration } from '../time/duration.js';
2
+ /**
3
+ * The wait between attempts, in the family's retry vocabulary: `delay`, growing by `factor` per retry up to
4
+ * `maxDelay`, optionally randomized (`jitter`). Named so it doesn't clash with `@nestjs/resilience`'s
5
+ * `BackoffOptions`, which owns the unprefixed name.
6
+ *
7
+ * ```ts
8
+ * const backoff: BackoffSettings = { delay: '1s', factor: 2, maxDelay: '5m', jitter: 'full' };
9
+ * ```
10
+ */
11
+ export interface BackoffSettings {
12
+ /** Wait before the first retry. Default `'1s'`. */
13
+ delay?: Duration;
14
+ /** Growth per retry; 1 = constant. Default 2. */
15
+ factor?: number;
16
+ /** Cap for a single wait. Default `'5m'`. */
17
+ maxDelay?: Duration;
18
+ /** Randomize each wait: `'full'` (0 to the wait), `'equal'` (half to all of it) or `'none'`. Default `'none'`. */
19
+ jitter?: 'full' | 'equal' | 'none';
20
+ }
21
+ /**
22
+ * How an operation retries, in the family's retry vocabulary: a package takes `retry?: number | false |
23
+ * RetrySettings` (`5` means `{ attempts: 5 }`, `false` a single attempt) and exports it under its own name
24
+ * (`WorkflowRetryOptions`); `resolveRetry()` checks it. `attempt` arguments are 1-based: the attempt that just failed.
25
+ *
26
+ * ```ts
27
+ * const retry: RetrySettings = {
28
+ * attempts: 5,
29
+ * backoff: { delay: '2s', jitter: 'equal' },
30
+ * retryIf: (error) => !(error instanceof ValidationError),
31
+ * };
32
+ * ```
33
+ */
34
+ export interface RetrySettings {
35
+ /** Total attempts, including the first. Default 3. */
36
+ attempts?: number;
37
+ /**
38
+ * Wait between attempts. An object is merged field by field over the default; a function receives the attempt
39
+ * that just failed (1-based) and its error, and returns the wait.
40
+ */
41
+ backoff?: BackoffSettings | ((attempt: number, error: unknown) => Duration);
42
+ /** Return false to stop retrying. Gets the error and the attempt that just failed (1-based). */
43
+ retryIf?: (error: unknown, attempt: number) => boolean;
44
+ }
45
+ /**
46
+ * `BackoffSettings`, checked, with every field set: times in milliseconds.
47
+ *
48
+ * ```ts
49
+ * const backoff: ResolvedBackoff = { delay: 1_000, factor: 2, maxDelay: 300_000, jitter: 'none' };
50
+ * backoffDelay(backoff, 3); // 4_000
51
+ * ```
52
+ */
53
+ export interface ResolvedBackoff {
54
+ delay: number;
55
+ factor: number;
56
+ maxDelay: number;
57
+ jitter: 'full' | 'equal' | 'none';
58
+ }
59
+ /**
60
+ * A retry policy as `resolveRetry()` returns it: checked, with every field set. A backoff function's `Duration` is
61
+ * converted to milliseconds.
62
+ *
63
+ * ```ts
64
+ * const policy: ResolvedRetry = resolveRetry({ attempts: 5 });
65
+ * ```
66
+ */
67
+ export interface ResolvedRetry {
68
+ attempts: number;
69
+ backoff: ResolvedBackoff | ((attempt: number, error: unknown) => number);
70
+ retryIf?: (error: unknown, attempt: number) => boolean;
71
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Where schedules live, for the core's `Scheduler`: any store can implement it (SQL, a Redis store as scripts), and
3
+ * `scheduleStoreContract()` from `@nestjs/workflows/testing` checks one, races included. `InMemoryScheduleStore` is
4
+ * the in-memory one. A schedule's `spec`, `payload` and `state` are the scheduler's JSON: keep them as they are.
5
+ *
6
+ * Three methods need more than a plain read or write: `claimSchedules` (a lock that skips rows other claims hold),
7
+ * `writeSchedule` (fenced by the lease token) and `saveSchedule` (conditional on a revision). Everything else is safe
8
+ * to implement naively.
9
+ *
10
+ * ```ts
11
+ * export class RedisScheduleStore implements ScheduleStore {
12
+ * async claimSchedules(request: ScheduleClaimRequest): Promise<ScheduleRecord[]> {
13
+ * // One script: due ids by wakeAt (ZRANGEBYSCORE), skipping leased ones, leased to request.token.
14
+ * }
15
+ * // ...
16
+ * }
17
+ * ```
18
+ */
19
+ export interface ScheduleStore {
20
+ /**
21
+ * Stores a schedule, as one conditional write, and returns it as stored, or `null` when the condition fails. With
22
+ * `expectRevision: null`, inserts it unless a schedule with the id exists (revision 1, `createdAt = now`); with a
23
+ * number, replaces every field of the one whose `revision` is that number (revision + 1). Either way sets
24
+ * `updatedAt = now`; with `releaseLease`, clears its lease, so the worker that holds it can't `writeSchedule()` any
25
+ * more. Of two concurrent saves expecting the same revision, one lands.
26
+ */
27
+ saveSchedule(schedule: ScheduleSave): Promise<ScheduleRecord | null>;
28
+ /** The schedule, or `null` for an unknown id. */
29
+ getSchedule(id: string): Promise<ScheduleRecord | null>;
30
+ /** Schedules matching the filter, ordered by `id`; a page of them. */
31
+ listSchedules(query: ScheduleQuery): Promise<ScheduleRecord[]>;
32
+ /** Deletes the schedule (if its `revision` is still `revision`, when given), and returns whether it did. */
33
+ deleteSchedule(id: string, revision?: number): Promise<boolean>;
34
+ /**
35
+ * Leases up to `limit` due schedules to a worker, most overdue first (`wakeAt`, then `id`). Due: not paused,
36
+ * `wakeAt <= now`, no lease or an expired one (`leaseUntil < now`), and `target` in `targets`. Each gets
37
+ * `leaseToken = token`, `leaseOwner = owner` and `leaseUntil`; nothing else changes (not `revision`). Two
38
+ * concurrent claims never return the same schedule: lock the candidates and skip those another claim holds.
39
+ */
40
+ claimSchedules(request: ScheduleClaimRequest): Promise<ScheduleRecord[]>;
41
+ /**
42
+ * The lease holder's write: sets `state` and `wakeAt`, `revision + 1` and `updatedAt = now`, and with `release`
43
+ * clears the lease (`leaseToken`, `leaseUntil`), only while `token` is the schedule's lease token; otherwise writes
44
+ * nothing and returns `false`. One conditional update.
45
+ */
46
+ writeSchedule(id: string, token: string, write: ScheduleWrite): Promise<boolean>;
47
+ }
48
+ /**
49
+ * A schedule as stored: `ScheduleStore.getSchedule()`, `listSchedules()` and `claimSchedules()` return it.
50
+ *
51
+ * ```ts
52
+ * const due = await store.claimSchedules({ owner, token, now, leaseUntil, limit: 100, targets: ['emails'] });
53
+ * ```
54
+ */
55
+ export interface ScheduleRecord {
56
+ id: string;
57
+ /** What it starts runs of, such as a workflow's or a queue's name (claims filter on it). */
58
+ target: string;
59
+ /** Declared in the code (kept in step with it), rather than saved at runtime. */
60
+ declared: boolean;
61
+ /** The scheduler's JSON: when it runs and how. Store it as it is. */
62
+ spec: unknown;
63
+ /** What the runs it starts get (JSON: a string with a codec), or `null`. */
64
+ payload: unknown;
65
+ paused: boolean;
66
+ /** When a worker next has something to do for it (`claimSchedules()` looks for `wakeAt <= now`), or `null`. */
67
+ wakeAt: number | null;
68
+ /** The scheduler's JSON bookkeeping. Store it as it is. */
69
+ state: unknown;
70
+ /** Bumped by every `saveSchedule()` and `writeSchedule()`. */
71
+ revision: number;
72
+ leaseOwner: string | null;
73
+ leaseUntil: number | null;
74
+ createdAt: number;
75
+ updatedAt: number;
76
+ }
77
+ /**
78
+ * What `ScheduleStore.saveSchedule()` receives.
79
+ *
80
+ * ```ts
81
+ * await store.saveSchedule({ ...fields, id: 'weekly-digest', expectRevision: current?.revision ?? null, releaseLease: false, now });
82
+ * ```
83
+ */
84
+ export interface ScheduleSave {
85
+ id: string;
86
+ target: string;
87
+ declared: boolean;
88
+ spec: unknown;
89
+ payload: unknown;
90
+ paused: boolean;
91
+ wakeAt: number | null;
92
+ state: unknown;
93
+ /** `null`: insert it, if no schedule has the id. A number: replace the one whose `revision` is still this. */
94
+ expectRevision: number | null;
95
+ /** Clear the schedule's lease (a change to when it runs: the lease holder's work is outdated). */
96
+ releaseLease: boolean;
97
+ now: number;
98
+ }
99
+ /**
100
+ * What `ScheduleStore.listSchedules()` receives.
101
+ *
102
+ * ```ts
103
+ * await store.listSchedules({ target: 'emails', declared: true, limit: 100, offset: 0 });
104
+ * ```
105
+ */
106
+ export interface ScheduleQuery {
107
+ target?: string;
108
+ /** Only the declared ones (`true`), or only the others (`false`). */
109
+ declared?: boolean;
110
+ limit: number;
111
+ offset: number;
112
+ }
113
+ /**
114
+ * What `ScheduleStore.claimSchedules()` receives.
115
+ *
116
+ * ```ts
117
+ * await store.claimSchedules({ owner: 'pod-1', token: randomUUID(), now, leaseUntil: now + 30_000, limit: 100, targets: ['emails'] });
118
+ * ```
119
+ */
120
+ export interface ScheduleClaimRequest {
121
+ owner: string;
122
+ token: string;
123
+ now: number;
124
+ leaseUntil: number;
125
+ /** At least 1. */
126
+ limit: number;
127
+ /** The targets this worker runs (at least one): it starts only their schedules' runs. */
128
+ targets: string[];
129
+ }
130
+ /**
131
+ * What `ScheduleStore.writeSchedule()` receives.
132
+ *
133
+ * ```ts
134
+ * await store.writeSchedule(id, token, { now, state, wakeAt: next, release: true });
135
+ * ```
136
+ */
137
+ export interface ScheduleWrite {
138
+ now: number;
139
+ state: unknown;
140
+ wakeAt: number | null;
141
+ /** Hand the schedule back: clear its lease. */
142
+ release: boolean;
143
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ import type { Duration } from '../time/duration.js';
2
+ /**
3
+ * When a schedule's occurrences are: exactly one of `cron`, `every` or `rrule`, with its time zone and bounds. Times
4
+ * are wall-clock times in `tz`: on the day clocks go forward, a time that doesn't exist runs that much later (02:30
5
+ * runs at 03:30); on the day they go back, a time that happens twice runs once, the first time. For a fixed interval
6
+ * whatever the clocks do, use `every` (or `tz: 'UTC'`).
7
+ *
8
+ * ```ts
9
+ * const timing: ScheduleTiming = { cron: '0 0 8 * * MON', tz: 'Europe/Warsaw' };
10
+ * ```
11
+ */
12
+ export interface ScheduleTiming {
13
+ /**
14
+ * A cron expression: 5 fields (minute, hour, day of month, month, day of week) or 6 (seconds first), such as
15
+ * `'0 8 * * MON'` or `'0 0 8 * * MON'`. Ranges, steps, lists, names (`JAN`, `MON-FRI`), `L` (the last day of
16
+ * the month; `5L` the last Friday), `#` (`1#2`: the second Monday) and `@daily`-style nicknames. When both
17
+ * the day of month and the day of week are restricted, a day matching either runs, as in cron.
18
+ */
19
+ cron?: string;
20
+ /** A fixed interval of at least a second, counted from `startAt` (default: the Unix epoch), whatever `tz` says. */
21
+ every?: Duration;
22
+ /**
23
+ * An RFC 5545 recurrence rule: FREQ, INTERVAL, COUNT, UNTIL, WKST, BYMONTH, BYMONTHDAY, BYDAY (with ordinals:
24
+ * `-1FR`), BYHOUR, BYMINUTE and BYSECOND, such as `'FREQ=MONTHLY;BYDAY=-1FR;BYHOUR=17'`. `startAt` is its
25
+ * DTSTART (default: midnight, 1 January 1970, in `tz`), whose fields fill the parts the rule leaves out (so
26
+ * give BYHOUR and BYMINUTE), and COUNT counts from it (so COUNT needs a `startAt`).
27
+ */
28
+ rrule?: string;
29
+ /** The IANA time zone of `cron` and `rrule`, such as `'Europe/Warsaw'`. Default `'UTC'`. */
30
+ tz?: string;
31
+ /** No occurrence before it. With `every`, the first occurrence; with `rrule`, its DTSTART. */
32
+ startAt?: Date | number;
33
+ /** No occurrence after it. */
34
+ endAt?: Date | number;
35
+ }
36
+ /**
37
+ * What a schedule does with occurrences no worker was up to start (a deploy, an outage): `'skip'` them (only an
38
+ * occurrence found within a minute of its time starts), start the latest of them `'once'`, or start `'all'` of
39
+ * them at once (the latest 100 at most; it needs `overlap: 'allow'`).
40
+ *
41
+ * ```ts
42
+ * const missed: ScheduleMissed = 'once';
43
+ * ```
44
+ */
45
+ export type ScheduleMissed = 'skip' | 'once' | 'all';
46
+ /**
47
+ * What an occurrence does while a run the schedule started is still unfinished: `'skip'` it, `'allow'` it (both run),
48
+ * `'cancel-previous'` (the running one is cancelled while the new one starts), or `'buffer-one'` (it starts once the
49
+ * running one ends; one occurrence waits at most, later ones are skipped).
50
+ *
51
+ * ```ts
52
+ * const overlap: ScheduleOverlap = 'buffer-one';
53
+ * ```
54
+ */
55
+ export type ScheduleOverlap = 'skip' | 'allow' | 'cancel-previous' | 'buffer-one';
56
+ /**
57
+ * A schedule's timing and policies, as a package takes them (it adds its own, such as workflows' `priority`):
58
+ * `parseSchedule()` checks them.
59
+ *
60
+ * ```ts
61
+ * const schedule: ScheduleOptions = { rrule: 'FREQ=WEEKLY;BYDAY=MO;BYHOUR=8', tz: 'Europe/Warsaw', missed: 'once', limit: 52 };
62
+ * ```
63
+ */
64
+ export interface ScheduleOptions extends ScheduleTiming {
65
+ /** The most occurrences it starts, after which it ends. Default: none. */
66
+ limit?: number;
67
+ /** Default `'skip'`. */
68
+ missed?: ScheduleMissed;
69
+ /** Default `'skip'`. */
70
+ overlap?: ScheduleOverlap;
71
+ }
72
+ /**
73
+ * A schedule's options, checked, as `parseSchedule()` returns them and a `ScheduleStore` keeps them (`spec`, JSON):
74
+ * one of `cron`, `every` (milliseconds) or `rrule`, times in epoch milliseconds, defaults applied. A package adds
75
+ * fields of its own, which the `Scheduler` keeps and compares with the rest.
76
+ *
77
+ * ```ts
78
+ * const spec: ScheduleSpec = parseSchedule({ every: '15m' });
79
+ * // { every: 900_000, tz: 'UTC', startAt: null, endAt: null, limit: null, missed: 'skip', overlap: 'skip' }
80
+ * ```
81
+ */
82
+ export interface ScheduleSpec {
83
+ cron?: string;
84
+ every?: number;
85
+ rrule?: string;
86
+ tz: string;
87
+ startAt: number | null;
88
+ endAt: number | null;
89
+ limit: number | null;
90
+ missed: ScheduleMissed;
91
+ overlap: ScheduleOverlap;
92
+ }
93
+ /**
94
+ * An occurrence of a schedule: what a declared schedule's payload function receives. The run it starts has the id
95
+ * `occurrenceId(id, at)`.
96
+ *
97
+ * ```ts
98
+ * const data = ({ at }: ScheduleOccurrence) => ({ week: isoWeek(new Date(at)) });
99
+ * ```
100
+ */
101
+ export interface ScheduleOccurrence {
102
+ /** The schedule's id. */
103
+ id: string;
104
+ /** When the occurrence was due (epoch milliseconds), also when it starts later (`missed`, `'buffer-one'`) or was triggered (then: when). */
105
+ at: number;
106
+ }
@@ -0,0 +1 @@
1
+ export {};