@voltro/cli 0.49.0 → 0.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/apiBuild-BZ-plaQN.js +2 -0
- package/dist/{apiBuild-QmYHaCF4.js → apiBuild-D3s7lx57.js} +2 -2
- package/dist/appGraph-CM9wvUmD.js +2 -0
- package/dist/appGraph-Cv2W_wO5.js +111 -0
- package/dist/bin.js +1 -1
- package/dist/{build-Bi4O_g7T.js → build-CFmqF39c.js} +7 -7
- package/dist/{checkCommand-udbQpphy.js → checkCommand-DomvYDi1.js} +1 -1
- package/dist/{checkCommand-D8vjQXQt.js → checkCommand-DsKIjL6k.js} +54 -45
- package/dist/{codegenCommand-DApSH7hK.js → codegenCommand-C0S27nbB.js} +2 -2
- package/dist/{codemodRunner-De1_316I.js → codemodRunner-DwY8Rjbl.js} +696 -481
- package/dist/{commands-a5PxCaIg.js → commands-Brp4DI8u.js} +22 -22
- package/dist/{dashboardCommand-DaoUUqJS.js → dashboardCommand-bbLfc9FW.js} +3 -3
- package/dist/{dataCommand-CJv_OMoS.js → dataCommand-k9mXTXin.js} +511 -333
- package/dist/{dev-C31Nvlbv.js → dev-C0cbqysu.js} +4 -4
- package/dist/{dev-DFVuzOj7.js → dev-Dz9GqA6P.js} +1 -1
- package/dist/doctorCommand-BlciWUJb.js +2 -0
- package/dist/{doctorCommand-B3uraeAe.js → doctorCommand-DyViaaCP.js} +38 -22
- package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
- package/dist/{dormancyCommand-QUXQ9yoz.js → dormancyCommand-DMZPA_HW.js} +1 -1
- package/dist/{embeddingsCommand-Zw1-rYSF.js → embeddingsCommand-CMxRPkg6.js} +1 -1
- package/dist/{envCommand-DtaJ4xEf.js → envCommand-CDLr-yD4.js} +2 -2
- package/dist/{evolveCommand-JDD3hE1i.js → evolveCommand-Ct4NesL3.js} +2 -2
- package/dist/{generateCommand-CcyvH2ve.js → generateCommand-BDdOxhnv.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-COsUC1DB.js → infoCommand-orsKJKUy.js} +1 -1
- package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
- package/dist/inspect-CuGDYES0.js +2 -0
- package/dist/{interruptedReplace-C1ZI84xi.js → interruptedReplace-C3O3M1MM.js} +2 -1
- package/dist/interruptedReplace-CvmiAM9K.js +2 -0
- package/dist/manifestBuild-CPjhvM62.js +2 -0
- package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
- package/dist/{runtimeTrace-Di6fcndp.js → runtimeTrace-Bo8jWsNe.js} +1 -1
- package/dist/{sdkgen-CIi5gM17.js → sdkgen-OZhiT2vz.js} +1 -1
- package/dist/serveCommand-Bbgg7Paq.js +2 -0
- package/dist/{serveCommand-N3udT49p.js → serveCommand-YbD5VgfV.js} +8 -8
- package/dist/serveEntry.js +1 -1
- package/dist/{start-DvlYDyAr.js → start-7b_bL8Uc.js} +1 -1
- package/dist/{start-7HcSIKEp.js → start-B1nH9ahf.js} +2 -2
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-2BfbeMvp.js → updateCommand-BgH6vTtq.js} +2 -1
- package/dist/updateCommand-BweY17z-.js +2 -0
- package/dist/{webDev-B4IEMB0n.js → webDev-BbdkzMbd.js} +1 -1
- package/dist/webDev-BcRFTsMA.js +2 -0
- package/dist/{webhooksCommand-CwX9IzvJ.js → webhooksCommand-DjE6u0r4.js} +1 -1
- package/package.json +41 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +113 -3
- package/templates/agent-docs/data.md +36 -0
- package/templates/agent-docs/whats-new.md +30 -41
- 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-dZbHdqMg.js +0 -2
- package/dist/appGraph-KGDPTuTy.js +0 -110
- package/dist/appGraph-zuMGKVYX.js +0 -2
- package/dist/doctorCommand-CPL6EhrW.js +0 -2
- package/dist/inspect-DUze25t0.js +0 -2
- package/dist/interruptedReplace-DNJ7MKph.js +0 -2
- package/dist/manifestBuild-j0n109tt.js +0 -2
- package/dist/serveCommand-CzSDngKm.js +0 -2
- package/dist/updateCommand-CtYm7aKH.js +0 -2
- package/dist/webDev-B79ji8M5.js +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as e, t } from "./subcommandNames-DpYs3DXr.js";
|
|
2
2
|
import { t as n } from "./cliError-BmdYnghb.js";
|
|
3
|
-
import { c as r, i, o as a, z as o } from "./dev-
|
|
3
|
+
import { c as r, i, o as a, z as o } from "./dev-C0cbqysu.js";
|
|
4
4
|
import { n as s, t as c } from "./cliArgs-D4p8n7EE.js";
|
|
5
5
|
import { r as l } from "./dialectDriver-czCHYpeH.js";
|
|
6
6
|
import { i as u, n as d, r as f, t as p } from "./dataProfile-Du3ztYxv.js";
|
|
@@ -8,17 +8,17 @@ import { o as m, t as h } from "./inspectFetch-EMuhTG_9.js";
|
|
|
8
8
|
import { r as g } from "./dbCommand-Bf7Cy0mn.js";
|
|
9
9
|
import { resolve as _ } from "node:path";
|
|
10
10
|
import { createLogger as v } from "@voltro/logger";
|
|
11
|
-
import { DATA_TRANSFERS_TABLE as y,
|
|
12
|
-
import { Effect as
|
|
13
|
-
import { SqlClient as
|
|
14
|
-
import { declaredSnapshot as
|
|
15
|
-
import { STORAGE_REFS_TABLE as
|
|
16
|
-
import { createHash as
|
|
17
|
-
import { asImportError as
|
|
18
|
-
import { request as
|
|
19
|
-
import { request as
|
|
11
|
+
import { DATA_TRANSFERS_TABLE as y, REPLACE_IN_PROGRESS_TABLE as b, _voltroDataTransfersTable as x, _voltroReplaceInProgressTable as S, connectionIdentity as C, derivedRowId as ee, ensureTableRegistered as w, normaliseTarget as te, sameDatabase as ne, streamTable as re } from "@voltro/database";
|
|
12
|
+
import { Cause as T, Effect as E, Exit as ie, Option as ae, Stream as oe } from "effect";
|
|
13
|
+
import { SqlClient as se } from "@effect/sql";
|
|
14
|
+
import { declaredSnapshot as ce, fingerprintSchema as le, introspectSchema as ue } from "@voltro/database/sql";
|
|
15
|
+
import { STORAGE_REFS_TABLE as de, appStorageProvider as fe, configuredStorageProvider as D } from "@voltro/plugin-storage";
|
|
16
|
+
import { createHash as pe } from "node:crypto";
|
|
17
|
+
import { ASSETS_DIR as O, ASSET_INDEX_FILE as k, BundleError as me, asImportError as he, assessLiveImport as ge, backupArtifactName as A, backupCommand as _e, captureAssetsToCas as ve, classificationFromSnapshot as ye, decryptStream as j, encryptStream as be, packBundle as xe, peekBundle as M, previewMasking as Se, restoreAssetsFromCas as Ce, restoreCommand as we, runExport as Te, runImport as Ee, runNativeStep as N, storageAssetSink as De, storageAssetSource as Oe, storageBlobSink as ke, storageBlobSource as Ae, unpackBundle as je, unpackBundleToDir as Me } from "@voltro/data-transfer";
|
|
18
|
+
import { request as Ne } from "node:http";
|
|
19
|
+
import { request as Pe } from "node:https";
|
|
20
20
|
//#region src/longRequest.ts
|
|
21
|
-
var
|
|
21
|
+
var Fe = /* @__PURE__ */ new Set([
|
|
22
22
|
"ECONNREFUSED",
|
|
23
23
|
"ETIMEDOUT",
|
|
24
24
|
"EHOSTUNREACH",
|
|
@@ -26,7 +26,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
26
26
|
"ENOTFOUND",
|
|
27
27
|
"ECONNRESET",
|
|
28
28
|
"EAI_AGAIN"
|
|
29
|
-
]),
|
|
29
|
+
]), P = class extends Error {
|
|
30
30
|
url;
|
|
31
31
|
cause;
|
|
32
32
|
constructor(e, t) {
|
|
@@ -39,14 +39,14 @@ var we = /* @__PURE__ */ new Set([
|
|
|
39
39
|
})();
|
|
40
40
|
super(`the instance at ${n} is not answering (${t.code ?? "connect failed"}).`), this.url = e, this.cause = t, this.name = "InstanceUnreachable";
|
|
41
41
|
}
|
|
42
|
-
},
|
|
42
|
+
}, F = class extends Error {
|
|
43
43
|
elapsedMs;
|
|
44
44
|
url;
|
|
45
45
|
constructor(e, t) {
|
|
46
46
|
super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
|
|
47
47
|
}
|
|
48
|
-
},
|
|
49
|
-
let i = new URL(e), a = i.protocol === "https:" ?
|
|
48
|
+
}, I = async (e, t, n, r) => {
|
|
49
|
+
let i = new URL(e), a = i.protocol === "https:" ? Pe : Ne, o = Date.now();
|
|
50
50
|
return await new Promise((s, c) => {
|
|
51
51
|
let l = a({
|
|
52
52
|
protocol: i.protocol,
|
|
@@ -64,8 +64,8 @@ var we = /* @__PURE__ */ new Set([
|
|
|
64
64
|
})), e.on("error", c);
|
|
65
65
|
});
|
|
66
66
|
if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
|
|
67
|
-
l.destroy(new
|
|
68
|
-
}), l.on("error", (t) => c(
|
|
67
|
+
l.destroy(new F(Date.now() - o, e));
|
|
68
|
+
}), l.on("error", (t) => c(Fe.has(t.code ?? "") ? new P(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
|
|
69
69
|
l.end(t);
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
@@ -78,7 +78,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
78
78
|
}
|
|
79
79
|
})();
|
|
80
80
|
});
|
|
81
|
-
},
|
|
81
|
+
}, L = (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`, Ie = (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.", Le = 16777216, Re = async function* (e, t) {
|
|
82
82
|
let n = [], r = 0, i = 0, a = function* (e) {
|
|
83
83
|
let t = Buffer.concat(n);
|
|
84
84
|
n = [], r = 0;
|
|
@@ -100,12 +100,12 @@ var we = /* @__PURE__ */ new Set([
|
|
|
100
100
|
i += a.byteLength, yield s;
|
|
101
101
|
}
|
|
102
102
|
yield* a(!0);
|
|
103
|
-
},
|
|
103
|
+
}, ze = (e) => pe("sha256").update(Buffer.from(e)).digest("hex"), Be = async (e) => {
|
|
104
104
|
let t = 0, n = 0, r = {
|
|
105
105
|
status: 0,
|
|
106
106
|
body: {}
|
|
107
107
|
};
|
|
108
|
-
for await (let i of
|
|
108
|
+
for await (let i of Re(e.stream, e.chunkBytes)) {
|
|
109
109
|
if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
|
|
110
110
|
n++;
|
|
111
111
|
continue;
|
|
@@ -131,13 +131,14 @@ var we = /* @__PURE__ */ new Set([
|
|
|
131
131
|
bytes: t,
|
|
132
132
|
resumedFrom: e.alreadyHeld
|
|
133
133
|
};
|
|
134
|
-
},
|
|
134
|
+
}, R = "voltro-backup-stamp.json", Ve = (e) => ({
|
|
135
135
|
stampVersion: 1,
|
|
136
136
|
dialect: e.dialect,
|
|
137
137
|
schemaFingerprint: e.schemaFingerprint,
|
|
138
138
|
voltroVersion: e.voltroVersion,
|
|
139
|
-
createdAt: e.createdAt
|
|
140
|
-
|
|
139
|
+
createdAt: e.createdAt,
|
|
140
|
+
assets: e.assets ?? null
|
|
141
|
+
}), He = (e) => {
|
|
141
142
|
let t;
|
|
142
143
|
try {
|
|
143
144
|
t = JSON.parse(e);
|
|
@@ -148,17 +149,23 @@ var we = /* @__PURE__ */ new Set([
|
|
|
148
149
|
let n = t;
|
|
149
150
|
if (n.stampVersion !== 1 || typeof n.dialect != "string" || typeof n.voltroVersion != "string" || typeof n.createdAt != "string") return null;
|
|
150
151
|
let r = n.schemaFingerprint;
|
|
151
|
-
|
|
152
|
+
if (r !== null && typeof r != "string") return null;
|
|
153
|
+
let i = n.assets, a = typeof i == "object" && i && typeof i.count == "number" && typeof i.totalBytes == "number" ? {
|
|
154
|
+
count: i.count,
|
|
155
|
+
totalBytes: i.totalBytes
|
|
156
|
+
} : null;
|
|
157
|
+
return {
|
|
152
158
|
stampVersion: 1,
|
|
153
159
|
dialect: n.dialect,
|
|
154
160
|
schemaFingerprint: r,
|
|
155
161
|
voltroVersion: n.voltroVersion,
|
|
156
|
-
createdAt: n.createdAt
|
|
162
|
+
createdAt: n.createdAt,
|
|
163
|
+
assets: a
|
|
157
164
|
};
|
|
158
|
-
},
|
|
165
|
+
}, Ue = (e) => {
|
|
159
166
|
let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
|
|
160
167
|
if (t === null) return {
|
|
161
|
-
warn: `no ${
|
|
168
|
+
warn: `no ${R} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
162
169
|
info: []
|
|
163
170
|
};
|
|
164
171
|
let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
|
|
@@ -169,20 +176,102 @@ var we = /* @__PURE__ */ new Set([
|
|
|
169
176
|
refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
|
|
170
177
|
info: i
|
|
171
178
|
};
|
|
172
|
-
},
|
|
179
|
+
}, We = async (e, t) => {
|
|
180
|
+
let n = ee("dimp", `${t.direction}-${t.artifact}-${Date.now()}-${Math.random()}`);
|
|
181
|
+
try {
|
|
182
|
+
return w(x), await e.insert(y, {
|
|
183
|
+
id: n,
|
|
184
|
+
direction: t.direction,
|
|
185
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
186
|
+
finishedAt: null,
|
|
187
|
+
failure: null,
|
|
188
|
+
mode: `native ${t.dialect}${t.assets ? " + assets" : ""}`,
|
|
189
|
+
via: "cli",
|
|
190
|
+
bundle: t.artifact,
|
|
191
|
+
sourceFingerprint: null,
|
|
192
|
+
tablesWritten: 0,
|
|
193
|
+
rowsWritten: 0,
|
|
194
|
+
staged: null
|
|
195
|
+
}), {
|
|
196
|
+
runId: n,
|
|
197
|
+
recorded: !0
|
|
198
|
+
};
|
|
199
|
+
} catch {
|
|
200
|
+
return {
|
|
201
|
+
runId: n,
|
|
202
|
+
recorded: !1
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}, z = async (e, t, n) => {
|
|
206
|
+
t.recorded && await e.update(y, t.runId, {
|
|
207
|
+
finishedAt: /* @__PURE__ */ new Date(),
|
|
208
|
+
failure: n.failure ?? null,
|
|
209
|
+
...n.assets === void 0 ? {} : { rowsWritten: n.assets }
|
|
210
|
+
}).then(() => void 0, () => void 0);
|
|
211
|
+
}, Ge = async (e, t) => {
|
|
212
|
+
let n = ee("repl", `restore-${t.artifact}-${Date.now()}-${Math.random()}`), r = {
|
|
213
|
+
id: n,
|
|
214
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
215
|
+
kind: "restore",
|
|
216
|
+
tables: null,
|
|
217
|
+
capture: t.artifact,
|
|
218
|
+
via: "cli"
|
|
219
|
+
};
|
|
220
|
+
try {
|
|
221
|
+
w(S), await e.insert(b, r);
|
|
222
|
+
} catch {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
let i = async () => (await e.query({
|
|
226
|
+
table: b,
|
|
227
|
+
predicate: void 0,
|
|
228
|
+
order: [],
|
|
229
|
+
take: void 0,
|
|
230
|
+
skip: void 0,
|
|
231
|
+
projection: void 0
|
|
232
|
+
})).some((e) => String(e.id ?? "") === n);
|
|
233
|
+
return {
|
|
234
|
+
reassert: async () => {
|
|
235
|
+
try {
|
|
236
|
+
if (await i()) return "held";
|
|
237
|
+
} catch {}
|
|
238
|
+
try {
|
|
239
|
+
return w(S), await e.insert(b, r), "rewritten";
|
|
240
|
+
} catch {
|
|
241
|
+
return "lost";
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
clear: async () => {
|
|
245
|
+
await e.deleteMany(b, { where: {
|
|
246
|
+
column: "id",
|
|
247
|
+
op: "eq",
|
|
248
|
+
value: n
|
|
249
|
+
} }).then(() => void 0, () => void 0);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}, Ke = async (e) => {
|
|
253
|
+
let t = await E.runPromiseExit(e);
|
|
254
|
+
if (ie.isSuccess(t)) return;
|
|
255
|
+
let n = T.failureOption(t.cause);
|
|
256
|
+
if (ae.isSome(n)) {
|
|
257
|
+
let e = n.value;
|
|
258
|
+
return String(e?.message ?? e);
|
|
259
|
+
}
|
|
260
|
+
return T.pretty(t.cause);
|
|
261
|
+
}, B = (e) => {
|
|
173
262
|
try {
|
|
174
263
|
let t = new URL(e), n = t.pathname.replace(/^\//, "");
|
|
175
264
|
return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
|
|
176
265
|
} catch {
|
|
177
266
|
return e.trim();
|
|
178
267
|
}
|
|
179
|
-
},
|
|
268
|
+
}, qe = (e) => {
|
|
180
269
|
let t = e.flagUrl ?? e.envUrl;
|
|
181
270
|
if (t === void 0 || t.trim() === "") return {
|
|
182
271
|
ok: !1,
|
|
183
272
|
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."
|
|
184
273
|
};
|
|
185
|
-
let n =
|
|
274
|
+
let n = B(t);
|
|
186
275
|
return e.liveKeys.some((e) => e === n) ? {
|
|
187
276
|
ok: !1,
|
|
188
277
|
error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
|
|
@@ -190,7 +279,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
190
279
|
ok: !0,
|
|
191
280
|
url: t
|
|
192
281
|
};
|
|
193
|
-
},
|
|
282
|
+
}, Je = (e) => {
|
|
194
283
|
let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
|
|
195
284
|
return r === 0 ? {
|
|
196
285
|
pass: !1,
|
|
@@ -205,24 +294,24 @@ var we = /* @__PURE__ */ new Set([
|
|
|
205
294
|
pass: !0,
|
|
206
295
|
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
207
296
|
};
|
|
208
|
-
},
|
|
297
|
+
}, V = v({ scope: "voltro:data" }), Ye = async () => {
|
|
209
298
|
try {
|
|
210
299
|
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");
|
|
211
300
|
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
212
301
|
} catch {
|
|
213
302
|
return "unknown";
|
|
214
303
|
}
|
|
215
|
-
},
|
|
304
|
+
}, Xe = async (e) => {
|
|
216
305
|
try {
|
|
217
|
-
return
|
|
306
|
+
return le(await tt(e));
|
|
218
307
|
} catch {
|
|
219
308
|
return null;
|
|
220
309
|
}
|
|
221
|
-
},
|
|
310
|
+
}, H = (e) => {
|
|
222
311
|
if (e.event !== "done") return;
|
|
223
312
|
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
224
|
-
|
|
225
|
-
},
|
|
313
|
+
V.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
314
|
+
}, Ze = async (e) => {
|
|
226
315
|
let t = (e) => e?.meta?.databaseId;
|
|
227
316
|
if (e) {
|
|
228
317
|
let n = await h({
|
|
@@ -242,21 +331,21 @@ var we = /* @__PURE__ */ new Set([
|
|
|
242
331
|
};
|
|
243
332
|
}
|
|
244
333
|
return null;
|
|
245
|
-
},
|
|
246
|
-
let t = e.includes("--allow-live"), n = t ? null : await
|
|
247
|
-
dialect:
|
|
334
|
+
}, Qe = async (e) => {
|
|
335
|
+
let t = e.includes("--allow-live"), n = t ? null : await Ze(U(e, "--api-url")), r = C({
|
|
336
|
+
dialect: G(),
|
|
248
337
|
...a()
|
|
249
|
-
}), i =
|
|
250
|
-
dialect:
|
|
338
|
+
}), i = te({
|
|
339
|
+
dialect: G(),
|
|
251
340
|
...a()
|
|
252
|
-
}), { refuse: o, message: s } =
|
|
341
|
+
}), { refuse: o, message: s } = ge({
|
|
253
342
|
liveInstance: n?.label ?? null,
|
|
254
343
|
allowLive: t,
|
|
255
|
-
sameTarget:
|
|
344
|
+
sameTarget: ne(r, n?.databaseId),
|
|
256
345
|
...i === void 0 ? {} : { targetLabel: `${i.host}:${i.port}/${i.database}` }
|
|
257
346
|
});
|
|
258
|
-
return !o || (
|
|
259
|
-
},
|
|
347
|
+
return !o || (V.error(s ?? "refusing: live instance detected"), !1);
|
|
348
|
+
}, U = s, $e = [
|
|
260
349
|
"--api-url",
|
|
261
350
|
"--bundle-key",
|
|
262
351
|
"--compression",
|
|
@@ -276,50 +365,50 @@ var we = /* @__PURE__ */ new Set([
|
|
|
276
365
|
"--timeout",
|
|
277
366
|
"--tenant",
|
|
278
367
|
"--token"
|
|
279
|
-
],
|
|
368
|
+
], W = (e) => c(e, $e), G = () => {
|
|
280
369
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
281
370
|
if (e === "memory") throw new n("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
282
371
|
return e;
|
|
283
|
-
},
|
|
372
|
+
}, et = async (e, t) => {
|
|
284
373
|
let n = (await l(e)).makeSqlLayer(t);
|
|
285
|
-
return
|
|
286
|
-
},
|
|
374
|
+
return E.runPromise(se.SqlClient.pipe(E.flatMap(ue), E.provide(n)));
|
|
375
|
+
}, tt = (e) => et(e, a()), nt = () => {
|
|
287
376
|
let e = a(), t = [];
|
|
288
|
-
return e.url && t.push(
|
|
289
|
-
},
|
|
377
|
+
return e.url && t.push(B(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
|
|
378
|
+
}, K = async () => {
|
|
290
379
|
let e = process.cwd(), t = await r(e);
|
|
291
380
|
if (!t) throw new n("voltro data: no app.config.ts found in the current directory.");
|
|
292
|
-
let a = await i(t, void 0), o =
|
|
381
|
+
let a = await i(t, void 0), o = G();
|
|
293
382
|
return {
|
|
294
383
|
store: a,
|
|
295
|
-
snapshot: await
|
|
384
|
+
snapshot: await tt(o),
|
|
296
385
|
dialect: o
|
|
297
386
|
};
|
|
298
|
-
},
|
|
299
|
-
table:
|
|
387
|
+
}, q = (e) => re(e, {
|
|
388
|
+
table: de,
|
|
300
389
|
chunkSize: 500
|
|
301
|
-
}).pipe(
|
|
390
|
+
}).pipe(oe.map((e) => ({
|
|
302
391
|
key: String(e.key),
|
|
303
392
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
304
393
|
size: Number(e.size ?? 0)
|
|
305
|
-
}))),
|
|
394
|
+
}))), J = (e) => !!e && e.endsWith(".vbundle"), Y = (e) => U(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, rt = (e) => e.includes("--encrypt") || U(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, it = async (e, t) => {
|
|
306
395
|
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
307
396
|
await r(i.from(e), n(t));
|
|
308
|
-
},
|
|
397
|
+
}, X = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
309
398
|
let { createReadStream: t } = await import("node:fs");
|
|
310
399
|
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
311
|
-
} }),
|
|
400
|
+
} }), at = async (e, t) => {
|
|
312
401
|
if (t !== void 0 || e === void 0) return null;
|
|
313
402
|
try {
|
|
314
|
-
let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a =
|
|
403
|
+
let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = J(e) ? (await M(X(i), ["manifest.json"])).get("manifest.json") : await r.readFile(n(i, "manifest.json"));
|
|
315
404
|
if (a === void 0) return null;
|
|
316
405
|
let o = (JSON.parse(Buffer.from(a).toString("utf8")).tables ?? []).map((e) => e.name).filter((e) => typeof e == "string");
|
|
317
406
|
return o.length > 0 ? o : null;
|
|
318
407
|
} catch {
|
|
319
408
|
return null;
|
|
320
409
|
}
|
|
321
|
-
},
|
|
322
|
-
let t =
|
|
410
|
+
}, ot = (e) => {
|
|
411
|
+
let t = U(e, "--max-request-seconds") ?? process.env.VOLTRO_MAX_REQUEST_SECONDS;
|
|
323
412
|
if (t === void 0) return { pollMs: 2e3 };
|
|
324
413
|
let r = Number(t);
|
|
325
414
|
if (!Number.isFinite(r) || r <= 0) throw new n(`--max-request-seconds expects a positive number of seconds, got '${t}'`);
|
|
@@ -328,8 +417,8 @@ var we = /* @__PURE__ */ new Set([
|
|
|
328
417
|
perRequest: { timeoutMs: i },
|
|
329
418
|
pollMs: Math.max(500, Math.min(2e3, Math.floor(i / 2)))
|
|
330
419
|
};
|
|
331
|
-
},
|
|
332
|
-
let r = await fetch(`${e.replace(/\/+$/, "")}/_voltro/admin/
|
|
420
|
+
}, st = async (e, t, n) => {
|
|
421
|
+
let r = await fetch(`${e.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=50`, { headers: { authorization: `Bearer ${t}` } }).catch(() => null);
|
|
333
422
|
if (r === null || r.status !== 200) return null;
|
|
334
423
|
let i = ((await r.json().catch(() => null))?.runs ?? []).find((e) => e.id === n);
|
|
335
424
|
return i === void 0 ? null : {
|
|
@@ -337,7 +426,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
337
426
|
startedAt: new Date(String(i.startedAt)),
|
|
338
427
|
finishedAt: i.finishedAt === null || i.finishedAt === void 0 ? null : new Date(String(i.finishedAt))
|
|
339
428
|
};
|
|
340
|
-
},
|
|
429
|
+
}, ct = async (e) => {
|
|
341
430
|
let t = e.apiUrl.replace(/\/+$/, ""), n = [], r = 0, i;
|
|
342
431
|
for (;;) {
|
|
343
432
|
let a = `${t}/_voltro/admin/export/download?runId=${encodeURIComponent(e.runId)}&offset=${r}`, o = new AbortController(), s = e.perRequest === void 0 ? void 0 : setTimeout(() => {
|
|
@@ -359,12 +448,12 @@ var we = /* @__PURE__ */ new Set([
|
|
|
359
448
|
e.log.info(` downloading — ${r}/${i ?? "?"} bytes`);
|
|
360
449
|
}
|
|
361
450
|
return new Uint8Array(Buffer.concat(n));
|
|
362
|
-
},
|
|
363
|
-
let r =
|
|
451
|
+
}, Z = "transfers", lt = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), ut = (e, t) => {
|
|
452
|
+
let r = U(e, "--tenant"), i = U(e, "--tables"), a = U(e, "--exclude");
|
|
364
453
|
if (a !== void 0) {
|
|
365
454
|
if (i !== void 0) throw new n("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
|
|
366
455
|
if (r !== void 0) throw new n("voltro data export: --exclude does not apply to --tenant, whose table set is derived from the tenant closure.");
|
|
367
|
-
let e =
|
|
456
|
+
let e = lt(a), o = new Set(t), s = e.filter((e) => !o.has(e));
|
|
368
457
|
if (s.length > 0) throw new n(`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.`);
|
|
369
458
|
return {
|
|
370
459
|
kind: "all",
|
|
@@ -376,15 +465,15 @@ var we = /* @__PURE__ */ new Set([
|
|
|
376
465
|
tenantId: r
|
|
377
466
|
} : i ? {
|
|
378
467
|
kind: "tables",
|
|
379
|
-
tables:
|
|
468
|
+
tables: lt(i)
|
|
380
469
|
} : { kind: "all" };
|
|
381
|
-
},
|
|
470
|
+
}, dt = async () => {
|
|
382
471
|
let e = await g(process.cwd());
|
|
383
|
-
return
|
|
384
|
-
},
|
|
385
|
-
let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id ===
|
|
472
|
+
return ye(ce(e));
|
|
473
|
+
}, ft = "voltro/tenant", pt = async () => {
|
|
474
|
+
let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === ft)).map((e) => e.tableName);
|
|
386
475
|
return e.length > 0 ? e : void 0;
|
|
387
|
-
},
|
|
476
|
+
}, mt = (e) => {
|
|
388
477
|
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
389
478
|
console.log(`\n ${t.table}`);
|
|
390
479
|
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
@@ -397,8 +486,8 @@ var we = /* @__PURE__ */ new Set([
|
|
|
397
486
|
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
398
487
|
for (let t of e.unclassified) console.log(` ${t}`);
|
|
399
488
|
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
400
|
-
},
|
|
401
|
-
let { resolve: t, join: r } = await import("node:path"), i = await import("node:fs/promises"), a =
|
|
489
|
+
}, ht = async (e) => {
|
|
490
|
+
let { resolve: t, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = U(e.args, "--chunk-size"), o = a === void 0 ? Le : Math.round(Number(a) * 1024 * 1024);
|
|
402
491
|
if (!Number.isFinite(o) || o <= 0) throw new n(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
|
|
403
492
|
let s = e.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
|
|
404
493
|
for (; l <= o;) {
|
|
@@ -413,11 +502,11 @@ var we = /* @__PURE__ */ new Set([
|
|
|
413
502
|
kind: "small",
|
|
414
503
|
body: Buffer.concat(c)
|
|
415
504
|
};
|
|
416
|
-
let d = t(process.cwd(), e.bundleDir), f =
|
|
505
|
+
let d = t(process.cwd(), e.bundleDir), f = ze(J(e.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await I(e.url, Buffer.alloc(0), {
|
|
417
506
|
...e.headers,
|
|
418
507
|
"x-import-upload-id": p,
|
|
419
508
|
"x-import-probe": "1"
|
|
420
|
-
},
|
|
509
|
+
}, $(e.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
|
|
421
510
|
h > 0 && e.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
|
|
422
511
|
let g = (async function* () {
|
|
423
512
|
for (let e of c) yield e;
|
|
@@ -426,7 +515,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
426
515
|
if (e.done === !0) return;
|
|
427
516
|
yield e.value;
|
|
428
517
|
}
|
|
429
|
-
})(), _ = 0, v = await
|
|
518
|
+
})(), _ = 0, v = await Be({
|
|
430
519
|
stream: g,
|
|
431
520
|
chunkBytes: o,
|
|
432
521
|
uploadId: p,
|
|
@@ -440,7 +529,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
440
529
|
e.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.");
|
|
441
530
|
},
|
|
442
531
|
target: { send: async (t, n) => {
|
|
443
|
-
let r = await
|
|
532
|
+
let r = await I(e.url, t, n, $(e.args)), i = {};
|
|
444
533
|
try {
|
|
445
534
|
i = JSON.parse(r.body.toString("utf8") || "{}");
|
|
446
535
|
} catch {}
|
|
@@ -462,83 +551,83 @@ var we = /* @__PURE__ */ new Set([
|
|
|
462
551
|
body: Buffer.from(JSON.stringify(v.body))
|
|
463
552
|
}
|
|
464
553
|
};
|
|
465
|
-
},
|
|
554
|
+
}, gt = (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.`, Q = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", _t = (e, t, n) => {
|
|
466
555
|
if (t !== "replace" || e.includes("--no-rollback")) return;
|
|
467
|
-
let r =
|
|
556
|
+
let r = U(e, "--rollback-dir");
|
|
468
557
|
return r === void 0 ? o(n) : _(r);
|
|
469
|
-
},
|
|
470
|
-
let r =
|
|
558
|
+
}, vt = (e, t, n) => {
|
|
559
|
+
let r = _t(e, t, n);
|
|
471
560
|
return {
|
|
472
561
|
...r === void 0 ? {} : { rollbackDir: r },
|
|
473
562
|
recordInterruption: !0,
|
|
474
563
|
via: "direct"
|
|
475
564
|
};
|
|
476
|
-
},
|
|
477
|
-
let t =
|
|
565
|
+
}, $ = (e) => {
|
|
566
|
+
let t = U(e, "--timeout");
|
|
478
567
|
if (t === void 0) return {};
|
|
479
568
|
let r = Number(t);
|
|
480
569
|
if (!Number.isFinite(r) || r <= 0) throw new n(`--timeout expects a positive number of seconds, got '${t}'`);
|
|
481
570
|
return { timeoutMs: Math.round(r * 1e3) };
|
|
482
|
-
},
|
|
483
|
-
let t =
|
|
484
|
-
if (!r && !(t && n)) return
|
|
571
|
+
}, yt = async (e) => {
|
|
572
|
+
let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
|
|
573
|
+
if (!r && !(t && n)) return V.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;
|
|
485
574
|
if (t) {
|
|
486
|
-
let t =
|
|
487
|
-
if (!t) return
|
|
488
|
-
let i =
|
|
489
|
-
if (!i) return
|
|
490
|
-
let a =
|
|
491
|
-
if (o !== void 0 && a !== void 0) return
|
|
575
|
+
let t = U(e, "--api-url");
|
|
576
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
577
|
+
let i = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
578
|
+
if (!i) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
579
|
+
let a = U(e, "--tables"), o = U(e, "--exclude");
|
|
580
|
+
if (o !== void 0 && a !== void 0) return V.error("voltro data export: --exclude and --tables are two ways to say the same thing; use one."), 1;
|
|
492
581
|
let s = {
|
|
493
|
-
...
|
|
494
|
-
...
|
|
582
|
+
...U(e, "--profile") ? { profile: U(e, "--profile") } : {},
|
|
583
|
+
...U(e, "--tenant") ? { tenant: U(e, "--tenant") } : {},
|
|
495
584
|
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
496
585
|
...o ? { exclude: o.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
497
|
-
...
|
|
498
|
-
...
|
|
586
|
+
...U(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
587
|
+
...U(e, "--compression") ? { compression: U(e, "--compression") } : {},
|
|
499
588
|
...e.includes("--assets") ? { assets: !0 } : {},
|
|
500
589
|
...n ? { bundleKey: n } : {}
|
|
501
|
-
}, c =
|
|
590
|
+
}, c = ot(e), l;
|
|
502
591
|
try {
|
|
503
|
-
let n = await
|
|
592
|
+
let n = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(s), {
|
|
504
593
|
"content-type": "application/json",
|
|
505
594
|
authorization: `Bearer ${i}`
|
|
506
|
-
}, c.perRequest ??
|
|
507
|
-
if (n.status < 200 || n.status >= 300) return
|
|
595
|
+
}, c.perRequest ?? $(e));
|
|
596
|
+
if (n.status < 200 || n.status >= 300) return V.error(`api export failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
|
|
508
597
|
l = JSON.parse(n.body.toString("utf8"));
|
|
509
598
|
} catch (e) {
|
|
510
|
-
if (e instanceof
|
|
511
|
-
if (e instanceof
|
|
599
|
+
if (e instanceof F) return V.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline. Nothing was written locally; re-run, or raise --timeout.`), 1;
|
|
600
|
+
if (e instanceof P) return V.error(`api export: ${L(e)}`), 1;
|
|
512
601
|
throw e;
|
|
513
602
|
}
|
|
514
|
-
if (e.includes("--detach")) return
|
|
515
|
-
if (l.recorded === !1) return
|
|
603
|
+
if (e.includes("--detach")) return V.info(`api export: started as ${l.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${Z} --target api --api-url ${t}`), 0;
|
|
604
|
+
if (l.recorded === !1) return V.warn(`api export: started as ${l.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next one is watchable.`), 0;
|
|
516
605
|
let p = await d({
|
|
517
|
-
read: () =>
|
|
606
|
+
read: () => st(t, i, l.runId),
|
|
518
607
|
onTick: (e) => {
|
|
519
|
-
|
|
608
|
+
V.info(f(e));
|
|
520
609
|
},
|
|
521
610
|
intervalMs: c.pollMs,
|
|
522
611
|
...c.total === void 0 ? {} : { timeoutMs: c.total }
|
|
523
612
|
}), m = u(p);
|
|
524
|
-
if (m !== 0) return
|
|
525
|
-
if (n) return
|
|
613
|
+
if (m !== 0) return V.error(m === 1 ? `api export FAILED (${l.runId}): ${String(p?.failure ?? "no reason recorded")}` : `api export: no outcome visible for ${l.runId}. The run is not cancelled; it is simply not readable from here.`), m;
|
|
614
|
+
if (n) return V.info("api export complete (pushed to storage)", {
|
|
526
615
|
via: t,
|
|
527
616
|
runId: l.runId,
|
|
528
617
|
bundleKey: n,
|
|
529
618
|
tables: p?.tablesWritten ?? 0,
|
|
530
619
|
rows: p?.rowsWritten ?? 0
|
|
531
620
|
}), 0;
|
|
532
|
-
let { resolve: h } = await import("node:path"), g = await
|
|
621
|
+
let { resolve: h } = await import("node:path"), g = await ct({
|
|
533
622
|
apiUrl: t,
|
|
534
623
|
secret: i,
|
|
535
624
|
runId: l.runId,
|
|
536
|
-
log:
|
|
625
|
+
log: V,
|
|
537
626
|
...c.perRequest ? { perRequest: c.perRequest } : {}
|
|
538
627
|
});
|
|
539
|
-
return g === null ? 1 : (await
|
|
628
|
+
return g === null ? 1 : (await Me((async function* () {
|
|
540
629
|
yield g;
|
|
541
|
-
})(), h(process.cwd(), r)),
|
|
630
|
+
})(), h(process.cwd(), r)), V.info("api export complete (downloaded from the running instance)", {
|
|
542
631
|
outDir: r,
|
|
543
632
|
runId: l.runId,
|
|
544
633
|
tables: p?.tablesWritten ?? 0,
|
|
@@ -546,11 +635,13 @@ var we = /* @__PURE__ */ new Set([
|
|
|
546
635
|
bytes: g.byteLength
|
|
547
636
|
}), 0);
|
|
548
637
|
}
|
|
549
|
-
if (!r) return
|
|
550
|
-
let i =
|
|
638
|
+
if (!r) return V.error("a <outDir> is required for a direct export"), 1;
|
|
639
|
+
let i = U(e, "--profile"), a = i ? await p(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: c } = await K(), l = e.includes("--assets") || a.assets === !0, m = l ? D() : void 0;
|
|
640
|
+
if (l && !m) return V.error("--assets: this app has no storage provider configured, so there are no blobs to export.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
|
|
641
|
+
let h = U(e, "--compression") ?? a.compression, g = U(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", _ = a.masking, v = a.subset, y = a.scope ?? ut(e, s.tables.map((e) => e.name)), b = _ ? await dt() : void 0, x = y.kind === "tenant" ? await pt() : void 0, S = x ? { tenantTables: x } : {};
|
|
551
642
|
if (e.includes("--dry-run")) {
|
|
552
643
|
let e = y.kind === "tables" && y.tables ? [...y.tables] : s.tables.map((e) => e.name);
|
|
553
|
-
if (
|
|
644
|
+
if (V.info("export DRY RUN — nothing was written", {
|
|
554
645
|
outDir: r,
|
|
555
646
|
tables: e.length,
|
|
556
647
|
scope: y.kind,
|
|
@@ -558,8 +649,8 @@ var we = /* @__PURE__ */ new Set([
|
|
|
558
649
|
compression: h ?? "zstd",
|
|
559
650
|
assets: l,
|
|
560
651
|
masking: _ ? "profile policy applies" : "none"
|
|
561
|
-
}),
|
|
562
|
-
let t = await
|
|
652
|
+
}), V.info(`would export: ${e.join(", ")}`), !_) return 0;
|
|
653
|
+
let t = await E.runPromise(Se({
|
|
563
654
|
store: o,
|
|
564
655
|
snapshot: s,
|
|
565
656
|
masking: _,
|
|
@@ -569,12 +660,12 @@ var we = /* @__PURE__ */ new Set([
|
|
|
569
660
|
...S
|
|
570
661
|
}
|
|
571
662
|
}));
|
|
572
|
-
return
|
|
663
|
+
return mt(t), +(t.unclassified.length > 0);
|
|
573
664
|
}
|
|
574
|
-
if (
|
|
665
|
+
if (J(r)) {
|
|
575
666
|
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), u = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
|
|
576
667
|
try {
|
|
577
|
-
let t = await
|
|
668
|
+
let t = await E.runPromise(Te({
|
|
578
669
|
store: o,
|
|
579
670
|
snapshot: s,
|
|
580
671
|
dialect: c,
|
|
@@ -587,17 +678,17 @@ var we = /* @__PURE__ */ new Set([
|
|
|
587
678
|
...h ? { compression: h } : {},
|
|
588
679
|
..._ ? { masking: _ } : {},
|
|
589
680
|
...b ? { classification: b } : {},
|
|
590
|
-
onProgress:
|
|
591
|
-
})), n =
|
|
681
|
+
onProgress: H
|
|
682
|
+
})), n = xe({
|
|
592
683
|
dir: u,
|
|
593
|
-
...m ? { blobs:
|
|
594
|
-
}), a =
|
|
684
|
+
...m ? { blobs: Ae(m, () => q(o)) } : {}
|
|
685
|
+
}), a = rt(e);
|
|
595
686
|
if (a) {
|
|
596
|
-
let t =
|
|
597
|
-
if (!t) return
|
|
598
|
-
n =
|
|
687
|
+
let t = Y(e);
|
|
688
|
+
if (!t) return V.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
689
|
+
n = be(n, t);
|
|
599
690
|
}
|
|
600
|
-
return await
|
|
691
|
+
return await it(n, i(process.cwd(), r)), V.info("export complete (single file)", {
|
|
601
692
|
file: r,
|
|
602
693
|
encrypted: a,
|
|
603
694
|
assets: l,
|
|
@@ -611,7 +702,7 @@ var we = /* @__PURE__ */ new Set([
|
|
|
611
702
|
});
|
|
612
703
|
}
|
|
613
704
|
}
|
|
614
|
-
let C = await
|
|
705
|
+
let C = await E.runPromise(Te({
|
|
615
706
|
store: o,
|
|
616
707
|
snapshot: s,
|
|
617
708
|
dialect: c,
|
|
@@ -624,10 +715,10 @@ var we = /* @__PURE__ */ new Set([
|
|
|
624
715
|
...h ? { compression: h } : {},
|
|
625
716
|
..._ ? { masking: _ } : {},
|
|
626
717
|
...b ? { classification: b } : {},
|
|
627
|
-
...m ? { assets:
|
|
628
|
-
onProgress:
|
|
718
|
+
...m ? { assets: Oe(m, () => q(o)) } : {},
|
|
719
|
+
onProgress: H
|
|
629
720
|
}));
|
|
630
|
-
return
|
|
721
|
+
return V.info("export complete", {
|
|
631
722
|
outDir: r,
|
|
632
723
|
tables: C.tables.length,
|
|
633
724
|
rows: C.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -635,13 +726,13 @@ var we = /* @__PURE__ */ new Set([
|
|
|
635
726
|
compression: C.compression,
|
|
636
727
|
...C.masking ? { maskedColumns: C.masking.transformed.length } : {}
|
|
637
728
|
}), 0;
|
|
638
|
-
},
|
|
639
|
-
let t =
|
|
729
|
+
}, bt = () => "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.", xt = (e) => {
|
|
730
|
+
let t = U(e, "--tables");
|
|
640
731
|
return {
|
|
641
732
|
...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
642
733
|
...e.includes("--dry-run") ? { dryRun: !0 } : {}
|
|
643
734
|
};
|
|
644
|
-
},
|
|
735
|
+
}, St = (e) => ({
|
|
645
736
|
tables: e.tablesWritten,
|
|
646
737
|
rows: e.rowsWritten,
|
|
647
738
|
carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -649,28 +740,28 @@ var we = /* @__PURE__ */ new Set([
|
|
|
649
740
|
skippedTables: e.tablesSkipped,
|
|
650
741
|
skippedRows: e.rowsSkipped
|
|
651
742
|
} : {}
|
|
652
|
-
}),
|
|
743
|
+
}), Ct = (e, t) => {
|
|
653
744
|
if (e.includes("--dry-run")) {
|
|
654
|
-
|
|
745
|
+
V.info("import DRY RUN — nothing was written", t), V.info(bt());
|
|
655
746
|
return;
|
|
656
747
|
}
|
|
657
748
|
if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
|
|
658
|
-
|
|
749
|
+
V.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
|
|
659
750
|
return;
|
|
660
751
|
}
|
|
661
|
-
|
|
662
|
-
},
|
|
663
|
-
let t =
|
|
752
|
+
V.info("import complete", t);
|
|
753
|
+
}, wt = Symbol("refused"), Tt = (e) => {
|
|
754
|
+
let t = he(e) ?? e;
|
|
664
755
|
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
|
|
665
756
|
let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
|
|
666
|
-
|
|
757
|
+
V.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.`);
|
|
667
758
|
let o = Array.isArray(t.byTable) ? t.byTable : [];
|
|
668
|
-
o.length > 1 &&
|
|
669
|
-
for (let e of r)
|
|
670
|
-
n.length < i &&
|
|
759
|
+
o.length > 1 && V.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
|
|
760
|
+
for (let e of r) V.error(` ${e.table} ${e.id} ${e.reason}`);
|
|
761
|
+
n.length < i && V.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
|
|
671
762
|
let s = i - a;
|
|
672
|
-
return s > 0 &&
|
|
673
|
-
},
|
|
763
|
+
return s > 0 && V.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
|
|
764
|
+
}, Et = (e) => {
|
|
674
765
|
let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
|
|
675
766
|
try {
|
|
676
767
|
let e = JSON.parse(t), r = e.error ?? e.message;
|
|
@@ -710,22 +801,22 @@ var we = /* @__PURE__ */ new Set([
|
|
|
710
801
|
return;
|
|
711
802
|
}
|
|
712
803
|
}
|
|
713
|
-
},
|
|
714
|
-
let t =
|
|
715
|
-
if (!r && !(t && n)) return
|
|
716
|
-
let i =
|
|
804
|
+
}, Dt = async (e) => {
|
|
805
|
+
let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
|
|
806
|
+
if (!r && !(t && n)) return V.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;
|
|
807
|
+
let i = U(e, "--mode") ?? "upsert", o = U(e, "--on-conflict") ?? "skip";
|
|
717
808
|
if (t) {
|
|
718
|
-
let t =
|
|
719
|
-
if (!t) return
|
|
720
|
-
let a =
|
|
721
|
-
if (!a) return
|
|
722
|
-
let s =
|
|
723
|
-
i === "replace" && c === void 0 &&
|
|
809
|
+
let t = U(e, "--api-url");
|
|
810
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
811
|
+
let a = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
812
|
+
if (!a) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
813
|
+
let s = U(e, "--tables"), c = U(e, "--rollback-key");
|
|
814
|
+
i === "replace" && c === void 0 && V.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.");
|
|
724
815
|
let l = {
|
|
725
816
|
authorization: `Bearer ${a}`,
|
|
726
817
|
"x-import-mode": i,
|
|
727
818
|
"x-import-on-conflict": o,
|
|
728
|
-
...
|
|
819
|
+
...Q(e, i) ? { "x-import-atomic": "1" } : {},
|
|
729
820
|
...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
|
|
730
821
|
...c === void 0 ? {} : { "x-import-rollback-key": c },
|
|
731
822
|
...e.includes("--force") ? { "x-import-force": "1" } : {},
|
|
@@ -733,66 +824,66 @@ var we = /* @__PURE__ */ new Set([
|
|
|
733
824
|
...s ? { "x-import-tables": s } : {}
|
|
734
825
|
}, { resolve: p } = await import("node:path"), m;
|
|
735
826
|
if (n) l["content-type"] = "application/json", m = JSON.stringify({ bundleKey: n });
|
|
736
|
-
else if (
|
|
737
|
-
let t =
|
|
738
|
-
n && (t =
|
|
739
|
-
} else await (await import("node:fs/promises")).access(p(process.cwd(), r, "assets", "index.ndjson")).then(() => !0, () => !1) &&
|
|
827
|
+
else if (J(r)) {
|
|
828
|
+
let t = X(p(process.cwd(), r)), n = Y(e);
|
|
829
|
+
n && (t = j(t, n)), l["content-type"] = "application/octet-stream", m = t;
|
|
830
|
+
} else await (await import("node:fs/promises")).access(p(process.cwd(), r, "assets", "index.ndjson")).then(() => !0, () => !1) && V.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", m = xe({ dir: p(process.cwd(), r) });
|
|
740
831
|
if (i === "replace") {
|
|
741
|
-
let i = await
|
|
832
|
+
let i = await at(r, n);
|
|
742
833
|
{
|
|
743
|
-
let r = i === null ? { bundleKey: n } : { tables: i }, a = await
|
|
834
|
+
let r = i === null ? { bundleKey: n } : { tables: i }, a = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, JSON.stringify(r), {
|
|
744
835
|
...l,
|
|
745
836
|
"content-type": "application/json",
|
|
746
837
|
"x-import-preflight": "1"
|
|
747
|
-
},
|
|
748
|
-
o === null ?
|
|
838
|
+
}, $(e)).catch(() => null), o = a !== null && a.status === 200 ? JSON.parse(a.body.toString("utf8")) : null;
|
|
839
|
+
o === null ? V.warn("api replace: the instance did not answer the preflight (an older build does not have it). The run still works; the decision will only be visible in the pod log.") : o.staged === !0 ? V.info(`api replace: the instance WILL stage — ${o.stagedTables ?? 0} table(s) load into copies and the\n target keeps its rows until one short swap at the end. Interrupting the load leaves the target intact.`) : V.warn(`api replace: the instance will NOT stage — it empties the target first and refills it, so an
|
|
749
840
|
interrupted run leaves the target empty.\n Reason: ${o.reason ?? "not stated"}`);
|
|
750
841
|
}
|
|
751
842
|
}
|
|
752
|
-
i === "replace" && !
|
|
753
|
-
let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g =
|
|
843
|
+
i === "replace" && !Q(e, i) && V.warn(gt(!0));
|
|
844
|
+
let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g = ot(e), _;
|
|
754
845
|
try {
|
|
755
|
-
let t = typeof m == "string" ? null : await
|
|
846
|
+
let t = typeof m == "string" ? null : await ht({
|
|
756
847
|
stream: m,
|
|
757
848
|
url: h,
|
|
758
849
|
headers: l,
|
|
759
850
|
bundleDir: r,
|
|
760
851
|
args: e,
|
|
761
|
-
log:
|
|
762
|
-
}), n = t === null ? await
|
|
763
|
-
if (n.status < 200 || n.status >= 300) return
|
|
852
|
+
log: V
|
|
853
|
+
}), n = t === null ? await I(h, m, l, g.perRequest ?? $(e)) : t.kind === "sent" ? t.res : await I(h, t.body, l, g.perRequest ?? $(e));
|
|
854
|
+
if (n.status < 200 || n.status >= 300) return Tt(Et(n.body)) || V.error(`api import failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
|
|
764
855
|
_ = JSON.parse(n.body.toString("utf8"));
|
|
765
856
|
} catch (e) {
|
|
766
|
-
if (e instanceof
|
|
767
|
-
if (e instanceof
|
|
857
|
+
if (e instanceof F) return V.error(`api import: ${Ie(e, i)}`), 1;
|
|
858
|
+
if (e instanceof P) return V.error(`api import: ${L(e)}`), 1;
|
|
768
859
|
throw e;
|
|
769
860
|
}
|
|
770
|
-
let v = await
|
|
861
|
+
let v = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import/start`, JSON.stringify(_.bundleKey === void 0 ? { uploadId: _.uploadId } : { bundleKey: _.bundleKey }), {
|
|
771
862
|
...l,
|
|
772
863
|
"content-type": "application/json"
|
|
773
|
-
}, g.perRequest ??
|
|
774
|
-
if (e instanceof
|
|
864
|
+
}, g.perRequest ?? $(e)).catch((e) => {
|
|
865
|
+
if (e instanceof P) return V.error(`api import: ${L(e)}`), null;
|
|
775
866
|
throw e;
|
|
776
867
|
});
|
|
777
868
|
if (v === null) return 1;
|
|
778
|
-
if (v.status < 200 || v.status >= 300) return
|
|
869
|
+
if (v.status < 200 || v.status >= 300) return Tt(Et(v.body)) || V.error(`api import refused: HTTP ${v.status} — ${v.body.toString("utf8")}`), 1;
|
|
779
870
|
let y = JSON.parse(v.body.toString("utf8"));
|
|
780
|
-
if (y.alreadyStarted === !0 &&
|
|
781
|
-
if (y.recorded === !1) return
|
|
871
|
+
if (y.alreadyStarted === !0 && V.info(`api import: this bundle is already being imported as ${y.runId} — watching that run instead of starting a second one`), e.includes("--detach")) return V.info(`api import: started as ${y.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${Z} --target api --api-url ${t}`), 0;
|
|
872
|
+
if (y.recorded === !1) return V.warn(`api import: started as ${y.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next import is watchable.`), 0;
|
|
782
873
|
let b = await d({
|
|
783
|
-
read: () =>
|
|
874
|
+
read: () => st(t, a, y.runId),
|
|
784
875
|
onTick: (e) => {
|
|
785
|
-
|
|
876
|
+
V.info(f(e));
|
|
786
877
|
},
|
|
787
878
|
intervalMs: g.pollMs,
|
|
788
879
|
...g.total === void 0 ? {} : { timeoutMs: g.total }
|
|
789
880
|
}), x = u(b);
|
|
790
|
-
return x === 2 ? (
|
|
881
|
+
return x === 2 ? (V.warn(b === null ? `api import: started as ${y.runId}, but its record could not be READ back from this instance.\n That is a channel problem, not a slow import — an older instance without the history route, a
|
|
791
882
|
proxy dropping the GET, or a target with no history table. The run itself is unaffected and
|
|
792
|
-
is NOT cancelled; its outcome is simply not visible from here.` : `api import: still running after the time allowed. The run is NOT cancelled — it is decoupled from\n this client by design. Watch it with: voltro data ${
|
|
883
|
+
is NOT cancelled; its outcome is simply not visible from here.` : `api import: still running after the time allowed. The run is NOT cancelled — it is decoupled from\n this client by design. Watch it with: voltro data ${Z} --target api --api-url ${t}`), 2) : x === 1 ? (V.error(`api import FAILED (${y.runId}): ${String(b?.failure ?? "no reason recorded")}`), 1) : e.includes("--dry-run") ? (V.info("api import DRY RUN — nothing was written", {
|
|
793
884
|
via: t,
|
|
794
885
|
runId: y.runId
|
|
795
|
-
}),
|
|
886
|
+
}), V.info(bt()), 0) : (V.info("api import complete (imported by the running instance)", {
|
|
796
887
|
via: t,
|
|
797
888
|
runId: y.runId,
|
|
798
889
|
tables: b?.tablesWritten ?? 0,
|
|
@@ -800,74 +891,76 @@ var we = /* @__PURE__ */ new Set([
|
|
|
800
891
|
...b?.staged !== null && b?.staged !== void 0 ? { staged: b.staged } : {}
|
|
801
892
|
}), 0);
|
|
802
893
|
}
|
|
803
|
-
if (!r) return
|
|
804
|
-
if (!await
|
|
805
|
-
i === "replace" && e.includes("--allow-live") && !
|
|
806
|
-
let { store: s, snapshot: c } = await
|
|
807
|
-
c.tables.some((e) => e.name === y) ||
|
|
808
|
-
let l = e.includes("--force"), p = (e) =>
|
|
809
|
-
if (
|
|
894
|
+
if (!r) return V.error("a <bundleDir> is required for a direct import"), 1;
|
|
895
|
+
if (!await Qe(e)) return 1;
|
|
896
|
+
i === "replace" && e.includes("--allow-live") && !Q(e, i) && V.warn(gt(!1));
|
|
897
|
+
let { store: s, snapshot: c } = await K();
|
|
898
|
+
c.tables.some((e) => e.name === y) || V.warn(`this target has no \`${y}\` table, so this import will not be recorded.\n The import itself is unaffected. \`voltro db apply\` creates it.`);
|
|
899
|
+
let l = e.includes("--force"), p = (e) => V.warn(e), m = e.includes("--assets"), h = m ? D() : void 0;
|
|
900
|
+
if (m && !h) return V.error("--assets: this app has no storage provider configured, so there is nowhere to put the blobs.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
|
|
901
|
+
let g = G() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(a()) : void 0;
|
|
902
|
+
if (J(r)) {
|
|
810
903
|
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: a, join: u } = await import("node:path"), d = await n.mkdtemp(u(t.tmpdir(), "voltro-import-"));
|
|
811
904
|
try {
|
|
812
|
-
let t =
|
|
813
|
-
return n && (t =
|
|
905
|
+
let t = X(a(process.cwd(), r)), n = Y(e);
|
|
906
|
+
return n && (t = j(t, n)), await je(t, {
|
|
814
907
|
dir: d,
|
|
815
|
-
blobs:
|
|
816
|
-
}),
|
|
908
|
+
blobs: ke(h ?? fe())
|
|
909
|
+
}), Ct(e, {
|
|
817
910
|
file: r,
|
|
818
|
-
...
|
|
911
|
+
...St(await E.runPromise(Ee({
|
|
819
912
|
store: s,
|
|
820
913
|
bundleDir: d,
|
|
821
914
|
mode: i,
|
|
822
915
|
onConflict: o,
|
|
823
|
-
atomic:
|
|
824
|
-
...
|
|
916
|
+
atomic: Q(e, i),
|
|
917
|
+
...vt(e, i, r),
|
|
825
918
|
verify: !e.includes("--no-verify"),
|
|
826
|
-
targetDialect:
|
|
919
|
+
targetDialect: G(),
|
|
827
920
|
allowIncompatible: l,
|
|
828
921
|
targetSnapshot: c,
|
|
829
922
|
force: l,
|
|
830
923
|
onWarn: p,
|
|
831
|
-
onProgress:
|
|
924
|
+
onProgress: H,
|
|
832
925
|
assetsAlreadyRestored: !0,
|
|
833
|
-
...
|
|
834
|
-
...
|
|
926
|
+
...xt(e),
|
|
927
|
+
...g ? { copyLoader: g } : {}
|
|
835
928
|
})))
|
|
836
929
|
}), 0;
|
|
837
930
|
} finally {
|
|
838
|
-
await
|
|
931
|
+
await g?.close(), await n.rm(d, {
|
|
839
932
|
recursive: !0,
|
|
840
933
|
force: !0
|
|
841
934
|
});
|
|
842
935
|
}
|
|
843
936
|
}
|
|
844
|
-
let
|
|
845
|
-
...
|
|
937
|
+
let _ = await E.runPromise(Ee({
|
|
938
|
+
...vt(e, i, r),
|
|
846
939
|
store: s,
|
|
847
940
|
bundleDir: r,
|
|
848
941
|
mode: i,
|
|
849
942
|
onConflict: o,
|
|
850
|
-
atomic:
|
|
943
|
+
atomic: Q(e, i),
|
|
851
944
|
verify: !e.includes("--no-verify"),
|
|
852
|
-
targetDialect:
|
|
945
|
+
targetDialect: G(),
|
|
853
946
|
allowIncompatible: l,
|
|
854
947
|
targetSnapshot: c,
|
|
855
948
|
force: l,
|
|
856
949
|
onWarn: p,
|
|
857
|
-
onProgress:
|
|
858
|
-
...
|
|
859
|
-
...
|
|
860
|
-
...
|
|
950
|
+
onProgress: H,
|
|
951
|
+
...xt(e),
|
|
952
|
+
...h ? { assets: De(h) } : {},
|
|
953
|
+
...g ? { copyLoader: g } : {}
|
|
861
954
|
})).catch((e) => {
|
|
862
|
-
if (
|
|
955
|
+
if (Tt(e)) return wt;
|
|
863
956
|
throw e;
|
|
864
|
-
}).finally(() =>
|
|
865
|
-
return typeof
|
|
957
|
+
}).finally(() => g?.close());
|
|
958
|
+
return typeof _ == "symbol" ? 1 : (Ct(e, {
|
|
866
959
|
bundleDir: r,
|
|
867
|
-
...
|
|
868
|
-
assets:
|
|
960
|
+
...St(_),
|
|
961
|
+
assets: _.manifest.assets.count
|
|
869
962
|
}), 0);
|
|
870
|
-
},
|
|
963
|
+
}, Ot = () => {
|
|
871
964
|
let e = a();
|
|
872
965
|
return {
|
|
873
966
|
...e.url ? { url: e.url } : {},
|
|
@@ -877,75 +970,159 @@ var we = /* @__PURE__ */ new Set([
|
|
|
877
970
|
...e.password ? { password: e.password } : {},
|
|
878
971
|
...e.database ? { database: e.database } : {}
|
|
879
972
|
};
|
|
880
|
-
},
|
|
881
|
-
let t =
|
|
882
|
-
if (!t) return
|
|
883
|
-
let n =
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
973
|
+
}, kt = async (e) => {
|
|
974
|
+
let t = W(e);
|
|
975
|
+
if (!t) return V.error("usage: voltro data backup <outDir> [--assets] (native pg_dump/mariadb-dump/… + consistent artifact)"), 1;
|
|
976
|
+
let n = G(), r = e.includes("--assets"), { mkdir: i, writeFile: a } = await import("node:fs/promises"), { join: o } = await import("node:path"), s = await At(), c = r ? D() : void 0;
|
|
977
|
+
if (r && !c) return V.error("--assets: this app has no storage provider configured, so there are no blobs to capture.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the\n flag to take the rows-only dump."), 1;
|
|
978
|
+
await i(t, { recursive: !0 });
|
|
979
|
+
let l = o(t, A(n)), u = s ? await We(s.store, {
|
|
980
|
+
direction: "backup",
|
|
888
981
|
dialect: n,
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
});
|
|
893
|
-
|
|
982
|
+
artifact: l,
|
|
983
|
+
assets: r
|
|
984
|
+
}) : void 0, d = await Ke(N(_e(n, Ot(), l)));
|
|
985
|
+
if (d !== void 0) return s && u && await z(s.store, u, { failure: d }), V.error(`native backup failed: ${d}`), 1;
|
|
986
|
+
let f = await Xe(n), p = await Ye(), m = (/* @__PURE__ */ new Date()).toISOString(), h = async (e) => {
|
|
987
|
+
let r = Ve({
|
|
988
|
+
dialect: n,
|
|
989
|
+
schemaFingerprint: f,
|
|
990
|
+
voltroVersion: p,
|
|
991
|
+
createdAt: m,
|
|
992
|
+
assets: e
|
|
993
|
+
});
|
|
994
|
+
return await a(o(t, R), `${JSON.stringify(r, null, 2)}\n`, "utf8"), r;
|
|
995
|
+
}, g = 0, _ = 0, v = [];
|
|
996
|
+
if (c && s) {
|
|
997
|
+
let e = o(t, O);
|
|
998
|
+
await i(e, { recursive: !0 });
|
|
999
|
+
let n = o(t, k), r = s.store, a = await E.runPromiseExit(ve({
|
|
1000
|
+
source: Oe(c, () => q(r)),
|
|
1001
|
+
assetsDir: e,
|
|
1002
|
+
progress: {
|
|
1003
|
+
done: () => !1,
|
|
1004
|
+
mark: (e, t) => E.tryPromise({
|
|
1005
|
+
try: async () => {
|
|
1006
|
+
await (await import("node:fs/promises")).appendFile(n, `${JSON.stringify(t)}\n`);
|
|
1007
|
+
},
|
|
1008
|
+
catch: (e) => new me({
|
|
1009
|
+
reason: `asset index append failed: ${String(e)}`,
|
|
1010
|
+
path: n
|
|
1011
|
+
})
|
|
1012
|
+
})
|
|
1013
|
+
}
|
|
1014
|
+
}));
|
|
1015
|
+
if (a._tag === "Failure") {
|
|
1016
|
+
let e = T.pretty(a.cause);
|
|
1017
|
+
return u && await z(s.store, u, { failure: `assets: ${e}` }), await h(null).catch(() => void 0), V.error(`the rows were dumped to ${l}, but the asset capture failed: ${e}\n This directory holds a ROWS-ONLY backup. Re-run with --assets once the storage
|
|
1018
|
+
backend is reachable, or restore without --assets and accept the dangling refs.`), 1;
|
|
1019
|
+
}
|
|
1020
|
+
g = a.value.count, _ = a.value.totalBytes, v = a.value.missing, v.length > 0 && V.warn(`${v.length} of ${v.length + g} blob reference(s) point at objects the storage provider does not have; they are NOT in this backup and no restore can bring them back.
|
|
1021
|
+
e.g. ${v.slice(0, 3).map((e) => `'${e}'`).join(", ")}` + (v.length > 3 ? `, and ${v.length - 3} more` : ""));
|
|
1022
|
+
}
|
|
1023
|
+
let y = await h(r ? {
|
|
1024
|
+
count: g,
|
|
1025
|
+
totalBytes: _
|
|
1026
|
+
} : null);
|
|
1027
|
+
return s && u && await z(s.store, u, { assets: g }), V.info("native backup complete", {
|
|
894
1028
|
dialect: n,
|
|
895
|
-
artifact:
|
|
896
|
-
stamp:
|
|
897
|
-
schemaFingerprint:
|
|
898
|
-
assets: "NOT included — use `voltro data export --assets`
|
|
1029
|
+
artifact: l,
|
|
1030
|
+
stamp: R,
|
|
1031
|
+
schemaFingerprint: y.schemaFingerprint ?? "<none>",
|
|
1032
|
+
assets: r ? `${g} blob(s), ${_} byte(s) under ${O}/` + (v.length > 0 ? ` — ${v.length} reference(s) had no object and are NOT in here` : "") : "NOT included — re-run with --assets, or use `voltro data export --assets`",
|
|
1033
|
+
recorded: u?.recorded === !0 ? "voltro data transfers" : "NOT recorded (this target keeps no transfer history)"
|
|
899
1034
|
}), 0;
|
|
900
|
-
},
|
|
901
|
-
|
|
902
|
-
|
|
1035
|
+
}, At = async () => {
|
|
1036
|
+
try {
|
|
1037
|
+
let e = await r(process.cwd());
|
|
1038
|
+
return e ? { store: await i(e, void 0) } : void 0;
|
|
1039
|
+
} catch {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
}, jt = async (e, t) => {
|
|
1043
|
+
let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, A(n)), o = qe({
|
|
1044
|
+
flagUrl: U(t, "--drill-url"),
|
|
903
1045
|
envUrl: process.env.DRILL_DB_URL,
|
|
904
|
-
liveKeys:
|
|
1046
|
+
liveKeys: nt()
|
|
905
1047
|
});
|
|
906
|
-
if (!o.ok) return
|
|
907
|
-
let s = await i(r(e,
|
|
908
|
-
if (s && s.dialect !== n) return
|
|
909
|
-
|
|
1048
|
+
if (!o.ok) return V.error(`restore --drill: ${o.error}`), 1;
|
|
1049
|
+
let s = await i(r(e, R), "utf8").then(He, () => null);
|
|
1050
|
+
if (s && s.dialect !== n) return V.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
|
|
1051
|
+
V.info("restore drill: restoring into throwaway DB", {
|
|
910
1052
|
dialect: n,
|
|
911
|
-
target:
|
|
912
|
-
}), await
|
|
1053
|
+
target: B(o.url)
|
|
1054
|
+
}), await E.runPromise(N(we(n, { url: o.url }, a)));
|
|
913
1055
|
let c = null, l = 0;
|
|
914
1056
|
try {
|
|
915
|
-
let e = await
|
|
916
|
-
c =
|
|
1057
|
+
let e = await et(n, { url: o.url });
|
|
1058
|
+
c = le(e), l = e.tables.length;
|
|
917
1059
|
} catch (e) {
|
|
918
|
-
return
|
|
1060
|
+
return V.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
919
1061
|
}
|
|
920
|
-
let u =
|
|
1062
|
+
let u = Je({
|
|
921
1063
|
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
922
1064
|
restoredFingerprint: c,
|
|
923
1065
|
restoredTableCount: l
|
|
924
1066
|
});
|
|
925
|
-
for (let e of u.lines) u.pass ?
|
|
1067
|
+
for (let e of u.lines) u.pass ? V.info(`restore drill: ${e}`) : V.error(`restore drill: ${e}`);
|
|
926
1068
|
return +!u.pass;
|
|
927
|
-
},
|
|
928
|
-
let t =
|
|
929
|
-
if (!t) return
|
|
930
|
-
if (e.includes("--drill")) return
|
|
931
|
-
if (!await
|
|
932
|
-
let n =
|
|
933
|
-
stamp: await i(r(t,
|
|
1069
|
+
}, Mt = async (e) => {
|
|
1070
|
+
let t = W(e);
|
|
1071
|
+
if (!t) return V.error("usage: voltro data restore <backupDir> [--assets] [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mariadb/…)"), 1;
|
|
1072
|
+
if (e.includes("--drill")) return jt(t, e);
|
|
1073
|
+
if (!await Qe(e)) return 1;
|
|
1074
|
+
let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, A(n)), o = e.includes("--assets"), s = Ue({
|
|
1075
|
+
stamp: await i(r(t, R), "utf8").then(He, () => null),
|
|
934
1076
|
currentDialect: n,
|
|
935
|
-
currentFingerprint: await
|
|
1077
|
+
currentFingerprint: await Xe(n)
|
|
936
1078
|
});
|
|
937
|
-
for (let e of
|
|
938
|
-
if (
|
|
939
|
-
if (!e.includes("--force")) return
|
|
940
|
-
|
|
1079
|
+
for (let e of s.info) V.info(`backup stamp: ${e}`);
|
|
1080
|
+
if (s.warn && V.warn(s.warn), s.refuse) {
|
|
1081
|
+
if (!e.includes("--force")) return V.error(`restore refused: ${s.refuse} (pass --force to override)`), 1;
|
|
1082
|
+
V.warn(`restore proceeding despite: ${s.refuse} (--force)`);
|
|
941
1083
|
}
|
|
942
|
-
|
|
1084
|
+
let c = r(t, k), l = await i(c, "utf8").then(() => !0, () => !1);
|
|
1085
|
+
if (o && !l) return V.error(`--assets: this backup carries no assets — there is no ${k} in ${t}.\n It was taken without --assets, so its rows will reference blobs this restore cannot
|
|
1086
|
+
put back. Restore from a backup taken with --assets, or drop the flag to accept that.`), 1;
|
|
1087
|
+
!o && l && V.warn("this backup CARRIES assets and --assets was not passed: the rows will be restored and the blobs will not. Pass --assets to restore both.");
|
|
1088
|
+
let u = await At(), d = o ? D() : void 0;
|
|
1089
|
+
if (o && !d) return V.error("--assets: this app has no storage provider configured, so there is nowhere to put the blobs.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
|
|
1090
|
+
let f = u ? await Ge(u.store, { artifact: a }) : void 0;
|
|
1091
|
+
if (u && f === void 0) return V.error(`refusing to restore: could not write the in-progress marker.
|
|
1092
|
+
It is one row in \`${b}\`, written before the first destructive\n statement so a killed restore refuses the next boot rather than serving a half-loaded
|
|
1093
|
+
database. Run \`voltro db apply\` so the table exists, then retry.`), 1;
|
|
1094
|
+
let p = u ? await We(u.store, {
|
|
1095
|
+
direction: "restore",
|
|
943
1096
|
dialect: n,
|
|
944
|
-
artifact: a
|
|
1097
|
+
artifact: a,
|
|
1098
|
+
assets: o
|
|
1099
|
+
}) : void 0, m = await Ke(N(we(n, Ot(), a))), h = f === void 0 ? "held" : await f.reassert();
|
|
1100
|
+
if (h === "rewritten" ? V.warn(`the restore artefact removed the in-progress marker (it carries \`${b}\`) — it has been written back.\n Backups taken from this version on exclude that table, so this only affects older
|
|
1101
|
+
and hand-made dumps.`) : h === "lost" && V.error(`the restore artefact removed the in-progress marker and it could NOT be written back.\n \`${b}\` is unwritable, so if this restore does not finish,\n nothing will stop the next boot from serving a half-loaded database. Check the
|
|
1102
|
+
result of this command before you start the app.`), m !== void 0) return u && p && await z(u.store, p, { failure: m }), V.error(`native restore failed: ${m}\n` + (h === "lost" ? " This database is now in an unknown state and NOTHING WILL STOP THE NEXT BOOT — see above.\n" : " This database is now in an unknown state and the next boot will REFUSE, by design.\n") + " Re-run the restore to completion, or clear the marker deliberately with\n `voltro data clear-replace-marker --yes` once you have decided the state is correct."), 1;
|
|
1103
|
+
let g = 0;
|
|
1104
|
+
if (d) {
|
|
1105
|
+
let e = await E.runPromiseExit(Ce({
|
|
1106
|
+
sink: De(d),
|
|
1107
|
+
assetsDir: r(t, O),
|
|
1108
|
+
indexPath: c
|
|
1109
|
+
}));
|
|
1110
|
+
if (e._tag === "Failure") {
|
|
1111
|
+
let t = T.pretty(e.cause);
|
|
1112
|
+
return u && p && await z(u.store, p, { failure: `assets: ${t}` }), V.error(`the rows were restored, but the asset restore failed: ${t}\n The in-progress marker is still set and the next boot will refuse. Re-run
|
|
1113
|
+
\`voltro data restore <dir> --assets\` once storage is reachable.`), 1;
|
|
1114
|
+
}
|
|
1115
|
+
g = e.value;
|
|
1116
|
+
}
|
|
1117
|
+
return f && await f.clear(), u && p && await z(u.store, p, { assets: g }), V.info("native restore complete", {
|
|
1118
|
+
dialect: n,
|
|
1119
|
+
artifact: a,
|
|
1120
|
+
assets: o ? `${g} blob(s) restored` : "not restored (no --assets)",
|
|
1121
|
+
recorded: p?.recorded === !0 ? "voltro data transfers" : "NOT recorded (this target keeps no transfer history)"
|
|
945
1122
|
}), 0;
|
|
946
|
-
},
|
|
947
|
-
let t =
|
|
948
|
-
if (!t) return
|
|
1123
|
+
}, Nt = async (e) => {
|
|
1124
|
+
let t = W(e);
|
|
1125
|
+
if (!t) return V.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
|
|
949
1126
|
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
|
|
950
1127
|
if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
|
|
951
1128
|
let n = r(a, "manifest.json"), o = r(a, "README.md");
|
|
@@ -954,24 +1131,24 @@ var we = /* @__PURE__ */ new Set([
|
|
|
954
1131
|
if (e !== void 0) return console.log(e), 0;
|
|
955
1132
|
}
|
|
956
1133
|
let s = await i.readFile(n, "utf8").catch(() => void 0);
|
|
957
|
-
return s === void 0 ? (
|
|
1134
|
+
return s === void 0 ? (V.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);
|
|
958
1135
|
}
|
|
959
|
-
let o =
|
|
960
|
-
let e =
|
|
961
|
-
return o ?
|
|
1136
|
+
let o = Y(e), s = () => {
|
|
1137
|
+
let e = X(a);
|
|
1138
|
+
return o ? j(e, o) : e;
|
|
962
1139
|
};
|
|
963
1140
|
if (e.includes("--json")) {
|
|
964
|
-
let e = (await
|
|
965
|
-
return e ? (console.log(e.toString("utf8")), 0) : (
|
|
1141
|
+
let e = (await M(s(), ["manifest.json"])).get("manifest.json");
|
|
1142
|
+
return e ? (console.log(e.toString("utf8")), 0) : (V.error("no manifest.json found in the bundle"), 1);
|
|
966
1143
|
}
|
|
967
|
-
let c = (await
|
|
1144
|
+
let c = (await M(s(), ["README.md"])).get("README.md");
|
|
968
1145
|
if (c) return console.log(c.toString("utf8")), 0;
|
|
969
|
-
let l = (await
|
|
970
|
-
return l ? (console.log(l.toString("utf8")), 0) : (
|
|
971
|
-
},
|
|
972
|
-
let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-
|
|
973
|
-
if (i === void 0) return
|
|
974
|
-
if (!e.includes("--yes")) return
|
|
1146
|
+
let l = (await M(s(), ["manifest.json"])).get("manifest.json");
|
|
1147
|
+
return l ? (console.log(l.toString("utf8")), 0) : (V.error("bundle has no README.md or manifest.json"), 1);
|
|
1148
|
+
}, Pt = async (e) => {
|
|
1149
|
+
let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-CvmiAM9K.js"), { store: r } = await K(), i = await n(r);
|
|
1150
|
+
if (i === void 0) return V.info("no interrupted replace is recorded — nothing to clear"), 0;
|
|
1151
|
+
if (!e.includes("--yes")) return V.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
|
|
975
1152
|
let a = await r.query({
|
|
976
1153
|
table: t,
|
|
977
1154
|
predicate: void 0,
|
|
@@ -981,30 +1158,30 @@ var we = /* @__PURE__ */ new Set([
|
|
|
981
1158
|
projection: void 0
|
|
982
1159
|
});
|
|
983
1160
|
for (let e of a) await r.delete(t, String(e.id));
|
|
984
|
-
return
|
|
985
|
-
},
|
|
986
|
-
let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await
|
|
987
|
-
if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return
|
|
1161
|
+
return V.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
|
|
1162
|
+
}, Ft = async (e) => {
|
|
1163
|
+
let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await K(), a = G();
|
|
1164
|
+
if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return V.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
|
|
988
1165
|
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));
|
|
989
|
-
return s.length === 0 ? (
|
|
990
|
-
},
|
|
991
|
-
let { describeTransferRun: t, DATA_TRANSFERS_TABLE: n } = await import("@voltro/database"), r = Number(
|
|
992
|
-
if (
|
|
993
|
-
let t =
|
|
994
|
-
if (!t) return
|
|
995
|
-
let n =
|
|
996
|
-
if (!n) return
|
|
997
|
-
let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/
|
|
998
|
-
if (r.status !== 200) return
|
|
1166
|
+
return s.length === 0 ? (V.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))), V.info(`dropped ${s.length} staging table(s)`), 0) : (V.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);
|
|
1167
|
+
}, It = async (e) => {
|
|
1168
|
+
let { describeTransferRun: t, DATA_TRANSFERS_TABLE: n } = await import("@voltro/database"), r = Number(U(e, "--limit") ?? "20"), i = Number.isFinite(r) ? Math.max(1, Math.min(200, Math.trunc(r))) : 20;
|
|
1169
|
+
if (U(e, "--target") === "api") {
|
|
1170
|
+
let t = U(e, "--api-url");
|
|
1171
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
1172
|
+
let n = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
1173
|
+
if (!n) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET)"), 1;
|
|
1174
|
+
let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=${i}`, { headers: { authorization: `Bearer ${n}` } }), a = await r.text();
|
|
1175
|
+
if (r.status !== 200) return V.error(`api transfers: ${r.status} ${a}`), 1;
|
|
999
1176
|
let o = JSON.parse(a);
|
|
1000
1177
|
if (e.includes("--json")) return console.log(a), 0;
|
|
1001
1178
|
let s = o.runs ?? [];
|
|
1002
|
-
if (s.length === 0) return
|
|
1003
|
-
(o.running ?? 0) > 0 &&
|
|
1004
|
-
for (let e of s)
|
|
1179
|
+
if (s.length === 0) return V.info("no data transfers recorded on this instance"), 0;
|
|
1180
|
+
(o.running ?? 0) > 0 && V.info(`${o.running} transfer(s) IN FLIGHT — re-run this to watch the counters move`);
|
|
1181
|
+
for (let e of s) V.info(` ${e.line ?? ""}`);
|
|
1005
1182
|
return 0;
|
|
1006
1183
|
}
|
|
1007
|
-
let { store: a } = await
|
|
1184
|
+
let { store: a } = await K(), o;
|
|
1008
1185
|
try {
|
|
1009
1186
|
o = await a.query({
|
|
1010
1187
|
table: n,
|
|
@@ -1018,27 +1195,27 @@ var we = /* @__PURE__ */ new Set([
|
|
|
1018
1195
|
take: i
|
|
1019
1196
|
});
|
|
1020
1197
|
} catch {
|
|
1021
|
-
return
|
|
1198
|
+
return V.error(`this target has no \`${n}\` table, so no transfer has been recorded here.\n Run \`voltro db apply\`; the transfers themselves are unaffected by its absence.`), 1;
|
|
1022
1199
|
}
|
|
1023
|
-
if (o.length === 0) return
|
|
1200
|
+
if (o.length === 0) return V.info("no data transfers recorded against this target"), 0;
|
|
1024
1201
|
for (let e of o) {
|
|
1025
1202
|
let n = e;
|
|
1026
|
-
|
|
1203
|
+
V.info(` ${t({
|
|
1027
1204
|
...e,
|
|
1028
1205
|
startedAt: new Date(n.startedAt),
|
|
1029
1206
|
finishedAt: n.finishedAt === null ? null : new Date(n.finishedAt)
|
|
1030
1207
|
})}`);
|
|
1031
1208
|
}
|
|
1032
1209
|
return 0;
|
|
1033
|
-
},
|
|
1034
|
-
let t =
|
|
1035
|
-
if (!t || !n) return
|
|
1036
|
-
let { resolve: r } = await import("node:path"), i =
|
|
1037
|
-
return a && (i =
|
|
1210
|
+
}, Lt = async (e) => {
|
|
1211
|
+
let t = W(e), n = W(e.filter((e) => e !== t));
|
|
1212
|
+
if (!t || !n) return V.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
|
|
1213
|
+
let { resolve: r } = await import("node:path"), i = X(r(process.cwd(), t)), a = Y(e);
|
|
1214
|
+
return a && (i = j(i, a)), await Me(i, r(process.cwd(), n)), V.info("unpack complete", {
|
|
1038
1215
|
file: t,
|
|
1039
1216
|
dir: n
|
|
1040
1217
|
}), 0;
|
|
1041
|
-
},
|
|
1218
|
+
}, Rt = {
|
|
1042
1219
|
export: {
|
|
1043
1220
|
common: [
|
|
1044
1221
|
"--target",
|
|
@@ -1111,46 +1288,47 @@ var we = /* @__PURE__ */ new Set([
|
|
|
1111
1288
|
backup: { common: ["--json", "--assets"] },
|
|
1112
1289
|
restore: { common: [
|
|
1113
1290
|
"--json",
|
|
1291
|
+
"--assets",
|
|
1114
1292
|
"--allow-live",
|
|
1115
1293
|
"--force",
|
|
1116
1294
|
"--drill",
|
|
1117
1295
|
"--drill-url",
|
|
1118
1296
|
"--api-url"
|
|
1119
1297
|
] }
|
|
1120
|
-
},
|
|
1121
|
-
let n =
|
|
1298
|
+
}, zt = (e) => e.split("=")[0], Bt = (e, t) => {
|
|
1299
|
+
let n = Rt[e];
|
|
1122
1300
|
if (!n) return;
|
|
1123
|
-
let r =
|
|
1301
|
+
let r = U(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 ?? []);
|
|
1124
1302
|
for (let n of t) {
|
|
1125
1303
|
if (!n.startsWith("--")) continue;
|
|
1126
|
-
let t =
|
|
1304
|
+
let t = zt(n);
|
|
1127
1305
|
if (i.has(t)) continue;
|
|
1128
1306
|
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.`;
|
|
1129
|
-
let o = Object.entries(
|
|
1307
|
+
let o = Object.entries(Rt).filter(([n, r]) => n !== e && [
|
|
1130
1308
|
...r.common,
|
|
1131
1309
|
...r.directOnly ?? [],
|
|
1132
1310
|
...r.apiOnly ?? []
|
|
1133
1311
|
].includes(t)).map(([e]) => e);
|
|
1134
1312
|
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.`;
|
|
1135
1313
|
}
|
|
1136
|
-
},
|
|
1137
|
-
export:
|
|
1138
|
-
import:
|
|
1139
|
-
transfers:
|
|
1140
|
-
inspect:
|
|
1141
|
-
unpack:
|
|
1142
|
-
backup:
|
|
1143
|
-
restore:
|
|
1144
|
-
"clear-replace-marker":
|
|
1145
|
-
"clear-staging":
|
|
1146
|
-
},
|
|
1314
|
+
}, Vt = {
|
|
1315
|
+
export: yt,
|
|
1316
|
+
import: Dt,
|
|
1317
|
+
transfers: It,
|
|
1318
|
+
inspect: Nt,
|
|
1319
|
+
unpack: Lt,
|
|
1320
|
+
backup: kt,
|
|
1321
|
+
restore: Mt,
|
|
1322
|
+
"clear-replace-marker": Pt,
|
|
1323
|
+
"clear-staging": Ft
|
|
1324
|
+
}, Ht = async (n) => {
|
|
1147
1325
|
let [r, ...i] = n;
|
|
1148
|
-
if (r !== void 0 &&
|
|
1149
|
-
let e =
|
|
1150
|
-
if (e) return
|
|
1326
|
+
if (r !== void 0 && Rt[r] !== void 0) {
|
|
1327
|
+
let e = Bt(r, i);
|
|
1328
|
+
if (e) return V.error(e), 2;
|
|
1151
1329
|
}
|
|
1152
|
-
let a = r === void 0 ? void 0 :
|
|
1153
|
-
return a === void 0 ? (
|
|
1330
|
+
let a = r === void 0 ? void 0 : Vt[r];
|
|
1331
|
+
return a === void 0 ? (V.error(`usage: voltro data ${e(t)} … (logical: export/import; native: backup/restore; \`imports\` reads the history, incl. a run in flight)`), r ? 2 : 1) : a(i);
|
|
1154
1332
|
};
|
|
1155
1333
|
//#endregion
|
|
1156
|
-
export {
|
|
1334
|
+
export { Ht as runDataCommand };
|