@realitycollective/service-framework-iwsdk 1.0.2-preview.2 → 1.0.3-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,12 +4,21 @@ Change log for the Reality Collective Service Framework for TypeScript. All pack
4
4
 
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Preview builds are not listed separately. The entry for a version accumulates while its previews are published, and is dated when that version is released.
6
6
 
7
- ## [1.0.2]
7
+ ## [1.0.3]
8
+
9
+ ### Changed
10
+
11
+ - `runtime-examples/client-runtime-app-example` runs on `three` ^0.185.0 with `@types/three` ^0.185.4, the version line every WebXR repository now uses.
12
+
13
+ ## [1.0.2] - 2026-10-01
8
14
 
9
15
  A native platform, beside IWSDK, three.js and Babylon.js, and byte I/O that works the same on the web and on a native host.
10
16
 
11
17
  ### Added
12
18
 
19
+ - A native test harness in `harness/native`, a small app for the conversion pipeline that runs on a Quest headset. In `kits` mode it runs `runtimeAdapterContractCases()`, `renderTickContractCases()` and `hostIOContractCases()` over `NativeRuntimeAdapter` and `createNativeHostIO`, plus a boot with no global `AbortController`, against the shell's test host, or this repository's reference fake under Node. In `play` mode it logs session, visibility, pause, focus, capability and telemetry changes from the live host, and reads a packed file through `HostIO`. `npm run harness:compile` typechecks and bundles it through the engine and browser-global gates and compiles it to Hermes bytecode; CI compiles it and never runs it.
20
+ - `@realitycollective/service-framework-iwsdk` - IWSDK 1.0.0's player rig: `world.player` is the `XROrigin` itself (an `Object3D` with the head as its `head` child), not an entity carrying `object3D`. `IWSDKPlayerLike` now accepts both shapes (`IWSDKPlayerEntityLike`, `IWSDKXROriginLike`), `playerRig` and `playerHead` read either, and `recentre()` moves a 1.0 rig; an IWSDK 1.0 `World` type-checks against `IWSDKWorldLike` again.
21
+ - `@realitycollective/service-framework` - `RuntimeAdapterDriver.sessionStart` states the mode-switch semantics the contract case relies on: called while a session is live, the host hands over the new session once the adapter has ended the old one itself, so a driver whose host starts sessions at once queues the start until the adapter's `endSession` completes or its next request arrives.
13
22
  - `FrameInfo.frame`, the binding's frame count from 1: the same number the `renderTick` context carries for that frame. The native host's frames, the IWSDK bridge and the three.js and Babylon.js owned loops all set it from the one counter they already kept for `renderTick`, and `emitFrame` takes it as an optional third argument, so a client that needs a frame number reads one clock instead of counting frames again. Additive: a frame an app emits itself without one carries none.
14
23
  - `@realitycollective/service-framework-native` - `NativeSessionInfo.features`, the WebXR feature names the session enabled (`"hand-tracking"`, `"plane-detection"` and the rest), which the capabilities read as the IWSDK adapter reads `enabledFeatures`; `NativeHost.input`, the `input` slice's `onSourcesChanged` signal and source kinds; `NativeHost.onSessionRefused`, the app's way to say it will not start a requested session; and the `manager` option, the service manager whose focus and pause signals follow the session.
15
24
  - `@realitycollective/service-framework-native`, the binding for a native XR app that embeds a JavaScript engine such as Hermes. The app owns the session, the frame loop, rendering and physics, and installs one host object, `globalThis.__rcHost`, before it evaluates the bundle. Everything crosses that object as plain values. `NativeRuntimeAdapter` publishes the app's frames and session through the same `RuntimeAdapter` seam as the other bindings, with the session facet, and passes `runtimeAdapterContractCases()`. It derives capabilities from the OpenXR session state, the enabled extensions, system hand tracking support and the blend mode. It keeps the same sticky `setCapabilities` override, `clearCapabilityOverrides()` and `refreshCapabilities()` the other adapters have. Given a `scheduler`, it emits `renderTick` with `source: "native"`. `createNativeHostIO()` returns a `HostIO` over the host's `io` slice. The host object's shape was proven on a Quest 3 and in the visionOS Simulator by the team that built the first native host. The package README documents it, including the slices the other families' native packages read.
@@ -35,6 +44,7 @@ A native platform, beside IWSDK, three.js and Babylon.js, and byte I/O that work
35
44
  - `@realitycollective/service-framework-native` - a refused session request resolves with the app's reason, `"denied"`, `"unsupported"` or `"error"`, at once. Before this only `"timeout"` and `"error"` could be reached, so a dismissed permission prompt waited ten seconds and read as a timeout.
36
45
  - `@realitycollective/service-framework-native` - every member of `NativeHost` and `NativeSessionInfo` states its units and meaning: milliseconds and seconds on `onFrame`, what counts as live, what the blend mode must reflect, and what each signal triggers.
37
46
  - **Behaviour change on every platform:** `session.request(mode)` while a session is already active is now a no-op only when `mode` matches the live session. A different mode ends the live session first and then requests the new one ("end-and-request"), instead of the previous no-op that always resolved `{ ok: true }` regardless of the mode asked for. A session the adapter did not itself request - already active at construction, or started through a host's own UI - has no known mode, so a request against it is treated as a switch and goes through the same end-and-request path.
47
+ - Dependencies on other Reality Collective repositories now follow one rule, checked by `scripts/rc-dependencies.mjs`. Going into `main`, each names the release npm tags `latest` and never a preview. Going into any other branch, each names the newest version on npm, release or preview. The rule covers every `package.json` in the repository (published packages, demos, harnesses and examples) and every copy `package-lock.json` records. A range passes only when its lowest version is the target, so `^0.1.8` passes for 0.1.8 and `^0.1.4` does not. CI runs it before the install, against the branch a pull request targets, and the publish workflow runs it before publishing. `release.mjs status` reports it. `release.mjs prepare` stops before cutting a release when a dependency names an unreleased preview of another repository, because that repository has to release first, and moves any range that is only behind. Other dependencies are out of scope. The script stays identical across the Reality Collective repositories.
38
48
 
39
49
  ### Fixed
40
50
 
@@ -110,5 +120,6 @@ First release. Six packages, published together.
110
120
  - `@realitycollective/service-framework-iwsdk` - Meta IWSDK (WebXR) bindings. IWSDK owns its own render loop, so this package is a passive frame source: one system relays each frame to services and maps `visibilityState` onto focus and pause, so services pause when the headset comes off. Services depend only on `RuntimeAdapter`, never on `@iwsdk/core`.
111
121
  - A worked example in every package's `Examples/` folder, and two runnable Vite apps in `runtime-examples/`.
112
122
 
123
+ [1.0.2]: https://github.com/realitycollective/com.realitycollective.service-framework.ts/releases/tag/v1.0.2
113
124
  [1.0.1]: https://github.com/realitycollective/com.realitycollective.service-framework.ts/releases/tag/v1.0.1
114
125
  [1.0.0]: https://github.com/realitycollective/com.realitycollective.service-framework.ts/releases/tag/v1.0.0
package/README.md CHANGED
@@ -16,7 +16,7 @@ Instead it is a **passive frame source**: it receives frames rather than produci
16
16
 
17
17
  IWSDK calls one system, `ServiceBridgeSystem`, once per frame. That system hands the frame to every subscribed service through `IWSDKAdapter`. It also maps IWSDK's `visibilityState` onto the manager's focus and pause signals, so services pause when the headset comes off.
18
18
 
19
- ```
19
+ ```text
20
20
  IWSDK World (render loop, XR session, input, ECS)
21
21
  │ world.registerSystem(makeServiceBridgeSystem({ ... }))
22
22
  ▼
@@ -38,11 +38,15 @@ Each focused frame goes out twice, on the two channels a service can be written
38
38
 
39
39
  ---
40
40
 
41
+ ## IWSDK 1.0
42
+
43
+ The adapter accepts IWSDK 1.0.0's player rig (`world.player` is the `XROrigin` itself, with the head as its `head` child) as well as the earlier entity form. `IWSDKPlayerLike` names both, and the adapter reads the rig and the head from either. `session.recentre()` moves the rig by the core's `recentreRig` rule. `IWSDKAdapterOptions.xrSystem` is the `navigator.xr` slice `session.isSupported` reads.
44
+
41
45
  ## No hard dependency on `@iwsdk/core`
42
46
 
43
- Like the three.js and Babylon.js bridges keep their renderer packages at arm's length, this package **never imports `@iwsdk/core`**. The IWSDK primitives the bridge needs - `createSystem` and the `VisibilityState.Visible` value - are passed in by the consumer (who owns IWSDK). This keeps the package tree-shakeable, version-tolerant across IWSDK `0.4.x` and `0.5.x`, and trivially mockable in unit tests.
47
+ Like the three.js and Babylon.js bridges keep their renderer packages at arm's length, this package **never imports `@iwsdk/core`**. The IWSDK primitives the bridge needs - `createSystem` and the `VisibilityState.Visible` value - are passed in by the consumer (who owns IWSDK). This keeps the package tree-shakeable, version-tolerant from IWSDK `0.4.x` to `1.0`, and trivially mockable in unit tests.
44
48
 
