aontu 0.55.0 → 0.57.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.
Files changed (128) hide show
  1. package/README.md +3 -3
  2. package/dist/agentsmd.d.ts +1 -0
  3. package/dist/agentsmd.js +10 -3
  4. package/dist/agentsmd.js.map +1 -1
  5. package/dist/aontu.d.ts +4 -2
  6. package/dist/aontu.js +5 -2
  7. package/dist/aontu.js.map +1 -1
  8. package/dist/cli.d.ts +10 -3
  9. package/dist/cli.js +311 -53
  10. package/dist/cli.js.map +1 -1
  11. package/dist/diff.d.ts +1 -0
  12. package/dist/diff.js +3 -2
  13. package/dist/diff.js.map +1 -1
  14. package/dist/escape.d.ts +5 -0
  15. package/dist/escape.js +455 -0
  16. package/dist/escape.js.map +1 -0
  17. package/dist/format.d.ts +26 -0
  18. package/dist/format.js +1489 -0
  19. package/dist/format.js.map +1 -0
  20. package/dist/hints.js +39 -1
  21. package/dist/hints.js.map +1 -1
  22. package/dist/jsonschema.d.ts +1 -0
  23. package/dist/jsonschema.js +3 -2
  24. package/dist/jsonschema.js.map +1 -1
  25. package/dist/lang.js +84 -6
  26. package/dist/lang.js.map +1 -1
  27. package/dist/lsp.d.ts +2 -1
  28. package/dist/lsp.js +4 -4
  29. package/dist/lsp.js.map +1 -1
  30. package/dist/mcp-server.js +2 -2
  31. package/dist/mcp-server.js.map +1 -1
  32. package/dist/mcp.js +6 -4
  33. package/dist/mcp.js.map +1 -1
  34. package/dist/mod-tool.js +8 -7
  35. package/dist/mod-tool.js.map +1 -1
  36. package/dist/mod.js +6 -6
  37. package/dist/mod.js.map +1 -1
  38. package/dist/patch.d.ts +1 -0
  39. package/dist/patch.js +1 -0
  40. package/dist/patch.js.map +1 -1
  41. package/dist/query.d.ts +1 -0
  42. package/dist/query.js +4 -3
  43. package/dist/query.js.map +1 -1
  44. package/dist/reach.d.ts +1 -0
  45. package/dist/reach.js +3 -2
  46. package/dist/reach.js.map +1 -1
  47. package/dist/relation.d.ts +1 -0
  48. package/dist/relation.js +3 -2
  49. package/dist/relation.js.map +1 -1
  50. package/dist/sigdecl.js +1 -1
  51. package/dist/sigdecl.js.map +1 -1
  52. package/dist/std.js +2 -1
  53. package/dist/std.js.map +1 -1
  54. package/dist/subsume.d.ts +1 -0
  55. package/dist/subsume.js +3 -2
  56. package/dist/subsume.js.map +1 -1
  57. package/dist/trim.d.ts +1 -0
  58. package/dist/trim.js +3 -2
  59. package/dist/trim.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/dist/type.d.ts +1 -0
  62. package/dist/type.js.map +1 -1
  63. package/dist/utility.d.ts +8 -2
  64. package/dist/utility.js +9 -1
  65. package/dist/utility.js.map +1 -1
  66. package/dist/val/EachFuncVal.js +1 -2
  67. package/dist/val/EachFuncVal.js.map +1 -1
  68. package/dist/val/EmitFuncVal.d.ts +23 -0
  69. package/dist/val/EmitFuncVal.js +261 -0
  70. package/dist/val/EmitFuncVal.js.map +1 -0
  71. package/dist/val/FilterFuncVal.js +1 -2
  72. package/dist/val/FilterFuncVal.js.map +1 -1
  73. package/dist/val/FuncBaseVal.d.ts +1 -0
  74. package/dist/val/FuncBaseVal.js +16 -0
  75. package/dist/val/FuncBaseVal.js.map +1 -1
  76. package/dist/val/PackFuncVal.js +1 -2
  77. package/dist/val/PackFuncVal.js.map +1 -1
  78. package/dist/val/PlaceVal.d.ts +3 -1
  79. package/dist/val/PlaceVal.js +8 -6
  80. package/dist/val/PlaceVal.js.map +1 -1
  81. package/dist/val/StrFuncVal.d.ts +32 -0
  82. package/dist/val/StrFuncVal.js +292 -0
  83. package/dist/val/StrFuncVal.js.map +1 -0
  84. package/dist/vet.d.ts +1 -0
  85. package/dist/vet.js +1 -1
  86. package/dist/vet.js.map +1 -1
  87. package/dist/view.d.ts +2 -1
  88. package/dist/view.js +379 -8
  89. package/dist/view.js.map +1 -1
  90. package/grammar/aontu.abnf +156 -0
  91. package/grammar/aontu.gbnf +4 -3
  92. package/grammar/aontu.lark +4 -3
  93. package/grammar/aontu.tmLanguage.json +1 -1
  94. package/package.json +2 -1
  95. package/skill/grammar-card.md +5 -2
  96. package/src/agentsmd.ts +15 -3
  97. package/src/aontu.ts +8 -1
  98. package/src/cli.ts +365 -58
  99. package/src/diff.ts +7 -2
  100. package/src/escape.ts +371 -0
  101. package/src/format.ts +1728 -0
  102. package/src/hints.ts +53 -1
  103. package/src/jsonschema.ts +7 -1
  104. package/src/lang.ts +93 -6
  105. package/src/lsp.ts +8 -6
  106. package/src/mcp-server.ts +3 -2
  107. package/src/mcp.ts +6 -4
  108. package/src/mod-tool.ts +9 -8
  109. package/src/mod.ts +6 -6
  110. package/src/patch.ts +7 -0
  111. package/src/query.ts +8 -4
  112. package/src/reach.ts +7 -2
  113. package/src/relation.ts +7 -2
  114. package/src/sigdecl.ts +1 -1
  115. package/src/std.ts +2 -1
  116. package/src/subsume.ts +7 -2
  117. package/src/trim.ts +7 -2
  118. package/src/type.ts +8 -0
  119. package/src/utility.ts +27 -2
  120. package/src/val/EachFuncVal.ts +1 -3
  121. package/src/val/EmitFuncVal.ts +401 -0
  122. package/src/val/FilterFuncVal.ts +1 -3
  123. package/src/val/FuncBaseVal.ts +18 -0
  124. package/src/val/PackFuncVal.ts +1 -3
  125. package/src/val/PlaceVal.ts +8 -6
  126. package/src/val/StrFuncVal.ts +334 -0
  127. package/src/vet.ts +9 -3
  128. package/src/view.ts +442 -9
