@topgunbuild/core 0.1.0 → 0.2.0-alpha

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/dist/index.d.mts CHANGED
@@ -156,6 +156,86 @@ declare class LWWMap<K, V> {
156
156
  allKeys(): IterableIterator<K>;
157
157
  }
158
158
 
159
+ /**
160
+ * Merkle Node for ORMap.
161
+ * Uses a prefix trie structure based on key hash (similar to LWWMap MerkleTree).
162
+ */
163
+ interface ORMapMerkleNode {
164
+ hash: number;
165
+ children?: {
166
+ [key: string]: ORMapMerkleNode;
167
+ };
168
+ entries?: Map<string, number>;
169
+ }
170
+ /**
171
+ * A Merkle Tree implementation specifically for ORMap synchronization.
172
+ * Uses a Prefix Trie structure based on the hash of the Record Key.
173
+ *
174
+ * Structure:
175
+ * - Level 0: Root
176
+ * - Level 1..N: Buckets based on hex digits of Key Hash.
177
+ *
178
+ * Key difference from LWWMap MerkleTree:
179
+ * - Each key can have multiple records (tags), so the entry hash includes all records for that key.
180
+ */
181
+ declare class ORMapMerkleTree {
182
+ private root;
183
+ private readonly depth;
184
+ constructor(depth?: number);
185
+ /**
186
+ * Update tree from ORMap data.
187
+ * Rebuilds hashes for all entries in the map.
188
+ */
189
+ updateFromORMap<K, V>(map: ORMap<K, V>): void;
190
+ /**
191
+ * Incrementally update a single key's hash.
192
+ * Call this when records for a key change.
193
+ */
194
+ update<V>(key: string, records: Map<string, ORMapRecord<V>>): void;
195
+ /**
196
+ * Remove a key from the tree.
197
+ * Called when all records for a key are removed.
198
+ */
199
+ remove(key: string): void;
200
+ private updateNode;
201
+ private removeNode;
202
+ /**
203
+ * Get the root hash for quick comparison.
204
+ */
205
+ getRootHash(): number;
206
+ /**
207
+ * Get node at a specific path.
208
+ */
209
+ getNode(path: string): ORMapMerkleNode | undefined;
210
+ /**
211
+ * Returns the hashes of the children at the given path.
212
+ * Used by the client/server to compare buckets.
213
+ */
214
+ getBuckets(path: string): Record<string, number>;
215
+ /**
216
+ * For a leaf node (bucket), returns the actual keys it contains.
217
+ * Used to request specific keys when a bucket differs.
218
+ */
219
+ getKeysInBucket(path: string): string[];
220
+ /**
221
+ * Find keys that differ between this tree and bucket info from remote.
222
+ * Returns keys that:
223
+ * - Exist locally but have different hash on remote
224
+ * - Exist on remote but not locally
225
+ * - Exist locally but not on remote
226
+ */
227
+ findDiffKeys(path: string, remoteEntries: Map<string, number>): Set<string>;
228
+ /**
229
+ * Get all entry hashes at a leaf path.
230
+ * Used when sending bucket details to remote.
231
+ */
232
+ getEntryHashes(path: string): Map<string, number>;
233
+ /**
234
+ * Check if a path leads to a leaf node.
235
+ */
236
+ isLeaf(path: string): boolean;
237
+ }
238
+
159
239
  /**
160
240
  * A record in the OR-Map (Observed-Remove Map).
161
241
  * Represents a single value instance with a unique tag.
@@ -166,6 +246,20 @@ interface ORMapRecord<V> {
166
246
  tag: string;
167
247
  ttlMs?: number;
168
248
  }
249
+ /**
250
+ * Result of merging records for a key.
251
+ */
252
+ interface MergeKeyResult {
253
+ added: number;
254
+ updated: number;
255
+ }
256
+ /**
257
+ * Snapshot of ORMap internal state for Merkle Tree synchronization.
258
+ */
259
+ interface ORMapSnapshot<K, V> {
260
+ items: Map<K, Map<string, ORMapRecord<V>>>;
261
+ tombstones: Set<string>;
262
+ }
169
263
  /**
170
264
  * OR-Map (Observed-Remove Map) Implementation.
171
265
  *
@@ -181,6 +275,7 @@ declare class ORMap<K, V> {
181
275
  private items;
182
276
  private tombstones;
183
277
  private readonly hlc;
278
+ private merkleTree;
184
279
  constructor(hlc: HLC);
185
280
  private listeners;
186
281
  onChange(callback: () => void): () => void;
@@ -219,8 +314,9 @@ declare class ORMap<K, V> {
219
314
  getTombstones(): string[];
220
315
  /**
221
316
  * Applies a record from a remote source (Sync).
317
+ * Returns true if the record was applied (not tombstoned).
222
318
  */
