{"_id":"@alliumcloud/odyssey-configurator-sdk","_rev":"4-4fc0656c6edd13a1356b2dfd1a8e86d7","name":"@alliumcloud/odyssey-configurator-sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alliumcloud/odyssey-configurator-sdk","version":"1.0.0","keywords":["odyssey","configurator-service","sdk","sketchfab","unreal"],"author":{"name":"New Game Plus Inc"},"license":"MIT","_id":"@alliumcloud/odyssey-configurator-sdk@1.0.0","maintainers":[{"name":"ashik.odyssey","email":"ashik@odyssey.stream"},{"name":"mahinabid","email":"mahin@odyssey.stream"},{"name":"odyssey_naim","email":"naim@odyssey.stream"},{"name":"odyssey.stream","email":"reid@odyssey.stream"},{"name":"imran6554","email":"imran@odyssey.stream"}],"homepage":"","dist":{"shasum":"70a5c98b3549555d7f3806fc008af30297fd732f","tarball":"https://registry.npmjs.org/@alliumcloud/odyssey-configurator-sdk/-/odyssey-configurator-sdk-1.0.0.tgz","fileCount":8,"integrity":"sha512-E47IzRafTPUVdKqfv3Y0rksDwHMSc58eARdIAANlmOISGaW+af2/Jx/OsVgYiBJoR3JKkfyozPr0keYRqQIaFQ==","signatures":[{"sig":"MEQCIG9Ttv5MPiujKPydp0UNrLCBXg6NAQcQT0CQZuwoQrddAiBo/wT9UbxgpGbFrA3paJEegCp/GLMt8PvwvWkV2XFD+g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121385},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"58dfe4425f761d5945013d11d3ad111b36857eb3","scripts":{"dev":"tsup --watch","lint":"eslint src/**/*.ts","build":"tsup","deploy":"node scripts/remove-console-logs.js && npm run build && echo '✅ Ready for deployment!'","format":"prettier --write \"src/**/*.ts\"","prepare":"husky","lint:fix":"eslint src/**/*.ts --fix","typecheck":"tsc --noEmit","clean:logs":"node scripts/remove-console-logs.js","publish:npm":"npm publish","format:check":"prettier --check \"src/**/*.ts\"","version:major":"npm version major","version:minor":"npm version minor","version:patch":"npm version patch","prepublishOnly":"npm run typecheck && npm run build"},"_npmUser":{"name":"mahinabid","email":"mahin@odyssey.stream"},"deprecated":"This package is deprecated. Use @alliumcloud/configurator-service instead.","repository":{"url":"","type":"git"},"_npmVersion":"11.6.2","description":"Official SDK for the Odyssey Configurator Service backend","directories":{},"lint-staged":{"src/**/*.ts":["prettier --write","eslint --fix"]},"sideEffects":false,"_nodeVersion":"24.13.0","dependencies":{"axios":"^1.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","husky":"^9.1.7","dotenv":"^17.3.1","eslint":"^9.39.2","globals":"^17.1.0","prettier":"^3.8.1","typescript":"^5.0.0","@types/node":"^20.0.0","lint-staged":"^16.2.7","typescript-eslint":"^8.54.0","@typescript-eslint/parser":"^8.54.0","@typescript-eslint/eslint-plugin":"^8.54.0"},"_npmOperationalInternal":{"tmp":"tmp/odyssey-configurator-sdk_1.0.0_1779304633210_0.45714780874525895","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-05-20T19:17:13.002Z","modified":"2026-07-08T16:29:59.544Z","1.0.0":"2026-05-20T19:17:13.358Z"},"author":{"name":"New Game Plus Inc"},"license":"MIT","keywords":["odyssey","configurator-service","sdk","sketchfab","unreal"],"repository":{"url":"","type":"git"},"description":"Official SDK for the Odyssey Configurator Service backend","maintainers":[{"email":"reid@odyssey.stream","name":"odyssey.stream"}],"readme":"# @alliumcloud/odyssey-configurator-sdk\n\n[![npm version](https://img.shields.io/npm/v/@alliumcloud/odyssey-configurator-sdk)](https://www.npmjs.com/package/@alliumcloud/odyssey-configurator-sdk)\n[![npm downloads](https://img.shields.io/npm/dt/@alliumcloud/odyssey-configurator-sdk)](https://www.npmjs.com/package/@alliumcloud/odyssey-configurator-sdk)\n\nOfficial TypeScript SDK for the Odyssey Configurator backend. Provides a fully typed, modular HTTP client for all space configuration operations — media screens, 3D objects, transforms, layers, and configurator items.\n\n---\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Architecture](#architecture)\n- [Configuration](#configuration)\n- [Error Handling](#error-handling)\n- [Debug Logging](#debug-logging)\n- [API Reference](#api-reference)\n  - [Media](#media)\n  - [Sketchfab](#sketchfab)\n  - [Transform](#transform)\n  - [Layer](#layer)\n  - [Configurator Items](#configurator-items)\n  - [Configurator Objects](#configurator-objects)\n- [Types Reference](#types-reference)\n  - [Config](#config)\n  - [Common](#common)\n  - [Media Types](#media-types)\n  - [Sketchfab Types](#sketchfab-types)\n  - [Transform Types](#transform-types)\n  - [Layer Types](#layer-types)\n  - [Configurator Item Types](#configurator-item-types)\n  - [Configurator Object Types](#configurator-object-types)\n  - [Error Types](#error-types)\n- [Versioning](#versioning)\n\n---\n\n## Installation\n\n```bash\nnpm install @alliumcloud/odyssey-configurator-sdk\n# or\nyarn add @alliumcloud/odyssey-configurator-sdk\n# or\npnpm add @alliumcloud/odyssey-configurator-sdk\n```\n\n---\n\n## Quick Start\n\n```typescript\nimport { createConfiguratorSDK } from \"@alliumcloud/odyssey-configurator-sdk\";\n\n// Initialize once — pass your API key, that's it.\nconst sdk = createConfiguratorSDK({\n  apiKey: \"your-api-key\",\n  jwksUri: \"https://your-auth-domain/.well-known/jwks.json\",\n  accessTokenProvider: async () => await yourAuthService.getToken(),\n});\n\n// Add a 3D model to a space\nawait sdk.sketchfab.addObject({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Model\",\n  type: \"RuntimeModel\",\n  gltfUrl: \"https://...\",\n  materialOverrideId: \"None\",\n  scale: { x: 1, y: 1, z: 1 },\n  autoScale: true,\n  offsetUniformScale: 1,\n  collisionsEnabled: false,\n  position: { x: 0, y: 0, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n});\n\n// Update its position\nawait sdk.transform.updatePosition(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  position: { x: 3, y: 0, z: 0 },\n});\n```\n\nYou can also use the namespace default export:\n\n```typescript\nimport ConfiguratorSDK from \"@alliumcloud/odyssey-configurator-sdk\";\n\nconst sdk = ConfiguratorSDK.create({\n  apiKey: \"your-api-key\",\n  jwksUri: \"https://your-auth-domain/.well-known/jwks.json\",\n  accessTokenProvider: async () => await yourAuthService.getToken(),\n});\n```\n\n---\n\n## Architecture\n\nThe SDK is structured as a set of isolated, focused modules. Each module handles one domain of the backend API. All modules share a single configured HTTP client that is created once at initialization.\n\n```\ncreateConfiguratorSDK({ apiKey })\n│\n├── sdk.media                → /media\n├── sdk.sketchfab            → /sketchfab\n├── sdk.transform            → /transform\n├── sdk.layer                → /layer\n├── sdk.configuratorItems    → /configurator_items\n└── sdk.configuratorObjects  → /configurator_objects\n```\n\n**Key design decisions:**\n\n- The backend base URL is baked into the SDK at build time. You never pass a URL — it is not configurable by consumers.\n- Your API key is passed once at initialization and is automatically injected as `x-sdk-key` on every request.\n- All errors are normalized into typed `OdysseyError` objects with a `code` and `name` so you can catch them reliably.\n- Debug logging is fully opt-in and zero-cost in production.\n\n---\n\n## Configuration\n\n```typescript\nimport {\n  createConfiguratorSDK,\n  ConfiguratorServiceSDKConfig,\n} from \"@alliumcloud/odyssey-configurator-sdk\";\n\nconst sdk = createConfiguratorSDK({\n  apiKey: \"your-api-key\",\n  jwksUri: \"https://your-auth-domain/.well-known/jwks.json\",\n  accessTokenProvider: async () => await yourAuthService.getToken(),\n  timeout: 60_000,\n});\n```\n\n| Option                | Type                                 | Required | Default | Description                                                                     |\n| --------------------- | ------------------------------------ | -------- | ------- | ------------------------------------------------------------------------------- |\n| `apiKey`              | `string`                             | Yes      | —       | API key issued after SDK purchase. Sent as `x-sdk-key` header on every request. |\n| `jwksUri`             | `string`                             | No       | —       | JWKS endpoint URI. Sent as `x-jwks-uri` on every request.                       |\n| `accessTokenProvider` | `() => string \\| Promise<string>`    | Yes      | —       | Called before every request to retrieve a Bearer token.                         |\n| `timeout`             | `number`                             | No       | `30000` | Request timeout in milliseconds.                                                |\n---\n\n## Error Handling\n\nAll SDK errors are instances of `OdysseyError`. You can use `isOdysseyError()` to safely narrow the type in catch blocks.\n\n```typescript\nimport { isOdysseyError } from \"@alliumcloud/odyssey-configurator-sdk\";\n\ntry {\n  await sdk.sketchfab.addObject({ ... });\n} catch (err) {\n  if (isOdysseyError(err)) {\n    console.log(err.name);    // e.g. 'OdysseyAuthError'\n    console.log(err.code);    // e.g. 'AUTH_ERROR'\n    console.log(err.message); // human-readable description\n  }\n}\n```\n\n### Error Types\n\n| `err.name`               | `err.code`         | When it is thrown                                           |\n| ------------------------ | ------------------ | ----------------------------------------------------------- |\n| `OdysseyConfigError`     | `INVALID_CONFIG`   | API key is missing or base URL not configured in the build. |\n| `OdysseyAuthError`       | `AUTH_ERROR`       | Server returns `401` or `403` — key is invalid or expired.  |\n| `OdysseyValidationError` | `VALIDATION_ERROR` | Server returns `400` or `422` — bad input data.             |\n| `OdysseyRequestError`    | `REQUEST_ERROR`    | Network failure, timeout, or any other `5xx` response.      |\n| `OdysseySDKError`        | `SDK_ERROR`        | Generic fallback for uncategorized errors.                  |\n\n---\n\n## Debug Logging\n\nThe SDK ships with a built-in debug logger that is completely silent by default. Enable it to see every outgoing request, response status, and module-level operation in your console.\n\n**In Node.js:**\n\n```bash\nODYSSEY_DEBUG=true node your-script.js\n```\n\n**In the browser:**\n\n```javascript\nwindow.ODYSSEY_DEBUG = true;\n```\n\nAll log lines are prefixed with a timestamp and category:\n\n```\n[14:23:01.452][SDK:HTTP] → POST /sketchfab/object\n[14:23:01.891][SDK:HTTP] ← 200 /sketchfab/object\n[14:23:01.892][SDK:Sketchfab] addObject { orgId: '...', spaceItemId: '...' }\n```\n\n---\n\n## API Reference\n\n### Media\n\n#### `sdk.media.addVideo(data)`\n\nAdd a Vimeo video screen to a space.\n\n```typescript\nawait sdk.media.addVideo({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Video\",\n  type: \"SpatialMedia\",\n  mediaType: \"video\",\n  url: \"https://vimeo.com/...\",\n  attenuation: 1000,\n  volume: 100,\n  loop: true,\n  audioOnly: false,\n  showPreview: false,\n  position: { x: 0, y: 1, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.media.updateVideo(spaceItemId, data)`\n\nUpdate properties of an existing video screen.\n\n```typescript\nawait sdk.media.updateVideo(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  url: \"https://vimeo.com/new-video\",\n  volume: 80,\n  loop: false,\n});\n```\n\n| Field         | Type      | Required | Description                   |\n| ------------- | --------- | -------- | ----------------------------- |\n| `orgId`       | `string`  | Yes      | Organization ID.              |\n| `spaceId`     | `string`  | Yes      | Space ID.                     |\n| `spaceItemId` | `string`  | Yes      | Space item ID.                |\n| `url`         | `string`  | No       | New Vimeo URL.                |\n| `volume`      | `number`  | No       | Volume level (0–100).         |\n| `attenuation` | `number`  | No       | Audio attenuation distance.   |\n| `loop`        | `boolean` | No       | Whether to loop the video.    |\n| `audioOnly`   | `boolean` | No       | Play audio only, no visuals.  |\n| `showPreview` | `boolean` | No       | Show video preview thumbnail. |\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.media.addLink(data)`\n\nAdd a website link screen to a space.\n\n```typescript\nawait sdk.media.addLink({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Link\",\n  type: \"SpatialMedia\",\n  mediaType: \"link\",\n  url: \"https://example.com\",\n  attenuation: 1000,\n  isPortrait: false,\n  openInNewTab: true,\n  position: { x: 0, y: 1, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.media.updateLink(spaceItemId, data)`\n\nUpdate properties of an existing link screen.\n\n```typescript\nawait sdk.media.updateLink(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  url: \"https://new-url.com\",\n  isPortrait: true,\n});\n```\n\n| Field          | Type      | Required | Description                     |\n| -------------- | --------- | -------- | ------------------------------- |\n| `orgId`        | `string`  | Yes      | Organization ID.                |\n| `spaceId`      | `string`  | Yes      | Space ID.                       |\n| `spaceItemId`  | `string`  | Yes      | Space item ID.                  |\n| `url`          | `string`  | No       | New website URL.                |\n| `name`         | `string`  | No       | Display name.                   |\n| `openInNewTab` | `boolean` | No       | Open link in a new tab.         |\n| `isPortrait`   | `boolean` | No       | Render screen in portrait mode. |\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.media.addStream(data)`\n\nAdd a live stream screen to a space.\n\n```typescript\nawait sdk.media.addStream({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Stream\",\n  type: \"RuntimeStream\",\n  spaceStreamId: \"stream-id\",\n  scale: { x: 1, y: 1, z: 1 },\n  offsetUniformScale: 1,\n  attenuation: 1000,\n  volume: 100,\n  audioOnly: false,\n  position: { x: 0, y: 1, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.media.updateStream(spaceItemId, data)`\n\nUpdate properties of an existing stream screen.\n\n```typescript\nawait sdk.media.updateStream(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  volume: 50,\n  aspectRatio: \"16:9\",\n  attenuation: 2000,\n});\n```\n\n| Field         | Type              | Required | Description                             |\n| ------------- | ----------------- | -------- | --------------------------------------- |\n| `orgId`       | `string`          | Yes      | Organization ID.                        |\n| `spaceId`     | `string`          | Yes      | Space ID.                               |\n| `spaceItemId` | `string`          | Yes      | Space item ID.                          |\n| `audioOnly`   | `boolean`         | No       | Play audio only.                        |\n| `curvature`   | `number`          | No       | Screen curvature (−100 to 100).         |\n| `aspectRatio` | `\"4:3\" \\| \"16:9\"` | No       | Screen aspect ratio.                    |\n| `attenuation` | `number`          | No       | Audio attenuation distance (200–10000). |\n| `volume`      | `number`          | No       | Volume level (0–100).                   |\n\n**Returns:** `Promise<void>`\n\n---\n\n### Sketchfab\n\n#### `sdk.sketchfab.addObject(data)`\n\nAdd a 3D model to a space. Supports Sketchfab browse, file upload, and recent model reuse — all produce the same payload shape.\n\n```typescript\nawait sdk.sketchfab.addObject({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Model\",\n  type: \"RuntimeModel\",\n  gltfUrl: \"https://...\",\n  materialOverrideId: \"None\",\n  scale: { x: 1, y: 1, z: 1 },\n  autoScale: true,\n  offsetUniformScale: 1,\n  collisionsEnabled: false,\n  position: { x: 0, y: 0, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n  metadata: {\n    uid: \"sketchfab-uid\",\n    author: { username: \"artist\", profileUrl: \"https://sketchfab.com/artist\" },\n  },\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.sketchfab.updateObject(spaceItemId, data)`\n\nUpdate collision and animation properties of an existing 3D model.\n\n```typescript\nawait sdk.sketchfab.updateObject(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  collisionsEnabled: true,\n  currentAnimation: \"Walk\",\n});\n```\n\n| Field               | Type      | Required | Description                    |\n| ------------------- | --------- | -------- | ------------------------------ |\n| `orgId`             | `string`  | Yes      | Organization ID.               |\n| `spaceId`           | `string`  | Yes      | Space ID.                      |\n| `spaceItemId`       | `string`  | Yes      | Space item ID.                 |\n| `collisionsEnabled` | `boolean` | No       | Enable or disable collisions.  |\n| `currentAnimation`  | `string`  | No       | Name of the animation to play. |\n\n**Returns:** `Promise<void>`\n\n---\n\n### Transform\n\n#### `sdk.transform.updatePosition(spaceItemId, data)`\n\nUpdate the world position of a space item.\n\n```typescript\nawait sdk.transform.updatePosition(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  position: { x: 3, y: 0, z: 0 },\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.transform.updateRotation(spaceItemId, data)`\n\nUpdate the rotation of a space item.\n\n```typescript\nawait sdk.transform.updateRotation(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  rotation: { x: 0, y: 90, z: 0 },\n  offsetUpRotation: 45,\n});\n```\n\n| Field              | Type      | Required | Description                                |\n| ------------------ | --------- | -------- | ------------------------------------------ |\n| `orgId`            | `string`  | Yes      | Organization ID.                           |\n| `spaceId`          | `string`  | Yes      | Space ID.                                  |\n| `spaceItemId`      | `string`  | Yes      | Space item ID.                             |\n| `rotation`         | `Vector3` | Yes      | Rotation in degrees per axis.              |\n| `offsetUpRotation` | `number`  | No       | Additional up-axis rotation (−180 to 180). |\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.transform.updateScale(spaceItemId, data)`\n\nUpdate the scale of a space item.\n\n```typescript\nawait sdk.transform.updateScale(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  scale: { x: 2, y: 2, z: 2 },\n  offsetUniformScale: 2,\n});\n```\n\n| Field                | Type      | Required | Description                          |\n| -------------------- | --------- | -------- | ------------------------------------ |\n| `orgId`              | `string`  | Yes      | Organization ID.                     |\n| `spaceId`            | `string`  | Yes      | Space ID.                            |\n| `spaceItemId`        | `string`  | Yes      | Space item ID.                       |\n| `scale`              | `Vector3` | No       | Per-axis scale values.               |\n| `offsetUniformScale` | `number`  | Yes      | Uniform scale multiplier (min 0.01). |\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.transform.snapToGround(spaceItemId, data)`\n\nSnap a space item to the ground. The UE bridge calculates the correct Z position.\n\n```typescript\nawait sdk.transform.snapToGround(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n### Layer\n\n#### `sdk.layer.lockItem(spaceItemId, data)`\n\nLock or unlock a layer item.\n\n```typescript\n// Lock\nawait sdk.layer.lockItem(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  isLocked: true,\n});\n\n// Unlock\nawait sdk.layer.lockItem(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  isLocked: false,\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.layer.renameItem(spaceItemId, data)`\n\nRename a layer item.\n\n```typescript\nawait sdk.layer.renameItem(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"New Name\",\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.layer.deleteItem(spaceItemId, data)`\n\nDelete a single layer item from a space.\n\n```typescript\nawait sdk.layer.deleteItem(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.layer.deleteAll(data)`\n\nDelete all layer items in a space. Locked items are skipped by the organization service.\n\n```typescript\nawait sdk.layer.deleteAll({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n### Configurator Items\n\n#### `sdk.configuratorItems.create(data)`\n\nCreate a configurator item. Typically synced from Unreal Engine on session start via the WebRTC bridge.\n\n```typescript\nawait sdk.configuratorItems.create({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  itemTemplateId: \"template-id\",\n  name: \"Wall Color\",\n  denormalizeOnUpdate: true,\n  currentState: \"white\",\n  schema: {\n    displayName: \"Wall Color\",\n    type: \"enum\",\n    default: \"white\",\n    values: [\"white\", \"grey\", \"black\"],\n  },\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.configuratorItems.updateState(spaceItemId, data)`\n\nUpdate the current state of a level-wide configurator property.\n\n```typescript\nawait sdk.configuratorItems.updateState(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  currentState: \"grey\",\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n### Configurator Objects\n\n#### `sdk.configuratorObjects.create(data)`\n\nPlace a UE actor in a space. Called when a user selects a card in the UE Object Importer grid.\n\n```typescript\nawait sdk.configuratorObjects.create({\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  name: \"My Chair\",\n  type: \"ConfiguratorObject\",\n  ueId: \"ue-actor-id\",\n  versionId: \"version-id\",\n  scale: { x: 1, y: 1, z: 1 },\n  offsetUniformScale: 1,\n  position: { x: 0, y: 0, z: 0 },\n  rotation: { x: 0, y: 0, z: 0 },\n  offsetUpRotation: 0,\n  configuration: {\n    color: {\n      displayName: \"Color\",\n      type: \"enum\",\n      currentState: \"red\",\n      default: \"red\",\n      values: [\"red\", \"blue\", \"green\"],\n      displayType: \"dropdown\",\n    },\n  },\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `sdk.configuratorObjects.updateConfiguration(spaceItemId, data)`\n\nUpdate the full configuration map of a placed UE actor. Updates the entire map with new `currentState` per key.\n\n```typescript\nawait sdk.configuratorObjects.updateConfiguration(\"item-id\", {\n  orgId: \"org-id\",\n  spaceId: \"space-id\",\n  spaceItemId: \"item-id\",\n  configuration: {\n    color: {\n      displayName: \"Color\",\n      type: \"enum\",\n      currentState: \"blue\",\n      default: \"red\",\n      values: [\"red\", \"blue\", \"green\"],\n      displayType: \"dropdown\",\n    },\n  },\n});\n```\n\n**Returns:** `Promise<void>`\n\n---\n\n## Types Reference\n\nAll types are exported directly from the package root:\n\n```typescript\nimport type { ... } from \"@alliumcloud/odyssey-configurator-sdk\";\n```\n\n---\n\n### Config\n\n```typescript\ntype ConfiguratorServiceSDKConfig = {\n  apiKey: string;\n  jwksUri?: string;\n  accessTokenProvider: () => string | Promise<string>;\n  timeout?: number;\n};\n```\n\n---\n\n### Common\n\n```typescript\ntype SpaceItemType =\n  | \"RuntimeModel\"\n  | \"SpatialMedia\"\n  | \"LibraryModel\"\n  | \"RuntimeStream\"\n  | \"BridgeToolkitSettings\"\n  | \"Configurator\"\n  | \"ConfiguratorObject\";\n\ntype ConfiguratorType =\n  | \"string\"\n  | \"number\"\n  | \"boolean\"\n  | \"enum\"\n  | \"trigger\"\n  | \"image\"\n  | \"String\"\n  | \"Number\"\n  | \"Bool\"\n  | \"Enum\"\n  | \"Trigger\"\n  | \"Image\";\n\ntype Vector3 = {\n  x: number;\n  y: number;\n  z: number;\n};\n\ntype SpaceContext = {\n  orgId: string;\n  spaceId: string;\n  type: SpaceItemType;\n  name: string;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  thumb?: string;\n  isLocked?: boolean;\n  user?: string;\n};\n```\n\n---\n\n### Media Types\n\n```typescript\ntype SpatialMediaTypes = \"video\" | \"link\";\n\ntype CreateVideoInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n  type: \"SpatialMedia\";\n  mediaType: \"video\";\n  url: string;\n  attenuation: number;\n  volume: number;\n  loop: boolean;\n  audioOnly: boolean;\n  showPreview: boolean;\n  autoplay?: boolean;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  thumb?: string;\n  isLocked?: boolean;\n  user?: string;\n};\n\ntype UpdateVideoInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  url?: string;\n  volume?: number;\n  attenuation?: number;\n  loop?: boolean;\n  audioOnly?: boolean;\n  showPreview?: boolean;\n};\n\ntype CreateLinkInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n  type: \"SpatialMedia\";\n  mediaType: \"link\";\n  url: string;\n  attenuation: number;\n  isPortrait: boolean;\n  openInNewTab?: boolean;\n  displayName?: string;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  thumb?: string;\n  isLocked?: boolean;\n  user?: string;\n};\n\ntype UpdateLinkInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  url?: string;\n  name?: string;\n  openInNewTab?: boolean;\n  isPortrait?: boolean;\n};\n\ntype CreateStreamInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n  type: \"RuntimeStream\";\n  spaceStreamId: string;\n  scale?: Vector3;\n  autoScale?: boolean;\n  offsetUniformScale?: number;\n  attenuation?: number;\n  volume?: number;\n  audioOnly?: boolean;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  thumb?: string;\n  isLocked?: boolean;\n  user?: string;\n};\n\ntype UpdateStreamInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  audioOnly?: boolean;\n  curvature?: number;\n  aspectRatio?: \"4:3\" | \"16:9\";\n  attenuation?: number;\n  volume?: number;\n};\n```\n\n---\n\n### Sketchfab Types\n\n```typescript\ntype ModelAuthor = {\n  username: string;\n  profileUrl: string;\n};\n\ntype ModelGlbInfo = {\n  faceCount?: number;\n  textureCount?: number;\n  size?: number;\n  vertexCount?: number;\n  textureMaxResolution?: number;\n};\n\ntype ModelMetadata = {\n  uid?: string;\n  viewerUrl?: string;\n  author?: ModelAuthor;\n  license?: string;\n  glb?: ModelGlbInfo;\n};\n\ntype CreateSketchfabObjectInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n  type: \"RuntimeModel\";\n  gltfUrl?: string;\n  materialOverrideId: string;\n  scale: Vector3;\n  autoScale: boolean;\n  offsetUniformScale: number;\n  collisionsEnabled: boolean;\n  currentAnimation?: string;\n  availableAnimations?: string[];\n  metadata?: ModelMetadata;\n  thumb?: string;\n  sketchfabTempUrl?: string;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  isLocked?: boolean;\n  user?: string;\n};\n\ntype UpdateSketchfabObjectInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  collisionsEnabled?: boolean;\n  currentAnimation?: string;\n};\n```\n\n---\n\n### Transform Types\n\n```typescript\ntype TransformItemContext = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n};\n\ntype UpdatePositionInput = TransformItemContext & {\n  position: Vector3;\n};\n\ntype UpdateRotationInput = TransformItemContext & {\n  rotation: Vector3;\n  offsetUpRotation?: number;\n};\n\ntype UpdateScaleInput = TransformItemContext & {\n  scale?: Vector3;\n  offsetUniformScale: number;\n};\n\ntype SnapToGroundInput = TransformItemContext;\n```\n\n---\n\n### Layer Types\n\n```typescript\ntype LockLayerItemInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  isLocked: boolean;\n};\n\ntype RenameLayerItemInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n};\n\ntype DeleteLayerItemInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n};\n\ntype DeleteAllLayerItemsInput = {\n  orgId: string;\n  spaceId: string;\n};\n```\n\n---\n\n### Configurator Item Types\n\n```typescript\ntype ConfiguratorItemSchema = {\n  displayName: string;\n  type: ConfiguratorType;\n  default: string | number | boolean;\n  values?: (string | number)[];\n  max?: number;\n  min?: number;\n  step?: number;\n  isPersisted?: boolean;\n  isNetworked?: boolean;\n};\n\ntype CreateConfiguratorItemInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  itemTemplateId: string;\n  name: string;\n  levelFilePath?: string;\n  denormalizeOnUpdate: boolean;\n  currentState?: string | number | boolean;\n  schema: ConfiguratorItemSchema;\n};\n\ntype UpdateConfiguratorItemStateInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  currentState: string | number | boolean;\n};\n```\n\n---\n\n### Configurator Object Types\n\n```typescript\ntype ObjectConfigurationValue = {\n  displayName: string;\n  type: ConfiguratorType;\n  currentState: string | number | boolean;\n  default: string | number | boolean;\n  max?: number;\n  min?: number;\n  step?: number;\n  values: (string | number)[];\n  displayType?: \"image\" | \"dropdown\";\n  valuesImages?: { displayName: string; thumb?: string }[];\n};\n\ntype EnumObjectConfigurationValue = {\n  displayName: string;\n  type: \"enum\";\n  currentState: string | number;\n  default: string | number;\n  values: (string | number)[];\n  displayType: \"image\" | \"dropdown\";\n  max?: number;\n  min?: number;\n  step?: number;\n  valuesImages?: { displayName: string; thumb?: string }[];\n};\n\ntype ConfigurationMap = {\n  [key: string]: ObjectConfigurationValue | EnumObjectConfigurationValue;\n};\n\ntype CreateConfiguratorObjectInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  name: string;\n  type: \"ConfiguratorObject\";\n  ueId: string;\n  versionId: string;\n  scale: Vector3;\n  offsetUniformScale: number;\n  configuration: ConfigurationMap;\n  position: Vector3;\n  rotation: Vector3;\n  offsetUpRotation: number;\n  thumb?: string;\n  isLocked?: boolean;\n  user?: string;\n};\n\ntype UpdateConfiguratorObjectConfigInput = {\n  orgId: string;\n  spaceId: string;\n  spaceItemId: string;\n  configuration: ConfigurationMap;\n};\n```\n\n---\n\n### Error Types\n\n```typescript\ninterface OdysseyError extends Error {\n  readonly code: string; // Machine-readable error code\n  readonly name: string; // Human-readable error name\n  readonly message: string; // Description of what went wrong\n}\n\n// Type guard — use in catch blocks to narrow the error type\nfunction isOdysseyError(err: unknown): err is OdysseyError;\n```\n\n---\n\n## Versioning\n\nThis SDK follows semantic versioning:\n\n| Bump              | When                                                     |\n| ----------------- | -------------------------------------------------------- |\n| `patch` — `x.x.1` | Bug fixes, no API changes.                               |\n| `minor` — `x.1.0` | New methods or fields added, fully backwards compatible. |\n| `major` — `2.0.0` | Breaking changes to existing method signatures or types. |\n\n---\n\n## License\n\nPrivate — Odyssey Internal Use Only\n","readmeFilename":"README.md"}