@cloudbitmaps/core 0.1.2 → 0.1.3

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.
Files changed (47) hide show
  1. package/dist/azure/index.d.cts +1 -1
  2. package/dist/azure/index.d.ts +1 -1
  3. package/dist/cassandra/index.cjs.map +1 -1
  4. package/dist/cassandra/index.d.cts +1 -1
  5. package/dist/cassandra/index.d.ts +1 -1
  6. package/dist/cassandra/index.js +1 -1
  7. package/dist/{chunk-2YDULGXS.js → chunk-L54FL5DW.js} +2 -2
  8. package/dist/{chunk-2YDULGXS.js.map → chunk-L54FL5DW.js.map} +1 -1
  9. package/dist/{chunk-AS6ODRLT.js → chunk-YEHMG6W2.js} +2 -2
  10. package/dist/{chunk-AS6ODRLT.js.map → chunk-YEHMG6W2.js.map} +1 -1
  11. package/dist/dynamodb/index.cjs.map +1 -1
  12. package/dist/dynamodb/index.d.cts +6 -6
  13. package/dist/dynamodb/index.d.ts +6 -6
  14. package/dist/dynamodb/index.js +2 -2
  15. package/dist/dynamodb/index.js.map +1 -1
  16. package/dist/gcs/index.d.cts +1 -1
  17. package/dist/gcs/index.d.ts +1 -1
  18. package/dist/index.cjs +34 -0
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +36 -15
  21. package/dist/index.d.ts +36 -15
  22. package/dist/index.js +37 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/mongodb/index.cjs.map +1 -1
  25. package/dist/mongodb/index.d.cts +1 -1
  26. package/dist/mongodb/index.d.ts +1 -1
  27. package/dist/mongodb/index.js +1 -1
  28. package/dist/mysql/index.cjs.map +1 -1
  29. package/dist/mysql/index.d.cts +1 -1
  30. package/dist/mysql/index.d.ts +1 -1
  31. package/dist/mysql/index.js +1 -1
  32. package/dist/mysql/index.js.map +1 -1
  33. package/dist/{ports-D3BrJ6ax.d.cts → ports-5BQTGKze.d.cts} +2 -3
  34. package/dist/{ports-D3BrJ6ax.d.ts → ports-5BQTGKze.d.ts} +2 -3
  35. package/dist/postgres/index.cjs.map +1 -1
  36. package/dist/postgres/index.d.cts +1 -1
  37. package/dist/postgres/index.d.ts +1 -1
  38. package/dist/postgres/index.js +1 -1
  39. package/dist/redis/index.cjs.map +1 -1
  40. package/dist/redis/index.d.cts +1 -1
  41. package/dist/redis/index.d.ts +1 -1
  42. package/dist/redis/index.js +1 -1
  43. package/dist/s3/index.cjs.map +1 -1
  44. package/dist/s3/index.d.cts +1 -1
  45. package/dist/s3/index.d.ts +1 -1
  46. package/dist/s3/index.js +1 -1
  47. package/package.json +1 -1
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as IWarmDriver, h as ColdChunkSource, S as SegmentRef, W as WarmReadOptions, i as SegmentSize, I as IColdDriver, d as IRegistryDriver, j as IKeystore, b as ChunkRef, C as ColdCaps, G as GenKey, B as BlobSink, R as RegCaps, e as RegistryRecord, f as NewRegistryRecord, T as Token, g as RegistryPatch, c as WarmRow, N as NoRow, k as CrbmCrypto, l as BlobReader, m as WrappedDek, A as Aead, n as AeadSealed } from './ports-D3BrJ6ax.cjs';
2
- export { o as BufferReader, p as BufferSink, q as GovernanceMeta, r as NO_ROW, s as RegistryStatus, t as aadFor } from './ports-D3BrJ6ax.cjs';
1
+ import { a as IWarmDriver, h as ColdChunkSource, S as SegmentRef, W as WarmReadOptions, i as SegmentSize, I as IColdDriver, d as IRegistryDriver, j as IKeystore, b as ChunkRef, C as ColdCaps, G as GenKey, B as BlobSink, R as RegCaps, e as RegistryRecord, f as NewRegistryRecord, T as Token, g as RegistryPatch, c as WarmRow, N as NoRow, k as CrbmCrypto, l as BlobReader, m as WrappedDek, A as Aead, n as AeadSealed } from './ports-5BQTGKze.cjs';
2
+ export { o as BufferReader, p as BufferSink, q as GovernanceMeta, r as NO_ROW, s as RegistryStatus, t as aadFor } from './ports-5BQTGKze.cjs';
3
3
 
