@voltro/data-transfer 0.86.0 → 0.87.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.
@@ -1,14 +1,22 @@
1
- import { createHash as e } from "node:crypto";
2
- import { createReadStream as t, createWriteStream as n } from "node:fs";
3
- import { PassThrough as r } from "node:stream";
4
- import { mkdir as i, readdir as a, stat as o } from "node:fs/promises";
5
- import { dirname as s, join as c, resolve as l, sep as u } from "node:path";
1
+ import { admitFilesystemPath as e, publishBundleFile as t } from "./filesystemAdmission.js";
2
+ import { isSafeBundlePath as n } from "./bundlePaths.js";
3
+ import { AssetAddress as r, MANIFEST_FILE as i, decodeManifest as a, encodeManifest as o } from "./manifest.js";
4
+ import { decodeAssetSourceInfo as s, prepareAssetEntries as c } from "./assetAdmission.js";
5
+ import { readAssetIndex as l } from "./casAssetPhase.js";
6
+ import { Effect as u, Schema as d, Stream as f } from "effect";
7
+ import { createHash as p } from "node:crypto";
8
+ import { createReadStream as m, createWriteStream as h } from "node:fs";
9
+ import { lstat as g, mkdir as _, mkdtemp as v, readdir as y, rm as b, stat as x } from "node:fs/promises";
10
+ import { dirname as S, join as C, resolve as w, sep as T } from "node:path";
11
+ import { tmpdir as E } from "node:os";
12
+ import { PassThrough as D } from "node:stream";
13
+ import { storageAddressIdentity as O } from "@voltro/plugin-storage/errors";
6
14
  //#region src/bundleArchive.ts
