/** * THE CREW BUS (wicked-core#631) — every bus row in-daemon crew code writes or reads goes through * the ENGINE that holds the bus. * * The daemon's bus file is the engine's (DES-TEAMING-002 T0): the engine opens it once, through its * bundled SQLite, and never closes it. A second SQLite library in the same process cannot share that * file safely: POSIX locks belong to the process, so two libraries do not exclude each other's * writes, and a close in either drops the other's locks (sqlite.org/howtocorrupt.html §2.2.1, * F-E2E-021). So crew holds no SQLite library at all. It emits with `Core.busEmit` and reads with * `Core.busRead`; the engine is the one writer and the one reader on its bus. * * The adapter attaches its engine to the bus file it handed it (`attachEngineBus`), and each seam * names the bus it uses by path. A seam whose bus no engine holds cannot arm: it throws, and the * seam logs and disables itself. tests/bus-no-write.test.ts fails the build if in-daemon code * names a SQLite library or wicked-bus. */ /** The engine's two bus calls (wicked-core-ts `Core.busEmit` / `Core.busRead`). */ export interface EngineBus { busEmit(eventJson: string): Promise; busRead(afterId: number, limit: number, typePrefix?: string | null, includeExpired?: boolean | null): Promise; } /** One row, as wicked-bus `emit` takes it. */ export interface BusRow { event_type: string; domain: string; subdomain?: string; payload: unknown; idempotency_key?: string; producer_id?: string; /** Per-event TTL in whole hours (wicked-bus `emit`'s override); default the bus config's. */ ttl_hours?: number; } /** One `events` row as the engine reads it back: every column the file has, `payload` parsed * (the stored string when it is not JSON). */ export interface BusEvent { event_id: number; event_type: string; domain: string; subdomain: string; payload: unknown; idempotency_key: string; emitted_at: number; [k: string]: unknown; } /** Test seam only: the bus for a path no engine is attached to (tests/setup/bus-double.ts). */ export declare const busTesting: { unattached: ((dbPath: string) => EngineBus) | undefined; }; /** The engine at `dbPath` serves every seam that names that bus (the adapter, once its engine is up). */ export declare function attachEngineBus(dbPath: string, engine: EngineBus): void; /** Undo {@link attachEngineBus} (the adapter, on close) — only if `engine` is still the one attached. */ export declare function detachEngineBus(dbPath: string, engine: EngineBus): void; /** The resolved path of the bus at `dbPath` when an engine holds it; throws when none does (a seam * checks this at arm time, so a seam with no bus disables itself before arming anything). */ export declare function requireEngineBus(dbPath: string | undefined): string; /** * Emit one row on the bus at `dbPath`. Resolves to the row's `event_id`; a key already on the bus * resolves to the existing row's id (the engine's dedup: success, as wicked-bus's WB-002 was). * Rejects when no engine holds the bus or the engine refuses the row (`WB-001 …`). */ export declare function emitOnBus(dbPath: string, row: BusRow): Promise; /** * Every row on the bus at `dbPath` whose type starts with `typePrefix`, oldest first. `history: * true` includes rows past their TTL: the daemon's bus is never swept, so an expired row is still * the record (the activity feed, the team route); a live consumer leaves it off. */ export declare function readBus(dbPath: string, typePrefix: string, opts?: { history?: boolean; }): Promise; export interface BusTapOptions { /** The bus db (the one the daemon handed its engine). */ dbPath: string | undefined; /** `.**` or an exact type, optionally `@` (`wicked.interactive.**`, `wicked.qe.**`, …). */ filter: string; /** Poll cadence, ms. */ pollIntervalMs?: number | undefined; /** Called once per matching row, in order; awaited before the next row. */ handler: (event: BusEvent) => void | Promise; /** A failed read (`event` null) or a failed handler (the row). Never retried; the cursor moves on. */ onError?: ((err: Error, event?: BusEvent | null) => void) | undefined; } export interface BusTap { /** The bus file this tap reads. */ readonly dbPath: string; /** Stop polling; resolves once an in-flight handler has returned. */ stop(): Promise; } /** * Arm a tap: poll the engine's bus from its newest row (rows already on the bus are history, as * wicked-bus's `latest` cursor treated them), cursor in memory, handing each matching row to the * handler in order. Rejects when no engine holds the bus or the filter is not one crew supports * (the caller logs and disables its seam); a failed read goes to `onError` and the next poll * retries it. A * restart starts at the newest row again, so rows emitted while the daemon was down are not * delivered; a failed handler is not retried. A bus file replaced under the same path is followed: * the engine answers cursor 0 and the tap reads the new file from its first row. */ export declare function tapBus(opts: BusTapOptions): Promise; //# sourceMappingURL=bus.d.ts.map