@frostpillar/frostpillar-btree 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README-JA.md +93 -59
  2. package/README.md +93 -59
  3. package/dist/InMemoryBTree.d.cts +45 -0
  4. package/dist/btree/autoScale.d.cts +11 -0
  5. package/dist/btree/bulkLoad.d.cts +5 -0
  6. package/dist/btree/deleteRange.d.cts +3 -0
  7. package/dist/btree/entry-lookup.d.cts +8 -0
  8. package/dist/btree/integrity-helpers.d.cts +6 -0
  9. package/dist/btree/integrity.d.cts +2 -0
  10. package/dist/btree/mutations.d.cts +10 -0
  11. package/dist/btree/navigation.d.cts +23 -0
  12. package/dist/btree/node-ops.d.cts +15 -0
  13. package/dist/btree/rangeQuery.d.cts +7 -0
  14. package/dist/btree/rebalance-branch.d.cts +4 -0
  15. package/dist/btree/rebalance.d.cts +7 -0
  16. package/dist/btree/serialization.d.cts +18 -0
  17. package/dist/btree/split.d.cts +3 -0
  18. package/dist/btree/stats.d.cts +2 -0
  19. package/dist/btree/traversal.d.cts +7 -0
  20. package/dist/btree/types.d.cts +110 -0
  21. package/dist/{chunk-UGGWGP4E.js → chunk-OFXDCKLC.js} +4 -3
  22. package/dist/concurrency/ConcurrentInMemoryBTree.d.cts +49 -0
  23. package/dist/concurrency/coordinator.d.cts +21 -0
  24. package/dist/concurrency/helpers.d.cts +34 -0
  25. package/dist/concurrency/index.d.cts +2 -0
  26. package/dist/concurrency/syncLogValidation.d.cts +2 -0
  27. package/dist/concurrency/types.d.cts +54 -0
  28. package/dist/concurrency/writeOps.d.cts +48 -0
  29. package/dist/core.cjs +4 -3
  30. package/dist/core.d.cts +4 -0
  31. package/dist/core.js +1 -1
  32. package/dist/errors.d.cts +12 -0
  33. package/dist/frostpillar-btree-core.min.js +1 -1
  34. package/dist/frostpillar-btree.min.js +1 -1
  35. package/dist/index.cjs +4 -3
  36. package/dist/index.d.cts +6 -0
  37. package/dist/index.js +1 -1
  38. package/package.json +21 -9
