0
/devs documentation v0.0.1-beta Storage

SDK · v0.0.1-beta

Storage

Persistir progreso/settings del juego compatible con iframes sandboxeados

Persistencia de progreso/settings del juego, compatible con iframes sandboxeados.

Por qué existe: la plataforma sirve los juegos uploaded en un <iframe sandbox="allow-scripts ..."> SIN allow-same-origin. Eso es defense in depth — un juego de un dev externo no puede leer cookies/localStorage del parent ni atacar a otros jugadores. Pero también significa que localStorage directo del juego falla con SecurityError. Este módulo bridgea via postMessage al parent, que guarda los datos namespaced por slug. Mismo API funciona en dev local (sin sandbox) — automágico.

Quickstart

import { storage } from "/sdk/v0.0.1-beta/jugafy.js";

// Esperar al hydrate inicial (datos llegan del parent en sandbox).
await storage.ready();

// Set / get / remove / clear.
storage.set("level", 5);
storage.set("settings", { sfx: true, music: 0.6 });

const lvl = storage.get("level");          // 5
const cfg = storage.get("settings");       // { sfx: true, music: 0.6 }
const k   = storage.keys();                // ["level", "settings"]

storage.remove("level");
storage.clear();                            // borra todo el namespace del juego

API

storage.ready(): Promise<void>

Resuelve cuando el cache está hidratado desde el parent. Llamala antes de cualquier get — antes del hydrate, get puede devolver undefined aunque haya valor guardado de sesiones previas.

En contexto same-origin (dev local con iframe.src apuntando a un path same-origin, o juego corriendo standalone fuera de iframe), ready() resuelve inmediato. En sandbox, espera hasta 1.5s al hydrate del parent — si no llegó (parent caído o desconectado), resuelve con cache vacío.

await storage.ready();
const score = storage.get("highscore") ?? 0;

storage.set(key, value): void

Setea un valor. Sync. value debe ser serializable con JSON.stringify (objetos, arrays, primitivos — no funciones, no Date, no Map).

key debe matchear /^[a-zA-Z0-9_.\-:]{1,128}$/. El parent rechaza keys que no cumplan.

storage.get(key): any | undefined

Lee el cache local. Sync. Devuelve undefined si no existe.

storage.remove(key): void

Elimina la key (cache + parent).

storage.clear(): void

Borra todas las keys del namespace (slug actual). No afecta otros juegos.

storage.keys(): string[]

Array de keys conocidas. En same-origin refleja el localStorage real. En sandbox refleja el cache local — await ready() antes para que esté completo.

storage.mode: "same-origin" | "sandboxed-bridge"

Diagnóstico. Útil para debuguear divergencias entre dev local (typically same-origin) y prod (sandboxed-bridge).

console.log("[mygame] storage mode:", storage.mode);

storage.slug: string

El slug del juego actual (lee window.NEON_GAME). Solo informativo — no necesitás pasárselo a las APIs.

Migration: localStorageJugafy.storage

Patrón típico ANTES (roto en sandbox)

// Boot
const saved = JSON.parse(localStorage.getItem("mygame_state") || "null");
if (saved) restoreState(saved);

// Game over
localStorage.setItem("mygame_state", JSON.stringify(currentState));
localStorage.setItem("mygame_highscore", score);

DESPUÉS (sandbox-safe)

import { storage } from "/sdk/v0.0.1-beta/jugafy.js";

// Boot
await storage.ready();
const saved = storage.get("state");
if (saved) restoreState(saved);

// Game over
storage.set("state", currentState);
storage.set("highscore", score);

Diferencias de comportamiento:

  • No JSON.parse / stringify manual. El módulo lo hace internamente.
  • Una llamada async al boot. await storage.ready(). Después todo es sync.
  • Sin colisiones de namespace. Los datos de tu juego viven en jugafy:store:<slug>:<key> del localStorage del parent. No te pisás con otros juegos ni con la plataforma.

Persistencia y límites

  • Source-of-truth en sandbox: localStorage del dominio jugafy.com (parent). Si el user limpia el storage del dominio, pierde el progreso de TODOS los juegos del catálogo. Trade-off conocido.
  • Same-origin (dev local): localStorage del propio iframe con prefix por slug. Persistente entre reloads, scoped por dominio.
  • Cross-device: hoy NO hay sync server-side. Si el user juega en mobile y desktop con la misma cuenta, los progresos no se mergean automáticamente. Apuesta separada en el roadmap del SDK (sync a tabla game_progress en Supabase para users logueados).
  • Quota: el browser impone ~5MB por dominio para localStorage. Compartido entre todos los juegos. No persistas binarios grandes — para eso usá Supabase Storage via edge function dedicada.

Caveats

  • El primer get antes de ready() devuelve undefined. Esto cambia comportamiento si tu boot leía localStorage síncrono. Si no podés awaitar (porque tu loop ya arrancó), suscribite a un evento de “datos cargados”:

    storage.ready().then(() => {
      // Acá ya es seguro hacer get() de cualquier key.
      const saved = storage.get("state");
      if (saved) applySaved(saved);
    });
  • set es fire-and-forget. No esperás confirmación del parent. Si el parent está caído, los datos solo viven en el cache local de la tab actual hasta el próximo reload. Trade-off para no bloquear el game loop.

  • Múltiples tabs del mismo juego: cada tab tiene su cache independiente. Cambios en una tab NO se propagan a la otra hasta un reload. Si necesitás multi-tab live sync, abrí issue en el repo del SDK.

  • JSON serializable únicamente: si guardás un objeto con métodos / Date / Map, los recuperás como objeto plano sin esos campos.

Modo same-origin (dev local)

Cuando corras tu juego standalone (ej. python -m http.server en la carpeta del juego), el shim detecta que está en top-level (window.parent === window) y usa localStorage directo con prefix jugafy:store:<slug>:. Esto te permite desarrollar y testear sin la plataforma. Cuando subas el bundle a Jugafy, automáticamente cambia a modo bridge — sin cambios en tu código.

Para forzar window.NEON_GAME en dev local:

<script>window.NEON_GAME = "mi-juego";</script>
<script type="module" src="game.js"></script>

Versionado

Este módulo vive en v0.0.1-beta del SDK. La API es estable hasta que cambie a v1.0.0. Cambios breaking (si los hay) van a un nuevo path /sdk/v0.x.x-beta/ — los uploads ya servidos se quedan apuntando a su versión.

Ingresá a Jugafy

Guardá tus scores, subí al leaderboard y sincronizá progreso entre dispositivos.

Contanos

Bug, idea, crítica — todo nos sirve para hacer esto mejor.