@sanvika/cloudinary 0.2.0 → 0.2.2

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.
Files changed (2) hide show
  1. package/dist/index.js +108 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -110,14 +110,14 @@ var TRANSFORM_PRESETS = {
110
110
 
111
111
  // src/gatewayClient.js
112
112
  function isProxyMode() {
113
- return Boolean(process.env.SANVIKA_CLOUDINARY_GATEWAY_URL && process.env.SANVIKA_CLOUDINARY_TOKEN);
113
+ return Boolean(process.env.CLOUDINARY_GATEWAY_URL && process.env.CLOUDINARY_TOKEN);
114
114
  }
115
115
  function requireProxyEnv() {
116
- const base = process.env.SANVIKA_CLOUDINARY_GATEWAY_URL;
117
- const token = process.env.SANVIKA_CLOUDINARY_TOKEN;
116
+ const base = process.env.CLOUDINARY_GATEWAY_URL;
117
+ const token = process.env.CLOUDINARY_TOKEN;
118
118
  if (!base || !token) {
119
119
  throw new CloudinaryError(
120
- "Proxy gateway env missing. Set SANVIKA_CLOUDINARY_GATEWAY_URL and SANVIKA_CLOUDINARY_TOKEN.",
120
+ "Proxy gateway env missing. Set CLOUDINARY_GATEWAY_URL and CLOUDINARY_TOKEN.",
121
121
  "gateway_env"
122
122
  );
123
123
  }
@@ -192,7 +192,7 @@ async function loadLegacyCloudinary() {
192
192
  return _cloudinary;
193
193
  }
194
194
  async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecret } = {}) {
195
- const resolvedApp = appName || process.env.SANVIKA_CLOUDINARY_APP_NAME;
195
+ const resolvedApp = appName || process.env.CLOUDINARY_APP_NAME;
196
196
  if (!resolvedApp) throw new Error("appName is required for configureSanvikaCloudinary");
197
197
  _appName = resolvedApp;
198
198
  if (isProxyMode()) {
@@ -204,7 +204,7 @@ async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecre
204
204
  const as = apiSecret || process.env.CLOUDINARY_API_SECRET;
205
205
  if (!cn || !ak || !as) {
206
206
  throw new Error(
207
- "Cloudinary credentials missing. Either use proxy mode (SANVIKA_CLOUDINARY_GATEWAY_URL + SANVIKA_CLOUDINARY_TOKEN) or legacy env (CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET)."
207
+ "Cloudinary credentials missing. Either use proxy mode (CLOUDINARY_GATEWAY_URL + CLOUDINARY_TOKEN) or legacy env (CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET)."
208
208
  );
209
209
  }
210
210
  const cloudinary = await loadLegacyCloudinary();
@@ -214,12 +214,12 @@ async function configureSanvikaCloudinary({ appName, cloudName, apiKey, apiSecre
214
214
  async function ensureConfigured() {
215
215
  if (_configured) return;
216
216
  if (isProxyMode()) {
217
- _appName = _appName || process.env.SANVIKA_CLOUDINARY_APP_NAME || "sanvika-app";
217
+ _appName = _appName || process.env.CLOUDINARY_APP_NAME || "sanvika-app";
218
218
  _configured = true;
219
219
  return;
220
220
  }
221
- if (process.env.SANVIKA_CLOUDINARY_APP_NAME) {
222
- await configureSanvikaCloudinary({ appName: process.env.SANVIKA_CLOUDINARY_APP_NAME });
221
+ if (process.env.CLOUDINARY_APP_NAME) {
222
+ await configureSanvikaCloudinary({ appName: process.env.CLOUDINARY_APP_NAME });
223
223
  return;
224
224
  }
225
225
  throw new Error("Call configureSanvikaCloudinary({ appName }) before using SDK operations.");
@@ -496,7 +496,7 @@ function verifySanvikaWebhookSignature({ headers, rawBody, secret, toleranceSec
496
496
  };
497
497
  const sig = String(getHeader("x-sanvika-signature") || "").trim();
498
498
  const ts = String(getHeader("x-sanvika-timestamp") || "").trim();
499
- const token = secret || process.env.SANVIKA_CLOUDINARY_TOKEN;
499
+ const token = secret || process.env.CLOUDINARY_TOKEN;
500
500
  if (!token) return { valid: false, reason: "missing_secret" };
501
501
  if (!sig) return { valid: false, reason: "missing_signature" };
502
502
  if (!ts) return { valid: false, reason: "missing_timestamp" };
@@ -512,10 +512,103 @@ function verifySanvikaWebhookSignature({ headers, rawBody, secret, toleranceSec
512
512
  const ok = crypto2.timingSafeEqual(a, b);
513
513
  return { valid: ok, reason: ok ? void 0 : "signature_mismatch" };
514
514
  }
515
+
516
+ // src/cloudinaryValidation.js
517
+ var MAX_IMAGE_SIZE = 5 * 1024 * 1024;
518
+ var MAX_PROFILE_PIC_SIZE = 6 * 1024 * 1024;
519
+ var MAX_VIDEO_SIZE = 100 * 1024 * 1024;
520
+ var SUPPORTED_IMAGE_FORMATS = [".jpg", ".jpeg", ".png", ".webp"];
521
+ var SUPPORTED_PROFILE_MIME_TYPES = ["image/jpeg", "image/jpg", "image/png"];
522
+ var SUPPORTED_VIDEO_FORMATS = [".mp4", ".mov", ".avi", ".wmv", ".flv", ".mkv", ".webm", ".m4v"];
523
+ function splitFileName(name) {
524
+ const safe = typeof name === "string" && name.trim() ? name.trim() : "";
525
+ const dotIdx = safe.lastIndexOf(".");
526
+ if (dotIdx <= 0) return { ext: "", base: safe || "asset" };
527
+ return { ext: safe.slice(dotIdx).toLowerCase(), base: safe.slice(0, dotIdx) };
528
+ }
529
+ function normalizeFolderPath(value, fallback) {
530
+ if (typeof value !== "string") return fallback;
531
+ const sanitized = value.trim().replace(/\\/g, "/").split("/").filter(Boolean).join("/");
532
+ return sanitized || fallback;
533
+ }
534
+ function createScopedPublicId({ baseName, userPrefix, timestamp }) {
535
+ const safeBaseName = baseName && baseName.trim() ? baseName.trim() : "asset";
536
+ const safePrefix = userPrefix && userPrefix.trim() ? userPrefix.trim() : "anon";
537
+ const safeTimestamp = timestamp && timestamp.trim() ? timestamp.trim() : Date.now().toString().slice(-6);
538
+ return `${safePrefix}_${safeTimestamp}_${safeBaseName}`;
539
+ }
540
+ function validateImageFile(file) {
541
+ const { ext: extFromName } = splitFileName(file.name || "");
542
+ const mimeExt = file.type ? `.${file.type.split("/")[1]}`.toLowerCase() : "";
543
+ const finalExt = extFromName || mimeExt;
544
+ if (!SUPPORTED_IMAGE_FORMATS.includes(finalExt)) {
545
+ return {
546
+ valid: false,
547
+ status: 415,
548
+ error: "INVALID_FORMAT",
549
+ message: `Unsupported format: ${finalExt}. Allowed: ${SUPPORTED_IMAGE_FORMATS.join(", ")}`
550
+ };
551
+ }
552
+ if (file.size > MAX_IMAGE_SIZE) {
553
+ return {
554
+ valid: false,
555
+ status: 413,
556
+ error: "FILE_TOO_LARGE",
557
+ message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_IMAGE_SIZE / 1024 / 1024}MB limit`
558
+ };
559
+ }
560
+ return { valid: true };
561
+ }
562
+ function validateProfilePicFile(file) {
563
+ if (!SUPPORTED_PROFILE_MIME_TYPES.includes(file.type)) {
564
+ return {
565
+ valid: false,
566
+ status: 415,
567
+ error: "INVALID_FORMAT",
568
+ message: "Unsupported format. Only JPG, JPEG, and PNG are allowed."
569
+ };
570
+ }
571
+ if (file.size > MAX_PROFILE_PIC_SIZE) {
572
+ return {
573
+ valid: false,
574
+ status: 413,
575
+ error: "FILE_TOO_LARGE",
576
+ message: `File size too large. Maximum is ${MAX_PROFILE_PIC_SIZE / 1024 / 1024}MB.`
577
+ };
578
+ }
579
+ return { valid: true };
580
+ }
581
+ function validateVideoFile(file) {
582
+ const { ext } = splitFileName(file.name || "");
583
+ if (!SUPPORTED_VIDEO_FORMATS.includes(ext)) {
584
+ return {
585
+ valid: false,
586
+ status: 415,
587
+ error: "INVALID_FORMAT",
588
+ message: `Unsupported format: ${ext}. Allowed: ${SUPPORTED_VIDEO_FORMATS.join(", ")}`
589
+ };
590
+ }
591
+ if (file.size > MAX_VIDEO_SIZE) {
592
+ return {
593
+ valid: false,
594
+ status: 413,
595
+ error: "FILE_TOO_LARGE",
596
+ message: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds the ${MAX_VIDEO_SIZE / 1024 / 1024}MB limit`
597
+ };
598
+ }
599
+ return { valid: true };
600
+ }
515
601
  export {
516
602
  CloudinaryError,
603
+ MAX_IMAGE_SIZE,
604
+ MAX_PROFILE_PIC_SIZE,
605
+ MAX_VIDEO_SIZE,
606
+ SUPPORTED_IMAGE_FORMATS,
607
+ SUPPORTED_PROFILE_MIME_TYPES,
608
+ SUPPORTED_VIDEO_FORMATS,
517
609
  TRANSFORM_PRESETS,
518
610
  configureSanvikaCloudinary,
611
+ createScopedPublicId,
519
612
  deleteImage,
520
613
  deleteImages,
521
614
  extractPublicId,
@@ -530,14 +623,19 @@ export {
530
623
  isCloudinaryUrl,
531
624
  isProxyMode,
532
625
  isRetriableError,
626
+ normalizeFolderPath,
533
627
  pingCloudinary,
534
628
  runCloudinaryDiagnostics,
629
+ splitFileName,
535
630
  testCloudinaryWebhookSignature,
536
631
  uploadImage,
537
632
  uploadImages,
538
633
  uploadRawFile,
539
634
  uploadVideo,
635
+ validateImageFile,
636
+ validateProfilePicFile,
540
637
  validatePublicId,
638
+ validateVideoFile,
541
639
  verifySanvikaWebhookSignature,
542
640
  withRetry
543
641
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanvika/cloudinary",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Centralized Cloudinary SDK for the Sanvika ecosystem — proxy gateway mode (zero credentials) + legacy direct mode",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",