{"_id":"@alterno-dev/spatial-review","_rev":"7-ae9d45c724ae443de6994365d1296193","name":"@alterno-dev/spatial-review","dist-tags":{"latest":"0.7.0"},"versions":{"0.1.0":{"name":"@alterno-dev/spatial-review","version":"0.1.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.1.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"6f5181b09790f396d7d7cc19efcd707fcbf0db16","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.1.0.tgz","fileCount":26,"integrity":"sha512-l+UzsHYf54SVCJ1/oUNV2ucp/KDPKkNrEVdy0TyicBxeXbmUHx8Wk1qKwG4ru7RiOsz3ya7k8KGUw+gIYk7/kA==","signatures":[{"sig":"MEYCIQDrC3USYtWXN6zRbsZT7J5iH2aw/d1XzbnVOKSaeJaWuQIhAIvw2qJjeovDe1aHSVP6VZAAGcMx0RRSHly5gJIDWQ5S","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50985},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"f4e53744d7799548cfc72ba60dc7847016d3eb4e","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"23.5.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.180.0","@types/three":"^0.180.0"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.1.0_1787388522147_0.5081958509161875","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alterno-dev/spatial-review","version":"0.2.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.2.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"3a48f0cfcaaf6ab831cae92b56967a8cd425f511","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.2.0.tgz","fileCount":34,"integrity":"sha512-nw9KTiVVl6pHDwtPgmgGDBxs1s8kJw0Ps6H3aBae0HjGXwvPnJ1MOBJ7yUna0gYrBlc5tGm2A8VV7b/e3oX8Sg==","signatures":[{"sig":"MEUCIQCWiq3ml8XK8ip4YIbGdrGSq4NVeskDaJyX4RUOdrGrsgIgJ9iyR9YYnl2ALQQ3/RK2qpF++WjI9dP4GTs7cmBKP4s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":74846},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"fe504bbddc7638b0add99e7e0b0ebd4d3ce9a553","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"24.19.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.180.0"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.2.0_1787472281889_0.71525103199755","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@alterno-dev/spatial-review","version":"0.3.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.3.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"42bab640a22cf9afa7c53ff60b5687fc844935fb","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.3.0.tgz","fileCount":34,"integrity":"sha512-7zozuXWQ9Reph1F1oQDHT5Rrge6RoLHUSq6lSfy3npURtc5f0dZXiRaob1xkvEZgHUrXRCyj+VVitOViBWyXEQ==","signatures":[{"sig":"MEYCIQCEkGIVIdbnIeKwwUIVuzitgZnFwAWv23k94rQVMWkvpQIhAP1DinSiTMRAO2MEKJ7hWb/qTcUuqWZVQ3jAFOC1Jg8A","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":77235},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"6f0275e991bee8e448c126ab225c7304b2257913","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"23.5.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.180.0"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.3.0_1787481289094_0.7899037699209275","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@alterno-dev/spatial-review","version":"0.4.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.4.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"b48b36febdf251b712f7294249a035abbf9a6541","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.4.0.tgz","fileCount":46,"integrity":"sha512-754lv8UK5M5gXT0V448PZ2/F4DhTV+29WWpGKy/eKGsxj4jxmsywhlSeDvMzs7OWnyp+NebDXBiwelbhq3MzVg==","signatures":[{"sig":"MEUCIQDrhIjp/B6X718ATjFeImTJvhbkgWrIdSPJ7+P1Ft8A7AIgaJeLi0Gx/QdZ0f7NjwFbQzmzKds+Nx2AEewu3HNOZds=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":139348},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"bb7bfac609b1ef6eba68b26141b9582a04f4b326","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com","approver":{"name":"oltremago","email":"oltremago@gmail.com"}},"_npmVersion":"12.0.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"23.5.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.180.0"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.4.0_1787938904160_0.2731244254865075","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@alterno-dev/spatial-review","version":"0.5.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.5.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"25867c92ef560f0023919e32fbc5f0ce97947004","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.5.0.tgz","fileCount":50,"integrity":"sha512-Je8BxyksXU34wW3cZ9xQ8uOkaK4gjcvKg+q09vrI/E/x0PZFxqiODoUzUjRtFtqKNcWg4e2EAfPClKiXuw+cAA==","signatures":[{"sig":"MEQCIEmRmwtfMENwSyQp+NWbKUouUXyXLIxZE8HguWQXwR7aAiBQzOm/4q29SWQ3iucqx3MIDmTFs8HuGztwaSwwmCeXCw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":162270},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"d78231744205f0df48c7404b85b7b464a4aee70f","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"23.5.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.5.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.185.4"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.5.0_1788039837466_0.052290173846688326","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@alterno-dev/spatial-review","version":"0.6.0","license":"MIT","_id":"@alterno-dev/spatial-review@0.6.0","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"dist":{"shasum":"1b0a71a9f3147992bb4933da1aac93041ab974d6","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.6.0.tgz","fileCount":50,"integrity":"sha512-aWslWCgB5qncJWQSBIIH/M+yEt2fhJGrGK6cvCQp7BGvvLLZEf3zVdE7LYhvrC5RpBmqGnx59hYgQihY7zJm7A==","signatures":[{"sig":"MEUCIQDlXXp24bnWcygFQT9mb9k+B3NR5wa6UmY0ebvj8i1gJwIgBT22s3dvr/Igp80UjlCOPZcwymlapis/Gd+mm9MmFV8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":299132},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"0c5d127a832516b0e1c818f9e76106d1f32ea3d3","scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@alterno-dev/spatial-review-protocol":"0.6.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.185.4"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"tmp":"tmp/spatial-review_0.6.0_1788168310803_0.17689125126567595","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"_id":"@alterno-dev/spatial-review@0.7.0","dist":{"shasum":"3cd96e6bc4b292ea874fe2828f2a6a4a4dde59ec","tarball":"https://registry.npmjs.org/@alterno-dev/spatial-review/-/spatial-review-0.7.0.tgz","fileCount":54,"integrity":"sha512-tXHBn3XU8fMXhgCC87CwcMAbfG764qSJKSYTQaSigkjhOq4hhfgHVsDd7wFibS3M32tj//nnNoh+Gp5EyuKUqQ==","signatures":[{"sig":"MEUCIATZrl4IU0sIQtviHZstWeAFP+qZ+4qcbz7PbQ2swWSCAiEAoQ+BpG7O8bSQUqnC4m/fAMddn8wT2HasDOb0+Uu9fIg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDbmGbFjvDvtDXuRtPR/a+nX9Hpa0aFOmdlERziC20jngIhAJnR9JydSAqR0LvcFQnBn9cydfCIpyDETVRaqC376PQZ"}],"unpackedSize":324973},"main":"./dist/index.js","name":"@alterno-dev/spatial-review","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"e41ac9603d40b995f1b3ea35f69ba26e07d55303","license":"MIT","scripts":{"build":"tsc -p tsconfig.json"},"version":"0.7.0","_npmUser":{"name":"oltremago","email":"oltremago@gmail.com"},"_npmVersion":"10.9.2","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","directories":{},"maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"_nodeVersion":"24.19.0","dependencies":{"@alterno-dev/spatial-review-protocol":"0.7.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"three":"^0.185.1","@types/three":"^0.185.4"},"peerDependencies":{"three":">=0.160.0 <1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/spatial-review_0.7.0_1788459791880_0.7233288530785682"}}},"time":{"created":"2026-08-22T08:48:42.013Z","modified":"2026-09-03T18:23:12.144Z","0.1.0":"2026-08-22T08:48:42.283Z","0.2.0":"2026-08-23T08:04:42.060Z","0.3.0":"2026-08-23T10:34:49.230Z","0.4.0":"2026-08-28T17:41:44.255Z","0.5.0":"2026-08-29T21:43:57.616Z","0.6.0":"2026-08-31T09:25:10.944Z","0.7.0":"2026-09-03T18:23:11.976Z"},"license":"MIT","description":"Website SDK and Three.js adapter for Alterno Spatial Review.","maintainers":[{"name":"oltremago","email":"oltremago@gmail.com"}],"readme":"# `@alterno-dev/spatial-review`\n\nRegister semantic Three.js roots and expose them to compatible review tools.\nThe editor receives only explicitly registered objects.\n\n## Scene ownership\n\nUse `registerAssembly()` for explicit transform-only place/room owners and\n`parentAssemblyId` on actor registrations for their contents. Assemblies read an\nexisting root's pose or accept a `localTransform` snapshot; they never register\nits geometry. `toScene()` exports hierarchy and world-space compatibility data;\n`toScene(false)` provides a flattened fallback. The bridge negotiates\n`scene-assemblies-v1` explicitly and preserves the old flat producer path.\nRead the [complete example and migration rules](../../docs/ownership-first-scene.md)\nbefore adopting this capability. Apply the registration-owner rule in\n[Choose actor boundaries](../../agents/structuring-for-review.md#choose-actor-boundaries).\n\n## Official editor authorization\n\nUse [Obtain permission](../../agents/install.md#1-obtain-permission) for the\nauthorization decision. This section describes the package behavior.\n\nInstalling this package alone does not expose page data or start a bridge.\n`attachSpatialReviewDiscoveryBridge()` starts the discovery bridge.\n`attachSceneAssetRegistryBridge()` starts the capture bridge. In this document,\nbrowser bridge means either interface.\nBy default, both functions trust the exact official Alterno editor origin:\n\n```text\nhttps://spatial-review.alterno.dev\n```\n\nThrough the discovery bridge, that origin may request discovery metadata.\nThrough the capture bridge, it may request registered roots and their supported\ndescendants. Capture data can include scene and asset structures, descendant\ngeometry, materials, textures, and registered texture bytes. Neither bridge\nexposes arbitrary DOM, cookies, storage, unrelated application state, or\nobjects outside registered roots. The serializer can copy registered texture\n`sourceRef`, `requestUrl`, `currentSrc`, and `src` strings. The integration must\nremove credentials and secrets from those strings before bridge attachment.\n\n`allowOfficialEditor: true` enables the official editor origin.\n`allowOfficialEditor: false` disables it. `allowedOrigins` adds exact\nself-hosted editor origins. `allowLoopbackPeers: true` explicitly enables\ncross-origin loopback development; it defaults to `false`.\n\nBoth bridges always accept the producer's own origin. A different loopback\norigin is a separate security principal and is accepted only with the explicit\nloopback opt-in. Earlier SDK versions allowed cross-origin loopback implicitly;\nexisting local integrations must set `allowLoopbackPeers: true` to retain that\nbehavior after upgrading.\n\n```ts\nimport { createSpatialReviewEditorAuthorization } from \"@alterno-dev/spatial-review\";\n\nconst authorization = createSpatialReviewEditorAuthorization({\n  allowOfficialEditor: true,\n  allowedOrigins: [],\n  allowLoopbackPeers: false,\n  advertiseEditorOriginPolicy: {\n    publicOrigins: [\"https://spatial-review.alterno.dev\"],\n  },\n});\n\nattachSpatialReviewDiscoveryBridge(registration, authorization);\nattachSceneAssetRegistryBridge(registry, authorization);\n```\n\n`spatialReviewEditorUrl(websiteUrl)` creates a hosted-editor deep link that\nconnects to the supplied website. It does not bypass the website's bridge origin\nchecks.\n\n`allowedOrigins` accepts only exact canonical origins: no credentials, paths,\nqueries, fragments, wildcards, default-port aliases, or insecure non-loopback\nHTTP. Invalid input fails before a bridge listener is attached.\n\nFor browser discovery, runtime origins remain private by default. The SDK\nderives `capabilities.liveCapture.editorOriginPolicy` only from a frozen shared\nauthorization created with an explicit\n`advertiseEditorOriginPolicy.publicOrigins` disclosure. That public list must\nexactly match every finite non-same-origin runtime origin, including the\nofficial editor when enabled. Reuse the same authorization with both bridges.\nRaw option objects still configure runtime access but never advertise a policy,\nand cannot accompany an explicit policy. Dynamic `allowOrigin` authorization\nalways remains unspecified. Discovery metadata never authorizes a request. A\nrecognized unauthorized catalog handshake with a valid request ID receives a\ncorrelated, exact-origin\n`spatial-review:connection-rejected` response and no scene data. See the\n[complete authorization contract](../../docs/editor-origin-authorization.md).\n\nWhen an editor embeds a website page, that page must permit framing by the\neditor. An editor that opens the page as a popup can use the opener bridge and\ndoes not require a framing exception. The SDK does not modify Content Security\nPolicy or `X-Frame-Options` headers.\n\n`attachSpatialReviewDiscoveryBridge()` lets a client-only editor discover the\nwebsite's live-capture URL through an embedded landing page. A direct CORS fetch\nof `/.well-known/spatial-review.json` is optional rather than required.\n\nFor a project-path or custom static manifest, set `discoveryUrl` on the bridge\nregistration. It must be an HTTP(S), credential-free URL on the website origin;\nrelative values resolve below the normalized website project path. The bridge\nreturns this locator alongside its message, but it never inserts it into the\ndiscovery document itself:\n\n```ts\nattachSpatialReviewDiscoveryBridge({\n  name: \"GitHub Pages project\",\n  websiteUrl: \"https://owner.github.io/project/\",\n  discoveryUrl: \".well-known/spatial-review.json\",\n  liveCapture: \"../?spatial-review-capture=1\",\n});\n```\n\nEditors first try an explicit locator, the canonical origin-root locator, and\nthe project-relative locator in order. They use the discovery bridge only after\nthose static candidates fail. Existing root-only integrations and registrations\nwithout `discoveryUrl` retain their previous behavior.\n\nRegistered texture maps receive session resource IDs. Compatible editors can\nrequest their bytes over the origin-checked capture bridge, so integrated\nwebsites do not need to expose texture CORS headers. Direct URL loading remains\nan optional editor optimization. The editor and website negotiate a per-resource\nbyte limit during the catalog handshake and enforce the lower offer.\n\nA registered `sourceRef` is expected to return a successful response whose\n`Content-Type` starts with `image/`. Check that header on the deployed asset,\nespecially when a CDN or static host serves WebP or other image formats. When a\nsuccessful response instead has a non-image MIME type, the SDK discards those\nresponse bytes and tries the already-decoded registered texture source. Canvas,\nimage, bitmap, video, and supported RGB/RGBA data sources can be encoded as a\nsafe image fallback. If neither path is exportable, the resource response names\nthe MIME mismatch and remediation. Both direct and decoded paths enforce the\nnegotiated byte limit.\n\nAs an installation check, open one representative textured asset in the editor\nand confirm that its live texture reports ready and matches the website. This\ncheck complements inspecting the deployed `Content-Type`; a resource ID alone\ndoes not prove that transferable texture bytes are available.\n\nThe package also exports `buildThreeAsset()`, `makeAssetGeometry()`, and\n`disposeThreeAsset()` for websites that render an engine-neutral\n`ReviewAsset3D` contract back into a Three.js hierarchy.\n\n`buildThreeAsset()` remains synchronous and never fetches texture references.\nUse `buildThreeAssetAsync()` when a trusted integration wants to hydrate the\nprotocol's supported material-map slots:\n\n```ts\nconst textureCache = new Map<string, Promise<THREE.Texture>>();\nconst built = await buildThreeAssetAsync(asset, {\n  resolveTexture(map) {\n    if (!map.sourceRef) throw new Error(\"This integration requires a source URL.\");\n    const url = new URL(map.sourceRef, approvedAssetBaseUrl);\n    if (url.origin !== approvedAssetBaseUrl.origin) throw new Error(\"Cross-origin texture rejected.\");\n    let pending = textureCache.get(url.href);\n    if (!pending) {\n      pending = new THREE.TextureLoader().loadAsync(url.href);\n      textureCache.set(url.href, pending);\n    }\n    return pending;\n  },\n});\n```\n\nThe SDK calls only the supplied resolver; source allowlists, credentials,\nresponse-size limits, decoding, and live `resourceId` lookup remain application\npolicy. Resolver results are caller-owned cache entries. Each material binding\nreceives a Texture clone that shares the decoded source while retaining its own\nwrap, repeat, offset, rotation, `flipY`, and color-space settings. Existing\nhierarchies can use `hydrateThreeAssetTextures(asset, built, resolver)` directly.\n\n## Large scenes and resource ownership\n\nRegister each independently reviewable actor with its own stable `actorId`.\nActors may share an `assetId` when they use the same canonical model. The\ncatalog sends one asset definition and each actor's own source transform and\nworld bounds; sharing the asset does not merge the actors or their feedback.\n\nThe registry caches world transforms, bounds and serialized asset families.\nNormal transform/hierarchy changes, geometry attribute identity/version changes,\ninstance updates and material changes are detected on the next request. A cheap\nhierarchy inspection still runs; unchanged actors do not repeat their bounds\ncalculations. Use the usual `attribute.needsUpdate = true` after editing attribute\ndata. If changing raw buffers without updating their version, explicitly call:\n\n```ts\nregistry.invalidate(\"actor-id\"); // or invalidate() for all registered actors\nregistry.unregister(\"removed-actor-id\");\n```\n\n`registry.cacheMetrics` exposes the latest inspection's matrix, bounds and\ngeometry calculation counts and the number of cached asset variants.\n`toAsset(assetId, profile, compact)` serializes only the requested profile;\n`toReviewIndex(profile, false, true)` produces actors and asset descriptors\nwithout serializing geometry. Existing catalog methods continue to return JSON\nnumber arrays by default.\n\nThe runtime shares geometry and materials between live builds of the same\nimmutable definitions and view mode. Always release a built hierarchy with\n`disposeThreeAsset(root)`. Use `cloneThreeAssetObject(root)` for a retained\npreview clone. Dispose both hierarchies independently. Calling Three's raw\n`geometry.dispose()` or `material.dispose()` on a shared resource bypasses this\nownership contract. A `ThreeAssetResourceCache` can be supplied as the fourth\nargument of `buildThreeAsset` to scope sharing explicitly. Resources are released\nwhen the last owner is disposed, not kept indefinitely.\n\n## Progressive live geometry\n\nCompatible editors negotiate `progressive-assets-v1` and\n`geometry-transfer-v1` in the catalog handshake. They first receive actors,\nbounds and asset descriptors, then request individual scene or review families.\nGeometry attributes use transferable typed arrays; transfer buffers are owned\ncopies, so the source scene and registry's reusable cache remain attached.\nOlder editors continue to receive the complete JSON-compatible catalog.\n\nThe existing origin and window-source checks apply to the new requests. The\nbridge negotiates a geometry byte limit (64 MiB by default), checks family size\nbefore allocating its serialized buffers, bounds pending requests, and cancels\nqueued work on detach. Texture resources keep their separate negotiated limit.\nCompleted deferred geometry uses a 32-entry / 64 MiB LRU. Because texture bytes\nare requested after geometry delivery, generated texture owners receive a\n60-second delivery grace and then use a separate 64-owner / approximately\n256 MiB LRU. Resource eviction invalidates the matching geometry/revision reuse\nso a later request regenerates valid IDs; detaching the final bridge clears all\ndeferred session resources.\nFor direct protocol implementations, see the\n[progressive asset family contract](../protocol/README.md#progressive-asset-families).\n\nThese capabilities require upgrading the SDK used by the integrated website.\nUpgrading an editor alone cannot enable partial catalogs on an older bridge.\nThis work includes a coordinated release Changeset; the development changes do\nnot publish or deploy a release automatically.\n\nFor geometry that should not exist until requested, use\n`registry.registerDeferred()`. Negotiated `asset-stream-v1` catalogs expose its\nworld transform, bounds, and immutable overview/detail revisions before calling\nthe asynchronous producer. The producer receives the lower byte budget, request\npriority, an `AbortSignal`, and progress callback. The bridge enforces bounded\npriority queues, aggregate in-flight bytes, cancellation, and revision-aware\n`notModified` responses. Configured and derived aggregate limits are clamped\nto the protocol ceiling. Streamed instance matrices use owned `Float32Array`\nbuffers, and the byte budget reserves every owned copy when source views alias\none buffer; ordinary JSON exports retain nested number arrays.\n\nCancellation releases the bridge's queue and aggregate-byte reservation\nimmediately and suppresses late producer results. Producers remain responsible\nfor observing their `AbortSignal` and disposing only review-owned temporary\nwork. Live texture IDs are leased to the serialized asset/representation that\nadvertises them; superseded catalog resources are forgotten without disposing\ntextures owned by the website.\n\nRead [Deferred asset streaming](../../docs/deferred-asset-streaming.md) for the\nregistration example, wire order, status lifecycle, cache identity, migration\nfallback, and security limits.\n\n## Register camera journeys\n\nUse [Export navigation sequences](../../agents/exporting-navigation-sequences.md)\nas the source of truth for identity, source mapping, editability, and curve\nselection. The example below shows the SDK field shape.\n\n`registerNavigationSequence()` exposes an engine-neutral, semantically named\ncamera journey alongside registered scene actors. A sequence may use linear,\nquadratic Bézier, cubic Bézier, Catmull–Rom, or read-only sampled curves. It also\ndescribes how the camera aims, how long each segment feels, and when its FOV\ntransition begins.\n\n```ts\nregistry.registerNavigationSequence({\n  id: \"arrival\",\n  name: \"Arrival journey\",\n  sourceRef: \"src/scene/rail.ts#arrivalJourney\",\n  stops: [\n    { id: \"outside\", name: \"Outside\", camera: [0, 1.7, 6], target: [0, 1.5, 0], fov: 50, sourceRef: \"src/scene/rail.ts#outside\" },\n    { id: \"inside\", name: \"Inside\", camera: [4, 1.7, 1], target: [0, 1.5, 0], fov: 44, sourceRef: \"src/scene/rail.ts#inside\" },\n  ],\n  segments: [{\n    id: \"outside--inside\",\n    fromStopId: \"outside\",\n    toStopId: \"inside\",\n    weight: 1.4,\n    lensStart: 0.2,\n    camera: {\n      kind: \"cubic-bezier\",\n      points: [\n        { id: \"outside-camera\", role: \"stop\", stopId: \"outside\", position: [0, 1.7, 6], sourceRef: \"src/scene/rail.ts#outside\" },\n        { id: \"outside-out\", role: \"control-out\", position: [1, 1.7, 6], sourceRef: \"src/scene/rail.ts#outsideOut\" },\n        { id: \"inside-in\", role: \"control-in\", position: [3, 1.7, 2], sourceRef: \"src/scene/rail.ts#insideIn\" },\n        { id: \"inside-camera\", role: \"stop\", stopId: \"inside\", position: [4, 1.7, 1], sourceRef: \"src/scene/rail.ts#inside\" },\n      ],\n    },\n    aim: { kind: \"path-facing\", lookDistance: 6, turnFraction: 0.18 },\n  }],\n});\n```\n","readmeFilename":"README.md"}