@voltro/cli 0.43.2 → 0.44.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 (87) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/{apiBuild-BWuw8ZXS.js → apiBuild-DvWreIQP.js} +1 -1
  3. package/dist/apiBuild-XaW40W8x.js +2 -0
  4. package/dist/bin.js +1 -1
  5. package/dist/{build-CGBs4ZOv.js → build-Ckc-FA7d.js} +6 -6
  6. package/dist/{checkCommand-lTPSN3EQ.js → checkCommand-AlbawIal.js} +1 -1
  7. package/dist/{checkCommand-BFtFBsqL.js → checkCommand-BNfEs4KG.js} +2 -2
  8. package/dist/{codegenCommand-BJeyFlip.js → codegenCommand-iClXoRCW.js} +1 -1
  9. package/dist/{codemodRunner-Cnnz4-I7.js → codemodRunner-BKTnaGOz.js} +22 -0
  10. package/dist/{commands-pEDOcOo2.js → commands-fsnezHLH.js} +18 -18
  11. package/dist/{dashboardCommand-Bt2hHMro.js → dashboardCommand-DHDmlo9A.js} +1 -1
  12. package/dist/{dataCommand-DD2bRHCu.js → dataCommand-sfpGV694.js} +254 -235
  13. package/dist/{dev-DMx-MF2s.js → dev-BZO5aLdr.js} +1 -1
  14. package/dist/{dev-wumUfDm1.js → dev-D-JZ6CGQ.js} +8 -4
  15. package/dist/doctorCommand-BeLOwq7S.js +2 -0
  16. package/dist/{doctorCommand-RFKpnLih.js → doctorCommand-BlUyESpD.js} +9 -9
  17. package/dist/{dormancyCommand-EFDQWiUw.js → dormancyCommand-lQxdrU8C.js} +1 -1
  18. package/dist/{embeddingsCommand-OeYp3qSq.js → embeddingsCommand-ByBTju6X.js} +1 -1
  19. package/dist/{envCommand-CKgBZK7x.js → envCommand-CpCbYAgR.js} +1 -1
  20. package/dist/{evolveCommand-ClQlAH0j.js → evolveCommand-CBV3-TtK.js} +2 -2
  21. package/dist/index.js +1 -1
  22. package/dist/{infoCommand-Bs_8RU22.js → infoCommand-DJoA9cKD.js} +1 -1
  23. package/dist/{runtimeTrace-CgpAD9wL.js → runtimeTrace-DJ9tPhrc.js} +1 -1
  24. package/dist/{sdkgen-Ca2zj4cd.js → sdkgen-7dr8-Onw.js} +1 -1
  25. package/dist/{serveCommand-CgK7Kj6j.js → serveCommand-BlnAlNQg.js} +632 -621
  26. package/dist/serveCommand-Dg_E1ev4.js +2 -0
  27. package/dist/serveEntry.js +1 -1
  28. package/dist/{updateCommand-CrI9B4d7.js → updateCommand-BvRidHp6.js} +1 -1
  29. package/dist/updateCommand-DcKVmLVN.js +2 -0
  30. package/dist/{webhooksCommand-BVNtNsI0.js → webhooksCommand-BfO_JF2b.js} +1 -1
  31. package/package.json +23 -17
  32. package/templates/AGENTS.md +1 -1
  33. package/templates/agent-docs/_index.md +1 -1
  34. package/templates/agent-docs/cli.md +20 -4
  35. package/templates/agent-docs/plugins/versioning.md +2 -0
  36. package/templates/agent-docs/whats-new.md +18 -15
  37. package/templates/apps/api-ai/package.json +7 -7
  38. package/templates/apps/api-auth/package.json +8 -8
  39. package/templates/apps/api-backend/package.json +7 -7
  40. package/templates/apps/api-backend-deactivation/package.json +7 -7
  41. package/templates/apps/api-backend-mail/package.json +8 -8
  42. package/templates/apps/api-backend-mariadb/package.json +9 -9
  43. package/templates/apps/api-backend-sqlite/package.json +8 -8
  44. package/templates/apps/api-backend-storage/package.json +8 -8
  45. package/templates/apps/api-cms/package.json +10 -10
  46. package/templates/apps/api-collab/package.json +8 -8
  47. package/templates/apps/api-data-advanced/package.json +8 -8
  48. package/templates/apps/api-durable/package.json +8 -8
  49. package/templates/apps/api-feature-flags/package.json +9 -9
  50. package/templates/apps/api-governance/package.json +8 -8
  51. package/templates/apps/api-kv/package.json +8 -8
  52. package/templates/apps/api-moderation/package.json +8 -8
  53. package/templates/apps/api-observability/package.json +8 -8
  54. package/templates/apps/api-ratelimit/package.json +8 -8
  55. package/templates/apps/api-rbac/package.json +8 -8
  56. package/templates/apps/api-rest/package.json +7 -7
  57. package/templates/apps/api-saas/package.json +11 -11
  58. package/templates/apps/api-saas-starter/package.json +10 -10
  59. package/templates/apps/api-search/package.json +8 -8
  60. package/templates/apps/api-status/package.json +8 -8
  61. package/templates/apps/api-versioning/package.json +8 -8
  62. package/templates/apps/api-webhooks/package.json +9 -9
  63. package/templates/apps/changelog/package.json +6 -6
  64. package/templates/apps/edge-functions/package.json +2 -2
  65. package/templates/apps/frontend-admin/package.json +8 -8
  66. package/templates/apps/frontend-app/package.json +9 -9
  67. package/templates/apps/frontend-auth/package.json +8 -8
  68. package/templates/apps/frontend-blank/package.json +7 -7
  69. package/templates/apps/frontend-cms/package.json +9 -9
  70. package/templates/apps/frontend-collab/package.json +10 -10
  71. package/templates/apps/frontend-contact/package.json +7 -7
  72. package/templates/apps/frontend-dashboard/package.json +7 -7
  73. package/templates/apps/frontend-docs/package.json +7 -7
  74. package/templates/apps/frontend-i18n/package.json +6 -6
  75. package/templates/apps/frontend-landing/package.json +7 -7
  76. package/templates/apps/frontend-portal/package.json +8 -8
  77. package/templates/apps/frontend-saas/package.json +8 -8
  78. package/templates/apps/frontend-spa/package.json +7 -7
  79. package/templates/apps/frontend-ssr/package.json +7 -7
  80. package/templates/apps/frontend-ssr-api/package.json +8 -8
  81. package/templates/apps/frontend-static-blog/package.json +6 -6
  82. package/templates/apps/frontend-status/package.json +8 -8
  83. package/templates/apps/mobile-app/package.json +4 -4
  84. package/dist/apiBuild-Cw2-TEXW.js +0 -2
  85. package/dist/doctorCommand-FOXTTaGp.js +0 -2
  86. package/dist/serveCommand-DUm2-ikx.js +0 -2
  87. package/dist/updateCommand-BvkKlV7D.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-wumUfDm1.js";
