@velajs/storage 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/dist/body-BMUcBeQ0.js +93 -0
  3. package/dist/body-BMUcBeQ0.js.map +1 -0
  4. package/dist/client/index.d.ts +49 -46
  5. package/dist/client/index.js +207 -228
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/drivers/memory/index.d.ts +17 -13
  8. package/dist/drivers/memory/index.js +190 -197
  9. package/dist/drivers/memory/index.js.map +1 -0
  10. package/dist/drivers/r2/index.d.ts +7 -4
  11. package/dist/drivers/r2/index.js +162 -160
  12. package/dist/drivers/r2/index.js.map +1 -0
  13. package/dist/drivers/r2-http/index.d.ts +23 -19
  14. package/dist/drivers/r2-http/index.js +52 -45
  15. package/dist/drivers/r2-http/index.js.map +1 -0
  16. package/dist/drivers/s3/index.d.ts +79 -5
  17. package/dist/drivers/s3/index.js +2 -3
  18. package/dist/index.d.ts +271 -17
  19. package/dist/index.js +1078 -16
  20. package/dist/index.js.map +1 -0
  21. package/dist/middleware/index.d.ts +177 -15
  22. package/dist/middleware/index.js +553 -8
  23. package/dist/middleware/index.js.map +1 -0
  24. package/dist/protocol.types-C5PPBD12.d.ts +80 -0
  25. package/dist/r2.types-Bw4Ft83t.d.ts +71 -0
  26. package/dist/retry-DXtO6R9D.js +119 -0
  27. package/dist/retry-DXtO6R9D.js.map +1 -0
  28. package/dist/s3-DrE9NrnD.js +557 -0
  29. package/dist/s3-DrE9NrnD.js.map +1 -0
  30. package/dist/storage.error-DVCOkJhc.js +49 -0
  31. package/dist/storage.error-DVCOkJhc.js.map +1 -0
  32. package/dist/storage.types-BJN7Yp1J.d.ts +288 -0
  33. package/dist/storagesdk/index.d.ts +73 -69
  34. package/dist/storagesdk/index.js +117 -130
  35. package/dist/storagesdk/index.js.map +1 -0
  36. package/dist/stored-file-2Yl07eRc.js +105 -0
  37. package/dist/stored-file-2Yl07eRc.js.map +1 -0
  38. package/dist/testing/index.d.ts +12 -8
  39. package/dist/testing/index.js +36 -53
  40. package/dist/testing/index.js.map +1 -0
  41. package/package.json +62 -48
  42. package/dist/base64.d.ts +0 -8
  43. package/dist/base64.js +0 -25
  44. package/dist/decorators/inject-storage.decorator.d.ts +0 -17
  45. package/dist/decorators/inject-storage.decorator.js +0 -21
  46. package/dist/drivers/r2/r2.types.d.ts +0 -67
  47. package/dist/drivers/r2/r2.types.js +0 -5
  48. package/dist/drivers/s3/s3-client.d.ts +0 -33
  49. package/dist/drivers/s3/s3-client.js +0 -154
  50. package/dist/drivers/s3/s3.driver.d.ts +0 -4
  51. package/dist/drivers/s3/s3.driver.js +0 -404
  52. package/dist/drivers/s3/s3.types.d.ts +0 -28
  53. package/dist/drivers/s3/s3.types.js +0 -1
  54. package/dist/drivers/s3/xml.d.ts +0 -6
  55. package/dist/drivers/s3/xml.js +0 -52
  56. package/dist/http/authorizer.types.d.ts +0 -59
  57. package/dist/http/authorizer.types.js +0 -1
  58. package/dist/http/http-helpers.d.ts +0 -22
  59. package/dist/http/http-helpers.js +0 -80
  60. package/dist/http/protocol.types.d.ts +0 -76
  61. package/dist/http/protocol.types.js +0 -1
  62. package/dist/internal/body.d.ts +0 -18
  63. package/dist/internal/body.js +0 -90
  64. package/dist/internal/retry.d.ts +0 -41
  65. package/dist/internal/retry.js +0 -127
  66. package/dist/internal/stored-file.d.ts +0 -27
  67. package/dist/internal/stored-file.js +0 -114
  68. package/dist/middleware/cache.d.ts +0 -50
  69. package/dist/middleware/cache.js +0 -101
  70. package/dist/middleware/compose.d.ts +0 -12
  71. package/dist/middleware/compose.js +0 -11
  72. package/dist/middleware/compression.d.ts +0 -16
  73. package/dist/middleware/compression.js +0 -101
  74. package/dist/middleware/encryption.d.ts +0 -14
  75. package/dist/middleware/encryption.js +0 -134
  76. package/dist/middleware/failover.d.ts +0 -18
  77. package/dist/middleware/failover.js +0 -52
  78. package/dist/middleware/retry.d.ts +0 -14
  79. package/dist/middleware/retry.js +0 -47
  80. package/dist/middleware/versioning.d.ts +0 -36
  81. package/dist/middleware/versioning.js +0 -89
  82. package/dist/middleware/wrap.d.ts +0 -11
  83. package/dist/middleware/wrap.js +0 -46
  84. package/dist/object-key.d.ts +0 -17
  85. package/dist/object-key.js +0 -42
  86. package/dist/storage.controller.d.ts +0 -21
  87. package/dist/storage.controller.js +0 -380
  88. package/dist/storage.error.d.ts +0 -28
  89. package/dist/storage.error.js +0 -46
  90. package/dist/storage.facade.d.ts +0 -47
  91. package/dist/storage.facade.js +0 -443
  92. package/dist/storage.module.d.ts +0 -46
  93. package/dist/storage.module.js +0 -113
  94. package/dist/storage.service.d.ts +0 -42
  95. package/dist/storage.service.js +0 -86
  96. package/dist/storage.tokens.d.ts +0 -13
  97. package/dist/storage.tokens.js +0 -26
  98. package/dist/storage.types.d.ts +0 -245
  99. package/dist/storage.types.js +0 -1
