@voltro/cli 0.47.0 → 0.48.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 (91) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/apiBuild-DOnvi2zm.js +2 -0
  3. package/dist/{apiBuild-FpHCWiv6.js → apiBuild-Dz8TJSxp.js} +1 -1
  4. package/dist/bin.js +1 -1
  5. package/dist/{build-zVygXxGB.js → build-DLme2ppa.js} +6 -6
  6. package/dist/{checkCommand-B2Fw73lu.js → checkCommand-BUBqPBlI.js} +1 -1
  7. package/dist/{checkCommand-D0dAKciE.js → checkCommand-Dals7g-I.js} +3 -3
  8. package/dist/{codegenCommand-BTtsnRus.js → codegenCommand-L6vacVRW.js} +3 -2
  9. package/dist/{commands-BrFKxnYW.js → commands-CAxFLqG4.js} +17 -17
  10. package/dist/{dashboardCommand-D9SjDc8E.js → dashboardCommand-CJC8Kg-s.js} +1 -1
  11. package/dist/{dataCommand-Dl11q14e.js → dataCommand-hIaq2iKY.js} +266 -189
  12. package/dist/dbCommand-D1Q33ktl.js +2 -0
  13. package/dist/{dbCommand-jbrmzwW_.js → dbCommand-ZGNItjoJ.js} +4 -4
  14. package/dist/declaredSchemaGates-BlcwObhb.js +25 -0
  15. package/dist/{dev-CaIfZnw5.js → dev-cGGsLNn6.js} +1 -1
  16. package/dist/{dev-RjB7eE_B.js → dev-lvnVq5RD.js} +31 -30
  17. package/dist/{doctorCommand-D7X29cw6.js → doctorCommand-BK0qu7eD.js} +32 -14
  18. package/dist/doctorCommand-CWgUPye-.js +2 -0
  19. package/dist/doctorDecidedSchema-BlbWgCD_.js +23 -0
  20. package/dist/{dormancyCommand-BGOP27fz.js → dormancyCommand-CyIk-zq4.js} +1 -1
  21. package/dist/{embeddingsCommand-D72E22bw.js → embeddingsCommand-D7TLklps.js} +1 -1
  22. package/dist/{envCommand-BHSxz6wC.js → envCommand-BHxKkGXq.js} +1 -1
  23. package/dist/{evolveCommand-C2ofGGlz.js → evolveCommand-CQh6dw21.js} +1 -1
  24. package/dist/{frameworkTableAssembly-BvZ0Lil_.js → frameworkTableAssembly-BNod_DKN.js} +97 -120
  25. package/dist/frameworkTableAssembly-Rft-DPUg.js +2 -0
  26. package/dist/index.js +1 -1
  27. package/dist/{migrate-odCPGHPq.js → migrate-CL5Ed01M.js} +4 -4
  28. package/dist/{runtimeTrace-C_OKr0jY.js → runtimeTrace-C0SlIoQY.js} +1 -1
  29. package/dist/{sdkgen-D9trW7sl.js → sdkgen-BXm7zx7F.js} +1 -1
  30. package/dist/{serveCommand-BU0wfV6V.js → serveCommand-CV3YO9L3.js} +469 -365
  31. package/dist/serveCommand-ZP_Cb-yz.js +2 -0
  32. package/dist/serveEntry.js +1 -1
  33. package/dist/{webhooksCommand-BqcWcbuW.js → webhooksCommand-QlAk_tED.js} +1 -1
  34. package/package.json +17 -17
  35. package/templates/AGENTS.md +1 -1
  36. package/templates/agent-docs/_index.md +1 -1
  37. package/templates/agent-docs/cli.md +58 -3
  38. package/templates/agent-docs/database/migrations.md +19 -5
  39. package/templates/agent-docs/whats-new.md +63 -13
  40. package/templates/apps/api-ai/package.json +7 -7
  41. package/templates/apps/api-auth/package.json +8 -8
  42. package/templates/apps/api-backend/package.json +7 -7
  43. package/templates/apps/api-backend-deactivation/package.json +7 -7
  44. package/templates/apps/api-backend-mail/package.json +8 -8
  45. package/templates/apps/api-backend-mariadb/package.json +9 -9
  46. package/templates/apps/api-backend-sqlite/package.json +8 -8
  47. package/templates/apps/api-backend-storage/package.json +8 -8
  48. package/templates/apps/api-cms/package.json +10 -10
  49. package/templates/apps/api-collab/package.json +8 -8
  50. package/templates/apps/api-data-advanced/package.json +8 -8
  51. package/templates/apps/api-durable/package.json +8 -8
  52. package/templates/apps/api-feature-flags/package.json +9 -9
  53. package/templates/apps/api-governance/package.json +8 -8
  54. package/templates/apps/api-kv/package.json +8 -8
  55. package/templates/apps/api-moderation/package.json +8 -8
  56. package/templates/apps/api-observability/package.json +8 -8
  57. package/templates/apps/api-ratelimit/package.json +8 -8
  58. package/templates/apps/api-rbac/package.json +8 -8
  59. package/templates/apps/api-rest/package.json +7 -7
  60. package/templates/apps/api-saas/package.json +11 -11
  61. package/templates/apps/api-saas-starter/package.json +10 -10
  62. package/templates/apps/api-search/package.json +8 -8
  63. package/templates/apps/api-status/package.json +8 -8
  64. package/templates/apps/api-versioning/package.json +8 -8
  65. package/templates/apps/api-webhooks/package.json +9 -9
  66. package/templates/apps/changelog/package.json +6 -6
  67. package/templates/apps/edge-functions/package.json +2 -2
  68. package/templates/apps/frontend-admin/package.json +8 -8
  69. package/templates/apps/frontend-app/package.json +9 -9
  70. package/templates/apps/frontend-auth/package.json +8 -8
  71. package/templates/apps/frontend-blank/package.json +7 -7
  72. package/templates/apps/frontend-cms/package.json +9 -9
  73. package/templates/apps/frontend-collab/package.json +10 -10
  74. package/templates/apps/frontend-contact/package.json +7 -7
  75. package/templates/apps/frontend-dashboard/package.json +7 -7
  76. package/templates/apps/frontend-docs/package.json +7 -7
  77. package/templates/apps/frontend-i18n/package.json +6 -6
  78. package/templates/apps/frontend-landing/package.json +7 -7
  79. package/templates/apps/frontend-portal/package.json +8 -8
  80. package/templates/apps/frontend-saas/package.json +8 -8
  81. package/templates/apps/frontend-spa/package.json +7 -7
  82. package/templates/apps/frontend-ssr/package.json +7 -7
  83. package/templates/apps/frontend-ssr-api/package.json +8 -8
  84. package/templates/apps/frontend-static-blog/package.json +6 -6
  85. package/templates/apps/frontend-status/package.json +8 -8
  86. package/templates/apps/mobile-app/package.json +4 -4
  87. package/dist/apiBuild-BsooZpw2.js +0 -2
  88. package/dist/dbCommand-BUD4Fd8v.js +0 -2
  89. package/dist/doctorCommand-5zv6PN-G.js +0 -2
  90. package/dist/frameworkTableAssembly-DDKtwsy2.js +0 -2
  91. package/dist/serveCommand-Bwrn_9Fn.js +0 -2
@@ -1,19 +1,19 @@
1
1
  import { t as e } from "./cliError-BmdYnghb.js";
2
- import { c as t, i as n, o as r, z as i } from "./dev-RjB7eE_B.js";
2
+ import { c as t, i as n, o as r, z as i } from "./dev-lvnVq5RD.js";
3
3
  import { n as a, t as o } from "./cliArgs-D4p8n7EE.js";
4
4
  import { r as s } from "./dialectDriver-czCHYpeH.js";
5
5
  import { t as c } from "./dataProfile-Cm0YVKSy.js";
6
6
  import { o as l, t as u } from "./inspectFetch-EMuhTG_9.js";
7
- import { r as d } from "./dbCommand-jbrmzwW_.js";
7
+ import { r as d } from "./dbCommand-ZGNItjoJ.js";
8
8
  import { resolve as f } from "node:path";
9
9
  import { createLogger as p } from "@voltro/logger";
