@frostpillar/frostpillar-btree 0.2.4 → 0.2.6
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/README-JA.md +39 -6
- package/README.md +39 -6
- package/dist/InMemoryBTree.d.ts +33 -0
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/node-ops.d.ts +15 -0
- package/dist/btree/rangeQuery.d.ts +1 -0
- package/dist/btree/serialization.d.ts +0 -4
- package/dist/btree/types.d.ts +12 -26
- package/dist/{chunk-ZA3EQNDI.js → chunk-OWHENPGJ.js} +259 -212
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +20 -16
- package/dist/concurrency/helpers.d.ts +9 -2
- package/dist/concurrency/types.d.ts +14 -1
- package/dist/concurrency/writeOps.d.ts +48 -0
- package/dist/core.cjs +259 -212
- package/dist/core.js +1 -1
- package/dist/errors.d.ts +3 -0
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +543 -278
- package/dist/index.js +285 -67
- package/package.json +1 -1
package/README-JA.md
CHANGED
|
@@ -280,7 +280,7 @@ const last = tree.popLast();
|
|
|
280
280
|
// { entryId: ..., key: 20, value: 'twenty' } または null(ツリーが空の場合)
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
-
**`clear()`** -- 全エントリを削除し、ツリーを空の状態に O(1)
|
|
283
|
+
**`clear()`** -- 全エントリを削除し、ツリーを空の状態に O(1) でリセットします。内部シーケンスカウンタはリセット**されない**ため、`EntryId` はインスタンスの存続期間中単調増加し、再利用されません。
|
|
284
284
|
|
|
285
285
|
```ts
|
|
286
286
|
tree.clear();
|
|
@@ -429,7 +429,7 @@ tree.assertInvariants(); // 不正な場合はスロー
|
|
|
429
429
|
|
|
430
430
|
#### クローンとシリアライズ
|
|
431
431
|
|
|
432
|
-
**`clone()`** --
|
|
432
|
+
**`clone()`** -- 構造的に独立したコピーを作成します。ツリー構造(ノード、リンク、エントリID)は完全に独立しますが、格納されたキーと値の参照は元のツリーと共有されます。
|
|
433
433
|
|
|
434
434
|
```ts
|
|
435
435
|
const copy = tree.clone();
|
|
@@ -437,6 +437,8 @@ copy.put(99, 'new');
|
|
|
437
437
|
tree.hasKey(99); // false -- 元のツリーには影響しない
|
|
438
438
|
```
|
|
439
439
|
|
|
440
|
+
注意: `EntryId` の値はクローン内で再割り当てされます — 元のツリーの ID はクローンに対して有効ではありません。
|
|
441
|
+
|
|
440
442
|
**`toJSON()` / `fromJSON()`** -- ツリーをシリアライズ・復元します。
|
|
441
443
|
|
|
442
444
|
```ts
|
|
@@ -501,7 +503,9 @@ const tree = new InMemoryBTree<number, string>({
|
|
|
501
503
|
- **`getLogEntriesSince(version)`** -- 指定バージョン以降のすべてのミューテーションを返し、各インスタンスが最新状態へキャッチアップできるようにします。
|
|
502
504
|
- **`append(expectedVersion, mutations)`** -- バージョンが一致する場合にミューテーションをアトミックに追加します(compare-and-swap)。`{ applied, version }` を返します。
|
|
503
505
|
|
|
504
|
-
|
|
506
|
+
`getLogEntriesSince` で過去のミューテーションを返すストアは**マルチインスタンス・キャッチアップ**を実現します。各インスタンスが未受信のミューテーションをリプレイし、同一状態に収束します。ミューテーションをリプレイしないストア(空の `mutations` 配列と更新された `version` を返す)もサポートされています。この場合、各インスタンスは自身のローカル書き込みとバージョン進行のみを認識します。一貫性要件に応じてリプレイ戦略を選択してください。
|
|
507
|
+
|
|
508
|
+
ストアの実装は自由です。インメモリ配列、データベーステーブル、Redis stream など何でも使えます。以下はリプレイ対応のインメモリ参考実装です。
|
|
505
509
|
|
|
506
510
|
**Node.js / TypeScript:**
|
|
507
511
|
|
|
@@ -754,6 +758,23 @@ try {
|
|
|
754
758
|
|
|
755
759
|
---
|
|
756
760
|
|
|
761
|
+
#### 共有ストアのセキュリティ前提
|
|
762
|
+
|
|
763
|
+
`ConcurrentInMemoryBTree` は共有ストアが**信頼されている**ことを前提としています。悪意を持って細工されたミューテーションペイロードや、異常に大きなペイロードに対する防御機能はありません。
|
|
764
|
+
|
|
765
|
+
**信頼境界:**
|
|
766
|
+
- ストアはあなたのアプリケーションの管理下にあること。
|
|
767
|
+
- 同一ストアを共有するすべてのインスタンスは同一の設定を使用すること(最初の書き込み時に `init` ミューテーションの設定フィンガープリントで検証されますが、リプレイバッチに `init` が含まれている場合に限ります)。
|
|
768
|
+
- ミューテーションはリプレイ前に構造的に検証されますが、セマンティックな正確性(キー型の整合性など)は呼び出し側の責任です。
|
|
769
|
+
|
|
770
|
+
**共有・マルチテナントデプロイメントにおける堅牢化の推奨事項:**
|
|
771
|
+
- 認可レイヤーなしに `append` や `getLogEntriesSince` を信頼されていないクライアントに公開しないこと。
|
|
772
|
+
- 格納するミューテーションペイロードのサイズ制限をストアレベルで適用してから `ConcurrentInMemoryBTree` に渡すこと。
|
|
773
|
+
- `maxSyncMutationsPerBatch` を使用して、1回の sync 呼び出しで適用するミューテーション数を制限すること(デフォルト:100,000)。
|
|
774
|
+
- リプレイ失敗により `sync()` が `BTreeConcurrencyError` をスローした場合、インスタンスは永続的に汚染されます。そのインスタンスを破棄し、新しいインスタンスを作成してください。
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
757
778
|
## API リファレンス
|
|
758
779
|
|
|
759
780
|
### InMemoryBTree
|
|
@@ -791,7 +812,7 @@ try {
|
|
|
791
812
|
| `size` | `() => number` | エントリ数を返す。 |
|
|
792
813
|
| `getStats` | `() => BTreeStats` | 構造統計を返す。 |
|
|
793
814
|
| `assertInvariants` | `() => void` | B+ tree の構造的な整合性を検証する。不正な場合はスローする。 |
|
|
794
|
-
| `clone` | `() => InMemoryBTree<TKey, TValue>` |
|
|
815
|
+
| `clone` | `() => InMemoryBTree<TKey, TValue>` | 構造的に独立したコピーを返す(キー・値参照は共有)。 |
|
|
795
816
|
| `toJSON` | `() => BTreeJSON<TKey, TValue>` | バージョン付き JSON 互換ペイロードにシリアライズする。 |
|
|
796
817
|
| `fromJSON` (静的) | `(json, compareKeys) => InMemoryBTree<TKey, TValue>` | `toJSON` ペイロードからツリーを再構築する。 |
|
|
797
818
|
|
|
@@ -803,18 +824,21 @@ new InMemoryBTree<TKey, TValue>(config: InMemoryBTreeConfig<TKey>)
|
|
|
803
824
|
|
|
804
825
|
### ConcurrentInMemoryBTree
|
|
805
826
|
|
|
806
|
-
`InMemoryBTree`
|
|
827
|
+
`InMemoryBTree` メソッドを `Promise` を返す非同期版として提供します。書き込みは shared store を介して協調し、`readMode` が `'strong'`(デフォルト)の場合は読み取り前に同期します。`readMode` が `'local'` の場合、読み取りは同期なしでローカルツリーに対して実行されます。
|
|
807
828
|
|
|
808
829
|
| メソッド | シグネチャ | 説明 |
|
|
809
830
|
| -------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
810
831
|
| `sync` | `() => Promise<void>` | shared store の最新ログを取得して適用する。 |
|
|
811
832
|
| `put` | `(key: TKey, value: TValue) => Promise<EntryId>` | 楽観的並行制御で挿入する。 |
|
|
833
|
+
| `putMany` | `(entries: readonly { key: TKey; value: TValue }[]) => Promise<EntryId[]>` | 楽観的並行制御で一括挿入する。 |
|
|
812
834
|
| `remove` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | 指定キーに一致する最初のエントリを削除する。 |
|
|
813
835
|
| `removeById` | `(entryId: EntryId) => Promise<BTreeEntry<TKey, TValue> \| null>` | ID でエントリを削除する。 |
|
|
814
836
|
| `peekById` | `(entryId: EntryId) => Promise<BTreeEntry<TKey, TValue> \| null>` | ID でエントリを参照する(事前に同期)。 |
|
|
815
837
|
| `updateById` | `(entryId: EntryId, value: TValue) => Promise<BTreeEntry<TKey, TValue> \| null>` | 楽観的並行制御で ID のエントリ値を更新する。 |
|
|
816
838
|
| `popFirst` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | 最小キーのエントリを削除して返す。 |
|
|
817
839
|
| `popLast` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | 最大キーのエントリを削除して返す。 |
|
|
840
|
+
| `deleteRange` | `(startKey: TKey, endKey: TKey, options?: RangeBounds) => Promise<number>` | 楽観的並行制御で範囲内のエントリを削除する。 |
|
|
841
|
+
| `clear` | `() => Promise<void>` | 楽観的並行制御で全エントリを削除する。 |
|
|
818
842
|
| `peekFirst` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | 最小キーのエントリを返す(事前に同期)。 |
|
|
819
843
|
| `peekLast` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | 最大キーのエントリを返す(事前に同期)。 |
|
|
820
844
|
| `findFirst` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | キーに一致する最初のエントリを返す(事前に同期)。 |
|
|
@@ -826,10 +850,19 @@ new InMemoryBTree<TKey, TValue>(config: InMemoryBTreeConfig<TKey>)
|
|
|
826
850
|
| `nextHigherKey` | `(key: TKey) => Promise<TKey \| null>` | 指定キーより大きい次のキー(事前に同期)。 |
|
|
827
851
|
| `nextLowerKey` | `(key: TKey) => Promise<TKey \| null>` | 指定キーより小さい次のキー(事前に同期)。 |
|
|
828
852
|
| `getPairOrNextLower` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | 一致または次に小さいエントリ(事前に同期)。 |
|
|
853
|
+
| `entries` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | 全エントリを配列で返す(事前に同期)。 |
|
|
854
|
+
| `entriesReversed` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | 全エントリを逆順で配列として返す(事前に同期)。 |
|
|
855
|
+
| `keys` | `() => Promise<TKey[]>` | 全キーを配列で返す(事前に同期)。 |
|
|
856
|
+
| `values` | `() => Promise<TValue[]>` | 全値を配列で返す(事前に同期)。 |
|
|
857
|
+
| `forEach` | `(callback: (entry: BTreeEntry<TKey, TValue>) => void) => Promise<void>` | 全エントリを反復する(事前に同期)。 |
|
|
829
858
|
| `snapshot` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | 全エントリを返す(事前に同期)。 |
|
|
830
859
|
| `size` | `() => Promise<number>` | エントリ数を返す(事前に同期)。 |
|
|
831
860
|
| `getStats` | `() => Promise<BTreeStats>` | 構造統計を返す(事前に同期)。 |
|
|
832
861
|
| `assertInvariants` | `() => Promise<void>` | 構造的な整合性を検証する(事前に同期)。 |
|
|
862
|
+
| `clone` | `() => Promise<InMemoryBTree<TKey, TValue>>` | 独立したローカルコピーを返す(事前に同期)。 |
|
|
863
|
+
| `toJSON` | `() => Promise<BTreeJSON<TKey, TValue>>` | JSON にシリアライズする(事前に同期)。 |
|
|
864
|
+
| `fromJSON` (static) | `(json: BTreeJSON<TKey, TValue>, compareKeys: KeyComparator<TKey>) => InMemoryBTree<TKey, TValue>` | JSON からデシリアライズする(ローカルツリーを返す)。 |
|
|
865
|
+
| `[Symbol.asyncIterator]` | `() => AsyncIterableIterator<BTreeEntry<TKey, TValue>>` | 全エントリを非同期反復する(事前に同期)。 |
|
|
833
866
|
|
|
834
867
|
**コンストラクタ:**
|
|
835
868
|
|
|
@@ -853,7 +886,7 @@ new ConcurrentInMemoryBTree<TKey, TValue>(config: ConcurrentInMemoryBTreeConfig<
|
|
|
853
886
|
| `ConcurrentInMemoryBTreeConfig<TKey, TValue>` | `InMemoryBTreeConfig<TKey>` を拡張し、`store: SharedTreeStore<TKey, TValue>`、`maxRetries?: number`、`maxSyncMutationsPerBatch?: number`、`readMode?: ReadMode` を追加。 |
|
|
854
887
|
| `SharedTreeStore<TKey, TValue>` | `getLogEntriesSince(version)` と `append(expectedVersion, mutations)` を持つインターフェース。 |
|
|
855
888
|
| `SharedTreeLog<TKey, TValue>` | `{ version: bigint; mutations: BTreeMutation<TKey, TValue>[] }` |
|
|
856
|
-
| `BTreeMutation<TKey, TValue>` | 判別共用体: `init`、`put`、`remove`、`removeById`、`updateById`、`popFirst`、`popLast`。
|
|
889
|
+
| `BTreeMutation<TKey, TValue>` | 判別共用体: `init`、`put`、`putMany`、`remove`、`removeById`、`updateById`、`popFirst`、`popLast`、`deleteRange`、`clear`。 |
|
|
857
890
|
| `BTreeValidationError` | コンパレータや設定の違反でスローされるエラー。 |
|
|
858
891
|
| `BTreeInvariantError` | ツリー構造の整合性違反でスローされるエラー。 |
|
|
859
892
|
| `BTreeConcurrencyError` | 並行処理コンフリクトやストア契約違反でスローされるエラー。 |
|
package/README.md
CHANGED
|
@@ -280,7 +280,7 @@ const last = tree.popLast();
|
|
|
280
280
|
// { entryId: ..., key: 20, value: 'twenty' } or null if empty
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
-
**`clear()`** -- remove all entries and reset the tree to its empty state in O(1). The internal sequence counter is
|
|
283
|
+
**`clear()`** -- remove all entries and reset the tree to its empty state in O(1). The internal sequence counter is **not** reset, so `EntryId` values continue to increase monotonically and are never reused within the lifetime of the instance.
|
|
284
284
|
|
|
285
285
|
```ts
|
|
286
286
|
tree.clear();
|
|
@@ -429,7 +429,7 @@ tree.assertInvariants(); // throws if invalid
|
|
|
429
429
|
|
|
430
430
|
#### Clone and Serialization
|
|
431
431
|
|
|
432
|
-
**`clone()`** -- create a structurally independent
|
|
432
|
+
**`clone()`** -- create a structurally independent copy. The tree structure (nodes, links, entry IDs) is fully independent, but stored key and value references are shared with the source tree:
|
|
433
433
|
|
|
434
434
|
```ts
|
|
435
435
|
const copy = tree.clone();
|
|
@@ -437,6 +437,8 @@ copy.put(99, 'new');
|
|
|
437
437
|
tree.hasKey(99); // false — original is unaffected
|
|
438
438
|
```
|
|
439
439
|
|
|
440
|
+
Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
441
|
+
|
|
440
442
|
**`toJSON()` / `fromJSON()`** -- serialize and reconstruct:
|
|
441
443
|
|
|
442
444
|
```ts
|
|
@@ -501,7 +503,9 @@ const tree = new InMemoryBTree<number, string>({
|
|
|
501
503
|
- **`getLogEntriesSince(version)`** -- returns all mutations since a given version, so each instance can catch up.
|
|
502
504
|
- **`append(expectedVersion, mutations)`** -- atomically appends mutations if the version matches (compare-and-swap). Returns `{ applied, version }`.
|
|
503
505
|
|
|
504
|
-
|
|
506
|
+
A store that returns historical mutations from `getLogEntriesSince` enables **multi-instance catch-up**: any instance can replay missed mutations to converge on the same state. Stores that do not replay mutations (returning an empty `mutations` array with an updated `version`) are also supported — in this mode, each instance only sees its own local writes and version advancement. Choose the replay strategy that matches your consistency requirements.
|
|
507
|
+
|
|
508
|
+
The store can be backed by anything: an in-memory array, a database table, a Redis stream, etc. Below is a complete in-memory reference implementation with replay support.
|
|
505
509
|
|
|
506
510
|
**Node.js / TypeScript:**
|
|
507
511
|
|
|
@@ -754,6 +758,23 @@ try {
|
|
|
754
758
|
|
|
755
759
|
---
|
|
756
760
|
|
|
761
|
+
#### Shared Store Security Assumptions
|
|
762
|
+
|
|
763
|
+
`ConcurrentInMemoryBTree` assumes the shared store is **trusted**. It does not defend against a store that returns maliciously crafted or arbitrarily large mutation payloads.
|
|
764
|
+
|
|
765
|
+
**Trust boundary:**
|
|
766
|
+
- The store is under your control or the control of your application.
|
|
767
|
+
- All instances sharing a store must use identical configuration (enforced via config fingerprint on the first write, but only when an `init` mutation is present in the replayed batch).
|
|
768
|
+
- Mutations are structurally validated before replay, but semantic correctness (e.g., key type consistency) is the caller's responsibility.
|
|
769
|
+
|
|
770
|
+
**Hardening recommendations for shared or multi-tenant deployments:**
|
|
771
|
+
- Do not expose `append` or `getLogEntriesSince` to untrusted clients without an authorization layer.
|
|
772
|
+
- Apply size limits to stored mutation payloads at the store level before they reach `ConcurrentInMemoryBTree`.
|
|
773
|
+
- Use `maxSyncMutationsPerBatch` to cap the number of mutations applied per sync call (default: 100,000).
|
|
774
|
+
- If a `sync()` throws `BTreeConcurrencyError` due to a replay failure, the instance is permanently poisoned. Discard it and create a new one.
|
|
775
|
+
|
|
776
|
+
---
|
|
777
|
+
|
|
757
778
|
## API Reference
|
|
758
779
|
|
|
759
780
|
### InMemoryBTree
|
|
@@ -791,7 +812,7 @@ try {
|
|
|
791
812
|
| `size` | `() => number` | Return the total number of entries. |
|
|
792
813
|
| `getStats` | `() => BTreeStats` | Return structural statistics. |
|
|
793
814
|
| `assertInvariants` | `() => void` | Assert B+ tree structural integrity. Throws if invalid. |
|
|
794
|
-
| `clone` | `() => InMemoryBTree<TKey, TValue>` | Return a structurally independent
|
|
815
|
+
| `clone` | `() => InMemoryBTree<TKey, TValue>` | Return a structurally independent copy (shared key/value refs). |
|
|
795
816
|
| `toJSON` | `() => BTreeJSON<TKey, TValue>` | Serialize to a versioned JSON-safe payload. |
|
|
796
817
|
| `fromJSON` (static) | `(json, compareKeys) => InMemoryBTree<TKey, TValue>` | Reconstruct a tree from a `toJSON` payload. |
|
|
797
818
|
|
|
@@ -803,18 +824,21 @@ new InMemoryBTree<TKey, TValue>(config: InMemoryBTreeConfig<TKey>)
|
|
|
803
824
|
|
|
804
825
|
### ConcurrentInMemoryBTree
|
|
805
826
|
|
|
806
|
-
Exposes
|
|
827
|
+
Exposes `InMemoryBTree` methods as async equivalents returning `Promise`. Writes coordinate through the shared store; reads sync before returning when `readMode` is `'strong'` (the default). When `readMode` is `'local'`, reads execute against the local tree without syncing.
|
|
807
828
|
|
|
808
829
|
| Method | Signature | Description |
|
|
809
830
|
| -------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
810
831
|
| `sync` | `() => Promise<void>` | Fetch and apply the latest log entries from the shared store. |
|
|
811
832
|
| `put` | `(key: TKey, value: TValue) => Promise<EntryId>` | Insert with optimistic concurrency. |
|
|
833
|
+
| `putMany` | `(entries: readonly { key: TKey; value: TValue }[]) => Promise<EntryId[]>` | Batch insert with optimistic concurrency. |
|
|
812
834
|
| `remove` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | Remove the first matching entry by key. |
|
|
813
835
|
| `removeById` | `(entryId: EntryId) => Promise<BTreeEntry<TKey, TValue> \| null>` | Remove a specific entry by ID. |
|
|
814
836
|
| `peekById` | `(entryId: EntryId) => Promise<BTreeEntry<TKey, TValue> \| null>` | Look up an entry by ID (syncs first). |
|
|
815
837
|
| `updateById` | `(entryId: EntryId, value: TValue) => Promise<BTreeEntry<TKey, TValue> \| null>` | Update an entry by ID with optimistic concurrency. |
|
|
816
838
|
| `popFirst` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | Remove and return the smallest entry. |
|
|
817
839
|
| `popLast` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | Remove and return the largest entry. |
|
|
840
|
+
| `deleteRange` | `(startKey: TKey, endKey: TKey, options?: RangeBounds) => Promise<number>` | Delete entries in range with optimistic concurrency. |
|
|
841
|
+
| `clear` | `() => Promise<void>` | Remove all entries with optimistic concurrency. |
|
|
818
842
|
| `peekFirst` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | Return the smallest entry (syncs first). |
|
|
819
843
|
| `peekLast` | `() => Promise<BTreeEntry<TKey, TValue> \| null>` | Return the largest entry (syncs first). |
|
|
820
844
|
| `findFirst` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | Return the first entry matching key (syncs first). |
|
|
@@ -826,10 +850,19 @@ Exposes a subset of `InMemoryBTree` methods as async equivalents returning `Prom
|
|
|
826
850
|
| `nextHigherKey` | `(key: TKey) => Promise<TKey \| null>` | Next key strictly greater (syncs first). |
|
|
827
851
|
| `nextLowerKey` | `(key: TKey) => Promise<TKey \| null>` | Next key strictly less (syncs first). |
|
|
828
852
|
| `getPairOrNextLower` | `(key: TKey) => Promise<BTreeEntry<TKey, TValue> \| null>` | Exact match or next lower (syncs first). |
|
|
853
|
+
| `entries` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | Return all entries as array (syncs first). |
|
|
854
|
+
| `entriesReversed` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | Return all entries in reverse as array (syncs first). |
|
|
855
|
+
| `keys` | `() => Promise<TKey[]>` | Return all keys as array (syncs first). |
|
|
856
|
+
| `values` | `() => Promise<TValue[]>` | Return all values as array (syncs first). |
|
|
857
|
+
| `forEach` | `(callback: (entry: BTreeEntry<TKey, TValue>) => void) => Promise<void>` | Iterate all entries (syncs first). |
|
|
829
858
|
| `snapshot` | `() => Promise<BTreeEntry<TKey, TValue>[]>` | Return all entries (syncs first). |
|
|
830
859
|
| `size` | `() => Promise<number>` | Return entry count (syncs first). |
|
|
831
860
|
| `getStats` | `() => Promise<BTreeStats>` | Return structural statistics (syncs first). |
|
|
832
861
|
| `assertInvariants` | `() => Promise<void>` | Assert structural integrity (syncs first). |
|
|
862
|
+
| `clone` | `() => Promise<InMemoryBTree<TKey, TValue>>` | Return an independent local copy (syncs first). |
|
|
863
|
+
| `toJSON` | `() => Promise<BTreeJSON<TKey, TValue>>` | Serialize to JSON (syncs first). |
|
|
864
|
+
| `fromJSON` (static) | `(json: BTreeJSON<TKey, TValue>, compareKeys: KeyComparator<TKey>) => InMemoryBTree<TKey, TValue>` | Deserialize from JSON (returns local tree). |
|
|
865
|
+
| `[Symbol.asyncIterator]` | `() => AsyncIterableIterator<BTreeEntry<TKey, TValue>>` | Async iteration over all entries (syncs first). |
|
|
833
866
|
|
|
834
867
|
**Constructor:**
|
|
835
868
|
|
|
@@ -853,7 +886,7 @@ new ConcurrentInMemoryBTree<TKey, TValue>(config: ConcurrentInMemoryBTreeConfig<
|
|
|
853
886
|
| `ConcurrentInMemoryBTreeConfig<TKey, TValue>` | Extends `InMemoryBTreeConfig<TKey>` with `store: SharedTreeStore<TKey, TValue>`, `maxRetries?: number`, `maxSyncMutationsPerBatch?: number`, and `readMode?: ReadMode`. |
|
|
854
887
|
| `SharedTreeStore<TKey, TValue>` | Interface with `getLogEntriesSince(version)` and `append(expectedVersion, mutations)`. |
|
|
855
888
|
| `SharedTreeLog<TKey, TValue>` | `{ version: bigint; mutations: BTreeMutation<TKey, TValue>[] }` |
|
|
856
|
-
| `BTreeMutation<TKey, TValue>` | Discriminated union: `init`, `put`, `remove`, `removeById`, `updateById`, `popFirst`, `popLast`.
|
|
889
|
+
| `BTreeMutation<TKey, TValue>` | Discriminated union: `init`, `put`, `putMany`, `remove`, `removeById`, `updateById`, `popFirst`, `popLast`, `deleteRange`, `clear`. |
|
|
857
890
|
| `BTreeValidationError` | Error thrown for comparator or config violations. |
|
|
858
891
|
| `BTreeInvariantError` | Error thrown for tree structural integrity violations. |
|
|
859
892
|
| `BTreeConcurrencyError` | Error thrown for concurrency conflicts or store contract violations. |
|
package/dist/InMemoryBTree.d.ts
CHANGED
|
@@ -22,11 +22,37 @@ export declare class InMemoryBTree<TKey, TValue> {
|
|
|
22
22
|
get(key: TKey): TValue | null;
|
|
23
23
|
hasKey(key: TKey): boolean;
|
|
24
24
|
findFirst(key: TKey): BTreeEntry<TKey, TValue> | null;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the last entry whose key matches `key`, or `null` if not found.
|
|
27
|
+
* Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
|
|
28
|
+
*/
|
|
25
29
|
findLast(key: TKey): BTreeEntry<TKey, TValue> | null;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
32
|
+
* or `null` if no such key exists.
|
|
33
|
+
*/
|
|
26
34
|
nextHigherKey(key: TKey): TKey | null;
|
|
35
|
+
/**
|
|
36
|
+
* Returns the largest key in the tree that is strictly less than `key`,
|
|
37
|
+
* or `null` if no such key exists.
|
|
38
|
+
*/
|
|
27
39
|
nextLowerKey(key: TKey): TKey | null;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the entry for `key` if it exists; otherwise returns the entry with
|
|
42
|
+
* the largest key strictly less than `key`. Returns `null` when the tree is
|
|
43
|
+
* empty or every key is greater than `key`.
|
|
44
|
+
*/
|
|
28
45
|
getPairOrNextLower(key: TKey): BTreeEntry<TKey, TValue> | null;
|
|
46
|
+
/**
|
|
47
|
+
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
48
|
+
* Pass `options` to make either bound exclusive.
|
|
49
|
+
*/
|
|
29
50
|
count(startKey: TKey, endKey: TKey, options?: RangeBounds): number;
|
|
51
|
+
/**
|
|
52
|
+
* Deletes all entries whose keys fall within [`startKey`, `endKey`].
|
|
53
|
+
* Pass `options` to make either bound exclusive.
|
|
54
|
+
* @returns The number of entries deleted.
|
|
55
|
+
*/
|
|
30
56
|
deleteRange(startKey: TKey, endKey: TKey, options?: RangeBounds): number;
|
|
31
57
|
range(startKey: TKey, endKey: TKey, options?: RangeBounds): BTreeEntry<TKey, TValue>[];
|
|
32
58
|
entries(): IterableIterator<BTreeEntry<TKey, TValue>>;
|
|
@@ -36,6 +62,13 @@ export declare class InMemoryBTree<TKey, TValue> {
|
|
|
36
62
|
[Symbol.iterator](): IterableIterator<BTreeEntry<TKey, TValue>>;
|
|
37
63
|
forEach(callback: (entry: BTreeEntry<TKey, TValue>) => void, thisArg?: unknown): void;
|
|
38
64
|
snapshot(): BTreeEntry<TKey, TValue>[];
|
|
65
|
+
/**
|
|
66
|
+
* Returns a structurally independent `InMemoryBTree` with identical
|
|
67
|
+
* configuration and entries. The tree structure (nodes, links, entry IDs)
|
|
68
|
+
* is fully independent, but stored key and value references are shared
|
|
69
|
+
* with the source tree.
|
|
70
|
+
* Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
71
|
+
*/
|
|
39
72
|
clone(): InMemoryBTree<TKey, TValue>;
|
|
40
73
|
toJSON(): BTreeJSON<TKey, TValue>;
|
|
41
74
|
static fromJSON<TKey, TValue>(json: BTreeJSON<TKey, TValue>, compareKeys: KeyComparator<TKey>): InMemoryBTree<TKey, TValue>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BranchNode, BTreeNode, LeafEntry, LeafNode, NodeKey } from './types.js';
|
|
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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type BTreeEntry, type BTreeState, type RangeBounds } from './types.js';
|
|
2
|
+
export declare function isEmptyRange<TKey>(compare: (a: TKey, b: TKey) => number, startKey: TKey, endKey: TKey, options?: RangeBounds): boolean;
|
|
2
3
|
export declare const countRangeEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>, startKey: TKey, endKey: TKey, options?: RangeBounds) => number;
|
|
3
4
|
export declare const rangeQueryEntries: <TKey, TValue>(state: BTreeState<TKey, TValue>, startKey: TKey, endKey: TKey, options?: RangeBounds) => BTreeEntry<TKey, TValue>[];
|
|
@@ -10,10 +10,6 @@ export interface BTreeJSON<TKey, TValue> {
|
|
|
10
10
|
};
|
|
11
11
|
entries: [TKey, TValue][];
|
|
12
12
|
}
|
|
13
|
-
export declare const collectEntryPairs: <TKey, TValue>(state: BTreeState<TKey, TValue>) => {
|
|
14
|
-
key: TKey;
|
|
15
|
-
value: TValue;
|
|
16
|
-
}[];
|
|
17
13
|
export declare const buildConfigFromState: <TKey, TValue>(state: BTreeState<TKey, TValue>) => InMemoryBTreeConfig<TKey>;
|
|
18
14
|
export declare const serializeToJSON: <TKey, TValue>(state: BTreeState<TKey, TValue>) => BTreeJSON<TKey, TValue>;
|
|
19
15
|
export declare const validateBTreeJSON: <TKey, TValue>(json: BTreeJSON<TKey, TValue>) => void;
|
package/dist/btree/types.d.ts
CHANGED
|
@@ -6,8 +6,14 @@ export declare const NODE_LEAF: 0;
|
|
|
6
6
|
export declare const NODE_BRANCH: 1;
|
|
7
7
|
export type KeyComparator<TKey> = (left: TKey, right: TKey) => number;
|
|
8
8
|
export type DuplicateKeyPolicy = 'allow' | 'reject' | 'replace';
|
|
9
|
+
/**
|
|
10
|
+
* Defines the inclusivity of the lower and upper bounds for a key range scan.
|
|
11
|
+
* Both bounds default to `'inclusive'` when omitted.
|
|
12
|
+
*/
|
|
9
13
|
export interface RangeBounds {
|
|
14
|
+
/** Lower bound type. Defaults to `'inclusive'` when omitted. */
|
|
10
15
|
lowerBound?: 'inclusive' | 'exclusive';
|
|
16
|
+
/** Upper bound type. Defaults to `'inclusive'` when omitted. */
|
|
11
17
|
upperBound?: 'inclusive' | 'exclusive';
|
|
12
18
|
}
|
|
13
19
|
export declare const normalizeDuplicateKeyPolicy: (value: DuplicateKeyPolicy | undefined) => DuplicateKeyPolicy;
|
|
@@ -29,6 +35,11 @@ export interface LeafEntry<TKey, TValue> {
|
|
|
29
35
|
key: TKey;
|
|
30
36
|
value: TValue;
|
|
31
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Shallow-copies a `LeafEntry` into a plain `BTreeEntry` for safe public API return.
|
|
40
|
+
* Prevents callers from holding a reference that mutates when `updateById` is called.
|
|
41
|
+
*/
|
|
42
|
+
export declare const toPublicEntry: <TKey, TValue>(entry: LeafEntry<TKey, TValue>) => BTreeEntry<TKey, TValue>;
|
|
32
43
|
export interface LeafNode<TKey, TValue> {
|
|
33
44
|
kind: typeof NODE_LEAF;
|
|
34
45
|
entries: LeafEntry<TKey, TValue>[];
|
|
@@ -85,29 +96,4 @@ export interface BTreeStats {
|
|
|
85
96
|
export declare const isLeafNode: <TKey, TValue>(node: BTreeNode<TKey, TValue>) => node is LeafNode<TKey, TValue>;
|
|
86
97
|
export declare const writeMinKeyTo: <TKey, TValue>(node: BTreeNode<TKey, TValue>, target: NodeKey<TKey>) => boolean;
|
|
87
98
|
export declare const normalizeNodeCapacity: (value: number | undefined, field: string, defaultValue: number) => number;
|
|
88
|
-
export
|
|
89
|
-
export declare const createBranchNode: <TKey, TValue>(children: BTreeNode<TKey, TValue>[], parent: BranchNode<TKey, TValue> | null) => BranchNode<TKey, TValue>;
|
|
90
|
-
/** Number of logical entries in the leaf */
|
|
91
|
-
export declare const leafEntryCount: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => number;
|
|
92
|
-
/** Get logical entry at index i (0-based from the logical start) */
|
|
93
|
-
export declare const leafEntryAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, i: number) => LeafEntry<TKey, TValue>;
|
|
94
|
-
/** Remove and return the first logical entry. O(1) amortized — increments offset and compacts when dead slots reach half. */
|
|
95
|
-
export declare const leafShiftEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => LeafEntry<TKey, TValue> | undefined;
|
|
96
|
-
/** Remove and return the last logical entry. O(1) — pops from the backing array tail. */
|
|
97
|
-
export declare const leafPopEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => LeafEntry<TKey, TValue> | undefined;
|
|
98
|
-
/** Prepend entry to logical start. Falls back to unshift if no gap, otherwise fills gap. */
|
|
99
|
-
export declare const leafUnshiftEntry: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, entry: LeafEntry<TKey, TValue>) => void;
|
|
100
|
-
/** Remove logical entry at index. Shifts the smaller side to halve average cost. */
|
|
101
|
-
export declare const leafRemoveAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, logicalIndex: number) => void;
|
|
102
|
-
/** Insert entry at logical index. Uses entryOffset gap when inserting in the first half. */
|
|
103
|
-
export declare const leafInsertAt: <TKey, TValue>(leaf: LeafNode<TKey, TValue>, logicalIndex: number, entry: LeafEntry<TKey, TValue>) => void;
|
|
104
|
-
/** Compact the backing array — remove dead slots before entryOffset. Call before splits/merges. */
|
|
105
|
-
export declare const leafCompact: <TKey, TValue>(leaf: LeafNode<TKey, TValue>) => void;
|
|
106
|
-
/** Compact branch arrays — remove dead slots before childOffset, keeping a small gap. */
|
|
107
|
-
export declare const branchCompact: <TKey, TValue>(branch: BranchNode<TKey, TValue>) => void;
|
|
108
|
-
/** Number of logical children in the branch. */
|
|
109
|
-
export declare const branchChildCount: <TKey, TValue>(branch: BranchNode<TKey, TValue>) => number;
|
|
110
|
-
/** Insert a child at a logical index. Shifts the smaller side to halve average cost. */
|
|
111
|
-
export declare const branchInsertAt: <TKey, TValue>(branch: BranchNode<TKey, TValue>, logicalIndex: number, child: BTreeNode<TKey, TValue>, key: NodeKey<TKey>) => void;
|
|
112
|
-
/** Remove a child at a physical index. Shifts the smaller side. */
|
|
113
|
-
export declare const branchRemoveAt: <TKey, TValue>(branch: BranchNode<TKey, TValue>, physIndex: number) => void;
|
|
99
|
+
export { createLeafNode, createBranchNode, leafEntryCount, leafEntryAt, leafShiftEntry, leafPopEntry, leafUnshiftEntry, leafRemoveAt, leafInsertAt, leafCompact, branchCompact, branchChildCount, branchInsertAt, branchRemoveAt, } from './node-ops.js';
|