CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yjs

Conflict-free Replicated Data Type (CRDT) framework for real-time collaborative applications

Overview
Eval results
Files

xml-types.mddocs/

XML Types

XML-aware collaborative types for structured document editing with DOM integration. These types enable collaborative editing of XML/HTML documents while maintaining proper document structure and supporting real-time synchronization.

Capabilities

YXmlFragment

Container type for XML elements and text nodes, providing the foundation for structured document editing.

/**
 * XML container that can hold XML elements and text nodes
 */
class YXmlFragment {
  constructor();
  
  /** Number of child elements in the fragment */
  readonly length: number;
  
  /** First child element or null if empty */
  readonly firstChild: YXmlElement | YXmlText | null;
}

Fragment Modification Methods:

/**
 * Insert child nodes at the specified index
 * @param index - Position to insert at
 * @param content - Array of XML elements or text nodes to insert
 */
insert(index: number, content: Array<YXmlElement | YXmlText>): void;

/**
 * Insert child nodes after a reference node
 * @param ref - Reference node to insert after (null for beginning)
 * @param content - Array of XML elements or text nodes to insert
 */
insertAfter(ref: null | Item | YXmlElement | YXmlText, content: Array<YXmlElement | YXmlText>): void;

/**
 * Delete child nodes starting at index
 * @param index - Starting position for deletion
 * @param length - Number of children to delete (default: 1)
 */
delete(index: number, length?: number): void;

/**
 * Append child nodes to the end
 * @param content - Array of XML elements or text nodes to append
 */
push(content: Array<YXmlElement | YXmlText>): void;

/**
 * Prepend child nodes to the beginning
 * @param content - Array of XML elements or text nodes to prepend
 */
unshift(content: Array<YXmlElement | YXmlText>): void;

Fragment Access Methods:

/**
 * Get child node at the specified index
 * @param index - Index of child to retrieve
 * @returns Child node or undefined
 */
get(index: number): YXmlElement | YXmlText;

/**
 * Get slice of children as JavaScript Array
 * @param start - Start index (default: 0)
 * @param end - End index (default: length)
 * @returns Array of child nodes
 */
slice(start?: number, end?: number): Array<YXmlElement | YXmlText>;

/**
 * Convert all children to JavaScript Array
 * @returns Array of all child nodes
 */
toArray(): Array<YXmlElement | YXmlText | YXmlHook>;

Fragment Iteration and Selection:

/**
 * Execute function for each child node
 * @param f - Function to execute for each child
 */
forEach(f: (item: YXmlElement | YXmlText, index: number, fragment: YXmlFragment) => void): void;

/**
 * Create a tree walker for traversing the fragment
 * @param filter - Optional filter function for nodes
 * @returns Tree walker instance
 */
createTreeWalker(filter?: (node: AbstractType<any>) => boolean): YXmlTreeWalker;

/**
 * Find the first element matching a CSS selector
 * @param query - CSS selector string
 * @returns First matching element or null
 */
querySelector(query: string): YXmlElement | YXmlText | YXmlHook | null;

/**
 * Find all elements matching a CSS selector
 * @param query - CSS selector string
 * @returns Array of matching elements
 */
querySelectorAll(query: string): Array<YXmlElement | YXmlText | YXmlHook | null>;

Fragment Serialization:

/**
 * Convert fragment to XML string
 * @returns XML string representation
 */
toString(): string;

/**
 * Convert to JSON representation
 * @returns JSON string
 */
toJSON(): string;

/**
 * Convert to DOM DocumentFragment
 * @param document - Document to create nodes in (default: globalThis.document)
 * @param hooks - Custom hooks for special elements
 * @param binding - Optional binding object
 * @returns DOM DocumentFragment
 */
toDOM(document?: Document, hooks?: { [key: string]: any }, binding?: any): DocumentFragment;

/**
 * Create a copy of the fragment
 * @returns New YXmlFragment with same content
 */
clone(): YXmlFragment;

Usage Examples:

import * as Y from "yjs";

const doc = new Y.Doc();
const xmlFragment = doc.getXmlFragment("document");

