@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,233 @@
|
|
|
1
|
+
import { ConnectionState } from './types.js';
|
|
2
|
+
import { createSignal } from './signal.js';
|
|
3
|
+
import { createCollection } from './collection.js';
|
|
4
|
+
// Re-export types
|
|
5
|
+
export { ConnectionState, SyncStatus, SyncErrorCode, SyncPriority, } from './types.js';
|
|
6
|
+
// Validation exports
|
|
7
|
+
export { CollectionValidationError, createZodValidator, } from './validation.js';
|
|
8
|
+
/**
|
|
9
|
+
* Internal implementation of OfflineEdgeClient.
|
|
10
|
+
*/
|
|
11
|
+
class OfflineEdgeClientImpl {
|
|
12
|
+
persistence;
|
|
13
|
+
config;
|
|
14
|
+
collections = new Map();
|
|
15
|
+
_connectionState;
|
|
16
|
+
// Selective sync state
|
|
17
|
+
// null = sync all collections (default), Set = only sync specified collections
|
|
18
|
+
syncedCollections;
|
|
19
|
+
syncChangeCallbacks = new Set();
|
|
20
|
+
constructor(persistence, config) {
|
|
21
|
+
this.persistence = persistence;
|
|
22
|
+
this.config = config;
|
|
23
|
+
this._connectionState = createSignal(ConnectionState.Offline);
|
|
24
|
+
// Initialize selective sync based on config
|
|
25
|
+
if (config.syncCollections === undefined) {
|
|
26
|
+
// undefined = sync all collections (default behavior)
|
|
27
|
+
this.syncedCollections = null;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Array (empty or not) = only sync specified collections
|
|
31
|
+
this.syncedCollections = new Set(config.syncCollections);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
collection(name) {
|
|
35
|
+
let col = this.collections.get(name);
|
|
36
|
+
if (!col) {
|
|
37
|
+
col = createCollection(name, this.persistence);
|
|
38
|
+
this.collections.set(name, col);
|
|
39
|
+
}
|
|
40
|
+
return col;
|
|
41
|
+
}
|
|
42
|
+
get connectionState() {
|
|
43
|
+
return this._connectionState;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Internal method to update connection state.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
setConnectionState(state) {
|
|
50
|
+
this._connectionState.set(state);
|
|
51
|
+
}
|
|
52
|
+
isSyncing(name) {
|
|
53
|
+
// If syncedCollections is null, all collections are synced
|
|
54
|
+
if (this.syncedCollections === null) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return this.syncedCollections.has(name);
|
|
58
|
+
}
|
|
59
|
+
syncCollection(name) {
|
|
60
|
+
// If syncing all collections, nothing to do
|
|
61
|
+
if (this.syncedCollections === null) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Check if already syncing
|
|
65
|
+
if (this.syncedCollections.has(name)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Add to sync list
|
|
69
|
+
this.syncedCollections.add(name);
|
|
70
|
+
// Notify callbacks
|
|
71
|
+
this.notifySyncChange(name, true);
|
|
72
|
+
}
|
|
73
|
+
async unsyncCollection(name) {
|
|
74
|
+
// If syncing all collections, we need to switch to explicit mode
|
|
75
|
+
if (this.syncedCollections === null) {
|
|
76
|
+
// Get all currently used collection names and create a Set excluding this one
|
|
77
|
+
const currentCollections = new Set(this.collections.keys());
|
|
78
|
+
currentCollections.delete(name);
|
|
79
|
+
this.syncedCollections = currentCollections;
|
|
80
|
+
}
|
|
81
|
+
else if (!this.syncedCollections.has(name)) {
|
|
82
|
+
// Not syncing this collection anyway
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Remove from sync list
|
|
87
|
+
this.syncedCollections.delete(name);
|
|
88
|
+
}
|
|
89
|
+
// Clean up local data for this collection
|
|
90
|
+
await this.clearCollectionData(name);
|
|
91
|
+
// Notify callbacks
|
|
92
|
+
this.notifySyncChange(name, false);
|
|
93
|
+
}
|
|
94
|
+
getSyncedCollections() {
|
|
95
|
+
if (this.syncedCollections === null) {
|
|
96
|
+
// When syncing all, return all known collection names
|
|
97
|
+
return Array.from(this.collections.keys());
|
|
98
|
+
}
|
|
99
|
+
return Array.from(this.syncedCollections);
|
|
100
|
+
}
|
|
101
|
+
onSyncCollectionsChange(callback) {
|
|
102
|
+
this.syncChangeCallbacks.add(callback);
|
|
103
|
+
return () => {
|
|
104
|
+
this.syncChangeCallbacks.delete(callback);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clear all data for a collection from memory and persistence.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
async clearCollectionData(name) {
|
|
112
|
+
// Get existing collection if any
|
|
113
|
+
const collection = this.collections.get(name);
|
|
114
|
+
if (collection) {
|
|
115
|
+
// Use the collection's _clear method to clear in-memory and persisted data
|
|
116
|
+
await collection._clear();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// No collection instance, clear directly from persistence
|
|
120
|
+
const docIds = await this.persistence.list(name);
|
|
121
|
+
for (const docId of docIds) {
|
|
122
|
+
await this.persistence.delete(name, docId);
|
|
123
|
+
}
|
|
124
|
+
// Also clear tombstones for this collection
|
|
125
|
+
const tombstoneCollection = `_tombstones_${name}`;
|
|
126
|
+
const tombstoneIds = await this.persistence.list(tombstoneCollection);
|
|
127
|
+
for (const tombstoneId of tombstoneIds) {
|
|
128
|
+
await this.persistence.delete(tombstoneCollection, tombstoneId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Remove the collection from the cache so it gets recreated fresh
|
|
132
|
+
this.collections.delete(name);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Notify all sync change callbacks.
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
notifySyncChange(collection, syncing) {
|
|
139
|
+
for (const callback of this.syncChangeCallbacks) {
|
|
140
|
+
callback(collection, syncing);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Factory for creating offline-first edge clients.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* import { offlineEdge, ConnectionState } from '@fatagnus/dink-sync';
|
|
150
|
+
* import { MemoryPersistence } from '@fatagnus/dink-sync/persistence';
|
|
151
|
+
*
|
|
152
|
+
* // Create the edge instance
|
|
153
|
+
* const edge = offlineEdge.create({
|
|
154
|
+
* persistence: new MemoryPersistence(),
|
|
155
|
+
* config: {
|
|
156
|
+
* serverUrl: 'nats://localhost:4222',
|
|
157
|
+
* apiKey: 'your-api-key',
|
|
158
|
+
* },
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* // Initialize (loads persisted data)
|
|
162
|
+
* await edge.init();
|
|
163
|
+
*
|
|
164
|
+
* // Get the client
|
|
165
|
+
* const client = edge.get();
|
|
166
|
+
*
|
|
167
|
+
* // Use typed collections
|
|
168
|
+
* interface Task {
|
|
169
|
+
* id: string;
|
|
170
|
+
* title: string;
|
|
171
|
+
* completed: boolean;
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* const tasks = client.collection<Task>('tasks');
|
|
175
|
+
* const task = await tasks.insert({ title: 'Buy milk', completed: false });
|
|
176
|
+
*
|
|
177
|
+
* // Subscribe to changes
|
|
178
|
+
* tasks.subscribe((docs) => {
|
|
179
|
+
* console.log('Tasks updated:', docs);
|
|
180
|
+
* });
|
|
181
|
+
*
|
|
182
|
+
* // Check connection state
|
|
183
|
+
* client.connectionState.subscribe((state) => {
|
|
184
|
+
* console.log('Connection state:', state);
|
|
185
|
+
* });
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
export const offlineEdge = {
|
|
189
|
+
/**
|
|
190
|
+
* Create a new offline edge instance.
|
|
191
|
+
*
|
|
192
|
+
* @param options - Creation options including persistence and config
|
|
193
|
+
* @returns A lazy-initialized edge instance
|
|
194
|
+
*/
|
|
195
|
+
create(options) {
|
|
196
|
+
const { persistence, config } = options;
|
|
197
|
+
let client = null;
|
|
198
|
+
let initialized = false;
|
|
199
|
+
return {
|
|
200
|
+
async init() {
|
|
201
|
+
if (initialized) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
// Create the client instance
|
|
205
|
+
client = new OfflineEdgeClientImpl(persistence, config);
|
|
206
|
+
initialized = true;
|
|
207
|
+
},
|
|
208
|
+
get() {
|
|
209
|
+
if (!initialized || !client) {
|
|
210
|
+
throw new Error('Call init() before get()');
|
|
211
|
+
}
|
|
212
|
+
return client;
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
// Sync engine exports
|
|
218
|
+
export { createSyncEngine, ConnectionState as SyncConnectionState, DEFAULT_DEBOUNCE_MS, DEFAULT_MAX_RETRIES, DEFAULT_BATCH_SIZE, SyncRejectionCode, } from './sync-engine.js';
|
|
219
|
+
// Transport exports
|
|
220
|
+
export { createNatsTransport, NatsTransport, } from './transport.js';
|
|
221
|
+
// Mock transport for testing
|
|
222
|
+
export { createMockTransport, MockTransport, } from './mock-transport.js';
|
|
223
|
+
// Network detector exports
|
|
224
|
+
export { createNetworkDetector, createBrowserNetworkDetector, createNodeNetworkDetector, NetworkState, } from './network-detector.js';
|
|
225
|
+
// CRDT exports
|
|
226
|
+
export { YjsDocument } from './crdt/index.js';
|
|
227
|
+
// Attachment exports
|
|
228
|
+
export { AttachmentManager, AttachmentStorage, DownloadPolicy, } from './attachment.js';
|
|
229
|
+
// Provisioning exports (for demos and development)
|
|
230
|
+
export { createAdminClient, readAdminKeyWithRetry, } from './provisioning.js';
|
|
231
|
+
// SyncedOfflineEdge factory
|
|
232
|
+
export { createSyncedOfflineEdge, } from './synced-offline-edge.js';
|
|
233
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockTransport - A mock transport for testing sync engine without real NATS
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Configurable connection behavior
|
|
6
|
+
* - Manual control over sync responses
|
|
7
|
+
* - Ability to simulate rejections and errors
|
|
8
|
+
*/
|
|
9
|
+
import type { Transport, TransportState, StateChangeCallback, UpdateCallback, ErrorCallback, SyncChange, SyncResponse, SyncUpdate, StateVectorExchange, ForcePushResponse } from './transport.js';
|
|
10
|
+
export interface MockTransportConfig {
|
|
11
|
+
/** App ID to use */
|
|
12
|
+
appId?: string;
|
|
13
|
+
/** Edge ID to use */
|
|
14
|
+
edgeId?: string;
|
|
15
|
+
/** If true, connect() will succeed immediately */
|
|
16
|
+
autoConnect?: boolean;
|
|
17
|
+
/** If true, connect() will throw an error */
|
|
18
|
+
failConnect?: boolean;
|
|
19
|
+
/** Error message for failed connection */
|
|
20
|
+
connectError?: string;
|
|
21
|
+
/** Default response for sync operations */
|
|
22
|
+
defaultResponse?: 'success' | 'reject' | 'error';
|
|
23
|
+
/** Rejection error details */
|
|
24
|
+
rejectionError?: {
|
|
25
|
+
code: string;
|
|
26
|
+
field?: string;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* MockTransport for testing
|
|
32
|
+
*/
|
|
33
|
+
export declare class MockTransport implements Transport {
|
|
34
|
+
private state;
|
|
35
|
+
private stateCallbacks;
|
|
36
|
+
private updateCallbacks;
|
|
37
|
+
private errorCallbacks;
|
|
38
|
+
config: Required<Omit<MockTransportConfig, 'rejectionError'>> & {
|
|
39
|
+
rejectionError?: MockTransportConfig['rejectionError'];
|
|
40
|
+
};
|
|
41
|
+
sentBatches: {
|
|
42
|
+
collection: string;
|
|
43
|
+
changes: SyncChange[];
|
|
44
|
+
}[];
|
|
45
|
+
publishedBatches: {
|
|
46
|
+
collection: string;
|
|
47
|
+
changes: SyncChange[];
|
|
48
|
+
}[];
|
|
49
|
+
stateVectorRequests: {
|
|
50
|
+
collection: string;
|
|
51
|
+
docId: string;
|
|
52
|
+
localVector?: Uint8Array;
|
|
53
|
+
}[];
|
|
54
|
+
lastStateVectorRequest: {
|
|
55
|
+
collection: string;
|
|
56
|
+
docId: string;
|
|
57
|
+
localVector?: Uint8Array;
|
|
58
|
+
} | null;
|
|
59
|
+
snapshotRequests: {
|
|
60
|
+
collection: string;
|
|
61
|
+
docId: string;
|
|
62
|
+
}[];
|
|
63
|
+
forcePushRequests: {
|
|
64
|
+
collection: string;
|
|
65
|
+
docId: string;
|
|
66
|
+
stateVector: Uint8Array;
|
|
67
|
+
changes: Uint8Array[];
|
|
68
|
+
force: true;
|
|
69
|
+
}[];
|
|
70
|
+
private responseQueue;
|
|
71
|
+
private updateQueue;
|
|
72
|
+
private stateVectorResponseQueue;
|
|
73
|
+
private forcePushResponseQueue;
|
|
74
|
+
constructor(config?: MockTransportConfig);
|
|
75
|
+
getState(): TransportState;
|
|
76
|
+
getAppId(): string;
|
|
77
|
+
getEdgeId(): string;
|
|
78
|
+
onStateChange(callback: StateChangeCallback): () => void;
|
|
79
|
+
onUpdate(callback: UpdateCallback): () => void;
|
|
80
|
+
onError(callback: ErrorCallback): () => void;
|
|
81
|
+
connect(): Promise<void>;
|
|
82
|
+
disconnect(): Promise<void>;
|
|
83
|
+
sendBatch(collection: string, changes: SyncChange[]): Promise<SyncResponse[]>;
|
|
84
|
+
publish(collection: string, changes: SyncChange[]): void;
|
|
85
|
+
exchangeStateVector(collection: string, docId: string, localVector?: Uint8Array): Promise<StateVectorExchange>;
|
|
86
|
+
requestSnapshot(collection: string, docId: string): Promise<Uint8Array | null>;
|
|
87
|
+
forcePush(collection: string, docId: string, stateVector: Uint8Array, changes: Uint8Array[]): Promise<ForcePushResponse>;
|
|
88
|
+
/**
|
|
89
|
+
* Manually set the transport state
|
|
90
|
+
*/
|
|
91
|
+
setState(state: TransportState): void;
|
|
92
|
+
/**
|
|
93
|
+
* Queue a specific response for a document
|
|
94
|
+
*/
|
|
95
|
+
queueResponse(collection: string, docId: string, response: SyncResponse): void;
|
|
96
|
+
/**
|
|
97
|
+
* Queue a rejection for a document
|
|
98
|
+
*/
|
|
99
|
+
queueRejection(collection: string, docId: string, error: {
|
|
100
|
+
code: string;
|
|
101
|
+
field?: string;
|
|
102
|
+
message: string;
|
|
103
|
+
}): void;
|
|
104
|
+
/**
|
|
105
|
+
* Simulate an external update from center
|
|
106
|
+
*/
|
|
107
|
+
simulateUpdate(update: SyncUpdate): void;
|
|
108
|
+
private nextSendBatchError;
|
|
109
|
+
/**
|
|
110
|
+
* Simulate an error from the transport
|
|
111
|
+
*/
|
|
112
|
+
simulateError(error: Error): void;
|
|
113
|
+
/**
|
|
114
|
+
* Simulate a server rejection for the next sendBatch call.
|
|
115
|
+
* Sets the default response to 'reject' with the given error.
|
|
116
|
+
*/
|
|
117
|
+
simulateRejection(error: {
|
|
118
|
+
code: string;
|
|
119
|
+
field?: string;
|
|
120
|
+
message: string;
|
|
121
|
+
}): void;
|
|
122
|
+
/**
|
|
123
|
+
* Simulate a disconnection
|
|
124
|
+
*/
|
|
125
|
+
simulateDisconnect(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Simulate a reconnection
|
|
128
|
+
*/
|
|
129
|
+
simulateReconnect(): void;
|
|
130
|
+
/**
|
|
131
|
+
* Set a specific response for state vector exchange
|
|
132
|
+
*/
|
|
133
|
+
setStateVectorResponse(collection: string, docId: string, response: StateVectorExchange): void;
|
|
134
|
+
/**
|
|
135
|
+
* Set a specific response for force push
|
|
136
|
+
*/
|
|
137
|
+
setForcePushResponse(collection: string, docId: string, response: ForcePushResponse): void;
|
|
138
|
+
/**
|
|
139
|
+
* Reset all recorded calls
|
|
140
|
+
*/
|
|
141
|
+
reset(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Get the number of sendBatch calls made
|
|
144
|
+
*/
|
|
145
|
+
getSendBatchCallCount(): number;
|
|
146
|
+
/**
|
|
147
|
+
* Get the list of collections that have been synced
|
|
148
|
+
*/
|
|
149
|
+
getSyncedCollections(): string[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create a mock transport for testing
|
|
153
|
+
*/
|
|
154
|
+
export declare function createMockTransport(config?: MockTransportConfig): MockTransport;
|
|
155
|
+
//# sourceMappingURL=mock-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-transport.d.ts","sourceRoot":"","sources":["../../src/client/mock-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockTransport - A mock transport for testing sync engine without real NATS
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Configurable connection behavior
|
|
6
|
+
* - Manual control over sync responses
|
|
7
|
+
* - Ability to simulate rejections and errors
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* MockTransport for testing
|
|
11
|
+
*/
|
|
12
|
+
export class MockTransport {
|
|
13
|
+
state = 'disconnected';
|
|
14
|
+
stateCallbacks = new Set();
|
|
15
|
+
updateCallbacks = new Set();
|
|
16
|
+
errorCallbacks = new Set();
|
|
17
|
+
config;
|
|
18
|
+
// Recorded calls for assertions
|
|
19
|
+
sentBatches = [];
|
|
20
|
+
publishedBatches = [];
|
|
21
|
+
stateVectorRequests = [];
|
|
22
|
+
lastStateVectorRequest = null;
|
|
23
|
+
snapshotRequests = [];
|
|
24
|
+
forcePushRequests = [];
|
|
25
|
+
// Queued responses for specific documents
|
|
26
|
+
responseQueue = new Map();
|
|
27
|
+
updateQueue = [];
|
|
28
|
+
stateVectorResponseQueue = new Map();
|
|
29
|
+
forcePushResponseQueue = new Map();
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
this.config = {
|
|
32
|
+
appId: config.appId ?? 'test-app',
|
|
33
|
+
edgeId: config.edgeId ?? 'test-edge',
|
|
34
|
+
autoConnect: config.autoConnect ?? true,
|
|
35
|
+
failConnect: config.failConnect ?? false,
|
|
36
|
+
connectError: config.connectError ?? 'Mock connection failed',
|
|
37
|
+
defaultResponse: config.defaultResponse ?? 'success',
|
|
38
|
+
rejectionError: config.rejectionError,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
getState() {
|
|
42
|
+
return this.state;
|
|
43
|
+
}
|
|
44
|
+
getAppId() {
|
|
45
|
+
return this.config.appId;
|
|
46
|
+
}
|
|
47
|
+
getEdgeId() {
|
|
48
|
+
return this.config.edgeId;
|
|
49
|
+
}
|
|
50
|
+
onStateChange(callback) {
|
|
51
|
+
this.stateCallbacks.add(callback);
|
|
52
|
+
callback(this.state);
|
|
53
|
+
return () => this.stateCallbacks.delete(callback);
|
|
54
|
+
}
|
|
55
|
+
onUpdate(callback) {
|
|
56
|
+
this.updateCallbacks.add(callback);
|
|
57
|
+
// Deliver any queued updates
|
|
58
|
+
for (const update of this.updateQueue) {
|
|
59
|
+
callback(update);
|
|
60
|
+
}
|
|
61
|
+
this.updateQueue = [];
|
|
62
|
+
return () => this.updateCallbacks.delete(callback);
|
|
63
|
+
}
|
|
64
|
+
onError(callback) {
|
|
65
|
+
this.errorCallbacks.add(callback);
|
|
66
|
+
return () => this.errorCallbacks.delete(callback);
|
|
67
|
+
}
|
|
68
|
+
async connect() {
|
|
69
|
+
if (this.config.failConnect) {
|
|
70
|
+
throw new Error(this.config.connectError);
|
|
71
|
+
}
|
|
72
|
+
this.setState('connecting');
|
|
73
|
+
if (this.config.autoConnect) {
|
|
74
|
+
// Simulate async connection
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
76
|
+
this.setState('connected');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async disconnect() {
|
|
80
|
+
this.setState('disconnected');
|
|
81
|
+
}
|
|
82
|
+
async sendBatch(collection, changes) {
|
|
83
|
+
this.sentBatches.push({ collection, changes });
|
|
84
|
+
if (this.state !== 'connected') {
|
|
85
|
+
throw new Error('Not connected');
|
|
86
|
+
}
|
|
87
|
+
// Check if we should throw an error
|
|
88
|
+
if (this.nextSendBatchError) {
|
|
89
|
+
const error = this.nextSendBatchError;
|
|
90
|
+
this.nextSendBatchError = null;
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
return changes.map(change => {
|
|
94
|
+
const key = `${change.collection}:${change.docId}`;
|
|
95
|
+
const queuedResponse = this.responseQueue.get(key);
|
|
96
|
+
if (queuedResponse) {
|
|
97
|
+
this.responseQueue.delete(key);
|
|
98
|
+
return queuedResponse;
|
|
99
|
+
}
|
|
100
|
+
// Use default response
|
|
101
|
+
if (this.config.defaultResponse === 'reject') {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
docId: change.docId,
|
|
105
|
+
collection: change.collection,
|
|
106
|
+
error: this.config.rejectionError ?? {
|
|
107
|
+
code: 'UNKNOWN',
|
|
108
|
+
message: 'Mock rejection',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (this.config.defaultResponse === 'error') {
|
|
113
|
+
throw new Error('Mock sync error');
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
docId: change.docId,
|
|
118
|
+
collection: change.collection,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
publish(collection, changes) {
|
|
123
|
+
this.publishedBatches.push({ collection, changes });
|
|
124
|
+
if (this.state !== 'connected') {
|
|
125
|
+
throw new Error('Not connected');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async exchangeStateVector(collection, docId, localVector) {
|
|
129
|
+
const request = { collection, docId, localVector };
|
|
130
|
+
this.stateVectorRequests.push(request);
|
|
131
|
+
this.lastStateVectorRequest = request;
|
|
132
|
+
// Check for queued response
|
|
133
|
+
const key = `${collection}:${docId}`;
|
|
134
|
+
const queuedResponse = this.stateVectorResponseQueue.get(key);
|
|
135
|
+
if (queuedResponse) {
|
|
136
|
+
return queuedResponse;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
collection,
|
|
140
|
+
docId,
|
|
141
|
+
vector: new Uint8Array(0),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async requestSnapshot(collection, docId) {
|
|
145
|
+
this.snapshotRequests.push({ collection, docId });
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
async forcePush(collection, docId, stateVector, changes) {
|
|
149
|
+
this.forcePushRequests.push({
|
|
150
|
+
collection,
|
|
151
|
+
docId,
|
|
152
|
+
stateVector,
|
|
153
|
+
changes,
|
|
154
|
+
force: true,
|
|
155
|
+
});
|
|
156
|
+
if (this.state !== 'connected') {
|
|
157
|
+
throw new Error('Not connected');
|
|
158
|
+
}
|
|
159
|
+
// Check for queued response
|
|
160
|
+
const key = `${collection}:${docId}`;
|
|
161
|
+
const queuedResponse = this.forcePushResponseQueue.get(key);
|
|
162
|
+
if (queuedResponse) {
|
|
163
|
+
this.forcePushResponseQueue.delete(key);
|
|
164
|
+
return queuedResponse;
|
|
165
|
+
}
|
|
166
|
+
// Default success response
|
|
167
|
+
return { success: true };
|
|
168
|
+
}
|
|
169
|
+
// Test helper methods
|
|
170
|
+
/**
|
|
171
|
+
* Manually set the transport state
|
|
172
|
+
*/
|
|
173
|
+
setState(state) {
|
|
174
|
+
if (this.state !== state) {
|
|
175
|
+
this.state = state;
|
|
176
|
+
for (const callback of this.stateCallbacks) {
|
|
177
|
+
callback(state);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Queue a specific response for a document
|
|
183
|
+
*/
|
|
184
|
+
queueResponse(collection, docId, response) {
|
|
185
|
+
this.responseQueue.set(`${collection}:${docId}`, response);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Queue a rejection for a document
|
|
189
|
+
*/
|
|
190
|
+
queueRejection(collection, docId, error) {
|
|
191
|
+
this.queueResponse(collection, docId, {
|
|
192
|
+
success: false,
|
|
193
|
+
docId,
|
|
194
|
+
collection,
|
|
195
|
+
error,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Simulate an external update from center
|
|
200
|
+
*/
|
|
201
|
+
simulateUpdate(update) {
|
|
202
|
+
if (this.updateCallbacks.size === 0) {
|
|
203
|
+
this.updateQueue.push(update);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
for (const callback of this.updateCallbacks) {
|
|
207
|
+
callback(update);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Store the next error to throw on sendBatch
|
|
212
|
+
nextSendBatchError = null;
|
|
213
|
+
/**
|
|
214
|
+
* Simulate an error from the transport
|
|
215
|
+
*/
|
|
216
|
+
simulateError(error) {
|
|
217
|
+
this.nextSendBatchError = error;
|
|
218
|
+
for (const callback of this.errorCallbacks) {
|
|
219
|
+
callback(error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Simulate a server rejection for the next sendBatch call.
|
|
224
|
+
* Sets the default response to 'reject' with the given error.
|
|
225
|
+
*/
|
|
226
|
+
simulateRejection(error) {
|
|
227
|
+
this.config.defaultResponse = 'reject';
|
|
228
|
+
this.config.rejectionError = error;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Simulate a disconnection
|
|
232
|
+
*/
|
|
233
|
+
simulateDisconnect() {
|
|
234
|
+
this.setState('reconnecting');
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Simulate a reconnection
|
|
238
|
+
*/
|
|
239
|
+
simulateReconnect() {
|
|
240
|
+
this.setState('connected');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Set a specific response for state vector exchange
|
|
244
|
+
*/
|
|
245
|
+
setStateVectorResponse(collection, docId, response) {
|
|
246
|
+
this.stateVectorResponseQueue.set(`${collection}:${docId}`, response);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Set a specific response for force push
|
|
250
|
+
*/
|
|
251
|
+
setForcePushResponse(collection, docId, response) {
|
|
252
|
+
this.forcePushResponseQueue.set(`${collection}:${docId}`, response);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Reset all recorded calls
|
|
256
|
+
*/
|
|
257
|
+
reset() {
|
|
258
|
+
this.sentBatches = [];
|
|
259
|
+
this.publishedBatches = [];
|
|
260
|
+
this.stateVectorRequests = [];
|
|
261
|
+
this.lastStateVectorRequest = null;
|
|
262
|
+
this.stateVectorResponseQueue.clear();
|
|
263
|
+
this.snapshotRequests = [];
|
|
264
|
+
this.responseQueue.clear();
|
|
265
|
+
this.updateQueue = [];
|
|
266
|
+
this.forcePushRequests = [];
|
|
267
|
+
this.forcePushResponseQueue.clear();
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get the number of sendBatch calls made
|
|
271
|
+
*/
|
|
272
|
+
getSendBatchCallCount() {
|
|
273
|
+
return this.sentBatches.length;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get the list of collections that have been synced
|
|
277
|
+
*/
|
|
278
|
+
getSyncedCollections() {
|
|
279
|
+
const collections = new Set();
|
|
280
|
+
for (const batch of this.sentBatches) {
|
|
281
|
+
collections.add(batch.collection);
|
|
282
|
+
}
|
|
283
|
+
return Array.from(collections);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Create a mock transport for testing
|
|
288
|
+
*/
|
|
289
|
+
export function createMockTransport(config = {}) {
|
|
290
|
+
return new MockTransport(config);
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=mock-transport.js.map
|