@voltro/cli 0.44.1 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +280 -1
  2. package/dist/{apiBuild-DvWreIQP.js → apiBuild-BKdd0I1Z.js} +2 -2
  3. package/dist/apiBuild-P2gewrfQ.js +2 -0
  4. package/dist/bin.d.ts +24 -0
  5. package/dist/bin.js +46 -11
  6. package/dist/{build-Ckc-FA7d.js → build-co6yadYz.js} +8 -8
  7. package/dist/{checkCommand-BNfEs4KG.js → checkCommand-B-eiW-G_.js} +12 -12
  8. package/dist/{checkCommand-AlbawIal.js → checkCommand-CXAXTjZL.js} +1 -1
  9. package/dist/codegen-DcE2li1i.js +2 -0
  10. package/dist/{codegen-CbpGCWLG.js → codegen-sbN1Y8PB.js} +37 -37
  11. package/dist/codegenCommand-CB6_3sX0.js +144 -0
  12. package/dist/{codemodRunner-BKTnaGOz.js → codemodRunner-DSZjgyCL.js} +49 -0
  13. package/dist/{commands-CYkHTox2.js → commands-C-hxgAGr.js} +24 -24
  14. package/dist/{dashboardCommand-DHDmlo9A.js → dashboardCommand-CEVO0GWF.js} +3 -3
  15. package/dist/{dataCommand-WFn8CM11.js → dataCommand-k0ezbQWa.js} +355 -250
  16. package/dist/{dbCommand-B237qI-j.js → dbCommand-BwkyNxn-.js} +110 -110
  17. package/dist/dbCommand-C2XK63Dd.js +2 -0
  18. package/dist/{dev-D-JZ6CGQ.js → dev-8hTEnN6s.js} +2154 -2076
  19. package/dist/{dev-BZO5aLdr.js → dev-DyoYst4j.js} +1 -1
  20. package/dist/devActivity.d.ts +24 -0
  21. package/dist/{doctorCommand-BlUyESpD.js → doctorCommand-B6D7HH1-.js} +249 -228
  22. package/dist/doctorCommand-Btl2Jz1a.js +2 -0
  23. package/dist/doctorSourceRelations-CPjSwYuZ.js +83 -0
  24. package/dist/{dormancyCommand-lQxdrU8C.js → dormancyCommand-CrPAwOgo.js} +1 -1
  25. package/dist/{embeddingsCommand-ByBTju6X.js → embeddingsCommand-DsSxsn_b.js} +1 -1
  26. package/dist/{envCommand-CpCbYAgR.js → envCommand-B_NPVGUf.js} +2 -2
  27. package/dist/{evalCommand-6RUfPen4.js → evalCommand-Db01Kbng.js} +24 -24
  28. package/dist/{evolveCommand-CBV3-TtK.js → evolveCommand-DUuaCIyn.js} +2 -2
  29. package/dist/{frameworkTableAssembly-oMPBKqlE.js → frameworkTableAssembly-BPlPfU07.js} +82 -78
  30. package/dist/frameworkTableAssembly-Bob3vj9R.js +2 -0
  31. package/dist/index.d.ts +24 -0
  32. package/dist/index.js +1 -1
  33. package/dist/{infoCommand-DJoA9cKD.js → infoCommand-CuNl9cbh.js} +1 -1
  34. package/dist/inspectState.d.ts +24 -0
  35. package/dist/interruptedReplace-C1ZI84xi.js +27 -0
  36. package/dist/interruptedReplace-DNJ7MKph.js +2 -0
  37. package/dist/{migrate-DN47p2PG.js → migrate-CM1qwalu.js} +27 -27
  38. package/dist/{runtimeTrace-DJ9tPhrc.js → runtimeTrace-BRNM06vK.js} +1 -1
  39. package/dist/{sdkgen-7dr8-Onw.js → sdkgen-CRCw_XcR.js} +1 -1
  40. package/dist/{seedRunner-IdHEprqf.js → seedRunner-Dgsiwk_e.js} +59 -59
  41. package/dist/serveCommand-BNHMVXyp.js +2 -0
  42. package/dist/serveCommand-CHOr74WM.js +1928 -0
  43. package/dist/serveEntry.d.ts +24 -0
  44. package/dist/serveEntry.js +1 -1
  45. package/dist/{start-Ds3xyn8R.js → start-7HcSIKEp.js} +1 -1
  46. package/dist/{start-BAlGcjF5.js → start-DvlYDyAr.js} +1 -1
  47. package/dist/startEntry.d.ts +24 -0
  48. package/dist/startEntry.js +1 -1
  49. package/dist/startup.d.ts +24 -0
  50. package/dist/{test-CLYXrZ2F.js → test-CHb7UwvE.js} +1 -1
  51. package/dist/{updateCommand-BvRidHp6.js → updateCommand-BlyXavoG.js} +1 -1
  52. package/dist/updateCommand-CT5AvVg7.js +2 -0
  53. package/dist/{webDev-101csg6Q.js → webDev-B4IEMB0n.js} +6 -6
  54. package/dist/webDev-B79ji8M5.js +2 -0
  55. package/dist/{webhooksCommand-BfO_JF2b.js → webhooksCommand-BRrG_sGf.js} +1 -1
  56. package/package.json +29 -17
  57. package/templates/AGENTS.md +1 -1
  58. package/templates/agent-docs/_index.md +1 -1
  59. package/templates/agent-docs/cli.md +179 -3
  60. package/templates/agent-docs/data.md +103 -7
  61. package/templates/agent-docs/plugins/versioning.md +2 -0
  62. package/templates/agent-docs/whats-new.md +166 -6
  63. package/templates/apps/api-ai/package.json +7 -7
  64. package/templates/apps/api-auth/package.json +8 -8
  65. package/templates/apps/api-backend/package.json +7 -7
  66. package/templates/apps/api-backend-deactivation/package.json +7 -7
  67. package/templates/apps/api-backend-mail/package.json +8 -8
  68. package/templates/apps/api-backend-mariadb/package.json +9 -9
  69. package/templates/apps/api-backend-sqlite/package.json +8 -8
  70. package/templates/apps/api-backend-storage/package.json +8 -8
  71. package/templates/apps/api-cms/package.json +10 -10
  72. package/templates/apps/api-collab/package.json +8 -8
  73. package/templates/apps/api-data-advanced/package.json +8 -8
  74. package/templates/apps/api-durable/package.json +8 -8
  75. package/templates/apps/api-feature-flags/package.json +9 -9
  76. package/templates/apps/api-governance/package.json +8 -8
  77. package/templates/apps/api-kv/package.json +8 -8
  78. package/templates/apps/api-moderation/package.json +8 -8
  79. package/templates/apps/api-observability/package.json +8 -8
  80. package/templates/apps/api-ratelimit/package.json +8 -8
  81. package/templates/apps/api-rbac/package.json +8 -8
  82. package/templates/apps/api-rest/package.json +7 -7
  83. package/templates/apps/api-saas/package.json +11 -11
  84. package/templates/apps/api-saas-starter/package.json +10 -10
  85. package/templates/apps/api-search/package.json +8 -8
  86. package/templates/apps/api-status/package.json +8 -8
  87. package/templates/apps/api-versioning/package.json +8 -8
  88. package/templates/apps/api-webhooks/package.json +9 -9
  89. package/templates/apps/changelog/package.json +6 -6
  90. package/templates/apps/edge-functions/package.json +2 -2
  91. package/templates/apps/frontend-admin/package.json +8 -8
  92. package/templates/apps/frontend-app/package.json +9 -9
  93. package/templates/apps/frontend-auth/package.json +8 -8
  94. package/templates/apps/frontend-blank/package.json +7 -7
  95. package/templates/apps/frontend-cms/package.json +9 -9
  96. package/templates/apps/frontend-collab/package.json +10 -10
  97. package/templates/apps/frontend-contact/package.json +7 -7
  98. package/templates/apps/frontend-dashboard/package.json +7 -7
  99. package/templates/apps/frontend-docs/package.json +7 -7
  100. package/templates/apps/frontend-i18n/package.json +6 -6
  101. package/templates/apps/frontend-landing/package.json +7 -7
  102. package/templates/apps/frontend-portal/package.json +8 -8
  103. package/templates/apps/frontend-saas/package.json +8 -8
  104. package/templates/apps/frontend-spa/package.json +7 -7
  105. package/templates/apps/frontend-ssr/package.json +7 -7
  106. package/templates/apps/frontend-ssr-api/package.json +8 -8
  107. package/templates/apps/frontend-static-blog/package.json +6 -6
  108. package/templates/apps/frontend-status/package.json +8 -8
  109. package/templates/apps/mobile-app/package.json +4 -4
  110. package/dist/apiBuild-XaW40W8x.js +0 -2
  111. package/dist/codegen-i8QGcHsi.js +0 -2
  112. package/dist/codegenCommand-iClXoRCW.js +0 -129
  113. package/dist/dbCommand-BvQeKQOb.js +0 -2
  114. package/dist/doctorCommand-BeLOwq7S.js +0 -2
  115. package/dist/frameworkTableAssembly-DgylYh9_.js +0 -2
  116. package/dist/serveCommand-BlnAlNQg.js +0 -1891
  117. package/dist/serveCommand-Dg_E1ev4.js +0 -2
  118. package/dist/updateCommand-DcKVmLVN.js +0 -2
  119. package/dist/webDev-BLKBO34K.js +0 -2
