@voltro/cli 0.41.0 → 0.42.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/bin.js +1 -1
  3. package/dist/{commands-D0VR1yBB.js → commands-DoTClnSk.js} +4 -4
  4. package/dist/{dataCommand-OKC5FscP.js → dataCommand-Rj57y1vg.js} +286 -168
  5. package/dist/dataProfile-Cm0YVKSy.js +18 -0
  6. package/dist/{dbCommand-BHRamDsy.js → dbCommand-B237qI-j.js} +361 -361
  7. package/dist/dbCommand-BvQeKQOb.js +2 -0
  8. package/dist/index.js +1 -1
  9. package/dist/{migrate-DLtPv2_D.js → migrate-DN47p2PG.js} +1 -1
  10. package/dist/{serveCommand-Cba_M2Su.js → serveCommand-C512kzZA.js} +415 -409
  11. package/dist/serveCommand-gj7MsqXY.js +2 -0
  12. package/dist/serveEntry.js +1 -1
  13. package/package.json +17 -17
  14. package/templates/AGENTS.md +1 -1
  15. package/templates/agent-docs/_index.md +1 -1
  16. package/templates/agent-docs/cli.md +73 -6
  17. package/templates/agent-docs/data.md +33 -0
  18. package/templates/agent-docs/whats-new.md +59 -36
  19. package/templates/apps/api-ai/package.json +7 -7
  20. package/templates/apps/api-auth/package.json +8 -8
  21. package/templates/apps/api-backend/package.json +7 -7
  22. package/templates/apps/api-backend-deactivation/package.json +7 -7
  23. package/templates/apps/api-backend-mail/package.json +8 -8
  24. package/templates/apps/api-backend-mariadb/package.json +9 -9
  25. package/templates/apps/api-backend-sqlite/package.json +8 -8
  26. package/templates/apps/api-backend-storage/package.json +8 -8
  27. package/templates/apps/api-cms/package.json +10 -10
  28. package/templates/apps/api-collab/package.json +8 -8
  29. package/templates/apps/api-data-advanced/package.json +8 -8
  30. package/templates/apps/api-durable/package.json +8 -8
  31. package/templates/apps/api-feature-flags/package.json +9 -9
  32. package/templates/apps/api-governance/package.json +8 -8
  33. package/templates/apps/api-kv/package.json +8 -8
  34. package/templates/apps/api-moderation/package.json +8 -8
  35. package/templates/apps/api-observability/package.json +8 -8
  36. package/templates/apps/api-ratelimit/package.json +8 -8
  37. package/templates/apps/api-rbac/package.json +8 -8
  38. package/templates/apps/api-rest/package.json +7 -7
  39. package/templates/apps/api-saas/package.json +11 -11
  40. package/templates/apps/api-saas-starter/package.json +10 -10
  41. package/templates/apps/api-search/package.json +8 -8
  42. package/templates/apps/api-status/package.json +8 -8
  43. package/templates/apps/api-versioning/package.json +8 -8
  44. package/templates/apps/api-webhooks/package.json +9 -9
  45. package/templates/apps/changelog/package.json +6 -6
  46. package/templates/apps/edge-functions/package.json +2 -2
  47. package/templates/apps/frontend-admin/package.json +8 -8
  48. package/templates/apps/frontend-app/package.json +9 -9
  49. package/templates/apps/frontend-auth/package.json +8 -8
  50. package/templates/apps/frontend-blank/package.json +7 -7
  51. package/templates/apps/frontend-cms/package.json +9 -9
  52. package/templates/apps/frontend-collab/package.json +10 -10
  53. package/templates/apps/frontend-contact/package.json +7 -7
  54. package/templates/apps/frontend-dashboard/package.json +7 -7
  55. package/templates/apps/frontend-docs/package.json +7 -7
  56. package/templates/apps/frontend-i18n/package.json +6 -6
  57. package/templates/apps/frontend-landing/package.json +7 -7
  58. package/templates/apps/frontend-portal/package.json +8 -8
  59. package/templates/apps/frontend-saas/package.json +8 -8
  60. package/templates/apps/frontend-spa/package.json +7 -7
  61. package/templates/apps/frontend-ssr/package.json +7 -7
  62. package/templates/apps/frontend-ssr-api/package.json +8 -8
  63. package/templates/apps/frontend-static-blog/package.json +6 -6
  64. package/templates/apps/frontend-status/package.json +8 -8
  65. package/templates/apps/mobile-app/package.json +4 -4
  66. package/dist/dataProfile-dW-PsfLB.js +0 -15
  67. package/dist/dbCommand-C9iE1pd9.js +0 -2
  68. package/dist/serveCommand-DRbYK646.js +0 -2
