@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
@@ -0,0 +1,346 @@
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
2
+
3
+ describe('getRuntime / supports', () => {
4
+ beforeEach(() => {
5
+ vi.resetModules()
6
+ })
7
+
8
+ test('plain browser: no shell token → supports is false', async () => {
9
+ vi.stubGlobal('navigator', {
10
+ userAgent:
11
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
12
+ } as Navigator)
13
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
14
+ resetRuntimeCacheForTests()
15
+ expect(supports('Model')).toBe(false)
16
+ expect(supports('useMetrics')).toBe(false)
17
+ })
18
+
19
+ test('like Quest without Pico tokens does not force picoos (WSAppShell + no Mac → type null)', async () => {
20
+ vi.stubGlobal('navigator', {
21
+ userAgent:
22
+ 'Mozilla/5.0 (X11; Linux x86_64; unknown OS0.11.0 like Quest) AppleWebKit/537.36 WSAppShell/1.5.0 WebSpatial/1.5.0',
23
+ } as Navigator)
24
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
25
+ './supports'
26
+ )
27
+ resetRuntimeCacheForTests()
28
+ const rt = getRuntime()
29
+ expect(rt.type).toBe(null)
30
+ expect(rt.shellVersion).toBe('1.5.0')
31
+ expect(supports('Model')).toBe(false)
32
+ })
33
+
34
+ test('WSAppShell without Mac OS X: not visionos; spatial supports false', async () => {
35
+ vi.stubGlobal('navigator', {
36
+ userAgent:
37
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 WSAppShell/9.9.9 WebSpatial/1.5.0',
38
+ } as Navigator)
39
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
40
+ './supports'
41
+ )
42
+ resetRuntimeCacheForTests()
43
+ const rt = getRuntime()
44
+ expect(rt.type).toBe(null)
45
+ expect(rt.shellVersion).toBe('9.9.9')
46
+ expect(supports('Model')).toBe(false)
47
+ })
48
+
49
+ test('visionOS UA with WSAppShell: resolves and Model can be true (stub table)', async () => {
50
+ vi.stubGlobal('navigator', {
51
+ userAgent:
52
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 (KHTML, like Gecko) WSAppShell/1.5.0 WebSpatial/1.5.0 Safari/537.36',
53
+ } as Navigator)
54
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
55
+ './supports'
56
+ )
57
+ resetRuntimeCacheForTests()
58
+ const rt = getRuntime()
59
+ expect(rt.type).toBe('visionos')
60
+ expect(rt.shellVersion).toBe('1.5.0')
61
+ expect(supports('Model')).toBe(true)
62
+ expect(supports('xrInnerDepth')).toBe(true)
63
+ expect(supports('xrOuterDepth')).toBe(true)
64
+ expect(supports('UnknownThing' as any)).toBe(false)
65
+ })
66
+
67
+ test('pico UA: type picoos; matrix 0.1.1 sub-tokens (alpha2.0 baseline)', async () => {
68
+ vi.stubGlobal('navigator', {
69
+ userAgent:
70
+ 'Mozilla/5.0 (X11; Linux x86_64; unknown OS0.11.0 like Quest) AppleWebKit/537.36 PicoWebApp/0.1.1 (like PicoBrowser) Chrome/138.0 WebSpatial/1.5.0',
71
+ } as Navigator)
72
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
73
+ resetRuntimeCacheForTests()
74
+ expect(supports('initScene')).toBe(true)
75
+ expect(supports('Model', ['not-a-token' as any])).toBe(false)
76
+ expect(supports('Model', ['source'])).toBe(false)
77
+ expect(supports('Model', ['ready', 'currentSrc'])).toBe(true)
78
+ expect(supports('Model', ['stagemode'])).toBe(false)
79
+ expect(supports('WindowScene', ['defaultSize', 'resizability'])).toBe(true)
80
+ expect(supports('xrInnerDepth')).toBe(false)
81
+ expect(supports('xrOuterDepth')).toBe(false)
82
+ })
83
+
84
+ test('pico UA PicoWebApp/0.1.2: matrix playback row (beta2.0)', async () => {
85
+ vi.stubGlobal('navigator', {
86
+ userAgent:
87
+ 'Mozilla/5.0 (X11; Linux x86_64; unknown OS0.11.0 like Quest) AppleWebKit/537.36 PicoWebApp/0.1.2 (like PicoBrowser) Chrome/138.0 WebSpatial/1.5.0',
88
+ } as Navigator)
89
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
90
+ resetRuntimeCacheForTests()
91
+ expect(supports('Model', ['autoplay', 'loop', 'source'])).toBe(true)
92
+ expect(supports('Model', ['currentTime'])).toBe(true)
93
+ expect(supports('Model', ['poster'])).toBe(true)
94
+ expect(supports('Model', ['loading'])).toBe(false)
95
+ expect(supports('xrInnerDepth')).toBe(false)
96
+ expect(supports('xrOuterDepth')).toBe(false)
97
+ })
98
+
99
+ test('pico UA PicoWebApp/0.3.1: beta2.1 enables Model loading', async () => {
100
+ vi.stubGlobal('navigator', {
101
+ userAgent:
102
+ 'Mozilla/5.0 (X11; Linux x86_64; unknown OS0.11.0 like Quest) AppleWebKit/537.36 PicoWebApp/0.3.1 (like PicoBrowser) Chrome/138.0 WebSpatial/1.5.0',
103
+ } as Navigator)
104
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
105
+ resetRuntimeCacheForTests()
106
+ expect(supports('Model', ['currentTime', 'poster', 'loading'])).toBe(true)
107
+ expect(supports('Model', ['stagemode'])).toBe(false)
108
+ expect(supports('useAnimation')).toBe(false)
109
+ expect(supports('useAnimation', ['entity'])).toBe(false)
110
+ expect(supports('xrInnerDepth')).toBe(false)
111
+ expect(supports('xrOuterDepth')).toBe(false)
112
+ })
113
+
114
+ test('pico UA above PicoWebApp/0.3.1 keeps its capabilities before OTA0', async () => {
115
+ vi.stubGlobal('navigator', {
116
+ userAgent:
117
+ 'Mozilla/5.0 (X11; Linux x86_64; unknown OS0.11.0 like Quest) AppleWebKit/537.36 PicoWebApp/0.3.2 (like PicoBrowser) Chrome/138.0 WebSpatial/1.5.0',
118
+ } as Navigator)
119
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
120
+ resetRuntimeCacheForTests()
121
+ expect(supports('Model', ['currentTime', 'poster', 'loading'])).toBe(true)
122
+ expect(supports('useAnimation')).toBe(false)
123
+ expect(supports('useAnimation', ['entity'])).toBe(false)
124
+ })
125
+
126
+ test('alias Box → BoxEntity', async () => {
127
+ vi.stubGlobal('navigator', {
128
+ userAgent:
129
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 WSAppShell/1.5.0 WebSpatial/1.5.0',
130
+ } as Navigator)
131
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
132
+ resetRuntimeCacheForTests()
133
+ expect(supports('Box')).toBe(supports('BoxEntity'))
134
+ })
135
+
136
+ test('supports(name, []) ≡ supports(name)', async () => {
137
+ vi.stubGlobal('navigator', {
138
+ userAgent:
139
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 WSAppShell/1.5.0 WebSpatial/1.5.0',
140
+ } as Navigator)
141
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
142
+ resetRuntimeCacheForTests()
143
+ expect(supports('Reality')).toBe(supports('Reality', []))
144
+ })
145
+
146
+ test('initScene is top-level only; WindowScene / VolumeScene sub-tokens', async () => {
147
+ vi.stubGlobal('navigator', {
148
+ userAgent:
149
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 WSAppShell/1.5.0 WebSpatial/1.5.0',
150
+ } as Navigator)
151
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
152
+ resetRuntimeCacheForTests()
153
+ expect(supports('initScene')).toBe(true)
154
+ expect(supports('initScene', ['defaultSize' as any])).toBe(false)
155
+ expect(supports('WindowScene')).toBe(true)
156
+ expect(supports('WindowScene', ['defaultSize', 'resizability'])).toBe(true)
157
+ expect(supports('VolumeScene', ['baseplateVisibility'])).toBe(true)
158
+ })
159
+
160
+ test('Puppeteer UA: type puppeteer; all valid supports() true', async () => {
161
+ vi.stubGlobal('navigator', {
162
+ userAgent:
163
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Puppeteer WSAppShell/1.5.0 WebSpatial/1.5.0',
164
+ } as Navigator)
165
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
166
+ './supports'
167
+ )
168
+ resetRuntimeCacheForTests()
169
+ const rt = getRuntime()
170
+ expect(rt.type).toBe('puppeteer')
171
+ expect(rt.shellVersion).toBe('1.5.0')
172
+ expect(supports('Model')).toBe(true)
173
+ expect(supports('Model', ['poster'])).toBe(true)
174
+ expect(supports('UnknownThing' as any)).toBe(false)
175
+ expect(supports('Model', ['not-a-token' as any])).toBe(false)
176
+ })
177
+
178
+ test('Puppeteer UA without shell token: still puppeteer; valid supports() true', async () => {
179
+ vi.stubGlobal('navigator', {
180
+ userAgent: 'Mozilla/5.0 Puppeteer HeadlessChrome/120.0.0.0',
181
+ } as Navigator)
182
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
183
+ './supports'
184
+ )
185
+ resetRuntimeCacheForTests()
186
+ expect(getRuntime().type).toBe('puppeteer')
187
+ expect(getRuntime().shellVersion).toBe(null)
188
+ expect(supports('Model')).toBe(true)
189
+ })
190
+
191
+ test('visionOS WS_SHELL_VERSION placeholder: debug mode; all valid supports() true', async () => {
192
+ vi.stubGlobal('navigator', {
193
+ userAgent:
194
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/WS_SHELL_VERSION WebSpatial/1.5.0 Safari/537.36',
195
+ } as Navigator)
196
+ const { supports, getRuntime, resetRuntimeCacheForTests } = await import(
197
+ './supports'
198
+ )
199
+ resetRuntimeCacheForTests()
200
+ const rt = getRuntime()
201
+ expect(rt.type).toBe('visionos')
202
+ expect(rt.shellVersion).toBe('WS_SHELL_VERSION')
203
+ expect(supports('Model')).toBe(true)
204
+ expect(supports('Model', ['poster'])).toBe(true)
205
+ expect(supports('Model', ['stagemode'])).toBe(true)
206
+ expect(supports('UnknownThing' as any)).toBe(false)
207
+ expect(supports('Model', ['not-a-token' as any])).toBe(false)
208
+ })
209
+
210
+ test('visionOS WSAppShell/1.5.0: Model sub-tokens (alpha2.0 baseline)', async () => {
211
+ vi.stubGlobal('navigator', {
212
+ userAgent:
213
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 WSAppShell/1.5.0 WebSpatial/1.5.0',
214
+ } as Navigator)
215
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
216
+ resetRuntimeCacheForTests()
217
+ expect(supports('Model', ['autoplay'])).toBe(false)
218
+ expect(supports('Model', ['poster'])).toBe(false)
219
+ expect(supports('Model', ['ready', 'currentSrc'])).toBe(true)
220
+ expect(supports('Model', ['currentTime'])).toBe(false)
221
+ })
222
+
223
+ test('visionOS WSAppShell/1.6.0: Model playback row (WebSpatial April)', async () => {
224
+ vi.stubGlobal('navigator', {
225
+ userAgent:
226
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 (KHTML, like Gecko) WSAppShell/1.6.0 WebSpatial/1.5.0 Safari/537.36',
227
+ } as Navigator)
228
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
229
+ resetRuntimeCacheForTests()
230
+ expect(supports('Model', ['autoplay', 'loop', 'source'])).toBe(true)
231
+ expect(supports('Model', ['currentTime'])).toBe(false)
232
+ expect(supports('Model', ['poster'])).toBe(false)
233
+ expect(supports('Model', ['play', 'pause', 'duration'])).toBe(true)
234
+ })
235
+
236
+ test('visionOS WSAppShell/1.7.0: Model currentTime, loading, and poster are supported', async () => {
237
+ vi.stubGlobal('navigator', {
238
+ userAgent:
239
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 (KHTML, like Gecko) WSAppShell/1.7.0 WebSpatial/1.5.0 Safari/537.36',
240
+ } as Navigator)
241
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
242
+ resetRuntimeCacheForTests()
243
+ expect(supports('Model', ['currentTime', 'loading', 'poster'])).toBe(true)
244
+ expect(supports('Model', ['stagemode'])).toBe(false)
245
+ expect(supports('useAnimation')).toBe(false)
246
+ expect(supports('useAnimation', ['entity'])).toBe(false)
247
+ })
248
+
249
+ test('visionOS WSAppShell/1.8.0: useAnimation is true while sub-tokens are false', async () => {
250
+ vi.stubGlobal('navigator', {
251
+ userAgent:
252
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.8.0 WebSpatial/1.5.0 Safari/537.36',
253
+ } as Navigator)
254
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
255
+ resetRuntimeCacheForTests()
256
+ expect(supports('useAnimation')).toBe(true)
257
+ expect(supports('useAnimation', ['entity'])).toBe(false)
258
+ expect(supports('useAnimation', ['element'])).toBe(false)
259
+ })
260
+
261
+ test('useAnimation rejects all sub-tokens', async () => {
262
+ vi.stubGlobal('navigator', {
263
+ userAgent:
264
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.7.0 WebSpatial/1.5.0 Safari/537.36',
265
+ } as Navigator)
266
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
267
+ resetRuntimeCacheForTests()
268
+ expect(supports('useAnimation')).toBe(false)
269
+ expect(supports('useAnimation', ['entity'])).toBe(false)
270
+ expect(supports('useAnimation', ['element'])).toBe(false)
271
+ expect(supports('useAnimation', ['static3d'])).toBe(false)
272
+ expect(supports('useAnimation', ['dynamic3d'])).toBe(false)
273
+
274
+ vi.stubGlobal('navigator', {
275
+ userAgent:
276
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 WSAppShell/1.5.0 WebSpatial/1.5.0',
277
+ } as Navigator)
278
+ const mod2 = await import('./supports')
279
+ mod2.resetRuntimeCacheForTests()
280
+ expect(mod2.supports('useAnimation')).toBe(false)
281
+ expect(mod2.supports('useAnimation', ['entity'])).toBe(false)
282
+ expect(mod2.supports('useAnimation', ['element'])).toBe(false)
283
+ })
284
+
285
+ test('useAnimation result is stable across repeated calls', async () => {
286
+ vi.stubGlobal('navigator', {
287
+ userAgent:
288
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.8.0 WebSpatial/1.5.0 Safari/537.36',
289
+ } as Navigator)
290
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
291
+ resetRuntimeCacheForTests()
292
+ const first = supports('useAnimation')
293
+ const second = supports('useAnimation')
294
+ const third = supports('useAnimation')
295
+ expect(first).toBe(true)
296
+ expect(second).toBe(true)
297
+ expect(third).toBe(true)
298
+ })
299
+ })
300
+
301
+ describe('supports("useAnimation") for motion', () => {
302
+ afterEach(() => {
303
+ vi.unstubAllGlobals()
304
+ })
305
+
306
+ test('visionOS WSAppShell/1.8.0: useAnimation is true while sub-tokens are false', async () => {
307
+ vi.stubGlobal('navigator', {
308
+ userAgent:
309
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.8.0 WebSpatial/1.5.0 Safari/537.36',
310
+ } as Navigator)
311
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
312
+ resetRuntimeCacheForTests()
313
+ expect(supports('useAnimation')).toBe(true)
314
+ expect(supports('useAnimation', [])).toBe(true)
315
+ expect(supports('useAnimation', ['entity'])).toBe(false)
316
+ expect(supports('useAnimation', ['element'])).toBe(false)
317
+ expect(supports('useAnimation', ['static3d'])).toBe(false)
318
+ expect(supports('useAnimation', ['dynamic3d'])).toBe(false)
319
+ })
320
+
321
+ test('visionOS WSAppShell/1.7.0: useAnimation and sub-tokens are false', async () => {
322
+ vi.stubGlobal('navigator', {
323
+ userAgent:
324
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.7.0 WebSpatial/1.5.0 Safari/537.36',
325
+ } as Navigator)
326
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
327
+ resetRuntimeCacheForTests()
328
+ expect(supports('useAnimation')).toBe(false)
329
+ expect(supports('useAnimation', [])).toBe(false)
330
+ expect(supports('useAnimation', ['entity'])).toBe(false)
331
+ expect(supports('useAnimation', ['element'])).toBe(false)
332
+ expect(supports('useAnimation', ['static3d'])).toBe(false)
333
+ expect(supports('useAnimation', ['dynamic3d'])).toBe(false)
334
+ })
335
+
336
+ test('visionOS WSAppShell/1.6.0: element token remains false', async () => {
337
+ vi.stubGlobal('navigator', {
338
+ userAgent:
339
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7; wv) AppleWebKit/605.1.15 WSAppShell/1.6.0 WebSpatial/1.5.0 Safari/537.36',
340
+ } as Navigator)
341
+ const { supports, resetRuntimeCacheForTests } = await import('./supports')
342
+ resetRuntimeCacheForTests()
343
+ expect(supports('useAnimation')).toBe(false)
344
+ expect(supports('useAnimation', ['element'])).toBe(false)
345
+ })
346
+ })
@@ -0,0 +1,112 @@
1
+ import { CAPABILITY_TABLE, type CapabilityVersionRow } from './capability-data'
2
+ import { compareSemver, parseSemverOrNull } from './semver'
3
+ import type { WebSpatialRuntimeSnapshot } from './types'
4
+ import { computeRuntimeFromUserAgent } from './userAgent'
5
+ import {
6
+ type CapabilityKey,
7
+ isKnownSubToken,
8
+ isKnownTopLevel,
9
+ normalizeCapabilityName,
10
+ } from './keys'
11
+
12
+ /**
13
+ * Unsubstituted shell version placeholder from visionOS manifest / Xcode template
14
+ * (`manifest.swift`). When still present in `WSAppShell/<version>`, treat as debug
15
+ * build: {@link supports} returns true for every valid capability query.
16
+ */
17
+ export const VISIONOS_DEBUG_SHELL_VERSION_PLACEHOLDER = 'WS_SHELL_VERSION'
18
+
19
+ let runtimeCache: WebSpatialRuntimeSnapshot | undefined
20
+
21
+ /** Test helper: clear cached UA/runtime snapshot between Vitest cases. */
22
+ export function resetRuntimeCacheForTests(): void {
23
+ runtimeCache = undefined
24
+ }
25
+
26
+ /**
27
+ * Internal runtime snapshot (not part of the public `@webspatial/react-sdk` surface).
28
+ */
29
+ export function getRuntime(): WebSpatialRuntimeSnapshot {
30
+ if (runtimeCache !== undefined) return runtimeCache
31
+ if (typeof navigator === 'undefined') {
32
+ runtimeCache = { type: null, shellVersion: null }
33
+ return runtimeCache
34
+ }
35
+ runtimeCache = computeRuntimeFromUserAgent(navigator.userAgent)
36
+ return runtimeCache
37
+ }
38
+
39
+ function selectRow(
40
+ type: 'visionos' | 'picoos',
41
+ shellVersion: string,
42
+ ): CapabilityVersionRow | null {
43
+ const norm = parseSemverOrNull(shellVersion)
44
+ if (!norm) return null
45
+ const rows = CAPABILITY_TABLE[type]
46
+ if (!rows.length) return null
47
+ const sorted = [...rows].sort((a, b) => compareSemver(a.version, b.version))
48
+ const minV = sorted[0].version
49
+ if (compareSemver(norm, minV) < 0) return null
50
+
51
+ let chosen: CapabilityVersionRow | null = null
52
+ for (const row of sorted) {
53
+ if (compareSemver(row.version, norm) <= 0) {
54
+ chosen = row
55
+ } else {
56
+ break
57
+ }
58
+ }
59
+ return chosen
60
+ }
61
+
62
+ /**
63
+ * Public capability probe (`WebSpatialRuntime.supports` re-exports this from React SDK).
64
+ */
65
+ export function supports(
66
+ name: CapabilityKey,
67
+ tokens?: readonly string[],
68
+ ): boolean
69
+ export function supports(name: string, tokens?: readonly string[]): boolean
70
+ export function supports(name: string, tokens?: readonly string[]): boolean {
71
+ if (typeof name !== 'string') return false
72
+ const canonical = normalizeCapabilityName(name)
73
+ if (!isKnownTopLevel(canonical)) return false
74
+
75
+ const tokList =
76
+ tokens === undefined ? [] : Array.isArray(tokens) ? [...tokens] : []
77
+ if (tokList.some(t => typeof t !== 'string')) return false
78
+
79
+ for (const t of tokList) {
80
+ if (!isKnownSubToken(canonical, t)) return false
81
+ }
82
+
83
+ const rt = getRuntime()
84
+ if (rt.type === 'puppeteer') {
85
+ return true
86
+ }
87
+ if (rt.type === null) return false
88
+ if (rt.shellVersion === null) return false
89
+
90
+ if (
91
+ rt.type === 'visionos' &&
92
+ rt.shellVersion === VISIONOS_DEBUG_SHELL_VERSION_PLACEHOLDER
93
+ ) {
94
+ return true
95
+ }
96
+
97
+ const parsedShell = parseSemverOrNull(rt.shellVersion)
98
+ if (!parsedShell) return false
99
+
100
+ if (rt.type !== 'visionos' && rt.type !== 'picoos') return false
101
+
102
+ const row = selectRow(rt.type, parsedShell)
103
+ if (!row) return false
104
+
105
+ if (tokList.length === 0) {
106
+ return row.flags[canonical] === true
107
+ }
108
+ if (row.flags[canonical] !== true) {
109
+ return false
110
+ }
111
+ return tokList.every(t => row.flags[`${canonical}:${t}`] === true)
112
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Internal runtime classification (see OpenSpec `review.md`).
3
+ * `puppeteer`: automation / test harness UA — not a product matrix row; see `supports()`.
4
+ */
5
+ export type WebSpatialRuntimeType = 'visionos' | 'picoos' | 'puppeteer' | null
6
+
7
+ /** Snapshot returned by internal `getRuntime()` (not a public app API). */
8
+ export type WebSpatialRuntimeSnapshot = {
9
+ type: WebSpatialRuntimeType
10
+ shellVersion: string | null
11
+ }