@voltro/cli 0.41.0 → 0.43.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 +188 -0
- package/dist/apiBuild-B7NgSMhX.js +2 -0
- package/dist/{apiBuild-B_iH24qf.js → apiBuild-DF0f69Gf.js} +2 -2
- package/dist/bin.js +1 -1
- package/dist/{build-DUdi6F_Q.js → build-LC_IJaHL.js} +7 -7
- package/dist/{checkCommand-B2RWrpWi.js → checkCommand-DBpTSK8d.js} +1 -1
- package/dist/{checkCommand-DWL2_PPR.js → checkCommand-DDxwJCja.js} +2 -2
- package/dist/{codegenCommand-DkjoJ30Q.js → codegenCommand-LjxfK4Am.js} +2 -2
- package/dist/{codemodRunner-sFuWDETH.js → codemodRunner-Cnnz4-I7.js} +19 -4
- package/dist/{commands-D0VR1yBB.js → commands-CbQcuWsE.js} +22 -22
- package/dist/{dashboardCommand-Bro7Jwts.js → dashboardCommand-CAHjCe_9.js} +3 -3
- package/dist/{dataCommand-OKC5FscP.js → dataCommand-CrqvbVOv.js} +298 -170
- package/dist/dataProfile-Cm0YVKSy.js +18 -0
- package/dist/{dbCommand-BHRamDsy.js → dbCommand-B237qI-j.js} +361 -361
- package/dist/dbCommand-BvQeKQOb.js +2 -0
- package/dist/{dev-D4l_YOzI.js → dev-B2rZfqxw.js} +2 -2
- package/dist/{dev-DuqBJC7g.js → dev-D0E5x2Ue.js} +1 -1
- package/dist/doctorCommand-CdsctSwg.js +2 -0
- package/dist/{doctorCommand-TmZTAepc.js → doctorCommand-Ct_3BHgd.js} +226 -216
- package/dist/{dormancyCommand-CUKhRmGG.js → dormancyCommand-D1DKnYce.js} +1 -1
- package/dist/{embeddingsCommand-Db-iQM3l.js → embeddingsCommand-1sZRBKlM.js} +1 -1
- package/dist/{envCommand-DQ_URZq9.js → envCommand-7qjlQgLD.js} +2 -2
- package/dist/{evolveCommand-CghRPrGO.js → evolveCommand-D5opB48I.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BCs_ZFnl.js → infoCommand-Bs_8RU22.js} +1 -1
- package/dist/{migrate-DLtPv2_D.js → migrate-DN47p2PG.js} +1 -1
- package/dist/{runtimeTrace-jFKvOPBz.js → runtimeTrace-Wmi7V-wN.js} +1 -1
- package/dist/{sdkgen-COQqHB9V.js → sdkgen-DbTFGCjg.js} +1 -1
- package/dist/{serveCommand-Cba_M2Su.js → serveCommand-C8xzYCwa.js} +417 -411
- package/dist/serveCommand-CGNTXafv.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-fifc0xpW.js → start-BAlGcjF5.js} +1 -1
- package/dist/{start-BjcuFME_.js → start-Ds3xyn8R.js} +1 -1
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +1 -1
- package/dist/updateCommand-BvkKlV7D.js +2 -0
- package/dist/{updateCommand-HZQVBU3T.js → updateCommand-CrI9B4d7.js} +1 -1
- package/dist/{webDev-Dk8lFUzf.js → webDev-101csg6Q.js} +2 -2
- package/dist/webDev-BLKBO34K.js +2 -0
- package/dist/{webhooksCommand-D7ZpH7w0.js → webhooksCommand-BXUP5YjW.js} +1 -1
- package/package.json +23 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/authentication.md +1 -1
- package/templates/agent-docs/cli.md +75 -8
- package/templates/agent-docs/data.md +46 -11
- package/templates/agent-docs/database/migrations.md +1 -1
- package/templates/agent-docs/internationalization.md +2 -2
- package/templates/agent-docs/observability.md +1 -1
- package/templates/agent-docs/plugins/openapi.md +1 -1
- package/templates/agent-docs/plugins/versioning.md +1 -1
- package/templates/agent-docs/plugins/webhooks.md +1 -1
- package/templates/agent-docs/plugins.md +1 -1
- package/templates/agent-docs/routing.md +1 -1
- package/templates/agent-docs/scheduling.md +1 -1
- package/templates/agent-docs/security.md +2 -2
- package/templates/agent-docs/templates/apibackends.md +1 -1
- package/templates/agent-docs/whats-new.md +54 -37
- 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-RM5W5YXk.js +0 -2
- package/dist/dataProfile-dW-PsfLB.js +0 -15
- package/dist/dbCommand-C9iE1pd9.js +0 -2
- package/dist/doctorCommand-Bw3V4L1j.js +0 -2
- package/dist/serveCommand-DRbYK646.js +0 -2
- package/dist/updateCommand-B2alql9k.js +0 -2
- package/dist/webDev-D6QeXw3P.js +0 -2
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { t as e } from "./cliError-BmdYnghb.js";
|
|
2
|
-
import { c as t, i as n, o as r } from "./dev-
|
|
2
|
+
import { c as t, i as n, o as r } from "./dev-B2rZfqxw.js";
|
|
3
3
|
import { n as i, t as a } from "./cliArgs-D4p8n7EE.js";
|
|
4
4
|
import { r as o } from "./dialectDriver-czCHYpeH.js";
|
|
5
|
-
import { t as s } from "./dataProfile-
|
|
5
|
+
import { t as s } from "./dataProfile-Cm0YVKSy.js";
|
|
6
6
|
import { o as c, t as l } from "./inspectFetch-EMuhTG_9.js";
|
|
7
|
-
import { r as u } from "./dbCommand-
|
|
7
|
+
import { r as u } from "./dbCommand-B237qI-j.js";
|
|
8
8
|
import { createLogger as d } from "@voltro/logger";
|
|
9
9
|
import { Effect as f, Stream as p } from "effect";
|
|
10
10
|
import { SqlClient as m } from "@effect/sql";
|
|
11
11
|
import { declaredSnapshot as h, fingerprintSchema as g, introspectSchema as _ } from "@voltro/database/sql";
|
|
12
12
|
import { connectionIdentity as v, normaliseTarget as y, sameDatabase as b, streamTable as x } from "@voltro/database";
|
|
13
13
|
import { STORAGE_REFS_TABLE as ee, resolveStorageProvider as S } from "@voltro/plugin-storage";
|
|
14
|
-
import { assessLiveImport as te, backupArtifactName as C, backupCommand as ne, classificationFromSnapshot as re, decryptStream as w, encryptStream as ie, packBundle as T, peekBundle as E, previewMasking as ae, restoreCommand as D, runExport as O, runImport as k, runNativeStep as A, storageAssetSink as
|
|
14
|
+
import { assessLiveImport as te, backupArtifactName as C, backupCommand as ne, classificationFromSnapshot as re, decryptStream as w, encryptStream as ie, packBundle as T, peekBundle as E, previewMasking as ae, restoreCommand as D, runExport as O, runImport as k, runNativeStep as A, storageAssetSink as oe, storageAssetSource as se, storageBlobSink as ce, storageBlobSource as le, unpackBundle as ue, unpackBundleToDir as de } from "@voltro/data-transfer";
|
|
15
15
|
//#region src/backupStamp.ts
|
|
16
|
-
var
|
|
16
|
+
var j = "voltro-backup-stamp.json", fe = (e) => ({
|
|
17
17
|
stampVersion: 1,
|
|
18
18
|
dialect: e.dialect,
|
|
19
19
|
schemaFingerprint: e.schemaFingerprint,
|
|
20
20
|
voltroVersion: e.voltroVersion,
|
|
21
21
|
createdAt: e.createdAt
|
|
22
|
-
}),
|
|
22
|
+
}), M = (e) => {
|
|
23
23
|
let t;
|
|
24
24
|
try {
|
|
25
25
|
t = JSON.parse(e);
|
|
@@ -37,10 +37,10 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
37
37
|
voltroVersion: n.voltroVersion,
|
|
38
38
|
createdAt: n.createdAt
|
|
39
39
|
};
|
|
40
|
-
},
|
|
40
|
+
}, pe = (e) => {
|
|
41
41
|
let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
|
|
42
42
|
if (t === null) return {
|
|
43
|
-
warn: `no ${
|
|
43
|
+
warn: `no ${j} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
|
|
44
44
|
info: []
|
|
45
45
|
};
|
|
46
46
|
let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
|
|
@@ -51,20 +51,20 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
51
51
|
refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
|
|
52
52
|
info: i
|
|
53
53
|
};
|
|
54
|
-
},
|
|
54
|
+
}, N = (e) => {
|
|
55
55
|
try {
|
|
56
56
|
let t = new URL(e), n = t.pathname.replace(/^\//, "");
|
|
57
57
|
return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
|
|
58
58
|
} catch {
|
|
59
59
|
return e.trim();
|
|
60
60
|
}
|
|
61
|
-
},
|
|
61
|
+
}, me = (e) => {
|
|
62
62
|
let t = e.flagUrl ?? e.envUrl;
|
|
63
63
|
if (t === void 0 || t.trim() === "") return {
|
|
64
64
|
ok: !1,
|
|
65
65
|
error: "no drill target — pass `--drill-url <url>` or set `DRILL_DB_URL`. The drill restores into a THROWAWAY database (never the live one), so it needs its own connection."
|
|
66
66
|
};
|
|
67
|
-
let n =
|
|
67
|
+
let n = N(t);
|
|
68
68
|
return e.liveKeys.some((e) => e === n) ? {
|
|
69
69
|
ok: !1,
|
|
70
70
|
error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
|
|
@@ -72,7 +72,7 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
72
72
|
ok: !0,
|
|
73
73
|
url: t
|
|
74
74
|
};
|
|
75
|
-
},
|
|
75
|
+
}, he = (e) => {
|
|
76
76
|
let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
|
|
77
77
|
return r === 0 ? {
|
|
78
78
|
pass: !1,
|
|
@@ -87,24 +87,24 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
87
87
|
pass: !0,
|
|
88
88
|
lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
|
|
89
89
|
};
|
|
90
|
-
},
|
|
90
|
+
}, P = d({ scope: "voltro:data" }), ge = async () => {
|
|
91
91
|
try {
|
|
92
92
|
let { createRequire: e } = await import("node:module"), { join: t } = await import("node:path"), { readFileSync: n } = await import("node:fs"), r = e(t(process.cwd(), "package.json")).resolve("@voltro/cli/package.json");
|
|
93
93
|
return JSON.parse(n(r, "utf8")).version ?? "unknown";
|
|
94
94
|
} catch {
|
|
95
95
|
return "unknown";
|
|
96
96
|
}
|
|
97
|
-
},
|
|
97
|
+
}, F = async (e) => {
|
|
98
98
|
try {
|
|
99
|
-
return g(await
|
|
99
|
+
return g(await H(e));
|
|
100
100
|
} catch {
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
|
-
},
|
|
103
|
+
}, I = (e) => {
|
|
104
104
|
if (e.event !== "done") return;
|
|
105
105
|
let t = e.total === void 0 ? "" : ` / ${e.total}`;
|
|
106
|
-
|
|
107
|
-
},
|
|
106
|
+
P.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
|
|
107
|
+
}, _e = async (e) => {
|
|
108
108
|
let t = (e) => e?.meta?.databaseId;
|
|
109
109
|
if (e) {
|
|
110
110
|
let n = await l({
|
|
@@ -124,12 +124,12 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
return null;
|
|
127
|
-
},
|
|
128
|
-
let t = e.includes("--allow-live"), n = t ? null : await
|
|
129
|
-
dialect:
|
|
127
|
+
}, L = async (e) => {
|
|
128
|
+
let t = e.includes("--allow-live"), n = t ? null : await _e(R(e, "--api-url")), i = v({
|
|
129
|
+
dialect: B(),
|
|
130
130
|
...r()
|
|
131
131
|
}), a = y({
|
|
132
|
-
dialect:
|
|
132
|
+
dialect: B(),
|
|
133
133
|
...r()
|
|
134
134
|
}), { refuse: o, message: s } = te({
|
|
135
135
|
liveInstance: n?.label ?? null,
|
|
@@ -137,8 +137,8 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
137
137
|
sameTarget: b(i, n?.databaseId),
|
|
138
138
|
...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
|
|
139
139
|
});
|
|
140
|
-
return !o || (
|
|
141
|
-
},
|
|
140
|
+
return !o || (P.error(s ?? "refusing: live instance detected"), !1);
|
|
141
|
+
}, R = i, ve = [
|
|
142
142
|
"--api-url",
|
|
143
143
|
"--bundle-key",
|
|
144
144
|
"--compression",
|
|
@@ -153,44 +153,44 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
153
153
|
"--target",
|
|
154
154
|
"--tenant",
|
|
155
155
|
"--token"
|
|
156
|
-
],
|
|
156
|
+
], z = (e) => a(e, ve), B = () => {
|
|
157
157
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
158
158
|
if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
|
|
159
159
|
return t;
|
|
160
|
-
},
|
|
160
|
+
}, V = async (e, t) => {
|
|
161
161
|
let n = (await o(e)).makeSqlLayer(t);
|
|
162
162
|
return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
|
|
163
|
-
},
|
|
163
|
+
}, H = (e) => V(e, r()), ye = () => {
|
|
164
164
|
let e = r(), t = [];
|
|
165
|
-
return e.url && t.push(
|
|
166
|
-
},
|
|
165
|
+
return e.url && t.push(N(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
|
|
166
|
+
}, U = async () => {
|
|
167
167
|
let r = process.cwd(), i = await t(r);
|
|
168
168
|
if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
|
|
169
|
-
let a = await n(i, void 0), o =
|
|
169
|
+
let a = await n(i, void 0), o = B();
|
|
170
170
|
return {
|
|
171
171
|
store: a,
|
|
172
|
-
snapshot: await
|
|
172
|
+
snapshot: await H(o),
|
|
173
173
|
dialect: o
|
|
174
174
|
};
|
|
175
|
-
},
|
|
175
|
+
}, W = (e) => x(e, {
|
|
176
176
|
table: ee,
|
|
177
177
|
chunkSize: 500
|
|
178
178
|
}).pipe(p.map((e) => ({
|
|
179
179
|
key: String(e.key),
|
|
180
180
|
contentType: String(e.contentType ?? "application/octet-stream"),
|
|
181
181
|
size: Number(e.size ?? 0)
|
|
182
|
-
}))),
|
|
182
|
+
}))), G = (e) => !!e && e.endsWith(".vbundle"), K = (e) => R(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, be = (e) => e.includes("--encrypt") || R(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, xe = async (e, t) => {
|
|
183
183
|
let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
|
|
184
184
|
await r(i.from(e), n(t));
|
|
185
|
-
},
|
|
185
|
+
}, q = (e) => ({ async *[Symbol.asyncIterator]() {
|
|
186
186
|
let { createReadStream: t } = await import("node:fs");
|
|
187
187
|
for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
|
|
188
|
-
} }),
|
|
189
|
-
let r =
|
|
188
|
+
} }), J = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Se = (t, n) => {
|
|
189
|
+
let r = R(t, "--tenant"), i = R(t, "--tables"), a = R(t, "--exclude");
|
|
190
190
|
if (a !== void 0) {
|
|
191
191
|
if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
|
|
192
192
|
if (r !== void 0) throw new e("voltro data export: --exclude does not apply to --tenant, whose table set is derived from the tenant closure.");
|
|
193
|
-
let t =
|
|
193
|
+
let t = J(a), o = new Set(n), s = t.filter((e) => !o.has(e));
|
|
194
194
|
if (s.length > 0) throw new e(`voltro data export: --exclude names ${s.length} table(s) that do not exist: ${s.join(", ")}. An exclusion that excludes nothing would leave the run looking like it worked.`);
|
|
195
195
|
let c = new Set(t);
|
|
196
196
|
return {
|
|
@@ -203,15 +203,15 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
203
203
|
tenantId: r
|
|
204
204
|
} : i ? {
|
|
205
205
|
kind: "tables",
|
|
206
|
-
tables:
|
|
206
|
+
tables: J(i)
|
|
207
207
|
} : { kind: "all" };
|
|
208
|
-
},
|
|
208
|
+
}, Ce = async () => {
|
|
209
209
|
let e = await u(process.cwd());
|
|
210
210
|
return re(h(e));
|
|
211
|
-
},
|
|
212
|
-
let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id ===
|
|
211
|
+
}, we = "voltro/tenant", Te = async () => {
|
|
212
|
+
let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === we)).map((e) => e.tableName);
|
|
213
213
|
return e.length > 0 ? e : void 0;
|
|
214
|
-
},
|
|
214
|
+
}, Ee = (e) => {
|
|
215
215
|
for (let t of e.tables) if (t.samples.length !== 0) {
|
|
216
216
|
console.log(`\n ${t.table}`);
|
|
217
217
|
for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
|
|
@@ -224,21 +224,21 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
224
224
|
console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
|
|
225
225
|
for (let t of e.unclassified) console.log(` ${t}`);
|
|
226
226
|
} else console.log("\n ✓ every exported column is classified — safe to run.");
|
|
227
|
-
},
|
|
228
|
-
let t =
|
|
229
|
-
if (!r && !(t && n)) return
|
|
227
|
+
}, De = async (e) => {
|
|
228
|
+
let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), r = z(e);
|
|
229
|
+
if (!r && !(t && n)) return P.error("usage: voltro data export <outDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--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;
|
|
230
230
|
if (t) {
|
|
231
|
-
let t =
|
|
232
|
-
if (!t) return
|
|
233
|
-
let i =
|
|
234
|
-
if (!i) return
|
|
235
|
-
if (
|
|
236
|
-
let a =
|
|
237
|
-
...
|
|
238
|
-
...
|
|
231
|
+
let t = R(e, "--api-url");
|
|
232
|
+
if (!t) return P.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
233
|
+
let i = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
234
|
+
if (!i) return P.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
235
|
+
if (R(e, "--exclude") !== void 0) return P.error("--exclude is a direct-target flag: it expands against the live table list, which --target api does not expose. Use --tables with the explicit set."), 1;
|
|
236
|
+
let a = R(e, "--tables"), o = {
|
|
237
|
+
...R(e, "--profile") ? { profile: R(e, "--profile") } : {},
|
|
238
|
+
...R(e, "--tenant") ? { tenant: R(e, "--tenant") } : {},
|
|
239
239
|
...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
240
|
-
...
|
|
241
|
-
...
|
|
240
|
+
...R(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
|
|
241
|
+
...R(e, "--compression") ? { compression: R(e, "--compression") } : {},
|
|
242
242
|
...e.includes("--assets") ? { assets: !0 } : {},
|
|
243
243
|
...n ? { bundleKey: n } : {}
|
|
244
244
|
}, s = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, {
|
|
@@ -249,26 +249,35 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
249
249
|
},
|
|
250
250
|
body: JSON.stringify(o)
|
|
251
251
|
});
|
|
252
|
-
if (!s.ok) return
|
|
253
|
-
if (n) return
|
|
252
|
+
if (!s.ok) return P.error(`api export failed: HTTP ${s.status} — ${await s.text()}`), 1;
|
|
253
|
+
if (n) return P.info("api export complete (pushed to storage)", {
|
|
254
254
|
via: t,
|
|
255
255
|
...await s.json()
|
|
256
256
|
}), 0;
|
|
257
257
|
let { resolve: c } = await import("node:path"), l = new Uint8Array(await s.arrayBuffer());
|
|
258
|
-
return await
|
|
258
|
+
return await de((async function* () {
|
|
259
259
|
yield l;
|
|
260
|
-
})(), c(process.cwd(), r)),
|
|
260
|
+
})(), c(process.cwd(), r)), P.info("api export complete (downloaded from the running instance)", {
|
|
261
261
|
outDir: r,
|
|
262
262
|
tables: s.headers.get("x-export-tables") ?? void 0,
|
|
263
263
|
rows: s.headers.get("x-export-rows") ?? void 0,
|
|
264
264
|
masked: s.headers.get("x-export-masked") ?? void 0
|
|
265
265
|
}), 0;
|
|
266
266
|
}
|
|
267
|
-
if (!r) return
|
|
268
|
-
let i =
|
|
267
|
+
if (!r) return P.error("a <outDir> is required for a direct export"), 1;
|
|
268
|
+
let i = R(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await U(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = R(e, "--compression") ?? a.compression, m = R(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? Se(e, c.tables.map((e) => e.name)), v = h ? await Ce() : void 0, y = _.kind === "tenant" ? await Te() : void 0, b = y ? { tenantTables: y } : {};
|
|
269
269
|
if (e.includes("--dry-run")) {
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
|
|
271
|
+
if (P.info("export DRY RUN — nothing was written", {
|
|
272
|
+
outDir: r,
|
|
273
|
+
tables: e.length,
|
|
274
|
+
scope: _.kind,
|
|
275
|
+
consistency: m,
|
|
276
|
+
compression: p ?? "zstd",
|
|
277
|
+
assets: u,
|
|
278
|
+
masking: h ? "profile policy applies" : "none"
|
|
279
|
+
}), P.info(`would export: ${e.join(", ")}`), !h) return 0;
|
|
280
|
+
let t = await f.runPromise(ae({
|
|
272
281
|
store: o,
|
|
273
282
|
snapshot: c,
|
|
274
283
|
masking: h,
|
|
@@ -278,9 +287,9 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
278
287
|
...b
|
|
279
288
|
}
|
|
280
289
|
}));
|
|
281
|
-
return
|
|
290
|
+
return Ee(t), +(t.unclassified.length > 0);
|
|
282
291
|
}
|
|
283
|
-
if (
|
|
292
|
+
if (G(r)) {
|
|
284
293
|
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), s = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
|
|
285
294
|
try {
|
|
286
295
|
let t = await f.runPromise(O({
|
|
@@ -296,17 +305,17 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
296
305
|
...p ? { compression: p } : {},
|
|
297
306
|
...h ? { masking: h } : {},
|
|
298
307
|
...v ? { classification: v } : {},
|
|
299
|
-
onProgress:
|
|
308
|
+
onProgress: I
|
|
300
309
|
})), n = T({
|
|
301
310
|
dir: s,
|
|
302
|
-
...d ? { blobs:
|
|
303
|
-
}), a =
|
|
311
|
+
...d ? { blobs: le(d, () => W(o)) } : {}
|
|
312
|
+
}), a = be(e);
|
|
304
313
|
if (a) {
|
|
305
|
-
let t =
|
|
306
|
-
if (!t) return
|
|
314
|
+
let t = K(e);
|
|
315
|
+
if (!t) return P.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
|
|
307
316
|
n = ie(n, t);
|
|
308
317
|
}
|
|
309
|
-
return await
|
|
318
|
+
return await xe(n, i(process.cwd(), r)), P.info("export complete (single file)", {
|
|
310
319
|
file: r,
|
|
311
320
|
encrypted: a,
|
|
312
321
|
assets: u,
|
|
@@ -333,10 +342,10 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
333
342
|
...p ? { compression: p } : {},
|
|
334
343
|
...h ? { masking: h } : {},
|
|
335
344
|
...v ? { classification: v } : {},
|
|
336
|
-
...d ? { assets:
|
|
337
|
-
onProgress:
|
|
345
|
+
...d ? { assets: se(d, () => W(o)) } : {},
|
|
346
|
+
onProgress: I
|
|
338
347
|
}));
|
|
339
|
-
return
|
|
348
|
+
return P.info("export complete", {
|
|
340
349
|
outDir: r,
|
|
341
350
|
tables: x.tables.length,
|
|
342
351
|
rows: x.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
@@ -344,55 +353,82 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
344
353
|
compression: x.compression,
|
|
345
354
|
...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
|
|
346
355
|
}), 0;
|
|
347
|
-
},
|
|
348
|
-
let t =
|
|
349
|
-
|
|
350
|
-
|
|
356
|
+
}, Y = () => "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.", X = (e) => {
|
|
357
|
+
let t = R(e, "--tables");
|
|
358
|
+
return {
|
|
359
|
+
...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
|
|
360
|
+
...e.includes("--dry-run") ? { dryRun: !0 } : {}
|
|
361
|
+
};
|
|
362
|
+
}, Z = (e, t) => {
|
|
363
|
+
if (e.includes("--dry-run")) {
|
|
364
|
+
P.info("import DRY RUN — nothing was written", t), P.info(Y());
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
P.info("import complete", t);
|
|
368
|
+
}, Oe = (e) => {
|
|
369
|
+
let t = e;
|
|
370
|
+
if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return;
|
|
371
|
+
let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
|
|
372
|
+
P.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.`);
|
|
373
|
+
for (let e of r) P.error(` ${e.table} ${e.id} ${e.reason}`);
|
|
374
|
+
let o = i - a;
|
|
375
|
+
o > 0 && P.error(` … and ${o} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`);
|
|
376
|
+
}, ke = async (e) => {
|
|
377
|
+
let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), i = z(e);
|
|
378
|
+
if (!i && !(t && n)) return P.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--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;
|
|
379
|
+
let a = R(e, "--mode") ?? "upsert", o = R(e, "--on-conflict") ?? "skip";
|
|
351
380
|
if (t) {
|
|
352
|
-
let t =
|
|
353
|
-
if (!t) return
|
|
354
|
-
let r =
|
|
355
|
-
if (!r) return
|
|
356
|
-
let s = {
|
|
381
|
+
let t = R(e, "--api-url");
|
|
382
|
+
if (!t) return P.error("--target api requires --api-url <url> (the running instance)"), 1;
|
|
383
|
+
let r = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
|
|
384
|
+
if (!r) return P.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
|
|
385
|
+
let s = R(e, "--tables"), c = {
|
|
357
386
|
authorization: `Bearer ${r}`,
|
|
358
387
|
"x-import-mode": a,
|
|
359
388
|
"x-import-on-conflict": o,
|
|
360
389
|
...e.includes("--atomic") ? { "x-import-atomic": "1" } : {},
|
|
361
|
-
...e.includes("--force") ? { "x-import-force": "1" } : {}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
390
|
+
...e.includes("--force") ? { "x-import-force": "1" } : {},
|
|
391
|
+
...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
|
|
392
|
+
...s ? { "x-import-tables": s } : {}
|
|
393
|
+
}, { resolve: l } = await import("node:path"), u;
|
|
394
|
+
if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
|
|
395
|
+
else if (G(i)) {
|
|
396
|
+
let t = q(l(process.cwd(), i)), n = K(e);
|
|
366
397
|
n && (t = w(t, n));
|
|
367
398
|
let r = [];
|
|
368
399
|
for await (let e of t) r.push(Buffer.from(e));
|
|
369
|
-
|
|
400
|
+
c["content-type"] = "application/octet-stream", u = Buffer.concat(r);
|
|
370
401
|
} else {
|
|
371
|
-
await (await import("node:fs/promises")).access(
|
|
402
|
+
await (await import("node:fs/promises")).access(l(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && P.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.");
|
|
372
403
|
let e = [];
|
|
373
|
-
for await (let t of T({ dir:
|
|
374
|
-
|
|
404
|
+
for await (let t of T({ dir: l(process.cwd(), i) })) e.push(Buffer.from(t));
|
|
405
|
+
c["content-type"] = "application/octet-stream", u = Buffer.concat(e);
|
|
375
406
|
}
|
|
376
|
-
let
|
|
407
|
+
let d = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, {
|
|
377
408
|
method: "POST",
|
|
378
|
-
body:
|
|
379
|
-
headers:
|
|
409
|
+
body: u,
|
|
410
|
+
headers: c
|
|
380
411
|
});
|
|
381
|
-
|
|
412
|
+
if (!d.ok) return P.error(`api import failed: HTTP ${d.status} — ${await d.text()}`), 1;
|
|
413
|
+
let f = await d.json();
|
|
414
|
+
return e.includes("--dry-run") ? (P.info("api import DRY RUN — nothing was written", {
|
|
415
|
+
via: t,
|
|
416
|
+
...f
|
|
417
|
+
}), P.info(Y()), 0) : (P.info("api import complete (imported by the running instance)", {
|
|
382
418
|
via: t,
|
|
383
|
-
...
|
|
384
|
-
}), 0)
|
|
419
|
+
...f
|
|
420
|
+
}), 0);
|
|
385
421
|
}
|
|
386
|
-
if (!i) return
|
|
387
|
-
if (!await
|
|
388
|
-
let { store: s, snapshot: c } = await
|
|
389
|
-
if (
|
|
422
|
+
if (!i) return P.error("a <bundleDir> is required for a direct import"), 1;
|
|
423
|
+
if (!await L(e)) return 1;
|
|
424
|
+
let { store: s, snapshot: c } = await U(), l = e.includes("--force"), u = (e) => P.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = B() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
|
|
425
|
+
if (G(i)) {
|
|
390
426
|
let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join: m } = await import("node:path"), h = await n.mkdtemp(m(t.tmpdir(), "voltro-import-"));
|
|
391
427
|
try {
|
|
392
|
-
let t =
|
|
393
|
-
n && (t = w(t, n)), await
|
|
428
|
+
let t = q(r(process.cwd(), i)), n = K(e);
|
|
429
|
+
n && (t = w(t, n)), await ue(t, {
|
|
394
430
|
dir: h,
|
|
395
|
-
blobs:
|
|
431
|
+
blobs: ce(d ?? S({}))
|
|
396
432
|
});
|
|
397
433
|
let m = await f.runPromise(k({
|
|
398
434
|
store: s,
|
|
@@ -401,16 +437,17 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
401
437
|
onConflict: o,
|
|
402
438
|
atomic: e.includes("--atomic"),
|
|
403
439
|
verify: !e.includes("--no-verify"),
|
|
404
|
-
targetDialect:
|
|
440
|
+
targetDialect: B(),
|
|
405
441
|
allowIncompatible: l,
|
|
406
442
|
targetSnapshot: c,
|
|
407
443
|
force: l,
|
|
408
444
|
onWarn: u,
|
|
409
|
-
onProgress:
|
|
445
|
+
onProgress: I,
|
|
410
446
|
assetsAlreadyRestored: !0,
|
|
447
|
+
...X(e),
|
|
411
448
|
...p ? { copyLoader: p } : {}
|
|
412
449
|
}));
|
|
413
|
-
return
|
|
450
|
+
return Z(e, {
|
|
414
451
|
file: i,
|
|
415
452
|
tables: m.tables.length,
|
|
416
453
|
rows: m.tables.reduce((e, t) => e + t.rowCount, 0)
|
|
@@ -429,22 +466,25 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
429
466
|
onConflict: o,
|
|
430
467
|
atomic: e.includes("--atomic"),
|
|
431
468
|
verify: !e.includes("--no-verify"),
|
|
432
|
-
targetDialect:
|
|
469
|
+
targetDialect: B(),
|
|
433
470
|
allowIncompatible: l,
|
|
434
471
|
targetSnapshot: c,
|
|
435
472
|
force: l,
|
|
436
473
|
onWarn: u,
|
|
437
|
-
onProgress:
|
|
438
|
-
...
|
|
474
|
+
onProgress: I,
|
|
475
|
+
...X(e),
|
|
476
|
+
...d ? { assets: oe(d) } : {},
|
|
439
477
|
...p ? { copyLoader: p } : {}
|
|
440
|
-
})).
|
|
441
|
-
|
|
478
|
+
})).catch((e) => {
|
|
479
|
+
throw Oe(e), e;
|
|
480
|
+
}).finally(() => p?.close());
|
|
481
|
+
return Z(e, {
|
|
442
482
|
bundleDir: i,
|
|
443
483
|
tables: m.tables.length,
|
|
444
484
|
rows: m.tables.reduce((e, t) => e + t.rowCount, 0),
|
|
445
485
|
assets: m.assets.count
|
|
446
486
|
}), 0;
|
|
447
|
-
},
|
|
487
|
+
}, Q = () => {
|
|
448
488
|
let e = r();
|
|
449
489
|
return {
|
|
450
490
|
...e.url ? { url: e.url } : {},
|
|
@@ -454,98 +494,186 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
|
|
|
454
494
|
...e.password ? { password: e.password } : {},
|
|
455
495
|
...e.database ? { database: e.database } : {}
|
|
456
496
|
};
|
|
457
|
-
},
|
|
458
|
-
let t =
|
|
459
|
-
if (!t) return
|
|
460
|
-
let n =
|
|
497
|
+
}, Ae = async (e) => {
|
|
498
|
+
let t = z(e);
|
|
499
|
+
if (!t) return P.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
|
|
500
|
+
let n = B(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
|
|
461
501
|
await r(t, { recursive: !0 });
|
|
462
502
|
let o = a(t, C(n));
|
|
463
|
-
await f.runPromise(A(ne(n,
|
|
464
|
-
let s =
|
|
503
|
+
await f.runPromise(A(ne(n, Q(), o)));
|
|
504
|
+
let s = fe({
|
|
465
505
|
dialect: n,
|
|
466
|
-
schemaFingerprint: await
|
|
467
|
-
voltroVersion: await
|
|
506
|
+
schemaFingerprint: await F(n),
|
|
507
|
+
voltroVersion: await ge(),
|
|
468
508
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
469
509
|
});
|
|
470
|
-
return await i(a(t,
|
|
510
|
+
return await i(a(t, j), `${JSON.stringify(s, null, 2)}\n`, "utf8"), P.info("native backup complete", {
|
|
471
511
|
dialect: n,
|
|
472
512
|
artifact: o,
|
|
473
|
-
stamp:
|
|
513
|
+
stamp: j,
|
|
474
514
|
schemaFingerprint: s.schemaFingerprint ?? "<none>",
|
|
475
515
|
assets: "NOT included — use `voltro data export --assets` for rows + blobs"
|
|
476
516
|
}), 0;
|
|
477
|
-
},
|
|
478
|
-
let n =
|
|
479
|
-
flagUrl:
|
|
517
|
+
}, je = async (e, t) => {
|
|
518
|
+
let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = me({
|
|
519
|
+
flagUrl: R(t, "--drill-url"),
|
|
480
520
|
envUrl: process.env.DRILL_DB_URL,
|
|
481
|
-
liveKeys:
|
|
521
|
+
liveKeys: ye()
|
|
482
522
|
});
|
|
483
|
-
if (!o.ok) return
|
|
484
|
-
let s = await i(r(e,
|
|
485
|
-
if (s && s.dialect !== n) return
|
|
486
|
-
|
|
523
|
+
if (!o.ok) return P.error(`restore --drill: ${o.error}`), 1;
|
|
524
|
+
let s = await i(r(e, j), "utf8").then(M, () => null);
|
|
525
|
+
if (s && s.dialect !== n) return P.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
|
|
526
|
+
P.info("restore drill: restoring into throwaway DB", {
|
|
487
527
|
dialect: n,
|
|
488
|
-
target:
|
|
528
|
+
target: N(o.url)
|
|
489
529
|
}), await f.runPromise(A(D(n, { url: o.url }, a)));
|
|
490
530
|
let c = null, l = 0;
|
|
491
531
|
try {
|
|
492
|
-
let e = await
|
|
532
|
+
let e = await V(n, { url: o.url });
|
|
493
533
|
c = g(e), l = e.tables.length;
|
|
494
534
|
} catch (e) {
|
|
495
|
-
return
|
|
535
|
+
return P.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
|
|
496
536
|
}
|
|
497
|
-
let u =
|
|
537
|
+
let u = he({
|
|
498
538
|
stampFingerprint: s?.schemaFingerprint ?? null,
|
|
499
539
|
restoredFingerprint: c,
|
|
500
540
|
restoredTableCount: l
|
|
501
541
|
});
|
|
502
|
-
for (let e of u.lines) u.pass ?
|
|
542
|
+
for (let e of u.lines) u.pass ? P.info(`restore drill: ${e}`) : P.error(`restore drill: ${e}`);
|
|
503
543
|
return +!u.pass;
|
|
504
|
-
},
|
|
505
|
-
let t =
|
|
506
|
-
if (!t) return
|
|
507
|
-
if (e.includes("--drill")) return
|
|
508
|
-
if (!await
|
|
509
|
-
let n =
|
|
510
|
-
stamp: await i(r(t,
|
|
544
|
+
}, Me = async (e) => {
|
|
545
|
+
let t = z(e);
|
|
546
|
+
if (!t) return P.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
|
|
547
|
+
if (e.includes("--drill")) return je(t, e);
|
|
548
|
+
if (!await L(e)) return 1;
|
|
549
|
+
let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = pe({
|
|
550
|
+
stamp: await i(r(t, j), "utf8").then(M, () => null),
|
|
511
551
|
currentDialect: n,
|
|
512
|
-
currentFingerprint: await
|
|
552
|
+
currentFingerprint: await F(n)
|
|
513
553
|
});
|
|
514
|
-
for (let e of o.info)
|
|
515
|
-
if (o.warn &&
|
|
516
|
-
if (!e.includes("--force")) return
|
|
517
|
-
|
|
554
|
+
for (let e of o.info) P.info(`backup stamp: ${e}`);
|
|
555
|
+
if (o.warn && P.warn(o.warn), o.refuse) {
|
|
556
|
+
if (!e.includes("--force")) return P.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
|
|
557
|
+
P.warn(`restore proceeding despite: ${o.refuse} (--force)`);
|
|
518
558
|
}
|
|
519
|
-
return await f.runPromise(A(D(n,
|
|
559
|
+
return await f.runPromise(A(D(n, Q(), a))), P.info("native restore complete", {
|
|
520
560
|
dialect: n,
|
|
521
561
|
artifact: a
|
|
522
562
|
}), 0;
|
|
523
|
-
},
|
|
524
|
-
let t =
|
|
525
|
-
if (!t) return
|
|
526
|
-
let { resolve: n } = await import("node:path"),
|
|
527
|
-
|
|
528
|
-
|
|
563
|
+
}, Ne = async (e) => {
|
|
564
|
+
let t = z(e);
|
|
565
|
+
if (!t) return P.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
|
|
566
|
+
let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
|
|
567
|
+
if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
|
|
568
|
+
let n = r(a, "manifest.json"), o = r(a, "README.md");
|
|
569
|
+
if (!e.includes("--json")) {
|
|
570
|
+
let e = await i.readFile(o, "utf8").catch(() => void 0);
|
|
571
|
+
if (e !== void 0) return console.log(e), 0;
|
|
572
|
+
}
|
|
573
|
+
let s = await i.readFile(n, "utf8").catch(() => void 0);
|
|
574
|
+
return s === void 0 ? (P.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);
|
|
575
|
+
}
|
|
576
|
+
let o = K(e), s = () => {
|
|
577
|
+
let e = q(a);
|
|
578
|
+
return o ? w(e, o) : e;
|
|
529
579
|
};
|
|
530
580
|
if (e.includes("--json")) {
|
|
531
|
-
let e = (await E(
|
|
532
|
-
return e ? (console.log(e.toString("utf8")), 0) : (
|
|
581
|
+
let e = (await E(s(), ["manifest.json"])).get("manifest.json");
|
|
582
|
+
return e ? (console.log(e.toString("utf8")), 0) : (P.error("no manifest.json found in the bundle"), 1);
|
|
533
583
|
}
|
|
534
|
-
let
|
|
535
|
-
if (
|
|
536
|
-
let
|
|
537
|
-
return
|
|
538
|
-
},
|
|
539
|
-
let t =
|
|
540
|
-
if (!t || !n) return
|
|
541
|
-
let { resolve: r } = await import("node:path"), i =
|
|
542
|
-
return a && (i = w(i, a)), await
|
|
584
|
+
let c = (await E(s(), ["README.md"])).get("README.md");
|
|
585
|
+
if (c) return console.log(c.toString("utf8")), 0;
|
|
586
|
+
let l = (await E(s(), ["manifest.json"])).get("manifest.json");
|
|
587
|
+
return l ? (console.log(l.toString("utf8")), 0) : (P.error("bundle has no README.md or manifest.json"), 1);
|
|
588
|
+
}, Pe = async (e) => {
|
|
589
|
+
let t = z(e), n = z(e.filter((e) => e !== t));
|
|
590
|
+
if (!t || !n) return P.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
|
|
591
|
+
let { resolve: r } = await import("node:path"), i = q(r(process.cwd(), t)), a = K(e);
|
|
592
|
+
return a && (i = w(i, a)), await de(i, r(process.cwd(), n)), P.info("unpack complete", {
|
|
543
593
|
file: t,
|
|
544
594
|
dir: n
|
|
545
595
|
}), 0;
|
|
546
|
-
},
|
|
596
|
+
}, $ = {
|
|
597
|
+
export: {
|
|
598
|
+
common: [
|
|
599
|
+
"--target",
|
|
600
|
+
"--tables",
|
|
601
|
+
"--profile",
|
|
602
|
+
"--tenant",
|
|
603
|
+
"--assets",
|
|
604
|
+
"--compression",
|
|
605
|
+
"--consistency",
|
|
606
|
+
"--json"
|
|
607
|
+
],
|
|
608
|
+
directOnly: [
|
|
609
|
+
"--exclude",
|
|
610
|
+
"--passphrase",
|
|
611
|
+
"--dry-run",
|
|
612
|
+
"--encrypt"
|
|
613
|
+
],
|
|
614
|
+
apiOnly: [
|
|
615
|
+
"--api-url",
|
|
616
|
+
"--token",
|
|
617
|
+
"--bundle-key"
|
|
618
|
+
]
|
|
619
|
+
},
|
|
620
|
+
import: {
|
|
621
|
+
common: [
|
|
622
|
+
"--target",
|
|
623
|
+
"--tables",
|
|
624
|
+
"--mode",
|
|
625
|
+
"--on-conflict",
|
|
626
|
+
"--atomic",
|
|
627
|
+
"--assets",
|
|
628
|
+
"--force",
|
|
629
|
+
"--dry-run",
|
|
630
|
+
"--json"
|
|
631
|
+
],
|
|
632
|
+
directOnly: [
|
|
633
|
+
"--no-verify",
|
|
634
|
+
"--allow-live",
|
|
635
|
+
"--passphrase"
|
|
636
|
+
],
|
|
637
|
+
apiOnly: [
|
|
638
|
+
"--api-url",
|
|
639
|
+
"--token",
|
|
640
|
+
"--bundle-key"
|
|
641
|
+
]
|
|
642
|
+
},
|
|
643
|
+
unpack: { common: ["--passphrase", "--json"] },
|
|
644
|
+
inspect: { common: ["--json", "--passphrase"] },
|
|
645
|
+
backup: { common: ["--json", "--assets"] },
|
|
646
|
+
restore: { common: [
|
|
647
|
+
"--json",
|
|
648
|
+
"--allow-live",
|
|
649
|
+
"--force",
|
|
650
|
+
"--drill",
|
|
651
|
+
"--drill-url",
|
|
652
|
+
"--api-url"
|
|
653
|
+
] }
|
|
654
|
+
}, Fe = (e) => e.split("=")[0], Ie = (e, t) => {
|
|
655
|
+
let n = $[e];
|
|
656
|
+
if (!n) return;
|
|
657
|
+
let r = R(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
|
|
658
|
+
for (let n of t) {
|
|
659
|
+
if (!n.startsWith("--")) continue;
|
|
660
|
+
let t = Fe(n);
|
|
661
|
+
if (i.has(t)) continue;
|
|
662
|
+
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.`;
|
|
663
|
+
let o = Object.entries($).filter(([n, r]) => n !== e && [
|
|
664
|
+
...r.common,
|
|
665
|
+
...r.directOnly ?? [],
|
|
666
|
+
...r.apiOnly ?? []
|
|
667
|
+
].includes(t)).map(([e]) => e);
|
|
668
|
+
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.`;
|
|
669
|
+
}
|
|
670
|
+
}, Le = async (e) => {
|
|
547
671
|
let [t, ...n] = e;
|
|
548
|
-
|
|
672
|
+
if (t !== void 0 && $[t] !== void 0) {
|
|
673
|
+
let e = Ie(t, n);
|
|
674
|
+
if (e) return P.error(e), 2;
|
|
675
|
+
}
|
|
676
|
+
return t === "export" ? De(n) : t === "import" ? ke(n) : t === "unpack" ? Pe(n) : t === "inspect" ? Ne(n) : t === "backup" ? Ae(n) : t === "restore" ? Me(n) : (P.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
|
|
549
677
|
};
|
|
550
678
|
//#endregion
|
|
551
|
-
export {
|
|
679
|
+
export { Le as runDataCommand };
|