@alexify/migronaut 2.0.0 → 2.1.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 (49) hide show
  1. package/CHANGELOG.md +320 -0
  2. package/README.md +208 -6
  3. package/bullmq.d.ts +845 -0
  4. package/bullmq.js +1 -0
  5. package/index.d.ts +634 -18
  6. package/migronaut.schema.json +182 -1
  7. package/package.json +21 -5
  8. package/src/bullmq/index.js +55 -0
  9. package/src/bullmq/jobs.js +454 -0
  10. package/src/bullmq/processor.js +608 -0
  11. package/src/bullmq/producer.js +424 -0
  12. package/src/bullmq/service.js +653 -0
  13. package/src/bullmq/wait.js +124 -0
  14. package/src/cli/args.js +12 -2
  15. package/src/cli/commands/converge.js +160 -0
  16. package/src/cli/commands/down.js +2 -0
  17. package/src/cli/commands/lock.js +2 -1
  18. package/src/cli/commands/redo.js +8 -1
  19. package/src/cli/commands/up.js +14 -1
  20. package/src/cli/exit-codes.js +9 -2
  21. package/src/cli/index.js +2 -0
  22. package/src/cli/shared.js +14 -4
  23. package/src/cli/table.js +105 -0
  24. package/src/core/changelog.js +71 -6
  25. package/src/core/collections.js +372 -0
  26. package/src/core/config.js +100 -25
  27. package/src/core/converge-log.js +47 -0
  28. package/src/core/converge-plan.js +483 -0
  29. package/src/core/converge.js +867 -0
  30. package/src/core/index-spec.js +496 -0
  31. package/src/core/lock-wait.js +260 -0
  32. package/src/core/lock.js +45 -16
  33. package/src/core/migrator.js +563 -283
  34. package/src/core/options.js +251 -0
  35. package/src/core/run-recorder.js +157 -0
  36. package/src/core/run.js +58 -90
  37. package/src/core/sequence.js +134 -0
  38. package/src/errors/index.js +56 -0
  39. package/src/index.js +8 -0
  40. package/src/utils/actor.js +48 -0
  41. package/src/utils/canonical.js +179 -0
  42. package/src/utils/collection-name.js +21 -0
  43. package/src/utils/error.js +18 -1
  44. package/src/utils/id.js +77 -0
  45. package/src/utils/loader.js +39 -21
  46. package/src/utils/migration-name.js +32 -0
  47. package/src/utils/redact.js +21 -1
  48. package/src/utils/telemetry.js +393 -0
  49. package/src/utils/template.js +36 -2
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://migronaut.vercel.app/migronaut.schema.json",
4
4
  "title": "migronaut configuration",
5
- "description": "Configuration for migronaut.config.json. Options that hold live instances (hooks, logger, mongoose, client) are only available in a .ts/.js config.",
5
+ "description": "Configuration for migronaut.config.json. Options that hold live instances or functions (hooks, logger, mongoose, client, generateId, telemetry) are only available in a .ts/.js config.",
6
6
  "type": "object",
