@pelatform/storage 1.0.1

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/s3.js ADDED
@@ -0,0 +1,799 @@
1
+ import {
2
+ loadS3Config
3
+ } from "./chunk-ZTZZCC52.js";
4
+ import {
5
+ buildPublicUrl,
6
+ getMimeType
7
+ } from "./chunk-KJMGBTTL.js";
8
+
9
+ // src/providers/s3.ts
10
+ import { S3Client } from "@aws-sdk/client-s3";
11
+
12
+ // src/operations/file-operations.ts
13
+ import {
14
+ CopyObjectCommand,
15
+ DeleteObjectCommand,
16
+ DeleteObjectsCommand,
17
+ GetObjectCommand,
18
+ HeadObjectCommand,
19
+ ListObjectsV2Command,
20
+ PutObjectCommand
21
+ } from "@aws-sdk/client-s3";
22
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
23
+ var FileOperations = class {
24
+ constructor(client, config) {
25
+ this.client = client;
26
+ this.config = config;
27
+ }
28
+ async upload(options) {
29
+ try {
30
+ const contentType = options.contentType || getMimeType(options.key);
31
+ const command = new PutObjectCommand({
32
+ Bucket: this.config.bucket,
33
+ Key: options.key,
34
+ Body: options.file,
35
+ ContentType: contentType,
36
+ Metadata: options.metadata,
37
+ CacheControl: options.cacheControl,
38
+ ContentDisposition: options.contentDisposition,
39
+ ACL: options.acl,
40
+ Expires: options.expires
41
+ });
42
+ const result = await this.client.send(command);
43
+ const publicUrl = this.getPublicUrl(options.key);
44
+ return {
45
+ success: true,
46
+ key: options.key,
47
+ url: publicUrl,
48
+ publicUrl,
49
+ etag: result.ETag?.replace(/"/g, ""),
50
+ size: typeof options.file === "string" ? Buffer.byteLength(options.file, "utf8") : options.file.length
51
+ };
52
+ } catch (error) {
53
+ return {
54
+ success: false,
55
+ error: error instanceof Error ? error.message : "Upload failed"
56
+ };
57
+ }
58
+ }
59
+ async download(options) {
60
+ try {
61
+ const command = new GetObjectCommand({
62
+ Bucket: this.config.bucket,
63
+ Key: options.key,
64
+ Range: options.range
65
+ });
66
+ const result = await this.client.send(command);
67
+ if (!result.Body) {
68
+ return {
69
+ success: false,
70
+ error: "No data received"
71
+ };
72
+ }
73
+ const chunks = [];
74
+ const reader = result.Body.transformToWebStream().getReader();
75
+ while (true) {
76
+ const { done, value } = await reader.read();
77
+ if (done) break;
78
+ chunks.push(value);
79
+ }
80
+ const data = Buffer.concat(chunks);
81
+ return {
82
+ success: true,
83
+ content: data,
84
+ // Main content field
85
+ data,
86
+ // Alias for backward compatibility
87
+ contentType: result.ContentType,
88
+ contentLength: result.ContentLength,
89
+ lastModified: result.LastModified,
90
+ etag: result.ETag?.replace(/"/g, ""),
91
+ metadata: result.Metadata
92
+ };
93
+ } catch (error) {
94
+ return {
95
+ success: false,
96
+ error: error instanceof Error ? error.message : "Download failed"
97
+ };
98
+ }
99
+ }
100
+ async delete(options) {
101
+ try {
102
+ const command = new DeleteObjectCommand({
103
+ Bucket: this.config.bucket,
104
+ Key: options.key
105
+ });
106
+ await this.client.send(command);
107
+ return {
108
+ success: true
109
+ };
110
+ } catch (error) {
111
+ return {
112
+ success: false,
113
+ error: error instanceof Error ? error.message : "Delete failed"
114
+ };
115
+ }
116
+ }
117
+ async batchDelete(options) {
118
+ try {
119
+ const command = new DeleteObjectsCommand({
120
+ Bucket: this.config.bucket,
121
+ Delete: {
122
+ Objects: options.keys.map((key) => ({ Key: key })),
123
+ Quiet: false
124
+ }
125
+ });
126
+ const result = await this.client.send(command);
127
+ const deleted = result.Deleted?.map((obj) => obj.Key).filter(Boolean);
128
+ const errors = result.Errors?.map((err) => ({
129
+ key: err.Key || "",
130
+ error: err.Message || "Unknown error"
131
+ })) || [];
132
+ return {
133
+ success: true,
134
+ deleted,
135
+ errors: errors.length > 0 ? errors : void 0
136
+ };
137
+ } catch (error) {
138
+ return {
139
+ success: false,
140
+ errors: options.keys.map((key) => ({
141
+ key,
142
+ error: error instanceof Error ? error.message : "Batch delete failed"
143
+ }))
144
+ };
145
+ }
146
+ }
147
+ async list(options = {}) {
148
+ try {
149
+ const command = new ListObjectsV2Command({
150
+ Bucket: this.config.bucket,
151
+ Prefix: options.prefix,
152
+ Delimiter: options.delimiter,
153
+ MaxKeys: options.maxKeys,
154
+ ContinuationToken: options.continuationToken
155
+ });
156
+ const result = await this.client.send(command);
157
+ const files = result.Contents?.map((obj) => ({
158
+ key: obj.Key,
159
+ size: obj.Size || 0,
160
+ lastModified: obj.LastModified || /* @__PURE__ */ new Date(),
161
+ etag: obj.ETag?.replace(/"/g, "") || "",
162
+ contentType: getMimeType(obj.Key)
163
+ })) || [];
164
+ return {
165
+ success: true,
166
+ files,
167
+ isTruncated: result.IsTruncated,
168
+ nextContinuationToken: result.NextContinuationToken,
169
+ commonPrefixes: result.CommonPrefixes?.map((cp) => cp.Prefix)
170
+ };
171
+ } catch (error) {
172
+ return {
173
+ success: false,
174
+ error: error instanceof Error ? error.message : "List failed"
175
+ };
176
+ }
177
+ }
178
+ async exists(key) {
179
+ try {
180
+ const command = new HeadObjectCommand({
181
+ Bucket: this.config.bucket,
182
+ Key: key
183
+ });
184
+ const result = await this.client.send(command);
185
+ return {
186
+ exists: true,
187
+ fileInfo: {
188
+ key,
189
+ size: result.ContentLength || 0,
190
+ lastModified: result.LastModified || /* @__PURE__ */ new Date(),
191
+ etag: result.ETag?.replace(/"/g, "") || "",
192
+ contentType: result.ContentType,
193
+ metadata: result.Metadata
194
+ }
195
+ };
196
+ } catch (error) {
197
+ if (error instanceof Error && error.name === "NotFound") {
198
+ return { exists: false };
199
+ }
200
+ return {
201
+ exists: false,
202
+ error: error instanceof Error ? error.message : "Check existence failed"
203
+ };
204
+ }
205
+ }
206
+ async copy(options) {
207
+ try {
208
+ const command = new CopyObjectCommand({
209
+ Bucket: this.config.bucket,
210
+ CopySource: `${this.config.bucket}/${options.sourceKey}`,
211
+ Key: options.destinationKey,
212
+ Metadata: options.metadata,
213
+ MetadataDirective: options.metadataDirective || "COPY"
214
+ });
215
+ const result = await this.client.send(command);
216
+ return {
217
+ success: true,
218
+ etag: result.CopyObjectResult?.ETag?.replace(/"/g, "")
219
+ };
220
+ } catch (error) {
221
+ return {
222
+ success: false,
223
+ error: error instanceof Error ? error.message : "Copy failed"
224
+ };
225
+ }
226
+ }
227
+ async move(options) {
228
+ try {
229
+ const copyResult = await this.copy({
230
+ sourceKey: options.sourceKey,
231
+ destinationKey: options.destinationKey,
232
+ metadata: options.metadata,
233
+ metadataDirective: options.metadataDirective
234
+ });
235
+ if (!copyResult.success) {
236
+ return {
237
+ success: false,
238
+ error: copyResult.error || "Move failed during copy operation"
239
+ };
240
+ }
241
+ const deleteResult = await this.delete({ key: options.sourceKey });
242
+ if (!deleteResult.success) {
243
+ return {
244
+ success: false,
245
+ error: deleteResult.error || "Move failed during delete operation"
246
+ };
247
+ }
248
+ return {
249
+ success: true,
250
+ etag: copyResult.etag
251
+ };
252
+ } catch (error) {
253
+ return {
254
+ success: false,
255
+ error: error instanceof Error ? error.message : "Move failed"
256
+ };
257
+ }
258
+ }
259
+ async duplicate(options) {
260
+ return this.copy(options);
261
+ }
262
+ async getPresignedUrl(options) {
263
+ try {
264
+ const expiresIn = options.expiresIn || 3600;
265
+ let command;
266
+ if (options.operation === "get") {
267
+ command = new GetObjectCommand({
268
+ Bucket: this.config.bucket,
269
+ Key: options.key
270
+ });
271
+ } else {
272
+ command = new PutObjectCommand({
273
+ Bucket: this.config.bucket,
274
+ Key: options.key,
275
+ ContentType: options.contentType
276
+ });
277
+ }
278
+ const url = await getSignedUrl(this.client, command, {
279
+ expiresIn
280
+ });
281
+ return {
282
+ success: true,
283
+ url,
284
+ expiresAt: new Date(Date.now() + expiresIn * 1e3)
285
+ };
286
+ } catch (error) {
287
+ return {
288
+ success: false,
289
+ error: error instanceof Error ? error.message : "Presigned URL generation failed"
290
+ };
291
+ }
292
+ }
293
+ getPublicUrl(key) {
294
+ if (this.config.publicUrl) {
295
+ return buildPublicUrl(this.config.publicUrl, this.config.bucket, key);
296
+ }
297
+ if (this.config.endpoint) {
298
+ return buildPublicUrl(this.config.endpoint, this.config.bucket, key);
299
+ }
300
+ return `https://${this.config.bucket}.s3.${this.config.region}.amazonaws.com/${key}`;
301
+ }
302
+ };
303
+
304
+ // src/operations/folder-operations.ts
305
+ import {
306
+ CopyObjectCommand as CopyObjectCommand2,
307
+ DeleteObjectsCommand as DeleteObjectsCommand2,
308
+ ListObjectsV2Command as ListObjectsV2Command2,
309
+ PutObjectCommand as PutObjectCommand2
310
+ } from "@aws-sdk/client-s3";
311
+ var FolderOperations = class {
312
+ constructor(client, config) {
313
+ this.client = client;
314
+ this.config = config;
315
+ }
316
+ async createFolder(options) {
317
+ try {
318
+ const folderPath = options.path.endsWith("/") ? options.path : `${options.path}/`;
319
+ const command = new PutObjectCommand2({
320
+ Bucket: this.config.bucket,
321
+ Key: folderPath,
322
+ Body: "",
323
+ ContentType: "application/x-directory"
324
+ });
325
+ await this.client.send(command);
326
+ return {
327
+ success: true,
328
+ path: folderPath
329
+ };
330
+ } catch (error) {
331
+ return {
332
+ success: false,
333
+ error: error instanceof Error ? error.message : "Create folder failed"
334
+ };
335
+ }
336
+ }
337
+ async deleteFolder(options) {
338
+ try {
339
+ const folderPath = options.path.endsWith("/") ? options.path : `${options.path}/`;
340
+ if (options.recursive) {
341
+ const listCommand = new ListObjectsV2Command2({
342
+ Bucket: this.config.bucket,
343
+ Prefix: folderPath
344
+ });
345
+ const listResult = await this.client.send(listCommand);
346
+ if (!listResult.Contents || listResult.Contents.length === 0) {
347
+ return {
348
+ success: true,
349
+ deletedFiles: []
350
+ };
351
+ }
352
+ const deleteCommand = new DeleteObjectsCommand2({
353
+ Bucket: this.config.bucket,
354
+ Delete: {
355
+ Objects: listResult.Contents.map((obj) => ({ Key: obj.Key })),
356
+ Quiet: false
357
+ }
358
+ });
359
+ const deleteResult = await this.client.send(deleteCommand);
360
+ const deletedFiles = deleteResult.Deleted?.map((obj) => obj.Key).filter(Boolean) || [];
361
+ return {
362
+ success: true,
363
+ deletedFiles
364
+ };
365
+ } else {
366
+ const deleteCommand = new DeleteObjectsCommand2({
367
+ Bucket: this.config.bucket,
368
+ Delete: {
369
+ Objects: [{ Key: folderPath }],
370
+ Quiet: false
371
+ }
372
+ });
373
+ await this.client.send(deleteCommand);
374
+ return {
375
+ success: true,
376
+ deletedFiles: [folderPath]
377
+ };
378
+ }
379
+ } catch (error) {
380
+ return {
381
+ success: false,
382
+ error: error instanceof Error ? error.message : "Delete folder failed"
383
+ };
384
+ }
385
+ }
386
+ async listFolders(options = {}) {
387
+ try {
388
+ const command = new ListObjectsV2Command2({
389
+ Bucket: this.config.bucket,
390
+ Prefix: options.prefix,
391
+ Delimiter: options.delimiter || "/",
392
+ MaxKeys: options.maxKeys,
393
+ ContinuationToken: options.continuationToken
394
+ });
395
+ const result = await this.client.send(command);
396
+ const folders = result.CommonPrefixes?.map((cp) => {
397
+ const path = cp.Prefix;
398
+ const name = path.split("/").filter(Boolean).pop() || "";
399
+ return {
400
+ name,
401
+ path,
402
+ size: 0,
403
+ // Will be calculated separately if needed
404
+ fileCount: 0,
405
+ // Will be calculated separately if needed
406
+ lastModified: /* @__PURE__ */ new Date()
407
+ };
408
+ }) || [];
409
+ const files = result.Contents?.map((obj) => ({
410
+ key: obj.Key,
411
+ size: obj.Size || 0,
412
+ lastModified: obj.LastModified || /* @__PURE__ */ new Date(),
413
+ etag: obj.ETag?.replace(/"/g, "") || "",
414
+ contentType: getMimeType(obj.Key)
415
+ })) || [];
416
+ return {
417
+ success: true,
418
+ folders,
419
+ files,
420
+ isTruncated: result.IsTruncated,
421
+ nextContinuationToken: result.NextContinuationToken
422
+ };
423
+ } catch (error) {
424
+ return {
425
+ success: false,
426
+ error: error instanceof Error ? error.message : "List folders failed"
427
+ };
428
+ }
429
+ }
430
+ async folderExists(path) {
431
+ try {
432
+ const folderPath = path.endsWith("/") ? path : `${path}/`;
433
+ const command = new ListObjectsV2Command2({
434
+ Bucket: this.config.bucket,
435
+ Prefix: folderPath,
436
+ MaxKeys: 1
437
+ });
438
+ const result = await this.client.send(command);
439
+ const exists = result.Contents && result.Contents.length > 0 || result.CommonPrefixes && result.CommonPrefixes.length > 0;
440
+ if (exists) {
441
+ const name = folderPath.split("/").filter(Boolean).pop() || "";
442
+ return {
443
+ exists: true,
444
+ folderInfo: {
445
+ name,
446
+ path: folderPath,
447
+ size: 0,
448
+ fileCount: 0,
449
+ lastModified: /* @__PURE__ */ new Date()
450
+ }
451
+ };
452
+ }
453
+ return { exists: false };
454
+ } catch (error) {
455
+ return {
456
+ exists: false,
457
+ error: error instanceof Error ? error.message : "Check folder existence failed"
458
+ };
459
+ }
460
+ }
461
+ async renameFolder(options) {
462
+ try {
463
+ const oldPath = options.oldPath.endsWith("/") ? options.oldPath : `${options.oldPath}/`;
464
+ const newPath = options.newPath.endsWith("/") ? options.newPath : `${options.newPath}/`;
465
+ const listCommand = new ListObjectsV2Command2({
466
+ Bucket: this.config.bucket,
467
+ Prefix: oldPath
468
+ });
469
+ const listResult = await this.client.send(listCommand);
470
+ if (!listResult.Contents || listResult.Contents.length === 0) {
471
+ return {
472
+ success: true,
473
+ movedFiles: []
474
+ };
475
+ }
476
+ const movedFiles = [];
477
+ for (const obj of listResult.Contents) {
478
+ const oldKey = obj.Key;
479
+ const newKey = oldKey.replace(oldPath, newPath);
480
+ const copyCommand = new CopyObjectCommand2({
481
+ Bucket: this.config.bucket,
482
+ Key: newKey,
483
+ CopySource: `${this.config.bucket}/${oldKey}`
484
+ });
485
+ await this.client.send(copyCommand);
486
+ movedFiles.push(newKey);
487
+ }
488
+ const deleteCommand = new DeleteObjectsCommand2({
489
+ Bucket: this.config.bucket,
490
+ Delete: {
491
+ Objects: listResult.Contents.map((obj) => ({ Key: obj.Key })),
492
+ Quiet: true
493
+ }
494
+ });
495
+ await this.client.send(deleteCommand);
496
+ return {
497
+ success: true,
498
+ movedFiles
499
+ };
500
+ } catch (error) {
501
+ return {
502
+ success: false,
503
+ error: error instanceof Error ? error.message : "Rename folder failed"
504
+ };
505
+ }
506
+ }
507
+ async copyFolder(options) {
508
+ try {
509
+ const sourcePath = options.sourcePath.endsWith("/") ? options.sourcePath : `${options.sourcePath}/`;
510
+ const destPath = options.destinationPath.endsWith("/") ? options.destinationPath : `${options.destinationPath}/`;
511
+ const listCommand = new ListObjectsV2Command2({
512
+ Bucket: this.config.bucket,
513
+ Prefix: sourcePath
514
+ });
515
+ const listResult = await this.client.send(listCommand);
516
+ if (!listResult.Contents || listResult.Contents.length === 0) {
517
+ return {
518
+ success: true,
519
+ copiedFiles: []
520
+ };
521
+ }
522
+ const copiedFiles = [];
523
+ for (const obj of listResult.Contents) {
524
+ const sourceKey = obj.Key;
525
+ const destKey = sourceKey.replace(sourcePath, destPath);
526
+ const copyCommand = new CopyObjectCommand2({
527
+ Bucket: this.config.bucket,
528
+ Key: destKey,
529
+ CopySource: `${this.config.bucket}/${sourceKey}`
530
+ });
531
+ await this.client.send(copyCommand);
532
+ copiedFiles.push(destKey);
533
+ }
534
+ return {
535
+ success: true,
536
+ copiedFiles
537
+ };
538
+ } catch (error) {
539
+ return {
540
+ success: false,
541
+ error: error instanceof Error ? error.message : "Copy folder failed"
542
+ };
543
+ }
544
+ }
545
+ };
546
+
547
+ // src/providers/s3.ts
548
+ var S3Provider = class {
549
+ client;
550
+ config;
551
+ fileOps;
552
+ folderOps;
553
+ constructor(config) {
554
+ this.config = config;
555
+ this.client = new S3Client({
556
+ region: config.region,
557
+ credentials: {
558
+ accessKeyId: config.accessKeyId,
559
+ secretAccessKey: config.secretAccessKey
560
+ },
561
+ endpoint: config.endpoint,
562
+ forcePathStyle: config.forcePathStyle || false
563
+ });
564
+ this.fileOps = new FileOperations(this.client, this.config);
565
+ this.folderOps = new FolderOperations(this.client, this.config);
566
+ }
567
+ // File operations
568
+ async upload(options) {
569
+ return this.fileOps.upload(options);
570
+ }
571
+ async download(options) {
572
+ return this.fileOps.download(options);
573
+ }
574
+ async delete(options) {
575
+ return this.fileOps.delete(options);
576
+ }
577
+ async batchDelete(options) {
578
+ return this.fileOps.batchDelete(options);
579
+ }
580
+ async list(options) {
581
+ return this.fileOps.list(options);
582
+ }
583
+ async exists(key) {
584
+ return this.fileOps.exists(key);
585
+ }
586
+ async copy(options) {
587
+ return this.fileOps.copy(options);
588
+ }
589
+ async move(options) {
590
+ return this.fileOps.move(options);
591
+ }
592
+ async duplicate(options) {
593
+ return this.fileOps.duplicate(options);
594
+ }
595
+ async getPresignedUrl(options) {
596
+ return this.fileOps.getPresignedUrl(options);
597
+ }
598
+ getPublicUrl(key) {
599
+ return this.fileOps.getPublicUrl(key);
600
+ }
601
+ // Folder operations
602
+ async createFolder(options) {
603
+ return this.folderOps.createFolder(options);
604
+ }
605
+ async deleteFolder(options) {
606
+ return this.folderOps.deleteFolder(options);
607
+ }
608
+ async listFolders(options) {
609
+ return this.folderOps.listFolders(options);
610
+ }
611
+ async folderExists(path) {
612
+ return this.folderOps.folderExists(path);
613
+ }
614
+ async renameFolder(options) {
615
+ return this.folderOps.renameFolder(options);
616
+ }
617
+ async copyFolder(options) {
618
+ return this.folderOps.copyFolder(options);
619
+ }
620
+ };
621
+
622
+ // src/services/s3.ts
623
+ var S3Service = class {
624
+ provider;
625
+ config;
626
+ constructor(config) {
627
+ this.config = config || loadS3Config();
628
+ this.provider = this.createProvider(this.config);
629
+ }
630
+ createProvider(config) {
631
+ switch (config.provider) {
632
+ case "aws":
633
+ case "cloudflare-r2":
634
+ case "minio":
635
+ case "digitalocean":
636
+ case "supabase":
637
+ case "custom":
638
+ return new S3Provider(config);
639
+ default:
640
+ throw new Error(`Unsupported S3 provider: ${config.provider}`);
641
+ }
642
+ }
643
+ // Utility methods
644
+ getConfig() {
645
+ return { ...this.config };
646
+ }
647
+ getBucket() {
648
+ return this.config.bucket;
649
+ }
650
+ getRegion() {
651
+ return this.config.region;
652
+ }
653
+ getProvider() {
654
+ return this.config.provider;
655
+ }
656
+ getPublicUrl(key) {
657
+ return this.provider.getPublicUrl(key);
658
+ }
659
+ // File operations
660
+ async upload(options) {
661
+ return this.provider.upload(options);
662
+ }
663
+ async download(options) {
664
+ return this.provider.download(options);
665
+ }
666
+ async delete(options) {
667
+ return this.provider.delete(options);
668
+ }
669
+ async batchDelete(options) {
670
+ return this.provider.batchDelete(options);
671
+ }
672
+ async list(options) {
673
+ return this.provider.list(options);
674
+ }
675
+ async exists(key) {
676
+ return this.provider.exists(key);
677
+ }
678
+ async copy(options) {
679
+ return this.provider.copy(options);
680
+ }
681
+ async move(options) {
682
+ return this.provider.move(options);
683
+ }
684
+ async duplicate(options) {
685
+ return this.provider.duplicate(options);
686
+ }
687
+ async getPresignedUrl(options) {
688
+ return this.provider.getPresignedUrl(options);
689
+ }
690
+ // Folder operations
691
+ async createFolder(options) {
692
+ return this.provider.createFolder(options);
693
+ }
694
+ async deleteFolder(options) {
695
+ return this.provider.deleteFolder(options);
696
+ }
697
+ async listFolders(options) {
698
+ return this.provider.listFolders(options);
699
+ }
700
+ async folderExists(path) {
701
+ return this.provider.folderExists(path);
702
+ }
703
+ async renameFolder(options) {
704
+ return this.provider.renameFolder(options);
705
+ }
706
+ async copyFolder(options) {
707
+ return this.provider.copyFolder(options);
708
+ }
709
+ // Convenience methods
710
+ async uploadFile(key, file, options) {
711
+ return this.upload({
712
+ key,
713
+ file,
714
+ ...options
715
+ });
716
+ }
717
+ async downloadFile(key) {
718
+ return this.download({ key });
719
+ }
720
+ async deleteFile(key) {
721
+ return this.delete({ key });
722
+ }
723
+ async deleteFiles(keys) {
724
+ return this.batchDelete({ keys });
725
+ }
726
+ async fileExists(key) {
727
+ const result = await this.exists(key);
728
+ return result.exists;
729
+ }
730
+ async copyFile(sourceKey, destinationKey, options) {
731
+ return this.copy({
732
+ sourceKey,
733
+ destinationKey,
734
+ ...options
735
+ });
736
+ }
737
+ async moveFile(sourceKey, destinationKey, options) {
738
+ return this.move({
739
+ sourceKey,
740
+ destinationKey,
741
+ ...options
742
+ });
743
+ }
744
+ async duplicateFile(sourceKey, destinationKey, options) {
745
+ return this.duplicate({
746
+ sourceKey,
747
+ destinationKey,
748
+ ...options
749
+ });
750
+ }
751
+ async renameFile(sourceKey, newKey, options) {
752
+ return this.moveFile(sourceKey, newKey, options);
753
+ }
754
+ async getDownloadUrl(key, expiresIn) {
755
+ return this.getPresignedUrl({
756
+ key,
757
+ operation: "get",
758
+ expiresIn
759
+ });
760
+ }
761
+ async getUploadUrl(key, contentType, expiresIn) {
762
+ return this.getPresignedUrl({
763
+ key,
764
+ operation: "put",
765
+ contentType,
766
+ expiresIn
767
+ });
768
+ }
769
+ // Folder convenience methods
770
+ async createFolderPath(path) {
771
+ return this.createFolder({ path });
772
+ }
773
+ async deleteFolderPath(path, recursive = false) {
774
+ return this.deleteFolder({ path, recursive });
775
+ }
776
+ async folderPathExists(path) {
777
+ const result = await this.folderExists(path);
778
+ return result.exists;
779
+ }
780
+ async renameFolderPath(oldPath, newPath) {
781
+ return this.renameFolder({ oldPath, newPath });
782
+ }
783
+ async copyFolderPath(sourcePath, destinationPath, recursive = true) {
784
+ return this.copyFolder({ sourcePath, destinationPath, recursive });
785
+ }
786
+ };
787
+
788
+ // src/s3.ts
789
+ function createS3(config) {
790
+ if (config) {
791
+ return new S3Service(config);
792
+ }
793
+ const envConfig = loadS3Config();
794
+ return new S3Service(envConfig);
795
+ }
796
+ export {
797
+ S3Service,
798
+ createS3
799
+ };