4
4
  /**
5
5
  * The **bitmap-codec seam**.
@@ -50,6 +50,17 @@ interface CodecBitmap {
50
50
  /** Ascending iterator over the set values. */
51
51
  [Symbol.iterator](): IterableIterator<number>;
52
52
  toArray(): number[];
53
+ /**
54
+ * The largest value in the set, or `undefined` when empty.
55
+ *
56
+ * **Optional, and deliberately so.** The engine uses it for one thing: asserting that a payload it is about
57
+ * to interpret **as a chunk** holds only 16-bit remainders (see {@link assertChunkPayload}). A codec that
58
+ * cannot answer this in better than O(n) should simply omit it — the engine then skips the check rather
59
+ * than walking every value on the read path, which is the one thing this must never cost.
60
+ *
61
+ * Roaring answers it in O(1) from its container index, so the flagship codec implements it.
62
+ */
63
+ maximum?(): number | undefined;
53
64
  }
54
65
  /**
55
66
  * A pluggable bitmap codec — the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.
@@ -426,7 +437,7 @@ interface EngineDeps {
426
437
  readonly cache?: BoundedLru<string, CodecBitmap>;
427
438
  readonly maxBitmapBytes?: number;
428
439
  /**
429
- * The bitmap codec (DECISIONS #58) — **required**. `core/` is
440
+ * The bitmap codec — **required**. `core/` is
430
441
  * codec-agnostic: it can have no default, because the concrete codec lives in a *flavor* package that depends
431
442
  * on core (a default here would invert that arrow). A flavor's facade injects it — `@cloudbitmaps/roaring`
432
443
  * passes `roaringCodec` — so applications never see this.
@@ -545,6 +556,21 @@ declare class SegmentEngine {
545
556
  * letting it flow into `joinId` and produce a bogus id.
546
557
  */
547
558
  private assertChunkKeyInRange;
559
+ /**
560
+ * The other half of invariant 5, and the one that was missing: a chunk payload holds **remainders** — 16-bit
561
+ * offsets within one chunk — so every value must be `<= 0xffff`.
562
+ *
563
+ * Nothing upstream established that. The byte cap bounds *size*, and CRC/AEAD prove the bytes are the bytes
564
+ * that were written — which anyone able to write the tier satisfies trivially. A value `>= 65536` then
565
+ * reaches `joinId`, which masks it (`remainder & 0xffff`) and emits a **fabricated id belonging to a
566
+ * different chunk's id space**: indistinguishable from real data, inflating `count()` and creating spurious
567
+ * `intersect` matches. Compaction is the only path that already failed loud (`CrbmWriter` rejects
568
+ * `cardinality > 65536`), which merely turns the same row into a permanently poison segment.
569
+ *
570
+ * Costs one `maximum()` per chunk, not per id — `maximum` is optional on the codec seam precisely so a codec
571
+ * that cannot answer in O(1) opts out instead of making the read path walk every value.
572
+ */
573
+ private assertChunkPayloadInRange;
548
574
  private effectiveChunk;
549
575
  /** The segment's current generation, resolved once per op (`undefined` ⇒ source can't report it). */
550
576
  private currentGen;
