@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.
Files changed (47) hide show
  1. package/README.md +14 -14
  2. package/dist/cli.cjs +5 -5
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/cli.mjs +5 -5
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{constants-YnHDwShD.mjs → constants-BY97wXjA.mjs} +3 -3
  7. package/dist/constants-BY97wXjA.mjs.map +1 -0
  8. package/dist/{constants-B7sCf4fg.cjs → constants-CNZn5Xro.cjs} +3 -3
  9. package/dist/constants-CNZn5Xro.cjs.map +1 -0
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.mts +1 -1
  13. package/dist/index.mjs +1 -1
  14. package/dist/jest.cjs +3 -3
  15. package/dist/jest.cjs.map +1 -1
  16. package/dist/jest.d.cts +2 -2
  17. package/dist/jest.d.mts +2 -2
  18. package/dist/jest.mjs +3 -3
  19. package/dist/jest.mjs.map +1 -1
  20. package/dist/{lifecycle-D10usEjM.cjs → lifecycle-BGYtVXtx.cjs} +3 -3
  21. package/dist/lifecycle-BGYtVXtx.cjs.map +1 -0
  22. package/dist/{lifecycle-PbTXNC2Z.mjs → lifecycle-CT_8AWPv.mjs} +3 -3
  23. package/dist/lifecycle-CT_8AWPv.mjs.map +1 -0
  24. package/dist/nx.cjs +3 -3
  25. package/dist/nx.cjs.map +1 -1
  26. package/dist/nx.d.cts +1 -1
  27. package/dist/nx.d.mts +1 -1
  28. package/dist/nx.mjs +3 -3
  29. package/dist/nx.mjs.map +1 -1
  30. package/dist/{tasks-CW2D0n46.mjs → tasks-13P6tMth.mjs} +2 -2
  31. package/dist/{tasks-CW2D0n46.mjs.map → tasks-13P6tMth.mjs.map} +1 -1
  32. package/dist/{tasks-DZ7hMmGJ.cjs → tasks-B8ryV9xo.cjs} +2 -2
  33. package/dist/{tasks-DZ7hMmGJ.cjs.map → tasks-B8ryV9xo.cjs.map} +1 -1
  34. package/dist/vite-plus.cjs +2 -2
  35. package/dist/vite-plus.mjs +2 -2
  36. package/dist/vitest.cjs +2 -2
  37. package/dist/vitest.cjs.map +1 -1
  38. package/dist/vitest.d.cts +1 -1
  39. package/dist/vitest.d.mts +1 -1
  40. package/dist/vitest.mjs +2 -2
  41. package/dist/vitest.mjs.map +1 -1
  42. package/package.json +2 -2
  43. package/scripts/postinstall.js +5 -5
  44. package/dist/constants-B7sCf4fg.cjs.map +0 -1
  45. package/dist/constants-YnHDwShD.mjs.map +0 -1
  46. package/dist/lifecycle-D10usEjM.cjs.map +0 -1
  47. 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** — powered by [autopg](https://github.com/automagik-dev/autopg).
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
- Under the hood you get an **embedded PostgreSQL server with true concurrent connections** — native PostgreSQL 18 (not WASM), real process forking, no connection locks. It just works: **no credentials needed**, **zero config**, and **auto-provisioned databases** on first use. Any client works (`psql`, `node-postgres`, Prisma, Drizzle, TypeORM).
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** sits on that autopg host and gives each **git worktree** its own database + role, with readable names, leases, and teardown — so parallel checkouts don’t fight over one shared DB:
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 — concurrent, zero-config, auto-provision |
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 — 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 |
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`) — not under autopg's `~/.autopg/`, not a generic `.pg`.
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-D10usEjM.cjs");
3
- const require_constants = require("./constants-B7sCf4fg.cjs");
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} — worktree-isolated local Postgres (via autopg)
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} — autopg host unavailable (lease kept for retry)\n`);
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 — run \`${require_constants.CLI_NAME} ensure --mode=${mode}\` first\n`);
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} — 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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"}
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-PbTXNC2Z.mjs";
3
- import { t as CLI_NAME } from "./constants-YnHDwShD.mjs";
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} — worktree-isolated local Postgres (via autopg)
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} — autopg host unavailable (lease kept for retry)\n`);
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 — run \`${CLI_NAME} ensure --mode=${mode}\` first\n`);
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} — 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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"}
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`) — product-owned, not nested under
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 — independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.
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-YnHDwShD.mjs.map
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`) — product-owned, not nested under
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 — independent of STATE_DIRNAME / CLI_NAME. Bump ROLE_PASSWORD_SCHEME to change.
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-B7sCf4fg.cjs.map
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-D10usEjM.cjs");
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 — always re-ensure so
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 — always re-ensure so
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-PbTXNC2Z.mjs";
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-D10usEjM.cjs");
5
+ const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
6
6
  //#region src/adapters/jest.ts
