@astralarium/repo-postgres 0.0.0-stage → 0.2.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -2
  3. package/container/init/.containerignore +2 -0
  4. package/container/init/Containerfile +26 -0
  5. package/container/init/entrypoint.d/init-db.sh +20 -0
  6. package/container/snapshot/run-snapshot.sh +23 -0
  7. package/container/util/.containerignore +2 -0
  8. package/container/util/Containerfile +7 -0
  9. package/container/util/entrypoint.d/dump-db.sh +19 -0
  10. package/container/util/entrypoint.d/upgrade-db.sh +103 -0
  11. package/dist/bin/stage-schema.d.ts +2 -0
  12. package/dist/bin/stage-schema.js +20 -0
  13. package/dist/src/provision/connection.d.ts +11 -0
  14. package/dist/src/provision/connection.js +33 -0
  15. package/dist/src/provision/container.d.ts +87 -0
  16. package/dist/src/provision/container.js +244 -0
  17. package/dist/src/provision/data.d.ts +8 -0
  18. package/dist/src/provision/data.js +47 -0
  19. package/dist/src/provision/disposable.d.ts +49 -0
  20. package/dist/src/provision/disposable.js +188 -0
  21. package/dist/src/provision/image.d.ts +63 -0
  22. package/dist/src/provision/image.js +159 -0
  23. package/dist/src/provision/index.d.ts +17 -0
  24. package/dist/src/provision/index.js +205 -0
  25. package/dist/src/provision/options.d.ts +23 -0
  26. package/dist/src/provision/options.js +1 -0
  27. package/dist/src/provision/schema.d.ts +11 -0
  28. package/dist/src/provision/schema.js +134 -0
  29. package/dist/src/provision/test-database.d.ts +29 -0
  30. package/dist/src/provision/test-database.js +130 -0
  31. package/dist/src/queue.d.ts +86 -0
  32. package/dist/src/queue.js +201 -0
  33. package/dist/src/schema/index.d.ts +3 -0
  34. package/dist/src/schema/index.js +3 -0
  35. package/dist/src/schema/module.d.ts +59 -0
  36. package/dist/src/schema/module.js +122 -0
  37. package/dist/src/schema/modules.d.ts +24 -0
  38. package/dist/src/schema/modules.js +71 -0
  39. package/dist/src/sql.d.ts +83 -0
  40. package/dist/src/sql.js +146 -0
  41. package/dist/testkit/index.d.ts +14 -0
  42. package/dist/testkit/index.js +32 -0
  43. package/dist/testkit/locks.d.ts +27 -0
  44. package/dist/testkit/locks.js +57 -0
  45. package/dist/testkit/schema.d.ts +14 -0
  46. package/dist/testkit/schema.js +89 -0
  47. package/package.json +63 -3
  48. package/scripts/build-images.sh +52 -0
  49. package/scripts/dump-db.sh +32 -0
  50. package/sql/app/schema.sql +8 -0
  51. package/sql/bootstrap.sql +27 -0
  52. package/sql/database-grants.sql +3 -0
  53. package/sql/drop-schemas.sql +11 -0
  54. package/sql/extensions/cron/schema.sql +23 -0
  55. package/sql/extensions/partman/before-reset.sql +38 -0
  56. package/sql/extensions/partman/schema.sql +8 -0
  57. package/sql/extensions/trgm/schema.sql +1 -0
  58. package/sql/reset.sql +22 -0
  59. package/sql/roles.sql +10 -0
  60. package/sql/slug/schema.sql +25 -0
  61. package/sql/taskclaim/jobs/schema.sql +19 -0
  62. package/sql/taskclaim/schema.sql +41 -0
  63. package/sql/taskclaim/scrub.sql +3 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Astralarium
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,39 @@
1
- # Temporary Holding Version
1
+ # `@astralarium/repo-postgres`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Shared Postgres tooling: container images, schema modules, clients.
4
+
5
+ Roles:
6
+
7
+ - `app_admin`: schema owner.
8
+ - `app_service`: app service.
9
+
10
+ Roles are cluster-wide: one app per cluster.
11
+
12
+ ```sql
13
+ SELECT slug.slug('Room 52'); -- room-52
14
+ ```
15
+
16
+ ```ts
17
+ // app modules: `taskclaimJobs` (requires `taskclaim`, `cron`) deletes expired claims hourly
18
+ postgresDatabase({ modules: [appModule, slug, taskclaimJobs], name: "app-db" });
19
+ ```
20
+
21
+ ```ts
22
+ // producer: one message per key per window
23
+ await enqueueClaimed(
24
+ sql,
25
+ env.REFRESH,
26
+ `project:${id}`,
27
+ { id },
28
+ { windowSeconds: 300 },
29
+ );
30
+
31
+ // consumer
32
+ await consumeClaimed(sql, batch, {
33
+ parse: parseRefreshTask,
34
+ run: refreshProject,
35
+ maxRetries: 3, // the consumer's max_retries
36
+ retryAfterMs: (e) =>
37
+ e instanceof RetryAfterError ? e.retryAfterMs : undefined,
38
+ });
39
+ ```
@@ -0,0 +1,2 @@
1
+ *
2
+ !entrypoint.d
@@ -0,0 +1,26 @@
1
+ FROM postgres:18-trixie
2
+
3
+ # Extension names, space-separated: Debian's postgresql-<major>-<name> packages.
4
+ ARG DB_EXTENSIONS=""
5
+ RUN if [ -n "$DB_EXTENSIONS" ]; then \
6
+ packages=""; \
7
+ for name in $DB_EXTENSIONS; do packages="$packages postgresql-${PG_MAJOR}-${name}"; done; \
8
+ apt-get update \
9
+ && apt-get install -y --no-install-recommends $packages \
10
+ && rm -rf /var/lib/apt/lists/*; \
11
+ fi
12
+
13
+ # `schemaHash` of the staged schema, this package and DB_EXTENSIONS.
14
+ # The RUN keys the layer cache on it, so each hash gets its own ENV and LABEL.
15
+ ARG DB_SCHEMA
16
+ RUN echo "$DB_SCHEMA" > /etc/db-schema
17
+ ENV DB_SCHEMA=$DB_SCHEMA
18
+ LABEL db.schema=$DB_SCHEMA
19
+
20
+ WORKDIR /docker-entrypoint-initdb.d
21
+ COPY entrypoint.d/ .
22
+ # Packed packages ship scripts without mode bits.
23
+ RUN chmod 0755 *.sh
24
+
25
+ # The staged schema: schema.sql beside sql/.
26
+ COPY --from=schema . schema
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # First boot: creates `db_template`, the app roles (schema/roles.sql) and
3
+ # its grants (schema/database-grants.sql), loads the staged schema
4
+ # (schema/schema.sql includes sql/ in order) with schema/ as cwd, then
5
+ # stamps the data directory with the image's DB_SCHEMA.
6
+
7
+ set -euo pipefail
8
+
9
+ cd "$( dirname "${BASH_SOURCE[0]}" )"
10
+
11
+ export PGDATABASE="db_template"
12
+
13
+ createdb "${PGDATABASE}"
14
+
15
+ cd schema
16
+ psql -X -v ON_ERROR_STOP=1 --file roles.sql
17
+ psql -X -v ON_ERROR_STOP=1 --file database-grants.sql
18
+ psql -X -v ON_ERROR_STOP=1 --file schema.sql
19
+
20
+ echo "${DB_SCHEMA}" > /var/lib/postgresql/db-schema
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ # First boot, after init-db.sh: recreates `db_template` with the staged
3
+ # database-grants.sql, loads the dump at /app/data.sql, runs the staged
4
+ # scrub.sql (each module's `scrub`) unless SNAPSHOT_SCRUB=0, then vacuums.
5
+ # Init runs before the server listens: clients only see the scrubbed data.
6
+
7
+ set -euo pipefail
8
+
9
+ cd /docker-entrypoint-initdb.d/schema
10
+
11
+ export PGDATABASE="db_template"
12
+
13
+ dropdb --force "$PGDATABASE"
14
+ createdb "$PGDATABASE"
15
+ psql -X -v ON_ERROR_STOP=1 --file database-grants.sql
16
+
17
+ psql -X -v ON_ERROR_STOP=1 --file /app/data.sql
18
+
19
+ if [[ ${SNAPSHOT_SCRUB:-1} != 0 ]]; then
20
+ psql -X -v ON_ERROR_STOP=1 --single-transaction --file scrub.sql
21
+ fi
22
+
23
+ vacuumdb --full --analyze --echo
@@ -0,0 +1,2 @@
1
+ *
2
+ !entrypoint.d
@@ -0,0 +1,7 @@
1
+ # Same base as container/init: pg_dump and psql match the server major.
2
+ FROM postgres:18-trixie
3
+
4
+ WORKDIR /entrypoint.d
5
+ COPY entrypoint.d/ .
6
+ # Packed packages ship scripts without mode bits.
7
+ RUN chmod 0755 *.sh
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Data-only dump of the PG* database: the app's rows, excluding extension
3
+ # config tables (the target schema owns its own) and the rows of tables
4
+ # matching DB_DUMP_EXCLUDE (space-separated schema-qualified pg_dump
5
+ # patterns; a partitioned table covers its partitions). The replica role
6
+ # skips triggers and FK checks on load.
7
+
8
+ set -euo pipefail
9
+
10
+ read -ra patterns <<< "${DB_DUMP_EXCLUDE:-}"
11
+ excludes=()
12
+ for pattern in ${patterns[@]+"${patterns[@]}"}; do
13
+ [[ $pattern == *.* ]] || { echo "dump-db: DB_DUMP_EXCLUDE pattern $pattern lacks a schema" >&2; exit 1; }
14
+ excludes+=("--exclude-table-data-and-children=$pattern")
15
+ done
16
+
17
+ echo "SET session_replication_role = 'replica';"
18
+
19
+ pg_dump --data-only --exclude-extension='*' ${excludes[@]+"${excludes[@]}"}
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # Loads the PG* source database's data into a db_template clone on the
3
+ # throwaway instance at CONTAINER_PGHOST and prints that clone's full
4
+ # pg_dump. With /app/migrate.d/*.sql, the data comes from a copy of the
5
+ # source those files migrate, in name order. Tables matching
6
+ # DB_DUMP_EXCLUDE keep the clone's seed rows.
7
+
8
+ set -euo pipefail
9
+
10
+ CONTAINER_PGHOST=${CONTAINER_PGHOST:?temporary database host}
11
+
12
+ TEMPLATE_DATABASE=db_template
13
+ UPGRADE_PGDATABASE=db_upgrade
14
+ MIGRATE_PGDATABASE=db_migrate
15
+
16
+ # Name order, independent of the locale.
17
+ LC_COLLATE=C
18
+ shopt -s nullglob
19
+ migratefiles=(/app/migrate.d/*.sql)
20
+
21
+ # Runs a client against the throwaway instance's database <db>.
22
+ on_local() {
23
+ local db=$1
24
+ shift
25
+ PGSSLMODE=prefer \
26
+ PGHOST="$CONTAINER_PGHOST" \
27
+ PGPORT=5432 \
28
+ PGUSER=postgres \
29
+ PGPASSWORD="" \
30
+ PGDATABASE="$db" \
31
+ "$@"
32
+ }
33
+
34
+ # Empties every table of the app_admin-owned schemas in the upgrade db: the
35
+ # template's seed rows give way to the source's data. Tables matching
36
+ # DB_DUMP_EXCLUDE (schema-qualified pg_dump patterns, `*` and `?`
37
+ # wildcards) keep theirs, as the dump skips their rows.
38
+ clear_app_data() {
39
+ on_local "$UPGRADE_PGDATABASE" psql -X -q -v ON_ERROR_STOP=1 \
40
+ -v exclude="${DB_DUMP_EXCLUDE:-}" >&2 <<'SQL'
41
+ SET upgrade.dump_exclude = :'exclude';
42
+ -- One pg_dump pattern part as an anchored regex.
43
+ CREATE FUNCTION pg_temp.pattern_regex(part text) RETURNS text
44
+ LANGUAGE sql IMMUTABLE
45
+ RETURN '^(' || replace(replace(replace(lower(part), '$', '\$'), '?', '.'), '*', '.*') || ')$';
46
+ DO $$
47
+ DECLARE tables text;
48
+ BEGIN
49
+ SELECT string_agg(c.oid::regclass::text, ', ') INTO tables
50
+ FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
51
+ WHERE n.nspowner = 'app_admin'::regrole
52
+ AND c.relkind IN ('r', 'p')
53
+ AND NOT EXISTS (SELECT 1 FROM pg_inherits WHERE inhrelid = c.oid)
54
+ AND NOT EXISTS (
55
+ SELECT 1
56
+ FROM regexp_split_to_table(current_setting('upgrade.dump_exclude'), '\s+') AS pattern
57
+ WHERE pattern <> ''
58
+ AND n.nspname ~ pg_temp.pattern_regex(split_part(pattern, '.', 1))
59
+ AND c.relname ~ pg_temp.pattern_regex(
60
+ substr(pattern, strpos(pattern, '.') + 1))
61
+ );
62
+ IF tables IS NOT NULL THEN
63
+ EXECUTE 'TRUNCATE ' || tables;
64
+ END IF;
65
+ END $$;
66
+ SQL
67
+ }
68
+
69
+ while ! on_local postgres pg_isready >&2; do
70
+ echo "Waiting for local db instance..." >&2
71
+ sleep 2
72
+ done
73
+ echo "Local db connected!" >&2
74
+
75
+ if (( ${#migratefiles[@]} )); then
76
+ echo "Copy remote db into the migration db..." >&2
77
+ on_local postgres createdb "$MIGRATE_PGDATABASE" >&2
78
+ # pg_cron loads only via shared_preload_libraries; the throwaway runs stock Postgres.
79
+ # The copy feeds a data-only dump and migrate files run as `postgres`,
80
+ # so owners and grants stay out.
81
+ pg_dump --no-owner --no-privileges --exclude-extension=pg_cron | on_local "$MIGRATE_PGDATABASE" psql -v ON_ERROR_STOP=1 >&2
82
+
83
+ for migratefile in "${migratefiles[@]}"; do
84
+ echo "Migrate: $(basename "$migratefile")..." >&2
85
+ on_local "$MIGRATE_PGDATABASE" psql -v ON_ERROR_STOP=1 < "$migratefile" >&2
86
+ done
87
+ fi
88
+
89
+ echo "Create upgrade db" >&2
90
+ on_local postgres createdb -T "$TEMPLATE_DATABASE" "$UPGRADE_PGDATABASE" >&2
91
+ clear_app_data
92
+
93
+ echo "Copy data into the upgrade db..." >&2
94
+ if (( ${#migratefiles[@]} )); then
95
+ on_local "$MIGRATE_PGDATABASE" /entrypoint.d/dump-db.sh
96
+ else
97
+ /entrypoint.d/dump-db.sh
98
+ fi | on_local "$UPGRADE_PGDATABASE" psql -v ON_ERROR_STOP=1 >&2
99
+
100
+ echo "Dump upgrade db data" >&2
101
+ echo "SET session_replication_role = 'replica';"
102
+ on_local "$UPGRADE_PGDATABASE" pg_dump
103
+ echo "DONE" >&2
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ /** `stage-schema --app <module> <dir>`: stages the app's schema for psql
3
+ * under `<dir>` (`PostgresProvider.stageSchema`). */
4
+ import { appArgs } from "@astralarium/repo-deploy-info/server";
5
+ import { loadDevConfig } from "@astralarium/repo-dev";
6
+ import { ExpectedError, log } from "@astralarium/repo-telemetry";
7
+ const USAGE = "Usage: stage-schema --app <module> <dir>";
8
+ try {
9
+ const { app, argv } = appArgs(process.argv.slice(2), USAGE, {
10
+ positionals: 1,
11
+ });
12
+ const { database } = await loadDevConfig(app);
13
+ if (!database || !("stageSchema" in database))
14
+ throw new ExpectedError(`${app}: dev config declares no Postgres database`);
15
+ database.stageSchema(argv[0]);
16
+ }
17
+ catch (exception) {
18
+ log.error("stage_schema.failed", "stage-schema failed", { exception });
19
+ process.exitCode = 1;
20
+ }
@@ -0,0 +1,11 @@
1
+ /** Preferred published port; a taken one moves to the next free port up. */
2
+ export declare const DEFAULT_PORT = 5432;
3
+ /** The app's login role, as `sql/roles.sql` creates it: non-superuser;
4
+ * schema access through grants; `app_admin` inherits it. */
5
+ export declare const APP_SERVICE_ROLE = "app_service";
6
+ /** Local connection as `APP_SERVICE_ROLE`. */
7
+ export declare function appConnectionString(port: number, database: string): string;
8
+ /** Local connection as `postgres`. */
9
+ export declare function adminConnectionString(port: number, database: string): string;
10
+ /** Authenticates through the host port and executes a read-only query. */
11
+ export declare function probePostgres(connectionString: string): Promise<void>;
@@ -0,0 +1,33 @@
1
+ /** Connections to local instances: trust auth on the published loopback port. */
2
+ import postgres from "postgres";
3
+ /** Preferred published port; a taken one moves to the next free port up. */
4
+ export const DEFAULT_PORT = 5432;
5
+ /**
6
+ * Loopback connection string for a local instance. The containers trust
7
+ * every connection on their published loopback port; the password is a
8
+ * placeholder the Cloudflare tooling insists on.
9
+ */
10
+ function localConnectionString(user, port, database) {
11
+ return `postgres://${user}:trust@127.0.0.1:${port}/${database}`;
12
+ }
13
+ /** The app's login role, as `sql/roles.sql` creates it: non-superuser;
14
+ * schema access through grants; `app_admin` inherits it. */
15
+ export const APP_SERVICE_ROLE = "app_service";
16
+ /** Local connection as `APP_SERVICE_ROLE`. */
17
+ export function appConnectionString(port, database) {
18
+ return localConnectionString(APP_SERVICE_ROLE, port, database);
19
+ }
20
+ /** Local connection as `postgres`. */
21
+ export function adminConnectionString(port, database) {
22
+ return localConnectionString("postgres", port, database);
23
+ }
24
+ /** Authenticates through the host port and executes a read-only query. */
25
+ export async function probePostgres(connectionString) {
26
+ const sql = postgres(connectionString, { max: 1, connect_timeout: 5 });
27
+ try {
28
+ await sql `SELECT 1`;
29
+ }
30
+ finally {
31
+ await sql.end({ timeout: 1 });
32
+ }
33
+ }
@@ -0,0 +1,87 @@
1
+ import { type ResourceScope } from "@astralarium/repo-dev";
2
+ /** Image label carrying the `schemaHash()` it was built from. */
3
+ export declare const SCHEMA_LABEL = "db.schema";
4
+ /** Container and volume label naming the checkout that owns it. */
5
+ export declare const OWNER_LABEL = "dev.owner";
6
+ /** `CONTAINER_RUNTIME`, else the first of podman, docker that answers `info`. */
7
+ export declare function detectRuntime(): string;
8
+ /** The container's data was initialised from a schema other than its image's. */
9
+ export declare class StaleSchemaError extends Error {
10
+ constructor(runtime: string, container: string, stamp: string | null);
11
+ }
12
+ /** Existing container: run state, image ID and labels. */
13
+ interface ContainerState {
14
+ running: boolean;
15
+ /** SHA-256 image ID, including the algorithm prefix. */
16
+ image: string;
17
+ /** `SCHEMA_LABEL` value; undefined on an unlabeled image. */
18
+ schema?: string;
19
+ /** `OWNER_LABEL` value; undefined on an unlabeled container. */
20
+ owner?: string;
21
+ /** `ARGS_LABEL` value; undefined on an unlabeled container. */
22
+ args?: string;
23
+ /** Requested loopback publish specification. */
24
+ bindings: {
25
+ HostIp: string;
26
+ HostPort: string;
27
+ }[];
28
+ /** Mount at the Postgres data directory. */
29
+ dataMount?: {
30
+ Type: string;
31
+ Name?: string;
32
+ Destination: string;
33
+ };
34
+ }
35
+ /** One `container inspect`; null for an absent `name`. */
36
+ export declare function inspectContainer(runtime: string, name: string): ContainerState | null;
37
+ /** Persistent data volume of dev instance `name`. */
38
+ export declare function volumeName(name: string): string;
39
+ /** Something holding dev instance `name`: its container or its data volume. */
40
+ interface Holder {
41
+ kind: "container" | "volume";
42
+ /** Container or volume name. */
43
+ name: string;
44
+ /** `OWNER_LABEL` value; undefined when unlabeled. */
45
+ owner?: string;
46
+ }
47
+ /** The existing container and data volume of dev instance `name`. */
48
+ export declare function holders(runtime: string, name: string): Holder[];
49
+ /** Container `name` and its data volume are each absent or labeled as
50
+ * owned by `owner`, so a volume surviving its container stays with the
51
+ * checkout that made it. */
52
+ export declare function claimable(runtime: string, name: string, owner: string): boolean;
53
+ /** `rm -f`; a missing container is fine, any other failure throws. */
54
+ export declare function removeContainer(runtime: string, name: string): void;
55
+ /** `volume rm -f`; a missing volume is fine, any other failure throws. */
56
+ export declare function removeVolume(runtime: string, name: string): void;
57
+ /** Local Postgres instance. */
58
+ interface PostgresContainerOptions {
59
+ /** Container name. */
60
+ name: string;
61
+ /** Immutable image ID from `ensureImage`. */
62
+ image: string;
63
+ /** Schema hash of the image. */
64
+ hash: string;
65
+ /** Published loopback port; omitted lets the runtime assign one. */
66
+ port?: number;
67
+ /** Persistent data volume, created labeled for `owner`; omitted uses the container's disposable storage. */
68
+ volume?: string;
69
+ /** Owning checkout, stored as `OWNER_LABEL` on the container and the volume. */
70
+ owner?: string;
71
+ /** Postgres server arguments appended to the image command. */
72
+ serverArgs?: readonly string[];
73
+ /** Further container labels, applied at creation. */
74
+ labels?: Readonly<Record<string, string>>;
75
+ }
76
+ /**
77
+ * Reuses a container of exactly `image`, `owner`, server arguments and
78
+ * settings, else recreates it (named volumes survive). Resolves once
79
+ * Postgres accepts connections and the data carries `hash`, with the
80
+ * published host port. `resources` stops a persistent container this call
81
+ * started; one already running is left alone. The caller removes an
82
+ * ephemeral one (`volume` unset).
83
+ */
84
+ export declare function startPostgresContainer(runtime: string, resources: ResourceScope, { name, image, hash, port, volume, owner, serverArgs, labels, }: PostgresContainerOptions): Promise<{
85
+ port: number;
86
+ }>;
87
+ export {};