@jarenjs/md 0.49.2 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -365,7 +365,7 @@ an editable data pane.
365
365
  ### Directives — a number a machine derives and a human reads
366
366
 
367
367
  ```markdown
368
- Jaren is <!--bm:jsonpath.ctsRatio-->8.8<!--/bm-->x faster across the CTS queries.
368
+ Jaren is <!--fact:jsonpath.ctsRatio-->8.8<!--/fact-->x faster across the CTS queries.
369
369
  ```
370
370
 
371
371
  Every markdown renderer on earth drops HTML comments, so GitHub, an editor
@@ -389,7 +389,7 @@ being true. It splices only the spans between markers — a document with
389
389
  no directives comes back byte-identical — because `toMarkdown` is a
390
390
  canonicalizing printer and re-printing a hand-written README would reflow
391
391
  every list for no reason. This repository's own figures work exactly this
392
- way (`npm run docs:benchmarks`, `npm run docs:check`).
392
+ way (`npm run docs:derive`, `npm run docs:check`).
393
393
 
394
394
  The layer never interprets the payload: `bm` puts a derivation key there,
395
395
  `mdx` puts a query expression, and the vocabulary belongs to the
@@ -451,25 +451,25 @@ active policy ([PLUGINS.md](docs/PLUGINS.md) §5, [MD-FORMAT.md](docs/MD-FORMAT.
451
451
 
452
452
  ## Performance contract
453
453
 
454
- Measured, not claimed — `npm run benchmark:markdown`, <!--bm:md.measured-->2026-08-11, Node v24.19.0<!--/bm-->
454
+ Measured, not claimed — `npm run benchmark:markdown`, <!--fact:md.measured-->2026-08-11, Node v24.19.0<!--/fact-->
455
455
  (run it yourself; micro-timings vary ±15%):
456
456
 
457
- - **Parse to AST**: <!--bm:md.parseTimes-->~0.099 ms for a typical ~2 kB document, ~0.51 ms for ~10 kB, ~4.8 ms for ~100 kB<!--/bm--> — linear in input. A CPU profile puts the
457
+ - **Parse to AST**: <!--fact:md.parseTimes-->~0.099 ms for a typical ~2 kB document, ~0.51 ms for ~10 kB, ~4.8 ms for ~100 kB<!--/fact--> — linear in input. A CPU profile puts the
458
458
  inline phase at ~36% of that and the source hash for `meta.hash` at
459
459
  ~10%; the block scan, the obvious suspect, is ~14%. (Replacing one
460
460
  `/\s+$/` regex at paragraph close with a scan was worth 4–17%
461
461
  depending on how paragraph-dense the document is — measured as an A/B
462
462
  on this corpus, because the same change looked like noise on a
463
463
  differently shaped one.)
464
- - **Parse + render to HTML** (the cross-engine row, `toHtml`): takes <!--bm:md.vsPeers-->0.6–1.1<!--/bm-->x the time `marked` and `markdown-it` take — <!--bm:md.vsPeersDetail-->faster than both at every size measured except one — `markdown-it` is ahead at ~10 kB (1.1x)<!--/bm--> — and is <!--bm:md.vsMicromark-->12.9–21.4<!--/bm-->x faster than `micromark`, on the same GFM documents.
464
+ - **Parse + render to HTML** (the cross-engine row, `toHtml`): takes <!--fact:md.vsPeers-->0.6–1.1<!--/fact-->x the time `marked` and `markdown-it` take — <!--fact:md.vsPeersDetail-->faster than both at every size measured except one — `markdown-it` is ahead at ~10 kB (1.1x)<!--/fact--> — and is <!--fact:md.vsMicromark-->12.9–21.4<!--/fact-->x faster than `micromark`, on the same GFM documents.
465
465
  Through the **vnode** path the same documents cost roughly twice that
466
466
  — keys, memoization and a tree the patcher can reconcile are not free,
467
467
  and the benchmark publishes that row beside this one rather than
468
468
  quoting only the flattering half.
469
469
  - **Where the time goes** (~100 kB, the phase split the benchmark now
470
- prints and publishes): <!--bm:md.phaseSplit-->parse 35%, AST→vnode 50%, vnode→HTML 16%<!--/bm-->. The projection, not
471
- the parse, is the expensive half — and <!--bm:md.keyCost-->53%<!--/bm--> of the projection is
472
- computing the content-hash **keys** (<!--bm:md.unkeyedMs-->3.3 ms against 6.9 ms<!--/bm--> without them).
470
+ prints and publishes): <!--fact:md.phaseSplit-->parse 35%, AST→vnode 50%, vnode→HTML 16%<!--/fact-->. The projection, not
471
+ the parse, is the expensive half — and <!--fact:md.keyCost-->53%<!--/fact--> of the projection is
472
+ computing the content-hash **keys** (<!--fact:md.unkeyedMs-->3.3 ms against 6.9 ms<!--/fact--> without them).
473
473
  Keys are what let the patcher reorder blocks instead of rebuilding
474
474
  them, so they are worth it for a tree that will be patched — and worth
475
475
  nothing to a caller that renders once and throws the tree away. That
@@ -483,7 +483,7 @@ Measured, not claimed — `npm run benchmark:markdown`, <!--bm:md.measured-->202
483
483
  output will be patched, and guessing wrong turns O(1) reconciliation
484
484
  into a rebuild with no error to show for it.
485
485
  - **The compiled fast path**: `compileMarkdown(...).toVnode()` returns
486
- the cached projection in <!--bm:md.cachedNs-->39–82<!--/bm--> ns — and because block vnodes carry
486
+ the cached projection in <!--fact:md.cachedNs-->39–82<!--/fact--> ns — and because block vnodes carry
487
487
  content-hash keys and unchanged AST nodes emit reference-equal
488
488
  vnodes, the view patcher skips unchanged blocks in O(1). A JSLT
489
489
  identity transform returns the document by reference; a partial
@@ -491,12 +491,12 @@ Measured, not claimed — `npm run benchmark:markdown`, <!--bm:md.measured-->202
491
491
  the one-shot HTML render — is what this package is optimized for.
492
492
  - **CommonMark scorecard**, both paths, because the difference between
493
493
  them *is* the safety boundary:
494
- - `toHtml` (`html: 'raw'`, the like-for-like row): <!--bm:md.scorecard-->655 of 655 (100.0%)<!--/bm-->; for scale, <!--bm:md.scorecardPeers-->marked 620, markdown-it 655, micromark 650<!--/bm-->.
494
+ - `toHtml` (`html: 'raw'`, the like-for-like row): <!--fact:md.scorecard-->655 of 655 (100.0%)<!--/fact-->; for scale, <!--fact:md.scorecardPeers-->marked 620, markdown-it 655, micromark 650<!--/fact-->.
495
495
  **No dialect gap remains on this path**: every example the spec
496
496
  contains passes, and the round-trip suite additionally asserts that
497
497
  all 655 survive `parseMarkdown → toMarkdown → parseMarkdown` with an
498
498
  identical AST and an unchanged canonical form.
499
- - `mdToVnode` + SSR: <!--bm:md.scorecardVnode-->593 of 655 (90.5%)<!--/bm-->. **Every** example the two
499
+ - `mdToVnode` + SSR: <!--fact:md.scorecardVnode-->593 of 655 (90.5%)<!--/fact-->. **Every** example the two
500
500
  paths disagree on contains raw HTML — asserted, not asserted-at:
501
501
  [`test/md/to-html.test.js`](../../test/md/to-html.test.js) checks that no
502
502
  vnode-path failure is free of an `html` node. The spec renders raw
@@ -508,9 +508,9 @@ Measured, not claimed — `npm run benchmark:markdown`, <!--bm:md.measured-->202
508
508
  - **GFM extension scorecard**, the five extension sections of the GFM
509
509
  specification with every engine's extensions switched on — because the
510
510
  CommonMark corpus says nothing about any of them, and the part of the
511
- dialect every engine advertises was the only part nobody measured: <!--bm:md.gfmScorecard-->22 of 24 (91.7%)<!--/bm--> through `toHtml`, and <!--bm:md.gfmScorecardVnode-->22 of 24 (91.7%)<!--/bm--> through the vnode
512
- path; for scale, <!--bm:md.gfmPeers-->marked 22, markdown-it 14, micromark 23<!--/bm-->.
513
- Autolink literals are <!--bm:md.gfmAutolinks-->11 of 11<!--/bm-->, ahead of every rival here. The two
511
+ dialect every engine advertises was the only part nobody measured: <!--fact:md.gfmScorecard-->22 of 24 (91.7%)<!--/fact--> through `toHtml`, and <!--fact:md.gfmScorecardVnode-->22 of 24 (91.7%)<!--/fact--> through the vnode
512
+ path; for scale, <!--fact:md.gfmPeers-->marked 22, markdown-it 14, micromark 23<!--/fact-->.
513
+ Autolink literals are <!--fact:md.gfmAutolinks-->11 of 11<!--/fact-->, ahead of every rival here. The two
514
514
  this package does not pass are **stated boundaries, not to-do items**:
515
515
  - **table alignment is written as `style="text-align:center"`, not the
516
516
  deprecated `align` attribute** (1 example). Both render identically;
@@ -3,7 +3,7 @@
3
3
  * human reads.
4
4
  *
5
5
  * ```markdown
6
- * Jaren is <!--bm:jsonpath.ctsRatio-->23.1<!--/bm-->x faster on the CTS mean.
6
+ * Jaren is <!--fact:jsonpath.ctsRatio-->23.1<!--/fact-->x faster on the CTS mean.
7
7
  * ```
8
8
  *
9
9
  * Every markdown renderer on earth drops HTML comments, so GitHub, an
@@ -18,11 +18,13 @@
18
18
  * as literal gibberish anywhere the transform has not run — usable only
19
19
  * in documents nobody reads raw.
20
20
  *
21
- * **The layer never interprets the payload.** `bm` puts a fact key
22
- * there, `mdx` puts a query expression; the vocabulary belongs to the
21
+ * **The layer never interprets the payload.** `fact` puts a derivation
22
+ * key there, `mdx` puts a query expression; the vocabulary belongs to the
23
23
  * consumer, and this module owns exactly one thing — the marker grammar
24
24
  * and the pairing — so two consumers cannot disagree about what a
25
- * directive is.
25
+ * directive is. The namespace is the consumer's too, and one consumer
26
+ * SHOULD claim one: pairing and the orphan report are per-namespace, so a
27
+ * repository that spells the same idea two ways has two blind spots.
26
28
  *
27
29
  * There are two ways in, because there are two questions:
28
30
  *
package/docs/MD-FORMAT.md CHANGED
@@ -411,7 +411,7 @@ A **directive** is a value in a document that a machine derives and a
411
411
  human reads:
412
412
 
413
413
  ```markdown
414
- Jaren is <!--bm:jsonpath.ctsRatio-->23.1<!--/bm-->x faster on the CTS mean.
414
+ Jaren is <!--fact:jsonpath.ctsRatio-->23.1<!--/fact-->x faster on the CTS mean.
415
415
  ```
416
416
 
417
417
  The carrier is an HTML comment, and the choice is the whole design:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/md",
3
3
  "private": false,
4
- "version": "0.49.2",
4
+ "version": "0.56.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -73,8 +73,8 @@
73
73
  "prepack": "npm run build:types"
74
74
  },
