@meterapp/car-image-sdk 1.4.0 → 1.6.0

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/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # @meterapp/car-image-sdk
2
2
 
3
- TypeScript SDK for the **Car Image API** by Meter — studio-quality, transparent-background renders of any vehicle in the open [`@meterapp/vehicle-db`](https://github.com/MeterApp/vehicle-db) catalog (1,599 makes, 44,254 models, model years 1990–2027).
3
+ TypeScript SDK for the **Car Image API** by Meter — studio-quality, transparent-background renders of any vehicle in the open [`@meterapp/vehicle-db`](https://github.com/MeterApp/vehicle-db) catalog (1,599 makes, 44,254 models, model years 1990–2027), in any paint color; plus stable vehicle ids, free VIN decoding and textured 3D models.
4
4
 
5
5
  - Zero runtime dependencies. Works in Node 20+, Vercel/Cloudflare edge runtimes, Deno, Bun and browsers.
6
6
  - Typed parameters and responses for every endpoint.
7
7
  - `CarImageError` carries `status`, `title`, `detail`, `requestId` and the raw RFC 9457 problem.
8
8
  - Automatic retries with full jitter on `429`/`503` (honoring `Retry-After`); never retries `402`.
9
- - Signed-URL creation carries an `Idempotency-Key`, so a retried `POST` replays instead of billing twice.
9
+ - Signed-URL and 3D-model creation carry an `Idempotency-Key`, so a retried `POST` replays instead of billing twice.
10
10
  - Shared MCP tool definitions (`@meterapp/car-image-sdk/mcp`) used by both the stdio and the remote MCP servers.
11
11
 
12
12
  ```bash
@@ -15,7 +15,7 @@ npm install @meterapp/car-image-sdk
15
15
 
16
16
  ## Pricing in one line
17
17
 
18
- Every delivered image costs **1 credit**, cold or CDN hit. **$1 = 1,000 credits**, every account starts with **100 free credits**, there is no subscription. Signed delivery URLs cost 1 credit when created; loading them is free.
18
+ Every delivered image costs **1 credit**, cold or CDN hit. **$1 = 1,000 credits**, every account starts with **100 free credits**, there is no subscription. Signed delivery URLs cost 1 credit when created; loading them is free. A 3D model costs **1,000 credits** ($1.00) when requested; VIN decoding, catalog lookups, polling and downloads are free.
19
19
 
20
20
  Get a key at <https://carimage.dev/dashboard?ref=npm-sdk> or run `npx @meterapp/car-image login`.
21
21
 
@@ -32,7 +32,7 @@ const image = await client.getImage({
32
32
  model: "911",
33
33
  year: 2024,
34
34
  view: "front-3-4", // front | front-3-4 | side | side-right | rear | rear-3-4
35
- color: "red", // 15 presets: white black gray silver blue red green brown beige tan orange yellow gold burgundy purple
35
+ color: "red", // 15 presets (white black gray silver blue red green brown beige tan orange yellow gold burgundy purple) or any hex: "#1a2b3c"
36
36
  size: "medium", // thumb=256 small=512 medium=768 large=1024 — or width/height, 1–1024 px each
37
37
  format: "png", // png | webp | jpg | auto (auto negotiates WebP or PNG from the Accept header)
38
38
  });
@@ -43,9 +43,75 @@ console.log(image.source, image.creditsCharged, image.creditsRemaining, image.re
43
43
 
44
44
  `apiKey` and `baseUrl` fall back to `CAR_IMAGE_API_KEY` / `CAR_IMAGE_API_URL` when running on a server.
45
45
 
46
+ ## Any paint color
47
+
48
+ `color` is one of the fifteen presets or any hex paint — `#1a2b3c`, `1a2b3c`, `#abc` or `abc`. Every image call takes it (`getImage`, `getImageUrl`, `createImageUrls`, `feedback`) and so does `create3dModel`. The SDK spells a hex bare in query strings (`color=1a2b3c`) and sends it as written in JSON bodies; responses echo it as `#1a2b3c`. A hex that matches a preset's own swatch is that preset. The `Color` type keeps the preset names in autocomplete while accepting any string; `isColor(value)` tells the two apart from anything else, and `COLORS` is still the list of presets.
49
+
50
+ ```ts
51
+ await client.getImage({ make: "Porsche", model: "911", year: 2024, color: "#1a2b3c" });
52
+ ```
53
+
54
+ ## Stable vehicle ids
55
+
56
+ Every catalog vehicle (one make, model and year) has a stable id such as `veh_395yw8tn73ff8`. It never changes across catalog releases, so it is what to store instead of three strings. Every catalog response carries them (`searchVehicles` hits get `id` for the year the query named and `ids` for every year; `vehicles({ year, makeId })` models get `id`; `resolve()` params get `vehicle_id`; `decodeVin()` names one), and every image or 3D call accepts `{ vehicle }` in place of `{ make, model, year }`:
57
+
58
+ ```ts
59
+ const { data } = await client.searchVehicles("2024 porsche 911");
60
+ const id = data.results[0].id; // "veh_395yw8tn73ff8"
61
+ await client.getImage({ vehicle: id, view: "side" }); // same image as make/model/year
62
+ await client.vehicle(id); // { make, model, year, years, image_path, images } — public, no key
63
+ ```
64
+
65
+ `ImageParams` is `{ make, model, year } | { vehicle }` plus the options, so TypeScript refuses both at once; at runtime whichever you pass is sent and the server answers `400` to both. Responses echo the id as `vehicle.vehicle_id` (null when the catalog cannot name one).
66
+
67
+ ## VIN decoding
68
+
69
+ `decodeVin(vin, { year? })` decodes a full 17-character VIN, or a partial one of at least 5 characters with `*` for unknown positions, with NHTSA vPIC data — free, key required. `data.vehicle` is the catalog vehicle it maps to, with the id that renders it, or null when the catalog does not carry the car.
70
+
71
+ ```ts
72
+ const { data } = await client.decodeVin("1HGCM82633A004352");
73
+ data.year, data.make, data.model, data.trim; // 2003, "Honda", "Accord", "EX-V6"
74
+ data.engine, data.transmission, data.plant; // typed sub-objects; unknown values are null
75
+ data.attributes; // every decoded vPIC variable, verbatim
76
+ if (data.vehicle) await client.getImage({ vehicle: data.vehicle.id });
77
+ ```
78
+
79
+ `valid`, `errors` and `suggested_vin` carry the decoder's diagnostics (a bad check digit, a model-year mismatch with the `year` hint). A VIN NHTSA has no record of is a `404`; when neither decoder answers it is a `502`.
80
+
81
+ ## 3D models
82
+
83
+ `create3dModel` builds a textured 3D model — GLB, USDZ, FBX, a PNG thumbnail and `glb_web`, a browser build a tenth the size — of any catalog vehicle in any paint color, from the same renders the images use. It costs 1,000 credits at creation whether the model is cached, in progress or new, and nothing for a vehicle and color your account already owns (`billing.already_owned`; a failed model is refunded); polling, downloads and publishing are free. A model takes one to seven minutes.
84
+
85
+ ```ts
86
+ const { data: model, billing } = await client.create3dModel(
87
+ { make: "Toyota", model: "Camry", year: 2025 }, // or { vehicle: "veh_…" }
88
+ { color: "#1a2b3c", webhookUrl: "https://example.com/hooks/3d", webhookSecret: process.env.HOOK_SECRET, publish: true }
89
+ );
90
+ model.status; // "queued" | "processing" | "ready" (cached) | "failed"
91
+ billing.already_owned; // true (and credits_charged 0) when you already owned this vehicle in this color
92
+ model.public?.embed.html; // '<script src="https://carimage.dev/embed/3d.js" async></script>\n<car-3d model="m3d_…"></car-3d>'
93
+
94
+ const ready = await client.wait3dModel(model.id, { // polls GET /api/v1/3d/{id}
95
+ intervalMs: 5_000, // default 5 s
96
+ timeoutMs: 600_000, // default 10 min; throws a CarImageError with status 408
97
+ onProgress: (state) => console.log(state.status, state.progress, state.stage, state.estimated_seconds_remaining),
98
+ });
99
+ if (ready.status === "ready") {
100
+ const glb = await client.download3dModel(ready.id, "glb"); // { bytes, contentType } — also "usdz" | "fbx" | "thumbnail"
101
+ await writeFile("camry.glb", glb.bytes);
102
+ }
103
+
104
+ await client.get3dModel(model.id); // one poll
105
+ await client.list3dModels({ limit: 20 }); // your recent requests + pricing.credits_per_model
106
+ await client.publish3dModel(model.id); // data.public: key-free URLs (glb, usdz, poster) and the embed; works before ready
107
+ await client.unpublish3dModel(model.id); // the links stop working within the hour
108
+ ```
109
+
110
+ `ready.files.<kind>.url` are API URLs that answer `302` to a signed download valid for an hour; `download3dModel` follows that redirect itself and never sends the API key to the storage host. A published model's `data.public.files` need no key at all: `model.glb` (the browser build), `model.usdz` and `poster.png` redirect to immutable copies on the CDN, and `data.public.embed.html` is a two-line `<car-3d>` embed that shows the poster at once, loads Google's `<model-viewer>` when it scrolls into view and takes `view`, `spin`, `backdrop`, `ar`, `static`, `no-zoom` and `alt` (`fetch` only attaches it to the client's own origin). With `webhookUrl` (https, public host) the server POSTs `{ event: "3d_model.ready" | "3d_model.failed", data, sent_at }` once the model settles, signed as `X-CarImage-Signature: sha256=<hex HMAC of the raw body>` when `webhookSecret` is set. `create3dModel` sends an `Idempotency-Key` like `createImageUrls` (pass `idempotencyKey` to make retries across processes safe). A `503` means 3D models are switched off or at the day's capacity; nothing is charged and `retryAfterSeconds` says when to try again.
111
+
46
112
  ## Sizing, backgrounds and trimming
47
113
 
48
- Six views, fifteen colors, PNG/WebP/JPG up to 1024 px — delivered at whatever box your layout needs.
114
+ Six views, any paint color, PNG/WebP/JPG up to 1024 px — delivered at whatever box your layout needs.
49
115
 
50
116
  ```ts
51
117
  await client.getImage({
@@ -61,6 +127,7 @@ await client.getImage({
61
127
 
62
128
  | Parameter | Values | What it does |
63
129
  | --- | --- | --- |
130
+ | `color` | one of `COLORS` · hex as `#rrggbb`, `rrggbb`, `#rgb` or `rgb` | The paint. Defaults to `silver`; echoed back as the preset name or `#rrggbb`. |
64
131
  | `size` | `thumb` 256 · `small` 512 · `medium` 768 · `large` 1024 | Square presets; shorthand for `width`. |
65
132
  | `width`, `height` | 1–1024 px each | One dimension keeps the aspect ratio. Both together give exactly `width`×`height` (see `fit`). Nothing is upscaled past 1024. |
66
133
  | `fit` | `contain` (default) · `cover` · `inside` | Only matters with both dimensions. `contain` returns exactly the box with the whole car and transparent (or `background`) padding; `cover` fills the box and centre-crops; `inside` keeps the car within the box and may return a smaller image — the old behaviour. |
@@ -69,7 +136,7 @@ await client.getImage({
69
136
  | `padding` | 0–50 | Margin around a trimmed car, as a percentage of its longer side. Only with `trim`. |
70
137
  | `format` | `png` (default) · `webp` · `jpg` · `auto` | `auto` picks WebP for clients whose `Accept` header lists it and PNG otherwise (`Vary: Accept`); a signed URL created with `auto` negotiates on every load. |
71
138
 
72
- The same parameters apply to `getImageUrl` and to every entry of `createImageUrls`; the returned `vehicle` echoes `fit`, `background`, `trim` and `padding` back. `assertImageParams` (and therefore every call) rejects a `padding` without `trim` and dimensions outside 1–1024 before anything is sent.
139
+ The same parameters apply to `getImageUrl` and to every entry of `createImageUrls`; the returned `vehicle` echoes `vehicle_id`, `color`, `fit`, `background`, `trim` and `padding` back. `assertImageParams` (and therefore every call) rejects a `padding` without `trim`, dimensions outside 1–1024, a color that is neither a preset nor a hex, and a request that names neither a vehicle id nor all of make, model and year, before anything is sent.
73
140
 
74
141
  ## Next.js route handler
75
142
 
@@ -122,13 +189,17 @@ await client.createImageUrls(images, { ttlSeconds: 604_800, idempotencyKey: `ord
122
189
 
123
190
  ```ts
124
191
  await client.getImageUrl(params); // 1 credit, returns a signed URL instead of bytes
125
- await client.options(); // views, colors, sizes, fits, backgrounds, trim limits, formats, pricing, catalog coverage
126
- await client.resolve("red 2024 porsche 911 side view"); // free text -> { make, model, year, view, color } + candidates + confidence high|medium|low
192
+ await client.options(); // views, colors (and how hex paint is spelled), sizes, fits, backgrounds, trim limits, formats, pricing, catalog coverage, vehicle ids
193
+ await client.resolve("red 2024 porsche 911 side view"); // free text -> { vehicle_id, make, model, year, view, color } + candidates + confidence high|medium|low
127
194
  await client.vehicles(); // years
128
195
  await client.vehicles({ year: 2024 }); // makes for a year
129
- await client.vehicles({ year: 2024, makeId: 7 }); // models for a year + make
130
- await client.searchVehicles("porshe 911", { limit: 5 }); // fuzzy catalog search
131
- await client.feedback({ requestId, verdict: "good" }); // or { make, model, year, rating: 4, reason }
196
+ await client.vehicles({ year: 2024, makeId: 7 }); // models for a year + make, each with its vehicle id
197
+ await client.vehicles({ make: "porsche", model: "911" }); // a model's years, each with its vehicle id
198
+ await client.vehicle("veh_395yw8tn73ff8"); // one vehicle by stable id (public)
199
+ await client.searchVehicles("porshe 911", { limit: 5 }); // fuzzy catalog search; model hits carry ids
200
+ await client.decodeVin("1HGCM82633A004352"); // free VIN decode + the catalog vehicle it maps to
201
+ await client.create3dModel({ vehicle: id }, { color }); // 1,000 credits; then wait3dModel, get3dModel, list3dModels, download3dModel
202
+ await client.feedback({ requestId, verdict: "good" }); // or { make, model, year, rating: 4, reason } or { vehicle, verdict }
132
203
  await client.listRequests({ sort: "top" }); // vehicle and feature requests; see "Requests & feedback"
133
204
  await client.account(); // credits, auto-reload, usage_30d, key scopes
134
205
  await client.createCheckout(5000); // hosted Stripe Checkout URL (scope billing:write)
@@ -173,12 +244,12 @@ try {
173
244
  await client.getImage({ make: "Porsche", model: "911", year: 2024 });
174
245
  } catch (error) {
175
246
  if (error instanceof CarImageError) {
176
- error.status; // 400 | 401 | 402 | 404 | 409 | 422 | 429 | 502 | 0 (network)
247
+ error.status; // 400 | 401 | 402 | 404 | 409 | 422 | 429 | 502 | 503 | 408 (wait3dModel timeout) | 0 (network)
177
248
  error.title; // "Insufficient credits"
178
249
  error.detail; // "This request needs 1 credit; balance is 0"
179
250
  error.requestId; // "0d1e…" — quote this when contacting support
180
251
  error.problem; // the raw application/problem+json body (402 includes balance, required_credits)
181
- error.retryAfterSeconds; // set on 429, and on a 409 Idempotency-Key conflict
252
+ error.retryAfterSeconds; // set on 429, on a 409 (Idempotency-Key conflict, or a 3D file requested before the model is ready) and on a 503
182
253
  if (error.isInsufficientCredits) {
183
254
  // Send a human to https://carimage.dev/dashboard?ref=npm-sdk#billing — never buy credits automatically.
184
255
  }
@@ -199,13 +270,13 @@ import {
199
270
  } from "@meterapp/car-image-sdk/mcp";
200
271
  ```
201
272
 
202
- Tools come in two sets. `MCP_TOOLSETS.core` (`DEFAULT_MCP_TOOLSET`, `"core"`) is the eight an agent needs to find, render and embed a car: `get_car_image`, `create_car_image_urls`, `search_vehicles`, `resolve_vehicle`, `list_image_options`, `get_account`, `rate_image`, `describe_api`. `MCP_TOOLSETS.all` adds the request board and the two "about you" tools: `list_requests`, `request_vehicle`, `request_feature`, `get_request`, `upvote_request`, `comment_on_request`, `share_building`, `share_referral`. The hosted server exposes `core` at `https://carimage.dev/api/mcp` and `all` at `https://carimage.dev/api/mcp?toolset=all`; the stdio server takes `car-image mcp --toolset all`. `mcpInstructions(toolset)` returns the matching server instructions (`MCP_INSTRUCTIONS` is the `all` text) and `isMcpToolset(value)` validates a name from a URL or a flag.
273
+ Tools come in two sets. `MCP_TOOLSETS.core` (`DEFAULT_MCP_TOOLSET`, `"core"`) is the eleven an agent needs to find, render, embed and model a car: `get_car_image`, `create_car_image_urls`, `search_vehicles`, `resolve_vehicle`, `decode_vin`, `list_image_options`, `get_account`, `rate_image`, `create_3d_model`, `get_3d_model`, `describe_api`. `MCP_TOOLSETS.all` adds the request board and the two "about you" tools: `list_requests`, `request_vehicle`, `request_feature`, `get_request`, `upvote_request`, `comment_on_request`, `share_building`, `share_referral`. The hosted server exposes `core` at `https://carimage.dev/api/mcp` and `all` at `https://carimage.dev/api/mcp?toolset=all`; the stdio server takes `car-image mcp --toolset all`. `mcpInstructions(toolset)` returns the matching server instructions (`MCP_INSTRUCTIONS` is the `all` text) and `isMcpToolset(value)` validates a name from a URL or a flag.
203
274
 
204
- `get_car_image` and each `create_car_image_urls` entry take `fit`, `background`, `trim` and `padding` alongside the vehicle, view, color, size and format (`auto` included; `inlineImageFormat` is what both servers deliver an inline image as for it: png); `create_car_image_urls` also honors `renew` and `renew_days` and takes `idempotency_key` (`IDEMPOTENCY_KEY_PATTERN`), which the stdio server forwards as the `Idempotency-Key` header. Every tool but `describe_api`, whose result is the reference prose itself, advertises an `outputSchema` and returns `structuredContent` that matches it, so a host can hand the model typed data instead of re-parsing the text block. The schemas allow unknown fields: the payloads mirror the REST API and gain fields as it grows.
275
+ `get_car_image`, each `create_car_image_urls` entry, `rate_image` and `create_3d_model` name the vehicle by make, model and year or by `vehicle` (a stable id) and take `color` as a preset or a hex; the image tools also take `fit`, `background`, `trim` and `padding` alongside view, size and format (`auto` included; `inlineImageFormat` is what both servers deliver an inline image as for it: png), and `create_car_image_urls` honors `renew` and `renew_days` and takes `idempotency_key` (`IDEMPOTENCY_KEY_PATTERN`), which the stdio server forwards as the `Idempotency-Key` header. `create_3d_model` costs 1,000 credits, and its description and the server instructions tell the agent to confirm with the human first. Every tool but `describe_api`, whose result is the reference prose itself, advertises an `outputSchema` and returns `structuredContent` that matches it, so a host can hand the model typed data instead of re-parsing the text block. The schemas allow unknown fields: the payloads mirror the REST API and gain fields as it grows.
205
276
 
206
277
  ## Types
207
278
 
208
- `View`, `Color`, `Size`, `Format`, `RequestFormat`, `Fit`, `ImageParams`, `ImageResult`, `CreateImageUrlsOptions`, `CreateImageUrlsResponse`, `OptionsResponse`, `FitOption`, `ResolveResponse`, `ResolveConfidence` (`"high" | "medium" | "low"`), `VehicleSearchResponse`, `AccountResponse`, `RequestRecord`, `RequestComment`, `CreateRequestInput`, `ReferralSource`, … plus the constant arrays `VIEWS`, `COLORS`, `SIZES`, `FORMATS`, `REQUEST_FORMATS` (the formats plus `auto`), `FITS`, `REQUEST_STATUSES`, `REFERRAL_SOURCES` and the limits `MAX_DIMENSION` (1024) and `MAX_PADDING_PERCENT` (50).
279
+ `View`, `Color` (a `PresetColor` or any hex string), `Size`, `Format`, `RequestFormat`, `Fit`, `VehicleId`, `VehicleSelector`, `ImageParams`, `ImageOptions`, `ImageResult`, `CreateImageUrlsOptions`, `CreateImageUrlsResponse`, `OptionsResponse`, `FitOption`, `ResolveResponse`, `ResolveConfidence` (`"high" | "medium" | "low"`), `VehicleSearchResponse`, `VehicleResponse`, `VinResult`, `VinResponse`, `Model3d`, `Model3dFileKind`, `Model3dStatus`, `CreateModel3dOptions`, `CreateModel3dResponse`, `Model3dListResponse`, `Model3dFileResult`, `WaitModel3dOptions`, `AccountResponse`, `RequestRecord`, `RequestComment`, `CreateRequestInput`, `ReferralSource`, … plus the constant arrays `VIEWS`, `COLORS` (the presets), `SIZES`, `FORMATS`, `REQUEST_FORMATS` (the formats plus `auto`), `FITS`, `MODEL_3D_FILE_KINDS`, `REQUEST_STATUSES`, `REFERRAL_SOURCES`, the patterns `HEX_COLOR_PATTERN` and `VEHICLE_ID_PATTERN`, the helpers `isColor`, `isPresetColor`, `isHexColor`, `colorParam` and `vehicleRequestBody`, and the limits `MAX_DIMENSION` (1024), `MAX_PADDING_PERCENT` (50) and `CREDITS_PER_3D_MODEL` (1000).
209
280
 
210
281
  ## License
211
282
 
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AccountResponse, type BuildingResponse, type CheckoutResponse, type CreateImageUrlsOptions, type CreateImageUrlsResponse, type CreateRequestInput, type CreateRequestResponse, type DeviceCodeResponse, type DeviceTokenResult, type FeedbackInput, type FeedbackResponse, type FetchLike, type HealthResponse, type ImageParams, type ImageResult, type ImageUrlResponse, type ListRequestsParams, type OpenApiDocument, type OptionsResponse, type PortalResponse, type ReferralResponse, type ReferralSource, type RequestCommentResponse, type RequestCommentsResponse, type RequestDetailResponse, type RequestListResponse, type RequestResponse, type ResolveResponse, type TelemetryEvent, type VehicleMakesResponse, type VehicleModelsResponse, type VehicleSearchResponse, type VehicleYearsResponse } from "./types.js";
1
+ import { type AccountResponse, type BuildingResponse, type CheckoutResponse, type CreateImageUrlsOptions, type CreateImageUrlsResponse, type CreateModel3dOptions, type CreateModel3dResponse, type CreateRequestInput, type CreateRequestResponse, type DecodeVinOptions, type DeviceCodeResponse, type DeviceTokenResult, type FeedbackInput, type FeedbackResponse, type FetchLike, type HealthResponse, type ImageParams, type ImageResult, type ImageUrlResponse, type ListRequestsParams, type Model3d, type Model3dFileKind, type Model3dFileResult, type Model3dListResponse, type Model3dResponse, type OpenApiDocument, type OptionsResponse, type PortalResponse, type ReferralResponse, type ReferralSource, type RequestCommentResponse, type RequestCommentsResponse, type RequestDetailResponse, type RequestListResponse, type RequestResponse, type ResolveResponse, type TelemetryEvent, type VehicleMakeModelsResponse, type VehicleMakesResponse, type VehicleModelLookupResponse, type VehicleModelsResponse, type VehicleResponse, type VehicleSearchResponse, type VehicleSelector, type VehicleYearsResponse, type VinResponse, type WaitModel3dOptions } from "./types.js";
2
2
  export interface CarImageClientOptions {
3
3
  /** `cimg_…` key. Falls back to `process.env.CAR_IMAGE_API_KEY` when running on a server. */
4
4
  apiKey?: string;
@@ -62,7 +62,11 @@ export declare class CarImageClient {
62
62
  options(options?: RequestOptions): Promise<OptionsResponse>;
63
63
  /** POST /api/v1/images/resolve — free; turns "red 2024 porsche 911 side view" into parameters. */
64
64
  resolve(query: string, options?: RequestOptions): Promise<ResolveResponse>;
65
- /** GET /api/v1/vehicles — public. No filter: years; `year`: makes; `year`+`makeId`: models. */
65
+ /**
66
+ * GET /api/v1/vehicles — public. No filter: years; `year`: makes; `year`+`makeId`:
67
+ * models (each with its vehicle id); `make` slug: that make's models; `make`+`model`
68
+ * slugs: the model's years, each with its vehicle id.
69
+ */
66
70
  vehicles(filter?: Record<string, never>, options?: RequestOptions): Promise<VehicleYearsResponse>;
67
71
  vehicles(filter: {
68
72
  year: number;
@@ -71,12 +75,67 @@ export declare class CarImageClient {
71
75
  year: number;
72
76
  makeId: number;
73
77
  }, options?: RequestOptions): Promise<VehicleModelsResponse>;
78
+ vehicles(filter: {
79
+ make: string;
80
+ year?: number;
81
+ }, options?: RequestOptions): Promise<VehicleMakeModelsResponse>;
82
+ vehicles(filter: {
83
+ make: string;
84
+ model: string;
85
+ year?: number;
86
+ }, options?: RequestOptions): Promise<VehicleModelLookupResponse>;
87
+ /** GET /api/v1/vehicles/{id} — public; the vehicle a stable id names, its years and the image URLs that render it. */
88
+ vehicle(id: string, options?: RequestOptions): Promise<VehicleResponse>;
89
+ /**
90
+ * GET /api/v1/vin/{vin} — free; decodes a full 17-character VIN, or a partial
91
+ * one of at least 5 characters with `*` for unknown positions, and names the
92
+ * catalog vehicle it maps to (`data.vehicle`, with the id that renders it).
93
+ */
94
+ decodeVin(vin: string, options?: DecodeVinOptions & RequestOptions): Promise<VinResponse>;
95
+ /**
96
+ * POST /api/v1/3d — a textured 3D model (GLB, USDZ, FBX and a thumbnail) of one
97
+ * vehicle in one paint color. Costs `credits_per_model` credits (1,000 at
98
+ * launch) at creation, whether the model is cached, in progress or new; a
99
+ * failed model is refunded, and polling and downloads are free. Resolves at
100
+ * once with `status` queued or processing (202), or ready when it was cached
101
+ * (200); `wait3dModel` polls until it settles. Every call carries an
102
+ * `Idempotency-Key`, like `createImageUrls`.
103
+ */
104
+ create3dModel(vehicle: VehicleSelector, options?: CreateModel3dOptions & RequestOptions): Promise<CreateModel3dResponse>;
105
+ /** GET /api/v1/3d/{id} — free; status, progress and, once ready, the files of one 3D request. */
106
+ get3dModel(id: string, options?: RequestOptions): Promise<Model3dResponse>;
107
+ /**
108
+ * POST /api/v1/3d/{id}/publish — free; gives one of your 3D requests
109
+ * key-free URLs and a two-line embed (`data.public`). The URLs are stable
110
+ * from this moment; the files follow when the model is ready. Idempotent.
111
+ */
112
+ publish3dModel(id: string, options?: RequestOptions): Promise<Model3dResponse>;
113
+ /** DELETE /api/v1/3d/{id}/publish — free; the public URLs stop working within the hour and `data.public` is null again. Idempotent. */
114
+ unpublish3dModel(id: string, options?: RequestOptions): Promise<Model3dResponse>;
115
+ /** GET /api/v1/3d — free; the caller's recent 3D requests, newest first (`limit` 1–50, default 20). */
116
+ list3dModels(options?: {
117
+ limit?: number;
118
+ } & RequestOptions): Promise<Model3dListResponse>;
119
+ /**
120
+ * GET /api/v1/3d/{id}/files/{kind} — free; the bytes of one file of a ready
121
+ * model. The route answers 302 to a short-lived signed URL on the storage
122
+ * host, which is followed here by hand so the API key never travels there:
123
+ * `fetch` attaches the key only to the client's own origin.
124
+ */
125
+ download3dModel(id: string, kind: Model3dFileKind, options?: RequestOptions): Promise<Model3dFileResult>;
126
+ /**
127
+ * Polls GET /api/v1/3d/{id} until the model is `ready` or `failed` and
128
+ * returns it either way (check `status`; a failed model carries `error` and
129
+ * was refunded). Throws a `CarImageError` with status 408 when `timeoutMs`
130
+ * (default 10 minutes) passes first; `intervalMs` defaults to 5 seconds.
131
+ */
132
+ wait3dModel(id: string, options?: WaitModel3dOptions & RequestOptions): Promise<Model3d>;
74
133
  /** GET /api/v1/vehicles?q= — public fuzzy search across makes and models. */
75
134
  searchVehicles(query: string, options?: {
76
135
  limit?: number;
77
136
  year?: number;
78
137
  } & RequestOptions): Promise<VehicleSearchResponse>;
79
- /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle. */
138
+ /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle (make, model and year, or a vehicle id). */
80
139
  feedback(input: FeedbackInput, options?: RequestOptions): Promise<FeedbackResponse>;
81
140
  /** GET /api/v1/requests — public board of vehicle and feature requests; `viewer.voted` is filled in when a key is sent. */
82
141
  listRequests(params?: ListRequestsParams, options?: RequestOptions): Promise<RequestListResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CarImageError } from "./errors.js";
2
- import { imageRequestBody, imageSearchParams, normalizeYear } from "./params.js";
2
+ import { imageRequestBody, imageSearchParams, isColor, normalizeYear, vehicleRequestBody } from "./params.js";
3
3
  import { RETRYABLE_STATUSES, backoffDelayMs, parseRetryAfter } from "./retry.js";
4
- import { DEFAULT_BASE_URL, REFERRAL_SOURCES, } from "./types.js";
4
+ import { COLORS, DEFAULT_BASE_URL, MODEL_3D_FILE_KINDS, REFERRAL_SOURCES, } from "./types.js";
5
5
  import { SDK_VERSION } from "./version.js";
6
6
  function readEnv(name) {
7
7
  const proc = globalThis.process;
@@ -173,10 +173,185 @@ export class CarImageClient {
173
173
  search.set("year", String(normalizeYear(filter.year)));
174
174
  if (filter.makeId !== undefined)
175
175
  search.set("makeId", String(filter.makeId));
176
+ if (filter.make !== undefined)
177
+ search.set("make", filter.make.trim());
178
+ if (filter.model !== undefined)
179
+ search.set("model", filter.model.trim());
176
180
  const query = search.size ? `?${search}` : "";
177
181
  const response = await this.send(`/api/v1/vehicles${query}`, { ...options, auth: "optional" });
178
182
  return this.json(response);
179
183
  }
184
+ /** GET /api/v1/vehicles/{id} — public; the vehicle a stable id names, its years and the image URLs that render it. */
185
+ async vehicle(id, options = {}) {
186
+ if (typeof id !== "string" || !id.trim())
187
+ throw new TypeError("vehicle id is required");
188
+ const response = await this.send(`/api/v1/vehicles/${encodeURIComponent(id.trim())}`, { ...options, auth: "optional" });
189
+ return this.json(response);
190
+ }
191
+ // ------------------------------------------------------------------ VIN
192
+ /**
193
+ * GET /api/v1/vin/{vin} — free; decodes a full 17-character VIN, or a partial
194
+ * one of at least 5 characters with `*` for unknown positions, and names the
195
+ * catalog vehicle it maps to (`data.vehicle`, with the id that renders it).
196
+ */
197
+ async decodeVin(vin, options = {}) {
198
+ if (typeof vin !== "string" || !vin.trim())
199
+ throw new TypeError("vin is required");
200
+ const { year, ...request } = options;
201
+ const search = new URLSearchParams();
202
+ if (year !== undefined)
203
+ search.set("year", String(normalizeYear(year)));
204
+ const query = search.size ? `?${search}` : "";
205
+ const response = await this.send(`/api/v1/vin/${encodeURIComponent(vin.trim())}${query}`, request);
206
+ return this.json(response);
207
+ }
208
+ // ------------------------------------------------------------ 3D models
209
+ /**
210
+ * POST /api/v1/3d — a textured 3D model (GLB, USDZ, FBX and a thumbnail) of one
211
+ * vehicle in one paint color. Costs `credits_per_model` credits (1,000 at
212
+ * launch) at creation, whether the model is cached, in progress or new; a
213
+ * failed model is refunded, and polling and downloads are free. Resolves at
214
+ * once with `status` queued or processing (202), or ready when it was cached
215
+ * (200); `wait3dModel` polls until it settles. Every call carries an
216
+ * `Idempotency-Key`, like `createImageUrls`.
217
+ */
218
+ async create3dModel(vehicle, options = {}) {
219
+ const { color, webhookUrl, webhookSecret, publish, idempotencyKey, ...request } = options;
220
+ const body = vehicleRequestBody(vehicle);
221
+ if (color !== undefined) {
222
+ if (!isColor(color))
223
+ throw new TypeError(`Invalid color "${String(color)}": expected one of ${COLORS.join(", ")}, or a hex paint such as #1a2b3c`);
224
+ body.color = String(color).trim();
225
+ }
226
+ if (webhookUrl !== undefined)
227
+ body.webhook_url = webhookUrl;
228
+ if (webhookSecret !== undefined)
229
+ body.webhook_secret = webhookSecret;
230
+ if (publish !== undefined)
231
+ body.publish = publish;
232
+ const key = idempotencyKey ?? generateIdempotencyKey();
233
+ const headers = { ...request.headers };
234
+ if (key)
235
+ headers["Idempotency-Key"] = key;
236
+ const response = await this.send("/api/v1/3d", {
237
+ ...request,
238
+ headers,
239
+ method: "POST",
240
+ body: JSON.stringify(body),
241
+ });
242
+ return this.json(response);
243
+ }
244
+ /** GET /api/v1/3d/{id} — free; status, progress and, once ready, the files of one 3D request. */
245
+ async get3dModel(id, options = {}) {
246
+ const response = await this.send(`/api/v1/3d/${requestPath(id)}`, options);
247
+ return this.json(response);
248
+ }
249
+ /**
250
+ * POST /api/v1/3d/{id}/publish — free; gives one of your 3D requests
251
+ * key-free URLs and a two-line embed (`data.public`). The URLs are stable
252
+ * from this moment; the files follow when the model is ready. Idempotent.
253
+ */
254
+ async publish3dModel(id, options = {}) {
255
+ const response = await this.send(`/api/v1/3d/${requestPath(id)}/publish`, { ...options, method: "POST" });
256
+ return this.json(response);
257
+ }
258
+ /** DELETE /api/v1/3d/{id}/publish — free; the public URLs stop working within the hour and `data.public` is null again. Idempotent. */
259
+ async unpublish3dModel(id, options = {}) {
260
+ const response = await this.send(`/api/v1/3d/${requestPath(id)}/publish`, { ...options, method: "DELETE" });
261
+ return this.json(response);
262
+ }
263
+ /** GET /api/v1/3d — free; the caller's recent 3D requests, newest first (`limit` 1–50, default 20). */
264
+ async list3dModels(options = {}) {
265
+ const { limit, ...request } = options;
266
+ const search = new URLSearchParams();
267
+ if (limit !== undefined)
268
+ search.set("limit", String(limit));
269
+ const query = search.size ? `?${search}` : "";
270
+ const response = await this.send(`/api/v1/3d${query}`, request);
271
+ return this.json(response);
272
+ }
273
+ /**
274
+ * GET /api/v1/3d/{id}/files/{kind} — free; the bytes of one file of a ready
275
+ * model. The route answers 302 to a short-lived signed URL on the storage
276
+ * host, which is followed here by hand so the API key never travels there:
277
+ * `fetch` attaches the key only to the client's own origin.
278
+ */
279
+ async download3dModel(id, kind, options = {}) {
280
+ if (!MODEL_3D_FILE_KINDS.includes(kind)) {
281
+ throw new RangeError(`kind must be one of: ${MODEL_3D_FILE_KINDS.join(", ")}`);
282
+ }
283
+ const redirect = await this.rawSend(`/api/v1/3d/${requestPath(id)}/files/${kind}`, { ...options, accept: "*/*", redirect: "manual" });
284
+ const requestId = redirect.headers.get("x-request-id");
285
+ const location = redirect.headers.get("location");
286
+ if (redirect.status >= 300 && redirect.status < 400 && location) {
287
+ try {
288
+ await redirect.body?.cancel();
289
+ }
290
+ catch {
291
+ // Nothing to release.
292
+ }
293
+ const target = new URL(location, this.baseUrl).toString();
294
+ const file = await this.fetch(target, { headers: { Accept: "*/*" }, signal: this.signalFor(options) });
295
+ if (!file.ok) {
296
+ throw new CarImageError({
297
+ status: file.status,
298
+ title: "3D file download failed",
299
+ detail: `The storage host answered HTTP ${file.status} for the ${kind} file. Ask for the model again to get a fresh link.`,
300
+ requestId,
301
+ });
302
+ }
303
+ return {
304
+ kind,
305
+ bytes: new Uint8Array(await file.arrayBuffer()),
306
+ contentType: file.headers.get("content-type") ?? redirect.headers.get("x-file-content-type") ?? "application/octet-stream",
307
+ requestId,
308
+ };
309
+ }
310
+ if (redirect.ok) {
311
+ return {
312
+ kind,
313
+ bytes: new Uint8Array(await redirect.arrayBuffer()),
314
+ contentType: redirect.headers.get("content-type") ?? "application/octet-stream",
315
+ requestId,
316
+ };
317
+ }
318
+ if (redirect.status === 0 || redirect.type === "opaqueredirect") {
319
+ throw new CarImageError({
320
+ status: 0,
321
+ title: "Redirect hidden by the runtime",
322
+ detail: `This runtime does not expose redirects; fetch model.files.${kind}.url yourself and follow it without the API key.`,
323
+ requestId,
324
+ });
325
+ }
326
+ throw await CarImageError.fromResponse(redirect);
327
+ }
328
+ /**
329
+ * Polls GET /api/v1/3d/{id} until the model is `ready` or `failed` and
330
+ * returns it either way (check `status`; a failed model carries `error` and
331
+ * was refunded). Throws a `CarImageError` with status 408 when `timeoutMs`
332
+ * (default 10 minutes) passes first; `intervalMs` defaults to 5 seconds.
333
+ */
334
+ async wait3dModel(id, options = {}) {
335
+ const { intervalMs = 5_000, timeoutMs = 600_000, onProgress, ...request } = options;
336
+ const deadline = Date.now() + timeoutMs;
337
+ for (;;) {
338
+ if (request.signal?.aborted)
339
+ throw request.signal.reason ?? new Error("wait3dModel aborted");
340
+ const { data } = await this.get3dModel(id, request);
341
+ onProgress?.(data);
342
+ if (data.status === "ready" || data.status === "failed")
343
+ return data;
344
+ const remaining = deadline - Date.now();
345
+ if (remaining <= 0) {
346
+ throw new CarImageError({
347
+ status: 408,
348
+ title: "Timed out waiting for the 3D model",
349
+ detail: `Model ${id} is still ${data.status} (${data.progress}%) after ${Math.round(timeoutMs / 1000)} s. Keep polling get3dModel, or pass a webhookUrl when creating one.`,
350
+ });
351
+ }
352
+ await this.sleep(Math.min(Math.max(1, intervalMs), remaining));
353
+ }
354
+ }
180
355
  /** GET /api/v1/vehicles?q= — public fuzzy search across makes and models. */
181
356
  async searchVehicles(query, options = {}) {
182
357
  if (typeof query !== "string" || !query.trim())
@@ -191,7 +366,7 @@ export class CarImageClient {
191
366
  return this.json(response);
192
367
  }
193
368
  // ------------------------------------------------------------- feedback
194
- /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle. */
369
+ /** POST /api/v1/feedback — free; rate a delivered image by request id or by vehicle (make, model and year, or a vehicle id). */
195
370
  async feedback(input, options = {}) {
196
371
  if (input.rating === undefined && input.verdict === undefined) {
197
372
  throw new TypeError("feedback requires a rating (1-5) or a verdict (good|bad)");
@@ -200,14 +375,14 @@ export class CarImageClient {
200
375
  if ("requestId" in input && input.requestId) {
201
376
  body.request_id = input.requestId;
202
377
  }
203
- else if ("make" in input) {
378
+ else if ("make" in input || "vehicle" in input) {
204
379
  Object.assign(body, imageRequestBody(input));
205
380
  delete body.width;
206
381
  delete body.height;
207
382
  delete body.format;
208
383
  }
209
384
  else {
210
- throw new TypeError("feedback requires requestId or make/model/year");
385
+ throw new TypeError("feedback requires requestId, a vehicle id, or make/model/year");
211
386
  }
212
387
  if (input.rating !== undefined)
213
388
  body.rating = input.rating;
@@ -505,6 +680,7 @@ export class CarImageClient {
505
680
  headers,
506
681
  body: options.body,
507
682
  signal: this.signalFor(options),
683
+ ...(options.redirect ? { redirect: options.redirect } : {}),
508
684
  });
509
685
  }
510
686
  catch (error) {