aontu 0.62.0 → 0.63.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 (356) hide show
  1. package/README.md +1 -1
  2. package/dist/agentsmd.js +0 -27
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/alias.js.map +1 -1
  5. package/dist/allow.js +0 -92
  6. package/dist/allow.js.map +1 -1
  7. package/dist/aontu.d.ts +1 -1
  8. package/dist/aontu.js +1 -79
  9. package/dist/aontu.js.map +1 -1
  10. package/dist/aontumodel.d.ts +4 -0
  11. package/dist/aontumodel.js +33 -0
  12. package/dist/aontumodel.js.map +1 -0
  13. package/dist/cli.js +101 -620
  14. package/dist/cli.js.map +1 -1
  15. package/dist/ctx.js +0 -48
  16. package/dist/ctx.js.map +1 -1
  17. package/dist/diff.js +0 -32
  18. package/dist/diff.js.map +1 -1
  19. package/dist/err.js +0 -40
  20. package/dist/err.js.map +1 -1
  21. package/dist/escape.js +0 -45
  22. package/dist/escape.js.map +1 -1
  23. package/dist/exactjson.d.ts +0 -35
  24. package/dist/exactjson.js +0 -131
  25. package/dist/exactjson.js.map +1 -1
  26. package/dist/format.js +13 -203
  27. package/dist/format.js.map +1 -1
  28. package/dist/grammar.d.ts +9 -0
  29. package/dist/grammar.js +54 -0
  30. package/dist/grammar.js.map +1 -0
  31. package/dist/graph.js +0 -26
  32. package/dist/graph.js.map +1 -1
  33. package/dist/hcanon.js +0 -82
  34. package/dist/hcanon.js.map +1 -1
  35. package/dist/helpdoc.js +1 -1
  36. package/dist/helpdoc.js.map +1 -1
  37. package/dist/hints.d.ts +0 -6
  38. package/dist/hints.js +54 -55
  39. package/dist/hints.js.map +1 -1
  40. package/dist/jsonschema.js +0 -114
  41. package/dist/jsonschema.js.map +1 -1
  42. package/dist/keyorder.d.ts +0 -7
  43. package/dist/keyorder.js +0 -41
  44. package/dist/keyorder.js.map +1 -1
  45. package/dist/lang.js +17 -915
  46. package/dist/lang.js.map +1 -1
  47. package/dist/lower.js +11 -61
  48. package/dist/lower.js.map +1 -1
  49. package/dist/lsp-server.js +0 -16
  50. package/dist/lsp-server.js.map +1 -1
  51. package/dist/lsp.d.ts +1 -1
  52. package/dist/lsp.js +12 -159
  53. package/dist/lsp.js.map +1 -1
  54. package/dist/mcp-server.js +0 -26
  55. package/dist/mcp-server.js.map +1 -1
  56. package/dist/mcp.js +0 -113
  57. package/dist/mcp.js.map +1 -1
  58. package/dist/mod-tool.js +0 -130
  59. package/dist/mod-tool.js.map +1 -1
  60. package/dist/mod.js +0 -162
  61. package/dist/mod.js.map +1 -1
  62. package/dist/patch.js +0 -217
  63. package/dist/patch.js.map +1 -1
  64. package/dist/provenance.js +0 -140
  65. package/dist/provenance.js.map +1 -1
  66. package/dist/query.js +0 -75
  67. package/dist/query.js.map +1 -1
  68. package/dist/reach.js +0 -43
  69. package/dist/reach.js.map +1 -1
  70. package/dist/relation.js +0 -61
  71. package/dist/relation.js.map +1 -1
  72. package/dist/render.js +20 -135
  73. package/dist/render.js.map +1 -1
  74. package/dist/report-sarif.d.ts +0 -11
  75. package/dist/report-sarif.js +0 -28
  76. package/dist/report-sarif.js.map +1 -1
  77. package/dist/sig.js +0 -35
  78. package/dist/sig.js.map +1 -1
  79. package/dist/sigdecl.js +1 -1
  80. package/dist/sigdecl.js.map +1 -1
  81. package/dist/siggate.js +0 -4
  82. package/dist/siggate.js.map +1 -1
  83. package/dist/site.js +3 -29
  84. package/dist/site.js.map +1 -1
  85. package/dist/subsume.d.ts +0 -10
  86. package/dist/subsume.js +0 -137
  87. package/dist/subsume.js.map +1 -1
  88. package/dist/template.d.ts +2 -1
  89. package/dist/template.js +58 -138
  90. package/dist/template.js.map +1 -1
  91. package/dist/trim.js +0 -41
  92. package/dist/trim.js.map +1 -1
  93. package/dist/tsconfig.tsbuildinfo +1 -1
  94. package/dist/type.js.map +1 -1
  95. package/dist/unify.js +12 -242
  96. package/dist/unify.js.map +1 -1
  97. package/dist/utility.js +0 -22
  98. package/dist/utility.js.map +1 -1
  99. package/dist/val/AbnfFuncVal.d.ts +18 -0
  100. package/dist/val/AbnfFuncVal.js +132 -0
  101. package/dist/val/AbnfFuncVal.js.map +1 -0
  102. package/dist/val/AbsentVal.d.ts +11 -0
  103. package/dist/val/AbsentVal.js +30 -0
  104. package/dist/val/AbsentVal.js.map +1 -0
  105. package/dist/val/AggFuncVal.d.ts +10 -1
  106. package/dist/val/AggFuncVal.js +104 -116
  107. package/dist/val/AggFuncVal.js.map +1 -1
  108. package/dist/val/ArithFuncVal.js +0 -12
  109. package/dist/val/ArithFuncVal.js.map +1 -1
  110. package/dist/val/BagVal.js +1 -78
  111. package/dist/val/BagVal.js.map +1 -1
  112. package/dist/val/BigDecimalVal.js +0 -16
  113. package/dist/val/BigDecimalVal.js.map +1 -1
  114. package/dist/val/BigIntegerVal.js +0 -16
  115. package/dist/val/BigIntegerVal.js.map +1 -1
  116. package/dist/val/CloseFuncVal.js +0 -9
  117. package/dist/val/CloseFuncVal.js.map +1 -1
  118. package/dist/val/CmpFuncVal.js +2 -59
  119. package/dist/val/CmpFuncVal.js.map +1 -1
  120. package/dist/val/ConjunctVal.js +0 -29
  121. package/dist/val/ConjunctVal.js.map +1 -1
  122. package/dist/val/ConstraintVal.js +0 -500
  123. package/dist/val/ConstraintVal.js.map +1 -1
  124. package/dist/val/ContainerKindVal.js +0 -2
  125. package/dist/val/ContainerKindVal.js.map +1 -1
  126. package/dist/val/CopyFuncVal.js +0 -3
  127. package/dist/val/CopyFuncVal.js.map +1 -1
  128. package/dist/val/Decimal.js +0 -179
  129. package/dist/val/Decimal.js.map +1 -1
  130. package/dist/val/DeprecateFuncVal.js.map +1 -1
  131. package/dist/val/DisjunctVal.js +0 -152
  132. package/dist/val/DisjunctVal.js.map +1 -1
  133. package/dist/val/EachFuncVal.js +0 -3
  134. package/dist/val/EachFuncVal.js.map +1 -1
  135. package/dist/val/EmitFuncVal.d.ts +1 -1
  136. package/dist/val/EmitFuncVal.js +6 -119
  137. package/dist/val/EmitFuncVal.js.map +1 -1
  138. package/dist/val/ExpectVal.js +0 -62
  139. package/dist/val/ExpectVal.js.map +1 -1
  140. package/dist/val/FilterFuncVal.js +0 -25
  141. package/dist/val/FilterFuncVal.js.map +1 -1
  142. package/dist/val/FuncBaseVal.d.ts +1 -0
  143. package/dist/val/FuncBaseVal.js +7 -127
  144. package/dist/val/FuncBaseVal.js.map +1 -1
  145. package/dist/val/GraphAtomVal.js +0 -15
  146. package/dist/val/GraphAtomVal.js.map +1 -1
  147. package/dist/val/HideFuncVal.js +0 -13
  148. package/dist/val/HideFuncVal.js.map +1 -1
  149. package/dist/val/IntegerVal.js +0 -61
  150. package/dist/val/IntegerVal.js.map +1 -1
  151. package/dist/val/JunctionVal.js +0 -20
  152. package/dist/val/JunctionVal.js.map +1 -1
  153. package/dist/val/KeyFuncVal.js +0 -46
  154. package/dist/val/KeyFuncVal.js.map +1 -1
  155. package/dist/val/ListVal.js +0 -57
  156. package/dist/val/ListVal.js.map +1 -1
  157. package/dist/val/LowerFuncVal.js +0 -11
  158. package/dist/val/LowerFuncVal.js.map +1 -1
  159. package/dist/val/MapVal.js +0 -151
  160. package/dist/val/MapVal.js.map +1 -1
  161. package/dist/val/MatchFuncVal.js +0 -27
  162. package/dist/val/MatchFuncVal.js.map +1 -1
  163. package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
  164. package/dist/val/MaybeFuncVal.js +50 -0
  165. package/dist/val/MaybeFuncVal.js.map +1 -0
  166. package/dist/val/MoveFuncVal.js +0 -18
  167. package/dist/val/MoveFuncVal.js.map +1 -1
  168. package/dist/val/NilVal.js +0 -60
  169. package/dist/val/NilVal.js.map +1 -1
  170. package/dist/val/NomFuncVal.js +8 -42
  171. package/dist/val/NomFuncVal.js.map +1 -1
  172. package/dist/val/NumberVal.js +0 -15
  173. package/dist/val/NumberVal.js.map +1 -1
  174. package/dist/val/OpBaseVal.d.ts +1 -0
  175. package/dist/val/OpBaseVal.js +3 -15
  176. package/dist/val/OpBaseVal.js.map +1 -1
  177. package/dist/val/PackFuncVal.js +0 -34
  178. package/dist/val/PackFuncVal.js.map +1 -1
  179. package/dist/val/PathFuncVal.js +0 -6
  180. package/dist/val/PathFuncVal.js.map +1 -1
  181. package/dist/val/PathVal.js +0 -41
  182. package/dist/val/PathVal.js.map +1 -1
  183. package/dist/val/PlaceVal.js +0 -25
  184. package/dist/val/PlaceVal.js.map +1 -1
  185. package/dist/val/PlusOpVal.d.ts +1 -7
  186. package/dist/val/PlusOpVal.js +13 -74
  187. package/dist/val/PlusOpVal.js.map +1 -1
  188. package/dist/val/PrefFuncVal.js +0 -1
  189. package/dist/val/PrefFuncVal.js.map +1 -1
  190. package/dist/val/PrefVal.js +0 -167
  191. package/dist/val/PrefVal.js.map +1 -1
  192. package/dist/val/RecurseVal.js +0 -55
  193. package/dist/val/RecurseVal.js.map +1 -1
  194. package/dist/val/RefVal.js +0 -282
  195. package/dist/val/RefVal.js.map +1 -1
  196. package/dist/val/ReferFuncVal.js +0 -232
  197. package/dist/val/ReferFuncVal.js.map +1 -1
  198. package/dist/val/ScalarKindVal.js +0 -49
  199. package/dist/val/ScalarKindVal.js.map +1 -1
  200. package/dist/val/ScalarVal.js +0 -11
  201. package/dist/val/ScalarVal.js.map +1 -1
  202. package/dist/val/StrFuncVal.js +0 -18
  203. package/dist/val/StrFuncVal.js.map +1 -1
  204. package/dist/val/SuperFuncVal.js +0 -32
  205. package/dist/val/SuperFuncVal.js.map +1 -1
  206. package/dist/val/TopVal.js +0 -1
  207. package/dist/val/TopVal.js.map +1 -1
  208. package/dist/val/TranslateFuncVal.js +1 -3
  209. package/dist/val/TranslateFuncVal.js.map +1 -1
  210. package/dist/val/UpperFuncVal.js +0 -11
  211. package/dist/val/UpperFuncVal.js.map +1 -1
  212. package/dist/val/Val.d.ts +1 -0
  213. package/dist/val/Val.js +2 -133
  214. package/dist/val/Val.js.map +1 -1
  215. package/dist/val/VarVal.js +0 -12
  216. package/dist/val/VarVal.js.map +1 -1
  217. package/dist/val/arith.js +0 -37
  218. package/dist/val/arith.js.map +1 -1
  219. package/dist/val/caserange.js +0 -61
  220. package/dist/val/caserange.js.map +1 -1
  221. package/dist/val/members.js +0 -6
  222. package/dist/val/members.js.map +1 -1
  223. package/dist/val/numcmp.js +0 -11
  224. package/dist/val/numcmp.js.map +1 -1
  225. package/dist/val/numkind.js +0 -145
  226. package/dist/val/numkind.js.map +1 -1
  227. package/dist/val/valutil.js +0 -16
  228. package/dist/val/valutil.js.map +1 -1
  229. package/dist/vet.js +0 -461
  230. package/dist/vet.js.map +1 -1
  231. package/dist/view.js +0 -414
  232. package/dist/view.js.map +1 -1
  233. package/dist/walk.js +0 -41
  234. package/dist/walk.js.map +1 -1
  235. package/grammar/aontu.abnf +8 -6
  236. package/grammar/aontu.gbnf +4 -4
  237. package/grammar/aontu.lark +4 -4
  238. package/grammar/aontu.tmLanguage.json +1 -1
  239. package/package.json +4 -2
  240. package/src/agentsmd.ts +0 -35
  241. package/src/alias.ts +0 -39
  242. package/src/allow.ts +1 -96
  243. package/src/aontu.ts +3 -110
  244. package/src/aontumodel.ts +32 -0
  245. package/src/cli.ts +113 -682
  246. package/src/ctx.ts +0 -103
  247. package/src/diff.ts +0 -40
  248. package/src/err.ts +0 -40
  249. package/src/escape.ts +0 -46
  250. package/src/exactjson.ts +0 -131
  251. package/src/format.ts +14 -257
  252. package/src/grammar.ts +72 -0
  253. package/src/graph.ts +0 -61
  254. package/src/hcanon.ts +0 -82
  255. package/src/helpdoc.ts +1 -1
  256. package/src/hints.ts +66 -57
  257. package/src/jsonschema.ts +0 -123
  258. package/src/keyorder.ts +0 -42
  259. package/src/lang.ts +19 -931
  260. package/src/lower.ts +12 -62
  261. package/src/lsp-server.ts +0 -16
  262. package/src/lsp.ts +12 -180
  263. package/src/mcp-server.ts +0 -31
  264. package/src/mcp.ts +0 -130
  265. package/src/mod-tool.ts +0 -158
  266. package/src/mod.ts +0 -178
  267. package/src/patch.ts +0 -232
  268. package/src/provenance.ts +0 -183
  269. package/src/query.ts +0 -84
  270. package/src/reach.ts +0 -53
  271. package/src/relation.ts +0 -84
  272. package/src/render.ts +20 -172
  273. package/src/report-sarif.ts +0 -48
  274. package/src/sig.ts +0 -35
  275. package/src/sigdecl.ts +1 -1
  276. package/src/siggate.ts +0 -30
  277. package/src/site.ts +3 -29
  278. package/src/subsume.ts +1 -161
  279. package/src/template.ts +69 -140
  280. package/src/trim.ts +0 -53
  281. package/src/type.ts +2 -45
  282. package/src/unify.ts +13 -251
  283. package/src/utility.ts +0 -31
  284. package/src/val/AbnfFuncVal.ts +181 -0
  285. package/src/val/AbsentVal.ts +54 -0
  286. package/src/val/AggFuncVal.ts +152 -188
  287. package/src/val/ArithFuncVal.ts +0 -20
  288. package/src/val/BagVal.ts +1 -78
  289. package/src/val/BigDecimalVal.ts +0 -16
  290. package/src/val/BigIntegerVal.ts +0 -16
  291. package/src/val/CloseFuncVal.ts +0 -9
  292. package/src/val/CmpFuncVal.ts +4 -166
  293. package/src/val/ConjunctVal.ts +0 -33
  294. package/src/val/ConstraintVal.ts +2 -537
  295. package/src/val/ContainerKindVal.ts +0 -18
  296. package/src/val/CopyFuncVal.ts +0 -5
  297. package/src/val/Decimal.ts +1 -185
  298. package/src/val/DeprecateFuncVal.ts +0 -10
  299. package/src/val/DisjunctVal.ts +0 -157
  300. package/src/val/EachFuncVal.ts +0 -40
  301. package/src/val/EmitFuncVal.ts +8 -208
  302. package/src/val/ExpectVal.ts +0 -62
  303. package/src/val/FilterFuncVal.ts +0 -55
  304. package/src/val/FuncBaseVal.ts +9 -130
  305. package/src/val/GraphAtomVal.ts +0 -42
  306. package/src/val/HideFuncVal.ts +0 -15
  307. package/src/val/IntegerVal.ts +0 -61
  308. package/src/val/JunctionVal.ts +0 -20
  309. package/src/val/KeyFuncVal.ts +0 -48
  310. package/src/val/ListVal.ts +0 -59
  311. package/src/val/LowerFuncVal.ts +0 -12
  312. package/src/val/MapVal.ts +0 -151
  313. package/src/val/MatchFuncVal.ts +0 -59
  314. package/src/val/MaybeFuncVal.ts +86 -0
  315. package/src/val/MoveFuncVal.ts +0 -20
  316. package/src/val/NilVal.ts +0 -60
  317. package/src/val/NomFuncVal.ts +8 -95
  318. package/src/val/NumberVal.ts +0 -16
  319. package/src/val/OpBaseVal.ts +4 -17
  320. package/src/val/PackFuncVal.ts +0 -63
  321. package/src/val/PathFuncVal.ts +0 -32
  322. package/src/val/PathVal.ts +0 -66
  323. package/src/val/PlaceVal.ts +0 -45
  324. package/src/val/PlusOpVal.ts +18 -75
  325. package/src/val/PrefFuncVal.ts +0 -1
  326. package/src/val/PrefVal.ts +0 -179
  327. package/src/val/RecurseVal.ts +0 -81
  328. package/src/val/RefVal.ts +1 -285
  329. package/src/val/ReferFuncVal.ts +0 -255
  330. package/src/val/ScalarKindVal.ts +0 -50
  331. package/src/val/ScalarVal.ts +0 -12
  332. package/src/val/StrFuncVal.ts +0 -44
  333. package/src/val/SuperFuncVal.ts +0 -42
  334. package/src/val/TopVal.ts +0 -1
  335. package/src/val/TranslateFuncVal.ts +1 -51
  336. package/src/val/UpperFuncVal.ts +0 -12
  337. package/src/val/Val.ts +3 -192
  338. package/src/val/VarVal.ts +0 -15
  339. package/src/val/arith.ts +0 -92
  340. package/src/val/caserange.ts +0 -62
  341. package/src/val/members.ts +0 -23
  342. package/src/val/numcmp.ts +1 -27
  343. package/src/val/numkind.ts +0 -149
  344. package/src/val/valutil.ts +0 -16
  345. package/src/vet.ts +1 -582
  346. package/src/view.ts +0 -507
  347. package/src/walk.ts +0 -41
  348. package/dist/std.d.ts +0 -3
  349. package/dist/std.js +0 -672
  350. package/dist/std.js.map +0 -1
  351. package/dist/val/FormFuncVal.js +0 -55
  352. package/dist/val/FormFuncVal.js.map +0 -1
  353. package/dist/val/NamerFuncVal.d.ts +0 -12
  354. package/dist/val/NamerFuncVal.js +0 -176
  355. package/dist/val/NamerFuncVal.js.map +0 -1
  356. package/src/std.ts +0 -683
