Emitter 2d: the opaque carry in both positions and the reserved info string #15
@@ -27,11 +27,11 @@ Round-trip equality is a property tested over a corpus, not a claim made in pros
|
|||||||
## 3. Unknown input policy
|
## 3. Unknown input policy
|
||||||
|
|
||||||
- Unknown ADF node: carried opaquely — raw JSON rides a dedicated syntax in both formats and
|
- Unknown ADF node: carried opaquely — raw JSON rides a dedicated syntax in both formats and
|
||||||
restores to a deep-equal node. The round-trip holds for documents newer than the library. A
|
restores to a deep-equal node. The round-trip holds for documents newer than the library. So
|
||||||
known node the position leaves unspelled carries the same way — validity against ADF's content
|
does a known node no section spells where it stands, `hardBreak`, `listItem` and `text` where a
|
||||||
models is not the library's business (§14) — except `hardBreak`, `listItem` and `text` where a
|
block belongs excepted. Where a container's own spelling cannot hold the child it has — a
|
||||||
block belongs: their spelling lives inside another node's body, so the misplacement is an error
|
`bulletList` outside `listItem`, a `codeBlock` outside text — the error result names that
|
||||||
result naming it.
|
instead.
|
||||||
- Unmappable foreign HTML element: error result naming the element — never a silent drop.
|
- Unmappable foreign HTML element: error result naming the element — never a silent drop.
|
||||||
- Bare `@name` / `:smile:` in typed text: stays a text node. Only directives produce
|
- Bare `@name` / `:smile:` in typed text: stays a text node. Only directives produce
|
||||||
mention/emoji/media nodes; resolving names to ids needs I/O, which is the consumer's job.
|
mention/emoji/media nodes; resolving names to ids needs I/O, which is the consumer's job.
|
||||||
@@ -121,8 +121,8 @@ live Atlassian APIs; property-generated ADF trees; the CommonMark spec suite aga
|
|||||||
returns `string | undefined`, never a `Result`: any failure is the fallback signal, and the
|
returns `string | undefined`, never a `Result`: any failure is the fallback signal, and the
|
||||||
general form owns the refusal. Refusing there refuses a document the general form spells.
|
general form owns the refusal. Refusing there refuses a document the general form spells.
|
||||||
- Nothing recurses unbounded: the guards walk iteratively, and blocks, marks and JSON values — an
|
- Nothing recurses unbounded: the guards walk iteratively, and blocks, marks and JSON values — an
|
||||||
attribute's and a carried node's alike — are all held to 500 levels, so a deep document is a `Result` rather than the stack overflow that
|
attribute's and a carried node's alike — are all held to 500 levels, so a deep document is a
|
||||||
waits near 2000.
|
`Result` rather than the stack overflow that waits near 2000.
|
||||||
- No casts: `as`, `as unknown as`, non-null `!`. A boundary owes a type guard validating the
|
- No casts: `as`, `as unknown as`, non-null `!`. A boundary owes a type guard validating the
|
||||||
fields it claims (`isAdfDocument`); past it everything is typed. Make invalid states
|
fields it claims (`isAdfDocument`); past it everything is typed. Make invalid states
|
||||||
unrepresentable.
|
unrepresentable.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Lossless conversion between **Atlassian Document Format** (ADF), an extended markdown flavour, and
|
Lossless conversion between **Atlassian Document Format** (ADF), an extended markdown flavour, and
|
||||||
an HTML dialect.
|
an HTML dialect.
|
||||||
|
|
||||||
**Status: pre-release — `adfToMarkdown` is built, nothing else is.**
|
**Status: pre-release — `adfToMarkdown` only, and not yet every document.**
|
||||||
Plan: `todo.md`. Decisions: `AGENTS.md`. The flavour's grammar:
|
Plan: `todo.md`. Decisions: `AGENTS.md`. The flavour's grammar:
|
||||||
[`spec/flavour.md`](spec/flavour.md).
|
[`spec/flavour.md`](spec/flavour.md).
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -113,8 +113,11 @@ literal-text fallback — a typo that reparses as prose is the silent loss §2 r
|
|||||||
|
|
||||||
## The opaque carry (AGENTS.md §3)
|
## The opaque carry (AGENTS.md §3)
|
||||||
|
|
||||||
A node type the library does not know rides as its raw JSON and restores to a deep-equal node.
|
A node no section spells where it stands — an unknown type, or a known one whose spelling belongs
|
||||||
Block and inline positions canonicalize differently, each fitting where it sits:
|
to the other position — rides as its raw JSON and restores to a deep-equal node. `hardBreak`,
|
||||||
|
`listItem` and `text` where a block belongs are the exception: their spelling lives inside another
|
||||||
|
node's body, so the misplacement is a named error. Block and inline positions canonicalize
|
||||||
|
differently, each fitting where it sits:
|
||||||
|
|
||||||
- **Block position**: a fenced code block with info string `adf`, body = the node's JSON —
|
- **Block position**: a fenced code block with info string `adf`, body = the node's JSON —
|
||||||
two-space indent, object keys sorted.
|
two-space indent, object keys sorted.
|
||||||
|
|||||||
@@ -104,9 +104,13 @@ detail is settled at its own milestone.
|
|||||||
The carry's fallback triggers land here too: `spec/flavour.md` carries a node its section
|
The carry's fallback triggers land here too: `spec/flavour.md` carries a node its section
|
||||||
cannot spell — an attrs key no section lists, a value that is not the section's type, an
|
cannot spell — an attrs key no section lists, a value that is not the section's type, an
|
||||||
arg slot holding no bare token, marks no nesting spells — where the emitter still refuses,
|
arg slot holding no bare token, marks no nesting spells — where the emitter still refuses,
|
||||||
which leaves 1d's blocked CommonMark nodes as the only refusal. The `\u007c` rule covers
|
which leaves the refusals a container's own spelling owns. The `\u007c` rule is wider than
|
||||||
the inline carry's `json` value with them: the carry spells `escaping: 'none'` today so
|
the pipe: a quoted attribute value spells `` ` ``, `&` and `<` raw today, so a backtick
|
||||||
that `tryPipeCell` refuses the pipe form for a carry whose JSON holds a `|`.
|
pair in an `inlineCard`'s `data`, a `status`'s `style` or the inline carry's `json` reads
|
||||||
|
back as a code span, an entity or raw HTML inside the value. One escape settles all four,
|
||||||
|
and the pick decides whether a directive or a code span wins where they overlap (3). The
|
||||||
|
carry spells `escaping: 'none'` meanwhile, which is what makes `tryPipeCell` refuse the
|
||||||
|
pipe form for a carry whose JSON holds a pipe.
|
||||||
The gate gains the collision property here: no two corpus documents may emit the same
|
The gate gains the collision property here: no two corpus documents may emit the same
|
||||||
bytes — one spelling for two documents is a round-trip break no parser can undo, and it is
|
bytes — one spelling for two documents is a round-trip break no parser can undo, and it is
|
||||||
provable without one. It also settles the emitter's one known approximation: delimiter
|
provable without one. It also settles the emitter's one known approximation: delimiter
|
||||||
@@ -123,8 +127,9 @@ detail is settled at its own milestone.
|
|||||||
every raw-HTML construct in input is an error result. The CommonMark spec suite runs
|
every raw-HTML construct in input is an error result. The CommonMark spec suite runs
|
||||||
against it from here (§10). The parser owes `~` the same `can_open`/`can_close` the emitter
|
against it from here (§10). The parser owes `~` the same `can_open`/`can_close` the emitter
|
||||||
assumes — CommonMark flanking, as for `*` — which `spec/flavour.md` does not yet pin, and
|
assumes — CommonMark flanking, as for `*` — which `spec/flavour.md` does not yet pin, and
|
||||||
the precedence between a directive and a code span that overlap, which the emitter already
|
the precedence between a directive and the constructs a raw attribute value opens inside it —
|
||||||
spells leftmost-first (`` `a`:adf{json="…`…"}`b` ``).
|
a code span, an entity, raw HTML — which one directive alone already reaches until 2e's
|
||||||
|
escape lands.
|
||||||
`src/` gets its hierarchy at the same split — `adf/`,
|
`src/` gets its hierarchy at the same split — `adf/`,
|
||||||
`markdown/`, `html/`, the grammar module shared inside `markdown/` — while the rename is
|
`markdown/`, `html/`, the grammar module shared inside `markdown/` — while the rename is
|
||||||
still mechanical. Three files do not move whole: `block-directives.ts` and
|
still mechanical. Three files do not move whole: `block-directives.ts` and
|
||||||
|
|||||||
Reference in New Issue
Block a user