@@ -0,0 +1,23 @@
1
+ import { type BTreeState, type LeafNode } from './types.cjs';
2
+ export declare const findLeafForKey: <TKey, TValue>(state: BTreeState<TKey, TValue>, userKey: TKey, sequence: number) => LeafNode<TKey, TValue>;
3
+ export declare const lowerBoundInLeaf: <TKey, TValue>(state: BTreeState<TKey, TValue>, leaf: LeafNode<TKey, TValue>, userKey: TKey, sequence: number) => number;
4
+ export declare const upperBoundInLeaf: <TKey, TValue>(state: BTreeState<TKey, TValue>, leaf: LeafNode<TKey, TValue>, userKey: TKey, sequence: number) => number;
5
+ export declare const findLeafFromHint: <TKey, TValue>(state: BTreeState<TKey, TValue>, hint: LeafNode<TKey, TValue>, userKey: TKey, sequence: number) => LeafNode<TKey, TValue>;
6
+ /** Returns shared cursor — caller must consume result before the next navigation call. */
7
+ export declare const findFirstMatchingUserKey: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => {
8
+ leaf: LeafNode<TKey, TValue>;
9
+ index: number;
10
+ } | null;
11
+ /** Returns shared cursor — caller must consume result before the next navigation call. */
12
+ export declare const findLastMatchingUserKey: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => {
13
+ leaf: LeafNode<TKey, TValue>;
14
+ index: number;
15
+ } | null;
16
+ export declare const hasKeyEntry: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => boolean;
17
+ export declare const findNextHigherKey: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => TKey | null;
18
+ export declare const findNextLowerKey: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => TKey | null;
19
+ /** Returns shared cursor — caller must consume result before the next navigation call. */
20
+ export declare const findPairOrNextLower: <TKey, TValue>(state: BTreeState<TKey, TValue>, key: TKey) => {
21
+ leaf: LeafNode<TKey, TValue>;
22
+ index: number;
23
+ } | null;
@@ -0,0 +1,15 @@
1
+ import type { BranchNode, BTreeNode, LeafEntry, LeafNode, NodeKey } from './types.cjs';
2
+ export declare const createLeafNode: <TKey, TValue>(entries: LeafEntry<TKey, TValue>[], parent: BranchNode<TKey, TValue> | null) => LeafNode<TKey, TValue>;
3
+ export declare const createBranchNode: <TKey, TValue>(children: BTreeNode<TKey, TValue>[], parent: BranchNode<TKey, TValue> | null) => BranchNode<TKey, TValue>;
4
+ export declare const leafEntryCount: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => number;
5
+ export declare const leafEntryAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, i: number) => LeafEntry<TKey, TValue>;
6
+ export declare const leafShiftEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => LeafEntry<TKey, TValue> | undefined;
7
+ export declare const leafPopEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => LeafEntry<TKey, TValue> | undefined;
8
+ export declare const leafUnshiftEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, entry: LeafEntry<TKey, TValue>) => void;
9
+ export declare const leafRemoveAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, logicalIndex: number) => void;
10
+ export declare const leafInsertAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, logicalIndex: number, entry: LeafEntry<TKey, TValue>) => void;
11
+ export declare const leafCompact: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => void;
12
+ export declare const branchCompact: <TKey, TValue>(branch: BranchNode<TKey, TValue>) => void;
13
+ export declare const branchChildCount: <TKey, TValue>(branch: BranchNode<TKey, TValue>) => number;
14
+ export declare const branchInsertAt: <TKey, TValue>(branch: BranchNode<TKey, TValue>, logicalIndex: number, child: BTreeNode<TKey, TValue>, key: NodeKey<TKey>) => void;
15
+ export declare const branchRemoveAt: <TKey, TValue>(branch: BranchNode<TKey, TValue>, physIndex: number) => void;
@@ -0,0 +1,7 @@
1
+ import { type BTreeEntry, type BTreeState, type RangeBounds } from './types.cjs';
2
+ export declare function isEmptyRange<TKey>(compare: (a: TKey, b: TKey) => number, startKey: TKey, endKey: TKey, options?: RangeBounds): boolean;
3
+ export declare const countRangeEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>, startKey: TKey, endKey: TKey, options?: RangeBounds) => number;
4
+ /** Single-pass range query that produces public entries (via freezeEntry) inline. */
5
+ export declare const rangeQueryPublicEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>, startKey: TKey, endKey: TKey, options?: RangeBounds) => BTreeEntry<TKey, TValue>[];
6
+ /** Streaming range iteration — invokes callback for each entry without array allocation. */
7
+ export declare const forEachRangeEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>, startKey: TKey, endKey: TKey, callback: (entry: BTreeEntry<TKey, TValue>) => void, options?: RangeBounds) => void;
@@ -0,0 +1,4 @@
1
+ import { type BTreeNode, type BTreeState, type BranchNode } from './types.cjs';
2
+ export declare const updateMinKeyInAncestors: <TKey, TValue>(node: BTreeNode<TKey, TValue>) => void;
3
+ export declare const removeChildFromBranch: <TKey, TValue>(branch: BranchNode<TKey, TValue>, childIndex: number) => void;
4
+ export declare const rebalanceAfterBranchRemoval: <TKey, TValue>(state: BTreeState<TKey, TValue>, branch: BranchNode<TKey, TValue>) => void;
@@ -0,0 +1,7 @@
1
+ import { type BTreeState, type LeafNode } from './types.cjs';
2
+ import { updateMinKeyInAncestors } from './rebalance-branch.cjs';
3
+ export { updateMinKeyInAncestors };
4
+ /** Applies the lazy divisor to a minimum-occupancy value. */
5
+ export declare const applyLazyThreshold: (min: number) => number;
6
+ export declare const leafRebalanceThreshold: <TKey, TValue>(state: BTreeState<TKey, TValue>) => number;
7
+ export declare const rebalanceAfterLeafRemoval: <TKey, TValue>(state: BTreeState<TKey, TValue>, leaf: LeafNode<TKey, TValue>) => void;
@@ -0,0 +1,18 @@
1
+ import { type BTreeState, type DeleteRebalancePolicy, type DuplicateKeyPolicy, type InMemoryBTreeConfig, type KeyComparator } from './types.cjs';
2
+ export interface BTreeJSON<TKey, TValue> {
3
+ version: number;
4
+ config: {
5
+ maxLeafEntries: number;
6
+ maxBranchChildren: number;
7
+ duplicateKeys: DuplicateKeyPolicy;
8
+ enableEntryIdLookup: boolean;
9
+ autoScale: boolean;
10
+ deleteRebalancePolicy?: DeleteRebalancePolicy;
11
+ };
12
+ entries: [TKey, TValue][];
13
+ }
14
+ export declare const buildConfigFromState: <TKey, TValue>(state: BTreeState<TKey, TValue>) => InMemoryBTreeConfig<TKey>;
15
+ export declare const serializeToJSON: <TKey, TValue>(state: BTreeState<TKey, TValue>) => BTreeJSON<TKey, TValue>;
16
+ export declare const validateBTreeJSON: <TKey, TValue>(json: BTreeJSON<TKey, TValue>) => void;
17
+ export declare const validateBTreeJSONSortOrder: <TKey, TValue>(json: BTreeJSON<TKey, TValue>, compareKeys: KeyComparator<TKey>) => void;
18
+ export declare const buildConfigFromJSON: <TKey>(json: BTreeJSON<TKey, unknown>, compareKeys: KeyComparator<TKey>) => InMemoryBTreeConfig<TKey>;
@@ -0,0 +1,3 @@
1
+ import { type BTreeState, type BranchNode, type LeafNode } from './types.cjs';
2
+ export declare const splitLeaf: <TKey, TValue>(state: BTreeState<TKey, TValue>, leaf: LeafNode<TKey, TValue>) => void;
3
+ export declare const splitBranch: <TKey, TValue>(state: BTreeState<TKey, TValue>, branch: BranchNode<TKey, TValue>) => void;
@@ -0,0 +1,2 @@
1
+ import { type BTreeState, type BTreeStats } from './types.cjs';
2
+ export declare const getStats: <TKey, TValue>(state: BTreeState<TKey, TValue>) => BTreeStats;
@@ -0,0 +1,7 @@
1
+ import { type BTreeEntry, type BTreeState } from './types.cjs';
2
+ /** Collect all entries into a pre-allocated array, frozen for safe external use. */
3
+ export declare const snapshotEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>) => BTreeEntry<TKey, TValue>[];
4
+ /** Collect all internal entries (no freeze) for internal use (clone, serialize). */
5
+ export declare const collectInternalEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>) => BTreeEntry<TKey, TValue>[];
6
+ /** Iterate all entries, invoking callback with frozen entries. */
7
+ export declare const forEachEntry: <TKey, TValue>(state: BTreeState<TKey, TValue>, callback: (entry: BTreeEntry<TKey, TValue>) => void, thisArg?: unknown) => void;
@@ -0,0 +1,110 @@
1
+ export declare const DEFAULT_MAX_LEAF_ENTRIES = 64;
2
+ export declare const DEFAULT_MAX_BRANCH_CHILDREN = 64;
3
+ export declare const MIN_NODE_CAPACITY = 3;
4
+ export declare const MAX_NODE_CAPACITY = 16384;
5
+ export declare const NODE_LEAF: 0;
6
+ export declare const NODE_BRANCH: 1;
7
+ export type KeyComparator<TKey> = (left: TKey, right: TKey) => number;
8
+ export type DuplicateKeyPolicy = 'allow' | 'reject' | 'replace';
9
+ export type DeleteRebalancePolicy = 'standard' | 'lazy';
10
+ /**
11
+ * Defines the inclusivity of the lower and upper bounds for a key range scan.
12
+ * Both bounds default to `'inclusive'` when omitted.
13
+ */
14
+ export interface RangeBounds {
15
+ /** Lower bound type. Defaults to `'inclusive'` when omitted. */
16
+ lowerBound?: 'inclusive' | 'exclusive';
17
+ /** Upper bound type. Defaults to `'inclusive'` when omitted. */
18
+ upperBound?: 'inclusive' | 'exclusive';
19
+ }
20
+ export declare const normalizeDuplicateKeyPolicy: (value: DuplicateKeyPolicy | undefined) => DuplicateKeyPolicy;
21
+ export declare const normalizeDeleteRebalancePolicy: (value: DeleteRebalancePolicy | undefined) => DeleteRebalancePolicy;
22
+ export type EntryId = number & {
23
+ readonly __brand: 'EntryId';
24
+ };
25
+ export interface BTreeEntry<TKey, TValue> {
26
+ readonly entryId: EntryId;
27
+ readonly key: TKey;
28
+ readonly value: TValue;
29
+ }
30
+ export interface NodeKey<TKey> {
31
+ key: TKey;
32
+ sequence: number;
33
+ }
34
+ /** Internal mutable entry stored in leaf nodes. */
35
+ export interface LeafEntry<TKey, TValue> {
36
+ entryId: EntryId;
37
+ key: TKey;
38
+ value: TValue;
39
+ }
40
+ /**
41
+ * Freezes and returns an internal entry for safe exposure via the public API.
42
+ * Idempotent: re-freezing an already-frozen object is a no-op in V8.
43
+ * All entries are frozen at creation via createEntry, so this is a zero-allocation cast.
44
+ */
45
+ export declare const freezeEntry: <TKey, TValue>(entry: LeafEntry<TKey, TValue>) => BTreeEntry<TKey, TValue>;
46
+ /**
47
+ * Creates a frozen LeafEntry with a canonical property order.
48
+ * All entry creation MUST go through this function to guarantee a single
49
+ * V8 hidden class across all entries in the tree.
50
+ */
51
+ export declare const createEntry: <TKey, TValue>(key: TKey, entryId: EntryId, value: TValue) => LeafEntry<TKey, TValue>;
52
+ export interface LeafNode<TKey, TValue> {
53
+ kind: typeof NODE_LEAF;
54
+ entries: LeafEntry<TKey, TValue>[];
55
+ entryOffset: number;
56
+ parent: BranchNode<TKey, TValue> | null;
57
+ indexInParent: number;
58
+ prev: LeafNode<TKey, TValue> | null;
59
+ next: LeafNode<TKey, TValue> | null;
60
+ }
61
+ export interface BranchNode<TKey, TValue> {
62
+ kind: typeof NODE_BRANCH;
63
+ children: BTreeNode<TKey, TValue>[];
64
+ keys: NodeKey<TKey>[];
65
+ childOffset: number;
66
+ parent: BranchNode<TKey, TValue> | null;
67
+ indexInParent: number;
68
+ }
69
+ export type BTreeNode<TKey, TValue> = LeafNode<TKey, TValue> | BranchNode<TKey, TValue>;
70
+ export interface BTreeState<TKey, TValue> {
71
+ compareKeys: KeyComparator<TKey>;
72
+ maxLeafEntries: number;
73
+ maxBranchChildren: number;
74
+ duplicateKeys: DuplicateKeyPolicy;
75
+ root: BTreeNode<TKey, TValue>;
76
+ leftmostLeaf: LeafNode<TKey, TValue>;
77
+ rightmostLeaf: LeafNode<TKey, TValue>;
78
+ entryCount: number;
79
+ nextSequence: number;
80
+ minLeafEntries: number;
81
+ minBranchChildren: number;
82
+ entryKeys: Map<EntryId, TKey> | null;
83
+ autoScale: boolean;
84
+ deleteRebalancePolicy: DeleteRebalancePolicy;
85
+ _nextAutoScaleThreshold: number;
86
+ /** @internal Shared return object for navigation functions — never store a reference across calls. */
87
+ _cursor: {
88
+ leaf: LeafNode<TKey, TValue>;
89
+ index: number;
90
+ };
91
+ }
92
+ export interface InMemoryBTreeConfig<TKey> {
93
+ compareKeys: KeyComparator<TKey>;
94
+ maxLeafEntries?: number;
95
+ maxBranchChildren?: number;
96
+ duplicateKeys?: DuplicateKeyPolicy;
97
+ enableEntryIdLookup?: boolean;
98
+ autoScale?: boolean;
99
+ deleteRebalancePolicy?: DeleteRebalancePolicy;
100
+ }
101
+ export interface BTreeStats {
102
+ height: number;
103
+ leafCount: number;
104
+ branchCount: number;
105
+ entryCount: number;
106
+ }
107
+ export declare const isLeafNode: <TKey, TValue>(node: BTreeNode<TKey, TValue>) => node is LeafNode<TKey, TValue>;
108
+ export declare const writeMinKeyTo: <TKey, TValue>(node: BTreeNode<TKey, TValue>, target: NodeKey<TKey>) => boolean;
109
+ export declare const normalizeNodeCapacity: (value: number | undefined, field: string, defaultValue: number) => number;
110
+ export { createLeafNode, createBranchNode, leafEntryCount, leafEntryAt, leafShiftEntry, leafPopEntry, leafUnshiftEntry, leafRemoveAt, leafInsertAt, leafCompact, branchCompact, branchChildCount, branchInsertAt, branchRemoveAt, } from './node-ops.cjs';
@@ -806,8 +806,10 @@ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rig
806
806
  const borrowed = leafShiftEntry(rightSibling);
