Goals become one-line aims, their detail moves to the sections that meet them
This commit is contained in:
@@ -130,13 +130,11 @@ One-line commit messages and PR titles; short PR summaries. No AI-attribution ma
|
|||||||
|
|
||||||
## 7. The working loop
|
## 7. The working loop
|
||||||
|
|
||||||
`todo.md` lists what is left under the release that ships it, in shipping order. One item per
|
`todo.md` lists what is left under the release that ships it, in shipping order. A session works
|
||||||
session — the first under the earliest release — in the smallest PR-able chunk; split a big item
|
one chunk, starting from the first item under the earliest release, and stops there whatever it
|
||||||
into sub-items in `todo.md` before starting it. A chunk running a little over or under that is not
|
was asked to finish: a release is a chain of sessions, so an instruction to work until a release is
|
||||||
worth deliberating; what matters is that nothing is left undone in the end. The session stops there
|
done names the chain, not the session. An open PR is a chunk already in flight, and finishing it is
|
||||||
whatever it was asked to finish: a release is a chain of sessions, so an instruction to work until a
|
the session.
|
||||||
release is done names the chain, not the session. An open PR is a chunk already in flight, and
|
|
||||||
finishing it is the session.
|
|
||||||
Per chunk:
|
Per chunk:
|
||||||
|
|
||||||
1. Fresh worktree off updated `origin/main`; implement tests-first (§3).
|
1. Fresh worktree off updated `origin/main`; implement tests-first (§3).
|
||||||
@@ -145,7 +143,7 @@ Per chunk:
|
|||||||
result exists for the commit under review, or when the diff since that result cannot affect
|
result exists for the commit under review, or when the diff since that result cannot affect
|
||||||
it (docs-only) — re-run only what its own findings or fixes invalidate.
|
it (docs-only) — re-run only what its own findings or fixes invalidate.
|
||||||
3. Merge the PR (standing authorization, this repo only, granted by the maintainer through the
|
3. Merge the PR (standing authorization, this repo only, granted by the maintainer through the
|
||||||
`0.2.0` release), delete the item from `todo.md` — what a consumer sees of it is
|
`0.2.0` release), delete the chunk's items from `todo.md` — what a consumer sees of it is
|
||||||
reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop.
|
reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop.
|
||||||
|
|
||||||
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
|
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
|
||||||
@@ -165,7 +163,7 @@ reading is the ask. Never ask "A or B?": state the gap, the earlier entries of i
|
|||||||
nearest text, a candidate entry in that file's voice, and the instance it yields. An entry that
|
nearest text, a candidate entry in that file's voice, and the instance it yields. An entry that
|
||||||
keeps collecting instances is wrong: rewrite it.
|
keeps collecting instances is wrong: rewrite it.
|
||||||
|
|
||||||
Which output the audience expects — README goal 6 — is settled by a reader panel rather than
|
Which output the audience expects — README goal 5 — is settled by a reader panel rather than
|
||||||
asked: three fresh-context readers, one per README persona the conversion serves, each given only
|
asked: three fresh-context readers, one per README persona the conversion serves, each given only
|
||||||
`## Audience` and the input, writing what they expect before picking among outputs the goals
|
`## Audience` and the input, writing what they expect before picking among outputs the goals
|
||||||
allow, rendered, shuffled, with no rationale and nothing saying what is implemented. Three agreeing
|
allow, rendered, shuffled, with no rationale and nothing saying what is implemented. Three agreeing
|
||||||
@@ -174,7 +172,7 @@ The verdict lands in `docs/decisions.md`.
|
|||||||
|
|
||||||
### Findings, numbers and empty releases
|
### Findings, numbers and empty releases
|
||||||
|
|
||||||
- A finding inside the chunk's item is fixed in the chunk. Outside it, a new `todo.md` item, always
|
- A finding inside the chunk's items is fixed in the chunk. Outside it, a new `todo.md` item, always
|
||||||
in a release, weighed against every item on that release by the personas and `docs/decisions.md`
|
in a release, weighed against every item on that release by the personas and `docs/decisions.md`
|
||||||
§Plain markdown is a flavour of the grammar through §Names stay text — an item it outweighs moves
|
§Plain markdown is a flavour of the grammar through §Names stay text — an item it outweighs moves
|
||||||
later. A weighing no entry decides is asked as a gap.
|
later. A weighing no entry decides is asked as a gap.
|
||||||
|
|||||||
@@ -23,47 +23,21 @@ represent.
|
|||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
In priority order.
|
The most useful ADF conversion library available, by these goals in priority order:
|
||||||
|
|
||||||
1. **Lossless, and every call returns.** The round-trip holds for every document the lossless
|
1. **Lossless, and every call returns a result, never a throw.**
|
||||||
conversions take, node types this version does not know included; one that has no spelling is
|
2. **ADF is the hub.**
|
||||||
refused and says where, never silently reduced. No input makes a call loop forever or overflow
|
3. **Each format reads and writes as its standard says.**
|
||||||
the stack. Every goal below gives way to this one.
|
4. **Our markdown is CommonMark, extended only where CommonMark has no spelling.**
|
||||||
2. **ADF is the hub.** Every format and flavour converts to and from ADF, and no two others
|
5. **No surprises: output reads and edits the way its audience expects.**
|
||||||
convert directly: markdown↔HTML composes through ADF. Adding a format or flavour costs one
|
6. **Lossy conversion drops form, never content.**
|
||||||
reader and one writer. A flavour of a grammar shares that grammar's reader and writer and adds
|
7. **Runs in any JavaScript engine, with nothing to install, configure or connect.**
|
||||||
only its own spellings.
|
8. **Fast, and linear in the document's size.**
|
||||||
3. **Plain CommonMark is input.** Markdown written for something else converts — the exceptions
|
9. **Easy to find, and clear at a glance what it does.**
|
||||||
below are the whole of them — and every spelling a flavour claims on top of CommonMark is
|
|
||||||
escapable, so each flavour is opt-in.
|
|
||||||
4. **Output a person can edit.** A node CommonMark can spell gets that spelling; the lossless
|
|
||||||
flavour's directive form carries only what CommonMark cannot hold.
|
|
||||||
5. **Lossy conversion keeps the content.** `adfToPlainMarkdown` and `plainMarkdownToAdf` drop what
|
|
||||||
plain markdown cannot hold — format, design, structure — never content the document holds: what
|
|
||||||
a reader of the rendered document sees or follows, its text, images and link targets. The lossy
|
|
||||||
pair creates and exports; it never saves back over the document it read — a document's identity
|
|
||||||
(task, mention, media ids) survives a round trip only through the lossless pair.
|
|
||||||
6. **What happens is what the audience expects.** Where the goals leave a choice, a conversion
|
|
||||||
takes the one its audience would predict, reading the input as written.
|
|
||||||
7. **Failures are values.** Nothing throws, and `code` is a closed list — as much a contract as
|
|
||||||
the emitted formats are.
|
|
||||||
8. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on
|
|
||||||
any ES2022 engine, in a browser as readily as on a server, installed from public npm. The public
|
|
||||||
surface is the conversions, their types, `isAdfDocument`, and what a consumer needs to check a
|
|
||||||
guarantee this README makes; a helper is exported only when a persona cannot do without it.
|
|
||||||
9. **Correct before fast.** Each format means what its own specification says — markdown as the
|
|
||||||
CommonMark spec reads it, well-formed HTML as the HTML standard parses it — both in what this
|
|
||||||
library reads and in what a conforming parser reads from what it writes. A call takes a whole
|
|
||||||
document and returns a whole result.
|
|
||||||
10. **Fast once correct.** Conversion time grows linearly with the document wherever the goals
|
|
||||||
above allow it; a faster path that risks one of them is not taken.
|
|
||||||
11. **Source a contributor can hold.** Any one function reads in one sitting, and no change makes
|
|
||||||
the longest one longer.
|
|
||||||
|
|
||||||
## Audience
|
## Audience
|
||||||
|
|
||||||
Application developers embedding the library, addressed as personas rather than named consumers
|
Application developers embedding the library, in four personas. All four rely on the guarantees below and on `code` being a closed list; none may
|
||||||
(AGENTS.md §1). All four rely on the guarantees below and on `code` being a closed list; none may
|
|
||||||
rely on an error message's wording, which is free text.
|
rely on an error message's wording, which is free text.
|
||||||
|
|
||||||
- **Viewer/editor app** — shows a document, lets a human edit, posts it back. Relies on the
|
- **Viewer/editor app** — shows a document, lets a human edit, posts it back. Relies on the
|
||||||
@@ -94,7 +68,8 @@ if (result.ok) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Pure functions, no I/O, no configuration.
|
Serves Goals 1 and 7. Pure functions, each taking a whole document and returning a whole result;
|
||||||
|
no I/O, no configuration.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
adfToMarkdown(doc: AdfDocument): Result<string>
|
adfToMarkdown(doc: AdfDocument): Result<string>
|
||||||
@@ -114,9 +89,10 @@ htmlToMarkdown(html: string): Result<string> // 0.2.0, via ADF
|
|||||||
|
|
||||||
## Plain markdown
|
## Plain markdown
|
||||||
|
|
||||||
Plain markdown is a second flavour of the same grammar. `adfToPlainMarkdown` writes markdown other
|
Serves Goal 6. Plain markdown is a second flavour of the same grammar. `adfToPlainMarkdown` writes
|
||||||
tools render — GitHub, GitLab, Obsidian and the like — keeping the content and dropping the rest:
|
markdown other tools render — GitHub, GitLab, Obsidian and the like — keeping the content and
|
||||||
attributes, colours, layout, identity. It refuses only
|
dropping the rest: attributes, colours, layout, identity. Content is what a reader of the rendered
|
||||||
|
document sees or follows: its text, images and link targets. It refuses only
|
||||||
`not-an-adf-document`, `unsupported-document-version` and `unsupported-nesting-depth`, and writes
|
`not-an-adf-document`, `unsupported-document-version` and `unsupported-nesting-depth`, and writes
|
||||||
no directive.
|
no directive.
|
||||||
|
|
||||||
@@ -167,7 +143,7 @@ read replaces mentions, attachments and macros with text.
|
|||||||
|
|
||||||
## The errors
|
## The errors
|
||||||
|
|
||||||
An ADF node type this version does not know is not an error: the lossless pair carries it opaquely
|
Serves Goal 1. An ADF node type this version does not know is not an error: the lossless pair carries it opaquely
|
||||||
and restores it unchanged ([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
|
and restores it unchanged ([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
|
||||||
|
|
||||||
`ConvertError` is `{ code, message, path, position? }`. `code` is the exported `ConvertErrorCode`,
|
`ConvertError` is `{ code, message, path, position? }`. `code` is the exported `ConvertErrorCode`,
|
||||||
@@ -219,6 +195,11 @@ emit refuses:
|
|||||||
|
|
||||||
## The guarantees
|
## The guarantees
|
||||||
|
|
||||||
|
Serves Goals 1, 3 and 4.
|
||||||
|
|
||||||
|
- Markdown means what the CommonMark spec says, and well-formed HTML what the HTML standard
|
||||||
|
parses, both in what this library reads and in what a conforming parser reads from what it
|
||||||
|
writes; the bullets below name every exception.
|
||||||
- `markdownToAdf(adfToMarkdown(doc))` equals `doc` — unknown node types included, carried opaquely
|
- `markdownToAdf(adfToMarkdown(doc))` equals `doc` — unknown node types included, carried opaquely
|
||||||
([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
|
([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
|
||||||
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html`
|
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html`
|
||||||
@@ -247,7 +228,8 @@ emit refuses:
|
|||||||
input.
|
input.
|
||||||
- Past that and `~~`, no GFM: an autolink literal and a `- [ ]` marker stay text, and a checklist
|
- Past that and `~~`, no GFM: an autolink literal and a `- [ ]` marker stay text, and a checklist
|
||||||
is the `taskList` directive — `plainMarkdownToAdf` turns the marker into a `taskList`.
|
is the `taskList` directive — `plainMarkdownToAdf` turns the marker into a `taskList`.
|
||||||
- A document nested deeper than 500 levels is an error result, not a stack overflow.
|
- A document nested deeper than 500 levels is an error result, not a stack overflow, and no input
|
||||||
|
makes a call loop forever.
|
||||||
- The emitted formats are semver surface
|
- The emitted formats are semver surface
|
||||||
([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#the-formats-are-api)).
|
([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#the-formats-are-api)).
|
||||||
- **`0.2.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
|
- **`0.2.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
|
||||||
@@ -257,7 +239,7 @@ emit refuses:
|
|||||||
|
|
||||||
## The package
|
## The package
|
||||||
|
|
||||||
ESM only, no runtime dependencies, public npm. Built JavaScript with `.d.ts` beside it.
|
Serves Goal 7. ESM only, no runtime dependencies, public npm. Built JavaScript with `.d.ts` beside it.
|
||||||
Pure ECMAScript at an ES2022 baseline, reaching for no host API; the test suite runs under Node,
|
Pure ECMAScript at an ES2022 baseline, reaching for no host API; the test suite runs under Node,
|
||||||
Deno and Bun, and a headless Firefox converts the corpus through the built entrypoint.
|
Deno and Bun, and a headless Firefox converts the corpus through the built entrypoint.
|
||||||
Contract: [`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#any-es2022-engine), §Any
|
Contract: [`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#any-es2022-engine), §Any
|
||||||
|
|||||||
+36
-35
@@ -23,7 +23,7 @@ sanitized and a mention keeps the test user's real account id.
|
|||||||
|
|
||||||
## Markdown in is a canonical fixpoint
|
## Markdown in is a canonical fixpoint
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goals 1 and 3. Valid while markdown input may be written by hand.
|
2026-08-23, the maintainer. Goals 1 and 4. Valid while markdown input may be written by hand.
|
||||||
|
|
||||||
The other direction is a canonical fixpoint, not byte-identity: human markdown normalizes, the way
|
The other direction is a canonical fixpoint, not byte-identity: human markdown normalizes, the way
|
||||||
back yields the library's canonical spelling, and that spelling round-trips byte-identically —
|
back yields the library's canonical spelling, and that spelling round-trips byte-identically —
|
||||||
@@ -55,7 +55,7 @@ Where a container's own spelling cannot hold the child it has — a `bulletList`
|
|||||||
|
|
||||||
## Foreign HTML sorts three ways
|
## Foreign HTML sorts three ways
|
||||||
|
|
||||||
2026-08-23, the sort 2026-09-20, the maintainer. Goals 1, 3 and 7. Valid while ADF holds no node
|
2026-08-23, the sort 2026-09-20, the maintainer. Goals 1 and 4. Valid while ADF holds no node
|
||||||
for a bare container, a comment or a script. Lands with `todo.md` 6.
|
for a bare container, a comment or a script. Lands with `todo.md` 6.
|
||||||
|
|
||||||
Every foreign element `htmlToAdf` and `markdownToAdf` read sorts one of three ways, never a silent
|
Every foreign element `htmlToAdf` and `markdownToAdf` read sorts one of three ways, never a silent
|
||||||
@@ -76,39 +76,40 @@ than they buy.
|
|||||||
|
|
||||||
## Names stay text
|
## Names stay text
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 8. Valid while resolving a name to an id needs I/O.
|
2026-08-23, the maintainer. Goal 7. Valid while resolving a name to an id needs I/O.
|
||||||
|
|
||||||
A bare `@name` or `:smile:` in typed text stays a text node. Only directives produce
|
A bare `@name` or `:smile:` in typed text stays a text node. Only directives produce
|
||||||
mention/emoji/media nodes; resolving names to ids is the consumer's job.
|
mention/emoji/media nodes; resolving names to ids is the consumer's job.
|
||||||
|
|
||||||
## Directives under `!adf:`
|
## Directives under `!adf:`
|
||||||
|
|
||||||
2026-08-23, prefixed `!adf:` 2026-09-16, the maintainer. Goals 3 and 4. Valid while prose does not
|
2026-08-23, prefixed `!adf:` 2026-09-16, the maintainer. Goals 4 and 5. Valid while prose does not
|
||||||
write `!adf:`.
|
write `!adf:`.
|
||||||
|
|
||||||
Directives are one grammar for everything markdown lacks, namespaced under `!adf:`:
|
Directives are one grammar for everything markdown lacks, namespaced under `!adf:`:
|
||||||
`!adf:panel info` … `!adf:/panel` blocks, `!adf:mention[@Mikael]{id=5b10a2}` inline, `\!adf:` the
|
`!adf:panel info` … `!adf:/panel` blocks, `!adf:mention[@Mikael]{id=5b10a2}` inline, `\!adf:` the
|
||||||
one escape. Not CommonMark's generic-directives proposal: its `:::` claims a form prose writes, and
|
one escape. A node CommonMark can spell takes that spelling, never a directive. Not CommonMark's
|
||||||
its fence-length discipline ties a container's opener to its own body, where closing from the
|
generic-directives proposal: its `:::` claims a form prose writes, and its fence-length discipline
|
||||||
opener nests by itself and leaf versus container falls out of the node's content model.
|
ties a container's opener to its own body, where closing from the opener nests by itself and leaf
|
||||||
|
versus container falls out of the node's content model.
|
||||||
|
|
||||||
## CommonMark is a subset
|
## CommonMark is a subset
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 3. Valid while prose rarely writes the shapes the carve-outs claim.
|
2026-08-23, the maintainer. Goal 4. Valid while prose rarely writes the shapes the carve-outs claim.
|
||||||
|
|
||||||
Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a
|
Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a
|
||||||
directive, a pipe table or a `~~` pair is claimed — plus one image gap.
|
directive, a pipe table or a `~~` pair is claimed — plus one image gap.
|
||||||
|
|
||||||
## Tables
|
## Tables
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 4. Valid while a pipe table holds only one header row and inline
|
2026-08-23, the maintainer. Goal 5. Valid while a pipe table holds only one header row and inline
|
||||||
cells.
|
cells.
|
||||||
|
|
||||||
One header row plus plain inline cells → pipe table; anything richer → directive form.
|
One header row plus plain inline cells → pipe table; anything richer → directive form.
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
2026-09-13, nesting 2026-09-17, the maintainer. Goals 1 and 4. Valid while CommonMark's link
|
2026-09-13, nesting 2026-09-17, the maintainer. Goals 1 and 5. Valid while CommonMark's link
|
||||||
syntax is what readers edit.
|
syntax is what readers edit.
|
||||||
|
|
||||||
`[text](url "title")`, or `<url>` for a bare autolink-shaped text, wherever CommonMark spells the
|
`[text](url "title")`, or `<url>` for a bare autolink-shaped text, wherever CommonMark spells the
|
||||||
@@ -127,7 +128,7 @@ accepted.
|
|||||||
|
|
||||||
## Plain task ids come from position
|
## Plain task ids come from position
|
||||||
|
|
||||||
2026-09-26, spelling 2026-09-29, the maintainer. Goals 5 and 8. Valid while a site rejects a task
|
2026-09-26, spelling 2026-09-29, the maintainer. Goals 6 and 7. Valid while a site rejects a task
|
||||||
node with no `localId`.
|
node with no `localId`.
|
||||||
|
|
||||||
`plainMarkdownToAdf` gives each `taskList`, `taskItem` and `blockTaskItem` lacking one a `localId`
|
`plainMarkdownToAdf` gives each `taskList`, `taskItem` and `blockTaskItem` lacking one a `localId`
|
||||||
@@ -167,7 +168,7 @@ past `[x]`/`[ ]`, and lifting bare URLs, `@name`, `:shortcode:` or ISO dates int
|
|||||||
|
|
||||||
## The HTML dialect
|
## The HTML dialect
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goals 4 and 8. Valid while HTML output is read by consumers styling it
|
2026-08-23, the maintainer. Goals 5 and 7. Valid while HTML output is read by consumers styling it
|
||||||
themselves.
|
themselves.
|
||||||
|
|
||||||
The HTML dialect mirrors the markdown flavour: semantic elements, stable `adf-*` classes, `data-*`
|
The HTML dialect mirrors the markdown flavour: semantic elements, stable `adf-*` classes, `data-*`
|
||||||
@@ -175,7 +176,7 @@ for what HTML cannot express, text always escaped. No stylesheet ships.
|
|||||||
|
|
||||||
## No runtime dependencies
|
## No runtime dependencies
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 8. Valid while ~20 lines of own code, or a vendored table, do each
|
2026-08-23, the maintainer. Goal 7. Valid while ~20 lines of own code, or a vendored table, do each
|
||||||
job a dependency would.
|
job a dependency would.
|
||||||
|
|
||||||
`dependencies` is empty. A runtime dependency enters only through an entry here stating why ~20
|
`dependencies` is empty. A runtime dependency enters only through an entry here stating why ~20
|
||||||
@@ -185,7 +186,7 @@ and HTML parsers are written in this repo.
|
|||||||
## Standards ship as data
|
## Standards ship as data
|
||||||
|
|
||||||
2026-08-30, the CommonMark suite 2026-09-05 and ADF's schemas 2026-09-13, the maintainer. Goals 1,
|
2026-08-30, the CommonMark suite 2026-09-05 and ADF's schemas 2026-09-13, the maintainer. Goals 1,
|
||||||
3 and 8. Valid while each table is fixed data a dependency would only wrap.
|
4 and 7. Valid while each table is fixed data a dependency would only wrap.
|
||||||
|
|
||||||
A table a standard fixes is data rather than a dependency: HTML5's 2125 semicolon-terminated
|
A table a standard fixes is data rather than a dependency: HTML5's 2125 semicolon-terminated
|
||||||
character references ship packed in their own module, so entity decoding is complete without one.
|
character references ship packed in their own module, so entity decoding is complete without one.
|
||||||
@@ -208,7 +209,7 @@ nodes that break it.
|
|||||||
|
|
||||||
## Any ES2022 engine
|
## Any ES2022 engine
|
||||||
|
|
||||||
2026-09-01, the maintainer. Goal 8. Valid while ES2022 is the floor browsers and servers share.
|
2026-09-01, the maintainer. Goal 7. Valid while ES2022 is the floor browsers and servers share.
|
||||||
|
|
||||||
The library runs on any ES2022 engine, not only Node — a browser as readily as a server. The
|
The library runs on any ES2022 engine, not only Node — a browser as readily as a server. The
|
||||||
shipped source is ECMAScript and nothing else: no host import, no host global, no DOM.
|
shipped source is ECMAScript and nothing else: no host import, no host global, no DOM.
|
||||||
@@ -222,13 +223,13 @@ never the higher one those repo-only tools want.
|
|||||||
|
|
||||||
## ESM only
|
## ESM only
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 8. Valid while the audience's toolchains all import ES modules.
|
2026-08-23, the maintainer. Goal 7. Valid while the audience's toolchains all import ES modules.
|
||||||
|
|
||||||
No CommonJS build, no dual-package hazard.
|
No CommonJS build, no dual-package hazard.
|
||||||
|
|
||||||
## One built entrypoint
|
## One built entrypoint
|
||||||
|
|
||||||
2026-08-23, the maintainer. Goal 8. Valid while Node refuses to type-strip under `node_modules`.
|
2026-08-23, the maintainer. Goal 7. Valid while Node refuses to type-strip under `node_modules`.
|
||||||
|
|
||||||
Built JavaScript, `.d.ts` beside it. Do not add a TypeScript-source entrypoint — Node refuses to
|
Built JavaScript, `.d.ts` beside it. Do not add a TypeScript-source entrypoint — Node refuses to
|
||||||
type-strip under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so it cannot serve
|
type-strip under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), so it cannot serve
|
||||||
@@ -236,7 +237,7 @@ an npm consumer.
|
|||||||
|
|
||||||
## Public on npm
|
## Public on npm
|
||||||
|
|
||||||
2026-08-23, the name 2026-09-01, the maintainer. Goals 2 and 8. Valid while the package's source
|
2026-08-23, the name 2026-09-01, the maintainer. Goals 2 and 7. Valid while the package's source
|
||||||
stays public beside it.
|
stays public beside it.
|
||||||
|
|
||||||
Published to public npm as `@larvit/adf-codec`. Public source: the Gitea repo goes public,
|
Published to public npm as `@larvit/adf-codec`. Public source: the Gitea repo goes public,
|
||||||
@@ -245,7 +246,7 @@ for the hub rather than the formats around it.
|
|||||||
|
|
||||||
## The formats are API
|
## The formats are API
|
||||||
|
|
||||||
2026-08-23, strict input 2026-09-01, content models 2026-09-16, the maintainer. Goals 1 and 7.
|
2026-08-23, strict input 2026-09-01, content models 2026-09-16, the maintainer. Goal 1.
|
||||||
Valid while consumers store what the library emits.
|
Valid while consumers store what the library emits.
|
||||||
|
|
||||||
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
|
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
|
||||||
@@ -260,7 +261,7 @@ types in `src/result.ts` hold its shape.
|
|||||||
|
|
||||||
## The code list
|
## The code list
|
||||||
|
|
||||||
2026-08-25, the maintainer; dated below where a rule came later. Goal 7. Valid while a consumer
|
2026-08-25, the maintainer; dated below where a rule came later. Goal 1. Valid while a consumer
|
||||||
switches on `code` with no `default`.
|
switches on `code` with no `default`.
|
||||||
|
|
||||||
- Adding, removing or renaming a code is breaking, so a new cause takes an existing code whose
|
- Adding, removing or renaming a code is breaking, so a new cause takes an existing code whose
|
||||||
@@ -276,7 +277,7 @@ switches on `code` with no `default`.
|
|||||||
|
|
||||||
## Which code a cause takes
|
## Which code a cause takes
|
||||||
|
|
||||||
2026-08-28, the maintainer; dated below where a rule came later. Goal 7. Valid while a consumer
|
2026-08-28, the maintainer; dated below where a rule came later. Goal 1. Valid while a consumer
|
||||||
handles one cause alike whichever node, attribute or direction raised it.
|
handles one cause alike whichever node, attribute or direction raised it.
|
||||||
|
|
||||||
- A code names the cause; where one cause recurs across node types, across one mark's attributes
|
- A code names the cause; where one cause recurs across node types, across one mark's attributes
|
||||||
@@ -309,7 +310,7 @@ handles one cause alike whichever node, attribute or direction raised it.
|
|||||||
|
|
||||||
## `message` and `path`
|
## `message` and `path`
|
||||||
|
|
||||||
2026-09-03, the path 2026-09-23, the maintainer. Goals 3 and 7. Valid while a person fixing the
|
2026-09-03, the path 2026-09-23, the maintainer. Goals 1 and 4. Valid while a person fixing the
|
||||||
input reads `message`.
|
input reads `message`.
|
||||||
|
|
||||||
- A message names the violation, not the rule alone — a rule by itself states a truth the reader
|
- A message names the violation, not the rule alone — a rule by itself states a truth the reader
|
||||||
@@ -323,7 +324,7 @@ input reads `message`.
|
|||||||
|
|
||||||
## Publish on a version bump
|
## Publish on a version bump
|
||||||
|
|
||||||
2026-08-23, converging 2026-09-03, the maintainer. Goal 8. Valid while CI on `main` holds the npm
|
2026-08-23, converging 2026-09-03, the maintainer. Goal 7. Valid while CI on `main` holds the npm
|
||||||
token.
|
token.
|
||||||
|
|
||||||
`package.json` version on `main` is the source of truth. CI on `main`: tests green and the version
|
`package.json` version on `main` is the source of truth. CI on `main`: tests green and the version
|
||||||
@@ -339,7 +340,7 @@ depend on a store that the gate would then have to keep.
|
|||||||
|
|
||||||
## Docs describe the release being built
|
## Docs describe the release being built
|
||||||
|
|
||||||
2026-09-16, the maintainer. Goal 8. Valid while a bump on `main` publishes.
|
2026-09-16, the maintainer. Goal 7. Valid while a bump on `main` publishes.
|
||||||
|
|
||||||
Docs on `main` describe the release being built rather than the version npm holds, so they match it
|
Docs on `main` describe the release being built rather than the version npm holds, so they match it
|
||||||
the moment the bump publishes; add no interim note marking the gap.
|
the moment the bump publishes; add no interim note marking the gap.
|
||||||
@@ -355,7 +356,7 @@ nesting a spelling inside its own kind (`*(*a*)*`) names that mark once.
|
|||||||
|
|
||||||
## The gate runs on Deno and Bun
|
## The gate runs on Deno and Bun
|
||||||
|
|
||||||
2026-09-01, Deno's reason 2026-09-28, the maintainer. Goals 8 and 9. Valid while the library claims
|
2026-09-01, Deno's reason 2026-09-28, the maintainer. Goals 3 and 7. Valid while the library claims
|
||||||
any ES2022 engine.
|
any ES2022 engine.
|
||||||
|
|
||||||
The gate runs the suite under Deno and Bun as well as Node. Bun runs JavaScriptCore, the one engine
|
The gate runs the suite under Deno and Bun as well as Node. Bun runs JavaScriptCore, the one engine
|
||||||
@@ -367,7 +368,7 @@ over the corpus rather than over a smoke import.
|
|||||||
|
|
||||||
## The gate installs the tarball
|
## The gate installs the tarball
|
||||||
|
|
||||||
2026-09-03, the maintainer. Goal 8. Valid while consumers install the packed package.
|
2026-09-03, the maintainer. Goal 7. Valid while consumers install the packed package.
|
||||||
|
|
||||||
The gate packs the build and installs the tarball under `package-tests/`, so `files`, `exports`
|
The gate packs the build and installs the tarball under `package-tests/`, so `files`, `exports`
|
||||||
and `types` are proved on the artifact that ships rather than on the source tree a self-reference
|
and `types` are proved on the artifact that ships rather than on the source tree a self-reference
|
||||||
@@ -379,7 +380,7 @@ resolver maps them, under `NodeNext` alone; a `.d.ts` reader that is not `tsc` s
|
|||||||
|
|
||||||
## Firefox reads the build
|
## Firefox reads the build
|
||||||
|
|
||||||
2026-09-04, the maintainer. Goal 8. Valid while the library claims a browser and no other leg runs
|
2026-09-04, the maintainer. Goal 7. Valid while the library claims a browser and no other leg runs
|
||||||
SpiderMonkey.
|
SpiderMonkey.
|
||||||
|
|
||||||
A headless Firefox loads `dist/index.js` over HTTP and converts the round-trip, normalization and
|
A headless Firefox loads `dist/index.js` over HTTP and converts the round-trip, normalization and
|
||||||
@@ -407,8 +408,8 @@ compared against `undefined` — have a half no valid document reaches.
|
|||||||
|
|
||||||
## The size ratchet
|
## The size ratchet
|
||||||
|
|
||||||
2026-09-20, the maintainer. Goal 11. Valid while no measure picks out what readers find hard
|
2026-09-20, the maintainer. KISS, a technical principle. Valid while no measure picks out what
|
||||||
better than a function's length.
|
readers find hard better than a function's length.
|
||||||
|
|
||||||
`.oxlintrc.json`'s single rule, over the files `tsconfig.build.json` builds, is a per-function line
|
`.oxlintrc.json`'s single rule, over the files `tsconfig.build.json` builds, is a per-function line
|
||||||
ceiling, set at that set's worst and moving only downward. It covers the built files alone, since
|
ceiling, set at that set's worst and moving only downward. It covers the built files alone, since
|
||||||
@@ -431,7 +432,7 @@ markdown, on a fixed seed in the gate; a counterexample found becomes a round-tr
|
|||||||
|
|
||||||
## The CommonMark suite checks three ways
|
## The CommonMark suite checks three ways
|
||||||
|
|
||||||
2026-08-27, the maintainer. Goals 1 and 9. Valid while the suite's answers are HTML ADF cannot be
|
2026-08-27, the maintainer. Goals 1 and 3. Valid while the suite's answers are HTML ADF cannot be
|
||||||
compared against.
|
compared against.
|
||||||
|
|
||||||
Each example is a named error or markdown that parses and emits to itself byte for byte; its
|
Each example is a named error or markdown that parses and emits to itself byte for byte; its
|
||||||
@@ -494,7 +495,7 @@ termination is the loop's own check.
|
|||||||
|
|
||||||
## Readers scan by index
|
## Readers scan by index
|
||||||
|
|
||||||
2026-08-30, the kept scan 2026-09-18, the maintainer. Goal 10. Valid while the pipeline persona
|
2026-08-30, the kept scan 2026-09-18, the maintainer. Goal 8. Valid while the pipeline persona
|
||||||
feeds documents nobody typed.
|
feeds documents nobody typed.
|
||||||
|
|
||||||
A reader takes the text and an index — a sticky regex whose `lastIndex` the caller sets on the line
|
A reader takes the text and an index — a sticky regex whose `lastIndex` the caller sets on the line
|
||||||
@@ -506,7 +507,7 @@ two cannot disagree — which is what makes the kept value a memo rather than a
|
|||||||
|
|
||||||
## The spelling memo
|
## The spelling memo
|
||||||
|
|
||||||
2026-09-19, the maintainer. Goal 10. Valid while the `commonMarkSpelling` ask spells a node once
|
2026-09-19, the maintainer. Goal 8. Valid while the `commonMarkSpelling` ask spells a node once
|
||||||
per level above it otherwise.
|
per level above it otherwise.
|
||||||
|
|
||||||
The parse and the plain reduction keep each node's readable spelling in a memo, so the
|
The parse and the plain reduction keep each node's readable spelling in a memo, so the
|
||||||
@@ -518,7 +519,7 @@ one. Only what succeeded is kept, so no path minted at another position is ever
|
|||||||
|
|
||||||
## Cost fixes are measured, never timed
|
## Cost fixes are measured, never timed
|
||||||
|
|
||||||
2026-09-18, the maintainer and the stability-reviewer. Goal 10. Valid while Goal 10 promises growth
|
2026-09-18, the maintainer and the stability-reviewer. Goal 8. Valid while Goal 8 promises growth
|
||||||
rather than a figure.
|
rather than a figure.
|
||||||
|
|
||||||
A cost fix that changes no behaviour lands on the suite staying green with no fixture output
|
A cost fix that changes no behaviour lands on the suite staying green with no fixture output
|
||||||
@@ -537,7 +538,7 @@ refuses it — which is how the whitespace carry finds a line edge.
|
|||||||
|
|
||||||
## Emphasis follows CommonMark's matching
|
## Emphasis follows CommonMark's matching
|
||||||
|
|
||||||
2026-08-27, the maintainer. Goals 1 and 9. Valid while CommonMark's emphasis rules are the
|
2026-08-27, the maintainer. Goals 1 and 3. Valid while CommonMark's emphasis rules are the
|
||||||
reader's.
|
reader's.
|
||||||
|
|
||||||
Emphasis is spelled against CommonMark's matching, never flanking alone: a delimiter run in text
|
Emphasis is spelled against CommonMark's matching, never flanking alone: a delimiter run in text
|
||||||
@@ -546,7 +547,7 @@ only ones in play, and a pair that matching hands to another delimiter rides the
|
|||||||
|
|
||||||
## Readable spellings take the `try` prefix
|
## Readable spellings take the `try` prefix
|
||||||
|
|
||||||
2026-09-21, the maintainer. Goals 1 and 4. Valid while a readable spelling's refusal would cost a
|
2026-09-21, the maintainer. Goals 1 and 5. Valid while a readable spelling's refusal would cost a
|
||||||
document the general form spells.
|
document the general form spells.
|
||||||
|
|
||||||
A readable spelling tried ahead of a general one takes the `try` prefix and fails only where the
|
A readable spelling tried ahead of a general one takes the `try` prefix and fails only where the
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Export only the conversions, their types, isAdfDocument and what checking a README guarantee needs.
|
||||||
export type { AdfAttributes, AdfDocument, AdfMark, AdfNode } from './adf/document.ts'
|
export type { AdfAttributes, AdfDocument, AdfMark, AdfNode } from './adf/document.ts'
|
||||||
export type { ConvertError, ConvertErrorCode, ConvertErrorPath, ParseError, Result, SourcePosition } from './result.ts'
|
export type { ConvertError, ConvertErrorCode, ConvertErrorPath, ParseError, Result, SourcePosition } from './result.ts'
|
||||||
export type { JsonValue } from './json-value.ts'
|
export type { JsonValue } from './json-value.ts'
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
|
- **43 — Give each markdown input its own reader, strict to its own standard.** Today
|
||||||
|
`markdownToAdf` reads CommonMark and the lossless flavour as one input, so text CommonMark reads
|
||||||
|
one way — shaped like a directive, a pipe table or a `~~` pair — the flavour claims (Goals 3 and
|
||||||
|
4). A caller names the markdown it hands in: CommonMark, read as its spec says, or the lossless
|
||||||
|
flavour, read as `spec/flavour.md` says. Breaking: `MIGRATION.md` says which call a caller takes.
|
||||||
- **40 — Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document it takes.**
|
- **40 — Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document it takes.**
|
||||||
Today it holds for editor-normal documents only: two adjacent text nodes with the same marks
|
Today it holds for editor-normal documents only: two adjacent text nodes with the same marks
|
||||||
merge, an empty `attrs`, `marks` or `content` drops, and `-0` reads back `0` — shapes pipelines
|
merge, an empty `attrs`, `marks` or `content` drops, and `-0` reads back `0` — shapes pipelines
|
||||||
@@ -26,11 +31,11 @@
|
|||||||
measured one.
|
measured one.
|
||||||
- **33 — Make a carried mark run cost the line one re-emit.** `adfToMarkdown` spends 23 s on one
|
- **33 — Make a carried mark run cost the line one re-emit.** `adfToMarkdown` spends 23 s on one
|
||||||
paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the whole line
|
paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the whole line
|
||||||
before riding the carry, quadratic in the runs (Goal 10), and the plain reduction's
|
before riding the carry, quadratic in the runs (Goal 8), and the plain reduction's
|
||||||
`spellableLine` drops one mark per re-emit the same way. Make both linear.
|
`spellableLine` drops one mark per re-emit the same way. Make both linear.
|
||||||
- **42 — Trim a text leaf's trailing blanks in linear time.** `plain-inline.ts`'s `leafEdges` finds
|
- **42 — Trim a text leaf's trailing blanks in linear time.** `plain-inline.ts`'s `leafEdges` finds
|
||||||
the trail with an unanchored `/[ \t]*$/`, quadratic in a run of blanks inside one leaf: a
|
the trail with an unanchored `/[ \t]*$/`, quadratic in a run of blanks inside one leaf: a
|
||||||
paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in `adfToPlainMarkdown` (Goal 10). Scan backward,
|
paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in `adfToPlainMarkdown` (Goal 8). Scan backward,
|
||||||
as the expand title's trim does.
|
as the expand title's trim does.
|
||||||
- **34 — Read emphasis flanking by the whole character beside an astral symbol.** Check whether
|
- **34 — Read emphasis flanking by the whole character beside an astral symbol.** Check whether
|
||||||
`line-escaping.ts`'s `charAt` and the parser's flanking read one UTF-16 unit beside an astral
|
`line-escaping.ts`'s `charAt` and the parser's flanking read one UTF-16 unit beside an astral
|
||||||
@@ -72,6 +77,10 @@
|
|||||||
Revisit: whether npm has added Gitea or self-hosted OIDC, and otherwise whether the release moves
|
Revisit: whether npm has added Gitea or self-hosted OIDC, and otherwise whether the release moves
|
||||||
to the staged publish — which fits badly with publish-on-merge, and is the trade to weigh rather
|
to the staged publish — which fits badly with publish-on-merge, and is the trade to weigh rather
|
||||||
than discover on a red release run. It stays out of `0.2.0` knowing the cutoff may land first.
|
than discover on a red release run. It stays out of `0.2.0` knowing the cutoff may land first.
|
||||||
- **8 — Ship a CLI, shaped around the personas.**
|
|
||||||
- **9 — Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on
|
- **9 — Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on
|
||||||
the library's browser build.**
|
the library's browser build.**
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
|
- **8 — Ship a CLI.** Its goal and its persona land in the README's `## Goals` and `## Audience`
|
||||||
|
with it.
|
||||||
|
|||||||
Reference in New Issue
Block a user