@@ -2,24 +2,24 @@ import { t as e } from "./cliError-BmdYnghb.js";
2
2
  import { c as t, i as n, o as r } from "./dev-D4l_YOzI.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-dW-PsfLB.js";
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-BHRamDsy.js";
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 j, storageAssetSource as oe, storageBlobSink as se, storageBlobSource as ce, unpackBundle as le, unpackBundleToDir as M } from "@voltro/data-transfer";
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 j } from "@voltro/data-transfer";
15
15
  //#region src/backupStamp.ts
16
- var N = "voltro-backup-stamp.json", ue = (e) => ({
16
+ var M = "voltro-backup-stamp.json", de = (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
- }), P = (e) => {
22
+ }), N = (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
- }, de = (e) => {
40
+ }, fe = (e) => {
41
41
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
42
42
  if (t === null) return {
43
- warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
43
+ warn: `no ${M} 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
- }, F = (e) => {
54
+ }, P = (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
- }, fe = (e) => {
61
+ }, pe = (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 = F(t);
67
+ let n = P(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
- }, pe = (e) => {
75
+ }, me = (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
- }, I = d({ scope: "voltro:data" }), me = async () => {
90
+ }, F = d({ scope: "voltro:data" }), he = 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
- }, L = async (e) => {
97
+ }, I = async (e) => {
98
98
  try {
99
- return g(await G(e));
99
+ return g(await U(e));
100
100
  } catch {
101
101
  return null;
102
102
  }
103
- }, R = (e) => {
103
+ }, L = (e) => {
104
104
  if (e.event !== "done") return;
105
105
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
106
- I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
107
- }, z = async (e) => {
106
+ F.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
107
+ }, ge = 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
- }, B = async (e) => {
128
- let t = e.includes("--allow-live"), n = t ? null : await z(V(e, "--api-url")), i = v({
129
- dialect: U(),
127
+ }, R = async (e) => {
128
+ let t = e.includes("--allow-live"), n = t ? null : await ge(z(e, "--api-url")), i = v({
129
+ dialect: V(),
130
130
  ...r()
131
131
  }), a = y({
132
- dialect: U(),
132
+ dialect: V(),
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 || (I.error(s ?? "refusing: live instance detected"), !1);
141
- }, V = i, he = [
140
+ return !o || (F.error(s ?? "refusing: live instance detected"), !1);
141
+ }, z = i, _e = [
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
- ], H = (e) => a(e, he), U = () => {
156
+ ], B = (e) => a(e, _e), V = () => {
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
- }, W = async (e, t) => {
160
+ }, H = 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
- }, G = (e) => W(e, r()), ge = () => {
163
+ }, U = (e) => H(e, r()), ve = () => {
164
164
  let e = r(), t = [];
165
- return e.url && t.push(F(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
166
- }, K = async () => {
165
+ return e.url && t.push(P(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
166
+ }, ye = 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 = U();
169
+ let a = await n(i, void 0), o = V();
170
170
  return {
171
171
  store: a,
172
- snapshot: await G(o),
172
+ snapshot: await U(o),
173
173
  dialect: o
174
174
  };
175
- }, q = (e) => x(e, {
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
- }))), J = (e) => !!e && e.endsWith(".vbundle"), Y = (e) => V(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, _e = (e) => e.includes("--encrypt") || V(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, ve = async (e, t) => {
182
+ }))), G = (e) => !!e && e.endsWith(".vbundle"), K = (e) => z(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, be = (e) => e.includes("--encrypt") || z(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
- }, X = (e) => ({ async *[Symbol.asyncIterator]() {
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
- } }), Z = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), ye = (t, n) => {
189
- let r = V(t, "--tenant"), i = V(t, "--tables"), a = V(t, "--exclude");
188
+ } }), J = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Se = (t, n) => {
189
+ let r = z(t, "--tenant"), i = z(t, "--tables"), a = z(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 = Z(a), o = new Set(n), s = t.filter((e) => !o.has(e));
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: Z(i)
206
+ tables: J(i)
207
207
  } : { kind: "all" };
208
- }, be = async () => {
208
+ }, Ce = async () => {
209
209
  let e = await u(process.cwd());
210
210
  return re(h(e));
211
- }, xe = "voltro/tenant", Se = async () => {
212
- let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === xe)).map((e) => e.tableName);
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
- }, Ce = (e) => {
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
- }, we = async (e) => {
228
- let t = V(e, "--target") === "api", n = V(e, "--bundle-key"), r = H(e);
229
- if (!r && !(t && n)) return I.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;
227
+ }, De = async (e) => {
228
+ let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), r = B(e);
229
+ if (!r && !(t && n)) return F.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 = V(e, "--api-url");
232
- if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
233
- let i = V(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
234
- if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
235
- if (V(e, "--exclude") !== void 0) return I.error("--exclude is a direct-target flag: it expands against the live table list, which --target api does not expose. Use --tables with the explicit set."), 1;
236
- let a = V(e, "--tables"), o = {
237
- ...V(e, "--profile") ? { profile: V(e, "--profile") } : {},
238
- ...V(e, "--tenant") ? { tenant: V(e, "--tenant") } : {},
231
+ let t = z(e, "--api-url");
232
+ if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
233
+ let i = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
234
+ if (!i) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
235
+ if (z(e, "--exclude") !== void 0) return F.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 = z(e, "--tables"), o = {
237
+ ...z(e, "--profile") ? { profile: z(e, "--profile") } : {},
238
+ ...z(e, "--tenant") ? { tenant: z(e, "--tenant") } : {},
239
239
  ...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
240
- ...V(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
241
- ...V(e, "--compression") ? { compression: V(e, "--compression") } : {},
240
+ ...z(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
241
+ ...z(e, "--compression") ? { compression: z(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 I.error(`api export failed: HTTP ${s.status} — ${await s.text()}`), 1;
253
- if (n) return I.info("api export complete (pushed to storage)", {
252
+ if (!s.ok) return F.error(`api export failed: HTTP ${s.status} — ${await s.text()}`), 1;
253
+ if (n) return F.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 M((async function* () {
258
+ return await j((async function* () {
259
259
  yield l;
260
- })(), c(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
260
+ })(), c(process.cwd(), r)), F.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 I.error("a <outDir> is required for a direct export"), 1;
268
- let i = V(e, "--profile"), a = i ? await s(i) : {}, { store: o, snapshot: c, dialect: l } = await K(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = V(e, "--compression") ?? a.compression, m = V(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? ye(e, c.tables.map((e) => e.name)), v = h ? await be() : void 0, y = _.kind === "tenant" ? await Se() : void 0, b = y ? { tenantTables: y } : {};
267
+ if (!r) return F.error("a <outDir> is required for a direct export"), 1;
268
+ let i = z(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await ye(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = z(e, "--compression") ?? a.compression, m = z(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
- if (!h) return I.error("--dry-run previews masking, but the profile sets no masking policy"), 1;
271
- let e = await f.runPromise(ae({
270
+ let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
271
+ if (F.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
+ }), F.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 Ce(e), +(e.unclassified.length > 0);
290
+ return Ee(t), +(t.unclassified.length > 0);
282
291
  }
283
- if (J(r)) {
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: R
308
+ onProgress: L
300
309
  })), n = T({
301
310
  dir: s,
302
- ...d ? { blobs: ce(d, () => q(o)) } : {}
303
- }), a = _e(e);
311
+ ...d ? { blobs: le(d, () => W(o)) } : {}
312
+ }), a = be(e);
304
313
  if (a) {
305
- let t = Y(e);
306
- if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
314
+ let t = K(e);
315
+ if (!t) return F.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 ve(n, i(process.cwd(), r)), I.info("export complete (single file)", {
318
+ return await xe(n, i(process.cwd(), r)), F.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: oe(d, () => q(o)) } : {},
337
- onProgress: R
345
+ ...d ? { assets: se(d, () => W(o)) } : {},
346
+ onProgress: L
338
347
  }));
339
- return I.info("export complete", {
348
+ return F.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,74 @@ 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
- }, Q = async (e) => {
348
- let t = V(e, "--target") === "api", n = V(e, "--bundle-key"), i = H(e);
349
- if (!i && !(t && n)) return I.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--bundle-key <storage-key>] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] (--force: import despite schema drift AND cross-dialect warnings)"), 1;
350
- let a = V(e, "--mode") ?? "upsert", o = V(e, "--on-conflict") ?? "skip";
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 = z(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
+ F.info("import DRY RUN — nothing was written", t), F.info(Y());
365
+ return;
366
+ }
367
+ F.info("import complete", t);
368
+ }, Oe = async (e) => {
369
+ let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
370
+ if (!i && !(t && n)) return F.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;
371
+ let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
351
372
  if (t) {
352
- let t = V(e, "--api-url");
353
- if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
354
- let r = V(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
355
- if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
356
- let s = {
373
+ let t = z(e, "--api-url");
374
+ if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
375
+ let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
376
+ if (!r) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
377
+ let s = z(e, "--tables"), c = {
357
378
  authorization: `Bearer ${r}`,
358
379
  "x-import-mode": a,
359
380
  "x-import-on-conflict": o,
360
381
  ...e.includes("--atomic") ? { "x-import-atomic": "1" } : {},
361
- ...e.includes("--force") ? { "x-import-force": "1" } : {}
362
- }, { resolve: c } = await import("node:path"), l;
363
- if (n) s["content-type"] = "application/json", l = JSON.stringify({ bundleKey: n });
364
- else if (J(i)) {
365
- let t = X(c(process.cwd(), i)), n = Y(e);
382
+ ...e.includes("--force") ? { "x-import-force": "1" } : {},
383
+ ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
384
+ ...s ? { "x-import-tables": s } : {}
385
+ }, { resolve: l } = await import("node:path"), u;
386
+ if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
387
+ else if (G(i)) {
388
+ let t = q(l(process.cwd(), i)), n = K(e);
366
389
  n && (t = w(t, n));
367
390
  let r = [];
368
391
  for await (let e of t) r.push(Buffer.from(e));
369
- s["content-type"] = "application/octet-stream", l = Buffer.concat(r);
392
+ c["content-type"] = "application/octet-stream", u = Buffer.concat(r);
370
393
  } else {
371
- await (await import("node:fs/promises")).access(c(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && I.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API.");
394
+ await (await import("node:fs/promises")).access(l(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && F.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
395
  let e = [];
373
- for await (let t of T({ dir: c(process.cwd(), i) })) e.push(Buffer.from(t));
374
- s["content-type"] = "application/octet-stream", l = Buffer.concat(e);
396
+ for await (let t of T({ dir: l(process.cwd(), i) })) e.push(Buffer.from(t));
397
+ c["content-type"] = "application/octet-stream", u = Buffer.concat(e);
375
398
  }
376
- let u = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, {
399
+ let d = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, {
377
400
  method: "POST",
378
- body: l,
379
- headers: s
401
+ body: u,
402
+ headers: c
380
403
  });
381
- return u.ok ? (I.info("api import complete (imported by the running instance)", {
404
+ if (!d.ok) return F.error(`api import failed: HTTP ${d.status} ${await d.text()}`), 1;
405
+ let f = await d.json();
406
+ return e.includes("--dry-run") ? (F.info("api import DRY RUN — nothing was written", {
407
+ via: t,
408
+ ...f
409
+ }), F.info(Y()), 0) : (F.info("api import complete (imported by the running instance)", {
382
410
  via: t,
383
- ...await u.json()
384
- }), 0) : (I.error(`api import failed: HTTP ${u.status} — ${await u.text()}`), 1);
411
+ ...f
412
+ }), 0);
385
413
  }
386
- if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
387
- if (!await B(e)) return 1;
388
- let { store: s, snapshot: c } = await K(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = U() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
389
- if (J(i)) {
414
+ if (!i) return F.error("a <bundleDir> is required for a direct import"), 1;
415
+ if (!await R(e)) return 1;
416
+ let { store: s, snapshot: c } = await ye(), l = e.includes("--force"), u = (e) => F.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = V() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
417
+ if (G(i)) {
390
418
  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
419
  try {
392
- let t = X(r(process.cwd(), i)), n = Y(e);
393
- n && (t = w(t, n)), await le(t, {
420
+ let t = q(r(process.cwd(), i)), n = K(e);
421
+ n && (t = w(t, n)), await ue(t, {
394
422
  dir: h,
395
- blobs: se(d ?? S({}))
423
+ blobs: ce(d ?? S({}))
396
424
  });
397
425
  let m = await f.runPromise(k({
398
426
  store: s,
@@ -401,16 +429,17 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
401
429
  onConflict: o,
402
430
  atomic: e.includes("--atomic"),
403
431
  verify: !e.includes("--no-verify"),
404
- targetDialect: U(),
432
+ targetDialect: V(),
405
433
  allowIncompatible: l,
406
434
  targetSnapshot: c,
407
435
  force: l,
408
436
  onWarn: u,
409
- onProgress: R,
437
+ onProgress: L,
410
438
  assetsAlreadyRestored: !0,
439
+ ...X(e),
411
440
  ...p ? { copyLoader: p } : {}
412
441
  }));
413
- return I.info("import complete (single file)", {
442
+ return Z(e, {
414
443
  file: i,
415
444
  tables: m.tables.length,
416
445
  rows: m.tables.reduce((e, t) => e + t.rowCount, 0)
@@ -429,22 +458,23 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
429
458
  onConflict: o,
430
459
  atomic: e.includes("--atomic"),
431
460
  verify: !e.includes("--no-verify"),
432
- targetDialect: U(),
461
+ targetDialect: V(),
433
462
  allowIncompatible: l,
434
463
  targetSnapshot: c,
435
464
  force: l,
436
465
  onWarn: u,
437
- onProgress: R,
438
- ...d ? { assets: j(d) } : {},
466
+ onProgress: L,
467
+ ...X(e),
468
+ ...d ? { assets: oe(d) } : {},
439
469
  ...p ? { copyLoader: p } : {}
440
470
  })).finally(() => p?.close());
441
- return I.info("import complete", {
471
+ return Z(e, {
442
472
  bundleDir: i,
443
473
  tables: m.tables.length,
444
474
  rows: m.tables.reduce((e, t) => e + t.rowCount, 0),
445
475
  assets: m.assets.count
446
476
  }), 0;
447
- }, $ = () => {
477
+ }, Q = () => {
448
478
  let e = r();
449
479
  return {
450
480
  ...e.url ? { url: e.url } : {},
@@ -454,98 +484,186 @@ var N = "voltro-backup-stamp.json", ue = (e) => ({
454
484
  ...e.password ? { password: e.password } : {},
455
485
  ...e.database ? { database: e.database } : {}
456
486
  };
457
- }, Te = async (e) => {
458
- let t = H(e);
459
- if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
460
- let n = U(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
487
+ }, ke = async (e) => {
488
+ let t = B(e);
489
+ if (!t) return F.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
490
+ let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
461
491
  await r(t, { recursive: !0 });
462
492
  let o = a(t, C(n));
463
- await f.runPromise(A(ne(n, $(), o)));
464
- let s = ue({
493
+ await f.runPromise(A(ne(n, Q(), o)));
494
+ let s = de({
465
495
  dialect: n,
466
- schemaFingerprint: await L(n),
467
- voltroVersion: await me(),
496
+ schemaFingerprint: await I(n),
497
+ voltroVersion: await he(),
468
498
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
469
499
  });
470
- return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
500
+ return await i(a(t, M), `${JSON.stringify(s, null, 2)}\n`, "utf8"), F.info("native backup complete", {
471
501
  dialect: n,
472
502
  artifact: o,
473
- stamp: N,
503
+ stamp: M,
474
504
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
475
505
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
476
506
  }), 0;
477
- }, Ee = async (e, t) => {
478
- let n = U(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = fe({
479
- flagUrl: V(t, "--drill-url"),
507
+ }, Ae = async (e, t) => {
508
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = pe({
509
+ flagUrl: z(t, "--drill-url"),
480
510
  envUrl: process.env.DRILL_DB_URL,
481
- liveKeys: ge()
511
+ liveKeys: ve()
482
512
  });
483
- if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
484
- let s = await i(r(e, N), "utf8").then(P, () => null);
485
- if (s && s.dialect !== n) return I.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
486
- I.info("restore drill: restoring into throwaway DB", {
513
+ if (!o.ok) return F.error(`restore --drill: ${o.error}`), 1;
514
+ let s = await i(r(e, M), "utf8").then(N, () => null);
515
+ if (s && s.dialect !== n) return F.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
516
+ F.info("restore drill: restoring into throwaway DB", {
487
517
  dialect: n,
488
- target: F(o.url)
518
+ target: P(o.url)
489
519
  }), await f.runPromise(A(D(n, { url: o.url }, a)));
490
520
  let c = null, l = 0;
491
521
  try {
492
- let e = await W(n, { url: o.url });
522
+ let e = await H(n, { url: o.url });
493
523
  c = g(e), l = e.tables.length;
494
524
  } catch (e) {
495
- return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
525
+ return F.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
496
526
  }
497
- let u = pe({
527
+ let u = me({
498
528
  stampFingerprint: s?.schemaFingerprint ?? null,
499
529
  restoredFingerprint: c,
500
530
  restoredTableCount: l
501
531
  });
502
- for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
532
+ for (let e of u.lines) u.pass ? F.info(`restore drill: ${e}`) : F.error(`restore drill: ${e}`);
503
533
  return +!u.pass;
504
- }, De = async (e) => {
505
- let t = H(e);
506
- if (!t) return I.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
507
- if (e.includes("--drill")) return Ee(t, e);
508
- if (!await B(e)) return 1;
509
- let n = U(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = de({
510
- stamp: await i(r(t, N), "utf8").then(P, () => null),
534
+ }, je = async (e) => {
535
+ let t = B(e);
536
+ if (!t) return F.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
537
+ if (e.includes("--drill")) return Ae(t, e);
538
+ if (!await R(e)) return 1;
539
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = fe({
540
+ stamp: await i(r(t, M), "utf8").then(N, () => null),
511
541
  currentDialect: n,
512
- currentFingerprint: await L(n)
542
+ currentFingerprint: await I(n)
513
543
  });
514
- for (let e of o.info) I.info(`backup stamp: ${e}`);
515
- if (o.warn && I.warn(o.warn), o.refuse) {
516
- if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
517
- I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
544
+ for (let e of o.info) F.info(`backup stamp: ${e}`);
545
+ if (o.warn && F.warn(o.warn), o.refuse) {
546
+ if (!e.includes("--force")) return F.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
547
+ F.warn(`restore proceeding despite: ${o.refuse} (--force)`);
518
548
  }
519
- return await f.runPromise(A(D(n, $(), a))), I.info("native restore complete", {
549
+ return await f.runPromise(A(D(n, Q(), a))), F.info("native restore complete", {
520
550
  dialect: n,
521
551
  artifact: a
522
552
  }), 0;
523
- }, Oe = async (e) => {
524
- let t = H(e);
525
- if (!t) return I.error("usage: voltro data inspect <file.vbundle> [--json] [--passphrase <p>]"), 1;
526
- let { resolve: n } = await import("node:path"), r = n(process.cwd(), t), i = Y(e), a = () => {
527
- let e = X(r);
528
- return i ? w(e, i) : e;
553
+ }, Me = async (e) => {
554
+ let t = B(e);
555
+ if (!t) return F.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
556
+ let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
557
+ if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
558
+ let n = r(a, "manifest.json"), o = r(a, "README.md");
559
+ if (!e.includes("--json")) {
560
+ let e = await i.readFile(o, "utf8").catch(() => void 0);
561
+ if (e !== void 0) return console.log(e), 0;
562
+ }
563
+ let s = await i.readFile(n, "utf8").catch(() => void 0);
564
+ return s === void 0 ? (F.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);
565
+ }
566
+ let o = K(e), s = () => {
567
+ let e = q(a);
568
+ return o ? w(e, o) : e;
529
569
  };
530
570
  if (e.includes("--json")) {
531
- let e = (await E(a(), ["manifest.json"])).get("manifest.json");
532
- return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
571
+ let e = (await E(s(), ["manifest.json"])).get("manifest.json");
572
+ return e ? (console.log(e.toString("utf8")), 0) : (F.error("no manifest.json found in the bundle"), 1);
533
573
  }
534
- let o = (await E(a(), ["README.md"])).get("README.md");
535
- if (o) return console.log(o.toString("utf8")), 0;
536
- let s = (await E(a(), ["manifest.json"])).get("manifest.json");
537
- return s ? (console.log(s.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
538
- }, ke = async (e) => {
539
- let t = H(e), n = H(e.filter((e) => e !== t));
540
- if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
541
- let { resolve: r } = await import("node:path"), i = X(r(process.cwd(), t)), a = Y(e);
542
- return a && (i = w(i, a)), await M(i, r(process.cwd(), n)), I.info("unpack complete", {
574
+ let c = (await E(s(), ["README.md"])).get("README.md");
575
+ if (c) return console.log(c.toString("utf8")), 0;
576
+ let l = (await E(s(), ["manifest.json"])).get("manifest.json");
577
+ return l ? (console.log(l.toString("utf8")), 0) : (F.error("bundle has no README.md or manifest.json"), 1);
578
+ }, Ne = async (e) => {
579
+ let t = B(e), n = B(e.filter((e) => e !== t));
580
+ if (!t || !n) return F.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
581
+ let { resolve: r } = await import("node:path"), i = q(r(process.cwd(), t)), a = K(e);
582
+ return a && (i = w(i, a)), await j(i, r(process.cwd(), n)), F.info("unpack complete", {
543
583
  file: t,
544
584
  dir: n
545
585
  }), 0;
546
- }, Ae = async (e) => {
586
+ }, $ = {
587
+ export: {
588
+ common: [
589
+ "--target",
590
+ "--tables",
591
+ "--profile",
592
+ "--tenant",
593
+ "--assets",
594
+ "--compression",
595
+ "--consistency",
596
+ "--json"
597
+ ],
598
+ directOnly: [
599
+ "--exclude",
600
+ "--passphrase",
601
+ "--dry-run",
602
+ "--encrypt"
603
+ ],
604
+ apiOnly: [
605
+ "--api-url",
606
+ "--token",
607
+ "--bundle-key"
608
+ ]
609
+ },
610
+ import: {
611
+ common: [
612
+ "--target",
613
+ "--tables",
614
+ "--mode",
615
+ "--on-conflict",
616
+ "--atomic",
617
+ "--assets",
618
+ "--force",
619
+ "--dry-run",
620
+ "--json"
621
+ ],
622
+ directOnly: [
623
+ "--no-verify",
624
+ "--allow-live",
625
+ "--passphrase"
626
+ ],
627
+ apiOnly: [
628
+ "--api-url",
629
+ "--token",
630
+ "--bundle-key"
631
+ ]
632
+ },
633
+ unpack: { common: ["--passphrase", "--json"] },
634
+ inspect: { common: ["--json", "--passphrase"] },
635
+ backup: { common: ["--json", "--assets"] },
636
+ restore: { common: [
637
+ "--json",
638
+ "--allow-live",
639
+ "--force",
640
+ "--drill",
641
+ "--drill-url",
642
+ "--api-url"
643
+ ] }
644
+ }, Pe = (e) => e.split("=")[0], Fe = (e, t) => {
645
+ let n = $[e];
646
+ if (!n) return;
647
+ let r = z(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
648
+ for (let n of t) {
649
+ if (!n.startsWith("--")) continue;
650
+ let t = Pe(n);
651
+ if (i.has(t)) continue;
652
+ 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.`;
653
+ let o = Object.entries($).filter(([n, r]) => n !== e && [
654
+ ...r.common,
655
+ ...r.directOnly ?? [],
656
+ ...r.apiOnly ?? []
657
+ ].includes(t)).map(([e]) => e);
658
+ 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.`;
659
+ }
660
+ }, Ie = async (e) => {
547
661
  let [t, ...n] = e;
548
- return t === "export" ? we(n) : t === "import" ? Q(n) : t === "unpack" ? ke(n) : t === "inspect" ? Oe(n) : t === "backup" ? Te(n) : t === "restore" ? De(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
662
+ if (t !== void 0 && $[t] !== void 0) {
663
+ let e = Fe(t, n);
664
+ if (e) return F.error(e), 2;
665
+ }
666
+ return t === "export" ? De(n) : t === "import" ? Oe(n) : t === "unpack" ? Ne(n) : t === "inspect" ? Me(n) : t === "backup" ? ke(n) : t === "restore" ? je(n) : (F.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
549
667
  };
550
668
  //#endregion
551
- export { Ae as runDataCommand };
669
+ export { Ie as runDataCommand };