@@ -1,29 +1,51 @@
1
1
  import { t as e } from "./cliError-BmdYnghb.js";
2
- import { c as t, i as n, o as r } from "./dev-D-JZ6CGQ.js";
3
- import { n as i, t as a } from "./cliArgs-D4p8n7EE.js";
4
- import { r as o } from "./dialectDriver-czCHYpeH.js";
5
- import { t as s } from "./dataProfile-Cm0YVKSy.js";
6
- import { o as c, t as l } from "./inspectFetch-EMuhTG_9.js";
7
- import { r as u } from "./dbCommand-B237qI-j.js";
8
- import { createLogger as d } from "@voltro/logger";
9
- import { Effect as f, Stream as p } from "effect";
10
- import { SqlClient as m } from "@effect/sql";
11
- import { declaredSnapshot as h, fingerprintSchema as g, introspectSchema as _ } from "@voltro/database/sql";
12
- import { connectionIdentity as v, normaliseTarget as y, sameDatabase as b, streamTable as x } from "@voltro/database";
13
- import { STORAGE_REFS_TABLE as ee, resolveStorageProvider as S } from "@voltro/plugin-storage";
14
- import { createHash as te } from "node:crypto";
15
- import { assessLiveImport as ne, backupArtifactName as C, backupCommand as re, classificationFromSnapshot as ie, decryptStream as w, encryptStream as ae, packBundle as T, peekBundle as E, previewMasking as oe, restoreCommand as D, runExport as se, runImport as ce, runNativeStep as O, storageAssetSink as le, storageAssetSource as ue, storageBlobSink as de, storageBlobSource as fe, unpackBundle as pe, unpackBundleToDir as k } from "@voltro/data-transfer";
16
- import { request as me } from "node:http";
17
- import { request as he } from "node:https";
2
+ import { c as t, i as n, o as r, z as i } from "./dev-8hTEnN6s.js";
3
+ import { n as a, t as o } from "./cliArgs-D4p8n7EE.js";
4
+ import { r as s } from "./dialectDriver-czCHYpeH.js";
5
+ import { t as c } from "./dataProfile-Cm0YVKSy.js";
6
+ import { o as l, t as u } from "./inspectFetch-EMuhTG_9.js";
7
+ import { r as d } from "./dbCommand-BwkyNxn-.js";
8
+ import { resolve as f } from "node:path";
9
+ import { createLogger as p } from "@voltro/logger";
10
+ import { connectionIdentity as m, normaliseTarget as h, sameDatabase as g, streamTable as _ } from "@voltro/database";
11
+ import { Effect as v, Stream as y } from "effect";
12
+ import { SqlClient as b } from "@effect/sql";
13
+ import { declaredSnapshot as x, fingerprintSchema as ee, introspectSchema as te } from "@voltro/database/sql";
14
+ import { STORAGE_REFS_TABLE as ne, resolveStorageProvider as S } from "@voltro/plugin-storage";
15
+ import { createHash as re } from "node:crypto";
16
+ import { asImportError as ie, assessLiveImport as ae, backupArtifactName as C, backupCommand as oe, classificationFromSnapshot as se, decryptStream as w, encryptStream as ce, packBundle as le, peekBundle as T, previewMasking as ue, restoreCommand as de, runExport as E, runImport as D, runNativeStep as O, storageAssetSink as fe, storageAssetSource as pe, storageBlobSink as me, storageBlobSource as he, unpackBundle as ge, unpackBundleToDir as k } from "@voltro/data-transfer";
17
+ import { request as _e } from "node:http";
18
+ import { request as ve } from "node:https";
18
19
  //#region src/longRequest.ts
19
- var A = class extends Error {
20
+ var ye = /* @__PURE__ */ new Set([
21
+ "ECONNREFUSED",
22
+ "ETIMEDOUT",
23
+ "EHOSTUNREACH",
24
+ "ENETUNREACH",
25
+ "ENOTFOUND",
26
+ "ECONNRESET",
27
+ "EAI_AGAIN"
28
+ ]), A = class extends Error {
29
+ url;
30
+ cause;
31
+ constructor(e, t) {
32
+ let n = (() => {
33
+ try {
34
+ return new URL(e).host;
35
+ } catch {
36
+ return e;
37
+ }
38
+ })();
39
+ super(`the instance at ${n} is not answering (${t.code ?? "connect failed"}).`), this.url = e, this.cause = t, this.name = "InstanceUnreachable";
40
+ }
41
+ }, j = class extends Error {
20
42
  elapsedMs;
21
43
  url;
22
44
  constructor(e, t) {
23
45
  super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
24
46
  }
25
- }, j = async (e, t, n, r) => {
26
- let i = new URL(e), a = i.protocol === "https:" ? he : me, o = Date.now();
47
+ }, M = async (e, t, n, r) => {
48
+ let i = new URL(e), a = i.protocol === "https:" ? ve : _e, o = Date.now();
27
49
  return await new Promise((s, c) => {
28
50
  let l = a({
29
51
  protocol: i.protocol,
@@ -41,8 +63,8 @@ var A = class extends Error {
41
63
  })), e.on("error", c);
42
64
  });
43
65
  if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
44
- l.destroy(new A(Date.now() - o, e));
45
- }), l.on("error", c), typeof t == "string" || Buffer.isBuffer(t)) {
66
+ l.destroy(new j(Date.now() - o, e));
67
+ }), l.on("error", (t) => c(ye.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
46
68
  l.end(t);
47
69
  return;
48
70
  }
@@ -55,7 +77,7 @@ var A = class extends Error {
55
77
  }
56
78
  })();
57
79
  });
