@cloudbitmaps/roaring 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * prefix. Add `encryption: { keystore }` for encryption-at-rest / crypto-shred.
9
9
  *
10
10
  * Two narrower shapes are also accepted for `storage`: a bare {@link IStorageDriver}, which has no pointer and
11
- * so resolves generations by list-scanning storage (**cleartext and read-only**), and an already-built
12
- * {@link StorageChunkSource} for advanced reader options you configure yourself.
11
+ * so resolves generations by list-scanning storage (**cleartext and read-only**), and a {@link StorageChunkSource}
12
+ * of your own, a read source the store uses as it is (both ports are in `@cloudbitmaps/core/driver-kit`).
13
13
  *
14
14
  * **Data gets in by loading a generation**, never by mutating one: `store.load()` streams a set of ids into one
15
15
  * immutable object and advances the segment's pointer to it. The pointer only moves forward, so a load out-raced by
@@ -23,10 +23,38 @@
23
23
  * `checkConsistency`, `exportSegments` — reuse the store's own drivers, so you never re-pass them (they need the
24
24
  * store built with a backend). See the README and the getting-started guide.
25
25
  */
26
- import type { BudgetOption, GenerationEntry, GenerationMetadata, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, RollbackResult, Clock, StorageChunkSource, ConsistencyReport, DropResult, ExportManifest, ExportOptions, ExportSink, IAuditSink, IStorageDriver, IKeystore, IMetricsSink, StorageBackend, RetentionPolicy, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, SetRetentionResult, PinnedAt, IdRange, SegmentRef } from '@cloudbitmaps/core';
26
+ import type { BudgetOption, GenerationEntry, GenerationMetadata, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, RollbackResult, Clock, Entropy, ConsistencyReport, DropResult, ExportManifest, ExportOptions, ExportSink, IAuditSink, IMetricsSink, RetentionPolicy, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, SetRetentionResult, PinnedAt, SegmentRef } from '@cloudbitmaps/core';
27
+ import type { IKeystore, IStorageDriver, StorageBackend, StorageChunkSource } from '@cloudbitmaps/core/driver-kit';
28
+ import type { DestroyResult, EraseDeps } from '@cloudbitmaps/core';
27
29
  import type { SegmentInfo } from '@cloudbitmaps/core';
28
- import type { CombineExpr, CombineManyFeedRecord, CombineManyOperandStats, CombineManyOutputStats, CombineManyStats } from '@cloudbitmaps/core';
30
+ import type { PreflightResult } from '@cloudbitmaps/core';
31
+ import type { Expr, MaterializeManyFeedRecord, MaterializeManyStats } from '@cloudbitmaps/core';
29
32
  import { deserializePortable } from './roaring-codec.js';
33
+ /**
34
+ * Crypto-shred one segment. It cannot be undone through the library (a registry restore to a point before it can).
35
+ * `confirmSegment` must equal `ref.segment` (a guard against an accidental destroy). A non-encrypted segment is
36
+ * rejected unless you opt in via `allowCleartext`: it has no key to shred, its Storage bytes stay readable, and the
37
+ * call emits a `segment.tombstone` audit event instead of `segment.erase`. A write that gets no answer is settled by
38
+ * reading the row back, waiting between reads on `deps.clock` (the system clock by default).
39
+ */
40
+ export declare function destroySegment(ref: SegmentRef, deps: EraseDeps, options: {
41
+ confirmSegment: string;
42
+ allowCleartext?: boolean;
43
+ audit?: IAuditSink;
44
+ }): Promise<DestroyResult>;
45
+ /**
46
+ * Crypto-shred every segment in a namespace. It cannot be undone through the library. `confirmNamespace` must equal
47
+ * `namespace`. Returns a per-segment result, in the order the registry listed the segments, which is unspecified. A
48
+ * write that gets no answer is settled as {@link destroySegment}'s is.
49
+ */
50
+ export declare function eraseNamespace(namespace: string, deps: EraseDeps, options: {
51
+ confirmNamespace: string;
52
+ allowCleartext?: boolean;
53
+ audit?: IAuditSink;
54
+ maxScanSegments?: number;
55
+ }): Promise<{
56
+ destroyed: DestroyResult[];
57
+ }>;
30
58
  /**
31
59
  * Wiring for a {@link CloudRoaring} store. **Only `storage` is required** — the minimal call is
32
60
  * `new CloudRoaring({ storage: backend })`. A backend resolves generations with one strong read, reads
@@ -50,10 +78,13 @@ export interface CloudRoaringOptions {
50
78
  *
51
79
  * Two lower-level shapes stay accepted for wiring the facade does not cover:
52
80
  *
53
- * - a **raw {@link IStorageDriver}**, which the store wraps in a {@link CrbmStorageChunkSource}. There is no
81
+ * - a **raw {@link IStorageDriver}**, which the store reads through its own `.crbm` reader. There is no
54
82
  * registry in this shape, so the store resolves generations by list-scanning storage: **cleartext and
55
83
  * read-only**. Encrypted segments, the `*Into` verbs and every lifecycle helper need a backend.
56
- * - an already-built {@link StorageChunkSource}, used as-is, for advanced reader options.
84
+ * - a {@link StorageChunkSource} of your own, a read source used as it is: read-only, like the raw driver.
85
+ *
86
+ * The two port types are exported by `@cloudbitmaps/core/driver-kit`, not by this package; to name the option's
87
+ * type without importing them, write `CloudRoaringOptions['storage']`.
57
88
  */
58
89
  readonly storage: StorageBackend | IStorageDriver | StorageChunkSource;
59
90
  /**
@@ -68,10 +99,12 @@ export interface CloudRoaringOptions {
68
99
  readonly encryption?: EncryptionOptions;
69
100
  /**
70
101
  * Resilience: by default every read that answers a query retries **transient** faults (throttling, 5xx, dropped
71
- * connections) with bounded, jittered exponential backoff (see {@link DEFAULT_RETRY_POLICY}): `has`, `count`,
102
+ * connections) with bounded, jittered exponential backoff (by default 4 attempts, a 50 ms base delay doubling each
103
+ * attempt, capped at 2 s, with full jitter): `has`, `count`,
72
104
  * `iterate` and the combines, the `*Into` verbs' reads of their operands included, a pinned handle's reads and
73
105
  * `pin()` itself, and the reads a write makes along the way: a load's guard read of the current generation, and an
74
- * erasure's reads of the generation it rewrites, of the one it wrote and of any other that may still hold the id.
106
+ * erasure's reads of the generation it rewrites, of the one it wrote and of any other that may still hold the id, and
107
+ * the search `subjectReport` and `eraseSubject` make under a destroyed segment (its listing of the bucket included).
75
108
  * This option does not govern writes, nor the calls that read the registry or list the bucket directly (`exists`,
76
109
  * `segments`, `generations`, `getRetention`, and the registry scan `subjectReport`, `exportSegments` and
77
110
  * `checkConsistency` start from): they report a transient fault to their caller, because a conditional write that
@@ -95,7 +128,7 @@ export interface CloudRoaringOptions {
95
128
  * `count`/`iterate`/`intersect`/`union`/`andNot`/`subjectReport`/`eraseSubject` may fan out into before it's
96
129
  * refused with {@link BudgetExceededError} (`eraseSubject` reports an over-budget segment in its ledger instead,
97
130
  * see {@link CloudRoaring.eraseSubject}) — so one runaway op can't drive unbounded GET cost on a shared
98
- * backend. **On by default, generous** ({@link DEFAULT_BUDGET}: 1,000,000 requests — a normal op never hits it).
131
+ * backend. **On by default, generous** (1,000,000 requests — a normal op never hits it).
99
132
  * Tune with `{ maxRequests }`, override per op (on the combines / `subjectReport` / `eraseSubject`), or set
100
133
  * `false` to disable. The check is O(1) (before fan-out), so the hot path is untouched; per-request bytes are
101
134
  * separately size-capped, so bounding requests transitively bounds bytes.
@@ -109,9 +142,12 @@ export interface CloudRoaringOptions {
109
142
  }
110
143
  /** {@link CloudRoaringOptions.cache} — the memory and staleness bounds. */
111
144
  export interface CacheOptions {
112
- /** Ceiling on decoded Storage chunks held in RAM (default 1024). */
145
+ /** Ceiling on decoded Storage chunks held in RAM (default 1024): an integer of at least 1, else `ValidationError`. */
113
146
  readonly maxChunks?: number;
114
- /** Optional TTL on cached chunks (ms). Omit to keep a chunk until it is evicted by the count bound. */
147
+ /**
148
+ * Optional TTL on cached chunks (ms): a finite number above 0, else `ValidationError`. Omit to keep a chunk until it is
149
+ * evicted by the count bound.
150
+ */
115
151
  readonly ttlMs?: number;
116
152
  /**
117
153
  * How long (ms) the store trusts a segment's resolved `currentGen` before re-resolving it on the next read
@@ -139,7 +175,8 @@ export interface CacheOptions {
139
175
  readonly genTtlMs?: number;
140
176
  /**
141
177
  * Ceiling on how many segments' `.crbm` readers (each holding a parsed index) the store keeps open at once
142
- * (default 1024) — the steady-state memory bound for a long-running server that reads across many segments.
178
+ * (default 1024; an integer of at least 1, else `ValidationError`) — the steady-state memory bound for a long-running
179
+ * server that reads across many segments.
143
180
  * Past it the least-recently-used segment's reader is evicted; re-opening it later is one cheap tail GET. With a
144
181
  * timed refresh ({@link CacheOptions.genTtlMs} above 0, and a backend) the store also keeps up to 8 times this many
145
182
  * segments' resolutions (what it read of each segment's row: never an unwrapped key, though the wrapped keys are in
@@ -162,7 +199,7 @@ export interface CacheOptions {
162
199
  * heap (e.g. a 128 MB Lambda); this evicts the least-recently-used reader once the summed weight
163
200
  * would exceed the ceiling — whichever of the count/byte bounds binds first. A sixteenth of it (4 MiB by default)
164
201
  * bounds the segments' resolutions a store with a timed refresh keeps besides. Lower it for memory-tight
165
- * deployments that read across wide segments. A ceiling below two readers' weight (about 7.7 KB for tiny ones) keeps one reader open at a time. Applies whenever the store builds its own read path.
202
+ * deployments that read across wide segments. An integer of at least 1, else `ValidationError`. A ceiling below two readers' weight (about 7.7 KB for tiny ones) keeps one reader open at a time. Applies whenever the store builds its own read path.
166
203
  */
167
204
  readonly readerMaxBytes?: number;
168
205
  }
