File
Blob: src/worker/sites/project-page-json.ts
| 1 | import { getSchema, type JSONContent } from "@tiptap/core"; |
| 2 | import { yXmlFragmentToProseMirrorRootNode } from "@tiptap/y-tiptap"; |
| 3 | import * as Y from "yjs"; |
| 4 | |
| 5 | import { YJS_DOCUMENT_STORE } from "@/shared/constants"; |
| 6 | import { |
| 7 | collectEditorTextMetrics, |
| 8 | createHeadlessEditorExtensions, |
| 9 | type EditorTextMetrics, |
| 10 | } from "@/shared/editor/schema"; |
| 11 | |
| 12 | export interface ProjectedPageJson { |
| 13 | content: JSONContent; |
| 14 | metrics: EditorTextMetrics; |
| 15 | } |
| 16 | |
| 17 | export async function projectPageJson(env: Pick<Env, "DocSync">, pageId: string): Promise<ProjectedPageJson | null> { |
| 18 | const stub = env.DocSync.getByName(pageId); |
| 19 | const snapshot = await stub.getSnapshotResponse(pageId); |
| 20 | if (snapshot.kind === "missing") return createEmptyPageJson(); |
| 21 | |
| 22 | const bytes = new Uint8Array(await snapshot.response.arrayBuffer()); |
| 23 | const ydoc = new Y.Doc(); |
| 24 | try { |
| 25 | Y.applyUpdate(ydoc, bytes); |
| 26 | const fragment = ydoc.getXmlFragment(YJS_DOCUMENT_STORE); |
| 27 | const schema = getSchema(createHeadlessEditorExtensions()); |
| 28 | const root = yXmlFragmentToProseMirrorRootNode(fragment, schema); |
| 29 | return { |
| 30 | content: root.toJSON() as JSONContent, |
| 31 | metrics: collectEditorTextMetrics(root), |
| 32 | }; |
| 33 | } finally { |
| 34 | ydoc.destroy(); |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | function createEmptyPageJson(): ProjectedPageJson { |
| 39 | return { |
| 40 | content: { type: "doc", content: [] }, |
| 41 | metrics: { words: 0, characters: 0 }, |
| 42 | }; |
| 43 | } |