223
- apply(key: K, record: ORMapRecord<V>): void;
319
+ apply(key: K, record: ORMapRecord<V>): boolean;
224
320
  /**
225
321
  * Applies a tombstone (deletion) from a remote source.
226
322
  */
@@ -236,8 +332,76 @@ declare class ORMap<K, V> {
236
332
  * Garbage Collection: Prunes tombstones older than the specified timestamp.
237
333
  */
238
334
  prune(olderThan: Timestamp): string[];
335
+ /**
336
+ * Get the Merkle Tree for this ORMap.
337
+ * Used for efficient synchronization.
338
+ */
339
+ getMerkleTree(): ORMapMerkleTree;
340
+ /**
341
+ * Get a snapshot of internal state for Merkle Tree synchronization.
342
+ * Returns references to internal structures - do not modify!
343
+ */
344
+ getSnapshot(): ORMapSnapshot<K, V>;
345
+ /**
346
+ * Get all keys in this ORMap.
347
+ */
348
+ allKeys(): K[];
349
+ /**
350
+ * Get the internal records map for a key.
351
+ * Returns Map<tag, record> or undefined if key doesn't exist.
352
+ * Used for Merkle sync.
353
+ */
354
+ getRecordsMap(key: K): Map<string, ORMapRecord<V>> | undefined;
355
+ /**
356
+ * Merge remote records for a specific key into local state.
357
+ * Implements Observed-Remove CRDT semantics.
358
+ * Used during Merkle Tree synchronization.
359
+ *
360
+ * @param key The key to merge
361
+ * @param remoteRecords Array of records from remote
362
+ * @param remoteTombstones Array of tombstone tags from remote
363
+ * @returns Result with count of added and updated records
364
+ */
365
+ mergeKey(key: K, remoteRecords: ORMapRecord<V>[], remoteTombstones?: string[]): MergeKeyResult;
366
+ /**
367
+ * Check if a tag is tombstoned.
368
+ */
369
+ isTombstoned(tag: string): boolean;
370
+ /**
371
+ * Update the Merkle Tree for a specific key.
372
+ * Called internally after any modification.
373
+ */
374
+ private updateMerkleTree;
239
375
  }
240
376
 
377
+ /**
378
+ * Convert Timestamp to deterministic string for hashing.
379
+ * Format: millis:counter:nodeId
380
+ */
381
+ declare function timestampToString(ts: Timestamp): string;
382
+ /**
383
+ * Hash an ORMap entry (key + all its records).
384
+ * Must be deterministic regardless of insertion order.
385
+ *
386
+ * @param key The key of the entry
387
+ * @param records Map of tag -> record for this key
388
+ * @returns Hash as a number (FNV-1a hash)
389
+ */
390
+ declare function hashORMapEntry<V>(key: string, records: Map<string, ORMapRecord<V>>): number;
391
+ /**
392
+ * Hash a single ORMapRecord for comparison.
393
+ * Used when comparing individual records during merge.
394
+ */
395
+ declare function hashORMapRecord<V>(record: ORMapRecord<V>): number;
396
+ /**
397
+ * Compare two timestamps.
398
+ * Returns:
399
+ * < 0 if a < b
400
+ * > 0 if a > b
401
+ * = 0 if a == b
402
+ */
403
+ declare function compareTimestamps(a: Timestamp, b: Timestamp): number;
404
+
241
405
  /**
242
406
  * FNV-1a Hash implementation for strings.
243
407
  * Fast, non-cryptographic, synchronous.
@@ -559,6 +723,142 @@ declare const TopicMessageEventSchema: z.ZodObject<{
559
723
  timestamp: z.ZodNumber;
560
724
  }, z.core.$strip>;
561
725
  }, z.core.$strip>;
726
+ declare const PingMessageSchema: z.ZodObject<{
727
+ type: z.ZodLiteral<"PING">;
728
+ timestamp: z.ZodNumber;
729
+ }, z.core.$strip>;
730
+ declare const PongMessageSchema: z.ZodObject<{
731
+ type: z.ZodLiteral<"PONG">;
732
+ timestamp: z.ZodNumber;
733
+ serverTime: z.ZodNumber;
734
+ }, z.core.$strip>;
735
+ /**
736
+ * ORMAP_SYNC_INIT: Client initiates ORMap sync
737
+ * Sends root hash and bucket hashes to server
738
+ */
739
+ declare const ORMapSyncInitSchema: z.ZodObject<{
740
+ type: z.ZodLiteral<"ORMAP_SYNC_INIT">;
741
+ mapName: z.ZodString;
742
+ rootHash: z.ZodNumber;
743
+ bucketHashes: z.ZodRecord<z.ZodString, z.ZodNumber>;
744
+ lastSyncTimestamp: z.ZodOptional<z.ZodNumber>;
745
+ }, z.core.$strip>;
746
+ /**
747
+ * ORMAP_SYNC_RESP_ROOT: Server responds with its root hash
748
+ */
749
+ declare const ORMapSyncRespRootSchema: z.ZodObject<{
750
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_ROOT">;
751
+ payload: z.ZodObject<{
752
+ mapName: z.ZodString;
753
+ rootHash: z.ZodNumber;
754
+ timestamp: z.ZodObject<{
755
+ millis: z.ZodNumber;
756
+ counter: z.ZodNumber;
757
+ nodeId: z.ZodString;
758
+ }, z.core.$strip>;
759
+ }, z.core.$strip>;
760
+ }, z.core.$strip>;
761
+ /**
762
+ * ORMAP_SYNC_RESP_BUCKETS: Server sends bucket hashes for comparison
763
+ */
764
+ declare const ORMapSyncRespBucketsSchema: z.ZodObject<{
765
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_BUCKETS">;
766
+ payload: z.ZodObject<{
767
+ mapName: z.ZodString;
768
+ path: z.ZodString;
769
+ buckets: z.ZodRecord<z.ZodString, z.ZodNumber>;
770
+ }, z.core.$strip>;
771
+ }, z.core.$strip>;
772
+ /**
773
+ * ORMAP_MERKLE_REQ_BUCKET: Client requests bucket details
774
+ */
775
+ declare const ORMapMerkleReqBucketSchema: z.ZodObject<{
776
+ type: z.ZodLiteral<"ORMAP_MERKLE_REQ_BUCKET">;
777
+ payload: z.ZodObject<{
778
+ mapName: z.ZodString;
779
+ path: z.ZodString;
780
+ }, z.core.$strip>;
781
+ }, z.core.$strip>;
782
+ /**
783
+ * ORMAP_SYNC_RESP_LEAF: Server sends actual records for differing keys
784
+ */
785
+ declare const ORMapSyncRespLeafSchema: z.ZodObject<{
786
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_LEAF">;
787
+ payload: z.ZodObject<{
788
+ mapName: z.ZodString;
789
+ path: z.ZodString;
790
+ entries: z.ZodArray<z.ZodObject<{
791
+ key: z.ZodString;
792
+ records: z.ZodArray<z.ZodObject<{
793
+ value: z.ZodAny;
794
+ timestamp: z.ZodObject<{
795
+ millis: z.ZodNumber;
796
+ counter: z.ZodNumber;
797
+ nodeId: z.ZodString;
798
+ }, z.core.$strip>;
799
+ tag: z.ZodString;
800
+ ttlMs: z.ZodOptional<z.ZodNumber>;
801
+ }, z.core.$strip>>;
802
+ tombstones: z.ZodArray<z.ZodString>;
803
+ }, z.core.$strip>>;
804
+ }, z.core.$strip>;
805
+ }, z.core.$strip>;
806
+ /**
807
+ * ORMAP_DIFF_REQUEST: Client requests data for specific keys
808
+ */
809
+ declare const ORMapDiffRequestSchema: z.ZodObject<{
810
+ type: z.ZodLiteral<"ORMAP_DIFF_REQUEST">;
811
+ payload: z.ZodObject<{
812
+ mapName: z.ZodString;
813
+ keys: z.ZodArray<z.ZodString>;
814
+ }, z.core.$strip>;
815
+ }, z.core.$strip>;
816
+ /**
817
+ * ORMAP_DIFF_RESPONSE: Server responds with data for requested keys
818
+ */
819
+ declare const ORMapDiffResponseSchema: z.ZodObject<{
820
+ type: z.ZodLiteral<"ORMAP_DIFF_RESPONSE">;
821
+ payload: z.ZodObject<{
822
+ mapName: z.ZodString;
823
+ entries: z.ZodArray<z.ZodObject<{
824
+ key: z.ZodString;
825
+ records: z.ZodArray<z.ZodObject<{
826
+ value: z.ZodAny;
827
+ timestamp: z.ZodObject<{
828
+ millis: z.ZodNumber;
829
+ counter: z.ZodNumber;
830
+ nodeId: z.ZodString;
831
+ }, z.core.$strip>;
832
+ tag: z.ZodString;
833
+ ttlMs: z.ZodOptional<z.ZodNumber>;
834
+ }, z.core.$strip>>;
835
+ tombstones: z.ZodArray<z.ZodString>;
836
+ }, z.core.$strip>>;
837
+ }, z.core.$strip>;
838
+ }, z.core.$strip>;
839
+ /**
840
+ * ORMAP_PUSH_DIFF: Client pushes local diffs to server
841
+ */
842
+ declare const ORMapPushDiffSchema: z.ZodObject<{
843
+ type: z.ZodLiteral<"ORMAP_PUSH_DIFF">;
844
+ payload: z.ZodObject<{
845
+ mapName: z.ZodString;
846
+ entries: z.ZodArray<z.ZodObject<{
847
+ key: z.ZodString;
848
+ records: z.ZodArray<z.ZodObject<{
849
+ value: z.ZodAny;
850
+ timestamp: z.ZodObject<{
851
+ millis: z.ZodNumber;
852
+ counter: z.ZodNumber;
853
+ nodeId: z.ZodString;
854
+ }, z.core.$strip>;
855
+ tag: z.ZodString;
856
+ ttlMs: z.ZodOptional<z.ZodNumber>;
857
+ }, z.core.$strip>>;
858
+ tombstones: z.ZodArray<z.ZodString>;
859
+ }, z.core.$strip>>;
860
+ }, z.core.$strip>;
861
+ }, z.core.$strip>;
562
862
  declare const MessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
