From 39a7f16b95588272dca6f5442ea1a2a23c120364 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Wed, 14 Oct 2026 12:57:15 +0200 Subject: [PATCH] =?UTF-8?q?Answer=20prose=20round=201:=200=20=F0=9F=94=B4,?= =?UTF-8?q?=206=20=F0=9F=9F=A1;=20list=20unsupported-node-shape=20causes?= =?UTF-8?q?=20as=20sub-items=20with=20their=20remedies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 46 ++++++++++++++------------- src/markdown/parse/directive-nodes.ts | 2 +- todo.md | 2 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index fc81fa6..0284583 100644 --- a/README.md +++ b/README.md @@ -452,31 +452,33 @@ Every call can raise these: | --- | --- | --- | | `engine-limit` | in every call: a conversion that passes the longest array, `Map` or `Set` the JavaScript engine builds, or that the engine reports as out of memory; on JavaScriptCore (Bun, Safari), also one that passes its longest string, 2^31 − 1 characters | retry once; if it repeats, writing: set the document aside, keeping the ADF and passing it over or showing it read-only; reading: shorten the markdown, or nest its lists and quotes less deeply | | `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it | -| `unsupported-node-shape` | a node the call cannot read or write in its shape; the list below names each cause | the list below names each cause's remedy | +| `unsupported-node-shape` | each cause listed below the table | the remedy beside each cause below | -`unsupported-node-shape` fires on these causes: - -- **Reading, in `losslessMarkdownToAdf`**: markdown spells a node with an attribute, value, - argument or body its type does not take, or without one it needs, puts a line ending or a null - character in an `emoji`, `mention` or `status` content slot, wraps a directive mark around nothing - or only hard breaks, writes as a directive a node or mark the lossless flavour spells as - CommonMark, or puts a reserved directive out of place: `!adf:textBreak{}` parting nothing, - `!adf:doc` anywhere but as the whole document. Fix what the message names; +- **Reading, in `losslessMarkdownToAdf`.** Fix what the message names; [`spec/flavour.md`](https://github.com/larvit/adf-codec/blob/main/spec/flavour.md) lists every - type's attributes and body. For a line ending in a content slot, replace it with a space or write - the node as `!adf:carry`; for a null character there, write the node as `!adf:carry`. + type's attributes and body. The markdown: + - spells a node with an attribute, value, argument or body its type does not take, or without + one it needs; + - puts a line ending or a null character in an `emoji`, `mention` or `status` content slot: + replace a line ending with a space, or, for either, write the node as `!adf:carry`; + - wraps a directive mark around nothing or only hard breaks; + - writes as a directive a node or mark the lossless flavour spells as CommonMark; + - puts `!adf:textBreak{}` where it parts nothing, or `!adf:doc` anywhere but as the whole + document. - **Writing, in `adfToLosslessMarkdown`**: a node not riding the carry that carries one mark type twice, a block or a non-text inline node holding `text`, or a leaf block or a non-text inline node - holding `content`. Fix what the message names in the ADF. + holding `content`. Keep the ADF and show the document read-only; where you built the ADF, fix + what the message names. - **Writing, in `adfToPortableMarkdown`**: a line or task list the portable writer cannot spell. This is a bug in this library: report it. -- **Past the longest string**: in both writers, a document whose markdown would be longer than - 536870888 characters, V8's longest string, on every engine, and in `adfToPortableMarkdown` also a - document whose text alone is that long, edge whitespace included; in `losslessMarkdownToAdf`, a - directive whose plain CommonMark would be that long; in every call, a conversion that passes V8's - or SpiderMonkey's longest string. Writing, set the document aside, keeping the ADF and passing it - over or showing it read-only; reading, shorten the markdown, or nest its lists and quotes less - deeply. +- **Past the engine's longest string**: + - a writer's markdown would pass 536870888 characters, V8's longest string, on every engine; + - in `adfToPortableMarkdown`, the document's text alone would, edge whitespace included; + - in `losslessMarkdownToAdf`, a directive's plain CommonMark would; + - in every call, a conversion passes V8's or SpiderMonkey's longest string. + + When writing, set the document aside: keep the ADF, and pass it over or show it read-only. When + reading, shorten the markdown, or nest its lists and quotes less deeply. ## The guarantees @@ -518,9 +520,9 @@ Serves [Goals](#goals) 1, 4 and 5. the outer brackets literal text, which the spec requires and the reference itself breaks, nesting one `` in the other. - Not every document converts back: `adfToLosslessMarkdown` refuses a document nesting past 500 - levels, the causes `unsupported-node-shape` lists, and a conversion `engine-limit` - stops. Show the refusal and keep the document read-only; saving markdown you could not produce is the loss the round-trip exists to - stop. + levels, a document meeting a writing or longest-string cause `unsupported-node-shape` lists, and + a conversion `engine-limit` stops. Show the refusal and keep the document read-only; saving + markdown you could not produce is the loss the round-trip exists to stop. - Both readers narrow GFM's pipe table. A body row whose cell count differs from the header's is an error result, where GFM pads or cuts it. A line holding no `|` ends the table, where GFM reads it as a row. diff --git a/src/markdown/parse/directive-nodes.ts b/src/markdown/parse/directive-nodes.ts index 10f6080..53e2415 100644 --- a/src/markdown/parse/directive-nodes.ts +++ b/src/markdown/parse/directive-nodes.ts @@ -134,7 +134,7 @@ function readNamedMark(type: string, text: string, index: number, path: ConvertE const vocabulary = markVocabularies.get(name) if (vocabulary === undefined) return failure('unsupported-node-shape', `the ${marksAttribute} attribute of ${type} names ${name}, no mark the flavour lists: write the marks as their JSON array, marks="[{\\"type\\":\\"${name}\\"}]"`, path) const start = index + name.length - // A quoted value holds no directive, so a malformed pair takes no escape hint. + // These {attrs} sit inside the quoted marks value, where no directive opens, so a malformed pair takes no escape hint. const attributes = text.charAt(start) === '{' ? readAttributes(text, start, attributePairCause) : { value: { attributes: new Map(), end: start } } if (attributes.fault !== undefined) { return failure('unsupported-node-shape', `the ${name} mark in the ${marksAttribute} attribute of ${type}: ${attributes.fault.message}`, path) diff --git a/todo.md b/todo.md index 6f6be40..946b94a 100644 --- a/todo.md +++ b/todo.md @@ -227,7 +227,7 @@ document's whole tree. Give the node's path, and name its type and the attribute `emitLeaf` (`emit/inline-line.ts`) and `emitDirectiveBlock` (`emit/adf-to-markdown.ts`) refuse these with `unsupported-node-shape`, though `isAdfDocument` accepts them and the carry round-trips them; `docs/decisions.md` §The code list gives a cause the carry answers no code. Drop -them from the emitting half of the README's `unsupported-node-shape` row. Found by the README-goals +them from the README's `unsupported-node-shape` cause "Writing, in `adfToLosslessMarkdown`". Found by the README-goals audit, 2026-10-04. ### 75. Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.