// Create XML elements
const header = new Y.XmlElement("h1");
const paragraph = new Y.XmlElement("p");
const textNode = new Y.XmlText();

textNode.insert(0, "Hello World!");
header.insert(0, [textNode]);

// Insert into fragment
xmlFragment.push([header, paragraph]);

// Query elements
const h1 = xmlFragment.querySelector("h1");
const allPs = xmlFragment.querySelectorAll("p");

// Convert to DOM
const domFragment = xmlFragment.toDOM();
document.body.appendChild(domFragment);

YXmlElement

XML element with attributes that can contain child elements and text nodes.

/**
 * XML element with attributes and children
 */
class YXmlElement<KV = { [key: string]: any }> extends YXmlFragment {
  constructor(nodeName?: string);
  
  /** Tag name of the XML element */
  readonly nodeName: string;
  
  /** Next sibling element or null */
  readonly nextSibling: YXmlElement | YXmlText | null;
  
  /** Previous sibling element or null */
  readonly prevSibling: YXmlElement | YXmlText | null;
}

Element Attribute Methods:

/**
 * Set an attribute on the element
 * @param attributeName - Name of the attribute
 * @param attributeValue - Value to set
 */
setAttribute<KEY extends keyof KV>(attributeName: KEY, attributeValue: KV[KEY]): void;

/**
 * Get an attribute from the element
 * @param attributeName - Name of the attribute
 * @returns Attribute value or undefined
 */
getAttribute<KEY extends keyof KV>(attributeName: KEY): KV[KEY] | undefined;

/**
 * Check if element has an attribute
 * @param attributeName - Name of the attribute to check
 * @returns True if attribute exists
 */
hasAttribute(attributeName: string): boolean;

/**
 * Remove an attribute from the element
 * @param attributeName - Name of the attribute to remove
 */
removeAttribute(attributeName: string): void;

/**
 * Get all attributes from the element
 * @param snapshot - Optional snapshot for historical state
 * @returns Object with all attributes
 */
getAttributes(snapshot?: Snapshot): { [key: string]: any };

Element Serialization:

/**
 * Convert element to XML string with attributes
 * @returns XML string representation
 */
toString(): string;

/**
 * Convert to DOM Element
 * @param document - Document to create element in (default: globalThis.document)
 * @param hooks - Custom hooks for special elements
 * @param binding - Optional binding object
 * @returns DOM Element
 */
toDOM(document?: Document, hooks?: { [key: string]: any }, binding?: any): Element;

/**
 * Create a copy of the element
 * @returns New YXmlElement with same content and attributes
 */
clone(): YXmlElement<KV>;

Usage Examples:

import * as Y from "yjs";

const doc = new Y.Doc();
const xmlFragment = doc.getXmlFragment("document");

// Create an element with attributes
const div = new Y.XmlElement("div");
div.setAttribute("class", "container");
div.setAttribute("id", "main-content");

// Add text content
const text = new Y.XmlText();
text.insert(0, "Hello World!");
div.insert(0, [text]);

// Create nested structure
const article = new Y.XmlElement("article");
const title = new Y.XmlElement("h2");
const titleText = new Y.XmlText();
titleText.insert(0, "Article Title");
title.insert(0, [titleText]);

article.push([title, div]);
xmlFragment.push([article]);

// Work with attributes
console.log(div.getAttribute("class")); // "container"
div.removeAttribute("id");

// Convert to DOM
const domElement = article.toDOM();

YXmlText

XML text node with formatting support for rich text within XML structures.

/**
 * XML text node with rich formatting capabilities
 */
class YXmlText extends YText {
  constructor();
  
  /** Next sibling element or null */
  readonly nextSibling: YXmlElement | YXmlText | null;
  
  /** Previous sibling element or null */
  readonly prevSibling: YXmlElement | YXmlText | null;
}

XML Text Methods:

/**
 * Convert to formatted XML string
 * @returns XML string with formatting
 */
toString(): string;

/**
 * Convert to JSON representation
 * @returns JSON string
 */
toJSON(): string;

