@cloudbitmaps/core 0.19.0 → 0.20.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/{chunk-TNWTIAON.js → chunk-YSC7NJWV.js} +314 -216
- package/dist/chunk-YSC7NJWV.js.map +7 -0
- package/dist/core/audit.d.ts +56 -3
- package/dist/core/combine-many.d.ts +13 -4
- package/dist/core/consistency.d.ts +10 -4
- package/dist/core/crbm/fingerprint.d.ts +18 -0
- package/dist/core/crbm/format.d.ts +2 -4
- package/dist/core/crbm/frame.d.ts +10 -0
- package/dist/core/crbm/reader.d.ts +1 -3
- package/dist/core/crbm-storage-source.d.ts +249 -96
- package/dist/core/engine.d.ts +22 -14
- package/dist/core/erase-id.d.ts +62 -26
- package/dist/core/generation-gc.d.ts +3 -2
- package/dist/core/keys.d.ts +28 -0
- package/dist/core/leases.d.ts +7 -0
- package/dist/core/load.d.ts +11 -7
- package/dist/core/metrics.d.ts +5 -5
- package/dist/core/pinned-storage-source.d.ts +3 -3
- package/dist/core/pointer-id.d.ts +19 -0
- package/dist/core/ports.d.ts +131 -35
- package/dist/core/reader-defaults.d.ts +14 -3
- package/dist/core/rollback.d.ts +2 -1
- package/dist/core/summary.d.ts +38 -21
- package/dist/core/token.d.ts +13 -8
- package/dist/driver-kit.d.ts +2 -1
- package/dist/driver-kit.js +11 -4
- package/dist/driver-kit.js.map +2 -2
- package/dist/drivers/_shared/object-registry.d.ts +1 -1
- package/dist/drivers/_shared/registry.d.ts +26 -17
- package/dist/drivers/localfs/storage.d.ts +7 -6
- package/dist/drivers/memory.d.ts +8 -6
- package/dist/drivers/retry/retrying-chunk-source.d.ts +1 -5
- package/dist/export/index.d.ts +16 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +1288 -1080
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/dist/chunk-TNWTIAON.js.map +0 -7
- package/dist/core/cost.d.ts +0 -351
package/dist/core/audit.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* from the metrics sink (`IMetricsSink`). Different audience (an audit log / SIEM, not a dashboard), different
|
|
4
4
|
* retention, and only the compliance-relevant *state changes* — never routine reads/writes (that's the metrics
|
|
5
5
|
* sink). It doubles as the GDPR Art. 30 "record of processing" surface: publishes, refused loads, rollbacks,
|
|
6
|
-
* rewrites,
|
|
6
|
+
* rewrites, an erasure's deletion of the generations holding an id, crypto-shreds and disposals.
|
|
7
7
|
*
|
|
8
8
|
* Like `Clock`/`Rng`/`IMetricsSink`, it's injected (into the operations that emit — a load, the `*Into`
|
|
9
9
|
* verbs, a rollback, the erasure rewrite, crypto-shred, disposal and the retention sweep) and wrapped
|
|
@@ -16,12 +16,24 @@
|
|
|
16
16
|
* wrappings, no data re-encryption), so there is no library call to hook. A future `rewrapSegment()` op would
|
|
17
17
|
* add a `kek.rotate` variant; until then, audit key changes at your keystore-config layer.
|
|
18
18
|
*/
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* A security/compliance-relevant state change. Vendor-neutral; the sink adds its own timestamp/actor.
|
|
21
|
+
*
|
|
22
|
+
* Every event about a segment carries `incarnation`: the id of the registry row's incarnation the operation acted on.
|
|
23
|
+
* A segment's name can be purged and created again, and its generations then start again at `0`; the incarnation
|
|
24
|
+
* tells those lives of the name apart, and every write of a row keeps it. It does not make a generation number name
|
|
25
|
+
* one object for good: within one incarnation, a number whose object was deleted (a refused load's object, or a
|
|
26
|
+
* generation an erasure deleted above the pointer) can be taken again by a later load. It is absent when the row's
|
|
27
|
+
* token carries no incarnation id, for example from a registry of the caller's own that issues tokens in another
|
|
28
|
+
* form, and on a `segment.load-refused` from a load that found no row.
|
|
29
|
+
*/
|
|
20
30
|
export type AuditEvent = {
|
|
21
31
|
/** A new immutable Storage generation *became the segment's current generation*, by a load's publish. */
|
|
22
32
|
readonly kind: 'segment.publish';
|
|
23
33
|
readonly namespace?: string;
|
|
24
34
|
readonly segment: string;
|
|
35
|
+
/** The incarnation of the row whose pointer the publish moved. */
|
|
36
|
+
readonly incarnation?: string;
|
|
25
37
|
readonly generation: number;
|
|
26
38
|
} | {
|
|
27
39
|
/**
|
|
@@ -38,6 +50,8 @@ export type AuditEvent = {
|
|
|
38
50
|
readonly kind: 'segment.rollback';
|
|
39
51
|
readonly namespace?: string;
|
|
40
52
|
readonly segment: string;
|
|
53
|
+
/** The incarnation of the row whose pointer the rollback moved. */
|
|
54
|
+
readonly incarnation?: string;
|
|
41
55
|
readonly fromGeneration: number | null;
|
|
42
56
|
readonly generation: number;
|
|
43
57
|
} | {
|
|
@@ -59,6 +73,8 @@ export type AuditEvent = {
|
|
|
59
73
|
readonly kind: 'segment.load-refused';
|
|
60
74
|
readonly namespace?: string;
|
|
61
75
|
readonly segment: string;
|
|
76
|
+
/** The incarnation of the row the load read before it wrote; absent when it found no row. */
|
|
77
|
+
readonly incarnation?: string;
|
|
62
78
|
readonly generation: number;
|
|
63
79
|
readonly reason: 'empty' | 'min-cardinality' | 'min-retained' | 'max-growth' | 'superseded';
|
|
64
80
|
readonly cardinality: number;
|
|
@@ -69,13 +85,46 @@ export type AuditEvent = {
|
|
|
69
85
|
* place. Today the one emitter is `eraseIdFromSegment` (a subject erasure clearing one id), and the event is
|
|
70
86
|
* emitted at the publish — before the superseded generation is collected — so the record exists the moment
|
|
71
87
|
* the generation without the id is authoritative. A `segment.publish` is NOT also emitted for a rewrite: a
|
|
72
|
-
* rewrite derives its content from the segment itself, a publish brings content in from outside.
|
|
88
|
+
* rewrite derives its content from the segment itself, a publish brings content in from outside. An erasure
|
|
89
|
+
* that finds the id only outside the current generation, or deletes only objects no read of the segment can
|
|
90
|
+
* open, rewrites nothing and emits `segment.collect`.
|
|
73
91
|
*/
|
|
74
92
|
readonly kind: 'segment.rewrite';
|
|
75
93
|
readonly namespace?: string;
|
|
76
94
|
readonly segment: string;
|
|
95
|
+
/** The incarnation of the row the rewrite read `fromGeneration` from and published on. */
|
|
96
|
+
readonly incarnation?: string;
|
|
77
97
|
readonly fromGeneration: number;
|
|
78
98
|
readonly generation: number;
|
|
99
|
+
} | {
|
|
100
|
+
/**
|
|
101
|
+
* An erasure of one id **deleted objects** no reader resolves, and rewrote none. It found the id only in such
|
|
102
|
+
* objects, or deleted only objects no read of the segment can open, which it cannot search and deletes whatever
|
|
103
|
+
* the id. The cases: an id that only generations other than the current one hold (someone who left the segment,
|
|
104
|
+
* whose bit the reader grace window or a rollback target keeps); objects left under a tombstone (a cleartext
|
|
105
|
+
* destroy, or a drop whose sweep left one); a first load's object on a segment with no generation yet (its load
|
|
106
|
+
* still running, or one that crashed before its publish); and an object sealed under a key the segment's row does
|
|
107
|
+
* not hold, or an encrypted one under a cleartext row (a first load's that lost the race, or crashed). The one
|
|
108
|
+
* emitter is `eraseIdFromSegment`, once a listing of the bucket shows nothing left holding the id, so the record
|
|
109
|
+
* exists only for a finished erasure. A call that ends otherwise throws or reports `erased: false` with another
|
|
110
|
+
* reason and emits nothing, even when it had deleted a holder; its re-run emits when it finishes, and reports
|
|
111
|
+
* `'not-member'` with no event when another collection deleted the last holder meanwhile, as its ledger entry
|
|
112
|
+
* does.
|
|
113
|
+
*
|
|
114
|
+
* `fromGeneration` is the newest generation the call found holding the id, and is absent when it found the id in
|
|
115
|
+
* none and deleted only objects it could not search: then the ledger lists no entry for the segment, and this
|
|
116
|
+
* event is the record of the deletion. `collected` is every generation the call deleted, ascending, and the same
|
|
117
|
+
* list as its result's: below the pointer, when a searched generation held the id, that is every generation,
|
|
118
|
+
* holder or not, since the deletion takes them all; above it, and wherever none did, only the holders and the
|
|
119
|
+
* objects it could not search. A holder another collection deleted first is not in it, so the list can be empty.
|
|
120
|
+
*/
|
|
121
|
+
readonly kind: 'segment.collect';
|
|
122
|
+
readonly namespace?: string;
|
|
123
|
+
readonly segment: string;
|
|
124
|
+
/** The incarnation of the row the erasure read: the active row, or the tombstone. */
|
|
125
|
+
readonly incarnation?: string;
|
|
126
|
+
readonly fromGeneration?: number;
|
|
127
|
+
readonly collected: readonly number[];
|
|
79
128
|
} | {
|
|
80
129
|
/**
|
|
81
130
|
* A segment was **crypto-shredded** — its wrapped DEK(s) are gone, so its at-rest Storage bytes are now
|
|
@@ -85,6 +134,8 @@ export type AuditEvent = {
|
|
|
85
134
|
readonly kind: 'segment.erase';
|
|
86
135
|
readonly namespace?: string;
|
|
87
136
|
readonly segment: string;
|
|
137
|
+
/** The incarnation of the row the shred turned into a tombstone. */
|
|
138
|
+
readonly incarnation?: string;
|
|
88
139
|
} | {
|
|
89
140
|
/**
|
|
90
141
|
* A segment was **disposed of** — tombstoned and its storage reclaimed (its Storage generations deleted) by
|
|
@@ -106,6 +157,8 @@ export type AuditEvent = {
|
|
|
106
157
|
readonly kind: 'segment.dispose';
|
|
107
158
|
readonly namespace?: string;
|
|
108
159
|
readonly segment: string;
|
|
160
|
+
/** The incarnation of the tombstone the drop wrote or found. */
|
|
161
|
+
readonly incarnation?: string;
|
|
109
162
|
readonly generationsDeleted: number;
|
|
110
163
|
} | {
|
|
111
164
|
/** A namespace erasure was executed. `segmentsShredded` is the count actually crypto-shredded (may be 0). */
|
|
@@ -26,16 +26,19 @@ export interface CombineManyOperand {
|
|
|
26
26
|
/** The generation the operand is pinned to: `null` when pinned to a segment with none, `undefined` when not pinned. */
|
|
27
27
|
readonly pinnedGeneration?: number | null;
|
|
28
28
|
/**
|
|
29
|
-
* The pin's version: the generation and the registry row's
|
|
30
|
-
* the generation where there is no row. It is what tells a name deleted and created again, which restarts at
|
|
29
|
+
* The pin's version: the generation and the registry row's `pointerId` it was taken under (`<generation>:<pointerId>`),
|
|
30
|
+
* or just the generation where there is no row. It is what tells a name deleted and created again, which restarts at
|
|
31
31
|
* generation 0, from the one that was pinned.
|
|
32
32
|
*/
|
|
33
33
|
readonly pinnedVersion?: string | null;
|
|
34
34
|
/** The fingerprint of the object the pin holds: the identity of the generation's bytes, where a number can be taken again. */
|
|
35
35
|
readonly pinnedFingerprint?: string | null;
|
|
36
36
|
/**
|
|
37
|
-
* A fresh read of the segment's registry row: its current generation and token
|
|
38
|
-
*
|
|
37
|
+
* A fresh read of the segment's registry row: its current generation and, as `token`, its `pointerId` (the token of the
|
|
38
|
+
* last write that named a field a read resolves through, so a lease or a policy write does not change it), or `null`
|
|
39
|
+
* when it
|
|
40
|
+
* has no row. Read for every subtracted pinned operand just before the publishes, and once more at the end of the call
|
|
41
|
+
* for the rest.
|
|
39
42
|
*/
|
|
40
43
|
readonly current?: (options?: {
|
|
41
44
|
readonly fingerprint?: boolean;
|
|
@@ -310,3 +313,9 @@ export declare class GroupCost {
|
|
|
310
313
|
* budget, both before any chunk is read.
|
|
311
314
|
*/
|
|
312
315
|
export declare function runCombineMany<R>(compiled: CompiledCombineMany<R>, deps: CombineManyDeps): Promise<CombineManyRun<R>>;
|
|
316
|
+
/**
|
|
317
|
+
* The `pointerId` of a row's version (`<generation>:<pointerId>`, as a pin holds it), or `undefined` where the version
|
|
318
|
+
* names no row. A live version from the `.crbm` source ends with the opened object's fingerprint: read it through
|
|
319
|
+
* `rowVersionOf` first.
|
|
320
|
+
*/
|
|
321
|
+
export declare function tokenOfVersion(version: string | null | undefined): string | undefined;
|
|
@@ -37,9 +37,13 @@ export interface ConsistencyIssue {
|
|
|
37
37
|
readonly currentGen: number;
|
|
38
38
|
/**
|
|
39
39
|
* `missing-storage-generation`: `currentGen` references a Storage generation that is not present (torn restore).
|
|
40
|
-
* `summary-mismatch` (only when asked for): the generation is there, but its row's summary
|
|
41
|
-
* count or metadata than the object holds (a row restored from
|
|
42
|
-
*
|
|
40
|
+
* `summary-mismatch` (only when asked for): the generation is there, but its row's summary names another object
|
|
41
|
+
* than the one under its number, says a different id count or metadata than the object holds (a row restored from
|
|
42
|
+
* another point than its bucket, or an object put back from outside the library), or records a fingerprint that
|
|
43
|
+
* names no object. A summary that names another object is what a count and a `stat()` answer from while every read
|
|
44
|
+
* that opens the object refuses it; one whose count or metadata disagrees is answered from until a read opens the
|
|
45
|
+
* object, which stops that store using it; one whose fingerprint names no object is not used, so every read opens the
|
|
46
|
+
* object unchecked.
|
|
43
47
|
*/
|
|
44
48
|
readonly issue: 'missing-storage-generation' | 'summary-mismatch';
|
|
45
49
|
}
|
|
@@ -75,7 +79,9 @@ export interface ConsistencyReport {
|
|
|
75
79
|
* `errored`.
|
|
76
80
|
*
|
|
77
81
|
* With `summaries: true` it also opens each segment's current object (one tail read, and a second for an index longer
|
|
78
|
-
* than it) and holds the row's summary against it
|
|
82
|
+
* than it) and holds the row's summary against it: the object it names by its fingerprint, and the count and metadata it
|
|
83
|
+
* says, reporting `summary-mismatch` where they disagree, and for a clear summary whose fingerprint names no object,
|
|
84
|
+
* which no read uses. A sealed summary
|
|
79
85
|
* needs `deps.keystore`; without it the segment is counted in `summariesUnchecked`. Off by default, since the
|
|
80
86
|
* default check only lists.
|
|
81
87
|
*/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** What names an object: its size, then its footer's CRC. One spelling, for an open reader and a footer read alone. */
|
|
2
|
+
export declare const sizePart: (size: number) => string;
|
|
3
|
+
/** An object's fingerprint, from its size and its footer's CRC (see `CrbmReader.fingerprint`). */
|
|
4
|
+
export declare const fingerprintFor: (size: number, footerCrc: number) => string;
|
|
5
|
+
/**
|
|
6
|
+
* A fingerprint taken apart, or `undefined` for text that is not one an object can have: a size that is a safe integer
|
|
7
|
+
* no smaller than a `.crbm` preamble and footer, and a footer CRC that fits in 32 bits. For a row's summary, which
|
|
8
|
+
* names its object by it and reports the size from it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fingerprintParts(fingerprint: unknown): {
|
|
11
|
+
readonly size: number;
|
|
12
|
+
readonly footerCrc: number;
|
|
13
|
+
} | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* A fingerprint spelled short, for a key a read builds on every chunk: its size and its footer's CRC in base 36,
|
|
16
|
+
* `<size>.<crc>`. Two compact spellings are equal exactly when the fingerprints are.
|
|
17
|
+
*/
|
|
18
|
+
export declare function compactFingerprint(fingerprint: string): string;
|
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
* These byte widths/offsets are pinned by the golden corpus and must never change for v1 —
|
|
5
5
|
* a new layout is a new format version. All multi-byte integers are little-endian (v1 fixes LE).
|
|
6
6
|
*/
|
|
7
|
+
import { FOOTER_BYTES, PREAMBLE_BYTES } from './frame.js';
|
|
7
8
|
/** 4-byte magic at both file ends: "CRBM". */
|
|
8
9
|
export declare const MAGIC: Uint8Array<ArrayBuffer>;
|
|
9
10
|
export declare const VERSION_MAJOR = 1;
|
|
10
11
|
export declare const VERSION_MINOR = 0;
|
|
11
|
-
|
|
12
|
-
export declare const PREAMBLE_BYTES = 8;
|
|
12
|
+
export { FOOTER_BYTES, PREAMBLE_BYTES };
|
|
13
13
|
/** Payloads begin immediately after the preamble. */
|
|
14
14
|
export declare const PAYLOAD_START = 8;
|
|
15
|
-
/** Fixed footer size (v1.0). */
|
|
16
|
-
export declare const FOOTER_BYTES = 104;
|
|
17
15
|
/** Byte offsets of each field within the 104-byte footer. */
|
|
18
16
|
export declare const FOOTER: {
|
|
19
17
|
readonly indexOffset: 0;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two widths every `.crbm` v1 object is framed by: its front preamble and its footer. Frozen with the rest of the
|
|
3
|
+
* layout in `format.ts`, which re-exports them. They live in a module of their own, with no imports, so that what needs
|
|
4
|
+
* only the smallest size an object can have (the registry's check of a summary's fingerprint) does not bring the
|
|
5
|
+
* format's other constants, or the reader, with it.
|
|
6
|
+
*/
|
|
7
|
+
/** Front preamble: magic(4) + version_major(1) + version_minor(1) + reserved(2). */
|
|
8
|
+
export declare const PREAMBLE_BYTES = 8;
|
|
9
|
+
/** Fixed footer size (v1.0). */
|
|
10
|
+
export declare const FOOTER_BYTES = 104;
|
|
@@ -49,8 +49,6 @@ export interface CrbmReaderOptions {
|
|
|
49
49
|
*/
|
|
50
50
|
readonly lineage?: unknown;
|
|
51
51
|
}
|
|
52
|
-
/** An object's fingerprint, from its size and its footer's CRC (see {@link CrbmReader.fingerprint}). */
|
|
53
|
-
export declare const fingerprintFor: (size: number, footerCrc: number) => string;
|
|
54
52
|
/**
|
|
55
53
|
* Whether the object behind `blob` says it is encrypted, from one tail read of a footer's worth, with no key and no
|
|
56
54
|
* index; a footer that fails its own checks throws, as an open would. For a caller that must not open an object with
|
|
@@ -102,7 +100,7 @@ export declare class CrbmReader {
|
|
|
102
100
|
* the object it reopens is the one it pinned, since a purged and reloaded name reuses the generation number.
|
|
103
101
|
*/
|
|
104
102
|
get fingerprint(): string;
|
|
105
|
-
/** Total object bytes (from the one-GET tail read)
|
|
103
|
+
/** Total object bytes (from the one-GET tail read): the `sizeBytes` a segment's `stat()` reports. */
|
|
106
104
|
get sizeBytes(): number;
|
|
107
105
|
/**
|
|
108
106
|
* Retained JS heap of this reader's parsed index — the weight the storage reader cache bounds on (a wide
|