fix(realtime): use number[] for transport but ArrayBuffer for database

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2025-05-19 11:37:22 +02:00
parent 21a1f35281
commit 4869f3310c
No known key found for this signature in database
GPG key ID: DB99ADDDC5C0AF82
9 changed files with 37 additions and 28 deletions

View file

@ -31,7 +31,7 @@ export class RealtimeNoteStore {
const realtimeNote = new RealtimeNote(
noteId,
initialTextContent,
initialYjsState,
initialYjsState ? Array.from(new Uint8Array(initialYjsState)) : undefined,
);
realtimeNote.on('destroy', () => {
this.noteIdToRealtimeNote.delete(noteId);

View file

@ -49,7 +49,7 @@ export class RealtimeNoteService implements BeforeApplicationShutdown {
realtimeNote.getRealtimeDoc().getCurrentContent(),
false,
undefined,
realtimeNote.getRealtimeDoc().encodeStateAsUpdate(),
new Uint8Array(realtimeNote.getRealtimeDoc().encodeStateAsUpdate()),
)
.then(() => {
realtimeNote.announceMetadataUpdate();

View file

@ -34,7 +34,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
constructor(
private readonly noteId: number,
initialTextContent: string,
initialYjsState?: ArrayBuffer,
initialYjsState?: number[],
) {
super();
this.logger = new Logger(`${RealtimeNote.name} ${noteId}`);

View file

@ -29,8 +29,8 @@ export enum ConnectionStateEvent {
}
export interface MessagePayloads {
[MessageType.NOTE_CONTENT_STATE_REQUEST]: ArrayBuffer
[MessageType.NOTE_CONTENT_UPDATE]: ArrayBuffer
[MessageType.NOTE_CONTENT_STATE_REQUEST]: number[]
[MessageType.NOTE_CONTENT_UPDATE]: number[]
[MessageType.REALTIME_USER_STATE_SET]: {
users: RealtimeUser[]
ownUser: {

View file

@ -21,12 +21,12 @@ describe('realtime doc', () => {
it('restores a yjs state vector update correctly', () => {
const realtimeDoc = new RealtimeDoc(
'notTheVectorText',
new Uint8Array([
[
1, 1, 221, 208, 165, 230, 3, 0, 4, 1, 15, 109, 97, 114, 107, 100, 111,
119, 110, 67, 111, 110, 116, 101, 110, 116, 32, 116, 101, 120, 116, 67,
111, 110, 116, 101, 110, 116, 70, 114, 111, 109, 83, 116, 97, 116, 101,
86, 101, 99, 116, 111, 114, 85, 112, 100, 97, 116, 101, 0,
]),
],
)
expect(realtimeDoc.getCurrentContent()).toBe(

View file

@ -16,7 +16,7 @@ import {
const MARKDOWN_CONTENT_CHANNEL_NAME = 'markdownContent'
export interface RealtimeDocEvents extends EventMap {
update: (update: ArrayBuffer, origin: unknown) => void
update: (update: number[], origin: unknown) => void
}
/**
@ -37,16 +37,26 @@ export class RealtimeDoc extends EventEmitter2<RealtimeDocEvents> {
* @param initialTextContent the initial text content of the {@link Doc YDoc}
* @param initialYjsState the initial yjs state. If provided this will be used instead of the text content
*/
constructor(initialTextContent?: string, initialYjsState?: ArrayBuffer) {
constructor(initialTextContent?: string, initialYjsState?: number[]) {
super()
if (initialYjsState) {
console.debug(
'Creating new RealtimeDoc',
'initialYjsState',
initialYjsState,
'initialTextContent',
initialTextContent,
)
if (initialYjsState !== undefined) {
console.debug('Applying update')
this.applyUpdate(initialYjsState, this)
} else if (initialTextContent) {
} else if (initialTextContent !== undefined) {
console.debug('Setting initial text content')
this.getMarkdownContentChannel().insert(0, initialTextContent)
}
console.debug('Setting up listeners')
this.docUpdateListener = (update, origin) => {
this.emit('update', update, origin)
this.emit('update', Array.from(update), origin)
}
this.doc.on('update', this.docUpdateListener)
}
@ -77,13 +87,12 @@ export class RealtimeDoc extends EventEmitter2<RealtimeDocEvents> {
*
* @param encodedTargetStateVector The current state vector of the other y-doc. If provided the update will contain only the differences.
*/
public encodeStateAsUpdate(
encodedTargetStateVector?: ArrayBuffer,
): ArrayBuffer {
const update = encodedTargetStateVector
? new Uint8Array(encodedTargetStateVector)
: undefined
return encodeStateAsUpdate(this.doc, update)
public encodeStateAsUpdate(encodedTargetStateVector?: number[]): number[] {
const update =
encodedTargetStateVector !== undefined
? new Uint8Array(encodedTargetStateVector)
: undefined
return Array.from(encodeStateAsUpdate(this.doc, update))
}
public destroy(): void {
@ -97,11 +106,11 @@ export class RealtimeDoc extends EventEmitter2<RealtimeDocEvents> {
* @param payload The update to apply
* @param origin A reference that triggered the update
*/
public applyUpdate(payload: ArrayBuffer, origin: unknown): void {
public applyUpdate(payload: number[], origin: unknown): void {
applyUpdate(this.doc, new Uint8Array(payload), origin)
}
public encodeStateVector(): ArrayBuffer {
return encodeStateVector(this.doc)
public encodeStateVector(): number[] {
return Array.from(encodeStateVector(this.doc))
}
}

View file

@ -104,7 +104,7 @@ describe('y-doc-sync-adapter', () => {
console.log('s>2 is connected'),
)
docServer.on('update', (update: ArrayBuffer, origin: unknown) => {
docServer.on('update', (update: number[], origin: unknown) => {
const message: Message<MessageType.NOTE_CONTENT_UPDATE> = {
type: MessageType.NOTE_CONTENT_UPDATE,
payload: update,
@ -118,12 +118,12 @@ describe('y-doc-sync-adapter', () => {
messageTransporterServerTo2.sendMessage(message)
}
})
docClient1.on('update', (update: ArrayBuffer, origin: unknown) => {
docClient1.on('update', (update: number[], origin: unknown) => {
if (origin !== messageTransporterClient1) {
console.log('YDoc on client 1 updated. Sending to Server')
}
})
docClient2.on('update', (update: ArrayBuffer, origin: unknown) => {
docClient2.on('update', (update: number[], origin: unknown) => {
if (origin !== messageTransporterClient2) {
console.log('YDoc on client 2 updated. Sending to Server')
}

View file

@ -97,11 +97,11 @@ export abstract class YDocSyncAdapter {
}
}
protected applyIncomingUpdatePayload(update: ArrayBuffer): void {
protected applyIncomingUpdatePayload(update: number[]): void {
this.doc.applyUpdate(update, this)
}
private distributeDocUpdate(update: ArrayBuffer, origin: unknown): void {
private distributeDocUpdate(update: number[], origin: unknown): void {
if (!this.isSynced() || origin === this) {
return
}

View file

@ -17,7 +17,7 @@ export class YDocSyncServerAdapter extends YDocSyncAdapter {
this.markAsSynced()
}
protected applyIncomingUpdatePayload(update: ArrayBuffer): void {
protected applyIncomingUpdatePayload(update: number[]): void {
if (!this.acceptEditsProvider()) {
return
}