@cloudbitmaps/core 0.21.0 → 0.23.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/README.md +23 -8
- package/dist/chunk-7GT632UF.js +641 -0
- package/dist/chunk-7GT632UF.js.map +7 -0
- package/dist/chunk-CGILDXGP.js +73 -0
- package/dist/chunk-CGILDXGP.js.map +7 -0
- package/dist/chunk-GKHERVVQ.js +121 -0
- package/dist/chunk-GKHERVVQ.js.map +7 -0
- package/dist/{chunk-IGLIZBR7.js → chunk-MVUD4R72.js} +253 -765
- package/dist/chunk-MVUD4R72.js.map +7 -0
- package/dist/core/audit.d.ts +53 -15
- package/dist/core/bit-route.d.ts +9 -0
- package/dist/core/blob.d.ts +4 -0
- package/dist/core/budget.d.ts +8 -3
- package/dist/core/check.d.ts +27 -0
- package/dist/core/codec.d.ts +1 -1
- package/dist/core/combine-expr.d.ts +9 -5
- package/dist/core/combine-feed.d.ts +11 -3
- package/dist/core/combine-many.d.ts +28 -13
- package/dist/core/consistency.d.ts +11 -2
- package/dist/core/cooperative.d.ts +7 -0
- package/dist/core/crbm-storage-source.d.ts +7 -4
- package/dist/core/crypto.d.ts +5 -0
- package/dist/core/determinism.d.ts +2 -1
- package/dist/core/discover.d.ts +5 -3
- package/dist/core/engine.d.ts +4 -4
- package/dist/core/erase-id.d.ts +52 -19
- package/dist/core/erasure.d.ts +62 -28
- package/dist/core/errors.d.ts +83 -9
- package/dist/core/leases.d.ts +2 -0
- package/dist/core/load-input.d.ts +48 -0
- package/dist/core/load.d.ts +71 -16
- package/dist/core/metrics.d.ts +22 -1
- package/dist/core/pinned-storage-source.d.ts +27 -8
- package/dist/core/pointer-id.d.ts +9 -0
- package/dist/core/ports.d.ts +107 -13
- package/dist/core/preflight.d.ts +80 -0
- package/dist/core/registry-scan.d.ts +0 -5
- package/dist/core/retention-sweep.d.ts +61 -16
- package/dist/core/retention.d.ts +18 -7
- package/dist/core/retry.d.ts +5 -2
- package/dist/core/rollback.d.ts +23 -12
- package/dist/core/validate.d.ts +33 -2
- package/dist/driver-kit.d.ts +9 -4
- package/dist/driver-kit.js +63 -27
- package/dist/driver-kit.js.map +2 -2
- package/dist/drivers/_shared/object-registry.d.ts +24 -1
- package/dist/drivers/_shared/options.d.ts +15 -7
- package/dist/drivers/backends.d.ts +19 -2
- package/dist/drivers/crypto.d.ts +18 -3
- package/dist/export/index.d.ts +25 -10
- package/dist/index.d.ts +19 -17
- package/dist/index.js +2457 -2283
- package/dist/index.js.map +4 -4
- package/dist/node.d.ts +14 -0
- package/dist/node.js +1064 -0
- package/dist/node.js.map +7 -0
- package/package.json +16 -2
- package/dist/chunk-IGLIZBR7.js.map +0 -7
- package/dist/core/absent-operands.d.ts +0 -8
- package/dist/core/bulk-load.d.ts +0 -148
- package/dist/core/cas-retry.d.ts +0 -27
- package/dist/core/chunk-checks.d.ts +0 -39
- package/dist/core/chunk-stream.d.ts +0 -22
- package/dist/core/chunk-window.d.ts +0 -37
- package/dist/core/combine-ledger.d.ts +0 -43
- package/dist/core/crbm/crc32c.d.ts +0 -10
- package/dist/core/crbm/fingerprint.d.ts +0 -23
- package/dist/core/crbm/format.d.ts +0 -150
- package/dist/core/crbm/frame.d.ts +0 -10
- package/dist/core/crbm/plan-reads.d.ts +0 -50
- package/dist/core/crbm/varint.d.ts +0 -14
- package/dist/core/crbm/writer.d.ts +0 -72
- package/dist/core/crbm-open.d.ts +0 -154
- package/dist/core/crbm-write.d.ts +0 -75
- package/dist/core/due-index.d.ts +0 -53
- package/dist/core/generation-gc.d.ts +0 -293
- package/dist/core/generation-list.d.ts +0 -11
- package/dist/core/id-window.d.ts +0 -46
- package/dist/core/invalidation-scope.d.ts +0 -77
- package/dist/core/item-pull.d.ts +0 -29
- package/dist/core/known-generations-shape.d.ts +0 -13
- package/dist/core/known-generations.d.ts +0 -147
- package/dist/core/ledger-seed.d.ts +0 -17
- package/dist/core/metadata.d.ts +0 -42
- package/dist/core/order.d.ts +0 -2
- package/dist/core/publish.d.ts +0 -207
- package/dist/core/reader-defaults.d.ts +0 -29
- package/dist/core/summary.d.ts +0 -110
- package/dist/core/token.d.ts +0 -41
- package/dist/drivers/_shared/entropy.d.ts +0 -5
- package/dist/drivers/_shared/registry.d.ts +0 -107
- package/dist/drivers/localfs/exact-case.d.ts +0 -34
- package/dist/drivers/localfs/fs-util.d.ts +0 -55
- package/dist/drivers/localfs/paths.d.ts +0 -40
- package/dist/drivers/localfs/registry.d.ts +0 -50
- package/dist/drivers/localfs/storage.d.ts +0 -28
- package/dist/drivers/memory.d.ts +0 -64
- package/dist/testing/fuzz-core.d.ts +0 -15
package/dist/core/summary.d.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A generation's summary on its registry row: how a writer builds it, how a reader opens it, and how it is held
|
|
3
|
-
* against the object it describes.
|
|
4
|
-
*
|
|
5
|
-
* The row carries a cached description of the current generation, `{ generation, cardinality, fingerprint, tailBytes,
|
|
6
|
-
* metadata? }`, written by the same compare-and-swap that moves the pointer. The fingerprint names the generation's
|
|
7
|
-
* object, by its size and footer checksum, as `CrbmReader.fingerprint` does, and `tailBytes` is how much of the object's
|
|
8
|
-
* end one open reads. On a cleartext segment the values are in the clear. On an encrypted one the row holds
|
|
9
|
-
* `{ generation, sealed }` instead: the cardinality as a fixed-width little-endian u64, the object's size as a u64 and
|
|
10
|
-
* its footer checksum as a u32, then `tailBytes` as a u32, then the metadata's canonical JSON if there is any, sealed
|
|
11
|
-
* under the segment's data key with the summary scope of the associated data, which binds it to its namespace, segment
|
|
12
|
-
* and generation. The numbers are fixed-width so the ciphertext's length reveals only the metadata's size, never how
|
|
13
|
-
* many digits the count or the size has, and `tailBytes`, which tracks the number of chunks, is sealed for the same
|
|
14
|
-
* reason the size is. Pure: no I/O.
|
|
15
|
-
*
|
|
16
|
-
* The generation's `.crbm` object stays the truth, and the summary is a copy of it. {@link summaryAgrees} is the
|
|
17
|
-
* comparison of one with the other, and the fingerprint is what a reader holds every object it opens against.
|
|
18
|
-
*/
|
|
19
|
-
import type { Aead } from './crypto.js';
|
|
20
|
-
import type { ClearRegistrySummary, GenerationMetadata, RegistryRecord, RegistrySummary, SealedRegistrySummary, SegmentRef } from './ports.js';
|
|
21
|
-
/** A generation holds at most every 32-bit id. */
|
|
22
|
-
export declare const MAX_SUMMARY_CARDINALITY: number;
|
|
23
|
-
/**
|
|
24
|
-
* The most a summary's `tailBytes` can be: the reader's own default caps on what an open fetches, an index of at most
|
|
25
|
-
* 8 MiB, an extension block of at most its cap and its trailer, and the footer. The library's writer cannot reach it. A
|
|
26
|
-
* reader can report more for an object it did not write, since it accepts bytes between the index and the footer. A
|
|
27
|
-
* literal, so the registry validators that use it do not import the format module; a test holds it to the sum of the
|
|
28
|
-
* format's constants.
|
|
29
|
-
*/
|
|
30
|
-
export declare const MAX_TAIL_BYTES = 8392820;
|
|
31
|
-
/** Whether `tailBytes` is one a summary of an object of `size` bytes can carry. */
|
|
32
|
-
export declare function tailBytesFit(tailBytes: unknown, size: number): boolean;
|
|
33
|
-
/**
|
|
34
|
-
* What a summary says about a generation: its id count, and its metadata when it has any. `tailBytes`, when given, is
|
|
35
|
-
* the bytes of the object's end one open reads.
|
|
36
|
-
*/
|
|
37
|
-
export interface GenerationDescription {
|
|
38
|
-
readonly cardinality: number;
|
|
39
|
-
readonly metadata?: GenerationMetadata | undefined;
|
|
40
|
-
readonly tailBytes?: number | undefined;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* What a row's summary says: the generation's description, the fingerprint of the object it describes and the
|
|
44
|
-
* bytes of that object's end one open reads.
|
|
45
|
-
*/
|
|
46
|
-
export interface SummaryDescription extends GenerationDescription {
|
|
47
|
-
readonly fingerprint: string;
|
|
48
|
-
readonly tailBytes: number;
|
|
49
|
-
}
|
|
50
|
-
/** The size of the object a fingerprint names, in bytes: what `stat()` reports from a row's summary. */
|
|
51
|
-
export declare function sizeOfFingerprint(fingerprint: string): number;
|
|
52
|
-
/**
|
|
53
|
-
* The clear summary of a generation on a cleartext segment, naming its object by `fingerprint`. `metadata` is left off
|
|
54
|
-
* when there is none.
|
|
55
|
-
*/
|
|
56
|
-
export declare function clearSummary(generation: number, cardinality: number, fingerprint: string, tailBytes: number, metadata?: GenerationMetadata): ClearRegistrySummary;
|
|
57
|
-
/**
|
|
58
|
-
* The sealed summary of a generation on an encrypted segment: the cardinality as a u64, the object's size as a u64,
|
|
59
|
-
* its footer checksum as a u32 and its `tailBytes` as a u32, then the metadata's canonical JSON, sealed under `aead` and
|
|
60
|
-
* bound to `ref` and `generation`.
|
|
61
|
-
*/
|
|
62
|
-
export declare function sealSummary(aead: Aead, ref: SegmentRef, generation: number, cardinality: number, fingerprint: string, tailBytes: number, metadata?: GenerationMetadata): SealedRegistrySummary;
|
|
63
|
-
/**
|
|
64
|
-
* What a sealed summary says, opened with `aead` under the summary scope of `ref` and the summary's own generation.
|
|
65
|
-
* The bytes are untrusted: a blob that is not base64, is the wrong size, fails its authentication (tampered, or moved
|
|
66
|
-
* from another generation's row), or holds a count outside 0 to 2^32, a size no object can have, or metadata that
|
|
67
|
-
* breaks the rules, is an {@link IntegrityError}.
|
|
68
|
-
*/
|
|
69
|
-
export declare function openSummary(aead: Aead, ref: SegmentRef, summary: SealedRegistrySummary): SummaryDescription;
|
|
70
|
-
/**
|
|
71
|
-
* What a row's summary says of its current generation, or `undefined` when it says nothing a reader may use. It is
|
|
72
|
-
* used only for the generation it names, on an active row, and in the shape the row's keys call for: clear on a row
|
|
73
|
-
* with no wrapped keys, sealed on a row with them. A sealed one is opened with `aead`, and is unusable without it,
|
|
74
|
-
* or when it does not open, so a summary a tamperer moved or rewrote is never believed; a clear one whose fingerprint
|
|
75
|
-
* names no object is unusable too. Never throws for a bad summary: the row's copy is a cache, and a copy that cannot be
|
|
76
|
-
* used is a copy that is not there.
|
|
77
|
-
*/
|
|
78
|
-
export declare function usableSummary(ref: SegmentRef, row: Pick<RegistryRecord, 'summary' | 'status' | 'currentGen' | 'wrappedDeks'>, aead: Aead | undefined): SummaryDescription | undefined;
|
|
79
|
-
/**
|
|
80
|
-
* Whether a summary's description of a generation is what its object holds: the same id count and the same metadata,
|
|
81
|
-
* compared in canonical form, so key order never matters, and the same `tailBytes` when both say one (a cold open sizes
|
|
82
|
-
* its tail read from the row's, so a number the object does not bear out is not believed again). A summary with
|
|
83
|
-
* metadata over an object with none, and the reverse, disagree: whether an encrypted object's metadata block is present
|
|
84
|
-
* is not authenticated (stripping it needs no key), so the row's sealed copy is what a missing block is held against.
|
|
85
|
-
*/
|
|
86
|
-
export declare function summaryAgrees(summary: GenerationDescription, object: GenerationDescription): boolean;
|
|
87
|
-
/**
|
|
88
|
-
* The metadata an erasure carries into its rewrite of a generation: the object's own, except that an object with none
|
|
89
|
-
* whose row's authenticated summary of the same generation has some gets the row's. Whether an encrypted object has a
|
|
90
|
-
* metadata block is not authenticated, so a block stripped from one reads as none, and the sealed summary on the row is
|
|
91
|
-
* the one copy that says there was a block; carrying it keeps the rewrite from erasing the record of what it dropped.
|
|
92
|
-
* Pass `authenticated` only for a summary that was opened (a clear one has no more authority than the block it sits
|
|
93
|
-
* beside). Never refuses. The erasure reaches this with a stripped block only past a fingerprint forged to match the
|
|
94
|
-
* row's, since stripping the block changes the object's size and the open is held to the row's fingerprint.
|
|
95
|
-
*/
|
|
96
|
-
export declare function metadataToCarry(object: GenerationMetadata | undefined, authenticated: GenerationDescription | undefined): GenerationMetadata | undefined;
|
|
97
|
-
/**
|
|
98
|
-
* The summary of a generation of `ref`, describing the object `description.fingerprint` names, clear or sealed under
|
|
99
|
-
* `aead` as the segment's keys call for. `undefined` when the object's `tailBytes` is above {@link MAX_TAIL_BYTES}: a
|
|
100
|
-
* reader accepts bytes between an object's index and its footer, so an object this library did not write can report a
|
|
101
|
-
* tail the row cannot record, and a row is legal without a summary (a read then opens the object with the default tail).
|
|
102
|
-
* The description's size, footer checksum and `tailBytes` must all come from the one object.
|
|
103
|
-
*/
|
|
104
|
-
export declare function summaryOf(ref: SegmentRef, generation: number, description: SummaryDescription, aead: Aead | undefined): RegistrySummary | undefined;
|
|
105
|
-
/**
|
|
106
|
-
* {@link summaryOf} for an object this library wrote, whose tail is within {@link MAX_TAIL_BYTES} by construction (its
|
|
107
|
-
* index holds at most 65,536 entries and its extension block at most 4 KiB). Throws {@link IntegrityError} for a tail
|
|
108
|
-
* that is not, which the writer cannot produce.
|
|
109
|
-
*/
|
|
110
|
-
export declare function ownSummary(ref: SegmentRef, generation: number, description: SummaryDescription, aead: Aead | undefined): RegistrySummary;
|
package/dist/core/token.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The registry token's incarnation: the part of a token that says which incarnation of a name a row is.
|
|
3
|
-
*
|
|
4
|
-
* A shipped registry gives a row it creates a token `<incarnation>.<counter>.<write>`: 32 lowercase hex digits drawn
|
|
5
|
-
* from injected entropy when the row is created, a decimal counter, and 16 hex digits drawn for each write. Every write
|
|
6
|
-
* of the row keeps the incarnation, and a name created again draws a new one, so two incarnations are told apart by it
|
|
7
|
-
* even when their rows were created in the same millisecond or one is gone. A registry of someone else's issues tokens
|
|
8
|
-
* in any form, and one whose tokens carry no incarnation id is told apart only by the row's creation stamp.
|
|
9
|
-
*
|
|
10
|
-
* This lives in `core/` so the publish can read it, and the registry drivers, which build the tokens, import it from
|
|
11
|
-
* here: the form has one definition.
|
|
12
|
-
*/
|
|
13
|
-
import type { Token } from './ports.js';
|
|
14
|
-
/** An incarnation-form token: the incarnation id, the counter and a write part, `.`-separated, in lowercase hex. */
|
|
15
|
-
export declare const INCARNATION_TOKEN: RegExp;
|
|
16
|
-
/**
|
|
17
|
-
* The incarnation id in a token, or `undefined` for one that has none: a token a registry of someone else's issues in
|
|
18
|
-
* another form. Two tokens with the same incarnation are writes of one incarnation of a row.
|
|
19
|
-
*/
|
|
20
|
-
export declare function incarnationOf(token: Token): string | undefined;
|
|
21
|
-
/** An audit event's `incarnation`, from the token of the row it is about: absent for a token that carries none. */
|
|
22
|
-
export declare function incarnationField(token: Token | undefined): {
|
|
23
|
-
readonly incarnation?: string;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Whether two reads of one name's row are one incarnation of it.
|
|
27
|
-
*
|
|
28
|
-
* - Both tokens carry an incarnation id: the ids decide, and the clock stamps do not enter.
|
|
29
|
-
* - Only one does: different incarnations. A registry gives its rows one form of token, so a row without an id never
|
|
30
|
-
* becomes a row with one, nor the reverse.
|
|
31
|
-
* - Neither does (a registry of someone else's whose tokens carry none): there is no id to compare, and the creation
|
|
32
|
-
* stamp is the only signal left. It is a clock reading, so `landedHere` also checks the object under the number by
|
|
33
|
-
* its footer, and `onlyLeasesDiffer` also requires every field but the leases to match.
|
|
34
|
-
*/
|
|
35
|
-
export declare function sameIncarnation(a: {
|
|
36
|
-
readonly token: Token;
|
|
37
|
-
readonly createdAt: number;
|
|
38
|
-
}, b: {
|
|
39
|
-
readonly token: Token;
|
|
40
|
-
readonly createdAt: number;
|
|
41
|
-
}): boolean;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Entropy } from '../../core/determinism.js';
|
|
2
|
-
/** Draws from the platform's Web Crypto; `UnsupportedError` on a runtime without it. */
|
|
3
|
-
export declare const webCryptoEntropy: Entropy;
|
|
4
|
-
/** Whether `entropy` can draw here: an injected source always, the default only where Web Crypto exists. */
|
|
5
|
-
export declare function entropyIsAvailable(entropy: Entropy): boolean;
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import type { Entropy } from '../../core/determinism.js';
|
|
2
|
-
import { incarnationOf } from '../../core/token.js';
|
|
3
|
-
import { usableKnownGens } from '../../core/known-generations.js';
|
|
4
|
-
import type { NewRegistryRecord, RegistryPatch, RegistryRecord, SegmentRef, Token } from '../../core/ports.js';
|
|
5
|
-
/**
|
|
6
|
-
* The fields a stored record may carry: {@link RegistryRecord}'s, and nothing else. A field no reader resolves
|
|
7
|
-
* through is refused on read-back like any other corruption (invariant 5), rather than ignored. A field the record
|
|
8
|
-
* gains is listed here with its validator; the registry test fails to compile when this list and the record disagree.
|
|
9
|
-
*/
|
|
10
|
-
export declare const RECORD_FIELDS: readonly ["namespace", "segment", "currentGen", "wrappedDeks", "keyId", "status", "retention", "residency", "summary", "createdAt", "updatedAt", "token", "knownGens", "leases", "pointerId"];
|
|
11
|
-
/**
|
|
12
|
-
* Validate the caller-settable fields at `create`, and return the record to store: the caller's, with its summary
|
|
13
|
-
* replaced by the checked, frozen copy {@link validateSummary} builds.
|
|
14
|
-
*/
|
|
15
|
-
export declare function validateNewRegistryRecord(rec: NewRegistryRecord): NewRegistryRecord;
|
|
16
|
-
/**
|
|
17
|
-
* Validate the caller-settable fields in a `compareAndSwap` patch, and return the patch to apply: the caller's, with
|
|
18
|
-
* its summary replaced by the checked, frozen copy. That a given `summary` names the row's resulting `currentGen`,
|
|
19
|
-
* and agrees with its resulting keys, needs the stored row, so {@link applyRegistryPatch} checks both.
|
|
20
|
-
*/
|
|
21
|
-
export declare function validateRegistryPatch(patch: RegistryPatch): RegistryPatch;
|
|
22
|
-
/**
|
|
23
|
-
* The persisted envelope for a registry row: the record plus a tombstone flag. A **deleted** row keeps its
|
|
24
|
-
* record (with an advanced counter) rather than being removed, so a re-create carries the counter on and the two
|
|
25
|
-
* incarnations' tokens are apart by construction, as well as by their random parts — ABA-safety. A row removed by a
|
|
26
|
-
* conditional delete leaves no counter to carry on, so a re-create starts at 0 and only the random parts keep its
|
|
27
|
-
* tokens apart from the old ones. Shared by the
|
|
28
|
-
* persistent drivers (LocalFs file, S3 object).
|
|
29
|
-
*
|
|
30
|
-
* `schemaVersion` is a wire-only concern — it stamps the persisted bytes, not the in-memory domain object —
|
|
31
|
-
* so it lives on the serialize/parse boundary ({@link serializeRegistryEnvelope} /
|
|
32
|
-
* {@link parseRegistryEnvelope}), never on this interface.
|
|
33
|
-
*/
|
|
34
|
-
export interface RegistryEnvelope {
|
|
35
|
-
readonly deleted: boolean;
|
|
36
|
-
readonly record: RegistryRecord;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Current registry-row schema version — a pre-1.0 format-freeze prerequisite. Every persistent
|
|
40
|
-
* registry driver — every one of which persists the `{ deleted, record }` envelope — stamps its rows
|
|
41
|
-
* with this so a reader can fail-closed on a layout it does not read instead of misparsing it. Bump only
|
|
42
|
-
* on a backward-incompatible change. Policy: any stamp but this one → `UnsupportedError` (fail-closed); an
|
|
43
|
-
* **absent** or malformed stamp → `IntegrityError`, since every row this build writes has one.
|
|
44
|
-
*
|
|
45
|
-
* Schema 5 is the record with `knownGens`, the ledger of the generations a row made current (each with its object's footer
|
|
46
|
-
* checksum, in the order they were made current), and a summary that records `tailBytes`. This build reads schema 5 and
|
|
47
|
-
* nothing else: a row stamped lower carries no ledger and, below 4, no `pointerId` either, so nothing could say which
|
|
48
|
-
* generations the row published or which write set what it resolves to.
|
|
49
|
-
*/
|
|
50
|
-
export declare const REGISTRY_SCHEMA_VERSION = 5;
|
|
51
|
-
/**
|
|
52
|
-
* Validate a persisted registry row's `schemaVersion` (untrusted bytes, invariant 5) and return it: an absent or
|
|
53
|
-
* malformed value → `IntegrityError`; any version but {@link REGISTRY_SCHEMA_VERSION} → `UnsupportedError`
|
|
54
|
-
* (fail-closed rather than misread a format this build does not read), naming what to do for an earlier one. The steps
|
|
55
|
-
* are in the changelog's entry for the release that introduced the schema; the message names that entry and no anchor,
|
|
56
|
-
* which cannot go stale.
|
|
57
|
-
*/
|
|
58
|
-
export declare function assertRegistrySchemaVersion(raw: unknown, ctx: string): number;
|
|
59
|
-
/** Serialize a registry envelope for persistence, stamping the current {@link REGISTRY_SCHEMA_VERSION}. */
|
|
60
|
-
export declare function serializeRegistryEnvelope(env: RegistryEnvelope): string;
|
|
61
|
-
export { usableKnownGens };
|
|
62
|
-
export { incarnationOf };
|
|
63
|
-
/** A fresh incarnation id from `entropy`: 128 bits as 32 lowercase hex digits. */
|
|
64
|
-
export declare function drawIncarnation(entropy: Entropy): string;
|
|
65
|
-
/** A fresh write part from `entropy`: 64 bits as 16 lowercase hex digits. */
|
|
66
|
-
export declare function drawWrite(entropy: Entropy): string;
|
|
67
|
-
/**
|
|
68
|
-
* The token for the write after `record`'s: the same incarnation, the counter one on, and a fresh write part. For a
|
|
69
|
-
* compare-and-swap or a tombstone.
|
|
70
|
-
*/
|
|
71
|
-
export declare function nextRegistryToken(record: RegistryRecord, entropy: Entropy): Token;
|
|
72
|
-
/**
|
|
73
|
-
* The token of a new row: a fresh incarnation and write part from `entropy`, and a counter that continues
|
|
74
|
-
* `tombstone`'s when the row is created over one, else starts at 0.
|
|
75
|
-
*/
|
|
76
|
-
export declare function newIncarnationToken(entropy: Entropy, tombstone: RegistryRecord | undefined): Token;
|
|
77
|
-
/**
|
|
78
|
-
* Parse + structurally validate a persisted `{ deleted, record }` envelope from stored bytes. A published row
|
|
79
|
-
* is always whole (atomic write), so a parse failure or a missing/mistyped field means corruption/tampering —
|
|
80
|
-
* fail fast (invariant 5), never silently report "absent". `ctx` names the source (path/key) for the message.
|
|
81
|
-
* `expected` is the segment the row was read under; a row that names another is refused.
|
|
82
|
-
*/
|
|
83
|
-
export declare function parseRegistryEnvelope(text: string, ctx: string, expected?: SegmentRef): RegistryEnvelope;
|
|
84
|
-
/**
|
|
85
|
-
* Build a full record from a {@link NewRegistryRecord} plus identity, audit timestamps, and an OCC token. The create
|
|
86
|
-
* sets what the row resolves to, so its `pointerId` is its own token.
|
|
87
|
-
*/
|
|
88
|
-
export declare function recordFromNew(ref: SegmentRef, rec: NewRegistryRecord, now: number, token: Token): RegistryRecord;
|
|
89
|
-
/**
|
|
90
|
-
* Fail-fast structural check on a record read back from a persistent tier (untrusted bytes, invariant 5):
|
|
91
|
-
* a published row is always whole, so a missing/mistyped required field means corruption/tampering — reject
|
|
92
|
-
* it rather than silently report "absent". The form of `token` and `pointerId`, and how the two relate, is checked by
|
|
93
|
-
* the caller.
|
|
94
|
-
*/
|
|
95
|
-
export declare function assertStoredRecordShape(r: Record<string, unknown>, ctx: string): void;
|
|
96
|
-
/**
|
|
97
|
-
* Apply a patch to an existing record, returning a new one with a fresh `updatedAt` + `token` (identity and
|
|
98
|
-
* `createdAt` are preserved). Optional fields use `'k' in patch` so a patch can *clear* them (set to
|
|
99
|
-
* `undefined`, e.g. dropping `keyId` on crypto-shred); required fields use `??` (they always have a value). A patch
|
|
100
|
-
* that names a resolved field ({@link renewsPointer}) sets `pointerId` to this write's token; any other keeps it.
|
|
101
|
-
*
|
|
102
|
-
* `summary` describes the current generation, so it follows the pointer and the keys: a patch that moves
|
|
103
|
-
* `currentGen`, or changes `wrappedDeks` so the summary's shape no longer agrees, without mentioning `summary` drops
|
|
104
|
-
* the old one; and a summary the patch gives must name the resulting `currentGen` and agree with the resulting keys
|
|
105
|
-
* (`ValidationError`, before anything is written).
|
|
106
|
-
*/
|
|
107
|
-
export declare function applyRegistryPatch(prev: RegistryRecord, patch: RegistryPatch, now: number, token: Token): RegistryRecord;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The index of the first component of `actual` (the path as it is on disk, relative to the root) that equals the same
|
|
3
|
-
* component of `expected` ignoring case but not exactly, or `-1` when there is none. A component that is a different
|
|
4
|
-
* name altogether, or a path that is longer or shorter, is not a case difference.
|
|
5
|
-
*/
|
|
6
|
-
export declare function firstCaseDifference(expected: readonly string[], actual: readonly string[]): number;
|
|
7
|
-
export declare class ExactCase {
|
|
8
|
-
private readonly root;
|
|
9
|
-
/** `undefined` until the root holds an entry to try; then fixed, as a filesystem does not change its mind. */
|
|
10
|
-
private insensitive;
|
|
11
|
-
constructor(root: string);
|
|
12
|
-
/**
|
|
13
|
-
* Whether the root's filesystem folds case, learned from an entry already in the root: the entry's name with its
|
|
14
|
-
* case swapped is the same file or it is not. An empty or absent root holds nothing to alias, so it is not
|
|
15
|
-
* insensitive yet, and is looked at again on the next call.
|
|
16
|
-
*/
|
|
17
|
-
private foldsCase;
|
|
18
|
-
/**
|
|
19
|
-
* The same question asked of the root's own name, or the nearest existing directory above it, when the root holds
|
|
20
|
-
* nothing to try: a root with no entries has none to alias yet, but two names created at once still would. Not
|
|
21
|
-
* remembered, since it is the volume above the root that answers.
|
|
22
|
-
*/
|
|
23
|
-
private foldsAtAncestor;
|
|
24
|
-
/** Whether the root's filesystem folds case: names that differ only by case are one file there. */
|
|
25
|
-
folds(): Promise<boolean>;
|
|
26
|
-
/**
|
|
27
|
-
* `true` when `path` (inside the root) is, on disk, a path whose name differs only by case from the one given:
|
|
28
|
-
* the deepest part of it that exists is resolved to its real name and compared exactly. A path that does not exist
|
|
29
|
-
* yet, or that nothing differs from, is `false`.
|
|
30
|
-
*/
|
|
31
|
-
differs(path: string): Promise<boolean>;
|
|
32
|
-
/** Refuse a write to `path` when its real name differs from it only by case. */
|
|
33
|
-
refuseVariant(path: string): Promise<void>;
|
|
34
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
-
/** True when `err` is a Node system error with the given `code` (e.g. `'ENOENT'`, `'EEXIST'`). */
|
|
3
|
-
export declare const isCode: (err: unknown, code: string) => boolean;
|
|
4
|
-
/**
|
|
5
|
-
* True when `err` says nothing is at the path: it is not there, a parent is not a directory, or the name is longer than
|
|
6
|
-
* the filesystem allows, so no such object can exist.
|
|
7
|
-
*/
|
|
8
|
-
export declare const isAbsent: (err: unknown) => boolean;
|
|
9
|
-
export declare const isFsTransient: (err: unknown) => boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Reclassify a transient filesystem fault as a retryable {@link TransientError}, so the store's read retry can
|
|
12
|
-
* ride it out and a write's caller can tell it from a deterministic failure; everything else — including the
|
|
13
|
-
* driver's own typed errors — propagates unchanged.
|
|
14
|
-
*/
|
|
15
|
-
export declare const mapFsError: (err: unknown) => unknown;
|
|
16
|
-
/**
|
|
17
|
-
* `O_NOFOLLOW` where the platform supports it (absent on Windows → 0). OR it into an `open` flag set so a
|
|
18
|
-
* symlink **at the final path component** is refused rather than followed outside the storage root.
|
|
19
|
-
* Containment of symlinked *directory* components assumes the root itself is not attacker-writable.
|
|
20
|
-
*/
|
|
21
|
-
export declare const O_NOFOLLOW: number;
|
|
22
|
-
/**
|
|
23
|
-
* The modes LocalFs creates what it stores with: files readable and writable by their owner alone, directories
|
|
24
|
-
* searchable by it alone, so another user of the machine cannot read or list a segment. A process umask can only
|
|
25
|
-
* remove bits from these, never add any. On Windows Node ignores a mode apart from the read-only bit, so passing
|
|
26
|
-
* them there changes nothing.
|
|
27
|
-
*/
|
|
28
|
-
export declare const FILE_MODE = 384;
|
|
29
|
-
/**
|
|
30
|
-
* Best-effort parent-directory fsync, so a just-published `rename`/`link` survives a crash. Without it the
|
|
31
|
-
* directory entry can still be in the page cache when power goes: the generation's bytes are durable and
|
|
32
|
-
* the name pointing at them is not, which is the one way a published generation can come back missing.
|
|
33
|
-
*/
|
|
34
|
-
export declare function fsyncDir(dir: string): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Write every byte of `bytes` at the handle's position. One `write` can write fewer bytes than it was asked to and not
|
|
37
|
-
* fail (a full disk, a quota, some network filesystems), and a caller that took it for the whole would store a torn row
|
|
38
|
-
* or object as a whole one. A write that makes no progress is a full device.
|
|
39
|
-
*/
|
|
40
|
-
export declare function writeAll(handle: FileHandle, bytes: Uint8Array): Promise<void>;
|
|
41
|
-
/**
|
|
42
|
-
* Create `dir` and the directories missing above it, private from `top` down: what is missing above `top` is created
|
|
43
|
-
* with the default mode (another service may need to traverse it), `top` and everything below it {@link DIR_MODE}. A
|
|
44
|
-
* directory that exists keeps its mode.
|
|
45
|
-
*/
|
|
46
|
-
export declare function makeDirs(dir: string, top: string): Promise<void>;
|
|
47
|
-
/** The name of a temp file beside `finalPath`: `<finalPath>.<uuid>.tmp`. The only names {@link sweepOrphanTemps} removes. */
|
|
48
|
-
export declare const tempPathFor: (finalPath: string) => string;
|
|
49
|
-
/**
|
|
50
|
-
* Remove the temp files in `dir` named by {@link tempPathFor} that were last written more than a day ago, streaming the
|
|
51
|
-
* directory, and at most once an hour per directory in this process. Only regular files go (a symlink is never followed
|
|
52
|
-
* or removed). Best effort: a failure leaves the files for a later sweep and is never raised. Callers start it without
|
|
53
|
-
* awaiting it.
|
|
54
|
-
*/
|
|
55
|
-
export declare function sweepOrphanTemps(dir: string, now?: number): Promise<void>;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { GenKey, SegmentRef } from '../../core/ports.js';
|
|
2
|
-
/**
|
|
3
|
-
* Refuse, before anything is written, a generation whose namespace directory or temp file name (the object's name and
|
|
4
|
-
* the temp suffix) would be longer than a filesystem allows. A name inside the library's own cap can still be.
|
|
5
|
-
*/
|
|
6
|
-
export declare function assertStorageNamesFit(key: GenKey): void;
|
|
7
|
-
/** The same refusal for a registry row: the namespace directory, and the row file with the temp suffix. */
|
|
8
|
-
export declare function assertRegistryNamesFit(ref: SegmentRef): void;
|
|
9
|
-
/** Directory holding all of a namespace's segment objects. */
|
|
10
|
-
export declare function segmentsDir(root: string, ref: SegmentRef): string;
|
|
11
|
-
/** Absolute path of one `.crbm` generation object. */
|
|
12
|
-
export declare function storageObjectPath(root: string, key: GenKey): string;
|
|
13
|
-
/** Filename pattern for a segment's generations: `<segment>.<gen>.crbm`. */
|
|
14
|
-
export declare function storageObjectFilename(segment: string, generation: number): string;
|
|
15
|
-
/** Parse a generation number out of a `<segment>.<gen>.crbm` filename, or `null` if it doesn't match. */
|
|
16
|
-
export declare function parseGeneration(segment: string, filename: string): number | null;
|
|
17
|
-
/** Directory holding a namespace's registry rows (one file per segment). */
|
|
18
|
-
export declare function registryDir(root: string, namespace: string | undefined): string;
|
|
19
|
-
/** Absolute path of one segment's registry row file: `<ns>/registry/<segment>.reg`. */
|
|
20
|
-
export declare function registryRowPath(root: string, ref: SegmentRef): string;
|
|
21
|
-
/**
|
|
22
|
-
* Parse a segment name out of a `<segment>.reg` filename, or `null` if it doesn't match — including any
|
|
23
|
-
* file whose stem isn't a valid segment name. A strict parser means a stray or planted `.reg` file makes
|
|
24
|
-
* `list()` *skip* it, never abort the whole enumeration on a boundary throw.
|
|
25
|
-
*/
|
|
26
|
-
export declare function parseRegistryRow(filename: string): string | null;
|
|
27
|
-
/**
|
|
28
|
-
* Parse a namespace out of a directory name under the storage root, or `null` if it was not written by us.
|
|
29
|
-
*
|
|
30
|
-
* The inverse of the `namespacePathPart(ns)` that {@link segmentsDir} and {@link registryDir}
|
|
31
|
-
* write. A fleet-wide scan enumerates these directories, so getting it wrong does not fail one segment — it
|
|
32
|
-
* aborts the whole enumeration, taking the consistency check, the retention sweep and subject erasure with it.
|
|
33
|
-
*
|
|
34
|
-
* Like {@link parseRegistryRow} this requires the encoding to **round-trip**, so a planted literal `tenant:acme`
|
|
35
|
-
* directory sitting next to the driver's own `tenant%3Aacme` is skipped rather than reported as a namespace
|
|
36
|
-
* whose rows then resolve to the other directory. `_default` maps back to "no namespace".
|
|
37
|
-
*/
|
|
38
|
-
export declare function parseNamespaceDir(entry: string): {
|
|
39
|
-
namespace: string | undefined;
|
|
40
|
-
} | null;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { Entropy } from '../../core/determinism.js';
|
|
2
|
-
import type { IRegistryDriver, NewRegistryRecord, RegCaps, RegistryPatch, RegistryRecord, RegistryWriteOptions, SegmentRef, Token } from '../../core/ports.js';
|
|
3
|
-
/** Entries in the process-wide row-lock map; the map is empty whenever no registry operation is in flight. */
|
|
4
|
-
export declare function localFsRowLockCount(): number;
|
|
5
|
-
interface LocalFsRegistryDriverOptions {
|
|
6
|
-
/** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now`. */
|
|
7
|
-
readonly now?: () => number;
|
|
8
|
-
/** Draws every token's random parts; defaults to Web Crypto. Inject one only to make a test replayable. */
|
|
9
|
-
readonly entropy?: Entropy;
|
|
10
|
-
/** The directory this root lives under, from which down directories are created private; defaults to the root. */
|
|
11
|
-
readonly privateRoot?: string;
|
|
12
|
-
}
|
|
13
|
-
export declare class LocalFsRegistryDriver implements IRegistryDriver {
|
|
14
|
-
private readonly root;
|
|
15
|
-
private readonly options;
|
|
16
|
-
private readonly now;
|
|
17
|
-
private readonly entropy;
|
|
18
|
-
private readonly exactCase;
|
|
19
|
-
constructor(root: string, options?: LocalFsRegistryDriverOptions);
|
|
20
|
-
/**
|
|
21
|
-
* `conditionalDelete`: a delete reads the row, checks it, and unlinks it under the row's lock, which every writer on
|
|
22
|
-
* the root in this process takes, so no write can land between the check and the unlink.
|
|
23
|
-
*/
|
|
24
|
-
capabilities(): RegCaps;
|
|
25
|
-
/** Every write reports its token to `onToken` before the row's file is written. */
|
|
26
|
-
get reportsWriteTokens(): true;
|
|
27
|
-
get(ref: SegmentRef): Promise<RegistryRecord | null>;
|
|
28
|
-
create(ref: SegmentRef, record: NewRegistryRecord, options?: RegistryWriteOptions): Promise<{
|
|
29
|
-
token: Token;
|
|
30
|
-
}>;
|
|
31
|
-
/**
|
|
32
|
-
* A caller's `held` row (see `RegistryWriteOptions`) has no read to spare here: the row's file is read under its lock and its token compared
|
|
33
|
-
* there, a local read and no request, so a stale one fails exactly as a lost race does.
|
|
34
|
-
*/
|
|
35
|
-
compareAndSwap(ref: SegmentRef, expected: Token, patch: RegistryPatch, options?: RegistryWriteOptions): Promise<{
|
|
36
|
-
token: Token;
|
|
37
|
-
}>;
|
|
38
|
-
list(namespace?: string): AsyncIterable<RegistryRecord>;
|
|
39
|
-
delete(ref: SegmentRef, expected?: Token): Promise<void>;
|
|
40
|
-
/** Remove a row's file and make the removal durable (fsync the directory). */
|
|
41
|
-
private unlinkRow;
|
|
42
|
-
/** Namespaces to scan: just the one requested, or every namespace dir under the root. */
|
|
43
|
-
private namespaceDirs;
|
|
44
|
-
/** Read the row at `path`, which must be the row of `ref`: one that names another segment is refused. */
|
|
45
|
-
private readRow;
|
|
46
|
-
private writeRow;
|
|
47
|
-
/** Serialize callbacks for a row so read-modify-write is atomic across every instance in the process. */
|
|
48
|
-
private withRowLock;
|
|
49
|
-
}
|
|
50
|
-
export {};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type BlobSink } from '../../core/blob.js';
|
|
2
|
-
import type { StorageCaps, GenKey, IStorageDriver, SegmentRef, StorageDeleteOptions, TailRead } from '../../core/ports.js';
|
|
3
|
-
export declare class LocalFsStorageDriver implements IStorageDriver {
|
|
4
|
-
private readonly root;
|
|
5
|
-
private readonly options;
|
|
6
|
-
private readonly exactCase;
|
|
7
|
-
/**
|
|
8
|
-
* `options.privateRoot` is the directory this one lives under, from which down directories are created private; it
|
|
9
|
-
* defaults to `root`.
|
|
10
|
-
*/
|
|
11
|
-
constructor(root: string, options?: {
|
|
12
|
-
readonly privateRoot?: string;
|
|
13
|
-
});
|
|
14
|
-
capabilities(): StorageCaps;
|
|
15
|
-
putImmutable(key: GenKey, write: (sink: BlobSink) => Promise<void>): Promise<{
|
|
16
|
-
size: number;
|
|
17
|
-
sha256: string;
|
|
18
|
-
}>;
|
|
19
|
-
getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array>;
|
|
20
|
-
getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
|
|
21
|
-
/**
|
|
22
|
-
* Idempotent. Ignores `options.ifVersion`, as `conditionalDelete: false` says: see the note on the class for why it
|
|
23
|
-
* cannot apply one. The parameter is declared so that a wrapper of this driver can hand the port's options on.
|
|
24
|
-
*/
|
|
25
|
-
delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
|
|
26
|
-
list(ref: SegmentRef): AsyncIterable<GenKey>;
|
|
27
|
-
private openRead;
|
|
28
|
-
}
|
package/dist/drivers/memory.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { BlobSink } from '../core/blob.js';
|
|
2
|
-
import type { Entropy } from '../core/determinism.js';
|
|
3
|
-
import type { StorageCaps, StorageDeleteOptions, TailRead, GenKey, IStorageDriver, IRegistryDriver, NewRegistryRecord, RegCaps, RegistryPatch, RegistryRecord, RegistryWriteOptions, SegmentRef, Token } from '../core/ports.js';
|
|
4
|
-
interface MemoryRegistryDriverOptions {
|
|
5
|
-
/** Injected clock for `createdAt`/`updatedAt`; defaults to `Date.now` (drivers may use ambient time). */
|
|
6
|
-
readonly now?: () => number;
|
|
7
|
-
/** Draws every token's random parts; defaults to Web Crypto. Inject one only to make a test replayable. */
|
|
8
|
-
readonly entropy?: Entropy;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* In-memory {@link IRegistryDriver} — one record per segment under OCC. The token has the form every shipped driver
|
|
12
|
-
* gives it, an incarnation id drawn at create, a counter and a per-write random part, but its counter is global to the
|
|
13
|
-
* driver: monotonic and never reused, so a record recreated after `delete` never meets an earlier token, by
|
|
14
|
-
* construction, even though `delete` removes the row physically.
|
|
15
|
-
*/
|
|
16
|
-
export declare class MemoryRegistryDriver implements IRegistryDriver {
|
|
17
|
-
private readonly rows;
|
|
18
|
-
private readonly now;
|
|
19
|
-
private readonly entropy;
|
|
20
|
-
private seq;
|
|
21
|
-
constructor(options?: MemoryRegistryDriverOptions);
|
|
22
|
-
private nextToken;
|
|
23
|
-
/** `conditionalDelete`: a delete checks the token and removes the row in one step, with no await between. */
|
|
24
|
-
capabilities(): RegCaps;
|
|
25
|
-
/** Every write reports its token to `onToken` before it is applied. */
|
|
26
|
-
get reportsWriteTokens(): true;
|
|
27
|
-
get(ref: SegmentRef): Promise<RegistryRecord | null>;
|
|
28
|
-
create(ref: SegmentRef, record: NewRegistryRecord, options?: RegistryWriteOptions): Promise<{
|
|
29
|
-
token: Token;
|
|
30
|
-
}>;
|
|
31
|
-
/**
|
|
32
|
-
* A caller's `held` row (see `RegistryWriteOptions`) has no read to spare here: the token is compared with the stored row in the same step
|
|
33
|
-
* as the write, in memory, so a stale one fails exactly as a lost race does.
|
|
34
|
-
*/
|
|
35
|
-
compareAndSwap(ref: SegmentRef, expected: Token, patch: RegistryPatch, options?: RegistryWriteOptions): Promise<{
|
|
36
|
-
token: Token;
|
|
37
|
-
}>;
|
|
38
|
-
list(namespace?: string): AsyncIterable<RegistryRecord>;
|
|
39
|
-
delete(ref: SegmentRef, expected?: Token): Promise<void>;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* In-memory {@link IStorageDriver} — write-once immutable generation objects as opaque bytes. A "dumb byte
|
|
43
|
-
* mover" (understands neither roaring nor `.crbm`), so it's a faithful storage backend for tests and a zero-setup
|
|
44
|
-
* local storage tier. Mirrors the LocalFs/S3 contract (write-once, range/tail reads).
|
|
45
|
-
*
|
|
46
|
-
* Every object stored gets a version from a counter the driver never reuses, so two objects stored one after the other
|
|
47
|
-
* under one key never share one, whatever their bytes. A delete given `ifVersion` compares it with the stored object's
|
|
48
|
-
* and removes it in the same step, with no await between them: `conditionalDelete` is `true`.
|
|
49
|
-
*/
|
|
50
|
-
export declare class MemoryStorageDriver implements IStorageDriver {
|
|
51
|
-
private readonly objects;
|
|
52
|
-
private stored;
|
|
53
|
-
capabilities(): StorageCaps;
|
|
54
|
-
putImmutable(key: GenKey, write: (sink: BlobSink) => Promise<void>): Promise<{
|
|
55
|
-
size: number;
|
|
56
|
-
sha256: string;
|
|
57
|
-
}>;
|
|
58
|
-
getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array>;
|
|
59
|
-
getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
|
|
60
|
-
delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
|
|
61
|
-
list(ref: SegmentRef): AsyncIterable<GenKey>;
|
|
62
|
-
private require;
|
|
63
|
-
}
|
|
64
|
-
export {};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal surface for the coverage-guided fuzz harness, CORE half.
|
|
3
|
-
*
|
|
4
|
-
* Exposes the untrusted-bytes entry points the fuzz targets drive — including `parseIndex` and `parseExtension`,
|
|
5
|
-
* which are deliberately **NOT public API**. Bundled by its own esbuild entry in `scripts/build.mjs` to the git-ignored repo-root `fuzz/build/`,
|
|
6
|
-
* so it never enters `dist/` or the published package. Keeping it here (rather than widening
|
|
7
|
-
* `packages/core/src/index.ts`) is the point: a test harness must not expand the published surface.
|
|
8
|
-
* The codec half lives in the flavor package (`packages/roaring/src/testing/fuzz-codec.ts`), because a concrete
|
|
9
|
-
* bitmap codec is exactly what core does not have.
|
|
10
|
-
*/
|
|
11
|
-
export { CrbmReader, parseExtension, parseIndex } from '../core/crbm/reader.js';
|
|
12
|
-
export { CrbmWriter } from '../core/crbm/writer.js';
|
|
13
|
-
export { BufferReader, BufferSink } from '../core/blob.js';
|
|
14
|
-
export { CloudRoaringError, IntegrityError, isCloudRoaringError } from '../core/errors.js';
|
|
15
|
-
export { DEFAULT_MAX_BITMAP_BYTES, DEFAULT_MAX_PAYLOAD_BYTES } from '../core/crbm/format.js';
|