@pelatform/storage 1.0.3 → 1.0.4

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.
@@ -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,13 +41,11 @@ function validateFileType(fileName, allowedTypes) {
41
41
  return { valid: true };
42
42
  }
43
43
  function formatFileSize(bytes) {
44
- if (bytes === 0) {
45
- return "0 Bytes";
46
- }
44
+ if (bytes === 0) return "0 Bytes";
47
45
  const k = 1024;
48
46
  const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
49
47
  const i = Math.floor(Math.log(bytes) / Math.log(k));
50
- return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
48
+ return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
51
49
  }
52
50
  function sanitizeFileName(fileName) {
53
51
  return fileName.replace(/[^a-zA-Z0-9.-]/g, "_").replace(/_{2,}/g, "_").replace(/^_|_$/g, "").toLowerCase();
@@ -70,10 +68,11 @@ function parseS3Url(url) {
70
68
  bucket: pathParts2[0],
71
69
  key: pathParts2.slice(1).join("/")
72
70
  };
71
+ } else {
72
+ const bucket = urlObj.hostname.split(".")[0];
73
+ const key = urlObj.pathname.substring(1);
74
+ return { bucket, key };
73
75
  }
74
- const bucket = urlObj.hostname.split(".")[0];
75
- const key = urlObj.pathname.substring(1);
76
- return { bucket, key };
77
76
  }
78
77
  const pathParts = urlObj.pathname.split("/").filter(Boolean);
