@owlmeans/resource 0.1.14 → 0.1.16-rc.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 +24 -1
- package/agent-meta/instructions/resource.instructions.md +20 -10
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/resource/SKILL.md +47 -10
- package/build/consts.d.ts +16 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +17 -0
- package/build/consts.js.map +1 -1
- package/build/errors.d.ts +18 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +26 -0
- package/build/errors.js.map +1 -1
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +8 -7
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/migration.d.ts +21 -0
- package/build/migration.d.ts.map +1 -0
- package/build/migration.js +0 -0
- package/build/migration.js.map +1 -0
- package/build/types.d.ts +68 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/consts.ts +17 -0
- package/src/errors.ts +30 -0
- package/src/helper.ts +8 -6
- package/src/index.ts +1 -0
- package/src/migration.ts +0 -0
- package/src/types.ts +74 -0
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ const result: ListResult<ProjectRecord> = await ctx.project().list({
|
|
|
65
65
|
- `save(record, opts?)` — upsert by ID
|
|
66
66
|
- `update(record, opts?)` — update; throws `UnknownRecordError` if missing
|
|
67
67
|
- `delete(id, opts?)` — remove; returns the deleted record or `null`
|
|
68
|
-
- `pick(id, opts?)` —
|
|
68
|
+
- `pick(id, opts?)` — **delete and return** the record; throws `UnknownRecordError` if missing. Never use it for read-only checks
|
|
69
69
|
|
|
70
70
|
### `ResourceMaker<R, T>`
|
|
71
71
|
|
|
@@ -75,10 +75,31 @@ interface ResourceMaker<R extends ResourceRecord, T extends Resource<R> = Resour
|
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### Migration framework
|
|
79
|
+
|
|
80
|
+
Storage-agnostic and optional — a backend that supports code migrations (mongo, postgres)
|
|
81
|
+
extends `MigratableResource<Tx>`; backends with nothing to migrate simply don't (the same way
|
|
82
|
+
pub/sub is an optional capability of redis resources). Migrations run **automatically during
|
|
83
|
+
resource initialization** (app setup) — registering one is all an app ever does.
|
|
84
|
+
|
|
85
|
+
- `MigratableResource<Tx>` — `migration(name, apply, stage?): this` + `migrations()`
|
|
86
|
+
- `createMigrationRegistry<Tx>()` — per-alias ledger of registered migrations; declaration
|
|
87
|
+
order is application order; identical re-registration is a no-op, a changed body under a
|
|
88
|
+
used name throws `MigrationConflict`
|
|
89
|
+
- `MigrationStore<Tx>` — the *migration register* a database implements to track applied
|
|
90
|
+
migrations (`ensure` / `applied` / `baseline` / `run`); both mongo and postgres persist it
|
|
91
|
+
as `_owlmeans_migrations`
|
|
92
|
+
- `runMigrations(alias, registry, store, opts?)` — the storage-agnostic runner; with
|
|
93
|
+
`baseline: true` migrations are recorded instead of run (used on just-created structures);
|
|
94
|
+
`strictChecksum` (default) rejects edited applied bodies
|
|
95
|
+
- `MigrationStage.Pre` / `MigrationStage.Post` — run before / after the backend's structure
|
|
96
|
+
reconciliation
|
|
97
|
+
|
|
78
98
|
### Helpers
|
|
79
99
|
|
|
80
100
|
- `prepareListOptions(defPageSize, criteria?, opts?)` — normalize list criteria + pager
|
|
81
101
|
- `filterObject(obj, keep?)` — strip null/undefined fields from a record before saving
|
|
102
|
+
- `createDbService(alias, override, init?)` — base plumbing for `ResourceDbService` implementations
|
|
82
103
|
|
|
83
104
|
### Error Classes
|
|
84
105
|
|
|
@@ -86,6 +107,8 @@ interface ResourceMaker<R extends ResourceRecord, T extends Resource<R> = Resour
|
|
|
86
107
|
- `UnknownRecordError` — record not found (has `.id` getter)
|
|
87
108
|
- `RecordExists` — duplicate record on `create`
|
|
88
109
|
- `MisshapedRecord` — invalid record structure
|
|
110
|
+
- `MigrationError` — a migration body threw; initialization aborts
|
|
111
|
+
- `MigrationConflict` — an already applied migration's body changed, or a name was redeclared with a different body
|
|
89
112
|
|
|
90
113
|
## Related Packages
|
|
91
114
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "How to use @owlmeans/resource — generic resource abstraction (CRUD over
|
|
2
|
+
description: "How to use @owlmeans/resource — generic resource abstraction (CRUD over records) plus the storage-agnostic migration framework (MigratableResource, registry/store/runner). Use when implementing a custom resource or adding migration support to a database backend."
|
|
3
3
|
applyTo: "**/*.ts, **/*.tsx"
|
|
4
4
|
---
|
|
5
5
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -7,27 +7,37 @@ applyTo: "**/*.ts, **/*.tsx"
|
|
|
7
7
|
# @owlmeans/resource
|
|
8
8
|
|
|
9
9
|
**Layer:** Core
|
|
10
|
-
**Install:** `"@owlmeans/resource": "^0.1.
|
|
10
|
+
**Install:** `"@owlmeans/resource": "^0.1.16-rc.0"` in `dependencies`
|
|
11
11
|
|
|
12
12
|
## Key Exports
|
|
13
13
|
|
|
14
14
|
| Export | Description |
|
|
15
15
|
|--------|-------------|
|
|
16
|
-
| `Resource<T>`
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
16
|
+
| `Resource<T>` | Generic contract: get/load/list/save/create/update/delete/pick |
|
|
17
|
+
| `MigratableResource<Tx>` | Optional migration capability — `migration(name, apply, stage?)` + `migrations()`. Optional like pub/sub on redis: mongo/postgres extend it, others don't |
|
|
18
|
+
| `MigrationRegistry`, `MigrationStore`, `createMigrationRegistry`, `runMigrations`, `MigrationStage` | The framework: per-alias registry (declaration order = application order), the applied-tracking register a db implements, the storage-agnostic runner (`baseline` for fresh structures, strict checksums) |
|
|
19
|
+
| Errors | `UnknownRecordError`, `MisshapedRecord`, `RecordExists`, `RecordUpdateFailed`, `UnsupportedArgumentError`, `MigrationError`, `MigrationConflict` |
|
|
20
|
+
| `createDbService`, `prepareListOptions`, `DbConfig` | Service base, list helpers, `cfg.dbs` config type |
|
|
20
21
|
|
|
21
22
|
## Usage
|
|
22
23
|
|
|
23
24
|
```typescript
|
|
24
25
|
import type { Resource } from '@owlmeans/resource'
|
|
25
|
-
|
|
26
|
-
interface Project { id: string; name: string; entityId: string }
|
|
27
|
-
const projects = ctx.getResource<Resource<Project>>('projects')
|
|
26
|
+
const projects = ctx.resource<Resource<Project>>('projects')
|
|
28
27
|
const { items } = await projects.list({ entityId: 'abc' })
|
|
29
28
|
```
|
|
30
29
|
|
|
30
|
+
## Migration framework rules
|
|
31
|
+
|
|
32
|
+
- Migrations run **automatically on app setup** inside resource `init()` — apps only register.
|
|
33
|
+
- Each migration self-checks: registry skips ledger-recorded names; `baseline: true` records
|
|
34
|
+
instead of running on just-created structures; bodies are written idempotent.
|
|
35
|
+
- New backend support: extend the concrete interface with `MigratableResource<YourTx>`; keep
|
|
36
|
+
registrations in a module-scope declaration keyed by alias (context rebuilds must not lose
|
|
37
|
+
them); implement `MigrationStore` over a durable ledger; in `init()` probe → baseline|Pre →
|
|
38
|
+
reconcile structure → Post; make `run` atomic with the ledger where the db allows
|
|
39
|
+
(postgres tx), claim-then-complete where it doesn't (mongo).
|
|
40
|
+
|
|
31
41
|
## Depends On
|
|
32
42
|
|
|
33
|
-
- `@owlmeans/
|
|
43
|
+
- `@owlmeans/context`, `@noble/hashes` (body checksums)
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"package": "@owlmeans/resource",
|
|
4
|
-
"version": "0.1.
|
|
5
|
-
"generatedAt": "2026-08-
|
|
4
|
+
"version": "0.1.16-rc.0",
|
|
5
|
+
"generatedAt": "2026-08-11T14:02:34.972Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: resource
|
|
3
|
-
description: How to use @owlmeans/resource — generic resource abstraction (CRUD over
|
|
3
|
+
description: How to use @owlmeans/resource — generic resource abstraction (CRUD over records) plus the storage-agnostic migration framework (MigratableResource, migration registry/store/runner) used by mongo-resource, postgres-resource, redis-resource, state, storage-resource, etc. Auto-invoked when importing from this package, implementing a custom resource type, or adding migration support to a database backend.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,30 +8,67 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/resource
|
|
9
9
|
|
|
10
10
|
**Layer:** Core
|
|
11
|
-
**Install:** `"@owlmeans/resource": "^0.1.
|
|
11
|
+
**Install:** `"@owlmeans/resource": "^0.1.16-rc.0"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
## Key Exports
|
|
14
14
|
|
|
15
15
|
| Export | Description |
|
|
16
16
|
|--------|-------------|
|
|
17
|
-
| `Resource<T>`
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
17
|
+
| `Resource<T>` | Generic resource contract: `get`/`load`/`list`/`save`/`create`/`update`/`delete`/`pick`. |
|
|
18
|
+
| `MigratableResource<Tx>` | **Optional migration capability** — `migration(name, apply, stage?)` (chainable) + `migrations()`. Optional the way pub/sub is on redis resources: mongo and postgres extend it, backends with nothing to migrate don't. |
|
|
19
|
+
| `Migration`, `MigrationRegistry`, `MigrationStore`, `MigrationReport`, `MigrationRunOptions` | The framework's contracts. `MigrationStore` is the *migration register* a database implements to track applied migrations (`ensure`/`applied`/`baseline`/`run`). |
|
|
20
|
+
| `createMigrationRegistry<Tx>()` | Storage-agnostic ledger of code-registered migrations per alias. Declaration order = application order; re-registering an identical body is a no-op, a changed body under a used name throws `MigrationConflict`. |
|
|
21
|
+
| `runMigrations(alias, registry, store, opts?)` | Apply one stage's pending migrations through a store. `baseline: true` records instead of running (fresh structures); `strictChecksum` (default on) rejects edited applied bodies. |
|
|
22
|
+
| `MigrationStage` | `Pre` (before structure reconciliation — renames, casts, rescues) / `Post` (after — backfills into new structure). |
|
|
23
|
+
| `createDbService` | Base for `ResourceDbService` implementations (config/alias/name/client plumbing). |
|
|
24
|
+
| `prepareListOptions`, `filterObject`, `createListSchema` | List/criteria helpers. |
|
|
25
|
+
| Errors | `ResourceError`, `UnknownRecordError`, `MisshapedRecord`, `RecordExists`, `RecordUpdateFailed`, `UnsupportedArgumentError`, `UnsupportedMethodError`, `MigrationError`, `MigrationConflict`. |
|
|
26
|
+
| `DbConfig`, `Config`, `Context` | Database config (`cfg.dbs`) types. |
|
|
21
27
|
|
|
22
28
|
## Usage
|
|
23
29
|
|
|
24
|
-
A resource is a typed CRUD-over-records abstraction. Concrete implementations come from
|
|
30
|
+
A resource is a typed CRUD-over-records abstraction. Concrete implementations come from
|
|
31
|
+
`@owlmeans/mongo-resource`, `@owlmeans/postgres-resource`, `@owlmeans/redis-resource`,
|
|
32
|
+
`@owlmeans/state`, etc. Consumers register with the context and access via
|
|
33
|
+
`ctx.resource<T>(alias)`.
|
|
25
34
|
|
|
26
35
|
```typescript
|
|
27
36
|
import type { Resource } from '@owlmeans/resource'
|
|
28
37
|
|
|
29
38
|
interface Project { id: string; name: string; entityId: string }
|
|
30
|
-
const projects = ctx.
|
|
39
|
+
const projects = ctx.resource<Resource<Project>>('projects')
|
|
31
40
|
const { items } = await projects.list({ entityId: 'abc' })
|
|
32
41
|
```
|
|
33
42
|
|
|
43
|
+
## The migration framework
|
|
44
|
+
|
|
45
|
+
Migrations are **automatic on app setup**: a backend runs them inside the resource's
|
|
46
|
+
`init()` — registering one is all an app ever does. Every migration self-describes when it
|
|
47
|
+
applies: the registry skips names the store has recorded, `baseline` covers structures
|
|
48
|
+
created already at head, and bodies are written idempotent so an interrupted run can repeat.
|
|
49
|
+
|
|
50
|
+
Adding migration support to a new database backend:
|
|
51
|
+
|
|
52
|
+
1. Extend the concrete resource interface with `MigratableResource<YourTx>` and design the
|
|
53
|
+
`Tx` façade a migration receives.
|
|
54
|
+
2. Keep registrations in a **module-scope declaration keyed by alias** (see either
|
|
55
|
+
implementation's `declarations.ts`) — `reinitializeContext` rebuilds resource objects, and
|
|
56
|
+
a registry lost to a rebuild silently loses data transformations.
|
|
57
|
+
3. Implement `MigrationStore` over a durable ledger when the database can store one
|
|
58
|
+
(`_owlmeans_migrations` in both mongo and postgres); a store-less backend may run
|
|
59
|
+
migrations unconditionally if its bodies are self-checking.
|
|
60
|
+
4. In the resource's `init()`: probe structure → absent: `runMigrations(..., { baseline: true })`;
|
|
61
|
+
present: run `Pre` → reconcile structure → run `Post`.
|
|
62
|
+
5. Make `run` atomic with the ledger write where the database allows it (postgres: same
|
|
63
|
+
transaction). Where it doesn't (mongo: no multi-doc transactions on standalone), use
|
|
64
|
+
claim-then-complete on a unique `(alias, name)` key and require idempotent bodies.
|
|
65
|
+
|
|
34
66
|
## Depends On
|
|
35
67
|
|
|
36
|
-
- `@owlmeans/
|
|
37
|
-
- `@
|
|
68
|
+
- `@owlmeans/context` — service/resource lifecycle
|
|
69
|
+
- `@noble/hashes` — migration body checksums
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- [[mongo-resource]] · [[postgres-resource]] — the two `MigratableResource` implementations
|
|
74
|
+
- [[redis-resource]] — a non-migratable backend (pub/sub capability instead)
|
package/build/consts.d.ts
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv';
|
|
2
2
|
import type { ListResult, ResourceRecord } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* When a migration runs relative to the structure reconciliation a db implementation
|
|
5
|
+
* performs at resource initialization.
|
|
6
|
+
*
|
|
7
|
+
* - `pre` — before the structure is reconciled. This is where a change reconciliation
|
|
8
|
+
* cannot express correctly belongs: renames, casts that need a `USING` expression,
|
|
9
|
+
* backfills that have to precede a `NOT NULL`. Once the migration has reshaped the
|
|
10
|
+
* structure, reconciliation observes no drift and emits nothing — the two mechanisms
|
|
11
|
+
* never fight because the migration pre-empts the reconciler.
|
|
12
|
+
* - `post` — after reconciliation. For data backfills into columns reconciliation has
|
|
13
|
+
* just created.
|
|
14
|
+
*/
|
|
15
|
+
export declare enum MigrationStage {
|
|
16
|
+
Pre = "pre",
|
|
17
|
+
Post = "post"
|
|
18
|
+
}
|
|
3
19
|
export declare const createListSchema: <T extends ResourceRecord>(schema: JSONSchemaType<T>) => JSONSchemaType<ListResult<T>>;
|
|
4
20
|
//# sourceMappingURL=consts.d.ts.map
|
package/build/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5D,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,cAAc,EAAE,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CA6CjH,CAAA"}
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE5D;;;;;;;;;;;GAWG;AACH,oBAAY,cAAc;IACxB,GAAG,QAAQ;IACX,IAAI,SAAS;CACd;AAED,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,cAAc,EAAE,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CA6CjH,CAAA"}
|
package/build/consts.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a migration runs relative to the structure reconciliation a db implementation
|
|
3
|
+
* performs at resource initialization.
|
|
4
|
+
*
|
|
5
|
+
* - `pre` — before the structure is reconciled. This is where a change reconciliation
|
|
6
|
+
* cannot express correctly belongs: renames, casts that need a `USING` expression,
|
|
7
|
+
* backfills that have to precede a `NOT NULL`. Once the migration has reshaped the
|
|
8
|
+
* structure, reconciliation observes no drift and emits nothing — the two mechanisms
|
|
9
|
+
* never fight because the migration pre-empts the reconciler.
|
|
10
|
+
* - `post` — after reconciliation. For data backfills into columns reconciliation has
|
|
11
|
+
* just created.
|
|
12
|
+
*/
|
|
13
|
+
export var MigrationStage;
|
|
14
|
+
(function (MigrationStage) {
|
|
15
|
+
MigrationStage["Pre"] = "pre";
|
|
16
|
+
MigrationStage["Post"] = "post";
|
|
17
|
+
})(MigrationStage || (MigrationStage = {}));
|
|
1
18
|
export const createListSchema = (schema) => ({
|
|
2
19
|
type: 'object',
|
|
3
20
|
properties: {
|
package/build/consts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAA2B,MAAyB,EAAiC,EAAE,CAAC,CAAC;IACvH,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE;gBACL,GAAG,MAAM;gBACT,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;oBACtC,GAAG,MAAM,CAAC,UAAU;iBACrB;gBACD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBACrE,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE;wBACL,KAAK,EAAE;4BACL,EAAE,IAAI,EAAE,QAAQ,EAAE;4BAClB;gCACE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;oCACpB,EAAE,IAAI,EAAE,QAAQ,EAAE;oCAClB,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;iCACpC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;6BAC5B;yBACF;qBACF;oBACD,QAAQ,EAAE,IAAI;iBACf;gBACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACxC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACxC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;aAC1C;YACD,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE,IAAI;YACd,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD,QAAQ,EAAE,CAAC,OAAO,CAAC;IACnB,oBAAoB,EAAE,KAAK;CAC5B,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,6BAAW,CAAA;IACX,+BAAa,CAAA;AACf,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAA2B,MAAyB,EAAiC,EAAE,CAAC,CAAC;IACvH,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE;gBACL,GAAG,MAAM;gBACT,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;oBACtC,GAAG,MAAM,CAAC,UAAU;iBACrB;gBACD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBACrE,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE;wBACL,KAAK,EAAE;4BACL,EAAE,IAAI,EAAE,QAAQ,EAAE;4BAClB;gCACE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;oCACpB,EAAE,IAAI,EAAE,QAAQ,EAAE;oCAClB,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;iCACpC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;6BAC5B;yBACF;qBACF;oBACD,QAAQ,EAAE,IAAI;iBACf;gBACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACxC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACxC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;aAC1C;YACD,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE,IAAI;YACd,oBAAoB,EAAE,KAAK;SAC5B;KACF;IACD,QAAQ,EAAE,CAAC,OAAO,CAAC;IACnB,oBAAoB,EAAE,KAAK;CAC5B,CAAC,CAAA"}
|
package/build/errors.d.ts
CHANGED
|
@@ -29,4 +29,22 @@ export declare class UnsupportedMethodError extends ResourceError {
|
|
|
29
29
|
static typeName: string;
|
|
30
30
|
constructor(method: string);
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* A registered migration threw while being applied. Resource initialization aborts —
|
|
34
|
+
* a structure problem has to fail the boot loudly rather than leave the process
|
|
35
|
+
* running against a half-shaped database.
|
|
36
|
+
*/
|
|
37
|
+
export declare class MigrationError extends ResourceError {
|
|
38
|
+
static typeName: string;
|
|
39
|
+
constructor(msg: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A migration name was registered twice with different bodies, or an already applied
|
|
43
|
+
* migration's body has changed since it ran. Write a new migration instead of editing
|
|
44
|
+
* one that has already been applied somewhere.
|
|
45
|
+
*/
|
|
46
|
+
export declare class MigrationConflict extends ResourceError {
|
|
47
|
+
static typeName: string;
|
|
48
|
+
constructor(msg: string);
|
|
49
|
+
}
|
|
32
50
|
//# sourceMappingURL=errors.d.ts.map
|
package/build/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAG,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEjD,qBAAa,aAAc,SAAQ,cAAc;IAC/C,OAAuB,QAAQ,SAAkB;gBAErC,OAAO,GAAE,MAAgB;CAGtC;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,SAAgD;IAE/E,gBAAuB,WAAW,EAAE,MAAM,CAAM;gBAEpC,EAAE,EAAE,MAAM;IAKtB,IAAI,EAAE,IAAI,MAAM,CAEf;CACF;AAED,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAuB,QAAQ,SAA6C;gBAEhE,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,OAAuB,QAAQ,SAA0C;gBAE7D,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,SAAgD;gBAEnE,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,wBAAyB,SAAQ,aAAa;IACzD,OAAuB,QAAQ,SAAsD;gBAEzE,QAAQ,EAAE,MAAM;CAI7B;AAED,qBAAa,sBAAuB,SAAQ,aAAa;IACvD,OAAuB,QAAQ,SAAoD;gBAEvE,MAAM,EAAE,MAAM;CAI3B"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAG,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEjD,qBAAa,aAAc,SAAQ,cAAc;IAC/C,OAAuB,QAAQ,SAAkB;gBAErC,OAAO,GAAE,MAAgB;CAGtC;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,SAAgD;IAE/E,gBAAuB,WAAW,EAAE,MAAM,CAAM;gBAEpC,EAAE,EAAE,MAAM;IAKtB,IAAI,EAAE,IAAI,MAAM,CAEf;CACF;AAED,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAuB,QAAQ,SAA6C;gBAEhE,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,YAAa,SAAQ,aAAa;IAC7C,OAAuB,QAAQ,SAA0C;gBAE7D,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,OAAuB,QAAQ,SAAgD;gBAEnE,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,wBAAyB,SAAQ,aAAa;IACzD,OAAuB,QAAQ,SAAsD;gBAEzE,QAAQ,EAAE,MAAM;CAI7B;AAED,qBAAa,sBAAuB,SAAQ,aAAa;IACvD,OAAuB,QAAQ,SAAoD;gBAEvE,MAAM,EAAE,MAAM;CAI3B;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,aAAa;IAC/C,OAAuB,QAAQ,SAA4C;gBAE/D,GAAG,EAAE,MAAM;CAIxB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,aAAa;IAClD,OAAuB,QAAQ,SAA+C;gBAElE,GAAG,EAAE,MAAM;CAIxB"}
|
package/build/errors.js
CHANGED
|
@@ -51,6 +51,30 @@ export class UnsupportedMethodError extends ResourceError {
|
|
|
51
51
|
this.type = UnsupportedMethodError.typeName;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* A registered migration threw while being applied. Resource initialization aborts —
|
|
56
|
+
* a structure problem has to fail the boot loudly rather than leave the process
|
|
57
|
+
* running against a half-shaped database.
|
|
58
|
+
*/
|
|
59
|
+
export class MigrationError extends ResourceError {
|
|
60
|
+
static typeName = `${ResourceError.typeName}MigrationError`;
|
|
61
|
+
constructor(msg) {
|
|
62
|
+
super(`migration-failed:${msg}`);
|
|
63
|
+
this.type = MigrationError.typeName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A migration name was registered twice with different bodies, or an already applied
|
|
68
|
+
* migration's body has changed since it ran. Write a new migration instead of editing
|
|
69
|
+
* one that has already been applied somewhere.
|
|
70
|
+
*/
|
|
71
|
+
export class MigrationConflict extends ResourceError {
|
|
72
|
+
static typeName = `${ResourceError.typeName}MigrationConflict`;
|
|
73
|
+
constructor(msg) {
|
|
74
|
+
super(`migration-conflict:${msg}`);
|
|
75
|
+
this.type = MigrationConflict.typeName;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
54
78
|
ResilientError.registerErrorClass(ResourceError);
|
|
55
79
|
ResilientError.registerErrorClass(UnknownRecordError);
|
|
56
80
|
ResilientError.registerErrorClass(MisshapedRecord);
|
|
@@ -58,4 +82,6 @@ ResilientError.registerErrorClass(RecordExists);
|
|
|
58
82
|
ResilientError.registerErrorClass(RecordUpdateFailed);
|
|
59
83
|
ResilientError.registerErrorClass(UnsupportedArgumentError);
|
|
60
84
|
ResilientError.registerErrorClass(UnsupportedMethodError);
|
|
85
|
+
ResilientError.registerErrorClass(MigrationError);
|
|
86
|
+
ResilientError.registerErrorClass(MigrationConflict);
|
|
61
87
|
//# sourceMappingURL=errors.js.map
|
package/build/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAG,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEjD,MAAM,OAAO,aAAc,SAAQ,cAAc;IACxC,MAAM,CAAU,QAAQ,GAAG,eAAe,CAAA;IAEjD,YAAY,UAAkB,OAAO;QACnC,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,YAAY,OAAO,EAAE,CAAC,CAAA;IACtD,CAAC;;AAGH,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAC5C,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,oBAAoB,CAAA;IAExE,MAAM,CAAU,WAAW,GAAW,GAAG,CAAA;IAEhD,YAAY,EAAU;QACpB,KAAK,CAAC,iBAAiB,kBAAkB,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC,CAAA;QAC7D,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAA;IACzC,CAAC;IAED,IAAI,EAAE;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9D,CAAC;;AAGH,MAAM,OAAO,eAAgB,SAAQ,aAAa;IACzC,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,iBAAiB,CAAA;IAE5E,YAAY,GAAW;QACrB,KAAK,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAA;QAChC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAA;IACtC,CAAC;;AAGH,MAAM,OAAO,YAAa,SAAQ,aAAa;IACtC,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,cAAc,CAAA;IAEzE,YAAY,GAAW;QACrB,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAA;QAC7B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAA;IACnC,CAAC;;AAGH,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAC5C,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,oBAAoB,CAAA;IAE/E,YAAY,GAAW;QACrB,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAA;IACzC,CAAC;;AAGH,MAAM,OAAO,wBAAyB,SAAQ,aAAa;IAClD,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,0BAA0B,CAAA;IAErF,YAAY,QAAgB;QAC1B,KAAK,CAAC,wBAAwB,QAAQ,EAAE,CAAC,CAAA;QACzC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC,QAAQ,CAAA;IAC/C,CAAC;;AAGH,MAAM,OAAO,sBAAuB,SAAQ,aAAa;IAChD,MAAM,CAAU,QAAQ,GAAG,GAAG,aAAa,CAAC,QAAQ,wBAAwB,CAAA;IAEnF,YAAY,MAAc;QACxB,KAAK,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAA;QACrC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC,QAAQ,CAAA;IAC7C,CAAC;;AAGH,cAAc,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAA;AAChD,cAAc,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAA;AACrD,cAAc,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAA;AAClD,cAAc,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAA;AAC/C,cAAc,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAA;AACrD,cAAc,CAAC,kBAAkB,CAAC,wBAAwB,CAAC,CAAA;AAC3D,cAAc,CAAC,kBAAkB,CAAC,sBAAsB,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/build/helper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAa,MAAM,YAAY,CAAA;AAEtE,eAAO,MAAM,kBAAkB,GAAI,cAAa,MAAW,EAAE,WAAW,WAAW,GAAG,YAAY,EAAE,OAAO,WAAW,KAAG,
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAa,MAAM,YAAY,CAAA;AAEtE,eAAO,MAAM,kBAAkB,GAAI,cAAa,MAAW,EAAE,WAAW,WAAW,GAAG,YAAY,EAAE,OAAO,WAAW,KAAG,WAwBxH,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,MAAM,EAAE,KAAG,CAMxD,CAAA"}
|
package/build/helper.js
CHANGED
|
@@ -4,13 +4,14 @@ export const prepareListOptions = (defPageSize = 10, criteria, opts) => {
|
|
|
4
4
|
criteria = {};
|
|
5
5
|
opts = {};
|
|
6
6
|
}
|
|
7
|
-
else if ('criteria' in criteria) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
else if ('criteria' in criteria || 'pager' in criteria) {
|
|
8
|
+
/**
|
|
9
|
+
* Both keys have to be read off the original object before it's reassigned —
|
|
10
|
+
* reading `pager` after narrowing to `.criteria` silently drops the pager.
|
|
11
|
+
*/
|
|
12
|
+
const options = criteria;
|
|
13
|
+
opts = { pager: options.pager };
|
|
14
|
+
criteria = (options.criteria ?? {});
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
const size = (opts?.pager?.size ?? defPageSize);
|
package/build/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,cAAsB,EAAE,EAAE,QAAqC,EAAE,IAAkB,EAAe,EAAE;IACrI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,QAAQ,GAAG,EAAE,CAAA;YACb,IAAI,GAAG,EAAE,CAAA;QACX,CAAC;aAAM,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,cAAsB,EAAE,EAAE,QAAqC,EAAE,IAAkB,EAAe,EAAE;IACrI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,QAAQ,GAAG,EAAE,CAAA;YACb,IAAI,GAAG,EAAE,CAAA;QACX,CAAC;aAAM,IAAI,UAAU,IAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;YACzD;;;eAGG;YACH,MAAM,OAAO,GAAG,QAAuB,CAAA;YACvC,IAAI,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAA;YAC/B,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAiB,CAAA;QACrD,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,WAAW,CAAC,CAAA;IAC/C,MAAM,KAAK,GAAc;QACvB,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI;QACvB,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;QAC5B,IAAI,EAAE,KAAK,EAAE,CAAC;KACf,CAAA;IAED,OAAO,EAAE,QAAQ,EAAE,QAAwB,EAAE,KAAK,EAAE,CAAA;AACtD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAe,GAAM,EAAE,IAAe,EAAK,EAAE,CACvE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;IAC/C,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,OAAO,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAA;AACjC,CAAC,EAAE,EAAO,CAAC,CAAA"}
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,mBAAmB,YAAY,CAAA"}
|
package/build/index.js
CHANGED
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MigrationRegistry, MigrationReport, MigrationRunOptions, MigrationStore } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Storage agnostic ledger of code registered migrations for a single resource alias.
|
|
4
|
+
*
|
|
5
|
+
* Declaration order is the application order — the registry never sorts by name, so a
|
|
6
|
+
* migration that depends on an earlier one only has to be registered after it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createMigrationRegistry: <Tx = unknown>() => MigrationRegistry<Tx>;
|
|
9
|
+
/**
|
|
10
|
+
* Apply the pending migrations of one stage for one resource alias.
|
|
11
|
+
*
|
|
12
|
+
* With `baseline: true` the structure has just been created from the current schema, so
|
|
13
|
+
* every registered migration is already satisfied by it — they're recorded as applied
|
|
14
|
+
* without running. Replaying a historical `ADD COLUMN` against a table that was just
|
|
15
|
+
* created with that column would fail, and replaying a backfill would scan for nothing.
|
|
16
|
+
*
|
|
17
|
+
* @throws {MigrationError} a migration threw — initialization must abort.
|
|
18
|
+
* @throws {MigrationConflict} an already applied migration's body has changed.
|
|
19
|
+
*/
|
|
20
|
+
export declare const runMigrations: <Tx = unknown>(alias: string, registry: MigrationRegistry<Tx>, store: MigrationStore<Tx>, opts?: MigrationRunOptions) => Promise<MigrationReport>;
|
|
21
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACC,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,cAAc,EACnF,MAAM,YAAY,CAAA;AAqBnB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,EAAE,GAAG,OAAO,OAAK,iBAAiB,CAAC,EAAE,CA+B5E,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAU,EAAE,GAAG,OAAO,EAC9C,OAAO,MAAM,EACb,UAAU,iBAAiB,CAAC,EAAE,CAAC,EAC/B,OAAO,cAAc,CAAC,EAAE,CAAC,EACzB,OAAO,mBAAmB,KACzB,OAAO,CAAC,eAAe,CAqDzB,CAAA"}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext, BasicResource, LazyService } from '@owlmeans/context';
|
|
2
|
+
import type { MigrationStage } from './consts.js';
|
|
2
3
|
export interface Resource<T extends ResourceRecord> extends BasicResource {
|
|
3
4
|
/**
|
|
4
5
|
* @throws {UnknownRecordError}
|
|
@@ -101,6 +102,73 @@ export interface DbConfig<P extends {} = {}> {
|
|
|
101
102
|
encryptionKey?: string;
|
|
102
103
|
meta?: P;
|
|
103
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Optional migration capability of a Resource implementation — optional the same way
|
|
107
|
+
* pub/sub is on redis resources: a backend that supports code migrations extends this
|
|
108
|
+
* interface (mongo, postgres), a backend with nothing to migrate simply never implements
|
|
109
|
+
* it. The base {@link Resource} contract stays migration free.
|
|
110
|
+
*
|
|
111
|
+
* The capability is automatic: implementations run the registered migrations during
|
|
112
|
+
* resource initialization (app setup), so a migration only has to be registered — never
|
|
113
|
+
* invoked. Backends with a durable structure also implement {@link MigrationStore}, the
|
|
114
|
+
* register that tracks which migrations have been applied.
|
|
115
|
+
*/
|
|
116
|
+
export interface MigratableResource<Tx = unknown> {
|
|
117
|
+
/**
|
|
118
|
+
* Register a migration, applied once per database in declaration order.
|
|
119
|
+
*
|
|
120
|
+
* Chainable and idempotent: re-registering the same name with the same body is a no-op,
|
|
121
|
+
* which is what makes it safe to call from a resource maker that `reinitializeContext`
|
|
122
|
+
* re-runs. Re-registering a *changed* body under a used name throws
|
|
123
|
+
* {@link MigrationConflict}.
|
|
124
|
+
*/
|
|
125
|
+
migration: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) => this;
|
|
126
|
+
/** The registered migrations for this resource's alias. Read-only; use {@link MigratableResource.migration}. */
|
|
127
|
+
migrations: () => MigrationRegistry<Tx>;
|
|
128
|
+
}
|
|
129
|
+
export interface Migration<Tx = unknown> {
|
|
130
|
+
name: string;
|
|
131
|
+
stage: MigrationStage;
|
|
132
|
+
apply: (tx: Tx) => Promise<void>;
|
|
133
|
+
/** Stable hash of the migration body — detects edits to an already applied migration. */
|
|
134
|
+
checksum: string;
|
|
135
|
+
}
|
|
136
|
+
export interface MigrationRegistry<Tx = unknown> {
|
|
137
|
+
register: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) => MigrationRegistry<Tx>;
|
|
138
|
+
list: (stage?: MigrationStage) => Migration<Tx>[];
|
|
139
|
+
has: (name: string) => boolean;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The database specific half of the migration subsystem. Postgres and Mongo each
|
|
143
|
+
* provide an implementation; {@link runMigrations} stays storage agnostic.
|
|
144
|
+
*/
|
|
145
|
+
export interface MigrationStore<Tx = unknown> {
|
|
146
|
+
/** Create the tracking structure if it's absent. */
|
|
147
|
+
ensure: () => Promise<void>;
|
|
148
|
+
/** Names already applied for the given resource alias, mapped to their recorded checksum. */
|
|
149
|
+
applied: (alias: string) => Promise<Record<string, string | null>>;
|
|
150
|
+
/** Record migrations as applied *without* running them — used on freshly created structures. */
|
|
151
|
+
baseline: (alias: string, migrations: Migration<Tx>[]) => Promise<void>;
|
|
152
|
+
/** Run one migration and record it atomically. */
|
|
153
|
+
run: (alias: string, migration: Migration<Tx>) => Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
export interface MigrationRunOptions {
|
|
156
|
+
stage?: MigrationStage;
|
|
157
|
+
/**
|
|
158
|
+
* The structure has just been created, so every registered migration is already
|
|
159
|
+
* satisfied by it — record them instead of running them.
|
|
160
|
+
*/
|
|
161
|
+
baseline?: boolean;
|
|
162
|
+
/** Throw when an already applied migration's body has changed. Defaults to `true`. */
|
|
163
|
+
strictChecksum?: boolean;
|
|
164
|
+
}
|
|
165
|
+
export interface MigrationReport {
|
|
166
|
+
alias: string;
|
|
167
|
+
stage: MigrationStage;
|
|
168
|
+
applied: string[];
|
|
169
|
+
baselined: string[];
|
|
170
|
+
skipped: string[];
|
|
171
|
+
}
|
|
104
172
|
export interface Config extends BasicConfig {
|
|
105
173
|
dbs?: DbConfig[];
|
|
106
174
|
}
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/resource",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16-rc.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@noble/hashes": "^1.5.0",
|
|
25
|
-
"@owlmeans/context": "^0.1.
|
|
26
|
-
"@owlmeans/error": "^0.1.
|
|
25
|
+
"@owlmeans/context": "^0.1.16-rc.0",
|
|
26
|
+
"@owlmeans/error": "^0.1.16-rc.0",
|
|
27
27
|
"@scure/base": "^1.1.9"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
package/src/consts.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv'
|
|
2
2
|
import type { ListResult, ResourceRecord } from './types.js'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* When a migration runs relative to the structure reconciliation a db implementation
|
|
6
|
+
* performs at resource initialization.
|
|
7
|
+
*
|
|
8
|
+
* - `pre` — before the structure is reconciled. This is where a change reconciliation
|
|
9
|
+
* cannot express correctly belongs: renames, casts that need a `USING` expression,
|
|
10
|
+
* backfills that have to precede a `NOT NULL`. Once the migration has reshaped the
|
|
11
|
+
* structure, reconciliation observes no drift and emits nothing — the two mechanisms
|
|
12
|
+
* never fight because the migration pre-empts the reconciler.
|
|
13
|
+
* - `post` — after reconciliation. For data backfills into columns reconciliation has
|
|
14
|
+
* just created.
|
|
15
|
+
*/
|
|
16
|
+
export enum MigrationStage {
|
|
17
|
+
Pre = 'pre',
|
|
18
|
+
Post = 'post'
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
export const createListSchema = <T extends ResourceRecord>(schema: JSONSchemaType<T>): JSONSchemaType<ListResult<T>> => ({
|
|
5
22
|
type: 'object',
|
|
6
23
|
properties: {
|
package/src/errors.ts
CHANGED
|
@@ -69,6 +69,34 @@ export class UnsupportedMethodError extends ResourceError {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* A registered migration threw while being applied. Resource initialization aborts —
|
|
74
|
+
* a structure problem has to fail the boot loudly rather than leave the process
|
|
75
|
+
* running against a half-shaped database.
|
|
76
|
+
*/
|
|
77
|
+
export class MigrationError extends ResourceError {
|
|
78
|
+
public static override typeName = `${ResourceError.typeName}MigrationError`
|
|
79
|
+
|
|
80
|
+
constructor(msg: string) {
|
|
81
|
+
super(`migration-failed:${msg}`)
|
|
82
|
+
this.type = MigrationError.typeName
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A migration name was registered twice with different bodies, or an already applied
|
|
88
|
+
* migration's body has changed since it ran. Write a new migration instead of editing
|
|
89
|
+
* one that has already been applied somewhere.
|
|
90
|
+
*/
|
|
91
|
+
export class MigrationConflict extends ResourceError {
|
|
92
|
+
public static override typeName = `${ResourceError.typeName}MigrationConflict`
|
|
93
|
+
|
|
94
|
+
constructor(msg: string) {
|
|
95
|
+
super(`migration-conflict:${msg}`)
|
|
96
|
+
this.type = MigrationConflict.typeName
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
72
100
|
ResilientError.registerErrorClass(ResourceError)
|
|
73
101
|
ResilientError.registerErrorClass(UnknownRecordError)
|
|
74
102
|
ResilientError.registerErrorClass(MisshapedRecord)
|
|
@@ -76,3 +104,5 @@ ResilientError.registerErrorClass(RecordExists)
|
|
|
76
104
|
ResilientError.registerErrorClass(RecordUpdateFailed)
|
|
77
105
|
ResilientError.registerErrorClass(UnsupportedArgumentError)
|
|
78
106
|
ResilientError.registerErrorClass(UnsupportedMethodError)
|
|
107
|
+
ResilientError.registerErrorClass(MigrationError)
|
|
108
|
+
ResilientError.registerErrorClass(MigrationConflict)
|
package/src/helper.ts
CHANGED
|
@@ -5,12 +5,14 @@ export const prepareListOptions = (defPageSize: number = 10, criteria?: ListOpti
|
|
|
5
5
|
if (criteria == null) {
|
|
6
6
|
criteria = {}
|
|
7
7
|
opts = {}
|
|
8
|
-
} else if ('criteria' in criteria) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
} else if ('criteria' in criteria || 'pager' in criteria) {
|
|
9
|
+
/**
|
|
10
|
+
* Both keys have to be read off the original object before it's reassigned —
|
|
11
|
+
* reading `pager` after narrowing to `.criteria` silently drops the pager.
|
|
12
|
+
*/
|
|
13
|
+
const options = criteria as ListOptions
|
|
14
|
+
opts = { pager: options.pager }
|
|
15
|
+
criteria = (options.criteria ?? {}) as ListCriteria
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
package/src/index.ts
CHANGED
package/src/migration.ts
ADDED
|
Binary file
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext, BasicResource, LazyService } from '@owlmeans/context'
|
|
2
|
+
import type { MigrationStage } from './consts.js'
|
|
2
3
|
|
|
3
4
|
export interface Resource<T extends ResourceRecord> extends BasicResource {
|
|
4
5
|
/**
|
|
@@ -121,6 +122,79 @@ export interface DbConfig<P extends {} = {}> {
|
|
|
121
122
|
meta?: P
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Optional migration capability of a Resource implementation — optional the same way
|
|
127
|
+
* pub/sub is on redis resources: a backend that supports code migrations extends this
|
|
128
|
+
* interface (mongo, postgres), a backend with nothing to migrate simply never implements
|
|
129
|
+
* it. The base {@link Resource} contract stays migration free.
|
|
130
|
+
*
|
|
131
|
+
* The capability is automatic: implementations run the registered migrations during
|
|
132
|
+
* resource initialization (app setup), so a migration only has to be registered — never
|
|
133
|
+
* invoked. Backends with a durable structure also implement {@link MigrationStore}, the
|
|
134
|
+
* register that tracks which migrations have been applied.
|
|
135
|
+
*/
|
|
136
|
+
export interface MigratableResource<Tx = unknown> {
|
|
137
|
+
/**
|
|
138
|
+
* Register a migration, applied once per database in declaration order.
|
|
139
|
+
*
|
|
140
|
+
* Chainable and idempotent: re-registering the same name with the same body is a no-op,
|
|
141
|
+
* which is what makes it safe to call from a resource maker that `reinitializeContext`
|
|
142
|
+
* re-runs. Re-registering a *changed* body under a used name throws
|
|
143
|
+
* {@link MigrationConflict}.
|
|
144
|
+
*/
|
|
145
|
+
migration: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) => this
|
|
146
|
+
/** The registered migrations for this resource's alias. Read-only; use {@link MigratableResource.migration}. */
|
|
147
|
+
migrations: () => MigrationRegistry<Tx>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface Migration<Tx = unknown> {
|
|
151
|
+
name: string
|
|
152
|
+
stage: MigrationStage
|
|
153
|
+
apply: (tx: Tx) => Promise<void>
|
|
154
|
+
/** Stable hash of the migration body — detects edits to an already applied migration. */
|
|
155
|
+
checksum: string
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface MigrationRegistry<Tx = unknown> {
|
|
159
|
+
register: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) => MigrationRegistry<Tx>
|
|
160
|
+
list: (stage?: MigrationStage) => Migration<Tx>[]
|
|
161
|
+
has: (name: string) => boolean
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The database specific half of the migration subsystem. Postgres and Mongo each
|
|
166
|
+
* provide an implementation; {@link runMigrations} stays storage agnostic.
|
|
167
|
+
*/
|
|
168
|
+
export interface MigrationStore<Tx = unknown> {
|
|
169
|
+
/** Create the tracking structure if it's absent. */
|
|
170
|
+
ensure: () => Promise<void>
|
|
171
|
+
/** Names already applied for the given resource alias, mapped to their recorded checksum. */
|
|
172
|
+
applied: (alias: string) => Promise<Record<string, string | null>>
|
|
173
|
+
/** Record migrations as applied *without* running them — used on freshly created structures. */
|
|
174
|
+
baseline: (alias: string, migrations: Migration<Tx>[]) => Promise<void>
|
|
175
|
+
/** Run one migration and record it atomically. */
|
|
176
|
+
run: (alias: string, migration: Migration<Tx>) => Promise<void>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface MigrationRunOptions {
|
|
180
|
+
stage?: MigrationStage
|
|
181
|
+
/**
|
|
182
|
+
* The structure has just been created, so every registered migration is already
|
|
183
|
+
* satisfied by it — record them instead of running them.
|
|
184
|
+
*/
|
|
185
|
+
baseline?: boolean
|
|
186
|
+
/** Throw when an already applied migration's body has changed. Defaults to `true`. */
|
|
187
|
+
strictChecksum?: boolean
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface MigrationReport {
|
|
191
|
+
alias: string
|
|
192
|
+
stage: MigrationStage
|
|
193
|
+
applied: string[]
|
|
194
|
+
baselined: string[]
|
|
195
|
+
skipped: string[]
|
|
196
|
+
}
|
|
197
|
+
|
|
124
198
|
export interface Config extends BasicConfig {
|
|
125
199
|
dbs?: DbConfig[]
|
|
126
200
|
}
|