Validation, normalisation and plain text extraction of documents.
A document is a plain map tree with string keys, exactly as it comes out
of Jason.decode/1 or out of ProseMirror's toJSON():
%{
"type" => "doc",
"content" => [
%{
"type" => "paragraph",
"content" => [
%{"type" => "text", "text" => "hello", "marks" => [%{"type" => "bold"}]}
]
}
]
}No struct wraps it, so what is validated is what is stored, and a jsonb round trip is the identity.
Validation is the sanitisation
validate/2 is strict on purpose: an unknown node type, an unknown mark,
an unknown attribute or an attribute failing its validator all reject the
document. Nothing outside the schema reaches the database, so rendering
never has to escape its way out of untrusted markup. This is what storing
the document buys over storing HTML and filtering tags on the way in.
Validation also normalises: missing optional attributes are filled with their schema default, so stored documents are canonical.
Untrusted input
validate/2 is the boundary a hostile document hits first, so it is
written to survive one: node type names are resolved against the schema
rather than converted to atoms, nesting deeper than 100 levels is rejected
outright, and error paths are accumulated in reverse so that validating a
deep document stays linear in its size.
Summary
Functions
Extracts the plain text of a document, for full text search.
Validates and normalises a document against a schema.
Functions
@spec to_text(map(), Coelho.Schema.t()) :: String.t()
Extracts the plain text of a document, for full text search.
@spec validate(term(), Coelho.Schema.t()) :: {:ok, map()} | {:error, [Coelho.Document.Error.t()]}
Validates and normalises a document against a schema.
Returns the normalised document, or every error found. Paths in the
errors read from the root, as in content[0].attrs.href.