@xiboplayer/utils 0.5.19 → 0.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiboplayer/utils",
3
- "version": "0.5.19",
3
+ "version": "0.6.0",
4
4
  "description": "Shared utilities for Xibo Player packages",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -12,7 +12,7 @@
12
12
  "./config": "./src/config.js"
13
13
  },
14
14
  "dependencies": {
15
- "@xiboplayer/crypto": "0.5.19"
15
+ "@xiboplayer/crypto": "0.6.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "vitest": "^2.0.0"
package/src/config.js CHANGED
@@ -334,3 +334,45 @@ export class Config {
334
334
  }
335
335
 
336
336
  export const config = new Config();
337
+
338
+ /**
339
+ * Shell-only config keys common to ALL player shells (Electron, Chromium, etc.).
340
+ * These control the native shell window/process and must NOT be forwarded to the PWA.
341
+ *
342
+ * Each shell may have additional shell-specific keys — pass them as extraShellKeys
343
+ * to extractPwaConfig().
344
+ *
345
+ * Electron extras: autoLaunch
346
+ * Chromium extras: browser, extraBrowserFlags, relaxSslCerts
347
+ */
348
+ export const SHELL_ONLY_KEYS = new Set([
349
+ 'serverPort',
350
+ 'kioskMode',
351
+ 'fullscreen',
352
+ 'hideMouseCursor',
353
+ 'preventSleep',
354
+ 'width',
355
+ 'height',
356
+ ]);
357
+
358
+ /**
359
+ * Extract PWA config from a full shell config.json.
360
+ *
361
+ * Uses a deny-list approach: filters out shell-only keys, passes everything else.
362
+ * This is future-proof — new config.json fields automatically reach the PWA
363
+ * without code changes in each shell.
364
+ *
365
+ * @param {Object} config - Full config object from config.json
366
+ * @param {Iterable<string>} [extraShellKeys] - Additional shell-specific keys to exclude
367
+ * @returns {Object} Config to pass to the PWA (via proxy pwaConfig)
368
+ */
369
+ export function extractPwaConfig(config, extraShellKeys) {
370
+ const exclude = new Set([...SHELL_ONLY_KEYS, ...(extraShellKeys || [])]);
371
+ const pwaConfig = {};
372
+ for (const [key, value] of Object.entries(config)) {
373
+ if (!exclude.has(key)) {
374
+ pwaConfig[key] = value;
375
+ }
376
+ }
377
+ return Object.keys(pwaConfig).length > 0 ? pwaConfig : undefined;
378
+ }
@@ -83,6 +83,17 @@ export async function fetchWithRetry(url, options = {}, retryOptions = {}) {
83
83
  lastResponse = response;
84
84
  lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);
85
85
  lastError.status = response.status;
86
+
87
+ // Respect Retry-After on 503 Service Unavailable (e.g. CMS cache warming)
88
+ if (response.status === 503 && attempt < maxRetries) {
89
+ const retryAfter = response.headers.get('Retry-After');
90
+ if (retryAfter) {
91
+ const delayMs = parseRetryAfter(retryAfter);
92
+ log.debug(`503 Service Unavailable, Retry-After: ${retryAfter} (${delayMs}ms)`);
93
+ await new Promise(resolve => setTimeout(resolve, delayMs));
94
+ continue;
95
+ }
96
+ }
86
97
  } catch (error) {
87
98
  // Network error — retryable
88
99
  lastError = error;
package/src/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export const VERSION: string;
2
+ export let PLAYER_API: string;
2
3
 
3
4
  export interface Logger {
4
5
  debug(...args: any[]): void;
@@ -56,6 +57,17 @@ export class Config {
56
57
 
57
58
  export const config: Config;
58
59
 
60
+ export const SHELL_ONLY_KEYS: Set<string>;
61
+
62
+ /**
63
+ * Extract PWA config from a full shell config.json.
64
+ * Filters out shell-only keys, passes everything else to the PWA.
65
+ */
66
+ export function extractPwaConfig(
67
+ config: Record<string, any>,
68
+ extraShellKeys?: Iterable<string>
69
+ ): Record<string, any> | undefined;
70
+
59
71
  export function fetchWithRetry(
60
72
  url: string,
61
73
  options?: RequestInit,
package/src/index.js CHANGED
@@ -3,6 +3,30 @@ import pkg from '../package.json' with { type: 'json' };
3
3
  export const VERSION = pkg.version;
4
4
  export { createLogger, setLogLevel, getLogLevel, isDebug, applyCmsLogLevel, mapCmsLogLevel, registerLogSink, unregisterLogSink, LOG_LEVELS } from './logger.js';
5
5
  export { EventEmitter } from './event-emitter.js';
6
- export { config } from './config.js';
6
+ import { config as _config } from './config.js';
7
+ export { config, SHELL_ONLY_KEYS, extractPwaConfig } from './config.js';
7
8
  export { fetchWithRetry } from './fetch-retry.js';
8
9
  export { CmsApiClient, CmsApiError } from './cms-api.js';
10
+
11
+ /**
12
+ * CMS Player API base path — all media, dependencies, and widgets are served
13
+ * under this prefix.
14
+ *
15
+ * Default: '/api/v2/player' (standalone index.php endpoint).
16
+ * Override: set `playerApiBase` in config.json / localStorage, or call
17
+ * setPlayerApi('/new/path') before route registration (proxy).
18
+ *
19
+ * Browser: reads from config.data.playerApiBase at import time.
20
+ * Node: call setPlayerApi() before createProxyApp().
21
+ */
22
+ const DEFAULT_PLAYER_API = '/api/v2/player';
23
+ let _playerApi = _config.data?.playerApiBase || DEFAULT_PLAYER_API;
24
+
25
+ /** Current Player API base path (no trailing slash). */
26
+ export let PLAYER_API = _playerApi;
27
+
28
+ /** Override the Player API base path at runtime (call before route registration). */
29
+ export function setPlayerApi(base) {
30
+ _playerApi = base.replace(/\/+$/, '');
31
+ PLAYER_API = _playerApi;
32
+ }