3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Answer product-owner round 1: 0 🔴, 4 🟡 (0 misread); keep unreleased calls out of the Breaking entries and sharpen the feature page's intro

This commit is contained in:
2026-11-05 11:51:50 +01:00
committed by Lillem4n
parent d7c6a6b1f5
commit 0d7a071f4e
5 changed files with 50 additions and 44 deletions
+28 -24
View File
@@ -2,13 +2,32 @@
## Unreleased
- `docs/feature-table.md` sets real documents beside what `adfToLosslessMarkdown`,
- **Breaking:** `adfToPortableMarkdown` refuses, as `unsupported-node-shape`, a known block holding
`text`, a known non-text inline node holding `text` or non-empty `content`, and a known block that
takes no content, such as `rule`, holding non-empty `content`. `adfToLosslessMarkdown` now refuses
them inside a carried node too, and `losslessMarkdownToAdf` refuses a carry holding one.
`MIGRATION.md` says how to convert stored markdown.
- **Breaking:** `losslessMarkdownToAdf` gives a `date`, `emoji`, `inlineCard`, `mention`, `status`
or `mediaInline` only the marks Atlassian's schema lets it hold: `**!adf:mention[@A]{id=…}**` now
reads as a plain mention. A link or directive mark around only nodes that cannot take it is
`unsupported-node-shape`, even in markdown `adfToLosslessMarkdown` wrote. A link around hard
breaks alone still reads as before. `adfToLosslessMarkdown` writes a node holding a mark its type
cannot take as `!adf:carry{json="…"}`, except an inline card a link marks, which it refuses.
`MIGRATION.md` says how to convert stored markdown.
- **Breaking:** `adfToLosslessMarkdown` and `adfToPortableMarkdown` refuse an inline card a link marks
as `unsupported-node-shape` at the card's path, and `losslessMarkdownToAdf` refuses a carry
holding one, since Atlassian's schema gives a card no link mark. `MIGRATION.md` says what to do.
- **Breaking:** every reader gives code text no mark but `link` and `annotation`, as Atlassian's
schema requires: ``**`npm ci`**`` now reads as plain code, and a directive mark other than a link
around only code spans is `unsupported-node-shape`. `adfToLosslessMarkdown` writes code text
holding another mark as `!adf:carry{json="…"}`, and `adfToPortableMarkdown` drops that mark.
- **Breaking:** `losslessMarkdownToAdf` gives text no `border` mark, as Atlassian's schema requires:
`!adf:border[x]{color="#091e42" size=2}` is `unsupported-node-shape`, and only a
`mediaInline` takes `border`. `adfToLosslessMarkdown` writes text holding `border` as `!adf:carry{json="…"}`.
`MIGRATION.md` says how to convert stored markdown.
- `docs/feature-table.md` sets example documents beside what `adfToLosslessMarkdown`,
`adfToPortableMarkdown` and `adfToHtml` write for them, and says whether each output reads back to
the same document.
- **Breaking:** `adfToHtml`, `adfToPortableMarkdown`, `htmlToAdf` and `losslessMarkdownToAdf` refuse
a block or non-text inline node holding `text`, and a leaf block or non-text inline node holding
`content`, as `adfToLosslessMarkdown` already did outside a carried node, which it now also
refuses. `MIGRATION.md` says how to convert a stored document.
- The package's description and keywords say it needs no React, Atlaskit or runtime dependency and
runs in Node, Forge, Deno, Bun and browsers, and the README opens with the call to take for each
need. The README's first comparison table lists the npm packages running in Node that convert ADF
@@ -25,31 +44,16 @@
node's JSON in `data-adf` and showing its content, links included. Every URL is written as the
ADF holds it, `javascript:` included: sanitize HTML from an author you do not trust before
showing it.
It refuses, carried or not and as `unsupported-node-shape`, a known block holding `text`, a known
non-text inline node holding `text` or non-empty `content`, and a known block that takes no
content, such as `rule`, holding non-empty `content`.
- `htmlToAdf` reads `adfToHtml`'s HTML back to the document it came from, a carry element from its
`data-adf`. It reads that HTML as a browser's `innerHTML` writes it too (a raw `"` in text,
`&nbsp;`, a raw `<` or `>` in an attribute value) and as a form post sends it (`\r\n` for each
line feed). An edit to an emoji's, mention's or status's text becomes its `text`; an edit to a
date's or a card's text alone is refused. It refuses any other HTML, including an edit to what a
carry element shows, with the codes README §The calls lists.
- **Breaking:** `losslessMarkdownToAdf` gives a `date`, `emoji`, `inlineCard`, `mention`, `status`
or `mediaInline` only the marks Atlassian's schema lets it hold: `**!adf:mention[@A]{id=…}**` now
reads as a plain mention. A link or directive mark around only nodes that cannot take it is
`unsupported-node-shape`, even in markdown `adfToLosslessMarkdown` wrote. A link around hard
breaks alone still reads as before. `adfToLosslessMarkdown` writes a node holding a mark its type
cannot take as `!adf:carry{json="…"}`, except an inline card a link marks, which it refuses.
`MIGRATION.md` says how to convert stored markdown.
- **Breaking:** `adfToLosslessMarkdown` and `adfToPortableMarkdown` refuse an inline card a link marks
as `unsupported-node-shape` at the card's path, and `losslessMarkdownToAdf` refuses a carry
holding one, since Atlassian's schema gives a card no link mark. `adfToHtml` and `htmlToAdf` do
the same. `MIGRATION.md` says what to do.
- **Breaking:** every reader gives code text no mark but `link` and `annotation`, as Atlassian's
schema requires: ``**`npm ci`**`` now reads as plain code, and a directive mark other than a link
around only code spans is `unsupported-node-shape`. `adfToLosslessMarkdown` writes code text
holding another mark as `!adf:carry{json="…"}`, and `adfToPortableMarkdown` drops that mark.
- **Breaking:** `losslessMarkdownToAdf` gives text no `border` mark, as Atlassian's schema requires:
`!adf:border[x]{color="#091e42" size=2}` is `unsupported-node-shape`, and only a
`mediaInline` takes `border`. `adfToLosslessMarkdown` writes text holding `border` as `!adf:carry{json="…"}`.
`MIGRATION.md` says how to convert stored markdown.
It also refuses a carry element holding a shape `adfToHtml` refuses.
- `adfToLosslessMarkdown` and `adfToPortableMarkdown` write an `em`, `strike`, `strong` or `link`
mark once across the hard breaks between two texts it marks: `_One\` then `Two_`, where they
wrote `_One_\` then `_Two_`. They write an `em`, `strike` or `strong` mark once around a code
+8 -7
View File
@@ -301,7 +301,7 @@ documentation says, on 2026-10-28.
Serves [Goals](#goals) 1, 2 and 8. Pure functions, each taking a whole document and returning a
whole result; no I/O, no configuration. [What each call writes](https://github.com/larvit/adf-codec/blob/main/docs/feature-table.md)
sets real documents beside each writer's output and says whether that output reads back the same.
sets example documents beside each writer's output and says whether that output reads back the same.
```ts
adfToLosslessMarkdown(doc: AdfDocument): Result<string>
@@ -528,7 +528,7 @@ content. No stylesheet ships. The complete dialect:
| `table`, `tableRow`, `tableCell`, `tableHeader` | `<table>`, `<thead>` for a first row of `tableHeader` cells alone, `<tbody>`, `<tr>`, `<td>` and `<th>`; a cell's first paragraph stands as an item's does, and `<td colspan="2" rowspan="2" style="background-color:#fffae6" data-colwidth="120,80">` writes its attributes |
| `text` marked `code`, `em`, `strong` | `<code>`, `<em>`, `<strong>`, the first mark outermost |
| an attribute its type holds that its element does not write, such as `localId`, on a paragraph, heading, quote, list, list item, table, row, cell, code block, rule, emoji, mention, status, date or card | `data-*` named as HTML's `dataset` names it: `<p data-local-id="a1f3">` |
| any other node, or a node holding what its element cannot, except an inline card a link marks, which is refused ([The errors](#the-errors)) | `<div class="adf-carry" data-adf="{…}"><p>its content</p></div>`, a `<span>` inline |
| any other node, or a node holding what its element cannot, except a shape Atlassian's schema forbids, which is refused ([The errors](#the-errors)) | `<div class="adf-carry" data-adf="{…}"><p>its content</p></div>`, a `<span>` inline |
- Every URL is written as the ADF holds it, `javascript:` included: a link's, an image's, a
card's, and a carried link's in the carry element. Sanitize the HTML before showing a document from
@@ -649,11 +649,12 @@ Every call can raise these:
- **Writing, in `adfToLosslessMarkdown`**: a node not riding the carry that carries one mark type
twice. Keep the ADF and show the document read-only; where you built the ADF, fix
what the message names.
- **Writing, in `adfToHtml`, `adfToLosslessMarkdown` and `adfToPortableMarkdown`**: what Atlassian's
schema gives no node, even inside a node that rides the carry: an inline card marked by a link, a
block or a non-text inline node holding `text`, or a leaf block or a non-text inline node holding
`content`. Keep the ADF and pass the document over or show it read-only; where you built the ADF,
remove what the error's `path` and message name.
- **Writing, in `adfToHtml`, `adfToLosslessMarkdown` and `adfToPortableMarkdown`**: a shape
Atlassian's schema forbids, even inside a node that rides the carry: an inline card a link marks,
a known block holding `text`, a known non-text inline node holding `text` or non-empty `content`,
or a known block that takes no content, such as `rule`, holding non-empty `content`. Keep the ADF
and pass the document over or show it read-only; where you built the ADF, remove what the error's
`path` and message name.
- **Writing, in `adfToPortableMarkdown`**: a line or task list the portable writer cannot spell.
This is a bug in this library: report it.
- **Past the engine's longest string**:
+4 -7
View File
@@ -463,8 +463,7 @@ and `<span … data-data>` 2. The maintainer took the leading spelling of each,
- a mention is `<span class="adf-mention" data-access-level="CONTAINER" data-id="5b10a2" data-user-type="DEFAULT">@Mikael</span>`, its `text` the content;
- a card holding `data` and no URL is `<a class="adf-inline-card" href="https://example.com/plan" data-data="{…}">Release plan</a>`, `href` the URL its `data` holds and the text its name, else that URL. A card holding both `data` and a `url` rides the carry: its `href`, read back, could not say which of the two URLs it spells.
A sixth panel (2026-11-05, three readers in the same roles) picked each of these 3 of 3 for a table,
which until then rode the carry:
A sixth panel (2026-11-05, three readers in the same roles) picked each of these 3 of 3 for a table:
- a first row of header cells alone in `<thead>` and the rest in `<tbody>`, a row to a line, over a
bare `<table>` of `<tr>` rows and over a line per cell;
@@ -472,9 +471,6 @@ which until then rode the carry:
- a cell's attributes as `<td colspan="2" rowspan="2" style="background-color:#fffae6" data-colwidth="120,80">`,
over `data-*` for all four.
A `background` holding anything but letters and digits after an optional `#` rides the carry, so no
value can end the `style` declaration.
## `htmlToAdf` also reads the dialect as a browser serializes it
2026-10-28, the maintainer. Goal 6. Valid while a viewer/editor app may show `adfToHtml`'s HTML in a
@@ -773,8 +769,9 @@ writer refuses alike, with one code, message and path, wherever the shape stands
too, and a carry reader refuses it with the same code and the writers' message after its holder's
name. The first step refuses an `inlineCard` with a `link` mark as `unsupported-node-shape`: the
schema gives a card the `annotation` mark alone (§The node tables answer to Atlassian's schema),
and HTML nests no `<a>` in an `<a>`. It also refuses a block or non-text inline node holding `text`,
and a leaf block or non-text inline node holding a non-empty `content`.
and HTML nests no `<a>` in an `<a>`. The first step also refuses a known block holding `text`, a
known non-text inline node holding `text` or non-empty `content`, and a known block that takes no
content holding non-empty `content`.
Nothing else joins the first step. Nothing refuses a node type, mark or attribute the schema does
not name (§Unknown nodes ride the carry), a known node where no section spells it, or a mark a node
+4 -2
View File
@@ -1,8 +1,10 @@
# What each call writes
`feature-table/build.mjs` writes this page from the corpus and the built package, and `./ci.sh` fails where the page differs.
Each section sets a sample ADF document beside what each call writes for it, then reads that output back: `adfToLosslessMarkdown` with `losslessMarkdownToAdf`, `adfToPortableMarkdown` with `portableMarkdownToAdf`, `adfToHtml` with `htmlToAdf`. `holds` means the reader gives the same document, `loses` a different one, and `refused` that the writer or reader returned an error, whose code the section names.
Each output is read back by its format's reader. `holds` means that gives the same document, `loses` a different one, and `refused` an error whose code the example names.
A result covers that example alone, and the examples are plain. Portable markdown drops every attribute, so a document holding one loses it even where its row says `holds`: to edit and save back, use `adfToLosslessMarkdown` and `losslessMarkdownToAdf`. An image holding an attribute, as editor-written ones do, rides the carry element in HTML, where this page shows `<img>`.
`./feature-table/write.sh` writes this page. Don't edit it by hand.
| Example | Lossless | Portable | HTML |
| --- | --- | --- | --- |
+6 -4
View File
@@ -8,16 +8,18 @@ const writers = [
const intro = `# What each call writes
\`feature-table/build.mjs\` writes this page from the corpus and the built package, and \`./ci.sh\` fails where the page differs.
Each section sets a sample ADF document beside what each call writes for it, then reads that output back: \`adfToLosslessMarkdown\` with \`losslessMarkdownToAdf\`, \`adfToPortableMarkdown\` with \`portableMarkdownToAdf\`, \`adfToHtml\` with \`htmlToAdf\`. \`holds\` means the reader gives the same document, \`loses\` a different one, and \`refused\` that the writer or reader returned an error, whose code the section names.
Each output is read back by its format's reader. \`holds\` means that gives the same document, \`loses\` a different one, and \`refused\` an error whose code the example names.
A result covers that example alone, and the examples are plain. Portable markdown drops every attribute, so a document holding one loses it even where its row says \`holds\`: to edit and save back, use \`adfToLosslessMarkdown\` and \`losslessMarkdownToAdf\`. An image holding an attribute, as editor-written ones do, rides the carry element in HTML, where this page shows \`<img>\`.
\`./feature-table/write.sh\` writes this page. Don't edit it by hand.
`
// `library` holds the package's calls, so a test can hand in what a real document cannot make them do.
// `library` is a parameter, so a test can hand in calls that refuse, which no corpus example makes them do.
export function renderFeatureTable(examples, library) {
const written = examples.map(({ doc, name }) => ({ doc, name, outcomes: writers.map((writer) => outcome(doc, writer, library)) }))
const rows = written.map(({ name, outcomes }) => `| [${name}](#${name.toLowerCase().replaceAll(' ', '-')}) | ${outcomes.map(({ result }) => result).join(' | ')} |`)
const head = `| Example | ${writers.map(({ column }) => column).join(' | ')} |\n| --- | --- | --- | --- |`
const head = `| Example | ${writers.map(({ column }) => column).join(' | ')} |\n| --- | ${writers.map(() => '---').join(' | ')} |`
return [intro, [head, ...rows].join('\n') + '\n', ...written.map(section)].join('\n')
}