staff_data/packages/common/src/collaboration/y-sync.ts

190 lines
6.0 KiB
TypeScript
Raw Normal View History

2024-12-30 08:26:40 +08:00
/**
* @module sync-protocol
*
*/
import * as encoding from 'lib0/encoding'
import * as decoding from 'lib0/decoding'
import * as Y from 'yjs'
/**
* StateMap
*/
export type StateMap = Map<number, number>
/**
* Yjs
* YjsSyncStep1: 包含发送客户端的状态集 YjsSyncStep2
* YjsSyncStep2: 包含所有缺失的结构和完整的删除集
*
*
* SyncDone SyncStep1
* SyncStep2 SyncDone SyncStep2
* SyncDone
*
* - SyncStep1
* SyncStep1 SyncStep2 SyncStep1
* SyncStep1 SyncStep2 SyncStep2 SyncDone
* 1.
* http websockets 2.
*
*
*
* [messageType : varUint, message definition..]
*
*
*
* stringify[messageType] (messageType )
*/
export const messageYjsSyncStep1: number = 0
export const messageYjsSyncStep2: number = 1
export const messageYjsUpdate: number = 2
/**
* 1
* ,
*
* @param encoder -
* @param doc -
*/
export const writeSyncStep1 = (encoder: encoding.Encoder, doc: Y.Doc): void => {
// 写入消息类型标识
encoding.writeVarUint(encoder, messageYjsSyncStep1)
// 获取并编码当前文档的状态向量
// 状态向量记录了每个客户端最新的更新序号
const sv = Y.encodeStateVector(doc)
// 将状态向量写入为变长字节数组
encoding.writeVarUint8Array(encoder, sv)
}
/**
* 2
* ,
*
* @param encoder -
* @param doc -
* @param encodedStateVector - ()
*/
export const writeSyncStep2 = (
encoder: encoding.Encoder,
doc: Y.Doc,
encodedStateVector?: Uint8Array
): void => {
// 写入消息类型标识
encoding.writeVarUint(encoder, messageYjsSyncStep2)
// 根据对方状态向量编码增量更新
// 只发送对方缺少的更新内容
encoding.writeVarUint8Array(encoder, Y.encodeStateAsUpdate(doc, encodedStateVector))
}
/**
* 1
* 2
*
* @param decoder -
* @param encoder -
* @param doc -
*/
export const readSyncStep1 = (
decoder: decoding.Decoder,
encoder: encoding.Encoder,
doc: Y.Doc
): void =>
// 读取状态向量并直接调用writeSyncStep2回复
writeSyncStep2(encoder, doc, decoding.readVarUint8Array(decoder))
/**
* 2
*
*
* @param decoder -
* @param doc -
* @param transactionOrigin -
*/
export const readSyncStep2 = (
decoder: decoding.Decoder,
doc: Y.Doc,
transactionOrigin: any
): void => {
try {
// 读取并应用增量更新
// transactionOrigin用于标识更新来源
Y.applyUpdate(doc, decoding.readVarUint8Array(decoder), transactionOrigin)
} catch (error) {
// 错误处理 - 记录日志但不中断程序
console.error('Caught error while handling a Yjs update', error)
}
}
/**
*
*/
export const writeUpdate = (encoder: encoding.Encoder, update: Uint8Array): void => {
encoding.writeVarUint(encoder, messageYjsUpdate)
encoding.writeVarUint8Array(encoder, update)
}
/**
* Structs DeleteStore y
*/
export const readUpdate = readSyncStep2
/**
*
* Yjs
*
* @param decoder -
* @param encoder -
* @param doc - Y.Doc ,
* @param transactionOrigin -
* @returns
*/
export const readSyncMessage = (
decoder: decoding.Decoder,
encoder: encoding.Encoder,
doc: Y.Doc,
transactionOrigin: any
): number => {
/**
*
* 使(VarUint)
* VarUint 使1-8
*/
const messageType = decoding.readVarUint(decoder)
/**
*
* :
* 1. Step1 - ,
* 2. Step2 -
* 3. Update -
*/
switch (messageType) {
case messageYjsSyncStep1:
// 处理同步第一步 - 读取状态向量并编码响应
readSyncStep1(decoder, encoder, doc)
break
case messageYjsSyncStep2:
// 处理同步第二步 - 合并增量更新
readSyncStep2(decoder, doc, transactionOrigin)
break
case messageYjsUpdate:
// 处理实时更新 - 直接应用更新
readUpdate(decoder, doc, transactionOrigin)
break
default:
// 未知消息类型则抛出异常
throw new Error('Unknown message type')
}
/**
* 使
*
*/
return messageType
}