OverviewFilesCommitsIssuesDocsReleasesPeers

favela-markdown / spec / vectors / markdown-strip.json

411 lines · json ref:
{
  "$comment": "Vectors for spec/markdown-strip.md. Offsets are UTF-8 BYTE offsets into `text`, half-open [byteStart, byteEnd). An implementation is conformant when it reproduces every entry byte-for-byte.",
  "spec": "spec/markdown-strip.md",
  "vectors": [
    {
      "name": "emphasis-stripped",
      "why": "§3.1 markers go, text stays, no facet — atproto has no bold",
      "source": "hello **world** and *more*",
      "text": "hello world and more",
      "facets": []
    },
    {
      "name": "code-span",
      "why": "§3.1 a code span is text, not markup",
      "source": "use `npm test` now",
      "text": "use npm test now",
      "facets": []
    },
    {
      "name": "link",
      "why": "§3.2 label becomes text; the uri rides in the facet",
      "source": "click [here](https://example.com)",
      "text": "click here",
      "facets": [
        {
          "byteStart": 6,
          "byteEnd": 10,
          "type": "link",
          "value": "https://example.com"
        }
      ]
    },
    {
      "name": "link-with-emphasis-label",
      "why": "§3.2 the label is stripped FIRST, then the facet spans the result",
      "source": "click [**bold**](https://example.com)",
      "text": "click bold",
      "facets": [
        {
          "byteStart": 6,
          "byteEnd": 10,
          "type": "link",
          "value": "https://example.com"
        }
      ]
    },
    {
      "name": "link-empty-label",
      "why": "§3.2 an empty label emits the url as text",
      "source": "[](https://example.com)",
      "text": "https://example.com",
      "facets": [
        {
          "byteStart": 0,
          "byteEnd": 19,
          "type": "link",
          "value": "https://example.com"
        }
      ]
    },
    {
      "name": "link-non-http-refused",
      "why": "§3.2 ⛔ javascript: emits the label as PLAIN TEXT, no facet",
      "source": "[click](javascript:alert(1))",
      "text": "click",
      "facets": []
    },
    {
      "name": "bare-url-trailing-punctuation",
      "why": "§3.3 the normative URL_RE stops before trailing punctuation — the full stop stays text",
      "source": "see https://x.com.",
      "text": "see https://x.com.",
      "facets": [
        {
          "byteStart": 4,
          "byteEnd": 17,
          "type": "link",
          "value": "https://x.com"
        }
      ]
    },
    {
      "name": "mention-resolved",
      "why": "§3.4 a mention MUST carry a resolved did in the target",
      "source": "hi [@alice](did:plc:xyz)",
      "text": "hi @alice",
      "facets": [
        {
          "byteStart": 3,
          "byteEnd": 9,
          "type": "mention",
          "value": "did:plc:xyz"
        }
      ]
    },
    {
      "name": "mention-bare-is-text",
      "why": "§3.4 ⛔ a bare @alice is plain text — never a guessed did",
      "source": "hi @alice",
      "text": "hi @alice",
      "facets": []
    },
    {
      "name": "image-produces-nothing",
      "why": "§3.5 images are embed, not body text",
      "source": "![a cat](https://x/c.png)",
      "text": "",
      "facets": []
    },
    {
      "name": "image-only-strips-empty",
      "why": "§3.5 a post that is only an image has empty text — valid",
      "source": "![](https://x/c.png)",
      "text": "",
      "facets": []
    },
    {
      "name": "wikilink",
      "why": "§3.6 degrades to its label, no facet — no atproto/AS2 equivalent",
      "source": "see [[Design Note]]",
      "text": "see Design Note",
      "facets": []
    },
    {
      "name": "wikilink-labelled",
      "why": "§3.6 the pipe form uses the label",
      "source": "see [[Design Note|the note]]",
      "text": "see the note",
      "facets": []
    },
    {
      "name": "hashtag",
      "why": "§3.7 the # stays in TEXT, the facet value drops it",
      "source": "a #p2p post",
      "text": "a #p2p post",
      "facets": [
        {
          "byteStart": 2,
          "byteEnd": 6,
          "type": "tag",
          "value": "p2p"
        }
      ]
    },
    {
      "name": "hashtag-midword-is-text",
      "why": "§3.7 a # inside a word is literal",
      "source": "c#sharp",
      "text": "c#sharp",
      "facets": []
    },
    {
      "name": "strikethrough",
      "why": "§3.1 extension: markers go",
      "source": "~~gone~~ here",
      "text": "gone here",
      "facets": []
    },
    {
      "name": "footnote-dropped",
      "why": "§3.9 refs and definitions have no projection target",
      "source": "claim[^1]\n\n[^1]: the source",
      "text": "claim",
      "facets": []
    },
    {
      "name": "hard-break",
      "why": "§3.8 backslash break becomes one newline",
      "source": "one\\\ntwo",
      "text": "one\ntwo",
      "facets": []
    },
    {
      "name": "soft-break-is-space",
      "why": "§3.8 a soft wrap is a space, not a newline",
      "source": "one\ntwo",
      "text": "one two",
      "facets": []
    },
    {
      "name": "multibyte-before-facet",
      "why": "★ é is 2 bytes — a character-offset implementation puts this facet one byte early",
      "source": "café https://x.com",
      "text": "café https://x.com",
      "facets": [
        {
          "byteStart": 6,
          "byteEnd": 19,
          "type": "link",
          "value": "https://x.com"
        }
      ]
    },
    {
      "name": "emoji-before-facet",
      "why": "★ each emoji is 4 bytes — the classic off-by-six for a char-offset implementation",
      "source": "🎉🎉 tap [here](https://example.com)",
      "text": "🎉🎉 tap here",
      "facets": [
        {
          "byteStart": 13,
          "byteEnd": 17,
          "type": "link",
          "value": "https://example.com"
        }
      ]
    },
    {
      "name": "multibyte-inside-mention",
      "why": "★ the facet itself spans multi-byte characters",
      "source": "héllo [@wörld](did:plc:abc)",
      "text": "héllo @wörld",
      "facets": [
        {
          "byteStart": 7,
          "byteEnd": 14,
          "type": "mention",
          "value": "did:plc:abc"
        }
      ]
    },
    {
      "name": "heading",
      "why": "§4.2 marker removed, block like any other",
      "source": "## Title\n\nbody",
      "text": "Title\n\nbody",
      "facets": []
    },
    {
      "name": "code-fence-verbatim",
      "why": "§4.3 contents verbatim, no fence, no re-indent",
      "source": "```js\nconst a = 1\n```",
      "text": "const a = 1",
      "facets": []
    },
    {
      "name": "blockquote-no-prefix",
      "why": "§4.4 no '> ' is invented — that would be a convention to re-specify",
      "source": "> quoted\n> more",
      "text": "quoted more",
      "facets": []
    },
    {
      "name": "bullet-list",
      "why": "§4.5 a marker IS kept, or a list becomes indistinguishable from paragraphs",
      "source": "- one\n- two",
      "text": "• one\n• two",
      "facets": []
    },
    {
      "name": "ordered-list-renumbered",
      "why": "§4.5 the RENDERED sequence, not the literal source numbers",
      "source": "1. a\n1. b\n1. c",
      "text": "1. a\n2. b\n3. c",
      "facets": []
    },
    {
      "name": "table",
      "why": "§4.6 rows on lines, cells joined with ' | '; lossy and deliberately unclever",
      "source": "| a | b |\n|---|---|\n| 1 | 2 |",
      "text": "a | b\n1 | 2",
      "facets": []
    },
    {
      "name": "thematic-break-omitted",
      "why": "§4.7 omitted entirely, including its separator",
      "source": "one\n\n---\n\ntwo",
      "text": "one\n\ntwo",
      "facets": []
    },
    {
      "name": "directive-contents-kept",
      "why": "§4.8 a spoiler projects as its inner text — dropping it would misrepresent the post elsewhere",
      "source": ":::spoiler\nthe ending\n:::",
      "text": "the ending",
      "facets": []
    },
    {
      "name": "crlf-normalised",
      "why": "§5.1 CRLF becomes LF before parsing",
      "source": "one\r\n\r\ntwo",
      "text": "one\n\ntwo",
      "facets": []
    },
    {
      "name": "blank-lines-collapsed",
      "why": "§5.3 three or more newlines collapse to two",
      "source": "one\n\n\n\ntwo",
      "text": "one\n\ntwo",
      "facets": []
    },
    {
      "name": "outer-whitespace-trimmed",
      "why": "§5.4 leading and trailing whitespace is removed from the whole text",
      "source": "\n\n  hello  \n\n",
      "text": "hello",
      "facets": []
    },
    {
      "name": "empty-source",
      "why": "§2 total function — empty in, empty out, no throw",
      "source": "",
      "text": "",
      "facets": []
    },
    {
      "name": "whitespace-only-source",
      "why": "§2 total function — a whitespace-only source degrades to empty, never throws",
      "source": "   \n\n  ",
      "text": "",
      "facets": []
    },
    {
      "name": "facets-sorted-by-bytestart",
      "why": "§6.1 sorted ascending — required for byte-determinism",
      "source": "[a](https://one.example) and [b](https://two.example)",
      "text": "a and b",
      "facets": [
        {
          "byteStart": 0,
          "byteEnd": 1,
          "type": "link",
          "value": "https://one.example"
        },
        {
          "byteStart": 6,
          "byteEnd": 7,
          "type": "link",
          "value": "https://two.example"
        }
      ]
    },
    {
      "name": "unknown-syntax-is-literal",
      "why": "§2.1 the dialect is closed, so syntax outside it is TEXT, never markup (hyper-social #43)",
      "source": "==highlight== and $$x$$ and {: .cls}",
      "text": "==highlight== and $$x$$ and {: .cls}",
      "facets": []
    },
    {
      "name": "single-tilde-is-literal",
      "why": "§2.1 strikethrough is pinned as ~~x~~; a SINGLE tilde is outside the dialect and stays text",
      "source": "~sub~ not strikethrough",
      "text": "~sub~ not strikethrough",
      "facets": []
    },
    {
      "name": "unknown-directive-keeps-contents",
      "why": "§4.8 the ONE exception — an unknown directive NAME still yields its contents, not literal text",
      "source": ":::unknownName\nbody\n:::",
      "text": "body",
      "facets": []
    },
    {
      "name": "mention-favela-target",
      "why": "§3.4 a mention names the USER by identity root; the DID is resolved at PROJECTION time, not stored (identity.md §5)",
      "source": "hi [@alice](favela://96b89f87)",
      "text": "hi @alice",
      "facets": [
        {
          "byteStart": 3,
          "byteEnd": 9,
          "type": "mention",
          "value": "favela://96b89f87"
        }
      ]
    },
    {
      "name": "mention-did-target-legacy",
      "why": "§8 forward-only — a legacy record carrying a did: target still resolves; nothing is rewritten",
      "source": "hi [@alice](did:plc:xyz)",
      "text": "hi @alice",
      "facets": [
        {
          "byteStart": 3,
          "byteEnd": 9,
          "type": "mention",
          "value": "did:plc:xyz"
        }
      ]
    },
    {
      "name": "facet-at-end-of-block",
      "why": "§6.4 offsets map through §5 normalisation — a facet ending at a BLOCK BOUNDARY must survive it",
      "source": "a #p2p\n\nmore",
      "text": "a #p2p\n\nmore",
      "facets": [
        {
          "byteStart": 2,
          "byteEnd": 6,
          "type": "tag",
          "value": "p2p"
        }
      ]
    },
    {
      "name": "link-at-end-of-block",
      "why": "§6.4 the same for a link facet — the normalisation index map must cover dropped characters",
      "source": "see [here](https://x.com)\n\nnext",
      "text": "see here\n\nnext",
      "facets": [
        {
          "byteStart": 4,
          "byteEnd": 8,
          "type": "link",
          "value": "https://x.com"
        }
      ]
    }
  ]
}