@realitycollective/service-framework-client 1.0.1 → 1.0.2-preview.1
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 +33 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ 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]
|
|
8
|
+
|
|
9
|
+
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
|
+
|
|
11
|
+
### Added
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **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.
|
|
27
|
+
- **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.
|
|
28
|
+
- `@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.
|
|
29
|
+
- `@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.
|
|
30
|
+
- `@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.
|
|
31
|
+
- `@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.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- The IWSDK bridge reported `timestamp` in seconds. IWSDK calls a system's `update(delta, time)` with `time` as elapsed seconds from three.js `Clock`, and the bridge passed it straight into `FrameInfo.timestamp` and `LifecycleContext.timestamp`, which are milliseconds on every other platform. A service reading `timestamp` got a value a thousand times smaller on IWSDK, which is a platform conditional a client should never need. The bridge now converts it, and `renderTickContractCases()` fails a binding that does not. The `FrameInfo` doc no longer cites IWSDK as the source of its unit. Code that read the IWSDK timestamp as seconds must now read milliseconds.
|
|
36
|
+
- `createWebHostIO().gunzip` left the stream's write-side error unhandled when given bytes that are not gzip, so a host logged an unhandled rejection besides the rejection the caller received. It now rejects once. The new `HostIO` suite found it.
|
|
37
|
+
- `TimerScheduler` counted frames on one counter shared by all four of its channels, and named `source` after the channel. A service counting render frames therefore saw gaps that depended on how often the other channels fired, where every engine binding counts 1, 2, 3. Each channel now counts its own ticks from 1, and every context names `"timer"` as its source, as a binding names itself. The timer now runs the shared `renderTickContractCases()` suite, which fails on the old shared counter.
|
|
38
|
+
- `ServiceManager` no longer needs a global `AbortController`. Every service record created one unconditionally to hand the service its `signal`, so a host without it failed to boot any profile before client code ran. Hermes, the engine a native host embeds, is such a host: `ReferenceError: Property 'AbortController' doesn't exist`. The manager now uses the global when it exists and otherwise falls back to a small internal controller. Its signal carries `aborted`, `reason`, `onabort`, `addEventListener("abort")` and `removeEventListener`, and `abort()` fires once. Web and Node behaviour is unchanged, because both have the global. `ServiceActivationContext.signal` is still typed `AbortSignal`, and nothing was added to the public exports.
|
|
39
|
+
|
|
7
40
|
## [1.0.1] - 2026-09-17
|
|
8
41
|
|
|
9
42
|
Packaging, tooling and documentation, plus additive runtime API: the engine-free adapter contracts move into the core, the IWSDK adapter derives capabilities from the live session and gains a session facet, and the three.js and Babylon.js packages gain runtime adapters so a three.js, Babylon or desktop app reaches the same seam. Nothing that existed was removed, and one interface changed shape: `AdapterCapabilities` gains a required `environmentBlendMode` key, so code that builds the whole object by hand - an adapter of your own, or a test fake - has one more field to supply. Code that only reads capabilities is unaffected.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/service-framework-client",
|
|
3
|
-
"version": "1.0.1",
|
|
3
|
+
"version": "1.0.2-preview.1",
|
|
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.1",
|
|
40
|
-
"@realitycollective/service-framework-react": "^1.0.1",
|
|
41
|
-
"@realitycollective/service-framework-three": "^1.0.1"
|
|
39
|
+
"@realitycollective/service-framework": "^1.0.2-preview.1",
|
|
40
|
+
"@realitycollective/service-framework-react": "^1.0.2-preview.1",
|
|
41
|
+
"@realitycollective/service-framework-three": "^1.0.2-preview.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": "^19.2.0"
|