[{"data":1,"prerenderedAt":304},["ShallowReactive",2],{"docs-ca-\u002Fdocs\u002Fsdk\u002Farchitecture":3},{"id":4,"title":5,"author":6,"body":7,"date":6,"description":296,"extension":297,"meta":298,"navigation":299,"path":300,"seo":301,"stem":302,"tags":6,"__hash__":303},"content_en\u002Fdocs\u002Fsdk\u002Farchitecture.md","SDK Architecture",null,{"type":8,"value":9,"toc":284},"minimark",[10,14,18,23,34,38,44,48,53,63,67,78,82,88,92,109,113,116,129,227,231,234,280],[11,12,5],"h1",{"id":13},"sdk-architecture",[15,16,17],"p",{},"The xiboplayer SDK is a modular JavaScript library organized as a pnpm monorepo. Each package handles one concern — combine them to build a full player, or use individual packages in your own projects.",[19,20,22],"h2",{"id":21},"package-dependency-graph","Package dependency graph",[24,25,30],"pre",{"className":26,"code":28,"language":29},[27],"language-text","@xiboplayer\u002Fcrypto\n  └── @xiboplayer\u002Futils (logger, EventEmitter, config, fetchWithRetry)\n        ├── @xiboplayer\u002Fschedule (campaigns, dayparting, timeline)\n        │     └── @xiboplayer\u002Frenderer (XLF rendering, transitions, LayoutPool)\n        ├── @xiboplayer\u002Fcache (offline storage, chunked downloads)\n        │     └── @xiboplayer\u002Fsw (Service Worker)\n        ├── @xiboplayer\u002Fxmds (SOAP + REST CMS client)\n        ├── @xiboplayer\u002Fxmr (WebSocket push commands)\n        ├── @xiboplayer\u002Fsettings (display settings from CMS)\n        ├── @xiboplayer\u002Fstats (proof of play, log reporting)\n        ├── @xiboplayer\u002Fsync (multi-display video walls)\n        └── @xiboplayer\u002Fproxy (Express CORS proxy + PWA server)\n\n@xiboplayer\u002Fcore (orchestration — peerDeps on cache, renderer, schedule, xmds)\n@xiboplayer\u002Fpwa (TypeScript platform layer for browsers)\n","text",[31,32,28],"code",{"__ignoreMap":33},"",[19,35,37],{"id":36},"data-flow","Data flow",[24,39,42],{"className":40,"code":41,"language":29},[27],"CMS Server\n  ↓ XMDS (SOAP\u002FREST)\n@xiboplayer\u002Fxmds → RegisterDisplay, RequiredFiles, Schedule, GetResource\n  ↓\n@xiboplayer\u002Fcore (PlayerCore)\n  ├── Parses schedule → @xiboplayer\u002Fschedule\n  ├── Downloads media → @xiboplayer\u002Fcache + DownloadManager\n  ├── Renders layouts → @xiboplayer\u002Frenderer (RendererLite)\n  ├── Reports stats → @xiboplayer\u002Fstats\n  └── Listens for push → @xiboplayer\u002Fxmr (XMR WebSocket)\n  ↓\nPlatform Layer (PWA \u002F Electron \u002F Chromium \u002F Android \u002F webOS)\n  → Displays content on screen\n",[31,43,41],{"__ignoreMap":33},[19,45,47],{"id":46},"key-design-decisions","Key design decisions",[49,50,52],"h3",{"id":51},"peer-dependencies-for-core","Peer dependencies for core",[15,54,55,58,59,62],{},[31,56,57],{},"@xiboplayer\u002Fcore"," uses ",[31,60,61],{},"peerDependencies"," for its heavy modules (cache, renderer, schedule, xmds). This means the platform layer — not core — controls which versions are wired together. It prevents version conflicts and allows the same core to work across different platform setups.",[49,64,66],{"id":65},"dual-cms-transport","Dual CMS transport",[15,68,69,70,73,74,77],{},"The ",[31,71,72],{},"@xiboplayer\u002Fxmds"," package supports both SOAP (traditional Xibo protocol) and REST (lighter JSON transport). A ",[31,75,76],{},"ProtocolDetector"," probes the CMS at startup and auto-selects the best transport. Both expose the same method names.",[49,79,81],{"id":80},"offline-first-architecture","Offline-first architecture",[15,83,69,84,87],{},[31,85,86],{},"@xiboplayer\u002Fcache"," package stores all media and schedule data locally. The player can continue operating indefinitely when the CMS is unreachable, replaying the last-known schedule from its local cache.",[49,89,91],{"id":90},"event-driven-orchestration","Event-driven orchestration",[15,93,94,95,98,99,98,102,98,105,108],{},"PlayerCore communicates with the platform layer entirely through events. It never manipulates the DOM or makes platform-specific calls. Events include ",[31,96,97],{},"collection-start",", ",[31,100,101],{},"layout-ready",[31,103,104],{},"download-request",[31,106,107],{},"layout-blacklisted",", and more. This makes it reusable across PWA, Electron, Chromium, Android, and webOS.",[19,110,112],{"id":111},"platform-wrappers","Platform wrappers",[15,114,115],{},"Each platform wrapper provides:",[117,118,119,123,126],"ol",{},[120,121,122],"li",{},"A way to serve the PWA (Express server for Electron\u002FChromium, asset loader for Android, Luna service for webOS)",[120,124,125],{},"CORS proxying for CMS API calls",[120,127,128],{},"Platform-specific features (GPU acceleration, power management, kiosk mode)",[130,131,132,151],"table",{},[133,134,135],"thead",{},[136,137,138,142,145,148],"tr",{},[139,140,141],"th",{},"Platform",[139,143,144],{},"Server",[139,146,147],{},"Port",[139,149,150],{},"Special features",[152,153,154,169,186,201,214],"tbody",{},[136,155,156,160,163,166],{},[157,158,159],"td",{},"PWA",[157,161,162],{},"Browser (Service Worker)",[157,164,165],{},"—",[157,167,168],{},"Offline via SW, works in any browser",[136,170,171,174,180,183],{},[157,172,173],{},"Electron",[157,175,176,177],{},"Express via ",[31,178,179],{},"@xiboplayer\u002Fproxy",[157,181,182],{},"8765",[157,184,185],{},"GPU detection, IPC, auto-launch, tray",[136,187,188,191,195,198],{},[157,189,190],{},"Chromium",[157,192,176,193],{},[31,194,179],{},[157,196,197],{},"8766",[157,199,200],{},"Bash launcher, system Chromium",[136,202,203,206,209,211],{},[157,204,205],{},"Android",[157,207,208],{},"WebViewAssetLoader",[157,210,165],{},[157,212,213],{},"DPM kiosk, foreground service",[136,215,216,219,222,224],{},[157,217,218],{},"webOS",[157,220,221],{},"Luna native service",[157,223,182],{},[157,225,226],{},"ActivityManager keep-alive",[19,228,230],{"id":229},"testing","Testing",[15,232,233],{},"The SDK has 1744 unit tests across 54 test suites (Vitest + jsdom). Integration tests run nightly against a dev CMS instance. The PWA has Playwright e2e tests with a mock CMS.",[24,235,239],{"className":236,"code":237,"language":238,"meta":33,"style":33},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","pnpm test              # unit tests\npnpm lint              # Biome linter\npnpm test:coverage     # with thresholds (50% lines, 40% branches)\n","bash",[31,240,241,258,269],{"__ignoreMap":33},[242,243,246,250,254],"span",{"class":244,"line":245},"line",1,[242,247,249],{"class":248},"sbgvK","pnpm",[242,251,253],{"class":252},"s_sjI"," test",[242,255,257],{"class":256},"sutJx","              # unit tests\n",[242,259,261,263,266],{"class":244,"line":260},2,[242,262,249],{"class":248},[242,264,265],{"class":252}," lint",[242,267,268],{"class":256},"              # Biome linter\n",[242,270,272,274,277],{"class":244,"line":271},3,[242,273,249],{"class":248},[242,275,276],{"class":252}," test:coverage",[242,278,279],{"class":256},"     # with thresholds (50% lines, 40% branches)\n",[281,282,283],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":33,"searchDepth":271,"depth":271,"links":285},[286,287,288,294,295],{"id":21,"depth":260,"text":22},{"id":36,"depth":260,"text":37},{"id":46,"depth":260,"text":47,"children":289},[290,291,292,293],{"id":51,"depth":271,"text":52},{"id":65,"depth":271,"text":66},{"id":80,"depth":271,"text":81},{"id":90,"depth":271,"text":91},{"id":111,"depth":260,"text":112},{"id":229,"depth":260,"text":230},"How the xiboplayer SDK packages work together","md",{"order":245},true,"\u002Fdocs\u002Fsdk\u002Farchitecture",{"title":5,"description":296},"docs\u002Fsdk\u002Farchitecture","XcP_5-otvnaAT-j4kYyIxbpZLVBZ-n7j8lFwkmPFadA",1775148112905]