@shivaedev/local-postgres 0.8.0 → 0.10.0
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 +18 -0
- package/README.md +8 -7
- package/dist/server/assertLocalDatabase.d.ts +4 -0
- package/dist/server/assertLocalDatabase.d.ts.map +1 -0
- package/dist/server/assertLocalDatabase.js +14 -0
- package/dist/server/assertLocalDatabase.js.map +1 -0
- package/dist/server/clientConnection.d.ts +4 -2
- package/dist/server/clientConnection.d.ts.map +1 -1
- package/dist/server/clientConnection.js +8 -3
- package/dist/server/clientConnection.js.map +1 -1
- package/dist/server/localConnections.d.ts.map +1 -1
- package/dist/server/localConnections.js +6 -1
- package/dist/server/localConnections.js.map +1 -1
- package/dist/server/localContainer.js +1 -1
- package/dist/server/localContainer.js.map +1 -1
- package/dist/server/localDocker.d.ts.map +1 -1
- package/dist/server/localDocker.js +9 -2
- package/dist/server/localDocker.js.map +1 -1
- package/dist/server/localPostgres.d.ts +0 -1
- package/dist/server/localPostgres.d.ts.map +1 -1
- package/dist/server/localPostgres.js +5 -13
- package/dist/server/localPostgres.js.map +1 -1
- package/package.json +3 -3
- package/src/server/assertLocalDatabase.ts +16 -0
- package/src/server/clientConnection.ts +8 -3
- package/src/server/localConnections.ts +8 -1
- package/src/server/localContainer.ts +1 -1
- package/src/server/localDocker.ts +9 -2
- package/src/server/localPostgres.ts +5 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0 - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- Import `assertLocalDatabase` from `@shivaedev/local-postgres/server/assertLocalDatabase.ts` instead of `@shivaedev/local-postgres/server/localPostgres.ts`. The validator has its own module so validation stays independent of process execution; its validation behavior is unchanged.
|
|
8
|
+
|
|
9
|
+
## 0.9.0 - 2026-11-02
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Pin Docker operations to the validated local endpoint and refuse conflicting Docker host or context settings in the command environment before invoking Docker.
|
|
14
|
+
- Accept PostgreSQL 18.6 distribution version suffixes and leave native server connection limits unchanged when the managed container is unavailable.
|
|
15
|
+
- Return a typed `LocalPostgresError` for malformed URL password escapes instead of a defect.
|
|
16
|
+
|
|
17
|
+
### Breaking
|
|
18
|
+
|
|
19
|
+
- `clientConnection` now returns an Effect. Replace direct calls with `yield* clientConnection(url, options)` inside an Effect program so password decoding failures stay in the typed error channel.
|
|
20
|
+
|
|
3
21
|
## 0.8.0 - 2026-10-31
|
|
4
22
|
|
|
5
23
|
### Fixed
|
package/README.md
CHANGED
|
@@ -59,7 +59,8 @@ Use `assertLocalDatabase` when the setup script owns a fixed set of database nam
|
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
import { Effect } from "effect";
|
|
62
|
-
import { assertLocalDatabase
|
|
62
|
+
import { assertLocalDatabase } from "@shivaedev/local-postgres/server/assertLocalDatabase.ts";
|
|
63
|
+
import { LOCAL_SERVER, localPostgres } from "@shivaedev/local-postgres/server/localPostgres.ts";
|
|
63
64
|
|
|
64
65
|
const setup = Effect.gen(function* () {
|
|
65
66
|
const development = `${LOCAL_SERVER}/example_dev`;
|
|
@@ -90,15 +91,15 @@ const setup = Effect.gen(function* () {
|
|
|
90
91
|
});
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
Run these setup Effects at a Node entry point with `NodeRuntime.runMain(setup.pipe(Effect.provide(NodeServices.layer)))`, as in the first example. The version query returns
|
|
94
|
+
Run these setup Effects at a Node entry point with `NodeRuntime.runMain(setup.pipe(Effect.provide(NodeServices.layer)))`, as in the first example. The version query returns `18.6` for the managed image; native distributions may append packaging details. `sql` also accepts a `URL` and returns the command's trimmed text. URLs carrying the application's `schema` and `connection_limit` settings work for this query; those settings are removed before calling PostgreSQL's client.
|
|
94
95
|
|
|
95
96
|
`startPostgres(connection)` prepares the service without choosing an application database to create. Its connection defaults to `LOCAL_SERVER`. Prefer `prepareDatabases` when the setup script needs named databases as well.
|
|
96
97
|
|
|
97
|
-
On each start, setup reads `SHOW max_connections`. When the
|
|
98
|
+
On each start, setup reads `SHOW max_connections`. Native servers retain their configured limit. When the running managed container publishes port 55432 and 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 fails with `PostgresSettingError` from `@shivaedev/local-postgres/server/localConnections.ts`, with `_tag`, `container`, `setting` and the original `cause`.
|
|
98
99
|
|
|
99
100
|
### API
|
|
100
101
|
|
|
101
|
-
Import
|
|
102
|
+
Import service preparation from `@shivaedev/local-postgres/server/localPostgres.ts` and fixed-name validation from `@shivaedev/local-postgres/server/assertLocalDatabase.ts`:
|
|
102
103
|
|
|
103
104
|
| Export | Purpose |
|
|
104
105
|
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -109,7 +110,7 @@ Import the setup API from `@shivaedev/local-postgres/server/localPostgres.ts`:
|
|
|
109
110
|
| `startPostgres(connection?: string)` | Return an Effect preparing the service; the default connection is `LOCAL_SERVER`. |
|
|
110
111
|
| `sql(value: string \| URL, query: string)` | Return an Effect executing SQL through PostgreSQL's command-line client. |
|
|
111
112
|
|
|
112
|
-
The last three functions are members of the object returned by `localPostgres`, not module-level exports. They fail in the typed error channel. `PostgresCommandError` carries the command, diagnostic message and exit code or platform cause; `LocalPostgresError` describes local-target and image refusals. Exhausted readiness checks retain their final failure; a version mismatch always names a version the service returned.
|
|
113
|
+
The last three functions are members of the object returned by `localPostgres`, not module-level exports. They fail in the typed error channel. `PostgresCommandError` carries the command, diagnostic message and exit code or platform cause; `LocalPostgresError` describes local-target and image refusals, and malformed URL password escapes. Exhausted readiness checks retain their final failure; a version mismatch always names a version the service returned.
|
|
113
114
|
|
|
114
115
|
The module export pattern also exposes the lower-level Docker and container command functions in `@shivaedev/local-postgres/server/localDocker.ts` and `@shivaedev/local-postgres/server/localContainer.ts`. Use `@shivaedev/local-postgres/server/localPostgres.ts` for application preparation.
|
|
115
116
|
|
|
@@ -127,9 +128,9 @@ The shared baseline is PostgreSQL 18.6 on loopback port 55432. Docker preparatio
|
|
|
127
128
|
postgres:18.6-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873
|
|
128
129
|
```
|
|
129
130
|
|
|
130
|
-
A newly created container uses username and password `postgres`; connection URLs supply credentials for client commands. Setup refuses a running
|
|
131
|
+
A newly created container uses username and password `postgres`; connection URLs supply credentials for client commands. Setup accepts native PostgreSQL 18.6 version strings with distribution suffixes, such as `18.6 (Debian 18.6-1.pgdg13+2)`, without requiring a managed container or Docker installation. Setup refuses a running release other than 18.6 with `PostgresVersionMismatch` from `@shivaedev/local-postgres/server/PostgresVersionMismatch.ts`. The error carries `_tag: "PostgresVersionMismatch"`, `expectedVersion` and `observedVersion`. A running PostgreSQL 17 service is refused without losing its existing rows; upgrading it while preserving its data is your job. Container preparation and container-client fallback also check the managed image; a successful host-`psql` version probe does not inspect that image.
|
|
131
132
|
|
|
132
|
-
Read Docker settings through Effect Config and pass them to `localPostgres`, as in the configuration example. 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.
|
|
133
|
+
Read Docker settings through Effect Config and pass them to `localPostgres`, as in the configuration example. 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. Each operation pins `DOCKER_HOST` to the validated endpoint and clears `DOCKER_CONTEXT`. The lower-level `docker` function refuses command environment settings that conflict with the supplied `DOCKER_HOST` or `DOCKER_CONTEXT` before invoking Docker.
|
|
133
134
|
|
|
134
135
|
### Limits
|
|
135
136
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
3
|
+
export declare const assertLocalDatabase: (value: string, names: readonly string[]) => Effect.Effect<import("url").URL, LocalPostgresError, never>;
|
|
4
|
+
//# sourceMappingURL=assertLocalDatabase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertLocalDatabase.d.ts","sourceRoot":"","sources":["../../src/server/assertLocalDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,eAAO,MAAM,mBAAmB,0GAY9B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
3
|
+
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value, names) {
|
|
4
|
+
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
5
|
+
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message }), try: () => new URL(value) });
|
|
6
|
+
if (!(["postgres:", "postgresql:"].includes(url.protocol) && ["localhost", "127.0.0.1"].includes(url.hostname))
|
|
7
|
+
|| url.port !== "55432"
|
|
8
|
+
|| ["host", "hostaddr", "port", "dbname", "service"].some((key) => url.searchParams.has(key))
|
|
9
|
+
|| !names.includes(url.pathname.slice(1))) {
|
|
10
|
+
return yield* new LocalPostgresError({ message });
|
|
11
|
+
}
|
|
12
|
+
return url;
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=assertLocalDatabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertLocalDatabase.js","sourceRoot":"","sources":["../../src/server/assertLocalDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,EAAE,CAAC,mCAAmC,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAa,EAAE,KAAwB;IACnI,MAAM,OAAO,GAAG,iDAAiD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACvF,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/G,IACC,CAAC,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;WACxG,GAAG,CAAC,IAAI,KAAK,OAAO;WACpB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;WAC1F,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EACxC,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC,CAAC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
1
3
|
import type { CommandOptions } from "#runCommand.ts";
|
|
2
|
-
export declare
|
|
4
|
+
export declare const clientConnection: (value: URL, options?: CommandOptions | undefined) => Effect.Effect<{
|
|
3
5
|
options: {
|
|
4
6
|
env: {
|
|
5
7
|
PGPASSWORD?: string;
|
|
@@ -7,5 +9,5 @@ export declare function clientConnection(value: URL, options?: CommandOptions):
|
|
|
7
9
|
extendEnv: boolean;
|
|
8
10
|
};
|
|
9
11
|
url: import("url").URL;
|
|
10
|
-
}
|
|
12
|
+
}, LocalPostgresError, never>;
|
|
11
13
|
//# sourceMappingURL=clientConnection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientConnection.d.ts","sourceRoot":"","sources":["../../src/server/clientConnection.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"clientConnection.d.ts","sourceRoot":"","sources":["../../src/server/clientConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,eAAO,MAAM,gBAAgB;;;;;;;;6BAe3B,CAAC"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
3
|
+
export const clientConnection = Effect.fn("LocalPostgres.clientConnection")(function* (value, options = {}) {
|
|
2
4
|
const url = new URL(value);
|
|
3
5
|
// libpq treats + literally in URI passwords.
|
|
4
|
-
const password =
|
|
6
|
+
const password = yield* Effect.try({
|
|
7
|
+
catch: () => new LocalPostgresError({ message: "Invalid local database password escape." }),
|
|
8
|
+
try: () => new URLSearchParams(url.search.replaceAll("+", "%2B")).getAll("password").at(-1) ?? decodeURIComponent(url.password),
|
|
9
|
+
});
|
|
5
10
|
url.password = "";
|
|
6
11
|
for (const key of ["password", "schema", "connection_limit", "pool_timeout", "pgbouncer"]) {
|
|
7
12
|
url.searchParams.delete(key);
|
|
@@ -10,5 +15,5 @@ export function clientConnection(value, options = {}) {
|
|
|
10
15
|
url.searchParams.set("connect_timeout", "3");
|
|
11
16
|
}
|
|
12
17
|
return { options: { ...options, env: { ...options.env, ...(password && { "PGPASSWORD": password }) }, extendEnv: true }, url };
|
|
13
|
-
}
|
|
18
|
+
});
|
|
14
19
|
//# sourceMappingURL=clientConnection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientConnection.js","sourceRoot":"","sources":["../../src/server/clientConnection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clientConnection.js","sourceRoot":"","sources":["../../src/server/clientConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,gCAAgC,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAU,EAAE,OAAO,GAAmB,EAAE;IAC9H,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3B,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QAClC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC;QAC3F,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC;KAC/H,CAAC,CAAC;IACH,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,CAAC;QAC3F,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC;AAChI,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localConnections.d.ts","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;;;;;;AAE3E,qBAAa,oBAAqB,SAAQ,yBAIxC;IACD,IAAa,OAAO,WAEnB;CACD;AAED,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;AAE1I,eAAO,MAAM,iBAAiB,kBACd,SAAS,MAAM,EAAE,KAAK,UAAU,CAAC,OAAO,WAAW,CAAC,2BAEtD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,oBAAoB,GAAG,uBAAuB,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"localConnections.d.ts","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;;;;;;AAE3E,qBAAa,oBAAqB,SAAQ,yBAIxC;IACD,IAAa,OAAO,WAEnB;CACD;AAED,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,CAAC;AAE1I,eAAO,MAAM,iBAAiB,kBACd,SAAS,MAAM,EAAE,KAAK,UAAU,CAAC,OAAO,WAAW,CAAC,2BAEtD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,oBAAoB,GAAG,uBAAuB,EAAE,mBAAmB,CAAC,mBAAmB,CAAC,uFA6B7I,CAAC"}
|
|
@@ -19,7 +19,12 @@ export const ensureConnections = Effect.fn("LocalPostgres.ensureConnections")(fu
|
|
|
19
19
|
if (current >= 500) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
const managed = yield* docker(["ps", "--filter", "name=^/development-postgres$", "--filter", "publish=55432", "--format", "{{.ID}}"]).pipe(Effect.catchTag("PostgresCommandError", () => Effect.succeed("")));
|
|
23
|
+
if (!managed) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const image = yield* docker(["inspect", "--format", "{{.Config.Image}}", "development-postgres"]);
|
|
27
|
+
if (image !== POSTGRES_IMAGE) {
|
|
23
28
|
return yield* new LocalPostgresError({
|
|
24
29
|
message: "The shared PostgreSQL container uses a different image. Preserve its data and upgrade it explicitly.",
|
|
25
30
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localConnections.js","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,MAAM,OAAO,oBAAqB,SAAQ,MAAM,CAAC,WAAW,EAAwB,CAAC,sBAAsB,EAAE;IAC5G,KAAK,EAAE,MAAM,CAAC,OAAO;IACrB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC;CAC1C,CAAC;IACD,IAAa,OAAO;QACnB,OAAO,kFAAkF,CAAC;IAC3F,CAAC;CACD;AAID,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,iCAAiC,CAAC,CAAC,QAAQ,CAAC,EACtF,MAAmE,EACnE,GAAU,EACV,KAA8I;IAE9I,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC,CAAC;QAC5G,CAAC;QACD,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,cAAc,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"localConnections.js","sourceRoot":"","sources":["../../src/server/localConnections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,MAAM,OAAO,oBAAqB,SAAQ,MAAM,CAAC,WAAW,EAAwB,CAAC,sBAAsB,EAAE;IAC5G,KAAK,EAAE,MAAM,CAAC,OAAO;IACrB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC;CAC1C,CAAC;IACD,IAAa,OAAO;QACnB,OAAO,kFAAkF,CAAC;IAC3F,CAAC;CACD;AAID,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,iCAAiC,CAAC,CAAC,QAAQ,CAAC,EACtF,MAAmE,EACnE,GAAU,EACV,KAA8I;IAE9I,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC,CAAC;QAC5G,CAAC;QACD,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,8BAA8B,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CACzI,MAAM,CAAC,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CACjE,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,CAAC,CAAC;QAClG,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC;gBACpC,OAAO,EAAE,sGAAsG;aAC/G,CAAC,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;QACrD,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACnD,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC1D,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,4DAA4D,EAAE,CAAC,CAAC;QACjH,CAAC;IACF,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;AACzI,CAAC,CAAC,CAAC"}
|
|
@@ -14,7 +14,7 @@ export const sqlInContainer = Effect.fn("LocalPostgres.sqlInContainer")(function
|
|
|
14
14
|
message: "The shared PostgreSQL image differs from the pinned baseline. Upgrade explicitly, preserving data.",
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
-
const { url: inside, options: settings } = clientConnection(url, options);
|
|
17
|
+
const { url: inside, options: settings } = yield* clientConnection(url, options);
|
|
18
18
|
inside.hostname = "127.0.0.1";
|
|
19
19
|
inside.port = "5432";
|
|
20
20
|
return yield* docker(["exec", "--env", "PGPASSWORD", id, "psql", ...args, "--dbname", inside.toString()], settings);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localContainer.js","sourceRoot":"","sources":["../../src/server/localContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGhE,MAAM,CAAC,MAAM,cAAc,GAAG,8FAA8F,CAAC;AAC7H,MAAM,SAAS,GAAG,sBAAsB,CAAC;AAGzC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,8BAA8B,CAAC,CAAC,QAAQ,CAAC,EAChF,MAAc,EACd,GAAQ,EACR,IAAuB,EACvB,OAAuB;IAEvB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;IAC9F,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,CAAC,IAAI,oBAAoB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kEAAkE,EAAE,CAAC,CAAC;IAC1I,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,KAAK,cAAc,EAAE,CAAC;QACnG,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC;YACpC,OAAO,EAAE,oGAAoG;SAC7G,CAAC,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"localContainer.js","sourceRoot":"","sources":["../../src/server/localContainer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localDocker.d.ts","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC,CAAC;AAExH,eAAO,MAAM,MAAM;qBAF2C,MAAM,GAAG,SAAS;kBAAkB,MAAM,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"localDocker.d.ts","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC,CAAC;AAExH,eAAO,MAAM,MAAM;qBAF2C,MAAM,GAAG,SAAS;kBAAkB,MAAM,GAAG,SAAS;4OA8BlH,CAAC"}
|
|
@@ -4,7 +4,14 @@ import { runCommand } from "#runCommand.ts";
|
|
|
4
4
|
export const docker = Effect.fn("LocalPostgres.docker")(function* (environment, args, options = {}) {
|
|
5
5
|
const context = environment.DOCKER_CONTEXT;
|
|
6
6
|
const host = environment.DOCKER_HOST;
|
|
7
|
-
|
|
7
|
+
if (["DOCKER_CONTEXT", "DOCKER_HOST"].some((key) => options.env?.[key] && options.env[key] !== environment[key])) {
|
|
8
|
+
return yield* new LocalPostgresError({ message: "Docker command environment conflicts with the Docker endpoint settings." });
|
|
9
|
+
}
|
|
10
|
+
const settings = {
|
|
11
|
+
...options,
|
|
12
|
+
env: { ...environment, ...options.env, "DOCKER_CONTEXT": context ?? "", "DOCKER_HOST": host ?? "" },
|
|
13
|
+
extendEnv: true,
|
|
14
|
+
};
|
|
8
15
|
const endpoint = (!context && host)
|
|
9
16
|
|| (yield* runCommand("docker", ["context", "inspect", ...(context ? [context] : []), "--format", "{{.Endpoints.docker.Host}}"], settings));
|
|
10
17
|
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid Docker endpoint." }), try: () => new URL(endpoint) });
|
|
@@ -12,6 +19,6 @@ export const docker = Effect.fn("LocalPostgres.docker")(function* (environment,
|
|
|
12
19
|
|| (url.protocol === "tcp:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)))) {
|
|
13
20
|
return yield* new LocalPostgresError({ message: "Local database setup requires a local Docker endpoint; remote contexts are refused." });
|
|
14
21
|
}
|
|
15
|
-
return yield* runCommand("docker", args, settings);
|
|
22
|
+
return yield* runCommand("docker", args, { ...settings, env: { ...settings.env, "DOCKER_CONTEXT": "", "DOCKER_HOST": endpoint } });
|
|
16
23
|
});
|
|
17
24
|
//# sourceMappingURL=localDocker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localDocker.js","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAuB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjE,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,QAAQ,CAAC,EAChE,WAA8B,EAC9B,IAAuB,EACvB,OAAO,GAAmB,EAAE;IAE5B,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC;IACrC,
|
|
1
|
+
{"version":3,"file":"localDocker.js","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAuB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjE,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,QAAQ,CAAC,EAChE,WAA8B,EAC9B,IAAuB,EACvB,OAAO,GAAmB,EAAE;IAE5B,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC;IACrC,IAAK,CAAC,gBAAgB,EAAE,aAAa,CAAW,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7H,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,yEAAyE,EAAE,CAAC,CAAC;IAC9H,CAAC;IACD,MAAM,QAAQ,GAAG;QAChB,GAAG,OAAO;QACV,GAAG,EAAE,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,IAAI,EAAE,EAAE;QACnG,SAAS,EAAE,IAAI;KACf,CAAC;IACF,MAAM,QAAQ,GACb,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC;WACf,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,4BAA4B,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7I,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC9I,IACC,CAAC,CACA,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;WACrF,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAC1F,EACA,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,qFAAqF,EAAE,CAAC,CAAC;IAC1I,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,EAAE,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AACpI,CAAC,CAAC,CAAC"}
|
|
@@ -3,7 +3,6 @@ import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
|
3
3
|
import { type DockerEnvironment } from "#localDocker.ts";
|
|
4
4
|
import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
|
|
5
5
|
export declare const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
6
|
-
export declare const assertLocalDatabase: (value: string, names: readonly string[]) => Effect.Effect<import("url").URL, LocalPostgresError, never>;
|
|
7
6
|
export interface ReadinessOptions {
|
|
8
7
|
readonly attempts?: number;
|
|
9
8
|
readonly delayMillis?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;AAG1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,OAAO,EAAE,KAAK,iBAAiB,EAAU,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAGtE,eAAO,MAAM,YAAY,mDAAmD,CAAC;AAE7E,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;;;;EAuDzH"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect, Schedule } from "effect";
|
|
2
|
+
import { assertLocalDatabase } from "#assertLocalDatabase.ts";
|
|
2
3
|
import { clientConnection } from "#clientConnection.ts";
|
|
3
4
|
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
4
5
|
import { ensureConnections } from "#localConnections.ts";
|
|
@@ -7,17 +8,6 @@ import { docker } from "#localDocker.ts";
|
|
|
7
8
|
import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
|
|
8
9
|
import { missingCommand, runCommand } from "#runCommand.ts";
|
|
9
10
|
export const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
10
|
-
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value, names) {
|
|
11
|
-
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
12
|
-
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message }), try: () => new URL(value) });
|
|
13
|
-
if (!(["postgres:", "postgresql:"].includes(url.protocol) && ["localhost", "127.0.0.1"].includes(url.hostname))
|
|
14
|
-
|| url.port !== "55432"
|
|
15
|
-
|| ["host", "hostaddr", "port", "dbname", "service"].some((key) => url.searchParams.has(key))
|
|
16
|
-
|| !names.includes(url.pathname.slice(1))) {
|
|
17
|
-
return yield* new LocalPostgresError({ message });
|
|
18
|
-
}
|
|
19
|
-
return url;
|
|
20
|
-
});
|
|
21
11
|
export function localPostgres(environment, { attempts = 30, delayMillis = 1000 } = {}) {
|
|
22
12
|
function localDocker(args, options) {
|
|
23
13
|
return docker(environment, args, options);
|
|
@@ -34,7 +24,9 @@ export function localPostgres(environment, { attempts = 30, delayMillis = 1000 }
|
|
|
34
24
|
return sql(server, "SHOW server_version").pipe(Effect.retry({ schedule: Schedule.spaced(delayMillis), times: attempts - 1 }));
|
|
35
25
|
}
|
|
36
26
|
function confirmVersion(observed) {
|
|
37
|
-
return
|
|
27
|
+
return /^18\.6(?:\s|$)/u.test(observed)
|
|
28
|
+
? Effect.void
|
|
29
|
+
: Effect.fail(new PostgresVersionMismatch({ expectedVersion: "18.6", observedVersion: observed }));
|
|
38
30
|
}
|
|
39
31
|
const version = yield* sql(server, "SHOW server_version").pipe(Effect.catchTag("PostgresCommandError", () => Effect.andThen(startContainer(localDocker), waitForVersion)));
|
|
40
32
|
yield* confirmVersion(version);
|
|
@@ -69,7 +61,7 @@ export function localPostgres(environment, { attempts = 30, delayMillis = 1000 }
|
|
|
69
61
|
function localSql(localDocker) {
|
|
70
62
|
return Effect.fn("LocalPostgres.sql")(function* (value, query) {
|
|
71
63
|
const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
|
|
72
|
-
const { url, options } = clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
64
|
+
const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
73
65
|
const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
|
|
74
66
|
return yield* runCommand("psql", [...args, "--dbname", url.toString()], options).pipe(Effect.catchTag("PostgresCommandError", (error) => missingCommand(error) ? sqlInContainer(localDocker, url, args, options) : Effect.fail(error)));
|
|
75
67
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localPostgres.js","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAA0B,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAuB,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjF,MAAM,CAAC,MAAM,YAAY,GAAG,gDAAgD,CAAC;
|
|
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.
|
|
3
|
+
"version": "0.10.0",
|
|
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": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@effect/platform-node": "4.0.6",
|
|
44
|
-
"@shivaedev/quality": "0.
|
|
45
|
-
"@shivaedev/test-story": "0.
|
|
44
|
+
"@shivaedev/quality": "0.56.0",
|
|
45
|
+
"@shivaedev/test-story": "0.34.0",
|
|
46
46
|
"@types/node": "24.19.2",
|
|
47
47
|
"effect": "4.0.6",
|
|
48
48
|
"typescript": "7.0.2",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
3
|
+
|
|
4
|
+
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value: string, names: readonly string[]) {
|
|
5
|
+
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
6
|
+
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message }), try: () => new URL(value) });
|
|
7
|
+
if (
|
|
8
|
+
!(["postgres:", "postgresql:"].includes(url.protocol) && ["localhost", "127.0.0.1"].includes(url.hostname))
|
|
9
|
+
|| url.port !== "55432"
|
|
10
|
+
|| ["host", "hostaddr", "port", "dbname", "service"].some((key) => url.searchParams.has(key))
|
|
11
|
+
|| !names.includes(url.pathname.slice(1))
|
|
12
|
+
) {
|
|
13
|
+
return yield* new LocalPostgresError({ message });
|
|
14
|
+
}
|
|
15
|
+
return url;
|
|
16
|
+
});
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
1
3
|
import type { CommandOptions } from "#runCommand.ts";
|
|
2
4
|
|
|
3
|
-
export
|
|
5
|
+
export const clientConnection = Effect.fn("LocalPostgres.clientConnection")(function* (value: URL, options: CommandOptions = {}) {
|
|
4
6
|
const url = new URL(value);
|
|
5
7
|
// libpq treats + literally in URI passwords.
|
|
6
|
-
const password =
|
|
8
|
+
const password = yield* Effect.try({
|
|
9
|
+
catch: () => new LocalPostgresError({ message: "Invalid local database password escape." }),
|
|
10
|
+
try: () => new URLSearchParams(url.search.replaceAll("+", "%2B")).getAll("password").at(-1) ?? decodeURIComponent(url.password),
|
|
11
|
+
});
|
|
7
12
|
url.password = "";
|
|
8
13
|
for (const key of ["password", "schema", "connection_limit", "pool_timeout", "pgbouncer"]) {
|
|
9
14
|
url.searchParams.delete(key);
|
|
@@ -12,4 +17,4 @@ export function clientConnection(value: URL, options: CommandOptions = {}) {
|
|
|
12
17
|
url.searchParams.set("connect_timeout", "3");
|
|
13
18
|
}
|
|
14
19
|
return { options: { ...options, env: { ...options.env, ...(password && { "PGPASSWORD": password }) }, extendEnv: true }, url };
|
|
15
|
-
}
|
|
20
|
+
});
|
|
@@ -31,7 +31,14 @@ export const ensureConnections = Effect.fn("LocalPostgres.ensureConnections")(fu
|
|
|
31
31
|
if (current >= 500) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
const managed = yield* docker(["ps", "--filter", "name=^/development-postgres$", "--filter", "publish=55432", "--format", "{{.ID}}"]).pipe(
|
|
35
|
+
Effect.catchTag("PostgresCommandError", () => Effect.succeed("")),
|
|
36
|
+
);
|
|
37
|
+
if (!managed) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const image = yield* docker(["inspect", "--format", "{{.Config.Image}}", "development-postgres"]);
|
|
41
|
+
if (image !== POSTGRES_IMAGE) {
|
|
35
42
|
return yield* new LocalPostgresError({
|
|
36
43
|
message: "The shared PostgreSQL container uses a different image. Preserve its data and upgrade it explicitly.",
|
|
37
44
|
});
|
|
@@ -24,7 +24,7 @@ export const sqlInContainer = Effect.fn("LocalPostgres.sqlInContainer")(function
|
|
|
24
24
|
message: "The shared PostgreSQL image differs from the pinned baseline. Upgrade explicitly, preserving data.",
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
-
const { url: inside, options: settings } = clientConnection(url, options);
|
|
27
|
+
const { url: inside, options: settings } = yield* clientConnection(url, options);
|
|
28
28
|
inside.hostname = "127.0.0.1";
|
|
29
29
|
inside.port = "5432";
|
|
30
30
|
return yield* docker(["exec", "--env", "PGPASSWORD", id, "psql", ...args, "--dbname", inside.toString()], settings);
|
|
@@ -11,7 +11,14 @@ export const docker = Effect.fn("LocalPostgres.docker")(function* (
|
|
|
11
11
|
) {
|
|
12
12
|
const context = environment.DOCKER_CONTEXT;
|
|
13
13
|
const host = environment.DOCKER_HOST;
|
|
14
|
-
|
|
14
|
+
if ((["DOCKER_CONTEXT", "DOCKER_HOST"] as const).some((key) => options.env?.[key] && options.env[key] !== environment[key])) {
|
|
15
|
+
return yield* new LocalPostgresError({ message: "Docker command environment conflicts with the Docker endpoint settings." });
|
|
16
|
+
}
|
|
17
|
+
const settings = {
|
|
18
|
+
...options,
|
|
19
|
+
env: { ...environment, ...options.env, "DOCKER_CONTEXT": context ?? "", "DOCKER_HOST": host ?? "" },
|
|
20
|
+
extendEnv: true,
|
|
21
|
+
};
|
|
15
22
|
const endpoint =
|
|
16
23
|
(!context && host)
|
|
17
24
|
|| (yield* runCommand("docker", ["context", "inspect", ...(context ? [context] : []), "--format", "{{.Endpoints.docker.Host}}"], settings));
|
|
@@ -24,5 +31,5 @@ export const docker = Effect.fn("LocalPostgres.docker")(function* (
|
|
|
24
31
|
) {
|
|
25
32
|
return yield* new LocalPostgresError({ message: "Local database setup requires a local Docker endpoint; remote contexts are refused." });
|
|
26
33
|
}
|
|
27
|
-
return yield* runCommand("docker", args, settings);
|
|
34
|
+
return yield* runCommand("docker", args, { ...settings, env: { ...settings.env, "DOCKER_CONTEXT": "", "DOCKER_HOST": endpoint } });
|
|
28
35
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect, Schedule } from "effect";
|
|
2
|
+
import { assertLocalDatabase } from "#assertLocalDatabase.ts";
|
|
2
3
|
import { clientConnection } from "#clientConnection.ts";
|
|
3
4
|
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
4
5
|
import { ensureConnections } from "#localConnections.ts";
|
|
@@ -9,20 +10,6 @@ import { type CommandOptions, missingCommand, runCommand } from "#runCommand.ts"
|
|
|
9
10
|
|
|
10
11
|
export const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
11
12
|
|
|
12
|
-
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value: string, names: readonly string[]) {
|
|
13
|
-
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
14
|
-
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message }), try: () => new URL(value) });
|
|
15
|
-
if (
|
|
16
|
-
!(["postgres:", "postgresql:"].includes(url.protocol) && ["localhost", "127.0.0.1"].includes(url.hostname))
|
|
17
|
-
|| url.port !== "55432"
|
|
18
|
-
|| ["host", "hostaddr", "port", "dbname", "service"].some((key) => url.searchParams.has(key))
|
|
19
|
-
|| !names.includes(url.pathname.slice(1))
|
|
20
|
-
) {
|
|
21
|
-
return yield* new LocalPostgresError({ message });
|
|
22
|
-
}
|
|
23
|
-
return url;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
13
|
export interface ReadinessOptions {
|
|
27
14
|
readonly attempts?: number;
|
|
28
15
|
readonly delayMillis?: number;
|
|
@@ -44,7 +31,9 @@ export function localPostgres(environment: DockerEnvironment, { attempts = 30, d
|
|
|
44
31
|
return sql(server, "SHOW server_version").pipe(Effect.retry({ schedule: Schedule.spaced(delayMillis), times: attempts - 1 }));
|
|
45
32
|
}
|
|
46
33
|
function confirmVersion(observed: string) {
|
|
47
|
-
return
|
|
34
|
+
return /^18\.6(?:\s|$)/u.test(observed)
|
|
35
|
+
? Effect.void
|
|
36
|
+
: Effect.fail(new PostgresVersionMismatch({ expectedVersion: "18.6", observedVersion: observed }));
|
|
48
37
|
}
|
|
49
38
|
const version = yield* sql(server, "SHOW server_version").pipe(
|
|
50
39
|
Effect.catchTag("PostgresCommandError", () => Effect.andThen(startContainer(localDocker), waitForVersion)),
|
|
@@ -86,7 +75,7 @@ export function localPostgres(environment: DockerEnvironment, { attempts = 30, d
|
|
|
86
75
|
function localSql(localDocker: (args: readonly string[], options?: CommandOptions) => ReturnType<typeof docker>) {
|
|
87
76
|
return Effect.fn("LocalPostgres.sql")(function* (value: string | URL, query: string) {
|
|
88
77
|
const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
|
|
89
|
-
const { url, options } = clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
78
|
+
const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
90
79
|
const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
|
|
91
80
|
return yield* runCommand("psql", [...args, "--dbname", url.toString()], options).pipe(
|
|
92
81
|
Effect.catchTag("PostgresCommandError", (error) =>
|