@voltro/cli 0.45.0 → 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 (112) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/dist/{apiBuild-CDQBNRDx.js → apiBuild-BKdd0I1Z.js} +2 -2
  3. package/dist/apiBuild-P2gewrfQ.js +2 -0
  4. package/dist/bin.d.ts +8 -0
  5. package/dist/bin.js +1 -1
  6. package/dist/{build-yvsLG77U.js → build-co6yadYz.js} +7 -7
  7. package/dist/{checkCommand-Boxov8lr.js → checkCommand-B-eiW-G_.js} +3 -3
  8. package/dist/{checkCommand-lrzwOzV1.js → checkCommand-CXAXTjZL.js} +1 -1
  9. package/dist/{codegenCommand-CQOLytL-.js → codegenCommand-CB6_3sX0.js} +15 -15
  10. package/dist/{codemodRunner-5ZhDH3rB.js → codemodRunner-DSZjgyCL.js} +8 -0
  11. package/dist/{commands-CEkVoFM5.js → commands-C-hxgAGr.js} +23 -23
  12. package/dist/{dashboardCommand-0ahyp-zA.js → dashboardCommand-CEVO0GWF.js} +3 -3
  13. package/dist/{dataCommand-D4IlZcih.js → dataCommand-k0ezbQWa.js} +271 -230
  14. package/dist/{dbCommand-B237qI-j.js → dbCommand-BwkyNxn-.js} +110 -110
  15. package/dist/dbCommand-C2XK63Dd.js +2 -0
  16. package/dist/{dev-DAM5CnAa.js → dev-8hTEnN6s.js} +2080 -2057
  17. package/dist/{dev-P4HyeY9W.js → dev-DyoYst4j.js} +1 -1
  18. package/dist/devActivity.d.ts +8 -0
  19. package/dist/{doctorCommand-DW2cwAvC.js → doctorCommand-B6D7HH1-.js} +14 -14
  20. package/dist/doctorCommand-Btl2Jz1a.js +2 -0
  21. package/dist/{dormancyCommand-BqQz7lbM.js → dormancyCommand-CrPAwOgo.js} +1 -1
  22. package/dist/{embeddingsCommand-DJyArpU4.js → embeddingsCommand-DsSxsn_b.js} +1 -1
  23. package/dist/{envCommand-CviRZx1L.js → envCommand-B_NPVGUf.js} +2 -2
  24. package/dist/{evalCommand-6RUfPen4.js → evalCommand-Db01Kbng.js} +24 -24
  25. package/dist/{evolveCommand-qdlP-R9G.js → evolveCommand-DUuaCIyn.js} +2 -2
  26. package/dist/{frameworkTableAssembly-oMPBKqlE.js → frameworkTableAssembly-BPlPfU07.js} +82 -78
  27. package/dist/frameworkTableAssembly-Bob3vj9R.js +2 -0
  28. package/dist/index.d.ts +8 -0
  29. package/dist/index.js +1 -1
  30. package/dist/{infoCommand-CNWPSUcW.js → infoCommand-CuNl9cbh.js} +1 -1
  31. package/dist/inspectState.d.ts +8 -0
  32. package/dist/interruptedReplace-C1ZI84xi.js +27 -0
  33. package/dist/interruptedReplace-DNJ7MKph.js +2 -0
  34. package/dist/{migrate-DN47p2PG.js → migrate-CM1qwalu.js} +27 -27
  35. package/dist/{runtimeTrace-BDZSt9W_.js → runtimeTrace-BRNM06vK.js} +1 -1
  36. package/dist/{sdkgen-sNaD3oZB.js → sdkgen-CRCw_XcR.js} +1 -1
  37. package/dist/{seedRunner-IdHEprqf.js → seedRunner-Dgsiwk_e.js} +59 -59
  38. package/dist/serveCommand-BNHMVXyp.js +2 -0
  39. package/dist/serveCommand-CHOr74WM.js +1928 -0
  40. package/dist/serveEntry.d.ts +8 -0
  41. package/dist/serveEntry.js +1 -1
  42. package/dist/{start-Ds3xyn8R.js → start-7HcSIKEp.js} +1 -1
  43. package/dist/{start-BAlGcjF5.js → start-DvlYDyAr.js} +1 -1
  44. package/dist/startEntry.d.ts +8 -0
  45. package/dist/startEntry.js +1 -1
  46. package/dist/startup.d.ts +8 -0
  47. package/dist/{updateCommand-rCnhaj84.js → updateCommand-BlyXavoG.js} +1 -1
  48. package/dist/updateCommand-CT5AvVg7.js +2 -0
  49. package/dist/{webDev-101csg6Q.js → webDev-B4IEMB0n.js} +6 -6
  50. package/dist/webDev-B79ji8M5.js +2 -0
  51. package/dist/{webhooksCommand-C0iZZfaI.js → webhooksCommand-BRrG_sGf.js} +1 -1
  52. package/package.json +23 -17
  53. package/templates/AGENTS.md +1 -1
  54. package/templates/agent-docs/_index.md +1 -1
  55. package/templates/agent-docs/cli.md +138 -2
  56. package/templates/agent-docs/data.md +70 -4
  57. package/templates/agent-docs/whats-new.md +127 -54
  58. package/templates/apps/api-ai/package.json +7 -7
  59. package/templates/apps/api-auth/package.json +8 -8
  60. package/templates/apps/api-backend/package.json +7 -7
  61. package/templates/apps/api-backend-deactivation/package.json +7 -7
  62. package/templates/apps/api-backend-mail/package.json +8 -8
  63. package/templates/apps/api-backend-mariadb/package.json +9 -9
  64. package/templates/apps/api-backend-sqlite/package.json +8 -8
  65. package/templates/apps/api-backend-storage/package.json +8 -8
  66. package/templates/apps/api-cms/package.json +10 -10
  67. package/templates/apps/api-collab/package.json +8 -8
  68. package/templates/apps/api-data-advanced/package.json +8 -8
  69. package/templates/apps/api-durable/package.json +8 -8
  70. package/templates/apps/api-feature-flags/package.json +9 -9
  71. package/templates/apps/api-governance/package.json +8 -8
  72. package/templates/apps/api-kv/package.json +8 -8
  73. package/templates/apps/api-moderation/package.json +8 -8
  74. package/templates/apps/api-observability/package.json +8 -8
  75. package/templates/apps/api-ratelimit/package.json +8 -8
  76. package/templates/apps/api-rbac/package.json +8 -8
  77. package/templates/apps/api-rest/package.json +7 -7
  78. package/templates/apps/api-saas/package.json +11 -11
  79. package/templates/apps/api-saas-starter/package.json +10 -10
  80. package/templates/apps/api-search/package.json +8 -8
  81. package/templates/apps/api-status/package.json +8 -8
  82. package/templates/apps/api-versioning/package.json +8 -8
  83. package/templates/apps/api-webhooks/package.json +9 -9
  84. package/templates/apps/changelog/package.json +6 -6
  85. package/templates/apps/edge-functions/package.json +2 -2
  86. package/templates/apps/frontend-admin/package.json +8 -8
  87. package/templates/apps/frontend-app/package.json +9 -9
  88. package/templates/apps/frontend-auth/package.json +8 -8
  89. package/templates/apps/frontend-blank/package.json +7 -7
  90. package/templates/apps/frontend-cms/package.json +9 -9
  91. package/templates/apps/frontend-collab/package.json +10 -10
  92. package/templates/apps/frontend-contact/package.json +7 -7
  93. package/templates/apps/frontend-dashboard/package.json +7 -7
  94. package/templates/apps/frontend-docs/package.json +7 -7
  95. package/templates/apps/frontend-i18n/package.json +6 -6
  96. package/templates/apps/frontend-landing/package.json +7 -7
  97. package/templates/apps/frontend-portal/package.json +8 -8
  98. package/templates/apps/frontend-saas/package.json +8 -8
  99. package/templates/apps/frontend-spa/package.json +7 -7
  100. package/templates/apps/frontend-ssr/package.json +7 -7
  101. package/templates/apps/frontend-ssr-api/package.json +8 -8
  102. package/templates/apps/frontend-static-blog/package.json +6 -6
  103. package/templates/apps/frontend-status/package.json +8 -8
  104. package/templates/apps/mobile-app/package.json +4 -4
  105. package/dist/apiBuild-yJCqJYQ8.js +0 -2
  106. package/dist/dbCommand-BvQeKQOb.js +0 -2
  107. package/dist/doctorCommand-2K1dIzYk.js +0 -2
  108. package/dist/frameworkTableAssembly-DgylYh9_.js +0 -2
  109. package/dist/serveCommand-BBhT2U94.js +0 -1885
  110. package/dist/serveCommand-DY2jLy0o.js +0 -2
  111. package/dist/updateCommand-BH6EGuCc.js +0 -2
  112. package/dist/webDev-BLKBO34K.js +0 -2
