how to get the doc text in y-websocket server side

552 views Asked by At

I want to flush the yjs document into disk in y-websocket server side, this is how I do it:

const handleFileSync = async (docName, ydoc) => {
  try {
    let fileContent = await getFileJsonData(docName)
    if (!fileContent) {
      console.error(`get file info failed,file info: ${fileContent},docName:${docName}`)
      return
    }
    let projectId = fileContent.result.project_id
    let fileName = fileContent.result.name
    let text = ydoc.getText(docName)
    console.info('get file text:', text)
    fs.writeFile(`/opt/data/project/${projectId}/${fileName}`, text, (err) => {
      if (err) {
        console.error('Failed to write file:', err)
      }
    })
  } catch (err) {
    console.error('Failed to sync file to disk', err)
  }
}

in this function, I want to flush the latest ydoc document into disk, the docName is the file uniq id (guid/uuid). when I run into this code, I get the error:

Failed to sync file to disk TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of YText
    at Object.writeFile (node:fs:2155:5)
    at handleFileSync (/home/node/app/bin/rd_file.js:15:8)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 'ERR_INVALID_ARG_TYPE'
}
{
  result: {
    id: 9,
    name: 'main.tex',
    created_time: 1691766535428,
    updated_time: 1691766535428,
    user_id: 1,
    doc_status: 1,
    project_id: '8919d43a4f034c41ab582c27a04a7058',
    file_type: 1,
    file_id: '298028e1a37d46abbe6b7105ab8cbb9b',
    parent: '8919d43a4f034c41ab582c27a04a7058',
    main_flag: 1
  },
  statusCode: '200',
  resultCode: '200',
  msg: 'ok'
}
get file text: <ref *2> YText {
  _item: null,
  _map: Map(0) {},
  _start: <ref *1> Item {
    id: ID { client: 587431845, clock: 0 },
    length: 1,
    origin: null,
    left: null,
    right: Item {
      id: [ID],
      length: 9,
      origin: [ID],
      left: [Circular *1],
      right: [Item],
      rightOrigin: null,
      parent: [Circular *2],
      parentSub: null,
      redone: null,
      content: [ContentDeleted],
      info: 4
    },
    rightOrigin: null,
    parent: [Circular *2],
    parentSub: null,
    redone: null,
    content: ContentDeleted { len: 1 },
    info: 4
  },
  doc: <ref *3> WSSharedDoc {
    _observers: Map(4) {
      'load' => [Set],
      'sync' => [Set],
      'destroy' => [Set],
      'update' => [Set]
    },
    gc: true,
    gcFilter: [Function: gcFilter],
    clientID: 2850057843,
    guid: '5a060d00-ed9d-4150-8ac7-4714a6f26bc3',
    collectionid: null,
    share: Map(1) { '298028e1a37d46abbe6b7105ab8cbb9b' => [Circular *2] },
    store: StructStore {
      clients: [Map],
      pendingStructs: null,
      pendingDs: null
    },
    _transaction: null,
    _transactionCleanups: [],
    subdocs: Set(0) {},
    _item: null,
    shouldLoad: true,
    autoLoad: false,
    meta: null,
    isLoaded: false,
    isSynced: false,
    whenLoaded: Promise { <pending> },
    whenSynced: Promise { <pending> },
    name: '298028e1a37d46abbe6b7105ab8cbb9b',
    conns: Map(2) { [WebSocket] => [Set], [WebSocket] => [Set] },
    awareness: Awareness {
      _observers: [Map],
      doc: [Circular *3],
      clientID: 2850057843,
      states: [Map],
      meta: [Map],
      _checkInterval: Timeout {
        _idleTimeout: 3000,
        _idlePrev: [TimersList],
        _idleNext: [Timeout],
        _idleStart: 212717,
        _onTimeout: [Function (anonymous)],
        _timerArgs: undefined,
        _repeat: 3000,
        _destroyed: false,
        [Symbol(refed)]: true,
        [Symbol(kHasPrimitive)]: false,
        [Symbol(asyncId)]: 280,
        [Symbol(triggerId)]: 243
      }
    }
  },
  _length: 49,
  _eH: EventHandler { l: [] },
  _dEH: EventHandler { l: [] },
  _searchMarker: [],
  _pending: null
}

