import type { Schema } from '@ecsia/schema'; import type { Buffers } from '../memory/index.js'; import type { TopicDef } from './define.js'; /** Reserved-zero metadata words per event row (future: tick + publishing system; payload-only v1). */ export declare const TOPIC_HEADER_WORDS = 2; /** * Shared per-topic header region (`topic..hdr`, u32×4): [headRel, baseRel, 0, 0], where * `headRel = head - tail` (the retained row count) and `baseRel = tail - baseSeq` (the ring-front * offset of the oldest retained row). Written by the main thread ONLY at serial slots; read by * worker consumers mid-wave with plain loads — frozen for the wave by the same quiescence argument * as archetype columns. * * Everything on the worker wire is TAIL-RELATIVE (hdr words, cursor-table slots, OP_CONSUMED seq): * absolute sequence numbers are unbounded JS integers main-side, and a wrapped u32 absolute would * stall consumers for ~2^32 events at the wrap boundary (max(unwrapped, wrapped) pins the cursor). * Relative values are bounded by the retained window — which fits u32 by construction — so the * wire is wrap-safe forever. The main thread reconstructs absolutes from its own `rt.tail`, which * cannot move during an update (retention drops only at the frame reset). */ export declare const TOPIC_HDR_HEAD_REL = 0; export declare const TOPIC_HDR_BASE_REL = 1; export declare const TOPIC_HDR_WORDS = 4; export interface TopicsConfig { readonly buffers: Buffers; /** Mint the next dense ComponentId (the topic's virtual id; same space as relations' synthetics). */ readonly allocId: () => number; readonly phase: () => 'serial' | 'wave'; readonly dev: boolean; readonly warn: (message: string) => void; } export declare class Topics { #private; constructor(cfg: TopicsConfig); /** Number of topics registered with this world. Zero ⇒ every per-frame hook is a no-op. */ get count(): number; /** Intern a topic with this world (idempotent): mints its virtual ComponentId + canonical ring. */ register(def: TopicDef): void; /** The topic's virtual ComponentId, or -1 if not registered. */ idOf(def: TopicDef): number; /** `(name, id)` pairs for worker id alignment (the boot manifest). */ manifest(): readonly { name: string; id: number; }[]; /** Scheduler frame hooks: world.publish is illegal while an update is in progress. */ beginUpdate(): void; endUpdate(): void; /** * `world.publish` — the outside-systems path (input/network handlers, between frames). Appends * directly to the canonical stream in call order, ahead of wave 0 of the next frame. Registers * the topic on first use. Serial phase only; inside a system body use `ctx.publish` instead * (direct appends mid-update would bypass the SystemId canonicalization). */ publishOutside(def: TopicDef, init: Record | undefined): void; /** `ctx.publish` (main-thread system) — encode now, stage to the system's segment. */ stageValues(def: TopicDef, systemId: number, init: Record | undefined): void; /** OP_PUBLISH apply (worker publishes replayed at the command flush) — stage raw field words. */ stageWords(topicId: number, systemId: number, words: ArrayLike, at: number, fieldWords: number): void; /** * The wave's serial-slot merge: for every topic with staged events, sort the staging segments by * publishing SystemId ascending and append each segment FIFO to the canonical stream. Runs once * per wave, after the wave's command flush — the single canonicalization point for every * execution mode. */ mergeStaged(): void; /** * Eagerly position a reader's cursor at plan time. Before the first update has ever run * (the FIRST plan), the cursor starts at the oldest retained event so `world.publish` calls made * before `createScheduler` are still delivered; once any update has run, a newly-added reader * (a late re-plan join) starts at the current head — no replay of stale retained events. */ initCursor(def: TopicDef, readerKey: string): void; /** * The cursor-table slot for `(def, readerKey)`, assigned on first request and stable for the * world's lifetime (re-plans re-request and get the same slot — cursor continuity mirrors the * name-keyed map). The pool ships this to workers at boot; a worker consumer reads its own slot * mid-wave (frozen — the main thread writes cursors only at serial slots or for systems running * in OTHER batches, never the one reading it). */ readerSlotFor(def: TopicDef, readerKey: string): number; /** * OP_CONSUMED apply: a worker-run consumer observed events up to `seq` (exclusive) mid-wave. * Idempotent and monotonic — `max` absorbs duplicate records and replays. The missed-events * warning fires here with the same wording as the main-thread consume path: the cursor the worker * started from is the map value, and `tail` cannot have moved since the wave started (retention * drops only at the frame reset). */ advanceFromWorker(topicId: number, readerKey: string, relSeq: number): void; /** * Iterate the reader's unseen events up to the current visible head and advance its cursor — * exactly-once per (reader, topic), independent cursors per reader (no drain stealing). The * yielded view is pooled: read fields inside the loop, never store the view itself. * * A reader with no cursor starts at the oldest retained event (the manual-consumption default); * scheduler consumers are positioned eagerly via `initCursor` at plan time — at the oldest * retained event for the first plan (pre-plan `world.publish` events are delivered), at the * current head for a re-plan join (no replay). */ consume(def: TopicDef, readerKey: string): IterableIterator>; /** * Frame reset (serial, quiescent): drop events two frames old, fold any spill back into the ring * (regrowing it to 2x the observed peak), and compact retained rows to the ring front. */ frameReset(): void; /** Oldest retained / next sequence numbers for `def`. */ bounds(def: TopicDef): { tail: number; head: number; }; /** * A copy of the retained canonical stream `[tail, head)` in sequence order — the byte-identity * surface the serial-equivalence suite compares across worker counts and transports. */ streamWords(def: TopicDef): Uint32Array; } //# sourceMappingURL=store.d.ts.map