Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/xXmizzeryXx/zenodeployment/llms.txt

Use this file to discover all available pages before exploring further.

zeno-game-sw.js is the core of Zeno’s game serving layer. It maintains a single fileStore Map keyed by full pathname — /zeno-games/{gameId}/{relPath} — and intercepts every fetch request that targets that prefix, responding with the appropriate file buffer and MIME type directly from memory. No network requests are made for registered games.

Install and activate lifecycle

The SW installs and takes control immediately without waiting for existing tabs to close.
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()));
skipWaiting() forces the new SW version to activate as soon as it finishes installing. clients.claim() makes the active SW take control of all open clients on the same origin immediately, ensuring games registered before a SW update are still served correctly.

Message protocol

The page communicates with the SW via postMessage. All messages use the shape { type, payload }. Responses that require a reply are sent back over a MessageChannel port passed as event.ports[0].
Transfers all file buffers for a game into the SW’s in-memory store.Payload:
FieldTypeDescription
gameIdstringUnique identifier for the game
filesMeta{ path: string, mimeType: string }[]Metadata for each file, parallel-indexed with buffers
buffersArrayBuffer[]Raw file contents, one per entry in filesMeta
Response: { type: 'GAME_REGISTERED', gameId }
// Sent by the page
const channel = new MessageChannel();
sw.postMessage(
  {
    type: 'REGISTER_GAME',
    payload: {
      gameId: 'my-platformer',
      filesMeta: [
        { path: 'index.html', mimeType: 'text/html' },
        { path: 'game.js',    mimeType: 'application/javascript' },
      ],
      buffers: [indexBuffer, gameJsBuffer],
    },
  },
  [channel.port2]
);
channel.port1.onmessage = ({ data }) => {
  // data.type === 'GAME_REGISTERED'
};
Zeno always clones buffers with .slice(0) before transfer so the originals in fileRecords remain intact and can be re-sent after an SW restart.
Checks whether at least one file for a given game is still present in the store.Payload:
FieldTypeDescription
gameIdstringThe game to check
Response: { type: 'GAME_FOUND', gameId } or { type: 'GAME_MISSING', gameId }The SW checks for any key with the prefix /zeno-games/{gameId}/. A GAME_MISSING response is the signal that the SW was restarted and the game must be re-registered before launching.
const channel = new MessageChannel();
sw.postMessage(
  { type: 'PING_GAME', payload: { gameId: 'my-platformer' } },
  [channel.port2]
);
channel.port1.onmessage = ({ data }) => {
  if (data.type === 'GAME_MISSING') {
    // Re-register before opening the iframe
  }
};
Removes all files for a single game from the store. Used when deleting a game from the library.Payload:
FieldTypeDescription
gameIdstringThe game whose files should be removed
Response: None.
sw.postMessage({
  type: 'UNREGISTER_GAME',
  payload: { gameId: 'my-platformer' },
});
Wipes the entire fileStore Map. Useful for a full reset.Payload: None.Response: None.
sw.postMessage({ type: 'CLEAR_ALL' });

Fetch handler

When a request URL contains /zeno-games/, the SW intercepts it and normalizes the pathname to prevent directory traversal (.. segments are resolved). It then looks up the normalized path in fileStore.
  • Match found — responds with the stored ArrayBuffer, the registered mimeType, Cache-Control: no-store, and Access-Control-Allow-Origin: *.
  • No match, SPA fallback — strips the final path segment and tries index.html in the same directory, responding with text/html.
  • No match at all — responds with a plain-text 404 body and HTTP status 404.
function normalizePath(p) {
  const parts = p.split('/');
  const out = [];
  for (const part of parts) {
    if (part === '..') { if (out.length > 1) out.pop(); }
    else if (part !== '.') out.push(part);
  }
  return out.join('/');
}
The SPA fallback means games that use client-side routing (where navigating to a sub-URL like /zeno-games/my-game/level/2 would not have a physical file) still load correctly.

Why the store resets

The fileStore Map lives entirely in the SW’s JavaScript heap. Browsers routinely terminate idle service workers to reclaim memory — when this happens, the Map is gone. Zeno’s PING → re-register pattern handles this transparently:
  1. Before opening a game iframe, the page sends PING_GAME.
  2. If the response is GAME_MISSING, the page re-reads the files from its fileRecords array and sends REGISTER_GAME again.
  3. Only after receiving GAME_REGISTERED does the page load the iframe.
File buffers are always cloned with .slice(0) before being transferred to the SW. This keeps the originals in page memory intact so they can be re-sent any number of times without re-reading from disk.

Build docs developers (and LLMs) love