@voltro/cli 0.49.0 → 0.50.1

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 (108) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/dist/apiBuild-BZ-plaQN.js +2 -0
  3. package/dist/{apiBuild-QmYHaCF4.js → apiBuild-D3s7lx57.js} +2 -2
  4. package/dist/appGraph-CM9wvUmD.js +2 -0
  5. package/dist/appGraph-Cv2W_wO5.js +111 -0
  6. package/dist/bin.js +1 -1
  7. package/dist/{build-Bi4O_g7T.js → build-CFmqF39c.js} +7 -7
  8. package/dist/{checkCommand-udbQpphy.js → checkCommand-DomvYDi1.js} +1 -1
  9. package/dist/{checkCommand-D8vjQXQt.js → checkCommand-DsKIjL6k.js} +54 -45
  10. package/dist/{codegenCommand-DApSH7hK.js → codegenCommand-C0S27nbB.js} +2 -2
  11. package/dist/{codemodRunner-De1_316I.js → codemodRunner-DwY8Rjbl.js} +696 -481
  12. package/dist/{commands-a5PxCaIg.js → commands-Brp4DI8u.js} +22 -22
  13. package/dist/{dashboardCommand-DaoUUqJS.js → dashboardCommand-bbLfc9FW.js} +3 -3
  14. package/dist/{dataCommand-CJv_OMoS.js → dataCommand-k9mXTXin.js} +511 -333
  15. package/dist/{dev-C31Nvlbv.js → dev-C0cbqysu.js} +4 -4
  16. package/dist/{dev-DFVuzOj7.js → dev-Dz9GqA6P.js} +1 -1
  17. package/dist/doctorCommand-BlciWUJb.js +2 -0
  18. package/dist/{doctorCommand-B3uraeAe.js → doctorCommand-DyViaaCP.js} +38 -22
  19. package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
  20. package/dist/{dormancyCommand-QUXQ9yoz.js → dormancyCommand-DMZPA_HW.js} +1 -1
  21. package/dist/{embeddingsCommand-Zw1-rYSF.js → embeddingsCommand-CMxRPkg6.js} +1 -1
  22. package/dist/{envCommand-DtaJ4xEf.js → envCommand-CDLr-yD4.js} +2 -2
  23. package/dist/{evolveCommand-JDD3hE1i.js → evolveCommand-Ct4NesL3.js} +2 -2
  24. package/dist/{generateCommand-CcyvH2ve.js → generateCommand-BDdOxhnv.js} +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/{infoCommand-COsUC1DB.js → infoCommand-orsKJKUy.js} +1 -1
  27. package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
  28. package/dist/inspect-CuGDYES0.js +2 -0
  29. package/dist/{interruptedReplace-C1ZI84xi.js → interruptedReplace-C3O3M1MM.js} +2 -1
  30. package/dist/interruptedReplace-CvmiAM9K.js +2 -0
  31. package/dist/manifestBuild-CPjhvM62.js +2 -0
  32. package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
  33. package/dist/{runtimeTrace-Di6fcndp.js → runtimeTrace-Bo8jWsNe.js} +1 -1
  34. package/dist/{sdkgen-CIi5gM17.js → sdkgen-OZhiT2vz.js} +1 -1
  35. package/dist/serveCommand-Bbgg7Paq.js +2 -0
  36. package/dist/{serveCommand-N3udT49p.js → serveCommand-YbD5VgfV.js} +8 -8
  37. package/dist/serveEntry.js +1 -1
  38. package/dist/{start-DvlYDyAr.js → start-7b_bL8Uc.js} +1 -1
  39. package/dist/{start-7HcSIKEp.js → start-B1nH9ahf.js} +2 -2
  40. package/dist/startEntry.js +1 -1
  41. package/dist/{updateCommand-2BfbeMvp.js → updateCommand-BgH6vTtq.js} +2 -1
  42. package/dist/updateCommand-BweY17z-.js +2 -0
  43. package/dist/{webDev-B4IEMB0n.js → webDev-BbdkzMbd.js} +1 -1
  44. package/dist/webDev-BcRFTsMA.js +2 -0
  45. package/dist/{webhooksCommand-CwX9IzvJ.js → webhooksCommand-DjE6u0r4.js} +1 -1
  46. package/package.json +41 -17
  47. package/templates/AGENTS.md +1 -1
  48. package/templates/agent-docs/_index.md +1 -1
  49. package/templates/agent-docs/cli.md +113 -3
  50. package/templates/agent-docs/data.md +36 -0
  51. package/templates/agent-docs/whats-new.md +30 -41
  52. package/templates/apps/api-ai/package.json +7 -7
  53. package/templates/apps/api-auth/package.json +8 -8
  54. package/templates/apps/api-backend/package.json +7 -7
  55. package/templates/apps/api-backend-deactivation/package.json +7 -7
  56. package/templates/apps/api-backend-mail/package.json +8 -8
  57. package/templates/apps/api-backend-mariadb/package.json +9 -9
  58. package/templates/apps/api-backend-sqlite/package.json +8 -8
  59. package/templates/apps/api-backend-storage/package.json +8 -8
  60. package/templates/apps/api-cms/package.json +10 -10
  61. package/templates/apps/api-collab/package.json +8 -8
  62. package/templates/apps/api-data-advanced/package.json +8 -8
  63. package/templates/apps/api-durable/package.json +8 -8
  64. package/templates/apps/api-feature-flags/package.json +9 -9
  65. package/templates/apps/api-governance/package.json +8 -8
  66. package/templates/apps/api-kv/package.json +8 -8
  67. package/templates/apps/api-moderation/package.json +8 -8
  68. package/templates/apps/api-observability/package.json +8 -8
  69. package/templates/apps/api-ratelimit/package.json +8 -8
  70. package/templates/apps/api-rbac/package.json +8 -8
  71. package/templates/apps/api-rest/package.json +7 -7
  72. package/templates/apps/api-saas/package.json +11 -11
  73. package/templates/apps/api-saas-starter/package.json +10 -10
  74. package/templates/apps/api-search/package.json +8 -8
  75. package/templates/apps/api-status/package.json +8 -8
  76. package/templates/apps/api-versioning/package.json +8 -8
  77. package/templates/apps/api-webhooks/package.json +9 -9
  78. package/templates/apps/changelog/package.json +6 -6
  79. package/templates/apps/edge-functions/package.json +2 -2
  80. package/templates/apps/frontend-admin/package.json +8 -8
  81. package/templates/apps/frontend-app/package.json +9 -9
  82. package/templates/apps/frontend-auth/package.json +8 -8
  83. package/templates/apps/frontend-blank/package.json +7 -7
  84. package/templates/apps/frontend-cms/package.json +9 -9
  85. package/templates/apps/frontend-collab/package.json +10 -10
  86. package/templates/apps/frontend-contact/package.json +7 -7
  87. package/templates/apps/frontend-dashboard/package.json +7 -7
  88. package/templates/apps/frontend-docs/package.json +7 -7
  89. package/templates/apps/frontend-i18n/package.json +6 -6
  90. package/templates/apps/frontend-landing/package.json +7 -7
  91. package/templates/apps/frontend-portal/package.json +8 -8
  92. package/templates/apps/frontend-saas/package.json +8 -8
  93. package/templates/apps/frontend-spa/package.json +7 -7
  94. package/templates/apps/frontend-ssr/package.json +7 -7
  95. package/templates/apps/frontend-ssr-api/package.json +8 -8
  96. package/templates/apps/frontend-static-blog/package.json +6 -6
  97. package/templates/apps/frontend-status/package.json +8 -8
  98. package/templates/apps/mobile-app/package.json +4 -4
  99. package/dist/apiBuild-dZbHdqMg.js +0 -2
  100. package/dist/appGraph-KGDPTuTy.js +0 -110
  101. package/dist/appGraph-zuMGKVYX.js +0 -2
  102. package/dist/doctorCommand-CPL6EhrW.js +0 -2
  103. package/dist/inspect-DUze25t0.js +0 -2
  104. package/dist/interruptedReplace-DNJ7MKph.js +0 -2
  105. package/dist/manifestBuild-j0n109tt.js +0 -2
  106. package/dist/serveCommand-CzSDngKm.js +0 -2
  107. package/dist/updateCommand-CtYm7aKH.js +0 -2
  108. package/dist/webDev-B79ji8M5.js +0 -2
@@ -1,6 +1,6 @@
1
1
  import { i as e, t } from "./subcommandNames-DpYs3DXr.js";
2
2
  import { t as n } from "./cliError-BmdYnghb.js";
3
- import { c as r, i, o as a, z as o } from "./dev-C31Nvlbv.js";
3
+ import { c as r, i, o as a, z as o } from "./dev-C0cbqysu.js";
4
4
  import { n as s, t as c } from "./cliArgs-D4p8n7EE.js";
5
5
  import { r as l } from "./dialectDriver-czCHYpeH.js";
6
6
  import { i as u, n as d, r as f, t as p } from "./dataProfile-Du3ztYxv.js";
@@ -8,17 +8,17 @@ import { o as m, t as h } from "./inspectFetch-EMuhTG_9.js";
8
8
  import { r as g } from "./dbCommand-Bf7Cy0mn.js";
9
9
  import { resolve as _ } from "node:path";
10
10
  import { createLogger as v } from "@voltro/logger";
11
- import { DATA_TRANSFERS_TABLE as y, connectionIdentity as b, normaliseTarget as x, sameDatabase as S, streamTable as C } from "@voltro/database";
12
- import { Effect as w, Stream as ee } from "effect";
13
- import { SqlClient as te } from "@effect/sql";
14
- import { declaredSnapshot as ne, fingerprintSchema as re, introspectSchema as ie } from "@voltro/database/sql";
15
- import { STORAGE_REFS_TABLE as ae, resolveStorageProvider as T } from "@voltro/plugin-storage";
16
- import { createHash as oe } from "node:crypto";
17
- import { asImportError as se, assessLiveImport as ce, backupArtifactName as E, backupCommand as le, classificationFromSnapshot as ue, decryptStream as D, encryptStream as de, packBundle as fe, peekBundle as O, previewMasking as pe, restoreCommand as k, runExport as me, runImport as he, runNativeStep as A, storageAssetSink as ge, storageAssetSource as _e, storageBlobSink as ve, storageBlobSource as ye, unpackBundle as be, unpackBundleToDir as xe } from "@voltro/data-transfer";
18
- import { request as Se } from "node:http";
19
- import { request as Ce } from "node:https";
11
+ import { DATA_TRANSFERS_TABLE as y, REPLACE_IN_PROGRESS_TABLE as b, _voltroDataTransfersTable as x, _voltroReplaceInProgressTable as S, connectionIdentity as C, derivedRowId as ee, ensureTableRegistered as w, normaliseTarget as te, sameDatabase as ne, streamTable as re } from "@voltro/database";
12
+ import { Cause as T, Effect as E, Exit as ie, Option as ae, Stream as oe } from "effect";
13
+ import { SqlClient as se } from "@effect/sql";
14
+ import { declaredSnapshot as ce, fingerprintSchema as le, introspectSchema as ue } from "@voltro/database/sql";
15
+ import { STORAGE_REFS_TABLE as de, appStorageProvider as fe, configuredStorageProvider as D } from "@voltro/plugin-storage";
16
+ import { createHash as pe } from "node:crypto";
17
+ import { ASSETS_DIR as O, ASSET_INDEX_FILE as k, BundleError as me, asImportError as he, assessLiveImport as ge, backupArtifactName as A, backupCommand as _e, captureAssetsToCas as ve, classificationFromSnapshot as ye, decryptStream as j, encryptStream as be, packBundle as xe, peekBundle as M, previewMasking as Se, restoreAssetsFromCas as Ce, restoreCommand as we, runExport as Te, runImport as Ee, runNativeStep as N, storageAssetSink as De, storageAssetSource as Oe, storageBlobSink as ke, storageBlobSource as Ae, unpackBundle as je, unpackBundleToDir as Me } from "@voltro/data-transfer";
18
+ import { request as Ne } from "node:http";
19
+ import { request as Pe } from "node:https";
20
20
  //#region src/longRequest.ts
