@electric-sql/y-electric 0.1.21 → 0.1.23
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 +13 -6
- package/dist/cjs/index.cjs +43 -22
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +8 -6
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.legacy-esm.js +43 -22
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +43 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/local-storage-resume-state.ts +0 -1
- package/src/types.ts +2 -5
- package/src/y-electric.ts +50 -26
package/src/y-electric.ts
CHANGED
|
@@ -56,6 +56,8 @@ export class ElectricProvider<
|
|
|
56
56
|
private pendingChanges: Uint8Array | null = null
|
|
57
57
|
private sendingAwarenessState: boolean = false
|
|
58
58
|
private pendingAwarenessUpdate: AwarenessUpdate | null = null
|
|
59
|
+
private debounceMs: number
|
|
60
|
+
private debounceTimer: ReturnType<typeof setTimeout> | null = null
|
|
59
61
|
|
|
60
62
|
private documentUpdateHandler: (
|
|
61
63
|
update: Uint8Array,
|
|
@@ -93,6 +95,7 @@ export class ElectricProvider<
|
|
|
93
95
|
* @param {ResumeState} [options.resumeState] - Resume state for the provider
|
|
94
96
|
* @param {boolean} [options.connect=true] - Whether to automatically connect upon initialization
|
|
95
97
|
* @param {typeof fetch} [options.fetchClient] - Custom fetch implementation to use for HTTP requests
|
|
98
|
+
* @param {number} [options.debounceMs] - Debounce window in milliseconds for sending document updates. If 0 or undefined, debouncing is disabled.
|
|
96
99
|
*/
|
|
97
100
|
constructor({
|
|
98
101
|
doc,
|
|
@@ -101,6 +104,7 @@ export class ElectricProvider<
|
|
|
101
104
|
resumeState,
|
|
102
105
|
connect = true,
|
|
103
106
|
fetchClient,
|
|
107
|
+
debounceMs,
|
|
104
108
|
}: ElectricProviderOptions<RowWithDocumentUpdate, RowWithAwarenessUpdate>) {
|
|
105
109
|
super()
|
|
106
110
|
|
|
@@ -108,6 +112,7 @@ export class ElectricProvider<
|
|
|
108
112
|
this.documentUpdates = documentUpdatesConfig
|
|
109
113
|
this.awarenessUpdates = awarenessUpdatesConfig
|
|
110
114
|
this.resumeState = resumeState ?? {}
|
|
115
|
+
this.debounceMs = debounceMs ?? 0
|
|
111
116
|
|
|
112
117
|
this.fetchClient = fetchClient
|
|
113
118
|
|
|
@@ -174,7 +179,35 @@ export class ElectricProvider<
|
|
|
174
179
|
}
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
private clearDebounceTimer() {
|
|
183
|
+
if (this.debounceTimer !== null) {
|
|
184
|
+
clearTimeout(this.debounceTimer)
|
|
185
|
+
this.debounceTimer = null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private scheduleSendOperations() {
|
|
190
|
+
if (this.debounceMs > 0) {
|
|
191
|
+
if (this.debounceTimer === null) {
|
|
192
|
+
this.debounceTimer = setTimeout(async () => {
|
|
193
|
+
this.debounceTimer = null
|
|
194
|
+
await this.sendOperations()
|
|
195
|
+
if (
|
|
196
|
+
this.pendingChanges &&
|
|
197
|
+
this.connected &&
|
|
198
|
+
!this.sendingPendingChanges
|
|
199
|
+
) {
|
|
200
|
+
this.scheduleSendOperations()
|
|
201
|
+
}
|
|
202
|
+
}, this.debounceMs)
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
this.sendOperations()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
177
209
|
destroy() {
|
|
210
|
+
this.clearDebounceTimer()
|
|
178
211
|
this.disconnect()
|
|
179
212
|
|
|
180
213
|
this.doc.off(`update`, this.documentUpdateHandler)
|
|
@@ -187,6 +220,12 @@ export class ElectricProvider<
|
|
|
187
220
|
}
|
|
188
221
|
|
|
189
222
|
disconnect() {
|
|
223
|
+
// Flush any pending changes before disconnecting
|
|
224
|
+
this.clearDebounceTimer()
|
|
225
|
+
if (this.pendingChanges && this.connected) {
|
|
226
|
+
this.sendOperations()
|
|
227
|
+
}
|
|
228
|
+
|
|
190
229
|
this.unsubscribeShapes?.()
|
|
191
230
|
|
|
192
231
|
if (!this.connected) {
|
|
@@ -234,7 +273,7 @@ export class ElectricProvider<
|
|
|
234
273
|
})
|
|
235
274
|
|
|
236
275
|
const operationsShapeUnsubscribe = operationsStream.subscribe(
|
|
237
|
-
(messages) => {
|
|
276
|
+
(messages: Message<RowWithDocumentUpdate>[]) => {
|
|
238
277
|
this.operationsShapeHandler(
|
|
239
278
|
messages,
|
|
240
279
|
operationsStream.lastOffset,
|
|
@@ -247,17 +286,15 @@ export class ElectricProvider<
|
|
|
247
286
|
if (this.awarenessUpdates) {
|
|
248
287
|
const awarenessStream = new ShapeStream<RowWithAwarenessUpdate>({
|
|
249
288
|
...this.awarenessUpdates.shape,
|
|
250
|
-
...this.resumeState.awareness,
|
|
251
289
|
signal: abortController.signal,
|
|
290
|
+
offset: `now`,
|
|
252
291
|
})
|
|
253
292
|
|
|
254
|
-
awarenessShapeUnsubscribe = awarenessStream.subscribe(
|
|
255
|
-
|
|
256
|
-
messages
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
)
|
|
260
|
-
})
|
|
293
|
+
awarenessShapeUnsubscribe = awarenessStream.subscribe(
|
|
294
|
+
(messages: Message<RowWithAwarenessUpdate>[]) => {
|
|
295
|
+
this.awarenessShapeHandler(messages)
|
|
296
|
+
}
|
|
297
|
+
)
|
|
261
298
|
}
|
|
262
299
|
|
|
263
300
|
this.unsubscribeShapes = () => {
|
|
@@ -301,8 +338,6 @@ export class ElectricProvider<
|
|
|
301
338
|
}
|
|
302
339
|
}
|
|
303
340
|
|
|
304
|
-
// TODO: add an optional throttler that batches updates
|
|
305
|
-
// before pushing to the server
|
|
306
341
|
private async applyDocumentUpdate(update: Uint8Array, origin: unknown) {
|
|
307
342
|
// don't re-send updates from electric
|
|
308
343
|
if (origin === `server`) {
|
|
@@ -310,10 +345,12 @@ export class ElectricProvider<
|
|
|
310
345
|
}
|
|
311
346
|
|
|
312
347
|
this.batch(update)
|
|
313
|
-
this.
|
|
348
|
+
this.scheduleSendOperations()
|
|
314
349
|
}
|
|
315
350
|
|
|
316
351
|
private async sendOperations() {
|
|
352
|
+
this.clearDebounceTimer()
|
|
353
|
+
|
|
317
354
|
if (!this.connected || this.sendingPendingChanges) {
|
|
318
355
|
return
|
|
319
356
|
}
|
|
@@ -397,11 +434,7 @@ export class ElectricProvider<
|
|
|
397
434
|
}
|
|
398
435
|
}
|
|
399
436
|
|
|
400
|
-
private awarenessShapeHandler(
|
|
401
|
-
messages: Message<RowWithAwarenessUpdate>[],
|
|
402
|
-
offset: Offset,
|
|
403
|
-
handle: string
|
|
404
|
-
) {
|
|
437
|
+
private awarenessShapeHandler(messages: Message<RowWithAwarenessUpdate>[]) {
|
|
405
438
|
for (const message of messages) {
|
|
406
439
|
if (isChangeMessage(message)) {
|
|
407
440
|
if (message.headers.operation === `delete`) {
|
|
@@ -418,15 +451,6 @@ export class ElectricProvider<
|
|
|
418
451
|
this
|
|
419
452
|
)
|
|
420
453
|
}
|
|
421
|
-
} else if (
|
|
422
|
-
isControlMessage(message) &&
|
|
423
|
-
message.headers.control === `up-to-date`
|
|
424
|
-
) {
|
|
425
|
-
this.resumeState.awareness = {
|
|
426
|
-
offset: offset,
|
|
427
|
-
handle: handle,
|
|
428
|
-
}
|
|
429
|
-
this.emit(`resumeState`, [this.resumeState])
|
|
430
454
|
}
|
|
431
455
|
}
|
|
432
456
|
}
|