@voltro/plugin-storage 0.85.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +77 -0
  3. package/THIRD-PARTY-NOTICES.md +860 -249
  4. package/dist/access.js +47 -0
  5. package/dist/assetRef.js +13 -0
  6. package/dist/avatar.js +39 -0
  7. package/dist/byteSecurity.d.ts +3 -0
  8. package/dist/byteSecurity.js +7 -0
  9. package/dist/connection.js +99 -0
  10. package/dist/constraints.js +93 -0
  11. package/dist/custody.js +132 -0
  12. package/dist/derivatives.js +30 -0
  13. package/dist/env.js +162 -0
  14. package/dist/grant.js +132 -0
  15. package/dist/index.d.ts +525 -203
  16. package/dist/index.js +765 -3196
  17. package/dist/ingest.js +87 -0
  18. package/dist/ingestPolicy.js +1 -0
  19. package/dist/metadataError.js +19 -0
  20. package/dist/objectAbandon.js +16 -0
  21. package/dist/objectLifecycle.js +243 -0
  22. package/dist/objectMetadata.js +308 -0
  23. package/dist/objectWriter.js +87 -0
  24. package/dist/pathDurability.js +16 -0
  25. package/dist/physicalReferences.js +54 -0
  26. package/dist/providers.js +638 -0
  27. package/dist/referenceCatalogue.js +31 -0
  28. package/dist/renditionProof.js +82 -0
  29. package/dist/retainedCapture.js +391 -0
  30. package/dist/retainedObjects.js +190 -0
  31. package/dist/rpc.d.ts +3 -15
  32. package/dist/rpc.js +3 -1
  33. package/dist/scanners.js +46 -0
  34. package/dist/server.d.ts +153 -0
  35. package/dist/server.js +4 -0
  36. package/dist/service.js +1418 -0
  37. package/dist/sourceIdentity.js +14 -0
  38. package/dist/sourcePin.js +35 -0
  39. package/dist/storageNamespaces.js +80 -0
  40. package/dist/storageNumbers.js +13 -0
  41. package/dist/storageObjectNamespaces.js +243 -0
  42. package/dist/storageReferenceInstances.js +52 -0
  43. package/dist/storageRequest.js +10 -0
  44. package/dist/storageRouting.js +46 -0
  45. package/dist/streamBridge.js +33 -0
  46. package/dist/table.js +54 -0
  47. package/dist/transcodeVideo.js +91 -0
  48. package/dist/transform.js +94 -0
  49. package/dist/transformCache.js +31 -0
  50. package/dist/types.d.ts +116 -134
  51. package/dist/types.js +14 -14
  52. package/package.json +17 -6
