npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arrisa/doc

v1.0.29

Published

Arrisa — doc

Readme

@arrisa/doc

Immutable document model, schema, change sets, and HTML I/O for Arrisa.

Foundation package with no dependencies. Everything else in the monorepo builds on this tree: nodes (Leaf / Plot), marks, Schema, ChangeSet, slices/tokens, positions, and HTML parse/serialize.

Install

pnpm add @arrisa/doc

Quick start

import {Schema, Plot, Leaf, Mark, ChangeSet, Slice, parse, serialize} from "@arrisa/doc"

let paragraph = Plot.define("paragraph", {
    inlineContent: true,
    shape: {element: "p"},
})
let bold = Mark.define("bold", {shape: {element: "strong"}})
let docType = Plot.defineDoc({blockContent: paragraph})

let schema = Schema.define([docType, paragraph, bold])

// Build a document (open/close tokens around content → length includes structure)
let doc = schema.doc([paragraph.create([Leaf.text("hello")])])
// "hello" textblock: positions 0 open, 1..5 text, 6 close → length 7

// Mark a range
let marked = ChangeSet.create(doc, {from: 1, to: 6, add: bold}).apply(doc)

// Replace text
let next = ChangeSet.create(marked, {
    from: 2,
    to: 5,
    insert: Slice.of([Leaf.text("i")]),
}).apply(marked)

// HTML (browser DOM for parse)
// let loaded = parse(schema, someElement)
// let vdom = serialize(next)

Features / concepts

| Concept | Role | |---------|------| | Leaf | Terminal node (text or atom). Text length = string length; other leaves length 1. | | Plot | Branch node with ordered children. Length = 2 + contentLength (open + close), except the document root. | | Mark | Annotation on a tag (not a separate tree node). Sets stay sorted by rank. | | Schema | Allowed nodes/marks, content queries, mark targets, JSON/HTML rules. | | Shape / Elt | Virtual DOM templates for serialize and schema shapes. | | ChangeSet | Length-preserving transform: keep ranges (optional mark mods) + replace ranges (slices). | | Slice / Token | Open tag / full node / close end stream for fragments and replacements. | | Pos | Resolved position into a document (parent path, index, optional text offset). |

Positions treat structure as tokens: plots open and close, non-text leaves occupy one unit, text spans by code unit length.

Main public API

Exports from @arrisa/doc match src/index.ts:

import {
    Node,
    Leaf,
    Plot,
    Mark,
    type Shape,
    Elt,
    Attributes,
    NodeShape,
    isSafeAttributeName,
    Schema,
    SchemaError,
    ValidationError,
    ChangeSet,
    Slice,
    Token,
    Pos,
    parse,
    serialize,
} from "@arrisa/doc"

Document model

| Export | Description | |--------|-------------| | Node | Union type Plot \| Leaf, plus namespace: Node.Group, Node.Role, Node.Query, JSON/spec helpers. | | Leaf | Terminal node. Leaf.define(name, spec), Leaf.text(str, marks?), built-in Leaf.Text. | | Plot | Branch node. Plot.define(name, spec), Plot.defineDoc({…}), Plot.Doc / document helpers, tag.create(children). | | Mark | Mark instance + Mark.define / Mark.Type.define, set helpers (addToSet, removeFromSet, Mark.none). | | Pos | Pos.resolve(doc, pos), path navigation, advance, nodeAfter / nodeBefore. | | Slice | Fragment of tokens. Slice.of(tokens), Slice.empty, fromJSON, slice/concat/eq. | | Token | Stream unit: full Node, open Plot.Tag, or Token.End (close). |

Schema

| API | Description | |-----|-------------| | Schema.define(elements) | Build (or reuse cached) schema. Requires exactly one document type. | | schema.doc(children) | Create a document under this schema. | | schema.validate(node) | Deep structural + mark-target check (memoized). | | schema.canContain / markAllowed / matchNode | Content and mark queries. | | schema.nodeFromJSON / docFromJSON / tagFromJSON | Deserialize with validation. | | Schema.Override | markTarget, plotContent, nodeGroup overrides. | | SchemaError / ValidationError | Schema definition vs document validation failures. |

Element specs use shape (Shape.Element or Shape.Structure) and optional parseRules, groups, roles, etc.

Shapes / virtual DOM

| Export | Description | |--------|-------------| | Shape | Type-only namespace: Element, Structure, Attribute, Attributes specs. | | NodeShape.from | Resolve a node shape into create(param) → Elt. | | Elt | Immutable virtual element: Elt.mk, Elt.create, fill/holes, toDOM / HTML helpers on the class. | | Attributes | Sorted attribute bag for Elt. | | isSafeAttributeName | Rejects dangerous attribute names (e.g. on*) on serialize/attr paths. |

Changes

| API | Description | |-----|-------------| | ChangeSet.create(doc, spec) | Build from replace / mark / compose / correct specs. | | change.apply(doc) | Produce the new document. | | change.invert(doc) | Inverse for undo-style application. | | change.compose(other) | Sequential composition. | | ChangeSet.transform(doc, a, b) | OT transform pair. | | change.mapPos(pos, assoc?) | Map positions through the change. | | ChangeSet.empty(length) | Identity keep of length. | | change.toJSON / ChangeSet.fromJSON | Wire/persistence form. |

Typical change object:

// Replace or insert
{from, to, insert: Slice, fit?: boolean}

// Marks (not combined with insert on the same object)
{from, to, add: Mark}
{from, to, remove: Mark}

// Nested
[changeA, changeB]
{correct: innerSpec, local?: boolean}

HTML I/O

| API | Description | |-----|-------------| | parse(schema, elementOrFragment, options?) | DOM → Plot.Doc (browser). | | parse.slice(schema, …) | DOM → open slice + context. | | serialize(doc, options?) | Doc → Elt.Fragment. | | serialize.node / serialize.slice | Single node or slice serialization. |

parse requires a real DOM (Element / DocumentFragment). Schema rules and ignore lists shape the model; they are not an XSS sanitizer.

Layering / related packages

@arrisa/doc          ← you are here (layer 0)
  ├─ @arrisa/state
  └─ @arrisa/types

| Package | Relation | |---------|----------| | @arrisa/state | EditorState, selection, transactions over docs. | | @arrisa/types | Standard nodes/marks (Paragraph, Strong, Link, …). | | @arrisa/editor | View layer (depends on state + command stack). |

Security

  • Schema validation is not an XSS boundary.
  • Untrusted HTML must be sanitized before DOM assignment / parse (see app hooks in @arrisa/state / @arrisa/editor).
  • isSafeAttributeName helps on attribute names only; values still need app policy where relevant.

Full guidance: repository root SECURITY.md.

License

MIT