@voltro/cli 0.50.0 → 0.51.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 +160 -0
- package/dist/{apiBuild-CFYXo06U.js → apiBuild-CPDHXF72.js} +2 -2
- package/dist/apiBuild-GDKuGOMV.js +2 -0
- package/dist/appGraph-CFmFKHb2.js +2 -0
- package/dist/appGraph-yRuKjOba.js +111 -0
- package/dist/bin.js +1 -1
- package/dist/{build-CnSH-zru.js → build-DETLZAFt.js} +271 -240
- package/dist/{checkCommand-OL41BMIb.js → checkCommand-CWcnDArJ.js} +1 -1
- package/dist/{checkCommand-Do3SQbLx.js → checkCommand-DNuPiWMc.js} +56 -46
- package/dist/{codegenCommand-BJzaRz6q.js → codegenCommand-BNBHcNNj.js} +2 -2
- package/dist/{codemodRunner-De3k0pRd.js → codemodRunner-BDVixlSw.js} +278 -136
- package/dist/{commands-By23Ljny.js → commands-B1OiS9bX.js} +24 -23
- package/dist/{dashboardCommand-DN1rAABE.js → dashboardCommand-C-vvPY1B.js} +5 -5
- package/dist/{dataCommand-B93TUBIx.js → dataCommand-C1GxXW5q.js} +458 -384
- package/dist/dbCommand-C27HIsGE.js +2 -0
- package/dist/{dbCommand-Bf7Cy0mn.js → dbCommand-If4Y1xQ-.js} +1 -1
- package/dist/{dev-BWwE0QjK.js → dev-CK522MV5.js} +1 -1
- package/dist/{dev-1yu42ubu.js → dev-kdAg9Q7l.js} +1246 -1246
- package/dist/doctorCommand-BK4l18eG.js +2 -0
- package/dist/{doctorCommand-y-EbP8st.js → doctorCommand-nKmeW78u.js} +42 -26
- package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
- package/dist/{dormancyCommand-Dc7KRNCG.js → dormancyCommand-CY3wa_SW.js} +1 -1
- package/dist/{embeddingsCommand-kPs132bB.js → embeddingsCommand-BDLIgje_.js} +1 -1
- package/dist/{envCommand-E12zSt51.js → envCommand-C6V_xVlT.js} +13 -13
- package/dist/{evolveCommand-B3nagPVc.js → evolveCommand-D3c4DSfN.js} +2 -2
- package/dist/{generateCommand-CcyvH2ve.js → generateCommand-DTNPGjxo.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-Cq0Yz98u.js → infoCommand-BnRFEF1o.js} +1 -1
- package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
- package/dist/inspect-CuGDYES0.js +2 -0
- package/dist/manifestBuild-CPjhvM62.js +2 -0
- package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
- package/dist/{migrate-D3MK9BpK.js → migrate-DehuBakM.js} +1 -1
- package/dist/{runtimeTrace-BpLUcrfy.js → runtimeTrace-ZsBU7Tkx.js} +1 -1
- package/dist/{sdkgen-dl4-WKF6.js → sdkgen-O4XqWOjM.js} +1 -1
- package/dist/serveCommand-BRnPCxVd.js +2 -0
- package/dist/{serveCommand-8CTzQCY0.js → serveCommand-DdiYNBBu.js} +4 -4
- package/dist/serveEntry.js +1 -1
- package/dist/{start-7HcSIKEp.js → start-BLNmWkLa.js} +331 -327
- package/dist/{start-DvlYDyAr.js → start-Dzicuyw8.js} +1 -1
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-i8JbrLAA.js → updateCommand-Bs322Q78.js} +1 -1
- package/dist/updateCommand-eXB35SEv.js +2 -0
- package/dist/{webDev-B4IEMB0n.js → webDev-B-ubQEMX.js} +283 -224
- package/dist/webDev-DposiF3j.js +2 -0
- package/dist/{webhooksCommand-B0bdyuQ4.js → webhooksCommand-FLYY9IXh.js} +1 -1
- package/package.json +41 -17
- package/templates/AGENTS.core.md +15 -5
- package/templates/AGENTS.md +16 -6
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +92 -3
- package/templates/agent-docs/data.md +36 -0
- package/templates/agent-docs/internationalization.md +131 -13
- package/templates/agent-docs/whats-new.md +73 -27
- 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-BWo8KI6n.js +0 -2
- package/dist/appGraph-KGDPTuTy.js +0 -110
- package/dist/appGraph-zuMGKVYX.js +0 -2
- package/dist/dbCommand-Bnl7MSGC.js +0 -2
- package/dist/doctorCommand-BmovAwb7.js +0 -2
- package/dist/inspect-DUze25t0.js +0 -2
- package/dist/manifestBuild-j0n109tt.js +0 -2
- package/dist/serveCommand-C_WI4TTG.js +0 -2
- package/dist/updateCommand-D91-TZ7E.js +0 -2
- package/dist/webDev-B79ji8M5.js +0 -2
|
@@ -1,20 +1,20 @@
|
|
|
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-kdAg9Q7l.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";
|
|
7
7
|
import { o as m, t as h } from "./inspectFetch-EMuhTG_9.js";
|
|
8
|
-
import { r as g } from "./dbCommand-
|
|
8
|
+
import { r as g } from "./dbCommand-If4Y1xQ-.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, REPLACE_IN_PROGRESS_TABLE as b, _voltroDataTransfersTable as x, _voltroReplaceInProgressTable as S, connectionIdentity as C, derivedRowId as
|
|
12
|
-
import { Cause as
|
|
13
|
-
import { SqlClient as
|
|
14
|
-
import { declaredSnapshot as
|
|
15
|
-
import { STORAGE_REFS_TABLE as
|
|
16
|
-
import { createHash as
|
|
17
|
-
import { ASSETS_DIR 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 w, ensureTableRegistered as T, normaliseTarget as ee, sameDatabase as te, streamTable as ne } from "@voltro/database";
|
|
12
|
+
import { Cause as E, Effect as D, Exit as re, Option as ie, Stream as ae } from "effect";
|
|
13
|
+
import { SqlClient as oe } from "@effect/sql";
|
|
14
|
+
import { declaredSnapshot as se, fingerprintSchema as ce, introspectSchema as le } from "@voltro/database/sql";
|
|
15
|
+
import { STORAGE_REFS_TABLE as ue, appStorageProvider as de, configuredStorageProvider as O } from "@voltro/plugin-storage";
|
|
16
|
+
import { createHash as fe } from "node:crypto";
|
|
17
|
+
import { ASSETS_DIR as k, ASSET_INDEX_FILE as A, BundleError as pe, asImportError as me, assessLiveImport as he, backupArtifactName as j, backupCommand as ge, captureAssetsToCas as _e, classificationFromSnapshot as ve, decryptStream as M, encryptStream as ye, packBundle as be, peekBundle as N, previewMasking as xe, restoreAssetsFromCas as Se, restoreCommand as Ce, runExport as we, runImport as Te, runNativeStep as Ee, storageAssetSink as De, storageAssetSource as Oe, storageBlobSink as ke, storageBlobSource as Ae, unpackBundle as je, unpackBundleToDir as Me } from "@voltro/data-transfer";
|
|
18
18
|
import { request as Ne } from "node:http";
|
|
19
19
|
import { request as Pe } from "node:https";
|
|
20
20
|
//#region src/longRequest.ts
|
|
@@ -26,7 +26,7 @@ var Fe = /* @__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,13 +39,13 @@ var Fe = /* @__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
|
-
},
|
|
48
|
+
}, I = async (e, t, n, r) => {
|
|
49
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({
|
|
@@ -64,8 +64,8 @@ var Fe = /* @__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(Fe.has(t.code ?? "") ? new
|
|
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 Fe = /* @__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,7 +100,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
100
100
|
i += a.byteLength, yield s;
|
|
101
101
|
}
|
|
102
102
|
yield* a(!0);
|
|
103
|
-
}, ze = (e) =>
|
|
103
|
+
}, ze = (e) => fe("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: {}
|
|
@@ -131,7 +131,7 @@ var Fe = /* @__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,
|
|
@@ -150,9 +150,15 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
150
150
|
if (n.stampVersion !== 1 || typeof n.dialect != "string" || typeof n.voltroVersion != "string" || typeof n.createdAt != "string") return null;
|
|
151
151
|
let r = n.schemaFingerprint;
|
|
152
152
|
if (r !== null && typeof r != "string") return null;
|
|
153
|
-
let i = n.assets, a =
|
|
153
|
+
let i = n.assets, a = (e) => {
|
|
154
|
+
let t = i?.[e];
|
|
155
|
+
return typeof t == "number" ? t : void 0;
|
|
156
|
+
}, o = typeof i == "object" && i && typeof i.count == "number" && typeof i.totalBytes == "number" ? {
|
|
154
157
|
count: i.count,
|
|
155
|
-
totalBytes: i.totalBytes
|
|
158
|
+
totalBytes: i.totalBytes,
|
|
159
|
+
...a("references") === void 0 ? {} : { references: a("references") },
|
|
160
|
+
...a("objects") === void 0 ? {} : { objects: a("objects") },
|
|
161
|
+
...a("objectBytes") === void 0 ? {} : { objectBytes: a("objectBytes") }
|
|
156
162
|
} : null;
|
|
157
163
|
return {
|
|
158
164
|
stampVersion: 1,
|
|
@@ -160,85 +166,121 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
160
166
|
schemaFingerprint: r,
|
|
161
167
|
voltroVersion: n.voltroVersion,
|
|
162
168
|
createdAt: n.createdAt,
|
|
163
|
-
assets:
|
|
169
|
+
assets: o
|
|
164
170
|
};
|
|
165
171
|
}, Ue = (e) => {
|
|
166
|
-
let { stamp: t, currentDialect: n,
|
|
172
|
+
let { stamp: t, currentDialect: n, targetFingerprint: r } = e;
|
|
167
173
|
if (t === null) return {
|
|
168
|
-
warn: `no ${
|
|
174
|
+
warn: `no ${R} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
169
175
|
info: []
|
|
170
176
|
};
|
|
171
177
|
let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
|
|
172
178
|
return t.dialect === n ? t.schemaFingerprint !== null && r !== null && t.schemaFingerprint !== r ? {
|
|
173
|
-
warn: `schema skew — the backup's schema (${t.schemaFingerprint.slice(0, 12)}) differs from
|
|
179
|
+
warn: `schema skew — the backup's schema (${t.schemaFingerprint.slice(0, 12)}) differs from the schema this TARGET database currently has (${r.slice(0, 12)}). This compares the backup against the target's present state, not against what your code declares. The restore replaces the target's schema with the backup's; run \`voltro db apply\` afterwards to bring it to what the code declares.`,
|
|
174
180
|
info: i
|
|
175
181
|
} : { info: i } : {
|
|
176
182
|
refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
|
|
177
183
|
info: i
|
|
178
184
|
};
|
|
179
185
|
}, We = async (e, t) => {
|
|
180
|
-
let n =
|
|
186
|
+
let n = w("dimp", `${t.direction}-${t.artifact}-${Date.now()}-${Math.random()}`), r = {
|
|
187
|
+
id: n,
|
|
188
|
+
direction: t.direction,
|
|
189
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
190
|
+
finishedAt: null,
|
|
191
|
+
failure: null,
|
|
192
|
+
mode: `native ${t.dialect}${t.assets ? " + assets" : ""}`,
|
|
193
|
+
via: "cli",
|
|
194
|
+
bundle: t.artifact,
|
|
195
|
+
sourceFingerprint: null,
|
|
196
|
+
tablesWritten: 0,
|
|
197
|
+
rowsWritten: 0,
|
|
198
|
+
staged: null
|
|
199
|
+
};
|
|
181
200
|
try {
|
|
182
|
-
return
|
|
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
|
-
}), {
|
|
201
|
+
return T(x), await e.insert(y, r), {
|
|
196
202
|
runId: n,
|
|
197
|
-
recorded: !0
|
|
203
|
+
recorded: !0,
|
|
204
|
+
opened: r
|
|
198
205
|
};
|
|
199
206
|
} catch {
|
|
200
207
|
return {
|
|
201
208
|
runId: n,
|
|
202
|
-
recorded: !1
|
|
209
|
+
recorded: !1,
|
|
210
|
+
opened: r
|
|
203
211
|
};
|
|
204
212
|
}
|
|
205
|
-
},
|
|
206
|
-
|
|
213
|
+
}, z = async (e, t, n) => {
|
|
214
|
+
if (!t.recorded) return;
|
|
215
|
+
let r = {
|
|
207
216
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
208
217
|
failure: n.failure ?? null,
|
|
209
218
|
...n.assets === void 0 ? {} : { rowsWritten: n.assets }
|
|
210
|
-
}
|
|
219
|
+
};
|
|
220
|
+
if (await e.update(y, t.runId, r).then(() => void 0, () => void 0), !await e.query({
|
|
221
|
+
table: y,
|
|
222
|
+
predicate: void 0,
|
|
223
|
+
order: [],
|
|
224
|
+
take: void 0,
|
|
225
|
+
skip: void 0,
|
|
226
|
+
projection: void 0
|
|
227
|
+
}).then((e) => e.some((e) => String(e.id ?? "") === t.runId), () => !0)) try {
|
|
228
|
+
T(x), await e.insert(y, {
|
|
229
|
+
...t.opened,
|
|
230
|
+
...r
|
|
231
|
+
});
|
|
232
|
+
} catch {}
|
|
211
233
|
}, Ge = async (e, t) => {
|
|
212
|
-
let n =
|
|
234
|
+
let n = w("repl", `restore-${t.artifact}-${Date.now()}-${Math.random()}`), r = {
|
|
235
|
+
id: n,
|
|
236
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
237
|
+
kind: "restore",
|
|
238
|
+
tables: null,
|
|
239
|
+
capture: t.artifact,
|
|
240
|
+
via: "cli"
|
|
241
|
+
};
|
|
213
242
|
try {
|
|
214
|
-
|
|
215
|
-
id: n,
|
|
216
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
217
|
-
kind: "restore",
|
|
218
|
-
tables: null,
|
|
219
|
-
capture: t.artifact,
|
|
220
|
-
via: "cli"
|
|
221
|
-
});
|
|
243
|
+
T(S), await e.insert(b, r);
|
|
222
244
|
} catch {
|
|
223
245
|
return;
|
|
224
246
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
247
|
+
let i = async () => (await e.query({
|
|
248
|
+
table: b,
|
|
249
|
+
predicate: void 0,
|
|
250
|
+
order: [],
|
|
251
|
+
take: void 0,
|
|
252
|
+
skip: void 0,
|
|
253
|
+
projection: void 0
|
|
254
|
+
})).some((e) => String(e.id ?? "") === n);
|
|
255
|
+
return {
|
|
256
|
+
reassert: async () => {
|
|
257
|
+
try {
|
|
258
|
+
if (await i()) return "held";
|
|
259
|
+
} catch {}
|
|
260
|
+
try {
|
|
261
|
+
return T(S), await e.insert(b, r), "rewritten";
|
|
262
|
+
} catch {
|
|
263
|
+
return "lost";
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
clear: async () => {
|
|
267
|
+
await e.deleteMany(b, { where: {
|
|
268
|
+
column: "id",
|
|
269
|
+
op: "eq",
|
|
270
|
+
value: n
|
|
271
|
+
} }).then(() => void 0, () => void 0);
|
|
272
|
+
}
|
|
231
273
|
};
|
|
232
274
|
}, Ke = async (e) => {
|
|
233
|
-
let t = await
|
|
234
|
-
if (
|
|
235
|
-
let n =
|
|
236
|
-
if (
|
|
275
|
+
let t = await D.runPromiseExit(e);
|
|
276
|
+
if (re.isSuccess(t)) return;
|
|
277
|
+
let n = E.failureOption(t.cause);
|
|
278
|
+
if (ie.isSome(n)) {
|
|
237
279
|
let e = n.value;
|
|
238
280
|
return String(e?.message ?? e);
|
|
239
281
|
}
|
|
240
|
-
return
|
|
241
|
-
},
|
|
282
|
+
return E.pretty(t.cause);
|
|
283
|
+
}, B = (e) => {
|
|
242
284
|
try {
|
|
243
285
|
let t = new URL(e), n = t.pathname.replace(/^\//, "");
|
|
244
286
|
return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
|
|
@@ -251,7 +293,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
251
293
|
ok: !1,
|
|
252
294
|
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."
|
|
253
295
|
};
|
|
254
|
-
let n =
|
|
296
|
+
let n = B(t);
|
|
255
297
|
return e.liveKeys.some((e) => e === n) ? {
|
|
256
298
|
ok: !1,
|
|
257
299
|
error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
|
|
@@ -274,7 +316,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
274
316
|
pass: !0,
|
|
275
317
|
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
276
318
|
};
|
|
277
|
-
},
|
|
319
|
+
}, V = v({ scope: "voltro:data" }), Ye = async () => {
|
|
278
320
|
try {
|
|
279
321
|
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");
|
|
280
322
|
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
@@ -283,14 +325,14 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
283
325
|
}
|
|
284
326
|
}, Xe = async (e) => {
|
|
285
327
|
try {
|
|
286
|
-
return
|
|
328
|
+
return ce(await tt(e));
|
|
287
329
|
} catch {
|
|
288
330
|
return null;
|
|
289
331
|
}
|
|
290
|
-
},
|
|
332
|
+
}, H = (e) => {
|
|
291
333
|
if (e.event !== "done") return;
|
|
292
334
|
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
293
|
-
|
|
335
|
+
V.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
294
336
|
}, Ze = async (e) => {
|
|
295
337
|
let t = (e) => e?.meta?.databaseId;
|
|
296
338
|
if (e) {
|
|
@@ -312,20 +354,20 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
312
354
|
}
|
|
313
355
|
return null;
|
|
314
356
|
}, Qe = async (e) => {
|
|
315
|
-
let t = e.includes("--allow-live"), n = t ? null : await Ze(
|
|
316
|
-
dialect:
|
|
357
|
+
let t = e.includes("--allow-live"), n = t ? null : await Ze(U(e, "--api-url")), r = C({
|
|
358
|
+
dialect: G(),
|
|
317
359
|
...a()
|
|
318
|
-
}), i =
|
|
319
|
-
dialect:
|
|
360
|
+
}), i = ee({
|
|
361
|
+
dialect: G(),
|
|
320
362
|
...a()
|
|
321
|
-
}), { refuse: o, message: s } =
|
|
363
|
+
}), { refuse: o, message: s } = he({
|
|
322
364
|
liveInstance: n?.label ?? null,
|
|
323
365
|
allowLive: t,
|
|
324
|
-
sameTarget:
|
|
366
|
+
sameTarget: te(r, n?.databaseId),
|
|
325
367
|
...i === void 0 ? {} : { targetLabel: `${i.host}:${i.port}/${i.database}` }
|
|
326
368
|
});
|
|
327
|
-
return !o || (
|
|
328
|
-
},
|
|
369
|
+
return !o || (V.error(s ?? "refusing: live instance detected"), !1);
|
|
370
|
+
}, U = s, $e = [
|
|
329
371
|
"--api-url",
|
|
330
372
|
"--bundle-key",
|
|
331
373
|
"--compression",
|
|
@@ -345,50 +387,50 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
345
387
|
"--timeout",
|
|
346
388
|
"--tenant",
|
|
347
389
|
"--token"
|
|
348
|
-
],
|
|
390
|
+
], W = (e) => c(e, $e), G = () => {
|
|
349
391
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
350
392
|
if (e === "memory") throw new n("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
351
393
|
return e;
|
|
352
394
|
}, et = async (e, t) => {
|
|
353
395
|
let n = (await l(e)).makeSqlLayer(t);
|
|
354
|
-
return
|
|
396
|
+
return D.runPromise(oe.SqlClient.pipe(D.flatMap(le), D.provide(n)));
|
|
355
397
|
}, tt = (e) => et(e, a()), nt = () => {
|
|
356
398
|
let e = a(), t = [];
|
|
357
|
-
return e.url && t.push(
|
|
358
|
-
},
|
|
399
|
+
return e.url && t.push(B(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
|
|
400
|
+
}, K = async () => {
|
|
359
401
|
let e = process.cwd(), t = await r(e);
|
|
360
402
|
if (!t) throw new n("voltro data: no app.config.ts found in the current directory.");
|
|
361
|
-
let a = await i(t, void 0), o =
|
|
403
|
+
let a = await i(t, void 0), o = G();
|
|
362
404
|
return {
|
|
363
405
|
store: a,
|
|
364
406
|
snapshot: await tt(o),
|
|
365
407
|
dialect: o
|
|
366
408
|
};
|
|
367
|
-
},
|
|
368
|
-
table:
|
|
409
|
+
}, rt = (e) => ne(e, {
|
|
410
|
+
table: ue,
|
|
369
411
|
chunkSize: 500
|
|
370
|
-
}).pipe(
|
|
412
|
+
}).pipe(ae.map((e) => ({
|
|
371
413
|
key: String(e.key),
|
|
372
414
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
373
415
|
size: Number(e.size ?? 0)
|
|
374
|
-
}))), q = (e) => !!e && e.endsWith(".vbundle"), J = (e) =>
|
|
416
|
+
}))), q = (e) => !!e && e.endsWith(".vbundle"), J = (e) => U(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, it = (e) => e.includes("--encrypt") || U(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, at = async (e, t) => {
|
|
375
417
|
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
376
418
|
await r(i.from(e), n(t));
|
|
377
419
|
}, Y = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
378
420
|
let { createReadStream: t } = await import("node:fs");
|
|
379
421
|
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
380
|
-
} }),
|
|
422
|
+
} }), ot = async (e, t) => {
|
|
381
423
|
if (t !== void 0 || e === void 0) return null;
|
|
382
424
|
try {
|
|
383
|
-
let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = q(e) ? (await
|
|
425
|
+
let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = q(e) ? (await N(Y(i), ["manifest.json"])).get("manifest.json") : await r.readFile(n(i, "manifest.json"));
|
|
384
426
|
if (a === void 0) return null;
|
|
385
427
|
let o = (JSON.parse(Buffer.from(a).toString("utf8")).tables ?? []).map((e) => e.name).filter((e) => typeof e == "string");
|
|
386
428
|
return o.length > 0 ? o : null;
|
|
387
429
|
} catch {
|
|
388
430
|
return null;
|
|
389
431
|
}
|
|
390
|
-
},
|
|
391
|
-
let t =
|
|
432
|
+
}, st = (e) => {
|
|
433
|
+
let t = U(e, "--max-request-seconds") ?? process.env.VOLTRO_MAX_REQUEST_SECONDS;
|
|
392
434
|
if (t === void 0) return { pollMs: 2e3 };
|
|
393
435
|
let r = Number(t);
|
|
394
436
|
if (!Number.isFinite(r) || r <= 0) throw new n(`--max-request-seconds expects a positive number of seconds, got '${t}'`);
|
|
@@ -397,7 +439,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
397
439
|
perRequest: { timeoutMs: i },
|
|
398
440
|
pollMs: Math.max(500, Math.min(2e3, Math.floor(i / 2)))
|
|
399
441
|
};
|
|
400
|
-
},
|
|
442
|
+
}, ct = async (e, t, n) => {
|
|
401
443
|
let r = await fetch(`${e.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=50`, { headers: { authorization: `Bearer ${t}` } }).catch(() => null);
|
|
402
444
|
if (r === null || r.status !== 200) return null;
|
|
403
445
|
let i = ((await r.json().catch(() => null))?.runs ?? []).find((e) => e.id === n);
|
|
@@ -406,7 +448,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
406
448
|
startedAt: new Date(String(i.startedAt)),
|
|
407
449
|
finishedAt: i.finishedAt === null || i.finishedAt === void 0 ? null : new Date(String(i.finishedAt))
|
|
408
450
|
};
|
|
409
|
-
},
|
|
451
|
+
}, lt = async (e) => {
|
|
410
452
|
let t = e.apiUrl.replace(/\/+$/, ""), n = [], r = 0, i;
|
|
411
453
|
for (;;) {
|
|
412
454
|
let a = `${t}/_voltro/admin/export/download?runId=${encodeURIComponent(e.runId)}&offset=${r}`, o = new AbortController(), s = e.perRequest === void 0 ? void 0 : setTimeout(() => {
|
|
@@ -428,12 +470,12 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
428
470
|
e.log.info(` downloading — ${r}/${i ?? "?"} bytes`);
|
|
429
471
|
}
|
|
430
472
|
return new Uint8Array(Buffer.concat(n));
|
|
431
|
-
},
|
|
432
|
-
let r =
|
|
473
|
+
}, ut = "transfers", dt = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), ft = (e, t) => {
|
|
474
|
+
let r = U(e, "--tenant"), i = U(e, "--tables"), a = U(e, "--exclude");
|
|
433
475
|
if (a !== void 0) {
|
|
434
476
|
if (i !== void 0) throw new n("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
|
|
435
477
|
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.");
|
|
436
|
-
let e =
|
|
478
|
+
let e = dt(a), o = new Set(t), s = e.filter((e) => !o.has(e));
|
|
437
479
|
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.`);
|
|
438
480
|
return {
|
|
439
481
|
kind: "all",
|
|
@@ -445,15 +487,15 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
445
487
|
tenantId: r
|
|
446
488
|
} : i ? {
|
|
447
489
|
kind: "tables",
|
|
448
|
-
tables:
|
|
490
|
+
tables: dt(i)
|
|
449
491
|
} : { kind: "all" };
|
|
450
|
-
},
|
|
492
|
+
}, pt = async () => {
|
|
451
493
|
let e = await g(process.cwd());
|
|
452
|
-
return
|
|
453
|
-
},
|
|
454
|
-
let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id ===
|
|
494
|
+
return ve(se(e));
|
|
495
|
+
}, mt = "voltro/tenant", ht = async () => {
|
|
496
|
+
let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === mt)).map((e) => e.tableName);
|
|
455
497
|
return e.length > 0 ? e : void 0;
|
|
456
|
-
},
|
|
498
|
+
}, gt = (e) => {
|
|
457
499
|
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
458
500
|
console.log(`\n ${t.table}`);
|
|
459
501
|
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
@@ -466,8 +508,8 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
466
508
|
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
467
509
|
for (let t of e.unclassified) console.log(` ${t}`);
|
|
468
510
|
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
469
|
-
},
|
|
470
|
-
let { resolve: t, join: r } = await import("node:path"), i = await import("node:fs/promises"), a =
|
|
511
|
+
}, _t = async (e) => {
|
|
512
|
+
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);
|
|
471
513
|
if (!Number.isFinite(o) || o <= 0) throw new n(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
|
|
472
514
|
let s = e.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
|
|
473
515
|
for (; l <= o;) {
|
|
@@ -482,11 +524,11 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
482
524
|
kind: "small",
|
|
483
525
|
body: Buffer.concat(c)
|
|
484
526
|
};
|
|
485
|
-
let d = t(process.cwd(), e.bundleDir), f = ze(q(e.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await
|
|
527
|
+
let d = t(process.cwd(), e.bundleDir), f = ze(q(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), {
|
|
486
528
|
...e.headers,
|
|
487
529
|
"x-import-upload-id": p,
|
|
488
530
|
"x-import-probe": "1"
|
|
489
|
-
},
|
|
531
|
+
}, Z(e.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
|
|
490
532
|
h > 0 && e.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
|
|
491
533
|
let g = (async function* () {
|
|
492
534
|
for (let e of c) yield e;
|
|
@@ -509,7 +551,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
509
551
|
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.");
|
|
510
552
|
},
|
|
511
553
|
target: { send: async (t, n) => {
|
|
512
|
-
let r = await
|
|
554
|
+
let r = await I(e.url, t, n, Z(e.args)), i = {};
|
|
513
555
|
try {
|
|
514
556
|
i = JSON.parse(r.body.toString("utf8") || "{}");
|
|
515
557
|
} catch {}
|
|
@@ -531,83 +573,83 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
531
573
|
body: Buffer.from(JSON.stringify(v.body))
|
|
532
574
|
}
|
|
533
575
|
};
|
|
534
|
-
},
|
|
576
|
+
}, vt = (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", yt = (e, t, n) => {
|
|
535
577
|
if (t !== "replace" || e.includes("--no-rollback")) return;
|
|
536
|
-
let r =
|
|
578
|
+
let r = U(e, "--rollback-dir");
|
|
537
579
|
return r === void 0 ? o(n) : _(r);
|
|
538
|
-
},
|
|
539
|
-
let r =
|
|
580
|
+
}, bt = (e, t, n) => {
|
|
581
|
+
let r = yt(e, t, n);
|
|
540
582
|
return {
|
|
541
583
|
...r === void 0 ? {} : { rollbackDir: r },
|
|
542
584
|
recordInterruption: !0,
|
|
543
585
|
via: "direct"
|
|
544
586
|
};
|
|
545
|
-
},
|
|
546
|
-
let t =
|
|
587
|
+
}, Z = (e) => {
|
|
588
|
+
let t = U(e, "--timeout");
|
|
547
589
|
if (t === void 0) return {};
|
|
548
590
|
let r = Number(t);
|
|
549
591
|
if (!Number.isFinite(r) || r <= 0) throw new n(`--timeout expects a positive number of seconds, got '${t}'`);
|
|
550
592
|
return { timeoutMs: Math.round(r * 1e3) };
|
|
551
|
-
},
|
|
552
|
-
let t =
|
|
553
|
-
if (!r && !(t && n)) return
|
|
593
|
+
}, xt = async (e) => {
|
|
594
|
+
let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
|
|
595
|
+
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;
|
|
554
596
|
if (t) {
|
|
555
|
-
let t =
|
|
556
|
-
if (!t) return
|
|
557
|
-
let i =
|
|
558
|
-
if (!i) return
|
|
559
|
-
let a =
|
|
560
|
-
if (o !== void 0 && a !== void 0) return
|
|
597
|
+
let t = U(e, "--api-url");
|
|
598
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
599
|
+
let i = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
600
|
+
if (!i) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
601
|
+
let a = U(e, "--tables"), o = U(e, "--exclude");
|
|
602
|
+
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;
|
|
561
603
|
let s = {
|
|
562
|
-
...
|
|
563
|
-
...
|
|
604
|
+
...U(e, "--profile") ? { profile: U(e, "--profile") } : {},
|
|
605
|
+
...U(e, "--tenant") ? { tenant: U(e, "--tenant") } : {},
|
|
564
606
|
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
565
607
|
...o ? { exclude: o.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
566
|
-
...
|
|
567
|
-
...
|
|
608
|
+
...U(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
609
|
+
...U(e, "--compression") ? { compression: U(e, "--compression") } : {},
|
|
568
610
|
...e.includes("--assets") ? { assets: !0 } : {},
|
|
569
611
|
...n ? { bundleKey: n } : {}
|
|
570
|
-
}, c =
|
|
612
|
+
}, c = st(e), l;
|
|
571
613
|
try {
|
|
572
|
-
let n = await
|
|
614
|
+
let n = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(s), {
|
|
573
615
|
"content-type": "application/json",
|
|
574
616
|
authorization: `Bearer ${i}`
|
|
575
|
-
}, c.perRequest ??
|
|
576
|
-
if (n.status < 200 || n.status >= 300) return
|
|
617
|
+
}, c.perRequest ?? Z(e));
|
|
618
|
+
if (n.status < 200 || n.status >= 300) return V.error(`api export failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
|
|
577
619
|
l = JSON.parse(n.body.toString("utf8"));
|
|
578
620
|
} catch (e) {
|
|
579
|
-
if (e instanceof
|
|
580
|
-
if (e instanceof
|
|
621
|
+
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;
|
|
622
|
+
if (e instanceof P) return V.error(`api export: ${L(e)}`), 1;
|
|
581
623
|
throw e;
|
|
582
624
|
}
|
|
583
|
-
if (e.includes("--detach")) return
|
|
584
|
-
if (l.recorded === !1) return
|
|
625
|
+
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 ${ut} --target api --api-url ${t}`), 0;
|
|
626
|
+
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;
|
|
585
627
|
let p = await d({
|
|
586
|
-
read: () =>
|
|
628
|
+
read: () => ct(t, i, l.runId),
|
|
587
629
|
onTick: (e) => {
|
|
588
|
-
|
|
630
|
+
V.info(f(e));
|
|
589
631
|
},
|
|
590
632
|
intervalMs: c.pollMs,
|
|
591
633
|
...c.total === void 0 ? {} : { timeoutMs: c.total }
|
|
592
634
|
}), m = u(p);
|
|
593
|
-
if (m !== 0) return
|
|
594
|
-
if (n) return
|
|
635
|
+
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;
|
|
636
|
+
if (n) return V.info("api export complete (pushed to storage)", {
|
|
595
637
|
via: t,
|
|
596
638
|
runId: l.runId,
|
|
597
639
|
bundleKey: n,
|
|
598
640
|
tables: p?.tablesWritten ?? 0,
|
|
599
641
|
rows: p?.rowsWritten ?? 0
|
|
600
642
|
}), 0;
|
|
601
|
-
let { resolve: h } = await import("node:path"), g = await
|
|
643
|
+
let { resolve: h } = await import("node:path"), g = await lt({
|
|
602
644
|
apiUrl: t,
|
|
603
645
|
secret: i,
|
|
604
646
|
runId: l.runId,
|
|
605
|
-
log:
|
|
647
|
+
log: V,
|
|
606
648
|
...c.perRequest ? { perRequest: c.perRequest } : {}
|
|
607
649
|
});
|
|
608
650
|
return g === null ? 1 : (await Me((async function* () {
|
|
609
651
|
yield g;
|
|
610
|
-
})(), h(process.cwd(), r)),
|
|
652
|
+
})(), h(process.cwd(), r)), V.info("api export complete (downloaded from the running instance)", {
|
|
611
653
|
outDir: r,
|
|
612
654
|
runId: l.runId,
|
|
613
655
|
tables: p?.tablesWritten ?? 0,
|
|
@@ -615,11 +657,13 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
615
657
|
bytes: g.byteLength
|
|
616
658
|
}), 0);
|
|
617
659
|
}
|
|
618
|
-
if (!r) return
|
|
619
|
-
let i =
|
|
660
|
+
if (!r) return V.error("a <outDir> is required for a direct export"), 1;
|
|
661
|
+
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 ? O() : void 0;
|
|
662
|
+
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;
|
|
663
|
+
let h = U(e, "--compression") ?? a.compression, g = U(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", _ = a.masking, v = a.subset, y = a.scope ?? ft(e, s.tables.map((e) => e.name)), b = _ ? await pt() : void 0, x = y.kind === "tenant" ? await ht() : void 0, S = x ? { tenantTables: x } : {};
|
|
620
664
|
if (e.includes("--dry-run")) {
|
|
621
665
|
let e = y.kind === "tables" && y.tables ? [...y.tables] : s.tables.map((e) => e.name);
|
|
622
|
-
if (
|
|
666
|
+
if (V.info("export DRY RUN — nothing was written", {
|
|
623
667
|
outDir: r,
|
|
624
668
|
tables: e.length,
|
|
625
669
|
scope: y.kind,
|
|
@@ -627,8 +671,8 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
627
671
|
compression: h ?? "zstd",
|
|
628
672
|
assets: l,
|
|
629
673
|
masking: _ ? "profile policy applies" : "none"
|
|
630
|
-
}),
|
|
631
|
-
let t = await
|
|
674
|
+
}), V.info(`would export: ${e.join(", ")}`), !_) return 0;
|
|
675
|
+
let t = await D.runPromise(xe({
|
|
632
676
|
store: o,
|
|
633
677
|
snapshot: s,
|
|
634
678
|
masking: _,
|
|
@@ -638,12 +682,12 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
638
682
|
...S
|
|
639
683
|
}
|
|
640
684
|
}));
|
|
641
|
-
return
|
|
685
|
+
return gt(t), +(t.unclassified.length > 0);
|
|
642
686
|
}
|
|
643
687
|
if (q(r)) {
|
|
644
688
|
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-"));
|
|
645
689
|
try {
|
|
646
|
-
let t = await
|
|
690
|
+
let t = await D.runPromise(we({
|
|
647
691
|
store: o,
|
|
648
692
|
snapshot: s,
|
|
649
693
|
dialect: c,
|
|
@@ -656,17 +700,17 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
656
700
|
...h ? { compression: h } : {},
|
|
657
701
|
..._ ? { masking: _ } : {},
|
|
658
702
|
...b ? { classification: b } : {},
|
|
659
|
-
onProgress:
|
|
660
|
-
})), n =
|
|
703
|
+
onProgress: H
|
|
704
|
+
})), n = be({
|
|
661
705
|
dir: u,
|
|
662
|
-
...m ? { blobs: Ae(m, () =>
|
|
663
|
-
}), a =
|
|
706
|
+
...m ? { blobs: Ae(m, () => rt(o)) } : {}
|
|
707
|
+
}), a = it(e);
|
|
664
708
|
if (a) {
|
|
665
709
|
let t = J(e);
|
|
666
|
-
if (!t) return
|
|
667
|
-
n =
|
|
710
|
+
if (!t) return V.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
711
|
+
n = ye(n, t);
|
|
668
712
|
}
|
|
669
|
-
return await
|
|
713
|
+
return await at(n, i(process.cwd(), r)), V.info("export complete (single file)", {
|
|
670
714
|
file: r,
|
|
671
715
|
encrypted: a,
|
|
672
716
|
assets: l,
|
|
@@ -680,7 +724,7 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
680
724
|
});
|
|
681
725
|
}
|
|
682
726
|
}
|
|
683
|
-
let C = await
|
|
727
|
+
let C = await D.runPromise(we({
|
|
684
728
|
store: o,
|
|
685
729
|
snapshot: s,
|
|
686
730
|
dialect: c,
|
|
@@ -693,10 +737,10 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
693
737
|
...h ? { compression: h } : {},
|
|
694
738
|
..._ ? { masking: _ } : {},
|
|
695
739
|
...b ? { classification: b } : {},
|
|
696
|
-
...m ? { assets: Oe(m, () =>
|
|
697
|
-
onProgress:
|
|
740
|
+
...m ? { assets: Oe(m, () => rt(o)) } : {},
|
|
741
|
+
onProgress: H
|
|
698
742
|
}));
|
|
699
|
-
return
|
|
743
|
+
return V.info("export complete", {
|
|
700
744
|
outDir: r,
|
|
701
745
|
tables: C.tables.length,
|
|
702
746
|
rows: C.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -704,13 +748,13 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
704
748
|
compression: C.compression,
|
|
705
749
|
...C.masking ? { maskedColumns: C.masking.transformed.length } : {}
|
|
706
750
|
}), 0;
|
|
707
|
-
},
|
|
708
|
-
let t =
|
|
751
|
+
}, St = () => "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.", Ct = (e) => {
|
|
752
|
+
let t = U(e, "--tables");
|
|
709
753
|
return {
|
|
710
754
|
...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
711
755
|
...e.includes("--dry-run") ? { dryRun: !0 } : {}
|
|
712
756
|
};
|
|
713
|
-
},
|
|
757
|
+
}, wt = (e) => ({
|
|
714
758
|
tables: e.tablesWritten,
|
|
715
759
|
rows: e.rowsWritten,
|
|
716
760
|
carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -718,28 +762,28 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
718
762
|
skippedTables: e.tablesSkipped,
|
|
719
763
|
skippedRows: e.rowsSkipped
|
|
720
764
|
} : {}
|
|
721
|
-
}),
|
|
765
|
+
}), Tt = (e, t) => {
|
|
722
766
|
if (e.includes("--dry-run")) {
|
|
723
|
-
|
|
767
|
+
V.info("import DRY RUN — nothing was written", t), V.info(St());
|
|
724
768
|
return;
|
|
725
769
|
}
|
|
726
770
|
if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
|
|
727
|
-
|
|
771
|
+
V.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
|
|
728
772
|
return;
|
|
729
773
|
}
|
|
730
|
-
|
|
731
|
-
},
|
|
732
|
-
let t =
|
|
774
|
+
V.info("import complete", t);
|
|
775
|
+
}, Et = Symbol("refused"), Q = (e) => {
|
|
776
|
+
let t = me(e) ?? e;
|
|
733
777
|
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
|
|
734
778
|
let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
|
|
735
|
-
|
|
779
|
+
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.`);
|
|
736
780
|
let o = Array.isArray(t.byTable) ? t.byTable : [];
|
|
737
|
-
o.length > 1 &&
|
|
738
|
-
for (let e of r)
|
|
739
|
-
n.length < i &&
|
|
781
|
+
o.length > 1 && V.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
|
|
782
|
+
for (let e of r) V.error(` ${e.table} ${e.id} ${e.reason}`);
|
|
783
|
+
n.length < i && V.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
|
|
740
784
|
let s = i - a;
|
|
741
|
-
return s > 0 &&
|
|
742
|
-
},
|
|
785
|
+
return s > 0 && V.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
|
|
786
|
+
}, Dt = (e) => {
|
|
743
787
|
let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
|
|
744
788
|
try {
|
|
745
789
|
let e = JSON.parse(t), r = e.error ?? e.message;
|
|
@@ -779,22 +823,22 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
779
823
|
return;
|
|
780
824
|
}
|
|
781
825
|
}
|
|
782
|
-
},
|
|
783
|
-
let t =
|
|
784
|
-
if (!r && !(t && n)) return
|
|
785
|
-
let i =
|
|
826
|
+
}, Ot = async (e) => {
|
|
827
|
+
let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
|
|
828
|
+
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;
|
|
829
|
+
let i = U(e, "--mode") ?? "upsert", o = U(e, "--on-conflict") ?? "skip";
|
|
786
830
|
if (t) {
|
|
787
|
-
let t =
|
|
788
|
-
if (!t) return
|
|
789
|
-
let a =
|
|
790
|
-
if (!a) return
|
|
791
|
-
let s =
|
|
792
|
-
i === "replace" && c === void 0 &&
|
|
831
|
+
let t = U(e, "--api-url");
|
|
832
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
833
|
+
let a = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
834
|
+
if (!a) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
835
|
+
let s = U(e, "--tables"), c = U(e, "--rollback-key");
|
|
836
|
+
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.");
|
|
793
837
|
let l = {
|
|
794
838
|
authorization: `Bearer ${a}`,
|
|
795
839
|
"x-import-mode": i,
|
|
796
840
|
"x-import-on-conflict": o,
|
|
797
|
-
...
|
|
841
|
+
...X(e, i) ? { "x-import-atomic": "1" } : {},
|
|
798
842
|
...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
|
|
799
843
|
...c === void 0 ? {} : { "x-import-rollback-key": c },
|
|
800
844
|
...e.includes("--force") ? { "x-import-force": "1" } : {},
|
|
@@ -804,64 +848,64 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
804
848
|
if (n) l["content-type"] = "application/json", m = JSON.stringify({ bundleKey: n });
|
|
805
849
|
else if (q(r)) {
|
|
806
850
|
let t = Y(p(process.cwd(), r)), n = J(e);
|
|
807
|
-
n && (t =
|
|
808
|
-
} else await (await import("node:fs/promises")).access(p(process.cwd(), r, "assets", "index.ndjson")).then(() => !0, () => !1) &&
|
|
851
|
+
n && (t = M(t, n)), l["content-type"] = "application/octet-stream", m = t;
|
|
852
|
+
} 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 = be({ dir: p(process.cwd(), r) });
|
|
809
853
|
if (i === "replace") {
|
|
810
|
-
let i = await
|
|
854
|
+
let i = await ot(r, n);
|
|
811
855
|
{
|
|
812
|
-
let r = i === null ? { bundleKey: n } : { tables: i }, a = await
|
|
856
|
+
let r = i === null ? { bundleKey: n } : { tables: i }, a = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, JSON.stringify(r), {
|
|
813
857
|
...l,
|
|
814
858
|
"content-type": "application/json",
|
|
815
859
|
"x-import-preflight": "1"
|
|
816
|
-
},
|
|
817
|
-
o === null ?
|
|
860
|
+
}, Z(e)).catch(() => null), o = a !== null && a.status === 200 ? JSON.parse(a.body.toString("utf8")) : null;
|
|
861
|
+
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
|
|
818
862
|
interrupted run leaves the target empty.\n Reason: ${o.reason ?? "not stated"}`);
|
|
819
863
|
}
|
|
820
864
|
}
|
|
821
|
-
i === "replace" && !
|
|
822
|
-
let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g =
|
|
865
|
+
i === "replace" && !X(e, i) && V.warn(vt(!0));
|
|
866
|
+
let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g = st(e), _;
|
|
823
867
|
try {
|
|
824
|
-
let t = typeof m == "string" ? null : await
|
|
868
|
+
let t = typeof m == "string" ? null : await _t({
|
|
825
869
|
stream: m,
|
|
826
870
|
url: h,
|
|
827
871
|
headers: l,
|
|
828
872
|
bundleDir: r,
|
|
829
873
|
args: e,
|
|
830
|
-
log:
|
|
831
|
-
}), n = t === null ? await
|
|
832
|
-
if (n.status < 200 || n.status >= 300) return
|
|
874
|
+
log: V
|
|
875
|
+
}), n = t === null ? await I(h, m, l, g.perRequest ?? Z(e)) : t.kind === "sent" ? t.res : await I(h, t.body, l, g.perRequest ?? Z(e));
|
|
876
|
+
if (n.status < 200 || n.status >= 300) return Q(Dt(n.body)) || V.error(`api import failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
|
|
833
877
|
_ = JSON.parse(n.body.toString("utf8"));
|
|
834
878
|
} catch (e) {
|
|
835
|
-
if (e instanceof
|
|
836
|
-
if (e instanceof
|
|
879
|
+
if (e instanceof F) return V.error(`api import: ${Ie(e, i)}`), 1;
|
|
880
|
+
if (e instanceof P) return V.error(`api import: ${L(e)}`), 1;
|
|
837
881
|
throw e;
|
|
838
882
|
}
|
|
839
|
-
let v = await
|
|
883
|
+
let v = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import/start`, JSON.stringify(_.bundleKey === void 0 ? { uploadId: _.uploadId } : { bundleKey: _.bundleKey }), {
|
|
840
884
|
...l,
|
|
841
885
|
"content-type": "application/json"
|
|
842
|
-
}, g.perRequest ??
|
|
843
|
-
if (e instanceof
|
|
886
|
+
}, g.perRequest ?? Z(e)).catch((e) => {
|
|
887
|
+
if (e instanceof P) return V.error(`api import: ${L(e)}`), null;
|
|
844
888
|
throw e;
|
|
845
889
|
});
|
|
846
890
|
if (v === null) return 1;
|
|
847
|
-
if (v.status < 200 || v.status >= 300) return
|
|
891
|
+
if (v.status < 200 || v.status >= 300) return Q(Dt(v.body)) || V.error(`api import refused: HTTP ${v.status} — ${v.body.toString("utf8")}`), 1;
|
|
848
892
|
let y = JSON.parse(v.body.toString("utf8"));
|
|
849
|
-
if (y.alreadyStarted === !0 &&
|
|
850
|
-
if (y.recorded === !1) return
|
|
893
|
+
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 ${ut} --target api --api-url ${t}`), 0;
|
|
894
|
+
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;
|
|
851
895
|
let b = await d({
|
|
852
|
-
read: () =>
|
|
896
|
+
read: () => ct(t, a, y.runId),
|
|
853
897
|
onTick: (e) => {
|
|
854
|
-
|
|
898
|
+
V.info(f(e));
|
|
855
899
|
},
|
|
856
900
|
intervalMs: g.pollMs,
|
|
857
901
|
...g.total === void 0 ? {} : { timeoutMs: g.total }
|
|
858
902
|
}), x = u(b);
|
|
859
|
-
return x === 2 ? (
|
|
903
|
+
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
|
|
860
904
|
proxy dropping the GET, or a target with no history table. The run itself is unaffected and
|
|
861
|
-
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 ${
|
|
905
|
+
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 ${ut} --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", {
|
|
862
906
|
via: t,
|
|
863
907
|
runId: y.runId
|
|
864
|
-
}),
|
|
908
|
+
}), V.info(St()), 0) : (V.info("api import complete (imported by the running instance)", {
|
|
865
909
|
via: t,
|
|
866
910
|
runId: y.runId,
|
|
867
911
|
tables: b?.tablesWritten ?? 0,
|
|
@@ -869,74 +913,76 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
869
913
|
...b?.staged !== null && b?.staged !== void 0 ? { staged: b.staged } : {}
|
|
870
914
|
}), 0);
|
|
871
915
|
}
|
|
872
|
-
if (!r) return
|
|
916
|
+
if (!r) return V.error("a <bundleDir> is required for a direct import"), 1;
|
|
873
917
|
if (!await Qe(e)) return 1;
|
|
874
|
-
i === "replace" && e.includes("--allow-live") && !
|
|
875
|
-
let { store: s, snapshot: c } = await
|
|
876
|
-
c.tables.some((e) => e.name === y) ||
|
|
877
|
-
let l = e.includes("--force"), p = (e) =>
|
|
918
|
+
i === "replace" && e.includes("--allow-live") && !X(e, i) && V.warn(vt(!1));
|
|
919
|
+
let { store: s, snapshot: c } = await K();
|
|
920
|
+
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.`);
|
|
921
|
+
let l = e.includes("--force"), p = (e) => V.warn(e), m = e.includes("--assets"), h = m ? O() : void 0;
|
|
922
|
+
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;
|
|
923
|
+
let g = G() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(a()) : void 0;
|
|
878
924
|
if (q(r)) {
|
|
879
925
|
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-"));
|
|
880
926
|
try {
|
|
881
927
|
let t = Y(a(process.cwd(), r)), n = J(e);
|
|
882
|
-
return n && (t =
|
|
928
|
+
return n && (t = M(t, n)), await je(t, {
|
|
883
929
|
dir: d,
|
|
884
|
-
blobs: ke(
|
|
885
|
-
}),
|
|
930
|
+
blobs: ke(h ?? de())
|
|
931
|
+
}), Tt(e, {
|
|
886
932
|
file: r,
|
|
887
|
-
...
|
|
933
|
+
...wt(await D.runPromise(Te({
|
|
888
934
|
store: s,
|
|
889
935
|
bundleDir: d,
|
|
890
936
|
mode: i,
|
|
891
937
|
onConflict: o,
|
|
892
|
-
atomic:
|
|
893
|
-
...
|
|
938
|
+
atomic: X(e, i),
|
|
939
|
+
...bt(e, i, r),
|
|
894
940
|
verify: !e.includes("--no-verify"),
|
|
895
|
-
targetDialect:
|
|
941
|
+
targetDialect: G(),
|
|
896
942
|
allowIncompatible: l,
|
|
897
943
|
targetSnapshot: c,
|
|
898
944
|
force: l,
|
|
899
945
|
onWarn: p,
|
|
900
|
-
onProgress:
|
|
946
|
+
onProgress: H,
|
|
901
947
|
assetsAlreadyRestored: !0,
|
|
902
|
-
...
|
|
903
|
-
...
|
|
948
|
+
...Ct(e),
|
|
949
|
+
...g ? { copyLoader: g } : {}
|
|
904
950
|
})))
|
|
905
951
|
}), 0;
|
|
906
952
|
} finally {
|
|
907
|
-
await
|
|
953
|
+
await g?.close(), await n.rm(d, {
|
|
908
954
|
recursive: !0,
|
|
909
955
|
force: !0
|
|
910
956
|
});
|
|
911
957
|
}
|
|
912
958
|
}
|
|
913
|
-
let
|
|
914
|
-
...
|
|
959
|
+
let _ = await D.runPromise(Te({
|
|
960
|
+
...bt(e, i, r),
|
|
915
961
|
store: s,
|
|
916
962
|
bundleDir: r,
|
|
917
963
|
mode: i,
|
|
918
964
|
onConflict: o,
|
|
919
|
-
atomic:
|
|
965
|
+
atomic: X(e, i),
|
|
920
966
|
verify: !e.includes("--no-verify"),
|
|
921
|
-
targetDialect:
|
|
967
|
+
targetDialect: G(),
|
|
922
968
|
allowIncompatible: l,
|
|
923
969
|
targetSnapshot: c,
|
|
924
970
|
force: l,
|
|
925
971
|
onWarn: p,
|
|
926
|
-
onProgress:
|
|
927
|
-
...
|
|
928
|
-
...
|
|
929
|
-
...
|
|
972
|
+
onProgress: H,
|
|
973
|
+
...Ct(e),
|
|
974
|
+
...h ? { assets: De(h) } : {},
|
|
975
|
+
...g ? { copyLoader: g } : {}
|
|
930
976
|
})).catch((e) => {
|
|
931
|
-
if (
|
|
977
|
+
if (Q(e)) return Et;
|
|
932
978
|
throw e;
|
|
933
|
-
}).finally(() =>
|
|
934
|
-
return typeof
|
|
979
|
+
}).finally(() => g?.close());
|
|
980
|
+
return typeof _ == "symbol" ? 1 : (Tt(e, {
|
|
935
981
|
bundleDir: r,
|
|
936
|
-
...
|
|
937
|
-
assets:
|
|
982
|
+
...wt(_),
|
|
983
|
+
assets: _.manifest.assets.count
|
|
938
984
|
}), 0);
|
|
939
|
-
},
|
|
985
|
+
}, kt = () => {
|
|
940
986
|
let e = a();
|
|
941
987
|
return {
|
|
942
988
|
...e.url ? { url: e.url } : {},
|
|
@@ -946,33 +992,42 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
946
992
|
...e.password ? { password: e.password } : {},
|
|
947
993
|
...e.database ? { database: e.database } : {}
|
|
948
994
|
};
|
|
949
|
-
},
|
|
950
|
-
let t =
|
|
951
|
-
if (!t) return
|
|
952
|
-
let n =
|
|
953
|
-
if (r && !
|
|
995
|
+
}, At = async (e) => {
|
|
996
|
+
let t = W(e);
|
|
997
|
+
if (!t) return V.error("usage: voltro data backup <outDir> [--assets] (native pg_dump/mariadb-dump/… + consistent artifact)"), 1;
|
|
998
|
+
let n = G(), r = e.includes("--assets"), { mkdir: i, writeFile: a } = await import("node:fs/promises"), { join: o } = await import("node:path"), s = await Mt(), c = r ? O() : void 0;
|
|
999
|
+
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;
|
|
954
1000
|
await i(t, { recursive: !0 });
|
|
955
|
-
let
|
|
1001
|
+
let l = o(t, j(n)), u = Pt(s), d = Ft(s), f = u ? await We(u, {
|
|
956
1002
|
direction: "backup",
|
|
957
1003
|
dialect: n,
|
|
958
|
-
artifact:
|
|
1004
|
+
artifact: l,
|
|
959
1005
|
assets: r
|
|
960
|
-
}) : void 0,
|
|
961
|
-
if (
|
|
962
|
-
let
|
|
963
|
-
|
|
964
|
-
|
|
1006
|
+
}) : void 0, p = await Ke(Ee(ge(n, kt(), l)));
|
|
1007
|
+
if (p !== void 0) return u && f && await z(u, f, { failure: p }), V.error(`native backup failed: ${p}`), 1;
|
|
1008
|
+
let m = await Xe(n), h = await Ye(), g = (/* @__PURE__ */ new Date()).toISOString(), _ = async (e) => {
|
|
1009
|
+
let r = Ve({
|
|
1010
|
+
dialect: n,
|
|
1011
|
+
schemaFingerprint: m,
|
|
1012
|
+
voltroVersion: h,
|
|
1013
|
+
createdAt: g,
|
|
1014
|
+
assets: e
|
|
1015
|
+
});
|
|
1016
|
+
return await a(o(t, R), `${JSON.stringify(r, null, 2)}\n`, "utf8"), r;
|
|
1017
|
+
}, v = 0, y = 0, b = [], x = 0, S = 0, C = 0;
|
|
1018
|
+
if (c && u) {
|
|
1019
|
+
let e = o(t, k);
|
|
965
1020
|
await i(e, { recursive: !0 });
|
|
966
|
-
let n = o(t,
|
|
967
|
-
source: Oe(
|
|
1021
|
+
let n = o(t, A), r = u, a = await D.runPromiseExit(_e({
|
|
1022
|
+
source: Oe(c, () => rt(r)),
|
|
968
1023
|
assetsDir: e,
|
|
969
1024
|
progress: {
|
|
970
1025
|
done: () => !1,
|
|
971
|
-
mark: (e, t) =>
|
|
1026
|
+
mark: (e, t) => D.tryPromise({
|
|
972
1027
|
try: async () => {
|
|
973
1028
|
await (await import("node:fs/promises")).appendFile(n, `${JSON.stringify(t)}\n`);
|
|
974
1029
|
},
|
|
975
|
-
catch: (e) => new
|
|
1030
|
+
catch: (e) => new pe({
|
|
976
1031
|
reason: `asset index append failed: ${String(e)}`,
|
|
977
1032
|
path: n
|
|
978
1033
|
})
|
|
@@ -980,121 +1035,139 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
980
1035
|
}
|
|
981
1036
|
}));
|
|
982
1037
|
if (a._tag === "Failure") {
|
|
983
|
-
let e =
|
|
984
|
-
return
|
|
1038
|
+
let e = E.pretty(a.cause);
|
|
1039
|
+
return f && await z(u, f, { failure: `assets: ${e}` }), await _(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
|
|
985
1040
|
backend is reachable, or restore without --assets and accept the dangling refs.`), 1;
|
|
986
1041
|
}
|
|
987
|
-
|
|
1042
|
+
v = a.value.count, y = a.value.totalBytes, b = a.value.missing, x = a.value.objects, S = a.value.objectBytes, C = a.value.references, b.length > 0 && V.warn(`${b.length} of ${b.length + v} 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.
|
|
1043
|
+
e.g. ${b.slice(0, 3).map((e) => `'${e}'`).join(", ")}` + (b.length > 3 ? `, and ${b.length - 3} more` : ""));
|
|
988
1044
|
}
|
|
989
|
-
let
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
} : null
|
|
998
|
-
});
|
|
999
|
-
return await a(o(t, L), `${JSON.stringify(m, null, 2)}\n`, "utf8"), l && u && await R(l.store, u, { assets: f }), B.info("native backup complete", {
|
|
1045
|
+
let w = await _(r ? {
|
|
1046
|
+
count: v,
|
|
1047
|
+
totalBytes: y,
|
|
1048
|
+
references: C,
|
|
1049
|
+
objects: x,
|
|
1050
|
+
objectBytes: S
|
|
1051
|
+
} : null);
|
|
1052
|
+
return u && f && await z(u, f, { assets: v }), V.info("native backup complete", {
|
|
1000
1053
|
dialect: n,
|
|
1001
|
-
artifact:
|
|
1002
|
-
stamp:
|
|
1003
|
-
schemaFingerprint:
|
|
1004
|
-
assets: r ? `${
|
|
1005
|
-
recorded:
|
|
1054
|
+
artifact: l,
|
|
1055
|
+
stamp: R,
|
|
1056
|
+
schemaFingerprint: w.schemaFingerprint ?? "<none>",
|
|
1057
|
+
assets: r ? `${v} key(s) → ${x} object(s), ${S} byte(s) under ${k}/ (from ${C} reference(s))` + (b.length > 0 ? ` — ${b.length} reference(s) had no object and are NOT in here` : "") : "NOT included — re-run with --assets, or use `voltro data export --assets`",
|
|
1058
|
+
recorded: jt(f, d)
|
|
1006
1059
|
}), 0;
|
|
1007
|
-
},
|
|
1060
|
+
}, jt = (e, t) => e?.recorded === !0 ? "voltro data transfers" : t === void 0 ? "NOT recorded (this target keeps no transfer history)" : `NOT recorded — bookkeeping was unavailable HERE, not at the target: ${t}`, Mt = async () => {
|
|
1061
|
+
let e;
|
|
1008
1062
|
try {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1063
|
+
e = await r(process.cwd());
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
return {
|
|
1066
|
+
unavailable: "store-unopenable",
|
|
1067
|
+
reason: `app.config.ts could not be loaded: ${Nt(e)}`
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
if (!e) return { unavailable: "no-app-config" };
|
|
1071
|
+
try {
|
|
1072
|
+
return { store: await i(e, void 0) };
|
|
1073
|
+
} catch (e) {
|
|
1074
|
+
return {
|
|
1075
|
+
unavailable: "store-unopenable",
|
|
1076
|
+
reason: Nt(e)
|
|
1077
|
+
};
|
|
1013
1078
|
}
|
|
1014
|
-
},
|
|
1015
|
-
let
|
|
1016
|
-
|
|
1079
|
+
}, Nt = (e) => {
|
|
1080
|
+
let t = e?.message, n = typeof t == "string" && t.trim() !== "" ? t : String(e);
|
|
1081
|
+
return n.split("\n")[0] ?? n;
|
|
1082
|
+
}, Pt = (e) => "store" in e ? e.store : void 0, Ft = (e) => "store" in e ? void 0 : e.unavailable === "no-app-config" ? "no app.config.ts was found from this directory, so there is no store to record through" : e.reason, It = async (e, t) => {
|
|
1083
|
+
let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, j(n)), o = qe({
|
|
1084
|
+
flagUrl: U(t, "--drill-url"),
|
|
1017
1085
|
envUrl: process.env.DRILL_DB_URL,
|
|
1018
1086
|
liveKeys: nt()
|
|
1019
1087
|
});
|
|
1020
|
-
if (!o.ok) return
|
|
1021
|
-
let s = await i(r(e,
|
|
1022
|
-
if (s && s.dialect !== n) return
|
|
1023
|
-
|
|
1088
|
+
if (!o.ok) return V.error(`restore --drill: ${o.error}`), 1;
|
|
1089
|
+
let s = await i(r(e, R), "utf8").then(He, () => null);
|
|
1090
|
+
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;
|
|
1091
|
+
V.info("restore drill: restoring into throwaway DB", {
|
|
1024
1092
|
dialect: n,
|
|
1025
|
-
target:
|
|
1026
|
-
}), await
|
|
1093
|
+
target: B(o.url)
|
|
1094
|
+
}), await D.runPromise(Ee(Ce(n, { url: o.url }, a)));
|
|
1027
1095
|
let c = null, l = 0;
|
|
1028
1096
|
try {
|
|
1029
1097
|
let e = await et(n, { url: o.url });
|
|
1030
|
-
c =
|
|
1098
|
+
c = ce(e), l = e.tables.length;
|
|
1031
1099
|
} catch (e) {
|
|
1032
|
-
return
|
|
1100
|
+
return V.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
1033
1101
|
}
|
|
1034
1102
|
let u = Je({
|
|
1035
1103
|
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
1036
1104
|
restoredFingerprint: c,
|
|
1037
1105
|
restoredTableCount: l
|
|
1038
1106
|
});
|
|
1039
|
-
for (let e of u.lines) u.pass ?
|
|
1107
|
+
for (let e of u.lines) u.pass ? V.info(`restore drill: ${e}`) : V.error(`restore drill: ${e}`);
|
|
1040
1108
|
return +!u.pass;
|
|
1041
|
-
},
|
|
1042
|
-
let t =
|
|
1043
|
-
if (!t) return
|
|
1044
|
-
if (e.includes("--drill")) return
|
|
1109
|
+
}, Lt = async (e) => {
|
|
1110
|
+
let t = W(e);
|
|
1111
|
+
if (!t) return V.error("usage: voltro data restore <backupDir> [--assets] [--allow-live] [--api-url <url>] [--force] [--no-marker] [--drill [--drill-url <url>]] (native pg_restore/mariadb/…)"), 1;
|
|
1112
|
+
if (e.includes("--drill")) return It(t, e);
|
|
1045
1113
|
if (!await Qe(e)) return 1;
|
|
1046
|
-
let n =
|
|
1047
|
-
stamp: await i(r(t,
|
|
1114
|
+
let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, j(n)), o = e.includes("--assets"), s = Ue({
|
|
1115
|
+
stamp: await i(r(t, R), "utf8").then(He, () => null),
|
|
1048
1116
|
currentDialect: n,
|
|
1049
|
-
|
|
1117
|
+
targetFingerprint: await Xe(n)
|
|
1050
1118
|
});
|
|
1051
|
-
for (let e of s.info)
|
|
1052
|
-
if (s.warn &&
|
|
1053
|
-
if (!e.includes("--force")) return
|
|
1054
|
-
|
|
1119
|
+
for (let e of s.info) V.info(`backup stamp: ${e}`);
|
|
1120
|
+
if (s.warn && V.warn(s.warn), s.refuse) {
|
|
1121
|
+
if (!e.includes("--force")) return V.error(`restore refused: ${s.refuse} (pass --force to override)`), 1;
|
|
1122
|
+
V.warn(`restore proceeding despite: ${s.refuse} (--force)`);
|
|
1055
1123
|
}
|
|
1056
|
-
let c = r(t,
|
|
1057
|
-
if (o && !l) return
|
|
1124
|
+
let c = r(t, A), l = await i(c, "utf8").then(() => !0, () => !1);
|
|
1125
|
+
if (o && !l) return V.error(`--assets: this backup carries no assets — there is no ${A} in ${t}.\n It was taken without --assets, so its rows will reference blobs this restore cannot
|
|
1058
1126
|
put back. Restore from a backup taken with --assets, or drop the flag to accept that.`), 1;
|
|
1059
|
-
!o && l &&
|
|
1060
|
-
let u = o ?
|
|
1061
|
-
if (o && !
|
|
1062
|
-
let
|
|
1063
|
-
if (
|
|
1064
|
-
|
|
1065
|
-
database.
|
|
1066
|
-
|
|
1127
|
+
!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.");
|
|
1128
|
+
let u = await Mt(), d = o ? O() : void 0;
|
|
1129
|
+
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;
|
|
1130
|
+
let f = Ft(u), p = e.includes("--no-marker"), m = Pt(u), h = m ? await Ge(m, { artifact: a }) : void 0, g = f === void 0 ? h === void 0 ? `the marker row could not be written to \`${b}\` (run \`voltro db apply\` so the table exists)` : void 0 : `bookkeeping is unavailable: ${f}`;
|
|
1131
|
+
if (g !== void 0 && !p) return V.error(`refusing to restore: the in-progress marker cannot be written.
|
|
1132
|
+
reason: ${g}\n The marker is one row in \`${b}\`, written before the first\n destructive statement so a killed restore refuses the next boot instead of serving a
|
|
1133
|
+
half-loaded database. Without it this restore has no guard at all.
|
|
1134
|
+
Fix the reason above, or pass --no-marker to restore WITHOUT that protection,
|
|
1135
|
+
deliberately.`), 1;
|
|
1136
|
+
g !== void 0 && V.warn(`--no-marker: restoring with NO in-progress marker. If this run does not finish, nothing
|
|
1137
|
+
will stop the next boot from serving a half-loaded database.
|
|
1138
|
+
${g}`);
|
|
1139
|
+
let _ = m ? await We(m, {
|
|
1067
1140
|
direction: "restore",
|
|
1068
1141
|
dialect: n,
|
|
1069
1142
|
artifact: a,
|
|
1070
1143
|
assets: o
|
|
1071
|
-
}) : void 0,
|
|
1072
|
-
if (
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
let
|
|
1076
|
-
if (
|
|
1077
|
-
let e = await
|
|
1078
|
-
sink: De(
|
|
1079
|
-
assetsDir: r(t,
|
|
1144
|
+
}) : void 0, v = await Ke(Ee(Ce(n, kt(), a))), y = h === void 0 ? "lost" : await h.reassert();
|
|
1145
|
+
if (y === "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
|
|
1146
|
+
and hand-made dumps.`) : y === "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
|
|
1147
|
+
result of this command before you start the app.`), v !== void 0) return m && _ && await z(m, _, { failure: v }), V.error(`native restore failed: ${v}\n` + (y === "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;
|
|
1148
|
+
let x = 0, S = 0;
|
|
1149
|
+
if (d) {
|
|
1150
|
+
let e = await D.runPromiseExit(Se({
|
|
1151
|
+
sink: De(d),
|
|
1152
|
+
assetsDir: r(t, k),
|
|
1080
1153
|
indexPath: c
|
|
1081
1154
|
}));
|
|
1082
1155
|
if (e._tag === "Failure") {
|
|
1083
|
-
let t =
|
|
1084
|
-
return
|
|
1156
|
+
let t = E.pretty(e.cause);
|
|
1157
|
+
return m && _ && await z(m, _, { 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
|
|
1085
1158
|
\`voltro data restore <dir> --assets\` once storage is reachable.`), 1;
|
|
1086
1159
|
}
|
|
1087
|
-
|
|
1160
|
+
x = e.value.count, S = e.value.objects;
|
|
1088
1161
|
}
|
|
1089
|
-
return
|
|
1162
|
+
return h && await h.clear(), m && _ && await z(m, _, { assets: x }), V.info("native restore complete", {
|
|
1090
1163
|
dialect: n,
|
|
1091
1164
|
artifact: a,
|
|
1092
|
-
assets: o ? `${
|
|
1093
|
-
recorded:
|
|
1165
|
+
assets: o ? `${x} key(s) restored from ${S} object(s)` : "not restored (no --assets)",
|
|
1166
|
+
recorded: jt(_, f)
|
|
1094
1167
|
}), 0;
|
|
1095
|
-
},
|
|
1096
|
-
let t =
|
|
1097
|
-
if (!t) return
|
|
1168
|
+
}, Rt = async (e) => {
|
|
1169
|
+
let t = W(e);
|
|
1170
|
+
if (!t) return V.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
|
|
1098
1171
|
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
|
|
1099
1172
|
if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
|
|
1100
1173
|
let n = r(a, "manifest.json"), o = r(a, "README.md");
|
|
@@ -1103,24 +1176,24 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
1103
1176
|
if (e !== void 0) return console.log(e), 0;
|
|
1104
1177
|
}
|
|
1105
1178
|
let s = await i.readFile(n, "utf8").catch(() => void 0);
|
|
1106
|
-
return s === void 0 ? (
|
|
1179
|
+
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);
|
|
1107
1180
|
}
|
|
1108
1181
|
let o = J(e), s = () => {
|
|
1109
1182
|
let e = Y(a);
|
|
1110
|
-
return o ?
|
|
1183
|
+
return o ? M(e, o) : e;
|
|
1111
1184
|
};
|
|
1112
1185
|
if (e.includes("--json")) {
|
|
1113
|
-
let e = (await
|
|
1114
|
-
return e ? (console.log(e.toString("utf8")), 0) : (
|
|
1186
|
+
let e = (await N(s(), ["manifest.json"])).get("manifest.json");
|
|
1187
|
+
return e ? (console.log(e.toString("utf8")), 0) : (V.error("no manifest.json found in the bundle"), 1);
|
|
1115
1188
|
}
|
|
1116
|
-
let c = (await
|
|
1189
|
+
let c = (await N(s(), ["README.md"])).get("README.md");
|
|
1117
1190
|
if (c) return console.log(c.toString("utf8")), 0;
|
|
1118
|
-
let l = (await
|
|
1119
|
-
return l ? (console.log(l.toString("utf8")), 0) : (
|
|
1120
|
-
},
|
|
1121
|
-
let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-CvmiAM9K.js"), { store: r } = await
|
|
1122
|
-
if (i === void 0) return
|
|
1123
|
-
if (!e.includes("--yes")) return
|
|
1191
|
+
let l = (await N(s(), ["manifest.json"])).get("manifest.json");
|
|
1192
|
+
return l ? (console.log(l.toString("utf8")), 0) : (V.error("bundle has no README.md or manifest.json"), 1);
|
|
1193
|
+
}, zt = async (e) => {
|
|
1194
|
+
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);
|
|
1195
|
+
if (i === void 0) return V.info("no interrupted replace is recorded — nothing to clear"), 0;
|
|
1196
|
+
if (!e.includes("--yes")) return V.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
|
|
1124
1197
|
let a = await r.query({
|
|
1125
1198
|
table: t,
|
|
1126
1199
|
predicate: void 0,
|
|
@@ -1130,30 +1203,30 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
1130
1203
|
projection: void 0
|
|
1131
1204
|
});
|
|
1132
1205
|
for (let e of a) await r.delete(t, String(e.id));
|
|
1133
|
-
return
|
|
1134
|
-
},
|
|
1135
|
-
let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await
|
|
1136
|
-
if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return
|
|
1206
|
+
return V.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
|
|
1207
|
+
}, Bt = async (e) => {
|
|
1208
|
+
let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await K(), a = G();
|
|
1209
|
+
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;
|
|
1137
1210
|
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));
|
|
1138
|
-
return s.length === 0 ? (
|
|
1139
|
-
},
|
|
1140
|
-
let { describeTransferRun: t, DATA_TRANSFERS_TABLE: n } = await import("@voltro/database"), r = Number(
|
|
1141
|
-
if (
|
|
1142
|
-
let t =
|
|
1143
|
-
if (!t) return
|
|
1144
|
-
let n =
|
|
1145
|
-
if (!n) return
|
|
1211
|
+
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);
|
|
1212
|
+
}, Vt = async (e) => {
|
|
1213
|
+
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;
|
|
1214
|
+
if (U(e, "--target") === "api") {
|
|
1215
|
+
let t = U(e, "--api-url");
|
|
1216
|
+
if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
1217
|
+
let n = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
1218
|
+
if (!n) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET)"), 1;
|
|
1146
1219
|
let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=${i}`, { headers: { authorization: `Bearer ${n}` } }), a = await r.text();
|
|
1147
|
-
if (r.status !== 200) return
|
|
1220
|
+
if (r.status !== 200) return V.error(`api transfers: ${r.status} ${a}`), 1;
|
|
1148
1221
|
let o = JSON.parse(a);
|
|
1149
1222
|
if (e.includes("--json")) return console.log(a), 0;
|
|
1150
1223
|
let s = o.runs ?? [];
|
|
1151
|
-
if (s.length === 0) return
|
|
1152
|
-
(o.running ?? 0) > 0 &&
|
|
1153
|
-
for (let e of s)
|
|
1224
|
+
if (s.length === 0) return V.info("no data transfers recorded on this instance"), 0;
|
|
1225
|
+
(o.running ?? 0) > 0 && V.info(`${o.running} transfer(s) IN FLIGHT — re-run this to watch the counters move`);
|
|
1226
|
+
for (let e of s) V.info(` ${e.line ?? ""}`);
|
|
1154
1227
|
return 0;
|
|
1155
1228
|
}
|
|
1156
|
-
let { store: a } = await
|
|
1229
|
+
let { store: a } = await K(), o;
|
|
1157
1230
|
try {
|
|
1158
1231
|
o = await a.query({
|
|
1159
1232
|
table: n,
|
|
@@ -1167,23 +1240,23 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
1167
1240
|
take: i
|
|
1168
1241
|
});
|
|
1169
1242
|
} catch {
|
|
1170
|
-
return
|
|
1243
|
+
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;
|
|
1171
1244
|
}
|
|
1172
|
-
if (o.length === 0) return
|
|
1245
|
+
if (o.length === 0) return V.info("no data transfers recorded against this target"), 0;
|
|
1173
1246
|
for (let e of o) {
|
|
1174
1247
|
let n = e;
|
|
1175
|
-
|
|
1248
|
+
V.info(` ${t({
|
|
1176
1249
|
...e,
|
|
1177
1250
|
startedAt: new Date(n.startedAt),
|
|
1178
1251
|
finishedAt: n.finishedAt === null ? null : new Date(n.finishedAt)
|
|
1179
1252
|
})}`);
|
|
1180
1253
|
}
|
|
1181
1254
|
return 0;
|
|
1182
|
-
},
|
|
1183
|
-
let t =
|
|
1184
|
-
if (!t || !n) return
|
|
1255
|
+
}, Ht = async (e) => {
|
|
1256
|
+
let t = W(e), n = W(e.filter((e) => e !== t));
|
|
1257
|
+
if (!t || !n) return V.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
|
|
1185
1258
|
let { resolve: r } = await import("node:path"), i = Y(r(process.cwd(), t)), a = J(e);
|
|
1186
|
-
return a && (i =
|
|
1259
|
+
return a && (i = M(i, a)), await Me(i, r(process.cwd(), n)), V.info("unpack complete", {
|
|
1187
1260
|
file: t,
|
|
1188
1261
|
dir: n
|
|
1189
1262
|
}), 0;
|
|
@@ -1263,17 +1336,18 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
1263
1336
|
"--assets",
|
|
1264
1337
|
"--allow-live",
|
|
1265
1338
|
"--force",
|
|
1339
|
+
"--no-marker",
|
|
1266
1340
|
"--drill",
|
|
1267
1341
|
"--drill-url",
|
|
1268
1342
|
"--api-url"
|
|
1269
1343
|
] }
|
|
1270
|
-
},
|
|
1344
|
+
}, Ut = (e) => e.split("=")[0], Wt = (e, t) => {
|
|
1271
1345
|
let n = $[e];
|
|
1272
1346
|
if (!n) return;
|
|
1273
|
-
let r =
|
|
1347
|
+
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 ?? []);
|
|
1274
1348
|
for (let n of t) {
|
|
1275
1349
|
if (!n.startsWith("--")) continue;
|
|
1276
|
-
let t =
|
|
1350
|
+
let t = Ut(n);
|
|
1277
1351
|
if (i.has(t)) continue;
|
|
1278
1352
|
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.`;
|
|
1279
1353
|
let o = Object.entries($).filter(([n, r]) => n !== e && [
|
|
@@ -1283,24 +1357,24 @@ var Fe = /* @__PURE__ */ new Set([
|
|
|
1283
1357
|
].includes(t)).map(([e]) => e);
|
|
1284
1358
|
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.`;
|
|
1285
1359
|
}
|
|
1286
|
-
},
|
|
1287
|
-
export:
|
|
1288
|
-
import:
|
|
1289
|
-
transfers:
|
|
1290
|
-
inspect:
|
|
1291
|
-
unpack:
|
|
1292
|
-
backup:
|
|
1293
|
-
restore:
|
|
1294
|
-
"clear-replace-marker":
|
|
1295
|
-
"clear-staging":
|
|
1296
|
-
},
|
|
1360
|
+
}, Gt = {
|
|
1361
|
+
export: xt,
|
|
1362
|
+
import: Ot,
|
|
1363
|
+
transfers: Vt,
|
|
1364
|
+
inspect: Rt,
|
|
1365
|
+
unpack: Ht,
|
|
1366
|
+
backup: At,
|
|
1367
|
+
restore: Lt,
|
|
1368
|
+
"clear-replace-marker": zt,
|
|
1369
|
+
"clear-staging": Bt
|
|
1370
|
+
}, Kt = async (n) => {
|
|
1297
1371
|
let [r, ...i] = n;
|
|
1298
1372
|
if (r !== void 0 && $[r] !== void 0) {
|
|
1299
|
-
let e =
|
|
1300
|
-
if (e) return
|
|
1373
|
+
let e = Wt(r, i);
|
|
1374
|
+
if (e) return V.error(e), 2;
|
|
1301
1375
|
}
|
|
1302
|
-
let a = r === void 0 ? void 0 :
|
|
1303
|
-
return a === void 0 ? (
|
|
1376
|
+
let a = r === void 0 ? void 0 : Gt[r];
|
|
1377
|
+
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);
|
|
1304
1378
|
};
|
|
1305
1379
|
//#endregion
|
|
1306
|
-
export {
|
|
1380
|
+
export { Kt as runDataCommand };
|