@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.
Files changed (186) hide show
  1. package/README.md +312 -0
  2. package/dist/client/attachment.d.ts +225 -0
  3. package/dist/client/attachment.d.ts.map +1 -0
  4. package/dist/client/attachment.js +402 -0
  5. package/dist/client/attachment.js.map +1 -0
  6. package/dist/client/binary-encoding.d.ts +45 -0
  7. package/dist/client/binary-encoding.d.ts.map +1 -0
  8. package/dist/client/binary-encoding.js +90 -0
  9. package/dist/client/binary-encoding.js.map +1 -0
  10. package/dist/client/collection.d.ts +10 -0
  11. package/dist/client/collection.d.ts.map +1 -0
  12. package/dist/client/collection.js +924 -0
  13. package/dist/client/collection.js.map +1 -0
  14. package/dist/client/compression.d.ts +56 -0
  15. package/dist/client/compression.d.ts.map +1 -0
  16. package/dist/client/compression.js +173 -0
  17. package/dist/client/compression.js.map +1 -0
  18. package/dist/client/crdt/index.d.ts +2 -0
  19. package/dist/client/crdt/index.d.ts.map +1 -0
  20. package/dist/client/crdt/index.js +2 -0
  21. package/dist/client/crdt/index.js.map +1 -0
  22. package/dist/client/crdt/yjs-doc.d.ts +88 -0
  23. package/dist/client/crdt/yjs-doc.d.ts.map +1 -0
  24. package/dist/client/crdt/yjs-doc.js +123 -0
  25. package/dist/client/crdt/yjs-doc.js.map +1 -0
  26. package/dist/client/index.d.ts +66 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +233 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client/mock-transport.d.ts +155 -0
  31. package/dist/client/mock-transport.d.ts.map +1 -0
  32. package/dist/client/mock-transport.js +292 -0
  33. package/dist/client/mock-transport.js.map +1 -0
  34. package/dist/client/network-detector.d.ts +65 -0
  35. package/dist/client/network-detector.d.ts.map +1 -0
  36. package/dist/client/network-detector.js +147 -0
  37. package/dist/client/network-detector.js.map +1 -0
  38. package/dist/client/provisioning.d.ts +126 -0
  39. package/dist/client/provisioning.d.ts.map +1 -0
  40. package/dist/client/provisioning.js +125 -0
  41. package/dist/client/provisioning.js.map +1 -0
  42. package/dist/client/signal.d.ts +13 -0
  43. package/dist/client/signal.d.ts.map +1 -0
  44. package/dist/client/signal.js +27 -0
  45. package/dist/client/signal.js.map +1 -0
  46. package/dist/client/sync-engine.d.ts +298 -0
  47. package/dist/client/sync-engine.d.ts.map +1 -0
  48. package/dist/client/sync-engine.js +904 -0
  49. package/dist/client/sync-engine.js.map +1 -0
  50. package/dist/client/synced-edge.d.ts +109 -0
  51. package/dist/client/synced-edge.d.ts.map +1 -0
  52. package/dist/client/synced-edge.js +179 -0
  53. package/dist/client/synced-edge.js.map +1 -0
  54. package/dist/client/synced-offline-edge-types.d.ts +540 -0
  55. package/dist/client/synced-offline-edge-types.d.ts.map +1 -0
  56. package/dist/client/synced-offline-edge-types.js +10 -0
  57. package/dist/client/synced-offline-edge-types.js.map +1 -0
  58. package/dist/client/synced-offline-edge.d.ts +54 -0
  59. package/dist/client/synced-offline-edge.d.ts.map +1 -0
  60. package/dist/client/synced-offline-edge.js +731 -0
  61. package/dist/client/synced-offline-edge.js.map +1 -0
  62. package/dist/client/transport.d.ts +202 -0
  63. package/dist/client/transport.d.ts.map +1 -0
  64. package/dist/client/transport.js +409 -0
  65. package/dist/client/transport.js.map +1 -0
  66. package/dist/client/types.d.ts +622 -0
  67. package/dist/client/types.d.ts.map +1 -0
  68. package/dist/client/types.js +60 -0
  69. package/dist/client/types.js.map +1 -0
  70. package/dist/client/validation.d.ts +61 -0
  71. package/dist/client/validation.d.ts.map +1 -0
  72. package/dist/client/validation.js +57 -0
  73. package/dist/client/validation.js.map +1 -0
  74. package/dist/client/versioning.d.ts +134 -0
  75. package/dist/client/versioning.d.ts.map +1 -0
  76. package/dist/client/versioning.js +304 -0
  77. package/dist/client/versioning.js.map +1 -0
  78. package/dist/index.d.ts +40 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +51 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/persistence/encryption.d.ts +114 -0
  83. package/dist/persistence/encryption.d.ts.map +1 -0
  84. package/dist/persistence/encryption.js +286 -0
  85. package/dist/persistence/encryption.js.map +1 -0
  86. package/dist/persistence/index.d.ts +21 -0
  87. package/dist/persistence/index.d.ts.map +1 -0
  88. package/dist/persistence/index.js +20 -0
  89. package/dist/persistence/index.js.map +1 -0
  90. package/dist/persistence/memory.d.ts +32 -0
  91. package/dist/persistence/memory.d.ts.map +1 -0
  92. package/dist/persistence/memory.js +57 -0
  93. package/dist/persistence/memory.js.map +1 -0
  94. package/dist/persistence/migrations.d.ts +106 -0
  95. package/dist/persistence/migrations.d.ts.map +1 -0
  96. package/dist/persistence/migrations.js +176 -0
  97. package/dist/persistence/migrations.js.map +1 -0
  98. package/dist/persistence/pending-queue.d.ts +109 -0
  99. package/dist/persistence/pending-queue.d.ts.map +1 -0
  100. package/dist/persistence/pending-queue.js +249 -0
  101. package/dist/persistence/pending-queue.js.map +1 -0
  102. package/dist/persistence/pglite.d.ts +72 -0
  103. package/dist/persistence/pglite.d.ts.map +1 -0
  104. package/dist/persistence/pglite.js +126 -0
  105. package/dist/persistence/pglite.js.map +1 -0
  106. package/dist/persistence/quota-manager.d.ts +134 -0
  107. package/dist/persistence/quota-manager.d.ts.map +1 -0
  108. package/dist/persistence/quota-manager.js +242 -0
  109. package/dist/persistence/quota-manager.js.map +1 -0
  110. package/dist/persistence/types.d.ts +54 -0
  111. package/dist/persistence/types.d.ts.map +1 -0
  112. package/dist/persistence/types.js +2 -0
  113. package/dist/persistence/types.js.map +1 -0
  114. package/dist/react/OfflineEdgeProvider.d.ts +91 -0
  115. package/dist/react/OfflineEdgeProvider.d.ts.map +1 -0
  116. package/dist/react/OfflineEdgeProvider.js +127 -0
  117. package/dist/react/OfflineEdgeProvider.js.map +1 -0
  118. package/dist/react/SyncedOfflineEdgeProvider.d.ts +105 -0
  119. package/dist/react/SyncedOfflineEdgeProvider.d.ts.map +1 -0
  120. package/dist/react/SyncedOfflineEdgeProvider.js +138 -0
  121. package/dist/react/SyncedOfflineEdgeProvider.js.map +1 -0
  122. package/dist/react/index.d.ts +50 -0
  123. package/dist/react/index.d.ts.map +1 -0
  124. package/dist/react/index.js +51 -0
  125. package/dist/react/index.js.map +1 -0
  126. package/dist/react/useCollection.d.ts +77 -0
  127. package/dist/react/useCollection.d.ts.map +1 -0
  128. package/dist/react/useCollection.js +113 -0
  129. package/dist/react/useCollection.js.map +1 -0
  130. package/dist/react/useCollectionSyncMode.d.ts +61 -0
  131. package/dist/react/useCollectionSyncMode.d.ts.map +1 -0
  132. package/dist/react/useCollectionSyncMode.js +93 -0
  133. package/dist/react/useCollectionSyncMode.js.map +1 -0
  134. package/dist/react/useConnectionState.d.ts +44 -0
  135. package/dist/react/useConnectionState.d.ts.map +1 -0
  136. package/dist/react/useConnectionState.js +46 -0
  137. package/dist/react/useConnectionState.js.map +1 -0
  138. package/dist/react/useDocumentSyncStatus.d.ts +72 -0
  139. package/dist/react/useDocumentSyncStatus.d.ts.map +1 -0
  140. package/dist/react/useDocumentSyncStatus.js +110 -0
  141. package/dist/react/useDocumentSyncStatus.js.map +1 -0
  142. package/dist/react/useOfflineEdge.d.ts +58 -0
  143. package/dist/react/useOfflineEdge.d.ts.map +1 -0
  144. package/dist/react/useOfflineEdge.js +54 -0
  145. package/dist/react/useOfflineEdge.js.map +1 -0
  146. package/dist/react/usePendingChanges.d.ts +67 -0
  147. package/dist/react/usePendingChanges.d.ts.map +1 -0
  148. package/dist/react/usePendingChanges.js +90 -0
  149. package/dist/react/usePendingChanges.js.map +1 -0
  150. package/dist/react/useRejectedDocuments.d.ts +112 -0
  151. package/dist/react/useRejectedDocuments.d.ts.map +1 -0
  152. package/dist/react/useRejectedDocuments.js +213 -0
  153. package/dist/react/useRejectedDocuments.js.map +1 -0
  154. package/dist/react/useSyncControls.d.ts +96 -0
  155. package/dist/react/useSyncControls.d.ts.map +1 -0
  156. package/dist/react/useSyncControls.js +112 -0
  157. package/dist/react/useSyncControls.js.map +1 -0
  158. package/dist/react/useSyncProgress.d.ts +78 -0
  159. package/dist/react/useSyncProgress.d.ts.map +1 -0
  160. package/dist/react/useSyncProgress.js +90 -0
  161. package/dist/react/useSyncProgress.js.map +1 -0
  162. package/dist/react/useSyncRejected.d.ts +47 -0
  163. package/dist/react/useSyncRejected.d.ts.map +1 -0
  164. package/dist/react/useSyncRejected.js +55 -0
  165. package/dist/react/useSyncRejected.js.map +1 -0
  166. package/dist/react/useSyncStatus.d.ts +56 -0
  167. package/dist/react/useSyncStatus.d.ts.map +1 -0
  168. package/dist/react/useSyncStatus.js +59 -0
  169. package/dist/react/useSyncStatus.js.map +1 -0
  170. package/dist/react/useSyncedOfflineEdge.d.ts +69 -0
  171. package/dist/react/useSyncedOfflineEdge.d.ts.map +1 -0
  172. package/dist/react/useSyncedOfflineEdge.js +65 -0
  173. package/dist/react/useSyncedOfflineEdge.js.map +1 -0
  174. package/dist/service-worker/index.d.ts +7 -0
  175. package/dist/service-worker/index.d.ts.map +1 -0
  176. package/dist/service-worker/index.js +7 -0
  177. package/dist/service-worker/index.js.map +1 -0
  178. package/dist/service-worker/sync-worker.d.ts +230 -0
  179. package/dist/service-worker/sync-worker.d.ts.map +1 -0
  180. package/dist/service-worker/sync-worker.js +471 -0
  181. package/dist/service-worker/sync-worker.js.map +1 -0
  182. package/dist/types.d.ts +6 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +3 -0
  185. package/dist/types.js.map +1 -0
  186. 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":"AAeA,OAAO,EAAE,eAAe,EAA6B,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,YAAY,EAAuB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,kBAAkB;AAClB,OAAO,EACL,eAAe,EACf,UAAU,EACV,aAAa,EACb,YAAY,GAyBb,MAAM,YAAY,CAAC;AAEpB,qBAAqB;AACrB,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GAEnB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,qBAAqB;IACR,WAAW,CAAsB;IACjC,MAAM,CAAoB;IAC1B,WAAW,GAAG,IAAI,GAAG,EAA2B,CAAC;IACjD,gBAAgB,CAAkC;IAEnE,uBAAuB;IACvB,+EAA+E;IACvE,iBAAiB,CAAqB;IAC7B,mBAAmB,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEhF,YAAY,WAAgC,EAAE,MAAyB;QACrE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAE9D,4CAA4C;QAC5C,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACzC,sDAAsD;YACtD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,yDAAyD;YACzD,IAAI,CAAC,iBAAiB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,UAAU,CAA2B,IAAY;QAC/C,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,gBAAgB,CAAI,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAClD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,GAAoB,CAAC;IAC9B,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,kBAAkB,CAAC,KAAsB;QACvC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,2DAA2D;QAC3D,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,cAAc,CAAC,IAAY;QACzB,4CAA4C;QAC5C,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,OAAO;QACT,CAAC;QAED,2BAA2B;QAC3B,IAAI,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,mBAAmB;QACnB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEjC,mBAAmB;QACnB,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAY;QACjC,iEAAiE;QACjE,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,8EAA8E;YAC9E,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,iBAAiB,GAAG,kBAAkB,CAAC;QAC9C,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,qCAAqC;YACrC,OAAO;QACT,CAAC;aAAM,CAAC;YACN,wBAAwB;YACxB,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,0CAA0C;QAC1C,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAErC,mBAAmB;QACnB,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,oBAAoB;QAClB,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACpC,sDAAsD;YACtD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC5C,CAAC;IAED,uBAAuB,CAAC,QAAuC;QAC7D,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,mBAAmB,CAAC,IAAY;QAC5C,iCAAiC;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE9C,IAAI,UAAU,EAAE,CAAC;YACf,2EAA2E;YAC3E,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,0DAA0D;YAC1D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC7C,CAAC;YAED,4CAA4C;YAC5C,MAAM,mBAAmB,GAAG,eAAe,IAAI,EAAE,CAAC;YAClD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACtE,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;gBACvC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;OAGG;IACK,gBAAgB,CAAC,UAAkB,EAAE,OAAgB;QAC3D,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAChD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB;;;;;OAKG;IACH,MAAM,CAAC,OAAiC;QACtC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACxC,IAAI,MAAM,GAAiC,IAAI,CAAC;QAChD,IAAI,WAAW,GAAG,KAAK,CAAC;QAExB,OAAO;YACL,KAAK,CAAC,IAAI;gBACR,IAAI,WAAW,EAAE,CAAC;oBAChB,OAAO;gBACT,CAAC;gBAED,6BAA6B;gBAC7B,MAAM,GAAG,IAAI,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;gBACxD,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YAED,GAAG;gBACD,IAAI,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAC9C,CAAC;gBACD,OAAO,MAAM,CAAC;YAChB,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,sBAAsB;AACtB,OAAO,EACL,gBAAgB,EAChB,eAAe,IAAI,mBAAmB,EACtC,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GAelB,MAAM,kBAAkB,CAAC;AAE1B,oBAAoB;AACpB,OAAO,EACL,mBAAmB,EACnB,aAAa,GAYd,MAAM,gBAAgB,CAAC;AAExB,6BAA6B;AAC7B,OAAO,EACL,mBAAmB,EACnB,aAAa,GAEd,MAAM,qBAAqB,CAAC;AAE7B,2BAA2B;AAC3B,OAAO,EACL,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,EACzB,YAAY,GAKb,MAAM,uBAAuB,CAAC;AAE/B,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,qBAAqB;AACrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GAQf,MAAM,iBAAiB,CAAC;AAEzB,mDAAmD;AACnD,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GAOtB,MAAM,mBAAmB,CAAC;AAc3B,4BAA4B;AAC5B,OAAO,EACL,uBAAuB,GAExB,MAAM,0BAA0B,CAAC"}
@@ -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