7
- var d = Buffer.from("VBUNDLE2"), f = 0, p = 1, m = 2, h = 32, g = async (e) => {
15
+ var k = Buffer.from("VBUNDLE3"), A = 0, j = 1, M = 2, N = 32, P = async (e) => {
8
16
  let t = [], n = async (r) => {
9
17
  let i;
10
18
  try {
11
- i = await a(c(e, r), { withFileTypes: !0 });
19
+ i = await y(C(e, r), { withFileTypes: !0 });
12
20
  } catch {
13
21
  return;
14
22
  }
@@ -18,93 +26,166 @@ var d = Buffer.from("VBUNDLE2"), f = 0, p = 1, m = 2, h = 32, g = async (e) => {
18
26
  }
19
27
  };
20
28
  return await n(""), t.sort();
21
- }, _ = (e, t) => {
29
+ }, F = (e, t) => {
22
30
  let n = Buffer.from(e, "utf8"), r = Buffer.alloc(5 + n.length + 8);
23
- return r.writeUInt8(p, 0), r.writeUInt32BE(n.length, 1), n.copy(r, 5), r.writeBigUInt64BE(BigInt(t), 5 + n.length), r;
24
- }, v = (e, t, n) => {
25
- let r = Buffer.from(e, "utf8"), i = Buffer.from(t, "utf8"), a = Buffer.alloc(5 + r.length + 2 + i.length + 8), o = 0;
26
- return a.writeUInt8(m, o), o += 1, a.writeUInt32BE(r.length, o), o += 4, r.copy(a, o), o += r.length, a.writeUInt16BE(i.length, o), o += 2, i.copy(a, o), o += i.length, a.writeBigUInt64BE(BigInt(n), o), a;
31
+ return r.writeUInt8(j, 0), r.writeUInt32BE(n.length, 1), n.copy(r, 5), r.writeBigUInt64BE(BigInt(t), 5 + n.length), r;
32
+ }, I = (e, t, n) => {
33
+ let i = Buffer.from(JSON.stringify(d.decodeUnknownSync(r)(e)), "utf8"), a = Buffer.from(t, "utf8"), o = Buffer.alloc(5 + i.length + 2 + a.length + 8), s = 0;
34
+ return o.writeUInt8(M, s), s += 1, o.writeUInt32BE(i.length, s), s += 4, i.copy(o, s), s += i.length, o.writeUInt16BE(a.length, s), s += 2, a.copy(o, s), s += a.length, o.writeBigUInt64BE(BigInt(n), s), o;
27
35
  };
28
- async function* y(n) {
29
- yield new Uint8Array(d);
30
- for (let e of await g(n.dir)) {
31
- let r = c(n.dir, e), i = await o(r);
32
- if (yield new Uint8Array(_(e, i.size)), i.size > 0) for await (let e of t(r)) yield new Uint8Array(e);
36
+ async function* L(e) {
37
+ yield new Uint8Array(k);
38
+ for (let t of await P(e.dir)) {
39
+ let n = C(e.dir, t), r = await x(n);
40
+ if (yield new Uint8Array(F(t, r.size)), r.size > 0) for await (let e of m(n)) yield new Uint8Array(e);
33
41
  }
34
- if (n.blobs) for await (let t of n.blobs.list()) {
35
- yield new Uint8Array(v(t.key, t.contentType, t.size));
36
- let r = e("sha256"), i = 0;
37
- for await (let e of n.blobs.open(t.key)) {
38
- let t = Buffer.from(e);
39
- r.update(t), i += t.length, yield new Uint8Array(t);
42
+ if (e.blobs) for await (let t of e.blobs.list(e.dir)) {
43
+ let n = await u.runPromise(s(t));
44
+ yield new Uint8Array(I(n, n.contentType, n.size));
45
+ let r = p("sha256"), i = 0;
46
+ for await (let t of e.blobs.open(n)) {
47
+ let e = Buffer.from(t);
48
+ r.update(e), i += e.length, yield new Uint8Array(e);
40
49
  }
41
- if (i !== t.size) throw Error(`blob '${t.key}' size mismatch: header ${t.size}, streamed ${i}`);
42
- yield new Uint8Array(r.digest());
50
+ if (i !== n.size) throw Error(`blob '${n.key}' size mismatch: header ${n.size}, streamed ${i}`);
51
+ let a = r.digest();
52
+ if (n.sha256 !== void 0 && a.toString("hex") !== n.sha256) throw Error("archive blob contradicts SQL checksum");
53
+ yield new Uint8Array(a);
43
54
  }
44
- yield new Uint8Array(Buffer.from([f]));
55
+ yield new Uint8Array(Buffer.from([A]));
45
56
  }
46
- var b = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), x = (e) => new Promise((t) => e.end(t)), S = async (t, a) => {
47
- let o = l(a.dir);
48
- await i(o, { recursive: !0 });
49
- let c = Buffer.alloc(0), g = t[Symbol.asyncIterator](), _ = !1, v = async () => {
50
- let { value: e, done: t } = await g.next();
51
- return t ? (_ = !0, !1) : (c = c.length === 0 ? Buffer.from(e) : Buffer.concat([c, Buffer.from(e)]), !0);
52
- }, y = async (e) => {
53
- for (; c.length < e;) if (!await v()) throw Error("truncated bundle archive");
54
- }, S = (e) => {
55
- let t = c.subarray(0, e);
56
- return c = c.subarray(e), t;
57
+ var R = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), z = (e) => new Promise((t) => e.end(t)), B = async (n, r) => {
58
+ let i = await v(C(E(), "voltro-admitted-archive-")), a = C(i, "files"), o = C(i, "objects");
59
+ try {
60
+ await V(n, {
61
+ dir: a,
62
+ blobs: {
63
+ ...W(o),
64
+ has: async () => !1
65
+ }
66
+ });
67
+ let i = C(o, "assets", "index.ndjson"), s = await x(i).then(() => !0, () => !1) ? Array.from(await u.runPromise(f.runCollect(l(i)))) : [], c = await P(a);
68
+ await e(r.dir, "directory");
69
+ for (let t of c) await e(C(w(r.dir), t), "file");
70
+ let d = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Set();
71
+ for (let e of s) {
72
+ let t = O(e), n = d.get(t);
73
+ if (n !== void 0 && (n.sha256 !== e.sha256 || n.size !== e.size || n.contentType !== e.contentType)) throw Error("archive contains conflicting blob entries");
74
+ d.set(t, e), await r.blobs.has(e), await r.blobs.matches(e) && p.add(t);
75
+ }
76
+ for (let e of c) {
77
+ let n = C(w(r.dir), e);
78
+ await t(C(a, e), n);
79
+ }
80
+ for (let [e, t] of d) {
81
+ if (p.has(e)) continue;
82
+ let n = m(C(o, "assets", t.sha256));
83
+ try {
84
+ await r.blobs.write(t, n, {
85
+ contentType: t.contentType,
86
+ size: t.size
87
+ });
88
+ } finally {
89
+ n.destroy();
90
+ }
91
+ }
92
+ } finally {
93
+ await b(i, {
94
+ recursive: !0,
95
+ force: !0
96
+ });
97
+ }
98
+ }, V = async (e, t) => {
99
+ let i = w(t.dir);
100
+ await _(i, { recursive: !0 });
101
+ let a = Buffer.alloc(0), o = e[Symbol.asyncIterator](), s = !1, c = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Map(), u = async () => {
102
+ let { value: e, done: t } = await o.next();
103
+ return t ? (s = !0, !1) : (a = a.length === 0 ? Buffer.from(e) : Buffer.concat([a, Buffer.from(e)]), !0);
104
+ }, f = async (e) => {
105
+ for (; a.length < e;) if (!await u()) throw Error("truncated bundle archive");
106
+ }, m = (e) => {
107
+ let t = a.subarray(0, e);
108
+ return a = a.subarray(e), t;
57
109
  };
58
110
  try {
59
- if (await y(d.length), !S(d.length).equals(d)) throw Error("not a bundle archive (bad magic)");
111
+ if (await f(k.length), !m(k.length).equals(k)) throw Error("not a bundle archive (bad magic)");
60
112
  for (;;) {
61
- await y(1);
62
- let t = S(1).readUInt8(0);
63
- if (t === f) return;
64
- if (t === p) {
65
- await y(4);
66
- let e = S(4).readUInt32BE(0);
67
- await y(e);
68
- let t = S(e).toString("utf8");
69
- await y(8);
70
- let r = S(8).readBigUInt64BE(0), a = l(o, t);
71
- if (a !== o && !a.startsWith(o + u)) throw Error(`archive path '${t}' escapes the destination`);
72
- await i(s(a), { recursive: !0 });
73
- let d = n(a);
74
- for (; r > 0n;) {
75
- c.length === 0 && await y(1);
76
- let e = r < BigInt(c.length) ? Number(r) : c.length;
77
- await b(d, S(e)), r -= BigInt(e);
113
+ await f(1);
114
+ let e = m(1).readUInt8(0);
115
+ if (e === A) {
116
+ if (a.length > 0) throw Error("trailing bytes after bundle end marker");
117
+ for (; await u();) if (a.length > 0) throw Error("trailing bytes after bundle end marker");
118
+ return;
119
+ }
120
+ if (e === j) {
121
+ await f(4);
122
+ let e = m(4).readUInt32BE(0);
123
+ if (e === 0 || e > 65536) throw Error("invalid archive path length");
124
+ await f(e);
125
+ let t = m(e).toString("utf8");
126
+ await f(8);
127
+ let r = m(8).readBigUInt64BE(0), o = w(i, t);
128
+ if (o === i || !o.startsWith(i + T) || !n(t)) throw Error(`archive path '${t}' escapes the destination`);
129
+ if (c.has(o)) throw Error("duplicate plain archive path");
130
+ c.add(o), await _(S(o), { recursive: !0 });
131
+ let s = h(o);
132
+ try {
133
+ for (; r > 0n;) {
134
+ a.length === 0 && await f(1);
135
+ let e = r < BigInt(a.length) ? Number(r) : a.length;
136
+ await R(s, m(e)), r -= BigInt(e);
137
+ }
138
+ await z(s);
139
+ } finally {
140
+ s.destroy();
78
141
  }
79
- await x(d);
80
- } else if (t === m) {
81
- await y(4);
82
- let t = S(4).readUInt32BE(0);
83
- await y(t);
84
- let n = S(t).toString("utf8");
85
- await y(2);
86
- let i = S(2).readUInt16BE(0);
87
- await y(i);
88
- let o = S(i).toString("utf8");
89
- await y(8);
90
- let s = S(8).readBigUInt64BE(0), l = await a.blobs.has(n), u = e("sha256"), d = new r(), f = l ? Promise.resolve() : a.blobs.write(n, d, {
91
- contentType: o,
92
- size: Number(s)
93
- }), p = s;
94
- for (; p > 0n;) {
95
- c.length === 0 && await y(1);
96
- let e = p < BigInt(c.length) ? Number(p) : c.length, t = S(e);
97
- u.update(t), l || await b(d, t), p -= BigInt(e);
142
+ } else if (e === M) {
143
+ await f(4);
144
+ let e = m(4).readUInt32BE(0);
145
+ if (e === 0 || e > 65536) throw Error("invalid archive key length");
146
+ await f(e);
147
+ let n = d.decodeUnknownSync(r)(JSON.parse(m(e).toString("utf8"))), i = O(n);
148
+ await f(2);
149
+ let o = m(2).readUInt16BE(0);
150
+ await f(o);
151
+ let s = m(o).toString("utf8");
152
+ await f(8);
153
+ let c = m(8).readBigUInt64BE(0);
154
+ if (c > BigInt(2 ** 53 - 1)) throw Error("archive blob size exceeds exact integer range");
155
+ let u = p("sha256"), h = new D(), g = t.blobs.write(n, h, {
156
+ contentType: s,
157
+ size: Number(c)
158
+ });
159
+ g.catch((e) => {
160
+ h.destroy(e instanceof Error ? e : /* @__PURE__ */ Error("archive staging failed"));
161
+ });
162
+ let _ = c;
163
+ try {
164
+ for (; _ > 0n;) {
165
+ a.length === 0 && await f(1);
166
+ let e = _ < BigInt(a.length) ? Number(_) : a.length, t = m(e);
167
+ u.update(t), await R(h, t), _ -= BigInt(e);
168
+ }
169
+ await z(h), await g, await f(N);
170
+ let e = m(N), t = u.digest();
171
+ if (!t.equals(e)) throw Error(`blob '${i}' failed integrity (sha mismatch)`);
172
+ let r = {
173
+ ...n,
174
+ sha256: t.toString("hex"),
175
+ size: Number(c),
176
+ contentType: s
177
+ }, o = l.get(i);
178
+ if (o !== void 0 && (o.sha256 !== r.sha256 || o.size !== r.size || o.contentType !== r.contentType)) throw Error("archive contains conflicting blob addresses");
179
+ l.set(i, r);
180
+ } finally {
181
+ h.destroy(), await g.catch(() => void 0);
98
182
  }
99
- l ? d.destroy() : await x(d), await f, await y(h);
100
- let m = S(h);
101
- if (!u.digest().equals(m)) throw Error(`blob '${n}' failed integrity (sha mismatch)`);
102
- } else throw Error(`unknown bundle entry kind ${t}`);
183
+ } else throw Error(`unknown bundle entry kind ${e}`);
103
184
  }
104
185
  } finally {
105
- _ || await Promise.resolve(g.return?.()).catch(() => void 0);
186
+ s || await Promise.resolve(o.return?.()).catch(() => void 0);
106
187
  }
107
- }, C = async (e, t) => {
188
+ }, H = async (e, t) => {
108
189
  let n = new Set(t), r = /* @__PURE__ */ new Map(), i = Buffer.alloc(0), a = e[Symbol.asyncIterator](), o = async () => {
109
190
  let { value: e, done: t } = await a.next();
110
191
  return !t && (i = i.length === 0 ? Buffer.from(e) : Buffer.concat([i, Buffer.from(e)]), !0);
@@ -123,11 +204,11 @@ var b = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), x =
123
204
  }
124
205
  return Buffer.concat(t);
125
206
  };
126
- if (!await s(d.length) || !c(d.length).equals(d)) throw Error("not a bundle archive (bad magic)");
207
+ if (!await s(k.length) || !c(k.length).equals(k)) throw Error("not a bundle archive (bad magic)");
127
208
  for (; await s(1);) {
128
209
  let e = c(1).readUInt8(0);
129
- if (e === f || e === m) break;
130
- if (e !== p) throw Error(`unknown bundle entry kind ${e}`);
210
+ if (e === A || e === M) break;
211
+ if (e !== j) throw Error(`unknown bundle entry kind ${e}`);
131
212
  if (!await s(4)) break;
132
213
  let t = c(4).readUInt32BE(0);
133
214
  if (!await s(t)) break;
@@ -137,29 +218,93 @@ var b = (e, t) => new Promise((n, r) => e.write(t, (e) => e ? r(e) : n())), x =
137
218
  if (a === null || n.has(i) && (r.set(i, a), n.delete(i), n.size === 0)) break;
138
219
  }
139
220
  return r;
140
- }, w = (e, t) => S(e, {
221
+ }, U = (e, t) => B(e, {
141
222
  dir: t,
142
- blobs: T(t)
143
- }), T = (t) => {
144
- let r = /* @__PURE__ */ new Set();
223
+ blobs: W(t)
224
+ }).then(async () => {
225
+ let e = await import("node:fs/promises"), n = C(t, "assets", "index.ndjson");
226
+ if (!await x(n).then(() => !0, () => !1)) return;
227
+ let r = await e.readFile(C(t, i), "utf8").catch(() => void 0);
228
+ if (r === void 0) return;
229
+ let s;
230
+ try {
231
+ s = a(r);
232
+ } catch {
233
+ return;
234
+ }
235
+ let c = Array.from(await u.runPromise(f.runCollect(l(n)))), d = new Map(c.map((e) => [O(e), e]));
236
+ await e.writeFile(C(t, i), o({
237
+ ...s,
238
+ assets: {
239
+ count: d.size,
240
+ totalBytes: [...d.values()].reduce((e, t) => e + t.size, 0),
241
+ index: "assets/index.ndjson"
242
+ }
243
+ }));
244
+ }), W = (t) => {
245
+ let n = /* @__PURE__ */ new Set();
145
246
  return {
146
- has: async (e) => r.has(e),
147
- write: async (a, o, s) => {
148
- let l = await import("node:fs/promises"), { pipeline: u } = await import("node:stream/promises"), { Transform: d } = await import("node:stream"), f = c(t, "assets");
149
- await i(f, { recursive: !0 });
150
- let p = e("sha256"), m = 0, h = new d({ transform(e, t, n) {
151
- p.update(e), m += e.length, n(null, e);
152
- } }), g = c(f, `.tmp-${Date.now()}-${r.size}`);
153
- await u(o, h, n(g));
154
- let _ = p.digest("hex");
155
- await l.rename(g, c(f, _)), await l.appendFile(c(f, "index.ndjson"), `${JSON.stringify({
156
- key: a,
157
- sha256: _,
158
- size: m,
159
- contentType: s.contentType
160
- })}\n`), r.add(a);
247
+ has: async (r) => (await e(C(t, "assets"), "directory"), await e(C(t, "assets", "index.ndjson"), "file"), n.has(O(r))),
248
+ matches: async (n) => {
249
+ let r = C(t, "assets", "index.ndjson");
250
+ if (await e(r, "file"), !await x(r).then(() => !0, () => !1)) return !1;
251
+ let i = Array.from(await u.runPromise(f.runCollect(l(r)))).filter((e) => O(e) === O(n));
252
+ if (i.length === 0 || i.some((e) => e.sha256 !== n.sha256 || e.size !== n.size || e.contentType !== n.contentType)) return !1;
253
+ let a = C(t, "assets", n.sha256);
254
+ await e(a, "file");
255
+ try {
256
+ await g(a);
257
+ } catch (e) {
258
+ if (typeof e == "object" && e && "code" in e && e.code === "ENOENT") return !1;
259
+ throw e;
260
+ }
261
+ return u.runPromise(u.scoped(c({
262
+ entries: [n],
263
+ assetsDir: C(t, "assets"),
264
+ sink: {
265
+ has: () => u.succeed(!1),
266
+ matches: () => u.succeed(!1),
267
+ write: () => u.void
268
+ }
269
+ }).pipe(u.as(!0), u.catchTag("IntegrityError", () => u.succeed(!1)))));
270
+ },
271
+ write: async (r, i, a) => {
272
+ let o = await import("node:fs/promises"), { pipeline: s } = await import("node:stream/promises"), { Transform: c } = await import("node:stream"), d = C(t, "assets");
273
+ await e(d, "directory"), await _(d, { recursive: !0 });
274
+ let m = p("sha256"), g = 0, y = new c({ transform(e, t, n) {
275
+ m.update(e), g += e.length, n(null, e);
276
+ } });
277
+ await e(d, "directory"), await e(C(d, "index.ndjson"), "file");
278
+ let S = await v(C(d, ".voltro-blob-"));
279
+ try {
280
+ let t = C(S, "body");
281
+ if (await s(i, y, h(t, {
282
+ flags: "wx",
283
+ mode: 384
284
+ })), g !== a.size) throw Error("blob body size does not match its metadata");
285
+ let n = m.digest("hex"), c = C(d, n);
286
+ await e(c, "file"), await e(C(d, "index.ndjson"), "file"), await o.rename(t, c);
287
+ let p = C(d, "index.ndjson"), _ = (await x(p).then(() => !0, () => !1) ? Array.from(await u.runPromise(f.runCollect(l(p)))) : []).filter((e) => O(e) !== O(r));
288
+ _.push({
289
+ ...r,
290
+ sha256: n,
291
+ size: g,
292
+ contentType: a.contentType
293
+ });
294
+ let v = C(S, "index");
295
+ await o.writeFile(v, _.map((e) => JSON.stringify(e)).join("\n") + "\n", {
296
+ flag: "wx",
297
+ mode: 384
298
+ }), await e(p, "file"), await o.rename(v, p);
299
+ } finally {
300
+ await b(S, {
301
+ recursive: !0,
302
+ force: !0
303
+ });
304
+ }
305
+ n.add(O(r));
161
306
  }
162
307
  };
163
308
  };
164
309
  //#endregion
165
- export { T as dirBlobSink, y as packBundle, C as peekBundle, S as unpackBundle, w as unpackBundleToDir };
310
+ export { W as dirBlobSink, L as packBundle, H as peekBundle, B as unpackBundle, U as unpackBundleToDir };
@@ -0,0 +1,4 @@
1
+ //#region src/bundlePaths.ts
2
+ var e = (e) => e.length > 0 && !e.includes("\0") && !e.includes("\\") && !e.startsWith("/") && !/^[a-z]:/i.test(e) && !e.split("/").some((e) => e === "" || e === "." || e === "..");
3
+ //#endregion
4
+ export { e as isSafeBundlePath };
@@ -1,43 +1,87 @@
1
1
  import { BundleError as e } from "./errors.js";
2
- import { restoreAssetFromCas as t, writeAssetToCas as n } from "./assets.js";
3
- import { Effect as r, Stream as i } from "effect";
4
- import { createReadStream as a } from "node:fs";
5
- import { fromNodeReadable as o } from "@voltro/plugin-storage";
2
+ import { admitFilesystemPath as t } from "./filesystemAdmission.js";
3
+ import { assertPreparedAssets as n, decodeAssetEntry as r, decodeAssetSourceInfo as i, prepareAssetEntries as a } from "./assetAdmission.js";
4
+ import { applyAdmittedAsset as o, writeAssetToCas as s } from "./assets.js";
5
+ import { Effect as c, Stream as l } from "effect";
6
+ import { constants as u } from "node:fs";
7
+ import { open as d } from "node:fs/promises";
8
+ import { fromNodeReadable as f } from "@voltro/plugin-storage";
9
+ import { storageAddressIdentity as p } from "@voltro/plugin-storage/errors";
6
10
  //#region src/casAssetPhase.ts
7
- var s = {
11
+ var m = {
8
12
  done: () => !1,
9
- mark: () => r.void
10
- }, c = (e) => typeof e == "object" && !!e && e.status === 404, l = (t) => r.gen(function* () {
11
- let a = t.progress ?? s, o = 0, l = 0, u = 0, d = 0, f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set(), m = [];
12
- return yield* i.runForEach(t.source.list(), (e) => (u++, a.done(e.key) || p.has(e.key) ? r.void : (p.add(e.key), t.source.open(e.key).pipe(r.map((e) => e), r.catchIf(c, () => r.sync(() => {
13
- m.push(e.key);
14
- })), r.flatMap((i) => i === void 0 ? r.void : n(i, t.assetsDir).pipe(r.flatMap((t) => {
15
- let n = {
16
- key: e.key,
13
+ mark: () => c.void
14
+ }, h = (e) => typeof e == "object" && !!e && e.status === 404, g = (t) => c.gen(function* () {
15
+ let n = t.progress ?? m, r = 0, a = 0, o = 0, u = 0, d = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Map(), g = [], _ = (t) => {
16
+ o++;
17
+ let r = p(t), i = f.get(r);
18
+ return i === void 0 ? (f.set(r, t), n.done(r) ? c.logDebug("Revalidating previously recorded source asset").pipe(c.andThen(v(t, r))) : v(t, r)) : i.size !== t.size || i.contentType !== t.contentType || i.sha256 !== void 0 && t.sha256 !== void 0 && i.sha256 !== t.sha256 ? c.fail(new e({ reason: "duplicate asset address has contradictory source metadata" })) : i.sha256 === void 0 && t.sha256 !== void 0 ? v(t, r) : c.void;
19
+ }, v = (i, o) => t.source.open(i).pipe(c.map((e) => e), c.catchIf(h, () => c.sync(() => {
20
+ g.push(o);
21
+ })), c.flatMap((l) => l === void 0 ? c.void : s(l, t.assetsDir).pipe(c.flatMap((t) => {
22
+ let s = {
23
+ destination: i.destination,
24
+ tenantId: i.tenantId,
25
+ key: i.key,
17
26
  sha256: t.sha256,
18
27
  size: t.size,
19
28
  contentType: t.contentType
20
29
  };
21
- return o++, l += t.size, f.has(t.sha256) || (f.add(t.sha256), d += t.size), a.mark(e.key, n);
22
- }))))))).pipe(r.mapError((t) => t instanceof e ? t : new e({ reason: `asset export failed: ${String(t)}` }))), {
23
- count: o,
24
- totalBytes: l,
25
- references: u,
26
- objects: f.size,
27
- objectBytes: d,
28
- missing: m
30
+ return t.size !== i.size || t.contentType !== i.contentType || i.sha256 !== void 0 && t.sha256 !== i.sha256 ? c.fail(new e({ reason: "asset bytes contradict their source metadata" })) : (f.set(o, {
31
+ ...i,
32
+ sha256: t.sha256
33
+ }), r++, a += t.size, d.has(t.sha256) || (d.add(t.sha256), u += t.size), n.mark(o, s));
34
+ }))));
35
+ return yield* l.runForEach(t.source.list().pipe(l.mapEffect(i)), _).pipe(c.mapError((t) => t instanceof e ? t : new e({ reason: `asset export failed: ${String(t)}` }))), {
36
+ count: r,
37
+ totalBytes: a,
38
+ references: o,
39
+ objects: d.size,
40
+ objectBytes: u,
41
+ missing: g
29
42
  };
30
- }), u = (t) => o("bundle", () => a(t)).pipe(i.decodeText(), i.splitLines, i.filter((e) => e.trim().length > 0), i.map((e) => JSON.parse(e)), i.mapError((n) => n instanceof e ? n : new e({
31
- reason: `asset index unreadable: ${String(n)}`,
32
- path: t
33
- }))), d = (e) => r.gen(function* () {
34
- let n = e.progress ?? s, a = 0, o = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
35
- return yield* i.runForEach(u(e.indexPath), (i) => n.done(i.key) || c.has(i.key) ? r.void : (c.add(i.key), t(e.sink, e.assetsDir, i)).pipe(r.andThen(r.sync(() => {
36
- a++, o.add(i.sha256);
37
- })), r.andThen(n.mark(i.key, i)))), {
38
- count: a,
39
- objects: o.size
43
+ }), _ = (n) => l.unwrap(c.gen(function* () {
44
+ let r = yield* c.acquireRelease(c.tryPromise({
45
+ try: async () => {
46
+ await t(n, "file");
47
+ let e = await d(n, u.O_RDONLY | u.O_NOFOLLOW | u.O_NONBLOCK);
48
+ try {
49
+ if (!(await e.stat()).isFile()) throw Error("asset index must be a regular file");
50
+ return e;
51
+ } catch (t) {
52
+ throw await e.close(), t;
53
+ }
54
+ },
55
+ catch: () => new e({
56
+ reason: "asset index could not be admitted",
57
+ path: n
58
+ })
59
+ }), (e) => c.promise(() => e.close()));
60
+ return f("bundle", () => r.createReadStream({ autoClose: !1 }));
61
+ })).pipe(l.decodeText(), l.splitLines, l.filter((e) => e.trim().length > 0), l.mapEffect((t) => c.try({
62
+ try: () => JSON.parse(t),
63
+ catch: () => new e({
64
+ reason: "asset index contains invalid JSON",
65
+ path: n
66
+ })
67
+ }).pipe(c.flatMap(r))), l.mapError((t) => t instanceof e ? t : new e({
68
+ reason: `asset index unreadable: ${String(t)}`,
69
+ path: n
70
+ }))), v = (e) => c.scoped(c.gen(function* () {
71
+ let t = e.prepared ?? (yield* y(e));
72
+ yield* n(t, e.sink);
73
+ let r = e.progress ?? m, i = 0, a = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set();
74
+ for (let n of t.entries) {
75
+ let l = p(n);
76
+ s.has(l) || (s.add(l), r.done(l) && (yield* c.logDebug("Revalidating previously recorded destination asset")), (yield* e.sink.matches(n)) || (yield* o(e.sink, t, n), i++, a.add(n.sha256)), yield* r.mark(l, n));
77
+ }
78
+ return {
79
+ count: i,
80
+ objects: a.size
40
81
  };
41
- });
82
+ })), y = (e) => l.runCollect(_(e.indexPath)).pipe(c.flatMap((t) => a({
83
+ ...e,
84
+ entries: Array.from(t)
85
+ })));
42
86
  //#endregion
43
- export { l as captureAssetsToCas, s as noAssetProgress, u as readAssetIndex, d as restoreAssetsFromCas };
87
+ export { g as captureAssetsToCas, m as noAssetProgress, y as prepareAssetsFromCas, _ as readAssetIndex, v as restoreAssetsFromCas };
package/dist/compress.js CHANGED
@@ -1,6 +1,6 @@
1
- import * as e from "node:zlib";
2
- import { PassThrough as t } from "node:stream";
1
+ import { PassThrough as e } from "node:stream";
2
+ import * as t from "node:zlib";
3
3
  //#region src/compress.ts
4
- var n = () => typeof e.createZstdCompress == "function", r = (e) => e === "none" ? "none" : e === "gzip" ? "gzip" : n() ? "zstd" : "gzip", i = (e) => e === "zstd" ? ".zst" : e === "gzip" ? ".gz" : "", a = (n) => n === "zstd" ? e.createZstdCompress() : n === "gzip" ? e.createGzip() : new t(), o = (n) => n === "zstd" ? e.createZstdDecompress() : n === "gzip" ? e.createGunzip() : new t();
4
+ var n = () => typeof t.createZstdCompress == "function", r = (e) => e === "none" ? "none" : e === "gzip" ? "gzip" : n() ? "zstd" : "gzip", i = (e) => e === "zstd" ? ".zst" : e === "gzip" ? ".gz" : "", a = (n) => n === "zstd" ? t.createZstdCompress() : n === "gzip" ? t.createGzip() : new e(), o = (n) => n === "zstd" ? t.createZstdDecompress() : n === "gzip" ? t.createGunzip() : new e();
5
5
  //#endregion
6
6
  export { i as compressionExt, n as hasZstd, a as makeCompressor, o as makeDecompressor, r as pickCompression };