7
7
  "properties": {
8
8
  "$schema": {
@@ -39,6 +39,13 @@
39
39
  "pattern": "^(?!system\\.)[^$\\u0000]+$",
40
40
  "description": "Collection used for the concurrency lock"
41
41
  },
42
+ "convergeLogCollection": {
43
+ "type": "string",
44
+ "minLength": 1,
45
+ "default": "_migronaut_converge",
46
+ "pattern": "^(?!system\\.)[^$\\u0000]+$",
47
+ "description": "Collection holding the converge history (written on the first converge that changes something)"
48
+ },
42
49
  "lockTTLSeconds": {
43
50
  "type": "integer",
44
51
  "minimum": 1,
@@ -139,6 +146,180 @@
139
146
  "clientOptions": {
140
147
  "type": "object",
141
148
  "description": "MongoDB driver options (MongoClientOptions): TLS, auth mechanisms, proxies, pool sizing. Passed to the driver as-is."
149
+ },
150
+ "collections": {
151
+ "type": "array",
152
+ "items": {
153
+ "$ref": "#/definitions/collection"
154
+ },
155
+ "description": "Declared collections: `migronaut converge` keeps their indexes and validator in the declared state, with no migration file per change"
156
+ },
157
+ "collectionsDir": {
158
+ "type": "string",
159
+ "minLength": 1,
160
+ "description": "Directory of collection definition files, one collection per file. Opt-in: nothing is read unless this is set"
161
+ },
162
+ "convergeAfterUp": {
163
+ "type": "boolean",
164
+ "default": false,
165
+ "description": "End every bulk `up` (no file, no --to) by converging the declared collections under the same lock"
166
+ }
167
+ },
168
+ "definitions": {
169
+ "collection": {
170
+ "type": "object",
171
+ "required": [
172
+ "name"
173
+ ],
174
+ "additionalProperties": false,
175
+ "anyOf": [
176
+ {
177
+ "required": [
178
+ "indexes"
179
+ ]
180
+ },
181
+ {
182
+ "required": [
183
+ "validator"
184
+ ]
185
+ }
186
+ ],
187
+ "properties": {
188
+ "name": {
189
+ "type": "string",
190
+ "minLength": 1,
191
+ "pattern": "^(?!system\\.)[^$\\u0000]+$",
192
+ "description": "Collection name"
193
+ },
194
+ "indexes": {
195
+ "type": "array",
196
+ "items": {
197
+ "$ref": "#/definitions/index"
198
+ },
199
+ "description": "Every index besides _id. Omit to leave the collection's indexes unmanaged"
200
+ },
201
+ "validator": {
202
+ "oneOf": [
203
+ {
204
+ "type": "object"
205
+ },
206
+ {
207
+ "type": "null"
208
+ }
209
+ ],
210
+ "description": "A query or { $jsonSchema } document; null or {} for no validator. Omit to leave the validator unmanaged"
211
+ },
212
+ "validationLevel": {
213
+ "enum": [
214
+ "off",
215
+ "strict",
216
+ "moderate"
217
+ ],
218
+ "description": "How the validator applies to updates. Defaults to 'strict'"
219
+ },
220
+ "validationAction": {
221
+ "enum": [
222
+ "error",
223
+ "warn",
224
+ "errorAndLog"
225
+ ],
226
+ "description": "What an invalid write does. Defaults to 'error'"
227
+ },
228
+ "prune": {
229
+ "type": "boolean",
230
+ "description": "Drop indexes this definition does not declare (otherwise they are kept and reported)"
231
+ }
232
+ }
233
+ },
234
+ "index": {
235
+ "type": "object",
236
+ "required": [
237
+ "key"
238
+ ],
239
+ "additionalProperties": false,
240
+ "properties": {
241
+ "key": {
242
+ "type": "object",
243
+ "minProperties": 1,
244
+ "additionalProperties": {
245
+ "enum": [
246
+ 1,
247
+ -1,
248
+ "text",
249
+ "hashed",
250
+ "2d",
251
+ "2dsphere"
252
+ ]
253
+ },
254
+ "description": "Field → direction, in index order"
255
+ },
256
+ "name": {
257
+ "type": "string",
258
+ "minLength": 1,
259
+ "description": "Defaults to the name MongoDB generates (email_1)"
260
+ },
261
+ "unique": {
262
+ "type": "boolean"
263
+ },
264
+ "sparse": {
265
+ "type": "boolean"
266
+ },
267
+ "hidden": {
268
+ "type": "boolean"
269
+ },
270
+ "expireAfterSeconds": {
271
+ "type": "integer",
272
+ "minimum": 0
273
+ },
274
+ "partialFilterExpression": {
275
+ "type": "object"
276
+ },
277
+ "collation": {
278
+ "type": "object",
279
+ "required": [
280
+ "locale"
281
+ ]
282
+ },
283
+ "wildcardProjection": {
284
+ "type": "object"
285
+ },
286
+ "weights": {
287
+ "type": "object"
288
+ },
289
+ "default_language": {
290
+ "type": "string",
291
+ "minLength": 1
292
+ },
293
+ "language_override": {
294
+ "type": "string",
295
+ "minLength": 1
296
+ },
297
+ "textIndexVersion": {
298
+ "type": "integer",
299
+ "minimum": 1
300
+ },
301
+ "2dsphereIndexVersion": {
302
+ "type": "integer",
303
+ "minimum": 1
304
+ },
305
+ "bits": {
306
+ "type": "integer",
307
+ "minimum": 1
308
+ },
309
+ "min": {
310
+ "type": "number"
311
+ },
312
+ "max": {
313
+ "type": "number"
314
+ },
315
+ "storageEngine": {
316
+ "type": "object"
317
+ },
318
+ "background": {
319
+ "type": "boolean",
320
+ "description": "Accepted and ignored — a no-op since MongoDB 4.2"
321
+ }
322
+ }
142
323
  }
143
324
  }
144
325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexify/migronaut",
3
- "version": "2.0.0",
3
+ "version": "2.1.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>",
@@ -24,6 +24,10 @@
24
24
  ".": {
25
25
  "types": "./index.d.ts",
26
26
  "default": "./index.js"
27
+ },
28
+ "./bullmq": {
29
+ "types": "./bullmq.d.ts",
30
+ "default": "./bullmq.js"
27
31
  }
28
32
  },
29
33
  "directories": {
@@ -32,6 +36,8 @@
32
36
  "files": [
33
37
  "index.js",
34
38
  "index.d.ts",
39
+ "bullmq.js",
40
+ "bullmq.d.ts",
35
41
  "migronaut.schema.json",
36
42
  "bin",
37
43
  "src",
@@ -58,6 +64,9 @@
58
64
  "migrate-mongo-alternative",
59
65
  "mongoose",
60
66
  "mongoose-migration",
67
+ "bullmq",
68
+ "job-queue",
69
+ "migration-service",
61
70
  "rollback",
62
71
  "transactions",
63
72
  "zero-dependency",
@@ -75,11 +84,18 @@
75
84
  }
76
85
  },
