@optimystic/db-core 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/src/btree/btree.d.ts +2 -0
  2. package/dist/src/btree/btree.d.ts.map +1 -1
  3. package/dist/src/btree/btree.js +72 -52
  4. package/dist/src/btree/btree.js.map +1 -1
  5. package/dist/src/cluster/structs.d.ts +13 -0
  6. package/dist/src/cluster/structs.d.ts.map +1 -1
  7. package/dist/src/collection/collection.d.ts +3 -0
  8. package/dist/src/collection/collection.d.ts.map +1 -1
  9. package/dist/src/collection/collection.js +6 -0
  10. package/dist/src/collection/collection.js.map +1 -1
  11. package/dist/src/index.d.ts +1 -0
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +1 -0
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/log/log.js +1 -1
  16. package/dist/src/log/log.js.map +1 -1
  17. package/dist/src/logger.d.ts +4 -0
  18. package/dist/src/logger.d.ts.map +1 -0
  19. package/dist/src/logger.js +8 -0
  20. package/dist/src/logger.js.map +1 -0
  21. package/dist/src/transaction/coordinator.d.ts +9 -1
  22. package/dist/src/transaction/coordinator.d.ts.map +1 -1
  23. package/dist/src/transaction/coordinator.js +76 -8
  24. package/dist/src/transaction/coordinator.js.map +1 -1
  25. package/dist/src/transaction/index.d.ts +2 -2
  26. package/dist/src/transaction/index.d.ts.map +1 -1
  27. package/dist/src/transaction/index.js +1 -1
  28. package/dist/src/transaction/index.js.map +1 -1
  29. package/dist/src/transaction/session.d.ts +7 -3
  30. package/dist/src/transaction/session.d.ts.map +1 -1
  31. package/dist/src/transaction/session.js +23 -10
  32. package/dist/src/transaction/session.js.map +1 -1
  33. package/dist/src/transaction/transaction.d.ts +9 -3
  34. package/dist/src/transaction/transaction.d.ts.map +1 -1
  35. package/dist/src/transaction/transaction.js +14 -7
  36. package/dist/src/transaction/transaction.js.map +1 -1
  37. package/dist/src/transaction/validator.d.ts +9 -2
  38. package/dist/src/transaction/validator.d.ts.map +1 -1
  39. package/dist/src/transaction/validator.js +26 -6
  40. package/dist/src/transaction/validator.js.map +1 -1
  41. package/dist/src/transactor/network-transactor.d.ts.map +1 -1
  42. package/dist/src/transactor/network-transactor.js +84 -9
  43. package/dist/src/transactor/network-transactor.js.map +1 -1
  44. package/dist/src/transactor/transactor-source.d.ts +4 -0
  45. package/dist/src/transactor/transactor-source.d.ts.map +1 -1
  46. package/dist/src/transactor/transactor-source.js +25 -9
  47. package/dist/src/transactor/transactor-source.js.map +1 -1
  48. package/dist/src/transform/atomic-proxy.d.ts +26 -0
  49. package/dist/src/transform/atomic-proxy.d.ts.map +1 -0
  50. package/dist/src/transform/atomic-proxy.js +47 -0
  51. package/dist/src/transform/atomic-proxy.js.map +1 -0
  52. package/dist/src/transform/cache-source.d.ts +3 -2
  53. package/dist/src/transform/cache-source.d.ts.map +1 -1
  54. package/dist/src/transform/cache-source.js +15 -3
  55. package/dist/src/transform/cache-source.js.map +1 -1
  56. package/dist/src/transform/index.d.ts +1 -0
  57. package/dist/src/transform/index.d.ts.map +1 -1
  58. package/dist/src/transform/index.js +1 -0
  59. package/dist/src/transform/index.js.map +1 -1
  60. package/dist/src/utility/batch-coordinator.d.ts.map +1 -1
  61. package/dist/src/utility/batch-coordinator.js +6 -1
  62. package/dist/src/utility/batch-coordinator.js.map +1 -1
  63. package/dist/src/utility/hash-string.d.ts +3 -6
  64. package/dist/src/utility/hash-string.d.ts.map +1 -1
  65. package/dist/src/utility/hash-string.js +8 -11
  66. package/dist/src/utility/hash-string.js.map +1 -1
  67. package/dist/src/utility/lru-map.d.ts +18 -0
  68. package/dist/src/utility/lru-map.d.ts.map +1 -0
  69. package/dist/src/utility/lru-map.js +52 -0
  70. package/dist/src/utility/lru-map.js.map +1 -0
  71. package/package.json +15 -8
  72. package/src/btree/btree.ts +71 -50
  73. package/src/cluster/structs.ts +11 -0
  74. package/src/collection/collection.ts +9 -0
  75. package/src/index.ts +1 -0
  76. package/src/log/log.ts +1 -1
  77. package/src/logger.ts +10 -0
  78. package/src/transaction/coordinator.ts +86 -9
  79. package/src/transaction/index.ts +4 -2
  80. package/src/transaction/session.ts +34 -10
  81. package/src/transaction/transaction.ts +23 -10
  82. package/src/transaction/validator.ts +34 -7
  83. package/src/transactor/network-transactor.ts +92 -11
  84. package/src/transactor/transactor-source.ts +28 -9
  85. package/src/transform/atomic-proxy.ts +49 -0
  86. package/src/transform/cache-source.ts +18 -4
  87. package/src/transform/index.ts +1 -0
  88. package/src/utility/batch-coordinator.ts +7 -1
  89. package/src/utility/hash-string.ts +14 -17
  90. package/src/utility/lru-map.ts +55 -0
  91. package/dist/index.min.js +0 -9
  92. package/dist/index.min.js.map +0 -7
