@quereus/plugin-sync 0.3.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.
Files changed (78) hide show
  1. package/README.md +154 -0
  2. package/dist/src/clock/hlc.d.ts +105 -0
  3. package/dist/src/clock/hlc.d.ts.map +1 -0
  4. package/dist/src/clock/hlc.js +251 -0
  5. package/dist/src/clock/hlc.js.map +1 -0
  6. package/dist/src/clock/index.d.ts +6 -0
  7. package/dist/src/clock/index.d.ts.map +1 -0
  8. package/dist/src/clock/index.js +6 -0
  9. package/dist/src/clock/index.js.map +1 -0
  10. package/dist/src/clock/site.d.ts +58 -0
  11. package/dist/src/clock/site.d.ts.map +1 -0
  12. package/dist/src/clock/site.js +137 -0
  13. package/dist/src/clock/site.js.map +1 -0
  14. package/dist/src/create-sync-module.d.ts +85 -0
  15. package/dist/src/create-sync-module.d.ts.map +1 -0
  16. package/dist/src/create-sync-module.js +54 -0
  17. package/dist/src/create-sync-module.js.map +1 -0
  18. package/dist/src/index.d.ts +31 -0
  19. package/dist/src/index.d.ts.map +1 -0
  20. package/dist/src/index.js +42 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/metadata/change-log.d.ts +67 -0
  23. package/dist/src/metadata/change-log.d.ts.map +1 -0
  24. package/dist/src/metadata/change-log.js +107 -0
  25. package/dist/src/metadata/change-log.js.map +1 -0
  26. package/dist/src/metadata/column-version.d.ts +58 -0
  27. package/dist/src/metadata/column-version.d.ts.map +1 -0
  28. package/dist/src/metadata/column-version.js +100 -0
  29. package/dist/src/metadata/column-version.js.map +1 -0
  30. package/dist/src/metadata/index.d.ts +11 -0
  31. package/dist/src/metadata/index.d.ts.map +1 -0
  32. package/dist/src/metadata/index.js +11 -0
  33. package/dist/src/metadata/index.js.map +1 -0
  34. package/dist/src/metadata/keys.d.ts +180 -0
  35. package/dist/src/metadata/keys.d.ts.map +1 -0
  36. package/dist/src/metadata/keys.js +390 -0
  37. package/dist/src/metadata/keys.js.map +1 -0
  38. package/dist/src/metadata/peer-state.d.ts +52 -0
  39. package/dist/src/metadata/peer-state.d.ts.map +1 -0
  40. package/dist/src/metadata/peer-state.js +87 -0
  41. package/dist/src/metadata/peer-state.js.map +1 -0
  42. package/dist/src/metadata/schema-migration.d.ts +60 -0
  43. package/dist/src/metadata/schema-migration.d.ts.map +1 -0
  44. package/dist/src/metadata/schema-migration.js +126 -0
  45. package/dist/src/metadata/schema-migration.js.map +1 -0
  46. package/dist/src/metadata/schema-version.d.ts +163 -0
  47. package/dist/src/metadata/schema-version.d.ts.map +1 -0
  48. package/dist/src/metadata/schema-version.js +307 -0
  49. package/dist/src/metadata/schema-version.js.map +1 -0
  50. package/dist/src/metadata/tombstones.d.ts +67 -0
  51. package/dist/src/metadata/tombstones.d.ts.map +1 -0
  52. package/dist/src/metadata/tombstones.js +125 -0
  53. package/dist/src/metadata/tombstones.js.map +1 -0
  54. package/dist/src/sync/events.d.ts +117 -0
  55. package/dist/src/sync/events.d.ts.map +1 -0
  56. package/dist/src/sync/events.js +56 -0
  57. package/dist/src/sync/events.js.map +1 -0
  58. package/dist/src/sync/index.d.ts +8 -0
  59. package/dist/src/sync/index.d.ts.map +1 -0
  60. package/dist/src/sync/index.js +8 -0
  61. package/dist/src/sync/index.js.map +1 -0
  62. package/dist/src/sync/manager.d.ts +146 -0
  63. package/dist/src/sync/manager.d.ts.map +1 -0
  64. package/dist/src/sync/manager.js +8 -0
  65. package/dist/src/sync/manager.js.map +1 -0
  66. package/dist/src/sync/protocol.d.ts +282 -0
  67. package/dist/src/sync/protocol.d.ts.map +1 -0
  68. package/dist/src/sync/protocol.js +16 -0
  69. package/dist/src/sync/protocol.js.map +1 -0
  70. package/dist/src/sync/store-adapter.d.ts +42 -0
  71. package/dist/src/sync/store-adapter.d.ts.map +1 -0
  72. package/dist/src/sync/store-adapter.js +232 -0
  73. package/dist/src/sync/store-adapter.js.map +1 -0
  74. package/dist/src/sync/sync-manager-impl.d.ts +91 -0
  75. package/dist/src/sync/sync-manager-impl.d.ts.map +1 -0
  76. package/dist/src/sync/sync-manager-impl.js +1123 -0
  77. package/dist/src/sync/sync-manager-impl.js.map +1 -0
  78. package/package.json +64 -0
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @quereus/plugin-sync
2
+
3
+ CRDT-based multi-master sync plugin for [Quereus](https://github.com/gotchoices/quereus). Enables offline-first applications with automatic conflict resolution.
4
+
5
+ ## Features
6
+
7
+ - **Multi-master replication**: Any replica can write, changes merge automatically
8
+ - **Column-level LWW**: Last-Write-Wins at the column level for fine-grained conflict resolution
9
+ - **Hybrid Logical Clocks**: Causally-ordered timestamps that work offline
10
+ - **Transport agnostic**: Bring your own WebSocket, HTTP, or WebRTC transport
11
+ - **Offline-first**: Local changes sync when connectivity returns
12
+ - **Schema sync**: DDL changes (CREATE TABLE, ALTER TABLE) propagate across replicas
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @quereus/plugin-sync @quereus/plugin-store
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { Database } from '@quereus/quereus';
24
+ import { StoreEventEmitter, LevelDBStore } from '@quereus/plugin-store';
25
+ import { createSyncModule, createStoreAdapter } from '@quereus/plugin-sync';
26
+
27
+ // Create the store with event emitter
28
+ const storeEvents = new StoreEventEmitter();
29
+ const store = await LevelDBStore.open({ path: './data' });
30
+
31
+ // Create sync-enabled module
32
+ const { syncModule, syncManager, syncEvents } = createSyncModule(store, storeEvents);
33
+
34
+ // Register with database
35
+ const db = new Database();
36
+ db.registerVtabModule('store', syncModule);
37
+
38
+ // Create tables and use normally - all changes are tracked
39
+ await db.exec(`
40
+ CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)
41
+ USING store
42
+ `);
43
+
44
+ await db.exec(`INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.com')`);
45
+
46
+ // Get changes to send to another replica
47
+ const changes = await syncManager.getChangesSince(peerSiteId);
48
+ ```
49
+
50
+ ## Architecture
51
+
52
+ ```
53
+ ┌─────────────────────────────────────────────────────────────┐
54
+ │ Application │
55
+ ├─────────────────────────────────────────────────────────────┤
56
+ │ SyncManager │
57
+ │ ├── HLCManager (Hybrid Logical Clock) │
58
+ │ ├── ColumnVersionStore (LWW metadata) │
59
+ │ ├── TombstoneStore (deletion tracking) │
60
+ │ ├── ChangeLogStore (HLC-indexed changes) │
61
+ │ └── PeerStateStore (delta sync state) │
62
+ ├─────────────────────────────────────────────────────────────┤
63
+ │ @quereus/plugin-store (KVStore) │
64
+ └─────────────────────────────────────────────────────────────┘
65
+ ```
66
+
67
+ ## Sync Protocol
68
+
69
+ ### Delta Sync
70
+
71
+ When replicas have synced before:
72
+
73
+ ```typescript
74
+ // Get changes since last sync
75
+ const changes = await syncManager.getChangesSince(peerSiteId);
76
+
77
+ // Apply received changes
78
+ const result = await syncManager.applyChanges(changeSets, applyToStoreCallback);
79
+ ```
80
+
81
+ ### Snapshot Sync
82
+
83
+ For new replicas or when delta sync isn't available:
84
+
85
+ ```typescript
86
+ // Stream snapshot chunks
87
+ for await (const chunk of syncManager.streamSnapshot({ chunkSize: 1000 })) {
88
+ sendToPeer(chunk);
89
+ }
90
+
91
+ // Apply received snapshot
92
+ await syncManager.applyStreamedSnapshot(chunks, applyToStoreCallback);
93
+ ```
94
+
95
+ ## Events
96
+
97
+ Subscribe to sync events for UI updates:
98
+
99
+ ```typescript
100
+ syncEvents.onLocalChange((event) => {
101
+ console.log('Local change:', event.tableName, event.type);
102
+ });
103
+
104
+ syncEvents.onRemoteChange((event) => {
105
+ console.log('Remote change:', event.tableName, event.type);
106
+ refreshUI();
107
+ });
108
+
109
+ syncEvents.onConflict((event) => {
110
+ console.log('Conflict resolved:', event.resolution);
111
+ });
112
+ ```
113
+
114
+ ## Conflict Resolution
115
+
116
+ Conflicts are resolved automatically using Last-Write-Wins at the column level:
117
+
118
+ - Each column has an associated HLC timestamp
119
+ - When merging, the column with the higher HLC wins
120
+ - Ties are broken by site ID (deterministic ordering)
121
+
122
+ This means concurrent updates to *different* columns of the same row both apply, while updates to the *same* column use the latest value.
123
+
124
+ ## API
125
+
126
+ ### Core Exports
127
+
128
+ - `createSyncModule(store, storeEvents, config?)` - Factory to create sync-enabled store module
129
+ - `createStoreAdapter(db, store, storeEvents)` - Adapter for applying remote changes to store
130
+ - `SyncManager` - Main sync coordination interface
131
+ - `SyncEventEmitter` - Event subscription interface
132
+
133
+ ### Clock Exports
134
+
135
+ - `HLCManager` - Hybrid Logical Clock manager
136
+ - `generateSiteId()` - Generate unique 16-byte site identifier
137
+ - `siteIdToBase64(id)` / `siteIdFromBase64(str)` - Site ID serialization
138
+
139
+ ### Protocol Types
140
+
141
+ - `ChangeSet` - Collection of changes from one transaction
142
+ - `Change` - Single column or row change
143
+ - `SchemaMigration` - Schema change (CREATE/ALTER/DROP TABLE)
144
+ - `SnapshotChunk` - Streaming snapshot data
145
+
146
+ ## Related Packages
147
+
148
+ - [`@quereus/plugin-store`](../quereus-plugin-store/) - Storage layer (required)
149
+ - [`@quereus/sync-coordinator`](../sync-coordinator/) - Server-side coordinator
150
+
151
+ ## License
152
+
153
+ MIT
154
+
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Hybrid Logical Clock (HLC) implementation.
3
+ *
4
+ * HLC combines physical time with a logical counter to provide:
5
+ * - Monotonically increasing timestamps
6
+ * - Causality tracking across distributed nodes
7
+ * - Bounded clock drift tolerance
8
+ *
9
+ * Ordering: (wallTime, counter, siteId) compared lexicographically
10
+ */
11
+ import type { SiteId } from './site.js';
12
+ /**
13
+ * Hybrid Logical Clock timestamp.
14
+ */
15
+ export interface HLC {
16
+ /** Physical wall time in milliseconds since epoch */
17
+ readonly wallTime: bigint;
18
+ /** Logical counter for events in the same millisecond (0-65535) */
19
+ readonly counter: number;
20
+ /** 16-byte UUID identifying the replica */
21
+ readonly siteId: SiteId;
22
+ }
23
+ /**
24
+ * Compare two HLCs for ordering.
25
+ * Returns negative if a < b, positive if a > b, zero if equal.
26
+ */
27
+ export declare function compareHLC(a: HLC, b: HLC): number;
28
+ /**
29
+ * Check if two HLCs are equal.
30
+ */
31
+ export declare function hlcEquals(a: HLC, b: HLC): boolean;
32
+ /**
33
+ * Create a new HLC with the given values.
34
+ */
35
+ export declare function createHLC(wallTime: bigint, counter: number, siteId: SiteId): HLC;
36
+ /**
37
+ * Serialize HLC to a Uint8Array for storage.
38
+ * Format: 8 bytes wallTime (BE) + 2 bytes counter (BE) + 16 bytes siteId = 26 bytes
39
+ */
40
+ export declare function serializeHLC(hlc: HLC): Uint8Array;
41
+ /**
42
+ * Deserialize HLC from a Uint8Array.
43
+ */
44
+ export declare function deserializeHLC(buffer: Uint8Array): HLC;
45
+ /**
46
+ * JSON-serializable representation of an HLC.
47
+ * Uses strings for bigint and base64url for binary data.
48
+ */
49
+ export interface SerializedHLC {
50
+ /** Wall time in milliseconds (string to preserve bigint precision) */
51
+ wallTime: string;
52
+ /** Logical counter (0-65535) */
53
+ counter: number;
54
+ /** Site ID as 22-character base64url string */
55
+ siteId: string;
56
+ }
57
+ /**
58
+ * Convert HLC to a JSON-serializable object.
59
+ * Useful for schema seeds, HTTP transport, or debugging.
60
+ */
61
+ export declare function hlcToJson(hlc: HLC): SerializedHLC;
62
+ /**
63
+ * Parse HLC from a JSON-serializable object.
64
+ */
65
+ export declare function hlcFromJson(json: SerializedHLC): HLC;
66
+ /**
67
+ * HLC Manager - maintains clock state for a single replica.
68
+ */
69
+ export declare class HLCManager {
70
+ private wallTime;
71
+ private counter;
72
+ private readonly siteId;
73
+ constructor(siteId: SiteId, initialState?: {
74
+ wallTime: bigint;
75
+ counter: number;
76
+ });
77
+ /**
78
+ * Get the current site ID.
79
+ */
80
+ getSiteId(): SiteId;
81
+ /**
82
+ * Get current clock state (for persistence).
83
+ */
84
+ getState(): {
85
+ wallTime: bigint;
86
+ counter: number;
87
+ };
88
+ /**
89
+ * Generate a new HLC for a local event.
90
+ * Advances the clock and returns the new timestamp.
91
+ */
92
+ tick(): HLC;
93
+ /**
94
+ * Update clock state upon receiving a remote HLC.
95
+ * Ensures our clock is always >= received clock.
96
+ * Returns a new HLC for the local receive event.
97
+ */
98
+ receive(remote: HLC): HLC;
99
+ /**
100
+ * Create an HLC at the current clock state without advancing.
101
+ * Useful for read operations.
102
+ */
103
+ now(): HLC;
104
+ }
105
+ //# sourceMappingURL=hlc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlc.d.ts","sourceRoot":"","sources":["../../../src/clock/hlc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAWjD;AAcD;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAEhF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU,CAcjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,GAAG,CAYtD;AAMD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,CAMjD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,GAAG,CAMpD;AAkDD;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAMhF;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,QAAQ,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAIjD;;;OAGG;IACH,IAAI,IAAI,GAAG;IAoBX;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG;IAuCzB;;;OAGG;IACH,GAAG,IAAI,GAAG;CAGX"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Hybrid Logical Clock (HLC) implementation.
3
+ *
4
+ * HLC combines physical time with a logical counter to provide:
5
+ * - Monotonically increasing timestamps
6
+ * - Causality tracking across distributed nodes
7
+ * - Bounded clock drift tolerance
8
+ *
9
+ * Ordering: (wallTime, counter, siteId) compared lexicographically
10
+ */
11
+ /**
12
+ * Maximum counter value before forcing time advancement.
13
+ */
14
+ const MAX_COUNTER = 0xFFFF;
15
+ /**
16
+ * Maximum allowed clock drift in milliseconds (1 minute).
17
+ * Rejects remote timestamps that are too far in the future.
18
+ */
19
+ const MAX_DRIFT_MS = 60000n;
20
+ /**
21
+ * Compare two HLCs for ordering.
22
+ * Returns negative if a < b, positive if a > b, zero if equal.
23
+ */
24
+ export function compareHLC(a, b) {
25
+ // First compare wall time
26
+ if (a.wallTime < b.wallTime)
27
+ return -1;
28
+ if (a.wallTime > b.wallTime)
29
+ return 1;
30
+ // Same wall time: compare counter
31
+ if (a.counter < b.counter)
32
+ return -1;
33
+ if (a.counter > b.counter)
34
+ return 1;
35
+ // Same counter: compare site ID lexicographically
36
+ return compareSiteIds(a.siteId, b.siteId);
37
+ }
38
+ /**
39
+ * Compare two site IDs lexicographically.
40
+ */
41
+ function compareSiteIds(a, b) {
42
+ const len = Math.min(a.length, b.length);
43
+ for (let i = 0; i < len; i++) {
44
+ if (a[i] < b[i])
45
+ return -1;
46
+ if (a[i] > b[i])
47
+ return 1;
48
+ }
49
+ return a.length - b.length;
50
+ }
51
+ /**
52
+ * Check if two HLCs are equal.
53
+ */
54
+ export function hlcEquals(a, b) {
55
+ return compareHLC(a, b) === 0;
56
+ }
57
+ /**
58
+ * Create a new HLC with the given values.
59
+ */
60
+ export function createHLC(wallTime, counter, siteId) {
61
+ return Object.freeze({ wallTime, counter, siteId });
62
+ }
63
+ /**
64
+ * Serialize HLC to a Uint8Array for storage.
65
+ * Format: 8 bytes wallTime (BE) + 2 bytes counter (BE) + 16 bytes siteId = 26 bytes
66
+ */
67
+ export function serializeHLC(hlc) {
68
+ const buffer = new Uint8Array(26);
69
+ const view = new DataView(buffer.buffer);
70
+ // Wall time as big-endian 64-bit
71
+ view.setBigUint64(0, hlc.wallTime, false);
72
+ // Counter as big-endian 16-bit
73
+ view.setUint16(8, hlc.counter, false);
74
+ // Site ID (16 bytes)
75
+ buffer.set(hlc.siteId, 10);
76
+ return buffer;
77
+ }
78
+ /**
79
+ * Deserialize HLC from a Uint8Array.
80
+ */
81
+ export function deserializeHLC(buffer) {
82
+ if (buffer.length !== 26) {
83
+ throw new Error(`Invalid HLC buffer length: ${buffer.length}, expected 26`);
84
+ }
85
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
86
+ const wallTime = view.getBigUint64(0, false);
87
+ const counter = view.getUint16(8, false);
88
+ const siteId = new Uint8Array(buffer.slice(10, 26));
89
+ return createHLC(wallTime, counter, siteId);
90
+ }
91
+ /**
92
+ * Convert HLC to a JSON-serializable object.
93
+ * Useful for schema seeds, HTTP transport, or debugging.
94
+ */
95
+ export function hlcToJson(hlc) {
96
+ return {
97
+ wallTime: hlc.wallTime.toString(),
98
+ counter: hlc.counter,
99
+ siteId: siteIdToBase64Local(hlc.siteId),
100
+ };
101
+ }
102
+ /**
103
+ * Parse HLC from a JSON-serializable object.
104
+ */
105
+ export function hlcFromJson(json) {
106
+ return createHLC(BigInt(json.wallTime), json.counter, siteIdFromBase64Local(json.siteId));
107
+ }
108
+ // Base64url alphabet (RFC 4648 Section 5)
109
+ const BASE64URL_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
110
+ /**
111
+ * Convert site ID to base64url string (local helper to avoid circular import).
112
+ */
113
+ function siteIdToBase64Local(siteId) {
114
+ let result = '';
115
+ for (let i = 0; i < siteId.length; i += 3) {
116
+ const byte1 = siteId[i];
117
+ const byte2 = i + 1 < siteId.length ? siteId[i + 1] : 0;
118
+ const byte3 = i + 2 < siteId.length ? siteId[i + 2] : 0;
119
+ const triplet = (byte1 << 16) | (byte2 << 8) | byte3;
120
+ result += BASE64URL_CHARS[(triplet >>> 18) & 0x3f];
121
+ result += BASE64URL_CHARS[(triplet >>> 12) & 0x3f];
122
+ if (i + 1 < siteId.length)
123
+ result += BASE64URL_CHARS[(triplet >>> 6) & 0x3f];
124
+ if (i + 2 < siteId.length)
125
+ result += BASE64URL_CHARS[triplet & 0x3f];
126
+ }
127
+ return result;
128
+ }
129
+ /**
130
+ * Parse site ID from base64url string (local helper to avoid circular import).
131
+ */
132
+ function siteIdFromBase64Local(base64) {
133
+ if (base64.length !== 22) {
134
+ throw new Error(`Invalid site ID base64 length: ${base64.length}, expected 22`);
135
+ }
136
+ // Build reverse lookup table
137
+ const lookup = {};
138
+ for (let i = 0; i < BASE64URL_CHARS.length; i++) {
139
+ lookup[BASE64URL_CHARS[i]] = i;
140
+ }
141
+ const result = new Uint8Array(16);
142
+ let writePos = 0;
143
+ for (let i = 0; i < base64.length; i += 4) {
144
+ const c1 = lookup[base64[i]] ?? 0;
145
+ const c2 = lookup[base64[i + 1]] ?? 0;
146
+ const c3 = i + 2 < base64.length ? lookup[base64[i + 2]] ?? 0 : 0;
147
+ const c4 = i + 3 < base64.length ? lookup[base64[i + 3]] ?? 0 : 0;
148
+ const triplet = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
149
+ if (writePos < 16)
150
+ result[writePos++] = (triplet >>> 16) & 0xff;
151
+ if (writePos < 16)
152
+ result[writePos++] = (triplet >>> 8) & 0xff;
153
+ if (writePos < 16)
154
+ result[writePos++] = triplet & 0xff;
155
+ }
156
+ return result;
157
+ }
158
+ /**
159
+ * HLC Manager - maintains clock state for a single replica.
160
+ */
161
+ export class HLCManager {
162
+ wallTime;
163
+ counter;
164
+ siteId;
165
+ constructor(siteId, initialState) {
166
+ this.siteId = siteId;
167
+ this.wallTime = initialState?.wallTime ?? 0n;
168
+ this.counter = initialState?.counter ?? 0;
169
+ }
170
+ /**
171
+ * Get the current site ID.
172
+ */
173
+ getSiteId() {
174
+ return this.siteId;
175
+ }
176
+ /**
177
+ * Get current clock state (for persistence).
178
+ */
179
+ getState() {
180
+ return { wallTime: this.wallTime, counter: this.counter };
181
+ }
182
+ /**
183
+ * Generate a new HLC for a local event.
184
+ * Advances the clock and returns the new timestamp.
185
+ */
186
+ tick() {
187
+ const now = BigInt(Date.now());
188
+ if (now > this.wallTime) {
189
+ // Physical time has advanced
190
+ this.wallTime = now;
191
+ this.counter = 0;
192
+ }
193
+ else {
194
+ // Same or earlier physical time, increment counter
195
+ this.counter++;
196
+ if (this.counter > MAX_COUNTER) {
197
+ // Counter overflow: force time advancement
198
+ this.wallTime++;
199
+ this.counter = 0;
200
+ }
201
+ }
202
+ return createHLC(this.wallTime, this.counter, this.siteId);
203
+ }
204
+ /**
205
+ * Update clock state upon receiving a remote HLC.
206
+ * Ensures our clock is always >= received clock.
207
+ * Returns a new HLC for the local receive event.
208
+ */
209
+ receive(remote) {
210
+ const now = BigInt(Date.now());
211
+ // Check for excessive drift
212
+ if (remote.wallTime > now + MAX_DRIFT_MS) {
213
+ throw new Error(`Remote clock too far in future: ${remote.wallTime - now}ms ahead (max ${MAX_DRIFT_MS}ms)`);
214
+ }
215
+ // Merge: take max of local, remote, and now
216
+ const maxWall = now > this.wallTime
217
+ ? (now > remote.wallTime ? now : remote.wallTime)
218
+ : (this.wallTime > remote.wallTime ? this.wallTime : remote.wallTime);
219
+ if (maxWall === this.wallTime && maxWall === remote.wallTime) {
220
+ // All three are equal: take max counter + 1
221
+ this.counter = Math.max(this.counter, remote.counter) + 1;
222
+ }
223
+ else if (maxWall === this.wallTime) {
224
+ // Local wins: increment local counter
225
+ this.counter++;
226
+ }
227
+ else if (maxWall === remote.wallTime) {
228
+ // Remote wins: take remote counter + 1
229
+ this.wallTime = remote.wallTime;
230
+ this.counter = remote.counter + 1;
231
+ }
232
+ else {
233
+ // Physical time wins: reset counter
234
+ this.wallTime = maxWall;
235
+ this.counter = 0;
236
+ }
237
+ if (this.counter > MAX_COUNTER) {
238
+ this.wallTime++;
239
+ this.counter = 0;
240
+ }
241
+ return createHLC(this.wallTime, this.counter, this.siteId);
242
+ }
243
+ /**
244
+ * Create an HLC at the current clock state without advancing.
245
+ * Useful for read operations.
246
+ */
247
+ now() {
248
+ return createHLC(this.wallTime, this.counter, this.siteId);
249
+ }
250
+ }
251
+ //# sourceMappingURL=hlc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hlc.js","sourceRoot":"","sources":["../../../src/clock/hlc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH;;GAEG;AACH,MAAM,WAAW,GAAG,MAAM,CAAC;AAE3B;;;GAGG;AACH,MAAM,YAAY,GAAG,MAAO,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,CAAM,EAAE,CAAM;IACvC,0BAA0B;IAC1B,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IAEtC,kCAAkC;IAClC,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IAEpC,kDAAkD;IAClD,OAAO,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,CAAS,EAAE,CAAS;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,CAAM,EAAE,CAAM;IACtC,OAAO,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,OAAe,EAAE,MAAc;IACzE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ;IACnC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzC,iCAAiC;IACjC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE1C,+BAA+B;IAC/B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEtC,qBAAqB;IACrB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,CAAC,MAAM,eAAe,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAE/E,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAEpD,OAAO,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAmBD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,GAAQ;IAChC,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE;QACjC,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC;KACxC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,IAAmB;IAC7C,OAAO,SAAS,CACd,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EACrB,IAAI,CAAC,OAAO,EACZ,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,CACnC,CAAC;AACJ,CAAC;AAED,0CAA0C;AAC1C,MAAM,eAAe,GAAG,kEAAkE,CAAC;AAE3F;;GAEG;AACH,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC;QACrD,MAAM,IAAI,eAAe,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACnD,MAAM,IAAI,eAAe,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM;YAAE,MAAM,IAAI,eAAe,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC7E,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM;YAAE,MAAM,IAAI,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,MAAc;IAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,kCAAkC,MAAM,CAAC,MAAM,eAAe,CAAC,CAAC;IAClF,CAAC;IACD,6BAA6B;IAC7B,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACzD,IAAI,QAAQ,GAAG,EAAE;YAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;QAChE,IAAI,QAAQ,GAAG,EAAE;YAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;QAC/D,IAAI,QAAQ,GAAG,EAAE;YAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,UAAU;IACb,QAAQ,CAAS;IACjB,OAAO,CAAS;IACP,MAAM,CAAS;IAEhC,YAAY,MAAc,EAAE,YAAoD;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,YAAY,EAAE,QAAQ,IAAI,EAAE,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAC5D,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAE/B,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,6BAA6B;YAC7B,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;YACpB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,mDAAmD;YACnD,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC/B,2CAA2C;gBAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,MAAW;QACjB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAE/B,4BAA4B;QAC5B,IAAI,MAAM,CAAC,QAAQ,GAAG,GAAG,GAAG,YAAY,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CACb,mCAAmC,MAAM,CAAC,QAAQ,GAAG,GAAG,iBAAiB,YAAY,KAAK,CAC3F,CAAC;QACJ,CAAC;QAED,4CAA4C;QAC5C,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ;YACjC,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;YACjD,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAExE,IAAI,OAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC7D,4CAA4C;YAC5C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,OAAO,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,sCAAsC;YACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;aAAM,IAAI,OAAO,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;YACvC,uCAAuC;YACvC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YAChC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,oCAAoC;YACpC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;YACxB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,GAAG,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED;;;OAGG;IACH,GAAG;QACD,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Clock module exports.
3
+ */
4
+ export * from './hlc.js';
5
+ export * from './site.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/clock/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Clock module exports.
3
+ */
4
+ export * from './hlc.js';
5
+ export * from './site.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/clock/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Site ID management - unique identifier for each replica.
3
+ *
4
+ * Site IDs are 16-byte UUIDs that uniquely identify a replica in the
5
+ * distributed system. They are used for:
6
+ * - Breaking ties in HLC comparison
7
+ * - Tracking which changes came from which replica
8
+ * - Peer-to-peer sync state tracking
9
+ */
10
+ /**
11
+ * 16-byte unique identifier for a replica.
12
+ */
13
+ export type SiteId = Uint8Array;
14
+ /**
15
+ * Generate a new random site ID (UUID v4).
16
+ */
17
+ export declare function generateSiteId(): SiteId;
18
+ /**
19
+ * Convert a Uint8Array to base64url encoding (no padding).
20
+ */
21
+ export declare function toBase64Url(bytes: Uint8Array): string;
22
+ /**
23
+ * Convert a base64url string to Uint8Array.
24
+ */
25
+ export declare function fromBase64Url(str: string): Uint8Array;
26
+ /**
27
+ * Convert site ID to base64url string for serialization.
28
+ * 16 bytes → 22 characters (no padding).
29
+ */
30
+ export declare function siteIdToBase64(siteId: SiteId): string;
31
+ /**
32
+ * Parse site ID from base64url string.
33
+ */
34
+ export declare function siteIdFromBase64(base64: string): SiteId;
35
+ /**
36
+ * Compare two site IDs for equality.
37
+ */
38
+ export declare function siteIdEquals(a: SiteId, b: SiteId): boolean;
39
+ /**
40
+ * Storage key for site identity.
41
+ */
42
+ export declare const SITE_ID_KEY = "si:";
43
+ /**
44
+ * Site identity record stored in the KV store.
45
+ */
46
+ export interface SiteIdentity {
47
+ siteId: SiteId;
48
+ createdAt: number;
49
+ }
50
+ /**
51
+ * Serialize site identity for storage.
52
+ */
53
+ export declare function serializeSiteIdentity(identity: SiteIdentity): Uint8Array;
54
+ /**
55
+ * Deserialize site identity from storage.
56
+ */
57
+ export declare function deserializeSiteIdentity(buffer: Uint8Array): SiteIdentity;
58
+ //# sourceMappingURL=site.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../../src/clock/site.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC;AAEhC;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAmBvC;AAKD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAmBrD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CA2BrD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAM1D;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,QAAQ,CAAC;AAEjC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,UAAU,CAQxE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAUxE"}