@voltro/plugin-storage 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 (52) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/README.md +27 -0
  3. package/THIRD-PARTY-NOTICES.md +2503 -690
  4. package/dist/access.js +26 -27
  5. package/dist/assetRef.js +1 -2
  6. package/dist/avatar.js +1 -2
  7. package/dist/byteSecurity.d.ts +3 -0
  8. package/dist/byteSecurity.js +6 -0
  9. package/dist/connection.js +98 -0
  10. package/dist/constraints.js +1 -2
  11. package/dist/custody.js +131 -0
  12. package/dist/derivatives.js +1 -2
  13. package/dist/env.js +8 -3
  14. package/dist/grant.js +90 -71
  15. package/dist/index.d.ts +453 -215
  16. package/dist/index.js +709 -537
  17. package/dist/ingest.js +7 -2
  18. package/dist/ingestPolicy.js +1 -1
  19. package/dist/metadataError.js +8 -9
  20. package/dist/objectAbandon.js +15 -0
  21. package/dist/objectLifecycle.js +135 -102
  22. package/dist/objectMetadata.js +253 -96
  23. package/dist/objectWriter.js +51 -23
  24. package/dist/pathDurability.js +1 -2
  25. package/dist/physicalReferences.js +53 -0
  26. package/dist/providers.js +451 -307
  27. package/dist/referenceCatalogue.js +1 -2
  28. package/dist/renditionProof.js +81 -0
  29. package/dist/retainedCapture.js +390 -0
  30. package/dist/retainedObjects.js +189 -0
  31. package/dist/rpc.d.ts +1 -15
  32. package/dist/rpc.js +1 -2
  33. package/dist/scanners.js +1 -2
  34. package/dist/server.d.ts +153 -0
  35. package/dist/server.js +4 -0
  36. package/dist/service.js +879 -546
  37. package/dist/sourceIdentity.js +13 -0
  38. package/dist/sourcePin.js +34 -0
  39. package/dist/storageNamespaces.js +52 -29
  40. package/dist/storageNumbers.js +1 -2
  41. package/dist/storageObjectNamespaces.js +170 -122
  42. package/dist/storageReferenceInstances.js +1 -2
  43. package/dist/storageRequest.js +9 -0
  44. package/dist/storageRouting.js +45 -0
  45. package/dist/streamBridge.js +5 -3
  46. package/dist/table.js +2 -2
  47. package/dist/transcodeVideo.js +85 -48
  48. package/dist/transform.js +30 -27
  49. package/dist/transformCache.js +1 -2
  50. package/dist/types.d.ts +115 -137
  51. package/dist/types.js +15 -16
  52. package/package.json +22 -11
