@viewrium/sdk 0.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/dist/index.js ADDED
@@ -0,0 +1,438 @@
1
+ // src/errors.ts
2
+ var ViewriumError = class extends Error {
3
+ constructor(message, options = {}) {
4
+ super(message);
5
+ this.name = "ViewriumError";
6
+ this.statusCode = options.statusCode;
7
+ this.responseBody = options.responseBody;
8
+ }
9
+ };
10
+ var AuthError = class extends ViewriumError {
11
+ constructor(message, options = {}) {
12
+ super(message, options);
13
+ this.name = "AuthError";
14
+ }
15
+ };
16
+ var NotFoundError = class extends ViewriumError {
17
+ constructor(message, options = {}) {
18
+ super(message, options);
19
+ this.name = "NotFoundError";
20
+ }
21
+ };
22
+ var ValidationError = class extends ViewriumError {
23
+ constructor(message, options = {}) {
24
+ super(message, options);
25
+ this.name = "ValidationError";
26
+ }
27
+ };
28
+ var RateLimitError = class extends ViewriumError {
29
+ constructor(message, options = {}) {
30
+ super(message, options);
31
+ this.name = "RateLimitError";
32
+ this.retryAfter = options.retryAfter;
33
+ }
34
+ };
35
+ var APIError = class extends ViewriumError {
36
+ constructor(message, options = {}) {
37
+ super(message, options);
38
+ this.name = "APIError";
39
+ }
40
+ };
41
+
42
+ // src/resources/models.ts
43
+ var BASE = "/v1/models";
44
+ var ModelsResource = class {
45
+ constructor(client) {
46
+ this.client = client;
47
+ }
48
+ async create(params) {
49
+ const hasImages = Array.isArray(params.images) && params.images.length > 0;
50
+ const hasSource = typeof params.sourceUrl === "string" && params.sourceUrl.length > 0;
51
+ if (hasImages === hasSource) {
52
+ throw new Error(
53
+ "Provide exactly one of `images` (URL list) or `sourceUrl` to models.create()."
54
+ );
55
+ }
56
+ const body = stripUndefined({
57
+ custom_id: params.customId,
58
+ title: params.title,
59
+ description: params.description,
60
+ image_urls: params.images,
61
+ source_url: params.sourceUrl,
62
+ height: params.height,
63
+ size_unit: params.sizeUnit,
64
+ tags: params.tags,
65
+ price: params.price,
66
+ currency: params.currency,
67
+ metadata: params.metadata,
68
+ display_enabled: params.displayEnabled
69
+ });
70
+ return this.client.request({ method: "POST", path: BASE, json: body });
71
+ }
72
+ async createFromImages(images, params = {}) {
73
+ if (!Array.isArray(images) || images.length < 1) {
74
+ throw new Error("At least 1 image is required.");
75
+ }
76
+ const form = new FormData();
77
+ for (const image of images) {
78
+ const { blob, filename } = await toBlob(image, "image");
79
+ form.append("images", blob, filename);
80
+ }
81
+ appendMultipartCommon(form, params);
82
+ return this.client.request({
83
+ method: "POST",
84
+ path: `${BASE}/from-images`,
85
+ body: form
86
+ });
87
+ }
88
+ async createFromVideo(video, params = {}) {
89
+ const { blob, filename } = await toBlob(video, "video");
90
+ const form = new FormData();
91
+ form.append("video", blob, filename);
92
+ appendMultipartCommon(form, params);
93
+ return this.client.request({
94
+ method: "POST",
95
+ path: `${BASE}/from-video`,
96
+ body: form
97
+ });
98
+ }
99
+ async retrieve(modelId) {
100
+ return this.client.request({ method: "GET", path: `${BASE}/${encodeURIComponent(modelId)}` });
101
+ }
102
+ async list(params = {}) {
103
+ const query = {};
104
+ if (params.tag !== void 0) query.tag = params.tag;
105
+ if (params.tags !== void 0) {
106
+ query.tags = Array.isArray(params.tags) ? params.tags.join(",") : params.tags;
107
+ }
108
+ if (params.customId !== void 0) query.custom_id = params.customId;
109
+ if (params.status !== void 0) query.status = params.status;
110
+ if (params.limit !== void 0) query.limit = params.limit;
111
+ if (params.offset !== void 0) query.offset = params.offset;
112
+ return this.client.request({
113
+ method: "GET",
114
+ path: BASE,
115
+ query
116
+ });
117
+ }
118
+ async modify(modelId, params) {
119
+ const body = stripUndefined({
120
+ custom_id: params.customId,
121
+ title: params.title,
122
+ description: params.description,
123
+ price: params.price,
124
+ currency: params.currency,
125
+ height: params.height,
126
+ size_unit: params.sizeUnit,
127
+ tags: params.tags,
128
+ metadata: params.metadata,
129
+ display_enabled: params.displayEnabled
130
+ });
131
+ return this.client.request({
132
+ method: "PATCH",
133
+ path: `${BASE}/${encodeURIComponent(modelId)}`,
134
+ json: body
135
+ });
136
+ }
137
+ async delete(modelId) {
138
+ return this.client.request({
139
+ method: "DELETE",
140
+ path: `${BASE}/${encodeURIComponent(modelId)}`
141
+ });
142
+ }
143
+ async listEvents(modelId, params = {}) {
144
+ const query = {};
145
+ if (params.limit !== void 0) query.limit = params.limit;
146
+ if (params.offset !== void 0) query.offset = params.offset;
147
+ return this.client.request({
148
+ method: "GET",
149
+ path: `${BASE}/${encodeURIComponent(modelId)}/events`,
150
+ query
151
+ });
152
+ }
153
+ };
154
+ function stripUndefined(obj) {
155
+ const out = {};
156
+ for (const [key, value] of Object.entries(obj)) {
157
+ if (value !== void 0) out[key] = value;
158
+ }
159
+ return out;
160
+ }
161
+ function appendMultipartCommon(form, params) {
162
+ if (params.title !== void 0) form.append("title", params.title);
163
+ if (params.customId !== void 0) form.append("custom_id", params.customId);
164
+ if (params.description !== void 0) form.append("description", params.description);
165
+ if (params.height !== void 0) form.append("height", String(params.height));
166
+ if (params.sizeUnit !== void 0) form.append("size_unit", params.sizeUnit);
167
+ if (params.tags !== void 0) form.append("tags", JSON.stringify(params.tags));
168
+ if (params.displayEnabled !== void 0) {
169
+ form.append("display_enabled", params.displayEnabled ? "true" : "false");
170
+ }
171
+ }
172
+ async function toBlob(input, fallbackKind) {
173
+ if (typeof input === "string") {
174
+ const { readFile } = await import('fs/promises');
175
+ const { basename } = await import('path');
176
+ const data = await readFile(input);
177
+ const blob = new Blob([data]);
178
+ return { blob, filename: basename(input) };
179
+ }
180
+ if (input instanceof Blob) {
181
+ const filename = input.name && typeof input.name === "string" ? input.name : defaultFilename(fallbackKind);
182
+ return { blob: input, filename };
183
+ }
184
+ if (input && typeof input === "object" && "data" in input) {
185
+ const { data, filename, contentType } = input;
186
+ const part = data instanceof Blob ? data : data instanceof ArrayBuffer ? data : data;
187
+ const blob = new Blob([part], contentType ? { type: contentType } : void 0);
188
+ return { blob, filename: filename ?? defaultFilename(fallbackKind) };
189
+ }
190
+ throw new Error("Unsupported file input: expected string path, Blob, File, or {data, filename}.");
191
+ }
192
+ function defaultFilename(kind) {
193
+ return kind === "image" ? "image.bin" : "video.bin";
194
+ }
195
+
196
+ // src/resources/apiKeys.ts
197
+ var BASE2 = "/v1/api_keys";
198
+ var ApiKeysResource = class {
199
+ constructor(client) {
200
+ this.client = client;
201
+ }
202
+ async create(params = {}) {
203
+ const body = {};
204
+ if (params.name !== void 0) body.name = params.name;
205
+ if (params.keyType !== void 0) body.key_type = params.keyType;
206
+ return this.client.request({
207
+ method: "POST",
208
+ path: BASE2,
209
+ json: body
210
+ });
211
+ }
212
+ async list() {
213
+ return this.client.request({ method: "GET", path: BASE2 });
214
+ }
215
+ async delete(keyId) {
216
+ return this.client.request({
217
+ method: "DELETE",
218
+ path: `${BASE2}/${encodeURIComponent(keyId)}`
219
+ });
220
+ }
221
+ };
222
+
223
+ // src/resources/usage.ts
224
+ var BASE3 = "/v1/usage";
225
+ var UsageResource = class {
226
+ constructor(client) {
227
+ this.client = client;
228
+ }
229
+ async retrieve(params = {}) {
230
+ const query = {};
231
+ if (params.since !== void 0) query.since = params.since;
232
+ if (params.until !== void 0) query.until = params.until;
233
+ return this.client.request({
234
+ method: "GET",
235
+ path: BASE3,
236
+ query
237
+ });
238
+ }
239
+ };
240
+
241
+ // src/resources/tags.ts
242
+ var BASE4 = "/v1/tags";
243
+ var TagsResource = class {
244
+ constructor(client) {
245
+ this.client = client;
246
+ }
247
+ async list() {
248
+ return this.client.request({ method: "GET", path: BASE4 });
249
+ }
250
+ };
251
+
252
+ // src/client.ts
253
+ var DEFAULT_API_BASE = "https://api.viewrium.com";
254
+ var DEFAULT_TIMEOUT_MS = 6e4;
255
+ var DEFAULT_MAX_RETRIES = 3;
256
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
257
+ var VERSION = "0.1.0";
258
+ var Viewrium = class {
259
+ constructor(apiKey, options = {}) {
260
+ const envKey = typeof process !== "undefined" ? process.env?.VIEWRIUM_API_KEY : void 0;
261
+ const resolvedKey = options.apiKey ?? apiKey ?? envKey;
262
+ if (!resolvedKey) {
263
+ throw new AuthError(
264
+ "No API key configured. Pass `new Viewrium('sk_live_...')` or set VIEWRIUM_API_KEY."
265
+ );
266
+ }
267
+ this.apiKey = resolvedKey;
268
+ const envBase = typeof process !== "undefined" ? process.env?.VIEWRIUM_API_BASE : void 0;
269
+ this.apiBase = (options.apiBase ?? envBase ?? DEFAULT_API_BASE).replace(/\/+$/, "");
270
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
271
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
272
+ const providedFetch = options.fetch;
273
+ if (providedFetch) {
274
+ this.fetchImpl = providedFetch;
275
+ } else if (typeof globalThis.fetch === "function") {
276
+ this.fetchImpl = globalThis.fetch.bind(globalThis);
277
+ } else {
278
+ throw new Error(
279
+ "No `fetch` implementation available. Use Node 18+ or pass `fetch` in options."
280
+ );
281
+ }
282
+ this.models = new ModelsResource(this);
283
+ this.apiKeys = new ApiKeysResource(this);
284
+ this.usage = new UsageResource(this);
285
+ this.tags = new TagsResource(this);
286
+ }
287
+ /** Low-level request. Resources delegate here. */
288
+ async request(options) {
289
+ const url = this.buildUrl(options.path, options.query);
290
+ const headers = this.buildHeaders(options);
291
+ const body = this.buildBody(options);
292
+ let attempt = 0;
293
+ let lastResponse;
294
+ while (true) {
295
+ lastResponse = void 0;
296
+ const controller = new AbortController();
297
+ const timeoutMs = options.timeoutMs ?? this.timeoutMs;
298
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
299
+ try {
300
+ lastResponse = await this.fetchImpl(url, {
301
+ method: options.method,
302
+ headers,
303
+ body,
304
+ signal: controller.signal
305
+ });
306
+ } catch (err) {
307
+ clearTimeout(timer);
308
+ if (attempt >= this.maxRetries) throw err;
309
+ await sleep(backoffMs(void 0, attempt));
310
+ attempt += 1;
311
+ continue;
312
+ }
313
+ clearTimeout(timer);
314
+ if (RETRYABLE_STATUSES.has(lastResponse.status) && attempt < this.maxRetries) {
315
+ await sleep(backoffMs(lastResponse, attempt));
316
+ attempt += 1;
317
+ continue;
318
+ }
319
+ break;
320
+ }
321
+ if (!lastResponse.ok) {
322
+ await raiseForStatus(lastResponse);
323
+ }
324
+ return await parseResponse(lastResponse);
325
+ }
326
+ buildUrl(path, query) {
327
+ const full = path.startsWith("http://") || path.startsWith("https://") ? path : `${this.apiBase}${path.startsWith("/") ? path : `/${path}`}`;
328
+ if (!query) return full;
329
+ const params = new URLSearchParams();
330
+ for (const [key, value] of Object.entries(query)) {
331
+ if (value === void 0 || value === null) continue;
332
+ if (Array.isArray(value)) {
333
+ for (const v of value) {
334
+ if (v !== void 0 && v !== null) params.append(key, String(v));
335
+ }
336
+ } else {
337
+ params.append(key, String(value));
338
+ }
339
+ }
340
+ const qs = params.toString();
341
+ return qs ? `${full}?${qs}` : full;
342
+ }
343
+ buildHeaders(options) {
344
+ const headers = new Headers(options.headers);
345
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
346
+ headers.set("Accept", "application/json");
347
+ if (!headers.has("User-Agent") && typeof process !== "undefined") {
348
+ try {
349
+ headers.set("User-Agent", `viewrium-js/${VERSION}`);
350
+ } catch {
351
+ }
352
+ }
353
+ if (options.json !== void 0 && !headers.has("Content-Type")) {
354
+ headers.set("Content-Type", "application/json");
355
+ }
356
+ return headers;
357
+ }
358
+ buildBody(options) {
359
+ if (options.body !== void 0) return options.body;
360
+ if (options.json !== void 0) return JSON.stringify(options.json);
361
+ return void 0;
362
+ }
363
+ };
364
+ async function parseResponse(response) {
365
+ if (response.status === 204) return {};
366
+ const text = await response.text();
367
+ if (!text) return {};
368
+ try {
369
+ return JSON.parse(text);
370
+ } catch (e) {
371
+ throw new APIError(`Failed to parse JSON response: ${e.message}`, {
372
+ statusCode: response.status,
373
+ responseBody: text
374
+ });
375
+ }
376
+ }
377
+ async function raiseForStatus(response) {
378
+ const body = await parseBodyForError(response);
379
+ const detail = extractDetail(body, response);
380
+ const status = response.status;
381
+ const opts = { statusCode: status, responseBody: body };
382
+ if (status === 401) throw new AuthError(detail, opts);
383
+ if (status === 404) throw new NotFoundError(detail, opts);
384
+ if (status === 400 || status === 409 || status === 422) {
385
+ throw new ValidationError(detail, opts);
386
+ }
387
+ if (status === 429) {
388
+ throw new RateLimitError(detail, {
389
+ ...opts,
390
+ retryAfter: parseRetryAfter(response)
391
+ });
392
+ }
393
+ throw new APIError(detail, opts);
394
+ }
395
+ async function parseBodyForError(response) {
396
+ const text = await response.text();
397
+ if (!text) return void 0;
398
+ try {
399
+ return JSON.parse(text);
400
+ } catch {
401
+ return text;
402
+ }
403
+ }
404
+ function extractDetail(body, response) {
405
+ if (body && typeof body === "object" && !Array.isArray(body)) {
406
+ const obj = body;
407
+ const candidate = obj.detail ?? obj.error ?? obj.message;
408
+ if (typeof candidate === "string" && candidate) return candidate;
409
+ if (candidate !== void 0) return String(candidate);
410
+ }
411
+ if (typeof body === "string" && body) return body;
412
+ return `HTTP ${response.status}`;
413
+ }
414
+ function parseRetryAfter(response) {
415
+ const raw = response.headers.get("Retry-After");
416
+ if (!raw) return void 0;
417
+ const n = Number(raw);
418
+ return Number.isFinite(n) ? n : void 0;
419
+ }
420
+ function backoffMs(response, attempt) {
421
+ if (response) {
422
+ const hinted = parseRetryAfter(response);
423
+ if (hinted !== void 0) return hinted * 1e3;
424
+ }
425
+ const base = Math.min(2 ** attempt, 30);
426
+ const jitter = Math.random() * 0.25;
427
+ return (base + jitter) * 1e3;
428
+ }
429
+ function sleep(ms) {
430
+ return new Promise((resolve) => setTimeout(resolve, ms));
431
+ }
432
+
433
+ // src/index.ts
434
+ var index_default = Viewrium;
435
+
436
+ export { APIError, AuthError, NotFoundError, RateLimitError, ValidationError, Viewrium, ViewriumError, index_default as default };
437
+ //# sourceMappingURL=index.js.map
438
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/resources/models.ts","../src/resources/apiKeys.ts","../src/resources/usage.ts","../src/resources/tags.ts","../src/client.ts","../src/index.ts"],"names":["BASE"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Typed error hierarchy for the Viewrium SDK.\n *\n * All API-originated failures inherit from `ViewriumError`. Network-level\n * failures (connection reset, DNS) bubble up as the underlying fetch error\n * unchanged.\n */\n\nexport class ViewriumError extends Error {\n readonly statusCode: number | undefined;\n readonly responseBody: unknown;\n\n constructor(\n message: string,\n options: { statusCode?: number; responseBody?: unknown } = {},\n ) {\n super(message);\n this.name = \"ViewriumError\";\n this.statusCode = options.statusCode;\n this.responseBody = options.responseBody;\n }\n}\n\nexport class AuthError extends ViewriumError {\n constructor(\n message: string,\n options: { statusCode?: number; responseBody?: unknown } = {},\n ) {\n super(message, options);\n this.name = \"AuthError\";\n }\n}\n\nexport class NotFoundError extends ViewriumError {\n constructor(\n message: string,\n options: { statusCode?: number; responseBody?: unknown } = {},\n ) {\n super(message, options);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class ValidationError extends ViewriumError {\n constructor(\n message: string,\n options: { statusCode?: number; responseBody?: unknown } = {},\n ) {\n super(message, options);\n this.name = \"ValidationError\";\n }\n}\n\nexport class RateLimitError extends ViewriumError {\n readonly retryAfter: number | undefined;\n\n constructor(\n message: string,\n options: {\n statusCode?: number;\n responseBody?: unknown;\n retryAfter?: number;\n } = {},\n ) {\n super(message, options);\n this.name = \"RateLimitError\";\n this.retryAfter = options.retryAfter;\n }\n}\n\nexport class APIError extends ViewriumError {\n constructor(\n message: string,\n options: { statusCode?: number; responseBody?: unknown } = {},\n ) {\n super(message, options);\n this.name = \"APIError\";\n }\n}\n","/**\n * Models resource - 3D models (backed by the `scans` table).\n *\n * Mirrors the 8 `/v1/models*` endpoints. camelCase param names are mapped\n * to the snake_case wire contract on the way out; response shapes pass\n * through as-is.\n */\n\nimport type { Viewrium } from \"../client.js\";\nimport type {\n DeletedResponse,\n FileInput,\n Model,\n ModelCreateFromImagesParams,\n ModelCreateFromVideoParams,\n ModelCreateParams,\n ModelEventListResponse,\n ModelListEventsParams,\n ModelListParams,\n ModelListResponse,\n ModelUpdateParams,\n} from \"../types.js\";\n\nconst BASE = \"/v1/models\";\n\nexport class ModelsResource {\n constructor(private readonly client: Viewrium) {}\n\n async create(params: ModelCreateParams): Promise<Model> {\n const hasImages = Array.isArray(params.images) && params.images.length > 0;\n const hasSource = typeof params.sourceUrl === \"string\" && params.sourceUrl.length > 0;\n if (hasImages === hasSource) {\n throw new Error(\n \"Provide exactly one of `images` (URL list) or `sourceUrl` to models.create().\",\n );\n }\n const body = stripUndefined({\n custom_id: params.customId,\n title: params.title,\n description: params.description,\n image_urls: params.images,\n source_url: params.sourceUrl,\n height: params.height,\n size_unit: params.sizeUnit,\n tags: params.tags,\n price: params.price,\n currency: params.currency,\n metadata: params.metadata,\n display_enabled: params.displayEnabled,\n });\n return this.client.request<Model>({ method: \"POST\", path: BASE, json: body });\n }\n\n async createFromImages(\n images: FileInput[],\n params: ModelCreateFromImagesParams = {},\n ): Promise<Model> {\n if (!Array.isArray(images) || images.length < 1) {\n throw new Error(\"At least 1 image is required.\");\n }\n const form = new FormData();\n for (const image of images) {\n const { blob, filename } = await toBlob(image, \"image\");\n form.append(\"images\", blob, filename);\n }\n appendMultipartCommon(form, params);\n return this.client.request<Model>({\n method: \"POST\",\n path: `${BASE}/from-images`,\n body: form,\n });\n }\n\n async createFromVideo(\n video: FileInput,\n params: ModelCreateFromVideoParams = {},\n ): Promise<Model> {\n const { blob, filename } = await toBlob(video, \"video\");\n const form = new FormData();\n form.append(\"video\", blob, filename);\n appendMultipartCommon(form, params);\n return this.client.request<Model>({\n method: \"POST\",\n path: `${BASE}/from-video`,\n body: form,\n });\n }\n\n async retrieve(modelId: string): Promise<Model> {\n return this.client.request<Model>({ method: \"GET\", path: `${BASE}/${encodeURIComponent(modelId)}` });\n }\n\n async list(params: ModelListParams = {}): Promise<ModelListResponse> {\n const query: Record<string, unknown> = {};\n if (params.tag !== undefined) query.tag = params.tag;\n if (params.tags !== undefined) {\n query.tags = Array.isArray(params.tags) ? params.tags.join(\",\") : params.tags;\n }\n if (params.customId !== undefined) query.custom_id = params.customId;\n if (params.status !== undefined) query.status = params.status;\n if (params.limit !== undefined) query.limit = params.limit;\n if (params.offset !== undefined) query.offset = params.offset;\n return this.client.request<ModelListResponse>({\n method: \"GET\",\n path: BASE,\n query,\n });\n }\n\n async modify(modelId: string, params: ModelUpdateParams): Promise<Model> {\n const body = stripUndefined({\n custom_id: params.customId,\n title: params.title,\n description: params.description,\n price: params.price,\n currency: params.currency,\n height: params.height,\n size_unit: params.sizeUnit,\n tags: params.tags,\n metadata: params.metadata,\n display_enabled: params.displayEnabled,\n });\n return this.client.request<Model>({\n method: \"PATCH\",\n path: `${BASE}/${encodeURIComponent(modelId)}`,\n json: body,\n });\n }\n\n async delete(modelId: string): Promise<DeletedResponse> {\n return this.client.request<DeletedResponse>({\n method: \"DELETE\",\n path: `${BASE}/${encodeURIComponent(modelId)}`,\n });\n }\n\n async listEvents(\n modelId: string,\n params: ModelListEventsParams = {},\n ): Promise<ModelEventListResponse> {\n const query: Record<string, unknown> = {};\n if (params.limit !== undefined) query.limit = params.limit;\n if (params.offset !== undefined) query.offset = params.offset;\n return this.client.request<ModelEventListResponse>({\n method: \"GET\",\n path: `${BASE}/${encodeURIComponent(modelId)}/events`,\n query,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction stripUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) out[key] = value;\n }\n return out as Partial<T>;\n}\n\nfunction appendMultipartCommon(\n form: FormData,\n params: ModelCreateFromImagesParams | ModelCreateFromVideoParams,\n): void {\n if (params.title !== undefined) form.append(\"title\", params.title);\n if (params.customId !== undefined) form.append(\"custom_id\", params.customId);\n if (params.description !== undefined) form.append(\"description\", params.description);\n if (params.height !== undefined) form.append(\"height\", String(params.height));\n if (params.sizeUnit !== undefined) form.append(\"size_unit\", params.sizeUnit);\n if (params.tags !== undefined) form.append(\"tags\", JSON.stringify(params.tags));\n if (params.displayEnabled !== undefined) {\n form.append(\"display_enabled\", params.displayEnabled ? \"true\" : \"false\");\n }\n}\n\n/**\n * Normalize a `FileInput` to a Blob + filename suitable for `FormData.append`.\n *\n * - string → treated as a filesystem path (Node-only; dynamic-imports `node:fs`).\n * - Blob/File → used directly; filename inferred from `File.name` if present.\n * - {data, filename?, contentType?} → wrapped in a Blob.\n */\nasync function toBlob(\n input: FileInput,\n fallbackKind: \"image\" | \"video\",\n): Promise<{ blob: Blob; filename: string }> {\n if (typeof input === \"string\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { basename } = await import(\"node:path\");\n const data = await readFile(input);\n const blob = new Blob([data as unknown as BlobPart]);\n return { blob, filename: basename(input) };\n }\n if (input instanceof Blob) {\n const filename =\n (input as File).name && typeof (input as File).name === \"string\"\n ? (input as File).name\n : defaultFilename(fallbackKind);\n return { blob: input, filename };\n }\n if (input && typeof input === \"object\" && \"data\" in input) {\n const { data, filename, contentType } = input;\n const part: BlobPart =\n data instanceof Blob\n ? data\n : data instanceof ArrayBuffer\n ? data\n : (data as unknown as BlobPart);\n const blob = new Blob([part], contentType ? { type: contentType } : undefined);\n return { blob, filename: filename ?? defaultFilename(fallbackKind) };\n }\n throw new Error(\"Unsupported file input: expected string path, Blob, File, or {data, filename}.\");\n}\n\nfunction defaultFilename(kind: \"image\" | \"video\"): string {\n return kind === \"image\" ? \"image.bin\" : \"video.bin\";\n}\n","/**\n * Api keys resource.\n *\n * Creates secret (`sk_*`) or publishable (`pk_*`) keys. pk keys inherit\n * origin restrictions from their project's `allowed_domains`. The plaintext\n * key is returned ONCE from `create()`; store it securely on your side.\n */\n\nimport type { Viewrium } from \"../client.js\";\nimport type {\n ApiKeyCreateParams,\n ApiKeyListResponse,\n ApiKeyWithSecret,\n RevokedResponse,\n} from \"../types.js\";\n\nconst BASE = \"/v1/api_keys\";\n\nexport class ApiKeysResource {\n constructor(private readonly client: Viewrium) {}\n\n async create(params: ApiKeyCreateParams = {}): Promise<ApiKeyWithSecret> {\n const body: Record<string, unknown> = {};\n if (params.name !== undefined) body.name = params.name;\n if (params.keyType !== undefined) body.key_type = params.keyType;\n return this.client.request<ApiKeyWithSecret>({\n method: \"POST\",\n path: BASE,\n json: body,\n });\n }\n\n async list(): Promise<ApiKeyListResponse> {\n return this.client.request<ApiKeyListResponse>({ method: \"GET\", path: BASE });\n }\n\n async delete(keyId: string): Promise<RevokedResponse> {\n return this.client.request<RevokedResponse>({\n method: \"DELETE\",\n path: `${BASE}/${encodeURIComponent(keyId)}`,\n });\n }\n}\n","/**\n * Usage resource.\n *\n * Returns aggregated event counts for the caller's organization. Internal\n * service events (rodin / gemini / scrape / convert / store) are never\n * exposed here.\n */\n\nimport type { Viewrium } from \"../client.js\";\nimport type { UsageRetrieveParams, UsageSummary } from \"../types.js\";\n\nconst BASE = \"/v1/usage\";\n\nexport class UsageResource {\n constructor(private readonly client: Viewrium) {}\n\n async retrieve(params: UsageRetrieveParams = {}): Promise<UsageSummary> {\n const query: Record<string, unknown> = {};\n if (params.since !== undefined) query.since = params.since;\n if (params.until !== undefined) query.until = params.until;\n return this.client.request<UsageSummary>({\n method: \"GET\",\n path: BASE,\n query,\n });\n }\n}\n","/**\n * Tags resource.\n *\n * Lists unique tags across the caller's organization with per-tag model\n * counts. Tags are created implicitly when assigned to a model; there is\n * no POST /v1/tags endpoint.\n */\n\nimport type { Viewrium } from \"../client.js\";\nimport type { TagListResponse } from \"../types.js\";\n\nconst BASE = \"/v1/tags\";\n\nexport class TagsResource {\n constructor(private readonly client: Viewrium) {}\n\n async list(): Promise<TagListResponse> {\n return this.client.request<TagListResponse>({ method: \"GET\", path: BASE });\n }\n}\n","/**\n * Viewrium client.\n *\n * Instantiate once per api key:\n *\n * const viewrium = new Viewrium(process.env.VIEWRIUM_API_KEY!);\n * const model = await viewrium.models.create({ images: [...] });\n *\n * The client uses `globalThis.fetch` by default (Node 18+, browsers, edge\n * runtimes all provide it). Pass `fetch` in options for custom transports\n * or testing.\n */\n\nimport {\n APIError,\n AuthError,\n NotFoundError,\n RateLimitError,\n ValidationError,\n ViewriumError,\n} from \"./errors.js\";\nimport type { ViewriumOptions } from \"./types.js\";\nimport { ModelsResource } from \"./resources/models.js\";\nimport { ApiKeysResource } from \"./resources/apiKeys.js\";\nimport { UsageResource } from \"./resources/usage.js\";\nimport { TagsResource } from \"./resources/tags.js\";\n\nconst DEFAULT_API_BASE = \"https://api.viewrium.com\";\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504]);\nconst VERSION = \"0.1.0\";\n\nexport interface RequestOptions {\n method: string;\n path: string;\n query?: Record<string, unknown>;\n json?: unknown;\n body?: BodyInit;\n headers?: Record<string, string>;\n /** Override per-request timeout in ms. */\n timeoutMs?: number;\n}\n\nexport class Viewrium {\n readonly models: ModelsResource;\n readonly apiKeys: ApiKeysResource;\n readonly usage: UsageResource;\n readonly tags: TagsResource;\n\n private readonly apiKey: string;\n private readonly apiBase: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(apiKey?: string, options: ViewriumOptions = {}) {\n const envKey =\n typeof process !== \"undefined\" ? process.env?.VIEWRIUM_API_KEY : undefined;\n const resolvedKey = options.apiKey ?? apiKey ?? envKey;\n if (!resolvedKey) {\n throw new AuthError(\n \"No API key configured. Pass `new Viewrium('sk_live_...')` or set VIEWRIUM_API_KEY.\",\n );\n }\n this.apiKey = resolvedKey;\n\n const envBase =\n typeof process !== \"undefined\" ? process.env?.VIEWRIUM_API_BASE : undefined;\n this.apiBase = (options.apiBase ?? envBase ?? DEFAULT_API_BASE).replace(/\\/+$/, \"\");\n\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n const providedFetch = options.fetch;\n if (providedFetch) {\n this.fetchImpl = providedFetch;\n } else if (typeof globalThis.fetch === \"function\") {\n this.fetchImpl = globalThis.fetch.bind(globalThis);\n } else {\n throw new Error(\n \"No `fetch` implementation available. Use Node 18+ or pass `fetch` in options.\",\n );\n }\n\n this.models = new ModelsResource(this);\n this.apiKeys = new ApiKeysResource(this);\n this.usage = new UsageResource(this);\n this.tags = new TagsResource(this);\n }\n\n /** Low-level request. Resources delegate here. */\n async request<T = unknown>(options: RequestOptions): Promise<T> {\n const url = this.buildUrl(options.path, options.query);\n const headers = this.buildHeaders(options);\n const body = this.buildBody(options);\n\n let attempt = 0;\n let lastResponse: Response | undefined;\n while (true) {\n lastResponse = undefined;\n const controller = new AbortController();\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n lastResponse = await this.fetchImpl(url, {\n method: options.method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n if (attempt >= this.maxRetries) throw err;\n await sleep(backoffMs(undefined, attempt));\n attempt += 1;\n continue;\n }\n clearTimeout(timer);\n\n if (\n RETRYABLE_STATUSES.has(lastResponse.status) &&\n attempt < this.maxRetries\n ) {\n await sleep(backoffMs(lastResponse, attempt));\n attempt += 1;\n continue;\n }\n break;\n }\n\n if (!lastResponse.ok) {\n await raiseForStatus(lastResponse);\n }\n return (await parseResponse(lastResponse)) as T;\n }\n\n private buildUrl(path: string, query?: Record<string, unknown>): string {\n const full =\n path.startsWith(\"http://\") || path.startsWith(\"https://\")\n ? path\n : `${this.apiBase}${path.startsWith(\"/\") ? path : `/${path}`}`;\n if (!query) return full;\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) {\n for (const v of value) {\n if (v !== undefined && v !== null) params.append(key, String(v));\n }\n } else {\n params.append(key, String(value));\n }\n }\n const qs = params.toString();\n return qs ? `${full}?${qs}` : full;\n }\n\n private buildHeaders(options: RequestOptions): Headers {\n const headers = new Headers(options.headers);\n headers.set(\"Authorization\", `Bearer ${this.apiKey}`);\n headers.set(\"Accept\", \"application/json\");\n if (!headers.has(\"User-Agent\") && typeof process !== \"undefined\") {\n // `fetch` in browsers forbids setting User-Agent; it's harmless in Node.\n try {\n headers.set(\"User-Agent\", `viewrium-js/${VERSION}`);\n } catch {\n /* ignore */\n }\n }\n if (options.json !== undefined && !headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n return headers;\n }\n\n private buildBody(options: RequestOptions): BodyInit | undefined {\n if (options.body !== undefined) return options.body;\n if (options.json !== undefined) return JSON.stringify(options.json);\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function parseResponse(response: Response): Promise<unknown> {\n if (response.status === 204) return {};\n const text = await response.text();\n if (!text) return {};\n try {\n return JSON.parse(text);\n } catch (e) {\n throw new APIError(`Failed to parse JSON response: ${(e as Error).message}`, {\n statusCode: response.status,\n responseBody: text,\n });\n }\n}\n\nasync function raiseForStatus(response: Response): Promise<never> {\n const body = await parseBodyForError(response);\n const detail = extractDetail(body, response);\n const status = response.status;\n const opts = { statusCode: status, responseBody: body };\n if (status === 401) throw new AuthError(detail, opts);\n if (status === 404) throw new NotFoundError(detail, opts);\n if (status === 400 || status === 409 || status === 422) {\n throw new ValidationError(detail, opts);\n }\n if (status === 429) {\n throw new RateLimitError(detail, {\n ...opts,\n retryAfter: parseRetryAfter(response),\n });\n }\n throw new APIError(detail, opts);\n}\n\nasync function parseBodyForError(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction extractDetail(body: unknown, response: Response): string {\n if (body && typeof body === \"object\" && !Array.isArray(body)) {\n const obj = body as Record<string, unknown>;\n const candidate = obj.detail ?? obj.error ?? obj.message;\n if (typeof candidate === \"string\" && candidate) return candidate;\n if (candidate !== undefined) return String(candidate);\n }\n if (typeof body === \"string\" && body) return body;\n return `HTTP ${response.status}`;\n}\n\nfunction parseRetryAfter(response: Response): number | undefined {\n const raw = response.headers.get(\"Retry-After\");\n if (!raw) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction backoffMs(response: Response | undefined, attempt: number): number {\n if (response) {\n const hinted = parseRetryAfter(response);\n if (hinted !== undefined) return hinted * 1000;\n }\n const base = Math.min(2 ** attempt, 30);\n const jitter = Math.random() * 0.25;\n return (base + jitter) * 1000;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Re-export for resources to reuse without a circular dep loop. */\nexport { ViewriumError };\n","/**\n * Official JavaScript / TypeScript SDK for the Viewrium API.\n *\n * import Viewrium from \"@viewrium/sdk\";\n *\n * const viewrium = new Viewrium(process.env.VIEWRIUM_API_KEY!);\n *\n * const model = await viewrium.models.create({\n * customId: \"sku-1\",\n * images: [\"https://example.com/img1.jpg\"],\n * height: 90,\n * tags: [\"chair\", \"summer-2026\"],\n * });\n */\n\nexport { Viewrium } from \"./client.js\";\nexport type { RequestOptions } from \"./client.js\";\n\nexport {\n APIError,\n AuthError,\n NotFoundError,\n RateLimitError,\n ValidationError,\n ViewriumError,\n} from \"./errors.js\";\n\nexport type {\n ApiKey,\n ApiKeyCreateParams,\n ApiKeyListResponse,\n ApiKeyWithSecret,\n DeletedResponse,\n FileInput,\n Model,\n ModelCreateFromImagesParams,\n ModelCreateFromVideoParams,\n ModelCreateParams,\n ModelEvent,\n ModelEventListResponse,\n ModelListEventsParams,\n ModelListParams,\n ModelListResponse,\n ModelStatus,\n ModelUpdateParams,\n RevokedResponse,\n SizeUnit,\n SourceType,\n TagListResponse,\n TagSummary,\n UsageEventType,\n UsageRetrieveParams,\n UsageSummary,\n ViewriumOptions,\n} from \"./types.js\";\n\nexport type { ModelsResource } from \"./resources/models.js\";\nexport type { ApiKeysResource } from \"./resources/apiKeys.js\";\nexport type { UsageResource } from \"./resources/usage.js\";\nexport type { TagsResource } from \"./resources/tags.js\";\n\n// Default export for convenience: `import Viewrium from \"@viewrium/sdk\"`.\nimport { Viewrium } from \"./client.js\";\nexport default Viewrium;\n"]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@viewrium/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official JavaScript / TypeScript SDK for the Viewrium API. Create and manage 3D models, api keys, and usage records.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "keywords": [
23
+ "viewrium",
24
+ "3d",
25
+ "ar",
26
+ "model-viewer",
27
+ "sdk",
28
+ "api"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/davidzagyvaig/viewrium.git",
33
+ "directory": "packages/sdk-js"
34
+ },
35
+ "homepage": "https://viewrium.com",
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.0.0",
44
+ "tsup": "^8.3.0",
45
+ "typescript": "^5.5.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "typecheck": "tsc --noEmit",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }