@shivaedev/local-postgres 0.7.0 → 0.9.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +51 -38
  3. package/dist/server/LocalPostgresError.d.ts +8 -0
  4. package/dist/server/LocalPostgresError.d.ts.map +1 -0
  5. package/dist/server/LocalPostgresError.js +6 -0
  6. package/dist/server/LocalPostgresError.js.map +1 -0
  7. package/dist/server/PostgresCommandError.d.ts +11 -0
  8. package/dist/server/PostgresCommandError.d.ts.map +1 -0
  9. package/dist/server/PostgresCommandError.js +9 -0
  10. package/dist/server/PostgresCommandError.js.map +1 -0
  11. package/dist/server/PostgresVersionMismatch.d.ts +8 -5
  12. package/dist/server/PostgresVersionMismatch.d.ts.map +1 -1
  13. package/dist/server/PostgresVersionMismatch.js +7 -7
  14. package/dist/server/PostgresVersionMismatch.js.map +1 -1
  15. package/dist/server/clientConnection.d.ts +13 -0
  16. package/dist/server/clientConnection.d.ts.map +1 -0
  17. package/dist/server/clientConnection.js +19 -0
  18. package/dist/server/clientConnection.js.map +1 -0
  19. package/dist/server/localConnections.d.ts +16 -6
  20. package/dist/server/localConnections.d.ts.map +1 -1
  21. package/dist/server/localConnections.js +30 -24
  22. package/dist/server/localConnections.js.map +1 -1
  23. package/dist/server/localContainer.d.ts +8 -4
  24. package/dist/server/localContainer.d.ts.map +1 -1
  25. package/dist/server/localContainer.js +24 -36
  26. package/dist/server/localContainer.js.map +1 -1
  27. package/dist/server/localDocker.d.ts +7 -2
  28. package/dist/server/localDocker.d.ts.map +1 -1
  29. package/dist/server/localDocker.js +17 -9
  30. package/dist/server/localDocker.js.map +1 -1
  31. package/dist/server/localPostgres.d.ts +8 -5
  32. package/dist/server/localPostgres.d.ts.map +1 -1
  33. package/dist/server/localPostgres.js +53 -82
  34. package/dist/server/localPostgres.js.map +1 -1
  35. package/dist/server/runCommand.d.ts +7 -0
  36. package/dist/server/runCommand.d.ts.map +1 -0
  37. package/dist/server/runCommand.js +23 -0
  38. package/dist/server/runCommand.js.map +1 -0
  39. package/package.json +8 -4
  40. package/src/server/LocalPostgresError.ts +5 -0
  41. package/src/server/PostgresCommandError.ts +8 -0
  42. package/src/server/PostgresVersionMismatch.ts +7 -7
  43. package/src/server/clientConnection.ts +20 -0
  44. package/src/server/localConnections.ts +42 -24
  45. package/src/server/localContainer.ts +32 -36
  46. package/src/server/localDocker.ts +18 -10
  47. package/src/server/localPostgres.ts +62 -85
  48. package/src/server/runCommand.ts +31 -0
