@viewrium/sdk 2.0.0 → 2.1.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 +239 -19
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -11
- package/dist/index.d.ts +143 -11
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +24 -4
package/README.md
CHANGED
|
@@ -1,31 +1,251 @@
|
|
|
1
1
|
# @viewrium/sdk
|
|
2
2
|
|
|
3
|
-
The official
|
|
4
|
-
|
|
3
|
+
The official JavaScript/TypeScript SDK for the [Viewrium](https://www.viewrium.com) API.
|
|
4
|
+
|
|
5
|
+
Viewrium is AR and 3D product visualization for furniture retailers: it turns product photos, a
|
|
6
|
+
video, a product page URL or an existing 3D file into catalog images, a GLB model and an
|
|
7
|
+
embeddable 3D/AR viewer. This package is a typed client for the Viewrium `/v1` REST API: create
|
|
8
|
+
products, review and publish them, export images, manage webhooks and API keys, and read usage
|
|
9
|
+
analytics.
|
|
10
|
+
|
|
11
|
+
- Documentation: https://www.viewrium.com/docs
|
|
12
|
+
- SDK reference: https://www.viewrium.com/docs/sdks/javascript
|
|
13
|
+
- Dashboard: https://www.viewrium.com
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @viewrium/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The package ships ESM and CommonJS builds with self-contained type declarations. It runs anywhere
|
|
22
|
+
there is a `fetch`: Node 18+, Bun, Deno, edge runtimes and the browser.
|
|
23
|
+
|
|
24
|
+
## Authentication
|
|
25
|
+
|
|
26
|
+
Every request is authenticated with an API key, sent as `Authorization: Bearer <key>`. The SDK
|
|
27
|
+
sets the header for you.
|
|
28
|
+
|
|
29
|
+
- **Secret keys (`sk_`)** are for servers. They can do everything their scopes allow. Never ship
|
|
30
|
+
one to a browser, a mobile app or a public repository.
|
|
31
|
+
- **Publishable keys (`pk_`)** are safe in a browser but are origin-gated and limited to reading a
|
|
32
|
+
single published product, minting its model ticket and sending analytics beacons. An
|
|
33
|
+
organization with no allowed domains configured has its publishable keys refused everywhere. They power the
|
|
34
|
+
[`<viewrium-viewer>` embed](https://www.viewrium.com/docs/embed); use the embed, not this SDK, to
|
|
35
|
+
show a model on a storefront.
|
|
36
|
+
|
|
37
|
+
Create keys in the dashboard under **Settings -> Advanced -> API keys**. A secret key is shown
|
|
38
|
+
exactly once, on create. See [Authentication](https://www.viewrium.com/docs/authentication) for
|
|
39
|
+
scopes.
|
|
5
40
|
|
|
6
41
|
```ts
|
|
7
42
|
import { Viewrium } from "@viewrium/sdk";
|
|
8
43
|
|
|
9
|
-
const
|
|
44
|
+
const client = new Viewrium({ apiKey: process.env.VIEWRIUM_API_KEY! });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Options:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const client = new Viewrium({
|
|
51
|
+
apiKey: "sk_live_...",
|
|
52
|
+
baseUrl: "https://api.viewrium.com", // default; trailing slash stripped
|
|
53
|
+
fetch: customFetch, // optional; defaults to globalThis.fetch
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`createViewrium({ apiKey })` is equivalent to `new Viewrium({ apiKey })`.
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
10
60
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
61
|
+
`products.create` is the single generation entrypoint. Supply exactly one source: `image_urls`,
|
|
62
|
+
`video_url`, `source_url` (a product page) or `model_url` (an existing 3D file). Sources are URLs
|
|
63
|
+
that Viewrium fetches, so they must be publicly reachable.
|
|
64
|
+
|
|
65
|
+
A fresh request **parks**: nothing is written, and you get back the plan, the 3D model verdict, the
|
|
66
|
+
price in credits and a `resume_token`. Posting the token back commits the quote and starts the run.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { Viewrium } from "@viewrium/sdk";
|
|
70
|
+
|
|
71
|
+
const client = new Viewrium({ apiKey: process.env.VIEWRIUM_API_KEY! });
|
|
72
|
+
|
|
73
|
+
// 1. Quote
|
|
74
|
+
const quote = await client.products.create({
|
|
75
|
+
image_urls: [
|
|
76
|
+
"https://cdn.example.com/chair-front.jpg",
|
|
77
|
+
"https://cdn.example.com/chair-side.jpg",
|
|
78
|
+
],
|
|
79
|
+
draft: { title: "Oak dining chair", custom_id: "sku-oak-chair", dimensions: { height: 92 } },
|
|
80
|
+
});
|
|
81
|
+
if (quote.status !== "parked") throw new Error("unexpected: already committed");
|
|
82
|
+
console.log(quote.cost.total_credits, quote.plan?.length, quote.model.allowed);
|
|
83
|
+
|
|
84
|
+
// 2. Commit
|
|
85
|
+
const committed = await client.products.create({ resume_token: quote.resume_token });
|
|
86
|
+
if (committed.status === "parked") throw new Error("a resume always commits");
|
|
87
|
+
|
|
88
|
+
// 3. Wait for the run (prefer webhooks in production)
|
|
89
|
+
let product = await client.products.retrieve(committed.id);
|
|
90
|
+
while (product.display_status === "generating") {
|
|
91
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
92
|
+
product = await client.products.retrieve(product.id);
|
|
15
93
|
}
|
|
94
|
+
console.log(product.display_status, product.model?.status);
|
|
95
|
+
|
|
96
|
+
// 4. Publish
|
|
97
|
+
await client.products.update(product.id, { published: true });
|
|
16
98
|
```
|
|
17
99
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
100
|
+
A resume may still edit the `draft` (title, price, tags, dimensions). It may not change the order
|
|
101
|
+
or the overrides: the quote is immutable, and changing what gets generated means a new request.
|
|
102
|
+
A `resume_token` expires after 7 days. A `model_url` source never parks; it commits straight away.
|
|
103
|
+
|
|
104
|
+
See [How generation works](https://www.viewrium.com/docs/generation) and
|
|
105
|
+
[the quote and commit guide](https://www.viewrium.com/docs/guides/quote-and-commit).
|
|
106
|
+
|
|
107
|
+
## Resources
|
|
108
|
+
|
|
109
|
+
| Resource | Methods |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `client.products` | `create`, `list`, `retrieve`, `update`, `delete`, `batchDelete`, `publish`, `tags`, `review`, `updateImage`, `updateModel`, `rescale`, `createTicket`, `order`, `export` |
|
|
112
|
+
| `client.apiKeys` | `list`, `create`, `revoke` |
|
|
113
|
+
| `client.usage` | `get` |
|
|
114
|
+
| `client.webhooks` | `list`, `create`, `retrieve`, `update`, `delete`, `listDeliveries` |
|
|
115
|
+
| `client.projects` | `list`, `retrieve` |
|
|
116
|
+
| `client.beacon` | `send` |
|
|
117
|
+
| `client` | `health()` |
|
|
118
|
+
|
|
119
|
+
Request and response types (`ProductCreateParams`, `ProductCreateResult`, `ProductParked`,
|
|
120
|
+
`Product`, `PlannedView`, `ModelPlan`, `ProductCost`, `Usage`, `WebhookEndpoint`, and so on) are
|
|
121
|
+
exported and derived from the API's OpenAPI schema.
|
|
122
|
+
|
|
123
|
+
### Showing a model: tickets
|
|
124
|
+
|
|
125
|
+
No response carries a URL for a 3D model file - not the product, not a webhook. The only way to a
|
|
126
|
+
model's bytes is a **ticket**: a single-use URL that serves the GLB once and expires within a
|
|
127
|
+
minute (60 s for `view`, 30 s for `ar`). Mint one right before the viewer loads, from the server,
|
|
128
|
+
and hand the URL straight to the page; never store it.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const ticket = await client.products.createTicket(productId, {
|
|
132
|
+
kind: "view",
|
|
133
|
+
origin: "https://shop.example.com", // the page that will load the model; an allowed domain
|
|
134
|
+
});
|
|
135
|
+
// ticket.url -> the page's viewer `src`; ticket.expires_at -> when it stops working
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
A `view` ticket streams only to a request from its `origin`. An `ar` ticket is for Android's Scene
|
|
139
|
+
Viewer, which downloads the file outside the page: mint it on the AR tap. Minting is rate limited
|
|
140
|
+
per IP and per product (429).
|
|
141
|
+
|
|
142
|
+
### Listing and filtering
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const page = await client.products.list({
|
|
146
|
+
status: ["published"],
|
|
147
|
+
tag: ["chairs"],
|
|
148
|
+
q: "oak",
|
|
149
|
+
limit: 50,
|
|
150
|
+
sort: "created_at",
|
|
151
|
+
order: "desc",
|
|
152
|
+
});
|
|
153
|
+
for (const product of page.data) {
|
|
154
|
+
console.log(product.id, product.title, product.display_status);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`products.tags` and `products.export` take the same filters.
|
|
159
|
+
|
|
160
|
+
### Exporting images
|
|
161
|
+
|
|
162
|
+
`products.export` resolves to a ZIP of finished images as a `Blob` (the 3D model is never
|
|
163
|
+
included). When more images need finishing than one request can do, it resolves to a queued answer
|
|
164
|
+
instead: the images are finished in the background, a download link is emailed, and the same call
|
|
165
|
+
returns the archive once they are done.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const archive = await client.products.export({ tag: ["chairs"] });
|
|
169
|
+
if (archive instanceof Blob) {
|
|
170
|
+
// write it out
|
|
171
|
+
} else {
|
|
172
|
+
console.log(`finishing ${archive.to_finish} images; a link goes to ${archive.notify_email}`);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Analytics
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const usage = await client.usage.get({
|
|
180
|
+
start: "2026-06-01T00:00:00Z",
|
|
181
|
+
end: "2026-09-01T00:00:00Z",
|
|
182
|
+
granularity: "day",
|
|
183
|
+
group_by: "product",
|
|
184
|
+
});
|
|
185
|
+
for (const row of usage.by_product ?? []) {
|
|
186
|
+
console.log(row.title, row.totals);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Traffic from your own team is excluded unless you pass `include_internal: true`.
|
|
191
|
+
|
|
192
|
+
## Errors
|
|
193
|
+
|
|
194
|
+
Non-2xx responses throw a `ViewriumError` subclass. Every error exposes `status`, `code`,
|
|
195
|
+
`message` and `details` from the API's error envelope.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import {
|
|
199
|
+
ViewriumError,
|
|
200
|
+
AuthenticationError,
|
|
201
|
+
PaymentRequiredError,
|
|
202
|
+
ConflictError,
|
|
203
|
+
RateLimitError,
|
|
204
|
+
ConnectionError,
|
|
205
|
+
} from "@viewrium/sdk";
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await client.products.create({ image_urls: ["https://cdn.example.com/chair.jpg"] });
|
|
209
|
+
} catch (err) {
|
|
210
|
+
if (err instanceof AuthenticationError) {
|
|
211
|
+
// 401: missing or invalid key
|
|
212
|
+
} else if (err instanceof PaymentRequiredError) {
|
|
213
|
+
// 402: the plan does not allow this; err.details.reason says why
|
|
214
|
+
} else if (err instanceof ConflictError) {
|
|
215
|
+
// 409: e.g. a duplicate custom_id or a replayed resume_token
|
|
216
|
+
} else if (err instanceof RateLimitError) {
|
|
217
|
+
// 429: rate limited, or code "quota_exceeded" when credits run out
|
|
218
|
+
} else if (err instanceof ConnectionError) {
|
|
219
|
+
// the request never reached the API
|
|
220
|
+
} else if (err instanceof ViewriumError) {
|
|
221
|
+
console.log(err.status, err.code, err.message, err.details);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| HTTP | Error |
|
|
227
|
+
|---|---|
|
|
228
|
+
| 400 | `BadRequestError` |
|
|
229
|
+
| 401 | `AuthenticationError` |
|
|
230
|
+
| 402 | `PaymentRequiredError` |
|
|
231
|
+
| 403 | `PermissionError` |
|
|
232
|
+
| 404 | `NotFoundError` |
|
|
233
|
+
| 409 | `ConflictError` |
|
|
234
|
+
| 422 | `UnprocessableEntityError` |
|
|
235
|
+
| 429 | `RateLimitError` |
|
|
236
|
+
| 5xx | `ServerError` |
|
|
237
|
+
| none | `ConnectionError` |
|
|
238
|
+
|
|
239
|
+
The SDK does not retry. On a `RateLimitError`, back off and retry with an exponential delay.
|
|
240
|
+
|
|
241
|
+
## Links
|
|
242
|
+
|
|
243
|
+
- [Quickstart](https://www.viewrium.com/docs/quickstart)
|
|
244
|
+
- [JavaScript SDK reference](https://www.viewrium.com/docs/sdks/javascript)
|
|
245
|
+
- [Webhooks](https://www.viewrium.com/docs/webhooks)
|
|
246
|
+
- [API reference](https://www.viewrium.com/docs/api-reference)
|
|
247
|
+
- Support: support@viewrium.com
|
|
22
248
|
|
|
23
|
-
##
|
|
249
|
+
## License
|
|
24
250
|
|
|
25
|
-
|
|
26
|
-
committed from the API's authoritative `apps/api/openapi.json` (the same generator
|
|
27
|
-
`@repo/api-client` uses; run `pnpm run generate` to refresh). Owning the committed schema keeps the
|
|
28
|
-
published `dist/index.d.ts` fully self-contained: tsup's dts bundler cannot inline a re-exported
|
|
29
|
-
type from the source-only, unpublished `@repo/api-client`, so deriving from it would leak an
|
|
30
|
-
unresolvable `import { paths } from "@repo/api-client"` into the published declarations. `tsup`
|
|
31
|
-
emits ESM + CJS + a self-contained `.d.ts`. Published by `.github/workflows/publish-sdk-js.yml`.
|
|
251
|
+
Apache-2.0
|
package/dist/index.cjs
CHANGED
|
@@ -210,6 +210,14 @@ function createProducts(request, requestBlob) {
|
|
|
210
210
|
* remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.
|
|
211
211
|
*/
|
|
212
212
|
rescale: (id, params) => request("POST", `/v1/products/${encode(id)}/model/rescale`, { body: params }),
|
|
213
|
+
/**
|
|
214
|
+
* POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way
|
|
215
|
+
* to its bytes (no response carries a model file URL). It works once, until `expires_at`
|
|
216
|
+
* (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`
|
|
217
|
+
* key the product must be published and the ticket is bound to the page's Origin; with an
|
|
218
|
+
* `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).
|
|
219
|
+
*/
|
|
220
|
+
createTicket: (id, params) => request("POST", `/v1/products/${encode(id)}/tickets`, { body: params }),
|
|
213
221
|
/** POST /v1/products/{id}/order - park the missing half (images or a model); commit the
|
|
214
222
|
* returned `resume_token` with `create`. */
|
|
215
223
|
order: (id, params) => request("POST", `/v1/products/${encode(id)}/order`, { body: params }),
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts"],"sourcesContent":["/**\n * @viewrium/sdk - the official Viewrium JavaScript/TypeScript SDK.\n *\n * An ergonomic wrapper over the `/v1` REST API. Request/response types are derived from\n * `src/generated/schema.ts` - openapi-typescript output committed from the API's authoritative\n * `apps/api/openapi.json` (the same generator `@repo/api-client` uses; regenerate with\n * `pnpm run generate`). Owning the committed schema keeps the published `dist/index.d.ts`\n * self-contained: it cannot reference the unpublished, source-only `@repo/api-client`.\n *\n * Auth: pass an `sk_` secret key (server) or a `pk_` publishable key (browser). The key is sent\n * as `Authorization: Bearer <key>`; the API enforces what each key type may call.\n *\n * E4 (2026-09-09): the scan-era `models` resource is GONE with its routes; `products` is the\n * resource. Resource and method names match `@repo/api-client` so every client reads the same.\n */\nimport { ConnectionError, ViewriumError, createApiError } from \"./errors\";\nimport type { paths } from \"./generated/schema\";\n\nexport * from \"./errors\";\n\n// --- Derived request/response types (source of truth = apps/api/openapi.json) ----------------\n\ntype JsonContent<T> = T extends { content: { \"application/json\": infer B } } ? B : never;\n\n/** GET /v1/products/{id} resolves to one of two shapes by principal: the full product for a\n * session or `sk_` key, the allow-listed public projection for a `pk_` key. */\ntype ProductGetResult = JsonContent<paths[\"/v1/products/{id}\"][\"get\"][\"responses\"][200]>;\n/** A product with its assets (`sk_` view). */\nexport type Product = Extract<ProductGetResult, { org_id: string }>;\n/** The `pk_` view of a published product: id, title, model_url, thumbnail_url, model_status. */\nexport type PublicProduct = Exclude<ProductGetResult, { org_id: string }>;\nexport type ProductList = JsonContent<paths[\"/v1/products\"][\"get\"][\"responses\"][200]>;\n/** A catalog row - the product plus what its card needs, with no nested assets. */\nexport type ProductSummary = ProductList[\"data\"][number];\nexport type ProductListParams = NonNullable<paths[\"/v1/products\"][\"get\"][\"parameters\"][\"query\"]>;\nexport type ProductCreateParams = JsonContent<paths[\"/v1/products\"][\"post\"][\"requestBody\"]>;\n/** POST /v1/products 200 - the gate parked; resume by POSTing `resume_token`. */\nexport type ProductParked = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][200]>;\n/** One page position of a parked plan. Both park shapes carry the same row type. */\nexport type PlannedView = NonNullable<ProductParked[\"plan\"]>[number];\n/** describe's structured read of the product (D25), with its verbatim reasons. */\nexport type GateFlags = ProductParked[\"flags\"];\n/** The models layer's verdict: whether a model is allowed, what blocks it, and the views it\n * would be built from - including `unseen_views`, the invented ones. */\nexport type ModelPlan = ProductParked[\"model\"];\n/** The priced bundle, in credits (1 credit = $0.50): 1 per deliverable image, a flat 10 for a model. */\nexport type ProductCost = ProductParked[\"cost\"];\n/** POST /v1/products 202 - a committed product whose run has started. */\nexport type ProductCreated = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][202]>;\n/** POST /v1/products resolves to one of the two (discriminate on `status === \"parked\"`). */\nexport type ProductCreateResult = ProductParked | ProductCreated;\nexport type ProductUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductBatchResult = JsonContent<paths[\"/v1/products\"][\"delete\"][\"responses\"][200]>;\nexport type ProductPublishParams = JsonContent<\n paths[\"/v1/products/publish\"][\"post\"][\"requestBody\"]\n>;\nexport type Tags = JsonContent<paths[\"/v1/products/tags\"][\"get\"][\"responses\"][200]>;\nexport type TagsParams = NonNullable<paths[\"/v1/products/tags\"][\"get\"][\"parameters\"][\"query\"]>;\n/** POST /v1/products/{id}/review - the write-behind flush (image + model decisions, the\n * product's user-set status). It records decisions only. */\nexport type ProductReviewParams = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductReviewResult = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductImagePatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductImage = JsonContent<\n paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"responses\"][200]\n>;\nexport type ProductModelPatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/model\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductModel = JsonContent<paths[\"/v1/products/{id}/model\"][\"patch\"][\"responses\"][200]>;\nexport type ProductRescaleParams = JsonContent<\n paths[\"/v1/products/{id}/model/rescale\"][\"post\"][\"requestBody\"]\n>;\n/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through\n * `products.create({ resume_token })`. */\nexport type ProductOrderParams = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductOrderParked = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductExportParams = NonNullable<\n paths[\"/v1/products/export\"][\"get\"][\"parameters\"][\"query\"]\n>;\n/** 202 from the export: the images are being finished in the background and a download link\n * is emailed; the same request streams the ZIP once they are done. */\nexport type ProductExportQueued = JsonContent<\n paths[\"/v1/products/export\"][\"get\"][\"responses\"][202]\n>;\n\nexport type ApiKeyList = JsonContent<paths[\"/v1/api_keys\"][\"get\"][\"responses\"][200]>;\nexport type ApiKeyCreateParams = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"requestBody\"]>;\nexport type ApiKeyCreated = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"responses\"][201]>;\n\nexport type Usage = JsonContent<paths[\"/v1/usage\"][\"get\"][\"responses\"][200]>;\nexport type UsageParams = NonNullable<paths[\"/v1/usage\"][\"get\"][\"parameters\"][\"query\"]>;\n/** One `group_by: \"product\"` row: a product's counts over the window, with its title. */\nexport type UsageProductTotals = NonNullable<Usage[\"by_product\"]>[number];\n\nexport type WebhookEndpoint = JsonContent<paths[\"/v1/webhooks/{id}\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointList = JsonContent<paths[\"/v1/webhooks\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointCreateParams = JsonContent<paths[\"/v1/webhooks\"][\"post\"][\"requestBody\"]>;\nexport type WebhookEndpointCreated = JsonContent<\n paths[\"/v1/webhooks\"][\"post\"][\"responses\"][201]\n>;\nexport type WebhookEndpointUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/webhooks/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type WebhookDeliveryList = JsonContent<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"responses\"][200]\n>;\nexport type WebhookDeliveriesParams = NonNullable<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type Project = JsonContent<paths[\"/v1/projects/{id}\"][\"get\"][\"responses\"][200]>;\nexport type ProjectList = JsonContent<paths[\"/v1/projects\"][\"get\"][\"responses\"][200]>;\n\nexport type BeaconParams = JsonContent<paths[\"/v1/beacon\"][\"post\"][\"requestBody\"]>;\n\nexport type Health = JsonContent<paths[\"/v1/health\"][\"get\"][\"responses\"][200]>;\n\n// --- Client options + transport --------------------------------------------------------------\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface ViewriumOptions {\n /** `sk_` secret key (server) or `pk_` publishable key (browser). */\n apiKey: string;\n /** API origin; defaults to `https://api.viewrium.com`. Trailing slash is stripped. */\n baseUrl?: string;\n /** Injectable fetch (tests, custom agents). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.viewrium.com\";\n\ntype QueryValue = string | number | boolean | readonly string[] | null | undefined;\ntype QueryParams = Record<string, QueryValue>;\n\ninterface RequestOptions {\n query?: QueryParams;\n body?: unknown;\n}\n\ntype RequestFn = <T>(method: string, path: string, options?: RequestOptions) => Promise<T>;\n/** The escape hatch for the one route that answers bytes rather than JSON (the export ZIP) -\n * or, on a 202, JSON after all. */\ntype BlobRequestFn = (\n method: string,\n path: string,\n options?: RequestOptions,\n) => Promise<Blob | ProductExportQueued>;\n\ninterface Transport {\n baseUrl: string;\n apiKey: string;\n fetch: FetchLike;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const url = new URL(baseUrl + path);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n for (const item of value as readonly string[]) url.searchParams.append(key, String(item));\n } else {\n url.searchParams.append(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\nasync function parseError(res: Response): Promise<ViewriumError> {\n let code = \"error\";\n let message = res.statusText || `HTTP ${res.status}`;\n let details: unknown;\n try {\n const data: unknown = await res.json();\n if (data && typeof data === \"object\" && \"error\" in data) {\n const envelope = (data as { error: unknown }).error;\n if (envelope && typeof envelope === \"object\") {\n const e = envelope as Record<string, unknown>;\n if (typeof e.code === \"string\") code = e.code;\n if (typeof e.message === \"string\") message = e.message;\n details = e.details;\n }\n }\n } catch {\n // Non-JSON error body; fall back to status text.\n }\n return createApiError(res.status, { code, message, details });\n}\n\nasync function send(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Response> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n authorization: `Bearer ${transport.apiKey}`,\n };\n const init: RequestInit = { method, headers };\n if (options?.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n init.body = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await transport.fetch(buildUrl(transport.baseUrl, path, options?.query), init);\n } catch (cause) {\n throw new ConnectionError(\"The request to the Viewrium API could not be sent.\", { cause });\n }\n\n if (!res.ok) {\n throw await parseError(res);\n }\n return res;\n}\n\nasync function doRequest<T>(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<T> {\n const res = await send(transport, method, path, options);\n if (res.status === 204) {\n return undefined as T;\n }\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n}\n\nasync function doBlobRequest(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Blob | ProductExportQueued> {\n const res = await send(transport, method, path, options);\n if (res.status === 202) return (await res.json()) as ProductExportQueued;\n return res.blob();\n}\n\nconst encode = encodeURIComponent;\n\n// --- Resource namespaces ---------------------------------------------------------------------\n\nfunction createProducts(request: RequestFn, requestBlob: BlobRequestFn) {\n return {\n /**\n * POST /v1/products - the single generation entrypoint. Supply exactly one source\n * (`image_paths` | `image_urls` | `video_url` | `source_url` | `model_url` |\n * `resume_token`). A fresh request ALWAYS parks (200, `status === \"parked\"`, nothing\n * written): the plan, the model verdict and the price come back inside `resume_token`,\n * which you POST back to commit (202, `ProductCreated`). A `model_url` commits at once.\n */\n create: (params: ProductCreateParams) =>\n request<ProductCreateResult>(\"POST\", \"/v1/products\", { body: params }),\n /**\n * GET /v1/products - the catalog: one org (`org_id`, or the caller's only org), or\n * `scope: \"all\"` for every org an agency member or staff reaches. Filters AND together.\n */\n list: (params?: ProductListParams) =>\n request<ProductList>(\"GET\", \"/v1/products\", { query: params as QueryParams | undefined }),\n /** GET /v1/products/{id} - the product with its assets (a `pk_` key gets `PublicProduct`). */\n retrieve: (id: string) => request<Product>(\"GET\", `/v1/products/${encode(id)}`),\n /**\n * PATCH /v1/products/{id} - the customer's fields, the user-set status and the publish\n * toggle. 409 on a `custom_id` another product in the org already holds.\n */\n update: (id: string, params: ProductUpdateParams) =>\n request<Product>(\"PATCH\", `/v1/products/${encode(id)}`, { body: params }),\n /** DELETE /v1/products/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/products/${encode(id)}`),\n /** DELETE /v1/products?ids=... - per-id affected/not_found/forbidden buckets. */\n batchDelete: (ids: readonly string[]) =>\n request<ProductBatchResult>(\"DELETE\", \"/v1/products\", { query: { ids } }),\n /** POST /v1/products/publish - the bulk publish toggle; one product toggles through `update`. */\n publish: (params: ProductPublishParams) =>\n request<ProductBatchResult>(\"POST\", \"/v1/products/publish\", { body: params }),\n /** GET /v1/products/tags - distinct tags across the same scope the list takes. */\n tags: (params?: TagsParams) =>\n request<Tags>(\"GET\", \"/v1/products/tags\", { query: params as QueryParams | undefined }),\n /** POST /v1/products/{id}/review - the review flush: image and model decisions plus the\n * product's user-set status, validated together and applied in order. */\n review: (id: string, params: ProductReviewParams) =>\n request<ProductReviewResult>(\"POST\", `/v1/products/${encode(id)}/review`, { body: params }),\n /** PATCH /v1/products/{id}/images/{image_id} - one image's decision and overrides. */\n updateImage: (id: string, imageId: string, params: ProductImagePatchParams) =>\n request<ProductImage>(\"PATCH\", `/v1/products/${encode(id)}/images/${encode(imageId)}`, {\n body: params,\n }),\n /** PATCH /v1/products/{id}/model - the model's review decision. */\n updateModel: (id: string, params: ProductModelPatchParams) =>\n request<ProductModel>(\"PATCH\", `/v1/products/${encode(id)}/model`, { body: params }),\n /**\n * POST /v1/products/{id}/model/rescale - re-apply real-world scale to the GLB in place.\n * Every supplied axis is applied to the geometry; `unit` covers them (omitted = cm) and is\n * remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.\n */\n rescale: (id: string, params: ProductRescaleParams) =>\n request<Product>(\"POST\", `/v1/products/${encode(id)}/model/rescale`, { body: params }),\n /** POST /v1/products/{id}/order - park the missing half (images or a model); commit the\n * returned `resume_token` with `create`. */\n order: (id: string, params: ProductOrderParams) =>\n request<ProductOrderParked>(\"POST\", `/v1/products/${encode(id)}/order`, { body: params }),\n /** GET /v1/products/export - a ZIP of finished images (never the 3D model), resolved as a\n * `Blob`; or `ProductExportQueued` (202) when the images are being finished in the\n * background and a download link is emailed - repeat the request later to stream it. */\n export: (params?: ProductExportParams) =>\n requestBlob(\"GET\", \"/v1/products/export\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createApiKeys(request: RequestFn) {\n return {\n /** GET /v1/api_keys */\n list: () => request<ApiKeyList>(\"GET\", \"/v1/api_keys\"),\n /** POST /v1/api_keys - the full key is returned exactly once. */\n create: (params: ApiKeyCreateParams) =>\n request<ApiKeyCreated>(\"POST\", \"/v1/api_keys\", { body: params }),\n /** DELETE /v1/api_keys/{id} - soft revoke. */\n revoke: (id: string) => request<void>(\"DELETE\", `/v1/api_keys/${encode(id)}`),\n };\n}\n\nfunction createUsage(request: RequestFn) {\n return {\n /** GET /v1/usage - buckets and totals over the window; `group_by: \"product\"` adds the\n * window's best-performing products in `by_product`. */\n get: (params?: UsageParams) =>\n request<Usage>(\"GET\", \"/v1/usage\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createWebhooks(request: RequestFn) {\n return {\n /** GET /v1/webhooks */\n list: () => request<WebhookEndpointList>(\"GET\", \"/v1/webhooks\"),\n /** POST /v1/webhooks - includes the signing secret. */\n create: (params: WebhookEndpointCreateParams) =>\n request<WebhookEndpointCreated>(\"POST\", \"/v1/webhooks\", { body: params }),\n /** GET /v1/webhooks/{id} */\n retrieve: (id: string) => request<WebhookEndpoint>(\"GET\", `/v1/webhooks/${encode(id)}`),\n /** PATCH /v1/webhooks/{id} */\n update: (id: string, params: WebhookEndpointUpdateParams) =>\n request<WebhookEndpoint>(\"PATCH\", `/v1/webhooks/${encode(id)}`, { body: params }),\n /** DELETE /v1/webhooks/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/webhooks/${encode(id)}`),\n /** GET /v1/webhooks/{id}/deliveries */\n listDeliveries: (id: string, params?: WebhookDeliveriesParams) =>\n request<WebhookDeliveryList>(\"GET\", `/v1/webhooks/${encode(id)}/deliveries`, {\n query: params as QueryParams | undefined,\n }),\n };\n}\n\nfunction createProjects(request: RequestFn) {\n return {\n /** GET /v1/projects */\n list: () => request<ProjectList>(\"GET\", \"/v1/projects\"),\n /** GET /v1/projects/{id} */\n retrieve: (id: string) => request<Project>(\"GET\", `/v1/projects/${encode(id)}`),\n };\n}\n\nfunction createBeacon(request: RequestFn) {\n return {\n /** POST /v1/beacon - ingest a view/AR analytics event (204). */\n send: (params: BeaconParams) => request<void>(\"POST\", \"/v1/beacon\", { body: params }),\n };\n}\n\n// --- Client ----------------------------------------------------------------------------------\n\nexport class Viewrium {\n readonly baseUrl: string;\n readonly products: ReturnType<typeof createProducts>;\n readonly apiKeys: ReturnType<typeof createApiKeys>;\n readonly usage: ReturnType<typeof createUsage>;\n readonly webhooks: ReturnType<typeof createWebhooks>;\n readonly projects: ReturnType<typeof createProjects>;\n readonly beacon: ReturnType<typeof createBeacon>;\n\n readonly #request: RequestFn;\n\n constructor(options: ViewriumOptions) {\n if (!options || !options.apiKey) {\n throw new ViewriumError(\"A Viewrium client requires an `apiKey` (sk_ or pk_).\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\n const provided = options.fetch;\n const globalFetch = typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined;\n const fetchImpl = provided ?? globalFetch;\n if (!fetchImpl) {\n throw new ViewriumError(\"No fetch implementation available; pass `options.fetch`.\");\n }\n\n const transport: Transport = { baseUrl: this.baseUrl, apiKey: options.apiKey, fetch: fetchImpl };\n const request: RequestFn = <T>(method: string, path: string, opts?: RequestOptions) =>\n doRequest<T>(transport, method, path, opts);\n const requestBlob: BlobRequestFn = (method, path, opts) =>\n doBlobRequest(transport, method, path, opts);\n this.#request = request;\n\n this.products = createProducts(request, requestBlob);\n this.apiKeys = createApiKeys(request);\n this.usage = createUsage(request);\n this.webhooks = createWebhooks(request);\n this.projects = createProjects(request);\n this.beacon = createBeacon(request);\n }\n\n /** GET /v1/health */\n health(): Promise<Health> {\n return this.#request<Health>(\"GET\", \"/v1/health\");\n }\n}\n\n/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */\nexport function createViewrium(options: ViewriumOptions): Viewrium {\n return new Viewrium(options);\n}\n","/**\n * Typed errors for the Viewrium SDK.\n *\n * The API returns one error envelope: `{ \"error\": { code, message, details } }`.\n * Non-2xx responses are parsed into a `ViewriumError` subclass keyed by HTTP status;\n * consumers branch with `instanceof`.\n */\n\nexport interface ViewriumErrorOptions {\n status?: number | null;\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class ViewriumError extends Error {\n /** HTTP status, or `null` for transport/connection failures. */\n readonly status: number | null;\n /** Machine-readable code from the error envelope. */\n readonly code: string;\n /** Optional structured details from the error envelope. */\n readonly details: unknown;\n\n constructor(message: string, options: ViewriumErrorOptions = {}) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = new.target.name;\n this.status = options.status ?? null;\n this.code = options.code ?? \"error\";\n this.details = options.details;\n }\n}\n\n/** 400 - malformed request. */\nexport class BadRequestError extends ViewriumError {}\n/** 401 - missing or invalid API key. */\nexport class AuthenticationError extends ViewriumError {}\n/**\n * 402 - the organization's plan does not allow this: no plan, a used free trial, a failed payment,\n * or past the plan's active-product cap. `details.reason` says which.\n */\nexport class PaymentRequiredError extends ViewriumError {}\n/** 403 - the key is valid but not allowed (scope, origin, or role). */\nexport class PermissionError extends ViewriumError {}\n/** 404 - no such resource. */\nexport class NotFoundError extends ViewriumError {}\n/** 409 - conflict (e.g. duplicate `custom_id`, model not in a rescalable state). */\nexport class ConflictError extends ViewriumError {}\n/** 422 - request failed validation. */\nexport class UnprocessableEntityError extends ViewriumError {}\n/** 429 - rate limited, or not enough credits (`code: \"quota_exceeded\"`, `details.shortfall_credits`). */\nexport class RateLimitError extends ViewriumError {}\n/** 5xx - the API failed to handle the request. */\nexport class ServerError extends ViewriumError {}\n/** The request never reached the API (DNS/TLS/offline/abort). */\nexport class ConnectionError extends ViewriumError {}\n\nexport interface ApiErrorPayload {\n code: string;\n message: string;\n details?: unknown;\n}\n\nconst BY_STATUS: Record<number, new (message: string, options: ViewriumErrorOptions) => ViewriumError> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 402: PaymentRequiredError,\n 403: PermissionError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n};\n\n/** Build the right `ViewriumError` subclass for an HTTP status + parsed envelope. */\nexport function createApiError(status: number, payload: ApiErrorPayload): ViewriumError {\n const options: ViewriumErrorOptions = { status, code: payload.code, details: payload.details };\n const Specific = BY_STATUS[status];\n if (Specific) {\n return new Specific(payload.message, options);\n }\n if (status >= 500) {\n return new ServerError(payload.message, options);\n }\n return new ViewriumError(payload.message, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeO,IAAM,gBAAN,cAA4B,MAAM;AAAA;AAAA,EAE9B;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,SAAiB,UAAgC,CAAC,GAAG;AAC/D,UAAM,SAAS,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,MAAS;AACjF,SAAK,OAAO,WAAW;AACvB,SAAK,SAAS,QAAQ,UAAU;AAChC,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,UAAU,QAAQ;AAAA,EACzB;AACF;AAGO,IAAM,kBAAN,cAA8B,cAAc;AAAC;AAE7C,IAAM,sBAAN,cAAkC,cAAc;AAAC;AAKjD,IAAM,uBAAN,cAAmC,cAAc;AAAC;AAElD,IAAM,kBAAN,cAA8B,cAAc;AAAC;AAE7C,IAAM,gBAAN,cAA4B,cAAc;AAAC;AAE3C,IAAM,gBAAN,cAA4B,cAAc;AAAC;AAE3C,IAAM,2BAAN,cAAuC,cAAc;AAAC;AAEtD,IAAM,iBAAN,cAA6B,cAAc;AAAC;AAE5C,IAAM,cAAN,cAA0B,cAAc;AAAC;AAEzC,IAAM,kBAAN,cAA8B,cAAc;AAAC;AAQpD,IAAM,YAAmG;AAAA,EACvG,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAGO,SAAS,eAAe,QAAgB,SAAyC;AACtF,QAAM,UAAgC,EAAE,QAAQ,MAAM,QAAQ,MAAM,SAAS,QAAQ,QAAQ;AAC7F,QAAM,WAAW,UAAU,MAAM;AACjC,MAAI,UAAU;AACZ,WAAO,IAAI,SAAS,QAAQ,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,UAAU,KAAK;AACjB,WAAO,IAAI,YAAY,QAAQ,SAAS,OAAO;AAAA,EACjD;AACA,SAAO,IAAI,cAAc,QAAQ,SAAS,OAAO;AACnD;;;AD2DA,IAAM,mBAAmB;AAyBzB,SAAS,SAAS,SAAiB,MAAc,OAA6B;AAC5E,QAAM,MAAM,IAAI,IAAI,UAAU,IAAI;AAClC,MAAI,OAAO;AACT,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,UAAU,UAAa,UAAU,KAAM;AAC3C,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,mBAAW,QAAQ,MAA4B,KAAI,aAAa,OAAO,KAAK,OAAO,IAAI,CAAC;AAAA,MAC1F,OAAO;AACL,YAAI,aAAa,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AACA,SAAO,IAAI,SAAS;AACtB;AAEA,eAAe,WAAW,KAAuC;AAC/D,MAAI,OAAO;AACX,MAAI,UAAU,IAAI,cAAc,QAAQ,IAAI,MAAM;AAClD,MAAI;AACJ,MAAI;AACF,UAAM,OAAgB,MAAM,IAAI,KAAK;AACrC,QAAI,QAAQ,OAAO,SAAS,YAAY,WAAW,MAAM;AACvD,YAAM,WAAY,KAA4B;AAC9C,UAAI,YAAY,OAAO,aAAa,UAAU;AAC5C,cAAM,IAAI;AACV,YAAI,OAAO,EAAE,SAAS,SAAU,QAAO,EAAE;AACzC,YAAI,OAAO,EAAE,YAAY,SAAU,WAAU,EAAE;AAC/C,kBAAU,EAAE;AAAA,MACd;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,eAAe,IAAI,QAAQ,EAAE,MAAM,SAAS,QAAQ,CAAC;AAC9D;AAEA,eAAe,KACb,WACA,QACA,MACA,SACmB;AACnB,QAAM,UAAkC;AAAA,IACtC,QAAQ;AAAA,IACR,eAAe,UAAU,UAAU,MAAM;AAAA,EAC3C;AACA,QAAM,OAAoB,EAAE,QAAQ,QAAQ;AAC5C,MAAI,SAAS,SAAS,QAAW;AAC/B,YAAQ,cAAc,IAAI;AAC1B,SAAK,OAAO,KAAK,UAAU,QAAQ,IAAI;AAAA,EACzC;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,UAAU,MAAM,SAAS,UAAU,SAAS,MAAM,SAAS,KAAK,GAAG,IAAI;AAAA,EACrF,SAAS,OAAO;AACd,UAAM,IAAI,gBAAgB,sDAAsD,EAAE,MAAM,CAAC;AAAA,EAC3F;AAEA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAM,WAAW,GAAG;AAAA,EAC5B;AACA,SAAO;AACT;AAEA,eAAe,UACb,WACA,QACA,MACA,SACY;AACZ,QAAM,MAAM,MAAM,KAAK,WAAW,QAAQ,MAAM,OAAO;AACvD,MAAI,IAAI,WAAW,KAAK;AACtB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAQ,OAAO,KAAK,MAAM,IAAI,IAAI;AACpC;AAEA,eAAe,cACb,WACA,QACA,MACA,SACqC;AACrC,QAAM,MAAM,MAAM,KAAK,WAAW,QAAQ,MAAM,OAAO;AACvD,MAAI,IAAI,WAAW,IAAK,QAAQ,MAAM,IAAI,KAAK;AAC/C,SAAO,IAAI,KAAK;AAClB;AAEA,IAAM,SAAS;AAIf,SAAS,eAAe,SAAoB,aAA4B;AACtE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQL,QAAQ,CAAC,WACP,QAA6B,QAAQ,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKvE,MAAM,CAAC,WACL,QAAqB,OAAO,gBAAgB,EAAE,OAAO,OAAkC,CAAC;AAAA;AAAA,IAE1F,UAAU,CAAC,OAAe,QAAiB,OAAO,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,IAK9E,QAAQ,CAAC,IAAY,WACnB,QAAiB,SAAS,gBAAgB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAE1E,QAAQ,CAAC,OAAe,QAAc,UAAU,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA;AAAA,IAE5E,aAAa,CAAC,QACZ,QAA4B,UAAU,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA;AAAA,IAE1E,SAAS,CAAC,WACR,QAA4B,QAAQ,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAE9E,MAAM,CAAC,WACL,QAAc,OAAO,qBAAqB,EAAE,OAAO,OAAkC,CAAC;AAAA;AAAA;AAAA,IAGxF,QAAQ,CAAC,IAAY,WACnB,QAA6B,QAAQ,gBAAgB,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAE5F,aAAa,CAAC,IAAY,SAAiB,WACzC,QAAsB,SAAS,gBAAgB,OAAO,EAAE,CAAC,WAAW,OAAO,OAAO,CAAC,IAAI;AAAA,MACrF,MAAM;AAAA,IACR,CAAC;AAAA;AAAA,IAEH,aAAa,CAAC,IAAY,WACxB,QAAsB,SAAS,gBAAgB,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMrF,SAAS,CAAC,IAAY,WACpB,QAAiB,QAAQ,gBAAgB,OAAO,EAAE,CAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA;AAAA,IAGvF,OAAO,CAAC,IAAY,WAClB,QAA4B,QAAQ,gBAAgB,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA,IAI1F,QAAQ,CAAC,WACP,YAAY,OAAO,uBAAuB,EAAE,OAAO,OAAkC,CAAC;AAAA,EAC1F;AACF;AAEA,SAAS,cAAc,SAAoB;AACzC,SAAO;AAAA;AAAA,IAEL,MAAM,MAAM,QAAoB,OAAO,cAAc;AAAA;AAAA,IAErD,QAAQ,CAAC,WACP,QAAuB,QAAQ,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAEjE,QAAQ,CAAC,OAAe,QAAc,UAAU,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA,EAC9E;AACF;AAEA,SAAS,YAAY,SAAoB;AACvC,SAAO;AAAA;AAAA;AAAA,IAGL,KAAK,CAAC,WACJ,QAAe,OAAO,aAAa,EAAE,OAAO,OAAkC,CAAC;AAAA,EACnF;AACF;AAEA,SAAS,eAAe,SAAoB;AAC1C,SAAO;AAAA;AAAA,IAEL,MAAM,MAAM,QAA6B,OAAO,cAAc;AAAA;AAAA,IAE9D,QAAQ,CAAC,WACP,QAAgC,QAAQ,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAE1E,UAAU,CAAC,OAAe,QAAyB,OAAO,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA;AAAA,IAEtF,QAAQ,CAAC,IAAY,WACnB,QAAyB,SAAS,gBAAgB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC;AAAA;AAAA,IAElF,QAAQ,CAAC,OAAe,QAAc,UAAU,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA;AAAA,IAE5E,gBAAgB,CAAC,IAAY,WAC3B,QAA6B,OAAO,gBAAgB,OAAO,EAAE,CAAC,eAAe;AAAA,MAC3E,OAAO;AAAA,IACT,CAAC;AAAA,EACL;AACF;AAEA,SAAS,eAAe,SAAoB;AAC1C,SAAO;AAAA;AAAA,IAEL,MAAM,MAAM,QAAqB,OAAO,cAAc;AAAA;AAAA,IAEtD,UAAU,CAAC,OAAe,QAAiB,OAAO,gBAAgB,OAAO,EAAE,CAAC,EAAE;AAAA,EAChF;AACF;AAEA,SAAS,aAAa,SAAoB;AACxC,SAAO;AAAA;AAAA,IAEL,MAAM,CAAC,WAAyB,QAAc,QAAQ,cAAc,EAAE,MAAM,OAAO,CAAC;AAAA,EACtF;AACF;AAIO,IAAM,WAAN,MAAe;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EAET,YAAY,SAA0B;AACpC,QAAI,CAAC,WAAW,CAAC,QAAQ,QAAQ;AAC/B,YAAM,IAAI,cAAc,sDAAsD;AAAA,IAChF;AACA,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AAEvE,UAAM,WAAW,QAAQ;AACzB,UAAM,cAAc,OAAO,WAAW,UAAU,aAAa,WAAW,MAAM,KAAK,UAAU,IAAI;AACjG,UAAM,YAAY,YAAY;AAC9B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,cAAc,0DAA0D;AAAA,IACpF;AAEA,UAAM,YAAuB,EAAE,SAAS,KAAK,SAAS,QAAQ,QAAQ,QAAQ,OAAO,UAAU;AAC/F,UAAM,UAAqB,CAAI,QAAgB,MAAc,SAC3D,UAAa,WAAW,QAAQ,MAAM,IAAI;AAC5C,UAAM,cAA6B,CAAC,QAAQ,MAAM,SAChD,cAAc,WAAW,QAAQ,MAAM,IAAI;AAC7C,SAAK,WAAW;AAEhB,SAAK,WAAW,eAAe,SAAS,WAAW;AACnD,SAAK,UAAU,cAAc,OAAO;AACpC,SAAK,QAAQ,YAAY,OAAO;AAChC,SAAK,WAAW,eAAe,OAAO;AACtC,SAAK,WAAW,eAAe,OAAO;AACtC,SAAK,SAAS,aAAa,OAAO;AAAA,EACpC;AAAA;AAAA,EAGA,SAA0B;AACxB,WAAO,KAAK,SAAiB,OAAO,YAAY;AAAA,EAClD;AACF;AAGO,SAAS,eAAe,SAAoC;AACjE,SAAO,IAAI,SAAS,OAAO;AAC7B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts"],"sourcesContent":["/**\n * @viewrium/sdk - the official Viewrium JavaScript/TypeScript SDK.\n *\n * An ergonomic wrapper over the `/v1` REST API. Request/response types are derived from\n * `src/generated/schema.ts` - openapi-typescript output committed from the API's authoritative\n * `apps/api/openapi.json` (the same generator `@repo/api-client` uses; regenerate with\n * `pnpm run generate`). Owning the committed schema keeps the published `dist/index.d.ts`\n * self-contained: it cannot reference the unpublished, source-only `@repo/api-client`.\n *\n * Auth: pass an `sk_` secret key (server) or a `pk_` publishable key (browser). The key is sent\n * as `Authorization: Bearer <key>`; the API enforces what each key type may call.\n *\n * E4 (2026-09-09): the scan-era `models` resource is GONE with its routes; `products` is the\n * resource. Resource and method names match `@repo/api-client` so every client reads the same.\n */\nimport { ConnectionError, ViewriumError, createApiError } from \"./errors\";\nimport type { paths } from \"./generated/schema\";\n\nexport * from \"./errors\";\n\n// --- Derived request/response types (source of truth = apps/api/openapi.json) ----------------\n\ntype JsonContent<T> = T extends { content: { \"application/json\": infer B } } ? B : never;\n\n/** GET /v1/products/{id} resolves to one of two shapes by principal: the full product for a\n * session or `sk_` key, the allow-listed public projection for a `pk_` key. */\ntype ProductGetResult = JsonContent<paths[\"/v1/products/{id}\"][\"get\"][\"responses\"][200]>;\n/** A product with its assets (`sk_` view). */\nexport type Product = Extract<ProductGetResult, { org_id: string }>;\n/** The `pk_` view of a published product: id, title, thumbnail_url, model_status, wall_mounted. The model\n * file itself is reached through `products.createTicket`. */\nexport type PublicProduct = Exclude<ProductGetResult, { org_id: string }>;\nexport type ProductList = JsonContent<paths[\"/v1/products\"][\"get\"][\"responses\"][200]>;\n/** A catalog row - the product plus what its card needs, with no nested assets. */\nexport type ProductSummary = ProductList[\"data\"][number];\nexport type ProductListParams = NonNullable<paths[\"/v1/products\"][\"get\"][\"parameters\"][\"query\"]>;\nexport type ProductCreateParams = JsonContent<paths[\"/v1/products\"][\"post\"][\"requestBody\"]>;\n/** POST /v1/products 200 - the gate parked; resume by POSTing `resume_token`. */\nexport type ProductParked = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][200]>;\n/** One page position of a parked plan. Both park shapes carry the same row type. */\nexport type PlannedView = NonNullable<ProductParked[\"plan\"]>[number];\n/** describe's structured read of the product (D25), with its verbatim reasons. */\nexport type GateFlags = ProductParked[\"flags\"];\n/** The models layer's verdict: whether a model is allowed, what blocks it, and the views it\n * would be built from - including `unseen_views`, the invented ones. */\nexport type ModelPlan = ProductParked[\"model\"];\n/** The priced bundle, in credits (1 credit = $0.50): 1 per deliverable image, a flat 10 for a model. */\nexport type ProductCost = ProductParked[\"cost\"];\n/** POST /v1/products 202 - a committed product whose run has started. */\nexport type ProductCreated = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][202]>;\n/** POST /v1/products resolves to one of the two (discriminate on `status === \"parked\"`). */\nexport type ProductCreateResult = ProductParked | ProductCreated;\nexport type ProductUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductBatchResult = JsonContent<paths[\"/v1/products\"][\"delete\"][\"responses\"][200]>;\nexport type ProductPublishParams = JsonContent<\n paths[\"/v1/products/publish\"][\"post\"][\"requestBody\"]\n>;\nexport type Tags = JsonContent<paths[\"/v1/products/tags\"][\"get\"][\"responses\"][200]>;\nexport type TagsParams = NonNullable<paths[\"/v1/products/tags\"][\"get\"][\"parameters\"][\"query\"]>;\n/** POST /v1/products/{id}/review - the write-behind flush (image + model decisions, the\n * product's user-set status). It records decisions only. */\nexport type ProductReviewParams = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductReviewResult = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductImagePatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductImage = JsonContent<\n paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"responses\"][200]\n>;\nexport type ProductModelPatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/model\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductModel = JsonContent<paths[\"/v1/products/{id}/model\"][\"patch\"][\"responses\"][200]>;\nexport type ProductRescaleParams = JsonContent<\n paths[\"/v1/products/{id}/model/rescale\"][\"post\"][\"requestBody\"]\n>;\n/** POST /v1/products/{id}/tickets - `{ kind: \"view\" | \"ar\", origin? }`. */\nexport type TicketParams = JsonContent<paths[\"/v1/products/{id}/tickets\"][\"post\"][\"requestBody\"]>;\n/** A single-use model URL: `{ url, kind, expires_at }`. */\nexport type Ticket = JsonContent<paths[\"/v1/products/{id}/tickets\"][\"post\"][\"responses\"][200]>;\n/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through\n * `products.create({ resume_token })`. */\nexport type ProductOrderParams = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductOrderParked = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductExportParams = NonNullable<\n paths[\"/v1/products/export\"][\"get\"][\"parameters\"][\"query\"]\n>;\n/** 202 from the export: the images are being finished in the background and a download link\n * is emailed; the same request streams the ZIP once they are done. */\nexport type ProductExportQueued = JsonContent<\n paths[\"/v1/products/export\"][\"get\"][\"responses\"][202]\n>;\n\nexport type ApiKeyList = JsonContent<paths[\"/v1/api_keys\"][\"get\"][\"responses\"][200]>;\nexport type ApiKeyCreateParams = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"requestBody\"]>;\nexport type ApiKeyCreated = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"responses\"][201]>;\n\nexport type Usage = JsonContent<paths[\"/v1/usage\"][\"get\"][\"responses\"][200]>;\nexport type UsageParams = NonNullable<paths[\"/v1/usage\"][\"get\"][\"parameters\"][\"query\"]>;\n/** One `group_by: \"product\"` row: a product's counts over the window, with its title. */\nexport type UsageProductTotals = NonNullable<Usage[\"by_product\"]>[number];\n\nexport type WebhookEndpoint = JsonContent<paths[\"/v1/webhooks/{id}\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointList = JsonContent<paths[\"/v1/webhooks\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointCreateParams = JsonContent<paths[\"/v1/webhooks\"][\"post\"][\"requestBody\"]>;\nexport type WebhookEndpointCreated = JsonContent<\n paths[\"/v1/webhooks\"][\"post\"][\"responses\"][201]\n>;\nexport type WebhookEndpointUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/webhooks/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type WebhookDeliveryList = JsonContent<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"responses\"][200]\n>;\nexport type WebhookDeliveriesParams = NonNullable<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type Project = JsonContent<paths[\"/v1/projects/{id}\"][\"get\"][\"responses\"][200]>;\nexport type ProjectList = JsonContent<paths[\"/v1/projects\"][\"get\"][\"responses\"][200]>;\n\nexport type BeaconParams = JsonContent<paths[\"/v1/beacon\"][\"post\"][\"requestBody\"]>;\n\nexport type Health = JsonContent<paths[\"/v1/health\"][\"get\"][\"responses\"][200]>;\n\n// --- Client options + transport --------------------------------------------------------------\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface ViewriumOptions {\n /** `sk_` secret key (server) or `pk_` publishable key (browser). */\n apiKey: string;\n /** API origin; defaults to `https://api.viewrium.com`. Trailing slash is stripped. */\n baseUrl?: string;\n /** Injectable fetch (tests, custom agents). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.viewrium.com\";\n\ntype QueryValue = string | number | boolean | readonly string[] | null | undefined;\ntype QueryParams = Record<string, QueryValue>;\n\ninterface RequestOptions {\n query?: QueryParams;\n body?: unknown;\n}\n\ntype RequestFn = <T>(method: string, path: string, options?: RequestOptions) => Promise<T>;\n/** The escape hatch for the one route that answers bytes rather than JSON (the export ZIP) -\n * or, on a 202, JSON after all. */\ntype BlobRequestFn = (\n method: string,\n path: string,\n options?: RequestOptions,\n) => Promise<Blob | ProductExportQueued>;\n\ninterface Transport {\n baseUrl: string;\n apiKey: string;\n fetch: FetchLike;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const url = new URL(baseUrl + path);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n for (const item of value as readonly string[]) url.searchParams.append(key, String(item));\n } else {\n url.searchParams.append(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\nasync function parseError(res: Response): Promise<ViewriumError> {\n let code = \"error\";\n let message = res.statusText || `HTTP ${res.status}`;\n let details: unknown;\n try {\n const data: unknown = await res.json();\n if (data && typeof data === \"object\" && \"error\" in data) {\n const envelope = (data as { error: unknown }).error;\n if (envelope && typeof envelope === \"object\") {\n const e = envelope as Record<string, unknown>;\n if (typeof e.code === \"string\") code = e.code;\n if (typeof e.message === \"string\") message = e.message;\n details = e.details;\n }\n }\n } catch {\n // Non-JSON error body; fall back to status text.\n }\n return createApiError(res.status, { code, message, details });\n}\n\nasync function send(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Response> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n authorization: `Bearer ${transport.apiKey}`,\n };\n const init: RequestInit = { method, headers };\n if (options?.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n init.body = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await transport.fetch(buildUrl(transport.baseUrl, path, options?.query), init);\n } catch (cause) {\n throw new ConnectionError(\"The request to the Viewrium API could not be sent.\", { cause });\n }\n\n if (!res.ok) {\n throw await parseError(res);\n }\n return res;\n}\n\nasync function doRequest<T>(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<T> {\n const res = await send(transport, method, path, options);\n if (res.status === 204) {\n return undefined as T;\n }\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n}\n\nasync function doBlobRequest(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Blob | ProductExportQueued> {\n const res = await send(transport, method, path, options);\n if (res.status === 202) return (await res.json()) as ProductExportQueued;\n return res.blob();\n}\n\nconst encode = encodeURIComponent;\n\n// --- Resource namespaces ---------------------------------------------------------------------\n\nfunction createProducts(request: RequestFn, requestBlob: BlobRequestFn) {\n return {\n /**\n * POST /v1/products - the single generation entrypoint. Supply exactly one source\n * (`image_paths` | `image_urls` | `video_url` | `source_url` | `model_url` |\n * `resume_token`). A fresh request ALWAYS parks (200, `status === \"parked\"`, nothing\n * written): the plan, the model verdict and the price come back inside `resume_token`,\n * which you POST back to commit (202, `ProductCreated`). A `model_url` commits at once.\n */\n create: (params: ProductCreateParams) =>\n request<ProductCreateResult>(\"POST\", \"/v1/products\", { body: params }),\n /**\n * GET /v1/products - the catalog: one org (`org_id`, or the caller's only org), or\n * `scope: \"all\"` for every org an agency member or staff reaches. Filters AND together.\n */\n list: (params?: ProductListParams) =>\n request<ProductList>(\"GET\", \"/v1/products\", { query: params as QueryParams | undefined }),\n /** GET /v1/products/{id} - the product with its assets (a `pk_` key gets `PublicProduct`). */\n retrieve: (id: string) => request<Product>(\"GET\", `/v1/products/${encode(id)}`),\n /**\n * PATCH /v1/products/{id} - the customer's fields, the user-set status and the publish\n * toggle. 409 on a `custom_id` another product in the org already holds.\n */\n update: (id: string, params: ProductUpdateParams) =>\n request<Product>(\"PATCH\", `/v1/products/${encode(id)}`, { body: params }),\n /** DELETE /v1/products/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/products/${encode(id)}`),\n /** DELETE /v1/products?ids=... - per-id affected/not_found/forbidden buckets. */\n batchDelete: (ids: readonly string[]) =>\n request<ProductBatchResult>(\"DELETE\", \"/v1/products\", { query: { ids } }),\n /** POST /v1/products/publish - the bulk publish toggle; one product toggles through `update`. */\n publish: (params: ProductPublishParams) =>\n request<ProductBatchResult>(\"POST\", \"/v1/products/publish\", { body: params }),\n /** GET /v1/products/tags - distinct tags across the same scope the list takes. */\n tags: (params?: TagsParams) =>\n request<Tags>(\"GET\", \"/v1/products/tags\", { query: params as QueryParams | undefined }),\n /** POST /v1/products/{id}/review - the review flush: image and model decisions plus the\n * product's user-set status, validated together and applied in order. */\n review: (id: string, params: ProductReviewParams) =>\n request<ProductReviewResult>(\"POST\", `/v1/products/${encode(id)}/review`, { body: params }),\n /** PATCH /v1/products/{id}/images/{image_id} - one image's decision and overrides. */\n updateImage: (id: string, imageId: string, params: ProductImagePatchParams) =>\n request<ProductImage>(\"PATCH\", `/v1/products/${encode(id)}/images/${encode(imageId)}`, {\n body: params,\n }),\n /** PATCH /v1/products/{id}/model - the model's review decision. */\n updateModel: (id: string, params: ProductModelPatchParams) =>\n request<ProductModel>(\"PATCH\", `/v1/products/${encode(id)}/model`, { body: params }),\n /**\n * POST /v1/products/{id}/model/rescale - re-apply real-world scale to the GLB in place.\n * Every supplied axis is applied to the geometry; `unit` covers them (omitted = cm) and is\n * remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.\n */\n rescale: (id: string, params: ProductRescaleParams) =>\n request<Product>(\"POST\", `/v1/products/${encode(id)}/model/rescale`, { body: params }),\n /**\n * POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way\n * to its bytes (no response carries a model file URL). It works once, until `expires_at`\n * (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`\n * key the product must be published and the ticket is bound to the page's Origin; with an\n * `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).\n */\n createTicket: (id: string, params: TicketParams) =>\n request<Ticket>(\"POST\", `/v1/products/${encode(id)}/tickets`, { body: params }),\n /** POST /v1/products/{id}/order - park the missing half (images or a model); commit the\n * returned `resume_token` with `create`. */\n order: (id: string, params: ProductOrderParams) =>\n request<ProductOrderParked>(\"POST\", `/v1/products/${encode(id)}/order`, { body: params }),\n /** GET /v1/products/export - a ZIP of finished images (never the 3D model), resolved as a\n * `Blob`; or `ProductExportQueued` (202) when the images are being finished in the\n * background and a download link is emailed - repeat the request later to stream it. */\n export: (params?: ProductExportParams) =>\n requestBlob(\"GET\", \"/v1/products/export\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createApiKeys(request: RequestFn) {\n return {\n /** GET /v1/api_keys */\n list: () => request<ApiKeyList>(\"GET\", \"/v1/api_keys\"),\n /** POST /v1/api_keys - the full key is returned exactly once. */\n create: (params: ApiKeyCreateParams) =>\n request<ApiKeyCreated>(\"POST\", \"/v1/api_keys\", { body: params }),\n /** DELETE /v1/api_keys/{id} - soft revoke. */\n revoke: (id: string) => request<void>(\"DELETE\", `/v1/api_keys/${encode(id)}`),\n };\n}\n\nfunction createUsage(request: RequestFn) {\n return {\n /** GET /v1/usage - buckets and totals over the window; `group_by: \"product\"` adds the\n * window's best-performing products in `by_product`. */\n get: (params?: UsageParams) =>\n request<Usage>(\"GET\", \"/v1/usage\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createWebhooks(request: RequestFn) {\n return {\n /** GET /v1/webhooks */\n list: () => request<WebhookEndpointList>(\"GET\", \"/v1/webhooks\"),\n /** POST /v1/webhooks - includes the signing secret. */\n create: (params: WebhookEndpointCreateParams) =>\n request<WebhookEndpointCreated>(\"POST\", \"/v1/webhooks\", { body: params }),\n /** GET /v1/webhooks/{id} */\n retrieve: (id: string) => request<WebhookEndpoint>(\"GET\", `/v1/webhooks/${encode(id)}`),\n /** PATCH /v1/webhooks/{id} */\n update: (id: string, params: WebhookEndpointUpdateParams) =>\n request<WebhookEndpoint>(\"PATCH\", `/v1/webhooks/${encode(id)}`, { body: params }),\n /** DELETE /v1/webhooks/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/webhooks/${encode(id)}`),\n /** GET /v1/webhooks/{id}/deliveries */\n listDeliveries: (id: string, params?: WebhookDeliveriesParams) =>\n request<WebhookDeliveryList>(\"GET\", `/v1/webhooks/${encode(id)}/deliveries`, {\n query: params as QueryParams | undefined,\n }),\n };\n}\n\nfunction createProjects(request: RequestFn) {\n return {\n /** GET /v1/projects */\n list: () => request<ProjectList>(\"GET\", \"/v1/projects\"),\n /** GET /v1/projects/{id} */\n retrieve: (id: string) => request<Project>(\"GET\", `/v1/projects/${encode(id)}`),\n };\n}\n\nfunction createBeacon(request: RequestFn) {\n return {\n /** POST /v1/beacon - ingest a view/AR analytics event (204). */\n send: (params: BeaconParams) => request<void>(\"POST\", \"/v1/beacon\", { body: params }),\n };\n}\n\n// --- Client ----------------------------------------------------------------------------------\n\nexport class Viewrium {\n readonly baseUrl: string;\n readonly products: ReturnType<typeof createProducts>;\n readonly apiKeys: ReturnType<typeof createApiKeys>;\n readonly usage: ReturnType<typeof createUsage>;\n readonly webhooks: ReturnType<typeof createWebhooks>;\n readonly projects: ReturnType<typeof createProjects>;\n readonly beacon: ReturnType<typeof createBeacon>;\n\n readonly #request: RequestFn;\n\n constructor(options: ViewriumOptions) {\n if (!options || !options.apiKey) {\n throw new ViewriumError(\"A Viewrium client requires an `apiKey` (sk_ or pk_).\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\n const provided = options.fetch;\n const globalFetch = typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined;\n const fetchImpl = provided ?? globalFetch;\n if (!fetchImpl) {\n throw new ViewriumError(\"No fetch implementation available; pass `options.fetch`.\");\n }\n\n const transport: Transport = { baseUrl: this.baseUrl, apiKey: options.apiKey, fetch: fetchImpl };\n const request: RequestFn = <T>(method: string, path: string, opts?: RequestOptions) =>\n doRequest<T>(transport, method, path, opts);\n const requestBlob: BlobRequestFn = (method, path, opts) =>\n doBlobRequest(transport, method, path, opts);\n this.#request = request;\n\n this.products = createProducts(request, requestBlob);\n this.apiKeys = createApiKeys(request);\n this.usage = createUsage(request);\n this.webhooks = createWebhooks(request);\n this.projects = createProjects(request);\n this.beacon = createBeacon(request);\n }\n\n /** GET /v1/health */\n health(): Promise<Health> {\n return this.#request<Health>(\"GET\", \"/v1/health\");\n }\n}\n\n/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */\nexport function createViewrium(options: ViewriumOptions): Viewrium {\n return new Viewrium(options);\n}\n","/**\n * Typed errors for the Viewrium SDK.\n *\n * The API returns one error envelope: `{ \"error\": { code, message, details } }`.\n * Non-2xx responses are parsed into a `ViewriumError` subclass keyed by HTTP status;\n * consumers branch with `instanceof`.\n */\n\nexport interface ViewriumErrorOptions {\n status?: number | null;\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class ViewriumError extends Error {\n /** HTTP status, or `null` for transport/connection failures. */\n readonly status: number | null;\n /** Machine-readable code from the error envelope. */\n readonly code: string;\n /** Optional structured details from the error envelope. */\n readonly details: unknown;\n\n constructor(message: string, options: ViewriumErrorOptions = {}) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = new.target.name;\n this.status = options.status ?? null;\n this.code = options.code ?? \"error\";\n this.details = options.details;\n }\n}\n\n/** 400 - malformed request. */\nexport class BadRequestError extends ViewriumError {}\n/** 401 - missing or invalid API key. */\nexport class AuthenticationError extends ViewriumError {}\n/**\n * 402 - the organization's plan does not allow this: no plan, a used free trial, a failed payment,\n * or past the plan's active-product cap. `details.reason` says which.\n */\nexport class PaymentRequiredError extends ViewriumError {}\n/** 403 - the key is valid but not allowed (scope, origin, or role). */\nexport class PermissionError extends ViewriumError {}\n/** 404 - no such resource. */\nexport class NotFoundError extends ViewriumError {}\n/** 409 - conflict (e.g. duplicate `custom_id`, model not in a rescalable state). */\nexport class ConflictError extends ViewriumError {}\n/** 422 - request failed validation. */\nexport class UnprocessableEntityError extends ViewriumError {}\n/** 429 - rate limited, or not enough credits (`code: \"quota_exceeded\"`, `details.shortfall_credits`). */\nexport class RateLimitError extends ViewriumError {}\n/** 5xx - the API failed to handle the request. */\nexport class ServerError extends ViewriumError {}\n/** The request never reached the API (DNS/TLS/offline/abort). */\nexport class ConnectionError extends ViewriumError {}\n\nexport interface ApiErrorPayload {\n code: string;\n message: string;\n details?: unknown;\n}\n\nconst BY_STATUS: Record<number, new (message: string, options: ViewriumErrorOptions) => ViewriumError> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 402: PaymentRequiredError,\n 403: PermissionError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n};\n\n/** Build the right `ViewriumError` subclass for an HTTP status + parsed envelope. */\nexport function createApiError(status: number, payload: ApiErrorPayload): ViewriumError {\n const options: ViewriumErrorOptions = { status, code: payload.code, details: payload.details };\n const Specific = BY_STATUS[status];\n if (Specific) {\n return new Specific(payload.message, options);\n }\n if (status >= 500) {\n return new ServerError(payload.message, options);\n }\n return new ViewriumError(payload.message, options);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -225,7 +225,8 @@ interface paths {
|
|
|
225
225
|
/**
|
|
226
226
|
* Rescale Product Model
|
|
227
227
|
* @description Re-apply real-world scale to the GLB in place; the product keeps the axes the
|
|
228
|
-
* customer stated. 409 without a ready model
|
|
228
|
+
* customer stated. 409 without a ready model, or for one with no measurable geometry; 502
|
|
229
|
+
* when the resize itself fails.
|
|
229
230
|
*/
|
|
230
231
|
post: operations["rescale_product_model_v1_products__id__model_rescale_post"];
|
|
231
232
|
delete?: never;
|
|
@@ -234,6 +235,38 @@ interface paths {
|
|
|
234
235
|
patch?: never;
|
|
235
236
|
trace?: never;
|
|
236
237
|
};
|
|
238
|
+
"/v1/products/{id}/tickets": {
|
|
239
|
+
parameters: {
|
|
240
|
+
query?: never;
|
|
241
|
+
header?: never;
|
|
242
|
+
path?: never;
|
|
243
|
+
cookie?: never;
|
|
244
|
+
};
|
|
245
|
+
get?: never;
|
|
246
|
+
put?: never;
|
|
247
|
+
/**
|
|
248
|
+
* Create Product Ticket
|
|
249
|
+
* @description A single-use URL for the product's 3D file - the ONLY way to its bytes (there is no
|
|
250
|
+
* model file URL in any response). It answers `model/gltf-binary` ONCE, until `expires_at`
|
|
251
|
+
* (60 s for `view`, 30 s for `ar`). Mint it right before the viewer loads, never store it.
|
|
252
|
+
*
|
|
253
|
+
* - publishable key: a PUBLISHED product only; the ticket is bound to the page's Origin (one
|
|
254
|
+
* of the org's allowed domains).
|
|
255
|
+
* - secret key / session: the org's own products, drafts included; bound to `origin` (the
|
|
256
|
+
* page that will fetch the file), else the request's Origin.
|
|
257
|
+
*
|
|
258
|
+
* A `view` ticket streams only to its bound origin; an `ar` ticket also to Android's Scene
|
|
259
|
+
* Viewer, which sends none. 404 for a product the caller cannot see (or, for a pk, one that is
|
|
260
|
+
* not published); 409 without a ready model; 400 for an origin outside the allowed domains;
|
|
261
|
+
* 429 past the mint limits.
|
|
262
|
+
*/
|
|
263
|
+
post: operations["create_product_ticket_v1_products__id__tickets_post"];
|
|
264
|
+
delete?: never;
|
|
265
|
+
options?: never;
|
|
266
|
+
head?: never;
|
|
267
|
+
patch?: never;
|
|
268
|
+
trace?: never;
|
|
269
|
+
};
|
|
237
270
|
"/v1/products/{id}/model": {
|
|
238
271
|
parameters: {
|
|
239
272
|
query?: never;
|
|
@@ -976,7 +1009,12 @@ interface components {
|
|
|
976
1009
|
ProductModelPatch: {
|
|
977
1010
|
review_status?: components["schemas"]["ReviewStatus"] | null;
|
|
978
1011
|
};
|
|
979
|
-
/**
|
|
1012
|
+
/**
|
|
1013
|
+
* ProductModelResponse
|
|
1014
|
+
* @description The product's 3D model. There is NO file URL here, on purpose (Dave, 2026-10-16: no
|
|
1015
|
+
* model file URL anywhere, merchants included): the bytes are reached only through a
|
|
1016
|
+
* single-use ticket, `POST /v1/products/{id}/tickets`.
|
|
1017
|
+
*/
|
|
980
1018
|
ProductModelResponse: {
|
|
981
1019
|
/**
|
|
982
1020
|
* Id
|
|
@@ -986,8 +1024,6 @@ interface components {
|
|
|
986
1024
|
status: components["schemas"]["AssetStatus"];
|
|
987
1025
|
/** @default pending */
|
|
988
1026
|
review_status: components["schemas"]["ReviewStatus"];
|
|
989
|
-
/** Glb Url */
|
|
990
|
-
glb_url?: string | null;
|
|
991
1027
|
/** Thumbnail Url */
|
|
992
1028
|
thumbnail_url?: string | null;
|
|
993
1029
|
/** Scale Factor */
|
|
@@ -1555,7 +1591,8 @@ interface components {
|
|
|
1555
1591
|
* storefronts, so anything here is world-readable. An allow-list fails closed: a field
|
|
1556
1592
|
* added to ProductResponse tomorrow is NOT exposed here until someone adds it on purpose.
|
|
1557
1593
|
* Only a PUBLISHED product with a READY model is served at all (the `public_products()`
|
|
1558
|
-
* gate, D72); the images half is post-launch backlog #4.
|
|
1594
|
+
* gate, D72); the images half is post-launch backlog #4. The model file itself is NOT here:
|
|
1595
|
+
* the embed mints a single-use ticket for it (`POST /v1/products/{id}/tickets`).
|
|
1559
1596
|
*/
|
|
1560
1597
|
PublicProductResponse: {
|
|
1561
1598
|
/**
|
|
@@ -1565,11 +1602,14 @@ interface components {
|
|
|
1565
1602
|
id: string;
|
|
1566
1603
|
/** Title */
|
|
1567
1604
|
title: string;
|
|
1568
|
-
/** Model Url */
|
|
1569
|
-
model_url?: string | null;
|
|
1570
1605
|
/** Thumbnail Url */
|
|
1571
1606
|
thumbnail_url?: string | null;
|
|
1572
1607
|
model_status?: components["schemas"]["AssetStatus"] | null;
|
|
1608
|
+
/**
|
|
1609
|
+
* Wall Mounted
|
|
1610
|
+
* @default false
|
|
1611
|
+
*/
|
|
1612
|
+
wall_mounted: boolean;
|
|
1573
1613
|
};
|
|
1574
1614
|
/**
|
|
1575
1615
|
* ReviewStatus
|
|
@@ -1668,6 +1708,47 @@ interface components {
|
|
|
1668
1708
|
/** Data */
|
|
1669
1709
|
data: components["schemas"]["TagCount"][];
|
|
1670
1710
|
};
|
|
1711
|
+
/**
|
|
1712
|
+
* TicketKind
|
|
1713
|
+
* @description `model_tickets.kind` (migration section 46): what one single-use model ticket is for.
|
|
1714
|
+
* `view` streams to the page that minted it (its Origin must match); `ar` is Android's Scene
|
|
1715
|
+
* Viewer, which re-downloads the file outside the page and sends no Origin.
|
|
1716
|
+
* @enum {string}
|
|
1717
|
+
*/
|
|
1718
|
+
TicketKind: "view" | "ar";
|
|
1719
|
+
/**
|
|
1720
|
+
* TicketRequest
|
|
1721
|
+
* @description POST /v1/products/{id}/tickets - one single-use ticket for the product's 3D file.
|
|
1722
|
+
*
|
|
1723
|
+
* `view` is for the page itself: the redeem streams the file only to a request whose Origin
|
|
1724
|
+
* is the one the ticket is bound to. `ar` is for Android's Scene Viewer, which downloads the
|
|
1725
|
+
* file outside the page (it sends no Origin) - mint it on the AR tap, it lives 30 s.
|
|
1726
|
+
*
|
|
1727
|
+
* `origin` is for a SERVER minting on a page's behalf (a secret key, or our dashboard's own
|
|
1728
|
+
* server): the origin of the page that will fetch the file. It must be one of the org's
|
|
1729
|
+
* allowed domains (or ours). A publishable key never sends it - a pk ticket is bound to the
|
|
1730
|
+
* request's own Origin, which the key's allowed domains already gated.
|
|
1731
|
+
*/
|
|
1732
|
+
TicketRequest: {
|
|
1733
|
+
kind: components["schemas"]["TicketKind"];
|
|
1734
|
+
/** Origin */
|
|
1735
|
+
origin?: string | null;
|
|
1736
|
+
};
|
|
1737
|
+
/**
|
|
1738
|
+
* TicketResponse
|
|
1739
|
+
* @description A single-use URL for the product's 3D file (`model/gltf-binary`). It works ONCE, until
|
|
1740
|
+
* `expires_at`, and for a view ticket only from its bound origin. Do not store it.
|
|
1741
|
+
*/
|
|
1742
|
+
TicketResponse: {
|
|
1743
|
+
/** Url */
|
|
1744
|
+
url: string;
|
|
1745
|
+
kind: components["schemas"]["TicketKind"];
|
|
1746
|
+
/**
|
|
1747
|
+
* Expires At
|
|
1748
|
+
* Format: date-time
|
|
1749
|
+
*/
|
|
1750
|
+
expires_at: string;
|
|
1751
|
+
};
|
|
1671
1752
|
/** UsageBucket */
|
|
1672
1753
|
UsageBucket: {
|
|
1673
1754
|
/**
|
|
@@ -2423,6 +2504,41 @@ interface operations {
|
|
|
2423
2504
|
};
|
|
2424
2505
|
};
|
|
2425
2506
|
};
|
|
2507
|
+
create_product_ticket_v1_products__id__tickets_post: {
|
|
2508
|
+
parameters: {
|
|
2509
|
+
query?: never;
|
|
2510
|
+
header?: never;
|
|
2511
|
+
path: {
|
|
2512
|
+
id: string;
|
|
2513
|
+
};
|
|
2514
|
+
cookie?: never;
|
|
2515
|
+
};
|
|
2516
|
+
requestBody: {
|
|
2517
|
+
content: {
|
|
2518
|
+
"application/json": components["schemas"]["TicketRequest"];
|
|
2519
|
+
};
|
|
2520
|
+
};
|
|
2521
|
+
responses: {
|
|
2522
|
+
/** @description Successful Response */
|
|
2523
|
+
200: {
|
|
2524
|
+
headers: {
|
|
2525
|
+
[name: string]: unknown;
|
|
2526
|
+
};
|
|
2527
|
+
content: {
|
|
2528
|
+
"application/json": components["schemas"]["TicketResponse"];
|
|
2529
|
+
};
|
|
2530
|
+
};
|
|
2531
|
+
/** @description Validation Error */
|
|
2532
|
+
422: {
|
|
2533
|
+
headers: {
|
|
2534
|
+
[name: string]: unknown;
|
|
2535
|
+
};
|
|
2536
|
+
content: {
|
|
2537
|
+
"application/json": components["schemas"]["HTTPValidationError"];
|
|
2538
|
+
};
|
|
2539
|
+
};
|
|
2540
|
+
};
|
|
2541
|
+
};
|
|
2426
2542
|
update_product_model_v1_products__id__model_patch: {
|
|
2427
2543
|
parameters: {
|
|
2428
2544
|
query?: never;
|
|
@@ -2942,7 +3058,8 @@ type ProductGetResult = JsonContent<paths["/v1/products/{id}"]["get"]["responses
|
|
|
2942
3058
|
type Product = Extract<ProductGetResult, {
|
|
2943
3059
|
org_id: string;
|
|
2944
3060
|
}>;
|
|
2945
|
-
/** The `pk_` view of a published product: id, title,
|
|
3061
|
+
/** The `pk_` view of a published product: id, title, thumbnail_url, model_status, wall_mounted. The model
|
|
3062
|
+
* file itself is reached through `products.createTicket`. */
|
|
2946
3063
|
type PublicProduct = Exclude<ProductGetResult, {
|
|
2947
3064
|
org_id: string;
|
|
2948
3065
|
}>;
|
|
@@ -2980,6 +3097,10 @@ type ProductImage = JsonContent<paths["/v1/products/{id}/images/{image_id}"]["pa
|
|
|
2980
3097
|
type ProductModelPatchParams = JsonContent<NonNullable<paths["/v1/products/{id}/model"]["patch"]["requestBody"]>>;
|
|
2981
3098
|
type ProductModel = JsonContent<paths["/v1/products/{id}/model"]["patch"]["responses"][200]>;
|
|
2982
3099
|
type ProductRescaleParams = JsonContent<paths["/v1/products/{id}/model/rescale"]["post"]["requestBody"]>;
|
|
3100
|
+
/** POST /v1/products/{id}/tickets - `{ kind: "view" | "ar", origin? }`. */
|
|
3101
|
+
type TicketParams = JsonContent<paths["/v1/products/{id}/tickets"]["post"]["requestBody"]>;
|
|
3102
|
+
/** A single-use model URL: `{ url, kind, expires_at }`. */
|
|
3103
|
+
type Ticket = JsonContent<paths["/v1/products/{id}/tickets"]["post"]["responses"][200]>;
|
|
2983
3104
|
/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through
|
|
2984
3105
|
* `products.create({ resume_token })`. */
|
|
2985
3106
|
type ProductOrderParams = JsonContent<paths["/v1/products/{id}/order"]["post"]["requestBody"]>;
|
|
@@ -3194,7 +3315,6 @@ declare function createProducts(request: RequestFn, requestBlob: BlobRequestFn):
|
|
|
3194
3315
|
id: string;
|
|
3195
3316
|
status: components["schemas"]["AssetStatus"];
|
|
3196
3317
|
review_status: components["schemas"]["ReviewStatus"];
|
|
3197
|
-
glb_url?: string | null;
|
|
3198
3318
|
thumbnail_url?: string | null;
|
|
3199
3319
|
scale_factor?: number | null;
|
|
3200
3320
|
width_axis?: ("x" | "z") | null;
|
|
@@ -3249,6 +3369,18 @@ declare function createProducts(request: RequestFn, requestBlob: BlobRequestFn):
|
|
|
3249
3369
|
warnings?: components["schemas"]["ProductWarning"][];
|
|
3250
3370
|
open_request_count: number;
|
|
3251
3371
|
}>;
|
|
3372
|
+
/**
|
|
3373
|
+
* POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way
|
|
3374
|
+
* to its bytes (no response carries a model file URL). It works once, until `expires_at`
|
|
3375
|
+
* (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`
|
|
3376
|
+
* key the product must be published and the ticket is bound to the page's Origin; with an
|
|
3377
|
+
* `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).
|
|
3378
|
+
*/
|
|
3379
|
+
createTicket: (id: string, params: TicketParams) => Promise<{
|
|
3380
|
+
url: string;
|
|
3381
|
+
kind: components["schemas"]["TicketKind"];
|
|
3382
|
+
expires_at: string;
|
|
3383
|
+
}>;
|
|
3252
3384
|
/** POST /v1/products/{id}/order - park the missing half (images or a model); commit the
|
|
3253
3385
|
* returned `resume_token` with `create`. */
|
|
3254
3386
|
order: (id: string, params: ProductOrderParams) => Promise<{
|
|
@@ -3285,7 +3417,7 @@ declare function createApiKeys(request: RequestFn): {
|
|
|
3285
3417
|
id: string;
|
|
3286
3418
|
org_id?: string | null;
|
|
3287
3419
|
project_id?: string | null;
|
|
3288
|
-
key_type: components["schemas"]["KeyType"];
|
|
3420
|
+
key_type: components["schemas"] /** PATCH /v1/webhooks/{id} */["KeyType"];
|
|
3289
3421
|
key_prefix: string;
|
|
3290
3422
|
name?: string | null;
|
|
3291
3423
|
scopes: string[];
|
|
@@ -3400,4 +3532,4 @@ declare class Viewrium {
|
|
|
3400
3532
|
/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */
|
|
3401
3533
|
declare function createViewrium(options: ViewriumOptions): Viewrium;
|
|
3402
3534
|
|
|
3403
|
-
export { type ApiErrorPayload, type ApiKeyCreateParams, type ApiKeyCreated, type ApiKeyList, AuthenticationError, BadRequestError, type BeaconParams, ConflictError, ConnectionError, type FetchLike, type GateFlags, type Health, type ModelPlan, NotFoundError, PaymentRequiredError, PermissionError, type PlannedView, type Product, type ProductBatchResult, type ProductCost, type ProductCreateParams, type ProductCreateResult, type ProductCreated, type ProductExportParams, type ProductExportQueued, type ProductImage, type ProductImagePatchParams, type ProductList, type ProductListParams, type ProductModel, type ProductModelPatchParams, type ProductOrderParams, type ProductOrderParked, type ProductParked, type ProductPublishParams, type ProductRescaleParams, type ProductReviewParams, type ProductReviewResult, type ProductSummary, type ProductUpdateParams, type Project, type ProjectList, type PublicProduct, RateLimitError, ServerError, type Tags, type TagsParams, UnprocessableEntityError, type Usage, type UsageParams, type UsageProductTotals, Viewrium, ViewriumError, type ViewriumErrorOptions, type ViewriumOptions, type WebhookDeliveriesParams, type WebhookDeliveryList, type WebhookEndpoint, type WebhookEndpointCreateParams, type WebhookEndpointCreated, type WebhookEndpointList, type WebhookEndpointUpdateParams, createApiError, createViewrium };
|
|
3535
|
+
export { type ApiErrorPayload, type ApiKeyCreateParams, type ApiKeyCreated, type ApiKeyList, AuthenticationError, BadRequestError, type BeaconParams, ConflictError, ConnectionError, type FetchLike, type GateFlags, type Health, type ModelPlan, NotFoundError, PaymentRequiredError, PermissionError, type PlannedView, type Product, type ProductBatchResult, type ProductCost, type ProductCreateParams, type ProductCreateResult, type ProductCreated, type ProductExportParams, type ProductExportQueued, type ProductImage, type ProductImagePatchParams, type ProductList, type ProductListParams, type ProductModel, type ProductModelPatchParams, type ProductOrderParams, type ProductOrderParked, type ProductParked, type ProductPublishParams, type ProductRescaleParams, type ProductReviewParams, type ProductReviewResult, type ProductSummary, type ProductUpdateParams, type Project, type ProjectList, type PublicProduct, RateLimitError, ServerError, type Tags, type TagsParams, type Ticket, type TicketParams, UnprocessableEntityError, type Usage, type UsageParams, type UsageProductTotals, Viewrium, ViewriumError, type ViewriumErrorOptions, type ViewriumOptions, type WebhookDeliveriesParams, type WebhookDeliveryList, type WebhookEndpoint, type WebhookEndpointCreateParams, type WebhookEndpointCreated, type WebhookEndpointList, type WebhookEndpointUpdateParams, createApiError, createViewrium };
|
package/dist/index.d.ts
CHANGED
|
@@ -225,7 +225,8 @@ interface paths {
|
|
|
225
225
|
/**
|
|
226
226
|
* Rescale Product Model
|
|
227
227
|
* @description Re-apply real-world scale to the GLB in place; the product keeps the axes the
|
|
228
|
-
* customer stated. 409 without a ready model
|
|
228
|
+
* customer stated. 409 without a ready model, or for one with no measurable geometry; 502
|
|
229
|
+
* when the resize itself fails.
|
|
229
230
|
*/
|
|
230
231
|
post: operations["rescale_product_model_v1_products__id__model_rescale_post"];
|
|
231
232
|
delete?: never;
|
|
@@ -234,6 +235,38 @@ interface paths {
|
|
|
234
235
|
patch?: never;
|
|
235
236
|
trace?: never;
|
|
236
237
|
};
|
|
238
|
+
"/v1/products/{id}/tickets": {
|
|
239
|
+
parameters: {
|
|
240
|
+
query?: never;
|
|
241
|
+
header?: never;
|
|
242
|
+
path?: never;
|
|
243
|
+
cookie?: never;
|
|
244
|
+
};
|
|
245
|
+
get?: never;
|
|
246
|
+
put?: never;
|
|
247
|
+
/**
|
|
248
|
+
* Create Product Ticket
|
|
249
|
+
* @description A single-use URL for the product's 3D file - the ONLY way to its bytes (there is no
|
|
250
|
+
* model file URL in any response). It answers `model/gltf-binary` ONCE, until `expires_at`
|
|
251
|
+
* (60 s for `view`, 30 s for `ar`). Mint it right before the viewer loads, never store it.
|
|
252
|
+
*
|
|
253
|
+
* - publishable key: a PUBLISHED product only; the ticket is bound to the page's Origin (one
|
|
254
|
+
* of the org's allowed domains).
|
|
255
|
+
* - secret key / session: the org's own products, drafts included; bound to `origin` (the
|
|
256
|
+
* page that will fetch the file), else the request's Origin.
|
|
257
|
+
*
|
|
258
|
+
* A `view` ticket streams only to its bound origin; an `ar` ticket also to Android's Scene
|
|
259
|
+
* Viewer, which sends none. 404 for a product the caller cannot see (or, for a pk, one that is
|
|
260
|
+
* not published); 409 without a ready model; 400 for an origin outside the allowed domains;
|
|
261
|
+
* 429 past the mint limits.
|
|
262
|
+
*/
|
|
263
|
+
post: operations["create_product_ticket_v1_products__id__tickets_post"];
|
|
264
|
+
delete?: never;
|
|
265
|
+
options?: never;
|
|
266
|
+
head?: never;
|
|
267
|
+
patch?: never;
|
|
268
|
+
trace?: never;
|
|
269
|
+
};
|
|
237
270
|
"/v1/products/{id}/model": {
|
|
238
271
|
parameters: {
|
|
239
272
|
query?: never;
|
|
@@ -976,7 +1009,12 @@ interface components {
|
|
|
976
1009
|
ProductModelPatch: {
|
|
977
1010
|
review_status?: components["schemas"]["ReviewStatus"] | null;
|
|
978
1011
|
};
|
|
979
|
-
/**
|
|
1012
|
+
/**
|
|
1013
|
+
* ProductModelResponse
|
|
1014
|
+
* @description The product's 3D model. There is NO file URL here, on purpose (Dave, 2026-10-16: no
|
|
1015
|
+
* model file URL anywhere, merchants included): the bytes are reached only through a
|
|
1016
|
+
* single-use ticket, `POST /v1/products/{id}/tickets`.
|
|
1017
|
+
*/
|
|
980
1018
|
ProductModelResponse: {
|
|
981
1019
|
/**
|
|
982
1020
|
* Id
|
|
@@ -986,8 +1024,6 @@ interface components {
|
|
|
986
1024
|
status: components["schemas"]["AssetStatus"];
|
|
987
1025
|
/** @default pending */
|
|
988
1026
|
review_status: components["schemas"]["ReviewStatus"];
|
|
989
|
-
/** Glb Url */
|
|
990
|
-
glb_url?: string | null;
|
|
991
1027
|
/** Thumbnail Url */
|
|
992
1028
|
thumbnail_url?: string | null;
|
|
993
1029
|
/** Scale Factor */
|
|
@@ -1555,7 +1591,8 @@ interface components {
|
|
|
1555
1591
|
* storefronts, so anything here is world-readable. An allow-list fails closed: a field
|
|
1556
1592
|
* added to ProductResponse tomorrow is NOT exposed here until someone adds it on purpose.
|
|
1557
1593
|
* Only a PUBLISHED product with a READY model is served at all (the `public_products()`
|
|
1558
|
-
* gate, D72); the images half is post-launch backlog #4.
|
|
1594
|
+
* gate, D72); the images half is post-launch backlog #4. The model file itself is NOT here:
|
|
1595
|
+
* the embed mints a single-use ticket for it (`POST /v1/products/{id}/tickets`).
|
|
1559
1596
|
*/
|
|
1560
1597
|
PublicProductResponse: {
|
|
1561
1598
|
/**
|
|
@@ -1565,11 +1602,14 @@ interface components {
|
|
|
1565
1602
|
id: string;
|
|
1566
1603
|
/** Title */
|
|
1567
1604
|
title: string;
|
|
1568
|
-
/** Model Url */
|
|
1569
|
-
model_url?: string | null;
|
|
1570
1605
|
/** Thumbnail Url */
|
|
1571
1606
|
thumbnail_url?: string | null;
|
|
1572
1607
|
model_status?: components["schemas"]["AssetStatus"] | null;
|
|
1608
|
+
/**
|
|
1609
|
+
* Wall Mounted
|
|
1610
|
+
* @default false
|
|
1611
|
+
*/
|
|
1612
|
+
wall_mounted: boolean;
|
|
1573
1613
|
};
|
|
1574
1614
|
/**
|
|
1575
1615
|
* ReviewStatus
|
|
@@ -1668,6 +1708,47 @@ interface components {
|
|
|
1668
1708
|
/** Data */
|
|
1669
1709
|
data: components["schemas"]["TagCount"][];
|
|
1670
1710
|
};
|
|
1711
|
+
/**
|
|
1712
|
+
* TicketKind
|
|
1713
|
+
* @description `model_tickets.kind` (migration section 46): what one single-use model ticket is for.
|
|
1714
|
+
* `view` streams to the page that minted it (its Origin must match); `ar` is Android's Scene
|
|
1715
|
+
* Viewer, which re-downloads the file outside the page and sends no Origin.
|
|
1716
|
+
* @enum {string}
|
|
1717
|
+
*/
|
|
1718
|
+
TicketKind: "view" | "ar";
|
|
1719
|
+
/**
|
|
1720
|
+
* TicketRequest
|
|
1721
|
+
* @description POST /v1/products/{id}/tickets - one single-use ticket for the product's 3D file.
|
|
1722
|
+
*
|
|
1723
|
+
* `view` is for the page itself: the redeem streams the file only to a request whose Origin
|
|
1724
|
+
* is the one the ticket is bound to. `ar` is for Android's Scene Viewer, which downloads the
|
|
1725
|
+
* file outside the page (it sends no Origin) - mint it on the AR tap, it lives 30 s.
|
|
1726
|
+
*
|
|
1727
|
+
* `origin` is for a SERVER minting on a page's behalf (a secret key, or our dashboard's own
|
|
1728
|
+
* server): the origin of the page that will fetch the file. It must be one of the org's
|
|
1729
|
+
* allowed domains (or ours). A publishable key never sends it - a pk ticket is bound to the
|
|
1730
|
+
* request's own Origin, which the key's allowed domains already gated.
|
|
1731
|
+
*/
|
|
1732
|
+
TicketRequest: {
|
|
1733
|
+
kind: components["schemas"]["TicketKind"];
|
|
1734
|
+
/** Origin */
|
|
1735
|
+
origin?: string | null;
|
|
1736
|
+
};
|
|
1737
|
+
/**
|
|
1738
|
+
* TicketResponse
|
|
1739
|
+
* @description A single-use URL for the product's 3D file (`model/gltf-binary`). It works ONCE, until
|
|
1740
|
+
* `expires_at`, and for a view ticket only from its bound origin. Do not store it.
|
|
1741
|
+
*/
|
|
1742
|
+
TicketResponse: {
|
|
1743
|
+
/** Url */
|
|
1744
|
+
url: string;
|
|
1745
|
+
kind: components["schemas"]["TicketKind"];
|
|
1746
|
+
/**
|
|
1747
|
+
* Expires At
|
|
1748
|
+
* Format: date-time
|
|
1749
|
+
*/
|
|
1750
|
+
expires_at: string;
|
|
1751
|
+
};
|
|
1671
1752
|
/** UsageBucket */
|
|
1672
1753
|
UsageBucket: {
|
|
1673
1754
|
/**
|
|
@@ -2423,6 +2504,41 @@ interface operations {
|
|
|
2423
2504
|
};
|
|
2424
2505
|
};
|
|
2425
2506
|
};
|
|
2507
|
+
create_product_ticket_v1_products__id__tickets_post: {
|
|
2508
|
+
parameters: {
|
|
2509
|
+
query?: never;
|
|
2510
|
+
header?: never;
|
|
2511
|
+
path: {
|
|
2512
|
+
id: string;
|
|
2513
|
+
};
|
|
2514
|
+
cookie?: never;
|
|
2515
|
+
};
|
|
2516
|
+
requestBody: {
|
|
2517
|
+
content: {
|
|
2518
|
+
"application/json": components["schemas"]["TicketRequest"];
|
|
2519
|
+
};
|
|
2520
|
+
};
|
|
2521
|
+
responses: {
|
|
2522
|
+
/** @description Successful Response */
|
|
2523
|
+
200: {
|
|
2524
|
+
headers: {
|
|
2525
|
+
[name: string]: unknown;
|
|
2526
|
+
};
|
|
2527
|
+
content: {
|
|
2528
|
+
"application/json": components["schemas"]["TicketResponse"];
|
|
2529
|
+
};
|
|
2530
|
+
};
|
|
2531
|
+
/** @description Validation Error */
|
|
2532
|
+
422: {
|
|
2533
|
+
headers: {
|
|
2534
|
+
[name: string]: unknown;
|
|
2535
|
+
};
|
|
2536
|
+
content: {
|
|
2537
|
+
"application/json": components["schemas"]["HTTPValidationError"];
|
|
2538
|
+
};
|
|
2539
|
+
};
|
|
2540
|
+
};
|
|
2541
|
+
};
|
|
2426
2542
|
update_product_model_v1_products__id__model_patch: {
|
|
2427
2543
|
parameters: {
|
|
2428
2544
|
query?: never;
|
|
@@ -2942,7 +3058,8 @@ type ProductGetResult = JsonContent<paths["/v1/products/{id}"]["get"]["responses
|
|
|
2942
3058
|
type Product = Extract<ProductGetResult, {
|
|
2943
3059
|
org_id: string;
|
|
2944
3060
|
}>;
|
|
2945
|
-
/** The `pk_` view of a published product: id, title,
|
|
3061
|
+
/** The `pk_` view of a published product: id, title, thumbnail_url, model_status, wall_mounted. The model
|
|
3062
|
+
* file itself is reached through `products.createTicket`. */
|
|
2946
3063
|
type PublicProduct = Exclude<ProductGetResult, {
|
|
2947
3064
|
org_id: string;
|
|
2948
3065
|
}>;
|
|
@@ -2980,6 +3097,10 @@ type ProductImage = JsonContent<paths["/v1/products/{id}/images/{image_id}"]["pa
|
|
|
2980
3097
|
type ProductModelPatchParams = JsonContent<NonNullable<paths["/v1/products/{id}/model"]["patch"]["requestBody"]>>;
|
|
2981
3098
|
type ProductModel = JsonContent<paths["/v1/products/{id}/model"]["patch"]["responses"][200]>;
|
|
2982
3099
|
type ProductRescaleParams = JsonContent<paths["/v1/products/{id}/model/rescale"]["post"]["requestBody"]>;
|
|
3100
|
+
/** POST /v1/products/{id}/tickets - `{ kind: "view" | "ar", origin? }`. */
|
|
3101
|
+
type TicketParams = JsonContent<paths["/v1/products/{id}/tickets"]["post"]["requestBody"]>;
|
|
3102
|
+
/** A single-use model URL: `{ url, kind, expires_at }`. */
|
|
3103
|
+
type Ticket = JsonContent<paths["/v1/products/{id}/tickets"]["post"]["responses"][200]>;
|
|
2983
3104
|
/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through
|
|
2984
3105
|
* `products.create({ resume_token })`. */
|
|
2985
3106
|
type ProductOrderParams = JsonContent<paths["/v1/products/{id}/order"]["post"]["requestBody"]>;
|
|
@@ -3194,7 +3315,6 @@ declare function createProducts(request: RequestFn, requestBlob: BlobRequestFn):
|
|
|
3194
3315
|
id: string;
|
|
3195
3316
|
status: components["schemas"]["AssetStatus"];
|
|
3196
3317
|
review_status: components["schemas"]["ReviewStatus"];
|
|
3197
|
-
glb_url?: string | null;
|
|
3198
3318
|
thumbnail_url?: string | null;
|
|
3199
3319
|
scale_factor?: number | null;
|
|
3200
3320
|
width_axis?: ("x" | "z") | null;
|
|
@@ -3249,6 +3369,18 @@ declare function createProducts(request: RequestFn, requestBlob: BlobRequestFn):
|
|
|
3249
3369
|
warnings?: components["schemas"]["ProductWarning"][];
|
|
3250
3370
|
open_request_count: number;
|
|
3251
3371
|
}>;
|
|
3372
|
+
/**
|
|
3373
|
+
* POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way
|
|
3374
|
+
* to its bytes (no response carries a model file URL). It works once, until `expires_at`
|
|
3375
|
+
* (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`
|
|
3376
|
+
* key the product must be published and the ticket is bound to the page's Origin; with an
|
|
3377
|
+
* `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).
|
|
3378
|
+
*/
|
|
3379
|
+
createTicket: (id: string, params: TicketParams) => Promise<{
|
|
3380
|
+
url: string;
|
|
3381
|
+
kind: components["schemas"]["TicketKind"];
|
|
3382
|
+
expires_at: string;
|
|
3383
|
+
}>;
|
|
3252
3384
|
/** POST /v1/products/{id}/order - park the missing half (images or a model); commit the
|
|
3253
3385
|
* returned `resume_token` with `create`. */
|
|
3254
3386
|
order: (id: string, params: ProductOrderParams) => Promise<{
|
|
@@ -3285,7 +3417,7 @@ declare function createApiKeys(request: RequestFn): {
|
|
|
3285
3417
|
id: string;
|
|
3286
3418
|
org_id?: string | null;
|
|
3287
3419
|
project_id?: string | null;
|
|
3288
|
-
key_type: components["schemas"]["KeyType"];
|
|
3420
|
+
key_type: components["schemas"] /** PATCH /v1/webhooks/{id} */["KeyType"];
|
|
3289
3421
|
key_prefix: string;
|
|
3290
3422
|
name?: string | null;
|
|
3291
3423
|
scopes: string[];
|
|
@@ -3400,4 +3532,4 @@ declare class Viewrium {
|
|
|
3400
3532
|
/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */
|
|
3401
3533
|
declare function createViewrium(options: ViewriumOptions): Viewrium;
|
|
3402
3534
|
|
|
3403
|
-
export { type ApiErrorPayload, type ApiKeyCreateParams, type ApiKeyCreated, type ApiKeyList, AuthenticationError, BadRequestError, type BeaconParams, ConflictError, ConnectionError, type FetchLike, type GateFlags, type Health, type ModelPlan, NotFoundError, PaymentRequiredError, PermissionError, type PlannedView, type Product, type ProductBatchResult, type ProductCost, type ProductCreateParams, type ProductCreateResult, type ProductCreated, type ProductExportParams, type ProductExportQueued, type ProductImage, type ProductImagePatchParams, type ProductList, type ProductListParams, type ProductModel, type ProductModelPatchParams, type ProductOrderParams, type ProductOrderParked, type ProductParked, type ProductPublishParams, type ProductRescaleParams, type ProductReviewParams, type ProductReviewResult, type ProductSummary, type ProductUpdateParams, type Project, type ProjectList, type PublicProduct, RateLimitError, ServerError, type Tags, type TagsParams, UnprocessableEntityError, type Usage, type UsageParams, type UsageProductTotals, Viewrium, ViewriumError, type ViewriumErrorOptions, type ViewriumOptions, type WebhookDeliveriesParams, type WebhookDeliveryList, type WebhookEndpoint, type WebhookEndpointCreateParams, type WebhookEndpointCreated, type WebhookEndpointList, type WebhookEndpointUpdateParams, createApiError, createViewrium };
|
|
3535
|
+
export { type ApiErrorPayload, type ApiKeyCreateParams, type ApiKeyCreated, type ApiKeyList, AuthenticationError, BadRequestError, type BeaconParams, ConflictError, ConnectionError, type FetchLike, type GateFlags, type Health, type ModelPlan, NotFoundError, PaymentRequiredError, PermissionError, type PlannedView, type Product, type ProductBatchResult, type ProductCost, type ProductCreateParams, type ProductCreateResult, type ProductCreated, type ProductExportParams, type ProductExportQueued, type ProductImage, type ProductImagePatchParams, type ProductList, type ProductListParams, type ProductModel, type ProductModelPatchParams, type ProductOrderParams, type ProductOrderParked, type ProductParked, type ProductPublishParams, type ProductRescaleParams, type ProductReviewParams, type ProductReviewResult, type ProductSummary, type ProductUpdateParams, type Project, type ProjectList, type PublicProduct, RateLimitError, ServerError, type Tags, type TagsParams, type Ticket, type TicketParams, UnprocessableEntityError, type Usage, type UsageParams, type UsageProductTotals, Viewrium, ViewriumError, type ViewriumErrorOptions, type ViewriumOptions, type WebhookDeliveriesParams, type WebhookDeliveryList, type WebhookEndpoint, type WebhookEndpointCreateParams, type WebhookEndpointCreated, type WebhookEndpointList, type WebhookEndpointUpdateParams, createApiError, createViewrium };
|
package/dist/index.js
CHANGED
|
@@ -171,6 +171,14 @@ function createProducts(request, requestBlob) {
|
|
|
171
171
|
* remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.
|
|
172
172
|
*/
|
|
173
173
|
rescale: (id, params) => request("POST", `/v1/products/${encode(id)}/model/rescale`, { body: params }),
|
|
174
|
+
/**
|
|
175
|
+
* POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way
|
|
176
|
+
* to its bytes (no response carries a model file URL). It works once, until `expires_at`
|
|
177
|
+
* (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`
|
|
178
|
+
* key the product must be published and the ticket is bound to the page's Origin; with an
|
|
179
|
+
* `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).
|
|
180
|
+
*/
|
|
181
|
+
createTicket: (id, params) => request("POST", `/v1/products/${encode(id)}/tickets`, { body: params }),
|
|
174
182
|
/** POST /v1/products/{id}/order - park the missing half (images or a model); commit the
|
|
175
183
|
* returned `resume_token` with `create`. */
|
|
176
184
|
order: (id, params) => request("POST", `/v1/products/${encode(id)}/order`, { body: params }),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/index.ts"],"sourcesContent":["/**\n * Typed errors for the Viewrium SDK.\n *\n * The API returns one error envelope: `{ \"error\": { code, message, details } }`.\n * Non-2xx responses are parsed into a `ViewriumError` subclass keyed by HTTP status;\n * consumers branch with `instanceof`.\n */\n\nexport interface ViewriumErrorOptions {\n status?: number | null;\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class ViewriumError extends Error {\n /** HTTP status, or `null` for transport/connection failures. */\n readonly status: number | null;\n /** Machine-readable code from the error envelope. */\n readonly code: string;\n /** Optional structured details from the error envelope. */\n readonly details: unknown;\n\n constructor(message: string, options: ViewriumErrorOptions = {}) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = new.target.name;\n this.status = options.status ?? null;\n this.code = options.code ?? \"error\";\n this.details = options.details;\n }\n}\n\n/** 400 - malformed request. */\nexport class BadRequestError extends ViewriumError {}\n/** 401 - missing or invalid API key. */\nexport class AuthenticationError extends ViewriumError {}\n/**\n * 402 - the organization's plan does not allow this: no plan, a used free trial, a failed payment,\n * or past the plan's active-product cap. `details.reason` says which.\n */\nexport class PaymentRequiredError extends ViewriumError {}\n/** 403 - the key is valid but not allowed (scope, origin, or role). */\nexport class PermissionError extends ViewriumError {}\n/** 404 - no such resource. */\nexport class NotFoundError extends ViewriumError {}\n/** 409 - conflict (e.g. duplicate `custom_id`, model not in a rescalable state). */\nexport class ConflictError extends ViewriumError {}\n/** 422 - request failed validation. */\nexport class UnprocessableEntityError extends ViewriumError {}\n/** 429 - rate limited, or not enough credits (`code: \"quota_exceeded\"`, `details.shortfall_credits`). */\nexport class RateLimitError extends ViewriumError {}\n/** 5xx - the API failed to handle the request. */\nexport class ServerError extends ViewriumError {}\n/** The request never reached the API (DNS/TLS/offline/abort). */\nexport class ConnectionError extends ViewriumError {}\n\nexport interface ApiErrorPayload {\n code: string;\n message: string;\n details?: unknown;\n}\n\nconst BY_STATUS: Record<number, new (message: string, options: ViewriumErrorOptions) => ViewriumError> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 402: PaymentRequiredError,\n 403: PermissionError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n};\n\n/** Build the right `ViewriumError` subclass for an HTTP status + parsed envelope. */\nexport function createApiError(status: number, payload: ApiErrorPayload): ViewriumError {\n const options: ViewriumErrorOptions = { status, code: payload.code, details: payload.details };\n const Specific = BY_STATUS[status];\n if (Specific) {\n return new Specific(payload.message, options);\n }\n if (status >= 500) {\n return new ServerError(payload.message, options);\n }\n return new ViewriumError(payload.message, options);\n}\n","/**\n * @viewrium/sdk - the official Viewrium JavaScript/TypeScript SDK.\n *\n * An ergonomic wrapper over the `/v1` REST API. Request/response types are derived from\n * `src/generated/schema.ts` - openapi-typescript output committed from the API's authoritative\n * `apps/api/openapi.json` (the same generator `@repo/api-client` uses; regenerate with\n * `pnpm run generate`). Owning the committed schema keeps the published `dist/index.d.ts`\n * self-contained: it cannot reference the unpublished, source-only `@repo/api-client`.\n *\n * Auth: pass an `sk_` secret key (server) or a `pk_` publishable key (browser). The key is sent\n * as `Authorization: Bearer <key>`; the API enforces what each key type may call.\n *\n * E4 (2026-09-09): the scan-era `models` resource is GONE with its routes; `products` is the\n * resource. Resource and method names match `@repo/api-client` so every client reads the same.\n */\nimport { ConnectionError, ViewriumError, createApiError } from \"./errors\";\nimport type { paths } from \"./generated/schema\";\n\nexport * from \"./errors\";\n\n// --- Derived request/response types (source of truth = apps/api/openapi.json) ----------------\n\ntype JsonContent<T> = T extends { content: { \"application/json\": infer B } } ? B : never;\n\n/** GET /v1/products/{id} resolves to one of two shapes by principal: the full product for a\n * session or `sk_` key, the allow-listed public projection for a `pk_` key. */\ntype ProductGetResult = JsonContent<paths[\"/v1/products/{id}\"][\"get\"][\"responses\"][200]>;\n/** A product with its assets (`sk_` view). */\nexport type Product = Extract<ProductGetResult, { org_id: string }>;\n/** The `pk_` view of a published product: id, title, model_url, thumbnail_url, model_status. */\nexport type PublicProduct = Exclude<ProductGetResult, { org_id: string }>;\nexport type ProductList = JsonContent<paths[\"/v1/products\"][\"get\"][\"responses\"][200]>;\n/** A catalog row - the product plus what its card needs, with no nested assets. */\nexport type ProductSummary = ProductList[\"data\"][number];\nexport type ProductListParams = NonNullable<paths[\"/v1/products\"][\"get\"][\"parameters\"][\"query\"]>;\nexport type ProductCreateParams = JsonContent<paths[\"/v1/products\"][\"post\"][\"requestBody\"]>;\n/** POST /v1/products 200 - the gate parked; resume by POSTing `resume_token`. */\nexport type ProductParked = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][200]>;\n/** One page position of a parked plan. Both park shapes carry the same row type. */\nexport type PlannedView = NonNullable<ProductParked[\"plan\"]>[number];\n/** describe's structured read of the product (D25), with its verbatim reasons. */\nexport type GateFlags = ProductParked[\"flags\"];\n/** The models layer's verdict: whether a model is allowed, what blocks it, and the views it\n * would be built from - including `unseen_views`, the invented ones. */\nexport type ModelPlan = ProductParked[\"model\"];\n/** The priced bundle, in credits (1 credit = $0.50): 1 per deliverable image, a flat 10 for a model. */\nexport type ProductCost = ProductParked[\"cost\"];\n/** POST /v1/products 202 - a committed product whose run has started. */\nexport type ProductCreated = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][202]>;\n/** POST /v1/products resolves to one of the two (discriminate on `status === \"parked\"`). */\nexport type ProductCreateResult = ProductParked | ProductCreated;\nexport type ProductUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductBatchResult = JsonContent<paths[\"/v1/products\"][\"delete\"][\"responses\"][200]>;\nexport type ProductPublishParams = JsonContent<\n paths[\"/v1/products/publish\"][\"post\"][\"requestBody\"]\n>;\nexport type Tags = JsonContent<paths[\"/v1/products/tags\"][\"get\"][\"responses\"][200]>;\nexport type TagsParams = NonNullable<paths[\"/v1/products/tags\"][\"get\"][\"parameters\"][\"query\"]>;\n/** POST /v1/products/{id}/review - the write-behind flush (image + model decisions, the\n * product's user-set status). It records decisions only. */\nexport type ProductReviewParams = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductReviewResult = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductImagePatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductImage = JsonContent<\n paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"responses\"][200]\n>;\nexport type ProductModelPatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/model\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductModel = JsonContent<paths[\"/v1/products/{id}/model\"][\"patch\"][\"responses\"][200]>;\nexport type ProductRescaleParams = JsonContent<\n paths[\"/v1/products/{id}/model/rescale\"][\"post\"][\"requestBody\"]\n>;\n/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through\n * `products.create({ resume_token })`. */\nexport type ProductOrderParams = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductOrderParked = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductExportParams = NonNullable<\n paths[\"/v1/products/export\"][\"get\"][\"parameters\"][\"query\"]\n>;\n/** 202 from the export: the images are being finished in the background and a download link\n * is emailed; the same request streams the ZIP once they are done. */\nexport type ProductExportQueued = JsonContent<\n paths[\"/v1/products/export\"][\"get\"][\"responses\"][202]\n>;\n\nexport type ApiKeyList = JsonContent<paths[\"/v1/api_keys\"][\"get\"][\"responses\"][200]>;\nexport type ApiKeyCreateParams = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"requestBody\"]>;\nexport type ApiKeyCreated = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"responses\"][201]>;\n\nexport type Usage = JsonContent<paths[\"/v1/usage\"][\"get\"][\"responses\"][200]>;\nexport type UsageParams = NonNullable<paths[\"/v1/usage\"][\"get\"][\"parameters\"][\"query\"]>;\n/** One `group_by: \"product\"` row: a product's counts over the window, with its title. */\nexport type UsageProductTotals = NonNullable<Usage[\"by_product\"]>[number];\n\nexport type WebhookEndpoint = JsonContent<paths[\"/v1/webhooks/{id}\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointList = JsonContent<paths[\"/v1/webhooks\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointCreateParams = JsonContent<paths[\"/v1/webhooks\"][\"post\"][\"requestBody\"]>;\nexport type WebhookEndpointCreated = JsonContent<\n paths[\"/v1/webhooks\"][\"post\"][\"responses\"][201]\n>;\nexport type WebhookEndpointUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/webhooks/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type WebhookDeliveryList = JsonContent<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"responses\"][200]\n>;\nexport type WebhookDeliveriesParams = NonNullable<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type Project = JsonContent<paths[\"/v1/projects/{id}\"][\"get\"][\"responses\"][200]>;\nexport type ProjectList = JsonContent<paths[\"/v1/projects\"][\"get\"][\"responses\"][200]>;\n\nexport type BeaconParams = JsonContent<paths[\"/v1/beacon\"][\"post\"][\"requestBody\"]>;\n\nexport type Health = JsonContent<paths[\"/v1/health\"][\"get\"][\"responses\"][200]>;\n\n// --- Client options + transport --------------------------------------------------------------\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface ViewriumOptions {\n /** `sk_` secret key (server) or `pk_` publishable key (browser). */\n apiKey: string;\n /** API origin; defaults to `https://api.viewrium.com`. Trailing slash is stripped. */\n baseUrl?: string;\n /** Injectable fetch (tests, custom agents). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.viewrium.com\";\n\ntype QueryValue = string | number | boolean | readonly string[] | null | undefined;\ntype QueryParams = Record<string, QueryValue>;\n\ninterface RequestOptions {\n query?: QueryParams;\n body?: unknown;\n}\n\ntype RequestFn = <T>(method: string, path: string, options?: RequestOptions) => Promise<T>;\n/** The escape hatch for the one route that answers bytes rather than JSON (the export ZIP) -\n * or, on a 202, JSON after all. */\ntype BlobRequestFn = (\n method: string,\n path: string,\n options?: RequestOptions,\n) => Promise<Blob | ProductExportQueued>;\n\ninterface Transport {\n baseUrl: string;\n apiKey: string;\n fetch: FetchLike;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const url = new URL(baseUrl + path);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n for (const item of value as readonly string[]) url.searchParams.append(key, String(item));\n } else {\n url.searchParams.append(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\nasync function parseError(res: Response): Promise<ViewriumError> {\n let code = \"error\";\n let message = res.statusText || `HTTP ${res.status}`;\n let details: unknown;\n try {\n const data: unknown = await res.json();\n if (data && typeof data === \"object\" && \"error\" in data) {\n const envelope = (data as { error: unknown }).error;\n if (envelope && typeof envelope === \"object\") {\n const e = envelope as Record<string, unknown>;\n if (typeof e.code === \"string\") code = e.code;\n if (typeof e.message === \"string\") message = e.message;\n details = e.details;\n }\n }\n } catch {\n // Non-JSON error body; fall back to status text.\n }\n return createApiError(res.status, { code, message, details });\n}\n\nasync function send(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Response> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n authorization: `Bearer ${transport.apiKey}`,\n };\n const init: RequestInit = { method, headers };\n if (options?.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n init.body = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await transport.fetch(buildUrl(transport.baseUrl, path, options?.query), init);\n } catch (cause) {\n throw new ConnectionError(\"The request to the Viewrium API could not be sent.\", { cause });\n }\n\n if (!res.ok) {\n throw await parseError(res);\n }\n return res;\n}\n\nasync function doRequest<T>(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<T> {\n const res = await send(transport, method, path, options);\n if (res.status === 204) {\n return undefined as T;\n }\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n}\n\nasync function doBlobRequest(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Blob | ProductExportQueued> {\n const res = await send(transport, method, path, options);\n if (res.status === 202) return (await res.json()) as ProductExportQueued;\n return res.blob();\n}\n\nconst encode = encodeURIComponent;\n\n// --- Resource namespaces ---------------------------------------------------------------------\n\nfunction createProducts(request: RequestFn, requestBlob: BlobRequestFn) {\n return {\n /**\n * POST /v1/products - the single generation entrypoint. Supply exactly one source\n * (`image_paths` | `image_urls` | `video_url` | `source_url` | `model_url` |\n * `resume_token`). A fresh request ALWAYS parks (200, `status === \"parked\"`, nothing\n * written): the plan, the model verdict and the price come back inside `resume_token`,\n * which you POST back to commit (202, `ProductCreated`). A `model_url` commits at once.\n */\n create: (params: ProductCreateParams) =>\n request<ProductCreateResult>(\"POST\", \"/v1/products\", { body: params }),\n /**\n * GET /v1/products - the catalog: one org (`org_id`, or the caller's only org), or\n * `scope: \"all\"` for every org an agency member or staff reaches. Filters AND together.\n */\n list: (params?: ProductListParams) =>\n request<ProductList>(\"GET\", \"/v1/products\", { query: params as QueryParams | undefined }),\n /** GET /v1/products/{id} - the product with its assets (a `pk_` key gets `PublicProduct`). */\n retrieve: (id: string) => request<Product>(\"GET\", `/v1/products/${encode(id)}`),\n /**\n * PATCH /v1/products/{id} - the customer's fields, the user-set status and the publish\n * toggle. 409 on a `custom_id` another product in the org already holds.\n */\n update: (id: string, params: ProductUpdateParams) =>\n request<Product>(\"PATCH\", `/v1/products/${encode(id)}`, { body: params }),\n /** DELETE /v1/products/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/products/${encode(id)}`),\n /** DELETE /v1/products?ids=... - per-id affected/not_found/forbidden buckets. */\n batchDelete: (ids: readonly string[]) =>\n request<ProductBatchResult>(\"DELETE\", \"/v1/products\", { query: { ids } }),\n /** POST /v1/products/publish - the bulk publish toggle; one product toggles through `update`. */\n publish: (params: ProductPublishParams) =>\n request<ProductBatchResult>(\"POST\", \"/v1/products/publish\", { body: params }),\n /** GET /v1/products/tags - distinct tags across the same scope the list takes. */\n tags: (params?: TagsParams) =>\n request<Tags>(\"GET\", \"/v1/products/tags\", { query: params as QueryParams | undefined }),\n /** POST /v1/products/{id}/review - the review flush: image and model decisions plus the\n * product's user-set status, validated together and applied in order. */\n review: (id: string, params: ProductReviewParams) =>\n request<ProductReviewResult>(\"POST\", `/v1/products/${encode(id)}/review`, { body: params }),\n /** PATCH /v1/products/{id}/images/{image_id} - one image's decision and overrides. */\n updateImage: (id: string, imageId: string, params: ProductImagePatchParams) =>\n request<ProductImage>(\"PATCH\", `/v1/products/${encode(id)}/images/${encode(imageId)}`, {\n body: params,\n }),\n /** PATCH /v1/products/{id}/model - the model's review decision. */\n updateModel: (id: string, params: ProductModelPatchParams) =>\n request<ProductModel>(\"PATCH\", `/v1/products/${encode(id)}/model`, { body: params }),\n /**\n * POST /v1/products/{id}/model/rescale - re-apply real-world scale to the GLB in place.\n * Every supplied axis is applied to the geometry; `unit` covers them (omitted = cm) and is\n * remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.\n */\n rescale: (id: string, params: ProductRescaleParams) =>\n request<Product>(\"POST\", `/v1/products/${encode(id)}/model/rescale`, { body: params }),\n /** POST /v1/products/{id}/order - park the missing half (images or a model); commit the\n * returned `resume_token` with `create`. */\n order: (id: string, params: ProductOrderParams) =>\n request<ProductOrderParked>(\"POST\", `/v1/products/${encode(id)}/order`, { body: params }),\n /** GET /v1/products/export - a ZIP of finished images (never the 3D model), resolved as a\n * `Blob`; or `ProductExportQueued` (202) when the images are being finished in the\n * background and a download link is emailed - repeat the request later to stream it. */\n export: (params?: ProductExportParams) =>\n requestBlob(\"GET\", \"/v1/products/export\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createApiKeys(request: RequestFn) {\n return {\n /** GET /v1/api_keys */\n list: () => request<ApiKeyList>(\"GET\", \"/v1/api_keys\"),\n /** POST /v1/api_keys - the full key is returned exactly once. */\n create: (params: ApiKeyCreateParams) =>\n request<ApiKeyCreated>(\"POST\", \"/v1/api_keys\", { body: params }),\n /** DELETE /v1/api_keys/{id} - soft revoke. */\n revoke: (id: string) => request<void>(\"DELETE\", `/v1/api_keys/${encode(id)}`),\n };\n}\n\nfunction createUsage(request: RequestFn) {\n return {\n /** GET /v1/usage - buckets and totals over the window; `group_by: \"product\"` adds the\n * window's best-performing products in `by_product`. */\n get: (params?: UsageParams) =>\n request<Usage>(\"GET\", \"/v1/usage\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createWebhooks(request: RequestFn) {\n return {\n /** GET /v1/webhooks */\n list: () => request<WebhookEndpointList>(\"GET\", \"/v1/webhooks\"),\n /** POST /v1/webhooks - includes the signing secret. */\n create: (params: WebhookEndpointCreateParams) =>\n request<WebhookEndpointCreated>(\"POST\", \"/v1/webhooks\", { body: params }),\n /** GET /v1/webhooks/{id} */\n retrieve: (id: string) => request<WebhookEndpoint>(\"GET\", `/v1/webhooks/${encode(id)}`),\n /** PATCH /v1/webhooks/{id} */\n update: (id: string, params: WebhookEndpointUpdateParams) =>\n request<WebhookEndpoint>(\"PATCH\", `/v1/webhooks/${encode(id)}`, { body: params }),\n /** DELETE /v1/webhooks/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/webhooks/${encode(id)}`),\n /** GET /v1/webhooks/{id}/deliveries */\n listDeliveries: (id: string, params?: WebhookDeliveriesParams) =>\n request<WebhookDeliveryList>(\"GET\", `/v1/webhooks/${encode(id)}/deliveries`, {\n query: params as QueryParams | undefined,\n }),\n };\n}\n\nfunction createProjects(request: RequestFn) {\n return {\n /** GET /v1/projects */\n list: () => request<ProjectList>(\"GET\", \"/v1/projects\"),\n /** GET /v1/projects/{id} */\n retrieve: (id: string) => request<Project>(\"GET\", `/v1/projects/${encode(id)}`),\n };\n}\n\nfunction createBeacon(request: RequestFn) {\n return {\n /** POST /v1/beacon - ingest a view/AR analytics event (204). */\n send: (params: BeaconParams) => request<void>(\"POST\", \"/v1/beacon\", { body: params }),\n };\n}\n\n// --- Client ----------------------------------------------------------------------------------\n\nexport class Viewrium {\n readonly baseUrl: string;\n readonly products: ReturnType<typeof createProducts>;\n readonly apiKeys: ReturnType<typeof createApiKeys>;\n readonly usage: ReturnType<typeof createUsage>;\n readonly webhooks: ReturnType<typeof createWebhooks>;\n readonly projects: ReturnType<typeof createProjects>;\n readonly beacon: ReturnType<typeof createBeacon>;\n\n readonly #request: RequestFn;\n\n constructor(options: ViewriumOptions) {\n if (!options || !options.apiKey) {\n throw new ViewriumError(\"A Viewrium client requires an `apiKey` (sk_ or pk_).\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\n const provided = options.fetch;\n const globalFetch = typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined;\n const fetchImpl = provided ?? globalFetch;\n if (!fetchImpl) {\n throw new ViewriumError(\"No fetch implementation available; pass `options.fetch`.\");\n }\n\n const transport: Transport = { baseUrl: this.baseUrl, apiKey: options.apiKey, fetch: fetchImpl };\n const request: RequestFn = <T>(method: string, path: string, opts?: RequestOptions) =>\n doRequest<T>(transport, method, path, opts);\n const requestBlob: BlobRequestFn = (method, path, opts) =>\n doBlobRequest(transport, method, path, opts);\n this.#request = request;\n\n this.products = createProducts(request, requestBlob);\n this.apiKeys = createApiKeys(request);\n this.usage = createUsage(request);\n this.webhooks = createWebhooks(request);\n this.projects = createProjects(request);\n this.beacon = createBeacon(request);\n }\n\n /** GET /v1/health */\n health(): Promise<Health> {\n return this.#request<Health>(\"GET\", \"/v1/health\");\n }\n}\n\n/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */\nexport function createViewrium(options: ViewriumOptions): Viewrium {\n return new Viewrium(options);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/index.ts"],"sourcesContent":["/**\n * Typed errors for the Viewrium SDK.\n *\n * The API returns one error envelope: `{ \"error\": { code, message, details } }`.\n * Non-2xx responses are parsed into a `ViewriumError` subclass keyed by HTTP status;\n * consumers branch with `instanceof`.\n */\n\nexport interface ViewriumErrorOptions {\n status?: number | null;\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class ViewriumError extends Error {\n /** HTTP status, or `null` for transport/connection failures. */\n readonly status: number | null;\n /** Machine-readable code from the error envelope. */\n readonly code: string;\n /** Optional structured details from the error envelope. */\n readonly details: unknown;\n\n constructor(message: string, options: ViewriumErrorOptions = {}) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = new.target.name;\n this.status = options.status ?? null;\n this.code = options.code ?? \"error\";\n this.details = options.details;\n }\n}\n\n/** 400 - malformed request. */\nexport class BadRequestError extends ViewriumError {}\n/** 401 - missing or invalid API key. */\nexport class AuthenticationError extends ViewriumError {}\n/**\n * 402 - the organization's plan does not allow this: no plan, a used free trial, a failed payment,\n * or past the plan's active-product cap. `details.reason` says which.\n */\nexport class PaymentRequiredError extends ViewriumError {}\n/** 403 - the key is valid but not allowed (scope, origin, or role). */\nexport class PermissionError extends ViewriumError {}\n/** 404 - no such resource. */\nexport class NotFoundError extends ViewriumError {}\n/** 409 - conflict (e.g. duplicate `custom_id`, model not in a rescalable state). */\nexport class ConflictError extends ViewriumError {}\n/** 422 - request failed validation. */\nexport class UnprocessableEntityError extends ViewriumError {}\n/** 429 - rate limited, or not enough credits (`code: \"quota_exceeded\"`, `details.shortfall_credits`). */\nexport class RateLimitError extends ViewriumError {}\n/** 5xx - the API failed to handle the request. */\nexport class ServerError extends ViewriumError {}\n/** The request never reached the API (DNS/TLS/offline/abort). */\nexport class ConnectionError extends ViewriumError {}\n\nexport interface ApiErrorPayload {\n code: string;\n message: string;\n details?: unknown;\n}\n\nconst BY_STATUS: Record<number, new (message: string, options: ViewriumErrorOptions) => ViewriumError> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 402: PaymentRequiredError,\n 403: PermissionError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n};\n\n/** Build the right `ViewriumError` subclass for an HTTP status + parsed envelope. */\nexport function createApiError(status: number, payload: ApiErrorPayload): ViewriumError {\n const options: ViewriumErrorOptions = { status, code: payload.code, details: payload.details };\n const Specific = BY_STATUS[status];\n if (Specific) {\n return new Specific(payload.message, options);\n }\n if (status >= 500) {\n return new ServerError(payload.message, options);\n }\n return new ViewriumError(payload.message, options);\n}\n","/**\n * @viewrium/sdk - the official Viewrium JavaScript/TypeScript SDK.\n *\n * An ergonomic wrapper over the `/v1` REST API. Request/response types are derived from\n * `src/generated/schema.ts` - openapi-typescript output committed from the API's authoritative\n * `apps/api/openapi.json` (the same generator `@repo/api-client` uses; regenerate with\n * `pnpm run generate`). Owning the committed schema keeps the published `dist/index.d.ts`\n * self-contained: it cannot reference the unpublished, source-only `@repo/api-client`.\n *\n * Auth: pass an `sk_` secret key (server) or a `pk_` publishable key (browser). The key is sent\n * as `Authorization: Bearer <key>`; the API enforces what each key type may call.\n *\n * E4 (2026-09-09): the scan-era `models` resource is GONE with its routes; `products` is the\n * resource. Resource and method names match `@repo/api-client` so every client reads the same.\n */\nimport { ConnectionError, ViewriumError, createApiError } from \"./errors\";\nimport type { paths } from \"./generated/schema\";\n\nexport * from \"./errors\";\n\n// --- Derived request/response types (source of truth = apps/api/openapi.json) ----------------\n\ntype JsonContent<T> = T extends { content: { \"application/json\": infer B } } ? B : never;\n\n/** GET /v1/products/{id} resolves to one of two shapes by principal: the full product for a\n * session or `sk_` key, the allow-listed public projection for a `pk_` key. */\ntype ProductGetResult = JsonContent<paths[\"/v1/products/{id}\"][\"get\"][\"responses\"][200]>;\n/** A product with its assets (`sk_` view). */\nexport type Product = Extract<ProductGetResult, { org_id: string }>;\n/** The `pk_` view of a published product: id, title, thumbnail_url, model_status, wall_mounted. The model\n * file itself is reached through `products.createTicket`. */\nexport type PublicProduct = Exclude<ProductGetResult, { org_id: string }>;\nexport type ProductList = JsonContent<paths[\"/v1/products\"][\"get\"][\"responses\"][200]>;\n/** A catalog row - the product plus what its card needs, with no nested assets. */\nexport type ProductSummary = ProductList[\"data\"][number];\nexport type ProductListParams = NonNullable<paths[\"/v1/products\"][\"get\"][\"parameters\"][\"query\"]>;\nexport type ProductCreateParams = JsonContent<paths[\"/v1/products\"][\"post\"][\"requestBody\"]>;\n/** POST /v1/products 200 - the gate parked; resume by POSTing `resume_token`. */\nexport type ProductParked = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][200]>;\n/** One page position of a parked plan. Both park shapes carry the same row type. */\nexport type PlannedView = NonNullable<ProductParked[\"plan\"]>[number];\n/** describe's structured read of the product (D25), with its verbatim reasons. */\nexport type GateFlags = ProductParked[\"flags\"];\n/** The models layer's verdict: whether a model is allowed, what blocks it, and the views it\n * would be built from - including `unseen_views`, the invented ones. */\nexport type ModelPlan = ProductParked[\"model\"];\n/** The priced bundle, in credits (1 credit = $0.50): 1 per deliverable image, a flat 10 for a model. */\nexport type ProductCost = ProductParked[\"cost\"];\n/** POST /v1/products 202 - a committed product whose run has started. */\nexport type ProductCreated = JsonContent<paths[\"/v1/products\"][\"post\"][\"responses\"][202]>;\n/** POST /v1/products resolves to one of the two (discriminate on `status === \"parked\"`). */\nexport type ProductCreateResult = ProductParked | ProductCreated;\nexport type ProductUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductBatchResult = JsonContent<paths[\"/v1/products\"][\"delete\"][\"responses\"][200]>;\nexport type ProductPublishParams = JsonContent<\n paths[\"/v1/products/publish\"][\"post\"][\"requestBody\"]\n>;\nexport type Tags = JsonContent<paths[\"/v1/products/tags\"][\"get\"][\"responses\"][200]>;\nexport type TagsParams = NonNullable<paths[\"/v1/products/tags\"][\"get\"][\"parameters\"][\"query\"]>;\n/** POST /v1/products/{id}/review - the write-behind flush (image + model decisions, the\n * product's user-set status). It records decisions only. */\nexport type ProductReviewParams = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductReviewResult = JsonContent<\n paths[\"/v1/products/{id}/review\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductImagePatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductImage = JsonContent<\n paths[\"/v1/products/{id}/images/{image_id}\"][\"patch\"][\"responses\"][200]\n>;\nexport type ProductModelPatchParams = JsonContent<\n NonNullable<paths[\"/v1/products/{id}/model\"][\"patch\"][\"requestBody\"]>\n>;\nexport type ProductModel = JsonContent<paths[\"/v1/products/{id}/model\"][\"patch\"][\"responses\"][200]>;\nexport type ProductRescaleParams = JsonContent<\n paths[\"/v1/products/{id}/model/rescale\"][\"post\"][\"requestBody\"]\n>;\n/** POST /v1/products/{id}/tickets - `{ kind: \"view\" | \"ar\", origin? }`. */\nexport type TicketParams = JsonContent<paths[\"/v1/products/{id}/tickets\"][\"post\"][\"requestBody\"]>;\n/** A single-use model URL: `{ url, kind, expires_at }`. */\nexport type Ticket = JsonContent<paths[\"/v1/products/{id}/tickets\"][\"post\"][\"responses\"][200]>;\n/** POST /v1/products/{id}/order - the missing half; parks like a create, commits through\n * `products.create({ resume_token })`. */\nexport type ProductOrderParams = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"requestBody\"]\n>;\nexport type ProductOrderParked = JsonContent<\n paths[\"/v1/products/{id}/order\"][\"post\"][\"responses\"][200]\n>;\nexport type ProductExportParams = NonNullable<\n paths[\"/v1/products/export\"][\"get\"][\"parameters\"][\"query\"]\n>;\n/** 202 from the export: the images are being finished in the background and a download link\n * is emailed; the same request streams the ZIP once they are done. */\nexport type ProductExportQueued = JsonContent<\n paths[\"/v1/products/export\"][\"get\"][\"responses\"][202]\n>;\n\nexport type ApiKeyList = JsonContent<paths[\"/v1/api_keys\"][\"get\"][\"responses\"][200]>;\nexport type ApiKeyCreateParams = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"requestBody\"]>;\nexport type ApiKeyCreated = JsonContent<paths[\"/v1/api_keys\"][\"post\"][\"responses\"][201]>;\n\nexport type Usage = JsonContent<paths[\"/v1/usage\"][\"get\"][\"responses\"][200]>;\nexport type UsageParams = NonNullable<paths[\"/v1/usage\"][\"get\"][\"parameters\"][\"query\"]>;\n/** One `group_by: \"product\"` row: a product's counts over the window, with its title. */\nexport type UsageProductTotals = NonNullable<Usage[\"by_product\"]>[number];\n\nexport type WebhookEndpoint = JsonContent<paths[\"/v1/webhooks/{id}\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointList = JsonContent<paths[\"/v1/webhooks\"][\"get\"][\"responses\"][200]>;\nexport type WebhookEndpointCreateParams = JsonContent<paths[\"/v1/webhooks\"][\"post\"][\"requestBody\"]>;\nexport type WebhookEndpointCreated = JsonContent<\n paths[\"/v1/webhooks\"][\"post\"][\"responses\"][201]\n>;\nexport type WebhookEndpointUpdateParams = JsonContent<\n NonNullable<paths[\"/v1/webhooks/{id}\"][\"patch\"][\"requestBody\"]>\n>;\nexport type WebhookDeliveryList = JsonContent<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"responses\"][200]\n>;\nexport type WebhookDeliveriesParams = NonNullable<\n paths[\"/v1/webhooks/{id}/deliveries\"][\"get\"][\"parameters\"][\"query\"]\n>;\n\nexport type Project = JsonContent<paths[\"/v1/projects/{id}\"][\"get\"][\"responses\"][200]>;\nexport type ProjectList = JsonContent<paths[\"/v1/projects\"][\"get\"][\"responses\"][200]>;\n\nexport type BeaconParams = JsonContent<paths[\"/v1/beacon\"][\"post\"][\"requestBody\"]>;\n\nexport type Health = JsonContent<paths[\"/v1/health\"][\"get\"][\"responses\"][200]>;\n\n// --- Client options + transport --------------------------------------------------------------\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface ViewriumOptions {\n /** `sk_` secret key (server) or `pk_` publishable key (browser). */\n apiKey: string;\n /** API origin; defaults to `https://api.viewrium.com`. Trailing slash is stripped. */\n baseUrl?: string;\n /** Injectable fetch (tests, custom agents). Defaults to `globalThis.fetch`. */\n fetch?: FetchLike;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.viewrium.com\";\n\ntype QueryValue = string | number | boolean | readonly string[] | null | undefined;\ntype QueryParams = Record<string, QueryValue>;\n\ninterface RequestOptions {\n query?: QueryParams;\n body?: unknown;\n}\n\ntype RequestFn = <T>(method: string, path: string, options?: RequestOptions) => Promise<T>;\n/** The escape hatch for the one route that answers bytes rather than JSON (the export ZIP) -\n * or, on a 202, JSON after all. */\ntype BlobRequestFn = (\n method: string,\n path: string,\n options?: RequestOptions,\n) => Promise<Blob | ProductExportQueued>;\n\ninterface Transport {\n baseUrl: string;\n apiKey: string;\n fetch: FetchLike;\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: QueryParams): string {\n const url = new URL(baseUrl + path);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n for (const item of value as readonly string[]) url.searchParams.append(key, String(item));\n } else {\n url.searchParams.append(key, String(value));\n }\n }\n }\n return url.toString();\n}\n\nasync function parseError(res: Response): Promise<ViewriumError> {\n let code = \"error\";\n let message = res.statusText || `HTTP ${res.status}`;\n let details: unknown;\n try {\n const data: unknown = await res.json();\n if (data && typeof data === \"object\" && \"error\" in data) {\n const envelope = (data as { error: unknown }).error;\n if (envelope && typeof envelope === \"object\") {\n const e = envelope as Record<string, unknown>;\n if (typeof e.code === \"string\") code = e.code;\n if (typeof e.message === \"string\") message = e.message;\n details = e.details;\n }\n }\n } catch {\n // Non-JSON error body; fall back to status text.\n }\n return createApiError(res.status, { code, message, details });\n}\n\nasync function send(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Response> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n authorization: `Bearer ${transport.apiKey}`,\n };\n const init: RequestInit = { method, headers };\n if (options?.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n init.body = JSON.stringify(options.body);\n }\n\n let res: Response;\n try {\n res = await transport.fetch(buildUrl(transport.baseUrl, path, options?.query), init);\n } catch (cause) {\n throw new ConnectionError(\"The request to the Viewrium API could not be sent.\", { cause });\n }\n\n if (!res.ok) {\n throw await parseError(res);\n }\n return res;\n}\n\nasync function doRequest<T>(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<T> {\n const res = await send(transport, method, path, options);\n if (res.status === 204) {\n return undefined as T;\n }\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n}\n\nasync function doBlobRequest(\n transport: Transport,\n method: string,\n path: string,\n options?: RequestOptions,\n): Promise<Blob | ProductExportQueued> {\n const res = await send(transport, method, path, options);\n if (res.status === 202) return (await res.json()) as ProductExportQueued;\n return res.blob();\n}\n\nconst encode = encodeURIComponent;\n\n// --- Resource namespaces ---------------------------------------------------------------------\n\nfunction createProducts(request: RequestFn, requestBlob: BlobRequestFn) {\n return {\n /**\n * POST /v1/products - the single generation entrypoint. Supply exactly one source\n * (`image_paths` | `image_urls` | `video_url` | `source_url` | `model_url` |\n * `resume_token`). A fresh request ALWAYS parks (200, `status === \"parked\"`, nothing\n * written): the plan, the model verdict and the price come back inside `resume_token`,\n * which you POST back to commit (202, `ProductCreated`). A `model_url` commits at once.\n */\n create: (params: ProductCreateParams) =>\n request<ProductCreateResult>(\"POST\", \"/v1/products\", { body: params }),\n /**\n * GET /v1/products - the catalog: one org (`org_id`, or the caller's only org), or\n * `scope: \"all\"` for every org an agency member or staff reaches. Filters AND together.\n */\n list: (params?: ProductListParams) =>\n request<ProductList>(\"GET\", \"/v1/products\", { query: params as QueryParams | undefined }),\n /** GET /v1/products/{id} - the product with its assets (a `pk_` key gets `PublicProduct`). */\n retrieve: (id: string) => request<Product>(\"GET\", `/v1/products/${encode(id)}`),\n /**\n * PATCH /v1/products/{id} - the customer's fields, the user-set status and the publish\n * toggle. 409 on a `custom_id` another product in the org already holds.\n */\n update: (id: string, params: ProductUpdateParams) =>\n request<Product>(\"PATCH\", `/v1/products/${encode(id)}`, { body: params }),\n /** DELETE /v1/products/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/products/${encode(id)}`),\n /** DELETE /v1/products?ids=... - per-id affected/not_found/forbidden buckets. */\n batchDelete: (ids: readonly string[]) =>\n request<ProductBatchResult>(\"DELETE\", \"/v1/products\", { query: { ids } }),\n /** POST /v1/products/publish - the bulk publish toggle; one product toggles through `update`. */\n publish: (params: ProductPublishParams) =>\n request<ProductBatchResult>(\"POST\", \"/v1/products/publish\", { body: params }),\n /** GET /v1/products/tags - distinct tags across the same scope the list takes. */\n tags: (params?: TagsParams) =>\n request<Tags>(\"GET\", \"/v1/products/tags\", { query: params as QueryParams | undefined }),\n /** POST /v1/products/{id}/review - the review flush: image and model decisions plus the\n * product's user-set status, validated together and applied in order. */\n review: (id: string, params: ProductReviewParams) =>\n request<ProductReviewResult>(\"POST\", `/v1/products/${encode(id)}/review`, { body: params }),\n /** PATCH /v1/products/{id}/images/{image_id} - one image's decision and overrides. */\n updateImage: (id: string, imageId: string, params: ProductImagePatchParams) =>\n request<ProductImage>(\"PATCH\", `/v1/products/${encode(id)}/images/${encode(imageId)}`, {\n body: params,\n }),\n /** PATCH /v1/products/{id}/model - the model's review decision. */\n updateModel: (id: string, params: ProductModelPatchParams) =>\n request<ProductModel>(\"PATCH\", `/v1/products/${encode(id)}/model`, { body: params }),\n /**\n * POST /v1/products/{id}/model/rescale - re-apply real-world scale to the GLB in place.\n * Every supplied axis is applied to the geometry; `unit` covers them (omitted = cm) and is\n * remembered as the product's `display_unit`. The result carries the ACHIEVED dimensions.\n */\n rescale: (id: string, params: ProductRescaleParams) =>\n request<Product>(\"POST\", `/v1/products/${encode(id)}/model/rescale`, { body: params }),\n /**\n * POST /v1/products/{id}/tickets - a single-use URL for the product's 3D file, the ONLY way\n * to its bytes (no response carries a model file URL). It works once, until `expires_at`\n * (60 s for `view`, 30 s for `ar`); a `view` ticket only from its bound origin. With a `pk_`\n * key the product must be published and the ticket is bound to the page's Origin; with an\n * `sk_` key pass `origin` (the page that will load the model, one of your allowed domains).\n */\n createTicket: (id: string, params: TicketParams) =>\n request<Ticket>(\"POST\", `/v1/products/${encode(id)}/tickets`, { body: params }),\n /** POST /v1/products/{id}/order - park the missing half (images or a model); commit the\n * returned `resume_token` with `create`. */\n order: (id: string, params: ProductOrderParams) =>\n request<ProductOrderParked>(\"POST\", `/v1/products/${encode(id)}/order`, { body: params }),\n /** GET /v1/products/export - a ZIP of finished images (never the 3D model), resolved as a\n * `Blob`; or `ProductExportQueued` (202) when the images are being finished in the\n * background and a download link is emailed - repeat the request later to stream it. */\n export: (params?: ProductExportParams) =>\n requestBlob(\"GET\", \"/v1/products/export\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createApiKeys(request: RequestFn) {\n return {\n /** GET /v1/api_keys */\n list: () => request<ApiKeyList>(\"GET\", \"/v1/api_keys\"),\n /** POST /v1/api_keys - the full key is returned exactly once. */\n create: (params: ApiKeyCreateParams) =>\n request<ApiKeyCreated>(\"POST\", \"/v1/api_keys\", { body: params }),\n /** DELETE /v1/api_keys/{id} - soft revoke. */\n revoke: (id: string) => request<void>(\"DELETE\", `/v1/api_keys/${encode(id)}`),\n };\n}\n\nfunction createUsage(request: RequestFn) {\n return {\n /** GET /v1/usage - buckets and totals over the window; `group_by: \"product\"` adds the\n * window's best-performing products in `by_product`. */\n get: (params?: UsageParams) =>\n request<Usage>(\"GET\", \"/v1/usage\", { query: params as QueryParams | undefined }),\n };\n}\n\nfunction createWebhooks(request: RequestFn) {\n return {\n /** GET /v1/webhooks */\n list: () => request<WebhookEndpointList>(\"GET\", \"/v1/webhooks\"),\n /** POST /v1/webhooks - includes the signing secret. */\n create: (params: WebhookEndpointCreateParams) =>\n request<WebhookEndpointCreated>(\"POST\", \"/v1/webhooks\", { body: params }),\n /** GET /v1/webhooks/{id} */\n retrieve: (id: string) => request<WebhookEndpoint>(\"GET\", `/v1/webhooks/${encode(id)}`),\n /** PATCH /v1/webhooks/{id} */\n update: (id: string, params: WebhookEndpointUpdateParams) =>\n request<WebhookEndpoint>(\"PATCH\", `/v1/webhooks/${encode(id)}`, { body: params }),\n /** DELETE /v1/webhooks/{id} */\n delete: (id: string) => request<void>(\"DELETE\", `/v1/webhooks/${encode(id)}`),\n /** GET /v1/webhooks/{id}/deliveries */\n listDeliveries: (id: string, params?: WebhookDeliveriesParams) =>\n request<WebhookDeliveryList>(\"GET\", `/v1/webhooks/${encode(id)}/deliveries`, {\n query: params as QueryParams | undefined,\n }),\n };\n}\n\nfunction createProjects(request: RequestFn) {\n return {\n /** GET /v1/projects */\n list: () => request<ProjectList>(\"GET\", \"/v1/projects\"),\n /** GET /v1/projects/{id} */\n retrieve: (id: string) => request<Project>(\"GET\", `/v1/projects/${encode(id)}`),\n };\n}\n\nfunction createBeacon(request: RequestFn) {\n return {\n /** POST /v1/beacon - ingest a view/AR analytics event (204). */\n send: (params: BeaconParams) => request<void>(\"POST\", \"/v1/beacon\", { body: params }),\n };\n}\n\n// --- Client ----------------------------------------------------------------------------------\n\nexport class Viewrium {\n readonly baseUrl: string;\n readonly products: ReturnType<typeof createProducts>;\n readonly apiKeys: ReturnType<typeof createApiKeys>;\n readonly usage: ReturnType<typeof createUsage>;\n readonly webhooks: ReturnType<typeof createWebhooks>;\n readonly projects: ReturnType<typeof createProjects>;\n readonly beacon: ReturnType<typeof createBeacon>;\n\n readonly #request: RequestFn;\n\n constructor(options: ViewriumOptions) {\n if (!options || !options.apiKey) {\n throw new ViewriumError(\"A Viewrium client requires an `apiKey` (sk_ or pk_).\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\n const provided = options.fetch;\n const globalFetch = typeof globalThis.fetch === \"function\" ? globalThis.fetch.bind(globalThis) : undefined;\n const fetchImpl = provided ?? globalFetch;\n if (!fetchImpl) {\n throw new ViewriumError(\"No fetch implementation available; pass `options.fetch`.\");\n }\n\n const transport: Transport = { baseUrl: this.baseUrl, apiKey: options.apiKey, fetch: fetchImpl };\n const request: RequestFn = <T>(method: string, path: string, opts?: RequestOptions) =>\n doRequest<T>(transport, method, path, opts);\n const requestBlob: BlobRequestFn = (method, path, opts) =>\n doBlobRequest(transport, method, path, opts);\n this.#request = request;\n\n this.products = createProducts(request, requestBlob);\n this.apiKeys = createApiKeys(request);\n this.usage = createUsage(request);\n this.webhooks = createWebhooks(request);\n this.projects = createProjects(request);\n this.beacon = createBeacon(request);\n }\n\n /** GET /v1/health */\n health(): Promise<Health> {\n return this.#request<Health>(\"GET\", \"/v1/health\");\n }\n}\n\n/** Convenience factory: `createViewrium({ apiKey })` is `new Viewrium({ apiKey })`. */\nexport function createViewrium(options: ViewriumOptions): Viewrium {\n return new Viewrium(options);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viewrium/sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Official Viewrium
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Official TypeScript/JavaScript client for the Viewrium API: AR and 3D product visualization for furniture retailers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"viewrium",
|
|
7
|
+
"sdk",
|
|
8
|
+
"api-client",
|
|
9
|
+
"3d",
|
|
10
|
+
"ar",
|
|
11
|
+
"augmented-reality",
|
|
12
|
+
"glb",
|
|
13
|
+
"product-visualization",
|
|
14
|
+
"furniture",
|
|
15
|
+
"ecommerce",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://www.viewrium.com/docs/sdks/javascript",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://www.viewrium.com/docs",
|
|
21
|
+
"email": "support@viewrium.com"
|
|
22
|
+
},
|
|
23
|
+
"author": "Viewrium",
|
|
5
24
|
"license": "Apache-2.0",
|
|
6
25
|
"type": "module",
|
|
7
26
|
"sideEffects": false,
|
|
@@ -17,6 +36,7 @@
|
|
|
17
36
|
},
|
|
18
37
|
"files": [
|
|
19
38
|
"dist",
|
|
39
|
+
"README.md",
|
|
20
40
|
"LICENSE"
|
|
21
41
|
],
|
|
22
42
|
"publishConfig": {
|
|
@@ -28,8 +48,8 @@
|
|
|
28
48
|
"tsup": "^8.5.1",
|
|
29
49
|
"typescript": "^5.9.3",
|
|
30
50
|
"vitest": "^4.1.10",
|
|
31
|
-
"@repo/
|
|
32
|
-
"@repo/
|
|
51
|
+
"@repo/eslint-config": "0.0.0",
|
|
52
|
+
"@repo/typescript-config": "0.0.0"
|
|
33
53
|
},
|
|
34
54
|
"scripts": {
|
|
35
55
|
"generate": "openapi-typescript ../../apps/api/openapi.json --output src/generated/schema.ts",
|