@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,39 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ describe('scene polyfill request metadata forwarding', () => {
4
+ afterEach(() => {
5
+ vi.restoreAllMocks()
6
+ vi.resetModules()
7
+ delete (window as any).webSpatial
8
+ delete (window as any).__webspatialShell__
9
+ })
10
+
11
+ it.each(['createSpatialized2DElement', 'createAttachment'])(
12
+ 'forwards rid and wsepoch for %s without introducing extra request fields',
13
+ async command => {
14
+ ;(window as any).webSpatial = {
15
+ genToken: vi.fn(() => 'token-123'),
16
+ }
17
+ const originalOpen = vi.fn(() => null)
18
+ vi.spyOn(window, 'open').mockImplementation(originalOpen)
19
+
20
+ const { hijackWindowOpen } = await import('./scene-polyfill')
21
+ hijackWindowOpen(window)
22
+
23
+ window.open(`webspatial://${command}?rid=req_1&wsepoch=9`, '_blank')
24
+
25
+ expect(originalOpen).toHaveBeenCalled()
26
+ const redirectedUrl = (originalOpen.mock.calls as unknown[][])[0]?.[0]
27
+ expect(typeof redirectedUrl).toBe('string')
28
+ const redirected = new URL(String(redirectedUrl))
29
+ if (redirected.protocol === 'webspatial:') {
30
+ expect(redirected.host).toBe(command)
31
+ } else {
32
+ expect(redirected.searchParams.get('command')).toBe(command)
33
+ }
34
+ expect(redirected.searchParams.get('rid')).toBe('req_1')
35
+ expect(redirected.searchParams.get('wsepoch')).toBe('9')
36
+ expect(redirected.searchParams.get('wsrid')).toBeNull()
37
+ },
38
+ )
39
+ })
@@ -265,93 +265,51 @@ describe('test formatSceneConfig in volume', () => {
265
265
 
266
266
  vi.mock('./JSBCommand', () => {
267
267
  return {
268
- GetSpatialSceneState: vi.fn().mockImplementation(() => ({
269
- execute: vi.fn().mockResolvedValue({ data: { name: 'pending' } }),
270
- })),
271
- UpdateSceneConfig: vi.fn().mockImplementation(() => ({
272
- execute: vi.fn().mockResolvedValue(undefined),
273
- })),
274
- UpdateSpatialSceneProperties: vi.fn().mockImplementation(() => ({
275
- execute: vi.fn().mockResolvedValue(undefined),
276
- })),
277
- AddSpatializedElementToSpatialScene: vi.fn().mockImplementation(() => ({
278
- execute: vi.fn().mockResolvedValue(undefined),
279
- })),
268
+ GetSpatialSceneState: vi.fn().mockImplementation(function () {
269
+ return {
270
+ execute: vi.fn().mockResolvedValue({ data: { name: 'pending' } }),
271
+ }
272
+ }),
273
+ UpdateSceneConfig: vi.fn().mockImplementation(function () {
274
+ return {
275
+ execute: vi.fn().mockResolvedValue(undefined),
276
+ }
277
+ }),
278
+ UpdateSpatialSceneProperties: vi.fn().mockImplementation(function () {
279
+ return {
280
+ execute: vi.fn().mockResolvedValue(undefined),
281
+ }
282
+ }),
283
+ AddSpatializedElementToSpatialScene: vi
284
+ .fn()
285
+ .mockImplementation(function () {
286
+ return {
287
+ execute: vi.fn().mockResolvedValue(undefined),
288
+ }
289
+ }),
280
290
  }
281
291
  })
282
292
 
283
- describe('injectScenePolyfill should call xrCurrentSceneDefaults and update scene config', () => {
293
+ describe('removed scene window globals', () => {
284
294
  beforeEach(() => {
285
295
  ;(window as any).opener = {}
286
296
  })
287
297
 
288
- it('with no type', async () => {
289
- vi.useFakeTimers()
290
-
291
- const mockFn = vi
292
- .fn()
293
- .mockResolvedValue({ defaultSize: { width: 800, height: 600 } })
294
- window.xrCurrentSceneDefaults = mockFn
295
-
296
- injectSceneHook()
297
-
298
- document.dispatchEvent(new Event('DOMContentLoaded'))
299
-
300
- await vi.runAllTimersAsync()
301
-
302
- expect(mockFn).toHaveBeenCalledWith(
303
- expect.objectContaining({ defaultSize: { width: 1280, height: 720 } }),
304
- )
305
-
306
- // verify UpdateSceneConfig.execute
307
- const { UpdateSceneConfig } = await import('./JSBCommand')
308
- expect(UpdateSceneConfig).toHaveBeenCalledWith({
309
- type: 'window',
310
- defaultSize: { width: 800, height: 600 },
311
- })
298
+ it('does not type removed window scene globals', () => {
299
+ // @ts-expect-error removed undocumented scene global
300
+ void window.xrCurrentSceneDefaults
301
+ // @ts-expect-error removed undocumented scene global
302
+ void window.xrCurrentSceneType
312
303
  })
313
304
 
314
- it('with window type', async () => {
305
+ it('does not use opened-page globals to override pending scene config', async () => {
315
306
  vi.useFakeTimers()
316
307
 
317
308
  const mockFn = vi
318
309
  .fn()
319
310
  .mockResolvedValue({ defaultSize: { width: 800, height: 600 } })
320
- window.xrCurrentSceneDefaults = mockFn
321
- window.xrCurrentSceneType = 'window'
322
-
323
- injectSceneHook()
324
-
325
- document.dispatchEvent(new Event('DOMContentLoaded'))
326
-
327
- await vi.runAllTimersAsync()
328
-
329
- expect(mockFn).toHaveBeenCalledWith(
330
- expect.objectContaining({ defaultSize: { width: 1280, height: 720 } }),
331
- )
332
-
333
- // verify UpdateSceneConfig.execute
334
- const { UpdateSceneConfig } = await import('./JSBCommand')
335
- expect(UpdateSceneConfig).toHaveBeenCalledWith({
336
- type: 'window',
337
- defaultSize: { width: 800, height: 600 },
338
- })
339
- })
340
-
341
- it('with volume type', async () => {
342
- vi.useFakeTimers()
343
-
344
- const mockFn = vi.fn().mockResolvedValue({
345
- defaultSize: { width: 1, height: 1, depth: 1 },
346
- resizability: {
347
- minWidth: 0.5,
348
- minHeight: 1,
349
- maxWidth: 0.5,
350
- maxHeight: 1,
351
- },
352
- })
353
- window.xrCurrentSceneDefaults = mockFn
354
- window.xrCurrentSceneType = 'volume'
311
+ ;(window as any).xrCurrentSceneDefaults = mockFn
312
+ ;(window as any).xrCurrentSceneType = 'volume'
355
313
 
356
314
  injectSceneHook()
357
315
 
@@ -359,28 +317,9 @@ describe('injectScenePolyfill should call xrCurrentSceneDefaults and update scen
359
317
 
360
318
  await vi.runAllTimersAsync()
361
319
 
362
- expect(mockFn).toHaveBeenCalledWith(
363
- expect.objectContaining({
364
- defaultSize: { width: '0.94m', height: '0.94m', depth: '0.94m' },
365
- }),
366
- )
367
-
368
- // verify UpdateSceneConfig.execute
369
320
  const { UpdateSceneConfig } = await import('./JSBCommand')
370
- expect(UpdateSceneConfig).toHaveBeenCalledWith({
371
- type: 'volume',
372
- defaultSize: {
373
- width: pointToPhysical(1),
374
- height: pointToPhysical(1),
375
- depth: pointToPhysical(1),
376
- },
377
- resizability: {
378
- minWidth: 0.5,
379
- minHeight: 1,
380
- maxWidth: 0.5,
381
- maxHeight: 1,
382
- },
383
- })
321
+ expect(mockFn).not.toHaveBeenCalled()
322
+ expect(UpdateSceneConfig).not.toHaveBeenCalled()
384
323
  })
385
324
  })
386
325
 
@@ -1,9 +1,8 @@
1
- import { createSpatialSceneCommand, FocusScene } from './JSBCommand'
2
- import { SpatialScene } from './SpatialScene'
1
+ import { FocusScene } from './JSBCommand'
2
+ import { openSpatialSceneSync } from './spatial-host'
3
3
  import {
4
4
  SpatialSceneCreationOptions,
5
5
  SpatialSceneType,
6
- SpatialSceneState,
7
6
  isValidSceneUnit,
8
7
  isValidSpatialSceneType,
9
8
  isValidWorldScalingType,
@@ -73,33 +72,93 @@ function deepMergePlain<
73
72
  return out
74
73
  }
75
74
 
75
+ /**
76
+ * Pick a value from a manifest-like object where both snake_case and camelCase may exist.
77
+ *
78
+ * Rule: if both keys exist on the same object, snake_case wins.
79
+ */
80
+ function preferSnake<T>(
81
+ src: Record<string, any> | undefined,
82
+ snakeKey: string,
83
+ camelKey: string,
84
+ ): T | undefined {
85
+ if (!src) return undefined
86
+ if (src[snakeKey] !== undefined) return src[snakeKey] as T
87
+ if (src[camelKey] !== undefined) return src[camelKey] as T
88
+ return undefined
89
+ }
90
+
91
+ /**
92
+ * Normalize resizability keys from manifest input into the runtime camelCase shape.
93
+ *
94
+ * Accepts both:
95
+ * - camelCase: minWidth/minHeight/maxWidth/maxHeight
96
+ * - snake_case: min_width/min_height/max_width/max_height
97
+ *
98
+ * Rule: if both exist on the same object, snake_case wins.
99
+ */
100
+ function normalizeResizability(
101
+ src: Record<string, any> | undefined,
102
+ ): SpatialSceneCreationOptions['resizability'] | undefined {
103
+ if (!src || typeof src !== 'object' || Array.isArray(src)) return undefined
104
+ const out: any = {}
105
+ const minWidth = preferSnake<any>(src, 'min_width', 'minWidth')
106
+ if (minWidth !== undefined) out.minWidth = minWidth
107
+ const minHeight = preferSnake<any>(src, 'min_height', 'minHeight')
108
+ if (minHeight !== undefined) out.minHeight = minHeight
109
+ const maxWidth = preferSnake<any>(src, 'max_width', 'maxWidth')
110
+ if (maxWidth !== undefined) out.maxWidth = maxWidth
111
+ const maxHeight = preferSnake<any>(src, 'max_height', 'maxHeight')
112
+ if (maxHeight !== undefined) out.maxHeight = maxHeight
113
+ return Object.keys(out).length > 0 ? out : undefined
114
+ }
115
+
76
116
  /**
77
117
  * Normalize XRSpatialSceneDefaults (manifest shape) into SpatialSceneCreationOptions (runtime shape).
78
- * - Only remap default_size -> defaultSize when present.
79
- * - Leaves other keys (resizability, worldScaling, etc.) unchanged.
80
- * - Units are left as-is; downstream formatting is handled by formatSceneConfig.
118
+ *
119
+ * - Remaps supported snake_case aliases into camelCase keys.
120
+ * - Keeps output camelCase-only (snake_case keys are treated as input aliases).
121
+ * - Units are not formatted here; downstream formatting is handled by formatSceneConfig.
122
+ *
123
+ * Rule: if both snake_case and camelCase are present on the same object, snake_case wins.
81
124
  */
82
125
  function normalizeXRDefaultsToSceneOptions(
83
126
  src: XRSpatialSceneDefaults | Record<string, any>,
84
127
  ): SpatialSceneCreationOptions {
85
- const out: any = { ...(src || {}) }
86
- const ds =
87
- (src as any).defaultSize !== undefined
88
- ? (src as any).defaultSize
89
- : (src as any).default_size
90
- if (ds !== undefined) {
91
- out.defaultSize = ds
92
- }
93
- if ('default_size' in out) {
94
- delete out.default_size
95
- }
128
+ const input: any = src || {}
129
+ const out: any = {}
130
+
131
+ const ds = preferSnake<any>(input, 'default_size', 'defaultSize')
132
+ if (ds !== undefined) out.defaultSize = ds
133
+
134
+ const res = input.resizability
135
+ const normalizedRes = normalizeResizability(res)
136
+ if (normalizedRes) out.resizability = normalizedRes
137
+
138
+ const worldScaling = preferSnake<any>(input, 'world_scaling', 'worldScaling')
139
+ if (worldScaling !== undefined) out.worldScaling = worldScaling
140
+
141
+ const worldAlignment = preferSnake<any>(
142
+ input,
143
+ 'world_alignment',
144
+ 'worldAlignment',
145
+ )
146
+ if (worldAlignment !== undefined) out.worldAlignment = worldAlignment
147
+
148
+ const baseplateVisibility = preferSnake<any>(
149
+ input,
150
+ 'baseplate_visibility',
151
+ 'baseplateVisibility',
152
+ )
153
+ if (baseplateVisibility !== undefined)
154
+ out.baseplateVisibility = baseplateVisibility
155
+
96
156
  return out
97
157
  }
98
158
 
99
159
  class SceneManager {
100
160
  private originalOpen: any
101
161
  private static instance: SceneManager
102
- private manifestReady: Promise<void> | null = null
103
162
  static getInstance() {
104
163
  if (!SceneManager.instance) {
105
164
  SceneManager.instance = new SceneManager()
@@ -108,7 +167,9 @@ class SceneManager {
108
167
  }
109
168
 
110
169
  init(window: WindowProxy) {
111
- this.manifestReady = this.setupManifest()
170
+ // Load manifest defaults in the background; scene creation reads the
171
+ // resolved xr_window_defaults / xr_volume_defaults once available.
172
+ void this.setupManifest()
112
173
  this.originalOpen = window.open.bind(window)
113
174
  ;(window as any).open = this.open
114
175
  }
@@ -124,10 +185,6 @@ class SceneManager {
124
185
  return this.configMap[name]
125
186
  }
126
187
 
127
- waitManifest(): Promise<void> {
128
- return this.manifestReady ?? Promise.resolve()
129
- }
130
-
131
188
  // Ensure URL is absolute; only convert when a relative path is provided
132
189
  // - Keep external and special schemes untouched (http, https, data, blob, about, file, mailto, etc.)
133
190
  // - Handle protocol-relative URLs (//example.com/path)
@@ -157,12 +214,41 @@ class SceneManager {
157
214
  const xr = manifest?.xr_spatial_scene
158
215
  if (!xr || typeof xr !== 'object') return
159
216
  const { overrides, ...topLevel } = xr as XRSpatialSceneConfig
160
- // Merge top-level defaults with per-scene overrides.
161
- const windowRaw = deepMergePlain(topLevel, overrides?.window_scene)
162
- const volumeRaw = deepMergePlain(topLevel, overrides?.volume_scene)
163
- const windowNext = normalizeXRDefaultsToSceneOptions(windowRaw)
164
217
 
165
- const volumeNext = normalizeXRDefaultsToSceneOptions(volumeRaw)
218
+ // Normalize top-level defaults and per-type overrides separately, then deep-merge.
219
+ // - Ensures overrides keep higher priority than top-level defaults.
220
+ // - Ensures snake_case vs camelCase resolution is applied within the same object only.
221
+ // - This affects only manifest-resolved defaults; initScene callback chaining remains unchanged
222
+ // because callbackReturnMap stores the raw callback return values.
223
+ const normalizedTopLevel = normalizeXRDefaultsToSceneOptions(
224
+ topLevel as any,
225
+ )
226
+ const overrideObj: any = overrides as any
227
+ const windowOverrideSrc = preferSnake<any>(
228
+ overrideObj,
229
+ 'window_scene',
230
+ 'windowScene',
231
+ )
232
+ const volumeOverrideSrc = preferSnake<any>(
233
+ overrideObj,
234
+ 'volume_scene',
235
+ 'volumeScene',
236
+ )
237
+ const normalizedWindowOverride = windowOverrideSrc
238
+ ? normalizeXRDefaultsToSceneOptions(windowOverrideSrc)
239
+ : undefined
240
+ const normalizedVolumeOverride = volumeOverrideSrc
241
+ ? normalizeXRDefaultsToSceneOptions(volumeOverrideSrc)
242
+ : undefined
243
+
244
+ const windowNext = deepMergePlain(
245
+ normalizedTopLevel,
246
+ normalizedWindowOverride,
247
+ )
248
+ const volumeNext = deepMergePlain(
249
+ normalizedTopLevel,
250
+ normalizedVolumeOverride,
251
+ )
166
252
  if (windowNext && Object.keys(windowNext).length > 0) {
167
253
  xr_window_defaults = windowNext
168
254
  }
@@ -193,9 +279,12 @@ class SceneManager {
193
279
  const host = window.location.host
194
280
  const protocol = window.location.protocol
195
281
  const finalURL = `${protocol}//${host}/${token}/?command=${command}`
196
- const rid = new URL(url).searchParams.get('rid')
282
+ const sourceParams = new URL(url).searchParams
197
283
  const final = new URL(finalURL)
198
- if (rid) final.searchParams.set('rid', rid)
284
+ for (const key of ['rid', 'wsepoch']) {
285
+ const value = sourceParams.get(key)
286
+ if (value) final.searchParams.set(key, value)
287
+ }
199
288
  return this.originalOpen(final.toString(), target, features)
200
289
  }
201
290
  }
@@ -221,8 +310,12 @@ class SceneManager {
221
310
  cfg = { ...ans, type: 'window' }
222
311
  }
223
312
 
224
- const cmd = new createSpatialSceneCommand(url!, cfg, target, features)
225
- const result = cmd.executeSync()
313
+ const result = openSpatialSceneSync(
314
+ url!,
315
+ cfg as SpatialSceneCreationOptionsInternal,
316
+ target,
317
+ features,
318
+ )
226
319
 
227
320
  const id = result.data?.id
228
321
 
@@ -429,8 +522,6 @@ export function formatSceneConfig(
429
522
  // defaultSize should format into px if window
430
523
  // defaultSize should format into m if volume
431
524
 
432
- const defaultSceneConfig = getSceneDefaultConfig(sceneType)
433
-
434
525
  const errors: string[] = []
435
526
 
436
527
  const isWindow = sceneType === 'window'
@@ -567,65 +658,7 @@ function getSceneDefaultConfig(sceneType: SpatialSceneType) {
567
658
  : xr_volume_defaults || defaultSceneConfigVolume
568
659
  }
569
660
 
570
- async function injectScenePolyfill() {
571
- if (!window.opener) return
572
-
573
- const state = await SpatialScene.getInstance().getState()
574
-
575
- // only run this in pending state
576
- if (state !== SpatialSceneState.pending) return
577
-
578
- function onContentLoaded(callback: any) {
579
- if (
580
- document.readyState === 'interactive' ||
581
- document.readyState === 'complete'
582
- ) {
583
- callback()
584
- } else {
585
- document.addEventListener('DOMContentLoaded', callback)
586
- }
587
- }
588
-
589
- onContentLoaded(async () => {
590
- await SceneManager.getInstance().waitManifest()
591
- const sceneType = window.xrCurrentSceneType ?? 'window'
592
- const rawDefault = getSceneDefaultConfig(sceneType)
593
- // Provide a formatted 'pre' to the callback for consistent units and types.
594
- const pre = deepCloneJSON(rawDefault)
595
-
596
- let cfg = pre
597
- if (typeof window.xrCurrentSceneDefaults === 'function') {
598
- try {
599
- cfg = await window.xrCurrentSceneDefaults?.(pre)
600
- } catch (error) {
601
- console.error(error)
602
- }
603
- }
604
- // fixme: this duration is too short so that hide and show is at racing, so add a little delay to avoid
605
- await new Promise((resolve, reject) => {
606
- setTimeout(() => {
607
- resolve(null)
608
- }, 1000)
609
- })
610
-
611
- // Merge callback return with base defaults to ensure missing fields are filled.
612
- const mergedCfg = deepMergePlain(deepCloneJSON(rawDefault), cfg)
613
- const [formattedConfig, errors] = formatSceneConfig(mergedCfg, sceneType)
614
- if (errors.length > 0) {
615
- console.warn(
616
- `window.xrCurrentSceneDefaults with errors: ${errors.join(', ')}`,
617
- )
618
- }
619
- const finalCfg = {
620
- ...formattedConfig,
621
- type: sceneType,
622
- }
623
- await SpatialScene.getInstance().updateSceneCreationConfig(finalCfg)
624
- })
625
- }
626
-
627
661
  export function injectSceneHook() {
628
662
  hijackWindowOpen(window)
629
663
  hijackWindowATag(window)
630
- injectScenePolyfill()
631
664
  }
@@ -0,0 +1,25 @@
1
+ import type { WebSpatialProtocolResult } from './platform-adapter/interface'
2
+ import { getPlatform, getPlatformSync } from './platform-runtime'
3
+ import type { SpatialSceneCreationOptionsInternal } from './types/internal'
4
+ import type { AttachmentEntityOptions } from './types/types'
5
+
6
+ export async function createNativeSpatialDiv(): Promise<WebSpatialProtocolResult> {
7
+ const platform = await getPlatform()
8
+ return platform.createNativeSpatialDiv()
9
+ }
10
+
11
+ export async function createNativeAttachment(
12
+ options: AttachmentEntityOptions,
13
+ ): Promise<WebSpatialProtocolResult> {
14
+ const platform = await getPlatform()
15
+ return platform.createNativeAttachment(options)
16
+ }
17
+
18
+ export function openSpatialSceneSync(
19
+ url: string,
20
+ config: SpatialSceneCreationOptionsInternal | undefined,
21
+ target?: string,
22
+ features?: string,
23
+ ): WebSpatialProtocolResult {
24
+ return getPlatformSync().openSpatialSceneSync(url, config, target, features)
25
+ }
@@ -78,10 +78,20 @@ function hijackDocumentElementStyle() {
78
78
  const rawDocumentStyle = document.documentElement.style
79
79
  const styleProxy = new Proxy(rawDocumentStyle, {
80
80
  set: function (target, key, value) {
81
- const ret = Reflect.set(target, key, value)
81
+ let ret: boolean
82
82
 
83
83
  if (key === SpatialGlobalCustomVars.backgroundMaterial) {
84
- setCurrentWindowStyle(value)
84
+ target.setProperty(
85
+ SpatialGlobalCustomVars.backgroundMaterial,
86
+ String(value),
87
+ )
88
+ ret = true
89
+ } else {
90
+ ret = Reflect.set(target, key, value)
91
+ }
92
+
93
+ if (key === SpatialGlobalCustomVars.backgroundMaterial) {
94
+ setCurrentWindowStyle(String(value))
85
95
  }
86
96
 
87
97
  if (
@@ -106,6 +116,12 @@ function hijackDocumentElementStyle() {
106
116
  return ret
107
117
  },
108
118
  get: function (target, prop: string) {
119
+ if (prop === SpatialGlobalCustomVars.backgroundMaterial) {
120
+ return target.getPropertyValue(
121
+ SpatialGlobalCustomVars.backgroundMaterial,
122
+ )
123
+ }
124
+
109
125
  if (typeof target[prop as keyof CSSStyleDeclaration] === 'function') {
110
126
  return function (this: any, ...args: any[]) {
111
127
  if (prop === 'setProperty') {
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { composeSRT, decomposeSRT } from './utils'
3
+
4
+ function approxEqual(a: number, b: number, eps = 1e-6) {
5
+ return Math.abs(a - b) < eps
6
+ }
7
+
8
+ describe('composeSRT / decomposeSRT round-trip', () => {
9
+ test('identity transform round-trips', () => {
10
+ const pos = { x: 0, y: 0, z: 0 }
11
+ const rot = { x: 0, y: 0, z: 0 }
12
+ const scl = { x: 1, y: 1, z: 1 }
13
+
14
+ const matrix = composeSRT(pos, rot, scl)
15
+ const arr = Array.from(matrix.toFloat64Array())
16
+ const result = decomposeSRT(arr)
17
+
18
+ expect(approxEqual(result.position!.x, 0)).toBe(true)
19
+ expect(approxEqual(result.position!.y, 0)).toBe(true)
20
+ expect(approxEqual(result.position!.z, 0)).toBe(true)
21
+ expect(approxEqual(result.scale!.x, 1)).toBe(true)
22
+ expect(approxEqual(result.scale!.y, 1)).toBe(true)
23
+ expect(approxEqual(result.scale!.z, 1)).toBe(true)
24
+ expect(approxEqual(result.rotation!.x, 0)).toBe(true)
25
+ expect(approxEqual(result.rotation!.y, 0)).toBe(true)
26
+ expect(approxEqual(result.rotation!.z, 0)).toBe(true)
27
+ })
28
+
29
+ test('translation-only round-trips', () => {
30
+ const pos = { x: 3, y: -5, z: 10 }
31
+ const rot = { x: 0, y: 0, z: 0 }
32
+ const scl = { x: 1, y: 1, z: 1 }
33
+
34
+ const matrix = composeSRT(pos, rot, scl)
35
+ const arr = Array.from(matrix.toFloat64Array())
36
+ const result = decomposeSRT(arr)
37
+
38
+ expect(approxEqual(result.position!.x, 3)).toBe(true)
39
+ expect(approxEqual(result.position!.y, -5)).toBe(true)
40
+ expect(approxEqual(result.position!.z, 10)).toBe(true)
41
+ })
42
+
43
+ test('scale-only round-trips', () => {
44
+ const pos = { x: 0, y: 0, z: 0 }
45
+ const rot = { x: 0, y: 0, z: 0 }
46
+ const scl = { x: 2, y: 0.5, z: 3 }
47
+
48
+ const matrix = composeSRT(pos, rot, scl)
49
+ const arr = Array.from(matrix.toFloat64Array())
50
+ const result = decomposeSRT(arr)
51
+
52
+ expect(approxEqual(result.scale!.x, 2)).toBe(true)
53
+ expect(approxEqual(result.scale!.y, 0.5)).toBe(true)
54
+ expect(approxEqual(result.scale!.z, 3)).toBe(true)
55
+ })
56
+
57
+ test('rotation-only round-trips (45 deg Y)', () => {
58
+ const pos = { x: 0, y: 0, z: 0 }
59
+ const rot = { x: 0, y: 45, z: 0 }
60
+ const scl = { x: 1, y: 1, z: 1 }
61
+
62
+ const matrix = composeSRT(pos, rot, scl)
63
+ const arr = Array.from(matrix.toFloat64Array())
64
+ const result = decomposeSRT(arr)
65
+
66
+ expect(approxEqual(result.rotation!.x, 0)).toBe(true)
67
+ expect(approxEqual(result.rotation!.y, 45)).toBe(true)
68
+ expect(approxEqual(result.rotation!.z, 0)).toBe(true)
69
+ })
70
+
71
+ test('combined SRT round-trips', () => {
72
+ const pos = { x: 1, y: 2, z: 3 }
73
+ const rot = { x: 30, y: 45, z: 60 }
74
+ const scl = { x: 2, y: 3, z: 0.5 }
75
+
76
+ const matrix = composeSRT(pos, rot, scl)
77
+ const arr = Array.from(matrix.toFloat64Array())
78
+ const result = decomposeSRT(arr)
79
+
80
+ expect(approxEqual(result.position!.x, 1)).toBe(true)
81
+ expect(approxEqual(result.position!.y, 2)).toBe(true)
82
+ expect(approxEqual(result.position!.z, 3)).toBe(true)
83
+ expect(approxEqual(result.scale!.x, 2, 1e-4)).toBe(true)
84
+ expect(approxEqual(result.scale!.y, 3, 1e-4)).toBe(true)
85
+ expect(approxEqual(result.scale!.z, 0.5, 1e-4)).toBe(true)
86
+ expect(approxEqual(result.rotation!.x, 30, 0.1)).toBe(true)
87
+ expect(approxEqual(result.rotation!.y, 45, 0.1)).toBe(true)
88
+ expect(approxEqual(result.rotation!.z, 60, 0.1)).toBe(true)
89
+ })
90
+ })