@sproux/media-sdk 0.1.0 → 0.1.1

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/dist/index.d.cts CHANGED
@@ -14,115 +14,99 @@ declare const MEDIA_STATUS: {
14
14
  readonly READY: "ready";
15
15
  readonly ERROR: "error";
16
16
  };
17
- declare const IMAGE_FORMAT: {
18
- readonly WEBP: "webp";
19
- readonly AVIF: "avif";
20
- readonly JPEG: "jpeg";
21
- readonly PNG: "png";
22
- readonly JPG: "jpg";
23
- };
24
- declare const IMAGE_RESIZE_TYPE: {
25
- readonly FIT: "fit";
26
- readonly FILL: "fill";
27
- readonly AUTO: "auto";
28
- };
17
+ declare enum IMAGE_FORMAT {
18
+ WEBP = "webp",
19
+ AVIF = "avif",
20
+ JPEG = "jpeg",
21
+ PNG = "png",
22
+ GIF = "gif",
23
+ ICO = "ico",
24
+ SVG = "svg",
25
+ JPG = "jpg"
26
+ }
27
+ declare enum IMAGE_RESIZE_TYPE {
28
+ FIT = "fit",
29
+ FILL = "fill",
30
+ FORCE = "force",
31
+ FILL_DOWN = "fill-down",
32
+ AUTO = "auto"
33
+ }
29
34
  declare const R2_PATH: {
30
35
  readonly HLS_PLAYLIST: "playlist.m3u8";
31
36
  readonly THUMBNAIL: "thumbnail.webp";
32
37
  };
33
38
  declare const MEDIA_DEFAULTS: {
34
- readonly IMAGE_FORMAT: "webp";
39
+ readonly IMAGE_FORMAT: IMAGE_FORMAT.WEBP;
35
40
  };
36
41
 
