@voltro/cli 0.43.2 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +50 -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-CYkHTox2.js} +18 -18
  11. package/dist/{dashboardCommand-Bt2hHMro.js → dashboardCommand-DHDmlo9A.js} +1 -1
  12. package/dist/{dataCommand-DD2bRHCu.js → dataCommand-WFn8CM11.js} +258 -236
  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 +43 -7
  35. package/templates/agent-docs/plugins/versioning.md +2 -0
  36. package/templates/agent-docs/whats-new.md +6 -21
  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,121 @@ 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}`);
538
- 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";
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
+ let o = Array.isArray(t.byTable) ? t.byTable : [];
557
+ o.length > 1 && F.error(` by table: ${o.map((e) => `${e.table} ${e.refused}`).join(", ")}`);
558
+ for (let e of r) F.error(` ${e.table} ${e.id} ${e.reason}`);
559
+ n.length < i && F.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
560
+ let s = i - a;
561
+ s > 0 && F.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`);
562
+ }, Ue = async (e) => {
563
+ let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
564
+ if (!i && !(t && n)) return F.error("usage: voltro data import <bundleDir> [--target direct|api] [--api-url <url>] [--token <secret>] [--timeout <seconds>] [--bundle-key <storage-key>] [--tables a,b] [--mode upsert|append|replace] [--on-conflict skip|fail] [--atomic] [--assets] [--no-verify] [--force] [--allow-live] [--dry-run] (--force: import despite schema drift AND cross-dialect warnings; --dry-run: report what would move, write nothing)"), 1;
565
+ let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
544
566
  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 = {
567
+ let t = z(e, "--api-url");
568
+ if (!t) return F.error("--target api requires --api-url <url> (the running instance)"), 1;
569
+ let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
570
+ if (!r) return F.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
571
+ let s = z(e, "--tables"), c = {
550
572
  authorization: `Bearer ${r}`,
551
573
  "x-import-mode": a,
552
574
  "x-import-on-conflict": o,
553
- ...e.includes("--atomic") ? { "x-import-atomic": "1" } : {},
575
+ ...Z(e, a) ? { "x-import-atomic": "1" } : {},
576
+ ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
554
577
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
555
578
  ...e.includes("--dry-run") ? { "x-import-dry-run": "1" } : {},
556
579
  ...s ? { "x-import-tables": s } : {}
557
580
  }, { resolve: l } = await import("node:path"), u;
558
581
  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);
582
+ else if (K(i)) {
583
+ let t = J(l(process.cwd(), i)), n = q(e);
561
584
  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) });
585
+ } else await (await import("node:fs/promises")).access(l(process.cwd(), i, "assets", "index.ndjson")).then(() => !0, () => !1) && F.warn("this directory bundle has assets; --target api uploads DATA ONLY. Pack a single-file `.vbundle` (voltro data export <name>.vbundle --assets) to transfer assets over the API."), c["content-type"] = "application/octet-stream", u = T({ dir: l(process.cwd(), i) });
586
+ a === "replace" && !Z(e, a) && F.warn(X(!0));
563
587
  let d = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, f;