@@ -1 +1 @@
1
- {"version":3,"file":"localContainer.js","sourceRoot":"","sources":["../../src/server/localContainer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"localContainer.js","sourceRoot":"","sources":["../../src/server/localContainer.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,12 @@
1
- import { type ExecFileSyncOptionsWithStringEncoding } from "node:child_process";
1
+ import { Effect } from "effect";
2
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
3
+ import { type CommandOptions } from "#runCommand.ts";
2
4
  export type DockerEnvironment = Readonly<{
3
5
  "DOCKER_CONTEXT"?: string | undefined;
4
6
  "DOCKER_HOST"?: string | undefined;
5
7
  }>;
6
- export declare function docker(environment: DockerEnvironment, args: readonly string[], options?: ExecFileSyncOptionsWithStringEncoding): string;
8
+ export declare const docker: (environment: Readonly<{
9
+ DOCKER_CONTEXT?: string | undefined;
10
+ DOCKER_HOST?: string | undefined;
11
+ }>, args: readonly string[], options?: CommandOptions | undefined) => Effect.Effect<string, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
7
12
  //# sourceMappingURL=localDocker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"localDocker.d.ts","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qCAAqC,EAAgB,MAAM,oBAAoB,CAAC;AAE9F,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,wBAAgB,MAAM,CACrB,WAAW,EAAE,iBAAiB,EAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,qCAA4D,UAmBrE"}
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"}
@@ -1,16 +1,24 @@
1
- import { execFileSync } from "node:child_process";
2
- export function docker(environment, args, options = { encoding: "utf8" }) {
1
+ import { Effect } from "effect";
2
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
3
+ import { runCommand } from "#runCommand.ts";
4
+ export const docker = Effect.fn("LocalPostgres.docker")(function* (environment, args, options = {}) {
3
5
  const context = environment.DOCKER_CONTEXT;
4
6
  const host = environment.DOCKER_HOST;
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
+ };
5
15
  const endpoint = (!context && host)
6
- || execFileSync("docker", ["context", "inspect", ...(context ? [context] : []), "--format", "{{.Endpoints.docker.Host}}"], {
7
- encoding: "utf8",
8
- }).trim();
9
- const url = new URL(endpoint);
16
+ || (yield* runCommand("docker", ["context", "inspect", ...(context ? [context] : []), "--format", "{{.Endpoints.docker.Host}}"], settings));
17
+ const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid Docker endpoint." }), try: () => new URL(endpoint) });
10
18
  if (!((["unix:", "npipe:"].includes(url.protocol) && ["", "localhost"].includes(url.hostname))
11
19
  || (url.protocol === "tcp:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)))) {
12
- throw new Error("Local database setup requires a local Docker endpoint; remote contexts are refused.");
20
+ return yield* new LocalPostgresError({ message: "Local database setup requires a local Docker endpoint; remote contexts are refused." });
13
21
  }
14
- return execFileSync("docker", args, options);
15
- }
22
+ return yield* runCommand("docker", args, { ...settings, env: { ...settings.env, "DOCKER_CONTEXT": "", "DOCKER_HOST": endpoint } });
23
+ });
16
24
  //# sourceMappingURL=localDocker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"localDocker.js","sourceRoot":"","sources":["../../src/server/localDocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI9F,MAAM,UAAU,MAAM,CACrB,WAA8B,EAC9B,IAAuB,EACvB,OAAO,GAA0C,EAAE,QAAQ,EAAE,MAAM,EAAE;IAErE,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC;IACrC,MAAM,QAAQ,GACb,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC;WACf,YAAY,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;YAC1H,QAAQ,EAAE,MAAM;SAChB,CAAC,CAAC,IAAI,EAAE,CAAC;IACX,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,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,MAAM,IAAI,KAAK,CAAC,qFAAqF,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
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"}
@@ -1,13 +1,16 @@
1
- import { type DockerEnvironment } from "./localDocker.ts";
1
+ import { Effect } from "effect";
2
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
3
+ import { type DockerEnvironment } from "#localDocker.ts";
4
+ import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
2
5
  export declare const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
3
- export declare function assertLocalDatabase(value: string, names: readonly string[]): import("url").URL;
6
+ export declare const assertLocalDatabase: (value: string, names: readonly string[]) => Effect.Effect<import("url").URL, LocalPostgresError, never>;
4
7
  export interface ReadinessOptions {
5
8
  readonly attempts?: number;
6
9
  readonly delayMillis?: number;
7
10
  }
8
11
  export declare function localPostgres(environment: DockerEnvironment, { attempts, delayMillis }?: ReadinessOptions): {
9
- prepareDatabases: (values: readonly string[]) => void;
10
- sql: (value: string | URL, query: string) => string;
11
- startPostgres: (connection?: string) => void;
12
+ prepareDatabases: (values: readonly string[]) => Effect.Effect<undefined, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("#localConnections.ts").PostgresSettingError | PostgresVersionMismatch, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
13
+ sql: (value: string | URL, query: string) => Effect.Effect<string, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
14
+ startPostgres: (connection?: any) => Effect.Effect<void, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("#localConnections.ts").PostgresSettingError | PostgresVersionMismatch, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
12
15
  };
13
16
  //# sourceMappingURL=localPostgres.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAU,MAAM,kBAAkB,CAAC;AAGlE,eAAO,MAAM,YAAY,mDAAmD,CAAC;AAE7E,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
+ {"version":3,"file":"localPostgres.d.ts","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;AAE1C,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,eAAO,MAAM,mBAAmB,0GAY9B,CAAC;AAEH,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,108 +1,79 @@
1
- import { execFileSync } from "node:child_process";
2
- import { env } from "node:process";
3
- import { ensureConnections } from "./localConnections.js";
4
- import { sqlInContainer, startContainer } from "./localContainer.js";
5
- import { docker } from "./localDocker.js";
6
- import { PostgresVersionMismatch } from "./PostgresVersionMismatch.js";
1
+ import { Effect, Schedule } from "effect";
2
+ import { clientConnection } from "#clientConnection.ts";
3
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
4
+ import { ensureConnections } from "#localConnections.ts";
5
+ import { sqlInContainer, startContainer } from "#localContainer.ts";
6
+ import { docker } from "#localDocker.ts";
7
+ import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
8
+ import { missingCommand, runCommand } from "#runCommand.ts";
7
9
  export const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
8
- export function assertLocalDatabase(value, names) {
9
- const url = new URL(value);
10
- const name = url.pathname.slice(1);
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) });
11
13
  if (!(["postgres:", "postgresql:"].includes(url.protocol) && ["localhost", "127.0.0.1"].includes(url.hostname))
12
14
  || url.port !== "55432"
13
15
  || ["host", "hostaddr", "port", "dbname", "service"].some((key) => url.searchParams.has(key))
14
- || !names.includes(name)) {
15
- throw new Error(`Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`);
16
+ || !names.includes(url.pathname.slice(1))) {
17
+ return yield* new LocalPostgresError({ message });
16
18
  }
17
19
  return url;
18
- }
19
- function clientEnvironment(url) {
20
- // libpq treats + literally in URI passwords.
21
- const password = new URLSearchParams(url.search.replaceAll("+", "%2B")).getAll("password").at(-1) ?? decodeURIComponent(url.password);
22
- return { ...env, ...(password && { "PGPASSWORD": password }) };
23
- }
20
+ });
24
21
  export function localPostgres(environment, { attempts = 30, delayMillis = 1000 } = {}) {
25
- function localDocker(args, options = { encoding: "utf8" }) {
22
+ function localDocker(args, options) {
26
23
  return docker(environment, args, options);
27
24
  }
28
- function sql(value, query) {
29
- const url = new URL(value);
30
- assertLocalDatabase(url.toString(), [url.pathname.slice(1)]);
31
- const childEnvironment = clientEnvironment(url);
32
- url.password = "";
33
- url.searchParams.delete("password");
34
- for (const key of ["schema", "connection_limit", "pool_timeout", "pgbouncer"]) {
35
- url.searchParams.delete(key);
36
- }
37
- const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
38
- if (!url.searchParams.has("connect_timeout")) {
39
- url.searchParams.set("connect_timeout", "3");
40
- }
41
- const options = {
42
- encoding: "utf8",
43
- env: childEnvironment,
44
- stdio: ["ignore", "pipe", "pipe"],
45
- };
46
- try {
47
- return execFileSync("psql", [...args, "--dbname", url.toString()], options).trim();
48
- }
49
- catch (error) {
50
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
51
- return sqlInContainer(localDocker, url, args, options);
52
- }
53
- throw new Error(error instanceof Error && "stderr" in error ? String(error.stderr) : "PostgreSQL query failed.", { cause: error });
54
- }
55
- }
56
- function startPostgres(connection = LOCAL_SERVER) {
57
- const server = new URL(connection);
25
+ const sql = localSql(localDocker);
26
+ const startPostgres = Effect.fn("LocalPostgres.startPostgres")(function* (connection = LOCAL_SERVER) {
27
+ const server = yield* Effect.try({
28
+ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }),
29
+ try: () => new URL(connection),
30
+ });
58
31
  server.pathname = "/postgres";
59
- assertLocalDatabase(server.toString(), ["postgres"]);
32
+ yield* assertLocalDatabase(server.toString(), ["postgres"]);
60
33
  function waitForVersion() {
61
- for (let attempt = 0; attempt < attempts; attempt += 1) {
62
- try {
63
- return sql(server, "SHOW server_version");
64
- }
65
- catch {
66
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMillis);
67
- }
68
- }
69
- return undefined;
34
+ return sql(server, "SHOW server_version").pipe(Effect.retry({ schedule: Schedule.spaced(delayMillis), times: attempts - 1 }));
70
35
  }
71
36
  function confirmVersion(observed) {
72
- if (observed !== "18.6") {
73
- throw new PostgresVersionMismatch(observed);
74
- }
75
- }
76
- let version;
77
- try {
78
- version = sql(server, "SHOW server_version");
37
+ return /^18\.6(?:\s|$)/u.test(observed)
38
+ ? Effect.void
39
+ : Effect.fail(new PostgresVersionMismatch({ expectedVersion: "18.6", observedVersion: observed }));
79
40
  }
80
- catch {
81
- startContainer(localDocker);
82
- version = waitForVersion();
83
- }
84
- confirmVersion(version);
85
- ensureConnections(localDocker, (query) => sql(server, query), () => confirmVersion(waitForVersion()));
86
- }
87
- function prepareDatabases(values) {
41
+ const version = yield* sql(server, "SHOW server_version").pipe(Effect.catchTag("PostgresCommandError", () => Effect.andThen(startContainer(localDocker), waitForVersion)));
42
+ yield* confirmVersion(version);
43
+ yield* ensureConnections(localDocker, (query) => sql(server, query), () => Effect.flatMap(waitForVersion(), confirmVersion));
44
+ });
45
+ const prepareDatabases = Effect.fn("LocalPostgres.prepareDatabases")(function* (values) {
46
+ const urls = [];
88
47
  for (const value of values) {
89
- const name = new URL(value).pathname.slice(1);
90
- assertLocalDatabase(value, [name]);
48
+ const parsed = yield* Effect.try({
49
+ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }),
50
+ try: () => new URL(value),
51
+ });
52
+ const name = parsed.pathname.slice(1);
53
+ const url = yield* assertLocalDatabase(value, [name]);
91
54
  if (!/^[a-z][a-z0-9_]{0,62}$/u.test(name)) {
92
- throw new Error("Invalid local database name.");
55
+ return yield* new LocalPostgresError({ message: "Invalid local database name." });
93
56
  }
57
+ urls.push(url);
94
58
  }