@@ -824,7 +850,7 @@ declare class RecordingAuditSink implements IAuditSink {
824
850
  * **Correctness without a lock:** the commit is the conditional `currentGen` swap; write-once generations +
825
851
  * version-fenced purge make even two concurrent compactions safe (the loser's staged generation is an orphan
826
852
  * GC'd later; no torn read — readers are pinned to a committed generation, **I5**). The lease only avoids that
827
- * wasted duplicate work (see DECISIONS #18).
853
+ * wasted duplicate work.
828
854
  *
829
855
  * Pure orchestration over the injected driver interfaces + an injected {@link Clock} (timer-free; the daemon
830
856
  * layer owns scheduling/heartbeat). A crash at *any* step is recoverable: nothing is destructive until after
@@ -839,7 +865,7 @@ interface CompactionDeps {
839
865
  readonly clock: Pick<Clock, 'now'>;
840
866
  readonly maxBitmapBytes?: number;
841
867
  /**
842
- * Bitmap codec ([DECISIONS #58]) — compaction only merges bitmaps through it. Optional in the type so this
868
+ * Bitmap codec — compaction only merges bitmaps through it. Optional in the type so this
843
869
  * stays call-compatible public API; a **flavor** package binds it (see {@link requireCodec}).
844
870
  */
845
871
  readonly codec?: CodecInterface;
@@ -1040,7 +1066,7 @@ interface ExportOptions {
1040
1066
  /** Output format; defaults to `'roaring'`. */
1041
1067
  readonly format?: ExportFormat;
1042
1068
  /**
1043
- * Bitmap codec used to build the exported bitmap for the `'roaring'` format ([DECISIONS #58]). Optional in the
1069
+ * Bitmap codec used to build the exported bitmap for the `'roaring'` format. Optional in the
1044
1070
  * type so this stays call-compatible public API; a **flavor** package binds it (see `requireCodec`). Not needed
1045
1071
  * for `'ndjson'`, which streams plain ids.
1046
1072
  */
@@ -1123,11 +1149,6 @@ interface BitRoute {
1123
1149
  }
1124
1150
  /** Split a u32 id into `{ chunkKey, remainder }`. Throws `ValidationError` for non-u32 input. */
1125
1151
  declare function splitId(id: number): BitRoute;
1126
- /**
1127
- * Recombine a chunk key + remainder back into the u32 id. Precondition: `chunkKey` is a validated
1128
- * 16-bit value (tier-derived keys are validated upstream in the engine before reaching here, per
1129
- * invariant 5); `remainder` is masked to 16 bits.
1130
- */
1131
1152
  declare function joinId(chunkKey: number, remainder: number): number;
1132
1153
 
1133
1154
  /**
@@ -1516,14 +1537,14 @@ interface BulkLoadResult {
1516
1537
  readonly cardinality: number;
1517
1538
  }
1518
1539
  /**
1519
- * **Bulk-load** a whole immutable generation from a flat id source — the batch "seed/sweep" entry point.
1540
+ * **Bulk-load** a whole immutable generation from a flat id source — the batch "seed/rebuild" entry point.
1520
1541
  * Unlike {@link writeCrbmGeneration} (which takes pre-grouped bitmaps), this
1521
1542
  * consumes an arbitrary, **unsorted** stream of u32 ids (sync or async) and routes each into its chunk's
1522
1543
  * Roaring bitmap as it goes. The input is consumed lazily and ids dedup on insert, so memory is bounded by
1523
1544
  * the **built generation** — the in-memory Roaring representation of the *distinct* set (≤ one bitmap per
1524
1545
  * non-empty chunk) — not by the input length: you can stream a billion duplicate-heavy ids and hold only the
1525
1546
  * distinct result. Note this is `O(distinct set)`, **not** window-bounded like {@link SegmentEngine.intersect}:
1526
- * bulk-load holds the whole generation in RAM, which suits batch seed/sweep jobs but not an
1547
+ * bulk-load holds the whole generation in RAM, which suits batch seed/rebuild jobs but not an
1527
1548
  * unbounded-cardinality stream (a segment larger than RAM needs external-merge / pre-sorted input — later phase).
1528
1549
  *
1529
1550
  * Each id must be an integer in `[0, 2³²)` ({@link splitId} throws {@link ValidationError} otherwise) — the
@@ -1541,7 +1562,7 @@ declare function bulkLoadCrbmGeneration(driver: IColdDriver, key: GenKey, ids: I
1541
1562
  keystore?: IKeystore;
1542
1563
  requireEncryption?: boolean;
1543
1564
  audit?: IAuditSink;
1544
- /** Bitmap codec ([DECISIONS #58]). Optional in the type; a **flavor** package binds it ({@link requireCodec}). */
1565
+ /** Bitmap codec. Optional in the type; a **flavor** package binds it ({@link requireCodec}). */
1545
1566
  codec?: CodecInterface;
1546
1567
  }): Promise<BulkLoadResult>;
1547
1568
 
@@ -1696,7 +1717,7 @@ declare class InProcessKeystore implements IKeystore {
1696
1717
  }
1697
1718
 
1698
1719
  /**
1699
- * Crypto-shred erasure (Phase 4e, [DECISIONS #19]).
1720
+ * Crypto-shred erasure (Phase 4e).
1700
1721
  *
1701
1722
  * GDPR "right to erasure" on immutable/backed-up Cold storage: you can't delete a `.crbm` object from every
1702
1723
  * backup, but you can **delete its key**. `destroySegment` drops the segment's wrapped DEK(s) from the registry
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as IWarmDriver, h as ColdChunkSource, S as SegmentRef, W as WarmReadOptions, i as SegmentSize, I as IColdDriver, d as IRegistryDriver, j as IKeystore, b as ChunkRef, C as ColdCaps, G as GenKey, B as BlobSink, R as RegCaps, e as RegistryRecord, f as NewRegistryRecord, T as Token, g as RegistryPatch, c as WarmRow, N as NoRow, k as CrbmCrypto, l as BlobReader, m as WrappedDek, A as Aead, n as AeadSealed } from './ports-D3BrJ6ax.js';
2
- export { o as BufferReader, p as BufferSink, q as GovernanceMeta, r as NO_ROW, s as RegistryStatus, t as aadFor } from './ports-D3BrJ6ax.js';
1
+ import { a as IWarmDriver, h as ColdChunkSource, S as SegmentRef, W as WarmReadOptions, i as SegmentSize, I as IColdDriver, d as IRegistryDriver, j as IKeystore, b as ChunkRef, C as ColdCaps, G as GenKey, B as BlobSink, R as RegCaps, e as RegistryRecord, f as NewRegistryRecord, T as Token, g as RegistryPatch, c as WarmRow, N as NoRow, k as CrbmCrypto, l as BlobReader, m as WrappedDek, A as Aead, n as AeadSealed } from './ports-5BQTGKze.js';
2
+ export { o as BufferReader, p as BufferSink, q as GovernanceMeta, r as NO_ROW, s as RegistryStatus, t as aadFor } from './ports-5BQTGKze.js';
3
3
 
4
4
  /**
5
5
  * The **bitmap-codec seam**.
@@ -50,6 +50,17 @@ interface CodecBitmap {
50
50
  /** Ascending iterator over the set values. */
51
51
  [Symbol.iterator](): IterableIterator<number>;
52
52
  toArray(): number[];
53
+ /**
54
+ * The largest value in the set, or `undefined` when empty.
55
+ *
56
+ * **Optional, and deliberately so.** The engine uses it for one thing: asserting that a payload it is about
57
+ * to interpret **as a chunk** holds only 16-bit remainders (see {@link assertChunkPayload}). A codec that
58
+ * cannot answer this in better than O(n) should simply omit it — the engine then skips the check rather
59
+ * than walking every value on the read path, which is the one thing this must never cost.
60
+ *
61
+ * Roaring answers it in O(1) from its container index, so the flagship codec implements it.
62
+ */
63
+ maximum?(): number | undefined;
53
64
  }
54
65
  /**
55
66
  * A pluggable bitmap codec — the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.
@@ -426,7 +437,7 @@ interface EngineDeps {
426
437
  readonly cache?: BoundedLru<string, CodecBitmap>;
427
438
  readonly maxBitmapBytes?: number;
428
439
  /**
429
- * The bitmap codec (DECISIONS #58) — **required**. `core/` is
440
+ * The bitmap codec — **required**. `core/` is
430
441
  * codec-agnostic: it can have no default, because the concrete codec lives in a *flavor* package that depends
431
442
  * on core (a default here would invert that arrow). A flavor's facade injects it — `@cloudbitmaps/roaring`
432
443
  * passes `roaringCodec` — so applications never see this.
@@ -545,6 +556,21 @@ declare class SegmentEngine {
545
556
  * letting it flow into `joinId` and produce a bogus id.
546
557
  */
547
558
  private assertChunkKeyInRange;
559
+ /**
560
+ * The other half of invariant 5, and the one that was missing: a chunk payload holds **remainders** — 16-bit
561
+ * offsets within one chunk — so every value must be `<= 0xffff`.
562
+ *
563
+ * Nothing upstream established that. The byte cap bounds *size*, and CRC/AEAD prove the bytes are the bytes
564
+ * that were written — which anyone able to write the tier satisfies trivially. A value `>= 65536` then
565
+ * reaches `joinId`, which masks it (`remainder & 0xffff`) and emits a **fabricated id belonging to a
566
+ * different chunk's id space**: indistinguishable from real data, inflating `count()` and creating spurious
567
+ * `intersect` matches. Compaction is the only path that already failed loud (`CrbmWriter` rejects
568
+ * `cardinality > 65536`), which merely turns the same row into a permanently poison segment.
569
+ *
570
+ * Costs one `maximum()` per chunk, not per id — `maximum` is optional on the codec seam precisely so a codec
571
+ * that cannot answer in O(1) opts out instead of making the read path walk every value.
572
+ */
573
+ private assertChunkPayloadInRange;
548
574
  private effectiveChunk;
549
575
  /** The segment's current generation, resolved once per op (`undefined` ⇒ source can't report it). */
550
576
  private currentGen;
@@ -824,7 +850,7 @@ declare class RecordingAuditSink implements IAuditSink {
824
850
  * **Correctness without a lock:** the commit is the conditional `currentGen` swap; write-once generations +
825
851
  * version-fenced purge make even two concurrent compactions safe (the loser's staged generation is an orphan
826
852
  * GC'd later; no torn read — readers are pinned to a committed generation, **I5**). The lease only avoids that
827
- * wasted duplicate work (see DECISIONS #18).
853
+ * wasted duplicate work.
828
854
  *
829
855
  * Pure orchestration over the injected driver interfaces + an injected {@link Clock} (timer-free; the daemon
830
856
  * layer owns scheduling/heartbeat). A crash at *any* step is recoverable: nothing is destructive until after
@@ -839,7 +865,7 @@ interface CompactionDeps {
839
865
  readonly clock: Pick<Clock, 'now'>;
840
866
  readonly maxBitmapBytes?: number;
841
867
  /**
842
- * Bitmap codec ([DECISIONS #58]) — compaction only merges bitmaps through it. Optional in the type so this
868
+ * Bitmap codec — compaction only merges bitmaps through it. Optional in the type so this
843
869
  * stays call-compatible public API; a **flavor** package binds it (see {@link requireCodec}).
844
870
  */
845
871
  readonly codec?: CodecInterface;
@@ -1040,7 +1066,7 @@ interface ExportOptions {
1040
1066
  /** Output format; defaults to `'roaring'`. */
1041
1067
  readonly format?: ExportFormat;
1042
1068
  /**
1043
- * Bitmap codec used to build the exported bitmap for the `'roaring'` format ([DECISIONS #58]). Optional in the
1069
+ * Bitmap codec used to build the exported bitmap for the `'roaring'` format. Optional in the
1044
1070
  * type so this stays call-compatible public API; a **flavor** package binds it (see `requireCodec`). Not needed
1045
1071
  * for `'ndjson'`, which streams plain ids.
1046
1072
  */
@@ -1123,11 +1149,6 @@ interface BitRoute {
1123
1149
  }
1124
1150
  /** Split a u32 id into `{ chunkKey, remainder }`. Throws `ValidationError` for non-u32 input. */
1125
1151
  declare function splitId(id: number): BitRoute;
1126
- /**
1127
- * Recombine a chunk key + remainder back into the u32 id. Precondition: `chunkKey` is a validated
1128
- * 16-bit value (tier-derived keys are validated upstream in the engine before reaching here, per
1129
- * invariant 5); `remainder` is masked to 16 bits.
1130
- */
1131
1152
  declare function joinId(chunkKey: number, remainder: number): number;
1132
1153
 
1133
1154
  /**
@@ -1516,14 +1537,14 @@ interface BulkLoadResult {
1516
1537
  readonly cardinality: number;
1517
1538
  }
1518
1539
  /**
1519
- * **Bulk-load** a whole immutable generation from a flat id source — the batch "seed/sweep" entry point.
1540
+ * **Bulk-load** a whole immutable generation from a flat id source — the batch "seed/rebuild" entry point.
1520
1541
  * Unlike {@link writeCrbmGeneration} (which takes pre-grouped bitmaps), this
1521
1542
  * consumes an arbitrary, **unsorted** stream of u32 ids (sync or async) and routes each into its chunk's
1522
1543
  * Roaring bitmap as it goes. The input is consumed lazily and ids dedup on insert, so memory is bounded by
1523
1544
  * the **built generation** — the in-memory Roaring representation of the *distinct* set (≤ one bitmap per
1524
1545
  * non-empty chunk) — not by the input length: you can stream a billion duplicate-heavy ids and hold only the
1525
1546
  * distinct result. Note this is `O(distinct set)`, **not** window-bounded like {@link SegmentEngine.intersect}:
1526
- * bulk-load holds the whole generation in RAM, which suits batch seed/sweep jobs but not an
1547
+ * bulk-load holds the whole generation in RAM, which suits batch seed/rebuild jobs but not an
1527
1548
  * unbounded-cardinality stream (a segment larger than RAM needs external-merge / pre-sorted input — later phase).
1528
1549
  *
1529
1550
  * Each id must be an integer in `[0, 2³²)` ({@link splitId} throws {@link ValidationError} otherwise) — the
@@ -1541,7 +1562,7 @@ declare function bulkLoadCrbmGeneration(driver: IColdDriver, key: GenKey, ids: I
1541
1562
  keystore?: IKeystore;
1542
1563
  requireEncryption?: boolean;
1543
1564
  audit?: IAuditSink;
1544
- /** Bitmap codec ([DECISIONS #58]). Optional in the type; a **flavor** package binds it ({@link requireCodec}). */
1565
+ /** Bitmap codec. Optional in the type; a **flavor** package binds it ({@link requireCodec}). */
1545
1566
  codec?: CodecInterface;
1546
1567
  }): Promise<BulkLoadResult>;
1547
1568
 
@@ -1696,7 +1717,7 @@ declare class InProcessKeystore implements IKeystore {
1696
1717
  }
1697
1718
 
1698
1719
  /**
1699
- * Crypto-shred erasure (Phase 4e, [DECISIONS #19]).
1720
+ * Crypto-shred erasure (Phase 4e).
1700
1721
  *
1701
1722
  * GDPR "right to erasure" on immutable/backed-up Cold storage: you can't delete a `.crbm` object from every
1702
1723
  * backup, but you can **delete its key**. `destroySegment` drops the segment's wrapped DEK(s) from the registry
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { mapWithConcurrency } from './chunk-SNJVZ227.js';
2
2
  export { mapWithConcurrency } from './chunk-SNJVZ227.js';
3
- import { validateNewRegistryRecord, recordFromNew, validateRegistryPatch, applyRegistryPatch, registryCounterOf, parseRegistryEnvelope, serializeRegistryEnvelope } from './chunk-2YDULGXS.js';
4
- import { NO_ROW } from './chunk-AS6ODRLT.js';
5
- export { NO_ROW } from './chunk-AS6ODRLT.js';
3
+ import { validateNewRegistryRecord, recordFromNew, validateRegistryPatch, applyRegistryPatch, registryCounterOf, parseRegistryEnvelope, serializeRegistryEnvelope } from './chunk-L54FL5DW.js';
4
+ import { NO_ROW } from './chunk-YEHMG6W2.js';
5
+ export { NO_ROW } from './chunk-YEHMG6W2.js';
6
6
  import { ValidationError, BudgetExceededError, isTransientError, isWriteConflictError, WriteConflictError, IntegrityError, UnsupportedError, CapabilityError, KeyUnavailableError, validateChunkRef, validateSegmentRef, isNotFoundError, NotFoundError, TransientError, namespacePart } from './chunk-NUIDEEFZ.js';
7
7
  export { BudgetExceededError, CapabilityError, CloudRoaringError, IntegrityError, KeyUnavailableError, NotFoundError, TimeoutError, TransientError, UnsupportedError, ValidationError, WriteConflictError, isCloudRoaringError, isIntegrityError, isNotFoundError, isTransientError, isValidationError, isWriteConflictError, validateSegmentRef } from './chunk-NUIDEEFZ.js';
8
8
  import { createHash, randomUUID, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
@@ -20,6 +20,7 @@ function splitId(id) {
20
20
  }
21
21
  return { chunkKey: id >>> 16, remainder: id & LOW_16 };
22
22
  }
23
+ var MAX_REMAINDER = CHUNK_COUNT - 1;
23
24
  function joinId(chunkKey, remainder) {
24
25
  return ((chunkKey & LOW_16) << 16 >>> 0) + (remainder & LOW_16);
25
26
  }
@@ -82,8 +83,18 @@ function decodeDelta(bytes, maxBitmapBytes, codec) {
82
83
  }
83
84
  const adds = codec.safeDeserialize(bytes.subarray(addsStart, addsEnd), maxBitmapBytes);
84
85
  const removes = codec.safeDeserialize(bytes.subarray(addsEnd), maxBitmapBytes);
86
+ assertRemainderRange(adds, "adds");
87
+ assertRemainderRange(removes, "removes");
85
88
  return { adds, removes };
86
89
  }
90
+ function assertRemainderRange(bitmap, half) {
91
+ const max = bitmap.maximum?.();
92
+ if (max !== void 0 && max > MAX_REMAINDER) {
93
+ throw new IntegrityError(
94
+ `warm delta ${half} holds value ${max}, outside the 16-bit remainder range [0, ${MAX_REMAINDER}] \u2014 the stored row is corrupt or was not written by this codec`
95
+ );
96
+ }
97
+ }
87
98
 
88
99
  // src/core/budget.ts
89
100
  var DEFAULT_BUDGET = { maxRequests: 1e6 };
@@ -611,6 +622,28 @@ var SegmentEngine = class {
611
622
  throw new IntegrityError(`chunk key from a tier is out of range: ${k}`);
612
623
  }
613
624
  }
625
+ /**
626
+ * The other half of invariant 5, and the one that was missing: a chunk payload holds **remainders** — 16-bit
627
+ * offsets within one chunk — so every value must be `<= 0xffff`.
628
+ *
629
+ * Nothing upstream established that. The byte cap bounds *size*, and CRC/AEAD prove the bytes are the bytes
630
+ * that were written — which anyone able to write the tier satisfies trivially. A value `>= 65536` then
631
+ * reaches `joinId`, which masks it (`remainder & 0xffff`) and emits a **fabricated id belonging to a
632
+ * different chunk's id space**: indistinguishable from real data, inflating `count()` and creating spurious
633
+ * `intersect` matches. Compaction is the only path that already failed loud (`CrbmWriter` rejects
634
+ * `cardinality > 65536`), which merely turns the same row into a permanently poison segment.
635
+ *
636
+ * Costs one `maximum()` per chunk, not per id — `maximum` is optional on the codec seam precisely so a codec
637
+ * that cannot answer in O(1) opts out instead of making the read path walk every value.
638
+ */
639
+ assertChunkPayloadInRange(bitmap, chunkKey) {
640
+ const max = bitmap.maximum?.();
641
+ if (max !== void 0 && max > MAX_REMAINDER) {
642
+ throw new IntegrityError(
643
+ `chunk ${chunkKey} payload holds value ${max}, outside the 16-bit remainder range [0, ${MAX_REMAINDER}] \u2014 the stored object is corrupt or was not written by this codec`
644
+ );
645
+ }
646
+ }
614
647
  async effectiveChunk(seg, chunkKey, warmRows, gen) {
615
648
  const warmBytes = warmRows.get(chunkKey);
616
649
  const delta = warmBytes ? decodeDelta(warmBytes, this.maxBitmapBytes, this.codec) : emptyDelta(this.codec);
@@ -655,6 +688,7 @@ var SegmentEngine = class {
655
688
  }
656
689
  if (!bytes) return null;
657
690
  const bitmap = this.codec.safeDeserialize(bytes, this.maxBitmapBytes);
691
+ this.assertChunkPayloadInRange(bitmap, ref.chunkKey);
658
692
  this.cache?.set(cacheKey, bitmap);
659
693
  return bitmap;
660
694
  }