@shivaedev/local-postgres 0.4.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.1 - 2026-10-18
4
+
5
+ ### Changed
6
+
7
+ - The shared development PostgreSQL container now allows 500 connections. Existing containers are raised on their next start, preserving their data; containers already allowing 500 or more connections are left running unchanged.
8
+ - Setting failures throw `PostgresSettingError` from `localConnections.ts`, identifying the container and `max_connections` setting. The lower-level `ensureConnections(docker, sql, ready)` function applies and verifies the setting through the existing synchronous setup API.
9
+
3
10
  ## 0.4.0 - 2026-10-18
4
11
 
5
12
  ### Breaking
package/README.md CHANGED
@@ -14,7 +14,7 @@ assertLocalDatabase(development, ["example_dev"]);
14
14
  localPostgres(process.env).prepareDatabases([development]);
15
15
  ```
16
16
 
17
- The application chooses `example_dev`. Preparation can run again: the package's real PostgreSQL test inserts a row, repeats preparation and reads the same row back.
17
+ The application chooses `example_dev`. Preparation can run again without losing the data already in the database.
18
18
 
19
19
  ## Using it
20
20
 
@@ -49,7 +49,7 @@ Keep the explicit name checks even though the preparation functions check their
49
49
 
50
50
  ### 2. Once per feature: prepare its schema in application tooling
51
51
 
52
- After `prepareDatabases`, run the application's own schema or migration entry point. This package provides no schema model, migration ledger, fixture catalogue or seed convention. In this monorepo, `script/setup-database.ts` chooses the database names and loads test schemas; `@shivaedev/effect-sql` owns the SQL and migration APIs.
52
+ After `prepareDatabases`, run the application's own schema or migration entry point. This package provides no schema model, migration ledger, fixture catalogue or seed convention. `@shivaedev/effect-sql` owns the SQL and migration APIs.
53
53
 
54
54
  ### 3. In daily setup: prepare again and inspect with SQL
55
55
 
@@ -68,6 +68,8 @@ The version query returns the string `18.6`. `sql` also accepts a `URL` and retu
68
68
 
69
69
  `startPostgres(connection)` prepares the service without choosing an application database to create. Its connection defaults to `localServer`. Prefer `prepareDatabases` when the setup script needs named databases as well.
70
70
 
71
+ On each start, setup reads `SHOW max_connections`. When the shared container allows fewer than 500 connections, setup applies `ALTER SYSTEM SET max_connections = 500`, restarts that same container, waits for readiness and confirms the setting. Existing data stays in place. A limit of 500 or higher causes no setting change or restart. If the setting cannot be applied, setup throws `PostgresSettingError` from `localConnections.ts`, with `_tag`, `container`, `setting` and the original `cause`.
72
+
71
73
  ### API
72
74
 
73
75
  Import the setup API from `@shivaedev/local-postgres/localPostgres.ts`:
@@ -83,14 +85,7 @@ Import the setup API from `@shivaedev/local-postgres/localPostgres.ts`:
83
85
 
84
86
  The last three functions are members of the object returned by `localPostgres`, not module-level exports. They throw on failure; they are synchronous Node functions rather than Effects.
85
87
 
86
- The package's module export pattern also exposes these lower-level functions:
87
-
88
- | Module | Exports |
89
- | --- | --- |
90
- | `@shivaedev/local-postgres/localDocker.ts` | `DockerEnvironment`, the type with optional `DOCKER_CONTEXT` and `DOCKER_HOST`; `docker(environment, args, options?)`, the local-endpoint-checked Docker command function. |
91
- | `@shivaedev/local-postgres/localContainer.ts` | `startContainer(docker)`, the container-start function; `sqlInContainer(docker, url, args, options)`, the container SQL-client function. Both take a Docker command callback. |
92
-
93
- Use `localPostgres.ts` for application preparation. The lower-level functions describe the command paths it uses and take Node child-process options where shown.
88
+ The module export pattern also exposes the lower-level Docker and container command functions in `localDocker.ts` and `localContainer.ts`. Use `localPostgres.ts` for application preparation.
94
89
 
95
90
  ### Install and machine setup
96
91
 
@@ -106,7 +101,7 @@ The shared baseline is PostgreSQL 18.6 on loopback port 55432. Docker preparatio
106
101
  postgres:18.6-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873
107
102
  ```
108
103
 
