From ba531e13bf8cec623fe29e59654750fcb2f5b513 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Fri, 9 Oct 2026 02:49:10 +0200 Subject: [PATCH] Answer stability round 1: every reader names unsupported-nesting-depth, Flavour stays file-local; product-owner round 1: model markdown reads in portableMarkdownToAdf, the migration rows say what each reader builds, each code names its readers, item 84 filed --- CHANGELOG.md | 7 ++++--- MIGRATION.md | 4 ++-- README.md | 17 ++++++++++------- src/markdown/portable/conventions.ts | 2 +- todo.md | 10 +++++++++- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43659d6..d4c836e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ ## Unreleased - **Breaking:** each call names the markdown it reads or writes. `adfToMarkdown` and `markdownToAdf` - are now `adfToLosslessMarkdown` and `losslessMarkdownToAdf`, and the new `commonMarkToAdf` reads - markdown a person or a model wrote as the CommonMark spec says: a directive, a pipe table, `~~` - and an `adf:` fence read as text and code. `MIGRATION.md` says which call to take. + are now `adfToLosslessMarkdown` and `losslessMarkdownToAdf`. A bot's or a model's markdown reads + in `portableMarkdownToAdf`, and markdown known to be strict CommonMark in the new + `commonMarkToAdf`, where a directive, a pipe table, `~~` and an `adf:` fence read as text and + code. `MIGRATION.md` says which call to take. - **Breaking:** the markdown readers refuse a value that is not a string with the new code `not-a-string`; `MIGRATION.md` lists what each value did before. - **Breaking:** an attribute value that is not a plain object, array or JSON primitive, such as a diff --git a/MIGRATION.md b/MIGRATION.md index bb6a1b9..1916b73 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,8 +7,8 @@ Each call names the markdown it reads or writes: | `0.1.0` | `0.2.0` | | --- | --- | | `adfToMarkdown` | `adfToLosslessMarkdown` | -| `markdownToAdf`, for markdown `adfToMarkdown` wrote | `losslessMarkdownToAdf` | -| `markdownToAdf`, for markdown a person or a model wrote | `portableMarkdownToAdf`, which keeps GFM's pipe tables and `~~`; or `commonMarkToAdf`, which reads them, a directive and an `adf:` fence as CommonMark does, as text and code | +| `markdownToAdf`, for markdown `adfToMarkdown` wrote, edited since or not | `losslessMarkdownToAdf` | +| `markdownToAdf`, for markdown no `adfToMarkdown` output went into | `portableMarkdownToAdf`, which keeps GFM's pipe tables and `~~` and reads alerts, callouts, task markers and `==` as nodes (README §Portable markdown); or `commonMarkToAdf`, which reads all of them, a directive and an `adf:` fence as CommonMark does, as text and code | Directives moved under the `!adf:` prefix. `losslessMarkdownToAdf` reads `0.1.0`'s spelling without an error, turning each directive into text and each carried node into an `adf` code block. Before diff --git a/README.md b/README.md index bf18c5c..5a176f7 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ products store as ADF. - **Lossless:** `adfToLosslessMarkdown` and `losslessMarkdownToAdf` give back the document they started from — panels, mentions, statuses and node types this version does not know included. -- **CommonMark in:** `commonMarkToAdf` reads a bot's or a model's markdown as the CommonMark spec - says, apart from raw HTML and the exceptions [The guarantees](#the-guarantees) lists; markdown - holding GFM's pipe tables or `~~` reads in `portableMarkdownToAdf`. +- **Markdown in:** `portableMarkdownToAdf` reads a bot's or a model's markdown, GFM's pipe tables + and `~~` included; `commonMarkToAdf` reads markdown known to be strict CommonMark as its spec + says, nothing in it becoming a table, task or panel. Both apart from raw HTML and the exceptions + [The guarantees](#the-guarantees) lists. - **Portable markdown, lossy:** `adfToPortableMarkdown` and `portableMarkdownToAdf` convert to and from the markdown that GitHub, GitLab and Obsidian render — GitHub Flavored Markdown's alerts, task lists and pipe tables, Obsidian Flavored Markdown's callouts and highlights. @@ -183,9 +184,11 @@ UTF-16 code unit, a JavaScript string index rather than a codepoint or a byte of or before the refusal — currently the start of the line the enclosing block begins on; a later minor may narrow that, never widen it. For `not-a-string` it is `{ line: 1, offset: 0 }`. -Parsing — `losslessMarkdownToAdf` raises every row, `portableMarkdownToAdf` the rows naming no -directive, and `commonMarkToAdf` `not-a-string`, `unmappable-html` and `unmappable-image`; -`htmlToAdf` at `0.3.0`: +Parsing — `commonMarkToAdf` raises `not-a-string`, `unmappable-html` and `unmappable-image`; +`portableMarkdownToAdf` those and `malformed-pipe-table`; `losslessMarkdownToAdf` every row; and +`htmlToAdf`, at `0.3.0`, the rows its dialect reaches. Every reader also raises +`unsupported-nesting-depth`, and `losslessMarkdownToAdf` `unsupported-node-shape`, from the table +after next: | Code | Fires when | What you can do | | --- | --- | --- | @@ -193,7 +196,7 @@ directive, and `commonMarkToAdf` `not-a-string`, `unmappable-html` and `unmappab | `malformed-pipe-table` | a pipe row that is no pipe table — a missing or ragged `---` delimiter row, an alignment colon in it, or a row not opening with a pipe | open every row with a pipe and give the delimiter row the header's cell count; to keep the lines literal text instead, escape the leading pipe of every one — escaping a single row leaves the next to open a fresh table and fail the same way | | `not-a-string` | the value handed in is not a string, such as `null`, a number or a `String` object | pass the input as a string, `String(value)` for a `String` object; the message names the value's type | | `unknown-directive-name` | a directive whose name is no node or mark this version spells | check the name in `spec/flavour.md`, or escape the prefix as `\!adf:`; the spelling itself is well formed, so a later minor may give the name meaning | -| `unmappable-html` | the input holds an HTML construct the documented element set does not map, a comment and a processing instruction among them — at this version that is every raw HTML construct in markdown, the element set landing at `0.3.0` | remove the construct, or write what it holds in the lossless flavour | +| `unmappable-html` | the input holds an HTML construct the documented element set does not map, a comment and a processing instruction among them — at this version that is every raw HTML construct in markdown, the element set landing at `0.3.0` | remove the construct, or write what it holds as markdown | | `unmappable-image` | an image sits inside other content that is not another image's description, or carries a title | give the image a paragraph of its own and drop the title | | `unspellable-whitespace` | an `emoji`, `mention` or `status` directive's content slot spells a newline or a carriage return — ` `, ` `, `!adf:text{text="\n"}` | replace it with a space, or write the node as `!adf:carry` — an inline directive never spans lines | diff --git a/src/markdown/portable/conventions.ts b/src/markdown/portable/conventions.ts index 648d0d4..83e0a7e 100644 --- a/src/markdown/portable/conventions.ts +++ b/src/markdown/portable/conventions.ts @@ -1,6 +1,6 @@ import { isWordCharacter } from '../commonmark/emphasis-matching.ts' -export type Flavour = 'commonmark' | 'lossless' | 'portable' +type Flavour = 'commonmark' | 'lossless' | 'portable' export type WrittenFlavour = Exclude diff --git a/todo.md b/todo.md index edea6e1..0a12573 100644 --- a/todo.md +++ b/todo.md @@ -6,7 +6,7 @@ `Bar = 9` -`Next ID = 84` +`Next ID = 85` | Goal | W | |---|---| @@ -38,6 +38,7 @@ | 77 | 0.3.0 | decision | **Read the input once into a plain copy, so an accessor property or a throwing Proxy yields `not-an-adf-document` and every call returns a result.** | 4 | 4 | 5 | 9 | 1 | 23.0 | | 83 | 0.3.0 | defect | **Refuse a document whose markdown would pass the engine's string limit with a result, never a `RangeError`.** | 3 | 4 | 5 | 8 | 1, 9 | 22.0 | | 71 | 0.3.0 | | **Add a README table comparing the package with the other ADF converters, each cell checked against that package's own docs or behaviour.** | 1 | 3 | 6 | 7 | 3 | 21.2 | +| 84 | 0.3.0 | | **Read GFM's pipe table without leading pipes, and with alignment colons, in `portableMarkdownToAdf`, dropping the alignment.** | 3 | 4 | 7 | 7 | 4, 7 | 19.4 | | 80 | 0.3.0 | | **Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document.** | 2 | 3 | 5 | 6 | 1 | 19.2 | | 68 | 0.3.0 | | **Carry a non-text inline node holding `content` or `text`, a block holding `text`, and a leaf block holding `content`.** | 3 | 3 | 4 | 7 | 1 | 18.2 | | 75 | 0.3.0 | | **Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.** | 2 | 2 | 3 | 7 | 1 | 17.5 | @@ -183,6 +184,13 @@ the table names each version and the date it was checked. Found while planning d candidates are `@atlaskit/editor-markdown-transformer`, `adf-to-markdown`, `adf-to-md`, `adf2markdown`, `extended-markdown-adf-parser`, `marklassian` and `md-to-adf`. +### 84. Read GFM's pipe table without leading pipes, and with alignment colons, in `portableMarkdownToAdf`, dropping the alignment. + +Models write `|:---|` delimiter rows and tables without a leading pipe; both are +`malformed-pipe-table` today, and `portableMarkdownToAdf` is the bot's reader. ADF holds no column +alignment, so the portable reader drops it (Goal 7: form, never content). The lossless reader keeps +§Tables' narrowing. Found by the product-owner review of item 43, 2026-10-09. + ### 80. Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document. Lands with items 45 and 77, whose reader walks every node. Today every refused node, such as one