@webspatial/core-sdk 1.6.1 → 2.0.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/CHANGELOG.md +84 -0
- package/README.md +10 -0
- package/dist/JSBCommand.d.ts +398 -0
- package/dist/JSBCommand.js +668 -0
- package/dist/JSBCommand.js.map +1 -0
- package/dist/Spatial.d.ts +63 -0
- package/dist/Spatial.js +60 -0
- package/dist/Spatial.js.map +1 -0
- package/dist/SpatialObject.d.ts +19 -0
- package/dist/SpatialObject.js +44 -0
- package/dist/SpatialObject.js.map +1 -0
- package/dist/SpatialScene.d.ts +51 -0
- package/dist/SpatialScene.js +74 -0
- package/dist/SpatialScene.js.map +1 -0
- package/dist/SpatialSession.d.ts +143 -0
- package/dist/SpatialSession.js +173 -0
- package/dist/SpatialSession.js.map +1 -0
- package/dist/SpatialWebEvent.d.ts +8 -0
- package/dist/SpatialWebEvent.js +18 -0
- package/dist/SpatialWebEvent.js.map +1 -0
- package/dist/SpatialWebEventCreator.d.ts +11 -0
- package/dist/SpatialWebEventCreator.js +11 -0
- package/dist/SpatialWebEventCreator.js.map +1 -0
- package/dist/Spatialized2DElement.d.ts +42 -0
- package/dist/Spatialized2DElement.js +47 -0
- package/dist/Spatialized2DElement.js.map +1 -0
- package/dist/SpatializedDynamic3DElement.d.ts +7 -0
- package/dist/SpatializedDynamic3DElement.js +43 -0
- package/dist/SpatializedDynamic3DElement.js.map +1 -0
- package/dist/SpatializedElement.d.ts +7 -0
- package/dist/SpatializedElement.js +206 -0
- package/dist/SpatializedElement.js.map +1 -0
- package/dist/SpatializedElementCreator.d.ts +15 -0
- package/dist/SpatializedElementCreator.js +47 -0
- package/dist/SpatializedElementCreator.js.map +1 -0
- package/dist/SpatializedStatic3DElement.d.ts +195 -0
- package/dist/SpatializedStatic3DElement.js +327 -0
- package/dist/SpatializedStatic3DElement.js.map +1 -0
- package/dist/WebMsgCommand.d.ts +7 -0
- package/dist/WebMsgCommand.js +23 -0
- package/dist/WebMsgCommand.js.map +1 -0
- package/dist/index.d.ts +35 -1101
- package/dist/index.js +21 -3393
- package/dist/index.js.map +1 -1
- package/dist/install-polyfills.d.ts +2 -0
- package/dist/install-polyfills.js +13 -0
- package/dist/install-polyfills.js.map +1 -0
- package/dist/isSSREnv.d.ts +3 -0
- package/dist/isSSREnv.js +6 -0
- package/dist/isSSREnv.js.map +1 -0
- package/dist/motion/AnimationObject.d.ts +7 -0
- package/dist/motion/AnimationObject.js +140 -0
- package/dist/motion/AnimationObject.js.map +1 -0
- package/dist/motion/compute/index.d.ts +10 -0
- package/dist/motion/compute/index.js +19 -0
- package/dist/motion/compute/index.js.map +1 -0
- package/dist/motion/compute/motionPropertyValues.d.ts +19 -0
- package/dist/motion/compute/motionPropertyValues.js +50 -0
- package/dist/motion/compute/motionPropertyValues.js.map +1 -0
- package/dist/motion/compute/normalize.d.ts +31 -0
- package/dist/motion/compute/normalize.js +201 -0
- package/dist/motion/compute/normalize.js.map +1 -0
- package/dist/motion/compute/validate.d.ts +22 -0
- package/dist/motion/compute/validate.js +103 -0
- package/dist/motion/compute/validate.js.map +1 -0
- package/dist/motion/index.d.ts +8 -0
- package/dist/motion/index.js +6 -0
- package/dist/motion/index.js.map +1 -0
- package/dist/motion/native/serializeMotionTimeline.d.ts +12 -0
- package/dist/motion/native/serializeMotionTimeline.js +21 -0
- package/dist/motion/native/serializeMotionTimeline.js.map +1 -0
- package/dist/physicalMetrics-W5uZ5xKb.d.ts +48 -0
- package/dist/physicalMetrics.d.ts +1 -0
- package/dist/physicalMetrics.js +58 -0
- package/dist/physicalMetrics.js.map +1 -0
- package/dist/platform-adapter/CommandResultUtils.d.ts +12 -0
- package/dist/platform-adapter/CommandResultUtils.js +21 -0
- package/dist/platform-adapter/CommandResultUtils.js.map +1 -0
- package/dist/platform-adapter/createPlatformSync.d.ts +16 -0
- package/dist/platform-adapter/createPlatformSync.js +30 -0
- package/dist/platform-adapter/createPlatformSync.js.map +1 -0
- package/dist/platform-adapter/index.d.ts +12 -0
- package/dist/platform-adapter/index.js +10 -0
- package/dist/platform-adapter/index.js.map +1 -0
- package/dist/platform-adapter/interface.d.ts +7 -0
- package/dist/platform-adapter/interface.js +1 -0
- package/dist/platform-adapter/interface.js.map +1 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.d.ts +25 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js +168 -0
- package/dist/platform-adapter/pico-os/PicoOSPlatform.js.map +1 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.d.ts +66 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js +372 -0
- package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js.map +1 -0
- package/dist/platform-adapter/spatialRequestMetadata.d.ts +6 -0
- package/dist/platform-adapter/spatialRequestMetadata.js +39 -0
- package/dist/platform-adapter/spatialRequestMetadata.js.map +1 -0
- package/dist/platform-adapter/spatialSceneQuery.d.ts +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js +12 -0
- package/dist/platform-adapter/spatialSceneQuery.js.map +1 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.d.ts +19 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js +77 -0
- package/dist/platform-adapter/vision-os/VisionOSPlatform.js.map +1 -0
- package/dist/platform-runtime.d.ts +12 -0
- package/dist/platform-runtime.js +14 -0
- package/dist/platform-runtime.js.map +1 -0
- package/dist/reality/Attachment.d.ts +19 -0
- package/dist/reality/Attachment.js +39 -0
- package/dist/reality/Attachment.js.map +1 -0
- package/dist/reality/component/ModelComponent.d.ts +14 -0
- package/dist/reality/component/ModelComponent.js +11 -0
- package/dist/reality/component/ModelComponent.js.map +1 -0
- package/dist/reality/component/SpatialComponent.d.ts +8 -0
- package/dist/reality/component/SpatialComponent.js +19 -0
- package/dist/reality/component/SpatialComponent.js.map +1 -0
- package/dist/reality/component/index.d.ts +8 -0
- package/dist/reality/component/index.js +3 -0
- package/dist/reality/component/index.js.map +1 -0
- package/dist/reality/entity/SpatialEntity.d.ts +7 -0
- package/dist/reality/entity/SpatialEntity.js +291 -0
- package/dist/reality/entity/SpatialEntity.js.map +1 -0
- package/dist/reality/entity/SpatialModelEntity.d.ts +17 -0
- package/dist/reality/entity/SpatialModelEntity.js +17 -0
- package/dist/reality/entity/SpatialModelEntity.js.map +1 -0
- package/dist/reality/entity/index.d.ts +8 -0
- package/dist/reality/entity/index.js +3 -0
- package/dist/reality/entity/index.js.map +1 -0
- package/dist/reality/geometry/SpatialBoxGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js +13 -0
- package/dist/reality/geometry/SpatialBoxGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialConeGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialConeGeometry.js +13 -0
- package/dist/reality/geometry/SpatialConeGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js +13 -0
- package/dist/reality/geometry/SpatialCylinderGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialGeometry.d.ts +7 -0
- package/dist/reality/geometry/SpatialGeometry.js +13 -0
- package/dist/reality/geometry/SpatialGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js +13 -0
- package/dist/reality/geometry/SpatialPlaneGeometry.js.map +1 -0
- package/dist/reality/geometry/SpatialSphereGeometry.d.ts +16 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js +13 -0
- package/dist/reality/geometry/SpatialSphereGeometry.js.map +1 -0
- package/dist/reality/geometry/index.d.ts +12 -0
- package/dist/reality/geometry/index.js +7 -0
- package/dist/reality/geometry/index.js.map +1 -0
- package/dist/reality/index.d.ts +18 -0
- package/dist/reality/index.js +7 -0
- package/dist/reality/index.js.map +1 -0
- package/dist/reality/material/SpatialMaterial.d.ts +7 -0
- package/dist/reality/material/SpatialMaterial.js +13 -0
- package/dist/reality/material/SpatialMaterial.js.map +1 -0
- package/dist/reality/material/SpatialUnlitMaterial.d.ts +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js +16 -0
- package/dist/reality/material/SpatialUnlitMaterial.js.map +1 -0
- package/dist/reality/material/index.d.ts +8 -0
- package/dist/reality/material/index.js +3 -0
- package/dist/reality/material/index.js.map +1 -0
- package/dist/reality/realityCreator.d.ts +22 -0
- package/dist/reality/realityCreator.js +89 -0
- package/dist/reality/realityCreator.js.map +1 -0
- package/dist/reality/resource/SpatialModelAsset.d.ts +15 -0
- package/dist/reality/resource/SpatialModelAsset.js +12 -0
- package/dist/reality/resource/SpatialModelAsset.js.map +1 -0
- package/dist/reality/resource/SpatialTextureResource.d.ts +16 -0
- package/dist/reality/resource/SpatialTextureResource.js +16 -0
- package/dist/reality/resource/SpatialTextureResource.js.map +1 -0
- package/dist/reality/resource/index.d.ts +9 -0
- package/dist/reality/resource/index.js +3 -0
- package/dist/reality/resource/index.js.map +1 -0
- package/dist/runtime/WebSpatialRuntimeError.d.ts +10 -0
- package/dist/runtime/WebSpatialRuntimeError.js +14 -0
- package/dist/runtime/WebSpatialRuntimeError.js.map +1 -0
- package/dist/runtime/capability-data.d.ts +12 -0
- package/dist/runtime/capability-data.js +134 -0
- package/dist/runtime/capability-data.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.js +27 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/jsbAdapterPlatform.d.ts +9 -0
- package/dist/runtime/jsbAdapterPlatform.js +38 -0
- package/dist/runtime/jsbAdapterPlatform.js.map +1 -0
- package/dist/runtime/keys.d.ts +34 -0
- package/dist/runtime/keys.js +122 -0
- package/dist/runtime/keys.js.map +1 -0
- package/dist/runtime/semver.d.ts +8 -0
- package/dist/runtime/semver.js +32 -0
- package/dist/runtime/semver.js.map +1 -0
- package/dist/runtime/supports.d.ts +22 -0
- package/dist/runtime/supports.js +78 -0
- package/dist/runtime/supports.js.map +1 -0
- package/dist/runtime/types.d.ts +12 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/runtime/userAgent.d.ts +20 -0
- package/dist/runtime/userAgent.js +41 -0
- package/dist/runtime/userAgent.js.map +1 -0
- package/dist/scene-polyfill.d.ts +18 -0
- package/dist/scene-polyfill.js +480 -0
- package/dist/scene-polyfill.js.map +1 -0
- package/dist/spatial-host.d.ts +13 -0
- package/dist/spatial-host.js +18 -0
- package/dist/spatial-host.js.map +1 -0
- package/dist/spatial-window-polyfill.d.ts +3 -0
- package/dist/spatial-window-polyfill.js +155 -0
- package/dist/spatial-window-polyfill.js.map +1 -0
- package/dist/spatializedElementMotion-_0XWPQCE.d.ts +1009 -0
- package/dist/types/animation.d.ts +7 -0
- package/dist/types/animation.js +1 -0
- package/dist/types/animation.js.map +1 -0
- package/dist/types/global.d.ts +38 -0
- package/dist/types/global.js +1 -0
- package/dist/types/global.js.map +1 -0
- package/dist/types/internal.d.ts +7 -0
- package/dist/types/internal.js +12 -0
- package/dist/types/internal.js.map +1 -0
- package/dist/types/motion/index.d.ts +7 -0
- package/dist/types/motion/index.js +3 -0
- package/dist/types/motion/index.js.map +1 -0
- package/dist/types/motion/spatializedElementMotion.d.ts +7 -0
- package/dist/types/motion/spatializedElementMotion.js +1 -0
- package/dist/types/motion/spatializedElementMotion.js.map +1 -0
- package/dist/types/motion/spatializedMotion.d.ts +7 -0
- package/dist/types/motion/spatializedMotion.js +16 -0
- package/dist/types/motion/spatializedMotion.js.map +1 -0
- package/dist/types/motion/spatializedPlayback.d.ts +11 -0
- package/dist/types/motion/spatializedPlayback.js +1 -0
- package/dist/types/motion/spatializedPlayback.js.map +1 -0
- package/dist/types/motion/spatializedVisual.d.ts +35 -0
- package/dist/types/motion/spatializedVisual.js +1 -0
- package/dist/types/motion/spatializedVisual.js.map +1 -0
- package/dist/types/types.d.ts +7 -0
- package/dist/types/types.js +117 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +86 -0
- package/dist/utils.js.map +1 -0
- package/package.json +20 -4
- package/src/JSBCommand.ts +148 -91
- package/src/Spatial.ts +0 -21
- package/src/SpatialObject.ts +10 -0
- package/src/SpatialScene.ts +1 -5
- package/src/SpatialSession.ts +17 -3
- package/src/Spatialized2DElement.ts +5 -0
- package/src/SpatializedDynamic3DElement.ts +5 -1
- package/src/SpatializedElement.ts +29 -0
- package/src/SpatializedElementCreator.ts +6 -4
- package/src/SpatializedStatic3DElement.test.ts +99 -0
- package/src/SpatializedStatic3DElement.ts +141 -4
- package/src/WebMsgCommand.ts +50 -0
- package/src/coverage-boost.test.ts +258 -316
- package/src/index.ts +28 -12
- package/src/install-polyfills.ts +38 -0
- package/src/jsbcommand.coverage.test.ts +42 -48
- package/src/motion/AnimationObject.test.ts +421 -0
- package/src/motion/AnimationObject.ts +208 -0
- package/src/motion/compute/index.ts +10 -0
- package/src/motion/compute/motionPropertyValues.test.ts +55 -0
- package/src/motion/compute/motionPropertyValues.ts +70 -0
- package/src/motion/compute/normalize.test.ts +99 -0
- package/src/motion/compute/normalize.ts +289 -0
- package/src/motion/compute/validate.test.ts +229 -0
- package/src/motion/compute/validate.ts +156 -0
- package/src/motion/index.ts +2 -0
- package/src/motion/native/serializeMotionTimeline.test.ts +61 -0
- package/src/motion/native/serializeMotionTimeline.ts +27 -0
- package/src/platform-adapter/CommandResultUtils.ts +2 -2
- package/src/platform-adapter/createPlatformSync.ts +36 -0
- package/src/platform-adapter/index.ts +5 -51
- package/src/platform-adapter/interface.ts +35 -23
- package/src/platform-adapter/pico-os/PicoOSPlatform.ts +156 -59
- package/src/platform-adapter/puppeteer/PuppeteerPlatform.ts +37 -11
- package/src/platform-adapter/spatialRequestMetadata.test.ts +175 -0
- package/src/platform-adapter/spatialRequestMetadata.ts +48 -0
- package/src/platform-adapter/spatialSceneQuery.ts +17 -0
- package/src/platform-adapter/vision-os/VisionOSPlatform.ts +60 -24
- package/src/platform-runtime.ts +13 -0
- package/src/reality/Attachment.ts +2 -2
- package/src/reality/entity/SpatialEntity.ts +127 -2
- package/src/reality/realityCreator.ts +15 -1
- package/src/reality/resource/SpatialTextureResource.ts +16 -0
- package/src/reality/resource/index.ts +1 -0
- package/src/runtime/WebSpatialRuntimeError.ts +16 -0
- package/src/runtime/capability-data.ts +213 -0
- package/src/runtime/contract-review.test.ts +44 -0
- package/src/runtime/index.ts +17 -0
- package/src/runtime/jsbAdapterPlatform.test.ts +36 -0
- package/src/runtime/jsbAdapterPlatform.ts +51 -0
- package/src/runtime/keys.ts +140 -0
- package/src/runtime/semver.ts +33 -0
- package/src/runtime/supports.test.ts +346 -0
- package/src/runtime/supports.ts +112 -0
- package/src/runtime/types.ts +11 -0
- package/src/runtime/useAnimation.capability.test.ts +275 -0
- package/src/runtime/userAgent.ts +64 -0
- package/src/scene-polyfill.manifest.test.ts +312 -9
- package/src/scene-polyfill.request-metadata.test.ts +39 -0
- package/src/scene-polyfill.test.ts +33 -94
- package/src/scene-polyfill.ts +125 -92
- package/src/spatial-host.ts +25 -0
- package/src/spatial-window-polyfill.ts +18 -2
- package/src/transform-roundtrip.test.ts +90 -0
- package/src/types/animation.ts +224 -0
- package/src/types/{global.d.ts → global.ts} +10 -12
- package/src/types/motion/index.ts +10 -0
- package/src/types/motion/spatializedElementMotion.ts +49 -0
- package/src/types/motion/spatializedMotion.ts +147 -0
- package/src/types/motion/spatializedPlayback.ts +17 -0
- package/src/types/motion/spatializedVisual.ts +24 -0
- package/src/types/types.ts +12 -0
- package/src/utils.ts +54 -0
- package/tsup.config.ts +61 -36
- package/vitest.config.ts +5 -1
- package/dist/iife/index.d.ts +0 -1101
- package/dist/iife/index.global.js +0 -75
- package/dist/iife/index.global.js.map +0 -1
- package/src/platform-adapter/android/AndroidPlatform.ts +0 -133
- package/src/platform-adapter/ssr/SSRPlatform.ts +0 -43
- /package/src/{ssr-polyfill.ts → isSSREnv.ts} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// `@webspatial/core-sdk` ESM entry — side-effect-free.
|
|
3
|
+
//
|
|
4
|
+
// The previous top-level polyfill installation (`if (UA contains
|
|
5
|
+
// 'WebSpatial/') { injectSceneHook(); spatialWindowPolyfill(); }`) lived
|
|
6
|
+
// here historically and was extracted out per the lazy-load proposal
|
|
7
|
+
// `tasks.md §12.9` ("Pre-v1 budget calibration"). ESM bundlers cannot
|
|
8
|
+
// tree-shake observable top-level side effects, so the runtime UA guard
|
|
9
|
+
// was useless for bundle size — every consumer that imported anything
|
|
10
|
+
// from this package statically bundled `scene-polyfill.ts` + the
|
|
11
|
+
// `spatial-window-polyfill.ts` pair regardless of whether they ran in a
|
|
12
|
+
// WebSpatial browser. After this extraction, consumers opt in:
|
|
13
|
+
//
|
|
14
|
+
// import '@webspatial/core-sdk/install-polyfills'
|
|
15
|
+
//
|
|
16
|
+
// `@webspatial/react-sdk` does this from inside its spatial chunk so the
|
|
17
|
+
// polyfills install only when `bootSpatial()` resolves, never in the
|
|
18
|
+
// lean default-entry bundle.
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
export type {} from './types/global'
|
|
22
|
+
|
|
1
23
|
export { SpatialObject } from './SpatialObject'
|
|
2
24
|
export { Spatial } from './Spatial'
|
|
3
25
|
export { SpatialSession } from './SpatialSession'
|
|
@@ -10,16 +32,10 @@ export * as PhysicalMetrics from './physicalMetrics'
|
|
|
10
32
|
|
|
11
33
|
export * from './reality'
|
|
12
34
|
export * from './types/types'
|
|
13
|
-
export * from './types/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import { isSSREnv } from './ssr-polyfill'
|
|
18
|
-
import { spatialWindowPolyfill } from './spatial-window-polyfill'
|
|
19
|
-
|
|
20
|
-
export { isSSREnv }
|
|
35
|
+
export * from './types/animation'
|
|
36
|
+
export * from './types/motion'
|
|
37
|
+
export { validateSpatializedMotionConfig } from './motion'
|
|
38
|
+
export * from './runtime'
|
|
21
39
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
spatialWindowPolyfill()
|
|
25
|
-
}
|
|
40
|
+
export { composeSRT, decomposeSRT } from './utils'
|
|
41
|
+
export { isSSREnv } from './isSSREnv'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// `@webspatial/core-sdk/install-polyfills` — opt-in polyfill installer.
|
|
3
|
+
//
|
|
4
|
+
// Side-effect-only module: installs the spatial scene hook + the
|
|
5
|
+
// `window.spatial` polyfill iff the page is running inside a
|
|
6
|
+
// WebSpatial-capable browser (UA contains `WebSpatial/`) AND we are not
|
|
7
|
+
// in an SSR / non-browser environment.
|
|
8
|
+
//
|
|
9
|
+
// Previously this side effect lived as a top-level `if (...)` block in
|
|
10
|
+
// `src/index.ts`, which forced every consumer of the package — even
|
|
11
|
+
// consumers that only imported pure runtime helpers like `supports()` —
|
|
12
|
+
// to bundle `scene-polyfill.ts` (~636 LoC) and `spatial-window-polyfill.ts`
|
|
13
|
+
// (~182 LoC) statically. ESM bundlers cannot tree-shake observable
|
|
14
|
+
// side effects, so the if-statement guard was useless for bundle size.
|
|
15
|
+
//
|
|
16
|
+
// After this extraction, `src/index.ts` is side-effect-free and consumers
|
|
17
|
+
// who need the polyfills opt in explicitly:
|
|
18
|
+
//
|
|
19
|
+
// import '@webspatial/core-sdk/install-polyfills'
|
|
20
|
+
//
|
|
21
|
+
// `@webspatial/react-sdk` does this from inside its spatial chunk
|
|
22
|
+
// (`packages/react/src/spatial/index.ts`) so the polyfill installs only
|
|
23
|
+
// when the spatial chunk dynamically loads — never in the SDK's lean
|
|
24
|
+
// default entry.
|
|
25
|
+
//
|
|
26
|
+
// Per the lazy-load proposal `tasks.md §12.9` ("Pre-v1 budget calibration"),
|
|
27
|
+
// extracting these polyfills is one of the named optimization candidates
|
|
28
|
+
// for closing the marginal-delta gap on the 8 KB strict budget.
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
31
|
+
import { injectSceneHook } from './scene-polyfill'
|
|
32
|
+
import { spatialWindowPolyfill } from './spatial-window-polyfill'
|
|
33
|
+
import { isSSREnv } from './isSSREnv'
|
|
34
|
+
|
|
35
|
+
if (!isSSREnv() && navigator.userAgent.indexOf('WebSpatial/') > 0) {
|
|
36
|
+
injectSceneHook()
|
|
37
|
+
spatialWindowPolyfill()
|
|
38
|
+
}
|
|
@@ -55,12 +55,14 @@ class DOMMatrixPolyfill {
|
|
|
55
55
|
|
|
56
56
|
const platformSpy = {
|
|
57
57
|
callJSB: vi.fn(),
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
openSpatialSceneSync: vi.fn(),
|
|
59
|
+
createNativeSpatialDiv: vi.fn(),
|
|
60
|
+
createNativeAttachment: vi.fn(),
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
vi.mock('./platform-adapter', () => ({
|
|
63
|
-
createPlatform: () => platformSpy,
|
|
64
|
+
createPlatform: () => Promise.resolve(platformSpy),
|
|
65
|
+
createPlatformSync: () => platformSpy,
|
|
64
66
|
}))
|
|
65
67
|
|
|
66
68
|
function ok(data: any = {}) {
|
|
@@ -72,23 +74,20 @@ function ok(data: any = {}) {
|
|
|
72
74
|
})
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
function parseQuery(q?: string) {
|
|
76
|
-
const sp = new URLSearchParams(q ?? '')
|
|
77
|
-
const out: Record<string, string> = {}
|
|
78
|
-
for (const [k, v] of sp.entries()) out[k] = v
|
|
79
|
-
return out
|
|
80
|
-
}
|
|
81
|
-
|
|
82
77
|
describe('JSBCommand', () => {
|
|
83
78
|
beforeEach(() => {
|
|
84
79
|
platformSpy.callJSB.mockReset()
|
|
85
|
-
platformSpy.
|
|
86
|
-
platformSpy.
|
|
80
|
+
platformSpy.openSpatialSceneSync.mockReset()
|
|
81
|
+
platformSpy.createNativeSpatialDiv.mockReset()
|
|
82
|
+
platformSpy.createNativeAttachment.mockReset()
|
|
87
83
|
platformSpy.callJSB.mockImplementation(() => ok({ id: 'id-1' }))
|
|
88
|
-
platformSpy.
|
|
84
|
+
platformSpy.createNativeSpatialDiv.mockImplementation(() =>
|
|
85
|
+
ok({ windowProxy: {}, id: 'spatial-1' }),
|
|
86
|
+
)
|
|
87
|
+
platformSpy.createNativeAttachment.mockImplementation(() =>
|
|
89
88
|
ok({ windowProxy: {}, id: 'spatial-1' }),
|
|
90
89
|
)
|
|
91
|
-
platformSpy.
|
|
90
|
+
platformSpy.openSpatialSceneSync.mockImplementation(() => ({
|
|
92
91
|
success: true,
|
|
93
92
|
data: { windowProxy: {}, id: 'spatial-1' },
|
|
94
93
|
errorCode: '',
|
|
@@ -129,6 +128,7 @@ describe('JSBCommand', () => {
|
|
|
129
128
|
it('builds element commands payloads', async () => {
|
|
130
129
|
const mod = await import('./JSBCommand')
|
|
131
130
|
const {
|
|
131
|
+
CreateSpatializedElementAnimationJSBCommand,
|
|
132
132
|
UpdateSpatializedElementTransform,
|
|
133
133
|
UpdateSpatialized2DElementProperties,
|
|
134
134
|
UpdateSpatializedDynamic3DElementProperties,
|
|
@@ -186,46 +186,24 @@ describe('JSBCommand', () => {
|
|
|
186
186
|
JSON.stringify({ id: 'so-1', spatializedElementId: 'ele-1' }),
|
|
187
187
|
)
|
|
188
188
|
|
|
189
|
+
await new CreateSpatializedElementAnimationJSBCommand({
|
|
190
|
+
elementId: 'ele-1',
|
|
191
|
+
timeline: { duration: 1, tracks: [] },
|
|
192
|
+
} as any).execute()
|
|
193
|
+
expect(platformSpy.callJSB).toHaveBeenCalledWith(
|
|
194
|
+
'CreateSpatializedElementAnimation',
|
|
195
|
+
JSON.stringify({
|
|
196
|
+
elementId: 'ele-1',
|
|
197
|
+
timeline: { duration: 1, tracks: [] },
|
|
198
|
+
}),
|
|
199
|
+
)
|
|
200
|
+
|
|
189
201
|
await new UpdateUnlitMaterialProperties(obj, { color: '#fff' }).execute()
|
|
190
202
|
expect(platformSpy.callJSB).toHaveBeenCalledWith(
|
|
191
203
|
'UpdateUnlitMaterialProperties',
|
|
192
204
|
JSON.stringify({ id: 'so-1', color: '#fff' }),
|
|
193
205
|
)
|
|
194
206
|
})
|
|
195
|
-
|
|
196
|
-
it('creates query string and calls WebSpatialProtocol', async () => {
|
|
197
|
-
const mod = await import('./JSBCommand')
|
|
198
|
-
const { createSpatialSceneCommand, createSpatialized2DElementCommand } = mod
|
|
199
|
-
|
|
200
|
-
await new createSpatialized2DElementCommand().execute()
|
|
201
|
-
expect(platformSpy.callWebSpatialProtocol).toHaveBeenCalledWith(
|
|
202
|
-
'createSpatialized2DElement',
|
|
203
|
-
'',
|
|
204
|
-
undefined,
|
|
205
|
-
undefined,
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
const cmd = new createSpatialSceneCommand(
|
|
209
|
-
'https://example.com/a?b=c',
|
|
210
|
-
{ type: 'window', defaultSize: { width: 1, height: 2 } } as any,
|
|
211
|
-
'_self',
|
|
212
|
-
'popup=1',
|
|
213
|
-
)
|
|
214
|
-
await cmd.execute()
|
|
215
|
-
const call = platformSpy.callWebSpatialProtocol.mock.calls.at(-1)
|
|
216
|
-
expect(call?.[0]).toBe('createSpatialScene')
|
|
217
|
-
expect(call?.[2]).toBe('_self')
|
|
218
|
-
expect(call?.[3]).toBe('popup=1')
|
|
219
|
-
const q = parseQuery(call?.[1])
|
|
220
|
-
expect(q.url).toBe('https://example.com/a?b=c')
|
|
221
|
-
expect(JSON.parse(q.config)).toEqual({
|
|
222
|
-
type: 'window',
|
|
223
|
-
defaultSize: { width: 1, height: 2 },
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
cmd.executeSync()
|
|
227
|
-
expect(platformSpy.callWebSpatialProtocolSync).toHaveBeenCalled()
|
|
228
|
-
})
|
|
229
207
|
})
|
|
230
208
|
|
|
231
209
|
describe('SpatialObject', () => {
|
|
@@ -272,6 +250,7 @@ describe('realityCreator', () => {
|
|
|
272
250
|
if (cmd === 'CreateModelComponent') return ok({ id: 'cmp-1' })
|
|
273
251
|
if (cmd === 'CreateSpatialModelEntity') return ok({ id: 'ment-1' })
|
|
274
252
|
if (cmd === 'CreateModelAsset') return ok({ id: 'asset-1' })
|
|
253
|
+
if (cmd === 'CreateTexture') return ok({ id: 'tex-1' })
|
|
275
254
|
return ok({ id: 'id-1' })
|
|
276
255
|
})
|
|
277
256
|
})
|
|
@@ -282,6 +261,7 @@ describe('realityCreator', () => {
|
|
|
282
261
|
const mat = await import('./reality/material/SpatialUnlitMaterial')
|
|
283
262
|
const cmp = await import('./reality/component/ModelComponent')
|
|
284
263
|
const asset = await import('./reality/resource/SpatialModelAsset')
|
|
264
|
+
const tex = await import('./reality/resource/SpatialTextureResource')
|
|
285
265
|
const ent = await import('./reality/entity/SpatialEntity')
|
|
286
266
|
const modelEnt = await import('./reality/entity/SpatialModelEntity')
|
|
287
267
|
|
|
@@ -311,6 +291,10 @@ describe('realityCreator', () => {
|
|
|
311
291
|
creator.createModelAsset({ url: 'https://example.com/a.glb' }),
|
|
312
292
|
).resolves.toBeInstanceOf(asset.SpatialModelAsset)
|
|
313
293
|
|
|
294
|
+
await expect(
|
|
295
|
+
creator.createSpatialTexture({ url: 'https://example.com/tex.png' }),
|
|
296
|
+
).resolves.toBeInstanceOf(tex.SpatialTextureResource)
|
|
297
|
+
|
|
314
298
|
await expect(
|
|
315
299
|
creator.createSpatialModelEntity(
|
|
316
300
|
{ modelAssetId: 'asset-1' },
|
|
@@ -441,12 +425,17 @@ describe('SpatializedElement', () => {
|
|
|
441
425
|
SpatialWebEvent.init()
|
|
442
426
|
|
|
443
427
|
class TestElement extends SpatializedElement {
|
|
428
|
+
/** Identifies the supported motion target kind for this test element. */
|
|
429
|
+
readonly kind = 'spatialized2d' as const
|
|
430
|
+
|
|
444
431
|
updateProperties = vi.fn().mockResolvedValue({
|
|
445
432
|
success: true,
|
|
446
433
|
data: undefined,
|
|
447
434
|
errorCode: '',
|
|
448
435
|
errorMessage: '',
|
|
449
436
|
})
|
|
437
|
+
|
|
438
|
+
motion = vi.fn()
|
|
450
439
|
}
|
|
451
440
|
|
|
452
441
|
const e = new TestElement('el3')
|
|
@@ -469,12 +458,17 @@ describe('SpatializedElement', () => {
|
|
|
469
458
|
SpatialWebEvent.init()
|
|
470
459
|
|
|
471
460
|
class TestElement extends SpatializedElement {
|
|
461
|
+
/** Identifies the supported motion target kind for this test element. */
|
|
462
|
+
readonly kind = 'spatialized2d' as const
|
|
463
|
+
|
|
472
464
|
updateProperties = vi.fn().mockResolvedValue({
|
|
473
465
|
success: true,
|
|
474
466
|
data: undefined,
|
|
475
467
|
errorCode: '',
|
|
476
468
|
errorMessage: '',
|
|
477
469
|
})
|
|
470
|
+
|
|
471
|
+
motion = vi.fn()
|
|
478
472
|
}
|
|
479
473
|
|
|
480
474
|
const e = new TestElement('el4')
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { SpatialWebMsgType } from '../WebMsgCommand'
|
|
3
|
+
import { SpatialWebEvent } from '../SpatialWebEvent'
|
|
4
|
+
import type { SpatialAnimationStateChangedMsg } from '../types/motion/spatializedElementMotion'
|
|
5
|
+
|
|
6
|
+
const spatialAnimationStateChangedType: SpatialAnimationStateChangedMsg['type'] =
|
|
7
|
+
SpatialWebMsgType.animationstatechange
|
|
8
|
+
|
|
9
|
+
const spies = vi.hoisted(() => ({
|
|
10
|
+
createCommandSpy: vi.fn(),
|
|
11
|
+
controlCommandSpy: vi.fn(),
|
|
12
|
+
destroyCommandSpy: vi.fn(),
|
|
13
|
+
failNextControlType: undefined as string | undefined,
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
vi.mock('../JSBCommand', () => {
|
|
17
|
+
const ok = (data: any = {}) => ({
|
|
18
|
+
success: true,
|
|
19
|
+
data,
|
|
20
|
+
errorCode: '',
|
|
21
|
+
errorMessage: '',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
class CreateSpatializedElementAnimationJSBCommand {
|
|
25
|
+
constructor(public command: any) {
|
|
26
|
+
spies.createCommandSpy(command)
|
|
27
|
+
}
|
|
28
|
+
execute = vi.fn().mockResolvedValue(ok({ id: 'native-anim-1' }))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class ControlSpatializedElementAnimationJSBCommand {
|
|
32
|
+
constructor(public command: any) {
|
|
33
|
+
spies.controlCommandSpy(command)
|
|
34
|
+
}
|
|
35
|
+
execute = vi.fn().mockImplementation(() => {
|
|
36
|
+
if (spies.failNextControlType === this.command.type) {
|
|
37
|
+
spies.failNextControlType = undefined
|
|
38
|
+
return Promise.resolve({
|
|
39
|
+
success: false,
|
|
40
|
+
data: undefined,
|
|
41
|
+
errorCode: 'E_CONTROL',
|
|
42
|
+
errorMessage: `${this.command.type} failed`,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
return Promise.resolve(ok())
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class DestroyCommand {
|
|
50
|
+
constructor(public id: string) {
|
|
51
|
+
spies.destroyCommandSpy(id)
|
|
52
|
+
}
|
|
53
|
+
execute = vi.fn().mockResolvedValue(ok({ destroyed: true }))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class InspectCommand {
|
|
57
|
+
constructor(public id: string = '') {}
|
|
58
|
+
execute = vi.fn().mockResolvedValue(ok({ inspected: true }))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
CreateSpatializedElementAnimationJSBCommand,
|
|
63
|
+
ControlSpatializedElementAnimationJSBCommand,
|
|
64
|
+
DestroyCommand,
|
|
65
|
+
InspectCommand,
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('AnimationObject', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
spies.createCommandSpy.mockClear()
|
|
72
|
+
spies.controlCommandSpy.mockClear()
|
|
73
|
+
spies.destroyCommandSpy.mockClear()
|
|
74
|
+
spies.failNextControlType = undefined
|
|
75
|
+
SpatialWebEvent.eventReceiver = {}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
async function createAnimation() {
|
|
79
|
+
const { SpatializedElement } = await import('../SpatializedElement')
|
|
80
|
+
|
|
81
|
+
class TestSpatializedElement extends SpatializedElement {
|
|
82
|
+
/** Identifies the supported motion target kind for this test element. */
|
|
83
|
+
readonly kind = 'static3d' as const
|
|
84
|
+
|
|
85
|
+
async updateProperties() {
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
data: undefined,
|
|
89
|
+
errorCode: '',
|
|
90
|
+
errorMessage: '',
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const element = new TestSpatializedElement('element-1')
|
|
96
|
+
return element.createAnimation({
|
|
97
|
+
duration: 1,
|
|
98
|
+
from: {
|
|
99
|
+
transform: { translate: { x: 0 } },
|
|
100
|
+
},
|
|
101
|
+
to: {
|
|
102
|
+
transform: { translate: { x: 10 } },
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
it('types native state events with the shared animationstatechange discriminant', () => {
|
|
108
|
+
expect(spatialAnimationStateChangedType).toBe(
|
|
109
|
+
SpatialWebMsgType.animationstatechange,
|
|
110
|
+
)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('creates from the native create command and tracks native state directly', async () => {
|
|
114
|
+
const { AnimationObject } = await import('./AnimationObject')
|
|
115
|
+
|
|
116
|
+
const animation = await createAnimation()
|
|
117
|
+
|
|
118
|
+
expect(animation).toBeInstanceOf(AnimationObject)
|
|
119
|
+
expect(animation.id).toBe('native-anim-1')
|
|
120
|
+
expect('uuid' in animation).toBe(false)
|
|
121
|
+
expect(spies.createCommandSpy).toHaveBeenCalledWith(
|
|
122
|
+
expect.objectContaining({
|
|
123
|
+
elementId: 'element-1',
|
|
124
|
+
timeline: expect.any(Object),
|
|
125
|
+
}),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
129
|
+
expect(receiver).toEqual(expect.any(Function))
|
|
130
|
+
|
|
131
|
+
const onValuesChange = vi.fn()
|
|
132
|
+
const onStateChange = vi.fn()
|
|
133
|
+
const onComplete = vi.fn()
|
|
134
|
+
animation.setCallbacks({
|
|
135
|
+
onValuesChange,
|
|
136
|
+
onStateChange,
|
|
137
|
+
onComplete,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
receiver?.({
|
|
141
|
+
detail: {
|
|
142
|
+
animationId: 'other-animation',
|
|
143
|
+
action: 'play',
|
|
144
|
+
playState: 'running',
|
|
145
|
+
finished: false,
|
|
146
|
+
values: { transform: { translate: { x: 2.5 } } },
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
expect(animation.playState).toBe('idle')
|
|
151
|
+
expect(onValuesChange).not.toHaveBeenCalled()
|
|
152
|
+
expect(onStateChange).not.toHaveBeenCalled()
|
|
153
|
+
|
|
154
|
+
receiver?.({
|
|
155
|
+
detail: {
|
|
156
|
+
animationId: animation.id,
|
|
157
|
+
action: 'play',
|
|
158
|
+
playState: 'running',
|
|
159
|
+
finished: false,
|
|
160
|
+
values: { transform: { translate: { x: 5 } } },
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
expect(animation.playState).toBe('running')
|
|
165
|
+
expect(animation.isAnimating).toBe(true)
|
|
166
|
+
expect(animation.isPaused).toBe(false)
|
|
167
|
+
expect(animation.finished).toBe(false)
|
|
168
|
+
expect(onValuesChange).toHaveBeenCalledWith({
|
|
169
|
+
transform: { translate: { x: 5 } },
|
|
170
|
+
})
|
|
171
|
+
expect(onStateChange).toHaveBeenCalledTimes(1)
|
|
172
|
+
|
|
173
|
+
receiver?.({
|
|
174
|
+
detail: {
|
|
175
|
+
animationId: animation.id,
|
|
176
|
+
action: 'complete',
|
|
177
|
+
playState: 'finished',
|
|
178
|
+
finished: true,
|
|
179
|
+
values: { transform: { translate: { x: 10 } } },
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
expect(animation.playState).toBe('finished')
|
|
184
|
+
expect(animation.finished).toBe(true)
|
|
185
|
+
expect(onComplete).toHaveBeenCalledWith({
|
|
186
|
+
transform: { translate: { x: 10 } },
|
|
187
|
+
})
|
|
188
|
+
expect(onStateChange).toHaveBeenCalledTimes(2)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('uses the inherited destroy path and unregisters its native WebMsg receiver', async () => {
|
|
192
|
+
const animation = await createAnimation()
|
|
193
|
+
|
|
194
|
+
expect(SpatialWebEvent.eventReceiver[animation.id]).toEqual(
|
|
195
|
+
expect.any(Function),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
await animation.destroy()
|
|
199
|
+
|
|
200
|
+
expect(spies.destroyCommandSpy).toHaveBeenCalledWith(animation.id)
|
|
201
|
+
expect(spies.controlCommandSpy).not.toHaveBeenCalledWith(
|
|
202
|
+
expect.objectContaining({ type: 'destroy' }),
|
|
203
|
+
)
|
|
204
|
+
expect(animation.isDestroyed).toBe(true)
|
|
205
|
+
expect(SpatialWebEvent.eventReceiver[animation.id]).toBeUndefined()
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('cleans up local lifecycle when native reports destroy', async () => {
|
|
209
|
+
const animation = await createAnimation()
|
|
210
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
211
|
+
|
|
212
|
+
expect(receiver).toEqual(expect.any(Function))
|
|
213
|
+
|
|
214
|
+
receiver?.({
|
|
215
|
+
detail: {
|
|
216
|
+
animationId: animation.id,
|
|
217
|
+
action: 'destroy',
|
|
218
|
+
playState: 'idle',
|
|
219
|
+
finished: false,
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
expect(animation.isDestroyed).toBe(true)
|
|
224
|
+
expect(SpatialWebEvent.eventReceiver[animation.id]).toBeUndefined()
|
|
225
|
+
|
|
226
|
+
await animation.play()
|
|
227
|
+
expect(spies.controlCommandSpy).not.toHaveBeenCalled()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('fires onStart only after the native start event arrives', async () => {
|
|
231
|
+
const animation = await createAnimation()
|
|
232
|
+
const onStart = vi.fn()
|
|
233
|
+
animation.setCallbacks({ onStart })
|
|
234
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
235
|
+
|
|
236
|
+
await animation.play()
|
|
237
|
+
expect(onStart).not.toHaveBeenCalled()
|
|
238
|
+
|
|
239
|
+
receiver?.({
|
|
240
|
+
detail: {
|
|
241
|
+
animationId: animation.id,
|
|
242
|
+
action: 'play',
|
|
243
|
+
playState: 'running',
|
|
244
|
+
finished: false,
|
|
245
|
+
values: { transform: { translate: { x: 1 } } },
|
|
246
|
+
},
|
|
247
|
+
})
|
|
248
|
+
expect(onStart).not.toHaveBeenCalled()
|
|
249
|
+
|
|
250
|
+
receiver?.({
|
|
251
|
+
detail: {
|
|
252
|
+
animationId: animation.id,
|
|
253
|
+
action: 'start',
|
|
254
|
+
playState: 'running',
|
|
255
|
+
finished: false,
|
|
256
|
+
values: { transform: { translate: { x: 1 } } },
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
expect(onStart).toHaveBeenCalledTimes(1)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('does not fire onStart when play control fails and retries only start after native start', async () => {
|
|
263
|
+
const animation = await createAnimation()
|
|
264
|
+
const onStart = vi.fn()
|
|
265
|
+
const onError = vi.fn()
|
|
266
|
+
animation.setCallbacks({ onStart, onError })
|
|
267
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
268
|
+
|
|
269
|
+
spies.failNextControlType = 'play'
|
|
270
|
+
|
|
271
|
+
await expect(animation.play()).rejects.toThrow('play failed')
|
|
272
|
+
expect(onStart).not.toHaveBeenCalled()
|
|
273
|
+
expect(onError).not.toHaveBeenCalled()
|
|
274
|
+
|
|
275
|
+
await animation.play()
|
|
276
|
+
expect(onStart).not.toHaveBeenCalled()
|
|
277
|
+
|
|
278
|
+
receiver?.({
|
|
279
|
+
detail: {
|
|
280
|
+
animationId: animation.id,
|
|
281
|
+
action: 'start',
|
|
282
|
+
playState: 'running',
|
|
283
|
+
finished: false,
|
|
284
|
+
values: { transform: { translate: { x: 2 } } },
|
|
285
|
+
},
|
|
286
|
+
})
|
|
287
|
+
expect(onStart).toHaveBeenCalledTimes(1)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('normalizes native playback errors without exposing animation id', async () => {
|
|
291
|
+
const animation = await createAnimation()
|
|
292
|
+
const onError = vi.fn()
|
|
293
|
+
animation.setCallbacks({ onError })
|
|
294
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
295
|
+
|
|
296
|
+
receiver?.({
|
|
297
|
+
detail: {
|
|
298
|
+
animationId: animation.id,
|
|
299
|
+
action: 'error',
|
|
300
|
+
playState: 'idle',
|
|
301
|
+
finished: false,
|
|
302
|
+
error: {
|
|
303
|
+
code: 'mask-conflict',
|
|
304
|
+
message: 'Another animation is already writing this target',
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
expect(onError).toHaveBeenCalledWith({
|
|
310
|
+
command: 'play',
|
|
311
|
+
code: 'mask-conflict',
|
|
312
|
+
reason: 'Another animation is already writing this target',
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it.each(['stop', 'reset', 'finish'] as const)(
|
|
317
|
+
'does not re-fire onStart after %s until native terminal event arrives',
|
|
318
|
+
async controlType => {
|
|
319
|
+
const animation = await createAnimation()
|
|
320
|
+
const onStart = vi.fn()
|
|
321
|
+
animation.setCallbacks({ onStart })
|
|
322
|
+
const receiver = SpatialWebEvent.eventReceiver[animation.id]
|
|
323
|
+
|
|
324
|
+
receiver?.({
|
|
325
|
+
detail: {
|
|
326
|
+
animationId: animation.id,
|
|
327
|
+
action: 'start',
|
|
328
|
+
playState: 'running',
|
|
329
|
+
finished: false,
|
|
330
|
+
values: { transform: { translate: { x: 1 } } },
|
|
331
|
+
},
|
|
332
|
+
})
|
|
333
|
+
expect(onStart).toHaveBeenCalledTimes(1)
|
|
334
|
+
|
|
335
|
+
await animation[controlType]()
|
|
336
|
+
|
|
337
|
+
receiver?.({
|
|
338
|
+
detail: {
|
|
339
|
+
animationId: animation.id,
|
|
340
|
+
action: 'start',
|
|
341
|
+
playState: 'running',
|
|
342
|
+
finished: false,
|
|
343
|
+
values: { transform: { translate: { x: 2 } } },
|
|
344
|
+
},
|
|
345
|
+
})
|
|
346
|
+
expect(onStart).toHaveBeenCalledTimes(1)
|
|
347
|
+
|
|
348
|
+
receiver?.({
|
|
349
|
+
detail: {
|
|
350
|
+
animationId: animation.id,
|
|
351
|
+
action: controlType,
|
|
352
|
+
playState: controlType === 'finish' ? 'finished' : 'idle',
|
|
353
|
+
finished: controlType === 'finish',
|
|
354
|
+
values: { transform: { translate: { x: 3 } } },
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
receiver?.({
|
|
359
|
+
detail: {
|
|
360
|
+
animationId: animation.id,
|
|
361
|
+
action: 'start',
|
|
362
|
+
playState: 'running',
|
|
363
|
+
finished: false,
|
|
364
|
+
values: { transform: { translate: { x: 4 } } },
|
|
365
|
+
},
|
|
366
|
+
})
|
|
367
|
+
expect(onStart).toHaveBeenCalledTimes(2)
|
|
368
|
+
},
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
it('sends all playback controls to the same native animation id without recreating', async () => {
|
|
372
|
+
const animation = await createAnimation()
|
|
373
|
+
spies.createCommandSpy.mockClear()
|
|
374
|
+
|
|
375
|
+
await animation.play()
|
|
376
|
+
await animation.pause()
|
|
377
|
+
await animation.play()
|
|
378
|
+
await animation.stop()
|
|
379
|
+
await animation.reset()
|
|
380
|
+
await animation.finish()
|
|
381
|
+
|
|
382
|
+
expect(spies.createCommandSpy).not.toHaveBeenCalled()
|
|
383
|
+
expect(spies.controlCommandSpy.mock.calls.map(call => call[0])).toEqual([
|
|
384
|
+
{ animationId: animation.id, type: 'play' },
|
|
385
|
+
{ animationId: animation.id, type: 'pause' },
|
|
386
|
+
{ animationId: animation.id, type: 'play' },
|
|
387
|
+
{ animationId: animation.id, type: 'stop' },
|
|
388
|
+
{ animationId: animation.id, type: 'reset' },
|
|
389
|
+
{ animationId: animation.id, type: 'finish' },
|
|
390
|
+
])
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
describe('Core exports', () => {
|
|
395
|
+
it('keeps internal motion architecture objects out of the public Core export surface', async () => {
|
|
396
|
+
const mod = await import('../index')
|
|
397
|
+
|
|
398
|
+
expect((mod as any).AnimationObject).toBeUndefined()
|
|
399
|
+
expect((mod as any).AnimationObjectChannel).toBeUndefined()
|
|
400
|
+
expect((mod as any).AnimationObjectBridge).toBeUndefined()
|
|
401
|
+
expect((mod as any).SpatialObjectBridge).toBeUndefined()
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('exports AnimationObject from the motion entrypoint', async () => {
|
|
405
|
+
const mod = await import('./index')
|
|
406
|
+
|
|
407
|
+
expect(mod.AnimationObject).toBeDefined()
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it('removes legacy animateMotion entry points from spatialized elements', async () => {
|
|
411
|
+
const mod = await import('../index')
|
|
412
|
+
|
|
413
|
+
expect('animateMotion' in mod.Spatialized2DElement.prototype).toBe(false)
|
|
414
|
+
expect('animateMotion' in mod.SpatializedStatic3DElement.prototype).toBe(
|
|
415
|
+
false,
|
|
416
|
+
)
|
|
417
|
+
expect('animateMotion' in mod.SpatializedDynamic3DElement.prototype).toBe(
|
|
418
|
+
false,
|
|
419
|
+
)
|
|
420
|
+
})
|
|
421
|
+
})
|