@voltro/cli 0.44.0 → 0.45.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 +120 -1
- package/dist/{apiBuild-DvWreIQP.js → apiBuild-CDQBNRDx.js} +1 -1
- package/dist/apiBuild-yJCqJYQ8.js +2 -0
- package/dist/bin.d.ts +16 -0
- package/dist/bin.js +46 -11
- package/dist/{build-Ckc-FA7d.js → build-yvsLG77U.js} +7 -7
- package/dist/{checkCommand-BNfEs4KG.js → checkCommand-Boxov8lr.js} +11 -11
- package/dist/{checkCommand-AlbawIal.js → checkCommand-lrzwOzV1.js} +1 -1
- package/dist/codegen-DcE2li1i.js +2 -0
- package/dist/{codegen-CbpGCWLG.js → codegen-sbN1Y8PB.js} +37 -37
- package/dist/codegenCommand-CQOLytL-.js +144 -0
- package/dist/{codemodRunner-BKTnaGOz.js → codemodRunner-5ZhDH3rB.js} +41 -0
- package/dist/{commands-fsnezHLH.js → commands-CEkVoFM5.js} +19 -19
- package/dist/{dashboardCommand-DHDmlo9A.js → dashboardCommand-0ahyp-zA.js} +1 -1
- package/dist/{dataCommand-sfpGV694.js → dataCommand-D4IlZcih.js} +277 -210
- package/dist/{dev-D-JZ6CGQ.js → dev-DAM5CnAa.js} +1856 -1801
- package/dist/{dev-BZO5aLdr.js → dev-P4HyeY9W.js} +1 -1
- package/dist/devActivity.d.ts +16 -0
- package/dist/doctorCommand-2K1dIzYk.js +2 -0
- package/dist/{doctorCommand-BlUyESpD.js → doctorCommand-DW2cwAvC.js} +244 -223
- package/dist/doctorSourceRelations-CPjSwYuZ.js +83 -0
- package/dist/{dormancyCommand-lQxdrU8C.js → dormancyCommand-BqQz7lbM.js} +1 -1
- package/dist/{embeddingsCommand-ByBTju6X.js → embeddingsCommand-DJyArpU4.js} +1 -1
- package/dist/{envCommand-CpCbYAgR.js → envCommand-CviRZx1L.js} +1 -1
- package/dist/{evolveCommand-CBV3-TtK.js → evolveCommand-qdlP-R9G.js} +2 -2
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1 -1
- package/dist/{infoCommand-DJoA9cKD.js → infoCommand-CNWPSUcW.js} +1 -1
- package/dist/inspectState.d.ts +16 -0
- package/dist/{runtimeTrace-DJ9tPhrc.js → runtimeTrace-BDZSt9W_.js} +1 -1
- package/dist/{sdkgen-7dr8-Onw.js → sdkgen-sNaD3oZB.js} +1 -1
- package/dist/{serveCommand-BlnAlNQg.js → serveCommand-BBhT2U94.js} +731 -737
- package/dist/serveCommand-DY2jLy0o.js +2 -0
- package/dist/serveEntry.d.ts +16 -0
- package/dist/serveEntry.js +1 -1
- package/dist/startEntry.d.ts +16 -0
- package/dist/startup.d.ts +16 -0
- package/dist/{test-CLYXrZ2F.js → test-CHb7UwvE.js} +1 -1
- package/dist/updateCommand-BH6EGuCc.js +2 -0
- package/dist/{updateCommand-BvRidHp6.js → updateCommand-rCnhaj84.js} +1 -1
- package/dist/{webhooksCommand-BfO_JF2b.js → webhooksCommand-C0iZZfaI.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/cli.md +63 -3
- package/templates/agent-docs/data.md +33 -3
- package/templates/agent-docs/plugins/versioning.md +2 -0
- package/templates/agent-docs/whats-new.md +83 -14
- 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-XaW40W8x.js +0 -2
- package/dist/codegen-i8QGcHsi.js +0 -2
- package/dist/codegenCommand-iClXoRCW.js +0 -129
- package/dist/doctorCommand-BeLOwq7S.js +0 -2
- package/dist/serveCommand-Dg_E1ev4.js +0 -2
- package/dist/updateCommand-DcKVmLVN.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./cliError-BmdYnghb.js";
|
|
2
|
-
import { c as t, i as n, o as r } from "./dev-
|
|
2
|
+
import { c as t, i as n, o as r } from "./dev-DAM5CnAa.js";
|
|
3
3
|
import { n as i, t as a } from "./cliArgs-D4p8n7EE.js";
|
|
4
4
|
import { r as o } from "./dialectDriver-czCHYpeH.js";
|
|
5
5
|
import { t as s } from "./dataProfile-Cm0YVKSy.js";
|
|
@@ -12,18 +12,39 @@ import { declaredSnapshot as h, fingerprintSchema as g, introspectSchema as _ }
|
|
|
12
12
|
import { connectionIdentity as v, normaliseTarget as y, sameDatabase as b, streamTable as x } from "@voltro/database";
|
|
13
13
|
import { STORAGE_REFS_TABLE as ee, resolveStorageProvider as S } from "@voltro/plugin-storage";
|
|
14
14
|
import { createHash as te } from "node:crypto";
|
|
15
|
-
import {
|
|
16
|
-
import { request as
|
|
17
|
-
import { request as
|
|
15
|
+
import { asImportError as ne, assessLiveImport as re, backupArtifactName as C, backupCommand as ie, classificationFromSnapshot as ae, decryptStream as w, encryptStream as oe, packBundle as T, peekBundle as E, previewMasking as se, restoreCommand as D, runExport as ce, runImport as le, runNativeStep as O, storageAssetSink as ue, storageAssetSource as de, storageBlobSink as fe, storageBlobSource as pe, unpackBundle as me, unpackBundleToDir as k } from "@voltro/data-transfer";
|
|
16
|
+
import { request as he } from "node:http";
|
|
17
|
+
import { request as ge } from "node:https";
|
|
18
18
|
//#region src/longRequest.ts
|
|
19
|
-
var
|
|
19
|
+
var _e = /* @__PURE__ */ new Set([
|
|
20
|
+
"ECONNREFUSED",
|
|
21
|
+
"ETIMEDOUT",
|
|
22
|
+
"EHOSTUNREACH",
|
|
23
|
+
"ENETUNREACH",
|
|
24
|
+
"ENOTFOUND",
|
|
25
|
+
"ECONNRESET",
|
|
26
|
+
"EAI_AGAIN"
|
|
27
|
+
]), A = class extends Error {
|
|
28
|
+
url;
|
|
29
|
+
cause;
|
|
30
|
+
constructor(e, t) {
|
|
31
|
+
let n = (() => {
|
|
32
|
+
try {
|
|
33
|
+
return new URL(e).host;
|
|
34
|
+
} catch {
|
|
35
|
+
return e;
|
|
36
|
+
}
|
|
37
|
+
})();
|
|
38
|
+
super(`the instance at ${n} is not answering (${t.code ?? "connect failed"}).`), this.url = e, this.cause = t, this.name = "InstanceUnreachable";
|
|
39
|
+
}
|
|
40
|
+
}, j = class extends Error {
|
|
20
41
|
elapsedMs;
|
|
21
42
|
url;
|
|
22
43
|
constructor(e, t) {
|
|
23
44
|
super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
|
|
24
45
|
}
|
|
25
|
-
},
|
|
26
|
-
let i = new URL(e), a = i.protocol === "https:" ?
|
|
46
|
+
}, M = async (e, t, n, r) => {
|
|
47
|
+
let i = new URL(e), a = i.protocol === "https:" ? ge : he, o = Date.now();
|
|
27
48
|
return await new Promise((s, c) => {
|
|
28
49
|
let l = a({
|
|
29
50
|
protocol: i.protocol,
|
|
@@ -41,8 +62,8 @@ var A = class extends Error {
|
|
|
41
62
|
})), e.on("error", c);
|
|
42
63
|
});
|
|
43
64
|
if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
|
|
44
|
-
l.destroy(new
|
|
45
|
-
}), l.on("error", c), typeof t == "string" || Buffer.isBuffer(t)) {
|
|
65
|
+
l.destroy(new j(Date.now() - o, e));
|
|
66
|
+
}), l.on("error", (t) => c(_e.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
|
|
46
67
|
l.end(t);
|
|
47
68
|
return;
|
|
48
69
|
}
|
|
@@ -55,7 +76,7 @@ var A = class extends Error {
|
|
|
55
76
|
}
|
|
56
77
|
})();
|
|
57
78
|
});
|
|
58
|
-
},
|
|
79
|
+
}, ve = (e) => `${e.message}\n That address came from --api-url (or VOLTRO_API_URL) — it is the running instance, not the database.\n Nothing was sent, so nothing was imported. Check the instance is up and reachable from here\n (a VPN, an ingress or a stopped deployment are the usual three), e.g. curl -sS ${e.url}/_voltro/inspect/app`, ye = (e, t) => `the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. This is a client-side deadline, not a failure on the server: the upload itself finished long before, and the response arrives only when the import does. The instance is very probably STILL IMPORTING and will finish.\n` + (t === "replace" ? " Do NOT simply re-run: --mode replace empties the target first, so a second run started while the first is mid-flight would empty it under the first one. Check the instance's log for the import's completion, and check the target's row counts, before deciding." : ` Re-running the same bundle is safe once the first run has finished (--mode ${t} is idempotent), but check the instance's log first — the work may already be done.`) + "\n To keep a deadline anyway, pass --timeout <seconds>; the default is to wait.", be = 16777216, xe = async function* (e, t) {
|
|
59
80
|
let n = [], r = 0, i = 0, a = function* (e) {
|
|
60
81
|
let t = Buffer.concat(n);
|
|
61
82
|
n = [], r = 0;
|
|
@@ -77,12 +98,12 @@ var A = class extends Error {
|
|
|
77
98
|
i += a.byteLength, yield s;
|
|
78
99
|
}
|
|
79
100
|
yield* a(!0);
|
|
80
|
-
},
|
|
101
|
+
}, Se = (e) => te("sha256").update(Buffer.from(e)).digest("hex"), Ce = async (e) => {
|
|
81
102
|
let t = 0, n = 0, r = {
|
|
82
103
|
status: 0,
|
|
83
104
|
body: {}
|
|
84
105
|
};
|
|
85
|
-
for await (let i of
|
|
106
|
+
for await (let i of xe(e.stream, e.chunkBytes)) {
|
|
86
107
|
if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
|
|
87
108
|
n++;
|
|
88
109
|
continue;
|
|
@@ -108,13 +129,13 @@ var A = class extends Error {
|
|
|
108
129
|
bytes: t,
|
|
109
130
|
resumedFrom: e.alreadyHeld
|
|
110
131
|
};
|
|
111
|
-
},
|
|
132
|
+
}, N = "voltro-backup-stamp.json", we = (e) => ({
|
|
112
133
|
stampVersion: 1,
|
|
113
134
|
dialect: e.dialect,
|
|
114
135
|
schemaFingerprint: e.schemaFingerprint,
|
|
115
136
|
voltroVersion: e.voltroVersion,
|
|
116
137
|
createdAt: e.createdAt
|
|
117
|
-
}),
|
|
138
|
+
}), P = (e) => {
|
|
118
139
|
let t;
|
|
119
140
|
try {
|
|
120
141
|
t = JSON.parse(e);
|
|
@@ -132,10 +153,10 @@ var A = class extends Error {
|
|
|
132
153
|
voltroVersion: n.voltroVersion,
|
|
133
154
|
createdAt: n.createdAt
|
|
134
155
|
};
|
|
135
|
-
},
|
|
156
|
+
}, Te = (e) => {
|
|
136
157
|
let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
|
|
137
158
|
if (t === null) return {
|
|
138
|
-
warn: `no ${
|
|
159
|
+
warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
139
160
|
info: []
|
|
140
161
|
};
|
|
141
162
|
let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
|
|
@@ -146,20 +167,20 @@ var A = class extends Error {
|
|
|
146
167
|
refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
|
|
147
168
|
info: i
|
|
148
169
|
};
|
|
149
|
-
},
|
|
170
|
+
}, F = (e) => {
|
|
150
171
|
try {
|
|
151
172
|
let t = new URL(e), n = t.pathname.replace(/^\//, "");
|
|
152
173
|
return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
|
|
153
174
|
} catch {
|
|
154
175
|
return e.trim();
|
|
155
176
|
}
|
|
156
|
-
},
|
|
177
|
+
}, Ee = (e) => {
|
|
157
178
|
let t = e.flagUrl ?? e.envUrl;
|
|
158
179
|
if (t === void 0 || t.trim() === "") return {
|
|
159
180
|
ok: !1,
|
|
160
181
|
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."
|
|
161
182
|
};
|
|
162
|
-
let n =
|
|
183
|
+
let n = F(t);
|
|
163
184
|
return e.liveKeys.some((e) => e === n) ? {
|
|
164
185
|
ok: !1,
|
|
165
186
|
error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
|
|
@@ -167,7 +188,7 @@ var A = class extends Error {
|
|
|
167
188
|
ok: !0,
|
|
168
189
|
url: t
|
|
169
190
|
};
|
|
170
|
-
},
|
|
191
|
+
}, De = (e) => {
|
|
171
192
|
let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
|
|
172
193
|
return r === 0 ? {
|
|
173
194
|
pass: !1,
|
|
@@ -182,24 +203,24 @@ var A = class extends Error {
|
|
|
182
203
|
pass: !0,
|
|
183
204
|
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
184
205
|
};
|
|
185
|
-
},
|
|
206
|
+
}, I = d({ scope: "voltro:data" }), Oe = async () => {
|
|
186
207
|
try {
|
|
187
208
|
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");
|
|
188
209
|
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
189
210
|
} catch {
|
|
190
211
|
return "unknown";
|
|
191
212
|
}
|
|
192
|
-
},
|
|
213
|
+
}, ke = async (e) => {
|
|
193
214
|
try {
|
|
194
|
-
return g(await
|
|
215
|
+
return g(await H(e));
|
|
195
216
|
} catch {
|
|
196
217
|
return null;
|
|
197
218
|
}
|
|
198
219
|
}, L = (e) => {
|
|
199
220
|
if (e.event !== "done") return;
|
|
200
221
|
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
201
|
-
|
|
202
|
-
},
|
|
222
|
+
I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
223
|
+
}, Ae = async (e) => {
|
|
203
224
|
let t = (e) => e?.meta?.databaseId;
|
|
204
225
|
if (e) {
|
|
205
226
|
let n = await l({
|
|
@@ -219,21 +240,21 @@ var A = class extends Error {
|
|
|
219
240
|
};
|
|
220
241
|
}
|
|
221
242
|
return null;
|
|
222
|
-
},
|
|
223
|
-
let t = e.includes("--allow-live"), n = t ? null : await
|
|
224
|
-
dialect:
|
|
243
|
+
}, je = async (e) => {
|
|
244
|
+
let t = e.includes("--allow-live"), n = t ? null : await Ae(R(e, "--api-url")), i = v({
|
|
245
|
+
dialect: B(),
|
|
225
246
|
...r()
|
|
226
247
|
}), a = y({
|
|
227
|
-
dialect:
|
|
248
|
+
dialect: B(),
|
|
228
249
|
...r()
|
|
229
|
-
}), { refuse: o, message: s } =
|
|
250
|
+
}), { refuse: o, message: s } = re({
|
|
230
251
|
liveInstance: n?.label ?? null,
|
|
231
252
|
allowLive: t,
|
|
232
253
|
sameTarget: b(i, n?.databaseId),
|
|
233
254
|
...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
|
|
234
255
|
});
|
|
235
|
-
return !o || (
|
|
236
|
-
},
|
|
256
|
+
return !o || (I.error(s ?? "refusing: live instance detected"), !1);
|
|
257
|
+
}, R = i, Me = [
|
|
237
258
|
"--api-url",
|
|
238
259
|
"--bundle-key",
|
|
239
260
|
"--compression",
|
|
@@ -251,44 +272,44 @@ var A = class extends Error {
|
|
|
251
272
|
"--timeout",
|
|
252
273
|
"--tenant",
|
|
253
274
|
"--token"
|
|
254
|
-
],
|
|
275
|
+
], z = (e) => a(e, Me), B = () => {
|
|
255
276
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
256
277
|
if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
257
278
|
return t;
|
|
258
|
-
},
|
|
279
|
+
}, V = async (e, t) => {
|
|
259
280
|
let n = (await o(e)).makeSqlLayer(t);
|
|
260
281
|
return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
|
|
261
|
-
},
|
|
282
|
+
}, H = (e) => V(e, r()), Ne = () => {
|
|
262
283
|
let e = r(), t = [];
|
|
263
|
-
return e.url && t.push(
|
|
264
|
-
},
|
|
284
|
+
return e.url && t.push(F(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
|
|
285
|
+
}, U = async () => {
|
|
265
286
|
let r = process.cwd(), i = await t(r);
|
|
266
287
|
if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
|
|
267
|
-
let a = await n(i, void 0), o =
|
|
288
|
+
let a = await n(i, void 0), o = B();
|
|
268
289
|
return {
|
|
269
290
|
store: a,
|
|
270
|
-
snapshot: await
|
|
291
|
+
snapshot: await H(o),
|
|
271
292
|
dialect: o
|
|
272
293
|
};
|
|
273
|
-
},
|
|
294
|
+
}, W = (e) => x(e, {
|
|
274
295
|
table: ee,
|
|
275
296
|
chunkSize: 500
|
|
276
297
|
}).pipe(p.map((e) => ({
|
|
277
298
|
key: String(e.key),
|
|
278
299
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
279
300
|
size: Number(e.size ?? 0)
|
|
280
|
-
}))),
|
|
301
|
+
}))), G = (e) => !!e && e.endsWith(".vbundle"), K = (e) => R(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Pe = (e) => e.includes("--encrypt") || R(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Fe = async (e, t) => {
|
|
281
302
|
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
282
303
|
await r(i.from(e), n(t));
|
|
283
|
-
},
|
|
304
|
+
}, q = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
284
305
|
let { createReadStream: t } = await import("node:fs");
|
|
285
306
|
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
286
|
-
} }),
|
|
287
|
-
let r =
|
|
307
|
+
} }), J = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Ie = (t, n) => {
|
|
308
|
+
let r = R(t, "--tenant"), i = R(t, "--tables"), a = R(t, "--exclude");
|
|
288
309
|
if (a !== void 0) {
|
|
289
310
|
if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
|
|
290
311
|
if (r !== void 0) throw new e("voltro data export: --exclude does not apply to --tenant, whose table set is derived from the tenant closure.");
|
|
291
|
-
let t =
|
|
312
|
+
let t = J(a), o = new Set(n), s = t.filter((e) => !o.has(e));
|
|
292
313
|
if (s.length > 0) throw new e(`voltro data export: --exclude names ${s.length} table(s) that do not exist: ${s.join(", ")}. An exclusion that excludes nothing would leave the run looking like it worked.`);
|
|
293
314
|
let c = new Set(t);
|
|
294
315
|
return {
|
|
@@ -301,15 +322,15 @@ var A = class extends Error {
|
|
|
301
322
|
tenantId: r
|
|
302
323
|
} : i ? {
|
|
303
324
|
kind: "tables",
|
|
304
|
-
tables:
|
|
325
|
+
tables: J(i)
|
|
305
326
|
} : { kind: "all" };
|
|
306
|
-
},
|
|
327
|
+
}, Le = async () => {
|
|
307
328
|
let e = await u(process.cwd());
|
|
308
|
-
return
|
|
309
|
-
},
|
|
310
|
-
let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id ===
|
|
329
|
+
return ae(h(e));
|
|
330
|
+
}, Re = "voltro/tenant", ze = async () => {
|
|
331
|
+
let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Re)).map((e) => e.tableName);
|
|
311
332
|
return e.length > 0 ? e : void 0;
|
|
312
|
-
},
|
|
333
|
+
}, Be = (e) => {
|
|
313
334
|
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
314
335
|
console.log(`\n ${t.table}`);
|
|
315
336
|
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
@@ -322,8 +343,8 @@ var A = class extends Error {
|
|
|
322
343
|
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
323
344
|
for (let t of e.unclassified) console.log(` ${t}`);
|
|
324
345
|
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
325
|
-
},
|
|
326
|
-
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a =
|
|
346
|
+
}, Ve = async (t) => {
|
|
347
|
+
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = R(t.args, "--chunk-size"), o = a === void 0 ? be : Math.round(Number(a) * 1024 * 1024);
|
|
327
348
|
if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
|
|
328
349
|
let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
|
|
329
350
|
for (; l <= o;) {
|
|
@@ -338,11 +359,11 @@ var A = class extends Error {
|
|
|
338
359
|
kind: "small",
|
|
339
360
|
body: Buffer.concat(c)
|
|
340
361
|
};
|
|
341
|
-
let d = n(process.cwd(), t.bundleDir), f =
|
|
362
|
+
let d = n(process.cwd(), t.bundleDir), f = Se(G(t.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await M(t.url, Buffer.alloc(0), {
|
|
342
363
|
...t.headers,
|
|
343
364
|
"x-import-upload-id": p,
|
|
344
365
|
"x-import-probe": "1"
|
|
345
|
-
},
|
|
366
|
+
}, Z(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
|
|
346
367
|
h > 0 && t.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
|
|
347
368
|
let g = (async function* () {
|
|
348
369
|
for (let e of c) yield e;
|
|
@@ -351,7 +372,7 @@ var A = class extends Error {
|
|
|
351
372
|
if (e.done === !0) return;
|
|
352
373
|
yield e.value;
|
|
353
374
|
}
|
|
354
|
-
})(), _ = 0, v = await
|
|
375
|
+
})(), _ = 0, v = await Ce({
|
|
355
376
|
stream: g,
|
|
356
377
|
chunkBytes: o,
|
|
357
378
|
uploadId: p,
|
|
@@ -365,7 +386,7 @@ var A = class extends Error {
|
|
|
365
386
|
t.log.info("api import: upload done — the instance is importing now. This request stays open until it finishes, which on a full bundle is minutes, and nothing more is printed until then. It is not a hang; the instance logs the import itself.");
|
|
366
387
|
},
|
|
367
388
|
target: { send: async (e, n) => {
|
|
368
|
-
let r = await
|
|
389
|
+
let r = await M(t.url, e, n, Z(t.args)), i = {};
|
|
369
390
|
try {
|
|
370
391
|
i = JSON.parse(r.body.toString("utf8") || "{}");
|
|
371
392
|
} catch {}
|
|
@@ -387,59 +408,60 @@ var A = class extends Error {
|
|
|
387
408
|
body: Buffer.from(JSON.stringify(v.body))
|
|
388
409
|
}
|
|
389
410
|
};
|
|
390
|
-
},
|
|
391
|
-
let n =
|
|
411
|
+
}, Y = (e) => `--mode replace with --no-atomic against a ${e ? "RUNNING instance" : "live database"}: the target is empty between the delete and the load, and anything the application writes in that window can collide with a row from the bundle — or survive the import as a row the bundle never carried. Drop --no-atomic to run the whole load in one transaction (concurrent writers then wait instead of racing), or stop the traffic for the duration.`, X = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", Z = (t) => {
|
|
412
|
+
let n = R(t, "--timeout");
|
|
392
413
|
if (n === void 0) return {};
|
|
393
414
|
let r = Number(n);
|
|
394
415
|
if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
|
|
395
416
|
return { timeoutMs: Math.round(r * 1e3) };
|
|
396
|
-
},
|
|
397
|
-
let t =
|
|
398
|
-
if (!r && !(t && n)) return
|
|
417
|
+
}, He = async (e) => {
|
|
418
|
+
let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), r = z(e);
|
|
419
|
+
if (!r && !(t && n)) return I.error("usage: voltro data export <outDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--profile <name>] [--tenant <id>] [--tables a,b] [--exclude a,b] [--bundle-key <storage-key>] [--assets] [--compression zstd|gzip|none] [--consistency live|snapshot] [--dry-run]"), 1;
|
|
399
420
|
if (t) {
|
|
400
|
-
let t =
|
|
401
|
-
if (!t) return
|
|
402
|
-
let i =
|
|
403
|
-
if (!i) return
|
|
404
|
-
if (
|
|
405
|
-
let a =
|
|
406
|
-
...
|
|
407
|
-
...
|
|
421
|
+
let t = R(e, "--api-url");
|
|
422
|
+
if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
423
|
+
let i = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
424
|
+
if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
425
|
+
if (R(e, "--exclude") !== void 0) return I.error("--exclude is a direct-target flag: it expands against the live table list, which --target api does not expose. Use --tables with the explicit set."), 1;
|
|
426
|
+
let a = R(e, "--tables"), o = {
|
|
427
|
+
...R(e, "--profile") ? { profile: R(e, "--profile") } : {},
|
|
428
|
+
...R(e, "--tenant") ? { tenant: R(e, "--tenant") } : {},
|
|
408
429
|
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
409
|
-
...
|
|
410
|
-
...
|
|
430
|
+
...R(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
431
|
+
...R(e, "--compression") ? { compression: R(e, "--compression") } : {},
|
|
411
432
|
...e.includes("--assets") ? { assets: !0 } : {},
|
|
412
433
|
...n ? { bundleKey: n } : {}
|
|
413
434
|
}, s;
|
|
414
435
|
try {
|
|
415
|
-
s = await
|
|
436
|
+
s = await M(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
|
|
416
437
|
"content-type": "application/json",
|
|
417
438
|
authorization: `Bearer ${i}`
|
|
418
|
-
},
|
|
439
|
+
}, Z(e));
|
|
419
440
|
} catch (e) {
|
|
420
|
-
if (e instanceof
|
|
441
|
+
if (e instanceof j) return I.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline, not a server failure — the response arrives only when the export does. Nothing was written locally; re-run, or pass --timeout <seconds> (the default is to wait).`), 1;
|
|
442
|
+
if (e instanceof A) return I.error(`api export: ${ve(e)}`), 1;
|
|
421
443
|
throw e;
|
|
422
444
|
}
|
|
423
|
-
if (s.status < 200 || s.status >= 300) return
|
|
424
|
-
if (n) return
|
|
445
|
+
if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
|
|
446
|
+
if (n) return I.info("api export complete (pushed to storage)", {
|
|
425
447
|
via: t,
|
|
426
448
|
...JSON.parse(s.body.toString("utf8"))
|
|
427
449
|
}), 0;
|
|
428
450
|
let { resolve: c } = await import("node:path"), l = new Uint8Array(s.body);
|
|
429
451
|
return await k((async function* () {
|
|
430
452
|
yield l;
|
|
431
|
-
})(), c(process.cwd(), r)),
|
|
453
|
+
})(), c(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
|
|
432
454
|
outDir: r,
|
|
433
455
|
tables: s.headers["x-export-tables"],
|
|
434
456
|
rows: s.headers["x-export-rows"],
|
|
435
457
|
masked: s.headers["x-export-masked"]
|
|
436
458
|
}), 0;
|
|
437
459
|
}
|
|
438
|
-
if (!r) return
|
|
439
|
-
let i =
|
|
460
|
+
if (!r) return I.error("a <outDir> is required for a direct export"), 1;
|
|
461
|
+
let i = R(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await U(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = R(e, "--compression") ?? a.compression, m = R(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? Ie(e, c.tables.map((e) => e.name)), v = h ? await Le() : void 0, y = _.kind === "tenant" ? await ze() : void 0, b = y ? { tenantTables: y } : {};
|
|
440
462
|
if (e.includes("--dry-run")) {
|
|
441
463
|
let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
|
|
442
|
-
if (
|
|
464
|
+
if (I.info("export DRY RUN — nothing was written", {
|
|
443
465
|
outDir: r,
|
|
444
466
|
tables: e.length,
|
|
445
467
|
scope: _.kind,
|
|
@@ -447,8 +469,8 @@ var A = class extends Error {
|
|
|
447
469
|
compression: p ?? "zstd",
|
|
448
470
|
assets: u,
|
|
449
471
|
masking: h ? "profile policy applies" : "none"
|
|
450
|
-
}),
|
|
451
|
-
let t = await f.runPromise(
|
|
472
|
+
}), I.info(`would export: ${e.join(", ")}`), !h) return 0;
|
|
473
|
+
let t = await f.runPromise(se({
|
|
452
474
|
store: o,
|
|
453
475
|
snapshot: c,
|
|
454
476
|
masking: h,
|
|
@@ -458,12 +480,12 @@ var A = class extends Error {
|
|
|
458
480
|
...b
|
|
459
481
|
}
|
|
460
482
|
}));
|
|
461
|
-
return
|
|
483
|
+
return Be(t), +(t.unclassified.length > 0);
|
|
462
484
|
}
|
|
463
|
-
if (
|
|
485
|
+
if (G(r)) {
|
|
464
486
|
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-"));
|
|
465
487
|
try {
|
|
466
|
-
let t = await f.runPromise(
|
|
488
|
+
let t = await f.runPromise(ce({
|
|
467
489
|
store: o,
|
|
468
490
|
snapshot: c,
|
|
469
491
|
dialect: l,
|
|
@@ -479,14 +501,14 @@ var A = class extends Error {
|
|
|
479
501
|
onProgress: L
|
|
480
502
|
})), n = T({
|
|
481
503
|
dir: s,
|
|
482
|
-
...d ? { blobs:
|
|
483
|
-
}), a =
|
|
504
|
+
...d ? { blobs: pe(d, () => W(o)) } : {}
|
|
505
|
+
}), a = Pe(e);
|
|
484
506
|
if (a) {
|
|
485
|
-
let t =
|
|
486
|
-
if (!t) return
|
|
487
|
-
n =
|
|
507
|
+
let t = K(e);
|
|
508
|
+
if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
509
|
+
n = oe(n, t);
|
|
488
510
|
}
|
|
489
|
-
return await
|
|
511
|
+
return await Fe(n, i(process.cwd(), r)), I.info("export complete (single file)", {
|
|
490
512
|
file: r,
|
|
491
513
|
encrypted: a,
|
|
492
514
|
assets: u,
|
|
@@ -500,7 +522,7 @@ var A = class extends Error {
|
|
|
500
522
|
});
|
|
501
523
|
}
|
|
502
524
|
}
|
|
503
|
-
let x = await f.runPromise(
|
|
525
|
+
let x = await f.runPromise(ce({
|
|
504
526
|
store: o,
|
|
505
527
|
snapshot: c,
|
|
506
528
|
dialect: l,
|
|
@@ -513,10 +535,10 @@ var A = class extends Error {
|
|
|
513
535
|
...p ? { compression: p } : {},
|
|
514
536
|
...h ? { masking: h } : {},
|
|
515
537
|
...v ? { classification: v } : {},
|
|
516
|
-
...d ? { assets:
|
|
538
|
+
...d ? { assets: de(d, () => W(o)) } : {},
|
|
517
539
|
onProgress: L
|
|
518
540
|
}));
|
|
519
|
-
return
|
|
541
|
+
return I.info("export complete", {
|
|
520
542
|
outDir: r,
|
|
521
543
|
tables: x.tables.length,
|
|
522
544
|
rows: x.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -524,13 +546,13 @@ var A = class extends Error {
|
|
|
524
546
|
compression: x.compression,
|
|
525
547
|
...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
|
|
526
548
|
}), 0;
|
|
527
|
-
},
|
|
528
|
-
let t =
|
|
549
|
+
}, Ue = () => "dry run checked: schema fit, cross-dialect portability, mode legality, the table selection. It did NOT read per-table checksums (those stream during the load) and cannot see conflicts that depend on the target's current rows.", We = (e) => {
|
|
550
|
+
let t = R(e, "--tables");
|
|
529
551
|
return {
|
|
530
552
|
...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
531
553
|
...e.includes("--dry-run") ? { dryRun: !0 } : {}
|
|
532
554
|
};
|
|
533
|
-
},
|
|
555
|
+
}, Ge = (e) => ({
|
|
534
556
|
tables: e.tablesWritten,
|
|
535
557
|
rows: e.rowsWritten,
|
|
536
558
|
carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -538,102 +560,146 @@ var A = class extends Error {
|
|
|
538
560
|
skippedTables: e.tablesSkipped,
|
|
539
561
|
skippedRows: e.rowsSkipped
|
|
540
562
|
} : {}
|
|
541
|
-
}),
|
|
563
|
+
}), Ke = (e, t) => {
|
|
542
564
|
if (e.includes("--dry-run")) {
|
|
543
|
-
|
|
565
|
+
I.info("import DRY RUN — nothing was written", t), I.info(Ue());
|
|
544
566
|
return;
|
|
545
567
|
}
|
|
546
568
|
if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
|
|
547
|
-
|
|
569
|
+
I.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
|
|
548
570
|
return;
|
|
549
571
|
}
|
|
550
|
-
|
|
551
|
-
},
|
|
552
|
-
let t = e;
|
|
553
|
-
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return;
|
|
572
|
+
I.info("import complete", t);
|
|
573
|
+
}, qe = Symbol("refused"), Q = (e) => {
|
|
574
|
+
let t = ne(e) ?? e;
|
|
575
|
+
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
|
|
554
576
|
let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
let
|
|
561
|
-
|
|
562
|
-
|
|
577
|
+
I.error(`import refused ${i} row(s), of which ${a} are the actual failures — the rest could not land because a row they reference did not.`);
|
|
578
|
+
let o = Array.isArray(t.byTable) ? t.byTable : [];
|
|
579
|
+
o.length > 1 && I.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
|
|
580
|
+
for (let e of r) I.error(` ${e.table} ${e.id} ${e.reason}`);
|
|
581
|
+
n.length < i && I.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
|
|
582
|
+
let s = i - a;
|
|
583
|
+
return s > 0 && I.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
|
|
584
|
+
}, Je = (e) => {
|
|
585
|
+
let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
|
|
586
|
+
try {
|
|
587
|
+
let e = JSON.parse(t), r = e.error ?? e.message;
|
|
588
|
+
typeof r == "string" && (n = r);
|
|
589
|
+
} catch {}
|
|
590
|
+
let r = n.indexOf("RowsRefusedError");
|
|
591
|
+
if (r < 0) return;
|
|
592
|
+
let i = n.indexOf("{", r);
|
|
593
|
+
if (i < 0) return;
|
|
594
|
+
let a = 0, o = !1, s = !1;
|
|
595
|
+
for (let e = i; e < n.length; e++) {
|
|
596
|
+
let t = n[e];
|
|
597
|
+
if (o) {
|
|
598
|
+
if (s) {
|
|
599
|
+
s = !1;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (t === "\\") {
|
|
603
|
+
s = !0;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
t === "\"" && (o = !1);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
if (t === "\"") {
|
|
610
|
+
o = !0;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
if (t === "{") a++;
|
|
614
|
+
else if (t === "}" && --a === 0) try {
|
|
615
|
+
let t = JSON.parse(n.slice(i, e + 1));
|
|
616
|
+
return Array.isArray(t.rows) ? {
|
|
617
|
+
_tag: "RowsRefusedError",
|
|
618
|
+
...t
|
|
619
|
+
} : void 0;
|
|
620
|
+
} catch {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}, Ye = async (e) => {
|
|
625
|
+
let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), i = z(e);
|
|
626
|
+
if (!i && !(t && n)) return I.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--bundle-key <storage-key>] [--tables a,b] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] [--dry-run] (--force: import despite schema drift AND cross-dialect warnings; --dry-run: report what would move, write nothing)"), 1;
|
|
627
|
+
let a = R(e, "--mode") ?? "upsert", o = R(e, "--on-conflict") ?? "skip";
|
|
563
628
|
if (t) {
|
|
564
|
-
let t =
|
|
565
|
-
if (!t) return
|
|
566
|
-
let r =
|
|
567
|
-
if (!r) return
|
|
568
|
-
let s =
|
|
629
|
+
let t = R(e, "--api-url");
|
|
630
|
+
if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
631
|
+
let r = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
632
|
+
if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
633
|
+
let s = R(e, "--tables"), c = {
|
|
569
634
|
authorization: `Bearer ${r}`,
|
|
570
635
|
"x-import-mode": a,
|
|
571
636
|
"x-import-on-conflict": o,
|
|
572
|
-
...
|
|
637
|
+
...X(e, a) ? { "x-import-atomic": "1" } : {},
|
|
573
638
|
...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
|
|
574
639
|
...e.includes("--force") ? { "x-import-force": "1" } : {},
|
|
575
640
|
...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
|
|
576
641
|
...s ? { "x-import-tables": s } : {}
|
|
577
642
|
}, { resolve: l } = await import("node:path"), u;
|
|
578
643
|
if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
|
|
579
|
-
else if (
|
|
580
|
-
let t =
|
|
644
|
+
else if (G(i)) {
|
|
645
|
+
let t = q(l(process.cwd(), i)), n = K(e);
|
|
581
646
|
n && (t = w(t, n)), c["content-type"] = "application/octet-stream", u = t;
|
|
582
|
-
} else await (await import("node:fs/promises")).access(l(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) &&
|
|
583
|
-
a === "replace" && !
|
|
647
|
+
} else await (await import("node:fs/promises")).access(l(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && I.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."), c["content-type"] = "application/octet-stream", u = T({ dir: l(process.cwd(), i) });
|
|
648
|
+
a === "replace" && !X(e, a) && I.warn(Y(!0));
|
|
584
649
|
let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
|
|
585
650
|
try {
|
|
586
|
-
let t = typeof u == "string" ? null : await
|
|
651
|
+
let t = typeof u == "string" ? null : await Ve({
|
|
587
652
|
stream: u,
|
|
588
653
|
url: d,
|
|
589
654
|
headers: c,
|
|
590
655
|
bundleDir: i,
|
|
591
656
|
args: e,
|
|
592
|
-
log:
|
|
657
|
+
log: I
|
|
593
658
|
});
|
|
594
|
-
(t === null || t.kind === "small") &&
|
|
659
|
+
(t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), f = t === null ? await M(d, u, c, Z(e)) : t.kind === "sent" ? t.res : await M(d, t.body, c, Z(e));
|
|
595
660
|
} catch (e) {
|
|
596
|
-
if (e instanceof
|
|
661
|
+
if (e instanceof j) return I.error(`api import: ${ye(e, a)}`), 1;
|
|
662
|
+
if (e instanceof A) return I.error(`api import: ${ve(e)}`), 1;
|
|
597
663
|
throw e;
|
|
598
664
|
}
|
|
599
|
-
if (f.status < 200 || f.status >= 300) return
|
|
665
|
+
if (f.status < 200 || f.status >= 300) return Q(Je(f.body)) || I.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
|
|
600
666
|
let p = JSON.parse(f.body.toString("utf8"));
|
|
601
|
-
return e.includes("--dry-run") ? (
|
|
667
|
+
return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
|
|
602
668
|
via: t,
|
|
603
669
|
...p
|
|
604
|
-
}),
|
|
670
|
+
}), I.info(Ue()), 0) : (I.info("api import complete (imported by the running instance)", {
|
|
605
671
|
via: t,
|
|
606
672
|
...p
|
|
607
673
|
}), 0);
|
|
608
674
|
}
|
|
609
|
-
if (!i) return
|
|
610
|
-
if (!await
|
|
611
|
-
a === "replace" && e.includes("--allow-live") && !
|
|
612
|
-
let { store: s, snapshot: c } = await
|
|
613
|
-
if (
|
|
675
|
+
if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
|
|
676
|
+
if (!await je(e)) return 1;
|
|
677
|
+
a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(Y(!1));
|
|
678
|
+
let { store: s, snapshot: c } = await U(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = B() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
|
|
679
|
+
if (G(i)) {
|
|
614
680
|
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-"));
|
|
615
681
|
try {
|
|
616
|
-
let t =
|
|
617
|
-
return n && (t = w(t, n)), await
|
|
682
|
+
let t = q(r(process.cwd(), i)), n = K(e);
|
|
683
|
+
return n && (t = w(t, n)), await me(t, {
|
|
618
684
|
dir: h,
|
|
619
|
-
blobs:
|
|
620
|
-
}),
|
|
685
|
+
blobs: fe(d ?? S({}))
|
|
686
|
+
}), Ke(e, {
|
|
621
687
|
file: i,
|
|
622
|
-
...
|
|
688
|
+
...Ge(await f.runPromise(le({
|
|
623
689
|
store: s,
|
|
624
690
|
bundleDir: h,
|
|
625
691
|
mode: a,
|
|
626
692
|
onConflict: o,
|
|
627
|
-
atomic:
|
|
693
|
+
atomic: X(e, a),
|
|
628
694
|
verify: !e.includes("--no-verify"),
|
|
629
|
-
targetDialect:
|
|
695
|
+
targetDialect: B(),
|
|
630
696
|
allowIncompatible: l,
|
|
631
697
|
targetSnapshot: c,
|
|
632
698
|
force: l,
|
|
633
699
|
onWarn: u,
|
|
634
700
|
onProgress: L,
|
|
635
701
|
assetsAlreadyRestored: !0,
|
|
636
|
-
...
|
|
702
|
+
...We(e),
|
|
637
703
|
...p ? { copyLoader: p } : {}
|
|
638
704
|
})))
|
|
639
705
|
}), 0;
|
|
@@ -644,31 +710,32 @@ var A = class extends Error {
|
|
|
644
710
|
});
|
|
645
711
|
}
|
|
646
712
|
}
|
|
647
|
-
let m = await f.runPromise(
|
|
713
|
+
let m = await f.runPromise(le({
|
|
648
714
|
store: s,
|
|
649
715
|
bundleDir: i,
|
|
650
716
|
mode: a,
|
|
651
717
|
onConflict: o,
|
|
652
|
-
atomic:
|
|
718
|
+
atomic: X(e, a),
|
|
653
719
|
verify: !e.includes("--no-verify"),
|
|
654
|
-
targetDialect:
|
|
720
|
+
targetDialect: B(),
|
|
655
721
|
allowIncompatible: l,
|
|
656
722
|
targetSnapshot: c,
|
|
657
723
|
force: l,
|
|
658
724
|
onWarn: u,
|
|
659
725
|
onProgress: L,
|
|
660
|
-
...
|
|
661
|
-
...d ? { assets:
|
|
726
|
+
...We(e),
|
|
727
|
+
...d ? { assets: ue(d) } : {},
|
|
662
728
|
...p ? { copyLoader: p } : {}
|
|
663
729
|
})).catch((e) => {
|
|
664
|
-
|
|
730
|
+
if (Q(e)) return qe;
|
|
731
|
+
throw e;
|
|
665
732
|
}).finally(() => p?.close());
|
|
666
|
-
return
|
|
733
|
+
return typeof m == "symbol" ? 1 : (Ke(e, {
|
|
667
734
|
bundleDir: i,
|
|
668
|
-
...
|
|
735
|
+
...Ge(m),
|
|
669
736
|
assets: m.manifest.assets.count
|
|
670
|
-
}), 0;
|
|
671
|
-
},
|
|
737
|
+
}), 0);
|
|
738
|
+
}, Xe = () => {
|
|
672
739
|
let e = r();
|
|
673
740
|
return {
|
|
674
741
|
...e.url ? { url: e.url } : {},
|
|
@@ -678,75 +745,75 @@ var A = class extends Error {
|
|
|
678
745
|
...e.password ? { password: e.password } : {},
|
|
679
746
|
...e.database ? { database: e.database } : {}
|
|
680
747
|
};
|
|
681
|
-
},
|
|
682
|
-
let t =
|
|
683
|
-
if (!t) return
|
|
684
|
-
let n =
|
|
748
|
+
}, Ze = async (e) => {
|
|
749
|
+
let t = z(e);
|
|
750
|
+
if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
|
|
751
|
+
let n = B(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
|
|
685
752
|
await r(t, { recursive: !0 });
|
|
686
753
|
let o = a(t, C(n));
|
|
687
|
-
await f.runPromise(O(
|
|
688
|
-
let s =
|
|
754
|
+
await f.runPromise(O(ie(n, Xe(), o)));
|
|
755
|
+
let s = we({
|
|
689
756
|
dialect: n,
|
|
690
|
-
schemaFingerprint: await
|
|
691
|
-
voltroVersion: await
|
|
757
|
+
schemaFingerprint: await ke(n),
|
|
758
|
+
voltroVersion: await Oe(),
|
|
692
759
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
693
760
|
});
|
|
694
|
-
return await i(a(t,
|
|
761
|
+
return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
|
|
695
762
|
dialect: n,
|
|
696
763
|
artifact: o,
|
|
697
|
-
stamp:
|
|
764
|
+
stamp: N,
|
|
698
765
|
schemaFingerprint: s.schemaFingerprint ?? "<none>",
|
|
699
766
|
assets: "NOT included — use `voltro data export --assets` for rows + blobs"
|
|
700
767
|
}), 0;
|
|
701
|
-
},
|
|
702
|
-
let n =
|
|
703
|
-
flagUrl:
|
|
768
|
+
}, Qe = async (e, t) => {
|
|
769
|
+
let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Ee({
|
|
770
|
+
flagUrl: R(t, "--drill-url"),
|
|
704
771
|
envUrl: process.env.DRILL_DB_URL,
|
|
705
|
-
liveKeys:
|
|
772
|
+
liveKeys: Ne()
|
|
706
773
|
});
|
|
707
|
-
if (!o.ok) return
|
|
708
|
-
let s = await i(r(e,
|
|
709
|
-
if (s && s.dialect !== n) return
|
|
710
|
-
|
|
774
|
+
if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
|
|
775
|
+
let s = await i(r(e, N), "utf8").then(P, () => null);
|
|
776
|
+
if (s && s.dialect !== n) return I.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
|
|
777
|
+
I.info("restore drill: restoring into throwaway DB", {
|
|
711
778
|
dialect: n,
|
|
712
|
-
target:
|
|
779
|
+
target: F(o.url)
|
|
713
780
|
}), await f.runPromise(O(D(n, { url: o.url }, a)));
|
|
714
781
|
let c = null, l = 0;
|
|
715
782
|
try {
|
|
716
|
-
let e = await
|
|
783
|
+
let e = await V(n, { url: o.url });
|
|
717
784
|
c = g(e), l = e.tables.length;
|
|
718
785
|
} catch (e) {
|
|
719
|
-
return
|
|
786
|
+
return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
720
787
|
}
|
|
721
|
-
let u =
|
|
788
|
+
let u = De({
|
|
722
789
|
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
723
790
|
restoredFingerprint: c,
|
|
724
791
|
restoredTableCount: l
|
|
725
792
|
});
|
|
726
|
-
for (let e of u.lines) u.pass ?
|
|
793
|
+
for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
|
|
727
794
|
return +!u.pass;
|
|
728
|
-
},
|
|
729
|
-
let t =
|
|
730
|
-
if (!t) return
|
|
731
|
-
if (e.includes("--drill")) return
|
|
732
|
-
if (!await
|
|
733
|
-
let n =
|
|
734
|
-
stamp: await i(r(t,
|
|
795
|
+
}, $e = async (e) => {
|
|
796
|
+
let t = z(e);
|
|
797
|
+
if (!t) return I.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
|
|
798
|
+
if (e.includes("--drill")) return Qe(t, e);
|
|
799
|
+
if (!await je(e)) return 1;
|
|
800
|
+
let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Te({
|
|
801
|
+
stamp: await i(r(t, N), "utf8").then(P, () => null),
|
|
735
802
|
currentDialect: n,
|
|
736
|
-
currentFingerprint: await
|
|
803
|
+
currentFingerprint: await ke(n)
|
|
737
804
|
});
|
|
738
|
-
for (let e of o.info)
|
|
739
|
-
if (o.warn &&
|
|
740
|
-
if (!e.includes("--force")) return
|
|
741
|
-
|
|
805
|
+
for (let e of o.info) I.info(`backup stamp: ${e}`);
|
|
806
|
+
if (o.warn && I.warn(o.warn), o.refuse) {
|
|
807
|
+
if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
|
|
808
|
+
I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
|
|
742
809
|
}
|
|
743
|
-
return await f.runPromise(O(D(n,
|
|
810
|
+
return await f.runPromise(O(D(n, Xe(), a))), I.info("native restore complete", {
|
|
744
811
|
dialect: n,
|
|
745
812
|
artifact: a
|
|
746
813
|
}), 0;
|
|
747
|
-
},
|
|
748
|
-
let t =
|
|
749
|
-
if (!t) return
|
|
814
|
+
}, et = async (e) => {
|
|
815
|
+
let t = z(e);
|
|
816
|
+
if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
|
|
750
817
|
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
|
|
751
818
|
if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
|
|
752
819
|
let n = r(a, "manifest.json"), o = r(a, "README.md");
|
|
@@ -755,25 +822,25 @@ var A = class extends Error {
|
|
|
755
822
|
if (e !== void 0) return console.log(e), 0;
|
|
756
823
|
}
|
|
757
824
|
let s = await i.readFile(n, "utf8").catch(() => void 0);
|
|
758
|
-
return s === void 0 ? (
|
|
825
|
+
return s === void 0 ? (I.error(`'${t}' is a directory but holds no manifest.json — is it an unpacked bundle? (a bundle exported with --target api is always unpacked into a directory, even when the path ends in .vbundle)`), 1) : (console.log(s), 0);
|
|
759
826
|
}
|
|
760
|
-
let o =
|
|
761
|
-
let e =
|
|
827
|
+
let o = K(e), s = () => {
|
|
828
|
+
let e = q(a);
|
|
762
829
|
return o ? w(e, o) : e;
|
|
763
830
|
};
|
|
764
831
|
if (e.includes("--json")) {
|
|
765
832
|
let e = (await E(s(), ["manifest.json"])).get("manifest.json");
|
|
766
|
-
return e ? (console.log(e.toString("utf8")), 0) : (
|
|
833
|
+
return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
|
|
767
834
|
}
|
|
768
835
|
let c = (await E(s(), ["README.md"])).get("README.md");
|
|
769
836
|
if (c) return console.log(c.toString("utf8")), 0;
|
|
770
837
|
let l = (await E(s(), ["manifest.json"])).get("manifest.json");
|
|
771
|
-
return l ? (console.log(l.toString("utf8")), 0) : (
|
|
772
|
-
},
|
|
773
|
-
let t =
|
|
774
|
-
if (!t || !n) return
|
|
775
|
-
let { resolve: r } = await import("node:path"), i =
|
|
776
|
-
return a && (i = w(i, a)), await k(i, r(process.cwd(), n)),
|
|
838
|
+
return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
|
|
839
|
+
}, tt = async (e) => {
|
|
840
|
+
let t = z(e), n = z(e.filter((e) => e !== t));
|
|
841
|
+
if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
|
|
842
|
+
let { resolve: r } = await import("node:path"), i = q(r(process.cwd(), t)), a = K(e);
|
|
843
|
+
return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), I.info("unpack complete", {
|
|
777
844
|
file: t,
|
|
778
845
|
dir: n
|
|
779
846
|
}), 0;
|
|
@@ -839,13 +906,13 @@ var A = class extends Error {
|
|
|
839
906
|
"--drill-url",
|
|
840
907
|
"--api-url"
|
|
841
908
|
] }
|
|
842
|
-
},
|
|
909
|
+
}, nt = (e) => e.split("=")[0], rt = (e, t) => {
|
|
843
910
|
let n = $[e];
|
|
844
911
|
if (!n) return;
|
|
845
|
-
let r =
|
|
912
|
+
let r = R(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
|
|
846
913
|
for (let n of t) {
|
|
847
914
|
if (!n.startsWith("--")) continue;
|
|
848
|
-
let t =
|
|
915
|
+
let t = nt(n);
|
|
849
916
|
if (i.has(t)) continue;
|
|
850
917
|
if (a.has(t)) return `voltro data ${e}: ${t} is not read with --target ${r} (it is a --target ${r === "api" ? "direct" : "api"} flag). Remove it, or change --target.`;
|
|
851
918
|
let o = Object.entries($).filter(([n, r]) => n !== e && [
|
|
@@ -855,13 +922,13 @@ var A = class extends Error {
|
|
|
855
922
|
].includes(t)).map(([e]) => e);
|
|
856
923
|
return o.length > 0 ? `voltro data ${e}: ${t} is not a \`${e}\` flag (it belongs to \`voltro data ${o.join("` / `")}\`). Remove it.` : `voltro data ${e}: unknown flag ${t}. Run \`voltro data --help\` for the flags this command reads.`;
|
|
857
924
|
}
|
|
858
|
-
},
|
|
925
|
+
}, it = async (e) => {
|
|
859
926
|
let [t, ...n] = e;
|
|
860
927
|
if (t !== void 0 && $[t] !== void 0) {
|
|
861
|
-
let e =
|
|
862
|
-
if (e) return
|
|
928
|
+
let e = rt(t, n);
|
|
929
|
+
if (e) return I.error(e), 2;
|
|
863
930
|
}
|
|
864
|
-
return t === "export" ?
|
|
931
|
+
return t === "export" ? He(n) : t === "import" ? Ye(n) : t === "unpack" ? tt(n) : t === "inspect" ? et(n) : t === "backup" ? Ze(n) : t === "restore" ? $e(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
|
|
865
932
|
};
|
|
866
933
|
//#endregion
|
|
867
|
-
export {
|
|
934
|
+
export { it as runDataCommand };
|