@platecms/delta-cast
v1.19.0
Published
Package containing the definition of CAST
Readme
CAST (Content Abstract Syntax Tree)
This package contains the CAST (Content Abstract Syntax Tree) schema and tools for working with it. CAST is a schema that adheres to the Unified collective
Features
Schema
The schema is defined in schema.ts, and is rendered into JSON Schema format in schema.json.
Node types
CAST nodes follow the unist tree model. Each document has a single root. Block nodes make up the document structure.
Inline nodes appear inside block (and some inline) parents.
Root
root
The root of a CAST document. Every document has exactly one root. Children may be any Content (block content, inline
content, or castSuggestion).
{
"type": "root",
"children": [
{
"type": "paragraph",
"children": [{ "type": "text", "value": "Hello world" }]
}
]
}Block content
paragraph
A paragraph of inline content.
children:InlineContent[]
{
"type": "paragraph",
"children": [{ "type": "text", "value": "Hello, world!" }]
}heading
A heading.
level:1 | 2 | 3 | 4 | 5 | 6children:InlineContent[]
{
"type": "heading",
"level": 1,
"children": [{ "type": "text", "value": "Heading" }]
}list
An ordered or unordered list. Children must be listItem nodes only.
ordered: whether the list is orderedstart: optional start number for an ordered listchildren:ListItem[]
{
"type": "list",
"ordered": true,
"start": 1,
"children": [
{
"type": "listItem",
"children": [{ "type": "text", "value": "Item" }]
}
]
}listItem
A single item in a list. Only used as a child of list.
children:InlineContent[]
{
"type": "listItem",
"children": [{ "type": "text", "value": "Item" }]
}blockquote
A block quotation of inline content.
children:InlineContent[]
{
"type": "blockquote",
"children": [{ "type": "text", "value": "Blockquote text" }]
}code
A code block. Children must be text nodes.
lang: optional language of the code blockchildren:Text[]
{
"type": "code",
"lang": "javascript",
"children": [{ "type": "text", "value": "console.log('Hello, world!');" }]
}Inline content
text
A text literal.
value: string
{
"type": "text",
"value": "Hello, world!"
}bold
Bold formatting. Children are inline content.
children:InlineContent[]
{
"type": "bold",
"children": [{ "type": "text", "value": "Bold text" }]
}italic
Italic formatting. Children are inline content.
children:InlineContent[]
{
"type": "italic",
"children": [{ "type": "text", "value": "Italic text" }]
}underline
Underline formatting. Children are inline content.
children:InlineContent[]
{
"type": "underline",
"children": [{ "type": "text", "value": "Underline text" }]
}strikethrough
Strikethrough formatting. Children are inline content.
children:InlineContent[]
{
"type": "strikethrough",
"children": [{ "type": "text", "value": "Strikethrough text" }]
}highlight
Highlight formatting. Children are inline content.
children:InlineContent[]
{
"type": "highlight",
"children": [{ "type": "text", "value": "Highlight text" }]
}inlineCode
An inline code literal.
value: string
{
"type": "inlineCode",
"value": "code"
}contentValue
An interpolated Content Value. The referenced value is stored elsewhere and inserted into the document at request-time.
Nested CAST trees on value are not normalized.
prn: PRN of the referenced Content Valuevalue: optional nested CAST tree (root) of the referenced Content Value.
CAST example
{
"type": "contentValue",
"prn": "prn:plate-dev:1234:cms:test:abcd",
"value": {
"type": "root",
"children": [
{
"type": "paragraph",
"children": [{ "type": "text", "value": "Resolved value" }]
}
]
}
}Create input
{
"type": "contentValue",
"prn": "prn:plate-dev:1234:cms:test:abcd",
}externalLink
A link to an external URL. Children must be text nodes.
url: URL of the linktarget: optional"_parent" | "_top" | "blank" | "self"children:Text[]
{
"type": "externalLink",
"url": "https://www.google.com",
"target": "blank",
"children": [{ "type": "text", "value": "External link" }]
}internalLink
A link to a Path Part or Grid Placement. Children must be text nodes. url is always defined when retrieving an
internal link, but is not required when saving one.
prn: PRN of the referenced Path Part or Grid Placementurl: optional URL (channel and path part, optionally with a grid placement reference such as#anchor-name)target: optional"_parent" | "_top" | "blank" | "self"children:Text[]
CAST example
{
"type": "internalLink",
"prn": "prn:plate-dev:1234:cms:test:abcd",
"url": "https://www.example.test",
"target": "self",
"children": [{ "type": "text", "value": "Internal link" }]
}{
"type": "internalLink",
"prn": "prn:plate-dev:1234:cms:test:abcd#grid-placement",
"url": "https://www.example.test",
"target": "self",
"children": [{ "type": "text", "value": "Internal link" }]
}Create input URL is ommitted here as it generated by the system to keep it up to date.
{
"type": "internalLink",
"prn": "prn:plate-dev:1234:cms:test:abcd",
"target": "self",
"children": [{ "type": "text", "value": "Internal link" }]
}Utilities
Related packages for converting, rendering, and authoring CAST trees:
- cast-util-from-slate — convert Slate editor documents to CAST
- cast-util-to-slate — convert CAST trees to Slate editor documents
- cast-util-to-hast — convert CAST trees to HAST
- cast-util-to-html — convert CAST trees to sanitized HTML (via HAST)
- cast-util-to-plaintext — convert CAST trees to plaintext
- castscript — scriptable helper for building CAST trees
Normalization
A CAST tree can and should be normalized to ensure that it is in a consistent and predictable state. Different trees that represent the same content should be normalized to a single, canonical form.
Usage
import { normalizeCast } from '@platecms/delta-cast';
const normalizedTree = normalizeCast(castTree);Normalization Rules
The normalizeCast function applies the following normalization rules in an iterative process:
1. Adjacent Node Merging
- Text nodes: Merge adjacent
textnodes by concatenating their values - Formatting nodes: Merge adjacent formatting nodes of the same type by combining their children
- Mergeable types:
text,bold,italic,underline,strikethrough,highlight - Non-mergeable types:
contentValue,externalLink,internalLink,inlineCode
2. Formatting Node Nesting Order
Formatting nodes are reordered to follow a consistent hierarchy:
bold -> italic -> underline -> strikethrough -> highlight3. Duplicate Formatting Removal
- Remove duplicate formatting nodes in a single path (e.g.,
boldcontainingbold) - This prevents redundant nesting that doesn't change the visual output
4. Empty Node Cleanup
- Remove empty formatting (inline) nodes that contain no children
- Block-level container nodes (paragraphs, headings, lists, etc.) are preserved even when empty
Special Behavior
- ContentValue nodes: Completely untouched - their nested CAST trees are never normalized
- Property preservation: All node properties (
url,target,prn,level,lang, etc.) are preserved - Iterative process: Normalization is an iterative process that continues until no more changes are detected
- Immutable: Returns a new tree without modifying the input
Formatting Priority
The FORMATTING_PRIORITY constant defines the canonical nesting order for formatting nodes:
export const FORMATTING_PRIORITY = {
'bold': 1, // Highest priority
'italic': 2,
'underline': 3,
'strikethrough': 4,
'highlight': 5 // Lowest priority
} as const;This constant should be used across all packages (e.g., cast-util-to-slate, cast-util-from-slate) to ensure consistent formatting hierarchy throughout the platform.
