claudaborative-editing 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +335 -0
- package/README.md +106 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2414 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/yjs/types.ts","../src/yjs/block-converter.ts","../src/yjs/document-manager.ts","../src/wordpress/api-client.ts","../src/wordpress/sync-client.ts","../src/wordpress/types.ts","../src/yjs/sync-protocol.ts","../src/blocks/parser.ts","../src/blocks/renderer.ts","../src/session/awareness.ts","../src/session/session-manager.ts","../src/tools/connect.ts","../src/tools/posts.ts","../src/tools/read.ts","../src/tools/edit.ts","../src/tools/status.ts","../src/server.ts","../src/cli/setup.ts","../src/index.ts"],"sourcesContent":["/**\n * Yjs document types matching the Gutenberg CRDT structure.\n *\n * Verified from @wordpress/sync and @wordpress/core-data source code.\n * The Y.Doc has two root Y.Maps: 'document' and 'state'.\n */\nimport * as Y from 'yjs';\n\n// --- Root-level keys ---\n\nexport const CRDT_RECORD_MAP_KEY = 'document';\nexport const CRDT_STATE_MAP_KEY = 'state';\nexport const CRDT_STATE_MAP_VERSION_KEY = 'version';\nexport const CRDT_STATE_MAP_SAVED_AT_KEY = 'savedAt';\nexport const CRDT_STATE_MAP_SAVED_BY_KEY = 'savedBy';\nexport const CRDT_DOC_VERSION = 1;\n\n// --- Block types ---\n\n/** Block attributes map: rich-text attributes are Y.Text, others are plain values. */\nexport type YBlockAttributes = Y.Map<Y.Text | unknown>;\n\n/** A block as represented in the Yjs document. */\nexport interface YBlockFields {\n name: string;\n clientId: string;\n attributes: YBlockAttributes;\n innerBlocks: Y.Array<Y.Map<unknown>>;\n isValid?: boolean;\n originalContent?: string;\n}\n\n/** A plain-object block for reading/writing (not Yjs types). */\nexport interface Block {\n name: string;\n clientId: string;\n attributes: Record<string, unknown>;\n innerBlocks: Block[];\n isValid?: boolean;\n originalContent?: string;\n}\n\n/**\n * Block attribute schema used to determine if an attribute is rich-text.\n * Since we don't have access to @wordpress/blocks' getBlockTypes() registry,\n * we maintain our own mapping of known rich-text attributes.\n */\nexport interface BlockAttributeSchema {\n type: 'rich-text' | 'string' | 'number' | 'boolean' | 'array' | 'object';\n role?: 'content' | 'local';\n}\n\n/**\n * Known rich-text attributes by block type.\n * This must be kept in sync with core block definitions.\n * When a block type is not in this map, all string attributes are treated as plain strings.\n */\nexport const RICH_TEXT_ATTRIBUTES: Record<string, Set<string>> = {\n 'core/paragraph': new Set(['content']),\n 'core/heading': new Set(['content']),\n 'core/list-item': new Set(['content']),\n 'core/quote': new Set(['value', 'citation']),\n 'core/pullquote': new Set(['value', 'citation']),\n 'core/verse': new Set(['content']),\n 'core/preformatted': new Set(['content']),\n 'core/freeform': new Set(['content']),\n 'core/button': new Set(['text']),\n 'core/table': new Set([]), // table cells use rich-text but are nested in body array\n 'core/footnotes': new Set(['content']),\n};\n\n/**\n * Default attribute values by block type.\n *\n * Gutenberg validates blocks against their schema and marks them as invalid\n * if expected attributes are missing. This map provides the default values\n * that Gutenberg would normally set when creating a block through the editor.\n */\nexport const DEFAULT_BLOCK_ATTRIBUTES: Record<string, Record<string, unknown>> = {\n 'core/paragraph': { dropCap: false },\n 'core/heading': { level: 2 },\n 'core/list': { ordered: false },\n};\n\n/**\n * Get default attributes for a block type.\n * Returns an empty object if no defaults are defined.\n */\nexport function getDefaultAttributes(blockName: string): Record<string, unknown> {\n return DEFAULT_BLOCK_ATTRIBUTES[blockName] ?? {};\n}\n\n/**\n * Check if a block attribute is rich-text typed.\n */\nexport function isRichTextAttribute(\n blockName: string,\n attributeName: string,\n): boolean {\n return RICH_TEXT_ATTRIBUTES[blockName]?.has(attributeName) ?? false;\n}\n\n// --- Post record in Y.Doc ---\n\n/**\n * The synced post properties stored in the 'document' Y.Map.\n * Title, content, excerpt are Y.Text. Blocks is Y.Array<Y.Map>.\n */\nexport const SYNCED_POST_PROPERTIES = new Set([\n 'author',\n 'blocks',\n 'content',\n 'comment_status',\n 'date',\n 'excerpt',\n 'featured_media',\n 'format',\n 'meta',\n 'ping_status',\n 'slug',\n 'status',\n 'sticky',\n 'tags',\n 'categories',\n 'template',\n 'title',\n]);\n\n// --- Collaborator awareness ---\n\nexport interface CollaboratorInfo {\n id: number;\n name: string;\n slug: string;\n avatar_urls: Record<string, string>;\n browserType: string;\n enteredAt: number;\n}\n\nexport interface AwarenessCursorPosition {\n relativePosition: {\n type: { client: number; clock: number };\n tname: null;\n item: null;\n assoc: number;\n };\n absoluteOffset: number;\n}\n\nexport interface AwarenessEditorState {\n selection:\n | { type: 'none' }\n | { type: 'cursor'; cursorPosition: AwarenessCursorPosition };\n}\n\nexport interface AwarenessLocalState {\n collaboratorInfo: CollaboratorInfo;\n editorState?: AwarenessEditorState;\n}\n","/**\n * Converts between plain Block objects and Yjs Y.Map types.\n *\n * Handles the mapping of rich-text attributes to Y.Text instances\n * based on the block type's known schema.\n */\nimport * as Y from 'yjs';\nimport { type Block, isRichTextAttribute } from './types.js';\n\n/**\n * Convert a plain Block object to a Y.Map suitable for insertion into the Y.Doc.\n *\n * Rich-text attributes (as determined by the block type) are stored as Y.Text.\n * All other attributes are stored as plain values.\n */\nexport function blockToYMap(block: Block): Y.Map<unknown> {\n const ymap = new Y.Map<unknown>();\n\n ymap.set('name', block.name);\n ymap.set('clientId', block.clientId);\n\n if (block.isValid !== undefined) {\n ymap.set('isValid', block.isValid);\n }\n if (block.originalContent !== undefined) {\n ymap.set('originalContent', block.originalContent);\n }\n\n // Attributes: rich-text ones become Y.Text, others stay as plain values\n const attrMap = new Y.Map<unknown>();\n if (block.attributes) {\n for (const [key, value] of Object.entries(block.attributes)) {\n if (isRichTextAttribute(block.name, key) && typeof value === 'string') {\n const ytext = new Y.Text();\n ytext.insert(0, value);\n attrMap.set(key, ytext);\n } else {\n attrMap.set(key, value);\n }\n }\n }\n ymap.set('attributes', attrMap);\n\n // Inner blocks: recurse\n const innerBlocksArray = new Y.Array<Y.Map<unknown>>();\n if (block.innerBlocks && block.innerBlocks.length > 0) {\n const innerMaps = block.innerBlocks.map((inner) => blockToYMap(inner));\n innerBlocksArray.push(innerMaps);\n }\n ymap.set('innerBlocks', innerBlocksArray);\n\n return ymap;\n}\n\n/**\n * Convert a Y.Map block from the Y.Doc to a plain Block object.\n *\n * Y.Text attributes are converted to their string representation.\n */\nexport function yMapToBlock(ymap: Y.Map<unknown>): Block {\n const name = ymap.get('name') as string;\n const clientId = ymap.get('clientId') as string;\n\n // Read attributes, converting Y.Text to strings\n const attrMap = ymap.get('attributes') as Y.Map<unknown> | undefined;\n const attributes: Record<string, unknown> = {};\n if (attrMap) {\n for (const [key, value] of attrMap.entries()) {\n if (value instanceof Y.Text) {\n attributes[key] = value.toString();\n } else {\n attributes[key] = value;\n }\n }\n }\n\n // Read inner blocks recursively\n const innerBlocksArray = ymap.get('innerBlocks') as\n | Y.Array<Y.Map<unknown>>\n | undefined;\n const innerBlocks: Block[] = [];\n if (innerBlocksArray) {\n for (let i = 0; i < innerBlocksArray.length; i++) {\n innerBlocks.push(yMapToBlock(innerBlocksArray.get(i)));\n }\n }\n\n const block: Block = {\n name,\n clientId,\n attributes,\n innerBlocks,\n };\n\n const isValid = ymap.get('isValid');\n if (isValid !== undefined) {\n block.isValid = isValid as boolean;\n }\n\n const originalContent = ymap.get('originalContent');\n if (originalContent !== undefined) {\n block.originalContent = originalContent as string;\n }\n\n return block;\n}\n\n/**\n * Update a Y.Text with a new string value using full replacement.\n *\n * WARNING: This uses delete-all + insert which targets specific CRDT items by ID.\n * In multi-client scenarios where the browser creates LOCAL Y.Text items alongside\n * REMOTE items (e.g. after `applyChangesToCRDTDoc`), the delete only removes our\n * items while the browser's survive — so the visible content doesn't change.\n *\n * For live collaborative editing, use `deltaUpdateYText()` instead.\n */\nexport function updateYText(ytext: Y.Text, newValue: string): void {\n if (ytext.length > 0) {\n ytext.delete(0, ytext.length);\n }\n if (newValue.length > 0) {\n ytext.insert(0, newValue);\n }\n}\n\n/**\n * Result of computing the delta between two strings.\n */\nexport interface TextDelta {\n prefixLen: number;\n deleteCount: number;\n insertText: string;\n}\n\n/**\n * Compute the minimal delta between two strings using common-prefix/common-suffix diff.\n * Returns null if the strings are identical.\n */\nexport function computeTextDelta(oldValue: string, newValue: string): TextDelta | null {\n if (oldValue === newValue) return null;\n\n // Find common prefix\n let prefixLen = 0;\n while (\n prefixLen < oldValue.length &&\n prefixLen < newValue.length &&\n oldValue[prefixLen] === newValue[prefixLen]\n ) {\n prefixLen++;\n }\n\n // Find common suffix (don't overlap with prefix)\n let suffixLen = 0;\n while (\n suffixLen < oldValue.length - prefixLen &&\n suffixLen < newValue.length - prefixLen &&\n oldValue[oldValue.length - 1 - suffixLen] ===\n newValue[newValue.length - 1 - suffixLen]\n ) {\n suffixLen++;\n }\n\n const deleteCount = oldValue.length - prefixLen - suffixLen;\n const insertText = newValue.slice(prefixLen, newValue.length - suffixLen);\n\n return { prefixLen, deleteCount, insertText };\n}\n\n/**\n * Update a Y.Text using delta-based editing via `Y.Text.applyDelta()`.\n *\n * Uses a simple common-prefix/common-suffix diff to compute the minimal\n * retain/delete/insert operations. Delta operations are POSITION-BASED,\n * not item-ID-based, so they operate on whatever CRDT items exist at\n * those positions — whether created by the browser or by us.\n *\n * This is the correct approach for live collaborative editing where\n * Gutenberg's `applyChangesToCRDTDoc` creates local items alongside\n * remote items in Y.Text.\n */\nexport function deltaUpdateYText(ytext: Y.Text, newValue: string): void {\n const oldValue = ytext.toString();\n const delta = computeTextDelta(oldValue, newValue);\n if (!delta) return;\n\n // Build delta ops\n const ops: Array<{ retain?: number; delete?: number; insert?: string }> = [];\n if (delta.prefixLen > 0) ops.push({ retain: delta.prefixLen });\n if (delta.deleteCount > 0) ops.push({ delete: delta.deleteCount });\n if (delta.insertText.length > 0) ops.push({ insert: delta.insertText });\n\n if (ops.length > 0) {\n ytext.applyDelta(ops);\n }\n}\n\n/**\n * Find the end position for an HTML-safe chunk of text.\n *\n * Given text and a starting offset, returns the end position for the next chunk,\n * ensuring it doesn't split inside an HTML tag. If the preferred end position\n * is inside a tag (between `<` and `>`), extends the chunk past the closing `>`.\n *\n * @param text The full text to chunk\n * @param offset The starting offset within the text\n * @param preferredSize The preferred chunk size in characters\n * @returns The end position (exclusive) for the chunk\n */\nexport function findHtmlSafeChunkEnd(text: string, offset: number, preferredSize: number): number {\n const end = Math.min(offset + preferredSize, text.length);\n if (end >= text.length) return text.length;\n\n // Check if we're inside an HTML tag at the proposed end position.\n // Scan backward from end to find the most recent '<' or '>' before the end.\n let inTag = false;\n for (let i = end - 1; i >= offset; i--) {\n if (text[i] === '>') {\n // We found a closing '>' before reaching any '<', so we're NOT inside a tag\n break;\n }\n if (text[i] === '<') {\n // We found an opening '<' without a closing '>' between it and end,\n // so the proposed end is inside a tag\n inTag = true;\n break;\n }\n }\n\n if (!inTag) return end;\n\n // Extend past the closing '>'\n const closingBracket = text.indexOf('>', end);\n if (closingBracket === -1) {\n // No closing bracket found — include the rest of the text\n return text.length;\n }\n return closingBracket + 1;\n}\n","/**\n * Manages Y.Doc lifecycle for WordPress post collaborative editing.\n *\n * Each Y.Doc mirrors the structure Gutenberg expects:\n * - Root map 'document': holds post fields (title, content, blocks, etc.)\n * - Root map 'state': holds sync metadata (version, savedAt, savedBy)\n */\nimport * as Y from 'yjs';\nimport {\n type Block,\n CRDT_DOC_VERSION,\n CRDT_RECORD_MAP_KEY,\n CRDT_STATE_MAP_KEY,\n CRDT_STATE_MAP_SAVED_AT_KEY,\n CRDT_STATE_MAP_SAVED_BY_KEY,\n CRDT_STATE_MAP_VERSION_KEY,\n isRichTextAttribute,\n} from './types.js';\nimport {\n blockToYMap,\n deltaUpdateYText,\n yMapToBlock,\n} from './block-converter.js';\n\nexport class DocumentManager {\n /**\n * Create a new Y.Doc initialized with Gutenberg's expected structure.\n */\n createDoc(): Y.Doc {\n const doc = new Y.Doc();\n\n doc.transact(() => {\n // Initialize state map only — matches Gutenberg's initializeYjsDoc.\n // Do NOT pre-populate the document map with empty Y.Text/Y.Array.\n // Those keys will be created on demand when content is first set,\n // or populated from a remote peer's state via sync.\n // Pre-creating them would cause CRDT merge conflicts during sync\n // (two competing values for the same Y.Map key).\n const stateMap = doc.getMap(CRDT_STATE_MAP_KEY);\n stateMap.set(CRDT_STATE_MAP_VERSION_KEY, CRDT_DOC_VERSION);\n });\n\n return doc;\n }\n\n /**\n * Get the root 'document' Y.Map.\n */\n getDocumentMap(doc: Y.Doc): Y.Map<unknown> {\n return doc.getMap(CRDT_RECORD_MAP_KEY);\n }\n\n /**\n * Get the root 'state' Y.Map.\n */\n getStateMap(doc: Y.Doc): Y.Map<unknown> {\n return doc.getMap(CRDT_STATE_MAP_KEY);\n }\n\n /**\n * Read the title as a plain string.\n */\n getTitle(doc: Y.Doc): string {\n const documentMap = this.getDocumentMap(doc);\n const title = documentMap.get('title');\n if (title instanceof Y.Text) {\n return title.toString();\n }\n return '';\n }\n\n /**\n * Set the title (replaces full Y.Text content).\n */\n setTitle(doc: Y.Doc, title: string): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n const ytext = documentMap.get('title');\n if (ytext instanceof Y.Text) {\n deltaUpdateYText(ytext, title);\n } else {\n const newYText = new Y.Text();\n newYText.insert(0, title);\n documentMap.set('title', newYText);\n }\n });\n }\n\n /**\n * Get all blocks as plain Block[] objects.\n */\n getBlocks(doc: Y.Doc): Block[] {\n const documentMap = this.getDocumentMap(doc);\n const blocksArray = documentMap.get('blocks') as\n | Y.Array<Y.Map<unknown>>\n | undefined;\n if (!blocksArray) {\n return [];\n }\n\n const blocks: Block[] = [];\n for (let i = 0; i < blocksArray.length; i++) {\n blocks.push(yMapToBlock(blocksArray.get(i)));\n }\n return blocks;\n }\n\n /**\n * Set blocks from plain Block[] objects (replaces all existing blocks).\n */\n setBlocks(doc: Y.Doc, blocks: Block[]): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n let blocksArray = documentMap.get('blocks') as Y.Array<\n Y.Map<unknown>\n > | undefined;\n\n if (!blocksArray) {\n blocksArray = new Y.Array<Y.Map<unknown>>();\n documentMap.set('blocks', blocksArray);\n }\n\n // Clear existing blocks\n if (blocksArray.length > 0) {\n blocksArray.delete(0, blocksArray.length);\n }\n\n // Insert new blocks\n const ymaps = blocks.map((block) => blockToYMap(block));\n blocksArray.push(ymaps);\n });\n }\n\n /**\n * Get a single block by index. Supports dot notation for nested blocks\n * (e.g., \"2.1\" means inner block at index 1 of top-level block at index 2).\n */\n getBlockByIndex(doc: Y.Doc, index: string): Block | null {\n const ymap = this._resolveBlockYMap(doc, index);\n if (!ymap) {\n return null;\n }\n return yMapToBlock(ymap);\n }\n\n /**\n * Update a block's content and/or attributes at a given index.\n */\n updateBlock(\n doc: Y.Doc,\n index: string,\n changes: { content?: string; attributes?: Record<string, unknown> },\n ): void {\n doc.transact(() => {\n const ymap = this._resolveBlockYMap(doc, index);\n if (!ymap) {\n return;\n }\n\n const blockName = ymap.get('name') as string;\n const attrMap = ymap.get('attributes') as Y.Map<unknown>;\n\n if (changes.content !== undefined) {\n // 'content' is the primary rich-text attribute for most blocks\n if (isRichTextAttribute(blockName, 'content')) {\n const ytext = attrMap.get('content');\n if (ytext instanceof Y.Text) {\n deltaUpdateYText(ytext, changes.content);\n } else {\n // Create Y.Text if it doesn't exist yet\n const newYText = new Y.Text();\n newYText.insert(0, changes.content);\n attrMap.set('content', newYText);\n }\n } else {\n attrMap.set('content', changes.content);\n }\n }\n\n if (changes.attributes) {\n for (const [key, value] of Object.entries(changes.attributes)) {\n if (\n isRichTextAttribute(blockName, key) &&\n typeof value === 'string'\n ) {\n const existing = attrMap.get(key);\n if (existing instanceof Y.Text) {\n deltaUpdateYText(existing, value);\n } else {\n const newYText = new Y.Text();\n newYText.insert(0, value);\n attrMap.set(key, newYText);\n }\n } else {\n attrMap.set(key, value);\n }\n }\n }\n });\n }\n\n /**\n * Insert a block at the given position in the top-level blocks array.\n */\n insertBlock(doc: Y.Doc, position: number, block: Block): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n let blocksArray = documentMap.get('blocks') as Y.Array<\n Y.Map<unknown>\n > | undefined;\n if (!blocksArray) {\n blocksArray = new Y.Array<Y.Map<unknown>>();\n documentMap.set('blocks', blocksArray);\n }\n const ymap = blockToYMap(block);\n blocksArray.insert(position, [ymap]);\n });\n }\n\n /**\n * Remove `count` blocks starting at `startIndex`.\n */\n removeBlocks(doc: Y.Doc, startIndex: number, count: number): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n const blocksArray = documentMap.get('blocks') as Y.Array<\n Y.Map<unknown>\n >;\n blocksArray.delete(startIndex, count);\n });\n }\n\n /**\n * Insert a block as an inner block of a parent block.\n */\n insertInnerBlock(doc: Y.Doc, parentIndex: string, position: number, block: Block): void {\n doc.transact(() => {\n const parentYMap = this._resolveBlockYMap(doc, parentIndex);\n if (!parentYMap) {\n throw new Error(`Block not found at index ${parentIndex}`);\n }\n\n let innerBlocksArray = parentYMap.get('innerBlocks') as Y.Array<Y.Map<unknown>> | undefined;\n if (!innerBlocksArray) {\n innerBlocksArray = new Y.Array<Y.Map<unknown>>();\n parentYMap.set('innerBlocks', innerBlocksArray);\n }\n\n const ymap = blockToYMap(block);\n innerBlocksArray.insert(position, [ymap]);\n });\n }\n\n /**\n * Remove inner blocks from a parent block.\n */\n removeInnerBlocks(doc: Y.Doc, parentIndex: string, startIndex: number, count: number): void {\n doc.transact(() => {\n const parentYMap = this._resolveBlockYMap(doc, parentIndex);\n if (!parentYMap) {\n throw new Error(`Block not found at index ${parentIndex}`);\n }\n\n const innerBlocksArray = parentYMap.get('innerBlocks') as Y.Array<Y.Map<unknown>> | undefined;\n if (!innerBlocksArray) {\n throw new Error(`Block at ${parentIndex} has no inner blocks`);\n }\n\n innerBlocksArray.delete(startIndex, count);\n });\n }\n\n /**\n * Move a block from one position to another.\n */\n moveBlock(doc: Y.Doc, fromIndex: number, toIndex: number): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n const blocksArray = documentMap.get('blocks') as Y.Array<\n Y.Map<unknown>\n >;\n\n // Read the block at fromIndex as a plain object, then re-insert\n const block = yMapToBlock(blocksArray.get(fromIndex));\n blocksArray.delete(fromIndex, 1);\n\n // Adjust target index if removing shifts it\n const adjustedIndex = fromIndex < toIndex ? toIndex - 1 : toIndex;\n const ymap = blockToYMap(block);\n blocksArray.insert(adjustedIndex, [ymap]);\n });\n }\n\n /**\n * Mark the document as saved by updating the state map.\n */\n markSaved(doc: Y.Doc): void {\n doc.transact(() => {\n const stateMap = this.getStateMap(doc);\n stateMap.set(CRDT_STATE_MAP_SAVED_AT_KEY, Date.now());\n stateMap.set(CRDT_STATE_MAP_SAVED_BY_KEY, doc.clientID);\n });\n }\n\n /**\n * Get the content field as a string.\n */\n getContent(doc: Y.Doc): string {\n const documentMap = this.getDocumentMap(doc);\n const content = documentMap.get('content');\n if (content instanceof Y.Text) {\n return content.toString();\n }\n return '';\n }\n\n /**\n * Set the content field.\n */\n setContent(doc: Y.Doc, content: string): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n const ytext = documentMap.get('content');\n if (ytext instanceof Y.Text) {\n deltaUpdateYText(ytext, content);\n } else {\n const newYText = new Y.Text();\n newYText.insert(0, content);\n documentMap.set('content', newYText);\n }\n });\n }\n\n /**\n * Get a post property from the document map.\n */\n getProperty(doc: Y.Doc, key: string): unknown {\n const documentMap = this.getDocumentMap(doc);\n const value = documentMap.get(key);\n if (value instanceof Y.Text) {\n return value.toString();\n }\n return value;\n }\n\n /**\n * Set a post property in the document map.\n */\n setProperty(doc: Y.Doc, key: string, value: unknown): void {\n doc.transact(() => {\n const documentMap = this.getDocumentMap(doc);\n const existing = documentMap.get(key);\n if (existing instanceof Y.Text && typeof value === 'string') {\n deltaUpdateYText(existing, value);\n } else {\n documentMap.set(key, value);\n }\n });\n }\n\n /**\n * Get the raw Y.Text for a block attribute.\n * Returns null if the block doesn't exist, the attribute doesn't exist,\n * or the attribute is not a Y.Text (i.e., not a rich-text attribute).\n */\n getBlockAttributeYText(doc: Y.Doc, index: string, attrName: string): Y.Text | null {\n const ymap = this._resolveBlockYMap(doc, index);\n if (!ymap) return null;\n const attrMap = ymap.get('attributes') as Y.Map<unknown> | undefined;\n if (!attrMap) return null;\n const attr = attrMap.get(attrName);\n return attr instanceof Y.Text ? attr : null;\n }\n\n /**\n * Get the raw Y.Text for a block's content attribute.\n * Returns null if the block or content attribute doesn't exist.\n */\n getBlockContentYText(doc: Y.Doc, index: string): Y.Text | null {\n return this.getBlockAttributeYText(doc, index, 'content');\n }\n\n /**\n * Resolve a dot-notation index to a Y.Map block reference.\n * E.g., \"2\" → top-level block 2, \"2.1\" → inner block 1 of block 2.\n */\n private _resolveBlockYMap(\n doc: Y.Doc,\n index: string,\n ): Y.Map<unknown> | null {\n const parts = index.split('.').map(Number);\n const documentMap = this.getDocumentMap(doc);\n const blocksArray = documentMap.get('blocks') as Y.Array<\n Y.Map<unknown>\n >;\n\n if (parts.length === 0 || isNaN(parts[0])) {\n return null;\n }\n\n let current: Y.Map<unknown> | null = null;\n let currentArray: Y.Array<Y.Map<unknown>> = blocksArray;\n\n for (const part of parts) {\n if (part < 0 || part >= currentArray.length) {\n return null;\n }\n current = currentArray.get(part);\n // For next iteration, if there are more parts, descend into innerBlocks\n const innerBlocks = current.get('innerBlocks') as\n | Y.Array<Y.Map<unknown>>\n | undefined;\n if (innerBlocks) {\n currentArray = innerBlocks;\n }\n }\n\n return current;\n }\n}\n","import type {\n WordPressConfig,\n WPPost,\n WPUser,\n SyncPayload,\n SyncResponse,\n} from './types.js';\n\n/**\n * WordPress REST API client using Application Password (HTTP Basic Auth).\n *\n * Uses native fetch() — requires Node.js 18+.\n */\nexport class WordPressApiClient {\n private baseUrl: string;\n private authHeader: string;\n\n constructor(config: WordPressConfig) {\n // Normalize URL: strip trailing slash(es)\n const siteUrl = config.siteUrl.replace(/\\/+$/, '');\n this.baseUrl = `${siteUrl}/wp-json`;\n this.authHeader = `Basic ${btoa(config.username + ':' + config.appPassword)}`;\n }\n\n /**\n * Validate the connection by fetching the current user.\n * Tests both auth and API availability.\n */\n async validateConnection(): Promise<WPUser> {\n return this.getCurrentUser();\n }\n\n /**\n * Check that the sync endpoint exists.\n * POSTs an empty rooms array to verify the endpoint responds.\n */\n async validateSyncEndpoint(): Promise<void> {\n await this.sendSyncUpdate({ rooms: [] });\n }\n\n /**\n * Get the current authenticated user.\n * GET /wp/v2/users/me\n */\n async getCurrentUser(): Promise<WPUser> {\n return this.apiFetch<WPUser>('/wp/v2/users/me');\n }\n\n /**\n * List posts with optional filters.\n * GET /wp/v2/posts?status=...&search=...&per_page=...&context=edit\n */\n async listPosts(options?: {\n status?: string;\n search?: string;\n perPage?: number;\n }): Promise<WPPost[]> {\n const params = new URLSearchParams({ context: 'edit' });\n\n if (options?.status) {\n params.set('status', options.status);\n }\n if (options?.search) {\n params.set('search', options.search);\n }\n if (options?.perPage !== undefined) {\n params.set('per_page', String(options.perPage));\n }\n\n return this.apiFetch<WPPost[]>(`/wp/v2/posts?${params.toString()}`);\n }\n\n /**\n * Get a single post by ID.\n * GET /wp/v2/posts/{id}?context=edit\n */\n async getPost(id: number): Promise<WPPost> {\n return this.apiFetch<WPPost>(`/wp/v2/posts/${id}?context=edit`);\n }\n\n /**\n * Create a new post.\n * POST /wp/v2/posts\n */\n async createPost(data: {\n title?: string;\n content?: string;\n status?: string;\n }): Promise<WPPost> {\n return this.apiFetch<WPPost>('/wp/v2/posts', {\n method: 'POST',\n body: JSON.stringify(data),\n });\n }\n\n /**\n * Send a sync payload and receive response.\n * POST /wp-sync/v1/updates\n */\n async sendSyncUpdate(payload: SyncPayload): Promise<SyncResponse> {\n return this.apiFetch<SyncResponse>('/wp-sync/v1/updates', {\n method: 'POST',\n body: JSON.stringify(payload),\n });\n }\n\n /**\n * Produce a human-friendly error message for common failure modes.\n */\n private formatErrorMessage(path: string, status: number, body: string): string {\n if (status === 401 || status === 403) {\n return `Authentication failed. Check your username and Application Password. (HTTP ${status})`;\n }\n\n if (status === 404 && path.startsWith('/wp-sync/')) {\n return (\n 'Collaborative editing is not enabled. ' +\n 'Enable it in Settings \\u2192 Writing in your WordPress admin, then try again. ' +\n '(Requires WordPress 7.0 or later.)'\n );\n }\n\n return `WordPress API error ${status}: ${body}`;\n }\n\n /**\n * Internal fetch helper with auth and error handling.\n */\n private async apiFetch<T>(path: string, options?: RequestInit): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n Authorization: this.authHeader,\n Accept: 'application/json',\n };\n\n // Add Content-Type for requests with a body\n if (options?.method === 'POST' || options?.method === 'PUT') {\n headers['Content-Type'] = 'application/json';\n }\n\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...(options?.headers as Record<string, string> | undefined),\n },\n });\n\n if (!response.ok) {\n let errorBody: string;\n try {\n errorBody = await response.text();\n } catch {\n errorBody = '(unable to read response body)';\n }\n\n const message = this.formatErrorMessage(path, response.status, errorBody);\n\n throw new WordPressApiError(\n message,\n response.status,\n errorBody,\n );\n }\n\n return (await response.json()) as T;\n }\n}\n\n/**\n * Custom error class for WordPress API errors,\n * carrying the HTTP status and response body.\n */\nexport class WordPressApiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body: string,\n ) {\n super(message);\n this.name = 'WordPressApiError';\n }\n}\n","import type {\n SyncUpdate,\n SyncClientConfig,\n SyncUpdateType,\n AwarenessState,\n LocalAwarenessState,\n} from './types.js';\nimport type { WordPressApiClient } from './api-client.js';\n\nexport type SyncStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface SyncCallbacks {\n /** Process an incoming update. Return a SyncUpdate to queue (e.g. sync_step2), or null. */\n onUpdate: (update: SyncUpdate) => SyncUpdate | null;\n /** Called when awareness state changes. */\n onAwareness: (state: AwarenessState) => void;\n /** Called when connection status changes. */\n onStatusChange: (status: SyncStatus) => void;\n /** Called when the server requests compaction; must return a compaction update. */\n onCompactionRequested: () => SyncUpdate;\n /** Return the current local awareness state, or null if disconnected. */\n getAwarenessState: () => LocalAwarenessState;\n}\n\n/**\n * HTTP polling sync client that maintains the Gutenberg sync loop.\n *\n * Uses chained setTimeout (not setInterval) so polling interval\n * can adapt dynamically to collaborator presence and errors.\n */\nexport class SyncClient {\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private endCursor: number = 0;\n private updateQueue: SyncUpdate[] = [];\n private queuePaused: boolean = true;\n private hasCollaborators: boolean = false;\n private currentBackoff: number;\n private isPolling: boolean = false;\n private pollInProgress: boolean = false;\n private flushRequested: boolean = false;\n private room: string = '';\n private clientId: number = 0;\n\n private callbacks: SyncCallbacks | null = null;\n private firstPollResolve: (() => void) | null = null;\n\n constructor(\n private apiClient: WordPressApiClient,\n private config: SyncClientConfig,\n ) {\n this.currentBackoff = config.pollingInterval;\n }\n\n /**\n * Returns a promise that resolves after the first poll cycle completes.\n * Used to wait for initial sync state before loading content.\n */\n waitForFirstPoll(): Promise<void> {\n return new Promise<void>((resolve) => {\n this.firstPollResolve = resolve;\n });\n }\n\n /**\n * Start the polling loop for a room.\n *\n * @param room Room identifier, e.g. 'postType/post:123'\n * @param clientId Y.Doc clientID\n * @param initialUpdates Initial sync updates to send (sync_step1)\n * @param callbacks Event callbacks\n */\n start(\n room: string,\n clientId: number,\n initialUpdates: SyncUpdate[],\n callbacks: SyncCallbacks,\n ): void {\n this.room = room;\n this.clientId = clientId;\n this.callbacks = callbacks;\n this.endCursor = 0;\n this.queuePaused = true;\n this.hasCollaborators = false;\n this.currentBackoff = this.config.pollingInterval;\n this.isPolling = true;\n\n // Seed queue with initial updates (e.g. sync_step1)\n this.updateQueue = [...initialUpdates];\n\n this.callbacks.onStatusChange('connecting');\n\n // Kick off the first poll immediately\n this.pollTimer = setTimeout(() => this.poll(), 0);\n }\n\n /**\n * Stop the polling loop.\n */\n stop(): void {\n this.isPolling = false;\n if (this.pollTimer !== null) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.callbacks?.onStatusChange('disconnected');\n this.callbacks = null;\n }\n\n /**\n * Flush the outgoing queue by triggering an immediate poll.\n *\n * If a poll is already in progress, sets a flag so that another poll\n * is triggered immediately after the current one completes. This avoids\n * concurrent poll execution while ensuring the flush is honoured.\n */\n flushQueue(): void {\n if (!this.isPolling) return;\n\n if (this.pollInProgress) {\n // A poll is already running — request a follow-up poll when it finishes\n this.flushRequested = true;\n return;\n }\n\n // Cancel the scheduled timer and poll immediately\n if (this.pollTimer !== null) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.pollTimer = setTimeout(() => this.poll(), 0);\n }\n\n /**\n * Add an update to the outgoing queue.\n */\n queueUpdate(update: SyncUpdate): void {\n this.updateQueue.push(update);\n }\n\n /**\n * Get sync status info.\n */\n getStatus(): {\n isPolling: boolean;\n hasCollaborators: boolean;\n queuePaused: boolean;\n endCursor: number;\n queueSize: number;\n } {\n return {\n isPolling: this.isPolling,\n hasCollaborators: this.hasCollaborators,\n queuePaused: this.queuePaused,\n endCursor: this.endCursor,\n queueSize: this.updateQueue.length,\n };\n }\n\n /**\n * Execute one poll cycle.\n */\n private async poll(): Promise<void> {\n if (!this.isPolling || !this.callbacks) {\n return;\n }\n\n // Re-entrancy guard: if a poll is already running, skip\n if (this.pollInProgress) {\n return;\n }\n\n this.pollInProgress = true;\n this.flushRequested = false;\n\n // Drain the queue: take all pending updates.\n // When paused, only initial/sync updates are sent (queue is drained regardless);\n // the distinction is that new edits are only queued when unpaused.\n const updates = this.updateQueue.splice(0);\n\n const awareness = this.callbacks.getAwarenessState();\n\n try {\n const payload = {\n rooms: [\n {\n room: this.room,\n client_id: this.clientId,\n after: this.endCursor,\n awareness,\n updates,\n },\n ],\n };\n\n const response = await this.apiClient.sendSyncUpdate(payload);\n\n // Success — reset backoff\n this.currentBackoff = this.hasCollaborators\n ? this.config.pollingIntervalWithCollaborators\n : this.config.pollingInterval;\n\n this.callbacks.onStatusChange('connected');\n\n this.processResponse(response);\n\n // Resolve the first-poll promise after processing the response\n if (this.firstPollResolve) {\n this.firstPollResolve();\n this.firstPollResolve = null;\n }\n } catch (error) {\n // Restore un-sent updates to front of queue,\n // excluding compaction updates (which are stale after an error).\n const restorable = updates.filter(\n (u) => (u.type as SyncUpdateType) !== ('compaction' as SyncUpdateType),\n );\n this.updateQueue.unshift(...restorable);\n\n this.callbacks.onStatusChange('error');\n\n // Exponential backoff: double current, cap at max\n this.currentBackoff = Math.min(\n this.currentBackoff * 2,\n this.config.maxErrorBackoff,\n );\n }\n\n this.pollInProgress = false;\n\n // If a flush was requested during this poll, trigger an immediate follow-up\n if (this.flushRequested) {\n this.flushRequested = false;\n if (this.pollTimer !== null) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.pollTimer = setTimeout(() => this.poll(), 0);\n } else {\n this.scheduleNextPoll();\n }\n }\n\n /**\n * Schedule the next poll using the current interval / backoff.\n */\n private scheduleNextPoll(): void {\n if (!this.isPolling) {\n return;\n }\n\n this.pollTimer = setTimeout(() => this.poll(), this.currentBackoff);\n }\n\n /**\n * Process a sync response from the server.\n */\n private processResponse(response: { rooms: Array<{\n room: string;\n end_cursor: number;\n awareness: AwarenessState;\n updates: SyncUpdate[];\n should_compact?: boolean;\n }> }): void {\n if (!this.callbacks) {\n return;\n }\n\n const roomData = response.rooms.find((r) => r.room === this.room);\n if (!roomData) {\n return;\n }\n\n // 1. Update end cursor\n this.endCursor = roomData.end_cursor;\n\n // 2. Process awareness — detect collaborators\n this.callbacks.onAwareness(roomData.awareness);\n\n const otherClients = Object.keys(roomData.awareness).filter(\n (id) => Number(id) !== this.clientId && roomData.awareness[id] !== null,\n );\n const hadCollaborators = this.hasCollaborators;\n this.hasCollaborators = otherClients.length > 0;\n\n // If we just gained collaborators, unpause the queue\n if (this.hasCollaborators && !hadCollaborators) {\n this.queuePaused = false;\n }\n\n // Adjust backoff/interval for collaborator presence\n this.currentBackoff = this.hasCollaborators\n ? this.config.pollingIntervalWithCollaborators\n : this.config.pollingInterval;\n\n // 3. Process incoming updates\n for (const update of roomData.updates) {\n const reply = this.callbacks.onUpdate(update);\n if (reply) {\n this.updateQueue.push(reply);\n }\n }\n\n // 4. Handle compaction request\n if (roomData.should_compact) {\n const compactionUpdate = this.callbacks.onCompactionRequested();\n this.updateQueue.push(compactionUpdate);\n }\n }\n}\n","/**\n * WordPress REST API and sync protocol types.\n *\n * These types match the wire format used by the Gutenberg HTTP polling\n * sync provider at POST /wp-sync/v1/updates.\n */\n\n// --- Sync Protocol Types ---\n\nexport enum SyncUpdateType {\n SYNC_STEP_1 = 'sync_step1',\n SYNC_STEP_2 = 'sync_step2',\n UPDATE = 'update',\n COMPACTION = 'compaction',\n}\n\n/** A single typed update with base64-encoded Yjs binary data. */\nexport interface SyncUpdate {\n type: SyncUpdateType;\n data: string; // base64-encoded Yjs V2 update\n}\n\n/** Awareness state: null means disconnected. */\nexport type LocalAwarenessState = object | null;\n\n/** Server-side awareness: map of clientId (as string) → state object. */\nexport type AwarenessState = Record<string, LocalAwarenessState>;\n\n// --- Client → Server ---\n\nexport interface SyncEnvelopeFromClient {\n room: string;\n client_id: number;\n after: number;\n awareness: LocalAwarenessState;\n updates: SyncUpdate[];\n}\n\nexport interface SyncPayload {\n rooms: SyncEnvelopeFromClient[];\n}\n\n// --- Server → Client ---\n\nexport interface SyncEnvelopeFromServer {\n room: string;\n end_cursor: number;\n awareness: AwarenessState;\n updates: SyncUpdate[];\n should_compact?: boolean;\n compaction_request?: SyncUpdate[]; // deprecated\n}\n\nexport interface SyncResponse {\n rooms: SyncEnvelopeFromServer[];\n}\n\n// --- WordPress REST API Types ---\n\n/** WordPress post as returned by the REST API (subset of fields we care about). */\nexport interface WPPost {\n id: number;\n title: { rendered: string; raw?: string };\n content: { rendered: string; raw?: string };\n excerpt: { rendered: string; raw?: string };\n status: string;\n type: string;\n slug: string;\n author: number;\n date: string | null;\n modified: string;\n categories?: number[];\n tags?: number[];\n meta?: Record<string, unknown>;\n}\n\n/** WordPress user as returned by /wp/v2/users/me. */\nexport interface WPUser {\n id: number;\n name: string;\n slug: string;\n avatar_urls: Record<string, string>;\n}\n\n// --- Connection Config ---\n\nexport interface WordPressConfig {\n siteUrl: string;\n username: string;\n appPassword: string;\n}\n\n// --- Sync Client Config ---\n\nexport interface SyncClientConfig {\n /** Polling interval in ms when editing solo. */\n pollingInterval: number;\n /** Polling interval in ms when collaborators are present. */\n pollingIntervalWithCollaborators: number;\n /** Max exponential backoff in ms on error. */\n maxErrorBackoff: number;\n}\n\nexport const DEFAULT_SYNC_CONFIG: SyncClientConfig = {\n pollingInterval: 1000,\n pollingIntervalWithCollaborators: 250,\n maxErrorBackoff: 30_000,\n};\n","/**\n * Yjs sync protocol helpers for the HTTP polling transport.\n *\n * Sync steps (step1/step2) use y-protocols' standard encoding.\n * Regular updates and compactions use Yjs V1 encoding (matching Gutenberg).\n * All binary data is base64-encoded for transport.\n */\nimport * as Y from 'yjs';\nimport * as syncProtocol from 'y-protocols/sync';\nimport * as encoding from 'lib0/encoding';\nimport * as decoding from 'lib0/decoding';\nimport { type SyncUpdate, SyncUpdateType } from '../wordpress/types.js';\n\n/**\n * Create a sync_step1 message announcing our state vector.\n */\nexport function createSyncStep1(doc: Y.Doc): SyncUpdate {\n const encoder = encoding.createEncoder();\n syncProtocol.writeSyncStep1(encoder, doc);\n const data = encoding.toUint8Array(encoder);\n\n return {\n type: SyncUpdateType.SYNC_STEP_1,\n data: uint8ArrayToBase64(data),\n };\n}\n\n/**\n * Process an incoming sync_step1 and create a sync_step2 response.\n *\n * Reads the remote state vector from the step1 message and encodes\n * the missing updates as a step2 reply.\n */\nexport function createSyncStep2(\n doc: Y.Doc,\n step1Data: Uint8Array,\n): SyncUpdate {\n const decoder = decoding.createDecoder(step1Data);\n const encoder = encoding.createEncoder();\n\n // readSyncMessage reads the message type byte and the state vector,\n // then writes the appropriate response (step2) into the encoder.\n syncProtocol.readSyncMessage(decoder, encoder, doc, 'sync');\n\n const data = encoding.toUint8Array(encoder);\n\n return {\n type: SyncUpdateType.SYNC_STEP_2,\n data: uint8ArrayToBase64(data),\n };\n}\n\n/**\n * Process an incoming sync update.\n *\n * For SYNC_STEP_1: generates a SYNC_STEP_2 response.\n * For SYNC_STEP_2: applies the update via y-protocols and returns null.\n * For UPDATE / COMPACTION: applies the V1 update and returns null.\n *\n * Returns a response SyncUpdate if one is needed (e.g., step2 reply),\n * or null if no response is required.\n */\nexport function processIncomingUpdate(\n doc: Y.Doc,\n update: SyncUpdate,\n): SyncUpdate | null {\n const rawData = base64ToUint8Array(update.data);\n\n switch (update.type) {\n case SyncUpdateType.SYNC_STEP_1: {\n // Respond with step2\n return createSyncStep2(doc, rawData);\n }\n\n case SyncUpdateType.SYNC_STEP_2: {\n // Apply step2 via y-protocols decoder\n const decoder = decoding.createDecoder(rawData);\n const encoder = encoding.createEncoder();\n syncProtocol.readSyncMessage(decoder, encoder, doc, 'sync');\n // step2 processing doesn't produce a response\n return null;\n }\n\n case SyncUpdateType.UPDATE:\n case SyncUpdateType.COMPACTION: {\n // Apply V1 update directly (Gutenberg uses V1 encoding for updates)\n Y.applyUpdate(doc, rawData, 'remote');\n return null;\n }\n\n default:\n return null;\n }\n}\n\n/**\n * Create an update message from a Y.Doc change (V1 encoded).\n */\nexport function createUpdateFromChange(update: Uint8Array): SyncUpdate {\n return {\n type: SyncUpdateType.UPDATE,\n data: uint8ArrayToBase64(update),\n };\n}\n\n/**\n * Create a compaction update containing the full document state (V1 encoded).\n */\nexport function createCompactionUpdate(doc: Y.Doc): SyncUpdate {\n const data = Y.encodeStateAsUpdate(doc);\n return {\n type: SyncUpdateType.COMPACTION,\n data: uint8ArrayToBase64(data),\n };\n}\n\n/**\n * Encode a Uint8Array to a base64 string.\n */\nexport function uint8ArrayToBase64(data: Uint8Array): string {\n return Buffer.from(data).toString('base64');\n}\n\n/**\n * Decode a base64 string to a Uint8Array.\n */\nexport function base64ToUint8Array(base64: string): Uint8Array {\n return new Uint8Array(Buffer.from(base64, 'base64'));\n}\n","/**\n * Block parser: wraps @wordpress/block-serialization-default-parser to produce\n * normalized ParsedBlock[] from Gutenberg HTML, then converts to Block[].\n */\n\nimport { parse as wpParse } from '@wordpress/block-serialization-default-parser';\nimport type { RawParsedBlock, ParsedBlock } from './types.js';\nimport type { Block } from '../yjs/types.js';\n\n/**\n * Parse Gutenberg HTML content into normalized blocks.\n * - Null blockNames (freeform HTML) become 'core/freeform'\n * - Attributes are extracted from the block comment delimiters (attrs field)\n * - innerHTML is preserved as originalContent\n * - For common blocks, extract text content from innerHTML into attributes\n */\nexport function parseBlocks(html: string): ParsedBlock[] {\n const raw = wpParse(html) as RawParsedBlock[];\n return raw\n .filter((block) => block.blockName !== null || block.innerHTML.trim() !== '')\n .map(normalizeParsedBlock);\n}\n\n/**\n * Convert a ParsedBlock to a Block (adding clientId, mapping innerBlocks).\n */\nexport function parsedBlockToBlock(parsed: ParsedBlock): Block {\n return {\n name: parsed.name,\n clientId: crypto.randomUUID(),\n attributes: { ...parsed.attributes },\n innerBlocks: parsed.innerBlocks.map(parsedBlockToBlock),\n originalContent: parsed.originalContent,\n };\n}\n\nfunction normalizeParsedBlock(raw: RawParsedBlock): ParsedBlock {\n const blockName = raw.blockName ?? 'core/freeform';\n const commentAttrs = raw.attrs ?? {};\n const extractedAttrs = extractAttributesFromHTML(\n blockName,\n raw.innerHTML,\n commentAttrs,\n );\n\n return {\n name: blockName,\n attributes: { ...commentAttrs, ...extractedAttrs },\n innerBlocks: raw.innerBlocks\n .filter(\n (block) => block.blockName !== null || block.innerHTML.trim() !== '',\n )\n .map(normalizeParsedBlock),\n originalContent: raw.innerHTML,\n };\n}\n\n/**\n * Extract content/text attributes from a block's innerHTML.\n * For core/paragraph: extract text between <p> tags -> attributes.content\n * For core/heading: extract text between <h1>-<h6> tags -> attributes.content\n * For core/list-item: extract text between <li> tags -> attributes.content\n * For core/image: extract src, alt from <img> tag -> attributes.url, attributes.alt\n * For core/button: extract text from <a> tag -> attributes.text\n */\nfunction extractAttributesFromHTML(\n blockName: string,\n innerHTML: string,\n attrs: Record<string, unknown>,\n): Record<string, unknown> {\n const extracted: Record<string, unknown> = {};\n\n switch (blockName) {\n case 'core/paragraph': {\n const match = innerHTML.match(/<p[^>]*>([\\s\\S]*?)<\\/p>/);\n if (match) {\n extracted.content = match[1].trim();\n }\n break;\n }\n case 'core/heading': {\n const match = innerHTML.match(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/);\n if (match) {\n extracted.content = match[1].trim();\n }\n // Preserve the level from comment attrs if present\n if (attrs.level !== undefined) {\n extracted.level = attrs.level;\n }\n break;\n }\n case 'core/list-item': {\n const match = innerHTML.match(/<li[^>]*>([\\s\\S]*?)<\\/li>/);\n if (match) {\n extracted.content = match[1].trim();\n }\n break;\n }\n case 'core/image': {\n const imgMatch = innerHTML.match(/<img[^>]*>/);\n if (imgMatch) {\n const srcMatch = imgMatch[0].match(/src=\"([^\"]*)\"/);\n if (srcMatch) {\n extracted.url = srcMatch[1];\n }\n const altMatch = imgMatch[0].match(/alt=\"([^\"]*)\"/);\n if (altMatch) {\n extracted.alt = altMatch[1];\n }\n }\n break;\n }\n case 'core/button': {\n const match = innerHTML.match(/<a[^>]*>([\\s\\S]*?)<\\/a>/);\n if (match) {\n extracted.text = match[1].trim();\n }\n // Also extract the href as url\n const hrefMatch = innerHTML.match(/<a[^>]*href=\"([^\"]*)\"[^>]*>/);\n if (hrefMatch) {\n extracted.url = hrefMatch[1];\n }\n break;\n }\n }\n\n return extracted;\n}\n","/**\n * Block renderer: converts Block[] to Claude-friendly text representation.\n */\n\nimport type { Block } from '../yjs/types.js';\n\n/**\n * Render a post's blocks as Claude-friendly text.\n *\n * Output format:\n * Title: \"Post Title Here\"\n *\n * [0] core/heading (level=2)\n * \"What Are Widgets?\"\n *\n * [1] core/paragraph\n * \"Widgets are fundamental building blocks...\"\n */\nexport function renderPost(title: string, blocks: Block[]): string {\n const parts: string[] = [];\n parts.push(`Title: \"${title}\"`);\n\n if (blocks.length > 0) {\n parts.push('');\n parts.push(renderBlockList(blocks));\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Render a single block with its details.\n */\nexport function renderBlock(block: Block, index: string): string {\n const displayAttrs = getDisplayAttributes(block);\n const attrStr = Object.entries(displayAttrs)\n .map(([k, v]) => `${k}=${typeof v === 'string' ? `\"${v}\"` : v}`)\n .join(', ');\n\n const header = attrStr\n ? `[${index}] ${block.name} (${attrStr})`\n : `[${index}] ${block.name}`;\n\n const lines: string[] = [header];\n\n const textContent = getBlockTextContent(block);\n if (textContent) {\n lines.push(` \"${textContent}\"`);\n }\n\n if (block.innerBlocks.length > 0) {\n const innerRendered = renderBlockList(block.innerBlocks, index, 1);\n lines.push(innerRendered);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Render blocks as a list with indices.\n * @param blocks - The blocks to render\n * @param parentIndex - Parent index prefix for nested blocks (e.g., \"2\" -> \"2.0\", \"2.1\")\n * @param indent - Current indentation level\n */\nfunction renderBlockList(\n blocks: Block[],\n parentIndex?: string,\n indent: number = 0,\n): string {\n const indentStr = ' '.repeat(indent);\n\n return blocks\n .map((block, i) => {\n const index =\n parentIndex !== undefined ? `${parentIndex}.${i}` : String(i);\n const rendered = renderBlock(block, index);\n // Indent each line of the rendered block\n if (indent > 0) {\n return rendered\n .split('\\n')\n .map((line) => indentStr + line)\n .join('\\n');\n }\n return rendered;\n })\n .join('\\n\\n');\n}\n\n/**\n * Get the primary text content of a block from its attributes.\n * Checks 'content' first, then 'text', then 'value', then 'citation'.\n */\nfunction getBlockTextContent(block: Block): string {\n for (const key of ['content', 'text', 'value', 'citation']) {\n const val = block.attributes[key];\n if (typeof val === 'string' && val.length > 0) {\n return val;\n }\n }\n return '';\n}\n\n/**\n * Get display-worthy attributes (non-content attributes that are useful to show).\n * For headings: show level\n * For images: show url, alt\n * For buttons: show url\n * For columns: show verticalAlignment\n * Skip 'content', 'text', 'value' (shown separately as text content)\n * Skip complex objects/arrays\n */\nfunction getDisplayAttributes(\n block: Block,\n): Record<string, string | number | boolean> {\n const skipKeys = new Set(['content', 'text', 'value', 'citation']);\n const result: Record<string, string | number | boolean> = {};\n\n for (const [key, val] of Object.entries(block.attributes)) {\n if (skipKeys.has(key)) continue;\n if (\n typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean'\n ) {\n result[key] = val;\n }\n }\n\n return result;\n}\n","/**\n * Awareness protocol helpers for Claude's presence in collaborative editing.\n *\n * Builds the local awareness state identifying Claude as a collaborator\n * and parses the server's awareness state into a list of other collaborators.\n */\n\nimport type { CollaboratorInfo, AwarenessLocalState } from '../yjs/types.js';\nimport type { AwarenessState } from '../wordpress/types.js';\nimport type { WPUser } from '../wordpress/types.js';\n\n/**\n * Build the local awareness state that identifies Claude as a collaborator.\n * This state is sent with each sync request.\n */\nexport function buildAwarenessState(user: WPUser): AwarenessLocalState {\n return {\n collaboratorInfo: {\n id: user.id,\n name: `${user.name} (Claude)`,\n slug: user.slug,\n avatar_urls: user.avatar_urls ?? {},\n browserType: 'Claude Code MCP',\n enteredAt: Date.now(),\n },\n // editorState with selection is required for Gutenberg to recognize us\n // as an active editor and process our CRDT updates in the live session.\n editorState: {\n selection: { type: 'none' },\n },\n };\n}\n\n/**\n * Parse the server's awareness state into a list of collaborators.\n * Excludes our own client ID and null (disconnected) states.\n */\nexport function parseCollaborators(\n awarenessState: AwarenessState,\n ownClientId: number,\n): CollaboratorInfo[] {\n const collaborators: CollaboratorInfo[] = [];\n\n for (const [clientIdStr, state] of Object.entries(awarenessState)) {\n const clientId = Number(clientIdStr);\n if (clientId === ownClientId) {\n continue;\n }\n if (state === null || state === undefined) {\n continue;\n }\n const info = (state as { collaboratorInfo?: CollaboratorInfo })\n .collaboratorInfo;\n if (info) {\n collaborators.push(info);\n }\n }\n\n return collaborators;\n}\n","/**\n * Session manager: orchestrates the full connection lifecycle for\n * collaborative editing of WordPress posts via the Gutenberg sync protocol.\n *\n * Lifecycle: connect → openPost → edit/read → closePost → disconnect\n */\n\nimport * as Y from 'yjs';\nimport { DocumentManager } from '../yjs/document-manager.js';\nimport { WordPressApiClient } from '../wordpress/api-client.js';\nimport { SyncClient } from '../wordpress/sync-client.js';\nimport {\n createSyncStep1,\n processIncomingUpdate,\n createCompactionUpdate,\n createUpdateFromChange,\n} from '../yjs/sync-protocol.js';\nimport {\n computeTextDelta,\n findHtmlSafeChunkEnd,\n} from '../yjs/block-converter.js';\nimport { parseBlocks, parsedBlockToBlock } from '../blocks/parser.js';\nimport { renderPost, renderBlock } from '../blocks/renderer.js';\nimport { buildAwarenessState, parseCollaborators } from './awareness.js';\nimport { DEFAULT_SYNC_CONFIG } from '../wordpress/types.js';\nimport type { Block, CollaboratorInfo, AwarenessLocalState } from '../yjs/types.js';\nimport { isRichTextAttribute, getDefaultAttributes } from '../yjs/types.js';\nimport type {\n WordPressConfig,\n WPUser,\n WPPost,\n} from '../wordpress/types.js';\n\nexport type SessionState = 'disconnected' | 'connected' | 'editing';\n\n/**\n * Origin marker for local edits made through the session manager.\n * Used to distinguish local changes from sync updates when observing Y.Doc events.\n */\nconst LOCAL_ORIGIN = 'local';\n\n/** Streaming chunk size range in characters. Randomized for a natural feel. */\nconst STREAM_CHUNK_SIZE_MIN = 2;\nconst STREAM_CHUNK_SIZE_MAX = 6;\n\n/** Delay between streaming chunks in milliseconds. */\nconst STREAM_CHUNK_DELAY_MS = 200;\n\n/** Minimum text length to trigger streaming (short text is applied atomically). */\nconst STREAM_THRESHOLD = 20;\n\n/** Input shape for blocks with optional recursive inner blocks. */\nexport interface BlockInput {\n name: string;\n content?: string;\n attributes?: Record<string, unknown>;\n innerBlocks?: BlockInput[];\n}\n\n/** A streaming target: a specific attribute in a block that needs progressive insertion. */\ninterface StreamTarget {\n blockIndex: string;\n attrName: string;\n value: string;\n}\n\n/**\n * Recursively prepare a block tree for insertion.\n * Applies default attributes, sets isValid/clientId, and separates\n * streamable rich-text content from atomic structure.\n *\n * @returns The Block (with empty placeholders for streamable content)\n * and a flat list of StreamTargets for progressive insertion.\n */\nfunction prepareBlockTree(\n input: BlockInput,\n indexPrefix: string,\n): { block: Block; streamTargets: StreamTarget[] } {\n const defaults = getDefaultAttributes(input.name);\n const attrs = { ...defaults, ...input.attributes };\n const streamTargets: StreamTarget[] = [];\n\n // Handle 'content' field\n if (input.content !== undefined) {\n if (\n isRichTextAttribute(input.name, 'content') &&\n input.content.length >= STREAM_THRESHOLD\n ) {\n streamTargets.push({ blockIndex: indexPrefix, attrName: 'content', value: input.content });\n attrs.content = '';\n } else {\n attrs.content = input.content;\n }\n }\n\n // Check other attributes for streaming\n for (const [key, value] of Object.entries(attrs)) {\n if (\n key !== 'content' &&\n isRichTextAttribute(input.name, key) &&\n typeof value === 'string' &&\n value.length >= STREAM_THRESHOLD\n ) {\n streamTargets.push({ blockIndex: indexPrefix, attrName: key, value });\n attrs[key] = '';\n }\n }\n\n // Recurse into inner blocks\n const innerBlocks: Block[] = [];\n if (input.innerBlocks) {\n for (let i = 0; i < input.innerBlocks.length; i++) {\n const childIndex = `${indexPrefix}.${i}`;\n const prepared = prepareBlockTree(input.innerBlocks[i], childIndex);\n innerBlocks.push(prepared.block);\n streamTargets.push(...prepared.streamTargets);\n }\n }\n\n const block: Block = {\n name: input.name,\n clientId: crypto.randomUUID(),\n attributes: attrs,\n innerBlocks,\n isValid: true,\n };\n\n return { block, streamTargets };\n}\n\nexport class SessionManager {\n private apiClient: WordPressApiClient | null = null;\n private syncClient: SyncClient | null = null;\n private documentManager: DocumentManager;\n private doc: Y.Doc | null = null;\n private user: WPUser | null = null;\n private currentPost: WPPost | null = null;\n private state: SessionState = 'disconnected';\n private awarenessState: AwarenessLocalState | null = null;\n private collaborators: CollaboratorInfo[] = [];\n private updateHandler: ((update: Uint8Array, origin: unknown) => void) | null = null;\n\n /** Max time (ms) to wait for sync to populate the doc before loading from REST API. Set to 0 in tests. */\n syncWaitTimeout = 5000;\n\n constructor() {\n this.documentManager = new DocumentManager();\n }\n\n // --- Connection ---\n\n /**\n * Connect to a WordPress site.\n * Validates credentials and sync endpoint availability.\n */\n async connect(config: WordPressConfig): Promise<WPUser> {\n this.apiClient = new WordPressApiClient(config);\n\n // Validate credentials\n const user = await this.apiClient.validateConnection();\n this.user = user;\n\n // Validate sync endpoint is available\n await this.apiClient.validateSyncEndpoint();\n\n // Build awareness state from user info\n this.awarenessState = buildAwarenessState(user);\n\n this.state = 'connected';\n return user;\n }\n\n /**\n * Disconnect from the WordPress site.\n */\n disconnect(): void {\n if (this.state === 'editing') {\n this.closePost();\n }\n\n this.apiClient = null;\n this.user = null;\n this.awarenessState = null;\n this.collaborators = [];\n this.state = 'disconnected';\n }\n\n // --- Posts ---\n\n /**\n * List posts (delegates to API client).\n */\n async listPosts(options?: {\n status?: string;\n search?: string;\n perPage?: number;\n }): Promise<WPPost[]> {\n this.requireState('connected', 'editing');\n return this.apiClient!.listPosts(options);\n }\n\n /**\n * Open a post for collaborative editing.\n * Creates Y.Doc, loads initial content, starts sync.\n */\n async openPost(postId: number): Promise<void> {\n this.requireState('connected');\n\n // Fetch post from API\n const post = await this.apiClient!.getPost(postId);\n this.currentPost = post;\n\n // Create an EMPTY Y.Doc — don't load content yet.\n // We first sync with the server to receive any existing CRDT state.\n // Loading content independently would create divergent CRDT histories,\n // causing duplicate blocks when two clients sync.\n const doc = this.documentManager.createDoc();\n this.doc = doc;\n\n // Create sync client\n const syncClient = new SyncClient(this.apiClient!, { ...DEFAULT_SYNC_CONFIG });\n this.syncClient = syncClient;\n\n // Start sync with room = postType/${post.type}:${postId}\n const room = `postType/${post.type}:${postId}`;\n const initialUpdates = [createSyncStep1(doc)];\n\n syncClient.start(room, doc.clientID, initialUpdates, {\n onUpdate: (update) => {\n try {\n return processIncomingUpdate(doc, update);\n } catch {\n return null;\n }\n },\n onAwareness: (awarenessState) => {\n this.collaborators = parseCollaborators(awarenessState, doc.clientID);\n },\n onStatusChange: () => {\n // Status is read directly from syncClient.getStatus()\n },\n onCompactionRequested: () => {\n return createCompactionUpdate(doc);\n },\n getAwarenessState: () => {\n return this.awarenessState;\n },\n });\n\n // Wait for the sync handshake to populate the doc with remote content.\n // The handshake takes multiple poll cycles:\n // Poll 1: We send sync_step1 → receive peer's sync_step1\n // Poll 2: We send sync_step2 → receive peer's sync_step2 (their full state)\n // So we wait until the doc has blocks (meaning remote state arrived),\n // or timeout (meaning no peers are editing this post).\n if (this.syncWaitTimeout > 0) {\n await new Promise<void>((resolve) => {\n let resolved = false;\n const done = () => {\n if (!resolved) {\n resolved = true;\n doc.off('update', onDocUpdate);\n resolve();\n }\n };\n\n const timeout = setTimeout(done, this.syncWaitTimeout);\n\n const onDocUpdate = () => {\n const blocks = this.documentManager.getBlocks(doc);\n const title = this.documentManager.getTitle(doc);\n if (blocks.length > 0 || title.length > 0) {\n clearTimeout(timeout);\n done();\n }\n };\n\n doc.on('update', onDocUpdate);\n });\n }\n\n // If the doc is still empty after sync, load content from the REST API.\n // This means we're the first CRDT client for this post.\n const existingBlocks = this.documentManager.getBlocks(doc);\n if (existingBlocks.length === 0) {\n doc.transact(() => {\n // Set title\n if (post.title.raw) {\n this.documentManager.setTitle(doc, post.title.raw);\n }\n\n // Parse content into blocks\n if (post.content.raw) {\n const parsedBlocks = parseBlocks(post.content.raw);\n const blocks = parsedBlocks.map(parsedBlockToBlock);\n this.documentManager.setBlocks(doc, blocks);\n this.documentManager.setContent(doc, post.content.raw);\n }\n\n // Set excerpt\n if (post.excerpt.raw) {\n this.documentManager.setProperty(doc, 'excerpt', post.excerpt.raw);\n }\n\n // Set other post properties\n this.documentManager.setProperty(doc, 'status', post.status);\n this.documentManager.setProperty(doc, 'slug', post.slug);\n this.documentManager.setProperty(doc, 'author', post.author);\n }, LOCAL_ORIGIN);\n }\n\n // Set up Y.Doc observer to queue updates for local changes\n this.updateHandler = (update: Uint8Array, origin: unknown) => {\n // Only queue updates from local edits, not from sync\n if (origin === LOCAL_ORIGIN) {\n const syncUpdate = createUpdateFromChange(update);\n syncClient.queueUpdate(syncUpdate);\n }\n };\n doc.on('update', this.updateHandler);\n\n this.state = 'editing';\n }\n\n /**\n * Create a new post and open it for editing.\n */\n async createPost(data: {\n title?: string;\n content?: string;\n }): Promise<WPPost> {\n this.requireState('connected');\n\n const post = await this.apiClient!.createPost({\n title: data.title,\n content: data.content,\n status: 'draft',\n });\n\n await this.openPost(post.id);\n\n return post;\n }\n\n /**\n * Close the currently open post (stop sync).\n */\n closePost(): void {\n if (this.syncClient) {\n this.syncClient.stop();\n this.syncClient = null;\n }\n\n if (this.doc && this.updateHandler) {\n this.doc.off('update', this.updateHandler);\n this.updateHandler = null;\n }\n\n this.doc = null;\n this.currentPost = null;\n this.collaborators = [];\n this.state = 'connected';\n }\n\n // --- Reading ---\n\n /**\n * Render the current post as Claude-friendly text.\n */\n readPost(): string {\n this.requireState('editing');\n\n const title = this.documentManager.getTitle(this.doc!);\n const blocks = this.documentManager.getBlocks(this.doc!);\n return renderPost(title, blocks);\n }\n\n /**\n * Read a specific block by index (dot notation).\n */\n readBlock(index: string): string {\n this.requireState('editing');\n\n const block = this.documentManager.getBlockByIndex(this.doc!, index);\n if (!block) {\n throw new Error(`Block not found at index ${index}`);\n }\n return renderBlock(block, index);\n }\n\n // --- Editing ---\n\n /**\n * Update a block's content and/or attributes.\n *\n * Rich-text attributes that exceed the streaming threshold are streamed\n * in chunks so the browser sees progressive updates (like fast typing).\n * Non-rich-text and short changes are applied atomically.\n */\n async updateBlock(\n index: string,\n changes: { content?: string; attributes?: Record<string, unknown> },\n ): Promise<void> {\n this.requireState('editing');\n\n // Set cursor position BEFORE the edit — pointing to existing items\n // the browser already has. Gutenberg requires a real cursor position\n // to process remote edits, but if we set it AFTER the edit, the cursor\n // references new items the browser doesn't have yet (causing a crash).\n this.updateCursorPosition(index);\n\n // Identify which changes should be streamed vs applied atomically.\n // Look up the block name to determine rich-text attributes.\n const block = this.documentManager.getBlockByIndex(this.doc!, index);\n if (!block) return;\n\n const streamTargets: Array<{ attrName: string; newValue: string }> = [];\n const atomicChanges: { content?: string; attributes?: Record<string, unknown> } = {};\n\n // Check 'content' field\n if (changes.content !== undefined) {\n if (isRichTextAttribute(block.name, 'content') && changes.content.length >= STREAM_THRESHOLD) {\n streamTargets.push({ attrName: 'content', newValue: changes.content });\n } else {\n atomicChanges.content = changes.content;\n }\n }\n\n // Check explicit attributes\n if (changes.attributes) {\n const atomicAttrs: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(changes.attributes)) {\n if (\n isRichTextAttribute(block.name, key) &&\n typeof value === 'string' &&\n value.length >= STREAM_THRESHOLD\n ) {\n streamTargets.push({ attrName: key, newValue: value });\n } else {\n atomicAttrs[key] = value;\n }\n }\n if (Object.keys(atomicAttrs).length > 0) {\n atomicChanges.attributes = atomicAttrs;\n }\n }\n\n // Apply atomic changes (non-streaming) in one transaction\n if (atomicChanges.content !== undefined || atomicChanges.attributes) {\n this.doc!.transact(() => {\n this.documentManager.updateBlock(this.doc!, index, atomicChanges);\n }, LOCAL_ORIGIN);\n }\n\n // Stream each rich-text attribute\n for (const target of streamTargets) {\n let ytext = this.documentManager.getBlockAttributeYText(this.doc!, index, target.attrName);\n if (!ytext) {\n // Y.Text doesn't exist yet — create it atomically before streaming\n this.doc!.transact(() => {\n this.documentManager.updateBlock(this.doc!, index, {\n ...(target.attrName === 'content' ? { content: '' } : { attributes: { [target.attrName]: '' } }),\n });\n }, LOCAL_ORIGIN);\n ytext = this.documentManager.getBlockAttributeYText(this.doc!, index, target.attrName);\n }\n if (ytext) {\n await this.streamTextToYText(ytext, target.newValue, index);\n }\n }\n }\n\n /**\n * Insert a new block at position.\n *\n * The block structure (with empty content) is inserted atomically,\n * then rich-text content is streamed in progressively.\n * Supports recursive inner blocks.\n */\n async insertBlock(\n position: number,\n block: BlockInput,\n ): Promise<void> {\n this.requireState('editing');\n\n const blockIndex = String(position);\n const { block: fullBlock, streamTargets } = prepareBlockTree(block, blockIndex);\n\n // Insert block structure atomically\n this.doc!.transact(() => {\n this.documentManager.insertBlock(this.doc!, position, fullBlock);\n }, LOCAL_ORIGIN);\n\n // Stream rich-text content (depth-first: parent first, then children)\n await this.streamTargets(streamTargets);\n }\n\n /**\n * Remove blocks starting at index.\n */\n removeBlocks(startIndex: number, count: number): void {\n this.requireState('editing');\n this.doc!.transact(() => {\n this.documentManager.removeBlocks(this.doc!, startIndex, count);\n }, LOCAL_ORIGIN);\n }\n\n /**\n * Move a block from one position to another.\n */\n moveBlock(fromIndex: number, toIndex: number): void {\n this.requireState('editing');\n this.doc!.transact(() => {\n this.documentManager.moveBlock(this.doc!, fromIndex, toIndex);\n }, LOCAL_ORIGIN);\n }\n\n /**\n * Replace a range of blocks with new ones.\n *\n * Old blocks are removed and new block structures (with empty content)\n * are inserted atomically. Rich-text content is then streamed progressively.\n */\n async replaceBlocks(\n startIndex: number,\n count: number,\n newBlocks: BlockInput[],\n ): Promise<void> {\n this.requireState('editing');\n\n // Prepare all blocks recursively\n const allStreamTargets: StreamTarget[] = [];\n const fullBlocks: Block[] = newBlocks.map((b, i) => {\n const blockIndex = String(startIndex + i);\n const { block, streamTargets } = prepareBlockTree(b, blockIndex);\n allStreamTargets.push(...streamTargets);\n return block;\n });\n\n // Remove old blocks and insert new structures atomically\n this.doc!.transact(() => {\n this.documentManager.removeBlocks(this.doc!, startIndex, count);\n for (let i = 0; i < fullBlocks.length; i++) {\n this.documentManager.insertBlock(\n this.doc!,\n startIndex + i,\n fullBlocks[i],\n );\n }\n }, LOCAL_ORIGIN);\n\n // Stream content for all blocks (depth-first order)\n await this.streamTargets(allStreamTargets);\n }\n\n /**\n * Insert a block as an inner block of an existing block.\n */\n async insertInnerBlock(\n parentIndex: string,\n position: number,\n block: BlockInput,\n ): Promise<void> {\n this.requireState('editing');\n\n const blockIndex = `${parentIndex}.${position}`;\n const { block: fullBlock, streamTargets } = prepareBlockTree(block, blockIndex);\n\n this.doc!.transact(() => {\n this.documentManager.insertInnerBlock(this.doc!, parentIndex, position, fullBlock);\n }, LOCAL_ORIGIN);\n\n await this.streamTargets(streamTargets);\n }\n\n /**\n * Remove inner blocks from an existing block.\n */\n removeInnerBlocks(parentIndex: string, startIndex: number, count: number): void {\n this.requireState('editing');\n this.doc!.transact(() => {\n this.documentManager.removeInnerBlocks(this.doc!, parentIndex, startIndex, count);\n }, LOCAL_ORIGIN);\n }\n\n /**\n * Set the post title.\n *\n * Long titles are streamed progressively; short titles are applied atomically.\n */\n async setTitle(title: string): Promise<void> {\n this.requireState('editing');\n\n if (title.length < STREAM_THRESHOLD) {\n this.doc!.transact(() => {\n this.documentManager.setTitle(this.doc!, title);\n }, LOCAL_ORIGIN);\n return;\n }\n\n // Get the title Y.Text\n const documentMap = this.documentManager.getDocumentMap(this.doc!);\n let ytext = documentMap.get('title');\n if (!(ytext instanceof Y.Text)) {\n // Create Y.Text if it doesn't exist\n this.doc!.transact(() => {\n const newYText = new Y.Text();\n documentMap.set('title', newYText);\n }, LOCAL_ORIGIN);\n ytext = documentMap.get('title');\n }\n if (ytext instanceof Y.Text) {\n await this.streamTextToYText(ytext, title);\n }\n }\n\n /**\n * Trigger a save.\n */\n save(): void {\n this.requireState('editing');\n this.doc!.transact(() => {\n this.documentManager.markSaved(this.doc!);\n }, LOCAL_ORIGIN);\n }\n\n // --- Status ---\n\n getState(): SessionState {\n return this.state;\n }\n\n getSyncStatus(): {\n isPolling: boolean;\n hasCollaborators: boolean;\n queueSize: number;\n } | null {\n if (!this.syncClient) {\n return null;\n }\n const status = this.syncClient.getStatus();\n return {\n isPolling: status.isPolling,\n hasCollaborators: status.hasCollaborators,\n queueSize: status.queueSize,\n };\n }\n\n getCollaborators(): CollaboratorInfo[] {\n return this.collaborators;\n }\n\n getCurrentPost(): WPPost | null {\n return this.currentPost;\n }\n\n getUser(): WPUser | null {\n return this.user;\n }\n\n /**\n * Stream text into a Y.Text in chunks, flushing the sync client between\n * each chunk so the browser sees progressive updates (like fast typing).\n *\n * 1. Compute the delta between the current and target text.\n * 2. Apply retain + delete atomically (old text removed immediately).\n * 3. Split the insert text into HTML-safe chunks (~20 chars each).\n * 4. For each chunk: apply in its own transaction, flush, and delay.\n */\n /**\n * Stream a list of targets (from prepareBlockTree) into their Y.Text instances.\n */\n private async streamTargets(targets: StreamTarget[]): Promise<void> {\n for (const target of targets) {\n let ytext = this.documentManager.getBlockAttributeYText(this.doc!, target.blockIndex, target.attrName);\n if (!ytext) {\n // Y.Text doesn't exist yet — create it atomically before streaming\n this.doc!.transact(() => {\n this.documentManager.updateBlock(this.doc!, target.blockIndex, {\n ...(target.attrName === 'content' ? { content: '' } : { attributes: { [target.attrName]: '' } }),\n });\n }, LOCAL_ORIGIN);\n ytext = this.documentManager.getBlockAttributeYText(this.doc!, target.blockIndex, target.attrName);\n }\n if (ytext) {\n await this.streamTextToYText(ytext, target.value, target.blockIndex);\n }\n }\n }\n\n private async streamTextToYText(ytext: Y.Text, newValue: string, blockIndex?: string): Promise<void> {\n const oldValue = ytext.toString();\n const delta = computeTextDelta(oldValue, newValue);\n if (!delta) return;\n\n // Set cursor to the block being edited\n if (blockIndex !== undefined) {\n this.updateCursorPosition(blockIndex);\n }\n\n // Apply retain + delete atomically (remove old text immediately)\n if (delta.deleteCount > 0) {\n this.doc!.transact(() => {\n const ops: Array<{ retain?: number; delete?: number }> = [];\n if (delta.prefixLen > 0) ops.push({ retain: delta.prefixLen });\n ops.push({ delete: delta.deleteCount });\n ytext.applyDelta(ops);\n }, LOCAL_ORIGIN);\n\n if (this.syncClient) {\n this.syncClient.flushQueue();\n }\n }\n\n // If there's nothing to insert, we're done\n if (delta.insertText.length === 0) return;\n\n // For short inserts, apply atomically (no streaming overhead)\n if (delta.insertText.length < STREAM_THRESHOLD) {\n this.doc!.transact(() => {\n const ops: Array<{ retain?: number; insert?: string }> = [];\n if (delta.prefixLen > 0) ops.push({ retain: delta.prefixLen });\n ops.push({ insert: delta.insertText });\n ytext.applyDelta(ops);\n }, LOCAL_ORIGIN);\n return;\n }\n\n // Stream the insert text in chunks.\n // Use Yjs relative positions to track the insertion point so that\n // concurrent edits (e.g., user typing earlier in the block) don't\n // throw off our position. The relative position is created right after\n // inserting a chunk (anchored to a CRDT item), then resolved AFTER the\n // flush+delay when remote edits may have shifted absolute positions.\n let offset = 0;\n let insertPos = delta.prefixLen;\n let nextInsertRelPos: Y.RelativePosition | null = null;\n\n while (offset < delta.insertText.length) {\n // Early exit: bail if session is no longer active\n if (!this.doc || !this.syncClient) return;\n\n // If we have a relative position from the previous chunk, resolve it\n // now (after the delay, when remote edits may have been applied).\n if (nextInsertRelPos) {\n const absPos = Y.createAbsolutePositionFromRelativePosition(nextInsertRelPos, this.doc);\n if (absPos) {\n insertPos = absPos.index;\n }\n nextInsertRelPos = null;\n }\n\n const chunkSize = STREAM_CHUNK_SIZE_MIN + Math.floor(Math.random() * (STREAM_CHUNK_SIZE_MAX - STREAM_CHUNK_SIZE_MIN + 1));\n const chunkEnd = findHtmlSafeChunkEnd(delta.insertText, offset, chunkSize);\n const chunk = delta.insertText.slice(offset, chunkEnd);\n\n this.doc.transact(() => {\n const ops: Array<{ retain?: number; insert?: string }> = [];\n if (insertPos > 0) ops.push({ retain: insertPos });\n ops.push({ insert: chunk });\n ytext.applyDelta(ops);\n }, LOCAL_ORIGIN);\n\n // Anchor a relative position at the end of what we just inserted.\n // This tracks the CRDT item, not the absolute offset, so it survives\n // concurrent edits that shift positions.\n insertPos += chunk.length;\n nextInsertRelPos = Y.createRelativePositionFromTypeIndex(ytext, insertPos);\n\n offset = chunkEnd;\n\n // Update cursor to the end of the inserted text so far\n this.updateCursorOffset(insertPos);\n\n // Flush and delay between chunks (but not after the last one)\n if (offset < delta.insertText.length) {\n this.syncClient.flushQueue();\n await new Promise((resolve) => setTimeout(resolve, STREAM_CHUNK_DELAY_MS));\n }\n }\n }\n\n /**\n * Update the awareness cursor to point to a block's Y.Text type.\n * References the Y.Text type itself (not items within it) so the\n * cursor always resolves — even after updateYText deletes all items.\n */\n private updateCursorPosition(blockIndex: string): void {\n if (!this.doc || !this.user) return;\n\n const ytext = this.documentManager.getBlockContentYText(this.doc, blockIndex);\n if (!ytext) return;\n\n // Get the Y.Text's internal item ID — this references the Y.Text TYPE,\n // not its content items. The type is never deleted, so this always resolves.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const typeItem = (ytext as any)._item;\n if (!typeItem?.id) return;\n\n const relPosJSON = {\n type: { client: typeItem.id.client, clock: typeItem.id.clock },\n tname: null,\n item: null,\n assoc: 0,\n };\n\n // Preserve enteredAt from existing awareness\n const enteredAt = this.awarenessState?.collaboratorInfo.enteredAt ?? Date.now();\n\n this.awarenessState = {\n collaboratorInfo: {\n id: this.user.id,\n name: `${this.user.name} (Claude)`,\n slug: this.user.slug,\n avatar_urls: this.user.avatar_urls ?? {},\n browserType: 'Claude Code MCP',\n enteredAt,\n },\n editorState: {\n selection: {\n type: 'cursor',\n cursorPosition: {\n relativePosition: relPosJSON,\n absoluteOffset: 0,\n },\n },\n },\n };\n }\n\n /**\n * Update just the cursor offset within the current awareness position.\n * Used during streaming to move the cursor forward as text is typed.\n */\n private updateCursorOffset(offset: number): void {\n if (!this.awarenessState?.editorState?.selection) return;\n const selection = this.awarenessState.editorState.selection;\n if (selection.type === 'cursor') {\n selection.cursorPosition.absoluteOffset = offset;\n }\n }\n\n // --- Internal ---\n\n /**\n * Assert that the session is in one of the allowed states.\n */\n private requireState(...allowed: SessionState[]): void {\n if (!allowed.includes(this.state)) {\n throw new Error(\n `Operation requires state ${allowed.join(' or ')}, but current state is '${this.state}'`,\n );\n }\n }\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SessionManager } from '../session/session-manager.js';\n\nexport function registerConnectTools(server: McpServer, session: SessionManager): void {\n server.tool(\n 'wp_connect',\n 'Connect to a WordPress site for collaborative editing',\n {\n siteUrl: z.string().describe('WordPress site URL (e.g., https://example.com)'),\n username: z.string().describe('WordPress username'),\n appPassword: z.string().describe('WordPress Application Password'),\n },\n async ({ siteUrl, username, appPassword }) => {\n try {\n const user = await session.connect({ siteUrl, username, appPassword });\n return {\n content: [{ type: 'text' as const, text: `Connected to ${siteUrl} as ${user.name} (ID: ${user.id})` }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Connection failed: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_disconnect',\n 'Disconnect from the WordPress site',\n {},\n async () => {\n session.disconnect();\n return {\n content: [{ type: 'text' as const, text: 'Disconnected from WordPress.' }],\n };\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SessionManager } from '../session/session-manager.js';\n\nexport function registerPostTools(server: McpServer, session: SessionManager): void {\n server.tool(\n 'wp_list_posts',\n 'List WordPress posts with optional filters',\n {\n status: z.string().optional().describe('Filter by post status (e.g., publish, draft, pending)'),\n search: z.string().optional().describe('Search posts by keyword'),\n perPage: z.number().optional().describe('Number of posts to return (default 10)'),\n },\n async ({ status, search, perPage }) => {\n try {\n const posts = await session.listPosts({ status, search, perPage });\n if (posts.length === 0) {\n return {\n content: [{ type: 'text' as const, text: 'No posts found.' }],\n };\n }\n\n const lines = posts.map(\n (post, i) =>\n `${i + 1}. [${post.id}] ${post.title.raw ?? post.title.rendered} (${post.status})`,\n );\n\n return {\n content: [{\n type: 'text' as const,\n text: `Found ${posts.length} posts:\\n\\n${lines.join('\\n')}`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to list posts: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_open_post',\n 'Open a WordPress post for collaborative editing',\n {\n postId: z.number().describe('The post ID to open'),\n },\n async ({ postId }) => {\n try {\n await session.openPost(postId);\n const content = session.readPost();\n return {\n content: [{\n type: 'text' as const,\n text: `Opened post ${postId} for editing.\\n\\n${content}`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to open post: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_create_post',\n 'Create a new WordPress post and open it for editing',\n {\n title: z.string().optional().describe('Post title'),\n content: z.string().optional().describe('Initial post content (Gutenberg HTML)'),\n },\n async ({ title, content }) => {\n try {\n const post = await session.createPost({ title, content });\n const rendered = session.readPost();\n return {\n content: [{\n type: 'text' as const,\n text: `Created and opened post ${post.id}.\\n\\n${rendered}`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to create post: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SessionManager } from '../session/session-manager.js';\n\nexport function registerReadTools(server: McpServer, session: SessionManager): void {\n server.tool(\n 'wp_read_post',\n 'Read the current post content as a block listing',\n {},\n async () => {\n try {\n const content = session.readPost();\n return {\n content: [{ type: 'text' as const, text: content }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to read post: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_read_block',\n 'Read a specific block by index (supports dot notation for nested blocks, e.g., \"2.1\")',\n {\n index: z.string().describe('Block index (e.g., \"0\", \"2.1\" for nested blocks)'),\n },\n async ({ index }) => {\n try {\n const content = session.readBlock(index);\n return {\n content: [{ type: 'text' as const, text: content }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to read block: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SessionManager } from '../session/session-manager.js';\n\n/**\n * Recursive block input schema for nested blocks.\n * Uses z.lazy() for self-referential innerBlocks.\n */\ninterface BlockInput {\n name: string;\n content?: string;\n attributes?: Record<string, unknown>;\n innerBlocks?: BlockInput[];\n}\n\nconst blockInputSchema: z.ZodType<BlockInput> = z.object({\n name: z.string().describe('Block type name (e.g., \"core/paragraph\", \"core/list-item\")'),\n content: z.string().optional().describe('Text content for the block'),\n attributes: z.record(z.unknown()).optional().describe('Block attributes (key-value pairs)'),\n innerBlocks: z.lazy(() => z.array(blockInputSchema)).optional()\n .describe('Nested child blocks (e.g., list-items inside a list)'),\n});\n\nexport function registerEditTools(server: McpServer, session: SessionManager): void {\n server.tool(\n 'wp_update_block',\n 'Update a block\\'s content and/or attributes',\n {\n index: z.string().describe('Block index (e.g., \"0\", \"2.1\" for nested blocks)'),\n content: z.string().optional().describe('New text content for the block'),\n attributes: z.record(z.unknown()).optional().describe('Attributes to update (key-value pairs)'),\n },\n async ({ index, content, attributes }) => {\n try {\n await session.updateBlock(index, { content, attributes });\n const updated = session.readBlock(index);\n return {\n content: [{ type: 'text' as const, text: `Updated block ${index}.\\n\\n${updated}` }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to update block: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_insert_block',\n 'Insert a new block at a position in the post. Supports nested blocks via innerBlocks.',\n {\n position: z.number().describe('Position to insert the block (0-based index)'),\n name: z.string().describe('Block type name (e.g., \"core/paragraph\", \"core/heading\", \"core/list\")'),\n content: z.string().optional().describe('Text content for the block'),\n attributes: z.record(z.unknown()).optional().describe('Block attributes (key-value pairs)'),\n innerBlocks: z.array(blockInputSchema).optional()\n .describe('Nested child blocks (e.g., list-items inside a list)'),\n },\n async ({ position, name, content, attributes, innerBlocks }) => {\n try {\n await session.insertBlock(position, { name, content, attributes, innerBlocks });\n return {\n content: [{ type: 'text' as const, text: `Inserted ${name} block at position ${position}.` }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to insert block: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_insert_inner_block',\n 'Insert a block as a child of an existing block (e.g., add a list-item to a list)',\n {\n parentIndex: z.string().describe('Dot-notation index of the parent block (e.g., \"0\", \"2.1\")'),\n position: z.number().describe('Position within the parent\\'s inner blocks (0-based)'),\n name: z.string().describe('Block type name (e.g., \"core/list-item\")'),\n content: z.string().optional().describe('Text content for the block'),\n attributes: z.record(z.unknown()).optional().describe('Block attributes (key-value pairs)'),\n innerBlocks: z.array(blockInputSchema).optional()\n .describe('Nested child blocks'),\n },\n async ({ parentIndex, position, name, content, attributes, innerBlocks }) => {\n try {\n await session.insertInnerBlock(parentIndex, position, { name, content, attributes, innerBlocks });\n return {\n content: [{ type: 'text' as const, text: `Inserted ${name} as inner block at ${parentIndex}.${position}.` }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to insert inner block: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_remove_blocks',\n 'Remove one or more blocks from the post',\n {\n startIndex: z.number().describe('Index of the first block to remove'),\n count: z.number().optional().describe('Number of blocks to remove (default 1)'),\n },\n async ({ startIndex, count }) => {\n try {\n const removeCount = count ?? 1;\n session.removeBlocks(startIndex, removeCount);\n return {\n content: [{\n type: 'text' as const,\n text: `Removed ${removeCount} block${removeCount !== 1 ? 's' : ''} starting at index ${startIndex}.`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to remove blocks: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_remove_inner_blocks',\n 'Remove inner blocks from a parent block',\n {\n parentIndex: z.string().describe('Dot-notation index of the parent block (e.g., \"0\")'),\n startIndex: z.number().describe('Index of the first inner block to remove'),\n count: z.number().optional().describe('Number of inner blocks to remove (default 1)'),\n },\n async ({ parentIndex, startIndex, count }) => {\n try {\n const removeCount = count ?? 1;\n session.removeInnerBlocks(parentIndex, startIndex, removeCount);\n return {\n content: [{\n type: 'text' as const,\n text: `Removed ${removeCount} inner block${removeCount !== 1 ? 's' : ''} from block ${parentIndex}.`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to remove inner blocks: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_move_block',\n 'Move a block from one position to another',\n {\n fromIndex: z.number().describe('Current position of the block'),\n toIndex: z.number().describe('Target position for the block'),\n },\n async ({ fromIndex, toIndex }) => {\n try {\n session.moveBlock(fromIndex, toIndex);\n return {\n content: [{\n type: 'text' as const,\n text: `Moved block from position ${fromIndex} to ${toIndex}.`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to move block: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_replace_blocks',\n 'Replace a range of blocks with new blocks. Supports nested blocks via innerBlocks.',\n {\n startIndex: z.number().describe('Index of the first block to replace'),\n count: z.number().describe('Number of blocks to replace'),\n blocks: z.array(blockInputSchema).describe('New blocks to insert in place of the removed ones'),\n },\n async ({ startIndex, count, blocks }) => {\n try {\n await session.replaceBlocks(startIndex, count, blocks);\n return {\n content: [{\n type: 'text' as const,\n text: `Replaced ${count} block${count !== 1 ? 's' : ''} at index ${startIndex} with ${blocks.length} new block${blocks.length !== 1 ? 's' : ''}.`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to replace blocks: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_set_title',\n 'Set the post title',\n {\n title: z.string().describe('New post title'),\n },\n async ({ title }) => {\n try {\n await session.setTitle(title);\n return {\n content: [{ type: 'text' as const, text: `Title set to \"${title}\".` }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to set title: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { SessionManager } from '../session/session-manager.js';\n\nexport function registerStatusTools(server: McpServer, session: SessionManager): void {\n server.tool(\n 'wp_status',\n 'Show current connection state, sync status, and post info',\n {},\n async () => {\n const state = session.getState();\n const lines: string[] = [];\n\n if (state === 'disconnected') {\n lines.push('Connection: disconnected');\n lines.push('');\n lines.push('Use wp_connect to connect to a WordPress site.');\n } else {\n const user = session.getUser();\n lines.push('Connection: connected');\n lines.push(`User: ${user?.name ?? 'unknown'} (ID: ${user?.id ?? '?'})`);\n\n const post = session.getCurrentPost();\n if (state === 'editing' && post) {\n const syncStatus = session.getSyncStatus();\n const collaboratorCount = session.getCollaborators().length;\n\n lines.push(`Sync: ${syncStatus?.isPolling ? 'polling' : 'stopped'} (${collaboratorCount + 1} collaborator${collaboratorCount + 1 !== 1 ? 's' : ''})`);\n lines.push(`Post: \"${post.title.raw ?? post.title.rendered}\" (ID: ${post.id}, status: ${post.status})`);\n lines.push(`Queue: ${syncStatus?.queueSize ?? 0} pending updates`);\n } else {\n lines.push('Post: none open');\n lines.push('');\n lines.push('Use wp_open_post or wp_create_post to start editing.');\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n },\n );\n\n server.tool(\n 'wp_collaborators',\n 'List active collaborators on the current post',\n {},\n async () => {\n try {\n const state = session.getState();\n if (state !== 'editing') {\n return {\n content: [{ type: 'text' as const, text: 'No post is currently open for editing.' }],\n isError: true,\n };\n }\n\n const collaborators = session.getCollaborators();\n const user = session.getUser();\n\n const lines: string[] = ['Active collaborators:'];\n\n // Add ourselves first\n if (user) {\n lines.push(`- ${user.name} (AI, Claude Code MCP)`);\n }\n\n // Add remote collaborators\n for (const collab of collaborators) {\n lines.push(`- ${collab.name} (Human, ${collab.browserType})`);\n }\n\n if (collaborators.length === 0 && !user) {\n lines.push('- No collaborators detected');\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to get collaborators: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n\n server.tool(\n 'wp_save',\n 'Save the current post',\n {},\n async () => {\n try {\n session.save();\n const post = session.getCurrentPost();\n return {\n content: [{\n type: 'text' as const,\n text: `Post \"${post?.title.raw ?? post?.title.rendered ?? 'Untitled'}\" saved.`,\n }],\n };\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: `Failed to save: ${error instanceof Error ? error.message : String(error)}` }],\n isError: true,\n };\n }\n },\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { SessionManager } from './session/session-manager.js';\nimport { registerConnectTools } from './tools/connect.js';\nimport { registerPostTools } from './tools/posts.js';\nimport { registerReadTools } from './tools/read.js';\nimport { registerEditTools } from './tools/edit.js';\nimport { registerStatusTools } from './tools/status.js';\n\ndeclare const __PKG_VERSION__: string;\n\nexport const VERSION = typeof __PKG_VERSION__ !== 'undefined' ? __PKG_VERSION__ : '0.0.0-dev';\n\nexport async function startServer(): Promise<void> {\n const server = new McpServer({\n name: 'claudaborative-editing',\n version: VERSION,\n });\n\n const session = new SessionManager();\n\n // Auto-connect from env vars if available\n const siteUrl = process.env.WP_SITE_URL;\n const username = process.env.WP_USERNAME;\n const appPassword = process.env.WP_APP_PASSWORD;\n\n if (siteUrl && username && appPassword) {\n try {\n await session.connect({ siteUrl, username, appPassword });\n } catch (e) {\n // Don't fail startup — user can connect via wp_connect tool\n console.error('Auto-connect failed:', e);\n }\n }\n\n // Register all tools\n registerConnectTools(server, session);\n registerPostTools(server, session);\n registerReadTools(server, session);\n registerEditTools(server, session);\n registerStatusTools(server, session);\n\n // Start stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","/**\n * Interactive setup wizard for claudaborative-editing.\n *\n * Prompts for WordPress credentials, validates them, and outputs\n * the `claude mcp add` command the user can copy-paste.\n */\n\nimport { createInterface } from 'readline';\nimport { WordPressApiClient, WordPressApiError } from '../wordpress/api-client.js';\n\nexport interface SetupDeps {\n prompt: (question: string) => Promise<string>;\n log: (message: string) => void;\n error: (message: string) => void;\n exit: (code: number) => never;\n}\n\nfunction defaultDeps(): SetupDeps {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n return {\n prompt: (question: string) =>\n new Promise((resolve) => {\n rl.question(question, (answer) => resolve(answer.trim()));\n }),\n log: (msg) => console.log(msg),\n error: (msg) => console.error(`Error: ${msg}`),\n exit: (code) => {\n rl.close();\n return process.exit(code);\n },\n };\n}\n\nexport async function runSetup(deps: SetupDeps = defaultDeps()): Promise<void> {\n const { prompt, log, error, exit } = deps;\n\n log('');\n log('claudaborative-editing setup');\n log('============================');\n log('');\n log('This wizard will validate your WordPress credentials and give you');\n log('the command to register this MCP server with Claude Code.');\n log('');\n log('Prerequisites:');\n log(' - WordPress 7.0+ with collaborative editing enabled');\n log(' (Settings → Writing in your WordPress admin)');\n log(' - An Application Password for your WordPress user');\n log(' (Users → Your Profile → Application Passwords)');\n log('');\n\n // 1. Collect credentials\n const siteUrl = await prompt('WordPress site URL: ');\n if (!siteUrl) {\n error('Site URL is required.');\n exit(1);\n }\n\n const username = await prompt('WordPress username: ');\n if (!username) {\n error('Username is required.');\n exit(1);\n }\n\n const appPassword = await prompt('Application Password: ');\n if (!appPassword) {\n error('Application Password is required.');\n exit(1);\n }\n\n log('');\n log('Validating credentials...');\n\n // 2. Validate auth\n const client = new WordPressApiClient({\n siteUrl,\n username,\n appPassword,\n });\n\n let displayName: string;\n try {\n const user = await client.validateConnection();\n displayName = user.name ?? username;\n log(` ✓ Authenticated as \"${displayName}\"`);\n } catch (err) {\n if (err instanceof WordPressApiError) {\n error(err.message);\n } else {\n error(`Could not connect to ${siteUrl}. Check the URL and try again.`);\n }\n exit(1);\n }\n\n // 3. Validate sync endpoint\n try {\n await client.validateSyncEndpoint();\n log(' ✓ Collaborative editing endpoint available');\n } catch (err) {\n if (err instanceof WordPressApiError && err.status === 404) {\n log('');\n error(\n 'Collaborative editing is not enabled.\\n' +\n ' Go to Settings → Writing in your WordPress admin and enable it.\\n' +\n ' (Requires WordPress 7.0 or later.)',\n );\n exit(1);\n }\n if (err instanceof WordPressApiError) {\n error(err.message);\n } else {\n error('Could not validate the sync endpoint.');\n }\n exit(1);\n }\n\n log('');\n log('Setup complete! Run this command to register the MCP server:');\n log('');\n\n // Build the env flags\n const envFlags = [\n `-e WP_SITE_URL=${shellQuote(siteUrl)}`,\n `-e WP_USERNAME=${shellQuote(username)}`,\n `-e WP_APP_PASSWORD=${shellQuote(appPassword)}`,\n ].join(' ');\n\n log(` claude mcp add claudaborative-editing ${envFlags} -- npx claudaborative-editing`);\n log('');\n}\n\n/**\n * Quote a value for safe shell use in the output command.\n * Wraps in double quotes if it contains spaces or special characters.\n */\nexport function shellQuote(value: string): string {\n if (/^[a-zA-Z0-9_./:@-]+$/.test(value)) {\n return value;\n }\n // Escape backslashes, double quotes, dollar signs, backticks\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\$/g, '\\\\$')\n .replace(/`/g, '\\\\`');\n return `\"${escaped}\"`;\n}\n","import { VERSION } from './server.js';\n\nconst args = process.argv.slice(2);\n\nif (args.includes('--version') || args.includes('-v')) {\n console.log(VERSION);\n process.exit(0);\n}\n\nif (args.includes('--help') || args.includes('-h')) {\n console.log(`claudaborative-editing v${VERSION}\n\nMCP server for collaborative WordPress post editing via Yjs CRDT.\n\nUsage:\n claudaborative-editing Start the MCP server (stdio transport)\n claudaborative-editing setup Interactive setup wizard\n claudaborative-editing --version Print version\n claudaborative-editing --help Show this help\n\nEnvironment variables:\n WP_SITE_URL WordPress site URL\n WP_USERNAME WordPress username\n WP_APP_PASSWORD WordPress Application Password\n\nMore info: https://github.com/pento/claudaborative-editing`);\n process.exit(0);\n}\n\nif (args[0] === 'setup') {\n const { runSetup } = await import('./cli/setup.js');\n await runSetup();\n} else {\n const { startServer } = await import('./server.js');\n startServer().catch((error) => {\n console.error('Failed to start server:', error);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":["Y","init_types","Y","init_types","Y","init_types","z","z","z","runSetup","startServer"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claudaborative-editing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for collaborative WordPress post editing via Yjs CRDT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"claudaborative-editing": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"license": "GPL-2.0-or-later",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/pento/claudaborative-editing.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"wordpress",
|
|
21
|
+
"gutenberg",
|
|
22
|
+
"collaborative-editing",
|
|
23
|
+
"yjs",
|
|
24
|
+
"claude"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"start": "node dist/index.js",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:watch": "vitest",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
40
|
+
"@wordpress/block-serialization-default-parser": "^5.42.0",
|
|
41
|
+
"lib0": "0.2.99",
|
|
42
|
+
"y-protocols": "^1.0.7",
|
|
43
|
+
"yjs": "13.6.29",
|
|
44
|
+
"zod": "^3.25.76"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^22.19.15",
|
|
48
|
+
"@wordpress/env": "^11.2.0",
|
|
49
|
+
"tsup": "^8.5.1",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vitest": "^4.1.0"
|
|
52
|
+
}
|
|
53
|
+
}
|