@@ -1,4 +1,6 @@
1
1
  import { Pending } from "./pending.js";
2
+ import { createLogger } from "../logger.js";
3
+ const log = createLogger('batch-coordinator');
2
4
  /**
3
5
  * Creates batches for a given payload, grouped by the coordinating peer for each block id
4
6
  */
@@ -93,6 +95,7 @@ export async function processBatches(batches, process, getBlockIds, getBlockPayl
93
95
  .catch(async (e) => {
94
96
  if (expiration > Date.now()) {
95
97
  const excludedPeers = [batch.peerId, ...(batch.excludedPeers ?? [])];
98
+ log('retry peer=%s excluded=%d', batch.peerId.toString(), excludedPeers.length);
96
99
  const retries = await createBatchesForPayload(getBlockIds(batch), batch.payload, getBlockPayload, excludedPeers, findCoordinator);
97
100
  if (retries.length > 0 && expiration > Date.now()) {
98
101
  const root = rootOf.get(batch) ?? batch;
@@ -121,6 +124,8 @@ export async function createBatchesForPayload(blockIds, payload, getBlockPayload
121
124
  // Find coordinator for each key
122
125
  const blockIdPeerId = await Promise.all(Array.from(distinctBlockIds).map(async (bid) => [bid, await findCoordinator(bid, { excludedPeers })]));
123
126
  // Group blocks around their coordinating peers
124
- return makeBatchesByPeer(blockIdPeerId, payload, getBlockPayload, excludedPeers);
127
+ const batches = makeBatchesByPeer(blockIdPeerId, payload, getBlockPayload, excludedPeers);
128
+ log('createBatches blockIds=%d batches=%d excluded=%d', distinctBlockIds.size, batches.length, excludedPeers.length);
129
+ return batches;
125
130
  }
126
131
  //# sourceMappingURL=batch-coordinator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch-coordinator.js","sourceRoot":"","sources":["../../../src/utility/batch-coordinator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAgBvC;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAChC,UAA0C,EAC1C,OAAiB,EACjB,eAA0G,EAC1G,aAAwB;IAExB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE;QAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAoD,CAAC;QAChI,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,EAA2C,CAAC,CAAC;QAClJ,OAAO,GAAG,CAAC;IACZ,CAAC,EAAE,IAAI,GAAG,EAAiD,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,SAAS,CAAC,CAAC,iBAAiB,CAAsB,OAAgD;IACpG,MAAM,KAAK,GAA4C,CAAC,GAAG,OAAO,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAC9C,MAAM,KAAK,CAAC;QAChB,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAsB,OAAgD,EAAE,SAAoE;IAClK,wFAAwF;IACxF,uEAAuE;IACvE,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,MAAM,KAAK,GAA4C,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;YAC1B,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAC,KAAK,GAAG,IAAI,CAAC;gBAAC,MAAM;YAAC,CAAC;YAC7C,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;gBAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC;YACrF,CAAC;QACL,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,SAAS,CAAC,CAAC,UAAU,CAAsB,OAAgD,EAAE,SAAqE;IACpK,MAAM,KAAK,GAA4C,CAAC,GAAG,OAAO,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;QAC3B,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,CAAC;QAChB,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAkB,EAAE,OAAgB,EAAE,gBAAuC;IACxG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,OAAgD,EAChD,OAA6E,EAC7E,WAAwE,EACxE,eAA0G,EAC1G,UAAkB,EAClB,eAA4F;IAEzF,kFAAkF;IAClF,MAAM,MAAM,GAAG,IAAI,OAAO,EAAgF,CAAC;IAC3G,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1C,mGAAmG;IACnG,MAAM,UAAU,GAAG,KAAK,EAAE,GAA4C,EAAE,EAAE;QACtE,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACtC,KAAK,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;iBACrC,KAAK,CAAC,KAAK,EAAC,CAAC,EAAC,EAAE;gBACb,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;oBAC1B,MAAM,aAAa,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC;oBACrE,MAAM,OAAO,GAAG,MAAM,uBAAuB,CACzC,WAAW,CAAC,KAAK,CAAC,EAClB,KAAK,CAAC,OAAO,EACb,eAAe,EACf,aAAa,EACb,eAAe,CAClB,CAAC;oBACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;wBAChD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;wBACxC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC;wBAC3D,KAAK,MAAM,CAAC,IAAI,OAAO;4BAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;wBAC7C,4EAA4E;wBAC5E,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;oBAC9B,CAAC;gBACL,CAAC;gBACD,MAAM,CAAC,CAAC;YACZ,CAAC,CAAC,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC,CAAC;QAEJ,qCAAqC;QACrC,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC,CAAC;IAEF,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC5C,QAAmB,EACnB,OAAiB,EACjB,eAA0G,EAC1G,aAAuB,EACvB,eAA4F;IAE5F,oBAAoB;IACpB,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE3C,gCAAgC;IAChC,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACtC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAC9C,CAAC,GAAG,EAAE,MAAM,eAAe,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,CAAC,CAAU,CAC7D,CACD,CAAC;IAEF,+CAA+C;IAC/C,OAAO,iBAAiB,CAAsB,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;AACvG,CAAC"}
1
+ {"version":3,"file":"batch-coordinator.js","sourceRoot":"","sources":["../../../src/utility/batch-coordinator.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Simple djb2 string hash function.
3
- *
4
- * This is a non-cryptographic hash suitable for generating short identifiers.
5
- * For security-critical hashing, use SHA-256 from multiformats/hashes/sha2.
2
+ * SHA-256 string hash function.
6
3
  *
7
4
  * @param str - The string to hash
8
- * @returns A base-36 encoded hash string
5
+ * @returns A base64url-encoded SHA-256 hash string
9
6
  */
10
- export declare function hashString(str: string): string;
7
+ export declare function hashString(str: string): Promise<string>;
11
8
  //# sourceMappingURL=hash-string.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash-string.d.ts","sourceRoot":"","sources":["../../../src/utility/hash-string.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C"}
1
+ {"version":3,"file":"hash-string.d.ts","sourceRoot":"","sources":["../../../src/utility/hash-string.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7D"}
@@ -1,17 +1,14 @@
1
+ import { sha256 } from 'multiformats/hashes/sha2';
2
+ import { toString } from 'uint8arrays/to-string';
1
3
  /**
2
- * Simple djb2 string hash function.
3
- *
4
- * This is a non-cryptographic hash suitable for generating short identifiers.
5
- * For security-critical hashing, use SHA-256 from multiformats/hashes/sha2.
4
+ * SHA-256 string hash function.
6
5
  *
7
6
  * @param str - The string to hash
8
- * @returns A base-36 encoded hash string
7
+ * @returns A base64url-encoded SHA-256 hash string
9
8
  */
10
- export function hashString(str) {
11
- let hash = 5381;
12
- for (let i = 0; i < str.length; i++) {
13
- hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;
14
- }
15
- return Math.abs(hash).toString(36);
9
+ export async function hashString(str) {
10
+ const input = new TextEncoder().encode(str);
11
+ const mh = await sha256.digest(input);
12
+ return toString(mh.digest, 'base64url');
16
13
  }
17
14
  //# sourceMappingURL=hash-string.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash-string.js","sourceRoot":"","sources":["../../../src/utility/hash-string.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACrC,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"hash-string.js","sourceRoot":"","sources":["../../../src/utility/hash-string.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC3C,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * A simple LRU (Least Recently Used) map backed by JavaScript's Map insertion order.
3
+ * Accessing or setting an entry refreshes it to the most-recently-used position.
4
+ * When the map exceeds maxSize, the least-recently-used entry is evicted.
5
+ */
6
+ export declare class LruMap<K, V> {
7
+ private readonly maxSize;
8
+ private readonly map;
9
+ constructor(maxSize: number);
10
+ get(key: K): V | undefined;
11
+ set(key: K, value: V): this;
12
+ has(key: K): boolean;
13
+ delete(key: K): boolean;
14
+ clear(): void;
15
+ get size(): number;
16
+ [Symbol.iterator](): IterableIterator<[K, V]>;
17
+ }
18
+ //# sourceMappingURL=lru-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru-map.d.ts","sourceRoot":"","sources":["../../../src/utility/lru-map.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,MAAM,CAAC,CAAC,EAAE,CAAC;IAGX,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmB;gBAEV,OAAO,EAAE,MAAM;IAI5C,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAU1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAa3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIvB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAG7C"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * A simple LRU (Least Recently Used) map backed by JavaScript's Map insertion order.
3
+ * Accessing or setting an entry refreshes it to the most-recently-used position.
4
+ * When the map exceeds maxSize, the least-recently-used entry is evicted.
5
+ */
6
+ export class LruMap {
7
+ maxSize;
8
+ map = new Map();
9
+ constructor(maxSize) {
10
+ this.maxSize = maxSize;
11
+ if (maxSize < 1)
12
+ throw new Error('LruMap maxSize must be >= 1');
13
+ }
14
+ get(key) {
15
+ const value = this.map.get(key);
16
+ if (value !== undefined) {
17
+ // Refresh: delete and re-insert to move to end (most recent)
18
+ this.map.delete(key);
19
+ this.map.set(key, value);
20
+ }
21
+ return value;
22
+ }
23
+ set(key, value) {
24
+ // If already present, delete first to refresh position
25
+ if (this.map.has(key)) {
26
+ this.map.delete(key);
27
+ }
28
+ else if (this.map.size >= this.maxSize) {
29
+ // Evict the oldest (first) entry
30
+ const oldest = this.map.keys().next().value;
31
+ this.map.delete(oldest);
32
+ }
33
+ this.map.set(key, value);
34
+ return this;
35
+ }
36
+ has(key) {
37
+ return this.map.has(key);
38
+ }
39
+ delete(key) {
40
+ return this.map.delete(key);
41
+ }
42
+ clear() {
43
+ this.map.clear();
44
+ }
45
+ get size() {
46
+ return this.map.size;
47
+ }
48
+ [Symbol.iterator]() {
49
+ return this.map[Symbol.iterator]();
50
+ }
51
+ }
52
+ //# sourceMappingURL=lru-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru-map.js","sourceRoot":"","sources":["../../../src/utility/lru-map.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,MAAM;IAGW;IAFZ,GAAG,GAAG,IAAI,GAAG,EAAQ,CAAC;IAEvC,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;QAC3C,IAAI,OAAO,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjE,CAAC;IAED,GAAG,CAAC,GAAM;QACT,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,6DAA6D;YAC7D,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,GAAG,CAAC,GAAM,EAAE,KAAQ;QACnB,uDAAuD;QACvD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC1C,iCAAiC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC;YAC7C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,GAAG,CAAC,GAAM;QACT,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,GAAM;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACtB,CAAC;IAED,CAAC,MAAM,CAAC,QAAQ,CAAC;QAChB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;IACpC,CAAC;CACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimystic/db-core",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Core database functionality for Optimystic",
6
6
  "main": "dist/src/index.js",
@@ -15,6 +15,10 @@
15
15
  ".": {
16
16
  "types": "./dist/src/index.d.ts",
17
17
  "import": "./dist/src/index.js"
18
+ },
19
+ "./test": {
20
+ "types": "./dist/test/test-transactor.d.ts",
21
+ "import": "./dist/test/test-transactor.js"
18
22
  }
19
23
  },
20
24
  "repository": {
@@ -36,12 +40,9 @@
36
40
  "acid"
37
41
  ],
38
42
  "scripts": {
39
- "clean": "aegir clean",
40
- "build": "aegir build --env node",
41
- "lint": "aegir lint",
42
- "test": "aegir test",
43
- "test:node": "aegir test -t node",
44
- "dep-check": "aegir dep-check"
43
+ "clean": "rimraf dist",
44
+ "build": "tsc",
45
+ "test": "node --import ./register.mjs node_modules/mocha/bin/mocha.js \"test/**/*.spec.ts\" --colors --reporter min"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@libp2p/crypto": "^5.1.13",
@@ -49,9 +50,15 @@
49
50
  "@libp2p/peer-id": "^6.0.4",
50
51
  "@libp2p/peer-id-factory": "^4.2.4",
51
52
  "@multiformats/multiaddr": "^13.0.1",
53
+ "@types/chai-as-promised": "^8.0.2",
54
+ "@types/debug": "^4.1.12",
52
55
  "@types/mocha": "^10.0.10",
53
56
  "@types/node": "^25.1.0",
54
- "aegir": "^47.0.26",
57
+ "chai": "^6.2.2",
58
+ "chai-as-promised": "^8.0.2",
59
+ "mocha": "^11.7.5",
60
+ "rimraf": "^6.1.2",
61
+ "ts-node": "^10.9.2",
55
62
  "typescript": "^5.9.3"
56
63
  },
57
64
  "dependencies": {
@@ -4,6 +4,7 @@ import { apply, get } from "../blocks/index.js";
4
4
  import { TreeLeafBlockType, TreeBranchBlockType, entries$, nodes$, partitions$ } from "./nodes.js";
5
5
  import type { BranchNode, ITreeNode, LeafNode } from "./nodes.js";
6
6
  import type { TreeBlock } from "./tree-block.js";
7
+ import { AtomicProxy } from "../transform/atomic-proxy.js";
7
8
 
8
9
  export const NodeCapacity = 64;
9
10
 
@@ -15,6 +16,7 @@ export const NodeCapacity = 64;
15
16
  */
16
17
  export class BTree<TKey, TEntry> {
17
18
  protected _version = 0; // only for path invalidation
19
+ private _proxy?: AtomicProxy<ITreeNode>;
18
20
 
19
21
  /**
20
22
  * @param [compare=(a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0] a comparison function for keys. The default uses < and > operators.
@@ -28,6 +30,10 @@ export class BTree<TKey, TEntry> {
28
30
  ) {
29
31
  }
30
32
 
33
+ private atomic<T>(fn: () => Promise<T>): Promise<T> {
34
+ return this._proxy ? this._proxy.atomic(fn) : fn();
35
+ }
36
+
31
37
  static createRoot(
32
38
  store: BlockStore<ITreeNode>
33
39
  ) {
@@ -41,10 +47,13 @@ export class BTree<TKey, TEntry> {
41
47
  compare = (a: TKey, b: TKey) => a < b ? -1 : a > b ? 1 : 0,
42
48
  newId?: BlockId,
43
49
  ) {
44
- const root = BTree.createRoot(store as BlockStore<TreeBlock>);
45
- store.insert(root);
46
- const trunk = createTrunk(store as BlockStore<TreeBlock>, root.header.id, newId);
47
- return new BTree(store, trunk, keyFromEntry, compare);
50
+ const proxy = new AtomicProxy(store);
51
+ const root = BTree.createRoot(proxy as BlockStore<TreeBlock>);
52
+ proxy.insert(root);
53
+ const trunk = createTrunk(proxy as BlockStore<TreeBlock>, root.header.id, newId);
54
+ const tree = new BTree(proxy, trunk, keyFromEntry, compare);
55
+ tree._proxy = proxy;
56
+ return tree;
48
57
  }
49
58
 
50
59
  /** @returns a path to the first entry (on = false if no entries) */
@@ -118,12 +127,14 @@ export class BTree<TKey, TEntry> {
118
127
  * Added entries are frozen to ensure immutability
119
128
  * @returns path to the new (on = true) or conflicting (on = false) row. */
120
129
  async insert(entry: TEntry): Promise<Path<TKey, TEntry>> {
121
- Object.freeze(entry); // Ensure immutability
122
- const path = await this.internalInsert(entry);
123
- if (path.on) {
124
- path.version = ++this._version;
125
- }
126
- return path;
130
+ return this.atomic(async () => {
131
+ Object.freeze(entry); // Ensure immutability
132
+ const path = await this.internalInsert(entry);
133
+ if (path.on) {
134
+ path.version = ++this._version;
135
+ }
136
+ return path;
137
+ });
127
138
  }
128
139
 
129
140
  /** Updates the entry at the given path to the given value. Deletes and inserts if the key changes.
@@ -135,30 +146,34 @@ export class BTree<TKey, TEntry> {
135
146
  * * wasUpdate = true, given path is not on an entry
136
147
  * * else newEntry's new key already present; returned path is "near" existing entry */
137
148
  async updateAt(path: Path<TKey, TEntry>, newEntry: TEntry): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]> {
138
- this.validatePath(path);
139
- if (path.on) {
140
- Object.freeze(newEntry);
141
- }
142
- const result = await this.internalUpdate(path, newEntry);
143
- if (result[0].on) {
144
- result[0].version = ++this._version;
145
- }
146
- return result;
149
+ return this.atomic(async () => {
150
+ this.validatePath(path);
151
+ if (path.on) {
152
+ Object.freeze(newEntry);
153
+ }
154
+ const result = await this.internalUpdate(path, newEntry);
155
+ if (result[0].on) {
156
+ result[0].version = ++this._version;
157
+ }
158
+ return result;
159
+ });
147
160
  }
148
161
 
149
162
  /** Inserts the entry if it doesn't exist, or updates it if it does.
150
163
  * The entry is frozen to ensure immutability.
151
164
  * @returns path to the new entry. on = true if existing; on = false if new. */
152
165
  async upsert(entry: TEntry): Promise<Path<TKey, TEntry>> {
153
- const path = await this.find(this.keyFromEntry(entry));
154
- Object.freeze(entry);
155
- if (path.on) {
156
- this.updateEntry(path, entry);
157
- } else {
158
- await this.internalInsertAt(path, entry);
159
- }
160
- path.version = ++this._version;
161
- return path;
166
+ return this.atomic(async () => {
167
+ const path = await this.find(this.keyFromEntry(entry));
168
+ Object.freeze(entry);
169
+ if (path.on) {
170
+ this.updateEntry(path, entry);
171
+ } else {
172
+ await this.internalInsertAt(path, entry);
173
+ }
174
+ path.version = ++this._version;
175
+ return path;
176
+ });
162
177
  }
163
178
 
164
179
  /** Inserts or updates depending on the existence of the given key, using callbacks to generate the new value.
@@ -167,18 +182,20 @@ export class BTree<TKey, TEntry> {
167
182
  * @returns path to new entry and whether an update or insert attempted.
168
183
  * If getUpdated callback returns a row that is already present, the resulting path will not be on. */
169
184
  async merge(newEntry: TEntry, getUpdated: (existing: TEntry) => TEntry): Promise<[path: Path<TKey, TEntry>, wasUpdate: boolean]> {
170
- const newKey = await this.keyFromEntry(newEntry);
171
- const path = await this.find(newKey);
172
- if (path.on) {
173
- const result = await this.updateAt(path, getUpdated(this.getEntry(path))); // Don't use internalUpdate - need to freeze and check for mutation
174
- // Note: updateAt already increments version, so don't double-increment here
175
- return result;
176
- } else {
177
- await this.internalInsertAt(path, Object.freeze(newEntry));
178
- path.on = true;
179
- path.version = ++this._version;
180
- return [path, false];
181
- }
185
+ return this.atomic(async () => {
186
+ const newKey = await this.keyFromEntry(newEntry);
187
+ const path = await this.find(newKey);
188
+ if (path.on) {
189
+ const result = await this.updateAt(path, getUpdated(this.getEntry(path))); // Don't use internalUpdate - need to freeze and check for mutation
190
+ // Note: updateAt already increments version, so don't double-increment here
191
+ return result;
192
+ } else {
193
+ await this.internalInsertAt(path, Object.freeze(newEntry));
194
+ path.on = true;
195
+ path.version = ++this._version;
196
+ return [path, false];
197
+ }
198
+ });
182
199
  }
183
200
 
184
201
  /** Deletes the entry at the given path.
@@ -186,19 +203,23 @@ export class BTree<TKey, TEntry> {
186
203
  * @returns true if the delete succeeded (the key was found); false otherwise.
187
204
  */
188
205
  async deleteAt(path: Path<TKey, TEntry>): Promise<boolean> {
189
- this.validatePath(path);
190
- const result = await this.internalDelete(path);
191
- if (result) {
192
- ++this._version;
193
- }
194
- return result;
206
+ return this.atomic(async () => {
207
+ this.validatePath(path);
208
+ const result = await this.internalDelete(path);
209
+ if (result) {
210
+ ++this._version;
211
+ }
212
+ return result;
213
+ });
195
214
  }
196
215
 
197
216
  async drop() { // Node: only when a root treeBlock
198
- const root = await this.trunk.get();
199
- for await (const id of this.nodeIds(root)) {
200
- this.store.delete(id);
201
- }
217
+ return this.atomic(async () => {
218
+ const root = await this.trunk.get();
219
+ for await (const id of this.nodeIds(root)) {
220
+ this.store.delete(id);
221
+ }
222
+ });
202
223
  }
203
224
 
204
225
  /** Iterates forward starting from the path location (inclusive) to the end.
@@ -27,6 +27,13 @@ export type ClusterRecord = {
27
27
  networkSizeHint?: number;
28
28
  /** Confidence in the network size estimate (0-1) */
29
29
  networkSizeConfidence?: number;
30
+ /** Transaction proceeded despite minority rejections */
31
+ disputed?: boolean;
32
+ /** Evidence of the dispute: which peers rejected and why */
33
+ disputeEvidence?: {
34
+ rejectingPeers: string[];
35
+ rejectReasons: { [peerId: string]: string };
36
+ };
30
37
  }
31
38
 
32
39
  export interface ClusterConsensusConfig {
@@ -42,4 +49,8 @@ export interface ClusterConsensusConfig {
42
49
  clusterSizeTolerance: number;
43
50
  /** Window for detecting partition in milliseconds (default 60000 = 1 min) */
44
51
  partitionDetectionWindow: number;
52
+ /** Enable dispute escalation protocol (default false) */
53
+ disputeEnabled?: boolean;
54
+ /** Timeout for dispute arbitration in milliseconds (default 60000) */
55
+ disputeArbitrationTimeoutMs?: number;
45
56
  }
@@ -1,6 +1,7 @@
1
1
  import type { IBlock, Action, ActionType, ActionHandler, BlockId, ITransactor, BlockStore } from "../index.js";
2
2
  import { Log, Atomic, Tracker, copyTransforms, CacheSource, isTransformsEmpty, TransactorSource } from "../index.js";
3
3
  import type { CollectionHeaderBlock, CollectionId, ICollection } from "./index.js";
4
+ import type { ReadDependency } from "../transaction/transaction.js";
4
5
  import { randomBytes } from '@noble/hashes/utils.js';
5
6
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
6
7
  import { Latches } from "../utility/latches.js";
@@ -207,6 +208,14 @@ export class Collection<TAction> implements ICollection<TAction> {
207
208
  }
208
209
  }
209
210
 
211
+ getReadDependencies(): ReadDependency[] {
212
+ return this.source.getReadDependencies();
213
+ }
214
+
215
+ clearReadDependencies(): void {
216
+ this.source.clearReadDependencies();
217
+ }
218
+
210
219
  /** Called for each local action that may be in conflict with a remote action (always called under latch).
211
220
  * @param action - The local action to check
212
221
  * @param potential - The remote action that is potentially in conflict
package/src/index.ts CHANGED
@@ -16,3 +16,4 @@ export * from "./utility/nameof.js";
16
16
  export * from "./utility/ensured.js";
17
17
  export * from "./utility/pending.js";
18
18
  export * from "./utility/block-id-to-bytes.js";
19
+ export * from "./utility/lru-map.js";
package/src/log/log.ts CHANGED
@@ -56,7 +56,7 @@ export class Log<TAction> {
56
56
  /** Gets the action context of the log. */
57
57
  async getActionContext(): Promise<ActionContext | undefined> {
58
58
  const tailPath = await this.chain.getTail();
59
- if (!tailPath) {
59
+ if (!tailPath || tailPath.block.entries.length === 0) {
60
60
  return undefined;
61
61
  }
62
62
  const checkpoint = await this.findCheckpoint(tailPath);
package/src/logger.ts ADDED
@@ -0,0 +1,10 @@
1
+ import debug from 'debug'
2
+
3
+ const BASE_NAMESPACE = 'optimystic:db-core'
4
+
5
+ export function createLogger(subNamespace: string): debug.Debugger {
6
+ return debug(`${BASE_NAMESPACE}:${subNamespace}`)
7
+ }
8
+
9
+ export const verbose = typeof process !== 'undefined'
10
+ && (process.env.OPTIMYSTIC_VERBOSE === '1' || process.env.OPTIMYSTIC_VERBOSE === 'true');