@voltro/plugin-storage 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.
@@ -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 };
@@ -1,51 +1,75 @@
1
- import { createHash as e, randomUUID as t } from "node:crypto";
2
- import { authoredError as n } from "@voltro/database";
3
- import { linkSync as r, mkdirSync as i, readFileSync as a, realpathSync as o, rmSync as s, writeFileSync as c } from "node:fs";
4
- import { basename as l, dirname as u, join as d, resolve as f } from "node:path";
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";
5
7
  //#region src/storageNamespaces.ts
6
- var p = (t) => e("sha256").update(JSON.stringify(t)).digest("hex"), m = (e) => {
7
- if (!e?.storageInstance?.trim() || !/^[a-f0-9]{64}$/.test(e.objectNamespace)) throw n("Storage requires a configured instance and a stable physical objectNamespace SHA-256 identity");
8
- }, h = (e) => {
9
- let t = f(e), n = [];
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 = [];
10
25
  for (;;) try {
11
- return d(o(t), ...n.reverse());
26
+ return p(c(t), ...n.reverse());
12
27
  } catch (e) {
13
28
  if (!(e instanceof Error && "code" in e && e.code === "ENOENT")) throw e;
14
- let r = u(t);
29
+ let r = f(t);
15
30
  if (r === t) throw e;
16
- n.push(l(t)), t = r;
31
+ n.push(d(t)), t = r;
17
32
  }
18
- }, g = (e) => {
33
+ }, y = (e) => {
19
34
  let t = new URL(e);
20
35
  return t.username = "", t.password = "", t.search = "", t.hash = "", t.href.replace(/\/+$/, "");
21
- }, _ = ".voltro-object-namespace", v = (e) => {
22
- i(e, { recursive: !0 });
23
- let o = d(e, _), l;
36
+ }, b = ".voltro-object-namespace", x = (e) => {
37
+ o(e, { recursive: !0 });
38
+ let t = p(e, b), n;
24
39
  try {
25
- l = a(o, "utf8");
40
+ n = s(t, "utf8");
26
41
  } catch (e) {
27
42
  if (!(e instanceof Error && "code" in e && e.code === "ENOENT")) throw e;
28
- let n = p(["filesystem-volume", t()]), i = `${o}-${t()}`;
43
+ let i = h(["filesystem-volume", r()]), o = `${t}-${r()}`;
29
44
  try {
30
- c(i, n, {
45
+ u(o, i, {
31
46
  flag: "wx",
32
47
  mode: 384
33
48
  });
34
49
  try {
35
- r(i, o);
50
+ a(o, t);
36
51
  } catch (e) {
37
52
  if (!(e instanceof Error && "code" in e && e.code === "EEXIST")) throw e;
38
53
  }
39
- l = a(o, "utf8");
54
+ n = s(t, "utf8");
40
55
  } finally {
41
- s(i, { force: !0 });
56
+ l(o, { force: !0 });
42
57
  }
43
58
  }
44
- if (!/^[a-f0-9]{64}$/.test(l)) throw n("Filesystem storage object namespace marker is invalid; restore the volume marker before serving storage");
45
- return l;
46
- }, y = (e) => {
47
- if (m(e), typeof e.catalogueId != "string" || !e.catalogueId.trim()) throw n("Storage upload tickets require an actual reference catalogue identity");
48
- return p([
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([
49
73
  "upload-ticket-destination",
50
74
  e.storageInstance,
51
75
  e.catalogueId,
@@ -53,4 +77,4 @@ var p = (t) => e("sha256").update(JSON.stringify(t)).digest("hex"), m = (e) => {
53
77
  ]);
54
78
  };
55
79
  //#endregion
56
- export { _ as FILESYSTEM_NAMESPACE_FILE, h as canonicalStorageRoot, v as filesystemObjectNamespace, m as requireStorageReferenceScope, g as storageEndpointIdentity, p as storageObjectNamespace, y as storageUploadTicketScope };
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 };
@@ -2,37 +2,37 @@ import { requireStorageReferenceScope as e } from "./storageNamespaces.js";
2
2
  import { STORAGE_GRANTS_TABLE as t, STORAGE_REFS_TABLE as n } from "./table.js";
3
3
  import { storageInteger as r, storageIntegerSum as i } from "./storageNumbers.js";
4
4
  import { storageObjectClaimId as a, storageObjectIdentifiers as o } from "./objectLifecycle.js";
5
- import { createHash as s } from "node:crypto";
6
- import { authoredError as c, isNull as l, or as u } from "@voltro/database";
7
- import { Schema as d } from "effect";
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
8
  //#region src/storageObjectNamespaces.ts
9
- var f = "_voltro_storage_put_intents", p = "_voltro_storage_operations", m = "_voltro_storage_object_heads", h = "_voltro_storage_object_claims", g = [
9
+ var p = "_voltro_storage_put_intents", m = "_voltro_storage_operations", h = "_voltro_storage_object_heads", g = "_voltro_storage_object_claims", _ = [
10
10
  n,
11
11
  t,
12
- f,
13
- p
14
- ], _ = d.Struct({
15
- tenantId: d.NullOr(d.String),
16
- bucket: d.String,
17
- operationId: d.String,
18
- fingerprint: d.String,
19
- checksum: d.String,
20
- logicalKey: d.String
21
- }), v = d.Struct({
22
- id: d.String,
23
- objectId: d.String,
24
- fingerprint: d.String,
25
- generation: d.String,
26
- key: d.String,
27
- refId: d.String,
28
- state: d.Literals([
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
29
  "reserved",
30
30
  "ready",
31
31
  "deleting",
32
32
  "deleted"
33
33
  ]),
34
- deleteKey: d.NullOr(d.String)
35
- }), y = (e) => s("sha256").update(JSON.stringify(e)).digest("hex"), b = (e) => JSON.stringify([e.storageInstance, e.objectNamespace]), x = (e, t) => JSON.stringify([e.objectNamespace, t]), S = (e, t) => e.query({
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
36
  table: t,
37
37
  predicate: void 0,
38
38
  lock: "update",
@@ -40,78 +40,108 @@ var f = "_voltro_storage_put_intents", p = "_voltro_storage_operations", m = "_v
40
40
  take: void 0,
41
41
  skip: void 0,
42
42
  projection: void 0
43
- }), C = async (e) => {
44
- for (let t of [...g, m]) if ((await e.query({
45
- table: t,
46
- predicate: t === m ? l("objectNamespace") : u(l("storageInstance"), l("objectNamespace")),
47
- order: [],
48
- take: 1,
49
- skip: void 0,
50
- projection: ["id"]
51
- })).length) throw c("Storage physical namespace migration required: assign references and retained operations to their configured storageInstance and provider objectNamespace before starting the app");
52
- }, w = async (s, l, u) => {
53
- for (let t of l) e(t);
54
- if (!l.length || new Set(l.map(b)).size !== l.length) throw c("Provide distinct configured storage instance and physical namespace pairs");
55
- let g = new Set(l.map(b)), w = /* @__PURE__ */ new Map();
56
- for (let { refId: t, ...n } of u) {
57
- if (e(n), !t.trim() || !g.has(b(n))) throw c("Storage namespace assignment must name a reference and a configured physical scope");
58
- let r = w.get(t);
59
- if (r && b(r) !== b(n)) throw c("Storage physical namespace assignment is ambiguous");
60
- w.set(t, n);
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}`);
61
63
  }
62
- let T = [];
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 = [];
63
85
  for (let e of [
64
86
  n,
65
87
  t,
66
- f,
67
88
  p,
68
89
  m,
69
- h
70
- ]) T.push(await S(s, e));
71
- let [E, D, O, k, A, j] = T, M = /* @__PURE__ */ new Set(), N = (e, t) => {
72
- let n = w.get(e);
73
- if (!n) throw c("Storage physical namespace assignment is incomplete");
74
- for (let e of ["storageInstance", "objectNamespace"]) if (t[e] != null && t[e] !== n[e]) throw c("Storage metadata already belongs to another physical scope; assignment does not move objects");
75
- return M.add(e), n;
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;
76
101
  };
77
- for (let e of E) N(String(e.id), e);
78
- for (let e of D) if (N(String(e.refId), e), !E.some((t) => t.id === e.refId)) throw c("Storage grant has no readable reference");
79
- let P = /* @__PURE__ */ new Map();
80
- for (let e of O) {
81
- if (N(String(e.refId), e), P.has(String(e.id))) throw c("Storage put intent ownership is ambiguous");
82
- P.set(String(e.id), e);
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);
83
109
  }
84
- let F = /* @__PURE__ */ new Map(), I = k.map((e) => {
85
- let t = d.decodeUnknownSync(v)(e), n = N(t.refId, e), a = P.get(t.id);
86
- if (!a || a.refId !== t.refId) throw c("Storage operation lost its retained put intent");
87
- let s = {
88
- ...d.decodeUnknownSync(_)(a.identity),
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),
89
115
  ...n
90
116
  };
91
- typeof a.identity == "object" && a.identity !== null && N(t.refId, a.identity);
92
- let l = o(s), u = y([
93
- s.bucket,
94
- s.tenantId,
95
- s.operationId
96
- ]), f = y([
97
- s.bucket,
98
- s.tenantId,
99
- s.logicalKey,
100
- s.checksum
101
- ]);
102
- if (![u, l.id].includes(t.id) || ![f, l.objectId].includes(t.objectId) || t.fingerprint !== s.fingerprint) throw c("Storage operation identity does not match its retained intent");
103
- if (typeof a.ref != "object" || a.ref === null || Array.isArray(a.ref)) throw c("Storage put intent reference is unreadable");
104
- let p = a.ref;
105
- if (p.id !== t.refId || p.key !== t.key || p.checksum !== s.checksum || p.tenantId !== s.tenantId || p.bucket !== s.bucket) throw c("Storage operation reference does not match its retained intent");
106
- let m = E.find((e) => e.id === t.refId);
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);
107
137
  if (t.state === "ready") {
108
- if (!m || m.key !== t.key || m.checksum !== s.checksum) throw c("Ready storage operation lost its readable reference");
109
- } else if (m) throw c("Unreadable storage operation still owns a readable reference");
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");
110
140
  if (t.state === "ready" || t.state === "reserved") {
111
- let e = F.get(l.objectId);
112
- if (e && (e.generation !== t.generation || e.key !== t.key)) throw c("Storage physical namespace has conflicting active generations");
113
- F.set(l.objectId, {
114
- id: l.objectId,
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,
115
145
  objectNamespace: n.objectNamespace,
116
146
  generation: t.generation,
117
147
  key: t.key,
@@ -120,75 +150,94 @@ var f = "_voltro_storage_put_intents", p = "_voltro_storage_operations", m = "_v
120
150
  }
121
151
  return {
122
152
  previous: t,
153
+ previousNamespace: e.objectNamespace,
123
154
  row: {
124
155
  ...t,
125
156
  ...n,
126
- id: l.id,
127
- objectId: l.objectId
157
+ id: u.id,
158
+ objectId: u.objectId
128
159
  },
129
160
  intent: a,
130
- identity: s
161
+ identity: c
131
162
  };
132
163
  });
133
- if (I.length !== O.length || new Set(I.map((e) => e.row.id)).size !== I.length) throw c("Storage operation namespace migration has unmatched or conflicting identities");
134
- for (let e of A) {
135
- let t = I.filter(({ previous: t }) => t.objectId === e.id && (t.state === "ready" || t.state === "reserved"));
136
- if (!t.length || t.length !== r(e.reservations, "shared-object reservation count") || t.some(({ previous: t, identity: n }) => t.generation !== e.generation || t.key !== e.key || e.objectNamespace != null && e.objectNamespace !== n.objectNamespace)) throw c("Storage object head does not match its active reservations");
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");
137
170
  }
138
- for (let { previous: e } of I) if ((e.state === "ready" || e.state === "reserved") && !A.some((t) => t.id === e.objectId)) throw c("Storage operation lost its active object head");
139
- if ([...w.keys()].some((e) => !M.has(e))) throw c("Storage namespace assignment target does not exist");
140
- let L = new Map(I.map((e) => [e.previous.id, e])), R = j.map((e) => {
141
- let t = L.get(String(e.operationId));
142
- if (!t || typeof e.claim != "string" || !e.claim || e.id !== a(String(e.operationId), e.claim)) throw c("Storage operation claim does not match its retained operation");
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");
143
177
  return {
144
178
  ...e,
145
179
  id: a(t.row.id, e.claim),
146
180
  operationId: t.row.id
147
181
  };
148
- }), z = /* @__PURE__ */ new Set([...E.map((e) => x(w.get(String(e.id)), String(e.key))), ...[...F.values()].map((e) => JSON.stringify([e.objectNamespace, e.key]))]), B = [];
149
- for (let e of I) {
150
- let t = z.has(x(e.identity, e.row.key));
151
- (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 && B.push({
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({
152
186
  refId: e.row.refId,
153
187
  storageInstance: e.identity.storageInstance,
154
188
  objectNamespace: e.identity.objectNamespace
155
189
  }));
156
190
  }
157
- let V = 0;
191
+ let q = 0;
158
192
  for (let [e, r, i] of [[
159
193
  n,
160
- E,
194
+ k,
161
195
  "id"
162
196
  ], [
163
197
  t,
164
- D,
198
+ A,
165
199
  "refId"
166
- ]]) for (let t of r) {
167
- let n = w.get(String(t[i]));
168
- if (t.storageInstance !== n.storageInstance || t.objectNamespace !== n.objectNamespace) {
169
- if (await s.update(e, String(t.id), { ...n }) === null) throw c("Storage physical namespace assignment lost its metadata row");
170
- V++;
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;
171
218
  }
172
219
  }
173
220
  for (let [e, t] of [
174
- [h, j],
175
- [f, O],
176
- [p, k],
177
- [m, A]
178
- ]) for (let n of t) await s.delete(e, String(n.id));
179
- for (let e of F.values()) await s.insert(m, e);
180
- for (let e of I) await s.insert(p, e.row), await s.insert(f, {
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) => ({
181
230
  ...e.intent,
182
231
  id: e.row.id,
183
232
  storageInstance: e.identity.storageInstance,
184
233
  objectNamespace: e.identity.objectNamespace,
185
234
  identity: e.identity
186
- }), (e.previous.id !== e.row.id || e.intent.objectNamespace !== e.identity.objectNamespace || e.previous.state !== e.row.state || e.previous.deleteKey !== e.row.deleteKey) && V++;
187
- for (let e of R) await s.insert(h, e);
188
- return await C(s), {
189
- assigned: V,
190
- pendingDeletes: B
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
191
240
  };
192
241
  };
193
242
  //#endregion
194
- export { w as assignStorageObjectNamespacesInTransaction, C as requireStorageObjectNamespaces };
243
+ export { D as assignStorageObjectNamespacesInTransaction, O as relocateRestoredStorageObjectNamespacesInTransaction, E as requireStorageObjectNamespaces };
@@ -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 };
@@ -18,7 +18,10 @@ var r = 1 << 20, i = (t, n, r = !1) => new e({
18
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
19
  let { Readable: t } = await import("node:stream");
20
20
  return t.fromWeb(n.toReadableStream(e));
21
- }, u = (e, n) => e.getStream ? e.getStream(n) : e.get(n).pipe(t.map((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) => ({
22
25
  stream: o(e.bytes),
23
26
  contentType: e.contentType,
24
27
  size: e.bytes.byteLength
package/dist/table.js CHANGED
@@ -24,6 +24,7 @@ var c = "_voltro_storage_refs", l = "_voltro_storage_grants", u = "_voltro_stora
24
24
  caption: o().nullable(),
25
25
  derivedFrom: o().nullable(),
26
26
  kind: o().nullable(),
27
+ renditionProof: i().nullable(),
27
28
  createdAt: s()
28
29
  }).index(["storageInstance"]).index(["objectNamespace"]).index(["key"]).index(["tenantId"]).index(["ownerId"]).index(["folder"]).index(["derivedFrom"]), p = a(l, {
29
30
  id: n({ prefix: "sgrant" }),