@voltro/data-transfer 0.86.0 → 0.88.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 (43) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/THIRD-PARTY-NOTICES.md +2503 -690
  3. package/dist/assetAdmission.js +91 -0
  4. package/dist/assets.js +62 -42
  5. package/dist/blobStorage.js +55 -32
  6. package/dist/bundleArchive.js +249 -105
  7. package/dist/bundleCrypto.js +1 -2
  8. package/dist/bundlePaths.js +3 -0
  9. package/dist/casAssetPhase.js +78 -35
  10. package/dist/compress.js +4 -5
  11. package/dist/consistency.js +1 -2
  12. package/dist/crossDialect.js +1 -2
  13. package/dist/driftClassification.js +1 -2
  14. package/dist/environmentLocal.js +267 -4
  15. package/dist/errors.js +13 -7
  16. package/dist/exporter.js +146 -133
  17. package/dist/filesystemAdmission.js +32 -0
  18. package/dist/importer.js +326 -287
  19. package/dist/index.d.ts +200 -46
  20. package/dist/index.js +28 -26
  21. package/dist/ledger.js +1 -2
  22. package/dist/liveGuard.js +1 -2
  23. package/dist/manifest.js +45 -42
  24. package/dist/masking.js +1 -2
  25. package/dist/maskingPreview.js +1 -2
  26. package/dist/nativeDump.js +134 -86
  27. package/dist/ndjson.js +1 -2
  28. package/dist/physicalRequest.js +9 -0
  29. package/dist/profile.js +1 -2
  30. package/dist/progress.js +1 -2
  31. package/dist/readme.js +1 -2
  32. package/dist/replacePreflight.js +1 -2
  33. package/dist/rollbackCapture.js +1 -2
  34. package/dist/rowFailureReason.js +1 -2
  35. package/dist/schemaDrift.js +1 -2
  36. package/dist/scope.js +1 -2
  37. package/dist/sealedKeys.js +39 -0
  38. package/dist/stagedSwap.js +1 -2
  39. package/dist/storageAssetMetadata.js +46 -0
  40. package/dist/subset.js +1 -2
  41. package/dist/topo.js +1 -2
  42. package/dist/typedCodec.js +1 -2
  43. package/package.json +4 -4