7
7
  /**
8
- * Jest globalSetup — call from jest config:
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 — no skip re-check (env may flip mid-suite). */
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 — 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"}
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 — call from jest config:
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 — no skip re-check (env may flip mid-suite). */
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 — call from jest config:
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 — no skip re-check (env may flip mid-suite). */
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-PbTXNC2Z.mjs";
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 — call from jest config:
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 — no skip re-check (env may flip mid-suite). */
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 — 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"}
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-B7sCf4fg.cjs");
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 — always re-ensure so
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-D10usEjM.cjs.map
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-YnHDwShD.mjs";
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 — always re-ensure so
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-PbTXNC2Z.mjs.map
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":";;;;;;;;AAIA,MAAM,SAAS;AACf,MAAM,SAAS;;;;;;;AAQf,SAAgB,kBAAkB,UAA4B,MAAsB;CAClF,MAAM,WAAW;CACjB,MAAM,WAAW,SAAS;CAI1B,MAAM,QACJ,IAGA,SAAS,SACT,IACA,SAAS;CAEX,IAAI,SAAS,SAAS;CACtB,IAAI,SAAS,GAEX,OAAO,GAAG,SAAS,SAAS,GAAG,WAAW,MAAM,GAAG,MAAM;CAG3D,IAAI,OAAO,SAAS;CACpB,IAAI,KAAK,SAAS;CAElB,OAAO,KAAK,SAAS,GAAG,SAAS,QAC/B,IAAI,KAAK,UAAU,GAAG,UAAU,KAAK,SAAS,GAC5C,OAAO,KAAK,MAAM,GAAG,EAAE;MAClB,IAAI,GAAG,SAAS,GACrB,KAAK,GAAG,MAAM,GAAG,EAAE;MAEnB;CAIJ,MAAM,OAAO,GAAG,SAAS,KAAK,GAAG,GAAG,GAAG,SAAS,GAAG;CACnD,IAAI,KAAK,SAAS,QAChB,OAAO,KAAK,MAAM,GAAG,MAAM;CAE7B,OAAO;AACT;AAEA,SAAgB,cAAc,cAA8B;CAC1D,MAAM,SAAS;CACf,MAAM,UAAU,SAAS;CAEzB,OAAO,GADM,aAAa,MAAM,GAAG,OACtB,IAAI;AACnB;;;AC9BA,SAAS,SAAS,OAAiC;CACjD,OAAO,UAAU,SAAS,UAAU;AACtC;AAEA,SAAS,iBAAiB,OAAiC;CACzD,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS;AACrD;;AAGA,SAAgB,WAAW,KAA4B;CACrD,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,OAAO;CAC5C,MAAM,IAAI;CACV,IAAI,EAAE,kBAAkB,GAAG,OAAO;CAClC,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO;CAC9B,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,OAAO;CACtC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,OAAO;CAC1C,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,OAAO;CAC9C,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,OAAO;CAC1C,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,OAAO;CAC9C,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,OAAO;CAC1C,IAAI,OAAO,EAAE,SAAS,UAAU,OAAO;CACvC,IAAI,OAAO,EAAE,QAAQ,UAAU,OAAO;CACtC,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,OAAO;CAC3C,OAAO;EACL,eAAe;EACf,MAAM,EAAE;EACR,MAAM,EAAE;EACR,UAAU,EAAE;EACZ,cAAc,EAAE;EAChB,UAAU,EAAE;EACZ,cAAc,EAAE;EAChB,UAAU,EAAE;EACZ,MAAM,EAAE;EACR,KAAK,EAAE;EACP,WAAW,EAAE;CACf;AACF;AAEA,SAAgB,SAAS,MAAsB;CAC7C,OAAO,KAAK,MAAM,aAAa;AACjC;AAEA,SAAgB,UAAU,MAAc,MAAsB;CAC5D,OAAO,KAAK,SAAS,IAAI,GAAG,GAAG,KAAK,MAAM;AAC5C;;AAGA,SAAgB,cAAsB;CACpC,OAAO,QAAQ,IAAI,yBAAyB,KAAK,QAAQ,GAAA,YAAkB,UAAU;AACvF;AAEA,SAAS,aAAa,cAA8B;CAClD,OAAO,KAAK,YAAY,GAAG,GAAG,aAAa,MAAM;AACnD;AAEA,SAAS,gBAAgB,MAAc,OAAsB;CAC3D,UAAU,QAAQ,IAAI,GAAG;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACzD,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ;CACnC,cAAc,KAAK,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;CACzE,WAAW,KAAK,IAAI;AACtB;AAEA,SAAS,cAAc,OAAoB;CACzC,gBAAgB,aAAa,MAAM,YAAY,GAAG,KAAK;AACzD;AAEA,SAAS,gBAAgB,cAA4B;CACnD,IAAI;EACF,WAAW,aAAa,YAAY,CAAC;CACvC,QAAQ,CAER;AACF;AAaA,SAAgB,UAAU,MAAc,MAA4B;CAClE,MAAM,OAAO,UAAU,MAAM,IAAI;CACjC,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO;CAC9B,IAAI;EACF,OAAO,WAAW,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC,CAAC;CAC1D,QAAQ;EACN,OAAO;CACT;AACF;;;;AAKA,SAAgB,WAAW,OAAoB;CAC7C,cAAc,KAAK;CAEnB,UADY,SAAS,MAAM,IACf,GAAG;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CAC/C,gBAAgB,UAAU,MAAM,MAAM,MAAM,IAAI,GAAG,KAAK;AAC1D;;;;;AAMA,SAAgB,YAAY,OAA4D;CACtF,gBAAgB,MAAM,YAAY;CAClC,IAAI;EACF,WAAW,UAAU,MAAM,MAAM,MAAM,IAAI,CAAC;CAC9C,QAAQ,CAER;AACF;;AAwBA,SAAgB,qBAA8B;CAC5C,MAAM,MAAM,YAAY;CACxB,IAAI,CAAC,WAAW,GAAG,GAAG,OAAO,CAAC;CAC9B,MAAM,MAAe,CAAC;CACtB,KAAK,MAAM,QAAQ,YAAY,GAAG,GAAG;EACnC,IAAI,CAAC,KAAK,SAAS,OAAO,GAAG;EAC7B,IAAI;GACF,MAAM,QAAQ,WAAW,KAAK,MAAM,aAAa,KAAK,KAAK,IAAI,GAAG,MAAM,CAAC,CAAC;GAC1E,IAAI,OAAO,IAAI,KAAK,KAAK;EAC3B,QAAQ,CAER;CACF;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,OAAuB;CACnD,OAAO,CAAC,WAAW,MAAM,IAAI;AAC/B;;;;;;;;AChKA,SAAgB,oBAAoB,KAAkC;CACpE,IAAI,CAAC,OAAO,IAAI,WAAW,OAAO,GAAG,OAAO;CAC5C,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,MAAM,eAAe,oBAClB,OAAO,SAAS,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,GAAA,CAAI,KAAK,CAChE;EACA,MAAM,OAAO,mBAAmB,OAAO,QAAQ;EAC/C,OAAO,aAAa,WAAW,MAAM,KAAK,KAAK,WAAW,MAAM;CAClE,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,SAAgB,8BAA8B,KAAkC;CAC9E,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI,IAAI,WAAW,OAAO,GAAG,OAAO;CACpC,IAAI,oBAAoB,GAAG,GAAG,OAAO;CACrC,OAAO;AACT;;;;;;AAOA,SAAgB,kBAAkB,QAAgC,CAAC,GAAe;CAGhF,IADE,MAAM,aAAa,QAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,aAAa,UAE5E,OAAO;EAAE,MAAM;EAAM,QAAQ;CAAW;CAI1C,IADc,MAAM,SAAS,QAAQ,IAAI,mBAAmB,KAE1D,OAAO,EAAE,MAAM,MAAM;CAGvB,MAAM,MAAM,MAAM,QAAQ,KAAA,IAAY,MAAM,MAAM,QAAQ,IAAI;CAC9D,IAAI,OAAO,8BAA8B,GAAG,GAC1C,OAAO;EAAE,MAAM;EAAM,QAAQ;EAAgB,aAAa;CAAI;CAEhE,OAAO,EAAE,MAAM,MAAM;AACvB;;;ACzDA,SAAS,IAAI,MAAgB,KAA4B;CACvD,IAAI;EACF,OAAO,aAAa,OAAO,MAAM;GAC/B;GACA,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;GAAQ;EACpC,CAAC,CAAC,CAAC,KAAK;CACV,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,aAAa,OAAuB;CAC3C,OAAO,MACJ,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,OAAO,GAAG,CAAC,CACnB,QAAQ,UAAU,EAAE;AACzB;;;;;AAMA,SAAgB,YAAY,MAAuB;CACjD,IAAI,MAAM,OAAO,QAAQ,IAAI;CAC7B,MAAM,QAAQ,QAAQ,QAAQ,IAAI,CAAC;CACnC,MAAM,WAAW,IAAI,CAAC,aAAa,iBAAiB,GAAG,KAAK;CAC5D,IAAI,UAAU,OAAO;CACrB,OAAO;AACT;;;;AAKA,SAAgB,wBAAwB,MAAiC;CACvE,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,WAAW,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;CAE9E,MAAM,YAAY,IAAI,CAAC,aAAa,kBAAkB,GAAG,OAAO;CAChE,IAAI,WAAW,aAAa,SAAS,OAAO,CAAC;CAC7C,IAAI,WAAW;EAEb,MAAM,YAAY,QAAQ,SAAS,SAAS;EAE5C,MAAM,WAAW,SAAS,SAAS,MAAM,SAAS,QAAQ,SAAS,IAAI;EAGvE,MAAM,UAAU,aAAa,SADX,SAAS,QAAQ,MAAM,SAAS,QAAQ,QAAQ,IAAI,QACvB,CAAC;EAChD,IAAI,SAAS,WAAW;CAC1B;CAGA,IAAI,eAAe,aAAa,SAAS,OAAO,CAAC;CACjD,MAAM,UAAU,QAAQ,SAAS,MAAM;CACvC,IAAI,WAAW,OAAO,GACpB,IAAI;EAEF,MAAM,QADU,aAAa,SAAS,MAAM,CAAC,CAAC,KAC1B,CAAC,CAAC,MAAM,kBAAkB;EAC9C,IAAI,QAAQ,IAAI;GAGd,MAAM,QAFS,QAAQ,SAAS,MAAM,EAAE,CAAC,KAAK,CAE3B,CAAC,CAAC,MAAM,OAAO;GAClC,MAAM,QAAQ,MAAM,YAAY,WAAW;GAC3C,IAAI,SAAS,KAAK,MAAM,QAAQ,IAAI;IAClC,MAAM,SAAS,aAAa,MAAM,QAAQ,EAAG;IAC7C,IAAI,QAAQ,eAAe;GAC7B;EACF;CACF,QAAQ,CAER;CAGF,IAAI,CAAC,UAAU,WAAW;CAC1B,IAAI,CAAC,cAAc,eAAe;CAElC,OAAO;EAAE,MAAM;EAAS;EAAU;EAAc;CAAS;AAC3D;;;AChFA,MAAa,eACX;;AAKF,MAAa,uBAAuB;AAEpC,SAAS,gBAA0B;CACjC,MAAM,MAAgB,CAAC;CACvB,IAAI,QAAQ,IAAI,YAAY,IAAI,KAAK,QAAQ,IAAI,UAAU;CAC3D,IAAI,KAAK,QAAQ;CACjB,IAAI,KAAK,KAAK,QAAQ,GAAG,UAAU,OAAO,QAAQ,CAAC;CACnD,OAAO;AACT;AAEA,SAAgB,mBAAkC;CAChD,KAAK,MAAM,OAAO,cAAc,GAAG;EACjC,IAAI,QAAQ,UAAU;GACpB,MAAM,QAAQ,UAAU,SAAS,CAAC,QAAQ,GAAG,EAAE,UAAU,OAAO,CAAC;GACjE,IAAI,MAAM,WAAW,KAAK,MAAM,OAAO,KAAK,GAAG,OAAO,MAAM,OAAO,KAAK;GACxE;EACF;EACA,IAAI,WAAW,GAAG,GAAG,OAAO;CAC9B;CACA,OAAO;AACT;AAEA,SAAgB,mBAA2B;CACzC,MAAM,MAAM,iBAAiB;CAC7B,IAAI,CAAC,KACH,MAAM,IAAI,MAAM,YAAY;CAE9B,OAAO;AACT;;;;AAKA,SAAgB,gBAAgB,MAAgC;CAC9D,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,QAAQ;EACN,MAAM,IAAI,MAAM,gDAAgD,cAAc;CAChF;CACA,IAAI,OAAO,OAAO,SAAS,UACzB,MAAM,IAAI,MAAM,+CAA+C,cAAc;CAE/E,IAAI,OAAO,YAAY,OACrB,MAAM,IAAI,MAAM,gCAAgC,cAAc;CAEhE,OAAO,EAAE,MAAM,OAAO,KAAK;AAC7B;AAEA,SAAS,YAAY,MAAsB;CACzC,OAAO,4CAA4C,KAAK;AAC1D;;;;AAKA,SAAgB,aAAa,MAAM,iBAAiB,GAAoB;CACtE,IAAI;CACJ,IAAI;EACF,SAAS,aAAa,KAAK,CAAC,UAAU,QAAQ,GAAG;GAC/C,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;GAAM;EAClC,CAAC;CACH,SAAS,KAAK;EACZ,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;EAC9D,MAAM,IAAI,MAAM,mCAAmC,OAAO,IAAI,cAAc;CAC9E;CACA,MAAM,EAAE,SAAS,gBAAgB,MAAM;CACvC,OAAO;EAAE;EAAM,UAAU,YAAY,IAAI;EAAG;CAAI;AAClD;;;;AAKA,SAAgB,kBAAkB,MAAM,iBAAiB,GAAoB;CAC3E,IAAI;EACF,OAAO,aAAa,GAAG;CACzB,QAAQ,CAER;CAEA,MAAM,UAAU,UAAU,KAAK,CAAC,SAAS,GAAG;EAC1C,UAAU;EACV,OAAO;GAAC;GAAU;GAAQ;EAAM;CAClC,CAAC;CACD,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MACR,qCAAqC,QAAQ,OAAO,MAC/C,QAAQ,UAAU,QAAQ,UAAU,GAAG,IAAI,cAClD;CAEF,OAAO,aAAa,GAAG;AACzB;AAEA,SAAS,WAAW,MAAsB;CACxC,OAAO,IAAI,KAAK,QAAQ,MAAM,MAAI,EAAE;AACtC;AAEA,SAAS,aAAa,OAAuB;CAC3C,OAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE;AACvC;;;;;AAMA,SAAgB,gBAAgB,cAA8B;CAC5D,OAAO,WAAW,QAAQ,CAAC,CACxB,OAAO,GAAG,qBAAqB,IAAI,cAAc,CAAC,CAClD,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;AAChB;;;;AAKA,eAAsB,eAAe,MAKnB;CAChB,MAAM,WAAW,KAAK,YAAY,gBAAgB,KAAK,YAAY;CACnE,MAAM,SAAS,IAAI,GAAG,OAAO,EAAE,kBAAkB,KAAK,SAAS,CAAC;CAChE,MAAM,OAAO,QAAQ;CACrB,IAAI;EAIF,KAAI,MAHqB,OAAO,MAAM,6CAA6C,CACjF,KAAK,QACP,CAAC,EAAA,CACc,aAAa,GAC1B,MAAM,OAAO,MACX,eAAe,WAAW,KAAK,QAAQ,EAAE,uBAAuB,aAAa,QAAQ,GACvF;OAEA,MAAM,OAAO,MACX,cAAc,WAAW,KAAK,QAAQ,EAAE,uBAAuB,aAAa,QAAQ,GACtF;EAMF,KAAI,MAHmB,OAAO,MAAM,gDAAgD,CAClF,KAAK,YACP,CAAC,EAAA,CACY,aAAa,GACxB,MAAM,OAAO,MACX,mBAAmB,WAAW,KAAK,YAAY,EAAE,SAAS,WAAW,KAAK,QAAQ,GACpF;OAEA,MAAM,OAAO,MACX,kBAAkB,WAAW,KAAK,YAAY,EAAE,YAAY,WAAW,KAAK,QAAQ,GACtF;CAEJ,UAAU;EACR,MAAM,OAAO,IAAI;CACnB;AACF;;;;AAKA,eAAsB,aAAa,MAIjB;CAChB,MAAM,SAAS,IAAI,GAAG,OAAO,EAAE,kBAAkB,KAAK,SAAS,CAAC;CAChE,MAAM,OAAO,QAAQ;CACrB,IAAI;EACF,MAAM,OAAO,MACX;;;;SAKA,CAAC,KAAK,YAAY,CACpB;EACA,MAAM,OAAO,MAAM,2BAA2B,WAAW,KAAK,YAAY,GAAG;EAK7E,KAAI,MAJe,OAAO,MACxB,kGACA,CAAC,KAAK,QAAQ,CAChB,EAAA,CACS,aAAa,GACpB,MAAM,OAAO,MAAM,uBAAuB,WAAW,KAAK,QAAQ,GAAG;CAEzE,UAAU;EACR,MAAM,OAAO,IAAI;CACnB;AACF;AAEA,SAAgB,iBAAiB,MAKtB;CACT,MAAM,WAAW,KAAK,YAAY,gBAAgB,KAAK,YAAY;CAGnE,OAAO,gBAFM,mBAAmB,KAAK,QAEX,EAAE,GADf,mBAAmB,QACE,EAAE,aAAa,KAAK,KAAK,GAAG,KAAK;AACrE;;;ACrMA,SAAS,aAAa,MAAc,MAAc,aAA2B;CAC3E,MAAM,MAAM,SAAS,IAAI;CACzB,UAAU,KAAK;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CAC/C,IAAI,OAAO,gBAAgB,YAAY;CACvC,IAAI,SAAS,QAAQ,QAAQ,qBAAqB,YAAY;CAC9D,cAAc,KAAK,KAAK,GAAG,KAAK,KAAK,GAAG,MAAM,EAAE,MAAM,IAAM,CAAC;AAC/D;AAEA,SAAgB,aAAa,OAAsB;CACjD,OAAO,iBAAiB;EACtB,MAAM,MAAM;EACZ,cAAc,MAAM;EACpB,UAAU,MAAM;CAClB,CAAC;AACH;;;;AAKA,eAAe,eAAe,OAAc,UAAiC;CAC3E,MAAM,aAAa;EACjB;EACA,cAAc,MAAM;EACpB,UAAU,MAAM;CAClB,CAAC;CACD,YAAY,KAAK;AACnB;;;;;;;AA4BA,eAAsB,OAAO,SAA+C;CAC1E,MAAM,WAAW,wBAAwB,QAAQ,IAAI;CACrD,MAAM,OAAO,QAAQ;CACrB,MAAM,eAAe,kBAAkB,UAAU,IAAI;CACrD,MAAM,WAAW,cAAc,YAAY;CAE3C,MAAM,OAAO,kBAAkB;CAC/B,MAAM,eAAe;EACnB,UAAU,KAAK;EACf;EACA;CACF,CAAC;CAED,MAAM,cAAc,iBAAiB;EACnC,MAAM,KAAK;EACX;EACA;CACF,CAAC;CAeD,WAAW;EAZT,eAAe;EACf;EACA,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,cAAc,SAAS;EACvB,UAAU,SAAS;EACnB;EACA;EACA,MAAM,KAAK;EACX,KAAK,QAAQ;EACb,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;CAErB,CAAC;CAChB,aAAa,SAAS,MAAM,MAAM,WAAW;CAE7C,IAAI,QAAQ,WAAW,OAAO;EAC5B,QAAQ,IAAI,eAAe;EAC3B,IAAI,SAAS,QACX,QAAQ,IAAI,oBAAoB;CAEpC;CAEA,MAAM,YAAY,YAAY;EAC5B,MAAM,QAAQ;GAAE,MAAM,SAAS;GAAM;EAAK,CAAC;CAC7C;CAEA,OAAO;EACL;EACA;EACA;EACA,UAAU,SAAS;EACnB,cAAc,SAAS;EACvB,UAAU,SAAS;EACnB,MAAM,SAAS;EACf;EACA,MAAM,KAAK;EACX,SAAS;CACX;AACF;;;;;AAaA,eAAsB,eACpB,SAC+B;CAC/B,MAAM,OAAO,kBAAkB;EAC7B,KAAK,QAAQ;EACb,OAAO,QAAQ;EACf,UAAU,QAAQ;CACpB,CAAC;CACD,IAAI,KAAK,MAAM;EACb,IAAI,KAAK,WAAW,gBAAgB;GAClC,IAAI,QAAQ,WAAW,OACrB,QAAQ,IAAI,eAAe,KAAK;GAElC,OAAO;IAAE,QAAQ;IAAW,QAAQ;IAAgB,aAAa,KAAK;GAAY;EACpF;EACA,OAAO;GAAE,QAAQ;GAAW,QAAQ;EAAW;CACjD;CAOA,OAAO;EAAE,QAAQ;EAAW,GAAG,MALV,OAAO;GAC1B,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,QAAQ,QAAQ;EAClB,CAAC;CACqC;AACxC;;;;;;AAgBA,eAAsB,QAAQ,UAA0B,CAAC,GAA2B;CAClF,MAAM,WAAW,wBAAwB,QAAQ,IAAI;CACrD,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,UAAU,SAAS,MAAM,IAAI;CAC3C,IAAI,CAAC,OAAO,OAAO;EAAE,SAAS;EAAO,QAAQ;CAAW;CAExD,IAAI;CACJ,IAAI;EACF,OAAO,kBAAkB;CAC3B,QAAQ;EAEN,OAAO;GAAE,SAAS;GAAO,QAAQ;EAAmB;CACtD;CAEA,MAAM,eAAe,OAAO,KAAK,QAAQ;CACzC,OAAO;EAAE,SAAS;EAAM,cAAc,MAAM;CAAa;AAC3D;;;;;AAMA,eAAsB,KAEnB;CACD,MAAM,UAAoB,CAAC;CAC3B,MAAM,UAAU,mBAAmB,CAAC,CAAC,OAAO,aAAa;CACzD,IAAI,QAAQ,WAAW,GAAG,OAAO,EAAE,QAAQ;CAE3C,IAAI;CACJ,IAAI;EACF,OAAO,kBAAkB;CAC3B,QAAQ;EACN,OAAO,EAAE,QAAQ;CACnB;CAEA,KAAK,MAAM,SAAS,SAClB,IAAI;EACF,MAAM,eAAe,OAAO,KAAK,QAAQ;EACzC,QAAQ,KAAK,MAAM,YAAY;CACjC,QAAQ,CAER;CAEF,OAAO,EAAE,QAAQ;AACnB"}
package/dist/nx.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_constants = require("./constants-B7sCf4fg.cjs");
3
- const require_tasks = require("./tasks-DZ7hMmGJ.cjs");
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 —\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"}
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-YnHDwShD.mjs";
2
- import { t as cedarPgCommands } from "./tasks-CW2D0n46.mjs";
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 —\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
+ {"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-YnHDwShD.mjs";
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-CW2D0n46.mjs.map
16
+ //# sourceMappingURL=tasks-13P6tMth.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-CW2D0n46.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
+ {"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-B7sCf4fg.cjs");
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-DZ7hMmGJ.cjs.map
21
+ //# sourceMappingURL=tasks-B8ryV9xo.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tasks-DZ7hMmGJ.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"}
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"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./constants-B7sCf4fg.cjs");
3
- const require_tasks = require("./tasks-DZ7hMmGJ.cjs");
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.
@@ -1,5 +1,5 @@
1
- import "./constants-YnHDwShD.mjs";
2
- import { t as cedarPgCommands } from "./tasks-CW2D0n46.mjs";
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-D10usEjM.cjs");
5
+ const require_lifecycle = require("./lifecycle-BGYtVXtx.cjs");
6
6
  //#region src/adapters/vitest.ts
