@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.
- package/LICENSE +23 -0
- package/README.md +50 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +8 -0
- package/dist/core/codecs/aes-gcm-payload.codec.d.ts +42 -0
- package/dist/core/codecs/aes-gcm-payload.codec.js +119 -0
- package/dist/core/codecs/index.d.ts +2 -0
- package/dist/core/codecs/index.js +2 -0
- package/dist/core/codecs/payload-codecs.d.ts +59 -0
- package/dist/core/codecs/payload-codecs.js +130 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +21 -0
- package/dist/core/interfaces/clock.interface.d.ts +18 -0
- package/dist/core/interfaces/clock.interface.js +1 -0
- package/dist/core/interfaces/index.d.ts +9 -0
- package/dist/core/interfaces/index.js +9 -0
- package/dist/core/interfaces/leased-worker-options.interface.d.ts +94 -0
- package/dist/core/interfaces/leased-worker-options.interface.js +1 -0
- package/dist/core/interfaces/limits.interface.d.ts +92 -0
- package/dist/core/interfaces/limits.interface.js +1 -0
- package/dist/core/interfaces/payload-codec.interface.d.ts +64 -0
- package/dist/core/interfaces/payload-codec.interface.js +1 -0
- package/dist/core/interfaces/result-waiter-options.interface.d.ts +52 -0
- package/dist/core/interfaces/result-waiter-options.interface.js +1 -0
- package/dist/core/interfaces/retry-settings.interface.d.ts +71 -0
- package/dist/core/interfaces/retry-settings.interface.js +1 -0
- package/dist/core/interfaces/schedule-store.interface.d.ts +143 -0
- package/dist/core/interfaces/schedule-store.interface.js +1 -0
- package/dist/core/interfaces/schedule.interface.d.ts +106 -0
- package/dist/core/interfaces/schedule.interface.js +1 -0
- package/dist/core/interfaces/scheduler-options.interface.d.ts +152 -0
- package/dist/core/interfaces/scheduler-options.interface.js +1 -0
- package/dist/core/limits/index.d.ts +1 -0
- package/dist/core/limits/index.js +1 -0
- package/dist/core/limits/limits.d.ts +61 -0
- package/dist/core/limits/limits.js +130 -0
- package/dist/core/results/index.d.ts +1 -0
- package/dist/core/results/index.js +1 -0
- package/dist/core/results/result-waiter.d.ts +54 -0
- package/dist/core/results/result-waiter.js +140 -0
- package/dist/core/retries/index.d.ts +1 -0
- package/dist/core/retries/index.js +1 -0
- package/dist/core/retries/retry.d.ts +60 -0
- package/dist/core/retries/retry.js +107 -0
- package/dist/core/scheduling/cron.util.d.ts +52 -0
- package/dist/core/scheduling/cron.util.js +262 -0
- package/dist/core/scheduling/encoded-schedule.store.d.ts +26 -0
- package/dist/core/scheduling/encoded-schedule.store.js +54 -0
- package/dist/core/scheduling/in-memory-schedule.store.d.ts +20 -0
- package/dist/core/scheduling/in-memory-schedule.store.js +90 -0
- package/dist/core/scheduling/index.d.ts +3 -0
- package/dist/core/scheduling/index.js +3 -0
- package/dist/core/scheduling/rrule.util.d.ts +55 -0
- package/dist/core/scheduling/rrule.util.js +371 -0
- package/dist/core/scheduling/schedule-spec.d.ts +57 -0
- package/dist/core/scheduling/schedule-spec.js +233 -0
- package/dist/core/scheduling/scheduler.d.ts +134 -0
- package/dist/core/scheduling/scheduler.js +450 -0
- package/dist/core/scheduling/time-zone.util.d.ts +18 -0
- package/dist/core/scheduling/time-zone.util.js +88 -0
- package/dist/core/time/clock.d.ts +28 -0
- package/dist/core/time/clock.js +35 -0
- package/dist/core/time/duration.d.ts +21 -0
- package/dist/core/time/duration.js +30 -0
- package/dist/core/time/index.d.ts +2 -0
- package/dist/core/time/index.js +2 -0
- package/dist/core/utils/canonical.util.d.ts +2 -0
- package/dist/core/utils/canonical.util.js +4 -0
- package/dist/core/utils/maybe.util.d.ts +8 -0
- package/dist/core/utils/maybe.util.js +14 -0
- package/dist/core/utils/payload-place.util.d.ts +10 -0
- package/dist/core/utils/payload-place.util.js +32 -0
- package/dist/core/workers/index.d.ts +1 -0
- package/dist/core/workers/index.js +1 -0
- package/dist/core/workers/leased-worker.d.ts +86 -0
- package/dist/core/workers/leased-worker.js +280 -0
- package/dist/cqrs/decorators/index.d.ts +2 -0
- package/dist/cqrs/decorators/index.js +2 -0
- package/dist/cqrs/decorators/signal-on.decorator.d.ts +18 -0
- package/dist/cqrs/decorators/signal-on.decorator.js +34 -0
- package/dist/cqrs/decorators/start-on.decorator.d.ts +18 -0
- package/dist/cqrs/decorators/start-on.decorator.js +49 -0
- package/dist/cqrs/index.d.ts +3 -0
- package/dist/cqrs/index.js +5 -0
- package/dist/cqrs/interfaces/index.d.ts +4 -0
- package/dist/cqrs/interfaces/index.js +4 -0
- package/dist/cqrs/interfaces/signal-on-options.interface.d.ts +22 -0
- package/dist/cqrs/interfaces/signal-on-options.interface.js +1 -0
- package/dist/cqrs/interfaces/start-on-options.interface.d.ts +23 -0
- package/dist/cqrs/interfaces/start-on-options.interface.js +1 -0
- package/dist/cqrs/interfaces/workflow-dispatcher-context.interface.d.ts +10 -0
- package/dist/cqrs/interfaces/workflow-dispatcher-context.interface.js +1 -0
- package/dist/cqrs/interfaces/workflow-event-route.interface.d.ts +20 -0
- package/dist/cqrs/interfaces/workflow-event-route.interface.js +1 -0
- package/dist/cqrs/utils/event-routes.util.d.ts +7 -0
- package/dist/cqrs/utils/event-routes.util.js +22 -0
- package/dist/cqrs/workflow-event.publisher.d.ts +52 -0
- package/dist/cqrs/workflow-event.publisher.js +257 -0
- package/dist/cqrs/workflow-events.explorer.d.ts +27 -0
- package/dist/cqrs/workflow-events.explorer.js +92 -0
- package/dist/cqrs/workflows-cqrs.module.d.ts +10 -0
- package/dist/cqrs/workflows-cqrs.module.js +27 -0
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/workflow.decorator.d.ts +7 -0
- package/dist/decorators/workflow.decorator.js +52 -0
- package/dist/errors/child-workflow-failed.error.d.ts +13 -0
- package/dist/errors/child-workflow-failed.error.js +14 -0
- package/dist/errors/index.d.ts +13 -0
- package/dist/errors/index.js +13 -0
- package/dist/errors/non-retryable-step.error.d.ts +4 -0
- package/dist/errors/non-retryable-step.error.js +7 -0
- package/dist/errors/step-failed.error.d.ts +15 -0
- package/dist/errors/step-failed.error.js +16 -0
- package/dist/errors/step-timeout.error.d.ts +5 -0
- package/dist/errors/step-timeout.error.js +8 -0
- package/dist/errors/workflow-definition.error.d.ts +10 -0
- package/dist/errors/workflow-definition.error.js +13 -0
- package/dist/errors/workflow-failed.error.d.ts +20 -0
- package/dist/errors/workflow-failed.error.js +17 -0
- package/dist/errors/workflow-id-conflict.error.d.ts +6 -0
- package/dist/errors/workflow-id-conflict.error.js +9 -0
- package/dist/errors/workflow-interrupt.error.d.ts +32 -0
- package/dist/errors/workflow-interrupt.error.js +19 -0
- package/dist/errors/workflow-non-determinism.error.d.ts +4 -0
- package/dist/errors/workflow-non-determinism.error.js +7 -0
- package/dist/errors/workflow-not-found.error.d.ts +10 -0
- package/dist/errors/workflow-not-found.error.js +13 -0
- package/dist/errors/workflow-result-timeout.error.d.ts +11 -0
- package/dist/errors/workflow-result-timeout.error.js +13 -0
- package/dist/errors/workflow-state.error.d.ts +11 -0
- package/dist/errors/workflow-state.error.js +14 -0
- package/dist/errors/workflow.error.d.ts +7 -0
- package/dist/errors/workflow.error.js +7 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +3 -0
- package/dist/events/workflow-events.interface.d.ts +145 -0
- package/dist/events/workflow-events.interface.js +1 -0
- package/dist/events/workflow-events.service.d.ts +17 -0
- package/dist/events/workflow-events.service.js +38 -0
- package/dist/events/workflows.channels.d.ts +3 -0
- package/dist/events/workflows.channels.js +9 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +23 -0
- package/dist/interfaces/index.d.ts +13 -0
- package/dist/interfaces/index.js +13 -0
- package/dist/interfaces/serialized-workflow-error.interface.d.ts +6 -0
- package/dist/interfaces/serialized-workflow-error.interface.js +1 -0
- package/dist/interfaces/workflow-client.interface.d.ts +156 -0
- package/dist/interfaces/workflow-client.interface.js +1 -0
- package/dist/interfaces/workflow-clock.interface.d.ts +7 -0
- package/dist/interfaces/workflow-clock.interface.js +1 -0
- package/dist/interfaces/workflow-context.interface.d.ts +241 -0
- package/dist/interfaces/workflow-context.interface.js +1 -0
- package/dist/interfaces/workflow-decorator-options.interface.d.ts +101 -0
- package/dist/interfaces/workflow-decorator-options.interface.js +1 -0
- package/dist/interfaces/workflow-instance.interface.d.ts +83 -0
- package/dist/interfaces/workflow-instance.interface.js +1 -0
- package/dist/interfaces/workflow-payload-codec.interface.d.ts +32 -0
- package/dist/interfaces/workflow-payload-codec.interface.js +1 -0
- package/dist/interfaces/workflow-retry-options.interface.d.ts +9 -0
- package/dist/interfaces/workflow-retry-options.interface.js +1 -0
- package/dist/interfaces/workflow-runner.interface.d.ts +11 -0
- package/dist/interfaces/workflow-runner.interface.js +1 -0
- package/dist/interfaces/workflow-schedule.interface.d.ts +104 -0
- package/dist/interfaces/workflow-schedule.interface.js +1 -0
- package/dist/interfaces/workflow-storage.interface.d.ts +4 -0
- package/dist/interfaces/workflow-storage.interface.js +1 -0
- package/dist/interfaces/workflow-store.interface.d.ts +488 -0
- package/dist/interfaces/workflow-store.interface.js +1 -0
- package/dist/interfaces/workflows-module-options.interface.d.ts +80 -0
- package/dist/interfaces/workflows-module-options.interface.js +1 -0
- package/dist/mysql/index.d.ts +5 -0
- package/dist/mysql/index.js +8 -0
- package/dist/mysql/interfaces/index.d.ts +1 -0
- package/dist/mysql/interfaces/index.js +1 -0
- package/dist/mysql/interfaces/mysql-workflow-store-options.interface.d.ts +25 -0
- package/dist/mysql/interfaces/mysql-workflow-store-options.interface.js +1 -0
- package/dist/mysql/key-limits.d.ts +30 -0
- package/dist/mysql/key-limits.js +41 -0
- package/dist/mysql/migrations/index.d.ts +7 -0
- package/dist/mysql/migrations/index.js +16 -0
- package/dist/mysql/migrations/initial.migration.d.ts +9 -0
- package/dist/mysql/migrations/initial.migration.js +104 -0
- package/dist/mysql/mysql-workflow.store.d.ts +197 -0
- package/dist/mysql/mysql-workflow.store.js +828 -0
- package/dist/postgres/errors/index.d.ts +1 -0
- package/dist/postgres/errors/index.js +2 -0
- package/dist/postgres/index.d.ts +5 -0
- package/dist/postgres/index.js +8 -0
- package/dist/postgres/interfaces/index.d.ts +1 -0
- package/dist/postgres/interfaces/index.js +1 -0
- package/dist/postgres/interfaces/postgres-workflow-store-options.interface.d.ts +22 -0
- package/dist/postgres/interfaces/postgres-workflow-store-options.interface.js +1 -0
- package/dist/postgres/migrations/index.d.ts +6 -0
- package/dist/postgres/migrations/index.js +15 -0
- package/dist/postgres/migrations/initial.migration.d.ts +6 -0
- package/dist/postgres/migrations/initial.migration.js +106 -0
- package/dist/postgres/postgres-workflow.store.d.ts +132 -0
- package/dist/postgres/postgres-workflow.store.js +586 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +6 -0
- package/dist/services/workflow-client.service.d.ts +146 -0
- package/dist/services/workflow-client.service.js +436 -0
- package/dist/services/workflow-execution.service.d.ts +267 -0
- package/dist/services/workflow-execution.service.js +1076 -0
- package/dist/services/workflow-registry.service.d.ts +56 -0
- package/dist/services/workflow-registry.service.js +172 -0
- package/dist/services/workflow-scheduler.service.d.ts +54 -0
- package/dist/services/workflow-scheduler.service.js +173 -0
- package/dist/services/workflow-schedules.service.d.ts +60 -0
- package/dist/services/workflow-schedules.service.js +193 -0
- package/dist/services/workflow-worker.service.d.ts +92 -0
- package/dist/services/workflow-worker.service.js +468 -0
- package/dist/signals/index.d.ts +1 -0
- package/dist/signals/index.js +1 -0
- package/dist/signals/workflow.signal.d.ts +17 -0
- package/dist/signals/workflow.signal.js +28 -0
- package/dist/sql/rate-windows.d.ts +41 -0
- package/dist/sql/rate-windows.js +53 -0
- package/dist/sql/workflow-rows.d.ts +16 -0
- package/dist/sql/workflow-rows.js +102 -0
- package/dist/sql/workflow-schema.error.d.ts +21 -0
- package/dist/sql/workflow-schema.error.js +15 -0
- package/dist/storage/encoded-workflow.store.d.ts +71 -0
- package/dist/storage/encoded-workflow.store.js +201 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/workflow.storage.d.ts +55 -0
- package/dist/storage/workflow.storage.js +159 -0
- package/dist/stores/in-memory-workflow.store.d.ts +75 -0
- package/dist/stores/in-memory-workflow.store.js +380 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/testing/assertions.d.ts +10 -0
- package/dist/testing/assertions.js +72 -0
- package/dist/testing/index.d.ts +38 -0
- package/dist/testing/index.js +1089 -0
- package/dist/testing/schedule-store.contract.d.ts +33 -0
- package/dist/testing/schedule-store.contract.js +161 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/journal-limits.util.d.ts +6 -0
- package/dist/utils/journal-limits.util.js +25 -0
- package/dist/utils/new-instance.util.d.ts +26 -0
- package/dist/utils/new-instance.util.js +87 -0
- package/dist/utils/normalize.util.d.ts +5 -0
- package/dist/utils/normalize.util.js +11 -0
- package/dist/utils/run-timeout.util.d.ts +3 -0
- package/dist/utils/run-timeout.util.js +15 -0
- package/dist/utils/schedule-spec.util.d.ts +16 -0
- package/dist/utils/schedule-spec.util.js +9 -0
- package/dist/utils/schedule-store.util.d.ts +15 -0
- package/dist/utils/schedule-store.util.js +44 -0
- package/dist/utils/serialize-error.util.d.ts +7 -0
- package/dist/utils/serialize-error.util.js +22 -0
- package/dist/utils/step-scope.util.d.ts +16 -0
- package/dist/utils/step-scope.util.js +38 -0
- package/dist/workflows.constants.d.ts +10 -0
- package/dist/workflows.constants.js +10 -0
- package/dist/workflows.module-definition.d.ts +4 -0
- package/dist/workflows.module-definition.js +6 -0
- package/dist/workflows.module.d.ts +24 -0
- package/dist/workflows.module.js +68 -0
- package/package.json +123 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import type { SerializedWorkflowError } from './serialized-workflow-error.interface.js';
|
|
2
|
+
import type { WorkflowRateWindow } from './workflow-decorator-options.interface.js';
|
|
3
|
+
import type { WorkflowInstance, WorkflowJournalEntry, WorkflowParentClose, WorkflowStatus, WorkflowWait } from './workflow-instance.interface.js';
|
|
4
|
+
/**
|
|
5
|
+
* Where workflow instances, their journals, their waits and the signals sent to them live.
|
|
6
|
+
* The package ships `InMemoryWorkflowStore` (the default, for development and tests); in
|
|
7
|
+
* production, write a provider on your database that implements this interface and registers
|
|
8
|
+
* itself with `WorkflowStorage.registerSource(this)` in its constructor.
|
|
9
|
+
*
|
|
10
|
+
* "The store contract" (https://docs.nestjs.com/reliability/workflows#the-store-contract) sums up
|
|
11
|
+
* every method; each one's JSDoc here says what it must do, what must be atomic, and the race
|
|
12
|
+
* each rule prevents. `workflowStoreContract()` from `@nestjs/workflows/testing` checks an
|
|
13
|
+
* implementation against it, races included.
|
|
14
|
+
*
|
|
15
|
+
* A few methods need more than a plain read or write: `claim` and `claimSchedules` (a lock that
|
|
16
|
+
* skips rows other claims hold; `claim` also counts limits under locks), `write`, `renew` and
|
|
17
|
+
* `writeSchedule` (fenced by the lease token), `signal` together with `write` when it registers
|
|
18
|
+
* waits (a lock that orders them, see `signal`), and `saveSchedule` (conditional on a revision).
|
|
19
|
+
* `reopen` and `purge` re-check their conditions on the rows they change. Everything else is safe
|
|
20
|
+
* to implement naively.
|
|
21
|
+
*/
|
|
22
|
+
export interface WorkflowStore {
|
|
23
|
+
/**
|
|
24
|
+
* Inserts a `pending` instance, due at `now`, unless one with the same id exists. Returns the
|
|
25
|
+
* stored instance: the new one (`created: true`) or the existing one, unchanged
|
|
26
|
+
* (`created: false`). Must be atomic per id: of two concurrent calls, one creates.
|
|
27
|
+
*/
|
|
28
|
+
create(instance: NewWorkflowInstance): Promise<{
|
|
29
|
+
instance: WorkflowInstance;
|
|
30
|
+
created: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Optional: `create()` through the application's transaction, for
|
|
34
|
+
* `start(workflow, input, { transaction })`. `transaction` is what the application's ORM
|
|
35
|
+
* hands its transaction callback (a Drizzle `tx`, a TypeORM `EntityManager`...). Write only
|
|
36
|
+
* through it, so the instance commits or rolls back with the application's rows, and never
|
|
37
|
+
* catch a database error inside it (on PostgreSQL that aborts the transaction: use
|
|
38
|
+
* insert-or-ignore). Without this method, `start()` with a transaction throws.
|
|
39
|
+
*/
|
|
40
|
+
createInTransaction?(transaction: unknown, instance: NewWorkflowInstance): Promise<{
|
|
41
|
+
instance: WorkflowInstance;
|
|
42
|
+
created: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* The instance with the waits its last suspension registered (`[]` when none), and its
|
|
46
|
+
* journal when `options.journal` is set, or `null` for an unknown id.
|
|
47
|
+
*/
|
|
48
|
+
get(id: string, options?: {
|
|
49
|
+
journal?: boolean;
|
|
50
|
+
}): Promise<WorkflowInstanceDetails | null>;
|
|
51
|
+
/** Instances matching every given filter, ordered by `createdAt`, then `id`; a page of them. */
|
|
52
|
+
list(query: WorkflowListQuery): Promise<WorkflowInstance[]>;
|
|
53
|
+
/**
|
|
54
|
+
* A cancel: sets `cancelRequested` and `cancelReason` on a `pending`, `running` or
|
|
55
|
+
* `suspended` instance whose `cancelRequested` is still false.
|
|
56
|
+
*
|
|
57
|
+
* A terminate (`request.terminate`): sets `cancelRequested`, `terminateRequested` and
|
|
58
|
+
* `cancelReason` on a `pending`, `running`, `suspended` or `compensating` instance whose
|
|
59
|
+
* `terminateRequested` is still false, whether or not a cancel was requested before.
|
|
60
|
+
*
|
|
61
|
+
* Either way it makes the instance due now (`wakeAt` becomes `now` unless it is already due)
|
|
62
|
+
* and sets `updatedAt`, and returns whether it changed anything: `false` for an unknown id, a
|
|
63
|
+
* status it doesn't apply to, or a repeated request. One conditional update: of two
|
|
64
|
+
* concurrent requests of the same kind, one is accepted.
|
|
65
|
+
*/
|
|
66
|
+
requestCancel(id: string, request: WorkflowCancelRequest): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* An operator's retry of a finished instance (`WorkflowClient.retry()`), as one conditional
|
|
69
|
+
* write: if the instance holds no lease, its status is `expect.status` and its `runs` is
|
|
70
|
+
* `expect.runs` (nothing changed it since the engine read it), upsert `entries` as `write()`
|
|
71
|
+
* does, set `status`, `error` and, when given, `deadline`, make it due (`wakeAt = now`), set
|
|
72
|
+
* `updatedAt = now`, and return `true`. Otherwise change nothing and return `false`. One
|
|
73
|
+
* transaction that locks the instance row: of two concurrent retries, one is accepted.
|
|
74
|
+
*/
|
|
75
|
+
reopen(id: string, reopen: WorkflowReopen): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Deletes the instance, with its journal and waits, if its status is one of `statuses`, and
|
|
78
|
+
* returns whether it did. One statement: a worker that holds its lease finds it gone at its
|
|
79
|
+
* next write or renewal.
|
|
80
|
+
*/
|
|
81
|
+
delete(id: string, statuses: WorkflowStatus[]): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Records a signal under the next signal id and makes due now (`wakeAt = now` unless already
|
|
84
|
+
* due, and `updatedAt`) every `suspended` instance with a wait for the same name and exactly
|
|
85
|
+
* the same key. Returns the id and how many instances it woke.
|
|
86
|
+
*
|
|
87
|
+
* Signals must be serialized with each other and with `write()`s that register waits: take
|
|
88
|
+
* an exclusive lock before choosing the id and hold it until commit (the `write()` side takes
|
|
89
|
+
* it shared). That gives the two guarantees the engine relies on: signal ids become visible
|
|
90
|
+
* in id order, and a signal and a suspension of the same instance never interleave.
|
|
91
|
+
*
|
|
92
|
+
* With a `dedupeId`, a signal stored earlier with the same name and `dedupeId` makes the call
|
|
93
|
+
* a no-op: write and wake nothing, and return that signal's id and key with `created: false`.
|
|
94
|
+
* Enforce it with a unique constraint on `(name, dedupeId)` and insert-or-ignore, never a read
|
|
95
|
+
* followed by a write, and never by catching the duplicate-key error (in the application's
|
|
96
|
+
* transaction, on PostgreSQL, that aborts it). Signals without a `dedupeId` never conflict.
|
|
97
|
+
*/
|
|
98
|
+
signal(signal: NewWorkflowSignal): Promise<WorkflowSignalResult>;
|
|
99
|
+
/**
|
|
100
|
+
* Optional: `signal()` through the application's transaction, for
|
|
101
|
+
* `signal(signal, payload, { transaction })`, under the same rules as `createInTransaction()`.
|
|
102
|
+
* The lock is then held until the application's transaction ends. On PostgreSQL the
|
|
103
|
+
* transaction must be READ COMMITTED, or the wake-up can miss waits committed after its
|
|
104
|
+
* snapshot: refuse other isolation levels.
|
|
105
|
+
*/
|
|
106
|
+
signalInTransaction?(transaction: unknown, signal: NewWorkflowSignal): Promise<WorkflowSignalResult>;
|
|
107
|
+
/** Signals named `name` with exactly `key` and `afterId < id <= upToId`, by id. */
|
|
108
|
+
signals(query: WorkflowSignalQuery): Promise<WorkflowSignalRecord[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Deletes, oldest `updatedAt` first, up to `limit` instances whose status is one of
|
|
111
|
+
* `statuses` (finished ones only) and whose `updatedAt` is below `before`, with their
|
|
112
|
+
* journals and waits. Re-check the status and `updatedAt` on the rows it deletes (in the
|
|
113
|
+
* `DELETE`'s own `WHERE`, not only in a subquery), so an instance that an operator reopened in
|
|
114
|
+
* the meantime stays.
|
|
115
|
+
*
|
|
116
|
+
* Also deletes, lowest id first, up to `limit` signals no instance can take any more: an
|
|
117
|
+
* instance only takes signals with an id above its `signalCursor`, and a new one starts at the
|
|
118
|
+
* last signal id. So a signal can go when its id is at or below the lowest `signalCursor` of
|
|
119
|
+
* the unfinished instances (`pending`, `running`, `suspended`, `compensating`), its
|
|
120
|
+
* `createdAt` is below `before` (its `dedupeId` keeps deduplicating until then), and it isn't
|
|
121
|
+
* the newest signal (which keeps the last signal id from going back).
|
|
122
|
+
*
|
|
123
|
+
* And deletes up to `limit` rate-limit windows (see `claim()`) that ended before `before`: a window that
|
|
124
|
+
* ended is the same as none. Re-check `windowEnd` on the rows it deletes, as for instances: a claim may
|
|
125
|
+
* have started a new window in one meanwhile; and pass over the windows a claim is locking (`SKIP
|
|
126
|
+
* LOCKED`) rather than wait for them, since claims lock them in another order. Returns how many
|
|
127
|
+
* instances, signals and windows it deleted. Each delete is one statement; nothing else is atomic.
|
|
128
|
+
*/
|
|
129
|
+
purge(query: WorkflowPurgeQuery): Promise<WorkflowPurgeResult>;
|
|
130
|
+
/**
|
|
131
|
+
* Stores a schedule, as one conditional write, and returns it as stored, or `null` when the condition fails.
|
|
132
|
+
* With `expectRevision: null`, inserts it unless a schedule with the id exists (revision 1, `createdAt = now`);
|
|
133
|
+
* with a number, replaces every field of the one whose `revision` is that number (revision + 1). Either way
|
|
134
|
+
* sets `updatedAt = now`; with `releaseLease`, clears its lease, so the worker that holds it can't
|
|
135
|
+
* `writeSchedule()` any more. Of two concurrent saves expecting the same revision, one lands.
|
|
136
|
+
*/
|
|
137
|
+
saveSchedule(schedule: WorkflowScheduleSave): Promise<WorkflowScheduleRecord | null>;
|
|
138
|
+
/** The schedule, or `null` for an unknown id. */
|
|
139
|
+
getSchedule(id: string): Promise<WorkflowScheduleRecord | null>;
|
|
140
|
+
/** Schedules matching the filter, ordered by `id`; a page of them. */
|
|
141
|
+
listSchedules(query: WorkflowScheduleQuery): Promise<WorkflowScheduleRecord[]>;
|
|
142
|
+
/** Deletes the schedule (if its `revision` is still `revision`, when given), and returns whether it did. */
|
|
143
|
+
deleteSchedule(id: string, revision?: number): Promise<boolean>;
|
|
144
|
+
/**
|
|
145
|
+
* Leases up to `limit` due schedules to a worker, most overdue first (`wakeAt`, then `id`). Due: not paused,
|
|
146
|
+
* `wakeAt <= now`, no lease or an expired one (`leaseUntil < now`), and `workflow` in `workflows`. Each gets
|
|
147
|
+
* `leaseToken = token`, `leaseOwner = owner` and `leaseUntil`; nothing else changes (not `revision`). Two
|
|
148
|
+
* concurrent claims never return the same schedule: lock the candidates and skip those another claim holds.
|
|
149
|
+
*/
|
|
150
|
+
claimSchedules(request: WorkflowScheduleClaimRequest): Promise<WorkflowScheduleRecord[]>;
|
|
151
|
+
/**
|
|
152
|
+
* The lease holder's write: sets `state` and `wakeAt`, `revision + 1` and `updatedAt = now`, and with `release`
|
|
153
|
+
* clears the lease (`leaseToken`, `leaseUntil`), only while `token` is the schedule's lease token; otherwise
|
|
154
|
+
* writes nothing and returns `false`. One conditional update.
|
|
155
|
+
*/
|
|
156
|
+
writeSchedule(id: string, token: string, write: WorkflowScheduleWrite): Promise<boolean>;
|
|
157
|
+
/**
|
|
158
|
+
* Leases up to `limit` due instances to a worker: lowest `priority` first, then most overdue (`wakeAt`),
|
|
159
|
+
* then `createdAt`, then `id`. Due: status `pending`, `running`, `suspended` or `compensating`,
|
|
160
|
+
* `wakeAt <= now`, no lease or an expired one (`leaseUntil < now`), and a `workflow`/`version` pair in
|
|
161
|
+
* `workflows`. Each claimed instance gets `leaseToken = token`, `leaseOwner = owner`, `leaseUntil`,
|
|
162
|
+
* `runs + 1`, `updatedAt = now` and status `running` (a `compensating` one keeps its status).
|
|
163
|
+
*
|
|
164
|
+
* Two concurrent claims must never return the same instance: lock the candidates and skip
|
|
165
|
+
* those another claim holds (`FOR UPDATE SKIP LOCKED`). Also returns the last signal id,
|
|
166
|
+
* read after the claim, as the execution's signal cursor.
|
|
167
|
+
*
|
|
168
|
+
* `limits`: an instance holds a slot while its lease is live (`leaseUntil >= now`): at most `limit` of a
|
|
169
|
+
* listed workflow's instances (any version) hold one, and at most `perKey` of those with the same
|
|
170
|
+
* `concurrencyKey` (instances without one count only toward `limit`).
|
|
171
|
+
*
|
|
172
|
+
* `rateLimits`: each claim of an instance of a listed workflow starts an execution, which takes room in its
|
|
173
|
+
* workflow's windows: `limit` (the workflow's own) and `perKey` (one per `rateLimitKey`; instances without one
|
|
174
|
+
* count only toward `limit`). A window holds at most `max` claims; it starts with the first claim after the
|
|
175
|
+
* previous one ended (`windowEnd <= now`) and ends `duration` later.
|
|
176
|
+
*
|
|
177
|
+
* Under limits, take the candidates (in the order above) in stages, so one busy key never holds back the
|
|
178
|
+
* others: pass over every candidate with no room at all (a full slot count or window of its workflow or its
|
|
179
|
+
* key); then keep each concurrency key's first candidates, as many as it has free slots; of those, each rate
|
|
180
|
+
* key's first, as many as its window has room for; of those, each workflow's first, as many as both its free
|
|
181
|
+
* slots and its window allow; then the first `limit`. (With both kinds of keys, a claim can take fewer than
|
|
182
|
+
* fit: a key's candidate dropped by the other kind of key isn't replaced until the next claim.)
|
|
183
|
+
*
|
|
184
|
+
* Count and lease as one step, so two claims never take the last room: take a transaction-scoped lock per
|
|
185
|
+
* workflow with concurrency limits, in a fixed order, before counting; and lock each rate-limit window the
|
|
186
|
+
* picked instances take room in (an insert-or-lock of its row, in a fixed order, so there is always a row to
|
|
187
|
+
* lock), re-count under the lock, and lease only what still fits. Claims that take no room in a window never
|
|
188
|
+
* wait for its lock.
|
|
189
|
+
*/
|
|
190
|
+
claim(request: WorkflowClaimRequest): Promise<WorkflowClaim>;
|
|
191
|
+
/**
|
|
192
|
+
* Extends the lease to `leaseUntil` if `token` is still the instance's lease token, and
|
|
193
|
+
* returns its `cancelRequested` and `terminateRequested`, or `null` (changing nothing) if the
|
|
194
|
+
* lease is gone. One conditional update.
|
|
195
|
+
*/
|
|
196
|
+
renew(id: string, token: string, leaseUntil: number): Promise<{
|
|
197
|
+
cancelRequested: boolean;
|
|
198
|
+
terminateRequested: boolean;
|
|
199
|
+
} | null>;
|
|
200
|
+
/**
|
|
201
|
+
* Every write by the worker that holds the lease: journal entries, a status change, the
|
|
202
|
+
* outcome, and handing the instance back. All or nothing, in one transaction, and only while
|
|
203
|
+
* `token` is the instance's lease token: otherwise write nothing and return `false`.
|
|
204
|
+
* See `WorkflowWrite` for what each field does.
|
|
205
|
+
*/
|
|
206
|
+
write(id: string, token: string, write: WorkflowWrite): Promise<boolean>;
|
|
207
|
+
}
|
|
208
|
+
/** `WorkflowStore.get()`'s result, and `WorkflowClient.getStatus()`'s. */
|
|
209
|
+
export interface WorkflowInstanceDetails extends WorkflowInstance {
|
|
210
|
+
/** Signals the instance waits for, if suspended in `waitForSignal()`. */
|
|
211
|
+
waits: WorkflowWait[];
|
|
212
|
+
/** `WorkflowClient.getStatus(id, { children: true })` only: the instances it started with `ctx.startChild()`, oldest first. */
|
|
213
|
+
children?: WorkflowInstance[];
|
|
214
|
+
/** With `{ journal: true }`: every step, sleep, wait and compensation, in first-write order. */
|
|
215
|
+
journal?: WorkflowJournalEntry[];
|
|
216
|
+
}
|
|
217
|
+
export interface NewWorkflowInstance {
|
|
218
|
+
id: string;
|
|
219
|
+
workflow: string;
|
|
220
|
+
version: number;
|
|
221
|
+
input: unknown;
|
|
222
|
+
/** For a child (`ctx.startChild()`): its parent's id, stored as `parentId`. Absent: `null`. */
|
|
223
|
+
parentId?: string | null;
|
|
224
|
+
/** For a child: stored as `parentClose`. Absent: `null`. */
|
|
225
|
+
parentClose?: WorkflowParentClose | null;
|
|
226
|
+
/** Stored as `concurrencyKey`: the key its workflow's per-key limit counts it under. Absent: `null`. */
|
|
227
|
+
concurrencyKey?: string | null;
|
|
228
|
+
/** Stored as `rateLimitKey`: the key its workflow's per-key rate limit counts it under. Absent: `null`. */
|
|
229
|
+
rateLimitKey?: string | null;
|
|
230
|
+
/** Stored as `priority`: lower is claimed first. Absent: `0`, which goes before every other priority. */
|
|
231
|
+
priority?: number;
|
|
232
|
+
/** For an instance a schedule started: the schedule's id, stored as `scheduleId`. Absent: `null`. */
|
|
233
|
+
scheduleId?: string | null;
|
|
234
|
+
/** For an instance a schedule started: the occurrence's time, stored as `scheduledAt`. Absent: `null`. */
|
|
235
|
+
scheduledAt?: number | null;
|
|
236
|
+
/** The instance's `deadline`: when its run timeout passes, or `null`. Stored as is. */
|
|
237
|
+
deadline: number | null;
|
|
238
|
+
/**
|
|
239
|
+
* `createdAt`, `updatedAt` and `wakeAt`. The new instance also gets `signalCursor` = the
|
|
240
|
+
* last signal id (signals sent after it started can match its waits), `runs: 0`, no lease,
|
|
241
|
+
* no cancel or terminate request and `customStatus: null`.
|
|
242
|
+
*/
|
|
243
|
+
now: number;
|
|
244
|
+
}
|
|
245
|
+
/** What `WorkflowStore.requestCancel()` receives. */
|
|
246
|
+
export interface WorkflowCancelRequest {
|
|
247
|
+
reason: string | null;
|
|
248
|
+
now: number;
|
|
249
|
+
/** `WorkflowClient.terminate()`: stop without compensating. */
|
|
250
|
+
terminate: boolean;
|
|
251
|
+
}
|
|
252
|
+
/** What `WorkflowStore.list()` receives: validated and defaulted by the engine. */
|
|
253
|
+
export interface WorkflowListQuery {
|
|
254
|
+
/** Any of these statuses (never empty). */
|
|
255
|
+
status?: WorkflowStatus[];
|
|
256
|
+
workflow?: string;
|
|
257
|
+
version?: number;
|
|
258
|
+
/** Children of this instance. */
|
|
259
|
+
parentId?: string;
|
|
260
|
+
/** Instances this schedule started. */
|
|
261
|
+
scheduleId?: string;
|
|
262
|
+
limit: number;
|
|
263
|
+
offset: number;
|
|
264
|
+
}
|
|
265
|
+
export interface NewWorkflowSignal {
|
|
266
|
+
name: string;
|
|
267
|
+
/** Correlation key; `null` for a signal sent without one. Matches waits with exactly this key. */
|
|
268
|
+
key: string | null;
|
|
269
|
+
/**
|
|
270
|
+
* The sender's id for this signal (`WorkflowClient.signal()`'s `id` option), unique per signal
|
|
271
|
+
* name: a signal with the same name and `dedupeId` is stored once. `null`: never deduplicated.
|
|
272
|
+
*/
|
|
273
|
+
dedupeId: string | null;
|
|
274
|
+
payload: unknown;
|
|
275
|
+
now: number;
|
|
276
|
+
}
|
|
277
|
+
/** What `WorkflowStore.signal()` and `signalInTransaction()` return. */
|
|
278
|
+
export interface WorkflowSignalResult {
|
|
279
|
+
/** The stored signal's id: the new one, or with `created: false` the one stored earlier. */
|
|
280
|
+
id: number;
|
|
281
|
+
/** Instances made due; `0` with `created: false`. */
|
|
282
|
+
woken: number;
|
|
283
|
+
/** `false` when a signal with the same name and `dedupeId` was stored earlier. */
|
|
284
|
+
created: boolean;
|
|
285
|
+
/** The stored signal's key: the given one, or with `created: false` the earlier signal's. */
|
|
286
|
+
key: string | null;
|
|
287
|
+
}
|
|
288
|
+
/** What `WorkflowStore.reopen()` receives. */
|
|
289
|
+
export interface WorkflowReopen {
|
|
290
|
+
/** What the engine read: the write applies only while the instance is still like this. */
|
|
291
|
+
expect: {
|
|
292
|
+
status: WorkflowStatus;
|
|
293
|
+
runs: number;
|
|
294
|
+
};
|
|
295
|
+
/** `pending` to run again, `compensating` to retry its compensations. */
|
|
296
|
+
status: 'pending' | 'compensating';
|
|
297
|
+
error: SerializedWorkflowError | null;
|
|
298
|
+
/** A new `deadline`; `undefined` leaves it as it is. */
|
|
299
|
+
deadline?: number | null;
|
|
300
|
+
/** Journal entries to upsert by name, as in `WorkflowWrite.entries`. */
|
|
301
|
+
entries: WorkflowJournalEntry[];
|
|
302
|
+
now: number;
|
|
303
|
+
}
|
|
304
|
+
/** What `WorkflowStore.purge()` receives. */
|
|
305
|
+
export interface WorkflowPurgeQuery {
|
|
306
|
+
/** Finished statuses to delete (at least one). */
|
|
307
|
+
statuses: WorkflowStatus[];
|
|
308
|
+
/** Instances whose `updatedAt` (when they finished), and signals whose `createdAt`, is below this. */
|
|
309
|
+
before: number;
|
|
310
|
+
/** At least 1: the most instances, the most signals, and the most rate-limit windows one call deletes. */
|
|
311
|
+
limit: number;
|
|
312
|
+
}
|
|
313
|
+
/** How many instances, signals and rate-limit windows a purge deleted. */
|
|
314
|
+
export interface WorkflowPurgeResult {
|
|
315
|
+
instances: number;
|
|
316
|
+
signals: number;
|
|
317
|
+
/** Rate-limit windows that had ended. */
|
|
318
|
+
rateLimits: number;
|
|
319
|
+
}
|
|
320
|
+
export interface WorkflowSignalQuery {
|
|
321
|
+
name: string;
|
|
322
|
+
/** Exactly this key: `null` matches only signals sent without one. */
|
|
323
|
+
key: string | null;
|
|
324
|
+
afterId: number;
|
|
325
|
+
upToId: number;
|
|
326
|
+
}
|
|
327
|
+
/** A signal as stored. */
|
|
328
|
+
export interface WorkflowSignalRecord {
|
|
329
|
+
id: number;
|
|
330
|
+
name: string;
|
|
331
|
+
key: string | null;
|
|
332
|
+
payload: unknown;
|
|
333
|
+
createdAt: number;
|
|
334
|
+
}
|
|
335
|
+
export interface WorkflowClaimRequest {
|
|
336
|
+
/** The worker's id, for `leaseOwner`. */
|
|
337
|
+
owner: string;
|
|
338
|
+
/** A new token for this claim; every write under the lease presents it. */
|
|
339
|
+
token: string;
|
|
340
|
+
now: number;
|
|
341
|
+
leaseUntil: number;
|
|
342
|
+
/** At least 1. */
|
|
343
|
+
limit: number;
|
|
344
|
+
/** The workflow versions this worker runs (at least one). Leave the others to other workers. */
|
|
345
|
+
workflows: Array<{
|
|
346
|
+
name: string;
|
|
347
|
+
version: number;
|
|
348
|
+
}>;
|
|
349
|
+
/** Concurrency limits of some of those workflows (by name, every version). Absent or empty: none. */
|
|
350
|
+
limits?: WorkflowConcurrencyLimit[];
|
|
351
|
+
/** Rate limits of some of those workflows (by name, every version). Absent or empty: none. */
|
|
352
|
+
rateLimits?: WorkflowRateLimitRule[];
|
|
353
|
+
}
|
|
354
|
+
/** A workflow's rate limits, as a claim applies them: at most `max` claims per window of `duration` ms. */
|
|
355
|
+
export interface WorkflowRateLimitRule {
|
|
356
|
+
workflow: string;
|
|
357
|
+
/** The workflow's own window (every instance counts), or `null`. */
|
|
358
|
+
limit: WorkflowRateWindow | null;
|
|
359
|
+
/** One window per non-null `rateLimitKey`, or `null`. */
|
|
360
|
+
perKey: WorkflowRateWindow | null;
|
|
361
|
+
}
|
|
362
|
+
/** A workflow's concurrency limits, as a claim applies them. */
|
|
363
|
+
export interface WorkflowConcurrencyLimit {
|
|
364
|
+
workflow: string;
|
|
365
|
+
/** At most this many of its instances hold a slot, or `null`. */
|
|
366
|
+
limit: number | null;
|
|
367
|
+
/** At most this many of its instances with the same non-null `concurrencyKey` hold a slot, or `null`. */
|
|
368
|
+
perKey: number | null;
|
|
369
|
+
}
|
|
370
|
+
export interface WorkflowClaim {
|
|
371
|
+
/** The claimed instances, as updated by the claim. */
|
|
372
|
+
instances: WorkflowInstance[];
|
|
373
|
+
/** The last signal id: the highest id `signal()` has returned, or 0. */
|
|
374
|
+
lastSignalId: number;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* One write by the lease holder (`WorkflowStore.write()`). In one transaction:
|
|
378
|
+
*
|
|
379
|
+
* 1. With a `signal`, take the signal lock (see `WorkflowStore.signal()`) exclusively; else, if
|
|
380
|
+
* `release.waits` is not empty, in shared mode. Before anything else.
|
|
381
|
+
* 2. Lock the instance row if its lease token is `token`; if not, write nothing, return `false`.
|
|
382
|
+
* 3. Upsert `entries` by name.
|
|
383
|
+
* 4. Set `status`, `output`, `error` and `customStatus` when present (`undefined` leaves them as
|
|
384
|
+
* they are), and record `signal` when present.
|
|
385
|
+
* 5. With `release`: replace the instance's waits with `release.waits`, clear `leaseToken` and
|
|
386
|
+
* `leaseUntil` (keep `leaseOwner`), and set `wakeAt`: `now` if a signal with an id above
|
|
387
|
+
* `release.signalCursor` matches one of the new waits (name and exact key), or if the
|
|
388
|
+
* instance has `cancelRequested` and the new status is `suspended`; otherwise
|
|
389
|
+
* `release.wakeAt`.
|
|
390
|
+
* 6. Set `updatedAt = now` when anything besides the journal changed.
|
|
391
|
+
*/
|
|
392
|
+
export interface WorkflowWrite {
|
|
393
|
+
now: number;
|
|
394
|
+
/**
|
|
395
|
+
* Journal entries, each with a name unique within the write. An entry replaces the stored
|
|
396
|
+
* entry with the same name entirely; a new name goes after every name the instance already
|
|
397
|
+
* has, in array order. `get(id, { journal: true })` returns them in that first-write order,
|
|
398
|
+
* with `null` and `undefined` fields kept apart (store each entry as one JSON document).
|
|
399
|
+
*/
|
|
400
|
+
entries: WorkflowJournalEntry[];
|
|
401
|
+
status?: WorkflowStatus;
|
|
402
|
+
output?: unknown;
|
|
403
|
+
error?: SerializedWorkflowError | null;
|
|
404
|
+
/** The instance's `customStatus` (`null` clears it). */
|
|
405
|
+
customStatus?: unknown;
|
|
406
|
+
/**
|
|
407
|
+
* A signal to record as `signal()` records it (dedupe and wake-ups included), in this write's
|
|
408
|
+
* transaction and only if the write lands: how a child that ends tells its parent. The signal
|
|
409
|
+
* lock is then taken exclusively in step 1.
|
|
410
|
+
*/
|
|
411
|
+
signal?: NewWorkflowSignal;
|
|
412
|
+
/** Hand the instance back: parked (`suspended`), finished, or due again at once. */
|
|
413
|
+
release?: WorkflowRelease;
|
|
414
|
+
}
|
|
415
|
+
export interface WorkflowRelease {
|
|
416
|
+
/** When the instance is next due; `null`: only a signal or a cancel can wake it. */
|
|
417
|
+
wakeAt: number | null;
|
|
418
|
+
/** The waits to register, replacing the previous ones (`[]` clears them). */
|
|
419
|
+
waits: WorkflowWait[];
|
|
420
|
+
/** The execution's signal cursor (`WorkflowClaim.lastSignalId`): signals above it weren't seen. */
|
|
421
|
+
signalCursor: number;
|
|
422
|
+
}
|
|
423
|
+
/** A schedule as stored: `WorkflowStore.getSchedule()`, `listSchedules()` and `claimSchedules()` return it. */
|
|
424
|
+
export interface WorkflowScheduleRecord {
|
|
425
|
+
id: string;
|
|
426
|
+
/** The workflow it starts (claims filter on it). */
|
|
427
|
+
workflow: string;
|
|
428
|
+
/** Declared with `@Workflow(name, { schedules })`, rather than saved with `WorkflowSchedules.upsert()`. */
|
|
429
|
+
declared: boolean;
|
|
430
|
+
/** The engine's JSON: when it runs and how. Store it as it is. */
|
|
431
|
+
spec: unknown;
|
|
432
|
+
/** The input of the instances it starts (JSON: a string with a codec), or `null`. */
|
|
433
|
+
input: unknown;
|
|
434
|
+
paused: boolean;
|
|
435
|
+
/** When a worker next has something to do for it (`claimSchedules()` looks for `wakeAt <= now`), or `null`. */
|
|
436
|
+
wakeAt: number | null;
|
|
437
|
+
/** The engine's JSON bookkeeping. Store it as it is. */
|
|
438
|
+
state: unknown;
|
|
439
|
+
/** Bumped by every `saveSchedule()` and `writeSchedule()`. */
|
|
440
|
+
revision: number;
|
|
441
|
+
leaseOwner: string | null;
|
|
442
|
+
leaseUntil: number | null;
|
|
443
|
+
createdAt: number;
|
|
444
|
+
updatedAt: number;
|
|
445
|
+
}
|
|
446
|
+
/** What `WorkflowStore.saveSchedule()` receives. */
|
|
447
|
+
export interface WorkflowScheduleSave {
|
|
448
|
+
id: string;
|
|
449
|
+
workflow: string;
|
|
450
|
+
declared: boolean;
|
|
451
|
+
spec: unknown;
|
|
452
|
+
input: unknown;
|
|
453
|
+
paused: boolean;
|
|
454
|
+
wakeAt: number | null;
|
|
455
|
+
state: unknown;
|
|
456
|
+
/** `null`: insert it, if no schedule has the id. A number: replace the one whose `revision` is still this. */
|
|
457
|
+
expectRevision: number | null;
|
|
458
|
+
/** Clear the schedule's lease (a change to when it runs: the lease holder's work is outdated). */
|
|
459
|
+
releaseLease: boolean;
|
|
460
|
+
now: number;
|
|
461
|
+
}
|
|
462
|
+
/** What `WorkflowStore.listSchedules()` receives. */
|
|
463
|
+
export interface WorkflowScheduleQuery {
|
|
464
|
+
workflow?: string;
|
|
465
|
+
/** Only the declared ones (`true`), or only the others (`false`). */
|
|
466
|
+
declared?: boolean;
|
|
467
|
+
limit: number;
|
|
468
|
+
offset: number;
|
|
469
|
+
}
|
|
470
|
+
/** What `WorkflowStore.claimSchedules()` receives. */
|
|
471
|
+
export interface WorkflowScheduleClaimRequest {
|
|
472
|
+
owner: string;
|
|
473
|
+
token: string;
|
|
474
|
+
now: number;
|
|
475
|
+
leaseUntil: number;
|
|
476
|
+
/** At least 1. */
|
|
477
|
+
limit: number;
|
|
478
|
+
/** The workflow names this worker runs (at least one): it starts only their schedules' instances. */
|
|
479
|
+
workflows: string[];
|
|
480
|
+
}
|
|
481
|
+
/** What `WorkflowStore.writeSchedule()` receives. */
|
|
482
|
+
export interface WorkflowScheduleWrite {
|
|
483
|
+
now: number;
|
|
484
|
+
state: unknown;
|
|
485
|
+
wakeAt: number | null;
|
|
486
|
+
/** Hand the schedule back: clear its lease. */
|
|
487
|
+
release: boolean;
|
|
488
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ConfigurableModuleAsyncOptions, Type } from '@nestjs/common';
|
|
2
|
+
import type { WorkflowPayloadCodec } from './workflow-payload-codec.interface.js';
|
|
3
|
+
import type { WorkflowClock } from './workflow-clock.interface.js';
|
|
4
|
+
import type { Duration } from '../core/time/duration.js';
|
|
5
|
+
import type { WorkflowRetryOptions } from './workflow-retry-options.interface.js';
|
|
6
|
+
export interface WorkflowWorkerOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Run the background polling loop. Default true. With false the process can
|
|
9
|
+
* still start, signal and inspect workflows, and `WorkflowWorker.drain()`
|
|
10
|
+
* runs due instances on demand (tests, scripts, a separate worker process).
|
|
11
|
+
*/
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
/** Shown as `leaseOwner`. Default `hostname:pid:random`. */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Instances executed at once by this process. Default 10. */
|
|
16
|
+
concurrency?: number;
|
|
17
|
+
/** How often to look for due instances. Default 1s; local starts and signals wake the loop at once. */
|
|
18
|
+
pollInterval?: Duration;
|
|
19
|
+
/** How long a claim is valid without a heartbeat. Default 30s. */
|
|
20
|
+
leaseDuration?: Duration;
|
|
21
|
+
/** How often a running instance extends its lease. Default a third of `leaseDuration`. */
|
|
22
|
+
heartbeatInterval?: Duration;
|
|
23
|
+
/** How long shutdown waits for running executions before abandoning them. Default 10s. */
|
|
24
|
+
shutdownTimeout?: Duration;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* How large an instance's journal may grow. Every execution loads the whole journal and replays
|
|
28
|
+
* `run()` from the top, so a journal that keeps growing (a loop with a step or sleep per round)
|
|
29
|
+
* makes every execution slower. Counts are journal entries (steps, sleeps, waits, commits,
|
|
30
|
+
* `now()`/`random()`/`uuid()` values and compensations), bytes their size as JSON. `Infinity`
|
|
31
|
+
* turns a check off.
|
|
32
|
+
*/
|
|
33
|
+
export interface WorkflowJournalLimits {
|
|
34
|
+
/** Log a warning, and emit `journal-large`, once, when an instance's journal reaches this many entries. Default 1,000. */
|
|
35
|
+
warnEntries?: number;
|
|
36
|
+
/** The same, at this many bytes. Default 1,000,000 (1 MB). */
|
|
37
|
+
warnBytes?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Fail the instance before it records a new entry once its journal holds this many: it
|
|
40
|
+
* compensates and ends as `failed` with a `WorkflowJournalLimitError`. Compensations may still
|
|
41
|
+
* record theirs. Default 10,000.
|
|
42
|
+
*/
|
|
43
|
+
maxEntries?: number;
|
|
44
|
+
/** The same, at this many bytes. Default 10,000,000 (10 MB). */
|
|
45
|
+
maxBytes?: number;
|
|
46
|
+
}
|
|
47
|
+
/** What `forRoot()` takes, and what `forRootAsync()`'s factory returns. */
|
|
48
|
+
export interface WorkflowsModuleOptions {
|
|
49
|
+
/** Default: the system clock. Pass a `ManualWorkflowClock` in tests. */
|
|
50
|
+
clock?: WorkflowClock;
|
|
51
|
+
/** `false` is shorthand for `{ enabled: false }`. */
|
|
52
|
+
worker?: WorkflowWorkerOptions | false;
|
|
53
|
+
/** How large a journal may grow before a warning, and before the instance fails. */
|
|
54
|
+
journal?: WorkflowJournalLimits;
|
|
55
|
+
/** Default step retry: 3 attempts, 1s doubling up to 5m, no jitter. */
|
|
56
|
+
retry?: number | false | WorkflowRetryOptions;
|
|
57
|
+
/**
|
|
58
|
+
* With `NODE_ENV=production`, startup fails when no `WorkflowStore` is registered, because
|
|
59
|
+
* the in-memory default loses every running instance on restart. Set this to run on it
|
|
60
|
+
* anyway (a demo, a single-process tool whose workflows may be lost).
|
|
61
|
+
*/
|
|
62
|
+
allowInMemoryStorage?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Encodes what workflows store for you before the store sees it (inputs, outputs, step results, checkpoints,
|
|
65
|
+
* signal payloads, custom statuses, schedule inputs, errors' messages), to encrypt it: an `AesGcmPayloadCodec`,
|
|
66
|
+
* your own `WorkflowPayloadCodec`, or its class (Nest creates it, with dependencies from this module's
|
|
67
|
+
* `imports` and from global modules). Several: the first encodes, and each keeps decoding what it encoded, so
|
|
68
|
+
* list the one you replace after the new one. Payloads stored before a codec was set are read as they are.
|
|
69
|
+
*/
|
|
70
|
+
codec?: WorkflowPayloadCodec | Type<WorkflowPayloadCodec> | Array<WorkflowPayloadCodec | Type<WorkflowPayloadCodec>>;
|
|
71
|
+
}
|
|
72
|
+
/** Implemented by a `forRootAsync({ useClass })` class: Nest calls `createWorkflowsOptions()`. */
|
|
73
|
+
export interface WorkflowsOptionsFactory {
|
|
74
|
+
createWorkflowsOptions(): WorkflowsModuleOptions | Promise<WorkflowsModuleOptions>;
|
|
75
|
+
}
|
|
76
|
+
/** What `forRootAsync()` takes: `imports`, `inject` and `useFactory`, `useClass` or `useExisting`, and `isGlobal`. */
|
|
77
|
+
export type WorkflowsModuleAsyncOptions = ConfigurableModuleAsyncOptions<WorkflowsModuleOptions, 'createWorkflowsOptions'> & {
|
|
78
|
+
/** Default true. */
|
|
79
|
+
isGlobal?: boolean;
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { MySqlWorkflowStore } from './mysql-workflow.store.js';
|
|
2
|
+
export { WorkflowSchemaError } from '../sql/workflow-schema.error.js';
|
|
3
|
+
export type { MySqlWorkflowStoreOptions } from './interfaces/index.js';
|
|
4
|
+
export { fromDrizzle, fromKysely, fromMysql2, fromPrisma, fromTypeOrm, type PrismaExecutorOptions } from '@nestjs/store-kit/mysql';
|
|
5
|
+
export type { SqlExecutor, SqlIsolationLevel, SqlTransaction, SqlTransactionOptions } from '@nestjs/store-kit/mysql';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The `@nestjs/workflows/mysql` entry: the first-party MySQL store. Nothing here imports a driver or an ORM: the
|
|
2
|
+
// executors (from @nestjs/store-kit, which every first-party store builds on) reach the client the application
|
|
3
|
+
// passes them.
|
|
4
|
+
// The store, and the error it fails startup with while its schema is behind (the same class as /postgres's)
|
|
5
|
+
export { MySqlWorkflowStore } from './mysql-workflow.store.js';
|
|
6
|
+
export { WorkflowSchemaError } from '../sql/workflow-schema.error.js';
|
|
7
|
+
// Executors: the store's SQL through the application's pool or ORM, and its transactions
|
|
8
|
+
export { fromDrizzle, fromKysely, fromMysql2, fromPrisma, fromTypeOrm } from '@nestjs/store-kit/mysql';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './mysql-workflow-store-options.interface.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './mysql-workflow-store-options.interface.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SqlExecutor } from '@nestjs/store-kit/mysql';
|
|
2
|
+
/** What `new MySqlWorkflowStore(options, storage)` takes. */
|
|
3
|
+
export interface MySqlWorkflowStoreOptions {
|
|
4
|
+
/**
|
|
5
|
+
* How the store reaches the database: `fromMysql2(pool)`, `fromDrizzle(db)`, `fromTypeOrm(dataSource)`,
|
|
6
|
+
* `fromPrisma(prisma)` or `fromKysely(db)` from `@nestjs/workflows/mysql`. The store keeps its tables in the database
|
|
7
|
+
* the pool or ORM connects to; its own transactions run on it, and `start()` and `signal()` with `{ transaction }`
|
|
8
|
+
* take that client's transaction object.
|
|
9
|
+
*/
|
|
10
|
+
executor: SqlExecutor<'mysql'>;
|
|
11
|
+
/**
|
|
12
|
+
* The name the store's tables start with, in the connection's database: `<schema>_<table>` (`nest_workflows`:
|
|
13
|
+
* `nest_workflows_instances`, `nest_workflows_signals`...). Keep it for the store alone. Lowercase letters, digits
|
|
14
|
+
* and underscores, not starting with a digit, at most 40 characters. Default: `'nest_workflows'`.
|
|
15
|
+
*/
|
|
16
|
+
schema?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Apply the store's pending migrations at startup, one statement at a time under a lock (`GET_LOCK()`), so processes
|
|
19
|
+
* that start together migrate once, and a run that stopped halfway resumes where it stopped. With `false`, startup
|
|
20
|
+
* fails with a `WorkflowSchemaError` while the schema is behind this version of the package: apply them with
|
|
21
|
+
* `npx nest-workflows migrate`, or with your own migration tool (`MySqlWorkflowStore.migrationSql()` or
|
|
22
|
+
* `migrationStatements()`). Default: `true`, except when `NODE_ENV` is `production`.
|
|
23
|
+
*/
|
|
24
|
+
migrate?: boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
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 declare 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
|
+
readonly instanceId: 255;
|
|
10
|
+
/** A schedule's id: its runs' ids (`<id>@<ISO time>`) add 25 characters and must fit an instance id. */
|
|
11
|
+
readonly scheduleId: 230;
|
|
12
|
+
/** A workflow's name. */
|
|
13
|
+
readonly workflow: 255;
|
|
14
|
+
/** An instance's concurrency key. */
|
|
15
|
+
readonly concurrencyKey: 255;
|
|
16
|
+
/** An instance's rate-limit key. */
|
|
17
|
+
readonly rateLimitKey: 255;
|
|
18
|
+
/** A signal's name, and a wait's. */
|
|
19
|
+
readonly signal: 255;
|
|
20
|
+
/** A signal's key, and a wait's. */
|
|
21
|
+
readonly signalKey: 255;
|
|
22
|
+
/** A signal's id (`signal(..., { id })`), which deduplicates it. */
|
|
23
|
+
readonly dedupeId: 255;
|
|
24
|
+
/** A journal entry's name: a step's, a wait's, or the engine's (`$child:<child id>`, `$compensate:<step>`...). */
|
|
25
|
+
readonly journalName: 512;
|
|
26
|
+
/** A lease's token (the engine's: a UUID). */
|
|
27
|
+
readonly leaseToken: 255;
|
|
28
|
+
};
|
|
29
|
+
/** Throws a `RangeError` that names `what` when `value` is longer than `max` characters (code points, as MySQL counts). */
|
|
30
|
+
export declare function checkKey(what: string, value: string | null | undefined, max: number): void;
|