@pelatform/storage 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -1,10 +1,258 @@
1
1
  # @pelatform/storage
2
2
 
3
- A comprehensive storage utilities for SaaS applications. This package provides a unified interface for working with various storage providers including AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, Supabase, and Cloudinary Storage.
4
-
5
3
  [![Version](https://img.shields.io/npm/v/@pelatform/storage.svg)](https://www.npmjs.com/package/@pelatform/storage)
6
- [![License](https://img.shields.io/npm/l/@pelatform/storage.svg)](https://github.com/devpelatform/storage/blob/main/LICENSE)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A comprehensive storage utilities package for SaaS applications. This package provides a unified interface for working with various storage providers including AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, Supabase Storage, and Cloudinary.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @pelatform/storage
12
+ # or
13
+ bun add @pelatform/storage
14
+
15
+ # Install peer dependencies for the provider(s) you need
16
+ npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner # For S3-compatible providers
17
+ npm install cloudinary # For Cloudinary
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### S3-Compatible Storage (AWS, R2, MinIO, etc.)
23
+
24
+ ```typescript
25
+ import { S3Storage } from "@pelatform/storage/s3";
26
+
27
+ const storage = new S3Storage({
28
+ provider: "aws", // or 'cloudflare-r2', 'minio', 'digitalocean', 'supabase'
29
+ region: "us-east-1",
30
+ bucket: "my-bucket",
31
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
32
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
33
+ });
34
+
35
+ // Upload a file
36
+ await storage.upload({
37
+ key: "documents/file.pdf",
38
+ file: fileBuffer,
39
+ contentType: "application/pdf",
40
+ });
41
+
42
+ // Download a file
43
+ const result = await storage.download({ key: "documents/file.pdf" });
44
+
45
+ // Generate a presigned URL (temporary access)
46
+ const url = await storage.getPresignedUrl({
47
+ key: "documents/file.pdf",
48
+ operation: "get",
49
+ expiresIn: 3600, // 1 hour
50
+ });
51
+ ```
52
+
53
+ ### Cloudinary Storage
54
+
55
+ ```typescript
56
+ import { CloudinaryStorage } from "@pelatform/storage/cloudinary";
57
+
58
+ const storage = new CloudinaryStorage({
59
+ provider: "cloudinary",
60
+ cloudName: process.env.CLOUDINARY_CLOUD_NAME,
61
+ apiKey: process.env.CLOUDINARY_API_KEY,
62
+ apiSecret: process.env.CLOUDINARY_API_SECRET,
63
+ });
64
+
65
+ // Upload an image
66
+ await storage.upload({
67
+ key: "images/photo.jpg",
68
+ file: imageBuffer,
69
+ contentType: "image/jpeg",
70
+ });
71
+ ```
72
+
73
+ ## Key Features
74
+
75
+ - **Unified Interface**: Single API for multiple storage providers
76
+ - **S3-Compatible Providers**: AWS S3, Cloudflare R2, MinIO, DigitalOcean Spaces, Supabase Storage
77
+ - **Cloudinary Support**: Image and media management with built-in optimizations
78
+ - **File Operations**: Upload, download, delete, copy, move, and check existence
79
+ - **Folder Operations**: Create, delete, list, and manage folders
80
+ - **Presigned URLs**: Generate time-limited access URLs for secure sharing
81
+ - **Public URLs**: Get public access URLs with optional CDN support
82
+ - **Type-Safe**: Full TypeScript support with comprehensive types
83
+ - **Flexible Configuration**: Support for custom endpoints and CDN URLs
84
+
85
+ ## Supported Providers
86
+
87
+ ### S3-Compatible
88
+
89
+ - **AWS S3** - Amazon's object storage service
90
+ - **Cloudflare R2** - Cloudflare's S3-compatible storage (no egress fees)
91
+ - **MinIO** - Self-hosted S3-compatible storage
92
+ - **DigitalOcean Spaces** - DigitalOcean's object storage
93
+ - **Supabase Storage** - Supabase's S3-compatible storage
94
+
95
+ ### Other Providers
96
+
97
+ - **Cloudinary** - Image and video management platform
98
+
99
+ ## Configuration
100
+
101
+ ### AWS S3
102
+
103
+ ```typescript
104
+ const storage = new S3Storage({
105
+ provider: "aws",
106
+ region: "us-east-1",
107
+ bucket: "my-bucket",
108
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
109
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
110
+ publicUrl: "https://cdn.example.com", // Optional CDN URL
111
+ });
112
+ ```
113
+
114
+ ### Cloudflare R2
115
+
116
+ ```typescript
117
+ const storage = new S3Storage({
118
+ provider: "cloudflare-r2",
119
+ region: "auto",
120
+ bucket: "my-bucket",
121
+ accessKeyId: process.env.R2_ACCESS_KEY_ID,
122
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
123
+ endpoint: "https://<account-id>.r2.cloudflarestorage.com",
124
+ });
125
+ ```
126
+
127
+ ### MinIO
128
+
129
+ ```typescript
130
+ const storage = new S3Storage({
131
+ provider: "minio",
132
+ region: "us-east-1",
133
+ bucket: "my-bucket",
134
+ accessKeyId: "minioadmin",
135
+ secretAccessKey: "minioadmin",
136
+ endpoint: "http://localhost:9000",
137
+ forcePathStyle: true, // Required for MinIO
138
+ });
139
+ ```
140
+
141
+ ## Basic Usage
142
+
143
+ ### File Operations
144
+
145
+ ```typescript
146
+ // Upload with options
147
+ await storage.upload({
148
+ key: "uploads/document.pdf",
149
+ file: buffer,
150
+ contentType: "application/pdf",
151
+ metadata: { userId: "123", category: "documents" },
152
+ cacheControl: "max-age=31536000",
153
+ });
154
+
155
+ // Download file
156
+ const { content, metadata } = await storage.download({
157
+ key: "uploads/document.pdf",
158
+ });
159
+
160
+ // Check if file exists
161
+ const exists = await storage.exists("uploads/document.pdf");
162
+
163
+ // Delete file
164
+ await storage.delete({ key: "uploads/document.pdf" });
165
+
166
+ // Copy file
167
+ await storage.copy({
168
+ sourceKey: "uploads/old.pdf",
169
+ destinationKey: "uploads/new.pdf",
170
+ });
171
+
172
+ // Move file
173
+ await storage.move({
174
+ sourceKey: "uploads/temp.pdf",
175
+ destinationKey: "uploads/final.pdf",
176
+ });
177
+
178
+ // List files
179
+ const files = await storage.list({
180
+ prefix: "uploads/",
181
+ maxKeys: 100,
182
+ });
183
+ ```
184
+
185
+ ### Folder Operations
186
+
187
+ ```typescript
188
+ // Create folder
189
+ await storage.createFolder({ path: "documents/" });
190
+
191
+ // Delete folder (recursive)
192
+ await storage.deleteFolder({
193
+ path: "documents/",
194
+ recursive: true,
195
+ });
196
+
197
+ // List folders
198
+ const folders = await storage.listFolders({
199
+ prefix: "uploads/",
200
+ });
201
+
202
+ // Check if folder exists
203
+ const exists = await storage.folderExists("documents/");
204
+ ```
205
+
206
+ ### URL Generation
207
+
208
+ ```typescript
209
+ // Get public URL
210
+ const publicUrl = storage.getPublicUrl("images/photo.jpg");
211
+
212
+ // Generate presigned URL for download
213
+ const downloadUrl = await storage.getPresignedUrl({
214
+ key: "documents/private.pdf",
215
+ operation: "get",
216
+ expiresIn: 3600, // 1 hour
217
+ });
218
+
219
+ // Generate presigned URL for upload
220
+ const uploadUrl = await storage.getPresignedUrl({
221
+ key: "uploads/new-file.pdf",
222
+ operation: "put",
223
+ expiresIn: 900, // 15 minutes
224
+ contentType: "application/pdf",
225
+ });
226
+ ```
227
+
228
+ ## API Reference
229
+
230
+ ### File Operations
231
+
232
+ - `upload(options)` - Upload a file
233
+ - `download(options)` - Download a file
234
+ - `delete(options)` - Delete a file
235
+ - `exists(key)` - Check if file exists
236
+ - `copy(options)` - Copy a file
237
+ - `move(options)` - Move a file
238
+ - `list(options)` - List files with prefix
239
+ - `getPresignedUrl(options)` - Generate presigned URL
240
+ - `getPublicUrl(key)` - Get public URL
241
+
242
+ ### Folder Operations
243
+
244
+ - `createFolder(options)` - Create a folder
245
+ - `deleteFolder(options)` - Delete a folder
246
+ - `listFolders(options)` - List folders
247
+ - `folderExists(path)` - Check if folder exists
248
+
249
+ ## Links
250
+
251
+ - [npm Package](https://www.npmjs.com/package/@pelatform/storage)
252
+ - [Contributing Guide](../../CONTRIBUTING.md)
253
+ - [Code of Conduct](../../CODE_OF_CONDUCT.md)
254
+ - [License](../../LICENSE)
7
255
 
8
- ## Getting Started
256
+ ## License
9
257
 
10
- Visit https://devpelatform.github.io/storage to get started with this package.
258
+ MIT © [Pelatform Inc.](../../LICENSE)
@@ -31,11 +31,15 @@ function getRequiredEnvVar(key) {
31
31
  }
32
32
  function getBooleanEnvVar(key, defaultValue = false) {
33
33
  const value = getEnvVar(key);
34
- if (!value) return defaultValue;
34
+ if (!value) {
35
+ return defaultValue;
36
+ }
35
37
  return value.toLowerCase() === "true" || value === "1";
36
38
  }
37
39
  function loadS3Config() {
38
- const provider = getRequiredEnvVar(ENV_VARS.PELATFORM_S3_PROVIDER);
40
+ const provider = getRequiredEnvVar(
41
+ ENV_VARS.PELATFORM_S3_PROVIDER
42
+ );
39
43
  const validProviders = [
40
44
  "aws",
41
45
  "cloudflare-r2",
@@ -75,15 +79,15 @@ function loadStorageConfig() {
75
79
  if (provider) {
76
80
  if (provider === "cloudinary") {
77
81
  return loadCloudinaryConfig();
78
- } else {
79
- return loadS3Config();
80
82
  }
83
+ return loadS3Config();
81
84
  }
82
85
  const hasCloudinaryVars = getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_CLOUD_NAME) && getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_API_KEY) && getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_API_SECRET);
83
86
  const hasS3Vars = getEnvVar(ENV_VARS.PELATFORM_S3_BUCKET) && getEnvVar(ENV_VARS.PELATFORM_S3_ACCESS_KEY_ID) && getEnvVar(ENV_VARS.PELATFORM_S3_SECRET_ACCESS_KEY);
84
87
  if (hasCloudinaryVars) {
85
88
  return loadCloudinaryConfig();
86
- } else if (hasS3Vars) {
89
+ }
90
+ if (hasS3Vars) {
87
91
  const config = loadS3Config();
88
92
  if (!config.provider) {
89
93
  config.provider = "aws";
@@ -107,11 +111,17 @@ function isStorageConfigured() {
107
111
  }
108
112
  function getStorageProvider() {
109
113
  const provider = getEnvVar(ENV_VARS.PELATFORM_S3_PROVIDER);
110
- if (provider) return provider;
114
+ if (provider) {
115
+ return provider;
116
+ }
111
117
  const hasCloudinaryVars = getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_CLOUD_NAME);
112
118
  const hasS3Vars = getEnvVar(ENV_VARS.PELATFORM_S3_BUCKET);
113
- if (hasCloudinaryVars) return "cloudinary";
114
- if (hasS3Vars) return "aws";
119
+ if (hasCloudinaryVars) {
120
+ return "cloudinary";
121
+ }
122
+ if (hasS3Vars) {
123
+ return "aws";
124
+ }
115
125
  return void 0;
116
126
  }
117
127
  function validateS3EnvVars() {
@@ -26,13 +26,13 @@ function validateFileType(fileName, allowedTypes) {
26
26
  const extension = fileName.split(".").pop()?.toLowerCase();
27
27
  const isValidMime = allowedTypes.some((type) => {
28
28
  if (type.includes("*")) {
29
- const baseType = type.split("/")[0];
29
+ const baseType = type.split("/")[0] ?? "";
30
30
  return mimeType.startsWith(baseType);
31
31
  }
32
32
  return mimeType === type;
33
33
  });
34
34
  const isValidExtension = extension && allowedTypes.includes(`.${extension}`);
35
- if (!isValidMime && !isValidExtension) {
35
+ if (!(isValidMime || isValidExtension)) {
36
36
  return {
37
37
  valid: false,
38
38
  error: `File type not allowed. Allowed types: ${allowedTypes.join(", ")}`
@@ -41,11 +41,13 @@ function validateFileType(fileName, allowedTypes) {
41
41
  return { valid: true };
42
42
  }
43
43
  function formatFileSize(bytes) {
44
- if (bytes === 0) return "0 Bytes";
44
+ if (bytes === 0) {
45
+ return "0 Bytes";
46
+ }
45
47
  const k = 1024;
46
48
  const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
47
49
  const i = Math.floor(Math.log(bytes) / Math.log(k));
48
- return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
50
+ return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
49
51
  }
50
52
  function sanitizeFileName(fileName) {
51
53
  return fileName.replace(/[^a-zA-Z0-9.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "").toLowerCase();
@@ -68,11 +70,10 @@ function parseS3Url(url) {
68
70
  bucket: pathParts2[0],
69
71
  key: pathParts2.slice(1).join("/")
70
72
  };
71
- } else {
72
- const bucket = urlObj.hostname.split(".")[0];
73
- const key = urlObj.pathname.substring(1);
74
- return { bucket, key };
75
73
  }
74
+ const bucket = urlObj.hostname.split(".")[0];
75
+ const key = urlObj.pathname.substring(1);
76
+ return { bucket, key };
76
77
  }
77
78
  const pathParts = urlObj.pathname.split("/").filter(Boolean);
78
79
  return {
@@ -138,19 +139,18 @@ function extractS3Info(url) {
138
139
  if (urlObj.hostname.includes("amazonaws.com")) {
139
140
  if (urlObj.hostname.startsWith("s3")) {
140
141
  const pathParts2 = urlObj.pathname.split("/").filter(Boolean);
141
- const region = urlObj.hostname.split(".")[1];
142
+ const region2 = urlObj.hostname.split(".")[1];
142
143
  return {
143
144
  bucket: pathParts2[0],
144
145
  key: pathParts2.slice(1).join("/"),
145
- region
146
+ region: region2
146
147
  };
147
- } else {
148
- const hostParts = urlObj.hostname.split(".");
149
- const bucket = hostParts[0];
150
- const region = hostParts[2];
151
- const key = urlObj.pathname.substring(1);
152
- return { bucket, key, region };
153
148
  }
149
+ const hostParts = urlObj.hostname.split(".");
150
+ const bucket = hostParts[0];
151
+ const region = hostParts[2];
152
+ const key = urlObj.pathname.substring(1);
153
+ return { bucket, key, region };
154
154
  }
155
155
  const pathParts = urlObj.pathname.split("/").filter(Boolean);
156
156
  return {
@@ -273,7 +273,7 @@ function getFileName(key) {
273
273
  return lastSlashIndex === -1 ? normalizedKey : normalizedKey.substring(lastSlashIndex + 1);
274
274
  }
275
275
  function base64ToBuffer(base64) {
276
- const cleanBase64 = base64.includes(",") ? base64.split(",")[1] : base64;
276
+ const cleanBase64 = base64.includes(",") ? base64.split(",")[1] ?? "" : base64;
277
277
  return Buffer.from(cleanBase64, "base64");
278
278
  }
279
279
  function bufferToBase64(buffer, includeDataUrl = false, mimeType) {
@@ -301,7 +301,9 @@ function validateBatchFiles(files, options) {
301
301
  return files.map((file) => ({
302
302
  valid: false,
303
303
  fileName: file.name,
304
- errors: [`Maximum ${options.maxFiles} files allowed, but ${files.length} files provided`]
304
+ errors: [
305
+ `Maximum ${options.maxFiles} files allowed, but ${files.length} files provided`
306
+ ]
305
307
  }));
306
308
  }
307
309
  for (const file of files) {
@@ -347,25 +349,35 @@ function detectFileTypeFromContent(buffer) {
347
349
  return "image/webp";
348
350
  }
349
351
  if (header[0] === 80 && header[1] === 75 && (header[2] === 3 || header[2] === 5)) {
350
- const zipContent = buffer.toString("utf8", 0, Math.min(buffer.length, 1e3));
351
- if (zipContent.includes("word/"))
352
+ const zipContent = buffer.toString(
353
+ "utf8",
354
+ 0,
355
+ Math.min(buffer.length, 1e3)
356
+ );
357
+ if (zipContent.includes("word/")) {
352
358
  return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
353
- if (zipContent.includes("xl/"))
359
+ }
360
+ if (zipContent.includes("xl/")) {
354
361
  return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
355
- if (zipContent.includes("ppt/"))
362
+ }
363
+ if (zipContent.includes("ppt/")) {
356
364
  return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
365
+ }
357
366
  return "application/zip";
358
367
  }
359
368
  if (header.subarray(4, 8).toString() === "ftyp") {
360
369
  return "video/mp4";
361
370
  }
362
- if (header[0] === 255 && (header[1] & 224) === 224 || header.subarray(0, 3).toString() === "ID3") {
371
+ if (header[0] !== void 0 && header[1] !== void 0 && header[0] === 255 && (header[1] & 224) === 224 || header.subarray(0, 3).toString() === "ID3") {
363
372
  return "audio/mpeg";
364
373
  }
365
374
  let isText = true;
366
375
  const sampleSize = Math.min(buffer.length, 512);
367
376
  for (let i = 0; i < sampleSize; i++) {
368
377
  const byte = buffer[i];
378
+ if (byte === void 0) {
379
+ continue;
380
+ }
369
381
  if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
370
382
  isText = false;
371
383
  break;
@@ -1,4 +1,4 @@
1
- import { A as StorageInterface, C as CloudinaryConfig, U as UploadOptions, e as UploadResult, D as DownloadOptions, f as DownloadResult, g as DeleteOptions, h as DeleteResult, B as BatchDeleteOptions, i as BatchDeleteResult, L as ListOptions, o as ListResult, E as ExistsResult, j as CopyOptions, k as CopyResult, M as MoveOptions, l as MoveResult, m as DuplicateOptions, n as DuplicateResult, P as PresignedUrlOptions, p as PresignedUrlResult, q as CreateFolderOptions, r as CreateFolderResult, s as DeleteFolderOptions, t as DeleteFolderResult, u as ListFoldersOptions, v as ListFoldersResult, w as FolderExistsResult, R as RenameFolderOptions, x as RenameFolderResult, y as CopyFolderOptions, z as CopyFolderResult } from './storage-interface-UizTndyU.js';
1
+ import { S as StorageInterface, C as CloudinaryConfig, U as UploadOptions, a as UploadResult, D as DownloadOptions, b as DownloadResult, c as DeleteOptions, d as DeleteResult, B as BatchDeleteOptions, e as BatchDeleteResult, L as ListOptions, f as ListResult, E as ExistsResult, g as CopyOptions, h as CopyResult, M as MoveOptions, i as MoveResult, j as DuplicateOptions, k as DuplicateResult, P as PresignedUrlOptions, l as PresignedUrlResult, m as CreateFolderOptions, n as CreateFolderResult, o as DeleteFolderOptions, p as DeleteFolderResult, q as ListFoldersOptions, r as ListFoldersResult, F as FolderExistsResult, R as RenameFolderOptions, s as RenameFolderResult, t as CopyFolderOptions, u as CopyFolderResult } from './storage-interface-CoYx1E3B.js';
2
2
 
3
3
  /**
4
4
  * Cloudinary storage service implementation
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadCloudinaryConfig
3
- } from "./chunk-ZTZZCC52.js";
3
+ } from "./chunk-NNFNVWIN.js";
4
4
 
5
5
  // src/providers/cloudinary.ts
6
6
  import { v2 as cloudinary } from "cloudinary";
@@ -30,11 +30,13 @@ var CloudinaryProvider = class {
30
30
  } else if (options.contentType?.startsWith("video/")) {
31
31
  resourceType = "video";
32
32
  }
33
- const keyParts = options.key.split("/");
33
+ const keyParts = options.key.split("/").filter(Boolean);
34
34
  let folder = "";
35
35
  let publicId = options.key;
36
36
  if (keyParts.length > 1) {
37
37
  folder = keyParts.slice(0, -1).join("/");
38
+ }
39
+ if (keyParts.length > 0) {
38
40
  publicId = keyParts[keyParts.length - 1];
39
41
  }
40
42
  const uploadOptions = {
@@ -294,10 +296,12 @@ var CloudinaryProvider = class {
294
296
  async listFolders(_options) {
295
297
  try {
296
298
  const result = await cloudinary.api.root_folders();
297
- const folders = (result.folders || []).map((folder) => ({
298
- name: folder.name,
299
- path: folder.path
300
- }));
299
+ const folders = (result.folders || []).map(
300
+ (folder) => ({
301
+ name: folder.name,
302
+ path: folder.path
303
+ })
304
+ );
301
305
  return {
302
306
  success: true,
303
307
  folders,
package/dist/helpers.d.ts CHANGED
@@ -420,7 +420,7 @@ declare function validateBucketName(bucketName: string): {
420
420
  * // Returns: 'attachment; filename="filename.txt"'
421
421
  * ```
422
422
  */
423
- declare function getContentDisposition(fileName: string, disposition?: 'inline' | 'attachment'): string;
423
+ declare function getContentDisposition(fileName: string, disposition?: "inline" | "attachment"): string;
424
424
  /**
425
425
  * Check if file is an image based on MIME type
426
426
  * @param fileName File name or path
@@ -659,7 +659,7 @@ declare function bufferToBase64(buffer: Buffer, includeDataUrl?: boolean, mimeTy
659
659
  * }
660
660
  * ```
661
661
  */
662
- declare function generateFileHash(content: Buffer | string, algorithm?: 'md5' | 'sha1' | 'sha256'): Promise<string>;
662
+ declare function generateFileHash(content: Buffer | string, algorithm?: "md5" | "sha1" | "sha256"): Promise<string>;
663
663
  /**
664
664
  * Generate multiple unique keys at once for batch operations
665
665
  * @param fileNames Array of file names
package/dist/helpers.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  validateFileType,
32
32
  validateS3Config,
33
33
  validateS3Key
34
- } from "./chunk-KJMGBTTL.js";
34
+ } from "./chunk-VZDXZ6EA.js";
35
35
  export {
36
36
  base64ToBuffer,
37
37
  bufferToBase64,
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as S3Config, C as CloudinaryConfig, a as StorageConfig } from './storage-interface-UizTndyU.js';
2
- export { B as BatchDeleteOptions, i as BatchDeleteResult, y as CopyFolderOptions, z as CopyFolderResult, j as CopyOptions, k as CopyResult, q as CreateFolderOptions, r as CreateFolderResult, s as DeleteFolderOptions, t as DeleteFolderResult, g as DeleteOptions, h as DeleteResult, D as DownloadOptions, f as DownloadResult, m as DuplicateOptions, n as DuplicateResult, E as ExistsResult, F as FileInfo, w as FolderExistsResult, d as FolderInfo, u as ListFoldersOptions, v as ListFoldersResult, L as ListOptions, o as ListResult, M as MoveOptions, l as MoveResult, P as PresignedUrlOptions, p as PresignedUrlResult, R as RenameFolderOptions, x as RenameFolderResult, b as S3ProviderType, A as StorageInterface, c as StorageProvider, U as UploadOptions, e as UploadResult } from './storage-interface-UizTndyU.js';
1
+ import { v as S3Config, C as CloudinaryConfig, w as StorageConfig } from './storage-interface-CoYx1E3B.js';
2
+ export { B as BatchDeleteOptions, e as BatchDeleteResult, t as CopyFolderOptions, u as CopyFolderResult, g as CopyOptions, h as CopyResult, m as CreateFolderOptions, n as CreateFolderResult, o as DeleteFolderOptions, p as DeleteFolderResult, c as DeleteOptions, d as DeleteResult, D as DownloadOptions, b as DownloadResult, j as DuplicateOptions, k as DuplicateResult, E as ExistsResult, z as FileInfo, F as FolderExistsResult, A as FolderInfo, q as ListFoldersOptions, r as ListFoldersResult, L as ListOptions, f as ListResult, M as MoveOptions, i as MoveResult, P as PresignedUrlOptions, l as PresignedUrlResult, R as RenameFolderOptions, s as RenameFolderResult, x as S3ProviderType, S as StorageInterface, y as StorageProvider, U as UploadOptions, a as UploadResult } from './storage-interface-CoYx1E3B.js';
3
3
 
4
4
  /**
5
5
  * Storage configuration from environment variables
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  loadStorageConfig,
10
10
  validateCloudinaryEnvVars,
11
11
  validateS3EnvVars
12
- } from "./chunk-ZTZZCC52.js";
12
+ } from "./chunk-NNFNVWIN.js";
13
13
  export {
14
14
  ENV_VARS,
15
15
  getStorageEnvVars,
package/dist/s3.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as StorageInterface, S as S3Config, U as UploadOptions, e as UploadResult, D as DownloadOptions, f as DownloadResult, g as DeleteOptions, h as DeleteResult, B as BatchDeleteOptions, i as BatchDeleteResult, L as ListOptions, o as ListResult, E as ExistsResult, j as CopyOptions, k as CopyResult, M as MoveOptions, l as MoveResult, m as DuplicateOptions, n as DuplicateResult, P as PresignedUrlOptions, p as PresignedUrlResult, q as CreateFolderOptions, r as CreateFolderResult, s as DeleteFolderOptions, t as DeleteFolderResult, u as ListFoldersOptions, v as ListFoldersResult, w as FolderExistsResult, R as RenameFolderOptions, x as RenameFolderResult, y as CopyFolderOptions, z as CopyFolderResult } from './storage-interface-UizTndyU.js';
1
+ import { S as StorageInterface, v as S3Config, U as UploadOptions, a as UploadResult, D as DownloadOptions, b as DownloadResult, c as DeleteOptions, d as DeleteResult, B as BatchDeleteOptions, e as BatchDeleteResult, L as ListOptions, f as ListResult, E as ExistsResult, g as CopyOptions, h as CopyResult, M as MoveOptions, i as MoveResult, j as DuplicateOptions, k as DuplicateResult, P as PresignedUrlOptions, l as PresignedUrlResult, m as CreateFolderOptions, n as CreateFolderResult, o as DeleteFolderOptions, p as DeleteFolderResult, q as ListFoldersOptions, r as ListFoldersResult, F as FolderExistsResult, R as RenameFolderOptions, s as RenameFolderResult, t as CopyFolderOptions, u as CopyFolderResult } from './storage-interface-CoYx1E3B.js';
2
2
 
3
3
  /**
4
4
  * S3 storage service implementation
package/dist/s3.js CHANGED
@@ -1,10 +1,10 @@
1
- import {
2
- loadS3Config
3
- } from "./chunk-ZTZZCC52.js";
4
1
  import {
5
2
  buildPublicUrl,
6
3
  getMimeType
7
- } from "./chunk-KJMGBTTL.js";
4
+ } from "./chunk-VZDXZ6EA.js";
5
+ import {
6
+ loadS3Config
7
+ } from "./chunk-NNFNVWIN.js";
8
8
 
9
9
  // src/providers/s3.ts
10
10
  import { S3Client } from "@aws-sdk/client-s3";
@@ -74,7 +74,9 @@ var FileOperations = class {
74
74
  const reader = result.Body.transformToWebStream().getReader();
75
75
  while (true) {
76
76
  const { done, value } = await reader.read();
77
- if (done) break;
77
+ if (done) {
78
+ break;
79
+ }
78
80
  chunks.push(value);
79
81
  }
80
82
  const data = Buffer.concat(chunks);
@@ -124,7 +126,9 @@ var FileOperations = class {
124
126
  }
125
127
  });
126
128
  const result = await this.client.send(command);
127
- const deleted = result.Deleted?.map((obj) => obj.Key).filter(Boolean);
129
+ const deleted = result.Deleted?.map((obj) => obj.Key).filter(
130
+ Boolean
131
+ );
128
132
  const errors = result.Errors?.map((err) => ({
129
133
  key: err.Key || "",
130
134
  error: err.Message || "Unknown error"
@@ -349,33 +353,32 @@ var FolderOperations = class {
349
353
  deletedFiles: []
350
354
  };
351
355
  }
352
- const deleteCommand = new DeleteObjectsCommand2({
356
+ const deleteCommand2 = new DeleteObjectsCommand2({
353
357
  Bucket: this.config.bucket,
354
358
  Delete: {
355
359
  Objects: listResult.Contents.map((obj) => ({ Key: obj.Key })),
356
360
  Quiet: false
357
361
  }
358
362
  });
359
- const deleteResult = await this.client.send(deleteCommand);
363
+ const deleteResult = await this.client.send(deleteCommand2);
360
364
  const deletedFiles = deleteResult.Deleted?.map((obj) => obj.Key).filter(Boolean) || [];
361
365
  return {
362
366
  success: true,
363
367
  deletedFiles
364
368
  };
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
369
  }
370
+ const deleteCommand = new DeleteObjectsCommand2({
371
+ Bucket: this.config.bucket,
372
+ Delete: {
373
+ Objects: [{ Key: folderPath }],
374
+ Quiet: false
375
+ }
376
+ });
377
+ await this.client.send(deleteCommand);
378
+ return {
379
+ success: true,
380
+ deletedFiles: [folderPath]
381
+ };
379
382
  } catch (error) {
380
383
  return {
381
384
  success: false,
@@ -559,7 +562,7 @@ var S3Provider = class {
559
562
  secretAccessKey: config.secretAccessKey
560
563
  },
561
564
  endpoint: config.endpoint,
562
- forcePathStyle: config.forcePathStyle || false
565
+ forcePathStyle: config.forcePathStyle
563
566
  });
564
567
  this.fileOps = new FileOperations(this.client, this.config);
565
568
  this.folderOps = new FolderOperations(this.client, this.config);
@@ -6,12 +6,12 @@
6
6
  * S3-compatible provider types
7
7
  * @public
8
8
  */
9
- type S3ProviderType = 'aws' | 'cloudflare-r2' | 'minio' | 'digitalocean' | 'supabase' | 'custom';
9
+ type S3ProviderType = "aws" | "cloudflare-r2" | "minio" | "digitalocean" | "supabase" | "custom";
10
10
  /**
11
11
  * All supported storage provider types
12
12
  * @public
13
13
  */
14
- type StorageProvider = S3ProviderType | 'cloudinary';
14
+ type StorageProvider = S3ProviderType | "cloudinary";
15
15
  /**
16
16
  * Union type for all storage configurations
17
17
  * @public
@@ -45,7 +45,7 @@ interface S3Config {
45
45
  */
46
46
  interface CloudinaryConfig {
47
47
  /** Provider type (always 'cloudinary') */
48
- provider: 'cloudinary';
48
+ provider: "cloudinary";
49
49
  /** Cloudinary cloud name */
50
50
  cloudName: string;
51
51
  /** Cloudinary API key */
@@ -104,7 +104,7 @@ interface UploadOptions {
104
104
  metadata?: Record<string, string>;
105
105
  cacheControl?: string;
106
106
  contentDisposition?: string;
107
- acl?: 'private' | 'public-read' | 'public-read-write';
107
+ acl?: "private" | "public-read" | "public-read-write";
108
108
  expires?: Date;
109
109
  }
110
110
  interface UploadResult {
@@ -153,7 +153,7 @@ interface CopyOptions {
153
153
  sourceKey: string;
154
154
  destinationKey: string;
155
155
  metadata?: Record<string, string>;
156
- metadataDirective?: 'COPY' | 'REPLACE';
156
+ metadataDirective?: "COPY" | "REPLACE";
157
157
  }
158
158
  interface CopyResult {
159
159
  success: boolean;
@@ -164,7 +164,7 @@ interface MoveOptions {
164
164
  sourceKey: string;
165
165
  destinationKey: string;
166
166
  metadata?: Record<string, string>;
167
- metadataDirective?: 'COPY' | 'REPLACE';
167
+ metadataDirective?: "COPY" | "REPLACE";
168
168
  }
169
169
  interface MoveResult {
170
170
  success: boolean;
@@ -194,7 +194,7 @@ interface ListResult {
194
194
  }
195
195
  interface PresignedUrlOptions {
196
196
  key: string;
197
- operation: 'get' | 'put';
197
+ operation: "get" | "put";
198
198
  expiresIn?: number;
199
199
  contentType?: string;
200
200
  }
@@ -313,4 +313,4 @@ interface StorageInterface {
313
313
  copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult>;
314
314
  }
315
315
 
316
- export type { StorageInterface as A, BatchDeleteOptions as B, CloudinaryConfig as C, DownloadOptions as D, ExistsResult as E, FileInfo as F, ListOptions as L, MoveOptions as M, PresignedUrlOptions as P, RenameFolderOptions as R, S3Config as S, UploadOptions as U, StorageConfig as a, S3ProviderType as b, StorageProvider as c, FolderInfo as d, UploadResult as e, DownloadResult as f, DeleteOptions as g, DeleteResult as h, BatchDeleteResult as i, CopyOptions as j, CopyResult as k, MoveResult as l, DuplicateOptions as m, DuplicateResult as n, ListResult as o, PresignedUrlResult as p, CreateFolderOptions as q, CreateFolderResult as r, DeleteFolderOptions as s, DeleteFolderResult as t, ListFoldersOptions as u, ListFoldersResult as v, FolderExistsResult as w, RenameFolderResult as x, CopyFolderOptions as y, CopyFolderResult as z };
316
+ export type { FolderInfo as A, BatchDeleteOptions as B, CloudinaryConfig as C, DownloadOptions as D, ExistsResult as E, FolderExistsResult as F, ListOptions as L, MoveOptions as M, PresignedUrlOptions as P, RenameFolderOptions as R, StorageInterface as S, UploadOptions as U, UploadResult as a, DownloadResult as b, DeleteOptions as c, DeleteResult as d, BatchDeleteResult as e, ListResult as f, CopyOptions as g, CopyResult as h, MoveResult as i, DuplicateOptions as j, DuplicateResult as k, PresignedUrlResult as l, CreateFolderOptions as m, CreateFolderResult as n, DeleteFolderOptions as o, DeleteFolderResult as p, ListFoldersOptions as q, ListFoldersResult as r, RenameFolderResult as s, CopyFolderOptions as t, CopyFolderResult as u, S3Config as v, StorageConfig as w, S3ProviderType as x, StorageProvider as y, FileInfo as z };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@pelatform/storage",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Storage utilities for SaaS applications.",
5
- "author": "Pelatform Dev",
5
+ "author": "Pelatform",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "main": "./dist/index.js",
@@ -26,10 +26,20 @@
26
26
  "default": "./dist/helpers.js"
27
27
  }
28
28
  },
29
- "homepage": "https://devpelatform.github.io/storage",
29
+ "scripts": {
30
+ "clean": "rimraf dist",
31
+ "clean:all": "rimraf .turbo dist node_modules",
32
+ "dev": "tsup --watch",
33
+ "build": "tsup",
34
+ "types:check": "tsc --noEmit"
35
+ },
36
+ "repository": "github:devpelatform/toolkits",
37
+ "homepage": "https://github.com/devpelatform/toolkits",
38
+ "bugs": {
39
+ "url": "https://github.com/devpelatform/toolkits/issues"
40
+ },
30
41
  "files": [
31
- "dist/*",
32
- "README.md"
42
+ "dist"
33
43
  ],
34
44
  "keywords": [
35
45
  "pelatform",
@@ -44,15 +54,19 @@
44
54
  "mime-types": "^3.0.2"
45
55
  },
46
56
  "devDependencies": {
57
+ "@aws-sdk/client-s3": "^3.943.0",
58
+ "@aws-sdk/s3-request-presigner": "^3.943.0",
59
+ "@pelatform/tsconfig": "0.1.0",
47
60
  "@types/mime-types": "^3.0.1",
48
61
  "@types/node": "^24.10.1",
62
+ "cloudinary": "^2.8.0",
49
63
  "tsup": "^8.5.1",
50
64
  "typescript": "^5.9.3"
51
65
  },
52
66
  "peerDependencies": {
53
- "@aws-sdk/client-s3": ">=3.0.0",
54
- "@aws-sdk/s3-request-presigner": ">=3.0.0",
55
- "cloudinary": ">=2.0.0"
67
+ "@aws-sdk/client-s3": ">=3.8.0",
68
+ "@aws-sdk/s3-request-presigner": ">=3.8.0",
69
+ "cloudinary": ">=2.8.0"
56
70
  },
57
71
  "peerDependenciesMeta": {
58
72
  "@aws-sdk/client-s3": {
@@ -66,13 +80,12 @@
66
80
  }
67
81
  },
68
82
  "publishConfig": {
83
+ "registry": "https://registry.npmjs.org/",
69
84
  "access": "public"
70
85
  },
71
- "scripts": {
72
- "clean": "rimraf dist",
73
- "clean:all": "rimraf .turbo dist node_modules",
74
- "dev": "tsup --watch",
75
- "build": "tsup --clean",
76
- "types:check": "tsc --noEmit"
86
+ "lint-staged": {
87
+ "*.{js,jsx,ts,tsx,cjs,mjs,cts,mts}": "biome check --write --no-errors-on-unmatched",
88
+ "*.{md,yml,yaml}": "prettier --write",
89
+ "*.{json,jsonc,html}": "biome format --write --no-errors-on-unmatched"
77
90
  }
78
- }
91
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Pelatform
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.