79
78
  return {
@@ -139,18 +138,19 @@ function extractS3Info(url) {
139
138
  if (urlObj.hostname.includes("amazonaws.com")) {
140
139
  if (urlObj.hostname.startsWith("s3")) {
141
140
  const pathParts2 = urlObj.pathname.split("/").filter(Boolean);
142
- const region2 = urlObj.hostname.split(".")[1];
141
+ const region = urlObj.hostname.split(".")[1];
143
142
  return {
144
143
  bucket: pathParts2[0],
145
144
  key: pathParts2.slice(1).join("/"),
146
- region: region2
145
+ region
147
146
  };
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 };
148
153
  }
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,9 +301,7 @@ function validateBatchFiles(files, options) {
301
301
  return files.map((file) => ({
302
302
  valid: false,
303
303
  fileName: file.name,
304
- errors: [
305
- `Maximum ${options.maxFiles} files allowed, but ${files.length} files provided`
306
- ]
304
+ errors: [`Maximum ${options.maxFiles} files allowed, but ${files.length} files provided`]
307
305
  }));
308
306
  }
309
307
  for (const file of files) {
@@ -349,35 +347,25 @@ function detectFileTypeFromContent(buffer) {
349
347
  return "image/webp";
350
348
  }
351
349
  if (header[0] === 80 && header[1] === 75 && (header[2] === 3 || header[2] === 5)) {
352
- const zipContent = buffer.toString(
353
- "utf8",
354
- 0,
355
- Math.min(buffer.length, 1e3)
356
- );
357
- if (zipContent.includes("word/")) {
350
+ const zipContent = buffer.toString("utf8", 0, Math.min(buffer.length, 1e3));
351
+ if (zipContent.includes("word/"))
358
352
  return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
359
- }
360
- if (zipContent.includes("xl/")) {
353
+ if (zipContent.includes("xl/"))
361
354
  return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
362
- }
363
- if (zipContent.includes("ppt/")) {
355
+ if (zipContent.includes("ppt/"))
364
356
  return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
365
- }
366
357
  return "application/zip";
367
358
  }
368
359
  if (header.subarray(4, 8).toString() === "ftyp") {
369
360
  return "video/mp4";
370
361
  }
371
- if (header[0] !== void 0 && header[1] !== void 0 && header[0] === 255 && (header[1] & 224) === 224 || header.subarray(0, 3).toString() === "ID3") {
362
+ if (header[0] === 255 && (header[1] & 224) === 224 || header.subarray(0, 3).toString() === "ID3") {
372
363
  return "audio/mpeg";
373
364
  }
374
365
  let isText = true;
375
366
  const sampleSize = Math.min(buffer.length, 512);
376
367
  for (let i = 0; i < sampleSize; i++) {
377
368
  const byte = buffer[i];
378
- if (byte === void 0) {
379
- continue;
380
- }
381
369
  if (byte === 0 || byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
382
370
  isText = false;
383
371
  break;
@@ -31,15 +31,11 @@ function getRequiredEnvVar(key) {
31
31
  }
32
32
  function getBooleanEnvVar(key, defaultValue = false) {
33
33
  const value = getEnvVar(key);
34
- if (!value) {
35
- return defaultValue;
36
- }
34
+ if (!value) return defaultValue;
37
35
  return value.toLowerCase() === "true" || value === "1";
38
36
  }
39
37
  function loadS3Config() {
40
- const provider = getRequiredEnvVar(
41
- ENV_VARS.PELATFORM_S3_PROVIDER
42
- );
38
+ const provider = getRequiredEnvVar(ENV_VARS.PELATFORM_S3_PROVIDER);
43
39
  const validProviders = [
44
40
  "aws",
45
41
  "cloudflare-r2",
@@ -79,15 +75,15 @@ function loadStorageConfig() {
79
75
  if (provider) {
80
76
  if (provider === "cloudinary") {
81
77
  return loadCloudinaryConfig();
78
+ } else {
79
+ return loadS3Config();
82
80
  }
83
- return loadS3Config();
84
81
  }
85
82
  const hasCloudinaryVars = getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_CLOUD_NAME) && getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_API_KEY) && getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_API_SECRET);
86
83
  const hasS3Vars = getEnvVar(ENV_VARS.PELATFORM_S3_BUCKET) && getEnvVar(ENV_VARS.PELATFORM_S3_ACCESS_KEY_ID) && getEnvVar(ENV_VARS.PELATFORM_S3_SECRET_ACCESS_KEY);
87
84
  if (hasCloudinaryVars) {
88
85
  return loadCloudinaryConfig();
89
- }
90
- if (hasS3Vars) {
86
+ } else if (hasS3Vars) {
91
87
  const config = loadS3Config();
92
88
  if (!config.provider) {
93
89
  config.provider = "aws";
@@ -111,17 +107,11 @@ function isStorageConfigured() {
111
107
  }
112
108
  function getStorageProvider() {
113
109
  const provider = getEnvVar(ENV_VARS.PELATFORM_S3_PROVIDER);
114
- if (provider) {
115
- return provider;
116
- }
110
+ if (provider) return provider;
117
111
  const hasCloudinaryVars = getEnvVar(ENV_VARS.PELATFORM_CLOUDINARY_CLOUD_NAME);
118
112
  const hasS3Vars = getEnvVar(ENV_VARS.PELATFORM_S3_BUCKET);
119
- if (hasCloudinaryVars) {
120
- return "cloudinary";
121
- }
122
- if (hasS3Vars) {
123
- return "aws";
124
- }
113
+ if (hasCloudinaryVars) return "cloudinary";
114
+ if (hasS3Vars) return "aws";
125
115
  return void 0;
126
116
  }
127
117
  function validateS3EnvVars() {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  loadCloudinaryConfig
3
- } from "./chunk-NNFNVWIN.js";
3
+ } from "./chunk-ZTZZCC52.js";
4
4
 
5
5
  // src/providers/cloudinary.ts
6
6
  import { v2 as cloudinary } from "cloudinary";
@@ -30,13 +30,11 @@ var CloudinaryProvider = class {
30
30
  } else if (options.contentType?.startsWith("video/")) {
31
31
  resourceType = "video";
32
32
  }
33
- const keyParts = options.key.split("/").filter(Boolean);
33
+ const keyParts = options.key.split("/");
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) {
40
38
  publicId = keyParts[keyParts.length - 1];
41
39
  }
42
40
  const uploadOptions = {
@@ -296,12 +294,10 @@ var CloudinaryProvider = class {
296
294
  async listFolders(_options) {
297
295
  try {
298
296
  const result = await cloudinary.api.root_folders();
299
- const folders = (result.folders || []).map(
300
- (folder) => ({
301
- name: folder.name,
302
- path: folder.path
303
- })
304
- );
297
+ const folders = (result.folders || []).map((folder) => ({
298
+ name: folder.name,
299
+ path: folder.path
300
+ }));
305
301
  return {
306
302
  success: true,
307
303
  folders,
package/dist/helpers.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  validateFileType,
32
32
  validateS3Config,
33
33
  validateS3Key
34
- } from "./chunk-VZDXZ6EA.js";
34
+ } from "./chunk-KJMGBTTL.js";
35
35
  export {
36
36
  base64ToBuffer,
37
37
  bufferToBase64,
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  loadStorageConfig,
10
10
  validateCloudinaryEnvVars,
11
11
  validateS3EnvVars
12
- } from "./chunk-NNFNVWIN.js";
12
+ } from "./chunk-ZTZZCC52.js";
13
13
  export {
14
14
  ENV_VARS,
15
15
  getStorageEnvVars,
package/dist/s3.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  buildPublicUrl,
3
3
  getMimeType
4
- } from "./chunk-VZDXZ6EA.js";
4
+ } from "./chunk-KJMGBTTL.js";
5
5
  import {
6
6
  loadS3Config
7
- } from "./chunk-NNFNVWIN.js";
7
+ } from "./chunk-ZTZZCC52.js";
8
8
 
9
9
  // src/providers/s3.ts
10
10
  import { S3Client } from "@aws-sdk/client-s3";
@@ -74,9 +74,7 @@ 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) {
78
- break;
79
- }
77
+ if (done) break;
80
78
  chunks.push(value);
81
79
  }
82
80
  const data = Buffer.concat(chunks);
@@ -126,9 +124,7 @@ var FileOperations = class {
126
124
  }
127
125
  });
128
126
  const result = await this.client.send(command);
129
- const deleted = result.Deleted?.map((obj) => obj.Key).filter(
130
- Boolean
131
- );
127
+ const deleted = result.Deleted?.map((obj) => obj.Key).filter(Boolean);
132
128
  const errors = result.Errors?.map((err) => ({
133
129
  key: err.Key || "",
134
130
  error: err.Message || "Unknown error"
@@ -353,32 +349,33 @@ var FolderOperations = class {
353
349
  deletedFiles: []
354
350
  };
355
351
  }
356
- const deleteCommand2 = new DeleteObjectsCommand2({
352
+ const deleteCommand = new DeleteObjectsCommand2({
357
353
  Bucket: this.config.bucket,
358
354
  Delete: {
359
355
  Objects: listResult.Contents.map((obj) => ({ Key: obj.Key })),
360
356
  Quiet: false
361
357
  }
362
358
  });
363
- const deleteResult = await this.client.send(deleteCommand2);
359
+ const deleteResult = await this.client.send(deleteCommand);
364
360
  const deletedFiles = deleteResult.Deleted?.map((obj) => obj.Key).filter(Boolean) || [];
365
361
  return {
366
362
  success: true,
367
363
  deletedFiles
368
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
+ };
369
378
  }
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
- };
382
379
  } catch (error) {
383
380
  return {
384
381
  success: false,
@@ -562,7 +559,7 @@ var S3Provider = class {
562
559
  secretAccessKey: config.secretAccessKey
563
560
  },
564
561
  endpoint: config.endpoint,
565
- forcePathStyle: config.forcePathStyle
562
+ forcePathStyle: config.forcePathStyle || false
566
563
  });
567
564
  this.fileOps = new FileOperations(this.client, this.config);
568
565
  this.folderOps = new FolderOperations(this.client, this.config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pelatform/storage",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Storage utilities for SaaS applications.",
5
5
  "author": "Pelatform",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "devDependencies": {
57
57
  "@aws-sdk/client-s3": "^3.943.0",
58
58
  "@aws-sdk/s3-request-presigner": "^3.943.0",
59
- "@pelatform/tsconfig": "0.1.0",
59
+ "@pelatform/tsconfig": "0.1.1",
60
60
  "@types/mime-types": "^3.0.1",
61
61
  "@types/node": "^24.10.1",
62
62
  "cloudinary": "^2.8.0",