45
- `@iwsdk/core` is **not declared as a dependency of any kind** - not even an optional peer. Everything this package reads off the world is structurally typed in `iwsdk-host.ts` and passed in: `createSystem`, the `VisibilityState.Visible` value, the visibility signal, and - all optional - `visibilityState.subscribe`, the live `session`, `launchXR` and `exitXR`. Because the additions are optional, a world that carries nothing but the visibility signal still type-checks and still works; it simply reports no capabilities and no session. Those contracts are identical across 0.4.x and 0.5.x. A peer range here would constrain nothing while still being able to fail a consumer's clean install, so there is deliberately no entry.
49
+ `@iwsdk/core` is **not declared as a dependency of any kind** - not even an optional peer. Everything this package reads off the world is structurally typed in `iwsdk-host.ts` and passed in: `createSystem`, the `VisibilityState.Visible` value, the visibility signal, and - all optional - `visibilityState.subscribe`, the live `session`, `launchXR`, `exitXR` and the player rig. Because the additions are optional, a world that carries nothing but the visibility signal still type-checks and still works; it simply reports no capabilities and no session. Those contracts are the same from 0.4.x to 1.0, apart from the 1.0 player rig described above, which the adapter reads in either form. A peer range here would constrain nothing while still being able to fail a consumer's clean install, so there is deliberately no entry.
46
50
 
47
51
  ---
48
52
 
@@ -135,7 +139,7 @@ No `@iwsdk/core` import appears anywhere in the test.
135
139
 