95
- startPostgres(values[0]);
96
- for (const value of values) {
97
- const url = new URL(value);
59
+ yield* startPostgres(values[0]);
60
+ for (const url of urls) {
98
61
  const name = url.pathname.slice(1);
99
62
  const server = new URL(url);
100
63
  server.pathname = "/postgres";
101
- if (!sql(server, `SELECT 1 FROM pg_database WHERE datname = '${name}'`)) {
102
- sql(server, `CREATE DATABASE "${name}"`);
64
+ if (!(yield* sql(server, `SELECT 1 FROM pg_database WHERE datname = '${name}'`))) {
65
+ yield* sql(server, `CREATE DATABASE "${name}"`);
103
66
  }
104
67
  }
105
- }
68
+ });
106
69
  return { prepareDatabases, sql, startPostgres };
107
70
  }
71
+ function localSql(localDocker) {
72
+ return Effect.fn("LocalPostgres.sql")(function* (value, query) {
73
+ const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
74
+ const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
75
+ const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
76
+ return yield* runCommand("psql", [...args, "--dbname", url.toString()], options).pipe(Effect.catchTag("PostgresCommandError", (error) => missingCommand(error) ? sqlInContainer(localDocker, url, args, options) : Effect.fail(error)));
77
+ });
78
+ }
108
79
  //# sourceMappingURL=localPostgres.js.map