21
- var we = /* @__PURE__ */ new Set([
21
+ var Fe = /* @__PURE__ */ new Set([
22
22
  "ECONNREFUSED",
23
23
  "ETIMEDOUT",
24
24
  "EHOSTUNREACH",
@@ -26,7 +26,7 @@ var we = /* @__PURE__ */ new Set([
26
26
  "ENOTFOUND",
27
27
  "ECONNRESET",
28
28
  "EAI_AGAIN"
29
- ]), j = class extends Error {
29
+ ]), P = class extends Error {
30
30
  url;
31
31
  cause;
32
32
  constructor(e, t) {
@@ -39,14 +39,14 @@ var we = /* @__PURE__ */ new Set([
39
39
  })();
40
40
  super(`the instance at ${n} is not answering (${t.code ?? "connect failed"}).`), this.url = e, this.cause = t, this.name = "InstanceUnreachable";
41
41
  }
42
- }, M = class extends Error {
42
+ }, F = class extends Error {
43
43
  elapsedMs;
44
44
  url;
45
45
  constructor(e, t) {
46
46
  super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
47
47
  }
48
- }, N = async (e, t, n, r) => {
49
- let i = new URL(e), a = i.protocol === "https:" ? Ce : Se, o = Date.now();
48
+ }, I = async (e, t, n, r) => {
49
+ let i = new URL(e), a = i.protocol === "https:" ? Pe : Ne, o = Date.now();
50
50
  return await new Promise((s, c) => {
51
51
  let l = a({
52
52
  protocol: i.protocol,
@@ -64,8 +64,8 @@ var we = /* @__PURE__ */ new Set([
64
64
  })), e.on("error", c);
65
65
  });
66
66
  if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
67
- l.destroy(new M(Date.now() - o, e));
68
- }), l.on("error", (t) => c(we.has(t.code ?? "") ? new j(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
67
+ l.destroy(new F(Date.now() - o, e));
68
+ }), l.on("error", (t) => c(Fe.has(t.code ?? "") ? new P(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
69
69
  l.end(t);
70
70
  return;
71
71
  }
@@ -78,7 +78,7 @@ var we = /* @__PURE__ */ new Set([
78
78
  }
79
79
  })();
80
80
  });
81
- }, P = (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`, Te = (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.", Ee = 16777216, De = async function* (e, t) {
81
+ }, L = (e) => `${e.message}\n That address came from --api-url (or VOLTRO_API_URL) — it is the running instance, not the database.\n Nothing was sent, so nothing was imported. Check the instance is up and reachable from here\n (a VPN, an ingress or a stopped deployment are the usual three), e.g. curl -sS ${e.url}/_voltro/inspect/app`, Ie = (e, t) => `the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. This is a client-side deadline, not a failure on the server: the upload itself finished long before, and the response arrives only when the import does. The instance is very probably STILL IMPORTING and will finish.\n` + (t === "replace" ? " Do NOT simply re-run: --mode replace empties the target first, so a second run started while the first is mid-flight would empty it under the first one. Check the instance's log for the import's completion, and check the target's row counts, before deciding." : ` Re-running the same bundle is safe once the first run has finished (--mode ${t} is idempotent), but check the instance's log first — the work may already be done.`) + "\n To keep a deadline anyway, pass --timeout <seconds>; the default is to wait.", Le = 16777216, Re = async function* (e, t) {
82
82
  let n = [], r = 0, i = 0, a = function* (e) {
83
83
  let t = Buffer.concat(n);
84
84
  n = [], r = 0;
@@ -100,12 +100,12 @@ var we = /* @__PURE__ */ new Set([
100
100
  i += a.byteLength, yield s;
101
101
  }
102
102
  yield* a(!0);
103
- }, Oe = (e) => oe("sha256").update(Buffer.from(e)).digest("hex"), ke = async (e) => {
103
+ }, ze = (e) => pe("sha256").update(Buffer.from(e)).digest("hex"), Be = async (e) => {
104
104
  let t = 0, n = 0, r = {
105
105
  status: 0,
106
106
  body: {}
107
107
  };
108
- for await (let i of De(e.stream, e.chunkBytes)) {
108
+ for await (let i of Re(e.stream, e.chunkBytes)) {
109
109
  if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
110
110
  n++;
111
111
  continue;
@@ -131,13 +131,14 @@ var we = /* @__PURE__ */ new Set([
131
131
  bytes: t,
132
132
  resumedFrom: e.alreadyHeld
133
133
  };
134
- }, F = "voltro-backup-stamp.json", Ae = (e) => ({
134
+ }, R = "voltro-backup-stamp.json", Ve = (e) => ({
135
135
  stampVersion: 1,
136
136
  dialect: e.dialect,
137
137
  schemaFingerprint: e.schemaFingerprint,
138
138
  voltroVersion: e.voltroVersion,
139
- createdAt: e.createdAt
140
- }), je = (e) => {
139
+ createdAt: e.createdAt,
140
+ assets: e.assets ?? null
141
+ }), He = (e) => {
141
142
  let t;
142
143
  try {
143
144
  t = JSON.parse(e);
@@ -148,17 +149,23 @@ var we = /* @__PURE__ */ new Set([
148
149
  let n = t;
149
150
  if (n.stampVersion !== 1 || typeof n.dialect != "string" || typeof n.voltroVersion != "string" || typeof n.createdAt != "string") return null;
150
151
  let r = n.schemaFingerprint;
151
- return r !== null && typeof r != "string" ? null : {
152
+ if (r !== null && typeof r != "string") return null;
153
+ let i = n.assets, a = typeof i == "object" && i && typeof i.count == "number" && typeof i.totalBytes == "number" ? {
154
+ count: i.count,
155
+ totalBytes: i.totalBytes
156
+ } : null;
157
+ return {
152
158
  stampVersion: 1,
153
159
  dialect: n.dialect,
154
160
  schemaFingerprint: r,
155
161
  voltroVersion: n.voltroVersion,
156
- createdAt: n.createdAt
162
+ createdAt: n.createdAt,
163
+ assets: a
157
164
  };
158
- }, Me = (e) => {
165
+ }, Ue = (e) => {
159
166
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
160
167
  if (t === null) return {
161
- warn: `no ${F} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
168
+ warn: `no ${R} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
162
169
  info: []
163
170
  };
164
171
  let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
@@ -169,20 +176,102 @@ var we = /* @__PURE__ */ new Set([
169
176
  refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
170
177
  info: i
171
178
  };
172
- }, I = (e) => {
179
+ }, We = async (e, t) => {
180
+ let n = ee("dimp", `${t.direction}-${t.artifact}-${Date.now()}-${Math.random()}`);
181
+ try {
182
+ return w(x), await e.insert(y, {
183
+ id: n,
184
+ direction: t.direction,
185
+ startedAt: /* @__PURE__ */ new Date(),
186
+ finishedAt: null,
187
+ failure: null,
188
+ mode: `native ${t.dialect}${t.assets ? " + assets" : ""}`,
189
+ via: "cli",
190
+ bundle: t.artifact,
191
+ sourceFingerprint: null,
192
+ tablesWritten: 0,
193
+ rowsWritten: 0,
194
+ staged: null
195
+ }), {
196
+ runId: n,
197
+ recorded: !0
198
+ };
199
+ } catch {
200
+ return {
201
+ runId: n,
202
+ recorded: !1
203
+ };
204
+ }
205
+ }, z = async (e, t, n) => {
206
+ t.recorded && await e.update(y, t.runId, {
207
+ finishedAt: /* @__PURE__ */ new Date(),
208
+ failure: n.failure ?? null,
209
+ ...n.assets === void 0 ? {} : { rowsWritten: n.assets }
210
+ }).then(() => void 0, () => void 0);
211
+ }, Ge = async (e, t) => {
212
+ let n = ee("repl", `restore-${t.artifact}-${Date.now()}-${Math.random()}`), r = {
213
+ id: n,
214
+ startedAt: /* @__PURE__ */ new Date(),
215
+ kind: "restore",
216
+ tables: null,
217
+ capture: t.artifact,
218
+ via: "cli"
219
+ };
220
+ try {
221
+ w(S), await e.insert(b, r);
222
+ } catch {
223
+ return;
224
+ }
225
+ let i = async () => (await e.query({
226
+ table: b,
227
+ predicate: void 0,
228
+ order: [],
229
+ take: void 0,
230
+ skip: void 0,
231
+ projection: void 0
232
+ })).some((e) => String(e.id ?? "") === n);
233
+ return {
234
+ reassert: async () => {
235
+ try {
236
+ if (await i()) return "held";
237
+ } catch {}
238
+ try {
239
+ return w(S), await e.insert(b, r), "rewritten";
240
+ } catch {
241
+ return "lost";
242
+ }
243
+ },
244
+ clear: async () => {
245
+ await e.deleteMany(b, { where: {
246
+ column: "id",
247
+ op: "eq",
248
+ value: n
249
+ } }).then(() => void 0, () => void 0);
250
+ }
251
+ };
252
+ }, Ke = async (e) => {
253
+ let t = await E.runPromiseExit(e);
254
+ if (ie.isSuccess(t)) return;
255
+ let n = T.failureOption(t.cause);
256
+ if (ae.isSome(n)) {
257
+ let e = n.value;
258
+ return String(e?.message ?? e);
259
+ }
260
+ return T.pretty(t.cause);
261
+ }, B = (e) => {
173
262
  try {
174
263
  let t = new URL(e), n = t.pathname.replace(/^\//, "");
175
264
  return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
176
265
  } catch {
177
266
  return e.trim();
178
267
  }
179
- }, Ne = (e) => {
268
+ }, qe = (e) => {
180
269
  let t = e.flagUrl ?? e.envUrl;
181
270
  if (t === void 0 || t.trim() === "") return {
182
271
  ok: !1,
183
272
  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."
184
273
  };
185
- let n = I(t);
274
+ let n = B(t);
186
275
  return e.liveKeys.some((e) => e === n) ? {
187
276
  ok: !1,
188
277
  error: `drill target (${n}) is the LIVE database — a restore drill must target a throwaway DB, never production. It would --clean and overwrite it.`
@@ -190,7 +279,7 @@ var we = /* @__PURE__ */ new Set([
190
279
  ok: !0,
191
280
  url: t
192
281
  };
193
- }, Pe = (e) => {
282
+ }, Je = (e) => {
194
283
  let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
195
284
  return r === 0 ? {
196
285
  pass: !1,
@@ -205,24 +294,24 @@ var we = /* @__PURE__ */ new Set([
205
294
  pass: !0,
206
295
  lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
207
296
  };
208
- }, L = v({ scope: "voltro:data" }), Fe = async () => {
297
+ }, V = v({ scope: "voltro:data" }), Ye = async () => {
209
298
  try {
210
299
  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");
211
300
  return JSON.parse(n(r, "utf8")).version ?? "unknown";
212
301
  } catch {
213
302
  return "unknown";
214
303
  }
215
- }, R = async (e) => {
304
+ }, Xe = async (e) => {
216
305
  try {
217
- return re(await Be(e));
306
+ return le(await tt(e));
218
307
  } catch {
219
308
  return null;
220
309
  }
221
- }, z = (e) => {
310
+ }, H = (e) => {
222
311
  if (e.event !== "done") return;
223
312
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
224
- L.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
225
- }, Ie = async (e) => {
313
+ V.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
314
+ }, Ze = async (e) => {
226
315
  let t = (e) => e?.meta?.databaseId;
227
316
  if (e) {
228
317
  let n = await h({
@@ -242,21 +331,21 @@ var we = /* @__PURE__ */ new Set([
242
331
  };
243
332
  }
244
333
  return null;
245
- }, Le = async (e) => {
246
- let t = e.includes("--allow-live"), n = t ? null : await Ie(B(e, "--api-url")), r = b({
247
- dialect: H(),
334
+ }, Qe = async (e) => {
335
+ let t = e.includes("--allow-live"), n = t ? null : await Ze(U(e, "--api-url")), r = C({
336
+ dialect: G(),
248
337
  ...a()
249
- }), i = x({
250
- dialect: H(),
338
+ }), i = te({
339
+ dialect: G(),
251
340
  ...a()
252
- }), { refuse: o, message: s } = ce({
341
+ }), { refuse: o, message: s } = ge({
253
342
  liveInstance: n?.label ?? null,
254
343
  allowLive: t,
255
- sameTarget: S(r, n?.databaseId),
344
+ sameTarget: ne(r, n?.databaseId),
256
345
  ...i === void 0 ? {} : { targetLabel: `${i.host}:${i.port}/${i.database}` }
257
346
  });
258
- return !o || (L.error(s ?? "refusing: live instance detected"), !1);
259
- }, B = s, Re = [
347
+ return !o || (V.error(s ?? "refusing: live instance detected"), !1);
348
+ }, U = s, $e = [
260
349
  "--api-url",
261
350
  "--bundle-key",
262
351
  "--compression",
@@ -276,50 +365,50 @@ var we = /* @__PURE__ */ new Set([
276
365
  "--timeout",
277
366
  "--tenant",
278
367
  "--token"
279
- ], V = (e) => c(e, Re), H = () => {
368
+ ], W = (e) => c(e, $e), G = () => {
280
369
  let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
281
370
  if (e === "memory") throw new n("voltro data: requires a real dialect, not DB_DIALECT=memory.");
282
371
  return e;
283
- }, ze = async (e, t) => {
372
+ }, et = async (e, t) => {
284
373
  let n = (await l(e)).makeSqlLayer(t);
285
- return w.runPromise(te.SqlClient.pipe(w.flatMap(ie), w.provide(n)));
286
- }, Be = (e) => ze(e, a()), Ve = () => {
374
+ return E.runPromise(se.SqlClient.pipe(E.flatMap(ue), E.provide(n)));
375
+ }, tt = (e) => et(e, a()), nt = () => {
287
376
  let e = a(), t = [];
288
- return e.url && t.push(I(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
289
- }, U = async () => {
377
+ return e.url && t.push(B(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
378
+ }, K = async () => {
290
379
  let e = process.cwd(), t = await r(e);
291
380
  if (!t) throw new n("voltro data: no app.config.ts found in the current directory.");
292
- let a = await i(t, void 0), o = H();
381
+ let a = await i(t, void 0), o = G();
293
382
  return {
294
383
  store: a,
295
- snapshot: await Be(o),
384
+ snapshot: await tt(o),
296
385
  dialect: o
297
386
  };
298
- }, W = (e) => C(e, {
299
- table: ae,
387
+ }, q = (e) => re(e, {
388
+ table: de,
300
389
  chunkSize: 500
301
- }).pipe(ee.map((e) => ({
390
+ }).pipe(oe.map((e) => ({
302
391
  key: String(e.key),
303
392
  contentType: String(e.contentType ?? "application/octet-stream"),
304
393
  size: Number(e.size ?? 0)
305
- }))), G = (e) => !!e && e.endsWith(".vbundle"), K = (e) => B(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, He = (e) => e.includes("--encrypt") || B(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ue = async (e, t) => {
394
+ }))), J = (e) => !!e && e.endsWith(".vbundle"), Y = (e) => U(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, rt = (e) => e.includes("--encrypt") || U(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, it = async (e, t) => {
306
395
  let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
307
396
  await r(i.from(e), n(t));
308
- }, q = (e) => ({ async *[Symbol.asyncIterator]() {
397
+ }, X = (e) => ({ async *[Symbol.asyncIterator]() {
309
398
  let { createReadStream: t } = await import("node:fs");
310
399
  for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
311
- } }), We = async (e, t) => {
400
+ } }), at = async (e, t) => {
312
401
  if (t !== void 0 || e === void 0) return null;
313
402
  try {
314
- let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = G(e) ? (await O(q(i), ["manifest.json"])).get("manifest.json") : await r.readFile(n(i, "manifest.json"));
403
+ let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = J(e) ? (await M(X(i), ["manifest.json"])).get("manifest.json") : await r.readFile(n(i, "manifest.json"));
315
404
  if (a === void 0) return null;
316
405
  let o = (JSON.parse(Buffer.from(a).toString("utf8")).tables ?? []).map((e) => e.name).filter((e) => typeof e == "string");
317
406
  return o.length > 0 ? o : null;
318
407
  } catch {
319
408
  return null;
320
409
  }
321
- }, Ge = (e) => {
322
- let t = B(e, "--max-request-seconds") ?? process.env.VOLTRO_MAX_REQUEST_SECONDS;
410
+ }, ot = (e) => {
411
+ let t = U(e, "--max-request-seconds") ?? process.env.VOLTRO_MAX_REQUEST_SECONDS;
323
412
  if (t === void 0) return { pollMs: 2e3 };
324
413
  let r = Number(t);
325
414
  if (!Number.isFinite(r) || r <= 0) throw new n(`--max-request-seconds expects a positive number of seconds, got '${t}'`);
@@ -328,8 +417,8 @@ var we = /* @__PURE__ */ new Set([
328
417
  perRequest: { timeoutMs: i },
329
418
  pollMs: Math.max(500, Math.min(2e3, Math.floor(i / 2)))
330
419
  };
331
- }, Ke = async (e, t, n) => {
332
- let r = await fetch(`${e.replace(/\/+$/, "")}/_voltro/admin/imports?limit=50`, { headers: { authorization: `Bearer ${t}` } }).catch(() => null);
420
+ }, st = async (e, t, n) => {
421
+ let r = await fetch(`${e.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=50`, { headers: { authorization: `Bearer ${t}` } }).catch(() => null);
333
422
  if (r === null || r.status !== 200) return null;
334
423
  let i = ((await r.json().catch(() => null))?.runs ?? []).find((e) => e.id === n);
335
424
  return i === void 0 ? null : {
@@ -337,7 +426,7 @@ var we = /* @__PURE__ */ new Set([
337
426
  startedAt: new Date(String(i.startedAt)),
338
427
  finishedAt: i.finishedAt === null || i.finishedAt === void 0 ? null : new Date(String(i.finishedAt))
339
428
  };
340
- }, qe = async (e) => {
429
+ }, ct = async (e) => {
341
430
  let t = e.apiUrl.replace(/\/+$/, ""), n = [], r = 0, i;
342
431
  for (;;) {
343
432
  let a = `${t}/_voltro/admin/export/download?runId=${encodeURIComponent(e.runId)}&offset=${r}`, o = new AbortController(), s = e.perRequest === void 0 ? void 0 : setTimeout(() => {
@@ -359,12 +448,12 @@ var we = /* @__PURE__ */ new Set([
359
448
  e.log.info(` downloading — ${r}/${i ?? "?"} bytes`);
360
449
  }
361
450
  return new Uint8Array(Buffer.concat(n));
362
- }, J = "transfers", Je = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Ye = (e, t) => {
363
- let r = B(e, "--tenant"), i = B(e, "--tables"), a = B(e, "--exclude");
451
+ }, Z = "transfers", lt = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), ut = (e, t) => {
452
+ let r = U(e, "--tenant"), i = U(e, "--tables"), a = U(e, "--exclude");
364
453
  if (a !== void 0) {
365
454
  if (i !== void 0) throw new n("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
366
455
  if (r !== void 0) throw new n("voltro data export: --exclude does not apply to --tenant, whose table set is derived from the tenant closure.");
367
- let e = Je(a), o = new Set(t), s = e.filter((e) => !o.has(e));
456
+ let e = lt(a), o = new Set(t), s = e.filter((e) => !o.has(e));
368
457
  if (s.length > 0) throw new n(`voltro data export: --exclude names ${s.length} table(s) that do not exist: ${s.join(", ")}. An exclusion that excludes nothing would leave the run looking like it worked.`);
369
458
  return {
370
459
  kind: "all",
@@ -376,15 +465,15 @@ var we = /* @__PURE__ */ new Set([
376
465
  tenantId: r
377
466
  } : i ? {
378
467
  kind: "tables",
379
- tables: Je(i)
468
+ tables: lt(i)
380
469
  } : { kind: "all" };
381
- }, Xe = async () => {
470
+ }, dt = async () => {
382
471
  let e = await g(process.cwd());
383
- return ue(ne(e));
384
- }, Ze = "voltro/tenant", Qe = async () => {
385
- let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Ze)).map((e) => e.tableName);
472
+ return ye(ce(e));
473
+ }, ft = "voltro/tenant", pt = async () => {
474
+ let e = (await g(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === ft)).map((e) => e.tableName);
386
475
  return e.length > 0 ? e : void 0;
387
- }, $e = (e) => {
476
+ }, mt = (e) => {
388
477
  for (let t of e.tables) if (t.samples.length !== 0) {
389
478
  console.log(`\n ${t.table}`);
390
479
  for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
@@ -397,8 +486,8 @@ var we = /* @__PURE__ */ new Set([
397
486
  console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
398
487
  for (let t of e.unclassified) console.log(` ${t}`);
399
488
  } else console.log("\n ✓ every exported column is classified — safe to run.");
400
- }, et = async (e) => {
401
- let { resolve: t, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = B(e.args, "--chunk-size"), o = a === void 0 ? Ee : Math.round(Number(a) * 1024 * 1024);
489
+ }, ht = async (e) => {
490
+ let { resolve: t, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = U(e.args, "--chunk-size"), o = a === void 0 ? Le : Math.round(Number(a) * 1024 * 1024);
402
491
  if (!Number.isFinite(o) || o <= 0) throw new n(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
403
492
  let s = e.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
404
493
  for (; l <= o;) {
@@ -413,11 +502,11 @@ var we = /* @__PURE__ */ new Set([
413
502
  kind: "small",
414
503
  body: Buffer.concat(c)
415
504
  };
416
- let d = t(process.cwd(), e.bundleDir), f = Oe(G(e.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await N(e.url, Buffer.alloc(0), {
505
+ let d = t(process.cwd(), e.bundleDir), f = ze(J(e.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await I(e.url, Buffer.alloc(0), {
417
506
  ...e.headers,
418
507
  "x-import-upload-id": p,
419
508
  "x-import-probe": "1"
420
- }, X(e.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
509
+ }, $(e.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
421
510
  h > 0 && e.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
422
511
  let g = (async function* () {
423
512
  for (let e of c) yield e;
@@ -426,7 +515,7 @@ var we = /* @__PURE__ */ new Set([
426
515
  if (e.done === !0) return;
427
516
  yield e.value;
428
517
  }
429
- })(), _ = 0, v = await ke({
518
+ })(), _ = 0, v = await Be({
430
519
  stream: g,
431
520
  chunkBytes: o,
432
521
  uploadId: p,
@@ -440,7 +529,7 @@ var we = /* @__PURE__ */ new Set([
440
529
  e.log.info("api import: upload done — the instance is importing now. This request stays open until it finishes, which on a full bundle is minutes, and nothing more is printed until then. It is not a hang; the instance logs the import itself.");
441
530
  },
442
531
  target: { send: async (t, n) => {
443
- let r = await N(e.url, t, n, X(e.args)), i = {};
532
+ let r = await I(e.url, t, n, $(e.args)), i = {};
444
533
  try {
445
534
  i = JSON.parse(r.body.toString("utf8") || "{}");
446
535
  } catch {}
@@ -462,83 +551,83 @@ var we = /* @__PURE__ */ new Set([
462
551
  body: Buffer.from(JSON.stringify(v.body))
463
552
  }
464
553
  };
465
- }, tt = (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.`, Y = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", nt = (e, t, n) => {
554
+ }, gt = (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.`, Q = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", _t = (e, t, n) => {
466
555
  if (t !== "replace" || e.includes("--no-rollback")) return;
467
- let r = B(e, "--rollback-dir");
556
+ let r = U(e, "--rollback-dir");
468
557
  return r === void 0 ? o(n) : _(r);
469
- }, rt = (e, t, n) => {
470
- let r = nt(e, t, n);
558
+ }, vt = (e, t, n) => {
559
+ let r = _t(e, t, n);
471
560
  return {
472
561
  ...r === void 0 ? {} : { rollbackDir: r },
473
562
  recordInterruption: !0,
474
563
  via: "direct"
475
564
  };
476
- }, X = (e) => {
477
- let t = B(e, "--timeout");
565
+ }, $ = (e) => {
566
+ let t = U(e, "--timeout");
478
567
  if (t === void 0) return {};
479
568
  let r = Number(t);
480
569
  if (!Number.isFinite(r) || r <= 0) throw new n(`--timeout expects a positive number of seconds, got '${t}'`);
481
570
  return { timeoutMs: Math.round(r * 1e3) };
482
- }, it = async (e) => {
483
- let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), r = V(e);
484
- if (!r && !(t && n)) return L.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;
571
+ }, yt = async (e) => {
572
+ let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
573
+ if (!r && !(t && n)) return V.error("usage: voltro data export <outDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--profile <name>] [--tenant <id>] [--tables a,b] [--exclude a,b] [--bundle-key <storage-key>] [--assets] [--compression zstd|gzip|none] [--consistency live|snapshot] [--dry-run]"), 1;
485
574
  if (t) {
486
- let t = B(e, "--api-url");
487
- if (!t) return L.error("--target api requires --api-url <url> (the running instance)"), 1;
488
- let i = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
489
- if (!i) return L.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
490
- let a = B(e, "--tables"), o = B(e, "--exclude");
491
- if (o !== void 0 && a !== void 0) return L.error("voltro data export: --exclude and --tables are two ways to say the same thing; use one."), 1;
575
+ let t = U(e, "--api-url");
576
+ if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
577
+ let i = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
578
+ if (!i) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
579
+ let a = U(e, "--tables"), o = U(e, "--exclude");
580
+ if (o !== void 0 && a !== void 0) return V.error("voltro data export: --exclude and --tables are two ways to say the same thing; use one."), 1;
492
581
  let s = {
493
- ...B(e, "--profile") ? { profile: B(e, "--profile") } : {},
494
- ...B(e, "--tenant") ? { tenant: B(e, "--tenant") } : {},
582
+ ...U(e, "--profile") ? { profile: U(e, "--profile") } : {},
583
+ ...U(e, "--tenant") ? { tenant: U(e, "--tenant") } : {},
495
584
  ...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
496
585
  ...o ? { exclude: o.split(",").map((e) => e.trim()).filter(Boolean) } : {},
497
- ...B(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
498
- ...B(e, "--compression") ? { compression: B(e, "--compression") } : {},
586
+ ...U(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
587
+ ...U(e, "--compression") ? { compression: U(e, "--compression") } : {},
499
588
  ...e.includes("--assets") ? { assets: !0 } : {},
500
589
  ...n ? { bundleKey: n } : {}
501
- }, c = Ge(e), l;
590
+ }, c = ot(e), l;
502
591
  try {
503
- let n = await N(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(s), {
592
+ let n = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(s), {
504
593
  "content-type": "application/json",
505
594
  authorization: `Bearer ${i}`
506
- }, c.perRequest ?? X(e));
507
- if (n.status < 200 || n.status >= 300) return L.error(`api export failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
595
+ }, c.perRequest ?? $(e));
596
+ if (n.status < 200 || n.status >= 300) return V.error(`api export failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
508
597
  l = JSON.parse(n.body.toString("utf8"));
509
598
  } catch (e) {
510
- if (e instanceof M) return L.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline. Nothing was written locally; re-run, or raise --timeout.`), 1;
511
- if (e instanceof j) return L.error(`api export: ${P(e)}`), 1;
599
+ if (e instanceof F) return V.error(`api export: the instance did not answer within ${Math.round(e.elapsedMs / 1e3)}s. That is a client-side deadline. Nothing was written locally; re-run, or raise --timeout.`), 1;
600
+ if (e instanceof P) return V.error(`api export: ${L(e)}`), 1;
512
601
  throw e;
513
602
  }
514
- if (e.includes("--detach")) return L.info(`api export: started as ${l.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${J} --target api --api-url ${t}`), 0;
515
- if (l.recorded === !1) return L.warn(`api export: started as ${l.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next one is watchable.`), 0;
603
+ if (e.includes("--detach")) return V.info(`api export: started as ${l.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${Z} --target api --api-url ${t}`), 0;
604
+ if (l.recorded === !1) return V.warn(`api export: started as ${l.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next one is watchable.`), 0;
516
605
  let p = await d({
517
- read: () => Ke(t, i, l.runId),
606
+ read: () => st(t, i, l.runId),
518
607
  onTick: (e) => {
519
- L.info(f(e));
608
+ V.info(f(e));
520
609
  },
521
610
  intervalMs: c.pollMs,
522
611
  ...c.total === void 0 ? {} : { timeoutMs: c.total }
523
612
  }), m = u(p);
524
- if (m !== 0) return L.error(m === 1 ? `api export FAILED (${l.runId}): ${String(p?.failure ?? "no reason recorded")}` : `api export: no outcome visible for ${l.runId}. The run is not cancelled; it is simply not readable from here.`), m;
525
- if (n) return L.info("api export complete (pushed to storage)", {
613
+ if (m !== 0) return V.error(m === 1 ? `api export FAILED (${l.runId}): ${String(p?.failure ?? "no reason recorded")}` : `api export: no outcome visible for ${l.runId}. The run is not cancelled; it is simply not readable from here.`), m;
614
+ if (n) return V.info("api export complete (pushed to storage)", {
526
615
  via: t,
527
616
  runId: l.runId,
528
617
  bundleKey: n,
529
618
  tables: p?.tablesWritten ?? 0,
530
619
  rows: p?.rowsWritten ?? 0
531
620
  }), 0;
532
- let { resolve: h } = await import("node:path"), g = await qe({
621
+ let { resolve: h } = await import("node:path"), g = await ct({
533
622
  apiUrl: t,
534
623
  secret: i,
535
624
  runId: l.runId,
536
- log: L,
625
+ log: V,
537
626
  ...c.perRequest ? { perRequest: c.perRequest } : {}
538
627
  });
539
- return g === null ? 1 : (await xe((async function* () {
628
+ return g === null ? 1 : (await Me((async function* () {
540
629
  yield g;
541
- })(), h(process.cwd(), r)), L.info("api export complete (downloaded from the running instance)", {
630
+ })(), h(process.cwd(), r)), V.info("api export complete (downloaded from the running instance)", {
542
631
  outDir: r,
543
632
  runId: l.runId,
544
633
  tables: p?.tablesWritten ?? 0,
@@ -546,11 +635,13 @@ var we = /* @__PURE__ */ new Set([
546
635
  bytes: g.byteLength
547
636
  }), 0);
548
637
  }
549
- if (!r) return L.error("a <outDir> is required for a direct export"), 1;
550
- let i = B(e, "--profile"), a = i ? await p(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: c } = await U(), l = e.includes("--assets") || a.assets === !0, m = l ? T({}) : void 0, h = B(e, "--compression") ?? a.compression, g = B(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", _ = a.masking, v = a.subset, y = a.scope ?? Ye(e, s.tables.map((e) => e.name)), b = _ ? await Xe() : void 0, x = y.kind === "tenant" ? await Qe() : void 0, S = x ? { tenantTables: x } : {};
638
+ if (!r) return V.error("a <outDir> is required for a direct export"), 1;
639
+ let i = U(e, "--profile"), a = i ? await p(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: c } = await K(), l = e.includes("--assets") || a.assets === !0, m = l ? D() : void 0;
640
+ if (l && !m) return V.error("--assets: this app has no storage provider configured, so there are no blobs to export.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
641
+ let h = U(e, "--compression") ?? a.compression, g = U(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", _ = a.masking, v = a.subset, y = a.scope ?? ut(e, s.tables.map((e) => e.name)), b = _ ? await dt() : void 0, x = y.kind === "tenant" ? await pt() : void 0, S = x ? { tenantTables: x } : {};
551
642
  if (e.includes("--dry-run")) {
552
643
  let e = y.kind === "tables" && y.tables ? [...y.tables] : s.tables.map((e) => e.name);
553
- if (L.info("export DRY RUN — nothing was written", {
644
+ if (V.info("export DRY RUN — nothing was written", {
554
645
  outDir: r,
555
646
  tables: e.length,
556
647
  scope: y.kind,
@@ -558,8 +649,8 @@ var we = /* @__PURE__ */ new Set([
558
649
  compression: h ?? "zstd",
559
650
  assets: l,
560
651
  masking: _ ? "profile policy applies" : "none"
561
- }), L.info(`would export: ${e.join(", ")}`), !_) return 0;
562
- let t = await w.runPromise(pe({
652
+ }), V.info(`would export: ${e.join(", ")}`), !_) return 0;
653
+ let t = await E.runPromise(Se({
563
654
  store: o,
564
655
  snapshot: s,
565
656
  masking: _,
@@ -569,12 +660,12 @@ var we = /* @__PURE__ */ new Set([
569
660
  ...S
570
661
  }
571
662
  }));
572
- return $e(t), +(t.unclassified.length > 0);
663
+ return mt(t), +(t.unclassified.length > 0);
573
664
  }
574
- if (G(r)) {
665
+ if (J(r)) {
575
666
  let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), u = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
576
667
  try {
577
- let t = await w.runPromise(me({
668
+ let t = await E.runPromise(Te({
578
669
  store: o,
579
670
  snapshot: s,
580
671
  dialect: c,
@@ -587,17 +678,17 @@ var we = /* @__PURE__ */ new Set([
587
678
  ...h ? { compression: h } : {},
588
679
  ..._ ? { masking: _ } : {},
589
680
  ...b ? { classification: b } : {},
590
- onProgress: z
591
- })), n = fe({
681
+ onProgress: H
682
+ })), n = xe({
592
683
  dir: u,
593
- ...m ? { blobs: ye(m, () => W(o)) } : {}
594
- }), a = He(e);
684
+ ...m ? { blobs: Ae(m, () => q(o)) } : {}
685
+ }), a = rt(e);
595
686
  if (a) {
596
- let t = K(e);
597
- if (!t) return L.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
598
- n = de(n, t);
687
+ let t = Y(e);
688
+ if (!t) return V.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
689
+ n = be(n, t);
599
690
  }
600
- return await Ue(n, i(process.cwd(), r)), L.info("export complete (single file)", {
691
+ return await it(n, i(process.cwd(), r)), V.info("export complete (single file)", {
601
692
  file: r,
602
693
  encrypted: a,
603
694
  assets: l,
@@ -611,7 +702,7 @@ var we = /* @__PURE__ */ new Set([
611
702
  });
612
703
  }
613
704
  }
614
- let C = await w.runPromise(me({
705
+ let C = await E.runPromise(Te({
615
706
  store: o,
616
707
  snapshot: s,
617
708
  dialect: c,
@@ -624,10 +715,10 @@ var we = /* @__PURE__ */ new Set([
624
715
  ...h ? { compression: h } : {},
625
716
  ..._ ? { masking: _ } : {},
626
717
  ...b ? { classification: b } : {},
627
- ...m ? { assets: _e(m, () => W(o)) } : {},
628
- onProgress: z
718
+ ...m ? { assets: Oe(m, () => q(o)) } : {},
719
+ onProgress: H
629
720
  }));
630
- return L.info("export complete", {
721
+ return V.info("export complete", {
631
722
  outDir: r,
632
723
  tables: C.tables.length,
633
724
  rows: C.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -635,13 +726,13 @@ var we = /* @__PURE__ */ new Set([
635
726
  compression: C.compression,
636
727
  ...C.masking ? { maskedColumns: C.masking.transformed.length } : {}
637
728
  }), 0;
638
- }, Z = () => "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.", at = (e) => {
639
- let t = B(e, "--tables");
729
+ }, bt = () => "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.", xt = (e) => {
730
+ let t = U(e, "--tables");
640
731
  return {
641
732
  ...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
642
733
  ...e.includes("--dry-run") ? { dryRun: !0 } : {}
643
734
  };
644
- }, ot = (e) => ({
735
+ }, St = (e) => ({
645
736
  tables: e.tablesWritten,
646
737
  rows: e.rowsWritten,
647
738
  carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -649,28 +740,28 @@ var we = /* @__PURE__ */ new Set([
649
740
  skippedTables: e.tablesSkipped,
650
741
  skippedRows: e.rowsSkipped
651
742
  } : {}
652
- }), st = (e, t) => {
743
+ }), Ct = (e, t) => {
653
744
  if (e.includes("--dry-run")) {
654
- L.info("import DRY RUN — nothing was written", t), L.info(Z());
745
+ V.info("import DRY RUN — nothing was written", t), V.info(bt());
655
746
  return;
656
747
  }
657
748
  if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
658
- L.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
749
+ V.info("import complete — 0 rows written, every table was already applied by an earlier run of this bundle directory", t);
659
750
  return;
660
751
  }
661
- L.info("import complete", t);
662
- }, ct = Symbol("refused"), Q = (e) => {
663
- let t = se(e) ?? e;
752
+ V.info("import complete", t);
753
+ }, wt = Symbol("refused"), Tt = (e) => {
754
+ let t = he(e) ?? e;
664
755
  if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
665
756
  let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
666
- L.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.`);
757
+ V.error(`import refused ${i} row(s), of which ${a} are the actual failures — the rest could not land because a row they reference did not.`);
667
758
  let o = Array.isArray(t.byTable) ? t.byTable : [];
668
- o.length > 1 && L.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
669
- for (let e of r) L.error(` ${e.table} ${e.id} ${e.reason}`);
670
- n.length < i && L.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
759
+ o.length > 1 && V.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
760
+ for (let e of r) V.error(` ${e.table} ${e.id} ${e.reason}`);
761
+ n.length < i && V.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
671
762
  let s = i - a;
672
- return s > 0 && L.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
673
- }, lt = (e) => {
763
+ return s > 0 && V.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
764
+ }, Et = (e) => {
674
765
  let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
675
766
  try {
676
767
  let e = JSON.parse(t), r = e.error ?? e.message;
@@ -710,22 +801,22 @@ var we = /* @__PURE__ */ new Set([
710
801
  return;
711
802
  }
712
803
  }
713
- }, ut = async (e) => {
714
- let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), r = V(e);
715
- if (!r && !(t && n)) return L.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;
716
- let i = B(e, "--mode") ?? "upsert", o = B(e, "--on-conflict") ?? "skip";
804
+ }, Dt = async (e) => {
805
+ let t = U(e, "--target") === "api", n = U(e, "--bundle-key"), r = W(e);
806
+ if (!r && !(t && n)) return V.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--bundle-key <storage-key>] [--tables a,b] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] [--dry-run] (--force: import despite schema drift AND cross-dialect warnings; --dry-run: report what would move, write nothing)"), 1;
807
+ let i = U(e, "--mode") ?? "upsert", o = U(e, "--on-conflict") ?? "skip";
717
808
  if (t) {
718
- let t = B(e, "--api-url");
719
- if (!t) return L.error("--target api requires --api-url <url> (the running instance)"), 1;
720
- let a = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
721
- if (!a) return L.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
722
- let s = B(e, "--tables"), c = B(e, "--rollback-key");
723
- i === "replace" && c === void 0 && L.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.");
809
+ let t = U(e, "--api-url");
810
+ if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
811
+ let a = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
812
+ if (!a) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
813
+ let s = U(e, "--tables"), c = U(e, "--rollback-key");
814
+ i === "replace" && c === void 0 && V.warn("api replace: no rollback capture — this run empties the target first and nothing was asked to be kept.\n Pass --rollback-key <storage-key> and the INSTANCE stores the target's current rows in its own\n object storage before deleting them, which is the one place that survives the pod going away.");
724
815
  let l = {
725
816
  authorization: `Bearer ${a}`,
726
817
  "x-import-mode": i,
727
818
  "x-import-on-conflict": o,
728
- ...Y(e, i) ? { "x-import-atomic": "1" } : {},
819
+ ...Q(e, i) ? { "x-import-atomic": "1" } : {},
729
820
  ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
730
821
  ...c === void 0 ? {} : { "x-import-rollback-key": c },
731
822
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
@@ -733,66 +824,66 @@ var we = /* @__PURE__ */ new Set([
733
824
  ...s ? { "x-import-tables": s } : {}
734
825
  }, { resolve: p } = await import("node:path"), m;
735
826
  if (n) l["content-type"] = "application/json", m = JSON.stringify({ bundleKey: n });
736
- else if (G(r)) {
737
- let t = q(p(process.cwd(), r)), n = K(e);
738
- n && (t = D(t, n)), l["content-type"] = "application/octet-stream", m = t;
739
- } else await (await import("node:fs/promises")).access(p(process.cwd(), r, "assets", "index.ndjson")).then(() => !0, () => !1) && L.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API."), l["content-type"] = "application/octet-stream", m = fe({ dir: p(process.cwd(), r) });
827
+ else if (J(r)) {
828
+ let t = X(p(process.cwd(), r)), n = Y(e);
829
+ n && (t = j(t, n)), l["content-type"] = "application/octet-stream", m = t;
830
+ } else await (await import("node:fs/promises")).access(p(process.cwd(), r, "assets", "index.ndjson")).then(() => !0, () => !1) && V.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API."), l["content-type"] = "application/octet-stream", m = xe({ dir: p(process.cwd(), r) });
740
831
  if (i === "replace") {
741
- let i = await We(r, n);
832
+ let i = await at(r, n);
742
833
  {
743
- let r = i === null ? { bundleKey: n } : { tables: i }, a = await N(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, JSON.stringify(r), {
834
+ let r = i === null ? { bundleKey: n } : { tables: i }, a = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, JSON.stringify(r), {
744
835
  ...l,
745
836
  "content-type": "application/json",
746
837
  "x-import-preflight": "1"
747
- }, X(e)).catch(() => null), o = a !== null && a.status === 200 ? JSON.parse(a.body.toString("utf8")) : null;
748
- o === null ? L.warn("api replace: the instance did not answer the preflight (an older build does not have it). The run still works; the decision will only be visible in the pod log.") : o.staged === !0 ? L.info(`api replace: the instance WILL stage — ${o.stagedTables ?? 0} table(s) load into copies and the\n target keeps its rows until one short swap at the end. Interrupting the load leaves the target intact.`) : L.warn(`api replace: the instance will NOT stage — it empties the target first and refills it, so an
838
+ }, $(e)).catch(() => null), o = a !== null && a.status === 200 ? JSON.parse(a.body.toString("utf8")) : null;
839
+ o === null ? V.warn("api replace: the instance did not answer the preflight (an older build does not have it). The run still works; the decision will only be visible in the pod log.") : o.staged === !0 ? V.info(`api replace: the instance WILL stage — ${o.stagedTables ?? 0} table(s) load into copies and the\n target keeps its rows until one short swap at the end. Interrupting the load leaves the target intact.`) : V.warn(`api replace: the instance will NOT stage — it empties the target first and refills it, so an
749
840
  interrupted run leaves the target empty.\n Reason: ${o.reason ?? "not stated"}`);
750
841
  }
751
842
  }
752
- i === "replace" && !Y(e, i) && L.warn(tt(!0));
753
- let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g = Ge(e), _;
843
+ i === "replace" && !Q(e, i) && V.warn(gt(!0));
844
+ let h = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, g = ot(e), _;
754
845
  try {
755
- let t = typeof m == "string" ? null : await et({
846
+ let t = typeof m == "string" ? null : await ht({
756
847
  stream: m,
757
848
  url: h,
758
849
  headers: l,
759
850
  bundleDir: r,
760
851
  args: e,
761
- log: L
762
- }), n = t === null ? await N(h, m, l, g.perRequest ?? X(e)) : t.kind === "sent" ? t.res : await N(h, t.body, l, g.perRequest ?? X(e));
763
- if (n.status < 200 || n.status >= 300) return Q(lt(n.body)) || L.error(`api import failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
852
+ log: V
853
+ }), n = t === null ? await I(h, m, l, g.perRequest ?? $(e)) : t.kind === "sent" ? t.res : await I(h, t.body, l, g.perRequest ?? $(e));
854
+ if (n.status < 200 || n.status >= 300) return Tt(Et(n.body)) || V.error(`api import failed: HTTP ${n.status} — ${n.body.toString("utf8")}`), 1;
764
855
  _ = JSON.parse(n.body.toString("utf8"));
765
856
  } catch (e) {
766
- if (e instanceof M) return L.error(`api import: ${Te(e, i)}`), 1;
767
- if (e instanceof j) return L.error(`api import: ${P(e)}`), 1;
857
+ if (e instanceof F) return V.error(`api import: ${Ie(e, i)}`), 1;
858
+ if (e instanceof P) return V.error(`api import: ${L(e)}`), 1;
768
859
  throw e;
769
860
  }
770
- let v = await N(`${t.replace(/\/+$/, "")}/_voltro/admin/import/start`, JSON.stringify(_.bundleKey === void 0 ? { uploadId: _.uploadId } : { bundleKey: _.bundleKey }), {
861
+ let v = await I(`${t.replace(/\/+$/, "")}/_voltro/admin/import/start`, JSON.stringify(_.bundleKey === void 0 ? { uploadId: _.uploadId } : { bundleKey: _.bundleKey }), {
771
862
  ...l,
772
863
  "content-type": "application/json"
773
- }, g.perRequest ?? X(e)).catch((e) => {
774
- if (e instanceof j) return L.error(`api import: ${P(e)}`), null;
864
+ }, g.perRequest ?? $(e)).catch((e) => {
865
+ if (e instanceof P) return V.error(`api import: ${L(e)}`), null;
775
866
  throw e;
776
867
  });
777
868
  if (v === null) return 1;
778
- if (v.status < 200 || v.status >= 300) return Q(lt(v.body)) || L.error(`api import refused: HTTP ${v.status} — ${v.body.toString("utf8")}`), 1;
869
+ if (v.status < 200 || v.status >= 300) return Tt(Et(v.body)) || V.error(`api import refused: HTTP ${v.status} — ${v.body.toString("utf8")}`), 1;
779
870
  let y = JSON.parse(v.body.toString("utf8"));
780
- if (y.alreadyStarted === !0 && L.info(`api import: this bundle is already being imported as ${y.runId} — watching that run instead of starting a second one`), e.includes("--detach")) return L.info(`api import: started as ${y.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${J} --target api --api-url ${t}`), 0;
781
- if (y.recorded === !1) return L.warn(`api import: started as ${y.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next import is watchable.`), 0;
871
+ if (y.alreadyStarted === !0 && V.info(`api import: this bundle is already being imported as ${y.runId} — watching that run instead of starting a second one`), e.includes("--detach")) return V.info(`api import: started as ${y.runId}. NOT waiting for it.\n The outcome is not known yet — read it with: voltro data ${Z} --target api --api-url ${t}`), 0;
872
+ if (y.recorded === !1) return V.warn(`api import: started as ${y.runId}, but this instance could not record the run — so its outcome\n cannot be read back. Run \`voltro db apply\` against it so the next import is watchable.`), 0;
782
873
  let b = await d({
783
- read: () => Ke(t, a, y.runId),
874
+ read: () => st(t, a, y.runId),
784
875
  onTick: (e) => {
785
- L.info(f(e));
876
+ V.info(f(e));
786
877
  },
787
878
  intervalMs: g.pollMs,
788
879
  ...g.total === void 0 ? {} : { timeoutMs: g.total }
789
880
  }), x = u(b);
790
- return x === 2 ? (L.warn(b === null ? `api import: started as ${y.runId}, but its record could not be READ back from this instance.\n That is a channel problem, not a slow import — an older instance without the history route, a
881
+ return x === 2 ? (V.warn(b === null ? `api import: started as ${y.runId}, but its record could not be READ back from this instance.\n That is a channel problem, not a slow import — an older instance without the history route, a
791
882
  proxy dropping the GET, or a target with no history table. The run itself is unaffected and
792
- is NOT cancelled; its outcome is simply not visible from here.` : `api import: still running after the time allowed. The run is NOT cancelled — it is decoupled from\n this client by design. Watch it with: voltro data ${J} --target api --api-url ${t}`), 2) : x === 1 ? (L.error(`api import FAILED (${y.runId}): ${String(b?.failure ?? "no reason recorded")}`), 1) : e.includes("--dry-run") ? (L.info("api import DRY RUN — nothing was written", {
883
+ is NOT cancelled; its outcome is simply not visible from here.` : `api import: still running after the time allowed. The run is NOT cancelled — it is decoupled from\n this client by design. Watch it with: voltro data ${Z} --target api --api-url ${t}`), 2) : x === 1 ? (V.error(`api import FAILED (${y.runId}): ${String(b?.failure ?? "no reason recorded")}`), 1) : e.includes("--dry-run") ? (V.info("api import DRY RUN — nothing was written", {
793
884
  via: t,
794
885
  runId: y.runId
795
- }), L.info(Z()), 0) : (L.info("api import complete (imported by the running instance)", {
886
+ }), V.info(bt()), 0) : (V.info("api import complete (imported by the running instance)", {
796
887
  via: t,
797
888
  runId: y.runId,
798
889
  tables: b?.tablesWritten ?? 0,
@@ -800,74 +891,76 @@ var we = /* @__PURE__ */ new Set([
800
891
  ...b?.staged !== null && b?.staged !== void 0 ? { staged: b.staged } : {}
801
892
  }), 0);
802
893
  }
803
- if (!r) return L.error("a <bundleDir> is required for a direct import"), 1;
804
- if (!await Le(e)) return 1;
805
- i === "replace" && e.includes("--allow-live") && !Y(e, i) && L.warn(tt(!1));
806
- let { store: s, snapshot: c } = await U();
807
- c.tables.some((e) => e.name === y) || L.warn(`this target has no \`${y}\` table, so this import will not be recorded.\n The import itself is unaffected. \`voltro db apply\` creates it.`);
808
- let l = e.includes("--force"), p = (e) => L.warn(e), m = e.includes("--assets") ? T({}) : void 0, h = H() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(a()) : void 0;
809
- if (G(r)) {
894
+ if (!r) return V.error("a <bundleDir> is required for a direct import"), 1;
895
+ if (!await Qe(e)) return 1;
896
+ i === "replace" && e.includes("--allow-live") && !Q(e, i) && V.warn(gt(!1));
897
+ let { store: s, snapshot: c } = await K();
898
+ c.tables.some((e) => e.name === y) || V.warn(`this target has no \`${y}\` table, so this import will not be recorded.\n The import itself is unaffected. \`voltro db apply\` creates it.`);
899
+ let l = e.includes("--force"), p = (e) => V.warn(e), m = e.includes("--assets"), h = m ? D() : void 0;
900
+ if (m && !h) return V.error("--assets: this app has no storage provider configured, so there is nowhere to put the blobs.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
901
+ let g = G() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(a()) : void 0;
902
+ if (J(r)) {
810
903
  let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: a, join: u } = await import("node:path"), d = await n.mkdtemp(u(t.tmpdir(), "voltro-import-"));
811
904
  try {
812
- let t = q(a(process.cwd(), r)), n = K(e);
813
- return n && (t = D(t, n)), await be(t, {
905
+ let t = X(a(process.cwd(), r)), n = Y(e);
906
+ return n && (t = j(t, n)), await je(t, {
814
907
  dir: d,
815
- blobs: ve(m ?? T({}))
816
- }), st(e, {
908
+ blobs: ke(h ?? fe())
909
+ }), Ct(e, {
817
910
  file: r,
818
- ...ot(await w.runPromise(he({
911
+ ...St(await E.runPromise(Ee({
819
912
  store: s,
820
913
  bundleDir: d,
821
914
  mode: i,
822
915
  onConflict: o,
823
- atomic: Y(e, i),
824
- ...rt(e, i, r),
916
+ atomic: Q(e, i),
917
+ ...vt(e, i, r),
825
918
  verify: !e.includes("--no-verify"),
826
- targetDialect: H(),
919
+ targetDialect: G(),
827
920
  allowIncompatible: l,
828
921
  targetSnapshot: c,
829
922
  force: l,
830
923
  onWarn: p,
831
- onProgress: z,
924
+ onProgress: H,
832
925
  assetsAlreadyRestored: !0,
833
- ...at(e),
834
- ...h ? { copyLoader: h } : {}
926
+ ...xt(e),
927
+ ...g ? { copyLoader: g } : {}
835
928
  })))
836
929
  }), 0;
837
930
  } finally {
838
- await h?.close(), await n.rm(d, {
931
+ await g?.close(), await n.rm(d, {
839
932
  recursive: !0,
840
933
  force: !0
841
934
  });
842
935
  }
843
936
  }
844
- let g = await w.runPromise(he({
845
- ...rt(e, i, r),
937
+ let _ = await E.runPromise(Ee({
938
+ ...vt(e, i, r),
846
939
  store: s,
847
940
  bundleDir: r,
848
941
  mode: i,
849
942
  onConflict: o,
850
- atomic: Y(e, i),
943
+ atomic: Q(e, i),
851
944
  verify: !e.includes("--no-verify"),
852
- targetDialect: H(),
945
+ targetDialect: G(),
853
946
  allowIncompatible: l,
854
947
  targetSnapshot: c,
855
948
  force: l,
856
949
  onWarn: p,
857
- onProgress: z,
858
- ...at(e),
859
- ...m ? { assets: ge(m) } : {},
860
- ...h ? { copyLoader: h } : {}
950
+ onProgress: H,
951
+ ...xt(e),
952
+ ...h ? { assets: De(h) } : {},
953
+ ...g ? { copyLoader: g } : {}
861
954
  })).catch((e) => {
862
- if (Q(e)) return ct;
955
+ if (Tt(e)) return wt;
863
956
  throw e;
864
- }).finally(() => h?.close());
865
- return typeof g == "symbol" ? 1 : (st(e, {
957
+ }).finally(() => g?.close());
958
+ return typeof _ == "symbol" ? 1 : (Ct(e, {
866
959
  bundleDir: r,
867
- ...ot(g),
868
- assets: g.manifest.assets.count
960
+ ...St(_),
961
+ assets: _.manifest.assets.count
869
962
  }), 0);
870
- }, dt = () => {
963
+ }, Ot = () => {
871
964
  let e = a();
872
965
  return {
873
966
  ...e.url ? { url: e.url } : {},
@@ -877,75 +970,159 @@ var we = /* @__PURE__ */ new Set([
877
970
  ...e.password ? { password: e.password } : {},
878
971
  ...e.database ? { database: e.database } : {}
879
972
  };
880
- }, ft = async (e) => {
881
- let t = V(e);
882
- if (!t) return L.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
883
- let n = H(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
884
- await r(t, { recursive: !0 });
885
- let o = a(t, E(n));
886
- await w.runPromise(A(le(n, dt(), o)));
887
- let s = Ae({
973
+ }, kt = async (e) => {
974
+ let t = W(e);
975
+ if (!t) return V.error("usage: voltro data backup <outDir> [--assets] (native pg_dump/mariadb-dump/… + consistent artifact)"), 1;
976
+ let n = G(), r = e.includes("--assets"), { mkdir: i, writeFile: a } = await import("node:fs/promises"), { join: o } = await import("node:path"), s = await At(), c = r ? D() : void 0;
977
+ if (r && !c) return V.error("--assets: this app has no storage provider configured, so there are no blobs to capture.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the\n flag to take the rows-only dump."), 1;
978
+ await i(t, { recursive: !0 });
979
+ let l = o(t, A(n)), u = s ? await We(s.store, {
980
+ direction: "backup",
888
981
  dialect: n,
889
- schemaFingerprint: await R(n),
890
- voltroVersion: await Fe(),
891
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
892
- });
893
- return await i(a(t, F), `${JSON.stringify(s, null, 2)}\n`, "utf8"), L.info("native backup complete", {
982
+ artifact: l,
983
+ assets: r
984
+ }) : void 0, d = await Ke(N(_e(n, Ot(), l)));
985
+ if (d !== void 0) return s && u && await z(s.store, u, { failure: d }), V.error(`native backup failed: ${d}`), 1;
986
+ let f = await Xe(n), p = await Ye(), m = (/* @__PURE__ */ new Date()).toISOString(), h = async (e) => {
987
+ let r = Ve({
988
+ dialect: n,
989
+ schemaFingerprint: f,
990
+ voltroVersion: p,
991
+ createdAt: m,
992
+ assets: e
993
+ });
994
+ return await a(o(t, R), `${JSON.stringify(r, null, 2)}\n`, "utf8"), r;
995
+ }, g = 0, _ = 0, v = [];
996
+ if (c && s) {
997
+ let e = o(t, O);
998
+ await i(e, { recursive: !0 });
999
+ let n = o(t, k), r = s.store, a = await E.runPromiseExit(ve({
1000
+ source: Oe(c, () => q(r)),
1001
+ assetsDir: e,
1002
+ progress: {
1003
+ done: () => !1,
1004
+ mark: (e, t) => E.tryPromise({
1005
+ try: async () => {
1006
+ await (await import("node:fs/promises")).appendFile(n, `${JSON.stringify(t)}\n`);
1007
+ },
1008
+ catch: (e) => new me({
1009
+ reason: `asset index append failed: ${String(e)}`,
1010
+ path: n
1011
+ })
1012
+ })
1013
+ }
1014
+ }));
1015
+ if (a._tag === "Failure") {
1016
+ let e = T.pretty(a.cause);
1017
+ return u && await z(s.store, u, { failure: `assets: ${e}` }), await h(null).catch(() => void 0), V.error(`the rows were dumped to ${l}, but the asset capture failed: ${e}\n This directory holds a ROWS-ONLY backup. Re-run with --assets once the storage
1018
+ backend is reachable, or restore without --assets and accept the dangling refs.`), 1;
1019
+ }
1020
+ g = a.value.count, _ = a.value.totalBytes, v = a.value.missing, v.length > 0 && V.warn(`${v.length} of ${v.length + g} blob reference(s) point at objects the storage provider does not have; they are NOT in this backup and no restore can bring them back.
1021
+ e.g. ${v.slice(0, 3).map((e) => `'${e}'`).join(", ")}` + (v.length > 3 ? `, and ${v.length - 3} more` : ""));
1022
+ }
1023
+ let y = await h(r ? {
1024
+ count: g,
1025
+ totalBytes: _
1026
+ } : null);
1027
+ return s && u && await z(s.store, u, { assets: g }), V.info("native backup complete", {
894
1028
  dialect: n,
895
- artifact: o,
896
- stamp: F,
897
- schemaFingerprint: s.schemaFingerprint ?? "<none>",
898
- assets: "NOT included — use `voltro data export --assets` for rows + blobs"
1029
+ artifact: l,
1030
+ stamp: R,
1031
+ schemaFingerprint: y.schemaFingerprint ?? "<none>",
1032
+ assets: r ? `${g} blob(s), ${_} byte(s) under ${O}/` + (v.length > 0 ? ` — ${v.length} reference(s) had no object and are NOT in here` : "") : "NOT included — re-run with --assets, or use `voltro data export --assets`",
1033
+ recorded: u?.recorded === !0 ? "voltro data transfers" : "NOT recorded (this target keeps no transfer history)"
899
1034
  }), 0;
900
- }, pt = async (e, t) => {
901
- let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, E(n)), o = Ne({
902
- flagUrl: B(t, "--drill-url"),
1035
+ }, At = async () => {
1036
+ try {
1037
+ let e = await r(process.cwd());
1038
+ return e ? { store: await i(e, void 0) } : void 0;
1039
+ } catch {
1040
+ return;
1041
+ }
1042
+ }, jt = async (e, t) => {
1043
+ let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, A(n)), o = qe({
1044
+ flagUrl: U(t, "--drill-url"),
903
1045
  envUrl: process.env.DRILL_DB_URL,
904
- liveKeys: Ve()
1046
+ liveKeys: nt()
905
1047
  });
906
- if (!o.ok) return L.error(`restore --drill: ${o.error}`), 1;
907
- let s = await i(r(e, F), "utf8").then(je, () => null);
908
- if (s && s.dialect !== n) return L.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
909
- L.info("restore drill: restoring into throwaway DB", {
1048
+ if (!o.ok) return V.error(`restore --drill: ${o.error}`), 1;
1049
+ let s = await i(r(e, R), "utf8").then(He, () => null);
1050
+ if (s && s.dialect !== n) return V.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
1051
+ V.info("restore drill: restoring into throwaway DB", {
910
1052
  dialect: n,
911
- target: I(o.url)
912
- }), await w.runPromise(A(k(n, { url: o.url }, a)));
1053
+ target: B(o.url)
1054
+ }), await E.runPromise(N(we(n, { url: o.url }, a)));
913
1055
  let c = null, l = 0;
914
1056
  try {
915
- let e = await ze(n, { url: o.url });
916
- c = re(e), l = e.tables.length;
1057
+ let e = await et(n, { url: o.url });
1058
+ c = le(e), l = e.tables.length;
917
1059
  } catch (e) {
918
- return L.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
1060
+ return V.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
919
1061
  }
920
- let u = Pe({
1062
+ let u = Je({
921
1063
  stampFingerprint: s?.schemaFingerprint ?? null,
922
1064
  restoredFingerprint: c,
923
1065
  restoredTableCount: l
924
1066
  });
925
- for (let e of u.lines) u.pass ? L.info(`restore drill: ${e}`) : L.error(`restore drill: ${e}`);
1067
+ for (let e of u.lines) u.pass ? V.info(`restore drill: ${e}`) : V.error(`restore drill: ${e}`);
926
1068
  return +!u.pass;
927
- }, mt = async (e) => {
928
- let t = V(e);
929
- if (!t) return L.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
930
- if (e.includes("--drill")) return pt(t, e);
931
- if (!await Le(e)) return 1;
932
- let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, E(n)), o = Me({
933
- stamp: await i(r(t, F), "utf8").then(je, () => null),
1069
+ }, Mt = async (e) => {
1070
+ let t = W(e);
1071
+ if (!t) return V.error("usage: voltro data restore <backupDir> [--assets] [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mariadb/…)"), 1;
1072
+ if (e.includes("--drill")) return jt(t, e);
1073
+ if (!await Qe(e)) return 1;
1074
+ let n = G(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, A(n)), o = e.includes("--assets"), s = Ue({
1075
+ stamp: await i(r(t, R), "utf8").then(He, () => null),
934
1076
  currentDialect: n,
935
- currentFingerprint: await R(n)
1077
+ currentFingerprint: await Xe(n)
936
1078
  });
937
- for (let e of o.info) L.info(`backup stamp: ${e}`);
938
- if (o.warn && L.warn(o.warn), o.refuse) {
939
- if (!e.includes("--force")) return L.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
940
- L.warn(`restore proceeding despite: ${o.refuse} (--force)`);
1079
+ for (let e of s.info) V.info(`backup stamp: ${e}`);
1080
+ if (s.warn && V.warn(s.warn), s.refuse) {
1081
+ if (!e.includes("--force")) return V.error(`restore refused: ${s.refuse} (pass --force to override)`), 1;
1082
+ V.warn(`restore proceeding despite: ${s.refuse} (--force)`);
941
1083
  }
942
- return await w.runPromise(A(k(n, dt(), a))), L.info("native restore complete", {
1084
+ let c = r(t, k), l = await i(c, "utf8").then(() => !0, () => !1);
1085
+ if (o && !l) return V.error(`--assets: this backup carries no assets — there is no ${k} in ${t}.\n It was taken without --assets, so its rows will reference blobs this restore cannot
1086
+ put back. Restore from a backup taken with --assets, or drop the flag to accept that.`), 1;
1087
+ !o && l && V.warn("this backup CARRIES assets and --assets was not passed: the rows will be restored and the blobs will not. Pass --assets to restore both.");
1088
+ let u = await At(), d = o ? D() : void 0;
1089
+ if (o && !d) return V.error("--assets: this app has no storage provider configured, so there is nowhere to put the blobs.\n Install `storagePlugin(...)` in app.config.ts or set STORAGE_PROVIDER, or drop the flag."), 1;
1090
+ let f = u ? await Ge(u.store, { artifact: a }) : void 0;
1091
+ if (u && f === void 0) return V.error(`refusing to restore: could not write the in-progress marker.
1092
+ It is one row in \`${b}\`, written before the first destructive\n statement so a killed restore refuses the next boot rather than serving a half-loaded
1093
+ database. Run \`voltro db apply\` so the table exists, then retry.`), 1;
1094
+ let p = u ? await We(u.store, {
1095
+ direction: "restore",
943
1096
  dialect: n,
944
- artifact: a
1097
+ artifact: a,
1098
+ assets: o
1099
+ }) : void 0, m = await Ke(N(we(n, Ot(), a))), h = f === void 0 ? "held" : await f.reassert();
1100
+ if (h === "rewritten" ? V.warn(`the restore artefact removed the in-progress marker (it carries \`${b}\`) — it has been written back.\n Backups taken from this version on exclude that table, so this only affects older
1101
+ and hand-made dumps.`) : h === "lost" && V.error(`the restore artefact removed the in-progress marker and it could NOT be written back.\n \`${b}\` is unwritable, so if this restore does not finish,\n nothing will stop the next boot from serving a half-loaded database. Check the
1102
+ result of this command before you start the app.`), m !== void 0) return u && p && await z(u.store, p, { failure: m }), V.error(`native restore failed: ${m}\n` + (h === "lost" ? " This database is now in an unknown state and NOTHING WILL STOP THE NEXT BOOT — see above.\n" : " This database is now in an unknown state and the next boot will REFUSE, by design.\n") + " Re-run the restore to completion, or clear the marker deliberately with\n `voltro data clear-replace-marker --yes` once you have decided the state is correct."), 1;
1103
+ let g = 0;
1104
+ if (d) {
1105
+ let e = await E.runPromiseExit(Ce({
1106
+ sink: De(d),
1107
+ assetsDir: r(t, O),
1108
+ indexPath: c
1109
+ }));
1110
+ if (e._tag === "Failure") {
1111
+ let t = T.pretty(e.cause);
1112
+ return u && p && await z(u.store, p, { failure: `assets: ${t}` }), V.error(`the rows were restored, but the asset restore failed: ${t}\n The in-progress marker is still set and the next boot will refuse. Re-run
1113
+ \`voltro data restore <dir> --assets\` once storage is reachable.`), 1;
1114
+ }
1115
+ g = e.value;
1116
+ }
1117
+ return f && await f.clear(), u && p && await z(u.store, p, { assets: g }), V.info("native restore complete", {
1118
+ dialect: n,
1119
+ artifact: a,
1120
+ assets: o ? `${g} blob(s) restored` : "not restored (no --assets)",
1121
+ recorded: p?.recorded === !0 ? "voltro data transfers" : "NOT recorded (this target keeps no transfer history)"
945
1122
  }), 0;
946
- }, ht = async (e) => {
947
- let t = V(e);
948
- if (!t) return L.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
1123
+ }, Nt = async (e) => {
1124
+ let t = W(e);
1125
+ if (!t) return V.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
949
1126
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
950
1127
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
951
1128
  let n = r(a, "manifest.json"), o = r(a, "README.md");
@@ -954,24 +1131,24 @@ var we = /* @__PURE__ */ new Set([
954
1131
  if (e !== void 0) return console.log(e), 0;
955
1132
  }
956
1133
  let s = await i.readFile(n, "utf8").catch(() => void 0);
957
- return s === void 0 ? (L.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);
1134
+ return s === void 0 ? (V.error(`'${t}' is a directory but holds no manifest.json — is it an unpacked bundle? (a bundle exported with --target api is always unpacked into a directory, even when the path ends in .vbundle)`), 1) : (console.log(s), 0);
958
1135
  }
959
- let o = K(e), s = () => {
960
- let e = q(a);
961
- return o ? D(e, o) : e;
1136
+ let o = Y(e), s = () => {
1137
+ let e = X(a);
1138
+ return o ? j(e, o) : e;
962
1139
  };
963
1140
  if (e.includes("--json")) {
964
- let e = (await O(s(), ["manifest.json"])).get("manifest.json");
965
- return e ? (console.log(e.toString("utf8")), 0) : (L.error("no manifest.json found in the bundle"), 1);
1141
+ let e = (await M(s(), ["manifest.json"])).get("manifest.json");
1142
+ return e ? (console.log(e.toString("utf8")), 0) : (V.error("no manifest.json found in the bundle"), 1);
966
1143
  }
967
- let c = (await O(s(), ["README.md"])).get("README.md");
1144
+ let c = (await M(s(), ["README.md"])).get("README.md");
968
1145
  if (c) return console.log(c.toString("utf8")), 0;
969
- let l = (await O(s(), ["manifest.json"])).get("manifest.json");
970
- return l ? (console.log(l.toString("utf8")), 0) : (L.error("bundle has no README.md or manifest.json"), 1);
971
- }, gt = async (e) => {
972
- let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-DNJ7MKph.js"), { store: r } = await U(), i = await n(r);
973
- if (i === void 0) return L.info("no interrupted replace is recorded — nothing to clear"), 0;
974
- if (!e.includes("--yes")) return L.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
1146
+ let l = (await M(s(), ["manifest.json"])).get("manifest.json");
1147
+ return l ? (console.log(l.toString("utf8")), 0) : (V.error("bundle has no README.md or manifest.json"), 1);
1148
+ }, Pt = async (e) => {
1149
+ let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-CvmiAM9K.js"), { store: r } = await K(), i = await n(r);
1150
+ if (i === void 0) return V.info("no interrupted replace is recorded — nothing to clear"), 0;
1151
+ if (!e.includes("--yes")) return V.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
975
1152
  let a = await r.query({
976
1153
  table: t,
977
1154
  predicate: void 0,
@@ -981,30 +1158,30 @@ var we = /* @__PURE__ */ new Set([
981
1158
  projection: void 0
982
1159
  });
983
1160
  for (let e of a) await r.delete(t, String(e.id));
984
- return L.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
985
- }, _t = async (e) => {
986
- let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await U(), a = H();
987
- if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return L.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
1161
+ return V.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
1162
+ }, Ft = async (e) => {
1163
+ let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await K(), a = G();
1164
+ if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return V.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
988
1165
  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));
989
- return s.length === 0 ? (L.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))), L.info(`dropped ${s.length} staging table(s)`), 0) : (L.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);
990
- }, vt = async (e) => {
991
- let { describeTransferRun: t, DATA_TRANSFERS_TABLE: n } = await import("@voltro/database"), r = Number(B(e, "--limit") ?? "20"), i = Number.isFinite(r) ? Math.max(1, Math.min(200, Math.trunc(r))) : 20;
992
- if (B(e, "--target") === "api") {
993
- let t = B(e, "--api-url");
994
- if (!t) return L.error("--target api requires --api-url <url> (the running instance)"), 1;
995
- let n = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
996
- if (!n) return L.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET)"), 1;
997
- let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/imports?limit=${i}`, { headers: { authorization: `Bearer ${n}` } }), a = await r.text();
998
- if (r.status !== 200) return L.error(`api imports: ${r.status} ${a}`), 1;
1166
+ return s.length === 0 ? (V.info("no staging tables are present — nothing to clear"), 0) : e.includes("--yes") ? (await r(i, s.map((e) => n(e.slice(t.length), a))), V.info(`dropped ${s.length} staging table(s)`), 0) : (V.error(`${s.length} staging table(s) from an earlier \`--mode replace\`:\n` + s.map((e) => ` ${e}`).join("\n") + "\n\n Each holds a copy of what a run was loading. Nothing else removes them.\n Make sure no import is running right now — this cannot tell a leftover from one in flight.\n Re-run with --yes to drop them."), 1);
1167
+ }, It = async (e) => {
1168
+ let { describeTransferRun: t, DATA_TRANSFERS_TABLE: n } = await import("@voltro/database"), r = Number(U(e, "--limit") ?? "20"), i = Number.isFinite(r) ? Math.max(1, Math.min(200, Math.trunc(r))) : 20;
1169
+ if (U(e, "--target") === "api") {
1170
+ let t = U(e, "--api-url");
1171
+ if (!t) return V.error("--target api requires --api-url <url> (the running instance)"), 1;
1172
+ let n = U(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
1173
+ if (!n) return V.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET)"), 1;
1174
+ let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/transfers?limit=${i}`, { headers: { authorization: `Bearer ${n}` } }), a = await r.text();
1175
+ if (r.status !== 200) return V.error(`api transfers: ${r.status} ${a}`), 1;
999
1176
  let o = JSON.parse(a);
1000
1177
  if (e.includes("--json")) return console.log(a), 0;
1001
1178
  let s = o.runs ?? [];
1002
- if (s.length === 0) return L.info("no imports recorded on this instance"), 0;
1003
- (o.running ?? 0) > 0 && L.info(`${o.running} import(s) IN FLIGHT — re-run this to watch the counters move`);
1004
- for (let e of s) L.info(` ${e.line ?? ""}`);
1179
+ if (s.length === 0) return V.info("no data transfers recorded on this instance"), 0;
1180
+ (o.running ?? 0) > 0 && V.info(`${o.running} transfer(s) IN FLIGHT — re-run this to watch the counters move`);
1181
+ for (let e of s) V.info(` ${e.line ?? ""}`);
1005
1182
  return 0;
1006
1183
  }
1007
- let { store: a } = await U(), o;
1184
+ let { store: a } = await K(), o;
1008
1185
  try {
1009
1186
  o = await a.query({
1010
1187
  table: n,
@@ -1018,27 +1195,27 @@ var we = /* @__PURE__ */ new Set([
1018
1195
  take: i
1019
1196
  });
1020
1197
  } catch {
1021
- return L.error(`this target has no \`${n}\` table, so no import has been recorded here.\n Run \`voltro db apply\`; imports themselves are unaffected by its absence.`), 1;
1198
+ return V.error(`this target has no \`${n}\` table, so no transfer has been recorded here.\n Run \`voltro db apply\`; the transfers themselves are unaffected by its absence.`), 1;
1022
1199
  }
1023
- if (o.length === 0) return L.info("no imports recorded against this target"), 0;
1200
+ if (o.length === 0) return V.info("no data transfers recorded against this target"), 0;
1024
1201
  for (let e of o) {
1025
1202
  let n = e;
1026
- L.info(` ${t({
1203
+ V.info(` ${t({
1027
1204
  ...e,
1028
1205
  startedAt: new Date(n.startedAt),
1029
1206
  finishedAt: n.finishedAt === null ? null : new Date(n.finishedAt)
1030
1207
  })}`);
1031
1208
  }
1032
1209
  return 0;
1033
- }, yt = async (e) => {
1034
- let t = V(e), n = V(e.filter((e) => e !== t));
1035
- if (!t || !n) return L.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
1036
- let { resolve: r } = await import("node:path"), i = q(r(process.cwd(), t)), a = K(e);
1037
- return a && (i = D(i, a)), await xe(i, r(process.cwd(), n)), L.info("unpack complete", {
1210
+ }, Lt = async (e) => {
1211
+ let t = W(e), n = W(e.filter((e) => e !== t));
1212
+ if (!t || !n) return V.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
1213
+ let { resolve: r } = await import("node:path"), i = X(r(process.cwd(), t)), a = Y(e);
1214
+ return a && (i = j(i, a)), await Me(i, r(process.cwd(), n)), V.info("unpack complete", {
1038
1215
  file: t,
1039
1216
  dir: n
1040
1217
  }), 0;
1041
- }, $ = {
1218
+ }, Rt = {
1042
1219
  export: {
1043
1220
  common: [
1044
1221
  "--target",
@@ -1111,46 +1288,47 @@ var we = /* @__PURE__ */ new Set([
1111
1288
  backup: { common: ["--json", "--assets"] },
1112
1289
  restore: { common: [
1113
1290
  "--json",
1291
+ "--assets",
1114
1292
  "--allow-live",
1115
1293
  "--force",
1116
1294
  "--drill",
1117
1295
  "--drill-url",
1118
1296
  "--api-url"
1119
1297
  ] }
1120
- }, bt = (e) => e.split("=")[0], xt = (e, t) => {
1121
- let n = $[e];
1298
+ }, zt = (e) => e.split("=")[0], Bt = (e, t) => {
1299
+ let n = Rt[e];
1122
1300
  if (!n) return;
1123
- let r = B(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 ?? []);
1301
+ let r = U(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
1124
1302
  for (let n of t) {
1125
1303
  if (!n.startsWith("--")) continue;
1126
- let t = bt(n);
1304
+ let t = zt(n);
1127
1305
  if (i.has(t)) continue;
1128
1306
  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.`;
1129
- let o = Object.entries($).filter(([n, r]) => n !== e && [
1307
+ let o = Object.entries(Rt).filter(([n, r]) => n !== e && [
1130
1308
  ...r.common,
1131
1309
  ...r.directOnly ?? [],
1132
1310
  ...r.apiOnly ?? []
1133
1311
  ].includes(t)).map(([e]) => e);
1134
1312
  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.`;
1135
1313
  }
1136
- }, St = {
1137
- export: it,
1138
- import: ut,
1139
- transfers: vt,
1140
- inspect: ht,
1141
- unpack: yt,
1142
- backup: ft,
1143
- restore: mt,
1144
- "clear-replace-marker": gt,
1145
- "clear-staging": _t
1146
- }, Ct = async (n) => {
1314
+ }, Vt = {
1315
+ export: yt,
1316
+ import: Dt,
1317
+ transfers: It,
1318
+ inspect: Nt,
1319
+ unpack: Lt,
1320
+ backup: kt,
1321
+ restore: Mt,
1322
+ "clear-replace-marker": Pt,
1323
+ "clear-staging": Ft
1324
+ }, Ht = async (n) => {
1147
1325
  let [r, ...i] = n;
1148
- if (r !== void 0 && $[r] !== void 0) {
1149
- let e = xt(r, i);
1150
- if (e) return L.error(e), 2;
1326
+ if (r !== void 0 && Rt[r] !== void 0) {
1327
+ let e = Bt(r, i);
1328
+ if (e) return V.error(e), 2;
1151
1329
  }
1152
- let a = r === void 0 ? void 0 : St[r];
1153
- return a === void 0 ? (L.error(`usage: voltro data ${e(t)} … (logical: export/import; native: backup/restore; \`imports\` reads the history, incl. a run in flight)`), r ? 2 : 1) : a(i);
1330
+ let a = r === void 0 ? void 0 : Vt[r];
1331
+ return a === void 0 ? (V.error(`usage: voltro data ${e(t)} … (logical: export/import; native: backup/restore; \`imports\` reads the history, incl. a run in flight)`), r ? 2 : 1) : a(i);
1154
1332
  };
1155
1333
  //#endregion
1156
- export { Ct as runDataCommand };
1334
+ export { Ht as runDataCommand };