@realitycollective/service-framework-client 1.0.2-preview.0 → 1.0.2-preview.2
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 +19 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,13 +10,31 @@ A native platform, beside IWSDK, three.js and Babylon.js, and byte I/O that work
|
|
|
10
10
|
|
|
11
11
|
### Added
|
|
12
12
|
|
|
13
|
+
- `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
|
+
- `@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.
|
|
13
15
|
- `@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.
|
|
14
16
|
- `HostIO` in the core, with `createWebHostIO()` over `fetch` and `DecompressionStream`. A service that reads assets takes a `HostIO` and never reaches for a global, so the same code runs on the web and on a native host. The globals are read when a method is called, so a host without them fails only if it uses them. Helpers that work on both: `fetchText`, `fetchJson`, `fetchMaybeGzipped` (gunzips only bytes that carry the gzip magic number, since hosts differ on whether they undo `Content-Encoding`), `decodeUtf8` (no `TextDecoder`, which Hermes lacks), `isGzip` and `concatBytes`. This is transport only. Decoding images, audio and models stays with the engine.
|
|
15
17
|
- A "Host requirements" section in the core package README. It names every global the core expects from its host beyond the ECMAScript language: `setTimeout` and `clearTimeout` for `resolveAsync`, `waitUntilInitialized` and the mock adapter's session request, and `setInterval` and `clearInterval` for a `TimerScheduler` built without injected timers. It names the two globals used only when present, `AbortController` (with a built-in fallback) and the `fetch` and `DecompressionStream` behind `createWebHostIO()`, and states that nothing else is assumed. A host that embeds a bare engine, such as Hermes in a native app, can now supply exactly that list. `test/host-requirements.test.ts` checks the table against the source, so a new host global fails the suite until it is listed.
|
|
16
18
|
- The capability override rule, stated in the README and on the `RuntimeAdapter` doc comment. Every adapter exposes `setCapabilities(partial)` as a sticky override over what it derives, and an adapter that derives also exposes `clearCapabilityOverrides()`. Every in-repo adapter already did, and every conformance driver already wired its `capabilities` hook to it. The rule was never written down, so an adapter written elsewhere that only derived from its host failed the capability-change case. The method stays off the `RuntimeAdapter` interface, because services must not call it. The same test checks that every in-repo adapter still exposes it.
|
|
17
|
-
|
|
18
19
|
- `hostIOContractCases()` and `renderTickContractCases()`, shared conformance suites shipped as data, like `runtimeAdapterContractCases()`. Clients only ever use the core: a service reads assets through `HostIO` and subscribes to `renderTick` on the core scheduler, and never asks which platform is underneath. That only holds if every platform behaves the same way, so every platform now runs the same checks. `hostIOContractCases()` runs against `createWebHostIO()` and `createNativeHostIO()`: served bytes come back exactly, a missing resource rejects, gunzip restores and rejects non-gzip, and the core helpers read plain and gzipped JSON alike. It needs no `CompressionStream` or `TextEncoder`, so a native app can run it on device. `renderTickContractCases()` runs against `ThreeRenderLoopBridge`, `WebXRRuntimeAdapter`, `BabylonRenderLoopBridge`, `BabylonRuntimeAdapter`, the IWSDK service bridge system and `NativeRuntimeAdapter`: one tick per frame, frames counted from 1, a stable source, and `timestamp` and `deltaTime` in milliseconds.
|
|
19
20
|
- `LifecycleContext` now documents its units: `timestamp` and `deltaTime` in milliseconds, `frame` counted from 1, and `source` naming the binding.
|
|
21
|
+
- `@realitycollective/service-framework-three` and `@realitycollective/service-framework-babylon` - the `manager` option on `WebXRRuntimeAdapter` and `BabylonRuntimeAdapter`, the service manager whose focus and pause signals follow a live session's visibility, as the `manager` option already does on the native adapter. `WebXRFocusSink` and `BabylonFocusSink` name the two methods it needs.
|
|
22
|
+
- `WebXRRuntimeAdapter.tick(timestampMs)` and `BabylonRuntimeAdapter.tick()`, now public. Each is the whole frame step `start()` already bound to the owned loop - the visibility gate, the focus/pause signals, the one frame count, `emitFrame` and `renderTick` - pulled out from under `start()` so a host that owns its OWN loop and will never call `start()` (an XR Blocks app, whose `Core` calls `setAnimationLoop` itself) can drive the exact same step by hand from whatever per-frame hook that host provides, rather than reimplementing the gate. The owned loop and a hand-driven app now share one code path; before this, a hand-driven three.js app could only reach the ungated `emitFrame`, so it had no way to reach the focus gate at all.
|
|
23
|
+
- `SESSION_REFERENCE_SPACE` (`"local-floor"`) in `@realitycollective/service-framework`, the WebXR reference space every binding requests: origin where the session began, +Y up with the floor at y = 0, -Z the initial forward direction. `SessionFacet` gains `getMode()` (the live session's mode, or null), `isSupported(mode)` (answers whether the host could start a mode, without changing state, never rejecting) and `recentre()` (moves the viewer's current floor position and yaw to the origin, y untouched, a no-op with no live session). `request()` now documents "end-and-request": asking for a different mode while a session is active ends the live session first and then requests the new one, walking `active`, `ending`, `none`, `requesting`, `active`; asking for the mode already active is a no-op; a host that cannot end its own session refuses with `{ ok: false, reason: "unsupported" }` and leaves the live session running. The pure helpers `recentreOffset(viewer)` and `recentreRig(rig, headLocal)` compute the rigid transform a platform applies, for a reference-space offset and for a moved player rig respectively, and are unit-tested in the core. `runtimeAdapterContractCases()` gains four cases proving every adapter implements all of this the same way, and `MockRuntimeAdapter` implements it in memory with a configurable `supportedModes` option and a `recentreCount`.
|
|
24
|
+
- `@realitycollective/service-framework-three` - `WebXRRuntimeAdapter.session.isSupported` reads `navigator.xr.isSessionSupported`, resolving `false` with no system or on a rejection; `getMode()` and end-and-request now work as the core documents; `recentre()` offsets the renderer's reference space through the new optional `WebXRManagerLike` members `getReferenceSpace`, `setReferenceSpace` and `getFrame`, building the offset with a `rigidTransform` factory that defaults to `globalThis.XRRigidTransform`, and does nothing when any of these is absent. A session request now also calls `setReferenceSpaceType(SESSION_REFERENCE_SPACE)` when the manager carries one, before handing the session to the renderer.
|
|
25
|
+
- `@realitycollective/service-framework-iwsdk` - `IWSDKAdapter` takes an optional second `IWSDKAdapterOptions` argument with `xrSystem`, the `navigator.xr` slice `session.isSupported` reads (defaulting to the global, `null` in Node). `getMode()` and end-and-request now work as the core documents; a world with no `exitXR` refuses a mode switch with `{ ok: false, reason: "unsupported" }` rather than waiting out the session timeout. `recentre()` moves the player rig through the new optional `IWSDKWorldLike` members `player.object3D` and `playerSpaceEntities.head.object3D`, leaving the head's pose local to the rig untouched, and does nothing when either is absent.
|
|
26
|
+
- `@realitycollective/service-framework-babylon` - `BabylonRuntimeAdapter.session.isSupported` is now public, resolving `false` with no experience or on a rejection rather than throwing. `getMode()` and end-and-request now work as the core documents. `recentre()` moves the XR camera through the new optional `BabylonXRExperienceLike.camera` member (`position`, `rotationQuaternion`, `devicePosition`, `deviceRotationQuaternion`), leaving the device's pose local to the camera untouched, and does nothing when the experience carries no camera.
|
|
27
|
+
- `@realitycollective/service-framework-native` - `NativeSessionInfo.supportedModes`, the fact `isSupported` answers from; absent, it reports both immersive modes supported and `"inline"` unsupported. `NativeHost.recentre`, the app's obligation to offset its own reference space so the head's current x, z and yaw become the origin, y untouched; `recentre()` does nothing without it. `getMode()` and end-and-request now work as the core documents: a different mode while active ends the live session first, and an app that refuses instead of ending reports `onSessionRefused("unsupported")` through the same request rather than the refusal being lost.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **Behaviour change for native apps:** `@realitycollective/service-framework-native` now bridges the native frame loop as IWSDK's `ServiceBridgeSystem` bridges IWSDK's. Services tick only while the session is FOCUSED: a host frame outside focus reaches no frame listener and no `renderTick`, and does not advance the frame count. Given the new `manager` option, the adapter emits `emitFocusChange(focused)` and `emitPauseChange({ paused: !focused })` on every change of focus. Before this a native app ticked its services while the headset was off the face.
|
|
32
|
+
- **Behaviour change for three.js and Babylon.js apps with a live XR session:** `@realitycollective/service-framework-three` and `@realitycollective/service-framework-babylon` now gate their owned loop on the session's own visibility while one is live: a tick that is not `"visible"` reaches no frame listener and no `renderTick`, and does not advance the frame count, exactly as the IWSDK bridge and the native adapter gate on focus. Given the new `manager` option, the adapter emits `emitFocusChange(focused)` and `emitPauseChange({ paused: !focused })` on every change. Unlike IWSDK and native, both packages also serve a desktop page with no session at all, and gating never applies there: with no session the loop ticks exactly as it did before this existed, and a session ending while paused restores focus at once. Before this, a three.js or Babylon app kept rendering into a hidden headset.
|
|
33
|
+
- `@realitycollective/service-framework-native` - a session in the OpenXR `ready` state is live: `immersive` is true there, as the session state already read `active`. For one phase of every session start the two used to disagree.
|
|
34
|
+
- `@realitycollective/service-framework-native` - capabilities re-derive on the `input` slice's source-change signal as well as on session changes, as the IWSDK adapter re-derives on `inputsourceschange`. `handTracking` is true for the `"hand-tracking"` feature, for `XR_EXT_hand_tracking` with system support, or while a hand is among the sources; `planeDetection` is true only for the `"plane-detection"` feature, where it was always false.
|
|
35
|
+
- `@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
|
+
- `@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
|
+
- **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.
|
|
20
38
|
|
|
21
39
|
### Fixed
|
|
22
40
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/service-framework-client",
|
|
3
|
-
"version": "1.0.2-preview.
|
|
3
|
+
"version": "1.0.2-preview.2",
|
|
4
4
|
"description": "Base React and three.js client composition package for the Reality Collective TypeScript Service Framework.",
|
|
5
5
|
"author": "Reality Collective",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"url": "https://github.com/realitycollective/com.realitycollective.service-framework.ts/issues"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@realitycollective/service-framework": "^1.0.2-preview.
|
|
40
|
-
"@realitycollective/service-framework-react": "^1.0.2-preview.
|
|
41
|
-
"@realitycollective/service-framework-three": "^1.0.2-preview.
|
|
39
|
+
"@realitycollective/service-framework": "^1.0.2-preview.2",
|
|
40
|
+
"@realitycollective/service-framework-react": "^1.0.2-preview.2",
|
|
41
|
+
"@realitycollective/service-framework-three": "^1.0.2-preview.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": "^19.2.0"
|