@alexify/migronaut 2.2.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +41 -3
  3. package/bullmq.d.ts +484 -8
  4. package/index.d.ts +1264 -9
  5. package/migronaut.schema.json +93 -1
  6. package/package.json +9 -2
  7. package/src/bullmq/background-processor.js +541 -0
  8. package/src/bullmq/index.js +12 -0
  9. package/src/bullmq/jobs.js +254 -7
  10. package/src/bullmq/processor.js +348 -21
  11. package/src/bullmq/producer.js +185 -13
  12. package/src/bullmq/service.js +484 -45
  13. package/src/cli/commands/background.js +500 -0
  14. package/src/cli/commands/create.js +6 -0
  15. package/src/cli/exit-codes.js +6 -0
  16. package/src/cli/index.js +2 -0
  17. package/src/core/audit.js +11 -1
  18. package/src/core/background-audit.js +139 -0
  19. package/src/core/background-drift.js +126 -0
  20. package/src/core/background-dry-run.js +375 -0
  21. package/src/core/background-engine.js +849 -0
  22. package/src/core/background-kit.js +432 -0
  23. package/src/core/background-partition.js +298 -0
  24. package/src/core/background-runner.js +305 -0
  25. package/src/core/background-sandbox.js +701 -0
  26. package/src/core/background-shard.js +542 -0
  27. package/src/core/background-spec.js +597 -0
  28. package/src/core/background-store.js +951 -0
  29. package/src/core/background-throttle.js +269 -0
  30. package/src/core/background-watch-plan.js +164 -0
  31. package/src/core/background-watch-store.js +78 -0
  32. package/src/core/background-watch.js +610 -0
  33. package/src/core/background.js +1127 -0
  34. package/src/core/bson-peer.js +23 -0
  35. package/src/core/changelog.js +32 -0
  36. package/src/core/collections.js +78 -8
  37. package/src/core/config.js +102 -12
  38. package/src/core/converge-plan.js +86 -7
  39. package/src/core/converge.js +88 -0
  40. package/src/core/lock.js +48 -21
  41. package/src/core/migration-logger.js +279 -0
  42. package/src/core/migrator.js +1027 -22
  43. package/src/core/options.js +36 -0
  44. package/src/core/run-recorder.js +6 -1
  45. package/src/core/run.js +26 -12
  46. package/src/core/runner.js +34 -8
  47. package/src/core/server-info.js +9 -2
  48. package/src/core/shard-info.js +76 -0
  49. package/src/core/versioning-spec.js +181 -0
  50. package/src/errors/index.js +88 -0
  51. package/src/index.js +16 -0
  52. package/src/utils/error.js +11 -2
  53. package/src/utils/job-ref.js +44 -0
  54. package/src/utils/loader.js +77 -9
  55. package/src/utils/migration-name.js +33 -1
  56. package/src/utils/redact.js +140 -3
  57. package/src/utils/telemetry.js +110 -0
  58. package/src/utils/template.js +62 -1
  59. package/src/versioning/config.js +155 -0
  60. package/src/versioning/document.js +326 -0
  61. package/src/versioning/index.js +50 -0
  62. package/src/versioning/internal.js +279 -0
  63. package/src/versioning/mongoose.js +151 -0
  64. package/src/versioning/occ.js +318 -0
  65. package/src/versioning/registry.js +187 -0
  66. package/src/versioning/upcaster.js +213 -0
  67. package/versioning.d.ts +666 -0
  68. package/versioning.js +1 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,196 @@
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
+
89
+ ## v2.3.0 — 2026-10-06
90
+
91
+ Document versioning and background migrations. Additive: nothing changes for a project that
92
+ declares no `versioning` and writes no `background` file. Everything new is experimental — its
93
+ shape may still change in a minor release (named here).
94
+
95
+ ### Added
96
+
97
+ - **Document versioning** — a collection definition takes `versioning: { current, min?, field?,
98
+ revision?, revisionField?, index? }`: the shape version (`__v`) and the optimistic-concurrency
99
+ revision (`__rev`) as one contract. Converge folds it into the validator (the version an `int`
100
+ with a `minimum` of `min` and no `maximum`; the revision `int|long`; `moderate` when the
101
+ validator exists for versioning alone) and an index `{ __v: 1, _id: 1 }` — on a sharded
102
+ collection `{ __v: 1, …shard key, _id: 1 }`, the ordinary one kept with a row that says why.
103
+ Raising `min` over documents still below it is refused before any write (a `conflict` row, no
104
+ id named).
105
+ - **`@alexify/migronaut/versioning`** — a third entry point, with no engine behind it, for the
106
+ repository layer: `defineShapes` (`stamp`, `onInsert`, `stampUpsert`, `upcaster`, `plugin`),
107
+ `updateWithRevision` / `replaceWithRevision` / `findOneAndUpdateWithRevision`
108
+ (`RevisionConflictError` with `reason: 'conflict' | 'not-found' | 'unknown'`),
109
+ `retryOnConflict`, `bumpRevision`, the in-memory `upcaster` (`ShapeVersionError`),
110
+ `isVersion`, and `versioningPlugin` for Mongoose (its version key moved to `__rev`, optimistic
111
+ concurrency on). Per-version shape types without codegen: `AnyShape`, `CurrentShape`,
112
+ `ShapeAt`, `Stamped`, `Body`, `BackgroundMigrationFor`, a typed `defineShapes<Shapes>()(…)`
113
+ (`versioning.d.ts` needs TypeScript ≥ 5.0).
114
+ - **Background migrations** — a migration file with `export const background = { collection,
115
+ from, to, migrate | migrateBatch, revert?, … }` (or a free-form `step(ctx)`), which `up`
116
+ registers without running; the rewrite runs beside the migration line, never holding the
117
+ migration lock:
118
+ - **partitions and lanes**: the collection split into `_id` ranges from a sampled quantile per
119
+ BSON type — or, on a sharded collection, into runs of chunks per shard, with targeted reads
120
+ and writes, a shard-key guard (`shard-key-changed`) and `shardConcurrency`; leases are slots,
121
+ capped by a unique index at `maxParallel` across every process, every checkpoint fenced;
122
+ - **a coordinator** that plans passes, finalizes them and completes when nothing matches any
123
+ more (`maxPasses` against an old release that keeps writing); writes are `stampedDiff`s under
124
+ the optimistic filter, so an application write in between is never lost and untouched fields
125
+ keep their BSON types;
126
+ - **transactional batches** (`transaction: true`) with side writes through `ctx.session`;
127
+ - **pacing**: `pauseMs`, a `throttle` hook, replication lag and an adaptive (AIMD) controller;
128
+ - **`requires`** between background migrations (a DAG, unblocked as they complete) and from
129
+ ordinary migrations, which wait (`BackgroundPendingError`, or `onBackgroundPending: 'stop'`);
130
+ `backgroundInline` runs them inside `up`;
131
+ - **controls**: pause, resume, cancel, retry (`fromStart`), repin, unlock; **dry runs** on a
132
+ sample (`--validate` through the real write path in an always-aborted transaction) and of
133
+ `step` migrations in a sandbox (refusals exit 34);
134
+ - **drift after completion**: the drift watch (`verifyBackground`, every 10 minutes in each
135
+ runtime, `backgroundOnDrift: 'reopen' | 'report'`) and the **live drift watcher**
136
+ (`watchBackground`, change streams, one leader per collection, `backgroundDrift: 'poll' |
137
+ 'stream' | 'both'`);
138
+ - **three runtimes**: `migronaut background run`, `startBackgroundRunner()` in the application,
139
+ and the queue (below); `status()`, `background status --partitions`, an audit check, events
140
+ `background:*`, and OpenTelemetry spans `migronaut.background.slice` /
141
+ `migronaut.background.coordinate` with their metrics.
142
+ - **`migronaut background <action>`** — status, run, pause, resume, cancel, retry, repin,
143
+ dry-run, unlock, verify and watch; `create --background`.
144
+ - **Background migrations on the queue** — `createMigrationQueue({ background: true | {…} })`: a
145
+ queue of their own, a coordinator job per background migration with its lanes as children,
146
+ heals from MongoDB (worker start, every sync tick, every drift-watch tick) and a stall
147
+ takeover; `startBackgroundWorker()`, `enqueueBackground()`, the `background-verify` schedule,
148
+ `createBackgroundProcessor()`. An `up` plan stops before a migration that waits for a
149
+ background one.
150
+ - **Config**: `backgroundCollection`, `backgroundInline`, `backgroundOnDrift`, `backgroundDrift`,
151
+ `backgroundShardAware`, each with its `MIGRONAUT_*` variable.
152
+ - **Errors and exit codes**: `REVISION_CONFLICT` (29), `SHAPE_VERSION_UNSUPPORTED` (30),
153
+ `BACKGROUND_PENDING` (31), `BACKGROUND_FAILED` (32), `BACKGROUND_CONFLICT` (33),
154
+ `SANDBOX_REFUSED` (34).
155
+ - **`shapes.occ(name)`** — the revision guards and `bumpRevision` bound to a collection's field
156
+ names; `RevisionConflictContext` types an error's `context`.
157
+ - **`startBackgroundRunner().stop({ timeoutMs })`**, `RunnableBackground` (what
158
+ `runnableBackground()` now returns: live leases, last progress, coordinator), `previous` on a
159
+ background status (the registration a re-registration replaced), `plan.atLeast`.
160
+
161
+ ### Changed
162
+
163
+ For code written against 2.2 — the queue adapter's types and results, and two type-level details:
164
+
165
+ - **`SyncJobResult.held` means a failure again, only.** A tick whose next migration waits for a
166
+ background migration reports `waiting: { migration, waitsFor }` instead; while it still waits
167
+ for the same thing, later ticks say so without planning. A group that stops at such a migration
168
+ has `upToDate: false` (`MigrationGroup.waiting` says why).
169
+ - **`JOB_NAMES` has three more values** (`background`, `background-lane`, `background-verify`) and
170
+ `MigrationJobView.returnvalue` three more result types: an exhaustive `Record` over either needs
171
+ them.
172
+ - **`MigronautErrorCode` has six more members** (above): an exhaustive `switch` with a `never`
173
+ default needs a case for each — as with the codes 2.1 added.
174
+ - **`CollectionDefinition.indexes` and `searchIndexes` are `readonly` arrays**, so a definition
175
+ declared `as const` type-checks; code that pushes into a typed definition's array must copy it.
176
+ - **Background metric names** (experimental): `migronaut.background.throttled`,
177
+ `migronaut.background.drift.detected`, `migronaut.background.transaction.retried`.
178
+
179
+ ### Notes
180
+
181
+ - A typed `current` past the highest declared shape is a compile error. Written inline it reads
182
+ "Type 'number' is not assignable to type 'never'"; from an imported `as const` definition, it
183
+ names the two versions ("Type '3' is not assignable to type '2'").
184
+ - The shard-aware mode needs `clusterMonitor` (it reads `config.collections` and
185
+ `config.chunks`); without it, a sharded collection is partitioned by `_id`.
186
+ - Fail-closed limits: `maxDocumentErrors` is at most 1000 (the ids a state keeps); a revision
187
+ guard refuses an `_id` given as an operator; a background `filter` may not run server-side
188
+ JavaScript; `create --background`'s placeholder collection (`'TODO'`) is refused by `up`;
189
+ `watchBackground`, `enqueueBackground` and `createBackgroundProcessor` refuse options they do
190
+ not know.
191
+ - A lane stopped by its process (a shutdown, a deploy) is not a failed slice, and
192
+ `maxSliceFailures` counts failed slices in a row. `down` of a one-way background migration pauses
193
+ it and waits for its lanes before it decides; a `step` migration counts as having rewritten
194
+ documents once one step was checkpointed.
195
+
6
196
  ## v2.2.0 — 2026-10-05