136
140
  `AdapterCapabilities` (`immersive`, `handTracking`, `planeDetection`, `passthrough`, `environmentBlendMode`) is what gating services read (`adapter.getCapabilities()`) or subscribe to (`adapter.onCapabilitiesChange(cb)` - mirrors `onFrame`, so gates don't poll every frame).
137
141
 
138
- `IWSDKAdapter` derives all four from the live session through `deriveCapabilities(session)`, exported by `@realitycollective/service-framework`. The rules live in the core so that every host binding - this one, the three.js `WebXRRuntimeAdapter`, and whatever comes next - reports the same flags for the same session. It derives on construction, every time the world's visibility signal fires, which is when a session comes or goes, and every time the live session raises `inputsourceschange`:
142
+ `IWSDKAdapter` derives all five from the live session through `deriveCapabilities(session)`, exported by `@realitycollective/service-framework`. The rules live in the core so that every host binding - this one, the three.js `WebXRRuntimeAdapter`, and whatever comes next - reports the same flags for the same session. It derives on construction, every time the world's visibility signal fires, which is when a session comes or goes, and every time the live session raises `inputsourceschange`:
139
143
 
140
144
  | Flag | Derived from |
141
145
  | --- | --- |
@@ -234,11 +238,13 @@ Owned by this package:
234
238
  | Symbol | Kind | Purpose |
235
239
  | --- | --- | --- |
236
240
  | `IWSDKAdapter` | class | Production adapter; `emitFrame`, `refreshCapabilities`, `setCapabilities`, `clearCapabilityOverrides`, `session`, `getWorld`, `dispose`. |
241
+ | `IWSDKAdapterOptions` | interface | `{ xrSystem? }` - the `navigator.xr` slice `session.isSupported` reads. |
237
242
  | `makeServiceBridgeSystem` | factory | Returns the IWSDK `ServiceBridgeSystem` class; pumps `onFrame` and `renderTick`. |
238
243
  | `ServiceBridgeSystemOptions` | interface | `{ adapter, manager, world, createSystem, visibleState }`. |
239
244
  | `startServiceRuntime` / `ServiceRuntime` | function / interface | Bootstraps `{ manager, adapter }` from a profile factory. |
240
245
  | `toIWSDKFeatures` / `IWSDK_FEATURE_KEYS` / `IWSDKFeatureMapping` | function / const / interface | Maps WebXR feature strings onto IWSDK's structured flags and reports what it could not map. |
241
- | `IWSDKWorldLike` / `IWSDKSignalLike` / `IWSDKSessionLike` / `IWSDKInputSourceLike` / `IWSDKSessionEventType` / `IWSDKSessionEventListener` / `IWSDKXROptionsLike` / `IWSDKXRFeatureOptionsLike` / `IWSDKFeatureFlagLike` / `IWSDKDepthSensingFlagLike` / `IWSDKSystemLike` / `IWSDKSystemConstructor` / `CreateSystemLike` | types | Structural `@iwsdk/core` contracts (no engine import). |
246
+ | `IWSDKWorldLike` / `IWSDKSignalLike` / `IWSDKSessionLike` / `IWSDKInputSourceLike` / `IWSDKSessionEventType` / `IWSDKSessionEventListener` / `IWSDKXROptionsLike` / `IWSDKXRFeatureOptionsLike` / `IWSDKFeatureFlagLike` / `IWSDKDepthSensingFlagLike` / `IWSDKXRSystemLike` / `IWSDKSystemLike` / `IWSDKSystemConstructor` / `CreateSystemLike` | types | Structural `@iwsdk/core` contracts (no engine import). |
247
+ | `IWSDKPlayerLike` / `IWSDKPlayerEntityLike` / `IWSDKXROriginLike` / `IWSDKPlayerSpaceEntitiesLike` / `IWSDKObject3DLike` / `IWSDKVector3Like` / `IWSDKQuaternionLike` | types | The player rig in both forms (the 1.0 `XROrigin` and the earlier entity), as `recentre()` reads it. |
242
248
 
243
249
  Re-exported from `@realitycollective/service-framework`. None of these ever touched IWSDK, and every host binding needs them, so they moved into the core in 1.0.1. They are re-exported here unchanged, so importing them from this package keeps working; new code should import them from the core package.
244
250
 
@@ -248,7 +254,7 @@ Re-exported from `@realitycollective/service-framework`. None of these ever touc
248
254
  | `FrameInfo` | interface | `{ timestamp, delta }`. |
249
255
  | `AdapterCapabilities` / `DEFAULT_CAPABILITIES` | interface / const | XR capability flags; all-false default. |
250
256
  | `Unsubscribe` / `FrameListener` / `CapabilitiesListener` | types | Callback / handle aliases. |
251
- | `SessionFacet` | interface | `getState`, `request`, `end`, `onStateChange`, `onVisibilityChange`. |
257
+ | `SessionFacet` | interface | `getState`, `getMode`, `isSupported`, `request`, `end`, `recentre`, `onStateChange`, `onVisibilityChange`. |
252
258
  | `SessionMode` / `SessionState` / `SessionResult` / `SessionFailureReason` / `SessionVisibility` / `SessionRequestOptions` | types | The session facet's vocabulary. |
253
259
  | `DEFAULT_SESSION_TIMEOUT_MS` | const | 10000 - the default `request` timeout. |
254
260
  | `RUNTIME_ADAPTER_FACETS` / `RuntimeAdapterFacet` | const / type | Every optional facet an adapter can carry; walked by the conformance suite. |
@@ -260,7 +266,7 @@ Re-exported from `@realitycollective/service-framework`. None of these ever touc
260
266
 
261
267
  ## Running tests
262
268
 
263
- From the workspace root:
269
+ From the repository root:
264
270
 
265
271
  ```bash
266
272
  npm test
@@ -283,4 +289,4 @@ The example mocks the `@iwsdk/core` primitives so it runs in plain Node.js: it d
283
289
 
284
290
  ## License
285
291
 
286
- MIT
292
+ MIT - see [LICENSE](./LICENSE).
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { DEFAULT_CAPABILITIES, DEFAULT_SESSION_TIMEOUT_MS, MockRuntimeAdapter, RUNTIME_ADAPTER_FACETS, SnapshotService, } from "@realitycollective/service-framework";
8
8
  export type { AdapterCapabilities, CapabilitiesListener, FrameInfo, FrameListener, RuntimeAdapter, RuntimeAdapterFacet, ServiceContext, SessionFacet, SessionFailureReason, SessionMode, SessionRequestOptions, SessionResult, SessionState, SessionVisibility, SnapshotListener, Unsubscribe, } from "@realitycollective/service-framework";
9
- export type { CreateSystemLike, IWSDKDepthSensingFlagLike, IWSDKFeatureFlagLike, IWSDKInputSourceLike, IWSDKObject3DLike, IWSDKPlayerLike, IWSDKPlayerSpaceEntitiesLike, IWSDKQuaternionLike, IWSDKSessionEventListener, IWSDKSessionEventType, IWSDKSessionLike, IWSDKSignalLike, IWSDKSystemConstructor, IWSDKSystemLike, IWSDKVector3Like, IWSDKWorldLike, IWSDKXRFeatureOptionsLike, IWSDKXROptionsLike, IWSDKXRSystemLike, } from "./iwsdk-host.js";
9
+ export type { CreateSystemLike, IWSDKDepthSensingFlagLike, IWSDKFeatureFlagLike, IWSDKInputSourceLike, IWSDKObject3DLike, IWSDKPlayerEntityLike, IWSDKPlayerLike, IWSDKXROriginLike, IWSDKPlayerSpaceEntitiesLike, IWSDKQuaternionLike, IWSDKSessionEventListener, IWSDKSessionEventType, IWSDKSessionLike, IWSDKSignalLike, IWSDKSystemConstructor, IWSDKSystemLike, IWSDKVector3Like, IWSDKWorldLike, IWSDKXRFeatureOptionsLike, IWSDKXROptionsLike, IWSDKXRSystemLike, } from "./iwsdk-host.js";
10
10
  export { IWSDK_FEATURE_KEYS, toIWSDKFeatures } from "./iwsdk-features.js";
11
11
  export type { IWSDKFeatureMapping } from "./iwsdk-features.js";
12
12
  export { IWSDKAdapter } from "./iwsdk-adapter.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,GAChB,MAAM,sCAAsC,CAAC;AA0C9C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * The runtime-adapter contract, the snapshot service base and the headless mock\n * moved into `@realitycollective/service-framework` in 1.0.1: none of them ever\n * touched IWSDK, and every host binding needs them. They are re-exported here\n * unchanged so existing imports from this package keep working.\n */\nexport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n MockRuntimeAdapter,\n RUNTIME_ADAPTER_FACETS,\n SnapshotService,\n} from \"@realitycollective/service-framework\";\nexport type {\n AdapterCapabilities,\n CapabilitiesListener,\n FrameInfo,\n FrameListener,\n RuntimeAdapter,\n RuntimeAdapterFacet,\n ServiceContext,\n SessionFacet,\n SessionFailureReason,\n SessionMode,\n SessionRequestOptions,\n SessionResult,\n SessionState,\n SessionVisibility,\n SnapshotListener,\n Unsubscribe,\n} from \"@realitycollective/service-framework\";\n\nexport type {\n CreateSystemLike,\n IWSDKDepthSensingFlagLike,\n IWSDKFeatureFlagLike,\n IWSDKInputSourceLike,\n IWSDKObject3DLike,\n IWSDKPlayerLike,\n IWSDKPlayerSpaceEntitiesLike,\n IWSDKQuaternionLike,\n IWSDKSessionEventListener,\n IWSDKSessionEventType,\n IWSDKSessionLike,\n IWSDKSignalLike,\n IWSDKSystemConstructor,\n IWSDKSystemLike,\n IWSDKVector3Like,\n IWSDKWorldLike,\n IWSDKXRFeatureOptionsLike,\n IWSDKXROptionsLike,\n IWSDKXRSystemLike,\n} from \"./iwsdk-host.js\";\n\nexport { IWSDK_FEATURE_KEYS, toIWSDKFeatures } from \"./iwsdk-features.js\";\nexport type { IWSDKFeatureMapping } from \"./iwsdk-features.js\";\n\nexport { IWSDKAdapter } from \"./iwsdk-adapter.js\";\nexport type { IWSDKAdapterOptions } from \"./iwsdk-adapter.js\";\n\nexport { makeServiceBridgeSystem } from \"./service-bridge-system.js\";\nexport type { ServiceBridgeSystemOptions } from \"./service-bridge-system.js\";\n\nexport { startServiceRuntime } from \"./bootstrap.js\";\nexport type { ServiceRuntime } from \"./bootstrap.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,GAChB,MAAM,sCAAsC,CAAC;AA4C9C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["/**\n * The runtime-adapter contract, the snapshot service base and the headless mock\n * moved into `@realitycollective/service-framework` in 1.0.1: none of them ever\n * touched IWSDK, and every host binding needs them. They are re-exported here\n * unchanged so existing imports from this package keep working.\n */\nexport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n MockRuntimeAdapter,\n RUNTIME_ADAPTER_FACETS,\n SnapshotService,\n} from \"@realitycollective/service-framework\";\nexport type {\n AdapterCapabilities,\n CapabilitiesListener,\n FrameInfo,\n FrameListener,\n RuntimeAdapter,\n RuntimeAdapterFacet,\n ServiceContext,\n SessionFacet,\n SessionFailureReason,\n SessionMode,\n SessionRequestOptions,\n SessionResult,\n SessionState,\n SessionVisibility,\n SnapshotListener,\n Unsubscribe,\n} from \"@realitycollective/service-framework\";\n\nexport type {\n CreateSystemLike,\n IWSDKDepthSensingFlagLike,\n IWSDKFeatureFlagLike,\n IWSDKInputSourceLike,\n IWSDKObject3DLike,\n IWSDKPlayerEntityLike,\n IWSDKPlayerLike,\n IWSDKXROriginLike,\n IWSDKPlayerSpaceEntitiesLike,\n IWSDKQuaternionLike,\n IWSDKSessionEventListener,\n IWSDKSessionEventType,\n IWSDKSessionLike,\n IWSDKSignalLike,\n IWSDKSystemConstructor,\n IWSDKSystemLike,\n IWSDKVector3Like,\n IWSDKWorldLike,\n IWSDKXRFeatureOptionsLike,\n IWSDKXROptionsLike,\n IWSDKXRSystemLike,\n} from \"./iwsdk-host.js\";\n\nexport { IWSDK_FEATURE_KEYS, toIWSDKFeatures } from \"./iwsdk-features.js\";\nexport type { IWSDKFeatureMapping } from \"./iwsdk-features.js\";\n\nexport { IWSDKAdapter } from \"./iwsdk-adapter.js\";\nexport type { IWSDKAdapterOptions } from \"./iwsdk-adapter.js\";\n\nexport { makeServiceBridgeSystem } from \"./service-bridge-system.js\";\nexport type { ServiceBridgeSystemOptions } from \"./service-bridge-system.js\";\n\nexport { startServiceRuntime } from \"./bootstrap.js\";\nexport type { ServiceRuntime } from \"./bootstrap.js\";\n"]}
@@ -11,7 +11,7 @@
11
11
  * `exitXR` entry points.
12
12
  */
13
13
  import { type AdapterCapabilities, type CapabilitiesListener, type FrameListener, type RuntimeAdapter, type SessionFacet, type Unsubscribe } from "@realitycollective/service-framework";
14
- import type { IWSDKWorldLike, IWSDKXRSystemLike } from "./iwsdk-host.js";
14
+ import { IWSDKWorldLike, IWSDKXRSystemLike } from "./iwsdk-host.js";
15
15
  /** Options for {@link IWSDKAdapter}, beyond the `World` it binds to. */
16
16
  export interface IWSDKAdapterOptions {
17
17
  /**
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { DEFAULT_CAPABILITIES, DEFAULT_SESSION_TIMEOUT_MS, deriveCapabilities, recentreRig, } from "@realitycollective/service-framework";
14
14
  import { toIWSDKFeatures } from "./iwsdk-features.js";
15
+ import { playerHead, playerRig, } from "./iwsdk-host.js";
15
16
  /** `navigator.xr`, read defensively: there is no navigator in a Node test. */
16
17
  function defaultXRSystem() {
17
18
  const globalNavigator = globalThis.navigator;
@@ -231,8 +232,8 @@ export class IWSDKAdapter {
231
232
  * nothing when the world carries no rig, or no head entity, to move.
232
233
  */
233
234
  recentrePlayerRig() {
234
- const rig = this.world.player?.object3D;
235
- const head = this.world.playerSpaceEntities?.head?.object3D;
235
+ const rig = playerRig(this.world.player);
236
+ const head = playerHead(this.world.player, this.world.playerSpaceEntities);
236
237
  if (!rig || !head) {
237
238
  return;
238
239
  }
@@ -1 +1 @@
1
- {"version":3,"file":"iwsdk-adapter.js","sourceRoot":"","sources":["../src/iwsdk-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IWSDK implementation of {@link RuntimeAdapter}. IWSDK owns the render loop, so\n * frames are pushed in rather than pulled: the `ServiceBridgeSystem` (a normal\n * IWSDK system) calls {@link IWSDKAdapter.emitFrame} once per frame and the\n * adapter notifies subscribed services.\n *\n * Capabilities are derived from the live session on construction and on every\n * visibility change, through the core's shared `deriveCapabilities`, with a\n * manual override layer on top. Session lifecycle is\n * exposed through {@link IWSDKAdapter.session}, over the world's `launchXR` and\n * `exitXR` entry points.\n */\nimport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n deriveCapabilities,\n recentreRig,\n type AdapterCapabilities,\n type CapabilitiesListener,\n type FrameInfo,\n type FrameListener,\n type RuntimeAdapter,\n type SessionFacet,\n type SessionMode,\n type SessionRequestOptions,\n type SessionResult,\n type SessionState,\n type SessionVisibility,\n type Unsubscribe,\n} from \"@realitycollective/service-framework\";\nimport { toIWSDKFeatures } from \"./iwsdk-features.js\";\nimport type {\n IWSDKSessionEventListener,\n IWSDKSessionLike,\n IWSDKWorldLike,\n IWSDKXRSystemLike,\n} from \"./iwsdk-host.js\";\n\n/** Options for {@link IWSDKAdapter}, beyond the `World` it binds to. */\nexport interface IWSDKAdapterOptions {\n /**\n * `navigator.xr`, the slice `isSupported()` reads. Defaults to the global\n * when omitted, and to `null` where there is no global, which is what a\n * Node test sees - `isSupported` then resolves `false` for every mode.\n */\n readonly xrSystem?: IWSDKXRSystemLike | null;\n}\n\n/** `navigator.xr`, read defensively: there is no navigator in a Node test. */\nfunction defaultXRSystem(): IWSDKXRSystemLike | null {\n const globalNavigator = (globalThis as { navigator?: { xr?: IWSDKXRSystemLike } }).navigator;\n\n return globalNavigator?.xr ?? null;\n}\n\ntype SessionStateListener = (state: SessionState) => void;\ntype SessionVisibilityListener = (visibility: SessionVisibility) => void;\n\n/** How often the session waits re-check the world when the signal is silent. */\nconst SESSION_POLL_INTERVAL_MS = 50;\n\nconst SESSION_VISIBILITY_VALUES: ReadonlySet<string> = new Set([\n \"visible\",\n \"visible-blurred\",\n \"hidden\",\n \"non-immersive\",\n]);\n\n/**\n * IWSDK's visibility signal carries the WebXR visibility strings. Anything this\n * adapter does not recognise is reported as `\"hidden\"`, because treating an\n * unknown state as visible would keep game logic running when it should not.\n */\nfunction toSessionVisibility(value: unknown): SessionVisibility {\n if (typeof value === \"string\" && SESSION_VISIBILITY_VALUES.has(value)) {\n return value as SessionVisibility;\n }\n\n return \"hidden\";\n}\n\n/** A `request()` or `end()` waiting for the world to reach some state. */\ninterface PendingWait {\n readonly predicate: () => boolean;\n readonly resolve: (settled: boolean) => void;\n readonly timer: ReturnType<typeof setTimeout>;\n readonly interval: ReturnType<typeof setInterval>;\n}\n\nexport class IWSDKAdapter implements RuntimeAdapter {\n private readonly frameListeners = new Set<FrameListener>();\n private readonly capabilitiesListeners = new Set<CapabilitiesListener>();\n private readonly stateListeners = new Set<SessionStateListener>();\n private readonly visibilityListeners = new Set<SessionVisibilityListener>();\n private readonly pendingWaits = new Set<PendingWait>();\n\n private readonly xrSystem: IWSDKXRSystemLike | null;\n\n private derived: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private overrides: Partial<AdapterCapabilities> = {};\n private capabilities: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private sessionState: SessionState = \"none\";\n private mode: SessionMode | null = null;\n private unsubscribeVisibility: Unsubscribe | undefined;\n private boundSession: IWSDKSessionLike | null = null;\n\n /**\n * Pre-bound, because `removeEventListener` has to be handed the same function\n * `addEventListener` got.\n */\n private readonly onInputSourcesChange: IWSDKSessionEventListener = () => {\n this.refreshCapabilities();\n };\n\n /** Session lifecycle over the world's `launchXR` / `exitXR` entry points. */\n public readonly session: SessionFacet = {\n getState: () => this.sessionState,\n getMode: () => (this.sessionState === \"active\" ? this.mode : null),\n isSupported: (mode) => this.checkSupported(mode),\n request: (mode, options) => this.requestSession(mode, options),\n end: () => this.endSession(),\n recentre: () => this.recentrePlayerRig(),\n onStateChange: (listener) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n },\n onVisibilityChange: (listener) => {\n this.visibilityListeners.add(listener);\n return () => {\n this.visibilityListeners.delete(listener);\n };\n },\n };\n\n public constructor(\n private readonly world: IWSDKWorldLike,\n options: IWSDKAdapterOptions = {},\n ) {\n this.xrSystem = options.xrSystem === undefined ? defaultXRSystem() : options.xrSystem;\n this.unsubscribeVisibility = world.visibilityState.subscribe?.((value) => {\n this.handleVisibilityChange(value);\n });\n\n this.bindSession(this.world.session ?? null);\n this.derived = deriveCapabilities(this.world.session);\n this.capabilities = this.derived;\n this.sessionState = this.hasSession() ? \"active\" : \"none\";\n }\n\n public onFrame(listener: FrameListener): Unsubscribe {\n this.frameListeners.add(listener);\n return () => {\n this.frameListeners.delete(listener);\n };\n }\n\n public getCapabilities(): AdapterCapabilities {\n return this.capabilities;\n }\n\n public onCapabilitiesChange(listener: CapabilitiesListener): Unsubscribe {\n this.capabilitiesListeners.add(listener);\n return () => {\n this.capabilitiesListeners.delete(listener);\n };\n }\n\n /** The IWSDK `World` this adapter is bound to. */\n public getWorld(): IWSDKWorldLike {\n return this.world;\n }\n\n /**\n * Re-read the live session and publish any capability change. The adapter\n * does this itself whenever the visibility signal fires and whenever the\n * session's input sources change; call it directly on a host whose signal has\n * no `subscribe`, or after the host enables a feature mid-session.\n */\n public refreshCapabilities(): void {\n this.bindSession(this.world.session ?? null);\n this.derived = deriveCapabilities(this.world.session);\n this.publishCapabilities();\n }\n\n /**\n * Force capability flags regardless of what the session reports. Overrides\n * are a layer on top of the derived values: they win for as long as they are\n * set, survive every later derivation, and are dropped only by\n * {@link IWSDKAdapter.clearCapabilityOverrides} or\n * {@link IWSDKAdapter.dispose}. Subscribers are notified if the effective\n * capabilities changed.\n */\n public setCapabilities(capabilities: Partial<AdapterCapabilities>): void {\n this.overrides = { ...this.overrides, ...capabilities };\n this.publishCapabilities();\n }\n\n /** Drop every manual override and fall back to the derived capabilities. */\n public clearCapabilityOverrides(): void {\n this.overrides = {};\n this.publishCapabilities();\n }\n\n /** Called once per frame by the ECS bridge system. */\n public emitFrame(timestamp: number, delta: number, frame?: number): void {\n const info: FrameInfo = frame === undefined ? { timestamp, delta } : { timestamp, delta, frame };\n this.frameListeners.forEach((listener) => listener(info));\n }\n\n /**\n * Release everything the adapter holds: the visibility subscription, the\n * session's own listener, any in-flight session wait, the manual overrides\n * and every listener.\n */\n public dispose(): void {\n this.unsubscribeVisibility?.();\n this.unsubscribeVisibility = undefined;\n this.unbindSession();\n\n for (const wait of Array.from(this.pendingWaits)) {\n this.settleWait(wait, false);\n }\n\n this.overrides = {};\n this.frameListeners.clear();\n this.capabilitiesListeners.clear();\n this.stateListeners.clear();\n this.visibilityListeners.clear();\n }\n\n private hasSession(): boolean {\n return Boolean(this.world.session);\n }\n\n /**\n * Follow one session's `inputsourceschange` event, so `handTracking` updates\n * when the player picks the controllers up or puts them down. IWSDK's world\n * carries a real `XRSession`, which raises the event; a host that reports a\n * session without listener methods is bound all the same and simply never\n * pushes, which is what `refreshCapabilities()` remains for.\n */\n private bindSession(session: IWSDKSessionLike | null): void {\n if (session === this.boundSession) {\n return;\n }\n\n this.unbindSession();\n this.boundSession = session;\n session?.addEventListener?.(\"inputsourceschange\", this.onInputSourcesChange);\n }\n\n private unbindSession(): void {\n this.boundSession?.removeEventListener?.(\"inputsourceschange\", this.onInputSourcesChange);\n this.boundSession = null;\n }\n\n private publishCapabilities(): void {\n const next: AdapterCapabilities = { ...this.derived, ...this.overrides };\n const current = this.capabilities;\n\n if (\n next.immersive === current.immersive &&\n next.handTracking === current.handTracking &&\n next.planeDetection === current.planeDetection &&\n next.passthrough === current.passthrough &&\n next.environmentBlendMode === current.environmentBlendMode\n ) {\n return;\n }\n\n this.capabilities = next;\n this.capabilitiesListeners.forEach((listener) => listener(next));\n }\n\n private handleVisibilityChange(value: unknown): void {\n this.refreshCapabilities();\n this.syncSessionState();\n this.checkWaits();\n\n const visibility = toSessionVisibility(value);\n this.visibilityListeners.forEach((listener) => listener(visibility));\n }\n\n /**\n * Follow the world when nothing local is mid-transition. `request()` and\n * `end()` own the state while they run, so the signal does not fight them.\n */\n private syncSessionState(): void {\n if (this.sessionState === \"requesting\" || this.sessionState === \"ending\") {\n return;\n }\n\n this.setSessionState(this.hasSession() ? \"active\" : \"none\");\n }\n\n /** Answers `isSupported()` off `navigator.xr`; never rejects. */\n private async checkSupported(mode: SessionMode): Promise<boolean> {\n const system = this.xrSystem;\n\n if (!system) {\n return false;\n }\n\n try {\n return await system.isSessionSupported(mode);\n } catch {\n return false;\n }\n }\n\n /**\n * Move the player rig so the head's current floor position and yaw become\n * the origin, leaving the head's pose local to the rig untouched. Does\n * nothing when the world carries no rig, or no head entity, to move.\n */\n private recentrePlayerRig(): void {\n const rig = this.world.player?.object3D;\n const head = this.world.playerSpaceEntities?.head?.object3D;\n\n if (!rig || !head) {\n return;\n }\n\n const next = recentreRig(\n {\n position: [rig.position.x, rig.position.y, rig.position.z],\n orientation: [rig.quaternion.x, rig.quaternion.y, rig.quaternion.z, rig.quaternion.w],\n },\n {\n position: [head.position.x, head.position.y, head.position.z],\n orientation: [head.quaternion.x, head.quaternion.y, head.quaternion.z, head.quaternion.w],\n },\n );\n\n rig.position.x = next.position[0];\n rig.position.y = next.position[1];\n rig.position.z = next.position[2];\n rig.quaternion.x = next.orientation[0];\n rig.quaternion.y = next.orientation[1];\n rig.quaternion.z = next.orientation[2];\n rig.quaternion.w = next.orientation[3];\n }\n\n private async requestSession(\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n if (this.sessionState === \"active\") {\n if (this.mode === mode) {\n return { ok: true };\n }\n\n // End-and-request: a world with no `exitXR` cannot end its own session,\n // so the switch is refused outright and the live session is left alone.\n if (!this.world.exitXR) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n await this.endSession();\n }\n\n const launch = this.world.launchXR;\n\n if (!launch) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;\n this.setSessionState(\"requesting\");\n\n const { features } = toIWSDKFeatures(options);\n\n try {\n // IWSDK reads `XROptions.sessionMode`; its `SessionMode` enum values are the\n // WebXR mode strings, so the facet mode passes through unchanged. The\n // request's feature strings become IWSDK's structured flags; `launchXR`\n // merges what it is given over the world's `xrDefaults`, so a request\n // that names no features leaves the app's defaults alone.\n launch.call(this.world, { sessionMode: mode, ...(features ? { features } : {}) });\n } catch (error) {\n this.setSessionState(\"none\");\n return { ok: false, reason: \"unsupported\", error };\n }\n\n const started = await this.waitFor(() => this.hasSession(), timeoutMs);\n\n if (!started) {\n this.setSessionState(\"none\");\n return { ok: false, reason: \"timeout\" };\n }\n\n this.mode = mode;\n this.setSessionState(\"active\");\n this.refreshCapabilities();\n return { ok: true };\n }\n\n private async endSession(): Promise<void> {\n if (this.sessionState === \"none\") {\n return;\n }\n\n const exit = this.world.exitXR;\n this.setSessionState(\"ending\");\n\n if (exit) {\n exit.call(this.world);\n }\n\n await this.waitFor(() => !this.hasSession(), DEFAULT_SESSION_TIMEOUT_MS);\n this.setSessionState(\"none\");\n this.refreshCapabilities();\n }\n\n /**\n * Resolve `true` once `predicate` holds, `false` on timeout. The visibility\n * signal drives this where the host has one; the interval is the fallback for\n * a host whose signal does not push, and for a session that appears without\n * a visibility transition.\n */\n private waitFor(predicate: () => boolean, timeoutMs: number): Promise<boolean> {\n if (predicate()) {\n return Promise.resolve(true);\n }\n\n return new Promise<boolean>((resolve) => {\n const wait: PendingWait = {\n predicate,\n resolve,\n timer: setTimeout(() => this.settleWait(wait, false), timeoutMs),\n interval: setInterval(() => this.checkWaits(), SESSION_POLL_INTERVAL_MS),\n };\n\n this.pendingWaits.add(wait);\n });\n }\n\n private checkWaits(): void {\n for (const wait of Array.from(this.pendingWaits)) {\n if (wait.predicate()) {\n this.settleWait(wait, true);\n }\n }\n }\n\n private settleWait(wait: PendingWait, settled: boolean): void {\n this.pendingWaits.delete(wait);\n clearTimeout(wait.timer);\n clearInterval(wait.interval);\n wait.resolve(settled);\n }\n\n private setSessionState(state: SessionState): void {\n if (this.sessionState === state) {\n return;\n }\n\n this.sessionState = state;\n\n if (state === \"none\") {\n this.mode = null;\n }\n\n this.stateListeners.forEach((listener) => listener(state));\n }\n}\n"]}
1
+ {"version":3,"file":"iwsdk-adapter.js","sourceRoot":"","sources":["../src/iwsdk-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IWSDK implementation of {@link RuntimeAdapter}. IWSDK owns the render loop, so\n * frames are pushed in rather than pulled: the `ServiceBridgeSystem` (a normal\n * IWSDK system) calls {@link IWSDKAdapter.emitFrame} once per frame and the\n * adapter notifies subscribed services.\n *\n * Capabilities are derived from the live session on construction and on every\n * visibility change, through the core's shared `deriveCapabilities`, with a\n * manual override layer on top. Session lifecycle is\n * exposed through {@link IWSDKAdapter.session}, over the world's `launchXR` and\n * `exitXR` entry points.\n */\nimport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n deriveCapabilities,\n recentreRig,\n type AdapterCapabilities,\n type CapabilitiesListener,\n type FrameInfo,\n type FrameListener,\n type RuntimeAdapter,\n type SessionFacet,\n type SessionMode,\n type SessionRequestOptions,\n type SessionResult,\n type SessionState,\n type SessionVisibility,\n type Unsubscribe,\n} from \"@realitycollective/service-framework\";\nimport { toIWSDKFeatures } from \"./iwsdk-features.js\";\nimport {\n playerHead,\n playerRig,\n type\n IWSDKSessionEventListener,\n IWSDKSessionLike,\n IWSDKWorldLike,\n IWSDKXRSystemLike,\n} from \"./iwsdk-host.js\";\n\n/** Options for {@link IWSDKAdapter}, beyond the `World` it binds to. */\nexport interface IWSDKAdapterOptions {\n /**\n * `navigator.xr`, the slice `isSupported()` reads. Defaults to the global\n * when omitted, and to `null` where there is no global, which is what a\n * Node test sees - `isSupported` then resolves `false` for every mode.\n */\n readonly xrSystem?: IWSDKXRSystemLike | null;\n}\n\n/** `navigator.xr`, read defensively: there is no navigator in a Node test. */\nfunction defaultXRSystem(): IWSDKXRSystemLike | null {\n const globalNavigator = (globalThis as { navigator?: { xr?: IWSDKXRSystemLike } }).navigator;\n\n return globalNavigator?.xr ?? null;\n}\n\ntype SessionStateListener = (state: SessionState) => void;\ntype SessionVisibilityListener = (visibility: SessionVisibility) => void;\n\n/** How often the session waits re-check the world when the signal is silent. */\nconst SESSION_POLL_INTERVAL_MS = 50;\n\nconst SESSION_VISIBILITY_VALUES: ReadonlySet<string> = new Set([\n \"visible\",\n \"visible-blurred\",\n \"hidden\",\n \"non-immersive\",\n]);\n\n/**\n * IWSDK's visibility signal carries the WebXR visibility strings. Anything this\n * adapter does not recognise is reported as `\"hidden\"`, because treating an\n * unknown state as visible would keep game logic running when it should not.\n */\nfunction toSessionVisibility(value: unknown): SessionVisibility {\n if (typeof value === \"string\" && SESSION_VISIBILITY_VALUES.has(value)) {\n return value as SessionVisibility;\n }\n\n return \"hidden\";\n}\n\n/** A `request()` or `end()` waiting for the world to reach some state. */\ninterface PendingWait {\n readonly predicate: () => boolean;\n readonly resolve: (settled: boolean) => void;\n readonly timer: ReturnType<typeof setTimeout>;\n readonly interval: ReturnType<typeof setInterval>;\n}\n\nexport class IWSDKAdapter implements RuntimeAdapter {\n private readonly frameListeners = new Set<FrameListener>();\n private readonly capabilitiesListeners = new Set<CapabilitiesListener>();\n private readonly stateListeners = new Set<SessionStateListener>();\n private readonly visibilityListeners = new Set<SessionVisibilityListener>();\n private readonly pendingWaits = new Set<PendingWait>();\n\n private readonly xrSystem: IWSDKXRSystemLike | null;\n\n private derived: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private overrides: Partial<AdapterCapabilities> = {};\n private capabilities: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private sessionState: SessionState = \"none\";\n private mode: SessionMode | null = null;\n private unsubscribeVisibility: Unsubscribe | undefined;\n private boundSession: IWSDKSessionLike | null = null;\n\n /**\n * Pre-bound, because `removeEventListener` has to be handed the same function\n * `addEventListener` got.\n */\n private readonly onInputSourcesChange: IWSDKSessionEventListener = () => {\n this.refreshCapabilities();\n };\n\n /** Session lifecycle over the world's `launchXR` / `exitXR` entry points. */\n public readonly session: SessionFacet = {\n getState: () => this.sessionState,\n getMode: () => (this.sessionState === \"active\" ? this.mode : null),\n isSupported: (mode) => this.checkSupported(mode),\n request: (mode, options) => this.requestSession(mode, options),\n end: () => this.endSession(),\n recentre: () => this.recentrePlayerRig(),\n onStateChange: (listener) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n },\n onVisibilityChange: (listener) => {\n this.visibilityListeners.add(listener);\n return () => {\n this.visibilityListeners.delete(listener);\n };\n },\n };\n\n public constructor(\n private readonly world: IWSDKWorldLike,\n options: IWSDKAdapterOptions = {},\n ) {\n this.xrSystem = options.xrSystem === undefined ? defaultXRSystem() : options.xrSystem;\n this.unsubscribeVisibility = world.visibilityState.subscribe?.((value) => {\n this.handleVisibilityChange(value);\n });\n\n this.bindSession(this.world.session ?? null);\n this.derived = deriveCapabilities(this.world.session);\n this.capabilities = this.derived;\n this.sessionState = this.hasSession() ? \"active\" : \"none\";\n }\n\n public onFrame(listener: FrameListener): Unsubscribe {\n this.frameListeners.add(listener);\n return () => {\n this.frameListeners.delete(listener);\n };\n }\n\n public getCapabilities(): AdapterCapabilities {\n return this.capabilities;\n }\n\n public onCapabilitiesChange(listener: CapabilitiesListener): Unsubscribe {\n this.capabilitiesListeners.add(listener);\n return () => {\n this.capabilitiesListeners.delete(listener);\n };\n }\n\n /** The IWSDK `World` this adapter is bound to. */\n public getWorld(): IWSDKWorldLike {\n return this.world;\n }\n\n /**\n * Re-read the live session and publish any capability change. The adapter\n * does this itself whenever the visibility signal fires and whenever the\n * session's input sources change; call it directly on a host whose signal has\n * no `subscribe`, or after the host enables a feature mid-session.\n */\n public refreshCapabilities(): void {\n this.bindSession(this.world.session ?? null);\n this.derived = deriveCapabilities(this.world.session);\n this.publishCapabilities();\n }\n\n /**\n * Force capability flags regardless of what the session reports. Overrides\n * are a layer on top of the derived values: they win for as long as they are\n * set, survive every later derivation, and are dropped only by\n * {@link IWSDKAdapter.clearCapabilityOverrides} or\n * {@link IWSDKAdapter.dispose}. Subscribers are notified if the effective\n * capabilities changed.\n */\n public setCapabilities(capabilities: Partial<AdapterCapabilities>): void {\n this.overrides = { ...this.overrides, ...capabilities };\n this.publishCapabilities();\n }\n\n /** Drop every manual override and fall back to the derived capabilities. */\n public clearCapabilityOverrides(): void {\n this.overrides = {};\n this.publishCapabilities();\n }\n\n /** Called once per frame by the ECS bridge system. */\n public emitFrame(timestamp: number, delta: number, frame?: number): void {\n const info: FrameInfo = frame === undefined ? { timestamp, delta } : { timestamp, delta, frame };\n this.frameListeners.forEach((listener) => listener(info));\n }\n\n /**\n * Release everything the adapter holds: the visibility subscription, the\n * session's own listener, any in-flight session wait, the manual overrides\n * and every listener.\n */\n public dispose(): void {\n this.unsubscribeVisibility?.();\n this.unsubscribeVisibility = undefined;\n this.unbindSession();\n\n for (const wait of Array.from(this.pendingWaits)) {\n this.settleWait(wait, false);\n }\n\n this.overrides = {};\n this.frameListeners.clear();\n this.capabilitiesListeners.clear();\n this.stateListeners.clear();\n this.visibilityListeners.clear();\n }\n\n private hasSession(): boolean {\n return Boolean(this.world.session);\n }\n\n /**\n * Follow one session's `inputsourceschange` event, so `handTracking` updates\n * when the player picks the controllers up or puts them down. IWSDK's world\n * carries a real `XRSession`, which raises the event; a host that reports a\n * session without listener methods is bound all the same and simply never\n * pushes, which is what `refreshCapabilities()` remains for.\n */\n private bindSession(session: IWSDKSessionLike | null): void {\n if (session === this.boundSession) {\n return;\n }\n\n this.unbindSession();\n this.boundSession = session;\n session?.addEventListener?.(\"inputsourceschange\", this.onInputSourcesChange);\n }\n\n private unbindSession(): void {\n this.boundSession?.removeEventListener?.(\"inputsourceschange\", this.onInputSourcesChange);\n this.boundSession = null;\n }\n\n private publishCapabilities(): void {\n const next: AdapterCapabilities = { ...this.derived, ...this.overrides };\n const current = this.capabilities;\n\n if (\n next.immersive === current.immersive &&\n next.handTracking === current.handTracking &&\n next.planeDetection === current.planeDetection &&\n next.passthrough === current.passthrough &&\n next.environmentBlendMode === current.environmentBlendMode\n ) {\n return;\n }\n\n this.capabilities = next;\n this.capabilitiesListeners.forEach((listener) => listener(next));\n }\n\n private handleVisibilityChange(value: unknown): void {\n this.refreshCapabilities();\n this.syncSessionState();\n this.checkWaits();\n\n const visibility = toSessionVisibility(value);\n this.visibilityListeners.forEach((listener) => listener(visibility));\n }\n\n /**\n * Follow the world when nothing local is mid-transition. `request()` and\n * `end()` own the state while they run, so the signal does not fight them.\n */\n private syncSessionState(): void {\n if (this.sessionState === \"requesting\" || this.sessionState === \"ending\") {\n return;\n }\n\n this.setSessionState(this.hasSession() ? \"active\" : \"none\");\n }\n\n /** Answers `isSupported()` off `navigator.xr`; never rejects. */\n private async checkSupported(mode: SessionMode): Promise<boolean> {\n const system = this.xrSystem;\n\n if (!system) {\n return false;\n }\n\n try {\n return await system.isSessionSupported(mode);\n } catch {\n return false;\n }\n }\n\n /**\n * Move the player rig so the head's current floor position and yaw become\n * the origin, leaving the head's pose local to the rig untouched. Does\n * nothing when the world carries no rig, or no head entity, to move.\n */\n private recentrePlayerRig(): void {\n const rig = playerRig(this.world.player);\n const head = playerHead(this.world.player, this.world.playerSpaceEntities);\n\n if (!rig || !head) {\n return;\n }\n\n const next = recentreRig(\n {\n position: [rig.position.x, rig.position.y, rig.position.z],\n orientation: [rig.quaternion.x, rig.quaternion.y, rig.quaternion.z, rig.quaternion.w],\n },\n {\n position: [head.position.x, head.position.y, head.position.z],\n orientation: [head.quaternion.x, head.quaternion.y, head.quaternion.z, head.quaternion.w],\n },\n );\n\n rig.position.x = next.position[0];\n rig.position.y = next.position[1];\n rig.position.z = next.position[2];\n rig.quaternion.x = next.orientation[0];\n rig.quaternion.y = next.orientation[1];\n rig.quaternion.z = next.orientation[2];\n rig.quaternion.w = next.orientation[3];\n }\n\n private async requestSession(\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n if (this.sessionState === \"active\") {\n if (this.mode === mode) {\n return { ok: true };\n }\n\n // End-and-request: a world with no `exitXR` cannot end its own session,\n // so the switch is refused outright and the live session is left alone.\n if (!this.world.exitXR) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n await this.endSession();\n }\n\n const launch = this.world.launchXR;\n\n if (!launch) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;\n this.setSessionState(\"requesting\");\n\n const { features } = toIWSDKFeatures(options);\n\n try {\n // IWSDK reads `XROptions.sessionMode`; its `SessionMode` enum values are the\n // WebXR mode strings, so the facet mode passes through unchanged. The\n // request's feature strings become IWSDK's structured flags; `launchXR`\n // merges what it is given over the world's `xrDefaults`, so a request\n // that names no features leaves the app's defaults alone.\n launch.call(this.world, { sessionMode: mode, ...(features ? { features } : {}) });\n } catch (error) {\n this.setSessionState(\"none\");\n return { ok: false, reason: \"unsupported\", error };\n }\n\n const started = await this.waitFor(() => this.hasSession(), timeoutMs);\n\n if (!started) {\n this.setSessionState(\"none\");\n return { ok: false, reason: \"timeout\" };\n }\n\n this.mode = mode;\n this.setSessionState(\"active\");\n this.refreshCapabilities();\n return { ok: true };\n }\n\n private async endSession(): Promise<void> {\n if (this.sessionState === \"none\") {\n return;\n }\n\n const exit = this.world.exitXR;\n this.setSessionState(\"ending\");\n\n if (exit) {\n exit.call(this.world);\n }\n\n await this.waitFor(() => !this.hasSession(), DEFAULT_SESSION_TIMEOUT_MS);\n this.setSessionState(\"none\");\n this.refreshCapabilities();\n }\n\n /**\n * Resolve `true` once `predicate` holds, `false` on timeout. The visibility\n * signal drives this where the host has one; the interval is the fallback for\n * a host whose signal does not push, and for a session that appears without\n * a visibility transition.\n */\n private waitFor(predicate: () => boolean, timeoutMs: number): Promise<boolean> {\n if (predicate()) {\n return Promise.resolve(true);\n }\n\n return new Promise<boolean>((resolve) => {\n const wait: PendingWait = {\n predicate,\n resolve,\n timer: setTimeout(() => this.settleWait(wait, false), timeoutMs),\n interval: setInterval(() => this.checkWaits(), SESSION_POLL_INTERVAL_MS),\n };\n\n this.pendingWaits.add(wait);\n });\n }\n\n private checkWaits(): void {\n for (const wait of Array.from(this.pendingWaits)) {\n if (wait.predicate()) {\n this.settleWait(wait, true);\n }\n }\n }\n\n private settleWait(wait: PendingWait, settled: boolean): void {\n this.pendingWaits.delete(wait);\n clearTimeout(wait.timer);\n clearInterval(wait.interval);\n wait.resolve(settled);\n }\n\n private setSessionState(state: SessionState): void {\n if (this.sessionState === state) {\n return;\n }\n\n this.sessionState = state;\n\n if (state === \"none\") {\n this.mode = null;\n }\n\n this.stateListeners.forEach((listener) => listener(state));\n }\n}\n"]}
@@ -108,21 +108,41 @@ export interface IWSDKObject3DLike {
108
108
  quaternion: IWSDKQuaternionLike;
109
109
  }
