UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

71 lines 4.7 kB
/** * # bus/read — the crash-tolerant, schema-validating bus reader (RUNTIME §1, §8) * * `readBus` turns bus text (or a bus file) into a stream of typed {@link BusEvent}s. It is * **crash-tolerant** exactly where a single-writer append-only log can tear: the very last * line, if the process died mid-write, is an incomplete JSON record with no trailing * newline — that torn final line is **dropped silently**. Any OTHER unparseable line is * **mid-file corruption**: it throws loudly with the 1-based line number (RUNTIME §1). A * `META` header with an unrecognized `bus_schema` is refused loudly (fail-closed, §8). * * The distinction is precise: a well-formed writer always ends a record with `\n` * ({@link ../bus/write.ts serializeEvent}), so a file that ends WITHOUT a newline has a * partially-written tail. Only that tail may be dropped; an interior blank/garbage line * means the log itself is damaged and the reader must not paper over it. * * Two **forward-compatible** tolerances keep an older build reading a newer bus without crashing * (fail-closed to a logged skip, never a false success, §8): (1) the META header may carry any * schema in {@link SUPPORTED_BUS_SCHEMAS} — a v1 bus (no JOURNAL) reads clean under a v2 reader; * (2) a well-formed {@link JournalEvent JOURNAL} with an unrecognized `kind` is **skipped with a * logged reason** ({@link BusReadOptions.onSkip}) rather than thrown — its `seq` is still consumed * so the log stays gap-free. Neither tolerance touches the engine: JOURNAL is author metadata. * * Validation is **fail-closed and complete** (RUNTIME §1), in two layers. Per-record * ({@link validate}): finite `seq`/`ts`, a legal `stream`, and a `type` that actually pairs * with that stream on the discriminated union (`isValidStreamType` — a `TICK` is `SPOT | BOOK * | HEARTBEAT`, an `ORDER` a `place | cancel | fill | reject`, …); META always carries the * supported `bus_schema`. Cross-record ({@link readBusText}): a well-formed bus opens with * **exactly one META** header (first event, no duplicate) and `seq` is **strictly monotonic, * gap-free from 0**. Every violation is a loud, line-located {@link BusReadError} — the reader * never widens a malformed record onto {@link BusEvent} by an unchecked cast. */ import type { BusEvent } from "./types.ts"; /** * A sink for a crash-tolerant SKIP: a record that is well-formed but **forward-compatible- * unknown** — today only a {@link JournalEvent JOURNAL} whose `kind` this build does not * recognize. The record is dropped from the yielded stream and its reason logged; it is never * thrown (fail-closed to a logged skip, RUNTIME §8) and its `seq` is still consumed so the log * stays gap-free. Defaults to a `console.warn` when the caller supplies none. */ export type BusSkipSink = (reason: string, line: number) => void; /** Options for the bus reader. */ export interface BusReadOptions { /** Where a crash-tolerant SKIP is reported (see {@link BusSkipSink}). */ readonly onSkip?: BusSkipSink; } /** A loud, located bus corruption error (mid-file damage or a schema violation). */ export declare class BusReadError extends Error { readonly line: number; constructor(message: string, line: number); } /** * Read typed events from bus **text** (already-loaded JSONL). Torn final line dropped silently; * interior corruption throws loudly with the line number. * * Beyond per-record validation ({@link validate}), this loop enforces the **cross-record** * well-formedness a single record cannot see (RUNTIME §1): a well-formed bus opens with * **exactly one META** — the header must be the **first** event and no second META may appear — * and `seq` is **strictly monotonic, gap-free from 0** (`0, 1, 2, …`). A backward, skipped, or * teleported `seq`, a missing/late header, or a duplicate header is mid-file corruption and * raises loudly. (A torn final line is still dropped first — it never reaches these checks.) */ export declare function readBusText(text: string, opts?: BusReadOptions): Generator<BusEvent>; /** Read typed events from a bus **file** (synchronous, deterministic — no wall clock). */ export declare function readBusFile(path: string, opts?: BusReadOptions): Generator<BusEvent>; /** * Read typed events from a bus given **either a path or raw JSONL content** (RUNTIME §1). * Content is detected structurally (a JSONL bus begins with `{`); anything else is read as a * filesystem path. Crash-tolerant and schema-validating per {@link readBusText}. */ export declare function readBus(source: string, opts?: BusReadOptions): Generator<BusEvent>; //# sourceMappingURL=read.d.ts.map