@@ -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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"}
@@ -0,0 +1,7 @@
1
+ import { Effect } from "effect";
2
+ import { ChildProcess, ChildProcessSpawner } from "effect/process";
3
+ import { PostgresCommandError } from "#PostgresCommandError.ts";
4
+ export type CommandOptions = Pick<ChildProcess.CommandOptions, "env" | "extendEnv">;
5
+ export declare const runCommand: (command: string, args: readonly string[], options?: CommandOptions | undefined) => Effect.Effect<string, PostgresCommandError, ChildProcessSpawner.ChildProcessSpawner>;
6
+ export declare function missingCommand(error: PostgresCommandError): boolean;
7
+ //# sourceMappingURL=runCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCommand.d.ts","sourceRoot":"","sources":["../../src/server/runCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAyB,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,GAAG,WAAW,CAAC,CAAC;AAEpF,eAAO,MAAM,UAAU,0KAoBrB,CAAC;AAEH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,WAEzD"}
@@ -0,0 +1,23 @@
1
+ import { Effect, PlatformError, Stream } from "effect";
2
+ import { ChildProcess, ChildProcessSpawner } from "effect/process";
3
+ import { PostgresCommandError } from "#PostgresCommandError.ts";
4
+ export const runCommand = Effect.fn("LocalPostgres.runCommand")(function* (command, args, options = {}) {
5
+ const label = command === "docker" ? `docker ${args[0] ?? ""}`.trim() : command;
6
+ return yield* Effect.scoped(Effect.gen(function* () {
7
+ const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
8
+ const child = yield* spawner.spawn(ChildProcess.make(command, args, { ...options, stdin: "ignore" }));
9
+ const [stdout, stderr, code] = yield* Effect.all([Stream.mkString(Stream.decodeText(child.stdout)), Stream.mkString(Stream.decodeText(child.stderr)), child.exitCode], { concurrency: "unbounded" });
10
+ if (code !== 0) {
11
+ return yield* new PostgresCommandError({
12
+ command: label,
13
+ exitCode: Number(code),
14
+ message: stderr.trim() || `${command} exited with code ${code}.`,
15
+ });
16
+ }
17
+ return stdout.trim();
18
+ })).pipe(Effect.catchTag("PlatformError", (cause) => Effect.fail(new PostgresCommandError({ cause, command: label, message: cause.message }))));
19
+ });
20
+ export function missingCommand(error) {
21
+ return error.cause instanceof PlatformError.PlatformError && error.cause.reason._tag === "NotFound";
22
+ }
23
+ //# sourceMappingURL=runCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCommand.js","sourceRoot":"","sources":["../../src/server/runCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIhE,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAe,EAAE,IAAuB,EAAE,OAAO,GAAmB,EAAE;IAChJ,MAAM,KAAK,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAChF,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAC1B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,mBAAmB,CAAC;QAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QACtG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAC/C,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,EACpH,EAAE,WAAW,EAAE,WAAW,EAAE,CAC5B,CAAC;QACF,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC,CAAC,IAAI,oBAAoB,CAAC;gBACtC,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC;gBACtB,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,qBAAqB,IAAI,GAAG;aAChE,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC,CAAC,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/I,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,KAA2B;IACzD,OAAO,KAAK,CAAC,KAAK,YAAY,aAAa,CAAC,aAAa,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;AACrG,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/local-postgres",
3
- "version": "0.7.0",
3
+ "version": "0.9.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": {
@@ -40,13 +40,17 @@
40
40
  "README.md"
41
41
  ],
