@zenbujs/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +11 -0
- package/dist/advice-config-CjgkEf2E.mjs +135 -0
- package/dist/advice-config-Cy133IQP.mjs +2 -0
- package/dist/advice-runtime.d.mts +35 -0
- package/dist/advice-runtime.mjs +131 -0
- package/dist/advice.d.mts +36 -0
- package/dist/advice.mjs +2 -0
- package/dist/base-window-BUt8pwbw.mjs +94 -0
- package/dist/base-window-DEIAk618.mjs +2 -0
- package/dist/build-config-pbv0w4oN.mjs +17 -0
- package/dist/build-electron-B4Gd0Gi4.mjs +516 -0
- package/dist/build-source-_q1n1zTV.mjs +162 -0
- package/dist/chunk-Dm34NbLt.mjs +6 -0
- package/dist/cli/bin.d.mts +1 -0
- package/dist/cli/bin.mjs +88 -0
- package/dist/cli/build.d.mts +53 -0
- package/dist/cli/build.mjs +48 -0
- package/dist/cli-BLbQQIVB.mjs +8054 -0
- package/dist/config-CdVrW85P.mjs +59 -0
- package/dist/config-LK73dJmO.mjs +2 -0
- package/dist/db-ByKPbnP6.mjs +2 -0
- package/dist/db-DhuAJrye.mjs +531 -0
- package/dist/db.d.mts +16 -0
- package/dist/db.mjs +16 -0
- package/dist/dev-BuqklM0k.mjs +85 -0
- package/dist/env-bootstrap-BtVME-CU.d.mts +16 -0
- package/dist/env-bootstrap-rj7I-59x.mjs +53 -0
- package/dist/env-bootstrap.d.mts +2 -0
- package/dist/env-bootstrap.mjs +2 -0
- package/dist/http-IBcLzbYu.mjs +2 -0
- package/dist/index-Bhlbyrn7.d.mts +63 -0
- package/dist/index-CPZ5d6Hl.d.mts +442 -0
- package/dist/index-FtE8MXJ_.d.mts +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +5 -0
- package/dist/launcher.mjs +173 -0
- package/dist/link-6roQ7Cn6.mjs +580 -0
- package/dist/loaders/zenbu.d.mts +22 -0
- package/dist/loaders/zenbu.mjs +267 -0
- package/dist/log-CyKv8hQg.mjs +20 -0
- package/dist/mirror-sync-CodOnwkD.mjs +332 -0
- package/dist/monorepo-CmGPHsVm.mjs +119 -0
- package/dist/node-D4M19_mV.mjs +5 -0
- package/dist/node-loader.d.mts +17 -0
- package/dist/node-loader.mjs +33 -0
- package/dist/pause-DvAUNmKn.mjs +52 -0
- package/dist/publish-source-BVgB62Zj.mjs +131 -0
- package/dist/react.d.mts +76 -0
- package/dist/react.mjs +291 -0
- package/dist/registry-Dh_e7HU1.d.mts +61 -0
- package/dist/registry.d.mts +2 -0
- package/dist/registry.mjs +1 -0
- package/dist/reloader-BCkLjDhS.mjs +2 -0
- package/dist/reloader-lLAJ3lqg.mjs +164 -0
- package/dist/renderer-host-Bg8QdeeH.mjs +1508 -0
- package/dist/renderer-host-DpvBPTHJ.mjs +2 -0
- package/dist/rpc-BwwQK6hD.mjs +71 -0
- package/dist/rpc-CqitnyR4.mjs +2 -0
- package/dist/rpc.d.mts +2 -0
- package/dist/rpc.mjs +2 -0
- package/dist/runtime-CjqDr8Yf.d.mts +109 -0
- package/dist/runtime-DUFKDIe4.mjs +409 -0
- package/dist/runtime.d.mts +2 -0
- package/dist/runtime.mjs +2 -0
- package/dist/schema-CIg4GzHQ.mjs +100 -0
- package/dist/schema-DMoSkwUx.d.mts +62 -0
- package/dist/schema-dGK6qkfR.mjs +28 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +2 -0
- package/dist/server-BXwZEQ-n.mjs +66 -0
- package/dist/server-DjrZUbbu.mjs +2 -0
- package/dist/services/default.d.mts +11 -0
- package/dist/services/default.mjs +22 -0
- package/dist/services/index.d.mts +276 -0
- package/dist/services/index.mjs +7 -0
- package/dist/setup-gate-BeD6WS6d.mjs +110 -0
- package/dist/setup-gate-BqOzm7zp.d.mts +4 -0
- package/dist/setup-gate.d.mts +2 -0
- package/dist/setup-gate.mjs +2 -0
- package/dist/src-pELM4_iH.mjs +376 -0
- package/dist/trace-DCB7qFzT.mjs +10 -0
- package/dist/transform-DJH3vN4b.mjs +84041 -0
- package/dist/transport-BMSzG2-F.mjs +1045 -0
- package/dist/view-registry-BualWgAf.mjs +2 -0
- package/dist/vite-plugins-Bh3SCOw-.mjs +331 -0
- package/dist/vite.d.mts +68 -0
- package/dist/vite.mjs +2 -0
- package/dist/window-CM2a9Kyc.mjs +2 -0
- package/dist/window-CmmpCVX6.mjs +156 -0
- package/dist/write-9dRFczGJ.mjs +1248 -0
- package/migrations/0000_migration.ts +34 -0
- package/migrations/meta/0000_snapshot.json +18 -0
- package/migrations/meta/_journal.json +10 -0
- package/package.json +124 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
//#region ../kyju/src/cli/config.ts
|
|
6
|
+
const localRequire = createRequire(import.meta.url);
|
|
7
|
+
const CONFIG_NAMES = [
|
|
8
|
+
"db.config.ts",
|
|
9
|
+
"db.config.js",
|
|
10
|
+
"db.config.mjs"
|
|
11
|
+
];
|
|
12
|
+
function findConfigFile(cwd) {
|
|
13
|
+
for (const name of CONFIG_NAMES) {
|
|
14
|
+
const p = path.join(cwd, name);
|
|
15
|
+
if (fs.existsSync(p)) return p;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`No db config found. Expected one of: ${CONFIG_NAMES.join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
async function loadConfig(configPath) {
|
|
20
|
+
const dir = path.dirname(path.resolve(configPath));
|
|
21
|
+
const mod = await loadModule(path.resolve(configPath));
|
|
22
|
+
const config = mod.default ?? mod;
|
|
23
|
+
if (!config.schema) throw new Error("db config must specify a 'schema' path");
|
|
24
|
+
return {
|
|
25
|
+
schemaPath: path.resolve(dir, config.schema),
|
|
26
|
+
outPath: path.resolve(dir, config.out ?? "./db"),
|
|
27
|
+
alias: config.alias
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function loadSchema(schemaPath) {
|
|
31
|
+
const mod = await loadModule(schemaPath);
|
|
32
|
+
const schema = mod.schema ?? mod.default;
|
|
33
|
+
if (!schema || !schema.shape) throw new Error(`Schema file must export a 'schema' (via named export or default) created with createSchema(). Got: ${typeof schema}`);
|
|
34
|
+
return schema;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load a user-authored TS/ESM module (config, schema, migrations entry).
|
|
38
|
+
* Uses tsx's ESM loader (registered once, lazily) so the loaded module can
|
|
39
|
+
* `import` from packages whose `exports` map only declares an ESM `import`
|
|
40
|
+
* condition — like `@zenbujs/core/db`. The legacy CJS path was incompatible
|
|
41
|
+
* with ESM-only deps because `require()` won't resolve `.mjs` files.
|
|
42
|
+
*/
|
|
43
|
+
async function loadModule(modulePath) {
|
|
44
|
+
await ensureTsxRegistered();
|
|
45
|
+
return import(pathToFileURL(path.resolve(modulePath)).href);
|
|
46
|
+
}
|
|
47
|
+
let tsxRegistered = null;
|
|
48
|
+
function ensureTsxRegistered() {
|
|
49
|
+
if (tsxRegistered) return tsxRegistered;
|
|
50
|
+
tsxRegistered = (async () => {
|
|
51
|
+
try {
|
|
52
|
+
const tsxApi = localRequire("tsx/esm/api");
|
|
53
|
+
if (typeof tsxApi.register === "function") tsxApi.register();
|
|
54
|
+
} catch {}
|
|
55
|
+
})();
|
|
56
|
+
return tsxRegistered;
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { loadSchema as i, loadConfig as n, loadModule as r, findConfigFile as t };
|
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import fs, { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import os, { homedir } from "node:os";
|
|
3
|
+
import path, { join, resolve } from "node:path";
|
|
4
|
+
import fsp from "node:fs/promises";
|
|
5
|
+
//#region src/cli/lib/db-registry.ts
|
|
6
|
+
const INTERNAL_DIR = path.join(os.homedir(), ".zenbu", ".internal");
|
|
7
|
+
const DB_CONFIG_JSON = path.join(INTERNAL_DIR, "db.json");
|
|
8
|
+
const DEFAULT_REGISTRY = {
|
|
9
|
+
defaultDbPath: null,
|
|
10
|
+
dbs: []
|
|
11
|
+
};
|
|
12
|
+
function normalize(p) {
|
|
13
|
+
return path.resolve(p);
|
|
14
|
+
}
|
|
15
|
+
async function ensureDir() {
|
|
16
|
+
await fsp.mkdir(INTERNAL_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
async function loadRegistry() {
|
|
19
|
+
let raw;
|
|
20
|
+
try {
|
|
21
|
+
raw = JSON.parse(await fsp.readFile(DB_CONFIG_JSON, "utf8"));
|
|
22
|
+
} catch {
|
|
23
|
+
return { ...DEFAULT_REGISTRY };
|
|
24
|
+
}
|
|
25
|
+
if (!raw || typeof raw !== "object") return { ...DEFAULT_REGISTRY };
|
|
26
|
+
const obj = raw;
|
|
27
|
+
return {
|
|
28
|
+
defaultDbPath: typeof obj.defaultDbPath === "string" ? normalize(obj.defaultDbPath) : null,
|
|
29
|
+
dbs: Array.isArray(obj.dbs) ? obj.dbs.filter((entry) => !!entry && typeof entry === "object" && typeof entry.path === "string").map((entry) => ({
|
|
30
|
+
path: normalize(entry.path),
|
|
31
|
+
lastUsedAt: typeof entry.lastUsedAt === "number" ? entry.lastUsedAt : 0
|
|
32
|
+
})) : []
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function saveRegistry(registry) {
|
|
36
|
+
await ensureDir();
|
|
37
|
+
await fsp.writeFile(DB_CONFIG_JSON, JSON.stringify(registry, null, 2));
|
|
38
|
+
}
|
|
39
|
+
async function addDb(absPath) {
|
|
40
|
+
const p = normalize(absPath);
|
|
41
|
+
await fsp.mkdir(p, { recursive: true });
|
|
42
|
+
const registry = await loadRegistry();
|
|
43
|
+
const existing = registry.dbs.find((entry) => entry.path === p);
|
|
44
|
+
if (existing) existing.lastUsedAt = Date.now();
|
|
45
|
+
else registry.dbs.push({
|
|
46
|
+
path: p,
|
|
47
|
+
lastUsedAt: Date.now()
|
|
48
|
+
});
|
|
49
|
+
await saveRegistry(registry);
|
|
50
|
+
return registry;
|
|
51
|
+
}
|
|
52
|
+
async function removeDb(absPath) {
|
|
53
|
+
const p = normalize(absPath);
|
|
54
|
+
const registry = await loadRegistry();
|
|
55
|
+
registry.dbs = registry.dbs.filter((entry) => entry.path !== p);
|
|
56
|
+
if (registry.defaultDbPath === p) registry.defaultDbPath = null;
|
|
57
|
+
await saveRegistry(registry);
|
|
58
|
+
return registry;
|
|
59
|
+
}
|
|
60
|
+
async function setDefault(absPath) {
|
|
61
|
+
const p = normalize(absPath);
|
|
62
|
+
await fsp.mkdir(p, { recursive: true });
|
|
63
|
+
const registry = await loadRegistry();
|
|
64
|
+
if (!registry.dbs.some((entry) => entry.path === p)) registry.dbs.push({
|
|
65
|
+
path: p,
|
|
66
|
+
lastUsedAt: Date.now()
|
|
67
|
+
});
|
|
68
|
+
registry.defaultDbPath = p;
|
|
69
|
+
await saveRegistry(registry);
|
|
70
|
+
return registry;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/cli/lib/runtime.ts
|
|
74
|
+
const RUNTIME_JSON = join(homedir(), ".zenbu", ".internal", "runtime.json");
|
|
75
|
+
function readRuntimeConfig() {
|
|
76
|
+
if (!existsSync(RUNTIME_JSON)) return null;
|
|
77
|
+
try {
|
|
78
|
+
const data = JSON.parse(readFileSync(RUNTIME_JSON, "utf-8"));
|
|
79
|
+
if (typeof data.wsPort !== "number" || typeof data.wsToken !== "string" || typeof data.pid !== "number") return null;
|
|
80
|
+
try {
|
|
81
|
+
process.kill(data.pid, 0);
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
wsPort: data.wsPort,
|
|
87
|
+
wsToken: data.wsToken,
|
|
88
|
+
dbPath: data.dbPath,
|
|
89
|
+
pid: data.pid
|
|
90
|
+
};
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/cli/lib/picker.ts
|
|
97
|
+
const ESC = "\x1B[";
|
|
98
|
+
const HIDE_CURSOR = `${ESC}?25l`;
|
|
99
|
+
const SHOW_CURSOR = `${ESC}?25h`;
|
|
100
|
+
const CLEAR_LINE = `${ESC}2K`;
|
|
101
|
+
const UP = (n) => `${ESC}${n}A`;
|
|
102
|
+
const tty = () => process.stdout.isTTY && process.stdin.isTTY;
|
|
103
|
+
const c = {
|
|
104
|
+
reset: () => tty() ? "\x1B[0m" : "",
|
|
105
|
+
dim: (s) => tty() ? `\x1b[2m${s}\x1b[0m` : s,
|
|
106
|
+
bold: (s) => tty() ? `\x1b[1m${s}\x1b[0m` : s,
|
|
107
|
+
green: (s) => tty() ? `\x1b[32m${s}\x1b[0m` : s,
|
|
108
|
+
cyan: (s) => tty() ? `\x1b[36m${s}\x1b[0m` : s,
|
|
109
|
+
yellow: (s) => tty() ? `\x1b[33m${s}\x1b[0m` : s,
|
|
110
|
+
magenta: (s) => tty() ? `\x1b[35m${s}\x1b[0m` : s
|
|
111
|
+
};
|
|
112
|
+
function tildify(p) {
|
|
113
|
+
const home = os.homedir();
|
|
114
|
+
if (p === home) return "~";
|
|
115
|
+
if (p.startsWith(home + "/")) return "~" + p.slice(home.length);
|
|
116
|
+
return p;
|
|
117
|
+
}
|
|
118
|
+
function relTime(ms) {
|
|
119
|
+
if (!ms) return "—";
|
|
120
|
+
const diff = Date.now() - ms;
|
|
121
|
+
if (diff < 0) return "in the future";
|
|
122
|
+
const s = Math.round(diff / 1e3);
|
|
123
|
+
if (s < 45) return "just now";
|
|
124
|
+
const m = Math.round(s / 60);
|
|
125
|
+
if (m < 60) return `${m}m ago`;
|
|
126
|
+
const h = Math.round(m / 60);
|
|
127
|
+
if (h < 24) return `${h}h ago`;
|
|
128
|
+
const d = Math.round(h / 24);
|
|
129
|
+
if (d < 30) return `${d}d ago`;
|
|
130
|
+
const mo = Math.round(d / 30);
|
|
131
|
+
if (mo < 12) return `${mo}mo ago`;
|
|
132
|
+
return `${Math.round(mo / 12)}y ago`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Arrow-key list picker. Returns the chosen index or null if user aborted
|
|
136
|
+
* (Esc / Ctrl-C). Falls back to a stdin number prompt when stdout isn't a
|
|
137
|
+
* TTY so piping/CI doesn't hang.
|
|
138
|
+
*/
|
|
139
|
+
async function pickOne(title, options, initialIndex = 0) {
|
|
140
|
+
if (options.length === 0) return null;
|
|
141
|
+
if (!tty()) return await numberPrompt(title, options, initialIndex);
|
|
142
|
+
const stdin = process.stdin;
|
|
143
|
+
const stdout = process.stdout;
|
|
144
|
+
let idx = Math.max(0, Math.min(initialIndex, options.length - 1));
|
|
145
|
+
const render = (first) => {
|
|
146
|
+
if (!first) stdout.write(UP(options.length + 1));
|
|
147
|
+
stdout.write(`${CLEAR_LINE}${title}\n`);
|
|
148
|
+
for (let i = 0; i < options.length; i++) {
|
|
149
|
+
const opt = options[i];
|
|
150
|
+
const selected = i === idx;
|
|
151
|
+
const arrow = selected ? c.cyan("›") : " ";
|
|
152
|
+
const label = selected ? c.bold(opt.label) : opt.label;
|
|
153
|
+
const detail = opt.detail ? ` ${c.dim(opt.detail)}` : "";
|
|
154
|
+
stdout.write(`${CLEAR_LINE}${arrow} ${label}${detail}\n`);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
stdout.write(HIDE_CURSOR);
|
|
158
|
+
render(true);
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
const wasRaw = stdin.isRaw;
|
|
161
|
+
stdin.setRawMode?.(true);
|
|
162
|
+
stdin.resume();
|
|
163
|
+
stdin.setEncoding("utf8");
|
|
164
|
+
const cleanup = (result) => {
|
|
165
|
+
stdin.off("data", onData);
|
|
166
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
167
|
+
stdin.pause();
|
|
168
|
+
stdout.write(SHOW_CURSOR);
|
|
169
|
+
resolve(result);
|
|
170
|
+
};
|
|
171
|
+
const onData = (data) => {
|
|
172
|
+
if (data === "" || data === "\x1B") return cleanup(null);
|
|
173
|
+
if (data === "\r" || data === "\n") return cleanup(idx);
|
|
174
|
+
if (data === "\x1B[A" || data === "k") {
|
|
175
|
+
idx = (idx - 1 + options.length) % options.length;
|
|
176
|
+
render(false);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (data === "\x1B[B" || data === "j") {
|
|
180
|
+
idx = (idx + 1) % options.length;
|
|
181
|
+
render(false);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const n = Number(data);
|
|
185
|
+
if (Number.isInteger(n) && n >= 1 && n <= options.length) {
|
|
186
|
+
idx = n - 1;
|
|
187
|
+
render(false);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
stdin.on("data", onData);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function numberPrompt(title, options, initialIndex) {
|
|
194
|
+
process.stdout.write(`${title}\n`);
|
|
195
|
+
for (let i = 0; i < options.length; i++) {
|
|
196
|
+
const opt = options[i];
|
|
197
|
+
const detail = opt.detail ? ` ${opt.detail}` : "";
|
|
198
|
+
process.stdout.write(` ${i + 1}. ${opt.label}${detail}\n`);
|
|
199
|
+
}
|
|
200
|
+
process.stdout.write(`Choose [1-${options.length}] (default ${initialIndex + 1}): `);
|
|
201
|
+
return new Promise((resolve) => {
|
|
202
|
+
let buf = "";
|
|
203
|
+
process.stdin.setEncoding("utf8");
|
|
204
|
+
process.stdin.resume();
|
|
205
|
+
const onData = (data) => {
|
|
206
|
+
buf += data;
|
|
207
|
+
const nl = buf.indexOf("\n");
|
|
208
|
+
if (nl === -1) return;
|
|
209
|
+
process.stdin.off("data", onData);
|
|
210
|
+
process.stdin.pause();
|
|
211
|
+
const trimmed = buf.slice(0, nl).trim();
|
|
212
|
+
if (!trimmed) return resolve(initialIndex);
|
|
213
|
+
const n = Number(trimmed);
|
|
214
|
+
if (!Number.isInteger(n) || n < 1 || n > options.length) return resolve(null);
|
|
215
|
+
resolve(n - 1);
|
|
216
|
+
};
|
|
217
|
+
process.stdin.on("data", onData);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
async function readLine(prompt) {
|
|
221
|
+
process.stdout.write(prompt);
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
let buf = "";
|
|
224
|
+
process.stdin.setEncoding("utf8");
|
|
225
|
+
process.stdin.resume();
|
|
226
|
+
const onData = (data) => {
|
|
227
|
+
buf += data;
|
|
228
|
+
const nl = buf.indexOf("\n");
|
|
229
|
+
if (nl === -1) return;
|
|
230
|
+
process.stdin.off("data", onData);
|
|
231
|
+
process.stdin.pause();
|
|
232
|
+
resolve(buf.slice(0, nl).trimEnd() || null);
|
|
233
|
+
};
|
|
234
|
+
process.stdin.on("data", onData);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/cli/commands/db.ts
|
|
239
|
+
const ADD_NEW_VALUE = "__add_new__";
|
|
240
|
+
function printUsage(exitCode = 0) {
|
|
241
|
+
console.log(`
|
|
242
|
+
Usage:
|
|
243
|
+
zen db Interactive picker for the default DB
|
|
244
|
+
zen db list List known DB paths
|
|
245
|
+
zen db add <path> Register a path (mkdir -p) without launching
|
|
246
|
+
zen db default [<path>] Set the default DB (interactive when omitted)
|
|
247
|
+
zen db remove [<path>] Drop a path from the registry (interactive when omitted)
|
|
248
|
+
zen db generate [...] Diff a schema against the last snapshot and
|
|
249
|
+
write a new migration. Default: walks up from
|
|
250
|
+
cwd to the nearest zenbu.plugin.json and reads
|
|
251
|
+
"schema" + "migrations" from it. Flags:
|
|
252
|
+
--manifest <path> override manifest path
|
|
253
|
+
--schema <path> schema file (bypasses
|
|
254
|
+
manifest discovery; must
|
|
255
|
+
be paired with --migrations)
|
|
256
|
+
--migrations <path> migrations output dir
|
|
257
|
+
(paired with --schema)
|
|
258
|
+
--name <tag> custom migration name
|
|
259
|
+
--custom generate editable migrate()
|
|
260
|
+
--amend replace the last migration
|
|
261
|
+
`);
|
|
262
|
+
process.exit(exitCode);
|
|
263
|
+
}
|
|
264
|
+
function annotate(e, defaultPath, activePath) {
|
|
265
|
+
const tags = [];
|
|
266
|
+
const rawTags = [];
|
|
267
|
+
if (e.path === defaultPath) {
|
|
268
|
+
tags.push(c.green("default"));
|
|
269
|
+
rawTags.push("default");
|
|
270
|
+
}
|
|
271
|
+
if (activePath && e.path === activePath) {
|
|
272
|
+
tags.push(c.cyan("running"));
|
|
273
|
+
rawTags.push("running");
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
tags,
|
|
277
|
+
rawTags
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
async function runList() {
|
|
281
|
+
const reg = await loadRegistry();
|
|
282
|
+
const activePath = readRuntimeConfig()?.dbPath ?? null;
|
|
283
|
+
if (reg.dbs.length === 0) {
|
|
284
|
+
console.log("No DBs registered yet. Run `zen db add <path>` or `zen --db <path>`.");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const rows = [...reg.dbs].sort((a, b) => b.lastUsedAt - a.lastUsedAt).map((e) => {
|
|
288
|
+
const { tags } = annotate(e, reg.defaultDbPath, activePath);
|
|
289
|
+
return {
|
|
290
|
+
path: tildify(e.path),
|
|
291
|
+
time: relTime(e.lastUsedAt),
|
|
292
|
+
tags: tags.join(" ")
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
const pathW = Math.max(...rows.map((r) => r.path.length), 4);
|
|
296
|
+
const timeW = Math.max(...rows.map((r) => r.time.length), 9);
|
|
297
|
+
console.log("");
|
|
298
|
+
console.log(` ${c.dim("path".padEnd(pathW))} ${c.dim("last used".padEnd(timeW))}`);
|
|
299
|
+
for (const r of rows) console.log(` ${r.path.padEnd(pathW)} ${c.dim(r.time.padEnd(timeW))} ${r.tags}`);
|
|
300
|
+
console.log("");
|
|
301
|
+
}
|
|
302
|
+
async function runAdd(argv) {
|
|
303
|
+
const target = argv[0];
|
|
304
|
+
if (!target) {
|
|
305
|
+
console.error("zen db add: missing <path>");
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
const abs = resolve(process.cwd(), target);
|
|
309
|
+
await addDb(abs);
|
|
310
|
+
console.log(`added ${tildify(abs)}`);
|
|
311
|
+
}
|
|
312
|
+
async function pickDefault() {
|
|
313
|
+
const reg = await loadRegistry();
|
|
314
|
+
const activePath = readRuntimeConfig()?.dbPath ?? null;
|
|
315
|
+
const sorted = [...reg.dbs].sort((a, b) => b.lastUsedAt - a.lastUsedAt);
|
|
316
|
+
const options = sorted.map((e) => {
|
|
317
|
+
const { rawTags } = annotate(e, reg.defaultDbPath, activePath);
|
|
318
|
+
return {
|
|
319
|
+
value: e.path,
|
|
320
|
+
label: tildify(e.path),
|
|
321
|
+
detail: [relTime(e.lastUsedAt), ...rawTags].filter(Boolean).join(" · ")
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
options.push({
|
|
325
|
+
value: ADD_NEW_VALUE,
|
|
326
|
+
label: c.magenta("+ Add a new path…")
|
|
327
|
+
});
|
|
328
|
+
const idx = await pickOne("Choose default DB:", options, Math.max(0, sorted.findIndex((e) => e.path === reg.defaultDbPath)));
|
|
329
|
+
if (idx === null) {
|
|
330
|
+
console.log("aborted");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const chosen = options[idx].value;
|
|
334
|
+
let target;
|
|
335
|
+
if (chosen === ADD_NEW_VALUE) {
|
|
336
|
+
const input = await readLine("New DB path: ");
|
|
337
|
+
if (!input) {
|
|
338
|
+
console.log("aborted");
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
target = resolve(process.cwd(), input.replace(/^~/, process.env.HOME ?? "~"));
|
|
342
|
+
} else target = chosen;
|
|
343
|
+
await setDefault(target);
|
|
344
|
+
console.log(`default = ${tildify(target)}`);
|
|
345
|
+
if (activePath && activePath !== target) console.log(c.yellow(`note: app is currently running on ${tildify(activePath)}; quit and relaunch to pick up the new default.`));
|
|
346
|
+
}
|
|
347
|
+
async function runDefault(argv) {
|
|
348
|
+
if (argv.length === 0) {
|
|
349
|
+
await pickDefault();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const abs = resolve(process.cwd(), argv[0]);
|
|
353
|
+
await setDefault(abs);
|
|
354
|
+
console.log(`default = ${tildify(abs)}`);
|
|
355
|
+
const running = readRuntimeConfig();
|
|
356
|
+
if (running?.dbPath && running.dbPath !== abs) console.log(c.yellow(`note: app is currently running on ${tildify(running.dbPath)}; quit and relaunch to pick up the new default.`));
|
|
357
|
+
}
|
|
358
|
+
async function runRemove(argv) {
|
|
359
|
+
let abs = null;
|
|
360
|
+
if (argv.length > 0) abs = resolve(process.cwd(), argv[0]);
|
|
361
|
+
else {
|
|
362
|
+
const reg = await loadRegistry();
|
|
363
|
+
if (reg.dbs.length === 0) {
|
|
364
|
+
console.log("registry is empty");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const options = reg.dbs.map((e) => ({
|
|
368
|
+
value: e.path,
|
|
369
|
+
label: tildify(e.path),
|
|
370
|
+
detail: relTime(e.lastUsedAt)
|
|
371
|
+
}));
|
|
372
|
+
const idx = await pickOne("Remove which DB?", options, 0);
|
|
373
|
+
if (idx === null) {
|
|
374
|
+
console.log("aborted");
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
abs = options[idx].value;
|
|
378
|
+
}
|
|
379
|
+
if (!(await loadRegistry()).dbs.some((e) => e.path === abs)) {
|
|
380
|
+
console.error(`zen db remove: ${tildify(abs)} not in registry`);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
await removeDb(abs);
|
|
384
|
+
console.log(`removed ${tildify(abs)}`);
|
|
385
|
+
}
|
|
386
|
+
async function runDb(argv) {
|
|
387
|
+
const [sub, ...rest] = argv;
|
|
388
|
+
if (!sub) {
|
|
389
|
+
await pickDefault();
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (sub === "help" || sub === "--help" || sub === "-h") printUsage();
|
|
393
|
+
switch (sub) {
|
|
394
|
+
case "list":
|
|
395
|
+
case "ls":
|
|
396
|
+
await runList();
|
|
397
|
+
return;
|
|
398
|
+
case "add":
|
|
399
|
+
await runAdd(rest);
|
|
400
|
+
return;
|
|
401
|
+
case "default":
|
|
402
|
+
await runDefault(rest);
|
|
403
|
+
return;
|
|
404
|
+
case "remove":
|
|
405
|
+
case "rm":
|
|
406
|
+
await runRemove(rest);
|
|
407
|
+
return;
|
|
408
|
+
case "generate":
|
|
409
|
+
case "gen":
|
|
410
|
+
await runGenerate(rest);
|
|
411
|
+
return;
|
|
412
|
+
default:
|
|
413
|
+
console.error(`zen db: unknown subcommand "${sub}"`);
|
|
414
|
+
printUsage(1);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Drive the embedded migration generator. Two input modes:
|
|
419
|
+
*
|
|
420
|
+
* • Manifest mode (default for plugins). Walks up from cwd to the
|
|
421
|
+
* nearest `zenbu.plugin.json` and reads `schema` + `migrations` from
|
|
422
|
+
* it — same paths `discoverSections` consumes at runtime, so there's
|
|
423
|
+
* one source of truth per plugin.
|
|
424
|
+
*
|
|
425
|
+
* • Direct mode (`--schema <path> --migrations <path>`). Skips manifest
|
|
426
|
+
* discovery entirely. Used for schemas that aren't a zenbu plugin —
|
|
427
|
+
* notably `@zenbujs/core`'s framework schema, which is invoked through
|
|
428
|
+
* a `db:generate` script in `packages/core/package.json`. Composable
|
|
429
|
+
* for any future internal section without growing privileged flags
|
|
430
|
+
* into the CLI.
|
|
431
|
+
*
|
|
432
|
+
* Resolution order: direct mode wins when both paths are given; otherwise
|
|
433
|
+
* `--manifest`; otherwise the walk-up. Mixing direct + manifest is an
|
|
434
|
+
* error (the user almost certainly meant one or the other).
|
|
435
|
+
*/
|
|
436
|
+
function findManifest(from) {
|
|
437
|
+
let dir = path.resolve(from);
|
|
438
|
+
while (true) {
|
|
439
|
+
const candidate = path.join(dir, "zenbu.plugin.json");
|
|
440
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
441
|
+
const parent = path.dirname(dir);
|
|
442
|
+
if (parent === dir) return null;
|
|
443
|
+
dir = parent;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function parseGenerateArgs(argv) {
|
|
447
|
+
const out = {
|
|
448
|
+
manifest: null,
|
|
449
|
+
schema: null,
|
|
450
|
+
migrationsOut: null,
|
|
451
|
+
custom: false,
|
|
452
|
+
amend: false
|
|
453
|
+
};
|
|
454
|
+
for (let i = 0; i < argv.length; i++) {
|
|
455
|
+
const arg = argv[i];
|
|
456
|
+
if (arg === "--manifest" && i + 1 < argv.length) out.manifest = argv[++i];
|
|
457
|
+
else if (arg.startsWith("--manifest=")) out.manifest = arg.slice(11);
|
|
458
|
+
else if (arg === "--schema" && i + 1 < argv.length) out.schema = argv[++i];
|
|
459
|
+
else if (arg.startsWith("--schema=")) out.schema = arg.slice(9);
|
|
460
|
+
else if (arg === "--migrations" && i + 1 < argv.length) out.migrationsOut = argv[++i];
|
|
461
|
+
else if (arg.startsWith("--migrations=")) out.migrationsOut = arg.slice(13);
|
|
462
|
+
else if (arg === "--name" && i + 1 < argv.length) out.name = argv[++i];
|
|
463
|
+
else if (arg.startsWith("--name=")) out.name = arg.slice(7);
|
|
464
|
+
else if (arg === "--custom") out.custom = true;
|
|
465
|
+
else if (arg === "--amend") out.amend = true;
|
|
466
|
+
}
|
|
467
|
+
return out;
|
|
468
|
+
}
|
|
469
|
+
async function runGenerate(argv) {
|
|
470
|
+
const flags = parseGenerateArgs(argv);
|
|
471
|
+
const { generateMigration } = await import("./cli-BLbQQIVB.mjs");
|
|
472
|
+
if (flags.schema || flags.migrationsOut) {
|
|
473
|
+
if (!flags.schema || !flags.migrationsOut) {
|
|
474
|
+
console.error("zen db generate: --schema and --migrations must be passed together.");
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
if (flags.manifest) {
|
|
478
|
+
console.error("zen db generate: --manifest is incompatible with --schema/--migrations; pick one input mode.");
|
|
479
|
+
process.exit(1);
|
|
480
|
+
}
|
|
481
|
+
const schemaPath = resolve(process.cwd(), flags.schema);
|
|
482
|
+
const outPath = resolve(process.cwd(), flags.migrationsOut);
|
|
483
|
+
console.log(`Schema: ${schemaPath}`);
|
|
484
|
+
console.log(`Output: ${outPath}`);
|
|
485
|
+
await generateMigration({
|
|
486
|
+
schemaPath,
|
|
487
|
+
outPath,
|
|
488
|
+
name: flags.name,
|
|
489
|
+
custom: flags.custom,
|
|
490
|
+
amend: flags.amend
|
|
491
|
+
});
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const manifestPath = flags.manifest ? resolve(process.cwd(), flags.manifest) : findManifest(process.cwd());
|
|
495
|
+
if (!manifestPath) {
|
|
496
|
+
console.error("zen db generate: no zenbu.plugin.json found in cwd or any parent.");
|
|
497
|
+
console.error(" Run from inside the plugin, pass --manifest <path>,");
|
|
498
|
+
console.error(" or use --schema <path> --migrations <path> directly.");
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
let manifest;
|
|
502
|
+
try {
|
|
503
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.error(`zen db generate: failed to parse ${manifestPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
if (!manifest.schema) {
|
|
509
|
+
console.error(`zen db generate: ${manifestPath} is missing the required "schema" field.`);
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
if (!manifest.migrations) {
|
|
513
|
+
console.error(`zen db generate: ${manifestPath} is missing the required "migrations" field`);
|
|
514
|
+
console.error(` (the directory the generator writes migrations into).`);
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
const baseDir = path.dirname(manifestPath);
|
|
518
|
+
const schemaPath = path.resolve(baseDir, manifest.schema);
|
|
519
|
+
const outPath = path.resolve(baseDir, manifest.migrations);
|
|
520
|
+
console.log(`Plugin: ${manifest.name ?? path.basename(baseDir)}`);
|
|
521
|
+
console.log(`Manifest: ${manifestPath}`);
|
|
522
|
+
await generateMigration({
|
|
523
|
+
schemaPath,
|
|
524
|
+
outPath,
|
|
525
|
+
name: flags.name,
|
|
526
|
+
custom: flags.custom,
|
|
527
|
+
amend: flags.amend
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
export { runDb };
|
package/dist/db.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { S as createSchema, _ as InferSchema$1, a as dbStringify, b as blob, f as CollectionRefBrand$1, g as InferRoot$1, i as dbParse, n as connectReplica, o as ClientProxy, p as CollectionRefValue$1, s as CollectionNode$1, v as Schema$1, x as collection, y as SchemaShape$1 } from "./index-CPZ5d6Hl.mjs";
|
|
2
|
+
import { z as z$1 } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/db.d.ts
|
|
5
|
+
declare const z: typeof z$1;
|
|
6
|
+
type Schema<TShape extends SchemaShape$1 = SchemaShape$1> = Schema$1<TShape>;
|
|
7
|
+
type SchemaShape = SchemaShape$1;
|
|
8
|
+
type InferSchema<S extends Schema$1> = InferSchema$1<S>;
|
|
9
|
+
type InferRoot<T extends SchemaShape$1> = InferRoot$1<T>;
|
|
10
|
+
type CollectionRefBrand<T = unknown> = CollectionRefBrand$1<T>;
|
|
11
|
+
type CollectionRefValue<T = unknown> = CollectionRefValue$1<T>;
|
|
12
|
+
type CollectionNode<T = unknown> = CollectionNode$1<T>;
|
|
13
|
+
type DbClient<T extends SchemaShape$1 = SchemaShape$1> = ClientProxy<T>;
|
|
14
|
+
declare const connectDb: typeof connectReplica;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CollectionNode, CollectionRefBrand, CollectionRefValue, DbClient, InferRoot, InferSchema, Schema, SchemaShape, blob, collection, connectDb, createSchema, dbParse, dbStringify, z };
|
package/dist/db.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { i as createSchema, n as blob, r as collection } from "./schema-CIg4GzHQ.mjs";
|
|
2
|
+
import { i as dbStringify, r as dbParse, t as connectReplica } from "./transport-BMSzG2-F.mjs";
|
|
3
|
+
import { z as z$1 } from "zod";
|
|
4
|
+
//#region src/db.ts
|
|
5
|
+
/**
|
|
6
|
+
* Public DB surface for plugin authors. The underlying engine (kyju) is an
|
|
7
|
+
* internal detail; only the names re-exported here are part of `@zenbujs/core`'s
|
|
8
|
+
* stable contract. The user authors schemas with raw zod plus two named
|
|
9
|
+
* extensions (`collection`, `blob`); they never import `zod` themselves —
|
|
10
|
+
* `z` is re-exported here so there's exactly one zod instance in play and
|
|
11
|
+
* type identities never drift across versions.
|
|
12
|
+
*/
|
|
13
|
+
const z = z$1;
|
|
14
|
+
const connectDb = connectReplica;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { blob, collection, connectDb, createSchema, dbParse, dbStringify, z };
|