perspectapi-ts-sdk 6.5.9 → 7.0.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.
Files changed (57) hide show
  1. package/README.md +46 -1011
  2. package/dist/chunk-MZ22HQBX.mjs +1451 -0
  3. package/dist/index-BL9-AZpq.d.mts +2227 -0
  4. package/dist/index-BL9-AZpq.d.ts +2227 -0
  5. package/dist/index.d.mts +130 -2221
  6. package/dist/index.d.ts +130 -2221
  7. package/dist/index.js +71 -7
  8. package/dist/index.mjs +13 -1364
  9. package/dist/v2/index.d.mts +1 -0
  10. package/dist/v2/index.d.ts +1 -0
  11. package/dist/v2/index.js +1477 -0
  12. package/dist/v2/index.mjs +40 -0
  13. package/docs/README.md +15 -0
  14. package/docs/v1-deprecated/README.md +9 -0
  15. package/docs/v1-deprecated/examples/README.md +324 -0
  16. package/docs/v1-deprecated/examples/basic-usage.ts +258 -0
  17. package/docs/v1-deprecated/examples/cloudflare-worker.ts +274 -0
  18. package/docs/v1-deprecated/examples/content-query-with-slug-prefix.ts +237 -0
  19. package/docs/v1-deprecated/examples/image-transforms.ts +200 -0
  20. package/docs/v1-deprecated/examples/site-user-checkout.ts +186 -0
  21. package/docs/v1-deprecated/examples/slug-prefix-examples.ts +491 -0
  22. package/docs/v1-deprecated/legacy-docs/caching.md +667 -0
  23. package/docs/v1-deprecated/legacy-docs/contact.md +1396 -0
  24. package/docs/v1-deprecated/legacy-docs/csrf-protection.md +664 -0
  25. package/docs/v1-deprecated/legacy-docs/image-transforms.md +523 -0
  26. package/docs/v1-deprecated/legacy-docs/loaders.md +304 -0
  27. package/docs/v1-deprecated/legacy-docs/newsletter.md +811 -0
  28. package/docs/v1-deprecated/legacy-docs/site-users.md +817 -0
  29. package/docs/v1-deprecated/legacy-notes/CHANGELOG-CHECKOUT.md +143 -0
  30. package/docs/v1-deprecated/legacy-notes/CSRF-CHECKOUT.md +271 -0
  31. package/docs/v1-deprecated/legacy-notes/IMAGE_TRANSFORMS_PORT.md +298 -0
  32. package/docs/v1-deprecated/sdk-readme.md +1076 -0
  33. package/examples/README.md +19 -0
  34. package/examples/basic-v2.ts +37 -0
  35. package/llms.txt +25 -0
  36. package/package.json +18 -7
  37. package/src/client/api-keys-client.ts +4 -0
  38. package/src/client/auth-client.ts +4 -0
  39. package/src/client/base-client.ts +7 -0
  40. package/src/client/bundles-client.ts +4 -0
  41. package/src/client/categories-client.ts +4 -0
  42. package/src/client/checkout-client.ts +4 -0
  43. package/src/client/contact-client.ts +4 -0
  44. package/src/client/content-client.ts +4 -0
  45. package/src/client/newsletter-client.ts +4 -0
  46. package/src/client/newsletter-management-client.ts +4 -0
  47. package/src/client/organizations-client.ts +4 -0
  48. package/src/client/products-client.ts +4 -0
  49. package/src/client/site-users-client.ts +10 -1
  50. package/src/client/sites-client.ts +4 -0
  51. package/src/client/webhooks-client.ts +4 -0
  52. package/src/deprecation.ts +2 -1
  53. package/src/index.ts +2 -1
  54. package/src/loaders.ts +59 -0
  55. package/src/perspect-api-client.ts +2 -2
  56. package/src/v2/client/orders-client.ts +89 -6
  57. package/src/v2/types.ts +3 -0