564
588
  try {
565
- let t = typeof u == "string" ? null : await Fe({
589
+ let t = typeof u == "string" ? null : await Ie({
566
590
  stream: u,
567
591
  url: d,
568
592
  headers: c,
569
593
  bundleDir: i,
570
594
  args: e,
571
- log: I
595
+ log: F
572
596
  });
573
- f = t === null ? await M(d, u, c, Z(e)) : t.kind === "sent" ? t.res : await M(d, t.body, c, Z(e));
597
+ (t === null || t.kind === "small") && F.info("api import: uploading the bundle — this request stays open until the instance has imported it"), f = t === null ? await j(d, u, c, Q(e)) : t.kind === "sent" ? t.res : await j(d, t.body, c, Q(e));
574
598
  } catch (e) {
575
- if (e instanceof j) return I.error(`api import: ${he(e, a)}`), 1;
599
+ if (e instanceof A) return F.error(`api import: ${ge(e, a)}`), 1;
576
600
  throw e;
577
601
  }
578
- if (f.status < 200 || f.status >= 300) return I.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
602
+ if (f.status < 200 || f.status >= 300) return F.error(`api import failed: HTTP ${f.status} — ${f.body.toString("utf8")}`), 1;
579
603
  let p = JSON.parse(f.body.toString("utf8"));
580
- return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
604
+ return e.includes("--dry-run") ? (F.info("api import DRY RUN — nothing was written", {
581
605
  via: t,
582
606
  ...p
583
- }), I.info(Q()), 0) : (I.info("api import complete (imported by the running instance)", {
607
+ }), F.info(Re()), 0) : (F.info("api import complete (imported by the running instance)", {
584
608
  via: t,
585
609
  ...p
586
610
  }), 0);
587
611
  }
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)) {
612
+ if (!i) return F.error("a <bundleDir> is required for a direct import"), 1;
613
+ if (!await R(e)) return 1;
614
+ a === "replace" && e.includes("--allow-live") && !Z(e, a) && F.warn(X(!1));
615
+ let { store: s, snapshot: c } = await W(), l = e.includes("--force"), u = (e) => F.warn(e), d = e.includes("--assets") ? S({}) : void 0, p = V() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
616
+ if (K(i)) {
592
617
  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
618
  try {
594
- let t = Y(r(process.cwd(), i)), n = J(e);
595
- n && (t = w(t, n)), await fe(t, {
619
+ let t = J(r(process.cwd(), i)), n = q(e);
620
+ return n && (t = w(t, n)), await pe(t, {
596
621
  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, {
622
+ blobs: de(d ?? S({}))
623
+ }), Ve(e, {
617
624
  file: i,
618
- tables: m.tables.length,
619
- rows: m.tables.reduce((e, t) => e + t.rowCount, 0)
625
+ ...Be(await f.runPromise(ce({
626
+ store: s,
627
+ bundleDir: h,
628
+ mode: a,
629
+ onConflict: o,
630
+ atomic: Z(e, a),
631
+ verify: !e.includes("--no-verify"),
632
+ targetDialect: V(),
633
+ allowIncompatible: l,
634
+ targetSnapshot: c,
635
+ force: l,
636
+ onWarn: u,
637
+ onProgress: L,
638
+ assetsAlreadyRestored: !0,
639
+ ...ze(e),
640
+ ...p ? { copyLoader: p } : {}
641
+ })))
620
642
  }), 0;
621
643
  } finally {
622
644
  await p?.close(), await n.rm(h, {
@@ -625,32 +647,31 @@ var j = class extends Error {
625
647
  });
626
648
  }
627
649
  }
628
- let m = await f.runPromise(se({
650
+ let m = await f.runPromise(ce({
629
651
  store: s,
630
652
  bundleDir: i,
631
653
  mode: a,
632
654
  onConflict: o,
633
- atomic: e.includes("--atomic"),
655
+ atomic: Z(e, a),
634
656
  verify: !e.includes("--no-verify"),
635
- targetDialect: H(),
657
+ targetDialect: V(),
636
658
  allowIncompatible: l,
637
659
  targetSnapshot: c,
638
660
  force: l,
639
661
  onWarn: u,
640
- onProgress: R,
641
- ...Le(e),
642
- ...d ? { assets: ce(d) } : {},
662
+ onProgress: L,
663
+ ...ze(e),
664
+ ...d ? { assets: le(d) } : {},
643
665
  ...p ? { copyLoader: p } : {}
644
666
  })).catch((e) => {
645
- throw ze(e), e;
667
+ throw He(e), e;
646
668
  }).finally(() => p?.close());
647
- return Re(e, {
669
+ return Ve(e, {
648
670
  bundleDir: i,
649
- tables: m.tables.length,
650
- rows: m.tables.reduce((e, t) => e + t.rowCount, 0),
651
- assets: m.assets.count
671
+ ...Be(m),
672
+ assets: m.manifest.assets.count
652
673
  }), 0;
653
- }, Ve = () => {
674
+ }, We = () => {
654
675
  let e = r();
655
676
  return {
656
677
  ...e.url ? { url: e.url } : {},
@@ -660,75 +681,75 @@ var j = class extends Error {
660
681
  ...e.password ? { password: e.password } : {},
661
682
  ...e.database ? { database: e.database } : {}
662
683
  };
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");
684
+ }, Ge = async (e) => {
685
+ let t = B(e);
686
+ if (!t) return F.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
687
+ let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
667
688
  await r(t, { recursive: !0 });
668
689
  let o = a(t, C(n));
669
- await f.runPromise(k(re(n, Ve(), o)));
670
- let s = be({
690
+ await f.runPromise(O(re(n, We(), o)));
691
+ let s = xe({
671
692
  dialect: n,
672
- schemaFingerprint: await L(n),
673
- voltroVersion: await we(),
693
+ schemaFingerprint: await I(n),
694
+ voltroVersion: await Te(),
674
695
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
675
696
  });
676
- return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
697
+ return await i(a(t, M), `${JSON.stringify(s, null, 2)}\n`, "utf8"), F.info("native backup complete", {
677
698
  dialect: n,
678
699
  artifact: o,
679
- stamp: N,
700
+ stamp: M,
680
701
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
681
702
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
682
703
  }), 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"),
704
+ }, Ke = async (e, t) => {
705
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Ce({
706
+ flagUrl: z(t, "--drill-url"),
686
707
  envUrl: process.env.DRILL_DB_URL,
687
- liveKeys: De()
708
+ liveKeys: Oe()
688
709
  });
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", {
710
+ if (!o.ok) return F.error(`restore --drill: ${o.error}`), 1;
711
+ let s = await i(r(e, M), "utf8").then(N, () => null);
712
+ if (s && s.dialect !== n) return F.error(`restore --drill: backup is a ${s.dialect} dump but DB_DIALECT is ${n} — cross-dialect restore is never valid.`), 1;
713
+ F.info("restore drill: restoring into throwaway DB", {
693
714
  dialect: n,
694
- target: F(o.url)
695
- }), await f.runPromise(k(D(n, { url: o.url }, a)));
715
+ target: P(o.url)
716
+ }), await f.runPromise(O(D(n, { url: o.url }, a)));
696
717
  let c = null, l = 0;
697
718
  try {
698
- let e = await U(n, { url: o.url });
719
+ let e = await H(n, { url: o.url });
699
720
  c = g(e), l = e.tables.length;
700
721
  } catch (e) {
701
- return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
722
+ return F.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
702
723
  }
703
- let u = Ce({
724
+ let u = we({
704
725
  stampFingerprint: s?.schemaFingerprint ?? null,
705
726
  restoredFingerprint: c,
706
727
  restoredTableCount: l
707
728
  });
708
- for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
729
+ for (let e of u.lines) u.pass ? F.info(`restore drill: ${e}`) : F.error(`restore drill: ${e}`);
709
730
  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),
731
+ }, qe = async (e) => {
732
+ let t = B(e);
733
+ if (!t) return F.error("usage: voltro data restore <backupDir> [--allow-live] [--api-url <url>] [--force] [--drill [--drill-url <url>]] (native pg_restore/mysql/…)"), 1;
734
+ if (e.includes("--drill")) return Ke(t, e);
735
+ if (!await R(e)) return 1;
736
+ let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Se({
737
+ stamp: await i(r(t, M), "utf8").then(N, () => null),
717
738
  currentDialect: n,
718
- currentFingerprint: await L(n)
739
+ currentFingerprint: await I(n)
719
740
  });
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)`);
741
+ for (let e of o.info) F.info(`backup stamp: ${e}`);
742
+ if (o.warn && F.warn(o.warn), o.refuse) {
743
+ if (!e.includes("--force")) return F.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
744
+ F.warn(`restore proceeding despite: ${o.refuse} (--force)`);
724
745
  }
725
- return await f.runPromise(k(D(n, Ve(), a))), I.info("native restore complete", {
746
+ return await f.runPromise(O(D(n, We(), a))), F.info("native restore complete", {
726
747
  dialect: n,
727
748
  artifact: a
728
749
  }), 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;
750
+ }, Je = async (e) => {
751
+ let t = B(e);
752
+ if (!t) return F.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
732
753
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
733
754
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
734
755
  let n = r(a, "manifest.json"), o = r(a, "README.md");
@@ -737,25 +758,25 @@ var j = class extends Error {
737
758
  if (e !== void 0) return console.log(e), 0;
738
759
  }
739
760
  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);
761
+ return s === void 0 ? (F.error(`'${t}' is a directory but holds no manifest.json — is it an unpacked bundle? (a bundle exported with --target api is always unpacked into a directory, even when the path ends in .vbundle)`), 1) : (console.log(s), 0);
741
762
  }
742
- let o = J(e), s = () => {
743
- let e = Y(a);
763
+ let o = q(e), s = () => {
764
+ let e = J(a);
744
765
  return o ? w(e, o) : e;
745
766
  };
746
767
  if (e.includes("--json")) {
747
768
  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);
769
+ return e ? (console.log(e.toString("utf8")), 0) : (F.error("no manifest.json found in the bundle"), 1);
749
770
  }
750
771
  let c = (await E(s(), ["README.md"])).get("README.md");
751
772
  if (c) return console.log(c.toString("utf8")), 0;
752
773
  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", {
774
+ return l ? (console.log(l.toString("utf8")), 0) : (F.error("bundle has no README.md or manifest.json"), 1);
775
+ }, Ye = async (e) => {
776
+ let t = B(e), n = B(e.filter((e) => e !== t));
777
+ if (!t || !n) return F.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
778
+ let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
779
+ return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), F.info("unpack complete", {
759
780
  file: t,
760
781
  dir: n
761
782
  }), 0;
@@ -791,6 +812,7 @@ var j = class extends Error {
791
812
  "--mode",
792
813
  "--on-conflict",
793
814
  "--atomic",
815
+ "--no-atomic",
794
816
  "--assets",
795
817
  "--force",
796
818
  "--dry-run",
@@ -820,13 +842,13 @@ var j = class extends Error {
820
842
  "--drill-url",
821
843
  "--api-url"
822
844
  ] }
823
- }, qe = (e) => e.split("=")[0], Je = (e, t) => {
845
+ }, Xe = (e) => e.split("=")[0], Ze = (e, t) => {
824
846
  let n = $[e];
825
847
  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 ?? []);
848
+ let r = z(t, "--target") === "api" ? "api" : "direct", i = /* @__PURE__ */ new Set([...n.common, ...r === "api" ? n.apiOnly ?? [] : n.directOnly ?? []]), a = new Set(r === "api" ? n.directOnly ?? [] : n.apiOnly ?? []);
827
849
  for (let n of t) {
828
850
  if (!n.startsWith("--")) continue;
829
- let t = qe(n);
851
+ let t = Xe(n);
830
852
  if (i.has(t)) continue;
831
853
  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
854
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -836,13 +858,13 @@ var j = class extends Error {
836
858
  ].includes(t)).map(([e]) => e);
837
859
  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
860
  }
839
- }, Ye = async (e) => {
861
+ }, Qe = async (e) => {
840
862
  let [t, ...n] = e;
841
863
  if (t !== void 0 && $[t] !== void 0) {
842
- let e = Je(t, n);
843
- if (e) return I.error(e), 2;
864
+ let e = Ze(t, n);
865
+ if (e) return F.error(e), 2;
844
866
  }
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);
867
+ return t === "export" ? Le(n) : t === "import" ? Ue(n) : t === "unpack" ? Ye(n) : t === "inspect" ? Je(n) : t === "backup" ? Ge(n) : t === "restore" ? qe(n) : (F.error("usage: voltro data <export|import|unpack|inspect|backup|restore> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
846
868
  };
847
869
  //#endregion
848
- export { Ye as runDataCommand };
870
+ export { Qe as runDataCommand };