807
807
  if (borrowed === void 0)
808
808
  throw new BTreeInvariantError("right leaf borrow failed");
809
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
809
810
  leaf.entries.push(borrowed);
810
811
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
812
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
811
813
  return true;
812
814
  }
813
815
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
@@ -830,8 +832,10 @@ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSi
830
832
  return;
831
833
  }
832
834
  if (rightSibling !== null) {
835
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
833
836
  mergeLeafEntries(leaf, rightSibling);
834
837
  detachLeafFromChain(state, rightSibling);
838
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
835
839
  removeChildFromBranch(parent, leafIndex + 1);
836
840
  rebalanceAfterBranchRemoval(state, parent);
837
841
  return;
@@ -902,9 +906,6 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
902
906
  break;
903
907
  safetyGuard -= 1;
904
908
  }
905
- if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
906
- updateMinKeyInAncestors(leaf);
907
- }
908
909
  return countAfterSplice;
909
910
  };
910
911
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
@@ -0,0 +1,49 @@
1
+ import { InMemoryBTree, type BTreeEntry, type BTreeJSON, type BTreeStats, type EntryId, type RangeBounds } from '../InMemoryBTree.cjs';
2
+ import type { KeyComparator } from '../btree/types.cjs';
3
+ import type { ConcurrentInMemoryBTreeConfig } from './types.cjs';
4
+ export declare class ConcurrentInMemoryBTree<TKey, TValue> {
5
+ private readonly coord;
6
+ private readonly compareKeys;
7
+ private readonly duplicateKeys;
8
+ constructor(config: ConcurrentInMemoryBTreeConfig<TKey, TValue>);
9
+ sync(): Promise<void>;
10
+ syncThenRead<TResult>(fn: (tree: InMemoryBTree<TKey, TValue>) => TResult): Promise<TResult>;
11
+ put(key: TKey, value: TValue): Promise<EntryId>;
12
+ remove(key: TKey): Promise<BTreeEntry<TKey, TValue> | null>;
13
+ removeById(entryId: EntryId): Promise<BTreeEntry<TKey, TValue> | null>;
14
+ updateById(entryId: EntryId, value: TValue): Promise<BTreeEntry<TKey, TValue> | null>;
15
+ popFirst(): Promise<BTreeEntry<TKey, TValue> | null>;
16
+ popLast(): Promise<BTreeEntry<TKey, TValue> | null>;
17
+ putMany(entries: readonly {
18
+ key: TKey;
19
+ value: TValue;
20
+ }[]): Promise<EntryId[]>;
21
+ deleteRange(startKey: TKey, endKey: TKey, options?: RangeBounds): Promise<number>;
22
+ clear(): Promise<void>;
23
+ get(key: TKey): Promise<TValue | null>;
24
+ hasKey(key: TKey): Promise<boolean>;
25
+ findFirst(key: TKey): Promise<BTreeEntry<TKey, TValue> | null>;
26
+ findLast(key: TKey): Promise<BTreeEntry<TKey, TValue> | null>;
27
+ range(startKey: TKey, endKey: TKey, options?: RangeBounds): Promise<BTreeEntry<TKey, TValue>[]>;
28
+ snapshot(): Promise<BTreeEntry<TKey, TValue>[]>;
29
+ size(): Promise<number>;
30
+ assertInvariants(): Promise<void>;
31
+ getStats(): Promise<BTreeStats>;
32
+ peekFirst(): Promise<BTreeEntry<TKey, TValue> | null>;
33
+ peekLast(): Promise<BTreeEntry<TKey, TValue> | null>;
34
+ peekById(entryId: EntryId): Promise<BTreeEntry<TKey, TValue> | null>;
35
+ count(startKey: TKey, endKey: TKey, options?: RangeBounds): Promise<number>;
36
+ nextHigherKey(key: TKey): Promise<TKey | null>;
37
+ nextLowerKey(key: TKey): Promise<TKey | null>;
38
+ getPairOrNextLower(key: TKey): Promise<BTreeEntry<TKey, TValue> | null>;
39
+ entries(): Promise<BTreeEntry<TKey, TValue>[]>;
40
+ entriesReversed(): Promise<BTreeEntry<TKey, TValue>[]>;
41
+ keys(): Promise<TKey[]>;
42
+ values(): Promise<TValue[]>;
43
+ forEach(callback: (entry: BTreeEntry<TKey, TValue>) => void): Promise<void>;
44
+ forEachRange(startKey: TKey, endKey: TKey, callback: (entry: BTreeEntry<TKey, TValue>) => void, options?: RangeBounds): Promise<void>;
45
+ [Symbol.asyncIterator](): AsyncIterableIterator<BTreeEntry<TKey, TValue>>;
46
+ clone(): Promise<InMemoryBTree<TKey, TValue>>;
47
+ toJSON(): Promise<BTreeJSON<TKey, TValue>>;
48
+ static fromJSON<TKey, TValue>(json: BTreeJSON<TKey, TValue>, compareKeys: KeyComparator<TKey>): InMemoryBTree<TKey, TValue>;
49
+ }
@@ -0,0 +1,21 @@
1
+ import { InMemoryBTree } from '../InMemoryBTree.cjs';
2
+ import type { BTreeMutation, ReadMode, SharedTreeStore } from './types.cjs';
3
+ import { type MutationResult } from './helpers.cjs';
4
+ export declare class Coordinator<TKey, TValue> {
5
+ readonly tree: InMemoryBTree<TKey, TValue>;
6
+ readonly store: SharedTreeStore<TKey, TValue>;
7
+ readonly maxRetries: number;
8
+ readonly maxSyncMutationsPerBatch: number;
9
+ readonly configFingerprint: string;
10
+ readonly readMode: ReadMode;
11
+ currentVersion: bigint;
12
+ operationQueue: Promise<void>;
13
+ initSeen: boolean;
14
+ corrupted: boolean;
15
+ constructor(tree: InMemoryBTree<TKey, TValue>, store: SharedTreeStore<TKey, TValue>, maxRetries: number, maxSyncMutationsPerBatch: number, configFingerprint: string, readMode: ReadMode);
16
+ syncUnlocked(): Promise<void>;
17
+ runExclusive<TResult>(operation: () => Promise<TResult>): Promise<TResult>;
18
+ readOp<TResult>(fn: (tree: InMemoryBTree<TKey, TValue>) => TResult): Promise<TResult>;
19
+ appendAndApply<TMutation extends BTreeMutation<TKey, TValue>>(evaluate: (tree: InMemoryBTree<TKey, TValue>) => TMutation | null): Promise<MutationResult<TKey, TValue, TMutation> | null>;
20
+ writeOp<TMutation extends BTreeMutation<TKey, TValue>>(evaluator: (tree: InMemoryBTree<TKey, TValue>) => TMutation | null): Promise<MutationResult<TKey, TValue, TMutation> | null>;
21
+ }
@@ -0,0 +1,34 @@
1
+ import type { BTreeMutation, ConcurrentInMemoryBTreeConfig, ReadMode } from './types.cjs';
2
+ import type { BTreeEntry, EntryId } from '../InMemoryBTree.cjs';
3
+ export declare const computeConfigFingerprint: <TKey>(config: ConcurrentInMemoryBTreeConfig<TKey, unknown>) => string;
4
+ export type MutationResult<TKey, TValue, TMutation extends BTreeMutation<TKey, TValue>> = TMutation extends {
5
+ type: 'init';
6
+ } ? null : TMutation extends {
7
+ type: 'put';
8
+ } ? EntryId : TMutation extends {
9
+ type: 'putMany';
10
+ } ? EntryId[] : TMutation extends {
11
+ type: 'remove';
12
+ } ? BTreeEntry<TKey, TValue> | null : TMutation extends {
13
+ type: 'removeById';
14
+ } ? BTreeEntry<TKey, TValue> | null : TMutation extends {
15
+ type: 'updateById';
16
+ } ? BTreeEntry<TKey, TValue> | null : TMutation extends {
17
+ type: 'popFirst';
18
+ } ? BTreeEntry<TKey, TValue> | null : TMutation extends {
19
+ type: 'popLast';
20
+ } ? BTreeEntry<TKey, TValue> | null : TMutation extends {
21
+ type: 'deleteRange';
22
+ } ? number : TMutation extends {
23
+ type: 'clear';
24
+ } ? null : never;
25
+ export type AnyMutationResult<TKey, TValue> = EntryId | EntryId[] | BTreeEntry<TKey, TValue> | number | null;
26
+ export declare const assertNeverMutation: (mutation: never) => never;
27
+ export declare const validateMutationBatch: <TKey, TValue>(mutations: BTreeMutation<TKey, TValue>[], expectedConfigFingerprint?: string) => void;
28
+ export declare const normalizeMaxRetries: (value: number | undefined) => number;
29
+ export declare const normalizeMaxSyncMutationsPerBatch: (value: number | undefined) => number;
30
+ export declare const normalizeReadMode: (value: ReadMode | undefined) => ReadMode;
31
+ export declare function assertAppendVersionContract(expectedVersion: bigint, appendResult: unknown): asserts appendResult is {
32
+ applied: boolean;
33
+ version: bigint;
34
+ };
@@ -0,0 +1,2 @@
1
+ export { ConcurrentInMemoryBTree } from './ConcurrentInMemoryBTree.cjs';
2
+ export type { BTreeMutation, ConcurrentInMemoryBTreeConfig, ReadMode, SharedTreeLog, SharedTreeStore, } from './types.cjs';
@@ -0,0 +1,2 @@
1
+ import type { SharedTreeLog } from './types.cjs';
2
+ export declare const validateSyncLog: <TKey, TValue>(log: SharedTreeLog<TKey, TValue>, maxSyncMutationsPerBatch: number) => void;
@@ -0,0 +1,54 @@
1
+ import type { EntryId, InMemoryBTreeConfig, RangeBounds } from '../InMemoryBTree.cjs';
2
+ export type BTreeMutation<TKey, TValue> = {
3
+ type: 'init';
4
+ configFingerprint: string;
5
+ } | {
6
+ type: 'put';
7
+ key: TKey;
8
+ value: TValue;
9
+ } | {
10
+ type: 'putMany';
11
+ entries: readonly {
12
+ key: TKey;
13
+ value: TValue;
14
+ }[];
15
+ } | {
16
+ type: 'remove';
17
+ key: TKey;
18
+ } | {
19
+ type: 'removeById';
20
+ entryId: EntryId;
21
+ } | {
22
+ type: 'updateById';
23
+ entryId: EntryId;
24
+ value: TValue;
25
+ } | {
26
+ type: 'popFirst';
27
+ } | {
28
+ type: 'popLast';
29
+ } | {
30
+ type: 'deleteRange';
31
+ startKey: TKey;
32
+ endKey: TKey;
33
+ options?: RangeBounds;
34
+ } | {
35
+ type: 'clear';
36
+ };
37
+ export interface SharedTreeLog<TKey, TValue> {
38
+ version: bigint;
39
+ mutations: BTreeMutation<TKey, TValue>[];
40
+ }
41
+ export interface SharedTreeStore<TKey, TValue> {
42
+ getLogEntriesSince(version: bigint): Promise<SharedTreeLog<TKey, TValue>>;
43
+ append(expectedVersion: bigint, mutations: BTreeMutation<TKey, TValue>[]): Promise<{
44
+ applied: boolean;
45
+ version: bigint;
46
+ }>;
47
+ }
48
+ export type ReadMode = 'strong' | 'local';
49
+ export interface ConcurrentInMemoryBTreeConfig<TKey, TValue> extends InMemoryBTreeConfig<TKey> {
50
+ store: SharedTreeStore<TKey, TValue>;
51
+ maxRetries?: number;
52
+ maxSyncMutationsPerBatch?: number;
53
+ readMode?: ReadMode;
54
+ }
@@ -0,0 +1,48 @@
1
+ import { InMemoryBTree, type EntryId, type RangeBounds } from '../InMemoryBTree.cjs';
2
+ import type { KeyComparator, DuplicateKeyPolicy } from '../btree/types.cjs';
3
+ import type { BTreeMutation } from './types.cjs';
4
+ import { type AnyMutationResult } from './helpers.cjs';
5
+ export declare const applyMutationLocal: <TKey, TValue>(tree: InMemoryBTree<TKey, TValue>, mutation: BTreeMutation<TKey, TValue>, onInit: () => void) => AnyMutationResult<TKey, TValue>;
6
+ export declare const createPutEvaluator: <TKey, TValue>(duplicateKeys: DuplicateKeyPolicy, key: TKey, value: TValue) => ((tree: InMemoryBTree<TKey, TValue>) => {
7
+ type: "put";
8
+ key: TKey;
9
+ value: TValue;
10
+ });
11
+ export declare const createRemoveEvaluator: <TKey, TValue>(key: TKey) => ((tree: InMemoryBTree<TKey, TValue>) => {
12
+ type: "remove";
13
+ key: TKey;
14
+ } | null);
15
+ export declare const createRemoveByIdEvaluator: <TKey, TValue>(entryId: EntryId) => ((tree: InMemoryBTree<TKey, TValue>) => {
16
+ type: "removeById";
17
+ entryId: EntryId;
18
+ } | null);
19
+ export declare const createUpdateByIdEvaluator: <TKey, TValue>(entryId: EntryId, value: TValue) => ((tree: InMemoryBTree<TKey, TValue>) => {
20
+ type: "updateById";
21
+ entryId: EntryId;
22
+ value: TValue;
23
+ } | null);
24
+ export declare const createPopFirstEvaluator: <TKey, TValue>() => ((tree: InMemoryBTree<TKey, TValue>) => {
25
+ type: "popFirst";
26
+ } | null);
27
+ export declare const createPopLastEvaluator: <TKey, TValue>() => ((tree: InMemoryBTree<TKey, TValue>) => {
28
+ type: "popLast";
29
+ } | null);
30
+ export declare const createPutManyEvaluator: <TKey, TValue>(entries: readonly {
31
+ key: TKey;
32
+ value: TValue;
33
+ }[], duplicateKeys: DuplicateKeyPolicy, compareKeys: KeyComparator<TKey>) => ((tree: InMemoryBTree<TKey, TValue>) => {
34
+ type: "putMany";
35
+ entries: readonly {
36
+ key: TKey;
37
+ value: TValue;
38
+ }[];
39
+ });
40
+ export declare const createDeleteRangeEvaluator: <TKey, TValue>(startKey: TKey, endKey: TKey, options: RangeBounds | undefined) => ((tree: InMemoryBTree<TKey, TValue>) => {
41
+ type: "deleteRange";
42
+ startKey: TKey;
43
+ endKey: TKey;
44
+ options?: RangeBounds;
45
+ } | null);
46
+ export declare const createClearEvaluator: <TKey, TValue>() => ((tree: InMemoryBTree<TKey, TValue>) => {
47
+ type: "clear";
48
+ });
package/dist/core.cjs CHANGED
@@ -827,8 +827,10 @@ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rig
827
827
  const borrowed = leafShiftEntry(rightSibling);
