@voltro/cli 0.44.0 → 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 +120 -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-fsnezHLH.js → commands-CEkVoFM5.js} +19 -19
  14. package/dist/{dashboardCommand-DHDmlo9A.js → dashboardCommand-0ahyp-zA.js} +1 -1
  15. package/dist/{dataCommand-sfpGV694.js → dataCommand-D4IlZcih.js} +277 -210
  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 +63 -3
  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 +83 -14
  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,102 +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.`);
556
- for (let e of r) F.error(` ${e.table} ${e.id} ${e.reason}`);
557
- let o = i - a;
558
- o > 0 && F.error(` … and ${o} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`);
559
- }, Ue = async (e) => {
560
- let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
561
- 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;
562
- let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
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.`);
578
+ let o = Array.isArray(t.byTable) ? t.byTable : [];
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)`);
582
+ let s = i - a;
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";
563
628
  if (t) {
564
- let t = z(e, "--api-url");
565
- if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
566
- let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
567
- if (!r) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
568
- 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 = {
569
634
  authorization: `Bearer ${r}`,
570
635
  "x-import-mode": a,
571
636
  "x-import-on-conflict": o,
572
- ...Z(e, a) ? { "x-import-atomic": "1" } : {},
637
+ ...X(e, a) ? { "x-import-atomic": "1" } : {},
573
638
  ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
574
639
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
575
640
  ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
576
641
  ...s ? { "x-import-tables": s } : {}
577
642
  }, { resolve: l } = await import("node:path"), u;
578
643
  if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
579
- else if (K(i)) {
580
- 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);
581
646
  n && (t = w(t, n)), c["content-type"] = "application/octet-stream", u = t;
582
- } 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) });
583
- 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));
584
649
  let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
585
650
  try {
586
- let t = typeof u == "string" ? null : await Ie({
651
+ let t = typeof u == "string" ? null : await Ve({
587
652
  stream: u,
588
653
  url: d,
589
654
  headers: c,
590
655
  bundleDir: i,
591
656
  args: e,
592
- log: F
657
+ log: I
593
658
  });
594
- (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));
595
660
  } catch (e) {
596
- 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;
597
663
  throw e;
598
664
  }
599
- 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;
600
666
  let p = JSON.parse(f.body.toString("utf8"));
601
- 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", {
602
668
  via: t,
603
669
  ...p
604
- }), 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)", {
605
671
  via: t,
606
672
  ...p
607
673
  }), 0);
608
674
  }
609
- if (!i) return F.error("a <bundleDir> is required for a direct import"), 1;
610
- if (!await R(e)) return 1;
611
- a === "replace" && e.includes("--allow-live") && !Z(e, a) && F.warn(X(!1));
612
- 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;
613
- 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)) {
614
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-"));
615
681
  try {
616
- let t = J(r(process.cwd(), i)), n = q(e);
617
- 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, {
618
684
  dir: h,
619
- blobs: de(d ?? S({}))
620
- }), Ve(e, {
685
+ blobs: fe(d ?? S({}))
686
+ }), Ke(e, {
621
687
  file: i,
622
- ...Be(await f.runPromise(ce({
688
+ ...Ge(await f.runPromise(le({
623
689
  store: s,
624
690
  bundleDir: h,
625
691
  mode: a,
626
692
  onConflict: o,
627
- atomic: Z(e, a),
693
+ atomic: X(e, a),
628
694
  verify: !e.includes("--no-verify"),
629
- targetDialect: V(),
695
+ targetDialect: B(),
630
696
  allowIncompatible: l,
631
697
  targetSnapshot: c,
632
698
  force: l,
633
699
  onWarn: u,
634
700
  onProgress: L,
635
701
  assetsAlreadyRestored: !0,
636
- ...ze(e),
702
+ ...We(e),
637
703
  ...p ? { copyLoader: p } : {}
638
704
  })))
639
705
  }), 0;
@@ -644,31 +710,32 @@ var A = class extends Error {
644
710
  });
645
711
  }
646
712
  }
647
- let m = await f.runPromise(ce({
713
+ let m = await f.runPromise(le({
648
714
  store: s,
649
715
  bundleDir: i,
650
716
  mode: a,
651
717
  onConflict: o,
652
- atomic: Z(e, a),
718
+ atomic: X(e, a),
653
719
  verify: !e.includes("--no-verify"),
654
- targetDialect: V(),
720
+ targetDialect: B(),
655
721
  allowIncompatible: l,
656
722
  targetSnapshot: c,
657
723
  force: l,
658
724
  onWarn: u,
659
725
  onProgress: L,
660
- ...ze(e),
661
- ...d ? { assets: le(d) } : {},
726
+ ...We(e),
727
+ ...d ? { assets: ue(d) } : {},
662
728
  ...p ? { copyLoader: p } : {}
663
729
  })).catch((e) => {
664
- throw He(e), e;
730
+ if (Q(e)) return qe;
731
+ throw e;
665
732
  }).finally(() => p?.close());
666
- return Ve(e, {
733
+ return typeof m == "symbol" ? 1 : (Ke(e, {
667
734
  bundleDir: i,
668
- ...Be(m),
735
+ ...Ge(m),
669
736
  assets: m.manifest.assets.count
670
- }), 0;
671
- }, We = () => {
737
+ }), 0);
738
+ }, Xe = () => {
672
739
  let e = r();
673
740
  return {
674
741
  ...e.url ? { url: e.url } : {},
@@ -678,75 +745,75 @@ var A = class extends Error {
678
745
  ...e.password ? { password: e.password } : {},
679
746
  ...e.database ? { database: e.database } : {}
680
747
  };
681
- }, Ge = async (e) => {
682
- let t = B(e);
683
- if (!t) return F.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
684
- 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");
685
752
  await r(t, { recursive: !0 });
686
753
  let o = a(t, C(n));
687
- await f.runPromise(O(re(n, We(), o)));
688
- let s = xe({
754
+ await f.runPromise(O(ie(n, Xe(), o)));
755
+ let s = we({
689
756
  dialect: n,
690
- schemaFingerprint: await I(n),
691
- voltroVersion: await Te(),
757
+ schemaFingerprint: await ke(n),
758
+ voltroVersion: await Oe(),
692
759
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
693
760
  });
694
- 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", {
695
762
  dialect: n,
696
763
  artifact: o,
697
- stamp: M,
764
+ stamp: N,
698
765
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
699
766
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
700
767
  }), 0;
701
- }, Ke = async (e, t) => {
702
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Ce({
703
- 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"),
704
771
  envUrl: process.env.DRILL_DB_URL,
705
- liveKeys: Oe()
772
+ liveKeys: Ne()
706
773
  });
707
- if (!o.ok) return F.error(`restore --drill: ${o.error}`), 1;
708
- let s = await i(r(e, M), "utf8").then(N, () => null);
709
- 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;
710
- 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", {
711
778
  dialect: n,
712
- target: P(o.url)
779
+ target: F(o.url)
713
780
  }), await f.runPromise(O(D(n, { url: o.url }, a)));
714
781
  let c = null, l = 0;
715
782
  try {
716
- let e = await H(n, { url: o.url });
783
+ let e = await V(n, { url: o.url });
717
784
  c = g(e), l = e.tables.length;
718
785
  } catch (e) {
719
- 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;
720
787
  }
721
- let u = we({
788
+ let u = De({
722
789
  stampFingerprint: s?.schemaFingerprint ?? null,
723
790
  restoredFingerprint: c,
724
791
  restoredTableCount: l
725
792
  });
726
- 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}`);
727
794
  return +!u.pass;
