@fatagnus/dink-sync 1.0.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.
- package/README.md +312 -0
- package/dist/client/attachment.d.ts +225 -0
- package/dist/client/attachment.d.ts.map +1 -0
- package/dist/client/attachment.js +402 -0
- package/dist/client/attachment.js.map +1 -0
- package/dist/client/binary-encoding.d.ts +45 -0
- package/dist/client/binary-encoding.d.ts.map +1 -0
- package/dist/client/binary-encoding.js +90 -0
- package/dist/client/binary-encoding.js.map +1 -0
- package/dist/client/collection.d.ts +10 -0
- package/dist/client/collection.d.ts.map +1 -0
- package/dist/client/collection.js +924 -0
- package/dist/client/collection.js.map +1 -0
- package/dist/client/compression.d.ts +56 -0
- package/dist/client/compression.d.ts.map +1 -0
- package/dist/client/compression.js +173 -0
- package/dist/client/compression.js.map +1 -0
- package/dist/client/crdt/index.d.ts +2 -0
- package/dist/client/crdt/index.d.ts.map +1 -0
- package/dist/client/crdt/index.js +2 -0
- package/dist/client/crdt/index.js.map +1 -0
- package/dist/client/crdt/yjs-doc.d.ts +88 -0
- package/dist/client/crdt/yjs-doc.d.ts.map +1 -0
- package/dist/client/crdt/yjs-doc.js +123 -0
- package/dist/client/crdt/yjs-doc.js.map +1 -0
- package/dist/client/index.d.ts +66 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +233 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/mock-transport.d.ts +155 -0
- package/dist/client/mock-transport.d.ts.map +1 -0
- package/dist/client/mock-transport.js +292 -0
- package/dist/client/mock-transport.js.map +1 -0
- package/dist/client/network-detector.d.ts +65 -0
- package/dist/client/network-detector.d.ts.map +1 -0
- package/dist/client/network-detector.js +147 -0
- package/dist/client/network-detector.js.map +1 -0
- package/dist/client/provisioning.d.ts +126 -0
- package/dist/client/provisioning.d.ts.map +1 -0
- package/dist/client/provisioning.js +125 -0
- package/dist/client/provisioning.js.map +1 -0
- package/dist/client/signal.d.ts +13 -0
- package/dist/client/signal.d.ts.map +1 -0
- package/dist/client/signal.js +27 -0
- package/dist/client/signal.js.map +1 -0
- package/dist/client/sync-engine.d.ts +298 -0
- package/dist/client/sync-engine.d.ts.map +1 -0
- package/dist/client/sync-engine.js +904 -0
- package/dist/client/sync-engine.js.map +1 -0
- package/dist/client/synced-edge.d.ts +109 -0
- package/dist/client/synced-edge.d.ts.map +1 -0
- package/dist/client/synced-edge.js +179 -0
- package/dist/client/synced-edge.js.map +1 -0
- package/dist/client/synced-offline-edge-types.d.ts +540 -0
- package/dist/client/synced-offline-edge-types.d.ts.map +1 -0
- package/dist/client/synced-offline-edge-types.js +10 -0
- package/dist/client/synced-offline-edge-types.js.map +1 -0
- package/dist/client/synced-offline-edge.d.ts +54 -0
- package/dist/client/synced-offline-edge.d.ts.map +1 -0
- package/dist/client/synced-offline-edge.js +731 -0
- package/dist/client/synced-offline-edge.js.map +1 -0
- package/dist/client/transport.d.ts +202 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +409 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/types.d.ts +622 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +60 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/validation.d.ts +61 -0
- package/dist/client/validation.d.ts.map +1 -0
- package/dist/client/validation.js +57 -0
- package/dist/client/validation.js.map +1 -0
- package/dist/client/versioning.d.ts +134 -0
- package/dist/client/versioning.d.ts.map +1 -0
- package/dist/client/versioning.js +304 -0
- package/dist/client/versioning.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence/encryption.d.ts +114 -0
- package/dist/persistence/encryption.d.ts.map +1 -0
- package/dist/persistence/encryption.js +286 -0
- package/dist/persistence/encryption.js.map +1 -0
- package/dist/persistence/index.d.ts +21 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +20 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/memory.d.ts +32 -0
- package/dist/persistence/memory.d.ts.map +1 -0
- package/dist/persistence/memory.js +57 -0
- package/dist/persistence/memory.js.map +1 -0
- package/dist/persistence/migrations.d.ts +106 -0
- package/dist/persistence/migrations.d.ts.map +1 -0
- package/dist/persistence/migrations.js +176 -0
- package/dist/persistence/migrations.js.map +1 -0
- package/dist/persistence/pending-queue.d.ts +109 -0
- package/dist/persistence/pending-queue.d.ts.map +1 -0
- package/dist/persistence/pending-queue.js +249 -0
- package/dist/persistence/pending-queue.js.map +1 -0
- package/dist/persistence/pglite.d.ts +72 -0
- package/dist/persistence/pglite.d.ts.map +1 -0
- package/dist/persistence/pglite.js +126 -0
- package/dist/persistence/pglite.js.map +1 -0
- package/dist/persistence/quota-manager.d.ts +134 -0
- package/dist/persistence/quota-manager.d.ts.map +1 -0
- package/dist/persistence/quota-manager.js +242 -0
- package/dist/persistence/quota-manager.js.map +1 -0
- package/dist/persistence/types.d.ts +54 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +2 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/react/OfflineEdgeProvider.d.ts +91 -0
- package/dist/react/OfflineEdgeProvider.d.ts.map +1 -0
- package/dist/react/OfflineEdgeProvider.js +127 -0
- package/dist/react/OfflineEdgeProvider.js.map +1 -0
- package/dist/react/SyncedOfflineEdgeProvider.d.ts +105 -0
- package/dist/react/SyncedOfflineEdgeProvider.d.ts.map +1 -0
- package/dist/react/SyncedOfflineEdgeProvider.js +138 -0
- package/dist/react/SyncedOfflineEdgeProvider.js.map +1 -0
- package/dist/react/index.d.ts +50 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +51 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useCollection.d.ts +77 -0
- package/dist/react/useCollection.d.ts.map +1 -0
- package/dist/react/useCollection.js +113 -0
- package/dist/react/useCollection.js.map +1 -0
- package/dist/react/useCollectionSyncMode.d.ts +61 -0
- package/dist/react/useCollectionSyncMode.d.ts.map +1 -0
- package/dist/react/useCollectionSyncMode.js +93 -0
- package/dist/react/useCollectionSyncMode.js.map +1 -0
- package/dist/react/useConnectionState.d.ts +44 -0
- package/dist/react/useConnectionState.d.ts.map +1 -0
- package/dist/react/useConnectionState.js +46 -0
- package/dist/react/useConnectionState.js.map +1 -0
- package/dist/react/useDocumentSyncStatus.d.ts +72 -0
- package/dist/react/useDocumentSyncStatus.d.ts.map +1 -0
- package/dist/react/useDocumentSyncStatus.js +110 -0
- package/dist/react/useDocumentSyncStatus.js.map +1 -0
- package/dist/react/useOfflineEdge.d.ts +58 -0
- package/dist/react/useOfflineEdge.d.ts.map +1 -0
- package/dist/react/useOfflineEdge.js +54 -0
- package/dist/react/useOfflineEdge.js.map +1 -0
- package/dist/react/usePendingChanges.d.ts +67 -0
- package/dist/react/usePendingChanges.d.ts.map +1 -0
- package/dist/react/usePendingChanges.js +90 -0
- package/dist/react/usePendingChanges.js.map +1 -0
- package/dist/react/useRejectedDocuments.d.ts +112 -0
- package/dist/react/useRejectedDocuments.d.ts.map +1 -0
- package/dist/react/useRejectedDocuments.js +213 -0
- package/dist/react/useRejectedDocuments.js.map +1 -0
- package/dist/react/useSyncControls.d.ts +96 -0
- package/dist/react/useSyncControls.d.ts.map +1 -0
- package/dist/react/useSyncControls.js +112 -0
- package/dist/react/useSyncControls.js.map +1 -0
- package/dist/react/useSyncProgress.d.ts +78 -0
- package/dist/react/useSyncProgress.d.ts.map +1 -0
- package/dist/react/useSyncProgress.js +90 -0
- package/dist/react/useSyncProgress.js.map +1 -0
- package/dist/react/useSyncRejected.d.ts +47 -0
- package/dist/react/useSyncRejected.d.ts.map +1 -0
- package/dist/react/useSyncRejected.js +55 -0
- package/dist/react/useSyncRejected.js.map +1 -0
- package/dist/react/useSyncStatus.d.ts +56 -0
- package/dist/react/useSyncStatus.d.ts.map +1 -0
- package/dist/react/useSyncStatus.js +59 -0
- package/dist/react/useSyncStatus.js.map +1 -0
- package/dist/react/useSyncedOfflineEdge.d.ts +69 -0
- package/dist/react/useSyncedOfflineEdge.d.ts.map +1 -0
- package/dist/react/useSyncedOfflineEdge.js +65 -0
- package/dist/react/useSyncedOfflineEdge.js.map +1 -0
- package/dist/service-worker/index.d.ts +7 -0
- package/dist/service-worker/index.d.ts.map +1 -0
- package/dist/service-worker/index.js +7 -0
- package/dist/service-worker/index.js.map +1 -0
- package/dist/service-worker/sync-worker.d.ts +230 -0
- package/dist/service-worker/sync-worker.d.ts.map +1 -0
- package/dist/service-worker/sync-worker.js +471 -0
- package/dist/service-worker/sync-worker.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { SyncPriority } from '../client/types.js';
|
|
2
|
+
// Re-export for convenience
|
|
3
|
+
export { SyncPriority };
|
|
4
|
+
/**
|
|
5
|
+
* Priority order for sorting (lower number = higher priority).
|
|
6
|
+
*/
|
|
7
|
+
const PRIORITY_ORDER = {
|
|
8
|
+
[SyncPriority.High]: 0,
|
|
9
|
+
[SyncPriority.Normal]: 1,
|
|
10
|
+
[SyncPriority.Low]: 2,
|
|
11
|
+
};
|
|
12
|
+
/** Reserved collection name for pending changes storage */
|
|
13
|
+
const PENDING_COLLECTION = '__pending_changes__';
|
|
14
|
+
/**
|
|
15
|
+
* Encode a PendingChange to Uint8Array for storage.
|
|
16
|
+
*/
|
|
17
|
+
function encodeChange(change) {
|
|
18
|
+
const metadata = JSON.stringify({
|
|
19
|
+
id: change.id,
|
|
20
|
+
collection: change.collection,
|
|
21
|
+
docId: change.docId,
|
|
22
|
+
timestamp: change.timestamp,
|
|
23
|
+
retryCount: change.retryCount ?? 0,
|
|
24
|
+
priority: change.priority ?? SyncPriority.Normal,
|
|
25
|
+
});
|
|
26
|
+
const metadataBytes = new TextEncoder().encode(metadata);
|
|
27
|
+
// Format: [4 bytes metadata length][metadata][delta]
|
|
28
|
+
const buffer = new Uint8Array(4 + metadataBytes.length + change.delta.length);
|
|
29
|
+
const view = new DataView(buffer.buffer);
|
|
30
|
+
view.setUint32(0, metadataBytes.length, false);
|
|
31
|
+
buffer.set(metadataBytes, 4);
|
|
32
|
+
buffer.set(change.delta, 4 + metadataBytes.length);
|
|
33
|
+
return buffer;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decode a Uint8Array to PendingChange.
|
|
37
|
+
*/
|
|
38
|
+
function decodeChange(data) {
|
|
39
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
40
|
+
const metadataLength = view.getUint32(0, false);
|
|
41
|
+
const metadataBytes = data.slice(4, 4 + metadataLength);
|
|
42
|
+
const metadata = JSON.parse(new TextDecoder().decode(metadataBytes));
|
|
43
|
+
const delta = data.slice(4 + metadataLength);
|
|
44
|
+
return {
|
|
45
|
+
id: metadata.id,
|
|
46
|
+
collection: metadata.collection,
|
|
47
|
+
docId: metadata.docId,
|
|
48
|
+
timestamp: metadata.timestamp,
|
|
49
|
+
retryCount: metadata.retryCount ?? 0,
|
|
50
|
+
priority: metadata.priority ?? SyncPriority.Normal,
|
|
51
|
+
delta,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Persistent queue for pending sync changes.
|
|
56
|
+
*
|
|
57
|
+
* Changes are persisted to storage immediately when enqueued,
|
|
58
|
+
* ensuring they survive app crashes or page refreshes.
|
|
59
|
+
*/
|
|
60
|
+
export class PendingQueue {
|
|
61
|
+
persistence;
|
|
62
|
+
changes = new Map();
|
|
63
|
+
initialized = false;
|
|
64
|
+
enqueueCallbacks = new Set();
|
|
65
|
+
acknowledgeCallbacks = new Set();
|
|
66
|
+
constructor(persistence) {
|
|
67
|
+
this.persistence = persistence;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the queue by loading pending changes from persistence.
|
|
71
|
+
*/
|
|
72
|
+
async init() {
|
|
73
|
+
if (this.initialized)
|
|
74
|
+
return;
|
|
75
|
+
// Load all pending changes from persistence
|
|
76
|
+
const changeIds = await this.persistence.list(PENDING_COLLECTION);
|
|
77
|
+
for (const changeId of changeIds) {
|
|
78
|
+
const data = await this.persistence.load(PENDING_COLLECTION, changeId);
|
|
79
|
+
if (data) {
|
|
80
|
+
const change = decodeChange(data);
|
|
81
|
+
this.changes.set(change.id, change);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
this.initialized = true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Add a change to the queue.
|
|
88
|
+
* The change is persisted to storage before being added to in-memory state.
|
|
89
|
+
*/
|
|
90
|
+
async enqueue(change) {
|
|
91
|
+
// Ensure priority is set (default to normal)
|
|
92
|
+
const changeWithPriority = {
|
|
93
|
+
...change,
|
|
94
|
+
priority: change.priority ?? SyncPriority.Normal,
|
|
95
|
+
};
|
|
96
|
+
// Persist first to ensure durability
|
|
97
|
+
const encoded = encodeChange(changeWithPriority);
|
|
98
|
+
await this.persistence.save(PENDING_COLLECTION, changeWithPriority.id, encoded);
|
|
99
|
+
// Then add to in-memory cache
|
|
100
|
+
this.changes.set(changeWithPriority.id, changeWithPriority);
|
|
101
|
+
// Notify subscribers
|
|
102
|
+
for (const callback of this.enqueueCallbacks) {
|
|
103
|
+
callback(change);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Acknowledge a change as synced, removing it from the queue.
|
|
108
|
+
*/
|
|
109
|
+
async acknowledge(id) {
|
|
110
|
+
// Remove from persistence first
|
|
111
|
+
await this.persistence.delete(PENDING_COLLECTION, id);
|
|
112
|
+
// Then remove from in-memory cache
|
|
113
|
+
this.changes.delete(id);
|
|
114
|
+
// Notify subscribers
|
|
115
|
+
for (const callback of this.acknowledgeCallbacks) {
|
|
116
|
+
callback(id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* List all pending changes for a specific document.
|
|
121
|
+
*/
|
|
122
|
+
async list(collection, docId) {
|
|
123
|
+
const result = [];
|
|
124
|
+
for (const change of this.changes.values()) {
|
|
125
|
+
if (change.collection === collection && change.docId === docId) {
|
|
126
|
+
result.push(change);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Sort by timestamp for consistent ordering
|
|
130
|
+
return result.sort((a, b) => a.timestamp - b.timestamp);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* List all pending changes.
|
|
134
|
+
*/
|
|
135
|
+
async listAll() {
|
|
136
|
+
const result = Array.from(this.changes.values());
|
|
137
|
+
return result.sort((a, b) => a.timestamp - b.timestamp);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* List pending changes for a specific collection.
|
|
141
|
+
*/
|
|
142
|
+
async listByCollection(collection) {
|
|
143
|
+
const result = [];
|
|
144
|
+
for (const change of this.changes.values()) {
|
|
145
|
+
if (change.collection === collection) {
|
|
146
|
+
result.push(change);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return result.sort((a, b) => a.timestamp - b.timestamp);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get total count of pending changes.
|
|
153
|
+
*/
|
|
154
|
+
async count() {
|
|
155
|
+
return this.changes.size;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get count of pending changes for a specific collection.
|
|
159
|
+
*/
|
|
160
|
+
async countByCollection(collection) {
|
|
161
|
+
let count = 0;
|
|
162
|
+
for (const change of this.changes.values()) {
|
|
163
|
+
if (change.collection === collection) {
|
|
164
|
+
count++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return count;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Clear all pending changes for a specific document.
|
|
171
|
+
*/
|
|
172
|
+
async clearDocument(collection, docId) {
|
|
173
|
+
const toRemove = [];
|
|
174
|
+
for (const change of this.changes.values()) {
|
|
175
|
+
if (change.collection === collection && change.docId === docId) {
|
|
176
|
+
toRemove.push(change.id);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const id of toRemove) {
|
|
180
|
+
await this.persistence.delete(PENDING_COLLECTION, id);
|
|
181
|
+
this.changes.delete(id);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Subscribe to enqueue events.
|
|
186
|
+
*/
|
|
187
|
+
onEnqueue(callback) {
|
|
188
|
+
this.enqueueCallbacks.add(callback);
|
|
189
|
+
return () => {
|
|
190
|
+
this.enqueueCallbacks.delete(callback);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Subscribe to acknowledge events.
|
|
195
|
+
*/
|
|
196
|
+
onAcknowledge(callback) {
|
|
197
|
+
this.acknowledgeCallbacks.add(callback);
|
|
198
|
+
return () => {
|
|
199
|
+
this.acknowledgeCallbacks.delete(callback);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if a specific change is pending.
|
|
204
|
+
*/
|
|
205
|
+
has(id) {
|
|
206
|
+
return this.changes.has(id);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get a specific pending change by ID.
|
|
210
|
+
*/
|
|
211
|
+
get(id) {
|
|
212
|
+
return this.changes.get(id);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* List all pending changes sorted by priority (high first) then timestamp.
|
|
216
|
+
*/
|
|
217
|
+
async listAllByPriority() {
|
|
218
|
+
const result = Array.from(this.changes.values());
|
|
219
|
+
return result.sort((a, b) => {
|
|
220
|
+
const priorityA = PRIORITY_ORDER[a.priority ?? SyncPriority.Normal];
|
|
221
|
+
const priorityB = PRIORITY_ORDER[b.priority ?? SyncPriority.Normal];
|
|
222
|
+
// Sort by priority first (lower number = higher priority)
|
|
223
|
+
if (priorityA !== priorityB) {
|
|
224
|
+
return priorityA - priorityB;
|
|
225
|
+
}
|
|
226
|
+
// Within same priority, sort by timestamp (older first)
|
|
227
|
+
return a.timestamp - b.timestamp;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Update the priority of a pending change.
|
|
232
|
+
*/
|
|
233
|
+
async updatePriority(id, priority) {
|
|
234
|
+
const change = this.changes.get(id);
|
|
235
|
+
if (!change) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const updatedChange = {
|
|
239
|
+
...change,
|
|
240
|
+
priority,
|
|
241
|
+
};
|
|
242
|
+
// Update in persistence
|
|
243
|
+
const encoded = encodeChange(updatedChange);
|
|
244
|
+
await this.persistence.save(PENDING_COLLECTION, id, encoded);
|
|
245
|
+
// Update in-memory cache
|
|
246
|
+
this.changes.set(id, updatedChange);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=pending-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-queue.js","sourceRoot":"","sources":["../../src/persistence/pending-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PGlite persistence implementation for browser environments.
|
|
3
|
+
*
|
|
4
|
+
* Uses @electric-sql/pglite to provide PostgreSQL in WASM,
|
|
5
|
+
* with IndexedDB backend for persistent browser storage.
|
|
6
|
+
*/
|
|
7
|
+
import type { PersistenceProvider } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Interface for PGlite database operations.
|
|
10
|
+
* This allows using any PGlite-compatible database instance.
|
|
11
|
+
*/
|
|
12
|
+
export interface PGliteInterface {
|
|
13
|
+
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
|
|
14
|
+
rows: T[];
|
|
15
|
+
}>;
|
|
16
|
+
exec(sql: string): Promise<unknown>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* PGlite-based persistence provider for browser environments.
|
|
21
|
+
*
|
|
22
|
+
* Uses PostgreSQL running in WASM with IndexedDB for persistence.
|
|
23
|
+
* Suitable for offline-first applications in the browser.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { PGlite } from '@electric-sql/pglite';
|
|
28
|
+
* import { PGlitePersistence } from './pglite.js';
|
|
29
|
+
*
|
|
30
|
+
* const pg = new PGlite('idb://my-app');
|
|
31
|
+
* const persistence = new PGlitePersistence(pg);
|
|
32
|
+
* await persistence.init();
|
|
33
|
+
*
|
|
34
|
+
* await persistence.save('tasks', 'task-1', encodedData);
|
|
35
|
+
* const data = await persistence.load('tasks', 'task-1');
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class PGlitePersistence implements PersistenceProvider {
|
|
39
|
+
private pg;
|
|
40
|
+
private initialized;
|
|
41
|
+
constructor(pg: PGliteInterface);
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the persistence provider.
|
|
44
|
+
* Must be called before any other operations.
|
|
45
|
+
*/
|
|
46
|
+
init(): Promise<void>;
|
|
47
|
+
save(collection: string, docId: string, data: Uint8Array): Promise<void>;
|
|
48
|
+
load(collection: string, docId: string): Promise<Uint8Array | undefined>;
|
|
49
|
+
list(collection: string): Promise<string[]>;
|
|
50
|
+
delete(collection: string, docId: string): Promise<void>;
|
|
51
|
+
saveStateVector(collection: string, docId: string, vector: Uint8Array): Promise<void>;
|
|
52
|
+
loadStateVector(collection: string, docId: string): Promise<Uint8Array | undefined>;
|
|
53
|
+
close(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a PGlite persistence provider with automatic initialization.
|
|
57
|
+
*
|
|
58
|
+
* @param dataDir - Optional path for IndexedDB persistence (e.g., 'idb://my-app')
|
|
59
|
+
* If not provided, uses in-memory storage (useful for testing)
|
|
60
|
+
* @returns Initialized PersistenceProvider
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // In-memory (for testing)
|
|
65
|
+
* const persistence = await createPGlitePersistence();
|
|
66
|
+
*
|
|
67
|
+
* // With IndexedDB persistence (for browser)
|
|
68
|
+
* const persistence = await createPGlitePersistence('idb://my-app');
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function createPGlitePersistence(dataDir?: string): Promise<PersistenceProvider>;
|
|
72
|
+
//# sourceMappingURL=pglite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pglite.d.ts","sourceRoot":"","sources":["../../src/persistence/pglite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAqCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAkB,YAAW,mBAAmB;IAG/C,OAAO,CAAC,EAAE;IAFtB,OAAO,CAAC,WAAW,CAAS;gBAER,EAAE,EAAE,eAAe;IAEvC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxE,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAYxE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQ3C,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAYnF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAS5F"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize the database schema for persistence.
|
|
3
|
+
* Creates tables for documents and state vectors if they don't exist.
|
|
4
|
+
*/
|
|
5
|
+
async function initSchema(pg) {
|
|
6
|
+
// Documents table - stores collection documents
|
|
7
|
+
await pg.exec(`
|
|
8
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
9
|
+
collection TEXT NOT NULL,
|
|
10
|
+
doc_id TEXT NOT NULL,
|
|
11
|
+
data BYTEA NOT NULL,
|
|
12
|
+
PRIMARY KEY (collection, doc_id)
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
// State vectors table - stores sync state for each document
|
|
16
|
+
await pg.exec(`
|
|
17
|
+
CREATE TABLE IF NOT EXISTS state_vectors (
|
|
18
|
+
collection TEXT NOT NULL,
|
|
19
|
+
doc_id TEXT NOT NULL,
|
|
20
|
+
vector BYTEA NOT NULL,
|
|
21
|
+
PRIMARY KEY (collection, doc_id)
|
|
22
|
+
)
|
|
23
|
+
`);
|
|
24
|
+
// Create indexes for efficient listing
|
|
25
|
+
await pg.exec(`
|
|
26
|
+
CREATE INDEX IF NOT EXISTS documents_collection_idx ON documents (collection)
|
|
27
|
+
`);
|
|
28
|
+
await pg.exec(`
|
|
29
|
+
CREATE INDEX IF NOT EXISTS state_vectors_collection_idx ON state_vectors (collection)
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* PGlite-based persistence provider for browser environments.
|
|
34
|
+
*
|
|
35
|
+
* Uses PostgreSQL running in WASM with IndexedDB for persistence.
|
|
36
|
+
* Suitable for offline-first applications in the browser.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { PGlite } from '@electric-sql/pglite';
|
|
41
|
+
* import { PGlitePersistence } from './pglite.js';
|
|
42
|
+
*
|
|
43
|
+
* const pg = new PGlite('idb://my-app');
|
|
44
|
+
* const persistence = new PGlitePersistence(pg);
|
|
45
|
+
* await persistence.init();
|
|
46
|
+
*
|
|
47
|
+
* await persistence.save('tasks', 'task-1', encodedData);
|
|
48
|
+
* const data = await persistence.load('tasks', 'task-1');
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export class PGlitePersistence {
|
|
52
|
+
pg;
|
|
53
|
+
initialized = false;
|
|
54
|
+
constructor(pg) {
|
|
55
|
+
this.pg = pg;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the persistence provider.
|
|
59
|
+
* Must be called before any other operations.
|
|
60
|
+
*/
|
|
61
|
+
async init() {
|
|
62
|
+
if (this.initialized)
|
|
63
|
+
return;
|
|
64
|
+
await initSchema(this.pg);
|
|
65
|
+
this.initialized = true;
|
|
66
|
+
}
|
|
67
|
+
async save(collection, docId, data) {
|
|
68
|
+
await this.pg.query(`INSERT INTO documents (collection, doc_id, data) VALUES ($1, $2, $3)
|
|
69
|
+
ON CONFLICT (collection, doc_id) DO UPDATE SET data = $3`, [collection, docId, data]);
|
|
70
|
+
}
|
|
71
|
+
async load(collection, docId) {
|
|
72
|
+
const result = await this.pg.query('SELECT data FROM documents WHERE collection = $1 AND doc_id = $2', [collection, docId]);
|
|
73
|
+
if (result.rows.length === 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
const raw = result.rows[0].data;
|
|
76
|
+
// Handle different data formats from PGlite
|
|
77
|
+
return raw instanceof Uint8Array ? raw : new Uint8Array(raw);
|
|
78
|
+
}
|
|
79
|
+
async list(collection) {
|
|
80
|
+
const result = await this.pg.query('SELECT doc_id FROM documents WHERE collection = $1', [collection]);
|
|
81
|
+
return result.rows.map(row => row.doc_id);
|
|
82
|
+
}
|
|
83
|
+
async delete(collection, docId) {
|
|
84
|
+
await this.pg.query('DELETE FROM documents WHERE collection = $1 AND doc_id = $2', [collection, docId]);
|
|
85
|
+
}
|
|
86
|
+
async saveStateVector(collection, docId, vector) {
|
|
87
|
+
await this.pg.query(`INSERT INTO state_vectors (collection, doc_id, vector) VALUES ($1, $2, $3)
|
|
88
|
+
ON CONFLICT (collection, doc_id) DO UPDATE SET vector = $3`, [collection, docId, vector]);
|
|
89
|
+
}
|
|
90
|
+
async loadStateVector(collection, docId) {
|
|
91
|
+
const result = await this.pg.query('SELECT vector FROM state_vectors WHERE collection = $1 AND doc_id = $2', [collection, docId]);
|
|
92
|
+
if (result.rows.length === 0)
|
|
93
|
+
return undefined;
|
|
94
|
+
const raw = result.rows[0].vector;
|
|
95
|
+
// Handle different data formats from PGlite
|
|
96
|
+
return raw instanceof Uint8Array ? raw : new Uint8Array(raw);
|
|
97
|
+
}
|
|
98
|
+
async close() {
|
|
99
|
+
await this.pg.close();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create a PGlite persistence provider with automatic initialization.
|
|
104
|
+
*
|
|
105
|
+
* @param dataDir - Optional path for IndexedDB persistence (e.g., 'idb://my-app')
|
|
106
|
+
* If not provided, uses in-memory storage (useful for testing)
|
|
107
|
+
* @returns Initialized PersistenceProvider
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* // In-memory (for testing)
|
|
112
|
+
* const persistence = await createPGlitePersistence();
|
|
113
|
+
*
|
|
114
|
+
* // With IndexedDB persistence (for browser)
|
|
115
|
+
* const persistence = await createPGlitePersistence('idb://my-app');
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export async function createPGlitePersistence(dataDir) {
|
|
119
|
+
// Dynamic import to avoid bundling PGlite in environments that don't need it
|
|
120
|
+
const { PGlite } = await import('@electric-sql/pglite');
|
|
121
|
+
const pg = dataDir ? new PGlite(dataDir) : new PGlite();
|
|
122
|
+
const persistence = new PGlitePersistence(pg);
|
|
123
|
+
await persistence.init();
|
|
124
|
+
return persistence;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=pglite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pglite.js","sourceRoot":"","sources":["../../src/persistence/pglite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuotaManager - Manages storage quota and eviction for offline-first sync.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Storage usage tracking
|
|
6
|
+
* - Configurable quota limits
|
|
7
|
+
* - Warning and full events at thresholds
|
|
8
|
+
* - Multiple eviction policies (LRU, oldest, priority-based)
|
|
9
|
+
* - Automatic or manual eviction
|
|
10
|
+
*/
|
|
11
|
+
import type { PersistenceProvider } from './types.js';
|
|
12
|
+
import type { SyncPriority, Unsubscribe } from '../client/types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Eviction policy for when storage quota is exceeded.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EvictionPolicy: {
|
|
17
|
+
/** Evict least recently used documents first */
|
|
18
|
+
readonly LRU: "lru";
|
|
19
|
+
/** Evict oldest documents first (by creation time) */
|
|
20
|
+
readonly Oldest: "oldest";
|
|
21
|
+
/** Evict lowest priority documents first */
|
|
22
|
+
readonly Priority: "priority";
|
|
23
|
+
};
|
|
24
|
+
export type EvictionPolicy = typeof EvictionPolicy[keyof typeof EvictionPolicy];
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the QuotaManager.
|
|
27
|
+
*/
|
|
28
|
+
export interface StorageQuotaConfig {
|
|
29
|
+
/** Maximum storage quota in bytes (default: 50MB) */
|
|
30
|
+
quotaBytes?: number;
|
|
31
|
+
/** Threshold (0-1) at which to emit warning event (default: 0.8 = 80%) */
|
|
32
|
+
warningThreshold?: number;
|
|
33
|
+
/** Eviction policy to use when quota exceeded (default: oldest) */
|
|
34
|
+
evictionPolicy?: EvictionPolicy;
|
|
35
|
+
/** Whether to automatically evict when quota exceeded (default: false) */
|
|
36
|
+
autoEvict?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Event emitted when storage usage exceeds warning threshold.
|
|
40
|
+
*/
|
|
41
|
+
export interface StorageWarningEvent {
|
|
42
|
+
/** Current storage usage in bytes */
|
|
43
|
+
usageBytes: number;
|
|
44
|
+
/** Configured quota in bytes */
|
|
45
|
+
quotaBytes: number;
|
|
46
|
+
/** Current usage as percentage (0-100) */
|
|
47
|
+
usagePercent: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Event emitted when storage quota is exceeded.
|
|
51
|
+
*/
|
|
52
|
+
export interface StorageFullEvent {
|
|
53
|
+
/** Current storage usage in bytes */
|
|
54
|
+
usageBytes: number;
|
|
55
|
+
/** Configured quota in bytes */
|
|
56
|
+
quotaBytes: number;
|
|
57
|
+
/** Amount over quota in bytes */
|
|
58
|
+
overageBytes: number;
|
|
59
|
+
}
|
|
60
|
+
export type { Unsubscribe };
|
|
61
|
+
/**
|
|
62
|
+
* Manages storage quota and eviction for persistence providers.
|
|
63
|
+
*/
|
|
64
|
+
export declare class QuotaManager {
|
|
65
|
+
private readonly persistence;
|
|
66
|
+
private readonly quotaBytes;
|
|
67
|
+
private readonly warningThreshold;
|
|
68
|
+
private readonly evictionPolicy;
|
|
69
|
+
private readonly autoEvict;
|
|
70
|
+
private readonly warningCallbacks;
|
|
71
|
+
private readonly fullCallbacks;
|
|
72
|
+
private readonly documentMetadata;
|
|
73
|
+
private warningEmitted;
|
|
74
|
+
constructor(persistence: PersistenceProvider, config?: StorageQuotaConfig);
|
|
75
|
+
/**
|
|
76
|
+
* Get the current storage usage in bytes.
|
|
77
|
+
*/
|
|
78
|
+
getStorageUsage(): Promise<number>;
|
|
79
|
+
/**
|
|
80
|
+
* Get the configured storage quota in bytes.
|
|
81
|
+
*/
|
|
82
|
+
getStorageQuota(): number;
|
|
83
|
+
/**
|
|
84
|
+
* Get current usage as a percentage (0-100).
|
|
85
|
+
*/
|
|
86
|
+
getUsagePercent(): Promise<number>;
|
|
87
|
+
/**
|
|
88
|
+
* Check quota and emit events / perform eviction as needed.
|
|
89
|
+
*/
|
|
90
|
+
checkQuota(): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Track document creation for eviction purposes.
|
|
93
|
+
*/
|
|
94
|
+
trackCreation(collection: string, docId: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Track document access for LRU eviction.
|
|
97
|
+
*/
|
|
98
|
+
trackAccess(collection: string, docId: string): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Track document priority for priority-based eviction.
|
|
101
|
+
*/
|
|
102
|
+
trackPriority(collection: string, docId: string, priority: SyncPriority): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Evict documents to free at least the specified bytes.
|
|
105
|
+
* @param bytesToFree - Minimum bytes to free
|
|
106
|
+
* @returns Number of documents evicted
|
|
107
|
+
*/
|
|
108
|
+
evict(bytesToFree: number): Promise<number>;
|
|
109
|
+
/**
|
|
110
|
+
* Subscribe to storage warning events.
|
|
111
|
+
*/
|
|
112
|
+
onStorageWarning(callback: (event: StorageWarningEvent) => void): Unsubscribe;
|
|
113
|
+
/**
|
|
114
|
+
* Subscribe to storage full events.
|
|
115
|
+
*/
|
|
116
|
+
onStorageFull(callback: (event: StorageFullEvent) => void): Unsubscribe;
|
|
117
|
+
/**
|
|
118
|
+
* Get eviction candidates sorted by the configured policy.
|
|
119
|
+
*/
|
|
120
|
+
private getEvictionCandidates;
|
|
121
|
+
/**
|
|
122
|
+
* Create a unique key for document metadata.
|
|
123
|
+
*/
|
|
124
|
+
private metadataKey;
|
|
125
|
+
/**
|
|
126
|
+
* Emit storage warning event.
|
|
127
|
+
*/
|
|
128
|
+
private emitWarning;
|
|
129
|
+
/**
|
|
130
|
+
* Emit storage full event.
|
|
131
|
+
*/
|
|
132
|
+
private emitFull;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=quota-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-manager.d.ts","sourceRoot":"","sources":["../../src/persistence/quota-manager.ts"],"names":[],"mappings":"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"}
|