@parsrun/storage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1316 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/types.ts
23
+ import {
24
+ type,
25
+ fileMetadata,
26
+ uploadOptions,
27
+ signedUrlOptions,
28
+ listFilesOptions,
29
+ listFilesResult,
30
+ localStorageConfig,
31
+ s3StorageConfig,
32
+ r2StorageConfig,
33
+ gcsStorageConfig,
34
+ storageProviderConfig
35
+ } from "@parsrun/types";
36
+ var StorageError, StorageErrorCodes;
37
+ var init_types = __esm({
38
+ "src/types.ts"() {
39
+ "use strict";
40
+ StorageError = class extends Error {
41
+ constructor(message, code, statusCode, cause) {
42
+ super(message);
43
+ this.code = code;
44
+ this.statusCode = statusCode;
45
+ this.cause = cause;
46
+ this.name = "StorageError";
47
+ }
48
+ };
49
+ StorageErrorCodes = {
50
+ NOT_FOUND: "NOT_FOUND",
51
+ ACCESS_DENIED: "ACCESS_DENIED",
52
+ BUCKET_NOT_FOUND: "BUCKET_NOT_FOUND",
53
+ INVALID_KEY: "INVALID_KEY",
54
+ UPLOAD_FAILED: "UPLOAD_FAILED",
55
+ DOWNLOAD_FAILED: "DOWNLOAD_FAILED",
56
+ DELETE_FAILED: "DELETE_FAILED",
57
+ COPY_FAILED: "COPY_FAILED",
58
+ LIST_FAILED: "LIST_FAILED",
59
+ PRESIGN_FAILED: "PRESIGN_FAILED",
60
+ QUOTA_EXCEEDED: "QUOTA_EXCEEDED",
61
+ INVALID_CONFIG: "INVALID_CONFIG",
62
+ ADAPTER_NOT_AVAILABLE: "ADAPTER_NOT_AVAILABLE"
63
+ };
64
+ }
65
+ });
66
+
67
+ // src/adapters/memory.ts
68
+ var memory_exports = {};
69
+ __export(memory_exports, {
70
+ MemoryAdapter: () => MemoryAdapter,
71
+ createMemoryAdapter: () => createMemoryAdapter
72
+ });
73
+ function createMemoryAdapter(config) {
74
+ return new MemoryAdapter({ ...config, type: "memory" });
75
+ }
76
+ var MemoryAdapter;
77
+ var init_memory = __esm({
78
+ "src/adapters/memory.ts"() {
79
+ "use strict";
80
+ init_types();
81
+ MemoryAdapter = class {
82
+ type = "memory";
83
+ bucket;
84
+ files = /* @__PURE__ */ new Map();
85
+ maxSize;
86
+ currentSize = 0;
87
+ basePath;
88
+ constructor(config) {
89
+ this.bucket = config.bucket;
90
+ this.maxSize = config.maxSize ?? Infinity;
91
+ this.basePath = config.basePath ?? "";
92
+ }
93
+ getFullKey(key) {
94
+ return this.basePath ? `${this.basePath}/${key}` : key;
95
+ }
96
+ validateKey(key) {
97
+ if (!key || key.includes("..") || key.startsWith("/")) {
98
+ throw new StorageError(
99
+ `Invalid key: ${key}`,
100
+ StorageErrorCodes.INVALID_KEY
101
+ );
102
+ }
103
+ }
104
+ dataToUint8Array(data) {
105
+ if (data instanceof Uint8Array) {
106
+ return data;
107
+ }
108
+ if (typeof data === "string") {
109
+ return new TextEncoder().encode(data);
110
+ }
111
+ if (data instanceof Blob) {
112
+ return data.arrayBuffer().then((buffer) => new Uint8Array(buffer));
113
+ }
114
+ return this.streamToUint8Array(data);
115
+ }
116
+ async streamToUint8Array(stream) {
117
+ const reader = stream.getReader();
118
+ const chunks = [];
119
+ while (true) {
120
+ const { done, value } = await reader.read();
121
+ if (done) break;
122
+ chunks.push(value);
123
+ }
124
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
125
+ const result = new Uint8Array(totalLength);
126
+ let offset = 0;
127
+ for (const chunk of chunks) {
128
+ result.set(chunk, offset);
129
+ offset += chunk.length;
130
+ }
131
+ return result;
132
+ }
133
+ async upload(key, data, options) {
134
+ this.validateKey(key);
135
+ const fullKey = this.getFullKey(key);
136
+ const uint8Data = await this.dataToUint8Array(data);
137
+ const existingFile = this.files.get(fullKey);
138
+ const sizeDiff = uint8Data.length - (existingFile?.data.length ?? 0);
139
+ if (this.currentSize + sizeDiff > this.maxSize) {
140
+ throw new StorageError(
141
+ "Storage quota exceeded",
142
+ StorageErrorCodes.QUOTA_EXCEEDED
143
+ );
144
+ }
145
+ const metadata = {
146
+ key: fullKey,
147
+ size: uint8Data.length,
148
+ contentType: options?.contentType ?? this.guessContentType(key),
149
+ lastModified: /* @__PURE__ */ new Date(),
150
+ etag: this.generateEtag(uint8Data),
151
+ metadata: options?.metadata ?? void 0
152
+ };
153
+ this.files.set(fullKey, { data: uint8Data, metadata });
154
+ this.currentSize += sizeDiff;
155
+ return metadata;
156
+ }
157
+ async download(key, _options) {
158
+ this.validateKey(key);
159
+ const fullKey = this.getFullKey(key);
160
+ const file = this.files.get(fullKey);
161
+ if (!file) {
162
+ throw new StorageError(
163
+ `File not found: ${key}`,
164
+ StorageErrorCodes.NOT_FOUND,
165
+ 404
166
+ );
167
+ }
168
+ return file.data;
169
+ }
170
+ async downloadStream(key, _options) {
171
+ const data = await this.download(key);
172
+ return new ReadableStream({
173
+ start(controller) {
174
+ controller.enqueue(data);
175
+ controller.close();
176
+ }
177
+ });
178
+ }
179
+ async head(key) {
180
+ this.validateKey(key);
181
+ const fullKey = this.getFullKey(key);
182
+ const file = this.files.get(fullKey);
183
+ return file?.metadata ?? null;
184
+ }
185
+ async exists(key) {
186
+ this.validateKey(key);
187
+ const fullKey = this.getFullKey(key);
188
+ return this.files.has(fullKey);
189
+ }
190
+ async delete(key) {
191
+ this.validateKey(key);
192
+ const fullKey = this.getFullKey(key);
193
+ const file = this.files.get(fullKey);
194
+ if (file) {
195
+ this.currentSize -= file.data.length;
196
+ this.files.delete(fullKey);
197
+ }
198
+ return { success: true, key: fullKey };
199
+ }
200
+ async deleteMany(keys) {
201
+ const deleted = [];
202
+ const errors = [];
203
+ for (const key of keys) {
204
+ try {
205
+ await this.delete(key);
206
+ deleted.push(this.getFullKey(key));
207
+ } catch (err) {
208
+ errors.push({
209
+ key: this.getFullKey(key),
210
+ error: err instanceof Error ? err.message : "Unknown error"
211
+ });
212
+ }
213
+ }
214
+ return { deleted, errors };
215
+ }
216
+ async list(options) {
217
+ const prefix = options?.prefix ? this.getFullKey(options.prefix) : this.basePath;
218
+ const delimiter = options?.delimiter ?? "/";
219
+ const maxKeys = options?.maxKeys ?? 1e3;
220
+ const files = [];
221
+ const prefixSet = /* @__PURE__ */ new Set();
222
+ for (const [key, file] of this.files) {
223
+ if (prefix && !key.startsWith(prefix)) {
224
+ continue;
225
+ }
226
+ const relativePath = prefix ? key.slice(prefix.length) : key;
227
+ if (delimiter) {
228
+ const delimiterIndex = relativePath.indexOf(delimiter);
229
+ if (delimiterIndex !== -1) {
230
+ const commonPrefix = key.slice(0, prefix.length + delimiterIndex + 1);
231
+ prefixSet.add(commonPrefix);
232
+ continue;
233
+ }
234
+ }
235
+ files.push(file.metadata);
236
+ if (files.length >= maxKeys) {
237
+ break;
238
+ }
239
+ }
240
+ return {
241
+ files,
242
+ prefixes: Array.from(prefixSet),
243
+ isTruncated: files.length >= maxKeys,
244
+ continuationToken: void 0
245
+ };
246
+ }
247
+ async copy(sourceKey, destKey, options) {
248
+ this.validateKey(sourceKey);
249
+ this.validateKey(destKey);
250
+ const sourceFullKey = this.getFullKey(sourceKey);
251
+ const destFullKey = this.getFullKey(destKey);
252
+ const sourceFile = this.files.get(sourceFullKey);
253
+ if (!sourceFile) {
254
+ throw new StorageError(
255
+ `Source file not found: ${sourceKey}`,
256
+ StorageErrorCodes.NOT_FOUND,
257
+ 404
258
+ );
259
+ }
260
+ const newMetadata = options?.metadataDirective === "REPLACE" ? {
261
+ key: destFullKey,
262
+ size: sourceFile.data.length,
263
+ contentType: options.contentType ?? sourceFile.metadata.contentType,
264
+ lastModified: /* @__PURE__ */ new Date(),
265
+ etag: sourceFile.metadata.etag,
266
+ metadata: options.metadata ?? void 0
267
+ } : {
268
+ ...sourceFile.metadata,
269
+ key: destFullKey,
270
+ lastModified: /* @__PURE__ */ new Date()
271
+ };
272
+ this.files.set(destFullKey, {
273
+ data: sourceFile.data,
274
+ metadata: newMetadata
275
+ });
276
+ this.currentSize += sourceFile.data.length;
277
+ return newMetadata;
278
+ }
279
+ async move(sourceKey, destKey) {
280
+ const metadata = await this.copy(sourceKey, destKey);
281
+ await this.delete(sourceKey);
282
+ return metadata;
283
+ }
284
+ async getPresignedUrl(key, options) {
285
+ this.validateKey(key);
286
+ const fullKey = this.getFullKey(key);
287
+ const expiresIn = options?.expiresIn ?? 3600;
288
+ const expires = Date.now() + expiresIn * 1e3;
289
+ return `memory://${this.bucket}/${fullKey}?expires=${expires}`;
290
+ }
291
+ async getUploadUrl(key, options) {
292
+ return this.getPresignedUrl(key, options);
293
+ }
294
+ /**
295
+ * Clear all files (useful for testing)
296
+ */
297
+ clear() {
298
+ this.files.clear();
299
+ this.currentSize = 0;
300
+ }
301
+ /**
302
+ * Get current storage size
303
+ */
304
+ getSize() {
305
+ return this.currentSize;
306
+ }
307
+ /**
308
+ * Get file count
309
+ */
310
+ getFileCount() {
311
+ return this.files.size;
312
+ }
313
+ guessContentType(key) {
314
+ const ext = key.split(".").pop()?.toLowerCase();
315
+ const contentTypes = {
316
+ txt: "text/plain",
317
+ html: "text/html",
318
+ css: "text/css",
319
+ js: "application/javascript",
320
+ json: "application/json",
321
+ xml: "application/xml",
322
+ pdf: "application/pdf",
323
+ zip: "application/zip",
324
+ png: "image/png",
325
+ jpg: "image/jpeg",
326
+ jpeg: "image/jpeg",
327
+ gif: "image/gif",
328
+ webp: "image/webp",
329
+ svg: "image/svg+xml",
330
+ mp3: "audio/mpeg",
331
+ mp4: "video/mp4",
332
+ webm: "video/webm"
333
+ };
334
+ return contentTypes[ext ?? ""] ?? "application/octet-stream";
335
+ }
336
+ generateEtag(data) {
337
+ let hash = 0;
338
+ for (const byte of data) {
339
+ hash = (hash << 5) - hash + byte | 0;
340
+ }
341
+ return `"${Math.abs(hash).toString(16)}"`;
342
+ }
343
+ };
344
+ }
345
+ });
346
+
347
+ // src/adapters/s3.ts
348
+ var s3_exports = {};
349
+ __export(s3_exports, {
350
+ S3Adapter: () => S3Adapter,
351
+ createDOSpacesAdapter: () => createDOSpacesAdapter,
352
+ createS3Adapter: () => createS3Adapter
353
+ });
354
+ function createS3Adapter(config) {
355
+ return new S3Adapter(config);
356
+ }
357
+ function createDOSpacesAdapter(config) {
358
+ return new S3Adapter({
359
+ ...config,
360
+ type: "do-spaces",
361
+ endpoint: `https://${config.region}.digitaloceanspaces.com`,
362
+ bucket: config.spaceName ?? config.bucket
363
+ });
364
+ }
365
+ var S3Adapter;
366
+ var init_s3 = __esm({
367
+ "src/adapters/s3.ts"() {
368
+ "use strict";
369
+ init_types();
370
+ S3Adapter = class {
371
+ type;
372
+ bucket;
373
+ client = null;
374
+ config;
375
+ basePath;
376
+ constructor(config) {
377
+ this.type = config.type;
378
+ this.bucket = config.bucket;
379
+ this.config = config;
380
+ this.basePath = config.basePath ?? "";
381
+ }
382
+ /**
383
+ * Lazy load S3 client
384
+ */
385
+ async getClient() {
386
+ if (this.client) return this.client;
387
+ try {
388
+ const { S3Client } = await import("@aws-sdk/client-s3");
389
+ const clientConfig = {
390
+ region: this.config.region,
391
+ credentials: {
392
+ accessKeyId: this.config.accessKeyId,
393
+ secretAccessKey: this.config.secretAccessKey
394
+ }
395
+ };
396
+ if (this.config.endpoint) {
397
+ clientConfig.endpoint = this.config.endpoint;
398
+ }
399
+ if (this.config.forcePathStyle !== void 0) {
400
+ clientConfig.forcePathStyle = this.config.forcePathStyle;
401
+ }
402
+ this.client = new S3Client(clientConfig);
403
+ return this.client;
404
+ } catch {
405
+ throw new StorageError(
406
+ "AWS SDK not installed. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner",
407
+ StorageErrorCodes.ADAPTER_NOT_AVAILABLE
408
+ );
409
+ }
410
+ }
411
+ getFullKey(key) {
412
+ return this.basePath ? `${this.basePath}/${key}` : key;
413
+ }
414
+ validateKey(key) {
415
+ if (!key || key.includes("..") || key.startsWith("/")) {
416
+ throw new StorageError(
417
+ `Invalid key: ${key}`,
418
+ StorageErrorCodes.INVALID_KEY
419
+ );
420
+ }
421
+ }
422
+ async dataToBody(data) {
423
+ if (data instanceof Uint8Array || typeof data === "string") {
424
+ return data;
425
+ }
426
+ if (data instanceof Blob) {
427
+ const buffer = await data.arrayBuffer();
428
+ return new Uint8Array(buffer);
429
+ }
430
+ return data;
431
+ }
432
+ async upload(key, data, options) {
433
+ this.validateKey(key);
434
+ const fullKey = this.getFullKey(key);
435
+ const client = await this.getClient();
436
+ const { PutObjectCommand } = await import("@aws-sdk/client-s3");
437
+ const body = await this.dataToBody(data);
438
+ const params = {
439
+ Bucket: this.bucket,
440
+ Key: fullKey,
441
+ Body: body,
442
+ ContentType: options?.contentType,
443
+ ContentDisposition: options?.contentDisposition,
444
+ CacheControl: options?.cacheControl,
445
+ ContentEncoding: options?.contentEncoding,
446
+ Metadata: options?.metadata
447
+ };
448
+ if (options?.acl) {
449
+ params.ACL = options.acl;
450
+ }
451
+ try {
452
+ const result = await client.send(new PutObjectCommand(params));
453
+ let size = 0;
454
+ if (typeof body === "string") {
455
+ size = new TextEncoder().encode(body).length;
456
+ } else if (body instanceof Uint8Array) {
457
+ size = body.length;
458
+ }
459
+ return {
460
+ key: fullKey,
461
+ size,
462
+ contentType: options?.contentType ?? void 0,
463
+ lastModified: /* @__PURE__ */ new Date(),
464
+ etag: result.ETag ?? void 0,
465
+ metadata: options?.metadata ?? void 0
466
+ };
467
+ } catch (err) {
468
+ throw new StorageError(
469
+ `Upload failed: ${err instanceof Error ? err.message : "Unknown error"}`,
470
+ StorageErrorCodes.UPLOAD_FAILED,
471
+ void 0,
472
+ err
473
+ );
474
+ }
475
+ }
476
+ async download(key, options) {
477
+ this.validateKey(key);
478
+ const fullKey = this.getFullKey(key);
479
+ const client = await this.getClient();
480
+ const { GetObjectCommand } = await import("@aws-sdk/client-s3");
481
+ try {
482
+ const params = {
483
+ Bucket: this.bucket,
484
+ Key: fullKey
485
+ };
486
+ if (options?.rangeStart !== void 0 || options?.rangeEnd !== void 0) {
487
+ const start = options.rangeStart ?? 0;
488
+ const end = options.rangeEnd ?? "";
489
+ params.Range = `bytes=${start}-${end}`;
490
+ }
491
+ if (options?.ifNoneMatch) {
492
+ params.IfNoneMatch = options.ifNoneMatch;
493
+ }
494
+ if (options?.ifModifiedSince) {
495
+ params.IfModifiedSince = options.ifModifiedSince;
496
+ }
497
+ const result = await client.send(new GetObjectCommand(params));
498
+ if (!result.Body) {
499
+ throw new StorageError(
500
+ "Empty response body",
501
+ StorageErrorCodes.DOWNLOAD_FAILED
502
+ );
503
+ }
504
+ const stream = result.Body;
505
+ return this.streamToUint8Array(stream);
506
+ } catch (err) {
507
+ if (err instanceof Error && "name" in err && err.name === "NoSuchKey") {
508
+ throw new StorageError(
509
+ `File not found: ${key}`,
510
+ StorageErrorCodes.NOT_FOUND,
511
+ 404
512
+ );
513
+ }
514
+ throw new StorageError(
515
+ `Download failed: ${err instanceof Error ? err.message : "Unknown error"}`,
516
+ StorageErrorCodes.DOWNLOAD_FAILED,
517
+ void 0,
518
+ err
519
+ );
520
+ }
521
+ }
522
+ async downloadStream(key, options) {
523
+ this.validateKey(key);
524
+ const fullKey = this.getFullKey(key);
525
+ const client = await this.getClient();
526
+ const { GetObjectCommand } = await import("@aws-sdk/client-s3");
527
+ try {
528
+ const params = {
529
+ Bucket: this.bucket,
530
+ Key: fullKey
531
+ };
532
+ if (options?.rangeStart !== void 0 || options?.rangeEnd !== void 0) {
533
+ const start = options.rangeStart ?? 0;
534
+ const end = options.rangeEnd ?? "";
535
+ params.Range = `bytes=${start}-${end}`;
536
+ }
537
+ const result = await client.send(new GetObjectCommand(params));
538
+ if (!result.Body) {
539
+ throw new StorageError(
540
+ "Empty response body",
541
+ StorageErrorCodes.DOWNLOAD_FAILED
542
+ );
543
+ }
544
+ return result.Body;
545
+ } catch (err) {
546
+ if (err instanceof Error && "name" in err && err.name === "NoSuchKey") {
547
+ throw new StorageError(
548
+ `File not found: ${key}`,
549
+ StorageErrorCodes.NOT_FOUND,
550
+ 404
551
+ );
552
+ }
553
+ throw err;
554
+ }
555
+ }
556
+ async head(key) {
557
+ this.validateKey(key);
558
+ const fullKey = this.getFullKey(key);
559
+ const client = await this.getClient();
560
+ const { HeadObjectCommand } = await import("@aws-sdk/client-s3");
561
+ try {
562
+ const result = await client.send(
563
+ new HeadObjectCommand({
564
+ Bucket: this.bucket,
565
+ Key: fullKey
566
+ })
567
+ );
568
+ return {
569
+ key: fullKey,
570
+ size: result.ContentLength ?? 0,
571
+ contentType: result.ContentType ?? void 0,
572
+ lastModified: result.LastModified ?? void 0,
573
+ etag: result.ETag ?? void 0,
574
+ metadata: result.Metadata ?? void 0
575
+ };
576
+ } catch (err) {
577
+ if (err instanceof Error && "name" in err && (err.name === "NoSuchKey" || err.name === "NotFound")) {
578
+ return null;
579
+ }
580
+ throw err;
581
+ }
582
+ }
583
+ async exists(key) {
584
+ const metadata = await this.head(key);
585
+ return metadata !== null;
586
+ }
587
+ async delete(key) {
588
+ this.validateKey(key);
589
+ const fullKey = this.getFullKey(key);
590
+ const client = await this.getClient();
591
+ const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
592
+ try {
593
+ await client.send(
594
+ new DeleteObjectCommand({
595
+ Bucket: this.bucket,
596
+ Key: fullKey
597
+ })
598
+ );
599
+ return { success: true, key: fullKey };
600
+ } catch (err) {
601
+ throw new StorageError(
602
+ `Delete failed: ${err instanceof Error ? err.message : "Unknown error"}`,
603
+ StorageErrorCodes.DELETE_FAILED,
604
+ void 0,
605
+ err
606
+ );
607
+ }
608
+ }
609
+ async deleteMany(keys) {
610
+ const client = await this.getClient();
611
+ const { DeleteObjectsCommand } = await import("@aws-sdk/client-s3");
612
+ const objects = keys.map((key) => ({
613
+ Key: this.getFullKey(key)
614
+ }));
615
+ try {
616
+ const result = await client.send(
617
+ new DeleteObjectsCommand({
618
+ Bucket: this.bucket,
619
+ Delete: { Objects: objects }
620
+ })
621
+ );
622
+ const deleted = result.Deleted?.map((d) => d.Key ?? "") ?? [];
623
+ const errors = result.Errors?.map((e) => ({
624
+ key: e.Key ?? "",
625
+ error: e.Message ?? "Unknown error"
626
+ })) ?? [];
627
+ return { deleted, errors };
628
+ } catch (err) {
629
+ throw new StorageError(
630
+ `Batch delete failed: ${err instanceof Error ? err.message : "Unknown error"}`,
631
+ StorageErrorCodes.DELETE_FAILED,
632
+ void 0,
633
+ err
634
+ );
635
+ }
636
+ }
637
+ async list(options) {
638
+ const client = await this.getClient();
639
+ const { ListObjectsV2Command } = await import("@aws-sdk/client-s3");
640
+ const prefix = options?.prefix ? this.getFullKey(options.prefix) : this.basePath || void 0;
641
+ try {
642
+ const result = await client.send(
643
+ new ListObjectsV2Command({
644
+ Bucket: this.bucket,
645
+ Prefix: prefix,
646
+ Delimiter: options?.delimiter,
647
+ MaxKeys: options?.maxKeys,
648
+ ContinuationToken: options?.continuationToken
649
+ })
650
+ );
651
+ const files = result.Contents?.map((item) => ({
652
+ key: item.Key ?? "",
653
+ size: item.Size ?? 0,
654
+ contentType: void 0,
655
+ lastModified: item.LastModified ?? void 0,
656
+ etag: item.ETag ?? void 0,
657
+ metadata: void 0
658
+ })) ?? [];
659
+ const prefixes = result.CommonPrefixes?.map((p) => p.Prefix ?? "") ?? [];
660
+ return {
661
+ files,
662
+ prefixes,
663
+ isTruncated: result.IsTruncated ?? false,
664
+ continuationToken: result.NextContinuationToken ?? void 0
665
+ };
666
+ } catch (err) {
667
+ throw new StorageError(
668
+ `List failed: ${err instanceof Error ? err.message : "Unknown error"}`,
669
+ StorageErrorCodes.LIST_FAILED,
670
+ void 0,
671
+ err
672
+ );
673
+ }
674
+ }
675
+ async copy(sourceKey, destKey, options) {
676
+ this.validateKey(sourceKey);
677
+ this.validateKey(destKey);
678
+ const client = await this.getClient();
679
+ const { CopyObjectCommand } = await import("@aws-sdk/client-s3");
680
+ const sourceFullKey = this.getFullKey(sourceKey);
681
+ const destFullKey = this.getFullKey(destKey);
682
+ const sourceBucket = options?.sourceBucket ?? this.bucket;
683
+ try {
684
+ const result = await client.send(
685
+ new CopyObjectCommand({
686
+ Bucket: this.bucket,
687
+ Key: destFullKey,
688
+ CopySource: `${sourceBucket}/${sourceFullKey}`,
689
+ MetadataDirective: options?.metadataDirective,
690
+ ContentType: options?.contentType,
691
+ Metadata: options?.metadata
692
+ })
693
+ );
694
+ const metadata = await this.head(destKey);
695
+ return metadata ?? {
696
+ key: destFullKey,
697
+ size: 0,
698
+ contentType: options?.contentType ?? void 0,
699
+ lastModified: result.CopyObjectResult?.LastModified ?? /* @__PURE__ */ new Date(),
700
+ etag: result.CopyObjectResult?.ETag ?? void 0,
701
+ metadata: options?.metadata ?? void 0
702
+ };
703
+ } catch (err) {
704
+ throw new StorageError(
705
+ `Copy failed: ${err instanceof Error ? err.message : "Unknown error"}`,
706
+ StorageErrorCodes.COPY_FAILED,
707
+ void 0,
708
+ err
709
+ );
710
+ }
711
+ }
712
+ async move(sourceKey, destKey) {
713
+ const metadata = await this.copy(sourceKey, destKey);
714
+ await this.delete(sourceKey);
715
+ return metadata;
716
+ }
717
+ async getPresignedUrl(key, options) {
718
+ this.validateKey(key);
719
+ const fullKey = this.getFullKey(key);
720
+ const client = await this.getClient();
721
+ try {
722
+ const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
723
+ const { GetObjectCommand } = await import("@aws-sdk/client-s3");
724
+ const command = new GetObjectCommand({
725
+ Bucket: this.bucket,
726
+ Key: fullKey,
727
+ ResponseCacheControl: options?.responseCacheControl,
728
+ ResponseContentType: options?.responseContentType,
729
+ ResponseContentDisposition: options?.contentDisposition
730
+ });
731
+ return getSignedUrl(client, command, {
732
+ expiresIn: options?.expiresIn ?? 3600
733
+ });
734
+ } catch (err) {
735
+ throw new StorageError(
736
+ `Presigned URL failed: ${err instanceof Error ? err.message : "Unknown error"}`,
737
+ StorageErrorCodes.PRESIGN_FAILED,
738
+ void 0,
739
+ err
740
+ );
741
+ }
742
+ }
743
+ async getUploadUrl(key, options) {
744
+ this.validateKey(key);
745
+ const fullKey = this.getFullKey(key);
746
+ const client = await this.getClient();
747
+ try {
748
+ const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
749
+ const { PutObjectCommand } = await import("@aws-sdk/client-s3");
750
+ const command = new PutObjectCommand({
751
+ Bucket: this.bucket,
752
+ Key: fullKey,
753
+ ContentType: options?.contentType,
754
+ ContentDisposition: options?.contentDisposition
755
+ });
756
+ return getSignedUrl(client, command, {
757
+ expiresIn: options?.expiresIn ?? 3600
758
+ });
759
+ } catch (err) {
760
+ throw new StorageError(
761
+ `Upload URL failed: ${err instanceof Error ? err.message : "Unknown error"}`,
762
+ StorageErrorCodes.PRESIGN_FAILED,
763
+ void 0,
764
+ err
765
+ );
766
+ }
767
+ }
768
+ async streamToUint8Array(stream) {
769
+ const reader = stream.getReader();
770
+ const chunks = [];
771
+ while (true) {
772
+ const { done, value } = await reader.read();
773
+ if (done) break;
774
+ if (value) chunks.push(value);
775
+ }
776
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
777
+ const result = new Uint8Array(totalLength);
778
+ let offset = 0;
779
+ for (const chunk of chunks) {
780
+ result.set(chunk, offset);
781
+ offset += chunk.length;
782
+ }
783
+ return result;
784
+ }
785
+ };
786
+ }
787
+ });
788
+
789
+ // src/adapters/r2.ts
790
+ var r2_exports = {};
791
+ __export(r2_exports, {
792
+ R2Adapter: () => R2Adapter,
793
+ createR2Adapter: () => createR2Adapter
794
+ });
795
+ function createR2Adapter(config) {
796
+ return new R2Adapter(config);
797
+ }
798
+ var R2Adapter;
799
+ var init_r2 = __esm({
800
+ "src/adapters/r2.ts"() {
801
+ "use strict";
802
+ init_types();
803
+ R2Adapter = class {
804
+ type = "r2";
805
+ bucket;
806
+ binding = null;
807
+ s3Adapter = null;
808
+ config;
809
+ basePath;
810
+ constructor(config) {
811
+ this.bucket = config.bucket;
812
+ this.config = config;
813
+ this.basePath = config.basePath ?? "";
814
+ this.binding = config.binding ?? null;
815
+ }
816
+ /**
817
+ * Get S3 adapter for fallback
818
+ */
819
+ async getS3Adapter() {
820
+ if (this.s3Adapter) return this.s3Adapter;
821
+ const { S3Adapter: S3Adapter2 } = await Promise.resolve().then(() => (init_s3(), s3_exports));
822
+ this.s3Adapter = new S3Adapter2({
823
+ type: "s3",
824
+ bucket: this.bucket,
825
+ region: "auto",
826
+ accessKeyId: this.config.accessKeyId,
827
+ secretAccessKey: this.config.secretAccessKey,
828
+ endpoint: `https://${this.config.accountId}.r2.cloudflarestorage.com`
829
+ });
830
+ return this.s3Adapter;
831
+ }
832
+ getFullKey(key) {
833
+ return this.basePath ? `${this.basePath}/${key}` : key;
834
+ }
835
+ validateKey(key) {
836
+ if (!key || key.includes("..") || key.startsWith("/")) {
837
+ throw new StorageError(
838
+ `Invalid key: ${key}`,
839
+ StorageErrorCodes.INVALID_KEY
840
+ );
841
+ }
842
+ }
843
+ async dataToBody(data) {
844
+ if (data instanceof Uint8Array) {
845
+ const buffer = new ArrayBuffer(data.length);
846
+ new Uint8Array(buffer).set(data);
847
+ return buffer;
848
+ }
849
+ return data;
850
+ }
851
+ async upload(key, data, options) {
852
+ this.validateKey(key);
853
+ const fullKey = this.getFullKey(key);
854
+ if (this.binding) {
855
+ const body = await this.dataToBody(data);
856
+ const httpMetadata = {};
857
+ if (options?.contentType) httpMetadata.contentType = options.contentType;
858
+ if (options?.contentDisposition) httpMetadata.contentDisposition = options.contentDisposition;
859
+ if (options?.cacheControl) httpMetadata.cacheControl = options.cacheControl;
860
+ if (options?.contentEncoding) httpMetadata.contentEncoding = options.contentEncoding;
861
+ const putOptions = {
862
+ httpMetadata
863
+ };
864
+ if (options?.metadata) {
865
+ putOptions.customMetadata = options.metadata;
866
+ }
867
+ try {
868
+ const result = await this.binding.put(fullKey, body, putOptions);
869
+ return {
870
+ key: fullKey,
871
+ size: result.size,
872
+ contentType: result.httpMetadata?.contentType ?? void 0,
873
+ lastModified: result.uploaded,
874
+ etag: result.etag,
875
+ metadata: result.customMetadata ?? void 0
876
+ };
877
+ } catch (err) {
878
+ throw new StorageError(
879
+ `Upload failed: ${err instanceof Error ? err.message : "Unknown error"}`,
880
+ StorageErrorCodes.UPLOAD_FAILED,
881
+ void 0,
882
+ err
883
+ );
884
+ }
885
+ }
886
+ const s3 = await this.getS3Adapter();
887
+ return s3.upload(key, data, options);
888
+ }
889
+ async download(key, options) {
890
+ this.validateKey(key);
891
+ const fullKey = this.getFullKey(key);
892
+ if (this.binding) {
893
+ try {
894
+ const getOptions = {};
895
+ if (options?.rangeStart !== void 0 || options?.rangeEnd !== void 0) {
896
+ const rangeOpts = {
897
+ offset: options.rangeStart ?? 0
898
+ };
899
+ if (options.rangeEnd !== void 0) {
900
+ rangeOpts.length = options.rangeEnd - (options.rangeStart ?? 0) + 1;
901
+ }
902
+ getOptions.range = rangeOpts;
903
+ }
904
+ if (options?.ifNoneMatch || options?.ifModifiedSince) {
905
+ const conditional = {};
906
+ if (options.ifNoneMatch) conditional.etagDoesNotMatch = options.ifNoneMatch;
907
+ if (options.ifModifiedSince) conditional.uploadedAfter = options.ifModifiedSince;
908
+ getOptions.onlyIf = conditional;
909
+ }
910
+ const result = await this.binding.get(fullKey, getOptions);
911
+ if (!result) {
912
+ throw new StorageError(
913
+ `File not found: ${key}`,
914
+ StorageErrorCodes.NOT_FOUND,
915
+ 404
916
+ );
917
+ }
918
+ const buffer = await result.arrayBuffer();
919
+ return new Uint8Array(buffer);
920
+ } catch (err) {
921
+ if (err instanceof StorageError) throw err;
922
+ throw new StorageError(
923
+ `Download failed: ${err instanceof Error ? err.message : "Unknown error"}`,
924
+ StorageErrorCodes.DOWNLOAD_FAILED,
925
+ void 0,
926
+ err
927
+ );
928
+ }
929
+ }
930
+ const s3 = await this.getS3Adapter();
931
+ return s3.download(key, options);
932
+ }
933
+ async downloadStream(key, options) {
934
+ this.validateKey(key);
935
+ const fullKey = this.getFullKey(key);
936
+ if (this.binding) {
937
+ try {
938
+ const getOptions = {};
939
+ if (options?.rangeStart !== void 0 || options?.rangeEnd !== void 0) {
940
+ const rangeOpts = {
941
+ offset: options.rangeStart ?? 0
942
+ };
943
+ if (options.rangeEnd !== void 0) {
944
+ rangeOpts.length = options.rangeEnd - (options.rangeStart ?? 0) + 1;
945
+ }
946
+ getOptions.range = rangeOpts;
947
+ }
948
+ const result = await this.binding.get(fullKey, getOptions);
949
+ if (!result) {
950
+ throw new StorageError(
951
+ `File not found: ${key}`,
952
+ StorageErrorCodes.NOT_FOUND,
953
+ 404
954
+ );
955
+ }
956
+ return result.body;
957
+ } catch (err) {
958
+ if (err instanceof StorageError) throw err;
959
+ throw new StorageError(
960
+ `Download failed: ${err instanceof Error ? err.message : "Unknown error"}`,
961
+ StorageErrorCodes.DOWNLOAD_FAILED,
962
+ void 0,
963
+ err
964
+ );
965
+ }
966
+ }
967
+ const s3 = await this.getS3Adapter();
968
+ return s3.downloadStream(key, options);
969
+ }
970
+ async head(key) {
971
+ this.validateKey(key);
972
+ const fullKey = this.getFullKey(key);
973
+ if (this.binding) {
974
+ try {
975
+ const result = await this.binding.head(fullKey);
976
+ if (!result) {
977
+ return null;
978
+ }
979
+ return {
980
+ key: fullKey,
981
+ size: result.size,
982
+ contentType: result.httpMetadata?.contentType ?? void 0,
983
+ lastModified: result.uploaded,
984
+ etag: result.etag,
985
+ metadata: result.customMetadata ?? void 0
986
+ };
987
+ } catch {
988
+ return null;
989
+ }
990
+ }
991
+ const s3 = await this.getS3Adapter();
992
+ return s3.head(key);
993
+ }
994
+ async exists(key) {
995
+ const metadata = await this.head(key);
996
+ return metadata !== null;
997
+ }
998
+ async delete(key) {
999
+ this.validateKey(key);
1000
+ const fullKey = this.getFullKey(key);
1001
+ if (this.binding) {
1002
+ try {
1003
+ await this.binding.delete(fullKey);
1004
+ return { success: true, key: fullKey };
1005
+ } catch (err) {
1006
+ throw new StorageError(
1007
+ `Delete failed: ${err instanceof Error ? err.message : "Unknown error"}`,
1008
+ StorageErrorCodes.DELETE_FAILED,
1009
+ void 0,
1010
+ err
1011
+ );
1012
+ }
1013
+ }
1014
+ const s3 = await this.getS3Adapter();
1015
+ return s3.delete(key);
1016
+ }
1017
+ async deleteMany(keys) {
1018
+ if (this.binding) {
1019
+ const fullKeys = keys.map((key) => this.getFullKey(key));
1020
+ try {
1021
+ await this.binding.delete(fullKeys);
1022
+ return { deleted: fullKeys, errors: [] };
1023
+ } catch (err) {
1024
+ return {
1025
+ deleted: [],
1026
+ errors: fullKeys.map((key) => ({
1027
+ key,
1028
+ error: err instanceof Error ? err.message : "Unknown error"
1029
+ }))
1030
+ };
1031
+ }
1032
+ }
1033
+ const s3 = await this.getS3Adapter();
1034
+ return s3.deleteMany(keys);
1035
+ }
1036
+ async list(options) {
1037
+ if (this.binding) {
1038
+ try {
1039
+ const listOpts = {
1040
+ include: ["httpMetadata", "customMetadata"]
1041
+ };
1042
+ const prefix = options?.prefix ? this.getFullKey(options.prefix) : this.basePath || null;
1043
+ if (prefix) listOpts.prefix = prefix;
1044
+ if (options?.delimiter) listOpts.delimiter = options.delimiter;
1045
+ if (options?.maxKeys) listOpts.limit = options.maxKeys;
1046
+ if (options?.continuationToken) listOpts.cursor = options.continuationToken;
1047
+ const result = await this.binding.list(listOpts);
1048
+ const files = result.objects.map((obj) => ({
1049
+ key: obj.key,
1050
+ size: obj.size,
1051
+ contentType: obj.httpMetadata?.contentType ?? void 0,
1052
+ lastModified: obj.uploaded,
1053
+ etag: obj.etag,
1054
+ metadata: obj.customMetadata ?? void 0
1055
+ }));
1056
+ return {
1057
+ files,
1058
+ prefixes: result.delimitedPrefixes,
1059
+ isTruncated: result.truncated,
1060
+ continuationToken: result.cursor ?? void 0
1061
+ };
1062
+ } catch (err) {
1063
+ throw new StorageError(
1064
+ `List failed: ${err instanceof Error ? err.message : "Unknown error"}`,
1065
+ StorageErrorCodes.LIST_FAILED,
1066
+ void 0,
1067
+ err
1068
+ );
1069
+ }
1070
+ }
1071
+ const s3 = await this.getS3Adapter();
1072
+ return s3.list(options);
1073
+ }
1074
+ async copy(sourceKey, destKey, options) {
1075
+ if (this.binding) {
1076
+ const data = await this.download(sourceKey);
1077
+ const sourceMetadata = await this.head(sourceKey);
1078
+ const uploadOptions2 = options?.metadataDirective === "REPLACE" ? {
1079
+ contentType: options.contentType,
1080
+ metadata: options.metadata
1081
+ } : {
1082
+ contentType: sourceMetadata?.contentType,
1083
+ metadata: sourceMetadata?.metadata
1084
+ };
1085
+ return this.upload(destKey, data, uploadOptions2);
1086
+ }
1087
+ const s3 = await this.getS3Adapter();
1088
+ return s3.copy(sourceKey, destKey, options);
1089
+ }
1090
+ async move(sourceKey, destKey) {
1091
+ const metadata = await this.copy(sourceKey, destKey);
1092
+ await this.delete(sourceKey);
1093
+ return metadata;
1094
+ }
1095
+ async getPresignedUrl(key, options) {
1096
+ const s3 = await this.getS3Adapter();
1097
+ return s3.getPresignedUrl(key, options);
1098
+ }
1099
+ async getUploadUrl(key, options) {
1100
+ const s3 = await this.getS3Adapter();
1101
+ return s3.getUploadUrl(key, options);
1102
+ }
1103
+ /**
1104
+ * Get public URL for a file (if custom domain is configured)
1105
+ */
1106
+ getPublicUrl(key) {
1107
+ if (!this.config.customDomain) {
1108
+ return null;
1109
+ }
1110
+ const fullKey = this.getFullKey(key);
1111
+ return `https://${this.config.customDomain}/${fullKey}`;
1112
+ }
1113
+ };
1114
+ }
1115
+ });
1116
+
1117
+ // src/index.ts
1118
+ init_types();
1119
+ init_memory();
1120
+ init_s3();
1121
+ init_r2();
1122
+ init_types();
1123
+ async function createStorage(config) {
1124
+ switch (config.type) {
1125
+ case "memory": {
1126
+ const { MemoryAdapter: MemoryAdapter2 } = await Promise.resolve().then(() => (init_memory(), memory_exports));
1127
+ return new MemoryAdapter2(config);
1128
+ }
1129
+ case "s3":
1130
+ case "do-spaces": {
1131
+ const { S3Adapter: S3Adapter2 } = await Promise.resolve().then(() => (init_s3(), s3_exports));
1132
+ return new S3Adapter2(config);
1133
+ }
1134
+ case "r2": {
1135
+ const { R2Adapter: R2Adapter2 } = await Promise.resolve().then(() => (init_r2(), r2_exports));
1136
+ return new R2Adapter2(config);
1137
+ }
1138
+ default:
1139
+ throw new StorageError(
1140
+ `Unknown storage type: ${config.type}`,
1141
+ StorageErrorCodes.INVALID_CONFIG
1142
+ );
1143
+ }
1144
+ }
1145
+ function createStorageSync(config) {
1146
+ switch (config.type) {
1147
+ case "memory": {
1148
+ const { MemoryAdapter: MemoryAdapter2 } = (init_memory(), __toCommonJS(memory_exports));
1149
+ return new MemoryAdapter2(config);
1150
+ }
1151
+ case "s3":
1152
+ case "do-spaces": {
1153
+ const { S3Adapter: S3Adapter2 } = (init_s3(), __toCommonJS(s3_exports));
1154
+ return new S3Adapter2(config);
1155
+ }
1156
+ case "r2": {
1157
+ const { R2Adapter: R2Adapter2 } = (init_r2(), __toCommonJS(r2_exports));
1158
+ return new R2Adapter2(config);
1159
+ }
1160
+ default:
1161
+ throw new StorageError(
1162
+ `Unknown storage type: ${config.type}`,
1163
+ StorageErrorCodes.INVALID_CONFIG
1164
+ );
1165
+ }
1166
+ }
1167
+ var StorageUtils = {
1168
+ /**
1169
+ * Get file extension from key
1170
+ */
1171
+ getExtension(key) {
1172
+ const parts = key.split(".");
1173
+ const lastPart = parts[parts.length - 1];
1174
+ return parts.length > 1 && lastPart ? lastPart.toLowerCase() : "";
1175
+ },
1176
+ /**
1177
+ * Get file name from key
1178
+ */
1179
+ getFileName(key) {
1180
+ return key.split("/").pop() ?? key;
1181
+ },
1182
+ /**
1183
+ * Get directory from key
1184
+ */
1185
+ getDirectory(key) {
1186
+ const parts = key.split("/");
1187
+ parts.pop();
1188
+ return parts.join("/");
1189
+ },
1190
+ /**
1191
+ * Join paths safely
1192
+ */
1193
+ joinPath(...parts) {
1194
+ return parts.filter(Boolean).map((p) => p.replace(/^\/|\/$/g, "")).join("/");
1195
+ },
1196
+ /**
1197
+ * Normalize key (remove leading slash, handle ..)
1198
+ */
1199
+ normalizeKey(key) {
1200
+ return key.replace(/^\/+/, "").split("/").filter((p) => p !== ".." && p !== ".").join("/");
1201
+ },
1202
+ /**
1203
+ * Generate a unique key with timestamp
1204
+ */
1205
+ generateUniqueKey(prefix, extension) {
1206
+ const timestamp = Date.now();
1207
+ const random = Math.random().toString(36).slice(2, 10);
1208
+ const ext = extension ? `.${extension}` : "";
1209
+ return `${prefix}/${timestamp}-${random}${ext}`;
1210
+ },
1211
+ /**
1212
+ * Guess content type from file extension
1213
+ */
1214
+ guessContentType(key) {
1215
+ const ext = StorageUtils.getExtension(key);
1216
+ const contentTypes = {
1217
+ // Text
1218
+ txt: "text/plain",
1219
+ html: "text/html",
1220
+ htm: "text/html",
1221
+ css: "text/css",
1222
+ csv: "text/csv",
1223
+ xml: "text/xml",
1224
+ // Application
1225
+ js: "application/javascript",
1226
+ mjs: "application/javascript",
1227
+ json: "application/json",
1228
+ pdf: "application/pdf",
1229
+ zip: "application/zip",
1230
+ gzip: "application/gzip",
1231
+ gz: "application/gzip",
1232
+ tar: "application/x-tar",
1233
+ doc: "application/msword",
1234
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1235
+ xls: "application/vnd.ms-excel",
1236
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1237
+ ppt: "application/vnd.ms-powerpoint",
1238
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
1239
+ // Images
1240
+ png: "image/png",
1241
+ jpg: "image/jpeg",
1242
+ jpeg: "image/jpeg",
1243
+ gif: "image/gif",
1244
+ webp: "image/webp",
1245
+ svg: "image/svg+xml",
1246
+ ico: "image/x-icon",
1247
+ bmp: "image/bmp",
1248
+ tiff: "image/tiff",
1249
+ tif: "image/tiff",
1250
+ // Audio
1251
+ mp3: "audio/mpeg",
1252
+ wav: "audio/wav",
1253
+ ogg: "audio/ogg",
1254
+ flac: "audio/flac",
1255
+ aac: "audio/aac",
1256
+ // Video
1257
+ mp4: "video/mp4",
1258
+ webm: "video/webm",
1259
+ avi: "video/x-msvideo",
1260
+ mov: "video/quicktime",
1261
+ mkv: "video/x-matroska",
1262
+ // Fonts
1263
+ woff: "font/woff",
1264
+ woff2: "font/woff2",
1265
+ ttf: "font/ttf",
1266
+ otf: "font/otf",
1267
+ eot: "application/vnd.ms-fontobject"
1268
+ };
1269
+ return contentTypes[ext] ?? "application/octet-stream";
1270
+ },
1271
+ /**
1272
+ * Format file size to human readable string
1273
+ */
1274
+ formatSize(bytes) {
1275
+ const units = ["B", "KB", "MB", "GB", "TB"];
1276
+ let size = bytes;
1277
+ let unitIndex = 0;
1278
+ while (size >= 1024 && unitIndex < units.length - 1) {
1279
+ size /= 1024;
1280
+ unitIndex++;
1281
+ }
1282
+ return `${size.toFixed(unitIndex > 0 ? 2 : 0)} ${units[unitIndex]}`;
1283
+ },
1284
+ /**
1285
+ * Parse file size string to bytes
1286
+ */
1287
+ parseSize(size) {
1288
+ const units = {
1289
+ b: 1,
1290
+ kb: 1024,
1291
+ mb: 1024 * 1024,
1292
+ gb: 1024 * 1024 * 1024,
1293
+ tb: 1024 * 1024 * 1024 * 1024
1294
+ };
1295
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([a-z]+)?$/);
1296
+ if (!match || !match[1]) return 0;
1297
+ const value = parseFloat(match[1]);
1298
+ const unit = match[2] ?? "b";
1299
+ return Math.floor(value * (units[unit] ?? 1));
1300
+ }
1301
+ };
1302
+ export {
1303
+ MemoryAdapter,
1304
+ R2Adapter,
1305
+ S3Adapter,
1306
+ StorageError,
1307
+ StorageErrorCodes,
1308
+ StorageUtils,
1309
+ createDOSpacesAdapter,
1310
+ createMemoryAdapter,
1311
+ createR2Adapter,
1312
+ createS3Adapter,
1313
+ createStorage,
1314
+ createStorageSync
1315
+ };
1316
+ //# sourceMappingURL=index.js.map