@@ -1,22 +1,23 @@
1
1
  import { t as e } from "./cliError-BmdYnghb.js";
2
- import { c as t, i as n, o as r } from "./dev-DAM5CnAa.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 { asImportError as ne, assessLiveImport as re, backupArtifactName as C, backupCommand as ie, classificationFromSnapshot as ae, decryptStream as w, encryptStream as oe, packBundle as T, peekBundle as E, previewMasking as se, restoreCommand as D, runExport as ce, runImport as le, runNativeStep as O, storageAssetSink as ue, storageAssetSource as de, storageBlobSink as fe, storageBlobSource as pe, unpackBundle as me, unpackBundleToDir as k } from "@voltro/data-transfer";
16
- import { request as he } from "node:http";
17
- import { request as ge } 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 _e = /* @__PURE__ */ new Set([
20
+ var ye = /* @__PURE__ */ new Set([
20
21
  "ECONNREFUSED",
21
22
  "ETIMEDOUT",
22
23
  "EHOSTUNREACH",
@@ -44,7 +45,7 @@ var _e = /* @__PURE__ */ new Set([
44
45
  super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
45
46
  }
46
47
  }, M = async (e, t, n, r) => {
47
- let i = new URL(e), a = i.protocol === "https:" ? ge : he, o = Date.now();
48
+ let i = new URL(e), a = i.protocol === "https:" ? ve : _e, o = Date.now();
48
49
  return await new Promise((s, c) => {
49
50
  let l = a({
50
51
  protocol: i.protocol,
@@ -63,7 +64,7 @@ var _e = /* @__PURE__ */ new Set([
63
64
  });
64
65
  if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
65
66
  l.destroy(new j(Date.now() - o, e));
66
- }), l.on("error", (t) => c(_e.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
67
+ }), l.on("error", (t) => c(ye.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
67
68
  l.end(t);
68
69
  return;
69
70
  }
@@ -76,7 +77,7 @@ var _e = /* @__PURE__ */ new Set([
76
77
  }
77
78
  })();
78
79
  });
79
- }, ve = (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`, ye = (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.", be = 16777216, xe = 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) {
80
81
  let n = [], r = 0, i = 0, a = function* (e) {
81
82
  let t = Buffer.concat(n);
82
83
  n = [], r = 0;
@@ -98,12 +99,12 @@ var _e = /* @__PURE__ */ new Set([
98
99
  i += a.byteLength, yield s;
99
100
  }
100
101
  yield* a(!0);
101
- }, Se = (e) => te("sha256").update(Buffer.from(e)).digest("hex"), Ce = async (e) => {
102
+ }, we = (e) => re("sha256").update(Buffer.from(e)).digest("hex"), Te = async (e) => {
102
103
  let t = 0, n = 0, r = {
103
104
  status: 0,
104
105
  body: {}
105
106
  };
106
- for await (let i of xe(e.stream, e.chunkBytes)) {
107
+ for await (let i of Ce(e.stream, e.chunkBytes)) {
107
108
  if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
108
109
  n++;
109
110
  continue;
@@ -129,7 +130,7 @@ var _e = /* @__PURE__ */ new Set([
129
130
  bytes: t,
130
131
  resumedFrom: e.alreadyHeld
131
132
  };
132
- }, N = "voltro-backup-stamp.json", we = (e) => ({
133
+ }, N = "voltro-backup-stamp.json", Ee = (e) => ({
133
134
  stampVersion: 1,
134
135
  dialect: e.dialect,
135
136
  schemaFingerprint: e.schemaFingerprint,
@@ -153,7 +154,7 @@ var _e = /* @__PURE__ */ new Set([
153
154
  voltroVersion: n.voltroVersion,
154
155
  createdAt: n.createdAt
155
156
  };
156
- }, Te = (e) => {
157
+ }, De = (e) => {
157
158
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
158
159
  if (t === null) return {
159
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.`,
@@ -174,7 +175,7 @@ var _e = /* @__PURE__ */ new Set([
174
175
  } catch {
175
176
  return e.trim();
176
177
  }
177
- }, Ee = (e) => {
178
+ }, Oe = (e) => {
178
179
  let t = e.flagUrl ?? e.envUrl;
179
180
  if (t === void 0 || t.trim() === "") return {
180
181
  ok: !1,
@@ -188,7 +189,7 @@ var _e = /* @__PURE__ */ new Set([
188
189
  ok: !0,
189
190
  url: t
190
191
  };
191
- }, De = (e) => {
192
+ }, ke = (e) => {
192
193
  let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
193
194
  return r === 0 ? {
194
195
  pass: !1,
@@ -203,27 +204,27 @@ var _e = /* @__PURE__ */ new Set([
203
204
  pass: !0,
204
205
  lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
205
206
  };
206
- }, I = d({ scope: "voltro:data" }), Oe = async () => {
207
+ }, I = p({ scope: "voltro:data" }), Ae = async () => {
207
208
  try {
208
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");
209
210
  return JSON.parse(n(r, "utf8")).version ?? "unknown";
210
211
  } catch {
211
212
  return "unknown";
212
213
  }
213
- }, ke = async (e) => {
214
+ }, L = async (e) => {
214
215
  try {
215
- return g(await H(e));
216
+ return ee(await U(e));
216
217
  } catch {
217
218
  return null;
218
219
  }
219
- }, L = (e) => {
220
+ }, R = (e) => {
220
221
  if (e.event !== "done") return;
221
222
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
222
223
  I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
223
- }, Ae = async (e) => {
224
+ }, je = async (e) => {
224
225
  let t = (e) => e?.meta?.databaseId;
225
226
  if (e) {
226
- let n = await l({
227
+ let n = await u({
227
228
  name: "target",
228
229
  url: e
229
230
  }, "/_voltro/inspect/app");
@@ -232,29 +233,29 @@ var _e = /* @__PURE__ */ new Set([
232
233
  databaseId: t(n.data)
233
234
  };
234
235
  }
235
- for (let e of await c()) {
236
- let n = await l(e, "/_voltro/inspect/app");
236
+ for (let e of await l()) {
237
+ let n = await u(e, "/_voltro/inspect/app");
237
238
  if (n.kind === "ok") return {
238
239
  label: `${e.name} (${e.url})`,
239
240
  databaseId: t(n.data)
240
241
  };
241
242
  }
242
243
  return null;
243
- }, je = async (e) => {
244
- let t = e.includes("--allow-live"), n = t ? null : await Ae(R(e, "--api-url")), i = v({
245
- dialect: B(),
244
+ }, Me = async (e) => {
245
+ let t = e.includes("--allow-live"), n = t ? null : await je(z(e, "--api-url")), i = m({
246
+ dialect: V(),
246
247
  ...r()
247
- }), a = y({
248
- dialect: B(),
248
+ }), a = h({
249
+ dialect: V(),
249
250
  ...r()
250
- }), { refuse: o, message: s } = re({
251
+ }), { refuse: o, message: s } = ae({
251
252
  liveInstance: n?.label ?? null,
252
253
  allowLive: t,
253
- sameTarget: b(i, n?.databaseId),
254
+ sameTarget: g(i, n?.databaseId),
254
255
  ...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
255
256
  });
256
257
  return !o || (I.error(s ?? "refusing: live instance detected"), !1);
257
- }, R = i, Me = [
258
+ }, z = a, Ne = [
258
259
  "--api-url",
259
260
  "--bundle-key",
260
261
  "--compression",
@@ -272,44 +273,44 @@ var _e = /* @__PURE__ */ new Set([
272
273
  "--timeout",
273
274
  "--tenant",
274
275
  "--token"
275
- ], z = (e) => a(e, Me), B = () => {
276
+ ], B = (e) => o(e, Ne), V = () => {
276
277
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
277
278
  if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
278
279
  return t;
279
- }, V = async (e, t) => {
280
- let n = (await o(e)).makeSqlLayer(t);
281
- return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
282
- }, H = (e) => V(e, r()), Ne = () => {
280
+ }, H = async (e, t) => {
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 = () => {
283
284
  let e = r(), t = [];
284
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;
285
- }, U = async () => {
286
+ }, W = async () => {
286
287
  let r = process.cwd(), i = await t(r);
287
288
  if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
288
- let a = await n(i, void 0), o = B();
289
+ let a = await n(i, void 0), o = V();
289
290
  return {
290
291
  store: a,
291
- snapshot: await H(o),
292
+ snapshot: await U(o),
292
293
  dialect: o
293
294
  };
294
- }, W = (e) => x(e, {
295
- table: ee,
295
+ }, G = (e) => _(e, {
296
+ table: ne,
296
297
  chunkSize: 500
297
- }).pipe(p.map((e) => ({
298
+ }).pipe(y.map((e) => ({
298
299
  key: String(e.key),
299
300
  contentType: String(e.contentType ?? "application/octet-stream"),
300
301
  size: Number(e.size ?? 0)
301
- }))), G = (e) => !!e && e.endsWith(".vbundle"), K = (e) => R(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Pe = (e) => e.includes("--encrypt") || R(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Fe = 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) => {
302
303
  let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
303
304
  await r(i.from(e), n(t));
304
- }, q = (e) => ({ async *[Symbol.asyncIterator]() {
305
+ }, J = (e) => ({ async *[Symbol.asyncIterator]() {
305
306
  let { createReadStream: t } = await import("node:fs");
306
307
  for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
307
- } }), J = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Ie = (t, n) => {
308
- let r = R(t, "--tenant"), i = R(t, "--tables"), a = R(t, "--exclude");
308
+ } }), Y = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Le = (t, n) => {
309
+ let r = z(t, "--tenant"), i = z(t, "--tables"), a = z(t, "--exclude");
309
310
  if (a !== void 0) {
310
311
  if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
311
312
  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.");
312
- let t = J(a), o = new Set(n), s = t.filter((e) => !o.has(e));
313
+ let t = Y(a), o = new Set(n), s = t.filter((e) => !o.has(e));
313
314
  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.`);
314
315
  let c = new Set(t);
315
316
  return {
@@ -322,15 +323,15 @@ var _e = /* @__PURE__ */ new Set([
322
323
  tenantId: r
323
324
  } : i ? {
324
325
  kind: "tables",
325
- tables: J(i)
326
+ tables: Y(i)
326
327
  } : { kind: "all" };
327
- }, Le = async () => {
328
- let e = await u(process.cwd());
329
- return ae(h(e));
330
- }, Re = "voltro/tenant", ze = async () => {
331
- let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Re)).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);
332
333
  return e.length > 0 ? e : void 0;
333
- }, Be = (e) => {
334
+ }, Ve = (e) => {
334
335
  for (let t of e.tables) if (t.samples.length !== 0) {
335
336
  console.log(`\n ${t.table}`);
336
337
  for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
@@ -343,8 +344,8 @@ var _e = /* @__PURE__ */ new Set([
343
344
  console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
344
345
  for (let t of e.unclassified) console.log(` ${t}`);
345
346
  } else console.log("\n ✓ every exported column is classified — safe to run.");
346
- }, Ve = async (t) => {
347
- let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = R(t.args, "--chunk-size"), o = a === void 0 ? be : 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);
348
349
  if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
349
350
  let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
350
351
  for (; l <= o;) {
@@ -359,7 +360,7 @@ var _e = /* @__PURE__ */ new Set([
359
360
  kind: "small",
360
361
  body: Buffer.concat(c)
361
362
  };
362
- let d = n(process.cwd(), t.bundleDir), f = Se(G(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), {
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), {
363
364
  ...t.headers,
364
365
  "x-import-upload-id": p,
365
366
  "x-import-probe": "1"
@@ -372,7 +373,7 @@ var _e = /* @__PURE__ */ new Set([
372
373
  if (e.done === !0) return;
373
374
  yield e.value;
374
375
  }
375
- })(), _ = 0, v = await Ce({
376
+ })(), _ = 0, v = await Te({
376
377
  stream: g,
377
378
  chunkBytes: o,
378
379
  uploadId: p,
@@ -408,27 +409,38 @@ var _e = /* @__PURE__ */ new Set([
408
409
  body: Buffer.from(JSON.stringify(v.body))
409
410
  }
410
411
  };
411
- }, Y = (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", Z = (t) => {
412
- let n = R(t, "--timeout");
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) => {
424
+ let n = z(t, "--timeout");
413
425
  if (n === void 0) return {};
414
426
  let r = Number(n);
415
427
  if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
416
428
  return { timeoutMs: Math.round(r * 1e3) };
417
- }, He = async (e) => {
418
- let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), r = z(e);
429
+ }, Ke = async (e) => {
430
+ let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), r = B(e);
419
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;
420
432
  if (t) {
421
- let t = R(e, "--api-url");
433
+ let t = z(e, "--api-url");
422
434
  if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
423
- let i = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
435
+ let i = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
424
436
  if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
425
- if (R(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;
426
- let a = R(e, "--tables"), o = {
427
- ...R(e, "--profile") ? { profile: R(e, "--profile") } : {},
428
- ...R(e, "--tenant") ? { tenant: R(e, "--tenant") } : {},
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;
438
+ let a = z(e, "--tables"), o = {
439
+ ...z(e, "--profile") ? { profile: z(e, "--profile") } : {},
440
+ ...z(e, "--tenant") ? { tenant: z(e, "--tenant") } : {},
429
441
  ...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
430
- ...R(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
431
- ...R(e, "--compression") ? { compression: R(e, "--compression") } : {},
442
+ ...z(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
443
+ ...z(e, "--compression") ? { compression: z(e, "--compression") } : {},
432
444
  ...e.includes("--assets") ? { assets: !0 } : {},
433
445
  ...n ? { bundleKey: n } : {}
434
446
  }, s;
@@ -439,7 +451,7 @@ var _e = /* @__PURE__ */ new Set([
439
451
  }, Z(e));
440
452
  } catch (e) {
441
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;
442
- if (e instanceof A) return I.error(`api export: ${ve(e)}`), 1;
454
+ if (e instanceof A) return I.error(`api export: ${be(e)}`), 1;
443
455
  throw e;
444
456
  }
445
457
  if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
@@ -458,57 +470,57 @@ var _e = /* @__PURE__ */ new Set([
458
470
  }), 0;
459
471
  }
460
472
  if (!r) return I.error("a <outDir> is required for a direct export"), 1;
461
- let i = R(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await U(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = R(e, "--compression") ?? a.compression, m = R(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? Ie(e, c.tables.map((e) => e.name)), v = h ? await Le() : void 0, y = _.kind === "tenant" ? await ze() : void 0, b = y ? { tenantTables: y } : {};
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 } : {};
462
474
  if (e.includes("--dry-run")) {
463
- let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
475
+ let e = g.kind === "tables" && g.tables ? [...g.tables] : s.tables.map((e) => e.name);
464
476
  if (I.info("export DRY RUN — nothing was written", {
465
477
  outDir: r,
466
478
  tables: e.length,
467
- scope: _.kind,
468
- consistency: m,
469
- compression: p ?? "zstd",
479
+ scope: g.kind,
480
+ consistency: p,
481
+ compression: f ?? "zstd",
470
482
  assets: u,
471
- masking: h ? "profile policy applies" : "none"
472
- }), I.info(`would export: ${e.join(", ")}`), !h) return 0;
473
- let t = await f.runPromise(se({
483
+ masking: m ? "profile policy applies" : "none"
484
+ }), I.info(`would export: ${e.join(", ")}`), !m) return 0;
485
+ let t = await v.runPromise(ue({
474
486
  store: o,
475
- snapshot: c,
476
- masking: h,
477
- ...v ? { classification: v } : {},
478
- ...g ? {} : {
479
- scope: _,
487
+ snapshot: s,
488
+ masking: m,
489
+ ..._ ? { classification: _ } : {},
490
+ ...h ? {} : {
491
+ scope: g,
480
492
  ...b
481
493
  }
482
494
  }));
483
- return Be(t), +(t.unclassified.length > 0);
495
+ return Ve(t), +(t.unclassified.length > 0);
484
496
  }
485
- if (G(r)) {
486
- 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-"));
497
+ if (K(r)) {
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-"));
487
499
  try {
488
- let t = await f.runPromise(ce({
500
+ let t = await v.runPromise(E({
489
501
  store: o,
490
- snapshot: c,
502
+ snapshot: s,
491
503
  dialect: l,
492
- outDir: s,
493
- ...g ? { subset: g } : {
494
- scope: _,
504
+ outDir: c,
505
+ ...h ? { subset: h } : {
506
+ scope: g,
495
507
  ...b
496
508
  },
497
- consistency: m,
498
- ...p ? { compression: p } : {},
499
- ...h ? { masking: h } : {},
500
- ...v ? { classification: v } : {},
501
- onProgress: L
502
- })), n = T({
503
- dir: s,
504
- ...d ? { blobs: pe(d, () => W(o)) } : {}
505
- }), a = Pe(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);
506
518
  if (a) {
507
- let t = K(e);
519
+ let t = q(e);
508
520
  if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
509
- n = oe(n, t);
521
+ n = ce(n, t);
510
522
  }
511
- return await Fe(n, i(process.cwd(), r)), I.info("export complete (single file)", {
523
+ return await Ie(n, i(process.cwd(), r)), I.info("export complete (single file)", {
512
524
  file: r,
513
525
  encrypted: a,
514
526
  assets: u,
@@ -516,27 +528,27 @@ var _e = /* @__PURE__ */ new Set([
516
528
  rows: t.tables.reduce((e, t) => e + t.rowCount, 0)
517
529
  }), 0;
518
530
  } finally {
519
- await n.rm(s, {
531
+ await n.rm(c, {
520
532
  recursive: !0,
521
533
  force: !0
522
534
  });
523
535
  }
524
536
  }
525
- let x = await f.runPromise(ce({
537
+ let x = await v.runPromise(E({
526
538
  store: o,
527
- snapshot: c,
539
+ snapshot: s,
528
540
  dialect: l,
529
541
  outDir: r,
530
- ...g ? { subset: g } : {
531
- scope: _,
542
+ ...h ? { subset: h } : {
543
+ scope: g,
532
544
  ...b
533
545
  },
534
- consistency: m,
535
- ...p ? { compression: p } : {},
536
- ...h ? { masking: h } : {},
537
- ...v ? { classification: v } : {},
538
- ...d ? { assets: de(d, () => W(o)) } : {},
539
- 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
540
552
  }));
541
553
  return I.info("export complete", {
542
554
  outDir: r,
@@ -546,13 +558,13 @@ var _e = /* @__PURE__ */ new Set([
546
558
  compression: x.compression,
547
559
  ...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
548
560
  }), 0;
549
- }, Ue = () => "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.", We = (e) => {
550
- let t = R(e, "--tables");
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) => {
562
+ let t = z(e, "--tables");
551
563
  return {
552
564
  ...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
553
565
  ...e.includes("--dry-run") ? { dryRun: !0 } : {}
554
566
  };
555
- }, Ge = (e) => ({
567
+ }, Q = (e) => ({
556
568
  tables: e.tablesWritten,
557
569
  rows: e.rowsWritten,
558
570
  carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -560,9 +572,9 @@ var _e = /* @__PURE__ */ new Set([
560
572
  skippedTables: e.tablesSkipped,
561
573
  skippedRows: e.rowsSkipped
562
574
  } : {}
563
- }), Ke = (e, t) => {
575
+ }), Ye = (e, t) => {
564
576
  if (e.includes("--dry-run")) {
565
- I.info("import DRY RUN — nothing was written", t), I.info(Ue());
577
+ I.info("import DRY RUN — nothing was written", t), I.info(qe());
566
578
  return;
567
579
  }
568
580
  if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
@@ -570,8 +582,8 @@ var _e = /* @__PURE__ */ new Set([
570
582
  return;
571
583
  }
572
584
  I.info("import complete", t);
573
- }, qe = Symbol("refused"), Q = (e) => {
574
- let t = ne(e) ?? e;
585
+ }, Xe = Symbol("refused"), Ze = (e) => {
586
+ let t = ie(e) ?? e;
575
587
  if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
576
588
  let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
577
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.`);
@@ -581,7 +593,7 @@ var _e = /* @__PURE__ */ new Set([
581
593
  n.length < i && I.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
582
594
  let s = i - a;
583
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;
584
- }, Je = (e) => {
596
+ }, Qe = (e) => {
585
597
  let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
586
598
  try {
587
599
  let e = JSON.parse(t), r = e.error ?? e.message;
@@ -621,121 +633,126 @@ var _e = /* @__PURE__ */ new Set([
621
633
  return;
622
634
  }
623
635
  }
624
- }, Ye = async (e) => {
625
- let t = R(e, "--target") === "api", n = R(e, "--bundle-key"), i = z(e);
636
+ }, $e = async (e) => {
637
+ let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
626
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;
627
- let a = R(e, "--mode") ?? "upsert", o = R(e, "--on-conflict") ?? "skip";
639
+ let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
628
640
  if (t) {
629
- let t = R(e, "--api-url");
641
+ let t = z(e, "--api-url");
630
642
  if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
631
- let r = R(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
643
+ let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
632
644
  if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
633
- let s = R(e, "--tables"), c = {
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 = {
634
648
  authorization: `Bearer ${r}`,
635
649
  "x-import-mode": a,
636
650
  "x-import-on-conflict": o,
637
651
  ...X(e, a) ? { "x-import-atomic": "1" } : {},
638
652
  ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
653
+ ...c === void 0 ? {} : { "x-import-rollback-key": c },
639
654
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
640
655
  ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
641
656
  ...s ? { "x-import-tables": s } : {}
642
- }, { resolve: l } = await import("node:path"), u;
643
- if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
644
- else if (G(i)) {
645
- let t = q(l(process.cwd(), i)), n = K(e);
646
- n && (t = w(t, n)), c["content-type"] = "application/octet-stream", u = t;
647
- } else await (await import("node:fs/promises")).access(l(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."), c["content-type"] = "application/octet-stream", u = T({ dir: l(process.cwd(), i) });
648
- a === "replace" && !X(e, a) && I.warn(Y(!0));
649
- let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
657
+ }, { resolve: u } = await import("node:path"), d;
658
+ if (n) l["content-type"] = "application/json", d = JSON.stringify({ bundleKey: n });
659
+ else if (K(i)) {
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;
650
665
  try {
651
- let t = typeof u == "string" ? null : await Ve({
652
- stream: u,
653
- url: d,
654
- headers: c,
666
+ let t = typeof d == "string" ? null : await He({
667
+ stream: d,
668
+ url: f,
669
+ headers: l,
655
670
  bundleDir: i,
656
671
  args: e,
657
672
  log: I
658
673
  });
659
- (t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), f = t === null ? await M(d, u, c, Z(e)) : t.kind === "sent" ? t.res : await M(d, t.body, c, Z(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));
660
675
  } catch (e) {
661
- if (e instanceof j) return I.error(`api import: ${ye(e, a)}`), 1;
662
- if (e instanceof A) return I.error(`api import: ${ve(e)}`), 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;
663
678
  throw e;
664
679
  }
665
- if (f.status < 200 || f.status >= 300) return Q(Je(f.body)) || I.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
666
- let p = JSON.parse(f.body.toString("utf8"));
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"));
667
682
  return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
668
683
  via: t,
669
- ...p
670
- }), I.info(Ue()), 0) : (I.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)", {
671
686
  via: t,
672
- ...p
687
+ ...m
673
688
  }), 0);
674
689
  }
675
690
  if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
676
- if (!await je(e)) return 1;
677
- a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(Y(!1));
678
- let { store: s, snapshot: c } = await U(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = B() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
679
- if (G(i)) {
680
- 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-"));
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;
694
+ if (K(i)) {
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-"));
681
696
  try {
682
- let t = q(r(process.cwd(), i)), n = K(e);
683
- return n && (t = w(t, n)), await me(t, {
684
- dir: h,
685
- blobs: fe(d ?? S({}))
686
- }), Ke(e, {
697
+ let t = J(r(process.cwd(), i)), n = q(e);
698
+ return n && (t = w(t, n)), await ge(t, {
699
+ dir: m,
700
+ blobs: me(d ?? S({}))
701
+ }), Ye(e, {
687
702
  file: i,
688
- ...Ge(await f.runPromise(le({
703
+ ...Q(await v.runPromise(D({
689
704
  store: s,
690
- bundleDir: h,
705
+ bundleDir: m,
691
706
  mode: a,
692
707
  onConflict: o,
693
708
  atomic: X(e, a),
709
+ ...Ge(e, a, i),
694
710
  verify: !e.includes("--no-verify"),
695
- targetDialect: B(),
711
+ targetDialect: V(),
696
712
  allowIncompatible: l,
697
713
  targetSnapshot: c,
698
714
  force: l,
699
715
  onWarn: u,
700
- onProgress: L,
716
+ onProgress: R,
701
717
  assetsAlreadyRestored: !0,
702
- ...We(e),
703
- ...p ? { copyLoader: p } : {}
718
+ ...Je(e),
719
+ ...f ? { copyLoader: f } : {}
704
720
  })))
705
721
  }), 0;
706
722
  } finally {
707
- await p?.close(), await n.rm(h, {
723
+ await f?.close(), await n.rm(m, {
708
724
  recursive: !0,
709
725
  force: !0
710
726
  });
711
727
  }
712
728
  }
713
- let m = await f.runPromise(le({
729
+ let p = await v.runPromise(D({
730
+ ...Ge(e, a, i),
714
731
  store: s,
715
732
  bundleDir: i,
716
733
  mode: a,
717
734
  onConflict: o,
718
735
  atomic: X(e, a),
719
736
  verify: !e.includes("--no-verify"),
720
- targetDialect: B(),
737
+ targetDialect: V(),
721
738
  allowIncompatible: l,
722
739
  targetSnapshot: c,
723
740
  force: l,
724
741
  onWarn: u,
725
- onProgress: L,
726
- ...We(e),
727
- ...d ? { assets: ue(d) } : {},
728
- ...p ? { copyLoader: p } : {}
742
+ onProgress: R,
743
+ ...Je(e),
744
+ ...d ? { assets: fe(d) } : {},
745
+ ...f ? { copyLoader: f } : {}
729
746
  })).catch((e) => {
730
- if (Q(e)) return qe;
747
+ if (Ze(e)) return Xe;
731
748
  throw e;
732
- }).finally(() => p?.close());
733
- return typeof m == "symbol" ? 1 : (Ke(e, {
749
+ }).finally(() => f?.close());
750
+ return typeof p == "symbol" ? 1 : (Ye(e, {
734
751
  bundleDir: i,
735
- ...Ge(m),
736
- assets: m.manifest.assets.count
752
+ ...Q(p),
753
+ assets: p.manifest.assets.count
737
754
  }), 0);
738
- }, Xe = () => {
755
+ }, et = () => {
739
756
  let e = r();
740
757
  return {
741
758
  ...e.url ? { url: e.url } : {},
@@ -745,17 +762,17 @@ var _e = /* @__PURE__ */ new Set([
745
762
  ...e.password ? { password: e.password } : {},
746
763
  ...e.database ? { database: e.database } : {}
747
764
  };
748
- }, Ze = async (e) => {
749
- let t = z(e);
765
+ }, tt = async (e) => {
766
+ let t = B(e);
750
767
  if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
751
- let n = B(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
768
+ let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
752
769
  await r(t, { recursive: !0 });
753
770
  let o = a(t, C(n));
754
- await f.runPromise(O(ie(n, Xe(), o)));
755
- let s = we({
771
+ await v.runPromise(O(oe(n, et(), o)));
772
+ let s = Ee({
756
773
  dialect: n,
757
- schemaFingerprint: await ke(n),
758
- voltroVersion: await Oe(),
774
+ schemaFingerprint: await L(n),
775
+ voltroVersion: await Ae(),
759
776
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
760
777
  });
761
778
  return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
@@ -765,11 +782,11 @@ var _e = /* @__PURE__ */ new Set([
765
782
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
766
783
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
767
784
  }), 0;
768
- }, Qe = async (e, t) => {
769
- let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Ee({
770
- flagUrl: R(t, "--drill-url"),
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({
787
+ flagUrl: z(t, "--drill-url"),
771
788
  envUrl: process.env.DRILL_DB_URL,
772
- liveKeys: Ne()
789
+ liveKeys: Pe()
773
790
  });
774
791
  if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
775
792
  let s = await i(r(e, N), "utf8").then(P, () => null);
@@ -777,42 +794,42 @@ var _e = /* @__PURE__ */ new Set([
777
794
  I.info("restore drill: restoring into throwaway DB", {
778
795
  dialect: n,
779
796
  target: F(o.url)
780
- }), await f.runPromise(O(D(n, { url: o.url }, a)));
797
+ }), await v.runPromise(O(de(n, { url: o.url }, a)));
781
798
  let c = null, l = 0;
782
799
  try {
783
- let e = await V(n, { url: o.url });
784
- c = g(e), l = e.tables.length;
800
+ let e = await H(n, { url: o.url });
801
+ c = ee(e), l = e.tables.length;
785
802
  } catch (e) {
786
803
  return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
787
804
  }
788
- let u = De({
805
+ let u = ke({
789
806
  stampFingerprint: s?.schemaFingerprint ?? null,
790
807
  restoredFingerprint: c,
791
808
  restoredTableCount: l
792
809
  });
793
810
  for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
794
811
  return +!u.pass;
795
- }, $e = async (e) => {
796
- let t = z(e);
812
+ }, rt = async (e) => {
813
+ let t = B(e);
797
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;
798
- if (e.includes("--drill")) return Qe(t, e);
799
- if (!await je(e)) return 1;
800
- let n = B(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Te({
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({
801
818
  stamp: await i(r(t, N), "utf8").then(P, () => null),
802
819
  currentDialect: n,
803
- currentFingerprint: await ke(n)
820
+ currentFingerprint: await L(n)
804
821
  });
805
822
  for (let e of o.info) I.info(`backup stamp: ${e}`);
806
823
  if (o.warn && I.warn(o.warn), o.refuse) {
807
824
  if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
808
825
  I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
809
826
  }
810
- return await f.runPromise(O(D(n, Xe(), a))), I.info("native restore complete", {
827
+ return await v.runPromise(O(de(n, et(), a))), I.info("native restore complete", {
811
828
  dialect: n,
812
829
  artifact: a
813
830
  }), 0;
814
- }, et = async (e) => {
815
- let t = z(e);
831
+ }, it = async (e) => {
832
+ let t = B(e);
816
833
  if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
817
834
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
818
835
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
@@ -824,22 +841,41 @@ var _e = /* @__PURE__ */ new Set([
824
841
  let s = await i.readFile(n, "utf8").catch(() => void 0);
825
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);
826
843
  }
827
- let o = K(e), s = () => {
828
- let e = q(a);
844
+ let o = q(e), s = () => {
845
+ let e = J(a);
829
846
  return o ? w(e, o) : e;
830
847
  };
831
848
  if (e.includes("--json")) {
832
- let e = (await E(s(), ["manifest.json"])).get("manifest.json");
849
+ let e = (await T(s(), ["manifest.json"])).get("manifest.json");
833
850
  return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
834
851
  }
835
- let c = (await E(s(), ["README.md"])).get("README.md");
852
+ let c = (await T(s(), ["README.md"])).get("README.md");
836
853
  if (c) return console.log(c.toString("utf8")), 0;
837
- let l = (await E(s(), ["manifest.json"])).get("manifest.json");
854
+ let l = (await T(s(), ["manifest.json"])).get("manifest.json");
838
855
  return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
839
- }, tt = async (e) => {
840
- let t = z(e), n = z(e.filter((e) => e !== t));
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) => {
876
+ let t = B(e), n = B(e.filter((e) => e !== t));
841
877
  if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
842
- let { resolve: r } = await import("node:path"), i = q(r(process.cwd(), t)), a = K(e);
878
+ let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
843
879
  return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), I.info("unpack complete", {
844
880
  file: t,
845
881
  dir: n
@@ -869,6 +905,8 @@ var _e = /* @__PURE__ */ new Set([
869
905
  "--timeout"
870
906
  ]
871
907
  },
908
+ "clear-staging": { common: ["--yes"] },
909
+ "clear-replace-marker": { common: ["--yes"] },
872
910
  import: {
873
911
  common: [
874
912
  "--target",
@@ -885,14 +923,17 @@ var _e = /* @__PURE__ */ new Set([
885
923
  directOnly: [
886
924
  "--no-verify",
887
925
  "--allow-live",
888
- "--passphrase"
926
+ "--passphrase",
927
+ "--rollback-dir",
928
+ "--no-rollback"
889
929
  ],
890
930
  apiOnly: [
891
931
  "--api-url",
892
932
  "--token",
893
933
  "--bundle-key",
894
934
  "--timeout",
895
- "--chunk-size"
935
+ "--chunk-size",
936
+ "--rollback-key"
896
937
  ]
897
938
  },
898
939
  unpack: { common: ["--passphrase", "--json"] },
@@ -906,13 +947,13 @@ var _e = /* @__PURE__ */ new Set([
906
947
  "--drill-url",
907
948
  "--api-url"
908
949
  ] }
909
- }, nt = (e) => e.split("=")[0], rt = (e, t) => {
950
+ }, ct = (e) => e.split("=")[0], lt = (e, t) => {
910
951
  let n = $[e];
911
952
  if (!n) return;
912
- let r = R(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
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 ?? []);
913
954
  for (let n of t) {
914
955
  if (!n.startsWith("--")) continue;
915
- let t = nt(n);
956
+ let t = ct(n);
916
957
  if (i.has(t)) continue;
917
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.`;
918
959
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -922,13 +963,13 @@ var _e = /* @__PURE__ */ new Set([
922
963
  ].includes(t)).map(([e]) => e);
923
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.`;
924
965
  }
925
- }, it = async (e) => {
966
+ }, ut = async (e) => {
926
967
  let [t, ...n] = e;
927
968
  if (t !== void 0 && $[t] !== void 0) {
928
- let e = rt(t, n);
969
+ let e = lt(t, n);
929
970
  if (e) return I.error(e), 2;
930
971
  }
931
- return t === "export" ? He(n) : t === "import" ? Ye(n) : t === "unpack" ? tt(n) : t === "inspect" ? et(n) : t === "backup" ? Ze(n) : t === "restore" ? $e(n) : (I.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);
932
973
  };
933
974
  //#endregion
934
- export { it as runDataCommand };
975
+ export { ut as runDataCommand };