/** * Incremental reader for the `files` member of a legacy journal document. * * WHY A HAND-WRITTEN SCANNER AND NOT `JSON.parse` * ----------------------------------------------- * The manifest builder's tail step reads ONE journal shard and needs six * fields per row. `JSON.parse` cannot give it those without first * materialising the entire document twice over: once as a UTF-8 string the * size of the file, and again as a parsed object graph. On a 200,000-entry * shard (~34 MB on disk) that pair costs ~200 MB of resident growth before the * caller has looked at a single row — and RSS, unlike heap, does not come back * when the garbage collector frees it. The transient allocation IS the peak, * and the peak is the budget. * * Streaming the document in 256 KB chunks and parsing each row's small value * object on its own keeps the transient set bounded by one chunk plus one row, * so the resident cost becomes the compact index the caller retains and * nothing else. Measured on the 200,000-entry bench fixture: 260 MB → 182 MB * of resident growth, and (perhaps counter-intuitively) faster than * `JSON.parse`, because 200,000 small parses beat one 34 MB parse plus the * `Object.entries` array it takes to walk the result. * * WHAT THIS DELIBERATELY IS NOT * ----------------------------- * It is not a general JSON parser and must never be used as one. It tracks * just enough structure — string/escape state and container depth — to find * the top-level `files` object and slice out each of its members verbatim. * Every value is then handed to the real `JSON.parse`, so number, escape, and * unicode semantics are V8's rather than this file's. Anything malformed * surfaces as a `SyntaxError` from that parse, which callers already treat as * a corrupt shard. * * On-disk format is untouched: this reads exactly the bytes the existing * writer produces. */ /** * Invoked once per member of the `files` object, in document order. * * `valueJson` is the member's raw JSON text. The callback owns the decision to * parse it — which is the point, since a caller keeping four fields out of ten * should never pay to build the other six. */ export type JournalLedgerRowVisitor = (key: string, valueJson: string) => void; /** * Stream the `files` members of the journal document at `filePath`. * * Stops reading as soon as the `files` object closes: a shard's `pulls` tail * is not the ledger, and on a large journal declining to read it is free. * * Throws whatever `fs` throws for an unreadable file, and `SyntaxError` for a * row whose JSON is malformed. A document with no top-level `files` member * simply yields nothing — the same thing an empty ledger yields, which is the * correct reading of a locator file. */ export declare function streamJournalLedgerRows(filePath: string, onRow: JournalLedgerRowVisitor): void; //# sourceMappingURL=journal-ledger-stream.d.ts.map