10
- import { connectionIdentity as m, normaliseTarget as h, sameDatabase as g, streamTable as _ } from "@voltro/database";
11
- import { Effect as v, Stream as y } from "effect";
12
- import { SqlClient as b } from "@effect/sql";
13
- import { declaredSnapshot as x, fingerprintSchema as ee, introspectSchema as te } from "@voltro/database/sql";
14
- import { STORAGE_REFS_TABLE as ne, resolveStorageProvider as S } from "@voltro/plugin-storage";
15
- import { createHash as re } from "node:crypto";
16
- import { asImportError as ie, assessLiveImport as ae, backupArtifactName as C, backupCommand as oe, classificationFromSnapshot as se, decryptStream as w, encryptStream as ce, packBundle as le, peekBundle as T, previewMasking as ue, restoreCommand as de, runExport as E, runImport as D, runNativeStep as O, storageAssetSink as fe, storageAssetSource as pe, storageBlobSink as me, storageBlobSource as he, unpackBundle as ge, unpackBundleToDir as k } from "@voltro/data-transfer";
10
+ import { IMPORT_RUNS_TABLE as m, connectionIdentity as h, normaliseTarget as g, sameDatabase as _, streamTable as v } from "@voltro/database";
11
+ import { Effect as y, Stream as b } from "effect";
12
+ import { SqlClient as x } from "@effect/sql";
13
+ import { declaredSnapshot as ee, fingerprintSchema as te, introspectSchema as ne } from "@voltro/database/sql";
14
+ import { STORAGE_REFS_TABLE as re, resolveStorageProvider as S } from "@voltro/plugin-storage";
15
+ import { createHash as ie } from "node:crypto";
16
+ import { asImportError as ae, assessLiveImport as oe, backupArtifactName as C, backupCommand as se, classificationFromSnapshot as ce, decryptStream as w, encryptStream as le, packBundle as ue, peekBundle as T, previewMasking as de, restoreCommand as E, runExport as D, runImport as O, runNativeStep as k, storageAssetSink as fe, storageAssetSource as pe, storageBlobSink as me, storageBlobSource as he, unpackBundle as ge, unpackBundleToDir as A } from "@voltro/data-transfer";
17
17
  import { request as _e } from "node:http";
18
18
  import { request as ve } from "node:https";
19
19
  //#region src/longRequest.ts
