Appearance
Local storage adapter
The browser backend: a SaveAdapter over window.localStorage. The default choice for shipping a web game.
ts
import { LocalStorageAdapter } from "@idlekitjs/storage/local-storage";Usage
ts
import { SaveManager } from "@idlekitjs/core";
import { LocalStorageAdapter } from "@idlekitjs/storage/local-storage";
const save = new SaveManager<State>({
key: "my-game", // the localStorage key
version: 1,
adapter: new LocalStorageAdapter(),
});Why localStorage (and not IndexedDB) by default
localStorage is synchronous — a write started in a pagehide handler completes before the page unloads. That is precisely the guarantee the autosave plugin relies on for its close-time save. IndexedDB is async and can lose that race. For the size of an idle-game save (kilobytes of JSON), synchronous is a feature, not a bottleneck.
Complete autosave setup
ts
import { createEngine, SaveManager } from "@idlekitjs/core";
import { LocalStorageAdapter } from "@idlekitjs/storage/local-storage";
import { autosave } from "@idlekitjs/plugins/autosave";
import { offlineProgress } from "@idlekitjs/plugins/offline-progress";
import { pageLifecycle } from "@idlekitjs/browser/page-lifecycle";
const engine = createEngine<State>({ initialState /* , renderer, scheduler */ });
const save = new SaveManager<State>({
key: "my-game",
version: 1,
adapter: new LocalStorageAdapter(),
});
engine.use(pageLifecycle());
engine.use(offlineProgress({ maxMs: 8 * 60 * 60 * 1000 }));
engine.use(autosave({ manager: save, getState: () => engine.state, intervalMs: 15_000 }));
await engine.load(save);
engine.start();A reset button is one call:
ts
resetEl.addEventListener("click", () => {
void save.clear().then(() => location.reload());
});Notes and limits
- Quota — typically ~5 MB per origin; monumental for JSON saves, but don't store assets in it.
- Corruption is handled — a hand-edited or truncated save makes
load()returnnull(fresh start), never throw; prototype-pollution via edited saves is neutralized by theSaveManager. - Private browsing — some browsers throw on write when storage is full or restricted; the
SaveManagercatches read errors, but a fully unavailablelocalStorageis a platform decision your game may want to surface. - Not for Node/SSR — the adapter assumes the global exists; use
MemoryAdapterin headless contexts.