563
863
  type: z.ZodLiteral<"AUTH">;
564
864
  token: z.ZodString;
@@ -717,9 +1017,112 @@ declare const MessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
717
1017
  topic: z.ZodString;
718
1018
  data: z.ZodAny;
719
1019
  }, z.core.$strip>;
1020
+ }, z.core.$strip>, z.ZodObject<{
1021
+ type: z.ZodLiteral<"PING">;
1022
+ timestamp: z.ZodNumber;
1023
+ }, z.core.$strip>, z.ZodObject<{
1024
+ type: z.ZodLiteral<"PONG">;
1025
+ timestamp: z.ZodNumber;
1026
+ serverTime: z.ZodNumber;
1027
+ }, z.core.$strip>, z.ZodObject<{
1028
+ type: z.ZodLiteral<"ORMAP_SYNC_INIT">;
1029
+ mapName: z.ZodString;
1030
+ rootHash: z.ZodNumber;
1031
+ bucketHashes: z.ZodRecord<z.ZodString, z.ZodNumber>;
1032
+ lastSyncTimestamp: z.ZodOptional<z.ZodNumber>;
1033
+ }, z.core.$strip>, z.ZodObject<{
1034
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_ROOT">;
1035
+ payload: z.ZodObject<{
1036
+ mapName: z.ZodString;
1037
+ rootHash: z.ZodNumber;
1038
+ timestamp: z.ZodObject<{
1039
+ millis: z.ZodNumber;
1040
+ counter: z.ZodNumber;
1041
+ nodeId: z.ZodString;
1042
+ }, z.core.$strip>;
1043
+ }, z.core.$strip>;
1044
+ }, z.core.$strip>, z.ZodObject<{
1045
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_BUCKETS">;
1046
+ payload: z.ZodObject<{
1047
+ mapName: z.ZodString;
1048
+ path: z.ZodString;
1049
+ buckets: z.ZodRecord<z.ZodString, z.ZodNumber>;
1050
+ }, z.core.$strip>;
1051
+ }, z.core.$strip>, z.ZodObject<{
1052
+ type: z.ZodLiteral<"ORMAP_MERKLE_REQ_BUCKET">;
1053
+ payload: z.ZodObject<{
1054
+ mapName: z.ZodString;
1055
+ path: z.ZodString;
1056
+ }, z.core.$strip>;
1057
+ }, z.core.$strip>, z.ZodObject<{
1058
+ type: z.ZodLiteral<"ORMAP_SYNC_RESP_LEAF">;
1059
+ payload: z.ZodObject<{
1060
+ mapName: z.ZodString;
1061
+ path: z.ZodString;
1062
+ entries: z.ZodArray<z.ZodObject<{
1063
+ key: z.ZodString;
1064
+ records: z.ZodArray<z.ZodObject<{
1065
+ value: z.ZodAny;
1066
+ timestamp: z.ZodObject<{
1067
+ millis: z.ZodNumber;
1068
+ counter: z.ZodNumber;
1069
+ nodeId: z.ZodString;
1070
+ }, z.core.$strip>;
1071
+ tag: z.ZodString;
1072
+ ttlMs: z.ZodOptional<z.ZodNumber>;
1073
+ }, z.core.$strip>>;
1074
+ tombstones: z.ZodArray<z.ZodString>;
1075
+ }, z.core.$strip>>;
1076
+ }, z.core.$strip>;
1077
+ }, z.core.$strip>, z.ZodObject<{
1078
+ type: z.ZodLiteral<"ORMAP_DIFF_REQUEST">;
1079
+ payload: z.ZodObject<{
1080
+ mapName: z.ZodString;
1081
+ keys: z.ZodArray<z.ZodString>;
1082
+ }, z.core.$strip>;
1083
+ }, z.core.$strip>, z.ZodObject<{
1084
+ type: z.ZodLiteral<"ORMAP_DIFF_RESPONSE">;
1085
+ payload: z.ZodObject<{
1086
+ mapName: z.ZodString;
1087
+ entries: z.ZodArray<z.ZodObject<{
1088
+ key: z.ZodString;
1089
+ records: z.ZodArray<z.ZodObject<{
1090
+ value: z.ZodAny;
1091
+ timestamp: z.ZodObject<{
1092
+ millis: z.ZodNumber;
1093
+ counter: z.ZodNumber;
1094
+ nodeId: z.ZodString;
1095
+ }, z.core.$strip>;
1096
+ tag: z.ZodString;
1097
+ ttlMs: z.ZodOptional<z.ZodNumber>;
1098
+ }, z.core.$strip>>;
1099
+ tombstones: z.ZodArray<z.ZodString>;
1100
+ }, z.core.$strip>>;
1101
+ }, z.core.$strip>;
1102
+ }, z.core.$strip>, z.ZodObject<{
1103
+ type: z.ZodLiteral<"ORMAP_PUSH_DIFF">;
1104
+ payload: z.ZodObject<{
1105
+ mapName: z.ZodString;
1106
+ entries: z.ZodArray<z.ZodObject<{
1107
+ key: z.ZodString;
1108
+ records: z.ZodArray<z.ZodObject<{
1109
+ value: z.ZodAny;
1110
+ timestamp: z.ZodObject<{
1111
+ millis: z.ZodNumber;
1112
+ counter: z.ZodNumber;
1113
+ nodeId: z.ZodString;
1114
+ }, z.core.$strip>;
1115
+ tag: z.ZodString;
1116
+ ttlMs: z.ZodOptional<z.ZodNumber>;
1117
+ }, z.core.$strip>>;
1118
+ tombstones: z.ZodArray<z.ZodString>;
1119
+ }, z.core.$strip>>;
1120
+ }, z.core.$strip>;
720
1121
  }, z.core.$strip>], "type">;
