@webspatial/core-sdk 1.6.1 → 1.7.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 (53) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/iife/index.d.ts +386 -236
  3. package/dist/iife/index.global.js +7 -7
  4. package/dist/iife/index.global.js.map +1 -1
  5. package/dist/index.d.ts +386 -236
  6. package/dist/index.js +1467 -1210
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/JSBCommand.ts +30 -100
  10. package/src/Spatial.ts +0 -21
  11. package/src/SpatialScene.ts +1 -5
  12. package/src/SpatialSession.ts +17 -3
  13. package/src/SpatializedDynamic3DElement.ts +0 -1
  14. package/src/SpatializedElementCreator.ts +6 -4
  15. package/src/SpatializedStatic3DElement.test.ts +99 -0
  16. package/src/SpatializedStatic3DElement.ts +99 -1
  17. package/src/WebMsgCommand.ts +10 -0
  18. package/src/coverage-boost.test.ts +38 -119
  19. package/src/index.ts +3 -1
  20. package/src/jsbcommand.coverage.test.ts +19 -48
  21. package/src/platform-adapter/CommandResultUtils.ts +2 -2
  22. package/src/platform-adapter/createPlatformSync.ts +34 -0
  23. package/src/platform-adapter/index.ts +5 -51
  24. package/src/platform-adapter/interface.ts +35 -23
  25. package/src/platform-adapter/pico-os/PicoOSPlatform.ts +84 -52
  26. package/src/platform-adapter/puppeteer/PuppeteerPlatform.ts +37 -11
  27. package/src/platform-adapter/spatialSceneQuery.ts +17 -0
  28. package/src/platform-adapter/ssr/SSRPlatform.ts +24 -15
  29. package/src/platform-adapter/vision-os/VisionOSPlatform.ts +55 -24
  30. package/src/platform-runtime.ts +13 -0
  31. package/src/reality/Attachment.ts +2 -2
  32. package/src/reality/entity/SpatialEntity.ts +0 -2
  33. package/src/reality/realityCreator.ts +15 -1
  34. package/src/reality/resource/SpatialTextureResource.ts +16 -0
  35. package/src/reality/resource/index.ts +1 -0
  36. package/src/runtime/WebSpatialRuntimeError.ts +16 -0
  37. package/src/runtime/capability-data.ts +113 -0
  38. package/src/runtime/contract-review.test.ts +44 -0
  39. package/src/runtime/index.ts +28 -0
  40. package/src/runtime/jsbAdapterPlatform.test.ts +36 -0
  41. package/src/runtime/jsbAdapterPlatform.ts +51 -0
  42. package/src/runtime/keys.ts +129 -0
  43. package/src/runtime/semver.ts +33 -0
  44. package/src/runtime/supports.test.ts +207 -0
  45. package/src/runtime/supports.ts +110 -0
  46. package/src/runtime/types.ts +11 -0
  47. package/src/runtime/userAgent.ts +64 -0
  48. package/src/scene-polyfill.manifest.test.ts +7 -5
  49. package/src/scene-polyfill.ts +8 -5
  50. package/src/spatial-host.ts +25 -0
  51. package/src/types/{global.d.ts → global.ts} +10 -5
  52. package/src/types/types.ts +9 -0
  53. package/src/platform-adapter/android/AndroidPlatform.ts +0 -133
@@ -263,115 +263,6 @@ describe('platform adapters', () => {
263
263
  vi.unmock('./JSBCommand')
264
264
  })
265
265
 
