@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.
Files changed (320) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +10 -0
  3. package/dist/JSBCommand.d.ts +398 -0
  4. package/dist/JSBCommand.js +668 -0
  5. package/dist/JSBCommand.js.map +1 -0
  6. package/dist/Spatial.d.ts +63 -0
  7. package/dist/Spatial.js +60 -0
  8. package/dist/Spatial.js.map +1 -0
  9. package/dist/SpatialObject.d.ts +19 -0
  10. package/dist/SpatialObject.js +44 -0
  11. package/dist/SpatialObject.js.map +1 -0
  12. package/dist/SpatialScene.d.ts +51 -0
  13. package/dist/SpatialScene.js +74 -0
  14. package/dist/SpatialScene.js.map +1 -0
  15. package/dist/SpatialSession.d.ts +143 -0
  16. package/dist/SpatialSession.js +173 -0
  17. package/dist/SpatialSession.js.map +1 -0
  18. package/dist/SpatialWebEvent.d.ts +8 -0
  19. package/dist/SpatialWebEvent.js +18 -0
  20. package/dist/SpatialWebEvent.js.map +1 -0
  21. package/dist/SpatialWebEventCreator.d.ts +11 -0
  22. package/dist/SpatialWebEventCreator.js +11 -0
  23. package/dist/SpatialWebEventCreator.js.map +1 -0
  24. package/dist/Spatialized2DElement.d.ts +42 -0
  25. package/dist/Spatialized2DElement.js +47 -0
  26. package/dist/Spatialized2DElement.js.map +1 -0
  27. package/dist/SpatializedDynamic3DElement.d.ts +7 -0
  28. package/dist/SpatializedDynamic3DElement.js +43 -0
  29. package/dist/SpatializedDynamic3DElement.js.map +1 -0
  30. package/dist/SpatializedElement.d.ts +7 -0
  31. package/dist/SpatializedElement.js +206 -0
  32. package/dist/SpatializedElement.js.map +1 -0
  33. package/dist/SpatializedElementCreator.d.ts +15 -0
  34. package/dist/SpatializedElementCreator.js +47 -0
  35. package/dist/SpatializedElementCreator.js.map +1 -0
  36. package/dist/SpatializedStatic3DElement.d.ts +195 -0
  37. package/dist/SpatializedStatic3DElement.js +327 -0
  38. package/dist/SpatializedStatic3DElement.js.map +1 -0
  39. package/dist/WebMsgCommand.d.ts +7 -0
  40. package/dist/WebMsgCommand.js +23 -0
  41. package/dist/WebMsgCommand.js.map +1 -0
  42. package/dist/index.d.ts +35 -1101
  43. package/dist/index.js +21 -3393
  44. package/dist/index.js.map +1 -1
  45. package/dist/install-polyfills.d.ts +2 -0
  46. package/dist/install-polyfills.js +13 -0
  47. package/dist/install-polyfills.js.map +1 -0
  48. package/dist/isSSREnv.d.ts +3 -0
  49. package/dist/isSSREnv.js +6 -0
  50. package/dist/isSSREnv.js.map +1 -0
  51. package/dist/motion/AnimationObject.d.ts +7 -0
  52. package/dist/motion/AnimationObject.js +140 -0
  53. package/dist/motion/AnimationObject.js.map +1 -0
  54. package/dist/motion/compute/index.d.ts +10 -0
  55. package/dist/motion/compute/index.js +19 -0
  56. package/dist/motion/compute/index.js.map +1 -0
  57. package/dist/motion/compute/motionPropertyValues.d.ts +19 -0
  58. package/dist/motion/compute/motionPropertyValues.js +50 -0
  59. package/dist/motion/compute/motionPropertyValues.js.map +1 -0
  60. package/dist/motion/compute/normalize.d.ts +31 -0
  61. package/dist/motion/compute/normalize.js +201 -0
  62. package/dist/motion/compute/normalize.js.map +1 -0
  63. package/dist/motion/compute/validate.d.ts +22 -0
  64. package/dist/motion/compute/validate.js +103 -0
  65. package/dist/motion/compute/validate.js.map +1 -0
  66. package/dist/motion/index.d.ts +8 -0
  67. package/dist/motion/index.js +6 -0
  68. package/dist/motion/index.js.map +1 -0
  69. package/dist/motion/native/serializeMotionTimeline.d.ts +12 -0
  70. package/dist/motion/native/serializeMotionTimeline.js +21 -0
  71. package/dist/motion/native/serializeMotionTimeline.js.map +1 -0
  72. package/dist/physicalMetrics-W5uZ5xKb.d.ts +48 -0
  73. package/dist/physicalMetrics.d.ts +1 -0
  74. package/dist/physicalMetrics.js +58 -0
  75. package/dist/physicalMetrics.js.map +1 -0
  76. package/dist/platform-adapter/CommandResultUtils.d.ts +12 -0
  77. package/dist/platform-adapter/CommandResultUtils.js +21 -0
  78. package/dist/platform-adapter/CommandResultUtils.js.map +1 -0
  79. package/dist/platform-adapter/createPlatformSync.d.ts +16 -0
  80. package/dist/platform-adapter/createPlatformSync.js +30 -0
  81. package/dist/platform-adapter/createPlatformSync.js.map +1 -0
  82. package/dist/platform-adapter/index.d.ts +12 -0
  83. package/dist/platform-adapter/index.js +10 -0
  84. package/dist/platform-adapter/index.js.map +1 -0
  85. package/dist/platform-adapter/interface.d.ts +7 -0
  86. package/dist/platform-adapter/interface.js +1 -0
  87. package/dist/platform-adapter/interface.js.map +1 -0
  88. package/dist/platform-adapter/pico-os/PicoOSPlatform.d.ts +25 -0
  89. package/dist/platform-adapter/pico-os/PicoOSPlatform.js +168 -0
  90. package/dist/platform-adapter/pico-os/PicoOSPlatform.js.map +1 -0
  91. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.d.ts +66 -0
  92. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js +372 -0
  93. package/dist/platform-adapter/puppeteer/PuppeteerPlatform.js.map +1 -0
  94. package/dist/platform-adapter/spatialRequestMetadata.d.ts +6 -0
  95. package/dist/platform-adapter/spatialRequestMetadata.js +39 -0
  96. package/dist/platform-adapter/spatialRequestMetadata.js.map +1 -0
  97. package/dist/platform-adapter/spatialSceneQuery.d.ts +12 -0
  98. package/dist/platform-adapter/spatialSceneQuery.js +12 -0
  99. package/dist/platform-adapter/spatialSceneQuery.js.map +1 -0
  100. package/dist/platform-adapter/vision-os/VisionOSPlatform.d.ts +19 -0
  101. package/dist/platform-adapter/vision-os/VisionOSPlatform.js +77 -0
  102. package/dist/platform-adapter/vision-os/VisionOSPlatform.js.map +1 -0
  103. package/dist/platform-runtime.d.ts +12 -0
  104. package/dist/platform-runtime.js +14 -0
  105. package/dist/platform-runtime.js.map +1 -0
  106. package/dist/reality/Attachment.d.ts +19 -0
  107. package/dist/reality/Attachment.js +39 -0
  108. package/dist/reality/Attachment.js.map +1 -0
  109. package/dist/reality/component/ModelComponent.d.ts +14 -0
  110. package/dist/reality/component/ModelComponent.js +11 -0
  111. package/dist/reality/component/ModelComponent.js.map +1 -0
  112. package/dist/reality/component/SpatialComponent.d.ts +8 -0
  113. package/dist/reality/component/SpatialComponent.js +19 -0
  114. package/dist/reality/component/SpatialComponent.js.map +1 -0
  115. package/dist/reality/component/index.d.ts +8 -0
  116. package/dist/reality/component/index.js +3 -0
  117. package/dist/reality/component/index.js.map +1 -0
  118. package/dist/reality/entity/SpatialEntity.d.ts +7 -0
  119. package/dist/reality/entity/SpatialEntity.js +291 -0
  120. package/dist/reality/entity/SpatialEntity.js.map +1 -0
  121. package/dist/reality/entity/SpatialModelEntity.d.ts +17 -0
  122. package/dist/reality/entity/SpatialModelEntity.js +17 -0
  123. package/dist/reality/entity/SpatialModelEntity.js.map +1 -0
  124. package/dist/reality/entity/index.d.ts +8 -0
  125. package/dist/reality/entity/index.js +3 -0
  126. package/dist/reality/entity/index.js.map +1 -0
  127. package/dist/reality/geometry/SpatialBoxGeometry.d.ts +16 -0
  128. package/dist/reality/geometry/SpatialBoxGeometry.js +13 -0
  129. package/dist/reality/geometry/SpatialBoxGeometry.js.map +1 -0
  130. package/dist/reality/geometry/SpatialConeGeometry.d.ts +16 -0
  131. package/dist/reality/geometry/SpatialConeGeometry.js +13 -0
  132. package/dist/reality/geometry/SpatialConeGeometry.js.map +1 -0
  133. package/dist/reality/geometry/SpatialCylinderGeometry.d.ts +16 -0
  134. package/dist/reality/geometry/SpatialCylinderGeometry.js +13 -0
  135. package/dist/reality/geometry/SpatialCylinderGeometry.js.map +1 -0
  136. package/dist/reality/geometry/SpatialGeometry.d.ts +7 -0
  137. package/dist/reality/geometry/SpatialGeometry.js +13 -0
  138. package/dist/reality/geometry/SpatialGeometry.js.map +1 -0
  139. package/dist/reality/geometry/SpatialPlaneGeometry.d.ts +16 -0
  140. package/dist/reality/geometry/SpatialPlaneGeometry.js +13 -0
  141. package/dist/reality/geometry/SpatialPlaneGeometry.js.map +1 -0
  142. package/dist/reality/geometry/SpatialSphereGeometry.d.ts +16 -0
  143. package/dist/reality/geometry/SpatialSphereGeometry.js +13 -0
  144. package/dist/reality/geometry/SpatialSphereGeometry.js.map +1 -0
  145. package/dist/reality/geometry/index.d.ts +12 -0
  146. package/dist/reality/geometry/index.js +7 -0
  147. package/dist/reality/geometry/index.js.map +1 -0
  148. package/dist/reality/index.d.ts +18 -0
  149. package/dist/reality/index.js +7 -0
  150. package/dist/reality/index.js.map +1 -0
  151. package/dist/reality/material/SpatialMaterial.d.ts +7 -0
  152. package/dist/reality/material/SpatialMaterial.js +13 -0
  153. package/dist/reality/material/SpatialMaterial.js.map +1 -0
  154. package/dist/reality/material/SpatialUnlitMaterial.d.ts +16 -0
  155. package/dist/reality/material/SpatialUnlitMaterial.js +16 -0
  156. package/dist/reality/material/SpatialUnlitMaterial.js.map +1 -0
  157. package/dist/reality/material/index.d.ts +8 -0
  158. package/dist/reality/material/index.js +3 -0
  159. package/dist/reality/material/index.js.map +1 -0
  160. package/dist/reality/realityCreator.d.ts +22 -0
  161. package/dist/reality/realityCreator.js +89 -0
  162. package/dist/reality/realityCreator.js.map +1 -0
  163. package/dist/reality/resource/SpatialModelAsset.d.ts +15 -0
  164. package/dist/reality/resource/SpatialModelAsset.js +12 -0
  165. package/dist/reality/resource/SpatialModelAsset.js.map +1 -0
  166. package/dist/reality/resource/SpatialTextureResource.d.ts +16 -0
  167. package/dist/reality/resource/SpatialTextureResource.js +16 -0
  168. package/dist/reality/resource/SpatialTextureResource.js.map +1 -0
  169. package/dist/reality/resource/index.d.ts +9 -0
  170. package/dist/reality/resource/index.js +3 -0
  171. package/dist/reality/resource/index.js.map +1 -0
  172. package/dist/runtime/WebSpatialRuntimeError.d.ts +10 -0
  173. package/dist/runtime/WebSpatialRuntimeError.js +14 -0
  174. package/dist/runtime/WebSpatialRuntimeError.js.map +1 -0
  175. package/dist/runtime/capability-data.d.ts +12 -0
  176. package/dist/runtime/capability-data.js +134 -0
  177. package/dist/runtime/capability-data.js.map +1 -0
  178. package/dist/runtime/index.d.ts +8 -0
  179. package/dist/runtime/index.js +27 -0
  180. package/dist/runtime/index.js.map +1 -0
  181. package/dist/runtime/jsbAdapterPlatform.d.ts +9 -0
  182. package/dist/runtime/jsbAdapterPlatform.js +38 -0
  183. package/dist/runtime/jsbAdapterPlatform.js.map +1 -0
  184. package/dist/runtime/keys.d.ts +34 -0
  185. package/dist/runtime/keys.js +122 -0
  186. package/dist/runtime/keys.js.map +1 -0
  187. package/dist/runtime/semver.d.ts +8 -0
  188. package/dist/runtime/semver.js +32 -0
  189. package/dist/runtime/semver.js.map +1 -0
  190. package/dist/runtime/supports.d.ts +22 -0
  191. package/dist/runtime/supports.js +78 -0
  192. package/dist/runtime/supports.js.map +1 -0
  193. package/dist/runtime/types.d.ts +12 -0
  194. package/dist/runtime/types.js +1 -0
  195. package/dist/runtime/types.js.map +1 -0
  196. package/dist/runtime/userAgent.d.ts +20 -0
  197. package/dist/runtime/userAgent.js +41 -0
  198. package/dist/runtime/userAgent.js.map +1 -0
  199. package/dist/scene-polyfill.d.ts +18 -0
  200. package/dist/scene-polyfill.js +480 -0
  201. package/dist/scene-polyfill.js.map +1 -0
  202. package/dist/spatial-host.d.ts +13 -0
  203. package/dist/spatial-host.js +18 -0
  204. package/dist/spatial-host.js.map +1 -0
  205. package/dist/spatial-window-polyfill.d.ts +3 -0
  206. package/dist/spatial-window-polyfill.js +155 -0
  207. package/dist/spatial-window-polyfill.js.map +1 -0
  208. package/dist/spatializedElementMotion-_0XWPQCE.d.ts +1009 -0
  209. package/dist/types/animation.d.ts +7 -0
  210. package/dist/types/animation.js +1 -0
  211. package/dist/types/animation.js.map +1 -0
  212. package/dist/types/global.d.ts +38 -0
  213. package/dist/types/global.js +1 -0
  214. package/dist/types/global.js.map +1 -0
  215. package/dist/types/internal.d.ts +7 -0
  216. package/dist/types/internal.js +12 -0
  217. package/dist/types/internal.js.map +1 -0
  218. package/dist/types/motion/index.d.ts +7 -0
  219. package/dist/types/motion/index.js +3 -0
  220. package/dist/types/motion/index.js.map +1 -0
  221. package/dist/types/motion/spatializedElementMotion.d.ts +7 -0
  222. package/dist/types/motion/spatializedElementMotion.js +1 -0
  223. package/dist/types/motion/spatializedElementMotion.js.map +1 -0
  224. package/dist/types/motion/spatializedMotion.d.ts +7 -0
  225. package/dist/types/motion/spatializedMotion.js +16 -0
  226. package/dist/types/motion/spatializedMotion.js.map +1 -0
  227. package/dist/types/motion/spatializedPlayback.d.ts +11 -0
  228. package/dist/types/motion/spatializedPlayback.js +1 -0
  229. package/dist/types/motion/spatializedPlayback.js.map +1 -0
  230. package/dist/types/motion/spatializedVisual.d.ts +35 -0
  231. package/dist/types/motion/spatializedVisual.js +1 -0
  232. package/dist/types/motion/spatializedVisual.js.map +1 -0
  233. package/dist/types/types.d.ts +7 -0
  234. package/dist/types/types.js +117 -0
  235. package/dist/types/types.js.map +1 -0
  236. package/dist/utils.d.ts +44 -0
  237. package/dist/utils.js +86 -0
  238. package/dist/utils.js.map +1 -0
  239. package/package.json +20 -4
  240. package/src/JSBCommand.ts +148 -91
  241. package/src/Spatial.ts +0 -21
  242. package/src/SpatialObject.ts +10 -0
  243. package/src/SpatialScene.ts +1 -5
  244. package/src/SpatialSession.ts +17 -3
  245. package/src/Spatialized2DElement.ts +5 -0
  246. package/src/SpatializedDynamic3DElement.ts +5 -1
  247. package/src/SpatializedElement.ts +29 -0
  248. package/src/SpatializedElementCreator.ts +6 -4
  249. package/src/SpatializedStatic3DElement.test.ts +99 -0
  250. package/src/SpatializedStatic3DElement.ts +141 -4
  251. package/src/WebMsgCommand.ts +50 -0
  252. package/src/coverage-boost.test.ts +258 -316
  253. package/src/index.ts +28 -12
  254. package/src/install-polyfills.ts +38 -0
  255. package/src/jsbcommand.coverage.test.ts +42 -48
  256. package/src/motion/AnimationObject.test.ts +421 -0
  257. package/src/motion/AnimationObject.ts +208 -0
  258. package/src/motion/compute/index.ts +10 -0
  259. package/src/motion/compute/motionPropertyValues.test.ts +55 -0
  260. package/src/motion/compute/motionPropertyValues.ts +70 -0
  261. package/src/motion/compute/normalize.test.ts +99 -0
  262. package/src/motion/compute/normalize.ts +289 -0
  263. package/src/motion/compute/validate.test.ts +229 -0
  264. package/src/motion/compute/validate.ts +156 -0
  265. package/src/motion/index.ts +2 -0
  266. package/src/motion/native/serializeMotionTimeline.test.ts +61 -0
  267. package/src/motion/native/serializeMotionTimeline.ts +27 -0
  268. package/src/platform-adapter/CommandResultUtils.ts +2 -2
  269. package/src/platform-adapter/createPlatformSync.ts +36 -0
  270. package/src/platform-adapter/index.ts +5 -51
  271. package/src/platform-adapter/interface.ts +35 -23
  272. package/src/platform-adapter/pico-os/PicoOSPlatform.ts +156 -59
  273. package/src/platform-adapter/puppeteer/PuppeteerPlatform.ts +37 -11
  274. package/src/platform-adapter/spatialRequestMetadata.test.ts +175 -0
  275. package/src/platform-adapter/spatialRequestMetadata.ts +48 -0
  276. package/src/platform-adapter/spatialSceneQuery.ts +17 -0
  277. package/src/platform-adapter/vision-os/VisionOSPlatform.ts +60 -24
  278. package/src/platform-runtime.ts +13 -0
  279. package/src/reality/Attachment.ts +2 -2
  280. package/src/reality/entity/SpatialEntity.ts +127 -2
  281. package/src/reality/realityCreator.ts +15 -1
  282. package/src/reality/resource/SpatialTextureResource.ts +16 -0
  283. package/src/reality/resource/index.ts +1 -0
  284. package/src/runtime/WebSpatialRuntimeError.ts +16 -0
  285. package/src/runtime/capability-data.ts +213 -0
  286. package/src/runtime/contract-review.test.ts +44 -0
  287. package/src/runtime/index.ts +17 -0
  288. package/src/runtime/jsbAdapterPlatform.test.ts +36 -0
  289. package/src/runtime/jsbAdapterPlatform.ts +51 -0
  290. package/src/runtime/keys.ts +140 -0
  291. package/src/runtime/semver.ts +33 -0
  292. package/src/runtime/supports.test.ts +346 -0
  293. package/src/runtime/supports.ts +112 -0
  294. package/src/runtime/types.ts +11 -0
  295. package/src/runtime/useAnimation.capability.test.ts +275 -0
  296. package/src/runtime/userAgent.ts +64 -0
  297. package/src/scene-polyfill.manifest.test.ts +312 -9
  298. package/src/scene-polyfill.request-metadata.test.ts +39 -0
  299. package/src/scene-polyfill.test.ts +33 -94
  300. package/src/scene-polyfill.ts +125 -92
  301. package/src/spatial-host.ts +25 -0
  302. package/src/spatial-window-polyfill.ts +18 -2
  303. package/src/transform-roundtrip.test.ts +90 -0
  304. package/src/types/animation.ts +224 -0
  305. package/src/types/{global.d.ts → global.ts} +10 -12
  306. package/src/types/motion/index.ts +10 -0
  307. package/src/types/motion/spatializedElementMotion.ts +49 -0
  308. package/src/types/motion/spatializedMotion.ts +147 -0
  309. package/src/types/motion/spatializedPlayback.ts +17 -0
  310. package/src/types/motion/spatializedVisual.ts +24 -0
  311. package/src/types/types.ts +12 -0
  312. package/src/utils.ts +54 -0
  313. package/tsup.config.ts +61 -36
  314. package/vitest.config.ts +5 -1
  315. package/dist/iife/index.d.ts +0 -1101
  316. package/dist/iife/index.global.js +0 -75
  317. package/dist/iife/index.global.js.map +0 -1
  318. package/src/platform-adapter/android/AndroidPlatform.ts +0 -133
  319. package/src/platform-adapter/ssr/SSRPlatform.ts +0 -43
  320. /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/global.d'
14
-
15
- // side effects
16
- import { injectSceneHook } from './scene-polyfill'
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
- if (!isSSREnv() && navigator.userAgent.indexOf('WebSpatial/') > 0) {
23
- injectSceneHook()
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
- callWebSpatialProtocol: vi.fn(),
59
- callWebSpatialProtocolSync: vi.fn(),
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.callWebSpatialProtocol.mockReset()
86
- platformSpy.callWebSpatialProtocolSync.mockReset()
80
+ platformSpy.openSpatialSceneSync.mockReset()
81
+ platformSpy.createNativeSpatialDiv.mockReset()
82
+ platformSpy.createNativeAttachment.mockReset()
87
83
  platformSpy.callJSB.mockImplementation(() => ok({ id: 'id-1' }))
88
- platformSpy.callWebSpatialProtocol.mockImplementation(() =>
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.callWebSpatialProtocolSync.mockImplementation(() => ({
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
+ })