@cloudbitmaps/core 0.5.0 → 0.6.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/dist/index.cjs +18 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -61,6 +61,27 @@ interface CodecBitmap {
|
|
|
61
61
|
* Roaring answers it in O(1) from its container index, so the flagship codec implements it.
|
|
62
62
|
*/
|
|
63
63
|
maximum?(): number | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Re-encode for size, in place, immediately before a **cold** write. Representation only — this must never
|
|
66
|
+
* change membership, and `serialize()` afterwards must decode back to exactly the same set.
|
|
67
|
+
*
|
|
68
|
+
* **Optional, like {@link maximum}.** A codec whose encoding has no size decision to make (a plain bitset has
|
|
69
|
+
* one representation and nothing to choose) simply omits it, and the engine skips the call.
|
|
70
|
+
*
|
|
71
|
+
* WHY THIS EXISTS. Roaring picks per container between an array, a bitset and a RUN — but the run choice is
|
|
72
|
+
* not automatic in any implementation: it is a `runOptimize()` pass you have to ask for, and nothing here was
|
|
73
|
+
* asking. So two of the three container types were ever used, and run-shaped data paid list or bitmap prices
|
|
74
|
+
* for a run. Measured on the shipped codec: a contiguous 1,000,000-id range serialized to 128.1 KiB where
|
|
75
|
+
* run-encoding needs **0.2 KiB** (570×), and a 2,000-run shape 536.5 KiB against **8.5 KiB** (63×). Sparse
|
|
76
|
+
* data is unchanged, because there are no runs to find — the pass is not a gamble.
|
|
77
|
+
*
|
|
78
|
+
* WHY ONLY ON THE COLD PATH. This is called where a whole immutable generation is written, so its cost
|
|
79
|
+
* amortizes over a write that is already serializing and checksumming every chunk. It is deliberately NOT
|
|
80
|
+
* called on the warm delta path (`chunk.ts`), which runs per operation: the hot path must not pay for a
|
|
81
|
+
* rare-ish win, per KISS/YAGNI. Warm rows are short-lived and get folded into a cold generation by
|
|
82
|
+
* compaction, where they are optimized then.
|
|
83
|
+
*/
|
|
84
|
+
optimize?(): void;
|
|
64
85
|
}
|
|
65
86
|
/**
|
|
66
87
|
* A pluggable bitmap codec — the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.
|
|
@@ -2021,7 +2042,15 @@ interface DestroyResult {
|
|
|
2021
2042
|
* `segment.erase` audit event keys off — a cleartext "erase" is not an irreversible destruction.
|
|
2022
2043
|
*/
|
|
2023
2044
|
readonly cryptoShredded: boolean;
|
|
2024
|
-
/**
|
|
2045
|
+
/**
|
|
2046
|
+
* Why this segment was not a fresh crypto-shred. `'absent'` (no registry row), `'already'` (already
|
|
2047
|
+
* destroyed), `'cleartext'` (no DEK to shred) — and from `eraseNamespace` only, where per-segment faults are
|
|
2048
|
+
* isolated so one failure cannot discard the ledger: `'contended'` (warm rows were rewritten during every
|
|
2049
|
+
* erase pass) or `` `failed: <message>` `` (any other fault).
|
|
2050
|
+
*
|
|
2051
|
+
* The last two come with `destroyed: false` and mean **the segment still holds data**. A namespace erase
|
|
2052
|
+
* returns them rather than throwing, so its entries have to be inspected — see {@link eraseNamespace}.
|
|
2053
|
+
*/
|
|
2025
2054
|
readonly reason?: string;
|
|
2026
2055
|
/** Warm rows physically deleted as part of the erase. */
|
|
2027
2056
|
readonly warmRowsDeleted: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,27 @@ interface CodecBitmap {
|
|
|
61
61
|
* Roaring answers it in O(1) from its container index, so the flagship codec implements it.
|
|
62
62
|
*/
|
|
63
63
|
maximum?(): number | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Re-encode for size, in place, immediately before a **cold** write. Representation only — this must never
|
|
66
|
+
* change membership, and `serialize()` afterwards must decode back to exactly the same set.
|
|
67
|
+
*
|
|
68
|
+
* **Optional, like {@link maximum}.** A codec whose encoding has no size decision to make (a plain bitset has
|
|
69
|
+
* one representation and nothing to choose) simply omits it, and the engine skips the call.
|
|
70
|
+
*
|
|
71
|
+
* WHY THIS EXISTS. Roaring picks per container between an array, a bitset and a RUN — but the run choice is
|
|
72
|
+
* not automatic in any implementation: it is a `runOptimize()` pass you have to ask for, and nothing here was
|
|
73
|
+
* asking. So two of the three container types were ever used, and run-shaped data paid list or bitmap prices
|
|
74
|
+
* for a run. Measured on the shipped codec: a contiguous 1,000,000-id range serialized to 128.1 KiB where
|
|
75
|
+
* run-encoding needs **0.2 KiB** (570×), and a 2,000-run shape 536.5 KiB against **8.5 KiB** (63×). Sparse
|
|
76
|
+
* data is unchanged, because there are no runs to find — the pass is not a gamble.
|
|
77
|
+
*
|
|
78
|
+
* WHY ONLY ON THE COLD PATH. This is called where a whole immutable generation is written, so its cost
|
|
79
|
+
* amortizes over a write that is already serializing and checksumming every chunk. It is deliberately NOT
|
|
80
|
+
* called on the warm delta path (`chunk.ts`), which runs per operation: the hot path must not pay for a
|
|
81
|
+
* rare-ish win, per KISS/YAGNI. Warm rows are short-lived and get folded into a cold generation by
|
|
82
|
+
* compaction, where they are optimized then.
|
|
83
|
+
*/
|
|
84
|
+
optimize?(): void;
|
|
64
85
|
}
|
|
65
86
|
/**
|
|
66
87
|
* A pluggable bitmap codec — the factory the codec-agnostic engine constructs {@link CodecBitmap}s through.
|
|
@@ -2021,7 +2042,15 @@ interface DestroyResult {
|
|
|
2021
2042
|
* `segment.erase` audit event keys off — a cleartext "erase" is not an irreversible destruction.
|
|
2022
2043
|
*/
|
|
2023
2044
|
readonly cryptoShredded: boolean;
|
|
2024
|
-
/**
|
|
2045
|
+
/**
|
|
2046
|
+
* Why this segment was not a fresh crypto-shred. `'absent'` (no registry row), `'already'` (already
|
|
2047
|
+
* destroyed), `'cleartext'` (no DEK to shred) — and from `eraseNamespace` only, where per-segment faults are
|
|
2048
|
+
* isolated so one failure cannot discard the ledger: `'contended'` (warm rows were rewritten during every
|
|
2049
|
+
* erase pass) or `` `failed: <message>` `` (any other fault).
|
|
2050
|
+
*
|
|
2051
|
+
* The last two come with `destroyed: false` and mean **the segment still holds data**. A namespace erase
|
|
2052
|
+
* returns them rather than throwing, so its entries have to be inspected — see {@link eraseNamespace}.
|
|
2053
|
+
*/
|
|
2025
2054
|
readonly reason?: string;
|
|
2026
2055
|
/** Warm rows physically deleted as part of the erase. */
|
|
2027
2056
|
readonly warmRowsDeleted: number;
|
package/dist/index.js
CHANGED
|
@@ -2074,6 +2074,7 @@ function writeCrbmGeneration(driver, key, chunks, options = {}) {
|
|
|
2074
2074
|
const writer = new CrbmWriter(sink, { generation: key.generation, crypto: options.crypto });
|
|
2075
2075
|
for (const { chunkKey, bitmap } of sorted) {
|
|
2076
2076
|
if (bitmap.isEmpty) continue;
|
|
2077
|
+
bitmap.optimize?.();
|
|
2077
2078
|
await writer.addChunk(chunkKey, bitmap.serialize(), bitmap.size);
|
|
2078
2079
|
const pause = tick();
|
|
2079
2080
|
if (pause !== null) await pause;
|
|
@@ -2089,6 +2090,7 @@ async function writeCrbmGenerationStream(driver, key, chunks, options = {}) {
|
|
|
2089
2090
|
const writer = new CrbmWriter(sink, { generation: key.generation, crypto: options.crypto });
|
|
2090
2091
|
for await (const { chunkKey, bitmap } of chunks) {
|
|
2091
2092
|
if (bitmap.isEmpty) continue;
|
|
2093
|
+
bitmap.optimize?.();
|
|
2092
2094
|
await writer.addChunk(chunkKey, bitmap.serialize(), bitmap.size);
|
|
2093
2095
|
chunkKeys.push(chunkKey);
|
|
2094
2096
|
cardinality += bitmap.size;
|
|
@@ -3671,7 +3673,22 @@ async function eraseNamespace(namespace, deps, options) {
|
|
|
3671
3673
|
const destroyed = [];
|
|
3672
3674
|
let segmentsShredded = 0;
|
|
3673
3675
|
for (const ref of refs) {
|
|
3674
|
-
|
|
3676
|
+
let result;
|
|
3677
|
+
try {
|
|
3678
|
+
result = await shredSegment(ref, deps, options.allowCleartext ?? false);
|
|
3679
|
+
} catch (err) {
|
|
3680
|
+
result = {
|
|
3681
|
+
segment: ref.segment,
|
|
3682
|
+
namespace: ref.namespace,
|
|
3683
|
+
destroyed: false,
|
|
3684
|
+
cryptoShredded: false,
|
|
3685
|
+
reason: isWriteConflictError(err) ? "contended" : `failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
3686
|
+
// Deliberately 0, and not a partial count. `eraseWarm` may have deleted rows before it threw, but the
|
|
3687
|
+
// erase did not complete as a unit, so a partial figure on a failed entry would invite reading it as
|
|
3688
|
+
// progress toward a destruction that did not happen. The attestation that matters is `destroyed`.
|
|
3689
|
+
warmRowsDeleted: 0
|
|
3690
|
+
};
|
|
3691
|
+
}
|
|
3675
3692
|
destroyed.push(result);
|
|
3676
3693
|
if (result.cryptoShredded) {
|
|
3677
3694
|
segmentsShredded += 1;
|