@cloudwerk/images 0.1.3
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.ts +1209 -0
- package/dist/index.js +823 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @cloudwerk/images - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core types for Cloudflare Images integration.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for an image variant (transformation preset).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const thumbnailVariant: ImageVariant = {
|
|
12
|
+
* width: 100,
|
|
13
|
+
* height: 100,
|
|
14
|
+
* fit: 'cover',
|
|
15
|
+
* quality: 80,
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
interface ImageVariant {
|
|
20
|
+
/** Width in pixels */
|
|
21
|
+
width?: number;
|
|
22
|
+
/** Height in pixels */
|
|
23
|
+
height?: number;
|
|
24
|
+
/**
|
|
25
|
+
* How the image should be resized to fit the dimensions.
|
|
26
|
+
*
|
|
27
|
+
* - 'cover': Fill the dimensions, cropping if necessary
|
|
28
|
+
* - 'contain': Fit within dimensions, may have letterboxing
|
|
29
|
+
* - 'scale-down': Only shrink, never enlarge
|
|
30
|
+
* - 'crop': Crop to exact dimensions from center
|
|
31
|
+
* - 'pad': Fit within dimensions, padding if necessary
|
|
32
|
+
*/
|
|
33
|
+
fit?: 'cover' | 'contain' | 'scale-down' | 'crop' | 'pad';
|
|
34
|
+
/**
|
|
35
|
+
* Blur radius (1-250).
|
|
36
|
+
* Higher values = more blur.
|
|
37
|
+
*/
|
|
38
|
+
blur?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Quality for lossy formats (1-100).
|
|
41
|
+
* @default 85
|
|
42
|
+
*/
|
|
43
|
+
quality?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Output format override.
|
|
46
|
+
* If not specified, format is negotiated based on Accept header.
|
|
47
|
+
*/
|
|
48
|
+
format?: 'webp' | 'avif' | 'json' | 'jpeg' | 'png';
|
|
49
|
+
/**
|
|
50
|
+
* Device pixel ratio (1-3).
|
|
51
|
+
* Multiplies width/height for retina displays.
|
|
52
|
+
*/
|
|
53
|
+
dpr?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Gravity for cropping (where to focus when cropping).
|
|
56
|
+
*/
|
|
57
|
+
gravity?: 'auto' | 'center' | 'top' | 'bottom' | 'left' | 'right' | 'face';
|
|
58
|
+
/**
|
|
59
|
+
* Sharpen the image (0-10).
|
|
60
|
+
*/
|
|
61
|
+
sharpen?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Brightness adjustment (-1 to 1).
|
|
64
|
+
*/
|
|
65
|
+
brightness?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Contrast adjustment (-1 to 1).
|
|
68
|
+
*/
|
|
69
|
+
contrast?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Rotate the image in degrees (0-360).
|
|
72
|
+
*/
|
|
73
|
+
rotate?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Metadata handling.
|
|
76
|
+
*
|
|
77
|
+
* - 'keep': Preserve all metadata
|
|
78
|
+
* - 'copyright': Keep only copyright info
|
|
79
|
+
* - 'none': Strip all metadata
|
|
80
|
+
*/
|
|
81
|
+
metadata?: 'keep' | 'copyright' | 'none';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Environment variable reference for sensitive configuration.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const config: ImageConfig = {
|
|
89
|
+
* accountId: { env: 'CF_ACCOUNT_ID' },
|
|
90
|
+
* apiToken: { env: 'CF_IMAGES_API_TOKEN' },
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
interface EnvRef {
|
|
95
|
+
/** Name of the environment variable */
|
|
96
|
+
env: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Configuration for defining a Cloudflare Images integration.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // app/images/avatars.ts
|
|
104
|
+
* export default defineImage({
|
|
105
|
+
* name: 'avatars',
|
|
106
|
+
* variants: {
|
|
107
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
108
|
+
* profile: { width: 400, height: 400, fit: 'cover' },
|
|
109
|
+
* },
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
interface ImageConfig {
|
|
114
|
+
/**
|
|
115
|
+
* Unique name for this image configuration.
|
|
116
|
+
* Defaults to the filename (without extension).
|
|
117
|
+
*/
|
|
118
|
+
name?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Cloudflare account ID.
|
|
121
|
+
* Can be a string value or environment variable reference.
|
|
122
|
+
*/
|
|
123
|
+
accountId?: string | EnvRef;
|
|
124
|
+
/**
|
|
125
|
+
* Cloudflare Images API token.
|
|
126
|
+
* Can be a string value or environment variable reference.
|
|
127
|
+
*/
|
|
128
|
+
apiToken?: string | EnvRef;
|
|
129
|
+
/**
|
|
130
|
+
* Predefined image variants (transformation presets).
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* variants: {
|
|
135
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
136
|
+
* large: { width: 1200, height: 800, fit: 'contain' },
|
|
137
|
+
* blur: { blur: 20, width: 20 },
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
variants?: Record<string, ImageVariant>;
|
|
142
|
+
/**
|
|
143
|
+
* Custom domain for image delivery.
|
|
144
|
+
* If not specified, uses the default Cloudflare Images URL.
|
|
145
|
+
*
|
|
146
|
+
* @example 'https://images.example.com'
|
|
147
|
+
*/
|
|
148
|
+
deliveryUrl?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Default variant to use when none is specified.
|
|
151
|
+
*/
|
|
152
|
+
defaultVariant?: string;
|
|
153
|
+
/**
|
|
154
|
+
* Whether to require signed URLs for this image configuration.
|
|
155
|
+
* @default false
|
|
156
|
+
*/
|
|
157
|
+
requireSignedURLs?: boolean;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* An image definition created by `defineImage()`.
|
|
161
|
+
*
|
|
162
|
+
* This is the return type of the factory function and contains
|
|
163
|
+
* the processed configuration with a brand marker for type safety.
|
|
164
|
+
*/
|
|
165
|
+
interface ImageDefinition {
|
|
166
|
+
/** Internal marker identifying this as an image definition */
|
|
167
|
+
readonly __brand: 'cloudwerk-image';
|
|
168
|
+
/** Image configuration name */
|
|
169
|
+
readonly name: string;
|
|
170
|
+
/** Processed configuration with defaults applied */
|
|
171
|
+
readonly config: ImageConfig;
|
|
172
|
+
/** Variant definitions */
|
|
173
|
+
readonly variants: Record<string, ImageVariant>;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Options for uploading an image.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const result = await images.avatars.upload(file, {
|
|
181
|
+
* id: 'user-123-avatar',
|
|
182
|
+
* metadata: { userId: '123', uploadedAt: new Date().toISOString() },
|
|
183
|
+
* })
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
interface UploadOptions {
|
|
187
|
+
/**
|
|
188
|
+
* Custom metadata to attach to the image.
|
|
189
|
+
* Can be used for organization and filtering.
|
|
190
|
+
*/
|
|
191
|
+
metadata?: Record<string, string>;
|
|
192
|
+
/**
|
|
193
|
+
* Whether to require signed URLs to access this image.
|
|
194
|
+
* Overrides the configuration-level setting.
|
|
195
|
+
*/
|
|
196
|
+
requireSignedURLs?: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Custom image ID.
|
|
199
|
+
* If not specified, Cloudflare generates a UUID.
|
|
200
|
+
*/
|
|
201
|
+
id?: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Options for generating a direct upload URL.
|
|
205
|
+
*
|
|
206
|
+
* Direct upload URLs allow clients to upload images directly to Cloudflare
|
|
207
|
+
* without going through your server, reducing bandwidth costs.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const { uploadUrl, id } = await images.avatars.getDirectUploadUrl({
|
|
212
|
+
* expiry: '1h',
|
|
213
|
+
* metadata: { userId: '123' },
|
|
214
|
+
* })
|
|
215
|
+
*
|
|
216
|
+
* // Return uploadUrl to client for direct upload
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
interface DirectUploadOptions {
|
|
220
|
+
/**
|
|
221
|
+
* Custom metadata to attach to the uploaded image.
|
|
222
|
+
*/
|
|
223
|
+
metadata?: Record<string, string>;
|
|
224
|
+
/**
|
|
225
|
+
* Whether to require signed URLs to access the uploaded image.
|
|
226
|
+
*/
|
|
227
|
+
requireSignedURLs?: boolean;
|
|
228
|
+
/**
|
|
229
|
+
* How long the upload URL is valid.
|
|
230
|
+
* Can be a duration string ('1h', '30m') or seconds.
|
|
231
|
+
* @default '30m'
|
|
232
|
+
*/
|
|
233
|
+
expiry?: string | number;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Options for listing images.
|
|
237
|
+
*/
|
|
238
|
+
interface ListOptions {
|
|
239
|
+
/** Page number (1-based) */
|
|
240
|
+
page?: number;
|
|
241
|
+
/** Number of images per page (1-100) */
|
|
242
|
+
perPage?: number;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Result of an image upload operation.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* const result = await images.avatars.upload(file)
|
|
250
|
+
* console.log(result.id) // 'abc123'
|
|
251
|
+
* console.log(result.variants) // ['thumbnail', 'profile']
|
|
252
|
+
* console.log(result.uploaded) // Date object
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
interface ImageResult {
|
|
256
|
+
/** Unique image ID */
|
|
257
|
+
id: string;
|
|
258
|
+
/** Original filename (if provided during upload) */
|
|
259
|
+
filename?: string;
|
|
260
|
+
/** When the image was uploaded */
|
|
261
|
+
uploaded: Date;
|
|
262
|
+
/** Available variant names for this image */
|
|
263
|
+
variants: string[];
|
|
264
|
+
/** Custom metadata attached to the image */
|
|
265
|
+
metadata?: Record<string, string>;
|
|
266
|
+
/** Whether the image requires signed URLs */
|
|
267
|
+
requireSignedURLs?: boolean;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Result of generating a direct upload URL.
|
|
271
|
+
*/
|
|
272
|
+
interface DirectUploadResult {
|
|
273
|
+
/** One-time upload URL for direct client upload */
|
|
274
|
+
uploadUrl: string;
|
|
275
|
+
/** Image ID that will be assigned to the uploaded image */
|
|
276
|
+
id: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Image client interface for interacting with Cloudflare Images.
|
|
280
|
+
*
|
|
281
|
+
* @typeParam T - Variant names type for this image configuration
|
|
282
|
+
*/
|
|
283
|
+
interface ImageClientInterface<T extends string = string> {
|
|
284
|
+
/**
|
|
285
|
+
* Upload an image file.
|
|
286
|
+
*
|
|
287
|
+
* @param file - File, Blob, or ReadableStream to upload
|
|
288
|
+
* @param options - Upload options
|
|
289
|
+
* @returns Upload result with image ID and metadata
|
|
290
|
+
*/
|
|
291
|
+
upload(file: File | Blob | ReadableStream, options?: UploadOptions): Promise<ImageResult>;
|
|
292
|
+
/**
|
|
293
|
+
* Generate a direct upload URL for client-side uploads.
|
|
294
|
+
*
|
|
295
|
+
* @param options - Direct upload options
|
|
296
|
+
* @returns Upload URL and image ID
|
|
297
|
+
*/
|
|
298
|
+
getDirectUploadUrl(options?: DirectUploadOptions): Promise<DirectUploadResult>;
|
|
299
|
+
/**
|
|
300
|
+
* Delete an image.
|
|
301
|
+
*
|
|
302
|
+
* @param imageId - ID of the image to delete
|
|
303
|
+
*/
|
|
304
|
+
delete(imageId: string): Promise<void>;
|
|
305
|
+
/**
|
|
306
|
+
* Get image details.
|
|
307
|
+
*
|
|
308
|
+
* @param imageId - ID of the image
|
|
309
|
+
* @returns Image result or null if not found
|
|
310
|
+
*/
|
|
311
|
+
get(imageId: string): Promise<ImageResult | null>;
|
|
312
|
+
/**
|
|
313
|
+
* List images with pagination.
|
|
314
|
+
*
|
|
315
|
+
* @param options - Pagination options
|
|
316
|
+
* @returns Array of image results
|
|
317
|
+
*/
|
|
318
|
+
list(options?: ListOptions): Promise<ImageResult[]>;
|
|
319
|
+
/**
|
|
320
|
+
* Generate URL for an image with optional variant.
|
|
321
|
+
*
|
|
322
|
+
* @param imageId - ID of the image
|
|
323
|
+
* @param variant - Variant name (optional)
|
|
324
|
+
* @returns Full URL to the image
|
|
325
|
+
*/
|
|
326
|
+
url(imageId: string, variant?: T): string;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A scanned image file from the app/images/ directory.
|
|
330
|
+
*/
|
|
331
|
+
interface ScannedImage {
|
|
332
|
+
/** Relative path from app/images/ (e.g., 'avatars.ts') */
|
|
333
|
+
relativePath: string;
|
|
334
|
+
/** Absolute filesystem path */
|
|
335
|
+
absolutePath: string;
|
|
336
|
+
/** File name without extension (e.g., 'avatars') */
|
|
337
|
+
name: string;
|
|
338
|
+
/** File extension (e.g., '.ts') */
|
|
339
|
+
extension: string;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Result of scanning the app/images/ directory.
|
|
343
|
+
*/
|
|
344
|
+
interface ImageScanResult {
|
|
345
|
+
/** All discovered image files */
|
|
346
|
+
images: ScannedImage[];
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* A compiled image entry in the manifest.
|
|
350
|
+
*/
|
|
351
|
+
interface ImageEntry {
|
|
352
|
+
/** Image name derived from filename (e.g., 'avatars') */
|
|
353
|
+
name: string;
|
|
354
|
+
/** Binding name for wrangler.toml (e.g., 'AVATARS_IMAGES') */
|
|
355
|
+
bindingName: string;
|
|
356
|
+
/** Relative path to the image definition file */
|
|
357
|
+
filePath: string;
|
|
358
|
+
/** Absolute path to the image definition file */
|
|
359
|
+
absolutePath: string;
|
|
360
|
+
/** Variant definitions from the image config */
|
|
361
|
+
variants: Record<string, ImageVariant>;
|
|
362
|
+
/** Full image configuration */
|
|
363
|
+
config: ImageConfig;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Validation error for an image definition.
|
|
367
|
+
*/
|
|
368
|
+
interface ImageValidationError {
|
|
369
|
+
/** Image file path */
|
|
370
|
+
file: string;
|
|
371
|
+
/** Error message */
|
|
372
|
+
message: string;
|
|
373
|
+
/** Error code for programmatic handling */
|
|
374
|
+
code: 'INVALID_CONFIG' | 'DUPLICATE_NAME' | 'INVALID_NAME' | 'MISSING_REQUIRED';
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Validation warning for an image definition.
|
|
378
|
+
*/
|
|
379
|
+
interface ImageValidationWarning {
|
|
380
|
+
/** Image file path */
|
|
381
|
+
file: string;
|
|
382
|
+
/** Warning message */
|
|
383
|
+
message: string;
|
|
384
|
+
/** Warning code */
|
|
385
|
+
code: 'NO_VARIANTS' | 'MISSING_ACCOUNT_ID' | 'MISSING_API_TOKEN';
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Complete image manifest generated during build.
|
|
389
|
+
*/
|
|
390
|
+
interface ImageManifest {
|
|
391
|
+
/** All compiled image entries */
|
|
392
|
+
images: ImageEntry[];
|
|
393
|
+
/** Validation errors (image won't be registered) */
|
|
394
|
+
errors: ImageValidationError[];
|
|
395
|
+
/** Validation warnings (image will be registered with warning) */
|
|
396
|
+
warnings: ImageValidationWarning[];
|
|
397
|
+
/** When the manifest was generated */
|
|
398
|
+
generatedAt: Date;
|
|
399
|
+
/** Root directory of the app */
|
|
400
|
+
rootDir: string;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @cloudwerk/images - Error Classes
|
|
405
|
+
*
|
|
406
|
+
* Custom error types for Cloudflare Images operations.
|
|
407
|
+
*/
|
|
408
|
+
/**
|
|
409
|
+
* Base error class for all image-related errors.
|
|
410
|
+
*/
|
|
411
|
+
declare class ImageError extends Error {
|
|
412
|
+
constructor(message: string);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Error thrown when image configuration is invalid.
|
|
416
|
+
*/
|
|
417
|
+
declare class ImageConfigError extends ImageError {
|
|
418
|
+
readonly field: string;
|
|
419
|
+
constructor(message: string, field: string);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Error thrown when a required environment variable is missing.
|
|
423
|
+
*/
|
|
424
|
+
declare class ImageEnvError extends ImageError {
|
|
425
|
+
readonly envVar: string;
|
|
426
|
+
constructor(envVar: string);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Error thrown when an image is not found.
|
|
430
|
+
*/
|
|
431
|
+
declare class ImageNotFoundError extends ImageError {
|
|
432
|
+
readonly imageId: string;
|
|
433
|
+
constructor(imageId: string);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Error thrown when an image upload fails.
|
|
437
|
+
*/
|
|
438
|
+
declare class ImageUploadError extends ImageError {
|
|
439
|
+
readonly statusCode?: number;
|
|
440
|
+
readonly details?: string;
|
|
441
|
+
constructor(message: string, statusCode?: number, details?: string);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Error thrown when an image operation fails due to API errors.
|
|
445
|
+
*/
|
|
446
|
+
declare class ImageApiError extends ImageError {
|
|
447
|
+
readonly statusCode: number;
|
|
448
|
+
readonly errors: Array<{
|
|
449
|
+
code: number;
|
|
450
|
+
message: string;
|
|
451
|
+
}>;
|
|
452
|
+
constructor(statusCode: number, errors: Array<{
|
|
453
|
+
code: number;
|
|
454
|
+
message: string;
|
|
455
|
+
}>);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Error thrown when an invalid variant is requested.
|
|
459
|
+
*/
|
|
460
|
+
declare class ImageVariantError extends ImageError {
|
|
461
|
+
readonly variant: string;
|
|
462
|
+
readonly availableVariants: string[];
|
|
463
|
+
constructor(variant: string, availableVariants: string[]);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Error thrown when image binding is accessed outside request context.
|
|
467
|
+
*/
|
|
468
|
+
declare class ImageContextError extends ImageError {
|
|
469
|
+
constructor();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Error thrown when an image configuration is not found.
|
|
473
|
+
*/
|
|
474
|
+
declare class ImageBindingNotFoundError extends ImageError {
|
|
475
|
+
readonly name: string;
|
|
476
|
+
readonly availableImages: string[];
|
|
477
|
+
constructor(name: string, availableImages: string[]);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* @cloudwerk/images - defineImage()
|
|
482
|
+
*
|
|
483
|
+
* Factory function for creating image definitions.
|
|
484
|
+
*/
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Define a Cloudflare Images configuration.
|
|
488
|
+
*
|
|
489
|
+
* This function creates an image definition that will be automatically
|
|
490
|
+
* discovered and registered by Cloudwerk during build.
|
|
491
|
+
*
|
|
492
|
+
* @param config - Image configuration
|
|
493
|
+
* @returns Image definition
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```typescript
|
|
497
|
+
* // app/images/avatars.ts
|
|
498
|
+
* import { defineImage } from '@cloudwerk/images'
|
|
499
|
+
*
|
|
500
|
+
* export default defineImage({
|
|
501
|
+
* variants: {
|
|
502
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
503
|
+
* profile: { width: 400, height: 400, fit: 'cover' },
|
|
504
|
+
* large: { width: 1200, height: 1200, fit: 'contain' },
|
|
505
|
+
* },
|
|
506
|
+
* })
|
|
507
|
+
* ```
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* // app/images/products.ts - with custom delivery domain
|
|
512
|
+
* import { defineImage } from '@cloudwerk/images'
|
|
513
|
+
*
|
|
514
|
+
* export default defineImage({
|
|
515
|
+
* deliveryUrl: 'https://images.mystore.com',
|
|
516
|
+
* variants: {
|
|
517
|
+
* card: { width: 300, height: 300, fit: 'cover' },
|
|
518
|
+
* detail: { width: 800, height: 600, fit: 'contain' },
|
|
519
|
+
* zoom: { width: 1600, height: 1200, fit: 'contain' },
|
|
520
|
+
* },
|
|
521
|
+
* defaultVariant: 'card',
|
|
522
|
+
* })
|
|
523
|
+
* ```
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```typescript
|
|
527
|
+
* // app/images/secure.ts - with signed URLs required
|
|
528
|
+
* import { defineImage } from '@cloudwerk/images'
|
|
529
|
+
*
|
|
530
|
+
* export default defineImage({
|
|
531
|
+
* requireSignedURLs: true,
|
|
532
|
+
* variants: {
|
|
533
|
+
* preview: { width: 200, height: 200, fit: 'cover', blur: 20 },
|
|
534
|
+
* full: { width: 1920, height: 1080, fit: 'contain' },
|
|
535
|
+
* },
|
|
536
|
+
* })
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
declare function defineImage(config?: ImageConfig): ImageDefinition;
|
|
540
|
+
/**
|
|
541
|
+
* Check if a value is an image definition created by defineImage().
|
|
542
|
+
*
|
|
543
|
+
* @param value - Value to check
|
|
544
|
+
* @returns true if value is an ImageDefinition
|
|
545
|
+
*/
|
|
546
|
+
declare function isImageDefinition(value: unknown): value is ImageDefinition;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @cloudwerk/images - Image Client
|
|
550
|
+
*
|
|
551
|
+
* Client for interacting with Cloudflare Images API.
|
|
552
|
+
*/
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Client for interacting with Cloudflare Images.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```typescript
|
|
559
|
+
* const client = new ImageClient('account-id', 'api-token', {
|
|
560
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
561
|
+
* })
|
|
562
|
+
*
|
|
563
|
+
* // Upload an image
|
|
564
|
+
* const result = await client.upload(file)
|
|
565
|
+
*
|
|
566
|
+
* // Get URL with variant
|
|
567
|
+
* const url = client.url(result.id, 'thumbnail')
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
declare class ImageClient<T extends string = string> implements ImageClientInterface<T> {
|
|
571
|
+
private readonly accountId;
|
|
572
|
+
private readonly apiToken;
|
|
573
|
+
private readonly deliveryUrl?;
|
|
574
|
+
private readonly variants;
|
|
575
|
+
private readonly variantNames;
|
|
576
|
+
constructor(accountId: string, apiToken: string, variants?: Record<string, ImageVariant>, deliveryUrl?: string);
|
|
577
|
+
/**
|
|
578
|
+
* Make an authenticated API request.
|
|
579
|
+
*/
|
|
580
|
+
private fetch;
|
|
581
|
+
/**
|
|
582
|
+
* Upload an image file.
|
|
583
|
+
*/
|
|
584
|
+
upload(file: File | Blob | ReadableStream, options?: UploadOptions): Promise<ImageResult>;
|
|
585
|
+
/**
|
|
586
|
+
* Generate a direct upload URL for client-side uploads.
|
|
587
|
+
*/
|
|
588
|
+
getDirectUploadUrl(options?: DirectUploadOptions): Promise<DirectUploadResult>;
|
|
589
|
+
/**
|
|
590
|
+
* Delete an image.
|
|
591
|
+
*/
|
|
592
|
+
delete(imageId: string): Promise<void>;
|
|
593
|
+
/**
|
|
594
|
+
* Get image details.
|
|
595
|
+
*/
|
|
596
|
+
get(imageId: string): Promise<ImageResult | null>;
|
|
597
|
+
/**
|
|
598
|
+
* List images with pagination.
|
|
599
|
+
*/
|
|
600
|
+
list(options?: ListOptions): Promise<ImageResult[]>;
|
|
601
|
+
/**
|
|
602
|
+
* Generate URL for an image with optional variant.
|
|
603
|
+
*/
|
|
604
|
+
url(imageId: string, variant?: T): string;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Create an image client from configuration.
|
|
608
|
+
*
|
|
609
|
+
* @param accountId - Cloudflare account ID
|
|
610
|
+
* @param apiToken - Cloudflare Images API token
|
|
611
|
+
* @param variants - Variant definitions
|
|
612
|
+
* @param deliveryUrl - Custom delivery URL (optional)
|
|
613
|
+
* @returns Image client instance
|
|
614
|
+
*/
|
|
615
|
+
declare function createImageClient<T extends string = string>(accountId: string, apiToken: string, variants?: Record<T, ImageVariant>, deliveryUrl?: string): ImageClient<T>;
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* @cloudwerk/images - Cloudflare API Types
|
|
619
|
+
*
|
|
620
|
+
* Type definitions for Cloudflare Images API responses.
|
|
621
|
+
* Based on Cloudflare API documentation.
|
|
622
|
+
*/
|
|
623
|
+
/**
|
|
624
|
+
* Standard Cloudflare API response wrapper.
|
|
625
|
+
*/
|
|
626
|
+
interface CloudflareApiResponse<T> {
|
|
627
|
+
result: T;
|
|
628
|
+
success: boolean;
|
|
629
|
+
errors: CloudflareApiError[];
|
|
630
|
+
messages: CloudflareApiMessage[];
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Cloudflare API error.
|
|
634
|
+
*/
|
|
635
|
+
interface CloudflareApiError {
|
|
636
|
+
code: number;
|
|
637
|
+
message: string;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Cloudflare API message.
|
|
641
|
+
*/
|
|
642
|
+
interface CloudflareApiMessage {
|
|
643
|
+
code: number;
|
|
644
|
+
message: string;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Cloudflare Image object from API.
|
|
648
|
+
*/
|
|
649
|
+
interface CloudflareImage {
|
|
650
|
+
/** Unique image identifier */
|
|
651
|
+
id: string;
|
|
652
|
+
/** Original filename */
|
|
653
|
+
filename: string;
|
|
654
|
+
/** Upload timestamp (ISO 8601) */
|
|
655
|
+
uploaded: string;
|
|
656
|
+
/** Whether signed URLs are required */
|
|
657
|
+
requireSignedURLs: boolean;
|
|
658
|
+
/** Available variant URLs */
|
|
659
|
+
variants: string[];
|
|
660
|
+
/** Custom metadata */
|
|
661
|
+
meta?: Record<string, string>;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Response from uploading an image.
|
|
665
|
+
*/
|
|
666
|
+
interface CloudflareUploadResponse {
|
|
667
|
+
id: string;
|
|
668
|
+
filename: string;
|
|
669
|
+
uploaded: string;
|
|
670
|
+
requireSignedURLs: boolean;
|
|
671
|
+
variants: string[];
|
|
672
|
+
meta?: Record<string, string>;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Response from creating a direct upload URL.
|
|
676
|
+
*/
|
|
677
|
+
interface CloudflareDirectUploadResponse {
|
|
678
|
+
id: string;
|
|
679
|
+
uploadURL: string;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Response from listing images.
|
|
683
|
+
*/
|
|
684
|
+
interface CloudflareListResponse {
|
|
685
|
+
images: CloudflareImage[];
|
|
686
|
+
continuation_token?: string;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Response from getting image details.
|
|
690
|
+
*/
|
|
691
|
+
interface CloudflareImageDetailsResponse {
|
|
692
|
+
id: string;
|
|
693
|
+
filename: string;
|
|
694
|
+
uploaded: string;
|
|
695
|
+
requireSignedURLs: boolean;
|
|
696
|
+
variants: string[];
|
|
697
|
+
meta?: Record<string, string>;
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Request body for creating a direct upload URL.
|
|
701
|
+
*/
|
|
702
|
+
interface DirectUploadRequest {
|
|
703
|
+
/** Whether to require signed URLs */
|
|
704
|
+
requireSignedURLs?: boolean;
|
|
705
|
+
/** Custom metadata */
|
|
706
|
+
metadata?: Record<string, string>;
|
|
707
|
+
/** Expiry timestamp (ISO 8601 or Unix timestamp) */
|
|
708
|
+
expiry?: string;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Cloudflare Images variant configuration (API format).
|
|
712
|
+
*/
|
|
713
|
+
interface CloudflareVariant {
|
|
714
|
+
id: string;
|
|
715
|
+
options: {
|
|
716
|
+
fit?: 'cover' | 'contain' | 'scale-down' | 'crop' | 'pad';
|
|
717
|
+
width?: number;
|
|
718
|
+
height?: number;
|
|
719
|
+
metadata?: 'keep' | 'copyright' | 'none';
|
|
720
|
+
};
|
|
721
|
+
neverRequireSignedURLs?: boolean;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Cloudflare Images usage statistics.
|
|
725
|
+
*/
|
|
726
|
+
interface CloudflareImagesStats {
|
|
727
|
+
count: {
|
|
728
|
+
current: number;
|
|
729
|
+
allowed: number;
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Cloudflare Images API endpoints.
|
|
734
|
+
*/
|
|
735
|
+
declare const CLOUDFLARE_IMAGES_API: {
|
|
736
|
+
/** Base URL for Cloudflare API v4 */
|
|
737
|
+
readonly BASE: "https://api.cloudflare.com/client/v4";
|
|
738
|
+
/**
|
|
739
|
+
* Get the images endpoint for an account.
|
|
740
|
+
*/
|
|
741
|
+
readonly images: (accountId: string) => string;
|
|
742
|
+
/**
|
|
743
|
+
* Get the direct upload endpoint.
|
|
744
|
+
*/
|
|
745
|
+
readonly directUpload: (accountId: string) => string;
|
|
746
|
+
/**
|
|
747
|
+
* Get a specific image endpoint.
|
|
748
|
+
*/
|
|
749
|
+
readonly image: (accountId: string, imageId: string) => string;
|
|
750
|
+
/**
|
|
751
|
+
* Get the default delivery URL format.
|
|
752
|
+
*/
|
|
753
|
+
readonly deliveryUrl: (accountId: string, imageId: string, variant: string) => string;
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* @cloudwerk/images - Image Transformer
|
|
758
|
+
*
|
|
759
|
+
* Route handler helper for Cloudflare's Image Resizing service.
|
|
760
|
+
* Creates a handler that transforms images via `fetch(url, { cf: { image: {...} } })`.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```typescript
|
|
764
|
+
* // app/cdn/images/[...path]/route.ts
|
|
765
|
+
* import { createImageTransformer } from '@cloudwerk/images'
|
|
766
|
+
*
|
|
767
|
+
* export const GET = createImageTransformer({
|
|
768
|
+
* allowedOrigins: ['https://images.mysite.com'],
|
|
769
|
+
* presets: {
|
|
770
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
771
|
+
* hero: { width: 1920, height: 1080, fit: 'cover' },
|
|
772
|
+
* },
|
|
773
|
+
* })
|
|
774
|
+
* ```
|
|
775
|
+
*/
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Configuration for the image transformer route handler.
|
|
779
|
+
*/
|
|
780
|
+
interface TransformerConfig {
|
|
781
|
+
/**
|
|
782
|
+
* Allowed origin URLs for image sources.
|
|
783
|
+
* If not specified, any origin is allowed (not recommended for production).
|
|
784
|
+
*
|
|
785
|
+
* @example ['https://images.mysite.com', 'https://cdn.example.com']
|
|
786
|
+
*/
|
|
787
|
+
allowedOrigins?: string[];
|
|
788
|
+
/**
|
|
789
|
+
* Named transformation presets.
|
|
790
|
+
* Can be applied via URL parameter (e.g., ?preset=thumbnail).
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* presets: {
|
|
795
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
796
|
+
* hero: { width: 1920, height: 1080, fit: 'cover' },
|
|
797
|
+
* }
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
presets?: Record<string, TransformPreset>;
|
|
801
|
+
/**
|
|
802
|
+
* Default transformation options applied to all requests.
|
|
803
|
+
*/
|
|
804
|
+
defaults?: TransformPreset;
|
|
805
|
+
/**
|
|
806
|
+
* Maximum allowed width. Requests exceeding this will be clamped.
|
|
807
|
+
* @default 4096
|
|
808
|
+
*/
|
|
809
|
+
maxWidth?: number;
|
|
810
|
+
/**
|
|
811
|
+
* Maximum allowed height. Requests exceeding this will be clamped.
|
|
812
|
+
* @default 4096
|
|
813
|
+
*/
|
|
814
|
+
maxHeight?: number;
|
|
815
|
+
/**
|
|
816
|
+
* Cache control header for transformed images.
|
|
817
|
+
* @default 'public, max-age=31536000' (1 year)
|
|
818
|
+
*/
|
|
819
|
+
cacheControl?: string;
|
|
820
|
+
/**
|
|
821
|
+
* Whether to allow arbitrary width/height from query parameters.
|
|
822
|
+
* If false, only preset transformations are allowed.
|
|
823
|
+
* @default true
|
|
824
|
+
*/
|
|
825
|
+
allowArbitrary?: boolean;
|
|
826
|
+
/**
|
|
827
|
+
* Custom validation function for source URLs.
|
|
828
|
+
* Return true to allow the URL, false to reject.
|
|
829
|
+
*/
|
|
830
|
+
validateSource?: (url: URL) => boolean | Promise<boolean>;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Transformation preset options.
|
|
834
|
+
* Subset of Cloudflare Image Resizing options.
|
|
835
|
+
*/
|
|
836
|
+
interface TransformPreset {
|
|
837
|
+
/** Width in pixels */
|
|
838
|
+
width?: number;
|
|
839
|
+
/** Height in pixels */
|
|
840
|
+
height?: number;
|
|
841
|
+
/** Fit mode */
|
|
842
|
+
fit?: 'cover' | 'contain' | 'scale-down' | 'crop' | 'pad';
|
|
843
|
+
/** Output format */
|
|
844
|
+
format?: 'webp' | 'avif' | 'json' | 'jpeg' | 'png' | 'auto';
|
|
845
|
+
/** Quality (1-100) */
|
|
846
|
+
quality?: number;
|
|
847
|
+
/** Device pixel ratio (1-3) */
|
|
848
|
+
dpr?: number;
|
|
849
|
+
/** Gravity for cropping */
|
|
850
|
+
gravity?: 'auto' | 'center' | 'top' | 'bottom' | 'left' | 'right' | 'face';
|
|
851
|
+
/** Sharpen (0-10) */
|
|
852
|
+
sharpen?: number;
|
|
853
|
+
/** Blur (1-250) */
|
|
854
|
+
blur?: number;
|
|
855
|
+
/** Brightness (-1 to 1) */
|
|
856
|
+
brightness?: number;
|
|
857
|
+
/** Contrast (-1 to 1) */
|
|
858
|
+
contrast?: number;
|
|
859
|
+
/** Rotation (0, 90, 180, 270) */
|
|
860
|
+
rotate?: 0 | 90 | 180 | 270;
|
|
861
|
+
/** Metadata handling */
|
|
862
|
+
metadata?: 'keep' | 'copyright' | 'none';
|
|
863
|
+
/** Background color for padding */
|
|
864
|
+
background?: string;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Error thrown when image transformation fails.
|
|
868
|
+
*/
|
|
869
|
+
declare class ImageTransformError extends Error {
|
|
870
|
+
readonly status: number;
|
|
871
|
+
readonly code: string;
|
|
872
|
+
constructor(message: string, status: number, code: string);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Create an image transformer route handler.
|
|
876
|
+
*
|
|
877
|
+
* This creates a GET handler that:
|
|
878
|
+
* 1. Extracts the source image URL from the request path
|
|
879
|
+
* 2. Parses transformation options from query parameters
|
|
880
|
+
* 3. Fetches the source image with Cloudflare Image Resizing options
|
|
881
|
+
* 4. Returns the transformed image
|
|
882
|
+
*
|
|
883
|
+
* @param config - Transformer configuration
|
|
884
|
+
* @returns Route handler function
|
|
885
|
+
*
|
|
886
|
+
* @example
|
|
887
|
+
* ```typescript
|
|
888
|
+
* // app/cdn/images/[...path]/route.ts
|
|
889
|
+
* import { createImageTransformer } from '@cloudwerk/images'
|
|
890
|
+
*
|
|
891
|
+
* export const GET = createImageTransformer({
|
|
892
|
+
* allowedOrigins: ['https://images.mysite.com'],
|
|
893
|
+
* presets: {
|
|
894
|
+
* thumbnail: { width: 100, height: 100, fit: 'cover' },
|
|
895
|
+
* hero: { width: 1920, height: 1080, fit: 'cover' },
|
|
896
|
+
* },
|
|
897
|
+
* defaults: {
|
|
898
|
+
* format: 'auto',
|
|
899
|
+
* quality: 85,
|
|
900
|
+
* },
|
|
901
|
+
* })
|
|
902
|
+
*
|
|
903
|
+
* // Usage:
|
|
904
|
+
* // /cdn/images/https://images.mysite.com/photo.jpg?preset=thumbnail
|
|
905
|
+
* // /cdn/images/https://images.mysite.com/photo.jpg?w=800&h=600&fit=cover
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
declare function createImageTransformer(config?: TransformerConfig): (request: Request, context: {
|
|
909
|
+
params: Record<string, string | string[]>;
|
|
910
|
+
}) => Promise<Response>;
|
|
911
|
+
/**
|
|
912
|
+
* Convert an ImageVariant to a TransformPreset.
|
|
913
|
+
* Useful when you want to reuse variants defined for Hosted Images.
|
|
914
|
+
*/
|
|
915
|
+
declare function variantToPreset(variant: ImageVariant): TransformPreset;
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* @cloudwerk/images - Cloudflare IMAGES Binding Types
|
|
919
|
+
*
|
|
920
|
+
* Type definitions for the Cloudflare IMAGES binding used in Workers.
|
|
921
|
+
* This binding provides on-the-fly image transformation without uploading
|
|
922
|
+
* to Cloudflare Images.
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* ```typescript
|
|
926
|
+
* // wrangler.toml
|
|
927
|
+
* [images]
|
|
928
|
+
* binding = "MY_IMAGES"
|
|
929
|
+
* ```
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* ```typescript
|
|
933
|
+
* import { getBinding } from '@cloudwerk/core/bindings'
|
|
934
|
+
* import type { CloudflareImagesBinding } from '@cloudwerk/images'
|
|
935
|
+
*
|
|
936
|
+
* const IMAGES = getBinding<CloudflareImagesBinding>('MY_IMAGES')
|
|
937
|
+
*
|
|
938
|
+
* // Transform an image
|
|
939
|
+
* const result = await IMAGES
|
|
940
|
+
* .input(imageStream)
|
|
941
|
+
* .transform({ width: 800 })
|
|
942
|
+
* .output({ format: 'image/webp' })
|
|
943
|
+
* .response()
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
/**
|
|
947
|
+
* Image information returned by the IMAGES binding.
|
|
948
|
+
*/
|
|
949
|
+
interface CloudflareImageInfo {
|
|
950
|
+
/** Width of the image in pixels */
|
|
951
|
+
width: number;
|
|
952
|
+
/** Height of the image in pixels */
|
|
953
|
+
height: number;
|
|
954
|
+
/** Format of the image (e.g., 'image/jpeg', 'image/png') */
|
|
955
|
+
format: string;
|
|
956
|
+
/** File size in bytes */
|
|
957
|
+
fileSize: number;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Transform options for the IMAGES binding.
|
|
961
|
+
*/
|
|
962
|
+
interface CloudflareImageTransformOptions {
|
|
963
|
+
/** Width in pixels */
|
|
964
|
+
width?: number;
|
|
965
|
+
/** Height in pixels */
|
|
966
|
+
height?: number;
|
|
967
|
+
/** Fit mode for resizing */
|
|
968
|
+
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
|
969
|
+
/** Gravity for cropping */
|
|
970
|
+
gravity?: 'auto' | 'left' | 'right' | 'top' | 'bottom' | 'center' | 'face';
|
|
971
|
+
/** Quality for lossy formats (1-100) */
|
|
972
|
+
quality?: number;
|
|
973
|
+
/** Device pixel ratio (1-3) */
|
|
974
|
+
dpr?: number;
|
|
975
|
+
/** Rotation in degrees (0, 90, 180, 270) */
|
|
976
|
+
rotate?: 0 | 90 | 180 | 270;
|
|
977
|
+
/** Sharpen amount (0-10) */
|
|
978
|
+
sharpen?: number;
|
|
979
|
+
/** Blur radius (1-250) */
|
|
980
|
+
blur?: number;
|
|
981
|
+
/** Brightness adjustment (-1 to 1) */
|
|
982
|
+
brightness?: number;
|
|
983
|
+
/** Contrast adjustment (-1 to 1) */
|
|
984
|
+
contrast?: number;
|
|
985
|
+
/** Background color for padding (hex or rgb) */
|
|
986
|
+
background?: string;
|
|
987
|
+
/** Border configuration */
|
|
988
|
+
border?: {
|
|
989
|
+
color: string;
|
|
990
|
+
width: number;
|
|
991
|
+
};
|
|
992
|
+
/** Trim whitespace from edges */
|
|
993
|
+
trim?: {
|
|
994
|
+
top?: number;
|
|
995
|
+
right?: number;
|
|
996
|
+
bottom?: number;
|
|
997
|
+
left?: number;
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Output options for the IMAGES binding.
|
|
1002
|
+
*/
|
|
1003
|
+
interface CloudflareImageOutputOptions {
|
|
1004
|
+
/** Output format */
|
|
1005
|
+
format?: 'image/jpeg' | 'image/png' | 'image/webp' | 'image/avif' | 'image/gif';
|
|
1006
|
+
/** Quality for lossy formats (1-100) */
|
|
1007
|
+
quality?: number;
|
|
1008
|
+
/** Metadata handling */
|
|
1009
|
+
metadata?: 'keep' | 'copyright' | 'none';
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Result object returned after awaiting the transform pipeline.
|
|
1013
|
+
* Use `.response()` to get the transformed image.
|
|
1014
|
+
*/
|
|
1015
|
+
interface CloudflareImagesTransformResult {
|
|
1016
|
+
/**
|
|
1017
|
+
* Get the transformed image as a Response.
|
|
1018
|
+
* @returns Response containing the transformed image
|
|
1019
|
+
*/
|
|
1020
|
+
response(): Response;
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Transform builder interface for the IMAGES binding.
|
|
1024
|
+
* Provides a fluent API for chaining transformations.
|
|
1025
|
+
*
|
|
1026
|
+
* @example
|
|
1027
|
+
* ```typescript
|
|
1028
|
+
* // The entire chain must be awaited, then call .response()
|
|
1029
|
+
* const result = await IMAGES
|
|
1030
|
+
* .input(stream)
|
|
1031
|
+
* .transform({ rotate: 90 })
|
|
1032
|
+
* .output({ format: 'image/webp' })
|
|
1033
|
+
*
|
|
1034
|
+
* return result.response()
|
|
1035
|
+
* ```
|
|
1036
|
+
*/
|
|
1037
|
+
interface CloudflareImagesTransformBuilder {
|
|
1038
|
+
/**
|
|
1039
|
+
* Apply transformation options to the image.
|
|
1040
|
+
* Can be chained multiple times.
|
|
1041
|
+
* @param options - Transformation options
|
|
1042
|
+
* @returns The builder for chaining
|
|
1043
|
+
*/
|
|
1044
|
+
transform(options: CloudflareImageTransformOptions): CloudflareImagesTransformBuilder;
|
|
1045
|
+
/**
|
|
1046
|
+
* Set output format and options.
|
|
1047
|
+
* @param options - Output options
|
|
1048
|
+
* @returns Promise that resolves to a result object with .response() method
|
|
1049
|
+
*/
|
|
1050
|
+
output(options: CloudflareImageOutputOptions): Promise<CloudflareImagesTransformResult>;
|
|
1051
|
+
/**
|
|
1052
|
+
* Draw another image over this one (watermarking/compositing).
|
|
1053
|
+
* @param image - Another transform builder or ReadableStream
|
|
1054
|
+
* @param position - Position options (top, left, bottom, right, opacity, repeat)
|
|
1055
|
+
* @returns The builder for chaining
|
|
1056
|
+
*/
|
|
1057
|
+
draw(image: CloudflareImagesTransformBuilder | ReadableStream<Uint8Array>, position?: {
|
|
1058
|
+
top?: number;
|
|
1059
|
+
left?: number;
|
|
1060
|
+
bottom?: number;
|
|
1061
|
+
right?: number;
|
|
1062
|
+
opacity?: number;
|
|
1063
|
+
repeat?: boolean | 'x' | 'y';
|
|
1064
|
+
}): CloudflareImagesTransformBuilder;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Cloudflare IMAGES binding interface.
|
|
1068
|
+
*
|
|
1069
|
+
* This binding provides access to Cloudflare's image transformation pipeline
|
|
1070
|
+
* for on-the-fly image processing without uploading to Cloudflare Images.
|
|
1071
|
+
*
|
|
1072
|
+
* @example
|
|
1073
|
+
* ```typescript
|
|
1074
|
+
* import { getBinding } from '@cloudwerk/core/bindings'
|
|
1075
|
+
* import type { CloudflareImagesBinding } from '@cloudwerk/images'
|
|
1076
|
+
*
|
|
1077
|
+
* export async function GET(request: Request) {
|
|
1078
|
+
* const IMAGES = getBinding<CloudflareImagesBinding>('MY_IMAGES')
|
|
1079
|
+
*
|
|
1080
|
+
* // Transform an image from a stream
|
|
1081
|
+
* // Note: await the chain, then call .response()
|
|
1082
|
+
* const result = await IMAGES
|
|
1083
|
+
* .input(imageStream)
|
|
1084
|
+
* .transform({ width: 800, rotate: 90 })
|
|
1085
|
+
* .output({ format: 'image/webp' })
|
|
1086
|
+
*
|
|
1087
|
+
* return result.response()
|
|
1088
|
+
* }
|
|
1089
|
+
* ```
|
|
1090
|
+
*
|
|
1091
|
+
* @example
|
|
1092
|
+
* ```typescript
|
|
1093
|
+
* // Get image information
|
|
1094
|
+
* const info = await IMAGES.info(imageStream)
|
|
1095
|
+
* console.log(`Image size: ${info.width}x${info.height}`)
|
|
1096
|
+
* ```
|
|
1097
|
+
*/
|
|
1098
|
+
interface CloudflareImagesBinding {
|
|
1099
|
+
/**
|
|
1100
|
+
* Start a transformation pipeline with an input image.
|
|
1101
|
+
* @param source - Image source as Blob, ArrayBuffer, or ReadableStream
|
|
1102
|
+
* @returns Transform builder for chaining operations
|
|
1103
|
+
*/
|
|
1104
|
+
input(source: Blob | ArrayBuffer | ReadableStream<Uint8Array>): CloudflareImagesTransformBuilder;
|
|
1105
|
+
/**
|
|
1106
|
+
* Get information about an image without transforming it.
|
|
1107
|
+
* @param source - Image source as Blob, ArrayBuffer, or ReadableStream
|
|
1108
|
+
* @returns Image information including dimensions and format
|
|
1109
|
+
*/
|
|
1110
|
+
info(source: Blob | ArrayBuffer | ReadableStream<Uint8Array>): Promise<CloudflareImageInfo>;
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Fit modes for image resizing.
|
|
1114
|
+
*
|
|
1115
|
+
* - `scale-down`: Shrink to fit within dimensions, never enlarge
|
|
1116
|
+
* - `contain`: Fit within dimensions, may add letterboxing
|
|
1117
|
+
* - `cover`: Fill dimensions, cropping if necessary
|
|
1118
|
+
* - `crop`: Crop to exact dimensions from center
|
|
1119
|
+
* - `pad`: Fit within dimensions, padding if necessary
|
|
1120
|
+
*/
|
|
1121
|
+
type ImageFitMode = 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
|
1122
|
+
/**
|
|
1123
|
+
* Gravity options for cropping.
|
|
1124
|
+
*
|
|
1125
|
+
* - `auto`: Automatic based on image content
|
|
1126
|
+
* - `left`, `right`, `top`, `bottom`: Align to edge
|
|
1127
|
+
* - `center`: Center crop
|
|
1128
|
+
* - `face`: Focus on detected faces
|
|
1129
|
+
*/
|
|
1130
|
+
type ImageGravity = 'auto' | 'left' | 'right' | 'top' | 'bottom' | 'center' | 'face';
|
|
1131
|
+
/**
|
|
1132
|
+
* Supported output formats.
|
|
1133
|
+
*/
|
|
1134
|
+
type ImageOutputFormat = 'image/jpeg' | 'image/png' | 'image/webp' | 'image/avif' | 'image/gif';
|
|
1135
|
+
/**
|
|
1136
|
+
* Metadata handling options.
|
|
1137
|
+
*
|
|
1138
|
+
* - `keep`: Preserve all metadata
|
|
1139
|
+
* - `copyright`: Keep only copyright information
|
|
1140
|
+
* - `none`: Strip all metadata
|
|
1141
|
+
*/
|
|
1142
|
+
type ImageMetadataHandling = 'keep' | 'copyright' | 'none';
|
|
1143
|
+
/**
|
|
1144
|
+
* Preset transform configurations for common use cases.
|
|
1145
|
+
*/
|
|
1146
|
+
interface ImageTransformPreset {
|
|
1147
|
+
/** Width in pixels */
|
|
1148
|
+
width?: number;
|
|
1149
|
+
/** Height in pixels */
|
|
1150
|
+
height?: number;
|
|
1151
|
+
/** Fit mode */
|
|
1152
|
+
fit?: ImageFitMode;
|
|
1153
|
+
/** Quality (1-100) */
|
|
1154
|
+
quality?: number;
|
|
1155
|
+
/** Output format */
|
|
1156
|
+
format?: ImageOutputFormat;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Common presets for image transformations.
|
|
1160
|
+
*/
|
|
1161
|
+
declare const IMAGE_PRESETS: {
|
|
1162
|
+
/** Small thumbnail (100x100 cover) */
|
|
1163
|
+
readonly thumbnail: {
|
|
1164
|
+
readonly width: 100;
|
|
1165
|
+
readonly height: 100;
|
|
1166
|
+
readonly fit: "cover";
|
|
1167
|
+
readonly quality: 80;
|
|
1168
|
+
};
|
|
1169
|
+
/** Medium preview (400x400 contain) */
|
|
1170
|
+
readonly preview: {
|
|
1171
|
+
readonly width: 400;
|
|
1172
|
+
readonly height: 400;
|
|
1173
|
+
readonly fit: "contain";
|
|
1174
|
+
readonly quality: 85;
|
|
1175
|
+
};
|
|
1176
|
+
/** Large display (1200px wide) */
|
|
1177
|
+
readonly large: {
|
|
1178
|
+
readonly width: 1200;
|
|
1179
|
+
readonly fit: "scale-down";
|
|
1180
|
+
readonly quality: 90;
|
|
1181
|
+
};
|
|
1182
|
+
/** Hero banner (1920x1080 cover) */
|
|
1183
|
+
readonly hero: {
|
|
1184
|
+
readonly width: 1920;
|
|
1185
|
+
readonly height: 1080;
|
|
1186
|
+
readonly fit: "cover";
|
|
1187
|
+
readonly quality: 85;
|
|
1188
|
+
};
|
|
1189
|
+
/** Social share (1200x630 for Open Graph) */
|
|
1190
|
+
readonly social: {
|
|
1191
|
+
readonly width: 1200;
|
|
1192
|
+
readonly height: 630;
|
|
1193
|
+
readonly fit: "cover";
|
|
1194
|
+
readonly quality: 85;
|
|
1195
|
+
};
|
|
1196
|
+
/** Mobile optimized (640px wide, WebP) */
|
|
1197
|
+
readonly mobile: {
|
|
1198
|
+
readonly width: 640;
|
|
1199
|
+
readonly fit: "scale-down";
|
|
1200
|
+
readonly quality: 75;
|
|
1201
|
+
readonly format: "image/webp";
|
|
1202
|
+
};
|
|
1203
|
+
};
|
|
1204
|
+
/**
|
|
1205
|
+
* Type for preset names.
|
|
1206
|
+
*/
|
|
1207
|
+
type ImagePresetName = keyof typeof IMAGE_PRESETS;
|
|
1208
|
+
|
|
1209
|
+
export { CLOUDFLARE_IMAGES_API, type CloudflareApiError, type CloudflareApiMessage, type CloudflareApiResponse, type CloudflareDirectUploadResponse, type CloudflareImage, type CloudflareImageDetailsResponse, type CloudflareImageInfo, type CloudflareImageOutputOptions, type CloudflareImageTransformOptions, type CloudflareImagesBinding, type CloudflareImagesStats, type CloudflareImagesTransformBuilder, type CloudflareImagesTransformResult, type CloudflareListResponse, type CloudflareUploadResponse, type CloudflareVariant, type DirectUploadOptions, type DirectUploadRequest, type DirectUploadResult, type EnvRef, IMAGE_PRESETS, ImageApiError, ImageBindingNotFoundError, ImageClient, type ImageClientInterface, type ImageConfig, ImageConfigError, ImageContextError, type ImageDefinition, type ImageEntry, ImageEnvError, ImageError, type ImageFitMode, type ImageGravity, type ImageManifest, type ImageMetadataHandling, ImageNotFoundError, type ImageOutputFormat, type ImagePresetName, type ImageResult, type ImageScanResult, ImageTransformError, type ImageTransformPreset, ImageUploadError, type ImageValidationError, type ImageValidationWarning, type ImageVariant, ImageVariantError, type ListOptions, type ScannedImage, type TransformPreset, type TransformerConfig, type UploadOptions, createImageClient, createImageTransformer, defineImage, isImageDefinition, variantToPreset };
|