package/dist/access.js CHANGED
@@ -1,33 +1,33 @@
1
- import { randomBytes as e, scryptSync as t, timingSafeEqual as n } from "node:crypto";
2
- import { Effect as r } from "effect";
3
- //#region src/access.ts
4
- var i = (n) => {
5
- let r = e(16).toString("hex");
6
- return `${r}:${t(n, r, 32).toString("hex")}`;
7
- }, a = (e, r) => {
8
- if (!e || !r) return !1;
9
- let [i, a] = r.split(":");
1
+ import { Effect as e } from "effect";
2
+ import { randomBytes as t, scryptSync as n, timingSafeEqual as r } from "node:crypto";
3
+ // src/access.ts
4
+ var i = (e) => {
5
+ let r = t(16).toString("hex");
6
+ return `${r}:${n(e, r, 32).toString("hex")}`;
7
+ }, a = (e, t) => {
8
+ if (!e || !t) return !1;
9
+ let [i, a] = t.split(":");
10
10
  if (!i || !a) return !1;
11
- let o = Buffer.from(a, "hex"), s = t(e, i, o.length);
12
- return s.length === o.length && n(s, o);
13
- }, o = (e) => Array.isArray(e) ? e.filter((e) => typeof e == "string") : [], s = (e) => o(e.metadata?.roles), c = (e) => o(e.scopes), l = (e, t) => e.some((e) => t.includes(e)), u = (e) => o(e.metadata?.groups), d = (e) => typeof e == "boolean" ? r.succeed(e) : r.isEffect(e) ? e : r.promise(() => e), f = (e, t, n = {}) => r.gen(function* () {
14
- if (t.id && e.ownerId && t.id === e.ownerId) return !0;
15
- let i = e.accessPolicy ?? [];
11
+ let o = Buffer.from(a, "hex"), s = n(e, i, o.length);
12
+ return s.length === o.length && r(s, o);
13
+ }, o = (e) => Array.isArray(e) ? e.filter((e) => typeof e == "string") : [], s = (e) => o(e.metadata?.roles), c = (e) => o(e.scopes), l = (e, t) => e.some((e) => t.includes(e)), u = (e) => o(e.metadata?.groups), d = (t) => typeof t == "boolean" ? e.succeed(t) : e.isEffect(t) ? t : e.promise(() => t), f = (t, n, r = {}) => e.gen(function* () {
14
+ if (n.id && t.ownerId && n.id === t.ownerId) return !0;
15
+ let i = t.accessPolicy ?? [];
16
16
  if (i.length === 0) return !1;
17
- let a = n.resolveGroups ? yield* r.promise(async () => await n.resolveGroups(t)) : u(t);
18
- for (let r of i) if (yield* p(r, e, t, a, n)) return !0;
17
+ let a = r.resolveGroups ? yield* e.promise(async () => await r.resolveGroups(n)) : u(n);
18
+ for (let e of i) if (yield* p(e, t, n, a, r)) return !0;
19
19
  return !1;
20
- }), p = (e, t, n, i, o) => r.gen(function* () {
21
- let r = !1, u = (e) => (r = !0, e);
22
- if (e.owner !== void 0 && e.owner && !u(!!n.id && !!t.ownerId && n.id === t.ownerId) || e.tenant !== void 0 && e.tenant && !u(!!n.tenantId && n.tenantId === t.tenantId) || e.apiKey !== void 0 && e.apiKey && !u(n.type === "apiKey") || e.roles && e.roles.length > 0 && !u(l(s(n), e.roles)) || e.groups && e.groups.length > 0 && !u(l(i, e.groups)) || e.scopes && e.scopes.length > 0 && !u(l(c(n), e.scopes)) || e.password !== void 0 && e.password && !u(a(o.password, t.passwordHash))) return !1;
23
- if (e.guard) {
24
- let r = o.guards?.[e.guard];
25
- if (!u(r ? yield* d(r({
26
- subject: n,
27
- ref: t
20
+ }), p = (t, n, r, i, o) => e.gen(function* () {
21
+ let e = !1, u = (t) => (e = !0, t);
22
+ if (t.owner !== void 0 && t.owner && !u(!!r.id && !!n.ownerId && r.id === n.ownerId) || t.tenant !== void 0 && t.tenant && !u(!!r.tenantId && r.tenantId === n.tenantId) || t.apiKey !== void 0 && t.apiKey && !u(r.type === "apiKey") || t.roles && t.roles.length > 0 && !u(l(s(r), t.roles)) || t.groups && t.groups.length > 0 && !u(l(i, t.groups)) || t.scopes && t.scopes.length > 0 && !u(l(c(r), t.scopes)) || t.password !== void 0 && t.password && !u(a(o.password, n.passwordHash))) return !1;
23
+ if (t.guard) {
24
+ let e = o.guards?.[t.guard];
25
+ if (!u(e ? yield* d(e({
26
+ subject: r,
27
+ ref: n
28
28
  })) : !1)) return !1;
29
29
  }
30
- return r;
30
+ return e;
31
31
  }), m = (e, t) => {
32
32
  let n = [];
33
33
  e.id && n.push({
@@ -42,6 +42,5 @@ var i = (n) => {
42
42
  }, h = (e, t, n, r, i) => {
43
43
  let a = m(t, n);
44
44
  return e.some((e) => e.expiresAt && e.expiresAt.getTime() < i || r === "write" && e.permission !== "write" ? !1 : a.some((t) => t.type === e.principalType && t.id === e.principalId));
45
- }, g = (e, t) => t.resolveGroups ? r.promise(async () => await t.resolveGroups(e)) : r.succeed(u(e));
46
- //#endregion
45
+ }, g = (t, n) => n.resolveGroups ? e.promise(async () => await n.resolveGroups(t)) : e.succeed(u(t));
47
46
  export { f as evaluatePolicy, h as grantsAllow, i as hashPassword, g as resolveSubjectGroups, m as subjectPrincipals, a as verifyPassword };
package/dist/assetRef.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { _voltroStorageRefsTable as e } from "./table.js";
2
2
  import { mixin as t, reference as n, text as r } from "@voltro/database";
3
- //#region src/assetRef.ts
3
+ // src/assetRef.ts
4
4
  function i(t = {}) {
5
5
  let i = t.nullable ?? !0, a = t.fk === !1 ? r().externalId() : n(() => e, { onDelete: t.onDelete ?? "setNull" });
6
6
  return i ? a.nullable() : a;
@@ -9,5 +9,4 @@ var a = () => t({
9
9
  id: "@voltro/plugin-storage/withStorage",
10
10
  fields: { asset: i() }
11
11
  });
12
- //#endregion
13
12
  export { i as assetRef, a as withStorage };
package/dist/avatar.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { StorageError as e } from "./types.js";
2
2
  import { Effect as t } from "effect";
3
- //#region src/avatar.ts
3
+ // src/avatar.ts
4
4
  var n = {
5
5
  "image/png": "png",
6
6
  "image/jpeg": "jpg",
@@ -35,5 +35,4 @@ var n = {
35
35
  key: `avatars/${a.userId}/avatar.${s}`
36
36
  });
37
37
  });
38
- //#endregion
39
38
  export { r as decodeDataUri, i as putAvatar };
@@ -0,0 +1,3 @@
1
+ export declare const storageByteSecurityHeaders: (contentType: string) => Readonly<Record<string, string>>;
2
+
3
+ export { }
@@ -0,0 +1,6 @@
1
+ // src/byteSecurity.ts
2
+ var e = /^(?:image\/(?!svg)|video\/|audio\/|application\/pdf(?:;|$)|text\/plain(?:;|$))/i, t = (t) => ({
3
+ "x-content-type-options": "nosniff",
4
+ "content-disposition": e.test(t) ? "inline" : "attachment"
5
+ });
6
+ export { t as storageByteSecurityHeaders };
@@ -0,0 +1,98 @@
1
+ import { storageObjectNamespace as e } from "./storageNamespaces.js";
2
+ import { azureConnectionTransport as t, databaseProvider as n, filesystemProvider as r, memoryProviderWithState as i, s3ConnectionTransport as a } from "./providers.js";
3
+ import { StorageError as o } from "@voltro/kv/storage";
4
+ import { Effect as s } from "effect";
5
+ // src/connection.ts
6
+ var c = (t, n = "") => {
7
+ if (!n) return t;
8
+ let r = (e) => `${n}${e}`;
9
+ return {
10
+ ...t,
11
+ objectNamespace: t.objectNamespace.pipe(s.map((t) => e([
12
+ "prefixed-object-namespace",
13
+ t,
14
+ n
15
+ ]))),
16
+ ...t.objectLocation === void 0 ? {} : { objectLocation: (e) => t.objectLocation(r(e)) },
17
+ put: (e, ...n) => t.put(r(e), ...n),
18
+ get: (e) => t.get(r(e)),
19
+ delete: (e) => t.delete(r(e)),
20
+ head: (e) => t.head(r(e)),
21
+ getUrl: (e, ...n) => t.getUrl(r(e), ...n),
22
+ ...t.publicUrl === void 0 ? {} : { publicUrl: (e) => t.publicUrl(r(e)) },
23
+ ...t.setObjectPublic === void 0 ? {} : { setObjectPublic: (e, n) => t.setObjectPublic(r(e), n) },
24
+ ...t.getUploadUrl === void 0 ? {} : { getUploadUrl: (e, n, i) => t.getUploadUrl(r(e), n, i) },
25
+ ...t.createMultipartUpload === void 0 ? {} : { createMultipartUpload: (e, n, i) => t.createMultipartUpload(r(e), n, i) },
26
+ ...t.presignUploadPart === void 0 ? {} : { presignUploadPart: (e, n, i, a) => t.presignUploadPart(r(e), n, i, a) },
27
+ ...t.completeMultipartUpload === void 0 ? {} : { completeMultipartUpload: (e, n, i) => t.completeMultipartUpload(r(e), n, i) },
28
+ ...t.abortMultipartUpload === void 0 ? {} : { abortMultipartUpload: (e, n) => t.abortMultipartUpload(r(e), n) },
29
+ ...t.getStream === void 0 ? {} : { getStream: (e) => t.getStream(r(e)) },
30
+ ...t.putStream === void 0 ? {} : { putStream: (e, n, i) => t.putStream(r(e), n, i) },
31
+ ...t.getRange === void 0 ? {} : { getRange: (e, n, i) => t.getRange(r(e), n, i) }
32
+ };
33
+ }, l = (e, l) => s.suspend(() => {
34
+ let u;
35
+ switch (e.provider) {
36
+ case "s3":
37
+ case "minio":
38
+ u = a({
39
+ bucket: e.bucket,
40
+ name: e.provider,
41
+ ...e.region === void 0 ? {} : { region: e.region },
42
+ ...e.endpoint === void 0 ? {} : { endpoint: e.endpoint },
43
+ ...e.accessKeyId === void 0 ? {} : { accessKeyId: e.accessKeyId },
44
+ ...e.secretAccessKey === void 0 ? {} : { secretAccessKey: e.secretAccessKey },
45
+ ...e.cdnBaseUrl === void 0 ? {} : { cdnBaseUrl: e.cdnBaseUrl },
46
+ ...e.publicAcl === void 0 ? {} : { publicAcl: e.publicAcl },
47
+ forcePathStyle: e.forcePathStyle ?? e.provider === "minio"
48
+ });
49
+ break;
50
+ case "filesystem":
51
+ u = {
52
+ provider: r({ root: e.root }),
53
+ close: s.void,
54
+ probe: s.tryPromise({
55
+ try: async () => {
56
+ let t = await import("node:fs/promises");
57
+ if (!(await t.stat(e.root)).isDirectory()) throw Error("Storage root must be a directory");
58
+ await t.access(e.root);
59
+ },
60
+ catch: () => new o({
61
+ provider: "filesystem",
62
+ message: "Storage root is not readable",
63
+ transient: !1
64
+ })
65
+ })
66
+ };
67
+ break;
68
+ case "memory":
69
+ u = {
70
+ provider: i(l.memory ?? /* @__PURE__ */ new Map()),
71
+ probe: s.void,
72
+ close: s.void
73
+ };
74
+ break;
75
+ case "database": {
76
+ let e = n();
77
+ u = {
78
+ provider: e,
79
+ probe: e.head("_connection_readiness").pipe(s.asVoid),
80
+ close: s.void
81
+ };
82
+ break;
83
+ }
84
+ case "azure": u = t({
85
+ container: e.bucket,
86
+ ...e.accountName === void 0 ? {} : { accountName: e.accountName },
87
+ ...e.accountKey === void 0 ? {} : { accountKey: e.accountKey },
88
+ ...e.connectionString === void 0 ? {} : { connectionString: e.connectionString },
89
+ ...e.endpoint === void 0 ? {} : { endpoint: e.endpoint },
90
+ ...e.cdnBaseUrl === void 0 ? {} : { cdnBaseUrl: e.cdnBaseUrl }
91
+ });
92
+ }
93
+ return s.succeed({
94
+ ...u,
95
+ provider: c(u.provider, e.keyPrefix)
96
+ });
97
+ });
98
+ export { l as connectStorage, c as withStorageKeyPrefix };
@@ -1,4 +1,4 @@
1
- //#region src/constraints.ts
1
+ // src/constraints.ts
2
2
  var e = (e, t) => {
3
3
  if (!t || t.length === 0) return !0;
4
4
  let n = e.toLowerCase();
@@ -89,5 +89,4 @@ var e = (e, t) => {
89
89
  reason: "content-type-not-allowed",
90
90
  detail: `content type '${r}' is not in the allow-list`
91
91
  } : null;
92
- //#endregion
93
92
  export { r as checkLimits, e as contentTypeAllowed, n as sniffMatches };
@@ -0,0 +1,131 @@
1
+ import { StorageError as e } from "./types.js";
2
+ import { storageMetadataError as t } from "./metadataError.js";
3
+ import { readRetainedStorageObject as n } from "./retainedObjects.js";
4
+ import { abandonRetainedCapture as r, admitRetainedCaptureObject as i, inspectRetainedSource as a, registerRetainedCapture as o, retainedCaptureItems as s, runRetainedCapture as c, sealRetainedCapture as l } from "./retainedCapture.js";
5
+ import { Context as u, Effect as d, Layer as f, Schema as p, Stream as m } from "effect";
6
+ import { canonicalJson as h, pluginInstanceName as g } from "@voltro/protocol";
7
+ // src/custody.ts
8
+ var _ = "@voltro/plugin-storage", v = (e) => g({
9
+ base: _,
10
+ alias: e.alias,
11
+ instance: e.name
12
+ }), y = (e) => `${_}/StorageCustody:${JSON.stringify(e)}`, b = class extends u.Service()(y(_)) {
13
+ static withSource(e) {
14
+ return d.suspend(() => {
15
+ let t = E.get(e.source), n = E.get(e.destination);
16
+ return t === void 0 || n === void 0 ? d.fail(x("Custody transfer requires configured storage services")) : t !== n && v(t.selection) === v(n.selection) ? d.fail(x("Custody source and destination need distinct configured instance identities")) : d.succeed(D(n, t));
17
+ });
18
+ }
19
+ static forInstance(e = {}) {
20
+ return u.Service(y(v(e)));
21
+ }
22
+ }, x = (t, n = 409) => new e({
23
+ provider: "custody",
24
+ message: t,
25
+ status: n,
26
+ transient: !1
27
+ }), S = (n) => p.is(e)(n) ? n : t(n, "custody"), C = (e) => d.tryPromise({
28
+ try: e,
29
+ catch: S
30
+ }), w = (e) => ({
31
+ id: e.id,
32
+ instance: e.instance,
33
+ objectNamespace: e.objectNamespace,
34
+ tenantId: e.tenantId,
35
+ bucket: e.bucket,
36
+ contextDigest: e.contextDigest,
37
+ ...e.source === void 0 ? {} : { source: {
38
+ instance: e.source.instance,
39
+ objectNamespace: e.source.objectNamespace,
40
+ bucket: e.source.bucket
41
+ } }
42
+ }), T = (e) => ({
43
+ ...w(e),
44
+ count: e.count,
45
+ digest: e.digest
46
+ }), E = /* @__PURE__ */ new WeakMap(), D = (e, t = e) => {
47
+ let u = v(t.selection), f = v(e.selection), p = e.bucket, g = e.provider, _ = (t) => d.gen(function* () {
48
+ let n = yield* g.objectNamespace;
49
+ if (t !== void 0 && (t.instance !== f || t.bucket !== p || t.objectNamespace !== n)) return yield* d.fail(x("Capture belongs to another storage instance or bucket", 403));
50
+ let r = e.store();
51
+ return yield* r === void 0 ? d.fail(x("Storage custody requires a bound database and the default reference and grant stores")) : d.succeed(r);
52
+ }), y = () => d.gen(function* () {
53
+ let e = yield* t.provider.objectNamespace;
54
+ return u === f && t.bucket === p ? void 0 : {
55
+ instance: u,
56
+ objectNamespace: e,
57
+ bucket: t.bucket
58
+ };
59
+ }), b = () => d.gen(function* () {
60
+ let e = yield* _();
61
+ return t.store() === e ? e : yield* d.fail(x("Custody source and destination must share the bound application database"));
62
+ }), D = {
63
+ inspect: (e, n) => d.gen(function* () {
64
+ let r = n.id, i = n.tenantId;
65
+ yield* b();
66
+ let o = yield* t.provider.objectNamespace;
67
+ return yield* C(() => a(e, {
68
+ instance: u,
69
+ objectNamespace: o,
70
+ bucket: t.bucket,
71
+ tenantId: i
72
+ }, r));
73
+ }),
74
+ register: (e, t) => d.gen(function* () {
75
+ let n = yield* y(), r = {
76
+ id: t.id,
77
+ instance: f,
78
+ objectNamespace: yield* g.objectNamespace,
79
+ tenantId: t.tenantId,
80
+ bucket: p,
81
+ contextDigest: t.contextDigest,
82
+ ...n === void 0 ? {} : { source: n }
83
+ }, i = t.maxBytes, a = t.sources;
84
+ return yield* b(), {
85
+ key: r,
86
+ count: (yield* C(() => o(e, r, {
87
+ maxBytes: i,
88
+ sources: a
89
+ }))).count
90
+ };
91
+ }),
92
+ run: (n) => d.gen(function* () {
93
+ let r = w(n);
94
+ yield* _(r);
95
+ let i = yield* b();
96
+ yield* c(i, g, r, e.quota, {
97
+ instance: u,
98
+ bucket: t.bucket,
99
+ provider: t.provider
100
+ }).pipe(d.mapError(S));
101
+ }),
102
+ seal: (e, t) => d.gen(function* () {
103
+ let n = w(t);
104
+ return yield* _(n), yield* C(() => l(e, n));
105
+ }),
106
+ items: (e, t) => m.unwrap(d.gen(function* () {
107
+ let n = T(e), r = yield* _(n);
108
+ return m.fromAsyncIterable(s(t ?? r, n), S);
109
+ })),
110
+ read: (e, t, r) => d.gen(function* () {
111
+ let a = T(e), o = yield* d.try({
112
+ try: () => JSON.parse(h(t)),
113
+ catch: S
114
+ }), s = {
115
+ storageInstance: f,
116
+ tenantId: r.tenantId,
117
+ maxBytes: r.maxBytes,
118
+ bucket: p
119
+ };
120
+ if (a.tenantId !== s.tenantId || o.tenantId !== s.tenantId) return yield* d.fail(x("Capture tenant does not match admitted tenant", 403));
121
+ let c = yield* _(a);
122
+ return yield* C(() => i(c, a, o)), yield* n(c, g, o, s).pipe(d.mapError(S));
123
+ }),
124
+ abandon: (e) => d.gen(function* () {
125
+ let t = w(e), n = yield* _(t);
126
+ yield* r(n, g, t).pipe(d.mapError(S));
127
+ })
128
+ };
129
+ return E.set(D, e), D;
130
+ }, O = (e) => f.succeed(b.forInstance(e.selection), D(e));
131
+ export { b as StorageCustody, O as storageCustodyLayer };
@@ -1,5 +1,5 @@
1
1
  import { isTransformable as e, loadSharp as t } from "./transform.js";
2
- //#region src/derivatives.ts
2
+ // src/derivatives.ts
3
3
  var n = async (n, r, i) => {
4
4
  if (!e(r)) return { bytes: n };
5
5
  let a = await t();
@@ -26,5 +26,4 @@ var n = async (n, r, i) => {
26
26
  return { bytes: n };
27
27
  }
28
28
  };
29
- //#endregion
30
29
  export { n as extractImageMetadata };
package/dist/env.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { pluginEnv as e } from "@voltro/env";
2
- //#region src/env.ts
2
+ // src/env.ts
3
3
  var t = e([
4
4
  {
5
5
  name: "STORAGE_PROVIDER",
@@ -131,7 +131,13 @@ var t = e([
131
131
  name: "VOLTRO_STORAGE_SECRET",
132
132
  required: !1,
133
133
  secret: !0,
134
- description: "HMAC secret for signed grant tokens. Falls back to the session secret."
134
+ description: "HMAC secret for signed storage links and upload tickets. Falls back to the session secret (and its VOLTRO_SESSION_SECRET_PREVIOUS)."
135
+ },
136
+ {
137
+ name: "VOLTRO_STORAGE_SECRET_PREVIOUS",
138
+ required: !1,
139
+ secret: !0,
140
+ description: "During a VOLTRO_STORAGE_SECRET rotation: the secret links and tickets were signed with until now. They keep verifying until they expire; then remove it."
135
141
  },
136
142
  {
137
143
  name: "VOLTRO_SESSION_COOKIE",
@@ -152,5 +158,4 @@ var t = e([
152
158
  description: "clamd port for the optional clamav virus scanner. Default 3310."
153
159
  }
154
160
  ]);
155
- //#endregion
156
161
  export { t as storageEnv };
package/dist/grant.js CHANGED
@@ -1,112 +1,131 @@
1
1
  import { storageEnv as e } from "./env.js";
2
- import { createHmac as t, timingSafeEqual as n } from "node:crypto";
3
- import { Exit as r, Schema as i } from "effect";
2
+ import { Exit as t, Schema as n } from "effect";
3
+ import { createHmac as r, timingSafeEqual as i } from "node:crypto";
4
4
  import { StorageAccessPolicySchema as a } from "@voltro/protocol";
5
- import { resolveSessionSecret as o } from "@voltro/protocol/session";
6
- //#region src/grant.ts
7
- var s = () => e.read("VOLTRO_STORAGE_SECRET") ?? o(), c = (e) => (typeof e == "string" ? Buffer.from(e, "utf8") : e).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), l = (e) => {
5
+ import { noteConfigured as o, noteSigned as s, noteSigningLifetime as c, previousAccepted as l, resolveSessionSecrets as u } from "@voltro/protocol/session";
6
+ // src/grant.ts
7
+ var d = () => {
8
+ let t = e.read("VOLTRO_STORAGE_SECRET");
9
+ if (t !== void 0 && t !== "") {
10
+ let n = e.read("VOLTRO_STORAGE_SECRET_PREVIOUS");
11
+ return o("storage", t, n), n === void 0 || n === "" || n === t || !l("storage", n) ? [t] : [t, n];
12
+ }
13
+ let n = u();
14
+ return n.previous === void 0 ? [n.current.secret] : [n.current.secret, n.previous.secret];
15
+ }, f = (t, n) => {
16
+ s(t === e.read("VOLTRO_STORAGE_SECRET") ? "storage" : "session", t, n * 1e3);
17
+ }, p = (t) => {
18
+ c(e.read("VOLTRO_STORAGE_SECRET") ? "storage" : "session", t * 1e3);
19
+ }, m = () => d()[0], h = (e, t, n) => {
20
+ let a = v(t), o = !1;
21
+ for (let t of typeof n == "string" ? [n] : n) {
22
+ if (!t) continue;
23
+ let n = r("sha256", t).update(e).digest();
24
+ a.length === n.length && i(a, n) && (o = !0);
25
+ }
26
+ return o;
27
+ }, g = (e) => typeof e == "string" ? e.length > 0 : e.some((e) => e.length > 0), _ = (e) => (typeof e == "string" ? Buffer.from(e, "utf8") : e).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), v = (e) => {
8
28
  let t = e.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - e.length % 4) % 4);
9
29
  return Buffer.from(t, "base64");
10
- }, u = (e, n, r = s(), i = "/_voltro/storage") => {
11
- if (!r) throw Error("signGrant: secret must be a non-empty string");
30
+ }, y = (e, t, n = m(), i = "/_voltro/storage") => {
31
+ if (!n) throw Error("signGrant: secret must be a non-empty string");
12
32
  let a = {
13
33
  refId: e,
14
34
  audience: i,
15
- exp: Math.floor(Date.now() / 1e3) + Math.floor(n)
16
- }, o = c(JSON.stringify(a));
17
- return `${o}.${c(t("sha256", r).update(o).digest())}`;
18
- }, d = (e, r = s()) => {
19
- if (!e || !r) return null;
20
- let i = e.indexOf(".");
21
- if (i <= 0 || i === e.length - 1) return null;
22
- let a = e.slice(0, i), o = e.slice(i + 1), c = t("sha256", r).update(a).digest(), u = l(o);
23
- if (u.length !== c.length || !n(u, c)) return null;
35
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
36
+ }, o = _(JSON.stringify(a)), s = _(r("sha256", n).update(o).digest());
37
+ return f(n, a.exp), `${o}.${s}`;
38
+ }, b = (e, t = d()) => {
39
+ if (!e || !g(t)) return null;
40
+ let n = e.indexOf(".");
41
+ if (n <= 0 || n === e.length - 1) return null;
42
+ let r = e.slice(0, n);
43
+ if (!h(r, e.slice(n + 1), t)) return null;
24
44
  try {
25
- let e = JSON.parse(l(a).toString("utf8"));
45
+ let e = JSON.parse(v(r).toString("utf8"));
26
46
  return typeof e.audience != "string" || !e.audience || typeof e.refId != "string" || typeof e.exp != "number" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
27
47
  } catch {
28
48
  return null;
29
49
  }
30
- }, f = (e) => e.version !== 2 || typeof e.writeScope != "string" || !/^[a-f0-9]{64}$/.test(e.writeScope) || Object.hasOwn(e, "replicaId") || e.issuerReplicaId !== void 0 && (typeof e.issuerReplicaId != "string" || !e.issuerReplicaId.trim()) ? !1 : e.access === void 0 || e.vis === "private" && r.isSuccess(i.decodeUnknownExit(a, { onExcessProperty: "error" })(e.access)), p = (e, n, r = s()) => {
31
- if (!r) throw Error("signUploadTicket: secret must be a non-empty string");
50
+ }, x = (e) => e.version !== 2 || typeof e.writeScope != "string" || !/^[a-f0-9]{64}$/.test(e.writeScope) || Object.hasOwn(e, "replicaId") || e.issuerReplicaId !== void 0 && (typeof e.issuerReplicaId != "string" || !e.issuerReplicaId.trim()) ? !1 : e.access === void 0 || e.vis === "private" && t.isSuccess(n.decodeUnknownExit(a, { onExcessProperty: "error" })(e.access)), S = (e, t, n = m()) => {
51
+ if (!n) throw Error("signUploadTicket: secret must be a non-empty string");
32
52
  let i = {
33
53
  ...e,
34
54
  version: 2,
35
- exp: Math.floor(Date.now() / 1e3) + Math.floor(n)
36
- }, a = c(JSON.stringify(i));
37
- return `${a}.${c(t("sha256", r).update(`u:${a}`).digest())}`;
38
- }, m = (e, r = s()) => {
39
- if (!e || !r) return null;
40
- let i = e.indexOf(".");
41
- if (i <= 0 || i === e.length - 1) return null;
42
- let a = e.slice(0, i), o = e.slice(i + 1), c = t("sha256", r).update(`u:${a}`).digest(), u = l(o);
43
- if (u.length !== c.length || !n(u, c)) return null;
55
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
56
+ }, a = _(JSON.stringify(i)), o = _(r("sha256", n).update(`u:${a}`).digest());
57
+ return f(n, i.exp), `${a}.${o}`;
58
+ }, C = (e, t = d()) => {
59
+ if (!e || !g(t)) return null;
60
+ let n = e.indexOf(".");
61
+ if (n <= 0 || n === e.length - 1) return null;
62
+ let r = e.slice(0, n), i = e.slice(n + 1);
63
+ if (!h(`u:${r}`, i, t)) return null;
44
64
  try {
45
- let e = JSON.parse(l(a).toString("utf8"));
46
- return !f(e) || e.operationId !== void 0 && (typeof e.operationId != "string" || !e.operationId.trim() || e.operationId.length > 200) || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
65
+ let e = JSON.parse(v(r).toString("utf8"));
66
+ return !x(e) || e.operationId !== void 0 && (typeof e.operationId != "string" || !e.operationId.trim() || e.operationId.length > 200) || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || typeof e.destination != "string" || !e.destination || e.destination.includes("\0") || /[\uD800-\uDFFF]/u.test(e.destination) || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
47
67
  } catch {
48
68
  return null;
49
69
  }
50
- }, h = (e, n, r = s()) => {
51
- if (!r) throw Error("signFinalizeTicket: secret must be a non-empty string");
70
+ }, w = (e, t, n = m()) => {
71
+ if (!n) throw Error("signFinalizeTicket: secret must be a non-empty string");
52
72
  let i = {
53
73
  ...e,
54
74
  version: 2,
55
- exp: Math.floor(Date.now() / 1e3) + Math.floor(n)
56
- }, a = c(JSON.stringify(i));
57
- return `${a}.${c(t("sha256", r).update(`f:${a}`).digest())}`;
58
- }, g = (e, r = s()) => {
59
- if (!e || !r) return null;
60
- let i = e.indexOf(".");
61
- if (i <= 0 || i === e.length - 1) return null;
62
- let a = e.slice(0, i), o = e.slice(i + 1), c = t("sha256", r).update(`f:${a}`).digest(), u = l(o);
63
- if (u.length !== c.length || !n(u, c)) return null;
75
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
76
+ }, a = _(JSON.stringify(i)), o = _(r("sha256", n).update(`f:${a}`).digest());
77
+ return f(n, i.exp), `${a}.${o}`;
78
+ }, T = (e, t = d()) => {
79
+ if (!e || !g(t)) return null;
80
+ let n = e.indexOf(".");
81
+ if (n <= 0 || n === e.length - 1) return null;
82
+ let r = e.slice(0, n), i = e.slice(n + 1);
83
+ if (!h(`f:${r}`, i, t)) return null;
64
84
  try {
65
- let e = JSON.parse(l(a).toString("utf8"));
66
- return !f(e) || typeof e.key != "string" || e.key.length === 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
85
+ let e = JSON.parse(v(r).toString("utf8"));
86
+ return !x(e) || typeof e.key != "string" || e.key.length === 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || typeof e.destination != "string" || !e.destination || e.destination.includes("\0") || /[\uD800-\uDFFF]/u.test(e.destination) || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
67
87
  } catch {
68
88
  return null;
69
89
  }
70
- }, _ = (e, n, r = s()) => {
71
- if (!r) throw Error("signResumableTicket: secret must be a non-empty string");
90
+ }, E = (e, t, n = m()) => {
91
+ if (!n) throw Error("signResumableTicket: secret must be a non-empty string");
72
92
  let i = {
73
93
  ...e,
74
94
  version: 2,
75
- exp: Math.floor(Date.now() / 1e3) + Math.floor(n)
76
- }, a = c(JSON.stringify(i));
77
- return `${a}.${c(t("sha256", r).update(`r:${a}`).digest())}`;
78
- }, v = (e, r = s()) => {
79
- if (!e || !r) return null;
80
- let i = e.indexOf(".");
81
- if (i <= 0 || i === e.length - 1) return null;
82
- let a = e.slice(0, i), o = e.slice(i + 1), c = t("sha256", r).update(`r:${a}`).digest(), u = l(o);
83
- if (u.length !== c.length || !n(u, c)) return null;
95
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
96
+ }, a = _(JSON.stringify(i)), o = _(r("sha256", n).update(`r:${a}`).digest());
97
+ return f(n, i.exp), `${a}.${o}`;
98
+ }, D = (e, t = d()) => {
99
+ if (!e || !g(t)) return null;
100
+ let n = e.indexOf(".");
101
+ if (n <= 0 || n === e.length - 1) return null;
102
+ let r = e.slice(0, n), i = e.slice(n + 1);
103
+ if (!h(`r:${r}`, i, t)) return null;
84
104
  try {
85
- let e = JSON.parse(l(a).toString("utf8"));
86
- return !f(e) || typeof e.id != "string" || e.id.length === 0 || typeof e.size != "number" || e.size < 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
105
+ let e = JSON.parse(v(r).toString("utf8"));
106
+ return !x(e) || typeof e.id != "string" || e.id.length === 0 || typeof e.size != "number" || e.size < 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || typeof e.destination != "string" || !e.destination || e.destination.includes("\0") || /[\uD800-\uDFFF]/u.test(e.destination) || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
87
107
  } catch {
88
108
  return null;
89
109
  }
90
- }, y = (e, n, r = s()) => {
91
- if (!r) throw Error("signMultipartTicket: secret must be a non-empty string");
110
+ }, O = (e, t, n = m()) => {
111
+ if (!n) throw Error("signMultipartTicket: secret must be a non-empty string");
92
112
  let i = {
93
113
  ...e,
94
114
  version: 2,
95
- exp: Math.floor(Date.now() / 1e3) + Math.floor(n)
96
- }, a = c(JSON.stringify(i));
97
- return `${a}.${c(t("sha256", r).update(`m:${a}`).digest())}`;
98
- }, b = (e, r = s()) => {
99
- if (!e || !r) return null;
100
- let i = e.indexOf(".");
101
- if (i <= 0 || i === e.length - 1) return null;
102
- let a = e.slice(0, i), o = e.slice(i + 1), c = t("sha256", r).update(`m:${a}`).digest(), u = l(o);
103
- if (u.length !== c.length || !n(u, c)) return null;
115
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
116
+ }, a = _(JSON.stringify(i)), o = _(r("sha256", n).update(`m:${a}`).digest());
117
+ return f(n, i.exp), `${a}.${o}`;
118
+ }, k = (e, t = d()) => {
119
+ if (!e || !g(t)) return null;
120
+ let n = e.indexOf(".");
121
+ if (n <= 0 || n === e.length - 1) return null;
122
+ let r = e.slice(0, n), i = e.slice(n + 1);
123
+ if (!h(`m:${r}`, i, t)) return null;
104
124
  try {
105
- let e = JSON.parse(l(a).toString("utf8"));
106
- return !f(e) || typeof e.key != "string" || e.key.length === 0 || typeof e.uploadId != "string" || e.uploadId.length === 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
125
+ let e = JSON.parse(v(r).toString("utf8"));
126
+ return !x(e) || typeof e.key != "string" || e.key.length === 0 || typeof e.uploadId != "string" || e.uploadId.length === 0 || typeof e.instance != "string" || !e.instance || typeof e.ct != "string" || typeof e.exp != "number" || typeof e.destination != "string" || !e.destination || e.destination.includes("\0") || /[\uD800-\uDFFF]/u.test(e.destination) || e.vis !== "public" && e.vis !== "private" || e.exp <= Math.floor(Date.now() / 1e3) ? null : e;
107
127
  } catch {
108
128
  return null;
109
129
  }
110
130
  };
111
- //#endregion
112
- export { s as resolveStorageSecret, h as signFinalizeTicket, u as signGrant, y as signMultipartTicket, _ as signResumableTicket, p as signUploadTicket, g as verifyFinalizeTicket, d as verifyGrant, b as verifyMultipartTicket, v as verifyResumableTicket, m as verifyUploadTicket };
131
+ export { p as declareStorageSigningLifetime, m as resolveStorageSecret, d as resolveStorageSecrets, w as signFinalizeTicket, y as signGrant, O as signMultipartTicket, E as signResumableTicket, S as signUploadTicket, T as verifyFinalizeTicket, b as verifyGrant, k as verifyMultipartTicket, D as verifyResumableTicket, C as verifyUploadTicket };