package/dist/view.js CHANGED
@@ -7,8 +7,11 @@ exports.viewTree = viewTree;
7
7
  exports.viewSet = viewSet;
8
8
  // THE VIEWS (docs/design/VIEWS.0.md and VIEWS-ORDER.0.md): figures of
9
9
  // an evaluated document, drawn as deterministic text a golden diff can
10
- // check. Seven kinds:
10
+ // check. Nine kinds:
11
11
  //
12
+ // doc the shape of the document itself
13
+ // lattice the language's value lattice, with the document's own
14
+ // values placed on it
12
15
  // tree the dependency tree of one relation
13
16
  // matrix the dependency matrix over one relation, in canon or
14
17
  // partition order, with closure and the unmirrored mark
@@ -50,6 +53,7 @@ const keyorder_1 = require("./keyorder");
50
53
  const provenance_1 = require("./provenance");
51
54
  const query_1 = require("./query");
52
55
  const subsume_1 = require("./subsume");
56
+ const utility_1 = require("./utility");
53
57
  // The text profile's mechanism: the eight named colours, `bold` and
54
58
  // `dim`, and nothing else. `label` is unstyled -- an entity's own name
55
59
  // is the figure's content, not a mark about it.
@@ -71,6 +75,7 @@ const styleOf = (style, as) => style ?? ('svg' === as ? 'css' : 'none');
71
75
  // node-link drawing and no sensible Mermaid form of a matrix.
