@velajs/storage 0.3.1 → 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 -407
  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 -386
  88. package/dist/storage.error.d.ts +0 -38
  89. package/dist/storage.error.js +0 -52
  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
@@ -0,0 +1,557 @@
1
+ import { t as byteLengthOf } from "./body-BMUcBeQ0.js";
2
+ import { t as StorageError } from "./storage.error-DVCOkJhc.js";
3
+ import { t as createStoredFile } from "./stored-file-2Yl07eRc.js";
4
+ import { AwsClient } from "aws4fetch";
5
+ //#region src/base64.ts
6
+ const CHUNK = 32768;
7
+ /** Encode bytes to standard base64. */
8
+ function bytesToBase64(bytes) {
9
+ let binary = "";
10
+ for (let i = 0; i < bytes.length; i += CHUNK) binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
11
+ return btoa(binary);
12
+ }
13
+ /** Encode a UTF-8 string to base64 (unicode-safe, unlike raw `btoa`). */
14
+ function utf8ToBase64(text) {
15
+ return bytesToBase64(new TextEncoder().encode(text));
16
+ }
17
+ //#endregion
18
+ //#region src/drivers/s3/s3-client.ts
19
+ const encoder$1 = new TextEncoder();
20
+ async function hmac(key, data) {
21
+ const cryptoKey = await crypto.subtle.importKey("raw", key, {
22
+ name: "HMAC",
23
+ hash: "SHA-256"
24
+ }, false, ["sign"]);
25
+ const sig = await crypto.subtle.sign("HMAC", cryptoKey, encoder$1.encode(data));
26
+ return new Uint8Array(sig);
27
+ }
28
+ function hex(bytes) {
29
+ let out = "";
30
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
31
+ return out;
32
+ }
33
+ async function sigV4SigningKey(secret, dateStamp, region, service = "s3") {
34
+ return hmac(await hmac(await hmac(await hmac(encoder$1.encode(`AWS4${secret}`), dateStamp), region), service), "aws4_request");
35
+ }
36
+ function amzDates() {
37
+ const amzDate = (/* @__PURE__ */ new Date()).toISOString().replace(/[:-]|\.\d{3}/g, "");
38
+ return {
39
+ amzDate,
40
+ dateStamp: amzDate.slice(0, 8)
41
+ };
42
+ }
43
+ /**
44
+ * Thin wrapper over aws4fetch: builds addressed URLs, sends SigV4-signed
45
+ * requests, and mints presigned GET/PUT URLs + presigned-POST policies.
46
+ * The single place S3 signing happens.
47
+ */
48
+ var S3Client = class {
49
+ #aws;
50
+ #cfg;
51
+ #fetch;
52
+ constructor(cfg) {
53
+ this.#cfg = cfg;
54
+ this.#fetch = cfg.fetch ?? fetch;
55
+ this.#aws = new AwsClient({
56
+ accessKeyId: cfg.credentials.accessKeyId,
57
+ secretAccessKey: cfg.credentials.secretAccessKey,
58
+ sessionToken: cfg.credentials.sessionToken,
59
+ region: cfg.region,
60
+ service: "s3"
61
+ });
62
+ }
63
+ get bucket() {
64
+ return this.#cfg.bucket;
65
+ }
66
+ /** Encode each key segment but keep `/` separators. */
67
+ #encodeKey(key) {
68
+ return key.split("/").map(encodeURIComponent).join("/");
69
+ }
70
+ objectUrl(key, query) {
71
+ const enc = this.#encodeKey(key);
72
+ const u = new URL(this.#cfg.endpoint);
73
+ if (this.#cfg.forcePathStyle) u.pathname = `/${this.#cfg.bucket}${enc ? `/${enc}` : ""}`;
74
+ else {
75
+ u.hostname = `${this.#cfg.bucket}.${u.hostname}`;
76
+ u.pathname = `/${enc}`;
77
+ }
78
+ if (query) for (const [k, v] of Object.entries(query)) u.searchParams.set(k, v);
79
+ return u;
80
+ }
81
+ /** The bucket-root URL used as the presigned-POST target. */
82
+ postUrl() {
83
+ const u = new URL(this.#cfg.endpoint);
84
+ if (this.#cfg.forcePathStyle) u.pathname = `/${this.#cfg.bucket}`;
85
+ else u.hostname = `${this.#cfg.bucket}.${u.hostname}`;
86
+ return u.toString();
87
+ }
88
+ /** Send a SigV4-signed request. */
89
+ async send(url, init) {
90
+ const signed = await this.#aws.sign(url.toString(), init);
91
+ return this.#fetch(signed);
92
+ }
93
+ /** Presign a GET or PUT URL via query signing (unsigned payload). */
94
+ async presign(key, method, expiresIn, query) {
95
+ const url = this.objectUrl(key, {
96
+ ...query,
97
+ "X-Amz-Expires": String(expiresIn)
98
+ });
99
+ return (await this.#aws.sign(url.toString(), {
100
+ method,
101
+ aws: { signQuery: true }
102
+ })).url;
103
+ }
104
+ /**
105
+ * Presigned POST policy — enforces `content-length-range` server-side.
106
+ * aws4fetch only signs requests, so the policy HMAC chain is hand-rolled
107
+ * on Web Crypto (edge-safe).
108
+ */
109
+ async signPostPolicy(key, opts) {
110
+ const { amzDate, dateStamp } = amzDates();
111
+ const { accessKeyId, secretAccessKey, sessionToken } = this.#cfg.credentials;
112
+ const credential = `${accessKeyId}/${dateStamp}/${this.#cfg.region}/s3/aws4_request`;
113
+ const expiration = new Date(Date.now() + opts.expiresIn * 1e3).toISOString();
114
+ const conditions = [
115
+ { bucket: this.#cfg.bucket },
116
+ { key },
117
+ { "x-amz-algorithm": "AWS4-HMAC-SHA256" },
118
+ { "x-amz-credential": credential },
119
+ { "x-amz-date": amzDate }
120
+ ];
121
+ if (sessionToken) conditions.push({ "x-amz-security-token": sessionToken });
122
+ if (opts.contentType) conditions.push({ "content-type": opts.contentType });
123
+ if (opts.maxSize != null || opts.minSize != null) conditions.push([
124
+ "content-length-range",
125
+ opts.minSize ?? 0,
126
+ opts.maxSize ?? opts.minSize ?? 0
127
+ ]);
128
+ const policyB64 = utf8ToBase64(JSON.stringify({
129
+ expiration,
130
+ conditions
131
+ }));
132
+ const fields = {
133
+ key,
134
+ "x-amz-algorithm": "AWS4-HMAC-SHA256",
135
+ "x-amz-credential": credential,
136
+ "x-amz-date": amzDate,
137
+ policy: policyB64,
138
+ "x-amz-signature": hex(await hmac(await sigV4SigningKey(secretAccessKey, dateStamp, this.#cfg.region), policyB64))
139
+ };
140
+ if (sessionToken) fields["x-amz-security-token"] = sessionToken;
141
+ if (opts.contentType) fields["content-type"] = opts.contentType;
142
+ return {
143
+ url: this.postUrl(),
144
+ fields
145
+ };
146
+ }
147
+ };
148
+ //#endregion
149
+ //#region src/drivers/s3/xml.ts
150
+ const ENTITIES = {
151
+ amp: "&",
152
+ lt: "<",
153
+ gt: ">",
154
+ quot: "\"",
155
+ apos: "'"
156
+ };
157
+ function unescapeXml(s) {
158
+ return s.replace(/&(#x?[0-9a-fA-F]+|amp|lt|gt|quot|apos);/g, (_m, e) => {
159
+ if (e[0] === "#") {
160
+ const code = e[1] === "x" ? Number.parseInt(e.slice(2), 16) : Number.parseInt(e.slice(1), 10);
161
+ return Number.isFinite(code) ? String.fromCodePoint(code) : _m;
162
+ }
163
+ return ENTITIES[e] ?? _m;
164
+ });
165
+ }
166
+ function escapeXml(s) {
167
+ return s.replace(/[&<>"']/g, (c) => {
168
+ switch (c) {
169
+ case "&": return "&amp;";
170
+ case "<": return "&lt;";
171
+ case ">": return "&gt;";
172
+ case "\"": return "&quot;";
173
+ default: return "&apos;";
174
+ }
175
+ });
176
+ }
177
+ function tagPattern(tag) {
178
+ return `<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)</${tag}>`;
179
+ }
180
+ /** First `<tag>…</tag>` inner text (entity-decoded), or `undefined`. */
181
+ function tagText(xml, tag) {
182
+ const m = new RegExp(tagPattern(tag)).exec(xml);
183
+ return m ? unescapeXml(m[1]) : void 0;
184
+ }
185
+ /** All `<tag>…</tag>` inner blocks (raw, not decoded — for nested extraction). */
186
+ function tagBlocks(xml, tag) {
187
+ const re = new RegExp(tagPattern(tag), "g");
188
+ const out = [];
189
+ let m;
190
+ while ((m = re.exec(xml)) !== null) out.push(m[1]);
191
+ return out;
192
+ }
193
+ //#endregion
194
+ //#region src/drivers/s3/s3.driver.ts
195
+ const encoder = new TextEncoder();
196
+ const DEFAULT_EXPIRES = 3600;
197
+ const DEFAULT_PART_SIZE = 5 * 1024 * 1024;
198
+ function stripQuotes(s) {
199
+ return s ? s.replace(/^"|"$/g, "") : void 0;
200
+ }
201
+ function parseDate(s) {
202
+ if (!s) return void 0;
203
+ const t = Date.parse(s);
204
+ return Number.isFinite(t) ? t : void 0;
205
+ }
206
+ function formatRange(range) {
207
+ return `bytes=${range.start}-${range.end ?? ""}`;
208
+ }
209
+ function metaFromHeaders(headers) {
210
+ const meta = {};
211
+ headers.forEach((value, key) => {
212
+ if (key.startsWith("x-amz-meta-")) meta[key.slice(11)] = value;
213
+ });
214
+ return Object.keys(meta).length ? meta : void 0;
215
+ }
216
+ function joinPublic(base, key) {
217
+ const enc = key.split("/").map(encodeURIComponent).join("/");
218
+ return `${base.replace(/\/$/, "")}/${enc}`;
219
+ }
220
+ /** Map an S3 `<Code>` (and/or HTTP status) to our error taxonomy. */
221
+ function mapS3Code(code, status) {
222
+ switch (code) {
223
+ case "NoSuchKey":
224
+ case "NoSuchUpload":
225
+ case "NoSuchBucket": return "NotFound";
226
+ case "AccessDenied":
227
+ case "InvalidAccessKeyId":
228
+ case "SignatureDoesNotMatch":
229
+ case "AllAccessDisabled": return "AccessDenied";
230
+ case "SlowDown":
231
+ case "RequestTimeout": return "RateLimited";
232
+ default: break;
233
+ }
234
+ if (status === 404) return "NotFound";
235
+ if (status === 403 || status === 401) return "AccessDenied";
236
+ if (status === 409) return "Conflict";
237
+ if (status === 429) return "RateLimited";
238
+ if (status >= 500) return "Provider";
239
+ if (status >= 400) return "InvalidRequest";
240
+ return "Provider";
241
+ }
242
+ function toError(status, code, message, key) {
243
+ const mapped = mapS3Code(code, status);
244
+ const retryable = mapped === "Provider" || mapped === "RateLimited" || mapped === "Timeout" || mapped === "Network";
245
+ return new StorageError(mapped, message ?? code ?? `S3 error${key ? ` (${key})` : ""}`, {
246
+ status: status >= 400 ? status : void 0,
247
+ retryable,
248
+ internal: true
249
+ });
250
+ }
251
+ async function s3Error(res, key) {
252
+ let code;
253
+ let message;
254
+ try {
255
+ const body = await res.text();
256
+ code = tagText(body, "Code");
257
+ message = tagText(body, "Message");
258
+ } catch {}
259
+ return toError(res.status, code, message, key);
260
+ }
261
+ function parseErrorBody(status, body, key) {
262
+ return toError(status, tagText(body, "Code"), tagText(body, "Message"), key);
263
+ }
264
+ /** Add `duplex: 'half'` when streaming a request body (required by fetch). */
265
+ function withDuplex(init, body) {
266
+ if (body instanceof ReadableStream) return {
267
+ ...init,
268
+ duplex: "half"
269
+ };
270
+ return init;
271
+ }
272
+ /** S3 / S3-compatible driver signed with aws4fetch (edge-safe). */
273
+ function s3Driver(options) {
274
+ const client = new S3Client(options);
275
+ const expires = options.defaultUrlExpiresIn ?? DEFAULT_EXPIRES;
276
+ function uploadHeaders(opts, unsignedPayload) {
277
+ const h = new Headers();
278
+ if (unsignedPayload) h.set("x-amz-content-sha256", "UNSIGNED-PAYLOAD");
279
+ if (opts?.contentType) h.set("content-type", opts.contentType);
280
+ if (opts?.cacheControl) h.set("cache-control", opts.cacheControl);
281
+ if (opts?.metadata) for (const [k, v] of Object.entries(opts.metadata)) h.set(`x-amz-meta-${k}`, v);
282
+ return h;
283
+ }
284
+ function multipartHandle(key, uploadId, createOpts) {
285
+ return {
286
+ key,
287
+ uploadId,
288
+ async uploadPart(partNumber, part, o) {
289
+ const url = client.objectUrl(key, {
290
+ partNumber: String(partNumber),
291
+ uploadId
292
+ });
293
+ const res = await client.send(url, withDuplex({
294
+ method: "PUT",
295
+ headers: new Headers({ "x-amz-content-sha256": "UNSIGNED-PAYLOAD" }),
296
+ body: part,
297
+ signal: o?.signal
298
+ }, part));
299
+ if (!res.ok) throw await s3Error(res, key);
300
+ const etag = res.headers.get("etag");
301
+ if (!etag) throw new StorageError("Provider", `missing ETag for part ${partNumber}`);
302
+ return {
303
+ partNumber,
304
+ etag
305
+ };
306
+ },
307
+ async complete(parts, o) {
308
+ const xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><CompleteMultipartUpload>" + [...parts].sort((a, b) => a.partNumber - b.partNumber).map((p) => `<Part><PartNumber>${p.partNumber}</PartNumber><ETag>${escapeXml(p.etag)}</ETag></Part>`).join("") + "</CompleteMultipartUpload>";
309
+ const res = await client.send(client.objectUrl(key, { uploadId }), {
310
+ method: "POST",
311
+ headers: new Headers({ "content-type": "application/xml" }),
312
+ body: encoder.encode(xml),
313
+ signal: o?.signal
314
+ });
315
+ const body = await res.text();
316
+ if (!res.ok || body.includes("<Error")) throw parseErrorBody(res.status, body, key);
317
+ return {
318
+ key,
319
+ size: 0,
320
+ contentType: createOpts?.contentType ?? "application/octet-stream",
321
+ etag: stripQuotes(tagText(body, "ETag"))
322
+ };
323
+ },
324
+ async abort(o) {
325
+ const res = await client.send(client.objectUrl(key, { uploadId }), {
326
+ method: "DELETE",
327
+ signal: o?.signal
328
+ });
329
+ if (!res.ok && res.status !== 404) throw await s3Error(res, key);
330
+ },
331
+ async listParts(o) {
332
+ const res = await client.send(client.objectUrl(key, { uploadId }), {
333
+ method: "GET",
334
+ signal: o?.signal
335
+ });
336
+ const xml = await res.text();
337
+ if (!res.ok) throw parseErrorBody(res.status, xml, key);
338
+ return tagBlocks(xml, "Part").map((b) => ({
339
+ partNumber: Number(tagText(b, "PartNumber") ?? 0),
340
+ etag: stripQuotes(tagText(b, "ETag")) ?? "",
341
+ size: Number(tagText(b, "Size") ?? 0)
342
+ }));
343
+ }
344
+ };
345
+ }
346
+ return {
347
+ name: options.name ?? "s3",
348
+ raw: client,
349
+ supportsRange: true,
350
+ supportsDelimiter: true,
351
+ supportsMetadata: true,
352
+ supportsCacheControl: true,
353
+ supportsServerSideCopy: true,
354
+ reportsUploadProgress: false,
355
+ signedUrl: {
356
+ supported: true,
357
+ upload: true
358
+ },
359
+ async upload(key, body, opts) {
360
+ const size = byteLengthOf(body) ?? 0;
361
+ const res = await client.send(client.objectUrl(key), withDuplex({
362
+ method: "PUT",
363
+ headers: uploadHeaders(opts, true),
364
+ body,
365
+ signal: opts?.signal
366
+ }, body));
367
+ if (!res.ok) throw await s3Error(res, key);
368
+ return {
369
+ key,
370
+ size,
371
+ contentType: opts?.contentType ?? "application/octet-stream",
372
+ etag: stripQuotes(res.headers.get("etag")),
373
+ lastModified: parseDate(res.headers.get("last-modified"))
374
+ };
375
+ },
376
+ async download(key, opts) {
377
+ const headers = new Headers();
378
+ if (opts?.range) headers.set("Range", formatRange(opts.range));
379
+ const res = await client.send(client.objectUrl(key), {
380
+ method: "GET",
381
+ headers,
382
+ signal: opts?.signal
383
+ });
384
+ if (!res.ok || !res.body) {
385
+ if (res.status === 404) throw new StorageError("NotFound", `not found: ${key}`, { status: 404 });
386
+ throw await s3Error(res, key);
387
+ }
388
+ return createStoredFile({
389
+ key,
390
+ size: Number(res.headers.get("content-length") ?? 0),
391
+ type: res.headers.get("content-type") ?? "application/octet-stream",
392
+ etag: stripQuotes(res.headers.get("etag")),
393
+ lastModified: parseDate(res.headers.get("last-modified")),
394
+ metadata: metaFromHeaders(res.headers)
395
+ }, {
396
+ kind: "stream",
397
+ stream: res.body
398
+ });
399
+ },
400
+ async head(key, opts) {
401
+ const res = await client.send(client.objectUrl(key), {
402
+ method: "HEAD",
403
+ signal: opts?.signal
404
+ });
405
+ if (!res.ok) {
406
+ if (res.status === 404) throw new StorageError("NotFound", `not found: ${key}`, { status: 404 });
407
+ throw await s3Error(res, key);
408
+ }
409
+ return createStoredFile({
410
+ key,
411
+ size: Number(res.headers.get("content-length") ?? 0),
412
+ type: res.headers.get("content-type") ?? "application/octet-stream",
413
+ etag: stripQuotes(res.headers.get("etag")),
414
+ lastModified: parseDate(res.headers.get("last-modified")),
415
+ metadata: metaFromHeaders(res.headers)
416
+ }, {
417
+ kind: "lazy",
418
+ fetch: () => client.send(client.objectUrl(key), { method: "GET" })
419
+ });
420
+ },
421
+ async exists(key, opts) {
422
+ const res = await client.send(client.objectUrl(key), {
423
+ method: "HEAD",
424
+ signal: opts?.signal
425
+ });
426
+ if (res.status === 404) return false;
427
+ if (!res.ok) throw await s3Error(res, key);
428
+ return true;
429
+ },
430
+ async delete(key, opts) {
431
+ const res = await client.send(client.objectUrl(key), {
432
+ method: "DELETE",
433
+ signal: opts?.signal
434
+ });
435
+ if (!res.ok && res.status !== 404) throw await s3Error(res, key);
436
+ },
437
+ async copy(from, to, opts) {
438
+ const source = `/${client.bucket}/${from.split("/").map(encodeURIComponent).join("/")}`;
439
+ const res = await client.send(client.objectUrl(to), {
440
+ method: "PUT",
441
+ headers: new Headers({ "x-amz-copy-source": source }),
442
+ signal: opts?.signal
443
+ });
444
+ const body = await res.text();
445
+ if (!res.ok || body.includes("<Error")) throw parseErrorBody(res.status, body, to);
446
+ },
447
+ async list(opts) {
448
+ const query = { "list-type": "2" };
449
+ if (opts?.prefix) query.prefix = opts.prefix;
450
+ if (opts?.delimiter) query.delimiter = opts.delimiter;
451
+ if (opts?.cursor) query["continuation-token"] = opts.cursor;
452
+ if (opts?.limit) query["max-keys"] = String(opts.limit);
453
+ const res = await client.send(client.objectUrl("", query), {
454
+ method: "GET",
455
+ signal: opts?.signal
456
+ });
457
+ const xml = await res.text();
458
+ if (!res.ok) throw parseErrorBody(res.status, xml);
459
+ const items = tagBlocks(xml, "Contents").map((b) => {
460
+ const k = tagText(b, "Key") ?? "";
461
+ return createStoredFile({
462
+ key: k,
463
+ size: Number(tagText(b, "Size") ?? 0),
464
+ type: "application/octet-stream",
465
+ etag: stripQuotes(tagText(b, "ETag")),
466
+ lastModified: parseDate(tagText(b, "LastModified"))
467
+ }, {
468
+ kind: "lazy",
469
+ fetch: () => client.send(client.objectUrl(k), { method: "GET" })
470
+ });
471
+ });
472
+ const prefixes = tagBlocks(xml, "CommonPrefixes").map((b) => tagText(b, "Prefix")).filter((p) => !!p);
473
+ const cursor = tagText(xml, "IsTruncated") === "true" ? tagText(xml, "NextContinuationToken") : void 0;
474
+ return {
475
+ items,
476
+ prefixes: prefixes.length ? prefixes : void 0,
477
+ cursor
478
+ };
479
+ },
480
+ async url(key, opts) {
481
+ const forceSign = !!opts?.responseContentDisposition;
482
+ if (options.publicBaseUrl && !forceSign) return joinPublic(options.publicBaseUrl, key);
483
+ const query = opts?.responseContentDisposition ? { "response-content-disposition": opts.responseContentDisposition } : void 0;
484
+ return client.presign(key, "GET", opts?.expiresIn ?? expires, query);
485
+ },
486
+ async signedUploadUrl(key, opts) {
487
+ if (opts.maxSize != null || opts.minSize != null) {
488
+ const { url, fields } = await client.signPostPolicy(key, {
489
+ expiresIn: opts.expiresIn,
490
+ contentType: opts.contentType,
491
+ maxSize: opts.maxSize,
492
+ minSize: opts.minSize
493
+ });
494
+ return {
495
+ method: "POST",
496
+ url,
497
+ fields
498
+ };
499
+ }
500
+ return {
501
+ method: "PUT",
502
+ url: await client.presign(key, "PUT", opts.expiresIn),
503
+ headers: opts.contentType ? { "content-type": opts.contentType } : void 0
504
+ };
505
+ },
506
+ async createMultipartUpload(key, opts) {
507
+ const res = await client.send(client.objectUrl(key, { uploads: "" }), {
508
+ method: "POST",
509
+ headers: uploadHeaders(opts, false),
510
+ signal: opts?.signal
511
+ });
512
+ const xml = await res.text();
513
+ if (!res.ok) throw parseErrorBody(res.status, xml, key);
514
+ const uploadId = tagText(xml, "UploadId");
515
+ if (!uploadId) throw new StorageError("Parse", "missing UploadId");
516
+ return multipartHandle(key, uploadId, opts);
517
+ },
518
+ resumeMultipartUpload(key, uploadId) {
519
+ return multipartHandle(key, uploadId);
520
+ },
521
+ signedMultipart: {
522
+ async create(key, o) {
523
+ const res = await client.send(client.objectUrl(key, { uploads: "" }), {
524
+ method: "POST",
525
+ headers: uploadHeaders({
526
+ contentType: o.contentType,
527
+ metadata: o.metadata
528
+ }, false)
529
+ });
530
+ const xml = await res.text();
531
+ if (!res.ok) throw parseErrorBody(res.status, xml, key);
532
+ const uploadId = tagText(xml, "UploadId");
533
+ if (!uploadId) throw new StorageError("Parse", "missing UploadId");
534
+ return {
535
+ uploadId,
536
+ partSize: o.partSize ?? DEFAULT_PART_SIZE
537
+ };
538
+ },
539
+ async signPart(key, uploadId, partNumber, o) {
540
+ return { url: await client.presign(key, "PUT", o?.expiresIn ?? expires, {
541
+ partNumber: String(partNumber),
542
+ uploadId
543
+ }) };
544
+ },
545
+ complete(key, uploadId, parts) {
546
+ return multipartHandle(key, uploadId).complete(parts);
547
+ },
548
+ abort(key, uploadId) {
549
+ return multipartHandle(key, uploadId).abort();
550
+ }
551
+ }
552
+ };
553
+ }
554
+ //#endregion
555
+ export { unescapeXml as a, tagText as i, escapeXml as n, S3Client as o, tagBlocks as r, s3Driver as t };
556
+
557
+ //# sourceMappingURL=s3-DrE9NrnD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-DrE9NrnD.js","names":["encoder","#aws","#cfg","#fetch","#encodeKey"],"sources":["../src/base64.ts","../src/drivers/s3/s3-client.ts","../src/drivers/s3/xml.ts","../src/drivers/s3/s3.driver.ts"],"sourcesContent":["// Edge-safe base64 — NO `Buffer` (banned by the edge-runtime audit).\n// Uses `btoa`/`atob` over chunked `String.fromCharCode`, present on every\n// target runtime (Workers, Deno, Bun, Node 20+, browsers).\n\n// Chunk stays <= 0x8000 so the spread into String.fromCharCode never exceeds\n// the engine argument limit.\nconst CHUNK = 0x8000;\n\n/** Encode bytes to standard base64. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < bytes.length; i += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(binary);\n}\n\n/** Decode standard base64 to bytes. */\nexport function base64ToBytes(b64: string): Uint8Array {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\n/** Encode a UTF-8 string to base64 (unicode-safe, unlike raw `btoa`). */\nexport function utf8ToBase64(text: string): string {\n return bytesToBase64(new TextEncoder().encode(text));\n}\n\n/** URL-safe base64 (base64url) without padding — used by SigV4 / policies. */\nexport function bytesToBase64Url(bytes: Uint8Array): string {\n return bytesToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n","import { AwsClient } from 'aws4fetch';\nimport { utf8ToBase64 } from '../../base64';\nimport type { S3DriverOptions } from './s3.types';\n\n// --- Web-Crypto SigV4 helpers (used only for the POST-policy path; the\n// request-signing path is aws4fetch). No node:crypto, no Buffer. ---\n\nconst encoder = new TextEncoder();\n\nasync function hmac(key: Uint8Array, data: string): Promise<Uint8Array> {\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n key as unknown as ArrayBuffer,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const sig = await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(data));\n return new Uint8Array(sig);\n}\n\nfunction hex(bytes: Uint8Array): string {\n let out = '';\n for (const b of bytes) out += b.toString(16).padStart(2, '0');\n return out;\n}\n\nasync function sigV4SigningKey(\n secret: string,\n dateStamp: string,\n region: string,\n service = 's3',\n): Promise<Uint8Array> {\n const kDate = await hmac(encoder.encode(`AWS4${secret}`), dateStamp);\n const kRegion = await hmac(kDate, region);\n const kService = await hmac(kRegion, service);\n return hmac(kService, 'aws4_request');\n}\n\nfunction amzDates(): { amzDate: string; dateStamp: string } {\n const amzDate = new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n return { amzDate, dateStamp: amzDate.slice(0, 8) };\n}\n\nexport interface PostPolicyOptions {\n expiresIn: number;\n contentType?: string;\n maxSize?: number;\n minSize?: number;\n}\n\n/**\n * Thin wrapper over aws4fetch: builds addressed URLs, sends SigV4-signed\n * requests, and mints presigned GET/PUT URLs + presigned-POST policies.\n * The single place S3 signing happens.\n */\nexport class S3Client {\n readonly #aws: AwsClient;\n readonly #cfg: S3DriverOptions;\n readonly #fetch: typeof fetch;\n\n constructor(cfg: S3DriverOptions) {\n this.#cfg = cfg;\n this.#fetch = cfg.fetch ?? fetch;\n this.#aws = new AwsClient({\n accessKeyId: cfg.credentials.accessKeyId,\n secretAccessKey: cfg.credentials.secretAccessKey,\n sessionToken: cfg.credentials.sessionToken,\n region: cfg.region,\n service: 's3',\n });\n }\n\n get bucket(): string {\n return this.#cfg.bucket;\n }\n\n /** Encode each key segment but keep `/` separators. */\n #encodeKey(key: string): string {\n return key.split('/').map(encodeURIComponent).join('/');\n }\n\n objectUrl(key: string, query?: Record<string, string>): URL {\n const enc = this.#encodeKey(key);\n const u = new URL(this.#cfg.endpoint);\n if (this.#cfg.forcePathStyle) {\n u.pathname = `/${this.#cfg.bucket}${enc ? `/${enc}` : ''}`;\n } else {\n u.hostname = `${this.#cfg.bucket}.${u.hostname}`;\n u.pathname = `/${enc}`;\n }\n if (query) for (const [k, v] of Object.entries(query)) u.searchParams.set(k, v);\n return u;\n }\n\n /** The bucket-root URL used as the presigned-POST target. */\n postUrl(): string {\n const u = new URL(this.#cfg.endpoint);\n if (this.#cfg.forcePathStyle) u.pathname = `/${this.#cfg.bucket}`;\n else u.hostname = `${this.#cfg.bucket}.${u.hostname}`;\n return u.toString();\n }\n\n /** Send a SigV4-signed request. */\n async send(url: URL, init: RequestInit): Promise<Response> {\n const signed = await this.#aws.sign(url.toString(), init);\n return this.#fetch(signed);\n }\n\n /** Presign a GET or PUT URL via query signing (unsigned payload). */\n async presign(\n key: string,\n method: 'GET' | 'PUT',\n expiresIn: number,\n query?: Record<string, string>,\n ): Promise<string> {\n const url = this.objectUrl(key, { ...query, 'X-Amz-Expires': String(expiresIn) });\n const signed = await this.#aws.sign(url.toString(), {\n method,\n aws: { signQuery: true },\n });\n return signed.url;\n }\n\n /**\n * Presigned POST policy — enforces `content-length-range` server-side.\n * aws4fetch only signs requests, so the policy HMAC chain is hand-rolled\n * on Web Crypto (edge-safe).\n */\n async signPostPolicy(\n key: string,\n opts: PostPolicyOptions,\n ): Promise<{ url: string; fields: Record<string, string> }> {\n const { amzDate, dateStamp } = amzDates();\n const { accessKeyId, secretAccessKey, sessionToken } = this.#cfg.credentials;\n const credential = `${accessKeyId}/${dateStamp}/${this.#cfg.region}/s3/aws4_request`;\n const expiration = new Date(Date.now() + opts.expiresIn * 1000).toISOString();\n\n const conditions: Array<Record<string, string> | [string, string | number, number]> = [\n { bucket: this.#cfg.bucket },\n { key },\n { 'x-amz-algorithm': 'AWS4-HMAC-SHA256' },\n { 'x-amz-credential': credential },\n { 'x-amz-date': amzDate },\n ];\n if (sessionToken) conditions.push({ 'x-amz-security-token': sessionToken });\n if (opts.contentType) conditions.push({ 'content-type': opts.contentType });\n if (opts.maxSize != null || opts.minSize != null) {\n conditions.push([\n 'content-length-range',\n opts.minSize ?? 0,\n opts.maxSize ?? opts.minSize ?? 0,\n ]);\n }\n\n const policyB64 = utf8ToBase64(JSON.stringify({ expiration, conditions }));\n const signingKey = await sigV4SigningKey(secretAccessKey, dateStamp, this.#cfg.region);\n const signature = hex(await hmac(signingKey, policyB64));\n\n const fields: Record<string, string> = {\n key,\n 'x-amz-algorithm': 'AWS4-HMAC-SHA256',\n 'x-amz-credential': credential,\n 'x-amz-date': amzDate,\n policy: policyB64,\n 'x-amz-signature': signature,\n };\n if (sessionToken) fields['x-amz-security-token'] = sessionToken;\n if (opts.contentType) fields['content-type'] = opts.contentType;\n\n return { url: this.postUrl(), fields };\n }\n}\n","// Dependency-free, edge-safe S3 XML helpers. Workers has no `DOMParser` and\n// the AWS XML parser is banned, so we extract from S3's fixed, shallow,\n// documented response shapes (ListBucketResult, Initiate/CompleteMultipartUpload,\n// Error, ...) with anchored, ReDoS-free regexes. We never parse untrusted XML.\n\nconst ENTITIES: Record<string, string> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n};\n\nexport function unescapeXml(s: string): string {\n return s.replace(/&(#x?[0-9a-fA-F]+|amp|lt|gt|quot|apos);/g, (_m, e: string) => {\n if (e[0] === '#') {\n const code = e[1] === 'x' ? Number.parseInt(e.slice(2), 16) : Number.parseInt(e.slice(1), 10);\n return Number.isFinite(code) ? String.fromCodePoint(code) : _m;\n }\n return ENTITIES[e] ?? _m;\n });\n}\n\nexport function escapeXml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&':\n return '&amp;';\n case '<':\n return '&lt;';\n case '>':\n return '&gt;';\n case '\"':\n return '&quot;';\n default:\n return '&apos;';\n }\n });\n}\n\n// Each pattern uses a single non-greedy `[\\s\\S]*?` bounded by a literal close\n// tag → linear time, no nested quantifiers, no catastrophic backtracking.\nfunction tagPattern(tag: string): string {\n return `<${tag}(?:\\\\s[^>]*)?>([\\\\s\\\\S]*?)</${tag}>`;\n}\n\n/** First `<tag>…</tag>` inner text (entity-decoded), or `undefined`. */\nexport function tagText(xml: string, tag: string): string | undefined {\n const m = new RegExp(tagPattern(tag)).exec(xml);\n return m ? unescapeXml(m[1]!) : undefined;\n}\n\n/** All `<tag>…</tag>` inner blocks (raw, not decoded — for nested extraction). */\nexport function tagBlocks(xml: string, tag: string): string[] {\n const re = new RegExp(tagPattern(tag), 'g');\n const out: string[] = [];\n let m: RegExpExecArray | null;\n while ((m = re.exec(xml)) !== null) out.push(m[1]!);\n return out;\n}\n","import { byteLengthOf } from '../../internal/body';\nimport { createStoredFile } from '../../internal/stored-file';\nimport { StorageError, type StorageErrorCode } from '../../storage.error';\nimport type {\n Body,\n CreateMultipartOptions,\n DownloadOptions,\n ListOptions,\n ListResult,\n MultipartUpload,\n OperationOptions,\n PartBody,\n SignedMultipartCapability,\n SignedUpload,\n SignUploadOptions,\n StorageDriver,\n StoredFile,\n UploadedPart,\n UploadOptions,\n UploadResult,\n UrlOptions,\n ByteRange,\n} from '../../storage.types';\nimport { S3Client } from './s3-client';\nimport type { S3DriverOptions } from './s3.types';\nimport { escapeXml, tagBlocks, tagText } from './xml';\n\nconst encoder = new TextEncoder();\nconst DEFAULT_EXPIRES = 3600;\nconst DEFAULT_PART_SIZE = 5 * 1024 * 1024;\n\nfunction stripQuotes(s: string | null | undefined): string | undefined {\n return s ? s.replace(/^\"|\"$/g, '') : undefined;\n}\n\nfunction parseDate(s: string | null | undefined): number | undefined {\n if (!s) return undefined;\n const t = Date.parse(s);\n return Number.isFinite(t) ? t : undefined;\n}\n\nfunction formatRange(range: ByteRange): string {\n return `bytes=${range.start}-${range.end ?? ''}`;\n}\n\nfunction metaFromHeaders(headers: Headers): Record<string, string> | undefined {\n const meta: Record<string, string> = {};\n headers.forEach((value, key) => {\n if (key.startsWith('x-amz-meta-')) meta[key.slice('x-amz-meta-'.length)] = value;\n });\n return Object.keys(meta).length ? meta : undefined;\n}\n\nfunction joinPublic(base: string, key: string): string {\n const enc = key.split('/').map(encodeURIComponent).join('/');\n return `${base.replace(/\\/$/, '')}/${enc}`;\n}\n\n/** Map an S3 `<Code>` (and/or HTTP status) to our error taxonomy. */\nfunction mapS3Code(code: string | undefined, status: number): StorageErrorCode {\n switch (code) {\n case 'NoSuchKey':\n case 'NoSuchUpload':\n case 'NoSuchBucket':\n return 'NotFound';\n case 'AccessDenied':\n case 'InvalidAccessKeyId':\n case 'SignatureDoesNotMatch':\n case 'AllAccessDisabled':\n return 'AccessDenied';\n case 'SlowDown':\n case 'RequestTimeout':\n return 'RateLimited';\n default:\n break;\n }\n if (status === 404) return 'NotFound';\n if (status === 403 || status === 401) return 'AccessDenied';\n if (status === 409) return 'Conflict';\n if (status === 429) return 'RateLimited';\n if (status >= 500) return 'Provider';\n if (status >= 400) return 'InvalidRequest';\n return 'Provider'; // e.g. a 200 response carrying an <Error> body\n}\n\nfunction toError(\n status: number,\n code: string | undefined,\n message: string | undefined,\n key?: string,\n): StorageError {\n const mapped = mapS3Code(code, status);\n const retryable =\n mapped === 'Provider' ||\n mapped === 'RateLimited' ||\n mapped === 'Timeout' ||\n mapped === 'Network';\n return new StorageError(mapped, message ?? code ?? `S3 error${key ? ` (${key})` : ''}`, {\n status: status >= 400 ? status : undefined,\n retryable,\n // `message`/`code` are the provider's own <Message>/<Code> — never client-safe,\n // even on a 4xx status (e.g. AccessDenied/NoSuchKey text can carry internal detail).\n internal: true,\n });\n}\n\nasync function s3Error(res: Response, key?: string): Promise<StorageError> {\n let code: string | undefined;\n let message: string | undefined;\n try {\n const body = await res.text();\n code = tagText(body, 'Code');\n message = tagText(body, 'Message');\n } catch {\n /* ignore body read errors */\n }\n return toError(res.status, code, message, key);\n}\n\nfunction parseErrorBody(status: number, body: string, key?: string): StorageError {\n return toError(status, tagText(body, 'Code'), tagText(body, 'Message'), key);\n}\n\n/** Add `duplex: 'half'` when streaming a request body (required by fetch). */\nfunction withDuplex(init: RequestInit, body: Body | PartBody): RequestInit {\n if (body instanceof ReadableStream) {\n return { ...init, duplex: 'half' } as RequestInit & { duplex: 'half' };\n }\n return init;\n}\n\n/** S3 / S3-compatible driver signed with aws4fetch (edge-safe). */\nexport function s3Driver(options: S3DriverOptions): StorageDriver {\n const client = new S3Client(options);\n const expires = options.defaultUrlExpiresIn ?? DEFAULT_EXPIRES;\n\n function uploadHeaders(\n opts:\n | { contentType?: string; cacheControl?: string; metadata?: Record<string, string> }\n | undefined,\n unsignedPayload: boolean,\n ): Headers {\n const h = new Headers();\n if (unsignedPayload) h.set('x-amz-content-sha256', 'UNSIGNED-PAYLOAD');\n if (opts?.contentType) h.set('content-type', opts.contentType);\n if (opts?.cacheControl) h.set('cache-control', opts.cacheControl);\n if (opts?.metadata)\n for (const [k, v] of Object.entries(opts.metadata)) h.set(`x-amz-meta-${k}`, v);\n return h;\n }\n\n function multipartHandle(\n key: string,\n uploadId: string,\n createOpts?: CreateMultipartOptions,\n ): MultipartUpload {\n return {\n key,\n uploadId,\n async uploadPart(partNumber: number, part: PartBody, o?: OperationOptions) {\n const url = client.objectUrl(key, { partNumber: String(partNumber), uploadId });\n const res = await client.send(\n url,\n withDuplex(\n {\n method: 'PUT',\n headers: new Headers({ 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD' }),\n body: part as BodyInit,\n signal: o?.signal,\n },\n part,\n ),\n );\n if (!res.ok) throw await s3Error(res, key);\n const etag = res.headers.get('etag');\n if (!etag) throw new StorageError('Provider', `missing ETag for part ${partNumber}`);\n return { partNumber, etag };\n },\n async complete(parts: UploadedPart[], o?: OperationOptions) {\n const xml =\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?><CompleteMultipartUpload>' +\n [...parts]\n .sort((a, b) => a.partNumber - b.partNumber)\n .map(\n (p) =>\n `<Part><PartNumber>${p.partNumber}</PartNumber><ETag>${escapeXml(p.etag)}</ETag></Part>`,\n )\n .join('') +\n '</CompleteMultipartUpload>';\n const res = await client.send(client.objectUrl(key, { uploadId }), {\n method: 'POST',\n headers: new Headers({ 'content-type': 'application/xml' }),\n body: encoder.encode(xml),\n signal: o?.signal,\n });\n const body = await res.text();\n if (!res.ok || body.includes('<Error')) throw parseErrorBody(res.status, body, key);\n return {\n key,\n size: 0,\n contentType: createOpts?.contentType ?? 'application/octet-stream',\n etag: stripQuotes(tagText(body, 'ETag')),\n };\n },\n async abort(o?: OperationOptions) {\n const res = await client.send(client.objectUrl(key, { uploadId }), {\n method: 'DELETE',\n signal: o?.signal,\n });\n if (!res.ok && res.status !== 404) throw await s3Error(res, key);\n },\n async listParts(o?: OperationOptions) {\n const res = await client.send(client.objectUrl(key, { uploadId }), {\n method: 'GET',\n signal: o?.signal,\n });\n const xml = await res.text();\n if (!res.ok) throw parseErrorBody(res.status, xml, key);\n return tagBlocks(xml, 'Part').map((b) => ({\n partNumber: Number(tagText(b, 'PartNumber') ?? 0),\n etag: stripQuotes(tagText(b, 'ETag')) ?? '',\n size: Number(tagText(b, 'Size') ?? 0),\n }));\n },\n };\n }\n\n const signedMultipart: SignedMultipartCapability = {\n async create(key, o) {\n const res = await client.send(client.objectUrl(key, { uploads: '' }), {\n method: 'POST',\n headers: uploadHeaders({ contentType: o.contentType, metadata: o.metadata }, false),\n });\n const xml = await res.text();\n if (!res.ok) throw parseErrorBody(res.status, xml, key);\n const uploadId = tagText(xml, 'UploadId');\n if (!uploadId) throw new StorageError('Parse', 'missing UploadId');\n return { uploadId, partSize: o.partSize ?? DEFAULT_PART_SIZE };\n },\n async signPart(key, uploadId, partNumber, o) {\n const url = await client.presign(key, 'PUT', o?.expiresIn ?? expires, {\n partNumber: String(partNumber),\n uploadId,\n });\n return { url };\n },\n complete(key, uploadId, parts) {\n return multipartHandle(key, uploadId).complete(parts);\n },\n abort(key, uploadId) {\n return multipartHandle(key, uploadId).abort();\n },\n };\n\n return {\n name: options.name ?? 's3',\n raw: client,\n supportsRange: true,\n supportsDelimiter: true,\n supportsMetadata: true,\n supportsCacheControl: true,\n supportsServerSideCopy: true,\n reportsUploadProgress: false,\n signedUrl: { supported: true, upload: true },\n\n async upload(key: string, body: Body, opts?: UploadOptions): Promise<UploadResult> {\n const size = byteLengthOf(body) ?? 0;\n const res = await client.send(\n client.objectUrl(key),\n withDuplex(\n {\n method: 'PUT',\n headers: uploadHeaders(opts, true),\n body: body as BodyInit,\n signal: opts?.signal,\n },\n body,\n ),\n );\n if (!res.ok) throw await s3Error(res, key);\n return {\n key,\n size,\n contentType: opts?.contentType ?? 'application/octet-stream',\n etag: stripQuotes(res.headers.get('etag')),\n lastModified: parseDate(res.headers.get('last-modified')),\n };\n },\n\n async download(key: string, opts?: DownloadOptions): Promise<StoredFile> {\n const headers = new Headers();\n if (opts?.range) headers.set('Range', formatRange(opts.range));\n const res = await client.send(client.objectUrl(key), {\n method: 'GET',\n headers,\n signal: opts?.signal,\n });\n if (!res.ok || !res.body) {\n if (res.status === 404)\n throw new StorageError('NotFound', `not found: ${key}`, { status: 404 });\n throw await s3Error(res, key);\n }\n return createStoredFile(\n {\n key,\n size: Number(res.headers.get('content-length') ?? 0),\n type: res.headers.get('content-type') ?? 'application/octet-stream',\n etag: stripQuotes(res.headers.get('etag')),\n lastModified: parseDate(res.headers.get('last-modified')),\n metadata: metaFromHeaders(res.headers),\n },\n { kind: 'stream', stream: res.body },\n );\n },\n\n async head(key: string, opts?: OperationOptions): Promise<StoredFile> {\n const res = await client.send(client.objectUrl(key), {\n method: 'HEAD',\n signal: opts?.signal,\n });\n if (!res.ok) {\n if (res.status === 404)\n throw new StorageError('NotFound', `not found: ${key}`, { status: 404 });\n throw await s3Error(res, key);\n }\n return createStoredFile(\n {\n key,\n size: Number(res.headers.get('content-length') ?? 0),\n type: res.headers.get('content-type') ?? 'application/octet-stream',\n etag: stripQuotes(res.headers.get('etag')),\n lastModified: parseDate(res.headers.get('last-modified')),\n metadata: metaFromHeaders(res.headers),\n },\n { kind: 'lazy', fetch: () => client.send(client.objectUrl(key), { method: 'GET' }) },\n );\n },\n\n async exists(key: string, opts?: OperationOptions): Promise<boolean> {\n const res = await client.send(client.objectUrl(key), {\n method: 'HEAD',\n signal: opts?.signal,\n });\n if (res.status === 404) return false;\n if (!res.ok) throw await s3Error(res, key);\n return true;\n },\n\n async delete(key: string, opts?: OperationOptions): Promise<void> {\n const res = await client.send(client.objectUrl(key), {\n method: 'DELETE',\n signal: opts?.signal,\n });\n if (!res.ok && res.status !== 404) throw await s3Error(res, key);\n },\n\n async copy(from: string, to: string, opts?: OperationOptions): Promise<void> {\n const source = `/${client.bucket}/${from.split('/').map(encodeURIComponent).join('/')}`;\n const res = await client.send(client.objectUrl(to), {\n method: 'PUT',\n headers: new Headers({ 'x-amz-copy-source': source }),\n signal: opts?.signal,\n });\n const body = await res.text();\n if (!res.ok || body.includes('<Error')) throw parseErrorBody(res.status, body, to);\n },\n\n async list(opts?: ListOptions): Promise<ListResult> {\n const query: Record<string, string> = { 'list-type': '2' };\n if (opts?.prefix) query.prefix = opts.prefix;\n if (opts?.delimiter) query.delimiter = opts.delimiter;\n if (opts?.cursor) query['continuation-token'] = opts.cursor;\n if (opts?.limit) query['max-keys'] = String(opts.limit);\n const res = await client.send(client.objectUrl('', query), {\n method: 'GET',\n signal: opts?.signal,\n });\n const xml = await res.text();\n if (!res.ok) throw parseErrorBody(res.status, xml);\n\n const items = tagBlocks(xml, 'Contents').map((b) => {\n const k = tagText(b, 'Key') ?? '';\n return createStoredFile(\n {\n key: k,\n size: Number(tagText(b, 'Size') ?? 0),\n type: 'application/octet-stream',\n etag: stripQuotes(tagText(b, 'ETag')),\n lastModified: parseDate(tagText(b, 'LastModified')),\n },\n { kind: 'lazy', fetch: () => client.send(client.objectUrl(k), { method: 'GET' }) },\n );\n });\n const prefixes = tagBlocks(xml, 'CommonPrefixes')\n .map((b) => tagText(b, 'Prefix'))\n .filter((p): p is string => !!p);\n const cursor =\n tagText(xml, 'IsTruncated') === 'true' ? tagText(xml, 'NextContinuationToken') : undefined;\n return { items, prefixes: prefixes.length ? prefixes : undefined, cursor };\n },\n\n async url(key: string, opts?: UrlOptions): Promise<string> {\n const forceSign = !!opts?.responseContentDisposition;\n if (options.publicBaseUrl && !forceSign) return joinPublic(options.publicBaseUrl, key);\n const query = opts?.responseContentDisposition\n ? { 'response-content-disposition': opts.responseContentDisposition }\n : undefined;\n return client.presign(key, 'GET', opts?.expiresIn ?? expires, query);\n },\n\n async signedUploadUrl(key: string, opts: SignUploadOptions): Promise<SignedUpload> {\n if (opts.maxSize != null || opts.minSize != null) {\n const { url, fields } = await client.signPostPolicy(key, {\n expiresIn: opts.expiresIn,\n contentType: opts.contentType,\n maxSize: opts.maxSize,\n minSize: opts.minSize,\n });\n return { method: 'POST', url, fields };\n }\n const url = await client.presign(key, 'PUT', opts.expiresIn);\n return {\n method: 'PUT',\n url,\n headers: opts.contentType ? { 'content-type': opts.contentType } : undefined,\n };\n },\n\n async createMultipartUpload(\n key: string,\n opts?: CreateMultipartOptions,\n ): Promise<MultipartUpload> {\n const res = await client.send(client.objectUrl(key, { uploads: '' }), {\n method: 'POST',\n headers: uploadHeaders(opts, false),\n signal: opts?.signal,\n });\n const xml = await res.text();\n if (!res.ok) throw parseErrorBody(res.status, xml, key);\n const uploadId = tagText(xml, 'UploadId');\n if (!uploadId) throw new StorageError('Parse', 'missing UploadId');\n return multipartHandle(key, uploadId, opts);\n },\n\n resumeMultipartUpload(key: string, uploadId: string): MultipartUpload {\n return multipartHandle(key, uploadId);\n },\n\n signedMultipart,\n };\n}\n"],"mappings":"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"}