/**
 * Convert to DOM Text node
 * @param document - Document to create text node in (default: globalThis.document)
 * @param hooks - Custom hooks for special formatting
 * @param binding - Optional binding object
 * @returns DOM Text node
 */
toDOM(document?: Document, hooks?: { [key: string]: any }, binding?: any): Text;

/**
 * Create a copy of the text node
 * @returns New YXmlText with same content
 */
clone(): YXmlText;

Usage Examples:

import * as Y from "yjs";

const doc = new Y.Doc();
const xmlFragment = doc.getXmlFragment("document");

// Create formatted text
const xmlText = new Y.XmlText();
xmlText.insert(0, "Hello ");
xmlText.insert(6, "World", { bold: true });
xmlText.insert(11, "!");

// Add to element
const paragraph = new Y.XmlElement("p");
paragraph.insert(0, [xmlText]);
xmlFragment.push([paragraph]);

// Convert to DOM
const domText = xmlText.toDOM();

YXmlHook

Custom XML element hook for extending XML functionality with custom components.

/**
 * Custom XML hook for extending functionality
 */
class YXmlHook extends YMap {
  constructor(hookName: string);
  
  /** Name of the hook */
  readonly hookName: string;
}

Hook Methods:

/**
 * Convert hook to DOM element using custom rendering
 * @param document - Document to create element in (default: globalThis.document)
 * @param hooks - Hook implementation functions
 * @param binding - Optional binding object
 * @returns DOM Element
 */
toDOM(document?: Document, hooks?: { [key: string]: any }, binding?: any): Element;

/**
 * Create a copy of the hook
 * @returns New YXmlHook with same hook name and data
 */
clone(): YXmlHook;

Usage Examples:

import * as Y from "yjs";

const doc = new Y.Doc();
const xmlFragment = doc.getXmlFragment("document");

// Create a custom hook
const customComponent = new Y.XmlHook("custom-widget");
customComponent.set("type", "chart");
customComponent.set("data", [1, 2, 3, 4, 5]);

xmlFragment.push([customComponent]);

// Convert to DOM with custom hook handler
const domFragment = xmlFragment.toDOM(document, {
  "custom-widget": (hook) => {
    const element = document.createElement("div");
    element.className = "custom-widget";
    element.textContent = `Chart with ${hook.get("data").length} points`;
    return element;
  }
});

YXmlTreeWalker

Iterator for traversing XML tree structures with optional filtering.

/**
 * Tree walker for traversing XML structures
 */
class YXmlTreeWalker {
  constructor(root: YXmlFragment | YXmlElement, filter?: (node: AbstractType<any>) => boolean);
  
  /**
   * Get next node in traversal
   * @returns Iterator result with next node
   */
  next(): IteratorResult<YXmlElement | YXmlText | YXmlHook>;
  
  /**
   * Iterator support for for...of loops
   */
  [Symbol.iterator](): YXmlTreeWalker;
}

Usage Examples:

import * as Y from "yjs";

const doc = new Y.Doc();
const xmlFragment = doc.getXmlFragment("document");

// Create tree structure
const root = new Y.XmlElement("div");
const child1 = new Y.XmlElement("p");
const child2 = new Y.XmlElement("span");
root.push([child1, child2]);
xmlFragment.push([root]);

// Walk the tree
const walker = xmlFragment.createTreeWalker();
for (const node of walker) {
  if (node instanceof Y.XmlElement) {
    console.log(`Element: ${node.nodeName}`);
  } else if (node instanceof Y.XmlText) {
    console.log(`Text: ${node.toString()}`);
  }
}

// Walk with filter
const elementWalker = xmlFragment.createTreeWalker(
  node => node instanceof Y.XmlElement
);
for (const element of elementWalker) {
  console.log(`Element: ${element.nodeName}`);
}

Install with Tessl CLI

npx tessl i tessl/npm-yjs

docs

document-management.md

event-system.md

index.md

position-tracking.md

shared-data-types.md

snapshot-system.md

synchronization.md

transaction-system.md

undo-redo-system.md

xml-types.md

tile.json