@makaio/storage-pg 1.0.0-dev-1781260968078

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Makaio GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @makaio/storage-pg
2
+
3
+ Postgres storage engine for the Makaio framework.
4
+
5
+ ## Install & Usage
6
+
7
+ ```bash
8
+ npm install @makaio/storage-pg
9
+ export MAKAIO_DATABASE_URL=postgres://user:password@host:5432/makaio
10
+ ```
11
+
12
+ The node-postgres driver (`pg`) is a regular dependency — installing this
13
+ package is everything a Postgres host needs for the driver. The committed
14
+ Postgres migration chain ships inside the npm tarball (`drizzle-postgres/`)
15
+ and is applied automatically at boot. The engine attaches through the storage
16
+ engine registry: Node and Bun runtime hosts auto-resolve it for recognized
17
+ `postgres://` / `postgresql://` URLs, the `database.engines` boot option
18
+ registers it explicitly, and direct callers use
19
+ `registerStorageEngine(storageEngine)` — see "How It Attaches" below.
20
+
21
+ ## What This Is
22
+
23
+ Packages everything Postgres-specific behind the storage engine seam of
24
+ `@makaio/storage-drizzle` (one `StorageEngine` per dialect, attached through
25
+ the global engine registry):
26
+
27
+ - **Engine definition** — `postgresStorageEngine` claims `postgres://` /
28
+ `postgresql://` URLs (case-insensitively, mirroring the core URL hint table)
29
+ and creates clients over the node-postgres driver.
30
+ - **Error classifiers** — `isPostgresDuplicateObjectError` (SQLSTATE
31
+ `42P07`/`42710`) and `isPostgresUniqueViolationError` (`23505`, with optional
32
+ constraint-name scoping); both walk wrapped error cause chains.
33
+ - **Migration behavior** — the `__makaio_migrations` ledger (identity primary
34
+ key plus `UNIQUE` hash), `BEGIN ISOLATION LEVEL READ COMMITTED` transaction
35
+ pinning, the `pg_advisory_xact_lock` cross-process protocol keyed by
36
+ `migrationAdvisoryLockKey`, `__makaio_migrations_<hash>` extension ledgers,
37
+ and the committed Postgres migration chain itself: it ships in this
38
+ package's `drizzle-postgres/` directory and is resolved through the engine's
39
+ `resolveSourceChainDir`. The directory name is deliberately distinct from
40
+ the default `drizzle` directory so embedded-host chain discovery never picks
41
+ up the Postgres chain.
42
+ - **Full-text search** — `postgresFtsSearchStrategy`: tsvector matching via
43
+ `websearch_to_tsquery`, `ts_rank` ordering, and `ts_headline` excerpts over
44
+ the stored generated `messages.content_tsv` column. Boot-time provisioning is
45
+ a no-op — the column and its GIN index ship through the central Postgres
46
+ migration chain.
47
+
48
+ ## How It Attaches
49
+
50
+ The engine is never imported statically by framework core — attachment always
51
+ goes through the engine registry (a lint rule enforces the dependency
52
+ direction, because a static import would let bundlers silently inline the
53
+ Postgres engine into the core distribution):
54
+
55
+ - **Explicit registration** — `registerStorageEngine(storageEngine)` before any
56
+ database client is created, or via the runtime boot option
57
+ `database.engines: [storageEngine]` (registration precedes database
58
+ initialization by construction).
59
+ - **Host auto-resolve** — Node and Bun runtime hosts recognize Postgres
60
+ database URLs through the core hint table, import this package, and register
61
+ its well-known `storageEngine` export before any client is created.
62
+ - **Test harnesses** — register explicitly (the storage conformance harness
63
+ calls its idempotent `ensurePostgresEngineRegistered()` in every
64
+ client-creating entry point).
65
+
66
+ ## Key Exports
67
+
68
+ - `postgresStorageEngine` — the engine definition
69
+ - `storageEngine` — well-known auto-resolve alias (same object as
70
+ `postgresStorageEngine`)
71
+ - `isPostgresDuplicateObjectError(error)` / `isPostgresUniqueViolationError(error, constraint?)`
72
+ — error classifiers (also wired as `postgresStorageEngine.errors`)
73
+ - `migrationAdvisoryLockKey(tableName)` — signed 64-bit advisory lock key
74
+ (first 8 bytes, big-endian, of `SHA-256("makaio:migrations:<tableName>")`)
75
+ - `buildPostgresLedgerDdl(tableName)` — idempotent ledger `CREATE TABLE` DDL
76
+ - `POSTGRES_MIGRATION_BEGIN` — the pinned `BEGIN ISOLATION LEVEL READ COMMITTED`
77
+ statement text
78
+ - `postgresFtsSearchStrategy` — the tsvector FTS strategy (also wired as
79
+ `postgresStorageEngine.fts`)
80
+
81
+ The Postgres column bundle (`epochMs`, `bool`, `jsonCol`, `autoPk`, `float8`)
82
+ is NOT part of this package: column bundles are schema-declaration vocabulary
83
+ owned by `@makaio/framework/storage/drizzle/columns/postgres`, so hand-written
84
+ Postgres twin schema files in framework core never import this engine package.
85
+
86
+ The migration statement texts, ledger names, and the advisory-lock key
87
+ derivation are **cross-version contracts**: runners built from different
88
+ framework versions must agree on them byte-for-byte, otherwise concurrent runs
89
+ stop serializing against each other or stop recognizing each other's ledgers.
90
+ All of them are byte-pinned by this package's tests and exercised live by the
91
+ storage conformance suite.
92
+
93
+ ---
94
+
95
+ *Part of Makaio Framework*
@@ -0,0 +1,52 @@
1
+ import type { DatabaseClient, PostgresClientOptions } from '@makaio/framework/storage/drizzle/client';
2
+ import { type PostgresPoolLike } from './raw-sql.js';
3
+ /** Structural surface of the `pg` module used by {@link createNodePgClient}. */
4
+ interface PgModule {
5
+ default: {
6
+ Pool: new (config: {
7
+ connectionString: string;
8
+ max: number;
9
+ }) => PostgresPoolLike;
10
+ };
11
+ }
12
+ /** Structural surface of the `drizzle-orm/node-postgres` module used by {@link createNodePgClient}. */
13
+ interface NodePgDrizzleModule {
14
+ drizzle: (pool: PostgresPoolLike) => object;
15
+ }
16
+ /**
17
+ * Lazy loaders for the Postgres driver modules.
18
+ *
19
+ * Internal seam: the production defaults are the literal dynamic imports below;
20
+ * tests inject a loader that throws to exercise the missing-driver error-wrap
21
+ * path without mocking the module system.
22
+ */
23
+ export interface NodePgDriverLoaders {
24
+ /** Loads the `pg` module. */
25
+ readonly loadPg: () => Promise<PgModule>;
26
+ /** Loads the `drizzle-orm/node-postgres` module. */
27
+ readonly loadDrizzlePg: () => Promise<NodePgDrizzleModule>;
28
+ }
29
+ /**
30
+ * Creates a database client backed by the node-postgres (`pg`) driver.
31
+ *
32
+ * Delegation target of the Postgres engine's `createClient`: the engine
33
+ * package owns both the dialect-specific behavior registered through the
34
+ * engine seam and this driver glue.
35
+ *
36
+ * Both `'pg'` and `'drizzle-orm/node-postgres'` load through direct dynamic
37
+ * `import()` calls, so the drivers load on the first Postgres client rather
38
+ * than at module load (laziness is preserved). Resolution happens from this
39
+ * module, which declares both as regular dependencies, so it stays
40
+ * strict-install-safe (pnpm, Yarn PnP) where the drivers are only resolvable
41
+ * from this package. Bundle-time resolvability is acceptable here precisely
42
+ * because they are declared dependencies of this package — the build leaves
43
+ * them external (see `build.ts`); bundler-opacity is only required for
44
+ * specifiers the resolving package does not declare.
45
+ * @param url - Postgres connection URL (`postgres://` or `postgresql://`).
46
+ * @param options - Optional pool tuning options.
47
+ * @param loaders - Internal driver-loader seam; defaults to the literal
48
+ * dynamic imports. Tests inject a throwing loader to exercise the error path.
49
+ * @returns Database client with drizzle ORM instance and async close method.
50
+ */
51
+ export declare function createNodePgClient(url: string, options: PostgresClientOptions | undefined, loaders?: NodePgDriverLoaders): Promise<DatabaseClient>;
52
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { StorageEngine } from '@makaio/framework/storage/drizzle';
2
+ /**
3
+ * The Postgres storage engine.
4
+ *
5
+ * Claims `postgres://` and `postgresql://` URLs (case-insensitively,
6
+ * mirroring the engine hint table in `@makaio/storage-drizzle`) and creates
7
+ * clients over the node-postgres driver glue owned by this package (`pg` is
8
+ * a regular dependency, loaded lazily when a client is created). Register it
9
+ * explicitly via `registerStorageEngine` or host boot options; Node runtime hosts
10
+ * additionally auto-register it for recognized database URLs through this
11
+ * package's well-known `storageEngine` export.
12
+ *
13
+ * Migration behavior preserves the cross-version Postgres contracts
14
+ * byte-for-byte (`__makaio_migrations` ledger name and DDL,
15
+ * `BEGIN ISOLATION LEVEL READ COMMITTED`, the advisory-lock key derivation,
16
+ * `__makaio_migrations_<hash>` extension ledgers). The chain directory name
17
+ * `drizzle-postgres` is deliberately distinct from the default `drizzle`
18
+ * directory so embedded-host chain discovery never picks up the Postgres
19
+ * chain.
20
+ */
21
+ export declare const postgresStorageEngine: StorageEngine;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Returns `true` when the error (or any link in its cause chain) reports
3
+ * that a schema object already exists on Postgres.
4
+ *
5
+ * Matches the SQLSTATE codes `42P07`/`42710`. Used by the migration
6
+ * applicator to decide whether a failed first CREATE can be adopted into the
7
+ * ledger.
8
+ * @param error - Error thrown by a DDL statement.
9
+ * @returns Whether the failure is a duplicate-schema-object conflict.
10
+ */
11
+ export declare function isPostgresDuplicateObjectError(error: unknown): boolean;
12
+ /**
13
+ * Returns `true` when the error (or any link in its cause chain) reports a
14
+ * Postgres unique-constraint violation.
15
+ *
16
+ * Matches SQLSTATE `23505` (unique_violation); when `constraint` is given,
17
+ * the driver error's `constraint` property must match it too, so callers can
18
+ * react to one specific index without swallowing unrelated violations.
19
+ *
20
+ * Used by write paths that resolve write-write races through a bounded retry
21
+ * (for example MAX-based counter assignment under `READ COMMITTED`, where two
22
+ * concurrent statements can compute the same next value).
23
+ * @param error - Error thrown by a DML statement.
24
+ * @param constraint - Optional constraint/index name to scope the match.
25
+ * @returns Whether the failure is a unique-constraint violation.
26
+ */
27
+ export declare function isPostgresUniqueViolationError(error: unknown, constraint?: string): boolean;
@@ -0,0 +1,6 @@
1
+ import { type FtsSearchStrategy } from '@makaio/framework/storage/drizzle';
2
+ /**
3
+ * The Postgres FTS strategy: tsvector matching via `websearch_to_tsquery`,
4
+ * `ts_rank` ordering, and `ts_headline` excerpts.
5
+ */
6
+ export declare const postgresFtsSearchStrategy: FtsSearchStrategy;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * \@makaio/storage-pg
3
+ *
4
+ * Postgres storage engine for the Makaio framework: driver glue, error
5
+ * classification, and migration behavior packaged behind the storage engine
6
+ * seam of `@makaio/storage-drizzle`.
7
+ *
8
+ * Hosts register the engine explicitly (boot `database.engines` option or
9
+ * `registerStorageEngine`); Node runtime hosts additionally auto-resolve this
10
+ * package for recognized `postgres://` / `postgresql://` URLs through the
11
+ * well-known {@link storageEngine} export.
12
+ * @packageDocumentation
13
+ */
14
+ export { postgresStorageEngine } from './engine.js';
15
+ export { isPostgresDuplicateObjectError, isPostgresUniqueViolationError } from './errors.js';
16
+ export { postgresFtsSearchStrategy } from './fts-strategy.js';
17
+ export { buildPostgresLedgerDdl, migrationAdvisoryLockKey, POSTGRES_MIGRATION_BEGIN } from './migrations.js';
18
+ /**
19
+ * Well-known engine export consumed by host URL auto-resolve: runtime hosts
20
+ * that recognize a Postgres database URL import this package and register
21
+ * `storageEngine` with the engine registry. Same object as
22
+ * {@link postgresStorageEngine}.
23
+ */
24
+ export declare const storageEngine: import("@makaio/framework/storage/drizzle").StorageEngine;