package/src/subsume.ts CHANGED
@@ -1,27 +1,6 @@
1
1
  /* Copyright (c) 2025 Richard Rodger, MIT License */
2
2
  import { includeOpts } from './utility'
3
3
 
4
- // Subsumption as a first-class query (G3 phases 1-2,
5
- // docs/capability-review/g3-subsumption-evolution.md): does the GENERAL
6
- // value admit every instance the SPECIFIC value admits?
7
- //
8
- // The recursion is a dedicated structural walk over EVALUATED values —
9
- // design option B. It never mutates its inputs (it runs after
10
- // evaluation, on finished trees), carries no fixpoint, and returns a
11
- // THREE-VALUED verdict: `subsumes`, `does_not_subsume` (with the
12
- // failing path and both sides' canons as the witness), or `undecided`
13
- // (with a reason code, never silently). Where a rule cannot decide the
14
- // answer folds toward "not subsumed" or "undecided" — the safe
15
- // directions: a compatibility gate that wrongly reports "breaking"
16
- // costs a second look, one that wrongly reports "compatible" ships the
17
- // break (docs/reference-language.md, "Subsumption").
18
- //
19
- // Findings reuse G2's report vocabulary (the vet finding object), with
20
- // the G3 codes appended to the shared registry: `compat_narrowed`,
21
- // `compat_required_added`, `compat_default_changed`,
22
- // `compat_marks_changed`, and the `sub_*` undecided reasons — all
23
- // class `compat`.
24
-
25
4
 