package/dist/manifest.js CHANGED
@@ -1,52 +1,55 @@
1
- import { Schema as e } from "effect";
2
- //#region src/manifest.ts
3
- var t = 1, n = e.Struct({
4
- name: e.String,
5
- file: e.String,
6
- rowCount: e.Number,
7
- checksum: e.String,
8
- columnTypes: e.Record(e.String, e.String),
9
- primaryKey: e.String
10
- }), r = e.Struct({
11
- kind: e.Literals([
1
+ import { isSafeBundlePath as e } from "./bundlePaths.js";
2
+ import { Schema as t } from "effect";
3
+ import { StorageAddress as n } from "@voltro/plugin-storage/errors";
4
+ // src/manifest.ts
5
+ var r = 2, i = t.Struct({
6
+ name: t.String,
7
+ file: t.String,
8
+ rowCount: t.Number,
9
+ checksum: t.String,
10
+ columnTypes: t.Record(t.String, t.String),
11
+ primaryKey: t.String
12
+ }), a = t.Struct({
13
+ kind: t.Literals([
12
14
  "all",
13
15
  "tenant",
14
16
  "tables"
15
17
  ]),
16
- tenantId: e.optional(e.String),
17
- tables: e.optional(e.Array(e.String)),
18
- exclude: e.optional(e.Array(e.String))
19
- }), i = e.Struct({
20
- count: e.Number,
21
- totalBytes: e.Number,
22
- index: e.optional(e.String)
23
- });
24
- e.Struct({
25
- key: e.String,
26
- sha256: e.String,
27
- size: e.Number,
28
- contentType: e.String
29
- });
30
- var a = e.Struct({
31
- formatVersion: e.Number,
32
- createdAt: e.String,
33
- source: e.Struct({
34
- dialect: e.String,
35
- schemaFingerprint: e.String
18
+ tenantId: t.optional(t.String),
19
+ tables: t.optional(t.Array(t.String)),
20
+ exclude: t.optional(t.Array(t.String))
21
+ }), o = t.Struct({
22
+ count: t.Number,
23
+ totalBytes: t.Number,
24
+ index: t.optional(t.String.pipe(t.check(t.makeFilter(e))))
25
+ }), s = n, c = t.Struct({
26
+ ...s.fields,
27
+ sha256: t.String.pipe(t.check(t.isPattern(/^[a-f0-9]{64}$/))),
28
+ size: t.Number.pipe(t.check(t.isInt()), t.check(t.isGreaterThanOrEqualTo(0)), t.check(t.isLessThanOrEqualTo(2 ** 53 - 1))),
29
+ contentType: t.String.pipe(t.check(t.isMinLength(1)), t.check(t.makeFilter((e) => !/[\uD800-\uDFFF]/u.test(e))))
30
+ }), l = t.Struct({
31
+ formatVersion: t.Number,
32
+ createdAt: t.String,
33
+ source: t.Struct({
34
+ dialect: t.String,
35
+ schemaFingerprint: t.String
36
36
  }),
37
- scope: r,
38
- consistency: e.Literals(["live", "snapshot"]),
39
- compression: e.Literals([
37
+ scope: a,
38
+ consistency: t.Literals(["live", "snapshot"]),
39
+ compression: t.Literals([
40
40
  "zstd",
41
41
  "gzip",
42
42
  "none"
43
43
  ]),
44
- tables: e.Array(n),
45
- assets: i,
46
- masking: e.optional(e.Struct({
47
- policyId: e.optional(e.String),
48
- transformed: e.Array(e.String)
44
+ tables: t.Array(i),
45
+ assets: o,
46
+ fieldKeys: t.optional(t.Struct({
47
+ keyIds: t.Array(t.String),
48
+ unnamed: t.Number
49
+ })),
50
+ masking: t.optional(t.Struct({
51
+ policyId: t.optional(t.String),
52
+ transformed: t.Array(t.String)
49
53
  }))
50
- }), o = e.encodeSync(e.fromJsonString(a)), s = e.decodeUnknownSync(e.fromJsonString(a)), c = "manifest.json", l = "README.md", u = "data", d = "assets", f = "assets/index.ndjson", p = ".ledger.json";
51
- //#endregion
52
- export { d as ASSETS_DIR, f as ASSET_INDEX_FILE, i as AssetSummary, u as DATA_DIR, r as ExportScope, t as FORMAT_VERSION, p as LEDGER_FILE, c as MANIFEST_FILE, a as Manifest, l as README_FILE, n as TableEntry, s as decodeManifest, o as encodeManifest };
54
+ }), u = t.encodeSync(t.fromJsonString(l)), d = t.decodeUnknownSync(t.fromJsonString(l)), f = "manifest.json", p = "README.md", m = "data", h = "assets", g = "assets/index.ndjson", _ = ".ledger.json";
55
+ export { h as ASSETS_DIR, g as ASSET_INDEX_FILE, s as AssetAddress, c as AssetEntry, o as AssetSummary, m as DATA_DIR, a as ExportScope, r as FORMAT_VERSION, _ as LEDGER_FILE, f as MANIFEST_FILE, l as Manifest, p as README_FILE, i as TableEntry, d as decodeManifest, u as encodeManifest };
package/dist/masking.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHmac as e } from "node:crypto";
2
- //#region src/masking.ts
2
+ // src/masking.ts
3
3
  var t = {
4
4
  email: { fake: "email" },
5
5
  fullName: { fake: "fullName" },
@@ -242,5 +242,4 @@ var t = {
242
242
  });
243
243
  return a;
244
244
  };
245
- //#endregion
246
245
  export { t as DEFAULT_CLASS_ACTIONS, h as applyAction, n as classificationFromSnapshot, v as isMaskAction, S as maskRow, y as planMasking, r as resolveAction, x as scanForLeaks };
@@ -3,7 +3,7 @@ import { resolveScope as i } from "./scope.js";
3
3
  import { topoSortTables as a } from "./topo.js";
4
4
  import { Effect as o, Stream as s } from "effect";
5
5
  import { streamTable as c } from "@voltro/database";
6
- //#region src/maskingPreview.ts
6
+ // src/maskingPreview.ts
7
7
  var l = (e) => typeof e == "object" && e ? "fake" in e ? `fake:${e.fake}` : "custom" : String(e), u = (u) => o.gen(function* () {
8
8
  let d = u.scope ?? { kind: "all" }, f = yield* i(d, u.snapshot, {
9
9
  store: u.store,
@@ -44,5 +44,4 @@ var l = (e) => typeof e == "object" && e ? "fake" in e ? `fake:${e.fake}` : "cus
44
44
  unclassified: _.unclassified
45
45
  };
46
46
  });
47
- //#endregion
48
47
  export { u as previewMasking };
@@ -5,7 +5,7 @@ import { randomUUID as i } from "node:crypto";
5
5
  import { copyFile as a, createReadStream as o, createWriteStream as s, existsSync as c, rename as l, statSync as u, unlinkSync as d } from "node:fs";
6
6
  import { basename as f, delimiter as p, join as m } from "node:path";
7
7
  import { spawn as h } from "node:child_process";
8
- //#region src/nativeDump.ts
8
+ // src/nativeDump.ts
9
9
  var g = (e, t) => {
10
10
  let n = t === "dump" ? "mariadb-dump" : "mariadb", r = t === "dump" ? "mysqldump" : "mysql";
11
11
  return e === "mariadb" ? _(n) ? { tool: n } : {
@@ -104,12 +104,16 @@ var g = (e, t) => {
104
104
  stdoutFile: r
105
105
  };
106
106
  }
107
- case "sqlite":
108
- case "turso": return {
109
- kind: "copy",
107
+ case "sqlite": return {
108
+ kind: "sqlite-backup",
110
109
  from: y(n),
111
110
  to: r
112
111
  };
112
+ case "turso": return {
113
+ kind: "vacuum-into",
114
+ conn: n,
115
+ to: r
116
+ };
113
117
  case "mssql": return {
114
118
  kind: "spawn",
115
119
  tool: "sqlpackage",
@@ -273,97 +277,141 @@ END`;
273
277
  return t.gen(function* () {
274
278
  yield* a(`preparing ${n.scratch}`, i.prepare), yield* L(n.load).pipe(t.tapError(() => o)), yield* a(`carrying ${n.carry.join(", ")} into the imported copy`, i.carry).pipe(t.tapError(() => o)), yield* a(`swapping ${n.scratch} in as ${n.live}`, i.swap).pipe(t.tapError(() => a("putting the original database back", i.undoSwap).pipe(t.ignore)));
275
279
  });
276
- }, L = (n, r) => n.kind === "mssql-replace" ? I(n, r) : n.kind === "copy" ? t.callback((r) => {
277
- let o = `${n.to}.restoring-${i()}`, s = (n) => {
278
- try {
279
- d(o);
280
- } catch {}
281
- r(t.fail(new e({
282
- tool: "copy",
283
- reason: n
284
- })));
285
- };
286
- a(n.from, o, (e) => {
287
- if (e) s(`copy ${n.from} → ${n.to}: ${e.message}`);
288
- else {
289
- for (let e of [
290
- `${n.to}-wal`,
291
- `${n.to}-shm`,
292
- `${n.to}-journal`
293
- ]) try {
294
- d(e);
280
+ }, L = (n, r) => {
281
+ if (n.kind === "mssql-replace") return I(n, r);
282
+ if (n.kind === "vacuum-into") {
283
+ if (r === void 0) return t.fail(new e({
284
+ tool: "vacuum-into",
285
+ reason: "a VACUUM INTO backup needs a SQL connection to the database"
286
+ }));
287
+ let a = `${n.to}.backing-up-${i()}`;
288
+ return r(n.conn, [`VACUUM INTO '${a.replaceAll("'", "''")}'`]).pipe(t.flatMap(() => t.callback((e) => {
289
+ l(a, n.to, (n) => e(n ? t.fail(n) : t.void));
290
+ })), t.mapError((t) => {
291
+ try {
292
+ d(a);
295
293
  } catch {}
296
- l(o, n.to, (e) => {
297
- e ? s(`copy ${n.from} → ${n.to}: ${e.message}`) : r(t.void);
294
+ return new e({
295
+ tool: "vacuum-into",
296
+ reason: `VACUUM INTO ${n.to}: ${t instanceof Error ? t.message : String(t)}`
298
297
  });
299
- }
300
- });
301
- }) : t.callback((r) => {
302
- let i = h(n.tool, [...n.args], {
303
- env: {
304
- ...process.env,
305
- ...n.env
306
- },
307
- stdio: [
308
- "pipe",
309
- n.stdoutFile ? "pipe" : "inherit",
310
- "pipe"
311
- ]
312
- }), a = "";
313
- i.stderr?.on("data", (e) => {
314
- a += String(e);
315
- });
316
- let c = !1, l = (e) => {
317
- c || (c = !0, r(e));
318
- }, u, p = (e) => {
319
- u ??= e;
320
- };
321
- if (n.stdoutFile && i.stdout) {
322
- let e = s(n.stdoutFile);
323
- e.on("error", (e) => p(`writing ${f(n.stdoutFile)}: ${e.message}`)), i.stdout.on("error", (e) => p(`reading the tool's output: ${e.message}`)), i.stdout.pipe(e);
324
- }
325
- let m = 0;
326
- if (n.stdinFile && i.stdin) {
327
- let e = o(n.stdinFile);
328
- e.on("data", (e) => {
329
- m += e.length;
330
- }), e.on("error", (e) => {
331
- p(`reading ${f(n.stdinFile)}: ${e.message}`), i.stdin?.end();
332
- }), i.stdin.on("error", (e) => {
333
- e.code !== "EPIPE" && p(`feeding ${f(n.stdinFile)} to ${n.tool}: ${e.message}`);
334
- }), e.pipe(i.stdin);
298
+ }));
335
299
  }
336
- let g = () => {
337
- if (n.stdoutFile !== void 0) try {
338
- d(n.stdoutFile);
339
- } catch {}
340
- };
341
- i.on("error", (r) => {
342
- g(), l(t.fail(new e({
343
- tool: n.tool,
344
- reason: `failed to launch (is '${n.tool}' installed + on PATH?): ${r.message}`,
345
- ...n.hint === void 0 ? {} : { hint: n.hint }
346
- })));
347
- }), i.on("close", (r) => {
348
- if (r === 0 && u === void 0) {
349
- l(t.void);
350
- return;
300
+ return n.kind === "sqlite-backup" ? t.tryPromise({
301
+ try: async () => {
302
+ let { backup: e, DatabaseSync: t } = await import("node:sqlite"), r = `${n.to}.backing-up-${i()}`, a = new t(n.from, { readOnly: !0 });
303
+ try {
304
+ await e(a, r);
305
+ } catch (e) {
306
+ try {
307
+ d(r);
308
+ } catch {}
309
+ throw e;
310
+ } finally {
311
+ a.close();
312
+ }
313
+ await new Promise((e, t) => {
314
+ l(r, n.to, (n) => {
315
+ n ? t(n) : e();
316
+ });
317
+ });
318
+ },
319
+ catch: (t) => new e({
320
+ tool: "sqlite-backup",
321
+ reason: `backup ${n.from} → ${n.to}: ${t instanceof Error ? t.message : String(t)}`
322
+ })
323
+ }) : n.kind === "copy" ? t.callback((r) => {
324
+ let o = `${n.to}.restoring-${i()}`, s = (n) => {
325
+ try {
326
+ d(o);
327
+ } catch {}
328
+ r(t.fail(new e({
329
+ tool: "copy",
330
+ reason: n
331
+ })));
332
+ };
333
+ a(n.from, o, (e) => {
334
+ if (e) s(`copy ${n.from} → ${n.to}: ${e.message}`);
335
+ else {
336
+ for (let e of [
337
+ `${n.to}-wal`,
338
+ `${n.to}-shm`,
339
+ `${n.to}-journal`,
340
+ `${n.to}-log`
341
+ ]) try {
342
+ d(e);
343
+ } catch {}
344
+ l(o, n.to, (e) => {
345
+ e ? s(`copy ${n.from} → ${n.to}: ${e.message}`) : r(t.void);
346
+ });
347
+ }
348
+ });
349
+ }) : t.callback((r) => {
350
+ let i = h(n.tool, [...n.args], {
351
+ env: {
352
+ ...process.env,
353
+ ...n.env
354
+ },
355
+ stdio: [
356
+ "pipe",
357
+ n.stdoutFile ? "pipe" : "inherit",
358
+ "pipe"
359
+ ]
360
+ }), a = "";
361
+ i.stderr?.on("data", (e) => {
362
+ a += String(e);
363
+ });
364
+ let c = !1, l = (e) => {
365
+ c || (c = !0, r(e));
366
+ }, u, p = (e) => {
367
+ u ??= e;
368
+ };
369
+ if (n.stdoutFile && i.stdout) {
370
+ let e = s(n.stdoutFile);
371
+ e.on("error", (e) => p(`writing ${f(n.stdoutFile)}: ${e.message}`)), i.stdout.on("error", (e) => p(`reading the tool's output: ${e.message}`)), i.stdout.pipe(e);
372
+ }
373
+ let m = 0;
374
+ if (n.stdinFile && i.stdin) {
375
+ let e = o(n.stdinFile);
376
+ e.on("data", (e) => {
377
+ m += e.length;
378
+ }), e.on("error", (e) => {
379
+ p(`reading ${f(n.stdinFile)}: ${e.message}`), i.stdin?.end();
380
+ }), i.stdin.on("error", (e) => {
381
+ e.code !== "EPIPE" && p(`feeding ${f(n.stdinFile)} to ${n.tool}: ${e.message}`);
382
+ }), e.pipe(i.stdin);
351
383
  }
352
- g();
353
- let i = n.stdinFile === void 0 ? "" : ` — ${m} of ${R(n.stdinFile)} byte(s) of ${f(n.stdinFile)} had been fed to it`;
354
- l(t.fail(new e({
355
- tool: n.tool,
356
- reason: (r === 0 ? `exited cleanly, but ${u}` : `exited with code ${r}${u === void 0 ? "" : ` (${u})`}`) + i + (n.stdoutFile === void 0 ? "" : ` — the partial ${f(n.stdoutFile)} was removed, so it cannot be mistaken for a backup`),
357
- stderr: a.trim() === "" ? `(${n.tool} printed nothing to stderr)` : a.slice(-2e3),
358
- ...n.hint === void 0 ? {} : { hint: n.hint }
359
- })));
384
+ let g = () => {
385
+ if (n.stdoutFile !== void 0) try {
386
+ d(n.stdoutFile);
387
+ } catch {}
388
+ };
389
+ i.on("error", (r) => {
390
+ g(), l(t.fail(new e({
391
+ tool: n.tool,
392
+ reason: `failed to launch (is '${n.tool}' installed + on PATH?): ${r.message}`,
393
+ ...n.hint === void 0 ? {} : { hint: n.hint }
394
+ })));
395
+ }), i.on("close", (r) => {
396
+ if (r === 0 && u === void 0) {
397
+ l(t.void);
398
+ return;
399
+ }
400
+ g();
401
+ let i = n.stdinFile === void 0 ? "" : ` — ${m} of ${R(n.stdinFile)} byte(s) of ${f(n.stdinFile)} had been fed to it`;
402
+ l(t.fail(new e({
403
+ tool: n.tool,
404
+ reason: (r === 0 ? `exited cleanly, but ${u}` : `exited with code ${r}${u === void 0 ? "" : ` (${u})`}`) + i + (n.stdoutFile === void 0 ? "" : ` — the partial ${f(n.stdoutFile)} was removed, so it cannot be mistaken for a backup`),
405
+ stderr: a.trim() === "" ? `(${n.tool} printed nothing to stderr)` : a.slice(-2e3),
406
+ ...n.hint === void 0 ? {} : { hint: n.hint }
407
+ })));
408
+ });
360
409
  });
361
- }), R = (e) => {
410
+ }, R = (e) => {
362
411
  try {
363
412
  return String(u(e).size);
364
413
  } catch {
365
414
  return "?";
366
415
  }
367
416
  };
368
- //#endregion
369
417
  export { C as EXCLUDED_FROM_DUMP, v as backupArtifactName, S as backupCommand, T as dumpExcludedTables, P as mssqlReplaceStatements, A as mssqlScratchName, E as restoreCommand, L as runNativeStep };
package/dist/ndjson.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CodecError as e } from "./errors.js";
2
2
  import { decodeRow as t, encodeRow as n } from "./typedCodec.js";
3
3
  import { Effect as r, Stream as i } from "effect";
4
- //#region src/ndjson.ts
4
+ // src/ndjson.ts
5
5
  var a = new TextEncoder(), o = (e, t) => e.pipe(i.map((e) => a.encode(`${JSON.stringify(n(e, t))}\n`))), s = (n, a, o) => n.pipe(i.decodeText(), i.splitLines, i.filter((e) => e.trim().length > 0), i.mapEffect((n) => r.try({
6
6
  try: () => t(JSON.parse(n), o),
7
7
  catch: (t) => new e({
@@ -9,5 +9,4 @@ var a = new TextEncoder(), o = (e, t) => e.pipe(i.map((e) => a.encode(`${JSON.st
9
9
  reason: String(t?.message ?? t)
10
10
  })
11
11
  })));
12
- //#endregion
13
12
  export { s as ndjsonToRows, o as rowsToNdjson };
@@ -0,0 +1,9 @@
1
+ import { Effect as e } from "effect";
2
+ // src/physicalRequest.ts
3
+ var t = (t, n) => e.callback((r) => {
4
+ let i = new AbortController(), a = Promise.resolve().then(() => t(i.signal));
5
+ return a.then((t) => r(e.succeed(t)), (t) => r(e.fail(n(t)))), e.sync(() => {
6
+ i.abort();
7
+ }).pipe(e.andThen(e.promise(() => a.then(() => void 0, () => void 0))));
8
+ });
9
+ export { t as physicalRequest };
package/dist/profile.js CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/profile.ts
1
+ // src/profile.ts
2
2
  var e = (e) => e;
3
- //#endregion
4
3
  export { e as defineDataProfile };
package/dist/progress.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Effect as e, Metric as t } from "effect";
2
- //#region src/progress.ts
2
+ // src/progress.ts
3
3
  var n = t.counter("voltro_data_transfer_rows", { description: "Rows exported/imported by @voltro/data-transfer, tagged by phase + table." }), r = Array.from({ length: 16 }, (e, t) => .01 * 2 ** t), i = t.histogram("voltro_data_transfer_table_seconds", {
4
4
  boundaries: r,
5
5
  description: "Per-table export/import duration in seconds, tagged by phase."
@@ -13,5 +13,4 @@ var n = t.counter("voltro_data_transfer_rows", { description: "Rows exported/imp
13
13
  tableCount: s,
14
14
  phase: r
15
15
  } }));
16
- //#endregion
17
16
  export { a as reportProgress, o as withTableSpan };
package/dist/readme.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/readme.ts
1
+ // src/readme.ts
2
2
  var e = (e) => e.kind === "tenant" ? `tenant \`${e.tenantId}\`` : e.kind === "tables" ? `tables \`${(e.tables ?? []).join(", ")}\`` : "all tables", t = (t, n) => {
3
3
  let r = t.tables.reduce((e, t) => e + t.rowCount, 0), i = t.masking ? `✅ **Masked at the source** — ${t.masking.transformed.length} column(s) transformed${t.masking.policyId ? ` (policy \`${t.masking.policyId}\`)` : ""}. Personal data was replaced with realistic, referentially-consistent fakes before any byte was written.` : "⚠️ **NOT masked — this bundle contains REAL data (PII included).** Treat it as production data: do not copy it into a lower environment or share it without review. To produce a safe copy, re-export under a masking profile (`--profile`).", a = t.masking && t.masking.transformed.length > 0 ? `\n\nMasked columns: ${t.masking.transformed.map((e) => `\`${e}\``).join(", ")}.` : "", o = t.assets.count > 0 ? `${t.assets.count} object(s), ${t.assets.totalBytes} bytes` : "none", s = t.tables.map((e) => `| \`${e.name}\` | ${e.rowCount} |`).join("\n");
4
4
  return `# Voltro data export
@@ -39,5 +39,4 @@ voltro data import <this-directory> --target api --api-url <url> --token <secret
39
39
  \`\`\`
40
40
  `;
41
41
  };
42
- //#endregion
43
42
  export { t as buildReadme };
@@ -1,4 +1,4 @@
1
- //#region src/replacePreflight.ts
1
+ // src/replacePreflight.ts
2
2
  var e = (e, t) => {
3
3
  let n = new Set(t), r = [];
4
4
  for (let t of e.tables) if (!n.has(t.name)) for (let e of t.columns) {
@@ -19,5 +19,4 @@ var e = (e, t) => {
19
19
  });
20
20
  return `replace cannot empty this target: ${t.size} table(s) OUTSIDE the bundle hold rows that reference tables INSIDE it. Emptying the bundle's tables would leave those rows pointing at nothing, and the bundle cannot restore them.\n${n.join("\n")}\nNothing has been deleted. Either re-export with a scope that includes those tables, or use --mode upsert if those rows are meant to survive.`;
21
21
  };
22
- //#endregion
23
22
  export { t as outsideReferenceRefusal, e as outsideReferencesInto };
@@ -1,7 +1,7 @@
1
1
  import { BundleError as e } from "./errors.js";
2
2
  import { runExport as t } from "./exporter.js";
3
3
  import { Effect as n } from "effect";
4
- //#region src/rollbackCapture.ts
4
+ // src/rollbackCapture.ts
5
5
  var r = (r) => n.gen(function* () {
6
6
  if (r.mode !== "replace" || r.dir === void 0 || r.tables.length === 0) return;
7
7
  if (r.snapshot === void 0) return yield* n.fail(new e({
@@ -32,5 +32,4 @@ var r = (r) => n.gen(function* () {
32
32
  tables: a.tables.length
33
33
  };
34
34
  }), i = (e) => `rollback capture: ${e.rows} row(s) across ${e.tables} table(s) → ${e.dir}\n If this run does not finish, restore with: voltro data import ${e.dir} --mode replace`;
35
- //#endregion
36
35
  export { r as captureRollback, i as rollbackNotice };
@@ -1,5 +1,5 @@
1
1
  import { classifyConstraintViolation as e, extractDbCause as t } from "@voltro/database";
2
- //#region src/rowFailureReason.ts
2
+ // src/rowFailureReason.ts
3
3
  var n = (e) => {
4
4
  let t = e.constraint ?? e.column, n = t === void 0 ? "" : ` ${t}`;
5
5
  switch (e.kind) {
@@ -32,5 +32,4 @@ var n = (e) => {
32
32
  }
33
33
  return s(n);
34
34
  };
35
- //#endregion
36
35
  export { o as rowFailureDiagnosis, s as rowFailureReason, c as tableFailureReason };
@@ -1,5 +1,5 @@
1
1
  import { fingerprintSchema as e } from "@voltro/database/sql";
2
- //#region src/schemaDrift.ts
2
+ // src/schemaDrift.ts
3
3
  var t = /__dropped_\d+$/, n = (e, n) => {
4
4
  let r = [], i = /* @__PURE__ */ new Map();
5
5
  for (let e of n.tables) {
@@ -36,5 +36,4 @@ var t = /__dropped_\d+$/, n = (e, n) => {
36
36
  diff: o
37
37
  };
38
38
  };
39
- //#endregion
40
39
  export { r as checkSchemaDrift, n as diffSnapshots };
package/dist/scope.js CHANGED
@@ -3,7 +3,7 @@ import { ScopeError as t } from "./errors.js";
3
3
  import { computeSubsetIds as n, pkOf as r } from "./subset.js";
4
4
  import { Effect as i } from "effect";
5
5
  import { eq as a, inSet as o } from "@voltro/database";
6
- //#region src/scope.ts
6
+ // src/scope.ts
7
7
  var s = "tenantId", c = (c, l, u) => i.gen(function* () {
8
8
  let d = l.tables.map((e) => e.name), f = new Map(l.tables.map((e) => [e.name, e]));
9
9
  if (c.kind === "all") {
@@ -36,5 +36,4 @@ var s = "tenantId", c = (c, l, u) => i.gen(function* () {
36
36
  predicateFor: (e) => _.has(e) ? a(s, p) : h.has(e) ? o(r(l, e), [...h.get(e)]) : void 0
37
37
  };
38
38
  });
39
- //#endregion
40
39
  export { c as resolveScope };
@@ -0,0 +1,39 @@
1
+ import { ciphertextKeyId as e } from "@voltro/database";
2
+ // src/sealedKeys.ts
3
+ var t = /enc:v[12]:[A-Za-z0-9+/=:_-]+/g, n = () => ({
4
+ byKey: {},
5
+ unnamed: 0
6
+ }), r = (e) => typeof e == "string" ? e : typeof e == "object" && e && !(e instanceof Uint8Array) && !(e instanceof Date) ? JSON.stringify(e) : void 0, i = (n, i) => {
7
+ for (let a of Object.values(n)) {
8
+ let n = r(a);
9
+ if (n !== void 0 && n.includes("enc:v")) for (let r of n.match(t) ?? []) {
10
+ let t = e(r);
11
+ t === void 0 ? i.unnamed += 1 : i.byKey[t] = (i.byKey[t] ?? 0) + 1;
12
+ }
13
+ }
14
+ }, a = (e, t) => {
15
+ for (let [n, r] of Object.entries(t.byKey)) e.byKey[n] = (e.byKey[n] ?? 0) + r;
16
+ e.unnamed += t.unnamed;
17
+ }, o = (e) => {
18
+ let n = e.match(t);
19
+ return n !== null && n.length === 1 && n[0] === e;
20
+ }, s = (n, i, a, s) => {
21
+ let c = new Set(a.held), l = (t, r) => {
22
+ let o = e(t);
23
+ return o === void 0 ? a.opens?.(t, {
24
+ table: n,
25
+ column: r,
26
+ row: i
27
+ }) ?? !0 : c.has(o);
28
+ }, u = { ...i };
29
+ for (let [e, n] of Object.entries(i)) {
30
+ if (typeof n == "string" && o(n)) {
31
+ l(n, e) || (u[e] = null, s.nulled.set(e, (s.nulled.get(e) ?? 0) + 1));
32
+ continue;
33
+ }
34
+ let i = r(n);
35
+ if (i !== void 0 && i.includes("enc:v")) for (let n of i.match(t) ?? []) l(n, e) || s.unreadable.set(e, (s.unreadable.get(e) ?? 0) + 1);
36
+ }
37
+ return u;
38
+ };
39
+ export { n as emptyTally, a as mergeTally, s as nullUnreadableSealed, i as tallyRow };
@@ -1,7 +1,7 @@
1
1
  import { Effect as e } from "effect";
2
2
  import { STAGING_TABLE_PREFIX as t } from "@voltro/database";
3
3
  import { SqlClient as n } from "effect/sql";
4
- //#region src/stagedSwap.ts
4
+ // src/stagedSwap.ts
5
5
  var r = t, i = (e) => `${r}${e}`, a = (e) => typeof e.run == "function", o = (e) => `staging is not available for this run — loading directly into the target instead: ${e}\n The load therefore holds the destructive transaction open for its whole duration, so a
6
6
  process that dies mid-load depends on the database rolling it back. The rollback capture
7
7
  written before the first delete is what covers that case.`, s = /^[a-zA-Z_][a-zA-Z0-9_]*$/, c = (e, t) => {
@@ -160,5 +160,4 @@ var r = t, i = (e) => `${r}${e}`, a = (e) => typeof e.run == "function", o = (e)
160
160
  tables: o
161
161
  };
162
162
  };
163
- //#endregion
164
163
  export { r as STAGING_PREFIX, a as canStage, l as createStagingSql, m as danglingProbeSql, h as danglingReport, x as decideStaging, u as dropStagingSql, g as execStatements, v as findDangling, y as forwardReferences, c as quoteIdent, f as registerStagingClones, _ as runSwap, p as stagedReferences, i as stagingNameFor, o as stagingUnavailable, b as suspendsIntegrity, d as swapStatements };
@@ -0,0 +1,46 @@
1
+ import { BundleError as e } from "./errors.js";
2
+ import { admitFilesystemPath as t } from "./filesystemAdmission.js";
3
+ import { isSafeBundlePath as n } from "./bundlePaths.js";
4
+ import { MANIFEST_FILE as r, decodeManifest as i } from "./manifest.js";
5
+ import { AssetSourceInfo as a } from "./assetAdmission.js";
6
+ import { makeDecompressor as o } from "./compress.js";
7
+ import { ndjsonToRows as s } from "./ndjson.js";
8
+ import { Effect as c, Schema as l, Stream as u } from "effect";
9
+ import { createReadStream as d } from "node:fs";
10
+ import { readFile as f } from "node:fs/promises";
11
+ import { join as p } from "node:path";
12
+ import { STORAGE_REFS_TABLE as m, fromNodeReadable as h } from "@voltro/plugin-storage";
13
+ import { compose as g } from "node:stream";
14
+ // src/storageAssetMetadata.ts
15
+ var _ = (t) => c.tryPromise({
16
+ try: async () => {
17
+ let e = i(await f(p(t, r), "utf8"));
18
+ return {
19
+ dir: t,
20
+ tables: e.tables,
21
+ compression: e.compression
22
+ };
23
+ },
24
+ catch: () => new e({ reason: "exported asset manifest unavailable" })
25
+ }), v = (e) => l.decodeUnknownSync(a)({
26
+ destination: e.bucket,
27
+ tenantId: e.tenantId,
28
+ key: e.key,
29
+ ...typeof e.checksum == "string" && /^[a-f0-9]{64}$/.test(e.checksum) ? { sha256: e.checksum } : {},
30
+ contentType: e.contentType,
31
+ size: typeof e.size == "bigint" || typeof e.size == "string" && /^(0|[1-9]\d*)$/.test(e.size) ? Number(e.size) : e.size
32
+ }), y = (r) => {
33
+ let i = r.tables.find((e) => e.name === m);
34
+ return i === void 0 ? u.empty : u.unwrap(c.gen(function* () {
35
+ if (!n(i.file)) return yield* c.fail(new e({ reason: "invalid asset metadata table path" }));
36
+ let a = p(r.dir, i.file);
37
+ return yield* c.tryPromise({
38
+ try: () => t(a, "file"),
39
+ catch: () => new e({ reason: "asset metadata path unavailable" })
40
+ }), s(h("bundle", () => g(d(a), o(r.compression))), i.name, i.columnTypes).pipe(u.mapEffect((t) => c.try({
41
+ try: () => v(t),
42
+ catch: () => new e({ reason: "invalid storage ownership in exported rows" })
43
+ })));
44
+ }));
45
+ };
46
+ export { _ as assetBundleContext, y as bundleAssetInfoStream, v as storageAssetInfo };
package/dist/subset.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect as e, Stream as t } from "effect";
2
2
  import { inSet as n, or as r, streamTable as i } from "@voltro/database";
3
- //#region src/subset.ts
3
+ // src/subset.ts
4
4
  var a = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type === "id")?.name ?? "id", o = (a, o, s) => e.gen(function* () {
5
5
  let c = s.maxDepth ?? 12, l = new Set(s.pinned ?? []), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map();
6
6
  for (let e of o.tables) p.has(e.name) || p.set(e.name, e);
@@ -86,5 +86,4 @@ var a = (e, t) => e.tables.find((e) => e.name === t)?.columns.find((e) => e.type
86
86
  return i ? n(a(t, r), [...i]) : void 0;
87
87
  }
88
88
  });
89
- //#endregion
90
89
  export { o as computeSubsetIds, a as pkOf, s as subsetToScope };
package/dist/topo.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/topo.ts
1
+ // src/topo.ts
2
2
  var e = (e, t) => {
3
3
  let n = new Set(e), r = /* @__PURE__ */ new Map();
4
4
  for (let e of t.tables) {
@@ -20,5 +20,4 @@ var e = (e, t) => {
20
20
  for (let t of e) n.has(t) && s(t);
21
21
  return i;
22
22
  };
23
- //#endregion
24
23
  export { e as topoSortTables };
@@ -1,4 +1,4 @@
1
- //#region src/typedCodec.ts
1
+ // src/typedCodec.ts
2
2
  var e = /* @__PURE__ */ new Set([
3
3
  "json",
4
4
  "array",
@@ -32,5 +32,4 @@ var e = /* @__PURE__ */ new Set([
32
32
  for (let [r, i] of Object.entries(e)) n[r] = s(i, t[r]);
33
33
  return n;
34
34
  };
35
- //#endregion
36
35
  export { l as decodeRow, s as decodeValue, c as encodeRow, a as encodeValue };