@cloudbitmaps/roaring 0.9.0 → 0.11.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/NOTICE +3 -3
- package/PRIVACY.md +268 -76
- package/README.md +85 -95
- package/dist/bin/export-segments.d.ts +21 -0
- package/dist/bin/export-segments.js +1972 -30
- package/dist/bin/export-segments.js.map +7 -1
- package/dist/index.d.ts +969 -458
- package/dist/index.js +1461 -354
- package/dist/index.js.map +7 -1
- package/dist/open-charging-storage.d.ts +34 -0
- package/dist/option-keys.d.ts +19 -0
- package/dist/portable/decode.d.ts +53 -0
- package/dist/portable/layout.d.ts +32 -0
- package/dist/reserved-namespace.d.ts +3 -0
- package/dist/roaring-codec.d.ts +68 -0
- package/dist/system-clock.d.ts +39 -0
- package/dist/testing/conformance.d.ts +54 -0
- package/dist/testing/fuzz-codec.d.ts +23 -0
- package/package.json +11 -117
- package/dist/azure/index.cjs +0 -14
- package/dist/azure/index.cjs.map +0 -1
- package/dist/azure/index.d.cts +0 -9
- package/dist/azure/index.d.ts +0 -9
- package/dist/azure/index.js +0 -3
- package/dist/azure/index.js.map +0 -1
- package/dist/bin/compact-segments.js +0 -201
- package/dist/bin/compact-segments.js.map +0 -1
- package/dist/cassandra/index.cjs +0 -14
- package/dist/cassandra/index.cjs.map +0 -1
- package/dist/cassandra/index.d.cts +0 -1
- package/dist/cassandra/index.d.ts +0 -1
- package/dist/cassandra/index.js +0 -3
- package/dist/cassandra/index.js.map +0 -1
- package/dist/chunk-D6LIJSGF.js +0 -906
- package/dist/chunk-D6LIJSGF.js.map +0 -1
- package/dist/dynamodb/index.cjs +0 -14
- package/dist/dynamodb/index.cjs.map +0 -1
- package/dist/dynamodb/index.d.cts +0 -1
- package/dist/dynamodb/index.d.ts +0 -1
- package/dist/dynamodb/index.js +0 -3
- package/dist/dynamodb/index.js.map +0 -1
- package/dist/gcs/index.cjs +0 -14
- package/dist/gcs/index.cjs.map +0 -1
- package/dist/gcs/index.d.cts +0 -1
- package/dist/gcs/index.d.ts +0 -1
- package/dist/gcs/index.js +0 -3
- package/dist/gcs/index.js.map +0 -1
- package/dist/index.cjs +0 -894
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -734
- package/dist/mongodb/index.cjs +0 -14
- package/dist/mongodb/index.cjs.map +0 -1
- package/dist/mongodb/index.d.cts +0 -1
- package/dist/mongodb/index.d.ts +0 -1
- package/dist/mongodb/index.js +0 -3
- package/dist/mongodb/index.js.map +0 -1
- package/dist/mysql/index.cjs +0 -14
- package/dist/mysql/index.cjs.map +0 -1
- package/dist/mysql/index.d.cts +0 -1
- package/dist/mysql/index.d.ts +0 -1
- package/dist/mysql/index.js +0 -3
- package/dist/mysql/index.js.map +0 -1
- package/dist/postgres/index.cjs +0 -14
- package/dist/postgres/index.cjs.map +0 -1
- package/dist/postgres/index.d.cts +0 -1
- package/dist/postgres/index.d.ts +0 -1
- package/dist/postgres/index.js +0 -3
- package/dist/postgres/index.js.map +0 -1
- package/dist/redis/index.cjs +0 -14
- package/dist/redis/index.cjs.map +0 -1
- package/dist/redis/index.d.cts +0 -1
- package/dist/redis/index.d.ts +0 -1
- package/dist/redis/index.js +0 -3
- package/dist/redis/index.js.map +0 -1
- package/dist/s3/index.cjs +0 -14
- package/dist/s3/index.cjs.map +0 -1
- package/dist/s3/index.d.cts +0 -1
- package/dist/s3/index.d.ts +0 -1
- package/dist/s3/index.js +0 -3
- package/dist/s3/index.js.map +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A storage driver that charges every **generation open** to a per-op budget, for `eraseSubject`.
|
|
3
|
+
*
|
|
4
|
+
* The budget counts fan-out in units of one backend read. `eraseSubject`'s scan charges one unit per registered
|
|
5
|
+
* segment, but a segment whose current generation lacks the id is searched anyway: every other generation in its
|
|
6
|
+
* bucket is opened, one tail read each, and with the keep-everything default of the `*Into` verbs that is one per
|
|
7
|
+
* generation the segment ever had. Those opens are the work the scan's own count leaves out. They are counted here,
|
|
8
|
+
* at the storage port where they happen, so an open added to the erasure later is charged without anyone
|
|
9
|
+
* remembering to charge it, and no option of the public erasure call had to grow.
|
|
10
|
+
*
|
|
11
|
+
* Two opens are never charged, because the segment's unit already pays for them: the generation the row named when
|
|
12
|
+
* the scan listed it, which every erasure must read to decide membership, and an object this call wrote itself,
|
|
13
|
+
* which it reads back to verify. A refusal here comes from a read, and in an erasure the reads that can be refused
|
|
14
|
+
* come before the deletes.
|
|
15
|
+
*/
|
|
16
|
+
import type { GenKey, IStorageDriver, SegmentRef } from '@cloudbitmaps/core';
|
|
17
|
+
export declare class OpenChargingStorage implements IStorageDriver {
|
|
18
|
+
private readonly inner;
|
|
19
|
+
private readonly charge;
|
|
20
|
+
private readonly free;
|
|
21
|
+
/**
|
|
22
|
+
* @param inner the driver the erasure would otherwise use
|
|
23
|
+
* @param current the generation the segment's row named when it was listed, or `null` when it named none
|
|
24
|
+
* @param charge called once per charged open; it throws to refuse
|
|
25
|
+
*/
|
|
26
|
+
constructor(inner: IStorageDriver, current: number | null, charge: () => void);
|
|
27
|
+
capabilities(): ReturnType<IStorageDriver['capabilities']>;
|
|
28
|
+
putImmutable(key: GenKey, write: Parameters<IStorageDriver['putImmutable']>[1]): ReturnType<IStorageDriver['putImmutable']>;
|
|
29
|
+
getRange(key: GenKey, offset: number, length: number): Promise<Uint8Array>;
|
|
30
|
+
/** One `getTail` is one open: a reader takes the footer and the index from it, and reads chunks with `getRange`. */
|
|
31
|
+
getTail(key: GenKey, maxBytes: number): ReturnType<IStorageDriver['getTail']>;
|
|
32
|
+
delete(key: GenKey): Promise<void>;
|
|
33
|
+
list(ref: SegmentRef): AsyncIterable<GenKey>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys `new CloudRoaring(options)` takes: at the top level, and inside each group whose value is an object. The
|
|
3
|
+
* constructor refuses any other key by name, because an option it ignored would do nothing and look as if it had,
|
|
4
|
+
* and the gates that read the docs' samples and the harnesses' option bags hold them to the same table. A test
|
|
5
|
+
* holds the table to the option interfaces, in both directions.
|
|
6
|
+
*
|
|
7
|
+
* NOT part of the public API. It is its own module, rather than an `export` from the barrel, so the constructor
|
|
8
|
+
* and those gates read one copy.
|
|
9
|
+
*/
|
|
10
|
+
export declare const OPTION_KEYS: {
|
|
11
|
+
readonly top: readonly ["storage", "cache", "encryption", "retry", "metrics", "budget", "seams"];
|
|
12
|
+
readonly cache: readonly ["maxChunks", "ttlMs", "genTtlMs", "readerMax", "readerMaxBytes"];
|
|
13
|
+
readonly encryption: readonly ["keystore", "required"];
|
|
14
|
+
readonly retry: readonly ["maxAttempts", "baseDelayMs", "maxDelayMs", "backoffFactor", "jitter", "onRetry"];
|
|
15
|
+
readonly budget: readonly ["maxRequests"];
|
|
16
|
+
readonly seams: readonly ["clock", "rng"];
|
|
17
|
+
};
|
|
18
|
+
/** The groups: the top-level keys whose value, when it is an object, has keys of its own. */
|
|
19
|
+
export type OptionGroup = Exclude<keyof typeof OPTION_KEYS, 'top'>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A decoded portable-roaring bitmap that can be queried but not modified.
|
|
3
|
+
*
|
|
4
|
+
* Construction checks every container once, in {@link parsePortableLayout}, which is the same check the native
|
|
5
|
+
* path runs; after that a membership test reads one container rather than materializing the set.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PortableRoaringReader {
|
|
8
|
+
private readonly view;
|
|
9
|
+
private readonly containers;
|
|
10
|
+
private constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Parse and check a portable-format bitmap.
|
|
13
|
+
*
|
|
14
|
+
* @throws {IntegrityError} if the bytes are not a well-formed portable bitmap: see {@link parsePortableLayout}.
|
|
15
|
+
*/
|
|
16
|
+
static decode(bytes: Uint8Array): PortableRoaringReader;
|
|
17
|
+
/**
|
|
18
|
+
* Total cardinality.
|
|
19
|
+
*
|
|
20
|
+
* Served entirely from the descriptive header, which {@link parsePortableLayout} has checked against every
|
|
21
|
+
* container's payload, so this is O(containers) and independent of how many values the bitmap holds.
|
|
22
|
+
*/
|
|
23
|
+
count(): number;
|
|
24
|
+
/** Is `value` (a `u32`) in the set? */
|
|
25
|
+
has(value: number): boolean;
|
|
26
|
+
/** Binary search the descriptive header for the container owning `key`. */
|
|
27
|
+
private findContainer;
|
|
28
|
+
/**
|
|
29
|
+
* 1,024 little-endian `u64` words. Read as `u32` at `(low >> 5) * 4`: a LE `u64` is two LE `u32`s with the
|
|
30
|
+
* low word first, so the 32-bit view lands on the right half without any 64-bit arithmetic.
|
|
31
|
+
*/
|
|
32
|
+
private bitmapHas;
|
|
33
|
+
/**
|
|
34
|
+
* Sorted `u16` values — binary search.
|
|
35
|
+
*
|
|
36
|
+
* The `at === low` early return means the `at < low` below is only ever evaluated when `at !== low`, so
|
|
37
|
+
* writing it `at <= low` would behave identically. That is recorded because mutation testing surfaced it as
|
|
38
|
+
* a surviving mutant: it is an *equivalent* one, unkillable by any test rather than a coverage gap — three
|
|
39
|
+
* non-equivalent mutations of these same lines (inverting the branches, never reporting a hit, dropping the
|
|
40
|
+
* last element) are all caught. Do not "fix" the survivor, and do not add a test chasing it.
|
|
41
|
+
*/
|
|
42
|
+
private arrayHas;
|
|
43
|
+
/**
|
|
44
|
+
* `u16` run count, then that many `(start, length)` `u16` pairs sorted by start.
|
|
45
|
+
*
|
|
46
|
+
* `length` is the number of values **after** `start`, so a run covers the inclusive range
|
|
47
|
+
* `[start, start + length]` and a single-value run is stored with length 0. Getting this off by one is the
|
|
48
|
+
* easiest mistake in the format and is why the differential test drives runs specifically.
|
|
49
|
+
*/
|
|
50
|
+
private runHas;
|
|
51
|
+
}
|
|
52
|
+
/** Convenience wrapper over {@link PortableRoaringReader.decode}. */
|
|
53
|
+
export declare function decodePortableRoaring(bytes: Uint8Array): PortableRoaringReader;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** A container of sorted `u16` values. */
|
|
2
|
+
export declare const ARRAY = 0;
|
|
3
|
+
/** A container of 65,536 bits. */
|
|
4
|
+
export declare const BITMAP = 1;
|
|
5
|
+
/** A container of `(start, length)` runs. */
|
|
6
|
+
export declare const RUN = 2;
|
|
7
|
+
export type ContainerKind = typeof ARRAY | typeof BITMAP | typeof RUN;
|
|
8
|
+
export interface PortableContainer {
|
|
9
|
+
/** High 16 bits of every value in this container. */
|
|
10
|
+
readonly key: number;
|
|
11
|
+
/** Number of values. Stored as `cardinality - 1` on the wire, normalized here. */
|
|
12
|
+
readonly cardinality: number;
|
|
13
|
+
readonly kind: ContainerKind;
|
|
14
|
+
/** Absolute byte offset of this container's payload within the source buffer; a run container's run count. */
|
|
15
|
+
readonly offset: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse the portable-format bitmap in `bytes`, and check its structure.
|
|
19
|
+
*
|
|
20
|
+
* An empty buffer is the empty bitmap, as it is to the native addon, which returns one without reading a byte.
|
|
21
|
+
*
|
|
22
|
+
* @throws {IntegrityError} if the cookie is unrecognized, any field runs past the buffer, or the structure is not
|
|
23
|
+
* one a writer produces (see the module header for the list).
|
|
24
|
+
*/
|
|
25
|
+
export declare function parsePortableLayout(bytes: Uint8Array): PortableContainer[];
|
|
26
|
+
/**
|
|
27
|
+
* {@link parsePortableLayout}'s check alone, for a caller that hands the bytes to another decoder afterwards and
|
|
28
|
+
* so has no use for the layout: it allocates nothing per container.
|
|
29
|
+
*
|
|
30
|
+
* @throws {IntegrityError} exactly where {@link parsePortableLayout} does.
|
|
31
|
+
*/
|
|
32
|
+
export declare function checkPortableLayout(bytes: Uint8Array): void;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CodecBitmap, CodecInterface } from '@cloudbitmaps/core';
|
|
2
|
+
export declare class SafeBitmap implements CodecBitmap {
|
|
3
|
+
private readonly bitmap;
|
|
4
|
+
private constructor();
|
|
5
|
+
static empty(): SafeBitmap;
|
|
6
|
+
static fromValues(values: Iterable<number>): SafeBitmap;
|
|
7
|
+
/**
|
|
8
|
+
* Cap the size, check the structure, then deserialize with the **portable** format.
|
|
9
|
+
* Throws `IntegrityError` if the input exceeds `maxBytes`, is not a well-formed portable bitmap, or fails to
|
|
10
|
+
* decode — the native addon is never handed unbounded, malformed or unsafe-format input.
|
|
11
|
+
*
|
|
12
|
+
* The structural check is what makes the decoded bitmap's answers mean anything. The native deserializer
|
|
13
|
+
* bounds its reads and checks nothing else, so bytes of the right length and the wrong shape (containers or
|
|
14
|
+
* values out of order, overlapping runs, a cardinality that disagrees with the bits) decode into a bitmap
|
|
15
|
+
* whose `maximum()`, `has()` and `size` are wrong, and some of those shapes crash the process when used.
|
|
16
|
+
*/
|
|
17
|
+
static safeDeserialize(bytes: Uint8Array, maxBytes: number): SafeBitmap;
|
|
18
|
+
serialize(): Uint8Array;
|
|
19
|
+
/**
|
|
20
|
+
* Convert containers to run-encoding where that is smaller. Representation only — membership is untouched, and
|
|
21
|
+
* the portable format still round-trips through {@link RoaringCodec.safeDeserialize}.
|
|
22
|
+
*
|
|
23
|
+
* Roaring's third container type is a RUN, and no implementation selects it on its own: it takes an explicit
|
|
24
|
+
* `runOptimize()` pass. Nothing here made that call, so run-shaped ids paid array or bitset prices — measured
|
|
25
|
+
* at **570×** for a contiguous 1M-id range (128.1 KiB → 0.2 KiB) and **63×** for a 2,000-run shape
|
|
26
|
+
* (536.5 KiB → 8.5 KiB). Sparse ids come out byte-identical, so this is never a losing trade; roaring keeps
|
|
27
|
+
* whichever encoding is smaller per container.
|
|
28
|
+
*
|
|
29
|
+
* Those two factors are THIS CALL's own before/after on the same ids. They are not the ones the site quotes
|
|
30
|
+
* (543× and 63×) — those compare the optimized encoding against the best *fixed* representation at its floor,
|
|
31
|
+
* a different baseline that happens to land near 63× on the run shape too. `pnpm bench:encoding` prints both
|
|
32
|
+
* columns; don't carry a figure between them without checking which denominator it had.
|
|
33
|
+
*
|
|
34
|
+
* Called only when writing an immutable objects generation — see {@link CodecBitmap.optimize} for why the read
|
|
35
|
+
* path deliberately does not.
|
|
36
|
+
*/
|
|
37
|
+
optimize(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Largest value, or `undefined` when empty. O(1) — roaring reads it off the last container, so the engine's
|
|
40
|
+
* per-chunk range assertion costs one call per chunk rather than a walk per id. The last container holds the
|
|
41
|
+
* largest value only because {@link SafeBitmap.safeDeserialize} refuses bytes whose containers or values are
|
|
42
|
+
* out of order.
|
|
43
|
+
*/
|
|
44
|
+
maximum(): number | undefined;
|
|
45
|
+
add(value: number): void;
|
|
46
|
+
addMany(values: Iterable<number>): void;
|
|
47
|
+
remove(value: number): void;
|
|
48
|
+
removeMany(values: Iterable<number>): void;
|
|
49
|
+
has(value: number): boolean;
|
|
50
|
+
get size(): number;
|
|
51
|
+
get isEmpty(): boolean;
|
|
52
|
+
clone(): SafeBitmap;
|
|
53
|
+
/** In-place union: `this = this ∪ other`. */
|
|
54
|
+
orInPlace(other: CodecBitmap): void;
|
|
55
|
+
/** In-place difference: `this = this \ other`. */
|
|
56
|
+
andNotInPlace(other: CodecBitmap): void;
|
|
57
|
+
/** In-place intersection: `this = this ∩ other`. */
|
|
58
|
+
andInPlace(other: CodecBitmap): void;
|
|
59
|
+
/** Ascending iterator over the set values. */
|
|
60
|
+
[Symbol.iterator](): IterableIterator<number>;
|
|
61
|
+
toArray(): number[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The roaring {@link CodecInterface} — the flagship codec, delegating to {@link SafeBitmap}'s statics. This is
|
|
65
|
+
* the default the `CloudRoaring` facade injects into the engine; `core/` itself never hard-references it once a
|
|
66
|
+
* caller supplies a codec.
|
|
67
|
+
*/
|
|
68
|
+
export declare const roaringCodec: CodecInterface;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default real-time {@link Clock} — the production half of the determinism seam.
|
|
3
|
+
*
|
|
4
|
+
* It lives outside `core/`, so `Date.now()`, `setTimeout` and `setImmediate` are allowed here; that separation is
|
|
5
|
+
* the whole point of the seam, and it is lint-enforced on the other side.
|
|
6
|
+
*
|
|
7
|
+
* It sits in its own module rather than in `index.ts` so the facade, the tests and anything else that needs a real
|
|
8
|
+
* clock can import it without importing the whole barrel.
|
|
9
|
+
*/
|
|
10
|
+
import type { Clock } from '@cloudbitmaps/core';
|
|
11
|
+
/** Default real-time clock — lives outside `core/`, so `Date.now()` + timers are allowed here. */
|
|
12
|
+
export declare class SystemClock implements Clock {
|
|
13
|
+
now(): number;
|
|
14
|
+
sleep(ms: number): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* `setImmediate`, and it has to be — this is the one place where the choice of primitive is the whole feature.
|
|
17
|
+
*
|
|
18
|
+
* A long CPU-bound loop in `core/` (bulk-load is the one that matters) periodically hands the loop back so a
|
|
19
|
+
* co-resident HTTP server keeps answering. Measured on the **synthetic yield-primitive benchmark** — a
|
|
20
|
+
* 61,035-iteration loop yielding every 1,024, isolating the primitive rather than timing a real load (the
|
|
21
|
+
* end-to-end figures are 519 ms → 24 ms; see `cooperative.ts`, which lists all three experiments):
|
|
22
|
+
*
|
|
23
|
+
* ```text
|
|
24
|
+
* no yield 568 ms wall 568.0 ms worst event-loop gap
|
|
25
|
+
* sleep(0) 555 ms wall 555.2 ms ← a microtask. Yields NOTHING.
|
|
26
|
+
* setImmediate 569 ms wall 13.8 ms ← free, and 41x less starvation
|
|
27
|
+
* setTimeout(1) 625 ms wall 15.0 ms ← +10% wall for no extra relief
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* `sleep(0)` resolving on a microtask is correct for its own contract and useless for this: microtasks drain
|
|
31
|
+
* before the loop ever advances a phase, so `await sleep(0)` in a tight loop never lets a single pending I/O
|
|
32
|
+
* callback run. `setImmediate` fires in the check phase *after* pending I/O, which is exactly the ordering a
|
|
33
|
+
* server wants — inbound requests are serviced before the loop resumes chewing.
|
|
34
|
+
*
|
|
35
|
+
* A timer is not ref'd here (unlike {@link sleep}): a yield expresses "someone else may go first", never
|
|
36
|
+
* "there is outstanding work" — and `setImmediate` cannot outlive the turn that scheduled it anyway.
|
|
37
|
+
*/
|
|
38
|
+
yieldNow(): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SafeBitmap } from '../roaring-codec.js';
|
|
2
|
+
import type { StorageChunkSource, IRegistryDriver, IStorageDriver } from '@cloudbitmaps/core';
|
|
3
|
+
/**
|
|
4
|
+
* The segment every conformance seeder must write. Exported so a driver's own test file derives the name
|
|
5
|
+
* instead of restating it — a second copy that drifts makes the suite query a segment nobody seeded, which
|
|
6
|
+
* fails as an empty result rather than as the mismatch it actually is.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CONFORMANCE_SEGMENT = "s:v1";
|
|
9
|
+
/**
|
|
10
|
+
* Contract tests for a {@link StorageChunkSource}. `makeSource` MUST build a fresh source seeded with the
|
|
11
|
+
* given chunks (each an immutable Storage bitmap) and nothing else.
|
|
12
|
+
*/
|
|
13
|
+
export declare function storageChunkSourceConformance(label: string, makeSource: (chunks: Array<{
|
|
14
|
+
chunkKey: number;
|
|
15
|
+
bitmap: SafeBitmap;
|
|
16
|
+
}>) => Promise<StorageChunkSource>): void;
|
|
17
|
+
/** The storage-driver cases {@link storageDriverConformance} runs; an emulator that cannot model one skips it by name. */
|
|
18
|
+
export type StorageDriverCase = 'write-once round trip' | 'collision' | 'collision on a large upload' | 'missing object' | 'out-of-range read' | 'tail size' | 'idempotent delete' | 'list read-after-delete';
|
|
19
|
+
/**
|
|
20
|
+
* Contract tests for an {@link IStorageDriver} — the contract its doc comment lists: write-once with
|
|
21
|
+
* `WriteConflictError` on a collision, `NotFoundError` for a missing object, `ValidationError` for an out-of-range
|
|
22
|
+
* read, `getTail`'s true total size, idempotent `delete`, and a `list` that is read-after-delete. `makeDriver` MUST
|
|
23
|
+
* return a driver over an empty, isolated keyspace on each call.
|
|
24
|
+
*
|
|
25
|
+
* `skip` names the cases a backend cannot exercise (a local emulator that does not model a contract), so each
|
|
26
|
+
* skip is visible at the call site, with the reason beside it, rather than hidden in a weakened assertion.
|
|
27
|
+
*/
|
|
28
|
+
export declare function storageDriverConformance(label: string, makeDriver: () => IStorageDriver, options?: {
|
|
29
|
+
readonly skip?: readonly StorageDriverCase[];
|
|
30
|
+
/**
|
|
31
|
+
* Size of the object the `collision on a large upload` case writes. Pass one above the driver's single-request
|
|
32
|
+
* upload threshold to exercise its multipart or resumable path, where the write-once precondition is
|
|
33
|
+
* sent on a different request than a small object's. Omitted, the case does not run.
|
|
34
|
+
*/
|
|
35
|
+
readonly largeBytes?: number;
|
|
36
|
+
}): void;
|
|
37
|
+
/**
|
|
38
|
+
* Contract tests for an {@link IRegistryDriver}. `makeDriver` MUST return a fresh, empty, isolated driver on
|
|
39
|
+
* each call. The OCC contract (create / token-fenced CAS / ABA) plus the registry's record semantics (forward
|
|
40
|
+
* currentGen, status, clearable keyId, discovery).
|
|
41
|
+
*/
|
|
42
|
+
export declare function registryConformance(label: string, makeDriver: () => IRegistryDriver): void;
|
|
43
|
+
/**
|
|
44
|
+
* Contract tests for an {@link IRegistryDriver} under **concurrent writers** — `makeDrivers` MUST return two
|
|
45
|
+
* independent driver instances over the **same** backing store, as two processes would see it.
|
|
46
|
+
*
|
|
47
|
+
* This exists because {@link registryConformance} drives one driver sequentially, and `ObjectStoreRegistry`
|
|
48
|
+
* checks the OCC token in memory before it ever issues a conditional write. That in-process check answers
|
|
49
|
+
* every sequential case, so the store-level precondition — the only thing that fences writers *across*
|
|
50
|
+
* processes, and the entire reason these drivers are built on conditional writes — is never load-bearing
|
|
51
|
+
* there. A backend that accepted `If-Match` and ignored it passed the full suite; so did a GCS driver whose
|
|
52
|
+
* writes were not fenced at all. These cases are what make the fence testable.
|
|
53
|
+
*/
|
|
54
|
+
export declare function registryConcurrency(label: string, makeDrivers: () => readonly [IRegistryDriver, IRegistryDriver]): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal surface for the coverage-guided fuzz harness, CODEC half.
|
|
3
|
+
*
|
|
4
|
+
* The concrete bitmap codec, and the one property the harness holds every decode it accepts to. The core half
|
|
5
|
+
* (`CrbmReader`, the non-public `parseIndex`, `BufferReader`, `CloudRoaringError`, `DEFAULT_MAX_PAYLOAD_BYTES`) is
|
|
6
|
+
* built from `@cloudbitmaps/core`'s own `src/testing/fuzz-core.ts` to `fuzz/build/fuzz-core.js`. Splitting it this
|
|
7
|
+
* way keeps the published entries of both packages free of test-only exports: neither widened its public API to
|
|
8
|
+
* feed the fuzzer.
|
|
9
|
+
*/
|
|
10
|
+
import { SafeBitmap } from '../roaring-codec.js';
|
|
11
|
+
export { SafeBitmap };
|
|
12
|
+
/**
|
|
13
|
+
* What an accepted decode must be: it iterates strictly ascending, `size` is the number of values it iterates, and
|
|
14
|
+
* `has` confirms what it iterates. Throws a plain `Error` otherwise, which the fuzz targets and the corpus replay
|
|
15
|
+
* report as a finding.
|
|
16
|
+
*
|
|
17
|
+
* Memory safety alone does not see a structurally broken bitmap. Bytes with containers or values out of order, or
|
|
18
|
+
* a cardinality that disagrees with the bits, decode into memory that behaves until an operation trusts one of the
|
|
19
|
+
* broken invariants, so a contract of "no crash" passes them. This fails them. It is bounded like the
|
|
20
|
+
* materialization it replaces, because a valid run-heavy bitmap is tiny on disk and can hold billions of ids, and it
|
|
21
|
+
* asks `has` of the first 4,096 values only, which is enough to reach every container a small input can hold.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertConsistentDecode(bm: SafeBitmap): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbitmaps/roaring",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Distributed, cloud-native Roaring Bitmaps — query and intersect billion-scale integer sets
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Distributed, cloud-native Roaring Bitmaps — query and intersect billion-scale integer sets straight out of object storage, from a bounded RAM cache over immutable objects in your own bucket, at a fraction of an always-on cache.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"roaring",
|
|
7
7
|
"bitmap",
|
|
@@ -16,29 +16,22 @@
|
|
|
16
16
|
"set",
|
|
17
17
|
"set-intersection",
|
|
18
18
|
"s3",
|
|
19
|
-
"dynamodb",
|
|
20
19
|
"gcs",
|
|
21
20
|
"azure",
|
|
22
|
-
"postgres",
|
|
23
|
-
"redis",
|
|
24
|
-
"mongodb",
|
|
25
|
-
"cassandra",
|
|
26
|
-
"mysql",
|
|
27
|
-
"mariadb",
|
|
28
21
|
"serverless",
|
|
29
|
-
"
|
|
22
|
+
"object-storage",
|
|
30
23
|
"scale-to-zero",
|
|
31
24
|
"segmentation",
|
|
32
25
|
"audience",
|
|
33
26
|
"cloud",
|
|
34
|
-
"cloud-roaring",
|
|
35
27
|
"cloudbitmaps"
|
|
36
28
|
],
|
|
37
29
|
"license": "Apache-2.0",
|
|
38
30
|
"author": "Sharvil Kadam",
|
|
39
31
|
"repository": {
|
|
40
32
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/cloudbitmaps/cloudbitmaps.git"
|
|
33
|
+
"url": "git+https://github.com/cloudbitmaps/cloudbitmaps.git",
|
|
34
|
+
"directory": "packages/roaring"
|
|
42
35
|
},
|
|
43
36
|
"homepage": "https://github.com/cloudbitmaps/cloudbitmaps#readme",
|
|
44
37
|
"bugs": {
|
|
@@ -46,62 +39,15 @@
|
|
|
46
39
|
},
|
|
47
40
|
"type": "module",
|
|
48
41
|
"engines": {
|
|
49
|
-
"node": ">=22"
|
|
42
|
+
"node": ">=22.12"
|
|
50
43
|
},
|
|
51
44
|
"sideEffects": false,
|
|
52
|
-
"main": "./dist/index.
|
|
53
|
-
"module": "./dist/index.js",
|
|
45
|
+
"main": "./dist/index.js",
|
|
54
46
|
"types": "./dist/index.d.ts",
|
|
55
47
|
"exports": {
|
|
56
48
|
".": {
|
|
57
49
|
"types": "./dist/index.d.ts",
|
|
58
|
-
"
|
|
59
|
-
"require": "./dist/index.cjs"
|
|
60
|
-
},
|
|
61
|
-
"./s3": {
|
|
62
|
-
"types": "./dist/s3/index.d.ts",
|
|
63
|
-
"import": "./dist/s3/index.js",
|
|
64
|
-
"require": "./dist/s3/index.cjs"
|
|
65
|
-
},
|
|
66
|
-
"./dynamodb": {
|
|
67
|
-
"types": "./dist/dynamodb/index.d.ts",
|
|
68
|
-
"import": "./dist/dynamodb/index.js",
|
|
69
|
-
"require": "./dist/dynamodb/index.cjs"
|
|
70
|
-
},
|
|
71
|
-
"./gcs": {
|
|
72
|
-
"types": "./dist/gcs/index.d.ts",
|
|
73
|
-
"import": "./dist/gcs/index.js",
|
|
74
|
-
"require": "./dist/gcs/index.cjs"
|
|
75
|
-
},
|
|
76
|
-
"./azure": {
|
|
77
|
-
"types": "./dist/azure/index.d.ts",
|
|
78
|
-
"import": "./dist/azure/index.js",
|
|
79
|
-
"require": "./dist/azure/index.cjs"
|
|
80
|
-
},
|
|
81
|
-
"./postgres": {
|
|
82
|
-
"types": "./dist/postgres/index.d.ts",
|
|
83
|
-
"import": "./dist/postgres/index.js",
|
|
84
|
-
"require": "./dist/postgres/index.cjs"
|
|
85
|
-
},
|
|
86
|
-
"./redis": {
|
|
87
|
-
"types": "./dist/redis/index.d.ts",
|
|
88
|
-
"import": "./dist/redis/index.js",
|
|
89
|
-
"require": "./dist/redis/index.cjs"
|
|
90
|
-
},
|
|
91
|
-
"./mongodb": {
|
|
92
|
-
"types": "./dist/mongodb/index.d.ts",
|
|
93
|
-
"import": "./dist/mongodb/index.js",
|
|
94
|
-
"require": "./dist/mongodb/index.cjs"
|
|
95
|
-
},
|
|
96
|
-
"./cassandra": {
|
|
97
|
-
"types": "./dist/cassandra/index.d.ts",
|
|
98
|
-
"import": "./dist/cassandra/index.js",
|
|
99
|
-
"require": "./dist/cassandra/index.cjs"
|
|
100
|
-
},
|
|
101
|
-
"./mysql": {
|
|
102
|
-
"types": "./dist/mysql/index.d.ts",
|
|
103
|
-
"import": "./dist/mysql/index.js",
|
|
104
|
-
"require": "./dist/mysql/index.cjs"
|
|
50
|
+
"default": "./dist/index.js"
|
|
105
51
|
}
|
|
106
52
|
},
|
|
107
53
|
"files": [
|
|
@@ -115,69 +61,17 @@
|
|
|
115
61
|
"access": "public"
|
|
116
62
|
},
|
|
117
63
|
"bin": {
|
|
118
|
-
"compact-segments": "./dist/bin/compact-segments.js",
|
|
119
64
|
"export-segments": "./dist/bin/export-segments.js"
|
|
120
65
|
},
|
|
121
66
|
"dependencies": {
|
|
122
|
-
"roaring": "
|
|
123
|
-
"@cloudbitmaps/core": "0.
|
|
124
|
-
},
|
|
125
|
-
"peerDependencies": {
|
|
126
|
-
"@aws-sdk/client-dynamodb": "^3",
|
|
127
|
-
"@aws-sdk/client-s3": "^3",
|
|
128
|
-
"@azure/storage-blob": "^12",
|
|
129
|
-
"@google-cloud/storage": "^7",
|
|
130
|
-
"cassandra-driver": "^4",
|
|
131
|
-
"ioredis": "^5",
|
|
132
|
-
"mongodb": "^6",
|
|
133
|
-
"mysql2": "^3",
|
|
134
|
-
"pg": "^8"
|
|
135
|
-
},
|
|
136
|
-
"peerDependenciesMeta": {
|
|
137
|
-
"@aws-sdk/client-dynamodb": {
|
|
138
|
-
"optional": true
|
|
139
|
-
},
|
|
140
|
-
"@aws-sdk/client-s3": {
|
|
141
|
-
"optional": true
|
|
142
|
-
},
|
|
143
|
-
"@azure/storage-blob": {
|
|
144
|
-
"optional": true
|
|
145
|
-
},
|
|
146
|
-
"@google-cloud/storage": {
|
|
147
|
-
"optional": true
|
|
148
|
-
},
|
|
149
|
-
"cassandra-driver": {
|
|
150
|
-
"optional": true
|
|
151
|
-
},
|
|
152
|
-
"ioredis": {
|
|
153
|
-
"optional": true
|
|
154
|
-
},
|
|
155
|
-
"mongodb": {
|
|
156
|
-
"optional": true
|
|
157
|
-
},
|
|
158
|
-
"mysql2": {
|
|
159
|
-
"optional": true
|
|
160
|
-
},
|
|
161
|
-
"pg": {
|
|
162
|
-
"optional": true
|
|
163
|
-
}
|
|
67
|
+
"roaring": "2.7.0",
|
|
68
|
+
"@cloudbitmaps/core": "^0.11.0"
|
|
164
69
|
},
|
|
165
70
|
"devDependencies": {
|
|
166
|
-
"@aws-sdk/client-dynamodb": "^3.1075.0",
|
|
167
|
-
"@aws-sdk/client-s3": "^3.1075.0",
|
|
168
|
-
"@azure/storage-blob": "^12",
|
|
169
|
-
"@google-cloud/storage": "^7.0.0",
|
|
170
|
-
"cassandra-driver": "^4",
|
|
171
|
-
"ioredis": "^5",
|
|
172
|
-
"mongodb": "^6",
|
|
173
|
-
"mysql2": "^3.23.1",
|
|
174
|
-
"pg": "^8",
|
|
175
|
-
"@types/pg": "^8",
|
|
176
71
|
"@types/node": "^22.0.0",
|
|
177
|
-
"tsup": "^8.0.0",
|
|
178
72
|
"typescript": "^5.5.0"
|
|
179
73
|
},
|
|
180
74
|
"scripts": {
|
|
181
|
-
"build": "
|
|
75
|
+
"build": "node ../../scripts/build.mjs"
|
|
182
76
|
}
|
|
183
77
|
}
|
package/dist/azure/index.cjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var azure = require('@cloudbitmaps/core/azure');
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Object.keys(azure).forEach(function (k) {
|
|
8
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
9
|
-
enumerable: true,
|
|
10
|
-
get: function () { return azure[k]; }
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
//# sourceMappingURL=index.cjs.map
|
|
14
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/azure/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs","sourcesContent":[]}
|
package/dist/azure/index.d.cts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from '@cloudbitmaps/core/azure';
|
|
2
|
-
import '@cloudbitmaps/core/cassandra';
|
|
3
|
-
import '@cloudbitmaps/core/dynamodb';
|
|
4
|
-
import '@cloudbitmaps/core/gcs';
|
|
5
|
-
import '@cloudbitmaps/core/mongodb';
|
|
6
|
-
import '@cloudbitmaps/core/mysql';
|
|
7
|
-
import '@cloudbitmaps/core/postgres';
|
|
8
|
-
import '@cloudbitmaps/core/redis';
|
|
9
|
-
import '@cloudbitmaps/core/s3';
|
package/dist/azure/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from '@cloudbitmaps/core/azure';
|
|
2
|
-
import '@cloudbitmaps/core/cassandra';
|
|
3
|
-
import '@cloudbitmaps/core/dynamodb';
|
|
4
|
-
import '@cloudbitmaps/core/gcs';
|
|
5
|
-
import '@cloudbitmaps/core/mongodb';
|
|
6
|
-
import '@cloudbitmaps/core/mysql';
|
|
7
|
-
import '@cloudbitmaps/core/postgres';
|
|
8
|
-
import '@cloudbitmaps/core/redis';
|
|
9
|
-
import '@cloudbitmaps/core/s3';
|
package/dist/azure/index.js
DELETED
package/dist/azure/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js","sourcesContent":[]}
|