109
- A newly created container uses username and password `postgres`; connection URLs supply credentials for client commands. An existing service with a different version requires an explicit upgrade that preserves its data. Container preparation and container-client fallback also check the managed image; a successful host-`psql` version probe does not inspect that image.
104
+ A newly created container uses username and password `postgres`; connection URLs supply credentials for client commands. Setup stops when a running service reports a version other than 18.6 and never replaces it; upgrading it while preserving its data is your job. No test covers that running-version check. Container preparation and container-client fallback also check the managed image; a successful host-`psql` version probe does not inspect that image.
110
105
 
111
106
  Pass the process's actual Docker settings to `localPostgres`, as `process.env` in the examples does. With `DOCKER_CONTEXT`, endpoint validation uses that context and ignores `DOCKER_HOST`; otherwise it validates `DOCKER_HOST` or the active context. Remote endpoints are rejected before a Docker operation. Docker subprocesses inherit the process environment. Supplying a different object does not configure those subprocesses.
112
107
 
@@ -116,6 +111,4 @@ Pass the process's actual Docker settings to `localPostgres`, as `process.env` i
116
111
  - Commands block the calling thread. Keep them in development and test setup tooling.
117
112
  - Keep allowed names, schema changes, test isolation and cleanup in the application. Preparation is not a database reset.
118
113
  - A prepared database name has 1–63 lowercase letters, digits or underscores and starts with a letter. An allowlisted connection URL does not bypass this SQL-name constraint.
119
- - Scripted command tests cover container start/create, image-mismatch refusal, client selection, Docker context precedence and local endpoint variants. A lifecycle test simulates host-client absence and delayed container discovery, then uses real Docker and PostgreSQL to check readiness. Real service tests cover supplied container-client credentials, password-free failure diagnostics on both clients and data preservation across repeated preparation.
120
- - Rejection of an exact running PostgreSQL version mismatch still needs a test; image-mismatch command tests do not prove that check.
121
- - Concurrent creation of the same absent database is not covered by the package's tests. Prepare databases before parallel tests begin.
114
+ - Concurrent creation of the same absent database is not supported. Prepare databases before parallel tests begin.
@@ -0,0 +1,8 @@
1
+ export declare class PostgresSettingError extends Error {
2
+ readonly _tag = "PostgresSettingError";
3
+ readonly container = "development-postgres";
4
+ readonly setting = "max_connections";
5
+ constructor(message: string, options: ErrorOptions);
6
+ }
7
+ export declare function ensureConnections(docker: (args: readonly string[]) => string, sql: (query: string) => string, ready: () => void): void;
8
+ //# sourceMappingURL=localConnections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localConnections.d.ts","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAEA,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,SAAS,0BAA0B;IAC5C,QAAQ,CAAC,OAAO,qBAAqB;IAErC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAGjD;CACD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,QAqB/H"}
@@ -0,0 +1,34 @@
1
+ import { POSTGRES_IMAGE } from "./localContainer.js";
2
+ export class PostgresSettingError extends Error {
3
+ _tag = "PostgresSettingError";
4
+ container = "development-postgres";
5
+ setting = "max_connections";
6
+ constructor(message, options) {
7
+ super(message, options);
8
+ this.name = "PostgresSettingError";
9
+ }
10
+ }
11
+ export function ensureConnections(docker, sql, ready) {
12
+ try {
13
+ const current = Number(sql("SHOW max_connections"));
14
+ if (!Number.isInteger(current) || current <= 0) {
15
+ throw new Error("PostgreSQL returned an invalid max_connections value.");
16
+ }
17
+ if (current >= 500) {
18
+ return;
19
+ }
20
+ if (docker(["inspect", "--format", "{{.Config.Image}}", "development-postgres"]).trim() !== POSTGRES_IMAGE) {
21
+ throw new Error("The shared PostgreSQL container uses a different image. Preserve its data and upgrade it explicitly.");
22
+ }
23
+ sql("ALTER SYSTEM SET max_connections = 500");
24
+ docker(["restart", "development-postgres"]);
25
+ ready();
26
+ if (!(Number(sql("SHOW max_connections")) >= 500)) {
27
+ throw new Error("PostgreSQL did not apply max_connections after restarting.");
28
+ }
29
+ }
30
+ catch (cause) {
31
+ throw new PostgresSettingError("Could not set max_connections to at least 500 in container development-postgres.", { cause });
32
+ }
33
+ }
34
+ //# sourceMappingURL=localConnections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localConnections.js","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACrC,IAAI,GAAG,sBAAsB,CAAC;IAC9B,SAAS,GAAG,sBAAsB,CAAC;IACnC,OAAO,GAAG,iBAAiB,CAAC;IAErC,YAAY,OAAe,EAAE,OAAqB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACpC,CAAC;CACD;AAED,MAAM,UAAU,iBAAiB,CAAC,MAA2C,EAAE,GAA8B,EAAE,KAAiB;IAC/H,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QACD,IAAI,MAAM,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,cAAc,EAAE,CAAC;YAC5G,MAAM,IAAI,KAAK,CAAC,sGAAsG,CAAC,CAAC;QACzH,CAAC;QACD,GAAG,CAAC,wCAAwC,CAAC,CAAC;QAC9C,MAAM,CAAC,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC,CAAC;QAC5C,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC/E,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,oBAAoB,CAAC,kFAAkF,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/H,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,iBAAiB,EAAU,MAAM,kBAAkB,CAAC;AAElE,eAAO,MAAM,WAAW,mDAAmD,CAAC;AAE5E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,qBAY1E;AAQD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,iBAAiB,EAAE,EAAE,QAAa,EAAE,WAAkB,EAAE,GAAE,gBAAqB;+BAwDvF,SAAS,MAAM,EAAE;iBApD/B,MAAM,GAAG,GAAG,SAAS,MAAM;;EAwE/C"}
1
+ {"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAU,MAAM,kBAAkB,CAAC;AAElE,eAAO,MAAM,WAAW,mDAAmD,CAAC;AAE5E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,qBAY1E;AAQD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,WAAW,EAAE,iBAAiB,EAAE,EAAE,QAAa,EAAE,WAAkB,EAAE,GAAE,gBAAqB;+BAmEvF,SAAS,MAAM,EAAE;iBA/D/B,MAAM,GAAG,GAAG,SAAS,MAAM;;EAmF/C"}
@@ -1,5 +1,6 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { env } from "node:process";
3
+ import { ensureConnections } from "./localConnections.js";
3
4
  import { sqlInContainer, startContainer } from "./localContainer.js";