266
- // it('AndroidPlatform.callJSB resolves from async SpatialWebEvent callback', async () => {
267
- // const postMessage = vi.fn((rId: string) => {
268
- // expect(rId.startsWith('rId_')).toBe(true)
269
- // return ''
270
- // })
271
- // ;(window as any).webspatialBridge = { postMessage }
272
-
273
- // const { AndroidPlatform } = await import(
274
- // './platform-adapter/android/AndroidPlatform'
275
- // )
276
- // const { SpatialWebEvent: SpatialWebEventInstance } = await import(
277
- // './SpatialWebEvent'
278
- // )
279
- // const platform = new AndroidPlatform()
280
- // const p = platform.callJSB('c', '{"a":1}')
281
-
282
- // const rId = postMessage.mock.calls[0]?.[0] as string
283
- // SpatialWebEventInstance.eventReceiver[rId]({
284
- // success: true,
285
- // data: { ok: true },
286
- // })
287
-
288
- // await expect(p).resolves.toEqual({
289
- // success: true,
290
- // data: { ok: true },
291
- // errorCode: '',
292
- // errorMessage: '',
293
- // })
294
- // })
295
-
296
- // it('AndroidPlatform.callJSB handles sync bridge response and failures', async () => {
297
- // ;(window as any).webspatialBridge = {
298
- // postMessage: vi.fn(() =>
299
- // JSON.stringify({
300
- // success: false,
301
- // data: { code: 'E_SYNC', message: 'bad' },
302
- // }),
303
- // ),
304
- // }
305
-
306
- // const { AndroidPlatform } = await import(
307
- // './platform-adapter/android/AndroidPlatform'
308
- // )
309
- // const platform = new AndroidPlatform()
310
- // await expect(platform.callJSB('c', '{}')).resolves.toEqual({
311
- // success: false,
312
- // data: undefined,
313
- // errorCode: 'E_SYNC',
314
- // errorMessage: 'bad',
315
- // })
316
- // })
317
-
318
- // it('AndroidPlatform.callWebSpatialProtocol polls and returns injected SpatialId', async () => {
319
- // vi.useFakeTimers()
320
-
321
- // let canCount = 0
322
- // vi.doMock('./JSBCommand', () => {
323
- // return {
324
- // CheckWebViewCanCreateCommand: vi.fn().mockImplementation(() => ({
325
- // execute: vi.fn().mockImplementation(() => {
326
- // canCount += 1
327
- // return Promise.resolve({
328
- // success: true,
329
- // data: { can: canCount >= 2 },
330
- // errorCode: '',
331
- // errorMessage: '',
332
- // })
333
- // }),
334
- // })),
335
- // }
336
- // })
337
-
338
- // const windowProxy: any = {}
339
- // const openFn = vi.fn()
340
- // ;(window as any).open = vi.fn(() => windowProxy)
341
-
342
- // setTimeout(() => {
343
- // windowProxy.open = openFn
344
- // }, 20)
345
-
346
- // const { AndroidPlatform } = await import(
347
- // './platform-adapter/android/AndroidPlatform'
348
- // )
349
- // const { SpatialWebEvent: SpatialWebEventInstance } = await import(
350
- // './SpatialWebEvent'
351
- // )
352
- // const platform = new AndroidPlatform()
353
- // const p = platform.callWebSpatialProtocol('open', 'x=1', '_blank', '')
354
-
355
- // const receiverIds = Object.keys(SpatialWebEventInstance.eventReceiver)
356
- // const createdId = receiverIds[0] as string
357
- // const loadedId = receiverIds[1] as string
358
- // SpatialWebEventInstance.eventReceiver[createdId]?.({ spatialId: 'temp' })
359
- // await vi.advanceTimersByTimeAsync(100)
360
- // SpatialWebEventInstance.eventReceiver[loadedId]?.({
361
- // spatialId: 'spatial-1',
362
- // })
363
-
364
- // await vi.advanceTimersByTimeAsync(200)
365
-
366
- // const result = await p
367
- // expect(result.success).toBe(true)
368
- // expect(result.data.id).toBe('spatial-1')
369
- // expect(result.data.windowProxy).toBe(windowProxy)
370
- // const call = openFn.mock.calls[0] as unknown as [string, string | undefined]
371
- // expect(call?.[0]).toMatch(/^about:blank\?rid=/)
372
- // expect(call?.[1]).toBe('_self')
373
- // })
374
-
375
266
  it('SSRPlatform returns successful no-op results', async () => {
376
267
  const { SSRPlatform } = await import('./platform-adapter/ssr/SSRPlatform')
377
268
  const platform = new SSRPlatform()
@@ -379,12 +270,7 @@ describe('platform adapters', () => {
379
270
  await expect(platform.callJSB('c', '{}')).resolves.toMatchObject({
380
271
  success: true,
381
272
  })
382
- await expect(
383
- platform.callWebSpatialProtocol('s', '', '', ''),
384
- ).resolves.toMatchObject({
385
- success: true,
386
- })
387
- expect(platform.callWebSpatialProtocolSync('s', '', '', '')).toMatchObject({
273
+ expect(platform.openSpatialSceneSync('s', undefined)).toMatchObject({
388
274
  success: true,
389
275
  })
390
276
  })
@@ -437,7 +323,7 @@ describe('platform adapters', () => {
437
323
  './platform-adapter/vision-os/VisionOSPlatform'
438
324
  )
439
325
  const platform = new VisionOSPlatform()
440
- const r = await platform.callWebSpatialProtocol('open', 'a=1')
326
+ const r = await platform.createNativeSpatialDiv()
441
327
 
442
328
  expect(r.success).toBe(true)
443
329
  expect(r.data.id).toBe(uuid)
@@ -584,6 +470,14 @@ describe('SpatializedElementCreator', () => {
584
470
  const windowProxy: any = {
585
471
  document: { head: { innerHTML: '' } },
586
472
  }
473
+ vi.doMock('./spatial-host', () => ({
474
+ createNativeSpatialDiv: vi.fn().mockResolvedValue({
475
+ success: true,
476
+ data: { id: 'w1', windowProxy },
477
+ errorCode: '',
478
+ errorMessage: '',
479
+ }),
480
+ }))
587
481
 
588
482
  vi.doMock('./JSBCommand', () => {
589
483
  class OkCommand {
@@ -649,6 +543,15 @@ describe('SpatializedElementCreator', () => {
649
543
  })
650
544
 
651
545
  it('createSpatialized2DElement throws when command fails', async () => {
546
+ vi.doMock('./spatial-host', () => ({
547
+ createNativeSpatialDiv: vi.fn().mockResolvedValue({
548
+ success: false,
549
+ data: undefined,
550
+ errorCode: 'E',
551
+ errorMessage: 'bad',
552
+ }),
553
+ }))
554
+
652
555
  vi.doMock('./JSBCommand', () => {
653
556
  class OkCommand {
654
557
  execute() {
@@ -1052,9 +955,25 @@ describe('platform-adapter', () => {
1052
955
  })
1053
956
 
1054
957
  const { createPlatform } = await import('./platform-adapter')
1055
- const p = createPlatform() as any
958
+ const p = await createPlatform()
1056
959
  expect(typeof p.callJSB).toBe('function')
1057
- expect(typeof p.callWebSpatialProtocol).toBe('function')
1058
- expect(typeof p.callWebSpatialProtocolSync).toBe('function')
960
+ expect(typeof p.openSpatialSceneSync).toBe('function')
961
+ expect(typeof p.createNativeSpatialDiv).toBe('function')
962
+ expect(typeof p.createNativeAttachment).toBe('function')
963
+ })
964
+
965
+ it('createPlatformSync uses SSR sync noop in SSR env', async () => {
966
+ vi.resetModules()
967
+ vi.doMock('./ssr-polyfill', () => {
968
+ return { isSSREnv: () => true }
969
+ })
970
+
971
+ const { createPlatformSync } = await import(
972
+ './platform-adapter/createPlatformSync'
973
+ )
974
+ const p = createPlatformSync()
975
+ const r = p.openSpatialSceneSync('https://x', undefined)
976
+ expect(r.success).toBe(true)
977
+ expect(r.data).toBeUndefined()
1059
978
  })
1060
979
  })
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export type {} from './types/global'
2
+
1
3
  export { SpatialObject } from './SpatialObject'
2
4
  export { Spatial } from './Spatial'
3
5
  export { SpatialSession } from './SpatialSession'
@@ -10,7 +12,7 @@ export * as PhysicalMetrics from './physicalMetrics'
10
12
 
11
13
  export * from './reality'
12
14
  export * from './types/types'
13
- export * from './types/global.d'
15
+ export * from './runtime'
14
16
 
15
17
  // side effects
16
18
  import { injectSceneHook } from './scene-polyfill'
@@ -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: '',
@@ -192,40 +191,6 @@ describe('JSBCommand', () => {
192
191
  JSON.stringify({ id: 'so-1', color: '#fff' }),
193
192
  )
194
193
  })
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
194
  })
230
195
 
231
196
  describe('SpatialObject', () => {
@@ -272,6 +237,7 @@ describe('realityCreator', () => {
272
237
  if (cmd === 'CreateModelComponent') return ok({ id: 'cmp-1' })
273
238
  if (cmd === 'CreateSpatialModelEntity') return ok({ id: 'ment-1' })
274
239
  if (cmd === 'CreateModelAsset') return ok({ id: 'asset-1' })
240
+ if (cmd === 'CreateTexture') return ok({ id: 'tex-1' })
275
241
  return ok({ id: 'id-1' })
276
242
  })
277
243
  })
@@ -282,6 +248,7 @@ describe('realityCreator', () => {
282
248
  const mat = await import('./reality/material/SpatialUnlitMaterial')
283
249
  const cmp = await import('./reality/component/ModelComponent')
284
250
  const asset = await import('./reality/resource/SpatialModelAsset')
251
+ const tex = await import('./reality/resource/SpatialTextureResource')
285
252
  const ent = await import('./reality/entity/SpatialEntity')
286
253
  const modelEnt = await import('./reality/entity/SpatialModelEntity')
287
254
 
@@ -311,6 +278,10 @@ describe('realityCreator', () => {
311
278
  creator.createModelAsset({ url: 'https://example.com/a.glb' }),
312
279
  ).resolves.toBeInstanceOf(asset.SpatialModelAsset)
313
280
 
281
+ await expect(
282
+ creator.createSpatialTexture({ url: 'https://example.com/tex.png' }),
283
+ ).resolves.toBeInstanceOf(tex.SpatialTextureResource)
284
+
314
285
  await expect(
315
286
  creator.createSpatialModelEntity(
316
287
  { modelAssetId: 'asset-1' },
@@ -1,6 +1,6 @@
1
1
  import { CommandResult } from './interface'
2
2
 
3
- export function CommandResultSuccess(data: any): CommandResult {
3
+ export function CommandResultSuccess<TData>(data: TData): CommandResult<TData> {
4
4
  return {
5
5
  success: true,
6
6
  data,
@@ -12,7 +12,7 @@ export function CommandResultSuccess(data: any): CommandResult {
12
12
  export function CommandResultFailure(
13
13
  errorCode: string,
14
14
  errorMessage = '',
15
- ): CommandResult {
15
+ ): CommandResult<undefined> {
16
16
  return {
17
17
  success: false,
18
18
  data: undefined,
@@ -0,0 +1,34 @@
1
+ import { resolveJsbAdapterPlatform } from '../runtime/jsbAdapterPlatform'
2
+ import { isSSREnv } from '../ssr-polyfill'
3
+ import { PlatformAbility } from './interface'
4
+ import { PicoOSPlatform } from './pico-os/PicoOSPlatform'
5
+ import { PuppeteerPlatform } from './puppeteer/PuppeteerPlatform'
6
+ import { SSRPlatform } from './ssr/SSRPlatform'
7
+ import { VisionOSPlatform } from './vision-os/VisionOSPlatform'
8
+
9
+ function assertNever(_: never): never {
10
+ throw new Error('Unhandled jsb adapter platform kind')
11
+ }
12
+
13
+ /**
14
+ * Construct the JSB platform synchronously (static imports).
15
+ * Used by getPlatformSync (e.g. scene-polyfill openSpatialSceneSync) and kept
16
+ * consistent with async createPlatform so paths share one implementation.
17
+ */
18
+ export function createPlatformSync(): PlatformAbility {
19
+ if (isSSREnv()) {
20
+ return new SSRPlatform()
21
+ }
22
+ const userAgent = window.navigator.userAgent
23
+ const kind = resolveJsbAdapterPlatform(userAgent)
24
+ switch (kind) {
25
+ case 'puppeteer':
26
+ return new PuppeteerPlatform()
27
+ case 'picoos':
28
+ return new PicoOSPlatform()
29
+ case 'visionos':
30
+ return new VisionOSPlatform()
31
+ default:
32
+ return assertNever(kind)
33
+ }
34
+ }
@@ -1,53 +1,7 @@
1
- import { isSSREnv } from '../ssr-polyfill'
2
- import { PlatformAbility } from './interface'
3
- import { SSRPlatform } from './ssr/SSRPlatform'
1
+ export { createPlatformSync } from './createPlatformSync'
2
+ import { createPlatformSync } from './createPlatformSync'
3
+ import type { PlatformAbility } from './interface'
4
4
 
5
- function getWebSpatialVersion(ua: string): number[] | null {
6
- const match = ua.match(/WebSpatial\/(\d+)\.(\d+)\.(\d+)/)
7
- if (!match) {
8
- return null
9
- }
10
- return [Number(match[1]), Number(match[2]), Number(match[3])]
11
- }
12
-
13
- function isVersionGreater(a: number[] | null, b: number[]): boolean {
14
- if (!a) {
15
- return false
16
- }
17
- for (let index = 0; index < 3; index += 1) {
18
- const diff = a[index] - b[index]
19
- if (diff > 0) {
20
- return true
21
- }
22
- if (diff < 0) {
23
- return false
24
- }
25
- }
26
- return false
27
- }
28
-
29
- export function createPlatform(): PlatformAbility {
30
- if (isSSREnv()) {
31
- return new SSRPlatform()
32
- }
33
- const userAgent = window.navigator.userAgent
34
- const webSpatialVersion = getWebSpatialVersion(userAgent)
35
- if (window.navigator.userAgent.includes('Puppeteer')) {
36
- const PuppeteerPlatform =
37
- require('./puppeteer/PuppeteerPlatform').PuppeteerPlatform
38
- return new PuppeteerPlatform()
39
- } else if (
40
- userAgent.includes('PicoWebApp') &&
41
- isVersionGreater(webSpatialVersion, [0, 0, 1])
42
- ) {
43
- const PicoOSPlatform = require('./pico-os/PicoOSPlatform').PicoOSPlatform
44
- return new PicoOSPlatform()
45
- } else if (userAgent.includes('Android') || userAgent.includes('Linux')) {
46
- const AndroidPlatform = require('./android/AndroidPlatform').AndroidPlatform
47
- return new AndroidPlatform()
48
- } else {
49
- const VisionOSPlatform =
50
- require('./vision-os/VisionOSPlatform').VisionOSPlatform
51
- return new VisionOSPlatform()
52
- }
5
+ export async function createPlatform(): Promise<PlatformAbility> {
6
+ return createPlatformSync()
53
7
  }
@@ -1,36 +1,48 @@
1
- export interface CommandResult {
2
- success: boolean
3
- data: any
4
- errorCode: string | undefined
5
- errorMessage: string | undefined
6
- }
1
+ import type { SpatialSceneCreationOptionsInternal } from '../types/internal'
2
+ import type { AttachmentEntityOptions } from '../types/types'
7
3
 
8
- export interface WebSpatialProtocolResult extends CommandResult {
4
+ export interface CommandResult<TData = any> {
9
5
  success: boolean
10
- data:
11
- | {
12
- windowProxy: WindowProxy
13
- id: string
14
- }
15
- | undefined
6
+ data: TData
16
7
  errorCode: string | undefined
17
8
  errorMessage: string | undefined
18
9
  }
19
10
 
11
+ export type WebSpatialProtocolResult = CommandResult<
12
+ | {
13
+ // Platform-dependent window proxy shape:
14
+ // - browser WindowProxy (visionOS/PICO)
15
+ // - puppeteer simulated window object
16
+ // Keep broad to avoid over-constraining platform adapters/callers.
17
+ windowProxy: any
18
+ id: string
19
+ }
20
+ | undefined
21
+ >
22
+
23
+ /**
24
+ * Host/runtime bridge: JSB plus explicit spatial host capabilities.
25
+ * Implementations may use webspatial://, iframes, native events, etc.
26
+ */
20
27
  export interface PlatformAbility {
21
28
  callJSB(cmd: string, msg: string): Promise<CommandResult>
22
- callWebSpatialProtocol(
23
- schema: string,
24
- query?: string,
25
- target?: string,
26
- features?: string,
27
- ): Promise<WebSpatialProtocolResult>
28
29
 
29
- callWebSpatialProtocolSync(
30
- schema: string,
31
- query?: string,
30
+ /**
31
+ * Open a spatial scene synchronously (e.g. window.open polyfill).
32
+ * Implementations may use webspatial://createSpatialScene under the hood.
33
+ */
34
+ openSpatialSceneSync(
35
+ url: string,
36
+ config: SpatialSceneCreationOptionsInternal | undefined,
32
37
  target?: string,
33
38
  features?: string,
34
- resultCallback?: (result: CommandResult) => void,
35
39
  ): WebSpatialProtocolResult
40
+
41
+ /** Create a native-backed spatial 2D surface (spatial div). */
42
+ createNativeSpatialDiv(): Promise<WebSpatialProtocolResult>
43
+
44
+ /** Create a native attachment surface; options reserved for future protocol fields. */
45
+ createNativeAttachment(
46
+ options?: AttachmentEntityOptions,
47
+ ): Promise<WebSpatialProtocolResult>
36
48
  }