diff --git a/packages/server/src/DirectConnection.ts b/packages/server/src/DirectConnection.ts index a4cebef2..7ecad0a7 100644 --- a/packages/server/src/DirectConnection.ts +++ b/packages/server/src/DirectConnection.ts @@ -42,7 +42,7 @@ export class DirectConnection implements DirectConnectionInterface { requestParameters: new URLSearchParams(), socketId: 'server', transactionOrigin, - }) + }, true) } async disconnect() { @@ -59,7 +59,7 @@ export class DirectConnection implements DirectConnectionInterface { requestHeaders: {}, requestParameters: new URLSearchParams(), socketId: 'server', - }) + }, true) this.document = null } diff --git a/packages/server/src/Hocuspocus.ts b/packages/server/src/Hocuspocus.ts index a9d3f78a..14877ba5 100644 --- a/packages/server/src/Hocuspocus.ts +++ b/packages/server/src/Hocuspocus.ts @@ -378,7 +378,7 @@ export class Hocuspocus { * "connection" is not necessarily type "Connection", it's the Yjs "origin" (which is "Connection" if * the update is incoming from the provider, but can be anything if the updates is originated from an extension. */ - private handleDocumentUpdate(document: Document, connection: Connection | undefined, update: Uint8Array, request?: IncomingMessage): void { + private async handleDocumentUpdate(document: Document, connection: Connection | undefined, update: Uint8Array, request?: IncomingMessage) { const hookPayload: onChangePayload | onStoreDocumentPayload = { instance: this, clientsCount: document.getConnectionsCount(), @@ -405,12 +405,7 @@ export class Hocuspocus { return } - this.debouncer.debounce( - `onStoreDocument-${document.name}`, - () => this.storeDocumentHooks(document, hookPayload), - this.configuration.debounce, - this.configuration.maxDebounce, - ) + await this.storeDocumentHooks(document, hookPayload) } /** @@ -521,26 +516,34 @@ export class Hocuspocus { return document } - storeDocumentHooks(document: Document, hookPayload: onStoreDocumentPayload) { - return this.hooks('onStoreDocument', hookPayload) - .then(() => { - this.hooks('afterStoreDocument', hookPayload).then(() => { - // Remove document from memory. - - if (document.getConnectionsCount() > 0) { - return - } + storeDocumentHooks(document: Document, hookPayload: onStoreDocumentPayload, immediately?: boolean) { + return this.debouncer.debounce( + `onStoreDocument-${document.name}`, + () => { + return this.hooks('onStoreDocument', hookPayload) + .then(() => { + this.hooks('afterStoreDocument', hookPayload).then(() => { + // Remove document from memory. + + if (document.getConnectionsCount() > 0) { + return + } + + this.unloadDocument(document) + }) + }) + .catch(error => { + console.error('Caught error during storeDocumentHooks', error) - this.unloadDocument(document) - }) - }) - .catch(error => { - console.error('Caught error during storeDocumentHooks', error) + if (error?.message) { + throw error + } + }) + }, + immediately ? 0 : this.configuration.debounce, + this.configuration.maxDebounce, + ) - if (error?.message) { - throw error - } - }) } /** diff --git a/tests/server/openDirectConnection.ts b/tests/server/openDirectConnection.ts index 00bdd072..aac0d8e6 100644 --- a/tests/server/openDirectConnection.ts +++ b/tests/server/openDirectConnection.ts @@ -218,3 +218,49 @@ test('direct connection transact awaits until onStoreDocument has finished, even resolve('done') }) }) + +test('creating a websocket connection after transact but before debounce interval doesnt create different docs', async t => { + let onStoreDocumentFinished = false + let disconnected = false + + await new Promise(async resolve => { + const server = await newHocuspocus({ + onStoreDocument: async () => { + onStoreDocumentFinished = false + await sleep(200) + onStoreDocumentFinished = true + }, + async afterUnloadDocument(data) { + console.log('called') + if (disconnected) { + t.fail('must not be called') + } + }, + }) + + const direct = await server.openDirectConnection('hocuspocus-test') + t.is(server.getDocumentsCount(), 1) + t.is(server.getConnectionsCount(), 1) + + t.is(onStoreDocumentFinished, false) + await direct.transact(document => { + document.transact(() => { + document.getArray('test').insert(0, ['value']) + }, 'testOrigin') + }) + t.is(onStoreDocumentFinished, true) + + await direct.disconnect() + disconnected = true + + t.is(server.getConnectionsCount(), 0) + t.is(server.getDocumentsCount(), 0) + t.is(onStoreDocumentFinished, true) + + const provider = newHocuspocusProvider(server) + + await sleep(server.configuration.debounce * 2) + + resolve('done') + }) +})