seems this line let text = ydoc.getText(docName) is not the correct way to get the document text. Am I missing something? how can I get the ydoc document content and flush it into disk? I have read the document from here https://docs.yjs.dev/api/shared-types/y.text. I also tried to use this code to get the content:

const content = Y.decodeSnapshot(persistedYdoc.snapshot)

this is the full code:

if (typeof persistenceDir === 'string') {
  console.info('Persisting documents to "' + persistenceDir + '"')
  // @ts-ignore
  const LeveldbPersistence = require('y-leveldb').LeveldbPersistence
  const ldb = new LeveldbPersistence(persistenceDir)
  persistence = {
    provider: ldb,
    bindState: async (docName, ydoc) => {
      const persistedYdoc = await ldb.getYDoc(docName)
      const newUpdates = Y.encodeStateAsUpdate(ydoc)
      ldb.storeUpdate(docName, newUpdates)
      Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc))
      ydoc.on('update', update => {
        ldb.storeUpdate(docName, update)
        const content = Y.decodeSnapshot(persistedYdoc.snapshot)
        handleFileSync(docName, ydoc, content)
      })
    },
    writeState: async (docName, ydoc) => { }
  }
}

shows error:

Caught error while handling a Yjs update TypeError: Cannot read properties of undefined (reading 'length')
    at Object.readVarUint (/home/node/app/node_modules/lib0/dist/buffer-9b566988.cjs:1135:27)
    at readDeleteSet (/home/node/app/node_modules/yjs/dist/yjs.cjs:309:42)
    at decodeSnapshotV2 (/home/node/app/node_modules/yjs/dist/yjs.cjs:2614:23)
    at Object.decodeSnapshot (/home/node/app/node_modules/yjs/dist/yjs.cjs:2621:31)
    at /home/node/app/bin/utils.js:44:27
    at /home/node/app/node_modules/lib0/dist/observable.cjs:77:90
    at Array.forEach (<anonymous>)
    at WSSharedDoc.emit (/home/node/app/node_modules/lib0/dist/observable.cjs:77:77)
    at cleanupTransactions (/home/node/app/node_modules/yjs/dist/yjs.cjs:3259:15)
    at transact (/home/node/app/node_modules/yjs/dist/yjs.cjs:3335:9)
Caught error while handling a Yjs update TypeError: Cannot read properties of undefined (reading 'length')
    at Object.readVarUint (/home/node/app/node_modules/lib0/dist/buffer-9b566988.cjs:1135:27)
    at readDeleteSet (/home/node/app/node_modules/yjs/dist/yjs.cjs:309:42)
    at decodeSnapshotV2 (/home/node/app/node_modules/yjs/dist/yjs.cjs:2614:23)
    at Object.decodeSnapshot (/home/node/app/node_modules/yjs/dist/yjs.cjs:2621:31)
    at /home/node/app/bin/utils.js:44:27
    at /home/node/app/node_modules/lib0/dist/observable.cjs:77:90
    at Array.forEach (<anonymous>)
    at WSSharedDoc.emit (/home/node/app/node_modules/lib0/dist/observable.cjs:77:77)
    at cleanupTransactions (/home/node/app/node_modules/yjs/dist/yjs.cjs:3259:15)
    at transact (/home/node/app/node_modules/yjs/dist/yjs.cjs:3335:9)
1

There are 1 answers

0
Dolphin On

using this code to get the document text(not the binary data):

const persistedYdoc = await ldb.getYDoc(docName)
let text = persistedYdoc.getText(docName)

the docName is the document uniq id. more info: https://discuss.yjs.dev/t/how-to-get-the-document-text-the-decode-content-not-binary-content-in-y-websocket/2033