@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.
Files changed (98) hide show
  1. package/README.md +23 -8
  2. package/dist/chunk-7GT632UF.js +641 -0
  3. package/dist/chunk-7GT632UF.js.map +7 -0
  4. package/dist/chunk-CGILDXGP.js +73 -0
  5. package/dist/chunk-CGILDXGP.js.map +7 -0
  6. package/dist/chunk-GKHERVVQ.js +121 -0
  7. package/dist/chunk-GKHERVVQ.js.map +7 -0
  8. package/dist/{chunk-IGLIZBR7.js → chunk-MVUD4R72.js} +253 -765
  9. package/dist/chunk-MVUD4R72.js.map +7 -0
  10. package/dist/core/audit.d.ts +53 -15
  11. package/dist/core/bit-route.d.ts +9 -0
  12. package/dist/core/blob.d.ts +4 -0
  13. package/dist/core/budget.d.ts +8 -3
  14. package/dist/core/check.d.ts +27 -0
  15. package/dist/core/codec.d.ts +1 -1
  16. package/dist/core/combine-expr.d.ts +9 -5
  17. package/dist/core/combine-feed.d.ts +11 -3
  18. package/dist/core/combine-many.d.ts +28 -13
  19. package/dist/core/consistency.d.ts +11 -2
  20. package/dist/core/cooperative.d.ts +7 -0
  21. package/dist/core/crbm-storage-source.d.ts +7 -4
  22. package/dist/core/crypto.d.ts +5 -0
  23. package/dist/core/determinism.d.ts +2 -1
  24. package/dist/core/discover.d.ts +5 -3
  25. package/dist/core/engine.d.ts +4 -4
  26. package/dist/core/erase-id.d.ts +52 -19
  27. package/dist/core/erasure.d.ts +62 -28
  28. package/dist/core/errors.d.ts +83 -9
  29. package/dist/core/leases.d.ts +2 -0
  30. package/dist/core/load-input.d.ts +48 -0
  31. package/dist/core/load.d.ts +71 -16
  32. package/dist/core/metrics.d.ts +22 -1
  33. package/dist/core/pinned-storage-source.d.ts +27 -8
  34. package/dist/core/pointer-id.d.ts +9 -0
  35. package/dist/core/ports.d.ts +107 -13
  36. package/dist/core/preflight.d.ts +80 -0
  37. package/dist/core/registry-scan.d.ts +0 -5
  38. package/dist/core/retention-sweep.d.ts +61 -16
  39. package/dist/core/retention.d.ts +18 -7
  40. package/dist/core/retry.d.ts +5 -2
  41. package/dist/core/rollback.d.ts +23 -12
  42. package/dist/core/validate.d.ts +33 -2
  43. package/dist/driver-kit.d.ts +9 -4
  44. package/dist/driver-kit.js +63 -27
  45. package/dist/driver-kit.js.map +2 -2
  46. package/dist/drivers/_shared/object-registry.d.ts +24 -1
  47. package/dist/drivers/_shared/options.d.ts +15 -7
  48. package/dist/drivers/backends.d.ts +19 -2
  49. package/dist/drivers/crypto.d.ts +18 -3
  50. package/dist/export/index.d.ts +25 -10
  51. package/dist/index.d.ts +19 -17
  52. package/dist/index.js +2457 -2283
  53. package/dist/index.js.map +4 -4
  54. package/dist/node.d.ts +14 -0
  55. package/dist/node.js +1064 -0
  56. package/dist/node.js.map +7 -0
  57. package/package.json +16 -2
  58. package/dist/chunk-IGLIZBR7.js.map +0 -7
  59. package/dist/core/absent-operands.d.ts +0 -8
  60. package/dist/core/bulk-load.d.ts +0 -148
  61. package/dist/core/cas-retry.d.ts +0 -27
  62. package/dist/core/chunk-checks.d.ts +0 -39
  63. package/dist/core/chunk-stream.d.ts +0 -22
  64. package/dist/core/chunk-window.d.ts +0 -37
  65. package/dist/core/combine-ledger.d.ts +0 -43
  66. package/dist/core/crbm/crc32c.d.ts +0 -10
  67. package/dist/core/crbm/fingerprint.d.ts +0 -23
  68. package/dist/core/crbm/format.d.ts +0 -150
  69. package/dist/core/crbm/frame.d.ts +0 -10
  70. package/dist/core/crbm/plan-reads.d.ts +0 -50
  71. package/dist/core/crbm/varint.d.ts +0 -14
  72. package/dist/core/crbm/writer.d.ts +0 -72
  73. package/dist/core/crbm-open.d.ts +0 -154
  74. package/dist/core/crbm-write.d.ts +0 -75
  75. package/dist/core/due-index.d.ts +0 -53
  76. package/dist/core/generation-gc.d.ts +0 -293
  77. package/dist/core/generation-list.d.ts +0 -11
  78. package/dist/core/id-window.d.ts +0 -46
  79. package/dist/core/invalidation-scope.d.ts +0 -77
  80. package/dist/core/item-pull.d.ts +0 -29
  81. package/dist/core/known-generations-shape.d.ts +0 -13
  82. package/dist/core/known-generations.d.ts +0 -147
  83. package/dist/core/ledger-seed.d.ts +0 -17
  84. package/dist/core/metadata.d.ts +0 -42
  85. package/dist/core/order.d.ts +0 -2
  86. package/dist/core/publish.d.ts +0 -207
  87. package/dist/core/reader-defaults.d.ts +0 -29
  88. package/dist/core/summary.d.ts +0 -110
  89. package/dist/core/token.d.ts +0 -41
  90. package/dist/drivers/_shared/entropy.d.ts +0 -5
  91. package/dist/drivers/_shared/registry.d.ts +0 -107
  92. package/dist/drivers/localfs/exact-case.d.ts +0 -34
  93. package/dist/drivers/localfs/fs-util.d.ts +0 -55
  94. package/dist/drivers/localfs/paths.d.ts +0 -40
  95. package/dist/drivers/localfs/registry.d.ts +0 -50
  96. package/dist/drivers/localfs/storage.d.ts +0 -28
  97. package/dist/drivers/memory.d.ts +0 -64
  98. package/dist/testing/fuzz-core.d.ts +0 -15
@@ -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;
@@ -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
- }
@@ -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';