@@ -176,11 +213,13 @@ export interface EncryptionOptions {
176
213
  readonly keystore?: IKeystore;
177
214
  /**
178
215
  * Refuse to touch a **cleartext** segment — a guard against silently reading, or writing, data that should be
179
- * encrypted. Needs a backend, for the same reason as {@link EncryptionOptions.keystore}. Off by default.
216
+ * encrypted. Needs a backend, for the same reason as {@link EncryptionOptions.keystore}. Off by default. Exactly a
217
+ * boolean: any other value, a truthy string included, throws `ValidationError`.
180
218
  *
181
219
  * It refuses **writes** as well as reads, which is easy to miss: the `*Into` verbs and `eraseSubject` both
182
220
  * carry it into their write path, so on a cleartext segment a materialisation throws and an erasure records
183
- * `note: 'error: requireEncryption: …'` in its ledger rather than erasing. And since a segment's encryption is
221
+ * `note: 'error: KeyUnavailableError: requireEncryption: …'` in its ledger rather than erasing, whatever id it erases and
222
+ * whether or not the id is in the segment: it does not read the segment. And since a segment's encryption is
184
223
  * decided at its **first** generation, this cannot be switched on for a segment that already has one — load
185
224
  * into a new encrypted segment and drop the old one.
186
225
  */
@@ -189,9 +228,9 @@ export interface EncryptionOptions {
189
228
  /**
190
229
  * {@link CloudRoaringOptions.retry} — a partial {@link RetryPolicy} plus the retry callback.
191
230
  *
192
- * Partial on purpose, so tuning one field does not mean restating all five. Anything omitted here keeps its
193
- * {@link DEFAULT_RETRY_POLICY} value, except that an omitted `maxDelayMs` is the `baseDelayMs` when that is larger
194
- * than the default cap.
231
+ * Partial on purpose, so tuning one field does not mean restating all five. Anything omitted here keeps its default
232
+ * (`maxAttempts` 4, `baseDelayMs` 50, `maxDelayMs` 2000, `backoffFactor` 2, `jitter` `'full'`), except that an
233
+ * omitted `maxDelayMs` is the `baseDelayMs` when that is larger than the default cap.
195
234
  */
196
235
  export interface RetryOptions extends Partial<RetryPolicy> {
197
236
  /**
@@ -210,6 +249,13 @@ export interface SeamOptions {
210
249
  readonly clock?: Clock;
211
250
  /** Defaults to `Math.random`-backed. Drives transient-retry jitter. */
212
251
  readonly rng?: Rng;
252
+ /**
253
+ * Defaults to the platform's Web Crypto. The source of every written generation's identity: 12 bytes a cleartext
254
+ * generation's footer carries (an encrypted one carries its index's AEAD nonce), so two loads of the same ids are
255
+ * two objects. Inject a deterministic one to replay a run or pin bytes in a test; never inject a constant in
256
+ * production, since a source that repeats makes the objects it writes alike again.
257
+ */
258
+ readonly entropy?: Entropy;
213
259
  }
214
260
  /**
215
261
  * What {@link CloudRoaring.segment} takes besides the name. A handle carries no deadline: check one where you read,
@@ -217,24 +263,39 @@ export interface SeamOptions {
217
263
  * passed here is refused with {@link ValidationError}.
218
264
  */
219
265
  export interface SegmentOptions {
266
+ /** The segment's namespace, or `undefined` for the default one. */
220
267
  readonly namespace?: string;
221
268
  }
222
269
  /** A segment reference in a subject report / erasure ledger. */
223
270
  export interface SubjectSegmentRef {
271
+ /** The segment's name. */
224
272
  readonly segment: string;
273
+ /** The segment's namespace, or `undefined` for the default one. */
225
274
  readonly namespace?: string;
226
275
  }
227
- /** Result of {@link CloudRoaring.subjectReport} — the segments an id is a member of (over registered segments). */
276
+ /** Result of {@link CloudRoaring.subjectReport}: the segments an id is a member of, and the destroyed segments whose leftover objects hold it. */
228
277
  export interface SubjectReport {
278
+ /** The id the report is about. */
229
279
  readonly id: number;
230
280
  /** The registered segments the id is currently a member of. */
231
281
  readonly segments: SubjectSegmentRef[];
282
+ /**
283
+ * The destroyed segments whose leftover objects still hold the id in the clear: one destroyed with `allowCleartext`,
284
+ * dropped while a load wrote an object its sweep did not catch, or crypto-shredded over an object a cleartext writer
285
+ * left. No read resolves them, so the id is a member of none, but its bytes are in the bucket, and `eraseSubject`
286
+ * deletes them. A crypto-shredded segment's own objects are sealed under a key no row holds and are never a holder,
287
+ * so it is listed only when an object left under it in the clear holds the id. A destroyed segment with no
288
+ * object left is not listed either. `[]` when none.
289
+ */
290
+ readonly tombstones: SubjectSegmentRef[];
232
291
  /** How many registered segments were scanned (the completeness denominator). */
233
292
  readonly scannedSegments: number;
234
293
  }
235
294
  /** One segment's entry in an erasure ledger (see {@link EraseSubjectResult}). */
236
295
  export interface SubjectErasureEntry {
296
+ /** The segment's name. */
237
297
  readonly segment: string;
298
+ /** The segment's namespace, or `undefined` for the default one. */
238
299
  readonly namespace?: string;
239
300
  /**
240
301
  * True iff the id **was** in the segment and **no generation of it holds the id now** — not the current one, not
@@ -253,18 +314,19 @@ export interface SubjectErasureEntry {
253
314
  readonly generation?: number;
254
315
  /**
255
316
  * Why the id was NOT erased from this segment, when `erased` is false. `'superseded'` — another writer (a load,
256
- * another erasure or a rollback) moved the pointer, or replaced an object the call meant to delete, while the call
257
- * was in flight, so **this call** did not erase the id;
317
+ * another erasure or a rollback) moved the pointer, replaced an object the call meant to delete, or left the row with
318
+ * no pointer (a purge and re-creation, or a hand-made write), while the call was in flight, so **this call** did
319
+ * not erase the id;
258
320
  * re-run against the new generation, which erases it if it is still there and reports nothing for the segment
259
- * if the racing writer already removed it. `` `error: <message>` `` — an isolated per-segment fault
321
+ * if the racing writer already removed it. `` `error: <class>: <message>` `` — an isolated per-segment fault, named by its error's class
260
322
  * (per-segment faults are recorded so one segment can't discard the whole ledger); re-run after fixing the
261
323
  * fault. A budget refusal is one of these: the segment's generations did not fit `budget`, so it was not finished;
262
324
  * re-run with a higher `budget`. A fault that landed once part of the work was already done — a Storage `delete` fault, a collect that
263
325
  * could not prove the segment was still the same one, or a generation still holding the id when the bucket was
264
326
  * listed at the end, whether or not a rewrite was published first — also re-runs, but **read what the re-run
265
327
  * says**: it usually reports `erased: true` against the generation it found the id in, it reports
266
- * nothing at all if a racing collector took that generation first (the bit is gone, but no run holds a
267
- * receipt for it), and if the segment's row has since been purged it is no longer scanned at all — anything
328
+ * nothing at all if a racing collector took that generation first, or this call's own delete of it landed after a
329
+ * fault the call could not settle (the bit is gone, but no run holds a receipt for it), and if the segment's row has since been purged it is no longer scanned at all — anything
268
330
  * left in its bucket is an orphan, which `store.generations(ref)` lists and `store.dropSegment(ref, {
269
331
  * confirmSegment })` deletes; `checkConsistency` and the collection a load runs read rows, so neither reaches it.
270
332
  * **Segments the id is not in
@@ -278,6 +340,7 @@ export interface SubjectErasureEntry {
278
340
  * return value only (no library-side persistence): persist it / route it to your audit sink as you see fit.
279
341
  */
280
342
  export interface EraseSubjectResult {
343
+ /** The id that was erased. */
281
344
  readonly id: number;
282
345
  /** Per-segment records for the segments the id was found in (absent segments are not listed). */
283
346
  readonly erasedFrom: SubjectErasureEntry[];
@@ -292,21 +355,6 @@ export interface EraseSubjectResult {
292
355
  * in prose means the compiler enforces it, and a caller's exhaustive `switch` has no dead branch.
293
356
  */
294
357
  export type MaterializeRefusal = Exclude<LoadRefusal, 'superseded'>;
295
- /**
296
- * An expression over the operands of a {@link CloudRoaring.materializeMany} call: a string names an operand; a node holds
297
- * exactly one of `and`, `or` or `andNot` over a non-empty list of expressions. `andNot` subtracts every entry after the
298
- * first from the first, so it takes at least two. Plain JSON, so it can be logged and sent between tasks.
299
- */
300
- export type Expr = CombineExpr;
301
- /** What the call did to one operand: see {@link MaterializeManyStats.operands}. */
302
- export type MaterializeManyOperandStats = CombineManyOperandStats;
303
- /** What the call did to one output: see {@link MaterializeManyStats.outputs}. */
304
- export type MaterializeManyOutputStats = CombineManyOutputStats;
305
- /**
306
- * What a {@link CloudRoaring.materializeMany} call did: the groups it ran, its requests by class, the chunk-read budget it
307
- * used, the most it held resident, and, for each operand, the generation it was pinned at, began at and ended at.
308
- */
309
- export type MaterializeManyStats = CombineManyStats;
310
358
  /** One output of a {@link CloudRoaring.materializeMany} call. */
311
359
  export interface MaterializeManyOutput {
312
360
  /** The segment this output becomes a new generation of. Not an operand of the call, and named by no other output. */
@@ -315,26 +363,17 @@ export interface MaterializeManyOutput {
315
363
  readonly expr: Expr;
316
364
  /** Subtracted from the result, as the entries of an `andNot` after the first are. Pinned operands it names are re-checked before the publish. */
317
365
  readonly exclude?: Expr[];
318
- /** As on {@link MaterializeOptions}. */
366
+ /** As {@link CombineOptions.guard} on an `*Into` verb. */
319
367
  readonly guard?: LoadGuard;
320
- /** As on {@link MaterializeOptions}. */
368
+ /** As {@link CombineOptions.allowEmpty} on an `*Into` verb. */
321
369
  readonly allowEmpty?: boolean;
322
- /** As on {@link MaterializeOptions}. */
370
+ /** As {@link CombineOptions.metadata} on an `*Into` verb. */
323
371
  readonly metadata?: GenerationMetadata;
324
- /** As on {@link MaterializeOptions}. */
372
+ /** As {@link CombineOptions.audit} on an `*Into` verb. */
325
373
  readonly audit?: IAuditSink;
326
- /** Generations to keep below the new one, as on {@link MaterializeOptions}; overrides the call's `keep`. */
374
+ /** Generations to keep below the new one, as {@link CombineOptions.keep} on an `*Into` verb; overrides the call's `keep`. */
327
375
  readonly keep?: number;
328
376
  }
329
- /**
330
- * One record of the feed of {@link CloudRoaring.materializeMany}: the ids some of the fed operands hold at one chunk key.
331
- * `key` is `id >>> 16`, an integer from 0 to 65,535, never below the previous record's; a key may arrive as several
332
- * consecutive records, each carrying some of the operands, and one record per key is the usual shape. `operands` maps a
333
- * declared fed name to its ids at the key: a real `Uint32Array` (not an `Int32Array`, `Buffer`, array or look-alike),
334
- * strictly ascending, every id inside the key. A name is given at most once per key, and an empty array is the same as
335
- * leaving the name out. The record is copied as it arrives, so the caller may drop or reuse its arrays once it has yielded it.
336
- */
337
- export type MaterializeManyFeedRecord = CombineManyFeedRecord;
338
377
  /**
339
378
  * Operands that arrive as records in chunk-key order instead of being stored, for {@link MaterializeManyOptions.feed}.
340
379
  * Outputs name a fed operand as they name a stored one.
@@ -353,7 +392,7 @@ export interface MaterializeManyFeed {
353
392
  readonly counts: (() => Record<string, number> | Promise<Record<string, number>>) | Record<string, number>;
354
393
  }
355
394
  /** The options of {@link CloudRoaring.materializeMany}. */
356
- export interface MaterializeManyOptions extends IdRange {
395
+ export interface MaterializeManyOptions extends Pick<CombineOptions, 'after' | 'through'> {
357
396
  /**
358
397
  * The stored segments the expressions name. A set the caller holds in memory arrives as a {@link MaterializeManyOptions.feed}.
359
398
  */
@@ -424,6 +463,10 @@ export type MaterializeManyOutcome = MaterializeResult | {
424
463
  export interface MaterializeManyRun {
425
464
  /** One per output, in the order given: one output's refusal or error never stops another. */
426
465
  readonly outputs: MaterializeManyOutcome[];
466
+ /**
467
+ * What the call did: the groups it ran, its requests by class, its budget and memory, and the figures of each operand
468
+ * and output.
469
+ */
427
470
  readonly stats: MaterializeManyStats;
428
471
  }
429
472
  /**
@@ -431,6 +474,7 @@ export interface MaterializeManyRun {
431
474
  * judged against its `dest` as its publish would be, and nothing is written. See {@link CloudRoaring.materializeMany}.
432
475
  */
433
476
  export interface MaterializeManyDryRunOptions extends MaterializeManyOptions {
477
+ /** `true` makes the call a dry run: every output is computed and judged, and nothing is written. */
434
478
  readonly dryRun: true;
435
479
  }
436
480
  /**
@@ -439,7 +483,9 @@ export interface MaterializeManyDryRunOptions extends MaterializeManyOptions {
439
483
  * run for a publish.
440
484
  */
441
485
  export interface MaterializeDryRunResult {
486
+ /** Always `true`: the output was computed and judged, not published. */
442
487
  readonly dryRun: true;
488
+ /** Always `false`: nothing was written. */
443
489
  readonly published: false;
444
490
  /** Distinct ids the output would hold. */
445
491
  readonly cardinality: number;
@@ -460,6 +506,10 @@ export type MaterializeManyDryRunOutcome = MaterializeDryRunResult | {
460
506
  export interface MaterializeManyDryRun {
461
507
  /** One per output, in the order given. */
462
508
  readonly outputs: MaterializeManyDryRunOutcome[];
509
+ /**
510
+ * What the dry run did: the groups it ran, its requests by class, its budget and memory, and the figures of each
511
+ * operand and output.
512
+ */
463
513
  readonly stats: MaterializeManyStats;
464
514
  }
465
515
  /** What an `*Into` verb wrote: the new generation of the destination, and whether it became current. */
@@ -498,21 +548,32 @@ export interface MaterializeResult {
498
548
  /**
499
549
  * The superseded generations collected after publishing. Empty when nothing was published — and **also
500
550
  * empty on a successful publish** unless you passed `keep`, because a materialisation collects nothing by
501
- * default. See {@link MaterializeOptions.keep}.
551
+ * default. See {@link CombineOptions.keep}.
502
552
  */
503
553
  readonly collected: readonly number[];
504
554
  }
555
+ /**
556
+ * The store: Roaring bitmap segments kept as write-once `.crbm` generations in the storage you give it, read through
557
+ * a bounded in-memory cache. Build one per process from a backend,
558
+ * `new CloudRoaring({ storage: new S3Storage({ bucket }) })`; building does no I/O. `store.segment(name)` is a handle
559
+ * for `has`, `count`, `iterate` and the combines, and `store.load(ref, ids)` replaces a segment's contents with a new
560
+ * generation.
561
+ */
505
562
  export declare class CloudRoaring {
506
563
  private readonly engine;
564
+ /** This store's identity: every handle it makes carries this object, and a combine compares it by reference. */
565
+ private readonly origin;
507
566
  private readonly cache;
508
567
  /** The store's retry options, for a pinned engine to read through as the store's own does; undefined when off. */
509
568
  private readonly retryOptions;
510
569
  private readonly crbmSource;
511
570
  private readonly clock;
512
571
  private readonly rng;
572
+ private readonly entropy;
513
573
  private readonly metrics;
514
574
  private readonly storageDriver;
515
575
  private readonly registry;
576
+ private readonly backend;
516
577
  private readonly keystore;
517
578
  private readonly requireEncryption;
518
579
  /** Resolved store-level per-op budget (null = disabled); the admin scans use it, with a per-op override. */
@@ -536,6 +597,19 @@ export declare class CloudRoaring {
536
597
  * no `next` or a keystore with no `createDek` builds a store that throws a raw `TypeError` at its first load or read.
537
598
  */
538
599
  private static rejectHollowSeams;
600
+ /**
601
+ * Refuse a `cache` or `encryption` value that can never be honoured, by the name the caller wrote. The sizes reach
602
+ * the caches under their own internal names, so left to them an error would name `maxEntries` or `maxBytes`; and
603
+ * `encryption.required` must be exactly a boolean, since the read path took a truthy string for `true` while every
604
+ * write path did not, which refused cleartext reads and still wrote cleartext.
605
+ */
606
+ private static rejectBadValues;
607
+ /**
608
+ * Builds a store. Only `storage` is required: pass a backend such as `S3Storage` or `MemoryStorage`. `cache`,
609
+ * `encryption`, `retry`, `metrics`, `budget` and `seams` tune it. An option it does not take, a `cache` size or `ttlMs`
610
+ * that is not a usable number, an `encryption.required` that is not a boolean, or a seam missing a method the store
611
+ * calls, throws `ValidationError`.
612
+ */
539
613
  constructor(options: CloudRoaringOptions);
540
614
  /**
541
615
  * The write-side deps, from the store's own drivers, for the lifecycle helpers and the `*Into` verbs. Requires
@@ -744,21 +818,50 @@ export declare class CloudRoaring {
744
818
  * tax every load for a rare request; this admin scan is `O(registered segments)` and touches no hot path. Needs
745
819
  * a storage backend (throws {@link UnsupportedError} otherwise).
746
820
  *
821
+ * **Scope: one `namespace`, or `{ allNamespaces: true }`, never both** (both throws `ValidationError`, as does
822
+ * neither). A segment loaded with no `namespace` is in no namespace you can name: only `{ allNamespaces: true }`
823
+ * reaches it. `'_default'`, the label the export tool and the disaster-recovery samples print, and the bucket's
824
+ * object keys use, for such a segment, is an ordinary namespace name here, and passing it scans a namespace of that name, not those segments.
825
+ *
747
826
  * **Current as of the registry row, not as of the reader's cache.** Each segment's resolved generation is
748
827
  * compared with the row the scan listed, and a segment whose cached generation differs is re-resolved before the
749
828
  * read, so a load or an erasure from another process shows at once — whatever `cache.genTtlMs` is, `0` included.
750
829
  * The comparison is on the generation **and** the row's token, so a segment retired, purged and loaded again from
751
830
  * generation 0 while this store held its old generation 0 is told apart too.
752
- */
753
- subjectReport(id: number, options?: {
754
- namespace?: string;
755
- allNamespaces?: boolean;
831
+ *
832
+ * **A destroyed segment is searched as {@link CloudRoaring.eraseSubject} searches it.** No read resolves a
833
+ * tombstone, so the id is a member of none, but a cleartext `destroySegment` (`allowCleartext`), or a `dropSegment`
834
+ * whose sweep missed an object a load was still writing, leaves objects anyone with bucket access can read. Each
835
+ * object left under the tombstone is opened newest first until one holds the id, and the segment is then listed in
836
+ * `tombstones`, never in `segments`. A crypto-shredded segment's own objects are sealed under a key no row holds and are
837
+ * never a holder: it is listed only when an object left under it in the clear holds the id. The cost is one listing per destroyed segment in scope (1 request when nothing is left), and for each
838
+ * object opened a tail read and, where its index lists the id's chunk, a chunk read (two tail reads for an encrypted
839
+ * object, which never holds the id): 7 requests for a cleartext destroy with 3 objects, 21 for 10. The budget is
840
+ * charged one unit for each object opened past the one the row names, as `eraseSubject` charges. A report that runs
841
+ * out, or whose listing or read fails after the store's retry, rejects rather than under-reporting, so one leftover
842
+ * object that cannot be opened makes every report over its scope reject until it is removed (`generations(ref)` lists
843
+ * what is left, `dropSegment` deletes it). A report refused for budget while it searches a destroyed segment reports
844
+ * `spent` as `limit + 1`, a lower bound.
845
+ */
846
+ subjectReport(id: number, options: {
756
847
  concurrency?: number;
757
848
  budget?: BudgetOption;
758
- }): Promise<SubjectReport>;
849
+ } & ({
850
+ namespace: string;
851
+ allNamespaces?: false;
852
+ } | {
853
+ allNamespaces: true;
854
+ namespace?: undefined;
855
+ })): Promise<SubjectReport>;
759
856
  /**
760
857
  * **Subject erasure (GDPR Art. 17): remove an id from every segment it's in, with the bit physically gone
761
- * from the bucket on return.**
858
+ * from the bucket on return for each segment the ledger reports `erased: true`.** A noncurrent version, a replica
859
+ * or a backup keeps the old object until its own lifecycle removes it.
860
+ *
861
+ * **Scope: one `namespace`, or `{ allNamespaces: true }`, never both** (both throws `ValidationError`, as does
862
+ * neither). A segment loaded with no `namespace` is in no namespace you can name: only `{ allNamespaces: true }`
863
+ * reaches it. `'_default'`, the label the export tool and the disaster-recovery samples print, and the bucket's
864
+ * object keys use, for such a segment, is an ordinary namespace name here, and passing it scans a namespace of that name, not those segments.
762
865
  *
763
866
  * For each **registered** segment the id is a member of, `eraseIdFromSegment` rewrites the current generation
764
867
  * without the id — every chunk streamed through, one bit cleared — publishes the rewrite **fenced on the
@@ -823,21 +926,27 @@ export declare class CloudRoaring {
823
926
  * `O(registered segments + superseded generations)`, no hot-path cost: a segment whose current generation lacks
824
927
  * the id is searched generation by generation, one open for each other generation in its bucket, and with the
825
928
  * keep-everything default of the `*Into` verbs that is one per generation the segment ever had. The per-op
826
- * `budget` is charged one unit for each segment and one for each generation opened beyond the one its row names,
827
- * so a fleet with long histories can exhaust it where the segment count alone would not. A segment that does is
929
+ * `budget` is charged one unit for each segment and one for each generation opened beyond the one its row names, so
930
+ * a fleet with long histories can exhaust it where the segment count alone would not. A generation is charged at its
931
+ * first open only: a later open of it in the call, a retry of a failed read included, costs no more units. A segment
932
+ * that exhausts the budget is
828
933
  * reported `erased: false` with an `error:` note, before it deletes anything (the one refusal that can come after a
829
934
  * rewrite is the last check for a generation a concurrent writer left behind), and the
830
935
  * rest of the scan continues: the call itself does not throw for it. Per-subject crypto-shred is infeasible (a subject's bit is
831
936
  * co-mingled in a shared container), so this is the single-subject erasure route; whole-segment/tenant erasure
832
937
  * is `dropSegment` / the `destroySegment`/`eraseNamespace` free functions.
833
938
  */
834
- eraseSubject(id: number, options?: {
835
- namespace?: string;
836
- allNamespaces?: boolean;
939
+ eraseSubject(id: number, options: {
837
940
  audit?: IAuditSink;
838
941
  concurrency?: number;
839
942
  budget?: BudgetOption;
840
- }): Promise<EraseSubjectResult>;
943
+ } & ({
944
+ namespace: string;
945
+ allNamespaces?: false;
946
+ } | {
947
+ allNamespaces: true;
948
+ namespace?: undefined;
949
+ })): Promise<EraseSubjectResult>;
841
950
  private eraseSubjectRun;
842
951
  /**
843
952
  * Every generation still in the bucket for this segment, ascending, with the current one marked.
@@ -847,6 +956,7 @@ export declare class CloudRoaring {
847
956
  * choose from, which is the reason to look at it. One registry read and one listing; it does not open the objects.
848
957
  * Each entry says whether the row knows the generation (`known`): a known generation needs no `allowUnknown`; an unknown
849
958
  * one does, except below a row's pointer when the row has no usable record or the target is older than a full one reaches.
959
+ * No entry is `current` on a destroyed segment: its row keeps the pointer it had, but no read resolves through it.
850
960
  *
851
961
  * Needs a backend.
852
962
  */
@@ -926,8 +1036,9 @@ export declare class CloudRoaring {
926
1036
  * {@link ValidationError} before any request to storage, and a known one whose object carries another checksum than
927
1037
  * the recorded one throws {@link IntegrityError}, with or without a keystore, since the footer is in the clear. A
928
1038
  * generation not in the bucket (collected, or never written) throws `NotFoundError` naming what *is* available, a
929
- * crypto-shredded segment throws {@link ValidationError} because every generation of it is unreadable, and a target
930
- * that is cleartext under an encrypted segment, or encrypted under a cleartext one, throws {@link IntegrityError} from
1039
+ * destroyed segment (crypto-shredded, destroyed in the clear or dropped) throws {@link ValidationError}, because no
1040
+ * generation resolves through a destroyed row, and a target that is cleartext under an encrypted segment, or
1041
+ * encrypted under a cleartext one, throws {@link IntegrityError} from
931
1042
  * one tail read of it, because every read would refuse it. Rolling to the generation already current is a no-op that
932
1043
  * reports itself, once one footer read finds its object in the bucket (and the one the row recorded); when the object is
933
1044
  * gone it throws `NotFoundError` naming what is.
@@ -938,8 +1049,10 @@ export declare class CloudRoaring {
938
1049
  * key (it holds none of the key's KEKs, one it holds does not unwrap it, or it throws `TransientError` or times out
939
1050
  * as `AbortSignal.timeout()` and the AWS SDK report a timeout), a rollback onto a known generation still happens,
940
1051
  * opening nothing, and the row carries no summary of the target. Any other failure of the keystore is
941
- * thrown. A swap of the pointer that gets no answer is settled by reading the row: one that landed, the row being the
942
- * one the swap leaves, is checked as any is, and otherwise its `TransientError` is thrown. After the swap the target's
1052
+ * thrown. A swap of the pointer that gets no answer, or that meets a conflict, is settled by reading the row: one
1053
+ * that landed, the row's pointer being at the target over a write this call sent (the write token, on a registry that
1054
+ * reports them), is checked as any is, and otherwise its `TransientError` is thrown, or a `WriteConflictError` after
1055
+ * a conflict, with no move recorded. After the swap the target's
943
1056
  * footer is read once more, and the pointer put back if the object is gone or is another.
944
1057
  *
945
1058
  * It deletes nothing. The generations above the new pointer stay put — which is what makes this reversible —
@@ -978,9 +1091,15 @@ export declare class CloudRoaring {
978
1091
  * `input` is ids (any sync or async iterable of integers in `[0, 2^32)`), or a whole bitmap: `{ bitmap }`, anything
979
1092
  * with `serialize('portable')` such as `roaring`'s `RoaringBitmap32`, or `{ serialized }`, portable Roaring bytes.
980
1093
  * A bitmap is checked (size cap, structure, safe deserializer) before the first request, written from its own
981
- * containers with no per-id work, and gives the generation byte for byte the one its ids would. A bare
982
- * `RoaringBitmap32` passed as ids loads as `{ bitmap }`. A `Uint8Array` or `Buffer` passed as ids is refused with
983
- * `ValidationError`, since each byte would load as an id: pass bytes as `{ serialized }`.
1094
+ * containers with no per-id work, and gives the generation chunk for chunk what its ids would. A bare
1095
+ * `RoaringBitmap32`, or a bitmap `deserializePortable` returned, passed as ids loads as `{ bitmap }`. A
1096
+ * `Uint8Array` or `Buffer` passed as ids is refused with `ValidationError`, since each byte would load as an id:
1097
+ * pass bytes as `{ serialized }`.
1098
+ *
1099
+ * Ids are held whole in memory before the object is written: the generation's bitmaps and a buffer of up to about 28
1100
+ * MB. Ids that already ascend can be loaded with `sorted: true` instead, which writes them one chunk at a time while
1101
+ * the object uploads, so the load holds one chunk and the writer's index (up to about 7 MiB, about 8 MiB for an
1102
+ * encrypted segment, derived), whatever the segment's size, and writes the same object ({@link LoadOptions.sorted}).
984
1103
  *
985
1104
  * The whole write path in one call: take the next generation number, write the object, check the result is
986
1105
  * plausible, move the pointer, collect what the move superseded. Composed by hand those are four functions and
@@ -1024,7 +1143,7 @@ export declare class CloudRoaring {
1024
1143
  * its leases), `destroyed` or gone, or has key material when the load wrote cleartext, which has no place in an
1025
1144
  * encrypted segment's bucket. Under a row that is gone or keyed, one read of the object's footer must match what the
1026
1145
  * load wrote first. Once another write has otherwise changed the row, the generation number it holds may name another
1027
- * writer's object, and a footer names bytes, not the load that wrote them, so it leaves the orphan rather than risk
1146
+ * writer's object, and the row does not say whether that write took this number, so it leaves the orphan rather than risk
1028
1147
  * deleting live data. A publish never adds the orphan to the row's list, and a load's listing deletes it once a
1029
1148
  * generation above it is current, unless the row's list names it; where the row records none (after a rollback, or
1030
1149
  * with a `keep` above 64), that listing's window can keep it and, at a `keep` up to 64, record it, and the publishes
@@ -1039,7 +1158,14 @@ export declare class CloudRoaring {
1039
1158
  * does the first load of a row that records none, which then records it. {@link LoadResult.collected} then names
1040
1159
  * what the pass deleted by name, and one name may have been gone already.
1041
1160
  *
1042
- * What it **throws** for is a fault rather than an outcome: invalid options or ids, and a crypto-shredded
1161
+ * `input` cannot be a stream of this library (`iterate`, `everyNth`, `intersect`, `union`, `andNot`, as returned) that
1162
+ * read the segment being loaded, from this store or one built over the same registry driver: a load publishes over
1163
+ * whatever the segment is by then, so it would replace another writer's newer load with a result built from an older
1164
+ * one, and it throws {@link ValidationError}. Ids read from another store over another backend load as any ids do.
1165
+ * Two separate store instances over one bucket cannot be told apart, so this does not cover them; this release has no
1166
+ * fenced in-place update, so write the result into a new segment and point readers at it.
1167
+ *
1168
+ * What it **throws** for is a fault rather than an outcome: invalid options or ids, and a destroyed
1043
1169
  * segment (`ValidationError`); a key the keystore cannot provide (`KeyUnavailableError`); a current generation
1044
1170
  * that will not open when a guard has to read its size, which it does only when the row has no usable summary of it
1045
1171
  * (`IntegrityError`); a driver failure; and a fault in the collection's own reads, listing or deletes, or in the write
@@ -1063,11 +1189,14 @@ export declare class CloudRoaring {
1063
1189
  */
1064
1190
  load(ref: SegmentRef, input: LoadInput, options?: LoadOptions): Promise<LoadResult>;
1065
1191
  /**
1066
- * **Dispose of a segment — tombstone it, then delete its storage objects.** Irreversible.
1192
+ * **Dispose of a segment — tombstone it, then delete its storage objects.** It cannot be undone through the
1193
+ * library (a restore from the registry's and the bucket's versions or backups can).
1067
1194
  *
1068
- * The operation a rolling window needs: `destroySegment` crypto-shreds (bytes unreadable everywhere including
1069
- * backups, but still sitting in your bucket and still billed, and it requires encryption), while this one
1070
- * removes the storage and works on a cleartext segment. On an encrypted segment it does both.
1195
+ * The operation a rolling window needs: `destroySegment` crypto-shreds (it removes the key from the segment's
1196
+ * current registry row, so the bytes are unreadable everywhere, backups included, once no retained copy of the row
1197
+ * holds the key or every KEK that wrapped it is destroyed; the objects still sit in your bucket and are still
1198
+ * billed, and it requires encryption), while this one removes the storage and works on a cleartext segment. On an
1199
+ * encrypted segment it does both.
1071
1200
  *
1072
1201
  * Pass `{ dryRun: true }` first — it reports the generations it *would* delete and changes nothing. That
1073
1202
  * matters more than the `confirmSegment` guard for anything automated, because in a loop the guard is the same
@@ -1098,7 +1227,7 @@ export declare class CloudRoaring {
1098
1227
  * segment may answer from its cached generation + cached chunks until that window lapses, or, while the registry
1099
1228
  * cannot be read because of a transient fault, until a retry 500 ms apart reaches it again. A reader that never
1100
1229
  * touched it sees empty at once. **That bound needs a registry and `cache.genTtlMs > 0`.** A store with no registry
1101
- * (a bare `IStorageDriver`), with `cache.genTtlMs: 0`, or on a storage source built with no clock, has no timed
1230
+ * (a bare `IStorageDriver`), with `cache.genTtlMs: 0`, or on a `StorageChunkSource` of your own, has no timed
1102
1231
  * refresh. It notices the drop only when a read has to fetch
1103
1232
  * from a deleted generation or its reader cache evicts the segment, and until then a chunk it has cached
1104
1233
  * answers without reaching storage, so it can keep answering `true` for a dropped segment indefinitely. Tell
@@ -1113,8 +1242,9 @@ export declare class CloudRoaring {
1113
1242
  audit?: IAuditSink;
1114
1243
  }): Promise<DropResult>;
1115
1244
  /**
1116
- * **Record when this segment becomes eligible for retirement.** One registry write; nothing is deleted here,
1117
- * and nothing starts running. `retireExpired` is what acts on the policy, and **you** decide when that runs —
1245
+ * **Record when this segment becomes eligible for retirement.** One policy write, plus a best-effort write of its
1246
+ * due-index pointer (and removal of the old pointer when the expiry day moves); no data is deleted here, and
1247
+ * nothing starts running. `retireExpired` is what acts on the policy, and **you** decide when that runs —
1118
1248
  * an EventBridge rule, a CronJob, a queue consumer, whatever your deployment already has. This library starts
1119
1249
  * no background thread (it has to work identically in a Lambda, an edge isolate and a long-lived server).
1120
1250
  *
@@ -1134,9 +1264,9 @@ export declare class CloudRoaring {
1134
1264
  * enumerable by the sweep; the first load then publishes onto that row.
1135
1265
  *
1136
1266
  * A value in the past is legal and means "eligible on the next sweep" — backfilling a policy onto existing
1137
- * buckets is normal. A value below `MIN_EXPIRES_AT_MS` (2001-09-09) is rejected: it is almost certainly epoch
1267
+ * buckets is normal. A value below `1_000_000_000_000` (2001-09-09) is rejected: it is almost certainly epoch
1138
1268
  * **seconds**, which would read as long-expired and retire the segment on the next pass. Needs a storage
1139
- * backend (throws {@link UnsupportedError} otherwise), and refuses a crypto-shredded segment.
1269
+ * backend (throws {@link UnsupportedError} otherwise), and refuses a destroyed segment.
1140
1270
  */
1141
1271
  setRetention(ref: SegmentRef, policy: RetentionPolicy): Promise<SetRetentionResult>;
1142
1272
  /**
@@ -1147,8 +1277,10 @@ export declare class CloudRoaring {
1147
1277
  */
1148
1278
  getRetention(ref: SegmentRef): Promise<RetentionPolicy | null | 'invalid'>;
1149
1279
  /**
1150
- * **Cancel a segment's expiry** so no sweep retires it. Returns whether a policy was actually removed (`false`
1151
- * when there was none). A separate verb from `setRetention` on purpose: "never expire" as a magic value passed
1280
+ * **Cancel a segment's expiry** so no sweep retires it. Returns whether this call removed a policy (`false`
1281
+ * when there was none, and also when a write that got no answer cannot be shown to be this call's and the
1282
+ * row holds no expiry: the expiry is gone either way, and its due-index pointer is removed. A later write that set an
1283
+ * expiry is cleared too, and the answer is `true`). A separate verb from `setRetention` on purpose: "never expire" as a magic value passed
1152
1284
  * to the setter is how a typo becomes a deletion.
1153
1285
  */
1154
1286
  clearRetention(ref: SegmentRef): Promise<boolean>;
@@ -1170,20 +1302,22 @@ export declare class CloudRoaring {
1170
1302
  *
1171
1303
  * const swept = await store.retireExpired({ namespace: 'active-daily' });
1172
1304
  * for (const e of swept.entries) {
1173
- * if (e.action === 'skipped') console.warn(`${e.segment}: ${e.reason}`); // invalid-policy / limit / failed: …
1305
+ * // invalid-policy / policy-changed / tombstone-not-empty / failed: …
1306
+ * if (e.action === 'skipped') console.warn(`${e.segment}: ${e.reason}`);
1174
1307
  * if (e.action === 'retired' && e.result.generationsRemaining.length > 0) {
1175
1308
  * console.warn(`${e.segment}: storage not fully reclaimed — re-run`);
1176
1309
  * }
1177
1310
  * }
1178
- * if (swept.limited) scheduleAnotherPassSoon(); // more are still eligible
1311
+ * if (swept.limited) scheduleAnotherPassSoon(); // more work (retirements or purges) remains
1179
1312
  * ```
1180
1313
  *
1181
1314
  * **Read the ledger.** A per-segment *fault* is an entry, not an exception — a throw from the middle of a fleet
1182
1315
  * sweep would leave the caller unable to say which segments were retired, having already retired some. (A bad
1183
1316
  * argument does throw, and so does a fleet larger than `maxScanSegments`.) `limited: true` means the per-cycle
1184
- * `limit` (default 100) cut the pass short and more are eligible. That cap is charged on **attempts**, not
1185
- * successes, which is what makes it a real bound: `dropSegment` writes the tombstone before sweeping Storage, so a
1186
- * fault in the Storage phase is a segment that is already retired. Retirements are sequential, so `limit` is a
1317
+ * `limit` (default 100) cut the pass short, and more work (retirements or purges) remains: `limit` caps the
1318
+ * retirements and tombstone purges one call attempts, together, and a segment it deferred gets no entry. That cap
1319
+ * is charged on **attempts**, not successes, which is what makes it a real bound: `dropSegment` writes the
1320
+ * tombstone before sweeping Storage, so a fault in the Storage phase is a segment that is already retired. Retirements are sequential, so `limit` is a
1187
1321
  * wall-clock knob too, and `retired` counts deletions only — a dry run reports `wouldRetire` instead, so a
1188
1322
  * dashboard summing `retired` can never show a phantom deletion.
1189
1323
  *
@@ -1193,9 +1327,11 @@ export declare class CloudRoaring {
1193
1327
  * charged to `limit`, so it never holds the retirements behind it, and purging stops for the rest of the call after
1194
1328
  * three refused purges in a row (a purge that succeeds starts the count again).
1195
1329
  *
1196
- * It also **deletes the tombstone rows its own past retirements left**, after `tombstoneGraceMs` (default 24 h)
1330
+ * It also **deletes the tombstone rows its own past retirements left**, after `tombstoneGraceMs` (default 24 h; at least one hour, else `ValidationError`)
1197
1331
  * and only once that segment's Storage generations are provably gone — collecting a straggler generation itself
1198
- * first, since nothing else ever would for a tombstoned segment. Attribution is a **positive marker the sweep
1332
+ * first, since nothing else runs that collection on its own for a tombstoned segment (an erasure collects there
1333
+ * only
1334
+ * when an object holds its id, and a re-run drop sweeps it). Attribution is a **positive marker the sweep
1199
1335
  * stamps on its own retirements**, not an inference from "destroyed + an expired policy": a crypto-shred leaves
1200
1336
  * `retention` untouched, so setting a policy and then honouring a right-to-erasure request mid-window produces
1201
1337
  * exactly that row, and deleting it would destroy the Art. 17 attestation and un-fence the name. By default a
@@ -1233,7 +1369,7 @@ export declare class CloudRoaring {
1233
1369
  * A pin answers with no timed bound, until it is told or both caches let it go.
1234
1370
  * - **Another process.** Erasing on one box invalidates nothing on the others; each store bounds its own
1235
1371
  * staleness by `cache.genTtlMs`, and a store with no registry (a bare `IStorageDriver`), with
1236
- * `cache.genTtlMs: 0`, or on a storage source built with no clock has no bound at all: it converges only when a read happens to miss its caches. If a compliance deadline depends on every
1372
+ * `cache.genTtlMs: 0`, or on a `StorageChunkSource` of your own has no bound at all: it converges only when a read happens to miss its caches. If a compliance deadline depends on every
1237
1373
  * reader converging, you need to signal them — this is the call to make when your own fan-out delivers.
1238
1374
  *
1239
1375
  * It also forgets any replacement this store found of a pin's object, so once it is called after a restore puts that
@@ -1295,8 +1431,9 @@ export declare class CloudRoaring {
1295
1431
  * throw). Read-only, bounded fan-out; run it at startup after a restore. Returns `{ checked, inconsistent,
1296
1432
  * errored }` — `inconsistent` empty ⇒ coherent; otherwise it names the segments to recover (restore the object
1297
1433
  * store, or roll the registry back to a coherent point). Needs the store built with a **backend**
1298
- * (throws {@link UnsupportedError} otherwise). `destroyed` (crypto-shredded) segments are skipped. Pair it with
1299
- * the DR runbook (docs/guide/disaster-recovery.md).
1434
+ * (throws {@link UnsupportedError} otherwise). `destroyed` segments (crypto-shredded, destroyed in the clear or
1435
+ * dropped) are skipped. Pair
1436
+ * it with the DR runbook (docs/guide/disaster-recovery.md).
1300
1437
  *
1301
1438
  * A segment whose pointer names a missing object is the torn restore this reports; a cold `count()` of it still
1302
1439
  * answers the number its row records, while a read of the object throws, so the count alone never shows it. With
@@ -1313,16 +1450,51 @@ export declare class CloudRoaring {
1313
1450
  * be used is counted in `generationsUnchecked`, and none of its objects is read.
1314
1451
  *
1315
1452
  * It holds the registry rows it enumerates resident, at most 250,000 of them, and throws
1316
- * `BudgetExceededError` past that rather than report a partial scan as a whole one. This method takes no
1317
- * ceiling of its own: narrow the scan with `namespace`, or call `runConsistencyCheck` over the backend's
1318
- * `storage` and `registry` with a higher `maxScanSegments`.
1453
+ * `BudgetExceededError` (`option: 'maxScanSegments'`) past that rather than report a partial scan as a whole one.
1454
+ * Narrow the scan with `namespace`, or raise `maxScanSegments` when the fleet really is that large and the memory
1455
+ * is there.
1319
1456
  */
1320
1457
  checkConsistency(options?: {
1321
1458
  namespace?: string;
1322
1459
  concurrency?: number;
1460
+ /** The most registry rows the scan holds resident (default 250,000); past it the call throws `BudgetExceededError`. */
1461
+ maxScanSegments?: number;
1323
1462
  summaries?: boolean;
1324
1463
  generations?: boolean;
1325
1464
  }): Promise<ConsistencyReport>;
1465
+ /**
1466
+ * **First-run check of the store's storage and registry.** Call it once at deploy time, or on a first run, before
1467
+ * the first load: it finds a missing permission, a storage that does not refuse a second write to a key, a listing
1468
+ * that lags, or a lifecycle rule that would delete the store's objects, while nothing depends on them yet.
1469
+ *
1470
+ * It runs seven checks and always lists all seven, in this order: `registry` (one read of a row that must be
1471
+ * absent, and the registry able to write), `write` (a new object), `write-once` (a second write to the same key must
1472
+ * be refused), `read` (the object reads back as written), `list` (the listing names it), `delete` (this run's own
1473
+ * object is removed, with the version the read gave where the driver applies one) and `lifecycle` (the backend's
1474
+ * lifecycle check). The probe is one small object at `cbm.due.preflight/segments/probe.<number>.crbm` under the
1475
+ * store's prefix; no registry row is written, and no segment of yours can share the name. A probe left by a run that
1476
+ * was cut off is named in the `list` detail and never deleted, since it could be a run in progress.
1477
+ *
1478
+ * It **never throws over a finding**. A failed step, a transient fault included, is a `problem` whose `detail`
1479
+ * says to run again when the fault was transient, and the checks that cannot run without it are `unchecked`. The
1480
+ * exception is the `lifecycle` step, whose failures, its timeout included, are `unchecked`. `ok` is true when no
1481
+ * check is a `problem`: read an `unchecked` check yourself, as `ok` does not vouch for it. The `lifecycle` check is
1482
+ * `unchecked` for a backend whose check says so (Azure Blob always), for a backend that has no such method (a
1483
+ * third-party backend, a `brandAsBackend` wrapper that does not forward it), when the identity lacks the optional
1484
+ * permission to read the lifecycle rules (`s3:GetLifecycleConfiguration`, `storage.buckets.get`), and when the check
1485
+ * takes longer than 30 seconds of the store's clock. The other steps are the store's ordinary calls and keep their
1486
+ * timing.
1487
+ *
1488
+ * **Not checked:** read-after-delete, the registry's `If-Match` on compare-and-swap, multipart abort permission,
1489
+ * versioning, backups, the keystore's backup and an S3 SDK from 3.641.0 to 3.699.0 (an older SDK shows as a
1490
+ * `write-once` problem; these drop `If-Match`, which only a registry write refuses). It makes no kind of request that a
1491
+ * load does not, though the lifecycle read needs its own optional permission. A run costs about seven requests (a
1492
+ * first run on GCS makes eight, the eighth a second listing that checks the bucket exists, and Azure Blob always
1493
+ * eight): two writes and a delete, the rest reads and one listing.
1494
+ *
1495
+ * Throws {@link UnsupportedError}, and only that, for a store with no backend, since there is nothing to check.
1496
+ */
1497
+ preflight(): Promise<PreflightResult>;
1326
1498
  /**
1327
1499
  * **Export ("eject") every registered segment's current generation** through the injected `sink`, using only
1328
1500
  * public read APIs — so your data is readable **without CloudRoaring** (the exit path; see the README's "Your
@@ -1331,19 +1503,48 @@ export declare class CloudRoaring {
1331
1503
  * Enumerates via the store's **own** registry (needs one — throws {@link UnsupportedError} otherwise) so the
1332
1504
  * enumeration and the read path provably share one registry. Encrypted segments are decrypted transparently
1333
1505
  * **iff** this store was wired with their keystore — the export is therefore **cleartext**; protect it.
1334
- * Crypto-shredded segments are skipped. A segment that can't be read is isolated into the manifest's `failed[]`
1335
- * (the run continues), so "a manifest exists" means the run finished — check `failed`. The `export-segments`
1336
- * CLI wraps this with a filesystem sink.
1506
+ * Destroyed segments (crypto-shredded, destroyed in the clear or dropped) are skipped. A segment that can't be read
1507
+ * is isolated into the manifest's `failed[]` (the run continues), so "a manifest exists" means the run finished —
1508
+ * check `failed`. The `export-segments`
1509
+ * CLI wraps this with a filesystem sink. The `'roaring'` format's bitmaps are built by the store's own codec.
1337
1510
  */
1338
1511
  exportSegments(sink: ExportSink, options?: ExportOptions): Promise<ExportManifest>;
1339
1512
  }
1340
1513
  /**
1341
- * Options common to every chunk-aligned combine (`intersect` / `union` / `andNot`), and the `*Into` verbs.
1514
+ * The options of a read or a combine on a {@link Segment}: one type for all of them, and each verb takes the fields that
1515
+ * apply to it.
1342
1516
  *
1343
- * The range, `after` and `through`, applies to every operand and every `exclude`: the combine yields only the ids
1344
- * in `(after, through]` and fetches only the chunks the range overlaps, as {@link Segment.iterate} does.
1517
+ * | verb | takes |
1518
+ * |---|---|
1519
+ * | `iterate`, `everyNth` | `after`, `through` |
1520
+ * | `intersect`, `union` | the range, `concurrency`, `budget`, `allowAbsentOperands`, `exclude` |
1521
+ * | `andNot` | the same but `exclude`: its `excludes` argument is the subtraction |
1522
+ * | `intersectInto`, `unionInto` | every field |
1523
+ * | `andNotInto` | every field but `exclude` |
1524
+ *
1525
+ * `audit`, `allowEmpty`, `guard`, `metadata` and `keep` decide what an `*Into` verb publishes, and a read publishes
1526
+ * nothing. A field a verb does not take is refused with a {@link ValidationError} that names it, before any request,
1527
+ * whether the object holds it as an own key, inherits it or holds it in a getter: a streaming read fails when it is first
1528
+ * read, and an `*Into` verb rejects. The compiler catches one only in an object
1529
+ * literal given to `iterate` or `everyNth`, which take the range alone: `Pick<CombineOptions, 'after' | 'through'>`,
1530
+ * the part {@link MaterializeManyOptions} extends.
1531
+ *
1532
+ * **The range**, `after` and `through`, is the ids greater than `after` and no greater than `through`, `(after,
1533
+ * through]`, built for keyset paging: `after` is the last id the caller already has, `through` the end of its window.
1534
+ * Each is optional: left out, the read starts at the first id or runs to the last. Each is an integer in
1535
+ * `0..4294967295`, or the read throws {@link ValidationError} when it is first read. `after >= through` is an empty range,
1536
+ * not an error, because a cursor that has reached the end of its window is normal; an empty range reads nothing, not even
1537
+ * whether a combine's operands exist. On a combine the range applies to every operand and every `exclude`, and a read
1538
+ * with a range fetches only the chunks it overlaps. The per-op budget is charged before the first fetch, for every chunk
1539
+ * in the range, so with `after` alone it is charged to the end of the segment however early the caller stops; pass
1540
+ * `through` to bound it. A read that lists its keys again after a move is charged again, with the chunks it has already
1541
+ * taken.
1345
1542
  */
1346
- export interface BaseCombineOptions extends IdRange {
1543
+ export interface CombineOptions {
1544
+ /** Exclusive lower bound: the read yields only ids greater than this. */
1545
+ readonly after?: number | undefined;
1546
+ /** Inclusive upper bound: the read yields only ids up to and including this. */
1547
+ readonly through?: number | undefined;
1347
1548
  /**
1348
1549
  * How far ahead each operand is read: a positive integer, default 32. The library's own stores read each operand as
1349
1550
  * one stream of coalesced ranges (chunks that sit near each other are one request), and `concurrency` is how many
@@ -1377,57 +1578,46 @@ export interface BaseCombineOptions extends IdRange {
1377
1578
  * operand that resolved to no chunks at all, so a normal combine pays nothing for it.
1378
1579
  */
1379
1580
  readonly allowAbsentOperands?: boolean;
1380
- }
1381
- /**
1382
- * A combine that can also subtract.
1383
- *
1384
- * `exclude` is why suppression does not need an intermediate segment: applied here it folds into the same
1385
- * chunk-aligned pass, and each exclude is read **only at the keys that survived** — so a large global opt-out
1386
- * list costs reads proportional to the audience, not to itself.
1387
- */
1388
- export interface CombineOptions extends BaseCombineOptions {
1389
- /** Segments whose ids are subtracted from the result. */
1581
+ /**
1582
+ * Segments whose ids are subtracted from the result, on `intersect`, `union`, `intersectInto` and `unionInto`.
1583
+ *
1584
+ * `exclude` is why suppression does not need an intermediate segment: applied here it folds into the same
1585
+ * chunk-aligned pass, and each exclude is read **only at the keys that survived** — so a large global opt-out
1586
+ * list costs reads proportional to the audience, not to itself. `andNot` and `andNotInto` refuse it: their `excludes`
1587
+ * argument is the subtraction, and a second one in the options would be two spellings of one thing.
1588
+ */
1390
1589
  readonly exclude?: Segment[];
1391
- }
1392
- /**
1393
- * A combine that WRITES its result — the extra options `intersect`/`union`/`andNot` have no use for.
1394
- *
1395
- * Separate from {@link CombineOptions} on purpose: `allowEmpty` and `guard` decide whether a generation is
1396
- * published, and a read verb publishes nothing. Offering them on `intersect()` would be offering a parameter
1397
- * that cannot do anything.
1398
- */
1399
- export interface MaterializeOptions extends CombineOptions {
1400
1590
  /**
1401
- * Audit sink. A materialisation publishes a generation, and a publish is an auditable event
1591
+ * Audit sink, for the `*Into` verbs. A materialisation publishes a generation, and a publish is an auditable event
1402
1592
  * (`segment.publish`, exactly as a load emits) — as is a refusal (`segment.load-refused`).
1403
1593
  *
1404
1594
  * It is on the call rather than on the store because that is where every other auditable operation takes it
1405
1595
  * (`eraseSubject`, `dropSegment`, `retireExpired`): the caller who performs the act decides where the record
1406
1596
  * goes. Without it a `*Into` would be the one write path in the library that could make a generation current
1407
- * and leave no trace in the compliance trail.
1408
- *
1409
- * It sits HERE rather than on {@link BaseCombineOptions} for the reason this type exists: the streaming verbs
1410
- * write nothing, so an audit sink on `intersect()` would be a parameter that could not do anything.
1597
+ * and leave no trace in the compliance trail. A read verb writes nothing and emits nothing, so it refuses one.
1411
1598
  */
1412
1599
  readonly audit?: IAuditSink;
1413
1600
  /**
1414
- * Publish an empty result over a non-empty destination. Off by default, exactly as on {@link CloudRoaring.load}:
1415
- * an empty combine is far more often a mistake upstream — a typo'd operand, a segment that has not loaded
1416
- * yet, an `exclude` that swallowed everything — than an intent, and once it lands it is indistinguishable
1417
- * from a correct run.
1601
+ * For the `*Into` verbs: publish an empty result over a non-empty destination. Off by default, exactly as on
1602
+ * {@link CloudRoaring.load}: an empty combine is far more often a mistake upstream — a typo'd operand, a segment that
1603
+ * has not loaded yet, an `exclude` that swallowed everything — than an intent, and once it lands it is
1604
+ * indistinguishable from a correct run.
1418
1605
  */
1419
1606
  readonly allowEmpty?: boolean;
1420
- /** Refuse an implausible result rather than publish it. Same bounds, and same meaning, as on `load()`. */
1607
+ /**
1608
+ * For the `*Into` verbs: refuse an implausible result rather than publish it. Same bounds, and same meaning, as on
1609
+ * `load()`.
1610
+ */
1421
1611
  readonly guard?: LoadGuard;
1422
1612
  /**
1423
- * Metadata for the generation this call publishes, under the rules and with the meaning of
1613
+ * For the `*Into` verbs: metadata for the generation the call publishes, under the rules and with the meaning of
1424
1614
  * {@link LoadOptions.metadata}: small, immutable, published with the pointer, never a subject's id. A value that
1425
1615
  * breaks a rule throws {@link ValidationError} before any request is made.
1426
1616
  */
1427
1617
  readonly metadata?: GenerationMetadata;
1428
1618
  /**
1429
- * Generations to keep below the new pointer — see {@link LoadOptions.keep}. A value that is not a non-negative
1430
- * integer throws `ValidationError`.
1619
+ * For the `*Into` verbs: generations to keep below the new pointer — see {@link LoadOptions.keep}. A value that is
1620
+ * not a non-negative integer throws `ValidationError`.
1431
1621
  *
1432
1622
  * **Defaults to keeping everything**, unlike `load()`, which keeps 1 and collects what it supersedes. An operator's
1433
1623
  * recovery story can depend on a materialisation collecting nothing: `rollbackSegment` refuses a target that
@@ -1482,19 +1672,11 @@ export interface IdStream extends IdIterator {
1482
1672
  */
1483
1673
  batches(): AsyncIterable<Uint32Array>;
1484
1674
  }
1485
- /**
1486
- * What `andNotInto` takes: the write options without `exclude`, because its `excludes` argument IS the
1487
- * subtraction — a second one in the options would be two spellings of one thing.
1488
- *
1489
- * Named rather than left as an inline `Omit` so it can be imported and annotated. The read sibling `andNot`
1490
- * solves the same problem the same way, by taking {@link BaseCombineOptions}.
1491
- */
1492
- export type AndNotIntoOptions = Omit<MaterializeOptions, 'exclude'>;
1493
1675
  /** What {@link Segment.pin} and {@link Segment.pinAt} take besides the generation. */
1494
1676
  export interface PinOptions {
1495
1677
  /**
1496
1678
  * Hold the pinned generation until this instant: epoch-**milliseconds**, after now and at most
1497
- * {@link MAX_LEASE_MS} (14 days) from it. A load's collection leaves a leased generation in the bucket until the lease
1679
+ * 14 days (1,209,600,000 ms) from it. A load's collection leaves a leased generation in the bucket until the lease
1498
1680
  * ends, and a read of the handle after it throws {@link LeaseExpiredError}, never empty.
1499
1681
  */
1500
1682
  readonly leaseUntil?: number;
@@ -1523,6 +1705,7 @@ export interface PinAt {
1523
1705
  export interface SegmentStat {
1524
1706
  /** The generation read, or `null` when the segment has none. */
1525
1707
  readonly generation: number | null;
1708
+ /** The number of ids in the generation read, or `0` when the segment has none. */
1526
1709
  readonly cardinality: number;
1527
1710
  /** The metadata the generation was loaded with; absent when it has none. */
1528
1711
  readonly metadata?: GenerationMetadata;
@@ -1546,6 +1729,8 @@ export interface SegmentStat {
1546
1729
  export declare class Segment {
1547
1730
  private readonly metricsOn;
1548
1731
  private readonly engine;
1732
+ /** The store that made this handle, by reference; a combine takes only handles of its own store. */
1733
+ private readonly origin;
1549
1734
  private readonly ref;
1550
1735
  private readonly clock;
1551
1736
  private readonly metrics;
@@ -1589,7 +1774,7 @@ export declare class Segment {
1589
1774
  *
1590
1775
  * **One call reads a segment at one generation**, so a combine that holds this segment at two —
1591
1776
  * `snap0.andNot([snap1])`, or `live.intersect([snap])` — throws {@link ValidationError} when it is read, and an
1592
- * `*Into` of one throws before it reads anything. Materialise one side first, with `intersectInto(dest, [])`.
1777
+ * `*Into` of one throws before it reads anything. Materialise one side first, with `materializeMany({ operands: { src: seg }, outputs: [{ dest, expr: { or: ['src'] } }], keep })`.
1593
1778
  *
1594
1779
  * **A pin knows its object, not only its number.** A name purged and loaded again starts again at generation
1595
1780
  * 0, so a pin of the old segment never reads the new one: what it has already read still answers, as the
@@ -1629,9 +1814,7 @@ export declare class Segment {
1629
1814
  * Anything else leaves the pin as it is: after a `destroySegment` beside this store, or an erasure, a drop or a
1630
1815
  * retirement through another store, in this process or another, the pin answers from what it holds until this
1631
1816
  * store's reader cache evicts the pin's reader and its chunk cache evicts the chunks the pin decoded, or
1632
- * {@link CloudRoaring.invalidate} is called on this store. A combine on another store that takes the pin as an
1633
- * operand reads it through that store's reader and chunk cache, which only that store's own writes and its
1634
- * `invalidate` reach. Where the
1817
+ * {@link CloudRoaring.invalidate} is called on this store. A combine on another store refuses the pin. Where the
1635
1818
  * object the pin reads has been deleted, by an erasure, a drop or a sweep, a chunk the pin has not cached fails at
1636
1819
  * once. No timed refresh bounds any of that, as none bounds anything else a pin holds.
1637
1820
  *
@@ -1651,7 +1834,7 @@ export declare class Segment {
1651
1834
  * or misfiled one.
1652
1835
  *
1653
1836
  * Needs a store built on the `.crbm` storage source (the default when you pass a backend or a raw driver). Throws
1654
- * {@link UnsupportedError} on a store wired with a pre-built source that cannot pin.
1837
+ * {@link UnsupportedError} on a store wired with a `StorageChunkSource` of your own, which it cannot pin.
1655
1838
  */
1656
1839
  pin(options?: PinOptions): Promise<Segment>;
1657
1840
  /**
@@ -1675,8 +1858,9 @@ export declare class Segment {
1675
1858
  */
1676
1859
  private leaseError;
1677
1860
  /**
1678
- * Refuse with a {@link ValidationError} anything among `handles` that is not a segment, a value a JavaScript caller
1679
- * passed. It runs first in every combine, before the lease check reads the handles.
1861
+ * Refuse with a {@link ValidationError} any of `handles` that this store did not make: a value that is not a segment,
1862
+ * a segment of another store (even one over the same backend) and a segment of another installed copy of the
1863
+ * package. It runs first in every combine, before the lease check reads the handles and before any request.
1680
1864
  */
1681
1865
  private assertSegments;
1682
1866
  /**
@@ -1691,10 +1875,10 @@ export declare class Segment {
1691
1875
  * `at.generation` instead of the current one. Pass what the earlier pin recorded, `{ generation, fingerprint }`
1692
1876
  * from its {@link Segment.pinnedAt}. A generation number alone is not an identity, since a purged and re-created
1693
1877
  * name starts again at 0, so the fingerprint is required: a bare number, a malformed fingerprint or a key other
1694
- * than `generation` and `fingerprint` is a {@link ValidationError}. Identify a pin by its `generation` and
1878
+ * than `generation`, `fingerprint` and `version` is a {@link ValidationError}. Identify a pin by its `generation` and
1695
1879
  * `fingerprint`: the handle's `pinnedAt.version` can differ from the earlier pin's.
1696
1880
  *
1697
- * Throws {@link NotFoundError} when the generation is gone (collected, purged, or on a crypto-shredded segment),
1881
+ * Throws {@link NotFoundError} when the generation is gone (collected, purged, or on a destroyed segment),
1698
1882
  * is above the row's pointer, or is another object than the fingerprint names, and never reads empty. A rollback
1699
1883
  * deletes nothing, so once a later load moves the pointer past a generation it rolled back from, that one can be
1700
1884
  * reopened while its object is stored. Nothing keeps a generation alive unless you lease it: a later load's
@@ -1707,6 +1891,10 @@ export declare class Segment {
1707
1891
  * then reads as one from `pin()` does, including its failure on a chunk it has not cached once the generation is
1708
1892
  * swept. Works for an encrypted segment. The first argument is an object so that fields can join it later; the lease
1709
1893
  * is in `options`, the second.
1894
+ *
1895
+ * It takes what {@link Segment.pinnedAt} holds: once `generation` is not `null`, the record passes as it is (its
1896
+ * `version` is not read), and `{ generation, fingerprint }` does as well. A pin of a segment that had no generation
1897
+ * has nothing to reopen and throws {@link ValidationError}.
1710
1898
  */
1711
1899
  pinAt(at: PinAt, options?: PinOptions): Promise<Segment>;
1712
1900
  /**
@@ -1717,15 +1905,25 @@ export declare class Segment {
1717
1905
  */
1718
1906
  key(): string;
1719
1907
  /**
1720
- * Refuse an `*Into` whose handles cannot be read, before any object is written: anything that is not a segment
1721
- * ({@link ValidationError}), and a handle (this one, `dest` or an operand) whose lease has ended or was released
1722
- * ({@link LeaseExpiredError}). Called by the three `*Into` verbs only.
1908
+ * Refuse an `*Into` that cannot run, before any request, in this order: anything that is not a segment or is a
1909
+ * segment of another store, an empty operand list for `intersectInto`, and a `dest` that is also a source (all
1910
+ * {@link ValidationError}); then a handle (this one, `dest`, an operand or an exclude) whose lease has ended or was
1911
+ * released ({@link LeaseExpiredError}). A call with both a lapsed lease and one of the first three refusals throws the
1912
+ * first. Called by the three `*Into` verbs only.
1723
1913
  *
1724
1914
  * The verbs are `async` so this surfaces as a **rejected promise**, like every other validation in the facade —
1725
1915
  * a synchronous throw out of a promise-returning method escapes a caller who attached `.catch()` instead of
1726
1916
  * awaiting.
1727
1917
  */
1728
1918
  private assertIntoHandles;
1919
+ /**
1920
+ * Refuse an `*Into` whose destination is also one of its sources (this segment, an operand or an exclude, by the key
1921
+ * a handle gives, so a pin of the source and a ref spelled with the default namespace count), before any request, as
1922
+ * `materializeMany` refuses it. The combine would read the destination at the generation this store last resolved,
1923
+ * up to `cache.genTtlMs` old, while the publish is fenced on the row as it stands: another process's newer load of
1924
+ * the destination would be overwritten by a result built from the one before it, with no conflict to report.
1925
+ */
1926
+ private refuseSourceDestination;
1729
1927
  /** `result` as is, or, when a handle of the call holds a lease, checking that lease each time it reads a chunk. */
1730
1928
  private guarded;
1731
1929
  /**
@@ -1801,7 +1999,7 @@ export declare class Segment {
1801
1999
  * Each bound is optional and an integer in `0..4294967295`; a bad one throws {@link ValidationError} when the
1802
2000
  * stream is first read. `after >= through` is an empty range, which reads nothing.
1803
2001
  */
1804
- iterate(options?: IdRange): IdStream;
2002
+ iterate(options?: Pick<CombineOptions, 'after' | 'through'>): IdStream;
1805
2003
  /**
1806
2004
  * **Pinned handles only.** The ids at 1-based ranks `n`, `2n`, `3n` …, counted over the ids in `(after, through]`
1807
2005
  * when a range is given, ascending: `everyNth(1000)` yields the 1,000th id, then the 2,000th, and so on. A last
@@ -1832,7 +2030,7 @@ export declare class Segment {
1832
2030
  * for await (const id of audience.everyNth(1_000)) ends.push(id); // the end of each window of 1,000
1833
2031
  * ```
1834
2032
  */
1835
- everyNth(n: number, options?: IdRange): IdIterator;
2033
+ everyNth(n: number, options?: Pick<CombineOptions, 'after' | 'through'>): IdIterator;
1836
2034
  /**
1837
2035
  * The options a combine hands the engine, read once, when it is called ({@link readOptions}), with the `exclude`
1838
2036
  * it was given ({@link excludesOf}) and mapped down to the plain refs
@@ -1857,6 +2055,11 @@ export declare class Segment {
1857
2055
  * requests held ahead per operand: a positive integer, 32 by default).
1858
2056
  * AND is commutative, so `a.intersect([b])` and `b.intersect([a])` yield the same ids. Pass `budget` to
1859
2057
  * override the store's per-op denial-of-wallet budget for this call (or `false` to lift it).
2058
+ *
2059
+ * Of {@link CombineOptions} it takes the range, `concurrency`, `budget`, `allowAbsentOperands` and `exclude`; a write
2060
+ * option (`audit`, `allowEmpty`, `guard`, `metadata`, `keep`) fails the stream with {@link ValidationError} when it is
2061
+ * first read, before any request. So does an empty `others`: an intersection over nothing has no meaning, and
2062
+ * to read every id of one segment, use {@link iterate}. (`union` and `andNot` read an empty list as `this`.)
1860
2063
  */
1861
2064
  intersect(others: Segment[], options?: CombineOptions): IdStream;
1862
2065
  /**
@@ -1878,8 +2081,18 @@ export declare class Segment {
1878
2081
  * materialisation that silently did not take effect is the one thing a caller cannot detect on its own.
1879
2082
  *
1880
2083
  * A call involving a handle whose lease has ended, or anything that is not a segment, is refused before any of this.
1881
- */
1882
- intersectInto(dest: Segment, others: Segment[], options?: MaterializeOptions): Promise<MaterializeResult>;
2084
+ * So is a `dest` that is also `this`, an operand or an exclude (the same segment by name and namespace, however the
2085
+ * handle was made), and, for `intersectInto` alone, an empty `others` (`unionInto` and `andNotInto` publish `this` for an
2086
+ * empty list): each throws {@link ValidationError} before any request. A destination that
2087
+ * is also a source would publish a result built from the generation this store last resolved over a newer load by
2088
+ * another process, with no conflict to report. This release has no fenced in-place update: write the result into a
2089
+ * new segment and point readers at it; a single writer that knows nothing else writes the segment may load the result
2090
+ * back, unfenced. To copy `src` into `dest`, call
2091
+ * `materializeMany({ operands: { src }, outputs: [{ dest, expr: { or: ['src'] } }], keep })`, which reads each chunk
2092
+ * once, at one instant.
2093
+ * It takes every field of {@link CombineOptions}, as `unionInto` does.
2094
+ */
2095
+ intersectInto(dest: Segment, others: Segment[], options?: CombineOptions): Promise<MaterializeResult>;
1883
2096
  /**
1884
2097
  * `this ∪ others…`, minus `options.exclude` — streamed ascending.
1885
2098
  *
@@ -1888,11 +2101,12 @@ export declare class Segment {
1888
2101
  * `intersect` prunes any key missing from any operand; union has nothing to prune. It is charged against the
1889
2102
  * same per-op budget, so a wide union is refused rather than quietly billed — pass `budget` to raise it
1890
2103
  * deliberately. If you find yourself unioning the same segments on every read, materializing the combined
1891
- * segment once (`unionInto`, or a load) is the cheaper shape.
2104
+ * segment once (`unionInto`, or a load) is the cheaper shape. It takes the options {@link intersect} takes, and an
2105
+ * empty `others` reads `this` itself.
1892
2106
  */
1893
2107
  union(others: Segment[], options?: CombineOptions): IdStream;
1894
2108
  /** Materialize `this ∪ others…` (minus `exclude`) as a **new generation of `dest`** — see {@link intersectInto}. */
1895
- unionInto(dest: Segment, others: Segment[], options?: MaterializeOptions): Promise<MaterializeResult>;
2109
+ unionInto(dest: Segment, others: Segment[], options?: CombineOptions): Promise<MaterializeResult>;
1896
2110
  /**
1897
2111
  * `this \ (excludes…)` — streamed ascending. Suppression on its own.
1898
2112
  *
@@ -1903,10 +2117,17 @@ export declare class Segment {
1903
2117
  *
1904
2118
  * To filter the *result of an intersection*, do not chain — pass `exclude` to {@link intersect} instead, so
1905
2119
  * the suppression folds into the same pass rather than materializing an intermediate segment first.
2120
+ *
2121
+ * It takes the options {@link intersect} takes but `exclude`, since `excludes` is its subtraction: `exclude` or a write
2122
+ * option fails the stream with {@link ValidationError} when it is first read, before any request.
2123
+ */
2124
+ andNot(excludes: Segment[], options?: CombineOptions): IdStream;
2125
+ /**
2126
+ * Materialize `this \ (excludes…)` as a **new generation of `dest`** — see {@link intersectInto}. It takes every field
2127
+ * of {@link CombineOptions} but `exclude`, since `excludes` is its subtraction: `exclude` is refused with
2128
+ * {@link ValidationError} before any request.
1906
2129
  */
1907
- andNot(excludes: Segment[], options?: BaseCombineOptions): IdStream;
1908
- /** Materialize `this \ (excludes…)` as a **new generation of `dest`** — see {@link intersectInto}. */
1909
- andNotInto(dest: Segment, excludes: Segment[], options?: AndNotIntoOptions): Promise<MaterializeResult>;
2130
+ andNotInto(dest: Segment, excludes: Segment[], options?: CombineOptions): Promise<MaterializeResult>;
1910
2131
  /**
1911
2132
  * The one body of the three combines, read as ids or as chunks. `operands` is `others` for `intersect` and
1912
2133
  * `union`, and `excludes` for `andNot`, which takes no `exclude` option.
@@ -1915,6 +2136,9 @@ export declare class Segment {
1915
2136
  /** The one body of the three `*Into` verbs, after each has checked its own options. */
1916
2137
  private combineInto;
1917
2138
  }
2139
+ export { MemoryStorage, LocalFsStorage, InProcessKeystore } from '@cloudbitmaps/core/node';
2140
+ export type { InProcessKeystoreOptions, LocalFsStorageOptions, MemoryStorageOptions, } from '@cloudbitmaps/core/node';
1918
2141
  export { deserializePortable };
1919
- export { MemoryStorage, LocalFsStorage, CrbmStorageChunkSource, destroySegment, eraseNamespace, excludingReservedRows, readRetentionPolicy, MIN_EXPIRES_AT_MS, LEASE_SKEW_MS, MAX_LEASE_MS, MAX_LEASES_PER_SEGMENT, InProcessKeystore, NodeAead, aadFor, CloudRoaringError, ValidationError, WriteConflictError, IntegrityError, NotFoundError, UnsupportedError, CapabilityError, TransientError, KeyUnavailableError, BudgetExceededError, LeaseExpiredError, LeaseLimitError, StaleOperandError, isCloudRoaringError, isWriteConflictError, isTransientError, isNotFoundError, isIntegrityError, isValidationError, isLeaseExpiredError, isLeaseLimitError, isStaleOperandError, isUnsupportedError, isCapabilityError, isBudgetExceededError, isKeyUnavailableError, CrbmReader, BufferReader, CountingMetricsSink, RecordingAuditSink, DEFAULT_RETRY_POLICY, } from '@cloudbitmaps/core';
1920
- export type { Aead, AeadSealed, AuditEvent, BlobReader, BlobSink, Budget, BudgetOption, ChunkRef, Clock, CodecBitmap, CodecInterface, ConsistencyErrorEntry, ConsistencyIssue, ConsistencyReport, CrbmCrypto, CrbmReaderOptions, CrbmStorageChunkSourceOptions, DestroyResult, ClearRegistrySummary, DropResult, EncodedChunk, EraseDeps, ExportFailure, ExportFormat, ExportManifest, ExportOptions, ExportSink, ExportSkipped, ExportWriter, ExportedSegment, GenKey, GenerationEntry, GenerationMetadata, GovernanceMeta, IAuditSink, IKeystore, IMetricsSink, IRegistryDriver, IStorageDriver, IdRange, InProcessKeystoreOptions, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, LocalFsStorageOptions, MemoryStorageOptions, MetricEvent, MetricOpName, MetricsSnapshot, NewRegistryRecord, PinnedAt, PinnedObject, PortableBitmap, RegCaps, RegistryPatch, RegistryRecord, RegistryStatus, RegistrySummary, RegistryWriteOptions, KnownGeneration, LeaseEntry, RetentionPolicy, RetireEntry, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, RollbackResult, SealedRegistrySummary, SegmentInfo, SegmentRef, SegmentSize, SetRetentionResult, StorageBackend, StorageCaps, StorageChunkSource, StorageDeleteOptions, TailRead, Token, WrappedDek, } from '@cloudbitmaps/core';
2142
+ export type { ReadonlyBitmap } from './roaring-codec.js';
2143
+ export { excludingReservedRows, readRetentionPolicy, CloudRoaringError, ValidationError, WriteConflictError, IntegrityError, NotFoundError, UnsupportedError, CapabilityError, TransientError, KeyUnavailableError, BudgetExceededError, LeaseExpiredError, LeaseLimitError, StaleOperandError, errorKind, CountingMetricsSink, RecordingAuditSink, } from '@cloudbitmaps/core';
2144
+ export type { AuditEvent, Budget, BudgetOption, Clock, ConsistencyErrorEntry, ConsistencyIssue, ConsistencyReport, DestroyResult, DropResult, Entropy, EraseDeps, ErrorKind, ExportFailure, ExportFormat, ExportManifest, ExportOptions, ExportSink, ExportSkipped, ExportWriter, ExportedSegment, Expr, GenerationEntry, GenerationMetadata, IAuditSink, IMetricsSink, LoadGuard, LoadInput, LoadOptions, LoadRefusal, LoadResult, MaterializeManyFeedRecord, MaterializeManyOperandStats, MaterializeManyOutputStats, MaterializeManyStats, MetricEvent, MetricOpName, MetricsSnapshot, PinnedAt, PortableBitmap, PreflightCheck, PreflightResult, RetentionPolicy, RetireEntry, RetireExpiredOptions, RetireExpiredResult, RetryPolicy, Rng, RollbackResult, SegmentInfo, SegmentRef, SetRetentionResult, } from '@cloudbitmaps/core';