721
1122
  type Query = z.infer<typeof QuerySchema>;
722
1123
  type ClientOp = z.infer<typeof ClientOpSchema>;
723
1124
  type Message = z.infer<typeof MessageSchema>;
1125
+ type PingMessage = z.infer<typeof PingMessageSchema>;
1126
+ type PongMessage = z.infer<typeof PongMessageSchema>;
724
1127
 
725
- export { AuthMessageSchema, type ClientOp, ClientOpMessageSchema, ClientOpSchema, HLC, LWWMap, type LWWRecord, LWWRecordSchema, LockReleaseSchema, LockRequestSchema, MerkleReqBucketMessageSchema, MerkleTree, type Message, MessageSchema, ORMap, type ORMapRecord, ORMapRecordSchema, OpBatchMessageSchema, type PermissionPolicy, type PermissionType, type PredicateNode, PredicateNodeSchema, type PredicateOp, PredicateOpSchema, Predicates, type Principal, type Query, QuerySchema, QuerySubMessageSchema, QueryUnsubMessageSchema, SyncInitMessageSchema, SyncRespBucketsMessageSchema, SyncRespLeafMessageSchema, SyncRespRootMessageSchema, type Timestamp, TimestampSchema, TopicMessageEventSchema, TopicPubSchema, TopicSubSchema, TopicUnsubSchema, combineHashes, deserialize, evaluatePredicate, hashString, serialize };
1128
+ export { AuthMessageSchema, type ClientOp, ClientOpMessageSchema, ClientOpSchema, HLC, LWWMap, type LWWRecord, LWWRecordSchema, LockReleaseSchema, LockRequestSchema, type MergeKeyResult, MerkleReqBucketMessageSchema, MerkleTree, type Message, MessageSchema, ORMap, ORMapDiffRequestSchema, ORMapDiffResponseSchema, type ORMapMerkleNode, ORMapMerkleReqBucketSchema, ORMapMerkleTree, ORMapPushDiffSchema, type ORMapRecord, ORMapRecordSchema, type ORMapSnapshot, ORMapSyncInitSchema, ORMapSyncRespBucketsSchema, ORMapSyncRespLeafSchema, ORMapSyncRespRootSchema, OpBatchMessageSchema, type PermissionPolicy, type PermissionType, type PingMessage, PingMessageSchema, type PongMessage, PongMessageSchema, type PredicateNode, PredicateNodeSchema, type PredicateOp, PredicateOpSchema, Predicates, type Principal, type Query, QuerySchema, QuerySubMessageSchema, QueryUnsubMessageSchema, SyncInitMessageSchema, SyncRespBucketsMessageSchema, SyncRespLeafMessageSchema, SyncRespRootMessageSchema, type Timestamp, TimestampSchema, TopicMessageEventSchema, TopicPubSchema, TopicSubSchema, TopicUnsubSchema, combineHashes, compareTimestamps, deserialize, evaluatePredicate, hashORMapEntry, hashORMapRecord, hashString, serialize, timestampToString };