26
5
  import type { TrustOptions } from './type'
27
6
  import { Aontu } from './aontu'
@@ -45,7 +24,7 @@ export type SubsumeProfile = 'values' | 'defaults' | 'gen'
45
24
  export type SubsumeOptions = {
46
25
  profile?: SubsumeProfile
47
26
  at?: string
48
- generalUrl?: string // provenance label for general sites
27
+ generalUrl?: string
49
28
  specificUrl?: string // provenance label for specific sites
50
29
  // Where each document CAME FROM, so a relative `@"file"` load inside
51
30
  // it resolves from its own directory — vet's schemaPath/dataPath
@@ -73,8 +52,6 @@ const DEFAULT_GENERAL_URL = 'general'
73
52
  const DEFAULT_SPECIFIC_URL = 'specific'
74
53
 
75
54
 
76
- // One comparison's running state: the finding list, the two provenance
77
- // labels, and the profile.
78
55
  type SubState = {
79
56
  profile: SubsumeProfile
80
57
  // Set inside a distribution trial: see trialSubsume.
@@ -94,10 +71,6 @@ function pathText(path: string[]): string {
94
71
  }
95
72
 
96
73
 
97
- // Every recorded operand is a real evaluated Val or the sited topLike
98
- // stand-in, so no null guards: a Val's site is always an object, and a
99
- // value parsed without a usable frame (a disjunct, say) already carries
100
- // the -1 coordinates in it.
101
74
  function siteOf(v: any, role: string, url: string): VetSite {
102
75
  return {
103
76
  file: url,
@@ -133,50 +106,17 @@ function record(
133
106
  }
134
107
 
135
108
 
136
- // The admission view of a value under a profile: a BARE preference
137
- // admits what its superior admits (the engine's own PrefVal.superpeg
138
- // semantics, and the docs' "a bare preference is gated by kind"); the
139
- // default itself is compared separately, by the `defaults` and `gen`
140
- // profiles.
141
109
  function admission(v: any): any {
142
110
  return true === v?.isPref ? v.superpeg : v
143
111
  }
144
112
 
145
113
 
146
- // A PREFERRED BRANCH CONTRIBUTES EXACTLY ITS OWN VALUE (ADR-004). The
147
- // admission gate made that the engine's rule -- `*'auto'|'literal'|'data'`
148
- // admits those three strings and nothing else -- and the subsumption
149
- // walk kept comparing a pref MEMBER by its kind superior, the
150
- // pre-ADR-004 reading. So a disjunction with a default did not subsume
151
- // ITSELF: every member of the specific side widened to `string`, which
152
- // no general member admits, and the walk answered the distribution
153
- // case. `aontu_policy: hide({compat: *backward|forward|full|none})` --
154
- // the verbatim idiom from reference-api.md -- failed self-subsumption
155
- // under --profile gen (use-cases/BUGS.md §29).
156
- //
157
- // Only for a member of a disjunction: a bare `*x` standing alone is
158
- // still gated by kind, which is the rule above and the documented one.
159
114
  function memberAdmission(v: any): any {
160
115
  return true === v?.isPref ? prefInnerPeg(v) : v
161
116
  }
162
117
 
163
118
 
164
- // The effective default of a value, or undefined when it has none, or
165
- // 'indeterminate' when equal-rank preferences disagree (which the
166
- // engine itself refuses only at generation).
167
- // Exported (with subsumeNode) for the default-validity lint in
168
- // ts/src/vet.ts (G3 phase 5): the lint asks exactly this walk's two
169
- // questions — what is the effective default, and does a member admit
170
- // it.
171
119
  export function effectiveDefault(v: any): any {
172
- // EVERY pref layer is unwrapped (prefInnerPeg), not just one: a
173
- // ranked default's effective value is the innermost peg — `**member`
174
- // generates "member" exactly as `*member` does (the rank-uniform
175
- // meet, ADR-004). The one-layer unwrap left a rank-2 default wearing
176
- // a `*`-wrapper no plain alternative subsumes, which is the
177
- // pref_not_instance lint's ranked false positive of
178
- // use-cases/BUGS.md §4 (`**member|member|admin|owner` warned while
179
- // generating a value its own branch admits).
180
120
  if (true === v?.isPref) {
181
121
  return prefInnerPeg(v)
182
122
  }
@@ -225,10 +165,6 @@ function unresolved(v: any): boolean {
225
165
  }
226
166
 
227
167
 
228
- // Exported for the defensive-arm unit test (ADR-002,
229
- // ts/test/coverage3.test.ts): the no-rule fold at the walk's tail is
230
- // unreachable through subsume() today — the ladder is total for every
231
- // evaluated former — and the walk is otherwise private.
232
168
  export function subsumeNode(
233
169
  state: SubState, path: string[], g0: any, s0: any): Tri {
234
170
 
@@ -247,32 +183,11 @@ export function subsumeNode(
247
183
  return 'no'
248
184
  }
249
185
 
250
- // TOP admits everything. There is no nil rule: an error-free
251
- // evaluated document carries no nil (failing disjunct members are
252
- // discarded, every other nil collects an error, and subsume() answers
253
- // `error` for a source that does not stand alone), so a nil handed to
254
- // the walk by a future caller falls to the no-rule fold below —
255
- // `undecided`, the safe direction.
256
186
  if (true === g?.isTop) {
257
187
  return 'yes'
258
188
  }
259
189
 
260
190
  if (unresolved(g) || unresolved(s)) {
261
- // REFLEXIVITY IS A LAW, not a rule the ladder gets to skip. Every
262
- // value admits itself, residue included: the set admitted by
263
- // `integer & min(0)` is exactly the set admitted by
264
- // `integer & min(0)`. Without this, a constraint inside a spread
265
- // template made a contract non-SELF-subsumable -- expected and
266
- // actual byte-identical, verdict `undecided` -- so `breaking` on
267
- // the documented close-per-entry idiom hard-failed reflexivity and
268
- // had to run --allow-undecided, which then masks the genuine
269
- // undecideds it exists to surface (use-cases/BUGS.md §28).
270
- //
271
- // Identity is the HASH FORM, not the canon: canon drops closedness
272
- // and the marks, so `close({a:1})` and `{a:1}` share a canon while
273
- // admitting different sets. Computed only on this branch, where
274
- // the answer would otherwise be undecided, so the hot path is
275
- // untouched.
276
191
  if (hcanon(g) === hcanon(s)) {
277
192
  return 'yes'
278
193
  }
@@ -281,10 +196,6 @@ export function subsumeNode(
281
196
  return 'undecided'
282
197
  }
283
198
 
284
- // Disjunctions. General side first: a specific member must be
285
- // subsumed by SOME general member; member-wise failure is not proof
286
- // (the distribution case), so a concrete failing member becomes the
287
- // witness and anything else is honestly undecided.
288
199
  if (true === s?.isDisjunct) {
289
200
  let out: Tri = 'yes'
290
201
  for (const raw of s.peg as any[]) {
@@ -343,11 +254,6 @@ export function subsumeNode(
343
254
  return 'no'
344
255
  }
345
256
  if (true === s?.isConstraint) {
346
- // A kind covers a residual of its own domain: `number` admits any
347
- // numeric residual (the supertype admits every leaf a bound can
348
- // pin), a numeric LEAF kind admits only a residual pinned to that
349
- // leaf, and `string` admits any pattern residual. A residual with
350
- // no domain (sizing-only) admits containers no kind covers.
351
257
  const dom = (s as any).domain
352
258
  const skind = (s as any).kind
353
259
  if ('number' === dom &&
@@ -383,7 +289,6 @@ export function subsumeNode(
383
289
  return 'no'
384
290
  }
385
291
 
386
- // Constraint residuals.
387
292
  if (true === g?.isConstraint) {
388
293
  if (true === s?.isConstraint) {
389
294
  const r = constraintSubsumesConstraint(g, s)
@@ -460,26 +365,6 @@ export function subsumeNode(
460
365
  (v: any, k: string) => v.peg[Number(k)])
461
366
  }
462
367
 
463
- // THE LADDER IS NOT TOTAL, and the formers that fall past it are the
464
- // ones the evaluator MEANS to leave standing: a recursion, a
465
- // relation and its graph atom, a `refer()` target constraint. None
466
- // of them describes a set of values a structural walk can compare,
467
- // so `undecided` is the honest answer for two DIFFERENT ones.
468
- //
469
- // For two IDENTICAL ones it is not. REFLEXIVITY IS A LAW -- every
470
- // value admits itself -- and identity is the HASH FORM, the same
471
- // rule the unresolved branch above applies and for the same reason:
472
- // it costs nothing, because it runs only where the answer would
473
- // otherwise be `undecided`. Without it a document that declares a
474
- // relation, shares a template by reference or alias, or recurses did
475
- // not subsume ITSELF, so `breaking` on the idiom the language exists
476
- // for hard-failed and had to run --allow-undecided, which masks the
477
- // genuine undecideds it exists to surface (use-cases/BUGS.md 64,
478
- // and 28 before it).
479
- //
480
- // A NIL is the exception, and the reason the law is spelled here
481
- // rather than in `unresolved`: a nil is not a value, so it admits
482
- // nothing, itself included.
483
368
  if (true !== g?.isNil && true !== s?.isNil && hcanon(g) === hcanon(s)) {
484
369
  return 'yes'
485
370
  }
@@ -554,16 +439,6 @@ function subsumeBag(
554
439
  }
555
440
  }
556
441
 
557
- // Spread templates: a path-dependent template's meaning depends on
558
- // where it lands, which no structural comparison can decide -- UNLESS
559
- // the two templates are the same template. REFLEXIVITY IS A LAW and
560
- // identity is the HASH FORM (the same rule the unresolved branch of
561
- // subsumeNode applies): two byte-identical templates admit the same
562
- // set wherever they land, so a document with a reference- or
563
- // alias-valued template subsumes itself, and the comparison either
564
- // side of the template is decided on its own merits rather than
565
- // dragged to `undecided` (use-cases/BUGS.md 64). Where they are NOT
566
- // identical, nothing structural can decide them and the fold stands.
567
442
  const gcj = g.spread?.cj
568
443
  const scj = s.spread?.cj
569
444
  if (null != gcj || null != scj) {
@@ -596,12 +471,6 @@ function subsumeBag(
596
471
  let topVal: any
597
472
  function topLike(): any {
598
473
  if (null == topVal) {
599
- // A full site, not a partial one: `len` and `src` are as much part
600
- // of the shape as row and col, and leaving them undefined dropped
601
- // both keys from the emitted report — so this stand-in was the one
602
- // site in either port with no `len` at all, and the Go twin (which
603
- // has no undefined) disagreed. It occupies no source, so the values
604
- // are the "unknown" ones.
605
474
  topVal = {
606
475
  isTop: true, canon: 'top',
607
476
  site: { row: -1, col: -1, len: -1, src: '' },
@@ -611,20 +480,6 @@ function topLike(): any {
611
480
  }
612
481
 
613
482
 
614
- // A trial comparison whose findings are DISCARDED: disjunct
615
- // member-matching asks many "would this member do?" questions, and only
616
- // the aggregated outcome is a finding.
617
- // A DISTRIBUTION TRIAL IS NOT A NODE CORRESPONDENCE. It asks whether
618
- // one ALTERNATIVE of one side admits one alternative of the other,
619
- // which is a question about admitted sets; the two values it compares
620
- // are not the same node of the two documents. The `gen` profile's mark
621
- // rule is a correspondence question -- did a field that used to be
622
- // generated become hidden -- and firing it here compared a whole
623
- // disjunction (carrying its enclosing bag's mark) against a member
624
- // extracted out of one (which does not), so `hide({c: *a|b})` stopped
625
- // subsuming ITSELF under --profile gen (use-cases/BUGS.md §29). The
626
- // enclosing node's marks are compared where they correspond: at that
627
- // node, by the ordinary walk.
628
483
  function trialSubsume(
629
484
  state: SubState, path: string[], g: any, s: any): Tri {
630
485
  const trial: SubState = { ...state, findings: [], distributing: true }
@@ -632,11 +487,6 @@ function trialSubsume(
632
487
  }
633
488
 
634
489
 
635
- // The defaults comparison (the `defaults` and `gen` profiles): the
636
- // specific side's effective default must survive into the general side
637
- // unchanged. Adding a default where none existed is compatible;
638
- // changing or removing one is not (removal turns previously generable
639
- // documents incomplete).
640
490
  function subsumeDefaults(
641
491
  state: SubState, path: string[], g: any, s: any): Tri {
642
492
  const sd = effectiveDefault(s)
@@ -682,16 +532,6 @@ function subsumeDefaultsWalk(
682
532
  }
683
533
 
684
534
 
685
- /**
686
- * Does `generalSrc` subsume `specificSrc` — is every instance the
687
- * specific admits admitted by the general too?
688
- *
689
- * Both sources are evaluated fresh (single-use trees make this
690
- * mandatory), and the recursion runs on the finished values. The
691
- * verdict is three-valued plus `error` (a source that does not stand up
692
- * on its own, mirroring vet's schema-error verdict); findings reuse
693
- * G2's object with class `compat`.
694
- */
695
535
  export function subsume(
696
536
  generalSrc: string, specificSrc: string, opts?: SubsumeOptions,
697
537
  ): SubsumeReport {
package/src/template.ts CHANGED
@@ -1,70 +1,6 @@
1
1
  /* Copyright (c) 2025 Richard Rodger, MIT License */
2
2
 
3
3
 
4
- // THE TEMPLATE SURFACE (docs/design/TEMPLATE.0.md; RENDER.0.md P8).
5
- // A generator written in the TARGET's own syntax: a marked line is
6
- // aontu source, and every other line is a line of output.
7
- //
8
- // //- code: units: [{ path: "hi.ts", lang: "typescript", decls: [{
9
- // //- k: "frag", of: emit($.svc, { match: { n: string }, body: [
10
- // export const NAME = "world"
11
- // //- ]}) }] }]
12
- //
13
- // That is the whole desugaring, and it is LINE-ORIENTED: nothing here
14
- // parses aontu, and nothing here reads the target's syntax beyond its
15
- // comment token. A marker line contributes its text to the document; an
16
- // unmarked line contributes one quoted string, which is a body element
17
- // where the marker lines left a list open.
18
- //
19
- // WHY A COMMENT TOKEN PLUS A DASH (D2). The generator file must stay
20
- // valid in its own language -- so an editor highlights it, a compiler
21
- // parses it, and a reviewer reads it -- and the one thing every
22
- // language has that the target's own tools already ignore is a
23
- // comment. Adding a dash distinguishes a marker from an ordinary
24
- // comment, and derives for a language this table has never seen: the
25
- // caller passes the token.
26
- //
27
- // A MARKER IS RECOGNISED AFTER LEADING WHITESPACE, AND THE INDENTATION
28
- // THE RESUGARING WRITES IS THE AONTU'S (D2, amended 2026-09-07). An
29
- // output line is verbatim -- a line indented two spaces in the template
30
- // is indented two spaces in the generated file (D7) -- and that is the
31
- // half a reader of the TARGET needs. The other half is the document:
32
- // the marker lines carry a tree, and a tree nobody can see the shape of
33
- // is the one thing this surface took away. So the marker stands at the
34
- // left margin and the aontu is indented AFTER it, which `aontu fmt`
35
- // (FMT.0.md M-BM-'3.14) is what writes.
36
- //
37
- // Reading is unchanged and stays generous: a marker after leading
38
- // whitespace is a marker, and its own indentation still counts toward
39
- // the aontu it carries, so a template written the other way desugars to
40
- // the same document. What changes is the spelling the round trip
41
- // answers, and `template --check` names the difference as the drift it
42
- // is.
43
- //
44
- // THE SUGAR IS THE FIXPOINT OF THE TWO TRANSFORMS (D6), and that is
45
- // the whole of the resugaring's safety. A canonical line that looks
46
- // like a body element is rebuilt as a target line, the rebuild is
47
- // desugared, and the rebuild is taken ONLY if that reproduces the
48
- // canonical line. A line no target line can carry -- one that would
49
- // itself read as a marker -- fails that test and stays where it is, so
50
- // there is no table of escapes to get wrong and no line the round trip
51
- // can lose.
52
- //
53
- // WHITESPACE CONTROL IS ABSENT AND UNNEEDED (D8): a marker line is not
54
- // an output line at all, so no construct can leak a newline into the
55
- // output. What IS significant is the template's own trailing
56
- // whitespace -- a line of two spaces is two spaces of output -- which
57
- // is why the canonical form quotes every body line explicitly. Note
58
- // what `template --check` does and does not catch: it holds the FILE
59
- // to the spelling these two transforms answer, so a marker written
60
- // without its space or with its aontu indented after it is named; a
61
- // body line an editor trimmed is still a valid template, and what
62
- // names that is `render --check` against the committed output.
63
-
64
-
65
- // A marker is a comment token plus a dash. These are the tokens the
66
- // verb derives from a file's extension; any other language passes its
67
- // own, which is why the surface needs no table of languages.
68
4
  const MARKERS: Record<string, string> = {
69
5
  c: '//-',
70
6
  cc: '//-',
@@ -79,6 +15,8 @@ const MARKERS: Record<string, string> = {
79
15
  jsx: '//-',
80
16
  kt: '//-',
81
17
  lua: '---',
18
+ markdown: '<!---',
19
+ md: '<!---',
82
20
  php: '//-',
83
21
  pl: '#-',
84
22
  py: '#-',
@@ -99,36 +37,70 @@ const MARKERS: Record<string, string> = {
99
37
  // C-family line comment, which is the one the note is written in.
100
38
  const DEFAULT_MARKER = '//-'
101
39
 
102
- // A BLOCK MARKER CLOSES ITSELF. A language with no line comment (CSS
103
- // is the one in the table) marks with `/*- … */`, and the closer is
104
- // implied by the opener rather than named separately: there is one
105
- // block form, and a marker that opens `/*-` ends `*/`.
106
- const BLOCK_CLOSE = '*/'
40
+ // A marker outside this table names its closer itself.
41
+ const IMPLIED_CLOSE: [string, string][] = [
42
+ ['<!--', '-->'],
43
+ ['/*', '*/'],
44
+ ]
45
+
46
+
47
+ function extensionOf(path: string): string {
48
+ const dot = path.lastIndexOf('.')
49
+ const slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))
50
+ return dot > slash ? path.slice(dot + 1).toLowerCase() : ''
51
+ }
107
52
 
108
53
 
109
54
  // The marker for a file, by its extension, or the C-family default.
110
55
  // The extension decides, exactly as it decides what an include is
111
56
  // (ADR-012) -- one rule, and no flag to remember for the common case.
112
57
  function markerFor(path: string): string {
113
- const dot = path.lastIndexOf('.')
114
- const slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))
115
- const ext = dot > slash ? path.slice(dot + 1).toLowerCase() : ''
116
- return MARKERS[ext] ?? DEFAULT_MARKER
58
+ return MARKERS[extensionOf(path)] ?? DEFAULT_MARKER
59
+ }
60
+
61
+
62
+ // What reaches a language the table has no entry for by FILENAME
63
+ // rather than by a flag on every call.
64
+ function markerFromProfiles(
65
+ profiles: any[], path: string
66
+ ): string | undefined {
67
+ const ext = extensionOf(path)
68
+ if ('' === ext) {
69
+ return undefined
70
+ }
71
+ for (const profile of profiles) {
72
+ const tmpl = profile?.template
73
+ if (null == tmpl || !Array.isArray(tmpl.ext) || !tmpl.ext.includes(ext)) {
74
+ continue
75
+ }
76
+ return null == tmpl.close ? tmpl.marker : tmpl.marker + ' ' + tmpl.close
77
+ }
78
+ return undefined
117
79
  }
118
80
 
119
81
 
120
- // Is this marker the block form?
121
- function isBlock(marker: string): boolean {
122
- return marker.startsWith('/*')
82
+ // A marker may carry its own closer after a space, which no comment
83
+ // opener holds. An empty closer is the line form.
84
+ type MarkerForm = { open: string, close: string }
85
+
86
+
87
+ function markerForm(marker: string): MarkerForm {
88
+ const cut = marker.indexOf(' ')
89
+ if (-1 < cut) {
90
+ return {
91
+ open: marker.slice(0, cut),
92
+ close: trimLine(marker.slice(cut + 1)),
93
+ }
94
+ }
95
+ for (const [open, close] of IMPLIED_CLOSE) {
96
+ if (marker.startsWith(open)) {
97
+ return { open: marker, close }
98
+ }
99
+ }
100
+ return { open: marker, close: '' }
123
101
  }
124
102
 
125
103
 
126
- // INDENTATION IS SPACES AND TABS, and nothing else. A host trim is not
127
- // the same set in the two ports -- JavaScript's trims every Unicode
128
- // space, Go's `TrimSpace` trims a different list -- and a line of a
129
- // template file that began with one of the characters they disagree
130
- // about would be a marker line in one engine and a line of output in
131
- // the other. Twin of templateIndent in go/template.go.
132
104
  function indentOf(line: string): number {
133
105
  let i = 0
134
106
  while (i < line.length && (' ' === line[i] || '\t' === line[i])) {
@@ -164,31 +136,21 @@ type Line = {
164
136
  }
165
137
 
166
138
 
167
- function readLine(line: string, marker: string): Line {
139
+ function readLine(line: string, form: MarkerForm): Line {
168
140
  const cut = indentOf(line)
169
141
  const indent = line.slice(0, cut)
170
142
  const rest = line.slice(cut)
171
- if (!rest.startsWith(marker)) {
143
+ if (!rest.startsWith(form.open)) {
172
144
  return { marker: false, indent: '', text: line }
173
145
  }
174
- let body = rest.slice(marker.length)
175
- // The block form's closer is part of the marker, not of the aontu.
176
- // A block marker line that never closes is not a marker line: the
177
- // language's own parser would not read it as a comment either.
178
- // Only the TRAILING space goes: what stands between the opener and
179
- // the aontu is the aontu's indentation, exactly as it is for a line
180
- // marker, and the one-space rule below takes the marker's own.
181
- if (isBlock(marker)) {
182
- const end = body.lastIndexOf(BLOCK_CLOSE)
146
+ let body = rest.slice(form.open.length)
147
+ if ('' !== form.close) {
148
+ const end = body.lastIndexOf(form.close)
183
149
  if (end < 0) {
184
150
  return { marker: false, indent: '', text: line }
185
151
  }
186
152
  body = trimEnd(body.slice(0, end))
187
153
  }
188
- // ONE SPACE AFTER THE MARKER IS THE MARKER'S, so `//- x: 1` carries
189
- // `x: 1` and the resugaring writes the space back. A marker written
190
- // without it carries the same aontu and is normalised on the round
191
- // trip, which `--check` reports as the drift it is.
192
154
  if (body.startsWith(' ')) {
193
155
  body = body.slice(1)
194
156
  }
@@ -196,16 +158,6 @@ function readLine(line: string, marker: string): Line {
196
158
  }
197
159
 
198
160
 
199
- // THE CANONICAL QUOTE IS CHOSEN PER LINE (D6). A backtick string
200
- // carries `"` and `'` unescaped, which is most of what target code
201
- // holds, so a line takes one unless it holds a backtick itself.
202
- //
203
- // A BACKSLASH IS ESCAPED IN EITHER QUOTE, and that is not what the
204
- // design note assumed: this engine reads the escapes of a backtick
205
- // string exactly as it reads a quoted one, and an unknown escape drops
206
- // its backslash (`\p` is `p`), so a target line holding `\n` inside a
207
- // string of its own would arrive in the output as a newline. Only the
208
- // delimiter differs between the two forms.
209
161
  function quoteLine(text: string): string {
210
162
  const escaped = text.replace(/\\/g, '\\\\')
211
163
  return text.includes('`') ?
@@ -214,10 +166,6 @@ function quoteLine(text: string): string {
214
166
  }
215
167
 
216
168
 
217
- // The line a quoted body element carries, or undefined when the line
218
- // is not one: anything but a lone string literal, and any escape the
219
- // quoting above does not write. The fixpoint test below is what makes
220
- // being conservative here safe -- a line this refuses stays aontu.
221
169
  function unquoteLine(text: string): string | undefined {
222
170
  const t = trimLine(text)
223
171
  if (2 > t.length) {
@@ -249,12 +197,8 @@ function unquoteLine(text: string): string | undefined {
249
197
  }
250
198
 
251
199
 
252
- // DESUGAR: a template file becomes the canonical aontu document.
253
- // A marker line is its own text, at its own indentation; every other
254
- // line is one quoted string, which lands wherever the marker lines
255
- // left a list open.
256
200
  function desugarTemplate(src: string, marker?: string): string {
257
- const mark = marker ?? DEFAULT_MARKER
201
+ const form = markerForm(marker ?? DEFAULT_MARKER)
258
202
  const lines = src.split('\n')
259
203
  // A trailing newline is the file's, not a line of output: a text
260
204
  // file ends with one, and the round trip must not grow an empty
@@ -264,30 +208,22 @@ function desugarTemplate(src: string, marker?: string): string {
264
208
  lines.pop()
265
209
  }
266
210
  const out = lines.map((line) => {
267
- const read = readLine(line, mark)
211
+ const read = readLine(line, form)
268
212
  return read.marker ? read.indent + read.text : quoteLine(read.text)
269
213
  })
270
214
  return out.join('\n') + (tail ? '\n' : '')
271
215
  }
272
216
 
273
217
 
274
- // RESUGAR: the canonical document becomes a template file. Every line
275
- // is a marker line unless it is a body element that survives the
276
- // fixpoint -- rebuild the target line, desugar the rebuild, and take
277
- // it only if that answers the canonical line back.
278
218
  function resugarTemplate(src: string, marker?: string): string {
279
219
  const mark = marker ?? DEFAULT_MARKER
220
+ const form = markerForm(mark)
280
221
  const lines = src.split('\n')
281
222
  const tail = 1 < lines.length && '' === lines[lines.length - 1]
282
223
  if (tail) {
283
224
  lines.pop()
284
225
  }
285
226
  const out = lines.map((line) => {
286
- // THE ELEMENT'S OWN INDENTATION IS NOT PART OF IT: the target line
287
- // is the string's content, and where the canonical form puts the
288
- // element on the page is the formatter's business. Comparing the
289
- // whole line instead would refuse every body element of a document
290
- // `aontu fmt` had indented, which is every document it has seen.
291
227
  const target = unquoteLine(line)
292
228
  if (undefined !== target && desugarTemplate(target, mark) === trimLine(line)) {
293
229
  return target
@@ -296,30 +232,22 @@ function resugarTemplate(src: string, marker?: string): string {
296
232
  // is written after it, so the aontu's own shape is on the page.
297
233
  // The one space is the marker's, which the reading takes back.
298
234
  const text = trimEnd(line)
299
- const close = isBlock(mark) ? ' ' + BLOCK_CLOSE : ''
300
- return '' === text ? mark + close : mark + ' ' + text + close
235
+ const close = '' === form.close ? '' : ' ' + form.close
236
+ return '' === text ? form.open + close :
237
+ form.open + ' ' + text + close
301
238
  })
302
239
  return out.join('\n') + (tail ? '\n' : '')
303
240
  }
304
241
 
305
242
 
306
- // WHICH LINES OF THE DESUGARED DOCUMENT ARE THE TARGET'S. The
307
- // desugaring is line for line, so this is one flag per line of what
308
- // `desugarTemplate` returns: true where the template's line was not a
309
- // marker, and so where the document's line is one quoted line of the
310
- // generated file.
311
- //
312
- // `aontu fmt` reads it (FMT.0.md M-BM-'3.14) to hold those lines on lines of
313
- // their own. A body element is a string like any other, and the packing
314
- // budget would put three of them on one line -- which is aontu where
315
- // three lines of output were, and a generator that writes them as one.
316
243
  function templateOutputs(src: string, marker: string): boolean[] {
244
+ const form = markerForm(marker)
317
245
  const lines = src.split('\n')
318
246
  if (1 < lines.length && '' === lines[lines.length - 1]) {
319
247
  lines.pop()
320
248
  }
321
- return lines.map((line) => !readLine(line, marker).marker)
322
- } /* node:coverage ignore next 9 */
249
+ return lines.map((line) => !readLine(line, form).marker)
250
+ } /* node:coverage ignore next 10 */
323
251
 
324
252
 
325
253
  export {
@@ -327,5 +255,6 @@ export {
327
255
  resugarTemplate,
328
256
  templateOutputs,
329
257
  markerFor,
258
+ markerFromProfiles,
330
259
  DEFAULT_MARKER,
331
260
  }