@@ -25,7 +25,7 @@ var ye = /* @__PURE__ */ new Set([
25
25
  "ENOTFOUND",
26
26
  "ECONNRESET",
27
27
  "EAI_AGAIN"
28
- ]), A = class extends Error {
28
+ ]), j = class extends Error {
29
29
  url;
30
30
  cause;
31
31
  constructor(e, t) {
@@ -38,13 +38,13 @@ var ye = /* @__PURE__ */ new Set([
38
38
  })();
39
39
  super(`the instance at ${n} is not answering (${t.code ?? "connect failed"}).`), this.url = e, this.cause = t, this.name = "InstanceUnreachable";
40
40
  }
41
- }, j = class extends Error {
41
+ }, M = class extends Error {
42
42
  elapsedMs;
43
43
  url;
44
44
  constructor(e, t) {
45
45
  super(`no response after ${Math.round(e / 1e3)}s`), this.elapsedMs = e, this.url = t, this.name = "LongRequestTimeout";
46
46
  }
47
- }, M = async (e, t, n, r) => {
47
+ }, N = async (e, t, n, r) => {
48
48
  let i = new URL(e), a = i.protocol === "https:" ? ve : _e, o = Date.now();
49
49
  return await new Promise((s, c) => {
50
50
  let l = a({
@@ -63,8 +63,8 @@ var ye = /* @__PURE__ */ new Set([
63
63
  })), e.on("error", c);
64
64
  });
65
65
  if (r?.timeoutMs !== void 0 && r.timeoutMs > 0 && l.setTimeout(r.timeoutMs, () => {
66
- l.destroy(new j(Date.now() - o, e));
67
- }), l.on("error", (t) => c(ye.has(t.code ?? "") ? new A(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
66
+ l.destroy(new M(Date.now() - o, e));
67
+ }), l.on("error", (t) => c(ye.has(t.code ?? "") ? new j(e, t) : t)), typeof t == "string" || Buffer.isBuffer(t)) {
68
68
  l.end(t);
69
69
  return;
70
70
  }
@@ -99,7 +99,7 @@ var ye = /* @__PURE__ */ new Set([
99
99
  i += a.byteLength, yield s;
100
100
  }
101
101
  yield* a(!0);
102
- }, we = (e) => re("sha256").update(Buffer.from(e)).digest("hex"), Te = async (e) => {
102
+ }, we = (e) => ie("sha256").update(Buffer.from(e)).digest("hex"), Te = async (e) => {
103
103
  let t = 0, n = 0, r = {
104
104
  status: 0,
105
105
  body: {}
@@ -130,13 +130,13 @@ var ye = /* @__PURE__ */ new Set([
130
130
  bytes: t,
131
131
  resumedFrom: e.alreadyHeld
132
132
  };
133
- }, N = "voltro-backup-stamp.json", Ee = (e) => ({
133
+ }, P = "voltro-backup-stamp.json", Ee = (e) => ({
134
134
  stampVersion: 1,
135
135
  dialect: e.dialect,
136
136
  schemaFingerprint: e.schemaFingerprint,
137
137
  voltroVersion: e.voltroVersion,
138
138
  createdAt: e.createdAt
139
- }), P = (e) => {
139
+ }), De = (e) => {
140
140
  let t;
141
141
  try {
142
142
  t = JSON.parse(e);
@@ -154,10 +154,10 @@ var ye = /* @__PURE__ */ new Set([
154
154
  voltroVersion: n.voltroVersion,
155
155
  createdAt: n.createdAt
156
156
  };
157
- }, De = (e) => {
157
+ }, Oe = (e) => {
158
158
  let { stamp: t, currentDialect: n, currentFingerprint: r } = e;
159
159
  if (t === null) return {
160
- warn: `no ${N} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
160
+ warn: `no ${P} beside the dump — an older/handmade backup. Cannot verify dialect or schema version; make sure this dump is a ${n} dump.`,
161
161
  info: []
162
162
  };
163
163
  let i = [`backup produced by @voltro/cli ${t.voltroVersion} at ${t.createdAt}`, `schema fingerprint: ${t.schemaFingerprint ?? "<none recorded>"}`];
@@ -175,7 +175,7 @@ var ye = /* @__PURE__ */ new Set([
175
175
  } catch {
176
176
  return e.trim();
177
177
  }
178
- }, Oe = (e) => {
178
+ }, ke = (e) => {
179
179
  let t = e.flagUrl ?? e.envUrl;
180
180
  if (t === void 0 || t.trim() === "") return {
181
181
  ok: !1,
@@ -189,7 +189,7 @@ var ye = /* @__PURE__ */ new Set([
189
189
  ok: !0,
190
190
  url: t
191
191
  };
192
- }, ke = (e) => {
192
+ }, Ae = (e) => {
193
193
  let { stampFingerprint: t, restoredFingerprint: n, restoredTableCount: r } = e;
194
194
  return r === 0 ? {
195
195
  pass: !1,
@@ -204,7 +204,7 @@ var ye = /* @__PURE__ */ new Set([
204
204
  pass: !0,
205
205
  lines: [`PASS — restored ${r} table(s); schema fingerprint matches the backup stamp (${n.slice(0, 12)}).`]
206
206
  };
207
- }, I = p({ scope: "voltro:data" }), Ae = async () => {
207
+ }, I = p({ scope: "voltro:data" }), je = async () => {
208
208
  try {
209
209
  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");
210
210
  return JSON.parse(n(r, "utf8")).version ?? "unknown";
@@ -213,7 +213,7 @@ var ye = /* @__PURE__ */ new Set([
213
213
  }
214
214
  }, L = async (e) => {
215
215
  try {
216
- return ee(await U(e));
216
+ return te(await W(e));
217
217
  } catch {
218
218
  return null;
219
219
  }
@@ -221,7 +221,7 @@ var ye = /* @__PURE__ */ new Set([
221
221
  if (e.event !== "done") return;
222
222
  let t = e.total === void 0 ? "" : ` / ${e.total}`;
223
223
  I.info(`${e.phase} [${e.index + 1}/${e.tableCount}] ${e.table}`, { rows: `${e.rowsDone}${t}` });
224
- }, je = async (e) => {
224
+ }, Me = async (e) => {
225
225
  let t = (e) => e?.meta?.databaseId;
226
226
  if (e) {
227
227
  let n = await u({
@@ -241,21 +241,21 @@ var ye = /* @__PURE__ */ new Set([
241
241
  };
242
242
  }
243
243
  return null;
244
- }, Me = async (e) => {
245
- let t = e.includes("--allow-live"), n = t ? null : await je(z(e, "--api-url")), i = m({
246
- dialect: V(),
244
+ }, z = async (e) => {
245
+ let t = e.includes("--allow-live"), n = t ? null : await Me(B(e, "--api-url")), i = h({
246
+ dialect: H(),
247
247
  ...r()
248
- }), a = h({
249
- dialect: V(),
248
+ }), a = g({
249
+ dialect: H(),
250
250
  ...r()
251
- }), { refuse: o, message: s } = ae({
251
+ }), { refuse: o, message: s } = oe({
252
252
  liveInstance: n?.label ?? null,
253
253
  allowLive: t,
254
- sameTarget: g(i, n?.databaseId),
254
+ sameTarget: _(i, n?.databaseId),
255
255
  ...a === void 0 ? {} : { targetLabel: `${a.host}:${a.port}/${a.database}` }
256
256
  });
257
257
  return !o || (I.error(s ?? "refusing: live instance detected"), !1);
258
- }, z = a, Ne = [
258
+ }, B = a, Ne = [
259
259
  "--api-url",
260
260
  "--bundle-key",
261
261
  "--compression",
@@ -263,6 +263,7 @@ var ye = /* @__PURE__ */ new Set([
263
263
  "--drill-url",
264
264
  "--chunk-size",
265
265
  "--exclude",
266
+ "--limit",
266
267
  "--mode",
267
268
  "--no-atomic",
268
269
  "--on-conflict",
@@ -273,49 +274,58 @@ var ye = /* @__PURE__ */ new Set([
273
274
  "--timeout",
274
275
  "--tenant",
275
276
  "--token"
276
- ], B = (e) => o(e, Ne), V = () => {
277
+ ], V = (e) => o(e, Ne), H = () => {
277
278
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
278
279
  if (t === "memory") throw new e("voltro data: requires a real dialect, not DB_DIALECT=memory.");
279
280
  return t;
280
- }, H = async (e, t) => {
281
+ }, U = async (e, t) => {
281
282
  let n = (await s(e)).makeSqlLayer(t);
282
- return v.runPromise(b.SqlClient.pipe(v.flatMap(te), v.provide(n)));
283
- }, U = (e) => H(e, r()), Pe = () => {
283
+ return y.runPromise(x.SqlClient.pipe(y.flatMap(ne), y.provide(n)));
284
+ }, W = (e) => U(e, r()), Pe = () => {
284
285
  let e = r(), t = [];
285
286
  return e.url && t.push(F(e.url)), (e.host || e.database) && t.push(`${(e.host ?? "localhost").toLowerCase()}:${e.port ?? "(default)"}/${e.database ?? ""}`), t;
286
- }, W = async () => {
287
+ }, G = async () => {
287
288
  let r = process.cwd(), i = await t(r);
288
289
  if (!i) throw new e("voltro data: no app.config.ts found in the current directory.");
289
- let a = await n(i, void 0), o = V();
290
+ let a = await n(i, void 0), o = H();
290
291
  return {
291
292
  store: a,
292
- snapshot: await U(o),
293
+ snapshot: await W(o),
293
294
  dialect: o
294
295
  };
295
- }, G = (e) => _(e, {
296
- table: ne,
296
+ }, K = (e) => v(e, {
297
+ table: re,
297
298
  chunkSize: 500
298
- }).pipe(y.map((e) => ({
299
+ }).pipe(b.map((e) => ({
299
300
  key: String(e.key),
300
301
  contentType: String(e.contentType ?? "application/octet-stream"),
301
302
  size: Number(e.size ?? 0)
302
- }))), K = (e) => !!e && e.endsWith(".vbundle"), q = (e) => z(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Fe = (e) => e.includes("--encrypt") || z(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ie = async (e, t) => {
303
+ }))), q = (e) => !!e && e.endsWith(".vbundle"), J = (e) => B(e, "--passphrase") ?? process.env.VOLTRO_BUNDLE_KEY, Fe = (e) => e.includes("--encrypt") || B(e, "--passphrase") !== void 0 || process.env.VOLTRO_BUNDLE_KEY !== void 0, Ie = async (e, t) => {
303
304
  let { createWriteStream: n } = await import("node:fs"), { pipeline: r } = await import("node:stream/promises"), { Readable: i } = await import("node:stream");
304
305
  await r(i.from(e), n(t));
305
- }, J = (e) => ({ async *[Symbol.asyncIterator]() {
306
+ }, Y = (e) => ({ async *[Symbol.asyncIterator]() {
306
307
  let { createReadStream: t } = await import("node:fs");
307
308
  for await (let n of t(e)) yield n instanceof Uint8Array ? n : new Uint8Array(n);
308
- } }), Y = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Le = (t, n) => {
309
- let r = z(t, "--tenant"), i = z(t, "--tables"), a = z(t, "--exclude");
309
+ } }), Le = async (e, t) => {
310
+ if (t !== void 0 || e === void 0) return null;
311
+ try {
312
+ let { resolve: t, join: n } = await import("node:path"), r = await import("node:fs/promises"), i = t(process.cwd(), e), a = q(e) ? (await T(Y(i), ["manifest.json"])).get("manifest.json") : await r.readFile(n(i, "manifest.json"));
313
+ if (a === void 0) return null;
314
+ let o = (JSON.parse(Buffer.from(a).toString("utf8")).tables ?? []).map((e) => e.name).filter((e) => typeof e == "string");
315
+ return o.length > 0 ? o : null;
316
+ } catch {
317
+ return null;
318
+ }
319
+ }, X = (e) => (e ?? "").split(",").map((e) => e.trim()).filter(Boolean), Re = (t, n) => {
320
+ let r = B(t, "--tenant"), i = B(t, "--tables"), a = B(t, "--exclude");
310
321
  if (a !== void 0) {
311
322
  if (i !== void 0) throw new e("voltro data export: --exclude and --tables are two ways to say the same thing; use one.");
312
323
  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.");
313
- let t = Y(a), o = new Set(n), s = t.filter((e) => !o.has(e));
324
+ let t = X(a), o = new Set(n), s = t.filter((e) => !o.has(e));
314
325
  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.`);
315
- let c = new Set(t);
316
326
  return {
317
- kind: "tables",
318
- tables: n.filter((e) => !c.has(e))
327
+ kind: "all",
328
+ exclude: t
319
329
  };
320
330
  }
321
331
  return r ? {
@@ -323,15 +333,15 @@ var ye = /* @__PURE__ */ new Set([
323
333
  tenantId: r
324
334
  } : i ? {
325
335
  kind: "tables",
326
- tables: Y(i)
336
+ tables: X(i)
327
337
  } : { kind: "all" };
328
- }, Re = async () => {
338
+ }, ze = async () => {
329
339
  let e = await d(process.cwd());
330
- return se(x(e));
331
- }, ze = "voltro/tenant", Be = async () => {
332
- let e = (await d(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === ze)).map((e) => e.tableName);
340
+ return ce(ee(e));
341
+ }, Be = "voltro/tenant", Ve = async () => {
342
+ let e = (await d(process.cwd())).filter((e) => (e.appliedMixins ?? []).some((e) => e.id === Be)).map((e) => e.tableName);
333
343
  return e.length > 0 ? e : void 0;
334
- }, Ve = (e) => {
344
+ }, He = (e) => {
335
345
  for (let t of e.tables) if (t.samples.length !== 0) {
336
346
  console.log(`\n ${t.table}`);
337
347
  for (let e of t.samples) console.log(` ${e.column} [${e.action}] ${JSON.stringify(e.before)} → ${JSON.stringify(e.after)}`);
@@ -344,8 +354,8 @@ var ye = /* @__PURE__ */ new Set([
344
354
  console.log("\n ✗ unclassified (would BLOCK a real export — add .sensitive()/.safe() or a policy override):");
345
355
  for (let t of e.unclassified) console.log(` ${t}`);
346
356
  } else console.log("\n ✓ every exported column is classified — safe to run.");
347
- }, He = async (t) => {
348
- 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 ? Se : Math.round(Number(a) * 1024 * 1024);
357
+ }, Ue = async (t) => {
358
+ 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 ? Se : Math.round(Number(a) * 1024 * 1024);
349
359
  if (!Number.isFinite(o) || o <= 0) throw new e(`--chunk-size expects a positive number of megabytes, got '${String(a)}'`);
350
360
  let s = t.stream[Symbol.asyncIterator](), c = [], l = 0, u = !1;
351
361
  for (; l <= o;) {
@@ -360,11 +370,11 @@ var ye = /* @__PURE__ */ new Set([
360
370
  kind: "small",
361
371
  body: Buffer.concat(c)
362
372
  };
363
- let d = n(process.cwd(), t.bundleDir), f = we(K(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), {
373
+ let d = n(process.cwd(), t.bundleDir), f = we(q(t.bundleDir) ? await i.readFile(d) : await i.readFile(r(d, "manifest.json"))), p = `up-${f.slice(0, 32)}`, m = await N(t.url, Buffer.alloc(0), {
364
374
  ...t.headers,
365
375
  "x-import-upload-id": p,
366
376
  "x-import-probe": "1"
367
- }, Z(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
377
+ }, Q(t.args)), h = m.status === 200 ? Number(JSON.parse(m.body.toString("utf8")).bytes ?? 0) : 0;
368
378
  h > 0 && t.log.info("api import: resuming a chunked upload the instance already holds", { bytes: h });
369
379
  let g = (async function* () {
370
380
  for (let e of c) yield e;
@@ -387,7 +397,7 @@ var ye = /* @__PURE__ */ new Set([
387
397
  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.");
388
398
  },
389
399
  target: { send: async (e, n) => {
390
- let r = await M(t.url, e, n, Z(t.args)), i = {};
400
+ let r = await N(t.url, e, n, Q(t.args)), i = {};
391
401
  try {
392
402
  i = JSON.parse(r.body.toString("utf8") || "{}");
393
403
  } catch {}
@@ -409,68 +419,70 @@ var ye = /* @__PURE__ */ new Set([
409
419
  body: Buffer.from(JSON.stringify(v.body))
410
420
  }
411
421
  };
412
- }, Ue = (e) => `--mode replace with --no-atomic against a ${e ? "RUNNING instance" : "live database"}: the target is empty between the delete and the load, and anything the application writes in that window can collide with a row from the bundle — or survive the import as a row the bundle never carried. Drop --no-atomic to run the whole load in one transaction (concurrent writers then wait instead of racing), or stop the traffic for the duration.`, X = (e, t) => e.includes("--no-atomic") ? !1 : e.includes("--atomic") || t === "replace", We = (e, t, n) => {
422
+ }, We = (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", Ge = (e, t, n) => {
413
423
  if (t !== "replace" || e.includes("--no-rollback")) return;
414
- let r = z(e, "--rollback-dir");
424
+ let r = B(e, "--rollback-dir");
415
425
  return r === void 0 ? i(n) : f(r);
416
- }, Ge = (e, t, n) => {
417
- let r = We(e, t, n);
426
+ }, Ke = (e, t, n) => {
427
+ let r = Ge(e, t, n);
418
428
  return {
419
429
  ...r === void 0 ? {} : { rollbackDir: r },
420
430
  recordInterruption: !0,
421
431
  via: "direct"
422
432
  };
423
- }, Z = (t) => {
424
- let n = z(t, "--timeout");
433
+ }, Q = (t) => {
434
+ let n = B(t, "--timeout");
425
435
  if (n === void 0) return {};
426
436
  let r = Number(n);
427
437
  if (!Number.isFinite(r) || r <= 0) throw new e(`--timeout expects a positive number of seconds, got '${n}'`);
428
438
  return { timeoutMs: Math.round(r * 1e3) };
429
- }, Ke = async (e) => {
430
- let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), r = B(e);
439
+ }, qe = async (e) => {
440
+ let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), r = V(e);
431
441
  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;
432
442
  if (t) {
433
- let t = z(e, "--api-url");
443
+ let t = B(e, "--api-url");
434
444
  if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
435
- let i = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
445
+ let i = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
436
446
  if (!i) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
437
- if (z(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;
438
- let a = z(e, "--tables"), o = {
439
- ...z(e, "--profile") ? { profile: z(e, "--profile") } : {},
440
- ...z(e, "--tenant") ? { tenant: z(e, "--tenant") } : {},
447
+ let a = B(e, "--tables"), o = B(e, "--exclude");
448
+ if (o !== void 0 && a !== void 0) return I.error("voltro data export: --exclude and --tables are two ways to say the same thing; use one."), 1;
449
+ let s = {
450
+ ...B(e, "--profile") ? { profile: B(e, "--profile") } : {},
451
+ ...B(e, "--tenant") ? { tenant: B(e, "--tenant") } : {},
441
452
  ...a ? { tables: a.split(",").map((e) => e.trim()).filter(Boolean) } : {},
442
- ...z(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
443
- ...z(e, "--compression") ? { compression: z(e, "--compression") } : {},
453
+ ...o ? { exclude: o.split(",").map((e) => e.trim()).filter(Boolean) } : {},
454
+ ...B(e, "--consistency") === "snapshot" ? { consistency: "snapshot" } : {},
455
+ ...B(e, "--compression") ? { compression: B(e, "--compression") } : {},
444
456
  ...e.includes("--assets") ? { assets: !0 } : {},
445
457
  ...n ? { bundleKey: n } : {}
446
- }, s;
458
+ }, c;
447
459
  try {
448
- s = await M(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(o), {
460
+ c = await N(`${t.replace(/\/+$/, "")}/_voltro/admin/export`, JSON.stringify(s), {
449
461
  "content-type": "application/json",
450
462
  authorization: `Bearer ${i}`
451
- }, Z(e));
463
+ }, Q(e));
452
464
  } catch (e) {
453
- 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;
454
- if (e instanceof A) return I.error(`api export: ${be(e)}`), 1;
465
+ if (e instanceof M) 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;
466
+ if (e instanceof j) return I.error(`api export: ${be(e)}`), 1;
455
467
  throw e;
456
468
  }
457
- if (s.status < 200 || s.status >= 300) return I.error(`api export failed: HTTP ${s.status} — ${s.body.toString("utf8")}`), 1;
469
+ if (c.status < 200 || c.status >= 300) return I.error(`api export failed: HTTP ${c.status} — ${c.body.toString("utf8")}`), 1;
458
470
  if (n) return I.info("api export complete (pushed to storage)", {
459
471
  via: t,
460
- ...JSON.parse(s.body.toString("utf8"))
472
+ ...JSON.parse(c.body.toString("utf8"))
461
473
  }), 0;
462
- let { resolve: c } = await import("node:path"), l = new Uint8Array(s.body);
463
- return await k((async function* () {
464
- yield l;
465
- })(), c(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
474
+ let { resolve: l } = await import("node:path"), u = new Uint8Array(c.body);
475
+ return await A((async function* () {
476
+ yield u;
477
+ })(), l(process.cwd(), r)), I.info("api export complete (downloaded from the running instance)", {
466
478
  outDir: r,
467
- tables: s.headers["x-export-tables"],
468
- rows: s.headers["x-export-rows"],
469
- masked: s.headers["x-export-masked"]
479
+ tables: c.headers["x-export-tables"],
480
+ rows: c.headers["x-export-rows"],
481
+ masked: c.headers["x-export-masked"]
470
482
  }), 0;
471
483
  }
472
484
  if (!r) return I.error("a <outDir> is required for a direct export"), 1;
473
- let i = z(e, "--profile"), a = i ? await c(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: l } = await W(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, f = z(e, "--compression") ?? a.compression, p = z(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", m = a.masking, h = a.subset, g = a.scope ?? Le(e, s.tables.map((e) => e.name)), _ = m ? await Re() : void 0, y = g.kind === "tenant" ? await Be() : void 0, b = y ? { tenantTables: y } : {};
485
+ let i = B(e, "--profile"), a = i ? await c(i, { allowPath: !0 }) : {}, { store: o, snapshot: s, dialect: l } = await G(), u = e.includes("--assets") || a.assets === !0, d = u ? S({}) : void 0, f = B(e, "--compression") ?? a.compression, p = B(e, "--consistency") === "snapshot" ? "snapshot" : a.consistency ?? "live", m = a.masking, h = a.subset, g = a.scope ?? Re(e, s.tables.map((e) => e.name)), _ = m ? await ze() : void 0, v = g.kind === "tenant" ? await Ve() : void 0, b = v ? { tenantTables: v } : {};
474
486
  if (e.includes("--dry-run")) {
475
487
  let e = g.kind === "tables" && g.tables ? [...g.tables] : s.tables.map((e) => e.name);
476
488
  if (I.info("export DRY RUN — nothing was written", {
@@ -482,7 +494,7 @@ var ye = /* @__PURE__ */ new Set([
482
494
  assets: u,
483
495
  masking: m ? "profile policy applies" : "none"
484
496
  }), I.info(`would export: ${e.join(", ")}`), !m) return 0;
485
- let t = await v.runPromise(ue({
497
+ let t = await y.runPromise(de({
486
498
  store: o,
487
499
  snapshot: s,
488
500
  masking: m,
@@ -492,12 +504,12 @@ var ye = /* @__PURE__ */ new Set([
492
504
  ...b
493
505
  }
494
506
  }));
495
- return Ve(t), +(t.unclassified.length > 0);
507
+ return He(t), +(t.unclassified.length > 0);
496
508
  }
497
- if (K(r)) {
509
+ if (q(r)) {
498
510
  let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: i, join: a } = await import("node:path"), c = await n.mkdtemp(a(t.tmpdir(), "voltro-export-"));
499
511
  try {
500
- let t = await v.runPromise(E({
512
+ let t = await y.runPromise(D({
501
513
  store: o,
502
514
  snapshot: s,
503
515
  dialect: l,
@@ -511,14 +523,14 @@ var ye = /* @__PURE__ */ new Set([
511
523
  ...m ? { masking: m } : {},
512
524
  ..._ ? { classification: _ } : {},
513
525
  onProgress: R
514
- })), n = le({
526
+ })), n = ue({
515
527
  dir: c,
516
- ...d ? { blobs: he(d, () => G(o)) } : {}
528
+ ...d ? { blobs: he(d, () => K(o)) } : {}
517
529
  }), a = Fe(e);
518
530
  if (a) {
519
- let t = q(e);
531
+ let t = J(e);
520
532
  if (!t) return I.error("--encrypt requires --passphrase <p> or VOLTRO_BUNDLE_KEY (a dedicated key, NOT the transfer secret)"), 1;
521
- n = ce(n, t);
533
+ n = le(n, t);
522
534
  }
523
535
  return await Ie(n, i(process.cwd(), r)), I.info("export complete (single file)", {
524
536
  file: r,
@@ -534,7 +546,7 @@ var ye = /* @__PURE__ */ new Set([
534
546
  });
535
547
  }
536
548
  }
537
- let x = await v.runPromise(E({
549
+ let x = await y.runPromise(D({
538
550
  store: o,
539
551
  snapshot: s,
540
552
  dialect: l,
@@ -547,7 +559,7 @@ var ye = /* @__PURE__ */ new Set([
547
559
  ...f ? { compression: f } : {},
548
560
  ...m ? { masking: m } : {},
549
561
  ..._ ? { classification: _ } : {},
550
- ...d ? { assets: pe(d, () => G(o)) } : {},
562
+ ...d ? { assets: pe(d, () => K(o)) } : {},
551
563
  onProgress: R
552
564
  }));
553
565
  return I.info("export complete", {
@@ -558,13 +570,13 @@ var ye = /* @__PURE__ */ new Set([
558
570
  compression: x.compression,
559
571
  ...x.masking ? { maskedColumns: x.masking.transformed.length } : {}
560
572
  }), 0;
561
- }, qe = () => "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.", Je = (e) => {
562
- let t = z(e, "--tables");
573
+ }, Je = () => "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.", Ye = (e) => {
574
+ let t = B(e, "--tables");
563
575
  return {
564
576
  ...t ? { tables: t.split(",").map((e) => e.trim()).filter(Boolean) } : {},
565
577
  ...e.includes("--dry-run") ? { dryRun: !0 } : {}
566
578
  };
567
- }, Q = (e) => ({
579
+ }, Xe = (e) => ({
568
580
  tables: e.tablesWritten,
569
581
  rows: e.rowsWritten,
570
582
  carried: e.manifest.tables.reduce((e, t) => e + t.rowCount, 0),
@@ -572,9 +584,9 @@ var ye = /* @__PURE__ */ new Set([
572
584
  skippedTables: e.tablesSkipped,
573
585
  skippedRows: e.rowsSkipped
574
586
  } : {}
575
- }), Ye = (e, t) => {
587
+ }), Ze = (e, t) => {
576
588
  if (e.includes("--dry-run")) {
577
- I.info("import DRY RUN — nothing was written", t), I.info(qe());
589
+ I.info("import DRY RUN — nothing was written", t), I.info(Je());
578
590
  return;
579
591
  }
580
592
  if (t.rows === 0 && typeof t.skippedRows == "number" && t.skippedRows !== 0) {
@@ -582,8 +594,8 @@ var ye = /* @__PURE__ */ new Set([
582
594
  return;
583
595
  }
584
596
  I.info("import complete", t);
585
- }, Xe = Symbol("refused"), Ze = (e) => {
586
- let t = ie(e) ?? e;
597
+ }, Qe = Symbol("refused"), $e = (e) => {
598
+ let t = ae(e) ?? e;
587
599
  if (t?._tag !== "RowsRefusedError" || !Array.isArray(t.rows)) return !1;
588
600
  let n = t.rows, r = n.filter((e) => !e.derived), i = Number(t.totalCount ?? n.length), a = Number(t.primaryCount ?? r.length);
589
601
  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.`);
@@ -593,7 +605,7 @@ var ye = /* @__PURE__ */ new Set([
593
605
  n.length < i && I.error(` (the list above is capped at ${n.length} of ${i} — the counts by table are complete)`);
594
606
  let s = i - a;
595
607
  return s > 0 && I.error(` … and ${s} row(s) behind them. Fix the ${a} above and re-run; they resolve with their parents.`), !0;
596
- }, Qe = (e) => {
608
+ }, et = (e) => {
597
609
  let t = typeof e == "string" ? e : e.toString("utf8"), n = t;
598
610
  try {
599
611
  let e = JSON.parse(t), r = e.error ?? e.message;
@@ -633,22 +645,22 @@ var ye = /* @__PURE__ */ new Set([
633
645
  return;
634
646
  }
635
647
  }
636
- }, $e = async (e) => {
637
- let t = z(e, "--target") === "api", n = z(e, "--bundle-key"), i = B(e);
648
+ }, tt = async (e) => {
649
+ let t = B(e, "--target") === "api", n = B(e, "--bundle-key"), i = V(e);
638
650
  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;
639
- let a = z(e, "--mode") ?? "upsert", o = z(e, "--on-conflict") ?? "skip";
651
+ let a = B(e, "--mode") ?? "upsert", o = B(e, "--on-conflict") ?? "skip";
640
652
  if (t) {
641
- let t = z(e, "--api-url");
653
+ let t = B(e, "--api-url");
642
654
  if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
643
- let r = z(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
655
+ let r = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
644
656
  if (!r) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET) matching the instance secret"), 1;
645
- let s = z(e, "--tables"), c = z(e, "--rollback-key");
657
+ let s = B(e, "--tables"), c = B(e, "--rollback-key");
646
658
  a === "replace" && c === void 0 && I.warn("api replace: no rollback capture — this run empties the target first and nothing was asked to be kept.\n Pass --rollback-key <storage-key> and the INSTANCE stores the target's current rows in its own\n object storage before deleting them, which is the one place that survives the pod going away.");
647
659
  let l = {
648
660
  authorization: `Bearer ${r}`,
649
661
  "x-import-mode": a,
650
662
  "x-import-on-conflict": o,
651
- ...X(e, a) ? { "x-import-atomic": "1" } : {},
663
+ ...Z(e, a) ? { "x-import-atomic": "1" } : {},
652
664
  ...e.includes("--no-atomic") ? { "x-import-atomic": "0" } : {},
653
665
  ...c === void 0 ? {} : { "x-import-rollback-key": c },
654
666
  ...e.includes("--force") ? { "x-import-force": "1" } : {},
@@ -656,14 +668,26 @@ var ye = /* @__PURE__ */ new Set([
656
668
  ...s ? { "x-import-tables": s } : {}
657
669
  }, { resolve: u } = await import("node:path"), d;
658
670
  if (n) l["content-type"] = "application/json", d = JSON.stringify({ bundleKey: n });
659
- else if (K(i)) {
660
- let t = J(u(process.cwd(), i)), n = q(e);
671
+ else if (q(i)) {
672
+ let t = Y(u(process.cwd(), i)), n = J(e);
661
673
  n && (t = w(t, n)), l["content-type"] = "application/octet-stream", d = t;
662
- } else await (await import("node:fs/promises")).access(u(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."), l["content-type"] = "application/octet-stream", d = le({ dir: u(process.cwd(), i) });
663
- a === "replace" && !X(e, a) && I.warn(Ue(!0));
674
+ } else await (await import("node:fs/promises")).access(u(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."), l["content-type"] = "application/octet-stream", d = ue({ dir: u(process.cwd(), i) });
675
+ if (a === "replace") {
676
+ let r = await Le(i, n);
677
+ {
678
+ let i = r === null ? { bundleKey: n } : { tables: r }, a = await N(`${t.replace(/\/+$/, "")}/_voltro/admin/import`, JSON.stringify(i), {
679
+ ...l,
680
+ "content-type": "application/json",
681
+ "x-import-preflight": "1"
682
+ }, Q(e)).catch(() => null), o = a !== null && a.status === 200 ? JSON.parse(a.body.toString("utf8")) : null;
683
+ o === null ? I.warn("api replace: the instance did not answer the preflight (an older build does not have it). The run still works; the decision will only be visible in the pod log.") : o.staged === !0 ? I.info(`api replace: the instance WILL stage — ${o.stagedTables ?? 0} table(s) load into copies and the\n target keeps its rows until one short swap at the end. Interrupting the load leaves the target intact.`) : I.warn(`api replace: the instance will NOT stage — it empties the target first and refills it, so an
684
+ interrupted run leaves the target empty.\n Reason: ${o.reason ?? "not stated"}`);
685
+ }
686
+ }
687
+ a === "replace" && !Z(e, a) && I.warn(We(!0));
664
688
  let f = `${t.replace(/\/+$/, "")}/_voltro/admin/import`, p;
665
689
  try {
666
- let t = typeof d == "string" ? null : await He({
690
+ let t = typeof d == "string" ? null : await Ue({
667
691
  stream: d,
668
692
  url: f,
669
693
  headers: l,
@@ -671,51 +695,53 @@ var ye = /* @__PURE__ */ new Set([
671
695
  args: e,
672
696
  log: I
673
697
  });
674
- (t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), p = t === null ? await M(f, d, l, Z(e)) : t.kind === "sent" ? t.res : await M(f, t.body, l, Z(e));
698
+ (t === null || t.kind === "small") && I.info("api import: uploading the bundle — this request stays open until the instance has imported it"), p = t === null ? await N(f, d, l, Q(e)) : t.kind === "sent" ? t.res : await N(f, t.body, l, Q(e));
675
699
  } catch (e) {
676
- if (e instanceof j) return I.error(`api import: ${xe(e, a)}`), 1;
677
- if (e instanceof A) return I.error(`api import: ${be(e)}`), 1;
700
+ if (e instanceof M) return I.error(`api import: ${xe(e, a)}`), 1;
701
+ if (e instanceof j) return I.error(`api import: ${be(e)}`), 1;
678
702
  throw e;
679
703
  }
680
- if (p.status < 200 || p.status >= 300) return Ze(Qe(p.body)) || I.error(`api import failed: HTTP ${p.status} — ${p.body.toString("utf8")}`), 1;
704
+ if (p.status < 200 || p.status >= 300) return $e(et(p.body)) || I.error(`api import failed: HTTP ${p.status} — ${p.body.toString("utf8")}`), 1;
681
705
  let m = JSON.parse(p.body.toString("utf8"));
682
706
  return e.includes("--dry-run") ? (I.info("api import DRY RUN — nothing was written", {
683
707
  via: t,
684
708
  ...m
685
- }), I.info(qe()), 0) : (I.info("api import complete (imported by the running instance)", {
709
+ }), I.info(Je()), 0) : (I.info("api import complete (imported by the running instance)", {
686
710
  via: t,
687
711
  ...m
688
712
  }), 0);
689
713
  }
690
714
  if (!i) return I.error("a <bundleDir> is required for a direct import"), 1;
691
- if (!await Me(e)) return 1;
692
- a === "replace" && e.includes("--allow-live") && !X(e, a) && I.warn(Ue(!1));
693
- let { store: s, snapshot: c } = await W(), l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, f = V() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
694
- if (K(i)) {
715
+ if (!await z(e)) return 1;
716
+ a === "replace" && e.includes("--allow-live") && !Z(e, a) && I.warn(We(!1));
717
+ let { store: s, snapshot: c } = await G();
718
+ c.tables.some((e) => e.name === m) || I.warn(`this target has no \`${m}\` table, so this import will not be recorded.\n The import itself is unaffected. \`voltro db apply\` creates it.`), c.tables.some((e) => e.name === m) || I.warn(`this target has no \`${m}\` table, so this import will not be recorded.\n The import itself is unaffected. \`voltro db apply\` creates it.`);
719
+ let l = e.includes("--force"), u = (e) => I.warn(e), d = e.includes("--assets") ? S({}) : void 0, f = H() === "postgres" ? (await import("@voltro/sql-postgres")).makePgCopySession(r()) : void 0;
720
+ if (q(i)) {
695
721
  let t = await import("node:os"), n = await import("node:fs/promises"), { resolve: r, join: p } = await import("node:path"), m = await n.mkdtemp(p(t.tmpdir(), "voltro-import-"));
696
722
  try {
697
- let t = J(r(process.cwd(), i)), n = q(e);
723
+ let t = Y(r(process.cwd(), i)), n = J(e);
698
724
  return n && (t = w(t, n)), await ge(t, {
699
725
  dir: m,
700
726
  blobs: me(d ?? S({}))
701
- }), Ye(e, {
727
+ }), Ze(e, {
702
728
  file: i,
703
- ...Q(await v.runPromise(D({
729
+ ...Xe(await y.runPromise(O({
704
730
  store: s,
705
731
  bundleDir: m,
706
732
  mode: a,
707
733
  onConflict: o,
708
- atomic: X(e, a),
709
- ...Ge(e, a, i),
734
+ atomic: Z(e, a),
735
+ ...Ke(e, a, i),
710
736
  verify: !e.includes("--no-verify"),
711
- targetDialect: V(),
737
+ targetDialect: H(),
712
738
  allowIncompatible: l,
713
739
  targetSnapshot: c,
714
740
  force: l,
715
741
  onWarn: u,
716
742
  onProgress: R,
717
743
  assetsAlreadyRestored: !0,
718
- ...Je(e),
744
+ ...Ye(e),
719
745
  ...f ? { copyLoader: f } : {}
720
746
  })))
721
747
  }), 0;
@@ -726,33 +752,33 @@ var ye = /* @__PURE__ */ new Set([
726
752
  });
727
753
  }
728
754
  }
729
- let p = await v.runPromise(D({
730
- ...Ge(e, a, i),
755
+ let p = await y.runPromise(O({
756
+ ...Ke(e, a, i),
731
757
  store: s,
732
758
  bundleDir: i,
733
759
  mode: a,
734
760
  onConflict: o,
735
- atomic: X(e, a),
761
+ atomic: Z(e, a),
736
762
  verify: !e.includes("--no-verify"),
737
- targetDialect: V(),
763
+ targetDialect: H(),
738
764
  allowIncompatible: l,
739
765
  targetSnapshot: c,
740
766
  force: l,
741
767
  onWarn: u,
742
768
  onProgress: R,
743
- ...Je(e),
769
+ ...Ye(e),
744
770
  ...d ? { assets: fe(d) } : {},
745
771
  ...f ? { copyLoader: f } : {}
746
772
  })).catch((e) => {
747
- if (Ze(e)) return Xe;
773
+ if ($e(e)) return Qe;
748
774
  throw e;
749
775
  }).finally(() => f?.close());
750
- return typeof p == "symbol" ? 1 : (Ye(e, {
776
+ return typeof p == "symbol" ? 1 : (Ze(e, {
751
777
  bundleDir: i,
752
- ...Q(p),
778
+ ...Xe(p),
753
779
  assets: p.manifest.assets.count
754
780
  }), 0);
755
- }, et = () => {
781
+ }, nt = () => {
756
782
  let e = r();
757
783
  return {
758
784
  ...e.url ? { url: e.url } : {},
@@ -762,60 +788,60 @@ var ye = /* @__PURE__ */ new Set([
762
788
  ...e.password ? { password: e.password } : {},
763
789
  ...e.database ? { database: e.database } : {}
764
790
  };
765
- }, tt = async (e) => {
766
- let t = B(e);
791
+ }, rt = async (e) => {
792
+ let t = V(e);
767
793
  if (!t) return I.error("usage: voltro data backup <outDir> (native pg_dump/mysqldump/… + consistent artifact)"), 1;
768
- let n = V(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
794
+ let n = H(), { mkdir: r, writeFile: i } = await import("node:fs/promises"), { join: a } = await import("node:path");
769
795
  await r(t, { recursive: !0 });
770
796
  let o = a(t, C(n));
771
- await v.runPromise(O(oe(n, et(), o)));
797
+ await y.runPromise(k(se(n, nt(), o)));
772
798
  let s = Ee({
773
799
  dialect: n,
774
800
  schemaFingerprint: await L(n),
775
- voltroVersion: await Ae(),
801
+ voltroVersion: await je(),
776
802
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
777
803
  });
778
- return await i(a(t, N), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
804
+ return await i(a(t, P), `${JSON.stringify(s, null, 2)}\n`, "utf8"), I.info("native backup complete", {
779
805
  dialect: n,
780
806
  artifact: o,
781
- stamp: N,
807
+ stamp: P,
782
808
  schemaFingerprint: s.schemaFingerprint ?? "<none>",
783
809
  assets: "NOT included — use `voltro data export --assets` for rows + blobs"
784
810
  }), 0;
785
- }, nt = async (e, t) => {
786
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = Oe({
787
- flagUrl: z(t, "--drill-url"),
811
+ }, it = async (e, t) => {
812
+ let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(e, C(n)), o = ke({
813
+ flagUrl: B(t, "--drill-url"),
788
814
  envUrl: process.env.DRILL_DB_URL,
789
815
  liveKeys: Pe()
790
816
  });
791
817
  if (!o.ok) return I.error(`restore --drill: ${o.error}`), 1;
792
- let s = await i(r(e, N), "utf8").then(P, () => null);
818
+ let s = await i(r(e, P), "utf8").then(De, () => null);
793
819
  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;
794
820
  I.info("restore drill: restoring into throwaway DB", {
795
821
  dialect: n,
796
822
  target: F(o.url)
797
- }), await v.runPromise(O(de(n, { url: o.url }, a)));
823
+ }), await y.runPromise(k(E(n, { url: o.url }, a)));
798
824
  let c = null, l = 0;
799
825
  try {
800
- let e = await H(n, { url: o.url });
801
- c = ee(e), l = e.tables.length;
826
+ let e = await U(n, { url: o.url });
827
+ c = te(e), l = e.tables.length;
802
828
  } catch (e) {
803
829
  return I.error(`restore --drill: could not introspect the restored DB — ${String(e)}`), 1;
804
830
  }
805
- let u = ke({
831
+ let u = Ae({
806
832
  stampFingerprint: s?.schemaFingerprint ?? null,
807
833
  restoredFingerprint: c,
808
834
  restoredTableCount: l
809
835
  });
810
836
  for (let e of u.lines) u.pass ? I.info(`restore drill: ${e}`) : I.error(`restore drill: ${e}`);
811
837
  return +!u.pass;
812
- }, rt = async (e) => {
813
- let t = B(e);
838
+ }, at = async (e) => {
839
+ let t = V(e);
814
840
  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;
815
- if (e.includes("--drill")) return nt(t, e);
816
- if (!await Me(e)) return 1;
817
- let n = V(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = De({
818
- stamp: await i(r(t, N), "utf8").then(P, () => null),
841
+ if (e.includes("--drill")) return it(t, e);
842
+ if (!await z(e)) return 1;
843
+ let n = H(), { join: r } = await import("node:path"), { readFile: i } = await import("node:fs/promises"), a = r(t, C(n)), o = Oe({
844
+ stamp: await i(r(t, P), "utf8").then(De, () => null),
819
845
  currentDialect: n,
820
846
  currentFingerprint: await L(n)
821
847
  });
@@ -824,12 +850,12 @@ var ye = /* @__PURE__ */ new Set([
824
850
  if (!e.includes("--force")) return I.error(`restore refused: ${o.refuse} (pass --force to override)`), 1;
825
851
  I.warn(`restore proceeding despite: ${o.refuse} (--force)`);
826
852
  }
827
- return await v.runPromise(O(de(n, et(), a))), I.info("native restore complete", {
853
+ return await y.runPromise(k(E(n, nt(), a))), I.info("native restore complete", {
828
854
  dialect: n,
829
855
  artifact: a
830
856
  }), 0;
831
- }, it = async (e) => {
832
- let t = B(e);
857
+ }, ot = async (e) => {
858
+ let t = V(e);
833
859
  if (!t) return I.error("usage: voltro data inspect <file.vbundle|dir> [--json] [--passphrase <p>]"), 1;
834
860
  let { resolve: n, join: r } = await import("node:path"), i = await import("node:fs/promises"), a = n(process.cwd(), t);
835
861
  if ((await i.stat(a).catch(() => void 0))?.isDirectory()) {
@@ -841,8 +867,8 @@ var ye = /* @__PURE__ */ new Set([
841
867
  let s = await i.readFile(n, "utf8").catch(() => void 0);
842
868
  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);
843
869
  }
844
- let o = q(e), s = () => {
845
- let e = J(a);
870
+ let o = J(e), s = () => {
871
+ let e = Y(a);
846
872
  return o ? w(e, o) : e;
847
873
  };
848
874
  if (e.includes("--json")) {
@@ -853,8 +879,8 @@ var ye = /* @__PURE__ */ new Set([
853
879
  if (c) return console.log(c.toString("utf8")), 0;
854
880
  let l = (await T(s(), ["manifest.json"])).get("manifest.json");
855
881
  return l ? (console.log(l.toString("utf8")), 0) : (I.error("bundle has no README.md or manifest.json"), 1);
856
- }, at = async (e) => {
857
- let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-DNJ7MKph.js"), { store: r } = await W(), i = await n(r);
882
+ }, st = async (e) => {
883
+ let { REPLACE_IN_PROGRESS_TABLE: t } = await import("@voltro/database"), { interruptedReplaceAtBoot: n } = await import("./interruptedReplace-DNJ7MKph.js"), { store: r } = await G(), i = await n(r);
858
884
  if (i === void 0) return I.info("no interrupted replace is recorded — nothing to clear"), 0;
859
885
  if (!e.includes("--yes")) return I.error(`${i}\n\n Re-run with --yes to clear it.`), 1;
860
886
  let a = await r.query({
@@ -867,16 +893,59 @@ var ye = /* @__PURE__ */ new Set([
867
893
  });
868
894
  for (let e of a) await r.delete(t, String(e.id));
869
895
  return I.info(`cleared ${a.length} interrupted-replace marker(s) — the next boot will start`), 0;
870
- }, ot = async (e) => {
871
- let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await W(), a = V();
896
+ }, ct = async (e) => {
897
+ let { STAGING_PREFIX: t, dropStagingSql: n, execStatements: r } = await import("@voltro/data-transfer"), { store: i } = await G(), a = H();
872
898
  if (a !== "postgres" && a !== "mysql" && a !== "mariadb" && a !== "sqlite" && a !== "mssql") return I.error(`this store has no staging tables to clear (dialect: ${a})`), 1;
873
899
  let { introspectSchema: o } = await import("@voltro/database/sql"), s = (await i.run((await import("@effect/sql")).SqlClient.SqlClient.pipe((await import("effect")).Effect.flatMap(o)))).tables.map((e) => e.name).filter((e) => e.startsWith(t));
874
900
  return s.length === 0 ? (I.info("no staging tables are present — nothing to clear"), 0) : e.includes("--yes") ? (await r(i, s.map((e) => n(e.slice(t.length), a))), I.info(`dropped ${s.length} staging table(s)`), 0) : (I.error(`${s.length} staging table(s) from an earlier \`--mode replace\`:\n` + s.map((e) => ` ${e}`).join("\n") + "\n\n Each holds a copy of what a run was loading. Nothing else removes them.\n Make sure no import is running right now — this cannot tell a leftover from one in flight.\n Re-run with --yes to drop them."), 1);
875
- }, st = async (e) => {
876
- let t = B(e), n = B(e.filter((e) => e !== t));
901
+ }, lt = async (e) => {
902
+ let { describeImportRun: t, IMPORT_RUNS_TABLE: n } = await import("@voltro/database"), r = Number(B(e, "--limit") ?? "20"), i = Number.isFinite(r) ? Math.max(1, Math.min(200, Math.trunc(r))) : 20;
903
+ if (B(e, "--target") === "api") {
904
+ let t = B(e, "--api-url");
905
+ if (!t) return I.error("--target api requires --api-url <url> (the running instance)"), 1;
906
+ let n = B(e, "--token") ?? process.env.VOLTRO_DATA_TRANSFER_SECRET;
907
+ if (!n) return I.error("--target api requires --token <secret> (or VOLTRO_DATA_TRANSFER_SECRET)"), 1;
908
+ let r = await fetch(`${t.replace(/\/+$/, "")}/_voltro/admin/imports?limit=${i}`, { headers: { authorization: `Bearer ${n}` } }), a = await r.text();
909
+ if (r.status !== 200) return I.error(`api imports: ${r.status} ${a}`), 1;
910
+ let o = JSON.parse(a);
911
+ if (e.includes("--json")) return console.log(a), 0;
912
+ let s = o.runs ?? [];
913
+ if (s.length === 0) return I.info("no imports recorded on this instance"), 0;
914
+ (o.running ?? 0) > 0 && I.info(`${o.running} import(s) IN FLIGHT — re-run this to watch the counters move`);
915
+ for (let e of s) I.info(` ${e.line ?? ""}`);
916
+ return 0;
917
+ }
918
+ let { store: a } = await G(), o;
919
+ try {
920
+ o = await a.query({
921
+ table: n,
922
+ order: [{
923
+ column: "startedAt",
924
+ direction: "desc"
925
+ }],
926
+ predicate: void 0,
927
+ projection: void 0,
928
+ skip: 0,
929
+ take: i
930
+ });
931
+ } catch {
932
+ return I.error(`this target has no \`${n}\` table, so no import has been recorded here.\n Run \`voltro db apply\`; imports themselves are unaffected by its absence.`), 1;
933
+ }
934
+ if (o.length === 0) return I.info("no imports recorded against this target"), 0;
935
+ for (let e of o) {
936
+ let n = e;
937
+ I.info(` ${t({
938
+ ...e,
939
+ startedAt: new Date(n.startedAt),
940
+ finishedAt: n.finishedAt === null ? null : new Date(n.finishedAt)
941
+ })}`);
942
+ }
943
+ return 0;
944
+ }, ut = async (e) => {
945
+ let t = V(e), n = V(e.filter((e) => e !== t));
877
946
  if (!t || !n) return I.error("usage: voltro data unpack <file.vbundle> <dir> [--passphrase <p>]"), 1;
878
- let { resolve: r } = await import("node:path"), i = J(r(process.cwd(), t)), a = q(e);
879
- return a && (i = w(i, a)), await k(i, r(process.cwd(), n)), I.info("unpack complete", {
947
+ let { resolve: r } = await import("node:path"), i = Y(r(process.cwd(), t)), a = J(e);
948
+ return a && (i = w(i, a)), await A(i, r(process.cwd(), n)), I.info("unpack complete", {
880
949
  file: t,
881
950
  dir: n
882
951
  }), 0;
@@ -885,6 +954,7 @@ var ye = /* @__PURE__ */ new Set([
885
954
  common: [
886
955
  "--target",
887
956
  "--tables",
957
+ "--exclude",
888
958
  "--profile",
889
959
  "--tenant",
890
960
  "--assets",
@@ -893,7 +963,6 @@ var ye = /* @__PURE__ */ new Set([
893
963
  "--json"
894
964
  ],
895
965
  directOnly: [
896
- "--exclude",
897
966
  "--passphrase",
898
967
  "--dry-run",
899
968
  "--encrypt"
@@ -906,6 +975,14 @@ var ye = /* @__PURE__ */ new Set([
906
975
  ]
907
976
  },
908
977
  "clear-staging": { common: ["--yes"] },
978
+ imports: {
979
+ common: [
980
+ "--target",
981
+ "--limit",
982
+ "--json"
983
+ ],
984
+ apiOnly: ["--api-url", "--token"]
985
+ },
909
986
  "clear-replace-marker": { common: ["--yes"] },
910
987
  import: {
911
988
  common: [
@@ -947,13 +1024,13 @@ var ye = /* @__PURE__ */ new Set([
947
1024
  "--drill-url",
948
1025
  "--api-url"
949
1026
  ] }
950
- }, ct = (e) => e.split("=")[0], lt = (e, t) => {
1027
+ }, dt = (e) => e.split("=")[0], ft = (e, t) => {
951
1028
  let n = $[e];
952
1029
  if (!n) return;
953
- 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 ?? []);
1030
+ 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 ?? []);
954
1031
  for (let n of t) {
955
1032
  if (!n.startsWith("--")) continue;
956
- let t = ct(n);
1033
+ let t = dt(n);
957
1034
  if (i.has(t)) continue;
958
1035
  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.`;
959
1036
  let o = Object.entries($).filter(([n, r]) => n !== e && [
@@ -963,13 +1040,13 @@ var ye = /* @__PURE__ */ new Set([
963
1040
  ].includes(t)).map(([e]) => e);
964
1041
  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.`;
965
1042
  }
966
- }, ut = async (e) => {
1043
+ }, pt = async (e) => {
967
1044
  let [t, ...n] = e;
968
1045
  if (t !== void 0 && $[t] !== void 0) {
969
- let e = lt(t, n);
1046
+ let e = ft(t, n);
970
1047
  if (e) return I.error(e), 2;
971
1048
  }
972
- return t === "export" ? Ke(n) : t === "import" ? $e(n) : t === "unpack" ? st(n) : t === "inspect" ? it(n) : t === "backup" ? tt(n) : t === "restore" ? rt(n) : t === "clear-replace-marker" ? at(n) : t === "clear-staging" ? ot(n) : (I.error("usage: voltro data <export|import|unpack|inspect|backup|restore|clear-replace-marker|clear-staging> … (logical: export/import; native: backup/restore)"), t ? 2 : 1);
1049
+ return t === "export" ? qe(n) : t === "import" ? tt(n) : t === "unpack" ? ut(n) : t === "inspect" ? ot(n) : t === "backup" ? rt(n) : t === "restore" ? at(n) : t === "clear-replace-marker" ? st(n) : t === "clear-staging" ? ct(n) : t === "imports" ? lt(n) : (I.error("usage: voltro data <export|import|imports|unpack|inspect|backup|restore|clear-replace-marker|clear-staging> … (logical: export/import; native: backup/restore; `imports` reads the history, incl. a run in flight)"), t ? 2 : 1);
973
1050
  };
974
1051
  //#endregion
975
- export { ut as runDataCommand };
1052
+ export { pt as runDataCommand };