@cloudbitmaps/core 0.20.0 → 0.22.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 +101 -5
- package/dist/{chunk-YSC7NJWV.js → chunk-OSU7W2Y3.js} +811 -164
- package/dist/chunk-OSU7W2Y3.js.map +7 -0
- package/dist/core/audit.d.ts +12 -2
- package/dist/core/bit-route.d.ts +9 -1
- package/dist/core/blob.d.ts +12 -1
- package/dist/core/codec.d.ts +1 -1
- package/dist/core/combine-expr.d.ts +10 -8
- package/dist/core/combine-feed.d.ts +12 -18
- package/dist/core/combine-many.d.ts +29 -22
- package/dist/core/concurrency.d.ts +8 -0
- package/dist/core/consistency.d.ts +69 -17
- package/dist/core/crbm/reader.d.ts +52 -6
- package/dist/core/crbm-storage-source.d.ts +65 -452
- package/dist/core/crypto.d.ts +6 -1
- package/dist/core/determinism.d.ts +2 -1
- package/dist/core/engine.d.ts +222 -33
- package/dist/core/erase-id.d.ts +60 -15
- package/dist/core/erasure.d.ts +5 -4
- package/dist/core/errors.d.ts +61 -11
- package/dist/core/leases.d.ts +4 -3
- package/dist/core/load.d.ts +58 -45
- package/dist/core/lru.d.ts +2 -15
- package/dist/core/metrics.d.ts +5 -3
- package/dist/core/pinned-storage-source.d.ts +4 -4
- package/dist/core/ports.d.ts +96 -22
- package/dist/core/registry-scan.d.ts +0 -7
- package/dist/core/retention-sweep.d.ts +70 -66
- package/dist/core/retention.d.ts +2 -2
- package/dist/core/retry.d.ts +15 -18
- package/dist/core/rollback.d.ts +61 -31
- package/dist/core/validate.d.ts +2 -0
- package/dist/driver-kit.d.ts +11 -2
- package/dist/driver-kit.js +84 -16
- package/dist/driver-kit.js.map +3 -3
- package/dist/drivers/_shared/bytes.d.ts +6 -0
- package/dist/drivers/_shared/faults.d.ts +15 -0
- package/dist/drivers/_shared/object-keys.d.ts +18 -0
- package/dist/drivers/_shared/object-registry-keys.d.ts +0 -2
- package/dist/drivers/_shared/object-registry.d.ts +2 -0
- package/dist/drivers/_shared/options.d.ts +19 -0
- package/dist/drivers/backends.d.ts +2 -0
- package/dist/drivers/crypto.d.ts +1 -1
- package/dist/export/index.d.ts +9 -8
- package/dist/index.d.ts +11 -10
- package/dist/index.js +4202 -2833
- package/dist/index.js.map +4 -4
- package/package.json +7 -2
- package/dist/chunk-YSC7NJWV.js.map +0 -7
- package/dist/core/chunk-checks.d.ts +0 -34
- package/dist/core/chunk-stream.d.ts +0 -22
- package/dist/core/chunk-window.d.ts +0 -30
- 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 -18
- package/dist/core/crbm/format.d.ts +0 -151
- package/dist/core/crbm/frame.d.ts +0 -10
- package/dist/core/crbm/plan-reads.d.ts +0 -44
- package/dist/core/crbm/varint.d.ts +0 -13
- package/dist/core/crbm/writer.d.ts +0 -68
- package/dist/core/due-index.d.ts +0 -53
- package/dist/core/generation-gc.d.ts +0 -179
- package/dist/core/item-pull.d.ts +0 -29
- package/dist/core/kept-generations.d.ts +0 -40
- package/dist/core/metadata.d.ts +0 -42
- package/dist/core/reader-defaults.d.ts +0 -29
- package/dist/core/summary.d.ts +0 -78
- 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 -104
- package/dist/drivers/localfs/exact-case.d.ts +0 -37
- package/dist/drivers/localfs/fs-util.d.ts +0 -33
- package/dist/drivers/localfs/paths.d.ts +0 -40
- package/dist/drivers/localfs/registry.d.ts +0 -44
- package/dist/drivers/localfs/storage.d.ts +0 -21
- package/dist/drivers/memory.d.ts +0 -61
- package/dist/testing/fuzz-core.d.ts +0 -15
package/dist/core/item-pull.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pulls the items of an async iterator one at a time so that the code relaying them never holds the one it just
|
|
3
|
-
* handed on. A binding of a generator's body (`for await (const item of …)`, `yield*`) is kept in the generator's
|
|
4
|
-
* frame across its next `yield`, so the relay would hold the previous item, and whatever buffer it is a view into,
|
|
5
|
-
* for as long as it waits for the next. Here the item sits in the pull between {@link advance} and {@link take}, and
|
|
6
|
-
* `take` clears it before it returns.
|
|
7
|
-
*
|
|
8
|
-
* ```ts
|
|
9
|
-
* const pull = new ItemPull(source);
|
|
10
|
-
* try {
|
|
11
|
-
* while (await pull.advance()) yield pull.take((item) => wrap(item));
|
|
12
|
-
* } finally {
|
|
13
|
-
* await pull.close();
|
|
14
|
-
* }
|
|
15
|
-
* ```
|
|
16
|
-
*
|
|
17
|
-
* Internal to core: not exported from any entry point.
|
|
18
|
-
*/
|
|
19
|
-
export declare class ItemPull<A> {
|
|
20
|
-
private readonly iterator;
|
|
21
|
-
private item;
|
|
22
|
-
constructor(source: AsyncIterable<A>);
|
|
23
|
-
/** Waits for the next item and keeps it inside; `false` once the source has ended. A failure of the source throws. */
|
|
24
|
-
advance(): Promise<boolean>;
|
|
25
|
-
/** Hands over the item {@link advance} fetched, through `map`, and forgets it. */
|
|
26
|
-
take<B>(map: (item: A) => B): B;
|
|
27
|
-
/** Ends the source early (a consumer that stopped), as leaving a `for await` does. */
|
|
28
|
-
close(): Promise<void>;
|
|
29
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The generations a segment's row records as kept: the grace window below its pointer, so a load collects what its
|
|
3
|
-
* publish pushed out of the window by name instead of listing the bucket.
|
|
4
|
-
*
|
|
5
|
-
* The record is a cache of what a listing would keep, never an inventory of the bucket. Absent means the row does not
|
|
6
|
-
* know, and the load lists; an empty list means nothing below the pointer is kept. A patch that moves the pointer
|
|
7
|
-
* without naming the list drops it (see `applyRegistryPatch`), so a writer that forgets the list leaves a row that
|
|
8
|
-
* lists, never one that deletes a generation it should keep. Pure: no I/O, time or randomness.
|
|
9
|
-
*/
|
|
10
|
-
import type { RegistryRecord } from './ports.js';
|
|
11
|
-
/**
|
|
12
|
-
* The most generations a writer records. A `keep` above it records none, and the load lists on every publish. A
|
|
13
|
-
* reader accepts up to {@link MAX_STORED_KEPT_GENERATIONS}, so a release can write more than this without a new row
|
|
14
|
-
* schema.
|
|
15
|
-
*/
|
|
16
|
-
export declare const MAX_KEPT_GENERATIONS = 64;
|
|
17
|
-
/** The most entries a stored row may hold: four times what a writer writes. */
|
|
18
|
-
export declare const MAX_STORED_KEPT_GENERATIONS = 256;
|
|
19
|
-
/**
|
|
20
|
-
* The row's list when a collection may rely on it: present, on a row with a pointer, and every entry below that
|
|
21
|
-
* pointer. A well-formed list that disagrees with the pointer is read but not used (the load lists instead), as a
|
|
22
|
-
* summary that names another generation is.
|
|
23
|
-
*/
|
|
24
|
-
export declare function usableKeptGens(record: RegistryRecord | null): readonly number[] | undefined;
|
|
25
|
-
/** What a publish of a generation writes into the row, and which names it pushes out of the window. */
|
|
26
|
-
export interface KeptAfter {
|
|
27
|
-
/** The list to write, or `undefined` to write none (the row then does not know, and the next load lists). */
|
|
28
|
-
readonly list: readonly number[] | undefined;
|
|
29
|
-
/** The generations the window no longer keeps, ascending: the names to delete. */
|
|
30
|
-
readonly evict: readonly number[];
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* The list a publish of `generation` over `prev` writes: the newest `keep` of the row's list and the generation that
|
|
34
|
-
* was current, and the names that fell out of it. Only generations that were current are named, so a number a refused
|
|
35
|
-
* or crashed load took is never in it.
|
|
36
|
-
*
|
|
37
|
-
* No list is written when the row has no usable list to extend (except for generation 0, which has nothing below it),
|
|
38
|
-
* when `keep` is above {@link MAX_KEPT_GENERATIONS}, or when no `keep` is given.
|
|
39
|
-
*/
|
|
40
|
-
export declare function nextKept(prev: RegistryRecord | null, generation: number, keep: number | undefined): KeptAfter;
|
package/dist/core/metadata.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generation metadata: a small, flat record of strings and finite numbers attached to one generation.
|
|
3
|
-
*
|
|
4
|
-
* The rules live here once, for every boundary that meets a value: a caller's input, refused with
|
|
5
|
-
* `ValidationError`, and bytes read back from a tier, refused with `IntegrityError`. The caller passes the error
|
|
6
|
-
* to raise. Pure: no I/O.
|
|
7
|
-
*/
|
|
8
|
-
import type { GenerationMetadata } from './ports.js';
|
|
9
|
-
/** Cap on the canonical JSON of one generation's metadata, in UTF-8 bytes (braces and quotes included). */
|
|
10
|
-
export declare const MAX_METADATA_BYTES = 1024;
|
|
11
|
-
/** Cap on one metadata key, in UTF-8 bytes. */
|
|
12
|
-
export declare const MAX_METADATA_KEY_BYTES = 128;
|
|
13
|
-
/**
|
|
14
|
-
* Validate `value` as generation metadata (`GenerationMetadata` in the ports) and return its canonical JSON: RFC 8785
|
|
15
|
-
* (the JSON Canonicalization Scheme) for a flat object of strings and finite numbers. Keys are sorted by UTF-16 code
|
|
16
|
-
* unit, strings are escaped as `JSON.stringify` escapes them, numbers are written as ECMAScript's
|
|
17
|
-
* `Number.prototype.toString` writes them (so `-0` is `0`, and `1e21` is `1e+21`), and there is no whitespace. Key
|
|
18
|
-
* order therefore never changes the bytes, and `tests/golden/metadata-canonical.json` holds vectors a port can check
|
|
19
|
-
* itself against. Both caps are in UTF-8 bytes. The container is an object whose prototype is
|
|
20
|
-
* `Object.prototype` or `null`, holding only enumerable data properties with string keys. `fail` raises the error the
|
|
21
|
-
* boundary calls for. The empty object passes and returns `{}`: whether it may be stored is the caller's rule.
|
|
22
|
-
*/
|
|
23
|
-
export declare function canonicalMetadataJson(value: unknown, fail: (message: string) => never): string;
|
|
24
|
-
/**
|
|
25
|
-
* The bytes a generation stores for `value`: its canonical JSON in UTF-8, or `undefined` when it has none (`undefined`
|
|
26
|
-
* or the empty object, which store nothing). Synchronous, so what is stored is what the caller passed at the call,
|
|
27
|
-
* whatever it does with its object afterwards. `fail` raises the boundary's error.
|
|
28
|
-
*/
|
|
29
|
-
export declare function metadataBytes(value: unknown, fail: (message: string) => never): Uint8Array | undefined;
|
|
30
|
-
/**
|
|
31
|
-
* A caller's metadata, checked against the rules and copied, so what is stored is what the caller passed at the call,
|
|
32
|
-
* whatever it does with its object afterwards: the frozen record in canonical key order, or `undefined` when there is
|
|
33
|
-
* none (`undefined`, or the empty object, which store nothing). Synchronous. `fail` raises the boundary's error.
|
|
34
|
-
*/
|
|
35
|
-
export declare function copiedMetadata(value: unknown, fail: (message: string) => never): GenerationMetadata | undefined;
|
|
36
|
-
/**
|
|
37
|
-
* Read metadata back from bytes a tier holds, which are untrusted. They must be at most {@link MAX_METADATA_BYTES},
|
|
38
|
-
* valid UTF-8, and exactly the canonical JSON of a non-empty record, so a stored copy has one spelling: whitespace, a
|
|
39
|
-
* key out of order or listed twice, and a number or an escape `JSON.stringify` would write another way are all
|
|
40
|
-
* refused. Returns a frozen record of its own. `fail` raises the boundary's error.
|
|
41
|
-
*/
|
|
42
|
-
export declare function metadataFromBytes(bytes: Uint8Array, fail: (message: string) => never): GenerationMetadata;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The storage reader's defaults. `@cloudbitmaps/tools` prices what two of them cause and keeps its own copies, which
|
|
3
|
-
* `tests/tools/store-defaults.test.ts` holds equal to these.
|
|
4
|
-
*/
|
|
5
|
-
/** Default TTL (ms) for re-resolving a segment's `currentGen` — the bound on post-publish read staleness. */
|
|
6
|
-
export declare const DEFAULT_CURRENT_GEN_TTL_MS = 2000;
|
|
7
|
-
/**
|
|
8
|
-
* How long (ms) a reader keeps serving after a pointer refresh failed transiently, before the next read asks the
|
|
9
|
-
* registry again. Short, so an outage ends the stale serving soon after the registry answers; fixed, so a registry
|
|
10
|
-
* that is down is asked at most once per this interval per segment being read (the refresh is coalesced per
|
|
11
|
-
* segment). Never longer than the TTL itself.
|
|
12
|
-
*/
|
|
13
|
-
export declare const REFRESH_RETRY_MS = 500;
|
|
14
|
-
/** Default ceiling on cached segment readers (each holds a parsed `.crbm` index) — the steady-state count bound. */
|
|
15
|
-
export declare const DEFAULT_MAX_OPEN_SEGMENTS = 1024;
|
|
16
|
-
/** Default aggregate ceiling (bytes) on resident parsed indices in the reader cache — the steady-state byte bound. */
|
|
17
|
-
export declare const DEFAULT_MAX_OPEN_INDEX_BYTES: number;
|
|
18
|
-
/**
|
|
19
|
-
* How many segments' resolutions a store keeps per reader it keeps open: the resolution cache's count bound is this
|
|
20
|
-
* times `cache.readerMax` (8,192 by default). A resolution is the few hundred bytes of a row a read resolves through,
|
|
21
|
-
* kept for `cache.genTtlMs` whether or not the segment's reader is still open, so a store reading more segments at once
|
|
22
|
-
* than its readers hold keeps more of them than it keeps readers.
|
|
23
|
-
*/
|
|
24
|
-
export declare const RESOLUTIONS_PER_OPEN_SEGMENT = 8;
|
|
25
|
-
/**
|
|
26
|
-
* The resolution cache's byte bound is `cache.readerMaxBytes` divided by this (4 MiB by default): what it holds is a
|
|
27
|
-
* sixteenth of what the readers may.
|
|
28
|
-
*/
|
|
29
|
-
export declare const RESOLUTION_BYTES_DIVISOR = 16;
|
package/dist/core/summary.d.ts
DELETED
|
@@ -1,78 +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, metadata? }`,
|
|
6
|
-
* written by the same compare-and-swap that moves the pointer. The fingerprint names the generation's object, by its
|
|
7
|
-
* size and footer checksum, as `CrbmReader.fingerprint` does. On a cleartext segment the values are in the clear. On an
|
|
8
|
-
* encrypted one the row holds `{ generation, sealed }` instead: the cardinality as a fixed-width little-endian u64, the
|
|
9
|
-
* object's size as a u64 and its footer checksum as a u32, then the metadata's canonical JSON if there is any, sealed
|
|
10
|
-
* under the segment's data key with the summary scope of the associated data, which binds it to its namespace, segment
|
|
11
|
-
* and generation. The numbers are fixed-width so the ciphertext's length reveals only the metadata's size, never how
|
|
12
|
-
* many digits the count or the size has. Pure: no I/O.
|
|
13
|
-
*
|
|
14
|
-
* The generation's `.crbm` object stays the truth, and the summary is a copy of it. {@link summaryAgrees} is the
|
|
15
|
-
* comparison of one with the other, and the fingerprint is what a reader holds every object it opens against.
|
|
16
|
-
*/
|
|
17
|
-
import type { Aead } from './crypto.js';
|
|
18
|
-
import type { ClearRegistrySummary, GenerationMetadata, RegistryRecord, RegistrySummary, SealedRegistrySummary, SegmentRef } from './ports.js';
|
|
19
|
-
/** What a summary says about a generation: its id count, and its metadata when it has any. */
|
|
20
|
-
export interface GenerationDescription {
|
|
21
|
-
readonly cardinality: number;
|
|
22
|
-
readonly metadata?: GenerationMetadata | undefined;
|
|
23
|
-
}
|
|
24
|
-
/** What a row's summary says: the generation's description, and the fingerprint of the object it describes. */
|
|
25
|
-
export interface SummaryDescription extends GenerationDescription {
|
|
26
|
-
readonly fingerprint: string;
|
|
27
|
-
}
|
|
28
|
-
/** The size of the object a fingerprint names, in bytes: what `stat()` reports from a row's summary. */
|
|
29
|
-
export declare function sizeOfFingerprint(fingerprint: string): number;
|
|
30
|
-
/**
|
|
31
|
-
* The clear summary of a generation on a cleartext segment, naming its object by `fingerprint`. `metadata` is left off
|
|
32
|
-
* when there is none.
|
|
33
|
-
*/
|
|
34
|
-
export declare function clearSummary(generation: number, cardinality: number, fingerprint: string, metadata?: GenerationMetadata): ClearRegistrySummary;
|
|
35
|
-
/**
|
|
36
|
-
* The sealed summary of a generation on an encrypted segment: the cardinality as a u64, the object's size as a u64 and
|
|
37
|
-
* its footer checksum as a u32, then the metadata's canonical JSON, sealed under `aead` and bound to `ref` and
|
|
38
|
-
* `generation`.
|
|
39
|
-
*/
|
|
40
|
-
export declare function sealSummary(aead: Aead, ref: SegmentRef, generation: number, cardinality: number, fingerprint: string, metadata?: GenerationMetadata): SealedRegistrySummary;
|
|
41
|
-
/**
|
|
42
|
-
* What a sealed summary says, opened with `aead` under the summary scope of `ref` and the summary's own generation.
|
|
43
|
-
* The bytes are untrusted: a blob that is not base64, is the wrong size, fails its authentication (tampered, or moved
|
|
44
|
-
* from another generation's row), or holds a count outside 0 to 2^32, a size no object can have, or metadata that
|
|
45
|
-
* breaks the rules, is an {@link IntegrityError}.
|
|
46
|
-
*/
|
|
47
|
-
export declare function openSummary(aead: Aead, ref: SegmentRef, summary: SealedRegistrySummary): SummaryDescription;
|
|
48
|
-
/**
|
|
49
|
-
* What a row's summary says of its current generation, or `undefined` when it says nothing a reader may use. It is
|
|
50
|
-
* 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
|
|
51
|
-
* with no wrapped keys, sealed on a row with them. A sealed one is opened with `aead`, and is unusable without it,
|
|
52
|
-
* or when it does not open, so a summary a tamperer moved or rewrote is never believed; a clear one whose fingerprint
|
|
53
|
-
* 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
|
|
54
|
-
* used is a copy that is not there.
|
|
55
|
-
*/
|
|
56
|
-
export declare function usableSummary(ref: SegmentRef, row: Pick<RegistryRecord, 'summary' | 'status' | 'currentGen' | 'wrappedDeks'>, aead: Aead | undefined): SummaryDescription | undefined;
|
|
57
|
-
/**
|
|
58
|
-
* Whether a summary's description of a generation is what its object holds: the same id count and the same metadata,
|
|
59
|
-
* compared in canonical form, so key order never matters. A summary with metadata over an object with none, and the
|
|
60
|
-
* reverse, disagree: whether an encrypted object's metadata block is present is not authenticated (stripping it needs
|
|
61
|
-
* no key), so the row's sealed copy is what a missing block is held against.
|
|
62
|
-
*/
|
|
63
|
-
export declare function summaryAgrees(summary: GenerationDescription, object: GenerationDescription): boolean;
|
|
64
|
-
/**
|
|
65
|
-
* The metadata an erasure carries into its rewrite of a generation: the object's own, except that an object with none
|
|
66
|
-
* whose row's authenticated summary of the same generation has some gets the row's. Whether an encrypted object has a
|
|
67
|
-
* metadata block is not authenticated, so a block stripped from one reads as none, and the sealed summary on the row is
|
|
68
|
-
* the one copy that says there was a block; carrying it keeps the rewrite from erasing the record of what it dropped.
|
|
69
|
-
* Pass `authenticated` only for a summary that was opened (a clear one has no more authority than the block it sits
|
|
70
|
-
* beside). Never refuses. The erasure reaches this with a stripped block only past a fingerprint forged to match the
|
|
71
|
-
* row's, since stripping the block changes the object's size and the open is held to the row's fingerprint.
|
|
72
|
-
*/
|
|
73
|
-
export declare function metadataToCarry(object: GenerationMetadata | undefined, authenticated: GenerationDescription | undefined): GenerationMetadata | undefined;
|
|
74
|
-
/**
|
|
75
|
-
* The summary of a generation of `ref`, describing the object `description.fingerprint` names, clear or sealed under
|
|
76
|
-
* `aead` as the segment's keys call for.
|
|
77
|
-
*/
|
|
78
|
-
export declare function summaryOf(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 a caller that acts on this answer proves its own object
|
|
33
|
-
* too.
|
|
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,104 +0,0 @@
|
|
|
1
|
-
import type { Entropy } from '../../core/determinism.js';
|
|
2
|
-
import { incarnationOf } from '../../core/token.js';
|
|
3
|
-
import { usableKeptGens } from '../../core/kept-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 that joins
|
|
8
|
-
* schema 4 before it is released is appended here, with its validator, and needs no new schema version.
|
|
9
|
-
*/
|
|
10
|
-
export declare const RECORD_FIELDS: readonly ["namespace", "segment", "currentGen", "wrappedDeks", "keyId", "status", "retention", "residency", "summary", "createdAt", "updatedAt", "token", "keptGens", "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 4 is the record with `pointerId`, the incarnation-form token and a summary that names its object. This build
|
|
46
|
-
* reads schema 4 and nothing else: a row stamped 1 to 3 carries no `pointerId`, so nothing could say which write set
|
|
47
|
-
* what it resolves to.
|
|
48
|
-
*/
|
|
49
|
-
export declare const REGISTRY_SCHEMA_VERSION = 4;
|
|
50
|
-
/**
|
|
51
|
-
* Validate a persisted registry row's `schemaVersion` (untrusted bytes, invariant 5) and return it: an absent or
|
|
52
|
-
* malformed value → `IntegrityError`; any version but {@link REGISTRY_SCHEMA_VERSION} → `UnsupportedError`
|
|
53
|
-
* (fail-closed rather than misread a format this build does not read), naming what to do for an earlier one.
|
|
54
|
-
*/
|
|
55
|
-
export declare function assertRegistrySchemaVersion(raw: unknown, ctx: string): number;
|
|
56
|
-
/** Serialize a registry envelope for persistence, stamping the current {@link REGISTRY_SCHEMA_VERSION}. */
|
|
57
|
-
export declare function serializeRegistryEnvelope(env: RegistryEnvelope): string;
|
|
58
|
-
export { usableKeptGens };
|
|
59
|
-
export { incarnationOf };
|
|
60
|
-
/** A fresh incarnation id from `entropy`: 128 bits as 32 lowercase hex digits. */
|
|
61
|
-
export declare function drawIncarnation(entropy: Entropy): string;
|
|
62
|
-
/** A fresh write part from `entropy`: 64 bits as 16 lowercase hex digits. */
|
|
63
|
-
export declare function drawWrite(entropy: Entropy): string;
|
|
64
|
-
/**
|
|
65
|
-
* The token for the write after `record`'s: the same incarnation, the counter one on, and a fresh write part. For a
|
|
66
|
-
* compare-and-swap or a tombstone.
|
|
67
|
-
*/
|
|
68
|
-
export declare function nextRegistryToken(record: RegistryRecord, entropy: Entropy): Token;
|
|
69
|
-
/**
|
|
70
|
-
* The token of a new row: a fresh incarnation and write part from `entropy`, and a counter that continues
|
|
71
|
-
* `tombstone`'s when the row is created over one, else starts at 0.
|
|
72
|
-
*/
|
|
73
|
-
export declare function newIncarnationToken(entropy: Entropy, tombstone: RegistryRecord | undefined): Token;
|
|
74
|
-
/**
|
|
75
|
-
* Parse + structurally validate a persisted `{ deleted, record }` envelope from stored bytes. A published row
|
|
76
|
-
* is always whole (atomic write), so a parse failure or a missing/mistyped field means corruption/tampering —
|
|
77
|
-
* fail fast (invariant 5), never silently report "absent". `ctx` names the source (path/key) for the message.
|
|
78
|
-
* `expected` is the segment the row was read under; a row that names another is refused.
|
|
79
|
-
*/
|
|
80
|
-
export declare function parseRegistryEnvelope(text: string, ctx: string, expected?: SegmentRef): RegistryEnvelope;
|
|
81
|
-
/**
|
|
82
|
-
* Build a full record from a {@link NewRegistryRecord} plus identity, audit timestamps, and an OCC token. The create
|
|
83
|
-
* sets what the row resolves to, so its `pointerId` is its own token.
|
|
84
|
-
*/
|
|
85
|
-
export declare function recordFromNew(ref: SegmentRef, rec: NewRegistryRecord, now: number, token: Token): RegistryRecord;
|
|
86
|
-
/**
|
|
87
|
-
* Fail-fast structural check on a record read back from a persistent tier (untrusted bytes, invariant 5):
|
|
88
|
-
* a published row is always whole, so a missing/mistyped required field means corruption/tampering — reject
|
|
89
|
-
* it rather than silently report "absent". The form of `token` and `pointerId`, and how the two relate, is checked by
|
|
90
|
-
* the caller.
|
|
91
|
-
*/
|
|
92
|
-
export declare function assertStoredRecordShape(r: Record<string, unknown>, ctx: string): void;
|
|
93
|
-
/**
|
|
94
|
-
* Apply a patch to an existing record, returning a new one with a fresh `updatedAt` + `token` (identity and
|
|
95
|
-
* `createdAt` are preserved). Optional fields use `'k' in patch` so a patch can *clear* them (set to
|
|
96
|
-
* `undefined`, e.g. dropping `keyId` on crypto-shred); required fields use `??` (they always have a value). A patch
|
|
97
|
-
* that names a resolved field ({@link renewsPointer}) sets `pointerId` to this write's token; any other keeps it.
|
|
98
|
-
*
|
|
99
|
-
* `summary` describes the current generation, so it follows the pointer and the keys: a patch that moves
|
|
100
|
-
* `currentGen`, or changes `wrappedDeks` so the summary's shape no longer agrees, without mentioning `summary` drops
|
|
101
|
-
* the old one; and a summary the patch gives must name the resulting `currentGen` and agree with the resulting keys
|
|
102
|
-
* (`ValidationError`, before anything is written).
|
|
103
|
-
*/
|
|
104
|
-
export declare function applyRegistryPatch(prev: RegistryRecord, patch: RegistryPatch, now: number, token: Token): RegistryRecord;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { ValidationError } from '../../core/errors.js';
|
|
2
|
-
/**
|
|
3
|
-
* The index of the first component of `actual` (the path as it is on disk, relative to the root) that equals the same
|
|
4
|
-
* component of `expected` ignoring case but not exactly, or `-1` when there is none. A component that is a different
|
|
5
|
-
* name altogether, or a path that is longer or shorter, is not a case difference.
|
|
6
|
-
*/
|
|
7
|
-
export declare function firstCaseDifference(expected: readonly string[], actual: readonly string[]): number;
|
|
8
|
-
/** What a path-resolving call needs of the filesystem: the error to refuse a write with. */
|
|
9
|
-
export declare function caseVariantError(): ValidationError;
|
|
10
|
-
export declare class ExactCase {
|
|
11
|
-
private readonly root;
|
|
12
|
-
/** `undefined` until the root holds an entry to try; then fixed, as a filesystem does not change its mind. */
|
|
13
|
-
private insensitive;
|
|
14
|
-
constructor(root: string);
|
|
15
|
-
/**
|
|
16
|
-
* Whether the root's filesystem folds case, learned from an entry already in the root: the entry's name with its
|
|
17
|
-
* case swapped is the same file or it is not. An empty or absent root holds nothing to alias, so it is not
|
|
18
|
-
* insensitive yet, and is looked at again on the next call.
|
|
19
|
-
*/
|
|
20
|
-
private foldsCase;
|
|
21
|
-
/**
|
|
22
|
-
* The same question asked of the root's own name, or the nearest existing directory above it, when the root holds
|
|
23
|
-
* nothing to try: a root with no entries has none to alias yet, but two names created at once still would. Not
|
|
24
|
-
* remembered, since it is the volume above the root that answers.
|
|
25
|
-
*/
|
|
26
|
-
private foldsAtAncestor;
|
|
27
|
-
/** Whether the root's filesystem folds case: names that differ only by case are one file there. */
|
|
28
|
-
folds(): Promise<boolean>;
|
|
29
|
-
/**
|
|
30
|
-
* `true` when `path` (inside the root) is, on disk, a path whose name differs only by case from the one given:
|
|
31
|
-
* the deepest part of it that exists is resolved to its real name and compared exactly. A path that does not exist
|
|
32
|
-
* yet, or that nothing differs from, is `false`.
|
|
33
|
-
*/
|
|
34
|
-
differs(path: string): Promise<boolean>;
|
|
35
|
-
/** Refuse a write to `path` when its real name differs from it only by case. */
|
|
36
|
-
refuseVariant(path: string): Promise<void>;
|
|
37
|
-
}
|
|
@@ -1,33 +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
|
-
* Best-effort parent-directory fsync, so a just-published `rename`/`link` survives a crash. Without it the
|
|
24
|
-
* directory entry can still be in the page cache when power goes: the generation's bytes are durable and
|
|
25
|
-
* the name pointing at them is not, which is the one way a published generation can come back missing.
|
|
26
|
-
*/
|
|
27
|
-
export declare function fsyncDir(dir: string): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Write every byte of `bytes` at the handle's position. One `write` can write fewer bytes than it was asked to and not
|
|
30
|
-
* fail (a full disk, a quota, some network filesystems), and a caller that took it for the whole would store a torn row
|
|
31
|
-
* or object as a whole one. A write that makes no progress is a full device.
|
|
32
|
-
*/
|
|
33
|
-
export declare function writeAll(handle: FileHandle, bytes: Uint8Array): 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,44 +0,0 @@
|
|
|
1
|
-
import type { Entropy } from '../../core/determinism.js';
|
|
2
|
-
import type { IRegistryDriver, NewRegistryRecord, RegCaps, RegistryPatch, RegistryRecord, 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
|
-
export 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
|
-
}
|
|
11
|
-
export declare class LocalFsRegistryDriver implements IRegistryDriver {
|
|
12
|
-
private readonly root;
|
|
13
|
-
private readonly now;
|
|
14
|
-
private readonly entropy;
|
|
15
|
-
private readonly exactCase;
|
|
16
|
-
constructor(root: string, options?: LocalFsRegistryDriverOptions);
|
|
17
|
-
/**
|
|
18
|
-
* `conditionalDelete`: a delete reads the row, checks it, and unlinks it under the row's lock, which every writer on
|
|
19
|
-
* the root in this process takes, so no write can land between the check and the unlink.
|
|
20
|
-
*/
|
|
21
|
-
capabilities(): RegCaps;
|
|
22
|
-
get(ref: SegmentRef): Promise<RegistryRecord | null>;
|
|
23
|
-
create(ref: SegmentRef, record: NewRegistryRecord): Promise<{
|
|
24
|
-
token: Token;
|
|
25
|
-
}>;
|
|
26
|
-
/**
|
|
27
|
-
* 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
|
|
28
|
-
* there, a local read and no request, so a stale one fails exactly as a lost race does.
|
|
29
|
-
*/
|
|
30
|
-
compareAndSwap(ref: SegmentRef, expected: Token, patch: RegistryPatch): Promise<{
|
|
31
|
-
token: Token;
|
|
32
|
-
}>;
|
|
33
|
-
list(namespace?: string): AsyncIterable<RegistryRecord>;
|
|
34
|
-
delete(ref: SegmentRef, expected?: Token): Promise<void>;
|
|
35
|
-
/** Remove a row's file and make the removal durable (fsync the directory). */
|
|
36
|
-
private unlinkRow;
|
|
37
|
-
/** Namespaces to scan: just the one requested, or every namespace dir under the root. */
|
|
38
|
-
private namespaceDirs;
|
|
39
|
-
/** Read the row at `path`, which must be the row of `ref`: one that names another segment is refused. */
|
|
40
|
-
private readRow;
|
|
41
|
-
private writeRow;
|
|
42
|
-
/** Serialize callbacks for a row so read-modify-write is atomic across every instance in the process. */
|
|
43
|
-
private withRowLock;
|
|
44
|
-
}
|
|
@@ -1,21 +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 exactCase;
|
|
6
|
-
constructor(root: string);
|
|
7
|
-
capabilities(): StorageCaps;
|
|
8
|
-
putImmutable(key: GenKey, write: (sink: BlobSink) => Promise<void>): Promise<{
|
|
9
|
-
size: number;
|
|
10
|
-
sha256: string;
|
|
11
|
-
}>;
|
|
12
|
-
getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array>;
|
|
13
|
-
getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
|
|
14
|
-
/**
|
|
15
|
-
* Idempotent. Ignores `options.ifVersion`, as `conditionalDelete: false` says: see the note on the class for why it
|
|
16
|
-
* cannot apply one. The parameter is declared so that a wrapper of this driver can hand the port's options on.
|
|
17
|
-
*/
|
|
18
|
-
delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
|
|
19
|
-
list(ref: SegmentRef): AsyncIterable<GenKey>;
|
|
20
|
-
private openRead;
|
|
21
|
-
}
|
package/dist/drivers/memory.d.ts
DELETED
|
@@ -1,61 +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, SegmentRef, Token } from '../core/ports.js';
|
|
4
|
-
export 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
|
-
get(ref: SegmentRef): Promise<RegistryRecord | null>;
|
|
26
|
-
create(ref: SegmentRef, record: NewRegistryRecord): Promise<{
|
|
27
|
-
token: Token;
|
|
28
|
-
}>;
|
|
29
|
-
/**
|
|
30
|
-
* 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
|
|
31
|
-
* as the write, in memory, so a stale one fails exactly as a lost race does.
|
|
32
|
-
*/
|
|
33
|
-
compareAndSwap(ref: SegmentRef, expected: Token, patch: RegistryPatch): Promise<{
|
|
34
|
-
token: Token;
|
|
35
|
-
}>;
|
|
36
|
-
list(namespace?: string): AsyncIterable<RegistryRecord>;
|
|
37
|
-
delete(ref: SegmentRef, expected?: Token): Promise<void>;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* In-memory {@link IStorageDriver} — write-once immutable generation objects as opaque bytes. A "dumb byte
|
|
41
|
-
* mover" (understands neither roaring nor `.crbm`), so it's a faithful storage backend for tests and a zero-setup
|
|
42
|
-
* local storage tier. Mirrors the LocalFs/S3 contract (write-once, range/tail reads).
|
|
43
|
-
*
|
|
44
|
-
* Every object stored gets a version from a counter the driver never reuses, so two objects stored one after the other
|
|
45
|
-
* under one key never share one, whatever their bytes. A delete given `ifVersion` compares it with the stored object's
|
|
46
|
-
* and removes it in the same step, with no await between them: `conditionalDelete` is `true`.
|
|
47
|
-
*/
|
|
48
|
-
export declare class MemoryStorageDriver implements IStorageDriver {
|
|
49
|
-
private readonly objects;
|
|
50
|
-
private stored;
|
|
51
|
-
capabilities(): StorageCaps;
|
|
52
|
-
putImmutable(key: GenKey, write: (sink: BlobSink) => Promise<void>): Promise<{
|
|
53
|
-
size: number;
|
|
54
|
-
sha256: string;
|
|
55
|
-
}>;
|
|
56
|
-
getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array>;
|
|
57
|
-
getTail(key: GenKey, maxBytes: number): Promise<TailRead>;
|
|
58
|
-
delete(key: GenKey, options?: StorageDeleteOptions): Promise<void>;
|
|
59
|
-
list(ref: SegmentRef): AsyncIterable<GenKey>;
|
|
60
|
-
private require;
|
|
61
|
-
}
|
|
@@ -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';
|