@voltro/plugin-storage 0.1.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 ADDED
@@ -0,0 +1,2806 @@
1
+ import { StorageAccessDenied as e, StorageError as t, StorageRejected as n, StorageScanRejected as r, StorageService as i } from "./types.js";
2
+ import { abortMultipartUploadDescriptor as a, beginMultipartUploadDescriptor as o, beginResumableUploadDescriptor as s, completeMultipartUploadDescriptor as c, finalizeUploadDescriptor as l, ingestUrlDescriptor as u, listGrantsDescriptor as d, listRefsDescriptor as f, mintPresignedUploadDescriptor as p, mintUploadTicketDescriptor as m, mintUploadUrlDescriptor as h, mintUrlDescriptor as g, revokeDescriptor as _, shareDescriptor as ee, signMultipartPartDescriptor as te, storageRpcClientImports as v, uploadDescriptor as ne } from "./rpc.js";
3
+ import { Chunk as y, Effect as b, Exit as x, Layer as S, Schedule as C, Stream as w } from "effect";
4
+ import { definePlugin as re } from "@voltro/protocol";
5
+ import { readCookie as T, resolveSessionSecret as ie, verifySession as ae } from "@voltro/protocol/session";
6
+ import { and as E, bytes as oe, eq as D, generateId as O, id as k, integer as A, json as j, mixin as se, reference as ce, table as M, text as N, timestamp as P } from "@voltro/database";
7
+ import { pluginEnv as le } from "@voltro/env";
8
+ import { createHmac as F, randomBytes as I, randomUUID as ue, scryptSync as de, timingSafeEqual as L } from "node:crypto";
9
+ import { spawn as fe } from "node:child_process";
10
+ import { mkdtemp as pe, readFile as me, rm as R, writeFile as z } from "node:fs/promises";
11
+ import { tmpdir as he } from "node:os";
12
+ import { join as B } from "node:path";
13
+ //#region src/streamBridge.ts
14
+ var ge = 1 << 20, _e = (e, n, r = !1) => new t({
15
+ provider: e,
16
+ message: n,
17
+ transient: r
18
+ }), ve = (e) => {
19
+ let t = 0;
20
+ for (let n of e) t += n.byteLength;
21
+ let n = new Uint8Array(t), r = 0;
22
+ for (let t of e) n.set(t, r), r += t.byteLength;
23
+ return n;
24
+ }, ye = (e, t = ge) => {
25
+ let n = [];
26
+ for (let r = 0; r < e.byteLength; r += t) n.push(e.subarray(r, Math.min(r + t, e.byteLength)));
27
+ return w.fromIterable(n);
28
+ }, be = (e) => e.pipe(w.runCollect, b.map((e) => ve(y.toReadonlyArray(e)))), xe = (e, t) => w.suspend(() => w.fromAsyncIterable(t(), (t) => _e(e, `read stream failed: ${String(t?.message ?? t)}`, !0))).pipe(w.map((e) => e instanceof Uint8Array ? e : new Uint8Array(e))), Se = async (e) => {
29
+ let { Readable: t } = await import("node:stream");
30
+ return t.fromWeb(w.toReadableStream(e));
31
+ }, Ce = (e, t) => e.getStream ? e.getStream(t) : e.get(t).pipe(b.map((e) => ({
32
+ stream: ye(e.bytes),
33
+ contentType: e.contentType,
34
+ size: e.bytes.byteLength
35
+ }))), we = (e, t, n, r) => e.putStream ? e.putStream(t, n, r) : be(n).pipe(b.flatMap((n) => e.put(t, n, {
36
+ contentType: r.contentType,
37
+ ...r.public === void 0 ? {} : { public: r.public }
38
+ }))), V = "_voltro_storage_refs", H = "_voltro_storage_grants", U = "_voltro_storage_blobs", Te = "_voltro_storage_usage", Ee = M(V, {
39
+ id: k({ prefix: "file" }),
40
+ tenantId: N().nullable(),
41
+ ownerId: N().nullable(),
42
+ bucket: N(),
43
+ key: N(),
44
+ contentType: N(),
45
+ size: A(),
46
+ checksum: N(),
47
+ visibility: N(),
48
+ accessPolicy: j().nullable(),
49
+ passwordHash: N().nullable(),
50
+ width: A().nullable(),
51
+ height: A().nullable(),
52
+ duration: A().nullable(),
53
+ placeholder: N().nullable(),
54
+ folder: N().nullable(),
55
+ tags: j().nullable(),
56
+ alt: N().nullable(),
57
+ caption: N().nullable(),
58
+ derivedFrom: N().nullable(),
59
+ kind: N().nullable(),
60
+ createdAt: P()
61
+ }).index(["key"]).index(["tenantId"]).index(["ownerId"]).index(["folder"]).index(["derivedFrom"]), De = M(H, {
62
+ id: k({ prefix: "sgrant" }),
63
+ refId: N(),
64
+ principalType: N(),
65
+ principalId: N(),
66
+ permission: N(),
67
+ createdAt: P(),
68
+ expiresAt: P().nullable(),
69
+ createdBy: N().nullable()
70
+ }).index(["refId"]).index(["principalId"]), Oe = M(Te, {
71
+ id: k({ prefix: "susage" }),
72
+ tenantId: N().maxLength(191),
73
+ bytes: A().default(0),
74
+ count: A().default(0),
75
+ updatedAt: P()
76
+ }).unique("byStorageUsageTenant", ["tenantId"]), ke = M(U, {
77
+ id: k({ prefix: "blob" }),
78
+ key: N(),
79
+ data: oe(),
80
+ contentType: N(),
81
+ size: A()
82
+ }).index(["key"]), W = le([
83
+ {
84
+ name: "STORAGE_PROVIDER",
85
+ required: !1,
86
+ secret: !1,
87
+ description: "Provider name: 's3' | 'minio' | 'azure' | 'database' | 'filesystem' | 'memory'. Default 'memory'."
88
+ },
89
+ {
90
+ name: "S3_BUCKET",
91
+ required: !1,
92
+ secret: !1,
93
+ description: "S3/MinIO bucket name. Default STORAGE_BUCKET, else \"voltro\"."
94
+ },
95
+ {
96
+ name: "STORAGE_BUCKET",
97
+ required: !1,
98
+ secret: !1,
99
+ description: "Fallback bucket name when S3_BUCKET is unset."
100
+ },
101
+ {
102
+ name: "S3_REGION",
103
+ required: !1,
104
+ secret: !1,
105
+ description: "S3 region. Falls back to AWS_REGION."
106
+ },
107
+ {
108
+ name: "AWS_REGION",
109
+ required: !1,
110
+ secret: !1,
111
+ description: "AWS region fallback for the S3 provider."
112
+ },
113
+ {
114
+ name: "S3_ENDPOINT",
115
+ required: !1,
116
+ secret: !1,
117
+ description: "Custom S3 endpoint URL. Falls back to MINIO_ENDPOINT."
118
+ },
119
+ {
120
+ name: "MINIO_ENDPOINT",
121
+ required: !1,
122
+ secret: !1,
123
+ description: "MinIO endpoint URL fallback for the S3 provider."
124
+ },
125
+ {
126
+ name: "S3_ACCESS_KEY_ID",
127
+ required: !1,
128
+ secret: !0,
129
+ description: "S3 access key id. Falls back to AWS_ACCESS_KEY_ID."
130
+ },
131
+ {
132
+ name: "AWS_ACCESS_KEY_ID",
133
+ required: !1,
134
+ secret: !0,
135
+ description: "AWS access key id fallback for the S3 provider."
136
+ },
137
+ {
138
+ name: "S3_SECRET_ACCESS_KEY",
139
+ required: !1,
140
+ secret: !0,
141
+ description: "S3 secret access key. Falls back to AWS_SECRET_ACCESS_KEY."
142
+ },
143
+ {
144
+ name: "AWS_SECRET_ACCESS_KEY",
145
+ required: !1,
146
+ secret: !0,
147
+ description: "AWS secret access key fallback for the S3 provider."
148
+ },
149
+ {
150
+ name: "S3_FORCE_PATH_STYLE",
151
+ required: !1,
152
+ secret: !1,
153
+ description: "Set to \"1\" to force path-style S3 addressing."
154
+ },
155
+ {
156
+ name: "STORAGE_CDN_URL",
157
+ required: !1,
158
+ secret: !1,
159
+ description: "Public base URL (CDN) served in front of the bucket for public objects."
160
+ },
161
+ {
162
+ name: "VOLTRO_PUBLIC_URL",
163
+ required: !1,
164
+ secret: !1,
165
+ description: "Absolute public base URL of THIS api (scheme://host[:port]) — prepended to the /_voltro/storage/:id serve URL so getUrl/mintUrl/the upload route emit ABSOLUTE URLs. Required when the api is a different origin than the app."
166
+ },
167
+ {
168
+ name: "STORAGE_ALLOWED_ORIGINS",
169
+ required: !1,
170
+ secret: !1,
171
+ description: "Comma-separated origins allowed to read the serve route + POST the upload route cross-origin (CORS). Use '*' for any. Omit for same-origin only."
172
+ },
173
+ {
174
+ name: "AZURE_STORAGE_ACCOUNT",
175
+ required: !1,
176
+ secret: !1,
177
+ description: "Azure storage account name (provider \"azure\")."
178
+ },
179
+ {
180
+ name: "AZURE_STORAGE_KEY",
181
+ required: !1,
182
+ secret: !0,
183
+ description: "Azure account key — needed to mint SAS URLs."
184
+ },
185
+ {
186
+ name: "AZURE_STORAGE_CONNECTION_STRING",
187
+ required: !1,
188
+ secret: !0,
189
+ description: "Azure connection string (carries the account key)."
190
+ },
191
+ {
192
+ name: "AZURE_STORAGE_ENDPOINT",
193
+ required: !1,
194
+ secret: !1,
195
+ description: "Custom Azure blob endpoint (e.g. Azurite)."
196
+ },
197
+ {
198
+ name: "AZURE_STORAGE_CONTAINER",
199
+ required: !1,
200
+ secret: !1,
201
+ description: "Azure blob container name (the \"bucket\")."
202
+ },
203
+ {
204
+ name: "STORAGE_ROOT",
205
+ required: !1,
206
+ secret: !1,
207
+ description: "Filesystem provider root directory. Default \".voltro-storage\"."
208
+ },
209
+ {
210
+ name: "VOLTRO_STORAGE_SECRET",
211
+ required: !1,
212
+ secret: !0,
213
+ description: "HMAC secret for signed grant tokens. Falls back to the session secret."
214
+ },
215
+ {
216
+ name: "VOLTRO_SESSION_COOKIE",
217
+ required: !1,
218
+ secret: !1,
219
+ description: "Session cookie name used to resolve private-object access. Default \"voltro:session\"."
220
+ },
221
+ {
222
+ name: "CLAMAV_HOST",
223
+ required: !1,
224
+ secret: !1,
225
+ description: "clamd host for the optional clamav virus scanner. Default 127.0.0.1."
226
+ },
227
+ {
228
+ name: "CLAMAV_PORT",
229
+ required: !1,
230
+ secret: !1,
231
+ description: "clamd port for the optional clamav virus scanner. Default 3310."
232
+ }
233
+ ]), G = (e, n, r, i) => new t({
234
+ provider: e,
235
+ message: n,
236
+ transient: r,
237
+ ...i === void 0 ? {} : { status: i }
238
+ }), K = (e) => e === void 0 || e === 408 || e === 429 || e >= 500, Ae = /* @__PURE__ */ new Map(), je = (e, t) => Ae.get(e)?.get(t)?.bytes, Me = (e) => {
239
+ Ae.get(e)?.clear();
240
+ }, Ne = (e = "default") => {
241
+ let t = Ae.get(e) ?? /* @__PURE__ */ new Map();
242
+ return Ae.set(e, t), {
243
+ name: "memory",
244
+ presigns: !1,
245
+ put: (e, n, r) => b.sync(() => (t.set(e, {
246
+ bytes: n,
247
+ contentType: r.contentType
248
+ }), { size: n.byteLength })),
249
+ get: (e) => b.suspend(() => {
250
+ let n = t.get(e);
251
+ return n ? b.succeed({
252
+ bytes: n.bytes,
253
+ contentType: n.contentType
254
+ }) : b.fail(G("memory", `no object at key '${e}'`, !1, 404));
255
+ }),
256
+ delete: (e) => b.sync(() => {
257
+ t.delete(e);
258
+ }),
259
+ publicUrl: () => null,
260
+ getUrl: () => b.succeed(""),
261
+ head: (e) => b.sync(() => {
262
+ let n = t.get(e);
263
+ return n ? { size: n.bytes.byteLength } : null;
264
+ })
265
+ };
266
+ }, Pe = (e) => {
267
+ let t = e.root, n = async (e) => {
268
+ let { join: n, resolve: r, sep: i } = await import("node:path"), a = r(t), o = r(a, e);
269
+ if (o !== a && !o.startsWith(a + i)) throw Error(`key '${e}' escapes the storage root`);
270
+ return n(o);
271
+ }, r = (e, t, n = !1) => b.tryPromise({
272
+ try: e,
273
+ catch: (e) => e?.code === "ENOENT" ? G("filesystem", `no object at key '${t}'`, !1, 404) : G("filesystem", String(e?.message ?? e), n)
274
+ });
275
+ return {
276
+ name: "filesystem",
277
+ presigns: !1,
278
+ put: (e, t, i) => r(async () => {
279
+ let r = await import("node:fs/promises"), { dirname: i } = await import("node:path"), { randomUUID: a } = await import("node:crypto"), o = await n(e), s = i(o);
280
+ await r.mkdir(s, { recursive: !0 });
281
+ let c = `${o}.tmp-${a()}`;
282
+ try {
283
+ await r.writeFile(c, t), await r.rename(c, o);
284
+ } catch (e) {
285
+ throw await r.rm(c, { force: !0 }).catch(() => {}), e;
286
+ }
287
+ return { size: t.byteLength };
288
+ }, e, !0),
289
+ get: (e) => r(async () => {
290
+ let t = await (await import("node:fs/promises")).readFile(await n(e));
291
+ return {
292
+ bytes: new Uint8Array(t),
293
+ contentType: "application/octet-stream"
294
+ };
295
+ }, e),
296
+ delete: (e) => r(async () => {
297
+ await (await import("node:fs/promises")).rm(await n(e), { force: !0 });
298
+ }, e),
299
+ publicUrl: () => null,
300
+ getUrl: () => b.succeed(""),
301
+ head: (e) => b.tryPromise({
302
+ try: async () => {
303
+ let t = await import("node:fs/promises");
304
+ try {
305
+ return { size: (await t.stat(await n(e))).size };
306
+ } catch (e) {
307
+ if (e?.code === "ENOENT") return null;
308
+ throw e;
309
+ }
310
+ },
311
+ catch: (e) => G("filesystem", String(e?.message ?? e), !1)
312
+ }),
313
+ getStream: (e) => r(async () => {
314
+ let t = await import("node:fs/promises"), { createReadStream: r } = await import("node:fs"), i = await n(e), a = await t.stat(i);
315
+ return {
316
+ stream: xe("filesystem", () => r(i)),
317
+ contentType: "application/octet-stream",
318
+ size: a.size
319
+ };
320
+ }, e),
321
+ putStream: (e, t, i) => r(async () => {
322
+ let r = await import("node:fs/promises"), { dirname: i } = await import("node:path"), { randomUUID: a } = await import("node:crypto"), { createWriteStream: o } = await import("node:fs"), { pipeline: s } = await import("node:stream/promises"), c = await n(e);
323
+ await r.mkdir(i(c), { recursive: !0 });
324
+ let l = `${c}.tmp-${a()}`;
325
+ try {
326
+ await s(await Se(t), o(l));
327
+ let e = await r.stat(l);
328
+ return await r.rename(l, c), { size: e.size };
329
+ } catch (e) {
330
+ throw await r.rm(l, { force: !0 }).catch(() => {}), e;
331
+ }
332
+ }, e, !0),
333
+ getRange: (e, t, i) => r(async () => {
334
+ let { createReadStream: r } = await import("node:fs"), a = await n(e), o = [];
335
+ return await new Promise((e, n) => {
336
+ let s = r(a, {
337
+ start: t,
338
+ end: i
339
+ });
340
+ s.on("data", (e) => o.push(e)), s.on("end", () => e()), s.on("error", n);
341
+ }), ve(o);
342
+ }, e)
343
+ };
344
+ }, Fe = (e) => {
345
+ let t = e.name ?? "s3", n = e.publicAcl ?? !0, r = null, i = () => r || (r = (async () => {
346
+ let t, n;
347
+ try {
348
+ t = await import("@aws-sdk/client-s3"), n = await import("@aws-sdk/s3-request-presigner");
349
+ } catch (e) {
350
+ throw Error("the 's3'/'minio' storage provider requires the '@aws-sdk/client-s3' + '@aws-sdk/s3-request-presigner' optional dependencies. Install them to enable it.", { cause: e });
351
+ }
352
+ return {
353
+ client: new t.S3Client({
354
+ ...e.region ? { region: e.region } : { region: "us-east-1" },
355
+ ...e.endpoint ? { endpoint: e.endpoint } : {},
356
+ ...e.forcePathStyle ? { forcePathStyle: !0 } : {},
357
+ ...e.accessKeyId && e.secretAccessKey ? { credentials: {
358
+ accessKeyId: e.accessKeyId,
359
+ secretAccessKey: e.secretAccessKey
360
+ } } : {}
361
+ }),
362
+ cmds: t,
363
+ presign: n.getSignedUrl
364
+ };
365
+ })(), r), a = (e) => b.tryPromise({
366
+ try: e,
367
+ catch: (e) => {
368
+ let n = e?.$metadata?.httpStatusCode;
369
+ return G(t, String(e?.message ?? e), K(n), n);
370
+ }
371
+ }), o = async (e) => {
372
+ let t = e;
373
+ if (typeof t.transformToByteArray == "function") return t.transformToByteArray();
374
+ let n = [];
375
+ for await (let e of t) n.push(e);
376
+ let r = n.reduce((e, t) => e + t.byteLength, 0), i = new Uint8Array(r), a = 0;
377
+ for (let e of n) i.set(e, a), a += e.byteLength;
378
+ return i;
379
+ };
380
+ return {
381
+ name: t,
382
+ presigns: !0,
383
+ put: (t, r, o) => a(async () => {
384
+ let { client: a, cmds: s } = await i();
385
+ return await a.send(new s.PutObjectCommand({
386
+ Bucket: e.bucket,
387
+ Key: t,
388
+ Body: r,
389
+ ContentType: o.contentType,
390
+ ...o.public && n ? { ACL: "public-read" } : {}
391
+ })), { size: r.byteLength };
392
+ }),
393
+ get: (t) => a(async () => {
394
+ let { client: n, cmds: r } = await i(), a = await n.send(new r.GetObjectCommand({
395
+ Bucket: e.bucket,
396
+ Key: t
397
+ }));
398
+ return {
399
+ bytes: await o(a.Body),
400
+ contentType: a.ContentType ?? "application/octet-stream"
401
+ };
402
+ }),
403
+ delete: (t) => a(async () => {
404
+ let { client: n, cmds: r } = await i();
405
+ await n.send(new r.DeleteObjectCommand({
406
+ Bucket: e.bucket,
407
+ Key: t
408
+ }));
409
+ }),
410
+ publicUrl: (t) => e.cdnBaseUrl ? `${e.cdnBaseUrl.replace(/\/$/, "")}/${t}` : null,
411
+ getUrl: (t, n) => a(async () => {
412
+ let { client: r, cmds: a, presign: o } = await i();
413
+ return o(r, new a.GetObjectCommand({
414
+ Bucket: e.bucket,
415
+ Key: t
416
+ }), { expiresIn: n });
417
+ }),
418
+ getUploadUrl: (t, n, r) => a(async () => {
419
+ let { client: a, cmds: o, presign: s } = await i();
420
+ return s(a, new o.PutObjectCommand({
421
+ Bucket: e.bucket,
422
+ Key: t,
423
+ ContentType: r
424
+ }), { expiresIn: n });
425
+ }),
426
+ createMultipartUpload: (t, r, o) => a(async () => {
427
+ let { client: a, cmds: s } = await i(), c = await a.send(new s.CreateMultipartUploadCommand({
428
+ Bucket: e.bucket,
429
+ Key: t,
430
+ ContentType: r,
431
+ ...o?.public && n ? { ACL: "public-read" } : {}
432
+ }));
433
+ if (!c.UploadId) throw Error("s3 createMultipartUpload returned no UploadId");
434
+ return { uploadId: c.UploadId };
435
+ }),
436
+ presignUploadPart: (t, n, r, o) => a(async () => {
437
+ let { client: a, cmds: s, presign: c } = await i();
438
+ return c(a, new s.UploadPartCommand({
439
+ Bucket: e.bucket,
440
+ Key: t,
441
+ UploadId: n,
442
+ PartNumber: r
443
+ }), { expiresIn: o });
444
+ }),
445
+ completeMultipartUpload: (t, n, r) => a(async () => {
446
+ let { client: a, cmds: o } = await i(), s = await a.send(new o.CompleteMultipartUploadCommand({
447
+ Bucket: e.bucket,
448
+ Key: t,
449
+ UploadId: n,
450
+ MultipartUpload: { Parts: r.map((e) => ({
451
+ PartNumber: e.partNumber,
452
+ ETag: e.etag
453
+ })) }
454
+ }));
455
+ return s.ETag === void 0 ? {} : { etag: s.ETag };
456
+ }),
457
+ abortMultipartUpload: (t, n) => a(async () => {
458
+ let { client: r, cmds: a } = await i();
459
+ await r.send(new a.AbortMultipartUploadCommand({
460
+ Bucket: e.bucket,
461
+ Key: t,
462
+ UploadId: n
463
+ }));
464
+ }),
465
+ head: (n) => b.tryPromise({
466
+ try: async () => {
467
+ let { client: t, cmds: r } = await i();
468
+ try {
469
+ let i = await t.send(new r.HeadObjectCommand({
470
+ Bucket: e.bucket,
471
+ Key: n
472
+ }));
473
+ return {
474
+ size: i.ContentLength ?? 0,
475
+ ...i.ETag === void 0 ? {} : { etag: i.ETag }
476
+ };
477
+ } catch (e) {
478
+ if (e?.$metadata?.httpStatusCode === 404) return null;
479
+ throw e;
480
+ }
481
+ },
482
+ catch: (e) => {
483
+ let n = e?.$metadata?.httpStatusCode;
484
+ return G(t, String(e?.message ?? e), K(n), n);
485
+ }
486
+ }),
487
+ getStream: (n) => a(async () => {
488
+ let { client: r, cmds: a } = await i(), o = await r.send(new a.GetObjectCommand({
489
+ Bucket: e.bucket,
490
+ Key: n
491
+ })), s = o.Body;
492
+ return {
493
+ stream: xe(t, () => s),
494
+ contentType: o.ContentType ?? "application/octet-stream",
495
+ ...o.ContentLength === void 0 ? {} : { size: o.ContentLength }
496
+ };
497
+ }),
498
+ putStream: (t, r, o) => a(async () => {
499
+ let { client: a, cmds: s } = await i(), c;
500
+ try {
501
+ c = await import("@aws-sdk/lib-storage");
502
+ } catch (e) {
503
+ throw Error("s3/minio `putStream` requires the '@aws-sdk/lib-storage' optional dependency (multipart upload). Install it to enable streamed uploads.", { cause: e });
504
+ }
505
+ let l = await Se(r);
506
+ return await new c.Upload({
507
+ client: a,
508
+ params: {
509
+ Bucket: e.bucket,
510
+ Key: t,
511
+ Body: l,
512
+ ContentType: o.contentType,
513
+ ...o.public && n ? { ACL: "public-read" } : {}
514
+ }
515
+ }).done(), { size: o.totalSize ?? (await a.send(new s.HeadObjectCommand({
516
+ Bucket: e.bucket,
517
+ Key: t
518
+ }))).ContentLength ?? 0 };
519
+ }),
520
+ getRange: (t, n, r) => a(async () => {
521
+ let { client: a, cmds: s } = await i(), c = await a.send(new s.GetObjectCommand({
522
+ Bucket: e.bucket,
523
+ Key: t,
524
+ Range: `bytes=${n}-${r}`
525
+ }));
526
+ return o(c.Body);
527
+ })
528
+ };
529
+ }, Ie = (e) => e instanceof Uint8Array ? e : typeof e == "string" ? new Uint8Array(Buffer.from(e, "base64")) : new Uint8Array(e), Le = () => {
530
+ let e = null, t = () => {
531
+ if (!e) throw Error("the 'database' storage provider isn't bound to a DataStore yet");
532
+ return e;
533
+ }, n = (e) => b.tryPromise({
534
+ try: e,
535
+ catch: (e) => G("database", String(e?.message ?? e), !0)
536
+ }), r = async (e) => (await t().query({
537
+ table: U,
538
+ predicate: {
539
+ column: "key",
540
+ op: "eq",
541
+ value: e
542
+ },
543
+ order: [],
544
+ take: 1,
545
+ skip: void 0,
546
+ projection: void 0
547
+ }))[0];
548
+ return {
549
+ name: "database",
550
+ presigns: !1,
551
+ bindDataStore: (t) => {
552
+ e = t;
553
+ },
554
+ put: (e, i, a) => n(async () => {
555
+ let n = await r(e);
556
+ return n ? await t().update(U, String(n.id), {
557
+ data: i,
558
+ contentType: a.contentType,
559
+ size: i.byteLength
560
+ }) : await t().insert(U, {
561
+ id: O({
562
+ kind: "typeid",
563
+ prefix: "blob"
564
+ }, U),
565
+ key: e,
566
+ data: i,
567
+ contentType: a.contentType,
568
+ size: i.byteLength
569
+ }), { size: i.byteLength };
570
+ }),
571
+ get: (e) => b.suspend(() => n(() => r(e)).pipe(b.flatMap((t) => t ? b.succeed({
572
+ bytes: Ie(t.data),
573
+ contentType: String(t.contentType)
574
+ }) : b.fail(G("database", `no object at key '${e}'`, !1, 404))))),
575
+ delete: (e) => n(async () => {
576
+ let n = await r(e);
577
+ n && await t().delete(U, String(n.id));
578
+ }),
579
+ publicUrl: () => null,
580
+ getUrl: () => b.succeed(""),
581
+ head: (e) => n(async () => {
582
+ let t = await r(e);
583
+ return t ? { size: Number(t.size) } : null;
584
+ })
585
+ };
586
+ }, Re = (e) => {
587
+ let t = null, n = () => t || (t = (async () => {
588
+ let t;
589
+ try {
590
+ t = await import("@azure/storage-blob");
591
+ } catch (e) {
592
+ throw Error("the 'azure' storage provider requires the '@azure/storage-blob' optional dependency. Install it to enable it.", { cause: e });
593
+ }
594
+ let n = W.read("AZURE_STORAGE_ACCOUNT", e.accountName), r = W.read("AZURE_STORAGE_KEY", e.accountKey), i = n && r ? new t.StorageSharedKeyCredential(n, r) : null, a = W.read("AZURE_STORAGE_CONNECTION_STRING", e.connectionString);
595
+ return {
596
+ container: (a ? t.BlobServiceClient.fromConnectionString(a) : new t.BlobServiceClient(e.endpoint ?? `https://${n}.blob.core.windows.net`, i ?? void 0)).getContainerClient(e.container),
597
+ sdk: t,
598
+ cred: i
599
+ };
600
+ })(), t), r = (e) => b.tryPromise({
601
+ try: e,
602
+ catch: (e) => {
603
+ let t = e?.statusCode;
604
+ return G("azure", String(e?.message ?? e), K(t), t);
605
+ }
606
+ }), i = async (e) => {
607
+ if (!e) return /* @__PURE__ */ new Uint8Array();
608
+ let t = [];
609
+ for await (let n of e) t.push(typeof n == "string" ? new TextEncoder().encode(n) : new Uint8Array(n));
610
+ let n = t.reduce((e, t) => e + t.byteLength, 0), r = new Uint8Array(n), i = 0;
611
+ for (let e of t) r.set(e, i), i += e.byteLength;
612
+ return r;
613
+ }, a = async (t, r, i) => {
614
+ let { container: a, sdk: o, cred: s } = await n();
615
+ if (!s) throw Error("azure SAS requires accountName + accountKey (set them or AZURE_STORAGE_ACCOUNT/AZURE_STORAGE_KEY)");
616
+ let c = a.getBlockBlobClient(t), l = o.generateBlobSASQueryParameters({
617
+ containerName: e.container,
618
+ blobName: t,
619
+ permissions: o.BlobSASPermissions.parse(r),
620
+ startsOn: /* @__PURE__ */ new Date(Date.now() - 6e4),
621
+ expiresOn: new Date(Date.now() + i * 1e3)
622
+ }, s).toString();
623
+ return `${c.url}?${l}`;
624
+ };
625
+ return {
626
+ name: "azure",
627
+ presigns: !0,
628
+ put: (e, t, i) => r(async () => {
629
+ let { container: r } = await n();
630
+ return await r.getBlockBlobClient(e).uploadData(Buffer.from(t), { blobHTTPHeaders: { blobContentType: i.contentType } }), { size: t.byteLength };
631
+ }),
632
+ get: (e) => r(async () => {
633
+ let { container: t } = await n(), r = await t.getBlockBlobClient(e).download();
634
+ return {
635
+ bytes: await i(r.readableStreamBody),
636
+ contentType: r.contentType ?? "application/octet-stream"
637
+ };
638
+ }),
639
+ delete: (e) => r(async () => {
640
+ let { container: t } = await n();
641
+ await t.getBlockBlobClient(e).deleteIfExists();
642
+ }),
643
+ publicUrl: (t) => e.cdnBaseUrl ? `${e.cdnBaseUrl.replace(/\/$/, "")}/${t}` : null,
644
+ getUrl: (e, t) => r(() => a(e, "r", t)),
645
+ getUploadUrl: (e, t, n) => r(() => a(e, "cw", t)),
646
+ head: (e) => b.tryPromise({
647
+ try: async () => {
648
+ let { container: t } = await n();
649
+ try {
650
+ let n = await t.getBlockBlobClient(e).getProperties();
651
+ return {
652
+ size: n.contentLength ?? 0,
653
+ ...n.etag === void 0 ? {} : { etag: n.etag }
654
+ };
655
+ } catch (e) {
656
+ if (e?.statusCode === 404) return null;
657
+ throw e;
658
+ }
659
+ },
660
+ catch: (e) => {
661
+ let t = e?.statusCode;
662
+ return G("azure", String(e?.message ?? e), K(t), t);
663
+ }
664
+ }),
665
+ getStream: (e) => r(async () => {
666
+ let { container: t } = await n(), r = await t.getBlockBlobClient(e).download();
667
+ return {
668
+ stream: xe("azure", () => r.readableStreamBody),
669
+ contentType: r.contentType ?? "application/octet-stream",
670
+ ...r.contentLength === void 0 ? {} : { size: r.contentLength }
671
+ };
672
+ }),
673
+ putStream: (e, t, i) => r(async () => {
674
+ let { container: r } = await n(), a = await Se(t);
675
+ return await r.getBlockBlobClient(e).uploadStream(a, 4 * 1024 * 1024, 5, { blobHTTPHeaders: { blobContentType: i.contentType } }), { size: i.totalSize ?? (await r.getBlockBlobClient(e).getProperties()).contentLength ?? 0 };
676
+ }),
677
+ getRange: (e, t, a) => r(async () => {
678
+ let { container: r } = await n(), o = await r.getBlockBlobClient(e).download(t, a - t + 1);
679
+ return i(o.readableStreamBody);
680
+ })
681
+ };
682
+ }, ze = {
683
+ webp: "image/webp",
684
+ avif: "image/avif",
685
+ jpeg: "image/jpeg",
686
+ jpg: "image/jpeg",
687
+ png: "image/png"
688
+ }, Be = (e) => {
689
+ if (!e) return null;
690
+ let t = new URLSearchParams(e), n = (e) => {
691
+ let n = t.get(e);
692
+ if (n == null) return;
693
+ let r = Number(n);
694
+ return Number.isFinite(r) && r > 0 ? Math.floor(r) : void 0;
695
+ }, r = n("w"), i = n("h"), a = n("q"), o = (t.get("format") ?? t.get("fm") ?? void 0)?.toLowerCase(), s = t.get("fit") ?? void 0;
696
+ return r === void 0 && i === void 0 && !o && a === void 0 ? null : {
697
+ ...r === void 0 ? {} : { w: r },
698
+ ...i === void 0 ? {} : { h: i },
699
+ ...o ? { format: o } : {},
700
+ ...a === void 0 ? {} : { q: a },
701
+ ...s ? { fit: s } : {}
702
+ };
703
+ }, Ve = (e) => e.startsWith("image/") && !e.includes("svg"), q, He = async () => {
704
+ if (q !== void 0) return q;
705
+ try {
706
+ let e = await import("sharp");
707
+ q = e.default ?? e;
708
+ } catch {
709
+ q = null;
710
+ }
711
+ return q;
712
+ }, Ue = async (e, t, n) => {
713
+ if (!Ve(t)) return null;
714
+ let r = await He();
715
+ if (!r) return null;
716
+ try {
717
+ let i = r(Buffer.from(e), { failOn: "none" });
718
+ (n.w !== void 0 || n.h !== void 0) && (i = i.resize({
719
+ ...n.w === void 0 ? {} : { width: n.w },
720
+ ...n.h === void 0 ? {} : { height: n.h },
721
+ fit: n.fit ?? "cover",
722
+ withoutEnlargement: !0
723
+ }));
724
+ let a = n.format && ze[n.format] ? n.format : void 0, o = t;
725
+ if (a) {
726
+ let e = a === "jpg" ? "jpeg" : a;
727
+ i = i.toFormat(e, n.q === void 0 ? {} : { quality: n.q }), o = ze[a];
728
+ } else n.q !== void 0 && (t === "image/jpeg" || t === "image/webp") && (i = i.toFormat(t === "image/webp" ? "webp" : "jpeg", { quality: n.q }));
729
+ let s = await i.toBuffer();
730
+ return {
731
+ bytes: new Uint8Array(s),
732
+ contentType: o
733
+ };
734
+ } catch {
735
+ return null;
736
+ }
737
+ }, We = 200, J = /* @__PURE__ */ new Map(), Ge = async (e, t, n, r) => {
738
+ let i = `${e}::${JSON.stringify(r)}`, a = J.get(i);
739
+ if (a) return a;
740
+ let o = await Ue(t, n, r);
741
+ if (o && (J.set(i, o), J.size > We)) {
742
+ let e = J.keys().next().value;
743
+ e !== void 0 && J.delete(e);
744
+ }
745
+ return o;
746
+ }, Ke = (e) => {
747
+ let t = I(16).toString("hex");
748
+ return `${t}:${de(e, t, 32).toString("hex")}`;
749
+ }, qe = (e, t) => {
750
+ if (!e || !t) return !1;
751
+ let [n, r] = t.split(":");
752
+ if (!n || !r) return !1;
753
+ let i = Buffer.from(r, "hex"), a = de(e, n, i.length);
754
+ return a.length === i.length && L(a, i);
755
+ }, Je = (e) => Array.isArray(e) ? e.filter((e) => typeof e == "string") : [], Ye = (e) => Je(e.metadata?.roles), Xe = (e) => Je(e.scopes), Ze = (e, t) => e.some((e) => t.includes(e)), Qe = (e) => Je(e.metadata?.groups), $e = (e) => typeof e == "boolean" ? b.succeed(e) : b.isEffect(e) ? e : b.promise(() => e), et = (e, t, n = {}) => b.gen(function* () {
756
+ if (t.id && e.ownerId && t.id === e.ownerId) return !0;
757
+ let r = e.accessPolicy ?? [];
758
+ if (r.length === 0) return !1;
759
+ let i = n.resolveGroups ? yield* b.promise(async () => await n.resolveGroups(t)) : Qe(t);
760
+ for (let a of r) if (yield* tt(a, e, t, i, n)) return !0;
761
+ return !1;
762
+ }), tt = (e, t, n, r, i) => b.gen(function* () {
763
+ let a = !1, o = (e) => (a = !0, e);
764
+ if (e.owner !== void 0 && e.owner && !o(!!n.id && !!t.ownerId && n.id === t.ownerId) || e.tenant !== void 0 && e.tenant && !o(!!n.tenantId && n.tenantId === t.tenantId) || e.apiKey !== void 0 && e.apiKey && !o(n.type === "apiKey") || e.roles && e.roles.length > 0 && !o(Ze(Ye(n), e.roles)) || e.groups && e.groups.length > 0 && !o(Ze(r, e.groups)) || e.scopes && e.scopes.length > 0 && !o(Ze(Xe(n), e.scopes)) || e.password !== void 0 && e.password && !o(qe(i.password, t.passwordHash))) return !1;
765
+ if (e.guard) {
766
+ let r = i.guards?.[e.guard];
767
+ if (!o(r ? yield* $e(r({
768
+ subject: n,
769
+ ref: t
770
+ })) : !1)) return !1;
771
+ }
772
+ return a;
773
+ }), nt = (e, t) => {
774
+ let n = [];
775
+ e.id && n.push({
776
+ type: e.type === "apiKey" ? "apiKey" : "user",
777
+ id: e.id
778
+ });
779
+ for (let e of t) n.push({
780
+ type: "group",
781
+ id: e
782
+ });
783
+ return n;
784
+ }, rt = (e, t, n, r, i) => {
785
+ let a = nt(t, n);
786
+ return e.some((e) => e.expiresAt && e.expiresAt.getTime() < i || r === "write" && e.permission !== "write" ? !1 : a.some((t) => t.type === e.principalType && t.id === e.principalId));
787
+ }, it = (e, t) => t.resolveGroups ? b.promise(async () => await t.resolveGroups(e)) : b.succeed(Qe(e)), Y = () => W.read("VOLTRO_STORAGE_SECRET") ?? ie(), X = (e) => (typeof e == "string" ? Buffer.from(e, "utf8") : e).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), Z = (e) => {
788
+ let t = e.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - e.length % 4) % 4);
789
+ return Buffer.from(t, "base64");
790
+ }, at = (e, t, n = Y()) => {
791
+ if (!n) throw Error("signGrant: secret must be a non-empty string");
792
+ let r = {
793
+ refId: e,
794
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
795
+ }, i = X(JSON.stringify(r));
796
+ return `${i}.${X(F("sha256", n).update(i).digest())}`;
797
+ }, ot = (e, t = Y()) => {
798
+ if (!e || !t) return null;
799
+ let n = e.indexOf(".");
800
+ if (n <= 0 || n === e.length - 1) return null;
801
+ let r = e.slice(0, n), i = e.slice(n + 1), a = F("sha256", t).update(r).digest(), o = Z(i);
802
+ if (o.length !== a.length || !L(o, a)) return null;
803
+ try {
804
+ let e = JSON.parse(Z(r).toString("utf8"));
805
+ return typeof e.refId != "string" || typeof e.exp != "number" || e.exp < Math.floor(Date.now() / 1e3) ? null : e;
806
+ } catch {
807
+ return null;
808
+ }
809
+ }, st = (e, t, n = Y()) => {
810
+ if (!n) throw Error("signUploadTicket: secret must be a non-empty string");
811
+ let r = {
812
+ ...e,
813
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
814
+ }, i = X(JSON.stringify(r));
815
+ return `${i}.${X(F("sha256", n).update(`u:${i}`).digest())}`;
816
+ }, ct = (e, t = Y()) => {
817
+ if (!e || !t) return null;
818
+ let n = e.indexOf(".");
819
+ if (n <= 0 || n === e.length - 1) return null;
820
+ let r = e.slice(0, n), i = e.slice(n + 1), a = F("sha256", t).update(`u:${r}`).digest(), o = Z(i);
821
+ if (o.length !== a.length || !L(o, a)) return null;
822
+ try {
823
+ let e = JSON.parse(Z(r).toString("utf8"));
824
+ return typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp < Math.floor(Date.now() / 1e3) ? null : e;
825
+ } catch {
826
+ return null;
827
+ }
828
+ }, lt = (e, t, n = Y()) => {
829
+ if (!n) throw Error("signFinalizeTicket: secret must be a non-empty string");
830
+ let r = {
831
+ ...e,
832
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
833
+ }, i = X(JSON.stringify(r));
834
+ return `${i}.${X(F("sha256", n).update(`f:${i}`).digest())}`;
835
+ }, ut = (e, t = Y()) => {
836
+ if (!e || !t) return null;
837
+ let n = e.indexOf(".");
838
+ if (n <= 0 || n === e.length - 1) return null;
839
+ let r = e.slice(0, n), i = e.slice(n + 1), a = F("sha256", t).update(`f:${r}`).digest(), o = Z(i);
840
+ if (o.length !== a.length || !L(o, a)) return null;
841
+ try {
842
+ let e = JSON.parse(Z(r).toString("utf8"));
843
+ return typeof e.key != "string" || e.key.length === 0 || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp < Math.floor(Date.now() / 1e3) ? null : e;
844
+ } catch {
845
+ return null;
846
+ }
847
+ }, dt = (e, t, n = Y()) => {
848
+ if (!n) throw Error("signResumableTicket: secret must be a non-empty string");
849
+ let r = {
850
+ ...e,
851
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
852
+ }, i = X(JSON.stringify(r));
853
+ return `${i}.${X(F("sha256", n).update(`r:${i}`).digest())}`;
854
+ }, ft = (e, t = Y()) => {
855
+ if (!e || !t) return null;
856
+ let n = e.indexOf(".");
857
+ if (n <= 0 || n === e.length - 1) return null;
858
+ let r = e.slice(0, n), i = e.slice(n + 1), a = F("sha256", t).update(`r:${r}`).digest(), o = Z(i);
859
+ if (o.length !== a.length || !L(o, a)) return null;
860
+ try {
861
+ let e = JSON.parse(Z(r).toString("utf8"));
862
+ return typeof e.id != "string" || e.id.length === 0 || typeof e.size != "number" || e.size < 0 || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp < Math.floor(Date.now() / 1e3) ? null : e;
863
+ } catch {
864
+ return null;
865
+ }
866
+ }, pt = (e, t, n = Y()) => {
867
+ if (!n) throw Error("signMultipartTicket: secret must be a non-empty string");
868
+ let r = {
869
+ ...e,
870
+ exp: Math.floor(Date.now() / 1e3) + Math.floor(t)
871
+ }, i = X(JSON.stringify(r));
872
+ return `${i}.${X(F("sha256", n).update(`m:${i}`).digest())}`;
873
+ }, mt = (e, t = Y()) => {
874
+ if (!e || !t) return null;
875
+ let n = e.indexOf(".");
876
+ if (n <= 0 || n === e.length - 1) return null;
877
+ let r = e.slice(0, n), i = e.slice(n + 1), a = F("sha256", t).update(`m:${r}`).digest(), o = Z(i);
878
+ if (o.length !== a.length || !L(o, a)) return null;
879
+ try {
880
+ let e = JSON.parse(Z(r).toString("utf8"));
881
+ return typeof e.key != "string" || e.key.length === 0 || typeof e.uploadId != "string" || e.uploadId.length === 0 || typeof e.ct != "string" || typeof e.exp != "number" || e.vis !== "public" && e.vis !== "private" || e.exp < Math.floor(Date.now() / 1e3) ? null : e;
882
+ } catch {
883
+ return null;
884
+ }
885
+ }, ht = (e, t) => {
886
+ if (!t || t.length === 0) return !0;
887
+ let n = e.toLowerCase();
888
+ return t.some((e) => {
889
+ let t = e.toLowerCase();
890
+ return t.endsWith("/*") ? n.startsWith(t.slice(0, -1)) : n === t;
891
+ });
892
+ }, gt = [
893
+ {
894
+ ct: /^image\/png$/,
895
+ offset: 0,
896
+ magic: [
897
+ 137,
898
+ 80,
899
+ 78,
900
+ 71
901
+ ]
902
+ },
903
+ {
904
+ ct: /^image\/jpe?g$/,
905
+ offset: 0,
906
+ magic: [
907
+ 255,
908
+ 216,
909
+ 255
910
+ ]
911
+ },
912
+ {
913
+ ct: /^image\/gif$/,
914
+ offset: 0,
915
+ magic: [
916
+ 71,
917
+ 73,
918
+ 70,
919
+ 56
920
+ ]
921
+ },
922
+ {
923
+ ct: /^image\/webp$/,
924
+ offset: 0,
925
+ magic: [
926
+ 82,
927
+ 73,
928
+ 70,
929
+ 70
930
+ ]
931
+ },
932
+ {
933
+ ct: /^image\/bmp$/,
934
+ offset: 0,
935
+ magic: [66, 77]
936
+ },
937
+ {
938
+ ct: /^application\/pdf$/,
939
+ offset: 0,
940
+ magic: [
941
+ 37,
942
+ 80,
943
+ 68,
944
+ 70
945
+ ]
946
+ },
947
+ {
948
+ ct: /^application\/zip$/,
949
+ offset: 0,
950
+ magic: [
951
+ 80,
952
+ 75,
953
+ 3,
954
+ 4
955
+ ]
956
+ },
957
+ {
958
+ ct: /^application\/gzip$/,
959
+ offset: 0,
960
+ magic: [31, 139]
961
+ }
962
+ ], _t = (e, t) => {
963
+ let n = t.toLowerCase(), r = gt.find((e) => e.ct.test(n));
964
+ return r ? e.length < r.offset + r.magic.length ? !1 : r.magic.every((t, n) => e[r.offset + n] === t) : !0;
965
+ }, vt = (e, t, n) => n ? n.maxBytes !== void 0 && e.byteLength > n.maxBytes ? {
966
+ reason: "too-large",
967
+ detail: `${e.byteLength} bytes exceeds the ${n.maxBytes}-byte limit`
968
+ } : ht(t, n.allowedContentTypes) ? n.sniff && !_t(e, t) ? {
969
+ reason: "content-mismatch",
970
+ detail: `bytes do not match the declared content type '${t}'`
971
+ } : null : {
972
+ reason: "content-type-not-allowed",
973
+ detail: `content type '${t}' is not in the allow-list`
974
+ } : null, yt = async (e, t, n) => {
975
+ if (!Ve(t)) return { bytes: e };
976
+ let r = await He();
977
+ if (!r) return { bytes: e };
978
+ try {
979
+ let t = e;
980
+ if (n?.normalize) {
981
+ let n = await r(Buffer.from(e), { failOn: "none" }).rotate().toBuffer();
982
+ t = new Uint8Array(n);
983
+ }
984
+ let i = await r(Buffer.from(t), { failOn: "none" }).metadata(), a = await r(Buffer.from(t), { failOn: "none" }).resize({
985
+ width: 24,
986
+ height: 24,
987
+ fit: "inside",
988
+ withoutEnlargement: !0
989
+ }).webp({ quality: 40 }).toBuffer().catch(() => null);
990
+ return {
991
+ bytes: t,
992
+ ...typeof i.width == "number" ? { width: i.width } : {},
993
+ ...typeof i.height == "number" ? { height: i.height } : {},
994
+ ...a ? { placeholder: `data:image/webp;base64,${a.toString("base64")}` } : {}
995
+ };
996
+ } catch {
997
+ return { bytes: e };
998
+ }
999
+ }, bt = async (e) => {
1000
+ let { createHash: t } = await import("node:crypto");
1001
+ return t("sha256").update(e).digest("hex");
1002
+ }, xt = (e, t) => e ? `${e.replace(/\/+$/, "")}/_voltro/storage/${t}` : `/_voltro/storage/${t}`, St = (e, t) => {
1003
+ if (t.tenantId !== void 0 && (e.tenantId ?? null) !== (t.tenantId ?? null) || t.ownerId !== void 0 && e.ownerId !== t.ownerId || !t.includeDerived && e.derivedFrom != null) return !1;
1004
+ if (t.folder !== void 0) {
1005
+ let n = t.folder.replace(/\/+$/, ""), r = (e.folder ?? "").replace(/\/+$/, "");
1006
+ if (r !== n && !r.startsWith(`${n}/`)) return !1;
1007
+ }
1008
+ if (t.tags.length > 0) {
1009
+ let n = e.tags ?? [];
1010
+ if (!t.tags.every((e) => n.includes(e))) return !1;
1011
+ }
1012
+ return !0;
1013
+ }, Ct = (e) => e ?? "global", wt = () => {
1014
+ let e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map();
1015
+ return {
1016
+ insert: (t) => b.sync(() => {
1017
+ e.set(t.id, t);
1018
+ }),
1019
+ getById: (t) => b.sync(() => e.get(t) ?? null),
1020
+ delete: (t) => b.sync(() => e.delete(t)),
1021
+ listRecent: (t) => b.sync(() => [...e.values()].sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime()).slice(0, t)),
1022
+ countByKey: (t, n) => b.sync(() => [...e.values()].filter((e) => e.bucket === t && e.key === n).length),
1023
+ usageByTenant: (t) => b.sync(() => {
1024
+ let n = 0, r = 0;
1025
+ for (let i of e.values()) (i.tenantId ?? null) === (t ?? null) && (n += i.size, r++);
1026
+ return {
1027
+ bytes: n,
1028
+ count: r
1029
+ };
1030
+ }),
1031
+ consumeUsage: (e, n, r) => b.sync(() => {
1032
+ let i = Ct(e), a = t.get(i) ?? {
1033
+ bytes: 0,
1034
+ count: 0
1035
+ }, o = (r?.maxBytes === void 0 || a.bytes + n.bytes <= r.maxBytes) && (r?.maxCount === void 0 || a.count + n.count <= r.maxCount);
1036
+ return o && t.set(i, {
1037
+ bytes: a.bytes + n.bytes,
1038
+ count: a.count + n.count
1039
+ }), {
1040
+ allowed: o,
1041
+ bytes: a.bytes,
1042
+ count: a.count
1043
+ };
1044
+ }),
1045
+ releaseUsage: (e, n) => b.sync(() => {
1046
+ let r = Ct(e), i = t.get(r);
1047
+ i && t.set(r, {
1048
+ bytes: Math.max(0, i.bytes - n.bytes),
1049
+ count: Math.max(0, i.count - n.count)
1050
+ });
1051
+ }),
1052
+ listByDerivedFrom: (t) => b.sync(() => [...e.values()].filter((e) => (e.derivedFrom ?? null) === t)),
1053
+ listRefs: (t) => b.sync(() => [...e.values()].filter((e) => St(e, t)).sort((e, t) => t.createdAt.getTime() - e.createdAt.getTime()).slice(t.offset, t.offset + t.limit))
1054
+ };
1055
+ }, Q = (e) => ({
1056
+ id: String(e.id),
1057
+ tenantId: e.tenantId ?? null,
1058
+ ownerId: e.ownerId ?? null,
1059
+ bucket: String(e.bucket),
1060
+ key: String(e.key),
1061
+ contentType: String(e.contentType),
1062
+ size: Number(e.size),
1063
+ checksum: String(e.checksum),
1064
+ visibility: e.visibility === "public" ? "public" : "private",
1065
+ accessPolicy: e.accessPolicy ?? null,
1066
+ passwordHash: e.passwordHash ?? null,
1067
+ createdAt: e.createdAt instanceof Date ? e.createdAt : new Date(String(e.createdAt)),
1068
+ width: e.width ?? null,
1069
+ height: e.height ?? null,
1070
+ duration: e.duration ?? null,
1071
+ placeholder: e.placeholder ?? null,
1072
+ folder: e.folder ?? null,
1073
+ tags: e.tags ?? null,
1074
+ alt: e.alt ?? null,
1075
+ caption: e.caption ?? null,
1076
+ derivedFrom: e.derivedFrom ?? null,
1077
+ kind: e.kind ?? null
1078
+ }), $ = (e) => b.tryPromise({
1079
+ try: e,
1080
+ catch: (e) => new t({
1081
+ provider: "refstore",
1082
+ message: String(e?.message ?? e),
1083
+ transient: !0
1084
+ })
1085
+ }), Tt = async (e, t) => (await e.query({
1086
+ table: "_voltro_storage_usage",
1087
+ predicate: {
1088
+ column: "tenantId",
1089
+ op: "eq",
1090
+ value: t
1091
+ },
1092
+ order: [],
1093
+ take: 1,
1094
+ skip: void 0,
1095
+ projection: void 0
1096
+ }))[0] ?? null, Et = (e) => ({
1097
+ insert: (t) => $(() => e.insert(V, { ...t })),
1098
+ getById: (t) => $(async () => {
1099
+ let n = await e.query({
1100
+ table: V,
1101
+ predicate: {
1102
+ column: "id",
1103
+ op: "eq",
1104
+ value: t
1105
+ },
1106
+ order: [],
1107
+ take: 1,
1108
+ skip: void 0,
1109
+ projection: void 0
1110
+ });
1111
+ return n[0] ? Q(n[0]) : null;
1112
+ }),
1113
+ delete: (t) => $(() => e.delete(V, t)),
1114
+ listRecent: (t) => $(async () => (await e.query({
1115
+ table: V,
1116
+ predicate: void 0,
1117
+ order: [{
1118
+ column: "createdAt",
1119
+ direction: "desc"
1120
+ }],
1121
+ take: t,
1122
+ skip: void 0,
1123
+ projection: void 0
1124
+ })).map(Q)),
1125
+ countByKey: (t, n) => $(async () => (await e.query({
1126
+ table: V,
1127
+ predicate: { and: [{
1128
+ column: "bucket",
1129
+ op: "eq",
1130
+ value: t
1131
+ }, {
1132
+ column: "key",
1133
+ op: "eq",
1134
+ value: n
1135
+ }] },
1136
+ order: [],
1137
+ take: void 0,
1138
+ skip: void 0,
1139
+ projection: void 0
1140
+ })).length),
1141
+ usageByTenant: (t) => $(async () => {
1142
+ let n = await e.query({
1143
+ table: V,
1144
+ predicate: {
1145
+ column: "tenantId",
1146
+ op: "eq",
1147
+ value: t
1148
+ },
1149
+ order: [],
1150
+ take: void 0,
1151
+ skip: void 0,
1152
+ projection: void 0
1153
+ }), r = 0;
1154
+ for (let e of n) r += Number(e.size ?? 0);
1155
+ return {
1156
+ bytes: r,
1157
+ count: n.length
1158
+ };
1159
+ }),
1160
+ consumeUsage: (t, n, r) => $(async () => {
1161
+ let i = Ct(t);
1162
+ for (let t = 0; t < 32; t++) {
1163
+ t > 0 && await new Promise((e) => setTimeout(e, t));
1164
+ let a = await Tt(e, i), o = a ? Number(a.bytes ?? 0) : 0, s = a ? Number(a.count ?? 0) : 0;
1165
+ if (!((r?.maxBytes === void 0 || o + n.bytes <= r.maxBytes) && (r?.maxCount === void 0 || s + n.count <= r.maxCount))) return {
1166
+ allowed: !1,
1167
+ bytes: o,
1168
+ count: s
1169
+ };
1170
+ if (a) {
1171
+ if (await e.updateMany("_voltro_storage_usage", {
1172
+ bytes: o + n.bytes,
1173
+ count: s + n.count,
1174
+ updatedAt: /* @__PURE__ */ new Date()
1175
+ }, { where: E(D("id", String(a.id)), D("bytes", o), D("count", s)) }) === 1) return {
1176
+ allowed: !0,
1177
+ bytes: o,
1178
+ count: s
1179
+ };
1180
+ } else {
1181
+ let t = O({
1182
+ kind: "typeid",
1183
+ prefix: "susage"
1184
+ }, Te), r = await e.insertIgnore(Te, {
1185
+ id: t,
1186
+ tenantId: i,
1187
+ bytes: n.bytes,
1188
+ count: n.count,
1189
+ updatedAt: /* @__PURE__ */ new Date()
1190
+ }, { conflictColumns: ["tenantId"] });
1191
+ if (String(r.id) === t) return {
1192
+ allowed: !0,
1193
+ bytes: 0,
1194
+ count: 0
1195
+ };
1196
+ }
1197
+ }
1198
+ throw Error(`storage usage consume: contention on tenant '${i}' exceeded 32 attempts`);
1199
+ }),
1200
+ releaseUsage: (t, n) => $(async () => {
1201
+ let r = Ct(t);
1202
+ for (let t = 0; t < 32; t++) {
1203
+ t > 0 && await new Promise((e) => setTimeout(e, t));
1204
+ let i = await Tt(e, r);
1205
+ if (!i) return;
1206
+ let a = Number(i.bytes ?? 0), o = Number(i.count ?? 0);
1207
+ if (await e.updateMany("_voltro_storage_usage", {
1208
+ bytes: Math.max(0, a - n.bytes),
1209
+ count: Math.max(0, o - n.count),
1210
+ updatedAt: /* @__PURE__ */ new Date()
1211
+ }, { where: E(D("id", String(i.id)), D("bytes", a), D("count", o)) }) === 1) return;
1212
+ }
1213
+ throw Error(`storage usage release: contention on tenant '${r}' exceeded 32 attempts`);
1214
+ }),
1215
+ listByDerivedFrom: (t) => $(async () => (await e.query({
1216
+ table: V,
1217
+ predicate: {
1218
+ column: "derivedFrom",
1219
+ op: "eq",
1220
+ value: t
1221
+ },
1222
+ order: [{
1223
+ column: "createdAt",
1224
+ direction: "asc"
1225
+ }],
1226
+ take: void 0,
1227
+ skip: void 0,
1228
+ projection: void 0
1229
+ })).map(Q)),
1230
+ listRefs: (t) => $(async () => {
1231
+ let n = [];
1232
+ t.tenantId !== void 0 && n.push({
1233
+ column: "tenantId",
1234
+ op: "eq",
1235
+ value: t.tenantId
1236
+ }), t.ownerId !== void 0 && n.push({
1237
+ column: "ownerId",
1238
+ op: "eq",
1239
+ value: t.ownerId
1240
+ });
1241
+ let r = n.length === 0 ? void 0 : n.length === 1 ? n[0] : { and: n };
1242
+ return (await e.query({
1243
+ table: V,
1244
+ predicate: r,
1245
+ order: [{
1246
+ column: "createdAt",
1247
+ direction: "desc"
1248
+ }],
1249
+ take: void 0,
1250
+ skip: void 0,
1251
+ projection: void 0
1252
+ })).map(Q).filter((e) => St(e, t)).slice(t.offset, t.offset + t.limit);
1253
+ })
1254
+ }), Dt = () => {
1255
+ let e = /* @__PURE__ */ new Map();
1256
+ return {
1257
+ insert: (t) => b.sync(() => {
1258
+ e.set(t.id, t);
1259
+ }),
1260
+ delete: (t) => b.sync(() => {
1261
+ e.delete(t);
1262
+ }),
1263
+ listByRef: (t) => b.sync(() => [...e.values()].filter((e) => e.refId === t)),
1264
+ deleteByRef: (t) => b.sync(() => {
1265
+ for (let [n, r] of e) r.refId === t && e.delete(n);
1266
+ })
1267
+ };
1268
+ }, Ot = (e) => ({
1269
+ id: String(e.id),
1270
+ refId: String(e.refId),
1271
+ principalType: e.principalType,
1272
+ principalId: String(e.principalId),
1273
+ permission: e.permission === "write" ? "write" : "read",
1274
+ createdAt: e.createdAt instanceof Date ? e.createdAt : new Date(String(e.createdAt)),
1275
+ expiresAt: e.expiresAt ? e.expiresAt instanceof Date ? e.expiresAt : new Date(String(e.expiresAt)) : null,
1276
+ createdBy: e.createdBy ?? null
1277
+ }), kt = (e) => ({
1278
+ insert: (t) => $(() => e.insert(H, { ...t })),
1279
+ delete: (t) => $(() => e.delete(H, t)),
1280
+ listByRef: (t) => $(async () => (await e.query({
1281
+ table: H,
1282
+ predicate: {
1283
+ column: "refId",
1284
+ op: "eq",
1285
+ value: t
1286
+ },
1287
+ order: [{
1288
+ column: "createdAt",
1289
+ direction: "desc"
1290
+ }],
1291
+ take: void 0,
1292
+ skip: void 0,
1293
+ projection: void 0
1294
+ })).map(Ot)),
1295
+ deleteByRef: (t) => $(async () => {
1296
+ let n = await e.query({
1297
+ table: H,
1298
+ predicate: {
1299
+ column: "refId",
1300
+ op: "eq",
1301
+ value: t
1302
+ },
1303
+ order: [],
1304
+ take: void 0,
1305
+ skip: void 0,
1306
+ projection: void 0
1307
+ });
1308
+ for (let t of n) await e.delete(H, String(t.id));
1309
+ })
1310
+ }), At = () => O({
1311
+ kind: "typeid",
1312
+ prefix: "file"
1313
+ }, V), jt = () => O({
1314
+ kind: "typeid",
1315
+ prefix: "grant"
1316
+ }, H), Mt = (i) => {
1317
+ let { provider: a, refStore: o, grantStore: s, bucket: c } = i, l = i.access ?? {}, u = i.attempts ?? 3, d = i.urlExpiresInSec ?? 3600, f = (e) => xt(i.publicBaseUrl, e), p = C.recurs(Math.max(0, u - 1)).pipe(C.intersect(C.exponential("100 millis")), C.whileInput((e) => e.transient)), m = (e, t, n) => [
1318
+ i.tenantPrefix,
1319
+ e ?? "global",
1320
+ n ?? t
1321
+ ].filter(Boolean).join("/"), h = (e, t, n) => [
1322
+ i.tenantPrefix,
1323
+ e ?? "global",
1324
+ "_incoming",
1325
+ t,
1326
+ String(n).padStart(6, "0")
1327
+ ].filter(Boolean).join("/"), g = (e, n) => n !== void 0 && e.tenantId !== n ? b.fail(new t({
1328
+ provider: a.name,
1329
+ message: "cross-tenant access denied",
1330
+ transient: !1,
1331
+ status: 403
1332
+ })) : b.succeed(e), _ = (e, n) => o.getById(e).pipe(b.flatMap((r) => r ? g(r, n) : b.fail(new t({
1333
+ provider: a.name,
1334
+ message: `no ref '${e}'`,
1335
+ transient: !1,
1336
+ status: 404
1337
+ })))), ee = (e) => b.suspend(() => {
1338
+ let n = i.scan(e);
1339
+ return b.isEffect(n) ? n : b.tryPromise({
1340
+ try: () => Promise.resolve(n),
1341
+ catch: (e) => new t({
1342
+ provider: "scan",
1343
+ message: String(e?.message ?? e),
1344
+ transient: !0
1345
+ })
1346
+ });
1347
+ }), te = (e) => b.suspend(() => {
1348
+ let t = e.contentType;
1349
+ if (!i.videoProbe || !(t.startsWith("video/") || t.startsWith("audio/"))) return b.succeed(null);
1350
+ let n;
1351
+ try {
1352
+ n = i.videoProbe(e);
1353
+ } catch {
1354
+ return b.succeed(null);
1355
+ }
1356
+ return b.isEffect(n) ? n.pipe(b.catchAll(() => b.succeed(null))) : b.tryPromise({
1357
+ try: () => Promise.resolve(n),
1358
+ catch: () => null
1359
+ }).pipe(b.catchAll(() => b.succeed(null)));
1360
+ }), v = (e) => b.gen(function* () {
1361
+ let t = vt(e.bytes, e.contentType, i.limits);
1362
+ if (t) return yield* b.fail(new n(t));
1363
+ if (i.scan) {
1364
+ let t = yield* ee({
1365
+ bytes: e.bytes,
1366
+ contentType: e.contentType,
1367
+ ...e.key ? { key: e.key } : {}
1368
+ });
1369
+ if (!t.clean) return yield* b.fail(new r({ threat: t.threat ?? "unknown" }));
1370
+ }
1371
+ let s = yield* b.promise(() => yt(e.bytes, e.contentType, { normalize: i.normalizeImages ?? !1 })), l = s.bytes, u = yield* te({
1372
+ bytes: l,
1373
+ contentType: e.contentType
1374
+ }), d = s.width ?? u?.width, f = s.height ?? u?.height, h = s.placeholder ?? u?.poster, g = e.duration ?? u?.duration, _ = yield* b.promise(() => bt(l)), v = e.tenantId ?? null, ne = e.visibility ?? "private", S = m(v, _, e.key), C = {
1375
+ bytes: l.byteLength,
1376
+ count: 1
1377
+ }, w = yield* o.consumeUsage(v, C, i.quota);
1378
+ if (!w.allowed) {
1379
+ let e = i.quota ?? {}, t = e.maxCount !== void 0 && w.count + 1 > e.maxCount ? `tenant object count ${w.count} + 1 exceeds max ${e.maxCount}` : `tenant usage ${w.bytes} + ${l.byteLength} bytes exceeds max ${e.maxBytes}`;
1380
+ return yield* b.fail(new n({
1381
+ reason: "quota-exceeded",
1382
+ detail: t
1383
+ }));
1384
+ }
1385
+ let re = e.password ? yield* b.sync(() => Ke(e.password)) : null, T = {
1386
+ id: At(),
1387
+ tenantId: v,
1388
+ ownerId: e.ownerId ?? null,
1389
+ bucket: c,
1390
+ key: S,
1391
+ contentType: e.contentType,
1392
+ size: l.byteLength,
1393
+ checksum: _,
1394
+ visibility: ne,
1395
+ accessPolicy: e.access ?? null,
1396
+ passwordHash: re,
1397
+ createdAt: /* @__PURE__ */ new Date(),
1398
+ ...d === void 0 ? {} : { width: d },
1399
+ ...f === void 0 ? {} : { height: f },
1400
+ ...h === void 0 ? {} : { placeholder: h },
1401
+ ...g === void 0 ? {} : { duration: g },
1402
+ ...e.folder === void 0 ? {} : { folder: e.folder },
1403
+ ...e.tags === void 0 ? {} : { tags: e.tags },
1404
+ ...e.alt === void 0 ? {} : { alt: e.alt },
1405
+ ...e.caption === void 0 ? {} : { caption: e.caption },
1406
+ ...e.derivedFrom === void 0 ? {} : { derivedFrom: e.derivedFrom },
1407
+ ...e.kind === void 0 ? {} : { kind: e.kind }
1408
+ };
1409
+ return yield* ((e) => e.pipe(b.onExit((e) => x.isSuccess(e) ? b.void : o.delete(T.id).pipe(b.zipRight(o.releaseUsage(v, C)), b.ignore))))(o.insert(T).pipe(b.zipRight(a.put(S, l, {
1410
+ contentType: e.contentType,
1411
+ public: ne === "public"
1412
+ }).pipe(b.retry(p))))), i.transcode !== void 0 && i.autoTranscode !== !1 && T.derivedFrom == null && (e.contentType.startsWith("video/") || e.contentType.startsWith("audio/")) && (yield* b.forkDaemon(y(T).pipe(b.ignore))), T;
1413
+ }), ne = (e) => b.suspend(() => {
1414
+ if (!i.transcode) return b.succeed([]);
1415
+ let t;
1416
+ try {
1417
+ t = i.transcode(e);
1418
+ } catch {
1419
+ return b.succeed([]);
1420
+ }
1421
+ return b.isEffect(t) ? t.pipe(b.catchAll(() => b.succeed([]))) : b.tryPromise({
1422
+ try: () => Promise.resolve(t),
1423
+ catch: () => []
1424
+ }).pipe(b.catchAll(() => b.succeed([])));
1425
+ }), y = (e) => b.gen(function* () {
1426
+ if (!i.transcode || e.derivedFrom != null) return [];
1427
+ let t = yield* a.get(e.key).pipe(b.retry(p)), n = yield* ne({
1428
+ bytes: t.bytes,
1429
+ contentType: e.contentType,
1430
+ ref: e
1431
+ }), r = [];
1432
+ for (let t of n) {
1433
+ let n = yield* v({
1434
+ bytes: t.bytes,
1435
+ contentType: t.contentType,
1436
+ tenantId: e.tenantId,
1437
+ ownerId: e.ownerId,
1438
+ visibility: e.visibility,
1439
+ derivedFrom: e.id,
1440
+ kind: t.kind,
1441
+ ...t.label === void 0 ? {} : { caption: t.label }
1442
+ });
1443
+ r.push(n);
1444
+ }
1445
+ return r;
1446
+ }), S = (e, t, n) => b.gen(function* () {
1447
+ if (e.visibility === "public" || (yield* et(e, t, {
1448
+ ...l,
1449
+ ...n?.password === void 0 ? {} : { password: n.password }
1450
+ }))) return !0;
1451
+ let r = yield* it(t, l);
1452
+ return rt(yield* s.listByRef(e.id), t, r, n?.permission ?? "read", Date.now());
1453
+ }), w = (e) => {
1454
+ if (e.visibility === "public") {
1455
+ let t = a.publicUrl?.(e.key);
1456
+ if (t != null) return t;
1457
+ }
1458
+ return f(e.id);
1459
+ };
1460
+ return {
1461
+ put: v,
1462
+ get: (e, t) => _(e, t?.tenantId).pipe(b.flatMap((e) => a.get(e.key).pipe(b.retry(p), b.map((t) => ({
1463
+ bytes: t.bytes,
1464
+ ref: e
1465
+ }))))),
1466
+ getRange: (e, n, r) => _(e, r?.tenantId).pipe(b.flatMap((e) => {
1467
+ let r = e.size, i = Math.max(0, Math.trunc(n.start));
1468
+ if (r > 0 && i >= r) return b.fail(new t({
1469
+ provider: a.name,
1470
+ message: `range start ${i} past end ${r}`,
1471
+ transient: !1,
1472
+ status: 416
1473
+ }));
1474
+ let o = Math.min(Math.trunc(n.endInclusive), Math.max(0, r - 1));
1475
+ return o < i ? b.fail(new t({
1476
+ provider: a.name,
1477
+ message: `empty range ${i}-${o}`,
1478
+ transient: !1,
1479
+ status: 416
1480
+ })) : (a.getRange ? a.getRange(e.key, i, o).pipe(b.retry(p)) : a.get(e.key).pipe(b.retry(p), b.map((e) => e.bytes.subarray(i, o + 1)))).pipe(b.map((t) => ({
1481
+ bytes: t,
1482
+ ref: e,
1483
+ totalSize: r
1484
+ })));
1485
+ })),
1486
+ getUrl: (e, t) => _(e, t?.tenantId).pipe(b.flatMap((e) => e.visibility === "public" ? b.succeed(w(e)) : a.presigns ? a.getUrl(e.key, t?.expiresInSec ?? d) : b.succeed(f(e.id)))),
1487
+ head: (e, t) => o.getById(e).pipe(b.flatMap((e) => e ? g(e, t?.tenantId).pipe(b.map((e) => e)) : b.succeed(null))),
1488
+ delete: (e, t) => _(e, t?.tenantId).pipe(b.flatMap((t) => s.deleteByRef(e).pipe(b.flatMap(() => o.delete(e)), b.flatMap((e) => e ? o.countByKey(t.bucket, t.key).pipe(b.flatMap((e) => e === 0 ? a.delete(t.key).pipe(b.retry(p)) : b.void), b.flatMap(() => o.releaseUsage(t.tenantId, {
1489
+ bytes: t.size,
1490
+ count: 1
1491
+ }))) : b.void), b.asVoid))),
1492
+ mintUrl: (n, r, i) => b.gen(function* () {
1493
+ let s = yield* o.getById(n).pipe(b.flatMap((e) => e ? b.succeed(e) : b.fail(new t({
1494
+ provider: a.name,
1495
+ message: `no ref '${n}'`,
1496
+ transient: !1,
1497
+ status: 404
1498
+ }))));
1499
+ if (s.visibility === "public") return w(s);
1500
+ if (!(yield* S(s, r, i?.password === void 0 ? {} : { password: i.password }))) return yield* b.fail(new e({
1501
+ refId: n,
1502
+ reason: "access denied"
1503
+ }));
1504
+ let c = i?.expiresInSec ?? d;
1505
+ if (a.presigns) return yield* a.getUrl(s.key, c);
1506
+ let l = yield* b.sync(() => at(s.id, c));
1507
+ return `${f(s.id)}?t=${l}`;
1508
+ }),
1509
+ mintUploadUrl: (e) => b.gen(function* () {
1510
+ if (!a.getUploadUrl) return yield* b.fail(new t({
1511
+ provider: a.name,
1512
+ message: `provider '${a.name}' does not support presigned uploads`,
1513
+ transient: !1,
1514
+ status: 400
1515
+ }));
1516
+ let n = m(e.tenantId ?? null, "", e.key);
1517
+ return yield* a.getUploadUrl(n, e.expiresInSec ?? d, e.contentType);
1518
+ }),
1519
+ checkAccess: S,
1520
+ grant: (e) => b.gen(function* () {
1521
+ let t = {
1522
+ id: jt(),
1523
+ refId: e.refId,
1524
+ principalType: e.principalType,
1525
+ principalId: e.principalId,
1526
+ permission: e.permission ?? "read",
1527
+ createdAt: /* @__PURE__ */ new Date(),
1528
+ expiresAt: e.expiresAt ?? null,
1529
+ createdBy: e.createdBy ?? null
1530
+ };
1531
+ return yield* s.insert(t), t;
1532
+ }),
1533
+ listRefs: (e) => b.suspend(() => {
1534
+ let t = Math.min(500, Math.max(1, Math.trunc(e?.limit ?? 50))), n = Math.max(0, Math.trunc(e?.offset ?? 0)), r = {
1535
+ tenantId: e && "tenantId" in e ? e.tenantId : void 0,
1536
+ folder: e?.folder,
1537
+ tags: e?.tags ?? [],
1538
+ ownerId: e?.ownerId,
1539
+ includeDerived: e?.includeDerived ?? !1,
1540
+ limit: t + 1,
1541
+ offset: n
1542
+ };
1543
+ return o.listRefs(r).pipe(b.map((e) => {
1544
+ let r = e.length > t;
1545
+ return {
1546
+ refs: r ? e.slice(0, t) : e,
1547
+ nextOffset: r ? n + t : null
1548
+ };
1549
+ }));
1550
+ }),
1551
+ revoke: (e) => s.delete(e),
1552
+ listGrants: (e) => s.listByRef(e),
1553
+ usage: (e) => o.usageByTenant(e),
1554
+ sweepOrphans: (e) => b.gen(function* () {
1555
+ let t = e?.limit ?? 1e3, n = e?.dryRun ?? !1, r = yield* o.listRecent(t), i = [];
1556
+ for (let e of r) (yield* a.head(e.key).pipe(b.catchAll(() => b.succeed(null)))) === null && (n || (yield* o.delete(e.id).pipe(b.catchAll(() => b.succeed(!1)))) && (yield* o.releaseUsage(e.tenantId, {
1557
+ bytes: e.size,
1558
+ count: 1
1559
+ }).pipe(b.ignore)), i.push(e.id));
1560
+ return {
1561
+ scanned: r.length,
1562
+ removed: i
1563
+ };
1564
+ }),
1565
+ ingestUrl: (e, n) => b.gen(function* () {
1566
+ let r = yield* b.tryPromise({
1567
+ try: () => fetch(e),
1568
+ catch: (e) => new t({
1569
+ provider: "ingest",
1570
+ message: `fetch failed: ${String(e?.message ?? e)}`,
1571
+ transient: !0
1572
+ })
1573
+ });
1574
+ if (!r.ok) return yield* b.fail(new t({
1575
+ provider: "ingest",
1576
+ message: `fetch ${e} → ${r.status}`,
1577
+ transient: r.status >= 500,
1578
+ status: r.status
1579
+ }));
1580
+ let i = yield* b.tryPromise({
1581
+ try: () => r.arrayBuffer(),
1582
+ catch: (e) => new t({
1583
+ provider: "ingest",
1584
+ message: `read body failed: ${String(e?.message ?? e)}`,
1585
+ transient: !0
1586
+ })
1587
+ }), a = n?.contentType ?? r.headers.get("content-type")?.split(";")[0]?.trim() ?? "application/octet-stream";
1588
+ return yield* v({
1589
+ bytes: new Uint8Array(i),
1590
+ contentType: a,
1591
+ ...n?.tenantId === void 0 ? {} : { tenantId: n.tenantId },
1592
+ ...n?.ownerId === void 0 ? {} : { ownerId: n.ownerId },
1593
+ ...n?.visibility === void 0 ? {} : { visibility: n.visibility },
1594
+ ...n?.key === void 0 ? {} : { key: n.key },
1595
+ ...n?.folder === void 0 ? {} : { folder: n.folder },
1596
+ ...n?.tags === void 0 ? {} : { tags: n.tags },
1597
+ ...n?.alt === void 0 ? {} : { alt: n.alt },
1598
+ ...n?.caption === void 0 ? {} : { caption: n.caption }
1599
+ });
1600
+ }),
1601
+ mintPresignedUpload: (e) => b.gen(function* () {
1602
+ if (!a.getUploadUrl) return yield* b.fail(new t({
1603
+ provider: a.name,
1604
+ message: `provider '${a.name}' does not support presigned uploads`,
1605
+ transient: !1,
1606
+ status: 400
1607
+ }));
1608
+ let n = e.expiresInSec ?? d, r = `_incoming/${O({
1609
+ kind: "typeid",
1610
+ prefix: "up"
1611
+ }, V)}`, i = m(e.tenantId ?? null, "", r);
1612
+ return {
1613
+ uploadUrl: yield* a.getUploadUrl(i, n, e.contentType),
1614
+ key: i,
1615
+ expiresInSec: n
1616
+ };
1617
+ }),
1618
+ finalizeUpload: (e) => a.get(e.key).pipe(b.retry(p), b.flatMap((t) => v({
1619
+ bytes: t.bytes,
1620
+ contentType: e.contentType,
1621
+ ...e.tenantId === void 0 ? {} : { tenantId: e.tenantId },
1622
+ ...e.ownerId === void 0 ? {} : { ownerId: e.ownerId },
1623
+ ...e.visibility === void 0 ? {} : { visibility: e.visibility },
1624
+ ...e.folder === void 0 ? {} : { folder: e.folder },
1625
+ ...e.tags === void 0 ? {} : { tags: e.tags },
1626
+ ...e.alt === void 0 ? {} : { alt: e.alt },
1627
+ ...e.caption === void 0 ? {} : { caption: e.caption }
1628
+ })), b.ensuring(a.delete(e.key).pipe(b.retry(p), b.ignore))),
1629
+ putResumableChunk: (e) => a.put(h(e.tenantId ?? null, e.uploadId, e.index), e.bytes, { contentType: "application/octet-stream" }).pipe(b.retry(p), b.asVoid),
1630
+ resumableStatus: (e) => b.gen(function* () {
1631
+ let t = 0, n = 0;
1632
+ for (; t < 1e6;) {
1633
+ let r = yield* a.head(h(e.tenantId ?? null, e.uploadId, t)).pipe(b.retry(p), b.catchAll(() => b.succeed(null)));
1634
+ if (r === null) break;
1635
+ n += r.size, t += 1;
1636
+ }
1637
+ return {
1638
+ offset: n,
1639
+ nextIndex: t
1640
+ };
1641
+ }),
1642
+ finalizeResumable: (e) => b.gen(function* () {
1643
+ let t = [];
1644
+ for (let n = 0; n < e.count; n++) {
1645
+ let r = yield* a.get(h(e.tenantId ?? null, e.uploadId, n)).pipe(b.retry(p));
1646
+ t.push(r.bytes);
1647
+ }
1648
+ let n = t.reduce((e, t) => e + t.byteLength, 0), r = new Uint8Array(n), i = 0;
1649
+ for (let e of t) r.set(e, i), i += e.byteLength;
1650
+ let o = b.forEach(Array.from({ length: e.count }, (e, t) => t), (t) => a.delete(h(e.tenantId ?? null, e.uploadId, t)).pipe(b.retry(p), b.ignore), { discard: !0 });
1651
+ return yield* v({
1652
+ bytes: r,
1653
+ contentType: e.contentType,
1654
+ ...e.tenantId === void 0 ? {} : { tenantId: e.tenantId },
1655
+ ...e.ownerId === void 0 ? {} : { ownerId: e.ownerId },
1656
+ ...e.visibility === void 0 ? {} : { visibility: e.visibility },
1657
+ ...e.folder === void 0 ? {} : { folder: e.folder },
1658
+ ...e.tags === void 0 ? {} : { tags: e.tags },
1659
+ ...e.alt === void 0 ? {} : { alt: e.alt },
1660
+ ...e.caption === void 0 ? {} : { caption: e.caption }
1661
+ }).pipe(b.ensuring(o));
1662
+ }),
1663
+ transcode: (e, t) => _(e, t?.tenantId).pipe(b.flatMap((e) => y(e))),
1664
+ renditions: (e, t) => _(e, t?.tenantId).pipe(b.flatMap(() => o.listByDerivedFrom(e))),
1665
+ beginMultipartUpload: (e) => b.gen(function* () {
1666
+ if (!a.createMultipartUpload || !a.presignUploadPart || !a.completeMultipartUpload) return yield* b.fail(new t({
1667
+ provider: a.name,
1668
+ message: `provider '${a.name}' does not support multipart uploads`,
1669
+ transient: !1,
1670
+ status: 400
1671
+ }));
1672
+ let n = m(e.tenantId ?? null, "", `multipart/${O({
1673
+ kind: "typeid",
1674
+ prefix: "mp"
1675
+ }, V)}`);
1676
+ return {
1677
+ uploadId: (yield* a.createMultipartUpload(n, e.contentType, { public: (e.visibility ?? "private") === "public" })).uploadId,
1678
+ key: n
1679
+ };
1680
+ }),
1681
+ signMultipartPart: (e) => b.suspend(() => a.presignUploadPart ? a.presignUploadPart(e.key, e.uploadId, e.partNumber, e.expiresInSec ?? d) : b.fail(new t({
1682
+ provider: a.name,
1683
+ message: "provider does not support multipart uploads",
1684
+ transient: !1,
1685
+ status: 400
1686
+ }))),
1687
+ completeMultipart: (e) => b.gen(function* () {
1688
+ if (!a.completeMultipartUpload) return yield* b.fail(new t({
1689
+ provider: a.name,
1690
+ message: "provider does not support multipart uploads",
1691
+ transient: !1,
1692
+ status: 400
1693
+ }));
1694
+ let r = yield* a.completeMultipartUpload(e.key, e.uploadId, e.parts), s = yield* a.head(e.key).pipe(b.retry(p)), l = s?.size ?? 0, u = (r.etag ?? s?.etag ?? "").replace(/"/g, ""), d = e.tenantId ?? null, f = {
1695
+ bytes: l,
1696
+ count: 1
1697
+ };
1698
+ if (!(yield* o.consumeUsage(d, f, i.quota)).allowed) return yield* a.delete(e.key).pipe(b.retry(p), b.ignore), yield* b.fail(new n({
1699
+ reason: "quota-exceeded",
1700
+ detail: `multipart upload of ${l} bytes exceeds tenant quota`
1701
+ }));
1702
+ let m = {
1703
+ id: At(),
1704
+ tenantId: e.tenantId ?? null,
1705
+ ownerId: e.ownerId ?? null,
1706
+ bucket: c,
1707
+ key: e.key,
1708
+ contentType: e.contentType,
1709
+ size: l,
1710
+ checksum: u,
1711
+ visibility: e.visibility ?? "private",
1712
+ accessPolicy: null,
1713
+ passwordHash: null,
1714
+ createdAt: /* @__PURE__ */ new Date(),
1715
+ ...e.folder === void 0 ? {} : { folder: e.folder },
1716
+ ...e.tags === void 0 ? {} : { tags: e.tags },
1717
+ ...e.alt === void 0 ? {} : { alt: e.alt },
1718
+ ...e.caption === void 0 ? {} : { caption: e.caption }
1719
+ };
1720
+ return yield* o.insert(m).pipe(b.onExit((e) => x.isSuccess(e) ? b.void : o.releaseUsage(d, f).pipe(b.ignore))), m;
1721
+ }),
1722
+ abortMultipart: (e) => b.suspend(() => a.abortMultipartUpload ? a.abortMultipartUpload(e.key, e.uploadId).pipe(b.retry(p)) : b.void)
1723
+ };
1724
+ }, Nt = (e) => S.succeed(i, Mt(e)), Pt = (e, t = 64 * 1024) => {
1725
+ let n = [Buffer.from("zINSTREAM\0", "utf8")];
1726
+ for (let r = 0; r < e.length; r += t) {
1727
+ let i = e.subarray(r, Math.min(r + t, e.length)), a = Buffer.alloc(4);
1728
+ a.writeUInt32BE(i.length, 0), n.push(a, Buffer.from(i));
1729
+ }
1730
+ let r = Buffer.alloc(4);
1731
+ return r.writeUInt32BE(0, 0), n.push(r), Buffer.concat(n);
1732
+ }, Ft = (e) => {
1733
+ let t = e.replace(/\0+$/, "").trim();
1734
+ if (/\bOK$/.test(t)) return { clean: !0 };
1735
+ let n = /:\s*(.+?)\s+FOUND$/.exec(t);
1736
+ if (n) return {
1737
+ clean: !1,
1738
+ threat: n[1] ?? "unknown"
1739
+ };
1740
+ throw Error(`clamav: unexpected response: ${t}`);
1741
+ }, It = async (e, t) => {
1742
+ let n = await import("node:net"), r = W.read("CLAMAV_HOST", t.host) ?? "127.0.0.1", i = W.read("CLAMAV_PORT"), a = t.port ?? (i ? Number(i) : 3310), o = t.timeoutMs ?? 3e4;
1743
+ return new Promise((i, s) => {
1744
+ let c = n.createConnection({
1745
+ host: r,
1746
+ port: a
1747
+ }), l = "", u = setTimeout(() => {
1748
+ c.destroy(), s(/* @__PURE__ */ Error("clamav scan timed out"));
1749
+ }, o);
1750
+ c.on("error", (e) => {
1751
+ clearTimeout(u), s(e);
1752
+ }), c.on("connect", () => {
1753
+ c.end(Pt(e, t.chunkSize));
1754
+ }), c.on("data", (e) => {
1755
+ l += e.toString("utf8");
1756
+ }), c.on("close", () => {
1757
+ clearTimeout(u);
1758
+ try {
1759
+ i(Ft(l));
1760
+ } catch (e) {
1761
+ s(e);
1762
+ }
1763
+ });
1764
+ });
1765
+ }, Lt = (e = {}) => (t) => It(t.bytes, e), Rt = (e, t, n) => new Promise((r, i) => {
1766
+ let a = fe(e, [...t], { stdio: [
1767
+ "ignore",
1768
+ "ignore",
1769
+ "pipe"
1770
+ ] }), o = "", s = setTimeout(() => {
1771
+ a.kill("SIGKILL"), i(/* @__PURE__ */ Error("ffmpeg timed out"));
1772
+ }, n);
1773
+ a.stderr?.on("data", (e) => {
1774
+ o += String(e);
1775
+ }), a.on("error", (e) => {
1776
+ clearTimeout(s), i(e);
1777
+ }), a.on("close", (e) => {
1778
+ clearTimeout(s), e === 0 ? r() : i(/* @__PURE__ */ Error(`ffmpeg exited ${String(e)}: ${o.slice(-400)}`));
1779
+ });
1780
+ }), zt = (e) => async ({ bytes: t }) => {
1781
+ let n = e.ffmpegPath ?? "ffmpeg", r = e.timeoutMs ?? 5 * 6e4, i = await pe(B(he(), "voltro-tc-")), a = B(i, "in"), o = [];
1782
+ try {
1783
+ await z(a, t);
1784
+ for (let t of e.renditions) {
1785
+ let e = B(i, `out.${t.ext}`);
1786
+ try {
1787
+ await Rt(n, [
1788
+ "-y",
1789
+ "-i",
1790
+ a,
1791
+ ...t.args,
1792
+ e
1793
+ ], r);
1794
+ let i = await me(e);
1795
+ o.push({
1796
+ bytes: new Uint8Array(i),
1797
+ contentType: t.contentType,
1798
+ kind: t.kind,
1799
+ ...t.label === void 0 ? {} : { label: t.label }
1800
+ }), await R(e, { force: !0 });
1801
+ } catch {}
1802
+ }
1803
+ } finally {
1804
+ await R(i, {
1805
+ recursive: !0,
1806
+ force: !0
1807
+ }).catch(() => void 0);
1808
+ }
1809
+ return o;
1810
+ };
1811
+ //#endregion
1812
+ //#region src/assetRef.ts
1813
+ function Bt(e = {}) {
1814
+ let t = e.nullable ?? !0, n = e.fk === !1 ? N() : ce(() => Ee, { onDelete: e.onDelete ?? "setNull" });
1815
+ return t ? n.nullable() : n;
1816
+ }
1817
+ var Vt = () => se({
1818
+ id: "@voltro/plugin-storage/withStorage",
1819
+ fields: { asset: Bt() }
1820
+ }), Ht = {
1821
+ "image/png": "png",
1822
+ "image/jpeg": "jpg",
1823
+ "image/jpg": "jpg",
1824
+ "image/gif": "gif",
1825
+ "image/webp": "webp",
1826
+ "image/svg+xml": "svg"
1827
+ }, Ut = (e) => {
1828
+ let t = /^data:([^;,]+)(;base64)?,(.*)$/s.exec(e);
1829
+ if (!t) throw Error("not a data URI");
1830
+ let n = t[1] || "application/octet-stream", r = !!t[2], i = t[3] ?? "";
1831
+ return {
1832
+ bytes: r ? new Uint8Array(Buffer.from(i, "base64")) : new Uint8Array(Buffer.from(decodeURIComponent(i), "utf8")),
1833
+ contentType: n
1834
+ };
1835
+ }, Wt = (e, n) => b.gen(function* () {
1836
+ let r = yield* b.try({
1837
+ try: () => Ut(n.dataUri),
1838
+ catch: (e) => new t({
1839
+ provider: "avatar",
1840
+ message: String(e?.message ?? e),
1841
+ transient: !1,
1842
+ status: 400
1843
+ })
1844
+ }), i = Ht[r.contentType] ?? "bin";
1845
+ return yield* e.put({
1846
+ bytes: r.bytes,
1847
+ contentType: r.contentType,
1848
+ tenantId: n.tenantId,
1849
+ ownerId: n.userId,
1850
+ visibility: "public",
1851
+ key: `avatars/${n.userId}/avatar.${i}`
1852
+ });
1853
+ }), Gt = W.read("VOLTRO_SESSION_COOKIE") ?? "voltro:session", Kt = (e) => {
1854
+ if (e.provider && typeof e.provider == "object") return e.provider;
1855
+ let t = (e.provider ?? W.read("STORAGE_PROVIDER") ?? "memory").toLowerCase(), n = W.read("S3_BUCKET", e.bucket) ?? W.read("STORAGE_BUCKET") ?? "voltro";
1856
+ switch (t) {
1857
+ case "s3":
1858
+ case "minio": {
1859
+ 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);
1860
+ return Fe({
1861
+ bucket: n,
1862
+ name: t,
1863
+ ...r ? { region: r } : {},
1864
+ ...i ? { endpoint: i } : {},
1865
+ ...a ? { accessKeyId: a } : {},
1866
+ ...o ? { secretAccessKey: o } : {},
1867
+ ...s ? { cdnBaseUrl: s } : {},
1868
+ ...e.publicAcl === void 0 ? {} : { publicAcl: e.publicAcl },
1869
+ ...t === "minio" || W.read("S3_FORCE_PATH_STYLE") === "1" ? { forcePathStyle: !0 } : {}
1870
+ });
1871
+ }
1872
+ case "azure": {
1873
+ 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);
1874
+ return Re({
1875
+ container: W.read("AZURE_STORAGE_CONTAINER", e.bucket) ?? n,
1876
+ ...t ? { accountName: t } : {},
1877
+ ...r ? { accountKey: r } : {},
1878
+ ...i ? { connectionString: i } : {},
1879
+ ...a ? { endpoint: a } : {},
1880
+ ...o ? { cdnBaseUrl: o } : {}
1881
+ });
1882
+ }
1883
+ case "database": return Le();
1884
+ case "filesystem": return Pe({ root: W.read("STORAGE_ROOT", e.root) ?? ".voltro-storage" });
1885
+ default: return Ne(e.name ?? "default");
1886
+ }
1887
+ }, qt = (e) => {
1888
+ if (e !== void 0) return e;
1889
+ let t = W.read("STORAGE_ALLOWED_ORIGINS");
1890
+ return t ? t.trim() === "*" ? "*" : t.split(",").map((e) => e.trim()).filter(Boolean) : [];
1891
+ }, Jt = (e, t) => {
1892
+ if (!e || t !== "*" && !t.includes(e)) return {};
1893
+ let n = {
1894
+ "access-control-allow-origin": t === "*" ? "*" : e,
1895
+ "access-control-allow-methods": "GET, POST, OPTIONS",
1896
+ "access-control-allow-headers": "authorization, content-type, range, x-visibility, x-key, x-filename",
1897
+ "access-control-expose-headers": "content-type, content-length, content-range, accept-ranges, location",
1898
+ vary: "Origin"
1899
+ };
1900
+ return t !== "*" && (n["access-control-allow-credentials"] = "true"), n;
1901
+ }, Yt = (e) => ({
1902
+ id: e.id,
1903
+ tenantId: e.tenantId,
1904
+ ownerId: e.ownerId,
1905
+ bucket: e.bucket,
1906
+ key: e.key,
1907
+ contentType: e.contentType,
1908
+ size: e.size,
1909
+ checksum: e.checksum,
1910
+ visibility: e.visibility,
1911
+ accessPolicy: e.accessPolicy ?? null,
1912
+ hasPassword: !!e.passwordHash,
1913
+ createdAt: e.createdAt.toISOString(),
1914
+ width: e.width ?? null,
1915
+ height: e.height ?? null,
1916
+ duration: e.duration ?? null,
1917
+ placeholder: e.placeholder ?? null,
1918
+ folder: e.folder ?? null,
1919
+ tags: e.tags ?? null,
1920
+ alt: e.alt ?? null,
1921
+ caption: e.caption ?? null,
1922
+ derivedFrom: e.derivedFrom ?? null,
1923
+ kind: e.kind ?? null
1924
+ }), Xt = (e) => ({
1925
+ id: e.id,
1926
+ refId: e.refId,
1927
+ principalType: e.principalType,
1928
+ principalId: e.principalId,
1929
+ permission: e.permission,
1930
+ createdAt: e.createdAt.toISOString(),
1931
+ expiresAt: e.expiresAt ? e.expiresAt.toISOString() : null,
1932
+ createdBy: e.createdBy
1933
+ }), Zt = (i = {}) => {
1934
+ let y = Kt(i), x = W.read("S3_BUCKET", i.bucket) ?? W.read("STORAGE_BUCKET") ?? "voltro", S = W.read("VOLTRO_PUBLIC_URL", i.publicBaseUrl), C = qt(i.allowedOrigins), w = i.limits?.maxBytes, E = 5 * 1024 * 1024, oe = 6 * 3600, D = i.refStore === void 0 || i.refStore === "memory", O = i.grantStore === void 0 || i.grantStore === "memory", k = { current: D ? wt() : i.refStore }, A = { current: O ? Dt() : i.grantStore }, j = {
1935
+ insert: (e) => k.current.insert(e),
1936
+ getById: (e) => k.current.getById(e),
1937
+ delete: (e) => k.current.delete(e),
1938
+ listRecent: (e) => k.current.listRecent(e),
1939
+ countByKey: (e, t) => k.current.countByKey(e, t),
1940
+ usageByTenant: (e) => k.current.usageByTenant(e),
1941
+ consumeUsage: (e, t, n) => k.current.consumeUsage(e, t, n),
1942
+ releaseUsage: (e, t) => k.current.releaseUsage(e, t),
1943
+ listByDerivedFrom: (e) => k.current.listByDerivedFrom(e),
1944
+ listRefs: (e) => k.current.listRefs(e)
1945
+ }, se = {
1946
+ insert: (e) => A.current.insert(e),
1947
+ delete: (e) => A.current.delete(e),
1948
+ listByRef: (e) => A.current.listByRef(e),
1949
+ deleteByRef: (e) => A.current.deleteByRef(e)
1950
+ }, ce = {
1951
+ provider: y,
1952
+ refStore: j,
1953
+ grantStore: se,
1954
+ bucket: x,
1955
+ ...i.tenantPrefix === void 0 ? {} : { tenantPrefix: i.tenantPrefix },
1956
+ ...i.attempts === void 0 ? {} : { attempts: i.attempts },
1957
+ ...i.urlExpiresInSec === void 0 ? {} : { urlExpiresInSec: i.urlExpiresInSec },
1958
+ ...i.access === void 0 ? {} : { access: i.access },
1959
+ ...i.limits === void 0 ? {} : { limits: i.limits },
1960
+ ...i.scan === void 0 ? {} : { scan: i.scan },
1961
+ ...i.videoProbe === void 0 ? {} : { videoProbe: i.videoProbe },
1962
+ ...i.transcode === void 0 ? {} : { transcode: i.transcode },
1963
+ ...i.autoTranscode === void 0 ? {} : { autoTranscode: i.autoTranscode },
1964
+ ...S ? { publicBaseUrl: S } : {},
1965
+ ...i.normalizeImages === void 0 ? {} : { normalizeImages: i.normalizeImages },
1966
+ ...i.quota === void 0 ? {} : { quota: i.quota }
1967
+ }, M = Nt(ce), N = Mt(ce), P = (e) => b.runPromise(e.pipe(b.catchAll(() => b.succeed(null)))), le = (e, t) => {
1968
+ if (!e) return "invalid";
1969
+ let n = /^bytes=(\d*)-(\d*)$/.exec(e.trim());
1970
+ if (!n) return "invalid";
1971
+ let r = n[1] ?? "", i = n[2] ?? "";
1972
+ if (r === "" && i === "") return "invalid";
1973
+ if (r === "") {
1974
+ let e = Number(i);
1975
+ return !Number.isFinite(e) || e <= 0 ? "invalid" : t === 0 ? "unsatisfiable" : {
1976
+ start: Math.max(0, t - e),
1977
+ endInclusive: t - 1
1978
+ };
1979
+ }
1980
+ let a = Number(r);
1981
+ if (!Number.isFinite(a)) return "invalid";
1982
+ if (t > 0 && a >= t) return "unsatisfiable";
1983
+ let o = i === "" ? Math.max(0, t - 1) : Math.min(Number(i), Math.max(0, t - 1));
1984
+ return !Number.isFinite(o) || o < a ? "invalid" : {
1985
+ start: a,
1986
+ endInclusive: o
1987
+ };
1988
+ }, F = /^(?:image\/(?!svg)|video\/|audio\/|application\/pdf(?:;|$)|text\/plain(?:;|$))/i, I = (e) => ({
1989
+ "x-content-type-options": "nosniff",
1990
+ "content-disposition": F.test(e) ? "inline" : "attachment"
1991
+ }), de = async (e, t, n, r) => {
1992
+ if (n) {
1993
+ let r = await P(N.get(e.id));
1994
+ if (!r) return {
1995
+ status: 404,
1996
+ body: "not found",
1997
+ contentType: "text/plain"
1998
+ };
1999
+ let i = await Ge(e.id, r.bytes, e.contentType, n).catch(() => null), a = i ? i.bytes : r.bytes, o = i ? i.contentType : e.contentType;
2000
+ return {
2001
+ status: 200,
2002
+ body: a,
2003
+ contentType: o,
2004
+ headers: {
2005
+ ...I(o),
2006
+ "cache-control": t,
2007
+ "accept-ranges": "none"
2008
+ }
2009
+ };
2010
+ }
2011
+ let i = r === void 0 ? "invalid" : le(r, e.size);
2012
+ if (i === "unsatisfiable") return {
2013
+ status: 416,
2014
+ body: "range not satisfiable",
2015
+ contentType: "text/plain",
2016
+ headers: {
2017
+ "content-range": `bytes */${e.size}`,
2018
+ "accept-ranges": "bytes"
2019
+ }
2020
+ };
2021
+ if (i !== "invalid") {
2022
+ let n = await P(N.getRange(e.id, i));
2023
+ return n ? {
2024
+ status: 206,
2025
+ body: n.bytes,
2026
+ contentType: e.contentType,
2027
+ headers: {
2028
+ ...I(e.contentType),
2029
+ "cache-control": t,
2030
+ "accept-ranges": "bytes",
2031
+ "content-range": `bytes ${i.start}-${i.endInclusive}/${n.totalSize}`
2032
+ }
2033
+ } : {
2034
+ status: 404,
2035
+ body: "not found",
2036
+ contentType: "text/plain"
2037
+ };
2038
+ }
2039
+ let a = await P(N.get(e.id));
2040
+ return a ? {
2041
+ status: 200,
2042
+ body: a.bytes,
2043
+ contentType: e.contentType,
2044
+ headers: {
2045
+ ...I(e.contentType),
2046
+ "cache-control": t,
2047
+ "accept-ranges": "bytes"
2048
+ }
2049
+ } : {
2050
+ status: 404,
2051
+ body: "not found",
2052
+ contentType: "text/plain"
2053
+ };
2054
+ }, L = async (e) => {
2055
+ let t = e.path.startsWith("/_voltro/storage/") ? e.path.slice(17).split("/")[0] ?? "" : "";
2056
+ if (!t) return {
2057
+ status: 404,
2058
+ body: "not found",
2059
+ contentType: "text/plain"
2060
+ };
2061
+ let n = await P(N.head(t));
2062
+ if (!n) return {
2063
+ status: 404,
2064
+ body: "not found",
2065
+ contentType: "text/plain"
2066
+ };
2067
+ let r = Be(e.query), i = e.headers.range;
2068
+ if (n.visibility === "public") {
2069
+ let e = y.publicUrl?.(n.key) ?? null;
2070
+ return e && !r ? {
2071
+ status: 302,
2072
+ headers: {
2073
+ location: e,
2074
+ "cache-control": "public, max-age=300"
2075
+ }
2076
+ } : de(n, "public, max-age=31536000, immutable", r, i);
2077
+ }
2078
+ let a = !1, o = e.query ? new URLSearchParams(e.query).get("t") : null;
2079
+ if (o) {
2080
+ let e = ot(o);
2081
+ a = !!e && e.refId === t;
2082
+ }
2083
+ if (!a) {
2084
+ let t = T(e.headers.cookie, Gt), r = t ? ae(t, ie()) : null;
2085
+ r && (a = await P(N.checkAccess(n, r)) === !0);
2086
+ }
2087
+ if (!a) return {
2088
+ status: 403,
2089
+ body: "forbidden",
2090
+ contentType: "text/plain"
2091
+ };
2092
+ if (y.presigns && !r) {
2093
+ let e = await P(y.getUrl(n.key, 300));
2094
+ if (e && /^https?:/.test(e)) return {
2095
+ status: 302,
2096
+ headers: {
2097
+ location: e,
2098
+ "cache-control": "private, max-age=60"
2099
+ }
2100
+ };
2101
+ }
2102
+ return de(n, "private, no-store", r, i);
2103
+ }, fe = {
2104
+ method: "GET",
2105
+ path: "/_voltro/storage",
2106
+ handle: async (e) => {
2107
+ let t = await L(e), n = Jt(e.headers.origin, C);
2108
+ return Object.keys(n).length === 0 ? t : {
2109
+ ...t,
2110
+ headers: {
2111
+ ...t.headers,
2112
+ ...n
2113
+ }
2114
+ };
2115
+ }
2116
+ }, pe = {
2117
+ method: "*",
2118
+ path: "/_voltro/storage/upload",
2119
+ handle: async (e) => {
2120
+ let t = Jt(e.headers.origin, C);
2121
+ if (e.method === "OPTIONS") return {
2122
+ status: 204,
2123
+ headers: t
2124
+ };
2125
+ if (e.method !== "POST") return {
2126
+ status: 405,
2127
+ body: "method not allowed",
2128
+ contentType: "text/plain",
2129
+ headers: t
2130
+ };
2131
+ let n = e.query ? new URLSearchParams(e.query).get("ticket") : null, r = n ? ct(n) : null;
2132
+ if (!r) return {
2133
+ status: 401,
2134
+ body: "invalid or expired upload ticket",
2135
+ contentType: "text/plain",
2136
+ headers: t
2137
+ };
2138
+ if (e.rawBody.length === 0) return {
2139
+ status: 400,
2140
+ body: "empty body",
2141
+ contentType: "text/plain",
2142
+ headers: t
2143
+ };
2144
+ if (r.max && e.rawBody.length > r.max) return {
2145
+ status: 413,
2146
+ body: "payload too large",
2147
+ contentType: "text/plain",
2148
+ headers: t
2149
+ };
2150
+ let i = await b.runPromise(N.put({
2151
+ bytes: e.rawBody,
2152
+ contentType: r.ct,
2153
+ tenantId: r.tenant,
2154
+ ownerId: r.sub,
2155
+ visibility: r.vis,
2156
+ ...r.key === void 0 ? {} : { key: r.key },
2157
+ ...r.folder === void 0 ? {} : { folder: r.folder },
2158
+ ...r.tags === void 0 ? {} : { tags: r.tags },
2159
+ ...r.alt === void 0 ? {} : { alt: r.alt },
2160
+ ...r.caption === void 0 ? {} : { caption: r.caption }
2161
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2162
+ kind: "ok",
2163
+ ref: e,
2164
+ url: t
2165
+ })))), b.catchTags({
2166
+ StorageRejected: (e) => b.succeed({
2167
+ kind: "rejected",
2168
+ status: e.reason === "too-large" ? 413 : 415,
2169
+ detail: e.detail
2170
+ }),
2171
+ StorageScanRejected: (e) => b.succeed({
2172
+ kind: "scan",
2173
+ threat: e.threat
2174
+ }),
2175
+ StorageError: (e) => b.succeed({
2176
+ kind: "error",
2177
+ message: e.message
2178
+ })
2179
+ })));
2180
+ return i.kind === "ok" ? {
2181
+ status: 201,
2182
+ body: JSON.stringify({
2183
+ id: i.ref.id,
2184
+ url: i.url,
2185
+ contentType: i.ref.contentType,
2186
+ size: i.ref.size
2187
+ }),
2188
+ contentType: "application/json",
2189
+ headers: t
2190
+ } : i.kind === "rejected" ? {
2191
+ status: i.status,
2192
+ body: i.detail,
2193
+ contentType: "text/plain",
2194
+ headers: t
2195
+ } : i.kind === "scan" ? {
2196
+ status: 422,
2197
+ body: `rejected: ${i.threat}`,
2198
+ contentType: "text/plain",
2199
+ headers: t
2200
+ } : {
2201
+ status: 500,
2202
+ body: i.message,
2203
+ contentType: "text/plain",
2204
+ headers: t
2205
+ };
2206
+ }
2207
+ }, me = {
2208
+ method: "*",
2209
+ path: "/_voltro/storage/upload/resumable",
2210
+ handle: async (e) => {
2211
+ let t = Jt(e.headers.origin, C);
2212
+ if (e.method === "OPTIONS") return {
2213
+ status: 204,
2214
+ headers: t
2215
+ };
2216
+ let n = new URLSearchParams(e.query || ""), r = ft(n.get("token") ?? "");
2217
+ if (!r) return {
2218
+ status: 401,
2219
+ body: "invalid or expired resumable token",
2220
+ contentType: "text/plain",
2221
+ headers: t
2222
+ };
2223
+ if (e.method === "GET" || e.method === "HEAD") {
2224
+ let e = await b.runPromise(N.resumableStatus({
2225
+ uploadId: r.id,
2226
+ tenantId: r.tenant
2227
+ }).pipe(b.match({
2228
+ onFailure: (e) => ({ error: e.message }),
2229
+ onSuccess: (e) => e
2230
+ })));
2231
+ return "error" in e ? {
2232
+ status: 500,
2233
+ body: e.error,
2234
+ contentType: "text/plain",
2235
+ headers: t
2236
+ } : {
2237
+ status: 200,
2238
+ body: JSON.stringify(e),
2239
+ contentType: "application/json",
2240
+ headers: {
2241
+ ...t,
2242
+ "upload-offset": String(e.offset)
2243
+ }
2244
+ };
2245
+ }
2246
+ if (e.method !== "POST") return {
2247
+ status: 405,
2248
+ body: "method not allowed",
2249
+ contentType: "text/plain",
2250
+ headers: t
2251
+ };
2252
+ let i = Number(n.get("index"));
2253
+ if (!Number.isInteger(i) || i < 0) return {
2254
+ status: 400,
2255
+ body: "missing/invalid chunk index",
2256
+ contentType: "text/plain",
2257
+ headers: t
2258
+ };
2259
+ if (e.rawBody.length === 0) return {
2260
+ status: 400,
2261
+ body: "empty chunk",
2262
+ contentType: "text/plain",
2263
+ headers: t
2264
+ };
2265
+ let a = n.get("final") === "1", o = await b.runPromise(N.putResumableChunk({
2266
+ uploadId: r.id,
2267
+ tenantId: r.tenant,
2268
+ index: i,
2269
+ bytes: e.rawBody
2270
+ }).pipe(b.flatMap(() => a ? N.finalizeResumable({
2271
+ uploadId: r.id,
2272
+ count: i + 1,
2273
+ contentType: r.ct,
2274
+ tenantId: r.tenant,
2275
+ ownerId: r.sub,
2276
+ visibility: r.vis,
2277
+ ...r.folder === void 0 ? {} : { folder: r.folder },
2278
+ ...r.tags === void 0 ? {} : { tags: r.tags },
2279
+ ...r.alt === void 0 ? {} : { alt: r.alt },
2280
+ ...r.caption === void 0 ? {} : { caption: r.caption }
2281
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2282
+ kind: "done",
2283
+ ref: e,
2284
+ url: t
2285
+ }))))) : b.succeed({ kind: "chunk" })), b.catchTags({
2286
+ StorageRejected: (e) => b.succeed({
2287
+ kind: "rejected",
2288
+ status: e.reason === "too-large" ? 413 : 415,
2289
+ detail: e.detail
2290
+ }),
2291
+ StorageScanRejected: (e) => b.succeed({
2292
+ kind: "scan",
2293
+ threat: e.threat
2294
+ }),
2295
+ StorageError: (e) => b.succeed({
2296
+ kind: "error",
2297
+ message: e.message
2298
+ })
2299
+ })));
2300
+ return o.kind === "chunk" ? {
2301
+ status: 204,
2302
+ headers: {
2303
+ ...t,
2304
+ "upload-offset": String(i + 1)
2305
+ }
2306
+ } : o.kind === "done" ? {
2307
+ status: 201,
2308
+ body: JSON.stringify({
2309
+ id: o.ref.id,
2310
+ url: o.url,
2311
+ contentType: o.ref.contentType,
2312
+ size: o.ref.size
2313
+ }),
2314
+ contentType: "application/json",
2315
+ headers: t
2316
+ } : o.kind === "rejected" ? {
2317
+ status: o.status,
2318
+ body: o.detail,
2319
+ contentType: "text/plain",
2320
+ headers: t
2321
+ } : o.kind === "scan" ? {
2322
+ status: 422,
2323
+ body: `rejected: ${o.threat}`,
2324
+ contentType: "text/plain",
2325
+ headers: t
2326
+ } : {
2327
+ status: 500,
2328
+ body: o.message,
2329
+ contentType: "text/plain",
2330
+ headers: t
2331
+ };
2332
+ }
2333
+ }, R = (e) => ({
2334
+ kind: "json",
2335
+ data: e
2336
+ }), z = (e, t) => ({
2337
+ kind: "json",
2338
+ status: e,
2339
+ data: { error: t }
2340
+ }), he = (e, t) => {
2341
+ try {
2342
+ return new URL(e, "http://localhost").searchParams.get(t);
2343
+ } catch {
2344
+ return null;
2345
+ }
2346
+ }, B = [
2347
+ {
2348
+ method: "GET",
2349
+ path: "/refs",
2350
+ description: "Recent storage refs (newest first)",
2351
+ handler: () => j.listRecent(100).pipe(b.map((e) => R({ refs: e.map(Yt) })), b.catchAll((e) => b.succeed(z(500, e.message))))
2352
+ },
2353
+ {
2354
+ method: "GET",
2355
+ path: "/stats",
2356
+ description: "Object count + total bytes per tenant + visibility split",
2357
+ handler: () => j.listRecent(1e4).pipe(b.map((e) => {
2358
+ let t = {}, n = 0, r = 0;
2359
+ for (let i of e) {
2360
+ let e = i.tenantId ?? "(global)", a = t[e] ?? {
2361
+ count: 0,
2362
+ bytes: 0
2363
+ };
2364
+ a.count++, a.bytes += i.size, t[e] = a, i.visibility === "public" ? n++ : r++;
2365
+ }
2366
+ return R({
2367
+ provider: y.name,
2368
+ bucket: x,
2369
+ byTenant: t,
2370
+ visibility: {
2371
+ public: n,
2372
+ private: r
2373
+ }
2374
+ });
2375
+ }), b.catchAll((e) => b.succeed(z(500, e.message))))
2376
+ },
2377
+ {
2378
+ method: "GET",
2379
+ path: "/grants",
2380
+ description: "Grants for a ref (?refId=…)",
2381
+ handler: (e) => {
2382
+ let t = he(e.url, "refId");
2383
+ return t ? se.listByRef(t).pipe(b.map((e) => R({ grants: e.map(Xt) })), b.catchAll((e) => b.succeed(z(500, e.message)))) : b.succeed(z(400, "refId query param required"));
2384
+ }
2385
+ },
2386
+ {
2387
+ method: "POST",
2388
+ path: "/share",
2389
+ description: "Grant a principal access to a ref",
2390
+ handler: (e) => b.gen(function* () {
2391
+ let n = yield* b.try({
2392
+ try: () => JSON.parse(e.body || "{}"),
2393
+ catch: () => new t({
2394
+ provider: "inspect",
2395
+ message: "invalid JSON body",
2396
+ transient: !1,
2397
+ status: 400
2398
+ })
2399
+ });
2400
+ if (!n.refId || !n.principalType || !n.principalId) return z(400, "refId, principalType, principalId required");
2401
+ let r = n.expiresInDays ? new Date(Date.now() + n.expiresInDays * 864e5) : null, i = yield* N.grant({
2402
+ refId: n.refId,
2403
+ principalType: n.principalType,
2404
+ principalId: n.principalId,
2405
+ ...n.permission ? { permission: n.permission } : {},
2406
+ expiresAt: r
2407
+ });
2408
+ return R({ grant: Xt(i) });
2409
+ }).pipe(b.catchAll((e) => b.succeed(z(e.status ?? 500, e.message ?? "share failed"))))
2410
+ },
2411
+ {
2412
+ method: "POST",
2413
+ path: "/revoke",
2414
+ description: "Revoke a grant by id",
2415
+ handler: (e) => b.gen(function* () {
2416
+ let n = yield* b.try({
2417
+ try: () => JSON.parse(e.body || "{}"),
2418
+ catch: () => new t({
2419
+ provider: "inspect",
2420
+ message: "invalid JSON body",
2421
+ transient: !1,
2422
+ status: 400
2423
+ })
2424
+ });
2425
+ return n.grantId ? (yield* N.revoke(n.grantId), R({ ok: !0 })) : z(400, "grantId required");
2426
+ }).pipe(b.catchAll((e) => b.succeed(z(e.status ?? 500, e.message ?? "revoke failed"))))
2427
+ }
2428
+ ], ge = [
2429
+ {
2430
+ ...g,
2431
+ execute: (e, t) => {
2432
+ let n = e;
2433
+ return N.mintUrl(n.id, t.request.subject, {
2434
+ ...n.password === void 0 ? {} : { password: n.password },
2435
+ ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
2436
+ }).pipe(b.map((e) => ({ url: e })));
2437
+ }
2438
+ },
2439
+ {
2440
+ ...h,
2441
+ execute: (e, t) => {
2442
+ let n = e;
2443
+ return N.mintUploadUrl({
2444
+ key: n.key,
2445
+ contentType: n.contentType,
2446
+ tenantId: t.request.subject.tenantId,
2447
+ ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
2448
+ }).pipe(b.map((e) => ({ url: e })));
2449
+ }
2450
+ },
2451
+ {
2452
+ ...m,
2453
+ execute: (e, t) => {
2454
+ let n = e;
2455
+ return b.sync(() => {
2456
+ let e = n.expiresInSec ?? 900, r = st({
2457
+ sub: t.request.subject.id,
2458
+ tenant: t.request.subject.tenantId,
2459
+ ct: n.contentType,
2460
+ vis: n.visibility ?? "private",
2461
+ ...n.key === void 0 ? {} : { key: n.key },
2462
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2463
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2464
+ ...n.alt === void 0 ? {} : { alt: n.alt },
2465
+ ...n.caption === void 0 ? {} : { caption: n.caption },
2466
+ ...w === void 0 ? {} : { max: w }
2467
+ }, e);
2468
+ return {
2469
+ uploadUrl: `${S ? S.replace(/\/+$/, "") : ""}/_voltro/storage/upload?ticket=${encodeURIComponent(r)}`,
2470
+ expiresInSec: e
2471
+ };
2472
+ });
2473
+ }
2474
+ },
2475
+ {
2476
+ ...p,
2477
+ execute: (e, t) => {
2478
+ let n = e;
2479
+ return N.mintPresignedUpload({
2480
+ contentType: n.contentType,
2481
+ tenantId: t.request.subject.tenantId,
2482
+ ...n.expiresInSec === void 0 ? {} : { expiresInSec: n.expiresInSec }
2483
+ }).pipe(b.map((e) => {
2484
+ let r = lt({
2485
+ key: e.key,
2486
+ sub: t.request.subject.id,
2487
+ tenant: t.request.subject.tenantId,
2488
+ ct: n.contentType,
2489
+ vis: n.visibility ?? "private",
2490
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2491
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2492
+ ...n.alt === void 0 ? {} : { alt: n.alt },
2493
+ ...n.caption === void 0 ? {} : { caption: n.caption },
2494
+ ...w === void 0 ? {} : { max: w }
2495
+ }, e.expiresInSec);
2496
+ return {
2497
+ uploadUrl: e.uploadUrl,
2498
+ finalizeToken: r,
2499
+ expiresInSec: e.expiresInSec
2500
+ };
2501
+ }));
2502
+ }
2503
+ },
2504
+ {
2505
+ ...l,
2506
+ execute: (e, n) => {
2507
+ let r = ut(e.finalizeToken);
2508
+ return r ? r.sub !== null && r.sub !== n.request.subject.id ? b.fail(new t({
2509
+ provider: "storage",
2510
+ message: "finalize token belongs to another subject",
2511
+ transient: !1,
2512
+ status: 403
2513
+ })) : N.finalizeUpload({
2514
+ key: r.key,
2515
+ contentType: r.ct,
2516
+ tenantId: r.tenant,
2517
+ ownerId: r.sub,
2518
+ visibility: r.vis,
2519
+ ...r.folder === void 0 ? {} : { folder: r.folder },
2520
+ ...r.tags === void 0 ? {} : { tags: r.tags },
2521
+ ...r.alt === void 0 ? {} : { alt: r.alt },
2522
+ ...r.caption === void 0 ? {} : { caption: r.caption }
2523
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2524
+ id: e.id,
2525
+ url: t,
2526
+ contentType: e.contentType,
2527
+ size: e.size,
2528
+ ...e.width == null ? {} : { width: e.width },
2529
+ ...e.height == null ? {} : { height: e.height }
2530
+ }))))) : b.fail(new t({
2531
+ provider: "storage",
2532
+ message: "invalid or expired finalize token",
2533
+ transient: !1,
2534
+ status: 401
2535
+ }));
2536
+ }
2537
+ },
2538
+ {
2539
+ ...s,
2540
+ execute: (e, t) => {
2541
+ let n = e;
2542
+ return b.sync(() => {
2543
+ let e = n.expiresInSec ?? oe, r = ue(), i = n.chunkSize && n.chunkSize > 0 ? n.chunkSize : E;
2544
+ return {
2545
+ uploadId: r,
2546
+ resumableToken: dt({
2547
+ id: r,
2548
+ sub: t.request.subject.id,
2549
+ tenant: t.request.subject.tenantId,
2550
+ ct: n.contentType,
2551
+ vis: n.visibility ?? "private",
2552
+ size: n.size,
2553
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2554
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2555
+ ...n.alt === void 0 ? {} : { alt: n.alt },
2556
+ ...n.caption === void 0 ? {} : { caption: n.caption },
2557
+ ...w === void 0 ? {} : { max: w }
2558
+ }, e),
2559
+ uploadUrl: `${S ? S.replace(/\/+$/, "") : ""}/_voltro/storage/upload/resumable`,
2560
+ chunkSize: i,
2561
+ expiresInSec: e
2562
+ };
2563
+ });
2564
+ }
2565
+ },
2566
+ {
2567
+ ...o,
2568
+ execute: (e, t) => {
2569
+ let n = e, r = n.expiresInSec ?? oe;
2570
+ return N.beginMultipartUpload({
2571
+ contentType: n.contentType,
2572
+ tenantId: t.request.subject.tenantId,
2573
+ visibility: n.visibility ?? "private",
2574
+ expiresInSec: r
2575
+ }).pipe(b.map((e) => ({
2576
+ multipartToken: pt({
2577
+ key: e.key,
2578
+ uploadId: e.uploadId,
2579
+ sub: t.request.subject.id,
2580
+ tenant: t.request.subject.tenantId,
2581
+ ct: n.contentType,
2582
+ vis: n.visibility ?? "private",
2583
+ size: n.size,
2584
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2585
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2586
+ ...n.alt === void 0 ? {} : { alt: n.alt },
2587
+ ...n.caption === void 0 ? {} : { caption: n.caption }
2588
+ }, r),
2589
+ partSize: n.partSize && n.partSize >= E ? n.partSize : E,
2590
+ expiresInSec: r
2591
+ })));
2592
+ }
2593
+ },
2594
+ {
2595
+ ...te,
2596
+ execute: (e) => {
2597
+ let n = e, r = mt(n.multipartToken);
2598
+ return r ? !Number.isInteger(n.partNumber) || n.partNumber < 1 ? b.fail(new t({
2599
+ provider: "storage",
2600
+ message: "partNumber must be a positive integer",
2601
+ transient: !1,
2602
+ status: 400
2603
+ })) : N.signMultipartPart({
2604
+ key: r.key,
2605
+ uploadId: r.uploadId,
2606
+ partNumber: n.partNumber
2607
+ }).pipe(b.map((e) => ({ url: e }))) : b.fail(new t({
2608
+ provider: "storage",
2609
+ message: "invalid or expired multipart token",
2610
+ transient: !1,
2611
+ status: 401
2612
+ }));
2613
+ }
2614
+ },
2615
+ {
2616
+ ...c,
2617
+ execute: (e, n) => {
2618
+ let r = e, i = mt(r.multipartToken);
2619
+ return i ? i.sub !== null && i.sub !== n.request.subject.id ? b.fail(new t({
2620
+ provider: "storage",
2621
+ message: "multipart token belongs to another subject",
2622
+ transient: !1,
2623
+ status: 403
2624
+ })) : N.completeMultipart({
2625
+ key: i.key,
2626
+ uploadId: i.uploadId,
2627
+ parts: r.parts,
2628
+ contentType: i.ct,
2629
+ tenantId: i.tenant,
2630
+ ownerId: i.sub,
2631
+ visibility: i.vis,
2632
+ ...i.folder === void 0 ? {} : { folder: i.folder },
2633
+ ...i.tags === void 0 ? {} : { tags: i.tags },
2634
+ ...i.alt === void 0 ? {} : { alt: i.alt },
2635
+ ...i.caption === void 0 ? {} : { caption: i.caption }
2636
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2637
+ id: e.id,
2638
+ url: t,
2639
+ size: e.size
2640
+ }))))) : b.fail(new t({
2641
+ provider: "storage",
2642
+ message: "invalid or expired multipart token",
2643
+ transient: !1,
2644
+ status: 401
2645
+ }));
2646
+ }
2647
+ },
2648
+ {
2649
+ ...a,
2650
+ execute: (e) => {
2651
+ let n = mt(e.multipartToken);
2652
+ return n ? N.abortMultipart({
2653
+ key: n.key,
2654
+ uploadId: n.uploadId
2655
+ }).pipe(b.as({ ok: !0 })) : b.fail(new t({
2656
+ provider: "storage",
2657
+ message: "invalid or expired multipart token",
2658
+ transient: !1,
2659
+ status: 401
2660
+ }));
2661
+ }
2662
+ },
2663
+ {
2664
+ ...ne,
2665
+ execute: (e, t) => {
2666
+ let n = e, r = new Uint8Array(Buffer.from(n.bytesBase64, "base64"));
2667
+ return N.put({
2668
+ bytes: r,
2669
+ contentType: n.contentType,
2670
+ tenantId: t.request.subject.tenantId,
2671
+ ownerId: t.request.subject.id,
2672
+ ...n.visibility ? { visibility: n.visibility } : {},
2673
+ ...n.key ? { key: n.key } : {},
2674
+ ...n.password ? { password: n.password } : {}
2675
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2676
+ id: e.id,
2677
+ url: t
2678
+ })))));
2679
+ }
2680
+ },
2681
+ {
2682
+ ...u,
2683
+ execute: (e, t) => {
2684
+ let n = e;
2685
+ return N.ingestUrl(n.url, {
2686
+ tenantId: t.request.subject.tenantId,
2687
+ ownerId: t.request.subject.id,
2688
+ ...n.visibility ? { visibility: n.visibility } : {},
2689
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2690
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2691
+ ...n.alt === void 0 ? {} : { alt: n.alt },
2692
+ ...n.caption === void 0 ? {} : { caption: n.caption },
2693
+ ...n.key === void 0 ? {} : { key: n.key }
2694
+ }).pipe(b.flatMap((e) => N.getUrl(e.id).pipe(b.map((t) => ({
2695
+ id: e.id,
2696
+ url: t
2697
+ })))));
2698
+ }
2699
+ },
2700
+ {
2701
+ ...ee,
2702
+ execute: (e, t) => {
2703
+ let n = e, r = n.expiresInDays ? new Date(Date.now() + n.expiresInDays * 864e5) : null;
2704
+ return N.grant({
2705
+ refId: n.refId,
2706
+ principalType: n.principalType,
2707
+ principalId: n.principalId,
2708
+ ...n.permission ? { permission: n.permission } : {},
2709
+ expiresAt: r,
2710
+ createdBy: t.request.subject.id
2711
+ }).pipe(b.map((e) => ({ grantId: e.id })));
2712
+ }
2713
+ },
2714
+ {
2715
+ ..._,
2716
+ execute: (e) => {
2717
+ let t = e;
2718
+ return N.revoke(t.grantId).pipe(b.map(() => ({ ok: !0 })));
2719
+ }
2720
+ },
2721
+ {
2722
+ ...d,
2723
+ execute: (e) => {
2724
+ let t = e;
2725
+ return N.listGrants(t.refId).pipe(b.map((e) => ({ grants: e.map(Xt) })));
2726
+ }
2727
+ },
2728
+ {
2729
+ ...f,
2730
+ execute: (e, t) => {
2731
+ let n = e;
2732
+ return N.listRefs({
2733
+ tenantId: t.request.subject.tenantId,
2734
+ ...n.folder === void 0 ? {} : { folder: n.folder },
2735
+ ...n.tags === void 0 ? {} : { tags: n.tags },
2736
+ ...n.ownerId === void 0 ? {} : { ownerId: n.ownerId },
2737
+ ...n.includeDerived === void 0 ? {} : { includeDerived: n.includeDerived },
2738
+ ...n.limit === void 0 ? {} : { limit: n.limit },
2739
+ ...n.offset === void 0 ? {} : { offset: n.offset }
2740
+ }).pipe(b.map((e) => ({
2741
+ refs: e.refs.map(Yt),
2742
+ nextOffset: e.nextOffset
2743
+ })));
2744
+ }
2745
+ }
2746
+ ];
2747
+ return re({
2748
+ name: i.name ? `@voltro/plugin-storage#${i.name}` : "@voltro/plugin-storage",
2749
+ 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.",
2750
+ permissions: ["network:outbound:*", "inspect:read"],
2751
+ declaredEnv: W.declared,
2752
+ services: M,
2753
+ inspectEndpoints: B,
2754
+ httpRoutes: [
2755
+ fe,
2756
+ pe,
2757
+ me
2758
+ ],
2759
+ routes: ge,
2760
+ rpcClientDescriptors: v,
2761
+ errorSchemas: [
2762
+ {
2763
+ schema: t,
2764
+ import: {
2765
+ module: "@voltro/plugin-storage/errors",
2766
+ name: "StorageError"
2767
+ }
2768
+ },
2769
+ {
2770
+ schema: e,
2771
+ import: {
2772
+ module: "@voltro/plugin-storage/errors",
2773
+ name: "StorageAccessDenied"
2774
+ }
2775
+ },
2776
+ {
2777
+ schema: n,
2778
+ import: {
2779
+ module: "@voltro/plugin-storage/errors",
2780
+ name: "StorageRejected"
2781
+ }
2782
+ },
2783
+ {
2784
+ schema: r,
2785
+ import: {
2786
+ module: "@voltro/plugin-storage/errors",
2787
+ name: "StorageScanRejected"
2788
+ }
2789
+ }
2790
+ ],
2791
+ bindDataStore: (e) => {
2792
+ D && (k.current = Et(e)), O && (A.current = kt(e)), y.bindDataStore?.(e);
2793
+ },
2794
+ onActivate: (e) => b.sync(() => {
2795
+ let t = !!S || !!W.read("STORAGE_CDN_URL", i.cdnBaseUrl);
2796
+ e.logger.info(`storage: provider=${y.name} presign=${y.presigns ? "yes" : "no"} publicUrl=${t ? "yes" : "no"} transforms=yes`, {
2797
+ provider: y.name,
2798
+ bucket: x,
2799
+ refStore: D ? "memory" : "custom"
2800
+ }), y.name === "memory" && process.env.NODE_ENV !== "test" && e.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.)"), !t && !y.presigns && e.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.");
2801
+ }),
2802
+ onDeactivate: () => b.void
2803
+ });
2804
+ };
2805
+ //#endregion
2806
+ export { U as STORAGE_BLOBS_TABLE, H as STORAGE_GRANTS_TABLE, V as STORAGE_REFS_TABLE, Te as STORAGE_USAGE_TABLE, e as StorageAccessDenied, t as StorageError, n as StorageRejected, r as StorageScanRejected, i as StorageService, ke as _voltroStorageBlobsTable, De as _voltroStorageGrantsTable, Ee as _voltroStorageRefsTable, Oe as _voltroStorageUsageTable, Bt as assetRef, Re as azureProvider, Mt as buildStorageService, ye as bytesToStream, vt as checkLimits, Lt as clamavScanner, Me as clearStorageBuffer, be as collectStream, ve as concatBytes, ht as contentTypeAllowed, kt as dataStoreGrantStore, Et as dataStoreRefStore, Le as databaseProvider, Ut as decodeDataUri, Pt as encodeInstream, zt as ffmpegTranscoder, Pe as filesystemProvider, xe as fromNodeReadable, Ce as getObjectStream, Ke as hashPassword, Dt as memoryGrantStore, Ne as memoryProvider, wt as memoryRefStore, Ft as parseClamavResponse, Be as parseTransformParams, Wt as putAvatar, we as putObjectStream, je as readStorageBuffer, St as refMatchesListQuery, Kt as resolveStorageProvider, Y as resolveStorageSecret, Fe as s3Provider, lt as signFinalizeTicket, at as signGrant, pt as signMultipartTicket, dt as signResumableTicket, st as signUploadTicket, _t as sniffMatches, Zt as storagePlugin, Nt as storageServiceLayer, Se as toNodeReadable, Ge as transformCached, Ue as transformImage, ut as verifyFinalizeTicket, ot as verifyGrant, mt as verifyMultipartTicket, qe as verifyPassword, ft as verifyResumableTicket, ct as verifyUploadTicket, Vt as withStorage };