@flightdev/image 0.0.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.
@@ -0,0 +1,109 @@
1
+ import { defineComponent, ref, computed, h } from 'vue';
2
+
3
+ // src/components/vue.ts
4
+ function buildOptimizedUrl(src, options, baseUrl = "") {
5
+ const params = new URLSearchParams();
6
+ if (options.width) params.set("w", options.width.toString());
7
+ if (options.height) params.set("h", options.height.toString());
8
+ if (options.format && options.format !== "auto") params.set("f", options.format);
9
+ if (options.quality) params.set("q", options.quality.toString());
10
+ if (options.fit) params.set("fit", options.fit);
11
+ const queryString = params.toString();
12
+ const normalizedSrc = src.startsWith("/") ? src : `/${src}`;
13
+ return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? "?" + queryString : ""}`;
14
+ }
15
+ var FlightImage = defineComponent({
16
+ name: "FlightImage",
17
+ props: {
18
+ src: { type: String, required: true },
19
+ alt: { type: String, required: true },
20
+ width: { type: Number, default: void 0 },
21
+ height: { type: Number, default: void 0 },
22
+ format: { type: String, default: "auto" },
23
+ quality: { type: Number, default: 80 },
24
+ fit: { type: String, default: "cover" },
25
+ priority: { type: Boolean, default: false },
26
+ placeholder: { type: String, default: "empty" },
27
+ blurDataUrl: { type: String, default: void 0 },
28
+ sizes: { type: String, default: void 0 },
29
+ baseUrl: { type: String, default: "" }
30
+ },
31
+ setup(props) {
32
+ const isLoaded = ref(false);
33
+ const error = ref(false);
34
+ const optimizedSrc = computed(
35
+ () => buildOptimizedUrl(props.src, {
36
+ width: props.width,
37
+ height: props.height,
38
+ format: props.format,
39
+ quality: props.quality,
40
+ fit: props.fit
41
+ }, props.baseUrl)
42
+ );
43
+ const srcSet = computed(() => {
44
+ if (!props.width) return void 0;
45
+ const widths = [props.width, props.width * 1.5, props.width * 2].map(Math.round);
46
+ return widths.map((w) => {
47
+ const h2 = props.height ? Math.round(props.height * w / props.width) : void 0;
48
+ return `${buildOptimizedUrl(props.src, { width: w, height: h2, format: props.format, quality: props.quality, fit: props.fit }, props.baseUrl)} ${w}w`;
49
+ }).join(", ");
50
+ });
51
+ const defaultSizes = computed(
52
+ () => props.width ? `(max-width: ${props.width}px) 100vw, ${props.width}px` : void 0
53
+ );
54
+ const handleLoad = () => {
55
+ isLoaded.value = true;
56
+ };
57
+ const handleError = () => {
58
+ error.value = true;
59
+ };
60
+ return () => {
61
+ const containerStyle = {
62
+ position: "relative",
63
+ overflow: "hidden",
64
+ width: props.width ? `${props.width}px` : void 0,
65
+ height: props.height ? `${props.height}px` : void 0
66
+ };
67
+ const imageStyle = {
68
+ width: "100%",
69
+ height: "100%",
70
+ objectFit: props.fit,
71
+ opacity: isLoaded.value ? 1 : 0,
72
+ transition: "opacity 0.3s ease-in-out"
73
+ };
74
+ const blurStyle = {
75
+ position: "absolute",
76
+ inset: 0,
77
+ backgroundImage: props.blurDataUrl ? `url(${props.blurDataUrl})` : void 0,
78
+ backgroundSize: "cover",
79
+ backgroundPosition: "center",
80
+ filter: "blur(20px)",
81
+ transform: "scale(1.1)"
82
+ };
83
+ return h("div", { style: containerStyle }, [
84
+ // Blur placeholder
85
+ props.placeholder === "blur" && props.blurDataUrl && !isLoaded.value ? h("div", { style: blurStyle, "aria-hidden": true }) : null,
86
+ // Image
87
+ h("img", {
88
+ src: optimizedSrc.value,
89
+ srcset: srcSet.value,
90
+ sizes: props.sizes ?? defaultSizes.value,
91
+ alt: props.alt,
92
+ width: props.width,
93
+ height: props.height,
94
+ loading: props.priority ? "eager" : "lazy",
95
+ decoding: props.priority ? "sync" : "async",
96
+ fetchpriority: props.priority ? "high" : void 0,
97
+ onLoad: handleLoad,
98
+ onError: handleError,
99
+ style: imageStyle
100
+ })
101
+ ]);
102
+ };
103
+ }
104
+ });
105
+ var vue_default = FlightImage;
106
+
107
+ export { FlightImage, vue_default as default };
108
+ //# sourceMappingURL=vue.js.map
109
+ //# sourceMappingURL=vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/vue.ts"],"names":["h"],"mappings":"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","file":"vue.js","sourcesContent":["/**\r\n * Vue Image Component for @flightdev/image\r\n * \r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * import { FlightImage } from '@flightdev/image/vue';\r\n * </script>\r\n * \r\n * <template>\r\n * <FlightImage\r\n * src=\"/hero.jpg\"\r\n * alt=\"Hero\"\r\n * :width=\"800\"\r\n * :height=\"600\"\r\n * priority\r\n * placeholder=\"blur\"\r\n * />\r\n * </template>\r\n * ```\r\n */\r\n\r\nimport { defineComponent, ref, computed, h, type PropType } from 'vue';\r\nimport type { ImageFormat, ImageFit } from '../index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface FlightImageProps {\r\n src: string;\r\n alt: string;\r\n width?: number;\r\n height?: number;\r\n format?: ImageFormat;\r\n quality?: number;\r\n fit?: ImageFit;\r\n priority?: boolean;\r\n placeholder?: 'blur' | 'empty' | 'none';\r\n blurDataUrl?: string;\r\n sizes?: string;\r\n baseUrl?: string;\r\n}\r\n\r\n// ============================================================================\r\n// URL Builder\r\n// ============================================================================\r\n\r\nfunction buildOptimizedUrl(\r\n src: string,\r\n options: { width?: number; height?: number; format?: ImageFormat; quality?: number; fit?: ImageFit },\r\n baseUrl: string = ''\r\n): string {\r\n const params = new URLSearchParams();\r\n\r\n if (options.width) params.set('w', options.width.toString());\r\n if (options.height) params.set('h', options.height.toString());\r\n if (options.format && options.format !== 'auto') params.set('f', options.format);\r\n if (options.quality) params.set('q', options.quality.toString());\r\n if (options.fit) params.set('fit', options.fit);\r\n\r\n const queryString = params.toString();\r\n const normalizedSrc = src.startsWith('/') ? src : `/${src}`;\r\n return `${baseUrl}/_flight/image${normalizedSrc}${queryString ? '?' + queryString : ''}`;\r\n}\r\n\r\n// ============================================================================\r\n// Vue Component\r\n// ============================================================================\r\n\r\nexport const FlightImage = defineComponent({\r\n name: 'FlightImage',\r\n\r\n props: {\r\n src: { type: String, required: true },\r\n alt: { type: String, required: true },\r\n width: { type: Number, default: undefined },\r\n height: { type: Number, default: undefined },\r\n format: { type: String as PropType<ImageFormat>, default: 'auto' },\r\n quality: { type: Number, default: 80 },\r\n fit: { type: String as PropType<ImageFit>, default: 'cover' },\r\n priority: { type: Boolean, default: false },\r\n placeholder: { type: String as PropType<'blur' | 'empty' | 'none'>, default: 'empty' },\r\n blurDataUrl: { type: String, default: undefined },\r\n sizes: { type: String, default: undefined },\r\n baseUrl: { type: String, default: '' },\r\n },\r\n\r\n setup(props) {\r\n const isLoaded = ref(false);\r\n const error = ref(false);\r\n\r\n const optimizedSrc = computed(() =>\r\n buildOptimizedUrl(props.src, {\r\n width: props.width,\r\n height: props.height,\r\n format: props.format,\r\n quality: props.quality,\r\n fit: props.fit,\r\n }, props.baseUrl)\r\n );\r\n\r\n const srcSet = computed(() => {\r\n if (!props.width) return undefined;\r\n\r\n const widths = [props.width, props.width * 1.5, props.width * 2].map(Math.round);\r\n return widths\r\n .map(w => {\r\n const h = props.height ? Math.round(props.height * w / props.width!) : undefined;\r\n return `${buildOptimizedUrl(props.src, { width: w, height: h, format: props.format, quality: props.quality, fit: props.fit }, props.baseUrl)} ${w}w`;\r\n })\r\n .join(', ');\r\n });\r\n\r\n const defaultSizes = computed(() =>\r\n props.width ? `(max-width: ${props.width}px) 100vw, ${props.width}px` : undefined\r\n );\r\n\r\n const handleLoad = () => {\r\n isLoaded.value = true;\r\n };\r\n\r\n const handleError = () => {\r\n error.value = true;\r\n };\r\n\r\n return () => {\r\n const containerStyle = {\r\n position: 'relative' as const,\r\n overflow: 'hidden' as const,\r\n width: props.width ? `${props.width}px` : undefined,\r\n height: props.height ? `${props.height}px` : undefined,\r\n };\r\n\r\n const imageStyle = {\r\n width: '100%',\r\n height: '100%',\r\n objectFit: props.fit,\r\n opacity: isLoaded.value ? 1 : 0,\r\n transition: 'opacity 0.3s ease-in-out',\r\n };\r\n\r\n const blurStyle = {\r\n position: 'absolute' as const,\r\n inset: 0,\r\n backgroundImage: props.blurDataUrl ? `url(${props.blurDataUrl})` : undefined,\r\n backgroundSize: 'cover',\r\n backgroundPosition: 'center',\r\n filter: 'blur(20px)',\r\n transform: 'scale(1.1)',\r\n };\r\n\r\n return h('div', { style: containerStyle }, [\r\n // Blur placeholder\r\n props.placeholder === 'blur' && props.blurDataUrl && !isLoaded.value\r\n ? h('div', { style: blurStyle, 'aria-hidden': true })\r\n : null,\r\n\r\n // Image\r\n h('img', {\r\n src: optimizedSrc.value,\r\n srcset: srcSet.value,\r\n sizes: props.sizes ?? defaultSizes.value,\r\n alt: props.alt,\r\n width: props.width,\r\n height: props.height,\r\n loading: props.priority ? 'eager' : 'lazy',\r\n decoding: props.priority ? 'sync' : 'async',\r\n fetchpriority: props.priority ? 'high' : undefined,\r\n onLoad: handleLoad,\r\n onError: handleError,\r\n style: imageStyle,\r\n }),\r\n ]);\r\n };\r\n },\r\n});\r\n\r\nexport default FlightImage;\r\n"]}
@@ -0,0 +1,210 @@
1
+ export { Buffer } from 'node:buffer';
2
+
3
+ /**
4
+ * @flightdev/image - Agnostic Image Optimization
5
+ *
6
+ * Flight provides the interface, you choose the processor.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createImage } from '@flightdev/image';
11
+ * import { sharp } from '@flightdev/image/sharp';
12
+ *
13
+ * const image = createImage(sharp());
14
+ * const optimized = await image.optimize('./input.jpg', { width: 800, format: 'webp' });
15
+ * ```
16
+ */
17
+ /** Supported image formats */
18
+ type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif' | 'tiff' | 'auto';
19
+ /** Image fit modes for resizing */
20
+ type ImageFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
21
+ /** Image position for cropping */
22
+ type ImagePosition = 'center' | 'top' | 'right' | 'bottom' | 'left' | 'top left' | 'top right' | 'bottom left' | 'bottom right' | 'entropy' | 'attention';
23
+ /** Image optimization options */
24
+ interface ImageOptimizeOptions {
25
+ /** Target width in pixels */
26
+ width?: number;
27
+ /** Target height in pixels */
28
+ height?: number;
29
+ /** Output format ('auto' will choose WebP/AVIF based on browser support) */
30
+ format?: ImageFormat;
31
+ /** Quality (1-100) */
32
+ quality?: number;
33
+ /** Fit mode for resizing */
34
+ fit?: ImageFit;
35
+ /** Position for cropping when using cover/contain */
36
+ position?: ImagePosition;
37
+ /** Generate blur placeholder (returns base64 data URL) */
38
+ blur?: boolean | number;
39
+ /** Prevent upscaling smaller images */
40
+ withoutEnlargement?: boolean;
41
+ /** Preserve metadata (EXIF, etc.) */
42
+ preserveMetadata?: boolean;
43
+ }
44
+ /** Result of image optimization */
45
+ interface ImageOptimizeResult {
46
+ /** Optimized image as Buffer */
47
+ buffer: Buffer;
48
+ /** Detected/output format */
49
+ format: ImageFormat;
50
+ /** Final width */
51
+ width: number;
52
+ /** Final height */
53
+ height: number;
54
+ /** File size in bytes */
55
+ size: number;
56
+ /** Base64 blur placeholder (if blur option was set) */
57
+ blurDataUrl?: string;
58
+ }
59
+ /** Image metadata */
60
+ interface ImageMetadata {
61
+ /** Image width in pixels */
62
+ width: number;
63
+ /** Image height in pixels */
64
+ height: number;
65
+ /** Image format */
66
+ format: string;
67
+ /** File size in bytes */
68
+ size?: number;
69
+ /** Color space */
70
+ space?: string;
71
+ /** Has alpha channel */
72
+ hasAlpha?: boolean;
73
+ /** EXIF data */
74
+ exif?: Record<string, unknown>;
75
+ }
76
+ /** Responsive image sizes configuration */
77
+ interface ResponsiveConfig {
78
+ /** Breakpoint widths to generate */
79
+ widths: number[];
80
+ /** Sizes attribute for HTML */
81
+ sizes?: string;
82
+ /** Base format */
83
+ format?: ImageFormat;
84
+ /** Additional formats to generate */
85
+ formats?: ImageFormat[];
86
+ }
87
+ /** Responsive image result */
88
+ interface ResponsiveResult {
89
+ /** Generated srcset string */
90
+ srcset: string;
91
+ /** Sizes attribute */
92
+ sizes: string;
93
+ /** All generated variants */
94
+ variants: Array<{
95
+ width: number;
96
+ format: ImageFormat;
97
+ url: string;
98
+ buffer: Buffer;
99
+ }>;
100
+ /** Blur placeholder */
101
+ blurDataUrl?: string;
102
+ }
103
+ /**
104
+ * Image Adapter Interface
105
+ *
106
+ * Implement this interface to create a custom image processor.
107
+ * Flight provides Sharp, Squoosh, Cloudinary, and Imgix adapters.
108
+ */
109
+ interface ImageAdapter {
110
+ /** Adapter name for debugging */
111
+ readonly name: string;
112
+ /**
113
+ * Check if the adapter supports a specific format
114
+ */
115
+ supportsFormat(format: ImageFormat): boolean;
116
+ /**
117
+ * Get image metadata without processing
118
+ */
119
+ getMetadata(input: Buffer | string): Promise<ImageMetadata>;
120
+ /**
121
+ * Optimize an image with the given options
122
+ */
123
+ optimize(input: Buffer | string, options: ImageOptimizeOptions): Promise<ImageOptimizeResult>;
124
+ /**
125
+ * Generate a blur placeholder (tiny base64 image)
126
+ */
127
+ generateBlurPlaceholder(input: Buffer | string, size?: number): Promise<string>;
128
+ /**
129
+ * Generate responsive image variants
130
+ */
131
+ generateResponsive(input: Buffer | string, config: ResponsiveConfig): Promise<ResponsiveResult>;
132
+ }
133
+ /** Image adapter factory function type */
134
+ type ImageAdapterFactory<TConfig = unknown> = (config?: TConfig) => ImageAdapter;
135
+ /**
136
+ * Image service options
137
+ */
138
+ interface ImageServiceOptions {
139
+ /** Base URL for generated images (for URL generation) */
140
+ baseUrl?: string;
141
+ /** Default optimization options */
142
+ defaults?: Partial<ImageOptimizeOptions>;
143
+ /** Cache directory for processed images */
144
+ cacheDir?: string;
145
+ }
146
+ /**
147
+ * Image service interface
148
+ */
149
+ interface ImageService {
150
+ /** The underlying adapter */
151
+ readonly adapter: ImageAdapter;
152
+ /** Get metadata for an image */
153
+ getMetadata(input: Buffer | string): Promise<ImageMetadata>;
154
+ /** Optimize an image */
155
+ optimize(input: Buffer | string, options?: ImageOptimizeOptions): Promise<ImageOptimizeResult>;
156
+ /** Generate blur placeholder */
157
+ blur(input: Buffer | string, size?: number): Promise<string>;
158
+ /** Generate responsive variants */
159
+ responsive(input: Buffer | string, config: ResponsiveConfig): Promise<ResponsiveResult>;
160
+ /** Generate a URL for optimized image (for CDN adapters) */
161
+ url(src: string, options?: ImageOptimizeOptions): string;
162
+ }
163
+ /**
164
+ * Create an image service with the given adapter
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * import { createImage } from '@flightdev/image';
169
+ * import { sharp } from '@flightdev/image/sharp';
170
+ *
171
+ * const image = createImage(sharp());
172
+ *
173
+ * // Optimize an image
174
+ * const result = await image.optimize('./hero.jpg', {
175
+ * width: 800,
176
+ * format: 'webp',
177
+ * quality: 80,
178
+ * });
179
+ *
180
+ * // Get blur placeholder
181
+ * const blur = await image.blur('./hero.jpg');
182
+ *
183
+ * // Generate responsive variants
184
+ * const responsive = await image.responsive('./hero.jpg', {
185
+ * widths: [640, 768, 1024, 1280],
186
+ * formats: ['webp', 'avif'],
187
+ * });
188
+ * ```
189
+ */
190
+ declare function createImage(adapter: ImageAdapter, options?: ImageServiceOptions): ImageService;
191
+ /**
192
+ * Determine best format based on Accept header
193
+ */
194
+ declare function getBestFormat(acceptHeader: string | null | undefined): ImageFormat;
195
+ /**
196
+ * Generate sizes attribute from breakpoints
197
+ */
198
+ declare function generateSizes(breakpoints: Array<{
199
+ maxWidth: number;
200
+ size: string;
201
+ }>): string;
202
+ /**
203
+ * Parse dimensions from string (e.g., "800x600", "800", "x600")
204
+ */
205
+ declare function parseDimensions(dimensions: string): {
206
+ width?: number;
207
+ height?: number;
208
+ };
209
+
210
+ export { type ImageAdapter, type ImageAdapterFactory, type ImageFit, type ImageFormat, type ImageMetadata, type ImageOptimizeOptions, type ImageOptimizeResult, type ImagePosition, type ImageService, type ImageServiceOptions, type ResponsiveConfig, type ResponsiveResult, createImage, generateSizes, getBestFormat, parseDimensions };
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ // src/index.ts
2
+ function createImage(adapter, options = {}) {
3
+ const { defaults = {}, baseUrl = "" } = options;
4
+ return {
5
+ adapter,
6
+ async getMetadata(input) {
7
+ return adapter.getMetadata(input);
8
+ },
9
+ async optimize(input, opts = {}) {
10
+ const mergedOptions = { ...defaults, ...opts };
11
+ return adapter.optimize(input, mergedOptions);
12
+ },
13
+ async blur(input, size = 10) {
14
+ return adapter.generateBlurPlaceholder(input, size);
15
+ },
16
+ async responsive(input, config) {
17
+ return adapter.generateResponsive(input, config);
18
+ },
19
+ url(src, opts = {}) {
20
+ const params = new URLSearchParams();
21
+ if (opts.width) params.set("w", opts.width.toString());
22
+ if (opts.height) params.set("h", opts.height.toString());
23
+ if (opts.format && opts.format !== "auto") params.set("f", opts.format);
24
+ if (opts.quality) params.set("q", opts.quality.toString());
25
+ if (opts.fit) params.set("fit", opts.fit);
26
+ const queryString = params.toString();
27
+ return `${baseUrl}${src}${queryString ? "?" + queryString : ""}`;
28
+ }
29
+ };
30
+ }
31
+ function getBestFormat(acceptHeader) {
32
+ if (!acceptHeader) return "webp";
33
+ if (acceptHeader.includes("image/avif")) return "avif";
34
+ if (acceptHeader.includes("image/webp")) return "webp";
35
+ return "jpeg";
36
+ }
37
+ function generateSizes(breakpoints) {
38
+ const parts = breakpoints.map((bp) => `(max-width: ${bp.maxWidth}px) ${bp.size}`);
39
+ parts.push("100vw");
40
+ return parts.join(", ");
41
+ }
42
+ function parseDimensions(dimensions) {
43
+ if (dimensions.includes("x")) {
44
+ const [w, h] = dimensions.split("x");
45
+ return {
46
+ width: w ? parseInt(w, 10) : void 0,
47
+ height: h ? parseInt(h, 10) : void 0
48
+ };
49
+ }
50
+ return { width: parseInt(dimensions, 10) };
51
+ }
52
+
53
+ export { createImage, generateSizes, getBestFormat, parseDimensions };
54
+ //# sourceMappingURL=index.js.map
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * @flightdev/image - Agnostic Image Optimization\r\n * \r\n * Flight provides the interface, you choose the processor.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createImage } from '@flightdev/image';\r\n * import { sharp } from '@flightdev/image/sharp';\r\n * \r\n * const image = createImage(sharp());\r\n * const optimized = await image.optimize('./input.jpg', { width: 800, format: 'webp' });\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/** Supported image formats */\r\nexport type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif' | 'tiff' | 'auto';\r\n\r\n/** Image fit modes for resizing */\r\nexport type ImageFit = 'cover' | 'contain' | 'fill' | 'inside' | 'outside';\r\n\r\n/** Image position for cropping */\r\nexport type ImagePosition =\r\n | 'center' | 'top' | 'right' | 'bottom' | 'left'\r\n | 'top left' | 'top right' | 'bottom left' | 'bottom right'\r\n | 'entropy' | 'attention';\r\n\r\n/** Image optimization options */\r\nexport interface ImageOptimizeOptions {\r\n /** Target width in pixels */\r\n width?: number;\r\n /** Target height in pixels */\r\n height?: number;\r\n /** Output format ('auto' will choose WebP/AVIF based on browser support) */\r\n format?: ImageFormat;\r\n /** Quality (1-100) */\r\n quality?: number;\r\n /** Fit mode for resizing */\r\n fit?: ImageFit;\r\n /** Position for cropping when using cover/contain */\r\n position?: ImagePosition;\r\n /** Generate blur placeholder (returns base64 data URL) */\r\n blur?: boolean | number;\r\n /** Prevent upscaling smaller images */\r\n withoutEnlargement?: boolean;\r\n /** Preserve metadata (EXIF, etc.) */\r\n preserveMetadata?: boolean;\r\n}\r\n\r\n/** Result of image optimization */\r\nexport interface ImageOptimizeResult {\r\n /** Optimized image as Buffer */\r\n buffer: Buffer;\r\n /** Detected/output format */\r\n format: ImageFormat;\r\n /** Final width */\r\n width: number;\r\n /** Final height */\r\n height: number;\r\n /** File size in bytes */\r\n size: number;\r\n /** Base64 blur placeholder (if blur option was set) */\r\n blurDataUrl?: string;\r\n}\r\n\r\n/** Image metadata */\r\nexport interface ImageMetadata {\r\n /** Image width in pixels */\r\n width: number;\r\n /** Image height in pixels */\r\n height: number;\r\n /** Image format */\r\n format: string;\r\n /** File size in bytes */\r\n size?: number;\r\n /** Color space */\r\n space?: string;\r\n /** Has alpha channel */\r\n hasAlpha?: boolean;\r\n /** EXIF data */\r\n exif?: Record<string, unknown>;\r\n}\r\n\r\n/** Responsive image sizes configuration */\r\nexport interface ResponsiveConfig {\r\n /** Breakpoint widths to generate */\r\n widths: number[];\r\n /** Sizes attribute for HTML */\r\n sizes?: string;\r\n /** Base format */\r\n format?: ImageFormat;\r\n /** Additional formats to generate */\r\n formats?: ImageFormat[];\r\n}\r\n\r\n/** Responsive image result */\r\nexport interface ResponsiveResult {\r\n /** Generated srcset string */\r\n srcset: string;\r\n /** Sizes attribute */\r\n sizes: string;\r\n /** All generated variants */\r\n variants: Array<{\r\n width: number;\r\n format: ImageFormat;\r\n url: string;\r\n buffer: Buffer;\r\n }>;\r\n /** Blur placeholder */\r\n blurDataUrl?: string;\r\n}\r\n\r\n// ============================================================================\r\n// Adapter Interface\r\n// ============================================================================\r\n\r\n/**\r\n * Image Adapter Interface\r\n * \r\n * Implement this interface to create a custom image processor.\r\n * Flight provides Sharp, Squoosh, Cloudinary, and Imgix adapters.\r\n */\r\nexport interface ImageAdapter {\r\n /** Adapter name for debugging */\r\n readonly name: string;\r\n\r\n /**\r\n * Check if the adapter supports a specific format\r\n */\r\n supportsFormat(format: ImageFormat): boolean;\r\n\r\n /**\r\n * Get image metadata without processing\r\n */\r\n getMetadata(input: Buffer | string): Promise<ImageMetadata>;\r\n\r\n /**\r\n * Optimize an image with the given options\r\n */\r\n optimize(input: Buffer | string, options: ImageOptimizeOptions): Promise<ImageOptimizeResult>;\r\n\r\n /**\r\n * Generate a blur placeholder (tiny base64 image)\r\n */\r\n generateBlurPlaceholder(input: Buffer | string, size?: number): Promise<string>;\r\n\r\n /**\r\n * Generate responsive image variants\r\n */\r\n generateResponsive(input: Buffer | string, config: ResponsiveConfig): Promise<ResponsiveResult>;\r\n}\r\n\r\n/** Image adapter factory function type */\r\nexport type ImageAdapterFactory<TConfig = unknown> = (config?: TConfig) => ImageAdapter;\r\n\r\n// ============================================================================\r\n// Image Service\r\n// ============================================================================\r\n\r\n/**\r\n * Image service options\r\n */\r\nexport interface ImageServiceOptions {\r\n /** Base URL for generated images (for URL generation) */\r\n baseUrl?: string;\r\n /** Default optimization options */\r\n defaults?: Partial<ImageOptimizeOptions>;\r\n /** Cache directory for processed images */\r\n cacheDir?: string;\r\n}\r\n\r\n/**\r\n * Image service interface\r\n */\r\nexport interface ImageService {\r\n /** The underlying adapter */\r\n readonly adapter: ImageAdapter;\r\n\r\n /** Get metadata for an image */\r\n getMetadata(input: Buffer | string): Promise<ImageMetadata>;\r\n\r\n /** Optimize an image */\r\n optimize(input: Buffer | string, options?: ImageOptimizeOptions): Promise<ImageOptimizeResult>;\r\n\r\n /** Generate blur placeholder */\r\n blur(input: Buffer | string, size?: number): Promise<string>;\r\n\r\n /** Generate responsive variants */\r\n responsive(input: Buffer | string, config: ResponsiveConfig): Promise<ResponsiveResult>;\r\n\r\n /** Generate a URL for optimized image (for CDN adapters) */\r\n url(src: string, options?: ImageOptimizeOptions): string;\r\n}\r\n\r\n/**\r\n * Create an image service with the given adapter\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createImage } from '@flightdev/image';\r\n * import { sharp } from '@flightdev/image/sharp';\r\n * \r\n * const image = createImage(sharp());\r\n * \r\n * // Optimize an image\r\n * const result = await image.optimize('./hero.jpg', {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 80,\r\n * });\r\n * \r\n * // Get blur placeholder\r\n * const blur = await image.blur('./hero.jpg');\r\n * \r\n * // Generate responsive variants\r\n * const responsive = await image.responsive('./hero.jpg', {\r\n * widths: [640, 768, 1024, 1280],\r\n * formats: ['webp', 'avif'],\r\n * });\r\n * ```\r\n */\r\nexport function createImage(adapter: ImageAdapter, options: ImageServiceOptions = {}): ImageService {\r\n const { defaults = {}, baseUrl = '' } = options;\r\n\r\n return {\r\n adapter,\r\n\r\n async getMetadata(input) {\r\n return adapter.getMetadata(input);\r\n },\r\n\r\n async optimize(input, opts = {}) {\r\n const mergedOptions: ImageOptimizeOptions = { ...defaults, ...opts };\r\n return adapter.optimize(input, mergedOptions);\r\n },\r\n\r\n async blur(input, size = 10) {\r\n return adapter.generateBlurPlaceholder(input, size);\r\n },\r\n\r\n async responsive(input, config) {\r\n return adapter.generateResponsive(input, config);\r\n },\r\n\r\n url(src, opts = {}) {\r\n // For local adapters, generate a URL with query params\r\n // CDN adapters will override this with their own URL generation\r\n const params = new URLSearchParams();\r\n\r\n if (opts.width) params.set('w', opts.width.toString());\r\n if (opts.height) params.set('h', opts.height.toString());\r\n if (opts.format && opts.format !== 'auto') params.set('f', opts.format);\r\n if (opts.quality) params.set('q', opts.quality.toString());\r\n if (opts.fit) params.set('fit', opts.fit);\r\n\r\n const queryString = params.toString();\r\n return `${baseUrl}${src}${queryString ? '?' + queryString : ''}`;\r\n },\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Determine best format based on Accept header\r\n */\r\nexport function getBestFormat(acceptHeader: string | null | undefined): ImageFormat {\r\n if (!acceptHeader) return 'webp';\r\n\r\n // AVIF has best compression but slower encoding\r\n if (acceptHeader.includes('image/avif')) return 'avif';\r\n // WebP is widely supported and good compression\r\n if (acceptHeader.includes('image/webp')) return 'webp';\r\n // Fallback to JPEG\r\n return 'jpeg';\r\n}\r\n\r\n/**\r\n * Generate sizes attribute from breakpoints\r\n */\r\nexport function generateSizes(breakpoints: Array<{ maxWidth: number; size: string }>): string {\r\n const parts = breakpoints.map(bp => `(max-width: ${bp.maxWidth}px) ${bp.size}`);\r\n parts.push('100vw'); // Default\r\n return parts.join(', ');\r\n}\r\n\r\n/**\r\n * Parse dimensions from string (e.g., \"800x600\", \"800\", \"x600\")\r\n */\r\nexport function parseDimensions(dimensions: string): { width?: number; height?: number } {\r\n if (dimensions.includes('x')) {\r\n const [w, h] = dimensions.split('x');\r\n return {\r\n width: w ? parseInt(w, 10) : undefined,\r\n height: h ? parseInt(h, 10) : undefined,\r\n };\r\n }\r\n return { width: parseInt(dimensions, 10) };\r\n}\r\n\r\n// Re-export types\r\nexport type { Buffer } from 'node:buffer';\r\n"]}
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "@flightdev/image",
3
+ "version": "0.0.2",
4
+ "description": "Agnostic image optimization for Flight Framework. Choose your processor: Sharp, Squoosh, Cloudinary, or custom.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./sharp": {
12
+ "types": "./dist/adapters/sharp.d.ts",
13
+ "import": "./dist/adapters/sharp.js"
14
+ },
15
+ "./squoosh": {
16
+ "types": "./dist/adapters/squoosh.d.ts",
17
+ "import": "./dist/adapters/squoosh.js"
18
+ },
19
+ "./cloudinary": {
20
+ "types": "./dist/adapters/cloudinary.d.ts",
21
+ "import": "./dist/adapters/cloudinary.js"
22
+ },
23
+ "./imgix": {
24
+ "types": "./dist/adapters/imgix.d.ts",
25
+ "import": "./dist/adapters/imgix.js"
26
+ },
27
+ "./react": {
28
+ "types": "./dist/components/react.d.ts",
29
+ "import": "./dist/components/react.js"
30
+ },
31
+ "./vue": {
32
+ "types": "./dist/components/vue.d.ts",
33
+ "import": "./dist/components/vue.js"
34
+ },
35
+ "./svelte": {
36
+ "types": "./dist/components/svelte.d.ts",
37
+ "import": "./dist/components/svelte.js"
38
+ },
39
+ "./solid": {
40
+ "types": "./dist/components/solid.d.ts",
41
+ "import": "./dist/components/solid.js"
42
+ }
43
+ },
44
+ "files": [
45
+ "dist"
46
+ ],
47
+ "dependencies": {},
48
+ "peerDependencies": {
49
+ "sharp": ">=0.33.0",
50
+ "react": ">=18.0.0",
51
+ "vue": ">=3.0.0",
52
+ "svelte": ">=4.0.0",
53
+ "solid-js": ">=1.0.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "sharp": {
57
+ "optional": true
58
+ },
59
+ "react": {
60
+ "optional": true
61
+ },
62
+ "vue": {
63
+ "optional": true
64
+ },
65
+ "svelte": {
66
+ "optional": true
67
+ },
68
+ "solid-js": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "devDependencies": {
73
+ "@types/node": "^22.0.0",
74
+ "tsup": "^8.0.0",
75
+ "typescript": "^5.7.0",
76
+ "vitest": "^2.0.0"
77
+ },
78
+ "keywords": [
79
+ "flight",
80
+ "image",
81
+ "optimization",
82
+ "sharp",
83
+ "webp",
84
+ "avif",
85
+ "responsive",
86
+ "agnostic"
87
+ ],
88
+ "author": "Flight Contributors",
89
+ "license": "MIT",
90
+ "scripts": {
91
+ "build": "tsup",
92
+ "dev": "tsup --watch",
93
+ "test": "vitest run",
94
+ "test:watch": "vitest",
95
+ "typecheck": "tsc --noEmit"
96
+ }
97
+ }