@@ -0,0 +1,143 @@
1
+ # Checkout Client Updates - Breaking Changes
2
+
3
+ ## Summary
4
+ Updated `CheckoutClient.createCheckoutSession()` to use the correct PerspectAPI route pattern with site name parameter.
5
+
6
+ ## Breaking Changes
7
+
8
+ ### Method Signature Change
9
+
10
+ **Before:**
11
+ ```typescript
12
+ async createCheckoutSession(data: CreateCheckoutSessionRequest): Promise<ApiResponse<CheckoutSession>>
13
+ ```
14
+
15
+ **After:**
16
+ ```typescript
17
+ async createCheckoutSession(siteName: string, data: CreateCheckoutSessionRequest): Promise<ApiResponse<CheckoutSession>>
18
+ ```
19
+
20
+ ### Route Change
21
+
22
+ **Before:**
23
+ ```
24
+ POST /api/v1/checkout/create-session
25
+ ```
26
+
27
+ **After:**
28
+ ```
29
+ POST /api/v1/{siteName}/checkout/create-session
30
+ ```
31
+
32
+ This matches the pattern used by other site-specific endpoints like:
33
+ - `/api/v1/{siteName}/products`
34
+ - `/api/v1/{siteName}/content`
35
+
36
+ ## Updated Type Definition
37
+
38
+ The `CreateCheckoutSessionRequest` interface now supports both single-item and multi-item checkouts:
39
+
40
+ ```typescript
41
+ export interface CreateCheckoutSessionRequest {
42
+ // Single item checkout (legacy/simple)
43
+ priceId?: string;
44
+ quantity?: number;
45
+
46
+ // Multiple items checkout (recommended)
47
+ line_items?: Array<{
48
+ price: string;
49
+ quantity: number;
50
+ }>;
51
+
52
+ // Common fields
53
+ success_url?: string;
54
+ cancel_url?: string;
55
+ successUrl?: string; // Alternative naming
56
+ cancelUrl?: string; // Alternative naming
57
+ customer_email?: string;
58
+ customerEmail?: string; // Alternative naming
59
+ metadata?: Record<string, string>;
60
+ mode?: 'payment' | 'subscription' | 'setup';
61
+ automatic_tax?: {
62
+ enabled: boolean;
63
+ };
64
+ shipping_address_collection?: {
65
+ allowed_countries: string[];
66
+ };
67
+ billing_address_collection?: 'auto' | 'required';
68
+ }
69
+ ```
70
+
71
+ ## Migration Guide
72
+
73
+ ### Old Usage (❌ Deprecated)
74
+
75
+ ```typescript
76
+ const client = createPerspectApiClient({
77
+ baseUrl: 'https://api.perspect.co',
78
+ apiKey: 'your-api-key'
79
+ });
80
+
81
+ // This will no longer work
82
+ const session = await client.checkout.createCheckoutSession({
83
+ priceId: 'price_xxxxx',
84
+ successUrl: 'https://example.com/success',
85
+ cancelUrl: 'https://example.com/cancel'
86
+ });
87
+ ```
88
+
89
+ ### New Usage (✅ Required)
90
+
91
+ ```typescript
92
+ const client = createPerspectApiClient({
93
+ baseUrl: 'https://api.perspect.co',
94
+ apiKey: 'your-api-key'
95
+ });
96
+
97
+ // Single item checkout
98
+ const session = await client.checkout.createCheckoutSession('museum-indian-art', {
99
+ line_items: [{
100
+ price: 'price_xxxxx',
101
+ quantity: 1
102
+ }],
103
+ success_url: 'https://example.com/success',
104
+ cancel_url: 'https://example.com/cancel'
105
+ });
106
+
107
+ // Multiple items checkout
108
+ const session = await client.checkout.createCheckoutSession('museum-indian-art', {
109
+ line_items: [
110
+ { price: 'price_xxxxx', quantity: 2 },
111
+ { price: 'price_yyyyy', quantity: 1 }
112
+ ],
113
+ success_url: 'https://example.com/success',
114
+ cancel_url: 'https://example.com/cancel',
115
+ customer_email: 'customer@example.com',
116
+ mode: 'payment',
117
+ automatic_tax: { enabled: true },
118
+ shipping_address_collection: {
119
+ allowed_countries: ['US', 'CA']
120
+ },
121
+ billing_address_collection: 'required'
122
+ });
123
+ ```
124
+
125
+ ## Benefits
126
+
127
+ 1. **Consistency** - Matches the pattern used by all other site-specific endpoints
128
+ 2. **Multi-tenancy** - Properly isolates checkout sessions by site
129
+ 3. **Flexibility** - Supports both single and multiple line items
130
+ 4. **Full Stripe Support** - Exposes all Stripe checkout session configuration options
131
+
132
+ ## Compatibility
133
+
134
+ - **Version**: 1.1.0+
135
+ - **PerspectAPI Backend**: Requires backend version that supports `/api/v1/{siteName}/checkout/create-session` route
136
+ - **Node.js**: 16.x or higher
137
+ - **TypeScript**: 4.5 or higher
138
+
139
+ ## Related Changes
140
+
141
+ - Updated `CreateCheckoutSessionRequest` interface to support multiple line items
142
+ - Added support for snake_case and camelCase field naming for flexibility
143
+ - Enhanced JSDoc documentation with parameter descriptions
@@ -0,0 +1,271 @@
1
+ # CSRF-Protected Checkout Implementation
2
+
3
+ ## Overview
4
+
5
+ The PerspectAPI SDK now includes automatic CSRF token handling for secure checkout operations. This follows the double-submit CSRF token pattern required by PerspectAPI.
6
+
7
+ ## SDK Updates
8
+
9
+ ### 1. CheckoutClient Enhancements
10
+
11
+ **New Method: `getCsrfToken(siteName)`**
12
+ ```typescript
13
+ // Get CSRF token for a specific site
14
+ const csrfResponse = await client.checkout.getCsrfToken('museum-indian-art');
15
+ // Returns: { csrf_token: "...", site_id: "...", site_name: "..." }
16
+ ```
17
+
18
+ **Enhanced: `createCheckoutSession(siteName, data, csrfToken?)`**
19
+ ```typescript
20
+ // Automatic CSRF handling (recommended)
21
+ const session = await client.checkout.createCheckoutSession(
22
+ 'museum-indian-art',
23
+ {
24
+ line_items: [
25
+ {
26
+ price: 'price_xxx', // Existing Stripe price ID
27
+ quantity: 1
28
+ }
29
+ ],
30
+ success_url: 'https://yoursite.com/success',
31
+ cancel_url: 'https://yoursite.com/cancel'
32
+ }
33
+ );
34
+
35
+ // Manual CSRF token (advanced)
36
+ const csrfToken = await client.checkout.getCsrfToken('museum-indian-art');
37
+ const session = await client.checkout.createCheckoutSession(
38
+ 'museum-indian-art',
39
+ data,
40
+ csrfToken.data.csrf_token
41
+ );
42
+ ```
43
+
44
+ ### 2. Main Client CSRF Methods
45
+
46
+ **Site-Specific Token (Recommended for Development)**
47
+ ```typescript
48
+ // Works from localhost, no Host header needed
49
+ const token = await client.getCsrfToken('museum-indian-art');
50
+ ```
51
+
52
+ **Host-Based Token (Production)**
53
+ ```typescript
54
+ // Requires correct Host header
55
+ const token = await client.getCsrfToken();
56
+ ```
57
+
58
+ ### 3. Enhanced Type Definitions
59
+
60
+ **Line Items Support:**
61
+ ```typescript
62
+ interface CreateCheckoutSessionRequest {
63
+ line_items?: Array<{
64
+ // Option 1: Use existing Stripe price ID
65
+ price?: string;
66
+ quantity: number;
67
+
68
+ // Option 2: Define price inline
69
+ price_data?: {
70
+ currency: string;
71
+ product_data: {
72
+ name: string;
73
+ description?: string;
74
+ images?: string[];
75
+ };
76
+ unit_amount: number; // Amount in cents
77
+ };
78
+ }>;
79
+
80
+ // Required fields
81
+ success_url: string;
82
+ cancel_url: string;
83
+
84
+ // Optional fields
85
+ customer_email?: string;
86
+ metadata?: Record<string, string>;
87
+ mode?: 'payment' | 'subscription' | 'setup';
88
+ automatic_tax?: { enabled: boolean };
89
+ shipping_address_collection?: { allowed_countries: string[] };
90
+ billing_address_collection?: 'auto' | 'required';
91
+ }
92
+ ```
93
+
94
+ ## API Endpoints
95
+
96
+ ### 1. Get CSRF Token
97
+
98
+ **Site-Specific (Development-Friendly)**
99
+ ```bash
100
+ GET /api/v1/csrf/token/{siteName}
101
+
102
+ # Example
103
+ curl -X GET "https://api.perspect.co/api/v1/csrf/token/museum-indian-art" \
104
+ -H "X-API-Key: your_api_key"
105
+
106
+ # Response
107
+ {
108
+ "csrf_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
109
+ "site_id": "site_xxx",
110
+ "site_name": "museum-indian-art"
111
+ }
112
+ ```
113
+
114
+ **Host-Based (Production)**
115
+ ```bash
116
+ GET /api/v1/csrf/token
117
+
118
+ curl -X GET "https://api.perspect.co/api/v1/csrf/token" \
119
+ -H "X-API-Key: your_api_key" \
120
+ -H "Host: indianart.perspect.com"
121
+ ```
122
+
123
+ ### 2. Create Checkout Session
124
+
125
+ ```bash
126
+ POST /api/v1/{siteName}/checkout/create-session
127
+
128
+ curl -X POST "https://api.perspect.co/api/v1/museum-indian-art/checkout/create-session" \
129
+ -H "Content-Type: application/json" \
130
+ -H "X-API-Key: your_api_key" \
131
+ -H "X-CSRF-Token: csrf_token_from_step_1" \
132
+ -d '{
133
+ "line_items": [
134
+ {
135
+ "price": "price_xxx",
136
+ "quantity": 1
137
+ }
138
+ ],
139
+ "success_url": "https://yoursite.com/success",
140
+ "cancel_url": "https://yoursite.com/cancel"
141
+ }'
142
+
143
+ # Response
144
+ {
145
+ "id": "cs_test_...",
146
+ "url": "https://checkout.stripe.com/pay/cs_test_..."
147
+ }
148
+ ```
149
+
150
+ ## How It Works
151
+
152
+ ### Automatic CSRF Flow (SDK)
153
+
154
+ 1. **Client calls** `createCheckoutSession(siteName, data)`
155
+ 2. **SDK checks** if CSRF token provided
156
+ 3. **If not**, SDK calls `GET /api/v1/csrf/token/{siteName}`
157
+ 4. **SDK extracts** `csrf_token` from response
158
+ 5. **SDK makes** checkout request with `X-CSRF-Token` header
159
+ 6. **API validates** CSRF token and creates session
160
+ 7. **SDK returns** checkout session with ID and URL
161
+
162
+ ### Manual CSRF Flow (Direct API)
163
+
164
+ 1. **Client calls** `GET /api/v1/csrf/token/{siteName}`
165
+ 2. **API returns** CSRF token
166
+ 3. **Client calls** `POST /api/v1/{siteName}/checkout/create-session` with `X-CSRF-Token` header
167
+ 4. **API validates** token and creates session
168
+ 5. **API returns** checkout session
169
+
170
+ ## Benefits
171
+
172
+ ### Security
173
+ - ✅ Double-submit CSRF protection
174
+ - ✅ Site-specific token signing
175
+ - ✅ JWT-based tokens with expiration
176
+ - ✅ Prevents cross-site request forgery attacks
177
+
178
+ ### Developer Experience
179
+ - ✅ Automatic token handling in SDK
180
+ - ✅ Works from localhost without configuration
181
+ - ✅ No Host header manipulation needed
182
+ - ✅ Explicit site specification
183
+
184
+ ### Portability
185
+ - ✅ Works across all environments (Cloudflare Workers, Node.js, etc.)
186
+ - ✅ No vendor lock-in
187
+ - ✅ Standard HTTP headers
188
+ - ✅ Compatible with any HTTP client
189
+
190
+ ## Migration Guide
191
+
192
+ ### Before (Direct Fetch)
193
+ ```typescript
194
+ const response = await fetch(`${baseUrl}/api/v1/${siteName}/checkout/create-session`, {
195
+ method: 'POST',
196
+ headers: {
197
+ 'Content-Type': 'application/json',
198
+ 'X-API-Key': apiKey
199
+ },
200
+ body: JSON.stringify(data)
201
+ });
202
+ // ❌ Missing CSRF token - will fail with 401
203
+ ```
204
+
205
+ ### After (SDK with Auto CSRF)
206
+ ```typescript
207
+ const response = await client.checkout.createCheckoutSession(siteName, data);
208
+ // ✅ CSRF token automatically handled
209
+ ```
210
+
211
+ ## Example: Remix Integration
212
+
213
+ ```typescript
214
+ // app/utils/perspectapi-loaders.server.ts
215
+ export async function createCheckoutSession(params: {
216
+ products: Array<{ stripe_product_id: string; quantity: number }>;
217
+ successUrl: string;
218
+ cancelUrl: string;
219
+ customerEmail?: string;
220
+ }) {
221
+ const siteName = getSiteName();
222
+
223
+ // SDK handles CSRF automatically
224
+ const response = await perspectClient.checkout.createCheckoutSession(
225
+ siteName,
226
+ {
227
+ line_items: params.products.map(p => ({
228
+ price: p.stripe_product_id,
229
+ quantity: p.quantity
230
+ })),
231
+ success_url: params.successUrl,
232
+ cancel_url: params.cancelUrl,
233
+ customer_email: params.customerEmail,
234
+ mode: 'payment',
235
+ automatic_tax: { enabled: true },
236
+ shipping_address_collection: {
237
+ allowed_countries: ['US', 'CA']
238
+ },
239
+ billing_address_collection: 'required'
240
+ }
241
+ );
242
+
243
+ return {
244
+ id: response.data?.id,
245
+ url: response.data?.url
246
+ };
247
+ }
248
+ ```
249
+
250
+ ## Troubleshooting
251
+
252
+ ### "CSRF token required" (401)
253
+ - Ensure you're using the SDK's `createCheckoutSession()` method
254
+ - Or manually fetch CSRF token and include in `X-CSRF-Token` header
255
+ - Verify API key has proper permissions
256
+
257
+ ### "Failed to obtain CSRF token"
258
+ - Check that site name is correct
259
+ - Verify API key is valid
260
+ - Ensure CSRF endpoint is accessible
261
+
262
+ ### CSRF token format issues
263
+ - SDK handles multiple response formats: `data.token`, `token`, `csrf_token`
264
+ - Response may vary based on API version
265
+ - SDK automatically extracts the correct field
266
+
267
+ ## Related Documentation
268
+
269
+ - [Checkout Integration Guide](../museumof-remix/CHECKOUT-INTEGRATION.md)
270
+ - [PerspectAPI CSRF Implementation](https://github.com/perspectapiworkers)
271
+ - [Stripe Checkout Documentation](https://stripe.com/docs/payments/checkout)
@@ -0,0 +1,298 @@
1
+ # Image Transformation Utilities - Port Complete
2
+
3
+ Successfully ported Cloudflare Image Resizing utilities from `perspectapiworkers` to `perspectapi-ts-sdk`.
4
+
5
+ ## What Was Ported
6
+
7
+ ### 1. Core Utilities (`src/utils/image-transform.ts`)
8
+
9
+ **Functions:**
10
+ - ✅ `buildImageUrl()` - Build single transformed URL with custom options
11
+ - ✅ `generateResponsiveUrls()` - Generate multiple sizes at once
12
+ - ✅ `generateSrcSet()` - Generate srcset for responsive images
13
+ - ✅ `generateSizesAttribute()` - Generate sizes attribute
14
+ - ✅ `generateResponsiveImageHtml()` - Complete HTML generation
15
+ - ✅ `transformMediaItem()` - **NEW** - Convenience function for MediaItem objects
16
+
17
+ **Types:**
18
+ - ✅ `ImageTransformOptions` - All transformation options
19
+ - ✅ `ResponsiveImageSizes` - Size presets interface
20
+ - ✅ `DEFAULT_IMAGE_SIZES` - Default responsive sizes
21
+
22
+ **Transform Options Supported:**
23
+ - `width`, `height` - Dimensions
24
+ - `fit` - Resize mode (scale-down, contain, cover, crop, pad)
25
+ - `gravity` - Crop focus (auto, left, right, top, bottom, center)
26
+ - `quality` - 1-100
27
+ - `format` - auto, avif, webp, jpeg, png
28
+ - `dpr` - Device pixel ratio (1, 2, 3)
29
+ - `sharpen`, `blur`, `rotate` - Effects
30
+ - `metadata`, `background`, `trim` - Advanced options
31
+
32
+ ### 2. Documentation (`docs/image-transforms.md`)
33
+
34
+ **Comprehensive guide covering:**
35
+ - ✅ Quick start examples
36
+ - ✅ All function documentation with examples
37
+ - ✅ Common use cases (e-commerce, blog, avatars)
38
+ - ✅ Framework examples (Next.js, Remix, Cloudflare Workers)
39
+ - ✅ React component examples
40
+ - ✅ Best practices
41
+ - ✅ Performance benefits
42
+ - ✅ Troubleshooting
43
+ - ✅ Requirements and setup
44
+
45
+ ### 3. Examples (`examples/image-transforms.ts`)
46
+
47
+ **Demonstrates:**
48
+ - ✅ Product image transformation
49
+ - ✅ Custom transformations
50
+ - ✅ Responsive images
51
+ - ✅ High DPR (Retina) support
52
+ - ✅ React component patterns
53
+ - ✅ Common use cases
54
+
55
+ ### 4. SDK Integration
56
+
57
+ **Exports added to `src/index.ts`:**
58
+ ```typescript
59
+ export {
60
+ buildImageUrl,
61
+ generateResponsiveUrls,
62
+ generateSrcSet,
63
+ generateSizesAttribute,
64
+ generateResponsiveImageHtml,
65
+ transformMediaItem,
66
+ DEFAULT_IMAGE_SIZES
67
+ } from './utils/image-transform';
68
+
69
+ export type {
70
+ ImageTransformOptions,
71
+ ResponsiveImageSizes
72
+ } from './utils/image-transform';
73
+ ```
74
+
75
+ **README updated:**
76
+ - ✅ Added Image Transformations section
77
+ - ✅ Quick start example
78
+ - ✅ Feature list
79
+ - ✅ Link to full documentation
80
+
81
+ ## Key Improvements Over Original
82
+
83
+ ### 1. `transformMediaItem()` Function
84
+
85
+ **New convenience function** specifically for working with SDK's `MediaItem` type:
86
+
87
+ ```typescript
88
+ const product = await client.products.getProduct('mysite', 123);
89
+ const media = product.data.media?.[0];
90
+
91
+ if (media) {
92
+ const urls = transformMediaItem('https://api.perspect.co', media);
93
+ // Automatically handles both 'link' and 'r2_key' formats
94
+ }
95
+ ```
96
+
97
+ This makes it trivial for SDK users to transform images from API responses.
98
+
99
+ ### 2. Better Type Safety
100
+
101
+ All functions have proper TypeScript types and JSDoc documentation with examples.
102
+
103
+ ### 3. SDK-Specific Examples
104
+
105
+ Documentation includes examples using the SDK's client methods, not just raw URLs.
106
+
107
+ ## Usage Examples
108
+
109
+ ### Basic Usage
110
+
111
+ ```typescript
112
+ import { createPerspectApiClient, transformMediaItem } from 'perspectapi-ts-sdk';
113
+
114
+ const client = createPerspectApiClient({
115
+ baseUrl: 'https://api.perspect.co',
116
+ apiKey: 'your-api-key'
117
+ });
118
+
119
+ // Get products
120
+ const products = await client.products.getProducts('mysite', { limit: 10 });
121
+
122
+ // Transform images
123
+ products.data.forEach(product => {
124
+ const media = product.media?.[0];
125
+ if (media && !Array.isArray(media)) {
126
+ const urls = transformMediaItem('https://api.perspect.co', media);
127
+ console.log('Thumbnail:', urls.thumbnail);
128
+ console.log('Large:', urls.large);
129
+ }
130
+ });
131
+ ```
132
+
133
+ ### React Component
134
+
135
+ ```tsx
136
+ import { transformMediaItem, type MediaItem } from 'perspectapi-ts-sdk';
137
+
138
+ interface ProductImageProps {
139
+ media: MediaItem;
140
+ alt: string;
141
+ }
142
+
143
+ export function ProductImage({ media, alt }: ProductImageProps) {
144
+ const urls = transformMediaItem('https://api.perspect.co', media);
145
+
146
+ return (
147
+ <img
148
+ src={urls.medium}
149
+ srcSet={`
150
+ ${urls.small} 400w,
151
+ ${urls.medium} 800w,
152
+ ${urls.large} 1200w
153
+ `}
154
+ sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw"
155
+ alt={alt}
156
+ loading="lazy"
157
+ className="rounded-lg"
158
+ />
159
+ );
160
+ }
161
+ ```
162
+
163
+ ### Custom Transformations
164
+
165
+ ```typescript
166
+ import { buildImageUrl } from 'perspectapi-ts-sdk';
167
+
168
+ // E-commerce thumbnail
169
+ const thumbnail = buildImageUrl(
170
+ 'https://api.perspect.co',
171
+ 'media/mysite/product.jpg',
172
+ {
173
+ width: 300,
174
+ height: 300,
175
+ fit: 'cover',
176
+ format: 'auto',
177
+ quality: 85
178
+ }
179
+ );
180
+
181
+ // Blog featured image
182
+ const featured = buildImageUrl(
183
+ 'https://api.perspect.co',
184
+ 'media/mysite/blog.jpg',
185
+ {
186
+ width: 800,
187
+ fit: 'scale-down',
188
+ format: 'auto',
189
+ quality: 85
190
+ }
191
+ );
192
+
193
+ // Retina display
194
+ const retina = buildImageUrl(
195
+ 'https://api.perspect.co',
196
+ 'media/mysite/icon.jpg',
197
+ {
198
+ width: 400,
199
+ dpr: 2,
200
+ format: 'auto'
201
+ }
202
+ );
203
+ ```
204
+
205
+ ## Benefits for SDK Users
206
+
207
+ ### 1. Zero Configuration
208
+ No setup needed - just import and use. Works with any MediaItem from the API.
209
+
210
+ ### 2. Type Safe
211
+ Full TypeScript support with autocomplete and type checking.
212
+
213
+ ### 3. Framework Agnostic
214
+ Works in Next.js, Remix, Cloudflare Workers, vanilla JS, etc.
215
+
216
+ ### 4. Performance
217
+ - On-the-fly resizing (no pre-generated thumbnails)
218
+ - Automatic format optimization (WebP/AVIF)
219
+ - CDN caching at the edge
220
+ - Bandwidth savings
221
+
222
+ ### 5. Easy Migration
223
+ If you're already using the API, just add image transformations with one function call.
224
+
225
+ ## Requirements
226
+
227
+ - Cloudflare Image Resizing must be enabled on your zone
228
+ - Images must be served through Cloudflare
229
+ - Free tier: 100,000 images/month
230
+ - Paid: $5 per 100,000 images after free tier
231
+
232
+ ## Testing
233
+
234
+ **Comprehensive test suite** (`tests/utils/image-transform.test.ts`):
235
+
236
+ ✅ **100+ test cases** covering:
237
+ - `buildImageUrl()` - All transform options, edge cases
238
+ - `generateResponsiveUrls()` - Default and custom sizes
239
+ - `generateSrcSet()` - Default and custom widths
240
+ - `generateSizesAttribute()` - Default and custom breakpoints
241
+ - `generateResponsiveImageHtml()` - All options and attributes
242
+ - `transformMediaItem()` - Both link and r2_key formats
243
+ - `DEFAULT_IMAGE_SIZES` - All presets
244
+ - Edge cases - Empty paths, query params, zero values
245
+
246
+ **Run tests:**
247
+ ```bash
248
+ npm test tests/utils/image-transform.test.ts
249
+ ```
250
+
251
+ **Test coverage includes:**
252
+ - URL format validation
253
+ - Cloudflare Image Resizing URL structure
254
+ - All transformation options (width, height, fit, gravity, quality, format, dpr, etc.)
255
+ - Responsive image generation
256
+ - MediaItem object handling
257
+ - Edge cases and error conditions
258
+
259
+ ## Files Added/Modified
260
+
261
+ **New Files:**
262
+ - `src/utils/image-transform.ts` - Core utilities (256 lines)
263
+ - `docs/image-transforms.md` - Complete documentation (600+ lines)
264
+ - `examples/image-transforms.ts` - Working examples (200+ lines)
265
+ - `tests/utils/image-transform.test.ts` - Comprehensive tests (400+ lines)
266
+ - `IMAGE_TRANSFORMS_PORT.md` - This summary
267
+
268
+ **Modified Files:**
269
+ - `src/index.ts` - Added exports
270
+ - `README.md` - Added Image Transformations section
271
+ - `examples/README.md` - Added image transforms example
272
+
273
+ ## Testing
274
+
275
+ Run the example:
276
+ ```bash
277
+ export API_URL=https://api.perspect.co
278
+ export API_KEY=your-api-key
279
+ export SITE_NAME=your-site
280
+
281
+ npx tsx examples/image-transforms.ts
282
+ ```
283
+
284
+ ## Next Steps
285
+
286
+ SDK users can now:
287
+ 1. Import image transformation utilities
288
+ 2. Transform MediaItem objects from API responses
289
+ 3. Build custom transformation URLs
290
+ 4. Generate responsive images with srcset
291
+ 5. Use in any framework (React, Vue, Svelte, etc.)
292
+
293
+ ## Documentation
294
+
295
+ - **Quick Start**: See README.md
296
+ - **Full Guide**: See docs/image-transforms.md
297
+ - **Examples**: See examples/image-transforms.ts
298
+ - **API Reference**: TypeScript types in src/utils/image-transform.ts