@@ -1,8 +1,553 @@
1
- export { compose } from "./compose.js";
2
- export { passthrough } from "./wrap.js";
3
- export { retry } from "./retry.js";
4
- export { failover } from "./failover.js";
5
- export { cache, MapCacheStore } from "./cache.js";
6
- export { encryption } from "./encryption.js";
7
- export { compression } from "./compression.js";
8
- export { versioning, unwrapVersioning, VERSIONED } from "./versioning.js";
1
+ import { a as isStream, o as toBytes, s as toStream, t as byteLengthOf } from "../body-BMUcBeQ0.js";
2
+ import { t as StorageError } from "../storage.error-DVCOkJhc.js";
3
+ import { n as runWithRetry } from "../retry-DXtO6R9D.js";
4
+ import { t as createStoredFile } from "../stored-file-2Yl07eRc.js";
5
+ //#region src/middleware/compose.ts
6
+ /**
7
+ * Compose middlewares over a base driver. Listed innermost → outermost:
8
+ * `compose(base, a, b, c)` yields `c(b(a(base)))`, so on any call `c` runs
9
+ * first (outermost) and `base` last. The same wrapper handles read and write,
10
+ * so the reverse order on download comes for free.
11
+ *
12
+ * There is deliberately NO default preset — each middleware is opt-in, so you
13
+ * never accidentally ship, e.g., compress-then-encrypt (a CRIME/BREACH vector).
14
+ */
15
+ function compose(base, ...middlewares) {
16
+ return middlewares.reduce((driver, wrap) => wrap(driver), base);
17
+ }
18
+ //#endregion
19
+ //#region src/middleware/wrap.ts
20
+ /**
21
+ * Build a driver that delegates to `inner`, applying `over` overrides and
22
+ * omitting the methods in `omit`. Explicit delegation (not spread) keeps
23
+ * class-based drivers' `this`. Optional methods are forwarded only when the
24
+ * inner driver has them (so `presence == capability` is preserved), unless
25
+ * omitted (e.g. a body-transforming middleware suppresses multipart/presign).
26
+ */
27
+ function passthrough(inner, over = {}, omit = []) {
28
+ const omitted = new Set(omit);
29
+ const has = (k) => !omitted.has(k);
30
+ const d = {
31
+ name: over.name ?? inner.name,
32
+ raw: inner.raw,
33
+ reportsUploadProgress: over.reportsUploadProgress ?? inner.reportsUploadProgress,
34
+ supportsRange: over.supportsRange ?? inner.supportsRange,
35
+ supportsDelimiter: over.supportsDelimiter ?? inner.supportsDelimiter,
36
+ supportsMetadata: over.supportsMetadata ?? inner.supportsMetadata,
37
+ supportsCacheControl: over.supportsCacheControl ?? inner.supportsCacheControl,
38
+ supportsServerSideCopy: over.supportsServerSideCopy ?? inner.supportsServerSideCopy,
39
+ signedUrl: over.signedUrl ?? inner.signedUrl,
40
+ upload: over.upload ?? ((k, b, o) => inner.upload(k, b, o)),
41
+ download: over.download ?? ((k, o) => inner.download(k, o)),
42
+ head: over.head ?? ((k, o) => inner.head(k, o)),
43
+ exists: over.exists ?? ((k, o) => inner.exists(k, o)),
44
+ delete: over.delete ?? ((k, o) => inner.delete(k, o)),
45
+ copy: over.copy ?? ((f, t, o) => inner.copy(f, t, o)),
46
+ list: over.list ?? ((o) => inner.list(o)),
47
+ url: over.url ?? ((k, o) => inner.url(k, o)),
48
+ signedUploadUrl: over.signedUploadUrl ?? ((k, o) => inner.signedUploadUrl(k, o))
49
+ };
50
+ if (has("deleteMany") && (over.deleteMany || inner.deleteMany)) d.deleteMany = over.deleteMany ?? ((ks, o) => inner.deleteMany(ks, o));
51
+ if (has("move") && (over.move || inner.move)) d.move = over.move ?? ((f, t, o) => inner.move(f, t, o));
52
+ if (has("createMultipartUpload") && (over.createMultipartUpload || inner.createMultipartUpload)) d.createMultipartUpload = over.createMultipartUpload ?? ((k, o) => inner.createMultipartUpload(k, o));
53
+ if (has("resumeMultipartUpload") && (over.resumeMultipartUpload || inner.resumeMultipartUpload)) d.resumeMultipartUpload = over.resumeMultipartUpload ?? ((k, u) => inner.resumeMultipartUpload(k, u));
54
+ if (has("signedMultipart") && (over.signedMultipart || inner.signedMultipart)) d.signedMultipart = over.signedMultipart ?? inner.signedMultipart;
55
+ return d;
56
+ }
57
+ //#endregion
58
+ //#region src/middleware/retry.ts
59
+ /**
60
+ * Retry transient failures at the driver level. Reads and idempotent ops
61
+ * always retry; an upload retries only when its body is replayable (not a
62
+ * one-shot stream). Note: the `Storage` facade also retries per
63
+ * `OperationOptions.retries` — use this middleware when driving a driver
64
+ * directly, or set the facade's retries to 0 to avoid compounding.
65
+ */
66
+ function retry(opts = {}) {
67
+ const base = opts.retries ?? 3;
68
+ const run = (o, fn) => runWithRetry(fn, {
69
+ retries: o?.retries ?? base,
70
+ timeout: o?.timeout,
71
+ signal: o?.signal
72
+ });
73
+ return (inner) => passthrough(inner, {
74
+ download: (k, o) => run(o, (s) => inner.download(k, {
75
+ ...o,
76
+ signal: s
77
+ })),
78
+ head: (k, o) => run(o, (s) => inner.head(k, {
79
+ ...o,
80
+ signal: s
81
+ })),
82
+ exists: (k, o) => run(o, (s) => inner.exists(k, {
83
+ ...o,
84
+ signal: s
85
+ })),
86
+ list: (o) => run(o, (s) => inner.list({
87
+ ...o,
88
+ signal: s
89
+ })),
90
+ delete: (k, o) => run(o, (s) => inner.delete(k, {
91
+ ...o,
92
+ signal: s
93
+ })),
94
+ copy: (f, t, o) => run(o, (s) => inner.copy(f, t, {
95
+ ...o,
96
+ signal: s
97
+ })),
98
+ upload: (k, b, o) => isStream(b) ? inner.upload(k, b, o) : run(o, (s) => inner.upload(k, b, {
99
+ ...o,
100
+ signal: s
101
+ }))
102
+ });
103
+ }
104
+ //#endregion
105
+ //#region src/middleware/failover.ts
106
+ function isTransient(error) {
107
+ return error instanceof StorageError ? error.retryable : true;
108
+ }
109
+ function intersectCapabilities(chain) {
110
+ return {
111
+ supportsRange: chain.every((d) => !!d.supportsRange),
112
+ supportsDelimiter: chain.every((d) => !!d.supportsDelimiter),
113
+ supportsMetadata: chain.every((d) => !!d.supportsMetadata),
114
+ supportsCacheControl: chain.every((d) => !!d.supportsCacheControl),
115
+ supportsServerSideCopy: chain.every((d) => !!d.supportsServerSideCopy)
116
+ };
117
+ }
118
+ /**
119
+ * Read-failover across a primary + fallbacks. Reads try each driver in order
120
+ * until one succeeds; writes and presigning go to the primary only (avoids
121
+ * partial/divergent writes). Capabilities are the intersection so the facade
122
+ * never routes a feature to a driver that can't serve it.
123
+ */
124
+ function failover(fallbacks, opts = {}) {
125
+ const should = opts.shouldFailover ?? isTransient;
126
+ return (primary) => {
127
+ const chain = [primary, ...fallbacks];
128
+ const read = async (fn) => {
129
+ let lastError;
130
+ for (let i = 0; i < chain.length; i++) try {
131
+ return await fn(chain[i]);
132
+ } catch (e) {
133
+ lastError = e;
134
+ if (!should(e) || i === chain.length - 1) throw e;
135
+ opts.onFailover?.({
136
+ from: i,
137
+ to: i + 1,
138
+ error: e
139
+ });
140
+ }
141
+ throw lastError;
142
+ };
143
+ return passthrough(primary, {
144
+ ...intersectCapabilities(chain),
145
+ download: (k, o) => read((d) => d.download(k, o)),
146
+ head: (k, o) => read((d) => d.head(k, o)),
147
+ exists: (k, o) => read((d) => d.exists(k, o)),
148
+ list: (o) => read((d) => d.list(o))
149
+ });
150
+ };
151
+ }
152
+ //#endregion
153
+ //#region src/middleware/cache.ts
154
+ /** In-memory, per-isolate cache with lazy TTL + insertion-order eviction (no timers). */
155
+ var MapCacheStore = class {
156
+ maxEntries;
157
+ now;
158
+ #map = /* @__PURE__ */ new Map();
159
+ constructor(maxEntries = 1e3, now = Date.now) {
160
+ this.maxEntries = maxEntries;
161
+ this.now = now;
162
+ }
163
+ get(key) {
164
+ const e = this.#map.get(key);
165
+ if (!e) return void 0;
166
+ if (e.expiresAt <= this.now()) {
167
+ this.#map.delete(key);
168
+ return;
169
+ }
170
+ return e;
171
+ }
172
+ set(key, value) {
173
+ this.#map.delete(key);
174
+ this.#map.set(key, value);
175
+ if (this.#map.size > this.maxEntries) {
176
+ const oldest = this.#map.keys().next().value;
177
+ if (oldest !== void 0) this.#map.delete(oldest);
178
+ }
179
+ }
180
+ delete(key) {
181
+ this.#map.delete(key);
182
+ }
183
+ };
184
+ function toMeta(f) {
185
+ return {
186
+ key: f.key,
187
+ name: f.name,
188
+ size: f.size,
189
+ type: f.type,
190
+ lastModified: f.lastModified,
191
+ etag: f.etag,
192
+ metadata: f.metadata
193
+ };
194
+ }
195
+ /**
196
+ * Read-through cache for `head` / `exists`. Bodies are never cached (a `head`
197
+ * hit still lazily fetches on body access), and `list` is not cached
198
+ * (cross-prefix invalidation is intractable). Writes invalidate the key.
199
+ */
200
+ function cache(opts = {}) {
201
+ const now = opts.now ?? Date.now;
202
+ const store = opts.store ?? new MapCacheStore(1e3, now);
203
+ const ttl = opts.ttlMs ?? 6e4;
204
+ const doHead = opts.cache?.head ?? true;
205
+ const doExists = opts.cache?.exists ?? true;
206
+ const ns = (k) => `${opts.keyPrefix ?? ""}${k}`;
207
+ return (inner) => {
208
+ const invalidate = async (...keys) => {
209
+ await Promise.all(keys.map((k) => store.delete(ns(k))));
210
+ };
211
+ return passthrough(inner, {
212
+ head: doHead ? async (k, o) => {
213
+ const hit = await store.get(ns(k));
214
+ if (hit?.kind === "meta" && hit.meta) return createStoredFile(hit.meta, {
215
+ kind: "lazy",
216
+ fetch: async () => new Response((await inner.download(k)).stream())
217
+ });
218
+ const file = await inner.head(k, o);
219
+ await store.set(ns(k), {
220
+ kind: "meta",
221
+ meta: toMeta(file),
222
+ expiresAt: now() + ttl
223
+ }, ttl);
224
+ return file;
225
+ } : void 0,
226
+ exists: doExists ? async (k, o) => {
227
+ const hit = await store.get(ns(k));
228
+ if (hit?.kind === "exists" && typeof hit.exists === "boolean") return hit.exists;
229
+ const exists = await inner.exists(k, o);
230
+ await store.set(ns(k), {
231
+ kind: "exists",
232
+ exists,
233
+ expiresAt: now() + ttl
234
+ }, ttl);
235
+ return exists;
236
+ } : void 0,
237
+ upload: async (k, b, o) => {
238
+ const r = await inner.upload(k, b, o);
239
+ await invalidate(k);
240
+ return r;
241
+ },
242
+ delete: async (k, o) => {
243
+ await inner.delete(k, o);
244
+ await invalidate(k);
245
+ },
246
+ copy: async (f, t, o) => {
247
+ await inner.copy(f, t, o);
248
+ await invalidate(t);
249
+ }
250
+ });
251
+ };
252
+ }
253
+ //#endregion
254
+ //#region src/middleware/encryption.ts
255
+ const IV_BYTES = 12;
256
+ const asBuf = (u) => u;
257
+ function stripVela$1(meta) {
258
+ if (!meta) return void 0;
259
+ const out = {};
260
+ for (const [k, v] of Object.entries(meta)) if (!k.startsWith("vela-")) out[k] = v;
261
+ return Object.keys(out).length ? out : void 0;
262
+ }
263
+ async function importKey(key) {
264
+ if (key instanceof Uint8Array) return crypto.subtle.importKey("raw", key, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
265
+ return key;
266
+ }
267
+ /**
268
+ * Transparent client-side encryption via Web Crypto AES-GCM. v1 encrypts the
269
+ * WHOLE object (buffered); the stored object is `IV(12) || ciphertext+tag`.
270
+ * Range reads, presigned URLs, and multipart are disabled (a signed URL would
271
+ * hand out ciphertext / accept plaintext, defeating the invariant).
272
+ */
273
+ function encryption(opts) {
274
+ const keyPromise = importKey(opts.key);
275
+ return (inner) => {
276
+ const decryptDownload = async (k, _o) => {
277
+ const file = await inner.download(k);
278
+ const framed = new Uint8Array(await file.arrayBuffer());
279
+ if (framed.byteLength < IV_BYTES) {
280
+ if (opts.onInvalid === "passthrough") return file;
281
+ throw new StorageError("Parse", `object too small to be encrypted: ${k}`);
282
+ }
283
+ const iv = framed.subarray(0, IV_BYTES);
284
+ const ciphertext = framed.subarray(IV_BYTES);
285
+ const key = await keyPromise;
286
+ let plain;
287
+ try {
288
+ plain = new Uint8Array(await crypto.subtle.decrypt({
289
+ name: "AES-GCM",
290
+ iv: asBuf(iv)
291
+ }, key, asBuf(ciphertext)));
292
+ } catch (e) {
293
+ if (opts.onInvalid === "passthrough") return file;
294
+ throw new StorageError("Provider", `decryption failed: ${k}`, { cause: e });
295
+ }
296
+ return createStoredFile({
297
+ key: k,
298
+ name: file.name,
299
+ size: plain.byteLength,
300
+ type: file.metadata?.["vela-ct"] ?? file.type,
301
+ lastModified: file.lastModified,
302
+ etag: file.etag,
303
+ metadata: stripVela$1(file.metadata)
304
+ }, {
305
+ kind: "bytes",
306
+ bytes: plain
307
+ });
308
+ };
309
+ return passthrough(inner, {
310
+ reportsUploadProgress: false,
311
+ supportsRange: false,
312
+ signedUrl: {
313
+ supported: false,
314
+ upload: false
315
+ },
316
+ async upload(k, body, o) {
317
+ const plaintext = await toBytes(body);
318
+ const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
319
+ const key = await keyPromise;
320
+ const ct = new Uint8Array(await crypto.subtle.encrypt({
321
+ name: "AES-GCM",
322
+ iv
323
+ }, key, asBuf(plaintext)));
324
+ const framed = new Uint8Array(IV_BYTES + ct.byteLength);
325
+ framed.set(iv, 0);
326
+ framed.set(ct, IV_BYTES);
327
+ const metadata = {
328
+ ...o?.metadata,
329
+ "vela-enc": "AES-GCM"
330
+ };
331
+ if (o?.contentType) metadata["vela-ct"] = o.contentType;
332
+ metadata["vela-size"] = String(plaintext.byteLength);
333
+ return {
334
+ ...await inner.upload(k, framed, {
335
+ ...o,
336
+ metadata: inner.supportsMetadata ? metadata : void 0,
337
+ contentType: "application/octet-stream"
338
+ }),
339
+ size: plaintext.byteLength,
340
+ contentType: o?.contentType ?? "application/octet-stream"
341
+ };
342
+ },
343
+ download: decryptDownload,
344
+ async head(k, o) {
345
+ const file = await inner.head(k, o);
346
+ const size = file.metadata?.["vela-size"] ? Number(file.metadata["vela-size"]) : file.size;
347
+ return createStoredFile({
348
+ key: k,
349
+ name: file.name,
350
+ size,
351
+ type: file.metadata?.["vela-ct"] ?? file.type,
352
+ lastModified: file.lastModified,
353
+ etag: file.etag,
354
+ metadata: stripVela$1(file.metadata)
355
+ }, {
356
+ kind: "lazy",
357
+ fetch: async () => new Response((await decryptDownload(k)).stream())
358
+ });
359
+ },
360
+ url() {
361
+ throw new StorageError("Unsupported", "encryption: url() would expose ciphertext");
362
+ },
363
+ signedUploadUrl() {
364
+ throw new StorageError("Unsupported", "encryption: signed uploads would store plaintext");
365
+ }
366
+ }, [
367
+ "createMultipartUpload",
368
+ "resumeMultipartUpload",
369
+ "signedMultipart"
370
+ ]);
371
+ };
372
+ }
373
+ //#endregion
374
+ //#region src/middleware/compression.ts
375
+ function stripVela(meta) {
376
+ if (!meta) return void 0;
377
+ const out = {};
378
+ for (const [k, v] of Object.entries(meta)) if (!k.startsWith("vela-")) out[k] = v;
379
+ return Object.keys(out).length ? out : void 0;
380
+ }
381
+ /**
382
+ * Transparent gzip/deflate via Web Streams `CompressionStream` — fully
383
+ * streaming, opaque mode (v1). The stored object is compressed and tagged
384
+ * `vela-zip`; reads detect the tag and decompress, so pre-existing (untagged)
385
+ * objects pass through unharmed. Range reads / presigned URLs are disabled
386
+ * (the byte stream is not seekable and a direct URL would serve gzip).
387
+ */
388
+ function compression(opts = {}) {
389
+ const format = opts.format ?? "gzip";
390
+ const available = typeof CompressionStream !== "undefined" && typeof DecompressionStream !== "undefined";
391
+ if (!available && (opts.onUnavailable ?? "throw") === "throw") throw new StorageError("Unsupported", "CompressionStream is not available in this runtime");
392
+ return (inner) => {
393
+ if (!available) return inner;
394
+ const decompressDownload = async (k, _o) => {
395
+ const file = await inner.download(k);
396
+ const zip = file.metadata?.["vela-zip"];
397
+ if (!zip) return file;
398
+ const plain = file.stream().pipeThrough(new DecompressionStream(zip));
399
+ const size = file.metadata?.["vela-size"] ? Number(file.metadata["vela-size"]) : file.size;
400
+ return createStoredFile({
401
+ key: k,
402
+ name: file.name,
403
+ size,
404
+ type: file.metadata?.["vela-ct"] ?? file.type,
405
+ lastModified: file.lastModified,
406
+ etag: file.etag,
407
+ metadata: stripVela(file.metadata)
408
+ }, {
409
+ kind: "stream",
410
+ stream: plain
411
+ });
412
+ };
413
+ return passthrough(inner, {
414
+ reportsUploadProgress: false,
415
+ supportsRange: false,
416
+ signedUrl: {
417
+ supported: false,
418
+ upload: false
419
+ },
420
+ async upload(k, body, o) {
421
+ const zipped = toStream(body).pipeThrough(new CompressionStream(format));
422
+ const metadata = {
423
+ ...o?.metadata,
424
+ "vela-zip": format
425
+ };
426
+ if (o?.contentType) metadata["vela-ct"] = o.contentType;
427
+ const known = byteLengthOf(body);
428
+ if (known != null) metadata["vela-size"] = String(known);
429
+ const r = await inner.upload(k, zipped, {
430
+ ...o,
431
+ metadata: inner.supportsMetadata ? metadata : void 0,
432
+ contentType: "application/octet-stream"
433
+ });
434
+ return {
435
+ ...r,
436
+ size: known ?? r.size,
437
+ contentType: o?.contentType ?? "application/octet-stream"
438
+ };
439
+ },
440
+ download: decompressDownload,
441
+ async head(k, o) {
442
+ const file = await inner.head(k, o);
443
+ if (!file.metadata?.["vela-zip"]) return file;
444
+ const size = file.metadata["vela-size"] ? Number(file.metadata["vela-size"]) : file.size;
445
+ return createStoredFile({
446
+ key: k,
447
+ name: file.name,
448
+ size,
449
+ type: file.metadata["vela-ct"] ?? file.type,
450
+ lastModified: file.lastModified,
451
+ etag: file.etag,
452
+ metadata: stripVela(file.metadata)
453
+ }, {
454
+ kind: "lazy",
455
+ fetch: async () => new Response((await decompressDownload(k)).stream())
456
+ });
457
+ },
458
+ url() {
459
+ throw new StorageError("Unsupported", "compression: url() would serve compressed bytes");
460
+ },
461
+ signedUploadUrl() {
462
+ throw new StorageError("Unsupported", "compression: signed uploads would store uncompressed");
463
+ }
464
+ }, [
465
+ "createMultipartUpload",
466
+ "resumeMultipartUpload",
467
+ "signedMultipart"
468
+ ]);
469
+ };
470
+ }
471
+ //#endregion
472
+ //#region src/middleware/versioning.ts
473
+ const VERSIONED = Symbol.for("vela.storage.versioned");
474
+ /**
475
+ * Keeps prior versions on overwrite via key-suffixing (no external manifest →
476
+ * no read-modify-write race). Snapshots are cheap when the base driver
477
+ * `supportsServerSideCopy`. Direct presigned uploads bypass snapshotting, so
478
+ * `signedUploadUrl` throws. Reach `listVersions`/`restore`/`deleteVersion` via
479
+ * {@link unwrapVersioning}.
480
+ */
481
+ function versioning(opts = {}) {
482
+ const prefix = opts.prefix ?? ".vela-versions/";
483
+ let seq = 0;
484
+ const vid = opts.versionId ?? (() => `${Date.now().toString(36)}-${(seq++).toString(36)}`);
485
+ const vkey = (key, id) => `${prefix}${key}/${id}`;
486
+ const isVersion = (key) => key.startsWith(prefix);
487
+ const listVersions = async (inner, key) => {
488
+ return (await inner.list({ prefix: `${prefix}${key}/` })).items.map((item) => ({
489
+ versionId: item.key.slice(item.key.lastIndexOf("/") + 1),
490
+ key: item.key,
491
+ size: item.size,
492
+ lastModified: item.lastModified
493
+ }));
494
+ };
495
+ const snapshot = async (inner, key) => {
496
+ if (!await inner.exists(key)) return;
497
+ const id = vid();
498
+ if (inner.supportsServerSideCopy) await inner.copy(key, vkey(key, id));
499
+ else {
500
+ const current = await inner.download(key);
501
+ await inner.upload(vkey(key, id), current.stream(), { contentType: current.type });
502
+ }
503
+ if (opts.maxVersions) {
504
+ const versions = (await listVersions(inner, key)).sort((a, b) => a.versionId < b.versionId ? -1 : 1);
505
+ for (const v of versions.slice(0, Math.max(0, versions.length - opts.maxVersions))) await inner.delete(v.key);
506
+ }
507
+ };
508
+ return (inner) => {
509
+ const driver = passthrough(inner, {
510
+ async upload(k, b, o) {
511
+ await snapshot(inner, k);
512
+ return inner.upload(k, b, o);
513
+ },
514
+ async copy(f, t, o) {
515
+ await snapshot(inner, t);
516
+ return inner.copy(f, t, o);
517
+ },
518
+ async delete(k, o) {
519
+ if (opts.onDelete !== "purge") await snapshot(inner, k);
520
+ await inner.delete(k, o);
521
+ if (opts.onDelete === "purge") for (const v of await listVersions(inner, k)) await inner.delete(v.key);
522
+ },
523
+ list: opts.hideFromList === false ? void 0 : async (o) => {
524
+ const res = await inner.list(o);
525
+ return {
526
+ items: res.items.filter((i) => !isVersion(i.key)),
527
+ prefixes: res.prefixes?.filter((p) => !isVersion(p)),
528
+ cursor: res.cursor
529
+ };
530
+ },
531
+ signedUploadUrl() {
532
+ throw new StorageError("Unsupported", "versioning: direct uploads bypass snapshots");
533
+ }
534
+ });
535
+ const versioned = {
536
+ listVersions: (key) => listVersions(inner, key),
537
+ async restore(key, versionId) {
538
+ await snapshot(inner, key);
539
+ await inner.copy(vkey(key, versionId), key);
540
+ },
541
+ deleteVersion: (key, versionId) => inner.delete(vkey(key, versionId))
542
+ };
543
+ return Object.assign(driver, { [VERSIONED]: versioned });
544
+ };
545
+ }
546
+ /** Retrieve the {@link Versioned} surface from a versioning-wrapped driver (or `undefined`). */
547
+ function unwrapVersioning(driver) {
548
+ return driver[VERSIONED];
549
+ }
550
+ //#endregion
551
+ export { MapCacheStore, VERSIONED, cache, compose, compression, encryption, failover, passthrough, retry, unwrapVersioning, versioning };
552
+
553
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["#map","stripVela"],"sources":["../../src/middleware/compose.ts","../../src/middleware/wrap.ts","../../src/middleware/retry.ts","../../src/middleware/failover.ts","../../src/middleware/cache.ts","../../src/middleware/encryption.ts","../../src/middleware/compression.ts","../../src/middleware/versioning.ts"],"sourcesContent":["import type { StorageDriver } from '../storage.types';\nimport type { Middleware } from './wrap';\n\n/**\n * Compose middlewares over a base driver. Listed innermost → outermost:\n * `compose(base, a, b, c)` yields `c(b(a(base)))`, so on any call `c` runs\n * first (outermost) and `base` last. The same wrapper handles read and write,\n * so the reverse order on download comes for free.\n *\n * There is deliberately NO default preset — each middleware is opt-in, so you\n * never accidentally ship, e.g., compress-then-encrypt (a CRIME/BREACH vector).\n */\nexport function compose(base: StorageDriver, ...middlewares: Middleware[]): StorageDriver {\n return middlewares.reduce((driver, wrap) => wrap(driver), base);\n}\n","import type { StorageDriver } from '../storage.types';\n\n/** A driver middleware: wraps a driver, returns a driver. */\nexport type Middleware = (inner: StorageDriver) => StorageDriver;\n\n/**\n * Build a driver that delegates to `inner`, applying `over` overrides and\n * omitting the methods in `omit`. Explicit delegation (not spread) keeps\n * class-based drivers' `this`. Optional methods are forwarded only when the\n * inner driver has them (so `presence == capability` is preserved), unless\n * omitted (e.g. a body-transforming middleware suppresses multipart/presign).\n */\nexport function passthrough(\n inner: StorageDriver,\n over: Partial<StorageDriver> = {},\n omit: ReadonlyArray<keyof StorageDriver> = [],\n): StorageDriver {\n const omitted = new Set<keyof StorageDriver>(omit);\n const has = (k: keyof StorageDriver) => !omitted.has(k);\n\n const d: StorageDriver = {\n name: over.name ?? inner.name,\n raw: inner.raw,\n reportsUploadProgress: over.reportsUploadProgress ?? inner.reportsUploadProgress,\n supportsRange: over.supportsRange ?? inner.supportsRange,\n supportsDelimiter: over.supportsDelimiter ?? inner.supportsDelimiter,\n supportsMetadata: over.supportsMetadata ?? inner.supportsMetadata,\n supportsCacheControl: over.supportsCacheControl ?? inner.supportsCacheControl,\n supportsServerSideCopy: over.supportsServerSideCopy ?? inner.supportsServerSideCopy,\n signedUrl: over.signedUrl ?? inner.signedUrl,\n upload: over.upload ?? ((k, b, o) => inner.upload(k, b, o)),\n download: over.download ?? ((k, o) => inner.download(k, o)),\n head: over.head ?? ((k, o) => inner.head(k, o)),\n exists: over.exists ?? ((k, o) => inner.exists(k, o)),\n delete: over.delete ?? ((k, o) => inner.delete(k, o)),\n copy: over.copy ?? ((f, t, o) => inner.copy(f, t, o)),\n list: over.list ?? ((o) => inner.list(o)),\n url: over.url ?? ((k, o) => inner.url(k, o)),\n signedUploadUrl: over.signedUploadUrl ?? ((k, o) => inner.signedUploadUrl(k, o)),\n };\n\n if (has('deleteMany') && (over.deleteMany || inner.deleteMany)) {\n d.deleteMany = over.deleteMany ?? ((ks, o) => inner.deleteMany!(ks, o));\n }\n if (has('move') && (over.move || inner.move)) {\n d.move = over.move ?? ((f, t, o) => inner.move!(f, t, o));\n }\n if (has('createMultipartUpload') && (over.createMultipartUpload || inner.createMultipartUpload)) {\n d.createMultipartUpload =\n over.createMultipartUpload ?? ((k, o) => inner.createMultipartUpload!(k, o));\n }\n if (has('resumeMultipartUpload') && (over.resumeMultipartUpload || inner.resumeMultipartUpload)) {\n d.resumeMultipartUpload =\n over.resumeMultipartUpload ?? ((k, u) => inner.resumeMultipartUpload!(k, u));\n }\n if (has('signedMultipart') && (over.signedMultipart || inner.signedMultipart)) {\n d.signedMultipart = over.signedMultipart ?? inner.signedMultipart;\n }\n return d;\n}\n","import { isStream } from '../internal/body';\nimport { runWithRetry } from '../internal/retry';\nimport type { OperationOptions, RetryOptions } from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport interface RetryMiddlewareOptions {\n /** Default retry policy for driver operations. Per-call `opts.retries` wins. */\n retries?: RetryOptions;\n}\n\n/**\n * Retry transient failures at the driver level. Reads and idempotent ops\n * always retry; an upload retries only when its body is replayable (not a\n * one-shot stream). Note: the `Storage` facade also retries per\n * `OperationOptions.retries` — use this middleware when driving a driver\n * directly, or set the facade's retries to 0 to avoid compounding.\n */\nexport function retry(opts: RetryMiddlewareOptions = {}): Middleware {\n const base = opts.retries ?? 3;\n const run = <T>(o: OperationOptions | undefined, fn: (s?: AbortSignal) => Promise<T>) =>\n runWithRetry(fn, { retries: o?.retries ?? base, timeout: o?.timeout, signal: o?.signal });\n\n return (inner) =>\n passthrough(inner, {\n download: (k, o) => run(o, (s) => inner.download(k, { ...o, signal: s })),\n head: (k, o) => run(o, (s) => inner.head(k, { ...o, signal: s })),\n exists: (k, o) => run(o, (s) => inner.exists(k, { ...o, signal: s })),\n list: (o) => run(o, (s) => inner.list({ ...o, signal: s })),\n delete: (k, o) => run(o, (s) => inner.delete(k, { ...o, signal: s })),\n copy: (f, t, o) => run(o, (s) => inner.copy(f, t, { ...o, signal: s })),\n upload: (k, b, o) =>\n isStream(b)\n ? inner.upload(k, b, o)\n : run(o, (s) => inner.upload(k, b, { ...o, signal: s })),\n });\n}\n","import { StorageError } from '../storage.error';\nimport type { StorageDriver } from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport interface FailoverOptions {\n /** Decide whether an error should trigger failover. Default: transient errors. */\n shouldFailover?: (error: unknown) => boolean;\n onFailover?: (info: { from: number; to: number; error: unknown }) => void;\n}\n\nfunction isTransient(error: unknown): boolean {\n return error instanceof StorageError ? error.retryable : true;\n}\n\nfunction intersectCapabilities(chain: StorageDriver[]): Partial<StorageDriver> {\n return {\n supportsRange: chain.every((d) => !!d.supportsRange),\n supportsDelimiter: chain.every((d) => !!d.supportsDelimiter),\n supportsMetadata: chain.every((d) => !!d.supportsMetadata),\n supportsCacheControl: chain.every((d) => !!d.supportsCacheControl),\n supportsServerSideCopy: chain.every((d) => !!d.supportsServerSideCopy),\n };\n}\n\n/**\n * Read-failover across a primary + fallbacks. Reads try each driver in order\n * until one succeeds; writes and presigning go to the primary only (avoids\n * partial/divergent writes). Capabilities are the intersection so the facade\n * never routes a feature to a driver that can't serve it.\n */\nexport function failover(fallbacks: StorageDriver[], opts: FailoverOptions = {}): Middleware {\n const should = opts.shouldFailover ?? isTransient;\n return (primary) => {\n const chain = [primary, ...fallbacks];\n const read = async <T>(fn: (d: StorageDriver) => Promise<T>): Promise<T> => {\n let lastError: unknown;\n for (let i = 0; i < chain.length; i++) {\n try {\n return await fn(chain[i]!);\n } catch (e) {\n lastError = e;\n if (!should(e) || i === chain.length - 1) throw e;\n opts.onFailover?.({ from: i, to: i + 1, error: e });\n }\n }\n throw lastError;\n };\n return passthrough(primary, {\n ...intersectCapabilities(chain),\n download: (k, o) => read((d) => d.download(k, o)),\n head: (k, o) => read((d) => d.head(k, o)),\n exists: (k, o) => read((d) => d.exists(k, o)),\n list: (o) => read((d) => d.list(o)),\n });\n };\n}\n","import { createStoredFile } from '../internal/stored-file';\nimport type { OperationOptions, StorageDriver, StoredFile } from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport interface CachedMeta {\n key: string;\n name: string;\n size: number;\n type: string;\n lastModified?: number;\n etag?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface CacheEntry {\n expiresAt: number;\n kind: 'meta' | 'exists';\n meta?: CachedMeta;\n exists?: boolean;\n}\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport interface CacheStore {\n get(key: string): MaybePromise<CacheEntry | undefined>;\n set(key: string, value: CacheEntry, ttlMs: number): MaybePromise<void>;\n delete(key: string): MaybePromise<void>;\n}\n\n/** In-memory, per-isolate cache with lazy TTL + insertion-order eviction (no timers). */\nexport class MapCacheStore implements CacheStore {\n #map = new Map<string, CacheEntry>();\n constructor(\n private readonly maxEntries = 1000,\n private readonly now: () => number = Date.now,\n ) {}\n get(key: string): CacheEntry | undefined {\n const e = this.#map.get(key);\n if (!e) return undefined;\n if (e.expiresAt <= this.now()) {\n this.#map.delete(key);\n return undefined;\n }\n return e;\n }\n set(key: string, value: CacheEntry): void {\n this.#map.delete(key);\n this.#map.set(key, value);\n if (this.#map.size > this.maxEntries) {\n const oldest = this.#map.keys().next().value;\n if (oldest !== undefined) this.#map.delete(oldest);\n }\n }\n delete(key: string): void {\n this.#map.delete(key);\n }\n}\n\nexport interface CacheOptions {\n store?: CacheStore;\n ttlMs?: number;\n /** Which reads to cache. Default: head + exists (body/list are not cached). */\n cache?: { head?: boolean; exists?: boolean };\n keyPrefix?: string;\n now?: () => number;\n}\n\nfunction toMeta(f: StoredFile): CachedMeta {\n return {\n key: f.key,\n name: f.name,\n size: f.size,\n type: f.type,\n lastModified: f.lastModified,\n etag: f.etag,\n metadata: f.metadata,\n };\n}\n\n/**\n * Read-through cache for `head` / `exists`. Bodies are never cached (a `head`\n * hit still lazily fetches on body access), and `list` is not cached\n * (cross-prefix invalidation is intractable). Writes invalidate the key.\n */\nexport function cache(opts: CacheOptions = {}): Middleware {\n const now = opts.now ?? Date.now;\n const store = opts.store ?? new MapCacheStore(1000, now);\n const ttl = opts.ttlMs ?? 60_000;\n const doHead = opts.cache?.head ?? true;\n const doExists = opts.cache?.exists ?? true;\n const ns = (k: string) => `${opts.keyPrefix ?? ''}${k}`;\n\n return (inner) => {\n const invalidate = async (...keys: string[]) => {\n await Promise.all(keys.map((k) => store.delete(ns(k))));\n };\n return passthrough(inner, {\n head: doHead\n ? async (k: string, o?: OperationOptions): Promise<StoredFile> => {\n const hit = await store.get(ns(k));\n if (hit?.kind === 'meta' && hit.meta) {\n return createStoredFile(hit.meta, {\n kind: 'lazy',\n fetch: async () => new Response((await inner.download(k)).stream()),\n });\n }\n const file = await inner.head(k, o);\n await store.set(\n ns(k),\n { kind: 'meta', meta: toMeta(file), expiresAt: now() + ttl },\n ttl,\n );\n return file;\n }\n : undefined,\n exists: doExists\n ? async (k: string, o?: OperationOptions): Promise<boolean> => {\n const hit = await store.get(ns(k));\n if (hit?.kind === 'exists' && typeof hit.exists === 'boolean') return hit.exists;\n const exists = await inner.exists(k, o);\n await store.set(ns(k), { kind: 'exists', exists, expiresAt: now() + ttl }, ttl);\n return exists;\n }\n : undefined,\n upload: async (k, b, o) => {\n const r = await inner.upload(k, b, o);\n await invalidate(k);\n return r;\n },\n delete: async (k, o) => {\n await inner.delete(k, o);\n await invalidate(k);\n },\n copy: async (f, t, o) => {\n await inner.copy(f, t, o);\n await invalidate(t);\n },\n } satisfies Partial<StorageDriver>);\n };\n}\n","import { toBytes } from '../internal/body';\nimport { createStoredFile } from '../internal/stored-file';\nimport { StorageError } from '../storage.error';\nimport type {\n DownloadOptions,\n OperationOptions,\n StoredFile,\n UploadOptions,\n} from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport interface EncryptionOptions {\n /** A 256-bit AES-GCM key: a `CryptoKey` or 32 raw bytes. */\n key: CryptoKey | Uint8Array;\n /** How to treat objects that aren't valid ciphertext (legacy data). Default `throw`. */\n onInvalid?: 'throw' | 'passthrough';\n}\n\nconst IV_BYTES = 12;\n\n// The Web Crypto typings pin BufferSource to ArrayBuffer-backed views; our byte\n// views are ArrayBufferLike-typed. They are ArrayBuffer-backed at runtime, so a\n// narrowing cast is safe and confined to this helper.\nconst asBuf = (u: Uint8Array): BufferSource => u as unknown as BufferSource;\n\nfunction stripVela(meta: Record<string, string> | undefined): Record<string, string> | undefined {\n if (!meta) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(meta)) if (!k.startsWith('vela-')) out[k] = v;\n return Object.keys(out).length ? out : undefined;\n}\n\nasync function importKey(key: CryptoKey | Uint8Array): Promise<CryptoKey> {\n if (key instanceof Uint8Array) {\n return crypto.subtle.importKey(\n 'raw',\n key as unknown as ArrayBuffer,\n { name: 'AES-GCM' },\n false,\n ['encrypt', 'decrypt'],\n );\n }\n return key;\n}\n\n/**\n * Transparent client-side encryption via Web Crypto AES-GCM. v1 encrypts the\n * WHOLE object (buffered); the stored object is `IV(12) || ciphertext+tag`.\n * Range reads, presigned URLs, and multipart are disabled (a signed URL would\n * hand out ciphertext / accept plaintext, defeating the invariant).\n */\nexport function encryption(opts: EncryptionOptions): Middleware {\n const keyPromise = importKey(opts.key);\n\n return (inner) => {\n const decryptDownload = async (k: string, _o?: DownloadOptions): Promise<StoredFile> => {\n const file = await inner.download(k);\n const framed = new Uint8Array(await file.arrayBuffer());\n if (framed.byteLength < IV_BYTES) {\n if (opts.onInvalid === 'passthrough') return file;\n throw new StorageError('Parse', `object too small to be encrypted: ${k}`);\n }\n const iv = framed.subarray(0, IV_BYTES);\n const ciphertext = framed.subarray(IV_BYTES);\n const key = await keyPromise;\n let plain: Uint8Array;\n try {\n plain = new Uint8Array(\n await crypto.subtle.decrypt({ name: 'AES-GCM', iv: asBuf(iv) }, key, asBuf(ciphertext)),\n );\n } catch (e) {\n if (opts.onInvalid === 'passthrough') return file;\n throw new StorageError('Provider', `decryption failed: ${k}`, { cause: e });\n }\n return createStoredFile(\n {\n key: k,\n name: file.name,\n size: plain.byteLength,\n type: file.metadata?.['vela-ct'] ?? file.type,\n lastModified: file.lastModified,\n etag: file.etag,\n metadata: stripVela(file.metadata),\n },\n { kind: 'bytes', bytes: plain },\n );\n };\n\n return passthrough(\n inner,\n {\n reportsUploadProgress: false,\n supportsRange: false,\n signedUrl: { supported: false, upload: false },\n async upload(k, body, o?: UploadOptions) {\n const plaintext = await toBytes(body);\n const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));\n const key = await keyPromise;\n const ct = new Uint8Array(\n await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, asBuf(plaintext)),\n );\n const framed = new Uint8Array(IV_BYTES + ct.byteLength);\n framed.set(iv, 0);\n framed.set(ct, IV_BYTES);\n\n const metadata: Record<string, string> = { ...o?.metadata, 'vela-enc': 'AES-GCM' };\n if (o?.contentType) metadata['vela-ct'] = o.contentType;\n metadata['vela-size'] = String(plaintext.byteLength);\n\n const r = await inner.upload(k, framed, {\n ...o,\n metadata: inner.supportsMetadata ? metadata : undefined,\n contentType: 'application/octet-stream',\n });\n return {\n ...r,\n size: plaintext.byteLength,\n contentType: o?.contentType ?? 'application/octet-stream',\n };\n },\n download: decryptDownload,\n async head(k, o?: OperationOptions) {\n const file = await inner.head(k, o);\n const size = file.metadata?.['vela-size']\n ? Number(file.metadata['vela-size'])\n : file.size;\n return createStoredFile(\n {\n key: k,\n name: file.name,\n size,\n type: file.metadata?.['vela-ct'] ?? file.type,\n lastModified: file.lastModified,\n etag: file.etag,\n metadata: stripVela(file.metadata),\n },\n { kind: 'lazy', fetch: async () => new Response((await decryptDownload(k)).stream()) },\n );\n },\n url() {\n throw new StorageError('Unsupported', 'encryption: url() would expose ciphertext');\n },\n signedUploadUrl() {\n throw new StorageError('Unsupported', 'encryption: signed uploads would store plaintext');\n },\n },\n ['createMultipartUpload', 'resumeMultipartUpload', 'signedMultipart'],\n );\n };\n}\n","import { byteLengthOf, toStream } from '../internal/body';\nimport { createStoredFile } from '../internal/stored-file';\nimport { StorageError } from '../storage.error';\nimport type {\n DownloadOptions,\n OperationOptions,\n StoredFile,\n UploadOptions,\n} from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport type CompressionFormat = 'gzip' | 'deflate' | 'deflate-raw';\n\nexport interface CompressionOptions {\n /** Stream format. Default `gzip`. */\n format?: CompressionFormat;\n /** What to do when `CompressionStream` is unavailable. Default `throw`. */\n onUnavailable?: 'throw' | 'passthrough';\n}\n\nfunction stripVela(meta: Record<string, string> | undefined): Record<string, string> | undefined {\n if (!meta) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(meta)) if (!k.startsWith('vela-')) out[k] = v;\n return Object.keys(out).length ? out : undefined;\n}\n\n/**\n * Transparent gzip/deflate via Web Streams `CompressionStream` — fully\n * streaming, opaque mode (v1). The stored object is compressed and tagged\n * `vela-zip`; reads detect the tag and decompress, so pre-existing (untagged)\n * objects pass through unharmed. Range reads / presigned URLs are disabled\n * (the byte stream is not seekable and a direct URL would serve gzip).\n */\nexport function compression(opts: CompressionOptions = {}): Middleware {\n const format = opts.format ?? 'gzip';\n const available =\n typeof CompressionStream !== 'undefined' && typeof DecompressionStream !== 'undefined';\n if (!available && (opts.onUnavailable ?? 'throw') === 'throw') {\n throw new StorageError('Unsupported', 'CompressionStream is not available in this runtime');\n }\n\n return (inner) => {\n if (!available) return inner; // passthrough mode\n\n const decompressDownload = async (k: string, _o?: DownloadOptions): Promise<StoredFile> => {\n const file = await inner.download(k);\n const zip = file.metadata?.['vela-zip'] as CompressionFormat | undefined;\n if (!zip) return file; // untagged / legacy object\n const plain = file\n .stream()\n .pipeThrough(\n new DecompressionStream(zip) as unknown as ReadableWritablePair<Uint8Array, Uint8Array>,\n );\n const size = file.metadata?.['vela-size'] ? Number(file.metadata['vela-size']) : file.size;\n return createStoredFile(\n {\n key: k,\n name: file.name,\n size,\n type: file.metadata?.['vela-ct'] ?? file.type,\n lastModified: file.lastModified,\n etag: file.etag,\n metadata: stripVela(file.metadata),\n },\n { kind: 'stream', stream: plain },\n );\n };\n\n return passthrough(\n inner,\n {\n reportsUploadProgress: false,\n supportsRange: false,\n signedUrl: { supported: false, upload: false },\n async upload(k, body, o?: UploadOptions) {\n const zipped = toStream(body).pipeThrough(\n new CompressionStream(format) as unknown as ReadableWritablePair<\n Uint8Array,\n Uint8Array\n >,\n );\n const metadata: Record<string, string> = { ...o?.metadata, 'vela-zip': format };\n if (o?.contentType) metadata['vela-ct'] = o.contentType;\n const known = byteLengthOf(body);\n if (known != null) metadata['vela-size'] = String(known);\n const r = await inner.upload(k, zipped, {\n ...o,\n metadata: inner.supportsMetadata ? metadata : undefined,\n contentType: 'application/octet-stream',\n });\n return {\n ...r,\n size: known ?? r.size,\n contentType: o?.contentType ?? 'application/octet-stream',\n };\n },\n download: decompressDownload,\n async head(k, o?: OperationOptions) {\n const file = await inner.head(k, o);\n if (!file.metadata?.['vela-zip']) return file;\n const size = file.metadata['vela-size'] ? Number(file.metadata['vela-size']) : file.size;\n return createStoredFile(\n {\n key: k,\n name: file.name,\n size,\n type: file.metadata['vela-ct'] ?? file.type,\n lastModified: file.lastModified,\n etag: file.etag,\n metadata: stripVela(file.metadata),\n },\n {\n kind: 'lazy',\n fetch: async () => new Response((await decompressDownload(k)).stream()),\n },\n );\n },\n url() {\n throw new StorageError('Unsupported', 'compression: url() would serve compressed bytes');\n },\n signedUploadUrl() {\n throw new StorageError(\n 'Unsupported',\n 'compression: signed uploads would store uncompressed',\n );\n },\n },\n ['createMultipartUpload', 'resumeMultipartUpload', 'signedMultipart'],\n );\n };\n}\n","import { StorageError } from '../storage.error';\nimport type { StorageDriver } from '../storage.types';\nimport { passthrough, type Middleware } from './wrap';\n\nexport interface VersionInfo {\n versionId: string;\n key: string;\n size: number;\n lastModified?: number;\n}\n\n/** Extra surface a versioning-wrapped driver exposes (reached via {@link unwrapVersioning}). */\nexport interface Versioned {\n listVersions(key: string): Promise<VersionInfo[]>;\n restore(key: string, versionId: string): Promise<void>;\n deleteVersion(key: string, versionId: string): Promise<void>;\n}\n\nexport const VERSIONED: unique symbol = Symbol.for('vela.storage.versioned');\n\nexport interface VersioningOptions {\n /** Prefix under which prior versions are stored. Default `.vela-versions/`. */\n prefix?: string;\n versionId?: () => string;\n /** Keep at most this many versions per key (prunes oldest). */\n maxVersions?: number;\n /** Hide the version prefix from `list()`. Default true. */\n hideFromList?: boolean;\n /** On delete: snapshot first (default) or purge all versions. */\n onDelete?: 'snapshot' | 'purge';\n}\n\n/**\n * Keeps prior versions on overwrite via key-suffixing (no external manifest →\n * no read-modify-write race). Snapshots are cheap when the base driver\n * `supportsServerSideCopy`. Direct presigned uploads bypass snapshotting, so\n * `signedUploadUrl` throws. Reach `listVersions`/`restore`/`deleteVersion` via\n * {@link unwrapVersioning}.\n */\nexport function versioning(opts: VersioningOptions = {}): Middleware {\n const prefix = opts.prefix ?? '.vela-versions/';\n let seq = 0;\n const vid = opts.versionId ?? (() => `${Date.now().toString(36)}-${(seq++).toString(36)}`);\n const vkey = (key: string, id: string) => `${prefix}${key}/${id}`;\n const isVersion = (key: string) => key.startsWith(prefix);\n\n const listVersions = async (inner: StorageDriver, key: string): Promise<VersionInfo[]> => {\n const res = await inner.list({ prefix: `${prefix}${key}/` });\n return res.items.map((item) => ({\n versionId: item.key.slice(item.key.lastIndexOf('/') + 1),\n key: item.key,\n size: item.size,\n lastModified: item.lastModified,\n }));\n };\n\n const snapshot = async (inner: StorageDriver, key: string): Promise<void> => {\n if (!(await inner.exists(key))) return;\n const id = vid();\n if (inner.supportsServerSideCopy) {\n await inner.copy(key, vkey(key, id));\n } else {\n const current = await inner.download(key);\n await inner.upload(vkey(key, id), current.stream(), { contentType: current.type });\n }\n if (opts.maxVersions) {\n const versions = (await listVersions(inner, key)).sort((a, b) =>\n a.versionId < b.versionId ? -1 : 1,\n );\n for (const v of versions.slice(0, Math.max(0, versions.length - opts.maxVersions))) {\n await inner.delete(v.key);\n }\n }\n };\n\n return (inner) => {\n const driver = passthrough(inner, {\n async upload(k, b, o) {\n await snapshot(inner, k);\n return inner.upload(k, b, o);\n },\n async copy(f, t, o) {\n await snapshot(inner, t);\n return inner.copy(f, t, o);\n },\n async delete(k, o) {\n if (opts.onDelete !== 'purge') await snapshot(inner, k);\n await inner.delete(k, o);\n if (opts.onDelete === 'purge') {\n for (const v of await listVersions(inner, k)) await inner.delete(v.key);\n }\n },\n list:\n opts.hideFromList === false\n ? undefined\n : async (o) => {\n const res = await inner.list(o);\n return {\n items: res.items.filter((i) => !isVersion(i.key)),\n prefixes: res.prefixes?.filter((p) => !isVersion(p)),\n cursor: res.cursor,\n };\n },\n signedUploadUrl() {\n throw new StorageError('Unsupported', 'versioning: direct uploads bypass snapshots');\n },\n });\n\n const versioned: Versioned = {\n listVersions: (key) => listVersions(inner, key),\n async restore(key, versionId) {\n await snapshot(inner, key);\n await inner.copy(vkey(key, versionId), key);\n },\n deleteVersion: (key, versionId) => inner.delete(vkey(key, versionId)),\n };\n\n return Object.assign(driver, { [VERSIONED]: versioned });\n };\n}\n\n/** Retrieve the {@link Versioned} surface from a versioning-wrapped driver (or `undefined`). */\nexport function unwrapVersioning(driver: StorageDriver): Versioned | undefined {\n return (driver as unknown as { [VERSIONED]?: Versioned })[VERSIONED];\n}\n"],"mappings":"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"}