4
5
  import { docker } from "./localDocker.js";
5
6
  export const localServer = "postgresql://postgres:postgres@127.0.0.1:55432";
@@ -55,25 +56,32 @@ export function localPostgres(environment, { attempts = 30, delayMillis = 1000 }
55
56
  const server = new URL(connection);
56
57
  server.pathname = "/postgres";
57
58
  assertLocalDatabase(server.toString(), ["postgres"]);
58
- let version;
59
- try {
60
- version = sql(server, "SHOW server_version");
61
- }
62
- catch {
63
- startContainer(localDocker);
59
+ function waitForVersion() {
64
60
  for (let attempt = 0; attempt < attempts; attempt += 1) {
65
61
  try {
66
- version = sql(server, "SHOW server_version");
67
- break;
62
+ return sql(server, "SHOW server_version");
68
63
  }
69
64
  catch {
70
65
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMillis);
71
66
  }
72
67
  }
68
+ return undefined;
69
+ }
70
+ function confirmVersion(observed) {
71
+ if (observed !== "18.6") {
72
+ throw new Error("Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.");
73
+ }
73
74
  }
74
- if (version !== "18.6") {
75
- throw new Error("Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.");
75
+ let version;
76
+ try {
77
+ version = sql(server, "SHOW server_version");
78
+ }
79
+ catch {
80
+ startContainer(localDocker);
81
+ version = waitForVersion();
76
82
  }
83
+ confirmVersion(version);
84
+ ensureConnections(localDocker, (query) => sql(server, query), () => confirmVersion(waitForVersion()));
77
85
  }
78
86
  function prepareDatabases(values) {
79
87
  for (const value of values) {
@@ -1 +1 @@
1
- {"version":3,"file":"localPostgres.js","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"localPostgres.js","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/local-postgres",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Repeatable local PostgreSQL preparation with one shared persistent service",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/local-postgres#readme",
6
6
  "bugs": {
@@ -36,17 +36,15 @@
36
36
  "!src/**/*.test.ts",
37
37
  "!src/**/*.spec.ts",
38
38
  "!src/**/test-support",
39
- "docs",
40
39
  "CHANGELOG.md",
41
40
  "README.md"
42
41
  ],
43
42
  "devDependencies": {
44
- "@shivaedev/quality": "0.25.0",
45
- "@shivaedev/test-story": "0.7.1",
43
+ "@shivaedev/quality": "0.27.0",
44
+ "@shivaedev/test-story": "0.9.0",
46
45
  "@types/node": "24.19.2",
47
- "@typescript/native": "npm:typescript@7.0.2",
48
46
  "effect": "4.0.4",
49
- "typescript": "npm:@typescript/typescript6@6.0.2",
47
+ "typescript": "7.0.2",
50
48
  "vitest": "5.0.3"
51
49
  },
52
50
  "engines": {
@@ -0,0 +1,35 @@
1
+ import { POSTGRES_IMAGE } from "./localContainer.ts";
2
+
3
+ export class PostgresSettingError extends Error {
4
+ readonly _tag = "PostgresSettingError";
5
+ readonly container = "development-postgres";
6
+ readonly setting = "max_connections";
7
+
8
+ constructor(message: string, options: ErrorOptions) {
9
+ super(message, options);
10
+ this.name = "PostgresSettingError";
11
+ }
12
+ }
13
+
14
+ export function ensureConnections(docker: (args: readonly string[]) => string, sql: (query: string) => string, ready: () => void) {
15
+ try {
16
+ const current = Number(sql("SHOW max_connections"));
17
+ if (!Number.isInteger(current) || current <= 0) {
18
+ throw new Error("PostgreSQL returned an invalid max_connections value.");
19
+ }
20
+ if (current >= 500) {
21
+ return;
22
+ }
23
+ if (docker(["inspect", "--format", "{{.Config.Image}}", "development-postgres"]).trim() !== POSTGRES_IMAGE) {
24
+ throw new Error("The shared PostgreSQL container uses a different image. Preserve its data and upgrade it explicitly.");
25
+ }
26
+ sql("ALTER SYSTEM SET max_connections = 500");
27
+ docker(["restart", "development-postgres"]);
28
+ ready();
29
+ if (!(Number(sql("SHOW max_connections")) >= 500)) {
30
+ throw new Error("PostgreSQL did not apply max_connections after restarting.");
31
+ }
32
+ } catch (cause) {
33
+ throw new PostgresSettingError("Could not set max_connections to at least 500 in container development-postgres.", { cause });
34
+ }
35
+ }
@@ -1,5 +1,6 @@
1
1
  import { type ExecFileSyncOptionsWithStringEncoding, execFileSync } from "node:child_process";
2
2
  import { env } from "node:process";
3
+ import { ensureConnections } from "./localConnections.ts";
3
4
  import { sqlInContainer, startContainer } from "./localContainer.ts";
4
5
  import { type DockerEnvironment, docker } from "./localDocker.ts";
5
6
 
@@ -67,23 +68,34 @@ export function localPostgres(environment: DockerEnvironment, { attempts = 30, d
67
68
  const server = new URL(connection);
68
69
  server.pathname = "/postgres";
69
70
  assertLocalDatabase(server.toString(), ["postgres"]);
70
- let version: string | undefined;
71
- try {
72
- version = sql(server, "SHOW server_version");
73
- } catch {
74
- startContainer(localDocker);
71
+ function waitForVersion() {
75
72
  for (let attempt = 0; attempt < attempts; attempt += 1) {
76
73
  try {
77
- version = sql(server, "SHOW server_version");
78
- break;
74
+ return sql(server, "SHOW server_version");
79
75
  } catch {
80
76
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMillis);
81
77
  }
82
78
  }
79
+ return undefined;
80
+ }
81
+ function confirmVersion(observed: string | undefined) {
82
+ if (observed !== "18.6") {
83
+ throw new Error("Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.");
84
+ }
83
85
  }
84
- if (version !== "18.6") {
85
- throw new Error("Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.");
86
+ let version: string | undefined;
87
+ try {
88
+ version = sql(server, "SHOW server_version");
89
+ } catch {
90
+ startContainer(localDocker);
91
+ version = waitForVersion();
86
92
  }
93
+ confirmVersion(version);
94
+ ensureConnections(
95
+ localDocker,
96
+ (query) => sql(server, query),
97
+ () => confirmVersion(waitForVersion()),
98
+ );
87
99
  }
88
100
 
89
101
  function prepareDatabases(values: readonly string[]) {
@@ -1,41 +0,0 @@
1
- # North star
2
-
3
- ## The problem
4
-
5
- Development and integration tests need PostgreSQL, but each repository has its own database names, schemas and test fixtures. Copying the whole setup script between repositories mixes those application choices with service management. A later shortcut in one copy can replace a container, lose a persistent volume or point a local setup command at a remote machine.
6
-
7
- Setup also runs more than once. The developer's useful state must survive another checkout, another dependency install and another test run. A mismatch is a reason to stop and inspect the machine, not a reason to rebuild its database service automatically.
8
-
9
- ## The ideal
10
-
11
- There is one small utility for preparing the shared local service, and each application writes only its own choices. A caller chooses explicit database names, checks incoming URLs against those names, prepares absent databases, and then runs its own schema and seed work. The common utility handles reaching the service and PostgreSQL's command-line client.
12
-
13
- Repeated preparation preserves existing data. Locality checks come before a command can act on a database or Docker endpoint. Upgrades are explicit decisions with a data-preservation plan rather than recovery work hidden inside a readiness check.
14
-
15
- ## What good looks like
16
-
17
- - A setup script names the databases it owns and validates configured URLs against those names.
18
- - A service mismatch fails clearly without destructive repair.
19
- - The application owns schemas, migrations, fixture loading, production guards and test cleanup. The package knows none of its domain tables or database naming conventions.
20
- - The common setup path stays visible: ordinary Node tools, PostgreSQL client commands and a fixed local baseline. Application runtime persistence goes through its database client and the framework's SQL path.
21
- - Tests distinguish real database preservation from inspection of generated commands. A test against an already-running PostgreSQL service cannot stand in for a test that starts Docker or exercises a missing client.
22
-
23
- ## Trade-offs
24
-
25
- When these goals conflict, prefer existing data, then an explicit local destination, then application ownership, then a small setup utility.
26
-
27
- A fixed service baseline is less flexible than a configurable process manager. It makes one shared machine service easier to reason about and avoids changing the version as a side effect of setup. A machine that needs an upgrade requires a deliberate action outside preparation.
28
-
29
- Explicit application name checks add a line to a setup script. That line records which database the script is authorized to prepare; an environment URL alone cannot answer that question.
30
-
31
- Synchronous commands are a fit for setup tooling. They should not become an application request path or a second database abstraction. A need for runtime SQL belongs with the native Effect SQL client and repository packages.
32
-
33
- ## What it deliberately leaves out
34
-
35
- - Remote or production database provisioning.
36
- - Automatic upgrades, container replacement, volume removal, database resets and test cleanup policy.
37
- - An application database catalogue, schema authoring, migrations and seeds.
38
- - Query composition, connection pooling, repository APIs or an Effect runtime owner.
39
- - A configurable multi-service process manager.
40
-
41
- The [package roadmap](./roadmap.md) owns implementation work and open decisions. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns composed database fixtures and host-level validation.
package/docs/roadmap.md DELETED
@@ -1,24 +0,0 @@
1
- # Roadmap
2
-
3
- This file owns local service preparation, its command paths and package API questions. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns composed repository and migration fixtures, application adoption and host-level validation. Their completion status is recorded there rather than repeated here.
4
-
5
- ## Built
6
-
7
- - [x] The main setup module exposes an explicit local server URL, application name validation, service preparation, missing-database preparation and SQL execution.
8
- - [x] Tests reject a remote database host, the wrong port, an unapproved database name and each redirecting query parameter (`host`, `hostaddr`, `port`, `dbname` and `service`). Invalid database identifiers are rejected before preparation. All three setup functions reject remote database targets before connecting.
9
- - [x] A real PostgreSQL test retains an inserted row across repeated preparation and checks PostgreSQL 18.6, including queries whose URLs carry `schema` and `connection_limit` settings.
10
- - [x] Scripted container tests cover daemon failures, starting an existing pinned container, creating a missing container with persistent storage and loopback publication, refusing image mismatches without replacement, and refusing client selection when discovery finds zero or multiple containers.
11
- - [x] Scripted Docker tests cover active and explicit context selection, context precedence over `DOCKER_HOST`, remote endpoint refusal, and local Unix socket, Windows pipe and loopback TCP endpoints.
12
- - [x] The lifecycle test simulates missing host `psql` and delayed container discovery, then checks readiness through the real Docker client and PostgreSQL service. It scripts container startup rather than starting or creating a real container.
13
- - [x] Real PostgreSQL tests check supplied container-client credentials and failure diagnostics on both host and Docker clients, including password-free original errors retained as causes.
14
- - [x] The installed consumer fixture checks the main module's exports and types.
15
-
16
- ## Next
17
-
18
- - [ ] Add a test for rejection of an exact running PostgreSQL version mismatch. Scripted image-mismatch checks do not prove this service-version check.
19
- - [ ] Establish whether parallel setup needs coordinated creation of an absent database; keep application test startup ordered until there is evidence for another contract.
20
-
21
- ## Open questions
22
-
23
- - Should `localPostgres(environment)` configure the environment of its Docker subprocesses, or only validate it? The implementation validates the supplied settings while Docker commands inherit the process environment. The README therefore asks callers to pass the actual process settings. Selecting a different context through the argument would need an explicit API decision and tests.
24
- - Should `docker`, `startContainer` and `sqlInContainer` be supported standalone public APIs? The package's wildcard module exports expose them, while the usage examples and installed consumer fixture use `localPostgres.ts`. Narrowing the exports would change the public API; supporting the helpers would need direct consumer and command-path tests.