01 @dirtytalk/engine

Engine

The algebra. Mark, coalesce, flush.

Chapter 01 Watch engine run, interactively →

@dirtytalk/engine has zero dependencies. It ships the algebra and the scheduling glue, but no concrete region type.

pnpm add @dirtytalk/engine

Space

A Space<Region> describes what “changed” means for your domain.

interface Space<Region> {
  empty(): Region;
  isEmpty(r: Region): boolean;
  union(a: Region, b: Region): Region;
  intersects(interest: Region, dirty: Region): boolean;
}

Both union and intersects must be pure.

Scheduler

A scheduler decides when a flush runs.

SchedulerFlushes
SyncSchedulerimmediately
ManualSchedulerwhen you call .pump()
MicrotaskSchedulerat the end of the microtask queue
RAFScheduleron the next animation frame

DirtyChannel

import { DirtyChannel, SyncScheduler } from '@dirtytalk/engine';
import type { Space } from '@dirtytalk/engine';

const KeySpace: Space<Set<string>> = {
  empty: () => new Set(),
  isEmpty: (r) => r.size === 0,
  union: (a, b) => new Set([...a, ...b]),
  intersects: (interest, dirty) => [...interest].some((k) => dirty.has(k)),
};

const channel = new DirtyChannel(KeySpace, new SyncScheduler());

const unsub = channel.subscribe(
  () => new Set(['users']),
  (dirty) => console.log('dirty:', [...dirty]),
);

channel.mark(new Set(['users'])); // dirty: ['users']
channel.mark(new Set(['theme'])); // no output

unsub();