@@ -0,0 +1,14 @@
1
+ import { createHash as e } from "node:crypto";
2
+ import { canonicalJson as t } from "@voltro/protocol";
3
+ //#region src/sourceIdentity.ts
4
+ var n = (n) => e("sha256").update(t({
5
+ id: n.id,
6
+ tenantId: n.tenantId,
7
+ bucket: n.bucket,
8
+ key: n.key,
9
+ contentType: n.contentType,
10
+ size: n.size,
11
+ checksum: n.checksum
12
+ })).digest("hex");
13
+ //#endregion
14
+ export { n as retainedSourceDigest };
@@ -0,0 +1,35 @@
1
+ import { StorageError as e } from "./types.js";
2
+ import { retainedSourceDigest as t } from "./sourceIdentity.js";
3
+ import { ownRenditionRef as n } from "./renditionProof.js";
4
+ import { createHash as r } from "node:crypto";
5
+ import { canonicalJson as i } from "@voltro/protocol";
6
+ //#region src/sourcePin.ts
7
+ var a = (e) => r("sha256").update(i(e)).digest("hex"), o = (e, t) => typeof e == "string" && e.length > 0 && e.length <= t, s = (e, t) => e == null || typeof e == "string" && e.length <= t, c = (r, i) => {
8
+ let c = n(r);
9
+ if (!o(i, 256) || !o(c.id, 64) || !o(c.bucket, 128) || c.tenantId !== null && !o(c.tenantId, 64) || !o(c.checksum, 512) || !o(c.contentType, 512) || !Number.isSafeInteger(c.size) || c.size < 0 || !s(c.derivedFrom, 64) || !s(c.kind, 512)) throw new e({
10
+ provider: "custody",
11
+ message: "Invalid custody source metadata",
12
+ status: 409,
13
+ transient: !1
14
+ });
15
+ let l = {
16
+ version: 1,
17
+ instance: i,
18
+ tenantId: c.tenantId,
19
+ bucket: c.bucket,
20
+ id: c.id,
21
+ sourceDigest: t(c),
22
+ checksum: c.checksum,
23
+ size: c.size,
24
+ contentType: c.contentType,
25
+ derivedFrom: c.derivedFrom ?? null,
26
+ kind: c.kind ?? null,
27
+ renditionProof: c.renditionProof ?? null
28
+ };
29
+ return {
30
+ ...l,
31
+ digest: a(l)
32
+ };
33
+ };
34
+ //#endregion
35
+ export { c as pinStorageSource };
@@ -0,0 +1,80 @@
1
+ import { StorageError as e } from "@voltro/kv/storage";
2
+ import { Effect as t } from "effect";
3
+ import { createHash as n, randomUUID as r } from "node:crypto";
4
+ import { authoredError as i } from "@voltro/database";
5
+ import { linkSync as a, mkdirSync as o, readFileSync as s, realpathSync as c, rmSync as l, writeFileSync as u } from "node:fs";
6
+ import { basename as d, dirname as f, join as p, resolve as m } from "node:path";
7
+ //#region src/storageNamespaces.ts
8
+ var h = (e) => n("sha256").update(JSON.stringify(e)).digest("hex"), g = (n, r) => ("resolveNamespace" in n ? n.resolveNamespace(r) : t.succeed(n.objectNamespace)).pipe(t.flatMap((r) => t.try({
9
+ try: () => {
10
+ let e = {
11
+ storageInstance: n.storageInstance,
12
+ objectNamespace: r
13
+ };
14
+ return _(e), e;
15
+ },
16
+ catch: () => new e({
17
+ provider: "metadata",
18
+ message: "Storage reference scope is invalid",
19
+ transient: !1
20
+ })
21
+ }))), _ = (e) => {
22
+ if (!e?.storageInstance?.trim() || !/^[a-f0-9]{64}$/.test(e.objectNamespace)) throw i("Storage requires a configured instance and a stable physical objectNamespace SHA-256 identity");
23
+ }, v = (e) => {
24
+ let t = m(e), n = [];
25
+ for (;;) try {
26
+ return p(c(t), ...n.reverse());
27
+ } catch (e) {
28
+ if (!(e instanceof Error && "code" in e && e.code === "ENOENT")) throw e;
29
+ let r = f(t);
30
+ if (r === t) throw e;
31
+ n.push(d(t)), t = r;
32
+ }
33
+ }, y = (e) => {
34
+ let t = new URL(e);
35
+ return t.username = "", t.password = "", t.search = "", t.hash = "", t.href.replace(/\/+$/, "");
36
+ }, b = ".voltro-object-namespace", x = (e) => {
37
+ o(e, { recursive: !0 });
38
+ let t = p(e, b), n;
39
+ try {
40
+ n = s(t, "utf8");
41
+ } catch (e) {
42
+ if (!(e instanceof Error && "code" in e && e.code === "ENOENT")) throw e;
43
+ let i = h(["filesystem-volume", r()]), o = `${t}-${r()}`;
44
+ try {
45
+ u(o, i, {
46
+ flag: "wx",
47
+ mode: 384
48
+ });
49
+ try {
50
+ a(o, t);
51
+ } catch (e) {
52
+ if (!(e instanceof Error && "code" in e && e.code === "EEXIST")) throw e;
53
+ }
54
+ n = s(t, "utf8");
55
+ } finally {
56
+ l(o, { force: !0 });
57
+ }
58
+ }
59
+ if (!/^[a-f0-9]{64}$/.test(n)) throw i("Filesystem storage object namespace marker is invalid; restore the volume marker before serving storage");
60
+ return n;
61
+ }, S = (e) => {
62
+ let t;
63
+ try {
64
+ t = s(p(e, b), "utf8");
65
+ } catch (t) {
66
+ throw t instanceof Error && "code" in t && t.code === "ENOENT" ? i(`Filesystem storage volume at ${e} carries no ${b} marker; mount the volume the serving replicas use before assigning namespaces`) : t;
67
+ }
68
+ if (!/^[a-f0-9]{64}$/.test(t)) throw i("Filesystem storage object namespace marker is invalid; restore the volume marker before serving storage");
69
+ return t;
70
+ }, C = (e) => {
71
+ if (_(e), typeof e.catalogueId != "string" || !e.catalogueId.trim()) throw i("Storage upload tickets require an actual reference catalogue identity");
72
+ return h([
73
+ "upload-ticket-destination",
74
+ e.storageInstance,
75
+ e.catalogueId,
76
+ e.objectNamespace
77
+ ]);
78
+ };
79
+ //#endregion
80
+ export { b as FILESYSTEM_NAMESPACE_FILE, v as canonicalStorageRoot, S as existingFilesystemObjectNamespace, x as filesystemObjectNamespace, _ as requireStorageReferenceScope, y as storageEndpointIdentity, h as storageObjectNamespace, g as storageReferenceScopeFor, C as storageUploadTicketScope };
@@ -0,0 +1,13 @@
1
+ import { StorageError as e } from "./types.js";
2
+ //#region src/storageNumbers.ts
3
+ var t = (t, n) => {
4
+ let r = typeof t == "number" ? t : typeof t == "bigint" || typeof t == "string" && /^\d+$/.test(t) ? Number(t) : NaN;
5
+ if (!Number.isSafeInteger(r) || r < 0) throw new e({
6
+ provider: "metadata",
7
+ message: `Storage ${n} must be a non-negative safe integer at most Number.MAX_SAFE_INTEGER`,
8
+ transient: !1
9
+ });
10
+ return r;
11
+ }, n = (e, n, r) => t(e + n, r);
12
+ //#endregion
13
+ export { t as storageInteger, n as storageIntegerSum };
@@ -0,0 +1,243 @@
1
+ import { requireStorageReferenceScope as e } from "./storageNamespaces.js";
2
+ import { STORAGE_GRANTS_TABLE as t, STORAGE_REFS_TABLE as n } from "./table.js";
3
+ import { storageInteger as r, storageIntegerSum as i } from "./storageNumbers.js";
4
+ import { storageObjectClaimId as a, storageObjectIdentifiers as o } from "./objectLifecycle.js";
5
+ import { Schema as s } from "effect";
6
+ import { createHash as c } from "node:crypto";
7
+ import { authoredError as l, inSet as u, isNull as d, or as f } from "@voltro/database";
8
+ //#region src/storageObjectNamespaces.ts
9
+ var p = "_voltro_storage_put_intents", m = "_voltro_storage_operations", h = "_voltro_storage_object_heads", g = "_voltro_storage_object_claims", _ = [
10
+ n,
11
+ t,
12
+ p,
13
+ m
14
+ ], v = s.Struct({
15
+ tenantId: s.NullOr(s.String),
16
+ bucket: s.String,
17
+ operationId: s.String,
18
+ fingerprint: s.String,
19
+ checksum: s.String,
20
+ logicalKey: s.String
21
+ }), y = s.Struct({
22
+ id: s.String,
23
+ objectId: s.String,
24
+ fingerprint: s.String,
25
+ generation: s.String,
26
+ key: s.String,
27
+ refId: s.String,
28
+ state: s.Literals([
29
+ "reserved",
30
+ "ready",
31
+ "deleting",
32
+ "deleted"
33
+ ]),
34
+ deleteKey: s.NullOr(s.String)
35
+ }), b = (e) => c("sha256").update(JSON.stringify(e)).digest("hex"), x = (e) => JSON.stringify([e.storageInstance, e.objectNamespace]), S = (e, t) => JSON.stringify([e.objectNamespace, t]), C = (e, t) => e.query({
36
+ table: t,
37
+ predicate: void 0,
38
+ lock: "update",
39
+ order: [],
40
+ take: void 0,
41
+ skip: void 0,
42
+ projection: void 0
43
+ }), w = 500, T = (e) => {
44
+ let t = [];
45
+ for (let n = 0; n < e.length; n += w) t.push(e.slice(n, n + w));
46
+ return t;
47
+ }, E = async (e) => {
48
+ let t = [];
49
+ for (let n of [..._, h]) {
50
+ let r = n === h ? d("objectNamespace") : f(d("storageInstance"), d("objectNamespace")), [i] = await e.query({
51
+ table: n,
52
+ predicate: r,
53
+ order: [],
54
+ take: void 0,
55
+ skip: void 0,
56
+ projection: void 0,
57
+ aggregations: [{
58
+ op: "count",
59
+ alias: "unassigned"
60
+ }]
61
+ }), a = Number(i?.unassigned ?? 0);
62
+ a > 0 && t.push(`${n}: ${a}`);
63
+ }
64
+ if (t.length > 0) throw l(`Storage physical namespace migration required: assign references and retained operations to their configured storageInstance and provider objectNamespace before starting the app (unassigned rows — ${t.join(", ")}). Rows written after an assignment already ran — by a replica of the previous version — need the assignment again: a recorded file migration does not re-run, so repeat the idempotent assignment over every reference in a new migration.`);
65
+ }, D = async (e, t, n) => k(e, t, n), O = async (t, n, r) => {
66
+ let i = /* @__PURE__ */ new Map();
67
+ for (let t of r) {
68
+ if (e(t.from), t.from.storageInstance !== t.storageInstance) throw l("Storage snapshot relocation must preserve the configured plugin instance");
69
+ let n = i.get(t.refId);
70
+ if (n && x(n) !== x(t.from)) throw l("Storage snapshot source scope is ambiguous");
71
+ i.set(t.refId, t.from);
72
+ }
73
+ return k(t, n, r.map(({ from: e, ...t }) => t), i);
74
+ }, k = async (c, d, f, _) => {
75
+ for (let t of d) e(t);
76
+ if (!d.length || new Set(d.map(x)).size !== d.length) throw l("Provide distinct configured storage instance and physical namespace pairs");
77
+ let w = new Set(d.map(x)), D = /* @__PURE__ */ new Map();
78
+ for (let { refId: t, ...n } of f) {
79
+ if (e(n), !t.trim() || !w.has(x(n))) throw l("Storage namespace assignment must name a reference and a configured physical scope");
80
+ let r = D.get(t);
81
+ if (r && x(r) !== x(n)) throw l("Storage physical namespace assignment is ambiguous");
82
+ D.set(t, n);
83
+ }
84
+ let O = [];
85
+ for (let e of [
86
+ n,
87
+ t,
88
+ p,
89
+ m,
90
+ h,
91
+ g
92
+ ]) O.push(await C(c, e));
93
+ let [k, A, j, M, N, P] = O, F = /* @__PURE__ */ new Set(), I = (e, t) => {
94
+ let n = D.get(e);
95
+ if (!n) throw l("Storage physical namespace assignment is incomplete");
96
+ if (_ !== void 0) {
97
+ let r = _.get(e);
98
+ if (r === void 0 || ![r, n].some((e) => t.storageInstance === e.storageInstance && t.objectNamespace === e.objectNamespace)) throw l("Storage snapshot metadata does not match its declared source or restored target scope");
99
+ } else for (let e of ["storageInstance", "objectNamespace"]) if (t[e] != null && t[e] !== n[e]) throw l("Storage metadata already belongs to another physical scope; assignment does not move objects");
100
+ return F.add(e), n;
101
+ };
102
+ for (let e of k) I(String(e.id), e);
103
+ let L = new Map(k.map((e) => [e.id, e]));
104
+ for (let e of A) if (I(String(e.refId), e), !L.has(e.refId)) throw l("Storage grant has no readable reference");
105
+ let R = /* @__PURE__ */ new Map();
106
+ for (let e of j) {
107
+ if (I(String(e.refId), e), R.has(String(e.id))) throw l("Storage put intent ownership is ambiguous");
108
+ R.set(String(e.id), e);
109
+ }
110
+ let z = /* @__PURE__ */ new Map(), B = M.map((e) => {
111
+ let t = s.decodeUnknownSync(y)(e), n = I(t.refId, e), a = R.get(t.id);
112
+ if (!a || a.refId !== t.refId) throw l("Storage operation lost its retained put intent");
113
+ let c = {
114
+ ...s.decodeUnknownSync(v)(a.identity),
115
+ ...n
116
+ };
117
+ typeof a.identity == "object" && a.identity !== null && I(t.refId, a.identity);
118
+ let u = o(c), d = b([
119
+ c.bucket,
120
+ c.tenantId,
121
+ c.operationId
122
+ ]), f = b([
123
+ c.bucket,
124
+ c.tenantId,
125
+ c.logicalKey,
126
+ c.checksum
127
+ ]), p = _ === void 0 ? void 0 : o({
128
+ ...c,
129
+ storageInstance: String(e.storageInstance),
130
+ objectNamespace: String(e.objectNamespace)
131
+ });
132
+ if (!(p === void 0 ? [d, u.id].includes(t.id) && [f, u.objectId].includes(t.objectId) : t.id === p.id && t.objectId === p.objectId) || t.fingerprint !== c.fingerprint) throw l("Storage operation identity does not match its retained intent");
133
+ if (typeof a.ref != "object" || a.ref === null || Array.isArray(a.ref)) throw l("Storage put intent reference is unreadable");
134
+ let m = a.ref;
135
+ if (m.id !== t.refId || m.key !== t.key || m.checksum !== c.checksum || m.tenantId !== c.tenantId || m.bucket !== c.bucket) throw l("Storage operation reference does not match its retained intent");
136
+ let h = L.get(t.refId);
137
+ if (t.state === "ready") {
138
+ if (!h || h.key !== t.key || h.checksum !== c.checksum) throw l("Ready storage operation lost its readable reference");
139
+ } else if (h) throw l("Unreadable storage operation still owns a readable reference");
140
+ if (t.state === "ready" || t.state === "reserved") {
141
+ let e = z.get(u.objectId);
142
+ if (e && (e.generation !== t.generation || e.key !== t.key)) throw l("Storage physical namespace has conflicting active generations");
143
+ z.set(u.objectId, {
144
+ id: u.objectId,
145
+ objectNamespace: n.objectNamespace,
146
+ generation: t.generation,
147
+ key: t.key,
148
+ reservations: String(i(e ? r(e.reservations, "shared-object reservation count") : 0, 1, "shared-object reservation count"))
149
+ });
150
+ }
151
+ return {
152
+ previous: t,
153
+ previousNamespace: e.objectNamespace,
154
+ row: {
155
+ ...t,
156
+ ...n,
157
+ id: u.id,
158
+ objectId: u.objectId
159
+ },
160
+ intent: a,
161
+ identity: c
162
+ };
163
+ });
164
+ if (B.length !== j.length || new Set(B.map((e) => e.row.id)).size !== B.length) throw l("Storage operation namespace migration has unmatched or conflicting identities");
165
+ let V = /* @__PURE__ */ new Map();
166
+ for (let e of B) (e.previous.state === "ready" || e.previous.state === "reserved") && V.set(e.previous.objectId, [...V.get(e.previous.objectId) ?? [], e]);
167
+ for (let e of N) {
168
+ let t = V.get(String(e.id)) ?? [];
169
+ if (!t.length || t.length !== r(e.reservations, "shared-object reservation count") || t.some(({ previous: t, previousNamespace: n, identity: r }) => t.generation !== e.generation || t.key !== e.key || e.objectNamespace != null && e.objectNamespace !== (_ === void 0 ? r.objectNamespace : n))) throw l("Storage object head does not match its active reservations");
170
+ }
171
+ let H = new Set(N.map((e) => e.id));
172
+ for (let { previous: e } of B) if ((e.state === "ready" || e.state === "reserved") && !H.has(e.objectId)) throw l("Storage operation lost its active object head");
173
+ if ([...D.keys()].some((e) => !F.has(e))) throw l("Storage namespace assignment target does not exist");
174
+ let U = new Map(B.map((e) => [e.previous.id, e])), W = P.map((e) => {
175
+ let t = U.get(String(e.operationId));
176
+ if (!t || typeof e.claim != "string" || !e.claim || e.id !== a(String(e.operationId), e.claim)) throw l("Storage operation claim does not match its retained operation");
177
+ return {
178
+ ...e,
179
+ id: a(t.row.id, e.claim),
180
+ operationId: t.row.id
181
+ };
182
+ }), G = /* @__PURE__ */ new Set([...k.map((e) => S(D.get(String(e.id)), String(e.key))), ...[...z.values()].map((e) => JSON.stringify([e.objectNamespace, e.key]))]), K = [];
183
+ for (let e of B) {
184
+ let t = G.has(S(e.identity, e.row.key));
185
+ (e.row.state === "deleting" || e.row.state === "deleted") && (t ? e.row.deleteKey = null : e.row.state === "deleted" && e.row.deleteKey === null && (e.row.state = "deleting", e.row.deleteKey = e.row.key), e.row.state === "deleting" && e.row.deleteKey !== null && K.push({
186
+ refId: e.row.refId,
187
+ storageInstance: e.identity.storageInstance,
188
+ objectNamespace: e.identity.objectNamespace
189
+ }));
190
+ }
191
+ let q = 0;
192
+ for (let [e, r, i] of [[
193
+ n,
194
+ k,
195
+ "id"
196
+ ], [
197
+ t,
198
+ A,
199
+ "refId"
200
+ ]]) {
201
+ let t = /* @__PURE__ */ new Map();
202
+ for (let e of r) {
203
+ let n = D.get(String(e[i]));
204
+ if (e.storageInstance === n.storageInstance && e.objectNamespace === n.objectNamespace) continue;
205
+ let r = t.get(x(n)) ?? {
206
+ scope: n,
207
+ ids: []
208
+ };
209
+ r.ids.push(String(e.id)), t.set(x(n), r);
210
+ }
211
+ for (let { scope: n, ids: r } of t.values()) for (let t of T(r)) {
212
+ let r = await c.updateMany(e, {
213
+ storageInstance: n.storageInstance,
214
+ objectNamespace: n.objectNamespace
215
+ }, { where: u("id", t) });
216
+ if (r !== t.length) throw l("Storage physical namespace assignment lost its metadata row");
217
+ q += r;
218
+ }
219
+ }
220
+ for (let [e, t] of [
221
+ [g, P],
222
+ [p, j],
223
+ [m, M],
224
+ [h, N]
225
+ ]) for (let n of T(t.map((e) => String(e.id)))) if (await c.deleteMany(e, { where: u("id", n) }) !== n.length) throw l("Storage physical namespace assignment lost a retained row");
226
+ let J = async (e, t) => {
227
+ for (let n of T(t)) await c.insertMany(e, n, { returning: "none" });
228
+ };
229
+ await J(h, [...z.values()]), await J(m, B.map((e) => e.row)), await J(p, B.map((e) => ({
230
+ ...e.intent,
231
+ id: e.row.id,
232
+ storageInstance: e.identity.storageInstance,
233
+ objectNamespace: e.identity.objectNamespace,
234
+ identity: e.identity
235
+ })));
236
+ for (let e of B) (e.previous.id !== e.row.id || e.intent.objectNamespace !== e.identity.objectNamespace || e.previous.state !== e.row.state || e.previous.deleteKey !== e.row.deleteKey) && q++;
237
+ return await J(g, W), await E(c), {
238
+ assigned: q,
239
+ pendingDeletes: K
240
+ };
241
+ };
242
+ //#endregion
243
+ export { D as assignStorageObjectNamespacesInTransaction, O as relocateRestoredStorageObjectNamespacesInTransaction, E as requireStorageObjectNamespaces };
@@ -0,0 +1,52 @@
1
+ import { STORAGE_GRANTS_TABLE as e, STORAGE_REFS_TABLE as t } from "./table.js";
2
+ import { authoredError as n, eq as r, isNull as i } from "@voltro/database";
3
+ //#region src/storageReferenceInstances.ts
4
+ var a = "@voltro/plugin-storage", o = [
5
+ t,
6
+ e,
7
+ "_voltro_storage_put_intents"
8
+ ], s = async (e) => {
9
+ for (let t of o) if ((await e.query({
10
+ table: t,
11
+ predicate: i("storageInstance"),
12
+ order: [],
13
+ take: 1,
14
+ skip: void 0,
15
+ projection: ["id"]
16
+ })).length) throw n("Storage reference ownership migration required: assign existing references, grants and put intents to their configured storageInstance before starting the app");
17
+ }, c = async (e, t, i) => {
18
+ if (t.length === 0 || t.some((e) => !e.trim()) || new Set(t).size !== t.length) throw n("Provide distinct configured storage instance names");
19
+ let a = new Set(t), s = /* @__PURE__ */ new Map();
20
+ for (let e of i) {
21
+ if (!e.refId.trim() || !a.has(e.storageInstance)) throw n("Storage reference assignment must name a reference and a configured instance");
22
+ let t = s.get(e.refId);
23
+ if (t !== void 0 && t !== e.storageInstance) throw n("Storage reference assignment is ambiguous");
24
+ s.set(e.refId, e.storageInstance);
25
+ }
26
+ let c = e, l = 0;
27
+ for (let [e, t] of s) {
28
+ let i = !1;
29
+ for (let a of o) {
30
+ let o = await c.query({
31
+ table: a,
32
+ predicate: r(a === "_voltro_storage_refs" ? "id" : "refId", e),
33
+ lock: "update",
34
+ order: [],
35
+ take: void 0,
36
+ skip: void 0,
37
+ projection: void 0
38
+ });
39
+ for (let e of o) {
40
+ if (i = !0, e.storageInstance !== null && e.storageInstance !== void 0 && e.storageInstance !== t) throw n("Storage reference already belongs to another instance; assignment does not move objects");
41
+ if (e.storageInstance !== t) {
42
+ if (await c.update(a, String(e.id), { storageInstance: t }) === null) throw n("Storage reference assignment lost its metadata row");
43
+ l++;
44
+ }
45
+ }
46
+ }
47
+ if (!i) throw n("Storage reference assignment target does not exist");
48
+ }
49
+ return { assigned: l };
50
+ };
51
+ //#endregion
52
+ export { a as DEFAULT_STORAGE_INSTANCE, c as assignStorageReferenceInstancesInTransaction, s as requireStorageReferenceOwnership };
@@ -0,0 +1,10 @@
1
+ import { Effect as e } from "effect";
2
+ //#region src/storageRequest.ts
3
+ var t = (t, n, r) => e.callback((i) => {
4
+ let a = new AbortController(), o = Promise.resolve().then(() => t(a.signal));
5
+ return o.then((t) => i(e.succeed(t)), (t) => i(e.fail(n(t)))), e.sync(() => {
6
+ a.abort();
7
+ }).pipe(e.andThen(e.promise(() => o.then((e) => ({ value: e }), () => void 0))), e.flatMap((t) => t !== void 0 && r !== void 0 ? r(t.value) : e.void));
8
+ });
9
+ //#endregion
10
+ export { t as storageRequest };
@@ -0,0 +1,46 @@
1
+ import { StorageAddress as e, StorageError as t } from "@voltro/kv/storage";
2
+ import { Effect as n, Schema as r } from "effect";
3
+ //#region src/storageRouting.ts
4
+ var i = () => new t({
5
+ provider: "storage",
6
+ message: "Storage destination is unknown, ambiguous or unavailable for this tenant",
7
+ transient: !1
8
+ }), a = (e, t, r) => n.suspend(() => {
9
+ if (e === void 0 || !t.startsWith("storage:")) return n.fail(i());
10
+ let a = t;
11
+ return e.validate(a).pipe(n.mapError(() => i()), n.flatMap((t) => t.tenantScoped && r === null ? n.fail(i()) : e.storage(a, r === null ? void 0 : { tenantId: r }).pipe(n.mapError(() => i()))));
12
+ }), o = (t) => {
13
+ let o = (e, r) => e.startsWith("storage:") ? a(t.registry(), e, r) : e === t.directDestination ? n.try({
14
+ try: t.directProvider,
15
+ catch: () => i()
16
+ }) : n.fail(i());
17
+ return {
18
+ select: (a) => n.suspend(() => {
19
+ let s = t.selected();
20
+ return r.decodeUnknownEffect(e)({
21
+ destination: s,
22
+ tenantId: a,
23
+ key: "selection"
24
+ }).pipe(n.mapError(() => i()), n.flatMap((e) => o(e.destination, e.tenantId)), n.map((e) => ({
25
+ destination: s,
26
+ provider: e
27
+ })));
28
+ }),
29
+ resolve: (t) => r.decodeUnknownEffect(e)(t).pipe(n.mapError(() => i()), n.flatMap((e) => o(e.destination, e.tenantId)))
30
+ };
31
+ }, s = (t) => {
32
+ let o = t.flatMap((e) => e.capabilities?.storageDestinations === void 0 ? [] : [e.capabilities.storageDestinations]);
33
+ return {
34
+ select: (e) => o.length === 1 ? o[0].router.select(e) : n.fail(i()),
35
+ resolve: (t) => r.decodeUnknownEffect(e)(t).pipe(n.mapError(() => i()), n.flatMap((e) => {
36
+ if (e.destination.startsWith("storage:")) {
37
+ let t = new Set(o.map((e) => e.registry()).filter((e) => e !== void 0));
38
+ return t.size === 1 ? a([...t][0], e.destination, e.tenantId) : n.fail(i());
39
+ }
40
+ let t = o.filter((t) => t.directDestination === e.destination);
41
+ return t.length === 1 ? t[0].router.resolve(e) : n.fail(i());
42
+ }))
43
+ };
44
+ };
45
+ //#endregion
46
+ export { s as appStorageRouter, o as makeStorageDestinationRouter };
@@ -0,0 +1,33 @@
1
+ import { StorageError as e } from "./types.js";
2
+ import { Effect as t, Stream as n } from "effect";
3
+ //#region src/streamBridge.ts
4
+ var r = 1 << 20, i = (t, n, r = !1) => new e({
5
+ provider: t,
6
+ message: n,
7
+ transient: r
8
+ }), a = (e) => {
9
+ let t = 0;
10
+ for (let n of e) t += n.byteLength;
11
+ let n = new Uint8Array(t), r = 0;
12
+ for (let t of e) n.set(t, r), r += t.byteLength;
13
+ return n;
14
+ }, o = (e, t = r) => {
15
+ let i = [];
16
+ for (let n = 0; n < e.byteLength; n += t) i.push(e.subarray(n, Math.min(n + t, e.byteLength)));
17
+ return n.fromIterable(i);
18
+ }, s = (e) => e.pipe(n.runCollect, t.map((e) => a(e))), c = (e, t) => n.suspend(() => n.fromAsyncIterable(t(), (t) => i(e, `read stream failed: ${String(t?.message ?? t)}`, !0))).pipe(n.map((e) => e instanceof Uint8Array ? e : new Uint8Array(e))), l = async (e) => {
19
+ let { Readable: t } = await import("node:stream");
20
+ return t.fromWeb(n.toReadableStream(e));
21
+ }, u = (e, r) => e.getStream ? e.getStream(r).pipe(t.map((e) => e.close === void 0 ? e : {
22
+ ...e,
23
+ stream: e.stream.pipe(n.ensuring(e.close))
24
+ })) : e.get(r).pipe(t.map((e) => ({
25
+ stream: o(e.bytes),
26
+ contentType: e.contentType,
27
+ size: e.bytes.byteLength
28
+ }))), d = (e, n, r, i) => e.putStream ? e.putStream(n, r, i) : s(r).pipe(t.flatMap((t) => e.put(n, t, {
29
+ contentType: i.contentType,
30
+ ...i.public === void 0 ? {} : { public: i.public }
31
+ })));
32
+ //#endregion
33
+ export { o as bytesToStream, s as collectStream, a as concatBytes, c as fromNodeReadable, u as getObjectStream, d as putObjectStream, l as toNodeReadable };
package/dist/table.js ADDED
@@ -0,0 +1,54 @@
1
+ import { bigint as e, bytes as t, id as n, integer as r, json as i, table as a, text as o, timestamp as s } from "@voltro/database";
2
+ //#region src/table.ts
3
+ var c = "_voltro_storage_refs", l = "_voltro_storage_grants", u = "_voltro_storage_blobs", d = "_voltro_storage_usage", f = a(c, {
4
+ id: n({ prefix: "file" }),
5
+ storageInstance: o().nullable(),
6
+ objectNamespace: o().maxLength(64).nullable(),
7
+ tenantId: o().nullable(),
8
+ ownerId: o().nullable(),
9
+ bucket: o(),
10
+ key: o(),
11
+ contentType: o(),
12
+ size: e(),
13
+ checksum: o(),
14
+ visibility: o(),
15
+ accessPolicy: i().nullable(),
16
+ passwordHash: o().nullable(),
17
+ width: r().nullable(),
18
+ height: r().nullable(),
19
+ duration: r().nullable(),
20
+ placeholder: o().nullable(),
21
+ folder: o().nullable(),
22
+ tags: i().nullable(),
23
+ alt: o().nullable(),
24
+ caption: o().nullable(),
25
+ derivedFrom: o().nullable(),
26
+ kind: o().nullable(),
27
+ renditionProof: i().nullable(),
28
+ createdAt: s()
29
+ }).index(["storageInstance"]).index(["objectNamespace"]).index(["key"]).index(["tenantId"]).index(["ownerId"]).index(["folder"]).index(["derivedFrom"]), p = a(l, {
30
+ id: n({ prefix: "sgrant" }),
31
+ storageInstance: o().nullable(),
32
+ objectNamespace: o().maxLength(64).nullable(),
33
+ refId: o(),
34
+ principalType: o(),
35
+ principalId: o(),
36
+ permission: o(),
37
+ createdAt: s(),
38
+ expiresAt: s().nullable(),
39
+ createdBy: o().nullable()
40
+ }).index(["storageInstance"]).index(["objectNamespace"]).index(["refId"]).index(["principalId"]), m = a(d, {
41
+ id: n({ prefix: "susage" }),
42
+ tenantId: o().maxLength(191),
43
+ bytes: e().default("0"),
44
+ count: e().default("0"),
45
+ updatedAt: s()
46
+ }).unique("byStorageUsageTenant", ["tenantId"]), h = a(u, {
47
+ id: n({ prefix: "blob" }),
48
+ key: o(),
49
+ data: t(),
50
+ contentType: o(),
51
+ size: e()
52
+ }).index(["key"]);
53
+ //#endregion
54
+ export { u as STORAGE_BLOBS_TABLE, l as STORAGE_GRANTS_TABLE, c as STORAGE_REFS_TABLE, d as STORAGE_USAGE_TABLE, h as _voltroStorageBlobsTable, p as _voltroStorageGrantsTable, f as _voltroStorageRefsTable, m as _voltroStorageUsageTable };
@@ -0,0 +1,91 @@
1
+ import { createHash as e } from "node:crypto";
2
+ import { join as t } from "node:path";
3
+ import { canonicalJson as n } from "@voltro/protocol";
4
+ import { mkdtemp as r, readFile as i, rm as a, writeFile as o } from "node:fs/promises";
5
+ import { tmpdir as s } from "node:os";
6
+ import { spawn as c } from "node:child_process";
7
+ //#region src/transcodeVideo.ts
8
+ var l = (e, t, n) => new Promise((r, i) => {
9
+ let a = c(e, [...t], { stdio: [
10
+ "ignore",
11
+ "pipe",
12
+ "pipe"
13
+ ] }), o = "", s = "", l, u = (e) => {
14
+ l === void 0 && (l = Error(e), a.kill("SIGKILL"));
15
+ }, d = setTimeout(() => u("ffmpeg timed out"), n);
16
+ a.stdout?.on("data", (e) => {
17
+ if (o.length + e.length > 65536) return u("ffmpeg output exceeds its metadata bound");
18
+ o += String(e);
19
+ }), a.stderr?.on("data", (e) => {
20
+ s = (s + String(e)).slice(-400);
21
+ }), a.on("error", (e) => {
22
+ clearTimeout(d), i(e);
23
+ }), a.on("close", (e) => {
24
+ clearTimeout(d), l === void 0 ? e === 0 ? r(o) : i(/* @__PURE__ */ Error(`ffmpeg exited ${String(e)}: ${s}`)) : i(l);
25
+ });
26
+ }), u = (t) => e("sha256").update(t).digest("hex"), d = (e) => {
27
+ let t = e.replace(/\r\n/g, "\n").trim(), n = t.split("\n")[0] ?? "";
28
+ if (!n.startsWith("ffmpeg version ") || n.length > 512) throw Error("ffmpeg version metadata unavailable");
29
+ return {
30
+ name: "ffmpeg",
31
+ version: n.slice(15),
32
+ digest: u(t)
33
+ };
34
+ }, f = (e) => {
35
+ let c = e.ffmpegPath ?? "ffmpeg", f = e.timeoutMs ?? 3e5, p = e.renditions.map((e) => ({
36
+ ...e,
37
+ args: [...e.args]
38
+ }));
39
+ return async ({ bytes: e }) => {
40
+ let m = await r(t(s(), "voltro-tc-")), h = t(m, "in"), g = [];
41
+ try {
42
+ await o(h, e);
43
+ for (let e of p) {
44
+ let r = t(m, `out.${e.ext}`);
45
+ try {
46
+ let t = performance.now() + f, o = (e) => {
47
+ let n = Math.ceil(t - performance.now());
48
+ if (n <= 0) throw Error("ffmpeg rendition timed out");
49
+ return l(c, e, n);
50
+ }, s = d(await o(["-version"]));
51
+ await o([
52
+ "-y",
53
+ "-i",
54
+ h,
55
+ ...e.args,
56
+ r
57
+ ]);
58
+ let p = d(await o(["-version"]));
59
+ if (s.digest !== p.digest) throw Error("ffmpeg build changed during rendition");
60
+ let m = await i(r);
61
+ g.push({
62
+ bytes: new Uint8Array(m),
63
+ contentType: e.contentType,
64
+ kind: e.kind,
65
+ derivation: {
66
+ engine: s,
67
+ recipe: {
68
+ format: "ffmpeg-cli/v1",
69
+ digest: u(n({
70
+ kind: e.kind,
71
+ contentType: e.contentType,
72
+ ext: e.ext,
73
+ args: e.args
74
+ }))
75
+ }
76
+ },
77
+ ...e.label === void 0 ? {} : { label: e.label }
78
+ }), await a(r, { force: !0 });
79
+ } catch {}
80
+ }
81
+ } finally {
82
+ await a(m, {
83
+ recursive: !0,
84
+ force: !0
85
+ }).catch(() => void 0);
86
+ }
87
+ return g;
88
+ };
89
+ };
90
+ //#endregion
91
+ export { f as ffmpegTranscoder };