@alexify/migronaut 1.0.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 (50) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/LICENSE +22 -0
  3. package/README.md +773 -0
  4. package/bin/migronaut.js +36 -0
  5. package/index.d.ts +903 -0
  6. package/index.js +1 -0
  7. package/migronaut.schema.json +135 -0
  8. package/package.json +105 -0
  9. package/src/cli/args.js +314 -0
  10. package/src/cli/commands/audit.js +40 -0
  11. package/src/cli/commands/create.js +29 -0
  12. package/src/cli/commands/down.js +30 -0
  13. package/src/cli/commands/dry-run.js +36 -0
  14. package/src/cli/commands/import.js +33 -0
  15. package/src/cli/commands/init.js +64 -0
  16. package/src/cli/commands/list.js +19 -0
  17. package/src/cli/commands/lock.js +35 -0
  18. package/src/cli/commands/redo.js +16 -0
  19. package/src/cli/commands/status.js +52 -0
  20. package/src/cli/commands/unlock.js +43 -0
  21. package/src/cli/commands/up.js +53 -0
  22. package/src/cli/exit-codes.js +38 -0
  23. package/src/cli/index.js +91 -0
  24. package/src/cli/shared.js +343 -0
  25. package/src/cli/spinner.js +59 -0
  26. package/src/cli/table.js +259 -0
  27. package/src/core/audit.js +152 -0
  28. package/src/core/changelog.js +231 -0
  29. package/src/core/config.js +479 -0
  30. package/src/core/context.js +16 -0
  31. package/src/core/import-runner.js +164 -0
  32. package/src/core/import.js +60 -0
  33. package/src/core/lock.js +348 -0
  34. package/src/core/migrator.js +1475 -0
  35. package/src/core/run.js +144 -0
  36. package/src/core/runner.js +150 -0
  37. package/src/errors/index.js +215 -0
  38. package/src/index.js +59 -0
  39. package/src/utils/checksum.js +23 -0
  40. package/src/utils/colors.js +68 -0
  41. package/src/utils/concurrency.js +32 -0
  42. package/src/utils/date.js +28 -0
  43. package/src/utils/env.js +51 -0
  44. package/src/utils/error.js +11 -0
  45. package/src/utils/loader.js +131 -0
  46. package/src/utils/logger.js +109 -0
  47. package/src/utils/redact.js +39 -0
  48. package/src/utils/sanitize.js +43 -0
  49. package/src/utils/template.js +615 -0
  50. package/src/utils/user.js +25 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## v1.0.0