2
+ import { c as t, i as n, o as r } from "./dev-D-JZ6CGQ.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,18 @@ 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 O, runImport as se, runNativeStep as k, storageAssetSink as ce, storageAssetSource as le, storageBlobSink as ue, storageBlobSource as de, unpackBundle as fe, unpackBundleToDir as A } from "@voltro/data-transfer";
16
- import { request as pe } from "node:http";
17
- import { request as me } from "node:https";
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";
18
18
  //#region src/longRequest.ts
19
- var j = class extends Error {
19
+ var A = class extends Error {
20
20
  elapsedMs;
21
21
  url;
22
22
  constructor(e, t) {
23
23
  super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
24
24
  }
25
- }, M = async (e, t, n, r) => {
26
- let i = new URL(e), a = i.protocol === "https:" ? me : pe, o = Date.now();
25
+ }, j = async (e, t, n, r) => {
26
+ let i = new URL(e), a = i.protocol === "https:" ? he : me, o = Date.now();
27
27
  return await new Promise((s, c) => {
28
28
  let l = a({
29
29
  protocol: i.protocol,
@@ -41,7 +41,7 @@ var j = class extends Error {
41
41
  })), e.on("error", c);
42
42
  });
43
43
  if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
44
- l.destroy(new j(Date.now() - o, e));
44
+ l.destroy(new A(Date.now() - o, e));
45
45
  }), l.on("error", c), typeof t == "string" || Buffer.isBuffer(t)) {
46
46
  l.end(t);
47
47
  return;
@@ -55,7 +55,7 @@ var j = class extends Error {
55
55
  }
56
56
  })();
57
57
  });
