@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
@@ -0,0 +1,91 @@
1
+ import { BundleError as e, IntegrityError as t } from "./errors.js";
2
+ import { admitFilesystemPath as n } from "./filesystemAdmission.js";
3
+ import { AssetEntry as r } from "./manifest.js";
4
+ import { physicalRequest as i } from "./physicalRequest.js";
5
+ import { Effect as a, Schema as o } from "effect";
6
+ import { createHash as s } from "node:crypto";
7
+ import { constants as c, createWriteStream as l } from "node:fs";
8
+ import { lstat as u, mkdtemp as d, open as f, rm as p } from "node:fs/promises";
9
+ import { join as m } from "node:path";
10
+ import { tmpdir as h } from "node:os";
11
+ import { Transform as g } from "node:stream";
12
+ import { pipeline as _ } from "node:stream/promises";
13
+ import { storageAddressIdentity as v } from "@voltro/plugin-storage/errors";
14
+ // src/assetAdmission.ts
15
+ var y = Symbol("admitted-assets"), b = /* @__PURE__ */ new WeakMap(), x = (t) => o.decodeUnknownEffect(r)(t).pipe(a.mapError(() => new e({ reason: "asset index contains invalid key, digest, size or content type" }))), S = o.Struct({
16
+ ...r.fields,
17
+ sha256: o.optionalKey(r.fields.sha256)
18
+ }), C = (t) => o.decodeUnknownEffect(S)(t).pipe(a.mapError(() => new e({ reason: "asset source metadata contains invalid destination, tenant, key, digest, size or content type" }))), w = (t, n) => b.get(t) === n ? a.void : a.fail(new e({ reason: "asset admission is absent, belongs to another sink or its scope has closed" })), T = (e) => w(e.prepared, e.previousSink).pipe(a.flatMap(() => E({
19
+ entries: e.prepared.entries,
20
+ assetsDir: e.prepared.assetsDir,
21
+ sink: e.sink
22
+ }))), E = (r) => a.gen(function* () {
23
+ yield* a.tryPromise({
24
+ try: () => n(r.assetsDir, "directory"),
25
+ catch: () => new e({ reason: "asset source directory could not be admitted" })
26
+ });
27
+ let o = [], S = /* @__PURE__ */ new Map();
28
+ for (let t of r.entries) {
29
+ let n = yield* x(t), r = v(n), i = S.get(r);
30
+ if (i !== void 0) {
31
+ if (i.sha256 !== n.sha256 || i.size !== n.size || i.contentType !== n.contentType) return yield* a.fail(new e({ reason: "asset index contains conflicting entries for one key" }));
32
+ } else S.set(r, n), o.push(n);
33
+ }
34
+ let C = yield* a.acquireRelease(a.tryPromise({
35
+ try: () => d(m(h(), "voltro-admitted-assets-")),
36
+ catch: () => new e({ reason: "cannot create asset admission directory" })
37
+ }), (e) => a.promise(() => p(e, {
38
+ recursive: !0,
39
+ force: !0
40
+ }))), w = /* @__PURE__ */ new Set();
41
+ for (let d of o) if (yield* r.sink.has(d), w.has(d.sha256)) {
42
+ let n = yield* a.tryPromise({
43
+ try: () => u(m(C, d.sha256)).then((e) => e.size),
44
+ catch: () => new e({ reason: "admitted asset body is unavailable" })
45
+ });
46
+ if (n !== d.size) return yield* a.fail(new t({
47
+ what: `asset ${d.key} size`,
48
+ expected: String(d.size),
49
+ actual: String(n)
50
+ }));
51
+ } else {
52
+ let o = yield* i(async (e) => {
53
+ let t = m(r.assetsDir, d.sha256);
54
+ await n(t, "file");
55
+ let i = await f(t, c.O_RDONLY | c.O_NOFOLLOW | c.O_NONBLOCK);
56
+ try {
57
+ if (!(await i.stat()).isFile()) throw Error("asset body must be a regular file");
58
+ let t = s("sha256"), n = 0, r = new g({ transform(e, r, i) {
59
+ t.update(e), n += e.length, i(null, e);
60
+ } });
61
+ return await _(i.createReadStream({ autoClose: !1 }), r, l(m(C, d.sha256), {
62
+ flags: "wx",
63
+ mode: 384
64
+ }), { signal: e }), {
65
+ sha256: t.digest("hex"),
66
+ size: n
67
+ };
68
+ } finally {
69
+ await i.close();
70
+ }
71
+ }, () => new e({
72
+ reason: "asset body could not be admitted",
73
+ path: d.sha256
74
+ }));
75
+ if (o.sha256 !== d.sha256 || o.size !== d.size) return yield* a.fail(new t({
76
+ what: `asset ${d.key}`,
77
+ expected: `${d.sha256}/${d.size}`,
78
+ actual: `${o.sha256}/${o.size}`
79
+ }));
80
+ w.add(d.sha256);
81
+ }
82
+ let T = Object.freeze({
83
+ [y]: !0,
84
+ assetsDir: C,
85
+ entries: Object.freeze(o.map((e) => Object.freeze(e)))
86
+ });
87
+ return b.set(T, r.sink), yield* a.addFinalizer(() => a.sync(() => {
88
+ b.delete(T);
89
+ })), T;
90
+ });
91
+ export { S as AssetSourceInfo, w as assertPreparedAssets, x as decodeAssetEntry, C as decodeAssetSourceInfo, E as prepareAssetEntries, T as readmitPreparedAssets };
package/dist/assets.js CHANGED
@@ -1,47 +1,67 @@
1
- import { BundleError as e, IntegrityError as t } from "./errors.js";
2
- import { Effect as n, Stream as r } from "effect";
3
- import { fromNodeReadable as i, getObjectStream as a, putObjectStream as o, toNodeReadable as s } from "@voltro/plugin-storage";
4
- //#region src/assets.ts
5
- var c = (e, t) => ({
1
+ import { BundleError as e } from "./errors.js";
2
+ import { admitFilesystemPath as t } from "./filesystemAdmission.js";
3
+ import { physicalRequest as n } from "./physicalRequest.js";
4
+ import { assertPreparedAssets as r, prepareAssetEntries as i } from "./assetAdmission.js";
5
+ import { Effect as a, Stream as o } from "effect";
6
+ import { createHash as s } from "node:crypto";
7
+ import { fromNodeReadable as c, getObjectStream as l, putObjectStream as u, toNodeReadable as d } from "@voltro/plugin-storage";
8
+ import { storageAddressIdentity as f } from "@voltro/plugin-storage/errors";
9
+ // src/assets.ts
10
+ var p = (e, t) => ({
6
11
  list: t,
7
- open: (t) => a(e, t)
8
- }), l = (e) => ({
9
- write: (t, n, r) => o(e, t, n, r),
10
- has: (t) => e.head(t).pipe(n.map((e) => e !== null))
11
- }), u = (t, r) => n.tryPromise({
12
- try: async () => {
13
- let { createWriteStream: e } = await import("node:fs"), n = await import("node:fs/promises"), { createHash: i, randomUUID: a } = await import("node:crypto"), { pipeline: o } = await import("node:stream/promises"), { Transform: c } = await import("node:stream"), l = await import("node:path"), u = l.join(r, `.tmp-${a()}`), d = i("sha256"), f = 0, p = new c({ transform(e, t, n) {
14
- d.update(e), f += e.length, n(null, e);
12
+ open: (t) => e.resolve(t).pipe(a.flatMap((e) => l(e, t.key)))
13
+ }), m = (e) => ({
14
+ write: (t, n, r) => e.resolve(t).pipe(a.flatMap((e) => u(e, t.key, n, r))),
15
+ has: (t) => e.resolve(t).pipe(a.flatMap((e) => e.head(t.key)), a.map((e) => e !== null)),
16
+ matches: (t) => a.gen(function* () {
17
+ let n = yield* e.resolve(t), r = yield* n.head(t.key);
18
+ if (r === null || r.size !== t.size || r.contentType !== void 0 && r.contentType !== t.contentType) return !1;
19
+ let i = yield* l(n, t.key);
20
+ if (i.contentType !== t.contentType) return !1;
21
+ let a = s("sha256");
22
+ return (yield* o.runFold(i.stream, () => 0, (e, t) => (a.update(t), e + t.byteLength))) === t.size && a.digest("hex") === t.sha256;
23
+ })
24
+ }), h = (r, i) => n(async (e) => {
25
+ let { createWriteStream: n } = await import("node:fs"), a = await import("node:fs/promises"), { pipeline: o, finished: c } = await import("node:stream/promises"), { Transform: l } = await import("node:stream"), u = await import("node:path");
26
+ await t(i, "directory");
27
+ let f = await a.mkdtemp(u.join(i, ".voltro-capture-")), p;
28
+ try {
29
+ let c = u.join(f, "body"), m = s("sha256"), h = 0, g = new l({ transform(e, t, n) {
30
+ m.update(e), h += e.length, n(null, e);
15
31
  } });
16
- await o(await s(t.stream), p, e(u));
17
- let m = d.digest("hex"), h = l.join(r, m);
18
- try {
19
- await n.access(h), await n.rm(u, { force: !0 });
20
- } catch {
21
- await n.rename(u, h);
22
- }
23
- return {
24
- sha256: m,
25
- size: f,
26
- contentType: t.contentType
32
+ p = await d(r.stream), await o(p, g, n(c, {
33
+ flags: "wx",
34
+ mode: 384
35
+ }), { signal: e });
36
+ let _ = m.digest("hex"), v = u.join(i, _);
37
+ return await t(v, "file"), await a.rename(c, v), {
38
+ sha256: _,
39
+ size: h,
40
+ contentType: r.contentType
27
41
  };
28
- },
29
- catch: (t) => new e({
30
- reason: `asset write failed: ${String(t?.message ?? t)}`,
31
- path: r
32
- })
33
- }), d = (e, a, o) => n.gen(function* () {
34
- let s = yield* n.promise(() => import("node:path")), { createReadStream: c } = yield* n.promise(() => import("node:fs")), { createHash: l } = yield* n.promise(() => import("node:crypto")), u = s.join(a, o.sha256), d = l("sha256"), f = i("bundle", () => c(u)).pipe(r.tap((e) => n.sync(() => d.update(e))));
35
- yield* e.write(o.key, f, {
36
- contentType: o.contentType,
37
- totalSize: o.size
42
+ } finally {
43
+ p !== void 0 && (p.destroy(), await c(p, { cleanup: !0 }).catch(() => void 0)), await a.rm(f, {
44
+ recursive: !0,
45
+ force: !0
46
+ });
47
+ }
48
+ }, () => new e({
49
+ reason: "asset capture failed",
50
+ path: i
51
+ })), g = (e, t, n) => a.scoped(a.gen(function* () {
52
+ yield* _(e, yield* i({
53
+ sink: e,
54
+ assetsDir: t,
55
+ entries: [n]
56
+ }), n);
57
+ })), _ = (t, n, i) => a.gen(function* () {
58
+ yield* r(n, t);
59
+ let o = yield* a.promise(() => import("node:path")), { createReadStream: s } = yield* a.promise(() => import("node:fs"));
60
+ if (!n.entries.some((e) => f(e) === f(i) && e.sha256 === i.sha256 && e.size === i.size && e.contentType === i.contentType)) return yield* a.fail(new e({ reason: "asset is outside the admitted index" }));
61
+ let l = o.join(n.assetsDir, i.sha256), u = c("bundle", () => s(l));
62
+ yield* t.write(i, u, {
63
+ contentType: i.contentType,
64
+ totalSize: i.size
38
65
  });
39
- let p = d.digest("hex");
40
- if (p !== o.sha256) return yield* n.fail(new t({
41
- what: `asset ${o.key}`,
42
- expected: o.sha256,
43
- actual: p
44
- }));
45
66
  });
46
- //#endregion
47
- export { d as restoreAssetFromCas, l as storageAssetSink, c as storageAssetSource, u as writeAssetToCas };
67
+ export { _ as applyAdmittedAsset, g as restoreAssetFromCas, m as storageAssetSink, p as storageAssetSource, h as writeAssetToCas };
@@ -1,40 +1,63 @@
1
- import { Effect as e, Stream as t } from "effect";
2
- import { streamTable as n } from "@voltro/database";
3
- import { STORAGE_REFS_TABLE as r, fromNodeReadable as i, getObjectStream as a, putObjectStream as o, toNodeReadable as s } from "@voltro/plugin-storage";
4
- import { Readable as c } from "node:stream";
5
- //#region src/blobStorage.ts
6
- var l = (n, r) => ({
7
- list: () => {
8
- let n = r();
1
+ import { physicalRequest as e } from "./physicalRequest.js";
2
+ import { storageAssetSink as t } from "./assets.js";
3
+ import { assetBundleContext as n, bundleAssetInfoStream as r, storageAssetInfo as i } from "./storageAssetMetadata.js";
4
+ import { Effect as a, Stream as o } from "effect";
5
+ import { streamTable as s } from "@voltro/database";
6
+ import { STORAGE_REFS_TABLE as c, StorageError as l, fromNodeReadable as u, getObjectStream as d, putObjectStream as f, toNodeReadable as p } from "@voltro/plugin-storage";
7
+ import { Readable as m } from "node:stream";
8
+ import { finished as h } from "node:stream/promises";
9
+ // src/blobStorage.ts
10
+ var g = (e, t) => ({
11
+ list: (e) => {
12
+ let n = t(e);
9
13
  return { async *[Symbol.asyncIterator]() {
10
- let r = await e.runPromise(t.runCollect(n));
11
- for (let e of r) yield {
12
- key: e.key,
13
- contentType: e.contentType,
14
- size: e.size
15
- };
14
+ let e = await a.runPromise(o.runCollect(n));
15
+ for (let t of e) yield t;
16
16
  } };
17
17
  },
18
18
  open: (t) => ({ async *[Symbol.asyncIterator]() {
19
- let r = await e.runPromise(a(n, t)), i = await s(r.stream);
20
- for await (let e of i) yield e instanceof Uint8Array ? e : new Uint8Array(e);
19
+ let n = await a.runPromise(e.resolve(t)), r = await a.runPromise(n.head(t.key));
20
+ if (r !== null && (r.contentType !== void 0 && r.contentType !== t.contentType || r.size !== t.size)) throw Error("archive blob contradicts source metadata");
21
+ let i = await a.runPromise(d(n, t.key));
22
+ if (r?.contentType !== void 0 && i.contentType !== t.contentType || i.size !== void 0 && i.size !== t.size) throw i.close !== void 0 && await a.runPromise(i.close), Error("archive blob contradicts source metadata");
23
+ let o = await p(i.stream);
24
+ for await (let e of o) yield e instanceof Uint8Array ? e : new Uint8Array(e);
21
25
  } })
22
- }), u = (e, i) => l(e, () => n(i, {
23
- table: r,
26
+ }), _ = (e, t) => g(e, (e) => e === void 0 ? s(t, {
27
+ table: c,
24
28
  chunkSize: 500
25
- }).pipe(t.map((e) => ({
26
- key: String(e.key),
27
- contentType: String(e.contentType ?? "application/octet-stream"),
28
- size: Number(e.size ?? 0)
29
- })))), d = (t) => ({
30
- has: (n) => e.runPromise(t.head(n).pipe(e.map((e) => e !== null))),
31
- write: async (n, r, a) => {
32
- let s = i("storage", () => c.from(r));
33
- await e.runPromise(o(t, n, s, {
34
- contentType: a.contentType,
35
- totalSize: a.size
36
- }));
29
+ }).pipe(o.map(i)) : o.unwrap(n(e).pipe(a.map(r)))), v = (e) => ({
30
+ has: (n, r) => a.runPromise(t(e).has(n), r),
31
+ matches: (n, r) => a.runPromise(t(e).matches(n), r),
32
+ write: async (t, n, r, i) => {
33
+ let o = u("storage", () => m.from(n));
34
+ await a.runPromise(e.resolve(t).pipe(a.flatMap((e) => f(e, t.key, o, {
35
+ contentType: r.contentType,
36
+ totalSize: r.size
37
+ }))), i);
37
38
  }
39
+ }), y = (t, n) => e(t, () => new l({
40
+ provider: "archive",
41
+ message: n,
42
+ transient: !1
43
+ })), b = (e) => ({
44
+ has: (t) => y((n) => e.has(t, { signal: n }), "asset destination admission failed"),
45
+ matches: (t) => y((n) => e.matches(t, { signal: n }), "asset destination content verification failed"),
46
+ write: (t, n, r) => y(async (i) => {
47
+ if (r.totalSize === void 0) throw Error("admitted asset size is required");
48
+ let a = await p(n), o = () => {
49
+ a.destroy();
50
+ };
51
+ i.addEventListener("abort", o, { once: !0 });
52
+ try {
53
+ if (i.aborted) throw o(), Error("asset write interrupted");
54
+ await e.write(t, a, {
55
+ contentType: r.contentType,
56
+ size: r.totalSize
57
+ }, { signal: i });
58
+ } finally {
59
+ i.removeEventListener("abort", o), a.destroy(), await h(a, { cleanup: !0 }).catch(() => void 0);
60
+ }
61
+ }, "admitted asset write failed")
38
62
  });
39
- //#endregion
40
- export { d as storageBlobSink, l as storageBlobSource, u as storageRefsBlobSource };
63
+ export { b as blobAssetSink, v as storageBlobSink, g as storageBlobSource, _ as storageRefsBlobSource };