@voltro/cli 0.44.1 → 0.45.0

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