77
86
  "devDependencies": {
87
+ "@opentelemetry/api": "^1.9.1",
88
+ "@opentelemetry/instrumentation-mongodb": "^0.75.0",
89
+ "@opentelemetry/sdk-metrics": "^2.11.0",
90
+ "@opentelemetry/sdk-trace-node": "^2.11.0",
78
91
  "@types/node": "^22.19.19",
79
92
  "@vercel/analytics": "^2.0.1",
80
93
  "@vercel/speed-insights": "^2.0.0",
94
+ "bullmq": "^6.3.11",
95
+ "bullmq-otel": "^2.0.1",
81
96
  "c8": "^10.1.3",
82
97
  "esbuild": "^0.28.1",
98
+ "ioredis": "^5.11.1",
83
99
  "mongodb": "^6.12.0",
84
100
  "mongodb-memory-server": "10.4.3",
85
101
  "mongoose": "^8.9.2",
@@ -96,10 +112,10 @@
96
112
  "test:integration": "node scripts/node-test.js --test-concurrency=1 \"tests/integration/**/*.test.js\"",
97
113
  "test:coverage": "c8 --all --include 'src/**' --check-coverage --lines 90 --branches 90 --functions 90 --reporter text --reporter lcov node scripts/node-test.js --test-concurrency=1 \"tests/unit/**/*.test.js\" \"tests/integration/**/*.test.js\"",
98
114
  "test:types": "tsd",
99
- "check:dts": "tsc --noEmit --strict --skipLibCheck false index.d.ts",
100
- "lint": "oxlint src bin scripts tests bench",
101
- "format": "oxfmt src bin scripts tests bench",
102
- "format:check": "oxfmt --check src bin scripts tests bench",
115
+ "check:dts": "tsc --noEmit --strict --skipLibCheck false index.d.ts bullmq.d.ts",
116
+ "lint": "oxlint src bin scripts tests bench examples",
117
+ "format": "oxfmt src bin scripts tests bench examples",
118
+ "format:check": "oxfmt --check src bin scripts tests bench examples",
103
119
  "size": "node scripts/size.js",
104
120
  "bench": "node bench/bench.js",
105
121
  "docs:dev": "vitepress dev docs",
@@ -0,0 +1,55 @@
1
+ const {
2
+ DEFAULT_CONVERGE_SCHEDULER_ID,
3
+ DEFAULT_QUEUE_NAME,
4
+ DEFAULT_SCHEDULER_ID,
5
+ JOB_DATA_VERSION,
6
+ JOB_NAMES,
7
+ MIN_JOB_DATA_VERSION,
8
+ dedupId,
9
+ parseJobData,
10
+ } = require('./jobs.js');
11
+ const { RETRYABLE_CODES, createMigrationProcessor, isRetryableError } = require('./processor.js');
12
+ const {
13
+ enqueueConverge,
14
+ enqueueDown,
15
+ enqueueUp,
16
+ planDownJobs,
17
+ planUpJobs,
18
+ } = require('./producer.js');
19
+ const { MigrationQueue, createMigrationQueue } = require('./service.js');
20
+ const { waitForGroup } = require('./wait.js');
21
+
22
+ /**
23
+ * `@alexify/migronaut/bullmq` — migrations as a queue, one migration per job.
24
+ *
25
+ * BullMQ is never required from here: the classes are injected by the caller
26
+ * (`createMigrationQueue({ bullmq: { Queue, Worker } })`), so this entry point
27
+ * costs nothing to anyone who does not use it and pins no BullMQ version.
28
+ */
29
+ module.exports = {
30
+ // The service facade — queue, worker, scheduling and status in one object
31
+ createMigrationQueue,
32
+ MigrationQueue,
33
+
34
+ // Building blocks, for a Queue/Worker the application already owns
35
+ // (NestJS processors, BullMQ Pro, a shared worker process)
36
+ createMigrationProcessor,
37
+ enqueueUp,
38
+ enqueueDown,
39
+ enqueueConverge,
40
+ planUpJobs,
41
+ planDownJobs,
42
+ waitForGroup,
43
+
44
+ // The job contract
45
+ JOB_NAMES,
46
+ JOB_DATA_VERSION,
47
+ MIN_JOB_DATA_VERSION,
48
+ DEFAULT_QUEUE_NAME,
49
+ DEFAULT_SCHEDULER_ID,
50
+ DEFAULT_CONVERGE_SCHEDULER_ID,
51
+ RETRYABLE_CODES,
52
+ dedupId,
53
+ isRetryableError,
54
+ parseJobData,
55
+ };