7
7
  /**
8
- * Vitest globalSetup — ensure test DB, return teardown that disposes it.
8
+ * Vitest globalSetup: ensure test DB, return teardown that disposes it.
9
9
  *
10
10
  * vitest.config.ts:
11
11
  * ```ts
@@ -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 — 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"}
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
@@ -1,6 +1,6 @@
1
1
  //#region src/adapters/vitest.d.ts
2
2
  /**
3
- * Vitest globalSetup — ensure test DB, return teardown that disposes it.
3
+ * Vitest globalSetup: ensure test DB, return teardown that disposes it.
4
4
  *
5
5
  * vitest.config.ts:
6
6
  * ```ts
package/dist/vitest.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/adapters/vitest.d.ts
2
2
  /**
3
- * Vitest globalSetup — ensure test DB, return teardown that disposes it.
3
+ * Vitest globalSetup: ensure test DB, return teardown that disposes it.
4
4
  *
5
5
  * vitest.config.ts:
6
6
  * ```ts
package/dist/vitest.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { r as ensureIfNeeded } from "./lifecycle-PbTXNC2Z.mjs";
1
+ import { r as ensureIfNeeded } from "./lifecycle-CT_8AWPv.mjs";
2
2
  //#region src/adapters/vitest.ts
3
3
  /**
4
- * Vitest globalSetup — ensure test DB, return teardown that disposes it.
4
+ * Vitest globalSetup: ensure test DB, return teardown that disposes it.
5
5
  *
6
6
  * vitest.config.ts:
7
7
  * ```ts
@@ -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 — 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"}
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.0",
4
- "description": "Worktree-isolated local Postgres for Vite+/Nx/CedarJS — powered by autopg (embedded PostgreSQL with true concurrent connections)",
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",
@@ -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 — prints a clear warning instead.
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 — do not track main/latest.
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 — CI images often bake autopg
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 — skip auto-install. " +
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 — installing ${AUTOPG_VERSION} via pinned install.sh…`);
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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"}