Fox Events Fox Events

Storage

IndexedDB persistence, hydrate, custom storage.

IndexedDBStorage

ts
import { Fox } from "fox-events";
import { IndexedDBStorage } from "fox-events/storage";

const storage = new IndexedDBStorage();
const channel = Fox.channel<{ id: string }>("user:action", {
  den: { storage },
});
// autoPersist defaults to true when storage is provided

channel.emit({ id: "a-1" });
storage.close();

Manual persist (without autoPersist)

If you set autoPersist: false, events are not saved automatically. You can still pass storage and call hydrate() to load previous state. To persist manually (e.g. on page unload or when you choose), get the trail and write it to storage using the same key format the library uses: ${keyPrefix}:${channel.name}:published, ${keyPrefix}:${channel.name}:received, and ${keyPrefix}:${channel.name}:unsubscribed. Use the same keyPrefix you passed in options (default is "fox").

ts
import { Fox } from "fox-events";
import { IndexedDBStorage } from "fox-events/storage";

const storage = new IndexedDBStorage();
const keyPrefix = "myapp";
const channel = Fox.channel<{ id: string }>("user:action", {
  den: { storage, keyPrefix, autoPersist: false },
});

channel.emit({ id: "a-1" });

// Later: persist manually (e.g. on page unload)
const t = channel.trail();
await storage.setItem(`${keyPrefix}:${channel.name}:published`, t.published);
await storage.setItem(`${keyPrefix}:${channel.name}:received`, t.received);
await storage.setItem(`${keyPrefix}:${channel.name}:unsubscribed`, t.unsubscribed);

Hydrate on Init

ts
const channel = Fox.channel<{ id: string }>("user:action", {
  den: { storage, hydrateOnInit: true },
});

await channel.hydrate();

Key Prefix

ts
const channel = Fox.channel("user:action", {
  den: { storage, keyPrefix: "myapp" },
});

Custom Storage

You can implement IEventStorage with any backend. Two examples:

SessionStorage (in-memory per tab):

ts
import type { IEventStorage } from "fox-events/storage";

const sessionStorageAdapter: IEventStorage = {
  async get(key) {
    const raw = sessionStorage.getItem(key);
    return raw ? JSON.parse(raw) : null;
  },
  async setItem(key, value) {
    sessionStorage.setItem(key, JSON.stringify(value));
  },
  async removeItem(key) {
    sessionStorage.removeItem(key);
  },
  async clear() {
    sessionStorage.clear();
  },
};

Fox.channel("session-events", { den: { storage: sessionStorageAdapter } });

HTTP (persist via API):

ts
import type { IEventStorage } from "fox-events/storage";

const API_BASE = "https://api.example.com/events";

const httpStorage: IEventStorage = {
  async get(key) {
    const res = await fetch(`${API_BASE}/${encodeURIComponent(key)}`);
    if (!res.ok) return null;
    return res.json();
  },
  async setItem(key, value) {
    await fetch(`${API_BASE}/${encodeURIComponent(key)}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(value),
    });
  },
  async removeItem(key) {
    await fetch(`${API_BASE}/${encodeURIComponent(key)}`, { method: "DELETE" });
  },
  async clear() {
    await fetch(API_BASE, { method: "DELETE" });
  },
};

Fox.channel("remote-events", { den: { storage: httpStorage } });