58
- }, ge = (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.", _e = 16777216, ve = async function* (e, t) {
80
+ }, be = (e) => `${e.message}\n That address came from --api-url (or VOLTRO_API_URL) — it is the running instance, not the database.\n Nothing was sent, so nothing was imported. Check the instance is up and reachable from here\n (a VPN, an ingress or a stopped deployment are the usual three), e.g. curl -sS ${e.url}/_voltro/inspect/app`, xe = (e, t) => `the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. This is a client-side deadline, not a failure on the server: the upload itself finished long before, and the response arrives only when the import does. The instance is very probably STILL IMPORTING and will finish.\n` + (t === "replace" ? " Do NOT simply re-run: --mode replace empties the target first, so a second run started while the first is mid-flight would empty it under the first one. Check the instance's log for the import's completion, and check the target's row counts, before deciding." : ` Re-running the same bundle is safe once the first run has finished (--mode ${t} is idempotent), but check the instance's log first — the work may already be done.`) + "\n To keep a deadline anyway, pass --timeout <seconds>; the default is to wait.", Se = 16777216, Ce = async function* (e, t) {
59
81
  let n = [], r = 0, i = 0, a = function* (e) {
60
82
  let t = Buffer.concat(n);
61
83
  n = [], r = 0;
@@ -77,12 +99,12 @@ var A = class extends Error {
77
99
  i += a.byteLength, yield s;
78
100
  }
79
101
  yield* a(!0);
80
- }, ye = (e) => te("sha256").update(Buffer.from(e)).digest("hex"), be = async (e) => {
102
+ }, we = (e) => re("sha256").update(Buffer.from(e)).digest("hex"), Te = async (e) => {
81
103
  let t = 0, n = 0, r = {
82
104
  status: 0,
83
105
  body: {}
84
106
  };
85
- for await (let i of ve(e.stream, e.chunkBytes)) {
107
+ for await (let i of Ce(e.stream, e.chunkBytes)) {
86
108
  if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
87
109
  n++;
88
110
  continue;
@@ -108,13 +130,13 @@ var A = class extends Error {
108
130
  bytes: t,
109
131
  resumedFrom: e.alreadyHeld
110
132
  };
111
- }, M = "voltro-backup-stamp.json", xe = (e) => ({
133
+ }, N = "voltro-backup-stamp.json", Ee = (e) => ({
112
134
  stampVersion: 1,
113
135
  dialect: e.dialect,
114
136
  schemaFingerprint: e.schemaFingerprint,
115
137
  voltroVersion: e.voltroVersion,
116
138
  createdAt: e.createdAt
117
- }), N = (e) => {
139
+ }), P = (e) => {
118
140
  let t;
119
141
  try {
120
142
  t = JSON.parse(e);
@@ -132,10 +154,10 @@ var A = class extends Error {
132
154
  voltroVersion: n.voltroVersion,
133
155
  createdAt: n.createdAt
134
156
  };
135
- }, Se = (e) => {
157
+ }, De = (e) => {
136
158
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
137
159
  if (t === null) return {
138
- warn: `no ${M} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
160
+ warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
139
161
  info: []
140
162
  };
141
163
  let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
@@ -146,20 +168,20 @@ var A = class extends Error {
146
168
  refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
147
169
  info: i
148
170
  };
149
- }, P = (e) => {
171
+ }, F = (e) => {
150
172
  try {
151
173
  let t = new URL(e), n = t.pathname.replace(/^\//, "");
152
174
  return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
153
175
  } catch {
154
176
  return e.trim();
155
177
  }
156
- }, Ce = (e) => {
178
+ }, Oe = (e) => {
157
179
  let t = e.flagUrl ?? e.envUrl;
158
180
  if (t === void 0 || t.trim() === "") return {
159
181
  ok: !1,
160
182
  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."
161
183
  };
162
- let n = P(t);
184
+ let n = F(t);
163
185
  return e.liveKeys.some((e) => e === n) ? {
164
186
  ok: !1,
165
187
  error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
@@ -167,7 +189,7 @@ var A = class extends Error {
167
189
  ok: !0,
168
190
  url: t
169
191
  };
170
- }, we = (e) => {
192
+ }, ke = (e) => {
171
193
  let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
172
194
  return r === 0 ? {
173
195
  pass: !1,
@@ -182,27 +204,27 @@ var A = class extends Error {
182
204
  pass: !0,
183
205
  lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
184
206
  };
185
- }, F = d({ scope: "voltro:data" }), Te = async () => {
207
+ }, I = p({ scope: "voltro:data" }), Ae = async () => {
186
208
  try {
187
209
  let { createRequire: e } = await import("node:module"), { join: t } = await import("node:path"), { readFileSync: n } = await import("node:fs"), r = e(t(process.cwd(), "package.json")).resolve("@voltro/cli/package.json");
188
210
  return JSON.parse(n(r, "utf8")).version ?? "unknown";
189
211
  } catch {
190
212
  return "unknown";
191
213
  }
192
- }, I = async (e) => {
214
+ }, L = async (e) => {
193
215
  try {
194
- return g(await U(e));
216
+ return ee(await U(e));
195
217
  } catch {
196
218
  return null;
197
219
  }
198
- }, L = (e) => {
220
+ }, R = (e) => {
199
221
  if (e.event !== "done") return;
200
222
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
201
- F.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
202
- }, Ee = async (e) => {
223
+ I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
224
+ }, je = async (e) => {
203
225
  let t = (e) => e?.meta?.databaseId;
204
226
  if (e) {
205
- let n = await l({
227
+ let n = await u({
206
228
  name: "target",
207
229
  url: e
208
230
  }, "/_voltro/inspect/app");
@@ -211,29 +233,29 @@ var A = class extends Error {
211
233
  databaseId: t(n.data)
212
234
  };
213
235
  }
214
- for (let e of await c()) {
215
- let n = await l(e, "/_voltro/inspect/app");
236
+ for (let e of await l()) {
237
+ let n = await u(e, "/_voltro/inspect/app");
216
238
  if (n.kind === "ok") return {
217
239
  label: `${e.name} (${e.url})`,
218
240
  databaseId: t(n.data)
219
241
  };
220
242
  }
221
243
  return null;
222
- }, R = async (e) => {
223
- let t = e.includes("--allow-live"), n = t ? null : await Ee(z(e, "--api-url")), i = v({
244
+ }, Me = async (e) => {
245
+ let t = e.includes("--allow-live"), n = t ? null : await je(z(e, "--api-url")), i = m({
224
246
  dialect: V(),
225
247
  ...r()
226
- }), a = y({
248
+ }), a = h({
227
249
  dialect: V(),
228
250
  ...r()
229
- }), { refuse: o, message: s } = ne({
251
+ }), { refuse: o, message: s } = ae({
230
252
  liveInstance: n?.label ?? null,
231
253
  allowLive: t,
232
- sameTarget: b(i, n?.databaseId),
254
+ sameTarget: g(i, n?.databaseId),
233
255
  ...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
234
256
  });
235
- return !o || (F.error(s ?? "refusing: live instance detected"), !1);
236
- }, z = i, De = [
257
+ return !o || (I.error(s ?? "refusing: live instance detected"), !1);
258
+ }, z = a, Ne = [
237
259
  "--api-url",
238
260
  "--bundle-key",
239
261
  "--compression",
@@ -251,16 +273,16 @@ var A = class extends Error {
251
273
  "--timeout",
252
274
  "--tenant",
253
275
  "--token"
254
- ], B = (e) => a(e, De), V = () => {
276
+ ], B = (e) => o(e, Ne), V = () => {
255
277
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
256
278
  if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
257
279
  return t;
258
280
  }, H = async (e, t) => {
259
- let n = (await o(e)).makeSqlLayer(t);
260
- return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
261
- }, U = (e) => H(e, r()), Oe = () => {
281
+ let n = (await s(e)).makeSqlLayer(t);
282
+ return v.runPromise(b.SqlClient.pipe(v.flatMap(te), v.provide(n)));
283
+ }, U = (e) => H(e, r()), Pe = () => {
262
284
  let e = r(), t = [];
263
- return e.url && t.push(P(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
285
+ return e.url && t.push(F(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
264
286
  }, W = async () => {
265
287
  let r = process.cwd(), i = await t(r);
266
288
  if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
@@ -270,20 +292,20 @@ var A = class extends Error {
270
292
  snapshot: await U(o),
271
293
  dialect: o
272
294
  };
273
- }, G = (e) => x(e, {
274
- table: ee,
295
+ }, G = (e) => _(e, {
296
+ table: ne,
275
297
  chunkSize: 500
276
- }).pipe(p.map((e) => ({
298
+ }).pipe(y.map((e) => ({
277
299
  key: String(e.key),
278
300
  contentType: String(e.contentType ?? "application/octet-stream"),
279
301
  size: Number(e.size ?? 0)
280
- }))), K = (e) => !!e && e.endsWith(".vbundle"), q = (e) => z(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, ke = (e) => e.includes("--encrypt") || z(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ae = async (e, t) => {
302
+ }))), K = (e) => !!e && e.endsWith(".vbundle"), q = (e) => z(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Fe = (e) => e.includes("--encrypt") || z(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ie = async (e, t) => {
281
303
  let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
282
304
  await r(i.from(e), n(t));
283
305
  }, J = (e) => ({ async *[Symbol.asyncIterator]() {
284
306
  let { createReadStream: t } = await import("node:fs");
285
307
  for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
286
- } }), Y = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), je = (t, n) => {
308
+ } }), Y = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Le = (t, n) => {
287
309
  let r = z(t, "--tenant"), i = z(t, "--tables"), a = z(t, "--exclude");
288
310
  if (a !== void 0) {
289
311
  if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
@@ -303,13 +325,13 @@ var A = class extends Error {
303
325
  kind: "tables",
304
326
  tables: Y(i)
305
327
  } : { kind: "all" };
306
- }, Me = async () => {
307
- let e = await u(process.cwd());
308
- return ie(h(e));
309
- }, Ne = "voltro/tenant", Pe = async () => {
310
- let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Ne)).map((e) => e.tableName);
328
+ }, Re = async () => {
329
+ let e = await d(process.cwd());
330
+ return se(x(e));
331
+ }, ze = "voltro/tenant", Be = async () => {
332
+ let e = (await d(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === ze)).map((e) => e.tableName);
311
333
  return e.length > 0 ? e : void 0;
312
- }, Fe = (e) => {
334
+ }, Ve = (e) => {
313
335
  for (let t of e.tables) if (t.samples.length !== 0) {
314
336
  console.log(`\n ${t.table}`);
315
337
  for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
@@ -322,8 +344,8 @@ var A = class extends Error {
322
344
  console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
323
345
  for (let t of e.unclassified) console.log(` ${t}`);
324
346
  } else console.log("\n ✓ every exported column is classified — safe to run.");
325
- }, Ie = async (t) => {
326
- let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = z(t.args, "--chunk-size"), o = a === void 0 ? _e : Math.round(Number(a) * 1024 * 1024);
347
+ }, He = async (t) => {
348
+ let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = z(t.args, "--chunk-size"), o = a === void 0 ? Se : Math.round(Number(a) * 1024 * 1024);
327
349
  if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
328
350
  let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
329
351
  for (; l <= o;) {
@@ -338,11 +360,11 @@ var A = class extends Error {
338
360
  kind: "small",
339
361
  body: Buffer.concat(c)
340
362
  };
341
- let d = n(process.cwd(), t.bundleDir), f = ye(K(t.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await j(t.url, Buffer.alloc(0), {
363
+ let d = n(process.cwd(), t.bundleDir), f = we(K(t.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await M(t.url, Buffer.alloc(0), {
342
364
  ...t.headers,
343
365
  "x-import-upload-id": p,
344
366
  "x-import-probe": "1"
345
- }, Q(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
367
+ }, Z(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
346
368
  h > 0 && t.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
347
369
  let g = (async function* () {
348
370
  for (let e of c) yield e;
@@ -351,7 +373,7 @@ var A = class extends Error {
351
373
  if (e.done === !0) return;
352
374
  yield e.value;
353
375
  }
354
- })(), _ = 0, v = await be({
376
+ })(), _ = 0, v = await Te({
355
377
  stream: g,
356
378
  chunkBytes: o,
357
379
  uploadId: p,
@@ -365,7 +387,7 @@ var A = class extends Error {
365
387
  t.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.");
366
388
  },
367
389
  target: { send: async (e, n) => {
368
- let r = await j(t.url, e, n, Q(t.args)), i = {};
390
+ let r = await M(t.url, e, n, Z(t.args)), i = {};
369
391
  try {
370
392
  i = JSON.parse(r.body.toString("utf8") || "{}");
371
393
  } catch {}
@@ -387,21 +409,32 @@ var A = class extends Error {
387
409
  body: Buffer.from(JSON.stringify(v.body))
388
410
  }
389
411
  };
390
- }, X = (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.`, Z = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", Q = (t) => {
412
+ }, Ue = (e) => `--mode replace with --no-atomic against a ${e ? "RUNNING instance" : "live database"}: the target is empty between the delete and the load, and anything the application writes in that window can collide with a row from the bundle — or survive the import as a row the bundle never carried. Drop --no-atomic to run the whole load in one transaction (concurrent writers then wait instead of racing), or stop the traffic for the duration.`, X = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", We = (e, t, n) => {
413
+ if (t !== "replace" || e.includes("--no-rollback")) return;
414
+ let r = z(e, "--rollback-dir");
415
+ return r === void 0 ? i(n) : f(r);
416
+ }, Ge = (e, t, n) => {
417
+ let r = We(e, t, n);
418
+ return {
419
+ ...r === void 0 ? {} : { rollbackDir: r },
420
+ recordInterruption: !0,
421
+ via: "direct"
422
+ };
423
+ }, Z = (t) => {
391
424
  let n = z(t, "--timeout");
392
425
  if (n === void 0) return {};
393
426
  let r = Number(n);
394
427
  if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
395
428
  return { timeoutMs: Math.round(r * 1e3) };
396
- }, Le = async (e) => {
429
+ }, Ke = async (e) => {
397
430
  let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), r = B(e);
398
- if (!r && !(t && n)) return F.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;
431
+ if (!r && !(t && n)) return I.error("usage: voltro data export <outDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--profile <name>] [--tenant <id>] [--tables a,b] [--exclude a,b] [--bundle-key <storage-key>] [--assets] [--compression zstd|gzip|none] [--consistency live|snapshot] [--dry-run]"), 1;
399
432
  if (t) {
400
433
  let t = z(e, "--api-url");
401
- if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
434
+ if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
402
435
  let i = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
403
- if (!i) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
404
- 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;
436
+ if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
437
+ if (z(e, "--exclude") !== void 0) return I.error("--exclude is a direct-target flag: it expands against the live table list, which --target api does not expose. Use --tables with the explicit set."), 1;
405
438
  let a = z(e, "--tables"), o = {
406
439
  ...z(e, "--profile") ? { profile: z(e, "--profile") } : {},
407
440
  ...z(e, "--tenant") ? { tenant: z(e, "--tenant") } : {},
@@ -412,81 +445,82 @@ var A = class extends Error {
412
445
  ...n ? { bundleKey: n } : {}
413
446
  }, s;
414
447
  try {
415
- s = await j(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
448
+ s = await M(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
416
449
  "content-type": "application/json",
417
450
  authorization: `Bearer ${i}`
418
- }, Q(e));
451
+ }, Z(e));
419
452
  } catch (e) {
420
- if (e instanceof A) return F.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline, not a server failure — the response arrives only when the export does. Nothing was written locally; re-run, or pass --timeout <seconds> (the default is to wait).`), 1;
453
+ if (e instanceof j) return I.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline, not a server failure — the response arrives only when the export does. Nothing was written locally; re-run, or pass --timeout <seconds> (the default is to wait).`), 1;
454
+ if (e instanceof A) return I.error(`api export: ${be(e)}`), 1;
421
455
  throw e;
422
456
  }
423
- if (s.status < 200 || s.status >= 300) return F.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
424
- if (n) return F.info("api export complete (pushed to storage)", {
457
+ if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
458
+ if (n) return I.info("api export complete (pushed to storage)", {
425
459
  via: t,
426
460
  ...JSON.parse(s.body.toString("utf8"))
427
461
  }), 0;
428
462
  let { resolve: c } = await import("node:path"), l = new Uint8Array(s.body);
429
463
  return await k((async function* () {
430
464
  yield l;
431
- })(), c(process.cwd(), r)), F.info("api export complete (downloaded from the running instance)", {
465
+ })(), c(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
432
466
  outDir: r,
433
467
  tables: s.headers["x-export-tables"],
434
468
  rows: s.headers["x-export-rows"],
435
469
  masked: s.headers["x-export-masked"]
436
470
  }), 0;
437
471
  }
438
- if (!r) return F.error("a <outDir> is required for a direct export"), 1;
439
- let i = z(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await W(), 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 ?? je(e, c.tables.map((e) => e.name)), v = h ? await Me() : void 0, y = _.kind === "tenant" ? await Pe() : void 0, b = y ? { tenantTables: y } : {};
472
+ if (!r) return I.error("a <outDir> is required for a direct export"), 1;
473
+ let i = z(e, "--profile"), a = i ? await c(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: l } = await W(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, f = z(e, "--compression") ?? a.compression, p = z(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", m = a.masking, h = a.subset, g = a.scope ?? Le(e, s.tables.map((e) => e.name)), _ = m ? await Re() : void 0, y = g.kind === "tenant" ? await Be() : void 0, b = y ? { tenantTables: y } : {};
440
474
  if (e.includes("--dry-run")) {
441
- let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
442
- if (F.info("export DRY RUN — nothing was written", {
475
+ let e = g.kind === "tables" && g.tables ? [...g.tables] : s.tables.map((e) => e.name);
476
+ if (I.info("export DRY RUN — nothing was written", {
443
477
  outDir: r,
444
478
  tables: e.length,
445
- scope: _.kind,
446
- consistency: m,
447
- compression: p ?? "zstd",
479
+ scope: g.kind,
480
+ consistency: p,
481
+ compression: f ?? "zstd",
448
482
  assets: u,
449
- masking: h ? "profile policy applies" : "none"
450
- }), F.info(`would export: ${e.join(", ")}`), !h) return 0;
451
- let t = await f.runPromise(oe({
483
+ masking: m ? "profile policy applies" : "none"
484
+ }), I.info(`would export: ${e.join(", ")}`), !m) return 0;
485
+ let t = await v.runPromise(ue({
452
486
  store: o,
453
- snapshot: c,
454
- masking: h,
455
- ...v ? { classification: v } : {},
456
- ...g ? {} : {
457
- scope: _,
487
+ snapshot: s,
488
+ masking: m,
489
+ ..._ ? { classification: _ } : {},
490
+ ...h ? {} : {
491
+ scope: g,
458
492
  ...b
459
493
  }
460
494
  }));
461
- return Fe(t), +(t.unclassified.length > 0);
495
+ return Ve(t), +(t.unclassified.length > 0);
462
496
  }
463
497
  if (K(r)) {
464
- 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-"));
498
+ let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), c = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
465
499
  try {
466
- let t = await f.runPromise(se({
500
+ let t = await v.runPromise(E({
467
501
  store: o,
468
- snapshot: c,
502
+ snapshot: s,
469
503
  dialect: l,
470
- outDir: s,
471
- ...g ? { subset: g } : {
472
- scope: _,
504
+ outDir: c,
505
+ ...h ? { subset: h } : {
506
+ scope: g,
473
507
  ...b
474
508
  },
475
- consistency: m,
476
- ...p ? { compression: p } : {},
477
- ...h ? { masking: h } : {},
478
- ...v ? { classification: v } : {},
479
- onProgress: L
480
- })), n = T({
481
- dir: s,
482
- ...d ? { blobs: fe(d, () => G(o)) } : {}
483
- }), a = ke(e);
509
+ consistency: p,
510
+ ...f ? { compression: f } : {},
511
+ ...m ? { masking: m } : {},
512
+ ..._ ? { classification: _ } : {},
513
+ onProgress: R
514
+ })), n = le({
515
+ dir: c,
516
+ ...d ? { blobs: he(d, () => G(o)) } : {}
517
+ }), a = Fe(e);
484
518
  if (a) {
485
519
  let t = q(e);
486
- if (!t) return F.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
487
- n = ae(n, t);
520
+ if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
521
+ n = ce(n, t);
488
522
  }
489
- return await Ae(n, i(process.cwd(), r)), F.info("export complete (single file)", {
523
+ return await Ie(n, i(process.cwd(), r)), I.info("export complete (single file)", {
490
524
  file: r,
491
525
  encrypted: a,
492
526
  assets: u,
@@ -494,29 +528,29 @@ var A = class extends Error {
494
528
  rows: t.tables.reduce((e, t) => e + t.rowCount, 0)
495
529
  }), 0;
496
530
  } finally {
497
- await n.rm(s, {
531
+ await n.rm(c, {
498
532
  recursive: !0,
499
533
  force: !0
500
534
  });
501
535
  }
502
536
  }
503
- let x = await f.runPromise(se({
537
+ let x = await v.runPromise(E({
504
538
  store: o,
505
- snapshot: c,
539
+ snapshot: s,
506
540
  dialect: l,
507
541
  outDir: r,
508
- ...g ? { subset: g } : {
509
- scope: _,
542
+ ...h ? { subset: h } : {
543
+ scope: g,
510
544
  ...b
511
545
  },
512
- consistency: m,
513
- ...p ? { compression: p } : {},
514
- ...h ? { masking: h } : {},
515
- ...v ? { classification: v } : {},
516
- ...d ? { assets: ue(d, () => G(o)) } : {},
517
- onProgress: L
546
+ consistency: p,
547
+ ...f ? { compression: f } : {},
548
+ ...m ? { masking: m } : {},
549
+ ..._ ? { classification: _ } : {},
550
+ ...d ? { assets: pe(d, () => G(o)) } : {},
551
+ onProgress: R
518
552
  }));
519
- return F.info("export complete", {
553
+ return I.info("export complete", {
520
554
  outDir: r,
521
555
  tables: x.tables.length,
522
556
  rows: x.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -524,13 +558,13 @@ var A = class extends Error {
524
558
  compression: x.compression,
525
559
  ...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
526
560
  }), 0;
527
- }, Re = () => "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.", ze = (e) => {
561
+ }, qe = () => "dry run checked: schema fit, cross-dialect portability, mode legality, the table selection. It did NOT read per-table checksums (those stream during the load) and cannot see conflicts that depend on the target's current rows.", Je = (e) => {
528
562
  let t = z(e, "--tables");
529
563
  return {
530
564
  ...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
531
565
  ...e.includes("--dry-run") ? { dryRun: !0 } : {}
532
566
  };
533
- }, Be = (e) => ({
567
+ }, Q = (e) => ({
534
568
  tables: e.tablesWritten,
535
569
  rows: e.rowsWritten,
536
570
  carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -538,140 +572,187 @@ var A = class extends Error {
538
572
  skippedTables: e.tablesSkipped,
539
573
  skippedRows: e.rowsSkipped
540
574
  } : {}
541
- }), Ve = (e, t) => {
575
+ }), Ye = (e, t) => {
542
576
  if (e.includes("--dry-run")) {
543
- F.info("import DRY RUN — nothing was written", t), F.info(Re());
577
+ I.info("import DRY RUN — nothing was written", t), I.info(qe());
544
578
  return;
545
579
  }
546
580
  if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
547
- F.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
581
+ I.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
548
582
  return;
549
583
  }
550
- F.info("import complete", t);
551
- }, He = (e) => {
552
- let t = e;
553
- if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return;
584
+ I.info("import complete", t);
585
+ }, Xe = Symbol("refused"), Ze = (e) => {
586
+ let t = ie(e) ?? e;
587
+ if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
554
588
  let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
555
- F.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.`);
589
+ I.error(`import refused ${i} row(s), of which ${a} are the actual failures — the rest could not land because a row they reference did not.`);
556
590
  let o = Array.isArray(t.byTable) ? t.byTable : [];
557
- o.length > 1 && F.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
558
- for (let e of r) F.error(` ${e.table} ${e.id} ${e.reason}`);
559
- n.length < i && F.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
591
+ o.length > 1 && I.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
592
+ for (let e of r) I.error(` ${e.table} ${e.id} ${e.reason}`);
593
+ n.length < i && I.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
560
594
  let s = i - a;
561
- s > 0 && F.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`);
562
- }, Ue = async (e) => {
595
+ return s > 0 && I.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
596
+ }, Qe = (e) => {
597
+ let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
598
+ try {
599
+ let e = JSON.parse(t), r = e.error ?? e.message;
600
+ typeof r == "string" && (n = r);
601
+ } catch {}
602
+ let r = n.indexOf("RowsRefusedError");
603
+ if (r < 0) return;
604
+ let i = n.indexOf("{", r);
605
+ if (i < 0) return;
606
+ let a = 0, o = !1, s = !1;
607
+ for (let e = i; e < n.length; e++) {
608
+ let t = n[e];
609
+ if (o) {
610
+ if (s) {
611
+ s = !1;
612
+ continue;
613
+ }
614
+ if (t === "\\") {
615
+ s = !0;
616
+ continue;
617
+ }
618
+ t === "\"" && (o = !1);
619
+ continue;
620
+ }
621
+ if (t === "\"") {
622
+ o = !0;
623
+ continue;
624
+ }
625
+ if (t === "{") a++;
626
+ else if (t === "}" && --a === 0) try {
627
+ let t = JSON.parse(n.slice(i, e + 1));
628
+ return Array.isArray(t.rows) ? {
629
+ _tag: "RowsRefusedError",
630
+ ...t
631
+ } : void 0;
632
+ } catch {
633
+ return;
634
+ }
635
+ }
636
+ }, $e = async (e) => {
563
637
  let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
564
- if (!i && !(t && n)) return F.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;
638
+ if (!i && !(t && n)) return I.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--bundle-key <storage-key>] [--tables a,b] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] [--dry-run] (--force: import despite schema drift AND cross-dialect warnings; --dry-run: report what would move, write nothing)"), 1;
565
639
  let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
566
640
  if (t) {
567
641
  let t = z(e, "--api-url");
568
- if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
642
+ if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
569
643
  let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
570
- if (!r) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
571
- let s = z(e, "--tables"), c = {
644
+ if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
645
+ let s = z(e, "--tables"), c = z(e, "--rollback-key");
646
+ a === "replace" && c === void 0 && I.warn("api replace: no rollback capture — this run empties the target first and nothing was asked to be kept.\n Pass --rollback-key <storage-key> and the INSTANCE stores the target's current rows in its own\n object storage before deleting them, which is the one place that survives the pod going away.");
647
+ let l = {
572
648
  authorization: `Bearer ${r}`,
573
649
  "x-import-mode": a,
574
650
  "x-import-on-conflict": o,
575
- ...Z(e, a) ? { "x-import-atomic": "1" } : {},
651
+ ...X(e, a) ? { "x-import-atomic": "1" } : {},
576
652
  ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
653
+ ...c === void 0 ? {} : { "x-import-rollback-key": c },
577
654
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
578
655
  ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
579
656
  ...s ? { "x-import-tables": s } : {}
580
- }, { resolve: l } = await import("node:path"), u;
581
- if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
657
+ }, { resolve: u } = await import("node:path"), d;
658
+ if (n) l["content-type"] = "application/json", d = JSON.stringify({ bundleKey: n });
582
659
  else if (K(i)) {
583
- let t = J(l(process.cwd(), i)), n = q(e);
584
- n && (t = w(t, n)), c["content-type"] = "application/octet-stream", u = t;
585
- } else 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."), c["content-type"] = "application/octet-stream", u = T({ dir: l(process.cwd(), i) });
586
- a === "replace" && !Z(e, a) && F.warn(X(!0));
587
- let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
660
+ let t = J(u(process.cwd(), i)), n = q(e);
661
+ n && (t = w(t, n)), l["content-type"] = "application/octet-stream", d = t;
662
+ } else await (await import("node:fs/promises")).access(u(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && I.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API."), l["content-type"] = "application/octet-stream", d = le({ dir: u(process.cwd(), i) });
663
+ a === "replace" && !X(e, a) && I.warn(Ue(!0));
664
+ let f = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, p;
588
665
  try {
589
- let t = typeof u == "string" ? null : await Ie({
590
- stream: u,
591
- url: d,
592
- headers: c,
666
+ let t = typeof d == "string" ? null : await He({
667
+ stream: d,
668
+ url: f,
669
+ headers: l,
593
670
  bundleDir: i,
594
671
  args: e,
595
- log: F
672
+ log: I
596
673
  });
597
- (t === null || t.kind === "small") && F.info("api import: uploading the bundle — this request stays open until the instance has imported it"), f = t === null ? await j(d, u, c, Q(e)) : t.kind === "sent" ? t.res : await j(d, t.body, c, Q(e));
674
+ (t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), p = t === null ? await M(f, d, l, Z(e)) : t.kind === "sent" ? t.res : await M(f, t.body, l, Z(e));
598
675
  } catch (e) {
599
- if (e instanceof A) return F.error(`api import: ${ge(e, a)}`), 1;
676
+ if (e instanceof j) return I.error(`api import: ${xe(e, a)}`), 1;
677
+ if (e instanceof A) return I.error(`api import: ${be(e)}`), 1;
600
678
  throw e;
601
679
  }
602
- if (f.status < 200 || f.status >= 300) return F.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
603
- let p = JSON.parse(f.body.toString("utf8"));
604
- return e.includes("--dry-run") ? (F.info("api import DRY RUN — nothing was written", {
680
+ if (p.status < 200 || p.status >= 300) return Ze(Qe(p.body)) || I.error(`api import failed: HTTP ${p.status} — ${p.body.toString("utf8")}`), 1;
681
+ let m = JSON.parse(p.body.toString("utf8"));
682
+ return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
605
683
  via: t,
606
- ...p
607
- }), F.info(Re()), 0) : (F.info("api import complete (imported by the running instance)", {
684
+ ...m
685
+ }), I.info(qe()), 0) : (I.info("api import complete (imported by the running instance)", {
608
686
  via: t,
609
- ...p
687
+ ...m
610
688
  }), 0);
611
689
  }
612
- if (!i) return F.error("a <bundleDir> is required for a direct import"), 1;
613
- if (!await R(e)) return 1;
614
- a === "replace" && e.includes("--allow-live") && !Z(e, a) && F.warn(X(!1));
615
- let { store: s, snapshot: c } = await W(), 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;
690
+ if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
691
+ if (!await Me(e)) return 1;
692
+ a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(Ue(!1));
693
+ let { store: s, snapshot: c } = await W(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, f = V() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
616
694
  if (K(i)) {
617
- 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-"));
695
+ let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join: p } = await import("node:path"), m = await n.mkdtemp(p(t.tmpdir(), "voltro-import-"));
618
696
  try {
619
697
  let t = J(r(process.cwd(), i)), n = q(e);
620
- return n && (t = w(t, n)), await pe(t, {
621
- dir: h,
622
- blobs: de(d ?? S({}))
623
- }), Ve(e, {
698
+ return n && (t = w(t, n)), await ge(t, {
699
+ dir: m,
700
+ blobs: me(d ?? S({}))
701
+ }), Ye(e, {
624
702
  file: i,
625
- ...Be(await f.runPromise(ce({
703
+ ...Q(await v.runPromise(D({
626
704
  store: s,
627
- bundleDir: h,
705
+ bundleDir: m,
628
706
  mode: a,
629
707
  onConflict: o,
630
- atomic: Z(e, a),
708
+ atomic: X(e, a),
709
+ ...Ge(e, a, i),
631
710
  verify: !e.includes("--no-verify"),
632
711
  targetDialect: V(),
633
712
  allowIncompatible: l,
634
713
  targetSnapshot: c,
635
714
  force: l,
636
715
  onWarn: u,
637
- onProgress: L,
716
+ onProgress: R,
638
717
  assetsAlreadyRestored: !0,
639
- ...ze(e),
640
- ...p ? { copyLoader: p } : {}
718
+ ...Je(e),
719
+ ...f ? { copyLoader: f } : {}
641
720
  })))
642
721
  }), 0;
643
722
  } finally {
644
- await p?.close(), await n.rm(h, {
723
+ await f?.close(), await n.rm(m, {
645
724
  recursive: !0,
646
725
  force: !0
647
726
  });
648
727
  }
649
728
  }
650
- let m = await f.runPromise(ce({
729
+ let p = await v.runPromise(D({
730
+ ...Ge(e, a, i),
651
731
  store: s,
652
732
  bundleDir: i,
653
733
  mode: a,
654
734
  onConflict: o,
655
- atomic: Z(e, a),
735
+ atomic: X(e, a),
656
736
  verify: !e.includes("--no-verify"),
657
737
  targetDialect: V(),
658
738
  allowIncompatible: l,
659
739
  targetSnapshot: c,
660
740
  force: l,
661
741
  onWarn: u,
662
- onProgress: L,
663
- ...ze(e),
664
- ...d ? { assets: le(d) } : {},
665
- ...p ? { copyLoader: p } : {}
742
+ onProgress: R,
743
+ ...Je(e),
744
+ ...d ? { assets: fe(d) } : {},
745
+ ...f ? { copyLoader: f } : {}
666
746
  })).catch((e) => {
667
- throw He(e), e;
668
- }).finally(() => p?.close());
669
- return Ve(e, {
747
+ if (Ze(e)) return Xe;
748
+ throw e;
749
+ }).finally(() => f?.close());
750
+ return typeof p == "symbol" ? 1 : (Ye(e, {
670
751
  bundleDir: i,
671
- ...Be(m),
672
- assets: m.manifest.assets.count
673
- }), 0;
674
- }, We = () => {
752
+ ...Q(p),
753
+ assets: p.manifest.assets.count
754
+ }), 0);
755
+ }, et = () => {
675
756
  let e = r();
676
757
  return {
677
758
  ...e.url ? { url: e.url } : {},
@@ -681,75 +762,75 @@ var A = class extends Error {
681
762
  ...e.password ? { password: e.password } : {},
682
763
  ...e.database ? { database: e.database } : {}
683
764
  };
684
- }, Ge = async (e) => {
765
+ }, tt = async (e) => {
685
766
  let t = B(e);
686
- if (!t) return F.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
767
+ if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
687
768
  let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
688
769
  await r(t, { recursive: !0 });
689
770
  let o = a(t, C(n));
690
- await f.runPromise(O(re(n, We(), o)));
691
- let s = xe({
771
+ await v.runPromise(O(oe(n, et(), o)));
772
+ let s = Ee({
692
773
  dialect: n,
693
- schemaFingerprint: await I(n),
694
- voltroVersion: await Te(),
774
+ schemaFingerprint: await L(n),
775
+ voltroVersion: await Ae(),
695
776
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
696
777
  });
697
- return await i(a(t, M), `${JSON.stringify(s, null, 2)}\n`, "utf8"), F.info("native backup complete", {
778
+ return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
698
779
  dialect: n,
699
780
  artifact: o,
700
- stamp: M,
781
+ stamp: N,
701
782
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
702
783
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
703
784
  }), 0;
704
- }, Ke = async (e, t) => {
705
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Ce({
785
+ }, nt = async (e, t) => {
786
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Oe({
706
787
  flagUrl: z(t, "--drill-url"),
707
788
  envUrl: process.env.DRILL_DB_URL,
708
- liveKeys: Oe()
789
+ liveKeys: Pe()
709
790
  });
710
- if (!o.ok) return F.error(`restore --drill: ${o.error}`), 1;
711
- let s = await i(r(e, M), "utf8").then(N, () => null);
712
- 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;
713
- F.info("restore drill: restoring into throwaway DB", {
791
+ if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
792
+ let s = await i(r(e, N), "utf8").then(P, () => null);
793
+ 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;
794
+ I.info("restore drill: restoring into throwaway DB", {
714
795
  dialect: n,
715
- target: P(o.url)
716
- }), await f.runPromise(O(D(n, { url: o.url }, a)));
796
+ target: F(o.url)
797
+ }), await v.runPromise(O(de(n, { url: o.url }, a)));
717
798
  let c = null, l = 0;
718
799
  try {
719
800
  let e = await H(n, { url: o.url });
720
- c = g(e), l = e.tables.length;
801
+ c = ee(e), l = e.tables.length;
721
802
  } catch (e) {
722
- return F.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
803
+ return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
723
804
  }
724
- let u = we({
805
+ let u = ke({
725
806
  stampFingerprint: s?.schemaFingerprint ?? null,
726
807
  restoredFingerprint: c,
727
808
  restoredTableCount: l
728
809
  });
729
- for (let e of u.lines) u.pass ? F.info(`restore drill: ${e}`) : F.error(`restore drill: ${e}`);
810
+ for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
730
811
  return +!u.pass;
731
- }, qe = async (e) => {
812
+ }, rt = async (e) => {
732
813
  let t = B(e);
733
- 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;
734
- if (e.includes("--drill")) return Ke(t, e);
735
- if (!await R(e)) return 1;
736
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Se({
737
- stamp: await i(r(t, M), "utf8").then(N, () => null),
814
+ if (!t) return I.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
815
+ if (e.includes("--drill")) return nt(t, e);
816
+ if (!await Me(e)) return 1;
817
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = De({
818
+ stamp: await i(r(t, N), "utf8").then(P, () => null),
738
819
  currentDialect: n,
739
- currentFingerprint: await I(n)
820
+ currentFingerprint: await L(n)
740
821
  });
741
- for (let e of o.info) F.info(`backup stamp: ${e}`);
742
- if (o.warn && F.warn(o.warn), o.refuse) {
743
- if (!e.includes("--force")) return F.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
744
- F.warn(`restore proceeding despite: ${o.refuse} (--force)`);
822
+ for (let e of o.info) I.info(`backup stamp: ${e}`);
823
+ if (o.warn && I.warn(o.warn), o.refuse) {
824
+ if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
825
+ I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
745
826
  }
746
- return await f.runPromise(O(D(n, We(), a))), F.info("native restore complete", {
827
+ return await v.runPromise(O(de(n, et(), a))), I.info("native restore complete", {
747
828
  dialect: n,
748
829
  artifact: a
749
830
  }), 0;
750
- }, Je = async (e) => {
831
+ }, it = async (e) => {
751
832
  let t = B(e);
752
- if (!t) return F.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
833
+ if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
753
834
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
754
835
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
755
836
  let n = r(a, "manifest.json"), o = r(a, "README.md");
@@ -758,25 +839,44 @@ var A = class extends Error {
758
839
  if (e !== void 0) return console.log(e), 0;
759
840
  }
760
841
  let s = await i.readFile(n, "utf8").catch(() => void 0);
761
- 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);
842
+ return s === void 0 ? (I.error(`'${t}' is a directory but holds no manifest.json — is it an unpacked bundle? (a bundle exported with --target api is always unpacked into a directory, even when the path ends in .vbundle)`), 1) : (console.log(s), 0);
762
843
  }
763
844
  let o = q(e), s = () => {
764
845
  let e = J(a);
765
846
  return o ? w(e, o) : e;
766
847
  };
767
848
  if (e.includes("--json")) {
768
- let e = (await E(s(), ["manifest.json"])).get("manifest.json");
769
- return e ? (console.log(e.toString("utf8")), 0) : (F.error("no manifest.json found in the bundle"), 1);
849
+ let e = (await T(s(), ["manifest.json"])).get("manifest.json");
850
+ return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
770
851
  }
771
- let c = (await E(s(), ["README.md"])).get("README.md");
852
+ let c = (await T(s(), ["README.md"])).get("README.md");
772
853
  if (c) return console.log(c.toString("utf8")), 0;
773
- let l = (await E(s(), ["manifest.json"])).get("manifest.json");
774
- return l ? (console.log(l.toString("utf8")), 0) : (F.error("bundle has no README.md or manifest.json"), 1);
775
- }, Ye = async (e) => {
854
+ let l = (await T(s(), ["manifest.json"])).get("manifest.json");
855
+ return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
856
+ }, at = async (e) => {
857
+ let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-DNJ7MKph.js"), { store: r } = await W(), i = await n(r);
858
+ if (i === void 0) return I.info("no interrupted replace is recorded — nothing to clear"), 0;
859
+ if (!e.includes("--yes")) return I.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
860
+ let a = await r.query({
861
+ table: t,
862
+ predicate: void 0,
863
+ order: [],
864
+ take: void 0,
865
+ skip: void 0,
866
+ projection: void 0
867
+ });
868
+ for (let e of a) await r.delete(t, String(e.id));
869
+ return I.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
870
+ }, ot = async (e) => {
871
+ let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await W(), a = V();
872
+ if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return I.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
873
+ let { introspectSchema: o } = await import("@voltro/database/sql"), s = (await i.run((await import("@effect/sql")).SqlClient.SqlClient.pipe((await import("effect")).Effect.flatMap(o)))).tables.map((e) => e.name).filter((e) => e.startsWith(t));
874
+ return s.length === 0 ? (I.info("no staging tables are present — nothing to clear"), 0) : e.includes("--yes") ? (await r(i, s.map((e) => n(e.slice(t.length), a))), I.info(`dropped ${s.length} staging table(s)`), 0) : (I.error(`${s.length} staging table(s) from an earlier \`--mode replace\`:\n` + s.map((e) => ` ${e}`).join("\n") + "\n\n Each holds a copy of what a run was loading. Nothing else removes them.\n Make sure no import is running right now — this cannot tell a leftover from one in flight.\n Re-run with --yes to drop them."), 1);
875
+ }, st = async (e) => {
776
876
  let t = B(e), n = B(e.filter((e) => e !== t));
777
- if (!t || !n) return F.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
877
+ if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
778
878
  let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
779
- return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), F.info("unpack complete", {
879
+ return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), I.info("unpack complete", {
780
880
  file: t,
781
881
  dir: n
782
882
  }), 0;
@@ -805,6 +905,8 @@ var A = class extends Error {
805
905
  "--timeout"
806
906
  ]
807
907
  },
908
+ "clear-staging": { common: ["--yes"] },
909
+ "clear-replace-marker": { common: ["--yes"] },
808
910
  import: {
809
911
  common: [
810
912
  "--target",
@@ -821,14 +923,17 @@ var A = class extends Error {
821
923
  directOnly: [
822
924
  "--no-verify",
823
925
  "--allow-live",
824
- "--passphrase"
926
+ "--passphrase",
927
+ "--rollback-dir",
928
+ "--no-rollback"
825
929
  ],
826
930
  apiOnly: [
827
931
  "--api-url",
828
932
  "--token",
829
933
  "--bundle-key",
830
934
  "--timeout",
831
- "--chunk-size"
935
+ "--chunk-size",
936
+ "--rollback-key"
832
937
  ]
833
938
  },
834
939
  unpack: { common: ["--passphrase", "--json"] },
@@ -842,13 +947,13 @@ var A = class extends Error {
842
947
  "--drill-url",
843
948
  "--api-url"
844
949
  ] }
845
- }, Xe = (e) => e.split("=")[0], Ze = (e, t) => {
950
+ }, ct = (e) => e.split("=")[0], lt = (e, t) => {
846
951
  let n = $[e];
847
952
  if (!n) return;
848
953
  let r = z(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
849
954
  for (let n of t) {
850
955
  if (!n.startsWith("--")) continue;
851
- let t = Xe(n);
956
+ let t = ct(n);
852
957
  if (i.has(t)) continue;
853
958
  if (a.has(t)) return `voltro data ${e}: ${t} is not read with --target ${r} (it is a --target ${r === "api" ? "direct" : "api"} flag). Remove it, or change --target.`;
854
959
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -858,13 +963,13 @@ var A = class extends Error {
858
963
  ].includes(t)).map(([e]) => e);
859
964
  return o.length > 0 ? `voltro data ${e}: ${t} is not a \`${e}\` flag (it belongs to \`voltro data ${o.join("` / `")}\`). Remove it.` : `voltro data ${e}: unknown flag ${t}. Run \`voltro data --help\` for the flags this command reads.`;
860
965
  }
861
- }, Qe = async (e) => {
966
+ }, ut = async (e) => {
862
967
  let [t, ...n] = e;
863
968
  if (t !== void 0 && $[t] !== void 0) {
864
- let e = Ze(t, n);
865
- if (e) return F.error(e), 2;
969
+ let e = lt(t, n);
970
+ if (e) return I.error(e), 2;
866
971
  }
867
- return t === "export" ? Le(n) : t === "import" ? Ue(n) : t === "unpack" ? Ye(n) : t === "inspect" ? Je(n) : t === "backup" ? Ge(n) : t === "restore" ? qe(n) : (F.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
972
+ return t === "export" ? Ke(n) : t === "import" ? $e(n) : t === "unpack" ? st(n) : t === "inspect" ? it(n) : t === "backup" ? tt(n) : t === "restore" ? rt(n) : t === "clear-replace-marker" ? at(n) : t === "clear-staging" ? ot(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore|clear-replace-marker|clear-staging> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
868
973
  };
869
974
  //#endregion
870
- export { Qe as runDataCommand };
975
+ export { ut as runDataCommand };