@pelatform/storage 1.0.5 → 1.0.7

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.
@@ -83,10 +83,15 @@ function parseS3Url(url) {
83
83
  return {};
84
84
  }
85
85
  }
86
- function buildPublicUrl(baseUrl, bucket, key) {
87
- const cleanBaseUrl = baseUrl.replace(/\/$/, "");
86
+ function buildPublicUrl(baseUrl, bucket, key, supabase) {
87
+ let cleanBaseUrl;
88
+ cleanBaseUrl = baseUrl.replace(/\/$/, "");
88
89
  const cleanKey = key.replace(/^\//, "");
89
- return `${cleanBaseUrl}/${bucket}/${cleanKey}`;
90
+ if (supabase) {
91
+ cleanBaseUrl = cleanBaseUrl.replace(/\.storage/, "").replace(/\/storage\/v1\/s3$/, "");
92
+ cleanBaseUrl = `${cleanBaseUrl}/storage/v1/object/public`;
93
+ }
94
+ return `${cleanBaseUrl}/${bucket === "" ? "" : `${bucket}/`}${cleanKey}`;
90
95
  }
91
96
  function validateS3Key(key) {
92
97
  if (!key || key.length === 0) {
@@ -38,12 +38,12 @@ declare class CloudinaryService implements StorageInterface {
38
38
  downloadFile(key: string): Promise<DownloadResult>;
39
39
  deleteFile(key: string): Promise<DeleteResult>;
40
40
  deleteFiles(keys: string[]): Promise<BatchDeleteResult>;
41
+ listFiles(prefix?: string, maxKeys?: number): Promise<ListResult>;
41
42
  fileExists(key: string): Promise<boolean>;
42
43
  copyFile(sourceKey: string, destinationKey: string, metadata?: Record<string, string>): Promise<CopyResult>;
43
44
  moveFile(sourceKey: string, destinationKey: string, metadata?: Record<string, string>): Promise<MoveResult>;
44
45
  duplicateFile(sourceKey: string, destinationKey: string, metadata?: Record<string, string>): Promise<DuplicateResult>;
45
46
  renameFile(sourceKey: string, destinationKey: string, metadata?: Record<string, string>): Promise<MoveResult>;
46
- listFiles(prefix?: string, maxKeys?: number): Promise<ListResult>;
47
47
  getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult>;
48
48
  getUploadUrl(key: string, contentType?: string, expiresIn?: number): Promise<PresignedUrlResult>;
49
49
  createFolderPath(path: string): Promise<CreateFolderResult>;
@@ -32,10 +32,13 @@ var CloudinaryProvider = class {
32
32
  }
33
33
  const keyParts = options.key.split("/");
34
34
  let folder = "";
35
- let publicId = options.key;
35
+ let publicId = keyParts[keyParts.length - 1];
36
36
  if (keyParts.length > 1) {
37
37
  folder = keyParts.slice(0, -1).join("/");
38
- publicId = keyParts[keyParts.length - 1];
38
+ }
39
+ const dotIndex = publicId.lastIndexOf(".");
40
+ if (dotIndex > 0) {
41
+ publicId = publicId.slice(0, dotIndex);
39
42
  }
40
43
  const uploadOptions = {
41
44
  public_id: publicId,
@@ -47,8 +50,11 @@ var CloudinaryProvider = class {
47
50
  if (folder) {
48
51
  uploadOptions.folder = folder;
49
52
  }
53
+ if (this.config.folder) {
54
+ uploadOptions.folder = `${this.config.folder}/${folder}`;
55
+ }
50
56
  const result = await cloudinary.uploader.upload(fileData, uploadOptions);
51
- const publicUrl = this.getPublicUrl(result.public_id);
57
+ const publicUrl = this.getPublicUrl(result.public_id, result.format);
52
58
  return {
53
59
  success: true,
54
60
  key: result.public_id,
@@ -102,6 +108,13 @@ var CloudinaryProvider = class {
102
108
  const filename = keyParts[keyParts.length - 1];
103
109
  publicId = `${folder}/${filename}`;
104
110
  }
111
+ const dotIndex = publicId.lastIndexOf(".");
112
+ if (dotIndex > 0) {
113
+ publicId = publicId.slice(0, dotIndex);
114
+ }
115
+ if (this.config.folder) {
116
+ publicId = `${this.config.folder}/${publicId}`;
117
+ }
105
118
  let resourceType = "raw";
106
119
  try {
107
120
  await cloudinary.api.resource(publicId, { resource_type: "image" });
@@ -254,11 +267,11 @@ var CloudinaryProvider = class {
254
267
  };
255
268
  }
256
269
  }
257
- getPublicUrl(key) {
270
+ getPublicUrl(key, extension) {
258
271
  const protocol = this.config.secure !== false ? "https" : "http";
259
272
  const baseUrl = `${protocol}://res.cloudinary.com/${this.config.cloudName}`;
260
273
  const publicId = key;
261
- return `${baseUrl}/image/upload/${publicId}`;
274
+ return `${baseUrl}/image/upload/${publicId}${extension ? `.${extension}` : ""}`;
262
275
  }
263
276
  // Folder operations
264
277
  async createFolder(options) {
@@ -433,6 +446,9 @@ var CloudinaryService = class {
433
446
  async deleteFiles(keys) {
434
447
  return this.batchDelete({ keys });
435
448
  }
449
+ async listFiles(prefix, maxKeys) {
450
+ return this.list({ prefix, maxKeys });
451
+ }
436
452
  async fileExists(key) {
437
453
  const result = await this.exists(key);
438
454
  return result.exists;
@@ -449,9 +465,6 @@ var CloudinaryService = class {
449
465
  async renameFile(sourceKey, destinationKey, metadata) {
450
466
  return this.moveFile(sourceKey, destinationKey, metadata);
451
467
  }
452
- async listFiles(prefix, maxKeys) {
453
- return this.list({ prefix, maxKeys });
454
- }
455
468
  async getDownloadUrl(key, expiresIn) {
456
469
  return this.getPresignedUrl({ key, operation: "get", expiresIn });
457
470
  }
package/dist/helpers.d.ts CHANGED
@@ -221,7 +221,7 @@ declare function parseS3Url(url: string): {
221
221
  * // Returns: "https://storage.example.com/assets/images/logo.png"
222
222
  * ```
223
223
  */
224
- declare function buildPublicUrl(baseUrl: string, bucket: string, key: string): string;
224
+ declare function buildPublicUrl(baseUrl: string, bucket: string, key: string, supabase?: boolean): string;
225
225
  /**
226
226
  * Validate S3 key format according to AWS S3 naming rules
227
227
  * @param key S3 object key to validate
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-7IPYXDG4.js";
35
35
  export {
36
36
  base64ToBuffer,
37
37
  bufferToBase64,
package/dist/s3.d.ts CHANGED
@@ -1,4 +1,4 @@
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';
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, x as S3ProviderType } from './storage-interface-CoYx1E3B.js';
2
2
 
3
3
  /**
4
4
  * S3 storage service implementation
@@ -41,6 +41,7 @@ declare class S3Service implements StorageInterface {
41
41
  downloadFile(key: string): Promise<DownloadResult>;
42
42
  deleteFile(key: string): Promise<DeleteResult>;
43
43
  deleteFiles(keys: string[]): Promise<BatchDeleteResult>;
44
+ listFiles(prefix?: string, maxKeys?: number): Promise<ListResult>;
44
45
  fileExists(key: string): Promise<boolean>;
45
46
  copyFile(sourceKey: string, destinationKey: string, options?: Partial<CopyOptions>): Promise<CopyResult>;
46
47
  moveFile(sourceKey: string, destinationKey: string, options?: Partial<MoveOptions>): Promise<MoveResult>;
@@ -55,6 +56,7 @@ declare class S3Service implements StorageInterface {
55
56
  copyFolderPath(sourcePath: string, destinationPath: string, recursive?: boolean): Promise<CopyFolderResult>;
56
57
  }
57
58
 
59
+ declare const S3_PROVIDER: S3ProviderType;
58
60
  /**
59
61
  * Create S3 service using environment variables or manual configuration
60
62
  * @param config Optional S3 configuration. If not provided, loads from environment variables
@@ -105,4 +107,4 @@ declare class S3Service implements StorageInterface {
105
107
  declare function createS3(): S3Service;
106
108
  declare function createS3(config: S3Config): S3Service;
107
109
 
108
- export { S3Service, createS3 };
110
+ export { S3Service, S3_PROVIDER, createS3 };
package/dist/s3.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildPublicUrl,
3
3
  getMimeType
4
- } from "./chunk-KJMGBTTL.js";
4
+ } from "./chunk-7IPYXDG4.js";
5
5
  import {
6
6
  loadS3Config
7
7
  } from "./chunk-ZTZZCC52.js";
@@ -291,11 +291,18 @@ var FileOperations = class {
291
291
  }
292
292
  }
293
293
  getPublicUrl(key) {
294
+ const isCloudflare = this.config.provider === "cloudflare-r2";
295
+ const isSupabase = this.config.provider === "supabase";
294
296
  if (this.config.publicUrl) {
295
- return buildPublicUrl(this.config.publicUrl, this.config.bucket, key);
297
+ return buildPublicUrl(
298
+ this.config.publicUrl,
299
+ isCloudflare ? "" : this.config.bucket,
300
+ key,
301
+ isSupabase
302
+ );
296
303
  }
297
304
  if (this.config.endpoint) {
298
- return buildPublicUrl(this.config.endpoint, this.config.bucket, key);
305
+ return buildPublicUrl(this.config.endpoint, this.config.bucket, key, isSupabase);
299
306
  }
300
307
  return `https://${this.config.bucket}.s3.${this.config.region}.amazonaws.com/${key}`;
301
308
  }
@@ -723,6 +730,9 @@ var S3Service = class {
723
730
  async deleteFiles(keys) {
724
731
  return this.batchDelete({ keys });
725
732
  }
733
+ async listFiles(prefix, maxKeys) {
734
+ return this.list({ prefix, maxKeys });
735
+ }
726
736
  async fileExists(key) {
727
737
  const result = await this.exists(key);
728
738
  return result.exists;
@@ -786,6 +796,7 @@ var S3Service = class {
786
796
  };
787
797
 
788
798
  // src/s3.ts
799
+ var S3_PROVIDER = process.env.PELATFORM_S3_PROVIDER;
789
800
  function createS3(config) {
790
801
  if (config) {
791
802
  return new S3Service(config);
@@ -795,5 +806,6 @@ function createS3(config) {
795
806
  }
796
807
  export {
797
808
  S3Service,
809
+ S3_PROVIDER,
798
810
  createS3
799
811
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pelatform/storage",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Storage utilities for SaaS applications.",
5
5
  "author": "Pelatform",
6
6
  "license": "MIT",
@@ -28,10 +28,13 @@
28
28
  },
29
29
  "scripts": {
30
30
  "clean": "rimraf dist",
31
- "clean:all": "rimraf .turbo dist node_modules",
31
+ "clean:all": "rimraf .turbo coverage dist node_modules",
32
32
  "dev": "tsup --watch",
33
33
  "build": "tsup",
34
- "types:check": "tsc --noEmit"
34
+ "types:check": "tsc --noEmit",
35
+ "test": "vitest run",
36
+ "test:watch": "vitest",
37
+ "test:coverage": "vitest run --coverage"
35
38
  },
36
39
  "repository": "github:devpelatform/toolkits",
37
40
  "homepage": "https://github.com/devpelatform/toolkits",
@@ -54,11 +57,11 @@
54
57
  "mime-types": "^3.0.2"
55
58
  },
56
59
  "devDependencies": {
57
- "@aws-sdk/client-s3": "^3.948.0",
58
- "@aws-sdk/s3-request-presigner": "^3.948.0",
60
+ "@aws-sdk/client-s3": "^3.953.0",
61
+ "@aws-sdk/s3-request-presigner": "^3.953.0",
59
62
  "@pelatform/tsconfig": "0.1.3",
60
63
  "@types/mime-types": "^3.0.1",
61
- "@types/node": "^25.0.1",
64
+ "@types/node": "^25.0.3",
62
65
  "cloudinary": "^2.8.0",
63
66
  "tsup": "^8.5.1",
64
67
  "typescript": "^5.9.3"