7
197
 
8
198
  Atlas Search and Vector Search indexes in declared collections. Additive: a definition without
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.
@@ -75,6 +79,15 @@ change before it touches your database.
75
79
  - **Migrations as a queue (optional)** — `@alexify/migronaut/bullmq` runs each migration as its own
76
80
  BullMQ job, in order, so migronaut can be a migration service: trigger it over HTTP, on a
77
81
  schedule, or from a deploy hook that waits for the result.
82
+ - **Document versioning (experimental)** — declare a collection's shape version (`__v`) and
83
+ optimistic-concurrency revision (`__rev`) once; converge enforces them, and
84
+ `@alexify/migronaut/versioning` gives your repository layer `updateWithRevision`, typed
85
+ per-version shapes, an upcaster and a Mongoose plugin ([guide](https://migronaut.vercel.app/guide/versioning)).
86
+ - **Background migrations (experimental)** — long data rewrites (`export const background = {…}`)
87
+ that `up` only registers and that run beside the migration line: partitions and parallel lanes
88
+ across pods, checkpoints, pause/resume, transactional batches, shard-aware on sharded clusters,
89
+ dry runs, and a drift watcher that upgrades old-shape writes after completion — from the CLI,
90
+ inside your app, or on the queue ([guide](https://migronaut.vercel.app/guide/background-migrations)).
78
91
 
79
92
  ### How it compares to `migrate-mongo`
80
93
 
@@ -180,7 +193,7 @@ Full docs, guides, and the API reference live at
180
193
  - [Core Concepts](https://migronaut.vercel.app/guide/concepts) — migrations, batches, the changelog, locking
181
194
  - [Getting Started](https://migronaut.vercel.app/guide/getting-started) & [Tutorial](https://migronaut.vercel.app/guide/tutorial)
182
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)
183
- - [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)
184
197
  - Reference: [CLI Cheatsheet](https://migronaut.vercel.app/reference/cli) · [Error Codes](https://migronaut.vercel.app/reference/error-codes)
185
198
 
186
199
  ---
@@ -200,6 +213,7 @@ Every command accepts the global flags `--uri`, `--db`, `--dir`, `--config`, `--
200
213
  | `migronaut down [file]` | Roll back the last batch, a chosen batch, the last N steps, one file, or to `--to <file>` |
201
214
  | `migronaut redo [file]` | Roll back then re-apply (the last migration, or one file) |
202
215
  | `migronaut converge` | Bring declared collections — indexes, search indexes and validators — to their declared state |
216
+ | `migronaut background <action> [name]` | Background migrations: `status`, `run`, `pause`, `resume`, `cancel`, `retry`, `repin`, `dry-run`, `unlock`, `verify`, `watch` |
203
217
  | `migronaut status` | Print the full migration status table (`--check` to fail CI on pending) |
204
218
  | `migronaut list` | List migrations, filtered by status |
205
219
  | `migronaut dry-run <up\|down> [file]` | Preview a run without touching the database |
@@ -207,8 +221,8 @@ Every command accepts the global flags `--uri`, `--db`, `--dir`, `--config`, `--
207
221
  | `migronaut lock` | Show who currently holds the migration lock |
208
222
  | `migronaut unlock` | Force-release a stuck lock left behind by a crashed run |
209
223
 
210
- Most data commands (`up`, `down`, `redo`, `converge`, `status`, `list`, `dry-run`, `import`,
211
- `baseline`, `create`, `audit`, `lock`, `unlock`) accept **`--json`** for machine-readable output — see
224
+ Most data commands (`up`, `down`, `redo`, `converge`, `background`, `status`, `list`, `dry-run`,
225
+ `import`, `baseline`, `create`, `audit`, `lock`, `unlock`) accept **`--json`** for machine-readable output — see
212
226
  [CI & automation](#ci--automation).
213
227
 
214
228
  <details>
@@ -285,6 +299,15 @@ migronaut converge --yes # no confirmation (required for drops/rebuild
285
299
  migronaut converge --no-lock # skip the concurrency lock (local dev only)
286
300
  migronaut converge --json # machine-readable output (the converge result)
287
301
 
302
+ # background — background migrations (registered by up, rewritten in partitions)
303
+ migronaut background status # every background migration
304
+ migronaut background status --check # exit 32 if one failed, 31 if one is not completed
305
+ migronaut background run <name> # drive it from here (--concurrency N, --once, --all)
306
+ migronaut background pause <name> --wait # stop its lanes at the next batch
307
+ migronaut background dry-run <name> --validate # on a sample, in an always-aborted transaction
308
+ migronaut background verify # look for old-shape documents after completion
309
+ migronaut background watch # upgrade old-shape writes as they land (Ctrl-C stops)
310
+
288
311
  # status — full status table
289
312
  migronaut status # the full status table
290
313
  migronaut status --check # exit 2 if any migration is pending (CI gate)
@@ -734,6 +757,9 @@ await mq.enqueueConverge(); // declared indexes and validators, as
734
757
  failed one. Duplicate enqueues are deduplicated; an already-applied migration completes as
735
758
  `skipped`.
736
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)).
737
763
 
738
764
  → **[Migrations as a Queue](https://migronaut.vercel.app/guide/bullmq)** ·
739
765
  [runnable example service](examples/migration-service)
@@ -786,6 +812,13 @@ export default {
786
812
  // onSearchUnavailable: 'fail', // 'skip' → converge without search indexes where Search is absent
787
813
  // waitForSearchIndexes: false, // true → converge waits until search indexes are queryable
788
814
 
815
+ // ── Background migrations (experimental) — see `migronaut background` ───
816
+ // backgroundCollection: '_migronaut_background', // state (+ _partitions, _watch)
817
+ // backgroundInline: false, // true → run it to the end inside the registering `up`
818
+ // backgroundOnDrift: 'reopen', // 'report' → only report old-shape documents after completion
819
+ // backgroundDrift: 'poll', // 'stream' | 'both' → watch drift with change streams
820
+ // backgroundShardAware: 'auto', // 'off' → no shard-key partitions on sharded collections
821
+
789
822
  // ── Code-only options (omit in migronaut.config.json) ─────────────────────────
790
823
  // hooks: { beforeAll, afterAll, beforeEach, afterEach, onError },
791
824
  // mongoose: myMongooseInstance, // pass if your migrations use Mongoose models
@@ -923,6 +956,11 @@ optional rather than merely discouraged:
923
956
  | `MIGRONAUT_ON_SEARCH_UNAVAILABLE` | `onSearchUnavailable` | `fail` |
924
957
  | `MIGRONAUT_WAIT_FOR_SEARCH_INDEXES` | `waitForSearchIndexes` | `false` |
925
958
  | `MIGRONAUT_SEARCH_INDEX_WAIT_TIMEOUT_MS` | `searchIndexWaitTimeoutMs` | `600000` |
959
+ | `MIGRONAUT_BACKGROUND_COLLECTION` | `backgroundCollection` | `_migronaut_background` |
960
+ | `MIGRONAUT_BACKGROUND_INLINE` | `backgroundInline` | `false` |
961
+ | `MIGRONAUT_BACKGROUND_ON_DRIFT` | `backgroundOnDrift` | `reopen` |
962
+ | `MIGRONAUT_BACKGROUND_DRIFT` | `backgroundDrift` | `poll` |
963
+ | `MIGRONAUT_BACKGROUND_SHARD_AWARE` | `backgroundShardAware` | `auto` |
926
964
  | `MIGRONAUT_ENV_FILE` | `envFile` | `.env` |
927
965
 
928
966
  `fileExtensions`, `clientOptions`, `collections`, `client`, `mongoose`, `hooks`, `logger`,