aontu 0.52.0 → 0.53.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 (273) hide show
  1. package/README.md +88 -0
  2. package/bin/aontu-mcp.js +4 -0
  3. package/dist/agentsmd.d.ts +16 -0
  4. package/dist/agentsmd.js +107 -0
  5. package/dist/agentsmd.js.map +1 -0
  6. package/dist/aontu.d.ts +14 -3
  7. package/dist/aontu.js +145 -4
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +44 -1
  10. package/dist/cli.js +2401 -44
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +16 -0
  13. package/dist/ctx.js +44 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/diff.d.ts +22 -0
  16. package/dist/diff.js +141 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/err.d.ts +3 -1
  19. package/dist/err.js +48 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +16 -0
  22. package/dist/graph.js +73 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +146 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +223 -5
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +391 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +698 -35
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +262 -46
  36. package/dist/lsp.js.map +1 -1
  37. package/dist/mcp-server.d.ts +20 -0
  38. package/dist/mcp-server.js +147 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp.d.ts +42 -0
  41. package/dist/mcp.js +814 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +498 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +31 -0
  47. package/dist/mod.js +250 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +44 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +40 -0
  53. package/dist/provenance.js +335 -0
  54. package/dist/provenance.js.map +1 -0
  55. package/dist/query.d.ts +27 -0
  56. package/dist/query.js +294 -0
  57. package/dist/query.js.map +1 -0
  58. package/dist/reach.d.ts +14 -0
  59. package/dist/reach.js +140 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +19 -0
  62. package/dist/relation.js +305 -0
  63. package/dist/relation.js.map +1 -0
  64. package/dist/report-sarif.d.ts +14 -0
  65. package/dist/report-sarif.js +102 -0
  66. package/dist/report-sarif.js.map +1 -0
  67. package/dist/site.d.ts +4 -0
  68. package/dist/site.js +31 -0
  69. package/dist/site.js.map +1 -1
  70. package/dist/std.d.ts +1 -0
  71. package/dist/std.js +73 -0
  72. package/dist/std.js.map +1 -0
  73. package/dist/subsume.d.ts +39 -0
  74. package/dist/subsume.js +526 -0
  75. package/dist/subsume.js.map +1 -0
  76. package/dist/trim.d.ts +19 -0
  77. package/dist/trim.js +155 -0
  78. package/dist/trim.js.map +1 -0
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/dist/type.d.ts +17 -1
  81. package/dist/type.js.map +1 -1
  82. package/dist/unify.d.ts +3 -1
  83. package/dist/unify.js +287 -18
  84. package/dist/unify.js.map +1 -1
  85. package/dist/utility.d.ts +9 -1
  86. package/dist/utility.js +122 -1
  87. package/dist/utility.js.map +1 -1
  88. package/dist/val/AggFuncVal.d.ts +33 -0
  89. package/dist/val/AggFuncVal.js +202 -0
  90. package/dist/val/AggFuncVal.js.map +1 -0
  91. package/dist/val/ArithFuncVal.d.ts +31 -0
  92. package/dist/val/ArithFuncVal.js +62 -0
  93. package/dist/val/ArithFuncVal.js.map +1 -0
  94. package/dist/val/BagVal.d.ts +5 -0
  95. package/dist/val/BagVal.js +96 -5
  96. package/dist/val/BagVal.js.map +1 -1
  97. package/dist/val/CloseFuncVal.js +9 -1
  98. package/dist/val/CloseFuncVal.js.map +1 -1
  99. package/dist/val/ConjunctVal.d.ts +1 -1
  100. package/dist/val/ConjunctVal.js +19 -0
  101. package/dist/val/ConjunctVal.js.map +1 -1
  102. package/dist/val/ConstraintVal.d.ts +48 -1
  103. package/dist/val/ConstraintVal.js +1501 -110
  104. package/dist/val/ConstraintVal.js.map +1 -1
  105. package/dist/val/CopyFuncVal.d.ts +1 -2
  106. package/dist/val/CopyFuncVal.js +7 -0
  107. package/dist/val/CopyFuncVal.js.map +1 -1
  108. package/dist/val/Decimal.d.ts +1 -0
  109. package/dist/val/Decimal.js +13 -0
  110. package/dist/val/Decimal.js.map +1 -1
  111. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  112. package/dist/val/DeprecateFuncVal.js +47 -0
  113. package/dist/val/DeprecateFuncVal.js.map +1 -0
  114. package/dist/val/DisjunctVal.js +130 -21
  115. package/dist/val/DisjunctVal.js.map +1 -1
  116. package/dist/val/EachFuncVal.d.ts +15 -0
  117. package/dist/val/EachFuncVal.js +75 -0
  118. package/dist/val/EachFuncVal.js.map +1 -0
  119. package/dist/val/ExpectVal.d.ts +1 -0
  120. package/dist/val/ExpectVal.js +41 -4
  121. package/dist/val/ExpectVal.js.map +1 -1
  122. package/dist/val/FeatureVal.js +1 -1
  123. package/dist/val/FeatureVal.js.map +1 -1
  124. package/dist/val/FilterFuncVal.d.ts +15 -0
  125. package/dist/val/FilterFuncVal.js +91 -0
  126. package/dist/val/FilterFuncVal.js.map +1 -0
  127. package/dist/val/FuncBaseVal.d.ts +6 -1
  128. package/dist/val/FuncBaseVal.js +178 -2
  129. package/dist/val/FuncBaseVal.js.map +1 -1
  130. package/dist/val/HideFuncVal.js.map +1 -1
  131. package/dist/val/IdFuncVal.d.ts +13 -0
  132. package/dist/val/IdFuncVal.js +54 -0
  133. package/dist/val/IdFuncVal.js.map +1 -0
  134. package/dist/val/JunctionVal.js +7 -1
  135. package/dist/val/JunctionVal.js.map +1 -1
  136. package/dist/val/KeyFuncVal.d.ts +1 -1
  137. package/dist/val/KeyFuncVal.js +38 -30
  138. package/dist/val/KeyFuncVal.js.map +1 -1
  139. package/dist/val/ListVal.js +117 -17
  140. package/dist/val/ListVal.js.map +1 -1
  141. package/dist/val/LowerFuncVal.js.map +1 -1
  142. package/dist/val/MapVal.js +102 -8
  143. package/dist/val/MapVal.js.map +1 -1
  144. package/dist/val/MatchFuncVal.d.ts +15 -0
  145. package/dist/val/MatchFuncVal.js +107 -0
  146. package/dist/val/MatchFuncVal.js.map +1 -0
  147. package/dist/val/MoveFuncVal.js.map +1 -1
  148. package/dist/val/NilVal.js +24 -0
  149. package/dist/val/NilVal.js.map +1 -1
  150. package/dist/val/OpBaseVal.d.ts +1 -1
  151. package/dist/val/OpBaseVal.js +24 -2
  152. package/dist/val/OpBaseVal.js.map +1 -1
  153. package/dist/val/OpenFuncVal.js +4 -1
  154. package/dist/val/OpenFuncVal.js.map +1 -1
  155. package/dist/val/PackFuncVal.d.ts +15 -0
  156. package/dist/val/PackFuncVal.js +108 -0
  157. package/dist/val/PackFuncVal.js.map +1 -0
  158. package/dist/val/PathFuncVal.js.map +1 -1
  159. package/dist/val/PlaceVal.d.ts +13 -0
  160. package/dist/val/PlaceVal.js +131 -0
  161. package/dist/val/PlaceVal.js.map +1 -0
  162. package/dist/val/PlusOpVal.js +11 -2
  163. package/dist/val/PlusOpVal.js.map +1 -1
  164. package/dist/val/PrefFuncVal.js.map +1 -1
  165. package/dist/val/PrefVal.d.ts +2 -2
  166. package/dist/val/PrefVal.js +78 -23
  167. package/dist/val/PrefVal.js.map +1 -1
  168. package/dist/val/RefVal.d.ts +1 -1
  169. package/dist/val/RefVal.js +158 -30
  170. package/dist/val/RefVal.js.map +1 -1
  171. package/dist/val/ReferFuncVal.d.ts +36 -0
  172. package/dist/val/ReferFuncVal.js +303 -0
  173. package/dist/val/ReferFuncVal.js.map +1 -0
  174. package/dist/val/ScalarKindVal.d.ts +1 -2
  175. package/dist/val/ScalarKindVal.js +0 -11
  176. package/dist/val/ScalarKindVal.js.map +1 -1
  177. package/dist/val/TopVal.js.map +1 -1
  178. package/dist/val/TypeFuncVal.js.map +1 -1
  179. package/dist/val/UpperFuncVal.js.map +1 -1
  180. package/dist/val/Val.d.ts +9 -2
  181. package/dist/val/Val.js +150 -4
  182. package/dist/val/Val.js.map +1 -1
  183. package/dist/val/VarVal.js.map +1 -1
  184. package/dist/val/arith.d.ts +6 -0
  185. package/dist/val/arith.js +170 -0
  186. package/dist/val/arith.js.map +1 -0
  187. package/dist/vet.d.ts +45 -0
  188. package/dist/vet.js +776 -0
  189. package/dist/vet.js.map +1 -0
  190. package/dist/walk.d.ts +2 -0
  191. package/dist/walk.js +91 -0
  192. package/dist/walk.js.map +1 -0
  193. package/grammar/aontu.gbnf +130 -0
  194. package/grammar/aontu.lark +113 -0
  195. package/package.json +30 -15
  196. package/skill/SKILL.md +37 -0
  197. package/skill/error-codes.md +62 -0
  198. package/skill/examples.md +99 -0
  199. package/skill/grammar-card.md +57 -0
  200. package/src/agentsmd.ts +135 -0
  201. package/src/aontu.ts +192 -4
  202. package/src/cli.ts +2858 -71
  203. package/src/ctx.ts +81 -0
  204. package/src/diff.ts +196 -0
  205. package/src/err.ts +52 -8
  206. package/src/graph.ts +135 -0
  207. package/src/hcanon.ts +169 -0
  208. package/src/hints.ts +271 -5
  209. package/src/jsonschema.ts +511 -0
  210. package/src/lang.ts +779 -37
  211. package/src/lsp.ts +281 -47
  212. package/src/mcp-server.ts +187 -0
  213. package/src/mcp.ts +993 -0
  214. package/src/mod-tool.ts +679 -0
  215. package/src/mod.ts +344 -0
  216. package/src/patch.ts +624 -0
  217. package/src/provenance.ts +430 -0
  218. package/src/query.ts +379 -0
  219. package/src/reach.ts +184 -0
  220. package/src/relation.ts +395 -0
  221. package/src/report-sarif.ts +137 -0
  222. package/src/site.ts +36 -1
  223. package/src/std.ts +73 -0
  224. package/src/subsume.ts +690 -0
  225. package/src/trim.ts +195 -0
  226. package/src/tsconfig.json +10 -4
  227. package/src/type.ts +51 -2
  228. package/src/unify.ts +311 -16
  229. package/src/utility.ts +139 -1
  230. package/src/val/AggFuncVal.ts +319 -0
  231. package/src/val/ArithFuncVal.ts +108 -0
  232. package/src/val/BagVal.ts +101 -4
  233. package/src/val/CloseFuncVal.ts +9 -1
  234. package/src/val/ConjunctVal.ts +20 -0
  235. package/src/val/ConstraintVal.ts +1699 -116
  236. package/src/val/CopyFuncVal.ts +7 -1
  237. package/src/val/Decimal.ts +15 -0
  238. package/src/val/DeprecateFuncVal.ts +84 -0
  239. package/src/val/DisjunctVal.ts +139 -28
  240. package/src/val/EachFuncVal.ts +133 -0
  241. package/src/val/ExpectVal.ts +43 -6
  242. package/src/val/FeatureVal.ts +1 -1
  243. package/src/val/FilterFuncVal.ts +154 -0
  244. package/src/val/FuncBaseVal.ts +200 -3
  245. package/src/val/HideFuncVal.ts +0 -2
  246. package/src/val/IdFuncVal.ts +91 -0
  247. package/src/val/JunctionVal.ts +7 -1
  248. package/src/val/KeyFuncVal.ts +39 -35
  249. package/src/val/ListVal.ts +125 -18
  250. package/src/val/LowerFuncVal.ts +0 -1
  251. package/src/val/MapVal.ts +110 -8
  252. package/src/val/MatchFuncVal.ts +176 -0
  253. package/src/val/MoveFuncVal.ts +0 -2
  254. package/src/val/NilVal.ts +25 -0
  255. package/src/val/OpBaseVal.ts +26 -3
  256. package/src/val/OpenFuncVal.ts +4 -2
  257. package/src/val/PackFuncVal.ts +175 -0
  258. package/src/val/PathFuncVal.ts +0 -1
  259. package/src/val/PlaceVal.ts +193 -0
  260. package/src/val/PlusOpVal.ts +11 -2
  261. package/src/val/PrefFuncVal.ts +0 -1
  262. package/src/val/PrefVal.ts +79 -36
  263. package/src/val/RefVal.ts +163 -30
  264. package/src/val/ReferFuncVal.ts +387 -0
  265. package/src/val/ScalarKindVal.ts +0 -13
  266. package/src/val/TopVal.ts +0 -1
  267. package/src/val/TypeFuncVal.ts +0 -2
  268. package/src/val/UpperFuncVal.ts +0 -1
  269. package/src/val/Val.ts +213 -3
  270. package/src/val/VarVal.ts +0 -1
  271. package/src/val/arith.ts +316 -0
  272. package/src/vet.ts +992 -0
  273. package/src/walk.ts +99 -0
