@jskit-ai/database-runtime-postgres 0.1.156 → 0.1.158

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/package.json CHANGED
@@ -1,72 +1,34 @@
1
1
  {
2
2
  "name": "@jskit-ai/database-runtime-postgres",
3
- "version": "0.1.156",
3
+ "version": "0.1.158",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
7
7
  },
8
8
  "exports": {
9
9
  "./client": "./src/client/index.js",
10
- "./server/providers/DatabaseRuntimePostgresServiceProvider": "./src/server/providers/DatabaseRuntimePostgresServiceProvider.js",
10
+ "./server/providers/PostgresDatabaseDriverProvider": "./src/server/providers/PostgresDatabaseDriverProvider.js",
11
11
  "./shared": "./src/shared/index.js",
12
12
  "./shared/dialect": "./src/shared/dialect.js"
13
13
  },
14
14
  "dependencies": {
15
- "@jskit-ai/database-runtime": "0.1.159"
15
+ "@jskit-ai/database-runtime": "0.1.161",
16
+ "pg": "^8.13.1"
16
17
  },
17
18
  "jskit": {
18
19
  "kind": "runtime",
19
- "options": {
20
- "db-host": {
21
- "required": false,
22
- "values": [],
23
- "defaultValue": "localhost",
24
- "promptLabel": "Database host",
25
- "promptHint": "Postgres host (for example 127.0.0.1)"
26
- },
27
- "db-port": {
28
- "required": false,
29
- "values": [],
30
- "defaultValue": "5432",
31
- "promptLabel": "Database port",
32
- "promptHint": "Postgres port (usually 5432)"
33
- },
34
- "db-name": {
35
- "required": true,
36
- "values": [],
37
- "promptLabel": "Database name",
38
- "promptHint": "Database name to connect to"
39
- },
40
- "db-user": {
41
- "required": true,
42
- "values": [],
43
- "promptLabel": "Database user",
44
- "promptHint": "Database username"
45
- },
46
- "db-password": {
47
- "required": true,
48
- "values": [],
49
- "inputType": "password",
50
- "promptLabel": "Database password",
51
- "promptHint": "Database password"
52
- }
53
- },
54
20
  "capabilities": {
55
21
  "provides": [
56
- "runtime.database.driver",
57
- "runtime.database.driver.postgres"
22
+ "runtime.database.driver"
58
23
  ],
59
- "requires": [
60
- "runtime.database"
61
- ]
24
+ "requires": []
62
25
  },
63
26
  "runtime": {
64
27
  "server": {
65
- "providerEntrypoint": "src/server/providers/DatabaseRuntimePostgresServiceProvider.js",
66
28
  "providers": [
67
29
  {
68
- "entrypoint": "src/server/providers/DatabaseRuntimePostgresServiceProvider.js",
69
- "export": "DatabaseRuntimePostgresServiceProvider"
30
+ "entrypoint": "src/server/providers/PostgresDatabaseDriverProvider.js",
31
+ "export": "PostgresDatabaseDriverProvider"
70
32
  }
71
33
  ]
72
34
  },
@@ -79,7 +41,7 @@
79
41
  "surfaces": [
80
42
  {
81
43
  "subpath": "./server",
82
- "summary": "Exports Postgres database runtime provider."
44
+ "summary": "Provides the exclusive runtime.database.driver capability for PostgreSQL."
83
45
  },
84
46
  {
85
47
  "subpath": "./shared",
@@ -89,115 +51,8 @@
89
51
  "subpath": "./client",
90
52
  "summary": "Exports no runtime API today (reserved client entrypoint)."
91
53
  }
92
- ],
93
- "containerTokens": {
94
- "server": [
95
- "runtime.database.driver.postgres"
96
- ],
97
- "client": []
98
- }
54
+ ]
99
55
  }
