@voltro/cli 0.40.0 → 0.42.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/CHANGELOG.md +171 -0
- package/dist/{apiBuild-C8xBEYbL.js → apiBuild-B_iH24qf.js} +2 -2
- package/dist/apiBuild-RM5W5YXk.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/{build-CkhFGnKA.js → build-DUdi6F_Q.js} +150 -150
- package/dist/{checkCommand-QbHshOac.js → checkCommand-B2RWrpWi.js} +1 -1
- package/dist/{checkCommand-BcXJa2IS.js → checkCommand-DWL2_PPR.js} +2 -2
- package/dist/{codegenCommand-DF2OJh3O.js → codegenCommand-DkjoJ30Q.js} +2 -2
- package/dist/{codemodRunner-BcOvi4A2.js → codemodRunner-sFuWDETH.js} +520 -460
- package/dist/{commands-7Z7-HOE3.js → commands-DoTClnSk.js} +22 -22
- package/dist/{dashboardCommand-CUycH6dR.js → dashboardCommand-Bro7Jwts.js} +5 -5
- package/dist/dataCommand-Rj57y1vg.js +669 -0
- package/dist/dataProfile-Cm0YVKSy.js +18 -0
- package/dist/{dbCommand-DSwGv9wS.js → dbCommand-B237qI-j.js} +343 -316
- package/dist/dbCommand-BvQeKQOb.js +2 -0
- package/dist/{dev-Dh1NuYx1.js → dev-D4l_YOzI.js} +22 -22
- package/dist/{dev-D8JSKlBM.js → dev-DuqBJC7g.js} +1 -1
- package/dist/doctorCommand-Bw3V4L1j.js +2 -0
- package/dist/{doctorCommand-C-8-Z9D6.js → doctorCommand-TmZTAepc.js} +385 -279
- package/dist/{dormancyCommand-A79Odglm.js → dormancyCommand-CUKhRmGG.js} +1 -1
- package/dist/{embeddingsCommand-CoiyHfkY.js → embeddingsCommand-Db-iQM3l.js} +1 -1
- package/dist/{envCommand-B6d0s-jZ.js → envCommand-DQ_URZq9.js} +12 -12
- package/dist/{evolveCommand-BC5a9lxX.js → evolveCommand-CghRPrGO.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-ZqSVzJZ4.js → infoCommand-BCs_ZFnl.js} +1 -1
- package/dist/{migrate-BnPw2zC8.js → migrate-DN47p2PG.js} +1 -1
- package/dist/{runtimeTrace-BslKJjDY.js → runtimeTrace-jFKvOPBz.js} +1 -1
- package/dist/{sdkgen-DQ8uEmmn.js → sdkgen-COQqHB9V.js} +1 -1
- package/dist/{serveCommand-Dqb9Ku1-.js → serveCommand-C512kzZA.js} +417 -411
- package/dist/serveCommand-gj7MsqXY.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-dkNYKtgQ.js → start-BjcuFME_.js} +379 -343
- package/dist/{start-BA7txHhY.js → start-fifc0xpW.js} +1 -1
- package/dist/startEntry.js +1 -1
- package/dist/updateCommand-B2alql9k.js +2 -0
- package/dist/{updateCommand-eoGpZr3E.js → updateCommand-HZQVBU3T.js} +1 -1
- package/dist/webDev-D6QeXw3P.js +2 -0
- package/dist/{webDev-bybjZA0o.js → webDev-Dk8lFUzf.js} +642 -525
- package/dist/{webhooksCommand-DubZWNOx.js → webhooksCommand-D7ZpH7w0.js} +1 -1
- package/package.json +23 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/_manifest.json +1 -1
- package/templates/agent-docs/cli.md +79 -6
- package/templates/agent-docs/data.md +33 -0
- package/templates/agent-docs/plugins/governance.md +8 -0
- package/templates/agent-docs/routing.md +141 -28
- package/templates/agent-docs/security.md +24 -0
- package/templates/agent-docs/whats-new.md +48 -109
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +10 -10
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-BIyGkym8.js +0 -2
- package/dist/dataCommand-By78lscK.js +0 -540
- package/dist/dataProfile-dW-PsfLB.js +0 -15
- package/dist/dbCommand-DcqEyxju.js +0 -2
- package/dist/doctorCommand-FhQ7EVYL.js +0 -2
- package/dist/serveCommand-BmvEQCMS.js +0 -2
- package/dist/updateCommand-BauSVxPp.js +0 -2
- package/dist/webDev-CBbKs4dZ.js +0 -2
|
@@ -1,540 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./cliError-BmdYnghb.js";
|
|
2
|
-
import { c as t, i as n, o as r } from "./dev-Dh1NuYx1.js";
|
|
3
|
-
import { n as i, t as a } from "./cliArgs-D4p8n7EE.js";
|
|
4
|
-
import { r as o } from "./dialectDriver-czCHYpeH.js";
|
|
5
|
-
import { t as s } from "./dataProfile-dW-PsfLB.js";
|
|
6
|
-
import { o as c, t as l } from "./inspectFetch-EMuhTG_9.js";
|
|
7
|
-
import { r as u } from "./dbCommand-DSwGv9wS.js";
|
|
8
|
-
import { createLogger as d } from "@voltro/logger";
|
|
9
|
-
import { Effect as f, Stream as p } from "effect";
|
|
10
|
-
import { SqlClient as m } from "@effect/sql";
|
|
11
|
-
import { declaredSnapshot as h, fingerprintSchema as g, introspectSchema as _ } from "@voltro/database/sql";
|
|
12
|
-
import { connectionIdentity as v, normaliseTarget as y, sameDatabase as b, streamTable as x } from "@voltro/database";
|
|
13
|
-
import { STORAGE_REFS_TABLE as S, resolveStorageProvider as C } from "@voltro/plugin-storage";
|
|
14
|
-
import { assessLiveImport as ee, backupArtifactName as w, backupCommand as te, classificationFromSnapshot as ne, decryptStream as T, encryptStream as re, packBundle as E, peekBundle as D, previewMasking as ie, restoreCommand as O, runExport as k, runImport as A, runNativeStep as j, storageAssetSink as M, storageAssetSource as ae, storageBlobSink as oe, storageBlobSource as se, unpackBundle as ce, unpackBundleToDir as N } from "@voltro/data-transfer";
|
|
15
|
-
//#region src/backupStamp.ts
|
|
16
|
-
var P = "voltro-backup-stamp.json", le = (e) => ({
|
|
17
|
-
stampVersion: 1,
|
|
18
|
-
dialect: e.dialect,
|
|
19
|
-
schemaFingerprint: e.schemaFingerprint,
|
|
20
|
-
voltroVersion: e.voltroVersion,
|
|
21
|
-
createdAt: e.createdAt
|
|
22
|
-
}), F = (e) => {
|
|
23
|
-
let t;
|
|
24
|
-
try {
|
|
25
|
-
t = JSON.parse(e);
|
|
26
|
-
} catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
if (typeof t != "object" || !t) return null;
|
|
30
|
-
let n = t;
|
|
31
|
-
if (n.stampVersion !== 1 || typeof n.dialect != "string" || typeof n.voltroVersion != "string" || typeof n.createdAt != "string") return null;
|
|
32
|
-
let r = n.schemaFingerprint;
|
|
33
|
-
return r !== null && typeof r != "string" ? null : {
|
|
34
|
-
stampVersion: 1,
|
|
35
|
-
dialect: n.dialect,
|
|
36
|
-
schemaFingerprint: r,
|
|
37
|
-
voltroVersion: n.voltroVersion,
|
|
38
|
-
createdAt: n.createdAt
|
|
39
|
-
};
|
|
40
|
-
}, ue = (e) => {
|
|
41
|
-
let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
|
|
42
|
-
if (t === null) return {
|
|
43
|
-
warn: `no ${P} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
44
|
-
info: []
|
|
45
|
-
};
|
|
46
|
-
let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
|
|
47
|
-
return t.dialect === n ? t.schemaFingerprint !== null && r !== null && t.schemaFingerprint !== r ? {
|
|
48
|
-
warn: `schema skew — the backup's schema (${t.schemaFingerprint.slice(0, 12)}) differs from what this code declares (${r.slice(0, 12)}). Run \`voltro db apply\` after the restore, or the app may boot against a schema it doesn't understand.`,
|
|
49
|
-
info: i
|
|
50
|
-
} : { info: i } : {
|
|
51
|
-
refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
|
|
52
|
-
info: i
|
|
53
|
-
};
|
|
54
|
-
}, I = (e) => {
|
|
55
|
-
try {
|
|
56
|
-
let t = new URL(e), n = t.pathname.replace(/^\//, "");
|
|
57
|
-
return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
|
|
58
|
-
} catch {
|
|
59
|
-
return e.trim();
|
|
60
|
-
}
|
|
61
|
-
}, de = (e) => {
|
|
62
|
-
let t = e.flagUrl ?? e.envUrl;
|
|
63
|
-
if (t === void 0 || t.trim() === "") return {
|
|
64
|
-
ok: !1,
|
|
65
|
-
error: "no drill target — pass `--drill-url <url>` or set `DRILL_DB_URL`. The drill restores into a THROWAWAY database (never the live one), so it needs its own connection."
|
|
66
|
-
};
|
|
67
|
-
let n = I(t);
|
|
68
|
-
return e.liveKeys.some((e) => e === n) ? {
|
|
69
|
-
ok: !1,
|
|
70
|
-
error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
|
|
71
|
-
} : {
|
|
72
|
-
ok: !0,
|
|
73
|
-
url: t
|
|
74
|
-
};
|
|
75
|
-
}, fe = (e) => {
|
|
76
|
-
let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
|
|
77
|
-
return r === 0 ? {
|
|
78
|
-
pass: !1,
|
|
79
|
-
lines: ["FAIL — the restore produced ZERO tables. The dump is empty or unreadable; this backup would NOT recover your app."]
|
|
80
|
-
} : t !== null && n !== null && t !== n ? {
|
|
81
|
-
pass: !1,
|
|
82
|
-
lines: [`FAIL — restored ${r} table(s), but the schema fingerprint (${n.slice(0, 12)}) does NOT match the backup's stamp (${t.slice(0, 12)}).`, "The restore did not reproduce the schema that was backed up — the artifact is inconsistent."]
|
|
83
|
-
} : t === null || n === null ? {
|
|
84
|
-
pass: !0,
|
|
85
|
-
lines: [`PASS (partial) — restored ${r} table(s) into the throwaway DB.`, "No stamped fingerprint to cross-check the schema shape (older/hand-made backup) — the bytes restore, but the shape was not verified."]
|
|
86
|
-
} : {
|
|
87
|
-
pass: !0,
|
|
88
|
-
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
89
|
-
};
|
|
90
|
-
}, L = d({ scope: "voltro:data" }), pe = async () => {
|
|
91
|
-
try {
|
|
92
|
-
let { createRequire: e } = await import("node:module"), { join: t } = await import("node:path"), { readFileSync: n } = await import("node:fs"), r = e(t(process.cwd(), "package.json")).resolve("@voltro/cli/package.json");
|
|
93
|
-
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
94
|
-
} catch {
|
|
95
|
-
return "unknown";
|
|
96
|
-
}
|
|
97
|
-
}, R = async (e) => {
|
|
98
|
-
try {
|
|
99
|
-
return g(await K(e));
|
|
100
|
-
} catch {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}, z = (e) => {
|
|
104
|
-
if (e.event !== "done") return;
|
|
105
|
-
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
106
|
-
L.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
107
|
-
}, B = async (e) => {
|
|
108
|
-
let t = (e) => e?.meta?.databaseId;
|
|
109
|
-
if (e) {
|
|
110
|
-
let n = await l({
|
|
111
|
-
name: "target",
|
|
112
|
-
url: e
|
|
113
|
-
}, "/_voltro/inspect/app");
|
|
114
|
-
if (n.kind === "ok") return {
|
|
115
|
-
label: e,
|
|
116
|
-
databaseId: t(n.data)
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
for (let e of await c()) {
|
|
120
|
-
let n = await l(e, "/_voltro/inspect/app");
|
|
121
|
-
if (n.kind === "ok") return {
|
|
122
|
-
label: `${e.name} (${e.url})`,
|
|
123
|
-
databaseId: t(n.data)
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
return null;
|
|
127
|
-
}, V = async (e) => {
|
|
128
|
-
let t = e.includes("--allow-live"), n = t ? null : await B(H(e, "--api-url")), i = v({
|
|
129
|
-
dialect: W(),
|
|
130
|
-
...r()
|
|
131
|
-
}), a = y({
|
|
132
|
-
dialect: W(),
|
|
133
|
-
...r()
|
|
134
|
-
}), { refuse: o, message: s } = ee({
|
|
135
|
-
liveInstance: n?.label ?? null,
|
|
136
|
-
allowLive: t,
|
|
137
|
-
sameTarget: b(i, n?.databaseId),
|
|
138
|
-
...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
|
|
139
|
-
});
|
|
140
|
-
return !o || (L.error(s ?? "refusing: live instance detected"), !1);
|
|
141
|
-
}, H = i, me = [
|
|
142
|
-
"--api-url",
|
|
143
|
-
"--bundle-key",
|
|
144
|
-
"--compression",
|
|
145
|
-
"--consistency",
|
|
146
|
-
"--drill-url",
|
|
147
|
-
"--mode",
|
|
148
|
-
"--on-conflict",
|
|
149
|
-
"--passphrase",
|
|
150
|
-
"--profile",
|
|
151
|
-
"--tables",
|
|
152
|
-
"--target",
|
|
153
|
-
"--tenant",
|
|
154
|
-
"--token"
|
|
155
|
-
], U = (e) => a(e, me), W = () => {
|
|
156
|
-
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
157
|
-
if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
158
|
-
return t;
|
|
159
|
-
}, G = async (e, t) => {
|
|
160
|
-
let n = (await o(e)).makeSqlLayer(t);
|
|
161
|
-
return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
|
|
162
|
-
}, K = (e) => G(e, r()), he = () => {
|
|
163
|
-
let e = r(), t = [];
|
|
164
|
-
return e.url && t.push(I(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
|
|
165
|
-
}, q = async () => {
|
|
166
|
-
let r = process.cwd(), i = await t(r);
|
|
167
|
-
if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
|
|
168
|
-
let a = await n(i, void 0), o = W();
|
|
169
|
-
return {
|
|
170
|
-
store: a,
|
|
171
|
-
snapshot: await K(o),
|
|
172
|
-
dialect: o
|
|
173
|
-
};
|
|
174
|
-
}, J = (e) => x(e, {
|
|
175
|
-
table: S,
|
|
176
|
-
chunkSize: 500
|
|
177
|
-
}).pipe(p.map((e) => ({
|
|
178
|
-
key: String(e.key),
|
|
179
|
-
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
180
|
-
size: Number(e.size ?? 0)
|
|
181
|
-
}))), Y = (e) => !!e && e.endsWith(".vbundle"), X = (e) => H(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, ge = (e) => e.includes("--encrypt") || H(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, _e = async (e, t) => {
|
|
182
|
-
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
183
|
-
await r(i.from(e), n(t));
|
|
184
|
-
}, Z = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
185
|
-
let { createReadStream: t } = await import("node:fs");
|
|
186
|
-
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
187
|
-
} }), ve = (e) => {
|
|
188
|
-
let t = H(e, "--tenant");
|
|
189
|
-
if (t) return {
|
|
190
|
-
kind: "tenant",
|
|
191
|
-
tenantId: t
|
|
192
|
-
};
|
|
193
|
-
let n = H(e, "--tables");
|
|
194
|
-
return n ? {
|
|
195
|
-
kind: "tables",
|
|
196
|
-
tables: n.split(",").map((e) => e.trim()).filter(Boolean)
|
|
197
|
-
} : { kind: "all" };
|
|
198
|
-
}, ye = async () => {
|
|
199
|
-
let e = await u(process.cwd());
|
|
200
|
-
return ne(h(e));
|
|
201
|
-
}, be = "voltro/tenant", xe = async () => {
|
|
202
|
-
let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === be)).map((e) => e.tableName);
|
|
203
|
-
return e.length > 0 ? e : void 0;
|
|
204
|
-
}, Q = (e) => {
|
|
205
|
-
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
206
|
-
console.log(`\n ${t.table}`);
|
|
207
|
-
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
208
|
-
}
|
|
209
|
-
if (e.leaks.length > 0) {
|
|
210
|
-
console.log("\n ⚠ possible leaks in KEPT columns:");
|
|
211
|
-
for (let t of e.leaks) console.log(` ${t.table}.${t.column} looks like ${t.pattern}: ${JSON.stringify(t.sample)}`);
|
|
212
|
-
}
|
|
213
|
-
if (e.unclassified.length > 0) {
|
|
214
|
-
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
215
|
-
for (let t of e.unclassified) console.log(` ${t}`);
|
|
216
|
-
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
217
|
-
}, Se = async (e) => {
|
|
218
|
-
let t = H(e, "--target") === "api", n = H(e, "--bundle-key"), r = U(e);
|
|
219
|
-
if (!r && !(t && n)) return L.error("usage: voltro data export <outDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--profile <name>] [--tenant <id>] [--tables a,b] [--bundle-key <storage-key>] [--assets] [--compression zstd|gzip|none] [--consistency live|snapshot] [--dry-run]"), 1;
|
|
220
|
-
if (t) {
|
|
221
|
-
let t = H(e, "--api-url");
|
|
222
|
-
if (!t) return L.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
223
|
-
let i = H(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
224
|
-
if (!i) return L.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
225
|
-
let a = H(e, "--tables"), o = {
|
|
226
|
-
...H(e, "--profile") ? { profile: H(e, "--profile") } : {},
|
|
227
|
-
...H(e, "--tenant") ? { tenant: H(e, "--tenant") } : {},
|
|
228
|
-
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
229
|
-
...H(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
230
|
-
...H(e, "--compression") ? { compression: H(e, "--compression") } : {},
|
|
231
|
-
...e.includes("--assets") ? { assets: !0 } : {},
|
|
232
|
-
...n ? { bundleKey: n } : {}
|
|
233
|
-
}, s = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, {
|
|
234
|
-
method: "POST",
|
|
235
|
-
headers: {
|
|
236
|
-
"content-type": "application/json",
|
|
237
|
-
authorization: `Bearer ${i}`
|
|
238
|
-
},
|
|
239
|
-
body: JSON.stringify(o)
|
|
240
|
-
});
|
|
241
|
-
if (!s.ok) return L.error(`api export failed: HTTP ${s.status} — ${await s.text()}`), 1;
|
|
242
|
-
if (n) return L.info("api export complete (pushed to storage)", {
|
|
243
|
-
via: t,
|
|
244
|
-
...await s.json()
|
|
245
|
-
}), 0;
|
|
246
|
-
let { resolve: c } = await import("node:path"), l = new Uint8Array(await s.arrayBuffer());
|
|
247
|
-
return await N((async function* () {
|
|
248
|
-
yield l;
|
|
249
|
-
})(), c(process.cwd(), r)), L.info("api export complete (downloaded from the running instance)", {
|
|
250
|
-
outDir: r,
|
|
251
|
-
tables: s.headers.get("x-export-tables") ?? void 0,
|
|
252
|
-
rows: s.headers.get("x-export-rows") ?? void 0,
|
|
253
|
-
masked: s.headers.get("x-export-masked") ?? void 0
|
|
254
|
-
}), 0;
|
|
255
|
-
}
|
|
256
|
-
if (!r) return L.error("a <outDir> is required for a direct export"), 1;
|
|
257
|
-
let i = H(e, "--profile"), a = i ? await s(i) : {}, { store: o, snapshot: c, dialect: l } = await q(), u = e.includes("--assets") || a.assets === !0, d = u ? C({}) : void 0, p = H(e, "--compression") ?? a.compression, m = H(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? ve(e), v = h ? await ye() : void 0, y = _.kind === "tenant" ? await xe() : void 0, b = y ? { tenantTables: y } : {};
|
|
258
|
-
if (e.includes("--dry-run")) {
|
|
259
|
-
if (!h) return L.error("--dry-run previews masking, but the profile sets no masking policy"), 1;
|
|
260
|
-
let e = await f.runPromise(ie({
|
|
261
|
-
store: o,
|
|
262
|
-
snapshot: c,
|
|
263
|
-
masking: h,
|
|
264
|
-
...v ? { classification: v } : {},
|
|
265
|
-
...g ? {} : {
|
|
266
|
-
scope: _,
|
|
267
|
-
...b
|
|
268
|
-
}
|
|
269
|
-
}));
|
|
270
|
-
return Q(e), +(e.unclassified.length > 0);
|
|
271
|
-
}
|
|
272
|
-
if (Y(r)) {
|
|
273
|
-
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), s = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
|
|
274
|
-
try {
|
|
275
|
-
let t = await f.runPromise(k({
|
|
276
|
-
store: o,
|
|
277
|
-
snapshot: c,
|
|
278
|
-
dialect: l,
|
|
279
|
-
outDir: s,
|
|
280
|
-
...g ? { subset: g } : {
|
|
281
|
-
scope: _,
|
|
282
|
-
...b
|
|
283
|
-
},
|
|
284
|
-
consistency: m,
|
|
285
|
-
...p ? { compression: p } : {},
|
|
286
|
-
...h ? { masking: h } : {},
|
|
287
|
-
...v ? { classification: v } : {},
|
|
288
|
-
onProgress: z
|
|
289
|
-
})), n = E({
|
|
290
|
-
dir: s,
|
|
291
|
-
...d ? { blobs: se(d, () => J(o)) } : {}
|
|
292
|
-
}), a = ge(e);
|
|
293
|
-
if (a) {
|
|
294
|
-
let t = X(e);
|
|
295
|
-
if (!t) return L.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
296
|
-
n = re(n, t);
|
|
297
|
-
}
|
|
298
|
-
return await _e(n, i(process.cwd(), r)), L.info("export complete (single file)", {
|
|
299
|
-
file: r,
|
|
300
|
-
encrypted: a,
|
|
301
|
-
assets: u,
|
|
302
|
-
tables: t.tables.length,
|
|
303
|
-
rows: t.tables.reduce((e, t) => e + t.rowCount, 0)
|
|
304
|
-
}), 0;
|
|
305
|
-
} finally {
|
|
306
|
-
await n.rm(s, {
|
|
307
|
-
recursive: !0,
|
|
308
|
-
force: !0
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
let x = await f.runPromise(k({
|
|
313
|
-
store: o,
|
|
314
|
-
snapshot: c,
|
|
315
|
-
dialect: l,
|
|
316
|
-
outDir: r,
|
|
317
|
-
...g ? { subset: g } : {
|
|
318
|
-
scope: _,
|
|
319
|
-
...b
|
|
320
|
-
},
|
|
321
|
-
consistency: m,
|
|
322
|
-
...p ? { compression: p } : {},
|
|
323
|
-
...h ? { masking: h } : {},
|
|
324
|
-
...v ? { classification: v } : {},
|
|
325
|
-
...d ? { assets: ae(d, () => J(o)) } : {},
|
|
326
|
-
onProgress: z
|
|
327
|
-
}));
|
|
328
|
-
return L.info("export complete", {
|
|
329
|
-
outDir: r,
|
|
330
|
-
tables: x.tables.length,
|
|
331
|
-
rows: x.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
332
|
-
assets: x.assets.count,
|
|
333
|
-
compression: x.compression,
|
|
334
|
-
...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
|
|
335
|
-
}), 0;
|
|
336
|
-
}, Ce = async (e) => {
|
|
337
|
-
let t = H(e, "--target") === "api", n = H(e, "--bundle-key"), i = U(e);
|
|
338
|
-
if (!i && !(t && n)) return L.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--bundle-key <storage-key>] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] (--force: import despite schema drift AND cross-dialect warnings)"), 1;
|
|
339
|
-
let a = H(e, "--mode") ?? "upsert", o = H(e, "--on-conflict") ?? "skip";
|
|
340
|
-
if (t) {
|
|
341
|
-
let t = H(e, "--api-url");
|
|
342
|
-
if (!t) return L.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
343
|
-
let r = H(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
344
|
-
if (!r) return L.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
345
|
-
let s = {
|
|
346
|
-
authorization: `Bearer ${r}`,
|
|
347
|
-
"x-import-mode": a,
|
|
348
|
-
"x-import-on-conflict": o,
|
|
349
|
-
...e.includes("--atomic") ? { "x-import-atomic": "1" } : {},
|
|
350
|
-
...e.includes("--force") ? { "x-import-force": "1" } : {}
|
|
351
|
-
}, { resolve: c } = await import("node:path"), l;
|
|
352
|
-
if (n) s["content-type"] = "application/json", l = JSON.stringify({ bundleKey: n });
|
|
353
|
-
else if (Y(i)) {
|
|
354
|
-
let t = Z(c(process.cwd(), i)), n = X(e);
|
|
355
|
-
n && (t = T(t, n));
|
|
356
|
-
let r = [];
|
|
357
|
-
for await (let e of t) r.push(Buffer.from(e));
|
|
358
|
-
s["content-type"] = "application/octet-stream", l = Buffer.concat(r);
|
|
359
|
-
} else {
|
|
360
|
-
await (await import("node:fs/promises")).access(c(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && L.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API.");
|
|
361
|
-
let e = [];
|
|
362
|
-
for await (let t of E({ dir: c(process.cwd(), i) })) e.push(Buffer.from(t));
|
|
363
|
-
s["content-type"] = "application/octet-stream", l = Buffer.concat(e);
|
|
364
|
-
}
|
|
365
|
-
let u = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, {
|
|
366
|
-
method: "POST",
|
|
367
|
-
body: l,
|
|
368
|
-
headers: s
|
|
369
|
-
});
|
|
370
|
-
return u.ok ? (L.info("api import complete (imported by the running instance)", {
|
|
371
|
-
via: t,
|
|
372
|
-
...await u.json()
|
|
373
|
-
}), 0) : (L.error(`api import failed: HTTP ${u.status} — ${await u.text()}`), 1);
|
|
374
|
-
}
|
|
375
|
-
if (!i) return L.error("a <bundleDir> is required for a direct import"), 1;
|
|
376
|
-
if (!await V(e)) return 1;
|
|
377
|
-
let { store: s, snapshot: c } = await q(), l = e.includes("--force"), u = (e) => L.warn(e), d = e.includes("--assets") ? C({}) : void 0, p = W() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
|
|
378
|
-
if (Y(i)) {
|
|
379
|
-
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join: m } = await import("node:path"), h = await n.mkdtemp(m(t.tmpdir(), "voltro-import-"));
|
|
380
|
-
try {
|
|
381
|
-
let t = Z(r(process.cwd(), i)), n = X(e);
|
|
382
|
-
n && (t = T(t, n)), await ce(t, {
|
|
383
|
-
dir: h,
|
|
384
|
-
blobs: oe(d ?? C({}))
|
|
385
|
-
});
|
|
386
|
-
let m = await f.runPromise(A({
|
|
387
|
-
store: s,
|
|
388
|
-
bundleDir: h,
|
|
389
|
-
mode: a,
|
|
390
|
-
onConflict: o,
|
|
391
|
-
atomic: e.includes("--atomic"),
|
|
392
|
-
verify: !e.includes("--no-verify"),
|
|
393
|
-
targetDialect: W(),
|
|
394
|
-
allowIncompatible: l,
|
|
395
|
-
targetSnapshot: c,
|
|
396
|
-
force: l,
|
|
397
|
-
onWarn: u,
|
|
398
|
-
onProgress: z,
|
|
399
|
-
assetsAlreadyRestored: !0,
|
|
400
|
-
...p ? { copyLoader: p } : {}
|
|
401
|
-
}));
|
|
402
|
-
return L.info("import complete (single file)", {
|
|
403
|
-
file: i,
|
|
404
|
-
tables: m.tables.length,
|
|
405
|
-
rows: m.tables.reduce((e, t) => e + t.rowCount, 0)
|
|
406
|
-
}), 0;
|
|
407
|
-
} finally {
|
|
408
|
-
await p?.close(), await n.rm(h, {
|
|
409
|
-
recursive: !0,
|
|
410
|
-
force: !0
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
let m = await f.runPromise(A({
|
|
415
|
-
store: s,
|
|
416
|
-
bundleDir: i,
|
|
417
|
-
mode: a,
|
|
418
|
-
onConflict: o,
|
|
419
|
-
atomic: e.includes("--atomic"),
|
|
420
|
-
verify: !e.includes("--no-verify"),
|
|
421
|
-
targetDialect: W(),
|
|
422
|
-
allowIncompatible: l,
|
|
423
|
-
targetSnapshot: c,
|
|
424
|
-
force: l,
|
|
425
|
-
onWarn: u,
|
|
426
|
-
onProgress: z,
|
|
427
|
-
...d ? { assets: M(d) } : {},
|
|
428
|
-
...p ? { copyLoader: p } : {}
|
|
429
|
-
})).finally(() => p?.close());
|
|
430
|
-
return L.info("import complete", {
|
|
431
|
-
bundleDir: i,
|
|
432
|
-
tables: m.tables.length,
|
|
433
|
-
rows: m.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
434
|
-
assets: m.assets.count
|
|
435
|
-
}), 0;
|
|
436
|
-
}, $ = () => {
|
|
437
|
-
let e = r();
|
|
438
|
-
return {
|
|
439
|
-
...e.url ? { url: e.url } : {},
|
|
440
|
-
...e.host ? { host: e.host } : {},
|
|
441
|
-
...e.port ? { port: e.port } : {},
|
|
442
|
-
...e.username ? { username: e.username } : {},
|
|
443
|
-
...e.password ? { password: e.password } : {},
|
|
444
|
-
...e.database ? { database: e.database } : {}
|
|
445
|
-
};
|
|
446
|
-
}, we = async (e) => {
|
|
447
|
-
let t = U(e);
|
|
448
|
-
if (!t) return L.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
|
|
449
|
-
let n = W(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
|
|
450
|
-
await r(t, { recursive: !0 });
|
|
451
|
-
let o = a(t, w(n));
|
|
452
|
-
await f.runPromise(j(te(n, $(), o)));
|
|
453
|
-
let s = le({
|
|
454
|
-
dialect: n,
|
|
455
|
-
schemaFingerprint: await R(n),
|
|
456
|
-
voltroVersion: await pe(),
|
|
457
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
458
|
-
});
|
|
459
|
-
return await i(a(t, P), `${JSON.stringify(s, null, 2)}\n`, "utf8"), L.info("native backup complete", {
|
|
460
|
-
dialect: n,
|
|
461
|
-
artifact: o,
|
|
462
|
-
stamp: P,
|
|
463
|
-
schemaFingerprint: s.schemaFingerprint ?? "<none>",
|
|
464
|
-
assets: "NOT included — use `voltro data export --assets` for rows + blobs"
|
|
465
|
-
}), 0;
|
|
466
|
-
}, Te = async (e, t) => {
|
|
467
|
-
let n = W(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, w(n)), o = de({
|
|
468
|
-
flagUrl: H(t, "--drill-url"),
|
|
469
|
-
envUrl: process.env.DRILL_DB_URL,
|
|
470
|
-
liveKeys: he()
|
|
471
|
-
});
|
|
472
|
-
if (!o.ok) return L.error(`restore --drill: ${o.error}`), 1;
|
|
473
|
-
let s = await i(r(e, P), "utf8").then(F, () => null);
|
|
474
|
-
if (s && s.dialect !== n) return L.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
|
|
475
|
-
L.info("restore drill: restoring into throwaway DB", {
|
|
476
|
-
dialect: n,
|
|
477
|
-
target: I(o.url)
|
|
478
|
-
}), await f.runPromise(j(O(n, { url: o.url }, a)));
|
|
479
|
-
let c = null, l = 0;
|
|
480
|
-
try {
|
|
481
|
-
let e = await G(n, { url: o.url });
|
|
482
|
-
c = g(e), l = e.tables.length;
|
|
483
|
-
} catch (e) {
|
|
484
|
-
return L.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
485
|
-
}
|
|
486
|
-
let u = fe({
|
|
487
|
-
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
488
|
-
restoredFingerprint: c,
|
|
489
|
-
restoredTableCount: l
|
|
490
|
-
});
|
|
491
|
-
for (let e of u.lines) u.pass ? L.info(`restore drill: ${e}`) : L.error(`restore drill: ${e}`);
|
|
492
|
-
return +!u.pass;
|
|
493
|
-
}, Ee = async (e) => {
|
|
494
|
-
let t = U(e);
|
|
495
|
-
if (!t) return L.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
|
|
496
|
-
if (e.includes("--drill")) return Te(t, e);
|
|
497
|
-
if (!await V(e)) return 1;
|
|
498
|
-
let n = W(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, w(n)), o = ue({
|
|
499
|
-
stamp: await i(r(t, P), "utf8").then(F, () => null),
|
|
500
|
-
currentDialect: n,
|
|
501
|
-
currentFingerprint: await R(n)
|
|
502
|
-
});
|
|
503
|
-
for (let e of o.info) L.info(`backup stamp: ${e}`);
|
|
504
|
-
if (o.warn && L.warn(o.warn), o.refuse) {
|
|
505
|
-
if (!e.includes("--force")) return L.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
|
|
506
|
-
L.warn(`restore proceeding despite: ${o.refuse} (--force)`);
|
|
507
|
-
}
|
|
508
|
-
return await f.runPromise(j(O(n, $(), a))), L.info("native restore complete", {
|
|
509
|
-
dialect: n,
|
|
510
|
-
artifact: a
|
|
511
|
-
}), 0;
|
|
512
|
-
}, De = async (e) => {
|
|
513
|
-
let t = U(e);
|
|
514
|
-
if (!t) return L.error("usage: voltro data inspect <file.vbundle> [--json] [--passphrase <p>]"), 1;
|
|
515
|
-
let { resolve: n } = await import("node:path"), r = n(process.cwd(), t), i = X(e), a = () => {
|
|
516
|
-
let e = Z(r);
|
|
517
|
-
return i ? T(e, i) : e;
|
|
518
|
-
};
|
|
519
|
-
if (e.includes("--json")) {
|
|
520
|
-
let e = (await D(a(), ["manifest.json"])).get("manifest.json");
|
|
521
|
-
return e ? (console.log(e.toString("utf8")), 0) : (L.error("no manifest.json found in the bundle"), 1);
|
|
522
|
-
}
|
|
523
|
-
let o = (await D(a(), ["README.md"])).get("README.md");
|
|
524
|
-
if (o) return console.log(o.toString("utf8")), 0;
|
|
525
|
-
let s = (await D(a(), ["manifest.json"])).get("manifest.json");
|
|
526
|
-
return s ? (console.log(s.toString("utf8")), 0) : (L.error("bundle has no README.md or manifest.json"), 1);
|
|
527
|
-
}, Oe = async (e) => {
|
|
528
|
-
let t = U(e), n = U(e.filter((e) => e !== t));
|
|
529
|
-
if (!t || !n) return L.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
|
|
530
|
-
let { resolve: r } = await import("node:path"), i = Z(r(process.cwd(), t)), a = X(e);
|
|
531
|
-
return a && (i = T(i, a)), await N(i, r(process.cwd(), n)), L.info("unpack complete", {
|
|
532
|
-
file: t,
|
|
533
|
-
dir: n
|
|
534
|
-
}), 0;
|
|
535
|
-
}, ke = async (e) => {
|
|
536
|
-
let [t, ...n] = e;
|
|
537
|
-
return t === "export" ? Se(n) : t === "import" ? Ce(n) : t === "unpack" ? Oe(n) : t === "inspect" ? De(n) : t === "backup" ? we(n) : t === "restore" ? Ee(n) : (L.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
|
|
538
|
-
};
|
|
539
|
-
//#endregion
|
|
540
|
-
export { ke as runDataCommand };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/dataProfile.ts
|
|
2
|
-
var e = async (e) => {
|
|
3
|
-
let { resolve: t } = await import("node:path"), { existsSync: n } = await import("node:fs"), r = [
|
|
4
|
-
e,
|
|
5
|
-
`${e}.profile.ts`,
|
|
6
|
-
`data-profiles/${e}.ts`,
|
|
7
|
-
`data-profiles/${e}.profile.ts`
|
|
8
|
-
].map((e) => t(process.cwd(), e)), i = r.find((e) => n(e));
|
|
9
|
-
if (!i) throw Error(`data profile '${e}' not found (looked for: ${r.join(", ")})`);
|
|
10
|
-
let a = await import(i), o = a.default ?? a.profile;
|
|
11
|
-
if (!o) throw Error(`data profile '${i}' has no default export`);
|
|
12
|
-
return o;
|
|
13
|
-
};
|
|
14
|
-
//#endregion
|
|
15
|
-
export { e as t };
|
package/dist/webDev-CBbKs4dZ.js
DELETED