@shivaedev/local-postgres 0.10.0 → 0.12.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 +48 -10
- package/dist/server/AppPostgresVersionMismatch.d.ts +11 -0
- package/dist/server/AppPostgresVersionMismatch.d.ts.map +1 -0
- package/dist/server/AppPostgresVersionMismatch.js +11 -0
- package/dist/server/AppPostgresVersionMismatch.js.map +1 -0
- package/dist/server/PostgresBinaries.d.ts +11 -0
- package/dist/server/PostgresBinaries.d.ts.map +1 -0
- package/dist/server/PostgresBinaries.js +4 -0
- package/dist/server/PostgresBinaries.js.map +1 -0
- package/dist/server/PostgresSettingError.d.ts +11 -0
- package/dist/server/PostgresSettingError.d.ts.map +1 -0
- package/dist/server/PostgresSettingError.js +11 -0
- package/dist/server/PostgresSettingError.js.map +1 -0
- package/dist/server/{clientConnection.d.ts → adapters/clientConnection.d.ts} +2 -2
- package/dist/server/adapters/clientConnection.d.ts.map +1 -0
- package/dist/server/{clientConnection.js → adapters/clientConnection.js} +1 -1
- package/dist/server/adapters/clientConnection.js.map +1 -0
- package/dist/server/adapters/docker.d.ts +8 -0
- package/dist/server/adapters/docker.d.ts.map +1 -0
- package/dist/server/{localDocker.js → adapters/docker.js} +3 -3
- package/dist/server/adapters/docker.js.map +1 -0
- package/dist/server/adapters/ensureConnections.d.ts +11 -0
- package/dist/server/adapters/ensureConnections.d.ts.map +1 -0
- package/dist/server/{localConnections.js → adapters/ensureConnections.js} +5 -13
- package/dist/server/adapters/ensureConnections.js.map +1 -0
- package/dist/server/adapters/hostPostgresBinaries.d.ts +4 -0
- package/dist/server/adapters/hostPostgresBinaries.d.ts.map +1 -0
- package/dist/server/adapters/hostPostgresBinaries.js +20 -0
- package/dist/server/adapters/hostPostgresBinaries.js.map +1 -0
- package/dist/server/{localContainer.d.ts → adapters/localContainer.d.ts} +4 -4
- package/dist/server/adapters/localContainer.d.ts.map +1 -0
- package/dist/server/{localContainer.js → adapters/localContainer.js} +3 -3
- package/dist/server/adapters/localContainer.js.map +1 -0
- package/dist/server/adapters/localSql.d.ts +6 -0
- package/dist/server/adapters/localSql.d.ts.map +1 -0
- package/dist/server/adapters/localSql.js +15 -0
- package/dist/server/adapters/localSql.js.map +1 -0
- package/dist/server/{runCommand.d.ts → adapters/runCommand.d.ts} +1 -1
- package/dist/server/adapters/runCommand.d.ts.map +1 -0
- package/dist/server/{runCommand.js → adapters/runCommand.js} +1 -1
- package/dist/server/adapters/runCommand.js.map +1 -0
- package/dist/server/appPostgres.d.ts +22 -0
- package/dist/server/appPostgres.d.ts.map +1 -0
- package/dist/server/appPostgres.js +50 -0
- package/dist/server/appPostgres.js.map +1 -0
- package/dist/server/assertLocalDatabase.d.ts +1 -1
- package/dist/server/assertLocalDatabase.d.ts.map +1 -1
- package/dist/server/assertLocalDatabase.js +1 -1
- package/dist/server/assertLocalDatabase.js.map +1 -1
- package/dist/server/dockerEnvironment.d.ts +10 -0
- package/dist/server/dockerEnvironment.d.ts.map +1 -0
- package/dist/server/dockerEnvironment.js +6 -0
- package/dist/server/dockerEnvironment.js.map +1 -0
- package/dist/server/localPostgres.d.ts +5 -5
- package/dist/server/localPostgres.d.ts.map +1 -1
- package/dist/server/localPostgres.js +7 -16
- package/dist/server/localPostgres.js.map +1 -1
- package/dist/server/migrateAppPostgres.d.ts +6 -0
- package/dist/server/migrateAppPostgres.d.ts.map +1 -0
- package/dist/server/migrateAppPostgres.js +31 -0
- package/dist/server/migrateAppPostgres.js.map +1 -0
- package/dist/server/prepareAppPostgresData.d.ts +6 -0
- package/dist/server/prepareAppPostgresData.d.ts.map +1 -0
- package/dist/server/prepareAppPostgresData.js +35 -0
- package/dist/server/prepareAppPostgresData.js.map +1 -0
- package/package.json +6 -6
- package/src/server/AppPostgresVersionMismatch.ts +11 -0
- package/src/server/PostgresBinaries.ts +9 -0
- package/src/server/PostgresSettingError.ts +11 -0
- package/src/server/{clientConnection.ts → adapters/clientConnection.ts} +2 -2
- package/src/server/{localDocker.ts → adapters/docker.ts} +3 -4
- package/src/server/{localConnections.ts → adapters/ensureConnections.ts} +7 -16
- package/src/server/adapters/hostPostgresBinaries.ts +27 -0
- package/src/server/{localContainer.ts → adapters/localContainer.ts} +5 -5
- package/src/server/adapters/localSql.ts +20 -0
- package/src/server/{runCommand.ts → adapters/runCommand.ts} +1 -1
- package/src/server/appPostgres.ts +80 -0
- package/src/server/assertLocalDatabase.ts +1 -1
- package/src/server/dockerEnvironment.ts +8 -0
- package/src/server/localPostgres.ts +9 -21
- package/src/server/migrateAppPostgres.ts +35 -0
- package/src/server/prepareAppPostgresData.ts +35 -0
- package/dist/server/clientConnection.d.ts.map +0 -1
- package/dist/server/clientConnection.js.map +0 -1
- package/dist/server/localConnections.d.ts +0 -18
- package/dist/server/localConnections.d.ts.map +0 -1
- package/dist/server/localConnections.js.map +0 -1
- package/dist/server/localContainer.d.ts.map +0 -1
- package/dist/server/localContainer.js.map +0 -1
- package/dist/server/localDocker.d.ts +0 -12
- package/dist/server/localDocker.d.ts.map +0 -1
- package/dist/server/localDocker.js.map +0 -1
- package/dist/server/runCommand.d.ts.map +0 -1
- package/dist/server/runCommand.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
2
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
3
3
|
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value, names) {
|
|
4
4
|
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
5
5
|
const url = yield* Effect.try({ catch: () => new LocalPostgresError({ message }), try: () => new URL(value) });
|
|
@@ -1 +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,
|
|
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,+BAA+B,CAAC;AAEnE,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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Config } from "effect";
|
|
2
|
+
export type DockerEnvironment = Readonly<{
|
|
3
|
+
"DOCKER_CONTEXT"?: string | undefined;
|
|
4
|
+
"DOCKER_HOST"?: string | undefined;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const dockerEnvironment: Config.Config<{
|
|
7
|
+
DOCKER_CONTEXT: string;
|
|
8
|
+
DOCKER_HOST: string;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=dockerEnvironment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerEnvironment.d.ts","sourceRoot":"","sources":["../../src/server/dockerEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,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,iBAAiB;;;EAG5B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Config } from "effect";
|
|
2
|
+
export const dockerEnvironment = Config.all({
|
|
3
|
+
"DOCKER_CONTEXT": Config.String("DOCKER_CONTEXT").pipe(Config.withDefault("")),
|
|
4
|
+
"DOCKER_HOST": Config.String("DOCKER_HOST").pipe(Config.withDefault("")),
|
|
5
|
+
});
|
|
6
|
+
//# sourceMappingURL=dockerEnvironment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dockerEnvironment.js","sourceRoot":"","sources":["../../src/server/dockerEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC;IAC3C,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC9E,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;CACxE,CAAC,CAAC"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
|
|
2
|
+
import type { DockerEnvironment } from "#server/dockerEnvironment.ts";
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import { PostgresVersionMismatch } from "#server/PostgresVersionMismatch.ts";
|
|
5
5
|
export declare const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
6
6
|
export interface ReadinessOptions {
|
|
7
7
|
readonly attempts?: number;
|
|
8
8
|
readonly delayMillis?: number;
|
|
9
9
|
}
|
|
10
10
|
export declare function localPostgres(environment: DockerEnvironment, { attempts, delayMillis }?: ReadinessOptions): {
|
|
11
|
-
prepareDatabases: (values: readonly string[]) => Effect.Effect<undefined, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("
|
|
11
|
+
prepareDatabases: (values: readonly string[]) => Effect.Effect<undefined, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("./PostgresSettingError.ts").PostgresSettingError | PostgresVersionMismatch, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
12
12
|
sql: (value: string | URL, query: string) => Effect.Effect<string, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
13
|
-
startPostgres: (connection?: any) => Effect.Effect<void, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("
|
|
13
|
+
startPostgres: (connection?: any) => Effect.Effect<void, LocalPostgresError | import("./PostgresCommandError.ts").PostgresCommandError | import("./PostgresSettingError.ts").PostgresSettingError | PostgresVersionMismatch, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
14
14
|
};
|
|
15
15
|
//# sourceMappingURL=localPostgres.d.ts.map
|
|
@@ -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;AAO1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,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,12 +1,11 @@
|
|
|
1
1
|
import { Effect, Schedule } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { PostgresVersionMismatch } from "#PostgresVersionMismatch.ts";
|
|
9
|
-
import { missingCommand, runCommand } from "#runCommand.ts";
|
|
2
|
+
import { docker } from "#server/adapters/docker.ts";
|
|
3
|
+
import { ensureConnections } from "#server/adapters/ensureConnections.ts";
|
|
4
|
+
import { startContainer } from "#server/adapters/localContainer.ts";
|
|
5
|
+
import { localSql } from "#server/adapters/localSql.ts";
|
|
6
|
+
import { assertLocalDatabase } from "#server/assertLocalDatabase.ts";
|
|
7
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
8
|
+
import { PostgresVersionMismatch } from "#server/PostgresVersionMismatch.ts";
|
|
10
9
|
export const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
11
10
|
export function localPostgres(environment, { attempts = 30, delayMillis = 1000 } = {}) {
|
|
12
11
|
function localDocker(args, options) {
|
|
@@ -58,12 +57,4 @@ export function localPostgres(environment, { attempts = 30, delayMillis = 1000 }
|
|
|
58
57
|
});
|
|
59
58
|
return { prepareDatabases, sql, startPostgres };
|
|
60
59
|
}
|
|
61
|
-
function localSql(localDocker) {
|
|
62
|
-
return Effect.fn("LocalPostgres.sql")(function* (value, query) {
|
|
63
|
-
const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
|
|
64
|
-
const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
65
|
-
const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
|
|
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)));
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
60
|
//# sourceMappingURL=localPostgres.js.map
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"localPostgres.js","sourceRoot":"","sources":["../../src/server/localPostgres.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import type { AppPostgresInstance } from "#server/appPostgres.ts";
|
|
3
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
4
|
+
export declare const backupAppPostgres: (instance: AppPostgresInstance) => Effect.Effect<string, import("effect/PlatformError").PlatformError | import("./PostgresCommandError.ts").PostgresCommandError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | PostgresBinaries>;
|
|
5
|
+
export declare function migrateAppPostgres<A, E, R>(instance: AppPostgresInstance, migration: Effect.Effect<A, E, R>): Effect.Effect<A, E | import("effect/PlatformError").PlatformError | import("./PostgresCommandError.ts").PostgresCommandError, R | import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | PostgresBinaries>;
|
|
6
|
+
//# sourceMappingURL=migrateAppPostgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateAppPostgres.d.ts","sourceRoot":"","sources":["../../src/server/migrateAppPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,eAAO,MAAM,iBAAiB,4RAyB5B,CAAC;AAEH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,8PAE3G"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Clock, Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import { runCommand } from "#server/adapters/runCommand.ts";
|
|
3
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
4
|
+
export const backupAppPostgres = Effect.fn("LocalPostgres.backupAppPostgres")(function* (instance) {
|
|
5
|
+
const fs = yield* FileSystem.FileSystem;
|
|
6
|
+
const path = yield* Path.Path;
|
|
7
|
+
const binaries = yield* PostgresBinaries;
|
|
8
|
+
const directory = path.join(instance.dataDirectory, "backups");
|
|
9
|
+
yield* fs.makeDirectory(directory, { mode: 0o700, recursive: true });
|
|
10
|
+
const timestamp = yield* Clock.currentTimeMillis;
|
|
11
|
+
const backup = yield* Effect.scoped(Effect.gen(function* () {
|
|
12
|
+
const staging = yield* fs.makeTempDirectoryScoped({ directory, prefix: `backup-${timestamp}-` });
|
|
13
|
+
const file = path.join(staging, "dump.partial");
|
|
14
|
+
yield* runCommand(binaries.pgDump, ["--format=custom", "--file", file, "--dbname", instance.connectionUrl]);
|
|
15
|
+
const completed = `${staging}.dump`;
|
|
16
|
+
yield* fs.rename(file, completed);
|
|
17
|
+
return completed;
|
|
18
|
+
}));
|
|
19
|
+
const files = (yield* fs.readDirectory(directory))
|
|
20
|
+
.filter((name) => /^backup-\d+-.+\.dump$/u.test(name))
|
|
21
|
+
.sort()
|
|
22
|
+
.reverse();
|
|
23
|
+
for (const file of files.slice(instance.backupsToKeep)) {
|
|
24
|
+
yield* fs.remove(path.join(directory, file));
|
|
25
|
+
}
|
|
26
|
+
return backup;
|
|
27
|
+
});
|
|
28
|
+
export function migrateAppPostgres(instance, migration) {
|
|
29
|
+
return backupAppPostgres(instance).pipe(Effect.andThen(migration));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=migrateAppPostgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateAppPostgres.js","sourceRoot":"","sources":["../../src/server/migrateAppPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,iCAAiC,CAAC,CAAC,QAAQ,CAAC,EAAE,QAA6B;IACrH,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAC/D,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAClC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,uBAAuB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,SAAS,GAAG,EAAE,CAAC,CAAC;QACjG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAChD,KAAK,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,iBAAiB,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;QAC5G,MAAM,SAAS,GAAG,GAAG,OAAO,OAAO,CAAC;QACpC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC,CACF,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;SAChD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACrD,IAAI,EAAE;SACN,OAAO,EAAE,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACxD,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,kBAAkB,CAAU,QAA6B,EAAE,SAAiC;IAC3G,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import { AppPostgresVersionMismatch } from "#server/AppPostgresVersionMismatch.ts";
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
5
|
+
export declare const prepareAppPostgresData: (dataDirectory: string) => Effect.Effect<undefined, AppPostgresVersionMismatch | LocalPostgresError | import("effect/PlatformError").PlatformError | import("./PostgresCommandError.ts").PostgresCommandError, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | FileSystem.FileSystem | Path.Path | PostgresBinaries>;
|
|
6
|
+
//# sourceMappingURL=prepareAppPostgresData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareAppPostgresData.d.ts","sourceRoot":"","sources":["../../src/server/prepareAppPostgresData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAEnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,eAAO,MAAM,sBAAsB,yUA4BjC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import { AppPostgresVersionMismatch } from "#server/AppPostgresVersionMismatch.ts";
|
|
3
|
+
import { runCommand } from "#server/adapters/runCommand.ts";
|
|
4
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
5
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
6
|
+
export const prepareAppPostgresData = Effect.fn("LocalPostgres.prepareAppPostgresData")(function* (dataDirectory) {
|
|
7
|
+
const binaries = yield* PostgresBinaries;
|
|
8
|
+
const fs = yield* FileSystem.FileSystem;
|
|
9
|
+
const path = yield* Path.Path;
|
|
10
|
+
const version = yield* runCommand(binaries.postgres, ["--version"]);
|
|
11
|
+
const expectedMajor = /\b(?<major>\d+)\./u.exec(version)?.groups?.major;
|
|
12
|
+
if (!expectedMajor) {
|
|
13
|
+
return yield* new LocalPostgresError({ message: `Cannot read PostgreSQL major version from ${version}.` });
|
|
14
|
+
}
|
|
15
|
+
const versionFile = path.join(dataDirectory, "PG_VERSION");
|
|
16
|
+
if (yield* fs.exists(versionFile)) {
|
|
17
|
+
const observedMajor = (yield* fs.readFileString(versionFile)).trim();
|
|
18
|
+
if (observedMajor !== expectedMajor) {
|
|
19
|
+
return yield* new AppPostgresVersionMismatch({ dataDirectory, expectedMajor, observedMajor });
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
yield* fs.makeDirectory(dataDirectory, { recursive: true });
|
|
24
|
+
yield* runCommand(binaries.initdb, [
|
|
25
|
+
"--pgdata",
|
|
26
|
+
dataDirectory,
|
|
27
|
+
"--username",
|
|
28
|
+
"postgres",
|
|
29
|
+
"--auth-local=trust",
|
|
30
|
+
"--auth-host=reject",
|
|
31
|
+
"--encoding=UTF8",
|
|
32
|
+
"--locale=C",
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=prepareAppPostgresData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareAppPostgresData.js","sourceRoot":"","sources":["../../src/server/prepareAppPostgresData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CAAC,sCAAsC,CAAC,CAAC,QAAQ,CAAC,EAAE,aAAqB;IACvH,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC;IACzC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC;IACxE,IAAI,CAAC,aAAa,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,OAAO,EAAE,6CAA6C,OAAO,GAAG,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC3D,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,IAAI,aAAa,KAAK,aAAa,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC,CAAC,IAAI,0BAA0B,CAAC,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO;IACR,CAAC;IACD,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE;QAClC,UAAU;QACV,aAAa;QACb,YAAY;QACZ,UAAU;QACV,oBAAoB;QACpB,oBAAoB;QACpB,iBAAiB;QACjB,YAAY;KACZ,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/local-postgres",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"imports": {
|
|
18
18
|
"#test/*": "./src/test-support/*",
|
|
19
19
|
"#*.ts": {
|
|
20
|
-
"source": "./src
|
|
21
|
-
"types": "./dist
|
|
22
|
-
"default": "./dist
|
|
20
|
+
"source": "./src/*.ts",
|
|
21
|
+
"types": "./dist/*.d.ts",
|
|
22
|
+
"default": "./dist/*.js"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"exports": {
|
|
@@ -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.57.1",
|
|
45
|
+
"@shivaedev/test-story": "0.36.0",
|
|
46
46
|
"@types/node": "24.19.2",
|
|
47
47
|
"effect": "4.0.6",
|
|
48
48
|
"typescript": "7.0.2",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
export class AppPostgresVersionMismatch extends Schema.TaggedError<AppPostgresVersionMismatch>()("AppPostgresVersionMismatch", {
|
|
4
|
+
dataDirectory: Schema.String,
|
|
5
|
+
expectedMajor: Schema.String,
|
|
6
|
+
observedMajor: Schema.String,
|
|
7
|
+
}) {
|
|
8
|
+
override get message() {
|
|
9
|
+
return `PostgreSQL ${this.expectedMajor} cannot open ${this.dataDirectory}: its data belongs to PostgreSQL ${this.observedMajor}.`;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
export interface PostgresExecutables {
|
|
4
|
+
readonly initdb: string;
|
|
5
|
+
readonly pgDump: string;
|
|
6
|
+
readonly postgres: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class PostgresBinaries extends Context.Service<PostgresBinaries, PostgresExecutables>()("@shivaedev/local-postgres/PostgresBinaries") {}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
export class PostgresSettingError extends Schema.TaggedError<PostgresSettingError>()("PostgresSettingError", {
|
|
4
|
+
cause: Schema.Unknown,
|
|
5
|
+
container: Schema.Literal("development-postgres"),
|
|
6
|
+
setting: Schema.Literal("max_connections"),
|
|
7
|
+
}) {
|
|
8
|
+
override get message() {
|
|
9
|
+
return "Could not set max_connections to at least 500 in container development-postgres.";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
3
|
-
import type { CommandOptions } from "
|
|
2
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
3
|
+
import type { CommandOptions } from "./runCommand.ts";
|
|
4
4
|
|
|
5
5
|
export const clientConnection = Effect.fn("LocalPostgres.clientConnection")(function* (value: URL, options: CommandOptions = {}) {
|
|
6
6
|
const url = new URL(value);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export type DockerEnvironment = Readonly<{ "DOCKER_CONTEXT"?: string | undefined; "DOCKER_HOST"?: string | undefined }>;
|
|
2
|
+
import type { DockerEnvironment } from "#server/dockerEnvironment.ts";
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import { type CommandOptions, runCommand } from "./runCommand.ts";
|
|
6
5
|
|
|
7
6
|
export const docker = Effect.fn("LocalPostgres.docker")(function* (
|
|
8
7
|
environment: DockerEnvironment,
|
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
import { Effect
|
|
1
|
+
import { Effect } from "effect";
|
|
2
2
|
import type { ChildProcessSpawner } from "effect/process";
|
|
3
|
-
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
|
|
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.";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import type { PostgresCommandError } from "#server/PostgresCommandError.ts";
|
|
5
|
+
import { PostgresSettingError } from "#server/PostgresSettingError.ts";
|
|
6
|
+
import type { PostgresVersionMismatch } from "#server/PostgresVersionMismatch.ts";
|
|
7
|
+
import type { docker as localDocker } from "./docker.ts";
|
|
8
|
+
import { POSTGRES_IMAGE } from "./localContainer.ts";
|
|
18
9
|
|
|
19
10
|
type Query = (query: string) => Effect.Effect<string, LocalPostgresError | PostgresCommandError, ChildProcessSpawner.ChildProcessSpawner>;
|
|
20
11
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Layer, Path } from "effect";
|
|
2
|
+
import { missingCommand, runCommand } from "#server/adapters/runCommand.ts";
|
|
3
|
+
import { PostgresBinaries, type PostgresExecutables } from "#server/PostgresBinaries.ts";
|
|
4
|
+
|
|
5
|
+
export function hostPostgresBinaries(directory?: string) {
|
|
6
|
+
return Layer.effect(
|
|
7
|
+
PostgresBinaries,
|
|
8
|
+
Effect.gen(function* () {
|
|
9
|
+
const path = yield* Path.Path;
|
|
10
|
+
const bin =
|
|
11
|
+
directory
|
|
12
|
+
?? (yield* runCommand("postgres", ["--version"]).pipe(
|
|
13
|
+
Effect.as(""),
|
|
14
|
+
Effect.catchTag("PostgresCommandError", (error) => (missingCommand(error) ? runCommand("pg_config", ["--bindir"]) : Effect.fail(error))),
|
|
15
|
+
));
|
|
16
|
+
const executables: PostgresExecutables = {
|
|
17
|
+
initdb: bin ? path.join(bin, "initdb") : "initdb",
|
|
18
|
+
pgDump: bin ? path.join(bin, "pg_dump") : "pg_dump",
|
|
19
|
+
postgres: bin ? path.join(bin, "postgres") : "postgres",
|
|
20
|
+
};
|
|
21
|
+
for (const command of Object.values(executables)) {
|
|
22
|
+
yield* runCommand(command, ["--version"]);
|
|
23
|
+
}
|
|
24
|
+
return executables;
|
|
25
|
+
}),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import type { CommandOptions } from "
|
|
2
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
3
|
+
import { PostgresCommandError } from "#server/PostgresCommandError.ts";
|
|
4
|
+
import { clientConnection } from "./clientConnection.ts";
|
|
5
|
+
import type { docker as runDocker } from "./docker.ts";
|
|
6
|
+
import type { CommandOptions } from "./runCommand.ts";
|
|
7
7
|
|
|
8
8
|
export const POSTGRES_IMAGE = "postgres:18.6-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873";
|
|
9
9
|
const CONTAINER = "development-postgres";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { assertLocalDatabase } from "#server/assertLocalDatabase.ts";
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import { clientConnection } from "./clientConnection.ts";
|
|
5
|
+
import type { docker } from "./docker.ts";
|
|
6
|
+
import { sqlInContainer } from "./localContainer.ts";
|
|
7
|
+
import { type CommandOptions, missingCommand, runCommand } from "./runCommand.ts";
|
|
8
|
+
|
|
9
|
+
export function localSql(localDocker: (args: readonly string[], options?: CommandOptions) => ReturnType<typeof docker>) {
|
|
10
|
+
return Effect.fn("LocalPostgres.sql")(function* (value: string | URL, query: string) {
|
|
11
|
+
const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
|
|
12
|
+
const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
13
|
+
const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
|
|
14
|
+
return yield* runCommand("psql", [...args, "--dbname", url.toString()], options).pipe(
|
|
15
|
+
Effect.catchTag("PostgresCommandError", (error) =>
|
|
16
|
+
missingCommand(error) ? sqlInContainer(localDocker, url, args, options) : Effect.fail(error),
|
|
17
|
+
),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, PlatformError, Stream } from "effect";
|
|
2
2
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
3
|
-
import { PostgresCommandError } from "#PostgresCommandError.ts";
|
|
3
|
+
import { PostgresCommandError } from "#server/PostgresCommandError.ts";
|
|
4
4
|
|
|
5
5
|
export type CommandOptions = Pick<ChildProcess.CommandOptions, "env" | "extendEnv">;
|
|
6
6
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Deferred, Effect, FileSystem, Path, Stream } from "effect";
|
|
2
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
3
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
4
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
5
|
+
import { PostgresCommandError } from "#server/PostgresCommandError.ts";
|
|
6
|
+
import { prepareAppPostgresData } from "#server/prepareAppPostgresData.ts";
|
|
7
|
+
|
|
8
|
+
export interface AppPostgresOptions {
|
|
9
|
+
readonly backupsToKeep?: number;
|
|
10
|
+
readonly dataDirectory: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AppPostgresInstance {
|
|
14
|
+
readonly backupsToKeep: number;
|
|
15
|
+
readonly connectionUrl: string;
|
|
16
|
+
readonly dataDirectory: string;
|
|
17
|
+
readonly socketDirectory: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const appPostgres = Effect.fn("LocalPostgres.appPostgres")(function* ({ dataDirectory, backupsToKeep = 5 }: AppPostgresOptions) {
|
|
21
|
+
if (!Number.isSafeInteger(backupsToKeep) || backupsToKeep < 1) {
|
|
22
|
+
return yield* new LocalPostgresError({ message: "backupsToKeep must be a positive integer." });
|
|
23
|
+
}
|
|
24
|
+
const fs = yield* FileSystem.FileSystem;
|
|
25
|
+
const path = yield* Path.Path;
|
|
26
|
+
const binaries = yield* PostgresBinaries;
|
|
27
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
28
|
+
const directory = path.resolve(dataDirectory);
|
|
29
|
+
yield* prepareAppPostgresData(directory);
|
|
30
|
+
const socketDirectory = yield* fs.makeTempDirectoryScoped({ prefix: "app-postgres-" });
|
|
31
|
+
yield* fs.chmod(socketDirectory, 0o700);
|
|
32
|
+
const child = yield* spawner.spawn(
|
|
33
|
+
ChildProcess.make(
|
|
34
|
+
binaries.postgres,
|
|
35
|
+
[
|
|
36
|
+
"-D",
|
|
37
|
+
directory,
|
|
38
|
+
"-h",
|
|
39
|
+
"",
|
|
40
|
+
"-k",
|
|
41
|
+
socketDirectory,
|
|
42
|
+
"-c",
|
|
43
|
+
"unix_socket_permissions=0700",
|
|
44
|
+
"-c",
|
|
45
|
+
"logging_collector=off",
|
|
46
|
+
"-c",
|
|
47
|
+
"log_destination=stderr",
|
|
48
|
+
"-c",
|
|
49
|
+
"lc_messages=C",
|
|
50
|
+
],
|
|
51
|
+
{ env: { "LC_ALL": "C" }, extendEnv: true, killSignal: "SIGINT", stdin: "ignore", stdout: "ignore" },
|
|
52
|
+
),
|
|
53
|
+
);
|
|
54
|
+
const ready = yield* Deferred.make<void>();
|
|
55
|
+
let diagnostics = "";
|
|
56
|
+
yield* child.stderr.pipe(
|
|
57
|
+
Stream.decodeText,
|
|
58
|
+
Stream.splitLines,
|
|
59
|
+
Stream.runForEach((line) => {
|
|
60
|
+
diagnostics = line;
|
|
61
|
+
return line.includes("database system is ready to accept connections") ? Deferred.succeed(ready, undefined) : Effect.void;
|
|
62
|
+
}),
|
|
63
|
+
Effect.forkScoped,
|
|
64
|
+
);
|
|
65
|
+
yield* Effect.raceFirst(
|
|
66
|
+
Deferred.await(ready),
|
|
67
|
+
Effect.flatMap(child.exitCode, (code) =>
|
|
68
|
+
Effect.fail(
|
|
69
|
+
new PostgresCommandError({
|
|
70
|
+
command: binaries.postgres,
|
|
71
|
+
exitCode: Number(code),
|
|
72
|
+
message: diagnostics || "PostgreSQL exited before becoming ready.",
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
).pipe(Effect.timeout("30 seconds"));
|
|
77
|
+
const connection = new URL("postgresql://postgres@localhost/postgres");
|
|
78
|
+
connection.searchParams.set("host", socketDirectory);
|
|
79
|
+
return { backupsToKeep, connectionUrl: connection.toString(), dataDirectory: directory, socketDirectory } satisfies AppPostgresInstance;
|
|
80
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { LocalPostgresError } from "#LocalPostgresError.ts";
|
|
2
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
3
3
|
|
|
4
4
|
export const assertLocalDatabase = Effect.fn("LocalPostgres.assertLocalDatabase")(function* (value: string, names: readonly string[]) {
|
|
5
5
|
const message = `Use a local database on 127.0.0.1:55432 named ${names.join(" or ")}.`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config } from "effect";
|
|
2
|
+
|
|
3
|
+
export type DockerEnvironment = Readonly<{ "DOCKER_CONTEXT"?: string | undefined; "DOCKER_HOST"?: string | undefined }>;
|
|
4
|
+
|
|
5
|
+
export const dockerEnvironment = Config.all({
|
|
6
|
+
"DOCKER_CONTEXT": Config.String("DOCKER_CONTEXT").pipe(Config.withDefault("")),
|
|
7
|
+
"DOCKER_HOST": Config.String("DOCKER_HOST").pipe(Config.withDefault("")),
|
|
8
|
+
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Effect, Schedule } from "effect";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
2
|
+
import { docker } from "#server/adapters/docker.ts";
|
|
3
|
+
import { ensureConnections } from "#server/adapters/ensureConnections.ts";
|
|
4
|
+
import { startContainer } from "#server/adapters/localContainer.ts";
|
|
5
|
+
import { localSql } from "#server/adapters/localSql.ts";
|
|
6
|
+
import type { CommandOptions } from "#server/adapters/runCommand.ts";
|
|
7
|
+
import { assertLocalDatabase } from "#server/assertLocalDatabase.ts";
|
|
8
|
+
import type { DockerEnvironment } from "#server/dockerEnvironment.ts";
|
|
9
|
+
import { LocalPostgresError } from "#server/LocalPostgresError.ts";
|
|
10
|
+
import { PostgresVersionMismatch } from "#server/PostgresVersionMismatch.ts";
|
|
10
11
|
|
|
11
12
|
export const LOCAL_SERVER = "postgresql://postgres:postgres@127.0.0.1:55432";
|
|
12
13
|
|
|
@@ -71,16 +72,3 @@ export function localPostgres(environment: DockerEnvironment, { attempts = 30, d
|
|
|
71
72
|
});
|
|
72
73
|
return { prepareDatabases, sql, startPostgres };
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
function localSql(localDocker: (args: readonly string[], options?: CommandOptions) => ReturnType<typeof docker>) {
|
|
76
|
-
return Effect.fn("LocalPostgres.sql")(function* (value: string | URL, query: string) {
|
|
77
|
-
const parsed = yield* Effect.try({ catch: () => new LocalPostgresError({ message: "Invalid local database URL." }), try: () => new URL(value) });
|
|
78
|
-
const { url, options } = yield* clientConnection(yield* assertLocalDatabase(parsed.toString(), [parsed.pathname.slice(1)]));
|
|
79
|
-
const args = ["-X", "--set", "ON_ERROR_STOP=1", "-At", "-c", query];
|
|
80
|
-
return yield* runCommand("psql", [...args, "--dbname", url.toString()], options).pipe(
|
|
81
|
-
Effect.catchTag("PostgresCommandError", (error) =>
|
|
82
|
-
missingCommand(error) ? sqlInContainer(localDocker, url, args, options) : Effect.fail(error),
|
|
83
|
-
),
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Clock, Effect, FileSystem, Path } from "effect";
|
|
2
|
+
import { runCommand } from "#server/adapters/runCommand.ts";
|
|
3
|
+
import type { AppPostgresInstance } from "#server/appPostgres.ts";
|
|
4
|
+
import { PostgresBinaries } from "#server/PostgresBinaries.ts";
|
|
5
|
+
|
|
6
|
+
export const backupAppPostgres = Effect.fn("LocalPostgres.backupAppPostgres")(function* (instance: AppPostgresInstance) {
|
|
7
|
+
const fs = yield* FileSystem.FileSystem;
|
|
8
|
+
const path = yield* Path.Path;
|
|
9
|
+
const binaries = yield* PostgresBinaries;
|
|
10
|
+
const directory = path.join(instance.dataDirectory, "backups");
|
|
11
|
+
yield* fs.makeDirectory(directory, { mode: 0o700, recursive: true });
|
|
12
|
+
const timestamp = yield* Clock.currentTimeMillis;
|
|
13
|
+
const backup = yield* Effect.scoped(
|
|
14
|
+
Effect.gen(function* () {
|
|
15
|
+
const staging = yield* fs.makeTempDirectoryScoped({ directory, prefix: `backup-${timestamp}-` });
|
|
16
|
+
const file = path.join(staging, "dump.partial");
|
|
17
|
+
yield* runCommand(binaries.pgDump, ["--format=custom", "--file", file, "--dbname", instance.connectionUrl]);
|
|
18
|
+
const completed = `${staging}.dump`;
|
|
19
|
+
yield* fs.rename(file, completed);
|
|
20
|
+
return completed;
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
const files = (yield* fs.readDirectory(directory))
|
|
24
|
+
.filter((name) => /^backup-\d+-.+\.dump$/u.test(name))
|
|
25
|
+
.sort()
|
|
26
|
+
.reverse();
|
|
27
|
+
for (const file of files.slice(instance.backupsToKeep)) {
|
|
28
|
+
yield* fs.remove(path.join(directory, file));
|
|
29
|
+
}
|
|
30
|
+
return backup;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export function migrateAppPostgres<A, E, R>(instance: AppPostgresInstance, migration: Effect.Effect<A, E, R>) {
|
|
34
|
+
return backupAppPostgres(instance).pipe(Effect.andThen(migration));
|
|
35
|
+
}
|