@framers/sql-storage-adapter 0.7.1 → 0.7.3
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 +43 -0
- package/dist/adapters/electron/preload.cjs +289 -0
- package/dist/adapters/electron/renderer.d.ts +9 -0
- package/dist/adapters/electron/renderer.d.ts.map +1 -0
- package/dist/adapters/electron/renderer.js +8 -0
- package/dist/adapters/electron/renderer.js.map +1 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.d.ts +2 -0
- package/dist/cjs/adapters/__tests__/indexedDbAdapter.spec.js +133 -0
- package/dist/cjs/adapters/baseStorageAdapter.d.ts +368 -0
- package/dist/cjs/adapters/baseStorageAdapter.js +744 -0
- package/dist/cjs/adapters/betterSqliteAdapter.d.ts +124 -0
- package/dist/cjs/adapters/betterSqliteAdapter.js +334 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.d.ts +83 -0
- package/dist/cjs/adapters/capacitorSqliteAdapter.js +250 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.d.ts +251 -0
- package/dist/cjs/adapters/electron/electronMainAdapter.js +392 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.d.ts +177 -0
- package/dist/cjs/adapters/electron/electronRendererAdapter.js +283 -0
- package/dist/cjs/adapters/electron/index.d.ts +74 -0
- package/dist/cjs/adapters/electron/index.js +86 -0
- package/dist/cjs/adapters/electron/ipc/channels.d.ts +196 -0
- package/dist/cjs/adapters/electron/ipc/channels.js +127 -0
- package/dist/cjs/adapters/electron/ipc/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/ipc/index.js +38 -0
- package/dist/cjs/adapters/electron/ipc/protocol.d.ts +78 -0
- package/dist/cjs/adapters/electron/ipc/protocol.js +341 -0
- package/dist/cjs/adapters/electron/ipc/types.d.ts +248 -0
- package/dist/cjs/adapters/electron/ipc/types.js +15 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.d.ts +184 -0
- package/dist/cjs/adapters/electron/migration/autoMigrator.js +441 -0
- package/dist/cjs/adapters/electron/migration/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/migration/index.js +29 -0
- package/dist/cjs/adapters/electron/preload.d.ts +126 -0
- package/dist/cjs/adapters/electron/preload.js +233 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.d.ts +214 -0
- package/dist/cjs/adapters/electron/recovery/corruptionDetector.js +359 -0
- package/dist/cjs/adapters/electron/recovery/index.d.ts +11 -0
- package/dist/cjs/adapters/electron/recovery/index.js +34 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.d.ts +186 -0
- package/dist/cjs/adapters/electron/recovery/walCheckpoint.js +263 -0
- package/dist/cjs/adapters/electron/renderer.d.ts +9 -0
- package/dist/cjs/adapters/electron/renderer.js +33 -0
- package/dist/cjs/adapters/electron/window/index.d.ts +9 -0
- package/dist/cjs/adapters/electron/window/index.js +29 -0
- package/dist/cjs/adapters/electron/window/windowManager.d.ts +190 -0
- package/dist/cjs/adapters/electron/window/windowManager.js +321 -0
- package/dist/cjs/adapters/indexedDbAdapter.d.ts +490 -0
- package/dist/cjs/adapters/indexedDbAdapter.js +993 -0
- package/dist/cjs/adapters/postgresAdapter.d.ts +424 -0
- package/dist/cjs/adapters/postgresAdapter.js +889 -0
- package/dist/cjs/adapters/sqlJsAdapter.d.ts +64 -0
- package/dist/cjs/adapters/sqlJsAdapter.js +268 -0
- package/dist/cjs/adapters/supabase.d.ts +58 -0
- package/dist/cjs/adapters/supabase.js +401 -0
- package/dist/cjs/codecs/BrowserBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/BrowserBlobCodec.js +57 -0
- package/dist/cjs/codecs/NodeBlobCodec.d.ts +14 -0
- package/dist/cjs/codecs/NodeBlobCodec.js +59 -0
- package/dist/cjs/core/contracts/blobCodec.d.ts +15 -0
- package/dist/cjs/core/contracts/blobCodec.js +15 -0
- package/dist/cjs/core/contracts/context.d.ts +221 -0
- package/dist/cjs/core/contracts/context.js +15 -0
- package/dist/cjs/core/contracts/dialect.d.ts +154 -0
- package/dist/cjs/core/contracts/dialect.js +15 -0
- package/dist/cjs/core/contracts/events.d.ts +225 -0
- package/dist/cjs/core/contracts/events.js +15 -0
- package/dist/cjs/core/contracts/exporter.d.ts +13 -0
- package/dist/cjs/core/contracts/exporter.js +15 -0
- package/dist/cjs/core/contracts/extensions.d.ts +73 -0
- package/dist/cjs/core/contracts/extensions.js +15 -0
- package/dist/cjs/core/contracts/features.d.ts +25 -0
- package/dist/cjs/core/contracts/features.js +61 -0
- package/dist/cjs/core/contracts/fts.d.ts +66 -0
- package/dist/cjs/core/contracts/fts.js +15 -0
- package/dist/cjs/core/contracts/hooks.d.ts +481 -0
- package/dist/cjs/core/contracts/hooks.js +140 -0
- package/dist/cjs/core/contracts/index.d.ts +260 -0
- package/dist/cjs/core/contracts/index.js +41 -0
- package/dist/cjs/core/contracts/limitations.d.ts +46 -0
- package/dist/cjs/core/contracts/limitations.js +218 -0
- package/dist/cjs/core/contracts/performance.d.ts +308 -0
- package/dist/cjs/core/contracts/performance.js +144 -0
- package/dist/cjs/core/database.d.ts +220 -0
- package/dist/cjs/core/database.js +136 -0
- package/dist/cjs/core/portable.d.ts +23 -0
- package/dist/cjs/core/portable.js +66 -0
- package/dist/cjs/core/resolver.d.ts +77 -0
- package/dist/cjs/core/resolver.js +235 -0
- package/dist/cjs/dialects/PostgresDialect.d.ts +35 -0
- package/dist/cjs/dialects/PostgresDialect.js +143 -0
- package/dist/cjs/dialects/SqliteDialect.d.ts +35 -0
- package/dist/cjs/dialects/SqliteDialect.js +142 -0
- package/dist/cjs/dialects/shared.d.ts +17 -0
- package/dist/cjs/dialects/shared.js +90 -0
- package/dist/cjs/exporters/PostgresExporter.d.ts +14 -0
- package/dist/cjs/exporters/PostgresExporter.js +61 -0
- package/dist/cjs/exporters/SqliteFileExporter.d.ts +16 -0
- package/dist/cjs/exporters/SqliteFileExporter.js +43 -0
- package/dist/cjs/features/backup/cloudBackup.d.ts +219 -0
- package/dist/cjs/features/backup/cloudBackup.js +209 -0
- package/dist/cjs/features/kv/idb.d.ts +79 -0
- package/dist/cjs/features/kv/idb.js +32 -0
- package/dist/cjs/features/kv/index.d.ts +14 -0
- package/dist/cjs/features/kv/index.js +40 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.d.ts +77 -0
- package/dist/cjs/features/kv/indexedDbKeyValueStore.js +116 -0
- package/dist/cjs/features/kv/keyValueStore.d.ts +48 -0
- package/dist/cjs/features/kv/keyValueStore.js +67 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.d.ts +34 -0
- package/dist/cjs/features/kv/sqlKeyValueStore.js +81 -0
- package/dist/cjs/features/kv/valueCodec.d.ts +35 -0
- package/dist/cjs/features/kv/valueCodec.js +426 -0
- package/dist/cjs/features/migrations/dataExport.d.ts +77 -0
- package/dist/cjs/features/migrations/dataExport.js +194 -0
- package/dist/cjs/features/migrations/dataImport.d.ts +54 -0
- package/dist/cjs/features/migrations/dataImport.js +300 -0
- package/dist/cjs/features/migrations/migration.d.ts +89 -0
- package/dist/cjs/features/migrations/migration.js +187 -0
- package/dist/cjs/features/migrations/schemaMigrations.d.ts +93 -0
- package/dist/cjs/features/migrations/schemaMigrations.js +220 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.d.ts +222 -0
- package/dist/cjs/features/sync/conflicts/conflictResolver.js +366 -0
- package/dist/cjs/features/sync/conflicts/index.d.ts +9 -0
- package/dist/cjs/features/sync/conflicts/index.js +21 -0
- package/dist/cjs/features/sync/crossPlatformSync.d.ts +281 -0
- package/dist/cjs/features/sync/crossPlatformSync.js +551 -0
- package/dist/cjs/features/sync/devices/deviceManager.d.ts +243 -0
- package/dist/cjs/features/sync/devices/deviceManager.js +450 -0
- package/dist/cjs/features/sync/devices/index.d.ts +10 -0
- package/dist/cjs/features/sync/devices/index.js +21 -0
- package/dist/cjs/features/sync/index.d.ts +37 -0
- package/dist/cjs/features/sync/index.js +111 -0
- package/dist/cjs/features/sync/protocol/index.d.ts +11 -0
- package/dist/cjs/features/sync/protocol/index.js +23 -0
- package/dist/cjs/features/sync/protocol/messages.d.ts +348 -0
- package/dist/cjs/features/sync/protocol/messages.js +215 -0
- package/dist/cjs/features/sync/protocol/vectorClock.d.ts +164 -0
- package/dist/cjs/features/sync/protocol/vectorClock.js +256 -0
- package/dist/cjs/features/sync/syncManager.d.ts +348 -0
- package/dist/cjs/features/sync/syncManager.js +455 -0
- package/dist/cjs/features/sync/tables/index.d.ts +10 -0
- package/dist/cjs/features/sync/tables/index.js +21 -0
- package/dist/cjs/features/sync/tables/syncLogManager.d.ts +216 -0
- package/dist/cjs/features/sync/tables/syncLogManager.js +423 -0
- package/dist/cjs/features/sync/transport/httpTransport.d.ts +123 -0
- package/dist/cjs/features/sync/transport/httpTransport.js +346 -0
- package/dist/cjs/features/sync/transport/index.d.ts +12 -0
- package/dist/cjs/features/sync/transport/index.js +25 -0
- package/dist/cjs/features/sync/transport/transport.d.ts +259 -0
- package/dist/cjs/features/sync/transport/transport.js +163 -0
- package/dist/cjs/features/sync/transport/websocketTransport.d.ts +126 -0
- package/dist/cjs/features/sync/transport/websocketTransport.js +342 -0
- package/dist/cjs/fts/PostgresFts.d.ts +29 -0
- package/dist/cjs/fts/PostgresFts.js +77 -0
- package/dist/cjs/fts/SqliteFts5.d.ts +22 -0
- package/dist/cjs/fts/SqliteFts5.js +58 -0
- package/dist/cjs/index.d.ts +30 -0
- package/dist/cjs/index.js +101 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/shared/connectionGate.d.ts +94 -0
- package/dist/cjs/shared/connectionGate.js +77 -0
- package/dist/cjs/shared/errors.d.ts +23 -0
- package/dist/cjs/shared/errors.js +45 -0
- package/dist/cjs/shared/logger.d.ts +26 -0
- package/dist/cjs/shared/logger.js +40 -0
- package/dist/cjs/shared/parameterUtils.d.ts +16 -0
- package/dist/cjs/shared/parameterUtils.js +59 -0
- package/dist/cjs/shared/postgresTarget.d.ts +11 -0
- package/dist/cjs/shared/postgresTarget.js +30 -0
- package/dist/cjs/shared/segment.d.ts +43 -0
- package/dist/cjs/shared/segment.js +63 -0
- package/dist/cjs/shared/serializedTransaction.d.ts +14 -0
- package/dist/cjs/shared/serializedTransaction.js +74 -0
- package/dist/cjs/shared/splitSqlStatements.d.ts +27 -0
- package/dist/cjs/shared/splitSqlStatements.js +163 -0
- package/dist/cjs/shared/sqlPlaceholders.d.ts +48 -0
- package/dist/cjs/shared/sqlPlaceholders.js +263 -0
- package/dist/cjs/types/index.d.ts +29 -0
- package/dist/cjs/types/index.js +33 -0
- package/dist/core/portable.d.ts +23 -0
- package/dist/core/portable.d.ts.map +1 -0
- package/dist/core/portable.js +23 -0
- package/dist/core/portable.js.map +1 -0
- package/dist/features/migrations/schemaMigrations.d.ts +93 -0
- package/dist/features/migrations/schemaMigrations.d.ts.map +1 -0
- package/dist/features/migrations/schemaMigrations.js +216 -0
- package/dist/features/migrations/schemaMigrations.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +139 -24
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var messages_exports = {};
|
|
19
|
+
__export(messages_exports, {
|
|
20
|
+
createAck: () => createAck,
|
|
21
|
+
createAckMessage: () => createAckMessage,
|
|
22
|
+
createBaseMessage: () => createBaseMessage,
|
|
23
|
+
createChangeRecord: () => createChangeRecord,
|
|
24
|
+
createDeltaPullRequest: () => createDeltaPullRequest,
|
|
25
|
+
createDeltaPushMessage: () => createDeltaPushMessage,
|
|
26
|
+
createError: () => createError,
|
|
27
|
+
createHandshakeRequest: () => createHandshakeRequest,
|
|
28
|
+
createHeartbeat: () => createHeartbeat,
|
|
29
|
+
createHeartbeatMessage: () => createHeartbeatMessage,
|
|
30
|
+
createPresenceMessage: () => createPresenceMessage,
|
|
31
|
+
generateChangeId: () => generateChangeId,
|
|
32
|
+
generateMessageId: () => generateMessageId,
|
|
33
|
+
isConflict: () => isConflict,
|
|
34
|
+
isDeltaPush: () => isDeltaPush,
|
|
35
|
+
isError: () => isError,
|
|
36
|
+
isHandshakeRequest: () => isHandshakeRequest,
|
|
37
|
+
isSyncMessage: () => isSyncMessage
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(messages_exports);
|
|
40
|
+
function generateMessageId() {
|
|
41
|
+
const timestamp = Date.now().toString(36);
|
|
42
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
43
|
+
return `msg_${timestamp}_${random}`;
|
|
44
|
+
}
|
|
45
|
+
function createBaseMessage(type, deviceId) {
|
|
46
|
+
return {
|
|
47
|
+
type,
|
|
48
|
+
messageId: generateMessageId(),
|
|
49
|
+
deviceId,
|
|
50
|
+
timestamp: Date.now()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function createHandshakeRequest(device, vectorClock, tables, capabilities = ["gzip", "delta"]) {
|
|
54
|
+
return {
|
|
55
|
+
...createBaseMessage("handshake_request", device.deviceId),
|
|
56
|
+
type: "handshake_request",
|
|
57
|
+
device,
|
|
58
|
+
vectorClock,
|
|
59
|
+
protocolVersion: 1,
|
|
60
|
+
capabilities,
|
|
61
|
+
tables
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createAck(deviceId, ackMessageId, success, error) {
|
|
65
|
+
return {
|
|
66
|
+
...createBaseMessage("ack", deviceId),
|
|
67
|
+
type: "ack",
|
|
68
|
+
ackMessageId,
|
|
69
|
+
success,
|
|
70
|
+
error
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function createHeartbeat(deviceId, sequence) {
|
|
74
|
+
return {
|
|
75
|
+
...createBaseMessage("heartbeat", deviceId),
|
|
76
|
+
type: "heartbeat",
|
|
77
|
+
sequence
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function createError(deviceId, code, message, recoverable = true, relatedMessageId) {
|
|
81
|
+
return {
|
|
82
|
+
...createBaseMessage("error", deviceId),
|
|
83
|
+
type: "error",
|
|
84
|
+
code,
|
|
85
|
+
message,
|
|
86
|
+
recoverable,
|
|
87
|
+
relatedMessageId
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function isHandshakeRequest(msg) {
|
|
91
|
+
return msg.type === "handshake_request";
|
|
92
|
+
}
|
|
93
|
+
function isDeltaPush(msg) {
|
|
94
|
+
return msg.type === "delta_push";
|
|
95
|
+
}
|
|
96
|
+
function isConflict(msg) {
|
|
97
|
+
return msg.type === "conflict";
|
|
98
|
+
}
|
|
99
|
+
function isError(msg) {
|
|
100
|
+
return msg.type === "error";
|
|
101
|
+
}
|
|
102
|
+
function isSyncMessage(value) {
|
|
103
|
+
if (!value || typeof value !== "object")
|
|
104
|
+
return false;
|
|
105
|
+
const msg = value;
|
|
106
|
+
if (typeof msg.type !== "string")
|
|
107
|
+
return false;
|
|
108
|
+
if (typeof msg.messageId !== "string")
|
|
109
|
+
return false;
|
|
110
|
+
if (typeof msg.deviceId !== "string")
|
|
111
|
+
return false;
|
|
112
|
+
if (typeof msg.timestamp !== "number")
|
|
113
|
+
return false;
|
|
114
|
+
const validTypes = [
|
|
115
|
+
"handshake_request",
|
|
116
|
+
"handshake_response",
|
|
117
|
+
"delta_push",
|
|
118
|
+
"delta_pull_request",
|
|
119
|
+
"delta_pull_response",
|
|
120
|
+
"ack",
|
|
121
|
+
"conflict",
|
|
122
|
+
"conflict_resolution",
|
|
123
|
+
"presence",
|
|
124
|
+
"heartbeat",
|
|
125
|
+
"heartbeat_ack",
|
|
126
|
+
"error"
|
|
127
|
+
];
|
|
128
|
+
return validTypes.includes(msg.type);
|
|
129
|
+
}
|
|
130
|
+
function createDeltaPushMessage(deviceId, changes, vectorClock, sequence = 1, totalBatches = 1) {
|
|
131
|
+
return {
|
|
132
|
+
...createBaseMessage("delta_push", deviceId),
|
|
133
|
+
type: "delta_push",
|
|
134
|
+
batch: {
|
|
135
|
+
changes,
|
|
136
|
+
sequence,
|
|
137
|
+
totalBatches
|
|
138
|
+
},
|
|
139
|
+
vectorClock
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createDeltaPullRequest(deviceId, sinceVectorClock, tables, limit) {
|
|
143
|
+
return {
|
|
144
|
+
...createBaseMessage("delta_pull_request", deviceId),
|
|
145
|
+
type: "delta_pull_request",
|
|
146
|
+
sinceVectorClock,
|
|
147
|
+
tables,
|
|
148
|
+
limit
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function createPresenceMessage(device, status, vectorClock) {
|
|
152
|
+
return {
|
|
153
|
+
...createBaseMessage("presence", device.deviceId),
|
|
154
|
+
type: "presence",
|
|
155
|
+
status,
|
|
156
|
+
device,
|
|
157
|
+
vectorClock
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function createHeartbeatMessage(deviceId, sequence = 0) {
|
|
161
|
+
return {
|
|
162
|
+
...createBaseMessage("heartbeat", deviceId),
|
|
163
|
+
type: "heartbeat",
|
|
164
|
+
sequence
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function createAckMessage(deviceId, ackMessageId, success = true, error) {
|
|
168
|
+
return {
|
|
169
|
+
...createBaseMessage("ack", deviceId),
|
|
170
|
+
type: "ack",
|
|
171
|
+
ackMessageId,
|
|
172
|
+
success,
|
|
173
|
+
error
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function generateChangeId() {
|
|
177
|
+
const timestamp = Date.now().toString(36);
|
|
178
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
179
|
+
return `chg_${timestamp}_${random}`;
|
|
180
|
+
}
|
|
181
|
+
function createChangeRecord(table, recordId, operation, deviceId, vectorClock, newData, oldData) {
|
|
182
|
+
return {
|
|
183
|
+
changeId: generateChangeId(),
|
|
184
|
+
table,
|
|
185
|
+
recordId,
|
|
186
|
+
operation,
|
|
187
|
+
deviceId,
|
|
188
|
+
vectorClock,
|
|
189
|
+
newData,
|
|
190
|
+
oldData,
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
synced: false
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
196
|
+
0 && (module.exports = {
|
|
197
|
+
createAck,
|
|
198
|
+
createAckMessage,
|
|
199
|
+
createBaseMessage,
|
|
200
|
+
createChangeRecord,
|
|
201
|
+
createDeltaPullRequest,
|
|
202
|
+
createDeltaPushMessage,
|
|
203
|
+
createError,
|
|
204
|
+
createHandshakeRequest,
|
|
205
|
+
createHeartbeat,
|
|
206
|
+
createHeartbeatMessage,
|
|
207
|
+
createPresenceMessage,
|
|
208
|
+
generateChangeId,
|
|
209
|
+
generateMessageId,
|
|
210
|
+
isConflict,
|
|
211
|
+
isDeltaPush,
|
|
212
|
+
isError,
|
|
213
|
+
isHandshakeRequest,
|
|
214
|
+
isSyncMessage
|
|
215
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Clock Implementation for Causality Tracking.
|
|
3
|
+
*
|
|
4
|
+
* Vector clocks track the causal ordering of events across distributed
|
|
5
|
+
* systems. Each device maintains a counter, and comparing clocks reveals
|
|
6
|
+
* whether events happened-before, happened-after, or are concurrent.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const clock = new VectorClock('device-1');
|
|
11
|
+
*
|
|
12
|
+
* // Local write - increment our counter
|
|
13
|
+
* clock.tick();
|
|
14
|
+
*
|
|
15
|
+
* // Received remote update - merge clocks
|
|
16
|
+
* clock.merge(remoteClockData);
|
|
17
|
+
*
|
|
18
|
+
* // Compare causality
|
|
19
|
+
* const relation = clock.compare(otherClock);
|
|
20
|
+
* if (relation === 'concurrent') {
|
|
21
|
+
* // Conflict detected!
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Vector clock data structure.
|
|
27
|
+
* Maps device IDs to their logical clock values.
|
|
28
|
+
*/
|
|
29
|
+
export type VectorClockData = Record<string, number>;
|
|
30
|
+
/**
|
|
31
|
+
* Causal relationship between two vector clocks.
|
|
32
|
+
*/
|
|
33
|
+
export type CausalRelation = 'before' | 'after' | 'concurrent' | 'equal';
|
|
34
|
+
/**
|
|
35
|
+
* Serialized vector clock for transmission.
|
|
36
|
+
*/
|
|
37
|
+
export interface SerializedVectorClock {
|
|
38
|
+
/** Device ID that owns this clock */
|
|
39
|
+
deviceId: string;
|
|
40
|
+
/** Clock values for all known devices */
|
|
41
|
+
values: VectorClockData;
|
|
42
|
+
/** Timestamp when clock was last updated */
|
|
43
|
+
updatedAt: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Vector Clock for distributed causality tracking.
|
|
47
|
+
*
|
|
48
|
+
* Implements Lamport's vector clock algorithm for determining
|
|
49
|
+
* causal ordering of events across multiple devices.
|
|
50
|
+
*/
|
|
51
|
+
export declare class VectorClock {
|
|
52
|
+
private readonly deviceId;
|
|
53
|
+
private values;
|
|
54
|
+
private lastUpdated;
|
|
55
|
+
constructor(deviceId: string);
|
|
56
|
+
/**
|
|
57
|
+
* Increment this device's clock (local event occurred).
|
|
58
|
+
* Call this before sending a message or making a local change.
|
|
59
|
+
*
|
|
60
|
+
* @returns The new clock value for this device
|
|
61
|
+
*/
|
|
62
|
+
tick(): number;
|
|
63
|
+
/**
|
|
64
|
+
* Merge another clock into this one (received remote event).
|
|
65
|
+
* Takes the maximum of each device's counter.
|
|
66
|
+
*
|
|
67
|
+
* @param other - The remote clock to merge
|
|
68
|
+
*/
|
|
69
|
+
merge(other: VectorClockData | VectorClock): void;
|
|
70
|
+
/**
|
|
71
|
+
* Update from received clock without incrementing local counter.
|
|
72
|
+
* Used when just observing, not participating in the event.
|
|
73
|
+
*
|
|
74
|
+
* @param other - The remote clock to observe
|
|
75
|
+
*/
|
|
76
|
+
observe(other: VectorClockData | VectorClock): void;
|
|
77
|
+
/**
|
|
78
|
+
* Compare this clock to another to determine causal relationship.
|
|
79
|
+
*
|
|
80
|
+
* @param other - The clock to compare against
|
|
81
|
+
* @returns The causal relationship
|
|
82
|
+
*/
|
|
83
|
+
compare(other: VectorClockData | VectorClock): CausalRelation;
|
|
84
|
+
/**
|
|
85
|
+
* Check if this clock happened before another.
|
|
86
|
+
*/
|
|
87
|
+
happenedBefore(other: VectorClockData | VectorClock): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Check if this clock happened after another.
|
|
90
|
+
*/
|
|
91
|
+
happenedAfter(other: VectorClockData | VectorClock): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Check if this clock is concurrent with another (conflict).
|
|
94
|
+
*/
|
|
95
|
+
isConcurrent(other: VectorClockData | VectorClock): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get this device's ID.
|
|
98
|
+
*/
|
|
99
|
+
getDeviceId(): string;
|
|
100
|
+
/**
|
|
101
|
+
* Get this device's current clock value.
|
|
102
|
+
*/
|
|
103
|
+
getValue(): number;
|
|
104
|
+
/**
|
|
105
|
+
* Get clock value for a specific device.
|
|
106
|
+
*/
|
|
107
|
+
getValueFor(deviceId: string): number;
|
|
108
|
+
/**
|
|
109
|
+
* Get all clock values.
|
|
110
|
+
*/
|
|
111
|
+
getValues(): VectorClockData;
|
|
112
|
+
/**
|
|
113
|
+
* Get list of known device IDs.
|
|
114
|
+
*/
|
|
115
|
+
getKnownDevices(): string[];
|
|
116
|
+
/**
|
|
117
|
+
* Get when the clock was last updated.
|
|
118
|
+
*/
|
|
119
|
+
getLastUpdated(): number;
|
|
120
|
+
/**
|
|
121
|
+
* Serialize clock for transmission.
|
|
122
|
+
*/
|
|
123
|
+
serialize(): SerializedVectorClock;
|
|
124
|
+
/**
|
|
125
|
+
* Create a clock from serialized data.
|
|
126
|
+
*/
|
|
127
|
+
static deserialize(data: SerializedVectorClock): VectorClock;
|
|
128
|
+
/**
|
|
129
|
+
* Create a copy of this clock.
|
|
130
|
+
*/
|
|
131
|
+
clone(): VectorClock;
|
|
132
|
+
/**
|
|
133
|
+
* Convert to JSON string.
|
|
134
|
+
*/
|
|
135
|
+
toJSON(): string;
|
|
136
|
+
/**
|
|
137
|
+
* Create from JSON string.
|
|
138
|
+
*/
|
|
139
|
+
static fromJSON(json: string): VectorClock;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create a new vector clock for a device.
|
|
143
|
+
*
|
|
144
|
+
* @param deviceId - Unique identifier for this device
|
|
145
|
+
* @returns New VectorClock instance
|
|
146
|
+
*/
|
|
147
|
+
export declare function createVectorClock(deviceId: string): VectorClock;
|
|
148
|
+
/**
|
|
149
|
+
* Compare two clock data objects without creating instances.
|
|
150
|
+
*/
|
|
151
|
+
export declare function compareClocks(a: VectorClockData, b: VectorClockData): CausalRelation;
|
|
152
|
+
/**
|
|
153
|
+
* Merge multiple clocks into a new clock.
|
|
154
|
+
*/
|
|
155
|
+
export declare function mergeClocks(deviceId: string, ...clocks: VectorClockData[]): VectorClock;
|
|
156
|
+
/**
|
|
157
|
+
* Check if a clock dominates another (happened strictly after).
|
|
158
|
+
*/
|
|
159
|
+
export declare function dominates(a: VectorClockData, b: VectorClockData): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Generate a unique device ID.
|
|
162
|
+
*/
|
|
163
|
+
export declare function generateDeviceId(): string;
|
|
164
|
+
//# sourceMappingURL=vectorClock.d.ts.map
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var vectorClock_exports = {};
|
|
19
|
+
__export(vectorClock_exports, {
|
|
20
|
+
VectorClock: () => VectorClock,
|
|
21
|
+
compareClocks: () => compareClocks,
|
|
22
|
+
createVectorClock: () => createVectorClock,
|
|
23
|
+
dominates: () => dominates,
|
|
24
|
+
generateDeviceId: () => generateDeviceId,
|
|
25
|
+
mergeClocks: () => mergeClocks
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(vectorClock_exports);
|
|
28
|
+
class VectorClock {
|
|
29
|
+
constructor(deviceId) {
|
|
30
|
+
this.deviceId = deviceId;
|
|
31
|
+
this.values = {};
|
|
32
|
+
this.lastUpdated = Date.now();
|
|
33
|
+
this.values[deviceId] = 0;
|
|
34
|
+
}
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Core Operations
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Increment this device's clock (local event occurred).
|
|
40
|
+
* Call this before sending a message or making a local change.
|
|
41
|
+
*
|
|
42
|
+
* @returns The new clock value for this device
|
|
43
|
+
*/
|
|
44
|
+
tick() {
|
|
45
|
+
this.values[this.deviceId] = (this.values[this.deviceId] ?? 0) + 1;
|
|
46
|
+
this.lastUpdated = Date.now();
|
|
47
|
+
return this.values[this.deviceId];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Merge another clock into this one (received remote event).
|
|
51
|
+
* Takes the maximum of each device's counter.
|
|
52
|
+
*
|
|
53
|
+
* @param other - The remote clock to merge
|
|
54
|
+
*/
|
|
55
|
+
merge(other) {
|
|
56
|
+
const otherValues = other instanceof VectorClock ? other.values : other;
|
|
57
|
+
for (const [deviceId, value] of Object.entries(otherValues)) {
|
|
58
|
+
this.values[deviceId] = Math.max(this.values[deviceId] ?? 0, value);
|
|
59
|
+
}
|
|
60
|
+
this.tick();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Update from received clock without incrementing local counter.
|
|
64
|
+
* Used when just observing, not participating in the event.
|
|
65
|
+
*
|
|
66
|
+
* @param other - The remote clock to observe
|
|
67
|
+
*/
|
|
68
|
+
observe(other) {
|
|
69
|
+
const otherValues = other instanceof VectorClock ? other.values : other;
|
|
70
|
+
for (const [deviceId, value] of Object.entries(otherValues)) {
|
|
71
|
+
this.values[deviceId] = Math.max(this.values[deviceId] ?? 0, value);
|
|
72
|
+
}
|
|
73
|
+
this.lastUpdated = Date.now();
|
|
74
|
+
}
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Comparison
|
|
77
|
+
// ============================================================================
|
|
78
|
+
/**
|
|
79
|
+
* Compare this clock to another to determine causal relationship.
|
|
80
|
+
*
|
|
81
|
+
* @param other - The clock to compare against
|
|
82
|
+
* @returns The causal relationship
|
|
83
|
+
*/
|
|
84
|
+
compare(other) {
|
|
85
|
+
const otherValues = other instanceof VectorClock ? other.values : other;
|
|
86
|
+
let thisGreater = false;
|
|
87
|
+
let otherGreater = false;
|
|
88
|
+
const allDevices = /* @__PURE__ */ new Set([
|
|
89
|
+
...Object.keys(this.values),
|
|
90
|
+
...Object.keys(otherValues)
|
|
91
|
+
]);
|
|
92
|
+
for (const deviceId of allDevices) {
|
|
93
|
+
const thisValue = this.values[deviceId] ?? 0;
|
|
94
|
+
const otherValue = otherValues[deviceId] ?? 0;
|
|
95
|
+
if (thisValue > otherValue) {
|
|
96
|
+
thisGreater = true;
|
|
97
|
+
} else if (otherValue > thisValue) {
|
|
98
|
+
otherGreater = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (thisGreater && otherGreater) {
|
|
102
|
+
return "concurrent";
|
|
103
|
+
} else if (thisGreater) {
|
|
104
|
+
return "after";
|
|
105
|
+
} else if (otherGreater) {
|
|
106
|
+
return "before";
|
|
107
|
+
} else {
|
|
108
|
+
return "equal";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if this clock happened before another.
|
|
113
|
+
*/
|
|
114
|
+
happenedBefore(other) {
|
|
115
|
+
return this.compare(other) === "before";
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if this clock happened after another.
|
|
119
|
+
*/
|
|
120
|
+
happenedAfter(other) {
|
|
121
|
+
return this.compare(other) === "after";
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if this clock is concurrent with another (conflict).
|
|
125
|
+
*/
|
|
126
|
+
isConcurrent(other) {
|
|
127
|
+
return this.compare(other) === "concurrent";
|
|
128
|
+
}
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Accessors
|
|
131
|
+
// ============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Get this device's ID.
|
|
134
|
+
*/
|
|
135
|
+
getDeviceId() {
|
|
136
|
+
return this.deviceId;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get this device's current clock value.
|
|
140
|
+
*/
|
|
141
|
+
getValue() {
|
|
142
|
+
return this.values[this.deviceId] ?? 0;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get clock value for a specific device.
|
|
146
|
+
*/
|
|
147
|
+
getValueFor(deviceId) {
|
|
148
|
+
return this.values[deviceId] ?? 0;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get all clock values.
|
|
152
|
+
*/
|
|
153
|
+
getValues() {
|
|
154
|
+
return { ...this.values };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get list of known device IDs.
|
|
158
|
+
*/
|
|
159
|
+
getKnownDevices() {
|
|
160
|
+
return Object.keys(this.values);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get when the clock was last updated.
|
|
164
|
+
*/
|
|
165
|
+
getLastUpdated() {
|
|
166
|
+
return this.lastUpdated;
|
|
167
|
+
}
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Serialization
|
|
170
|
+
// ============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Serialize clock for transmission.
|
|
173
|
+
*/
|
|
174
|
+
serialize() {
|
|
175
|
+
return {
|
|
176
|
+
deviceId: this.deviceId,
|
|
177
|
+
values: { ...this.values },
|
|
178
|
+
updatedAt: this.lastUpdated
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create a clock from serialized data.
|
|
183
|
+
*/
|
|
184
|
+
static deserialize(data) {
|
|
185
|
+
const clock = new VectorClock(data.deviceId);
|
|
186
|
+
clock.values = { ...data.values };
|
|
187
|
+
clock.lastUpdated = data.updatedAt;
|
|
188
|
+
return clock;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create a copy of this clock.
|
|
192
|
+
*/
|
|
193
|
+
clone() {
|
|
194
|
+
return VectorClock.deserialize(this.serialize());
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Convert to JSON string.
|
|
198
|
+
*/
|
|
199
|
+
toJSON() {
|
|
200
|
+
return JSON.stringify(this.serialize());
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create from JSON string.
|
|
204
|
+
*/
|
|
205
|
+
static fromJSON(json) {
|
|
206
|
+
return VectorClock.deserialize(JSON.parse(json));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function createVectorClock(deviceId) {
|
|
210
|
+
return new VectorClock(deviceId);
|
|
211
|
+
}
|
|
212
|
+
function compareClocks(a, b) {
|
|
213
|
+
let aGreater = false;
|
|
214
|
+
let bGreater = false;
|
|
215
|
+
const allDevices = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
216
|
+
for (const deviceId of allDevices) {
|
|
217
|
+
const aValue = a[deviceId] ?? 0;
|
|
218
|
+
const bValue = b[deviceId] ?? 0;
|
|
219
|
+
if (aValue > bValue) {
|
|
220
|
+
aGreater = true;
|
|
221
|
+
} else if (bValue > aValue) {
|
|
222
|
+
bGreater = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (aGreater && bGreater)
|
|
226
|
+
return "concurrent";
|
|
227
|
+
if (aGreater)
|
|
228
|
+
return "after";
|
|
229
|
+
if (bGreater)
|
|
230
|
+
return "before";
|
|
231
|
+
return "equal";
|
|
232
|
+
}
|
|
233
|
+
function mergeClocks(deviceId, ...clocks) {
|
|
234
|
+
const result = new VectorClock(deviceId);
|
|
235
|
+
for (const clock of clocks) {
|
|
236
|
+
result.observe(clock);
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
function dominates(a, b) {
|
|
241
|
+
return compareClocks(a, b) === "after";
|
|
242
|
+
}
|
|
243
|
+
function generateDeviceId() {
|
|
244
|
+
const timestamp = Date.now().toString(36);
|
|
245
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
246
|
+
return `device_${timestamp}_${random}`;
|
|
247
|
+
}
|
|
248
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
249
|
+
0 && (module.exports = {
|
|
250
|
+
VectorClock,
|
|
251
|
+
compareClocks,
|
|
252
|
+
createVectorClock,
|
|
253
|
+
dominates,
|
|
254
|
+
generateDeviceId,
|
|
255
|
+
mergeClocks
|
|
256
|
+
});
|