mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
13 KiB
13 KiB
Changelog
Unreleased
- Breaking:
losslessMarkdownToAdfgives adate,emoji,inlineCard,mention,statusormediaInlineonly 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 isunsupported-node-shape, even in markdownadfToLosslessMarkdownwrote. A link around hard breaks alone still reads as before.adfToLosslessMarkdownwrites a node holding a mark its type cannot take as!adf:carry{json="…"}.MIGRATION.mdsays how to convert stored markdown. - Breaking: every reader gives code text no mark but
linkandannotation, as Atlassian's schema requires:**`npm ci`**now reads as plain code, and a directive mark other than a link around only code spans isunsupported-node-shape.adfToLosslessMarkdownwrites code text holding another mark as!adf:carry{json="…"}, andadfToPortableMarkdowndrops that mark. - Every reader keeps the URL and title of an image inside a link's text, where it dropped them:
(image)follows the link, linked to the image, so[look at  tonight](/moon)reads as[look at the moon tonight](/moon) ([image](moon.png "Full")). A link to the image's own URL takes no(image), unless the image's title is one the link lacks. adfToPortableMarkdownkeeps both targets of a linked external image standing inline, as in a table cell:[alt](href) ([image](url)), where it wrote[alt](url)and dropped the link's target.adfToPortableMarkdownkeeps a link title holding a backslash or an entity reference such as&, and in a table cell one holding a|, where it dropped the title. It escapes the character where CommonMark would read it otherwise, as in[docs](https://example.com "AT\&T"), andportableMarkdownToAdfreads the title back.losslessMarkdownToAdfnames the CommonMark spelling to write when it refuses a directive whose node CommonMark spells, such as[](href)for a linked!adf:mediaSingle.- Every reader and writer takes time linear in the text when it holds many
<!--,<?,<![CDATA[or<!openers without a terminator, or many unclosed backtick runs outside a directive's[content]. Such a text took time quadratic in its length: 160000 characters of<?took seven seconds each way, now 50 ms. - Every reader takes time linear in the markdown when it holds blank lines under list items nested
deeply. Such a list is still refused as
unsupported-nesting-depth, now after 40 ms where 20000 nested items and as many blank lines took 10.7 seconds. - Every reader takes time linear in the markdown when tab-indented lines sit under list items
nested deeply. Such a list is still refused as
unsupported-nesting-depth, now after 107 ms where 20 lines of 40000 tabs under 80000 nested items took 1988 ms. losslessMarkdownToAdftakes time linear in the markdown when an inline directive's[content]holds many unclosed backtick runs: 640000 characters of them took one second, now 100 ms.- The README compares the package with seven other ADF converters, each checked against its published version.
0.2.0
- Breaking: each call names the markdown it reads or writes.
adfToMarkdownandmarkdownToAdfare nowadfToLosslessMarkdownandlosslessMarkdownToAdf. A bot's or a model's markdown reads inportableMarkdownToAdf, and markdown known to be strict CommonMark in the newcommonMarkToAdf, where a directive, a pipe table,~~and anadf:fence read as text and code.MIGRATION.mdsays which call to take. - Breaking: the markdown readers refuse a value that is not a string with the new code
not-a-string;MIGRATION.mdlists what each value did before. - Breaking: every call returns the new code
engine-limitwhere a conversion passes the JavaScript engine's longest array,MaporSet, or meets an out-of-memory throw the call can catch, and, on JavaScriptCore, its longest string. A retry may succeed. Every other refusal stays deterministic. - Breaking: an attribute value that is not a plain object, array or JSON primitive, such as a
Date,Map, typed array or class instance, makesisAdfDocumentreturnfalseand the converters refuse it asnot-an-adf-document. Before, the converters wrote it as{}or its own keys. Build it as JSON holds it:date.toISOString(),Object.fromEntries(map),Array.from(bytes),{ ...instance }. - Breaking: directives, the inline opaque carry among them (now
!adf:carry{json="…"}), are spelled under an!adf:prefix (!adf:name … !adf:/name,!adf:name[content]{attrs},!adf:name arg {attrs}) in place of the:::/::/:nameforms: text holding an unescaped!adf:is claimed. Convert stored markdown perMIGRATION.md. - Breaking: the block carry is a code fence whose info string
adf:<type>names the node's type, its body the node's JSON withouttype, and a code fence whose info string opensadf:is claimed, andadfis an ordinary code block language. Convert stored markdown perMIGRATION.md. - Breaking: the markdown readers read markdown holding no block as a document whose
contentis empty, as Atlassian's schema requires;!adf:doc {content=none}spells a document holding nocontentkey. - Breaking:
unspellable-character,unspellable-line-startandunspellable-whitespaceleaveConvertErrorCode. A line ending in anemoji,mentionorstatusdirective's content slot is refused asunsupported-node-shape. A carriage return in text is spelled and a null character!adf:text{text="\u0000"}; a paragraph line opening with a code span is spelled as that code span, its backticks opening no fence; a code block holding either character, a code span holding a null character, a text node holding no text or holdingcontent, and anemoji,mentionorstatuswhosetextholds a line ending or a null character ride the carry. SeeMIGRATION.md. - Breaking:
unspellable-linkleavesConvertErrorCode; a link whosehrefortitleno CommonMark escape spells is written as!adf:link[text]{attrs}. - Breaking: some directive refusals carry
malformed-directivewhere they carriedunsupported-node-shape, and an empty node's leaf and closed spellings swap which one parses;MIGRATION.mdlists each. - Breaking: a link whose text holds another link keeps the inner link and leaves the outer
brackets literal text, where
0.1.0split the outer link around it; seeMIGRATION.md. - Breaking:
unmappable-imageleavesConvertErrorCode, and every image reads. An image reads as an image only alone in its paragraph, and its title becomes the caption. Anywhere else, after a task marker too, it reads as its alt text linked to its URL; inside a link, as plain text the link marks. A link alone in its paragraph whose whole text is one image reads as that image, with the link marking it. To keep an image an image, give it a paragraph of its own. SeeMIGRATION.md. - Breaking: an empty link text reads as its destination,
[](/url)as/urllinked, and[]()as nothing, where both spellings stayed literal text. SeeMIGRATION.md. - Breaking: a hard break inside emphasis, strike, a link or a directive mark reads holding no
marks, as Atlassian's schema requires, where every reader gave it the marks around it. A link
whose text is only hard breaks reads as its destination linked, followed by those breaks, and
losslessMarkdownToAdfrefuses a directive mark wrapping only hard breaks asunsupported-node-shape.adfToLosslessMarkdownwrites a hard break holding marks as!adf:carry{json="…"}. SeeMIGRATION.md. - Breaking: a list whose bullet character or ordered delimiter changes reads as two lists, as
CommonMark reads it:
- athen* b,1. athen1) b.adfToLosslessMarkdowngives a list right after one of its kind the other marker,*or1), in place of the::listBreakseparator. Convert stored markdown perMIGRATION.md. - Breaking: a link reference definition between two list items, after a blank line and at the
list's own indentation, ends the list, as CommonMark reads it;
0.1.0read one list. SeeMIGRATION.md. - Breaking:
adfToLosslessMarkdownwrites a centered, uncaptioned external image whose one mark is a link holding onlyhrefandtitleas[](href "title"), where it wrote themediaSingledirective. Convert stored markdown perMIGRATION.md. - Breaking:
adfToLosslessMarkdownwritescolwidthas its widths joined by commas,colwidth="340,420", and a block'smarksas each mark's type and attributes,marks="breakout{mode=wide}", where it wrote both as JSON arrays, andlosslessMarkdownToAdfrefuses an array the new spelling holds.marksstays a JSON array where the flavour does not list a mark. Convert stored markdown perMIGRATION.md. - Fix the time
adfToLosslessMarkdownandadfToPortableMarkdowntake on text holding a long run of*,_,~or backticks, or a heading ending in a long run of#: the time is linear in the run, and a run of 20000*takes milliseconds where it took seconds. - Fix the round-trip, so
losslessMarkdownToAdf(adfToLosslessMarkdown(doc))deep-equalsdocasJSON.parsebuilds it. Two adjacent text nodes CommonMark would read back as one are parted by!adf:textBreak{}, an emptyattrs,contentormarksis spelled{attrs=empty},{content=empty}or{marks=empty},-0is spelled-0, and acodeBlockof several text nodes is a fence per node. AcodeBlockholding other than plain text nodes rides the block carry, where it was refused. - Fix a link
titleholding a null character: it is written as!adf:link[text]{attrs}, where it read back as U+FFFD. - Fix an image inside another image's description: it flattens into the alt text, where it was refused.
- Fix a JSON key holding an escape, in a carried node or a JSON attribute such as
parameters:losslessMarkdownToAdfreads it as the key it spells, where on V8 13.6 and later (Node 24, Deno, Chrome)markdownToAdfcould refuse markdownadfToMarkdownwrote, asunsupported-node-shape, once the process had read a key holding a backslash (V8 bug). - Fix a cyclic document, in which an object holds itself:
adfToLosslessMarkdownandadfToPortableMarkdownrefuse it asnot-an-adf-documentandisAdfDocumentreturnsfalse, where all three hung. - Fix a document whose markdown would be longer than 536870888 characters, V8's longest string:
adfToLosslessMarkdownandadfToPortableMarkdownrefuse it asunsupported-node-shapeon every engine, where they threw or ran out of memory. - Fix a directive that, written out as plain CommonMark, would be longer than 536870888 characters:
losslessMarkdownToAdfrefuses it asunsupported-node-shape, where a few megabytes of nested list lines ran the call out of memory. - Fix the markdown readers past V8's or SpiderMonkey's longest string: they return
unsupported-node-shape, where they threw. - Fix a paragraph of tens of millions of characters:
adfToLosslessMarkdownandadfToPortableMarkdownneed a few bytes a character on top of the document and its markdown, where they needed about 50. Before, 80 million characters ran Node's default heap out of memory and 20 million*threwRangeError.losslessMarkdownToAdfno longer throwsRangeErroron such a paragraph inside a directive. - Fix deeply nested compact JSON in an
adf:fence:losslessMarkdownToAdfrefuses it asunsupported-node-shape, as it refuses any compact JSON there, where about a megabyte of it threw. - Add
adfToPortableMarkdownandportableMarkdownToAdf, a lossy pair converting ADF to and from markdown GitHub, GitLab and Obsidian render: alerts, callouts, task lists,==highlights==and pipe tables. A task item keeps its paragraphs, then its nested task lists; the rest of the item, such as an image or a code block, stands after the task list.portableMarkdownToAdfreads GFM's pipe table with or without leading pipes, and with alignment colons, dropping the alignment. - Spell
rule'scolor,styleandweight,layoutSection'scolumnRuleStyleand a link'scollection,idandoccurrenceKeydirectly where they rode the opaque carry. - The README opens with what the package does and for whom, and
package.jsoncarries search keywords. - The source moves to github.com/larvit/adf-codec, where issues and pull requests go.
0.1.0
- First release: lossless conversion between ADF and an extended markdown flavour —
adfToMarkdown,markdownToAdfandisAdfDocument. Nothing throws, and every error carries acodefrom a closed list.