@plasius/gpu-xr 0.1.4 → 0.1.7

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
@@ -10,6 +10,44 @@ All notable changes to this project will be documented in this file.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ - **Added**
14
+ - (placeholder)
15
+
16
+ - **Changed**
17
+ - (placeholder)
18
+
19
+ - **Fixed**
20
+ - (placeholder)
21
+
22
+ - **Security**
23
+ - (placeholder)
24
+
25
+ ## [0.1.7] - 2026-03-14
26
+
27
+ - **Added**
28
+ - Added XR frame-rate capability helpers for current, supported, and
29
+ updatable runtime targets.
30
+ - Added XR performance hints that align immersive sessions with `render` /
31
+ `dag` worker-budget metadata for adaptive performance.
32
+ - Added XR manager methods and store state for observing and updating active
33
+ session frame targets.
34
+ - Added ADR, TDR, and design docs for XR adaptive-performance integration.
35
+
36
+ - **Changed**
37
+ - Clarified README guidance for negotiated XR frame targets and worker-budget
38
+ alignment.
39
+ - Updated GitHub Actions workflows to run JavaScript actions on Node 24,
40
+ refreshed core workflow action versions, and switched Codecov uploads to
41
+ the Codecov CLI.
42
+
43
+ - **Fixed**
44
+ - (placeholder)
45
+
46
+ - **Security**
47
+ - (placeholder)
48
+
49
+ ## [0.1.6] - 2026-03-04
50
+
13
51
  - **Added**
14
52
  - (placeholder)
15
53
 
@@ -65,3 +103,5 @@ All notable changes to this project will be documented in this file.
65
103
  - (placeholder)
66
104
  [0.1.1]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.1
67
105
  [0.1.2]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.2