728
- }, qe = async (e) => {
729
- let t = B(e);
730
- 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;
731
- if (e.includes("--drill")) return Ke(t, e);
732
- if (!await R(e)) return 1;
733
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Se({
734
- 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),
735
802
  currentDialect: n,
736
- currentFingerprint: await I(n)
803
+ currentFingerprint: await ke(n)
737
804
  });
738
- for (let e of o.info) F.info(`backup stamp: ${e}`);
739
- if (o.warn && F.warn(o.warn), o.refuse) {
740
- if (!e.includes("--force")) return F.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
741
- 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)`);
742
809
  }
743
- 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", {
744
811
  dialect: n,
745
812
  artifact: a
746
813
  }), 0;
747
- }, Je = async (e) => {
748
- let t = B(e);
749
- 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;
750
817
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
751
818
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
752
819
  let n = r(a, "manifest.json"), o = r(a, "README.md");
@@ -755,25 +822,25 @@ var A = class extends Error {
755
822
  if (e !== void 0) return console.log(e), 0;
756
823
  }
757
824
  let s = await i.readFile(n, "utf8").catch(() => void 0);
758
- 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);
759
826
  }
760
- let o = q(e), s = () => {
761
- let e = J(a);
827
+ let o = K(e), s = () => {
828
+ let e = q(a);
762
829
  return o ? w(e, o) : e;
763
830
  };
764
831
  if (e.includes("--json")) {
765
832
  let e = (await E(s(), ["manifest.json"])).get("manifest.json");
766
- 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);
767
834
  }
768
835
  let c = (await E(s(), ["README.md"])).get("README.md");
769
836
  if (c) return console.log(c.toString("utf8")), 0;
770
837
  let l = (await E(s(), ["manifest.json"])).get("manifest.json");
771
- return l ? (console.log(l.toString("utf8")), 0) : (F.error("bundle has no README.md or manifest.json"), 1);
772
- }, Ye = async (e) => {
773
- let t = B(e), n = B(e.filter((e) => e !== t));
774
- if (!t || !n) return F.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
775
- let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
776
- 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", {
777
844
  file: t,
778
845
  dir: n
779
846
  }), 0;
@@ -839,13 +906,13 @@ var A = class extends Error {
839
906
  "--drill-url",
840
907
  "--api-url"
841
908
  ] }
842
- }, Xe = (e) => e.split("=")[0], Ze = (e, t) => {
909
+ }, nt = (e) => e.split("=")[0], rt = (e, t) => {
843
910
  let n = $[e];
844
911
  if (!n) return;
845
- 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 ?? []);
846
913
  for (let n of t) {
847
914
  if (!n.startsWith("--")) continue;
848
- let t = Xe(n);
915
+ let t = nt(n);
849
916
  if (i.has(t)) continue;
850
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.`;
851
918
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -855,13 +922,13 @@ var A = class extends Error {
855
922
  ].includes(t)).map(([e]) => e);
856
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.`;
857
924
  }
858
- }, Qe = async (e) => {
925
+ }, it = async (e) => {
859
926
  let [t, ...n] = e;
860
927
  if (t !== void 0 && $[t] !== void 0) {
861
- let e = Ze(t, n);
862
- if (e) return F.error(e), 2;
928
+ let e = rt(t, n);
929
+ if (e) return I.error(e), 2;
863
930
  }
864
- 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);
865
932
  };
866
933
  //#endregion
867
- export { Qe as runDataCommand };
934
+ export { it as runDataCommand };