@alexify/migronaut 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +8 -1
- package/bullmq.d.ts +35 -2
- package/index.d.ts +257 -3
- package/package.json +2 -1
- package/src/bullmq/background-processor.js +77 -5
- package/src/bullmq/processor.js +221 -8
- package/src/bullmq/service.js +4 -0
- package/src/core/background-dry-run.js +9 -0
- package/src/core/background-engine.js +47 -16
- package/src/core/background-kit.js +18 -11
- package/src/core/background-watch.js +5 -0
- package/src/core/background.js +6 -0
- package/src/core/migration-logger.js +279 -0
- package/src/core/migrator.js +129 -16
- package/src/core/options.js +20 -0
- package/src/core/run-recorder.js +6 -1
- package/src/core/runner.js +33 -7
- package/src/utils/job-ref.js +44 -0
- package/src/utils/redact.js +140 -3
- package/src/utils/telemetry.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,89 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
Release headings carry the publish date (`## vX.Y.Z — YYYY-MM-DD`).
|
|
5
5
|
|
|
6
|
+
## v2.4.0 — 2026-10-07
|
|
7
|
+
|
|
8
|
+
Migration logs for the application's users. Additive: a migration that never touches the new
|
|
9
|
+
context fields, and a kit with no `migration:log` listener, behave as before. Everything new is
|
|
10
|
+
experimental — its shape may still change in a minor release (named here).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`ctx.logger` in every migration** — the kit's logger with the run's correlation bound into the
|
|
15
|
+
fields of every line: `runId`, `migration`, `direction`, `batch`, `attempt`, and `jobId` /
|
|
16
|
+
`groupId` when a queue job runs it. Pino's `(fields, msg)` order is accepted too.
|
|
17
|
+
- **`ctx.run`** — that correlation as a frozen object: `{ id, direction, migration?, batch?,
|
|
18
|
+
attempt?, jobId?, groupId?, requestedBy?, reason? }`. `attempt` is 2 or more when the driver
|
|
19
|
+
retried the transaction and the body runs again (each attempt gets a context of its own).
|
|
20
|
+
- **The `migration:log` event** — a `ctx.logger` call whose fields hold `userland: true` is also
|
|
21
|
+
emitted, for the application to store and show its users; calls without the marker emit
|
|
22
|
+
nothing. The payload is `{ kind, runId, …correlation, level, msg, data, at, seq, truncated? }`:
|
|
23
|
+
`data` a bounded, redacted copy of the fields (8 levels, 1000 entries, 4096-character strings;
|
|
24
|
+
an `Error` as `{ name, message, code?, codeName? }` with the values a server error quotes masked,
|
|
25
|
+
as in `msg`; BSON values and binary data up to 4096 bytes kept as they are),
|
|
26
|
+
`seq` increasing within a run, `at` a `Date` (TTL-ready). It fires whatever the logger's level,
|
|
27
|
+
and with `logger: null`. Migronaut stores none of it — the
|
|
28
|
+
[Migration Logs](https://migronaut.vercel.app/guide/migration-logs) guide has the recipe.
|
|
29
|
+
- **Hooks get it too** — `beforeAll`/`afterAll` the run's logger and `ctx.run` (no migration, no
|
|
30
|
+
attempt), `beforeEach`/`afterEach`/`onError` the migration's.
|
|
31
|
+
- **`job: { id, groupId? }` on `up`, `down` and `redo`** — the queue job a run works for, bound into
|
|
32
|
+
`ctx.run`, its lines (the kit's own included) and its events; and `job: { id }` on
|
|
33
|
+
`runBackgroundSlice`.
|
|
34
|
+
- **Queue adapter** — the processor passes each job's id and group to its run, and writes the
|
|
35
|
+
migration's `userland: true` lines into the job's log as one-line `✎ …` rows (matched by job and
|
|
36
|
+
run id, so a timed-out body never writes into the next job's log — nor into a later run of the
|
|
37
|
+
same job). A background lane does the same for its slice, into the lane job's log. The new
|
|
38
|
+
`userlandLogRows` option (`createMigrationQueue`, both processors; default 1000) caps the rows one
|
|
39
|
+
job's log takes — the rest are counted in one closing row.
|
|
40
|
+
- **Background migrations** — `ctx.logger` is bound to `ctx.background`, which gains the lane's
|
|
41
|
+
`runId`, its `jobId` (and, for a lane an `up` drives with `backgroundInline`, the run's job and
|
|
42
|
+
`groupId` — its lines land in that migration job's log) and the transaction `attempt`; a
|
|
43
|
+
`userland: true` call emits `migration:log` with `kind: 'background'`. A dry run marks its lines
|
|
44
|
+
`dryRun: true` and emits nothing.
|
|
45
|
+
- **Retried transactions, visible** — when the driver retried a migration's transaction and its
|
|
46
|
+
body ran again, `migration:success` / `migration:error` and the kit's `✔ Applied` / `✖ Error`
|
|
47
|
+
lines carry `attempts`, a failure's context too, and the `migronaut.migration` span always has
|
|
48
|
+
`migronaut.migration.attempts`. A run that names a `job` puts `migronaut.job.id` /
|
|
49
|
+
`migronaut.job.group_id` on its `migronaut.run` span.
|
|
50
|
+
- **A `ctx.logger` call that is dropped** (a field whose getter throws) **or cut to the event's
|
|
51
|
+
bounds** leaves one debug line per run — never one per call.
|
|
52
|
+
- **Types** — `MigrationRunInfo`, `JobRef`, `MigrationLogEvent` (`OrdinaryMigrationLogEvent |
|
|
53
|
+
BackgroundMigrationLogEvent`), `MigrationLogEventBase`, `MigrationLogLevel`,
|
|
54
|
+
`BackgroundRunInfo`, `MigrationLogger` (`ctx.logger`: `(msg, fields?)`, pino's `(fields, msg?)`
|
|
55
|
+
and an `Error` as the message — any `MigronautLogger` or pino instance is one); `logger?` and
|
|
56
|
+
`run?` on `MigrationContext` (optional, so a context built by hand still type-checks);
|
|
57
|
+
`attempts?` on `MigrationEvent`.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- `ctx.background` of a background migration is now frozen, and typed as `BackgroundRunInfo`.
|
|
62
|
+
**A type change on an experimental API:** its `generation` is now `number | undefined` (as it
|
|
63
|
+
always was at runtime for the live drift watcher), so strict TypeScript that reads it as a
|
|
64
|
+
`number` needs a check; `ctx.logger` is typed `MigrationLogger`, which every `MigronautLogger`
|
|
65
|
+
still fits.
|
|
66
|
+
- The kit's log lines of a run that names a `job` carry `jobId` / `groupId`.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- **An `async` event listener that rejects no longer crashes the process.** The kit is an
|
|
71
|
+
`EventEmitter` with `captureRejections`: a listener's rejected promise is logged at debug level,
|
|
72
|
+
like a listener that throws, instead of surfacing as an `unhandledRejection`.
|
|
73
|
+
|
|
74
|
+
### Documentation
|
|
75
|
+
|
|
76
|
+
- **How It Works** — a new guide page with architecture diagrams: the engine and its entry points,
|
|
77
|
+
the life of a run, the lock, where the state lives, which tool fits which change, background
|
|
78
|
+
migrations and what a run reports.
|
|
79
|
+
- **Diagrams across the guides** — Core Concepts (a migration's states, batches, a run), Transactions,
|
|
80
|
+
Declared Collections (a converge run), Document Versioning (the contract, optimistic concurrency,
|
|
81
|
+
the revision invariant, expand → background → contract), Background Migrations (statuses, one
|
|
82
|
+
lane's batches, passes, drift), the queue adapter (the line, a failure, background jobs) and
|
|
83
|
+
Migration Logs. They are Mermaid, drawn in the browser by a small theme component; `mermaid` is a
|
|
84
|
+
docs-only devDependency, loaded only by a page that has a diagram.
|
|
85
|
+
- **The home page** shows what 2.1 – 2.4 added — declared collections, Atlas Search, document
|
|
86
|
+
versioning, background migrations, OpenTelemetry, migration logs — with a diagram of how the parts
|
|
87
|
+
fit together.
|
|
88
|
+
|
|
6
89
|
## v2.3.0 — 2026-10-06
|
|
7
90
|
|
|
8
91
|
Document versioning and background migrations. Additive: nothing changes for a project that
|
package/README.md
CHANGED
|
@@ -66,6 +66,10 @@ change before it touches your database.
|
|
|
66
66
|
- **Zero config files required** — drive everything from env vars if you prefer.
|
|
67
67
|
- **Pino-friendly logging** — the `logger` option is pino-compatible; pass a pino instance directly
|
|
68
68
|
and migronaut logs through it (with a `component: 'migronaut'` child binding).
|
|
69
|
+
- **Migration logs for your users (experimental)** — every migration gets `ctx.logger`, bound to its
|
|
70
|
+
run (run id, migration, attempt, queue job), and `ctx.run`; a line marked `userland: true` is also
|
|
71
|
+
emitted as `migration:log` for your service to store and show — migronaut stores none of it
|
|
72
|
+
([guide](https://migronaut.vercel.app/guide/migration-logs)).
|
|
69
73
|
- **Your id format** — run ids and queue group ids are random UUIDs by default; pass
|
|
70
74
|
`generateId: ulid` (or cuid2, nanoid, UUIDv7 — any `() => string`) and every id migronaut mints
|
|
71
75
|
comes from your generator.
|
|
@@ -189,7 +193,7 @@ Full docs, guides, and the API reference live at
|
|
|
189
193
|
- [Core Concepts](https://migronaut.vercel.app/guide/concepts) — migrations, batches, the changelog, locking
|
|
190
194
|
- [Getting Started](https://migronaut.vercel.app/guide/getting-started) & [Tutorial](https://migronaut.vercel.app/guide/tutorial)
|
|
191
195
|
- [Configuration](https://migronaut.vercel.app/guide/configuration) · [Writing Migrations](https://migronaut.vercel.app/guide/writing-migrations) · [Transactions](https://migronaut.vercel.app/guide/transactions) · [Hooks](https://migronaut.vercel.app/guide/hooks)
|
|
192
|
-
- [Programmatic API](https://migronaut.vercel.app/guide/api) · [Migrations as a Queue (BullMQ)](https://migronaut.vercel.app/guide/bullmq) · [OpenTelemetry](https://migronaut.vercel.app/guide/opentelemetry) · [CI/CD](https://migronaut.vercel.app/guide/ci-cd) · [Troubleshooting](https://migronaut.vercel.app/guide/troubleshooting)
|
|
196
|
+
- [Programmatic API](https://migronaut.vercel.app/guide/api) · [Migrations as a Queue (BullMQ)](https://migronaut.vercel.app/guide/bullmq) · [Migration Logs](https://migronaut.vercel.app/guide/migration-logs) · [OpenTelemetry](https://migronaut.vercel.app/guide/opentelemetry) · [CI/CD](https://migronaut.vercel.app/guide/ci-cd) · [Troubleshooting](https://migronaut.vercel.app/guide/troubleshooting)
|
|
193
197
|
- Reference: [CLI Cheatsheet](https://migronaut.vercel.app/reference/cli) · [Error Codes](https://migronaut.vercel.app/reference/error-codes)
|
|
194
198
|
|
|
195
199
|
---
|
|
@@ -753,6 +757,9 @@ await mq.enqueueConverge(); // declared indexes and validators, as
|
|
|
753
757
|
failed one. Duplicate enqueues are deduplicated; an already-applied migration completes as
|
|
754
758
|
`skipped`.
|
|
755
759
|
- **Bring your own Worker** with `createMigrationProcessor()` (NestJS, BullMQ Pro).
|
|
760
|
+
- **What a migration logs for your users** (`logger.info(…, { userland: true })`) lands in the
|
|
761
|
+
job's log and reaches `mq.kit.on('migration:log', …)` with the job's id — keep it in a
|
|
762
|
+
collection of your own ([Migration Logs](https://migronaut.vercel.app/guide/migration-logs)).
|
|
756
763
|
|
|
757
764
|
→ **[Migrations as a Queue](https://migronaut.vercel.app/guide/bullmq)** ·
|
|
758
765
|
[runnable example service](examples/migration-service)
|
package/bullmq.d.ts
CHANGED
|
@@ -840,6 +840,13 @@ export interface CreateMigrationProcessorOptions {
|
|
|
840
840
|
jobOptions?: MigrationJobOptions;
|
|
841
841
|
/** What a job may ask for beyond the ordinary — see {@link MigrationJobPermissions} */
|
|
842
842
|
allow?: MigrationJobPermissions;
|
|
843
|
+
/**
|
|
844
|
+
* How many of a migration's `userland: true` lines one job's log takes (a
|
|
845
|
+
* lane: one slice) — the rest are counted in one closing row. `0` mirrors
|
|
846
|
+
* none; `migration:log` still carries every line. Default 1000
|
|
847
|
+
* @experimental New in 2.4
|
|
848
|
+
*/
|
|
849
|
+
userlandLogRows?: number;
|
|
843
850
|
/**
|
|
844
851
|
* The background queue: what an `up` (or `down`) job registers gets its
|
|
845
852
|
* coordinator there at once, and every `sync` tick heals it. @experimental
|
|
@@ -857,6 +864,11 @@ export interface CreateMigrationProcessorOptions {
|
|
|
857
864
|
* declares exactly three parameters, which is what makes BullMQ hand it the
|
|
858
865
|
* cancellation signal. Jobs are processed one at a time even when the Worker
|
|
859
866
|
* is configured for more.
|
|
867
|
+
*
|
|
868
|
+
* Each run is told which job it works for (the kit's `job` option), so a
|
|
869
|
+
* migration's `ctx.run` and every `migration:log` event carry `jobId` and
|
|
870
|
+
* `groupId`, and the migration's `userland: true` lines are written into the
|
|
871
|
+
* job's log next to the processor's own rows (`✎ …`).
|
|
860
872
|
*/
|
|
861
873
|
export interface MigrationProcessor {
|
|
862
874
|
(
|
|
@@ -864,7 +876,10 @@ export interface MigrationProcessor {
|
|
|
864
876
|
token?: string,
|
|
865
877
|
signal?: AbortSignal,
|
|
866
878
|
): Promise<MigrationJobResult | SyncJobResult | ConvergeJobResult>;
|
|
867
|
-
/**
|
|
879
|
+
/**
|
|
880
|
+
* The kit running the jobs — subscribe to its events for metrics, and to
|
|
881
|
+
* `migration:log` for what the migrations log for your users
|
|
882
|
+
*/
|
|
868
883
|
readonly kit: MigratorKit;
|
|
869
884
|
/**
|
|
870
885
|
* Stop taking the lock. Irreversible. A job that has not started its
|
|
@@ -911,10 +926,18 @@ export interface CreateBackgroundProcessorOptions {
|
|
|
911
926
|
stallMs?: number;
|
|
912
927
|
/** Failed slices in a row before a lane gives up (0–100). Default 8 */
|
|
913
928
|
maxLaneRetries?: number;
|
|
929
|
+
/**
|
|
930
|
+
* See {@link CreateMigrationProcessorOptions.userlandLogRows} — counted per slice
|
|
931
|
+
* @experimental New in 2.4
|
|
932
|
+
*/
|
|
933
|
+
userlandLogRows?: number;
|
|
914
934
|
}
|
|
915
935
|
|
|
916
936
|
/**
|
|
917
937
|
* The function a Worker on the background queue runs. Jobs run side by side.
|
|
938
|
+
* A lane's slice is told its job (`runBackgroundSlice`'s `job`), so its
|
|
939
|
+
* `migration:log` events carry `jobId` and its `userland: true` lines are
|
|
940
|
+
* written into the lane job's log (`✎ …`).
|
|
918
941
|
* @experimental New in 2.3
|
|
919
942
|
*/
|
|
920
943
|
export interface BackgroundProcessor {
|
|
@@ -1108,6 +1131,12 @@ export interface CreateMigrationQueueOptions<
|
|
|
1108
1131
|
* refuse fails at the call. Give every producer and worker the same policy.
|
|
1109
1132
|
*/
|
|
1110
1133
|
allow?: MigrationJobPermissions;
|
|
1134
|
+
/**
|
|
1135
|
+
* See {@link CreateMigrationProcessorOptions.userlandLogRows} — for the
|
|
1136
|
+
* migration jobs and, with `background`, the lanes
|
|
1137
|
+
* @experimental New in 2.4
|
|
1138
|
+
*/
|
|
1139
|
+
userlandLogRows?: number;
|
|
1111
1140
|
/**
|
|
1112
1141
|
* Background migrations on a queue of their own (`<queueName>-background`):
|
|
1113
1142
|
* a coordinator job each, with lanes as its children. `true` takes every
|
|
@@ -1164,7 +1193,11 @@ export class MigrationQueue<
|
|
|
1164
1193
|
> {
|
|
1165
1194
|
constructor(options: CreateMigrationQueueOptions<Q, W, E>);
|
|
1166
1195
|
|
|
1167
|
-
/**
|
|
1196
|
+
/**
|
|
1197
|
+
* The kit behind the queue — `kit.on('migration:success', …)` for metrics,
|
|
1198
|
+
* `kit.on('migration:log', …)` (in the worker's process) to keep what the
|
|
1199
|
+
* migrations log for your users
|
|
1200
|
+
*/
|
|
1168
1201
|
readonly kit: MigratorKit;
|
|
1169
1202
|
/** Your Queue, with its own type */
|
|
1170
1203
|
readonly queue: Q;
|
package/index.d.ts
CHANGED
|
@@ -35,6 +35,57 @@ export interface MigrationContext {
|
|
|
35
35
|
* migronaut cannot interrupt a running function by itself.
|
|
36
36
|
*/
|
|
37
37
|
signal?: AbortSignal;
|
|
38
|
+
/**
|
|
39
|
+
* The kit's logger, with this run's correlation ({@link run}) bound into the
|
|
40
|
+
* fields of every line. A call whose fields hold `userland: true` is also
|
|
41
|
+
* emitted as the `migration:log` event, for the application to store and show
|
|
42
|
+
* its users — migronaut stores none of it:
|
|
43
|
+
*
|
|
44
|
+
* ```js
|
|
45
|
+
* logger.info('batch done', { userland: true, processed: 1000 });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Always present when migronaut runs the migration; optional so a context
|
|
49
|
+
* built by hand (in a test) still type-checks.
|
|
50
|
+
* @experimental New in 2.4
|
|
51
|
+
*/
|
|
52
|
+
logger?: MigrationLogger;
|
|
53
|
+
/**
|
|
54
|
+
* Who this is: the run id, the migration, the direction, the transaction
|
|
55
|
+
* attempt and, when the caller named them, the queue job and the actor.
|
|
56
|
+
* Frozen; the same values `logger` binds and `migration:log` carries.
|
|
57
|
+
* Always present when migronaut runs the migration.
|
|
58
|
+
* @experimental New in 2.4
|
|
59
|
+
*/
|
|
60
|
+
run?: MigrationRunInfo;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The correlation of a run, as `ctx.run`. `migration`, `batch` and `attempt`
|
|
65
|
+
* describe one migration and are absent in `beforeAll`/`afterAll`.
|
|
66
|
+
* @experimental New in 2.4
|
|
67
|
+
*/
|
|
68
|
+
export interface MigrationRunInfo {
|
|
69
|
+
/** The run id — the same on the lock, the changelog record and every event of the run */
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly direction: 'up' | 'down';
|
|
72
|
+
/** The migration file */
|
|
73
|
+
readonly migration?: string;
|
|
74
|
+
/** The changelog batch (`up` only) */
|
|
75
|
+
readonly batch?: number;
|
|
76
|
+
/**
|
|
77
|
+
* 1 — or more when a transaction was retried and the body runs again. Not a
|
|
78
|
+
* queue retry: a migration job is never retried.
|
|
79
|
+
*/
|
|
80
|
+
readonly attempt?: number;
|
|
81
|
+
/** The queue job that runs this migration (set by the BullMQ adapter, or the `job` option) */
|
|
82
|
+
readonly jobId?: string;
|
|
83
|
+
/** The queue group the job belongs to */
|
|
84
|
+
readonly groupId?: string;
|
|
85
|
+
/** Who asked for the run (`requestedBy` option) */
|
|
86
|
+
readonly requestedBy?: string;
|
|
87
|
+
/** Why (`reason` option) */
|
|
88
|
+
readonly reason?: string;
|
|
38
89
|
}
|
|
39
90
|
|
|
40
91
|
/** Shape of an imported migration file module */
|
|
@@ -89,9 +140,18 @@ export type Body<T, N extends ShapeFieldNames = DefaultShapeFieldNames> = T exte
|
|
|
89
140
|
export interface BackgroundMigrationContext {
|
|
90
141
|
/** Aborted when the slice is stopping (lease lost, pause, shutdown) */
|
|
91
142
|
signal: AbortSignal;
|
|
92
|
-
|
|
143
|
+
/**
|
|
144
|
+
* The kit's logger with {@link background} bound into every line. Fields
|
|
145
|
+
* with `userland: true` also emit `migration:log` (`kind: 'background'`) —
|
|
146
|
+
* but `migrate` runs once per document, and again for a document a
|
|
147
|
+
* concurrent write moved: log from `migrateBatch` or a `step` rather than
|
|
148
|
+
* per document. In a dry run the lines say `dryRun: true` and nothing is
|
|
149
|
+
* emitted.
|
|
150
|
+
*/
|
|
151
|
+
logger: MigrationLogger;
|
|
93
152
|
direction: 'forward' | 'revert';
|
|
94
|
-
|
|
153
|
+
/** Where this runs — frozen */
|
|
154
|
+
background: BackgroundRunInfo;
|
|
95
155
|
session?: ClientSession;
|
|
96
156
|
db?: Db;
|
|
97
157
|
client?: MongoClient;
|
|
@@ -99,6 +159,43 @@ export interface BackgroundMigrationContext {
|
|
|
99
159
|
dryRun?: boolean;
|
|
100
160
|
}
|
|
101
161
|
|
|
162
|
+
/**
|
|
163
|
+
* `ctx.background`: which background migration, generation and partition —
|
|
164
|
+
* and the lane, its queue job and the transaction attempt, as its log lines
|
|
165
|
+
* and `migration:log` events carry them.
|
|
166
|
+
*/
|
|
167
|
+
export interface BackgroundRunInfo {
|
|
168
|
+
readonly name: string;
|
|
169
|
+
/** The plan generation — absent when the live drift watcher runs the transformation */
|
|
170
|
+
readonly generation?: number;
|
|
171
|
+
/** The partition (`''` for the drift watcher, `'dry-run'` in a dry run) */
|
|
172
|
+
readonly partition: string;
|
|
173
|
+
/**
|
|
174
|
+
* The lane's run id — its lease's owner, the runId of its `background:*`
|
|
175
|
+
* events. Absent in a dry run.
|
|
176
|
+
* @experimental New in 2.4
|
|
177
|
+
*/
|
|
178
|
+
readonly runId?: string;
|
|
179
|
+
/**
|
|
180
|
+
* The queue job working the lane — a background queue's lane job, or the
|
|
181
|
+
* migration job whose run drives it inline (`backgroundInline`)
|
|
182
|
+
* @experimental New in 2.4
|
|
183
|
+
*/
|
|
184
|
+
readonly jobId?: string;
|
|
185
|
+
/**
|
|
186
|
+
* The group of that migration job — only when a run drives it inline; a
|
|
187
|
+
* background queue's lanes have none
|
|
188
|
+
* @experimental New in 2.4
|
|
189
|
+
*/
|
|
190
|
+
readonly groupId?: string;
|
|
191
|
+
/**
|
|
192
|
+
* 1 — or more when a transactional batch or step runs again in a new
|
|
193
|
+
* transaction (a transient error, a conflict, a smaller batch)
|
|
194
|
+
* @experimental New in 2.4
|
|
195
|
+
*/
|
|
196
|
+
readonly attempt: number;
|
|
197
|
+
}
|
|
198
|
+
|
|
102
199
|
/** How a background migration splits its collection into partitions */
|
|
103
200
|
export interface BackgroundPartitionSettings {
|
|
104
201
|
/** Partitions per lane (default 4), so a slow partition does not hold the pass */
|
|
@@ -1086,9 +1183,41 @@ export interface MigronautLogger {
|
|
|
1086
1183
|
* `{ runId, migration, direction, batch, durationMs }` — so a machine-readable
|
|
1087
1184
|
* logger does not have to parse the human string. A plain `(msg) => …` logger
|
|
1088
1185
|
* remains valid: the extra argument is simply ignored.
|
|
1186
|
+
*
|
|
1187
|
+
* On a migration's `ctx.logger`, fields with `userland: true` also emit the
|
|
1188
|
+
* `migration:log` event (see {@link MigrationLogEvent}).
|
|
1089
1189
|
*/
|
|
1090
1190
|
export type LogMethod = (msg: string, fields?: Record<string, unknown>) => void;
|
|
1091
1191
|
|
|
1192
|
+
/**
|
|
1193
|
+
* `ctx.logger`: the kit's logger with the run's correlation bound into every
|
|
1194
|
+
* line. Each method takes `(msg, fields?)` — or pino's own `(fields, msg?)` —
|
|
1195
|
+
* and an `Error` as the message (its message, credentials masked). Fields with
|
|
1196
|
+
* `userland: true` also emit the `migration:log` event.
|
|
1197
|
+
*
|
|
1198
|
+
* Any {@link MigronautLogger} — or a pino instance — is one, so a context built
|
|
1199
|
+
* by hand in a test can pass the logger it has.
|
|
1200
|
+
* @experimental New in 2.4
|
|
1201
|
+
*/
|
|
1202
|
+
export interface MigrationLogger {
|
|
1203
|
+
debug(
|
|
1204
|
+
msgOrFields: string | Error | Record<string, unknown>,
|
|
1205
|
+
fieldsOrMsg?: Record<string, unknown> | string,
|
|
1206
|
+
): void;
|
|
1207
|
+
info(
|
|
1208
|
+
msgOrFields: string | Error | Record<string, unknown>,
|
|
1209
|
+
fieldsOrMsg?: Record<string, unknown> | string,
|
|
1210
|
+
): void;
|
|
1211
|
+
warn(
|
|
1212
|
+
msgOrFields: string | Error | Record<string, unknown>,
|
|
1213
|
+
fieldsOrMsg?: Record<string, unknown> | string,
|
|
1214
|
+
): void;
|
|
1215
|
+
error(
|
|
1216
|
+
msgOrFields: string | Error | Record<string, unknown>,
|
|
1217
|
+
fieldsOrMsg?: Record<string, unknown> | string,
|
|
1218
|
+
): void;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1092
1221
|
// ─── Telemetry ────────────────────────────────────────────────────────────────
|
|
1093
1222
|
|
|
1094
1223
|
/** A span or metric attribute value — the scalar subset migronaut sets */
|
|
@@ -1453,6 +1582,25 @@ export interface UpOptions {
|
|
|
1453
1582
|
requestedBy?: string;
|
|
1454
1583
|
/** Why (≤ 512 characters) — a ticket, a sentence; stamped like `requestedBy` */
|
|
1455
1584
|
reason?: string;
|
|
1585
|
+
/**
|
|
1586
|
+
* The queue job this run works for — bound into `ctx.run`, the run's log
|
|
1587
|
+
* lines and `migration:log`. The BullMQ adapter sets it; set it yourself
|
|
1588
|
+
* when you drive the kit from a queue of your own.
|
|
1589
|
+
* @experimental New in 2.4
|
|
1590
|
+
*/
|
|
1591
|
+
job?: JobRef;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* The queue job a run works for. Nothing is stored: the ids only correlate
|
|
1596
|
+
* what the run logs with the job a dashboard shows.
|
|
1597
|
+
* @experimental New in 2.4
|
|
1598
|
+
*/
|
|
1599
|
+
export interface JobRef {
|
|
1600
|
+
/** The job's id (≤ 1024 characters) */
|
|
1601
|
+
id: string;
|
|
1602
|
+
/** The group of jobs it was enqueued with (≤ 128 characters) */
|
|
1603
|
+
groupId?: string;
|
|
1456
1604
|
}
|
|
1457
1605
|
|
|
1458
1606
|
/** Options for {@link MigratorKit.down} */
|
|
@@ -1487,6 +1635,11 @@ export interface DownOptions {
|
|
|
1487
1635
|
requestedBy?: string;
|
|
1488
1636
|
/** Why (≤ 512 characters) — a ticket, a sentence; stamped as `revertReason` */
|
|
1489
1637
|
reason?: string;
|
|
1638
|
+
/**
|
|
1639
|
+
* The queue job this run works for — see {@link UpOptions.job}
|
|
1640
|
+
* @experimental New in 2.4
|
|
1641
|
+
*/
|
|
1642
|
+
job?: JobRef;
|
|
1490
1643
|
}
|
|
1491
1644
|
|
|
1492
1645
|
/** Payload common to every lifecycle event */
|
|
@@ -1500,6 +1653,12 @@ export interface MigrationEvent extends MigronautEventBase {
|
|
|
1500
1653
|
direction: 'up' | 'down';
|
|
1501
1654
|
batch?: number;
|
|
1502
1655
|
durationMs?: number;
|
|
1656
|
+
/**
|
|
1657
|
+
* How many times the body ran, when the driver retried its transaction (on
|
|
1658
|
+
* `migration:success` and `migration:error`; absent when it ran once)
|
|
1659
|
+
* @experimental New in 2.4
|
|
1660
|
+
*/
|
|
1661
|
+
attempts?: number;
|
|
1503
1662
|
/**
|
|
1504
1663
|
* Human-readable failure message (on `migration:error` only), with URI
|
|
1505
1664
|
* credentials already redacted — safe to ship to metrics/alerting as-is.
|
|
@@ -1621,6 +1780,82 @@ export interface ConvergeEndEvent extends MigronautEventBase {
|
|
|
1621
1780
|
error?: string;
|
|
1622
1781
|
}
|
|
1623
1782
|
|
|
1783
|
+
/** The level of a `ctx.logger` call */
|
|
1784
|
+
export type MigrationLogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* What every `migration:log` event carries.
|
|
1788
|
+
* @experimental New in 2.4
|
|
1789
|
+
*/
|
|
1790
|
+
export interface MigrationLogEventBase {
|
|
1791
|
+
level: MigrationLogLevel;
|
|
1792
|
+
/**
|
|
1793
|
+
* The message — URI credentials and the values a server error quotes (an
|
|
1794
|
+
* E11000's duplicate key) masked — at most 2048 characters
|
|
1795
|
+
*/
|
|
1796
|
+
msg: string;
|
|
1797
|
+
/**
|
|
1798
|
+
* The call's fields without the `userland` marker, as a document a driver can
|
|
1799
|
+
* store: a copy, its strings redacted, at most 8 levels and 1000 entries
|
|
1800
|
+
* deep, strings at most 4096 characters. Dates, regular expressions and BSON
|
|
1801
|
+
* values are kept as they are, binary data up to 4096 bytes too. An `Error`
|
|
1802
|
+
* becomes `{ name, message, code?, codeName? }`; a `Map` an object, a `Set`
|
|
1803
|
+
* an array; any other instance what `JSON.stringify` would see.
|
|
1804
|
+
*/
|
|
1805
|
+
data: Record<string, unknown>;
|
|
1806
|
+
/** When the call was made, by this process's clock — a TTL index can expire on it */
|
|
1807
|
+
at: Date;
|
|
1808
|
+
/** Increasing within one `runId`: orders the events of one millisecond */
|
|
1809
|
+
seq: number;
|
|
1810
|
+
/** Present when `msg` or `data` was cut to those bounds */
|
|
1811
|
+
truncated?: true;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
/**
|
|
1815
|
+
* A `ctx.logger` call with `userland: true` in an ordinary migration or its
|
|
1816
|
+
* hooks — `migration`, `batch` and `attempt` are absent in `beforeAll`/`afterAll`.
|
|
1817
|
+
* @experimental New in 2.4
|
|
1818
|
+
*/
|
|
1819
|
+
export interface OrdinaryMigrationLogEvent extends MigrationLogEventBase {
|
|
1820
|
+
kind: 'migration';
|
|
1821
|
+
runId: string;
|
|
1822
|
+
direction: 'up' | 'down';
|
|
1823
|
+
migration?: string;
|
|
1824
|
+
batch?: number;
|
|
1825
|
+
attempt?: number;
|
|
1826
|
+
jobId?: string;
|
|
1827
|
+
groupId?: string;
|
|
1828
|
+
requestedBy?: string;
|
|
1829
|
+
reason?: string;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
/**
|
|
1833
|
+
* A `ctx.logger` call with `userland: true` in a background migration's
|
|
1834
|
+
* `migrate`, `migrateBatch`, `step` (or their way back). `runId` is the lane's,
|
|
1835
|
+
* the one its `background:*` events carry.
|
|
1836
|
+
* @experimental New in 2.4
|
|
1837
|
+
*/
|
|
1838
|
+
export interface BackgroundMigrationLogEvent extends MigrationLogEventBase {
|
|
1839
|
+
kind: 'background';
|
|
1840
|
+
/** The lane's run id (a dry run, which has none, emits nothing) */
|
|
1841
|
+
runId: string;
|
|
1842
|
+
migration: string;
|
|
1843
|
+
direction: 'forward' | 'revert';
|
|
1844
|
+
generation?: number;
|
|
1845
|
+
partition: string;
|
|
1846
|
+
attempt: number;
|
|
1847
|
+
jobId?: string;
|
|
1848
|
+
/** Only when a run drives it inline — see {@link BackgroundRunInfo.groupId} */
|
|
1849
|
+
groupId?: string;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* The `migration:log` event — `kind` tells an ordinary migration's from a
|
|
1854
|
+
* background migration's.
|
|
1855
|
+
* @experimental New in 2.4
|
|
1856
|
+
*/
|
|
1857
|
+
export type MigrationLogEvent = OrdinaryMigrationLogEvent | BackgroundMigrationLogEvent;
|
|
1858
|
+
|
|
1624
1859
|
/**
|
|
1625
1860
|
* Lifecycle events emitted by {@link MigratorKit}. Subscribe to feed metrics or
|
|
1626
1861
|
* alerting without parsing log lines; a listener that throws is contained and
|
|
@@ -1634,6 +1869,12 @@ export interface MigronautEvents {
|
|
|
1634
1869
|
'migration:success': (event: MigrationEvent) => void;
|
|
1635
1870
|
'migration:skipped': (event: MigrationEvent) => void;
|
|
1636
1871
|
'migration:error': (event: MigrationEvent) => void;
|
|
1872
|
+
/**
|
|
1873
|
+
* A `ctx.logger` call marked `userland: true` — for the application to keep.
|
|
1874
|
+
* Logged lines without the marker emit nothing.
|
|
1875
|
+
* @experimental New in 2.4
|
|
1876
|
+
*/
|
|
1877
|
+
'migration:log': (event: MigrationLogEvent) => void;
|
|
1637
1878
|
'lock:acquired': (event: LockEvent) => void;
|
|
1638
1879
|
'lock:released': (event: LockEvent) => void;
|
|
1639
1880
|
'lock:lost': (event: LockEvent) => void;
|
|
@@ -1716,6 +1957,11 @@ export interface RedoOptions {
|
|
|
1716
1957
|
requestedBy?: string;
|
|
1717
1958
|
/** Why — stamped like `requestedBy` */
|
|
1718
1959
|
reason?: string;
|
|
1960
|
+
/**
|
|
1961
|
+
* The queue job this run works for — both halves carry it; see {@link UpOptions.job}
|
|
1962
|
+
* @experimental New in 2.4
|
|
1963
|
+
*/
|
|
1964
|
+
job?: JobRef;
|
|
1719
1965
|
}
|
|
1720
1966
|
|
|
1721
1967
|
/** Options for {@link MigratorKit.create} */
|
|
@@ -1952,7 +2198,15 @@ export class MigratorKit extends EventEmitter {
|
|
|
1952
2198
|
/** One slice of one lane: claim a partition and a slot, work it, release. @experimental */
|
|
1953
2199
|
runBackgroundSlice(
|
|
1954
2200
|
name: string,
|
|
1955
|
-
options?: {
|
|
2201
|
+
options?: {
|
|
2202
|
+
signal?: AbortSignal;
|
|
2203
|
+
sliceMs?: number;
|
|
2204
|
+
/**
|
|
2205
|
+
* The queue job working the lane — on its log lines and `migration:log` events
|
|
2206
|
+
* @experimental New in 2.4
|
|
2207
|
+
*/
|
|
2208
|
+
job?: Pick<JobRef, 'id'>;
|
|
2209
|
+
},
|
|
1956
2210
|
): Promise<BackgroundSliceResult>;
|
|
1957
2211
|
/**
|
|
1958
2212
|
* Drive a background migration from this process until it is done (or one
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexify/migronaut",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Elegant, fast, fully-typed, zero-dependency MongoDB migrations for Node.js — adopts an existing migrate-mongo changelog in one command",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Dolid <dolid.sasha@gmail.com>",
|
|
@@ -102,6 +102,7 @@
|
|
|
102
102
|
"c8": "^10.1.3",
|
|
103
103
|
"esbuild": "^0.28.1",
|
|
104
104
|
"ioredis": "^5.11.1",
|
|
105
|
+
"mermaid": "^11.17.2",
|
|
105
106
|
"mongodb": "^6.12.0",
|
|
106
107
|
"mongodb-memory-server": "10.4.3",
|
|
107
108
|
"mongoose": "^8.9.2",
|