@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,11 +0,0 @@
1
- /**
2
- * The generations a segment's bucket holds.
3
- */
4
- import type { IStorageDriver, SegmentRef } from './ports.js';
5
- /**
6
- * The generations the bucket holds for `ref`, from one listing, each once and in ascending order. A listing that spans
7
- * a purge and a re-create can yield one number twice (the objects are re-created under the numbers just swept), so the
8
- * duplicates are dropped here rather than counted by each caller. A caller that stops at the first object it wants
9
- * reads the listing itself.
10
- */
11
- export declare function generationsOf(storage: IStorageDriver, ref: SegmentRef): Promise<number[]>;
@@ -1,46 +0,0 @@
1
- import type { CodecBitmap, CodecInterface } from './codec.js';
2
- /** A validated range: inclusive id bounds, as the chunk key and the remainder at each end. */
3
- export interface IdWindow {
4
- readonly loKey: number;
5
- readonly loRem: number;
6
- readonly hiKey: number;
7
- readonly hiRem: number;
8
- }
9
- /** A range as a read is asked for it: each bound optional. */
10
- interface IdBounds {
11
- readonly after?: number | undefined;
12
- readonly through?: number | undefined;
13
- }
14
- /**
15
- * Throws {@link ValidationError} unless `bound` is left out or is an id: an integer from 0 to 4294967295. The message
16
- * says what is wrong and never carries the value, which is an id.
17
- */
18
- export declare function assertIdBound(name: 'after' | 'through', bound: number | undefined): void;
19
- /**
20
- * Validate a read's range. `null` when none was asked for, so the read does no extra work; `'empty'` when no id can be
21
- * in it (`after >= through`, which is a cursor at the end of its window, not an error).
22
- */
23
- export declare function windowOf(range: IdBounds | undefined): IdWindow | 'empty' | null;
24
- /** The window a range with neither bound set describes: every id. */
25
- export declare const WHOLE_ID_SPACE: IdWindow;
26
- /**
27
- * Whether the window cuts inside `chunkKey`: one of its two edge chunks, and only where the bound falls inside the chunk
28
- * rather than on its boundary. A chunk the window covers whole is read as it is.
29
- */
30
- export declare const isEdge: (chunkKey: number, w: IdWindow) => boolean;
31
- /** The ids of an edge chunk that lie inside the window. The chunk iterates ascending, so it stops at the top. */
32
- export declare function edgeIds(chunk: CodecBitmap, chunkKey: number, w: IdWindow): Generator<number>;
33
- /** The ascending `keys` inside the window's chunk span, as a new array: two binary searches, no scan. */
34
- export declare function keysWithin(keys: readonly number[], w: IdWindow): number[];
35
- /**
36
- * The ascending `keys` inside the window's chunk span (every key, for `null`) and above the key `after`: what a read
37
- * that plans again after taking `after` still reads.
38
- */
39
- export declare function keysAbove(keys: readonly number[], w: IdWindow | null, after: number): number[];
40
- /** The index of the first element of ascending `a` greater than `value`: a binary search. */
41
- export declare function firstAbove(a: Uint32Array, value: number): number;
42
- /** The part of an edge chunk's ascending remainders `rem` inside the window, as a view of `rem`. */
43
- export declare function remaindersWithin(rem: Uint32Array, chunkKey: number, w: IdWindow): Uint32Array;
44
- /** An edge chunk cut to the window, as a new bitmap: the remainders {@link edgeIds} would yield. */
45
- export declare function cutToWindow(codec: CodecInterface, chunk: CodecBitmap, chunkKey: number, w: IdWindow): CodecBitmap;
46
- export {};
@@ -1,77 +0,0 @@
1
- /**
2
- * What the engines reading through one chunk cache share about invalidations: how many there have been, the segment
3
- * each of the latest was of, and the chunk reads open right now. A store's own engine, each pin's and each combine's
4
- * with a pinned operand read through one cache, and an invalidation through any of them must reach a read running on
5
- * another: a read that had its bytes before the invalidation would otherwise write them back into the cache it
6
- * shares, and later reads would be answered from them, an erased id included, until the cache evicted them.
7
- *
8
- * ```
9
- * engine (store) ──┐
10
- * engine (pin) ──┼── one InvalidationScope ── epoch · latest invalidation of each segment · open reads
11
- * engine (combine)─┘ └── one chunk cache
12
- * ```
13
- *
14
- * A stream records the epoch it opened at and asks {@link InvalidationRecord.invalidatedSince} before it caches a
15
- * chunk, so the scope holds no reference to a stream: a read its caller drops without closing holds nothing here. The
16
- * `.crbm` source keeps an {@link InvalidationRecord} of its own, by segment, which a pinned stream asks before it hands
17
- * out a chunk, so only its own segment's invalidation makes it open its object again.
18
- *
19
- * Internal to core: not exported from any entry point.
20
- */
21
- import type { CodecBitmap } from './codec.js';
22
- /**
23
- * How many segments' latest invalidation a record remembers. Past it the oldest is forgotten, and a segment the record
24
- * does not remember, never-invalidated ones included, counts as invalidated at the newest invalidation it has
25
- * forgotten, which is always on the safe side. In the engines' scope it means a stream that opened before then caches
26
- * no more of that segment's chunks, which costs it cache entries, never correctness: so a sweep that invalidates more
27
- * than this many segments at once (a `retireExpired` or an `eraseSubject` over a large fleet) stops every stream open
28
- * at the time caching, whatever segment it reads. In the `.crbm` source it means a pinned stream looks its pin up
29
- * again, which costs a request only where the reader cache has let the pin's reader go. With names at their
30
- * 256-character limit a record is about 0.6 MB, and about 0.1 MB with 30-character names; a store keeps two.
31
- */
32
- export declare const REMEMBERED_INVALIDATIONS = 1024;
33
- /** A chunk read in flight: `token` tells the read whether its entry is still the registered one. */
34
- export interface OpenRead {
35
- readonly token: object;
36
- readonly read: Promise<CodecBitmap | null>;
37
- }
38
- /**
39
- * How many invalidations there have been, and the epoch of each of the latest {@link REMEMBERED_INVALIDATIONS}
40
- * segments' latest one, by whatever key names a segment to its owner: a cache-key prefix in the engines' scope, a
41
- * segment key in the `.crbm` source.
42
- */
43
- export declare class InvalidationRecord {
44
- private count;
45
- /** The epoch of each remembered segment's latest invalidation, the oldest first. */
46
- private readonly latest;
47
- /** The epoch of the newest invalidation the record has forgotten: what a segment it does not remember counts as. */
48
- private floor;
49
- /** How many invalidations there have been: a read compares it with the epoch it began under. */
50
- get epoch(): number;
51
- /** Record an invalidation of the segment `key` names. */
52
- invalidate(key: string): void;
53
- /** Whether the segment `key` names has been invalidated since epoch `since`. */
54
- invalidatedSince(key: string, since: number): boolean;
55
- }
56
- /** The engines' record, by cache-key prefix, and the chunk reads open on all of them. */
57
- export declare class InvalidationScope extends InvalidationRecord {
58
- /**
59
- * The chunk reads open right now, by cache key: a caller that misses the cache and finds its key here awaits that
60
- * read instead of making its own. An entry lives from the request until it settles, so the map holds at most one
61
- * promise per distinct key in flight. An invalidation drops the segment's entries, and a read whose entry is gone
62
- * caches nothing.
63
- */
64
- private readonly openReads;
65
- /** Record an invalidation of the segment whose cache keys start with `prefix`, and drop its open reads. */
66
- invalidate(prefix: string): void;
67
- /** The read of `key` open now, for a caller that misses the cache to await instead of making its own. */
68
- openRead(key: string): Promise<CodecBitmap | null> | undefined;
69
- /** Whether a read of `key` is open, without joining it. */
70
- hasOpenRead(key: string): boolean;
71
- /** Register `entry` as the open read of `key`. */
72
- beginRead(key: string, entry: OpenRead): void;
73
- /** `entry` has settled: forget it, unless an invalidation dropped it and a newer read has taken the key. */
74
- endRead(key: string, entry: OpenRead): void;
75
- /** Whether the read `token` names is still the open read of `key`: one an invalidation dropped caches nothing. */
76
- isOpen(key: string, token: object): boolean;
77
- }
@@ -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,13 +0,0 @@
1
- /**
2
- * The shape of a row's known-generations ledger (`knownGens`), in one place. A registry holds a row to it when it writes
3
- * and when it reads back, and `checkConsistency` holds a row to it before it reads or walks a ledger a registry of your
4
- * own handed back, so the two cannot part on what a ledger is. Pure: no I/O, time or randomness.
5
- */
6
- /**
7
- * Why `value` is not a ledger, or `undefined` when it is one (an absent ledger, `undefined`, passes: whether a row
8
- * without one is usable is another rule). A ledger is an array of at most `max` entries, each exactly `{ generation,
9
- * footerCrc }`, the generation a non-negative safe integer and unique, the checksum an integer from 0 to 2^32 - 1. The
10
- * order is not checked: it is not derivable from the numbers. `show` renders a value for the description, which a
11
- * caller can truncate.
12
- */
13
- export declare function knownGensShapeFault(value: unknown, max: number, show?: (v: unknown) => string): string | undefined;
@@ -1,147 +0,0 @@
1
- import type { KnownGeneration, RegistryRecord } from './ports.js';
2
- /**
3
- * The most generations a load that collects by name records as its window. A `keep` above it collects by listing.
4
- */
5
- export declare const MAX_KEPT_GENERATIONS = 64;
6
- /**
7
- * A writer keeps entries that nothing protects and no lease holds only while the ledger holds fewer than this many.
8
- */
9
- export declare const KNOWN_FILL = 128;
10
- /** The most entries a writer writes, and the most the shared validator accepts at a write. */
11
- export declare const MAX_KNOWN_GENERATIONS = 192;
12
- /** The most entries a stored row may hold: twice what a writer writes. */
13
- export declare const MAX_STORED_KNOWN_GENERATIONS = 384;
14
- /**
15
- * The row's ledger when a collection or a rollback may rely on it: present and not empty, on an active row with a
16
- * pointer, its last entry the pointer, and, where the row's summary is in the clear and describes that generation, its
17
- * checksum the one the summary's fingerprint carries. A well-formed ledger that disagrees with its row is read but not
18
- * used (the row counts as one with no ledger), as a summary that names another generation is.
19
- */
20
- export declare function usableKnownGens(record: RegistryRecord | null): readonly KnownGeneration[] | undefined;
21
- /**
22
- * The generations the row keeps as its window: the numbers of the usable ledger's entries below the pointer, in the order
23
- * they were made current, or `undefined` when the ledger is not usable. A collection compares against these. An entry
24
- * numbered above the pointer is one a rollback left; no collection can take it, so it is no part of the window.
25
- */
26
- export declare function windowOf(record: RegistryRecord | null): readonly number[] | undefined;
27
- /**
28
- * Whether the row's ledger is a window a load with a small `keep` can extend: usable, and naming no more than
29
- * {@link MAX_KEPT_GENERATIONS} generations below the pointer that no live lease holds. A ledger carried through
30
- * publishes with a larger `keep` (`*Into`, `materializeMany`) names up to 127, and a count by a small `keep` would keep
31
- * fewer than it names, so the next small-`keep` load collects by listing instead of by name, over the same ledger. A
32
- * generation held only for a lease is no part of the window and does not count toward the limit.
33
- */
34
- export declare function carriesWindow(record: RegistryRecord | null, leased: ReadonlySet<number>): boolean;
35
- /**
36
- * How many generations below its own a publish vouches for at most: what the writer cap leaves beside the new
37
- * generation and the 64 generations live leases can hold.
38
- */
39
- export declare const KNOWN_SPAN = 127;
40
- /** Which generations below a new pointer a grace window holds, and which it lets go: both ascending. */
41
- interface KeptWindow {
42
- readonly kept: number[];
43
- readonly evict: number[];
44
- }
45
- /**
46
- * The generations below a new pointer that a grace window of `keep` holds, out of `below`, when no ledger says which
47
- * were made current most recently: the generation the publish superseded first, when it is among them, then the newest
48
- * of the rest by number. When it is not among them (no generation was current, or its object has gone from the bucket),
49
- * the newest `keep`.
50
- *
51
- * The superseded generation is the one reads were on until the publish, and, after a rollback, the one the operator
52
- * restored, which a pin taken since reads, and the one to roll back to if the new load is bad too. The generations a
53
- * rollback moved away from are newer by number, so a window by number alone would keep those and delete it. The window
54
- * a listing keeps over a row with no usable ledger comes from here, and so does a ledger's first window
55
- * (`ledger-seed.ts`); once the row has a ledger, its order says which were made current most recently.
56
- */
57
- export declare function keptWindow(below: readonly number[], superseded: number | undefined, keep: number): KeptWindow;
58
- /**
59
- * `known`, held to the cap, in the same order. It always keeps the entries numbered in `protect`, then the ones numbered
60
- * in `leased`, most recently current first, up to {@link MAX_KNOWN_GENERATIONS}, then the rest, most recently current
61
- * first, while the ledger holds fewer than {@link KNOWN_FILL}. So a protected or leased entry is never dropped for room,
62
- * and an unprotected one only when the ledger is full. "Most recently current" is the end of the list.
63
- */
64
- export declare function capKnown(known: readonly KnownGeneration[], protect: ReadonlySet<number>, leased?: ReadonlySet<number>): KnownGeneration[];
65
- /** `known` with `entry` made the most recent: any entry for its generation removed, and the entry added last. */
66
- export declare function makeCurrent(known: readonly KnownGeneration[], entry: KnownGeneration): KnownGeneration[];
67
- /** What a publish of a generation writes into the row, and which names it pushes out of the window. */
68
- export interface KnownAfter {
69
- /** The ledger to write: the generation published is its last entry. */
70
- readonly known: readonly KnownGeneration[];
71
- /** The window as numbers to collect by name, or `undefined` to collect by listing. */
72
- readonly list: readonly number[] | undefined;
73
- /** The generations the window no longer keeps, ascending: the names to delete. */
74
- readonly evict: readonly number[];
75
- }
76
- /**
77
- * What a publish of `generation` (its object's footer checksum `footerCrc`) over `prev` writes into the row.
78
- *
79
- * The ledger is in the order the generations were made current, so the order says which were current most recently.
80
- * That ranks the window, and the window is drawn from the entries numbered below the new generation only: collection
81
- * can only take generations numbered below the pointer. The window is the `keep` most recently current of them (at most
82
- * {@link KNOWN_SPAN}), so the generation the publish superseded, the ledger's last entry, comes first, and after a
83
- * rollback the ones the operator restored outrank the ones the rollback moved away from.
84
- *
85
- * What it writes, in the order the entries were made current and the new generation last:
86
- *
87
- * - the window;
88
- * - the entries below the new generation that `leased` names (a generation a live lease holds stays known, so it stays a
89
- * rollback target while the lease lives), whatever `keep` says;
90
- * - the entries numbered above the new generation, which a rollback left and a load numbered under: they are carried and
91
- * never evicted, and take no slot of `keep`, while the ledger holds fewer than {@link KNOWN_FILL} entries (see
92
- * {@link capKnown});
93
- * - the new generation. An entry already numbered `generation` is a stale one (its object went outside the ledger) and
94
- * is replaced.
95
- *
96
- * `evict` is the entries below the new generation that it leaves out, ascending: never a name at or above the new
97
- * generation. With a `keep` of at most {@link MAX_KEPT_GENERATIONS} the publish collects by name, so `list` is the
98
- * generations of the ledger below the new one; above that, or with no `keep`, `list` is `undefined` and `evict` empty, and
99
- * a load lists. A row with a pointer and no usable ledger writes the new generation alone and lists, unless the caller
100
- * passes a `seed` (see `ledger-seed.ts`): the window a count keeps, read from the bucket, then the new generation, and
101
- * the publish collects by that count. A publish that clears leases is an erasure's rewrite, which deletes every older
102
- * generation, so its ledger is the new generation alone.
103
- */
104
- export declare function nextKnown(prev: RegistryRecord | null, generation: number, footerCrc: number, keep: number | undefined, options?: {
105
- readonly clearsLeases?: boolean;
106
- readonly seed?: readonly KnownGeneration[];
107
- readonly leased?: ReadonlySet<number>;
108
- }): KnownAfter;
109
- /**
110
- * The ledger a rollback onto `target` writes over `prev`: the ledger it found with the target moved to the end, as the
111
- * most recently current. A rollback never starts a ledger: over a row with no usable ledger it writes none, and the
112
- * next publish seeds one. A target the ledger did not name
113
- * is an insert, and the ledger it makes is held to the cap with the target and the generation the rollback leaves
114
- * protected; a known target's ledger is written as read, unless another build wrote one that is past the writer's cap.
115
- */
116
- export declare function rollbackKnown(prev: RegistryRecord, target: KnownGeneration): readonly KnownGeneration[] | undefined;
117
- /**
118
- * The row's ledger with the entries numbered in `doomed` removed, which a write that is about to delete those
119
- * generations makes first: the entries left keep their order, and the current generation is never removed (the ledger
120
- * must end with it). Another build's ledger past the writer's cap is held to it, with the current generation protected
121
- * and the generations the row's leases name kept. `undefined` for a row with no usable ledger, which names nothing to
122
- * remove.
123
- */
124
- export declare function pruneKnown(record: RegistryRecord, doomed: ReadonlySet<number>): readonly KnownGeneration[] | undefined;
125
- /**
126
- * The ledger that puts a row back as it was, for the undo of a rollback: the row's own when it is usable (the
127
- * generation it left is its last entry), held to the writer's cap when another build wrote one past it, with the
128
- * generation the undo leaves and the one it moves from protected; `undefined` for a row that had none to put back.
129
- */
130
- export declare function describingKnown(record: RegistryRecord, movedTo: number): readonly KnownGeneration[] | undefined;
131
- /**
132
- * Whether the ledger is full: it names at least {@link KNOWN_FILL} generations, which is when a writer may have let an
133
- * older one go (a publish vouches for at most {@link KNOWN_SPAN} below its own, and an inserting rollback drops an entry
134
- * once 128 that no lease holds are named). Leased entries do not switch it off: a lease on an entry inside the span
135
- * leaves the ledger as full as it was. Past the least recently current entry of a full ledger, a rollback can no longer
136
- * tell a generation the row forgot from one it never knew, so it judges a target numbered below that entry as it did
137
- * before the ledger existed.
138
- */
139
- export declare function isFullLedger(known: readonly KnownGeneration[]): boolean;
140
- /**
141
- * The number a target below the pointer is judged against on a full ledger: the least recently current entry that no
142
- * lease entry of the row names (live or ended: a rollback has no clock). A leased entry stays in the ledger for its lease,
143
- * however far the window has moved on since, so it does not mark where the ledger stopped remembering; the first entry no
144
- * lease holds does. A ledger whose every entry is leased is judged against its first entry.
145
- */
146
- export declare function fallbackBound(record: RegistryRecord, known: readonly KnownGeneration[]): number;
147
- export {};
@@ -1,17 +0,0 @@
1
- import { type ReadRetry } from './retry.js';
2
- import type { IStorageDriver, KnownGeneration, RegistryRecord, SegmentRef } from './ports.js';
3
- /**
4
- * Whether a publish over `row` seeds its ledger: only at a birth, over an active row that has a pointer and no usable
5
- * ledger. A usable ledger is never seeded again, however wide it is.
6
- */
7
- export declare function wantsSeed(row: RegistryRecord | null): boolean;
8
- /**
9
- * The entries a seed names: the listed generations below the new one that a live lease holds and the window does not
10
- * keep, then the window oldest first by number with the generation the row named as current last. `listed` is the
11
- * listing the load's numbering made, when it made one; otherwise the bucket is listed once. Every entry's checksum is
12
- * read from its object's footer (a 104-byte read, under the store's read retry, {@link SEED_READS} at a time), the
13
- * superseded generation's too: when a clear summary describes it with another checksum, the row and the bucket
14
- * disagree about that number and it is left out. A generation gone from the bucket, or whose footer fails its own
15
- * checks, is left out; any other failed read throws, before anything is published.
16
- */
17
- export declare function readSeed(storage: IStorageDriver, ref: SegmentRef, row: RegistryRecord | null, generation: number, keep: number, listed: readonly number[] | undefined, leased: ReadonlySet<number>, readRetry?: ReadRetry): Promise<KnownGeneration[]>;
@@ -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,2 +0,0 @@
1
- /** The comparator that sorts numbers smallest first, which `Array.prototype.sort` does not do by default. */
2
- export declare const ascending: (a: number, b: number) => number;
@@ -1,207 +0,0 @@
1
- import type { Yielder } from './cooperative.js';
2
- import type { Rng } from './determinism.js';
3
- import type { PublishedKept } from './generation-gc.js';
4
- import type { WrappedDek } from './crypto.js';
5
- import type { GenKey, IRegistryDriver, KnownGeneration, RegistryRecord, RegistrySummary, Token } from './ports.js';
6
- /** What a publish is told, beyond the registry and the generation key. */
7
- interface PublishOptions {
8
- wrappedDeks?: readonly WrappedDek[];
9
- expectFrom?: number;
10
- expectToken?: Token;
11
- /**
12
- * The row `expectToken` names, as the caller read it. With it, a row whose token has moved but which differs from
13
- * this one only in its leases (a reader took or released one) does not refuse the publish: it goes on against the
14
- * fresh row, after a jittered wait and without the caller redoing its work, up to a bound. Any other difference
15
- * refuses as ever. Without it, a moved token refuses.
16
- */
17
- expectRow?: RegistryRecord | null;
18
- /**
19
- * Fields of `expectRow` that a row may also differ in, beside its leases, without the fence refusing: a write that
20
- * changed only those did not touch what the caller derived its content from. An erasure's rewrite passes `knownGens`,
21
- * since another erasure's removal of generations from the ledger leaves the generation it streamed alone. A write that
22
- * also renewed the row's `pointerId` still refuses.
23
- */
24
- expectRowIgnores?: readonly (keyof RegistryRecord)[];
25
- /**
26
- * Publish only while the segment still has **no registry row**.
27
- *
28
- * The fence for a caller whose decision rests on the row being ABSENT: every load that found no row sets it,
29
- * guarded or not. `expectFrom` and `expectToken` cannot express it: both compare against a value read from a
30
- * row, so when there was no row there is nothing to compare and both are simply omitted — leaving the publish a
31
- * bare forward-only advance that happily lands over whatever appeared in the meantime.
32
- *
33
- * Without it, a guarded load into a segment that did not exist yet would judge "no row", pass its empty and
34
- * `minRetained` bounds vacuously, and land an EMPTY generation over the ids a concurrent writer had just loaded,
35
- * reporting `published: true`. An unguarded load would move the pointer over another first load's generation or
36
- * onto a row `setRetention` made, and, where an erasure had deleted its object above that row's pointer, onto an
37
- * object that is gone. Materialising into a destination that does not exist yet is the ordinary first run of a
38
- * pipeline, so the `*Into` verbs reach this as often as `loadSegment` does.
39
- */
40
- expectAbsent?: boolean;
41
- /**
42
- * The generation's summary (see {@link RegistrySummary}), written by the write that moves the pointer to it. It
43
- * names `key.generation`. Absent, the row is left with none for this generation.
44
- */
45
- summary?: RegistrySummary;
46
- /**
47
- * The segment's row as the caller already read it (`null`: it found none). The first attempt acts on it
48
- * instead of reading the row again, which is sound because that attempt writes only under the row's own
49
- * fence: its compare-and-swap carries this row's token, and with no row it creates create-only, so a row
50
- * that changed or appeared since makes the write lose and the next attempt reads the row afresh. Only the
51
- * idempotent "already current" answer writes nothing, so a reused row never gives it.
52
- */
53
- row?: RegistryRecord | null;
54
- /**
55
- * The object was written without encryption. Its publish is refused, with {@link KeyUnavailableError}, onto a row
56
- * that carries key material: a reader of that segment opens every generation with its key, and a later
57
- * `destroySegment` would attest that shredding it made a readable object unreadable. A caller decides its key
58
- * from the row it read before the write, so this closes the window in which another writer creates the
59
- * segment encrypted meanwhile.
60
- */
61
- cleartext?: boolean;
62
- /**
63
- * Whether the object under `key` is the one the caller wrote, and throws when it cannot tell. Asked only when a
64
- * registry write failed and the row then names `key.generation`, on a registry that does not report its writes'
65
- * tokens ({@link IRegistryDriver.reportsWriteTokens}; one that does settles it by the row's `pointerId` and never
66
- * asks), to tell this publish's write from another writer's at the same number. Every caller that writes the object
67
- * passes it. Without it, such a write that ended without an answer over a row that names `key.generation` cannot be
68
- * settled: the publish throws {@link TransientError} and the caller keeps its object.
69
- */
70
- holdsOwnObject?: () => Promise<boolean>;
71
- /**
72
- * What a publish waits on before it sends a fresh compare-and-swap, after a write that ended without an answer and
73
- * left the row as it was. Without a `sleep`, such a write throws at once instead of being sent again.
74
- */
75
- clock?: Yielder;
76
- /** Spreads that wait (full jitter) so concurrent publishers do not retry together. Without it the wait is the bound. */
77
- rng?: Rng;
78
- /**
79
- * Called once for each registry write that ended without an answer, before the row is read back. A caller whose
80
- * publish then answers `false` can say that it cannot tell whether its own write landed first: the row may have
81
- * held its generation for a while before another writer moved on.
82
- */
83
- onUnanswered?: () => void;
84
- /**
85
- * The clock that judges whether a lease has ended. With it, a publish over a row that holds ended leases drops
86
- * them in the write that moves the pointer, which rewrites the row anyway. Without it, leases are left as they are.
87
- */
88
- leasesNow?: () => number;
89
- /**
90
- * Clear the row's leases in the write that moves the pointer: the caller's collection deletes every generation
91
- * below the new pointer whatever a lease says, as a subject erasure's does.
92
- */
93
- clearLeases?: boolean;
94
- /**
95
- * How many generations below the new pointer the caller keeps. With it, the write that moves the pointer also
96
- * records the window in the row (the entries of `knownGens`: the `keep` most recently current of the generations
97
- * below the new one, and the ones a live lease holds), derived from the very row the write is made against, and the
98
- * result names the generations the window dropped. Without it, or above the most a row collects by name, the result
99
- * names no window, and a collection lists.
100
- */
101
- keep?: number;
102
- /**
103
- * The footer checksum of the object under `key` (the second half of its fingerprint), which the write that moves the
104
- * pointer records in the row's `knownGens` beside the generation. It comes from the object the caller wrote or opened,
105
- * the same one its `summary` describes.
106
- */
107
- footerCrc: number;
108
- /**
109
- * The window a ledger starts with, over a row that has no usable one ({@link readSeed}): the generations a count by
110
- * `keep` keeps, which the write records as the ledger's entries before the new generation.
111
- */
112
- seed?: readonly KnownGeneration[];
113
- }
114
- /**
115
- * Point a segment's registry `currentGen` at `key.generation` — the publish step that makes a freshly-written
116
- * generation the authoritative latest (so registry-aware readers see it). **Forward-only and idempotent:**
117
- * if the registry has no row it creates one; if it's already at/ahead of `key.generation` it's a no-op (an
118
- * out-of-order/duplicate publish never regresses the pointer); otherwise it advances via compare-and-swap,
119
- * retrying a few times under contention. Separated from the Storage write so callers can publish atomically
120
- * after the immutable object is durable (write-then-publish).
121
- *
122
- * **Throws** rather than returning, in two cases, because each would otherwise create a state no reader can
123
- * recover from: a `destroyed` (crypto-shredded) row, where advancing the pointer would name an object encrypted
124
- * under a shredded key; and an advance carrying **new** `wrappedDeks`, which would either strand that key or make
125
- * the row advertise encryption over cleartext generations. Contention that never clears throws
126
- * {@link WriteConflictError} after the retries.
127
- *
128
- * **`expectFrom` turns forward-only into read-modify-write.** Forward-only is the right rule for a writer whose
129
- * content does not depend on what was current — a load computes its ids upstream, so publishing over a newer
130
- * generation loses nothing the loader knew about. It is the WRONG rule for a writer that *derived* its content
131
- * from a particular generation: the erasure rewrite streams generation `from` and clears one bit, so publishing
132
- * it over a newer generation silently discards whatever that generation added. Passing `expectFrom` makes the
133
- * publish land only while the pointer is still exactly there, and return `false` otherwise, so the caller can
134
- * report `superseded` and re-derive. The compare-and-swap itself carries the token read in the same iteration,
135
- * so the check and the write see the same row.
136
- *
137
- * **`expectToken` fences the LINEAGE, and a derived writer needs both.** A generation number identifies a
138
- * generation only within one incarnation of a name: the numbering restarts at `0` once the row is purged and
139
- * the bucket empty, so a name that is retired and re-created has a *different* segment wearing the *same*
140
- * `currentGen`. `expectFrom` alone matched it — and an erasure rewrite then published one incarnation's content
141
- * over another's, deleted the live objects with its `keep: 0` collection, and returned `erased: true`. The row's
142
- * OCC token is the identity that survives this: a shipped registry's later `create` gets a token never issued before
143
- * under that name, but for a collision of probability 2^-128 per pair of incarnations, so no token is reused across them.
144
- *
145
- * Pass the token read alongside `expectFrom` and the publish lands only on the same row it was derived from.
146
- * The check is deliberately **conservative**: the token also changes on writes that are not supersessions at
147
- * all (a `setRetention`, a due-index reindex), so such a write makes a derived publish report `superseded` and
148
- * the caller re-derive. That costs a re-run on a rare, unrelated write; the alternative costs a segment.
149
- *
150
- * **The driver sends a registry write once, and an unanswered one is reconciled by its effect.** A `create` or
151
- * `compareAndSwap` that fails with {@link TransientError} (a throttle, a lost response, a timeout) may or may not
152
- * have landed, and may still be in flight. The publish reads the row once more and decides from what it holds:
153
- *
154
- * - the pointer names `key.generation`, on the incarnation the write was made against (the row's `createdAt`), and the
155
- * write is the caller's own: the write is taken to have landed, and the publish returns `true`. A registry that
156
- * reports each write's token ({@link IRegistryDriver.reportsWriteTokens}) settles it by the row's `pointerId`, which
157
- * names the writer of the write that last set the pointer: the write is the call's own when that is the token of a
158
- * write this call sent. One that does not is settled by `holdsOwnObject` finding the caller's bytes under the number:
159
- * only the writer of a write-once object publishes its number, but a number is taken again once its object is
160
- * deleted, and a load of the same cleartext ids writes the same bytes, so another writer's identical object under
161
- * the number passes for the caller's;
162
- * - the row is still the one the write was made against (the same token, or still no row): the write may land yet. The
163
- * publish then waits, on the injected clock, and sends a **new** write from the row it just read: a request of its
164
- * own, not a replay of the driver's, carrying the same version, so under the registry's fence at most one of the two
165
- * lands and the other meets a conflict, which the next read settles. At most {@link UNANSWERED_RESENDS} follow, after
166
- * a wait under 500 ms, then 1 s, then 2 s (full jitter when an `rng` is given); each is settled the same way. Still
167
- * unanswered, or with no clock to wait on, this throws the registry's own `TransientError` and writes nothing more.
168
- * A caller must not delete its object on it;
169
- * - anything else: the row has moved past the state the write was conditioned on, so that write can never land, and
170
- * the publish goes on as after a lost race, from the row it just read. A pointer at `key.generation` that is not over
171
- * the caller's own object, or is on another incarnation, is a lost race too and answers `false`, never an already
172
- * current re-publish.
173
- *
174
- * A failed read throws `TransientError` too. The same check of the pointer runs after a {@link WriteConflictError},
175
- * so a write that landed and then met itself (a registry whose client re-sent it) is not taken for a lost race.
176
- * The check runs before the next attempt's `expectAbsent`, `expectToken` and `expectFrom` fences, which a write of the
177
- * caller's own that landed would fail. With a registry that reports tokens it names the writer of the write that last
178
- * set the pointer, so another writer's identical object under the number does not pass it; with one that does not,
179
- * that object can.
180
- *
181
- * **A `summary` goes in the same write that moves the pointer**, so a reader that sees this generation as current
182
- * sees the count and metadata that describe it, and every attempt sends the same one. It must be the shape the row's
183
- * keys call for: sealed for an object written with a key, clear for one written without.
184
- */
185
- export declare function publishGenerationKept(registry: IRegistryDriver, key: GenKey, options: PublishOptions): Promise<PublishResult>;
186
- /** What a publish did: whether the pointer is at the generation, and what the write recorded in the row. */
187
- export interface PublishResult {
188
- readonly published: boolean;
189
- /**
190
- * What the write that moved the pointer recorded: the list, the generations the window dropped, and the new row's
191
- * token. Absent when no write of this call is known to have landed (the pointer was already there, or the outcome
192
- * was settled without knowing which write it was), and a collection then lists.
193
- */
194
- readonly kept?: PublishedKept;
195
- /**
196
- * A token of the row whose pointer is at the generation, when it is published: the one the write was given, or the
197
- * row read that showed the pointer there. Every write of a row keeps its incarnation, so this names the incarnation
198
- * the publish landed in, for the audit event.
199
- */
200
- readonly token?: Token;
201
- }
202
- /**
203
- * {@link publishGenerationKept}'s answer to "did the pointer move", for a caller with no use for what it recorded:
204
- * a publish with no `keep` leaves the row recording no list.
205
- */
206
- export declare function publishGeneration(registry: IRegistryDriver, key: GenKey, options: PublishOptions): Promise<boolean>;
207
- export {};
@@ -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;