@optimystic/db-core 0.0.1
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.md +328 -0
- package/dist/index.min.js +18 -0
- package/dist/index.min.js.map +7 -0
- package/dist/src/blocks/block-store.d.ts +12 -0
- package/dist/src/blocks/block-store.d.ts.map +1 -0
- package/dist/src/blocks/block-store.js +2 -0
- package/dist/src/blocks/block-store.js.map +1 -0
- package/dist/src/blocks/block-types.d.ts +3 -0
- package/dist/src/blocks/block-types.d.ts.map +1 -0
- package/dist/src/blocks/block-types.js +9 -0
- package/dist/src/blocks/block-types.js.map +1 -0
- package/dist/src/blocks/helpers.d.ts +4 -0
- package/dist/src/blocks/helpers.d.ts.map +1 -0
- package/dist/src/blocks/helpers.js +12 -0
- package/dist/src/blocks/helpers.js.map +1 -0
- package/dist/src/blocks/index.d.ts +5 -0
- package/dist/src/blocks/index.d.ts.map +1 -0
- package/dist/src/blocks/index.js +5 -0
- package/dist/src/blocks/index.js.map +1 -0
- package/dist/src/blocks/structs.d.ts +14 -0
- package/dist/src/blocks/structs.d.ts.map +1 -0
- package/dist/src/blocks/structs.js +2 -0
- package/dist/src/blocks/structs.js.map +1 -0
- package/dist/src/btree/btree.d.ts +135 -0
- package/dist/src/btree/btree.d.ts.map +1 -0
- package/dist/src/btree/btree.js +727 -0
- package/dist/src/btree/btree.js.map +1 -0
- package/dist/src/btree/independent-trunk.d.ts +17 -0
- package/dist/src/btree/independent-trunk.d.ts.map +1 -0
- package/dist/src/btree/independent-trunk.js +41 -0
- package/dist/src/btree/independent-trunk.js.map +1 -0
- package/dist/src/btree/index.d.ts +6 -0
- package/dist/src/btree/index.d.ts.map +1 -0
- package/dist/src/btree/index.js +6 -0
- package/dist/src/btree/index.js.map +1 -0
- package/dist/src/btree/key-range.d.ts +13 -0
- package/dist/src/btree/key-range.d.ts.map +1 -0
- package/dist/src/btree/key-range.js +20 -0
- package/dist/src/btree/key-range.js.map +1 -0
- package/dist/src/btree/keyset.d.ts +4 -0
- package/dist/src/btree/keyset.d.ts.map +1 -0
- package/dist/src/btree/keyset.js +4 -0
- package/dist/src/btree/keyset.js.map +1 -0
- package/dist/src/btree/nodes.d.ts +16 -0
- package/dist/src/btree/nodes.d.ts.map +1 -0
- package/dist/src/btree/nodes.js +9 -0
- package/dist/src/btree/nodes.js.map +1 -0
- package/dist/src/btree/path.d.ts +22 -0
- package/dist/src/btree/path.d.ts.map +1 -0
- package/dist/src/btree/path.js +39 -0
- package/dist/src/btree/path.js.map +1 -0
- package/dist/src/btree/tree-block.d.ts +7 -0
- package/dist/src/btree/tree-block.d.ts.map +1 -0
- package/dist/src/btree/tree-block.js +5 -0
- package/dist/src/btree/tree-block.js.map +1 -0
- package/dist/src/btree/trunk.d.ts +13 -0
- package/dist/src/btree/trunk.d.ts.map +1 -0
- package/dist/src/btree/trunk.js +2 -0
- package/dist/src/btree/trunk.js.map +1 -0
- package/dist/src/chain/chain-nodes.d.ts +18 -0
- package/dist/src/chain/chain-nodes.d.ts.map +1 -0
- package/dist/src/chain/chain-nodes.js +10 -0
- package/dist/src/chain/chain-nodes.js.map +1 -0
- package/dist/src/chain/chain.d.ts +75 -0
- package/dist/src/chain/chain.d.ts.map +1 -0
- package/dist/src/chain/chain.js +268 -0
- package/dist/src/chain/chain.js.map +1 -0
- package/dist/src/chain/index.d.ts +2 -0
- package/dist/src/chain/index.d.ts.map +1 -0
- package/dist/src/chain/index.js +2 -0
- package/dist/src/chain/index.js.map +1 -0
- package/dist/src/cluster/i-cluster.d.ts +5 -0
- package/dist/src/cluster/i-cluster.d.ts.map +1 -0
- package/dist/src/cluster/i-cluster.js +2 -0
- package/dist/src/cluster/i-cluster.js.map +1 -0
- package/dist/src/cluster/index.d.ts +3 -0
- package/dist/src/cluster/index.d.ts.map +1 -0
- package/dist/src/cluster/index.js +3 -0
- package/dist/src/cluster/index.js.map +1 -0
- package/dist/src/cluster/structs.d.ts +47 -0
- package/dist/src/cluster/structs.d.ts.map +1 -0
- package/dist/src/cluster/structs.js +2 -0
- package/dist/src/cluster/structs.js.map +1 -0
- package/dist/src/collection/action.d.ts +26 -0
- package/dist/src/collection/action.d.ts.map +1 -0
- package/dist/src/collection/action.js +2 -0
- package/dist/src/collection/action.js.map +1 -0
- package/dist/src/collection/collection.d.ts +48 -0
- package/dist/src/collection/collection.d.ts.map +1 -0
- package/dist/src/collection/collection.js +175 -0
- package/dist/src/collection/collection.js.map +1 -0
- package/dist/src/collection/index.d.ts +4 -0
- package/dist/src/collection/index.d.ts.map +1 -0
- package/dist/src/collection/index.js +4 -0
- package/dist/src/collection/index.js.map +1 -0
- package/dist/src/collection/struct.d.ts +16 -0
- package/dist/src/collection/struct.d.ts.map +1 -0
- package/dist/src/collection/struct.js +2 -0
- package/dist/src/collection/struct.js.map +1 -0
- package/dist/src/collections/diary/diary.d.ts +9 -0
- package/dist/src/collections/diary/diary.d.ts.map +1 -0
- package/dist/src/collections/diary/diary.js +37 -0
- package/dist/src/collections/diary/diary.js.map +1 -0
- package/dist/src/collections/diary/index.d.ts +3 -0
- package/dist/src/collections/diary/index.d.ts.map +1 -0
- package/dist/src/collections/diary/index.js +3 -0
- package/dist/src/collections/diary/index.js.map +1 -0
- package/dist/src/collections/diary/struct.d.ts +2 -0
- package/dist/src/collections/diary/struct.d.ts.map +1 -0
- package/dist/src/collections/diary/struct.js +3 -0
- package/dist/src/collections/diary/struct.js.map +1 -0
- package/dist/src/collections/index.d.ts +3 -0
- package/dist/src/collections/index.d.ts.map +1 -0
- package/dist/src/collections/index.js +3 -0
- package/dist/src/collections/index.js.map +1 -0
- package/dist/src/collections/tree/collection-trunk.d.ts +11 -0
- package/dist/src/collections/tree/collection-trunk.d.ts.map +1 -0
- package/dist/src/collections/tree/collection-trunk.js +22 -0
- package/dist/src/collections/tree/collection-trunk.js.map +1 -0
- package/dist/src/collections/tree/index.d.ts +3 -0
- package/dist/src/collections/tree/index.d.ts.map +1 -0
- package/dist/src/collections/tree/index.js +3 -0
- package/dist/src/collections/tree/index.js.map +1 -0
- package/dist/src/collections/tree/struct.d.ts +12 -0
- package/dist/src/collections/tree/struct.d.ts.map +1 -0
- package/dist/src/collections/tree/struct.js +4 -0
- package/dist/src/collections/tree/struct.js.map +1 -0
- package/dist/src/collections/tree/tree.d.ts +34 -0
- package/dist/src/collections/tree/tree.d.ts.map +1 -0
- package/dist/src/collections/tree/tree.js +100 -0
- package/dist/src/collections/tree/tree.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/log/index.d.ts +3 -0
- package/dist/src/log/index.d.ts.map +1 -0
- package/dist/src/log/index.js +3 -0
- package/dist/src/log/index.js.map +1 -0
- package/dist/src/log/log.d.ts +57 -0
- package/dist/src/log/log.d.ts.map +1 -0
- package/dist/src/log/log.js +131 -0
- package/dist/src/log/log.js.map +1 -0
- package/dist/src/log/struct.d.ts +36 -0
- package/dist/src/log/struct.d.ts.map +1 -0
- package/dist/src/log/struct.js +3 -0
- package/dist/src/log/struct.js.map +1 -0
- package/dist/src/network/i-key-network.d.ts +21 -0
- package/dist/src/network/i-key-network.d.ts.map +1 -0
- package/dist/src/network/i-key-network.js +2 -0
- package/dist/src/network/i-key-network.js.map +1 -0
- package/dist/src/network/i-peer-network.d.ts +8 -0
- package/dist/src/network/i-peer-network.d.ts.map +1 -0
- package/dist/src/network/i-peer-network.js +2 -0
- package/dist/src/network/i-peer-network.js.map +1 -0
- package/dist/src/network/i-repo.d.ts +17 -0
- package/dist/src/network/i-repo.d.ts.map +1 -0
- package/dist/src/network/i-repo.js +2 -0
- package/dist/src/network/i-repo.js.map +1 -0
- package/dist/src/network/index.d.ts +6 -0
- package/dist/src/network/index.d.ts.map +1 -0
- package/dist/src/network/index.js +6 -0
- package/dist/src/network/index.js.map +1 -0
- package/dist/src/network/repo-protocol.d.ts +19 -0
- package/dist/src/network/repo-protocol.d.ts.map +1 -0
- package/dist/src/network/repo-protocol.js +2 -0
- package/dist/src/network/repo-protocol.js.map +1 -0
- package/dist/src/network/struct.d.ts +115 -0
- package/dist/src/network/struct.d.ts.map +1 -0
- package/dist/src/network/struct.js +2 -0
- package/dist/src/network/struct.js.map +1 -0
- package/dist/src/transaction/actions-engine.d.ts +37 -0
- package/dist/src/transaction/actions-engine.d.ts.map +1 -0
- package/dist/src/transaction/actions-engine.js +67 -0
- package/dist/src/transaction/actions-engine.js.map +1 -0
- package/dist/src/transaction/context.d.ts +60 -0
- package/dist/src/transaction/context.d.ts.map +1 -0
- package/dist/src/transaction/context.js +91 -0
- package/dist/src/transaction/context.js.map +1 -0
- package/dist/src/transaction/coordinator.d.ts +118 -0
- package/dist/src/transaction/coordinator.d.ts.map +1 -0
- package/dist/src/transaction/coordinator.js +417 -0
- package/dist/src/transaction/coordinator.js.map +1 -0
- package/dist/src/transaction/index.d.ts +10 -0
- package/dist/src/transaction/index.d.ts.map +1 -0
- package/dist/src/transaction/index.js +7 -0
- package/dist/src/transaction/index.js.map +1 -0
- package/dist/src/transaction/session.d.ts +80 -0
- package/dist/src/transaction/session.d.ts.map +1 -0
- package/dist/src/transaction/session.js +161 -0
- package/dist/src/transaction/session.js.map +1 -0
- package/dist/src/transaction/transaction.d.ts +156 -0
- package/dist/src/transaction/transaction.d.ts.map +1 -0
- package/dist/src/transaction/transaction.js +31 -0
- package/dist/src/transaction/transaction.js.map +1 -0
- package/dist/src/transaction/validator.d.ts +46 -0
- package/dist/src/transaction/validator.d.ts.map +1 -0
- package/dist/src/transaction/validator.js +97 -0
- package/dist/src/transaction/validator.js.map +1 -0
- package/dist/src/transactor/index.d.ts +4 -0
- package/dist/src/transactor/index.d.ts.map +1 -0
- package/dist/src/transactor/index.js +4 -0
- package/dist/src/transactor/index.js.map +1 -0
- package/dist/src/transactor/network-transactor.d.ts +36 -0
- package/dist/src/transactor/network-transactor.d.ts.map +1 -0
- package/dist/src/transactor/network-transactor.js +297 -0
- package/dist/src/transactor/network-transactor.js.map +1 -0
- package/dist/src/transactor/transactor-source.d.ts +24 -0
- package/dist/src/transactor/transactor-source.d.ts.map +1 -0
- package/dist/src/transactor/transactor-source.js +62 -0
- package/dist/src/transactor/transactor-source.js.map +1 -0
- package/dist/src/transactor/transactor.d.ts +38 -0
- package/dist/src/transactor/transactor.d.ts.map +1 -0
- package/dist/src/transactor/transactor.js +2 -0
- package/dist/src/transactor/transactor.js.map +1 -0
- package/dist/src/transform/atomic.d.ts +8 -0
- package/dist/src/transform/atomic.d.ts.map +1 -0
- package/dist/src/transform/atomic.js +14 -0
- package/dist/src/transform/atomic.js.map +1 -0
- package/dist/src/transform/cache-source.d.ts +13 -0
- package/dist/src/transform/cache-source.d.ts.map +1 -0
- package/dist/src/transform/cache-source.js +52 -0
- package/dist/src/transform/cache-source.js.map +1 -0
- package/dist/src/transform/helpers.d.ts +25 -0
- package/dist/src/transform/helpers.d.ts.map +1 -0
- package/dist/src/transform/helpers.js +105 -0
- package/dist/src/transform/helpers.js.map +1 -0
- package/dist/src/transform/index.d.ts +6 -0
- package/dist/src/transform/index.d.ts.map +1 -0
- package/dist/src/transform/index.js +6 -0
- package/dist/src/transform/index.js.map +1 -0
- package/dist/src/transform/struct.d.ts +19 -0
- package/dist/src/transform/struct.d.ts.map +1 -0
- package/dist/src/transform/struct.js +2 -0
- package/dist/src/transform/struct.js.map +1 -0
- package/dist/src/transform/tracker.d.ts +22 -0
- package/dist/src/transform/tracker.d.ts.map +1 -0
- package/dist/src/transform/tracker.js +64 -0
- package/dist/src/transform/tracker.js.map +1 -0
- package/dist/src/utility/actor.d.ts +11 -0
- package/dist/src/utility/actor.d.ts.map +1 -0
- package/dist/src/utility/actor.js +39 -0
- package/dist/src/utility/actor.js.map +1 -0
- package/dist/src/utility/batch-coordinator.d.ts +56 -0
- package/dist/src/utility/batch-coordinator.d.ts.map +1 -0
- package/dist/src/utility/batch-coordinator.js +127 -0
- package/dist/src/utility/batch-coordinator.js.map +1 -0
- package/dist/src/utility/block-id-to-bytes.d.ts +3 -0
- package/dist/src/utility/block-id-to-bytes.d.ts.map +1 -0
- package/dist/src/utility/block-id-to-bytes.js +7 -0
- package/dist/src/utility/block-id-to-bytes.js.map +1 -0
- package/dist/src/utility/ensured.d.ts +3 -0
- package/dist/src/utility/ensured.d.ts.map +1 -0
- package/dist/src/utility/ensured.js +24 -0
- package/dist/src/utility/ensured.js.map +1 -0
- package/dist/src/utility/groupby.d.ts +8 -0
- package/dist/src/utility/groupby.d.ts.map +1 -0
- package/dist/src/utility/groupby.js +15 -0
- package/dist/src/utility/groupby.js.map +1 -0
- package/dist/src/utility/is-record-empty.d.ts +3 -0
- package/dist/src/utility/is-record-empty.d.ts.map +1 -0
- package/dist/src/utility/is-record-empty.js +7 -0
- package/dist/src/utility/is-record-empty.js.map +1 -0
- package/dist/src/utility/latches.d.ts +11 -0
- package/dist/src/utility/latches.d.ts.map +1 -0
- package/dist/src/utility/latches.js +36 -0
- package/dist/src/utility/latches.js.map +1 -0
- package/dist/src/utility/nameof.d.ts +3 -0
- package/dist/src/utility/nameof.d.ts.map +1 -0
- package/dist/src/utility/nameof.js +5 -0
- package/dist/src/utility/nameof.js.map +1 -0
- package/dist/src/utility/pending.d.ts +13 -0
- package/dist/src/utility/pending.d.ts.map +1 -0
- package/dist/src/utility/pending.js +37 -0
- package/dist/src/utility/pending.js.map +1 -0
- package/package.json +56 -0
- package/src/blocks/block-store.ts +13 -0
- package/src/blocks/block-types.ts +11 -0
- package/src/blocks/helpers.ts +13 -0
- package/src/blocks/index.ts +5 -0
- package/src/blocks/structs.ts +17 -0
- package/src/btree/btree.ts +804 -0
- package/src/btree/independent-trunk.ts +54 -0
- package/src/btree/index.ts +5 -0
- package/src/btree/key-range.ts +15 -0
- package/src/btree/keyset.ts +6 -0
- package/src/btree/nodes.ts +25 -0
- package/src/btree/path.ts +37 -0
- package/src/btree/tree-block.ts +11 -0
- package/src/btree/trunk.ts +14 -0
- package/src/chain/chain-nodes.ts +24 -0
- package/src/chain/chain.ts +324 -0
- package/src/chain/index.ts +2 -0
- package/src/cluster/i-cluster.ts +6 -0
- package/src/cluster/index.ts +2 -0
- package/src/cluster/structs.ts +46 -0
- package/src/collection/action.ts +31 -0
- package/src/collection/collection.ts +200 -0
- package/src/collection/index.ts +3 -0
- package/src/collection/struct.ts +20 -0
- package/src/collections/diary/diary.ts +43 -0
- package/src/collections/diary/index.ts +2 -0
- package/src/collections/diary/struct.ts +3 -0
- package/src/collections/index.ts +2 -0
- package/src/collections/tree/collection-trunk.ts +25 -0
- package/src/collections/tree/index.ts +2 -0
- package/src/collections/tree/readme.md +19 -0
- package/src/collections/tree/struct.ts +18 -0
- package/src/collections/tree/tree.ts +124 -0
- package/src/index.ts +17 -0
- package/src/log/index.ts +2 -0
- package/src/log/log.ts +155 -0
- package/src/log/struct.ts +40 -0
- package/src/network/i-key-network.ts +24 -0
- package/src/network/i-peer-network.ts +8 -0
- package/src/network/i-repo.ts +19 -0
- package/src/network/index.ts +5 -0
- package/src/network/repo-protocol.ts +12 -0
- package/src/network/struct.ts +137 -0
- package/src/transaction/actions-engine.ts +83 -0
- package/src/transaction/context.ts +103 -0
- package/src/transaction/coordinator.ts +583 -0
- package/src/transaction/index.ts +30 -0
- package/src/transaction/session.ts +182 -0
- package/src/transaction/transaction.ts +205 -0
- package/src/transaction/validator.ts +150 -0
- package/src/transactor/index.ts +4 -0
- package/src/transactor/network-transactor.ts +435 -0
- package/src/transactor/transactor-source.ts +65 -0
- package/src/transactor/transactor.ts +44 -0
- package/src/transform/atomic.ts +16 -0
- package/src/transform/cache-source.ts +57 -0
- package/src/transform/helpers.ts +117 -0
- package/src/transform/index.ts +5 -0
- package/src/transform/struct.ts +22 -0
- package/src/transform/tracker.ts +70 -0
- package/src/utility/actor.ts +62 -0
- package/src/utility/batch-coordinator.ts +174 -0
- package/src/utility/block-id-to-bytes.ts +8 -0
- package/src/utility/ensured.ts +32 -0
- package/src/utility/groupby.ts +18 -0
- package/src/utility/is-record-empty.ts +5 -0
- package/src/utility/latches.ts +42 -0
- package/src/utility/nameof.ts +7 -0
- package/src/utility/pending.ts +41 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { TransactionCoordinator } from "./coordinator.js";
|
|
2
|
+
import type { Transaction, ExecutionResult, ITransactionEngine, TransactionStamp, CollectionActions } from "./transaction.js";
|
|
3
|
+
import { createTransactionStamp, createTransactionId } from "./transaction.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TransactionSession manages incremental transaction building.
|
|
7
|
+
*
|
|
8
|
+
* This is the high-level API for building transactions incrementally:
|
|
9
|
+
* - Stamp is created at BEGIN (stable throughout transaction)
|
|
10
|
+
* - Execute statements one at a time
|
|
11
|
+
* - Engine translates statements to actions (if not already provided)
|
|
12
|
+
* - Actions are immediately applied to collections via coordinator.applyActions()
|
|
13
|
+
* - On commit, all statements are compiled into a complete Transaction
|
|
14
|
+
* - The Transaction is then committed through coordinator.commit() for PEND/COMMIT orchestration
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const session = new TransactionSession(coordinator, engine);
|
|
18
|
+
* await session.execute('INSERT INTO users (id, name) VALUES (?, ?)', [1, 'Alice']);
|
|
19
|
+
* await session.execute('SELECT * FROM orders WHERE user_id = ?', [1]);
|
|
20
|
+
* const result = await session.commit();
|
|
21
|
+
*
|
|
22
|
+
* For validation/replay, use engine.execute() directly with a complete Transaction.
|
|
23
|
+
*/
|
|
24
|
+
export class TransactionSession {
|
|
25
|
+
private readonly statements: string[] = [];
|
|
26
|
+
private readonly stamp: TransactionStamp;
|
|
27
|
+
private committed = false;
|
|
28
|
+
private rolledBack = false;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly coordinator: TransactionCoordinator,
|
|
32
|
+
private readonly engine: ITransactionEngine,
|
|
33
|
+
peerId: string = 'local', // TODO: Get from coordinator or config
|
|
34
|
+
schemaHash: string = '' // TODO: Get from engine
|
|
35
|
+
) {
|
|
36
|
+
// Create stamp at BEGIN (stable throughout transaction)
|
|
37
|
+
this.stamp = createTransactionStamp(
|
|
38
|
+
peerId,
|
|
39
|
+
Date.now(),
|
|
40
|
+
schemaHash,
|
|
41
|
+
'unknown' // TODO: Get engine ID from engine
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Execute a statement.
|
|
47
|
+
*
|
|
48
|
+
* If actions are provided, they are applied directly.
|
|
49
|
+
* Otherwise, the engine translates the statement to actions.
|
|
50
|
+
*
|
|
51
|
+
* @param statement - The statement to execute (engine-specific, e.g., SQL statement)
|
|
52
|
+
* @param actions - Optional pre-computed actions (for Quereus module case)
|
|
53
|
+
* @returns Execution result with any returned values
|
|
54
|
+
*/
|
|
55
|
+
async execute(statement: string, actions?: CollectionActions[]): Promise<{ success: boolean; error?: string }> {
|
|
56
|
+
if (this.committed) {
|
|
57
|
+
return { success: false, error: 'Transaction already committed' };
|
|
58
|
+
}
|
|
59
|
+
if (this.rolledBack) {
|
|
60
|
+
return { success: false, error: 'Transaction already rolled back' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
// If actions not provided, enlist engine to translate statement
|
|
65
|
+
let actionsToApply: CollectionActions[];
|
|
66
|
+
if (actions) {
|
|
67
|
+
actionsToApply = actions;
|
|
68
|
+
} else {
|
|
69
|
+
// Create a temporary transaction with just this statement for translation
|
|
70
|
+
const tempTransaction: Transaction = {
|
|
71
|
+
stamp: this.stamp,
|
|
72
|
+
statements: [statement],
|
|
73
|
+
reads: [],
|
|
74
|
+
id: 'temp' // Temporary ID for translation only
|
|
75
|
+
};
|
|
76
|
+
const result = await this.engine.execute(tempTransaction);
|
|
77
|
+
if (!result.success || !result.actions) {
|
|
78
|
+
return { success: false, error: result.error || 'Failed to translate statement' };
|
|
79
|
+
}
|
|
80
|
+
actionsToApply = result.actions;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Apply actions through coordinator
|
|
84
|
+
await this.coordinator.applyActions(actionsToApply, this.stamp.id);
|
|
85
|
+
|
|
86
|
+
// Accumulate the statement for later compilation
|
|
87
|
+
this.statements.push(statement);
|
|
88
|
+
|
|
89
|
+
return { success: true };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: `Failed to execute statement: ${error instanceof Error ? error.message : String(error)}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Commit the transaction.
|
|
100
|
+
*
|
|
101
|
+
* Compiles all statements into a complete Transaction and commits through coordinator.
|
|
102
|
+
*/
|
|
103
|
+
async commit(): Promise<ExecutionResult> {
|
|
104
|
+
if (this.committed) {
|
|
105
|
+
return { success: false, error: 'Transaction already committed' };
|
|
106
|
+
}
|
|
107
|
+
if (this.rolledBack) {
|
|
108
|
+
return { success: false, error: 'Transaction already rolled back' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create the complete transaction
|
|
112
|
+
const transaction: Transaction = {
|
|
113
|
+
stamp: this.stamp,
|
|
114
|
+
statements: this.statements,
|
|
115
|
+
reads: [], // TODO: Track reads during statement execution
|
|
116
|
+
id: createTransactionId(this.stamp.id, this.statements, [])
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Commit through coordinator (which will orchestrate PEND/COMMIT)
|
|
120
|
+
await this.coordinator.commit(transaction);
|
|
121
|
+
|
|
122
|
+
this.committed = true;
|
|
123
|
+
return { success: true };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Rollback the transaction (discard local state).
|
|
128
|
+
*
|
|
129
|
+
* Note: Actions have already been applied to collections' trackers.
|
|
130
|
+
* Rollback just prevents commit and clears session state.
|
|
131
|
+
* Collections will discard tracker state when they sync or update.
|
|
132
|
+
*/
|
|
133
|
+
async rollback(): Promise<void> {
|
|
134
|
+
if (this.committed) {
|
|
135
|
+
throw new Error('Cannot rollback: transaction already committed');
|
|
136
|
+
}
|
|
137
|
+
if (this.rolledBack) {
|
|
138
|
+
throw new Error('Transaction already rolled back');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Rollback through coordinator
|
|
142
|
+
await this.coordinator.rollback(this.stamp.id);
|
|
143
|
+
this.rolledBack = true;
|
|
144
|
+
this.statements.length = 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get the transaction stamp ID (stable throughout transaction).
|
|
149
|
+
*/
|
|
150
|
+
getStampId(): string {
|
|
151
|
+
return this.stamp.id;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get the transaction stamp (full metadata).
|
|
156
|
+
*/
|
|
157
|
+
getStamp(): TransactionStamp {
|
|
158
|
+
return this.stamp;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get the list of accumulated statements.
|
|
163
|
+
*/
|
|
164
|
+
getStatements(): readonly string[] {
|
|
165
|
+
return this.statements;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if the transaction has been committed.
|
|
170
|
+
*/
|
|
171
|
+
isCommitted(): boolean {
|
|
172
|
+
return this.committed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if the transaction has been rolled back.
|
|
177
|
+
*/
|
|
178
|
+
isRolledBack(): boolean {
|
|
179
|
+
return this.rolledBack;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { BlockId } from "../blocks/index.js";
|
|
2
|
+
import type { CollectionId } from "../collection/index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transaction Stamp: Created at BEGIN, stable throughout transaction lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* The stamp contains metadata about the transaction's origin and context.
|
|
8
|
+
* The id is computed as a hash of these fields.
|
|
9
|
+
*/
|
|
10
|
+
export type TransactionStamp = {
|
|
11
|
+
/** Peer that initiated the transaction */
|
|
12
|
+
peerId: string;
|
|
13
|
+
|
|
14
|
+
/** When transaction started (milliseconds since epoch) */
|
|
15
|
+
timestamp: number;
|
|
16
|
+
|
|
17
|
+
/** Hash of schema version(s) for validation */
|
|
18
|
+
schemaHash: string;
|
|
19
|
+
|
|
20
|
+
/** Which engine (e.g., 'quereus@0.5.3', 'actions@1.0.0') */
|
|
21
|
+
engineId: string;
|
|
22
|
+
|
|
23
|
+
/** Hash of the stamp fields (computed) - stable identifier throughout transaction */
|
|
24
|
+
id: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Transaction: Finalized at COMMIT with complete statement history.
|
|
29
|
+
*
|
|
30
|
+
* Transactions span multiple collections and use pluggable engines for interpreting the statements.
|
|
31
|
+
* The engine re-executes the statements to verify the resulting operations match what was proposed.
|
|
32
|
+
*/
|
|
33
|
+
export type Transaction = {
|
|
34
|
+
/** The transaction stamp (includes stable id) */
|
|
35
|
+
stamp: TransactionStamp;
|
|
36
|
+
|
|
37
|
+
/** Engine-specific statements (for replay/validation)
|
|
38
|
+
* Array of statements executed during the transaction.
|
|
39
|
+
* - For Quereus: SQL statements
|
|
40
|
+
* - For ActionsEngine: JSON-encoded actions
|
|
41
|
+
*/
|
|
42
|
+
statements: string[];
|
|
43
|
+
|
|
44
|
+
/** Read dependencies for optimistic concurrency control */
|
|
45
|
+
reads: ReadDependency[];
|
|
46
|
+
|
|
47
|
+
/** Transaction identifier (hash of stamp.id + statements + reads)
|
|
48
|
+
* Final transaction identity, used in logs
|
|
49
|
+
*/
|
|
50
|
+
id: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read dependency for optimistic concurrency control.
|
|
55
|
+
* Tracks which block revisions were read during transaction execution.
|
|
56
|
+
*/
|
|
57
|
+
export type ReadDependency = {
|
|
58
|
+
blockId: BlockId;
|
|
59
|
+
/** Expected revision number at time of read */
|
|
60
|
+
revision: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Transaction reference embedded in actions.
|
|
65
|
+
* Just the transaction ID - full transaction can be looked up separately if needed.
|
|
66
|
+
*/
|
|
67
|
+
export type TransactionRef = string; // The transaction ID
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a transaction stamp with computed id.
|
|
71
|
+
* The id is a hash of the stamp fields.
|
|
72
|
+
*/
|
|
73
|
+
export function createTransactionStamp(
|
|
74
|
+
peerId: string,
|
|
75
|
+
timestamp: number,
|
|
76
|
+
schemaHash: string,
|
|
77
|
+
engineId: string
|
|
78
|
+
): TransactionStamp {
|
|
79
|
+
const stampData = JSON.stringify({ peerId, timestamp, schemaHash, engineId });
|
|
80
|
+
const id = `stamp:${hashString(stampData)}`;
|
|
81
|
+
return { peerId, timestamp, schemaHash, engineId, id };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a transaction id from stamp id, statements, and reads.
|
|
86
|
+
* This is the final transaction identity used in logs.
|
|
87
|
+
*/
|
|
88
|
+
export function createTransactionId(
|
|
89
|
+
stampId: string,
|
|
90
|
+
statements: string[],
|
|
91
|
+
reads: ReadDependency[]
|
|
92
|
+
): string {
|
|
93
|
+
const txData = JSON.stringify({ stampId, statements, reads });
|
|
94
|
+
return `tx:${hashString(txData)}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Simple hash function for creating IDs.
|
|
99
|
+
* Uses a basic hash for now - can be replaced with proper cryptographic hash later.
|
|
100
|
+
*/
|
|
101
|
+
function hashString(str: string): string {
|
|
102
|
+
let hash = 0;
|
|
103
|
+
for (let i = 0; i < str.length; i++) {
|
|
104
|
+
const char = str.charCodeAt(i);
|
|
105
|
+
hash = ((hash << 5) - hash) + char;
|
|
106
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
107
|
+
}
|
|
108
|
+
return Math.abs(hash).toString(36);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Transaction engine interface.
|
|
113
|
+
* Pluggable engines implement this to process transaction statements.
|
|
114
|
+
*
|
|
115
|
+
* Engines are responsible for:
|
|
116
|
+
* 1. Parsing the engine-specific statements
|
|
117
|
+
* 2. Executing/re-executing to produce actions
|
|
118
|
+
* 3. Returning the resulting actions per collection
|
|
119
|
+
*/
|
|
120
|
+
export interface ITransactionEngine {
|
|
121
|
+
/**
|
|
122
|
+
* Process a transaction statements to produce actions.
|
|
123
|
+
*
|
|
124
|
+
* Used both for:
|
|
125
|
+
* - Initial execution (client creating transaction)
|
|
126
|
+
* - Re-execution (validators verifying transaction)
|
|
127
|
+
*
|
|
128
|
+
* @param transaction - The transaction to process
|
|
129
|
+
* @returns The resulting actions from execution
|
|
130
|
+
*/
|
|
131
|
+
execute(transaction: Transaction): Promise<ExecutionResult>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Result of transaction execution.
|
|
136
|
+
*/
|
|
137
|
+
export type ExecutionResult = {
|
|
138
|
+
/** Whether execution succeeded */
|
|
139
|
+
success: boolean;
|
|
140
|
+
/** Actions produced by executing the transaction */
|
|
141
|
+
actions?: CollectionActions[];
|
|
142
|
+
/** Results from executing actions (e.g., return values from reads) */
|
|
143
|
+
results?: Map<CollectionId, any[]>;
|
|
144
|
+
/** Error message if execution failed */
|
|
145
|
+
error?: string;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Actions for a specific collection resulting from transaction execution.
|
|
150
|
+
*/
|
|
151
|
+
export type CollectionActions = {
|
|
152
|
+
/** Collection identifier */
|
|
153
|
+
collectionId: string;
|
|
154
|
+
/** Actions to apply to this collection */
|
|
155
|
+
actions: unknown[];
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Result of transaction validation.
|
|
160
|
+
*/
|
|
161
|
+
export type ValidationResult = {
|
|
162
|
+
/** Whether validation succeeded */
|
|
163
|
+
valid: boolean;
|
|
164
|
+
/** Reason for validation failure (if valid=false) */
|
|
165
|
+
reason?: string;
|
|
166
|
+
/** The operations hash computed during validation (for debugging) */
|
|
167
|
+
computedHash?: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Transaction validator interface.
|
|
172
|
+
* Pluggable validators implement this to verify transaction integrity.
|
|
173
|
+
*
|
|
174
|
+
* Validators are invoked when a node receives a PendRequest with a transaction.
|
|
175
|
+
* They re-execute the transaction and verify the operations match.
|
|
176
|
+
*/
|
|
177
|
+
export interface ITransactionValidator {
|
|
178
|
+
/**
|
|
179
|
+
* Validate a transaction by re-executing and comparing operations hash.
|
|
180
|
+
*
|
|
181
|
+
* Validation steps:
|
|
182
|
+
* 1. Verify stamp.engineId matches a known engine
|
|
183
|
+
* 2. Verify stamp.schemaHash matches local schema
|
|
184
|
+
* 3. Verify read dependencies (no stale reads)
|
|
185
|
+
* 4. Re-execute transaction.statements through engine (isolated state)
|
|
186
|
+
* 5. Collect operations from re-execution
|
|
187
|
+
* 6. Compute hash of operations
|
|
188
|
+
* 7. Compare with sender's operationsHash
|
|
189
|
+
*
|
|
190
|
+
* @param transaction - The transaction to validate
|
|
191
|
+
* @param operationsHash - The hash to compare against
|
|
192
|
+
* @returns Validation result
|
|
193
|
+
*/
|
|
194
|
+
validate(transaction: Transaction, operationsHash: string): Promise<ValidationResult>;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get the schema hash for a given engine.
|
|
198
|
+
* Used to verify the sender's schema matches local schema.
|
|
199
|
+
*
|
|
200
|
+
* @param engineId - The engine identifier
|
|
201
|
+
* @returns The schema hash, or undefined if engine not found
|
|
202
|
+
*/
|
|
203
|
+
getSchemaHash(engineId: string): Promise<string | undefined>;
|
|
204
|
+
}
|
|
205
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { BlockId, CollectionId, IBlock, BlockOperations, Transforms, ITransactor } from '../index.js';
|
|
2
|
+
import type { Transaction, ITransactionEngine, ITransactionValidator, ValidationResult, CollectionActions } from './transaction.js';
|
|
3
|
+
import type { Collection } from '../collection/collection.js';
|
|
4
|
+
import { Tracker } from '../transform/tracker.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents an operation on a block within a collection.
|
|
8
|
+
* Must match the Operation type in coordinator.ts for consistent hashing.
|
|
9
|
+
*/
|
|
10
|
+
type Operation =
|
|
11
|
+
| { readonly type: 'insert'; readonly collectionId: CollectionId; readonly blockId: BlockId; readonly block: IBlock }
|
|
12
|
+
| { readonly type: 'update'; readonly collectionId: CollectionId; readonly blockId: BlockId; readonly operations: BlockOperations }
|
|
13
|
+
| { readonly type: 'delete'; readonly collectionId: CollectionId; readonly blockId: BlockId };
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Engine registration for validation.
|
|
17
|
+
*/
|
|
18
|
+
export type EngineRegistration = {
|
|
19
|
+
/** The transaction engine instance */
|
|
20
|
+
engine: ITransactionEngine;
|
|
21
|
+
/** Get the current schema hash for this engine */
|
|
22
|
+
getSchemaHash: () => Promise<string>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Factory function to create a validation coordinator.
|
|
27
|
+
* This allows isolated execution of transactions for validation.
|
|
28
|
+
*/
|
|
29
|
+
export type ValidationCoordinatorFactory = () => {
|
|
30
|
+
/** Apply actions to collections in isolated state */
|
|
31
|
+
applyActions(actions: CollectionActions[], stampId: string): Promise<void>;
|
|
32
|
+
/** Get all transforms from the validation state */
|
|
33
|
+
getTransforms(): Map<CollectionId, Transforms>;
|
|
34
|
+
/** Dispose of the validation coordinator */
|
|
35
|
+
dispose(): void;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Transaction validator implementation.
|
|
40
|
+
*
|
|
41
|
+
* Validates transactions by re-executing them and comparing operations hash.
|
|
42
|
+
* Used by cluster participants when receiving PendRequests.
|
|
43
|
+
*/
|
|
44
|
+
export class TransactionValidator implements ITransactionValidator {
|
|
45
|
+
constructor(
|
|
46
|
+
private readonly engines: Map<string, EngineRegistration>,
|
|
47
|
+
private readonly createValidationCoordinator: ValidationCoordinatorFactory
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
async validate(transaction: Transaction, operationsHash: string): Promise<ValidationResult> {
|
|
51
|
+
const { stamp, statements } = transaction;
|
|
52
|
+
|
|
53
|
+
// 1. Verify engine exists
|
|
54
|
+
const registration = this.engines.get(stamp.engineId);
|
|
55
|
+
if (!registration) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
reason: `Unknown engine: ${stamp.engineId}`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Verify schema hash matches
|
|
63
|
+
const localSchemaHash = await registration.getSchemaHash();
|
|
64
|
+
if (localSchemaHash !== stamp.schemaHash) {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
reason: `Schema mismatch: local=${localSchemaHash}, sender=${stamp.schemaHash}`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 3. Verify read dependencies (optimistic concurrency)
|
|
72
|
+
// TODO: Implement read dependency validation
|
|
73
|
+
// For now, we skip this check - will be implemented with proper block versioning
|
|
74
|
+
|
|
75
|
+
// 4. Create isolated validation coordinator
|
|
76
|
+
const validationCoordinator = this.createValidationCoordinator();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// 5. Re-execute transaction through engine
|
|
80
|
+
const result = await registration.engine.execute(transaction);
|
|
81
|
+
if (!result.success) {
|
|
82
|
+
return {
|
|
83
|
+
valid: false,
|
|
84
|
+
reason: `Re-execution failed: ${result.error}`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 6. Apply actions to validation coordinator (builds transforms)
|
|
89
|
+
if (result.actions && result.actions.length > 0) {
|
|
90
|
+
await validationCoordinator.applyActions(result.actions, stamp.id);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 7. Collect operations from validation coordinator
|
|
94
|
+
const transforms = validationCoordinator.getTransforms();
|
|
95
|
+
const allOperations = this.collectOperations(transforms);
|
|
96
|
+
|
|
97
|
+
// 8. Compute hash
|
|
98
|
+
const computedHash = this.hashOperations(allOperations);
|
|
99
|
+
|
|
100
|
+
// 9. Compare with sender's hash
|
|
101
|
+
if (computedHash !== operationsHash) {
|
|
102
|
+
return {
|
|
103
|
+
valid: false,
|
|
104
|
+
reason: `Operations hash mismatch`,
|
|
105
|
+
computedHash
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { valid: true, computedHash };
|
|
110
|
+
} finally {
|
|
111
|
+
validationCoordinator.dispose();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getSchemaHash(engineId: string): Promise<string | undefined> {
|
|
116
|
+
const registration = this.engines.get(engineId);
|
|
117
|
+
return registration ? await registration.getSchemaHash() : undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Collect all operations from transforms.
|
|
122
|
+
*/
|
|
123
|
+
private collectOperations(transforms: Map<CollectionId, Transforms>): readonly Operation[] {
|
|
124
|
+
return Array.from(transforms.entries()).flatMap(([collectionId, t]) => [
|
|
125
|
+
...Object.entries(t.inserts).map(([blockId, block]) =>
|
|
126
|
+
({ type: 'insert' as const, collectionId, blockId, block })
|
|
127
|
+
),
|
|
128
|
+
...Object.entries(t.updates).map(([blockId, operations]) =>
|
|
129
|
+
({ type: 'update' as const, collectionId, blockId, operations })
|
|
130
|
+
),
|
|
131
|
+
...t.deletes.map(blockId =>
|
|
132
|
+
({ type: 'delete' as const, collectionId, blockId })
|
|
133
|
+
)
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Compute hash of all operations.
|
|
139
|
+
* Must match TransactionCoordinator.hashOperations for consistent validation.
|
|
140
|
+
*/
|
|
141
|
+
private hashOperations(operations: readonly Operation[]): string {
|
|
142
|
+
const operationsData = JSON.stringify(operations);
|
|
143
|
+
const hash = Array.from(operationsData).reduce((acc, char) => {
|
|
144
|
+
const charCode = char.charCodeAt(0);
|
|
145
|
+
return ((acc << 5) - acc + charCode) & acc;
|
|
146
|
+
}, 0);
|
|
147
|
+
return `ops:${Math.abs(hash).toString(36)}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|