@sproux/media-sdk 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -23,8 +23,8 @@ import { SprouxMedia, IMAGE_FORMAT, IMAGE_RESIZE_TYPE } from '@sproux/media-sdk'
23
23
  // Initialize once (e.g. at app startup)
24
24
  const media = SprouxMedia.init({ cdnUrl: 'https://cdn.example.com' });
25
25
 
26
- // Build an image variant URL using enums
27
- const imageUrl = media.getImageUrl('avatar/image/usr-1/abc', {
26
+ // Build an image variant URL with all options
27
+ const imageUrl = media.getImageUrl('avatar/image/usr-1/abc.jpg', {
28
28
  extension: IMAGE_FORMAT.WEBP,
29
29
  width: 200,
30
30
  height: 200,
@@ -33,6 +33,10 @@ const imageUrl = media.getImageUrl('avatar/image/usr-1/abc', {
33
33
  });
34
34
  // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
35
35
 
36
+ // Or with just the object key (no variant transformation)
37
+ const originalUrl = media.getImageUrl('avatar/image/usr-1/abc.jpg');
38
+ // → "https://cdn.example.com/avatar/image/usr-1/abc.jpg"
39
+
36
40
  // Build a video HLS playlist URL
37
41
  const hlsUrl = media.getVideoHlsUrl('gallery/video/usr-1/intro');
38
42
  // → "https://cdn.example.com/gallery/video/usr-1/intro/playlist.m3u8"
@@ -52,12 +56,16 @@ SprouxMedia.init({ cdnUrl: 'https://cdn.example.com' });
52
56
 
53
57
  // Access from anywhere via getInstance()
54
58
  const media = SprouxMedia.getInstance();
55
- const url = media.getImageUrl('avatar/image/usr-1/photo', {
59
+ // With variant options
60
+ const url = media.getImageUrl('avatar/image/usr-1/photo.jpg', {
56
61
  extension: IMAGE_FORMAT.WEBP,
57
62
  width: 400,
58
63
  height: 300,
59
64
  resizeType: IMAGE_RESIZE_TYPE.FILL,
60
65
  });
66
+
67
+ // Without options — returns the original URL
68
+ const original = media.getImageUrl('avatar/image/usr-1/photo.jpg');
61
69
  ```
62
70
 
63
71
  > Calling `getInstance()` before `init()` will throw an error.
@@ -76,16 +84,18 @@ const media = SprouxMedia.init({ cdnUrl: 'https://cdn.example.com' });
76
84
 
77
85
  Returns the existing singleton instance. Throws if `init()` has not been called.
78
86
 
79
- ### `media.getImageUrl(objectKey: string, options: ImageUrlOptions): string`
87
+ ### `media.getImageUrl(objectKey: string, options?: ImageUrlOptions): string`
80
88
 
81
- Build a CDN URL for an image variant with specific dimensions, resize type, and quality.
89
+ Build a CDN URL for an image variant. When `options` is omitted, returns the original CDN URL for the object key as-is.
82
90
 
83
- - `objectKey` — Object key **without** extension (e.g. `"avatar/image/usr-1/abc"`)
91
+ - `objectKey` — Object key **with** extension (e.g. `"avatar/image/usr-1/abc.jpg"`)
92
+ - `options` — Optional variant transformation options
84
93
 
85
94
  ```typescript
86
95
  import { IMAGE_FORMAT, IMAGE_RESIZE_TYPE } from '@sproux/media-sdk';
87
96
 
88
- media.getImageUrl('avatar/image/usr-1/photo', {
97
+ // With variant options
98
+ media.getImageUrl('avatar/image/usr-1/photo.jpg', {
89
99
  extension: IMAGE_FORMAT.WEBP,
90
100
  width: 400,
91
101
  height: 300,
@@ -93,16 +103,22 @@ media.getImageUrl('avatar/image/usr-1/photo', {
93
103
  quality: 85,
94
104
  });
95
105
  // → "https://cdn.example.com/avatar/image/usr-1/photo-400x300-fill-q85.webp"
106
+
107
+ // Without options — returns the original URL
108
+ media.getImageUrl('avatar/image/usr-1/photo.jpg');
109
+ // → "https://cdn.example.com/avatar/image/usr-1/photo.jpg"
96
110
  ```
97
111
 
98
112
  #### ImageUrlOptions
99
113
 
114
+ All fields are optional.
115
+
100
116
  | Property | Type | Required | Description |
101
117
  | ------------ | ----------------- | -------- | -------------------------------------- |
102
- | `extension` | `ImageFormat` | Yes | Output format (`webp`, `avif`, `jpeg`, `png`, `gif`, `ico`, `svg`, `jpg`) |
103
- | `width` | `number` | Yes | Target width in pixels (1–4096) |
104
- | `height` | `number` | Yes | Target height in pixels (1–4096) |
105
- | `resizeType` | `ImageResizeType` | Yes | Resize strategy: `fit`, `fill`, `force`, `fill-down`, `auto` |
118
+ | `extension` | `ImageFormat` | No | Output format (`webp`, `avif`, `jpeg`, `png`, `gif`, `ico`, `svg`, `jpg`) |
119
+ | `width` | `number` | No | Target width in pixels (1–4096) |
120
+ | `height` | `number` | No | Target height in pixels (1–4096) |
121
+ | `resizeType` | `ImageResizeType` | No | Resize strategy: `fit`, `fill`, `force`, `fill-down`, `auto` |
106
122
  | `quality` | `number` | No | Quality 1–100 |
107
123
 
108
124
  ### `media.getVideoHlsUrl(objectKey: string): string`
@@ -167,7 +183,7 @@ import type {
167
183
  ImageFormat, // 'webp' | 'avif' | 'jpeg' | 'png' | 'gif' | 'ico' | 'svg' | 'jpg'
168
184
  ImageResizeType, // 'fit' | 'fill' | 'force' | 'fill-down' | 'auto'
169
185
  SprouxMediaConfig, // { cdnUrl: string }
170
- ImageUrlOptions, // { extension, width, height, resizeType, quality? }
186
+ ImageUrlOptions, // { extension?, width?, height?, resizeType?, quality? }
171
187
  } from '@sproux/media-sdk';
172
188
  ```
173
189
 
package/dist/index.cjs CHANGED
@@ -103,22 +103,33 @@ var SprouxMedia = class _SprouxMedia {
103
103
  return _SprouxMedia.instance;
104
104
  }
105
105
  /**
106
- * Build a CDN URL for an image variant.
106
+ * Build a CDN URL for an image, optionally with variant transformation.
107
107
  *
108
- * @param objectKey - Object key without extension (e.g. "avatar/image/usr-1/abc")
109
- * @param options - Image variant options including extension, dimensions, resize type, and optional quality
110
- * @returns Full CDN URL for the image variant
108
+ * @param objectKey - Object key with extension (e.g. "avatar/image/usr-1/abc.jpg")
109
+ * @param options - Optional image variant options (extension, dimensions, resize type, quality)
110
+ * @returns Full CDN URL variant URL if options are provided, original URL otherwise
111
111
  *
112
112
  * @example
113
- * media.getImageUrl('avatar/image/usr-1/abc', {
113
+ * // With variant options
114
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg', {
114
115
  * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
115
116
  * });
116
117
  * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
118
+ *
119
+ * @example
120
+ * // Without options — returns the original URL
121
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg');
122
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc.jpg"
117
123
  */
118
124
  getImageUrl(objectKey, options) {
125
+ if (!options || Object.keys(options).length === 0) {
126
+ return `${this.cdnUrl}/${objectKey}`;
127
+ }
119
128
  this.validateImageOptions(options);
129
+ const { name, extension: originalExt } = this.parseObjectKey(objectKey);
130
+ const ext = options.extension ?? originalExt;
120
131
  const variant = this.buildVariantString(options);
121
- return `${this.cdnUrl}/${objectKey}-${variant}.${options.extension}`;
132
+ return variant ? `${this.cdnUrl}/${name}-${variant}.${ext}` : `${this.cdnUrl}/${objectKey}`;
122
133
  }
123
134
  /**
124
135
  * Build a CDN URL for a video HLS playlist.
@@ -138,26 +149,49 @@ var SprouxMedia = class _SprouxMedia {
138
149
  getVideoThumbnailUrl(objectKey) {
139
150
  return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;
140
151
  }
152
+ /**
153
+ * Parse an object key into name (path without extension) and extension.
154
+ */
155
+ parseObjectKey(objectKey) {
156
+ const lastDot = objectKey.lastIndexOf(".");
157
+ if (lastDot === -1) {
158
+ return { name: objectKey, extension: "" };
159
+ }
160
+ return {
161
+ name: objectKey.substring(0, lastDot),
162
+ extension: objectKey.substring(lastDot + 1)
163
+ };
164
+ }
141
165
  /**
142
166
  * Build a deterministic variant string from image options.
143
167
  *
144
168
  * Format: {width}x{height}-{resizeType}[-q{quality}]
145
169
  */
146
170
  buildVariantString(options) {
147
- const parts = [`${options.width}x${options.height}`, options.resizeType];
171
+ const parts = [];
172
+ if (options.width != null && options.height != null) {
173
+ parts.push(`${options.width}x${options.height}`);
174
+ }
175
+ if (options.resizeType != null) {
176
+ parts.push(options.resizeType);
177
+ }
148
178
  if (options.quality != null) {
149
179
  parts.push(`q${options.quality}`);
150
180
  }
151
181
  return parts.join("-");
152
182
  }
153
183
  validateImageOptions(options) {
154
- if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {
155
- throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);
184
+ if (options.width != null) {
185
+ if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {
186
+ throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);
187
+ }
156
188
  }
157
- if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
158
- throw new Error(
159
- `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`
160
- );
189
+ if (options.height != null) {
190
+ if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
191
+ throw new Error(
192
+ `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`
193
+ );
194
+ }
161
195
  }
162
196
  if (options.quality != null) {
163
197
  if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/sproux-media.ts"],"sourcesContent":["// Constants\r\nexport {\r\n MEDIA_TYPE,\r\n MEDIA_PURPOSE,\r\n MEDIA_STATUS,\r\n IMAGE_FORMAT,\r\n IMAGE_RESIZE_TYPE,\r\n R2_PATH,\r\n MEDIA_DEFAULTS,\r\n} from './constants';\r\n\r\n// Types\r\nexport type { ImageFormat, ImageResizeType, SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\n// Singleton class\r\nexport { SprouxMedia } from './sproux-media';\r\n","// ============================================================================\r\n// MEDIA TYPE\r\n// ============================================================================\r\n\r\nexport const MEDIA_TYPE = {\r\n IMAGE: 'image',\r\n VIDEO: 'video',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA PURPOSE\r\n// ============================================================================\r\n\r\nexport const MEDIA_PURPOSE = {\r\n AVATAR: 'avatar',\r\n HERO: 'hero',\r\n GALLERY: 'gallery',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA STATUS\r\n// ============================================================================\r\n\r\nexport const MEDIA_STATUS = {\r\n PENDING: 'pending',\r\n UPLOADED: 'uploaded',\r\n PROCESSING: 'processing',\r\n READY: 'ready',\r\n ERROR: 'error',\r\n} as const;\r\n\r\n// ============================================================================\r\n// IMAGE FORMAT\r\n// ============================================================================\r\n\r\nexport enum IMAGE_FORMAT {\r\n WEBP = 'webp',\r\n AVIF = 'avif',\r\n JPEG = 'jpeg',\r\n PNG = 'png',\r\n GIF = 'gif',\r\n ICO = 'ico',\r\n SVG = 'svg',\r\n JPG = 'jpg',\r\n}\r\n\r\n// ============================================================================\r\n// IMAGE RESIZE TYPE\r\n// ============================================================================\r\n\r\nexport enum IMAGE_RESIZE_TYPE {\r\n FIT = 'fit',\r\n FILL = 'fill',\r\n FORCE = 'force',\r\n FILL_DOWN = 'fill-down',\r\n AUTO = 'auto',\r\n}\r\n\r\n// ============================================================================\r\n// R2/CDN PATH CONSTANTS\r\n// ============================================================================\r\n\r\nexport const R2_PATH = {\r\n HLS_PLAYLIST: 'playlist.m3u8',\r\n THUMBNAIL: 'thumbnail.webp',\r\n} as const;\r\n\r\n// ============================================================================\r\n// DEFAULTS\r\n// ============================================================================\r\n\r\nexport const MEDIA_DEFAULTS = {\r\n IMAGE_FORMAT: IMAGE_FORMAT.WEBP,\r\n} as const;\r\n","import { R2_PATH } from './constants';\r\nimport type { SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\nexport class SprouxMedia {\r\n private static instance: SprouxMedia | null = null;\r\n\r\n private readonly cdnUrl: string;\r\n\r\n private constructor(config: SprouxMediaConfig) {\r\n this.cdnUrl = config.cdnUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n /**\r\n * Initialize the singleton with CDN configuration.\r\n * Returns the singleton instance.\r\n */\r\n static init(config: SprouxMediaConfig): SprouxMedia {\r\n if (SprouxMedia.instance) {\r\n return SprouxMedia.instance;\r\n }\r\n \r\n return (SprouxMedia.instance = new SprouxMedia(config));\r\n }\r\n\r\n /**\r\n * Returns the existing singleton instance.\r\n * Throws if `init()` has not been called.\r\n */\r\n static getInstance(): SprouxMedia {\r\n if (!SprouxMedia.instance) {\r\n throw new Error('SprouxMedia has not been initialized. Call SprouxMedia.init() first.');\r\n }\r\n return SprouxMedia.instance;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for an image variant.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"avatar/image/usr-1/abc\")\r\n * @param options - Image variant options including extension, dimensions, resize type, and optional quality\r\n * @returns Full CDN URL for the image variant\r\n *\r\n * @example\r\n * media.getImageUrl('avatar/image/usr-1/abc', {\r\n * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,\r\n * });\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp\"\r\n */\r\n getImageUrl(objectKey: string, options: ImageUrlOptions): string {\r\n this.validateImageOptions(options);\r\n const variant = this.buildVariantString(options);\r\n return `${this.cdnUrl}/${objectKey}-${variant}.${options.extension}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video HLS playlist.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the HLS playlist\r\n */\r\n getVideoHlsUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.HLS_PLAYLIST}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video thumbnail.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the thumbnail\r\n */\r\n getVideoThumbnailUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;\r\n }\r\n\r\n /**\r\n * Build a deterministic variant string from image options.\r\n *\r\n * Format: {width}x{height}-{resizeType}[-q{quality}]\r\n */\r\n private buildVariantString(options: ImageUrlOptions): string {\r\n const parts: string[] = [`${options.width}x${options.height}`, options.resizeType];\r\n\r\n if (options.quality != null) {\r\n parts.push(`q${options.quality}`);\r\n }\r\n\r\n return parts.join('-');\r\n }\r\n\r\n private validateImageOptions(options: ImageUrlOptions): void {\r\n if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {\r\n throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);\r\n }\r\n\r\n if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {\r\n throw new Error(\r\n `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`,\r\n );\r\n }\r\n\r\n if (options.quality != null) {\r\n if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {\r\n throw new Error(\r\n `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`,\r\n );\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["IMAGE_FORMAT","IMAGE_RESIZE_TYPE"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/sproux-media.ts"],"sourcesContent":["// Constants\r\nexport {\r\n MEDIA_TYPE,\r\n MEDIA_PURPOSE,\r\n MEDIA_STATUS,\r\n IMAGE_FORMAT,\r\n IMAGE_RESIZE_TYPE,\r\n R2_PATH,\r\n MEDIA_DEFAULTS,\r\n} from './constants';\r\n\r\n// Types\r\nexport type { ImageFormat, ImageResizeType, SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\n// Singleton class\r\nexport { SprouxMedia } from './sproux-media';\r\n","// ============================================================================\r\n// MEDIA TYPE\r\n// ============================================================================\r\n\r\nexport const MEDIA_TYPE = {\r\n IMAGE: 'image',\r\n VIDEO: 'video',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA PURPOSE\r\n// ============================================================================\r\n\r\nexport const MEDIA_PURPOSE = {\r\n AVATAR: 'avatar',\r\n HERO: 'hero',\r\n GALLERY: 'gallery',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA STATUS\r\n// ============================================================================\r\n\r\nexport const MEDIA_STATUS = {\r\n PENDING: 'pending',\r\n UPLOADED: 'uploaded',\r\n PROCESSING: 'processing',\r\n READY: 'ready',\r\n ERROR: 'error',\r\n} as const;\r\n\r\n// ============================================================================\r\n// IMAGE FORMAT\r\n// ============================================================================\r\n\r\nexport enum IMAGE_FORMAT {\r\n WEBP = 'webp',\r\n AVIF = 'avif',\r\n JPEG = 'jpeg',\r\n PNG = 'png',\r\n GIF = 'gif',\r\n ICO = 'ico',\r\n SVG = 'svg',\r\n JPG = 'jpg',\r\n}\r\n\r\n// ============================================================================\r\n// IMAGE RESIZE TYPE\r\n// ============================================================================\r\n\r\nexport enum IMAGE_RESIZE_TYPE {\r\n FIT = 'fit',\r\n FILL = 'fill',\r\n FORCE = 'force',\r\n FILL_DOWN = 'fill-down',\r\n AUTO = 'auto',\r\n}\r\n\r\n// ============================================================================\r\n// R2/CDN PATH CONSTANTS\r\n// ============================================================================\r\n\r\nexport const R2_PATH = {\r\n HLS_PLAYLIST: 'playlist.m3u8',\r\n THUMBNAIL: 'thumbnail.webp',\r\n} as const;\r\n\r\n// ============================================================================\r\n// DEFAULTS\r\n// ============================================================================\r\n\r\nexport const MEDIA_DEFAULTS = {\r\n IMAGE_FORMAT: IMAGE_FORMAT.WEBP,\r\n} as const;\r\n","import { R2_PATH } from './constants';\r\nimport type { SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\nexport class SprouxMedia {\r\n private static instance: SprouxMedia | null = null;\r\n\r\n private readonly cdnUrl: string;\r\n\r\n private constructor(config: SprouxMediaConfig) {\r\n this.cdnUrl = config.cdnUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n /**\r\n * Initialize the singleton with CDN configuration.\r\n * Returns the singleton instance.\r\n */\r\n static init(config: SprouxMediaConfig): SprouxMedia {\r\n if (SprouxMedia.instance) {\r\n return SprouxMedia.instance;\r\n }\r\n \r\n return (SprouxMedia.instance = new SprouxMedia(config));\r\n }\r\n\r\n /**\r\n * Returns the existing singleton instance.\r\n * Throws if `init()` has not been called.\r\n */\r\n static getInstance(): SprouxMedia {\r\n if (!SprouxMedia.instance) {\r\n throw new Error('SprouxMedia has not been initialized. Call SprouxMedia.init() first.');\r\n }\r\n return SprouxMedia.instance;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for an image, optionally with variant transformation.\r\n *\r\n * @param objectKey - Object key with extension (e.g. \"avatar/image/usr-1/abc.jpg\")\r\n * @param options - Optional image variant options (extension, dimensions, resize type, quality)\r\n * @returns Full CDN URL — variant URL if options are provided, original URL otherwise\r\n *\r\n * @example\r\n * // With variant options\r\n * media.getImageUrl('avatar/image/usr-1/abc.jpg', {\r\n * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,\r\n * });\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp\"\r\n *\r\n * @example\r\n * // Without options — returns the original URL\r\n * media.getImageUrl('avatar/image/usr-1/abc.jpg');\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc.jpg\"\r\n */\r\n getImageUrl(objectKey: string, options?: ImageUrlOptions): string {\r\n if (!options || Object.keys(options).length === 0) {\r\n return `${this.cdnUrl}/${objectKey}`;\r\n }\r\n\r\n this.validateImageOptions(options);\r\n\r\n const { name, extension: originalExt } = this.parseObjectKey(objectKey);\r\n const ext = options.extension ?? originalExt;\r\n const variant = this.buildVariantString(options);\r\n\r\n return variant\r\n ? `${this.cdnUrl}/${name}-${variant}.${ext}`\r\n : `${this.cdnUrl}/${objectKey}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video HLS playlist.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the HLS playlist\r\n */\r\n getVideoHlsUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.HLS_PLAYLIST}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video thumbnail.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the thumbnail\r\n */\r\n getVideoThumbnailUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;\r\n }\r\n\r\n /**\r\n * Parse an object key into name (path without extension) and extension.\r\n */\r\n private parseObjectKey(objectKey: string): { name: string; extension: string } {\r\n const lastDot = objectKey.lastIndexOf('.');\r\n if (lastDot === -1) {\r\n return { name: objectKey, extension: '' };\r\n }\r\n return {\r\n name: objectKey.substring(0, lastDot),\r\n extension: objectKey.substring(lastDot + 1),\r\n };\r\n }\r\n\r\n /**\r\n * Build a deterministic variant string from image options.\r\n *\r\n * Format: {width}x{height}-{resizeType}[-q{quality}]\r\n */\r\n private buildVariantString(options: ImageUrlOptions): string {\r\n const parts: string[] = [];\r\n\r\n if (options.width != null && options.height != null) {\r\n parts.push(`${options.width}x${options.height}`);\r\n }\r\n\r\n if (options.resizeType != null) {\r\n parts.push(options.resizeType);\r\n }\r\n\r\n if (options.quality != null) {\r\n parts.push(`q${options.quality}`);\r\n }\r\n\r\n return parts.join('-');\r\n }\r\n\r\n private validateImageOptions(options: ImageUrlOptions): void {\r\n if (options.width != null) {\r\n if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {\r\n throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);\r\n }\r\n }\r\n\r\n if (options.height != null) {\r\n if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {\r\n throw new Error(\r\n `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`,\r\n );\r\n }\r\n }\r\n\r\n if (options.quality != null) {\r\n if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {\r\n throw new Error(\r\n `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`,\r\n );\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["IMAGE_FORMAT","IMAGE_RESIZE_TYPE"]}
package/dist/index.d.cts CHANGED
@@ -47,13 +47,13 @@ interface SprouxMediaConfig {
47
47
  }
48
48
  interface ImageUrlOptions {
49
49
  /** Output format extension (e.g. "webp", "avif") */
50
- extension: ImageFormat;
50
+ extension?: ImageFormat;
51
51
  /** Target width in pixels (1-4096) */
52
- width: number;
52
+ width?: number;
53
53
  /** Target height in pixels (1-4096) */
54
- height: number;
54
+ height?: number;
55
55
  /** Resize type: fit, fill, auto */
56
- resizeType: ImageResizeType;
56
+ resizeType?: ImageResizeType;
57
57
  /** Quality 1-100 (optional) */
58
58
  quality?: number;
59
59
  }
@@ -73,19 +73,25 @@ declare class SprouxMedia {
73
73
  */
74
74
  static getInstance(): SprouxMedia;
75
75
  /**
76
- * Build a CDN URL for an image variant.
76
+ * Build a CDN URL for an image, optionally with variant transformation.
77
77
  *
78
- * @param objectKey - Object key without extension (e.g. "avatar/image/usr-1/abc")
79
- * @param options - Image variant options including extension, dimensions, resize type, and optional quality
80
- * @returns Full CDN URL for the image variant
78
+ * @param objectKey - Object key with extension (e.g. "avatar/image/usr-1/abc.jpg")
79
+ * @param options - Optional image variant options (extension, dimensions, resize type, quality)
80
+ * @returns Full CDN URL variant URL if options are provided, original URL otherwise
81
81
  *
82
82
  * @example
83
- * media.getImageUrl('avatar/image/usr-1/abc', {
83
+ * // With variant options
84
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg', {
84
85
  * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
85
86
  * });
86
87
  * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
88
+ *
89
+ * @example
90
+ * // Without options — returns the original URL
91
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg');
92
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc.jpg"
87
93
  */
88
- getImageUrl(objectKey: string, options: ImageUrlOptions): string;
94
+ getImageUrl(objectKey: string, options?: ImageUrlOptions): string;
89
95
  /**
90
96
  * Build a CDN URL for a video HLS playlist.
91
97
  *
@@ -100,6 +106,10 @@ declare class SprouxMedia {
100
106
  * @returns Full CDN URL for the thumbnail
101
107
  */
102
108
  getVideoThumbnailUrl(objectKey: string): string;
109
+ /**
110
+ * Parse an object key into name (path without extension) and extension.
111
+ */
112
+ private parseObjectKey;
103
113
  /**
104
114
  * Build a deterministic variant string from image options.
105
115
  *
package/dist/index.d.ts CHANGED
@@ -47,13 +47,13 @@ interface SprouxMediaConfig {
47
47
  }
48
48
  interface ImageUrlOptions {
49
49
  /** Output format extension (e.g. "webp", "avif") */
50
- extension: ImageFormat;
50
+ extension?: ImageFormat;
51
51
  /** Target width in pixels (1-4096) */
52
- width: number;
52
+ width?: number;
53
53
  /** Target height in pixels (1-4096) */
54
- height: number;
54
+ height?: number;
55
55
  /** Resize type: fit, fill, auto */
56
- resizeType: ImageResizeType;
56
+ resizeType?: ImageResizeType;
57
57
  /** Quality 1-100 (optional) */
58
58
  quality?: number;
59
59
  }
@@ -73,19 +73,25 @@ declare class SprouxMedia {
73
73
  */
74
74
  static getInstance(): SprouxMedia;
75
75
  /**
76
- * Build a CDN URL for an image variant.
76
+ * Build a CDN URL for an image, optionally with variant transformation.
77
77
  *
78
- * @param objectKey - Object key without extension (e.g. "avatar/image/usr-1/abc")
79
- * @param options - Image variant options including extension, dimensions, resize type, and optional quality
80
- * @returns Full CDN URL for the image variant
78
+ * @param objectKey - Object key with extension (e.g. "avatar/image/usr-1/abc.jpg")
79
+ * @param options - Optional image variant options (extension, dimensions, resize type, quality)
80
+ * @returns Full CDN URL variant URL if options are provided, original URL otherwise
81
81
  *
82
82
  * @example
83
- * media.getImageUrl('avatar/image/usr-1/abc', {
83
+ * // With variant options
84
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg', {
84
85
  * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
85
86
  * });
86
87
  * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
88
+ *
89
+ * @example
90
+ * // Without options — returns the original URL
91
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg');
92
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc.jpg"
87
93
  */
88
- getImageUrl(objectKey: string, options: ImageUrlOptions): string;
94
+ getImageUrl(objectKey: string, options?: ImageUrlOptions): string;
89
95
  /**
90
96
  * Build a CDN URL for a video HLS playlist.
91
97
  *
@@ -100,6 +106,10 @@ declare class SprouxMedia {
100
106
  * @returns Full CDN URL for the thumbnail
101
107
  */
102
108
  getVideoThumbnailUrl(objectKey: string): string;
109
+ /**
110
+ * Parse an object key into name (path without extension) and extension.
111
+ */
112
+ private parseObjectKey;
103
113
  /**
104
114
  * Build a deterministic variant string from image options.
105
115
  *
package/dist/index.js CHANGED
@@ -70,22 +70,33 @@ var SprouxMedia = class _SprouxMedia {
70
70
  return _SprouxMedia.instance;
71
71
  }
72
72
  /**
73
- * Build a CDN URL for an image variant.
73
+ * Build a CDN URL for an image, optionally with variant transformation.
74
74
  *
75
- * @param objectKey - Object key without extension (e.g. "avatar/image/usr-1/abc")
76
- * @param options - Image variant options including extension, dimensions, resize type, and optional quality
77
- * @returns Full CDN URL for the image variant
75
+ * @param objectKey - Object key with extension (e.g. "avatar/image/usr-1/abc.jpg")
76
+ * @param options - Optional image variant options (extension, dimensions, resize type, quality)
77
+ * @returns Full CDN URL variant URL if options are provided, original URL otherwise
78
78
  *
79
79
  * @example
80
- * media.getImageUrl('avatar/image/usr-1/abc', {
80
+ * // With variant options
81
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg', {
81
82
  * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
82
83
  * });
83
84
  * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
85
+ *
86
+ * @example
87
+ * // Without options — returns the original URL
88
+ * media.getImageUrl('avatar/image/usr-1/abc.jpg');
89
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc.jpg"
84
90
  */
85
91
  getImageUrl(objectKey, options) {
92
+ if (!options || Object.keys(options).length === 0) {
93
+ return `${this.cdnUrl}/${objectKey}`;
94
+ }
86
95
  this.validateImageOptions(options);
96
+ const { name, extension: originalExt } = this.parseObjectKey(objectKey);
97
+ const ext = options.extension ?? originalExt;
87
98
  const variant = this.buildVariantString(options);
88
- return `${this.cdnUrl}/${objectKey}-${variant}.${options.extension}`;
99
+ return variant ? `${this.cdnUrl}/${name}-${variant}.${ext}` : `${this.cdnUrl}/${objectKey}`;
89
100
  }
90
101
  /**
91
102
  * Build a CDN URL for a video HLS playlist.
@@ -105,26 +116,49 @@ var SprouxMedia = class _SprouxMedia {
105
116
  getVideoThumbnailUrl(objectKey) {
106
117
  return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;
107
118
  }
119
+ /**
120
+ * Parse an object key into name (path without extension) and extension.
121
+ */
122
+ parseObjectKey(objectKey) {
123
+ const lastDot = objectKey.lastIndexOf(".");
124
+ if (lastDot === -1) {
125
+ return { name: objectKey, extension: "" };
126
+ }
127
+ return {
128
+ name: objectKey.substring(0, lastDot),
129
+ extension: objectKey.substring(lastDot + 1)
130
+ };
131
+ }
108
132
  /**
109
133
  * Build a deterministic variant string from image options.
110
134
  *
111
135
  * Format: {width}x{height}-{resizeType}[-q{quality}]
112
136
  */
113
137
  buildVariantString(options) {
114
- const parts = [`${options.width}x${options.height}`, options.resizeType];
138
+ const parts = [];
139
+ if (options.width != null && options.height != null) {
140
+ parts.push(`${options.width}x${options.height}`);
141
+ }
142
+ if (options.resizeType != null) {
143
+ parts.push(options.resizeType);
144
+ }
115
145
  if (options.quality != null) {
116
146
  parts.push(`q${options.quality}`);
117
147
  }
118
148
  return parts.join("-");
119
149
  }
120
150
  validateImageOptions(options) {
121
- if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {
122
- throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);
151
+ if (options.width != null) {
152
+ if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {
153
+ throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);
154
+ }
123
155
  }
124
- if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
125
- throw new Error(
126
- `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`
127
- );
156
+ if (options.height != null) {
157
+ if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
158
+ throw new Error(
159
+ `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`
160
+ );
161
+ }
128
162
  }
129
163
  if (options.quality != null) {
130
164
  if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/sproux-media.ts"],"sourcesContent":["// ============================================================================\r\n// MEDIA TYPE\r\n// ============================================================================\r\n\r\nexport const MEDIA_TYPE = {\r\n IMAGE: 'image',\r\n VIDEO: 'video',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA PURPOSE\r\n// ============================================================================\r\n\r\nexport const MEDIA_PURPOSE = {\r\n AVATAR: 'avatar',\r\n HERO: 'hero',\r\n GALLERY: 'gallery',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA STATUS\r\n// ============================================================================\r\n\r\nexport const MEDIA_STATUS = {\r\n PENDING: 'pending',\r\n UPLOADED: 'uploaded',\r\n PROCESSING: 'processing',\r\n READY: 'ready',\r\n ERROR: 'error',\r\n} as const;\r\n\r\n// ============================================================================\r\n// IMAGE FORMAT\r\n// ============================================================================\r\n\r\nexport enum IMAGE_FORMAT {\r\n WEBP = 'webp',\r\n AVIF = 'avif',\r\n JPEG = 'jpeg',\r\n PNG = 'png',\r\n GIF = 'gif',\r\n ICO = 'ico',\r\n SVG = 'svg',\r\n JPG = 'jpg',\r\n}\r\n\r\n// ============================================================================\r\n// IMAGE RESIZE TYPE\r\n// ============================================================================\r\n\r\nexport enum IMAGE_RESIZE_TYPE {\r\n FIT = 'fit',\r\n FILL = 'fill',\r\n FORCE = 'force',\r\n FILL_DOWN = 'fill-down',\r\n AUTO = 'auto',\r\n}\r\n\r\n// ============================================================================\r\n// R2/CDN PATH CONSTANTS\r\n// ============================================================================\r\n\r\nexport const R2_PATH = {\r\n HLS_PLAYLIST: 'playlist.m3u8',\r\n THUMBNAIL: 'thumbnail.webp',\r\n} as const;\r\n\r\n// ============================================================================\r\n// DEFAULTS\r\n// ============================================================================\r\n\r\nexport const MEDIA_DEFAULTS = {\r\n IMAGE_FORMAT: IMAGE_FORMAT.WEBP,\r\n} as const;\r\n","import { R2_PATH } from './constants';\r\nimport type { SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\nexport class SprouxMedia {\r\n private static instance: SprouxMedia | null = null;\r\n\r\n private readonly cdnUrl: string;\r\n\r\n private constructor(config: SprouxMediaConfig) {\r\n this.cdnUrl = config.cdnUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n /**\r\n * Initialize the singleton with CDN configuration.\r\n * Returns the singleton instance.\r\n */\r\n static init(config: SprouxMediaConfig): SprouxMedia {\r\n if (SprouxMedia.instance) {\r\n return SprouxMedia.instance;\r\n }\r\n \r\n return (SprouxMedia.instance = new SprouxMedia(config));\r\n }\r\n\r\n /**\r\n * Returns the existing singleton instance.\r\n * Throws if `init()` has not been called.\r\n */\r\n static getInstance(): SprouxMedia {\r\n if (!SprouxMedia.instance) {\r\n throw new Error('SprouxMedia has not been initialized. Call SprouxMedia.init() first.');\r\n }\r\n return SprouxMedia.instance;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for an image variant.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"avatar/image/usr-1/abc\")\r\n * @param options - Image variant options including extension, dimensions, resize type, and optional quality\r\n * @returns Full CDN URL for the image variant\r\n *\r\n * @example\r\n * media.getImageUrl('avatar/image/usr-1/abc', {\r\n * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,\r\n * });\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp\"\r\n */\r\n getImageUrl(objectKey: string, options: ImageUrlOptions): string {\r\n this.validateImageOptions(options);\r\n const variant = this.buildVariantString(options);\r\n return `${this.cdnUrl}/${objectKey}-${variant}.${options.extension}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video HLS playlist.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the HLS playlist\r\n */\r\n getVideoHlsUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.HLS_PLAYLIST}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video thumbnail.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the thumbnail\r\n */\r\n getVideoThumbnailUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;\r\n }\r\n\r\n /**\r\n * Build a deterministic variant string from image options.\r\n *\r\n * Format: {width}x{height}-{resizeType}[-q{quality}]\r\n */\r\n private buildVariantString(options: ImageUrlOptions): string {\r\n const parts: string[] = [`${options.width}x${options.height}`, options.resizeType];\r\n\r\n if (options.quality != null) {\r\n parts.push(`q${options.quality}`);\r\n }\r\n\r\n return parts.join('-');\r\n }\r\n\r\n private validateImageOptions(options: ImageUrlOptions): void {\r\n if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {\r\n throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);\r\n }\r\n\r\n if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {\r\n throw new Error(\r\n `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`,\r\n );\r\n }\r\n\r\n if (options.quality != null) {\r\n if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {\r\n throw new Error(\r\n `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`,\r\n );\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["IMAGE_FORMAT","IMAGE_RESIZE_TYPE"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/sproux-media.ts"],"sourcesContent":["// ============================================================================\r\n// MEDIA TYPE\r\n// ============================================================================\r\n\r\nexport const MEDIA_TYPE = {\r\n IMAGE: 'image',\r\n VIDEO: 'video',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA PURPOSE\r\n// ============================================================================\r\n\r\nexport const MEDIA_PURPOSE = {\r\n AVATAR: 'avatar',\r\n HERO: 'hero',\r\n GALLERY: 'gallery',\r\n} as const;\r\n\r\n// ============================================================================\r\n// MEDIA STATUS\r\n// ============================================================================\r\n\r\nexport const MEDIA_STATUS = {\r\n PENDING: 'pending',\r\n UPLOADED: 'uploaded',\r\n PROCESSING: 'processing',\r\n READY: 'ready',\r\n ERROR: 'error',\r\n} as const;\r\n\r\n// ============================================================================\r\n// IMAGE FORMAT\r\n// ============================================================================\r\n\r\nexport enum IMAGE_FORMAT {\r\n WEBP = 'webp',\r\n AVIF = 'avif',\r\n JPEG = 'jpeg',\r\n PNG = 'png',\r\n GIF = 'gif',\r\n ICO = 'ico',\r\n SVG = 'svg',\r\n JPG = 'jpg',\r\n}\r\n\r\n// ============================================================================\r\n// IMAGE RESIZE TYPE\r\n// ============================================================================\r\n\r\nexport enum IMAGE_RESIZE_TYPE {\r\n FIT = 'fit',\r\n FILL = 'fill',\r\n FORCE = 'force',\r\n FILL_DOWN = 'fill-down',\r\n AUTO = 'auto',\r\n}\r\n\r\n// ============================================================================\r\n// R2/CDN PATH CONSTANTS\r\n// ============================================================================\r\n\r\nexport const R2_PATH = {\r\n HLS_PLAYLIST: 'playlist.m3u8',\r\n THUMBNAIL: 'thumbnail.webp',\r\n} as const;\r\n\r\n// ============================================================================\r\n// DEFAULTS\r\n// ============================================================================\r\n\r\nexport const MEDIA_DEFAULTS = {\r\n IMAGE_FORMAT: IMAGE_FORMAT.WEBP,\r\n} as const;\r\n","import { R2_PATH } from './constants';\r\nimport type { SprouxMediaConfig, ImageUrlOptions } from './types';\r\n\r\nexport class SprouxMedia {\r\n private static instance: SprouxMedia | null = null;\r\n\r\n private readonly cdnUrl: string;\r\n\r\n private constructor(config: SprouxMediaConfig) {\r\n this.cdnUrl = config.cdnUrl.replace(/\\/+$/, '');\r\n }\r\n\r\n /**\r\n * Initialize the singleton with CDN configuration.\r\n * Returns the singleton instance.\r\n */\r\n static init(config: SprouxMediaConfig): SprouxMedia {\r\n if (SprouxMedia.instance) {\r\n return SprouxMedia.instance;\r\n }\r\n \r\n return (SprouxMedia.instance = new SprouxMedia(config));\r\n }\r\n\r\n /**\r\n * Returns the existing singleton instance.\r\n * Throws if `init()` has not been called.\r\n */\r\n static getInstance(): SprouxMedia {\r\n if (!SprouxMedia.instance) {\r\n throw new Error('SprouxMedia has not been initialized. Call SprouxMedia.init() first.');\r\n }\r\n return SprouxMedia.instance;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for an image, optionally with variant transformation.\r\n *\r\n * @param objectKey - Object key with extension (e.g. \"avatar/image/usr-1/abc.jpg\")\r\n * @param options - Optional image variant options (extension, dimensions, resize type, quality)\r\n * @returns Full CDN URL — variant URL if options are provided, original URL otherwise\r\n *\r\n * @example\r\n * // With variant options\r\n * media.getImageUrl('avatar/image/usr-1/abc.jpg', {\r\n * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,\r\n * });\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp\"\r\n *\r\n * @example\r\n * // Without options — returns the original URL\r\n * media.getImageUrl('avatar/image/usr-1/abc.jpg');\r\n * // → \"https://cdn.example.com/avatar/image/usr-1/abc.jpg\"\r\n */\r\n getImageUrl(objectKey: string, options?: ImageUrlOptions): string {\r\n if (!options || Object.keys(options).length === 0) {\r\n return `${this.cdnUrl}/${objectKey}`;\r\n }\r\n\r\n this.validateImageOptions(options);\r\n\r\n const { name, extension: originalExt } = this.parseObjectKey(objectKey);\r\n const ext = options.extension ?? originalExt;\r\n const variant = this.buildVariantString(options);\r\n\r\n return variant\r\n ? `${this.cdnUrl}/${name}-${variant}.${ext}`\r\n : `${this.cdnUrl}/${objectKey}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video HLS playlist.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the HLS playlist\r\n */\r\n getVideoHlsUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.HLS_PLAYLIST}`;\r\n }\r\n\r\n /**\r\n * Build a CDN URL for a video thumbnail.\r\n *\r\n * @param objectKey - Object key without extension (e.g. \"gallery/video/usr-1/xyz\")\r\n * @returns Full CDN URL for the thumbnail\r\n */\r\n getVideoThumbnailUrl(objectKey: string): string {\r\n return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;\r\n }\r\n\r\n /**\r\n * Parse an object key into name (path without extension) and extension.\r\n */\r\n private parseObjectKey(objectKey: string): { name: string; extension: string } {\r\n const lastDot = objectKey.lastIndexOf('.');\r\n if (lastDot === -1) {\r\n return { name: objectKey, extension: '' };\r\n }\r\n return {\r\n name: objectKey.substring(0, lastDot),\r\n extension: objectKey.substring(lastDot + 1),\r\n };\r\n }\r\n\r\n /**\r\n * Build a deterministic variant string from image options.\r\n *\r\n * Format: {width}x{height}-{resizeType}[-q{quality}]\r\n */\r\n private buildVariantString(options: ImageUrlOptions): string {\r\n const parts: string[] = [];\r\n\r\n if (options.width != null && options.height != null) {\r\n parts.push(`${options.width}x${options.height}`);\r\n }\r\n\r\n if (options.resizeType != null) {\r\n parts.push(options.resizeType);\r\n }\r\n\r\n if (options.quality != null) {\r\n parts.push(`q${options.quality}`);\r\n }\r\n\r\n return parts.join('-');\r\n }\r\n\r\n private validateImageOptions(options: ImageUrlOptions): void {\r\n if (options.width != null) {\r\n if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {\r\n throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);\r\n }\r\n }\r\n\r\n if (options.height != null) {\r\n if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {\r\n throw new Error(\r\n `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`,\r\n );\r\n }\r\n }\r\n\r\n if (options.quality != null) {\r\n if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {\r\n throw new Error(\r\n `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`,\r\n );\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["IMAGE_FORMAT","IMAGE_RESIZE_TYPE"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproux/media-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Media URL builder library for Sproux — build image variant URLs, video HLS playlist URLs, and thumbnails from original media URLs.",
5
5
  "author": "Sproux",
6
6
  "license": "MIT",