100
- },
101
- "ci": {
102
- "environment": {
103
- "DB_CLIENT": "pg",
104
- "DB_HOST": "127.0.0.1",
105
- "DB_PORT": "54320",
106
- "DB_NAME": "jskit_ci",
107
- "DB_USER": "jskit_ci",
108
- "DB_PASSWORD": "jskit_ci_only_password"
109
- },
110
- "services": [
111
- {
112
- "id": "postgres",
113
- "image": "postgres:16",
114
- "environment": {
115
- "POSTGRES_DB": "jskit_ci",
116
- "POSTGRES_USER": "jskit_ci",
117
- "POSTGRES_PASSWORD": "jskit_ci_only_password"
118
- },
119
- "ports": [
120
- "54320:5432"
121
- ],
122
- "healthCheck": {
123
- "command": "pg_isready --username=jskit_ci --dbname=jskit_ci",
124
- "interval": "10s",
125
- "timeout": "5s",
126
- "retries": 10
127
- }
128
- }
129
- ],
130
- "steps": []
131
- },
132
- "mutations": {
133
- "dependencies": {
134
- "runtime": {
135
- "@jskit-ai/database-runtime": "0.1.159",
136
- "pg": "^8.13.1"
137
- },
138
- "dev": {}
139
- },
140
- "packageJson": {
141
- "scripts": {}
142
- },
143
- "procfile": {},
144
- "files": [],
145
- "text": [
146
- {
147
- "file": ".env",
148
- "op": "upsert-env",
149
- "key": "DB_CLIENT",
150
- "value": "pg",
151
- "reason": "Configure database client driver for runtime wiring.",
152
- "category": "runtime-config",
153
- "id": "database-client-postgres"
154
- },
155
- {
156
- "file": ".env",
157
- "op": "upsert-env",
158
- "key": "DB_HOST",
159
- "value": "${option:db-host}",
160
- "reason": "Configure database host.",
161
- "category": "runtime-config",
162
- "id": "database-host"
163
- },
164
- {
165
- "file": ".env",
166
- "op": "upsert-env",
167
- "key": "DB_PORT",
168
- "value": "${option:db-port}",
169
- "reason": "Configure database port.",
170
- "category": "runtime-config",
171
- "id": "database-port"
172
- },
173
- {
174
- "file": ".env",
175
- "op": "upsert-env",
176
- "key": "DB_NAME",
177
- "value": "${option:db-name}",
178
- "reason": "Configure database name.",
179
- "category": "runtime-config",
180
- "id": "database-name"
181
- },
182
- {
183
- "file": ".env",
184
- "op": "upsert-env",
185
- "key": "DB_USER",
186
- "value": "${option:db-user}",
187
- "reason": "Configure database user.",
188
- "category": "runtime-config",
189
- "id": "database-user"
190
- },
191
- {
192
- "file": ".env",
193
- "op": "upsert-env",
194
- "key": "DB_PASSWORD",
195
- "value": "${option:db-password}",
196
- "reason": "Configure database password.",
197
- "category": "runtime-config",
198
- "id": "database-password"
199
- }
200
- ]
201
56
  }
202
57
  }
203
58
  }
