@zakkster/lite-bake-stream 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +457 -0
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/SPEC.md +364 -0
- package/llms.txt +81 -0
- package/package.json +117 -0
- package/src/FileIngest.js +104 -0
- package/src/MultiReader.js +160 -0
- package/src/PreserveReader.js +180 -0
- package/src/PreserveTokenizer.js +172 -0
- package/src/PreserveWriter.js +218 -0
- package/src/RangeReader.js +470 -0
- package/src/Reader.js +349 -0
- package/src/Split.js +359 -0
- package/src/StringTable.js +225 -0
- package/src/Tokenizer.js +691 -0
- package/src/Writer.js +713 -0
- package/src/index.js +193 -0
- package/types/FileIngest.d.ts +44 -0
- package/types/MultiReader.d.ts +36 -0
- package/types/PreserveReader.d.ts +44 -0
- package/types/PreserveTokenizer.d.ts +27 -0
- package/types/PreserveWriter.d.ts +35 -0
- package/types/RangeReader.d.ts +93 -0
- package/types/Reader.d.ts +85 -0
- package/types/Split.d.ts +66 -0
- package/types/StringTable.d.ts +23 -0
- package/types/Tokenizer.d.ts +42 -0
- package/types/Writer.d.ts +92 -0
- package/types/index.d.ts +58 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// @zakkster/lite-bake-stream / top-level convenience API
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
//
|
|
4
|
+
// The one-liners most consumers want. Two modes are dispatched from a single
|
|
5
|
+
// API:
|
|
6
|
+
// schema mode (default) - Tokenizer + Writer, F64/U32 lanes, zone maps, query pruning.
|
|
7
|
+
// preserve mode - PreserveTokenizer + PreserveWriter, opaque byte blobs.
|
|
8
|
+
// Set opts.preserve = true. Bytes in, same bytes out.
|
|
9
|
+
// deserialize() auto-detects the mode via the container's flag bit and
|
|
10
|
+
// returns the appropriate Reader.
|
|
11
|
+
|
|
12
|
+
import { Tokenizer, TokenizerError } from './Tokenizer.js';
|
|
13
|
+
import { Writer, WriterError } from './Writer.js';
|
|
14
|
+
import { Reader, ReaderError } from './Reader.js';
|
|
15
|
+
import { StringTable } from './StringTable.js';
|
|
16
|
+
import { PreserveTokenizer, PreserveTokenizerError } from './PreserveTokenizer.js';
|
|
17
|
+
import { PreserveWriter, PreserveWriterError } from './PreserveWriter.js';
|
|
18
|
+
import { PreserveReader, PreserveReaderError } from './PreserveReader.js';
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
Tokenizer, TokenizerError,
|
|
22
|
+
Writer, WriterError,
|
|
23
|
+
Reader, ReaderError,
|
|
24
|
+
StringTable,
|
|
25
|
+
PreserveTokenizer, PreserveTokenizerError,
|
|
26
|
+
PreserveWriter, PreserveWriterError,
|
|
27
|
+
PreserveReader, PreserveReaderError,
|
|
28
|
+
};
|
|
29
|
+
export const VERSION = '1.0.0';
|
|
30
|
+
|
|
31
|
+
const encoder = new TextEncoder();
|
|
32
|
+
|
|
33
|
+
// Serialize input data into an LBK1 container. Input may be:
|
|
34
|
+
// - Uint8Array (raw NDJSON bytes)
|
|
35
|
+
// - string (NDJSON text)
|
|
36
|
+
// - Iterable<object> (records; JSON.stringify'd internally). Schema mode only.
|
|
37
|
+
// - AsyncIterable<Uint8Array> or ReadableStream<Uint8Array>
|
|
38
|
+
//
|
|
39
|
+
// Options:
|
|
40
|
+
// preserve: when true, use preserve mode (opaque byte blobs, any JSON shape
|
|
41
|
+
// allowed). When false (default), schema mode with lane packing.
|
|
42
|
+
// framing: 'ndjson' | 'array' | 'auto'. Preserve mode is NDJSON-only.
|
|
43
|
+
// writer: writer options. Schema mode: { schema, targetShardBytes, sampleBytes }.
|
|
44
|
+
// Preserve mode: { targetShardBytes, maxRecordBytes }.
|
|
45
|
+
export function serialize(input, opts) {
|
|
46
|
+
opts = opts || {};
|
|
47
|
+
const preserve = opts.preserve === true;
|
|
48
|
+
|
|
49
|
+
if (input && typeof input.getReader === 'function') {
|
|
50
|
+
return preserve
|
|
51
|
+
? _serializeReadableStreamPreserve(input, opts)
|
|
52
|
+
: _serializeReadableStream(input, opts.writer || {}, opts.framing || 'ndjson');
|
|
53
|
+
}
|
|
54
|
+
if (input && typeof input[Symbol.asyncIterator] === 'function') {
|
|
55
|
+
return preserve
|
|
56
|
+
? _serializeAsyncIterablePreserve(input, opts)
|
|
57
|
+
: _serializeAsyncIterable(input, opts.writer || {}, opts.framing || 'ndjson');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (preserve) return _serializeSyncPreserve(input, opts);
|
|
61
|
+
return _serializeSyncSchema(input, opts.writer || {}, opts.framing || 'ndjson');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _serializeSyncSchema(input, writerOpts, framing) {
|
|
65
|
+
const w = new Writer(writerOpts);
|
|
66
|
+
const t = new Tokenizer(w, { framing });
|
|
67
|
+
if (input instanceof Uint8Array) {
|
|
68
|
+
t.feed(input);
|
|
69
|
+
} else if (typeof input === 'string') {
|
|
70
|
+
t.feed(encoder.encode(input));
|
|
71
|
+
} else if (input && typeof input[Symbol.iterator] === 'function') {
|
|
72
|
+
for (const record of input) {
|
|
73
|
+
if (record === null || typeof record !== 'object') {
|
|
74
|
+
throw new TypeError('serialize: iterable must yield objects, got ' + typeof record);
|
|
75
|
+
}
|
|
76
|
+
t.feed(encoder.encode(JSON.stringify(record) + '\n'));
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
throw new TypeError('serialize: unsupported input type');
|
|
80
|
+
}
|
|
81
|
+
t.end();
|
|
82
|
+
return new Uint8Array(w.finalize().buffer);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function _serializeSyncPreserve(input, opts) {
|
|
86
|
+
const w = new PreserveWriter(opts.writer || {});
|
|
87
|
+
const t = new PreserveTokenizer(w, {
|
|
88
|
+
framing: 'ndjson',
|
|
89
|
+
maxRecordBytes: (opts.writer && opts.writer.maxRecordBytes) || 0,
|
|
90
|
+
});
|
|
91
|
+
if (input instanceof Uint8Array) {
|
|
92
|
+
t.feed(input);
|
|
93
|
+
} else if (typeof input === 'string') {
|
|
94
|
+
t.feed(encoder.encode(input));
|
|
95
|
+
} else if (input && typeof input[Symbol.iterator] === 'function') {
|
|
96
|
+
// Iterable of records: JSON.stringify each and feed as NDJSON.
|
|
97
|
+
for (const record of input) {
|
|
98
|
+
if (record === null || typeof record !== 'object') {
|
|
99
|
+
throw new TypeError('serialize (preserve): iterable must yield objects, got ' + typeof record);
|
|
100
|
+
}
|
|
101
|
+
t.feed(encoder.encode(JSON.stringify(record) + '\n'));
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
throw new TypeError('serialize: unsupported input type');
|
|
105
|
+
}
|
|
106
|
+
t.end();
|
|
107
|
+
return new Uint8Array(w.finalize().buffer);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function _serializeReadableStream(stream, writerOpts, framing) {
|
|
111
|
+
const w = new Writer(writerOpts);
|
|
112
|
+
const t = new Tokenizer(w, { framing });
|
|
113
|
+
const reader = stream.getReader();
|
|
114
|
+
try {
|
|
115
|
+
while (true) {
|
|
116
|
+
const { value, done } = await reader.read();
|
|
117
|
+
if (done) break;
|
|
118
|
+
if (!(value instanceof Uint8Array)) throw new TypeError('serialize: stream must yield Uint8Array');
|
|
119
|
+
t.feed(value);
|
|
120
|
+
}
|
|
121
|
+
} finally { reader.releaseLock(); }
|
|
122
|
+
t.end();
|
|
123
|
+
return new Uint8Array(w.finalize().buffer);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function _serializeReadableStreamPreserve(stream, opts) {
|
|
127
|
+
const w = new PreserveWriter(opts.writer || {});
|
|
128
|
+
const t = new PreserveTokenizer(w, {
|
|
129
|
+
framing: 'ndjson',
|
|
130
|
+
maxRecordBytes: (opts.writer && opts.writer.maxRecordBytes) || 0,
|
|
131
|
+
});
|
|
132
|
+
const reader = stream.getReader();
|
|
133
|
+
try {
|
|
134
|
+
while (true) {
|
|
135
|
+
const { value, done } = await reader.read();
|
|
136
|
+
if (done) break;
|
|
137
|
+
if (!(value instanceof Uint8Array)) throw new TypeError('serialize: stream must yield Uint8Array');
|
|
138
|
+
t.feed(value);
|
|
139
|
+
}
|
|
140
|
+
} finally { reader.releaseLock(); }
|
|
141
|
+
t.end();
|
|
142
|
+
return new Uint8Array(w.finalize().buffer);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function _serializeAsyncIterable(iterable, writerOpts, framing) {
|
|
146
|
+
const w = new Writer(writerOpts);
|
|
147
|
+
const t = new Tokenizer(w, { framing });
|
|
148
|
+
for await (const chunk of iterable) {
|
|
149
|
+
if (!(chunk instanceof Uint8Array)) throw new TypeError('serialize: async iterable must yield Uint8Array');
|
|
150
|
+
t.feed(chunk);
|
|
151
|
+
}
|
|
152
|
+
t.end();
|
|
153
|
+
return new Uint8Array(w.finalize().buffer);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function _serializeAsyncIterablePreserve(iterable, opts) {
|
|
157
|
+
const w = new PreserveWriter(opts.writer || {});
|
|
158
|
+
const t = new PreserveTokenizer(w, {
|
|
159
|
+
framing: 'ndjson',
|
|
160
|
+
maxRecordBytes: (opts.writer && opts.writer.maxRecordBytes) || 0,
|
|
161
|
+
});
|
|
162
|
+
for await (const chunk of iterable) {
|
|
163
|
+
if (!(chunk instanceof Uint8Array)) throw new TypeError('serialize: async iterable must yield Uint8Array');
|
|
164
|
+
t.feed(chunk);
|
|
165
|
+
}
|
|
166
|
+
t.end();
|
|
167
|
+
return new Uint8Array(w.finalize().buffer);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Deserialize LBK1 bytes into the right Reader for the container's mode.
|
|
171
|
+
// Auto-detects preserve vs schema via header flag byte at offset 7 bit 0.
|
|
172
|
+
export function deserialize(bytes) {
|
|
173
|
+
let buffer;
|
|
174
|
+
if (bytes instanceof ArrayBuffer) {
|
|
175
|
+
buffer = bytes;
|
|
176
|
+
} else if (bytes instanceof Uint8Array) {
|
|
177
|
+
if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
|
|
178
|
+
buffer = bytes.buffer;
|
|
179
|
+
} else {
|
|
180
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
181
|
+
copy.set(bytes);
|
|
182
|
+
buffer = copy.buffer;
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
throw new TypeError('deserialize: expected Uint8Array or ArrayBuffer, got ' + typeof bytes);
|
|
186
|
+
}
|
|
187
|
+
if (buffer.byteLength < 8) {
|
|
188
|
+
throw new ReaderError('R_TRUNCATED', 'container too small to inspect header flags');
|
|
189
|
+
}
|
|
190
|
+
const flags = new Uint8Array(buffer, 7, 1)[0];
|
|
191
|
+
if (flags & 0x01) return new PreserveReader(buffer);
|
|
192
|
+
return new Reader(buffer);
|
|
193
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/file-ingest
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
import type { Reader } from './Reader.d.ts';
|
|
5
|
+
import type { WriterOptions } from './Writer.d.ts';
|
|
6
|
+
|
|
7
|
+
export const VERSION: string;
|
|
8
|
+
|
|
9
|
+
export interface IngestProgress {
|
|
10
|
+
bytesIngested: number;
|
|
11
|
+
totalBytes: number;
|
|
12
|
+
rowsWritten: number;
|
|
13
|
+
shardsCommitted: number;
|
|
14
|
+
chunkBytes: number;
|
|
15
|
+
chunkCount: number;
|
|
16
|
+
elapsedMs: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IngestStreamOptions {
|
|
20
|
+
/** 'ndjson' | 'array' | 'auto'. Default 'ndjson'. */
|
|
21
|
+
framing?: 'ndjson' | 'array' | 'auto';
|
|
22
|
+
writer?: WriterOptions;
|
|
23
|
+
/** Called after each chunk with progress state. */
|
|
24
|
+
onProgress?: (state: IngestProgress) => void;
|
|
25
|
+
/** Pass file.size for progress percentage. -1 for indeterminate streams. */
|
|
26
|
+
totalBytes?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Ingest a ReadableStream<Uint8Array> into an LBK1 Reader.
|
|
31
|
+
* Works with File.stream(), fetch().body, and any Web Streams source.
|
|
32
|
+
*/
|
|
33
|
+
export function ingestStream(
|
|
34
|
+
readableStream: ReadableStream<Uint8Array>,
|
|
35
|
+
opts?: IngestStreamOptions
|
|
36
|
+
): Promise<Reader>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convenience for Blob/File inputs. Pulls .stream() and .size automatically.
|
|
40
|
+
*/
|
|
41
|
+
export function ingestFile(
|
|
42
|
+
file: Blob | File,
|
|
43
|
+
opts?: IngestStreamOptions
|
|
44
|
+
): Promise<Reader>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/multi-reader
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
import type { Reader, FrozenSchema, Bounds, FindShardsOptions } from './Reader.d.ts';
|
|
5
|
+
|
|
6
|
+
export const VERSION: string;
|
|
7
|
+
|
|
8
|
+
export class MultiReader {
|
|
9
|
+
constructor(readers: Reader[]);
|
|
10
|
+
|
|
11
|
+
readonly schema: FrozenSchema;
|
|
12
|
+
readonly totalRows: number;
|
|
13
|
+
readonly shardCount: number;
|
|
14
|
+
readonly readerCount: number;
|
|
15
|
+
readonly hasZoneMaps: boolean;
|
|
16
|
+
|
|
17
|
+
fieldIndex(name: string): number;
|
|
18
|
+
get(rowIdx: number, fieldName: string): number | string | undefined;
|
|
19
|
+
|
|
20
|
+
/** Access a sub-Reader by index. */
|
|
21
|
+
reader(readerIdx: number): Reader;
|
|
22
|
+
/** Which sub-Reader owns a given global row? */
|
|
23
|
+
readerForRow(rowIdx: number): { readerIdx: number; localRow: number };
|
|
24
|
+
/** Which sub-Reader owns a given global shard? */
|
|
25
|
+
readerForShard(globalShardIdx: number): { readerIdx: number; localShard: number } | null;
|
|
26
|
+
|
|
27
|
+
// Zone maps — global shard indices merged across sub-readers
|
|
28
|
+
shardBounds(globalShardIdx: number, fieldName: string): Bounds | null;
|
|
29
|
+
findShards(fieldName: string, opts?: FindShardsOptions): number[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class MultiReaderError extends Error {
|
|
33
|
+
code: string;
|
|
34
|
+
name: 'MultiReaderError';
|
|
35
|
+
constructor(code: string, message: string);
|
|
36
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/preserve-reader
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
export interface PreserveShardHandle {
|
|
7
|
+
payloadOff: number;
|
|
8
|
+
payloadLen: number;
|
|
9
|
+
rowCount: number;
|
|
10
|
+
blobLen: number;
|
|
11
|
+
offsetTableOff: number;
|
|
12
|
+
firstRow: number;
|
|
13
|
+
endRow: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class PreserveReader {
|
|
17
|
+
static fromBuffer(input: ArrayBuffer | Uint8Array): PreserveReader;
|
|
18
|
+
constructor(buffer: ArrayBuffer);
|
|
19
|
+
|
|
20
|
+
readonly totalRows: number;
|
|
21
|
+
readonly shardCount: number;
|
|
22
|
+
readonly shards: PreserveShardHandle[];
|
|
23
|
+
readonly buffer: ArrayBuffer;
|
|
24
|
+
readonly mode: 'preserve';
|
|
25
|
+
readonly hasZoneMaps: false;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Zero-alloc: returns a Uint8Array subarray view into the container buffer.
|
|
29
|
+
* Do NOT mutate. Do NOT retain past the reader's lifetime.
|
|
30
|
+
*/
|
|
31
|
+
getBytes(rowIdx: number): Uint8Array;
|
|
32
|
+
|
|
33
|
+
/** Allocates one string via TextDecoder. */
|
|
34
|
+
getString(rowIdx: number): string;
|
|
35
|
+
|
|
36
|
+
/** Allocates the parsed value graph via JSON.parse. */
|
|
37
|
+
getJSON(rowIdx: number): unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class PreserveReaderError extends Error {
|
|
41
|
+
code: string;
|
|
42
|
+
name: 'PreserveReaderError';
|
|
43
|
+
constructor(code: string, message: string);
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/preserve-tokenizer
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
export interface PreserveSink {
|
|
7
|
+
onRecord(bytes: Uint8Array, from: number, to: number): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PreserveTokenizerOptions {
|
|
11
|
+
/** Only 'ndjson' is supported in v1. */
|
|
12
|
+
framing?: 'ndjson';
|
|
13
|
+
/** If > 0, throw E_RECORD_TOO_LARGE when a single record exceeds this. */
|
|
14
|
+
maxRecordBytes?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class PreserveTokenizer {
|
|
18
|
+
constructor(sink: PreserveSink, opts?: PreserveTokenizerOptions);
|
|
19
|
+
feed(chunk: Uint8Array): void;
|
|
20
|
+
end(): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class PreserveTokenizerError extends Error {
|
|
24
|
+
code: string;
|
|
25
|
+
name: 'PreserveTokenizerError';
|
|
26
|
+
constructor(code: string, message: string);
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/preserve-writer
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
export interface PreserveWriterOptions {
|
|
7
|
+
/** Target output bytes per shard. Default 8 MiB. */
|
|
8
|
+
targetShardBytes?: number;
|
|
9
|
+
/** Passed to PreserveTokenizer via the top-level API. */
|
|
10
|
+
maxRecordBytes?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PreserveContainer {
|
|
14
|
+
buffer: ArrayBuffer;
|
|
15
|
+
totalRows: number;
|
|
16
|
+
shardCount: number;
|
|
17
|
+
mode: 'preserve';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class PreserveWriter {
|
|
21
|
+
constructor(opts?: PreserveWriterOptions);
|
|
22
|
+
/** Sink protocol. Called by PreserveTokenizer for each detected record. */
|
|
23
|
+
onRecord(bytes: Uint8Array, from: number, to: number): void;
|
|
24
|
+
/** Public API for direct record writes. */
|
|
25
|
+
writeRecord(bytes: Uint8Array): void;
|
|
26
|
+
finalize(): PreserveContainer;
|
|
27
|
+
readonly totalRows: number;
|
|
28
|
+
readonly shardCount: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class PreserveWriterError extends Error {
|
|
32
|
+
code: string;
|
|
33
|
+
name: 'PreserveWriterError';
|
|
34
|
+
constructor(code: string, message: string);
|
|
35
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/range-reader
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
import type { FrozenSchema, Bounds, FindShardsOptions } from './Reader.d.ts';
|
|
5
|
+
import type { StringTableView } from './StringTable.d.ts';
|
|
6
|
+
|
|
7
|
+
export const VERSION: string;
|
|
8
|
+
|
|
9
|
+
export interface IOAdapter {
|
|
10
|
+
size: number;
|
|
11
|
+
fetch(byteOffset: number, byteLength: number): Promise<Uint8Array>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AdapterStats {
|
|
15
|
+
requests: number;
|
|
16
|
+
bytesFetched: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class HTTPRangeAdapter implements IOAdapter {
|
|
20
|
+
static open(url: string, opts?: { fetch?: typeof fetch }): Promise<HTTPRangeAdapter>;
|
|
21
|
+
readonly url: string;
|
|
22
|
+
size: number;
|
|
23
|
+
stats: AdapterStats;
|
|
24
|
+
fetch(byteOffset: number, byteLength: number): Promise<Uint8Array>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class MockRangeAdapter implements IOAdapter {
|
|
28
|
+
constructor(bytes: Uint8Array | ArrayBuffer);
|
|
29
|
+
size: number;
|
|
30
|
+
/** Fetch log: [[offset, length], ...] for test assertions. */
|
|
31
|
+
log: [number, number][];
|
|
32
|
+
stats: AdapterStats;
|
|
33
|
+
fetch(byteOffset: number, byteLength: number): Promise<Uint8Array>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RangeReaderOptions {
|
|
37
|
+
/** Bounded LRU cache size. Default 8. */
|
|
38
|
+
maxCachedShards?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RangeShardHandle {
|
|
42
|
+
payloadOff: number;
|
|
43
|
+
payloadLen: number;
|
|
44
|
+
rowCount: number;
|
|
45
|
+
localStrOff: number;
|
|
46
|
+
localStrLen: number;
|
|
47
|
+
firstRow: number;
|
|
48
|
+
endRow: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class RangeReader {
|
|
52
|
+
static open(adapter: IOAdapter, opts?: RangeReaderOptions): Promise<RangeReader>;
|
|
53
|
+
|
|
54
|
+
readonly adapter: IOAdapter;
|
|
55
|
+
readonly maxCachedShards: number;
|
|
56
|
+
readonly schema: FrozenSchema;
|
|
57
|
+
readonly totalRows: number;
|
|
58
|
+
readonly shardCount: number;
|
|
59
|
+
readonly shards: RangeShardHandle[];
|
|
60
|
+
readonly cachedShardCount: number;
|
|
61
|
+
readonly hasZoneMaps: boolean;
|
|
62
|
+
|
|
63
|
+
fieldIndex(name: string): number;
|
|
64
|
+
|
|
65
|
+
/** Fetch a shard's payload+string-table (one range request), cache, return record. */
|
|
66
|
+
loadShard(shardIdx: number): Promise<{
|
|
67
|
+
payloadBytes: Uint8Array;
|
|
68
|
+
payloadDv: DataView;
|
|
69
|
+
stringTable: StringTableView | null;
|
|
70
|
+
firstRow: number;
|
|
71
|
+
rowCount: number;
|
|
72
|
+
}>;
|
|
73
|
+
|
|
74
|
+
get(rowIdx: number, fieldName: string): Promise<number | string | undefined>;
|
|
75
|
+
|
|
76
|
+
/** Ensure the shards covering [firstRow, lastRow) are cached. */
|
|
77
|
+
prefetchRange(firstRow: number, lastRow: number): Promise<void>;
|
|
78
|
+
|
|
79
|
+
/** Await-free reads after prefetchRange. Throws if any covering shard is uncached. */
|
|
80
|
+
syncRange(firstRow: number, lastRow: number): {
|
|
81
|
+
get(rowIdx: number, fieldName: string): number | string | undefined;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Zone maps (M7) — synchronous, populated at open()
|
|
85
|
+
shardBounds(shardIdx: number, fieldName: string): Bounds | null;
|
|
86
|
+
findShards(fieldName: string, opts?: FindShardsOptions): number[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class RangeReaderError extends Error {
|
|
90
|
+
code: string;
|
|
91
|
+
name: 'RangeReaderError';
|
|
92
|
+
constructor(code: string, message: string);
|
|
93
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/reader
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
export type LaneKindNumeric = 1 | 3;
|
|
7
|
+
|
|
8
|
+
export interface FrozenField {
|
|
9
|
+
name: string;
|
|
10
|
+
/** 1 = F64, 3 = U32. */
|
|
11
|
+
laneKind: LaneKindNumeric;
|
|
12
|
+
offsetInRow: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface FrozenSchema {
|
|
16
|
+
fields: FrozenField[];
|
|
17
|
+
rowStride: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ShardHandle {
|
|
21
|
+
payloadOff: number;
|
|
22
|
+
payloadLen: number;
|
|
23
|
+
rowCount: number;
|
|
24
|
+
payloadBytes: Uint8Array;
|
|
25
|
+
payloadDv: DataView;
|
|
26
|
+
stringTable: StringTableView | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StringTableView {
|
|
30
|
+
readonly count: number;
|
|
31
|
+
get(idx: number): string | undefined;
|
|
32
|
+
bytesAt(idx: number): Uint8Array | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Bounds {
|
|
36
|
+
min: number;
|
|
37
|
+
max: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface FindShardsOptions {
|
|
41
|
+
min?: number;
|
|
42
|
+
max?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class Reader {
|
|
46
|
+
static fromBuffer(bufferOrArrayBuffer: ArrayBuffer | Uint8Array): Reader;
|
|
47
|
+
constructor(buffer: ArrayBuffer);
|
|
48
|
+
|
|
49
|
+
readonly schema: FrozenSchema;
|
|
50
|
+
readonly totalRows: number;
|
|
51
|
+
readonly shardCount: number;
|
|
52
|
+
readonly shards: ShardHandle[];
|
|
53
|
+
readonly buffer: ArrayBuffer;
|
|
54
|
+
|
|
55
|
+
fieldIndex(name: string): number;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get value at (rowIdx, fieldName). F64 lane → number; U32 lane → string
|
|
59
|
+
* (resolved via the shard's local string table).
|
|
60
|
+
*/
|
|
61
|
+
get(rowIdx: number, fieldName: string): number | string | undefined;
|
|
62
|
+
|
|
63
|
+
// Hot-loop friendly accessors
|
|
64
|
+
shardPayload(shardIdx: number): DataView;
|
|
65
|
+
strideBytes(): number;
|
|
66
|
+
offsetBytes(fieldName: string): number;
|
|
67
|
+
laneKind(fieldName: string): LaneKindNumeric;
|
|
68
|
+
shardStringTable(shardIdx: number): StringTableView | null;
|
|
69
|
+
|
|
70
|
+
// Back-compat with all-F64 schemas (M2 API surface)
|
|
71
|
+
shardF64(shardIdx: number): Float64Array;
|
|
72
|
+
strideF64(): number;
|
|
73
|
+
offsetF64(fieldName: string): number;
|
|
74
|
+
|
|
75
|
+
// Zone maps (M7)
|
|
76
|
+
readonly hasZoneMaps: boolean;
|
|
77
|
+
shardBounds(shardIdx: number, fieldName: string): Bounds | null;
|
|
78
|
+
findShards(fieldName: string, opts?: FindShardsOptions): number[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class ReaderError extends Error {
|
|
82
|
+
code: string;
|
|
83
|
+
name: 'ReaderError';
|
|
84
|
+
constructor(code: string, message: string);
|
|
85
|
+
}
|
package/types/Split.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/split
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
import type { WriterOptions } from './Writer.d.ts';
|
|
5
|
+
|
|
6
|
+
export const VERSION: string;
|
|
7
|
+
|
|
8
|
+
export interface SplitRange {
|
|
9
|
+
/** Byte offset (inclusive). */
|
|
10
|
+
start: number;
|
|
11
|
+
/** Byte offset (exclusive). */
|
|
12
|
+
end: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SplitOptions {
|
|
16
|
+
/** Desired part count. Actual count may be less on tiny inputs. Default 4. */
|
|
17
|
+
targetParts?: number;
|
|
18
|
+
/** Optional ceiling on part size. Splitter emits more parts to honor it. */
|
|
19
|
+
maxPartBytes?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CompilePartOptions {
|
|
23
|
+
framing?: 'ndjson' | 'array' | 'auto';
|
|
24
|
+
writer?: WriterOptions;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CompileInPartsOptions extends SplitOptions, CompilePartOptions {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Divide NDJSON bytes into N ranges at safe line boundaries. Every returned
|
|
31
|
+
* range contains complete records — no split mid-line. The union of ranges
|
|
32
|
+
* equals the original bytes (no gaps, no overlaps).
|
|
33
|
+
*/
|
|
34
|
+
export function splitNDJSON(bytes: Uint8Array, opts?: SplitOptions): SplitRange[];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Compile one byte range into a standalone LBK1 container. Trivially
|
|
38
|
+
* Transferable across worker boundaries via `postMessage(buf, [buf])`.
|
|
39
|
+
* For multi-part compilation, ALL parts MUST share the same explicit
|
|
40
|
+
* schema (see module docs); otherwise merge will fail with S_SCHEMA_MISMATCH.
|
|
41
|
+
*/
|
|
42
|
+
export function compilePart(bytes: Uint8Array, opts?: CompilePartOptions): Uint8Array;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sequential convenience: split + compile each part serially.
|
|
46
|
+
* Returns one Uint8Array per part. Use this when you don't have workers,
|
|
47
|
+
* or as a deterministic baseline against a worker-parallelized run.
|
|
48
|
+
*/
|
|
49
|
+
export function compileInParts(bytes: Uint8Array, opts?: CompileInPartsOptions): Uint8Array[];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Concatenate N LBK1 containers into ONE. All containers MUST share an
|
|
53
|
+
* identical schema. Preserves per-shard string tables and zone maps
|
|
54
|
+
* verbatim; rewrites the shard directory and header offsets.
|
|
55
|
+
*
|
|
56
|
+
* For query workloads over multiple parts, prefer MultiReader (M4) which
|
|
57
|
+
* avoids the copy. Use mergeContainers when you need a single-file
|
|
58
|
+
* distributable.
|
|
59
|
+
*/
|
|
60
|
+
export function mergeContainers(containers: (Uint8Array | ArrayBuffer)[]): Uint8Array;
|
|
61
|
+
|
|
62
|
+
export class SplitError extends Error {
|
|
63
|
+
code: string;
|
|
64
|
+
name: 'SplitError';
|
|
65
|
+
constructor(code: string, message: string);
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/string-table
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
export class StringTable {
|
|
7
|
+
constructor();
|
|
8
|
+
/** Intern a byte range. Zero-alloc on the hit path. Returns u32 index. */
|
|
9
|
+
intern(bytes: Uint8Array, from: number, to: number): number;
|
|
10
|
+
/** Zero-copy byte range for entry idx. */
|
|
11
|
+
bytesAt(idx: number): Uint8Array | undefined;
|
|
12
|
+
reset(): void;
|
|
13
|
+
serialize(): { bytes: Uint8Array; byteLength: number };
|
|
14
|
+
static parse(bytes: Uint8Array, byteOffset: number): StringTableView;
|
|
15
|
+
readonly count: number;
|
|
16
|
+
readonly blobLen: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class StringTableView {
|
|
20
|
+
readonly count: number;
|
|
21
|
+
get(idx: number): string | undefined;
|
|
22
|
+
bytesAt(idx: number): Uint8Array | undefined;
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Type declarations for @zakkster/lite-bake-stream/tokenizer
|
|
2
|
+
// Copyright (c) 2026 Zahary Shinikchiev. MIT.
|
|
3
|
+
|
|
4
|
+
export const VERSION: string;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sink protocol the Tokenizer emits into. Byte ranges (bytes[from, to)) are
|
|
8
|
+
* ephemeral — valid only for the duration of the call. Consumers that need
|
|
9
|
+
* to retain content must copy or decode inside the sink method.
|
|
10
|
+
*/
|
|
11
|
+
export interface TokenizerSink {
|
|
12
|
+
onStartObject?(): void;
|
|
13
|
+
onEndObject?(): void;
|
|
14
|
+
onStartArray?(): void;
|
|
15
|
+
onEndArray?(): void;
|
|
16
|
+
onKey?(bytes: Uint8Array, from: number, to: number): void;
|
|
17
|
+
onNumber?(value: number): void;
|
|
18
|
+
onString?(bytes: Uint8Array, from: number, to: number): void;
|
|
19
|
+
onTrue?(): void;
|
|
20
|
+
onFalse?(): void;
|
|
21
|
+
onNull?(): void;
|
|
22
|
+
onEnd?(): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TokenizerOptions {
|
|
26
|
+
/** 'ndjson' | 'array' | 'auto'. Default 'ndjson'. */
|
|
27
|
+
framing?: 'ndjson' | 'array' | 'auto';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class Tokenizer {
|
|
31
|
+
constructor(sink: TokenizerSink, opts?: TokenizerOptions);
|
|
32
|
+
/** Feed a chunk of UTF-8 bytes. Safe to split at any byte boundary. */
|
|
33
|
+
feed(chunk: Uint8Array): void;
|
|
34
|
+
/** Signal end of input. Any pending record is flushed to the sink. */
|
|
35
|
+
end(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class TokenizerError extends Error {
|
|
39
|
+
code: string;
|
|
40
|
+
name: 'TokenizerError';
|
|
41
|
+
constructor(code: string, message: string);
|
|
42
|
+
}
|