110
110
  /**
111
- * IWSDK's player rig entity. `object3D` is the rig's own world transform -
111
+ * IWSDK 0.5's player rig entity. `object3D` is the rig's own world transform -
112
112
  * what `recentre()` moves. Absent on a host that carries no rig at all.
113
113
  */
114
- export interface IWSDKPlayerLike {
114
+ export interface IWSDKPlayerEntityLike {
115
115
  readonly object3D?: IWSDKObject3DLike;
116
116
  }
117
+ /**
118
+ * IWSDK 1.0's player rig: `world.player` is the `XROrigin` itself, an
119
+ * `Object3D` (its own `position` and `quaternion` are what `recentre()`
120
+ * moves), with the head as its `head` child. The head's pose is local to the
121
+ * rig, read by `recentre()` and never written.
122
+ */
123
+ export interface IWSDKXROriginLike extends IWSDKObject3DLike {
124
+ readonly head?: IWSDKObject3DLike;
125
+ }
126
+ /**
127
+ * Either shape IWSDK has given `world.player`: the 0.5 entity carrying an
128
+ * `object3D`, or the 1.0 `XROrigin` that IS the object. `recentre()` reads
129
+ * both (`playerRig`, `playerHead`).
130
+ */
131
+ export type IWSDKPlayerLike = IWSDKPlayerEntityLike | IWSDKXROriginLike;
117
132
  /**
118
133
  * IWSDK's named player-space entities. `head.object3D` is the head's pose
119
- * local to the player rig - read by `recentre()`, never written.
134
+ * local to the player rig - read by `recentre()`, never written. IWSDK 1.0
135
+ * keeps them (`world.playerSpaceEntities.head`) beside `world.player.head`.
120
136
  */
121
137
  export interface IWSDKPlayerSpaceEntitiesLike {
122
138
  readonly head: {
123
139
  readonly object3D?: IWSDKObject3DLike;
124
140
  };
125
141
  }
142
+ /** The rig object `recentre()` moves, from either player shape, or undefined without a rig. */
143
+ export declare function playerRig(player: IWSDKPlayerLike | undefined): IWSDKObject3DLike | undefined;
144
+ /** The head pose `recentre()` reads: the named head entity, else the 1.0 rig's `head` child. */
145
+ export declare function playerHead(player: IWSDKPlayerLike | undefined, playerSpaceEntities: IWSDKPlayerSpaceEntitiesLike | undefined): IWSDKObject3DLike | undefined;
126
146
  /** The slice of `navigator.xr` the session facet's `isSupported` reads. */
127
147
  export interface IWSDKXRSystemLike {
128
148
  isSessionSupported(mode: string): Promise<boolean>;
@@ -11,5 +11,23 @@
11
11
  * Every member added here is optional, so a world that only carries the
12
12
  * visibility signal - the shape the 1.0.x bridge accepted - still type-checks.
13
13
  */
14
- export {};
14
+ /** The rig object `recentre()` moves, from either player shape, or undefined without a rig. */
15
+ export function playerRig(player) {
16
+ if (!player)
17
+ return undefined;
18
+ if ("object3D" in player && player.object3D)
19
+ return player.object3D;
20
+ if ("position" in player && "quaternion" in player)
21
+ return player;
22
+ return undefined;
23
+ }
24
+ /** The head pose `recentre()` reads: the named head entity, else the 1.0 rig's `head` child. */
25
+ export function playerHead(player, playerSpaceEntities) {
26
+ const named = playerSpaceEntities?.head?.object3D;
27
+ if (named)
28
+ return named;
29
+ if (player && "head" in player)
30
+ return player.head;
31
+ return undefined;
32
+ }
15
33
  //# sourceMappingURL=iwsdk-host.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"iwsdk-host.js","sourceRoot":"","sources":["../src/iwsdk-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG","sourcesContent":["/**\n * Structural contracts for the slice of `@iwsdk/core` this shim touches.\n *\n * Mirrors the approach the three.js and Babylon.js bridges take with their\n * engine packages: the shim never imports `@iwsdk/core` directly, so it builds\n * and unit-tests with no IWSDK / WebXR / headset present, and any 0.4.x IWSDK\n * works without bumping this package. Consumers pass the real `World`,\n * `createSystem` and `VisibilityState.Visible` value through\n * {@link makeServiceBridgeSystem}.\n *\n * Every member added here is optional, so a world that only carries the\n * visibility signal - the shape the 1.0.x bridge accepted - still type-checks.\n */\n\nimport type {\n CapabilityInputSourceLike,\n CapabilitySessionLike,\n} from \"@realitycollective/service-framework\";\n\n/**\n * IWSDK exposes reactive values as `{ value }` signals. The bridge only reads\n * `.value`; the adapter also subscribes when the signal supports it, which is\n * how it learns that a session came or went without polling every frame.\n */\nexport interface IWSDKSignalLike<TValue> {\n readonly value: TValue;\n /** Optional push notification. Returns an unsubscribe handle. */\n subscribe?(listener: (value: TValue) => void): () => void;\n}\n\n/** One entry of `XRSession.inputSources`; `hand` is set for a tracked hand. */\nexport type IWSDKInputSourceLike = CapabilityInputSourceLike;\n\n/** The event an `XRSession` raises that the adapter listens for. */\nexport type IWSDKSessionEventType = \"inputsourceschange\";\n\n/** Session event callback. The adapter reads the session, not the event. */\nexport type IWSDKSessionEventListener = (event?: unknown) => void;\n\n/**\n * The slice of the live `XRSession` the adapter reads to derive capabilities.\n * It is the core's {@link CapabilitySessionLike} with `inputSources` required,\n * because an IWSDK world always carries the collection, and the adapter hands\n * the session straight to the core's `deriveCapabilities`.\n *\n * The two listener members are what IWSDK's own session carries, because it is\n * a real `XRSession`. They are optional all the same, so a world faked in a\n * test still satisfies the type without them, and the adapter guards for their\n * absence rather than assuming a full session.\n */\nexport interface IWSDKSessionLike extends CapabilitySessionLike {\n readonly inputSources: Iterable<IWSDKInputSourceLike>;\n addEventListener?(type: IWSDKSessionEventType, listener: IWSDKSessionEventListener): void;\n removeEventListener?(type: IWSDKSessionEventType, listener: IWSDKSessionEventListener): void;\n}\n\n/**\n * The slice of an IWSDK `World` the bridge and adapter read: the visibility\n * signal, the live session, and the session entry points.\n */\n/**\n * IWSDK's `FeatureFlag`. `true` requests the feature as optional,\n * `{ required: true }` as required, and `false` or absent does not request it.\n */\nexport type IWSDKFeatureFlagLike = boolean | { readonly required?: boolean };\n\n/**\n * IWSDK's `DepthSensingFlag` - a {@link IWSDKFeatureFlagLike} that can also\n * carry the depth usage and format preferences.\n */\nexport type IWSDKDepthSensingFlagLike =\n | boolean\n | {\n readonly required?: boolean;\n readonly usage?: \"cpu-optimized\" | \"gpu-optimized\";\n readonly format?: \"luminance-alpha\" | \"float32\";\n };\n\n/**\n * IWSDK's `XRFeatureOptions`. IWSDK takes structured flags rather than the\n * WebXR feature strings, which is why the adapter maps between the two.\n */\nexport interface IWSDKXRFeatureOptionsLike {\n readonly handTracking?: IWSDKFeatureFlagLike;\n readonly anchors?: IWSDKFeatureFlagLike;\n readonly hitTest?: IWSDKFeatureFlagLike;\n readonly planeDetection?: IWSDKFeatureFlagLike;\n readonly meshDetection?: IWSDKFeatureFlagLike;\n readonly depthSensing?: IWSDKDepthSensingFlagLike;\n readonly layers?: IWSDKFeatureFlagLike;\n readonly unbounded?: IWSDKFeatureFlagLike;\n}\n\n/**\n * The slice of IWSDK `XROptions` the session facet passes to `launchXR`.\n * `sessionMode` carries the WebXR mode string (\"immersive-vr\", \"immersive-ar\",\n * \"inline\"), which is also the value of IWSDK's `SessionMode` enum members.\n * `launchXR` merges what it is given over the world's `xrDefaults`, so anything\n * left out here keeps the app's default.\n */\nexport interface IWSDKXROptionsLike {\n readonly sessionMode?: string;\n readonly features?: IWSDKXRFeatureOptionsLike;\n}\n\n/** A three.js `Vector3`-shaped position, in metres. */\nexport interface IWSDKVector3Like {\n x: number;\n y: number;\n z: number;\n}\n\n/** A three.js `Quaternion`-shaped orientation. */\nexport interface IWSDKQuaternionLike {\n x: number;\n y: number;\n z: number;\n w: number;\n}\n\n/** The slice of a three.js `Object3D` `recentre()` reads and writes. */\nexport interface IWSDKObject3DLike {\n position: IWSDKVector3Like;\n quaternion: IWSDKQuaternionLike;\n}\n\n/**\n * IWSDK's player rig entity. `object3D` is the rig's own world transform -\n * what `recentre()` moves. Absent on a host that carries no rig at all.\n */\nexport interface IWSDKPlayerLike {\n readonly object3D?: IWSDKObject3DLike;\n}\n\n/**\n * IWSDK's named player-space entities. `head.object3D` is the head's pose\n * local to the player rig - read by `recentre()`, never written.\n */\nexport interface IWSDKPlayerSpaceEntitiesLike {\n readonly head: { readonly object3D?: IWSDKObject3DLike };\n}\n\n/** The slice of `navigator.xr` the session facet's `isSupported` reads. */\nexport interface IWSDKXRSystemLike {\n isSessionSupported(mode: string): Promise<boolean>;\n}\n\nexport interface IWSDKWorldLike<TVisibility = unknown> {\n readonly visibilityState: IWSDKSignalLike<TVisibility>;\n /** The live XR session, or null/absent when the app is running in 2D. */\n readonly session?: IWSDKSessionLike | null;\n /** IWSDK's session entry point. Absent on a host that cannot start one. */\n launchXR?(options?: IWSDKXROptionsLike): void;\n /** IWSDK's session exit point. Absent on a host that cannot end one. */\n exitXR?(): void;\n /** The player rig - `recentre()`'s write. Absent on a host with no rig. */\n readonly player?: IWSDKPlayerLike;\n /** Named player-space entities, including the head - `recentre()`'s read. */\n readonly playerSpaceEntities?: IWSDKPlayerSpaceEntitiesLike;\n}\n\n/** The per-frame entry point IWSDK invokes on a registered system. */\nexport interface IWSDKSystemLike {\n update(delta: number, time: number): void;\n}\n\n/** Constructor shape of the base class IWSDK's `createSystem` returns. */\nexport type IWSDKSystemConstructor = new (...args: unknown[]) => IWSDKSystemLike;\n\n/**\n * Structural shape of IWSDK's `createSystem` factory. `createSystem(schema)`\n * returns a base system class that the bridge extends; the bridge passes an\n * empty schema because it queries no ECS components.\n */\nexport type CreateSystemLike = (schema?: Record<string, unknown>) => IWSDKSystemConstructor;\n"]}
1
+ {"version":3,"file":"iwsdk-host.js","sourceRoot":"","sources":["../src/iwsdk-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoJH,+FAA+F;AAC/F,MAAM,UAAU,SAAS,CAAC,MAAmC;IAC3D,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,IAAI,UAAU,IAAI,MAAM,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC;IACpE,IAAI,UAAU,IAAI,MAAM,IAAI,YAAY,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAClE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,UAAU,CACxB,MAAmC,EACnC,mBAA6D;IAE7D,MAAM,KAAK,GAAG,mBAAmB,EAAE,IAAI,EAAE,QAAQ,CAAC;IAClD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACnD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["/**\n * Structural contracts for the slice of `@iwsdk/core` this shim touches.\n *\n * Mirrors the approach the three.js and Babylon.js bridges take with their\n * engine packages: the shim never imports `@iwsdk/core` directly, so it builds\n * and unit-tests with no IWSDK / WebXR / headset present, and any 0.4.x IWSDK\n * works without bumping this package. Consumers pass the real `World`,\n * `createSystem` and `VisibilityState.Visible` value through\n * {@link makeServiceBridgeSystem}.\n *\n * Every member added here is optional, so a world that only carries the\n * visibility signal - the shape the 1.0.x bridge accepted - still type-checks.\n */\n\nimport type {\n CapabilityInputSourceLike,\n CapabilitySessionLike,\n} from \"@realitycollective/service-framework\";\n\n/**\n * IWSDK exposes reactive values as `{ value }` signals. The bridge only reads\n * `.value`; the adapter also subscribes when the signal supports it, which is\n * how it learns that a session came or went without polling every frame.\n */\nexport interface IWSDKSignalLike<TValue> {\n readonly value: TValue;\n /** Optional push notification. Returns an unsubscribe handle. */\n subscribe?(listener: (value: TValue) => void): () => void;\n}\n\n/** One entry of `XRSession.inputSources`; `hand` is set for a tracked hand. */\nexport type IWSDKInputSourceLike = CapabilityInputSourceLike;\n\n/** The event an `XRSession` raises that the adapter listens for. */\nexport type IWSDKSessionEventType = \"inputsourceschange\";\n\n/** Session event callback. The adapter reads the session, not the event. */\nexport type IWSDKSessionEventListener = (event?: unknown) => void;\n\n/**\n * The slice of the live `XRSession` the adapter reads to derive capabilities.\n * It is the core's {@link CapabilitySessionLike} with `inputSources` required,\n * because an IWSDK world always carries the collection, and the adapter hands\n * the session straight to the core's `deriveCapabilities`.\n *\n * The two listener members are what IWSDK's own session carries, because it is\n * a real `XRSession`. They are optional all the same, so a world faked in a\n * test still satisfies the type without them, and the adapter guards for their\n * absence rather than assuming a full session.\n */\nexport interface IWSDKSessionLike extends CapabilitySessionLike {\n readonly inputSources: Iterable<IWSDKInputSourceLike>;\n addEventListener?(type: IWSDKSessionEventType, listener: IWSDKSessionEventListener): void;\n removeEventListener?(type: IWSDKSessionEventType, listener: IWSDKSessionEventListener): void;\n}\n\n/**\n * The slice of an IWSDK `World` the bridge and adapter read: the visibility\n * signal, the live session, and the session entry points.\n */\n/**\n * IWSDK's `FeatureFlag`. `true` requests the feature as optional,\n * `{ required: true }` as required, and `false` or absent does not request it.\n */\nexport type IWSDKFeatureFlagLike = boolean | { readonly required?: boolean };\n\n/**\n * IWSDK's `DepthSensingFlag` - a {@link IWSDKFeatureFlagLike} that can also\n * carry the depth usage and format preferences.\n */\nexport type IWSDKDepthSensingFlagLike =\n | boolean\n | {\n readonly required?: boolean;\n readonly usage?: \"cpu-optimized\" | \"gpu-optimized\";\n readonly format?: \"luminance-alpha\" | \"float32\";\n };\n\n/**\n * IWSDK's `XRFeatureOptions`. IWSDK takes structured flags rather than the\n * WebXR feature strings, which is why the adapter maps between the two.\n */\nexport interface IWSDKXRFeatureOptionsLike {\n readonly handTracking?: IWSDKFeatureFlagLike;\n readonly anchors?: IWSDKFeatureFlagLike;\n readonly hitTest?: IWSDKFeatureFlagLike;\n readonly planeDetection?: IWSDKFeatureFlagLike;\n readonly meshDetection?: IWSDKFeatureFlagLike;\n readonly depthSensing?: IWSDKDepthSensingFlagLike;\n readonly layers?: IWSDKFeatureFlagLike;\n readonly unbounded?: IWSDKFeatureFlagLike;\n}\n\n/**\n * The slice of IWSDK `XROptions` the session facet passes to `launchXR`.\n * `sessionMode` carries the WebXR mode string (\"immersive-vr\", \"immersive-ar\",\n * \"inline\"), which is also the value of IWSDK's `SessionMode` enum members.\n * `launchXR` merges what it is given over the world's `xrDefaults`, so anything\n * left out here keeps the app's default.\n */\nexport interface IWSDKXROptionsLike {\n readonly sessionMode?: string;\n readonly features?: IWSDKXRFeatureOptionsLike;\n}\n\n/** A three.js `Vector3`-shaped position, in metres. */\nexport interface IWSDKVector3Like {\n x: number;\n y: number;\n z: number;\n}\n\n/** A three.js `Quaternion`-shaped orientation. */\nexport interface IWSDKQuaternionLike {\n x: number;\n y: number;\n z: number;\n w: number;\n}\n\n/** The slice of a three.js `Object3D` `recentre()` reads and writes. */\nexport interface IWSDKObject3DLike {\n position: IWSDKVector3Like;\n quaternion: IWSDKQuaternionLike;\n}\n\n/**\n * IWSDK 0.5's player rig entity. `object3D` is the rig's own world transform -\n * what `recentre()` moves. Absent on a host that carries no rig at all.\n */\nexport interface IWSDKPlayerEntityLike {\n readonly object3D?: IWSDKObject3DLike;\n}\n\n/**\n * IWSDK 1.0's player rig: `world.player` is the `XROrigin` itself, an\n * `Object3D` (its own `position` and `quaternion` are what `recentre()`\n * moves), with the head as its `head` child. The head's pose is local to the\n * rig, read by `recentre()` and never written.\n */\nexport interface IWSDKXROriginLike extends IWSDKObject3DLike {\n readonly head?: IWSDKObject3DLike;\n}\n\n/**\n * Either shape IWSDK has given `world.player`: the 0.5 entity carrying an\n * `object3D`, or the 1.0 `XROrigin` that IS the object. `recentre()` reads\n * both (`playerRig`, `playerHead`).\n */\nexport type IWSDKPlayerLike = IWSDKPlayerEntityLike | IWSDKXROriginLike;\n\n/**\n * IWSDK's named player-space entities. `head.object3D` is the head's pose\n * local to the player rig - read by `recentre()`, never written. IWSDK 1.0\n * keeps them (`world.playerSpaceEntities.head`) beside `world.player.head`.\n */\nexport interface IWSDKPlayerSpaceEntitiesLike {\n readonly head: { readonly object3D?: IWSDKObject3DLike };\n}\n\n/** The rig object `recentre()` moves, from either player shape, or undefined without a rig. */\nexport function playerRig(player: IWSDKPlayerLike | undefined): IWSDKObject3DLike | undefined {\n if (!player) return undefined;\n if (\"object3D\" in player && player.object3D) return player.object3D;\n if (\"position\" in player && \"quaternion\" in player) return player;\n return undefined;\n}\n\n/** The head pose `recentre()` reads: the named head entity, else the 1.0 rig's `head` child. */\nexport function playerHead(\n player: IWSDKPlayerLike | undefined,\n playerSpaceEntities: IWSDKPlayerSpaceEntitiesLike | undefined,\n): IWSDKObject3DLike | undefined {\n const named = playerSpaceEntities?.head?.object3D;\n if (named) return named;\n if (player && \"head\" in player) return player.head;\n return undefined;\n}\n\n/** The slice of `navigator.xr` the session facet's `isSupported` reads. */\nexport interface IWSDKXRSystemLike {\n isSessionSupported(mode: string): Promise<boolean>;\n}\n\nexport interface IWSDKWorldLike<TVisibility = unknown> {\n readonly visibilityState: IWSDKSignalLike<TVisibility>;\n /** The live XR session, or null/absent when the app is running in 2D. */\n readonly session?: IWSDKSessionLike | null;\n /** IWSDK's session entry point. Absent on a host that cannot start one. */\n launchXR?(options?: IWSDKXROptionsLike): void;\n /** IWSDK's session exit point. Absent on a host that cannot end one. */\n exitXR?(): void;\n /** The player rig - `recentre()`'s write. Absent on a host with no rig. */\n readonly player?: IWSDKPlayerLike;\n /** Named player-space entities, including the head - `recentre()`'s read. */\n readonly playerSpaceEntities?: IWSDKPlayerSpaceEntitiesLike;\n}\n\n/** The per-frame entry point IWSDK invokes on a registered system. */\nexport interface IWSDKSystemLike {\n update(delta: number, time: number): void;\n}\n\n/** Constructor shape of the base class IWSDK's `createSystem` returns. */\nexport type IWSDKSystemConstructor = new (...args: unknown[]) => IWSDKSystemLike;\n\n/**\n * Structural shape of IWSDK's `createSystem` factory. `createSystem(schema)`\n * returns a base system class that the bridge extends; the bridge passes an\n * empty schema because it queries no ECS components.\n */\nexport type CreateSystemLike = (schema?: Record<string, unknown>) => IWSDKSystemConstructor;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realitycollective/service-framework-iwsdk",
3
- "version": "1.0.2-preview.2",
3
+ "version": "1.0.3-preview.0",
4
4
  "description": "Meta IWSDK (WebXR) frame-source bindings for the Reality Collective TypeScript Service Framework.",
5
5
  "author": "Reality Collective",
6
6
  "license": "MIT",
@@ -25,7 +25,9 @@
25
25
  "meta",
26
26
  "xr",
27
27
  "realitycollective",
28
- "typescript"
28
+ "typescript",
29
+ "runtime-adapter",
30
+ "session"
29
31
  ],
30
32
  "repository": {
31
33
  "type": "git",
@@ -36,7 +38,7 @@
36
38
  "url": "https://github.com/realitycollective/com.realitycollective.service-framework.ts/issues"
37
39
  },
38
40
  "dependencies": {
39
- "@realitycollective/service-framework": "^1.0.2-preview.2"
41
+ "@realitycollective/service-framework": "^1.0.3-preview.0"
40
42
  },
41
43
  "scripts": {
42
44
  "build": "tsc -p tsconfig.build.json",