Coelho.Document (coelho v0.1.0)

Copy Markdown View Source

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

to_text(document, schema)

@spec to_text(map(), Coelho.Schema.t()) :: String.t()

Extracts the plain text of a document, for full text search.

validate(document, schema)

@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.