@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/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.5.0';
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
  }
@@ -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 global shard indices merged across sub-readers
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
  }
@@ -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) synchronous, populated at open()
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 number; U32 lane string
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 no split mid-line. The union of ranges
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[];
@@ -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 valid only for the duration of the call. Consumers that need
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 the Tokenizer calls these. Byte ranges are ephemeral.
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 the Writer conforms to the Tokenizer's sink protocol.
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;