828
828
  if (borrowed === void 0)
829
829
  throw new BTreeInvariantError("right leaf borrow failed");
830
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
830
831
  leaf.entries.push(borrowed);
831
832
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
833
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
832
834
  return true;
833
835
  }
834
836
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
@@ -851,8 +853,10 @@ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSi
851
853
  return;
852
854
  }
853
855
  if (rightSibling !== null) {
856
+ const leafWasEmpty = leafEntryCount(leaf) === 0;
854
857
  mergeLeafEntries(leaf, rightSibling);
855
858
  detachLeafFromChain(state, rightSibling);
859
+ if (leafWasEmpty) updateMinKeyInAncestors(leaf);
856
860
  removeChildFromBranch(parent, leafIndex + 1);
857
861
  rebalanceAfterBranchRemoval(state, parent);
858
862
  return;
@@ -923,9 +927,6 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
923
927
  break;
924
928
  safetyGuard -= 1;
925
929
  }
926
- if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
927
- updateMinKeyInAncestors(leaf);
928
- }
929
930
  return countAfterSplice;
930
931
  };
931
932
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
@@ -0,0 +1,4 @@
1
+ export { InMemoryBTree } from './InMemoryBTree.cjs';
2
+ export type { BTreeEntry, BTreeJSON, BTreeStats, DeleteRebalancePolicy, DuplicateKeyPolicy, EntryId, InMemoryBTreeConfig, RangeBounds, } from './InMemoryBTree.cjs';
3
+ export { BTreeInvariantError, BTreeValidationError } from './errors.cjs';
4
+ export type { KeyComparator } from './btree/types.cjs';
package/dist/core.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  BTreeInvariantError,
3
3
  BTreeValidationError,
4
4
  InMemoryBTree
5
- } from "./chunk-UGGWGP4E.js";
5
+ } from "./chunk-OFXDCKLC.js";
6
6
  export {
7
7
  BTreeInvariantError,
8
8
  BTreeValidationError,
@@ -0,0 +1,12 @@
1
+ /** Thrown when a caller supplies invalid input, such as a duplicate key, sequence overflow, or malformed configuration. */
2
+ export declare class BTreeValidationError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /** Thrown when an internal tree invariant is violated, indicating a bug in the library rather than invalid caller input. */
6
+ export declare class BTreeInvariantError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ /** Thrown when the concurrent store contract is violated or a mutation batch is malformed during optimistic concurrency operations. */
10
+ export declare class BTreeConcurrencyError extends Error {
11
+ constructor(message: string);
12
+ }