58
- }, he = (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.", ge = 16777216, _e = async function* (e, t) {
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) {
59
59
  let n = [], r = 0, i = 0, a = function* (e) {
60
60
  let t = Buffer.concat(n);
61
61
  n = [], r = 0;
@@ -77,12 +77,12 @@ var j = class extends Error {
77
77
  i += a.byteLength, yield s;
78
78
  }
79
79
  yield* a(!0);
80
- }, ve = (e) => te("sha256").update(Buffer.from(e)).digest("hex"), ye = async (e) => {
80
+ }, ye = (e) => te("sha256").update(Buffer.from(e)).digest("hex"), be = async (e) => {
81
81
  let t = 0, n = 0, r = {
82
82
  status: 0,
83
83
  body: {}
84
84
  };
85
- for await (let i of _e(e.stream, e.chunkBytes)) {
85
+ for await (let i of ve(e.stream, e.chunkBytes)) {
86
86
  if (i.offset + i.bytes.byteLength <= e.alreadyHeld && !i.final) {
87
87
  n++;
88
88
  continue;
@@ -92,7 +92,7 @@ var j = class extends Error {
92
92
  n++;
93
93
  continue;
94
94
  }
95
- if (r = await e.target.send(Buffer.from(o), {
95
+ if (i.final && e.onFinalChunk?.(), r = await e.target.send(Buffer.from(o), {
96
96
  ...e.baseHeaders,
97
97
  "content-type": "application/octet-stream",
98
98
  "x-import-upload-id": e.uploadId,
@@ -108,13 +108,13 @@ var j = class extends Error {
108
108
  bytes: t,
109
109
  resumedFrom: e.alreadyHeld
110
110
  };
111
- }, N = "voltro-backup-stamp.json", be = (e) => ({
111
+ }, M = "voltro-backup-stamp.json", xe = (e) => ({
112
112
  stampVersion: 1,
113
113
  dialect: e.dialect,
114
114
  schemaFingerprint: e.schemaFingerprint,
115
115
  voltroVersion: e.voltroVersion,
116
116
  createdAt: e.createdAt
117
- }), P = (e) => {
117
+ }), N = (e) => {
118
118
  let t;
119
119
  try {
120
120
  t = JSON.parse(e);
@@ -132,10 +132,10 @@ var j = class extends Error {
132
132
  voltroVersion: n.voltroVersion,
133
133
  createdAt: n.createdAt
134
134
  };
135
- }, xe = (e) => {
135
+ }, Se = (e) => {
136
136
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
137
137
  if (t === null) return {
138
- warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
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.`,
139
139
  info: []
140
140
  };
141
141
  let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
@@ -146,20 +146,20 @@ var j = class extends Error {
146
146
  refuse: `dialect mismatch — backup is a ${t.dialect} dump, but DB_DIALECT is ${n}. A cross-dialect restore is never valid.`,
147
147
  info: i
148
148
  };
149
- }, F = (e) => {
149
+ }, P = (e) => {
150
150
  try {
151
151
  let t = new URL(e), n = t.pathname.replace(/^\//, "");
152
152
  return `${t.hostname.toLowerCase()}:${t.port || "(default)"}/${n}`;
153
153
  } catch {
154
154
  return e.trim();
155
155
  }
156
- }, Se = (e) => {
156
+ }, Ce = (e) => {
157
157
  let t = e.flagUrl ?? e.envUrl;
158
158
  if (t === void 0 || t.trim() === "") return {
159
159
  ok: !1,
160
160
  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
161
  };
162
- let n = F(t);
162
+ let n = P(t);
163
163
  return e.liveKeys.some((e) => e === n) ? {
164
164
  ok: !1,
165
165
  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 +167,7 @@ var j = class extends Error {
167
167
  ok: !0,
168
168
  url: t
169
169
  };
170
- }, Ce = (e) => {
170
+ }, we = (e) => {
171
171
  let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
172
172
  return r === 0 ? {
173
173
  pass: !1,
@@ -182,24 +182,24 @@ var j = class extends Error {
182
182
  pass: !0,
183
183
  lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
184
184
  };
185
- }, I = d({ scope: "voltro:data" }), we = async () => {
185
+ }, F = d({ scope: "voltro:data" }), Te = async () => {
186
186
  try {
187
187
  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
188
  return JSON.parse(n(r, "utf8")).version ?? "unknown";
189
189
  } catch {
190
190
  return "unknown";
191
191
  }
192
- }, L = async (e) => {
192
+ }, I = async (e) => {
193
193
  try {
194
- return g(await W(e));
194
+ return g(await U(e));
195
195
  } catch {
196
196
  return null;
197
197
  }
198
- }, R = (e) => {
198
+ }, L = (e) => {
199
199
  if (e.event !== "done") return;
200
200
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
201
- I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
202
- }, Te = async (e) => {
201
+ F.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
202
+ }, Ee = async (e) => {
203
203
  let t = (e) => e?.meta?.databaseId;
204
204
  if (e) {
205
205
  let n = await l({
@@ -219,12 +219,12 @@ var j = class extends Error {
219
219
  };
220
220
  }
221
221
  return null;
222
- }, z = async (e) => {
223
- let t = e.includes("--allow-live"), n = t ? null : await Te(B(e, "--api-url")), i = v({
224
- dialect: H(),
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(),
225
225
  ...r()
226
226
  }), a = y({
227
- dialect: H(),
227
+ dialect: V(),
228
228
  ...r()
229
229
  }), { refuse: o, message: s } = ne({
230
230
  liveInstance: n?.label ?? null,
@@ -232,8 +232,8 @@ var j = class extends Error {
232
232
  sameTarget: b(i, n?.databaseId),
233
233
  ...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
234
234
  });
235
- return !o || (I.error(s ?? "refusing: live instance detected"), !1);
236
- }, B = i, Ee = [
235
+ return !o || (F.error(s ?? "refusing: live instance detected"), !1);
236
+ }, z = i, De = [
237
237
  "--api-url",
238
238
  "--bundle-key",
239
239
  "--compression",
@@ -242,6 +242,7 @@ var j = class extends Error {
242
242
  "--chunk-size",
243
243
  "--exclude",
244
244
  "--mode",
245
+ "--no-atomic",
245
246
  "--on-conflict",
246
247
  "--passphrase",
247
248
  "--profile",
@@ -250,44 +251,44 @@ var j = class extends Error {
250
251
  "--timeout",
251
252
  "--tenant",
252
253
  "--token"
253
- ], V = (e) => a(e, Ee), H = () => {
254
+ ], B = (e) => a(e, De), V = () => {
254
255
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
255
256
  if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
256
257
  return t;
257
- }, U = async (e, t) => {
258
+ }, H = async (e, t) => {
258
259
  let n = (await o(e)).makeSqlLayer(t);
259
260
  return f.runPromise(m.SqlClient.pipe(f.flatMap(_), f.provide(n)));
260
- }, W = (e) => U(e, r()), De = () => {
261
+ }, U = (e) => H(e, r()), Oe = () => {
261
262
  let e = r(), t = [];
262
- return e.url && t.push(F(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
263
- }, G = async () => {
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 () => {
264
265
  let r = process.cwd(), i = await t(r);
265
266
  if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
266
- let a = await n(i, void 0), o = H();
267
+ let a = await n(i, void 0), o = V();
267
268
  return {
268
269
  store: a,
269
- snapshot: await W(o),
270
+ snapshot: await U(o),
270
271
  dialect: o
271
272
  };
272
- }, K = (e) => x(e, {
273
+ }, G = (e) => x(e, {
273
274
  table: ee,
274
275
  chunkSize: 500
275
276
  }).pipe(p.map((e) => ({
276
277
  key: String(e.key),
277
278
  contentType: String(e.contentType ?? "application/octet-stream"),
278
279
  size: Number(e.size ?? 0)
279
- }))), q = (e) => !!e && e.endsWith(".vbundle"), J = (e) => B(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Oe = (e) => e.includes("--encrypt") || B(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, ke = async (e, t) => {
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) => {
280
281
  let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
281
282
  await r(i.from(e), n(t));
282
- }, Y = (e) => ({ async *[Symbol.asyncIterator]() {
283
+ }, J = (e) => ({ async *[Symbol.asyncIterator]() {
283
284
  let { createReadStream: t } = await import("node:fs");
284
285
  for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
285
- } }), X = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Ae = (t, n) => {
286
- let r = B(t, "--tenant"), i = B(t, "--tables"), a = B(t, "--exclude");
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");
287
288
  if (a !== void 0) {
288
289
  if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
289
290
  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.");
290
- let t = X(a), o = new Set(n), s = t.filter((e) => !o.has(e));
291
+ let t = Y(a), o = new Set(n), s = t.filter((e) => !o.has(e));
291
292
  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.`);
292
293
  let c = new Set(t);
293
294
  return {
@@ -300,15 +301,15 @@ var j = class extends Error {
300
301
  tenantId: r
301
302
  } : i ? {
302
303
  kind: "tables",
303
- tables: X(i)
304
+ tables: Y(i)
304
305
  } : { kind: "all" };
305
- }, je = async () => {
306
+ }, Me = async () => {
306
307
  let e = await u(process.cwd());
307
308
  return ie(h(e));
308
- }, Me = "voltro/tenant", Ne = async () => {
309
- let e = (await u(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Me)).map((e) => e.tableName);
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);
310
311
  return e.length > 0 ? e : void 0;
311
- }, Pe = (e) => {
312
+ }, Fe = (e) => {
312
313
  for (let t of e.tables) if (t.samples.length !== 0) {
313
314
  console.log(`\n ${t.table}`);
314
315
  for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
@@ -321,8 +322,8 @@ var j = class extends Error {
321
322
  console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
322
323
  for (let t of e.unclassified) console.log(` ${t}`);
323
324
  } else console.log("\n ✓ every exported column is classified — safe to run.");
324
- }, Fe = async (t) => {
325
- let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = B(t.args, "--chunk-size"), o = a === void 0 ? ge : Math.round(Number(a) * 1024 * 1024);
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);
326
327
  if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
327
328
  let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
328
329
  for (; l <= o;) {
@@ -337,28 +338,34 @@ var j = class extends Error {
337
338
  kind: "small",
338
339
  body: Buffer.concat(c)
339
340
  };
340
- let d = n(process.cwd(), t.bundleDir), f = ve(q(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), {
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), {
341
342
  ...t.headers,
342
343
  "x-import-upload-id": p,
343
344
  "x-import-probe": "1"
344
- }, Z(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
345
+ }, Q(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
345
346
  h > 0 && t.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
346
- let g = await ye({
347
- stream: (async function* () {
348
- for (let e of c) yield e;
349
- for (;;) {
350
- let e = await s.next();
351
- if (e.done === !0) return;
352
- yield e.value;
353
- }
354
- })(),
347
+ let g = (async function* () {
348
+ for (let e of c) yield e;
349
+ for (;;) {
350
+ let e = await s.next();
351
+ if (e.done === !0) return;
352
+ yield e.value;
353
+ }
354
+ })(), _ = 0, v = await be({
355
+ stream: g,
355
356
  chunkBytes: o,
356
357
  uploadId: p,
357
358
  key: f,
358
359
  alreadyHeld: h,
359
360
  baseHeaders: t.headers,
361
+ onProgress: (e) => {
362
+ _++, t.log.info(`api import: uploading — chunk ${_}, ${Math.round(e / 1024)} KiB sent`);
363
+ },
364
+ onFinalChunk: () => {
365
+ 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
+ },
360
367
  target: { send: async (e, n) => {
361
- let r = await M(t.url, e, n, Z(t.args)), i = {};
368
+ let r = await j(t.url, e, n, Q(t.args)), i = {};
362
369
  try {
363
370
  i = JSON.parse(r.body.toString("utf8") || "{}");
364
371
  } catch {}
@@ -369,70 +376,70 @@ var j = class extends Error {
369
376
  } }
370
377
  });
371
378
  return t.log.info("api import: uploaded in chunks", {
372
- chunks: g.chunks,
373
- bytes: g.bytes,
374
- ...g.resumedFrom > 0 ? { resumedFrom: g.resumedFrom } : {}
379
+ chunks: v.chunks,
380
+ bytes: v.bytes,
381
+ ...v.resumedFrom > 0 ? { resumedFrom: v.resumedFrom } : {}
375
382
  }), {
376
383
  kind: "sent",
377
384
  res: {
378
- status: g.status,
385
+ status: v.status,
379
386
  headers: {},
380
- body: Buffer.from(JSON.stringify(g.body))
387
+ body: Buffer.from(JSON.stringify(v.body))
381
388
  }
382
389
  };
383
- }, Z = (t) => {
384
- let n = B(t, "--timeout");
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");
385
392
  if (n === void 0) return {};
386
393
  let r = Number(n);
387
394
  if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
388
395
  return { timeoutMs: Math.round(r * 1e3) };
389
- }, Ie = async (e) => {
390
- let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), r = V(e);
391
- 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;
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;
392
399
  if (t) {
393
- let t = B(e, "--api-url");
394
- if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
395
- let i = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
396
- if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
397
- if (B(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;
398
- let a = B(e, "--tables"), o = {
399
- ...B(e, "--profile") ? { profile: B(e, "--profile") } : {},
400
- ...B(e, "--tenant") ? { tenant: B(e, "--tenant") } : {},
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") } : {},
401
408
  ...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
402
- ...B(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
403
- ...B(e, "--compression") ? { compression: B(e, "--compression") } : {},
409
+ ...z(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
410
+ ...z(e, "--compression") ? { compression: z(e, "--compression") } : {},
404
411
  ...e.includes("--assets") ? { assets: !0 } : {},
405
412
  ...n ? { bundleKey: n } : {}
406
413
  }, s;
407
414
  try {
408
- s = await M(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
415
+ s = await j(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
409
416
  "content-type": "application/json",
410
417
  authorization: `Bearer ${i}`
411
- }, Z(e));
418
+ }, Q(e));
412
419
  } catch (e) {
413
- 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;
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;
414
421
  throw e;
415
422
  }
416
- if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
417
- if (n) return I.info("api export complete (pushed to storage)", {
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)", {
418
425
  via: t,
419
426
  ...JSON.parse(s.body.toString("utf8"))
420
427
  }), 0;
421
428
  let { resolve: c } = await import("node:path"), l = new Uint8Array(s.body);
422
- return await A((async function* () {
429
+ return await k((async function* () {
423
430
  yield l;
424
- })(), c(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
431
+ })(), c(process.cwd(), r)), F.info("api export complete (downloaded from the running instance)", {
425
432
  outDir: r,
426
433
  tables: s.headers["x-export-tables"],
427
434
  rows: s.headers["x-export-rows"],
428
435
  masked: s.headers["x-export-masked"]
429
436
  }), 0;
430
437
  }
431
- if (!r) return I.error("a <outDir> is required for a direct export"), 1;
432
- let i = B(e, "--profile"), a = i ? await s(i, { allowPath: !0 }) : {}, { store: o, snapshot: c, dialect: l } = await G(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, p = B(e, "--compression") ?? a.compression, m = B(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", h = a.masking, g = a.subset, _ = a.scope ?? Ae(e, c.tables.map((e) => e.name)), v = h ? await je() : void 0, y = _.kind === "tenant" ? await Ne() : void 0, b = y ? { tenantTables: y } : {};
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 } : {};
433
440
  if (e.includes("--dry-run")) {
434
441
  let e = _.kind === "tables" && _.tables ? [..._.tables] : c.tables.map((e) => e.name);
435
- if (I.info("export DRY RUN — nothing was written", {
442
+ if (F.info("export DRY RUN — nothing was written", {
436
443
  outDir: r,
437
444
  tables: e.length,
438
445
  scope: _.kind,
@@ -440,7 +447,7 @@ var j = class extends Error {
440
447
  compression: p ?? "zstd",
441
448
  assets: u,
442
449
  masking: h ? "profile policy applies" : "none"
443
- }), I.info(`would export: ${e.join(", ")}`), !h) return 0;
450
+ }), F.info(`would export: ${e.join(", ")}`), !h) return 0;
444
451
  let t = await f.runPromise(oe({
445
452
  store: o,
446
453
  snapshot: c,
@@ -451,12 +458,12 @@ var j = class extends Error {
451
458
  ...b
452
459
  }
453
460
  }));
454
- return Pe(t), +(t.unclassified.length > 0);
461
+ return Fe(t), +(t.unclassified.length > 0);
455
462
  }
456
- if (q(r)) {
463
+ if (K(r)) {
457
464
  let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), s = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
458
465
  try {
459
- let t = await f.runPromise(O({
466
+ let t = await f.runPromise(se({
460
467
  store: o,
461
468
  snapshot: c,
462
469
  dialect: l,
@@ -469,17 +476,17 @@ var j = class extends Error {
469
476
  ...p ? { compression: p } : {},
470
477
  ...h ? { masking: h } : {},
471
478
  ...v ? { classification: v } : {},
472
- onProgress: R
479
+ onProgress: L
473
480
  })), n = T({
474
481
  dir: s,
475
- ...d ? { blobs: de(d, () => K(o)) } : {}
476
- }), a = Oe(e);
482
+ ...d ? { blobs: fe(d, () => G(o)) } : {}
483
+ }), a = ke(e);
477
484
  if (a) {
478
- let t = J(e);
479
- if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
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;
480
487
  n = ae(n, t);
481
488
  }
482
- return await ke(n, i(process.cwd(), r)), I.info("export complete (single file)", {
489
+ return await Ae(n, i(process.cwd(), r)), F.info("export complete (single file)", {
483
490
  file: r,
484
491
  encrypted: a,
485
492
  assets: u,
@@ -493,7 +500,7 @@ var j = class extends Error {
493
500
  });
494
501
  }
495
502
  }
496
- let x = await f.runPromise(O({
503
+ let x = await f.runPromise(se({
497
504
  store: o,
498
505
  snapshot: c,
499
506
  dialect: l,
@@ -506,10 +513,10 @@ var j = class extends Error {
506
513
  ...p ? { compression: p } : {},
507
514
  ...h ? { masking: h } : {},
508
515
  ...v ? { classification: v } : {},
509
- ...d ? { assets: le(d, () => K(o)) } : {},
510
- onProgress: R
516
+ ...d ? { assets: ue(d, () => G(o)) } : {},
517
+ onProgress: L
511
518
  }));
512
- return I.info("export complete", {
519
+ return F.info("export complete", {
513
520
  outDir: r,
514
521
  tables: x.tables.length,
515
522
  rows: x.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -517,106 +524,118 @@ var j = class extends Error {
517
524
  compression: x.compression,
518
525
  ...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
519
526
  }), 0;
520
- }, Q = () => "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.", Le = (e) => {
521
- let t = B(e, "--tables");
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");
522
529
  return {
523
530
  ...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
524
531
  ...e.includes("--dry-run") ? { dryRun: !0 } : {}
525
532
  };
526
- }, Re = (e, t) => {
533
+ }, Be = (e) => ({
534
+ tables: e.tablesWritten,
535
+ rows: e.rowsWritten,
536
+ carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
537
+ ...e.tablesSkipped > 0 ? {
538
+ skippedTables: e.tablesSkipped,
539
+ skippedRows: e.rowsSkipped
540
+ } : {}
541
+ }), Ve = (e, t) => {
527
542
  if (e.includes("--dry-run")) {
528
- I.info("import DRY RUN — nothing was written", t), I.info(Q());
543
+ F.info("import DRY RUN — nothing was written", t), F.info(Re());
529
544
  return;
530
545
  }
531
- I.info("import complete", t);
532
- }, ze = (e) => {
546
+ 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);
548
+ return;
549
+ }
550
+ F.info("import complete", t);
551
+ }, He = (e) => {
533
552
  let t = e;
534
553
  if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return;
535
554
  let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
536
- 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.`);
537
- for (let e of r) I.error(` ${e.table} ${e.id} ${e.reason}`);
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}`);
538
557
  let o = i - a;
539
- o > 0 && I.error(` … and ${o} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`);
540
- }, Be = async (e) => {
541
- let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), i = V(e);
542
- 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;
543
- let a = B(e, "--mode") ?? "upsert", o = B(e, "--on-conflict") ?? "skip";
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";
544
563
  if (t) {
545
- let t = B(e, "--api-url");
546
- if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
547
- let r = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
548
- if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
549
- let s = B(e, "--tables"), c = {
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 = {
550
569
  authorization: `Bearer ${r}`,
551
570
  "x-import-mode": a,
552
571
  "x-import-on-conflict": o,
553
- ...e.includes("--atomic") ? { "x-import-atomic": "1" } : {},
572
+ ...Z(e, a) ? { "x-import-atomic": "1" } : {},
573
+ ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
554
574
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
555
575
  ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
556
576
  ...s ? { "x-import-tables": s } : {}
557
577
  }, { resolve: l } = await import("node:path"), u;
558
578
  if (n) c["content-type"] = "application/json", u = JSON.stringify({ bundleKey: n });
559
- else if (q(i)) {
560
- let t = Y(l(process.cwd(), i)), n = J(e);
579
+ else if (K(i)) {
580
+ let t = J(l(process.cwd(), i)), n = q(e);
561
581
  n && (t = w(t, n)), c["content-type"] = "application/octet-stream", u = t;
562
- } 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) });
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));
563
584
  let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
564
585
  try {
565
- let t = typeof u == "string" ? null : await Fe({
586
+ let t = typeof u == "string" ? null : await Ie({
566
587
  stream: u,
567
588
  url: d,
568
589
  headers: c,
569
590
  bundleDir: i,
570
591
  args: e,
571
- log: I
592
+ log: F
572
593
  });
573
- f = t === null ? await M(d, u, c, Z(e)) : t.kind === "sent" ? t.res : await M(d, t.body, c, Z(e));
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));
574
595
  } catch (e) {
575
- if (e instanceof j) return I.error(`api import: ${he(e, a)}`), 1;
596
+ if (e instanceof A) return F.error(`api import: ${ge(e, a)}`), 1;
576
597
  throw e;
577
598
  }
578
- if (f.status < 200 || f.status >= 300) return I.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
599
+ if (f.status < 200 || f.status >= 300) return F.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
579
600
  let p = JSON.parse(f.body.toString("utf8"));
580
- return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
601
+ return e.includes("--dry-run") ? (F.info("api import DRY RUN — nothing was written", {
581
602
  via: t,
582
603
  ...p
583
- }), I.info(Q()), 0) : (I.info("api import complete (imported by the running instance)", {
604
+ }), F.info(Re()), 0) : (F.info("api import complete (imported by the running instance)", {
584
605
  via: t,
585
606
  ...p
586
607
  }), 0);
587
608
  }
588
- if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
589
- if (!await z(e)) return 1;
590
- let { store: s, snapshot: c } = await G(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = H() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
591
- if (q(i)) {
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)) {
592
614
  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-"));
593
615
  try {
594
- let t = Y(r(process.cwd(), i)), n = J(e);
595
- n && (t = w(t, n)), await fe(t, {
616
+ let t = J(r(process.cwd(), i)), n = q(e);
617
+ return n && (t = w(t, n)), await pe(t, {
596
618
  dir: h,
597
- blobs: ue(d ?? S({}))
598
- });
599
- let m = await f.runPromise(se({
600
- store: s,
601
- bundleDir: h,
602
- mode: a,
603
- onConflict: o,
604
- atomic: e.includes("--atomic"),
605
- verify: !e.includes("--no-verify"),
606
- targetDialect: H(),
607
- allowIncompatible: l,
608
- targetSnapshot: c,
609
- force: l,
610
- onWarn: u,
611
- onProgress: R,
612
- assetsAlreadyRestored: !0,
613
- ...Le(e),
614
- ...p ? { copyLoader: p } : {}
615
- }));
616
- return Re(e, {
619
+ blobs: de(d ?? S({}))
620
+ }), Ve(e, {
617
621
  file: i,
618
- tables: m.tables.length,
619
- rows: m.tables.reduce((e, t) => e + t.rowCount, 0)
622
+ ...Be(await f.runPromise(ce({
623
+ store: s,
624
+ bundleDir: h,
625
+ mode: a,
626
+ onConflict: o,
627
+ atomic: Z(e, a),
628
+ verify: !e.includes("--no-verify"),
629
+ targetDialect: V(),
630
+ allowIncompatible: l,
631
+ targetSnapshot: c,
632
+ force: l,
633
+ onWarn: u,
634
+ onProgress: L,
635
+ assetsAlreadyRestored: !0,
636
+ ...ze(e),
637
+ ...p ? { copyLoader: p } : {}
638
+ })))
620
639
  }), 0;
621
640
  } finally {
622
641
  await p?.close(), await n.rm(h, {
@@ -625,32 +644,31 @@ var j = class extends Error {
625
644
  });
626
645
  }
627
646
  }
628
- let m = await f.runPromise(se({
647
+ let m = await f.runPromise(ce({
629
648
  store: s,
630
649
  bundleDir: i,
631
650
  mode: a,
632
651
  onConflict: o,
633
- atomic: e.includes("--atomic"),
652
+ atomic: Z(e, a),
634
653
  verify: !e.includes("--no-verify"),
635
- targetDialect: H(),
654
+ targetDialect: V(),
636
655
  allowIncompatible: l,
637
656
  targetSnapshot: c,
638
657
  force: l,
639
658
  onWarn: u,
640
- onProgress: R,
641
- ...Le(e),
642
- ...d ? { assets: ce(d) } : {},
659
+ onProgress: L,
660
+ ...ze(e),
661
+ ...d ? { assets: le(d) } : {},
643
662
  ...p ? { copyLoader: p } : {}
644
663
  })).catch((e) => {
645
- throw ze(e), e;
664
+ throw He(e), e;
646
665
  }).finally(() => p?.close());
647
- return Re(e, {
666
+ return Ve(e, {
648
667
  bundleDir: i,
649
- tables: m.tables.length,
650
- rows: m.tables.reduce((e, t) => e + t.rowCount, 0),
651
- assets: m.assets.count
668
+ ...Be(m),
669
+ assets: m.manifest.assets.count
652
670
  }), 0;
653
- }, Ve = () => {
671
+ }, We = () => {
654
672
  let e = r();
655
673
  return {
656
674
  ...e.url ? { url: e.url } : {},
@@ -660,75 +678,75 @@ var j = class extends Error {
660
678
  ...e.password ? { password: e.password } : {},
661
679
  ...e.database ? { database: e.database } : {}
662
680
  };
663
- }, He = async (e) => {
664
- let t = V(e);
665
- if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
666
- let n = H(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
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");
667
685
  await r(t, { recursive: !0 });
668
686
  let o = a(t, C(n));
669
- await f.runPromise(k(re(n, Ve(), o)));
670
- let s = be({
687
+ await f.runPromise(O(re(n, We(), o)));
688
+ let s = xe({
671
689
  dialect: n,
672
- schemaFingerprint: await L(n),
673
- voltroVersion: await we(),
690
+ schemaFingerprint: await I(n),
691
+ voltroVersion: await Te(),
674
692
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
675
693
  });
676
- return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
694
+ return await i(a(t, M), `${JSON.stringify(s, null, 2)}\n`, "utf8"), F.info("native backup complete", {
677
695
  dialect: n,
678
696
  artifact: o,
679
- stamp: N,
697
+ stamp: M,
680
698
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
681
699
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
682
700
  }), 0;
683
- }, Ue = async (e, t) => {
684
- let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Se({
685
- flagUrl: B(t, "--drill-url"),
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"),
686
704
  envUrl: process.env.DRILL_DB_URL,
687
- liveKeys: De()
705
+ liveKeys: Oe()
688
706
  });
689
- if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
690
- let s = await i(r(e, N), "utf8").then(P, () => null);
691
- 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;
692
- I.info("restore drill: restoring into throwaway DB", {
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", {
693
711
  dialect: n,
694
- target: F(o.url)
695
- }), await f.runPromise(k(D(n, { url: o.url }, a)));
712
+ target: P(o.url)
713
+ }), await f.runPromise(O(D(n, { url: o.url }, a)));
696
714
  let c = null, l = 0;
697
715
  try {
698
- let e = await U(n, { url: o.url });
716
+ let e = await H(n, { url: o.url });
699
717
  c = g(e), l = e.tables.length;
700
718
  } catch (e) {
701
- return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
719
+ return F.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
702
720
  }
703
- let u = Ce({
721
+ let u = we({
704
722
  stampFingerprint: s?.schemaFingerprint ?? null,
705
723
  restoredFingerprint: c,
706
724
  restoredTableCount: l
707
725
  });
708
- for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
726
+ for (let e of u.lines) u.pass ? F.info(`restore drill: ${e}`) : F.error(`restore drill: ${e}`);
709
727
  return +!u.pass;
710
- }, We = async (e) => {
711
- let t = V(e);
712
- 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;
713
- if (e.includes("--drill")) return Ue(t, e);
714
- if (!await z(e)) return 1;
715
- let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = xe({
716
- stamp: await i(r(t, N), "utf8").then(P, () => null),
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),
717
735
  currentDialect: n,
718
- currentFingerprint: await L(n)
736
+ currentFingerprint: await I(n)
719
737
  });
720
- for (let e of o.info) I.info(`backup stamp: ${e}`);
721
- if (o.warn && I.warn(o.warn), o.refuse) {
722
- if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
723
- I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
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)`);
724
742
  }
725
- return await f.runPromise(k(D(n, Ve(), a))), I.info("native restore complete", {
743
+ return await f.runPromise(O(D(n, We(), a))), F.info("native restore complete", {
726
744
  dialect: n,
727
745
  artifact: a
728
746
  }), 0;
729
- }, Ge = async (e) => {
730
- let t = V(e);
731
- if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
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;
732
750
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
733
751
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
734
752
  let n = r(a, "manifest.json"), o = r(a, "README.md");
@@ -737,25 +755,25 @@ var j = class extends Error {
737
755
  if (e !== void 0) return console.log(e), 0;
738
756
  }
739
757
  let s = await i.readFile(n, "utf8").catch(() => void 0);
740
- 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);
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);
741
759
  }
742
- let o = J(e), s = () => {
743
- let e = Y(a);
760
+ let o = q(e), s = () => {
761
+ let e = J(a);
744
762
  return o ? w(e, o) : e;
745
763
  };
746
764
  if (e.includes("--json")) {
747
765
  let e = (await E(s(), ["manifest.json"])).get("manifest.json");
748
- return e ? (console.log(e.toString("utf8")), 0) : (I.error("no manifest.json found in the bundle"), 1);
766
+ return e ? (console.log(e.toString("utf8")), 0) : (F.error("no manifest.json found in the bundle"), 1);
749
767
  }
750
768
  let c = (await E(s(), ["README.md"])).get("README.md");
751
769
  if (c) return console.log(c.toString("utf8")), 0;
752
770
  let l = (await E(s(), ["manifest.json"])).get("manifest.json");
753
- return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
754
- }, Ke = async (e) => {
755
- let t = V(e), n = V(e.filter((e) => e !== t));
756
- if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
757
- let { resolve: r } = await import("node:path"), i = Y(r(process.cwd(), t)), a = J(e);
758
- return a && (i = w(i, a)), await A(i, r(process.cwd(), n)), I.info("unpack complete", {
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", {
759
777
  file: t,
760
778
  dir: n
761
779
  }), 0;
@@ -791,6 +809,7 @@ var j = class extends Error {
791
809
  "--mode",
792
810
  "--on-conflict",
793
811
  "--atomic",
812
+ "--no-atomic",
794
813
  "--assets",
795
814
  "--force",
796
815
  "--dry-run",
@@ -820,13 +839,13 @@ var j = class extends Error {
820
839
  "--drill-url",
821
840
  "--api-url"
822
841
  ] }
823
- }, qe = (e) => e.split("=")[0], Je = (e, t) => {
842
+ }, Xe = (e) => e.split("=")[0], Ze = (e, t) => {
824
843
  let n = $[e];
825
844
  if (!n) return;
826
- let r = B(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
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 ?? []);
827
846
  for (let n of t) {
828
847
  if (!n.startsWith("--")) continue;
829
- let t = qe(n);
848
+ let t = Xe(n);
830
849
  if (i.has(t)) continue;
831
850
  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.`;
832
851
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -836,13 +855,13 @@ var j = class extends Error {
836
855
  ].includes(t)).map(([e]) => e);
837
856
  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.`;
838
857
  }
839
- }, Ye = async (e) => {
858
+ }, Qe = async (e) => {
840
859
  let [t, ...n] = e;
841
860
  if (t !== void 0 && $[t] !== void 0) {
842
- let e = Je(t, n);
843
- if (e) return I.error(e), 2;
861
+ let e = Ze(t, n);
862
+ if (e) return F.error(e), 2;
844
863
  }
845
- return t === "export" ? Ie(n) : t === "import" ? Be(n) : t === "unpack" ? Ke(n) : t === "inspect" ? Ge(n) : t === "backup" ? He(n) : t === "restore" ? We(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
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);
846
865
  };
847
866
  //#endregion
848
- export { Ye as runDataCommand };
867
+ export { Qe as runDataCommand };