@voltro/cli 0.45.0 → 0.46.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 +176 -0
- package/dist/{apiBuild-CDQBNRDx.js → apiBuild-BKdd0I1Z.js} +2 -2
- package/dist/apiBuild-P2gewrfQ.js +2 -0
- package/dist/bin.d.ts +8 -0
- package/dist/bin.js +1 -1
- package/dist/{build-yvsLG77U.js → build-co6yadYz.js} +7 -7
- package/dist/{checkCommand-Boxov8lr.js → checkCommand-B-eiW-G_.js} +3 -3
- package/dist/{checkCommand-lrzwOzV1.js → checkCommand-CXAXTjZL.js} +1 -1
- package/dist/{codegenCommand-CQOLytL-.js → codegenCommand-CB6_3sX0.js} +15 -15
- package/dist/{codemodRunner-5ZhDH3rB.js → codemodRunner-DSZjgyCL.js} +8 -0
- package/dist/{commands-CEkVoFM5.js → commands-C-hxgAGr.js} +23 -23
- package/dist/{dashboardCommand-0ahyp-zA.js → dashboardCommand-CEVO0GWF.js} +3 -3
- package/dist/{dataCommand-D4IlZcih.js → dataCommand-k0ezbQWa.js} +271 -230
- package/dist/{dbCommand-B237qI-j.js → dbCommand-BwkyNxn-.js} +110 -110
- package/dist/dbCommand-C2XK63Dd.js +2 -0
- package/dist/{dev-DAM5CnAa.js → dev-8hTEnN6s.js} +2080 -2057
- package/dist/{dev-P4HyeY9W.js → dev-DyoYst4j.js} +1 -1
- package/dist/devActivity.d.ts +8 -0
- package/dist/{doctorCommand-DW2cwAvC.js → doctorCommand-B6D7HH1-.js} +14 -14
- package/dist/doctorCommand-Btl2Jz1a.js +2 -0
- package/dist/{dormancyCommand-BqQz7lbM.js → dormancyCommand-CrPAwOgo.js} +1 -1
- package/dist/{embeddingsCommand-DJyArpU4.js → embeddingsCommand-DsSxsn_b.js} +1 -1
- package/dist/{envCommand-CviRZx1L.js → envCommand-B_NPVGUf.js} +2 -2
- package/dist/{evalCommand-6RUfPen4.js → evalCommand-Db01Kbng.js} +24 -24
- package/dist/{evolveCommand-qdlP-R9G.js → evolveCommand-DUuaCIyn.js} +2 -2
- package/dist/{frameworkTableAssembly-oMPBKqlE.js → frameworkTableAssembly-BPlPfU07.js} +82 -78
- package/dist/frameworkTableAssembly-Bob3vj9R.js +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -1
- package/dist/{infoCommand-CNWPSUcW.js → infoCommand-CuNl9cbh.js} +1 -1
- package/dist/inspectState.d.ts +8 -0
- package/dist/interruptedReplace-C1ZI84xi.js +27 -0
- package/dist/interruptedReplace-DNJ7MKph.js +2 -0
- package/dist/{migrate-DN47p2PG.js → migrate-CM1qwalu.js} +27 -27
- package/dist/{runtimeTrace-BDZSt9W_.js → runtimeTrace-BRNM06vK.js} +1 -1
- package/dist/{sdkgen-sNaD3oZB.js → sdkgen-CRCw_XcR.js} +1 -1
- package/dist/{seedRunner-IdHEprqf.js → seedRunner-Dgsiwk_e.js} +59 -59
- package/dist/serveCommand-BNHMVXyp.js +2 -0
- package/dist/serveCommand-CHOr74WM.js +1928 -0
- package/dist/serveEntry.d.ts +8 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-Ds3xyn8R.js → start-7HcSIKEp.js} +1 -1
- package/dist/{start-BAlGcjF5.js → start-DvlYDyAr.js} +1 -1
- package/dist/startEntry.d.ts +8 -0
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +8 -0
- package/dist/{updateCommand-rCnhaj84.js → updateCommand-BlyXavoG.js} +1 -1
- package/dist/updateCommand-CT5AvVg7.js +2 -0
- package/dist/{webDev-101csg6Q.js → webDev-B4IEMB0n.js} +6 -6
- package/dist/webDev-B79ji8M5.js +2 -0
- package/dist/{webhooksCommand-C0iZZfaI.js → webhooksCommand-BRrG_sGf.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 +138 -2
- package/templates/agent-docs/data.md +70 -4
- package/templates/agent-docs/whats-new.md +127 -54
- 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-yJCqJYQ8.js +0 -2
- package/dist/dbCommand-BvQeKQOb.js +0 -2
- package/dist/doctorCommand-2K1dIzYk.js +0 -2
- package/dist/frameworkTableAssembly-DgylYh9_.js +0 -2
- package/dist/serveCommand-BBhT2U94.js +0 -1885
- package/dist/serveCommand-DY2jLy0o.js +0 -2
- package/dist/updateCommand-BH6EGuCc.js +0 -2
- package/dist/webDev-BLKBO34K.js +0 -2
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { t as e } from "./cliError-BmdYnghb.js";
|
|
2
|
-
import { c as t, i as n, o as r } from "./dev-
|
|
3
|
-
import { n as
|
|
4
|
-
import { r as
|
|
5
|
-
import { t as
|
|
6
|
-
import { o as
|
|
7
|
-
import { r as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { request as
|
|
2
|
+
import { c as t, i as n, o as r, z as i } from "./dev-8hTEnN6s.js";
|
|
3
|
+
import { n as a, t as o } from "./cliArgs-D4p8n7EE.js";
|
|
4
|
+
import { r as s } from "./dialectDriver-czCHYpeH.js";
|
|
5
|
+
import { t as c } from "./dataProfile-Cm0YVKSy.js";
|
|
6
|
+
import { o as l, t as u } from "./inspectFetch-EMuhTG_9.js";
|
|
7
|
+
import { r as d } from "./dbCommand-BwkyNxn-.js";
|
|
8
|
+
import { resolve as f } from "node:path";
|
|
9
|
+
import { createLogger as p } from "@voltro/logger";
|
|
10
|
+
import { connectionIdentity as m, normaliseTarget as h, sameDatabase as g, streamTable as _ } from "@voltro/database";
|
|
11
|
+
import { Effect as v, Stream as y } from "effect";
|
|
12
|
+
import { SqlClient as b } from "@effect/sql";
|
|
13
|
+
import { declaredSnapshot as x, fingerprintSchema as ee, introspectSchema as te } from "@voltro/database/sql";
|
|
14
|
+
import { STORAGE_REFS_TABLE as ne, resolveStorageProvider as S } from "@voltro/plugin-storage";
|
|
15
|
+
import { createHash as re } from "node:crypto";
|
|
16
|
+
import { asImportError as ie, assessLiveImport as ae, backupArtifactName as C, backupCommand as oe, classificationFromSnapshot as se, decryptStream as w, encryptStream as ce, packBundle as le, peekBundle as T, previewMasking as ue, restoreCommand as de, runExport as E, runImport as D, runNativeStep as O, storageAssetSink as fe, storageAssetSource as pe, storageBlobSink as me, storageBlobSource as he, unpackBundle as ge, unpackBundleToDir as k } from "@voltro/data-transfer";
|
|
17
|
+
import { request as _e } from "node:http";
|
|
18
|
+
import { request as ve } from "node:https";
|
|
18
19
|
//#region src/longRequest.ts
|
|
19
|
-
var
|
|
20
|
+
var ye = /* @__PURE__ */ new Set([
|
|
20
21
|
"ECONNREFUSED",
|
|
21
22
|
"ETIMEDOUT",
|
|
22
23
|
"EHOSTUNREACH",
|
|
@@ -44,7 +45,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
44
45
|
super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
|
|
45
46
|
}
|
|
46
47
|
}, M = async (e, t, n, r) => {
|
|
47
|
-
let i = new URL(e), a = i.protocol === "https:" ?
|
|
48
|
+
let i = new URL(e), a = i.protocol === "https:" ? ve : _e, o = Date.now();
|
|
48
49
|
return await new Promise((s, c) => {
|
|
49
50
|
let l = a({
|
|
50
51
|
protocol: i.protocol,
|
|
@@ -63,7 +64,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
63
64
|
});
|
|
64
65
|
if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
|
|
65
66
|
l.destroy(new j(Date.now() - o, e));
|
|
66
|
-
}), l.on("error", (t) => c(
|
|
67
|
+
}), l.on("error", (t) => c(ye.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
|
|
67
68
|
l.end(t);
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
@@ -76,7 +77,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
76
77
|
}
|
|
77
78
|
})();
|
|
78
79
|
});
|
|
79
|
-
},
|
|
80
|
+
}, be = (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`, xe = (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.", Se = 16777216, Ce = async function* (e, t) {
|
|
80
81
|
let n = [], r = 0, i = 0, a = function* (e) {
|
|
81
82
|
let t = Buffer.concat(n);
|
|
82
83
|
n = [], r = 0;
|
|
@@ -98,12 +99,12 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
98
99
|
i += a.byteLength, yield s;
|
|
99
100
|
}
|
|
100
101
|
yield* a(!0);
|
|
101
|
-
},
|
|
102
|
+
}, we = (e) => re("sha256").update(Buffer.from(e)).digest("hex"), Te = async (e) => {
|
|
102
103
|
let t = 0, n = 0, r = {
|
|
103
104
|
status: 0,
|
|
104
105
|
body: {}
|
|
105
106
|
};
|
|
106
|
-
for await (let i of
|
|
107
|
+
for await (let i of Ce(e.stream, e.chunkBytes)) {
|
|
107
108
|
if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
|
|
108
109
|
n++;
|
|
109
110
|
continue;
|
|
@@ -129,7 +130,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
129
130
|
bytes: t,
|
|
130
131
|
resumedFrom: e.alreadyHeld
|
|
131
132
|
};
|
|
132
|
-
}, N = "voltro-backup-stamp.json",
|
|
133
|
+
}, N = "voltro-backup-stamp.json", Ee = (e) => ({
|
|
133
134
|
stampVersion: 1,
|
|
134
135
|
dialect: e.dialect,
|
|
135
136
|
schemaFingerprint: e.schemaFingerprint,
|
|
@@ -153,7 +154,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
153
154
|
voltroVersion: n.voltroVersion,
|
|
154
155
|
createdAt: n.createdAt
|
|
155
156
|
};
|
|
156
|
-
},
|
|
157
|
+
}, De = (e) => {
|
|
157
158
|
let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
|
|
158
159
|
if (t === null) return {
|
|
159
160
|
warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
@@ -174,7 +175,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
174
175
|
} catch {
|
|
175
176
|
return e.trim();
|
|
176
177
|
}
|
|
177
|
-
},
|
|
178
|
+
}, Oe = (e) => {
|
|
178
179
|
let t = e.flagUrl ?? e.envUrl;
|
|
179
180
|
if (t === void 0 || t.trim() === "") return {
|
|
180
181
|
ok: !1,
|
|
@@ -188,7 +189,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
188
189
|
ok: !0,
|
|
189
190
|
url: t
|
|
190
191
|
};
|
|
191
|
-
},
|
|
192
|
+
}, ke = (e) => {
|
|
192
193
|
let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
|
|
193
194
|
return r === 0 ? {
|
|
194
195
|
pass: !1,
|
|
@@ -203,27 +204,27 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
203
204
|
pass: !0,
|
|
204
205
|
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
205
206
|
};
|
|
206
|
-
}, I =
|
|
207
|
+
}, I = p({ scope: "voltro:data" }), Ae = async () => {
|
|
207
208
|
try {
|
|
208
209
|
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");
|
|
209
210
|
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
210
211
|
} catch {
|
|
211
212
|
return "unknown";
|
|
212
213
|
}
|
|
213
|
-
},
|
|
214
|
+
}, L = async (e) => {
|
|
214
215
|
try {
|
|
215
|
-
return
|
|
216
|
+
return ee(await U(e));
|
|
216
217
|
} catch {
|
|
217
218
|
return null;
|
|
218
219
|
}
|
|
219
|
-
},
|
|
220
|
+
}, R = (e) => {
|
|
220
221
|
if (e.event !== "done") return;
|
|
221
222
|
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
222
223
|
I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
223
|
-
},
|
|
224
|
+
}, je = async (e) => {
|
|
224
225
|
let t = (e) => e?.meta?.databaseId;
|
|
225
226
|
if (e) {
|
|
226
|
-
let n = await
|
|
227
|
+
let n = await u({
|
|
227
228
|
name: "target",
|
|
228
229
|
url: e
|
|
229
230
|
}, "/_voltro/inspect/app");
|
|
@@ -232,29 +233,29 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
232
233
|
databaseId: t(n.data)
|
|
233
234
|
};
|
|
234
235
|
}
|
|
235
|
-
for (let e of await
|
|
236
|
-
let n = await
|
|
236
|
+
for (let e of await l()) {
|
|
237
|
+
let n = await u(e, "/_voltro/inspect/app");
|
|
237
238
|
if (n.kind === "ok") return {
|
|
238
239
|
label: `${e.name} (${e.url})`,
|
|
239
240
|
databaseId: t(n.data)
|
|
240
241
|
};
|
|
241
242
|
}
|
|
242
243
|
return null;
|
|
243
|
-
},
|
|
244
|
-
let t = e.includes("--allow-live"), n = t ? null : await
|
|
245
|
-
dialect:
|
|
244
|
+
}, Me = async (e) => {
|
|
245
|
+
let t = e.includes("--allow-live"), n = t ? null : await je(z(e, "--api-url")), i = m({
|
|
246
|
+
dialect: V(),
|
|
246
247
|
...r()
|
|
247
|
-
}), a =
|
|
248
|
-
dialect:
|
|
248
|
+
}), a = h({
|
|
249
|
+
dialect: V(),
|
|
249
250
|
...r()
|
|
250
|
-
}), { refuse: o, message: s } =
|
|
251
|
+
}), { refuse: o, message: s } = ae({
|
|
251
252
|
liveInstance: n?.label ?? null,
|
|
252
253
|
allowLive: t,
|
|
253
|
-
sameTarget:
|
|
254
|
+
sameTarget: g(i, n?.databaseId),
|
|
254
255
|
...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
|
|
255
256
|
});
|
|
256
257
|
return !o || (I.error(s ?? "refusing: live instance detected"), !1);
|
|
257
|
-
},
|
|
258
|
+
}, z = a, Ne = [
|
|
258
259
|
"--api-url",
|
|
259
260
|
"--bundle-key",
|
|
260
261
|
"--compression",
|
|
@@ -272,44 +273,44 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
272
273
|
"--timeout",
|
|
273
274
|
"--tenant",
|
|
274
275
|
"--token"
|
|
275
|
-
],
|
|
276
|
+
], B = (e) => o(e, Ne), V = () => {
|
|
276
277
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
277
278
|
if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
278
279
|
return t;
|
|
279
|
-
},
|
|
280
|
-
let n = (await
|
|
281
|
-
return
|
|
282
|
-
},
|
|
280
|
+
}, H = async (e, t) => {
|
|
281
|
+
let n = (await s(e)).makeSqlLayer(t);
|
|
282
|
+
return v.runPromise(b.SqlClient.pipe(v.flatMap(te), v.provide(n)));
|
|
283
|
+
}, U = (e) => H(e, r()), Pe = () => {
|
|
283
284
|
let e = r(), t = [];
|
|
284
285
|
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
|
-
},
|
|
286
|
+
}, W = async () => {
|
|
286
287
|
let r = process.cwd(), i = await t(r);
|
|
287
288
|
if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
|
|
288
|
-
let a = await n(i, void 0), o =
|
|
289
|
+
let a = await n(i, void 0), o = V();
|
|
289
290
|
return {
|
|
290
291
|
store: a,
|
|
291
|
-
snapshot: await
|
|
292
|
+
snapshot: await U(o),
|
|
292
293
|
dialect: o
|
|
293
294
|
};
|
|
294
|
-
},
|
|
295
|
-
table:
|
|
295
|
+
}, G = (e) => _(e, {
|
|
296
|
+
table: ne,
|
|
296
297
|
chunkSize: 500
|
|
297
|
-
}).pipe(
|
|
298
|
+
}).pipe(y.map((e) => ({
|
|
298
299
|
key: String(e.key),
|
|
299
300
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
300
301
|
size: Number(e.size ?? 0)
|
|
301
|
-
}))),
|
|
302
|
+
}))), K = (e) => !!e && e.endsWith(".vbundle"), q = (e) => z(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Fe = (e) => e.includes("--encrypt") || z(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ie = async (e, t) => {
|
|
302
303
|
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
303
304
|
await r(i.from(e), n(t));
|
|
304
|
-
},
|
|
305
|
+
}, J = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
305
306
|
let { createReadStream: t } = await import("node:fs");
|
|
306
307
|
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
307
|
-
} }),
|
|
308
|
-
let r =
|
|
308
|
+
} }), Y = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Le = (t, n) => {
|
|
309
|
+
let r = z(t, "--tenant"), i = z(t, "--tables"), a = z(t, "--exclude");
|
|
309
310
|
if (a !== void 0) {
|
|
310
311
|
if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
|
|
311
312
|
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.");
|
|
312
|
-
let t =
|
|
313
|
+
let t = Y(a), o = new Set(n), s = t.filter((e) => !o.has(e));
|
|
313
314
|
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.`);
|
|
314
315
|
let c = new Set(t);
|
|
315
316
|
return {
|
|
@@ -322,15 +323,15 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
322
323
|
tenantId: r
|
|
323
324
|
} : i ? {
|
|
324
325
|
kind: "tables",
|
|
325
|
-
tables:
|
|
326
|
+
tables: Y(i)
|
|
326
327
|
} : { kind: "all" };
|
|
327
|
-
},
|
|
328
|
-
let e = await
|
|
329
|
-
return
|
|
330
|
-
},
|
|
331
|
-
let e = (await
|
|
328
|
+
}, Re = async () => {
|
|
329
|
+
let e = await d(process.cwd());
|
|
330
|
+
return se(x(e));
|
|
331
|
+
}, ze = "voltro/tenant", Be = async () => {
|
|
332
|
+
let e = (await d(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === ze)).map((e) => e.tableName);
|
|
332
333
|
return e.length > 0 ? e : void 0;
|
|
333
|
-
},
|
|
334
|
+
}, Ve = (e) => {
|
|
334
335
|
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
335
336
|
console.log(`\n ${t.table}`);
|
|
336
337
|
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
@@ -343,8 +344,8 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
343
344
|
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
344
345
|
for (let t of e.unclassified) console.log(` ${t}`);
|
|
345
346
|
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
346
|
-
},
|
|
347
|
-
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a =
|
|
347
|
+
}, He = async (t) => {
|
|
348
|
+
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = z(t.args, "--chunk-size"), o = a === void 0 ? Se : Math.round(Number(a) * 1024 * 1024);
|
|
348
349
|
if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
|
|
349
350
|
let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
|
|
350
351
|
for (; l <= o;) {
|
|
@@ -359,7 +360,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
359
360
|
kind: "small",
|
|
360
361
|
body: Buffer.concat(c)
|
|
361
362
|
};
|
|
362
|
-
let d = n(process.cwd(), t.bundleDir), f =
|
|
363
|
+
let d = n(process.cwd(), t.bundleDir), f = we(K(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), {
|
|
363
364
|
...t.headers,
|
|
364
365
|
"x-import-upload-id": p,
|
|
365
366
|
"x-import-probe": "1"
|
|
@@ -372,7 +373,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
372
373
|
if (e.done === !0) return;
|
|
373
374
|
yield e.value;
|
|
374
375
|
}
|
|
375
|
-
})(), _ = 0, v = await
|
|
376
|
+
})(), _ = 0, v = await Te({
|
|
376
377
|
stream: g,
|
|
377
378
|
chunkBytes: o,
|
|
378
379
|
uploadId: p,
|
|
@@ -408,27 +409,38 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
408
409
|
body: Buffer.from(JSON.stringify(v.body))
|
|
409
410
|
}
|
|
410
411
|
};
|
|
411
|
-
},
|
|
412
|
-
|
|
412
|
+
}, Ue = (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", We = (e, t, n) => {
|
|
413
|
+
if (t !== "replace" || e.includes("--no-rollback")) return;
|
|
414
|
+
let r = z(e, "--rollback-dir");
|
|
415
|
+
return r === void 0 ? i(n) : f(r);
|
|
416
|
+
}, Ge = (e, t, n) => {
|
|
417
|
+
let r = We(e, t, n);
|
|
418
|
+
return {
|
|
419
|
+
...r === void 0 ? {} : { rollbackDir: r },
|
|
420
|
+
recordInterruption: !0,
|
|
421
|
+
via: "direct"
|
|
422
|
+
};
|
|
423
|
+
}, Z = (t) => {
|
|
424
|
+
let n = z(t, "--timeout");
|
|
413
425
|
if (n === void 0) return {};
|
|
414
426
|
let r = Number(n);
|
|
415
427
|
if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
|
|
416
428
|
return { timeoutMs: Math.round(r * 1e3) };
|
|
417
|
-
},
|
|
418
|
-
let t =
|
|
429
|
+
}, Ke = async (e) => {
|
|
430
|
+
let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), r = B(e);
|
|
419
431
|
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;
|
|
420
432
|
if (t) {
|
|
421
|
-
let t =
|
|
433
|
+
let t = z(e, "--api-url");
|
|
422
434
|
if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
423
|
-
let i =
|
|
435
|
+
let i = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
424
436
|
if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
425
|
-
if (
|
|
426
|
-
let a =
|
|
427
|
-
...
|
|
428
|
-
...
|
|
437
|
+
if (z(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;
|
|
438
|
+
let a = z(e, "--tables"), o = {
|
|
439
|
+
...z(e, "--profile") ? { profile: z(e, "--profile") } : {},
|
|
440
|
+
...z(e, "--tenant") ? { tenant: z(e, "--tenant") } : {},
|
|
429
441
|
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
430
|
-
...
|
|
431
|
-
...
|
|
442
|
+
...z(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
443
|
+
...z(e, "--compression") ? { compression: z(e, "--compression") } : {},
|
|
432
444
|
...e.includes("--assets") ? { assets: !0 } : {},
|
|
433
445
|
...n ? { bundleKey: n } : {}
|
|
434
446
|
}, s;
|
|
@@ -439,7 +451,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
439
451
|
}, Z(e));
|
|
440
452
|
} catch (e) {
|
|
441
453
|
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: ${
|
|
454
|
+
if (e instanceof A) return I.error(`api export: ${be(e)}`), 1;
|
|
443
455
|
throw e;
|
|
444
456
|
}
|
|
445
457
|
if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
|
|
@@ -458,57 +470,57 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
458
470
|
}), 0;
|
|
459
471
|
}
|
|
460
472
|
if (!r) return I.error("a <outDir> is required for a direct export"), 1;
|
|
461
|
-
let i =
|
|
473
|
+
let i = z(e, "--profile"), a = i ? await c(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: l } = await W(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, f = z(e, "--compression") ?? a.compression, p = z(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", m = a.masking, h = a.subset, g = a.scope ?? Le(e, s.tables.map((e) => e.name)), _ = m ? await Re() : void 0, y = g.kind === "tenant" ? await Be() : void 0, b = y ? { tenantTables: y } : {};
|
|
462
474
|
if (e.includes("--dry-run")) {
|
|
463
|
-
let e =
|
|
475
|
+
let e = g.kind === "tables" && g.tables ? [...g.tables] : s.tables.map((e) => e.name);
|
|
464
476
|
if (I.info("export DRY RUN — nothing was written", {
|
|
465
477
|
outDir: r,
|
|
466
478
|
tables: e.length,
|
|
467
|
-
scope:
|
|
468
|
-
consistency:
|
|
469
|
-
compression:
|
|
479
|
+
scope: g.kind,
|
|
480
|
+
consistency: p,
|
|
481
|
+
compression: f ?? "zstd",
|
|
470
482
|
assets: u,
|
|
471
|
-
masking:
|
|
472
|
-
}), I.info(`would export: ${e.join(", ")}`), !
|
|
473
|
-
let t = await
|
|
483
|
+
masking: m ? "profile policy applies" : "none"
|
|
484
|
+
}), I.info(`would export: ${e.join(", ")}`), !m) return 0;
|
|
485
|
+
let t = await v.runPromise(ue({
|
|
474
486
|
store: o,
|
|
475
|
-
snapshot:
|
|
476
|
-
masking:
|
|
477
|
-
...
|
|
478
|
-
...
|
|
479
|
-
scope:
|
|
487
|
+
snapshot: s,
|
|
488
|
+
masking: m,
|
|
489
|
+
..._ ? { classification: _ } : {},
|
|
490
|
+
...h ? {} : {
|
|
491
|
+
scope: g,
|
|
480
492
|
...b
|
|
481
493
|
}
|
|
482
494
|
}));
|
|
483
|
-
return
|
|
495
|
+
return Ve(t), +(t.unclassified.length > 0);
|
|
484
496
|
}
|
|
485
|
-
if (
|
|
486
|
-
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"),
|
|
497
|
+
if (K(r)) {
|
|
498
|
+
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), c = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
|
|
487
499
|
try {
|
|
488
|
-
let t = await
|
|
500
|
+
let t = await v.runPromise(E({
|
|
489
501
|
store: o,
|
|
490
|
-
snapshot:
|
|
502
|
+
snapshot: s,
|
|
491
503
|
dialect: l,
|
|
492
|
-
outDir:
|
|
493
|
-
...
|
|
494
|
-
scope:
|
|
504
|
+
outDir: c,
|
|
505
|
+
...h ? { subset: h } : {
|
|
506
|
+
scope: g,
|
|
495
507
|
...b
|
|
496
508
|
},
|
|
497
|
-
consistency:
|
|
498
|
-
...
|
|
499
|
-
...
|
|
500
|
-
...
|
|
501
|
-
onProgress:
|
|
502
|
-
})), n =
|
|
503
|
-
dir:
|
|
504
|
-
...d ? { blobs:
|
|
505
|
-
}), a =
|
|
509
|
+
consistency: p,
|
|
510
|
+
...f ? { compression: f } : {},
|
|
511
|
+
...m ? { masking: m } : {},
|
|
512
|
+
..._ ? { classification: _ } : {},
|
|
513
|
+
onProgress: R
|
|
514
|
+
})), n = le({
|
|
515
|
+
dir: c,
|
|
516
|
+
...d ? { blobs: he(d, () => G(o)) } : {}
|
|
517
|
+
}), a = Fe(e);
|
|
506
518
|
if (a) {
|
|
507
|
-
let t =
|
|
519
|
+
let t = q(e);
|
|
508
520
|
if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
509
|
-
n =
|
|
521
|
+
n = ce(n, t);
|
|
510
522
|
}
|
|
511
|
-
return await
|
|
523
|
+
return await Ie(n, i(process.cwd(), r)), I.info("export complete (single file)", {
|
|
512
524
|
file: r,
|
|
513
525
|
encrypted: a,
|
|
514
526
|
assets: u,
|
|
@@ -516,27 +528,27 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
516
528
|
rows: t.tables.reduce((e, t) => e + t.rowCount, 0)
|
|
517
529
|
}), 0;
|
|
518
530
|
} finally {
|
|
519
|
-
await n.rm(
|
|
531
|
+
await n.rm(c, {
|
|
520
532
|
recursive: !0,
|
|
521
533
|
force: !0
|
|
522
534
|
});
|
|
523
535
|
}
|
|
524
536
|
}
|
|
525
|
-
let x = await
|
|
537
|
+
let x = await v.runPromise(E({
|
|
526
538
|
store: o,
|
|
527
|
-
snapshot:
|
|
539
|
+
snapshot: s,
|
|
528
540
|
dialect: l,
|
|
529
541
|
outDir: r,
|
|
530
|
-
...
|
|
531
|
-
scope:
|
|
542
|
+
...h ? { subset: h } : {
|
|
543
|
+
scope: g,
|
|
532
544
|
...b
|
|
533
545
|
},
|
|
534
|
-
consistency:
|
|
535
|
-
...
|
|
536
|
-
...
|
|
537
|
-
...
|
|
538
|
-
...d ? { assets:
|
|
539
|
-
onProgress:
|
|
546
|
+
consistency: p,
|
|
547
|
+
...f ? { compression: f } : {},
|
|
548
|
+
...m ? { masking: m } : {},
|
|
549
|
+
..._ ? { classification: _ } : {},
|
|
550
|
+
...d ? { assets: pe(d, () => G(o)) } : {},
|
|
551
|
+
onProgress: R
|
|
540
552
|
}));
|
|
541
553
|
return I.info("export complete", {
|
|
542
554
|
outDir: r,
|
|
@@ -546,13 +558,13 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
546
558
|
compression: x.compression,
|
|
547
559
|
...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
|
|
548
560
|
}), 0;
|
|
549
|
-
},
|
|
550
|
-
let t =
|
|
561
|
+
}, qe = () => "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.", Je = (e) => {
|
|
562
|
+
let t = z(e, "--tables");
|
|
551
563
|
return {
|
|
552
564
|
...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
553
565
|
...e.includes("--dry-run") ? { dryRun: !0 } : {}
|
|
554
566
|
};
|
|
555
|
-
},
|
|
567
|
+
}, Q = (e) => ({
|
|
556
568
|
tables: e.tablesWritten,
|
|
557
569
|
rows: e.rowsWritten,
|
|
558
570
|
carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -560,9 +572,9 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
560
572
|
skippedTables: e.tablesSkipped,
|
|
561
573
|
skippedRows: e.rowsSkipped
|
|
562
574
|
} : {}
|
|
563
|
-
}),
|
|
575
|
+
}), Ye = (e, t) => {
|
|
564
576
|
if (e.includes("--dry-run")) {
|
|
565
|
-
I.info("import DRY RUN — nothing was written", t), I.info(
|
|
577
|
+
I.info("import DRY RUN — nothing was written", t), I.info(qe());
|
|
566
578
|
return;
|
|
567
579
|
}
|
|
568
580
|
if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
|
|
@@ -570,8 +582,8 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
570
582
|
return;
|
|
571
583
|
}
|
|
572
584
|
I.info("import complete", t);
|
|
573
|
-
},
|
|
574
|
-
let t =
|
|
585
|
+
}, Xe = Symbol("refused"), Ze = (e) => {
|
|
586
|
+
let t = ie(e) ?? e;
|
|
575
587
|
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
|
|
576
588
|
let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
|
|
577
589
|
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.`);
|
|
@@ -581,7 +593,7 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
581
593
|
n.length < i && I.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
|
|
582
594
|
let s = i - a;
|
|
583
595
|
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
|
-
},
|
|
596
|
+
}, Qe = (e) => {
|
|
585
597
|
let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
|
|
586
598
|
try {
|
|
587
599
|
let e = JSON.parse(t), r = e.error ?? e.message;
|
|
@@ -621,121 +633,126 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
621
633
|
return;
|
|
622
634
|
}
|
|
623
635
|
}
|
|
624
|
-
},
|
|
625
|
-
let t =
|
|
636
|
+
}, $e = async (e) => {
|
|
637
|
+
let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
|
|
626
638
|
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 =
|
|
639
|
+
let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
|
|
628
640
|
if (t) {
|
|
629
|
-
let t =
|
|
641
|
+
let t = z(e, "--api-url");
|
|
630
642
|
if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
631
|
-
let r =
|
|
643
|
+
let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
632
644
|
if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
633
|
-
let s =
|
|
645
|
+
let s = z(e, "--tables"), c = z(e, "--rollback-key");
|
|
646
|
+
a === "replace" && c === void 0 && I.warn("api replace: no rollback capture — this run empties the target first and nothing was asked to be kept.\n Pass --rollback-key <storage-key> and the INSTANCE stores the target's current rows in its own\n object storage before deleting them, which is the one place that survives the pod going away.");
|
|
647
|
+
let l = {
|
|
634
648
|
authorization: `Bearer ${r}`,
|
|
635
649
|
"x-import-mode": a,
|
|
636
650
|
"x-import-on-conflict": o,
|
|
637
651
|
...X(e, a) ? { "x-import-atomic": "1" } : {},
|
|
638
652
|
...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
|
|
653
|
+
...c === void 0 ? {} : { "x-import-rollback-key": c },
|
|
639
654
|
...e.includes("--force") ? { "x-import-force": "1" } : {},
|
|
640
655
|
...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
|
|
641
656
|
...s ? { "x-import-tables": s } : {}
|
|
642
|
-
}, { resolve:
|
|
643
|
-
if (n)
|
|
644
|
-
else if (
|
|
645
|
-
let t =
|
|
646
|
-
n && (t = w(t, n)),
|
|
647
|
-
} else await (await import("node:fs/promises")).access(
|
|
648
|
-
a === "replace" && !X(e, a) && I.warn(
|
|
649
|
-
let
|
|
657
|
+
}, { resolve: u } = await import("node:path"), d;
|
|
658
|
+
if (n) l["content-type"] = "application/json", d = JSON.stringify({ bundleKey: n });
|
|
659
|
+
else if (K(i)) {
|
|
660
|
+
let t = J(u(process.cwd(), i)), n = q(e);
|
|
661
|
+
n && (t = w(t, n)), l["content-type"] = "application/octet-stream", d = t;
|
|
662
|
+
} else await (await import("node:fs/promises")).access(u(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."), l["content-type"] = "application/octet-stream", d = le({ dir: u(process.cwd(), i) });
|
|
663
|
+
a === "replace" && !X(e, a) && I.warn(Ue(!0));
|
|
664
|
+
let f = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, p;
|
|
650
665
|
try {
|
|
651
|
-
let t = typeof
|
|
652
|
-
stream:
|
|
653
|
-
url:
|
|
654
|
-
headers:
|
|
666
|
+
let t = typeof d == "string" ? null : await He({
|
|
667
|
+
stream: d,
|
|
668
|
+
url: f,
|
|
669
|
+
headers: l,
|
|
655
670
|
bundleDir: i,
|
|
656
671
|
args: e,
|
|
657
672
|
log: I
|
|
658
673
|
});
|
|
659
|
-
(t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"),
|
|
674
|
+
(t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), p = t === null ? await M(f, d, l, Z(e)) : t.kind === "sent" ? t.res : await M(f, t.body, l, Z(e));
|
|
660
675
|
} catch (e) {
|
|
661
|
-
if (e instanceof j) return I.error(`api import: ${
|
|
662
|
-
if (e instanceof A) return I.error(`api import: ${
|
|
676
|
+
if (e instanceof j) return I.error(`api import: ${xe(e, a)}`), 1;
|
|
677
|
+
if (e instanceof A) return I.error(`api import: ${be(e)}`), 1;
|
|
663
678
|
throw e;
|
|
664
679
|
}
|
|
665
|
-
if (
|
|
666
|
-
let
|
|
680
|
+
if (p.status < 200 || p.status >= 300) return Ze(Qe(p.body)) || I.error(`api import failed: HTTP ${p.status} — ${p.body.toString("utf8")}`), 1;
|
|
681
|
+
let m = JSON.parse(p.body.toString("utf8"));
|
|
667
682
|
return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
|
|
668
683
|
via: t,
|
|
669
|
-
...
|
|
670
|
-
}), I.info(
|
|
684
|
+
...m
|
|
685
|
+
}), I.info(qe()), 0) : (I.info("api import complete (imported by the running instance)", {
|
|
671
686
|
via: t,
|
|
672
|
-
...
|
|
687
|
+
...m
|
|
673
688
|
}), 0);
|
|
674
689
|
}
|
|
675
690
|
if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
|
|
676
|
-
if (!await
|
|
677
|
-
a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(
|
|
678
|
-
let { store: s, snapshot: c } = await
|
|
679
|
-
if (
|
|
680
|
-
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join:
|
|
691
|
+
if (!await Me(e)) return 1;
|
|
692
|
+
a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(Ue(!1));
|
|
693
|
+
let { store: s, snapshot: c } = await W(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, f = V() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
|
|
694
|
+
if (K(i)) {
|
|
695
|
+
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join: p } = await import("node:path"), m = await n.mkdtemp(p(t.tmpdir(), "voltro-import-"));
|
|
681
696
|
try {
|
|
682
|
-
let t =
|
|
683
|
-
return n && (t = w(t, n)), await
|
|
684
|
-
dir:
|
|
685
|
-
blobs:
|
|
686
|
-
}),
|
|
697
|
+
let t = J(r(process.cwd(), i)), n = q(e);
|
|
698
|
+
return n && (t = w(t, n)), await ge(t, {
|
|
699
|
+
dir: m,
|
|
700
|
+
blobs: me(d ?? S({}))
|
|
701
|
+
}), Ye(e, {
|
|
687
702
|
file: i,
|
|
688
|
-
...
|
|
703
|
+
...Q(await v.runPromise(D({
|
|
689
704
|
store: s,
|
|
690
|
-
bundleDir:
|
|
705
|
+
bundleDir: m,
|
|
691
706
|
mode: a,
|
|
692
707
|
onConflict: o,
|
|
693
708
|
atomic: X(e, a),
|
|
709
|
+
...Ge(e, a, i),
|
|
694
710
|
verify: !e.includes("--no-verify"),
|
|
695
|
-
targetDialect:
|
|
711
|
+
targetDialect: V(),
|
|
696
712
|
allowIncompatible: l,
|
|
697
713
|
targetSnapshot: c,
|
|
698
714
|
force: l,
|
|
699
715
|
onWarn: u,
|
|
700
|
-
onProgress:
|
|
716
|
+
onProgress: R,
|
|
701
717
|
assetsAlreadyRestored: !0,
|
|
702
|
-
...
|
|
703
|
-
...
|
|
718
|
+
...Je(e),
|
|
719
|
+
...f ? { copyLoader: f } : {}
|
|
704
720
|
})))
|
|
705
721
|
}), 0;
|
|
706
722
|
} finally {
|
|
707
|
-
await
|
|
723
|
+
await f?.close(), await n.rm(m, {
|
|
708
724
|
recursive: !0,
|
|
709
725
|
force: !0
|
|
710
726
|
});
|
|
711
727
|
}
|
|
712
728
|
}
|
|
713
|
-
let
|
|
729
|
+
let p = await v.runPromise(D({
|
|
730
|
+
...Ge(e, a, i),
|
|
714
731
|
store: s,
|
|
715
732
|
bundleDir: i,
|
|
716
733
|
mode: a,
|
|
717
734
|
onConflict: o,
|
|
718
735
|
atomic: X(e, a),
|
|
719
736
|
verify: !e.includes("--no-verify"),
|
|
720
|
-
targetDialect:
|
|
737
|
+
targetDialect: V(),
|
|
721
738
|
allowIncompatible: l,
|
|
722
739
|
targetSnapshot: c,
|
|
723
740
|
force: l,
|
|
724
741
|
onWarn: u,
|
|
725
|
-
onProgress:
|
|
726
|
-
...
|
|
727
|
-
...d ? { assets:
|
|
728
|
-
...
|
|
742
|
+
onProgress: R,
|
|
743
|
+
...Je(e),
|
|
744
|
+
...d ? { assets: fe(d) } : {},
|
|
745
|
+
...f ? { copyLoader: f } : {}
|
|
729
746
|
})).catch((e) => {
|
|
730
|
-
if (
|
|
747
|
+
if (Ze(e)) return Xe;
|
|
731
748
|
throw e;
|
|
732
|
-
}).finally(() =>
|
|
733
|
-
return typeof
|
|
749
|
+
}).finally(() => f?.close());
|
|
750
|
+
return typeof p == "symbol" ? 1 : (Ye(e, {
|
|
734
751
|
bundleDir: i,
|
|
735
|
-
...
|
|
736
|
-
assets:
|
|
752
|
+
...Q(p),
|
|
753
|
+
assets: p.manifest.assets.count
|
|
737
754
|
}), 0);
|
|
738
|
-
},
|
|
755
|
+
}, et = () => {
|
|
739
756
|
let e = r();
|
|
740
757
|
return {
|
|
741
758
|
...e.url ? { url: e.url } : {},
|
|
@@ -745,17 +762,17 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
745
762
|
...e.password ? { password: e.password } : {},
|
|
746
763
|
...e.database ? { database: e.database } : {}
|
|
747
764
|
};
|
|
748
|
-
},
|
|
749
|
-
let t =
|
|
765
|
+
}, tt = async (e) => {
|
|
766
|
+
let t = B(e);
|
|
750
767
|
if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
|
|
751
|
-
let n =
|
|
768
|
+
let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
|
|
752
769
|
await r(t, { recursive: !0 });
|
|
753
770
|
let o = a(t, C(n));
|
|
754
|
-
await
|
|
755
|
-
let s =
|
|
771
|
+
await v.runPromise(O(oe(n, et(), o)));
|
|
772
|
+
let s = Ee({
|
|
756
773
|
dialect: n,
|
|
757
|
-
schemaFingerprint: await
|
|
758
|
-
voltroVersion: await
|
|
774
|
+
schemaFingerprint: await L(n),
|
|
775
|
+
voltroVersion: await Ae(),
|
|
759
776
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
760
777
|
});
|
|
761
778
|
return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
|
|
@@ -765,11 +782,11 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
765
782
|
schemaFingerprint: s.schemaFingerprint ?? "<none>",
|
|
766
783
|
assets: "NOT included — use `voltro data export --assets` for rows + blobs"
|
|
767
784
|
}), 0;
|
|
768
|
-
},
|
|
769
|
-
let n =
|
|
770
|
-
flagUrl:
|
|
785
|
+
}, nt = async (e, t) => {
|
|
786
|
+
let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Oe({
|
|
787
|
+
flagUrl: z(t, "--drill-url"),
|
|
771
788
|
envUrl: process.env.DRILL_DB_URL,
|
|
772
|
-
liveKeys:
|
|
789
|
+
liveKeys: Pe()
|
|
773
790
|
});
|
|
774
791
|
if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
|
|
775
792
|
let s = await i(r(e, N), "utf8").then(P, () => null);
|
|
@@ -777,42 +794,42 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
777
794
|
I.info("restore drill: restoring into throwaway DB", {
|
|
778
795
|
dialect: n,
|
|
779
796
|
target: F(o.url)
|
|
780
|
-
}), await
|
|
797
|
+
}), await v.runPromise(O(de(n, { url: o.url }, a)));
|
|
781
798
|
let c = null, l = 0;
|
|
782
799
|
try {
|
|
783
|
-
let e = await
|
|
784
|
-
c =
|
|
800
|
+
let e = await H(n, { url: o.url });
|
|
801
|
+
c = ee(e), l = e.tables.length;
|
|
785
802
|
} catch (e) {
|
|
786
803
|
return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
787
804
|
}
|
|
788
|
-
let u =
|
|
805
|
+
let u = ke({
|
|
789
806
|
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
790
807
|
restoredFingerprint: c,
|
|
791
808
|
restoredTableCount: l
|
|
792
809
|
});
|
|
793
810
|
for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
|
|
794
811
|
return +!u.pass;
|
|
795
|
-
},
|
|
796
|
-
let t =
|
|
812
|
+
}, rt = async (e) => {
|
|
813
|
+
let t = B(e);
|
|
797
814
|
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
|
|
799
|
-
if (!await
|
|
800
|
-
let n =
|
|
815
|
+
if (e.includes("--drill")) return nt(t, e);
|
|
816
|
+
if (!await Me(e)) return 1;
|
|
817
|
+
let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = De({
|
|
801
818
|
stamp: await i(r(t, N), "utf8").then(P, () => null),
|
|
802
819
|
currentDialect: n,
|
|
803
|
-
currentFingerprint: await
|
|
820
|
+
currentFingerprint: await L(n)
|
|
804
821
|
});
|
|
805
822
|
for (let e of o.info) I.info(`backup stamp: ${e}`);
|
|
806
823
|
if (o.warn && I.warn(o.warn), o.refuse) {
|
|
807
824
|
if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
|
|
808
825
|
I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
|
|
809
826
|
}
|
|
810
|
-
return await
|
|
827
|
+
return await v.runPromise(O(de(n, et(), a))), I.info("native restore complete", {
|
|
811
828
|
dialect: n,
|
|
812
829
|
artifact: a
|
|
813
830
|
}), 0;
|
|
814
|
-
},
|
|
815
|
-
let t =
|
|
831
|
+
}, it = async (e) => {
|
|
832
|
+
let t = B(e);
|
|
816
833
|
if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
|
|
817
834
|
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
|
|
818
835
|
if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
|
|
@@ -824,22 +841,41 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
824
841
|
let s = await i.readFile(n, "utf8").catch(() => void 0);
|
|
825
842
|
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);
|
|
826
843
|
}
|
|
827
|
-
let o =
|
|
828
|
-
let e =
|
|
844
|
+
let o = q(e), s = () => {
|
|
845
|
+
let e = J(a);
|
|
829
846
|
return o ? w(e, o) : e;
|
|
830
847
|
};
|
|
831
848
|
if (e.includes("--json")) {
|
|
832
|
-
let e = (await
|
|
849
|
+
let e = (await T(s(), ["manifest.json"])).get("manifest.json");
|
|
833
850
|
return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
|
|
834
851
|
}
|
|
835
|
-
let c = (await
|
|
852
|
+
let c = (await T(s(), ["README.md"])).get("README.md");
|
|
836
853
|
if (c) return console.log(c.toString("utf8")), 0;
|
|
837
|
-
let l = (await
|
|
854
|
+
let l = (await T(s(), ["manifest.json"])).get("manifest.json");
|
|
838
855
|
return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
|
|
839
|
-
},
|
|
840
|
-
let t =
|
|
856
|
+
}, at = async (e) => {
|
|
857
|
+
let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-DNJ7MKph.js"), { store: r } = await W(), i = await n(r);
|
|
858
|
+
if (i === void 0) return I.info("no interrupted replace is recorded — nothing to clear"), 0;
|
|
859
|
+
if (!e.includes("--yes")) return I.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
|
|
860
|
+
let a = await r.query({
|
|
861
|
+
table: t,
|
|
862
|
+
predicate: void 0,
|
|
863
|
+
order: [],
|
|
864
|
+
take: void 0,
|
|
865
|
+
skip: void 0,
|
|
866
|
+
projection: void 0
|
|
867
|
+
});
|
|
868
|
+
for (let e of a) await r.delete(t, String(e.id));
|
|
869
|
+
return I.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
|
|
870
|
+
}, ot = async (e) => {
|
|
871
|
+
let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await W(), a = V();
|
|
872
|
+
if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return I.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
|
|
873
|
+
let { introspectSchema: o } = await import("@voltro/database/sql"), s = (await i.run((await import("@effect/sql")).SqlClient.SqlClient.pipe((await import("effect")).Effect.flatMap(o)))).tables.map((e) => e.name).filter((e) => e.startsWith(t));
|
|
874
|
+
return s.length === 0 ? (I.info("no staging tables are present — nothing to clear"), 0) : e.includes("--yes") ? (await r(i, s.map((e) => n(e.slice(t.length), a))), I.info(`dropped ${s.length} staging table(s)`), 0) : (I.error(`${s.length} staging table(s) from an earlier \`--mode replace\`:\n` + s.map((e) => ` ${e}`).join("\n") + "\n\n Each holds a copy of what a run was loading. Nothing else removes them.\n Make sure no import is running right now — this cannot tell a leftover from one in flight.\n Re-run with --yes to drop them."), 1);
|
|
875
|
+
}, st = async (e) => {
|
|
876
|
+
let t = B(e), n = B(e.filter((e) => e !== t));
|
|
841
877
|
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 =
|
|
878
|
+
let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
|
|
843
879
|
return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), I.info("unpack complete", {
|
|
844
880
|
file: t,
|
|
845
881
|
dir: n
|
|
@@ -869,6 +905,8 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
869
905
|
"--timeout"
|
|
870
906
|
]
|
|
871
907
|
},
|
|
908
|
+
"clear-staging": { common: ["--yes"] },
|
|
909
|
+
"clear-replace-marker": { common: ["--yes"] },
|
|
872
910
|
import: {
|
|
873
911
|
common: [
|
|
874
912
|
"--target",
|
|
@@ -885,14 +923,17 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
885
923
|
directOnly: [
|
|
886
924
|
"--no-verify",
|
|
887
925
|
"--allow-live",
|
|
888
|
-
"--passphrase"
|
|
926
|
+
"--passphrase",
|
|
927
|
+
"--rollback-dir",
|
|
928
|
+
"--no-rollback"
|
|
889
929
|
],
|
|
890
930
|
apiOnly: [
|
|
891
931
|
"--api-url",
|
|
892
932
|
"--token",
|
|
893
933
|
"--bundle-key",
|
|
894
934
|
"--timeout",
|
|
895
|
-
"--chunk-size"
|
|
935
|
+
"--chunk-size",
|
|
936
|
+
"--rollback-key"
|
|
896
937
|
]
|
|
897
938
|
},
|
|
898
939
|
unpack: { common: ["--passphrase", "--json"] },
|
|
@@ -906,13 +947,13 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
906
947
|
"--drill-url",
|
|
907
948
|
"--api-url"
|
|
908
949
|
] }
|
|
909
|
-
},
|
|
950
|
+
}, ct = (e) => e.split("=")[0], lt = (e, t) => {
|
|
910
951
|
let n = $[e];
|
|
911
952
|
if (!n) return;
|
|
912
|
-
let r =
|
|
953
|
+
let r = z(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 ?? []);
|
|
913
954
|
for (let n of t) {
|
|
914
955
|
if (!n.startsWith("--")) continue;
|
|
915
|
-
let t =
|
|
956
|
+
let t = ct(n);
|
|
916
957
|
if (i.has(t)) continue;
|
|
917
958
|
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.`;
|
|
918
959
|
let o = Object.entries($).filter(([n, r]) => n !== e && [
|
|
@@ -922,13 +963,13 @@ var _e = /* @__PURE__ */ new Set([
|
|
|
922
963
|
].includes(t)).map(([e]) => e);
|
|
923
964
|
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.`;
|
|
924
965
|
}
|
|
925
|
-
},
|
|
966
|
+
}, ut = async (e) => {
|
|
926
967
|
let [t, ...n] = e;
|
|
927
968
|
if (t !== void 0 && $[t] !== void 0) {
|
|
928
|
-
let e =
|
|
969
|
+
let e = lt(t, n);
|
|
929
970
|
if (e) return I.error(e), 2;
|
|
930
971
|
}
|
|
931
|
-
return t === "export" ?
|
|
972
|
+
return t === "export" ? Ke(n) : t === "import" ? $e(n) : t === "unpack" ? st(n) : t === "inspect" ? it(n) : t === "backup" ? tt(n) : t === "restore" ? rt(n) : t === "clear-replace-marker" ? at(n) : t === "clear-staging" ? ot(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore|clear-replace-marker|clear-staging> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
|
|
932
973
|
};
|
|
933
974
|
//#endregion
|
|
934
|
-
export {
|
|
975
|
+
export { ut as runDataCommand };
|