106
+ [0.1.6]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.6
107
+ [0.1.7]: https://github.com/Plasius-LTD/gpu-xr/releases/tag/v0.1.7
package/README.md CHANGED
@@ -39,14 +39,39 @@ const session = await xr.enterVr({
39
39
  await xr.exitSession();
40
40
  ```
41
41
 
42
+ ## Adaptive Performance Integration
43
+
44
+ `@plasius/gpu-xr` now exposes XR runtime frame-rate hints so
45
+ `@plasius/gpu-performance` can negotiate platform-native frame targets while
46
+ `@plasius/gpu-renderer` and `@plasius/gpu-worker` stay aligned on the `xr`
47
+ worker budget profile.
48
+
49
+ ```js
50
+ import { createXrManager } from "@plasius/gpu-xr";
51
+
52
+ const xr = createXrManager();
53
+ await xr.enterVr();
54
+
55
+ const hint = xr.getPerformanceHint({ preferredFrameRates: [90, 72] });
56
+ console.log(hint.targetFrameRate, hint.workerBudget);
57
+
58
+ await xr.setTargetFrameRate(hint.targetFrameRate);
59
+ ```
60
+
42
61
  ## API
43
62
 
44
63
  - `isXrModeSupported(mode, options)`
45
64
  - `requestXrSession(options)`
65
+ - `readXrFrameRateCapabilities(session, options)`
66
+ - `createXrPerformanceHint(options)`
67
+ - `updateXrTargetFrameRate(session, frameRate)`
46
68
  - `createXrStore(initialState)`
47
69
  - `createXrManager(options)`
48
70
  - `mergeXrSessionInit(base, override)`
49
71
  - `defaultVrSessionInit`
72
+ - `xrWorkerQueueClass`
73
+ - `xrWorkerSchedulerMode`
74
+ - `defaultXrWorkerBudgetProfile`
50
75
 
51
76
  ## Demo
52
77
 
@@ -72,5 +97,8 @@ npm run pack:check
72
97
  ## Files
73
98
 
74
99
  - `src/index.js`: XR runtime/session manager and store.
100
+ - `src/index.d.ts`: public API typings.
75
101
  - `tests/package.test.js`: Unit tests for support probing and lifecycle handling.
76
102
  - `docs/adrs/*`: XR architecture decisions.
103
+ - `docs/tdrs/*`: XR technical direction records.
104
+ - `docs/design/*`: XR integration design notes.
package/dist/index.cjs CHANGED
@@ -20,19 +20,30 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  createXrManager: () => createXrManager,
23
+ createXrPerformanceHint: () => createXrPerformanceHint,
23
24
  createXrStore: () => createXrStore,
24
25
  defaultVrSessionInit: () => defaultVrSessionInit,
26
+ defaultXrWorkerBudgetProfile: () => defaultXrWorkerBudgetProfile,
25
27
  isXrModeSupported: () => isXrModeSupported,
26
28
  mergeXrSessionInit: () => mergeXrSessionInit,
29
+ readXrFrameRateCapabilities: () => readXrFrameRateCapabilities,
27
30
  requestXrSession: () => requestXrSession,
31
+ updateXrTargetFrameRate: () => updateXrTargetFrameRate,
28
32
  xrReferenceSpaceTypes: () => xrReferenceSpaceTypes,
29
- xrSessionModes: () => xrSessionModes
33
+ xrSessionModes: () => xrSessionModes,
34
+ xrWorkerQueueClass: () => xrWorkerQueueClass,
35
+ xrWorkerSchedulerMode: () => xrWorkerSchedulerMode
30
36
  });
31
37
  module.exports = __toCommonJS(index_exports);
32
38
  var DEFAULT_VR_SESSION_INIT = Object.freeze({
33
39
  requiredFeatures: ["local-floor"],
34
40
  optionalFeatures: ["bounded-floor", "hand-tracking", "layers"]
35
41
  });
42
+ var DEFAULT_MODE_FRAME_RATES = Object.freeze({
43
+ inline: 60,
44
+ "immersive-vr": 90,
45
+ "immersive-ar": 72
46
+ });
36
47
  var xrSessionModes = Object.freeze([
37
48
  "inline",
38
49
  "immersive-vr",
@@ -45,6 +56,9 @@ var xrReferenceSpaceTypes = Object.freeze([
45
56
  "bounded-floor",
46
57
  "unbounded"
47
58
  ]);
59
+ var xrWorkerQueueClass = "render";
60
+ var xrWorkerSchedulerMode = "dag";
61
+ var defaultXrWorkerBudgetProfile = "xr";
48
62
  function toStringArray(values) {
49
63
  if (!Array.isArray(values)) {
50
64
  return [];
@@ -54,6 +68,31 @@ function toStringArray(values) {
54
68
  function dedupeStrings(values) {
55
69
  return [...new Set(toStringArray(values))];
56
70
  }
71
+ function readPositiveNumber(value) {
72
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
73
+ }
74
+ function normalizeFrameRates(values) {
75
+ if (!values || typeof values === "string") {
76
+ return Object.freeze([]);
77
+ }
78
+ let collected;
79
+ try {
80
+ collected = Array.from(values);
81
+ } catch {
82
+ return Object.freeze([]);
83
+ }
84
+ return Object.freeze(
85
+ [...new Set(collected.map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0))].sort(
86
+ (left, right) => right - left
87
+ )
88
+ );
89
+ }
90
+ function getDefaultFrameRateForMode(mode) {
91
+ return DEFAULT_MODE_FRAME_RATES[mode] ?? DEFAULT_MODE_FRAME_RATES["immersive-vr"];
92
+ }
93
+ function getWorkerBudgetProfileForMode(mode) {
94
+ return mode === "inline" ? "realtime" : defaultXrWorkerBudgetProfile;
95
+ }
57
96
  function readNavigator(navigatorOverride) {
58
97
  const currentNavigator = navigatorOverride ?? globalThis.navigator;
59
98
  if (!currentNavigator || typeof currentNavigator !== "object") {
@@ -137,6 +176,101 @@ async function requestXrSession(options = {}) {
137
176
  const init = mergeXrSessionInit(baseSessionInit, sessionInit);
138
177
  return xr.requestSession(mode, init);
139
178
  }
179
+ function readXrFrameRateCapabilities(session, options = {}) {
180
+ const {
181
+ mode = "immersive-vr",
182
+ fallbackFrameRates = [],
183
+ defaultFrameRate
184
+ } = options;
185
+ assertSessionMode(mode);
186
+ const sessionFrameRate = readPositiveNumber(session?.frameRate);
187
+ const supportedFrameRates = normalizeFrameRates(session?.supportedFrameRates);
188
+ const fallbackRates = normalizeFrameRates(fallbackFrameRates);
189
+ const mergedSupported = supportedFrameRates.length ? [...supportedFrameRates] : [...fallbackRates];
190
+ if (sessionFrameRate && !mergedSupported.includes(sessionFrameRate)) {
191
+ mergedSupported.push(sessionFrameRate);
192
+ mergedSupported.sort((left, right) => right - left);
193
+ }
194
+ const refreshRateHz = sessionFrameRate ?? mergedSupported[0] ?? readPositiveNumber(defaultFrameRate) ?? getDefaultFrameRateForMode(mode);
195
+ return Object.freeze({
196
+ mode,
197
+ currentFrameRate: sessionFrameRate,
198
+ supportedFrameRates: Object.freeze(mergedSupported),
199
+ refreshRateHz,
200
+ canUpdateTargetFrameRate: Boolean(session) && typeof session.updateTargetFrameRate === "function"
201
+ });
202
+ }
203
+ function createXrPerformanceHint(options = {}) {
204
+ const {
205
+ session = null,
206
+ mode = "immersive-vr",
207
+ preferredFrameRates = [],
208
+ fallbackFrameRates = [],
209
+ defaultFrameRate
210
+ } = options;
211
+ const capabilities = readXrFrameRateCapabilities(session, {
212
+ mode,
213
+ fallbackFrameRates,
214
+ defaultFrameRate
215
+ });
216
+ const filteredPreferredFrameRates = normalizeFrameRates(preferredFrameRates).filter(
217
+ (frameRate) => capabilities.supportedFrameRates.length === 0 || capabilities.supportedFrameRates.includes(frameRate)
218
+ );
219
+ const derivedPreferredFrameRates = filteredPreferredFrameRates.length ? filteredPreferredFrameRates : capabilities.supportedFrameRates.length ? capabilities.supportedFrameRates : Object.freeze([capabilities.refreshRateHz]);
220
+ const targetFrameRate = derivedPreferredFrameRates[0] ?? capabilities.refreshRateHz;
221
+ const rationale = [];
222
+ if (capabilities.currentFrameRate) {
223
+ rationale.push(
224
+ `XR session reports a current frame rate of ${capabilities.currentFrameRate}Hz.`
225
+ );
226
+ } else {
227
+ rationale.push("XR session does not expose a current frame rate; using defaults.");
228
+ }
229
+ if (capabilities.supportedFrameRates.length) {
230
+ rationale.push(
231
+ `XR runtime exposes supported frame rates: ${capabilities.supportedFrameRates.join(", ")}Hz.`
232
+ );
233
+ } else {
234
+ rationale.push("XR runtime does not expose supported frame rates; using fallback targets.");
235
+ }
236
+ if (filteredPreferredFrameRates.length) {
237
+ rationale.push("Preferred XR frame rates were filtered against runtime-supported values.");
238
+ } else {
239
+ rationale.push("XR target frame rate defaults to the highest available runtime target.");
240
+ }
241
+ return Object.freeze({
242
+ ...capabilities,
243
+ preferredFrameRates: Object.freeze([...derivedPreferredFrameRates]),
244
+ targetFrameRate,
245
+ targetFrameTimeMs: 1e3 / targetFrameRate,
246
+ workerBudget: Object.freeze({
247
+ queueClass: xrWorkerQueueClass,
248
+ schedulerMode: xrWorkerSchedulerMode,
249
+ profile: getWorkerBudgetProfileForMode(mode)
250
+ }),
251
+ rationale: Object.freeze(rationale)
252
+ });
253
+ }
254
+ async function updateXrTargetFrameRate(session, frameRate) {
255
+ if (!session || typeof session !== "object") {
256
+ throw new Error("XR session is required to update target frame rate.");
257
+ }
258
+ const requestedFrameRate = readPositiveNumber(frameRate);
259
+ if (!requestedFrameRate) {
260
+ throw new Error("XR target frame rate must be a finite number greater than zero.");
261
+ }
262
+ if (typeof session.updateTargetFrameRate !== "function") {
263
+ throw new Error("XR session does not support updateTargetFrameRate(frameRate).");
264
+ }
265
+ const supportedFrameRates = normalizeFrameRates(session.supportedFrameRates);
266
+ if (supportedFrameRates.length > 0 && !supportedFrameRates.includes(requestedFrameRate)) {
267
+ throw new Error(
268
+ `XR target frame rate ${requestedFrameRate}Hz is not supported by the active session.`
269
+ );
270
+ }
271
+ await session.updateTargetFrameRate(requestedFrameRate);
272
+ return requestedFrameRate;
273
+ }
140
274
  function createXrStore(initialState = {}) {
141
275
  const listeners = /* @__PURE__ */ new Set();
142
276
  let state = {
@@ -145,6 +279,11 @@ function createXrStore(initialState = {}) {
145
279
  isEntering: false,
146
280
  lastError: null,
147
281
  supportedModes: {},
282
+ currentFrameRate: null,
283
+ targetFrameRate: null,
284
+ supportedFrameRates: [],
285
+ canUpdateTargetFrameRate: false,
286
+ workerBudgetProfile: null,
148
287
  ...initialState
149
288
  };
150
289
  const notify = () => {
@@ -176,6 +315,11 @@ function createXrStore(initialState = {}) {
176
315
  isEntering: false,
177
316
  lastError: null,
178
317
  supportedModes: {},
318
+ currentFrameRate: null,
319
+ targetFrameRate: null,
320
+ supportedFrameRates: [],
321
+ canUpdateTargetFrameRate: false,
322
+ workerBudgetProfile: null,
179
323
  ...initialState
180
324
  };
181
325
  notify();
@@ -205,7 +349,12 @@ function createXrManager(options = {}) {
205
349
  store.set({
206
350
  activeSession: null,
207
351
  mode: null,
208
- isEntering: false
352
+ isEntering: false,
353
+ currentFrameRate: null,
354
+ targetFrameRate: null,
355
+ supportedFrameRates: [],
356
+ canUpdateTargetFrameRate: false,
357
+ workerBudgetProfile: null
209
358
  });
210
359
  if (typeof onSessionEnd === "function") {
211
360
  onSessionEnd();
@@ -220,6 +369,46 @@ function createXrManager(options = {}) {
220
369
  };
221
370
  const getState = () => store.getSnapshot();
222
371
  const subscribe = (listener) => store.subscribe(listener);
372
+ const getFrameRateCapabilities = (options2 = {}) => {
373
+ const state = store.getSnapshot();
374
+ return readXrFrameRateCapabilities(
375
+ options2.session ?? state.activeSession,
376
+ {
377
+ mode: options2.mode ?? state.mode ?? defaultMode,
378
+ fallbackFrameRates: options2.fallbackFrameRates ?? state.supportedFrameRates,
379
+ defaultFrameRate: options2.defaultFrameRate ?? state.targetFrameRate ?? state.currentFrameRate ?? void 0
380
+ }
381
+ );
382
+ };
383
+ const getPerformanceHint = (options2 = {}) => {
384
+ const state = store.getSnapshot();
385
+ return createXrPerformanceHint({
386
+ session: options2.session ?? state.activeSession,
387
+ mode: options2.mode ?? state.mode ?? defaultMode,
388
+ preferredFrameRates: options2.preferredFrameRates ?? (state.targetFrameRate ? [state.targetFrameRate] : []),
389
+ fallbackFrameRates: options2.fallbackFrameRates ?? state.supportedFrameRates,
390
+ defaultFrameRate: options2.defaultFrameRate ?? state.targetFrameRate ?? state.currentFrameRate ?? void 0
391
+ });
392
+ };
393
+ const syncSessionPerformanceState = (session, mode, preferredFrameRates = []) => {
394
+ const hint = createXrPerformanceHint({
395
+ session,
396
+ mode,
397
+ preferredFrameRates
398
+ });
399
+ store.set({
400
+ activeSession: session,
401
+ mode,
402
+ isEntering: false,
403
+ lastError: null,
404
+ currentFrameRate: hint.currentFrameRate,
405
+ targetFrameRate: hint.targetFrameRate,
406
+ supportedFrameRates: hint.supportedFrameRates,
407
+ canUpdateTargetFrameRate: hint.canUpdateTargetFrameRate,
408
+ workerBudgetProfile: hint.workerBudget.profile
409
+ });
410
+ return hint;
411
+ };
223
412
  const probeSupport = async (modes = [defaultMode]) => {
224
413
  const supportedModes = {};
225
414
  for (const mode of modes) {
@@ -246,12 +435,7 @@ function createXrManager(options = {}) {
246
435
  navigator: navigatorOverride
247
436
  });
248
437
  attachSessionEndHandler(session);
249
- store.set({
250
- activeSession: session,
251
- mode,
252
- isEntering: false,
253
- lastError: null
254
- });
438
+ syncSessionPerformanceState(session, mode);
255
439
  if (typeof onSessionStart === "function") {
256
440
  onSessionStart(session, mode);
257
441
  }
@@ -268,6 +452,29 @@ function createXrManager(options = {}) {
268
452
  const enterVr = async (sessionInit = {}) => {
269
453
  return enterSession("immersive-vr", sessionInit);
270
454
  };
455
+ const setTargetFrameRate = async (frameRate) => {
456
+ const state = store.getSnapshot();
457
+ const activeSession = state.activeSession;
458
+ if (!activeSession) {
459
+ throw new Error(
460
+ "Cannot update XR target frame rate without an active XR session."
461
+ );
462
+ }
463
+ try {
464
+ const appliedFrameRate = await updateXrTargetFrameRate(
465
+ activeSession,
466
+ frameRate
467
+ );
468
+ syncSessionPerformanceState(activeSession, state.mode ?? defaultMode, [
469
+ appliedFrameRate
470
+ ]);
471
+ return appliedFrameRate;
472
+ } catch (error) {
473
+ const message = error instanceof Error ? error.message : String(error ?? "Unknown XR error");
474
+ store.set({ lastError: message });
475
+ throw error;
476
+ }
477
+ };
271
478
  const exitSession = async () => {
272
479
  const { activeSession } = store.getSnapshot();
273
480
  if (!activeSession) {
@@ -291,8 +498,11 @@ function createXrManager(options = {}) {
291
498
  getState,
292
499
  subscribe,
293
500
  probeSupport,
501
+ getFrameRateCapabilities,
502
+ getPerformanceHint,
294
503
  enterSession,
295
504
  enterVr,
505
+ setTargetFrameRate,
296
506
  exitSession,
297
507
  dispose
298
508
  };
@@ -301,12 +511,18 @@ var defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;
301
511
  // Annotate the CommonJS export names for ESM import in node:
302
512
  0 && (module.exports = {
303
513
  createXrManager,
514
+ createXrPerformanceHint,
304
515
  createXrStore,
305
516
  defaultVrSessionInit,
517
+ defaultXrWorkerBudgetProfile,
306
518
  isXrModeSupported,
307
519
  mergeXrSessionInit,
520
+ readXrFrameRateCapabilities,
308
521
  requestXrSession,
522
+ updateXrTargetFrameRate,
309
523
  xrReferenceSpaceTypes,
310
- xrSessionModes
524
+ xrSessionModes,
525
+ xrWorkerQueueClass,
526
+ xrWorkerSchedulerMode
311
527
  });
312
528
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"sourcesContent":["const DEFAULT_VR_SESSION_INIT = Object.freeze({\n requiredFeatures: [\"local-floor\"],\n optionalFeatures: [\"bounded-floor\", \"hand-tracking\", \"layers\"],\n});\n\nexport const xrSessionModes = Object.freeze([\n \"inline\",\n \"immersive-vr\",\n \"immersive-ar\",\n]);\n\nexport const xrReferenceSpaceTypes = Object.freeze([\n \"viewer\",\n \"local\",\n \"local-floor\",\n \"bounded-floor\",\n \"unbounded\",\n]);\n\nfunction toStringArray(values) {\n if (!Array.isArray(values)) {\n return [];\n }\n return values\n .filter((value) => typeof value === \"string\")\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction dedupeStrings(values) {\n return [...new Set(toStringArray(values))];\n}\n\nfunction readNavigator(navigatorOverride) {\n const currentNavigator = navigatorOverride ?? globalThis.navigator;\n if (!currentNavigator || typeof currentNavigator !== \"object\") {\n throw new Error(\n \"WebXR navigator unavailable. Provide a browser navigator with navigator.xr.\"\n );\n }\n return currentNavigator;\n}\n\nfunction readXrSystem(navigatorOverride) {\n const currentNavigator = readNavigator(navigatorOverride);\n const xr = currentNavigator.xr;\n if (!xr || typeof xr !== \"object\") {\n throw new Error(\n \"WebXR runtime unavailable. navigator.xr is missing in this environment.\"\n );\n }\n return xr;\n}\n\nfunction assertSessionMode(mode) {\n if (!xrSessionModes.includes(mode)) {\n const available = xrSessionModes.join(\", \");\n throw new Error(\n `Unknown XR session mode \"${mode}\". Available modes: ${available}.`\n );\n }\n}\n\nexport function mergeXrSessionInit(base = {}, override = {}) {\n const requiredFeatures = dedupeStrings([\n ...toStringArray(base.requiredFeatures),\n ...toStringArray(override.requiredFeatures),\n ]);\n\n const optionalFeatures = dedupeStrings([\n ...toStringArray(base.optionalFeatures),\n ...toStringArray(override.optionalFeatures),\n ]);\n\n const merged = {\n ...base,\n ...override,\n requiredFeatures,\n optionalFeatures,\n };\n\n if (requiredFeatures.length === 0) {\n delete merged.requiredFeatures;\n }\n\n if (optionalFeatures.length === 0) {\n delete merged.optionalFeatures;\n }\n\n return merged;\n}\n\nexport async function isXrModeSupported(\n mode = \"immersive-vr\",\n options = {}\n) {\n assertSessionMode(mode);\n\n const { navigator: navigatorOverride } = options;\n let xr;\n try {\n xr = readXrSystem(navigatorOverride);\n } catch {\n return false;\n }\n\n if (typeof xr.isSessionSupported !== \"function\") {\n return false;\n }\n\n try {\n return Boolean(await xr.isSessionSupported(mode));\n } catch {\n return false;\n }\n}\n\nexport async function requestXrSession(options = {}) {\n const {\n mode = \"immersive-vr\",\n sessionInit = {},\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n navigator: navigatorOverride,\n } = options;\n\n assertSessionMode(mode);\n\n const xr = readXrSystem(navigatorOverride);\n\n if (typeof xr.requestSession !== \"function\") {\n throw new Error(\"WebXR requestSession API unavailable.\");\n }\n\n const init = mergeXrSessionInit(baseSessionInit, sessionInit);\n return xr.requestSession(mode, init);\n}\n\nexport function createXrStore(initialState = {}) {\n const listeners = new Set();\n let state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n ...initialState,\n };\n\n const notify = () => {\n for (const listener of listeners) {\n listener(state);\n }\n };\n\n return {\n getSnapshot() {\n return state;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n set(partialState) {\n state = {\n ...state,\n ...partialState,\n };\n notify();\n },\n reset() {\n state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n ...initialState,\n };\n notify();\n },\n };\n}\n\nexport function createXrManager(options = {}) {\n const {\n navigator: navigatorOverride,\n defaultMode = \"immersive-vr\",\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n onSessionStart,\n onSessionEnd,\n } = options;\n\n assertSessionMode(defaultMode);\n\n const store = createXrStore();\n let activeSessionEndHandler = null;\n\n const detachSessionEndHandler = () => {\n const { activeSession } = store.getSnapshot();\n if (\n activeSession &&\n activeSessionEndHandler &&\n typeof activeSession.removeEventListener === \"function\"\n ) {\n activeSession.removeEventListener(\"end\", activeSessionEndHandler);\n }\n activeSessionEndHandler = null;\n };\n\n const handleSessionEnded = () => {\n detachSessionEndHandler();\n store.set({\n activeSession: null,\n mode: null,\n isEntering: false,\n });\n if (typeof onSessionEnd === \"function\") {\n onSessionEnd();\n }\n };\n\n const attachSessionEndHandler = (session) => {\n if (!session || typeof session.addEventListener !== \"function\") {\n return;\n }\n activeSessionEndHandler = handleSessionEnded;\n session.addEventListener(\"end\", activeSessionEndHandler);\n };\n\n const getState = () => store.getSnapshot();\n\n const subscribe = (listener) => store.subscribe(listener);\n\n const probeSupport = async (modes = [defaultMode]) => {\n const supportedModes = {};\n for (const mode of modes) {\n assertSessionMode(mode);\n supportedModes[mode] = await isXrModeSupported(mode, {\n navigator: navigatorOverride,\n });\n }\n store.set({ supportedModes });\n return supportedModes;\n };\n\n const enterSession = async (mode = defaultMode, sessionInit = {}) => {\n assertSessionMode(mode);\n\n const existing = store.getSnapshot().activeSession;\n if (existing) {\n return existing;\n }\n\n store.set({ isEntering: true, lastError: null });\n\n try {\n const session = await requestXrSession({\n mode,\n sessionInit,\n baseSessionInit,\n navigator: navigatorOverride,\n });\n\n attachSessionEndHandler(session);\n\n store.set({\n activeSession: session,\n mode,\n isEntering: false,\n lastError: null,\n });\n\n if (typeof onSessionStart === \"function\") {\n onSessionStart(session, mode);\n }\n\n return session;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({\n isEntering: false,\n lastError: message,\n });\n throw error;\n }\n };\n\n const enterVr = async (sessionInit = {}) => {\n return enterSession(\"immersive-vr\", sessionInit);\n };\n\n const exitSession = async () => {\n const { activeSession } = store.getSnapshot();\n if (!activeSession) {\n return false;\n }\n\n if (typeof activeSession.end === \"function\") {\n await activeSession.end();\n }\n\n // Fallback for test fakes or runtimes that do not emit an end event.\n if (store.getSnapshot().activeSession) {\n handleSessionEnded();\n }\n\n return true;\n };\n\n const dispose = async () => {\n await exitSession();\n detachSessionEndHandler();\n store.reset();\n };\n\n return {\n store,\n getState,\n subscribe,\n probeSupport,\n enterSession,\n enterVr,\n exitSession,\n dispose,\n };\n}\n\nexport const defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.js"],"sourcesContent":["const DEFAULT_VR_SESSION_INIT = Object.freeze({\n requiredFeatures: [\"local-floor\"],\n optionalFeatures: [\"bounded-floor\", \"hand-tracking\", \"layers\"],\n});\nconst DEFAULT_MODE_FRAME_RATES = Object.freeze({\n inline: 60,\n \"immersive-vr\": 90,\n \"immersive-ar\": 72,\n});\n\nexport const xrSessionModes = Object.freeze([\n \"inline\",\n \"immersive-vr\",\n \"immersive-ar\",\n]);\n\nexport const xrReferenceSpaceTypes = Object.freeze([\n \"viewer\",\n \"local\",\n \"local-floor\",\n \"bounded-floor\",\n \"unbounded\",\n]);\nexport const xrWorkerQueueClass = \"render\";\nexport const xrWorkerSchedulerMode = \"dag\";\nexport const defaultXrWorkerBudgetProfile = \"xr\";\n\nfunction toStringArray(values) {\n if (!Array.isArray(values)) {\n return [];\n }\n return values\n .filter((value) => typeof value === \"string\")\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction dedupeStrings(values) {\n return [...new Set(toStringArray(values))];\n}\n\nfunction readPositiveNumber(value) {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n ? value\n : null;\n}\n\nfunction normalizeFrameRates(values) {\n if (!values || typeof values === \"string\") {\n return Object.freeze([]);\n }\n\n let collected;\n try {\n collected = Array.from(values);\n } catch {\n return Object.freeze([]);\n }\n\n return Object.freeze(\n [...new Set(collected.map((value) => Number(value)).filter((value) => Number.isFinite(value) && value > 0))].sort(\n (left, right) => right - left\n )\n );\n}\n\nfunction getDefaultFrameRateForMode(mode) {\n return DEFAULT_MODE_FRAME_RATES[mode] ?? DEFAULT_MODE_FRAME_RATES[\"immersive-vr\"];\n}\n\nfunction getWorkerBudgetProfileForMode(mode) {\n return mode === \"inline\" ? \"realtime\" : defaultXrWorkerBudgetProfile;\n}\n\nfunction readNavigator(navigatorOverride) {\n const currentNavigator = navigatorOverride ?? globalThis.navigator;\n if (!currentNavigator || typeof currentNavigator !== \"object\") {\n throw new Error(\n \"WebXR navigator unavailable. Provide a browser navigator with navigator.xr.\"\n );\n }\n return currentNavigator;\n}\n\nfunction readXrSystem(navigatorOverride) {\n const currentNavigator = readNavigator(navigatorOverride);\n const xr = currentNavigator.xr;\n if (!xr || typeof xr !== \"object\") {\n throw new Error(\n \"WebXR runtime unavailable. navigator.xr is missing in this environment.\"\n );\n }\n return xr;\n}\n\nfunction assertSessionMode(mode) {\n if (!xrSessionModes.includes(mode)) {\n const available = xrSessionModes.join(\", \");\n throw new Error(\n `Unknown XR session mode \"${mode}\". Available modes: ${available}.`\n );\n }\n}\n\nexport function mergeXrSessionInit(base = {}, override = {}) {\n const requiredFeatures = dedupeStrings([\n ...toStringArray(base.requiredFeatures),\n ...toStringArray(override.requiredFeatures),\n ]);\n\n const optionalFeatures = dedupeStrings([\n ...toStringArray(base.optionalFeatures),\n ...toStringArray(override.optionalFeatures),\n ]);\n\n const merged = {\n ...base,\n ...override,\n requiredFeatures,\n optionalFeatures,\n };\n\n if (requiredFeatures.length === 0) {\n delete merged.requiredFeatures;\n }\n\n if (optionalFeatures.length === 0) {\n delete merged.optionalFeatures;\n }\n\n return merged;\n}\n\nexport async function isXrModeSupported(\n mode = \"immersive-vr\",\n options = {}\n) {\n assertSessionMode(mode);\n\n const { navigator: navigatorOverride } = options;\n let xr;\n try {\n xr = readXrSystem(navigatorOverride);\n } catch {\n return false;\n }\n\n if (typeof xr.isSessionSupported !== \"function\") {\n return false;\n }\n\n try {\n return Boolean(await xr.isSessionSupported(mode));\n } catch {\n return false;\n }\n}\n\nexport async function requestXrSession(options = {}) {\n const {\n mode = \"immersive-vr\",\n sessionInit = {},\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n navigator: navigatorOverride,\n } = options;\n\n assertSessionMode(mode);\n\n const xr = readXrSystem(navigatorOverride);\n\n if (typeof xr.requestSession !== \"function\") {\n throw new Error(\"WebXR requestSession API unavailable.\");\n }\n\n const init = mergeXrSessionInit(baseSessionInit, sessionInit);\n return xr.requestSession(mode, init);\n}\n\nexport function readXrFrameRateCapabilities(session, options = {}) {\n const {\n mode = \"immersive-vr\",\n fallbackFrameRates = [],\n defaultFrameRate,\n } = options;\n\n assertSessionMode(mode);\n\n const sessionFrameRate = readPositiveNumber(session?.frameRate);\n const supportedFrameRates = normalizeFrameRates(session?.supportedFrameRates);\n const fallbackRates = normalizeFrameRates(fallbackFrameRates);\n const mergedSupported = supportedFrameRates.length\n ? [...supportedFrameRates]\n : [...fallbackRates];\n\n if (sessionFrameRate && !mergedSupported.includes(sessionFrameRate)) {\n mergedSupported.push(sessionFrameRate);\n mergedSupported.sort((left, right) => right - left);\n }\n\n const refreshRateHz =\n sessionFrameRate ??\n mergedSupported[0] ??\n readPositiveNumber(defaultFrameRate) ??\n getDefaultFrameRateForMode(mode);\n\n return Object.freeze({\n mode,\n currentFrameRate: sessionFrameRate,\n supportedFrameRates: Object.freeze(mergedSupported),\n refreshRateHz,\n canUpdateTargetFrameRate:\n Boolean(session) && typeof session.updateTargetFrameRate === \"function\",\n });\n}\n\nexport function createXrPerformanceHint(options = {}) {\n const {\n session = null,\n mode = \"immersive-vr\",\n preferredFrameRates = [],\n fallbackFrameRates = [],\n defaultFrameRate,\n } = options;\n\n const capabilities = readXrFrameRateCapabilities(session, {\n mode,\n fallbackFrameRates,\n defaultFrameRate,\n });\n\n const filteredPreferredFrameRates = normalizeFrameRates(preferredFrameRates).filter(\n (frameRate) =>\n capabilities.supportedFrameRates.length === 0 ||\n capabilities.supportedFrameRates.includes(frameRate)\n );\n\n const derivedPreferredFrameRates = filteredPreferredFrameRates.length\n ? filteredPreferredFrameRates\n : capabilities.supportedFrameRates.length\n ? capabilities.supportedFrameRates\n : Object.freeze([capabilities.refreshRateHz]);\n const targetFrameRate =\n derivedPreferredFrameRates[0] ?? capabilities.refreshRateHz;\n const rationale = [];\n\n if (capabilities.currentFrameRate) {\n rationale.push(\n `XR session reports a current frame rate of ${capabilities.currentFrameRate}Hz.`\n );\n } else {\n rationale.push(\"XR session does not expose a current frame rate; using defaults.\");\n }\n\n if (capabilities.supportedFrameRates.length) {\n rationale.push(\n `XR runtime exposes supported frame rates: ${capabilities.supportedFrameRates.join(\", \")}Hz.`\n );\n } else {\n rationale.push(\"XR runtime does not expose supported frame rates; using fallback targets.\");\n }\n\n if (filteredPreferredFrameRates.length) {\n rationale.push(\"Preferred XR frame rates were filtered against runtime-supported values.\");\n } else {\n rationale.push(\"XR target frame rate defaults to the highest available runtime target.\");\n }\n\n return Object.freeze({\n ...capabilities,\n preferredFrameRates: Object.freeze([...derivedPreferredFrameRates]),\n targetFrameRate,\n targetFrameTimeMs: 1000 / targetFrameRate,\n workerBudget: Object.freeze({\n queueClass: xrWorkerQueueClass,\n schedulerMode: xrWorkerSchedulerMode,\n profile: getWorkerBudgetProfileForMode(mode),\n }),\n rationale: Object.freeze(rationale),\n });\n}\n\nexport async function updateXrTargetFrameRate(session, frameRate) {\n if (!session || typeof session !== \"object\") {\n throw new Error(\"XR session is required to update target frame rate.\");\n }\n\n const requestedFrameRate = readPositiveNumber(frameRate);\n if (!requestedFrameRate) {\n throw new Error(\"XR target frame rate must be a finite number greater than zero.\");\n }\n\n if (typeof session.updateTargetFrameRate !== \"function\") {\n throw new Error(\"XR session does not support updateTargetFrameRate(frameRate).\");\n }\n\n const supportedFrameRates = normalizeFrameRates(session.supportedFrameRates);\n if (\n supportedFrameRates.length > 0 &&\n !supportedFrameRates.includes(requestedFrameRate)\n ) {\n throw new Error(\n `XR target frame rate ${requestedFrameRate}Hz is not supported by the active session.`\n );\n }\n\n await session.updateTargetFrameRate(requestedFrameRate);\n return requestedFrameRate;\n}\n\nexport function createXrStore(initialState = {}) {\n const listeners = new Set();\n let state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n ...initialState,\n };\n\n const notify = () => {\n for (const listener of listeners) {\n listener(state);\n }\n };\n\n return {\n getSnapshot() {\n return state;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n set(partialState) {\n state = {\n ...state,\n ...partialState,\n };\n notify();\n },\n reset() {\n state = {\n activeSession: null,\n mode: null,\n isEntering: false,\n lastError: null,\n supportedModes: {},\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n ...initialState,\n };\n notify();\n },\n };\n}\n\nexport function createXrManager(options = {}) {\n const {\n navigator: navigatorOverride,\n defaultMode = \"immersive-vr\",\n baseSessionInit = DEFAULT_VR_SESSION_INIT,\n onSessionStart,\n onSessionEnd,\n } = options;\n\n assertSessionMode(defaultMode);\n\n const store = createXrStore();\n let activeSessionEndHandler = null;\n\n const detachSessionEndHandler = () => {\n const { activeSession } = store.getSnapshot();\n if (\n activeSession &&\n activeSessionEndHandler &&\n typeof activeSession.removeEventListener === \"function\"\n ) {\n activeSession.removeEventListener(\"end\", activeSessionEndHandler);\n }\n activeSessionEndHandler = null;\n };\n\n const handleSessionEnded = () => {\n detachSessionEndHandler();\n store.set({\n activeSession: null,\n mode: null,\n isEntering: false,\n currentFrameRate: null,\n targetFrameRate: null,\n supportedFrameRates: [],\n canUpdateTargetFrameRate: false,\n workerBudgetProfile: null,\n });\n if (typeof onSessionEnd === \"function\") {\n onSessionEnd();\n }\n };\n\n const attachSessionEndHandler = (session) => {\n if (!session || typeof session.addEventListener !== \"function\") {\n return;\n }\n activeSessionEndHandler = handleSessionEnded;\n session.addEventListener(\"end\", activeSessionEndHandler);\n };\n\n const getState = () => store.getSnapshot();\n\n const subscribe = (listener) => store.subscribe(listener);\n\n const getFrameRateCapabilities = (options = {}) => {\n const state = store.getSnapshot();\n return readXrFrameRateCapabilities(\n options.session ?? state.activeSession,\n {\n mode: options.mode ?? state.mode ?? defaultMode,\n fallbackFrameRates:\n options.fallbackFrameRates ?? state.supportedFrameRates,\n defaultFrameRate:\n options.defaultFrameRate ??\n state.targetFrameRate ??\n state.currentFrameRate ??\n undefined,\n }\n );\n };\n\n const getPerformanceHint = (options = {}) => {\n const state = store.getSnapshot();\n return createXrPerformanceHint({\n session: options.session ?? state.activeSession,\n mode: options.mode ?? state.mode ?? defaultMode,\n preferredFrameRates:\n options.preferredFrameRates ??\n (state.targetFrameRate ? [state.targetFrameRate] : []),\n fallbackFrameRates:\n options.fallbackFrameRates ?? state.supportedFrameRates,\n defaultFrameRate:\n options.defaultFrameRate ??\n state.targetFrameRate ??\n state.currentFrameRate ??\n undefined,\n });\n };\n\n const syncSessionPerformanceState = (session, mode, preferredFrameRates = []) => {\n const hint = createXrPerformanceHint({\n session,\n mode,\n preferredFrameRates,\n });\n\n store.set({\n activeSession: session,\n mode,\n isEntering: false,\n lastError: null,\n currentFrameRate: hint.currentFrameRate,\n targetFrameRate: hint.targetFrameRate,\n supportedFrameRates: hint.supportedFrameRates,\n canUpdateTargetFrameRate: hint.canUpdateTargetFrameRate,\n workerBudgetProfile: hint.workerBudget.profile,\n });\n\n return hint;\n };\n\n const probeSupport = async (modes = [defaultMode]) => {\n const supportedModes = {};\n for (const mode of modes) {\n assertSessionMode(mode);\n supportedModes[mode] = await isXrModeSupported(mode, {\n navigator: navigatorOverride,\n });\n }\n store.set({ supportedModes });\n return supportedModes;\n };\n\n const enterSession = async (mode = defaultMode, sessionInit = {}) => {\n assertSessionMode(mode);\n\n const existing = store.getSnapshot().activeSession;\n if (existing) {\n return existing;\n }\n\n store.set({ isEntering: true, lastError: null });\n\n try {\n const session = await requestXrSession({\n mode,\n sessionInit,\n baseSessionInit,\n navigator: navigatorOverride,\n });\n\n attachSessionEndHandler(session);\n syncSessionPerformanceState(session, mode);\n\n if (typeof onSessionStart === \"function\") {\n onSessionStart(session, mode);\n }\n\n return session;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({\n isEntering: false,\n lastError: message,\n });\n throw error;\n }\n };\n\n const enterVr = async (sessionInit = {}) => {\n return enterSession(\"immersive-vr\", sessionInit);\n };\n\n const setTargetFrameRate = async (frameRate) => {\n const state = store.getSnapshot();\n const activeSession = state.activeSession;\n if (!activeSession) {\n throw new Error(\n \"Cannot update XR target frame rate without an active XR session.\"\n );\n }\n\n try {\n const appliedFrameRate = await updateXrTargetFrameRate(\n activeSession,\n frameRate\n );\n syncSessionPerformanceState(activeSession, state.mode ?? defaultMode, [\n appliedFrameRate,\n ]);\n return appliedFrameRate;\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error ?? \"Unknown XR error\");\n store.set({ lastError: message });\n throw error;\n }\n };\n\n const exitSession = async () => {\n const { activeSession } = store.getSnapshot();\n if (!activeSession) {\n return false;\n }\n\n if (typeof activeSession.end === \"function\") {\n await activeSession.end();\n }\n\n // Fallback for test fakes or runtimes that do not emit an end event.\n if (store.getSnapshot().activeSession) {\n handleSessionEnded();\n }\n\n return true;\n };\n\n const dispose = async () => {\n await exitSession();\n detachSessionEndHandler();\n store.reset();\n };\n\n return {\n store,\n getState,\n subscribe,\n probeSupport,\n getFrameRateCapabilities,\n getPerformanceHint,\n enterSession,\n enterVr,\n setTargetFrameRate,\n exitSession,\n dispose,\n };\n}\n\nexport const defaultVrSessionInit = DEFAULT_VR_SESSION_INIT;\n"],"mappings":"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","names":["options"]}