@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,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eviction policy for when storage quota is exceeded.
|
|
3
|
+
*/
|
|
4
|
+
export const EvictionPolicy = {
|
|
5
|
+
/** Evict least recently used documents first */
|
|
6
|
+
LRU: 'lru',
|
|
7
|
+
/** Evict oldest documents first (by creation time) */
|
|
8
|
+
Oldest: 'oldest',
|
|
9
|
+
/** Evict lowest priority documents first */
|
|
10
|
+
Priority: 'priority',
|
|
11
|
+
};
|
|
12
|
+
/** Default quota: 50MB */
|
|
13
|
+
const DEFAULT_QUOTA_BYTES = 50 * 1024 * 1024;
|
|
14
|
+
/** Default warning threshold: 80% */
|
|
15
|
+
const DEFAULT_WARNING_THRESHOLD = 0.8;
|
|
16
|
+
/** Priority order for eviction (lower = evict first) */
|
|
17
|
+
const PRIORITY_EVICTION_ORDER = {
|
|
18
|
+
low: 0,
|
|
19
|
+
normal: 1,
|
|
20
|
+
high: 2,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Manages storage quota and eviction for persistence providers.
|
|
24
|
+
*/
|
|
25
|
+
export class QuotaManager {
|
|
26
|
+
persistence;
|
|
27
|
+
quotaBytes;
|
|
28
|
+
warningThreshold;
|
|
29
|
+
evictionPolicy;
|
|
30
|
+
autoEvict;
|
|
31
|
+
// Event callbacks
|
|
32
|
+
warningCallbacks = new Set();
|
|
33
|
+
fullCallbacks = new Set();
|
|
34
|
+
// Document metadata for eviction tracking
|
|
35
|
+
documentMetadata = new Map();
|
|
36
|
+
// Track if warning has been emitted to avoid duplicates
|
|
37
|
+
warningEmitted = false;
|
|
38
|
+
constructor(persistence, config = {}) {
|
|
39
|
+
this.persistence = persistence;
|
|
40
|
+
this.quotaBytes = config.quotaBytes ?? DEFAULT_QUOTA_BYTES;
|
|
41
|
+
this.warningThreshold = config.warningThreshold ?? DEFAULT_WARNING_THRESHOLD;
|
|
42
|
+
this.evictionPolicy = config.evictionPolicy ?? EvictionPolicy.Oldest;
|
|
43
|
+
this.autoEvict = config.autoEvict ?? false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the current storage usage in bytes.
|
|
47
|
+
*/
|
|
48
|
+
async getStorageUsage() {
|
|
49
|
+
let totalBytes = 0;
|
|
50
|
+
// Iterate through all tracked documents
|
|
51
|
+
for (const metadata of this.documentMetadata.values()) {
|
|
52
|
+
totalBytes += metadata.size;
|
|
53
|
+
}
|
|
54
|
+
return totalBytes;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the configured storage quota in bytes.
|
|
58
|
+
*/
|
|
59
|
+
getStorageQuota() {
|
|
60
|
+
return this.quotaBytes;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get current usage as a percentage (0-100).
|
|
64
|
+
*/
|
|
65
|
+
async getUsagePercent() {
|
|
66
|
+
const usage = await this.getStorageUsage();
|
|
67
|
+
return (usage / this.quotaBytes) * 100;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check quota and emit events / perform eviction as needed.
|
|
71
|
+
*/
|
|
72
|
+
async checkQuota() {
|
|
73
|
+
const usage = await this.getStorageUsage();
|
|
74
|
+
const usagePercent = (usage / this.quotaBytes) * 100;
|
|
75
|
+
// Check warning threshold
|
|
76
|
+
if (usagePercent >= this.warningThreshold * 100) {
|
|
77
|
+
if (!this.warningEmitted) {
|
|
78
|
+
this.emitWarning({
|
|
79
|
+
usageBytes: usage,
|
|
80
|
+
quotaBytes: this.quotaBytes,
|
|
81
|
+
usagePercent,
|
|
82
|
+
});
|
|
83
|
+
this.warningEmitted = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// Reset warning flag when below threshold
|
|
88
|
+
this.warningEmitted = false;
|
|
89
|
+
}
|
|
90
|
+
// Check if quota exceeded
|
|
91
|
+
if (usage > this.quotaBytes) {
|
|
92
|
+
const overageBytes = usage - this.quotaBytes;
|
|
93
|
+
this.emitFull({
|
|
94
|
+
usageBytes: usage,
|
|
95
|
+
quotaBytes: this.quotaBytes,
|
|
96
|
+
overageBytes,
|
|
97
|
+
});
|
|
98
|
+
// Auto-evict if enabled
|
|
99
|
+
if (this.autoEvict) {
|
|
100
|
+
await this.evict(overageBytes);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Track document creation for eviction purposes.
|
|
106
|
+
*/
|
|
107
|
+
async trackCreation(collection, docId) {
|
|
108
|
+
const key = this.metadataKey(collection, docId);
|
|
109
|
+
const data = await this.persistence.load(collection, docId);
|
|
110
|
+
const size = data?.length ?? 0;
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
this.documentMetadata.set(key, {
|
|
113
|
+
collection,
|
|
114
|
+
docId,
|
|
115
|
+
size,
|
|
116
|
+
createdAt: now,
|
|
117
|
+
lastAccessedAt: now,
|
|
118
|
+
priority: 'normal',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Track document access for LRU eviction.
|
|
123
|
+
*/
|
|
124
|
+
async trackAccess(collection, docId) {
|
|
125
|
+
const key = this.metadataKey(collection, docId);
|
|
126
|
+
const existing = this.documentMetadata.get(key);
|
|
127
|
+
if (existing) {
|
|
128
|
+
existing.lastAccessedAt = Date.now();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// If not tracked, start tracking
|
|
132
|
+
await this.trackCreation(collection, docId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Track document priority for priority-based eviction.
|
|
137
|
+
*/
|
|
138
|
+
async trackPriority(collection, docId, priority) {
|
|
139
|
+
const key = this.metadataKey(collection, docId);
|
|
140
|
+
const existing = this.documentMetadata.get(key);
|
|
141
|
+
if (existing) {
|
|
142
|
+
existing.priority = priority;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// If not tracked, start tracking
|
|
146
|
+
await this.trackCreation(collection, docId);
|
|
147
|
+
const metadata = this.documentMetadata.get(key);
|
|
148
|
+
if (metadata) {
|
|
149
|
+
metadata.priority = priority;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Evict documents to free at least the specified bytes.
|
|
155
|
+
* @param bytesToFree - Minimum bytes to free
|
|
156
|
+
* @returns Number of documents evicted
|
|
157
|
+
*/
|
|
158
|
+
async evict(bytesToFree) {
|
|
159
|
+
const candidates = this.getEvictionCandidates();
|
|
160
|
+
let freedBytes = 0;
|
|
161
|
+
let evictedCount = 0;
|
|
162
|
+
for (const metadata of candidates) {
|
|
163
|
+
if (freedBytes >= bytesToFree) {
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
// Delete the document
|
|
167
|
+
await this.persistence.delete(metadata.collection, metadata.docId);
|
|
168
|
+
// Remove from tracking
|
|
169
|
+
const key = this.metadataKey(metadata.collection, metadata.docId);
|
|
170
|
+
this.documentMetadata.delete(key);
|
|
171
|
+
freedBytes += metadata.size;
|
|
172
|
+
evictedCount++;
|
|
173
|
+
}
|
|
174
|
+
return evictedCount;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Subscribe to storage warning events.
|
|
178
|
+
*/
|
|
179
|
+
onStorageWarning(callback) {
|
|
180
|
+
this.warningCallbacks.add(callback);
|
|
181
|
+
return () => {
|
|
182
|
+
this.warningCallbacks.delete(callback);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Subscribe to storage full events.
|
|
187
|
+
*/
|
|
188
|
+
onStorageFull(callback) {
|
|
189
|
+
this.fullCallbacks.add(callback);
|
|
190
|
+
return () => {
|
|
191
|
+
this.fullCallbacks.delete(callback);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get eviction candidates sorted by the configured policy.
|
|
196
|
+
*/
|
|
197
|
+
getEvictionCandidates() {
|
|
198
|
+
const candidates = Array.from(this.documentMetadata.values());
|
|
199
|
+
switch (this.evictionPolicy) {
|
|
200
|
+
case EvictionPolicy.LRU:
|
|
201
|
+
// Sort by last access time (oldest first)
|
|
202
|
+
return candidates.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
|
|
203
|
+
case EvictionPolicy.Oldest:
|
|
204
|
+
// Sort by creation time (oldest first)
|
|
205
|
+
return candidates.sort((a, b) => a.createdAt - b.createdAt);
|
|
206
|
+
case EvictionPolicy.Priority:
|
|
207
|
+
// Sort by priority (lowest first), then by creation time
|
|
208
|
+
return candidates.sort((a, b) => {
|
|
209
|
+
const priorityDiff = PRIORITY_EVICTION_ORDER[a.priority] - PRIORITY_EVICTION_ORDER[b.priority];
|
|
210
|
+
if (priorityDiff !== 0) {
|
|
211
|
+
return priorityDiff;
|
|
212
|
+
}
|
|
213
|
+
return a.createdAt - b.createdAt;
|
|
214
|
+
});
|
|
215
|
+
default:
|
|
216
|
+
return candidates;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Create a unique key for document metadata.
|
|
221
|
+
*/
|
|
222
|
+
metadataKey(collection, docId) {
|
|
223
|
+
return `${collection}:${docId}`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Emit storage warning event.
|
|
227
|
+
*/
|
|
228
|
+
emitWarning(event) {
|
|
229
|
+
for (const callback of this.warningCallbacks) {
|
|
230
|
+
callback(event);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Emit storage full event.
|
|
235
|
+
*/
|
|
236
|
+
emitFull(event) {
|
|
237
|
+
for (const callback of this.fullCallbacks) {
|
|
238
|
+
callback(event);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=quota-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota-manager.js","sourceRoot":"","sources":["../../src/persistence/quota-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence provider interface for offline-first sync.
|
|
3
|
+
*
|
|
4
|
+
* Implementations handle storage of documents and state vectors
|
|
5
|
+
* for CRDT synchronization.
|
|
6
|
+
*/
|
|
7
|
+
export interface PersistenceProvider {
|
|
8
|
+
/**
|
|
9
|
+
* Save a document's data to storage.
|
|
10
|
+
* @param collection - Collection name
|
|
11
|
+
* @param docId - Document ID
|
|
12
|
+
* @param data - Binary data to store
|
|
13
|
+
*/
|
|
14
|
+
save(collection: string, docId: string, data: Uint8Array): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Load a document's data from storage.
|
|
17
|
+
* @param collection - Collection name
|
|
18
|
+
* @param docId - Document ID
|
|
19
|
+
* @returns The stored data, or undefined if not found
|
|
20
|
+
*/
|
|
21
|
+
load(collection: string, docId: string): Promise<Uint8Array | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* List all document IDs in a collection.
|
|
24
|
+
* @param collection - Collection name
|
|
25
|
+
* @returns Array of document IDs
|
|
26
|
+
*/
|
|
27
|
+
list(collection: string): Promise<string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Delete a document from storage.
|
|
30
|
+
* @param collection - Collection name
|
|
31
|
+
* @param docId - Document ID
|
|
32
|
+
*/
|
|
33
|
+
delete(collection: string, docId: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Save a state vector for a document.
|
|
36
|
+
* State vectors track sync progress for CRDT convergence.
|
|
37
|
+
* @param collection - Collection name
|
|
38
|
+
* @param docId - Document ID
|
|
39
|
+
* @param vector - Binary state vector data
|
|
40
|
+
*/
|
|
41
|
+
saveStateVector(collection: string, docId: string, vector: Uint8Array): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Load a state vector for a document.
|
|
44
|
+
* @param collection - Collection name
|
|
45
|
+
* @param docId - Document ID
|
|
46
|
+
* @returns The stored state vector, or undefined if not found
|
|
47
|
+
*/
|
|
48
|
+
loadStateVector(collection: string, docId: string): Promise<Uint8Array | undefined>;
|
|
49
|
+
/**
|
|
50
|
+
* Close the persistence provider and release resources.
|
|
51
|
+
*/
|
|
52
|
+
close?(): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/persistence/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzE;;;;;OAKG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAEzE;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE5C;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;OAMG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtF;;;;;OAKG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IAEpF;;OAEG;IACH,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/persistence/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OfflineEdgeProvider - React context provider for the offline edge client.
|
|
3
|
+
*
|
|
4
|
+
* Provides the OfflineEdgeClient to the component tree and handles
|
|
5
|
+
* initialization lifecycle including loading and error states.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { OfflineEdgeProvider, useOfflineEdgeClient } from '@fatagnus/dink-sync/react';
|
|
10
|
+
* import { MemoryPersistence } from '@fatagnus/dink-sync/persistence';
|
|
11
|
+
*
|
|
12
|
+
* function App() {
|
|
13
|
+
* return (
|
|
14
|
+
* <OfflineEdgeProvider
|
|
15
|
+
* config={{ serverUrl: 'nats://localhost:4222', apiKey: 'your-api-key' }}
|
|
16
|
+
* persistence={new MemoryPersistence()}
|
|
17
|
+
* >
|
|
18
|
+
* <TaskList />
|
|
19
|
+
* </OfflineEdgeProvider>
|
|
20
|
+
* );
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* function TaskList() {
|
|
24
|
+
* const client = useOfflineEdgeClient();
|
|
25
|
+
* const tasks = client.collection<Task>('tasks');
|
|
26
|
+
* // ...
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { type ReactNode } from 'react';
|
|
31
|
+
import type { OfflineEdgeClient, OfflineEdgeConfig } from '../client/types.js';
|
|
32
|
+
import type { PersistenceProvider } from '../persistence/types.js';
|
|
33
|
+
/**
|
|
34
|
+
* Props for the OfflineEdgeProvider component.
|
|
35
|
+
*/
|
|
36
|
+
export interface OfflineEdgeProviderProps {
|
|
37
|
+
/** Configuration for the offline edge client */
|
|
38
|
+
config: OfflineEdgeConfig;
|
|
39
|
+
/** Persistence provider for offline storage */
|
|
40
|
+
persistence: PersistenceProvider;
|
|
41
|
+
/** Children to render */
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
/** Optional callback for initialization errors */
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Internal context state.
|
|
48
|
+
*/
|
|
49
|
+
interface OfflineEdgeContextState {
|
|
50
|
+
/** The initialized client, or null if not yet ready */
|
|
51
|
+
client: OfflineEdgeClient | null;
|
|
52
|
+
/** True while initializing */
|
|
53
|
+
isLoading: boolean;
|
|
54
|
+
/** True when client is ready to use */
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
/** Error if initialization failed */
|
|
57
|
+
error: Error | null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Internal context for the offline edge provider.
|
|
61
|
+
* Exported for use by useOfflineEdge hook.
|
|
62
|
+
*/
|
|
63
|
+
export declare const OfflineEdgeContext: import("react").Context<OfflineEdgeContextState | null>;
|
|
64
|
+
/**
|
|
65
|
+
* OfflineEdgeProvider component.
|
|
66
|
+
*
|
|
67
|
+
* Wraps your app to provide the OfflineEdgeClient to all child components.
|
|
68
|
+
* Handles initialization on mount and cleanup on unmount.
|
|
69
|
+
*/
|
|
70
|
+
export declare function OfflineEdgeProvider({ config, persistence, children, onError, }: OfflineEdgeProviderProps): ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Hook to access the offline edge client directly.
|
|
73
|
+
*
|
|
74
|
+
* Throws if the client is not yet initialized. Use useOfflineEdge()
|
|
75
|
+
* if you need to handle loading states.
|
|
76
|
+
*
|
|
77
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
78
|
+
* @throws Error if client is not yet initialized
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* function TaskList() {
|
|
83
|
+
* const client = useOfflineEdgeClient();
|
|
84
|
+
* const tasks = client.collection<Task>('tasks');
|
|
85
|
+
* // ...
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function useOfflineEdgeClient(): OfflineEdgeClient;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=OfflineEdgeProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineEdgeProvider.d.ts","sourceRoot":"","sources":["../../src/react/OfflineEdgeProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,gDAAgD;IAChD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,mBAAmB,CAAC;IACjC,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,UAAU,uBAAuB;IAC/B,uDAAuD;IACvD,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,yDAAsD,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,WAAW,EACX,QAAQ,EACR,OAAO,GACR,EAAE,wBAAwB,GAAG,SAAS,CAmEtC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAYxD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* OfflineEdgeProvider - React context provider for the offline edge client.
|
|
4
|
+
*
|
|
5
|
+
* Provides the OfflineEdgeClient to the component tree and handles
|
|
6
|
+
* initialization lifecycle including loading and error states.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { OfflineEdgeProvider, useOfflineEdgeClient } from '@fatagnus/dink-sync/react';
|
|
11
|
+
* import { MemoryPersistence } from '@fatagnus/dink-sync/persistence';
|
|
12
|
+
*
|
|
13
|
+
* function App() {
|
|
14
|
+
* return (
|
|
15
|
+
* <OfflineEdgeProvider
|
|
16
|
+
* config={{ serverUrl: 'nats://localhost:4222', apiKey: 'your-api-key' }}
|
|
17
|
+
* persistence={new MemoryPersistence()}
|
|
18
|
+
* >
|
|
19
|
+
* <TaskList />
|
|
20
|
+
* </OfflineEdgeProvider>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* function TaskList() {
|
|
25
|
+
* const client = useOfflineEdgeClient();
|
|
26
|
+
* const tasks = client.collection<Task>('tasks');
|
|
27
|
+
* // ...
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import { createContext, useContext, useState, useEffect, useRef, } from 'react';
|
|
32
|
+
import { offlineEdge } from '../client/index.js';
|
|
33
|
+
/**
|
|
34
|
+
* Internal context for the offline edge provider.
|
|
35
|
+
* Exported for use by useOfflineEdge hook.
|
|
36
|
+
*/
|
|
37
|
+
export const OfflineEdgeContext = createContext(null);
|
|
38
|
+
/**
|
|
39
|
+
* OfflineEdgeProvider component.
|
|
40
|
+
*
|
|
41
|
+
* Wraps your app to provide the OfflineEdgeClient to all child components.
|
|
42
|
+
* Handles initialization on mount and cleanup on unmount.
|
|
43
|
+
*/
|
|
44
|
+
export function OfflineEdgeProvider({ config, persistence, children, onError, }) {
|
|
45
|
+
const [state, setState] = useState({
|
|
46
|
+
client: null,
|
|
47
|
+
isLoading: true,
|
|
48
|
+
isReady: false,
|
|
49
|
+
error: null,
|
|
50
|
+
});
|
|
51
|
+
// Track the edge instance for cleanup
|
|
52
|
+
const edgeRef = useRef(null);
|
|
53
|
+
const mountedRef = useRef(true);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
mountedRef.current = true;
|
|
56
|
+
const initializeClient = async () => {
|
|
57
|
+
try {
|
|
58
|
+
// Create the edge instance
|
|
59
|
+
const edge = offlineEdge.create({
|
|
60
|
+
persistence,
|
|
61
|
+
config,
|
|
62
|
+
});
|
|
63
|
+
edgeRef.current = edge;
|
|
64
|
+
// Initialize (loads persisted data)
|
|
65
|
+
await edge.init();
|
|
66
|
+
// Get the client
|
|
67
|
+
const client = edge.get();
|
|
68
|
+
if (mountedRef.current) {
|
|
69
|
+
setState({
|
|
70
|
+
client,
|
|
71
|
+
isLoading: false,
|
|
72
|
+
isReady: true,
|
|
73
|
+
error: null,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
79
|
+
if (mountedRef.current) {
|
|
80
|
+
setState({
|
|
81
|
+
client: null,
|
|
82
|
+
isLoading: false,
|
|
83
|
+
isReady: false,
|
|
84
|
+
error,
|
|
85
|
+
});
|
|
86
|
+
onError?.(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
initializeClient();
|
|
91
|
+
// Cleanup on unmount
|
|
92
|
+
return () => {
|
|
93
|
+
mountedRef.current = false;
|
|
94
|
+
edgeRef.current = null;
|
|
95
|
+
};
|
|
96
|
+
}, [config, persistence, onError]);
|
|
97
|
+
return (_jsx(OfflineEdgeContext.Provider, { value: state, children: children }));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Hook to access the offline edge client directly.
|
|
101
|
+
*
|
|
102
|
+
* Throws if the client is not yet initialized. Use useOfflineEdge()
|
|
103
|
+
* if you need to handle loading states.
|
|
104
|
+
*
|
|
105
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
106
|
+
* @throws Error if client is not yet initialized
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* function TaskList() {
|
|
111
|
+
* const client = useOfflineEdgeClient();
|
|
112
|
+
* const tasks = client.collection<Task>('tasks');
|
|
113
|
+
* // ...
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function useOfflineEdgeClient() {
|
|
118
|
+
const context = useContext(OfflineEdgeContext);
|
|
119
|
+
if (context === null) {
|
|
120
|
+
throw new Error('useOfflineEdgeClient must be used within an OfflineEdgeProvider');
|
|
121
|
+
}
|
|
122
|
+
if (!context.isReady || !context.client) {
|
|
123
|
+
throw new Error('OfflineEdgeClient is not yet initialized. Use useOfflineEdge() to handle loading states.');
|
|
124
|
+
}
|
|
125
|
+
return context.client;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=OfflineEdgeProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfflineEdgeProvider.js","sourceRoot":"","sources":["../../src/react/OfflineEdgeProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyncedOfflineEdgeProvider - React context provider for SyncedOfflineEdge.
|
|
3
|
+
*
|
|
4
|
+
* Provides the SyncedOfflineEdge instance to the component tree and handles
|
|
5
|
+
* initialization lifecycle including loading and error states.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { SyncedOfflineEdgeProvider, useSyncedOfflineEdgeClient } from '@fatagnus/dink-sync/react';
|
|
10
|
+
* import { MemoryPersistence } from '@fatagnus/dink-sync/persistence';
|
|
11
|
+
*
|
|
12
|
+
* function App() {
|
|
13
|
+
* return (
|
|
14
|
+
* <SyncedOfflineEdgeProvider
|
|
15
|
+
* config={{ serverUrl: 'nats://localhost:4222', apiKey: 'your-api-key' }}
|
|
16
|
+
* persistence={new MemoryPersistence()}
|
|
17
|
+
* syncConfig={{ tasks: 'auto', drafts: 'manual' }}
|
|
18
|
+
* >
|
|
19
|
+
* <TaskList />
|
|
20
|
+
* </SyncedOfflineEdgeProvider>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* function TaskList() {
|
|
25
|
+
* const { edge, isReady } = useSyncedOfflineEdgeClient();
|
|
26
|
+
* if (!isReady) return <Loading />;
|
|
27
|
+
*
|
|
28
|
+
* const tasks = edge.collection<Task>('tasks');
|
|
29
|
+
* // ...
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import { type ReactNode } from 'react';
|
|
34
|
+
import type { SyncedOfflineEdge, SyncConfigMap, SyncLifecycleHooks } from '../client/synced-offline-edge-types.js';
|
|
35
|
+
import type { OfflineEdgeConfig } from '../client/types.js';
|
|
36
|
+
import type { PersistenceProvider } from '../persistence/types.js';
|
|
37
|
+
import type { Transport } from '../client/transport.js';
|
|
38
|
+
/**
|
|
39
|
+
* Props for the SyncedOfflineEdgeProvider component.
|
|
40
|
+
*/
|
|
41
|
+
export interface SyncedOfflineEdgeProviderProps {
|
|
42
|
+
/** Configuration for the offline edge client */
|
|
43
|
+
config: OfflineEdgeConfig;
|
|
44
|
+
/** Persistence provider for offline storage */
|
|
45
|
+
persistence: PersistenceProvider;
|
|
46
|
+
/** Optional per-collection sync mode configuration */
|
|
47
|
+
syncConfig?: SyncConfigMap;
|
|
48
|
+
/** Optional lifecycle hooks for customizing sync behavior */
|
|
49
|
+
hooks?: SyncLifecycleHooks;
|
|
50
|
+
/** Children to render */
|
|
51
|
+
children: ReactNode;
|
|
52
|
+
/** Optional callback for initialization errors */
|
|
53
|
+
onError?: (error: Error) => void;
|
|
54
|
+
/** Optional custom transport for testing */
|
|
55
|
+
transport?: Transport;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Internal context state.
|
|
59
|
+
*/
|
|
60
|
+
interface SyncedOfflineEdgeContextState {
|
|
61
|
+
/** The initialized SyncedOfflineEdge, or null if not yet ready */
|
|
62
|
+
edge: SyncedOfflineEdge | null;
|
|
63
|
+
/** True while initializing */
|
|
64
|
+
isLoading: boolean;
|
|
65
|
+
/** True when edge is ready to use */
|
|
66
|
+
isReady: boolean;
|
|
67
|
+
/** Error if initialization failed */
|
|
68
|
+
error: Error | null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Internal context for the SyncedOfflineEdge provider.
|
|
72
|
+
* Exported for use by custom hooks.
|
|
73
|
+
*/
|
|
74
|
+
export declare const SyncedOfflineEdgeContext: import("react").Context<SyncedOfflineEdgeContextState | null>;
|
|
75
|
+
/**
|
|
76
|
+
* SyncedOfflineEdgeProvider component.
|
|
77
|
+
*
|
|
78
|
+
* Wraps your app to provide the SyncedOfflineEdge to all child components.
|
|
79
|
+
* Handles initialization on mount and cleanup on unmount.
|
|
80
|
+
*/
|
|
81
|
+
export declare function SyncedOfflineEdgeProvider({ config, persistence, syncConfig, hooks, children, onError, transport, }: SyncedOfflineEdgeProviderProps): ReactNode;
|
|
82
|
+
/**
|
|
83
|
+
* Hook to access the SyncedOfflineEdge context.
|
|
84
|
+
*
|
|
85
|
+
* Returns the full context state including loading state, error, and the edge instance.
|
|
86
|
+
*
|
|
87
|
+
* @throws Error if used outside of SyncedOfflineEdgeProvider
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* function TaskList() {
|
|
92
|
+
* const { edge, isLoading, isReady, error } = useSyncedOfflineEdgeClient();
|
|
93
|
+
*
|
|
94
|
+
* if (isLoading) return <Loading />;
|
|
95
|
+
* if (error) return <Error error={error} />;
|
|
96
|
+
* if (!isReady) return null;
|
|
97
|
+
*
|
|
98
|
+
* const tasks = edge.collection<Task>('tasks');
|
|
99
|
+
* // ...
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare function useSyncedOfflineEdgeClient(): SyncedOfflineEdgeContextState;
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=SyncedOfflineEdgeProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SyncedOfflineEdgeProvider.d.ts","sourceRoot":"","sources":["../../src/react/SyncedOfflineEdgeProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EACnB,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,gDAAgD;IAChD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,+CAA+C;IAC/C,WAAW,EAAE,mBAAmB,CAAC;IACjC,sDAAsD;IACtD,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,4CAA4C;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,UAAU,6BAA6B;IACrC,kEAAkE;IAClE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC/B,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,+DAA4D,CAAC;AAElG;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,MAAM,EACN,WAAW,EACX,UAAU,EACV,KAAK,EACL,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,8BAA8B,GAAG,SAAS,CAwE5C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,0BAA0B,IAAI,6BAA6B,CAQ1E"}
|