@cedarjs/pg 0.1.0-alpha.0 → 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/dist/cli.cjs +5 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +5 -5
- package/dist/cli.mjs.map +1 -1
- package/dist/{constants-YnHDwShD.mjs → constants-BY97wXjA.mjs} +3 -3
- package/dist/constants-BY97wXjA.mjs.map +1 -0
- package/dist/{constants-B7sCf4fg.cjs → constants-CNZn5Xro.cjs} +3 -3
- package/dist/constants-CNZn5Xro.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/jest.cjs +3 -3
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +2 -2
- package/dist/jest.d.mts +2 -2
- package/dist/jest.mjs +3 -3
- package/dist/jest.mjs.map +1 -1
- package/dist/{lifecycle-D10usEjM.cjs → lifecycle-BGYtVXtx.cjs} +3 -3
- package/dist/lifecycle-BGYtVXtx.cjs.map +1 -0
- package/dist/{lifecycle-PbTXNC2Z.mjs → lifecycle-CT_8AWPv.mjs} +3 -3
- package/dist/lifecycle-CT_8AWPv.mjs.map +1 -0
- package/dist/nx.cjs +3 -3
- package/dist/nx.cjs.map +1 -1
- package/dist/nx.d.cts +1 -1
- package/dist/nx.d.mts +1 -1
- package/dist/nx.mjs +3 -3
- package/dist/nx.mjs.map +1 -1
- package/dist/{tasks-CW2D0n46.mjs → tasks-13P6tMth.mjs} +2 -2
- package/dist/{tasks-CW2D0n46.mjs.map → tasks-13P6tMth.mjs.map} +1 -1
- package/dist/{tasks-DZ7hMmGJ.cjs → tasks-B8ryV9xo.cjs} +2 -2
- package/dist/{tasks-DZ7hMmGJ.cjs.map → tasks-B8ryV9xo.cjs.map} +1 -1
- package/dist/vite-plus.cjs +2 -2
- package/dist/vite-plus.mjs +2 -2
- package/dist/vitest.cjs +2 -2
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +1 -1
- package/dist/vitest.d.mts +1 -1
- package/dist/vitest.mjs +2 -2
- package/dist/vitest.mjs.map +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.js +5 -5
- package/dist/constants-B7sCf4fg.cjs.map +0 -1
- package/dist/constants-YnHDwShD.mjs.map +0 -1
- package/dist/lifecycle-D10usEjM.cjs.map +0 -1
- package/dist/lifecycle-PbTXNC2Z.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# cedar-pg
|
|
2
2
|
|
|
3
|
-
Worktree-isolated local Postgres for **Vite+**, **Nx**, and **CedarJS
|
|
3
|
+
Worktree-isolated local Postgres for **Vite+**, **Nx**, and **CedarJS**, powered by [autopg](https://github.com/automagik-dev/autopg).
|
|
4
4
|
|
|
5
5
|
Published on npm as **`@cedarjs/pg`**.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Published on npm as **`@cedarjs/pg`**.
|
|
|
8
8
|
|
|
9
9
|
## What you get (via autopg)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
autopg runs embedded PostgreSQL 18 (not WASM) with real concurrent connections. No credentials, zero config, and databases are provisioned on first use. Any client works (`psql`, `node-postgres`, Prisma, Drizzle, TypeORM).
|
|
12
12
|
|
|
13
13
|
### Development & testing
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ Under the hood you get an **embedded PostgreSQL server with true concurrent conn
|
|
|
21
21
|
|
|
22
22
|
## What cedar-pg adds
|
|
23
23
|
|
|
24
|
-
**cedar-pg**
|
|
24
|
+
**cedar-pg** gives each **git worktree** its own database and role on that autopg host (readable names, leases, teardown) so parallel checkouts do not share one DB:
|
|
25
25
|
|
|
26
26
|
- **1 database per git worktree** (visible in `\l` as `cpg_…`)
|
|
27
27
|
- **dev** DBs persist across restarts; **test** DBs drop on dispose
|
|
@@ -30,7 +30,7 @@ Under the hood you get an **embedded PostgreSQL server with true concurrent conn
|
|
|
30
30
|
|
|
31
31
|
| Layer | Responsibility |
|
|
32
32
|
| ------------ | ------------------------------------------------------------------- |
|
|
33
|
-
| **autopg** | Embedded Postgres host
|
|
33
|
+
| **autopg** | Embedded Postgres host (concurrent, zero-config, auto-provision) |
|
|
34
34
|
| **cedar-pg** | Per-worktree `CREATE DATABASE` / role, `DATABASE_URL`, dispose + GC |
|
|
35
35
|
|
|
36
36
|
## Install
|
|
@@ -135,19 +135,19 @@ await drop();
|
|
|
135
135
|
|
|
136
136
|
## Env
|
|
137
137
|
|
|
138
|
-
| Var | Meaning
|
|
139
|
-
| ----------------------------- |
|
|
140
|
-
| `AUTOPG_BIN` | Path to autopg
|
|
141
|
-
| `CEDAR_PG=0` | Disable auto-ensure in adapters
|
|
142
|
-
| `TEST_DATABASE_URL` | Escape hatch
|
|
143
|
-
| `CEDAR_PG_FORCE=1` | Ignore external-URL escape hatch
|
|
144
|
-
| `CEDAR_PG_REGISTRY_DIR` | Override global lease registry (for `gc`)
|
|
145
|
-
| `CEDAR_PG_SKIP_POSTINSTALL=1` | Skip autopg install hook
|
|
146
|
-
| `CEDAR_PG_INSTALL_AUTOPG=1` | Force autopg install in CI
|
|
138
|
+
| Var | Meaning |
|
|
139
|
+
| ----------------------------- | ------------------------------------------------------------------ |
|
|
140
|
+
| `AUTOPG_BIN` | Path to autopg |
|
|
141
|
+
| `CEDAR_PG=0` | Disable auto-ensure in adapters |
|
|
142
|
+
| `TEST_DATABASE_URL` | Escape hatch: skip ensure for external DBs (not `cpg_*` / `file:`) |
|
|
143
|
+
| `CEDAR_PG_FORCE=1` | Ignore external-URL escape hatch |
|
|
144
|
+
| `CEDAR_PG_REGISTRY_DIR` | Override global lease registry (for `gc`) |
|
|
145
|
+
| `CEDAR_PG_SKIP_POSTINSTALL=1` | Skip autopg install hook |
|
|
146
|
+
| `CEDAR_PG_INSTALL_AUTOPG=1` | Force autopg install in CI |
|
|
147
147
|
|
|
148
148
|
## Alpha caveats
|
|
149
149
|
|
|
150
150
|
- Public API may change before `0.1.0`.
|
|
151
151
|
- End-to-end Postgres flows assume a working local `autopg` host; CI unit tests do not start Postgres.
|
|
152
|
-
- State lives in product-owned `.cedarpg` (worktree + `~/.cedarpg/registry`)
|
|
152
|
+
- State lives in product-owned `.cedarpg` (worktree + `~/.cedarpg/registry`), not under autopg's `~/.autopg/` or a generic `.pg`.
|
|
153
153
|
- Password salt (`cedar-pg\\0`, scheme v1) is an opaque crypto constant; bump the scheme id to change it.
|
package/dist/cli.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_lifecycle = require("./lifecycle-
|
|
3
|
-
const require_constants = require("./constants-
|
|
2
|
+
const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
|
|
3
|
+
const require_constants = require("./constants-CNZn5Xro.cjs");
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
function printHelp() {
|
|
6
|
-
process.stdout.write(`${require_constants.CLI_NAME}
|
|
6
|
+
process.stdout.write(`${require_constants.CLI_NAME}: worktree-isolated local Postgres (via autopg)
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
${require_constants.CLI_NAME} ensure --mode=dev|test [--root <path>] [--json] [--print-env]
|
|
@@ -96,7 +96,7 @@ async function main() {
|
|
|
96
96
|
process.stdout.write(`${require_constants.CLI_NAME}: nothing to dispose for ${mode} (no lease)\n`);
|
|
97
97
|
return 0;
|
|
98
98
|
}
|
|
99
|
-
process.stderr.write(`${require_constants.CLI_NAME}: could not dispose ${mode}
|
|
99
|
+
process.stderr.write(`${require_constants.CLI_NAME}: could not dispose ${mode}: autopg host unavailable (lease kept for retry)\n`);
|
|
100
100
|
return 1;
|
|
101
101
|
}
|
|
102
102
|
if (args.cmd === "gc") {
|
|
@@ -112,7 +112,7 @@ async function main() {
|
|
|
112
112
|
const mode = args.mode ?? "dev";
|
|
113
113
|
const lease = require_lifecycle.readLease(require_lifecycle.resolveWorktreeIdentity(args.root).root, mode);
|
|
114
114
|
if (!lease) {
|
|
115
|
-
process.stderr.write(`${require_constants.CLI_NAME}: no ${mode} lease
|
|
115
|
+
process.stderr.write(`${require_constants.CLI_NAME}: no ${mode} lease; run \`${require_constants.CLI_NAME} ensure --mode=${mode}\` first\n`);
|
|
116
116
|
return 2;
|
|
117
117
|
}
|
|
118
118
|
process.stdout.write(`${require_lifecycle.urlFromLease(lease)}\n`);
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","names":["CLI_NAME","ensure","dispose","gc","readLease","resolveWorktreeIdentity","urlFromLease"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI_NAME } from \"./core/constants.ts\";\nimport { ensure, dispose, gc, urlFromLease } from \"./core/lifecycle.ts\";\nimport { resolveWorktreeIdentity } from \"./core/worktree.ts\";\nimport { readLease } from \"./core/lease.ts\";\nimport type { DbMode } from \"./core/naming.ts\";\n\nfunction printHelp(): void {\n process.stdout.write(`${CLI_NAME}
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["CLI_NAME","ensure","dispose","gc","readLease","resolveWorktreeIdentity","urlFromLease"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI_NAME } from \"./core/constants.ts\";\nimport { ensure, dispose, gc, urlFromLease } from \"./core/lifecycle.ts\";\nimport { resolveWorktreeIdentity } from \"./core/worktree.ts\";\nimport { readLease } from \"./core/lease.ts\";\nimport type { DbMode } from \"./core/naming.ts\";\n\nfunction printHelp(): void {\n process.stdout.write(`${CLI_NAME}: worktree-isolated local Postgres (via autopg)\n\nUsage:\n ${CLI_NAME} ensure --mode=dev|test [--root <path>] [--json] [--print-env]\n ${CLI_NAME} dispose [--mode=dev|test] [--root <path>]\n ${CLI_NAME} gc [--json]\n ${CLI_NAME} print-url [--mode=dev|test] [--root <path>]\n ${CLI_NAME} --help\n\nModes:\n dev Keep DB across restarts (default for ensure if omitted: dev)\n test Drop DB on dispose / test teardown\n\nEnv:\n AUTOPG_BIN Path to autopg binary\n CEDAR_PG=0 Disable adapters that auto-ensure\n`);\n}\n\nfunction parseMode(value: string | undefined): DbMode {\n if (value === \"dev\" || value === \"test\") return value;\n throw new Error(\"--mode must be dev or test\");\n}\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n mode?: DbMode;\n root?: string;\n json?: boolean;\n printEnv?: boolean;\n help?: boolean;\n } = {};\n const rest = [...argv];\n out.cmd = rest.shift();\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]!;\n if (a === \"--help\" || a === \"-h\") out.help = true;\n else if (a === \"--json\") out.json = true;\n else if (a === \"--print-env\") out.printEnv = true;\n else if (a.startsWith(\"--mode=\")) out.mode = parseMode(a.slice(7));\n else if (a === \"--mode\") out.mode = parseMode(rest[++i]);\n else if (a.startsWith(\"--root=\")) out.root = a.slice(7);\n else if (a === \"--root\") out.root = rest[++i];\n else throw new Error(`unknown argument: ${a}`);\n }\n return out;\n}\n\nasync function main(): Promise<number> {\n const argv = process.argv.slice(2);\n if (argv.length === 0 || argv[0] === \"help\" || argv[0] === \"--help\" || argv[0] === \"-h\") {\n printHelp();\n return 0;\n }\n const args = parseArgs(argv);\n if (args.help) {\n printHelp();\n return 0;\n }\n\n try {\n if (args.cmd === \"ensure\") {\n const mode = args.mode ?? \"dev\";\n const result = await ensure({\n root: args.root,\n mode,\n setEnv: true,\n });\n if (args.printEnv) {\n process.stdout.write(`DATABASE_URL=${result.databaseUrl}\\n`);\n if (mode === \"test\") {\n process.stdout.write(`TEST_DATABASE_URL=${result.databaseUrl}\\n`);\n }\n }\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n databaseUrl: result.databaseUrl,\n databaseName: result.databaseName,\n roleName: result.roleName,\n repoSlug: result.repoSlug,\n worktreeSlug: result.worktreeSlug,\n pathHash: result.pathHash,\n mode: result.mode,\n port: result.port,\n root: result.root,\n },\n null,\n 2,\n )}\\n`,\n );\n } else if (!args.printEnv) {\n process.stdout.write(\n `${CLI_NAME}: ${result.databaseName} (${result.repoSlug}/${result.worktreeSlug} ${result.mode})\\n`,\n );\n process.stdout.write(`${result.databaseUrl}\\n`);\n }\n return 0;\n }\n\n if (args.cmd === \"dispose\") {\n const mode = args.mode ?? \"test\";\n const result = await dispose({ root: args.root, mode });\n if (result.dropped) {\n process.stdout.write(`${CLI_NAME}: disposed ${mode} (${result.databaseName})\\n`);\n return 0;\n }\n if (result.reason === \"no-lease\") {\n process.stdout.write(`${CLI_NAME}: nothing to dispose for ${mode} (no lease)\\n`);\n return 0;\n }\n process.stderr.write(\n `${CLI_NAME}: could not dispose ${mode}: autopg host unavailable (lease kept for retry)\\n`,\n );\n return 1;\n }\n\n if (args.cmd === \"gc\") {\n const result = await gc();\n if (args.json) {\n process.stdout.write(`${JSON.stringify(result)}\\n`);\n } else {\n process.stdout.write(`${CLI_NAME} gc: dropped ${result.dropped.length} database(s)\\n`);\n for (const name of result.dropped) process.stdout.write(` ${name}\\n`);\n }\n return 0;\n }\n\n if (args.cmd === \"print-url\") {\n const mode = args.mode ?? \"dev\";\n const identity = resolveWorktreeIdentity(args.root);\n const lease = readLease(identity.root, mode);\n if (!lease) {\n process.stderr.write(\n `${CLI_NAME}: no ${mode} lease; run \\`${CLI_NAME} ensure --mode=${mode}\\` first\\n`,\n );\n return 2;\n }\n process.stdout.write(`${urlFromLease(lease)}\\n`);\n return 0;\n }\n\n process.stderr.write(`${CLI_NAME}: unknown command ${args.cmd}\\n`);\n printHelp();\n return 64;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`${CLI_NAME}: ${message}\\n`);\n return 1;\n }\n}\n\nmain()\n .then((code) => {\n process.exit(code);\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`${CLI_NAME}: ${message}\\n`);\n process.exit(1);\n });\n"],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { S as readLease, a as urlFromLease, g as resolveWorktreeIdentity, i as gc, n as ensure, t as dispose } from "./lifecycle-
|
|
3
|
-
import { t as CLI_NAME } from "./constants-
|
|
2
|
+
import { S as readLease, a as urlFromLease, g as resolveWorktreeIdentity, i as gc, n as ensure, t as dispose } from "./lifecycle-CT_8AWPv.mjs";
|
|
3
|
+
import { t as CLI_NAME } from "./constants-BY97wXjA.mjs";
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
function printHelp() {
|
|
6
|
-
process.stdout.write(`${CLI_NAME}
|
|
6
|
+
process.stdout.write(`${CLI_NAME}: worktree-isolated local Postgres (via autopg)
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
${CLI_NAME} ensure --mode=dev|test [--root <path>] [--json] [--print-env]
|
|
@@ -96,7 +96,7 @@ async function main() {
|
|
|
96
96
|
process.stdout.write(`${CLI_NAME}: nothing to dispose for ${mode} (no lease)\n`);
|
|
97
97
|
return 0;
|
|
98
98
|
}
|
|
99
|
-
process.stderr.write(`${CLI_NAME}: could not dispose ${mode}
|
|
99
|
+
process.stderr.write(`${CLI_NAME}: could not dispose ${mode}: autopg host unavailable (lease kept for retry)\n`);
|
|
100
100
|
return 1;
|
|
101
101
|
}
|
|
102
102
|
if (args.cmd === "gc") {
|
|
@@ -112,7 +112,7 @@ async function main() {
|
|
|
112
112
|
const mode = args.mode ?? "dev";
|
|
113
113
|
const lease = readLease(resolveWorktreeIdentity(args.root).root, mode);
|
|
114
114
|
if (!lease) {
|
|
115
|
-
process.stderr.write(`${CLI_NAME}: no ${mode} lease
|
|
115
|
+
process.stderr.write(`${CLI_NAME}: no ${mode} lease; run \`${CLI_NAME} ensure --mode=${mode}\` first\n`);
|
|
116
116
|
return 2;
|
|
117
117
|
}
|
|
118
118
|
process.stdout.write(`${urlFromLease(lease)}\n`);
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI_NAME } from \"./core/constants.ts\";\nimport { ensure, dispose, gc, urlFromLease } from \"./core/lifecycle.ts\";\nimport { resolveWorktreeIdentity } from \"./core/worktree.ts\";\nimport { readLease } from \"./core/lease.ts\";\nimport type { DbMode } from \"./core/naming.ts\";\n\nfunction printHelp(): void {\n process.stdout.write(`${CLI_NAME}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI_NAME } from \"./core/constants.ts\";\nimport { ensure, dispose, gc, urlFromLease } from \"./core/lifecycle.ts\";\nimport { resolveWorktreeIdentity } from \"./core/worktree.ts\";\nimport { readLease } from \"./core/lease.ts\";\nimport type { DbMode } from \"./core/naming.ts\";\n\nfunction printHelp(): void {\n process.stdout.write(`${CLI_NAME}: worktree-isolated local Postgres (via autopg)\n\nUsage:\n ${CLI_NAME} ensure --mode=dev|test [--root <path>] [--json] [--print-env]\n ${CLI_NAME} dispose [--mode=dev|test] [--root <path>]\n ${CLI_NAME} gc [--json]\n ${CLI_NAME} print-url [--mode=dev|test] [--root <path>]\n ${CLI_NAME} --help\n\nModes:\n dev Keep DB across restarts (default for ensure if omitted: dev)\n test Drop DB on dispose / test teardown\n\nEnv:\n AUTOPG_BIN Path to autopg binary\n CEDAR_PG=0 Disable adapters that auto-ensure\n`);\n}\n\nfunction parseMode(value: string | undefined): DbMode {\n if (value === \"dev\" || value === \"test\") return value;\n throw new Error(\"--mode must be dev or test\");\n}\n\nfunction parseArgs(argv: string[]) {\n const out: {\n cmd?: string;\n mode?: DbMode;\n root?: string;\n json?: boolean;\n printEnv?: boolean;\n help?: boolean;\n } = {};\n const rest = [...argv];\n out.cmd = rest.shift();\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]!;\n if (a === \"--help\" || a === \"-h\") out.help = true;\n else if (a === \"--json\") out.json = true;\n else if (a === \"--print-env\") out.printEnv = true;\n else if (a.startsWith(\"--mode=\")) out.mode = parseMode(a.slice(7));\n else if (a === \"--mode\") out.mode = parseMode(rest[++i]);\n else if (a.startsWith(\"--root=\")) out.root = a.slice(7);\n else if (a === \"--root\") out.root = rest[++i];\n else throw new Error(`unknown argument: ${a}`);\n }\n return out;\n}\n\nasync function main(): Promise<number> {\n const argv = process.argv.slice(2);\n if (argv.length === 0 || argv[0] === \"help\" || argv[0] === \"--help\" || argv[0] === \"-h\") {\n printHelp();\n return 0;\n }\n const args = parseArgs(argv);\n if (args.help) {\n printHelp();\n return 0;\n }\n\n try {\n if (args.cmd === \"ensure\") {\n const mode = args.mode ?? \"dev\";\n const result = await ensure({\n root: args.root,\n mode,\n setEnv: true,\n });\n if (args.printEnv) {\n process.stdout.write(`DATABASE_URL=${result.databaseUrl}\\n`);\n if (mode === \"test\") {\n process.stdout.write(`TEST_DATABASE_URL=${result.databaseUrl}\\n`);\n }\n }\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n databaseUrl: result.databaseUrl,\n databaseName: result.databaseName,\n roleName: result.roleName,\n repoSlug: result.repoSlug,\n worktreeSlug: result.worktreeSlug,\n pathHash: result.pathHash,\n mode: result.mode,\n port: result.port,\n root: result.root,\n },\n null,\n 2,\n )}\\n`,\n );\n } else if (!args.printEnv) {\n process.stdout.write(\n `${CLI_NAME}: ${result.databaseName} (${result.repoSlug}/${result.worktreeSlug} ${result.mode})\\n`,\n );\n process.stdout.write(`${result.databaseUrl}\\n`);\n }\n return 0;\n }\n\n if (args.cmd === \"dispose\") {\n const mode = args.mode ?? \"test\";\n const result = await dispose({ root: args.root, mode });\n if (result.dropped) {\n process.stdout.write(`${CLI_NAME}: disposed ${mode} (${result.databaseName})\\n`);\n return 0;\n }\n if (result.reason === \"no-lease\") {\n process.stdout.write(`${CLI_NAME}: nothing to dispose for ${mode} (no lease)\\n`);\n return 0;\n }\n process.stderr.write(\n `${CLI_NAME}: could not dispose ${mode}: autopg host unavailable (lease kept for retry)\\n`,\n );\n return 1;\n }\n\n if (args.cmd === \"gc\") {\n const result = await gc();\n if (args.json) {\n process.stdout.write(`${JSON.stringify(result)}\\n`);\n } else {\n process.stdout.write(`${CLI_NAME} gc: dropped ${result.dropped.length} database(s)\\n`);\n for (const name of result.dropped) process.stdout.write(` ${name}\\n`);\n }\n return 0;\n }\n\n if (args.cmd === \"print-url\") {\n const mode = args.mode ?? \"dev\";\n const identity = resolveWorktreeIdentity(args.root);\n const lease = readLease(identity.root, mode);\n if (!lease) {\n process.stderr.write(\n `${CLI_NAME}: no ${mode} lease; run \\`${CLI_NAME} ensure --mode=${mode}\\` first\\n`,\n );\n return 2;\n }\n process.stdout.write(`${urlFromLease(lease)}\\n`);\n return 0;\n }\n\n process.stderr.write(`${CLI_NAME}: unknown command ${args.cmd}\\n`);\n printHelp();\n return 64;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`${CLI_NAME}: ${message}\\n`);\n return 1;\n }\n}\n\nmain()\n .then((code) => {\n process.exit(code);\n })\n .catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`${CLI_NAME}: ${message}\\n`);\n process.exit(1);\n });\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Product identity vs frozen crypto.
|
|
4
4
|
*
|
|
5
|
-
* State dirs follow the CLI name (`.cedarpg`)
|
|
5
|
+
* State dirs follow the CLI name (`.cedarpg`): product-owned, not nested under
|
|
6
6
|
* autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).
|
|
7
7
|
* Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.
|
|
8
8
|
*/
|
|
@@ -15,10 +15,10 @@ const CLI_NAME = "cedarpg";
|
|
|
15
15
|
const STATE_DIRNAME = ".cedarpg";
|
|
16
16
|
/**
|
|
17
17
|
* Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\0` + databaseName).
|
|
18
|
-
* Opaque
|
|
18
|
+
* Opaque; independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.
|
|
19
19
|
*/
|
|
20
20
|
const PASSWORD_SALT_PREFIX = "cedar-pg";
|
|
21
21
|
//#endregion
|
|
22
22
|
export { PASSWORD_SALT_PREFIX as n, STATE_DIRNAME as r, CLI_NAME as t };
|
|
23
23
|
|
|
24
|
-
//# sourceMappingURL=constants-
|
|
24
|
+
//# sourceMappingURL=constants-BY97wXjA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants-BY97wXjA.mjs","names":[],"sources":["../src/core/constants.ts"],"sourcesContent":["/**\n * Product identity vs frozen crypto.\n *\n * State dirs follow the CLI name (`.cedarpg`): product-owned, not nested under\n * autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).\n * Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.\n */\n\n/** User-facing CLI binary (`package.json` bin) and log prefix */\nexport const CLI_NAME = \"cedarpg\";\n\n/**\n * Worktree-local state dir and home-registry parent (`~/.cedarpg/registry`).\n * Frozen for lease/gc discovery after first alpha consumers appear.\n */\nexport const STATE_DIRNAME = \".cedarpg\";\n\n/**\n * Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\\0` + databaseName).\n * Opaque; independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.\n */\nexport const PASSWORD_SALT_PREFIX = \"cedar-pg\";\n"],"mappings":";;;;;;;;;AASA,MAAa,WAAW;;;;;AAMxB,MAAa,gBAAgB;;;;;AAM7B,MAAa,uBAAuB"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Product identity vs frozen crypto.
|
|
4
4
|
*
|
|
5
|
-
* State dirs follow the CLI name (`.cedarpg`)
|
|
5
|
+
* State dirs follow the CLI name (`.cedarpg`): product-owned, not nested under
|
|
6
6
|
* autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).
|
|
7
7
|
* Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.
|
|
8
8
|
*/
|
|
@@ -15,7 +15,7 @@ const CLI_NAME = "cedarpg";
|
|
|
15
15
|
const STATE_DIRNAME = ".cedarpg";
|
|
16
16
|
/**
|
|
17
17
|
* Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\0` + databaseName).
|
|
18
|
-
* Opaque
|
|
18
|
+
* Opaque; independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.
|
|
19
19
|
*/
|
|
20
20
|
const PASSWORD_SALT_PREFIX = "cedar-pg";
|
|
21
21
|
//#endregion
|
|
@@ -38,4 +38,4 @@ Object.defineProperty(exports, "STATE_DIRNAME", {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//# sourceMappingURL=constants-
|
|
41
|
+
//# sourceMappingURL=constants-CNZn5Xro.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants-CNZn5Xro.cjs","names":[],"sources":["../src/core/constants.ts"],"sourcesContent":["/**\n * Product identity vs frozen crypto.\n *\n * State dirs follow the CLI name (`.cedarpg`): product-owned, not nested under\n * autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).\n * Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.\n */\n\n/** User-facing CLI binary (`package.json` bin) and log prefix */\nexport const CLI_NAME = \"cedarpg\";\n\n/**\n * Worktree-local state dir and home-registry parent (`~/.cedarpg/registry`).\n * Frozen for lease/gc discovery after first alpha consumers appear.\n */\nexport const STATE_DIRNAME = \".cedarpg\";\n\n/**\n * Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\\0` + databaseName).\n * Opaque; independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.\n */\nexport const PASSWORD_SALT_PREFIX = \"cedar-pg\";\n"],"mappings":";;;;;;;;;AASA,MAAa,WAAW;;;;;AAMxB,MAAa,gBAAgB;;;;;AAM7B,MAAa,uBAAuB"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_lifecycle = require("./lifecycle-
|
|
2
|
+
const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
|
|
3
3
|
exports.INSTALL_HINT = require_lifecycle.INSTALL_HINT;
|
|
4
4
|
exports.ROLE_PASSWORD_SCHEME = require_lifecycle.ROLE_PASSWORD_SCHEME;
|
|
5
5
|
exports.buildDatabaseName = require_lifecycle.buildDatabaseName;
|
package/dist/index.d.cts
CHANGED
|
@@ -76,7 +76,7 @@ type ResolveEnsureSkipInput = {
|
|
|
76
76
|
};
|
|
77
77
|
/**
|
|
78
78
|
* True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).
|
|
79
|
-
* These must never be treated as an external escape hatch
|
|
79
|
+
* These must never be treated as an external escape hatch; always re-ensure so
|
|
80
80
|
* disposed/stale shell env cannot skip provisioning.
|
|
81
81
|
*/
|
|
82
82
|
declare function isCedarPgManagedUrl(url: string | undefined): boolean;
|
package/dist/index.d.mts
CHANGED
|
@@ -76,7 +76,7 @@ type ResolveEnsureSkipInput = {
|
|
|
76
76
|
};
|
|
77
77
|
/**
|
|
78
78
|
* True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).
|
|
79
|
-
* These must never be treated as an external escape hatch
|
|
79
|
+
* These must never be treated as an external escape hatch; always re-ensure so
|
|
80
80
|
* disposed/stale shell env cannot skip provisioning.
|
|
81
81
|
*/
|
|
82
82
|
declare function isCedarPgManagedUrl(url: string | undefined): boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as buildDatabaseName, S as readLease, _ as isCedarPgManagedUrl, a as urlFromLease, b as isOrphanLease, c as buildDatabaseUrl, d as parseHostStatus, f as requireAutopgBin, g as resolveWorktreeIdentity, h as resolveRoot, i as gc, l as discoverHost, m as rolePasswordFor, n as ensure, o as INSTALL_HINT, p as resolveAutopgBin, r as ensureIfNeeded, s as ROLE_PASSWORD_SCHEME, t as dispose, u as ensureHostRunning, v as isExternalDatabaseEscapeHatch, w as buildRoleName, x as parseLease, y as resolveEnsureSkip } from "./lifecycle-
|
|
1
|
+
import { C as buildDatabaseName, S as readLease, _ as isCedarPgManagedUrl, a as urlFromLease, b as isOrphanLease, c as buildDatabaseUrl, d as parseHostStatus, f as requireAutopgBin, g as resolveWorktreeIdentity, h as resolveRoot, i as gc, l as discoverHost, m as rolePasswordFor, n as ensure, o as INSTALL_HINT, p as resolveAutopgBin, r as ensureIfNeeded, s as ROLE_PASSWORD_SCHEME, t as dispose, u as ensureHostRunning, v as isExternalDatabaseEscapeHatch, w as buildRoleName, x as parseLease, y as resolveEnsureSkip } from "./lifecycle-CT_8AWPv.mjs";
|
|
2
2
|
export { INSTALL_HINT, ROLE_PASSWORD_SCHEME, buildDatabaseName, buildDatabaseUrl, buildRoleName, discoverHost, dispose, ensure, ensureHostRunning, ensureIfNeeded, gc, isCedarPgManagedUrl, isExternalDatabaseEscapeHatch, isOrphanLease, parseHostStatus, parseLease, readLease, requireAutopgBin, resolveAutopgBin, resolveEnsureSkip, resolveRoot, resolveWorktreeIdentity, rolePasswordFor, urlFromLease };
|
package/dist/jest.cjs
CHANGED
|
@@ -2,10 +2,10 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_lifecycle = require("./lifecycle-
|
|
5
|
+
const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
|
|
6
6
|
//#region src/adapters/jest.ts
|
|
7
7
|
/**
|
|
8
|
-
* Jest globalSetup
|
|
8
|
+
* Jest globalSetup. Call from jest config:
|
|
9
9
|
* ```js
|
|
10
10
|
* globalSetup: require.resolve('@cedarjs/pg/jest')
|
|
11
11
|
* ```
|
|
@@ -17,7 +17,7 @@ async function globalSetup() {
|
|
|
17
17
|
setEnv: true
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
/** Lease-gated dispose
|
|
20
|
+
/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */
|
|
21
21
|
async function teardown() {
|
|
22
22
|
await require_lifecycle.dispose({ mode: "test" });
|
|
23
23
|
}
|
package/dist/jest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jest.cjs","names":["ensureIfNeeded","dispose"],"sources":["../src/adapters/jest.ts"],"sourcesContent":["import { dispose, ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Jest globalSetup
|
|
1
|
+
{"version":3,"file":"jest.cjs","names":["ensureIfNeeded","dispose"],"sources":["../src/adapters/jest.ts"],"sourcesContent":["import { dispose, ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Jest globalSetup. Call from jest config:\n * ```js\n * globalSetup: require.resolve('@cedarjs/pg/jest')\n * ```\n * Pair with `teardown` from this module as globalTeardown.\n */\nexport default async function globalSetup(): Promise<void> {\n await ensureIfNeeded({\n mode: \"test\",\n setEnv: true,\n });\n}\n\n/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */\nexport async function teardown(): Promise<void> {\n await dispose({ mode: \"test\" });\n}\n"],"mappings":";;;;;;;;;;;;;AASA,eAA8B,cAA6B;CACzD,MAAMA,kBAAAA,eAAe;EACnB,MAAM;EACN,QAAQ;CACV,CAAC;AACH;;AAGA,eAAsB,WAA0B;CAC9C,MAAMC,kBAAAA,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChC"}
|
package/dist/jest.d.cts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
//#region src/adapters/jest.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Jest globalSetup
|
|
3
|
+
* Jest globalSetup. Call from jest config:
|
|
4
4
|
* ```js
|
|
5
5
|
* globalSetup: require.resolve('@cedarjs/pg/jest')
|
|
6
6
|
* ```
|
|
7
7
|
* Pair with `teardown` from this module as globalTeardown.
|
|
8
8
|
*/
|
|
9
9
|
declare function globalSetup(): Promise<void>;
|
|
10
|
-
/** Lease-gated dispose
|
|
10
|
+
/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */
|
|
11
11
|
declare function teardown(): Promise<void>;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { globalSetup as default, teardown };
|
package/dist/jest.d.mts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
//#region src/adapters/jest.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Jest globalSetup
|
|
3
|
+
* Jest globalSetup. Call from jest config:
|
|
4
4
|
* ```js
|
|
5
5
|
* globalSetup: require.resolve('@cedarjs/pg/jest')
|
|
6
6
|
* ```
|
|
7
7
|
* Pair with `teardown` from this module as globalTeardown.
|
|
8
8
|
*/
|
|
9
9
|
declare function globalSetup(): Promise<void>;
|
|
10
|
-
/** Lease-gated dispose
|
|
10
|
+
/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */
|
|
11
11
|
declare function teardown(): Promise<void>;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { globalSetup as default, teardown };
|
package/dist/jest.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { r as ensureIfNeeded, t as dispose } from "./lifecycle-
|
|
1
|
+
import { r as ensureIfNeeded, t as dispose } from "./lifecycle-CT_8AWPv.mjs";
|
|
2
2
|
//#region src/adapters/jest.ts
|
|
3
3
|
/**
|
|
4
|
-
* Jest globalSetup
|
|
4
|
+
* Jest globalSetup. Call from jest config:
|
|
5
5
|
* ```js
|
|
6
6
|
* globalSetup: require.resolve('@cedarjs/pg/jest')
|
|
7
7
|
* ```
|
|
@@ -13,7 +13,7 @@ async function globalSetup() {
|
|
|
13
13
|
setEnv: true
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
-
/** Lease-gated dispose
|
|
16
|
+
/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */
|
|
17
17
|
async function teardown() {
|
|
18
18
|
await dispose({ mode: "test" });
|
|
19
19
|
}
|
package/dist/jest.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jest.mjs","names":[],"sources":["../src/adapters/jest.ts"],"sourcesContent":["import { dispose, ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Jest globalSetup
|
|
1
|
+
{"version":3,"file":"jest.mjs","names":[],"sources":["../src/adapters/jest.ts"],"sourcesContent":["import { dispose, ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Jest globalSetup. Call from jest config:\n * ```js\n * globalSetup: require.resolve('@cedarjs/pg/jest')\n * ```\n * Pair with `teardown` from this module as globalTeardown.\n */\nexport default async function globalSetup(): Promise<void> {\n await ensureIfNeeded({\n mode: \"test\",\n setEnv: true,\n });\n}\n\n/** Lease-gated dispose; no skip re-check (env may flip mid-suite). */\nexport async function teardown(): Promise<void> {\n await dispose({ mode: \"test\" });\n}\n"],"mappings":";;;;;;;;;AASA,eAA8B,cAA6B;CACzD,MAAM,eAAe;EACnB,MAAM;EACN,QAAQ;CACV,CAAC;AACH;;AAGA,eAAsB,WAA0B;CAC9C,MAAM,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChC"}
|
|
@@ -20,7 +20,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
22
|
//#endregion
|
|
23
|
-
const require_constants = require("./constants-
|
|
23
|
+
const require_constants = require("./constants-CNZn5Xro.cjs");
|
|
24
24
|
let node_fs = require("node:fs");
|
|
25
25
|
let node_path = require("node:path");
|
|
26
26
|
let node_os = require("node:os");
|
|
@@ -175,7 +175,7 @@ function isOrphanLease(lease) {
|
|
|
175
175
|
//#region src/core/policy.ts
|
|
176
176
|
/**
|
|
177
177
|
* True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).
|
|
178
|
-
* These must never be treated as an external escape hatch
|
|
178
|
+
* These must never be treated as an external escape hatch; always re-ensure so
|
|
179
179
|
* disposed/stale shell env cannot skip provisioning.
|
|
180
180
|
*/
|
|
181
181
|
function isCedarPgManagedUrl(url) {
|
|
@@ -740,4 +740,4 @@ Object.defineProperty(exports, "urlFromLease", {
|
|
|
740
740
|
}
|
|
741
741
|
});
|
|
742
742
|
|
|
743
|
-
//# sourceMappingURL=lifecycle-
|
|
743
|
+
//# sourceMappingURL=lifecycle-BGYtVXtx.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-BGYtVXtx.cjs","names":["STATE_DIRNAME","PASSWORD_SALT_PREFIX"],"sources":["../src/core/naming.ts","../src/core/lease.ts","../src/core/policy.ts","../src/core/worktree.ts","../src/providers/autopg.ts","../src/core/lifecycle.ts"],"sourcesContent":["import type { WorktreeIdentity } from \"./worktree.ts\";\n\nexport type DbMode = \"dev\" | \"test\";\n\nconst PG_MAX = 63;\nconst PREFIX = \"cpg_\";\n\n/**\n * Build an observable Postgres database name:\n * cpg_<repoSlug>_<worktreeSlug>_<mode>_<pathHash8>\n *\n * Never drops `mode` or `pathHash`. Truncates slugs to fit ≤63 chars.\n */\nexport function buildDatabaseName(identity: WorktreeIdentity, mode: DbMode): string {\n const modePart = mode;\n const hashPart = identity.pathHash;\n // PREFIX + slug + '_' + slug + '_' + mode + '_' + hash\n // fixed: PREFIX(4) + 2 underscores around mode/hash segments + mode + hash\n // layout: cpg_<repo>_<wt>_<mode>_<hash>\n const fixed =\n PREFIX.length +\n 1 + // after repo\n 1 + // after worktree\n modePart.length +\n 1 +\n hashPart.length;\n\n let budget = PG_MAX - fixed;\n if (budget < 2) {\n // Extreme truncation: keep mode+hash, minimal slugs\n return `${PREFIX}${modePart}_${hashPart}`.slice(0, PG_MAX);\n }\n\n let repo = identity.repoSlug;\n let wt = identity.worktreeSlug;\n // Split budget roughly evenly, preferring to keep both readable\n while (repo.length + wt.length > budget) {\n if (repo.length >= wt.length && repo.length > 1) {\n repo = repo.slice(0, -1);\n } else if (wt.length > 1) {\n wt = wt.slice(0, -1);\n } else {\n break;\n }\n }\n\n const name = `${PREFIX}${repo}_${wt}_${modePart}_${hashPart}`;\n if (name.length > PG_MAX) {\n return name.slice(0, PG_MAX);\n }\n return name;\n}\n\nexport function buildRoleName(databaseName: string): string {\n const suffix = \"_role\";\n const maxBase = PG_MAX - suffix.length;\n const base = databaseName.slice(0, maxBase);\n return `${base}${suffix}`;\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { STATE_DIRNAME } from \"./constants.ts\";\nimport type { DbMode } from \"./naming.ts\";\n\nexport type Lease = {\n schemaVersion: 1;\n mode: DbMode;\n root: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n databaseName: string;\n roleName: string;\n port: number;\n pid: number;\n createdAt: string;\n};\n\nfunction isDbMode(value: unknown): value is DbMode {\n return value === \"dev\" || value === \"test\";\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/** Narrow-parse a lease; reject incomplete or wrong-version records. */\nexport function parseLease(raw: unknown): Lease | null {\n if (!raw || typeof raw !== \"object\") return null;\n const o = raw as Record<string, unknown>;\n if (o.schemaVersion !== 1) return null;\n if (!isDbMode(o.mode)) return null;\n if (!isNonEmptyString(o.root)) return null;\n if (!isNonEmptyString(o.repoSlug)) return null;\n if (!isNonEmptyString(o.worktreeSlug)) return null;\n if (!isNonEmptyString(o.pathHash)) return null;\n if (!isNonEmptyString(o.databaseName)) return null;\n if (!isNonEmptyString(o.roleName)) return null;\n if (typeof o.port !== \"number\") return null;\n if (typeof o.pid !== \"number\") return null;\n if (!isNonEmptyString(o.createdAt)) return null;\n return {\n schemaVersion: 1,\n mode: o.mode,\n root: o.root,\n repoSlug: o.repoSlug,\n worktreeSlug: o.worktreeSlug,\n pathHash: o.pathHash,\n databaseName: o.databaseName,\n roleName: o.roleName,\n port: o.port,\n pid: o.pid,\n createdAt: o.createdAt,\n };\n}\n\nexport function leaseDir(root: string): string {\n return join(root, STATE_DIRNAME);\n}\n\nexport function leasePath(root: string, mode: DbMode): string {\n return join(leaseDir(root), `${mode}.json`);\n}\n\n/** Durable registry outside worktrees so GC can find orphans after a checkout is deleted. */\nexport function registryDir(): string {\n return process.env.CEDAR_PG_REGISTRY_DIR || join(homedir(), STATE_DIRNAME, \"registry\");\n}\n\nfunction registryPath(databaseName: string): string {\n return join(registryDir(), `${databaseName}.json`);\n}\n\nfunction writeJsonAtomic(file: string, value: unknown): void {\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.tmp.${process.pid}`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n}\n\nfunction registerLease(lease: Lease): void {\n writeJsonAtomic(registryPath(lease.databaseName), lease);\n}\n\nfunction unregisterLease(databaseName: string): void {\n try {\n unlinkSync(registryPath(databaseName));\n } catch {\n // missing is fine\n }\n}\n\nfunction peekDatabaseName(root: string, mode: DbMode): string | undefined {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return undefined;\n try {\n const raw = JSON.parse(readFileSync(file, \"utf8\")) as { databaseName?: unknown };\n return typeof raw.databaseName === \"string\" && raw.databaseName ? raw.databaseName : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function readLease(root: string, mode: DbMode): Lease | null {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return null;\n try {\n return parseLease(JSON.parse(readFileSync(file, \"utf8\")));\n } catch {\n return null;\n }\n}\n\n/**\n * Persist lease. Registry is written first (GC source of truth), then the worktree cache.\n */\nexport function writeLease(lease: Lease): void {\n registerLease(lease);\n const dir = leaseDir(lease.root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeJsonAtomic(leasePath(lease.root, lease.mode), lease);\n}\n\n/**\n * Remove registry entry by databaseName and best-effort delete the worktree lease file.\n * Call only after a successful DROP (or when intentionally abandoning tracking).\n */\nexport function forgetLease(lease: Pick<Lease, \"root\" | \"mode\" | \"databaseName\">): void {\n unregisterLease(lease.databaseName);\n try {\n unlinkSync(leasePath(lease.root, lease.mode));\n } catch {\n // root or file may already be gone\n }\n}\n\n/**\n * Clear worktree lease + registry. Unregisters by databaseName even when the local\n * file fails full parse (peeks databaseName from raw JSON).\n */\nexport function clearLease(root: string, mode: DbMode): void {\n const existing = readLease(root, mode);\n if (existing) {\n forgetLease(existing);\n return;\n }\n const databaseName = peekDatabaseName(root, mode);\n try {\n unlinkSync(leasePath(root, mode));\n } catch {\n // missing is fine\n }\n if (databaseName) {\n unregisterLease(databaseName);\n }\n}\n\n/** All registry entries (used by gc). */\nexport function listRegistryLeases(): Lease[] {\n const dir = registryDir();\n if (!existsSync(dir)) return [];\n const out: Lease[] = [];\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(\".json\")) continue;\n try {\n const lease = parseLease(JSON.parse(readFileSync(join(dir, name), \"utf8\")));\n if (lease) out.push(lease);\n } catch {\n // skip corrupt\n }\n }\n return out;\n}\n\nexport function isOrphanLease(lease: Lease): boolean {\n return !existsSync(lease.root);\n}\n","/**\n * Shared ensure skip policy for test-runner adapters and Cedar CLI bridges.\n */\n\nexport type EnsureSkip =\n | { skip: false }\n | { skip: true; reason: \"disabled\" }\n | { skip: true; reason: \"external-url\"; databaseUrl: string };\n\nexport type ResolveEnsureSkipInput = {\n /** Candidate URL (TEST_DATABASE_URL for tests, DATABASE_URL for dev). */\n url?: string;\n /** When true, never skip (CEDAR_PG_FORCE=1). */\n force?: boolean;\n /**\n * When true, skip ensure entirely.\n * Defaults from CEDAR_PG=0|false (opt-out adapters). Pass `false` for Cedar opt-in flows.\n */\n disabled?: boolean;\n};\n\n/**\n * True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).\n * These must never be treated as an external escape hatch; always re-ensure so\n * disposed/stale shell env cannot skip provisioning.\n */\nexport function isCedarPgManagedUrl(url: string | undefined): boolean {\n if (!url || url.startsWith(\"file:\")) return false;\n try {\n const parsed = new URL(url);\n const databaseName = decodeURIComponent(\n (parsed.pathname.replace(/^\\//, \"\").split(\"?\")[0] ?? \"\").trim(),\n );\n const user = decodeURIComponent(parsed.username);\n return databaseName.startsWith(\"cpg_\") || user.startsWith(\"cpg_\");\n } catch {\n return false;\n }\n}\n\n/**\n * True when `url` is a real external database and ensure should be skipped.\n * Sqlite `file:` URLs and cedarpg `cpg_*` URLs are not external.\n */\nexport function isExternalDatabaseEscapeHatch(url: string | undefined): boolean {\n if (!url) return false;\n if (url.startsWith(\"file:\")) return false;\n if (isCedarPgManagedUrl(url)) return false;\n return true;\n}\n\n/**\n * Decide whether ensure should run.\n * Adapters may call with no args (env defaults). Cedar opt-in should pass `disabled: false`\n * and the relevant `url` (`DATABASE_URL` or `TEST_DATABASE_URL`).\n */\nexport function resolveEnsureSkip(input: ResolveEnsureSkipInput = {}): EnsureSkip {\n const disabled =\n input.disabled ?? (process.env.CEDAR_PG === \"0\" || process.env.CEDAR_PG === \"false\");\n if (disabled) {\n return { skip: true, reason: \"disabled\" };\n }\n\n const force = input.force ?? process.env.CEDAR_PG_FORCE === \"1\";\n if (force) {\n return { skip: false };\n }\n\n const url = input.url !== undefined ? input.url : process.env.TEST_DATABASE_URL;\n if (url && isExternalDatabaseEscapeHatch(url)) {\n return { skip: true, reason: \"external-url\", databaseUrl: url };\n }\n return { skip: false };\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\nexport type WorktreeIdentity = {\n /** Absolute path to the worktree / project root. */\n root: string;\n /** Basename of the git common dir / main checkout (e.g. `cedar`). */\n repoSlug: string;\n /** Basename of this worktree directory (e.g. `feat-auth`). */\n worktreeSlug: string;\n /** First 8 hex chars of sha256(absolute root). */\n pathHash: string;\n};\n\nfunction git(args: string[], cwd: string): string | null {\n try {\n return execFileSync(\"git\", args, {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return null;\n }\n}\n\nfunction sanitizeSlug(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n}\n\n/**\n * Resolve the project / worktree root.\n * Explicit `root` wins (app project path). Otherwise prefer git toplevel, then cwd.\n */\nexport function resolveRoot(root?: string): string {\n if (root) return resolve(root);\n const start = resolve(process.cwd());\n const toplevel = git([\"rev-parse\", \"--show-toplevel\"], start);\n if (toplevel) return toplevel;\n return start;\n}\n\n/**\n * Identify repo + worktree for observable DB naming.\n */\nexport function resolveWorktreeIdentity(root?: string): WorktreeIdentity {\n const absRoot = resolveRoot(root);\n const pathHash = createHash(\"sha256\").update(absRoot).digest(\"hex\").slice(0, 8);\n\n const commonDir = git([\"rev-parse\", \"--git-common-dir\"], absRoot);\n let repoSlug = sanitizeSlug(basename(absRoot));\n if (commonDir) {\n // common-dir is often `<main>/.git` or an absolute path to the main .git\n const commonAbs = resolve(absRoot, commonDir);\n // If commonAbs ends with `.git`, parent is main worktree root\n const mainRoot = basename(commonAbs) === \".git\" ? dirname(commonAbs) : commonAbs;\n // For linked worktrees, common-dir may be `.../main/.git`\n const candidate = basename(mainRoot) === \".git\" ? dirname(mainRoot) : mainRoot;\n const fromGit = sanitizeSlug(basename(candidate));\n if (fromGit) repoSlug = fromGit;\n }\n\n // Prefer gitdir worktree name when present (`.git` file pointing at worktrees/<name>)\n let worktreeSlug = sanitizeSlug(basename(absRoot));\n const gitFile = resolve(absRoot, \".git\");\n if (existsSync(gitFile)) {\n try {\n const content = readFileSync(gitFile, \"utf8\").trim();\n const match = content.match(/gitdir:\\s*(.+)$/m);\n if (match?.[1]) {\n const gitdir = resolve(absRoot, match[1].trim());\n // .../.git/worktrees/<name>\n const parts = gitdir.split(/[/\\\\]/);\n const wtIdx = parts.lastIndexOf(\"worktrees\");\n if (wtIdx >= 0 && parts[wtIdx + 1]) {\n const fromWt = sanitizeSlug(parts[wtIdx + 1]!);\n if (fromWt) worktreeSlug = fromWt;\n }\n }\n } catch {\n // keep basename slug\n }\n }\n\n if (!repoSlug) repoSlug = \"repo\";\n if (!worktreeSlug) worktreeSlug = \"wt\";\n\n return { root: absRoot, repoSlug, worktreeSlug, pathHash };\n}\n","import { createHash } from \"node:crypto\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport pg from \"pg\";\nimport { PASSWORD_SALT_PREFIX } from \"../core/constants.ts\";\n\nexport type AutopgDiscovery = {\n port: number;\n adminUrl: string;\n bin: string;\n};\n\nexport const INSTALL_HINT =\n \"autopg is required. Install with:\\n\" +\n \" curl -fsSL https://raw.githubusercontent.com/automagik-dev/autopg/main/install.sh | bash\\n\" +\n \"Then ensure ~/.local/bin is on PATH, or set AUTOPG_BIN.\";\n\n/** Password scheme v1: sha256(PASSWORD_SALT_PREFIX + \"\\\\0\" + databaseName) hex[:32]. Frozen for URL rebuild. */\nexport const ROLE_PASSWORD_SCHEME = \"v1\" as const;\n\nfunction candidateBins(): string[] {\n const out: string[] = [];\n if (process.env.AUTOPG_BIN) out.push(process.env.AUTOPG_BIN);\n out.push(\"autopg\");\n out.push(join(homedir(), \".local\", \"bin\", \"autopg\"));\n return out;\n}\n\nexport function resolveAutopgBin(): string | null {\n for (const bin of candidateBins()) {\n if (bin === \"autopg\") {\n const which = spawnSync(\"which\", [\"autopg\"], { encoding: \"utf8\" });\n if (which.status === 0 && which.stdout.trim()) return which.stdout.trim();\n continue;\n }\n if (existsSync(bin)) return bin;\n }\n return null;\n}\n\nexport function requireAutopgBin(): string {\n const bin = resolveAutopgBin();\n if (!bin) {\n throw new Error(INSTALL_HINT);\n }\n return bin;\n}\n\n/**\n * Parse `autopg status --json` output. Requires a numeric port and running !== false.\n */\nexport function parseHostStatus(json: string): { port: number } {\n let parsed: { port?: unknown; running?: unknown };\n try {\n parsed = JSON.parse(json) as { port?: unknown; running?: unknown };\n } catch {\n throw new Error(`autopg status --json returned invalid JSON.\\n${INSTALL_HINT}`);\n }\n if (typeof parsed.port !== \"number\") {\n throw new Error(`autopg status --json missing numeric port.\\n${INSTALL_HINT}`);\n }\n if (parsed.running === false) {\n throw new Error(`autopg host is not running.\\n${INSTALL_HINT}`);\n }\n return { port: parsed.port };\n}\n\nfunction adminUrlFor(port: number): string {\n return `postgresql://postgres:postgres@127.0.0.1:${port}/postgres`;\n}\n\n/**\n * Discover a live autopg host via `autopg status --json`. Throws if the host is not proven live.\n */\nexport function discoverHost(bin = requireAutopgBin()): AutopgDiscovery {\n let status: string;\n try {\n status = execFileSync(bin, [\"status\", \"--json\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to query autopg status.\\n${detail}\\n${INSTALL_HINT}`);\n }\n const { port } = parseHostStatus(status);\n return { port, adminUrl: adminUrlFor(port), bin };\n}\n\n/**\n * Ensure the host postmaster is up. Runs `autopg install` only after status fails, then re-discovers.\n */\nexport function ensureHostRunning(bin = requireAutopgBin()): AutopgDiscovery {\n try {\n return discoverHost(bin);\n } catch {\n // status failed — attempt install, then require a successful rediscovery\n }\n\n const install = spawnSync(bin, [\"install\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (install.status !== 0) {\n throw new Error(\n `Failed to start autopg host (exit ${install.status}).\\n` +\n `${install.stderr || install.stdout || \"\"}\\n${INSTALL_HINT}`,\n );\n }\n return discoverHost(bin);\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction quoteLiteral(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Deterministic local-only password for an app role (Prisma/TCP need it;\n * autopg hba uses `password` for 127.0.0.1).\n */\nexport function rolePasswordFor(databaseName: string): string {\n return createHash(\"sha256\")\n .update(`${PASSWORD_SALT_PREFIX}\\0${databaseName}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n/**\n * Idempotently CREATE ROLE + CREATE DATABASE with cedar-pg owned names.\n */\nexport async function ensureDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n password?: string;\n}): Promise<void> {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n const roleExists = await client.query(\"SELECT 1 FROM pg_roles WHERE rolname = $1\", [\n opts.roleName,\n ]);\n if (roleExists.rowCount === 0) {\n await client.query(\n `CREATE ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n } else {\n await client.query(\n `ALTER ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n }\n\n const dbExists = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [\n opts.databaseName,\n ]);\n if (dbExists.rowCount === 0) {\n await client.query(\n `CREATE DATABASE ${quoteIdent(opts.databaseName)} OWNER ${quoteIdent(opts.roleName)}`,\n );\n } else {\n await client.query(\n `ALTER DATABASE ${quoteIdent(opts.databaseName)} OWNER TO ${quoteIdent(opts.roleName)}`,\n );\n }\n } finally {\n await client.end();\n }\n}\n\n/**\n * DROP DATABASE (force terminate backends) + DROP ROLE.\n */\nexport async function dropDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n}): Promise<void> {\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n await client.query(\n `\n SELECT pg_terminate_backend(pid)\n FROM pg_stat_activity\n WHERE datname = $1 AND pid <> pg_backend_pid()\n `,\n [opts.databaseName],\n );\n await client.query(`DROP DATABASE IF EXISTS ${quoteIdent(opts.databaseName)}`);\n const owns = await client.query(\n `SELECT 1 FROM pg_database WHERE datdba = (SELECT oid FROM pg_roles WHERE rolname = $1) LIMIT 1`,\n [opts.roleName],\n );\n if (owns.rowCount === 0) {\n await client.query(`DROP ROLE IF EXISTS ${quoteIdent(opts.roleName)}`);\n }\n } finally {\n await client.end();\n }\n}\n\nexport function buildDatabaseUrl(opts: {\n port: number;\n databaseName: string;\n roleName: string;\n password?: string;\n}): string {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const user = encodeURIComponent(opts.roleName);\n const pass = encodeURIComponent(password);\n return `postgresql://${user}:${pass}@127.0.0.1:${opts.port}/${opts.databaseName}`;\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { buildDatabaseName, buildRoleName, type DbMode } from \"./naming.ts\";\nimport {\n forgetLease,\n isOrphanLease,\n leaseDir,\n listRegistryLeases,\n readLease,\n writeLease,\n type Lease,\n} from \"./lease.ts\";\nimport { resolveEnsureSkip, type ResolveEnsureSkipInput } from \"./policy.ts\";\nimport { resolveWorktreeIdentity } from \"./worktree.ts\";\nimport {\n buildDatabaseUrl,\n dropDatabase,\n ensureDatabase,\n ensureHostRunning,\n} from \"../providers/autopg.ts\";\n\nfunction writeEnvFile(root: string, mode: DbMode, databaseUrl: string): void {\n const dir = leaseDir(root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n let body = `DATABASE_URL=${databaseUrl}\\n`;\n if (mode === \"test\") body += `TEST_DATABASE_URL=${databaseUrl}\\n`;\n writeFileSync(join(dir, `${mode}.env`), body, { mode: 0o600 });\n}\n\nexport function urlFromLease(lease: Lease): string {\n return buildDatabaseUrl({\n port: lease.port,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n}\n\n/**\n * DROP the database, then forget lease/registry. Never forget without a successful DROP.\n */\nasync function dropThenForget(lease: Lease, adminUrl: string): Promise<void> {\n await dropDatabase({\n adminUrl,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n forgetLease(lease);\n}\n\nexport type EnsureOptions = {\n root?: string;\n mode: DbMode;\n /** Inject DATABASE_URL / TEST_DATABASE_URL into process.env (default true). */\n setEnv?: boolean;\n};\n\nexport type EnsureResult = {\n databaseUrl: string;\n databaseName: string;\n roleName: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n root: string;\n mode: DbMode;\n port: number;\n dispose: () => Promise<void>;\n};\n\n/**\n * Ensure a worktree-scoped database exists and return connection info.\n *\n * - `dev`: keep DB across restarts.\n * - `test`: DROP when `dispose()` is awaited (callers / test runners own teardown).\n */\nexport async function ensure(options: EnsureOptions): Promise<EnsureResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode;\n const databaseName = buildDatabaseName(identity, mode);\n const roleName = buildRoleName(databaseName);\n\n const host = ensureHostRunning();\n await ensureDatabase({\n adminUrl: host.adminUrl,\n databaseName,\n roleName,\n });\n\n const databaseUrl = buildDatabaseUrl({\n port: host.port,\n databaseName,\n roleName,\n });\n\n const lease: Lease = {\n schemaVersion: 1,\n mode,\n root: identity.root,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n databaseName,\n roleName,\n port: host.port,\n pid: process.pid,\n createdAt: new Date().toISOString(),\n };\n writeLease(lease);\n writeEnvFile(identity.root, mode, databaseUrl);\n\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = databaseUrl;\n if (mode === \"test\") {\n process.env.TEST_DATABASE_URL = databaseUrl;\n }\n }\n\n const disposeFn = async () => {\n await dispose({ root: identity.root, mode });\n };\n\n return {\n databaseUrl,\n databaseName,\n roleName,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n root: identity.root,\n mode,\n port: host.port,\n dispose: disposeFn,\n };\n}\n\nexport type EnsureIfNeededOptions = EnsureOptions & ResolveEnsureSkipInput;\n\nexport type EnsureIfNeededResult =\n | { status: \"skipped\"; reason: \"disabled\" }\n | { status: \"skipped\"; reason: \"external-url\"; databaseUrl: string }\n | ({ status: \"ensured\" } & EnsureResult);\n\n/**\n * Resolve skip policy then ensure. Single entry for hosts (Cedar CLI, Jest, Vitest).\n * On external-url skip, sets DATABASE_URL when `setEnv` is not false.\n */\nexport async function ensureIfNeeded(\n options: EnsureIfNeededOptions,\n): Promise<EnsureIfNeededResult> {\n const skip = resolveEnsureSkip({\n url: options.url,\n force: options.force,\n disabled: options.disabled,\n });\n if (skip.skip) {\n if (skip.reason === \"external-url\") {\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = skip.databaseUrl;\n }\n return { status: \"skipped\", reason: \"external-url\", databaseUrl: skip.databaseUrl };\n }\n return { status: \"skipped\", reason: \"disabled\" };\n }\n\n const result = await ensure({\n root: options.root,\n mode: options.mode,\n setEnv: options.setEnv,\n });\n return { status: \"ensured\", ...result };\n}\n\nexport type DisposeOptions = {\n root?: string;\n mode?: DbMode;\n};\n\nexport type DisposeResult =\n | { dropped: true; databaseName: string }\n | { dropped: false; reason: \"no-lease\" | \"host-unavailable\" };\n\n/**\n * DROP the worktree database for the given mode (default: test).\n * No-ops without a valid lease (never invents a name to DROP).\n * If the host is unavailable, leaves the lease so dispose/gc can retry.\n */\nexport async function dispose(options: DisposeOptions = {}): Promise<DisposeResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode ?? \"test\";\n const lease = readLease(identity.root, mode);\n if (!lease) return { dropped: false, reason: \"no-lease\" };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n // Keep lease + registry so a later dispose/gc can still find the DB.\n return { dropped: false, reason: \"host-unavailable\" };\n }\n\n await dropThenForget(lease, host.adminUrl);\n return { dropped: true, databaseName: lease.databaseName };\n}\n\n/**\n * Drop databases whose registered worktree root no longer exists on disk.\n * Registry entries are removed only after a successful DROP.\n */\nexport async function gc(): Promise<{\n dropped: string[];\n}> {\n const dropped: string[] = [];\n const orphans = listRegistryLeases().filter(isOrphanLease);\n if (orphans.length === 0) return { dropped };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n return { dropped };\n }\n\n for (const lease of orphans) {\n try {\n await dropThenForget(lease, host.adminUrl);\n dropped.push(lease.databaseName);\n } catch {\n // Keep registry entry for retry\n }\n }\n return { dropped };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as PASSWORD_SALT_PREFIX, r as STATE_DIRNAME } from "./constants-
|
|
1
|
+
import { n as PASSWORD_SALT_PREFIX, r as STATE_DIRNAME } from "./constants-BY97wXjA.mjs";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
@@ -152,7 +152,7 @@ function isOrphanLease(lease) {
|
|
|
152
152
|
//#region src/core/policy.ts
|
|
153
153
|
/**
|
|
154
154
|
* True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).
|
|
155
|
-
* These must never be treated as an external escape hatch
|
|
155
|
+
* These must never be treated as an external escape hatch; always re-ensure so
|
|
156
156
|
* disposed/stale shell env cannot skip provisioning.
|
|
157
157
|
*/
|
|
158
158
|
function isCedarPgManagedUrl(url) {
|
|
@@ -574,4 +574,4 @@ async function gc() {
|
|
|
574
574
|
//#endregion
|
|
575
575
|
export { buildDatabaseName as C, readLease as S, isCedarPgManagedUrl as _, urlFromLease as a, isOrphanLease as b, buildDatabaseUrl as c, parseHostStatus as d, requireAutopgBin as f, resolveWorktreeIdentity as g, resolveRoot as h, gc as i, discoverHost as l, rolePasswordFor as m, ensure as n, INSTALL_HINT as o, resolveAutopgBin as p, ensureIfNeeded as r, ROLE_PASSWORD_SCHEME as s, dispose as t, ensureHostRunning as u, isExternalDatabaseEscapeHatch as v, buildRoleName as w, parseLease as x, resolveEnsureSkip as y };
|
|
576
576
|
|
|
577
|
-
//# sourceMappingURL=lifecycle-
|
|
577
|
+
//# sourceMappingURL=lifecycle-CT_8AWPv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-CT_8AWPv.mjs","names":[],"sources":["../src/core/naming.ts","../src/core/lease.ts","../src/core/policy.ts","../src/core/worktree.ts","../src/providers/autopg.ts","../src/core/lifecycle.ts"],"sourcesContent":["import type { WorktreeIdentity } from \"./worktree.ts\";\n\nexport type DbMode = \"dev\" | \"test\";\n\nconst PG_MAX = 63;\nconst PREFIX = \"cpg_\";\n\n/**\n * Build an observable Postgres database name:\n * cpg_<repoSlug>_<worktreeSlug>_<mode>_<pathHash8>\n *\n * Never drops `mode` or `pathHash`. Truncates slugs to fit ≤63 chars.\n */\nexport function buildDatabaseName(identity: WorktreeIdentity, mode: DbMode): string {\n const modePart = mode;\n const hashPart = identity.pathHash;\n // PREFIX + slug + '_' + slug + '_' + mode + '_' + hash\n // fixed: PREFIX(4) + 2 underscores around mode/hash segments + mode + hash\n // layout: cpg_<repo>_<wt>_<mode>_<hash>\n const fixed =\n PREFIX.length +\n 1 + // after repo\n 1 + // after worktree\n modePart.length +\n 1 +\n hashPart.length;\n\n let budget = PG_MAX - fixed;\n if (budget < 2) {\n // Extreme truncation: keep mode+hash, minimal slugs\n return `${PREFIX}${modePart}_${hashPart}`.slice(0, PG_MAX);\n }\n\n let repo = identity.repoSlug;\n let wt = identity.worktreeSlug;\n // Split budget roughly evenly, preferring to keep both readable\n while (repo.length + wt.length > budget) {\n if (repo.length >= wt.length && repo.length > 1) {\n repo = repo.slice(0, -1);\n } else if (wt.length > 1) {\n wt = wt.slice(0, -1);\n } else {\n break;\n }\n }\n\n const name = `${PREFIX}${repo}_${wt}_${modePart}_${hashPart}`;\n if (name.length > PG_MAX) {\n return name.slice(0, PG_MAX);\n }\n return name;\n}\n\nexport function buildRoleName(databaseName: string): string {\n const suffix = \"_role\";\n const maxBase = PG_MAX - suffix.length;\n const base = databaseName.slice(0, maxBase);\n return `${base}${suffix}`;\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { STATE_DIRNAME } from \"./constants.ts\";\nimport type { DbMode } from \"./naming.ts\";\n\nexport type Lease = {\n schemaVersion: 1;\n mode: DbMode;\n root: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n databaseName: string;\n roleName: string;\n port: number;\n pid: number;\n createdAt: string;\n};\n\nfunction isDbMode(value: unknown): value is DbMode {\n return value === \"dev\" || value === \"test\";\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/** Narrow-parse a lease; reject incomplete or wrong-version records. */\nexport function parseLease(raw: unknown): Lease | null {\n if (!raw || typeof raw !== \"object\") return null;\n const o = raw as Record<string, unknown>;\n if (o.schemaVersion !== 1) return null;\n if (!isDbMode(o.mode)) return null;\n if (!isNonEmptyString(o.root)) return null;\n if (!isNonEmptyString(o.repoSlug)) return null;\n if (!isNonEmptyString(o.worktreeSlug)) return null;\n if (!isNonEmptyString(o.pathHash)) return null;\n if (!isNonEmptyString(o.databaseName)) return null;\n if (!isNonEmptyString(o.roleName)) return null;\n if (typeof o.port !== \"number\") return null;\n if (typeof o.pid !== \"number\") return null;\n if (!isNonEmptyString(o.createdAt)) return null;\n return {\n schemaVersion: 1,\n mode: o.mode,\n root: o.root,\n repoSlug: o.repoSlug,\n worktreeSlug: o.worktreeSlug,\n pathHash: o.pathHash,\n databaseName: o.databaseName,\n roleName: o.roleName,\n port: o.port,\n pid: o.pid,\n createdAt: o.createdAt,\n };\n}\n\nexport function leaseDir(root: string): string {\n return join(root, STATE_DIRNAME);\n}\n\nexport function leasePath(root: string, mode: DbMode): string {\n return join(leaseDir(root), `${mode}.json`);\n}\n\n/** Durable registry outside worktrees so GC can find orphans after a checkout is deleted. */\nexport function registryDir(): string {\n return process.env.CEDAR_PG_REGISTRY_DIR || join(homedir(), STATE_DIRNAME, \"registry\");\n}\n\nfunction registryPath(databaseName: string): string {\n return join(registryDir(), `${databaseName}.json`);\n}\n\nfunction writeJsonAtomic(file: string, value: unknown): void {\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.tmp.${process.pid}`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n}\n\nfunction registerLease(lease: Lease): void {\n writeJsonAtomic(registryPath(lease.databaseName), lease);\n}\n\nfunction unregisterLease(databaseName: string): void {\n try {\n unlinkSync(registryPath(databaseName));\n } catch {\n // missing is fine\n }\n}\n\nfunction peekDatabaseName(root: string, mode: DbMode): string | undefined {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return undefined;\n try {\n const raw = JSON.parse(readFileSync(file, \"utf8\")) as { databaseName?: unknown };\n return typeof raw.databaseName === \"string\" && raw.databaseName ? raw.databaseName : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function readLease(root: string, mode: DbMode): Lease | null {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return null;\n try {\n return parseLease(JSON.parse(readFileSync(file, \"utf8\")));\n } catch {\n return null;\n }\n}\n\n/**\n * Persist lease. Registry is written first (GC source of truth), then the worktree cache.\n */\nexport function writeLease(lease: Lease): void {\n registerLease(lease);\n const dir = leaseDir(lease.root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeJsonAtomic(leasePath(lease.root, lease.mode), lease);\n}\n\n/**\n * Remove registry entry by databaseName and best-effort delete the worktree lease file.\n * Call only after a successful DROP (or when intentionally abandoning tracking).\n */\nexport function forgetLease(lease: Pick<Lease, \"root\" | \"mode\" | \"databaseName\">): void {\n unregisterLease(lease.databaseName);\n try {\n unlinkSync(leasePath(lease.root, lease.mode));\n } catch {\n // root or file may already be gone\n }\n}\n\n/**\n * Clear worktree lease + registry. Unregisters by databaseName even when the local\n * file fails full parse (peeks databaseName from raw JSON).\n */\nexport function clearLease(root: string, mode: DbMode): void {\n const existing = readLease(root, mode);\n if (existing) {\n forgetLease(existing);\n return;\n }\n const databaseName = peekDatabaseName(root, mode);\n try {\n unlinkSync(leasePath(root, mode));\n } catch {\n // missing is fine\n }\n if (databaseName) {\n unregisterLease(databaseName);\n }\n}\n\n/** All registry entries (used by gc). */\nexport function listRegistryLeases(): Lease[] {\n const dir = registryDir();\n if (!existsSync(dir)) return [];\n const out: Lease[] = [];\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(\".json\")) continue;\n try {\n const lease = parseLease(JSON.parse(readFileSync(join(dir, name), \"utf8\")));\n if (lease) out.push(lease);\n } catch {\n // skip corrupt\n }\n }\n return out;\n}\n\nexport function isOrphanLease(lease: Lease): boolean {\n return !existsSync(lease.root);\n}\n","/**\n * Shared ensure skip policy for test-runner adapters and Cedar CLI bridges.\n */\n\nexport type EnsureSkip =\n | { skip: false }\n | { skip: true; reason: \"disabled\" }\n | { skip: true; reason: \"external-url\"; databaseUrl: string };\n\nexport type ResolveEnsureSkipInput = {\n /** Candidate URL (TEST_DATABASE_URL for tests, DATABASE_URL for dev). */\n url?: string;\n /** When true, never skip (CEDAR_PG_FORCE=1). */\n force?: boolean;\n /**\n * When true, skip ensure entirely.\n * Defaults from CEDAR_PG=0|false (opt-out adapters). Pass `false` for Cedar opt-in flows.\n */\n disabled?: boolean;\n};\n\n/**\n * True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).\n * These must never be treated as an external escape hatch; always re-ensure so\n * disposed/stale shell env cannot skip provisioning.\n */\nexport function isCedarPgManagedUrl(url: string | undefined): boolean {\n if (!url || url.startsWith(\"file:\")) return false;\n try {\n const parsed = new URL(url);\n const databaseName = decodeURIComponent(\n (parsed.pathname.replace(/^\\//, \"\").split(\"?\")[0] ?? \"\").trim(),\n );\n const user = decodeURIComponent(parsed.username);\n return databaseName.startsWith(\"cpg_\") || user.startsWith(\"cpg_\");\n } catch {\n return false;\n }\n}\n\n/**\n * True when `url` is a real external database and ensure should be skipped.\n * Sqlite `file:` URLs and cedarpg `cpg_*` URLs are not external.\n */\nexport function isExternalDatabaseEscapeHatch(url: string | undefined): boolean {\n if (!url) return false;\n if (url.startsWith(\"file:\")) return false;\n if (isCedarPgManagedUrl(url)) return false;\n return true;\n}\n\n/**\n * Decide whether ensure should run.\n * Adapters may call with no args (env defaults). Cedar opt-in should pass `disabled: false`\n * and the relevant `url` (`DATABASE_URL` or `TEST_DATABASE_URL`).\n */\nexport function resolveEnsureSkip(input: ResolveEnsureSkipInput = {}): EnsureSkip {\n const disabled =\n input.disabled ?? (process.env.CEDAR_PG === \"0\" || process.env.CEDAR_PG === \"false\");\n if (disabled) {\n return { skip: true, reason: \"disabled\" };\n }\n\n const force = input.force ?? process.env.CEDAR_PG_FORCE === \"1\";\n if (force) {\n return { skip: false };\n }\n\n const url = input.url !== undefined ? input.url : process.env.TEST_DATABASE_URL;\n if (url && isExternalDatabaseEscapeHatch(url)) {\n return { skip: true, reason: \"external-url\", databaseUrl: url };\n }\n return { skip: false };\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\nexport type WorktreeIdentity = {\n /** Absolute path to the worktree / project root. */\n root: string;\n /** Basename of the git common dir / main checkout (e.g. `cedar`). */\n repoSlug: string;\n /** Basename of this worktree directory (e.g. `feat-auth`). */\n worktreeSlug: string;\n /** First 8 hex chars of sha256(absolute root). */\n pathHash: string;\n};\n\nfunction git(args: string[], cwd: string): string | null {\n try {\n return execFileSync(\"git\", args, {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return null;\n }\n}\n\nfunction sanitizeSlug(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n}\n\n/**\n * Resolve the project / worktree root.\n * Explicit `root` wins (app project path). Otherwise prefer git toplevel, then cwd.\n */\nexport function resolveRoot(root?: string): string {\n if (root) return resolve(root);\n const start = resolve(process.cwd());\n const toplevel = git([\"rev-parse\", \"--show-toplevel\"], start);\n if (toplevel) return toplevel;\n return start;\n}\n\n/**\n * Identify repo + worktree for observable DB naming.\n */\nexport function resolveWorktreeIdentity(root?: string): WorktreeIdentity {\n const absRoot = resolveRoot(root);\n const pathHash = createHash(\"sha256\").update(absRoot).digest(\"hex\").slice(0, 8);\n\n const commonDir = git([\"rev-parse\", \"--git-common-dir\"], absRoot);\n let repoSlug = sanitizeSlug(basename(absRoot));\n if (commonDir) {\n // common-dir is often `<main>/.git` or an absolute path to the main .git\n const commonAbs = resolve(absRoot, commonDir);\n // If commonAbs ends with `.git`, parent is main worktree root\n const mainRoot = basename(commonAbs) === \".git\" ? dirname(commonAbs) : commonAbs;\n // For linked worktrees, common-dir may be `.../main/.git`\n const candidate = basename(mainRoot) === \".git\" ? dirname(mainRoot) : mainRoot;\n const fromGit = sanitizeSlug(basename(candidate));\n if (fromGit) repoSlug = fromGit;\n }\n\n // Prefer gitdir worktree name when present (`.git` file pointing at worktrees/<name>)\n let worktreeSlug = sanitizeSlug(basename(absRoot));\n const gitFile = resolve(absRoot, \".git\");\n if (existsSync(gitFile)) {\n try {\n const content = readFileSync(gitFile, \"utf8\").trim();\n const match = content.match(/gitdir:\\s*(.+)$/m);\n if (match?.[1]) {\n const gitdir = resolve(absRoot, match[1].trim());\n // .../.git/worktrees/<name>\n const parts = gitdir.split(/[/\\\\]/);\n const wtIdx = parts.lastIndexOf(\"worktrees\");\n if (wtIdx >= 0 && parts[wtIdx + 1]) {\n const fromWt = sanitizeSlug(parts[wtIdx + 1]!);\n if (fromWt) worktreeSlug = fromWt;\n }\n }\n } catch {\n // keep basename slug\n }\n }\n\n if (!repoSlug) repoSlug = \"repo\";\n if (!worktreeSlug) worktreeSlug = \"wt\";\n\n return { root: absRoot, repoSlug, worktreeSlug, pathHash };\n}\n","import { createHash } from \"node:crypto\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport pg from \"pg\";\nimport { PASSWORD_SALT_PREFIX } from \"../core/constants.ts\";\n\nexport type AutopgDiscovery = {\n port: number;\n adminUrl: string;\n bin: string;\n};\n\nexport const INSTALL_HINT =\n \"autopg is required. Install with:\\n\" +\n \" curl -fsSL https://raw.githubusercontent.com/automagik-dev/autopg/main/install.sh | bash\\n\" +\n \"Then ensure ~/.local/bin is on PATH, or set AUTOPG_BIN.\";\n\n/** Password scheme v1: sha256(PASSWORD_SALT_PREFIX + \"\\\\0\" + databaseName) hex[:32]. Frozen for URL rebuild. */\nexport const ROLE_PASSWORD_SCHEME = \"v1\" as const;\n\nfunction candidateBins(): string[] {\n const out: string[] = [];\n if (process.env.AUTOPG_BIN) out.push(process.env.AUTOPG_BIN);\n out.push(\"autopg\");\n out.push(join(homedir(), \".local\", \"bin\", \"autopg\"));\n return out;\n}\n\nexport function resolveAutopgBin(): string | null {\n for (const bin of candidateBins()) {\n if (bin === \"autopg\") {\n const which = spawnSync(\"which\", [\"autopg\"], { encoding: \"utf8\" });\n if (which.status === 0 && which.stdout.trim()) return which.stdout.trim();\n continue;\n }\n if (existsSync(bin)) return bin;\n }\n return null;\n}\n\nexport function requireAutopgBin(): string {\n const bin = resolveAutopgBin();\n if (!bin) {\n throw new Error(INSTALL_HINT);\n }\n return bin;\n}\n\n/**\n * Parse `autopg status --json` output. Requires a numeric port and running !== false.\n */\nexport function parseHostStatus(json: string): { port: number } {\n let parsed: { port?: unknown; running?: unknown };\n try {\n parsed = JSON.parse(json) as { port?: unknown; running?: unknown };\n } catch {\n throw new Error(`autopg status --json returned invalid JSON.\\n${INSTALL_HINT}`);\n }\n if (typeof parsed.port !== \"number\") {\n throw new Error(`autopg status --json missing numeric port.\\n${INSTALL_HINT}`);\n }\n if (parsed.running === false) {\n throw new Error(`autopg host is not running.\\n${INSTALL_HINT}`);\n }\n return { port: parsed.port };\n}\n\nfunction adminUrlFor(port: number): string {\n return `postgresql://postgres:postgres@127.0.0.1:${port}/postgres`;\n}\n\n/**\n * Discover a live autopg host via `autopg status --json`. Throws if the host is not proven live.\n */\nexport function discoverHost(bin = requireAutopgBin()): AutopgDiscovery {\n let status: string;\n try {\n status = execFileSync(bin, [\"status\", \"--json\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to query autopg status.\\n${detail}\\n${INSTALL_HINT}`);\n }\n const { port } = parseHostStatus(status);\n return { port, adminUrl: adminUrlFor(port), bin };\n}\n\n/**\n * Ensure the host postmaster is up. Runs `autopg install` only after status fails, then re-discovers.\n */\nexport function ensureHostRunning(bin = requireAutopgBin()): AutopgDiscovery {\n try {\n return discoverHost(bin);\n } catch {\n // status failed — attempt install, then require a successful rediscovery\n }\n\n const install = spawnSync(bin, [\"install\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (install.status !== 0) {\n throw new Error(\n `Failed to start autopg host (exit ${install.status}).\\n` +\n `${install.stderr || install.stdout || \"\"}\\n${INSTALL_HINT}`,\n );\n }\n return discoverHost(bin);\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction quoteLiteral(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Deterministic local-only password for an app role (Prisma/TCP need it;\n * autopg hba uses `password` for 127.0.0.1).\n */\nexport function rolePasswordFor(databaseName: string): string {\n return createHash(\"sha256\")\n .update(`${PASSWORD_SALT_PREFIX}\\0${databaseName}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n/**\n * Idempotently CREATE ROLE + CREATE DATABASE with cedar-pg owned names.\n */\nexport async function ensureDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n password?: string;\n}): Promise<void> {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n const roleExists = await client.query(\"SELECT 1 FROM pg_roles WHERE rolname = $1\", [\n opts.roleName,\n ]);\n if (roleExists.rowCount === 0) {\n await client.query(\n `CREATE ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n } else {\n await client.query(\n `ALTER ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n }\n\n const dbExists = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [\n opts.databaseName,\n ]);\n if (dbExists.rowCount === 0) {\n await client.query(\n `CREATE DATABASE ${quoteIdent(opts.databaseName)} OWNER ${quoteIdent(opts.roleName)}`,\n );\n } else {\n await client.query(\n `ALTER DATABASE ${quoteIdent(opts.databaseName)} OWNER TO ${quoteIdent(opts.roleName)}`,\n );\n }\n } finally {\n await client.end();\n }\n}\n\n/**\n * DROP DATABASE (force terminate backends) + DROP ROLE.\n */\nexport async function dropDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n}): Promise<void> {\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n await client.query(\n `\n SELECT pg_terminate_backend(pid)\n FROM pg_stat_activity\n WHERE datname = $1 AND pid <> pg_backend_pid()\n `,\n [opts.databaseName],\n );\n await client.query(`DROP DATABASE IF EXISTS ${quoteIdent(opts.databaseName)}`);\n const owns = await client.query(\n `SELECT 1 FROM pg_database WHERE datdba = (SELECT oid FROM pg_roles WHERE rolname = $1) LIMIT 1`,\n [opts.roleName],\n );\n if (owns.rowCount === 0) {\n await client.query(`DROP ROLE IF EXISTS ${quoteIdent(opts.roleName)}`);\n }\n } finally {\n await client.end();\n }\n}\n\nexport function buildDatabaseUrl(opts: {\n port: number;\n databaseName: string;\n roleName: string;\n password?: string;\n}): string {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const user = encodeURIComponent(opts.roleName);\n const pass = encodeURIComponent(password);\n return `postgresql://${user}:${pass}@127.0.0.1:${opts.port}/${opts.databaseName}`;\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { buildDatabaseName, buildRoleName, type DbMode } from \"./naming.ts\";\nimport {\n forgetLease,\n isOrphanLease,\n leaseDir,\n listRegistryLeases,\n readLease,\n writeLease,\n type Lease,\n} from \"./lease.ts\";\nimport { resolveEnsureSkip, type ResolveEnsureSkipInput } from \"./policy.ts\";\nimport { resolveWorktreeIdentity } from \"./worktree.ts\";\nimport {\n buildDatabaseUrl,\n dropDatabase,\n ensureDatabase,\n ensureHostRunning,\n} from \"../providers/autopg.ts\";\n\nfunction writeEnvFile(root: string, mode: DbMode, databaseUrl: string): void {\n const dir = leaseDir(root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n let body = `DATABASE_URL=${databaseUrl}\\n`;\n if (mode === \"test\") body += `TEST_DATABASE_URL=${databaseUrl}\\n`;\n writeFileSync(join(dir, `${mode}.env`), body, { mode: 0o600 });\n}\n\nexport function urlFromLease(lease: Lease): string {\n return buildDatabaseUrl({\n port: lease.port,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n}\n\n/**\n * DROP the database, then forget lease/registry. Never forget without a successful DROP.\n */\nasync function dropThenForget(lease: Lease, adminUrl: string): Promise<void> {\n await dropDatabase({\n adminUrl,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n forgetLease(lease);\n}\n\nexport type EnsureOptions = {\n root?: string;\n mode: DbMode;\n /** Inject DATABASE_URL / TEST_DATABASE_URL into process.env (default true). */\n setEnv?: boolean;\n};\n\nexport type EnsureResult = {\n databaseUrl: string;\n databaseName: string;\n roleName: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n root: string;\n mode: DbMode;\n port: number;\n dispose: () => Promise<void>;\n};\n\n/**\n * Ensure a worktree-scoped database exists and return connection info.\n *\n * - `dev`: keep DB across restarts.\n * - `test`: DROP when `dispose()` is awaited (callers / test runners own teardown).\n */\nexport async function ensure(options: EnsureOptions): Promise<EnsureResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode;\n const databaseName = buildDatabaseName(identity, mode);\n const roleName = buildRoleName(databaseName);\n\n const host = ensureHostRunning();\n await ensureDatabase({\n adminUrl: host.adminUrl,\n databaseName,\n roleName,\n });\n\n const databaseUrl = buildDatabaseUrl({\n port: host.port,\n databaseName,\n roleName,\n });\n\n const lease: Lease = {\n schemaVersion: 1,\n mode,\n root: identity.root,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n databaseName,\n roleName,\n port: host.port,\n pid: process.pid,\n createdAt: new Date().toISOString(),\n };\n writeLease(lease);\n writeEnvFile(identity.root, mode, databaseUrl);\n\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = databaseUrl;\n if (mode === \"test\") {\n process.env.TEST_DATABASE_URL = databaseUrl;\n }\n }\n\n const disposeFn = async () => {\n await dispose({ root: identity.root, mode });\n };\n\n return {\n databaseUrl,\n databaseName,\n roleName,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n root: identity.root,\n mode,\n port: host.port,\n dispose: disposeFn,\n };\n}\n\nexport type EnsureIfNeededOptions = EnsureOptions & ResolveEnsureSkipInput;\n\nexport type EnsureIfNeededResult =\n | { status: \"skipped\"; reason: \"disabled\" }\n | { status: \"skipped\"; reason: \"external-url\"; databaseUrl: string }\n | ({ status: \"ensured\" } & EnsureResult);\n\n/**\n * Resolve skip policy then ensure. Single entry for hosts (Cedar CLI, Jest, Vitest).\n * On external-url skip, sets DATABASE_URL when `setEnv` is not false.\n */\nexport async function ensureIfNeeded(\n options: EnsureIfNeededOptions,\n): Promise<EnsureIfNeededResult> {\n const skip = resolveEnsureSkip({\n url: options.url,\n force: options.force,\n disabled: options.disabled,\n });\n if (skip.skip) {\n if (skip.reason === \"external-url\") {\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = skip.databaseUrl;\n }\n return { status: \"skipped\", reason: \"external-url\", databaseUrl: skip.databaseUrl };\n }\n return { status: \"skipped\", reason: \"disabled\" };\n }\n\n const result = await ensure({\n root: options.root,\n mode: options.mode,\n setEnv: options.setEnv,\n });\n return { status: \"ensured\", ...result };\n}\n\nexport type DisposeOptions = {\n root?: string;\n mode?: DbMode;\n};\n\nexport type DisposeResult =\n | { dropped: true; databaseName: string }\n | { dropped: false; reason: \"no-lease\" | \"host-unavailable\" };\n\n/**\n * DROP the worktree database for the given mode (default: test).\n * No-ops without a valid lease (never invents a name to DROP).\n * If the host is unavailable, leaves the lease so dispose/gc can retry.\n */\nexport async function dispose(options: DisposeOptions = {}): Promise<DisposeResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode ?? \"test\";\n const lease = readLease(identity.root, mode);\n if (!lease) return { dropped: false, reason: \"no-lease\" };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n // Keep lease + registry so a later dispose/gc can still find the DB.\n return { dropped: false, reason: \"host-unavailable\" };\n }\n\n await dropThenForget(lease, host.adminUrl);\n return { dropped: true, databaseName: lease.databaseName };\n}\n\n/**\n * Drop databases whose registered worktree root no longer exists on disk.\n * Registry entries are removed only after a successful DROP.\n */\nexport async function gc(): Promise<{\n dropped: string[];\n}> {\n const dropped: string[] = [];\n const orphans = listRegistryLeases().filter(isOrphanLease);\n if (orphans.length === 0) return { dropped };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n return { dropped };\n }\n\n for (const lease of orphans) {\n try {\n await dropThenForget(lease, host.adminUrl);\n dropped.push(lease.databaseName);\n } catch {\n // Keep registry entry for retry\n }\n }\n return { dropped };\n}\n"],"mappings":"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"}
|
package/dist/nx.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_constants = require("./constants-
|
|
3
|
-
const require_tasks = require("./tasks-
|
|
2
|
+
const require_constants = require("./constants-CNZn5Xro.cjs");
|
|
3
|
+
const require_tasks = require("./tasks-B8ryV9xo.cjs");
|
|
4
4
|
//#region src/adapters/nx.ts
|
|
5
5
|
/**
|
|
6
6
|
* Nx target command hints. Wire into project.json / package.json:
|
|
@@ -17,7 +17,7 @@ const require_tasks = require("./tasks-DZ7hMmGJ.cjs");
|
|
|
17
17
|
* }
|
|
18
18
|
* ```
|
|
19
19
|
*
|
|
20
|
-
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
20
|
+
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;
|
|
21
21
|
* there is no separate Nx runtime wrapper.
|
|
22
22
|
*/
|
|
23
23
|
/** Suggested target definitions for project.json / package.json nx targets. */
|
package/dist/nx.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nx.cjs","names":["CLI_NAME","cedarPgCommands"],"sources":["../src/adapters/nx.ts"],"sourcesContent":["/**\n * Nx target command hints. Wire into project.json / package.json:\n *\n * ```json\n * {\n * \"targets\": {\n * \"db:ensure\": { \"command\": \"cedarpg ensure --mode=dev\" },\n * \"test\": {\n * \"dependsOn\": [\"db:ensure-test\"],\n * \"command\": \"vitest run\"\n * }\n * }\n * }\n * ```\n *\n * Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
1
|
+
{"version":3,"file":"nx.cjs","names":["CLI_NAME","cedarPgCommands"],"sources":["../src/adapters/nx.ts"],"sourcesContent":["/**\n * Nx target command hints. Wire into project.json / package.json:\n *\n * ```json\n * {\n * \"targets\": {\n * \"db:ensure\": { \"command\": \"cedarpg ensure --mode=dev\" },\n * \"test\": {\n * \"dependsOn\": [\"db:ensure-test\"],\n * \"command\": \"vitest run\"\n * }\n * }\n * }\n * ```\n *\n * Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;\n * there is no separate Nx runtime wrapper.\n */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\nimport { cedarPgCommands } from \"./tasks.ts\";\n\n/** Suggested target definitions for project.json / package.json nx targets. */\nexport function nxTargetHints(bin = CLI_NAME): Record<string, { command: string }> {\n const cmds = cedarPgCommands(bin);\n return {\n \"db:ensure\": { command: cmds.ensureDev },\n \"db:ensure-test\": { command: cmds.ensureTest },\n \"db:dispose-test\": { command: cmds.disposeTest },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,cAAc,MAAMA,kBAAAA,UAA+C;CACjF,MAAM,OAAOC,cAAAA,gBAAgB,GAAG;CAChC,OAAO;EACL,aAAa,EAAE,SAAS,KAAK,UAAU;EACvC,kBAAkB,EAAE,SAAS,KAAK,WAAW;EAC7C,mBAAmB,EAAE,SAAS,KAAK,YAAY;CACjD;AACF"}
|
package/dist/nx.d.cts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* }
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
|
-
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
17
|
+
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;
|
|
18
18
|
* there is no separate Nx runtime wrapper.
|
|
19
19
|
*/
|
|
20
20
|
/** Suggested target definitions for project.json / package.json nx targets. */
|
package/dist/nx.d.mts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* }
|
|
15
15
|
* ```
|
|
16
16
|
*
|
|
17
|
-
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
17
|
+
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;
|
|
18
18
|
* there is no separate Nx runtime wrapper.
|
|
19
19
|
*/
|
|
20
20
|
/** Suggested target definitions for project.json / package.json nx targets. */
|
package/dist/nx.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as CLI_NAME } from "./constants-
|
|
2
|
-
import { t as cedarPgCommands } from "./tasks-
|
|
1
|
+
import { t as CLI_NAME } from "./constants-BY97wXjA.mjs";
|
|
2
|
+
import { t as cedarPgCommands } from "./tasks-13P6tMth.mjs";
|
|
3
3
|
//#region src/adapters/nx.ts
|
|
4
4
|
/**
|
|
5
5
|
* Nx target command hints. Wire into project.json / package.json:
|
|
@@ -16,7 +16,7 @@ import { t as cedarPgCommands } from "./tasks-CW2D0n46.mjs";
|
|
|
16
16
|
* }
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
|
-
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
19
|
+
* Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;
|
|
20
20
|
* there is no separate Nx runtime wrapper.
|
|
21
21
|
*/
|
|
22
22
|
/** Suggested target definitions for project.json / package.json nx targets. */
|
package/dist/nx.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nx.mjs","names":[],"sources":["../src/adapters/nx.ts"],"sourcesContent":["/**\n * Nx target command hints. Wire into project.json / package.json:\n *\n * ```json\n * {\n * \"targets\": {\n * \"db:ensure\": { \"command\": \"cedarpg ensure --mode=dev\" },\n * \"test\": {\n * \"dependsOn\": [\"db:ensure-test\"],\n * \"command\": \"vitest run\"\n * }\n * }\n * }\n * ```\n *\n * Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose
|
|
1
|
+
{"version":3,"file":"nx.mjs","names":[],"sources":["../src/adapters/nx.ts"],"sourcesContent":["/**\n * Nx target command hints. Wire into project.json / package.json:\n *\n * ```json\n * {\n * \"targets\": {\n * \"db:ensure\": { \"command\": \"cedarpg ensure --mode=dev\" },\n * \"test\": {\n * \"dependsOn\": [\"db:ensure-test\"],\n * \"command\": \"vitest run\"\n * }\n * }\n * }\n * ```\n *\n * Prefer the CLI (`cedarpg` from `@cedarjs/pg`) for ensure/dispose;\n * there is no separate Nx runtime wrapper.\n */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\nimport { cedarPgCommands } from \"./tasks.ts\";\n\n/** Suggested target definitions for project.json / package.json nx targets. */\nexport function nxTargetHints(bin = CLI_NAME): Record<string, { command: string }> {\n const cmds = cedarPgCommands(bin);\n return {\n \"db:ensure\": { command: cmds.ensureDev },\n \"db:ensure-test\": { command: cmds.ensureTest },\n \"db:dispose-test\": { command: cmds.disposeTest },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,cAAc,MAAM,UAA+C;CACjF,MAAM,OAAO,gBAAgB,GAAG;CAChC,OAAO;EACL,aAAa,EAAE,SAAS,KAAK,UAAU;EACvC,kBAAkB,EAAE,SAAS,KAAK,WAAW;EAC7C,mBAAmB,EAAE,SAAS,KAAK,YAAY;CACjD;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as CLI_NAME } from "./constants-
|
|
1
|
+
import { t as CLI_NAME } from "./constants-BY97wXjA.mjs";
|
|
2
2
|
//#region src/adapters/tasks.ts
|
|
3
3
|
/**
|
|
4
4
|
* Shared CLI command strings for Vite+ / Nx task wiring.
|
|
@@ -13,4 +13,4 @@ function cedarPgCommands(bin = CLI_NAME) {
|
|
|
13
13
|
//#endregion
|
|
14
14
|
export { cedarPgCommands as t };
|
|
15
15
|
|
|
16
|
-
//# sourceMappingURL=tasks-
|
|
16
|
+
//# sourceMappingURL=tasks-13P6tMth.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-
|
|
1
|
+
{"version":3,"file":"tasks-13P6tMth.mjs","names":[],"sources":["../src/adapters/tasks.ts"],"sourcesContent":["/**\n * Shared CLI command strings for Vite+ / Nx task wiring.\n */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\n\nexport type CedarPgCommands = {\n ensureDev: string;\n ensureTest: string;\n disposeTest: string;\n};\n\nexport function cedarPgCommands(bin = CLI_NAME): CedarPgCommands {\n return {\n ensureDev: `${bin} ensure --mode=dev`,\n ensureTest: `${bin} ensure --mode=test --print-env`,\n disposeTest: `${bin} dispose --mode=test`,\n };\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBAAgB,MAAM,UAA2B;CAC/D,OAAO;EACL,WAAW,GAAG,IAAI;EAClB,YAAY,GAAG,IAAI;EACnB,aAAa,GAAG,IAAI;CACtB;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_constants = require("./constants-
|
|
1
|
+
const require_constants = require("./constants-CNZn5Xro.cjs");
|
|
2
2
|
//#region src/adapters/tasks.ts
|
|
3
3
|
/**
|
|
4
4
|
* Shared CLI command strings for Vite+ / Nx task wiring.
|
|
@@ -18,4 +18,4 @@ Object.defineProperty(exports, "cedarPgCommands", {
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
//# sourceMappingURL=tasks-
|
|
21
|
+
//# sourceMappingURL=tasks-B8ryV9xo.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-
|
|
1
|
+
{"version":3,"file":"tasks-B8ryV9xo.cjs","names":["CLI_NAME"],"sources":["../src/adapters/tasks.ts"],"sourcesContent":["/**\n * Shared CLI command strings for Vite+ / Nx task wiring.\n */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\n\nexport type CedarPgCommands = {\n ensureDev: string;\n ensureTest: string;\n disposeTest: string;\n};\n\nexport function cedarPgCommands(bin = CLI_NAME): CedarPgCommands {\n return {\n ensureDev: `${bin} ensure --mode=dev`,\n ensureTest: `${bin} ensure --mode=test --print-env`,\n disposeTest: `${bin} dispose --mode=test`,\n };\n}\n"],"mappings":";;;;;AAYA,SAAgB,gBAAgB,MAAMA,kBAAAA,UAA2B;CAC/D,OAAO;EACL,WAAW,GAAG,IAAI;EAClB,YAAY,GAAG,IAAI;EACnB,aAAa,GAAG,IAAI;CACtB;AACF"}
|
package/dist/vite-plus.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./constants-
|
|
3
|
-
const require_tasks = require("./tasks-
|
|
2
|
+
require("./constants-CNZn5Xro.cjs");
|
|
3
|
+
const require_tasks = require("./tasks-B8ryV9xo.cjs");
|
|
4
4
|
//#region src/adapters/vite-plus.ts
|
|
5
5
|
/**
|
|
6
6
|
* Consumer adapter for external Vite+ projects.
|
package/dist/vite-plus.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./constants-
|
|
2
|
-
import { t as cedarPgCommands } from "./tasks-
|
|
1
|
+
import "./constants-BY97wXjA.mjs";
|
|
2
|
+
import { t as cedarPgCommands } from "./tasks-13P6tMth.mjs";
|
|
3
3
|
//#region src/adapters/vite-plus.ts
|
|
4
4
|
/**
|
|
5
5
|
* Consumer adapter for external Vite+ projects.
|
package/dist/vitest.cjs
CHANGED
|
@@ -2,10 +2,10 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_lifecycle = require("./lifecycle-
|
|
5
|
+
const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
|
|
6
6
|
//#region src/adapters/vitest.ts
|
|
7
7
|
/**
|
|
8
|
-
* Vitest globalSetup
|
|
8
|
+
* Vitest globalSetup: ensure test DB, return teardown that disposes it.
|
|
9
9
|
*
|
|
10
10
|
* vitest.config.ts:
|
|
11
11
|
* ```ts
|
package/dist/vitest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.cjs","names":["ensureIfNeeded"],"sources":["../src/adapters/vitest.ts"],"sourcesContent":["import { ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Vitest globalSetup
|
|
1
|
+
{"version":3,"file":"vitest.cjs","names":["ensureIfNeeded"],"sources":["../src/adapters/vitest.ts"],"sourcesContent":["import { ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Vitest globalSetup: ensure test DB, return teardown that disposes it.\n *\n * vitest.config.ts:\n * ```ts\n * export default defineConfig({\n * test: {\n * globalSetup: ['@cedarjs/pg/vitest'],\n * },\n * })\n * ```\n */\nexport async function setup(): Promise<() => Promise<void>> {\n const result = await ensureIfNeeded({\n mode: \"test\",\n setEnv: true,\n });\n if (result.status !== \"ensured\") {\n return async () => {};\n }\n return async () => {\n await result.dispose();\n };\n}\n\nexport default setup;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,eAAsB,QAAsC;CAC1D,MAAM,SAAS,MAAMA,kBAAAA,eAAe;EAClC,MAAM;EACN,QAAQ;CACV,CAAC;CACD,IAAI,OAAO,WAAW,WACpB,OAAO,YAAY,CAAC;CAEtB,OAAO,YAAY;EACjB,MAAM,OAAO,QAAQ;CACvB;AACF"}
|
package/dist/vitest.d.cts
CHANGED
package/dist/vitest.d.mts
CHANGED
package/dist/vitest.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { r as ensureIfNeeded } from "./lifecycle-
|
|
1
|
+
import { r as ensureIfNeeded } from "./lifecycle-CT_8AWPv.mjs";
|
|
2
2
|
//#region src/adapters/vitest.ts
|
|
3
3
|
/**
|
|
4
|
-
* Vitest globalSetup
|
|
4
|
+
* Vitest globalSetup: ensure test DB, return teardown that disposes it.
|
|
5
5
|
*
|
|
6
6
|
* vitest.config.ts:
|
|
7
7
|
* ```ts
|
package/dist/vitest.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest.mjs","names":[],"sources":["../src/adapters/vitest.ts"],"sourcesContent":["import { ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Vitest globalSetup
|
|
1
|
+
{"version":3,"file":"vitest.mjs","names":[],"sources":["../src/adapters/vitest.ts"],"sourcesContent":["import { ensureIfNeeded } from \"../core/lifecycle.ts\";\n\n/**\n * Vitest globalSetup: ensure test DB, return teardown that disposes it.\n *\n * vitest.config.ts:\n * ```ts\n * export default defineConfig({\n * test: {\n * globalSetup: ['@cedarjs/pg/vitest'],\n * },\n * })\n * ```\n */\nexport async function setup(): Promise<() => Promise<void>> {\n const result = await ensureIfNeeded({\n mode: \"test\",\n setEnv: true,\n });\n if (result.status !== \"ensured\") {\n return async () => {};\n }\n return async () => {\n await result.dispose();\n };\n}\n\nexport default setup;\n"],"mappings":";;;;;;;;;;;;;;AAcA,eAAsB,QAAsC;CAC1D,MAAM,SAAS,MAAM,eAAe;EAClC,MAAM;EACN,QAAQ;CACV,CAAC;CACD,IAAI,OAAO,WAAW,WACpB,OAAO,YAAY,CAAC;CAEtB,OAAO,YAAY;EACjB,MAAM,OAAO,QAAQ;CACvB;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarjs/pg",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
4
|
-
"description": "Worktree-isolated local Postgres for Vite
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Worktree-isolated local Postgres for Vite+, Nx, and CedarJS (via autopg)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"autopg",
|
|
7
7
|
"cedar",
|
package/scripts/postinstall.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Ensure the autopg host binary is available after @cedarjs/pg install.
|
|
4
4
|
* Idempotent across npm / yarn / pnpm. Never fails the parent install hard
|
|
5
|
-
* when network is unavailable
|
|
5
|
+
* when network is unavailable; prints a clear warning instead.
|
|
6
6
|
*
|
|
7
7
|
* Supply-chain: both the install script ref and the binary release are pinned.
|
|
8
8
|
* Bump AUTOPG_VERSION (and the matching tag in INSTALL_URL) deliberately when
|
|
9
|
-
* upgrading
|
|
9
|
+
* upgrading; do not track main/latest.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { spawnSync } from "node:child_process";
|
|
@@ -34,16 +34,16 @@ function main() {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// Skip in CI unless explicitly requested
|
|
37
|
+
// Skip in CI unless explicitly requested; CI images often bake autopg
|
|
38
38
|
if (process.env.CI === "true" && process.env.CEDAR_PG_INSTALL_AUTOPG !== "1") {
|
|
39
39
|
console.warn(
|
|
40
|
-
"[cedarpg] autopg not found; CI detected
|
|
40
|
+
"[cedarpg] autopg not found; CI detected, skip auto-install. " +
|
|
41
41
|
"Set CEDAR_PG_INSTALL_AUTOPG=1 or install autopg in the image.",
|
|
42
42
|
);
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
console.log(`[cedarpg] autopg not found
|
|
46
|
+
console.log(`[cedarpg] autopg not found; installing ${AUTOPG_VERSION} via pinned install.sh...`);
|
|
47
47
|
const result = spawnSync("bash", ["-c", `curl -fsSL ${INSTALL_URL} | bash`], {
|
|
48
48
|
stdio: "inherit",
|
|
49
49
|
env: { ...process.env, AUTOPG_VERSION },
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants-B7sCf4fg.cjs","names":[],"sources":["../src/core/constants.ts"],"sourcesContent":["/**\n * Product identity vs frozen crypto.\n *\n * State dirs follow the CLI name (`.cedarpg`) — product-owned, not nested under\n * autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).\n * Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.\n */\n\n/** User-facing CLI binary (`package.json` bin) and log prefix */\nexport const CLI_NAME = \"cedarpg\";\n\n/**\n * Worktree-local state dir and home-registry parent (`~/.cedarpg/registry`).\n * Frozen for lease/gc discovery after first alpha consumers appear.\n */\nexport const STATE_DIRNAME = \".cedarpg\";\n\n/**\n * Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\\0` + databaseName).\n * Opaque — independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.\n */\nexport const PASSWORD_SALT_PREFIX = \"cedar-pg\";\n"],"mappings":";;;;;;;;;AASA,MAAa,WAAW;;;;;AAMxB,MAAa,gBAAgB;;;;;AAM7B,MAAa,uBAAuB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants-YnHDwShD.mjs","names":[],"sources":["../src/core/constants.ts"],"sourcesContent":["/**\n * Product identity vs frozen crypto.\n *\n * State dirs follow the CLI name (`.cedarpg`) — product-owned, not nested under\n * autopg's `~/.autopg/` (host owns that) and not a generic `.pg` (collision-prone).\n * Password salt is an opaque crypto constant; bump ROLE_PASSWORD_SCHEME to change it.\n */\n\n/** User-facing CLI binary (`package.json` bin) and log prefix */\nexport const CLI_NAME = \"cedarpg\";\n\n/**\n * Worktree-local state dir and home-registry parent (`~/.cedarpg/registry`).\n * Frozen for lease/gc discovery after first alpha consumers appear.\n */\nexport const STATE_DIRNAME = \".cedarpg\";\n\n/**\n * Password scheme v1 salt prefix: sha256(`${PASSWORD_SALT_PREFIX}\\0` + databaseName).\n * Opaque — independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.\n */\nexport const PASSWORD_SALT_PREFIX = \"cedar-pg\";\n"],"mappings":";;;;;;;;;AASA,MAAa,WAAW;;;;;AAMxB,MAAa,gBAAgB;;;;;AAM7B,MAAa,uBAAuB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-D10usEjM.cjs","names":["STATE_DIRNAME","PASSWORD_SALT_PREFIX"],"sources":["../src/core/naming.ts","../src/core/lease.ts","../src/core/policy.ts","../src/core/worktree.ts","../src/providers/autopg.ts","../src/core/lifecycle.ts"],"sourcesContent":["import type { WorktreeIdentity } from \"./worktree.ts\";\n\nexport type DbMode = \"dev\" | \"test\";\n\nconst PG_MAX = 63;\nconst PREFIX = \"cpg_\";\n\n/**\n * Build an observable Postgres database name:\n * cpg_<repoSlug>_<worktreeSlug>_<mode>_<pathHash8>\n *\n * Never drops `mode` or `pathHash`. Truncates slugs to fit ≤63 chars.\n */\nexport function buildDatabaseName(identity: WorktreeIdentity, mode: DbMode): string {\n const modePart = mode;\n const hashPart = identity.pathHash;\n // PREFIX + slug + '_' + slug + '_' + mode + '_' + hash\n // fixed: PREFIX(4) + 2 underscores around mode/hash segments + mode + hash\n // layout: cpg_<repo>_<wt>_<mode>_<hash>\n const fixed =\n PREFIX.length +\n 1 + // after repo\n 1 + // after worktree\n modePart.length +\n 1 +\n hashPart.length;\n\n let budget = PG_MAX - fixed;\n if (budget < 2) {\n // Extreme truncation: keep mode+hash, minimal slugs\n return `${PREFIX}${modePart}_${hashPart}`.slice(0, PG_MAX);\n }\n\n let repo = identity.repoSlug;\n let wt = identity.worktreeSlug;\n // Split budget roughly evenly, preferring to keep both readable\n while (repo.length + wt.length > budget) {\n if (repo.length >= wt.length && repo.length > 1) {\n repo = repo.slice(0, -1);\n } else if (wt.length > 1) {\n wt = wt.slice(0, -1);\n } else {\n break;\n }\n }\n\n const name = `${PREFIX}${repo}_${wt}_${modePart}_${hashPart}`;\n if (name.length > PG_MAX) {\n return name.slice(0, PG_MAX);\n }\n return name;\n}\n\nexport function buildRoleName(databaseName: string): string {\n const suffix = \"_role\";\n const maxBase = PG_MAX - suffix.length;\n const base = databaseName.slice(0, maxBase);\n return `${base}${suffix}`;\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { STATE_DIRNAME } from \"./constants.ts\";\nimport type { DbMode } from \"./naming.ts\";\n\nexport type Lease = {\n schemaVersion: 1;\n mode: DbMode;\n root: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n databaseName: string;\n roleName: string;\n port: number;\n pid: number;\n createdAt: string;\n};\n\nfunction isDbMode(value: unknown): value is DbMode {\n return value === \"dev\" || value === \"test\";\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/** Narrow-parse a lease; reject incomplete or wrong-version records. */\nexport function parseLease(raw: unknown): Lease | null {\n if (!raw || typeof raw !== \"object\") return null;\n const o = raw as Record<string, unknown>;\n if (o.schemaVersion !== 1) return null;\n if (!isDbMode(o.mode)) return null;\n if (!isNonEmptyString(o.root)) return null;\n if (!isNonEmptyString(o.repoSlug)) return null;\n if (!isNonEmptyString(o.worktreeSlug)) return null;\n if (!isNonEmptyString(o.pathHash)) return null;\n if (!isNonEmptyString(o.databaseName)) return null;\n if (!isNonEmptyString(o.roleName)) return null;\n if (typeof o.port !== \"number\") return null;\n if (typeof o.pid !== \"number\") return null;\n if (!isNonEmptyString(o.createdAt)) return null;\n return {\n schemaVersion: 1,\n mode: o.mode,\n root: o.root,\n repoSlug: o.repoSlug,\n worktreeSlug: o.worktreeSlug,\n pathHash: o.pathHash,\n databaseName: o.databaseName,\n roleName: o.roleName,\n port: o.port,\n pid: o.pid,\n createdAt: o.createdAt,\n };\n}\n\nexport function leaseDir(root: string): string {\n return join(root, STATE_DIRNAME);\n}\n\nexport function leasePath(root: string, mode: DbMode): string {\n return join(leaseDir(root), `${mode}.json`);\n}\n\n/** Durable registry outside worktrees so GC can find orphans after a checkout is deleted. */\nexport function registryDir(): string {\n return process.env.CEDAR_PG_REGISTRY_DIR || join(homedir(), STATE_DIRNAME, \"registry\");\n}\n\nfunction registryPath(databaseName: string): string {\n return join(registryDir(), `${databaseName}.json`);\n}\n\nfunction writeJsonAtomic(file: string, value: unknown): void {\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.tmp.${process.pid}`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n}\n\nfunction registerLease(lease: Lease): void {\n writeJsonAtomic(registryPath(lease.databaseName), lease);\n}\n\nfunction unregisterLease(databaseName: string): void {\n try {\n unlinkSync(registryPath(databaseName));\n } catch {\n // missing is fine\n }\n}\n\nfunction peekDatabaseName(root: string, mode: DbMode): string | undefined {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return undefined;\n try {\n const raw = JSON.parse(readFileSync(file, \"utf8\")) as { databaseName?: unknown };\n return typeof raw.databaseName === \"string\" && raw.databaseName ? raw.databaseName : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function readLease(root: string, mode: DbMode): Lease | null {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return null;\n try {\n return parseLease(JSON.parse(readFileSync(file, \"utf8\")));\n } catch {\n return null;\n }\n}\n\n/**\n * Persist lease. Registry is written first (GC source of truth), then the worktree cache.\n */\nexport function writeLease(lease: Lease): void {\n registerLease(lease);\n const dir = leaseDir(lease.root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeJsonAtomic(leasePath(lease.root, lease.mode), lease);\n}\n\n/**\n * Remove registry entry by databaseName and best-effort delete the worktree lease file.\n * Call only after a successful DROP (or when intentionally abandoning tracking).\n */\nexport function forgetLease(lease: Pick<Lease, \"root\" | \"mode\" | \"databaseName\">): void {\n unregisterLease(lease.databaseName);\n try {\n unlinkSync(leasePath(lease.root, lease.mode));\n } catch {\n // root or file may already be gone\n }\n}\n\n/**\n * Clear worktree lease + registry. Unregisters by databaseName even when the local\n * file fails full parse (peeks databaseName from raw JSON).\n */\nexport function clearLease(root: string, mode: DbMode): void {\n const existing = readLease(root, mode);\n if (existing) {\n forgetLease(existing);\n return;\n }\n const databaseName = peekDatabaseName(root, mode);\n try {\n unlinkSync(leasePath(root, mode));\n } catch {\n // missing is fine\n }\n if (databaseName) {\n unregisterLease(databaseName);\n }\n}\n\n/** All registry entries (used by gc). */\nexport function listRegistryLeases(): Lease[] {\n const dir = registryDir();\n if (!existsSync(dir)) return [];\n const out: Lease[] = [];\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(\".json\")) continue;\n try {\n const lease = parseLease(JSON.parse(readFileSync(join(dir, name), \"utf8\")));\n if (lease) out.push(lease);\n } catch {\n // skip corrupt\n }\n }\n return out;\n}\n\nexport function isOrphanLease(lease: Lease): boolean {\n return !existsSync(lease.root);\n}\n","/**\n * Shared ensure skip policy for test-runner adapters and Cedar CLI bridges.\n */\n\nexport type EnsureSkip =\n | { skip: false }\n | { skip: true; reason: \"disabled\" }\n | { skip: true; reason: \"external-url\"; databaseUrl: string };\n\nexport type ResolveEnsureSkipInput = {\n /** Candidate URL (TEST_DATABASE_URL for tests, DATABASE_URL for dev). */\n url?: string;\n /** When true, never skip (CEDAR_PG_FORCE=1). */\n force?: boolean;\n /**\n * When true, skip ensure entirely.\n * Defaults from CEDAR_PG=0|false (opt-out adapters). Pass `false` for Cedar opt-in flows.\n */\n disabled?: boolean;\n};\n\n/**\n * True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).\n * These must never be treated as an external escape hatch — always re-ensure so\n * disposed/stale shell env cannot skip provisioning.\n */\nexport function isCedarPgManagedUrl(url: string | undefined): boolean {\n if (!url || url.startsWith(\"file:\")) return false;\n try {\n const parsed = new URL(url);\n const databaseName = decodeURIComponent(\n (parsed.pathname.replace(/^\\//, \"\").split(\"?\")[0] ?? \"\").trim(),\n );\n const user = decodeURIComponent(parsed.username);\n return databaseName.startsWith(\"cpg_\") || user.startsWith(\"cpg_\");\n } catch {\n return false;\n }\n}\n\n/**\n * True when `url` is a real external database and ensure should be skipped.\n * Sqlite `file:` URLs and cedarpg `cpg_*` URLs are not external.\n */\nexport function isExternalDatabaseEscapeHatch(url: string | undefined): boolean {\n if (!url) return false;\n if (url.startsWith(\"file:\")) return false;\n if (isCedarPgManagedUrl(url)) return false;\n return true;\n}\n\n/**\n * Decide whether ensure should run.\n * Adapters may call with no args (env defaults). Cedar opt-in should pass `disabled: false`\n * and the relevant `url` (`DATABASE_URL` or `TEST_DATABASE_URL`).\n */\nexport function resolveEnsureSkip(input: ResolveEnsureSkipInput = {}): EnsureSkip {\n const disabled =\n input.disabled ?? (process.env.CEDAR_PG === \"0\" || process.env.CEDAR_PG === \"false\");\n if (disabled) {\n return { skip: true, reason: \"disabled\" };\n }\n\n const force = input.force ?? process.env.CEDAR_PG_FORCE === \"1\";\n if (force) {\n return { skip: false };\n }\n\n const url = input.url !== undefined ? input.url : process.env.TEST_DATABASE_URL;\n if (url && isExternalDatabaseEscapeHatch(url)) {\n return { skip: true, reason: \"external-url\", databaseUrl: url };\n }\n return { skip: false };\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\nexport type WorktreeIdentity = {\n /** Absolute path to the worktree / project root. */\n root: string;\n /** Basename of the git common dir / main checkout (e.g. `cedar`). */\n repoSlug: string;\n /** Basename of this worktree directory (e.g. `feat-auth`). */\n worktreeSlug: string;\n /** First 8 hex chars of sha256(absolute root). */\n pathHash: string;\n};\n\nfunction git(args: string[], cwd: string): string | null {\n try {\n return execFileSync(\"git\", args, {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return null;\n }\n}\n\nfunction sanitizeSlug(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n}\n\n/**\n * Resolve the project / worktree root.\n * Explicit `root` wins (app project path). Otherwise prefer git toplevel, then cwd.\n */\nexport function resolveRoot(root?: string): string {\n if (root) return resolve(root);\n const start = resolve(process.cwd());\n const toplevel = git([\"rev-parse\", \"--show-toplevel\"], start);\n if (toplevel) return toplevel;\n return start;\n}\n\n/**\n * Identify repo + worktree for observable DB naming.\n */\nexport function resolveWorktreeIdentity(root?: string): WorktreeIdentity {\n const absRoot = resolveRoot(root);\n const pathHash = createHash(\"sha256\").update(absRoot).digest(\"hex\").slice(0, 8);\n\n const commonDir = git([\"rev-parse\", \"--git-common-dir\"], absRoot);\n let repoSlug = sanitizeSlug(basename(absRoot));\n if (commonDir) {\n // common-dir is often `<main>/.git` or an absolute path to the main .git\n const commonAbs = resolve(absRoot, commonDir);\n // If commonAbs ends with `.git`, parent is main worktree root\n const mainRoot = basename(commonAbs) === \".git\" ? dirname(commonAbs) : commonAbs;\n // For linked worktrees, common-dir may be `.../main/.git`\n const candidate = basename(mainRoot) === \".git\" ? dirname(mainRoot) : mainRoot;\n const fromGit = sanitizeSlug(basename(candidate));\n if (fromGit) repoSlug = fromGit;\n }\n\n // Prefer gitdir worktree name when present (`.git` file pointing at worktrees/<name>)\n let worktreeSlug = sanitizeSlug(basename(absRoot));\n const gitFile = resolve(absRoot, \".git\");\n if (existsSync(gitFile)) {\n try {\n const content = readFileSync(gitFile, \"utf8\").trim();\n const match = content.match(/gitdir:\\s*(.+)$/m);\n if (match?.[1]) {\n const gitdir = resolve(absRoot, match[1].trim());\n // .../.git/worktrees/<name>\n const parts = gitdir.split(/[/\\\\]/);\n const wtIdx = parts.lastIndexOf(\"worktrees\");\n if (wtIdx >= 0 && parts[wtIdx + 1]) {\n const fromWt = sanitizeSlug(parts[wtIdx + 1]!);\n if (fromWt) worktreeSlug = fromWt;\n }\n }\n } catch {\n // keep basename slug\n }\n }\n\n if (!repoSlug) repoSlug = \"repo\";\n if (!worktreeSlug) worktreeSlug = \"wt\";\n\n return { root: absRoot, repoSlug, worktreeSlug, pathHash };\n}\n","import { createHash } from \"node:crypto\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport pg from \"pg\";\nimport { PASSWORD_SALT_PREFIX } from \"../core/constants.ts\";\n\nexport type AutopgDiscovery = {\n port: number;\n adminUrl: string;\n bin: string;\n};\n\nexport const INSTALL_HINT =\n \"autopg is required. Install with:\\n\" +\n \" curl -fsSL https://raw.githubusercontent.com/automagik-dev/autopg/main/install.sh | bash\\n\" +\n \"Then ensure ~/.local/bin is on PATH, or set AUTOPG_BIN.\";\n\n/** Password scheme v1: sha256(PASSWORD_SALT_PREFIX + \"\\\\0\" + databaseName) hex[:32]. Frozen for URL rebuild. */\nexport const ROLE_PASSWORD_SCHEME = \"v1\" as const;\n\nfunction candidateBins(): string[] {\n const out: string[] = [];\n if (process.env.AUTOPG_BIN) out.push(process.env.AUTOPG_BIN);\n out.push(\"autopg\");\n out.push(join(homedir(), \".local\", \"bin\", \"autopg\"));\n return out;\n}\n\nexport function resolveAutopgBin(): string | null {\n for (const bin of candidateBins()) {\n if (bin === \"autopg\") {\n const which = spawnSync(\"which\", [\"autopg\"], { encoding: \"utf8\" });\n if (which.status === 0 && which.stdout.trim()) return which.stdout.trim();\n continue;\n }\n if (existsSync(bin)) return bin;\n }\n return null;\n}\n\nexport function requireAutopgBin(): string {\n const bin = resolveAutopgBin();\n if (!bin) {\n throw new Error(INSTALL_HINT);\n }\n return bin;\n}\n\n/**\n * Parse `autopg status --json` output. Requires a numeric port and running !== false.\n */\nexport function parseHostStatus(json: string): { port: number } {\n let parsed: { port?: unknown; running?: unknown };\n try {\n parsed = JSON.parse(json) as { port?: unknown; running?: unknown };\n } catch {\n throw new Error(`autopg status --json returned invalid JSON.\\n${INSTALL_HINT}`);\n }\n if (typeof parsed.port !== \"number\") {\n throw new Error(`autopg status --json missing numeric port.\\n${INSTALL_HINT}`);\n }\n if (parsed.running === false) {\n throw new Error(`autopg host is not running.\\n${INSTALL_HINT}`);\n }\n return { port: parsed.port };\n}\n\nfunction adminUrlFor(port: number): string {\n return `postgresql://postgres:postgres@127.0.0.1:${port}/postgres`;\n}\n\n/**\n * Discover a live autopg host via `autopg status --json`. Throws if the host is not proven live.\n */\nexport function discoverHost(bin = requireAutopgBin()): AutopgDiscovery {\n let status: string;\n try {\n status = execFileSync(bin, [\"status\", \"--json\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to query autopg status.\\n${detail}\\n${INSTALL_HINT}`);\n }\n const { port } = parseHostStatus(status);\n return { port, adminUrl: adminUrlFor(port), bin };\n}\n\n/**\n * Ensure the host postmaster is up. Runs `autopg install` only after status fails, then re-discovers.\n */\nexport function ensureHostRunning(bin = requireAutopgBin()): AutopgDiscovery {\n try {\n return discoverHost(bin);\n } catch {\n // status failed — attempt install, then require a successful rediscovery\n }\n\n const install = spawnSync(bin, [\"install\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (install.status !== 0) {\n throw new Error(\n `Failed to start autopg host (exit ${install.status}).\\n` +\n `${install.stderr || install.stdout || \"\"}\\n${INSTALL_HINT}`,\n );\n }\n return discoverHost(bin);\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction quoteLiteral(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Deterministic local-only password for an app role (Prisma/TCP need it;\n * autopg hba uses `password` for 127.0.0.1).\n */\nexport function rolePasswordFor(databaseName: string): string {\n return createHash(\"sha256\")\n .update(`${PASSWORD_SALT_PREFIX}\\0${databaseName}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n/**\n * Idempotently CREATE ROLE + CREATE DATABASE with cedar-pg owned names.\n */\nexport async function ensureDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n password?: string;\n}): Promise<void> {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n const roleExists = await client.query(\"SELECT 1 FROM pg_roles WHERE rolname = $1\", [\n opts.roleName,\n ]);\n if (roleExists.rowCount === 0) {\n await client.query(\n `CREATE ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n } else {\n await client.query(\n `ALTER ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n }\n\n const dbExists = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [\n opts.databaseName,\n ]);\n if (dbExists.rowCount === 0) {\n await client.query(\n `CREATE DATABASE ${quoteIdent(opts.databaseName)} OWNER ${quoteIdent(opts.roleName)}`,\n );\n } else {\n await client.query(\n `ALTER DATABASE ${quoteIdent(opts.databaseName)} OWNER TO ${quoteIdent(opts.roleName)}`,\n );\n }\n } finally {\n await client.end();\n }\n}\n\n/**\n * DROP DATABASE (force terminate backends) + DROP ROLE.\n */\nexport async function dropDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n}): Promise<void> {\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n await client.query(\n `\n SELECT pg_terminate_backend(pid)\n FROM pg_stat_activity\n WHERE datname = $1 AND pid <> pg_backend_pid()\n `,\n [opts.databaseName],\n );\n await client.query(`DROP DATABASE IF EXISTS ${quoteIdent(opts.databaseName)}`);\n const owns = await client.query(\n `SELECT 1 FROM pg_database WHERE datdba = (SELECT oid FROM pg_roles WHERE rolname = $1) LIMIT 1`,\n [opts.roleName],\n );\n if (owns.rowCount === 0) {\n await client.query(`DROP ROLE IF EXISTS ${quoteIdent(opts.roleName)}`);\n }\n } finally {\n await client.end();\n }\n}\n\nexport function buildDatabaseUrl(opts: {\n port: number;\n databaseName: string;\n roleName: string;\n password?: string;\n}): string {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const user = encodeURIComponent(opts.roleName);\n const pass = encodeURIComponent(password);\n return `postgresql://${user}:${pass}@127.0.0.1:${opts.port}/${opts.databaseName}`;\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { buildDatabaseName, buildRoleName, type DbMode } from \"./naming.ts\";\nimport {\n forgetLease,\n isOrphanLease,\n leaseDir,\n listRegistryLeases,\n readLease,\n writeLease,\n type Lease,\n} from \"./lease.ts\";\nimport { resolveEnsureSkip, type ResolveEnsureSkipInput } from \"./policy.ts\";\nimport { resolveWorktreeIdentity } from \"./worktree.ts\";\nimport {\n buildDatabaseUrl,\n dropDatabase,\n ensureDatabase,\n ensureHostRunning,\n} from \"../providers/autopg.ts\";\n\nfunction writeEnvFile(root: string, mode: DbMode, databaseUrl: string): void {\n const dir = leaseDir(root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n let body = `DATABASE_URL=${databaseUrl}\\n`;\n if (mode === \"test\") body += `TEST_DATABASE_URL=${databaseUrl}\\n`;\n writeFileSync(join(dir, `${mode}.env`), body, { mode: 0o600 });\n}\n\nexport function urlFromLease(lease: Lease): string {\n return buildDatabaseUrl({\n port: lease.port,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n}\n\n/**\n * DROP the database, then forget lease/registry. Never forget without a successful DROP.\n */\nasync function dropThenForget(lease: Lease, adminUrl: string): Promise<void> {\n await dropDatabase({\n adminUrl,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n forgetLease(lease);\n}\n\nexport type EnsureOptions = {\n root?: string;\n mode: DbMode;\n /** Inject DATABASE_URL / TEST_DATABASE_URL into process.env (default true). */\n setEnv?: boolean;\n};\n\nexport type EnsureResult = {\n databaseUrl: string;\n databaseName: string;\n roleName: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n root: string;\n mode: DbMode;\n port: number;\n dispose: () => Promise<void>;\n};\n\n/**\n * Ensure a worktree-scoped database exists and return connection info.\n *\n * - `dev`: keep DB across restarts.\n * - `test`: DROP when `dispose()` is awaited (callers / test runners own teardown).\n */\nexport async function ensure(options: EnsureOptions): Promise<EnsureResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode;\n const databaseName = buildDatabaseName(identity, mode);\n const roleName = buildRoleName(databaseName);\n\n const host = ensureHostRunning();\n await ensureDatabase({\n adminUrl: host.adminUrl,\n databaseName,\n roleName,\n });\n\n const databaseUrl = buildDatabaseUrl({\n port: host.port,\n databaseName,\n roleName,\n });\n\n const lease: Lease = {\n schemaVersion: 1,\n mode,\n root: identity.root,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n databaseName,\n roleName,\n port: host.port,\n pid: process.pid,\n createdAt: new Date().toISOString(),\n };\n writeLease(lease);\n writeEnvFile(identity.root, mode, databaseUrl);\n\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = databaseUrl;\n if (mode === \"test\") {\n process.env.TEST_DATABASE_URL = databaseUrl;\n }\n }\n\n const disposeFn = async () => {\n await dispose({ root: identity.root, mode });\n };\n\n return {\n databaseUrl,\n databaseName,\n roleName,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n root: identity.root,\n mode,\n port: host.port,\n dispose: disposeFn,\n };\n}\n\nexport type EnsureIfNeededOptions = EnsureOptions & ResolveEnsureSkipInput;\n\nexport type EnsureIfNeededResult =\n | { status: \"skipped\"; reason: \"disabled\" }\n | { status: \"skipped\"; reason: \"external-url\"; databaseUrl: string }\n | ({ status: \"ensured\" } & EnsureResult);\n\n/**\n * Resolve skip policy then ensure. Single entry for hosts (Cedar CLI, Jest, Vitest).\n * On external-url skip, sets DATABASE_URL when `setEnv` is not false.\n */\nexport async function ensureIfNeeded(\n options: EnsureIfNeededOptions,\n): Promise<EnsureIfNeededResult> {\n const skip = resolveEnsureSkip({\n url: options.url,\n force: options.force,\n disabled: options.disabled,\n });\n if (skip.skip) {\n if (skip.reason === \"external-url\") {\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = skip.databaseUrl;\n }\n return { status: \"skipped\", reason: \"external-url\", databaseUrl: skip.databaseUrl };\n }\n return { status: \"skipped\", reason: \"disabled\" };\n }\n\n const result = await ensure({\n root: options.root,\n mode: options.mode,\n setEnv: options.setEnv,\n });\n return { status: \"ensured\", ...result };\n}\n\nexport type DisposeOptions = {\n root?: string;\n mode?: DbMode;\n};\n\nexport type DisposeResult =\n | { dropped: true; databaseName: string }\n | { dropped: false; reason: \"no-lease\" | \"host-unavailable\" };\n\n/**\n * DROP the worktree database for the given mode (default: test).\n * No-ops without a valid lease (never invents a name to DROP).\n * If the host is unavailable, leaves the lease so dispose/gc can retry.\n */\nexport async function dispose(options: DisposeOptions = {}): Promise<DisposeResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode ?? \"test\";\n const lease = readLease(identity.root, mode);\n if (!lease) return { dropped: false, reason: \"no-lease\" };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n // Keep lease + registry so a later dispose/gc can still find the DB.\n return { dropped: false, reason: \"host-unavailable\" };\n }\n\n await dropThenForget(lease, host.adminUrl);\n return { dropped: true, databaseName: lease.databaseName };\n}\n\n/**\n * Drop databases whose registered worktree root no longer exists on disk.\n * Registry entries are removed only after a successful DROP.\n */\nexport async function gc(): Promise<{\n dropped: string[];\n}> {\n const dropped: string[] = [];\n const orphans = listRegistryLeases().filter(isOrphanLease);\n if (orphans.length === 0) return { dropped };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n return { dropped };\n }\n\n for (const lease of orphans) {\n try {\n await dropThenForget(lease, host.adminUrl);\n dropped.push(lease.databaseName);\n } catch {\n // Keep registry entry for retry\n }\n }\n return { dropped };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-PbTXNC2Z.mjs","names":[],"sources":["../src/core/naming.ts","../src/core/lease.ts","../src/core/policy.ts","../src/core/worktree.ts","../src/providers/autopg.ts","../src/core/lifecycle.ts"],"sourcesContent":["import type { WorktreeIdentity } from \"./worktree.ts\";\n\nexport type DbMode = \"dev\" | \"test\";\n\nconst PG_MAX = 63;\nconst PREFIX = \"cpg_\";\n\n/**\n * Build an observable Postgres database name:\n * cpg_<repoSlug>_<worktreeSlug>_<mode>_<pathHash8>\n *\n * Never drops `mode` or `pathHash`. Truncates slugs to fit ≤63 chars.\n */\nexport function buildDatabaseName(identity: WorktreeIdentity, mode: DbMode): string {\n const modePart = mode;\n const hashPart = identity.pathHash;\n // PREFIX + slug + '_' + slug + '_' + mode + '_' + hash\n // fixed: PREFIX(4) + 2 underscores around mode/hash segments + mode + hash\n // layout: cpg_<repo>_<wt>_<mode>_<hash>\n const fixed =\n PREFIX.length +\n 1 + // after repo\n 1 + // after worktree\n modePart.length +\n 1 +\n hashPart.length;\n\n let budget = PG_MAX - fixed;\n if (budget < 2) {\n // Extreme truncation: keep mode+hash, minimal slugs\n return `${PREFIX}${modePart}_${hashPart}`.slice(0, PG_MAX);\n }\n\n let repo = identity.repoSlug;\n let wt = identity.worktreeSlug;\n // Split budget roughly evenly, preferring to keep both readable\n while (repo.length + wt.length > budget) {\n if (repo.length >= wt.length && repo.length > 1) {\n repo = repo.slice(0, -1);\n } else if (wt.length > 1) {\n wt = wt.slice(0, -1);\n } else {\n break;\n }\n }\n\n const name = `${PREFIX}${repo}_${wt}_${modePart}_${hashPart}`;\n if (name.length > PG_MAX) {\n return name.slice(0, PG_MAX);\n }\n return name;\n}\n\nexport function buildRoleName(databaseName: string): string {\n const suffix = \"_role\";\n const maxBase = PG_MAX - suffix.length;\n const base = databaseName.slice(0, maxBase);\n return `${base}${suffix}`;\n}\n","import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { STATE_DIRNAME } from \"./constants.ts\";\nimport type { DbMode } from \"./naming.ts\";\n\nexport type Lease = {\n schemaVersion: 1;\n mode: DbMode;\n root: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n databaseName: string;\n roleName: string;\n port: number;\n pid: number;\n createdAt: string;\n};\n\nfunction isDbMode(value: unknown): value is DbMode {\n return value === \"dev\" || value === \"test\";\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\n/** Narrow-parse a lease; reject incomplete or wrong-version records. */\nexport function parseLease(raw: unknown): Lease | null {\n if (!raw || typeof raw !== \"object\") return null;\n const o = raw as Record<string, unknown>;\n if (o.schemaVersion !== 1) return null;\n if (!isDbMode(o.mode)) return null;\n if (!isNonEmptyString(o.root)) return null;\n if (!isNonEmptyString(o.repoSlug)) return null;\n if (!isNonEmptyString(o.worktreeSlug)) return null;\n if (!isNonEmptyString(o.pathHash)) return null;\n if (!isNonEmptyString(o.databaseName)) return null;\n if (!isNonEmptyString(o.roleName)) return null;\n if (typeof o.port !== \"number\") return null;\n if (typeof o.pid !== \"number\") return null;\n if (!isNonEmptyString(o.createdAt)) return null;\n return {\n schemaVersion: 1,\n mode: o.mode,\n root: o.root,\n repoSlug: o.repoSlug,\n worktreeSlug: o.worktreeSlug,\n pathHash: o.pathHash,\n databaseName: o.databaseName,\n roleName: o.roleName,\n port: o.port,\n pid: o.pid,\n createdAt: o.createdAt,\n };\n}\n\nexport function leaseDir(root: string): string {\n return join(root, STATE_DIRNAME);\n}\n\nexport function leasePath(root: string, mode: DbMode): string {\n return join(leaseDir(root), `${mode}.json`);\n}\n\n/** Durable registry outside worktrees so GC can find orphans after a checkout is deleted. */\nexport function registryDir(): string {\n return process.env.CEDAR_PG_REGISTRY_DIR || join(homedir(), STATE_DIRNAME, \"registry\");\n}\n\nfunction registryPath(databaseName: string): string {\n return join(registryDir(), `${databaseName}.json`);\n}\n\nfunction writeJsonAtomic(file: string, value: unknown): void {\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 });\n const tmp = `${file}.tmp.${process.pid}`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600 });\n renameSync(tmp, file);\n}\n\nfunction registerLease(lease: Lease): void {\n writeJsonAtomic(registryPath(lease.databaseName), lease);\n}\n\nfunction unregisterLease(databaseName: string): void {\n try {\n unlinkSync(registryPath(databaseName));\n } catch {\n // missing is fine\n }\n}\n\nfunction peekDatabaseName(root: string, mode: DbMode): string | undefined {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return undefined;\n try {\n const raw = JSON.parse(readFileSync(file, \"utf8\")) as { databaseName?: unknown };\n return typeof raw.databaseName === \"string\" && raw.databaseName ? raw.databaseName : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function readLease(root: string, mode: DbMode): Lease | null {\n const file = leasePath(root, mode);\n if (!existsSync(file)) return null;\n try {\n return parseLease(JSON.parse(readFileSync(file, \"utf8\")));\n } catch {\n return null;\n }\n}\n\n/**\n * Persist lease. Registry is written first (GC source of truth), then the worktree cache.\n */\nexport function writeLease(lease: Lease): void {\n registerLease(lease);\n const dir = leaseDir(lease.root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeJsonAtomic(leasePath(lease.root, lease.mode), lease);\n}\n\n/**\n * Remove registry entry by databaseName and best-effort delete the worktree lease file.\n * Call only after a successful DROP (or when intentionally abandoning tracking).\n */\nexport function forgetLease(lease: Pick<Lease, \"root\" | \"mode\" | \"databaseName\">): void {\n unregisterLease(lease.databaseName);\n try {\n unlinkSync(leasePath(lease.root, lease.mode));\n } catch {\n // root or file may already be gone\n }\n}\n\n/**\n * Clear worktree lease + registry. Unregisters by databaseName even when the local\n * file fails full parse (peeks databaseName from raw JSON).\n */\nexport function clearLease(root: string, mode: DbMode): void {\n const existing = readLease(root, mode);\n if (existing) {\n forgetLease(existing);\n return;\n }\n const databaseName = peekDatabaseName(root, mode);\n try {\n unlinkSync(leasePath(root, mode));\n } catch {\n // missing is fine\n }\n if (databaseName) {\n unregisterLease(databaseName);\n }\n}\n\n/** All registry entries (used by gc). */\nexport function listRegistryLeases(): Lease[] {\n const dir = registryDir();\n if (!existsSync(dir)) return [];\n const out: Lease[] = [];\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(\".json\")) continue;\n try {\n const lease = parseLease(JSON.parse(readFileSync(join(dir, name), \"utf8\")));\n if (lease) out.push(lease);\n } catch {\n // skip corrupt\n }\n }\n return out;\n}\n\nexport function isOrphanLease(lease: Lease): boolean {\n return !existsSync(lease.root);\n}\n","/**\n * Shared ensure skip policy for test-runner adapters and Cedar CLI bridges.\n */\n\nexport type EnsureSkip =\n | { skip: false }\n | { skip: true; reason: \"disabled\" }\n | { skip: true; reason: \"external-url\"; databaseUrl: string };\n\nexport type ResolveEnsureSkipInput = {\n /** Candidate URL (TEST_DATABASE_URL for tests, DATABASE_URL for dev). */\n url?: string;\n /** When true, never skip (CEDAR_PG_FORCE=1). */\n force?: boolean;\n /**\n * When true, skip ensure entirely.\n * Defaults from CEDAR_PG=0|false (opt-out adapters). Pass `false` for Cedar opt-in flows.\n */\n disabled?: boolean;\n};\n\n/**\n * True when the URL looks like a cedarpg provisioned database (`cpg_*` name/role).\n * These must never be treated as an external escape hatch — always re-ensure so\n * disposed/stale shell env cannot skip provisioning.\n */\nexport function isCedarPgManagedUrl(url: string | undefined): boolean {\n if (!url || url.startsWith(\"file:\")) return false;\n try {\n const parsed = new URL(url);\n const databaseName = decodeURIComponent(\n (parsed.pathname.replace(/^\\//, \"\").split(\"?\")[0] ?? \"\").trim(),\n );\n const user = decodeURIComponent(parsed.username);\n return databaseName.startsWith(\"cpg_\") || user.startsWith(\"cpg_\");\n } catch {\n return false;\n }\n}\n\n/**\n * True when `url` is a real external database and ensure should be skipped.\n * Sqlite `file:` URLs and cedarpg `cpg_*` URLs are not external.\n */\nexport function isExternalDatabaseEscapeHatch(url: string | undefined): boolean {\n if (!url) return false;\n if (url.startsWith(\"file:\")) return false;\n if (isCedarPgManagedUrl(url)) return false;\n return true;\n}\n\n/**\n * Decide whether ensure should run.\n * Adapters may call with no args (env defaults). Cedar opt-in should pass `disabled: false`\n * and the relevant `url` (`DATABASE_URL` or `TEST_DATABASE_URL`).\n */\nexport function resolveEnsureSkip(input: ResolveEnsureSkipInput = {}): EnsureSkip {\n const disabled =\n input.disabled ?? (process.env.CEDAR_PG === \"0\" || process.env.CEDAR_PG === \"false\");\n if (disabled) {\n return { skip: true, reason: \"disabled\" };\n }\n\n const force = input.force ?? process.env.CEDAR_PG_FORCE === \"1\";\n if (force) {\n return { skip: false };\n }\n\n const url = input.url !== undefined ? input.url : process.env.TEST_DATABASE_URL;\n if (url && isExternalDatabaseEscapeHatch(url)) {\n return { skip: true, reason: \"external-url\", databaseUrl: url };\n }\n return { skip: false };\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\nexport type WorktreeIdentity = {\n /** Absolute path to the worktree / project root. */\n root: string;\n /** Basename of the git common dir / main checkout (e.g. `cedar`). */\n repoSlug: string;\n /** Basename of this worktree directory (e.g. `feat-auth`). */\n worktreeSlug: string;\n /** First 8 hex chars of sha256(absolute root). */\n pathHash: string;\n};\n\nfunction git(args: string[], cwd: string): string | null {\n try {\n return execFileSync(\"git\", args, {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n return null;\n }\n}\n\nfunction sanitizeSlug(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\");\n}\n\n/**\n * Resolve the project / worktree root.\n * Explicit `root` wins (app project path). Otherwise prefer git toplevel, then cwd.\n */\nexport function resolveRoot(root?: string): string {\n if (root) return resolve(root);\n const start = resolve(process.cwd());\n const toplevel = git([\"rev-parse\", \"--show-toplevel\"], start);\n if (toplevel) return toplevel;\n return start;\n}\n\n/**\n * Identify repo + worktree for observable DB naming.\n */\nexport function resolveWorktreeIdentity(root?: string): WorktreeIdentity {\n const absRoot = resolveRoot(root);\n const pathHash = createHash(\"sha256\").update(absRoot).digest(\"hex\").slice(0, 8);\n\n const commonDir = git([\"rev-parse\", \"--git-common-dir\"], absRoot);\n let repoSlug = sanitizeSlug(basename(absRoot));\n if (commonDir) {\n // common-dir is often `<main>/.git` or an absolute path to the main .git\n const commonAbs = resolve(absRoot, commonDir);\n // If commonAbs ends with `.git`, parent is main worktree root\n const mainRoot = basename(commonAbs) === \".git\" ? dirname(commonAbs) : commonAbs;\n // For linked worktrees, common-dir may be `.../main/.git`\n const candidate = basename(mainRoot) === \".git\" ? dirname(mainRoot) : mainRoot;\n const fromGit = sanitizeSlug(basename(candidate));\n if (fromGit) repoSlug = fromGit;\n }\n\n // Prefer gitdir worktree name when present (`.git` file pointing at worktrees/<name>)\n let worktreeSlug = sanitizeSlug(basename(absRoot));\n const gitFile = resolve(absRoot, \".git\");\n if (existsSync(gitFile)) {\n try {\n const content = readFileSync(gitFile, \"utf8\").trim();\n const match = content.match(/gitdir:\\s*(.+)$/m);\n if (match?.[1]) {\n const gitdir = resolve(absRoot, match[1].trim());\n // .../.git/worktrees/<name>\n const parts = gitdir.split(/[/\\\\]/);\n const wtIdx = parts.lastIndexOf(\"worktrees\");\n if (wtIdx >= 0 && parts[wtIdx + 1]) {\n const fromWt = sanitizeSlug(parts[wtIdx + 1]!);\n if (fromWt) worktreeSlug = fromWt;\n }\n }\n } catch {\n // keep basename slug\n }\n }\n\n if (!repoSlug) repoSlug = \"repo\";\n if (!worktreeSlug) worktreeSlug = \"wt\";\n\n return { root: absRoot, repoSlug, worktreeSlug, pathHash };\n}\n","import { createHash } from \"node:crypto\";\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport pg from \"pg\";\nimport { PASSWORD_SALT_PREFIX } from \"../core/constants.ts\";\n\nexport type AutopgDiscovery = {\n port: number;\n adminUrl: string;\n bin: string;\n};\n\nexport const INSTALL_HINT =\n \"autopg is required. Install with:\\n\" +\n \" curl -fsSL https://raw.githubusercontent.com/automagik-dev/autopg/main/install.sh | bash\\n\" +\n \"Then ensure ~/.local/bin is on PATH, or set AUTOPG_BIN.\";\n\n/** Password scheme v1: sha256(PASSWORD_SALT_PREFIX + \"\\\\0\" + databaseName) hex[:32]. Frozen for URL rebuild. */\nexport const ROLE_PASSWORD_SCHEME = \"v1\" as const;\n\nfunction candidateBins(): string[] {\n const out: string[] = [];\n if (process.env.AUTOPG_BIN) out.push(process.env.AUTOPG_BIN);\n out.push(\"autopg\");\n out.push(join(homedir(), \".local\", \"bin\", \"autopg\"));\n return out;\n}\n\nexport function resolveAutopgBin(): string | null {\n for (const bin of candidateBins()) {\n if (bin === \"autopg\") {\n const which = spawnSync(\"which\", [\"autopg\"], { encoding: \"utf8\" });\n if (which.status === 0 && which.stdout.trim()) return which.stdout.trim();\n continue;\n }\n if (existsSync(bin)) return bin;\n }\n return null;\n}\n\nexport function requireAutopgBin(): string {\n const bin = resolveAutopgBin();\n if (!bin) {\n throw new Error(INSTALL_HINT);\n }\n return bin;\n}\n\n/**\n * Parse `autopg status --json` output. Requires a numeric port and running !== false.\n */\nexport function parseHostStatus(json: string): { port: number } {\n let parsed: { port?: unknown; running?: unknown };\n try {\n parsed = JSON.parse(json) as { port?: unknown; running?: unknown };\n } catch {\n throw new Error(`autopg status --json returned invalid JSON.\\n${INSTALL_HINT}`);\n }\n if (typeof parsed.port !== \"number\") {\n throw new Error(`autopg status --json missing numeric port.\\n${INSTALL_HINT}`);\n }\n if (parsed.running === false) {\n throw new Error(`autopg host is not running.\\n${INSTALL_HINT}`);\n }\n return { port: parsed.port };\n}\n\nfunction adminUrlFor(port: number): string {\n return `postgresql://postgres:postgres@127.0.0.1:${port}/postgres`;\n}\n\n/**\n * Discover a live autopg host via `autopg status --json`. Throws if the host is not proven live.\n */\nexport function discoverHost(bin = requireAutopgBin()): AutopgDiscovery {\n let status: string;\n try {\n status = execFileSync(bin, [\"status\", \"--json\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to query autopg status.\\n${detail}\\n${INSTALL_HINT}`);\n }\n const { port } = parseHostStatus(status);\n return { port, adminUrl: adminUrlFor(port), bin };\n}\n\n/**\n * Ensure the host postmaster is up. Runs `autopg install` only after status fails, then re-discovers.\n */\nexport function ensureHostRunning(bin = requireAutopgBin()): AutopgDiscovery {\n try {\n return discoverHost(bin);\n } catch {\n // status failed — attempt install, then require a successful rediscovery\n }\n\n const install = spawnSync(bin, [\"install\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (install.status !== 0) {\n throw new Error(\n `Failed to start autopg host (exit ${install.status}).\\n` +\n `${install.stderr || install.stdout || \"\"}\\n${INSTALL_HINT}`,\n );\n }\n return discoverHost(bin);\n}\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction quoteLiteral(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Deterministic local-only password for an app role (Prisma/TCP need it;\n * autopg hba uses `password` for 127.0.0.1).\n */\nexport function rolePasswordFor(databaseName: string): string {\n return createHash(\"sha256\")\n .update(`${PASSWORD_SALT_PREFIX}\\0${databaseName}`)\n .digest(\"hex\")\n .slice(0, 32);\n}\n\n/**\n * Idempotently CREATE ROLE + CREATE DATABASE with cedar-pg owned names.\n */\nexport async function ensureDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n password?: string;\n}): Promise<void> {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n const roleExists = await client.query(\"SELECT 1 FROM pg_roles WHERE rolname = $1\", [\n opts.roleName,\n ]);\n if (roleExists.rowCount === 0) {\n await client.query(\n `CREATE ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n } else {\n await client.query(\n `ALTER ROLE ${quoteIdent(opts.roleName)} WITH LOGIN PASSWORD ${quoteLiteral(password)}`,\n );\n }\n\n const dbExists = await client.query(\"SELECT 1 FROM pg_database WHERE datname = $1\", [\n opts.databaseName,\n ]);\n if (dbExists.rowCount === 0) {\n await client.query(\n `CREATE DATABASE ${quoteIdent(opts.databaseName)} OWNER ${quoteIdent(opts.roleName)}`,\n );\n } else {\n await client.query(\n `ALTER DATABASE ${quoteIdent(opts.databaseName)} OWNER TO ${quoteIdent(opts.roleName)}`,\n );\n }\n } finally {\n await client.end();\n }\n}\n\n/**\n * DROP DATABASE (force terminate backends) + DROP ROLE.\n */\nexport async function dropDatabase(opts: {\n adminUrl: string;\n databaseName: string;\n roleName: string;\n}): Promise<void> {\n const client = new pg.Client({ connectionString: opts.adminUrl });\n await client.connect();\n try {\n await client.query(\n `\n SELECT pg_terminate_backend(pid)\n FROM pg_stat_activity\n WHERE datname = $1 AND pid <> pg_backend_pid()\n `,\n [opts.databaseName],\n );\n await client.query(`DROP DATABASE IF EXISTS ${quoteIdent(opts.databaseName)}`);\n const owns = await client.query(\n `SELECT 1 FROM pg_database WHERE datdba = (SELECT oid FROM pg_roles WHERE rolname = $1) LIMIT 1`,\n [opts.roleName],\n );\n if (owns.rowCount === 0) {\n await client.query(`DROP ROLE IF EXISTS ${quoteIdent(opts.roleName)}`);\n }\n } finally {\n await client.end();\n }\n}\n\nexport function buildDatabaseUrl(opts: {\n port: number;\n databaseName: string;\n roleName: string;\n password?: string;\n}): string {\n const password = opts.password ?? rolePasswordFor(opts.databaseName);\n const user = encodeURIComponent(opts.roleName);\n const pass = encodeURIComponent(password);\n return `postgresql://${user}:${pass}@127.0.0.1:${opts.port}/${opts.databaseName}`;\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { buildDatabaseName, buildRoleName, type DbMode } from \"./naming.ts\";\nimport {\n forgetLease,\n isOrphanLease,\n leaseDir,\n listRegistryLeases,\n readLease,\n writeLease,\n type Lease,\n} from \"./lease.ts\";\nimport { resolveEnsureSkip, type ResolveEnsureSkipInput } from \"./policy.ts\";\nimport { resolveWorktreeIdentity } from \"./worktree.ts\";\nimport {\n buildDatabaseUrl,\n dropDatabase,\n ensureDatabase,\n ensureHostRunning,\n} from \"../providers/autopg.ts\";\n\nfunction writeEnvFile(root: string, mode: DbMode, databaseUrl: string): void {\n const dir = leaseDir(root);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n let body = `DATABASE_URL=${databaseUrl}\\n`;\n if (mode === \"test\") body += `TEST_DATABASE_URL=${databaseUrl}\\n`;\n writeFileSync(join(dir, `${mode}.env`), body, { mode: 0o600 });\n}\n\nexport function urlFromLease(lease: Lease): string {\n return buildDatabaseUrl({\n port: lease.port,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n}\n\n/**\n * DROP the database, then forget lease/registry. Never forget without a successful DROP.\n */\nasync function dropThenForget(lease: Lease, adminUrl: string): Promise<void> {\n await dropDatabase({\n adminUrl,\n databaseName: lease.databaseName,\n roleName: lease.roleName,\n });\n forgetLease(lease);\n}\n\nexport type EnsureOptions = {\n root?: string;\n mode: DbMode;\n /** Inject DATABASE_URL / TEST_DATABASE_URL into process.env (default true). */\n setEnv?: boolean;\n};\n\nexport type EnsureResult = {\n databaseUrl: string;\n databaseName: string;\n roleName: string;\n repoSlug: string;\n worktreeSlug: string;\n pathHash: string;\n root: string;\n mode: DbMode;\n port: number;\n dispose: () => Promise<void>;\n};\n\n/**\n * Ensure a worktree-scoped database exists and return connection info.\n *\n * - `dev`: keep DB across restarts.\n * - `test`: DROP when `dispose()` is awaited (callers / test runners own teardown).\n */\nexport async function ensure(options: EnsureOptions): Promise<EnsureResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode;\n const databaseName = buildDatabaseName(identity, mode);\n const roleName = buildRoleName(databaseName);\n\n const host = ensureHostRunning();\n await ensureDatabase({\n adminUrl: host.adminUrl,\n databaseName,\n roleName,\n });\n\n const databaseUrl = buildDatabaseUrl({\n port: host.port,\n databaseName,\n roleName,\n });\n\n const lease: Lease = {\n schemaVersion: 1,\n mode,\n root: identity.root,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n databaseName,\n roleName,\n port: host.port,\n pid: process.pid,\n createdAt: new Date().toISOString(),\n };\n writeLease(lease);\n writeEnvFile(identity.root, mode, databaseUrl);\n\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = databaseUrl;\n if (mode === \"test\") {\n process.env.TEST_DATABASE_URL = databaseUrl;\n }\n }\n\n const disposeFn = async () => {\n await dispose({ root: identity.root, mode });\n };\n\n return {\n databaseUrl,\n databaseName,\n roleName,\n repoSlug: identity.repoSlug,\n worktreeSlug: identity.worktreeSlug,\n pathHash: identity.pathHash,\n root: identity.root,\n mode,\n port: host.port,\n dispose: disposeFn,\n };\n}\n\nexport type EnsureIfNeededOptions = EnsureOptions & ResolveEnsureSkipInput;\n\nexport type EnsureIfNeededResult =\n | { status: \"skipped\"; reason: \"disabled\" }\n | { status: \"skipped\"; reason: \"external-url\"; databaseUrl: string }\n | ({ status: \"ensured\" } & EnsureResult);\n\n/**\n * Resolve skip policy then ensure. Single entry for hosts (Cedar CLI, Jest, Vitest).\n * On external-url skip, sets DATABASE_URL when `setEnv` is not false.\n */\nexport async function ensureIfNeeded(\n options: EnsureIfNeededOptions,\n): Promise<EnsureIfNeededResult> {\n const skip = resolveEnsureSkip({\n url: options.url,\n force: options.force,\n disabled: options.disabled,\n });\n if (skip.skip) {\n if (skip.reason === \"external-url\") {\n if (options.setEnv !== false) {\n process.env.DATABASE_URL = skip.databaseUrl;\n }\n return { status: \"skipped\", reason: \"external-url\", databaseUrl: skip.databaseUrl };\n }\n return { status: \"skipped\", reason: \"disabled\" };\n }\n\n const result = await ensure({\n root: options.root,\n mode: options.mode,\n setEnv: options.setEnv,\n });\n return { status: \"ensured\", ...result };\n}\n\nexport type DisposeOptions = {\n root?: string;\n mode?: DbMode;\n};\n\nexport type DisposeResult =\n | { dropped: true; databaseName: string }\n | { dropped: false; reason: \"no-lease\" | \"host-unavailable\" };\n\n/**\n * DROP the worktree database for the given mode (default: test).\n * No-ops without a valid lease (never invents a name to DROP).\n * If the host is unavailable, leaves the lease so dispose/gc can retry.\n */\nexport async function dispose(options: DisposeOptions = {}): Promise<DisposeResult> {\n const identity = resolveWorktreeIdentity(options.root);\n const mode = options.mode ?? \"test\";\n const lease = readLease(identity.root, mode);\n if (!lease) return { dropped: false, reason: \"no-lease\" };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n // Keep lease + registry so a later dispose/gc can still find the DB.\n return { dropped: false, reason: \"host-unavailable\" };\n }\n\n await dropThenForget(lease, host.adminUrl);\n return { dropped: true, databaseName: lease.databaseName };\n}\n\n/**\n * Drop databases whose registered worktree root no longer exists on disk.\n * Registry entries are removed only after a successful DROP.\n */\nexport async function gc(): Promise<{\n dropped: string[];\n}> {\n const dropped: string[] = [];\n const orphans = listRegistryLeases().filter(isOrphanLease);\n if (orphans.length === 0) return { dropped };\n\n let host;\n try {\n host = ensureHostRunning();\n } catch {\n return { dropped };\n }\n\n for (const lease of orphans) {\n try {\n await dropThenForget(lease, host.adminUrl);\n dropped.push(lease.databaseName);\n } catch {\n // Keep registry entry for retry\n }\n }\n return { dropped };\n}\n"],"mappings":"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"}
|