37
- type MediaType = (typeof MEDIA_TYPE)[keyof typeof MEDIA_TYPE];
38
- type MediaPurpose = (typeof MEDIA_PURPOSE)[keyof typeof MEDIA_PURPOSE];
39
- type MediaStatus = (typeof MEDIA_STATUS)[keyof typeof MEDIA_STATUS];
40
- type ImageFormat = (typeof IMAGE_FORMAT)[keyof typeof IMAGE_FORMAT];
41
- type ImageResizeType = (typeof IMAGE_RESIZE_TYPE)[keyof typeof IMAGE_RESIZE_TYPE];
42
- interface ParsedMediaUrl {
43
- /** Full original URL */
44
- originalUrl: string;
45
- /** CDN base including protocol and host */
46
- cdnBase: string;
47
- /** Object key without CDN base */
48
- objectKey: string;
49
- /** Purpose segment: avatar, hero, gallery */
50
- purpose: MediaPurpose;
51
- /** Type segment: image, video */
52
- type: MediaType;
53
- /** User ID segment */
54
- userId: string;
55
- /** File name without extension */
56
- name: string;
57
- /** File extension without dot */
58
- extension: string;
42
+ type ImageFormat = `${IMAGE_FORMAT}`;
43
+ type ImageResizeType = `${IMAGE_RESIZE_TYPE}`;
44
+ interface SprouxMediaConfig {
45
+ /** CDN base URL including protocol and host (e.g. "https://cdn.example.com") */
46
+ cdnUrl: string;
59
47
  }
60
- interface ImageVariantOptions {
48
+ interface ImageUrlOptions {
49
+ /** Output format extension (e.g. "webp", "avif") */
50
+ extension: ImageFormat;
61
51
  /** Target width in pixels (1-4096) */
62
52
  width: number;
63
53
  /** Target height in pixels (1-4096) */
64
54
  height: number;
65
55
  /** Resize type: fit, fill, auto */
66
56
  resizeType: ImageResizeType;
67
- /** Output format (default: webp) */
68
- format?: ImageFormat;
69
- /** Quality 1-100 */
57
+ /** Quality 1-100 (optional) */
70
58
  quality?: number;
71
59
  }
72
60
 
73
- /**
74
- * Parse a full media URL into structured components.
75
- *
76
- * Expected URL pattern:
77
- * {cdnBase}/{purpose}/{type}/{userId}/{name}.{extension}
78
- *
79
- * Example:
80
- * https://cdn.example.com/avatar/image/usr-1/abc.jpg
81
- * → { cdnBase: "https://cdn.example.com", purpose: "avatar", type: "image", userId: "usr-1", name: "abc", extension: "jpg" }
82
- */
83
- declare function parseMediaUrl(originalUrl: string): ParsedMediaUrl;
84
-
85
- /**
86
- * Build a deterministic variant string from image variant options.
87
- *
88
- * Format: {width}x{height}-{resizeType}[-q{quality}]
89
- *
90
- * Examples:
91
- * { width: 200, height: 200, resizeType: 'fit' } → "200x200-fit"
92
- * { width: 200, height: 200, resizeType: 'fit', quality: 80 } → "200x200-fit-q80"
93
- */
94
- declare function buildVariantString(options: ImageVariantOptions): string;
95
-
96
- /**
97
- * Build a full CDN URL for an image variant.
98
- *
99
- * Takes an original image URL and variant options, returns the CDN URL
100
- * for that specific variant.
101
- *
102
- * Example:
103
- * buildImageVariantUrl("https://cdn.example.com/avatar/image/usr-1/abc.jpg", {
104
- * width: 200, height: 200, resizeType: 'fit', quality: 80
105
- * })
106
- * → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
107
- */
108
- declare function buildImageVariantUrl(originalUrl: string, options: ImageVariantOptions): string;
109
-
110
- /**
111
- * Build HLS playlist URL from an original video URL.
112
- *
113
- * Example:
114
- * buildVideoHlsUrl("https://cdn.example.com/gallery/video/usr-1/xyz.mp4")
115
- * → "https://cdn.example.com/gallery/video/usr-1/xyz/playlist.m3u8"
116
- */
117
- declare function buildVideoHlsUrl(originalUrl: string): string;
118
-
119
- /**
120
- * Build thumbnail URL from an original video URL.
121
- *
122
- * Example:
123
- * buildVideoThumbnailUrl("https://cdn.example.com/gallery/video/usr-1/xyz.mp4")
124
- * → "https://cdn.example.com/gallery/video/usr-1/xyz/thumbnail.webp"
125
- */
126
- declare function buildVideoThumbnailUrl(originalUrl: string): string;
61
+ declare class SprouxMedia {
62
+ private static instance;
63
+ private readonly cdnUrl;
64
+ private constructor();
65
+ /**
66
+ * Initialize the singleton with CDN configuration.
67
+ * Returns the singleton instance.
68
+ */
69
+ static init(config: SprouxMediaConfig): SprouxMedia;
70
+ /**
71
+ * Returns the existing singleton instance.
72
+ * Throws if `init()` has not been called.
73
+ */
74
+ static getInstance(): SprouxMedia;
75
+ /**
76
+ * Build a CDN URL for an image variant.
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
81
+ *
82
+ * @example
83
+ * media.getImageUrl('avatar/image/usr-1/abc', {
84
+ * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
85
+ * });
86
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
87
+ */
88
+ getImageUrl(objectKey: string, options: ImageUrlOptions): string;
89
+ /**
90
+ * Build a CDN URL for a video HLS playlist.
91
+ *
92
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
93
+ * @returns Full CDN URL for the HLS playlist
94
+ */
95
+ getVideoHlsUrl(objectKey: string): string;
96
+ /**
97
+ * Build a CDN URL for a video thumbnail.
98
+ *
99
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
100
+ * @returns Full CDN URL for the thumbnail
101
+ */
102
+ getVideoThumbnailUrl(objectKey: string): string;
103
+ /**
104
+ * Build a deterministic variant string from image options.
105
+ *
106
+ * Format: {width}x{height}-{resizeType}[-q{quality}]
107
+ */
108
+ private buildVariantString;
109
+ private validateImageOptions;
110
+ }
127
111
 
128
- export { IMAGE_FORMAT, IMAGE_RESIZE_TYPE, type ImageFormat, type ImageResizeType, type ImageVariantOptions, MEDIA_DEFAULTS, MEDIA_PURPOSE, MEDIA_STATUS, MEDIA_TYPE, type MediaPurpose, type MediaStatus, type MediaType, type ParsedMediaUrl, R2_PATH, buildImageVariantUrl, buildVariantString, buildVideoHlsUrl, buildVideoThumbnailUrl, parseMediaUrl };
112
+ export { IMAGE_FORMAT, IMAGE_RESIZE_TYPE, type ImageFormat, type ImageResizeType, type ImageUrlOptions, MEDIA_DEFAULTS, MEDIA_PURPOSE, MEDIA_STATUS, MEDIA_TYPE, R2_PATH, SprouxMedia, type SprouxMediaConfig };
package/dist/index.d.ts CHANGED
@@ -14,115 +14,99 @@ declare const MEDIA_STATUS: {
14
14
  readonly READY: "ready";
15
15
  readonly ERROR: "error";
16
16
  };
17
- declare const IMAGE_FORMAT: {
18
- readonly WEBP: "webp";
19
- readonly AVIF: "avif";
20
- readonly JPEG: "jpeg";
21
- readonly PNG: "png";
22
- readonly JPG: "jpg";
23
- };
24
- declare const IMAGE_RESIZE_TYPE: {
25
- readonly FIT: "fit";
26
- readonly FILL: "fill";
27
- readonly AUTO: "auto";
28
- };
17
+ declare enum IMAGE_FORMAT {
18
+ WEBP = "webp",
19
+ AVIF = "avif",
20
+ JPEG = "jpeg",
21
+ PNG = "png",
22
+ GIF = "gif",
23
+ ICO = "ico",
24
+ SVG = "svg",
25
+ JPG = "jpg"
26
+ }
27
+ declare enum IMAGE_RESIZE_TYPE {
28
+ FIT = "fit",
29
+ FILL = "fill",
30
+ FORCE = "force",
31
+ FILL_DOWN = "fill-down",
32
+ AUTO = "auto"
33
+ }
29
34
  declare const R2_PATH: {
30
35
  readonly HLS_PLAYLIST: "playlist.m3u8";
31
36
  readonly THUMBNAIL: "thumbnail.webp";
32
37
  };
33
38
  declare const MEDIA_DEFAULTS: {
34
- readonly IMAGE_FORMAT: "webp";
39
+ readonly IMAGE_FORMAT: IMAGE_FORMAT.WEBP;
35
40
  };
36
41
 
37
- type MediaType = (typeof MEDIA_TYPE)[keyof typeof MEDIA_TYPE];
38
- type MediaPurpose = (typeof MEDIA_PURPOSE)[keyof typeof MEDIA_PURPOSE];
39
- type MediaStatus = (typeof MEDIA_STATUS)[keyof typeof MEDIA_STATUS];
40
- type ImageFormat = (typeof IMAGE_FORMAT)[keyof typeof IMAGE_FORMAT];
41
- type ImageResizeType = (typeof IMAGE_RESIZE_TYPE)[keyof typeof IMAGE_RESIZE_TYPE];
42
- interface ParsedMediaUrl {
43
- /** Full original URL */
44
- originalUrl: string;
45
- /** CDN base including protocol and host */
46
- cdnBase: string;
47
- /** Object key without CDN base */
48
- objectKey: string;
49
- /** Purpose segment: avatar, hero, gallery */
50
- purpose: MediaPurpose;
51
- /** Type segment: image, video */
52
- type: MediaType;
53
- /** User ID segment */
54
- userId: string;
55
- /** File name without extension */
56
- name: string;
57
- /** File extension without dot */
58
- extension: string;
42
+ type ImageFormat = `${IMAGE_FORMAT}`;
43
+ type ImageResizeType = `${IMAGE_RESIZE_TYPE}`;
44
+ interface SprouxMediaConfig {
45
+ /** CDN base URL including protocol and host (e.g. "https://cdn.example.com") */
46
+ cdnUrl: string;
59
47
  }
60
- interface ImageVariantOptions {
48
+ interface ImageUrlOptions {
49
+ /** Output format extension (e.g. "webp", "avif") */
50
+ extension: ImageFormat;
61
51
  /** Target width in pixels (1-4096) */
62
52
  width: number;
63
53
  /** Target height in pixels (1-4096) */
64
54
  height: number;
65
55
  /** Resize type: fit, fill, auto */
66
56
  resizeType: ImageResizeType;
67
- /** Output format (default: webp) */
68
- format?: ImageFormat;
69
- /** Quality 1-100 */
57
+ /** Quality 1-100 (optional) */
70
58
  quality?: number;
71
59
  }
72
60
 
73
- /**
74
- * Parse a full media URL into structured components.
75
- *
76
- * Expected URL pattern:
77
- * {cdnBase}/{purpose}/{type}/{userId}/{name}.{extension}
78
- *
79
- * Example:
80
- * https://cdn.example.com/avatar/image/usr-1/abc.jpg
81
- * → { cdnBase: "https://cdn.example.com", purpose: "avatar", type: "image", userId: "usr-1", name: "abc", extension: "jpg" }
82
- */
83
- declare function parseMediaUrl(originalUrl: string): ParsedMediaUrl;
84
-
85
- /**
86
- * Build a deterministic variant string from image variant options.
87
- *
88
- * Format: {width}x{height}-{resizeType}[-q{quality}]
89
- *
90
- * Examples:
91
- * { width: 200, height: 200, resizeType: 'fit' } → "200x200-fit"
92
- * { width: 200, height: 200, resizeType: 'fit', quality: 80 } → "200x200-fit-q80"
93
- */
94
- declare function buildVariantString(options: ImageVariantOptions): string;
95
-
96
- /**
97
- * Build a full CDN URL for an image variant.
98
- *
99
- * Takes an original image URL and variant options, returns the CDN URL
100
- * for that specific variant.
101
- *
102
- * Example:
103
- * buildImageVariantUrl("https://cdn.example.com/avatar/image/usr-1/abc.jpg", {
104
- * width: 200, height: 200, resizeType: 'fit', quality: 80
105
- * })
106
- * → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
107
- */
108
- declare function buildImageVariantUrl(originalUrl: string, options: ImageVariantOptions): string;
109
-
110
- /**
111
- * Build HLS playlist URL from an original video URL.
112
- *
113
- * Example:
114
- * buildVideoHlsUrl("https://cdn.example.com/gallery/video/usr-1/xyz.mp4")
115
- * → "https://cdn.example.com/gallery/video/usr-1/xyz/playlist.m3u8"
116
- */
117
- declare function buildVideoHlsUrl(originalUrl: string): string;
118
-
119
- /**
120
- * Build thumbnail URL from an original video URL.
121
- *
122
- * Example:
123
- * buildVideoThumbnailUrl("https://cdn.example.com/gallery/video/usr-1/xyz.mp4")
124
- * → "https://cdn.example.com/gallery/video/usr-1/xyz/thumbnail.webp"
125
- */
126
- declare function buildVideoThumbnailUrl(originalUrl: string): string;
61
+ declare class SprouxMedia {
62
+ private static instance;
63
+ private readonly cdnUrl;
64
+ private constructor();
65
+ /**
66
+ * Initialize the singleton with CDN configuration.
67
+ * Returns the singleton instance.
68
+ */
69
+ static init(config: SprouxMediaConfig): SprouxMedia;
70
+ /**
71
+ * Returns the existing singleton instance.
72
+ * Throws if `init()` has not been called.
73
+ */
74
+ static getInstance(): SprouxMedia;
75
+ /**
76
+ * Build a CDN URL for an image variant.
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
81
+ *
82
+ * @example
83
+ * media.getImageUrl('avatar/image/usr-1/abc', {
84
+ * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
85
+ * });
86
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
87
+ */
88
+ getImageUrl(objectKey: string, options: ImageUrlOptions): string;
89
+ /**
90
+ * Build a CDN URL for a video HLS playlist.
91
+ *
92
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
93
+ * @returns Full CDN URL for the HLS playlist
94
+ */
95
+ getVideoHlsUrl(objectKey: string): string;
96
+ /**
97
+ * Build a CDN URL for a video thumbnail.
98
+ *
99
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
100
+ * @returns Full CDN URL for the thumbnail
101
+ */
102
+ getVideoThumbnailUrl(objectKey: string): string;
103
+ /**
104
+ * Build a deterministic variant string from image options.
105
+ *
106
+ * Format: {width}x{height}-{resizeType}[-q{quality}]
107
+ */
108
+ private buildVariantString;
109
+ private validateImageOptions;
110
+ }
127
111
 
128
- export { IMAGE_FORMAT, IMAGE_RESIZE_TYPE, type ImageFormat, type ImageResizeType, type ImageVariantOptions, MEDIA_DEFAULTS, MEDIA_PURPOSE, MEDIA_STATUS, MEDIA_TYPE, type MediaPurpose, type MediaStatus, type MediaType, type ParsedMediaUrl, R2_PATH, buildImageVariantUrl, buildVariantString, buildVideoHlsUrl, buildVideoThumbnailUrl, parseMediaUrl };
112
+ export { IMAGE_FORMAT, IMAGE_RESIZE_TYPE, type ImageFormat, type ImageResizeType, type ImageUrlOptions, MEDIA_DEFAULTS, MEDIA_PURPOSE, MEDIA_STATUS, MEDIA_TYPE, R2_PATH, SprouxMedia, type SprouxMediaConfig };
package/dist/index.js CHANGED
@@ -15,136 +15,126 @@ var MEDIA_STATUS = {
15
15
  READY: "ready",
16
16
  ERROR: "error"
17
17
  };
18
- var IMAGE_FORMAT = {
19
- WEBP: "webp",
20
- AVIF: "avif",
21
- JPEG: "jpeg",
22
- PNG: "png",
23
- JPG: "jpg"
24
- };
25
- var IMAGE_RESIZE_TYPE = {
26
- FIT: "fit",
27
- FILL: "fill",
28
- AUTO: "auto"
29
- };
18
+ var IMAGE_FORMAT = /* @__PURE__ */ ((IMAGE_FORMAT2) => {
19
+ IMAGE_FORMAT2["WEBP"] = "webp";
20
+ IMAGE_FORMAT2["AVIF"] = "avif";
21
+ IMAGE_FORMAT2["JPEG"] = "jpeg";
22
+ IMAGE_FORMAT2["PNG"] = "png";
23
+ IMAGE_FORMAT2["GIF"] = "gif";
24
+ IMAGE_FORMAT2["ICO"] = "ico";
25
+ IMAGE_FORMAT2["SVG"] = "svg";
26
+ IMAGE_FORMAT2["JPG"] = "jpg";
27
+ return IMAGE_FORMAT2;
28
+ })(IMAGE_FORMAT || {});
29
+ var IMAGE_RESIZE_TYPE = /* @__PURE__ */ ((IMAGE_RESIZE_TYPE2) => {
30
+ IMAGE_RESIZE_TYPE2["FIT"] = "fit";
31
+ IMAGE_RESIZE_TYPE2["FILL"] = "fill";
32
+ IMAGE_RESIZE_TYPE2["FORCE"] = "force";
33
+ IMAGE_RESIZE_TYPE2["FILL_DOWN"] = "fill-down";
34
+ IMAGE_RESIZE_TYPE2["AUTO"] = "auto";
35
+ return IMAGE_RESIZE_TYPE2;
36
+ })(IMAGE_RESIZE_TYPE || {});
30
37
  var R2_PATH = {
31
38
  HLS_PLAYLIST: "playlist.m3u8",
32
39
  THUMBNAIL: "thumbnail.webp"
33
40
  };
34
41
  var MEDIA_DEFAULTS = {
35
- IMAGE_FORMAT: IMAGE_FORMAT.WEBP
42
+ IMAGE_FORMAT: "webp" /* WEBP */
36
43
  };
37
44
 
38
- // src/parse-media-url.ts
39
- var VALID_PURPOSES = new Set(Object.values(MEDIA_PURPOSE));
40
- var VALID_TYPES = new Set(Object.values(MEDIA_TYPE));
41
- function parseMediaUrl(originalUrl) {
42
- let url;
43
- try {
44
- url = new URL(originalUrl);
45
- } catch {
46
- throw new Error(`Invalid media URL: "${originalUrl}"`);
47
- }
48
- const pathname = url.pathname.startsWith("/") ? url.pathname.slice(1) : url.pathname;
49
- const segments = pathname.split("/");
50
- if (segments.length < 4) {
51
- throw new Error(
52
- `Invalid media URL path: expected at least 4 segments (purpose/type/userId/filename), got ${segments.length} in "${pathname}"`
53
- );
54
- }
55
- const [purpose, type, userId, filename] = segments;
56
- if (!VALID_PURPOSES.has(purpose)) {
57
- throw new Error(
58
- `Invalid media purpose: "${purpose}". Expected one of: ${[...VALID_PURPOSES].join(", ")}`
59
- );
60
- }
61
- if (!VALID_TYPES.has(type)) {
62
- throw new Error(
63
- `Invalid media type: "${type}". Expected one of: ${[...VALID_TYPES].join(", ")}`
64
- );
45
+ // src/sproux-media.ts
46
+ var SprouxMedia = class _SprouxMedia {
47
+ static instance = null;
48
+ cdnUrl;
49
+ constructor(config) {
50
+ this.cdnUrl = config.cdnUrl.replace(/\/+$/, "");
65
51
  }
66
- if (!userId) {
67
- throw new Error("Missing userId segment in media URL");
52
+ /**
53
+ * Initialize the singleton with CDN configuration.
54
+ * Returns the singleton instance.
55
+ */
56
+ static init(config) {
57
+ if (_SprouxMedia.instance) {
58
+ return _SprouxMedia.instance;
59
+ }
60
+ return _SprouxMedia.instance = new _SprouxMedia(config);
68
61
  }
69
- if (!filename) {
70
- throw new Error("Missing filename segment in media URL");
62
+ /**
63
+ * Returns the existing singleton instance.
64
+ * Throws if `init()` has not been called.
65
+ */
66
+ static getInstance() {
67
+ if (!_SprouxMedia.instance) {
68
+ throw new Error("SprouxMedia has not been initialized. Call SprouxMedia.init() first.");
69
+ }
70
+ return _SprouxMedia.instance;
71
71
  }
72
- const dotIndex = filename.lastIndexOf(".");
73
- if (dotIndex <= 0) {
74
- throw new Error(`Invalid filename: "${filename}". Expected format: "name.extension"`);
72
+ /**
73
+ * Build a CDN URL for an image variant.
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
78
+ *
79
+ * @example
80
+ * media.getImageUrl('avatar/image/usr-1/abc', {
81
+ * extension: 'webp', width: 200, height: 200, resizeType: 'fit', quality: 80,
82
+ * });
83
+ * // → "https://cdn.example.com/avatar/image/usr-1/abc-200x200-fit-q80.webp"
84
+ */
85
+ getImageUrl(objectKey, options) {
86
+ this.validateImageOptions(options);
87
+ const variant = this.buildVariantString(options);
88
+ return `${this.cdnUrl}/${objectKey}-${variant}.${options.extension}`;
75
89
  }
76
- const name = filename.slice(0, dotIndex);
77
- const extension = filename.slice(dotIndex + 1);
78
- if (!extension) {
79
- throw new Error(`Missing file extension in filename: "${filename}"`);
90
+ /**
91
+ * Build a CDN URL for a video HLS playlist.
92
+ *
93
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
94
+ * @returns Full CDN URL for the HLS playlist
95
+ */
96
+ getVideoHlsUrl(objectKey) {
97
+ return `${this.cdnUrl}/${objectKey}/${R2_PATH.HLS_PLAYLIST}`;
80
98
  }
81
- const cdnBase = `${url.protocol}//${url.host}`;
82
- const objectKey = pathname;
83
- return {
84
- originalUrl,
85
- cdnBase,
86
- objectKey,
87
- purpose,
88
- type,
89
- userId,
90
- name,
91
- extension
92
- };
93
- }
94
-
95
- // src/build-variant-string.ts
96
- function buildVariantString(options) {
97
- validateVariantOptions(options);
98
- const parts = [`${options.width}x${options.height}`, options.resizeType];
99
- if (options.quality != null) {
100
- parts.push(`q${options.quality}`);
99
+ /**
100
+ * Build a CDN URL for a video thumbnail.
101
+ *
102
+ * @param objectKey - Object key without extension (e.g. "gallery/video/usr-1/xyz")
103
+ * @returns Full CDN URL for the thumbnail
104
+ */
105
+ getVideoThumbnailUrl(objectKey) {
106
+ return `${this.cdnUrl}/${objectKey}/${R2_PATH.THUMBNAIL}`;
101
107
  }
102
- return parts.join("-");
103
- }
104
- function validateVariantOptions(options) {
105
- if (!Number.isInteger(options.width) || options.width < 1 || options.width > 4096) {
106
- throw new Error(`Invalid width: ${options.width}. Must be an integer between 1 and 4096.`);
107
- }
108
- if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
109
- throw new Error(`Invalid height: ${options.height}. Must be an integer between 1 and 4096.`);
108
+ /**
109
+ * Build a deterministic variant string from image options.
110
+ *
111
+ * Format: {width}x{height}-{resizeType}[-q{quality}]
112
+ */
113
+ buildVariantString(options) {
114
+ const parts = [`${options.width}x${options.height}`, options.resizeType];
115
+ if (options.quality != null) {
116
+ parts.push(`q${options.quality}`);
117
+ }
118
+ return parts.join("-");
110
119
  }
111
- if (options.quality != null) {
112
- if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {
120
+ 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.`);
123
+ }
124
+ if (!Number.isInteger(options.height) || options.height < 1 || options.height > 4096) {
113
125
  throw new Error(
114
- `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`
126
+ `Invalid height: ${options.height}. Must be an integer between 1 and 4096.`
115
127
  );
116
128
  }
129
+ if (options.quality != null) {
130
+ if (!Number.isInteger(options.quality) || options.quality < 1 || options.quality > 100) {
131
+ throw new Error(
132
+ `Invalid quality: ${options.quality}. Must be an integer between 1 and 100.`
133
+ );
134
+ }
135
+ }
117
136
  }
118
- }
119
-
120
- // src/build-image-variant-url.ts
121
- function buildImageVariantUrl(originalUrl, options) {
122
- const parsed = parseMediaUrl(originalUrl);
123
- if (parsed.type !== "image") {
124
- throw new Error(`Expected image URL, got type "${parsed.type}" in "${originalUrl}"`);
125
- }
126
- const variant = buildVariantString(options);
127
- const format = options.format ?? MEDIA_DEFAULTS.IMAGE_FORMAT;
128
- return `${parsed.cdnBase}/${parsed.purpose}/${parsed.type}/${parsed.userId}/${parsed.name}-${variant}.${format}`;
129
- }
130
-
131
- // src/build-video-hls-url.ts
132
- function buildVideoHlsUrl(originalUrl) {
133
- const parsed = parseMediaUrl(originalUrl);
134
- if (parsed.type !== "video") {
135
- throw new Error(`Expected video URL, got type "${parsed.type}" in "${originalUrl}"`);
136
- }
137
- return `${parsed.cdnBase}/${parsed.purpose}/video/${parsed.userId}/${parsed.name}/${R2_PATH.HLS_PLAYLIST}`;
138
- }
139
-
140
- // src/build-video-thumbnail-url.ts
141
- function buildVideoThumbnailUrl(originalUrl) {
142
- const parsed = parseMediaUrl(originalUrl);
143
- if (parsed.type !== "video") {
144
- throw new Error(`Expected video URL, got type "${parsed.type}" in "${originalUrl}"`);
145
- }
146
- return `${parsed.cdnBase}/${parsed.purpose}/video/${parsed.userId}/${parsed.name}/${R2_PATH.THUMBNAIL}`;
147
- }
137
+ };
148
138
  export {
149
139
  IMAGE_FORMAT,
150
140
  IMAGE_RESIZE_TYPE,
@@ -153,10 +143,6 @@ export {
153
143
  MEDIA_STATUS,
154
144
  MEDIA_TYPE,
155
145
  R2_PATH,
156
- buildImageVariantUrl,
157
- buildVariantString,
158
- buildVideoHlsUrl,
159
- buildVideoThumbnailUrl,
160
- parseMediaUrl
146
+ SprouxMedia
161
147
  };
162
148
  //# sourceMappingURL=index.js.map