@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.
package/dist/index.js CHANGED
@@ -1,80 +1,128 @@
1
- import { storageObjectNamespace as e } from "./storageNamespaces.js";
2
- import { STORAGE_REFERENCE_CATALOGUE_TABLE as t, ensureStorageReferenceCatalogue as n, storageReferenceCatalogueTable as r } from "./referenceCatalogue.js";
3
- import { STORAGE_BLOBS_TABLE as i, STORAGE_GRANTS_TABLE as a, STORAGE_REFS_TABLE as o, STORAGE_USAGE_TABLE as s, _voltroStorageBlobsTable as c, _voltroStorageGrantsTable as l, _voltroStorageRefsTable as u, _voltroStorageUsageTable as d } from "./table.js";
4
- import { assignStorageReferenceInstancesInTransaction as f, requireStorageReferenceOwnership as ee } from "./storageReferenceInstances.js";
5
- import { StorageAccessDenied as te, StorageError as p, StorageRejected as ne, StorageScanRejected as re, StorageService as m } from "./types.js";
6
- import { storageObjectLifecycleTables as h } from "./objectLifecycle.js";
7
- import { assignStorageObjectNamespacesInTransaction as g, requireStorageObjectNamespaces as _ } from "./storageObjectNamespaces.js";
8
- import { bytesToStream as v, collectStream as y, concatBytes as b, fromNodeReadable as x, getObjectStream as S, putObjectStream as ie, toNodeReadable as C } from "./streamBridge.js";
9
- import { storageEnv as w } from "./env.js";
10
- import { azureProvider as T, clearStorageBuffer as E, databaseProvider as D, filesystemProvider as O, memoryProvider as k, readStorageBuffer as ae, s3Provider as A } from "./providers.js";
11
- import { createTransformCache as oe } from "./transformCache.js";
12
- import { editImage as j, parseTransformParams as se, transformCached as ce, transformFallbackReason as le, transformImage as ue, transformKey as de } from "./transform.js";
13
- import { hashPassword as M, verifyPassword as fe } from "./access.js";
14
- import { resolveStorageSecret as pe, signFinalizeTicket as me, signGrant as he, signMultipartTicket as ge, signResumableTicket as _e, signUploadTicket as N, verifyFinalizeTicket as ve, verifyGrant as ye, verifyMultipartTicket as P, verifyResumableTicket as be, verifyUploadTicket as F } from "./grant.js";
15
- import { checkLimits as I, contentTypeAllowed as L, sniffMatches as R } from "./constraints.js";
16
- import { fetchIngestBody as z } from "./ingest.js";
17
- import { buildStorageService as xe, dataStoreGrantStore as Se, dataStoreRefStore as Ce, memoryGrantStore as we, memoryRefStore as B, refMatchesListQuery as V, storageServiceLayer as Te } from "./service.js";
18
- import { makeStorageObjectMetadata as Ee, storagePutIntentsTable as De } from "./objectMetadata.js";
19
- import { abortMultipartUploadDescriptor as Oe, beginMultipartUploadDescriptor as ke, beginResumableUploadDescriptor as Ae, completeMultipartUploadDescriptor as je, finalizeUploadDescriptor as Me, ingestUrlDescriptor as Ne, listGrantsDescriptor as Pe, listRefsDescriptor as Fe, mintPresignedUploadDescriptor as Ie, mintUploadTicketDescriptor as Le, mintUploadUrlDescriptor as Re, mintUrlDescriptor as ze, revokeDescriptor as Be, shareDescriptor as Ve, signMultipartPartDescriptor as He, storageRpcClientImports as Ue, uploadDescriptor as We } from "./rpc.js";
20
- import { clamavScanner as Ge, encodeInstream as Ke, parseClamavResponse as qe } from "./scanners.js";
21
- import { ffmpegTranscoder as Je } from "./transcodeVideo.js";
22
- import { assetRef as Ye, withStorage as Xe } from "./assetRef.js";
23
- import { decodeDataUri as Ze, putAvatar as Qe } from "./avatar.js";
24
- import { durabilityNotice as $e, pathDurability as et } from "./pathDurability.js";
25
- import { createHash as tt, randomUUID as H } from "node:crypto";
26
- import { Effect as U, Schema as W, Stream as nt } from "effect";
27
- import { processIdentity as rt } from "@voltro/protocol/identity";
28
- import { StorageAccessPolicySchema as it, definePlugin as at, pluginInstanceName as G } from "@voltro/protocol";
29
- import { readCookie as ot, resolveSessionSecret as st, verifySession as ct } from "@voltro/protocol/session";
1
+ import { appStorageRouter as e, makeStorageDestinationRouter as t } from "./storageRouting.js";
2
+ import { storageByteSecurityHeaders as n } from "./byteSecurity.js";
3
+ import { existingFilesystemObjectNamespace as r, storageObjectNamespace as i } from "./storageNamespaces.js";
4
+ import { STORAGE_REFERENCE_CATALOGUE_TABLE as a, ensureStorageReferenceCatalogue as o, storageReferenceCatalogueTable as s } from "./referenceCatalogue.js";
5
+ import { STORAGE_BLOBS_TABLE as c, STORAGE_GRANTS_TABLE as l, STORAGE_REFS_TABLE as u, STORAGE_USAGE_TABLE as d, _voltroStorageBlobsTable as ee, _voltroStorageGrantsTable as f, _voltroStorageRefsTable as p, _voltroStorageUsageTable as m } from "./table.js";
6
+ import { assignStorageReferenceInstancesInTransaction as h, requireStorageReferenceOwnership as te } from "./storageReferenceInstances.js";
7
+ import { StorageAccessDenied as ne, StorageError as g, StorageRejected as re, StorageScanRejected as ie, StorageService as _, storageAddressIdentity as ae, storageRefAddress as v } from "./types.js";
8
+ import { storageObjectLifecycleTables as oe } from "./objectLifecycle.js";
9
+ import { assignStorageObjectNamespacesInTransaction as y, requireStorageObjectNamespaces as se } from "./storageObjectNamespaces.js";
10
+ import { bytesToStream as b, collectStream as x, concatBytes as S, fromNodeReadable as C, getObjectStream as ce, putObjectStream as w, toNodeReadable as T } from "./streamBridge.js";
11
+ import { storageEnv as E } from "./env.js";
12
+ import { azureProvider as D, clearStorageBuffer as O, databaseProvider as k, filesystemProvider as A, memoryProvider as j, memoryProviderWithState as M, readStorageBuffer as N, s3Provider as P } from "./providers.js";
13
+ import { createTransformCache as le } from "./transformCache.js";
14
+ import { editImage as ue, parseTransformParams as de, transformCached as fe, transformFallbackReason as pe, transformImage as me, transformKey as he } from "./transform.js";
15
+ import { hashPassword as F, verifyPassword as I } from "./access.js";
16
+ import { resolveStorageSecret as L, signFinalizeTicket as ge, signGrant as R, signMultipartTicket as _e, signResumableTicket as ve, signUploadTicket as z, verifyFinalizeTicket as ye, verifyGrant as be, verifyMultipartTicket as B, verifyResumableTicket as xe, verifyUploadTicket as Se } from "./grant.js";
17
+ import { checkLimits as V, contentTypeAllowed as Ce, sniffMatches as we } from "./constraints.js";
18
+ import { fetchIngestBody as H } from "./ingest.js";
19
+ import { buildStorageService as Te, dataStoreGrantStore as Ee, dataStoreRefStore as De, memoryGrantStore as Oe, memoryRefStore as ke, refMatchesListQuery as Ae, storageServiceLayer as je } from "./service.js";
20
+ import { makeStorageObjectMetadata as Me, storagePutIntentsTable as Ne } from "./objectMetadata.js";
21
+ import { retainedCaptureTables as Pe } from "./retainedCapture.js";
22
+ import { StorageCustody as Fe, storageCustodyLayer as Ie } from "./custody.js";
23
+ import { abortMultipartUploadDescriptor as Le, beginMultipartUploadDescriptor as Re, beginResumableUploadDescriptor as ze, completeMultipartUploadDescriptor as Be, finalizeUploadDescriptor as Ve, ingestUrlDescriptor as He, listGrantsDescriptor as Ue, listRefsDescriptor as We, mintPresignedUploadDescriptor as Ge, mintUploadTicketDescriptor as Ke, mintUploadUrlDescriptor as qe, mintUrlDescriptor as Je, revokeDescriptor as Ye, shareDescriptor as Xe, signMultipartPartDescriptor as Ze, storageRpcClientImports as Qe, uploadDescriptor as $e } from "./rpc.js";
24
+ import { clamavScanner as U, encodeInstream as W, parseClamavResponse as G } from "./scanners.js";
25
+ import { ffmpegTranscoder as K } from "./transcodeVideo.js";
26
+ import { assetRef as et, withStorage as tt } from "./assetRef.js";
27
+ import { decodeDataUri as nt, putAvatar as rt } from "./avatar.js";
28
+ import { durabilityNotice as it, pathDurability as at } from "./pathDurability.js";
29
+ import { connectStorage as ot, withStorageKeyPrefix as st } from "./connection.js";
30
+ import { StorageAddress as ct, forwardStorageProvider as lt, isStorageKey as ut, storageAddressIdentity as dt } from "@voltro/kv/storage";
31
+ import { Effect as q, Layer as ft, Schema as J, Stream as pt } from "effect";
32
+ import { createHash as mt, randomUUID as ht } from "node:crypto";
33
+ import { authoredError as gt } from "@voltro/database";
34
+ import { processIdentity as _t } from "@voltro/protocol/identity";
35
+ import { StorageAccessPolicySchema as vt, definePlugin as yt, effectiveScopes as bt, pluginInstanceName as xt } from "@voltro/protocol";
36
+ import { readCookie as St, resolveSessionSecrets as Ct, verifySessionKeyed as wt } from "@voltro/protocol/session";
37
+ import { ifRangeAllowsRange as Tt, parseByteRange as Et } from "@voltro/runtime/http-range";
30
38
  //#region src/index.ts