75
75
  "dependencies": {
76
- "@jarenjs/core": "^0.49.2",
77
- "@jarenjs/mermaid": "^0.49.2",
78
- "@jarenjs/view": "^0.49.2"
76
+ "@jarenjs/core": "^0.56.0",
77
+ "@jarenjs/mermaid": "^0.56.0",
78
+ "@jarenjs/view": "^0.56.0"
79
79
  }
80
80
  }
package/src/directives.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * human reads.
5
5
  *
6
6
  * ```markdown
7
- * Jaren is <!--bm:jsonpath.ctsRatio-->23.1<!--/bm-->x faster on the CTS mean.
7
+ * Jaren is <!--fact:jsonpath.ctsRatio-->23.1<!--/fact-->x faster on the CTS mean.
8
8
  * ```
9
9
  *
10
10
  * Every markdown renderer on earth drops HTML comments, so GitHub, an
@@ -19,11 +19,13 @@
19
19
  * as literal gibberish anywhere the transform has not run — usable only
20
20
  * in documents nobody reads raw.
21
21
  *
22
- * **The layer never interprets the payload.** `bm` puts a fact key
23
- * there, `mdx` puts a query expression; the vocabulary belongs to the
22
+ * **The layer never interprets the payload.** `fact` puts a derivation
23
+ * key there, `mdx` puts a query expression; the vocabulary belongs to the
24
24
  * consumer, and this module owns exactly one thing — the marker grammar
25
25
  * and the pairing — so two consumers cannot disagree about what a
26
- * directive is.
26
+ * directive is. The namespace is the consumer's too, and one consumer
27
+ * SHOULD claim one: pairing and the orphan report are per-namespace, so a
28
+ * repository that spells the same idea two ways has two blind spots.
27
29
  *
28
30
  * There are two ways in, because there are two questions:
29
31
  *