@cedarjs/pg 0.2.0-alpha.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +300 -230
- package/dist/cli.cjs +99 -43
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +85 -29
- package/dist/cli.mjs.map +1 -1
- package/dist/dev-env.cjs +1 -1
- package/dist/dev-env.mjs +1 -1
- package/dist/index.cjs +9 -8
- package/dist/index.d.cts +80 -9
- package/dist/index.d.mts +80 -9
- package/dist/index.mjs +7 -7
- package/dist/jest-teardown.cjs +1 -1
- package/dist/jest-teardown.mjs +1 -1
- package/dist/jest-template.cjs +7 -2
- package/dist/jest-template.cjs.map +1 -1
- package/dist/jest-template.d.cts +7 -2
- package/dist/jest-template.d.mts +7 -2
- package/dist/jest-template.mjs +7 -2
- package/dist/jest-template.mjs.map +1 -1
- package/dist/jest.cjs +1 -1
- package/dist/jest.mjs +1 -1
- package/dist/{lease-BSBBpaR4.mjs → lease-DS1SX8U_.mjs} +23 -3
- package/dist/lease-DS1SX8U_.mjs.map +1 -0
- package/dist/{lease-Bq9wKwQa.cjs → lease-WlmOnNDi.cjs} +41 -3
- package/dist/lease-WlmOnNDi.cjs.map +1 -0
- package/dist/lifecycle-2BZgSVz2.mjs +1072 -0
- package/dist/lifecycle-2BZgSVz2.mjs.map +1 -0
- package/dist/{lifecycle-DEJ4GgWV.cjs → lifecycle-BCeM96tI.cjs} +517 -117
- package/dist/lifecycle-BCeM96tI.cjs.map +1 -0
- package/dist/{load-dev-env-7UjMHhw7.mjs → load-dev-env-BULkXyen.mjs} +2 -2
- package/dist/{load-dev-env-7UjMHhw7.mjs.map → load-dev-env-BULkXyen.mjs.map} +1 -1
- package/dist/{load-dev-env-cII6N4ZP.cjs → load-dev-env-Bl6Ddv1U.cjs} +2 -2
- package/dist/{load-dev-env-cII6N4ZP.cjs.map → load-dev-env-Bl6Ddv1U.cjs.map} +1 -1
- package/dist/{load-mode-env-B0qO9DZx.cjs → load-mode-env-CX9vd56X.cjs} +2 -2
- package/dist/{load-mode-env-B0qO9DZx.cjs.map → load-mode-env-CX9vd56X.cjs.map} +1 -1
- package/dist/{load-mode-env-C8gy4v9V.mjs → load-mode-env-Ce9ujisN.mjs} +2 -2
- package/dist/{load-mode-env-C8gy4v9V.mjs.map → load-mode-env-Ce9ujisN.mjs.map} +1 -1
- package/dist/{load-test-env-C34wpkIX.mjs → load-test-env-BqtA6V-d.mjs} +2 -2
- package/dist/{load-test-env-C34wpkIX.mjs.map → load-test-env-BqtA6V-d.mjs.map} +1 -1
- package/dist/{load-test-env-CkdUjTpV.cjs → load-test-env-Dcgh6YHV.cjs} +2 -2
- package/dist/{load-test-env-CkdUjTpV.cjs.map → load-test-env-Dcgh6YHV.cjs.map} +1 -1
- package/dist/{naming-C2nGVxPk.d.cts → naming-Df4fz_Fb.d.cts} +3 -2
- package/dist/{naming-C2nGVxPk.d.mts → naming-Df4fz_Fb.d.mts} +3 -2
- package/dist/status-B68SGSKx.mjs +26 -0
- package/dist/status-B68SGSKx.mjs.map +1 -0
- package/dist/status-Z_BYdxzI.cjs +31 -0
- package/dist/status-Z_BYdxzI.cjs.map +1 -0
- package/dist/studio-Bi6SU9jE.mjs +221 -0
- package/dist/studio-Bi6SU9jE.mjs.map +1 -0
- package/dist/studio-CdWIdnxr.cjs +280 -0
- package/dist/studio-CdWIdnxr.cjs.map +1 -0
- package/dist/{template-D0gJ_MS2.cjs → template-Cd77H7Cr.cjs} +21 -18
- package/dist/template-Cd77H7Cr.cjs.map +1 -0
- package/dist/{template-CGw3C1Ob.mjs → template-DlqHL8Go.mjs} +21 -18
- package/dist/template-DlqHL8Go.mjs.map +1 -0
- package/dist/template-mode-BqV3GjXI.d.cts +48 -0
- package/dist/template-mode-BqV3GjXI.d.mts +48 -0
- package/dist/template-mode-DK5cAlv9.mjs +81 -0
- package/dist/template-mode-DK5cAlv9.mjs.map +1 -0
- package/dist/template-mode-DMO5-z7O.cjs +92 -0
- package/dist/template-mode-DMO5-z7O.cjs.map +1 -0
- package/dist/test-env.cjs +1 -1
- package/dist/test-env.mjs +1 -1
- package/dist/vite-plus.cjs +113 -10
- package/dist/vite-plus.cjs.map +1 -1
- package/dist/vite-plus.d.cts +41 -2
- package/dist/vite-plus.d.mts +41 -2
- package/dist/vite-plus.mjs +110 -7
- package/dist/vite-plus.mjs.map +1 -1
- package/dist/vitest-template.cjs +6 -3
- package/dist/vitest-template.cjs.map +1 -1
- package/dist/vitest-template.d.cts +6 -3
- package/dist/vitest-template.d.mts +6 -3
- package/dist/vitest-template.mjs +6 -3
- package/dist/vitest-template.mjs.map +1 -1
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/package.json +11 -7
- package/scripts/autopg-version +1 -1
- package/scripts/ci-install-autopg.sh +7 -2
- package/scripts/postinstall.js +41 -13
- package/dist/constants-Ct8myrEn.cjs +0 -41
- package/dist/constants-Ct8myrEn.cjs.map +0 -1
- package/dist/constants-NLR0U4NR.mjs +0 -24
- package/dist/constants-NLR0U4NR.mjs.map +0 -1
- package/dist/lease-B3TuX92y.d.mts +0 -24
- package/dist/lease-BSBBpaR4.mjs.map +0 -1
- package/dist/lease-Bq9wKwQa.cjs.map +0 -1
- package/dist/lease-t4I9JahV.d.cts +0 -24
- package/dist/lifecycle-BOo6xBjD.mjs +0 -684
- package/dist/lifecycle-BOo6xBjD.mjs.map +0 -1
- package/dist/lifecycle-DEJ4GgWV.cjs.map +0 -1
- package/dist/nx.cjs +0 -44
- package/dist/nx.cjs.map +0 -1
- package/dist/nx.d.cts +0 -15
- package/dist/nx.d.mts +0 -15
- package/dist/nx.mjs +0 -36
- package/dist/nx.mjs.map +0 -1
- package/dist/tasks-Caud9yHr.cjs +0 -67
- package/dist/tasks-Caud9yHr.cjs.map +0 -1
- package/dist/tasks-CjaZRi_G.d.mts +0 -19
- package/dist/tasks-DdQoP9We.d.cts +0 -19
- package/dist/tasks-KherHLac.mjs +0 -38
- package/dist/tasks-KherHLac.mjs.map +0 -1
- package/dist/template-CGw3C1Ob.mjs.map +0 -1
- package/dist/template-D0gJ_MS2.cjs.map +0 -1
- package/dist/template-mode-BkEs9LnY.cjs +0 -91
- package/dist/template-mode-BkEs9LnY.cjs.map +0 -1
- package/dist/template-mode-CcxV_Iju.d.cts +0 -28
- package/dist/template-mode-CcxV_Iju.d.mts +0 -28
- package/dist/template-mode-IjlFOG4O.mjs +0 -80
- package/dist/template-mode-IjlFOG4O.mjs.map +0 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { p as CLI_NAME } from "./lease-DS1SX8U_.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { constants } from "node:os";
|
|
5
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
//#region src/adapters/child.ts
|
|
9
|
+
const FORWARDED_SIGNALS = [
|
|
10
|
+
"SIGINT",
|
|
11
|
+
"SIGTERM",
|
|
12
|
+
"SIGHUP"
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Attached child: inherit stdio, forward SIGINT / SIGTERM / SIGHUP to the
|
|
16
|
+
* child while it runs, resolve with its exit code (killed by signal → 128 + n).
|
|
17
|
+
*/
|
|
18
|
+
function runAttached(command, args, options = {}) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(command, args, {
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
cwd: options.cwd,
|
|
23
|
+
env: options.env,
|
|
24
|
+
shell: options.shell
|
|
25
|
+
});
|
|
26
|
+
const forward = (signal) => child.kill(signal);
|
|
27
|
+
for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);
|
|
28
|
+
const detach = () => {
|
|
29
|
+
for (const signal of FORWARDED_SIGNALS) process.off(signal, forward);
|
|
30
|
+
};
|
|
31
|
+
child.once("error", (error) => {
|
|
32
|
+
detach();
|
|
33
|
+
reject(error);
|
|
34
|
+
});
|
|
35
|
+
child.once("exit", (code, signal) => {
|
|
36
|
+
detach();
|
|
37
|
+
resolve(signal ? 128 + constants.signals[signal] : code ?? 1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/adapters/tasks.ts
|
|
43
|
+
/** Vite+ lifecycle task names and command strings (also used by the status hint). */
|
|
44
|
+
const CEDAR_PG_TASK_ACQUIRE_DEV = "db:acquire";
|
|
45
|
+
const CEDAR_PG_TASK_ACQUIRE_TEST = "db:acquire-test";
|
|
46
|
+
const CEDAR_PG_TASK_DISPOSE_TEST = "db:dispose-test";
|
|
47
|
+
function cedarPgCommands(bin = CLI_NAME) {
|
|
48
|
+
return {
|
|
49
|
+
acquireDev: `${bin} acquire --mode=dev`,
|
|
50
|
+
acquireTest: `${bin} acquire --mode=test --print-env`,
|
|
51
|
+
disposeTest: `${bin} dispose --mode=test`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function cedarPgLifecycleTargets(options = {}) {
|
|
55
|
+
const cmds = cedarPgCommands(options.bin ?? "cedarpg");
|
|
56
|
+
const targets = {
|
|
57
|
+
[CEDAR_PG_TASK_ACQUIRE_DEV]: {
|
|
58
|
+
command: cmds.acquireDev,
|
|
59
|
+
cache: false
|
|
60
|
+
},
|
|
61
|
+
[CEDAR_PG_TASK_ACQUIRE_TEST]: {
|
|
62
|
+
command: cmds.acquireTest,
|
|
63
|
+
cache: false
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (options.includeDisposeTest !== false) targets[CEDAR_PG_TASK_DISPOSE_TEST] = {
|
|
67
|
+
command: cmds.disposeTest,
|
|
68
|
+
cache: false
|
|
69
|
+
};
|
|
70
|
+
return targets;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/adapters/status-format.ts
|
|
74
|
+
/** Human-readable lines for the Vite panel / `cedarpg status`. */
|
|
75
|
+
function formatDevStatus(status) {
|
|
76
|
+
if (!status.ok) {
|
|
77
|
+
const task = status.mode === "test" ? CEDAR_PG_TASK_ACQUIRE_TEST : CEDAR_PG_TASK_ACQUIRE_DEV;
|
|
78
|
+
return [`${CLI_NAME}: no ${status.mode} lease at ${status.root}`, ` run \`${CLI_NAME} acquire --mode=${status.mode}\` (or depend on ${task})`];
|
|
79
|
+
}
|
|
80
|
+
const { lease, databaseUrl, envPath } = status;
|
|
81
|
+
return [
|
|
82
|
+
`${CLI_NAME}: ${lease.databaseName} (${lease.repoSlug}/${lease.worktreeSlug} ${lease.mode})`,
|
|
83
|
+
` port ${lease.port}`,
|
|
84
|
+
` DATABASE_URL ${databaseUrl}`,
|
|
85
|
+
` env ${envPath}`
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/adapters/studio.ts
|
|
90
|
+
const STUDIO_PACKAGE = {
|
|
91
|
+
prisma: "prisma",
|
|
92
|
+
drizzle: "drizzle-kit"
|
|
93
|
+
};
|
|
94
|
+
const AUTO_ORDER = ["prisma", "drizzle"];
|
|
95
|
+
function readPackageJson(dir) {
|
|
96
|
+
const file = join(dir, "package.json");
|
|
97
|
+
if (!existsSync(file)) return null;
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function hasDep(pkg, name) {
|
|
105
|
+
if (!pkg) return false;
|
|
106
|
+
return Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name] || pkg.optionalDependencies?.[name]);
|
|
107
|
+
}
|
|
108
|
+
function tryResolveBin(dir, packageName) {
|
|
109
|
+
try {
|
|
110
|
+
const pkgJsonPath = createRequire(pathToFileURL(join(dir, "package.json")).href).resolve(`${packageName}/package.json`);
|
|
111
|
+
const binField = JSON.parse(readFileSync(pkgJsonPath, "utf8")).bin;
|
|
112
|
+
let rel;
|
|
113
|
+
if (typeof binField === "string") rel = binField;
|
|
114
|
+
else if (binField && typeof binField === "object") rel = binField[packageName] ?? Object.values(binField)[0];
|
|
115
|
+
if (!rel) return null;
|
|
116
|
+
const abs = join(dirname(pkgJsonPath), rel);
|
|
117
|
+
return existsSync(abs) ? abs : null;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function npxStudio(kind, cwd) {
|
|
123
|
+
return {
|
|
124
|
+
kind,
|
|
125
|
+
command: "npx",
|
|
126
|
+
args: [STUDIO_PACKAGE[kind], "studio"],
|
|
127
|
+
cwd,
|
|
128
|
+
shell: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Local bin, else declared dep → npx, else null. Resolves the bin at most once. */
|
|
132
|
+
function studioAt(kind, dir) {
|
|
133
|
+
const pkg = STUDIO_PACKAGE[kind];
|
|
134
|
+
const bin = tryResolveBin(dir, pkg);
|
|
135
|
+
if (bin) return {
|
|
136
|
+
kind,
|
|
137
|
+
command: bin,
|
|
138
|
+
args: ["studio"],
|
|
139
|
+
cwd: dir,
|
|
140
|
+
shell: false
|
|
141
|
+
};
|
|
142
|
+
if (hasDep(readPackageJson(dir), pkg)) return npxStudio(kind, dir);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
function isInside(dir, ancestor) {
|
|
146
|
+
const rel = relative(ancestor, dir);
|
|
147
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
148
|
+
}
|
|
149
|
+
/** `start` up to `stop` (inclusive) when `start` is inside the worktree; otherwise only `start`. */
|
|
150
|
+
function searchDirs(start, stop) {
|
|
151
|
+
if (!isInside(start, stop)) return [start];
|
|
152
|
+
const dirs = [];
|
|
153
|
+
let dir = start;
|
|
154
|
+
for (;;) {
|
|
155
|
+
dirs.push(dir);
|
|
156
|
+
if (dir === stop) return dirs;
|
|
157
|
+
const parent = dirname(dir);
|
|
158
|
+
if (parent === dir) return dirs;
|
|
159
|
+
dir = parent;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Detect Prisma Studio or Drizzle Kit Studio, walking from `cwd` up to the worktree `root`.
|
|
164
|
+
*
|
|
165
|
+
* Auto: nearest directory wins, prisma before drizzle in that directory; no npx fallback.
|
|
166
|
+
* Forced `prefer`: same nearest walk for that kind only, then npx at `cwd`.
|
|
167
|
+
*/
|
|
168
|
+
function detectStudio(options) {
|
|
169
|
+
const stop = resolve(options.root);
|
|
170
|
+
const start = resolve(options.cwd ?? options.root);
|
|
171
|
+
const dirs = searchDirs(start, stop);
|
|
172
|
+
const kinds = options.prefer ? [options.prefer] : AUTO_ORDER;
|
|
173
|
+
for (const dir of dirs) for (const kind of kinds) {
|
|
174
|
+
const hit = studioAt(kind, dir);
|
|
175
|
+
if (hit) return hit;
|
|
176
|
+
}
|
|
177
|
+
return options.prefer ? npxStudio(options.prefer, start) : null;
|
|
178
|
+
}
|
|
179
|
+
function spawnStudio(options, spawnOpts) {
|
|
180
|
+
const { databaseUrl, studio } = options;
|
|
181
|
+
return spawn(studio.command, studio.args, {
|
|
182
|
+
cwd: studio.cwd,
|
|
183
|
+
env: {
|
|
184
|
+
...process.env,
|
|
185
|
+
DATABASE_URL: databaseUrl
|
|
186
|
+
},
|
|
187
|
+
...spawnOpts,
|
|
188
|
+
shell: studio.shell
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Detached Studio for the Vite shortcut. Caller attaches `error` / `exit` on the
|
|
193
|
+
* returned process (spawn failures emit on a later tick).
|
|
194
|
+
*/
|
|
195
|
+
function openStudio(options) {
|
|
196
|
+
const child = spawnStudio(options, {
|
|
197
|
+
detached: true,
|
|
198
|
+
stdio: "ignore"
|
|
199
|
+
});
|
|
200
|
+
child.unref();
|
|
201
|
+
return child;
|
|
202
|
+
}
|
|
203
|
+
/** Attached Studio for `cedarpg studio`: inherit stdio, resolve with the child's exit code. */
|
|
204
|
+
function runStudio(options) {
|
|
205
|
+
const { databaseUrl, studio } = options;
|
|
206
|
+
return runAttached(studio.command, studio.args, {
|
|
207
|
+
cwd: studio.cwd,
|
|
208
|
+
env: {
|
|
209
|
+
...process.env,
|
|
210
|
+
DATABASE_URL: databaseUrl
|
|
211
|
+
},
|
|
212
|
+
shell: studio.shell
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function studioChildRunning(child) {
|
|
216
|
+
return Boolean(child && child.exitCode === null && !child.killed);
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
export { formatDevStatus as a, CEDAR_PG_TASK_DISPOSE_TEST as c, studioChildRunning as i, cedarPgLifecycleTargets as l, openStudio as n, CEDAR_PG_TASK_ACQUIRE_DEV as o, runStudio as r, CEDAR_PG_TASK_ACQUIRE_TEST as s, detectStudio as t, runAttached as u };
|
|
220
|
+
|
|
221
|
+
//# sourceMappingURL=studio-Bi6SU9jE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio-Bi6SU9jE.mjs","names":[],"sources":["../src/adapters/child.ts","../src/adapters/tasks.ts","../src/adapters/status-format.ts","../src/adapters/studio.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:os\";\n\nexport type RunAttachedOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n shell?: boolean;\n};\n\nconst FORWARDED_SIGNALS = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const;\n\n/**\n * Attached child: inherit stdio, forward SIGINT / SIGTERM / SIGHUP to the\n * child while it runs, resolve with its exit code (killed by signal → 128 + n).\n */\nexport function runAttached(\n command: string,\n args: string[],\n options: RunAttachedOptions = {},\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n stdio: \"inherit\",\n cwd: options.cwd,\n env: options.env,\n shell: options.shell,\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);\n const detach = () => {\n for (const signal of FORWARDED_SIGNALS) process.off(signal, forward);\n };\n child.once(\"error\", (error) => {\n detach();\n reject(error);\n });\n child.once(\"exit\", (code, signal) => {\n detach();\n resolve(signal ? 128 + constants.signals[signal] : (code ?? 1));\n });\n });\n}\n","/** Vite+ lifecycle task names and command strings (also used by the status hint). */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\n\nexport const CEDAR_PG_TASK_ACQUIRE_DEV = \"db:acquire\";\nexport const CEDAR_PG_TASK_ACQUIRE_TEST = \"db:acquire-test\";\nexport const CEDAR_PG_TASK_DISPOSE_TEST = \"db:dispose-test\";\n\nexport type CedarPgCommands = {\n acquireDev: string;\n acquireTest: string;\n disposeTest: string;\n};\n\nexport type CedarPgLifecycleTarget = {\n command: string;\n cache?: boolean;\n};\n\nexport type CedarPgLifecycleTargetsOptions = {\n bin?: string;\n /** Include dispose-test target (default: true). */\n includeDisposeTest?: boolean;\n};\n\nexport function cedarPgCommands(bin = CLI_NAME): CedarPgCommands {\n return {\n acquireDev: `${bin} acquire --mode=dev`,\n acquireTest: `${bin} acquire --mode=test --print-env`,\n disposeTest: `${bin} dispose --mode=test`,\n };\n}\n\nexport function cedarPgLifecycleTargets(\n options: CedarPgLifecycleTargetsOptions = {},\n): Record<string, CedarPgLifecycleTarget> {\n const cmds = cedarPgCommands(options.bin ?? CLI_NAME);\n const targets: Record<string, CedarPgLifecycleTarget> = {\n [CEDAR_PG_TASK_ACQUIRE_DEV]: { command: cmds.acquireDev, cache: false },\n [CEDAR_PG_TASK_ACQUIRE_TEST]: { command: cmds.acquireTest, cache: false },\n };\n if (options.includeDisposeTest !== false) {\n targets[CEDAR_PG_TASK_DISPOSE_TEST] = { command: cmds.disposeTest, cache: false };\n }\n return targets;\n}\n","import { CLI_NAME } from \"../core/constants.ts\";\nimport type { DevStatus } from \"../core/status.ts\";\nimport { CEDAR_PG_TASK_ACQUIRE_DEV, CEDAR_PG_TASK_ACQUIRE_TEST } from \"./tasks.ts\";\n\n/** Human-readable lines for the Vite panel / `cedarpg status`. */\nexport function formatDevStatus(status: DevStatus): string[] {\n if (!status.ok) {\n const task = status.mode === \"test\" ? CEDAR_PG_TASK_ACQUIRE_TEST : CEDAR_PG_TASK_ACQUIRE_DEV;\n return [\n `${CLI_NAME}: no ${status.mode} lease at ${status.root}`,\n ` run \\`${CLI_NAME} acquire --mode=${status.mode}\\` (or depend on ${task})`,\n ];\n }\n const { lease, databaseUrl, envPath } = status;\n return [\n `${CLI_NAME}: ${lease.databaseName} (${lease.repoSlug}/${lease.worktreeSlug} ${lease.mode})`,\n ` port ${lease.port}`,\n ` DATABASE_URL ${databaseUrl}`,\n ` env ${envPath}`,\n ];\n}\n","import { createRequire } from \"node:module\";\nimport { spawn, type ChildProcess, type SpawnOptions } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { runAttached } from \"./child.ts\";\n\nexport type StudioKind = \"prisma\" | \"drizzle\";\n\nconst STUDIO_PACKAGE: Record<StudioKind, string> = {\n prisma: \"prisma\",\n drizzle: \"drizzle-kit\",\n};\n\nconst AUTO_ORDER: StudioKind[] = [\"prisma\", \"drizzle\"];\n\nexport type DetectStudioOptions = {\n /** Worktree / lease root — directory walk stops here. */\n root: string;\n /** App package to start the walk (Nx `apps/…`). Defaults to `root`. */\n cwd?: string;\n /** Force a kind. Omit to auto-detect (nearest package; prisma if both). */\n prefer?: StudioKind;\n};\n\nexport type DetectedStudio = {\n kind: StudioKind;\n command: string;\n args: string[];\n /** Directory to spawn in (where the ORM package / config was found). */\n cwd: string;\n /** True when falling back to `npx`. */\n shell: boolean;\n};\n\ntype PkgJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n};\n\nfunction readPackageJson(dir: string): PkgJson | null {\n const file = join(dir, \"package.json\");\n if (!existsSync(file)) return null;\n try {\n return JSON.parse(readFileSync(file, \"utf8\")) as PkgJson;\n } catch {\n return null;\n }\n}\n\nfunction hasDep(pkg: PkgJson | null, name: string): boolean {\n if (!pkg) return false;\n return Boolean(\n pkg.dependencies?.[name] || pkg.devDependencies?.[name] || pkg.optionalDependencies?.[name],\n );\n}\n\nfunction tryResolveBin(dir: string, packageName: string): string | null {\n try {\n const require = createRequire(pathToFileURL(join(dir, \"package.json\")).href);\n const pkgJsonPath = require.resolve(`${packageName}/package.json`);\n const pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf8\")) as {\n bin?: string | Record<string, string>;\n };\n const binField = pkg.bin;\n let rel: string | undefined;\n if (typeof binField === \"string\") rel = binField;\n else if (binField && typeof binField === \"object\") {\n rel = binField[packageName] ?? Object.values(binField)[0];\n }\n if (!rel) return null;\n const abs = join(dirname(pkgJsonPath), rel);\n return existsSync(abs) ? abs : null;\n } catch {\n return null;\n }\n}\n\nfunction npxStudio(kind: StudioKind, cwd: string): DetectedStudio {\n return {\n kind,\n command: \"npx\",\n args: [STUDIO_PACKAGE[kind], \"studio\"],\n cwd,\n shell: true,\n };\n}\n\n/** Local bin, else declared dep → npx, else null. Resolves the bin at most once. */\nfunction studioAt(kind: StudioKind, dir: string): DetectedStudio | null {\n const pkg = STUDIO_PACKAGE[kind];\n const bin = tryResolveBin(dir, pkg);\n if (bin) return { kind, command: bin, args: [\"studio\"], cwd: dir, shell: false };\n if (hasDep(readPackageJson(dir), pkg)) return npxStudio(kind, dir);\n return null;\n}\n\nfunction isInside(dir: string, ancestor: string): boolean {\n const rel = relative(ancestor, dir);\n return rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\n\n/** `start` up to `stop` (inclusive) when `start` is inside the worktree; otherwise only `start`. */\nfunction searchDirs(start: string, stop: string): string[] {\n if (!isInside(start, stop)) return [start];\n const dirs: string[] = [];\n let dir = start;\n for (;;) {\n dirs.push(dir);\n if (dir === stop) return dirs;\n const parent = dirname(dir);\n if (parent === dir) return dirs;\n dir = parent;\n }\n}\n\n/**\n * Detect Prisma Studio or Drizzle Kit Studio, walking from `cwd` up to the worktree `root`.\n *\n * Auto: nearest directory wins, prisma before drizzle in that directory; no npx fallback.\n * Forced `prefer`: same nearest walk for that kind only, then npx at `cwd`.\n */\nexport function detectStudio(options: DetectStudioOptions): DetectedStudio | null {\n const stop = resolve(options.root);\n const start = resolve(options.cwd ?? options.root);\n const dirs = searchDirs(start, stop);\n const kinds = options.prefer ? [options.prefer] : AUTO_ORDER;\n\n for (const dir of dirs) {\n for (const kind of kinds) {\n const hit = studioAt(kind, dir);\n if (hit) return hit;\n }\n }\n return options.prefer ? npxStudio(options.prefer, start) : null;\n}\n\nexport type SpawnStudioOptions = {\n databaseUrl: string;\n studio: DetectedStudio;\n};\n\nfunction spawnStudio(options: SpawnStudioOptions, spawnOpts: SpawnOptions): ChildProcess {\n const { databaseUrl, studio } = options;\n return spawn(studio.command, studio.args, {\n cwd: studio.cwd,\n env: { ...process.env, DATABASE_URL: databaseUrl },\n ...spawnOpts,\n shell: studio.shell,\n });\n}\n\n/**\n * Detached Studio for the Vite shortcut. Caller attaches `error` / `exit` on the\n * returned process (spawn failures emit on a later tick).\n */\nexport function openStudio(options: SpawnStudioOptions): ChildProcess {\n const child = spawnStudio(options, { detached: true, stdio: \"ignore\" });\n child.unref();\n return child;\n}\n\n/** Attached Studio for `cedarpg studio`: inherit stdio, resolve with the child's exit code. */\nexport function runStudio(options: SpawnStudioOptions): Promise<number> {\n const { databaseUrl, studio } = options;\n return runAttached(studio.command, studio.args, {\n cwd: studio.cwd,\n env: { ...process.env, DATABASE_URL: databaseUrl },\n shell: studio.shell,\n });\n}\n\nexport function studioChildRunning(child: ChildProcess | undefined): boolean {\n return Boolean(child && child.exitCode === null && !child.killed);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const require_lease = require("./lease-WlmOnNDi.cjs");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_os = require("node:os");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_child_process = require("node:child_process");
|
|
6
|
+
let node_module = require("node:module");
|
|
7
|
+
let node_url = require("node:url");
|
|
8
|
+
//#region src/adapters/child.ts
|
|
9
|
+
const FORWARDED_SIGNALS = [
|
|
10
|
+
"SIGINT",
|
|
11
|
+
"SIGTERM",
|
|
12
|
+
"SIGHUP"
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Attached child: inherit stdio, forward SIGINT / SIGTERM / SIGHUP to the
|
|
16
|
+
* child while it runs, resolve with its exit code (killed by signal → 128 + n).
|
|
17
|
+
*/
|
|
18
|
+
function runAttached(command, args, options = {}) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = (0, node_child_process.spawn)(command, args, {
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
cwd: options.cwd,
|
|
23
|
+
env: options.env,
|
|
24
|
+
shell: options.shell
|
|
25
|
+
});
|
|
26
|
+
const forward = (signal) => child.kill(signal);
|
|
27
|
+
for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);
|
|
28
|
+
const detach = () => {
|
|
29
|
+
for (const signal of FORWARDED_SIGNALS) process.off(signal, forward);
|
|
30
|
+
};
|
|
31
|
+
child.once("error", (error) => {
|
|
32
|
+
detach();
|
|
33
|
+
reject(error);
|
|
34
|
+
});
|
|
35
|
+
child.once("exit", (code, signal) => {
|
|
36
|
+
detach();
|
|
37
|
+
resolve(signal ? 128 + node_os.constants.signals[signal] : code ?? 1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/adapters/tasks.ts
|
|
43
|
+
/** Vite+ lifecycle task names and command strings (also used by the status hint). */
|
|
44
|
+
const CEDAR_PG_TASK_ACQUIRE_DEV = "db:acquire";
|
|
45
|
+
const CEDAR_PG_TASK_ACQUIRE_TEST = "db:acquire-test";
|
|
46
|
+
const CEDAR_PG_TASK_DISPOSE_TEST = "db:dispose-test";
|
|
47
|
+
function cedarPgCommands(bin = require_lease.CLI_NAME) {
|
|
48
|
+
return {
|
|
49
|
+
acquireDev: `${bin} acquire --mode=dev`,
|
|
50
|
+
acquireTest: `${bin} acquire --mode=test --print-env`,
|
|
51
|
+
disposeTest: `${bin} dispose --mode=test`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function cedarPgLifecycleTargets(options = {}) {
|
|
55
|
+
const cmds = cedarPgCommands(options.bin ?? "cedarpg");
|
|
56
|
+
const targets = {
|
|
57
|
+
[CEDAR_PG_TASK_ACQUIRE_DEV]: {
|
|
58
|
+
command: cmds.acquireDev,
|
|
59
|
+
cache: false
|
|
60
|
+
},
|
|
61
|
+
[CEDAR_PG_TASK_ACQUIRE_TEST]: {
|
|
62
|
+
command: cmds.acquireTest,
|
|
63
|
+
cache: false
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (options.includeDisposeTest !== false) targets[CEDAR_PG_TASK_DISPOSE_TEST] = {
|
|
67
|
+
command: cmds.disposeTest,
|
|
68
|
+
cache: false
|
|
69
|
+
};
|
|
70
|
+
return targets;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/adapters/status-format.ts
|
|
74
|
+
/** Human-readable lines for the Vite panel / `cedarpg status`. */
|
|
75
|
+
function formatDevStatus(status) {
|
|
76
|
+
if (!status.ok) {
|
|
77
|
+
const task = status.mode === "test" ? CEDAR_PG_TASK_ACQUIRE_TEST : CEDAR_PG_TASK_ACQUIRE_DEV;
|
|
78
|
+
return [`${require_lease.CLI_NAME}: no ${status.mode} lease at ${status.root}`, ` run \`${require_lease.CLI_NAME} acquire --mode=${status.mode}\` (or depend on ${task})`];
|
|
79
|
+
}
|
|
80
|
+
const { lease, databaseUrl, envPath } = status;
|
|
81
|
+
return [
|
|
82
|
+
`${require_lease.CLI_NAME}: ${lease.databaseName} (${lease.repoSlug}/${lease.worktreeSlug} ${lease.mode})`,
|
|
83
|
+
` port ${lease.port}`,
|
|
84
|
+
` DATABASE_URL ${databaseUrl}`,
|
|
85
|
+
` env ${envPath}`
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/adapters/studio.ts
|
|
90
|
+
const STUDIO_PACKAGE = {
|
|
91
|
+
prisma: "prisma",
|
|
92
|
+
drizzle: "drizzle-kit"
|
|
93
|
+
};
|
|
94
|
+
const AUTO_ORDER = ["prisma", "drizzle"];
|
|
95
|
+
function readPackageJson(dir) {
|
|
96
|
+
const file = (0, node_path.join)(dir, "package.json");
|
|
97
|
+
if (!(0, node_fs.existsSync)(file)) return null;
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse((0, node_fs.readFileSync)(file, "utf8"));
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function hasDep(pkg, name) {
|
|
105
|
+
if (!pkg) return false;
|
|
106
|
+
return Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name] || pkg.optionalDependencies?.[name]);
|
|
107
|
+
}
|
|
108
|
+
function tryResolveBin(dir, packageName) {
|
|
109
|
+
try {
|
|
110
|
+
const pkgJsonPath = (0, node_module.createRequire)((0, node_url.pathToFileURL)((0, node_path.join)(dir, "package.json")).href).resolve(`${packageName}/package.json`);
|
|
111
|
+
const binField = JSON.parse((0, node_fs.readFileSync)(pkgJsonPath, "utf8")).bin;
|
|
112
|
+
let rel;
|
|
113
|
+
if (typeof binField === "string") rel = binField;
|
|
114
|
+
else if (binField && typeof binField === "object") rel = binField[packageName] ?? Object.values(binField)[0];
|
|
115
|
+
if (!rel) return null;
|
|
116
|
+
const abs = (0, node_path.join)((0, node_path.dirname)(pkgJsonPath), rel);
|
|
117
|
+
return (0, node_fs.existsSync)(abs) ? abs : null;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function npxStudio(kind, cwd) {
|
|
123
|
+
return {
|
|
124
|
+
kind,
|
|
125
|
+
command: "npx",
|
|
126
|
+
args: [STUDIO_PACKAGE[kind], "studio"],
|
|
127
|
+
cwd,
|
|
128
|
+
shell: true
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Local bin, else declared dep → npx, else null. Resolves the bin at most once. */
|
|
132
|
+
function studioAt(kind, dir) {
|
|
133
|
+
const pkg = STUDIO_PACKAGE[kind];
|
|
134
|
+
const bin = tryResolveBin(dir, pkg);
|
|
135
|
+
if (bin) return {
|
|
136
|
+
kind,
|
|
137
|
+
command: bin,
|
|
138
|
+
args: ["studio"],
|
|
139
|
+
cwd: dir,
|
|
140
|
+
shell: false
|
|
141
|
+
};
|
|
142
|
+
if (hasDep(readPackageJson(dir), pkg)) return npxStudio(kind, dir);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
function isInside(dir, ancestor) {
|
|
146
|
+
const rel = (0, node_path.relative)(ancestor, dir);
|
|
147
|
+
return rel === "" || !rel.startsWith("..") && !(0, node_path.isAbsolute)(rel);
|
|
148
|
+
}
|
|
149
|
+
/** `start` up to `stop` (inclusive) when `start` is inside the worktree; otherwise only `start`. */
|
|
150
|
+
function searchDirs(start, stop) {
|
|
151
|
+
if (!isInside(start, stop)) return [start];
|
|
152
|
+
const dirs = [];
|
|
153
|
+
let dir = start;
|
|
154
|
+
for (;;) {
|
|
155
|
+
dirs.push(dir);
|
|
156
|
+
if (dir === stop) return dirs;
|
|
157
|
+
const parent = (0, node_path.dirname)(dir);
|
|
158
|
+
if (parent === dir) return dirs;
|
|
159
|
+
dir = parent;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Detect Prisma Studio or Drizzle Kit Studio, walking from `cwd` up to the worktree `root`.
|
|
164
|
+
*
|
|
165
|
+
* Auto: nearest directory wins, prisma before drizzle in that directory; no npx fallback.
|
|
166
|
+
* Forced `prefer`: same nearest walk for that kind only, then npx at `cwd`.
|
|
167
|
+
*/
|
|
168
|
+
function detectStudio(options) {
|
|
169
|
+
const stop = (0, node_path.resolve)(options.root);
|
|
170
|
+
const start = (0, node_path.resolve)(options.cwd ?? options.root);
|
|
171
|
+
const dirs = searchDirs(start, stop);
|
|
172
|
+
const kinds = options.prefer ? [options.prefer] : AUTO_ORDER;
|
|
173
|
+
for (const dir of dirs) for (const kind of kinds) {
|
|
174
|
+
const hit = studioAt(kind, dir);
|
|
175
|
+
if (hit) return hit;
|
|
176
|
+
}
|
|
177
|
+
return options.prefer ? npxStudio(options.prefer, start) : null;
|
|
178
|
+
}
|
|
179
|
+
function spawnStudio(options, spawnOpts) {
|
|
180
|
+
const { databaseUrl, studio } = options;
|
|
181
|
+
return (0, node_child_process.spawn)(studio.command, studio.args, {
|
|
182
|
+
cwd: studio.cwd,
|
|
183
|
+
env: {
|
|
184
|
+
...process.env,
|
|
185
|
+
DATABASE_URL: databaseUrl
|
|
186
|
+
},
|
|
187
|
+
...spawnOpts,
|
|
188
|
+
shell: studio.shell
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Detached Studio for the Vite shortcut. Caller attaches `error` / `exit` on the
|
|
193
|
+
* returned process (spawn failures emit on a later tick).
|
|
194
|
+
*/
|
|
195
|
+
function openStudio(options) {
|
|
196
|
+
const child = spawnStudio(options, {
|
|
197
|
+
detached: true,
|
|
198
|
+
stdio: "ignore"
|
|
199
|
+
});
|
|
200
|
+
child.unref();
|
|
201
|
+
return child;
|
|
202
|
+
}
|
|
203
|
+
/** Attached Studio for `cedarpg studio`: inherit stdio, resolve with the child's exit code. */
|
|
204
|
+
function runStudio(options) {
|
|
205
|
+
const { databaseUrl, studio } = options;
|
|
206
|
+
return runAttached(studio.command, studio.args, {
|
|
207
|
+
cwd: studio.cwd,
|
|
208
|
+
env: {
|
|
209
|
+
...process.env,
|
|
210
|
+
DATABASE_URL: databaseUrl
|
|
211
|
+
},
|
|
212
|
+
shell: studio.shell
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function studioChildRunning(child) {
|
|
216
|
+
return Boolean(child && child.exitCode === null && !child.killed);
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
Object.defineProperty(exports, "CEDAR_PG_TASK_ACQUIRE_DEV", {
|
|
220
|
+
enumerable: true,
|
|
221
|
+
get: function() {
|
|
222
|
+
return CEDAR_PG_TASK_ACQUIRE_DEV;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
Object.defineProperty(exports, "CEDAR_PG_TASK_ACQUIRE_TEST", {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
get: function() {
|
|
228
|
+
return CEDAR_PG_TASK_ACQUIRE_TEST;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
Object.defineProperty(exports, "CEDAR_PG_TASK_DISPOSE_TEST", {
|
|
232
|
+
enumerable: true,
|
|
233
|
+
get: function() {
|
|
234
|
+
return CEDAR_PG_TASK_DISPOSE_TEST;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
Object.defineProperty(exports, "cedarPgLifecycleTargets", {
|
|
238
|
+
enumerable: true,
|
|
239
|
+
get: function() {
|
|
240
|
+
return cedarPgLifecycleTargets;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
Object.defineProperty(exports, "detectStudio", {
|
|
244
|
+
enumerable: true,
|
|
245
|
+
get: function() {
|
|
246
|
+
return detectStudio;
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
Object.defineProperty(exports, "formatDevStatus", {
|
|
250
|
+
enumerable: true,
|
|
251
|
+
get: function() {
|
|
252
|
+
return formatDevStatus;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
Object.defineProperty(exports, "openStudio", {
|
|
256
|
+
enumerable: true,
|
|
257
|
+
get: function() {
|
|
258
|
+
return openStudio;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
Object.defineProperty(exports, "runAttached", {
|
|
262
|
+
enumerable: true,
|
|
263
|
+
get: function() {
|
|
264
|
+
return runAttached;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
Object.defineProperty(exports, "runStudio", {
|
|
268
|
+
enumerable: true,
|
|
269
|
+
get: function() {
|
|
270
|
+
return runStudio;
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
Object.defineProperty(exports, "studioChildRunning", {
|
|
274
|
+
enumerable: true,
|
|
275
|
+
get: function() {
|
|
276
|
+
return studioChildRunning;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
//# sourceMappingURL=studio-CdWIdnxr.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio-CdWIdnxr.cjs","names":["constants","CLI_NAME","CLI_NAME"],"sources":["../src/adapters/child.ts","../src/adapters/tasks.ts","../src/adapters/status-format.ts","../src/adapters/studio.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:os\";\n\nexport type RunAttachedOptions = {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n shell?: boolean;\n};\n\nconst FORWARDED_SIGNALS = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const;\n\n/**\n * Attached child: inherit stdio, forward SIGINT / SIGTERM / SIGHUP to the\n * child while it runs, resolve with its exit code (killed by signal → 128 + n).\n */\nexport function runAttached(\n command: string,\n args: string[],\n options: RunAttachedOptions = {},\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n stdio: \"inherit\",\n cwd: options.cwd,\n env: options.env,\n shell: options.shell,\n });\n const forward = (signal: NodeJS.Signals) => child.kill(signal);\n for (const signal of FORWARDED_SIGNALS) process.on(signal, forward);\n const detach = () => {\n for (const signal of FORWARDED_SIGNALS) process.off(signal, forward);\n };\n child.once(\"error\", (error) => {\n detach();\n reject(error);\n });\n child.once(\"exit\", (code, signal) => {\n detach();\n resolve(signal ? 128 + constants.signals[signal] : (code ?? 1));\n });\n });\n}\n","/** Vite+ lifecycle task names and command strings (also used by the status hint). */\n\nimport { CLI_NAME } from \"../core/constants.ts\";\n\nexport const CEDAR_PG_TASK_ACQUIRE_DEV = \"db:acquire\";\nexport const CEDAR_PG_TASK_ACQUIRE_TEST = \"db:acquire-test\";\nexport const CEDAR_PG_TASK_DISPOSE_TEST = \"db:dispose-test\";\n\nexport type CedarPgCommands = {\n acquireDev: string;\n acquireTest: string;\n disposeTest: string;\n};\n\nexport type CedarPgLifecycleTarget = {\n command: string;\n cache?: boolean;\n};\n\nexport type CedarPgLifecycleTargetsOptions = {\n bin?: string;\n /** Include dispose-test target (default: true). */\n includeDisposeTest?: boolean;\n};\n\nexport function cedarPgCommands(bin = CLI_NAME): CedarPgCommands {\n return {\n acquireDev: `${bin} acquire --mode=dev`,\n acquireTest: `${bin} acquire --mode=test --print-env`,\n disposeTest: `${bin} dispose --mode=test`,\n };\n}\n\nexport function cedarPgLifecycleTargets(\n options: CedarPgLifecycleTargetsOptions = {},\n): Record<string, CedarPgLifecycleTarget> {\n const cmds = cedarPgCommands(options.bin ?? CLI_NAME);\n const targets: Record<string, CedarPgLifecycleTarget> = {\n [CEDAR_PG_TASK_ACQUIRE_DEV]: { command: cmds.acquireDev, cache: false },\n [CEDAR_PG_TASK_ACQUIRE_TEST]: { command: cmds.acquireTest, cache: false },\n };\n if (options.includeDisposeTest !== false) {\n targets[CEDAR_PG_TASK_DISPOSE_TEST] = { command: cmds.disposeTest, cache: false };\n }\n return targets;\n}\n","import { CLI_NAME } from \"../core/constants.ts\";\nimport type { DevStatus } from \"../core/status.ts\";\nimport { CEDAR_PG_TASK_ACQUIRE_DEV, CEDAR_PG_TASK_ACQUIRE_TEST } from \"./tasks.ts\";\n\n/** Human-readable lines for the Vite panel / `cedarpg status`. */\nexport function formatDevStatus(status: DevStatus): string[] {\n if (!status.ok) {\n const task = status.mode === \"test\" ? CEDAR_PG_TASK_ACQUIRE_TEST : CEDAR_PG_TASK_ACQUIRE_DEV;\n return [\n `${CLI_NAME}: no ${status.mode} lease at ${status.root}`,\n ` run \\`${CLI_NAME} acquire --mode=${status.mode}\\` (or depend on ${task})`,\n ];\n }\n const { lease, databaseUrl, envPath } = status;\n return [\n `${CLI_NAME}: ${lease.databaseName} (${lease.repoSlug}/${lease.worktreeSlug} ${lease.mode})`,\n ` port ${lease.port}`,\n ` DATABASE_URL ${databaseUrl}`,\n ` env ${envPath}`,\n ];\n}\n","import { createRequire } from \"node:module\";\nimport { spawn, type ChildProcess, type SpawnOptions } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { runAttached } from \"./child.ts\";\n\nexport type StudioKind = \"prisma\" | \"drizzle\";\n\nconst STUDIO_PACKAGE: Record<StudioKind, string> = {\n prisma: \"prisma\",\n drizzle: \"drizzle-kit\",\n};\n\nconst AUTO_ORDER: StudioKind[] = [\"prisma\", \"drizzle\"];\n\nexport type DetectStudioOptions = {\n /** Worktree / lease root — directory walk stops here. */\n root: string;\n /** App package to start the walk (Nx `apps/…`). Defaults to `root`. */\n cwd?: string;\n /** Force a kind. Omit to auto-detect (nearest package; prisma if both). */\n prefer?: StudioKind;\n};\n\nexport type DetectedStudio = {\n kind: StudioKind;\n command: string;\n args: string[];\n /** Directory to spawn in (where the ORM package / config was found). */\n cwd: string;\n /** True when falling back to `npx`. */\n shell: boolean;\n};\n\ntype PkgJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n};\n\nfunction readPackageJson(dir: string): PkgJson | null {\n const file = join(dir, \"package.json\");\n if (!existsSync(file)) return null;\n try {\n return JSON.parse(readFileSync(file, \"utf8\")) as PkgJson;\n } catch {\n return null;\n }\n}\n\nfunction hasDep(pkg: PkgJson | null, name: string): boolean {\n if (!pkg) return false;\n return Boolean(\n pkg.dependencies?.[name] || pkg.devDependencies?.[name] || pkg.optionalDependencies?.[name],\n );\n}\n\nfunction tryResolveBin(dir: string, packageName: string): string | null {\n try {\n const require = createRequire(pathToFileURL(join(dir, \"package.json\")).href);\n const pkgJsonPath = require.resolve(`${packageName}/package.json`);\n const pkg = JSON.parse(readFileSync(pkgJsonPath, \"utf8\")) as {\n bin?: string | Record<string, string>;\n };\n const binField = pkg.bin;\n let rel: string | undefined;\n if (typeof binField === \"string\") rel = binField;\n else if (binField && typeof binField === \"object\") {\n rel = binField[packageName] ?? Object.values(binField)[0];\n }\n if (!rel) return null;\n const abs = join(dirname(pkgJsonPath), rel);\n return existsSync(abs) ? abs : null;\n } catch {\n return null;\n }\n}\n\nfunction npxStudio(kind: StudioKind, cwd: string): DetectedStudio {\n return {\n kind,\n command: \"npx\",\n args: [STUDIO_PACKAGE[kind], \"studio\"],\n cwd,\n shell: true,\n };\n}\n\n/** Local bin, else declared dep → npx, else null. Resolves the bin at most once. */\nfunction studioAt(kind: StudioKind, dir: string): DetectedStudio | null {\n const pkg = STUDIO_PACKAGE[kind];\n const bin = tryResolveBin(dir, pkg);\n if (bin) return { kind, command: bin, args: [\"studio\"], cwd: dir, shell: false };\n if (hasDep(readPackageJson(dir), pkg)) return npxStudio(kind, dir);\n return null;\n}\n\nfunction isInside(dir: string, ancestor: string): boolean {\n const rel = relative(ancestor, dir);\n return rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\n\n/** `start` up to `stop` (inclusive) when `start` is inside the worktree; otherwise only `start`. */\nfunction searchDirs(start: string, stop: string): string[] {\n if (!isInside(start, stop)) return [start];\n const dirs: string[] = [];\n let dir = start;\n for (;;) {\n dirs.push(dir);\n if (dir === stop) return dirs;\n const parent = dirname(dir);\n if (parent === dir) return dirs;\n dir = parent;\n }\n}\n\n/**\n * Detect Prisma Studio or Drizzle Kit Studio, walking from `cwd` up to the worktree `root`.\n *\n * Auto: nearest directory wins, prisma before drizzle in that directory; no npx fallback.\n * Forced `prefer`: same nearest walk for that kind only, then npx at `cwd`.\n */\nexport function detectStudio(options: DetectStudioOptions): DetectedStudio | null {\n const stop = resolve(options.root);\n const start = resolve(options.cwd ?? options.root);\n const dirs = searchDirs(start, stop);\n const kinds = options.prefer ? [options.prefer] : AUTO_ORDER;\n\n for (const dir of dirs) {\n for (const kind of kinds) {\n const hit = studioAt(kind, dir);\n if (hit) return hit;\n }\n }\n return options.prefer ? npxStudio(options.prefer, start) : null;\n}\n\nexport type SpawnStudioOptions = {\n databaseUrl: string;\n studio: DetectedStudio;\n};\n\nfunction spawnStudio(options: SpawnStudioOptions, spawnOpts: SpawnOptions): ChildProcess {\n const { databaseUrl, studio } = options;\n return spawn(studio.command, studio.args, {\n cwd: studio.cwd,\n env: { ...process.env, DATABASE_URL: databaseUrl },\n ...spawnOpts,\n shell: studio.shell,\n });\n}\n\n/**\n * Detached Studio for the Vite shortcut. Caller attaches `error` / `exit` on the\n * returned process (spawn failures emit on a later tick).\n */\nexport function openStudio(options: SpawnStudioOptions): ChildProcess {\n const child = spawnStudio(options, { detached: true, stdio: \"ignore\" });\n child.unref();\n return child;\n}\n\n/** Attached Studio for `cedarpg studio`: inherit stdio, resolve with the child's exit code. */\nexport function runStudio(options: SpawnStudioOptions): Promise<number> {\n const { databaseUrl, studio } = options;\n return runAttached(studio.command, studio.args, {\n cwd: studio.cwd,\n env: { ...process.env, DATABASE_URL: databaseUrl },\n shell: studio.shell,\n });\n}\n\nexport function studioChildRunning(child: ChildProcess | undefined): boolean {\n return Boolean(child && child.exitCode === null && !child.killed);\n}\n"],"mappings":"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"}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
const require_lifecycle = require("./lifecycle-
|
|
2
|
-
const require_lease = require("./lease-Bq9wKwQa.cjs");
|
|
1
|
+
const require_lifecycle = require("./lifecycle-BCeM96tI.cjs");
|
|
3
2
|
//#region src/core/template.ts
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Lease + admin URL the way `attach` connects: lease file, one TCP probe on the
|
|
5
|
+
* leased port, never host discovery / start / revive. Workers call this once per
|
|
6
|
+
* test file, so it must stay cheap (no git, no `autopg` processes).
|
|
7
|
+
*/
|
|
8
|
+
async function attachAdmin(options) {
|
|
9
|
+
const { lease } = await require_lifecycle.attach(options);
|
|
10
|
+
return {
|
|
11
|
+
lease,
|
|
12
|
+
adminUrl: options.adminUrl ?? require_lifecycle.adminUrlFor(lease.port)
|
|
13
|
+
};
|
|
6
14
|
}
|
|
7
15
|
/**
|
|
8
16
|
* After migrations, mark the leased DB as a PostgreSQL TEMPLATE so workers can clone it.
|
|
9
17
|
* Requires a lease from `acquire` (no datname override).
|
|
10
18
|
*/
|
|
11
19
|
async function markTemplate(options) {
|
|
12
|
-
const
|
|
13
|
-
const mode = options.mode;
|
|
14
|
-
const lease = require_lease.readLease(identity.root, mode);
|
|
15
|
-
if (!lease) throw new Error(`no ${mode} lease; run acquire before markTemplate`);
|
|
16
|
-
const adminUrl = await resolveAdminUrl(options.adminUrl);
|
|
20
|
+
const { lease, adminUrl } = await attachAdmin(options);
|
|
17
21
|
await require_lifecycle.setDatabaseIsTemplate({
|
|
18
22
|
adminUrl,
|
|
19
23
|
databaseName: lease.databaseName,
|
|
@@ -28,28 +32,27 @@ async function markTemplate(options) {
|
|
|
28
32
|
* Clone the leased TEMPLATE database via admin (`CREATE DATABASE … TEMPLATE`).
|
|
29
33
|
* Reuses the template role so `databaseUrl` passwords stay valid (scheme v2).
|
|
30
34
|
* Provider rejects when the leased DB is not marked TEMPLATE.
|
|
31
|
-
* Port comes from the lease; admin URL is passed through or
|
|
35
|
+
* Port comes from the lease; admin URL is passed through or built from the lease
|
|
36
|
+
* port. Fails when nothing listens there (setup should have acquired).
|
|
37
|
+
* An existing clone datname fails unless `reuse` is set and the lease role owns it.
|
|
32
38
|
*/
|
|
33
39
|
async function cloneFromTemplate(options) {
|
|
34
|
-
const
|
|
35
|
-
const mode = options.mode;
|
|
36
|
-
const lease = require_lease.readLease(identity.root, mode);
|
|
37
|
-
if (!lease) throw new Error(`no ${mode} lease; run acquire + markTemplate before cloneFromTemplate`);
|
|
38
|
-
const adminUrl = await resolveAdminUrl(options.adminUrl);
|
|
40
|
+
const { lease, adminUrl } = await attachAdmin(options);
|
|
39
41
|
const suffix = options.name ?? `${process.pid}_${Date.now().toString(36)}`;
|
|
40
42
|
const databaseName = require_lifecycle.buildCloneDatabaseName(lease.databaseName, suffix);
|
|
41
43
|
await require_lifecycle.cloneDatabaseFromTemplate({
|
|
42
44
|
adminUrl,
|
|
43
45
|
templateName: lease.databaseName,
|
|
44
46
|
databaseName,
|
|
45
|
-
roleName: lease.roleName
|
|
47
|
+
roleName: lease.roleName,
|
|
48
|
+
reuse: options.reuse
|
|
46
49
|
});
|
|
47
50
|
const databaseUrl = require_lifecycle.buildDatabaseUrl({
|
|
48
51
|
port: lease.port,
|
|
49
52
|
databaseName,
|
|
50
53
|
roleName: lease.roleName
|
|
51
54
|
});
|
|
52
|
-
if (options.setEnv) require_lifecycle.applyDatabaseUrlEnv(databaseUrl, { mode });
|
|
55
|
+
if (options.setEnv) require_lifecycle.applyDatabaseUrlEnv(databaseUrl, { mode: options.mode });
|
|
53
56
|
const roleName = lease.roleName;
|
|
54
57
|
return {
|
|
55
58
|
databaseUrl,
|
|
@@ -102,4 +105,4 @@ Object.defineProperty(exports, "markTemplate", {
|
|
|
102
105
|
}
|
|
103
106
|
});
|
|
104
107
|
|
|
105
|
-
//# sourceMappingURL=template-
|
|
108
|
+
//# sourceMappingURL=template-Cd77H7Cr.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-Cd77H7Cr.cjs","names":["attach","adminUrlFor","setDatabaseIsTemplate","buildCloneDatabaseName","cloneDatabaseFromTemplate","buildDatabaseUrl","dropDatabase","runIfNeeded"],"sources":["../src/core/template.ts"],"sourcesContent":["import { buildCloneDatabaseName, type DbMode } from \"./naming.ts\";\nimport type { Lease } from \"./lease.ts\";\nimport { attach } from \"./lifecycle.ts\";\nimport { applyDatabaseUrlEnv, runIfNeeded, type ResolveAcquireSkipInput } from \"./policy.ts\";\nimport {\n adminUrlFor,\n buildDatabaseUrl,\n cloneDatabaseFromTemplate,\n dropDatabase,\n setDatabaseIsTemplate,\n} from \"../providers/autopg.ts\";\n\n/**\n * Lease + admin URL the way `attach` connects: lease file, one TCP probe on the\n * leased port, never host discovery / start / revive. Workers call this once per\n * test file, so it must stay cheap (no git, no `autopg` processes).\n */\nasync function attachAdmin(options: {\n root?: string;\n mode: DbMode;\n adminUrl?: string;\n}): Promise<{ lease: Lease; adminUrl: string }> {\n const { lease } = await attach(options);\n return { lease, adminUrl: options.adminUrl ?? adminUrlFor(lease.port) };\n}\n\nexport type MarkTemplateOptions = {\n root?: string;\n mode: DbMode;\n /** Superuser URL from `acquire`; when omitted, built from the lease port (never starts the host). */\n adminUrl?: string;\n};\n\n/**\n * After migrations, mark the leased DB as a PostgreSQL TEMPLATE so workers can clone it.\n * Requires a lease from `acquire` (no datname override).\n */\nexport async function markTemplate(\n options: MarkTemplateOptions,\n): Promise<{ databaseName: string; adminUrl: string }> {\n const { lease, adminUrl } = await attachAdmin(options);\n await setDatabaseIsTemplate({\n adminUrl,\n databaseName: lease.databaseName,\n isTemplate: true,\n });\n return { databaseName: lease.databaseName, adminUrl };\n}\n\nexport type CloneFromTemplateOptions = {\n root?: string;\n mode: DbMode;\n /** Superuser URL from `acquire`; when omitted, built from the lease port (never starts the host). */\n adminUrl?: string;\n /**\n * Suffix for the clone datname (e.g. Jest worker id).\n * Defaults to `<pid>_<base36 time>`.\n */\n name?: string;\n /**\n * Keep an existing `<template>_c_<name>` owned by the lease role instead of\n * failing with `database already exists` (default false). Worker adapters pass\n * true so every test file in one worker attaches to the same clone.\n */\n reuse?: boolean;\n /**\n * Inject DATABASE_URL / TEST_DATABASE_URL for this clone (default false).\n * Host `cloneFromTemplateIfNeeded` defaults true; worker adapters pass true explicitly.\n */\n setEnv?: boolean;\n};\n\nexport type CloneResult = {\n databaseUrl: string;\n adminUrl: string;\n databaseName: string;\n roleName: string;\n templateName: string;\n port: number;\n /**\n * DROP this clone only (leaves TEMPLATE + role if still owned elsewhere).\n * Not suite teardown — use role-scoped `dispose` for that.\n */\n dropClone: () => Promise<void>;\n};\n\n/**\n * Clone the leased TEMPLATE database via admin (`CREATE DATABASE … TEMPLATE`).\n * Reuses the template role so `databaseUrl` passwords stay valid (scheme v2).\n * Provider rejects when the leased DB is not marked TEMPLATE.\n * Port comes from the lease; admin URL is passed through or built from the lease\n * port. Fails when nothing listens there (setup should have acquired).\n * An existing clone datname fails unless `reuse` is set and the lease role owns it.\n */\nexport async function cloneFromTemplate(options: CloneFromTemplateOptions): Promise<CloneResult> {\n const { lease, adminUrl } = await attachAdmin(options);\n const suffix = options.name ?? `${process.pid}_${Date.now().toString(36)}`;\n const databaseName = buildCloneDatabaseName(lease.databaseName, suffix);\n\n await cloneDatabaseFromTemplate({\n adminUrl,\n templateName: lease.databaseName,\n databaseName,\n roleName: lease.roleName,\n reuse: options.reuse,\n });\n\n const databaseUrl = buildDatabaseUrl({\n port: lease.port,\n databaseName,\n roleName: lease.roleName,\n });\n\n if (options.setEnv) {\n applyDatabaseUrlEnv(databaseUrl, { mode: options.mode });\n }\n\n const roleName = lease.roleName;\n\n return {\n databaseUrl,\n adminUrl,\n databaseName,\n roleName,\n templateName: lease.databaseName,\n port: lease.port,\n dropClone: async () => {\n await dropDatabase({ adminUrl, databaseName, roleName });\n },\n };\n}\n\nexport type CloneFromTemplateIfNeededOptions = CloneFromTemplateOptions & ResolveAcquireSkipInput;\n\nexport type CloneFromTemplateIfNeededResult =\n | { status: \"skipped\"; reason: \"disabled\" }\n | { status: \"skipped\"; reason: \"external-url\"; databaseUrl: string }\n | ({ status: \"cloned\" } & CloneResult);\n\n/**\n * Resolve skip policy then clone. Host entry for worker adapters (same skip\n * semantics as `acquireIfNeeded`). Defaults `setEnv` on for skip and clone paths.\n */\nexport async function cloneFromTemplateIfNeeded(\n options: CloneFromTemplateIfNeededOptions,\n): Promise<CloneFromTemplateIfNeededResult> {\n const outcome = await runIfNeeded(options, () =>\n cloneFromTemplate({\n ...options,\n setEnv: options.setEnv !== false,\n }),\n );\n if (outcome.status === \"skipped\") return outcome;\n return { status: \"cloned\", ...outcome.value };\n}\n"],"mappings":";;;;;;;AAiBA,eAAe,YAAY,SAIqB;CAC9C,MAAM,EAAE,UAAU,MAAMA,kBAAAA,OAAO,OAAO;CACtC,OAAO;EAAE;EAAO,UAAU,QAAQ,YAAYC,kBAAAA,YAAY,MAAM,IAAI;CAAE;AACxE;;;;;AAaA,eAAsB,aACpB,SACqD;CACrD,MAAM,EAAE,OAAO,aAAa,MAAM,YAAY,OAAO;CACrD,MAAMC,kBAAAA,sBAAsB;EAC1B;EACA,cAAc,MAAM;EACpB,YAAY;CACd,CAAC;CACD,OAAO;EAAE,cAAc,MAAM;EAAc;CAAS;AACtD;;;;;;;;;AA+CA,eAAsB,kBAAkB,SAAyD;CAC/F,MAAM,EAAE,OAAO,aAAa,MAAM,YAAY,OAAO;CACrD,MAAM,SAAS,QAAQ,QAAQ,GAAG,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE;CACvE,MAAM,eAAeC,kBAAAA,uBAAuB,MAAM,cAAc,MAAM;CAEtE,MAAMC,kBAAAA,0BAA0B;EAC9B;EACA,cAAc,MAAM;EACpB;EACA,UAAU,MAAM;EAChB,OAAO,QAAQ;CACjB,CAAC;CAED,MAAM,cAAcC,kBAAAA,iBAAiB;EACnC,MAAM,MAAM;EACZ;EACA,UAAU,MAAM;CAClB,CAAC;CAED,IAAI,QAAQ,QACV,kBAAA,oBAAoB,aAAa,EAAE,MAAM,QAAQ,KAAK,CAAC;CAGzD,MAAM,WAAW,MAAM;CAEvB,OAAO;EACL;EACA;EACA;EACA;EACA,cAAc,MAAM;EACpB,MAAM,MAAM;EACZ,WAAW,YAAY;GACrB,MAAMC,kBAAAA,aAAa;IAAE;IAAU;IAAc;GAAS,CAAC;EACzD;CACF;AACF;;;;;AAaA,eAAsB,0BACpB,SAC0C;CAC1C,MAAM,UAAU,MAAMC,kBAAAA,YAAY,eAChC,kBAAkB;EAChB,GAAG;EACH,QAAQ,QAAQ,WAAW;CAC7B,CAAC,CACH;CACA,IAAI,QAAQ,WAAW,WAAW,OAAO;CACzC,OAAO;EAAE,QAAQ;EAAU,GAAG,QAAQ;CAAM;AAC9C"}
|