Skip to content

Memory adapter

An in-memory SaveAdapter backed by a Map. Nothing persists beyond the process — which is exactly what tests, SSR and ephemeral sessions want.

ts
import { MemoryAdapter } from "@idlekitjs/storage/memory";

Usage

ts
import { SaveManager } from "@idlekitjs/core";
import { MemoryAdapter } from "@idlekitjs/storage/memory";

const save = new SaveManager<State>({
  key: "test",
  version: 1,
  adapter: new MemoryAdapter(),
});

In tests

The main use: exercising real save/load flows — including migrations and the loaded event — without a browser:

ts
import { describe, it, expect } from "vitest";
import { createEngine, SaveManager } from "@idlekitjs/core";
import { MemoryAdapter } from "@idlekitjs/storage/memory";

it("restores state through a save round-trip", async () => {
  const adapter = new MemoryAdapter();
  const save = new SaveManager<State>({ key: "t", version: 1, adapter });

  const engine = createEngine<State>({ initialState: { coins: 42 } });
  await save.save(engine.state);

  const fresh = createEngine<State>({ initialState: { coins: 0 } });
  const freshSave = new SaveManager<State>({ key: "t", version: 1, adapter });
  await fresh.load(freshSave);

  expect(fresh.state.coins).toBe(42);
});

You can also pre-seed the adapter to simulate an existing save (old versions for migration tests, a savedAt in the past for offline-progress tests):

ts
adapter.write(
  "t",
  JSON.stringify({ version: 1, savedAt: Date.now() - 3_600_000, state: { coins: 10 } }),
);

Notes

  • Each MemoryAdapter instance is its own isolated store — share the instance if two SaveManagers must see the same data.
  • All methods are synchronous; the SaveManager awaits them anyway, so the calling code is identical to any other backend.