diff --git a/packages/workspace/src/providers/sqlite-providers.ts b/packages/workspace/src/providers/sqlite-providers.ts index 28dd9f94ec..624121d87b 100644 --- a/packages/workspace/src/providers/sqlite-providers.ts +++ b/packages/workspace/src/providers/sqlite-providers.ts @@ -53,7 +53,7 @@ export const createSQLiteProvider: DocProviderCreator = ( passive: true, connect: () => { datasource = createDatasource(id); - provider = createLazyProvider(rootDoc, datasource); + provider = createLazyProvider(rootDoc, datasource, { origin: 'sqlite' }); provider.connect(); connected = true; }, diff --git a/packages/y-indexeddb/src/provider.ts b/packages/y-indexeddb/src/provider.ts index 8005b34d77..295627572d 100644 --- a/packages/y-indexeddb/src/provider.ts +++ b/packages/y-indexeddb/src/provider.ts @@ -114,7 +114,7 @@ export const createIndexedDBProvider = ( return { connect: () => { datasource = createDatasource({ dbName, mergeCount }); - provider = createLazyProvider(doc, datasource); + provider = createLazyProvider(doc, datasource, { origin: 'idb' }); provider.connect(); }, disconnect: () => { diff --git a/packages/y-provider/src/__tests__/index.spec.ts b/packages/y-provider/src/__tests__/index.spec.ts index f8f068b475..30b21a29f7 100644 --- a/packages/y-provider/src/__tests__/index.spec.ts +++ b/packages/y-provider/src/__tests__/index.spec.ts @@ -1,6 +1,6 @@ import { setTimeout } from 'node:timers/promises'; -import { describe, expect, test } from 'vitest'; +import { describe, expect, test, vi } from 'vitest'; import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'; import { createLazyProvider } from '../lazy-provider'; @@ -178,4 +178,19 @@ describe('y-provider', () => { expect(provider.connected).toBe(false); }); + + test('should not send remote update back', async () => { + const remoteRootDoc = new Doc(); // this is the remote doc lives in remote + const datasource = createMemoryDatasource(remoteRootDoc); + const spy = vi.spyOn(datasource, 'sendDocUpdate'); + + const rootDoc = new Doc({ guid: remoteRootDoc.guid }); // this is the doc that we want to sync + const provider = createLazyProvider(rootDoc, datasource); + + provider.connect(); + + remoteRootDoc.getText('text').insert(0, 'test-value'); + + expect(spy).not.toBeCalled(); + }); }); diff --git a/packages/y-provider/src/lazy-provider.ts b/packages/y-provider/src/lazy-provider.ts index 9413920853..33fb22fbfa 100644 --- a/packages/y-provider/src/lazy-provider.ts +++ b/packages/y-provider/src/lazy-provider.ts @@ -9,8 +9,6 @@ import { import type { DatasourceDocAdapter } from './types'; -const selfUpdateOrigin = 'lazy-provider-self-origin'; - function getDoc(doc: Doc, guid: string): Doc | undefined { if (doc.guid === guid) { return doc; @@ -24,12 +22,17 @@ function getDoc(doc: Doc, guid: string): Doc | undefined { return undefined; } +interface LazyProviderOptions { + origin?: string; +} + /** * Creates a lazy provider that connects to a datasource and synchronizes a root document. */ export const createLazyProvider = ( rootDoc: Doc, - datasource: DatasourceDocAdapter + datasource: DatasourceDocAdapter, + options: LazyProviderOptions = {} ): Omit => { let connected = false; const pendingMap = new Map(); // guid -> pending-updates @@ -37,6 +40,8 @@ export const createLazyProvider = ( const connectedDocs = new Set(); let datasourceUnsub: (() => void) | undefined; + const { origin = 'lazy-provider' } = options; + async function syncDoc(doc: Doc) { const guid = doc.guid; @@ -47,7 +52,7 @@ export const createLazyProvider = ( pendingMap.set(guid, []); if (remoteUpdate) { - applyUpdate(doc, remoteUpdate, selfUpdateOrigin); + applyUpdate(doc, remoteUpdate, origin); } const sv = remoteUpdate @@ -67,8 +72,8 @@ export const createLazyProvider = ( function setupDocListener(doc: Doc) { const disposables = new Set<() => void>(); disposableMap.set(doc.guid, disposables); - const updateHandler = async (update: Uint8Array, origin: unknown) => { - if (origin === selfUpdateOrigin) { + const updateHandler = async (update: Uint8Array, updateOrigin: unknown) => { + if (origin === updateOrigin) { return; } datasource.sendDocUpdate(doc.guid, update).catch(console.error); @@ -100,10 +105,12 @@ export const createLazyProvider = ( datasourceUnsub = datasource.onDocUpdate?.((guid, update) => { const doc = getDoc(rootDoc, guid); if (doc) { - applyUpdate(doc, update); + applyUpdate(doc, update, origin); // if (pendingMap.has(guid)) { - pendingMap.get(guid)?.forEach(update => applyUpdate(doc, update)); + pendingMap + .get(guid) + ?.forEach(update => applyUpdate(doc, update, origin)); pendingMap.delete(guid); } } else {