31
- var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
32
- K = e;
33
- }, dt = () => K ?? q({}), ft = () => K === void 0 ? w.read("STORAGE_PROVIDER") === void 0 ? void 0 : q({}) : K, q = (e) => {
39
+ var Dt = E.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", Ot = (e) => {
40
+ let t = e.filter((e) => e.capabilities?.storageProvider !== void 0);
41
+ if (t.length > 1) throw new g({
42
+ provider: "storage",
43
+ message: "Storage destination is ambiguous; installed instances: " + t.map((e) => e.name).sort().join(", "),
44
+ transient: !1
45
+ });
46
+ return t.length === 1 ? t[0].capabilities.storageProvider() : void 0;
47
+ }, kt = (e) => {
48
+ let t = Ot(e);
49
+ return t === void 0 || Y.has(t) ? void 0 : t;
50
+ }, Y = /* @__PURE__ */ new WeakSet(), At = (e) => {
51
+ if (Y.has(e) && process.env.NODE_ENV === "production") throw gt("storage: no provider is configured in this production process — `storagePlugin()` has no `provider` and STORAGE_PROVIDER is unset, so storage would fall back to process memory, which no other replica or later process can see. Set STORAGE_PROVIDER (s3 | minio | azure | filesystem | database) for every process that reaches storage, migrate jobs included, or pass `provider: 'memory'` to choose memory deliberately.");
52
+ return e;
53
+ }, jt = (e) => {
54
+ let t = Ot(e);
55
+ if (t === void 0) throw new g({
56
+ provider: "storage",
57
+ message: "This app has no configured storage destination",
58
+ transient: !1
59
+ });
60
+ return At(t);
61
+ }, X = (e) => {
34
62
  if (e.provider && typeof e.provider == "object") return e.provider;
35
- let t = (e.provider ?? w.read("STORAGE_PROVIDER") ?? "memory").toLowerCase(), n = w.read("S3_BUCKET", e.bucket) ?? w.read("STORAGE_BUCKET") ?? "voltro";
36
- switch (t) {
63
+ let t = e.provider ?? E.read("STORAGE_PROVIDER");
64
+ if (t === void 0) {
65
+ let t = X({
66
+ ...e,
67
+ provider: "memory"
68
+ });
69
+ return Y.add(t), t;
70
+ }
71
+ let n = t.toLowerCase(), r = E.read("S3_BUCKET", e.bucket) ?? E.read("STORAGE_BUCKET") ?? "voltro";
72
+ switch (n) {
37
73
  case "s3":
38
74
  case "minio": {
39
- let r = w.read("S3_REGION", e.region) ?? w.read("AWS_REGION"), i = w.read("S3_ENDPOINT", e.endpoint) ?? w.read("MINIO_ENDPOINT"), a = w.read("S3_ACCESS_KEY_ID", e.accessKeyId) ?? w.read("AWS_ACCESS_KEY_ID"), o = w.read("S3_SECRET_ACCESS_KEY", e.secretAccessKey) ?? w.read("AWS_SECRET_ACCESS_KEY"), s = w.read("STORAGE_CDN_URL", e.cdnBaseUrl), c = {
40
- bucket: n,
41
- name: t,
42
- ...r ? { region: r } : {},
75
+ let t = E.read("S3_REGION", e.region) ?? E.read("AWS_REGION"), i = E.read("S3_ENDPOINT", e.endpoint) ?? E.read("MINIO_ENDPOINT"), a = E.read("S3_ACCESS_KEY_ID", e.accessKeyId) ?? E.read("AWS_ACCESS_KEY_ID"), o = E.read("S3_SECRET_ACCESS_KEY", e.secretAccessKey) ?? E.read("AWS_SECRET_ACCESS_KEY"), s = E.read("STORAGE_CDN_URL", e.cdnBaseUrl), c = {
76
+ bucket: r,
77
+ name: n,
78
+ ...t ? { region: t } : {},
43
79
  ...i ? { endpoint: i } : {},
44
80
  ...a ? { accessKeyId: a } : {},
45
81
  ...o ? { secretAccessKey: o } : {},
46
82
  ...s ? { cdnBaseUrl: s } : {},
47
83
  ...e.publicAcl === void 0 ? {} : { publicAcl: e.publicAcl },
48
- ...t === "minio" || w.read("S3_FORCE_PATH_STYLE") === "1" ? { forcePathStyle: !0 } : {}
84
+ ...n === "minio" || E.read("S3_FORCE_PATH_STYLE") === "1" ? { forcePathStyle: !0 } : {}
49
85
  };
50
- return A(c);
86
+ return P(c);
51
87
  }
52
88
  case "azure": {
53
- let t = w.read("AZURE_STORAGE_ACCOUNT", e.accountName), r = w.read("AZURE_STORAGE_KEY", e.accountKey), i = w.read("AZURE_STORAGE_CONNECTION_STRING", e.connectionString), a = w.read("AZURE_STORAGE_ENDPOINT", e.endpoint), o = w.read("STORAGE_CDN_URL", e.cdnBaseUrl), s = {
54
- container: w.read("AZURE_STORAGE_CONTAINER", e.bucket) ?? n,
89
+ let t = E.read("AZURE_STORAGE_ACCOUNT", e.accountName), n = E.read("AZURE_STORAGE_KEY", e.accountKey), i = E.read("AZURE_STORAGE_CONNECTION_STRING", e.connectionString), a = E.read("AZURE_STORAGE_ENDPOINT", e.endpoint), o = E.read("STORAGE_CDN_URL", e.cdnBaseUrl), s = {
90
+ container: E.read("AZURE_STORAGE_CONTAINER", e.bucket) ?? r,
55
91
  ...t ? { accountName: t } : {},
56
- ...r ? { accountKey: r } : {},
92
+ ...n ? { accountKey: n } : {},
57
93
  ...i ? { connectionString: i } : {},
58
94
  ...a ? { endpoint: a } : {},
59
95
  ...o ? { cdnBaseUrl: o } : {}
60
96
  };
61
- return T(s);
97
+ return D(s);
62
98
  }
63
- case "database": return D();
64
- case "filesystem": return O({ root: w.read("STORAGE_ROOT", e.root) ?? ".voltro-storage" });
65
- default: return k(e.name ?? "default");
99
+ case "database": return k();
100
+ case "filesystem": return A({ root: E.read("STORAGE_ROOT", e.root) ?? ".voltro-storage" });
101
+ case "memory": return M(/* @__PURE__ */ new Map());
102
+ default: throw new g({
103
+ provider: "storage",
104
+ message: "Unknown storage provider; choose memory, filesystem, database, s3, minio or azure",
105
+ transient: !1
106
+ });
66
107
  }
67
- }, pt = (e) => {
68
- let t = "stored files (uploads, renditions) and their reads", n = "provider 'database' or 'filesystem' keeps the bytes inside the installation";
108
+ }, Mt = (e) => {
109
+ let t = "stored files (uploads, renditions) and their reads";
110
+ if (e.connection !== void 0) return [{
111
+ destination: `runtime:the app-owned ${e.connection} connection`,
112
+ purpose: t,
113
+ data: ["files"],
114
+ disable: "remove the storage connection binding"
115
+ }];
116
+ let n = "provider 'database' or 'filesystem' keeps the bytes inside the installation";
69
117
  if (e.provider && typeof e.provider == "object") return [{
70
118
  destination: `runtime:the '${e.provider.name}' storage provider`,
71
119
  purpose: t,
72
120
  data: ["files"],
73
121
  disable: n
74
122
  }];
75
- let r = (e.provider ?? w.read("STORAGE_PROVIDER") ?? "memory").toLowerCase();
123
+ let r = (e.provider ?? E.read("STORAGE_PROVIDER") ?? "memory").toLowerCase();
76
124
  if (r === "s3" || r === "minio") {
77
- let r = w.read("S3_ENDPOINT", e.endpoint) ?? w.read("MINIO_ENDPOINT"), i = w.read("S3_REGION", e.region) ?? w.read("AWS_REGION") ?? "us-east-1";
125
+ let r = E.read("S3_ENDPOINT", e.endpoint) ?? E.read("MINIO_ENDPOINT"), i = E.read("S3_REGION", e.region) ?? E.read("AWS_REGION") ?? "us-east-1";
78
126
  return [{
79
127
  destination: r ?? `s3.${i}.amazonaws.com`,
80
128
  purpose: t,
@@ -83,7 +131,7 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
83
131
  }];
84
132
  }
85
133
  if (r === "azure") {
86
- let r = w.read("AZURE_STORAGE_ENDPOINT", e.endpoint), i = w.read("AZURE_STORAGE_ACCOUNT", e.accountName);
134
+ let r = E.read("AZURE_STORAGE_ENDPOINT", e.endpoint), i = E.read("AZURE_STORAGE_ACCOUNT", e.accountName);
87
135
  return [{
88
136
  destination: r ?? (i === void 0 ? "runtime:the Azure storage connection string" : `${i}.blob.core.windows.net`),
89
137
  purpose: t,
@@ -92,11 +140,11 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
92
140
  }];
93
141
  }
94
142
  return [];
95
- }, mt = (e) => {
143
+ }, Nt = (e) => {
96
144
  if (e !== void 0) return e;
97
- let t = w.read("STORAGE_ALLOWED_ORIGINS");
145
+ let t = E.read("STORAGE_ALLOWED_ORIGINS");
98
146
  return t ? t.trim() === "*" ? "*" : t.split(",").map((e) => e.trim()).filter(Boolean) : [];
99
- }, J = (e, t) => {
147
+ }, Pt = (e, t) => {
100
148
  if (!e || t !== "*" && !t.includes(e)) return {};
101
149
  let n = {
102
150
  "access-control-allow-origin": t === "*" ? "*" : e,
@@ -106,7 +154,7 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
106
154
  vary: "Origin"
107
155
  };
108
156
  return t !== "*" && (n["access-control-allow-credentials"] = "true"), n;
109
- }, ht = (e) => ({
157
+ }, Ft = (e) => ({
110
158
  id: e.id,
111
159
  tenantId: e.tenantId,
112
160
  ownerId: e.ownerId,
@@ -129,29 +177,29 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
129
177
  caption: e.caption ?? null,
130
178
  derivedFrom: e.derivedFrom ?? null,
131
179
  kind: e.kind ?? null
132
- }), Y = () => new p({
180
+ }), It = () => new g({
133
181
  provider: "storage",
134
182
  message: "not permitted to administer sharing for this object",
135
183
  transient: !1,
136
184
  status: 403
137
- }), X = (e, t, n) => {
185
+ }), Lt = (e, t, n) => {
138
186
  let r = n.request.subject;
139
- return (r.scopes ?? []).includes("admin:full") ? U.void : e.head(t, { tenantId: r.tenantId ?? null }).pipe(U.catch(() => U.fail(Y())), U.flatMap((e) => {
187
+ return bt(r).includes("admin:full") ? q.void : e.head(t, { tenantId: r.tenantId ?? null }).pipe(q.catch(() => q.fail(It())), q.flatMap((e) => {
140
188
  let t = e?.ownerId;
141
- return t != null && t === r.id ? U.void : U.fail(Y());
189
+ return t != null && t === r.id ? q.void : q.fail(It());
142
190
  }));
143
- }, gt = () => new p({
191
+ }, Rt = () => new g({
144
192
  provider: "storage",
145
193
  message: "an upload needs an identified caller: this request carries no subject, and the app has not set `anonymousUploads: true` on storagePlugin. The ticket attributes the object to whoever minted it — with no subject there is nobody to attribute it to, so the finalized object would carry no owner and no tenant and no later owner check, tenant filter or quota could reach it.",
146
194
  transient: !1,
147
195
  status: 401
148
196
  }), Z = (e, t) => {
149
197
  let n = e.request.subject.id ?? null, r = e.request.subject.tenantId ?? null;
150
- return n === null && !t ? U.fail(gt()) : U.succeed({
198
+ return n === null && !t ? q.fail(Rt()) : q.succeed({
151
199
  id: n,
152
200
  tenantId: r
153
201
  });
154
- }, _t = (e, t) => e !== null && e !== (t ?? null), Q = (e) => ({
202
+ }, zt = (e, t) => e !== null && e !== (t ?? null), Bt = (e) => ({
155
203
  id: e.id,
156
204
  refId: e.refId,
157
205
  principalType: e.principalType,
@@ -160,32 +208,79 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
160
208
  createdAt: e.createdAt.toISOString(),
161
209
  expiresAt: e.expiresAt ? e.expiresAt.toISOString() : null,
162
210
  createdBy: e.createdBy
163
- }), $ = "@voltro/plugin-storage", vt = (e = {}) => G({
164
- base: $,
211
+ }), Q = "@voltro/plugin-storage", Vt = (e = {}) => xt({
212
+ base: Q,
165
213
  alias: e.alias,
166
214
  instance: e.name
167
- }), yt = (e) => e.reason === "too-large" ? 413 : e.reason === "operation-identity-reused" || e.reason === "operation-store-unbound" || e.reason === "claim-not-held" ? 409 : 415, bt = (e = {}) => {
168
- let t = G({
169
- base: $,
215
+ }), Ht = (e) => e.reason === "too-large" ? 413 : e.reason === "operation-identity-reused" || e.reason === "operation-store-unbound" || e.reason === "claim-not-held" ? 409 : 415, Ut = (e = {}) => {
216
+ let r = xt({
217
+ base: Q,
170
218
  alias: e.alias,
171
219
  instance: e.name
172
- }), r = t === $ ? "/_voltro/storage" : `/_voltro/storage/i/${Buffer.from(t).toString("hex")}`, i = q(e);
173
- ut(i);
174
- let a = w.read("S3_BUCKET", e.bucket) ?? w.read("STORAGE_BUCKET") ?? "voltro", o = w.read("VOLTRO_PUBLIC_URL", e.publicBaseUrl), s = mt(e.allowedOrigins), c = e.limits?.maxBytes, l = e.anonymousUploads === !0, u = 5242880, d = 21600, f = e.refStore === void 0 || e.refStore === "memory", m = e.grantStore === void 0 || e.grantStore === "memory", h = (t, n) => U.gen(function* () {
175
- let r = (e) => W.decodeUnknownEffect(it)(e, { onExcessProperty: "error" }).pipe(U.mapError(() => new p({
220
+ }), i = r === Q ? "/_voltro/storage" : `/_voltro/storage/i/${Buffer.from(r).toString("hex")}`, a = [
221
+ "provider",
222
+ "bucket",
223
+ "region",
224
+ "endpoint",
225
+ "accessKeyId",
226
+ "secretAccessKey",
227
+ "root",
228
+ "accountName",
229
+ "accountKey",
230
+ "connectionString",
231
+ "cdnBaseUrl"
232
+ ].some((t) => e[t] !== void 0);
233
+ if (e.connection !== void 0 && a) throw Error("Storage selects either a named connection or direct transport options");
234
+ let s = e.connection !== void 0 || !a;
235
+ if (e.connection === void 0 && typeof e.provider != "object" && ![
236
+ "memory",
237
+ "filesystem",
238
+ "database",
239
+ "s3",
240
+ "minio",
241
+ "azure"
242
+ ].includes((e.provider ?? E.read("STORAGE_PROVIDER") ?? "memory").toLowerCase())) throw new g({
243
+ provider: "storage",
244
+ message: "Unknown storage provider; choose memory, filesystem, database, s3, minio or azure",
245
+ transient: !1
246
+ });
247
+ let c = E.read("S3_BUCKET", e.bucket) ?? E.read("STORAGE_BUCKET") ?? "voltro", l = a ? X(e) : void 0, u = () => (l === void 0 && (l = X(e)), l), d = l, ee = e.connection, f, p = !1, m = s ? lt(() => {
248
+ if (d === void 0) {
249
+ if (ee !== void 0) throw new g({
250
+ provider: "storage",
251
+ message: "Named storage requires a bound tenant-aware destination",
252
+ transient: !1
253
+ });
254
+ d = u();
255
+ }
256
+ return d;
257
+ }) : d;
258
+ e.provider === void 0 && e.connection === void 0 && E.read("STORAGE_PROVIDER") === void 0 && Y.add(m);
259
+ let h = e.connection ?? c, _ = t({
260
+ selected: () => h,
261
+ directDestination: e.connection === void 0 ? c : void 0,
262
+ directProvider: u,
263
+ registry: () => f
264
+ }), oe = (e, t) => q.gen(function* () {
265
+ if (f = e, ee = t, p = !1, h = t ?? c, t === void 0) return;
266
+ if (Y.delete(m), e === void 0) throw Error("Named storage requires the app connection registry");
267
+ let n = yield* e.validate(t);
268
+ p = n.tenantScoped, d = n.tenantScoped ? void 0 : yield* e.storage(t);
269
+ }), y = E.read("VOLTRO_PUBLIC_URL", e.publicBaseUrl), b = Nt(e.allowedOrigins), x = e.limits?.maxBytes, S = e.anonymousUploads === !0, C = 5242880, ce = 21600, w = e.refStore === void 0 || e.refStore === "memory", T = e.grantStore === void 0 || e.grantStore === "memory", D = (t, n) => q.gen(function* () {
270
+ let r = (e) => J.decodeUnknownEffect(vt)(e, { onExcessProperty: "error" }).pipe(q.mapError(() => new g({
176
271
  provider: "storage",
177
272
  message: "Invalid upload access policy",
178
273
  transient: !1,
179
274
  status: 400
180
275
  }))), i = t.access === void 0 ? void 0 : yield* r(t.access), a = t.visibility ?? "private";
181
- if (i !== void 0 && a === "public") return yield* U.fail(new p({
276
+ if (i !== void 0 && a === "public") return yield* q.fail(new g({
182
277
  provider: "storage",
183
278
  message: "An access policy requires a private upload",
184
279
  transient: !1,
185
280
  status: 400
186
281
  }));
187
282
  let o = e.uploadAccess;
188
- if (i !== void 0 && typeof o != "function") return yield* U.fail(new p({
283
+ if (i !== void 0 && typeof o != "function") return yield* q.fail(new g({
189
284
  provider: "storage",
190
285
  message: "Requested upload access requires an uploadAccess authorization function on storagePlugin",
191
286
  transient: !1,
@@ -197,46 +292,55 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
197
292
  visibility: a,
198
293
  contentType: t.contentType
199
294
  }) : a === "private" ? o : void 0, c = s === void 0 ? void 0 : yield* r(s);
200
- return a === "public" ? c !== void 0 && c.length > 0 ? yield* U.fail(new p({
295
+ return a === "public" ? c !== void 0 && c.length > 0 ? yield* q.fail(new g({
201
296
  provider: "storage",
202
297
  message: "A public upload cannot carry an access policy",
203
298
  transient: !1,
204
299
  status: 400
205
300
  })) : {} : c === void 0 ? {} : { access: c };
206
- }), g = { current: f ? B() : e.refStore }, v = { current: m ? we() : e.grantStore }, y = { current: void 0 }, b = {
301
+ }), O = { current: w ? ke() : e.refStore }, k = { current: T ? Oe() : e.grantStore }, A = { current: void 0 }, j = { current: void 0 }, M = {
207
302
  get catalogueId() {
208
- return g.current.catalogueId;
303
+ return O.current.catalogueId;
209
304
  },
210
- insert: (e) => g.current.insert(e),
211
- getById: (e) => g.current.getById(e),
212
- delete: (e) => g.current.delete(e),
213
- statistics: () => g.current.statistics(),
214
- listRecent: (e) => g.current.listRecent(e),
215
- countByKey: (e, t) => g.current.countByKey(e, t),
216
- usageByTenant: (e) => g.current.usageByTenant(e),
217
- consumeUsage: (e, t, n) => g.current.consumeUsage(e, t, n),
218
- releaseUsage: (e, t) => g.current.releaseUsage(e, t),
219
- listByDerivedFrom: (e) => g.current.listByDerivedFrom(e),
220
- listRefs: (e) => g.current.listRefs(e),
305
+ insert: (e) => O.current.insert(e),
306
+ getById: (e) => O.current.getById(e),
307
+ delete: (e) => O.current.delete(e),
308
+ statistics: () => O.current.statistics(),
309
+ listRecent: (e) => O.current.listRecent(e),
310
+ countByAddress: (e) => O.current.countByAddress(e),
311
+ listObjectReferences: () => O.current.listObjectReferences(),
312
+ hasObjectReservation: (e, t) => O.current.hasObjectReservation?.(e, t) ?? q.succeed(!1),
313
+ usageByTenant: (e) => O.current.usageByTenant(e),
314
+ consumeUsage: (e, t, n) => O.current.consumeUsage(e, t, n),
315
+ releaseUsage: (e, t) => O.current.releaseUsage(e, t),
316
+ listByDerivedFrom: (e) => O.current.listByDerivedFrom(e),
317
+ listRefs: (e) => O.current.listRefs(e),
221
318
  get setVisibility() {
222
- return g.current.setVisibility;
319
+ return O.current.setVisibility;
223
320
  },
224
- get countPublicByKey() {
225
- return g.current.countPublicByKey;
321
+ get countPublicByAddress() {
322
+ return O.current.countPublicByAddress;
226
323
  }
227
- }, x = {
228
- insert: (e) => v.current.insert(e),
229
- delete: (e) => v.current.delete(e),
230
- getById: (e) => v.current.getById(e),
231
- listByRef: (e) => v.current.listByRef(e),
232
- deleteByRef: (e) => v.current.deleteByRef(e)
233
- }, S = {
234
- httpPath: r,
235
- storageInstance: t,
236
- provider: i,
237
- refStore: b,
238
- grantStore: x,
239
- bucket: a,
324
+ }, N = {
325
+ insert: (e) => k.current.insert(e),
326
+ delete: (e) => k.current.delete(e),
327
+ getById: (e) => k.current.getById(e),
328
+ listByRef: (e) => k.current.listByRef(e),
329
+ deleteByRef: (e) => k.current.deleteByRef(e)
330
+ }, P, ue = {
331
+ httpPath: i,
332
+ storageInstance: r,
333
+ provider: m,
334
+ refStore: M,
335
+ grantStore: N,
336
+ get bucket() {
337
+ return h;
338
+ },
339
+ destinations: _,
340
+ resolveObjectReference: (e) => P !== void 0 && e.storageInstance !== void 0 ? P({
341
+ storageInstance: e.storageInstance,
342
+ address: v(e)
343
+ }) : _.resolve(v(e)),
240
344
  ...e.tenantPrefix === void 0 ? {} : { tenantPrefix: e.tenantPrefix },
241
345
  ...e.attempts === void 0 ? {} : { attempts: e.attempts },
242
346
  ...e.urlExpiresInSec === void 0 ? {} : { urlExpiresInSec: e.urlExpiresInSec },
@@ -247,16 +351,28 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
247
351
  ...e.videoProbe === void 0 ? {} : { videoProbe: e.videoProbe },
248
352
  ...e.transcode === void 0 ? {} : { transcode: e.transcode },
249
353
  ...e.autoTranscode === void 0 ? {} : { autoTranscode: e.autoTranscode },
250
- ...o ? { publicBaseUrl: o } : {},
354
+ ...y ? { publicBaseUrl: y } : {},
251
355
  ...e.normalizeImages === void 0 ? {} : { normalizeImages: e.normalizeImages },
252
356
  ...e.quota === void 0 ? {} : { quota: e.quota },
253
- objects: () => y.current
254
- }, ie = Te(S), C = xe(S), T = (e) => C.uploadTicketScope().pipe(U.flatMap((t) => t === e.writeScope ? U.void : U.fail(new p({
357
+ objects: () => A.current
358
+ }, fe = ft.merge(je(ue), Ie({
359
+ selection: {
360
+ ...e.alias === void 0 ? {} : { alias: e.alias },
361
+ ...e.name === void 0 ? {} : { name: e.name }
362
+ },
363
+ bucket: h,
364
+ provider: m,
365
+ store: () => j.current,
366
+ ...e.quota === void 0 ? {} : { quota: e.quota }
367
+ })), F = Te(ue), I = (e) => F.uploadTicketScope({
368
+ tenantId: e.tenant,
369
+ destination: e.destination
370
+ }).pipe(q.flatMap((t) => t === e.writeScope ? q.void : q.fail(new g({
255
371
  provider: "storage",
256
372
  message: "Upload ticket belongs to another storage destination",
257
373
  transient: !1,
258
374
  status: 409
259
- })))), E = (e, t) => U.runPromise(T(e).pipe(U.match({
375
+ })))), L = (e, t) => q.runPromise(I(e).pipe(q.match({
260
376
  onSuccess: () => null,
261
377
  onFailure: (e) => ({
262
378
  status: e.status ?? 503,
@@ -264,56 +380,36 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
264
380
  contentType: "text/plain",
265
381
  headers: t
266
382
  })
267
- }))), D = `${r}/`, O = (e) => U.runPromise(e.pipe(U.catch(() => U.succeed(null)))), k = (e, t) => {
268
- if (!e) return "invalid";
269
- let n = /^bytes=(\d*)-(\d*)$/.exec(e.trim());
270
- if (!n) return "invalid";
271
- let r = n[1] ?? "", i = n[2] ?? "";
272
- if (r === "" && i === "") return "invalid";
273
- if (r === "") {
274
- let e = Number(i);
275
- return !Number.isFinite(e) || e <= 0 ? "invalid" : t === 0 ? "unsatisfiable" : {
276
- start: Math.max(0, t - e),
277
- endInclusive: t - 1
278
- };
279
- }
280
- let a = Number(r);
281
- if (!Number.isFinite(a)) return "invalid";
282
- if (t > 0 && a >= t) return "unsatisfiable";
283
- let o = i === "" ? Math.max(0, t - 1) : Math.min(Number(i), Math.max(0, t - 1));
284
- return !Number.isFinite(o) || o < a ? "invalid" : {
285
- start: a,
286
- endInclusive: o
287
- };
288
- }, ae = e.publicCacheSeconds === void 0 ? "public, max-age=31536000, immutable" : `public, max-age=${Math.max(0, Math.floor(e.publicCacheSeconds))}`, A = /^(?:image\/(?!svg)|video\/|audio\/|application\/pdf(?:;|$)|text\/plain(?:;|$))/i, j = (e) => ({
289
- "x-content-type-options": "nosniff",
290
- "content-disposition": A.test(e) ? "inline" : "attachment"
291
- }), ce = oe(), M = async (e, t, n, r) => {
292
- if (n) {
293
- let r, i = await ce.getOrCreate(de(JSON.stringify([e.id, e.checksum]), e.contentType, n), async () => {
294
- let t = await O(C.get(e.id));
295
- return t ? (r = t.bytes, ue(t.bytes, e.contentType, n)) : null;
383
+ }))), R = `${i}/`, V = (e) => q.runPromise(e.pipe(q.catch(() => q.succeed(null)))), Ce = e.publicCacheSeconds === void 0 ? "public, max-age=31536000, immutable" : `public, max-age=${Math.max(0, Math.floor(e.publicCacheSeconds))}`, we = le(), H = async (e, t, r, i, a) => {
384
+ if (r) {
385
+ let i, a = await we.getOrCreate(he(JSON.stringify([
386
+ ae(v(e)),
387
+ e.id,
388
+ e.checksum
389
+ ]), e.contentType, r), async () => {
390
+ let t = await V(F.get(e.id));
391
+ return t ? (i = t.bytes, me(t.bytes, e.contentType, r)) : null;
296
392
  }).catch(() => null);
297
- if (!i && r === void 0 && (r = (await O(C.get(e.id)))?.bytes), !i && r === void 0) return {
393
+ if (!a && i === void 0 && (i = (await V(F.get(e.id)))?.bytes), !a && i === void 0) return {
298
394
  status: 404,
299
395
  body: "not found",
300
396
  contentType: "text/plain"
301
397
  };
302
- let a = i ? i.bytes : r, o = i ? i.contentType : e.contentType, s = await le(e.contentType, i !== null);
398
+ let o = a ? a.bytes : i, s = a ? a.contentType : e.contentType, c = await pe(e.contentType, a !== null);
303
399
  return {
304
400
  status: 200,
305
- body: a,
306
- contentType: o,
401
+ body: o,
402
+ contentType: s,
307
403
  headers: {
308
- ...j(o),
404
+ ...n(s),
309
405
  "cache-control": t,
310
406
  "accept-ranges": "none",
311
- ...s === null ? {} : { "x-voltro-transform": `original; reason=${s}` }
407
+ ...c === null ? {} : { "x-voltro-transform": `original; reason=${c}` }
312
408
  }
313
409
  };
314
410
  }
315
- let i = r === void 0 ? "invalid" : k(r, e.size);
316
- if (i === "unsatisfiable") return {
411
+ let o = Tt(a, {}) ? Et(i, e.size) : { _tag: "ignored" };
412
+ if (o._tag === "unsatisfiable") return {
317
413
  status: 416,
318
414
  body: "range not satisfiable",
319
415
  contentType: "text/plain",
@@ -322,17 +418,20 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
322
418
  "accept-ranges": "bytes"
323
419
  }
324
420
  };
325
- if (i !== "invalid") {
326
- let n = await O(C.getRange(e.id, i));
327
- return n ? {
421
+ if (o._tag === "satisfiable") {
422
+ let r = await V(F.getRange(e.id, {
423
+ start: o.start,
424
+ endInclusive: o.end
425
+ }));
426
+ return r ? {
328
427
  status: 206,
329
- body: n.bytes,
428
+ body: r.bytes,
330
429
  contentType: e.contentType,
331
430
  headers: {
332
- ...j(e.contentType),
431
+ ...n(e.contentType),
333
432
  "cache-control": t,
334
433
  "accept-ranges": "bytes",
335
- "content-range": `bytes ${i.start}-${i.endInclusive}/${n.totalSize}`
434
+ "content-range": `bytes ${o.start}-${o.end}/${r.totalSize}`
336
435
  }
337
436
  } : {
338
437
  status: 404,
@@ -340,69 +439,81 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
340
439
  contentType: "text/plain"
341
440
  };
342
441
  }
343
- let a = await O(C.getStream(e.id));
344
- return a ? {
442
+ let s = await V(F.getStream(e.id));
443
+ return s ? {
345
444
  status: 200,
346
445
  contentType: e.contentType,
347
446
  headers: {
348
- ...j(e.contentType),
447
+ ...n(e.contentType),
349
448
  "cache-control": t,
350
449
  "accept-ranges": "bytes"
351
450
  },
352
451
  byteStream: {
353
452
  contentLength: e.size,
354
- stream: () => nt.toReadableStream(a.stream)
453
+ stream: () => pt.toReadableStream(s.stream)
355
454
  }
356
455
  } : {
357
456
  status: 404,
358
457
  body: "not found",
359
458
  contentType: "text/plain"
360
459
  };
361
- }, fe = async (e) => {
362
- let t = e.path.startsWith(D) ? e.path.slice(D.length).split("/")[0] ?? "" : "";
460
+ }, Ae = async (e) => {
461
+ let t = e.path.startsWith(R) ? e.path.slice(R.length).split("/")[0] ?? "" : "";
363
462
  if (!t) return {
364
463
  status: 404,
365
464
  body: "not found",
366
465
  contentType: "text/plain"
367
466
  };
368
- let n = await O(C.head(t));
467
+ let n = await V(F.head(t));
369
468
  if (!n) return {
370
469
  status: 404,
371
470
  body: "not found",
372
471
  contentType: "text/plain"
373
472
  };
374
- let a = se(e.query), o = e.headers.range, s = n.derivedFrom == null ? null : await O(C.head(n.derivedFrom));
473
+ let r = de(e.query), a = e.headers.range, o = e.headers["if-range"], s = n.derivedFrom == null ? null : await V(F.head(n.derivedFrom));
375
474
  if (n.derivedFrom != null && s === null) return {
376
475
  status: 404,
377
476
  body: "not found",
378
477
  contentType: "text/plain"
379
478
  };
380
479
  if (n.visibility === "public" && (s === null || s.visibility === "public")) {
381
- let e = i.publicUrl?.(n.key) ?? null;
382
- return e && !a ? {
480
+ let e = await V(_.resolve(v(n)));
481
+ if (!e) return {
482
+ status: 503,
483
+ body: "storage destination unavailable",
484
+ contentType: "text/plain"
485
+ };
486
+ let t = e.publicUrl?.(n.key) ?? null;
487
+ return t && !r ? {
383
488
  status: 302,
384
489
  headers: {
385
- location: e,
490
+ location: t,
386
491
  "cache-control": "public, max-age=300"
387
492
  }
388
- } : M(n, ae, a, o);
493
+ } : H(n, Ce, r, a, o);
389
494
  }
390
495
  let c = !1, l = e.query ? new URLSearchParams(e.query).get("t") : null;
391
496
  if (l) {
392
- let e = ye(l);
393
- c = !!e && (e.refId === t || s !== null && e.refId === s.id) && e.audience === r;
497
+ let e = be(l);
498
+ c = !!e && (e.refId === t || s !== null && e.refId === s.id) && e.audience === i;
394
499
  }
395
500
  if (!c) {
396
- let t = ot(e.headers.cookie, lt), r = t ? ct(t, st()) : null;
397
- r && (c = await O(C.checkAccess(n, r)) === !0);
501
+ let t = St(e.headers.cookie, Dt), r = t ? wt(t, Ct())?.subject ?? null : null;
502
+ r && (c = await V(F.checkAccess(n, r)) === !0);
398
503
  }
399
504
  if (!c) return {
400
505
  status: 403,
401
506
  body: "forbidden",
402
507
  contentType: "text/plain"
403
508
  };
404
- if (i.presigns && !a) {
405
- let e = await O(i.getUrl(n.key, 300));
509
+ let u = await V(_.resolve(v(n)));
510
+ if (!u) return {
511
+ status: 503,
512
+ body: "storage destination unavailable",
513
+ contentType: "text/plain"
514
+ };
515
+ if (u.presigns && !r) {
516
+ let e = await V(u.getUrl(n.key, 300));
406
517
  if (e && /^https?:/.test(e)) return {
407
518
  status: 302,
408
519
  headers: {
@@ -411,12 +522,12 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
411
522
  }
412
523
  };
413
524
  }
414
- return M(n, "private, no-store", a, o);
415
- }, pe = {
525
+ return H(n, "private, no-store", r, a, o);
526
+ }, Ne = {
416
527
  method: "GET",
417
- path: r,
528
+ path: i,
418
529
  handle: async (e) => {
419
- let t = await fe(e), n = J(e.headers.origin, s);
530
+ let t = await Ae(e), n = Pt(e.headers.origin, b);
420
531
  return Object.keys(n).length === 0 ? t : {
421
532
  ...t,
422
533
  headers: {
@@ -425,47 +536,48 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
425
536
  }
426
537
  };
427
538
  }
428
- }, he = {
539
+ }, Pe = {
429
540
  method: "*",
430
- path: `${r}/upload`,
541
+ path: `${i}/upload`,
431
542
  originGuard: "exempt",
432
543
  handle: async (e) => {
433
- let n = J(e.headers.origin, s);
544
+ let t = Pt(e.headers.origin, b);
434
545
  if (e.method === "OPTIONS") return {
435
546
  status: 204,
436
- headers: n
547
+ headers: t
437
548
  };
438
549
  if (e.method !== "POST") return {
439
550
  status: 405,
440
551
  body: "method not allowed",
441
552
  contentType: "text/plain",
442
- headers: n
553
+ headers: t
443
554
  };
444
- let r = e.query ? new URLSearchParams(e.query).get("ticket") : null, i = r ? F(r) : null;
445
- if (!i || i.instance !== t) return {
555
+ let n = e.query ? new URLSearchParams(e.query).get("ticket") : null, i = n ? Se(n) : null;
556
+ if (!i || i.instance !== r) return {
446
557
  status: 401,
447
558
  body: "invalid or expired upload ticket",
448
559
  contentType: "text/plain",
449
- headers: n
560
+ headers: t
450
561
  };
451
- let a = await E(i, n);
562
+ let a = await L(i, t);
452
563
  if (a) return a;
453
564
  if (e.rawBody.length === 0) return {
454
565
  status: 400,
455
566
  body: "empty body",
456
567
  contentType: "text/plain",
457
- headers: n
568
+ headers: t
458
569
  };
459
570
  if (i.max && e.rawBody.length > i.max) return {
460
571
  status: 413,
461
572
  body: "payload too large",
462
573
  contentType: "text/plain",
463
- headers: n
574
+ headers: t
464
575
  };
465
- let o = await U.runPromise(C.put({
576
+ let o = await q.runPromise(F.put({
466
577
  bytes: e.rawBody,
467
578
  contentType: i.ct,
468
579
  tenantId: i.tenant,
580
+ destination: i.destination,
469
581
  ownerId: i.sub,
470
582
  visibility: i.vis,
471
583
  ...i.access === void 0 ? {} : { access: i.access },
@@ -475,21 +587,21 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
475
587
  ...i.tags === void 0 ? {} : { tags: i.tags },
476
588
  ...i.alt === void 0 ? {} : { alt: i.alt },
477
589
  ...i.caption === void 0 ? {} : { caption: i.caption }
478
- }).pipe(U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
590
+ }).pipe(q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
479
591
  kind: "ok",
480
592
  ref: e,
481
593
  url: t
482
- })))), U.catchTags({
483
- StorageRejected: (e) => U.succeed({
594
+ })))), q.catchTags({
595
+ StorageRejected: (e) => q.succeed({
484
596
  kind: "rejected",
485
- status: yt(e),
597
+ status: Ht(e),
486
598
  detail: e.detail
487
599
  }),
488
- StorageScanRejected: (e) => U.succeed({
600
+ StorageScanRejected: (e) => q.succeed({
489
601
  kind: "scan",
490
602
  threat: e.threat
491
603
  }),
492
- StorageError: (e) => U.succeed({
604
+ StorageError: (e) => q.succeed({
493
605
  kind: "error",
494
606
  message: e.message,
495
607
  status: e.status ?? 500
@@ -504,48 +616,49 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
504
616
  size: o.ref.size
505
617
  }),
506
618
  contentType: "application/json",
507
- headers: n
619
+ headers: t
508
620
  } : o.kind === "rejected" ? {
509
621
  status: o.status,
510
622
  body: o.detail,
511
623
  contentType: "text/plain",
512
- headers: n
624
+ headers: t
513
625
  } : o.kind === "scan" ? {
514
626
  status: 422,
515
627
  body: `rejected: ${o.threat}`,
516
628
  contentType: "text/plain",
517
- headers: n
629
+ headers: t
518
630
  } : {
519
631
  status: o.status,
520
632
  body: o.message,
521
633
  contentType: "text/plain",
522
- headers: n
634
+ headers: t
523
635
  };
524
636
  }
525
- }, I = {
637
+ }, Fe = {
526
638
  method: "*",
527
- path: `${r}/upload/resumable`,
639
+ path: `${i}/upload/resumable`,
528
640
  originGuard: "exempt",
529
641
  handle: async (e) => {
530
- let n = J(e.headers.origin, s);
642
+ let t = Pt(e.headers.origin, b);
531
643
  if (e.method === "OPTIONS") return {
532
644
  status: 204,
533
- headers: n
645
+ headers: t
534
646
  };
535
- let r = new URLSearchParams(e.query || ""), i = be(r.get("token") ?? "");
536
- if (!i || i.instance !== t) return {
647
+ let n = new URLSearchParams(e.query || ""), i = xe(n.get("token") ?? "");
648
+ if (!i || i.instance !== r) return {
537
649
  status: 401,
538
650
  body: "invalid or expired resumable token",
539
651
  contentType: "text/plain",
540
- headers: n
652
+ headers: t
541
653
  };
542
- let a = await E(i, n);
654
+ let a = await L(i, t);
543
655
  if (a) return a;
544
656
  if (e.method === "GET" || e.method === "HEAD") {
545
- let e = await U.runPromise(C.resumableStatus({
657
+ let e = await q.runPromise(F.resumableStatus({
546
658
  uploadId: i.id,
547
- tenantId: i.tenant
548
- }).pipe(U.match({
659
+ tenantId: i.tenant,
660
+ destination: i.destination
661
+ }).pipe(q.match({
549
662
  onFailure: (e) => ({ error: e.message }),
550
663
  onSuccess: (e) => e
551
664
  })));
@@ -553,13 +666,13 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
553
666
  status: 500,
554
667
  body: e.error,
555
668
  contentType: "text/plain",
556
- headers: n
669
+ headers: t
557
670
  } : {
558
671
  status: 200,
559
672
  body: JSON.stringify(e),
560
673
  contentType: "application/json",
561
674
  headers: {
562
- ...n,
675
+ ...t,
563
676
  "upload-offset": String(e.offset)
564
677
  }
565
678
  };
@@ -568,28 +681,30 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
568
681
  status: 405,
569
682
  body: "method not allowed",
570
683
  contentType: "text/plain",
571
- headers: n
684
+ headers: t
572
685
  };
573
- let o = Number(r.get("index"));
686
+ let o = Number(n.get("index"));
574
687
  if (!Number.isInteger(o) || o < 0) return {
575
688
  status: 400,
576
689
  body: "missing/invalid chunk index",
577
690
  contentType: "text/plain",
578
- headers: n
691
+ headers: t
579
692
  };
580
693
  if (e.rawBody.length === 0) return {
581
694
  status: 400,
582
695
  body: "empty chunk",
583
696
  contentType: "text/plain",
584
- headers: n
697
+ headers: t
585
698
  };
586
- let c = r.get("final") === "1", l = await U.runPromise(C.putResumableChunk({
699
+ let s = n.get("final") === "1", c = await q.runPromise(F.putResumableChunk({
587
700
  uploadId: i.id,
588
701
  tenantId: i.tenant,
702
+ destination: i.destination,
589
703
  index: o,
590
704
  bytes: e.rawBody
591
- }).pipe(U.flatMap(() => c ? C.finalizeResumable({
705
+ }).pipe(q.flatMap(() => s ? F.finalizeResumable({
592
706
  uploadId: i.id,
707
+ destination: i.destination,
593
708
  count: o + 1,
594
709
  contentType: i.ct,
595
710
  tenantId: i.tenant,
@@ -600,111 +715,120 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
600
715
  ...i.tags === void 0 ? {} : { tags: i.tags },
601
716
  ...i.alt === void 0 ? {} : { alt: i.alt },
602
717
  ...i.caption === void 0 ? {} : { caption: i.caption }
603
- }).pipe(U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
718
+ }).pipe(q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
604
719
  kind: "done",
605
720
  ref: e,
606
721
  url: t
607
- }))))) : U.succeed({ kind: "chunk" })), U.catchTags({
608
- StorageRejected: (e) => U.succeed({
722
+ }))))) : q.succeed({ kind: "chunk" })), q.catchTags({
723
+ StorageRejected: (e) => q.succeed({
609
724
  kind: "rejected",
610
- status: yt(e),
725
+ status: Ht(e),
611
726
  detail: e.detail
612
727
  }),
613
- StorageScanRejected: (e) => U.succeed({
728
+ StorageScanRejected: (e) => q.succeed({
614
729
  kind: "scan",
615
730
  threat: e.threat
616
731
  }),
617
- StorageError: (e) => U.succeed({
732
+ StorageError: (e) => q.succeed({
618
733
  kind: "error",
619
734
  message: e.message,
620
735
  status: e.status ?? 500
621
736
  })
622
737
  })));
623
- return l.kind === "chunk" ? {
738
+ return c.kind === "chunk" ? {
624
739
  status: 204,
625
740
  headers: {
626
- ...n,
741
+ ...t,
627
742
  "upload-offset": String(o + 1)
628
743
  }
629
- } : l.kind === "done" ? {
744
+ } : c.kind === "done" ? {
630
745
  status: 201,
631
746
  body: JSON.stringify({
632
- id: l.ref.id,
633
- url: l.url,
634
- contentType: l.ref.contentType,
635
- size: l.ref.size
747
+ id: c.ref.id,
748
+ url: c.url,
749
+ contentType: c.ref.contentType,
750
+ size: c.ref.size
636
751
  }),
637
752
  contentType: "application/json",
638
- headers: n
639
- } : l.kind === "rejected" ? {
640
- status: l.status,
641
- body: l.detail,
753
+ headers: t
754
+ } : c.kind === "rejected" ? {
755
+ status: c.status,
756
+ body: c.detail,
642
757
  contentType: "text/plain",
643
- headers: n
644
- } : l.kind === "scan" ? {
758
+ headers: t
759
+ } : c.kind === "scan" ? {
645
760
  status: 422,
646
- body: `rejected: ${l.threat}`,
761
+ body: `rejected: ${c.threat}`,
647
762
  contentType: "text/plain",
648
- headers: n
763
+ headers: t
649
764
  } : {
650
- status: l.status,
651
- body: l.message,
765
+ status: c.status,
766
+ body: c.message,
652
767
  contentType: "text/plain",
653
- headers: n
768
+ headers: t
654
769
  };
655
770
  }
656
- }, L = (e) => ({
771
+ }, U = (e) => ({
657
772
  kind: "json",
658
773
  data: e
659
- }), R = (e, t) => ({
774
+ }), W = (e, t) => ({
660
775
  kind: "json",
661
776
  status: e,
662
777
  data: { error: t }
663
- }), z = (e, t) => {
778
+ }), G = (e, t) => {
664
779
  try {
665
780
  return new URL(e, "http://localhost").searchParams.get(t);
666
781
  } catch {
667
782
  return null;
668
783
  }
669
- }, V = U.gen(function* () {
670
- return {
671
- catalogueId: yield* b.catalogueId,
672
- objectNamespace: yield* i.objectNamespace,
673
- storageInstance: t
784
+ }, K = q.gen(function* () {
785
+ let e = yield* M.catalogueId;
786
+ return p ? {
787
+ catalogueId: e,
788
+ storageInstance: r,
789
+ tenantScoped: !0
790
+ } : {
791
+ catalogueId: e,
792
+ objectNamespace: yield* m.objectNamespace,
793
+ storageInstance: r
674
794
  };
675
- }), De = [
795
+ }), et = [
676
796
  {
677
797
  method: "POST",
678
798
  path: "/upload-ticket",
679
799
  description: "Authorize a binary upload with explicit tenant and owner attribution",
680
- handler: (n) => U.gen(function* () {
681
- let i = W.String.pipe(W.check(W.makeFilter((e) => e.trim().length > 0))), a = yield* W.decodeUnknownEffect(W.fromJsonString(W.Struct({
682
- operationId: W.optional(i.pipe(W.check(W.isMaxLength(200)))),
683
- tenantId: W.NullOr(i),
684
- ownerId: W.NullOr(i),
685
- key: i.pipe(W.check(W.isMaxLength(1024))),
686
- contentType: i.pipe(W.check(W.isMaxLength(255)), W.check(W.makeFilter((e) => !/[\r\n]/.test(e)))),
687
- size: W.Number.pipe(W.check(W.isInt()), W.check(W.isGreaterThan(0)), W.check(W.makeFilter(Number.isSafeInteger))),
688
- visibility: W.optional(W.Literals(["public", "private"]))
689
- })))(n.body).pipe(U.mapError(() => new p({
800
+ handler: (t) => q.gen(function* () {
801
+ let n = J.String.pipe(J.check(J.makeFilter((e) => e.trim().length > 0))), a = yield* J.decodeUnknownEffect(J.fromJsonString(J.Struct({
802
+ operationId: J.optional(n.pipe(J.check(J.isMaxLength(200)))),
803
+ tenantId: J.NullOr(n),
804
+ ownerId: J.NullOr(n),
805
+ key: n.pipe(J.check(J.isMaxLength(1024))),
806
+ contentType: n.pipe(J.check(J.isMaxLength(255)), J.check(J.makeFilter((e) => !/[\r\n]/.test(e)))),
807
+ size: J.Number.pipe(J.check(J.isInt()), J.check(J.isGreaterThan(0)), J.check(J.makeFilter(Number.isSafeInteger))),
808
+ visibility: J.optional(J.Literals(["public", "private"]))
809
+ })))(t.body).pipe(q.mapError(() => new g({
690
810
  provider: "inspect",
691
811
  message: "Provide explicit tenantId and ownerId (or null), key, contentType and a positive file size",
692
812
  transient: !1,
693
813
  status: 400
694
814
  })));
695
- if (a.operationId !== void 0 && y.current === void 0) return R(409, "Durable uploads require a bound operation store with default reference and grant stores");
696
- if (c !== void 0 && a.size > c) return R(413, "File exceeds the configured upload limit");
697
- let o = a.operationId ?? (y.current === void 0 ? void 0 : H()), s = o === void 0 ? void 0 : "inspect-upload:" + tt("sha256").update(JSON.stringify([t, o])).digest("hex"), l = a.visibility !== "public" && Array.isArray(e.uploadAccess) ? yield* W.decodeUnknownEffect(it)(e.uploadAccess, { onExcessProperty: "error" }).pipe(U.mapError(() => new p({
815
+ if (a.operationId !== void 0 && A.current === void 0) return W(409, "Durable uploads require a bound operation store with default reference and grant stores");
816
+ if (x !== void 0 && a.size > x) return W(413, "File exceeds the configured upload limit");
817
+ let o = yield* _.select(a.tenantId), s = a.operationId ?? (A.current === void 0 ? void 0 : ht()), c = s === void 0 ? void 0 : "inspect-upload:" + mt("sha256").update(JSON.stringify([r, s])).digest("hex"), l = a.visibility !== "public" && Array.isArray(e.uploadAccess) ? yield* J.decodeUnknownEffect(vt)(e.uploadAccess, { onExcessProperty: "error" }).pipe(q.mapError(() => new g({
698
818
  provider: "storage",
699
819
  message: "Invalid upload access policy",
700
820
  transient: !1,
701
821
  status: 400
702
- }))) : void 0, u = yield* C.uploadTicketScope(), d = N({
822
+ }))) : void 0, u = yield* F.uploadTicketScope({
823
+ tenantId: a.tenantId,
824
+ destination: o.destination
825
+ }), d = z({
703
826
  writeScope: u,
704
- instance: t,
705
- issuerReplicaId: rt().replicaId,
827
+ instance: r,
828
+ destination: o.destination,
829
+ issuerReplicaId: _t().replicaId,
706
830
  ...l === void 0 ? {} : { access: l },
707
- ...s === void 0 ? {} : { operationId: s },
831
+ ...c === void 0 ? {} : { operationId: c },
708
832
  sub: a.ownerId,
709
833
  tenant: a.tenantId,
710
834
  ct: a.contentType,
@@ -712,33 +836,33 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
712
836
  vis: a.visibility ?? "private",
713
837
  max: a.size
714
838
  }, 300);
715
- return L({
716
- uploadPath: `${r}/upload?ticket=${encodeURIComponent(d)}`,
839
+ return U({
840
+ uploadPath: `${i}/upload?ticket=${encodeURIComponent(d)}`,
717
841
  expiresInSec: 300,
718
842
  maxBytes: a.size,
719
- ...o === void 0 ? {} : { operationId: o }
843
+ ...s === void 0 ? {} : { operationId: s }
720
844
  });
721
- }).pipe(U.catch((e) => U.succeed(R(e.status ?? 500, e.message))))
845
+ }).pipe(q.catch((e) => q.succeed(W(e.status ?? 500, e.message))))
722
846
  },
723
847
  {
724
848
  method: "GET",
725
849
  path: "/download",
726
850
  description: "Stream a stored file as an attachment for authorized inspect readers",
727
- handler: (e) => U.gen(function* () {
728
- let t = z(e.url, "refId");
729
- if (!t?.trim()) return R(400, "A non-empty refId is required");
730
- let { ref: n, stream: r } = yield* C.getStream(t), i = n.key.split("/").pop() || n.id, a = encodeURIComponent(i).replace(/[!'()*]/g, (e) => `%${e.charCodeAt(0).toString(16).toUpperCase()}`);
851
+ handler: (e) => q.gen(function* () {
852
+ let t = G(e.url, "refId");
853
+ if (!t?.trim()) return W(400, "A non-empty refId is required");
854
+ let { ref: n, stream: r } = yield* F.getStream(t), i = n.key.split("/").pop() || n.id, a = encodeURIComponent(i).replace(/[!'()*]/g, (e) => `%${e.charCodeAt(0).toString(16).toUpperCase()}`);
731
855
  return {
732
856
  kind: "bytes",
733
857
  contentType: n.contentType,
734
858
  byteStream: {
735
- stream: () => nt.toReadableStream(r),
859
+ stream: () => pt.toReadableStream(r),
736
860
  contentLength: n.size,
737
861
  contentDisposition: `attachment; filename="download"; filename*=UTF-8''${a}`
738
862
  },
739
863
  headers: { "cache-control": "no-store" }
740
864
  };
741
- }).pipe(U.catch((e) => U.succeed(R(e.status ?? 500, e.message))))
865
+ }).pipe(q.catch((e) => q.succeed(W(e.status ?? 500, e.message))))
742
866
  },
743
867
  {
744
868
  method: "GET",
@@ -746,8 +870,8 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
746
870
  scope: "process",
747
871
  description: "Searchable storage refs, newest first, paginated",
748
872
  handler: (e) => {
749
- let t = Number(z(e.url, "offset") ?? 0), n = Number.isSafeInteger(t) && t >= 0 ? t : 0, r = (z(e.url, "search") ?? "").trim().slice(0, 200);
750
- return b.listRefs({
873
+ let t = Number(G(e.url, "offset") ?? 0), n = Number.isSafeInteger(t) && t >= 0 ? t : 0, r = (G(e.url, "search") ?? "").trim().slice(0, 200);
874
+ return M.listRefs({
751
875
  tenantId: void 0,
752
876
  ownerId: void 0,
753
877
  folder: void 0,
@@ -756,15 +880,15 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
756
880
  search: r,
757
881
  limit: 51,
758
882
  offset: n
759
- }).pipe(U.flatMap((e) => V.pipe(U.map((t) => L({
883
+ }).pipe(q.flatMap((e) => K.pipe(q.map((t) => U({
760
884
  source: t,
761
- refs: e.slice(0, 50).map(ht),
885
+ refs: e.slice(0, 50).map(Ft),
762
886
  page: {
763
887
  search: r,
764
888
  offset: n,
765
889
  nextOffset: e.length > 50 ? n + 50 : null
766
890
  }
767
- })))), U.catch((e) => U.succeed(R(500, e.message))));
891
+ })))), q.catch((e) => q.succeed(W(500, e.message))));
768
892
  }
769
893
  },
770
894
  {
@@ -772,12 +896,12 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
772
896
  path: "/stats",
773
897
  scope: "process",
774
898
  description: "Object count + total bytes per tenant + visibility split",
775
- handler: () => b.statistics().pipe(U.flatMap((e) => V.pipe(U.map((t) => L({
899
+ handler: () => M.statistics().pipe(q.flatMap((e) => K.pipe(q.map((t) => U({
776
900
  source: t,
777
- provider: i.name,
778
- bucket: a,
901
+ provider: p ? "tenant-scoped" : m.name,
902
+ bucket: h,
779
903
  ...e
780
- })))), U.catch((e) => U.succeed(R(500, e.message))))
904
+ })))), q.catch((e) => q.succeed(W(500, e.message))))
781
905
  },
782
906
  {
783
907
  method: "GET",
@@ -785,196 +909,202 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
785
909
  scope: "process",
786
910
  description: "Grants for a ref (?refId=…)",
787
911
  handler: (e) => {
788
- let t = z(e.url, "refId");
789
- return t ? x.listByRef(t).pipe(U.map((e) => L({ grants: e.map(Q) })), U.catch((e) => U.succeed(R(500, e.message)))) : U.succeed(R(400, "refId query param required"));
912
+ let t = G(e.url, "refId");
913
+ return t ? N.listByRef(t).pipe(q.map((e) => U({ grants: e.map(Bt) })), q.catch((e) => q.succeed(W(500, e.message)))) : q.succeed(W(400, "refId query param required"));
790
914
  }
791
915
  },
792
916
  {
793
917
  method: "POST",
794
918
  path: "/share",
795
919
  description: "Grant a principal access to a ref",
796
- handler: (e) => U.gen(function* () {
797
- let t = yield* W.decodeUnknownEffect(W.fromJsonString(W.Struct({
798
- refId: W.String.pipe(W.check(W.makeFilter((e) => e.trim().length > 0))),
799
- principalType: W.Literals([
920
+ handler: (e) => q.gen(function* () {
921
+ let t = yield* J.decodeUnknownEffect(J.fromJsonString(J.Struct({
922
+ refId: J.String.pipe(J.check(J.makeFilter((e) => e.trim().length > 0))),
923
+ principalType: J.Literals([
800
924
  "user",
801
925
  "group",
802
926
  "apiKey"
803
927
  ]),
804
- principalId: W.String.pipe(W.check(W.makeFilter((e) => e.trim().length > 0))),
805
- permission: W.optional(W.Literals(["read", "write"])),
806
- expiresInDays: W.optional(W.Number.pipe(W.check(W.isFinite()), W.check(W.isGreaterThan(0))))
807
- })))(e.body).pipe(U.mapError(() => new p({
928
+ principalId: J.String.pipe(J.check(J.makeFilter((e) => e.trim().length > 0))),
929
+ permission: J.optional(J.Literals(["read", "write"])),
930
+ expiresInDays: J.optional(J.Number.pipe(J.check(J.isFinite()), J.check(J.isGreaterThan(0))))
931
+ })))(e.body).pipe(q.mapError(() => new g({
808
932
  provider: "inspect",
809
933
  message: "Invalid share input: provide refId, principalType, principalId, read/write permission and a positive finite expiry if set",
810
934
  transient: !1,
811
935
  status: 400
812
936
  }))), n = t.expiresInDays === void 0 ? null : new Date(Date.now() + t.expiresInDays * 864e5);
813
- if (n !== null && !Number.isFinite(n.getTime())) return R(400, "Expiry exceeds the supported date range");
814
- let r = yield* C.grant({
937
+ if (n !== null && !Number.isFinite(n.getTime())) return W(400, "Expiry exceeds the supported date range");
938
+ let r = yield* F.grant({
815
939
  refId: t.refId,
816
940
  principalType: t.principalType,
817
941
  principalId: t.principalId,
818
942
  ...t.permission ? { permission: t.permission } : {},
819
943
  expiresAt: n
820
944
  });
821
- return L({ grant: Q(r) });
822
- }).pipe(U.catch((e) => U.succeed(R(e.status ?? 500, e.message ?? "share failed"))))
945
+ return U({ grant: Bt(r) });
946
+ }).pipe(q.catch((e) => q.succeed(W(e.status ?? 500, e.message ?? "share failed"))))
823
947
  },
824
948
  {
825
949
  method: "POST",
826
950
  path: "/delete",
827
951
  description: "Delete an object reference and its grants; release unreferenced bytes",
828
- handler: (e) => U.gen(function* () {
829
- let t = yield* W.decodeUnknownEffect(W.fromJsonString(W.Struct({ refId: W.String.pipe(W.check(W.makeFilter((e) => e.trim().length > 0))) })))(e.body).pipe(U.mapError(() => new p({
952
+ handler: (e) => q.gen(function* () {
953
+ let t = yield* J.decodeUnknownEffect(J.fromJsonString(J.Struct({ refId: J.String.pipe(J.check(J.makeFilter((e) => e.trim().length > 0))) })))(e.body).pipe(q.mapError(() => new g({
830
954
  provider: "inspect",
831
955
  message: "A non-empty refId is required",
832
956
  transient: !1,
833
957
  status: 400
834
958
  })));
835
- return yield* C.delete(t.refId), L({ ok: !0 });
836
- }).pipe(U.catch((e) => U.succeed(R(e.status ?? 500, e.message))))
959
+ return yield* F.delete(t.refId), U({ ok: !0 });
960
+ }).pipe(q.catch((e) => q.succeed(W(e.status ?? 500, e.message))))
837
961
  },
838
962
  {
839
963
  method: "POST",
840
964
  path: "/revoke",
841
965
  description: "Revoke a grant by id",
842
- handler: (e) => U.gen(function* () {
843
- let t = yield* W.decodeUnknownEffect(W.fromJsonString(W.Struct({ grantId: W.String.pipe(W.check(W.makeFilter((e) => e.trim().length > 0))) })))(e.body).pipe(U.mapError(() => new p({
966
+ handler: (e) => q.gen(function* () {
967
+ let t = yield* J.decodeUnknownEffect(J.fromJsonString(J.Struct({ grantId: J.String.pipe(J.check(J.makeFilter((e) => e.trim().length > 0))) })))(e.body).pipe(q.mapError(() => new g({
844
968
  provider: "inspect",
845
969
  message: "A non-empty grantId is required",
846
970
  transient: !1,
847
971
  status: 400
848
972
  })));
849
- return yield* C.revoke(t.grantId), L({ ok: !0 });
850
- }).pipe(U.catch((e) => U.succeed(R(e.status ?? 500, e.message ?? "revoke failed"))))
973
+ return yield* F.revoke(t.grantId), U({ ok: !0 });
974
+ }).pipe(q.catch((e) => q.succeed(W(e.status ?? 500, e.message ?? "revoke failed"))))
851
975
  }
852
- ], Ge = [
976
+ ], tt = [
853
977
  {
854
- ...ze,
978
+ ...Je,
855
979
  execute: (e, t) => {
856
980
  let n = e;
857
- return C.mintUrl(n.id, t.request.subject, {
981
+ return F.mintUrl(n.id, t.request.subject, {
858
982
  ...n.password === void 0 ? {} : { password: n.password },
859
983
  ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
860
- }).pipe(U.map((e) => ({ url: e })));
984
+ }).pipe(q.map((e) => ({ url: e })));
861
985
  }
862
986
  },
863
987
  {
864
- ...Re,
988
+ ...qe,
865
989
  execute: (e, t) => {
866
990
  let n = e;
867
- return C.mintUploadUrl({
991
+ return F.mintUploadUrl({
868
992
  key: n.key,
869
993
  contentType: n.contentType,
870
994
  tenantId: t.request.subject.tenantId,
871
995
  ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
872
- }).pipe(U.map((e) => ({ url: e })));
996
+ }).pipe(q.map((e) => ({ url: e })));
873
997
  }
874
998
  },
875
999
  {
876
- ...Le,
877
- execute: (e, n) => {
878
- let i = e;
879
- return Z(n, l).pipe(U.flatMap((e) => h(i, n.request.subject).pipe(U.map((t) => ({
1000
+ ...Ke,
1001
+ execute: (e, t) => {
1002
+ let n = e;
1003
+ return Z(t, S).pipe(q.flatMap((e) => D(n, t.request.subject).pipe(q.map((t) => ({
880
1004
  writer: e,
881
1005
  policy: t
882
- })))), U.flatMap((e) => i.operationId !== void 0 && y.current === void 0 ? U.fail(new p({
1006
+ })))), q.flatMap((e) => n.operationId !== void 0 && A.current === void 0 ? q.fail(new g({
883
1007
  provider: "storage",
884
1008
  message: "Durable uploads require a bound operation store with default reference and grant stores",
885
1009
  transient: !1,
886
1010
  status: 409
887
- })) : U.succeed(e)), U.flatMap(({ writer: e, policy: n }) => C.uploadTicketScope().pipe(U.map((a) => {
888
- let s = i.expiresInSec ?? 900, l = i.operationId ?? (y.current === void 0 ? void 0 : H()), u = l === void 0 ? void 0 : "client-upload:" + tt("sha256").update(JSON.stringify([
889
- t,
1011
+ })) : q.succeed(e)), q.flatMap(({ writer: e, policy: t }) => _.select(e.tenantId).pipe(q.flatMap((a) => F.uploadTicketScope({
1012
+ tenantId: e.tenantId,
1013
+ destination: a.destination
1014
+ }).pipe(q.map((o) => {
1015
+ let s = n.expiresInSec ?? 900, c = n.operationId ?? (A.current === void 0 ? void 0 : ht()), l = c === void 0 ? void 0 : "client-upload:" + mt("sha256").update(JSON.stringify([
1016
+ r,
890
1017
  e.tenantId,
891
1018
  e.id,
892
- l
893
- ])).digest("hex"), d = N({
894
- writeScope: a,
895
- instance: t,
896
- ...u === void 0 ? {} : { operationId: u },
1019
+ c
1020
+ ])).digest("hex"), u = z({
1021
+ writeScope: o,
1022
+ instance: r,
1023
+ destination: a.destination,
1024
+ ...l === void 0 ? {} : { operationId: l },
897
1025
  sub: e.id,
898
1026
  tenant: e.tenantId,
899
- ct: i.contentType,
900
- vis: i.visibility ?? "private",
901
- ...n,
902
- ...i.key === void 0 ? {} : { key: i.key },
903
- ...i.folder === void 0 ? {} : { folder: i.folder },
904
- ...i.tags === void 0 ? {} : { tags: i.tags },
905
- ...i.alt === void 0 ? {} : { alt: i.alt },
906
- ...i.caption === void 0 ? {} : { caption: i.caption },
907
- ...c === void 0 ? {} : { max: c }
1027
+ ct: n.contentType,
1028
+ vis: n.visibility ?? "private",
1029
+ ...t,
1030
+ ...n.key === void 0 ? {} : { key: n.key },
1031
+ ...n.folder === void 0 ? {} : { folder: n.folder },
1032
+ ...n.tags === void 0 ? {} : { tags: n.tags },
1033
+ ...n.alt === void 0 ? {} : { alt: n.alt },
1034
+ ...n.caption === void 0 ? {} : { caption: n.caption },
1035
+ ...x === void 0 ? {} : { max: x }
908
1036
  }, s);
909
1037
  return {
910
- uploadUrl: `${o ? o.replace(/\/+$/, "") : ""}${r}/upload?ticket=${encodeURIComponent(d)}`,
1038
+ uploadUrl: `${y ? y.replace(/\/+$/, "") : ""}${i}/upload?ticket=${encodeURIComponent(u)}`,
911
1039
  expiresInSec: s,
912
- ...l === void 0 ? {} : { operationId: l }
1040
+ ...c === void 0 ? {} : { operationId: c }
913
1041
  };
914
- }))));
1042
+ }))))));
915
1043
  }
916
1044
  },
917
1045
  {
918
- ...Ie,
919
- execute: (e, n) => {
920
- let r = e;
921
- return Z(n, l).pipe(U.flatMap((e) => h(r, n.request.subject).pipe(U.map((t) => ({
1046
+ ...Ge,
1047
+ execute: (e, t) => {
1048
+ let n = e;
1049
+ return Z(t, S).pipe(q.flatMap((e) => D(n, t.request.subject).pipe(q.map((t) => ({
922
1050
  writer: e,
923
1051
  policy: t
924
- })))), U.flatMap(({ writer: e, policy: n }) => C.uploadTicketScope().pipe(U.flatMap((i) => C.mintPresignedUpload({
925
- contentType: r.contentType,
1052
+ })))), q.flatMap(({ writer: e, policy: t }) => F.mintPresignedUpload({
1053
+ contentType: n.contentType,
926
1054
  tenantId: e.tenantId,
927
- ...r.expiresInSec === void 0 ? {} : { expiresInSec: r.expiresInSec }
928
- }).pipe(U.map((a) => {
929
- let o = me({
930
- writeScope: i,
931
- instance: t,
932
- key: a.key,
1055
+ ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
1056
+ }).pipe(q.map((i) => {
1057
+ let a = ge({
1058
+ writeScope: i.writeScope,
1059
+ instance: r,
1060
+ destination: i.destination,
1061
+ key: i.key,
933
1062
  sub: e.id,
934
1063
  tenant: e.tenantId,
935
- ct: r.contentType,
936
- vis: r.visibility ?? "private",
937
- ...n,
938
- ...r.folder === void 0 ? {} : { folder: r.folder },
939
- ...r.tags === void 0 ? {} : { tags: r.tags },
940
- ...r.alt === void 0 ? {} : { alt: r.alt },
941
- ...r.caption === void 0 ? {} : { caption: r.caption },
942
- ...c === void 0 ? {} : { max: c }
943
- }, a.expiresInSec);
1064
+ ct: n.contentType,
1065
+ vis: n.visibility ?? "private",
1066
+ ...t,
1067
+ ...n.folder === void 0 ? {} : { folder: n.folder },
1068
+ ...n.tags === void 0 ? {} : { tags: n.tags },
1069
+ ...n.alt === void 0 ? {} : { alt: n.alt },
1070
+ ...n.caption === void 0 ? {} : { caption: n.caption },
1071
+ ...x === void 0 ? {} : { max: x }
1072
+ }, i.expiresInSec);
944
1073
  return {
945
- uploadUrl: a.uploadUrl,
946
- finalizeToken: o,
947
- expiresInSec: a.expiresInSec
1074
+ uploadUrl: i.uploadUrl,
1075
+ finalizeToken: a,
1076
+ expiresInSec: i.expiresInSec
948
1077
  };
949
- }))))));
1078
+ }))));
950
1079
  }
951
1080
  },
952
1081
  {
953
- ...Me,
954
- execute: (e, n) => {
955
- let r = ve(e.finalizeToken);
956
- return !r || r.instance !== t ? U.fail(new p({
1082
+ ...Ve,
1083
+ execute: (e, t) => {
1084
+ let n = ye(e.finalizeToken);
1085
+ return !n || n.instance !== r ? q.fail(new g({
957
1086
  provider: "storage",
958
1087
  message: "invalid or expired finalize token",
959
1088
  transient: !1,
960
1089
  status: 401
961
- })) : _t(r.sub, n.request.subject.id) ? U.fail(new p({
1090
+ })) : zt(n.sub, t.request.subject.id) ? q.fail(new g({
962
1091
  provider: "storage",
963
1092
  message: "finalize token belongs to another subject",
964
1093
  transient: !1,
965
1094
  status: 403
966
- })) : T(r).pipe(U.flatMap(() => C.finalizeUpload({
967
- key: r.key,
968
- contentType: r.ct,
969
- tenantId: r.tenant,
970
- ownerId: r.sub,
971
- visibility: r.vis,
972
- ...r.access === void 0 ? {} : { access: r.access },
973
- ...r.folder === void 0 ? {} : { folder: r.folder },
974
- ...r.tags === void 0 ? {} : { tags: r.tags },
975
- ...r.alt === void 0 ? {} : { alt: r.alt },
976
- ...r.caption === void 0 ? {} : { caption: r.caption }
977
- }).pipe(U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
1095
+ })) : I(n).pipe(q.flatMap(() => F.finalizeUpload({
1096
+ destination: n.destination,
1097
+ key: n.key,
1098
+ contentType: n.ct,
1099
+ tenantId: n.tenant,
1100
+ ownerId: n.sub,
1101
+ visibility: n.vis,
1102
+ ...n.access === void 0 ? {} : { access: n.access },
1103
+ ...n.folder === void 0 ? {} : { folder: n.folder },
1104
+ ...n.tags === void 0 ? {} : { tags: n.tags },
1105
+ ...n.alt === void 0 ? {} : { alt: n.alt },
1106
+ ...n.caption === void 0 ? {} : { caption: n.caption }
1107
+ }).pipe(q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
978
1108
  id: e.id,
979
1109
  url: t,
980
1110
  contentType: e.contentType,
@@ -985,112 +1115,120 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
985
1115
  }
986
1116
  },
987
1117
  {
988
- ...Ae,
989
- execute: (e, n) => {
990
- let i = e;
991
- return Z(n, l).pipe(U.flatMap((e) => h(i, n.request.subject).pipe(U.map((t) => ({
1118
+ ...ze,
1119
+ execute: (e, t) => {
1120
+ let n = e;
1121
+ return Z(t, S).pipe(q.flatMap((e) => D(n, t.request.subject).pipe(q.map((t) => ({
992
1122
  writer: e,
993
1123
  policy: t
994
- })))), U.flatMap(({ writer: e, policy: n }) => C.uploadTicketScope().pipe(U.map((a) => {
995
- let s = i.expiresInSec ?? d, l = H(), f = i.chunkSize && i.chunkSize > 0 ? i.chunkSize : u;
1124
+ })))), q.flatMap(({ writer: e, policy: t }) => _.select(e.tenantId).pipe(q.flatMap((a) => F.uploadTicketScope({
1125
+ tenantId: e.tenantId,
1126
+ destination: a.destination
1127
+ }).pipe(q.map((o) => {
1128
+ let s = n.expiresInSec ?? ce, c = ht(), l = n.chunkSize && n.chunkSize > 0 ? n.chunkSize : C;
996
1129
  return {
997
- uploadId: l,
998
- resumableToken: _e({
999
- writeScope: a,
1000
- instance: t,
1001
- id: l,
1130
+ uploadId: c,
1131
+ resumableToken: ve({
1132
+ writeScope: o,
1133
+ instance: r,
1134
+ destination: a.destination,
1135
+ id: c,
1002
1136
  sub: e.id,
1003
1137
  tenant: e.tenantId,
1004
- ct: i.contentType,
1005
- vis: i.visibility ?? "private",
1006
- ...n,
1007
- size: i.size,
1008
- ...i.folder === void 0 ? {} : { folder: i.folder },
1009
- ...i.tags === void 0 ? {} : { tags: i.tags },
1010
- ...i.alt === void 0 ? {} : { alt: i.alt },
1011
- ...i.caption === void 0 ? {} : { caption: i.caption },
1012
- ...c === void 0 ? {} : { max: c }
1138
+ ct: n.contentType,
1139
+ vis: n.visibility ?? "private",
1140
+ ...t,
1141
+ size: n.size,
1142
+ ...n.folder === void 0 ? {} : { folder: n.folder },
1143
+ ...n.tags === void 0 ? {} : { tags: n.tags },
1144
+ ...n.alt === void 0 ? {} : { alt: n.alt },
1145
+ ...n.caption === void 0 ? {} : { caption: n.caption },
1146
+ ...x === void 0 ? {} : { max: x }
1013
1147
  }, s),
1014
- uploadUrl: `${o ? o.replace(/\/+$/, "") : ""}${r}/upload/resumable`,
1015
- chunkSize: f,
1148
+ uploadUrl: `${y ? y.replace(/\/+$/, "") : ""}${i}/upload/resumable`,
1149
+ chunkSize: l,
1016
1150
  expiresInSec: s
1017
1151
  };
1018
- }))));
1152
+ }))))));
1019
1153
  }
1020
1154
  },
1021
1155
  {
1022
- ...ke,
1023
- execute: (e, n) => {
1024
- let r = e, i = r.expiresInSec ?? d;
1025
- return Z(n, l).pipe(U.flatMap((e) => h(r, n.request.subject).pipe(U.map((t) => ({
1156
+ ...Re,
1157
+ execute: (e, t) => {
1158
+ let n = e, i = n.expiresInSec ?? ce;
1159
+ return Z(t, S).pipe(q.flatMap((e) => D(n, t.request.subject).pipe(q.map((t) => ({
1026
1160
  writer: e,
1027
1161
  policy: t
1028
- })))), U.flatMap(({ writer: e, policy: n }) => C.uploadTicketScope().pipe(U.flatMap((a) => C.beginMultipartUpload({
1029
- contentType: r.contentType,
1162
+ })))), q.flatMap(({ writer: e, policy: t }) => F.beginMultipartUpload({
1163
+ contentType: n.contentType,
1030
1164
  tenantId: e.tenantId,
1031
- visibility: r.visibility ?? "private",
1165
+ visibility: n.visibility ?? "private",
1032
1166
  expiresInSec: i
1033
- }).pipe(U.map((o) => ({
1034
- multipartToken: ge({
1035
- writeScope: a,
1036
- instance: t,
1037
- key: o.key,
1038
- uploadId: o.uploadId,
1167
+ }).pipe(q.map((a) => ({
1168
+ multipartToken: _e({
1169
+ writeScope: a.writeScope,
1170
+ instance: r,
1171
+ destination: a.destination,
1172
+ key: a.key,
1173
+ uploadId: a.uploadId,
1039
1174
  sub: e.id,
1040
1175
  tenant: e.tenantId,
1041
- ct: r.contentType,
1042
- vis: r.visibility ?? "private",
1043
- ...n,
1044
- size: r.size,
1045
- ...r.folder === void 0 ? {} : { folder: r.folder },
1046
- ...r.tags === void 0 ? {} : { tags: r.tags },
1047
- ...r.alt === void 0 ? {} : { alt: r.alt },
1048
- ...r.caption === void 0 ? {} : { caption: r.caption }
1176
+ ct: n.contentType,
1177
+ vis: n.visibility ?? "private",
1178
+ ...t,
1179
+ size: n.size,
1180
+ ...n.folder === void 0 ? {} : { folder: n.folder },
1181
+ ...n.tags === void 0 ? {} : { tags: n.tags },
1182
+ ...n.alt === void 0 ? {} : { alt: n.alt },
1183
+ ...n.caption === void 0 ? {} : { caption: n.caption }
1049
1184
  }, i),
1050
- partSize: r.partSize && r.partSize >= u ? r.partSize : u,
1185
+ partSize: n.partSize && n.partSize >= C ? n.partSize : C,
1051
1186
  expiresInSec: i
1052
- })))))));
1187
+ })))));
1053
1188
  }
1054
1189
  },
1055
1190
  {
1056
- ...He,
1191
+ ...Ze,
1057
1192
  execute: (e) => {
1058
- let n = e, r = P(n.multipartToken);
1059
- return !r || r.instance !== t ? U.fail(new p({
1193
+ let t = e, n = B(t.multipartToken);
1194
+ return !n || n.instance !== r ? q.fail(new g({
1060
1195
  provider: "storage",
1061
1196
  message: "invalid or expired multipart token",
1062
1197
  transient: !1,
1063
1198
  status: 401
1064
- })) : !Number.isInteger(n.partNumber) || n.partNumber < 1 ? U.fail(new p({
1199
+ })) : !Number.isInteger(t.partNumber) || t.partNumber < 1 ? q.fail(new g({
1065
1200
  provider: "storage",
1066
1201
  message: "partNumber must be a positive integer",
1067
1202
  transient: !1,
1068
1203
  status: 400
1069
- })) : T(r).pipe(U.flatMap(() => C.signMultipartPart({
1070
- key: r.key,
1071
- uploadId: r.uploadId,
1072
- partNumber: n.partNumber
1073
- }).pipe(U.map((e) => ({ url: e })))));
1204
+ })) : I(n).pipe(q.flatMap(() => F.signMultipartPart({
1205
+ key: n.key,
1206
+ destination: n.destination,
1207
+ tenantId: n.tenant,
1208
+ uploadId: n.uploadId,
1209
+ partNumber: t.partNumber
1210
+ }).pipe(q.map((e) => ({ url: e })))));
1074
1211
  }
1075
1212
  },
1076
1213
  {
1077
- ...je,
1078
- execute: (e, n) => {
1079
- let r = e, i = P(r.multipartToken);
1080
- return !i || i.instance !== t ? U.fail(new p({
1214
+ ...Be,
1215
+ execute: (e, t) => {
1216
+ let n = e, i = B(n.multipartToken);
1217
+ return !i || i.instance !== r ? q.fail(new g({
1081
1218
  provider: "storage",
1082
1219
  message: "invalid or expired multipart token",
1083
1220
  transient: !1,
1084
1221
  status: 401
1085
- })) : _t(i.sub, n.request.subject.id) ? U.fail(new p({
1222
+ })) : zt(i.sub, t.request.subject.id) ? q.fail(new g({
1086
1223
  provider: "storage",
1087
1224
  message: "multipart token belongs to another subject",
1088
1225
  transient: !1,
1089
1226
  status: 403
1090
- })) : T(i).pipe(U.flatMap(() => C.completeMultipart({
1227
+ })) : I(i).pipe(q.flatMap(() => F.completeMultipart({
1228
+ destination: i.destination,
1091
1229
  key: i.key,
1092
1230
  uploadId: i.uploadId,
1093
- parts: r.parts,
1231
+ parts: n.parts,
1094
1232
  contentType: i.ct,
1095
1233
  tenantId: i.tenant,
1096
1234
  ownerId: i.sub,
@@ -1100,7 +1238,7 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
1100
1238
  ...i.tags === void 0 ? {} : { tags: i.tags },
1101
1239
  ...i.alt === void 0 ? {} : { alt: i.alt },
1102
1240
  ...i.caption === void 0 ? {} : { caption: i.caption }
1103
- }).pipe(U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
1241
+ }).pipe(q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
1104
1242
  id: e.id,
1105
1243
  url: t,
1106
1244
  size: e.size
@@ -1108,28 +1246,30 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
1108
1246
  }
1109
1247
  },
1110
1248
  {
1111
- ...Oe,
1249
+ ...Le,
1112
1250
  execute: (e) => {
1113
- let n = P(e.multipartToken);
1114
- return !n || n.instance !== t ? U.fail(new p({
1251
+ let t = B(e.multipartToken);
1252
+ return !t || t.instance !== r ? q.fail(new g({
1115
1253
  provider: "storage",
1116
1254
  message: "invalid or expired multipart token",
1117
1255
  transient: !1,
1118
1256
  status: 401
1119
- })) : T(n).pipe(U.flatMap(() => C.abortMultipart({
1120
- key: n.key,
1121
- uploadId: n.uploadId
1122
- }).pipe(U.as({ ok: !0 }))));
1257
+ })) : I(t).pipe(q.flatMap(() => F.abortMultipart({
1258
+ key: t.key,
1259
+ destination: t.destination,
1260
+ tenantId: t.tenant,
1261
+ uploadId: t.uploadId
1262
+ }).pipe(q.as({ ok: !0 }))));
1123
1263
  }
1124
1264
  },
1125
1265
  {
1126
- ...We,
1266
+ ...$e,
1127
1267
  execute: (e, t) => {
1128
1268
  let n = e, r = new Uint8Array(Buffer.from(n.bytesBase64, "base64"));
1129
- return Z(t, l).pipe(U.flatMap((e) => h(n, t.request.subject).pipe(U.map((t) => ({
1269
+ return Z(t, S).pipe(q.flatMap((e) => D(n, t.request.subject).pipe(q.map((t) => ({
1130
1270
  writer: e,
1131
1271
  policy: t
1132
- })))), U.flatMap(({ writer: e, policy: t }) => C.put({
1272
+ })))), q.flatMap(({ writer: e, policy: t }) => F.put({
1133
1273
  bytes: r,
1134
1274
  ...t,
1135
1275
  contentType: n.contentType,
@@ -1138,17 +1278,17 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
1138
1278
  ...n.visibility ? { visibility: n.visibility } : {},
1139
1279
  ...n.key ? { key: n.key } : {},
1140
1280
  ...n.password ? { password: n.password } : {}
1141
- })), U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
1281
+ })), q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
1142
1282
  id: e.id,
1143
1283
  url: t
1144
1284
  })))));
1145
1285
  }
1146
1286
  },
1147
1287
  {
1148
- ...Ne,
1288
+ ...He,
1149
1289
  execute: (e, t) => {
1150
1290
  let n = e;
1151
- return Z(t, l).pipe(U.flatMap((e) => C.ingestUrl(n.url, {
1291
+ return Z(t, S).pipe(q.flatMap((e) => F.ingestUrl(n.url, {
1152
1292
  tenantId: e.tenantId,
1153
1293
  ownerId: e.id,
1154
1294
  ...n.visibility ? { visibility: n.visibility } : {},
@@ -1157,45 +1297,45 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
1157
1297
  ...n.alt === void 0 ? {} : { alt: n.alt },
1158
1298
  ...n.caption === void 0 ? {} : { caption: n.caption },
1159
1299
  ...n.key === void 0 ? {} : { key: n.key }
1160
- })), U.flatMap((e) => C.getUrl(e.id).pipe(U.map((t) => ({
1300
+ })), q.flatMap((e) => F.getUrl(e.id).pipe(q.map((t) => ({
1161
1301
  id: e.id,
1162
1302
  url: t
1163
1303
  })))));
1164
1304
  }
1165
1305
  },
1166
1306
  {
1167
- ...Ve,
1307
+ ...Xe,
1168
1308
  execute: (e, t) => {
1169
1309
  let n = e, r = n.expiresInDays ? new Date(Date.now() + n.expiresInDays * 864e5) : null;
1170
- return X(C, n.refId, t).pipe(U.flatMap(() => C.grant({
1310
+ return Lt(F, n.refId, t).pipe(q.flatMap(() => F.grant({
1171
1311
  refId: n.refId,
1172
1312
  principalType: n.principalType,
1173
1313
  principalId: n.principalId,
1174
1314
  ...n.permission ? { permission: n.permission } : {},
1175
1315
  expiresAt: r,
1176
1316
  createdBy: t.request.subject.id
1177
- })), U.map((e) => ({ grantId: e.id })));
1317
+ })), q.map((e) => ({ grantId: e.id })));
1178
1318
  }
1179
1319
  },
1180
1320
  {
1181
- ...Be,
1321
+ ...Ye,
1182
1322
  execute: (e, t) => {
1183
1323
  let n = e;
1184
- return C.getGrant(n.grantId).pipe(U.flatMap((e) => e === null ? U.fail(Y()) : X(C, e.refId, t)), U.flatMap(() => C.revoke(n.grantId)), U.map(() => ({ ok: !0 })));
1324
+ return F.getGrant(n.grantId).pipe(q.flatMap((e) => e === null ? q.fail(It()) : Lt(F, e.refId, t)), q.flatMap(() => F.revoke(n.grantId)), q.map(() => ({ ok: !0 })));
1185
1325
  }
1186
1326
  },
1187
1327
  {
1188
- ...Pe,
1328
+ ...Ue,
1189
1329
  execute: (e, t) => {
1190
1330
  let n = e;
1191
- return X(C, n.refId, t).pipe(U.flatMap(() => C.listGrants(n.refId)), U.map((e) => ({ grants: e.map(Q) })));
1331
+ return Lt(F, n.refId, t).pipe(q.flatMap(() => F.listGrants(n.refId)), q.map((e) => ({ grants: e.map(Bt) })));
1192
1332
  }
1193
1333
  },
1194
1334
  {
1195
- ...Fe,
1335
+ ...We,
1196
1336
  execute: (e, t) => {
1197
1337
  let n = e;
1198
- return C.listRefs({
1338
+ return F.listRefs({
1199
1339
  tenantId: t.request.subject.tenantId,
1200
1340
  ...n.folder === void 0 ? {} : { folder: n.folder },
1201
1341
  ...n.tags === void 0 ? {} : { tags: n.tags },
@@ -1203,121 +1343,154 @@ var lt = w.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", K, ut = (e) => {
1203
1343
  ...n.includeDerived === void 0 ? {} : { includeDerived: n.includeDerived },
1204
1344
  ...n.limit === void 0 ? {} : { limit: n.limit },
1205
1345
  ...n.offset === void 0 ? {} : { offset: n.offset }
1206
- }).pipe(U.map((e) => ({
1207
- refs: e.refs.map(ht),
1346
+ }).pipe(q.map((e) => ({
1347
+ refs: e.refs.map(Ft),
1208
1348
  nextOffset: e.nextOffset
1209
1349
  })));
1210
1350
  }
1211
1351
  }
1212
- ];
1213
- return at({
1214
- name: G({
1215
- base: $,
1352
+ ], nt = yt({
1353
+ name: xt({
1354
+ base: Q,
1216
1355
  alias: e.alias,
1217
1356
  instance: e.name
1218
1357
  }),
1219
- baseName: $,
1358
+ baseName: Q,
1220
1359
  description: "File storage — StorageService over S3 / MinIO / filesystem / memory. Public objects served direct from bucket/CDN; private objects gated by an access policy + per-object grants. Checksums, tenant key-prefixing, _voltro_storage_refs + _voltro_storage_grants, retry.",
1221
1360
  permissions: [
1222
1361
  "network:outbound:*",
1223
1362
  "inspect:read",
1224
1363
  "inspect:write"
1225
1364
  ],
1226
- declaredEnv: w.declared,
1227
- egress: pt(e),
1228
- services: ie,
1229
- inspectEndpoints: De,
1365
+ declaredEnv: E.declared,
1366
+ egress: Mt(e),
1367
+ services: fe,
1368
+ inspectEndpoints: et,
1230
1369
  httpRoutes: [
1231
- pe,
1232
- he,
1233
- I
1370
+ Ne,
1371
+ Pe,
1372
+ Fe
1234
1373
  ],
1235
- routes: Ge,
1236
- rpcClientDescriptors: Ue,
1374
+ routes: tt,
1375
+ rpcClientDescriptors: Qe,
1237
1376
  errorSchemas: [
1238
1377
  {
1239
- schema: p,
1378
+ schema: g,
1240
1379
  import: {
1241
1380
  module: "@voltro/plugin-storage/errors",
1242
1381
  name: "StorageError"
1243
1382
  }
1244
1383
  },
1245
1384
  {
1246
- schema: te,
1385
+ schema: ne,
1247
1386
  import: {
1248
1387
  module: "@voltro/plugin-storage/errors",
1249
1388
  name: "StorageAccessDenied"
1250
1389
  }
1251
1390
  },
1252
1391
  {
1253
- schema: ne,
1392
+ schema: re,
1254
1393
  import: {
1255
1394
  module: "@voltro/plugin-storage/errors",
1256
1395
  name: "StorageRejected"
1257
1396
  }
1258
1397
  },
1259
1398
  {
1260
- schema: re,
1399
+ schema: ie,
1261
1400
  import: {
1262
1401
  module: "@voltro/plugin-storage/errors",
1263
1402
  name: "StorageScanRejected"
1264
1403
  }
1265
1404
  }
1266
1405
  ],
1267
- capabilities: { mediaPersist: {
1268
- put: async ({ bytes: e, mediaType: t, tenantId: n, operationId: r, ownerId: i, visibility: a, tags: o }) => {
1269
- let s = await U.runPromise(C.put({
1270
- bytes: e,
1271
- contentType: t,
1272
- tenantId: n,
1273
- kind: "media",
1274
- ...i === void 0 ? {} : { ownerId: i },
1275
- ...a === void 0 ? {} : { visibility: a },
1276
- ...o === void 0 ? {} : { tags: o },
1277
- ...y.current === void 0 ? {} : { operationId: r }
1278
- }));
1279
- return {
1280
- url: await U.runPromise(C.getUrl(s.id, { tenantId: n })),
1281
- refId: s.id
1282
- };
1406
+ ...s ? { needs: { connections: [{
1407
+ kind: "storage",
1408
+ role: "objects",
1409
+ optional: e.connection === void 0
1410
+ }] } } : {},
1411
+ ...e.connection === void 0 ? {} : { connectionBindings: { objects: e.connection } },
1412
+ capabilities: {
1413
+ storageProvider: () => m,
1414
+ storageDestinations: {
1415
+ router: _,
1416
+ directDestination: e.connection === void 0 ? c : void 0,
1417
+ registry: () => f
1283
1418
  },
1284
- ingestUrl: async ({ url: e, tenantId: t, operationId: n, ownerId: r, visibility: i, tags: a }) => {
1285
- let o = await U.runPromise(C.ingestUrl(e, {
1286
- tenantId: t,
1287
- ...r === void 0 ? {} : { ownerId: r },
1288
- ...i === void 0 ? {} : { visibility: i },
1289
- ...a === void 0 ? {} : { tags: a },
1290
- ...y.current === void 0 ? {} : { operationId: n }
1291
- }));
1292
- return {
1293
- url: await U.runPromise(C.getUrl(o.id, { tenantId: t })),
1294
- refId: o.id
1295
- };
1419
+ mediaPersist: {
1420
+ put: async ({ bytes: e, mediaType: t, tenantId: n, operationId: r, ownerId: i, visibility: a, tags: o }) => {
1421
+ let s = await q.runPromise(F.put({
1422
+ bytes: e,
1423
+ contentType: t,
1424
+ tenantId: n,
1425
+ kind: "media",
1426
+ ...i === void 0 ? {} : { ownerId: i },
1427
+ ...a === void 0 ? {} : { visibility: a },
1428
+ ...o === void 0 ? {} : { tags: o },
1429
+ ...A.current === void 0 ? {} : { operationId: r }
1430
+ }));
1431
+ return {
1432
+ url: await q.runPromise(F.getUrl(s.id, { tenantId: n })),
1433
+ refId: s.id
1434
+ };
1435
+ },
1436
+ ingestUrl: async ({ url: e, tenantId: t, operationId: n, ownerId: r, visibility: i, tags: a }) => {
1437
+ let o = await q.runPromise(F.ingestUrl(e, {
1438
+ tenantId: t,
1439
+ ...r === void 0 ? {} : { ownerId: r },
1440
+ ...i === void 0 ? {} : { visibility: i },
1441
+ ...a === void 0 ? {} : { tags: a },
1442
+ ...A.current === void 0 ? {} : { operationId: n }
1443
+ }));
1444
+ return {
1445
+ url: await q.runPromise(F.getUrl(o.id, { tenantId: t })),
1446
+ refId: o.id
1447
+ };
1448
+ }
1296
1449
  }
1297
- } },
1298
- bindDataStore: async (e) => {
1299
- let r = await U.runPromise(i.objectNamespace);
1300
- (f || m) && await ee(e), (f || m) && await _(e), f && await n(e), f && (g.current = Ce(e, {
1301
- storageInstance: t,
1302
- objectNamespace: r
1303
- })), m && (v.current = Se(e, {
1304
- storageInstance: t,
1305
- objectNamespace: r
1306
- })), f && m && (y.current = Ee(e, {
1307
- storageInstance: t,
1308
- objectNamespace: r
1309
- })), i.bindDataStore?.(e);
1310
1450
  },
1311
- onActivate: (t) => U.sync(() => {
1312
- let n = !!o || !!w.read("STORAGE_CDN_URL", e.cdnBaseUrl);
1313
- t.logger.info(`storage: provider=${i.name} presign=${i.presigns ? "yes" : "no"} publicUrl=${n ? "yes" : "no"} transforms=yes`, {
1314
- provider: i.name,
1315
- bucket: a,
1316
- refStore: f ? "memory" : "custom"
1317
- }), i.name === "memory" && process.env.NODE_ENV !== "test" && t.logger.warn("storage: provider=memory — uploads are IN-MEMORY and LOST on restart. Set STORAGE_PROVIDER (filesystem / s3 / r2 / azure / gcs) for anything but throwaway dev. (Empty/placeholder storage env silently falls back to memory.)"), !n && !i.presigns && t.logger.warn("storage: no VOLTRO_PUBLIC_URL / STORAGE_CDN_URL and this provider can't presign — serve URLs are RELATIVE and will not resolve cross-origin (browser <img> / server-to-server AI fetches). Set VOLTRO_PUBLIC_URL to this api's absolute base URL.");
1451
+ bindDataStore: async (t, n) => {
1452
+ let i = s ? n?.connectionReference?.("objects", "storage") || e.connection : void 0;
1453
+ await q.runPromise(oe(n?.connectionRegistry, i)), P = n?.storageReferenceProvider;
1454
+ let a = {
1455
+ storageInstance: r,
1456
+ resolveNamespace: (e) => _.resolve(e).pipe(q.flatMap((e) => e.objectNamespace))
1457
+ };
1458
+ At(m), (w || T) && await te(t), (w || T) && await se(t), (w || !p && m.name === "database") && await o(t), w && (O.current = De(t, a)), T && (k.current = Ee(t, a)), w && T && (A.current = Me(t, a), j.current = t), p || m.bindDataStore?.(t);
1459
+ },
1460
+ onActivate: (t) => q.gen(function* () {
1461
+ let n = s ? t.connectionReference?.("objects", "storage") || e.connection : void 0;
1462
+ if (yield* oe(t.connectionRegistry, n), p) {
1463
+ t.logger.info("storage: tenant-scoped named destination; resolved per request", { destination: h });
1464
+ return;
1465
+ }
1466
+ let r = !!y || !!E.read("STORAGE_CDN_URL", e.cdnBaseUrl);
1467
+ t.logger.info(`storage: provider=${m.name} presign=${m.presigns ? "yes" : "no"} publicUrl=${r ? "yes" : "no"} transforms=yes`, {
1468
+ provider: m.name,
1469
+ bucket: h,
1470
+ refStore: w ? "memory" : "custom"
1471
+ }), m.name === "memory" && process.env.NODE_ENV !== "test" && t.logger.warn("storage: provider=memory — uploads are IN-MEMORY and LOST on restart. Set STORAGE_PROVIDER to filesystem, s3, minio, azure or database and configure a durable destination."), !r && !m.presigns && t.logger.warn("storage: no VOLTRO_PUBLIC_URL / STORAGE_CDN_URL and this provider can't presign — serve URLs are RELATIVE and will not resolve cross-origin (browser <img> / server-to-server AI fetches). Set VOLTRO_PUBLIC_URL to this api's absolute base URL.");
1318
1472
  }),
1319
- onDeactivate: () => U.void
1473
+ onDeactivate: () => q.void
1474
+ });
1475
+ return $.set(nt, () => A.current === void 0 ? void 0 : {
1476
+ service: F,
1477
+ metadata: A.current,
1478
+ bucket: h
1479
+ }), nt;
1480
+ }, $ = /* @__PURE__ */ new WeakMap(), Wt = (e) => {
1481
+ let t = e.filter((e) => $.has(e));
1482
+ if (t.length !== 1) throw new g({
1483
+ provider: "storage",
1484
+ message: t.length ? "Storage objects are ambiguous; installed instances: " + t.map((e) => e.name).sort().join(", ") : "This app has no Storage plugin",
1485
+ transient: !1
1486
+ });
1487
+ let n = $.get(t[0])();
1488
+ if (n === void 0) throw new g({
1489
+ provider: "storage",
1490
+ message: "Storage objects need a bound data store with the default reference and grant stores",
1491
+ transient: !1
1320
1492
  });
1493
+ return n;
1321
1494
  };
1322
1495
  //#endregion
1323
- export { i as STORAGE_BLOBS_TABLE, a as STORAGE_GRANTS_TABLE, t as STORAGE_REFERENCE_CATALOGUE_TABLE, o as STORAGE_REFS_TABLE, s as STORAGE_USAGE_TABLE, te as StorageAccessDenied, p as StorageError, ne as StorageRejected, re as StorageScanRejected, m as StorageService, c as _voltroStorageBlobsTable, l as _voltroStorageGrantsTable, u as _voltroStorageRefsTable, d as _voltroStorageUsageTable, dt as appStorageProvider, Ye as assetRef, g as assignStorageObjectNamespacesInTransaction, f as assignStorageReferenceInstancesInTransaction, T as azureProvider, xe as buildStorageService, v as bytesToStream, I as checkLimits, Ge as clamavScanner, E as clearStorageBuffer, y as collectStream, b as concatBytes, ft as configuredStorageProvider, L as contentTypeAllowed, Se as dataStoreGrantStore, Ce as dataStoreRefStore, D as databaseProvider, Ze as decodeDataUri, $e as durabilityNotice, j as editImage, Ke as encodeInstream, n as ensureStorageReferenceCatalogue, z as fetchIngestBody, Je as ffmpegTranscoder, O as filesystemProvider, x as fromNodeReadable, S as getObjectStream, M as hashPassword, Ee as makeStorageObjectMetadata, we as memoryGrantStore, k as memoryProvider, B as memoryRefStore, qe as parseClamavResponse, se as parseTransformParams, et as pathDurability, Qe as putAvatar, ie as putObjectStream, ae as readStorageBuffer, V as refMatchesListQuery, _ as requireStorageObjectNamespaces, q as resolveStorageProvider, pe as resolveStorageSecret, A as s3Provider, ut as setActiveStorageProvider, me as signFinalizeTicket, he as signGrant, ge as signMultipartTicket, _e as signResumableTicket, N as signUploadTicket, R as sniffMatches, pt as storageEgressOf, vt as storageInstanceName, h as storageObjectLifecycleTables, e as storageObjectNamespace, bt as storagePlugin, De as storagePutIntentsTable, r as storageReferenceCatalogueTable, Te as storageServiceLayer, C as toNodeReadable, ce as transformCached, le as transformFallbackReason, ue as transformImage, ve as verifyFinalizeTicket, ye as verifyGrant, P as verifyMultipartTicket, fe as verifyPassword, be as verifyResumableTicket, F as verifyUploadTicket, Xe as withStorage };
1496
+ export { c as STORAGE_BLOBS_TABLE, l as STORAGE_GRANTS_TABLE, a as STORAGE_REFERENCE_CATALOGUE_TABLE, u as STORAGE_REFS_TABLE, d as STORAGE_USAGE_TABLE, ne as StorageAccessDenied, ct as StorageAddress, Fe as StorageCustody, g as StorageError, re as StorageRejected, ie as StorageScanRejected, _ as StorageService, ee as _voltroStorageBlobsTable, f as _voltroStorageGrantsTable, p as _voltroStorageRefsTable, m as _voltroStorageUsageTable, Wt as appStorageObjects, jt as appStorageProvider, e as appStorageRouter, et as assetRef, y as assignStorageObjectNamespacesInTransaction, h as assignStorageReferenceInstancesInTransaction, D as azureProvider, Te as buildStorageService, b as bytesToStream, V as checkLimits, U as clamavScanner, O as clearStorageBuffer, x as collectStream, S as concatBytes, kt as configuredStorageProvider, ot as connectStorage, Ce as contentTypeAllowed, Ee as dataStoreGrantStore, De as dataStoreRefStore, k as databaseProvider, nt as decodeDataUri, it as durabilityNotice, ue as editImage, W as encodeInstream, o as ensureStorageReferenceCatalogue, r as existingFilesystemObjectNamespace, H as fetchIngestBody, K as ffmpegTranscoder, A as filesystemProvider, C as fromNodeReadable, ce as getObjectStream, F as hashPassword, ut as isStorageKey, t as makeStorageDestinationRouter, Me as makeStorageObjectMetadata, Oe as memoryGrantStore, j as memoryProvider, ke as memoryRefStore, G as parseClamavResponse, de as parseTransformParams, at as pathDurability, rt as putAvatar, w as putObjectStream, N as readStorageBuffer, Ae as refMatchesListQuery, se as requireStorageObjectNamespaces, X as resolveStorageProvider, L as resolveStorageSecret, Pe as retainedCaptureTables, P as s3Provider, ge as signFinalizeTicket, R as signGrant, _e as signMultipartTicket, ve as signResumableTicket, z as signUploadTicket, we as sniffMatches, dt as storageAddressIdentity, n as storageByteSecurityHeaders, Mt as storageEgressOf, Vt as storageInstanceName, oe as storageObjectLifecycleTables, i as storageObjectNamespace, Ut as storagePlugin, Ne as storagePutIntentsTable, v as storageRefAddress, s as storageReferenceCatalogueTable, je as storageServiceLayer, T as toNodeReadable, fe as transformCached, pe as transformFallbackReason, me as transformImage, ye as verifyFinalizeTicket, be as verifyGrant, B as verifyMultipartTicket, I as verifyPassword, xe as verifyResumableTicket, Se as verifyUploadTicket, tt as withStorage, st as withStorageKeyPrefix };