42
42
  "devDependencies": {
43
- "@shivaedev/quality": "0.35.0",
44
- "@shivaedev/test-story": "0.18.2",
43
+ "@effect/platform-node": "4.0.6",
44
+ "@shivaedev/quality": "0.55.0",
45
+ "@shivaedev/test-story": "0.33.0",
45
46
  "@types/node": "24.19.2",
46
- "effect": "4.0.5",
47
+ "effect": "4.0.6",
47
48
  "typescript": "7.0.2",
48
49
  "vitest": "5.0.5"
49
50
  },
51
+ "peerDependencies": {
52
+ "effect": "4.0.6"
53
+ },
50
54
  "engines": {
51
55
  "node": ">=24"
52
56
  },
@@ -0,0 +1,5 @@
1
+ import { Schema } from "effect";
2
+
3
+ export class LocalPostgresError extends Schema.TaggedError<LocalPostgresError>()("LocalPostgresError", {
4
+ message: Schema.String,
5
+ }) {}
@@ -0,0 +1,8 @@
1
+ import { Schema } from "effect";
2
+
3
+ export class PostgresCommandError extends Schema.TaggedError<PostgresCommandError>()("PostgresCommandError", {
4
+ cause: Schema.optional(Schema.Unknown),
5
+ command: Schema.String,
6
+ exitCode: Schema.optional(Schema.Number),
7
+ message: Schema.String,
8
+ }) {}
@@ -1,10 +1,10 @@
1
- export class PostgresVersionMismatch extends Error {
2
- readonly _tag = "PostgresVersionMismatch";
3
- readonly expectedVersion = "18.6";
4
- readonly observedVersion: string | undefined;
1
+ import { Schema } from "effect";
5
2
 
6
- constructor(observedVersion: string | undefined) {
7
- super("Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.");
8
- this.observedVersion = observedVersion;
3
+ export class PostgresVersionMismatch extends Schema.TaggedError<PostgresVersionMismatch>()("PostgresVersionMismatch", {
4
+ expectedVersion: Schema.Literal("18.6"),
5
+ observedVersion: Schema.String,
6
+ }) {
7
+ override get message() {
8
+ return "Expected local PostgreSQL 18.6. Check the existing service; setup never removes containers or data.";
9
9
  }
10
10
  }
@@ -0,0 +1,20 @@
1
+ import { Effect } from "effect";
2
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
3
+ import type { CommandOptions } from "#runCommand.ts";
4
+
5
+ export const clientConnection = Effect.fn("LocalPostgres.clientConnection")(function* (value: URL, options: CommandOptions = {}) {
6
+ const url = new URL(value);
7
+ // libpq treats + literally in URI passwords.
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
+ });
12
+ url.password = "";
13
+ for (const key of ["password", "schema", "connection_limit", "pool_timeout", "pgbouncer"]) {
14
+ url.searchParams.delete(key);
15
+ }
16
+ if (!url.searchParams.has("connect_timeout")) {
17
+ url.searchParams.set("connect_timeout", "3");
18
+ }
19
+ return { options: { ...options, env: { ...options.env, ...(password && { "PGPASSWORD": password }) }, extendEnv: true }, url };
20
+ });
@@ -1,35 +1,53 @@
1
- import { POSTGRES_IMAGE } from "./localContainer.ts";
1
+ import { Effect, Schema } from "effect";
2
+ import type { ChildProcessSpawner } from "effect/process";
3
+ import { LocalPostgresError } from "#LocalPostgresError.ts";
4
+ import { POSTGRES_IMAGE } from "#localContainer.ts";
5
+ import type { docker as localDocker } from "#localDocker.ts";
6
+ import type { PostgresCommandError } from "#PostgresCommandError.ts";
7
+ import type { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
2
8
 
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";
9
+ export class PostgresSettingError extends Schema.TaggedError<PostgresSettingError>()("PostgresSettingError", {
10
+ cause: Schema.Unknown,
11
+ container: Schema.Literal("development-postgres"),
12
+ setting: Schema.Literal("max_connections"),
13
+ }) {
14
+ override get message() {
15
+ return "Could not set max_connections to at least 500 in container development-postgres.";
11
16
  }
12
17
  }
13
18
 
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"));
19
+ type Query = (query: string) => Effect.Effect<string, LocalPostgresError | PostgresCommandError, ChildProcessSpawner.ChildProcessSpawner>;
20
+
21
+ export const ensureConnections = Effect.fn("LocalPostgres.ensureConnections")(function* (
22
+ docker: (args: readonly string[]) => ReturnType<typeof localDocker>,
23
+ sql: Query,
24
+ ready: () => Effect.Effect<void, LocalPostgresError | PostgresCommandError | PostgresVersionMismatch, ChildProcessSpawner.ChildProcessSpawner>,
25
+ ) {
26
+ yield* Effect.gen(function* () {
27
+ const current = Number(yield* sql("SHOW max_connections"));
17
28
  if (!Number.isInteger(current) || current <= 0) {
18
- throw new Error("PostgreSQL returned an invalid max_connections value.");
29
+ return yield* new LocalPostgresError({ message: "PostgreSQL returned an invalid max_connections value." });
19
30
  }
20
31
  if (current >= 500) {
21
32
  return;
22
33
  }
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.");
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) {
42
+ return yield* new LocalPostgresError({
43
+ message: "The shared PostgreSQL container uses a different image. Preserve its data and upgrade it explicitly.",
44
+ });
25
45
  }
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.");
46
+ yield* sql("ALTER SYSTEM SET max_connections = 500");
47
+ yield* docker(["restart", "development-postgres"]);
48
+ yield* ready();
49
+ if (!(Number(yield* sql("SHOW max_connections")) >= 500)) {
50
+ return yield* new LocalPostgresError({ message: "PostgreSQL did not apply max_connections after restarting." });
31
51
  }
32
- } catch (cause) {
33
- throw new PostgresSettingError("Could not set max_connections to at least 500 in container development-postgres.", { cause });
34
- }
35
- }
52
+ }).pipe(Effect.mapError((cause) => new PostgresSettingError({ cause, container: "development-postgres", setting: "max_connections" })));
53
+ });