@owlmeans/postgres 0.1.15

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/agent-meta/instructions/postgres.instructions.md +57 -0
  4. package/agent-meta/manifest.json +23 -0
  5. package/agent-meta/skills/postgres/SKILL.md +121 -0
  6. package/build/bootstrap.d.ts +16 -0
  7. package/build/bootstrap.d.ts.map +1 -0
  8. package/build/bootstrap.js +106 -0
  9. package/build/bootstrap.js.map +1 -0
  10. package/build/consts.d.ts +29 -0
  11. package/build/consts.d.ts.map +1 -0
  12. package/build/consts.js +30 -0
  13. package/build/consts.js.map +1 -0
  14. package/build/index.d.ts +8 -0
  15. package/build/index.d.ts.map +1 -0
  16. package/build/index.js +7 -0
  17. package/build/index.js.map +1 -0
  18. package/build/middleware.d.ts +11 -0
  19. package/build/middleware.d.ts.map +1 -0
  20. package/build/middleware.js +21 -0
  21. package/build/middleware.js.map +1 -0
  22. package/build/service.d.ts +9 -0
  23. package/build/service.d.ts.map +1 -0
  24. package/build/service.js +221 -0
  25. package/build/service.js.map +1 -0
  26. package/build/types.d.ts +40 -0
  27. package/build/types.d.ts.map +1 -0
  28. package/build/types.js +2 -0
  29. package/build/types.js.map +1 -0
  30. package/build/utils/config.d.ts +27 -0
  31. package/build/utils/config.d.ts.map +1 -0
  32. package/build/utils/config.js +96 -0
  33. package/build/utils/config.js.map +1 -0
  34. package/build/utils/connection.d.ts +15 -0
  35. package/build/utils/connection.d.ts.map +1 -0
  36. package/build/utils/connection.js +56 -0
  37. package/build/utils/connection.js.map +1 -0
  38. package/package.json +45 -0
  39. package/src/bootstrap.ts +126 -0
  40. package/src/consts.ts +36 -0
  41. package/src/index.ts +7 -0
  42. package/src/middleware.ts +26 -0
  43. package/src/service.ts +287 -0
  44. package/src/types.ts +42 -0
  45. package/src/utils/config.ts +109 -0
  46. package/src/utils/connection.ts +64 -0
  47. package/tests/bootstrap.spec.ts +142 -0
  48. package/tests/context.ts +204 -0
  49. package/tests/crud.spec.ts +227 -0
  50. package/tests/custom-sql.spec.ts +196 -0
  51. package/tests/migration.spec.ts +260 -0
  52. package/tests/sync.spec.ts +156 -0
  53. package/tsconfig.json +16 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OwlMeans Common — Fullstack typescript framework
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,139 @@
1
+ # @owlmeans/postgres
2
+
3
+ PostgreSQL service for OwlMeans server contexts — pooled connections, readiness probing, schema
4
+ provisioning, and an opt-in least-privilege bootstrap path.
5
+
6
+ ## Overview
7
+
8
+ - `makePostgresDbService(alias?)` — creates a PostgreSQL connection service
9
+ - `appendPostgres(context, alias?)` — registers the service **and** its drain middleware in the context
10
+ - Reads connection config from `context.cfg.dbs` entries whose `service` is `'postgres'`
11
+ - Backs `@owlmeans/postgres-resource`; the resource layer owns all DDL
12
+ - `bootstrap()` provisions a role, database and schema from a separate superuser config alias
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add @owlmeans/postgres @owlmeans/postgres-resource
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```typescript
23
+ import { appendPostgres, DEFAULT_ALIAS as POSTGRES_SERVICE } from '@owlmeans/postgres'
24
+
25
+ // In context setup (backend/src/context.ts)
26
+ appendPostgres<C, T>(context)
27
+ ```
28
+
29
+ Config:
30
+
31
+ ```typescript
32
+ cfg.dbs = [{
33
+ service: 'postgres',
34
+ alias: 'postgres',
35
+ host: '/etc/app-config/pg-host', // values starting with `/` are read as files
36
+ user: '/etc/app-config/pg-role-name',
37
+ secret: '/etc/master-secret/pg-app-password',
38
+ schema: 'app', // ← Postgres SCHEMA
39
+ meta: { database: '/etc/app-config/pg-db-name' }
40
+ }]
41
+ ```
42
+
43
+ A whole connection string works too, and wins over `host`/`user`/`secret`:
44
+
45
+ ```typescript
46
+ cfg.dbs = [{ service: 'postgres', alias: 'postgres', schema: 'app', meta: { url: process.env.DATABASE_URL } }]
47
+ ```
48
+
49
+ `schema` is the Postgres **schema**, not the database — `dbName()` suffixes it per Entity/User layer,
50
+ so per-tenant namespaces stay cheap. The database comes from `meta.database` and is never suffixed.
51
+
52
+ At `init()` the service opens a pool, runs a `SELECT 1` readiness probe (30 attempts, 2s apart by
53
+ default — a Postgres sidecar routinely accepts TCP before it accepts queries), issues
54
+ `CREATE SCHEMA IF NOT EXISTS`, and installs a `SIGTERM` handler that drains the pool.
55
+
56
+ ### Bootstrap (admin path)
57
+
58
+ Hold the superuser connection under a **separate** config alias so the application's own entry never
59
+ carries superuser credentials:
60
+
61
+ ```typescript
62
+ cfg.dbs = [
63
+ { service: 'postgres', alias: 'postgres', /* least privileged app role */ },
64
+ { service: 'postgres', alias: 'pg-admin', /* superuser */ }
65
+ ]
66
+
67
+ const postgres = context.service<PostgresService>('postgres')
68
+ await postgres.bootstrap('pg-admin', {
69
+ role: 'app_role', password: appPassword, database: 'app_db', schema: 'app', leastPrivilege: true
70
+ })
71
+ ```
72
+
73
+ Idempotent by design — it probes `pg_roles` and `pg_database` before creating anything, rotates the
74
+ password of a role that already exists, and applies `REVOKE CONNECT … FROM PUBLIC`,
75
+ `REVOKE CREATE ON SCHEMA public FROM PUBLIC`, `GRANT CONNECT … TO <role>` and
76
+ `ALTER ROLE … SET search_path`. Returns a `BootstrapReport` saying what it actually changed.
77
+ Identifiers are validated and quoted; the password is the only literal and is escaped.
78
+
79
+ ## API
80
+
81
+ ### `makePostgresDbService(alias?): PostgresService`
82
+
83
+ Creates the PostgreSQL service. `alias` defaults to `DEFAULT_ALIAS` (`'postgres'`).
84
+
85
+ ### `appendPostgres<C, T>(context, alias?): T`
86
+
87
+ Registers the service and the drain middleware in the context.
88
+
89
+ ### `PostgresService`
90
+
91
+ Extends `PostgresDbService` from `@owlmeans/postgres-resource`:
92
+
93
+ - `db(alias?): Promise<PostgresDb>` — `{ drizzle, pool, schema, database }`
94
+ - `client(alias?): Promise<Pool>` / `clients: Record<string, Pool>`
95
+ - `qualify(resourceAlias, configAlias?): string` — `"schema"."table"` of a registered resource
96
+ - `query(text, params?, configAlias?)` — parameterised query straight on the pool
97
+ - `transaction(fn, configAlias?)` — a `PostgresTx`
98
+ - `defer(configAlias, task)` / `drain(configAlias?)` — work held back until every resource has
99
+ initialized (foreign keys whose target table belongs to a resource that hasn't run `init()` yet)
100
+ - `lock` / `unlock` — AES field encryption via `config.encryptionKey`
101
+ - `bootstrap(configAlias, opts): Promise<BootstrapReport>`
102
+
103
+ ### Helpers
104
+
105
+ - `parseUrl(url)` / `prepareConfig(config, overrides?)` / `poolDatabase(pool)`
106
+ - `probe(pool, meta, location)` / `ensureSchema(pool, schema)`
107
+ - `bootstrapDb(...)` — the bootstrap implementation, usable without a context
108
+ - `drainMiddleware(alias?)`
109
+
110
+ ### Constants
111
+
112
+ - `DEFAULT_ALIAS` — `'postgres'`
113
+ - `DEF_ADMIN_ALIAS` — `'pg-admin'`
114
+ - `DEF_MAINTENANCE_DB` — `'postgres'`; `DEF_PORT` — `5432`; `DEF_POOL_SIZE` — `10`
115
+ - `DEF_RETRIES` — `30`; `DEF_RETRY_DELAY` — `2000`
116
+ - `TERMINAL_CONNECT_CODES` — auth/database/permission failures the probe treats as final
117
+
118
+ ## Related Packages
119
+
120
+ - [`@owlmeans/postgres-resource`](../postgres-resource) — `makePostgresResource` uses this service
121
+ - [`@owlmeans/server-app`](../server-app) — `makeContext` in conjunction with `appendPostgres`
122
+ - [`@owlmeans/mongo`](../mongo) — the MongoDB counterpart
123
+
124
+ <!-- owlmeans:agent-guidance:start -->
125
+ ## Agent guidance
126
+
127
+ This package ships embedded Claude Code skills and GitHub Copilot instructions under
128
+ `agent-meta/`. After installing your `@owlmeans/*` packages, run the OwlMeans
129
+ agent-skills installer to place them into your project's native locations
130
+ (`.claude/skills/` and `.github/instructions/`):
131
+
132
+ ```sh
133
+ npx @owlmeans/agent-skills
134
+ ```
135
+
136
+ The embedded files are version-matched to this package release. Do not edit them
137
+ directly — they are regenerated on each publish. To contribute guidance edits,
138
+ open a PR against the source monorepo.
139
+ <!-- owlmeans:agent-guidance:end -->
@@ -0,0 +1,57 @@
1
+ ---
2
+ description: "How to use @owlmeans/postgres — PostgreSQL connection service (makePostgresDbService / appendPostgres) registered on a server context, plus the least-privilege bootstrap admin path."
3
+ applyTo: "**/context.ts, **/config.ts, **/*.ts, **/*.tsx"
4
+ ---
5
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
+
7
+ # @owlmeans/postgres
8
+
9
+ **Layer:** Infra
10
+ **Install:** `"@owlmeans/postgres": "^0.1.15"` in `dependencies`
11
+
12
+ ## Key Exports
13
+
14
+ | Export | Description |
15
+ |--------|-------------|
16
+ | `makePostgresDbService(alias?)` | PostgreSQL connection service factory |
17
+ | `appendPostgres(context, alias?)` | Registers the service **and** its drain middleware |
18
+ | `PostgresService`, `BootstrapOptions`, `BootstrapReport` | Service contract and the admin path |
19
+ | `bootstrapDb`, `drainMiddleware`, `parseUrl`, `prepareConfig`, `probe`, `ensureSchema` | Helpers |
20
+ | `DEFAULT_ALIAS` (`'postgres'`), `DEF_ADMIN_ALIAS` (`'pg-admin'`), `DEF_POOL_SIZE`, `DEF_RETRIES` | Constants |
21
+
22
+ ## Usage
23
+
24
+ ```typescript
25
+ import { appendPostgres } from '@owlmeans/postgres'
26
+ appendPostgres<C, T>(context)
27
+
28
+ cfg.dbs = [{
29
+ service: 'postgres', alias: 'postgres',
30
+ host: '/etc/app-config/pg-host', user: '/etc/app-config/pg-role-name',
31
+ secret: '/etc/master-secret/pg-app-password',
32
+ schema: 'app', meta: { database: '/etc/app-config/pg-db-name' }
33
+ }]
34
+ ```
35
+
36
+ ## Rules
37
+
38
+ - Use `appendPostgres`, not a bare `registerService` — the drain middleware is half the wiring.
39
+ - `DbConfig.schema` is the Postgres **SCHEMA** (layer-suffixed by `dbName()`); the **DATABASE** is
40
+ `meta.database` and is never suffixed. `meta.url` accepts a whole connection string and wins over
41
+ `host`/`user`/`secret`. A value starting with `/` is read as a file.
42
+ - `init()` already probes readiness (`SELECT 1`, 30×2s) and creates the schema — do not hand-roll a
43
+ retry loop. Auth / missing-database / permission codes fail fast instead of retrying.
44
+ - Keep the pool small; `max_connections` is a cluster-wide cap, so an oversized pool starves other
45
+ clients. Default `DEF_POOL_SIZE` is 10.
46
+ - All application-table DDL belongs to `@owlmeans/postgres-resource`. This service only creates the
47
+ schema.
48
+ - Superuser credentials go under a **separate** config alias (`pg-admin`), never the app's own entry.
49
+ - `bootstrap()` is idempotent and replaces hand-written `CREATE ROLE` / `CREATE DATABASE` / `GRANT`
50
+ SQL. If such SQL exists in a deployment script or doc, collapse it into a `bootstrap()` call.
51
+ - Use `defer()` for work that depends on another resource being initialized (foreign keys); it is
52
+ drained by the middleware once the context is up.
53
+
54
+ ## Depends On
55
+
56
+ - `@owlmeans/postgres-resource`, `@owlmeans/resource`, `@owlmeans/context`,
57
+ `@owlmeans/server-context`, `@owlmeans/basic-keys`, `pg`, `drizzle-orm`
@@ -0,0 +1,23 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "package": "@owlmeans/postgres",
4
+ "version": "0.1.15",
5
+ "generatedAt": "2026-08-07T18:11:56.992Z",
6
+ "canonicalRepo": "https://github.com/owlmeans/common",
7
+ "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "postgres",
11
+ "category": "package-specific",
12
+ "file": "skills/postgres/SKILL.md",
13
+ "canonicalPath": ".claude/skills/postgres/SKILL.md"
14
+ },
15
+ {
16
+ "kind": "instruction",
17
+ "name": "postgres",
18
+ "category": "package-specific",
19
+ "file": "instructions/postgres.instructions.md",
20
+ "canonicalPath": ".github/instructions/postgres.instructions.md"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: postgres
3
+ description: How to use @owlmeans/postgres — PostgreSQL connection service (makePostgresDbService / appendPostgres) registered on a server context, plus the least-privilege bootstrap admin path. Auto-invoked when wiring PostgreSQL into a server app.
4
+ user-invocable: false
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # @owlmeans/postgres
9
+
10
+ **Layer:** Infra
11
+ **Install:** `"@owlmeans/postgres": "^0.1.15"` in `dependencies`
12
+
13
+ Pooled `pg` connections for a server context, plus the admin path that provisions the role, database
14
+ and schema an app connects with. All DDL for application tables belongs to [[postgres-resource]] —
15
+ this package only creates the schema those tables live in.
16
+
17
+ ## Key Exports
18
+
19
+ | Export | Description |
20
+ |--------|-------------|
21
+ | `makePostgresDbService(alias?)` | The connection service. `alias` defaults to `DEFAULT_ALIAS` (`'postgres'`). |
22
+ | `appendPostgres(context, alias?)` | Registers the service **and** its drain middleware. Use this, not a bare `registerService`. |
23
+ | `PostgresService` | `PostgresDbService` + `bootstrap(configAlias, opts)`. |
24
+ | `BootstrapOptions`, `BootstrapReport`, `bootstrapDb` | The admin path; `bootstrapDb` is usable without a context. |
25
+ | `drainMiddleware(alias?)` | Drains work deferred until every resource has initialized. |
26
+ | `parseUrl`, `prepareConfig`, `poolDatabase`, `probe`, `ensureSchema` | Config and connection helpers. |
27
+ | `DEFAULT_ALIAS`, `DEF_ADMIN_ALIAS`, `DEF_MAINTENANCE_DB`, `DEF_PORT`, `DEF_POOL_SIZE`, `DEF_RETRIES`, `DEF_RETRY_DELAY`, `TERMINAL_CONNECT_CODES` | Constants. |
28
+
29
+ ## Wiring
30
+
31
+ ```typescript
32
+ import { appendPostgres } from '@owlmeans/postgres'
33
+
34
+ appendPostgres<C, T>(context)
35
+ ```
36
+
37
+ ```typescript
38
+ cfg.dbs = [{
39
+ service: 'postgres',
40
+ alias: 'postgres',
41
+ host: '/etc/app-config/pg-host', // a leading `/` means "read this file"
42
+ user: '/etc/app-config/pg-role-name',
43
+ secret: '/etc/master-secret/pg-app-password',
44
+ schema: 'app', // ← Postgres SCHEMA
45
+ meta: { database: '/etc/app-config/pg-db-name' }
46
+ }]
47
+ ```
48
+
49
+ `meta.url` takes a whole connection string and wins over `host`/`user`/`secret` — that is the shape
50
+ for a `DATABASE_URL` env var. Because a leading `/` is auto-read, moving to file-mounted secrets
51
+ later is a config change, not a code change.
52
+
53
+ **`schema` is the SCHEMA, not the database.** `dbName()` suffixes it per Entity/User layer, so
54
+ per-tenant namespaces cost a `CREATE SCHEMA` rather than a `CREATE DATABASE`. The database comes
55
+ from `meta.database` and is never suffixed.
56
+
57
+ Other `meta` keys: `autoSync` (see [[postgres-resource]]), `ssl`, `max`, `idleTimeoutMillis`,
58
+ `connectionTimeoutMillis`, `statementTimeoutMillis`, `retries`, `retryDelayMillis`.
59
+
60
+ ## What `init()` does
61
+
62
+ Opens a `pg.Pool` → `SELECT 1` readiness probe (30 attempts, 2s apart) → `CREATE SCHEMA IF NOT
63
+ EXISTS` → `SIGTERM` handler that drains the pool. Do **not** hand-roll the retry loop around it: a
64
+ Postgres sidecar routinely accepts TCP before it accepts queries, which is precisely what the probe
65
+ is for. Credential, missing-database and permission failures (`TERMINAL_CONNECT_CODES`) fail
66
+ immediately instead of burning the full retry budget on an error that will never clear.
67
+
68
+ Keep the pool small. Postgres caps connections cluster-wide (`max_connections`, 100 by default), so
69
+ an oversized per-process pool starves every other client of the same server — the opposite of the
70
+ Mongo driver's tuning instinct.
71
+
72
+ ## Service surface
73
+
74
+ `db(alias?)` → `{ drizzle, pool, schema, database }` · `client(alias?)` / `clients` ·
75
+ `qualify(resourceAlias, configAlias?)` · `query(text, params?, configAlias?)` ·
76
+ `transaction(fn, configAlias?)` · `lock`/`unlock` (AES via `config.encryptionKey`) ·
77
+ `defer(configAlias, task)` / `drain(configAlias?)`.
78
+
79
+ `defer` exists for work that can't run during one resource's `init()` because it points at another
80
+ resource that hasn't initialized yet — foreign keys, above all. Queue it; the middleware
81
+ `appendPostgres` registers drains it once the context is up. Do not reorder resource registrations
82
+ to work around this.
83
+
84
+ ## Bootstrap — the admin path
85
+
86
+ Superuser credentials belong to a **separate config alias**. The application's own entry connects as
87
+ the least-privileged role and must never carry them.
88
+
89
+ ```typescript
90
+ await context.service<PostgresService>('postgres').bootstrap('pg-admin', {
91
+ role: 'app_role', password: appPassword, database: 'app_db', schema: 'app', leastPrivilege: true
92
+ })
93
+ ```
94
+
95
+ Idempotent by design — every OwlMeans deployment calls it on each start and each rebuild. It probes
96
+ `pg_roles` / `pg_database` before creating, rotates the password of an existing role (the caller
97
+ generated the password it just passed in, so the role has to accept it), and with `leastPrivilege`
98
+ applies `REVOKE CONNECT … FROM PUBLIC`, `REVOKE CREATE ON SCHEMA public FROM PUBLIC`,
99
+ `GRANT CONNECT … TO <role>`, `ALTER ROLE … SET search_path`. The returned `BootstrapReport` says
100
+ what actually changed. Identifiers are validated and quoted; the password is the only literal.
101
+
102
+ **This replaces hand-written bootstrap SQL.** If you find `CREATE ROLE` / `CREATE DATABASE` /
103
+ `GRANT` strings in a deployment script, a provisioner, or a `DEPLOYMENT.md`, they are a duplicate of
104
+ this function — collapse them into a `bootstrap()` call rather than keeping both in step.
105
+
106
+ ## Tests
107
+
108
+ `bun test ./tests` in the package. Integration specs that build a real `ServerContext` live **here**,
109
+ not in `postgres-resource` — see [[testing-integration]]. Gated on `POSTGRES_URL`; the bootstrap
110
+ specs additionally probe for `CREATEROLE`/`CREATEDB` and self-skip without them.
111
+
112
+ ## Depends On
113
+
114
+ - `@owlmeans/postgres-resource` · `@owlmeans/resource` · `@owlmeans/context` ·
115
+ `@owlmeans/server-context` · `@owlmeans/basic-keys`
116
+ - `pg` · `drizzle-orm`
117
+
118
+ ## Related
119
+
120
+ - [[postgres-resource]] — the `Resource<T>` implementation this service backs
121
+ - [[mongo]] — the MongoDB counterpart · [[kluster]] — `kluster:` directives in `cfg.dbs`
@@ -0,0 +1,16 @@
1
+ import type { BootstrapOptions, BootstrapReport, PostgresService } from './types.js';
2
+ /**
3
+ * Provision a least privileged role, its database and its schema.
4
+ *
5
+ * This is the one copy of a script every OwlMeans deployment used to carry inline. It runs
6
+ * on every start and every rebuild, so each step probes before it acts — "already exists"
7
+ * is the expected outcome, not an error.
8
+ *
9
+ * Two connections are unavoidable. `CREATE DATABASE` cannot run from inside the database
10
+ * it creates, so roles and databases are made over the maintenance connection the admin
11
+ * config points at, and the grants are applied over a short lived connection to the target.
12
+ *
13
+ * @throws {PostgresBootstrapError}
14
+ */
15
+ export declare const bootstrapDb: (service: PostgresService, configAlias: string, opts: BootstrapOptions) => Promise<BootstrapReport>;
16
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AASpF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,GACtB,SAAS,eAAe,EAAE,aAAa,MAAM,EAAE,MAAM,gBAAgB,KACpE,OAAO,CAAC,eAAe,CAwFzB,CAAA"}
@@ -0,0 +1,106 @@
1
+ import { assertSqlIdentifier, PostgresBootstrapError, quoteIdent, quoteLiteral } from '@owlmeans/postgres-resource';
2
+ import { Pool } from 'pg';
3
+ import { prepareConfig } from './utils/config.js';
4
+ const exists = async (client, text, value) => {
5
+ const result = await client.query(text, [value]);
6
+ return result.rowCount != null && result.rowCount > 0;
7
+ };
8
+ /**
9
+ * Provision a least privileged role, its database and its schema.
10
+ *
11
+ * This is the one copy of a script every OwlMeans deployment used to carry inline. It runs
12
+ * on every start and every rebuild, so each step probes before it acts — "already exists"
13
+ * is the expected outcome, not an error.
14
+ *
15
+ * Two connections are unavoidable. `CREATE DATABASE` cannot run from inside the database
16
+ * it creates, so roles and databases are made over the maintenance connection the admin
17
+ * config points at, and the grants are applied over a short lived connection to the target.
18
+ *
19
+ * @throws {PostgresBootstrapError}
20
+ */
21
+ export const bootstrapDb = async (service, configAlias, opts) => {
22
+ const role = assertSqlIdentifier(opts.role, 'role');
23
+ const database = assertSqlIdentifier(opts.database ?? opts.role, 'database');
24
+ const schema = opts.schema != null ? assertSqlIdentifier(opts.schema, 'schema') : null;
25
+ const leastPrivilege = opts.leastPrivilege ?? true;
26
+ const rotate = opts.rotatePassword ?? true;
27
+ if (opts.password === '') {
28
+ throw new PostgresBootstrapError(`empty-password:${role}`);
29
+ }
30
+ const report = {
31
+ roleCreated: false,
32
+ passwordRotated: false,
33
+ databaseCreated: false,
34
+ schemaCreated: false,
35
+ grantsApplied: false
36
+ };
37
+ const config = service.config(configAlias);
38
+ const admin = await service.client(configAlias);
39
+ /**
40
+ * The password is the only value here that can't be bound: `CREATE ROLE` takes no
41
+ * parameters, so it reaches the server inside the statement text. Identifiers have
42
+ * already been asserted safe; the literal is escaped.
43
+ */
44
+ const secret = quoteLiteral(opts.password);
45
+ const client = await admin.connect();
46
+ try {
47
+ if (await exists(client, 'SELECT 1 FROM pg_roles WHERE rolname = $1', role)) {
48
+ if (rotate) {
49
+ await client.query(`ALTER ROLE ${quoteIdent(role)} WITH LOGIN PASSWORD ${secret}`);
50
+ report.passwordRotated = true;
51
+ }
52
+ }
53
+ else {
54
+ await client.query(`CREATE ROLE ${quoteIdent(role)} WITH LOGIN PASSWORD ${secret}`);
55
+ report.roleCreated = true;
56
+ }
57
+ if (!await exists(client, 'SELECT 1 FROM pg_database WHERE datname = $1', database)) {
58
+ /** Never inside a transaction — Postgres refuses `CREATE DATABASE` in one. */
59
+ await client.query(`CREATE DATABASE ${quoteIdent(database)} OWNER ${quoteIdent(role)}`);
60
+ report.databaseCreated = true;
61
+ }
62
+ }
63
+ catch (error) {
64
+ throw wrap(error, `${role}@${database}`);
65
+ }
66
+ finally {
67
+ client.release();
68
+ }
69
+ const target = new Pool(prepareConfig(config, { database, max: 1 }));
70
+ try {
71
+ if (schema != null) {
72
+ /**
73
+ * The resource layer creates its own schema too, but only once an application with
74
+ * resources boots. Creating it here — owned by the role — is what lets that
75
+ * application connect as a role with no `CREATE` right on the database at all.
76
+ */
77
+ await target.query(`CREATE SCHEMA IF NOT EXISTS ${quoteIdent(schema)} AUTHORIZATION ${quoteIdent(role)}`);
78
+ await target.query(`GRANT ALL ON SCHEMA ${quoteIdent(schema)} TO ${quoteIdent(role)}`);
79
+ await target.query(`ALTER ROLE ${quoteIdent(role)} SET search_path = ${quoteIdent(schema)}`);
80
+ report.schemaCreated = true;
81
+ }
82
+ if (leastPrivilege) {
83
+ await target.query(`REVOKE CONNECT ON DATABASE ${quoteIdent(database)} FROM PUBLIC`);
84
+ await target.query('REVOKE CREATE ON SCHEMA public FROM PUBLIC');
85
+ await target.query(`GRANT CONNECT ON DATABASE ${quoteIdent(database)} TO ${quoteIdent(role)}`);
86
+ report.grantsApplied = true;
87
+ }
88
+ }
89
+ catch (error) {
90
+ throw wrap(error, `${role}@${database}`);
91
+ }
92
+ finally {
93
+ await target.end().catch(() => undefined);
94
+ }
95
+ console.log(`@owlmeans/postgres: bootstrap ${role}@${database} —`
96
+ + ` role ${report.roleCreated ? 'created' : report.passwordRotated ? 'rotated' : 'present'},`
97
+ + ` database ${report.databaseCreated ? 'created' : 'present'}`
98
+ + (schema != null ? `, schema ${schema} ready` : ''));
99
+ return report;
100
+ };
101
+ const wrap = (error, subject) => {
102
+ const failure = new PostgresBootstrapError(`${subject}:${error?.message ?? error}`);
103
+ failure.cause = error;
104
+ return failure;
105
+ };
106
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export declare const DEFAULT_ALIAS = "postgres";
2
+ /**
3
+ * Config alias conventionally holding the superuser connection the admin path needs.
4
+ * Never the same as {@link DEFAULT_ALIAS} — the application connects as a least
5
+ * privileged role and must not carry superuser credentials in the same config entry.
6
+ */
7
+ export declare const DEF_ADMIN_ALIAS = "pg-admin";
8
+ /** The database `CREATE DATABASE` has to be issued from, since it can't create the one it's in. */
9
+ export declare const DEF_MAINTENANCE_DB = "postgres";
10
+ export declare const DEF_PORT = 5432;
11
+ /**
12
+ * `SELECT 1` readiness probe, generalized from the boot loop every OwlMeans deployment
13
+ * hand-rolled: a Postgres sidecar routinely accepts TCP before it accepts queries.
14
+ */
15
+ export declare const DEF_RETRIES = 30;
16
+ export declare const DEF_RETRY_DELAY = 2000;
17
+ /**
18
+ * Postgres caps connections cluster wide (`max_connections`, 100 by default), so the
19
+ * per-process pool stays deliberately small — unlike a Mongo driver pool, an oversized
20
+ * one here starves every other client of the same server.
21
+ */
22
+ export declare const DEF_POOL_SIZE = 10;
23
+ /**
24
+ * Failures the readiness probe treats as final rather than as "not up yet":
25
+ * `28P01`/`28000` wrong credentials, `3D000` no such database, `42501` no rights.
26
+ * Retrying any of them just delays the same error by a minute.
27
+ */
28
+ export declare const TERMINAL_CONNECT_CODES: string[];
29
+ //# sourceMappingURL=consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,aAAmB,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,eAAe,aAAa,CAAA;AAEzC,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,aAAa,CAAA;AAE5C,eAAO,MAAM,QAAQ,OAAO,CAAA;AAE5B;;;GAGG;AACH,eAAO,MAAM,WAAW,KAAK,CAAA;AAC7B,eAAO,MAAM,eAAe,OAAO,CAAA;AAEnC;;;;GAIG;AACH,eAAO,MAAM,aAAa,KAAK,CAAA;AAE/B;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,UAAuC,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { DEFAULT_DB_ALIAS } from '@owlmeans/postgres-resource';
2
+ export const DEFAULT_ALIAS = DEFAULT_DB_ALIAS;
3
+ /**
4
+ * Config alias conventionally holding the superuser connection the admin path needs.
5
+ * Never the same as {@link DEFAULT_ALIAS} — the application connects as a least
6
+ * privileged role and must not carry superuser credentials in the same config entry.
7
+ */
8
+ export const DEF_ADMIN_ALIAS = 'pg-admin';
9
+ /** The database `CREATE DATABASE` has to be issued from, since it can't create the one it's in. */
10
+ export const DEF_MAINTENANCE_DB = 'postgres';
11
+ export const DEF_PORT = 5432;
12
+ /**
13
+ * `SELECT 1` readiness probe, generalized from the boot loop every OwlMeans deployment
14
+ * hand-rolled: a Postgres sidecar routinely accepts TCP before it accepts queries.
15
+ */
16
+ export const DEF_RETRIES = 30;
17
+ export const DEF_RETRY_DELAY = 2000;
18
+ /**
19
+ * Postgres caps connections cluster wide (`max_connections`, 100 by default), so the
20
+ * per-process pool stays deliberately small — unlike a Mongo driver pool, an oversized
21
+ * one here starves every other client of the same server.
22
+ */
23
+ export const DEF_POOL_SIZE = 10;
24
+ /**
25
+ * Failures the readiness probe treats as final rather than as "not up yet":
26
+ * `28P01`/`28000` wrong credentials, `3D000` no such database, `42501` no rights.
27
+ * Retrying any of them just delays the same error by a minute.
28
+ */
29
+ export const TERMINAL_CONNECT_CODES = ['28P01', '28000', '3D000', '42501'];
30
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAE9D,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAEzC,mGAAmG;AACnG,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAE5C,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAA;AAE5B;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAC7B,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAA;AAEnC;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAE/B;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ export type * from './types.js';
2
+ export * from './consts.js';
3
+ export * from './bootstrap.js';
4
+ export * from './middleware.js';
5
+ export * from './service.js';
6
+ export * from './utils/config.js';
7
+ export * from './utils/connection.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA"}
package/build/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './consts.js';
2
+ export * from './bootstrap.js';
3
+ export * from './middleware.js';
4
+ export * from './service.js';
5
+ export * from './utils/config.js';
6
+ export * from './utils/connection.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type { Middleware } from '@owlmeans/context';
2
+ /**
3
+ * Run the work resources held back until every one of them had initialized.
4
+ *
5
+ * Foreign keys are the reason this exists: a key points at a table another resource owns,
6
+ * and resource initialization order is registration order, not dependency order. The
7
+ * Loading stage of the Context middleware type runs immediately after the last resource's
8
+ * `init()` — the first moment at which every table is known to exist.
9
+ */
10
+ export declare const drainMiddleware: (alias?: string) => Middleware;
11
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAKnD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,QAAO,MAAsB,KAAG,UAW9D,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { MiddlewareStage, MiddlewareType } from '@owlmeans/context';
2
+ import { DEFAULT_ALIAS } from './consts.js';
3
+ /**
4
+ * Run the work resources held back until every one of them had initialized.
5
+ *
6
+ * Foreign keys are the reason this exists: a key points at a table another resource owns,
7
+ * and resource initialization order is registration order, not dependency order. The
8
+ * Loading stage of the Context middleware type runs immediately after the last resource's
9
+ * `init()` — the first moment at which every table is known to exist.
10
+ */
11
+ export const drainMiddleware = (alias = DEFAULT_ALIAS) => ({
12
+ type: MiddlewareType.Context,
13
+ stage: MiddlewareStage.Loading,
14
+ apply: async (context) => {
15
+ if (!context.hasService(alias)) {
16
+ return;
17
+ }
18
+ await context.service(alias).drain();
19
+ }
20
+ });
21
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAgB,aAAa,EAAc,EAAE,CAAC,CAAC;IAC7E,IAAI,EAAE,cAAc,CAAC,OAAO;IAC5B,KAAK,EAAE,eAAe,CAAC,OAAO;IAE9B,KAAK,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;QACrB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAM;QACR,CAAC;QAED,MAAM,OAAO,CAAC,OAAO,CAAkB,KAAK,CAAC,CAAC,KAAK,EAAE,CAAA;IACvD,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { ServerConfig, ServerContext } from '@owlmeans/server-context';
2
+ import type { PostgresService } from './types.js';
3
+ type Config = ServerConfig;
4
+ interface Context<C extends Config = Config> extends ServerContext<C> {
5
+ }
6
+ export declare const makePostgresDbService: (alias?: string) => PostgresService;
7
+ export declare const appendPostgres: <C extends Config, T extends Context<C> = Context<C>>(context: T, alias?: string) => T;
8
+ export {};
9
+ //# sourceMappingURL=service.d.ts.map