# API reference

> Reference for every public export of @dirtytalk/engine with exact TypeScript signatures, parameters, return values, behaviour and examples.

Source: https://dirtytalk.42.dog/docs/engine/api/

All exports of `@dirtytalk/engine`, with signatures taken from the source. For explanations see [DirtyChannel](/docs/engine/dirty-channel/), [Schedulers](/docs/engine/schedulers/) and [Custom spaces](/docs/engine/custom-spaces/).

```ts
import {
  DirtyChannel,
  SyncScheduler,
  ManualScheduler,
  MicrotaskScheduler,
  RAFScheduler,
} from '@dirtytalk/engine';
import type { Space, Scheduler } from '@dirtytalk/engine';
```

## Types

### Space

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

The algebra of "what changed" and "what I care about". Contracts: `union(empty(), r)` equals `r`; `intersects(empty(), _)` is `false`; all operations are pure, except that `unionInto` may mutate and return `acc`.

| Method                        | Returns                                                               |
| ----------------------------- | --------------------------------------------------------------------- |
| `empty()`                     | A region meaning "nothing".                                           |
| `isEmpty(r)`                  | `true` if `r` is empty.                                               |
| `union(a, b)`                 | A region covering both. May return `a` or `b` by reference.           |
| `intersects(interest, dirty)` | `true` if a subscriber with `interest` should be notified of `dirty`. |
| `unionInto(acc, b)`           | Optional. `union` that may mutate `acc`; must not mutate `b`.         |

```ts
import type { Space } from '@dirtytalk/engine';

const BitSpace: Space<number> = {
  empty: () => 0,
  isEmpty: (r) => r === 0,
  union: (a, b) => a | b,
  intersects: (i, d) => (i & d) !== 0,
};
```

See [Custom spaces](/docs/engine/custom-spaces/).

### Scheduler

```ts
interface Scheduler {
  request(flush: () => void): void;
  cancel?(flush: () => void): void;
}
```

Decides when a flush runs. `request` asks for `flush` to be called later; `cancel`, if present, drops a pending request.

```ts
import type { Scheduler } from '@dirtytalk/engine';

const immediate: Scheduler = {
  request: (flush) => flush(),
};
```

See [Schedulers](/docs/engine/schedulers/#writing-a-custom-scheduler).

## Classes

### DirtyChannel

```ts
class DirtyChannel<Region> {
  constructor(
    space: Space<Region>,
    scheduler: Scheduler,
    options?: { onError?: (err: unknown) => void },
  );
  mark(r: Region): void;
  subscribe(interest: () => Region, cb: (dirty: Region) => void): () => void;
  dispose(): void;
}
```

Accumulates marked regions and delivers them to interested subscribers in one flush per scheduler window.

```ts
import { DirtyChannel, ManualScheduler } from '@dirtytalk/engine';
import type { Space } from '@dirtytalk/engine';

const BitSpace: Space<number> = {
  empty: () => 0,
  isEmpty: (r) => r === 0,
  union: (a, b) => a | b,
  intersects: (i, d) => (i & d) !== 0,
};

const scheduler = new ManualScheduler();
const channel = new DirtyChannel(BitSpace, scheduler, {
  onError: (err) => console.error(err),
});

const unsubscribe = channel.subscribe(
  () => 0b01,
  (dirty) => console.log('dirty', dirty),
);
channel.mark(0b01);
scheduler.pump(); // logs: dirty 1
unsubscribe();
channel.dispose();
```

#### constructor

| Parameter         | Description                                                                             |
| ----------------- | --------------------------------------------------------------------------------------- |
| `space`           | The `Space` used to combine and compare regions.                                        |
| `scheduler`       | The `Scheduler` that triggers flushes. Not called during construction.                  |
| `options.onError` | Optional handler for subscriber errors. Without it, errors are rethrown from the flush. |

#### mark

`mark(r: Region): void`. Merges `r` into the pending dirty region and requests a flush if none is pending. Returns nothing. Does nothing after `dispose()`. Throws only if `space.union`/`unionInto` throws, or if the scheduler flushes synchronously and the flush rethrows subscriber errors.

#### subscribe

`subscribe(interest, cb): () => void`. Registers a subscriber. `interest` is evaluated once per flush; `cb` is called with the full dirty region when `space.intersects(interest(), dirty)` is true. Returns an idempotent unsubscribe function. After `dispose()`, returns a no-op function. Does not throw.

#### dispose

`dispose(): void`. Cancels a pending flush, drops subscribers and pending marks, and makes the channel inert. Idempotent.

#### Flush errors

During a flush, errors from `interest`, `space.intersects` and `cb` are isolated per subscriber. With `onError`, each is passed to it. Without, after all subscribers have run, a single error is rethrown as-is and several are rethrown as `AggregateError` with message `DirtyChannel: subscriber errors during flush`.

### SyncScheduler

```ts
class SyncScheduler implements Scheduler {
  request(flush: () => void): void;
}
```

Calls `flush` immediately inside `request`. No deduplication, no `cancel`. Throws whatever `flush` throws.

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

declare const space: Space<number>;
const channel = new DirtyChannel(space, new SyncScheduler());
```

### ManualScheduler

```ts
class ManualScheduler implements Scheduler {
  request(flush: () => void): void;
  cancel(flush: () => void): void;
  pump(): void;
}
```

Holds requests until `pump()`. Requests of the same function are deduplicated.

| Method           | Description                                                                                                                                                                                                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `request(flush)` | Adds `flush` to the pending set.                                                                                                                                                                                                                         |
| `cancel(flush)`  | Removes `flush` from the pending set.                                                                                                                                                                                                                    |
| `pump()`         | Runs the flushes pending now; requests made during the pump wait for the next one. No-op if none pending. Runs every flush even if some throw, then rethrows a single error as-is or an `AggregateError` (`ManualScheduler: flush errors during drain`). |

```ts
import { ManualScheduler } from '@dirtytalk/engine';

const scheduler = new ManualScheduler();
scheduler.request(() => console.log('flushed'));
scheduler.pump(); // logs: flushed
```

### MicrotaskScheduler

```ts
class MicrotaskScheduler implements Scheduler {
  request(flush: () => void): void;
  cancel(flush: () => void): void;
}
```

Runs all pending flushes in one `queueMicrotask` callback. Duplicates are deduplicated. `cancel` removes a flush from the pending set. Errors from flushes are rethrown inside the microtask after all have run (`MicrotaskScheduler: flush errors during drain` for several).

```ts
import { MicrotaskScheduler } from '@dirtytalk/engine';

const scheduler = new MicrotaskScheduler();
scheduler.request(() => console.log('flushed'));
await Promise.resolve(); // logs: flushed
```

### RAFScheduler

```ts
class RAFScheduler implements Scheduler {
  request(flush: () => void): void;
  cancel(flush: () => void): void;
}
```

Runs all pending flushes on the next animation frame. If `globalThis.requestAnimationFrame` is not a function when the scheduler is constructed, it uses `setTimeout(..., 16)` instead. `cancel` removes a flush and, if nothing else is pending, cancels the frame or timer. Errors are rethrown after all flushes have run (`RAFScheduler: flush errors during drain` for several).

```ts
import { RAFScheduler } from '@dirtytalk/engine';

const scheduler = new RAFScheduler();
scheduler.request(() => console.log('next frame'));
```
