@outfitcanvas/fitview-sdk 1.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.
- package/CHANGELOG.md +33 -0
- package/README.md +549 -0
- package/dist/chunk-2CROJB7V.mjs +4830 -0
- package/dist/index.d.mts +729 -0
- package/dist/index.d.ts +729 -0
- package/dist/index.js +14186 -0
- package/dist/index.mjs +9008 -0
- package/dist/multipart-parser-I2ECHDUD.mjs +369 -0
- package/package.json +55 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-02-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of `@outfitcanvas/fitview-sdk` (TypeScript/JavaScript).
|
|
13
|
+
- **Client:** `FitViewClient` with configurable `apiKey`, `baseURL`, `timeout`, `maxRetries`, `retryDelay`, `authMethod` (Bearer or X-API-Key).
|
|
14
|
+
- **FitView API:** `generateFitView`, `getFitView`, `batchGenerateFitView`, `listModels`, `waitForFitView`.
|
|
15
|
+
- **Billing & health:** `getRateLimitAddons`, `healthCheck`, `readinessCheck`, `livenessCheck`.
|
|
16
|
+
- **Result:** `FitViewResult` with `jobId`, `status`, `fitviewUrl`, `waitForCompletion`, `refresh`, `download`.
|
|
17
|
+
- **Request types:** Single garment (`garment_image`), multi-garment (`garments` array), base64 images, optional `model`, `quality`, `use_case`, `idempotency_key`, `webhook_url`, etc.
|
|
18
|
+
- **Errors:** Typed errors with clear messages: `FitViewAPIError`, `FitViewValidationError`, `FitViewAuthenticationError`, `FitViewInsufficientCreditsError`, `FitViewRateLimitError`, `FitViewTimeoutError`, `FitViewNetworkError`. User-friendly messages for job failures (no raw API crash text).
|
|
19
|
+
- **Retries:** Automatic retries for 429 and 5xx with exponential backoff.
|
|
20
|
+
- **Webhook:** `verifyWebhook(payload, signature, secret)` for signature verification.
|
|
21
|
+
- **Examples:** `examples/quickstart.ts` and `examples/quickstart.mjs` with commented alternatives (single/multi-garment, base64, options).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Polling: **10 seconds** before the first status fetch, then **2 seconds** between subsequent polls (`initialDelay` and `interval` in `WaitOptions`).
|
|
26
|
+
- API response envelope (`success`, `data`) is unwrapped internally; methods return the inner payload.
|
|
27
|
+
|
|
28
|
+
### Notes
|
|
29
|
+
|
|
30
|
+
- Default model when not specified: **canvas-standard**.
|
|
31
|
+
- Compatible with FitView API v1. Node.js >= 18.
|
|
32
|
+
|
|
33
|
+
[1.0.0]: #
|
package/README.md
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# FitView TypeScript SDK
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the FitView API - virtual try-on service.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @outfitcanvas/fitview-sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @outfitcanvas/fitview-sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @outfitcanvas/fitview-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { FitViewClient, OutfitCanvasModel } from '@outfitcanvas/fitview-sdk';
|
|
19
|
+
|
|
20
|
+
// Initialize the client
|
|
21
|
+
const client = new FitViewClient({
|
|
22
|
+
apiKey: 'pk_live_your_api_key_here',
|
|
23
|
+
baseURL: 'https://api.fitview.com', // Optional, defaults to production
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Generate a virtual try-on
|
|
27
|
+
const result = await client.generateFitView({
|
|
28
|
+
person_image: 'https://example.com/person.jpg',
|
|
29
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
30
|
+
model: OutfitCanvasModel.CanvasStandard,
|
|
31
|
+
quality: 'high',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Wait for completion
|
|
35
|
+
const completed = await result.waitForCompletion();
|
|
36
|
+
|
|
37
|
+
// Get the result image URL
|
|
38
|
+
console.log('FitView URL:', completed.fitviewUrl);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Security:** Prefer environment variables for your API key (e.g. `process.env.FITVIEW_API_KEY`). Never embed API keys in client-side or public code—use the SDK only in server-side or trusted environments.
|
|
42
|
+
|
|
43
|
+
**Quick reference:** For a one-page TypeScript method/signature overview (IDE autocomplete and LLM code generation), see [Quick Reference — TypeScript](../../docs/sdk/quick-reference-typescript.md).
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
### FitViewClient
|
|
48
|
+
|
|
49
|
+
Main client class for interacting with the FitView API.
|
|
50
|
+
|
|
51
|
+
#### Constructor
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const client = new FitViewClient({
|
|
55
|
+
apiKey: string, // Required: Your API key
|
|
56
|
+
baseURL?: string, // Optional: API base URL (default: https://api.fitview.com)
|
|
57
|
+
timeout?: number, // Optional: Request timeout in ms (default: 300000)
|
|
58
|
+
apiVersion?: string, // Optional: API version (default: 'v1')
|
|
59
|
+
maxRetries?: number, // Optional: Retries for 429/5xx (default: 3, set 0 to disable)
|
|
60
|
+
retryDelay?: number, // Optional: Base delay in ms between retries (default: 1000)
|
|
61
|
+
authMethod?: 'bearer' | 'apiKey', // Optional: Bearer token (default) or X-API-Key header
|
|
62
|
+
fetch?: typeof fetch, // Optional: Custom fetch implementation
|
|
63
|
+
headers?: Record<string, string>, // Optional: Additional headers
|
|
64
|
+
validateInputImages?: boolean, // Optional: Validate image format/size before sending (default: true)
|
|
65
|
+
optimizeImages?: boolean, // Optional: Optimize base64 images client-side when sharp is available (default: false)
|
|
66
|
+
optimizeImageOptions?: { maxDimension?: number; jpegQuality?: number; outputFormat?: 'jpeg' | 'png' | 'webp' | 'original' },
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Methods
|
|
71
|
+
|
|
72
|
+
##### `generateFitView(request: FitViewRequest): Promise<FitViewResult>`
|
|
73
|
+
|
|
74
|
+
Generate a virtual try-on result.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const result = await client.generateFitView({
|
|
78
|
+
person_image: 'https://example.com/person.jpg',
|
|
79
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
80
|
+
model: OutfitCanvasModel.CanvasStandard,
|
|
81
|
+
quality: 'high',
|
|
82
|
+
use_case: 'fashion',
|
|
83
|
+
idempotency_key: '550e8400-e29b-41d4-a716-446655440000',
|
|
84
|
+
webhook_url: 'https://your-app.com/webhook',
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
##### `getFitView(jobId: string): Promise<FitViewResult>`
|
|
89
|
+
|
|
90
|
+
Get the status of a FitView job.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const result = await client.getFitView('fv_abc123');
|
|
94
|
+
console.log(result.status); // 'queued' | 'processing' | 'completed' | 'failed'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
##### `batchGenerateFitView(requests: FitViewRequest[]): Promise<FitViewBatchResponse>`
|
|
98
|
+
|
|
99
|
+
Generate multiple virtual try-on results in a batch (max 50).
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const batch = await client.batchGenerateFitView([
|
|
103
|
+
{ person_image: '...', garment_image: '...' },
|
|
104
|
+
{ person_image: '...', garment_image: '...' },
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
console.log(`Success: ${batch.success_count}, Failed: ${batch.failure_count}`);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
##### `listModels(): Promise<ModelInfo[]>`
|
|
111
|
+
|
|
112
|
+
List available models for your account tier.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const models = await client.listModels();
|
|
116
|
+
models.forEach(model => {
|
|
117
|
+
console.log(`${model.displayName}: ${model.creditCost} credits`);
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
##### `waitForFitView(jobId: string, options?: WaitOptions): Promise<FitViewResult>`
|
|
122
|
+
|
|
123
|
+
Wait for a job to complete with polling. **Defaults** (used when you omit options): wait **10 seconds** before the first status fetch, then poll every **2 seconds** until done.
|
|
124
|
+
|
|
125
|
+
> **Warning:** Consult FitView support before changing `initialDelay` or `interval`. Other values may affect rate limits, reliability, and support eligibility.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Use defaults: 10s before first fetch, 2s between polls (recommended)
|
|
129
|
+
const result = await client.waitForFitView('fv_abc123');
|
|
130
|
+
|
|
131
|
+
// Or customize (consult support before changing polling defaults)
|
|
132
|
+
const result = await client.waitForFitView('fv_abc123', {
|
|
133
|
+
timeout: 300000,
|
|
134
|
+
initialDelay: 10000, // default: 10s before first fetch
|
|
135
|
+
interval: 2000, // default: 2s between subsequent polls
|
|
136
|
+
onProgress: (result) => console.log(`Status: ${result.status}`),
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
##### `getRateLimitAddons(): Promise<RateLimitAddonsResponse>`
|
|
141
|
+
|
|
142
|
+
Get active rate limit add-ons and total boost (billing).
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const { active_addons, total_boost } = await client.getRateLimitAddons();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
##### `healthCheck(): Promise<{ status: string; timestamp?: string }>`
|
|
149
|
+
|
|
150
|
+
Basic health check (no auth). Returns `{ status, timestamp }`.
|
|
151
|
+
|
|
152
|
+
##### `readinessCheck(): Promise<HealthStatus>`
|
|
153
|
+
|
|
154
|
+
Readiness check with dependency status (no auth). Returns status, version, uptime, checks.
|
|
155
|
+
|
|
156
|
+
##### `livenessCheck(): Promise<{ status: string }>`
|
|
157
|
+
|
|
158
|
+
Liveness check (no auth). Returns `{ status: 'alive' }`.
|
|
159
|
+
|
|
160
|
+
##### `verifyWebhook(payload: string | Buffer, signature: string, secret: string): boolean`
|
|
161
|
+
|
|
162
|
+
Verify webhook signature.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const isValid = client.verifyWebhook(
|
|
166
|
+
rawBody,
|
|
167
|
+
signature,
|
|
168
|
+
webhookSecret
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### FitViewResult
|
|
173
|
+
|
|
174
|
+
Wrapper class for FitView API responses with convenient methods.
|
|
175
|
+
|
|
176
|
+
#### Properties
|
|
177
|
+
|
|
178
|
+
- `jobId: string` - Job identifier
|
|
179
|
+
- `status: 'queued' | 'processing' | 'completed' | 'failed'` - Current status
|
|
180
|
+
- `fitviewUrl?: string` - URL of the generated image (if completed)
|
|
181
|
+
- `thumbnailUrl?: string` - URL of the thumbnail (if available)
|
|
182
|
+
- `modelUsed?: string` - Model used for generation
|
|
183
|
+
- `modelVersion?: string` - Model version
|
|
184
|
+
- `generationTimeMs?: number` - Generation time in milliseconds
|
|
185
|
+
- `enrichedData?: EnrichedData` - Additional enriched data
|
|
186
|
+
- `error?: FitViewError` - Error information (if failed)
|
|
187
|
+
- `metadata: ResponseMetadata` - Response metadata
|
|
188
|
+
|
|
189
|
+
#### Methods
|
|
190
|
+
|
|
191
|
+
##### `isCompleted(): boolean`
|
|
192
|
+
|
|
193
|
+
Check if the job is completed.
|
|
194
|
+
|
|
195
|
+
##### `isFailed(): boolean`
|
|
196
|
+
|
|
197
|
+
Check if the job has failed.
|
|
198
|
+
|
|
199
|
+
##### `isProcessing(): boolean`
|
|
200
|
+
|
|
201
|
+
Check if the job is still processing.
|
|
202
|
+
|
|
203
|
+
##### `download(): Promise<Buffer | Blob>`
|
|
204
|
+
|
|
205
|
+
Download the fitview image.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const image = await result.download();
|
|
209
|
+
// In Node.js: Buffer
|
|
210
|
+
// In browser: Blob
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
##### `refresh(): Promise<FitViewResult>`
|
|
214
|
+
|
|
215
|
+
Refresh the result by fetching the latest status.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const updated = await result.refresh();
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
##### `waitForCompletion(options?: WaitOptions): Promise<FitViewResult>`
|
|
222
|
+
|
|
223
|
+
Wait for the job to complete. Uses the same defaults as `waitForFitView` (10s before first fetch, 2s between polls). Consult FitView support before changing `initialDelay` or `interval`.
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// Defaults (recommended)
|
|
227
|
+
const completed = await result.waitForCompletion();
|
|
228
|
+
|
|
229
|
+
// With options (consult support before changing polling defaults)
|
|
230
|
+
const completed = await result.waitForCompletion({
|
|
231
|
+
timeout: 300000,
|
|
232
|
+
initialDelay: 10000,
|
|
233
|
+
interval: 2000,
|
|
234
|
+
onProgress: (result) => console.log(result.status),
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Examples
|
|
239
|
+
|
|
240
|
+
A runnable quick start (same flow as the Python example) is in `examples/`:
|
|
241
|
+
|
|
242
|
+
- **TypeScript:** `npx tsx examples/quickstart.ts` (from `packages/sdk-typescript`)
|
|
243
|
+
- **JavaScript:** `npm run build && node examples/quickstart.mjs`
|
|
244
|
+
|
|
245
|
+
Set `FITVIEW_API_KEY` and optionally `FITVIEW_BASE_URL` in the environment.
|
|
246
|
+
|
|
247
|
+
### Basic Generation
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { FitViewClient, OutfitCanvasModel } from '@outfitcanvas/fitview-sdk';
|
|
251
|
+
|
|
252
|
+
const client = new FitViewClient({
|
|
253
|
+
apiKey: process.env.FITVIEW_API_KEY!,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Generate
|
|
257
|
+
const result = await client.generateFitView({
|
|
258
|
+
person_image: 'https://example.com/person.jpg',
|
|
259
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
260
|
+
model: OutfitCanvasModel.CanvasStandard,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Wait for completion
|
|
264
|
+
const completed = await result.waitForCompletion();
|
|
265
|
+
|
|
266
|
+
// Use the result
|
|
267
|
+
if (completed.isCompleted()) {
|
|
268
|
+
console.log('Image URL:', completed.fitviewUrl);
|
|
269
|
+
|
|
270
|
+
// Download the image
|
|
271
|
+
const image = await completed.download();
|
|
272
|
+
// Save or process the image...
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Image sources (URLs vs device)
|
|
277
|
+
|
|
278
|
+
- **Images on your servers:** Pass a public **HTTP or HTTPS URL** in `person_image` and `garment_image`. FitView fetches the image from that URL; no separate upload step is required. The URL must be reachable by the FitView API (public or allowlisted).
|
|
279
|
+
- **Images on user devices:** The API accepts **base64 (or data URL) directly** in `person_image` and `garment_image` for generate requests—no upload step required. For very large images or when you prefer to upload once and reuse the URL, use **upload first, then generate:** call `client.uploadImage(base64)` or `client.uploadImageFromFile(filePath)` (Node.js) to get a URL, then pass that URL into `generateFitView`.
|
|
280
|
+
|
|
281
|
+
### Using Base64 Images
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const result = await client.generateFitView({
|
|
285
|
+
person_image: {
|
|
286
|
+
type: 'base64',
|
|
287
|
+
data: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
|
|
288
|
+
},
|
|
289
|
+
garment_image: {
|
|
290
|
+
type: 'base64',
|
|
291
|
+
data: 'data:image/jpeg;base64,/9j/4AAQSkZJRg...',
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Upload from file (Node.js)
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { FitViewClient } from '@outfitcanvas/fitview-sdk';
|
|
300
|
+
|
|
301
|
+
const client = new FitViewClient({ apiKey: process.env.FITVIEW_API_KEY! });
|
|
302
|
+
|
|
303
|
+
// Upload a local file and get a URL for generate
|
|
304
|
+
const { url } = await client.uploadImageFromFile('/path/to/person.jpg');
|
|
305
|
+
const result = await client.generateFitView({
|
|
306
|
+
person_image: url,
|
|
307
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Idempotency key (safe retries)
|
|
312
|
+
|
|
313
|
+
To retry a create-job request without creating duplicate jobs, send an `idempotency_key`. Generate a new UUID per logical request:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { FitViewClient, generateIdempotencyKey } from '@outfitcanvas/fitview-sdk';
|
|
317
|
+
|
|
318
|
+
const client = new FitViewClient({ apiKey: process.env.FITVIEW_API_KEY! });
|
|
319
|
+
|
|
320
|
+
const result = await client.generateFitView({
|
|
321
|
+
person_image: 'https://example.com/person.jpg',
|
|
322
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
323
|
+
idempotency_key: generateIdempotencyKey(), // or FitViewClient.generateIdempotencyKey()
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Batch Processing
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const batch = await client.batchGenerateFitView([
|
|
331
|
+
{
|
|
332
|
+
person_image: 'https://example.com/person1.jpg',
|
|
333
|
+
garment_image: 'https://example.com/garment1.jpg',
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
person_image: 'https://example.com/person2.jpg',
|
|
337
|
+
garment_image: 'https://example.com/garment2.jpg',
|
|
338
|
+
},
|
|
339
|
+
]);
|
|
340
|
+
|
|
341
|
+
// Process results
|
|
342
|
+
batch.results.forEach((item, index) => {
|
|
343
|
+
if (item.response) {
|
|
344
|
+
console.log(`Request ${index + 1}: ${item.response.job_id}`);
|
|
345
|
+
} else if (item.error) {
|
|
346
|
+
console.error(`Request ${index + 1} failed:`, item.error.message);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Error Handling
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import {
|
|
355
|
+
FitViewClient,
|
|
356
|
+
FitViewValidationError,
|
|
357
|
+
FitViewInsufficientCreditsError,
|
|
358
|
+
FitViewRateLimitError,
|
|
359
|
+
} from '@outfitcanvas/fitview-sdk';
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const result = await client.generateFitView({
|
|
363
|
+
person_image: 'invalid-url',
|
|
364
|
+
garment_image: 'https://example.com/garment.jpg',
|
|
365
|
+
});
|
|
366
|
+
} catch (error) {
|
|
367
|
+
if (error instanceof FitViewValidationError) {
|
|
368
|
+
console.error('Validation error:', error.validationErrors);
|
|
369
|
+
} else if (error instanceof FitViewInsufficientCreditsError) {
|
|
370
|
+
console.error(`Need ${error.required} credits, have ${error.available}`);
|
|
371
|
+
} else if (error instanceof FitViewRateLimitError) {
|
|
372
|
+
console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
|
|
373
|
+
} else {
|
|
374
|
+
console.error('Unexpected error:', error);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Webhook Verification
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
import express from 'express';
|
|
383
|
+
|
|
384
|
+
const app = express();
|
|
385
|
+
|
|
386
|
+
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
|
|
387
|
+
const signature = req.headers['x-fitview-signature'] as string;
|
|
388
|
+
const secret = process.env.WEBHOOK_SECRET!;
|
|
389
|
+
|
|
390
|
+
const isValid = client.verifyWebhook(
|
|
391
|
+
req.body,
|
|
392
|
+
signature,
|
|
393
|
+
secret
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
if (!isValid) {
|
|
397
|
+
return res.status(401).send('Invalid signature');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const payload = JSON.parse(req.body.toString());
|
|
401
|
+
// Process webhook...
|
|
402
|
+
|
|
403
|
+
res.status(200).send('OK');
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Custom Timeout, Retries, and X-API-Key
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
const client = new FitViewClient({
|
|
411
|
+
apiKey: process.env.FITVIEW_API_KEY!,
|
|
412
|
+
timeout: 60000, // 1 minute timeout
|
|
413
|
+
maxRetries: 3, // Retry 429 and 5xx (default)
|
|
414
|
+
retryDelay: 1000, // Base delay before retry (exponential backoff)
|
|
415
|
+
authMethod: 'apiKey', // Use X-API-Key header instead of Bearer
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// With custom wait options (consult support before changing initialDelay/interval)
|
|
419
|
+
const result = await client.waitForFitView('fv_abc123', {
|
|
420
|
+
timeout: 600000,
|
|
421
|
+
initialDelay: 10000,
|
|
422
|
+
interval: 5000,
|
|
423
|
+
onProgress: (result) => {
|
|
424
|
+
console.log(`Progress: ${result.status}`);
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Listing Available Models
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
const models = await client.listModels();
|
|
433
|
+
|
|
434
|
+
console.log('Available models:');
|
|
435
|
+
models.forEach(model => {
|
|
436
|
+
console.log(`
|
|
437
|
+
${model.displayName} (${model.id})
|
|
438
|
+
Cost: ${model.creditCost} credits
|
|
439
|
+
Estimated latency: ${model.estimatedLatencyMs}ms
|
|
440
|
+
`);
|
|
441
|
+
});
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Types
|
|
445
|
+
|
|
446
|
+
### FitViewRequest
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
interface FitViewRequest {
|
|
450
|
+
person_image: string | ImageInput;
|
|
451
|
+
garment_image: string | ImageInput;
|
|
452
|
+
model?: OutfitCanvasModel;
|
|
453
|
+
quality?: 'fast' | 'balanced' | 'high';
|
|
454
|
+
use_case?: 'casual' | 'fashion' | 'formal';
|
|
455
|
+
user_id?: string;
|
|
456
|
+
session_id?: string;
|
|
457
|
+
product_id?: string;
|
|
458
|
+
idempotency_key?: string;
|
|
459
|
+
webhook_url?: string;
|
|
460
|
+
context?: UserContext;
|
|
461
|
+
metadata?: Record<string, any>;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### OutfitCanvasModel
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
enum OutfitCanvasModel {
|
|
469
|
+
CanvasPro = 'canvas-pro',
|
|
470
|
+
CanvasProFast = 'canvas-pro-fast',
|
|
471
|
+
CanvasStandard = 'canvas-standard',
|
|
472
|
+
CanvasPlus = 'canvas-plus',
|
|
473
|
+
CanvasExpress = 'canvas-express',
|
|
474
|
+
CanvasInstant = 'canvas-instant',
|
|
475
|
+
CanvasFashion = 'canvas-fashion',
|
|
476
|
+
CanvasCasual = 'canvas-casual',
|
|
477
|
+
CanvasFormal = 'canvas-formal',
|
|
478
|
+
Canvas4K = 'canvas-4k',
|
|
479
|
+
CanvasVideo = 'canvas-video',
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Image validation and optimization
|
|
484
|
+
|
|
485
|
+
To reduce unnecessary backend compute, the SDK can validate and optionally optimize images on the client:
|
|
486
|
+
|
|
487
|
+
- **Validation (default: on)**
|
|
488
|
+
Before each request, the SDK checks that base64 images are valid, within the 20MB limit, and in a supported format (JPEG, PNG, WebP, HEIC/HEIF). Invalid inputs throw `FitViewValidationError` immediately, so you get clear errors without calling the API. Set `validateInputImages: false` in the client to disable.
|
|
489
|
+
|
|
490
|
+
- **Optimization (default: off)**
|
|
491
|
+
With `optimizeImages: true`, base64 images are resized (default max 2048px) and recompressed (e.g. JPEG 85%) on the client when [sharp](https://www.npmjs.com/package/sharp) is installed. This sends smaller payloads and can reduce backend processing. URL inputs are not optimized. Install sharp optionally: `npm install sharp`.
|
|
492
|
+
|
|
493
|
+
**Best practices**
|
|
494
|
+
|
|
495
|
+
- Pre-optimize images when possible: max dimension ~2048px, under ~5MB, JPEG/PNG/WebP. The API accepts up to 20MB and the same formats; smaller, well-formatted images are faster and cheaper to process.
|
|
496
|
+
- Use validation (default) to fail fast on bad input.
|
|
497
|
+
- Enable `optimizeImages` in Node.js if you often send large base64 images and have sharp installed.
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import { FitViewClient, validateBase64Image, optimizeBase64Image } from '@outfitcanvas/fitview-sdk';
|
|
501
|
+
|
|
502
|
+
// Optional: validate or optimize a single image before building the request
|
|
503
|
+
const result = validateBase64Image('data:image/jpeg;base64,...');
|
|
504
|
+
if (!result.valid) {
|
|
505
|
+
console.error(result.message);
|
|
506
|
+
}
|
|
507
|
+
const optimized = await optimizeBase64Image('data:image/jpeg;base64,...', { maxDimension: 2048, jpegQuality: 85 });
|
|
508
|
+
|
|
509
|
+
// Client with validation (default) and optional optimization
|
|
510
|
+
const client = new FitViewClient({
|
|
511
|
+
apiKey: process.env.FITVIEW_API_KEY!,
|
|
512
|
+
validateInputImages: true,
|
|
513
|
+
optimizeImages: true,
|
|
514
|
+
optimizeImageOptions: { maxDimension: 2048, jpegQuality: 85 },
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Error Handling
|
|
519
|
+
|
|
520
|
+
The SDK provides specific error classes for different scenarios:
|
|
521
|
+
|
|
522
|
+
- `FitViewError` - Base error class
|
|
523
|
+
- `FitViewAPIError` - API errors (4xx, 5xx)
|
|
524
|
+
- `FitViewValidationError` - Validation errors (400)
|
|
525
|
+
- `FitViewAuthenticationError` - Authentication errors (401)
|
|
526
|
+
- `FitViewInsufficientCreditsError` - Insufficient credits (402)
|
|
527
|
+
- `FitViewRateLimitError` - Rate limit errors (429)
|
|
528
|
+
- `FitViewTimeoutError` - Timeout errors
|
|
529
|
+
- `FitViewNetworkError` - Network errors
|
|
530
|
+
|
|
531
|
+
## Requirements
|
|
532
|
+
|
|
533
|
+
- Node.js 18.0.0 or higher
|
|
534
|
+
- TypeScript 5.0.0 or higher (for TypeScript projects)
|
|
535
|
+
|
|
536
|
+
## Publishing (maintainers)
|
|
537
|
+
|
|
538
|
+
To publish to npm via GitHub Actions, push a tag matching `sdk-ts-v*` (e.g. `sdk-ts-v1.0.1`). Ensure the repository secret **NPM_TOKEN** is set (npm Automation or Classic token with publish permission). The workflow runs build, type-check, and `npm publish --access public`.
|
|
539
|
+
|
|
540
|
+
## License
|
|
541
|
+
|
|
542
|
+
MIT
|
|
543
|
+
|
|
544
|
+
## Support
|
|
545
|
+
|
|
546
|
+
For issues and questions:
|
|
547
|
+
- Documentation: https://docs.fitview.com
|
|
548
|
+
- Email: support@fitview.com
|
|
549
|
+
|