@xemahq/biome-database-nest 1.0.0 → 1.0.1
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 +23 -117
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,120 +1,16 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<svg width="680" height="120" viewBox="0 0 680 120" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="@xemahq/biome-database-nest">
|
|
3
|
-
<rect width="680" height="120" rx="14" fill="#0B1020"/>
|
|
4
|
-
<g transform="translate(28,34)">
|
|
5
|
-
<path d="M26 0 L52 15 L52 45 L26 60 L0 45 L0 15 Z" fill="#8B5CF6" opacity="0.18"/>
|
|
6
|
-
<path d="M26 12 L41 21 L41 39 L26 48 L11 39 L11 21 Z" fill="#8B5CF6"/>
|
|
7
|
-
</g>
|
|
8
|
-
<text x="92" y="52" font-family="ui-monospace,SFMono-Regular,Menlo,monospace" font-size="22" fill="#F8FAFC" font-weight="700">@xemahq/biome-database-nest</text>
|
|
9
|
-
<text x="92" y="80" font-family="ui-sans-serif,system-ui,sans-serif" font-size="15" fill="#94A3B8">Explicit database ownership and runtime wiring for Xema biomes</text>
|
|
10
|
-
<text x="652" y="105" text-anchor="end" font-family="ui-sans-serif,system-ui,sans-serif" font-size="12" fill="#475569">xema.dev</text>
|
|
11
|
-
</svg>
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
<p align="center">
|
|
15
|
-
<a href="https://xema.dev">Website</a> ·
|
|
16
|
-
<a href="https://www.npmjs.com/package/@xemahq/biome-database-nest">npm</a>
|
|
17
|
-
</p>
|
|
18
|
-
|
|
19
|
-
<p align="center">
|
|
20
|
-
<img alt="npm" src="https://img.shields.io/npm/v/%40xemahq%2Fbiome-database-nest?color=2563eb&label=npm">
|
|
21
|
-
<img alt="license" src="https://img.shields.io/npm/l/%40xemahq%2Fbiome-database-nest?color=10b981">
|
|
22
|
-
<img alt="types" src="https://img.shields.io/npm/types/%40xemahq%2Fbiome-database-nest?color=3178c6">
|
|
23
|
-
</p>
|
|
24
|
-
|
|
25
1
|
# @xemahq/biome-database-nest
|
|
26
2
|
|
|
27
3
|
> Explicit database ownership and runtime wiring for Xema biomes
|
|
28
4
|
|
|
29
5
|
## Overview
|
|
30
6
|
|
|
31
|
-
Gives a
|
|
32
|
-
deployment environment. It supplies schema-qualified Prisma wiring, validates
|
|
33
|
-
image/schema compatibility, and provides a governed lifecycle for data
|
|
34
|
-
cutovers that require an operator between expansion and contraction.
|
|
7
|
+
Gives a biome service its own Postgres schema with schema-qualified Prisma wiring. It runs the service's migrations at boot, verifies the schema matches the image, and renews the short-lived runtime credential before it expires. It also provides an operator lifecycle for data cutovers where a destructive contraction must wait for a verified backfill.
|
|
35
8
|
|
|
36
9
|
## When to use it
|
|
37
10
|
|
|
38
11
|
- Use it when a NestJS biome service stores data or owns database migrations.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
DML-only runtime credential that the module renews before it expires.
|
|
42
|
-
An expired runtime credential refuses new connections and fails readiness.
|
|
43
|
-
- A local `DB_*` override is a development path and is refused when
|
|
44
|
-
`NODE_ENV=production`.
|
|
45
|
-
- Every Postgres session must run in UTC. The database or role default must be
|
|
46
|
-
`ALTER DATABASE … SET timezone TO 'UTC'`; the package only verifies it
|
|
47
|
-
(`SHOW TimeZone`) on the app pool, the bootstrap and operator migrations, and
|
|
48
|
-
refuses to start with `DatabaseSessionTimeZoneError` otherwise. It never sets
|
|
49
|
-
the zone itself.
|
|
50
|
-
- Use its operator lifecycle when a destructive contraction must wait for a
|
|
51
|
-
verified backfill instead of running automatically at application startup.
|
|
52
|
-
- Wrap the complete operator command with `withOperatorMigrationLock` so apply,
|
|
53
|
-
verification, and contraction cannot overlap across replicas.
|
|
54
|
-
- Use `ExpiringLeaseStore` to elect one replica for a recurring sweep whose work
|
|
55
|
-
makes network calls. It coordinates through a lease row with an expiry rather
|
|
56
|
-
than a session advisory lock, so nothing depends on which pooled connection
|
|
57
|
-
served a call and no transaction is held open across the work.
|
|
58
|
-
- Declare the Postgres extensions a service needs (`requiredExtensions`) and
|
|
59
|
-
the bootstrap VERIFIES they are installed in `public` before migrating. It
|
|
60
|
-
never creates one: an extension is a database-level prerequisite created by
|
|
61
|
-
the deployment's privileged bootstrap, and an absent one refuses the boot
|
|
62
|
-
with a message naming the extension, the database and the remedy.
|
|
63
|
-
|
|
64
|
-
## Tables this SDK owns
|
|
65
|
-
|
|
66
|
-
This SDK owns `xema_operator_migrations`, the ledger of the migration runner
|
|
67
|
-
(the way Prisma owns `_prisma_migrations`). It is the table's only creator, and
|
|
68
|
-
the ledger records its own creation. Consumers never declare it in
|
|
69
|
-
`schema.prisma` or in a migration.
|
|
70
|
-
|
|
71
|
-
Other SDKs' persistent tables (for example the authorization-facts outbox) are
|
|
72
|
-
created by **SDK-owned versioned migrations**. The owning SDK declares a
|
|
73
|
-
`SdkOwnedMigrationSet` (`owner`, versioned `migrations`, each naming the tables
|
|
74
|
-
it `creates`, and the `tables` shapes), and its Nest module lists
|
|
75
|
-
`contributeSdkOwnedMigrations(set)` in its `providers`. `BiomeDatabaseModule`
|
|
76
|
-
collects every contribution in the graph (one set per owner, ordered by owner)
|
|
77
|
-
and applies them in the migration phase. That is the only way a set reaches the
|
|
78
|
-
phase: a biome wires nothing by hand.
|
|
79
|
-
|
|
80
|
-
`runBiomeMigrations` is the one migration phase (the bootstrap runs it, and so
|
|
81
|
-
does `pg-test-harness`). Under a per-schema lock it:
|
|
82
|
-
|
|
83
|
-
1. reads the service's own migrations and refuses one that names an SDK-owned
|
|
84
|
-
table outside a PL/pgSQL function body;
|
|
85
|
-
2. runs `prisma migrate deploy` (first, so a fresh schema is still empty for
|
|
86
|
-
Prisma);
|
|
87
|
-
3. creates the ledger, or verifies that this SDK created it;
|
|
88
|
-
4. applies each SDK-owned migration once, records it in the ledger with the
|
|
89
|
-
sha256 of its SQL, and verifies every owned table's shape.
|
|
90
|
-
|
|
91
|
-
A table is never adopted. Every refusal below fails the boot with a coded error:
|
|
92
|
-
|
|
93
|
-
| Code | When |
|
|
94
|
-
| --- | --- |
|
|
95
|
-
| `SDK_OWNED_TABLE_NOT_CREATED_BY_OWNER` | An owned table exists and the ledger has no record of its owner creating it (a service migration, a `schema.prisma` model or manual DDL created it). |
|
|
96
|
-
| `SDK_OWNED_TABLE_SHAPE_MISMATCH` | An owned table differs from the shape its SDK declares. |
|
|
97
|
-
| `SDK_OWNED_MIGRATION_CHANGED` | An applied migration's SQL is not the SQL this image ships. |
|
|
98
|
-
| `SDK_OWNED_MIGRATIONS_UNKNOWN_TO_IMAGE` | The ledger records an SDK-owned migration this image does not ship (the database is newer, or the image dropped an SDK). |
|
|
99
|
-
| `SERVICE_MIGRATION_TOUCHES_SDK_OWNED_TABLE` | A service migration names an SDK-owned table at migration time. |
|
|
100
|
-
| `SDK_OWNED_MIGRATION_SET_INVALID` | A set is malformed (for example its SQL uses `IF NOT EXISTS`, or two owners claim one table). |
|
|
101
|
-
| `SDK_OWNED_MIGRATIONS_NOT_APPLIED` | A module that owns tables is mounted in a graph with no migration phase. |
|
|
102
|
-
| `SDK_OWNED_MIGRATIONS_CONFLICT` | Two different sets are contributed for one owner. |
|
|
103
|
-
|
|
104
|
-
The remedy for the first three is a reset of the database schema, so the owning
|
|
105
|
-
SDK creates and records its tables. The ledger is never edited by hand.
|
|
106
|
-
|
|
107
|
-
The shape check compares each column's name, type, nullability and default, the
|
|
108
|
-
primary key, every CHECK constraint (name and expression) and every index (name,
|
|
109
|
-
uniqueness, key columns, predicate), and refuses a foreign key, exclusion
|
|
110
|
-
constraint or constraint trigger on an owned table. It does not compare an
|
|
111
|
-
index's access method, operator classes, collations or INCLUDE columns, ordinary
|
|
112
|
-
triggers, row-security policies, privileges, the table owner or storage
|
|
113
|
-
parameters.
|
|
114
|
-
|
|
115
|
-
A released migration is immutable; a change is a new version. The package that
|
|
116
|
-
owns a set pins `sdkOwnedMigrationDigests(set)` in a spec, so an edit fails
|
|
117
|
-
there rather than at the boot of every deployed schema.
|
|
12
|
+
- Use `withOperatorMigrationLock` around an operator migration command so replicas cannot overlap.
|
|
13
|
+
- Use `ExpiringLeaseStore` to elect one replica for a recurring sweep without holding a transaction open.
|
|
118
14
|
|
|
119
15
|
## Installation
|
|
120
16
|
|
|
@@ -130,14 +26,6 @@ import {
|
|
|
130
26
|
BiomeDatabaseModule,
|
|
131
27
|
} from '@xemahq/biome-database-nest';
|
|
132
28
|
|
|
133
|
-
// Mounted beside `XemaServiceModule.forBiome(<SERVICE>_BOOTSTRAP)` (a service)
|
|
134
|
-
// or `XemaServiceModule.forWorker(<WORKER_KEY>_DESCRIPTOR)` (a worker, e.g.
|
|
135
|
-
// `ORG_DATABASE_POOL_WORKER_DESCRIPTOR`),
|
|
136
|
-
// which publish the manifest's `maximumPerInstance` the pool is sized from.
|
|
137
|
-
// Without one the boot is refused; the number is never typed here.
|
|
138
|
-
// The pool is exactly requests + activities + nexus + 2 (the SDK's own connections).
|
|
139
|
-
// `DB_POOL_MAX_<KEY>` / `DB_POOL_MAX` is a feasibility ceiling only: below that
|
|
140
|
-
// size the boot is refused with a `BiomeDatabasePoolOptionsError`, never shrunk.
|
|
141
29
|
BiomeDatabaseModule.forRoot({
|
|
142
30
|
biomeId: 'example',
|
|
143
31
|
migrationMode: BiomeDatabaseMigrationMode.ApplicationManaged,
|
|
@@ -148,10 +36,28 @@ BiomeDatabaseModule.forRoot({
|
|
|
148
36
|
});
|
|
149
37
|
```
|
|
150
38
|
|
|
39
|
+
Mount it beside `XemaServiceModule.forBiome(...)`, which publishes the connection count the pool is sized from.
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
| Option | Purpose |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `biomeId` | The biome the database belongs to. |
|
|
46
|
+
| `migrationMode` | `BiomeDatabaseMigrationMode.ApplicationManaged`. |
|
|
47
|
+
| `databases` | The databases the service owns. |
|
|
48
|
+
| `requiredExtensions` | Postgres extensions verified as installed in `public` before migrating; never created. |
|
|
49
|
+
|
|
50
|
+
| Environment | Purpose |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `DB_POOL_MAX`, `DB_POOL_MAX_<KEY>` | Feasibility ceiling for the pool. Below the derived size the boot is refused. |
|
|
53
|
+
| `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`, `DB_SCHEMA`, `DB_SSL_PARAMS`, `DB_POOLER_HOST`, `DB_POOLER_PORT`, `DB_POOLER_SSL_PARAMS` | Local development connection override; refused when `NODE_ENV=production`. |
|
|
54
|
+
| `DATABASE_URL` | Connection string for operator migrations when none is passed. |
|
|
55
|
+
|
|
56
|
+
Every Postgres session must run in UTC. The package verifies this and refuses to start otherwise; it never sets the zone.
|
|
57
|
+
|
|
151
58
|
## Peer requirements
|
|
152
59
|
|
|
153
|
-
|
|
154
|
-
- Compatible Xema identity, kernel-contract, service-registry, and decorator packages.
|
|
60
|
+
NestJS 10 or 11, Prisma 7 (`@prisma/client`, `@prisma/adapter-pg`), `@xemahq/identity-client`, `@xemahq/contracts`, `@xemahq/service-registry-nest`, `@xemahq/platform-common`.
|
|
155
61
|
|
|
156
62
|
## License
|
|
157
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/biome-database-nest",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Explicit database migration ownership, least-privilege runtime verification, and schema-qualified Prisma wiring for Xema biome services.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@types/node": "25.2.3",
|
|
31
31
|
"@types/pg": "^8.16.0",
|
|
32
32
|
"@xemahq/identity-client": "^0.15.5",
|
|
33
|
-
"@xemahq/contracts": "^0.
|
|
33
|
+
"@xemahq/contracts": "^0.79.0",
|
|
34
34
|
"@xemahq/pg-test-harness": "^0.3.1",
|
|
35
35
|
"@xemahq/platform-common": ">=0.77.0",
|
|
36
36
|
"eslint": "^9.39.4",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"prisma": "7.8.0",
|
|
40
40
|
"ts-jest": "^29.4.9",
|
|
41
41
|
"typescript": "5.9.3",
|
|
42
|
-
"@xemahq/
|
|
43
|
-
"@xemahq/
|
|
44
|
-
"@xemahq/
|
|
42
|
+
"@xemahq/service-registry-nest": "1.0.1",
|
|
43
|
+
"@xemahq/xema-decorators": "1.0.1",
|
|
44
|
+
"@xemahq/eslint-config": "0.14.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@nestjs/common": "^10 || ^11",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@xemahq/migration-runner-prisma": "^0.3.1",
|
|
58
|
-
"@xemahq/org-erasure-nest": "^2.0.
|
|
58
|
+
"@xemahq/org-erasure-nest": "^2.0.1",
|
|
59
59
|
"pg": "^8.16.3"
|
|
60
60
|
},
|
|
61
61
|
"exports": {
|
|
@@ -78,6 +78,6 @@
|
|
|
78
78
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
79
79
|
},
|
|
80
80
|
"xemaRelease": {
|
|
81
|
-
"sourceDigest": "sha256:
|
|
81
|
+
"sourceDigest": "sha256:d71e290d6ee853189569668b21c88db64034589b46296d141a1df86962c1e737"
|
|
82
82
|
}
|
|
83
83
|
}
|