@decaf-ts/for-nano 0.5.18 → 0.6.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/README.md +106 -0
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -268,8 +268,114 @@ await NanoAdapter.createDatabase(url, "mydb");
|
|
|
268
268
|
// ... createUser/deleteUser, deleteDatabase, etc.
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
+
## Task Engine guardrails for migration orchestration
|
|
271
272
|
|
|
273
|
+
Migration command runners and the integration tests rely on a dedicated `RamAdapter` task engine that never shares an alias with the adapters being migrated. `MigrationService.migrateAdapters` enforces this guardrail by comparing every adapter alias and rejecting runs where the task engine would also be a migration target. Keep your task engine adapter isolated (for example `new RamAdapter({}, "decaf-cli-task-engine")`) and set `concurrency: 1` so version steps stay sequential.
|
|
272
274
|
|
|
275
|
+
```ts
|
|
276
|
+
import { RamAdapter } from "@decaf-ts/core/ram";
|
|
277
|
+
import { TaskService } from "@decaf-ts/core/tasks";
|
|
278
|
+
|
|
279
|
+
const taskEngineAdapter = new RamAdapter({}, "decaf-cli-task-engine");
|
|
280
|
+
const taskService = new TaskService();
|
|
281
|
+
await taskService.boot({
|
|
282
|
+
adapter: taskEngineAdapter,
|
|
283
|
+
workerId: "nano-migration-worker",
|
|
284
|
+
leaseMs: 10_000,
|
|
285
|
+
logTailMax: 250,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Reserve `leaseMs` for longer-running migrations, tune `pollMsBusy`/`pollMsIdle`, and attach a `TaskEventBus` if you want progress logs in the CLI output. `taskService.track(taskId)` can then stream the same progress/log events that the integration tests already observe.
|
|
290
|
+
|
|
291
|
+
## Migration lifecycle and @migration semantics for Nano
|
|
292
|
+
|
|
293
|
+
`@migration` metadata controls ordering and flavour targeting:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
@migration("1.1.0-add-category-field", {
|
|
297
|
+
precedence: "1.1.0",
|
|
298
|
+
flavour: "nano",
|
|
299
|
+
rules: [
|
|
300
|
+
async (_, adapter) => Boolean(await adapter.exists("for_nano_migration_products")),
|
|
301
|
+
],
|
|
302
|
+
})
|
|
303
|
+
class AddCategoryMigration extends AbsMigration<NanoAdapter> { ... }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- `reference`: the canonical label (usually the semver string) used for logging, precedence tokens, and version normalization.
|
|
307
|
+
- `precedence`: a constructor, reference string, or object pointing to another migration to force ordering when version/flavour collide.
|
|
308
|
+
- `flavour`: limits execution to the Nano flavour (the for-nano tests only execute Nano-scoped migrations).
|
|
309
|
+
- `rules`: async predicates `(qr, adapter, ctx)` that gate execution; a `false` result skips the migration without failing the run.
|
|
310
|
+
|
|
311
|
+
`MigrationService` also expects handlers for `retrieveLastVersion` and `setCurrentVersion`. These functions are invoked per flavour so you can persist the head (e.g., in a `VersionRepo`). During the live integration test we initialize the version map at `"1.0.0"` and let `setCurrentVersion` advance it to the target version once the required property addition/backfill completes.
|
|
312
|
+
|
|
313
|
+
Use `MigrationService.migrateAdapters([nanoAdapter], { toVersion: "2.0.0", handlers: {...}, taskMode: true, taskService })` once your `NanoAdapter` is initialized. `taskMode: true` queues one `CompositeTask` per version and calls `setCurrentVersion` immediately after each task resolves, which keeps the persistent version marker aligned with the latest fully applied hop. Normal mode updates the version only after the whole batch finishes.
|
|
314
|
+
|
|
315
|
+
`MigrationService.retry(taskId)` rewrites the failed `TaskModel` to `PENDING`, clears `error`, `leaseOwner`, and timestamps, and re-enqueues the same version so the CLI can resume from the failing point. Because the version marker wasn't advanced for the failed task, rerunning the CLI with the same `toVersion` continues at the correct semantic boundary. Our tests ensure that every migration adds a required property/column and fills existing documents with the default value before the next version runs.
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
## Live migration workflow
|
|
321
|
+
|
|
322
|
+
Migration integration suites run against live CouchDB instances. `for-nano` tests are intentionally limited to `RamAdapter` + `NanoAdapter` so they stay independent of SQL modules. That means:
|
|
323
|
+
|
|
324
|
+
- Every migration must add a new required property/column and backfill every existing document with the default value before continuing.
|
|
325
|
+
- Use `MigrationService.migrateAdapters([nanoAdapter], config)` with flavour-scoped handlers so the last executed version is persisted independently per adapter.
|
|
326
|
+
- If you turn on `taskMode`, boot a separate `RamAdapter` (alias distinct from the ones being migrated) before you create the `TaskService`. The migration guard throws if the task engine adapter alias is also a migration target.
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
@migration("1.1.0-add-isActive", {
|
|
330
|
+
precedence: "1.1.0",
|
|
331
|
+
flavour: "nano",
|
|
332
|
+
})
|
|
333
|
+
export class AddIsActiveMigration implements Migration<any, NanoAdapter> {
|
|
334
|
+
async up(_, adapter) {
|
|
335
|
+
const repo = new Repository(adapter, UserModel);
|
|
336
|
+
const users = await repo.select().execute();
|
|
337
|
+
await Promise.all(
|
|
338
|
+
users.map((user) => repo.update({ ...user, isActive: true }))
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
const migrations = await MigrationService.migrateAdapters(
|
|
346
|
+
[nanoAdapter],
|
|
347
|
+
{
|
|
348
|
+
toVersion: "1.1.0",
|
|
349
|
+
flavours: ["nano"],
|
|
350
|
+
taskMode: true,
|
|
351
|
+
taskService,
|
|
352
|
+
handlers: {
|
|
353
|
+
nano: {
|
|
354
|
+
retrieveLastVersion: async (adapter) =>
|
|
355
|
+
(await versionRepo(adapter).read("nano"))?.version,
|
|
356
|
+
setCurrentVersion: async (version, adapter) =>
|
|
357
|
+
await versionRepo(adapter).upsert("nano", { version }),
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
for (const migration of migrations) {
|
|
363
|
+
await migration.track();
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`MigrationService` starts by calling the flavour-specific `retrieveLastVersion` handler so it knows which version the database already holds, then filters decorated migrations whose normalized versions are strictly greater than `currentVersion` and less than or equal to `toVersion`. `setCurrentVersion` is invoked after every successfully completed version: inline runs update once at the very end, while task mode updates immediately after each tracked `CompositeTask`. This guarantees the recorded `currentVersion` always equals the last fully finished hop, so rerunning the command will skip completed versions and replay only the pending ones. When a task fails, call `MigrationService.retry(taskId)` (optionally `taskService.track(id)` to observe progress) to reset the `TaskModel` to `PENDING`, clear its error/lease metadata, and let the TaskEngine reclaim the same version without revisiting already finished steps.
|
|
368
|
+
|
|
369
|
+
The `@migration` decorator handles ordering and targeting. The key arguments are:
|
|
370
|
+
|
|
371
|
+
- `reference`: the name/semver label used in logs and dependency graphs.
|
|
372
|
+
- `precedence`: optional hint (a constructor, string, or object) that the sorter uses when two migrations share the same version and flavour.
|
|
373
|
+
- `flavour`: restricts the migration to one adapter flavour (`"nano"`, `"type-orm"`, etc.). Omit it for generic migrations or to let `includeGenericInTaskMode` decide when to run.
|
|
374
|
+
- `rules`: async predicates that gate execution (`(qr, adapter, ctx) => Promise<boolean>`). When a rule returns `false` the migration is skipped without error.
|
|
375
|
+
|
|
376
|
+
The CLI migrations guard enforces that the `TaskEngine` runs on a separate `RamAdapter` alias that is never one of the migrating adapters, keeping persistence targets isolated and preventing lease conflicts.
|
|
377
|
+
|
|
378
|
+
Use `MigrationRule`s (the `rules` array in `@migration`) to gate execution based on adapter state. Keep every migration focused on one version jump so the live suites can always rerun and verify the required schema change and backfill.
|
|
273
379
|
|
|
274
380
|
## Coding Principles
|
|
275
381
|
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from "./adapter";
|
|
|
13
13
|
* @const VERSION
|
|
14
14
|
* @memberOf module:for-nano
|
|
15
15
|
*/
|
|
16
|
-
export declare const VERSION = "0.5.
|
|
16
|
+
export declare const VERSION = "0.5.18";
|
|
17
17
|
/**
|
|
18
18
|
* @description Package version identifier
|
|
19
19
|
* @summary Stores the current package version string for the for-nano module
|
package/lib/esm/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export * from "./adapter.js";
|
|
|
18
18
|
* @const VERSION
|
|
19
19
|
* @memberOf module:for-nano
|
|
20
20
|
*/
|
|
21
|
-
export const VERSION = "0.5.
|
|
21
|
+
export const VERSION = "0.5.18";
|
|
22
22
|
/**
|
|
23
23
|
* @description Package version identifier
|
|
24
24
|
* @summary Stores the current package version string for the for-nano module
|
package/lib/index.cjs
CHANGED
|
@@ -35,7 +35,7 @@ __exportStar(require("./adapter.cjs"), exports);
|
|
|
35
35
|
* @const VERSION
|
|
36
36
|
* @memberOf module:for-nano
|
|
37
37
|
*/
|
|
38
|
-
exports.VERSION = "0.5.
|
|
38
|
+
exports.VERSION = "0.5.18";
|
|
39
39
|
/**
|
|
40
40
|
* @description Package version identifier
|
|
41
41
|
* @summary Stores the current package version string for the for-nano module
|
package/lib/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from "./adapter";
|
|
|
13
13
|
* @const VERSION
|
|
14
14
|
* @memberOf module:for-nano
|
|
15
15
|
*/
|
|
16
|
-
export declare const VERSION = "0.5.
|
|
16
|
+
export declare const VERSION = "0.5.18";
|
|
17
17
|
/**
|
|
18
18
|
* @description Package version identifier
|
|
19
19
|
* @summary Stores the current package version string for the for-nano module
|