6
+
7
+ Initial release. Requires **Node.js ≥ 22.18**.
8
+
9
+ ### Core
10
+
11
+ - `MigratorKit` orchestration: `up`, `down`, `redo`, `dryRun`, `status`, `list`, `audit`,
12
+ `create`, `init`, `import`, `lockInfo`, `forceUnlock`
13
+ - `migronaut` CLI with `init`, `up`, `down`, `redo`, `status`, `list`, `dry-run`, `audit`,
14
+ `create`, `import`, `lock`, and `unlock` commands
15
+ - Config loader with priority: CLI flags → `MIGRONAUT_*` env vars → config file → defaults,
16
+ checked by a built-in zero-dependency validator (`ConfigInvalidError` with per-issue
17
+ `{ path, message }`)
18
+ - **Every scalar option is settable from the environment** — a table-driven `MIGRONAUT_*` layer
19
+ pinned against the config-key spec by a test, so a config file is genuinely optional rather than
20
+ merely discouraged. Values fail closed: `MIGRONAUT_STRICT=on`, `MIGRONAUT_LOCK_TTL=abc` and
21
+ `MIGRONAUT_CREATE_EXTENSION=tsx` are rejected with an error naming the variable, never coerced
22
+ - `MIGRONAUT_NO_COLOR` / `MIGRONAUT_FORCE_COLOR` pin migronaut's own color output above the
23
+ ecosystem-wide `NO_COLOR`/`FORCE_COLOR`, which stay honored underneath; `MIGRONAUT_USER`
24
+ overrides the OS user recorded in `executedBy`, for CI where that user is a meaningless `runner`
25
+ - Function / async config files — `export default` a (sync or async) factory returning the config,
26
+ for loading the connection from a secret manager at runtime with no bundled cloud SDKs
27
+ - **Client injection** (`config.client`) — reuse an already-connected `MongoClient` (its pool,
28
+ auth, TLS); ownership stays with the caller and `disconnect()` never closes it
29
+ - **Lifecycle events** — `MigratorKit` is an EventEmitter: `run:start`/`run:end` (with duration
30
+ and result counts), `migration:start`/`success`/`skipped`/`error`,
31
+ `lock:acquired`/`released`/`lost` — feed metrics or alerting without parsing log lines
32
+ - First-class `.ts` and `.js` (ESM + CJS) migration loading
33
+ - MongoDB-native concurrency lock with TTL-based stale reclaim and heartbeat renewal for long
34
+ migrations
35
+ - SHA-256 checksum tamper detection, surfaced in `status`
36
+ - Opt-in transactions (per-file or global) with automatic commit/abort
37
+ - Lifecycle hooks: `beforeAll`, `afterAll`, `beforeEach`, `afterEach`, `onError`
38
+ - Append-only audit trail in `_migronaut_migrations` — reverts are never deleted
39
+
40
+ ### Step controls & automation
41
+
42
+ - **`migronaut up --step`** — apply each pending file as its own sequential batch, so a later
43
+ `down` can peel migrations off one at a time
44
+ - **`migronaut down --steps <n>`** — revert the last N applied migrations, newest first, regardless
45
+ of batch
46
+ - **`migronaut up --to <file>` / `migronaut down --to <file>`** — migrate to a named point in the
47
+ sequence: `up --to` applies pending files up to and including it, `down --to` reverts everything
48
+ applied after it (exclusive), so the pair is a round trip
49
+ - **`migronaut dry-run`** previews the same selections — `--steps`, `--batch`, and `--to` — without
50
+ touching the database
51
+ - **`migronaut audit`** — read-only health check (config, connectivity, transaction support,
52
+ indexes, lock state, checksum drift, runtime) with pass/warn/fail per check
53
+ - **`migronaut lock`** — inspect the current migration lock holder without modifying it
54
+ - **`--json` machine-readable output** as a global flag (`migronaut --json status` and
55
+ `migronaut status --json` both work) on every command — `up`, `down`, `redo`, `status`, `list`,
56
+ `dry-run`, `import`, `create`, `audit`, `lock`, `unlock` — a single JSON document on stdout;
57
+ human logs and the spinner go to stderr, so stdout stays pipe-safe. The one exception is
58
+ `init`, whose deliverable is the config file itself: `init --format <js|ts|json>` selects the
59
+ file format, and a stray `init --json` is rejected with a pointer to `--format`
60
+ - **`migronaut status --check`** — exits with code 2 (`PENDING_MIGRATIONS`) when any migration is
61
+ pending, for CI deploy gates; `--pending` and `--limit <n>` filter the table
62
+ - **Typed exit codes for every error** — each `MigronautError` code maps to a dedicated exit code
63
+ (idempotency cases included: `CONFIG_FILE_EXISTS` = 16, `IMPORT_TARGET_NOT_EMPTY` = 17), audit
64
+ failures exit 22, and the full map is exported from the package root as `EXIT_CODES`
65
+ - **`migronaut unlock`** — force-release a stuck lock left behind by a crashed run, with holder
66
+ info (pid / host / user / since) and a confirmation prompt (`--yes` to skip)
67
+ - **`migronaut up <file> --force --yes`** — confirm a forced re-run non-interactively
68
+
69
+ ### Zero dependencies
70
+
71
+ - **No runtime dependencies at all** — `package.json` has no `dependencies` key; only `mongodb`
72
+ (required) and `mongoose` (optional) as peers
73
+ - `.env` loading via native `util.parseEnv` (quotes, `export ` prefix, comments, multiline
74
+ values) — real env vars always win over `.env`
75
+ - Hand-rolled ANSI colors (detection: `MIGRONAUT_FORCE_COLOR` > `MIGRONAUT_NO_COLOR` >
76
+ `FORCE_COLOR` > `NO_COLOR` > `TERM=dumb` > TTY), a TTY-only
77
+ spinner (complete no-op when piped), box-drawing tables with ANSI-aware column widths, and a
78
+ commander-compatible argument parser (combined short flags like `-fy` are not supported)
79
+ - `MigronautLogger` is pino-compatible (`{ debug, info, warn, error, child? }`) — pass a pino
80
+ instance directly as `logger`; a `component: 'migronaut'` child binding is applied once and a
81
+ throwing logger can never break a migration run
82
+
83
+ ### Import from `migrate-mongo`
84
+
85
+ - **`migronaut import`** — read an existing `migrate-mongo` `changelog` and record that history in
86
+ the migronaut changelog, so `migronaut up` runs only what is new. One-time and forward-only; the
87
+ source collection is never modified
88
+ - `--from <collection>` / `--to <collection>`, `--dry-run`, `--trust-hash`, `--force`, `--no-lock`
89
+ - **Forward-only safety** — imported records are tagged `origin: 'migrate-mongo'`; `migronaut
90
+ down`/`redo` refuse them up front with a clear reason, so the changelog is never corrupted
91
+
92
+ ### Hardening
93
+
94
+ - **Path-traversal protection** — migration names are validated as bare filenames confined to the
95
+ migrations directory, so a crafted name can't load or read a file outside it
96
+ (`MigrationInvalidNameError`)
97
+ - **Lock safety for long migrations** — heartbeat renewal at half the TTL, owner-scoped
98
+ acquire/release/renew, server-time (`$$NOW`) staleness judgments immune to host clock skew, and
99
+ a hard renewal deadline that stops the run strictly before a peer could reclaim the lock
100
+ - **Credential redaction** — connection-string passwords are masked (`user:****@`) in every error
101
+ message, `--json` payload, and `--verbose` stack the CLI emits
102
+ - **Terminal-injection safety** — control characters in DB-sourced values (descriptions,
103
+ filenames, lock-holder fields) are stripped in the default logger's write path, the spinner and
104
+ table rendering; migronaut's own SGR colors survive, cursor movement and screen clearing do not.
105
+ Raw `Error` objects never reach event subscribers either — `migration:error` and `run:end`
106
+ carry a pre-redacted message string
107
+ - **Unbounded-wait protection** — `runMigrations` validates `lockWaitTimeoutMs` /
108
+ `lockPollIntervalMs` up front (`ConfigInvalidError`), so a `NaN` can no longer turn the
109
+ lock-wait loop into an infinite retry storm; the summary reports `waitedMs` and `attempts`
110
+ - **Preview parity** — `dry-run down` uses the exact selection the real `down` executes,
111
+ including the refusal of forward-only migrate-mongo imports, and lists rows in revert order
112
+ - **`redo` correctness** — the target is resolved *inside* the lock (no race with a peer), and a
113
+ failed re-apply carries the already-reverted rows in `error.context.results`
114
+ - **CLI logger fallback** — a `logger` from the config file (pino, or `null` for silence) is
115
+ respected by the CLI instead of being clobbered by its console logger
116
+ - **Clear `.ts` runtime errors** — actionable messages when type stripping is disabled or a
117
+ migration uses non-erasable TypeScript syntax (`enum`, `namespace`)
118
+ - **`TransactionsUnsupportedError`** — `useTransaction` on a standalone server names the topology
119
+ as the problem instead of blaming the migration
120
+ - `prepublishOnly` runs `lint` + `format:check` + coverage-gated tests + `tsd` type tests +
121
+ a strict `tsc` pass over `index.d.ts` (`check:dts`), so a broken release can't be published
122
+
123
+ ### Documentation
124
+
125
+ - Full documentation site at <https://migronaut.vercel.app/> — guides, a full command reference, a
126
+ programmatic API overview, an FAQ, and a migrate-mongo migration guide
127
+ - Requires **Node.js ≥ 22.18** — `.ts` migrations run natively (built-in type stripping) with no
128
+ loader or build step; `.js` migrations likewise
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Santosh Gupta
4
+ Copyright (c) 2026 Alexis Technologies
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.