Fox Events Fox Events

Centralizing events in foxes.ts

Best practice: define all event names and payload types in a single file (e.g. foxes.ts) and import from there. That gives you one source of truth, type safety, and easier refactors.

Why centralize?

  • Single source of truth — Event names live in one place; no typos or duplicated strings across the app.
  • Typed payloads — Use an EventMap so emit and on are type-checked.
  • Easier refactors — Rename or add events in one file; TypeScript will flag every usage.
  • Discoverability — New developers see all app events in one file.

Example: foxes.ts

Create a file (e.g. src/foxes.ts or lib/foxes.ts) that defines your event map and optionally exports typed channels or helpers.

ts
import { Fox } from "fox-events";

// 1. Define the event map: event name → payload type (use void for no payload)
export type AppEvents = {
  "user:login": { userId: string; email?: string };
  "user:logout": void;
  "app:ready": { version: string };
  "app:command": { action: "reload" | "back" };
};

// 2. Optional: typed helpers so you don't repeat the generic everywhere
export const emit = Fox.emit<AppEvents>;
export const on = Fox.on<AppEvents>;
export const once = Fox.once<AppEvents>;
export const emitAsync = Fox.emitAsync<AppEvents>;

// 3. Optional: pre-created channels if you prefer channel API over static
export const channels = {
  userLogin: Fox.channel<AppEvents["user:login"]>("user:login"),
  userLogout: Fox.channel<AppEvents["user:logout"]>("user:logout"),
  appReady: Fox.channel<AppEvents["app:ready"]>("app:ready"),
  appCommand: Fox.channel<AppEvents["app:command"]>("app:command"),
} as const;

Using it in the app

With static API (emit/on/once):

ts
import { emit, on, once } from "@/foxes";

// Typed: payload must match AppEvents["user:login"]
emit("user:login", { userId: "u-1" });

on("app:ready", (payload) => {
  console.log(payload.version);
});

const payload = await once("user:login");

With channels:

ts
import { channels } from "@/foxes";

channels.userLogin.emit({ userId: "u-1" });
channels.appReady.on((payload) => console.log(payload.version));
const payload = await channels.userLogin.once();

If you only export the type:

ts
import { Fox } from "fox-events";
import type { AppEvents } from "@/foxes";

Fox.emit<AppEvents>("user:login", { userId: "u-1" });
Fox.on<AppEvents>("app:ready", (payload) => {});

Naming conventions

  • Use a domain:action style so names stay consistent and easy to search (e.g. user:login, app:ready, cart:item-added).
  • Keep the event map in sync: add new events in foxes.ts first, then use them.

Summary

ApproachUse when
Export AppEvents onlyYou want one central type and are fine with Fox.emit<AppEvents>(...) everywhere.
Export emit / on / onceYou want a drop-in replacement for Fox with types already applied.
Export channelsYou prefer the channel API and want named channels (e.g. channels.userLogin) from one place.

Putting everything in foxes.ts keeps event names and payloads consistent and makes the rest of the app easier to maintain.