@liveblocks/node-prosemirror
Version:
A server-side utility that lets you modify prosemirror and tiptap documents hosted in Liveblocks.
1 lines • 17.1 kB
Source Map (JSON)
{"version":3,"sources":["/home/runner/work/liveblocks/liveblocks/packages/liveblocks-node-prosemirror/dist/index.cjs","../src/index.ts","../src/version.ts","../src/document.ts","../src/comment.ts","../src/mention.ts"],"names":["mergeAttributes"],"mappings":"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","file":"/home/runner/work/liveblocks/liveblocks/packages/liveblocks-node-prosemirror/dist/index.cjs","sourcesContent":[null,"import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\nexport type {\n LiveblocksDocumentApi,\n LiveblocksProsemirrorOptions,\n} from \"./document\";\nexport { withProsemirrorDocument } from \"./document\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n","declare const __VERSION__: string;\ndeclare const TSUP_FORMAT: string;\n\nexport const PKG_NAME = \"@liveblocks/node-prosemirror\";\nexport const PKG_VERSION = typeof __VERSION__ === \"string\" && __VERSION__;\nexport const PKG_FORMAT = typeof TSUP_FORMAT === \"string\" && TSUP_FORMAT;\n","import type { Liveblocks } from \"@liveblocks/node\";\nimport type { TextSerializer } from \"@tiptap/core\";\nimport { getSchema, getText } from \"@tiptap/core\";\nimport type { Node, Schema } from \"@tiptap/pm/model\";\nimport type { Transaction } from \"@tiptap/pm/state\";\nimport { EditorState } from \"@tiptap/pm/state\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport type { MarkdownSerializer } from \"prosemirror-markdown\";\nimport { defaultMarkdownSerializer } from \"prosemirror-markdown\";\nimport { initProseMirrorDoc, updateYFragment } from \"y-prosemirror\";\nimport { applyUpdate, Doc, encodeStateAsUpdate, encodeStateVector } from \"yjs\";\n\nimport { CommentExtension } from \"./comment\";\nimport { MentionExtension } from \"./mention\";\n\nexport type LiveblocksProsemirrorOptions = {\n roomId: string;\n schema?: Schema;\n client: Liveblocks;\n field?: string;\n};\n\nexport type LiveblocksDocumentApi = {\n refresh: () => Promise<void>;\n update: (\n modifyFn: (doc: Node, tr: Transaction) => Transaction\n ) => Promise<void>;\n setContent: (content: null | object | string) => Promise<void>;\n clearContent: () => Promise<void>;\n getText: (options?: {\n blockSeparator?: string;\n textSerializers?: Record<string, TextSerializer>;\n }) => string;\n getEditorState: () => EditorState;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON: () => any; // upstream ProseMirror uses \"any\"\n toMarkdown: () => string;\n};\n\nconst DEFAULT_SCHEMA = getSchema([\n StarterKit,\n CommentExtension,\n MentionExtension,\n]);\n\nconst getLiveblocksDocumentState = async (\n roomId: string,\n client: Liveblocks,\n schema: Schema,\n field: string\n) => {\n const update = new Uint8Array(\n await client.getYjsDocumentAsBinaryUpdate(roomId)\n );\n const ydoc = new Doc();\n applyUpdate(ydoc, update);\n const fragment = ydoc.getXmlFragment(field ?? \"default\");\n const { mapping, doc } = initProseMirrorDoc(fragment, schema);\n const state = EditorState.create({\n schema,\n doc,\n });\n\n return {\n fragment,\n state,\n ydoc,\n mapping,\n };\n};\n\n/**\n *\n * This is simplified re-implementation of TipTap's createDocument function\n *\n * @param content\n * @param schema\n * @param parseOptions\n * @returns\n */\nconst createDocumentFromContent = (content: object, schema: Schema) => {\n try {\n return schema.nodeFromJSON(content);\n } catch (error) {\n console.warn(\n \"[warn]: Invalid content.\",\n \"Passed value:\",\n content,\n \"Error:\",\n error\n );\n return false;\n }\n};\n\n/**\n *\n * `withProsemirrorDocument` is the main entry point to access and modify prosemirror (or TipTap) documents on your backend.\n * This function internally instantiates a Prosemirror state and allows you to modify and export its values asynchronously\n * with a simplified interface. It's important to note that the api works with Prosemirror's document state and Transactions api.\n *\n * @param options Specify the roomId, client, and optionally the prosemirror schema. If no schema is applied, a default schema of TipTap's StarterKit + Liveblocks mentions/comments is used.\n * @param callback The call back function is optionally async and receives the document API as its only argument.\n *\n * @example\n *\n * import { Liveblocks } from \"@liveblocks/node\";\n * import { withProsemirrorDocument } from \"@liveblocks/node-prosemirror\";\n *\n * const client = new Liveblocks({secret: \"sk_your_secret_key\"});\n * const text = await withProsemirrorDocument(\n * { client, roomId: \"your-room\" },\n * async (docApi) => {\n * await docApi.update((tr) => {\n * return tr.insertText(\"Hello World, from Liveblocks!!\", 0);\n * });\n * return docApi.getTextContent();\n * }\n * );\n *\n */\nexport async function withProsemirrorDocument<T>(\n { roomId, schema: maybeSchema, client, field }: LiveblocksProsemirrorOptions,\n callback: (api: LiveblocksDocumentApi) => Promise<T> | T\n): Promise<T> {\n const schema = maybeSchema ?? DEFAULT_SCHEMA;\n let liveblocksState = await getLiveblocksDocumentState(\n roomId,\n client,\n schema,\n field ?? \"default\"\n );\n\n const val = await callback({\n /**\n * Fetches and resyncs the latest document with Liveblocks\n */\n async refresh() {\n liveblocksState = await getLiveblocksDocumentState(\n roomId,\n client,\n schema,\n field ?? \"default\"\n );\n },\n /**\n * Provide a callback to modify documetns with Lexical's standard api. All calls are discrete.\n */\n async update(modifyFn) {\n const { ydoc, fragment, state, mapping } = liveblocksState;\n // Flush any pending updates (there really shouldn't be any?), this may be a NOOP\n const beforeVector = encodeStateVector(ydoc);\n const afterState = state.apply(modifyFn(state.doc, state.tr));\n ydoc.transact(() => {\n updateYFragment(ydoc, fragment, afterState.doc, mapping);\n });\n // grab update after diffing\n const diffUpdate = encodeStateAsUpdate(ydoc, beforeVector);\n await client.sendYjsBinaryUpdate(roomId, diffUpdate);\n await this.refresh();\n },\n\n /**\n * allows you to set content similar to TipTap's setcontent. Only accepts nulls, objects or strings.\n * Unlike TipTap, strings won't be parsed with DOMParser\n * */\n async setContent(content: null | object | string) {\n if (typeof content === \"string\") {\n return this.update((doc, tr) => {\n tr.delete(0, doc.content.size);\n tr.insertText(content);\n return tr;\n });\n }\n if (content === null) {\n return this.clearContent();\n }\n const node = createDocumentFromContent(content, schema);\n if (!node) {\n throw \"Invalid content\";\n }\n return this.update((doc, tr) => {\n tr.delete(0, doc.content.size);\n tr.insert(0, node);\n return tr;\n });\n },\n async clearContent() {\n await this.update((doc, tr) => {\n tr.delete(0, doc.content.size);\n return tr;\n });\n },\n\n /**\n * Uses TipTap's getText function, which allows passing a custom text serializer\n */\n getText(options?: {\n blockSeparator?: string;\n textSerializers?: Record<string, TextSerializer>;\n }) {\n const { state } = liveblocksState;\n return getText(state.doc, options);\n },\n\n /**\n * Helper function to return prosemirror document in JSON form\n */\n toJSON() {\n // upstream ProseMirror uses \"any\" type for json\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return liveblocksState.state.doc.toJSON();\n },\n /**\n * Helper function to return editor state as Markdown. By default it uses the defaultMarkdownSerializer from prosemirror-markdown, but you may pass your own\n */\n toMarkdown(serializer?: MarkdownSerializer) {\n return (serializer ?? defaultMarkdownSerializer).serialize(\n liveblocksState.state.doc\n );\n },\n /**\n * Helper function to return the editor's current prosemirror state\n */\n getEditorState() {\n return liveblocksState.state;\n },\n });\n return val;\n}\n","import { Mark, mergeAttributes } from \"@tiptap/core\";\n\nexport const LIVEBLOCKS_COMMENT_MARK_TYPE = \"liveblocksCommentMark\";\n\nexport const CommentExtension = Mark.create({\n name: LIVEBLOCKS_COMMENT_MARK_TYPE,\n excludes: \"\",\n inclusive: false,\n keepOnSplit: true,\n addAttributes() {\n // Return an object with attribute configuration\n return {\n orphan: {\n parseHTML: (element) => !!element.getAttribute(\"data-orphan\"),\n renderHTML: (attributes) => {\n return (attributes as { orphan: boolean }).orphan\n ? {\n \"data-orphan\": \"true\",\n }\n : {};\n },\n default: false,\n },\n threadId: {\n parseHTML: (element) => element.getAttribute(\"data-lb-thread-id\"),\n renderHTML: (attributes) => {\n return {\n \"data-lb-thread-id\": (attributes as { threadId: string }).threadId,\n };\n },\n default: \"\",\n },\n };\n },\n\n renderHTML({ HTMLAttributes }: { HTMLAttributes: Record<string, unknown> }) {\n return [\n \"span\",\n mergeAttributes(HTMLAttributes, {\n class: \"lb-root lb-tiptap-thread-mark\",\n }),\n ];\n },\n});\n","import { mergeAttributes, Node } from \"@tiptap/core\";\n\nexport const LIVEBLOCKS_MENTION_TYPE = \"liveblocksMention\";\n\nexport const MentionExtension = Node.create({\n name: LIVEBLOCKS_MENTION_TYPE,\n group: \"inline\",\n inline: true,\n selectable: true,\n atom: true,\n\n priority: 101,\n parseHTML() {\n return [\n {\n tag: \"liveblocks-mention\",\n },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\"liveblocks-mention\", mergeAttributes(HTMLAttributes)];\n },\n\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-id\"),\n renderHTML: (attributes) => {\n if (!attributes.id) {\n return {};\n }\n\n return {\n \"data-id\": attributes.id as string, // \"as\" typing because TipTap doesn't have a way to type attributes\n };\n },\n },\n notificationId: {\n default: null,\n parseHTML: (element) => element.getAttribute(\"data-notification-id\"),\n renderHTML: (attributes) => {\n if (!attributes.notificationId) {\n return {};\n }\n\n return {\n \"data-notification-id\": attributes.notificationId as string, // \"as\" typing because TipTap doesn't have a way to type attributes\n };\n },\n },\n };\n },\n});\n"]}