package/src/mod.ts ADDED
@@ -0,0 +1,344 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // MODULE IDENTITY AND LOCAL RESOLUTION (G6 phase 2,
4
+ // docs/capability-review/g6-distribution.md).
5
+ //
6
+ // An import is still just `@"…"`; the string's SHAPE routes it, so the
7
+ // grammar is untouched and every existing include keeps its exact
8
+ // behaviour:
9
+ //
10
+ // service: @"corp.example/schemas/service@1"
11
+ // frozen: @"corp.example/schemas/service@1#aon1-4vJemVYtWFR2mQeN…"
12
+ // local: @"./fragment.aon" <- unchanged, not a module
13
+ //
14
+ // EVALUATION NEVER TOUCHES THE NETWORK. Resolution reads local stores
15
+ // only: `aon_vendor/` beside the project's `mod.aon`, then a
16
+ // content-addressed user cache keyed by canon-hash. Fetching is a
17
+ // separate, explicit tool step, and a module that is in neither store
18
+ // is an evaluation error that says so.
19
+ //
20
+ // TWO PINS, TWO ROLES. The lockfile's `oci` digest certifies that these
21
+ // are the bytes the registry served; the `canon` hash certifies that
22
+ // this is the MEANING that was reviewed. Only the second can be checked
23
+ // locally without the registry, and it is the one this file checks: the
24
+ // module is unified standalone and its canon-hash compared with the
25
+ // pin. An inline `#aon1-…` fragment is the same check without a
26
+ // lockfile — the degenerate mode for single-file and agent-sandbox use.
27
+
28
+ import { join as pathJoin, dirname as pathDirname } from 'node:path'
29
+
30
+
31
+ // A module import, as the string spells it.
32
+ export type ModuleRef = {
33
+ // The module path WITHOUT the major: `corp.example/schemas/service`.
34
+ path: string
35
+ // The major version, from the `@N` suffix.
36
+ major: number
37
+ // The inline canon-hash pin, if the import froze one.
38
+ hash?: string
39
+ }
40
+
41
+
42
+ // A file store the resolver can read. The engine passes its own `fs`
43
+ // when the host injected one, so a sandboxed evaluation stays in the
44
+ // filesystem the host gave it.
45
+ export type ModuleFs = {
46
+ existsSync: (p: string) => boolean
47
+ readFileSync: (p: string, enc: string) => string
48
+ }
49
+
50
+
51
+ // A module path is DOMAIN-SHAPED — the first segment carries a dot,
52
+ // which is what tells it apart from `./local.aon`, `pkg-name` and every
53
+ // other spelling already in use — and carries the major version in the
54
+ // path, CUE/Go-style, so two majors are two modules.
55
+ //
56
+ // The pattern is deliberately narrow: anything it does not match falls
57
+ // through to the existing resolver chain unchanged, so no document that
58
+ // worked before this phase can be routed somewhere new by it.
59
+ const MODULE_RE =
60
+ /^([a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+(?:\/[A-Za-z0-9._-]+)*)@(\d+)(?:#(aon1-[A-Za-z0-9_-]+))?$/
61
+
62
+
63
+ export function parseModuleRef(spec: string): ModuleRef | undefined {
64
+ const m = MODULE_RE.exec(spec)
65
+ if (null == m) {
66
+ return undefined
67
+ }
68
+ return {
69
+ path: m[1],
70
+ major: +m[2],
71
+ ...(null == m[3] ? {} : { hash: m[3] }),
72
+ }
73
+ }
74
+
75
+
76
+ // The directory a module's files live in, under a store root.
77
+ export function moduleDir(store: string, ref: ModuleRef): string {
78
+ return pathJoin(store, ...ref.path.split('/')) + '@' + ref.major
79
+ }
80
+
81
+
82
+ // EVERY project root at or above `from`, innermost first — a project
83
+ // root being a directory holding a `mod.aon`. This used to answer with
84
+ // the NEAREST one alone, and the plural is the fix, because a
85
+ // VENDORED MODULE IS A PROJECT INSIDE A PROJECT. A module in
86
+ // `aon_vendor/` carries its own `mod.aon`, which stopped the upward
87
+ // walk there, so a nested import resolved against the vendored
88
+ // module's own directory: a tree with no `aon_vendor/` of its own, and
89
+ // therefore a `module not fetched` for a dependency sitting flat
90
+ // beside it in the CONSUMER's vendor tree — the only layout `mod
91
+ // vendor` produces (use-cases/BUGS.md §31).
92
+ //
93
+ // The consumer's stores are searched after the module's own, so a
94
+ // module that vendors its dependencies nested still wins for its own
95
+ // tree, and one that does not falls through to the consumer that
96
+ // vendored it. The last element is `from` itself when nothing above it
97
+ // declares a module, which is the single-file inline-pin mode.
98
+ export function projectRoots(from: string, fs: ModuleFs): string[] {
99
+ const roots: string[] = []
100
+ let dir = from
101
+ for (; ;) {
102
+ if (fs.existsSync(pathJoin(dir, 'mod.aon'))) {
103
+ roots.push(dir)
104
+ }
105
+ const up = pathDirname(dir)
106
+ if (up === dir) {
107
+ return 0 < roots.length ? roots : [from]
108
+ }
109
+ dir = up
110
+ }
111
+ }
112
+
113
+
114
+ // The lockfile's pin for one import, or undefined.
115
+ //
116
+ // `mod-lock.aon` is machine-written CANONICAL Aontu, and canonical
117
+ // Aontu whose leaves are scalars IS JSON — which is why reading it here
118
+ // needs no evaluator, and why a hand-edited lockfile that is no longer
119
+ // canonical simply does not parse. It is generated; the file says so.
120
+ // The lockfile's JSON: its canonical line, with the generated-file
121
+ // header stripped. The file is AONTU, so it may carry `#` comments —
122
+ // and the header `aontu mod tidy` writes says not to edit it, which is
123
+ // worth more than the two lines it costs to skip. Everything below the
124
+ // comments is the canonical map, and canonical Aontu whose leaves are
125
+ // scalars is JSON.
126
+ export function lockJson(text: string): string {
127
+ return text
128
+ .split('\n')
129
+ .filter((line) => !line.trimStart().startsWith('#'))
130
+ .join('\n')
131
+ }
132
+
133
+
134
+ // The user cache: `$XDG_CACHE_HOME/aontu/mod` unless the host names
135
+ // another, else the platform's own cache location. A host with nowhere
136
+ // to put one has no cache, which is a miss rather than a failure. One
137
+ // rule, in one place: the resolver reads this cache during evaluation
138
+ // and `aontu mod` writes into it, and two spellings of "where the cache
139
+ // is" is one bug.
140
+ export function modCacheDir(): string | undefined {
141
+ return modCacheDirFor(process.platform, process.env)
142
+ }
143
+
144
+
145
+ // That rule with the platform and the environment PASSED IN, so the
146
+ // Windows arm can be exercised off Windows — the only way a rule about
147
+ // a platform nobody here runs gets tested at all. The Go port splits
148
+ // the same way (modCacheDirFor, go/aontu.go).
149
+ //
150
+ // THE ORDER IS EXPLICIT BEFORE IMPLICIT, and LOCALAPPDATA is LAST.
151
+ // XDG_CACHE_HOME is the override and wins everywhere, Windows included:
152
+ // a caller who names a cache directory means it. HOME comes next and is
153
+ // also honoured on Windows, where it is not standard but IS set by Git
154
+ // Bash and by most development shells — a user who has one expects
155
+ // their tools to agree about where home is.
156
+ //
157
+ // LOCALAPPDATA is the PLATFORM DEFAULT beneath both, which is the whole
158
+ // addition: Windows sets neither XDG_CACHE_HOME nor HOME by default —
159
+ // it supplies USERPROFILE and LOCALAPPDATA, and LOCALAPPDATA is what a
160
+ // cache directory means there — so a rule that knew only the first two
161
+ // left every Windows user with NO cache. Putting it ABOVE HOME was the
162
+ // first attempt and was wrong: it made an explicitly set HOME
163
+ // unreachable on Windows, which broke the existing fallback test the
164
+ // moment CI ran it. A platform default that overrides what the
165
+ // environment was told is not a default.
166
+ export function modCacheDirFor(
167
+ platform: string,
168
+ env: Record<string, string | undefined>,
169
+ ): string | undefined {
170
+ const xdg = env.XDG_CACHE_HOME
171
+ if ('string' === typeof xdg && '' !== xdg) {
172
+ return pathJoin(xdg, 'aontu', 'mod')
173
+ }
174
+ const home = env.HOME
175
+ if ('string' === typeof home && '' !== home) {
176
+ return pathJoin(home, '.cache', 'aontu', 'mod')
177
+ }
178
+ if ('win32' === platform) {
179
+ const local = env.LOCALAPPDATA
180
+ if ('string' === typeof local && '' !== local) {
181
+ return pathJoin(local, 'aontu', 'mod')
182
+ }
183
+ }
184
+ return undefined
185
+ }
186
+
187
+
188
+ export function lockHash(root: string, ref: ModuleRef, fs: ModuleFs):
189
+ string | undefined {
190
+ const file = pathJoin(root, 'mod-lock.aon')
191
+ if (!fs.existsSync(file)) {
192
+ return undefined
193
+ }
194
+
195
+ let lock: any
196
+ try {
197
+ lock = JSON.parse(lockJson(fs.readFileSync(file, 'utf8')))
198
+ }
199
+ catch {
200
+ return undefined
201
+ }
202
+
203
+ const entry = lock?.lock?.[ref.path + '@' + ref.major]
204
+ return 'string' === typeof entry?.canon ? entry.canon : undefined
205
+ }
206
+
207
+
208
+ // What a module resolution needs from the engine: evaluate a source
209
+ // standalone and answer both what it MEANS (the generated value, for
210
+ // reading a module file's own metadata) and what its meaning HASHES to
211
+ // (for the integrity check). Injected rather than imported, because
212
+ // this is EVALUATION — the very thing this file is called from the
213
+ // middle of — and a module resolver that imported the evaluator would
214
+ // close a cycle around the whole language.
215
+ export type ModuleEval =
216
+ (src: string, path: string) => { gen: any, hash: string }
217
+
218
+
219
+ // How deep module verification may nest before it is refused. A module
220
+ // is verified by EVALUATING it, and that evaluation resolves the
221
+ // module's own imports -- so a vendor tree that leads back to itself
222
+ // (a symlink is enough) would recurse until the host's stack gave out,
223
+ // and a verdict that depends on the host's stack size is exactly what
224
+ // the determinism clause forbids (docs/trust.md, and the same argument
225
+ // unify_cycle rests on). Sixteen is far above any real vendor nesting.
226
+ export const MODULE_MAX_DEPTH = 16
227
+
228
+
229
+ export type ModuleOptions = {
230
+ // The content-addressed user cache, keyed by canon-hash. Consulted
231
+ // only when the expected hash is known, which is what "content
232
+ // addressed" means: without a pin there is no address.
233
+ cache?: string
234
+ // The standalone evaluator, for reading module files and for the
235
+ // integrity check. Always present: Aontu injects it (ts/src/aontu.ts)
236
+ // because only the class that evaluates can answer what a module
237
+ // MEANS, and the resolver runs inside a parse that class started.
238
+ eval: ModuleEval
239
+ // How many module verifications deep this evaluation already is.
240
+ depth?: number
241
+ }
242
+
243
+
244
+ export type ModuleFound = {
245
+ // The module's main file, as an absolute path.
246
+ full: string
247
+ src: string
248
+ }
249
+
250
+
251
+ // A refusal that carries its code to the parse layer, exactly as a
252
+ // denied include does (makeModelResolver's `deny`): the resolver
253
+ // THROWS, so a bare-member module import cannot vanish in the merge and
254
+ // leave a plausible, silently-partial document.
255
+ function refuse(code: string, message: string): never {
256
+ const err: any = new Error(message)
257
+ err.code = code
258
+ throw err
259
+ }
260
+
261
+
262
+ // Resolve one module import against the local stores.
263
+ export function resolveModule(
264
+ ref: ModuleRef,
265
+ fromDir: string,
266
+ fs: ModuleFs,
267
+ options: ModuleOptions,
268
+ ): ModuleFound {
269
+ if (MODULE_MAX_DEPTH <= (options.depth ?? 0)) {
270
+ refuse('module_depth',
271
+ 'module depth: ' + ref.path + '@' + ref.major +
272
+ ' (verification nested past ' + MODULE_MAX_DEPTH + ')')
273
+ }
274
+
275
+ // EVERY enclosing project, innermost first (see projectRoots): a
276
+ // vendored module is a project inside a project, and its nested
277
+ // imports have to reach the tree the consumer vendored them into.
278
+ const roots = projectRoots(fromDir, fs)
279
+ // The PIN comes from the first lockfile that names this import. A
280
+ // vendored module usually ships none, so that is the consumer's --
281
+ // which is right: the consumer's lock is what its build is pinned to.
282
+ const expect = ref.hash ??
283
+ roots.map((r) => lockHash(r, ref, fs)).find((h) => null != h)
284
+
285
+ const stores: string[] =
286
+ roots.map((r) => moduleDir(pathJoin(r, 'aon_vendor'), ref))
287
+ if (null != options.cache && null != expect) {
288
+ // Content-addressed: the cache is keyed by the hash, so a cache hit
289
+ // is already the right MEANING before anything is read from it.
290
+ stores.push(pathJoin(options.cache, expect))
291
+ }
292
+
293
+ const dir = stores.find((d) => fs.existsSync(pathJoin(d, 'mod.aon')))
294
+ if (undefined === dir) {
295
+ // The wording is the contract (docs/capability-review/
296
+ // g6-distribution.md): it names the module AND the step that fixes
297
+ // it, because an agent reading this error is the audience.
298
+ refuse('module_missing',
299
+ 'module not fetched: ' + ref.path + '@' + ref.major +
300
+ ' (run: aontu mod get)')
301
+ }
302
+
303
+ // The module's own `mod.aon` names its entry file. Read with the
304
+ // evaluator rather than a regexp: a module file is ordinary Aontu,
305
+ // and the language reading its own metadata is the point.
306
+ const main = moduleMain(pathJoin(dir, 'mod.aon'), fs, options)
307
+ const full = pathJoin(dir, main)
308
+
309
+ if (!fs.existsSync(full)) {
310
+ refuse('module_missing',
311
+ 'module not fetched: ' + ref.path + '@' + ref.major +
312
+ ' (run: aontu mod get)')
313
+ }
314
+
315
+ const src = fs.readFileSync(full, 'utf8')
316
+
317
+ if (null != expect) {
318
+ // VERIFICATION IS ALWAYS LOCAL. The registry's annotation is
319
+ // advisory; what decides is the hash of the module as it is on this
320
+ // machine, recomputed now.
321
+ const got = options.eval(src, full).hash
322
+ if (got !== expect) {
323
+ refuse('module_integrity',
324
+ 'module integrity: ' + ref.path + '@' + ref.major +
325
+ ' expected ' + expect + ' got ' + got)
326
+ }
327
+ }
328
+
329
+ return { full, src }
330
+ }
331
+
332
+
333
+ // The `mod.main` a module file declares, or the default entry name.
334
+ // The module file is ORDINARY AONTU, read by the language itself — the
335
+ // toolchain dogfooding its own evaluator rather than pattern-matching
336
+ // its own syntax with a regexp.
337
+ function moduleMain(file: string, fs: ModuleFs, options: ModuleOptions): string {
338
+ const gen: any = options.eval(fs.readFileSync(file, 'utf8'), file).gen
339
+ const main = gen?.mod?.main
340
+ return 'string' === typeof main && '' !== main ? main : DEFAULT_MAIN
341
+ }
342
+
343
+
344
+ const DEFAULT_MAIN = 'main.aon'