@zakkster/lite-bake-stream 1.5.0 → 1.6.1
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 +156 -128
- package/README.md +294 -95
- package/SPEC.md +32 -30
- package/llms.txt +18 -13
- package/package.json +2 -2
- package/src/Crc32c.js +96 -0
- package/src/FileIngest.js +6 -2
- package/src/MultiReader.js +1 -1
- package/src/Opts.js +4 -0
- package/src/PreserveReader.js +29 -4
- package/src/PreserveTokenizer.js +12 -3
- package/src/PreserveWriter.js +218 -61
- package/src/RangeReader.js +29 -2
- package/src/Reader.js +34 -4
- package/src/Split.js +22 -6
- package/src/StringTable.js +7 -4
- package/src/Tokenizer.js +3 -2
- package/src/Views.js +65 -0
- package/src/Writer.js +387 -146
- package/src/index.js +8 -4
- package/types/MultiReader.d.ts +1 -1
- package/types/PreserveReader.d.ts +10 -2
- package/types/PreserveWriter.d.ts +30 -0
- package/types/RangeReader.d.ts +6 -1
- package/types/Reader.d.ts +11 -3
- package/types/Split.d.ts +1 -1
- package/types/Tokenizer.d.ts +1 -1
- package/types/Writer.d.ts +39 -2
- package/types/index.d.ts +2 -1
package/src/index.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
// Set opts.preserve = true. Bytes in, same bytes out.
|
|
9
9
|
// deserialize() auto-detects the mode via the container's flag bit and
|
|
10
10
|
// returns the appropriate Reader.
|
|
11
|
+
//
|
|
12
|
+
// Error codes:
|
|
13
|
+
// E_OPTION_CONFLICT - serialize() opts combine preserve with an incompatible framing
|
|
14
|
+
// R_TRUNCATED - deserialize() container too small to inspect header flags
|
|
11
15
|
|
|
12
16
|
import { Tokenizer, TokenizerError } from './Tokenizer.js';
|
|
13
17
|
import { Writer, WriterError } from './Writer.js';
|
|
@@ -28,7 +32,7 @@ export {
|
|
|
28
32
|
PreserveWriter, PreserveWriterError,
|
|
29
33
|
PreserveReader, PreserveReaderError,
|
|
30
34
|
};
|
|
31
|
-
export const VERSION = '1.
|
|
35
|
+
export const VERSION = '1.6.1';
|
|
32
36
|
|
|
33
37
|
const encoder = new TextEncoder();
|
|
34
38
|
|
|
@@ -188,12 +192,12 @@ async function _serializeAsyncIterablePreserve(iterable, opts) {
|
|
|
188
192
|
|
|
189
193
|
// Deserialize LBK1 bytes into the right Reader for the container's mode.
|
|
190
194
|
// Auto-detects preserve vs schema via header flag byte at offset 7 bit 0.
|
|
191
|
-
export function deserialize(bytes) {
|
|
195
|
+
export function deserialize(bytes, opts) {
|
|
192
196
|
const buffer = toContainerBuffer(bytes, 'deserialize');
|
|
193
197
|
if (buffer.byteLength < 8) {
|
|
194
198
|
throw new ReaderError('R_TRUNCATED', 'container too small to inspect header flags');
|
|
195
199
|
}
|
|
196
200
|
const flags = new Uint8Array(buffer, 7, 1)[0];
|
|
197
|
-
if (flags & 0x01) return new PreserveReader(buffer);
|
|
198
|
-
return new Reader(buffer);
|
|
201
|
+
if (flags & 0x01) return new PreserveReader(buffer, opts);
|
|
202
|
+
return new Reader(buffer, opts);
|
|
199
203
|
}
|
package/types/MultiReader.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export class MultiReader {
|
|
|
24
24
|
/** Which sub-Reader owns a given global shard? */
|
|
25
25
|
readerForShard(globalShardIdx: number): { readerIdx: number; localShard: number } | null;
|
|
26
26
|
|
|
27
|
-
// Zone maps
|
|
27
|
+
// Zone maps -- global shard indices merged across sub-readers
|
|
28
28
|
shardBounds(globalShardIdx: number, fieldName: string): Bounds | null;
|
|
29
29
|
findShards(fieldName: string, opts?: FindShardsOptions): number[];
|
|
30
30
|
}
|
|
@@ -13,9 +13,17 @@ export interface PreserveShardHandle {
|
|
|
13
13
|
endRow: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export interface PreserveReaderOptions {
|
|
17
|
+
/** Verify the footer CRC-32C at open; throws R_BAD_CRC on mismatch, R_CRC_ABSENT if absent. */
|
|
18
|
+
verifyCrc?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
export class PreserveReader {
|
|
17
|
-
static fromBuffer(input: ArrayBuffer | Uint8Array): PreserveReader;
|
|
18
|
-
constructor(buffer: ArrayBuffer);
|
|
22
|
+
static fromBuffer(input: ArrayBuffer | Uint8Array, opts?: PreserveReaderOptions): PreserveReader;
|
|
23
|
+
constructor(buffer: ArrayBuffer, opts?: PreserveReaderOptions);
|
|
24
|
+
|
|
25
|
+
/** Recompute the footer CRC-32C over [0, footer_off): 'ok' | 'absent'; throws R_BAD_CRC on mismatch. */
|
|
26
|
+
verifyCrc(): 'ok' | 'absent';
|
|
19
27
|
|
|
20
28
|
readonly totalRows: number;
|
|
21
29
|
readonly shardCount: number;
|
|
@@ -8,6 +8,8 @@ export interface PreserveWriterOptions {
|
|
|
8
8
|
targetShardBytes?: number;
|
|
9
9
|
/** Passed to PreserveTokenizer via the top-level API. */
|
|
10
10
|
maxRecordBytes?: number;
|
|
11
|
+
/** Emit a CRC-32C over [0, footer_off) into the footer. Default false. */
|
|
12
|
+
crc?: boolean;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export interface PreserveContainer {
|
|
@@ -17,6 +19,24 @@ export interface PreserveContainer {
|
|
|
17
19
|
mode: 'preserve';
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
export interface PreserveContainerSink {
|
|
23
|
+
write(bytes: Uint8Array): void;
|
|
24
|
+
writeAt?(bytes: Uint8Array, position: number): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PreserveFinalizeToSinkOptions {
|
|
28
|
+
layout?: 'prefix' | 'stream';
|
|
29
|
+
crc?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PreserveSinkResult {
|
|
33
|
+
totalRows: number;
|
|
34
|
+
shardCount: number;
|
|
35
|
+
mode: 'preserve';
|
|
36
|
+
bytesWritten: number;
|
|
37
|
+
layout: 'prefix' | 'stream';
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
export class PreserveWriter {
|
|
21
41
|
constructor(opts?: PreserveWriterOptions);
|
|
22
42
|
/** Sink protocol. Called by PreserveTokenizer for each detected record. */
|
|
@@ -24,6 +44,16 @@ export class PreserveWriter {
|
|
|
24
44
|
/** Public API for direct record writes. */
|
|
25
45
|
writeRecord(bytes: Uint8Array): void;
|
|
26
46
|
finalize(): PreserveContainer;
|
|
47
|
+
/**
|
|
48
|
+
* Bind a sink and stream shards as they finalize (bounded RAM). Call BEFORE
|
|
49
|
+
* feeding, then finish with finalizeToSink. opts.layout must be 'stream'.
|
|
50
|
+
*/
|
|
51
|
+
beginStream(sink: PreserveContainerSink, opts?: PreserveFinalizeToSinkOptions): void;
|
|
52
|
+
/**
|
|
53
|
+
* Emit to a caller sink. Preceded by beginStream it writes only the trailer
|
|
54
|
+
* (O(shard)); called alone it buffers first (O(container)).
|
|
55
|
+
*/
|
|
56
|
+
finalizeToSink(sink: PreserveContainerSink, opts?: PreserveFinalizeToSinkOptions): PreserveSinkResult;
|
|
27
57
|
readonly totalRows: number;
|
|
28
58
|
readonly shardCount: number;
|
|
29
59
|
}
|
package/types/RangeReader.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export class MockRangeAdapter implements IOAdapter {
|
|
|
36
36
|
export interface RangeReaderOptions {
|
|
37
37
|
/** Bounded LRU cache size. Default 8. */
|
|
38
38
|
maxCachedShards?: number;
|
|
39
|
+
/** Verify the footer CRC-32C at open; rejects R_BAD_CRC on mismatch, R_CRC_ABSENT if absent. */
|
|
40
|
+
verifyCrc?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export interface RangeShardHandle {
|
|
@@ -60,6 +62,9 @@ export class RangeReader {
|
|
|
60
62
|
readonly cachedShardCount: number;
|
|
61
63
|
readonly hasZoneMaps: boolean;
|
|
62
64
|
|
|
65
|
+
/** Ranged-fetch the body [0, footer_off) and verify its CRC-32C: 'ok' | 'absent'; rejects R_BAD_CRC on mismatch. */
|
|
66
|
+
verifyCrc(): Promise<'ok' | 'absent'>;
|
|
67
|
+
|
|
63
68
|
fieldIndex(name: string): number;
|
|
64
69
|
|
|
65
70
|
/** Fetch a shard's payload+string-table (one range request), cache, return record. */
|
|
@@ -81,7 +86,7 @@ export class RangeReader {
|
|
|
81
86
|
get(rowIdx: number, fieldName: string): number | string | undefined;
|
|
82
87
|
};
|
|
83
88
|
|
|
84
|
-
// Zone maps (M7)
|
|
89
|
+
// Zone maps (M7) -- synchronous, populated at open()
|
|
85
90
|
shardBounds(shardIdx: number, fieldName: string): Bounds | null;
|
|
86
91
|
findShards(fieldName: string, opts?: FindShardsOptions): number[];
|
|
87
92
|
}
|
package/types/Reader.d.ts
CHANGED
|
@@ -42,9 +42,17 @@ export interface FindShardsOptions {
|
|
|
42
42
|
max?: number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export interface ReaderOptions {
|
|
46
|
+
/** Verify the footer CRC-32C at open; throws R_BAD_CRC on mismatch, R_CRC_ABSENT if absent. */
|
|
47
|
+
verifyCrc?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
export class Reader {
|
|
46
|
-
static fromBuffer(bufferOrArrayBuffer: ArrayBuffer | Uint8Array): Reader;
|
|
47
|
-
constructor(buffer: ArrayBuffer);
|
|
51
|
+
static fromBuffer(bufferOrArrayBuffer: ArrayBuffer | Uint8Array, opts?: ReaderOptions): Reader;
|
|
52
|
+
constructor(buffer: ArrayBuffer, opts?: ReaderOptions);
|
|
53
|
+
|
|
54
|
+
/** Recompute the footer CRC-32C over [0, footer_off): 'ok' | 'absent'; throws R_BAD_CRC on mismatch. */
|
|
55
|
+
verifyCrc(): 'ok' | 'absent';
|
|
48
56
|
|
|
49
57
|
readonly schema: FrozenSchema;
|
|
50
58
|
readonly totalRows: number;
|
|
@@ -55,7 +63,7 @@ export class Reader {
|
|
|
55
63
|
fieldIndex(name: string): number;
|
|
56
64
|
|
|
57
65
|
/**
|
|
58
|
-
* Get value at (rowIdx, fieldName). F64 lane
|
|
66
|
+
* Get value at (rowIdx, fieldName). F64 lane -> number; U32 lane -> string
|
|
59
67
|
* (resolved via the shard's local string table).
|
|
60
68
|
*/
|
|
61
69
|
get(rowIdx: number, fieldName: string): number | string | undefined;
|
package/types/Split.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface CompileInPartsOptions extends SplitOptions, CompilePartOptions
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Divide NDJSON bytes into N ranges at safe line boundaries. Every returned
|
|
31
|
-
* range contains complete records
|
|
31
|
+
* range contains complete records -- no split mid-line. The union of ranges
|
|
32
32
|
* equals the original bytes (no gaps, no overlaps).
|
|
33
33
|
*/
|
|
34
34
|
export function splitNDJSON(bytes: Uint8Array, opts?: SplitOptions): SplitRange[];
|
package/types/Tokenizer.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export const VERSION: string;
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Sink protocol the Tokenizer emits into. Byte ranges (bytes[from, to)) are
|
|
8
|
-
* ephemeral
|
|
8
|
+
* ephemeral -- valid only for the duration of the call. Consumers that need
|
|
9
9
|
* to retain content must copy or decode inside the sink method.
|
|
10
10
|
*/
|
|
11
11
|
export interface TokenizerSink {
|
package/types/Writer.d.ts
CHANGED
|
@@ -30,6 +30,30 @@ export interface WriterOptions {
|
|
|
30
30
|
* Default = targetShardBytes.
|
|
31
31
|
*/
|
|
32
32
|
sampleBytes?: number;
|
|
33
|
+
/** Emit a CRC-32C over [0, footer_off) into the footer. Default false. */
|
|
34
|
+
crc?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A synchronous byte sink for `finalizeToSink`. */
|
|
38
|
+
export interface ContainerSink {
|
|
39
|
+
write(bytes: Uint8Array): void;
|
|
40
|
+
/** Required for layout:'stream' (one header backpatch at position 0). */
|
|
41
|
+
writeAt?(bytes: Uint8Array, position: number): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FinalizeToSinkOptions {
|
|
45
|
+
/** 'prefix' (classic, byte-identical to finalize()) or 'stream'. Default 'stream'. */
|
|
46
|
+
layout?: 'prefix' | 'stream';
|
|
47
|
+
/** Override the constructor `crc`. */
|
|
48
|
+
crc?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SinkResult {
|
|
52
|
+
totalRows: number;
|
|
53
|
+
shardCount: number;
|
|
54
|
+
schema: FrozenSchema;
|
|
55
|
+
bytesWritten: number;
|
|
56
|
+
layout: 'prefix' | 'stream';
|
|
33
57
|
}
|
|
34
58
|
|
|
35
59
|
export interface FrozenField {
|
|
@@ -54,7 +78,7 @@ export interface Container {
|
|
|
54
78
|
export class Writer implements TokenizerSinkForWriter {
|
|
55
79
|
constructor(opts?: WriterOptions);
|
|
56
80
|
/**
|
|
57
|
-
* Sink protocol
|
|
81
|
+
* Sink protocol -- the Tokenizer calls these. Byte ranges are ephemeral.
|
|
58
82
|
*/
|
|
59
83
|
onStartObject(): void;
|
|
60
84
|
onEndObject(): void;
|
|
@@ -85,12 +109,25 @@ export class Writer implements TokenizerSinkForWriter {
|
|
|
85
109
|
* Tokenizer has been given `end()`.
|
|
86
110
|
*/
|
|
87
111
|
finalize(): Container;
|
|
112
|
+
/**
|
|
113
|
+
* Bind a sink and stream shards as they finalize, for bounded-RAM output.
|
|
114
|
+
* Call BEFORE feeding the tokenizer, then finish with finalizeToSink. Peak
|
|
115
|
+
* memory is O(targetShardBytes + directory). opts.layout must be 'stream'.
|
|
116
|
+
*/
|
|
117
|
+
beginStream(sink: ContainerSink, opts?: FinalizeToSinkOptions): void;
|
|
118
|
+
/**
|
|
119
|
+
* Emit the container to a caller sink instead of returning a buffer. Preceded
|
|
120
|
+
* by beginStream it writes only the trailer (bounded RAM, O(shard)); called
|
|
121
|
+
* alone it buffers the shards first (O(container)). With layout:'prefix' the
|
|
122
|
+
* emitted bytes are identical to finalize().
|
|
123
|
+
*/
|
|
124
|
+
finalizeToSink(sink: ContainerSink, opts?: FinalizeToSinkOptions): SinkResult;
|
|
88
125
|
readonly schema: FrozenSchema | null;
|
|
89
126
|
readonly totalRows: number;
|
|
90
127
|
readonly shardCount: number;
|
|
91
128
|
}
|
|
92
129
|
|
|
93
|
-
// Structural type
|
|
130
|
+
// Structural type -- the Writer conforms to the Tokenizer's sink protocol.
|
|
94
131
|
// (Redeclared here so the /writer subpath is self-contained.)
|
|
95
132
|
interface TokenizerSinkForWriter {
|
|
96
133
|
onStartObject(): void;
|
package/types/index.d.ts
CHANGED
|
@@ -54,5 +54,6 @@ export function serialize(
|
|
|
54
54
|
* Auto-detects preserve vs schema via the header flag byte.
|
|
55
55
|
*/
|
|
56
56
|
export function deserialize(
|
|
57
|
-
bytes: Uint8Array | ArrayBuffer
|
|
57
|
+
bytes: Uint8Array | ArrayBuffer,
|
|
58
|
+
opts?: { verifyCrc?: boolean }
|
|
58
59
|
): import('./Reader.d.ts').Reader | import('./PreserveReader.d.ts').PreserveReader;
|