72
76
  const PROFILES = {
73
77
  doc: ['text', 'svg'],
78
+ lattice: ['text', 'svg'],
74
79
  tree: ['text', 'svg'],
75
80
  matrix: ['text', 'svg'],
76
81
  graph: ['mermaid', 'dot', 'er'],
@@ -593,6 +598,368 @@ function treeSvg(rows, about, style) {
593
598
  // drawn and the row says how many keys were not drawn, because a tree
594
599
  // that stops without saying so is the one thing a structural drawing
595
600
  // must not be.
601
+ // ---------------------------------------------------------------------
602
+ // THE VALUE LATTICE, and where this document's values sit on it.
603
+ //
604
+ // THE SCAFFOLD IS THE LANGUAGE'S, NOT THE DOCUMENT'S: `top` at the
605
+ // join, the four kind families under it, `path()` under `string`, the
606
+ // four numeric leaves under `number`, and `nil` at the meet. Every
607
+ // Aontu document is drawn against the SAME shape, which is what makes
608
+ // two of these figures comparable -- and what makes this a view of the
609
+ // language that a document annotates, rather than a picture assembled
610
+ // out of whatever the document happened to contain.
611
+ //
612
+ // See docs/unification.md for what the ordering means.
613
+ // The scaffold: each kind and the one above it. The ENGINE decides
614
+ // which kind sits under which -- kindParent in ts/src/val/ScalarKindVal.ts,
615
+ // and its twin in go/scalar.go -- and a test in each port holds this
616
+ // table to it, so adding a kind to the engine makes the figure grow a
617
+ // node rather than quietly leave one out.
618
+ const LATTICE_PARENT = [
619
+ ['string', 'top'],
620
+ ['path()', 'string'],
621
+ ['number', 'top'],
622
+ ['integer', 'number'],
623
+ ['float', 'number'],
624
+ ['biginteger', 'number'],
625
+ ['bigdecimal', 'number'],
626
+ ['boolean', 'top'],
627
+ ['null', 'top'],
628
+ ];
629
+ // The columns, left to right: the MINIMAL kinds, the ones with nothing
630
+ // under them. Everything else is drawn centred over the columns it
631
+ // covers, so this list alone fixes the figure's horizontal order -- and
632
+ // it puts the kinds that reach the bottom from higher up (`boolean`,
633
+ // `null`) on the outside, where their lines pass the numeric fan
634
+ // rather than crossing it.
635
+ const LATTICE_COLS = ['path()', 'integer', 'float', 'biginteger', 'bigdecimal', 'boolean',
636
+ 'null'];
637
+ // The rows, top to bottom. `top` and `nil` are the endpoints and are
638
+ // not kinds: no `superior()` answers either, and no entry above names
639
+ // them as a parent.
640
+ const LATTICE_ROWS = [
641
+ ['top'],
642
+ ['string', 'number', 'boolean', 'null'],
643
+ ['path()', 'integer', 'float', 'biginteger', 'bigdecimal'],
644
+ ['nil'],
645
+ ];
646
+ const LATTICE_NODES = ['top', ...LATTICE_PARENT.map(([name]) => name), 'nil'];
647
+ // Every node at or above one, itself included.
648
+ function latticeAncestors(name) {
649
+ const out = [name];
650
+ for (let at = name; '' !== at;) {
651
+ const row = LATTICE_PARENT.find(([child]) => child === at);
652
+ at = undefined === row ? '' : row[1];
653
+ if ('' !== at) {
654
+ out.push(at);
655
+ }
656
+ }
657
+ return out;
658
+ }
659
+ // The columns one node covers: its own if it is minimal, otherwise
660
+ // every column beneath it. `nil` is beneath everything and above
661
+ // nothing, so the walk finds no column under it and the whole width is
662
+ // its span -- which is where it belongs.
663
+ function latticeSpan(name) {
664
+ const own = LATTICE_COLS.indexOf(name);
665
+ if (-1 !== own) {
666
+ return [own];
667
+ }
668
+ const under = LATTICE_COLS
669
+ .map((col, i) => latticeAncestors(col).includes(name) ? i : -1)
670
+ .filter((i) => -1 !== i);
671
+ return 0 === under.length ? LATTICE_COLS.map((_, i) => i) : under;
672
+ }
673
+ // True when `parent` is immediately above `child`. NIL IS COVERED BY
674
+ // EVERY MINIMAL KIND: it is the meet of all of them, and the only node
675
+ // the parent table does not name, because nothing in the engine ever
676
+ // answers `nil` as a superior.
677
+ function latticeCovers(parent, child) {
678
+ return 'nil' === child
679
+ ? -1 !== LATTICE_COLS.indexOf(parent)
680
+ : LATTICE_PARENT.some(([c, p]) => c === child && p === parent);
681
+ }
682
+ // WHERE ONE VALUE SITS, or undefined for a value that is not at a
683
+ // single point. The answers are the kinds of thing a document holds:
684
+ //
685
+ // a CONCRETE scalar sits at its kind -- `8080` is an `integer`, and
686
+ // `superior()` is the lattice's own answer to which;
687
+ // a KIND MARKER sits AT that kind -- `integer` written as a schema
688
+ // is the node itself, not a value under it;
689
+ // everything else -- a constraint, an unresolved disjunction, a
690
+ // reference -- is not one point. `integer & min(1)` is a REGION of
691
+ // the lattice and `*8080 | integer` is two places at once, so
692
+ // drawing either at a node would be a claim the figure cannot
693
+ // support. Both are counted into the loss report instead.
694
+ function latticePoint(v) {
695
+ const node = throughDoc(v);
696
+ if (true === node?.isNil) {
697
+ return 'nil';
698
+ }
699
+ if (true === node?.isTop) {
700
+ return 'top';
701
+ }
702
+ // A kind marker names its own node; a concrete scalar names the node
703
+ // above it. Either way the name has to BE one of the figure's: a
704
+ // kind the scaffold does not draw has nowhere to go, and saying so
705
+ // through the loss report is the only honest answer.
706
+ const name = true === node?.isScalarKind ? String(node.canon)
707
+ : true === node?.isScalar ? String(node.superior?.().canon) : '';
708
+ return LATTICE_NODES.includes(name) ? name : undefined;
709
+ }
710
+ // The document's own values, gathered by lattice node. Containers are
711
+ // walked but not placed: a map is not a scalar lattice citizen, and
712
+ // counting one at `top` would put every document's root there.
713
+ function latticeCensus(root, at) {
714
+ const counts = new Map();
715
+ const unplaced = [];
716
+ const stack = [{ node: root, path: at }];
717
+ while (0 < stack.length) {
718
+ const { node, path } = stack.pop();
719
+ const kids = docKids(node);
720
+ if (0 < kids.length) {
721
+ // A container is a shape, not a point: walk into it and place
722
+ // what it holds.
723
+ for (const key of kids) {
724
+ stack.push({
725
+ node: throughDoc(throughDoc(node).peg[key]),
726
+ path: path + '.' + key,
727
+ });
728
+ }
729
+ continue;
730
+ }
731
+ const point = latticePoint(node);
732
+ if (undefined === point) {
733
+ // AN EMPTY CONTAINER IS NEITHER A POINT NOR A SHAPE with
734
+ // anything in it, and is no more unplaced than `{}` is a value:
735
+ // skip it rather than report a loss a reader cannot act on.
736
+ const inner = throughDoc(node);
737
+ if (true !== inner?.isMap && true !== inner?.isList) {
738
+ unplaced.push(path);
739
+ }
740
+ continue;
741
+ }
742
+ const there = counts.get(point) ?? [];
743
+ there.push(path);
744
+ counts.set(point, there);
745
+ }
746
+ for (const paths of counts.values()) {
747
+ paths.sort(keyorder_1.cmpCodePoint);
748
+ }
749
+ unplaced.sort(keyorder_1.cmpCodePoint);
750
+ return { counts, unplaced };
751
+ }
752
+ // What one node is written as: its name, and the count of the
753
+ // document's values that landed on it. A node with nothing at it is
754
+ // still drawn -- the shape is the language's, and a figure that left
755
+ // the empty nodes out would be a different lattice for every document.
756
+ function latticeCell(counts, name) {
757
+ const n = (counts.get(name) ?? []).length;
758
+ return 0 === n ? name : `${name} (${n})`;
759
+ }
760
+ // The horizontal layout, in characters: one column per minimal kind,
761
+ // each as wide as the widest cell drawn over it plus a gutter, and the
762
+ // centre of each. The spanning nodes are narrower than the span they
763
+ // cover, so none of them needs a width of its own. The gutter is THREE
764
+ // because the SVG draws a box a character wider than its text: two of
765
+ // those characters are the box's own padding and the third is the gap
766
+ // between one box and the next.
767
+ const LATTICE_GUTTER = 3;
768
+ function latticeCols(counts) {
769
+ const w = LATTICE_COLS.map((col) => LATTICE_GUTTER + Math.max(...LATTICE_ROWS.flat()
770
+ .filter((name) => {
771
+ const span = latticeSpan(name);
772
+ return 1 === span.length && col === LATTICE_COLS[span[0]];
773
+ })
774
+ .map((name) => latticeCell(counts, name).length)));
775
+ let x = 0;
776
+ const cx = w.map((n) => {
777
+ const c = x + Math.floor(n / 2);
778
+ x += n;
779
+ return c;
780
+ });
781
+ return { cx, width: x };
782
+ }
783
+ // The centre of a node, from the columns it covers.
784
+ function latticeAt(name, cx) {
785
+ const span = latticeSpan(name);
786
+ return Math.round((cx[span[0]] + cx[span[span.length - 1]]) / 2);
787
+ }
788
+ // The box-drawing glyph for one column of a rule, from the four facts
789
+ // that meet there: whether the rule continues left and right, and
790
+ // whether a stem leaves upward and downward. Deciding it this way is
791
+ // what lets `number` -- which is BOTH one of the many under `top` and
792
+ // the one above the numeric leaves -- come out as the join it is,
793
+ // without a case written for it. The table is total, so no column has
794
+ // to be asked whether it has a glyph.
795
+ const LATTICE_GLYPH = {
796
+ '....': '─', '...d': '│', '..u.': '│', '..ud': '│',
797
+ '.r..': '─', '.r.d': '┌', '.ru.': '└', '.rud': '├',
798
+ 'l...': '─', 'l..d': '┐', 'l.u.': '┘', 'l.ud': '┤',
799
+ 'lr..': '─', 'lr.d': '┬', 'lru.': '┴', 'lrud': '┼',
800
+ };
801
+ // The figure is PAINTED rather than assembled from padded strings: the
802
+ // nodes have to line up with the rules that join them, and a count
803
+ // changes a cell's width -- so the geometry is settled first, in
804
+ // columns, and every glyph is then written at a place already known.
805
+ function latticeText(counts, style) {
806
+ const paint = painter(style);
807
+ const { cx, width } = latticeCols(counts);
808
+ const canvas = [];
809
+ const roles = [];
810
+ const put = (y, x, text, role) => {
811
+ while (canvas.length <= y) {
812
+ canvas.push(new Array(width).fill(' '));
813
+ roles.push(new Array(width).fill('label'));
814
+ }
815
+ for (let i = 0; i < text.length; i++) {
816
+ canvas[y][x + i] = text[i];
817
+ roles[y][x + i] = role;
818
+ }
819
+ };
820
+ // A cell is its name and, where the document reached it, the count:
821
+ // two roles, so a terminal can mute the second without touching the
822
+ // first.
823
+ const cell = (y, name) => {
824
+ const text = latticeCell(counts, name);
825
+ const left = latticeAt(name, cx) - Math.floor(text.length / 2);
826
+ put(y, left, name, 'label');
827
+ put(y, left + name.length, text.slice(name.length), 'muted');
828
+ };
829
+ const stems = (y, at) => {
830
+ for (const name of at) {
831
+ put(y, latticeAt(name, cx), '│', 'rule');
832
+ }
833
+ };
834
+ // The rule that joins one row to the next, plus the lines that pass
835
+ // it by: a kind with nothing under it runs on down the OUTSIDE of the
836
+ // fan, which the column order guarantees is clear of it.
837
+ const rule = (y, up, down, by) => {
838
+ const at = (names) => names.map((n) => latticeAt(n, cx));
839
+ const [u, d] = [at(up), at(down)];
840
+ const lo = Math.min(...u, ...d), hi = Math.max(...u, ...d);
841
+ for (let x = lo; x <= hi; x++) {
842
+ put(y, x, LATTICE_GLYPH[(x > lo ? 'l' : '.') + (x < hi ? 'r' : '.') +
843
+ (u.includes(x) ? 'u' : '.') + (d.includes(x) ? 'd' : '.')], 'rule');
844
+ }
845
+ stems(y, by);
846
+ };
847
+ // Four node rows and three joins. `open` is every node whose line
848
+ // downward has not been drawn yet, which is what carries `boolean`
849
+ // and `null` past the numeric row to the bottom rule.
850
+ let open = [];
851
+ let y = 0;
852
+ for (let r = 0; r < LATTICE_ROWS.length; r++) {
853
+ stems(y, open);
854
+ for (const name of LATTICE_ROWS[r]) {
855
+ cell(y, name);
856
+ }
857
+ open = [...open, ...LATTICE_ROWS[r]];
858
+ if (LATTICE_ROWS.length - 1 === r) {
859
+ break;
860
+ }
861
+ const next = LATTICE_ROWS[r + 1];
862
+ const parents = open.filter((n) => next.some((k) => latticeCovers(n, k)));
863
+ const by = open.filter((n) => !parents.includes(n));
864
+ stems(y + 1, open);
865
+ rule(y + 2, parents, next, by);
866
+ open = by;
867
+ y += 3;
868
+ }
869
+ return canvas.map((line, i) => {
870
+ const bare = line.join('').replace(/\s+$/, '');
871
+ let out = '', at = 0;
872
+ while (at < bare.length) {
873
+ let end = at;
874
+ while (end < bare.length && roles[i][end] === roles[i][at]) {
875
+ end++;
876
+ }
877
+ out += paint(roles[i][at], bare.slice(at, end));
878
+ at = end;
879
+ }
880
+ return out;
881
+ }).join('\n');
882
+ }
883
+ // The same figure as SVG, off the same column layout, so the two
884
+ // profiles are one drawing in two grammars rather than two drawings.
885
+ // A node the document REACHES is drawn with the ordinary rule stroke
886
+ // (`av-box`) and one it does not with the faint one (`av-cell`),
887
+ // because every node is drawn whether this document reaches it or not
888
+ // and a reader has to see which is which without counting. NO NEW
889
+ // CLASS: those two already mean a box and a faint box, so a host page
890
+ // that themed the other figures gets this one for nothing.
891
+ function latticeSvg(counts, at, style) {
892
+ const ROWH = 3 * LH;
893
+ const BOXH = 26;
894
+ const { cx, width } = latticeCols(counts);
895
+ const parts = [];
896
+ const rowOf = new Map();
897
+ LATTICE_ROWS.forEach((row, r) => row.forEach((name) => rowOf.set(name, r)));
898
+ const x = (name) => PAD + latticeAt(name, cx) * CH;
899
+ const y = (name) => PAD + BOXH / 2 + rowOf.get(name) * ROWH;
900
+ // Edges first, so a box always sits over the lines that reach it.
901
+ // The horizontal jog is placed just above the CHILD rather than
902
+ // halfway down, which is what keeps `boolean` and `null` -- three
903
+ // rows from `top` to `nil` with nothing between -- clear of the
904
+ // numeric row they pass.
905
+ const edges = [...LATTICE_PARENT,
906
+ ...LATTICE_COLS.map((col) => ['nil', col])];
907
+ for (const [child, parent] of edges) {
908
+ const y2 = y(child) - BOXH / 2;
909
+ parts.push(svgPath(`M${x(parent)} ${y(parent) + BOXH / 2}` +
910
+ `V${y2 - (ROWH - BOXH) / 2}H${x(child)}V${y2}`, 'av-line'));
911
+ }
912
+ for (const name of LATTICE_ROWS.flat()) {
913
+ const text = latticeCell(counts, name);
914
+ const w = (text.length + 2) * CH;
915
+ parts.push(svgRect(x(name) - w / 2, y(name) - BOXH / 2, w, BOXH, name === text ? 'av-cell' : 'av-box'));
916
+ // The name and the count in ONE text element, as the tree does it:
917
+ // two runs on one baseline, so the count is muted without the
918
+ // figure having to place it.
919
+ parts.push(`<text x="${x(name)}" y="${y(name) + 5}" text-anchor="middle">` +
920
+ `<tspan class="av-t">${svgEsc(name)}</tspan>` +
921
+ `<tspan class="av-m">${svgEsc(text.slice(name.length))}</tspan></text>`);
922
+ }
923
+ const placed = [...counts.values()].reduce((n, p) => n + p.length, 0);
924
+ return svgDoc(width * CH + 2 * PAD, 2 * PAD + BOXH + (LATTICE_ROWS.length - 1) * ROWH, `Value lattice at ${at}: ${placed} value(s) placed`, parts, style);
925
+ }
926
+ // The figure. The row count is fixed -- the lattice is the language's,
927
+ // and no option makes it smaller -- so `--max-rows` below it is still a
928
+ // refusal, because a figure that quietly overran a stated bound is the
929
+ // thing every other kind here refuses to be; the message says raise
930
+ // rather than narrow.
931
+ const LATTICE_LINES = 3 * LATTICE_ROWS.length - 2;
932
+ function drawLattice(root, o, max, loss) {
933
+ const at = o.at ?? '$';
934
+ const anchor = (0, vet_1.anchorAt)(root, at);
935
+ if (null == anchor) {
936
+ // The same code and the same sentence `get` answers with, for the
937
+ // same question.
938
+ return {
939
+ errors: [finding('no_path', 'reference', at, `The path ${at} names nothing in this document.`)],
940
+ };
941
+ }
942
+ if (max < LATTICE_LINES) {
943
+ return {
944
+ errors: [finding('view_rows_exceeded', 'budget', '$', `The figure has ${LATTICE_LINES} rows, above --max-rows ${max}; ` +
945
+ 'the value lattice is fixed, so raise the limit.', `rows: ${LATTICE_LINES}, max: ${max}`)],
946
+ };
947
+ }
948
+ const { counts, unplaced } = latticeCensus(anchor, at);
949
+ if (0 < unplaced.length) {
950
+ // NOT A LOSS OF DETAIL BUT A LOSS OF PLACE: these values are real,
951
+ // and the figure cannot say where they are because they are not
952
+ // anywhere single. Named, not merely counted -- a reader who sees
953
+ // `2` wants to know which two.
954
+ loss.push({
955
+ code: 'lattice_unplaced', count: unplaced.length, detail: unplaced,
956
+ });
957
+ }
958
+ return {
959
+ text: 'svg' === o.as
960
+ ? latticeSvg(counts, at, o.style) : latticeText(counts, o.style),
961
+ };
962
+ }
596
963
  const DEFAULT_DOC_DEPTH = 3;
597
964
  // A node's own children, as the anchor walk sees them: map keys sorted
598
965
  // by code point, list indices in order, and nothing for a leaf.
@@ -1593,7 +1960,7 @@ function drawLadder(src, options, as, max) {
1593
1960
  errors: [finding('view_at_required', 'reference', '$', 'The ladder needs the path to draw; name it with --at.')],
1594
1961
  };
1595
1962
  }
1596
- const rep = (0, query_1.why)(src, options.at, { path: options.path, trust: options.trust });
1963
+ const rep = (0, query_1.why)(src, options.at, { path: options.path, trust: options.trust, textExt: options.textExt });
1597
1964
  if (undefined === rep.record) {
1598
1965
  return { errors: rep.findings };
1599
1966
  }
@@ -1640,7 +2007,7 @@ const compareBySubsume = (general, specific, options) => {
1640
2007
  const r = (0, subsume_1.subsume)(general.src, specific.src, {
1641
2008
  at: options.at, profile: options.profile,
1642
2009
  generalPath: general.path, specificPath: specific.path,
1643
- trust: options.trust,
2010
+ trust: options.trust, textExt: options.textExt,
1644
2011
  });
1645
2012
  return { verdict: r.verdict, code: r.findings[0]?.code ?? 'undecided' };
1646
2013
  };
@@ -1796,7 +2163,7 @@ function drawPoset(docs, options, as, max, loss, compare) {
1796
2163
  // on their own, each with its own finding, or the anchor a document
1797
2164
  // lacks.
1798
2165
  function docFailure(d, options) {
1799
- const loaded = load(d.src, d.path, options.trust, undefined);
2166
+ const loaded = load(d.src, d.path, options, undefined);
1800
2167
  if (undefined !== loaded.errors) {
1801
2168
  return loaded.errors;
1802
2169
  }
@@ -1808,8 +2175,8 @@ function docFailure(d, options) {
1808
2175
  // One evaluation, parsed and unified separately so the provenance
1809
2176
  // recorder can stamp the parsed tree before the fixpoint runs (`why`'s
1810
2177
  // precedent).
1811
- function load(src, path, trust, prov) {
1812
- const aontu = new aontu_1.Aontu(null == trust ? undefined : { trust });
2178
+ function load(src, path, include, prov) {
2179
+ const aontu = new aontu_1.Aontu((0, utility_1.includeOpts)(include));
1813
2180
  const ctx = aontu.ctx({ collect: true, prov });
1814
2181
  const parseOpts = null == path ? undefined : { path };
1815
2182
  const parsed = aontu.parse(src, parseOpts, ctx);
@@ -1887,7 +2254,7 @@ function view(src, opts, hooks) {
1887
2254
  }
1888
2255
  const prov = 'layers' === kind
1889
2256
  ? (hooks?.provenance ?? (() => new provenance_1.Provenance()))() : undefined;
1890
- const loaded = load(src, options.path, options.trust, prov);
2257
+ const loaded = load(src, options.path, options, prov);
1891
2258
  if (undefined !== loaded.errors) {
1892
2259
  return done({ errors: loaded.errors });
1893
2260
  }
@@ -1905,6 +2272,9 @@ function drawLoaded(root, ctx, gen, prov, kind, as, options, max, loss) {
1905
2272
  if ('doc' === kind) {
1906
2273
  return drawDoc(root, { ...options, as, style }, max, loss);
1907
2274
  }
2275
+ if ('lattice' === kind) {
2276
+ return drawLattice(root, { ...options, as, style }, max, loss);
2277
+ }
1908
2278
  if ('layers' === kind) {
1909
2279
  return drawLayers(prov, root, options.path, { ...options, as, style }, max, loss);
1910
2280
  }
@@ -2101,7 +2471,7 @@ function viewSet(src, opts, hooks) {
2101
2471
  // always costs a little and makes the one-evaluation claim true for
2102
2472
  // every kind but the ladder, which re-runs `why` by construction.
2103
2473
  const prov = (hooks?.provenance ?? (() => new provenance_1.Provenance()))();
2104
- const loaded = load(src, options.path, options.trust, prov);
2474
+ const loaded = load(src, options.path, options, prov);
2105
2475
  if (undefined !== loaded.errors) {
2106
2476
  return { verdict: 'error', views: [], errors: loaded.errors };
2107
2477
  }
@@ -2143,6 +2513,7 @@ function viewSet(src, opts, hooks) {
2143
2513
  const loss = [];
2144
2514
  const each = {
2145
2515
  ...plan.opts, path: options.path, trust: options.trust,
2516
+ textExt: options.textExt,
2146
2517
  };
2147
2518
  const fig = 'ladder' === plan.kind
2148
2519
  ? drawLadder(src, each, plan.as, plan.max)