Skip to content

Autosave

Drives saving for the lifetime of the game: on a periodic interval, when the tab goes to the background, and on page close. Install it once and stop thinking about save calls.

ts
import { autosave } from "@idlekitjs/plugins/autosave";

Browser lifecycle triggers

autosave drives a generic SaveManager, and its default triggers are browser lifecycle APIs (setInterval, visibilitychange, pagehide). Use it for browser games. Generic page lifecycle bridging lives in @idlekitjs/browser.

Usage

ts
import { SaveManager } from "@idlekitjs/core";
import { LocalStorageAdapter } from "@idlekitjs/storage/local-storage";
import { autosave } from "@idlekitjs/plugins/autosave";

const save = new SaveManager<State>({
  key: "my-game",
  version: 1,
  adapter: new LocalStorageAdapter(),
});

engine.use(
  autosave({
    manager: save,
    getState: () => engine.state,
    intervalMs: 15_000, // periodic safety net; 0 or omitted disables it
  }),
);

When it saves

TriggerWhy it matters
Every intervalMs (if > 0)Safety net against crashes
Tab hidden (visibilitychange)On mobile, a backgrounded tab may be killed without any further event
Page close (pagehide)More reliable than beforeunload, notably on mobile Safari

With the synchronous LocalStorageAdapter, the close-time write completes before the page actually unloads.

Missing manager or getState throws at wiring time — a clear startup failure instead of silently losing saves later.

Options

OptionRequiredRole
managerThe SaveManager to drive
getStateReturns the live state (() => engine.state)
intervalMsPeriodic autosave interval; 0/omitted disables

SaveScheduler

The scheduling core behind the plugin is public from the same subpath — use it when you need an imperative save outside the automatic triggers (a "Save now" button, saving after a milestone):

ts
import { SaveScheduler } from "@idlekitjs/plugins/autosave";

const scheduler = new SaveScheduler<State>({
  manager: save,
  getState: () => engine.state,
  intervalMs: 15_000,
});
scheduler.start(); // attach interval + lifecycle listeners
await scheduler.save(); // immediate save
scheduler.stop(); // detach everything

The autosave plugin is exactly this object tied to the extension lifecycle (setupstart, teardownstop).

Event-driven saves

For "save on meaningful progress", prefer the mechanics' callbacks — onComplete, onPurchase, onChange — calling save.save(state). Autosave is the safety net, not the only writer.

Common pitfalls

  • getState: () => snapshot — captures one stale object forever. Always return the live engine.state.
  • Relying on beforeunload — that's why the plugin uses pagehide and visibilitychange; don't add your own beforeunload writer on top.