@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,41 @@
1
+ /**
2
+ * The longest keys MySqlWorkflowStore keeps, in characters: its key columns' lengths (the migrations use them, and the
3
+ * store checks every key it writes against them, so they can't drift apart). Every id, name and key is an indexed
4
+ * `utf8mb4_0900_bin` column on MySQL, and InnoDB caps an index key at 3,072 bytes, 768 four-byte characters, which a
5
+ * composite index's columns share: a journal entry's `(instance id, name)` takes 255 + 512.
6
+ */
7
+ export const MYSQL_KEY_LIMITS = {
8
+ /** An instance's id (`start({ id })`), and so a parent's; a child's id and a schedule run's id are instance ids too. */
9
+ instanceId: 255,
10
+ /** A schedule's id: its runs' ids (`<id>@<ISO time>`) add 25 characters and must fit an instance id. */
11
+ scheduleId: 230,
12
+ /** A workflow's name. */
13
+ workflow: 255,
14
+ /** An instance's concurrency key. */
15
+ concurrencyKey: 255,
16
+ /** An instance's rate-limit key. */
17
+ rateLimitKey: 255,
18
+ /** A signal's name, and a wait's. */
19
+ signal: 255,
20
+ /** A signal's key, and a wait's. */
21
+ signalKey: 255,
22
+ /** A signal's id (`signal(..., { id })`), which deduplicates it. */
23
+ dedupeId: 255,
24
+ /** A journal entry's name: a step's, a wait's, or the engine's (`$child:<child id>`, `$compensate:<step>`...). */
25
+ journalName: 512,
26
+ /** A lease's token (the engine's: a UUID). */
27
+ leaseToken: 255,
28
+ };
29
+ /** Throws a `RangeError` that names `what` when `value` is longer than `max` characters (code points, as MySQL counts). */
30
+ export function checkKey(what, value, max) {
31
+ // A string's UTF-16 length is at least its code points: most keys pass on the first test.
32
+ if (typeof value !== 'string' || value.length <= max) {
33
+ return;
34
+ }
35
+ const characters = [...value].length;
36
+ if (characters > max) {
37
+ const preview = characters > 40 ? `${[...value].slice(0, 40).join('')}…` : value;
38
+ throw new RangeError(`MySqlWorkflowStore: ${what} holds at most ${max} characters on MySQL, and ${JSON.stringify(preview)} has ${characters}. ` +
39
+ "The store's ids, names and keys are indexed columns of bounded length there.");
40
+ }
41
+ }
@@ -0,0 +1,7 @@
1
+ import { StoreSchema } from '@nestjs/store-kit/mysql';
2
+ /**
3
+ * MySqlWorkflowStore's schema: every version of it, in order (a new one goes last, and none ever changes once
4
+ * released), and what the store, its statics and `nest-workflows` do with them. Its tables live in the connection's
5
+ * database, named `<schema>_<table>`, next to the kit's `<schema>_migrations` and `<schema>_locks`.
6
+ */
7
+ export declare const mysqlWorkflowStoreSchema: StoreSchema;
@@ -0,0 +1,16 @@
1
+ import { StoreSchema } from '@nestjs/store-kit/mysql';
2
+ import { WorkflowSchemaError } from '../../sql/workflow-schema.error.js';
3
+ import { initialMigration } from './initial.migration.js';
4
+ /**
5
+ * MySqlWorkflowStore's schema: every version of it, in order (a new one goes last, and none ever changes once
6
+ * released), and what the store, its statics and `nest-workflows` do with them. Its tables live in the connection's
7
+ * database, named `<schema>_<table>`, next to the kit's `<schema>_migrations` and `<schema>_locks`.
8
+ */
9
+ export const mysqlWorkflowStoreSchema = new StoreSchema({
10
+ packageName: '@nestjs/workflows',
11
+ storeName: 'MySqlWorkflowStore',
12
+ command: 'nest-workflows',
13
+ defaultSchema: 'nest_workflows',
14
+ migrations: [initialMigration],
15
+ createError: (message, details) => new WorkflowSchemaError(message, details),
16
+ });
@@ -0,0 +1,9 @@
1
+ import { type StoreMigration } from '@nestjs/store-kit/mysql';
2
+ /**
3
+ * The store's tables on MySQL, in the connection's database (`<schema>_instances`...), each created with its indexes in
4
+ * one statement. Times are epoch milliseconds from the engine's clock (`bigint`); inputs, outputs, errors, payloads,
5
+ * journal entries and schedule specs are the engine's JSON (`json`). Every id, name and key is a `utf8mb4_0900_bin`
6
+ * column (`keyColumn()`), so keys that differ in case or accents stay apart, of a bounded length (`MYSQL_KEY_LIMITS`).
7
+ * No foreign keys: a store's delete removes an instance's journal and waits in its own transaction.
8
+ */
9
+ export declare const initialMigration: StoreMigration;
@@ -0,0 +1,104 @@
1
+ import { keyColumn } from '@nestjs/store-kit/mysql';
2
+ import { MYSQL_KEY_LIMITS as L } from '../key-limits.js';
3
+ /**
4
+ * The store's tables on MySQL, in the connection's database (`<schema>_instances`...), each created with its indexes in
5
+ * one statement. Times are epoch milliseconds from the engine's clock (`bigint`); inputs, outputs, errors, payloads,
6
+ * journal entries and schedule specs are the engine's JSON (`json`). Every id, name and key is a `utf8mb4_0900_bin`
7
+ * column (`keyColumn()`), so keys that differ in case or accents stay apart, of a bounded length (`MYSQL_KEY_LIMITS`).
8
+ * No foreign keys: a store's delete removes an instance's journal and waits in its own transaction.
9
+ */
10
+ export const initialMigration = {
11
+ version: 1,
12
+ name: 'initial',
13
+ up: (t) => [
14
+ `CREATE TABLE ${t('instances')} (
15
+ id ${keyColumn(L.instanceId)} NOT NULL PRIMARY KEY,
16
+ workflow ${keyColumn(L.workflow)} NOT NULL,
17
+ version int NOT NULL,
18
+ parent_id ${keyColumn(L.instanceId)},
19
+ parent_close ${keyColumn(16)},
20
+ concurrency_key ${keyColumn(L.concurrencyKey)},
21
+ rate_limit_key ${keyColumn(L.rateLimitKey)},
22
+ priority int NOT NULL DEFAULT 0,
23
+ schedule_id ${keyColumn(L.scheduleId)},
24
+ scheduled_at bigint,
25
+ status ${keyColumn(32)} NOT NULL,
26
+ input json,
27
+ output json,
28
+ error json,
29
+ wake_at bigint,
30
+ lease_token ${keyColumn(L.leaseToken)},
31
+ lease_owner text,
32
+ lease_until bigint,
33
+ cancel_requested boolean NOT NULL DEFAULT false,
34
+ terminate_requested boolean NOT NULL DEFAULT false,
35
+ cancel_reason text,
36
+ deadline bigint,
37
+ custom_status json,
38
+ signal_cursor bigint NOT NULL,
39
+ runs int NOT NULL DEFAULT 0,
40
+ created_at bigint NOT NULL,
41
+ updated_at bigint NOT NULL,
42
+ KEY instances_due (wake_at),
43
+ KEY instances_created (created_at, id),
44
+ KEY instances_status (status, updated_at),
45
+ KEY instances_leased (workflow, concurrency_key, lease_until),
46
+ KEY instances_parent (parent_id, created_at),
47
+ KEY instances_schedule (schedule_id, created_at)
48
+ )`,
49
+ // One row per step, sleep, wait or compensation; seq keeps them in first-write order.
50
+ `CREATE TABLE ${t('journal')} (
51
+ seq bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
52
+ instance_id ${keyColumn(L.instanceId)} NOT NULL,
53
+ name ${keyColumn(L.journalName)} NOT NULL,
54
+ entry json NOT NULL,
55
+ UNIQUE KEY journal_name (instance_id, name)
56
+ )`,
57
+ // The signals a suspended instance waits for.
58
+ `CREATE TABLE ${t('waits')} (
59
+ instance_id ${keyColumn(L.instanceId)} NOT NULL,
60
+ position int NOT NULL,
61
+ \`signal\` ${keyColumn(L.signal)} NOT NULL,
62
+ \`key\` ${keyColumn(L.signalKey)},
63
+ PRIMARY KEY (instance_id, position),
64
+ KEY waits_signal (\`signal\`, \`key\`)
65
+ )`,
66
+ `CREATE TABLE ${t('signals')} (
67
+ id bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
68
+ name ${keyColumn(L.signal)} NOT NULL,
69
+ \`key\` ${keyColumn(L.signalKey)},
70
+ dedupe_id ${keyColumn(L.dedupeId)},
71
+ payload json,
72
+ created_at bigint NOT NULL,
73
+ UNIQUE KEY signals_dedupe (name, dedupe_id),
74
+ KEY signals_lookup (name, \`key\`, id)
75
+ )`,
76
+ `CREATE TABLE ${t('schedules')} (
77
+ id ${keyColumn(L.scheduleId)} NOT NULL PRIMARY KEY,
78
+ workflow ${keyColumn(L.workflow)} NOT NULL,
79
+ declared boolean NOT NULL,
80
+ spec json NOT NULL,
81
+ input json,
82
+ paused boolean NOT NULL,
83
+ wake_at bigint,
84
+ state json NOT NULL,
85
+ revision int NOT NULL,
86
+ lease_token ${keyColumn(L.leaseToken)},
87
+ lease_owner text,
88
+ lease_until bigint,
89
+ created_at bigint NOT NULL,
90
+ updated_at bigint NOT NULL,
91
+ KEY schedules_due (wake_at),
92
+ KEY schedules_workflow (workflow, id)
93
+ )`,
94
+ // The rate-limit windows claims count: the workflow's own (key '', which no instance key is) and one per key.
95
+ `CREATE TABLE ${t('rate_limits')} (
96
+ workflow ${keyColumn(L.workflow)} NOT NULL,
97
+ \`key\` ${keyColumn(L.rateLimitKey)} NOT NULL,
98
+ window_end bigint NOT NULL,
99
+ \`count\` int NOT NULL,
100
+ PRIMARY KEY (workflow, \`key\`),
101
+ KEY rate_limits_end (window_end)
102
+ )`,
103
+ ],
104
+ };
@@ -0,0 +1,197 @@
1
+ import { type OnModuleInit } from '@nestjs/common';
2
+ import { type MigrationSqlOptions, type MigrationStatementsOptions } from '@nestjs/store-kit/mysql';
3
+ import type { WorkflowInstance, WorkflowStatus } from '../interfaces/workflow-instance.interface.js';
4
+ import type { NewWorkflowInstance, NewWorkflowSignal, WorkflowCancelRequest, WorkflowClaim, WorkflowClaimRequest, WorkflowInstanceDetails, WorkflowListQuery, WorkflowPurgeQuery, WorkflowPurgeResult, WorkflowReopen, WorkflowScheduleClaimRequest, WorkflowScheduleQuery, WorkflowScheduleRecord, WorkflowScheduleSave, WorkflowScheduleWrite, WorkflowSignalQuery, WorkflowSignalRecord, WorkflowSignalResult, WorkflowStore, WorkflowWrite } from '../interfaces/workflow-store.interface.js';
5
+ import type { WorkflowStorage } from '../storage/workflow.storage.js';
6
+ import type { MySqlWorkflowStoreOptions } from './interfaces/mysql-workflow-store-options.interface.js';
7
+ /**
8
+ * The first-party `WorkflowStore` on MySQL (8.4 LTS and 9.x), through the client the application already has
9
+ * (`fromMysql2()`, `fromDrizzle()`, `fromTypeOrm()`, `fromPrisma()`, `fromKysely()`). It keeps its tables in the
10
+ * connection's database, named `<schema>_<table>` (`nest_workflows_instances`...), which its migrations create and
11
+ * bring up to date, and it joins the application's transactions for `start()` and `signal()` with `{ transaction }`.
12
+ *
13
+ * ```ts
14
+ * @Module({
15
+ * imports: [WorkflowsModule.forRoot()],
16
+ * providers: [
17
+ * {
18
+ * provide: MySqlWorkflowStore,
19
+ * inject: [getDrizzleToken(), WorkflowStorage],
20
+ * useFactory: (db: Database, storage: WorkflowStorage) => new MySqlWorkflowStore({ executor: fromDrizzle(db) }, storage),
21
+ * },
22
+ * ],
23
+ * })
24
+ * export class AppModule {}
25
+ * ```
26
+ *
27
+ * Given `storage`, it registers itself (`storage.registerSource(this)`), as every store does. It checks the server and
28
+ * its schema, or migrates it (`migrate`), in `onModuleInit` (so before the worker starts), or at its first call
29
+ * outside Nest.
30
+ *
31
+ * On MySQL:
32
+ * - Ids, names and keys are case- and accent-sensitive, and bounded: an instance id, a workflow's or a signal's name,
33
+ * a signal's key or id, a concurrency or rate-limit key hold at most 255 characters, a schedule's id 230, a
34
+ * journal entry's name 512. A longer one fails with a `RangeError` that names it, before any SQL.
35
+ * - The application's transactions may be REPEATABLE READ, MySQL's default. `signal(..., { transaction })` finds the
36
+ * instances to wake with a locking read of their waits, which sees the latest committed ones whatever the level; the
37
+ * signal lock is held until the application's transaction ends, as on PostgreSQL. `start(..., { transaction })`
38
+ * reads the last signal id from the transaction's snapshot: under REPEATABLE READ, signals committed after the
39
+ * transaction's first read can match the new instance's waits.
40
+ * - The store's own transactions run READ COMMITTED and are run again when MySQL breaks a deadlock (1213). A deadlock in
41
+ * the application's transaction rolled the application's own writes back too: the store lets it through, for the
42
+ * application to retry its transaction.
43
+ */
44
+ export declare class MySqlWorkflowStore implements WorkflowStore, OnModuleInit {
45
+ /**
46
+ * The SQL of the store's migrations, for teams that apply migrations with their own tool (drizzle-kit, TypeORM,
47
+ * Prisma Migrate, Flyway...) and run the store with `migrate: false`: from version `from` (default `0`, a new
48
+ * database) to `to` (default: the version this version of the package needs), with the bookkeeping that tells the
49
+ * store which versions a schema has (`<schema>_migrations`). MySQL commits each DDL statement on its own, so the
50
+ * statements don't run in one transaction: apply them in order, each once. Downgrades aren't supported. With
51
+ * `statementBreakpoints`, drizzle-kit's `--> statement-breakpoint` separates the statements, which its MySQL
52
+ * migrator needs (it runs one statement per call).
53
+ *
54
+ * ```ts
55
+ * // drizzle/0003_workflows.sql, created empty by `npx drizzle-kit generate --custom --name=workflows`
56
+ * writeFileSync('drizzle/0003_workflows.sql', MySqlWorkflowStore.migrationSql({ statementBreakpoints: true }));
57
+ * ```
58
+ */
59
+ static migrationSql(options?: MigrationSqlOptions): string;
60
+ /**
61
+ * `migrationSql()`'s statements, one per string, for a migration tool that runs one statement per call (TypeORM's
62
+ * `queryRunner.query()`, mysql2): run them in order, each once.
63
+ *
64
+ * ```ts
65
+ * export class Workflows1790000000000 implements MigrationInterface {
66
+ * async up(queryRunner: QueryRunner): Promise<void> {
67
+ * for (const statement of MySqlWorkflowStore.migrationStatements()) {
68
+ * await queryRunner.query(statement);
69
+ * }
70
+ * }
71
+ * }
72
+ * ```
73
+ */
74
+ static migrationStatements(options?: MigrationStatementsOptions): string[];
75
+ /** The schema version this version of the package needs: its last migration. */
76
+ static readonly schemaVersion: number;
77
+ private readonly logger;
78
+ private readonly executor;
79
+ private readonly schema;
80
+ private readonly t;
81
+ /** The server and the schema checked, and the schema migrated (`migrate`), before the first statement. */
82
+ private readonly readiness;
83
+ /** The lock keys whose rows this store created, or found (see `createLockRows()`). */
84
+ private readonly lockRows;
85
+ constructor(options: MySqlWorkflowStoreOptions, storage?: WorkflowStorage);
86
+ /**
87
+ * Checks the server and migrates the schema (`migrate`) or checks it, before the worker starts: startup fails if it
88
+ * can't serve. Then creates the signal lock's row (see `createLockRows()`).
89
+ */
90
+ onModuleInit(): Promise<void>;
91
+ /**
92
+ * Applies the migrations the schema hasn't had yet, whatever `migrate` says, one statement at a time under a lock
93
+ * (`GET_LOCK()`): of processes that migrate together, one applies them, and a run that stopped halfway resumes where
94
+ * it stopped. Resolves to the versions it applied (`[]`: none were pending).
95
+ */
96
+ migrate(): Promise<number[]>;
97
+ create(instance: NewWorkflowInstance): Promise<{
98
+ instance: WorkflowInstance;
99
+ created: boolean;
100
+ }>;
101
+ /** `start(..., { transaction })`: the instance commits or rolls back with the application's rows. */
102
+ createInTransaction(transaction: unknown, instance: NewWorkflowInstance): Promise<{
103
+ instance: WorkflowInstance;
104
+ created: boolean;
105
+ }>;
106
+ get(id: string, options?: {
107
+ journal?: boolean;
108
+ }): Promise<WorkflowInstanceDetails | null>;
109
+ list(query: WorkflowListQuery): Promise<WorkflowInstance[]>;
110
+ requestCancel(id: string, { reason, now, terminate }: WorkflowCancelRequest): Promise<boolean>;
111
+ /** `WorkflowClient.retry()`: one conditional update, then the journal, in one transaction. */
112
+ reopen(id: string, reopen: WorkflowReopen): Promise<boolean>;
113
+ /** `WorkflowClient.delete()`: its journal and waits go with it, in the same transaction. */
114
+ delete(id: string, statuses: WorkflowStatus[]): Promise<boolean>;
115
+ signal(signal: NewWorkflowSignal): Promise<WorkflowSignalResult>;
116
+ /**
117
+ * `signal(..., { transaction })`: the signal and its wake-ups commit with the application's rows. Any isolation
118
+ * level: the wake-up reads the latest committed waits with a locking read, even under REPEATABLE READ.
119
+ */
120
+ signalInTransaction(transaction: unknown, signal: NewWorkflowSignal): Promise<WorkflowSignalResult>;
121
+ signals(query: WorkflowSignalQuery): Promise<WorkflowSignalRecord[]>;
122
+ purge(query: WorkflowPurgeQuery): Promise<WorkflowPurgeResult>;
123
+ saveSchedule(save: WorkflowScheduleSave): Promise<WorkflowScheduleRecord | null>;
124
+ getSchedule(id: string): Promise<WorkflowScheduleRecord | null>;
125
+ listSchedules(query: WorkflowScheduleQuery): Promise<WorkflowScheduleRecord[]>;
126
+ deleteSchedule(id: string, revision?: number): Promise<boolean>;
127
+ claimSchedules(request: WorkflowScheduleClaimRequest): Promise<WorkflowScheduleRecord[]>;
128
+ writeSchedule(id: string, token: string, write: WorkflowScheduleWrite): Promise<boolean>;
129
+ claim(request: WorkflowClaimRequest): Promise<WorkflowClaim>;
130
+ /**
131
+ * A claim under concurrency or rate limits. Claims of a workflow with a concurrency limit take its lock first (the
132
+ * kit's `lockKeys()`, in one order), so they count the slots live leases hold and lease the instances that fit one
133
+ * after the other: two never both take the last slot. Rate-limit windows are counted again under their rows' locks,
134
+ * once the instances are picked (see `takeRoom()`).
135
+ */
136
+ private claimWithin;
137
+ /**
138
+ * Of `picked` (in claim order), the instances their rate-limit windows have room for, recorded in the windows. Each
139
+ * window's row is inserted where missing and locked, in one order (`RateWindowClaim.windows`): the count read under
140
+ * the lock is exact (a concurrent claim of the same window waits for this one), and a purge can't delete the row in
141
+ * between.
142
+ */
143
+ private takeRoom;
144
+ /**
145
+ * Locks up to `limit` rows of `table` (as `alias`) that `due` selects, in `order`, passing over the rows other
146
+ * transactions hold, as PostgreSQL's `FOR UPDATE SKIP LOCKED` under `ORDER BY ... LIMIT` does. InnoDB's own locks
147
+ * every row its sort reads, not only those it returns (a claim of 7 among 60 due instances held all 60, and a
148
+ * concurrent claim found none), so each round:
149
+ *
150
+ * 1. a consistent read finds the next candidates in `order` (it locks nothing, whatever the plan): as many as the rows
151
+ * still wanted, and as many more as other transactions held in the rounds before, so a round reaches past the rows
152
+ * the claims beside this one are taking;
153
+ * 2. a locking read takes those still due that no one holds (`SKIP LOCKED`), by primary key and in its order, up to the
154
+ * rows wanted: InnoDB stops there, and locks no candidate it doesn't return. (Among the candidates of a round, the
155
+ * ones taken are the first free by id: under contention, a claim may take a less overdue one than another claim.)
156
+ *
157
+ * Resolves to the ids locked.
158
+ */
159
+ private lockDue;
160
+ /** Leases the instances `ids` (locked by this transaction), and reads them back in claim order. */
161
+ private lease;
162
+ renew(id: string, token: string, leaseUntil: number): Promise<{
163
+ cancelRequested: boolean;
164
+ terminateRequested: boolean;
165
+ } | null>;
166
+ write(id: string, token: string, write: WorkflowWrite): Promise<boolean>;
167
+ /** The store's readiness (the server and the schema), then the signal lock's row: before its own first statement. */
168
+ private ready;
169
+ /**
170
+ * Creates the rows of lock keys (the kit's `<schema>_locks`, one per key, never deleted) ahead of time, with the kit's
171
+ * `ensureLockRows()`: in transactions of their own, which commit before any transaction takes those locks. When the
172
+ * transaction that created a lock's row rolls back, MySQL makes the transactions waiting for that row deadlock
173
+ * (1213): so a lock's first use, which may be in the application's transaction (`signal(..., { transaction })`) or in
174
+ * one that fails, never creates its row. The rows there already are only noted, by a read that takes no lock: a
175
+ * process's startup never waits behind the transactions holding them (a signal in the application's transaction).
176
+ * The store's lock keys are few: `signals`, and a concurrency lock per workflow with a limit. (A store first called
177
+ * inside the application's transaction, before `onModuleInit()` or any call outside one, creates the signal lock's row
178
+ * there.)
179
+ */
180
+ private createLockRows;
181
+ private insertInstance;
182
+ private insertSignal;
183
+ /**
184
+ * Replaces the instance's waits, and says whether a signal committed after the execution read its cursor matches
185
+ * one of the new ones: then the instance stays due instead of losing it. (Run under the signal lock, shared: every
186
+ * signal before it has committed, and none after it can commit before this transaction.)
187
+ */
188
+ private replaceWaits;
189
+ /** Journal entries by name: a new name goes last (`seq`), a known one is replaced in place. */
190
+ private upsertEntries;
191
+ /** Deletes the journals and waits of instances deleted in this transaction. */
192
+ private deleteChildren;
193
+ private readSchedule;
194
+ /** Due, unleased instances of the versions the worker runs, as `alias` in a statement of `p`. */
195
+ private isDue;
196
+ private lastSignalId;
197
+ }