@@ -0,0 +1,80 @@
1
+ ---
2
+ id: database/postgres-application
3
+ title: PostgreSQL application database
4
+ summary: Configure a JSKIT application for PostgreSQL with a fixed driver, ordinary environment values, and rebuildable Knex migrations.
5
+ keywords: database, knex, migrations, pg, postgres, postgresql
6
+ requires: @jskit-ai/database-runtime-postgres
7
+ ---
8
+
9
+ # PostgreSQL application database
10
+
11
+ ## Use when
12
+
13
+ Use this pattern when an application deliberately chooses PostgreSQL. Install
14
+ `@jskit-ai/database-runtime-postgres`; its normal npm dependencies provide the
15
+ database runtime, Knex, and `pg` without a package-setup wizard.
16
+
17
+ ## Do not use when
18
+
19
+ Do not use this pattern for MySQL, a database-neutral library, or a service that
20
+ does not own its database connection. Do not install both JSKIT drivers unless
21
+ the application genuinely selects between them at runtime.
22
+
23
+ ## Product decisions
24
+
25
+ Decide the database name, credentials, host, deployment secret source, backup
26
+ policy, and application migrations. Those are application and environment
27
+ decisions, not framework questions.
28
+
29
+ ## Invariants
30
+
31
+ - The application depends directly on the selected JSKIT driver.
32
+ - Credentials stay outside Git.
33
+ - `knexfile.js` fixes `pg`; a single-driver application does not require
34
+ `DB_CLIENT`.
35
+ - Schema changes are ordinary immutable migrations.
36
+ - The current package graph, environment, and migrations are authoritative.
37
+
38
+ ## Framework APIs
39
+
40
+ Use `createKnexMigrationConfigFromApp()` from
41
+ `@jskit-ai/database-runtime/server/knexMigrationConfig`. The running JSKIT
42
+ application automatically selects the sole installed database driver, while
43
+ Knex discovers package-owned migrations from the installed dependency graph.
44
+
45
+ ## Example files
46
+
47
+ `example/package.json` contains the runtime dependency and database scripts.
48
+ `example/knexfile.js` loads an optional local `.env` and fixes the PostgreSQL
49
+ dialect. `example/scripts/prepare-database.js` is the portable
50
+ migrate-then-seed entrypoint for managed sessions and deployments.
51
+ `example/.env.example` names the five connection values without a secret.
52
+
53
+ ## Variation points
54
+
55
+ Change scripts, migration location, connection values, and secret injection to
56
+ fit the application. Supply `DATABASE_URL` instead of individual values when
57
+ the environment owns a URL. Add `DB_CLIENT=pg` only when a shared external tool
58
+ explicitly needs a dialect selector. When the product needs initial data,
59
+ import one idempotent app-owned seed function in `prepare-database.js` and pass
60
+ it as `seed` to `prepareDatabaseFromApp()`.
61
+
62
+ ## Verification
63
+
64
+ - Install dependencies once with `npm install`.
65
+ - Run `npm run db:prepare` against a fresh disposable database.
66
+ - Run `npm run db:migrate:status` and the application verification command.
67
+ - Exercise one transaction and one negative connection case.
68
+ - When a seed exists, run `db:prepare` twice and require the second run to be safe.
69
+
70
+ `example/.github/workflows/verify.yml` is a normal app-owned CI workflow with
71
+ an explicit PostgreSQL service. Adapt it as source rather than generating it
72
+ from package metadata.
73
+
74
+ ## Avoid
75
+
76
+ - framework questionnaires for credentials
77
+ - generated `.env` secrets
78
+ - depending on a live schema as the source contract
79
+ - a second driver installed “just in case”
80
+ - migration receipts, provenance, replay logs, or generator ownership
@@ -0,0 +1,5 @@
1
+ DB_HOST=127.0.0.1
2
+ DB_PORT=5432
3
+ DB_NAME=application
4
+ DB_USER=application
5
+ DB_PASSWORD=replace-me
@@ -0,0 +1,38 @@
1
+ name: Verify
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ verify:
9
+ runs-on: ubuntu-latest
10
+ services:
11
+ postgres:
12
+ image: postgres:17
13
+ env:
14
+ POSTGRES_DB: app_test
15
+ POSTGRES_USER: app_test
16
+ POSTGRES_PASSWORD: app_test_password
17
+ ports:
18
+ - 5432:5432
19
+ options: >-
20
+ --health-cmd="pg_isready -U app_test -d app_test"
21
+ --health-interval=10s
22
+ --health-timeout=5s
23
+ --health-retries=10
24
+ env:
25
+ DB_HOST: 127.0.0.1
26
+ DB_PORT: 5432
27
+ DB_NAME: app_test
28
+ DB_USER: app_test
29
+ DB_PASSWORD: app_test_password
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: actions/setup-node@v4
33
+ with:
34
+ node-version-file: .nvmrc
35
+ cache: npm
36
+ - run: npm ci
37
+ - run: npm run db:migrate
38
+ - run: npm run verify
@@ -0,0 +1,11 @@
1
+ import { createKnexMigrationConfigFromApp } from "@jskit-ai/database-runtime/server/knexMigrationConfig";
2
+
3
+ try {
4
+ process.loadEnvFile?.(".env");
5
+ } catch (error) {
6
+ if (error?.code !== "ENOENT") {
7
+ throw error;
8
+ }
9
+ }
10
+
11
+ export default await createKnexMigrationConfigFromApp({ client: "pg" });
@@ -0,0 +1,13 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "scripts": {
5
+ "db:prepare": "node ./scripts/prepare-database.js",
6
+ "db:migrate": "knex --knexfile ./knexfile.js migrate:latest",
7
+ "db:migrate:rollback": "knex --knexfile ./knexfile.js migrate:rollback",
8
+ "db:migrate:status": "knex --knexfile ./knexfile.js migrate:list"
9
+ },
10
+ "dependencies": {
11
+ "@jskit-ai/database-runtime-postgres": "0.1.158"
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ import { prepareDatabaseFromApp } from "@jskit-ai/database-runtime/server/databaseSetup";
2
+
3
+ await prepareDatabaseFromApp({ client: "pg" });
@@ -0,0 +1,16 @@
1
+ import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
2
+ import * as postgresDriver from "../../shared/index.js";
3
+
4
+ const PostgresDatabaseDriverProvider = defineProvider({
5
+ id: "runtime.database.driver.postgres",
6
+ provides: {
7
+ driver: "runtime.database.driver"
8
+ },
9
+ setup() {
10
+ return {
11
+ driver: Object.freeze({ ...postgresDriver })
12
+ };
13
+ }
14
+ });
15
+
16
+ export { PostgresDatabaseDriverProvider };
@@ -0,0 +1,18 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFile } from "node:fs/promises";
3
+ import test from "node:test";
4
+
5
+ const patternUrl = new URL("../patterns/postgres-application/", import.meta.url);
6
+
7
+ test("PostgreSQL application pattern is driver-fixed and questionnaire-free", async () => {
8
+ const [document, manifestSource, knexfile] = await Promise.all([
9
+ readFile(new URL("PATTERN.md", patternUrl), "utf8"),
10
+ readFile(new URL("example/package.json", patternUrl), "utf8"),
11
+ readFile(new URL("example/knexfile.js", patternUrl), "utf8")
12
+ ]);
13
+ const manifest = JSON.parse(manifestSource);
14
+
15
+ assert.equal(manifest.dependencies["@jskit-ai/database-runtime-postgres"], "0.1.157");
16
+ assert.match(knexfile, /client: "pg"/u);
17
+ assert.doesNotMatch(`${document}\n${knexfile}`, /promptLabel|promptHint|\$\{option:/u);
18
+ });
@@ -4,15 +4,15 @@ import test from "node:test";
4
4
 
5
5
  import * as clientApi from "../src/client/index.js";
6
6
  import * as sharedApi from "../src/shared/index.js";
7
- import { DatabaseRuntimePostgresServiceProvider } from "../src/server/providers/DatabaseRuntimePostgresServiceProvider.js";
7
+ import { PostgresDatabaseDriverProvider } from "../src/server/providers/PostgresDatabaseDriverProvider.js";
8
8
 
9
9
  test("package exports include explicit shared and provider entrypoints", async () => {
10
10
  const packageJson = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
11
11
  const exportsMap = packageJson && typeof packageJson === "object" ? packageJson.exports : {};
12
12
  assert.equal(exportsMap["./server"], undefined);
13
13
  assert.equal(
14
- exportsMap["./server/providers/DatabaseRuntimePostgresServiceProvider"],
15
- "./src/server/providers/DatabaseRuntimePostgresServiceProvider.js"
14
+ exportsMap["./server/providers/PostgresDatabaseDriverProvider"],
15
+ "./src/server/providers/PostgresDatabaseDriverProvider.js"
16
16
  );
17
17
  assert.equal(exportsMap["./shared"], "./src/shared/index.js");
18
18
  });
@@ -21,12 +21,13 @@ test("client entrypoint exports no postgres runtime api", () => {
21
21
  assert.deepEqual(Object.keys(clientApi), []);
22
22
  });
23
23
 
24
- test("server provider module exports postgres service provider only", () => {
25
- assert.equal(typeof DatabaseRuntimePostgresServiceProvider, "function");
24
+ test("server provider module exports the declarative postgres driver provider", () => {
25
+ assert.equal(PostgresDatabaseDriverProvider.id, "runtime.database.driver.postgres");
26
+ assert.deepEqual(PostgresDatabaseDriverProvider.provides, { driver: "runtime.database.driver" });
26
27
  });
27
28
 
28
29
  test("shared entrypoint exports postgres dialect helpers", () => {
29
30
  assert.equal(sharedApi.DIALECT_ID, "pg");
30
31
  assert.equal(sharedApi.getDialectId(), "pg");
31
- assert.equal(typeof sharedApi.DatabaseRuntimePostgresServiceProvider, "undefined");
32
+ assert.equal(typeof sharedApi.PostgresDatabaseDriverProvider, "undefined");
32
33
  });
@@ -1,24 +1,23 @@
1
1
  import assert from "node:assert/strict";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
2
4
  import test from "node:test";
5
+ import { fileURLToPath } from "node:url";
3
6
  import packageJson from "../package.json" with { type: "json" };
4
7
 
5
- const packageMetadata = packageJson.jskit;
6
- import { DIALECT_ID } from "../src/shared/dialect.js";
8
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
9
 
8
- test("database-runtime-postgres contributes canonical synthetic Postgres CI requirements", () => {
9
- assert.equal(packageMetadata.ci.environment.DB_CLIENT, DIALECT_ID);
10
- assert.equal(packageMetadata.ci.environment.DB_HOST, "127.0.0.1");
11
- assert.equal(packageMetadata.ci.environment.DB_PORT, "54320");
12
- assert.match(packageMetadata.ci.environment.DB_PASSWORD, /ci_only/u);
10
+ test("database-runtime-postgres owns its driver while CI remains app-owned source", async () => {
11
+ const workflow = await readFile(
12
+ path.join(PACKAGE_ROOT, "patterns/postgres-application/example/.github/workflows/verify.yml"),
13
+ "utf8"
14
+ );
13
15
 
14
- const service = packageMetadata.ci.services.find((entry) => entry.id === "postgres");
15
- assert.equal(service.image, "postgres:16");
16
- assert.equal(service.environment.POSTGRES_DB, packageMetadata.ci.environment.DB_NAME);
17
- assert.equal(service.environment.POSTGRES_USER, packageMetadata.ci.environment.DB_USER);
18
- assert.equal(service.environment.POSTGRES_PASSWORD, packageMetadata.ci.environment.DB_PASSWORD);
19
- assert.deepEqual(service.ports, ["54320:5432"]);
20
- assert.match(service.healthCheck.command, /pg_isready/u);
21
-
22
- const clientMutation = packageMetadata.mutations.text.find((entry) => entry.key === "DB_CLIENT");
23
- assert.equal(clientMutation.value, DIALECT_ID);
16
+ assert.equal(packageJson.dependencies.pg, "^8.13.1");
17
+ assert.equal(Object.hasOwn(packageJson.jskit, "options"), false);
18
+ assert.equal(Object.hasOwn(packageJson.jskit, "mutations"), false);
19
+ assert.equal(Object.hasOwn(packageJson.jskit, "ci"), false);
20
+ assert.match(workflow, /image: postgres:17/u);
21
+ assert.match(workflow, /npm run db:migrate/u);
22
+ assert.match(workflow, /npm run verify/u);
24
23
  });
@@ -1,33 +1,23 @@
1
- import test from "node:test";
2
1
  import assert from "node:assert/strict";
3
- import { DatabaseRuntimePostgresServiceProvider } from "../src/server/providers/DatabaseRuntimePostgresServiceProvider.js";
2
+ import test from "node:test";
4
3
 
5
- function createSingletonApp() {
6
- const singletons = new Map();
4
+ import { createCapabilityRuntime, defineProvider } from "@jskit-ai/kernel/shared/capabilities";
5
+ import { PostgresDatabaseDriverProvider } from "../src/server/providers/PostgresDatabaseDriverProvider.js";
7
6
 
8
- return {
9
- has(token) {
10
- return singletons.has(token);
11
- },
12
- singleton(token, factory) {
13
- singletons.set(token, factory(this));
14
- },
15
- make(token) {
16
- if (!singletons.has(token)) {
17
- throw new Error(`Token ${String(token)} is not registered.`);
18
- }
19
- return singletons.get(token);
7
+ test("PostgresDatabaseDriverProvider provides the exclusive database driver capability", async () => {
8
+ let driver;
9
+ const consumer = defineProvider({
10
+ id: "test.postgres.consumer",
11
+ requires: { databaseDriver: "runtime.database.driver" },
12
+ setup({ databaseDriver }) {
13
+ driver = databaseDriver;
14
+ return {};
20
15
  }
21
- };
22
- }
23
-
24
- test("DatabaseRuntimePostgresServiceProvider registers postgres driver api", () => {
25
- const app = createSingletonApp();
26
- const provider = new DatabaseRuntimePostgresServiceProvider();
27
- provider.register(app);
16
+ });
17
+ const runtime = createCapabilityRuntime({ providers: [PostgresDatabaseDriverProvider, consumer] });
28
18
 
29
- assert.equal(app.has("runtime.database.driver.postgres"), true);
30
- const api = app.make("runtime.database.driver.postgres");
31
- assert.equal(api.DIALECT_ID, "pg");
32
- assert.equal(api.getDialectId(), "pg");
19
+ await runtime.start();
20
+ assert.equal(driver.DIALECT_ID, "pg");
21
+ assert.equal(driver.getDialectId(), "pg");
22
+ await runtime.shutdown();
33
23
  });
@@ -1,21 +0,0 @@
1
- import * as postgresDialect from "../../shared/index.js";
2
-
3
- const POSTGRES_DATABASE_DRIVER_API = Object.freeze({
4
- ...postgresDialect
5
- });
6
-
7
- class DatabaseRuntimePostgresServiceProvider {
8
- static id = "runtime.database.driver.postgres";
9
-
10
- register(app) {
11
- if (!app || typeof app.singleton !== "function") {
12
- throw new Error("DatabaseRuntimePostgresServiceProvider requires application singleton().");
13
- }
14
-
15
- app.singleton("runtime.database.driver.postgres", () => POSTGRES_DATABASE_DRIVER_API);
16
- }
17
-
18
- boot() {}
19
- }
20
-
21
- export { DatabaseRuntimePostgresServiceProvider };