aontu 0.63.0 → 0.65.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 (101) hide show
  1. package/README.md +8 -8
  2. package/dist/agentsmd.js +3 -3
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/aontu.d.ts +3 -3
  5. package/dist/aontu.js +4 -6
  6. package/dist/aontu.js.map +1 -1
  7. package/dist/aontumodel.js +6 -12
  8. package/dist/aontumodel.js.map +1 -1
  9. package/dist/casing.d.ts +5 -0
  10. package/dist/casing.js +90 -0
  11. package/dist/casing.js.map +1 -0
  12. package/dist/cli.d.ts +16 -3
  13. package/dist/cli.js +653 -455
  14. package/dist/cli.js.map +1 -1
  15. package/dist/compat.d.ts +7 -0
  16. package/dist/compat.js +113 -0
  17. package/dist/compat.js.map +1 -0
  18. package/dist/helpdoc.js +5 -5
  19. package/dist/helpdoc.js.map +1 -1
  20. package/dist/hints.js +14 -5
  21. package/dist/hints.js.map +1 -1
  22. package/dist/lang.d.ts +1 -0
  23. package/dist/lang.js +8 -0
  24. package/dist/lang.js.map +1 -1
  25. package/dist/lsp.d.ts +1 -1
  26. package/dist/mcp.js +0 -36
  27. package/dist/mcp.js.map +1 -1
  28. package/dist/mod.d.ts +18 -3
  29. package/dist/mod.js +117 -46
  30. package/dist/mod.js.map +1 -1
  31. package/dist/pkg-net.d.ts +227 -0
  32. package/dist/pkg-net.js +1598 -0
  33. package/dist/pkg-net.js.map +1 -0
  34. package/dist/pkg-zip.d.ts +8 -0
  35. package/dist/pkg-zip.js +196 -0
  36. package/dist/pkg-zip.js.map +1 -0
  37. package/dist/pkg.d.ts +159 -0
  38. package/dist/pkg.js +767 -0
  39. package/dist/pkg.js.map +1 -0
  40. package/dist/profile.d.ts +9 -0
  41. package/dist/profile.js +28 -0
  42. package/dist/profile.js.map +1 -0
  43. package/dist/report-sarif.js +1 -1
  44. package/dist/report-sarif.js.map +1 -1
  45. package/dist/subsume.d.ts +3 -1
  46. package/dist/subsume.js +2 -0
  47. package/dist/subsume.js.map +1 -1
  48. package/dist/trace.d.ts +21 -0
  49. package/dist/trace.js +107 -0
  50. package/dist/trace.js.map +1 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/dist/unify.js +1 -6
  53. package/dist/unify.js.map +1 -1
  54. package/dist/val/CmpFuncVal.d.ts +2 -0
  55. package/dist/val/CmpFuncVal.js +47 -13
  56. package/dist/val/CmpFuncVal.js.map +1 -1
  57. package/dist/val/NomFuncVal.js +11 -13
  58. package/dist/val/NomFuncVal.js.map +1 -1
  59. package/dist/val/ReferFuncVal.js +3 -0
  60. package/dist/val/ReferFuncVal.js.map +1 -1
  61. package/package.json +1 -1
  62. package/skill/SKILL.md +3 -3
  63. package/skill/error-codes.md +4 -4
  64. package/skill/examples.md +4 -4
  65. package/skill/grammar-card.md +1 -1
  66. package/skill/init/check.sh +2 -2
  67. package/skill/tasks.md +14 -12
  68. package/src/agentsmd.ts +3 -3
  69. package/src/aontu.ts +3 -8
  70. package/src/aontumodel.ts +6 -12
  71. package/src/casing.ts +95 -0
  72. package/src/cli.ts +758 -495
  73. package/src/compat.ts +148 -0
  74. package/src/helpdoc.ts +5 -5
  75. package/src/hints.ts +14 -7
  76. package/src/lang.ts +9 -1
  77. package/src/mcp.ts +0 -39
  78. package/src/mod.ts +142 -51
  79. package/src/pkg-net.ts +1945 -0
  80. package/src/pkg-zip.ts +228 -0
  81. package/src/pkg.ts +1018 -0
  82. package/src/profile.ts +42 -0
  83. package/src/report-sarif.ts +1 -1
  84. package/src/subsume.ts +2 -2
  85. package/src/trace.ts +157 -0
  86. package/src/unify.ts +1 -6
  87. package/src/val/CmpFuncVal.ts +66 -20
  88. package/src/val/NomFuncVal.ts +3 -5
  89. package/src/val/ReferFuncVal.ts +4 -0
  90. package/dist/lower.d.ts +0 -23
  91. package/dist/lower.js +0 -528
  92. package/dist/lower.js.map +0 -1
  93. package/dist/mod-tool.d.ts +0 -58
  94. package/dist/mod-tool.js +0 -403
  95. package/dist/mod-tool.js.map +0 -1
  96. package/dist/render.d.ts +0 -53
  97. package/dist/render.js +0 -432
  98. package/dist/render.js.map +0 -1
  99. package/src/lower.ts +0 -586
  100. package/src/mod-tool.ts +0 -561
  101. package/src/render.ts +0 -580
package/src/mod.ts CHANGED
@@ -5,9 +5,9 @@ import { join as pathJoin, dirname as pathDirname } from 'node:path'
5
5
 
6
6
 
7
7
  export type ModuleRef = {
8
- // The module path WITHOUT the major: `corp.example/schemas/service`.
8
+ // The package path, or the alias key `alias:<name>` when the import
9
+ // spells one; an alias is resolved by lookup, never by shape.
9
10
  path: string
10
- major: number
11
11
  // The inline canon-hash pin, if the import froze one.
12
12
  hash?: string
13
13
  }
@@ -19,23 +19,32 @@ export type ModuleFs = {
19
19
  }
20
20
 
21
21
 
22
+ // A package path is `<domain>/<path>` and carries no major (ADR-022).
22
23
  const MODULE_RE =
23
- /^([a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+(?:\/[A-Za-z0-9._-]+)*)@(\d+)(?:#(aon1-[A-Za-z0-9_-]+))?$/
24
+ /^([a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+(?:\/[A-Za-z0-9._-]+)*)(?:#(aon1-[A-Za-z0-9_-]+))?$/
25
+
26
+ const ALIAS_RE = /^(alias:[A-Za-z0-9._-]+)(?:#(aon1-[A-Za-z0-9_-]+))?$/
27
+
28
+ export const ALIAS_PREFIX = 'alias:'
24
29
 
25
30
 
26
31
  export function parseModuleRef(spec: string): ModuleRef | undefined {
27
- const m = MODULE_RE.exec(spec)
32
+ const m = MODULE_RE.exec(spec) ?? ALIAS_RE.exec(spec)
28
33
  if (null == m) {
29
34
  return undefined
30
35
  }
31
36
  return {
32
37
  path: m[1],
33
- major: +m[2],
34
- ...(null == m[3] ? {} : { hash: m[3] }),
38
+ ...(null == m[2] ? {} : { hash: m[2] }),
35
39
  }
36
40
  }
37
41
 
38
42
 
43
+ export function isAlias(path: string): boolean {
44
+ return path.startsWith(ALIAS_PREFIX)
45
+ }
46
+
47
+
39
48
  export const MODULE_MAX_PATH = 512
40
49
  export const MODULE_MAX_ELEMS = 32
41
50
 
@@ -73,21 +82,42 @@ export function validateModulePath(path: string): string | undefined {
73
82
  }
74
83
 
75
84
 
76
- function escapeElem(elem: string): string {
85
+ // The final element of a routed path, when it carries an extension the
86
+ // include table knows, was meant as a file: the commonest mistake once
87
+ // the major left the name (ADR-022 part 4), so it gets its own message.
88
+ export function localFileExt(path: string): string | undefined {
89
+ const last = path.split('/').pop() as string
90
+ const m = /\.([^.]+)$/.exec(last)
91
+ return null == m ? undefined : m[1].toLowerCase()
92
+ }
93
+
94
+
95
+ export function escapeElem(elem: string): string {
77
96
  return elem.replace(/[A-Z]/g, (c) => '!' + c.toLowerCase())
78
97
  }
79
98
 
80
99
 
81
- export function moduleDir(store: string, ref: ModuleRef): string {
82
- return pathJoin(store, ...ref.path.split('/').map(escapeElem)) + '@' + ref.major
100
+ // The directory a key lives at under a store: one directory per
101
+ // element, uppercase escaped; an alias under `alias/<name>`, which no
102
+ // package path can spell because a domain carries a dot.
103
+ export function moduleDir(store: string, path: string): string {
104
+ const elems = isAlias(path) ?
105
+ ['alias', path.slice(ALIAS_PREFIX.length)] : path.split('/')
106
+ return pathJoin(store, ...elems.map(escapeElem))
83
107
  }
84
108
 
85
109
 
110
+ export const PKG_FILE = 'pkg.aon'
111
+ export const LOCK_FILE = 'pkg-lock.aon'
112
+ export const META_DIR = 'aontu_meta'
113
+ export const VENDOR_DIR = 'vendor'
114
+
115
+
86
116
  export function projectRoots(from: string, fs: ModuleFs): string[] {
87
117
  const roots: string[] = []
88
118
  let dir = from
89
119
  for (; ;) {
90
- if (fs.existsSync(pathJoin(dir, 'mod.aon'))) {
120
+ if (fs.existsSync(pathJoin(dir, PKG_FILE))) {
91
121
  roots.push(dir)
92
122
  }
93
123
  const up = pathDirname(dir)
@@ -118,25 +148,42 @@ export function modCacheDirFor(
118
148
  ): string | undefined {
119
149
  const xdg = env.XDG_CACHE_HOME
120
150
  if ('string' === typeof xdg && '' !== xdg) {
121
- return pathJoin(xdg, 'aontu', 'mod')
151
+ return pathJoin(xdg, 'aontu', 'pkg')
122
152
  }
123
153
  const home = env.HOME
124
154
  if ('string' === typeof home && '' !== home) {
125
- return pathJoin(home, '.cache', 'aontu', 'mod')
155
+ return pathJoin(home, '.cache', 'aontu', 'pkg')
126
156
  }
127
157
  if ('win32' === platform) {
128
158
  const local = env.LOCALAPPDATA
129
159
  if ('string' === typeof local && '' !== local) {
130
- return pathJoin(local, 'aontu', 'mod')
160
+ return pathJoin(local, 'aontu', 'pkg')
131
161
  }
132
162
  }
133
163
  return undefined
134
164
  }
135
165
 
136
166
 
137
- export function lockHash(root: string, ref: ModuleRef, fs: ModuleFs):
138
- string | undefined {
139
- const file = pathJoin(root, 'aontu_meta', 'mod-lock.aon')
167
+ // The user cache's trees (ADR-039 part 2).
168
+ export function cacheStoreDir(cache: string, hash: string, pkg: string): string {
169
+ return moduleDir(pathJoin(cache, 'store', hash), pkg)
170
+ }
171
+
172
+ export function cacheDownloadDir(cache: string, pkg: string): string {
173
+ return pathJoin(moduleDir(pathJoin(cache, 'download'), pkg), '@v')
174
+ }
175
+
176
+ export function cacheSeenDir(cache: string, pkg: string): string {
177
+ return moduleDir(pathJoin(cache, 'seen'), pkg)
178
+ }
179
+
180
+
181
+ export type LockPins = { canon?: string, pkg?: string }
182
+
183
+
184
+ export function lockEntry(root: string, key: string, fs: ModuleFs):
185
+ LockPins | undefined {
186
+ const file = pathJoin(root, META_DIR, LOCK_FILE)
140
187
  if (!fs.existsSync(file)) {
141
188
  return undefined
142
189
  }
@@ -149,8 +196,14 @@ export function lockHash(root: string, ref: ModuleRef, fs: ModuleFs):
149
196
  return undefined
150
197
  }
151
198
 
152
- const entry = lock?.lock?.[ref.path + '@' + ref.major]
153
- return 'string' === typeof entry?.canon ? entry.canon : undefined
199
+ const entry = lock?.lock?.[key]
200
+ if (null == entry || 'object' !== typeof entry) {
201
+ return undefined
202
+ }
203
+ return {
204
+ ...('string' === typeof entry.canon ? { canon: entry.canon } : {}),
205
+ ...('string' === typeof entry.pkg ? { pkg: entry.pkg } : {}),
206
+ }
154
207
  }
155
208
 
156
209
 
@@ -162,9 +215,9 @@ export const MODULE_MAX_DEPTH = 16
162
215
 
163
216
 
164
217
  export type ModuleOptions = {
165
- // The content-addressed user cache, keyed by canon-hash. Consulted
166
- // only when the expected hash is known, which is what "content
167
- // addressed" means: without a pin there is no address.
218
+ // The content-addressed user cache. Consulted only when the expected
219
+ // hash and the package path are both known, which is what its key
220
+ // is made of.
168
221
  cache?: string
169
222
  eval: ModuleEval
170
223
  // How many module verifications deep this evaluation already is.
@@ -181,6 +234,7 @@ export type ModuleFound = {
181
234
 
182
235
  export const MODULE_REFUSAL_CODES: ReadonlySet<string> = new Set([
183
236
  'module_path', 'module_missing', 'module_integrity', 'module_depth',
237
+ 'module_local', 'module_moved',
184
238
  ])
185
239
 
186
240
 
@@ -191,6 +245,12 @@ function refuse(code: string, message: string): never {
191
245
  }
192
246
 
193
247
 
248
+ export function refuseLocalFile(path: string): never {
249
+ return refuse('module_local',
250
+ 'local files need a ./ prefix: ' + path + ' (write @"./' + path + '")')
251
+ }
252
+
253
+
194
254
  // Resolve one module import against the local stores.
195
255
  export function resolveModule(
196
256
  ref: ModuleRef,
@@ -198,65 +258,76 @@ export function resolveModule(
198
258
  fs: ModuleFs,
199
259
  options: ModuleOptions,
200
260
  ): ModuleFound {
201
- const badpath = validateModulePath(ref.path)
261
+ const alias = isAlias(ref.path)
262
+ const badpath = alias ? undefined : validateModulePath(ref.path)
202
263
  if (undefined !== badpath) {
203
- refuse('module_path',
204
- 'module path: ' + ref.path + '@' + ref.major + ' (' + badpath + ')')
264
+ refuse('module_path', 'module path: ' + ref.path + ' (' + badpath + ')')
205
265
  }
206
266
 
207
267
  if (MODULE_MAX_DEPTH <= (options.depth ?? 0)) {
208
268
  refuse('module_depth',
209
- 'module depth: ' + ref.path + '@' + ref.major +
269
+ 'module depth: ' + ref.path +
210
270
  ' (verification nested past ' + MODULE_MAX_DEPTH + ')')
211
271
  }
212
272
 
213
273
  // EVERY enclosing project, innermost first (see projectRoots): a
214
- // vendored module is a project inside a project, and its nested
274
+ // vendored package is a project inside a project, and its nested
215
275
  // imports have to reach the tree the consumer vendored them into.
216
276
  const roots = projectRoots(fromDir, fs)
217
- // The PIN comes from the first lockfile that names this import. A
218
- // vendored module usually ships none, so that is the consumer's --
219
- // which is right: the consumer's lock is what its build is pinned to.
220
- const expect = ref.hash ??
221
- roots.map((r) => lockHash(r, ref, fs)).find((h) => null != h)
277
+ const locked = roots.map((r) => lockEntry(r, ref.path, fs))
278
+ .find((e) => null != e)
279
+ const expect = ref.hash ?? locked?.canon
280
+ // The store is keyed by hash AND package path; an alias names its
281
+ // package in the lockfile, else in the package file that declares it.
282
+ const pkg = alias ?
283
+ (locked?.pkg ?? roots.map((r) => aliasTarget(r, ref.path, fs, options))
284
+ .find((p) => null != p)) : ref.path
285
+
286
+ if (alias && null == pkg) {
287
+ refuse('module_missing',
288
+ 'alias not declared: ' + ref.path + ' (declare it under dep in ' +
289
+ PKG_FILE + ')')
290
+ }
222
291
 
223
292
  const stores: string[] =
224
- roots.map((r) => moduleDir(pathJoin(r, 'aontu_meta', 'vendor'), ref))
293
+ roots.map((r) => moduleDir(pathJoin(r, META_DIR, VENDOR_DIR), ref.path))
225
294
  if (null != options.cache && null != expect) {
226
- // Content-addressed: the cache is keyed by the hash, so a cache hit
227
- // is already the right MEANING before anything is read from it.
228
- stores.push(pathJoin(options.cache, expect))
295
+ stores.push(cacheStoreDir(options.cache, expect, pkg as string))
229
296
  }
230
297
 
231
- const dir = stores.find((d) => fs.existsSync(pathJoin(d, 'mod.aon')))
298
+ const dir = stores.find((d) => fs.existsSync(pathJoin(d, PKG_FILE)))
232
299
  if (undefined === dir) {
233
300
  refuse('module_missing',
234
- 'module not fetched: ' + ref.path + '@' + ref.major +
235
- ' (run: aontu mod get)')
301
+ 'module not fetched: ' + ref.path + ' (run: aontu sync)')
236
302
  }
237
303
 
238
- // The module's own `mod.aon` names its entry file. Read with the
239
- // evaluator rather than a regexp: a module file is ordinary Aontu,
240
- // and the language reading its own metadata is the point.
241
- const main = moduleMain(pathJoin(dir, 'mod.aon'), fs, options)
242
- const full = pathJoin(dir, main)
304
+ // The package's own file names its entry and says whether it moved.
305
+ // Read with the evaluator rather than a regexp: a package file is
306
+ // ordinary Aontu, and the language reading its own metadata is the
307
+ // point.
308
+ const self = packageSelf(pathJoin(dir, PKG_FILE), fs, options)
309
+ if (null != self.moved) {
310
+ refuse('module_moved',
311
+ 'module moved: ' + ref.path + ' (now ' + self.moved +
312
+ '; import that instead, nothing follows a move)')
313
+ }
314
+ const full = pathJoin(dir, self.main)
243
315
 
244
316
  if (!fs.existsSync(full)) {
245
317
  refuse('module_missing',
246
- 'module not fetched: ' + ref.path + '@' + ref.major +
247
- ' (run: aontu mod get)')
318
+ 'module not fetched: ' + ref.path + ' (run: aontu sync)')
248
319
  }
249
320
 
250
321
  const src = fs.readFileSync(full, 'utf8')
251
322
 
252
323
  if (null != expect) {
253
- // VERIFICATION IS ALWAYS LOCAL. The registry's annotation is
254
- // advisory; what decides is the hash of the module as it is on this
324
+ // VERIFICATION IS ALWAYS LOCAL. The repository's manifest is a
325
+ // claim; what decides is the hash of the module as it is on this
255
326
  // machine, recomputed now.
256
327
  const got = options.eval(src, full).hash
257
328
  if (got !== expect) {
258
329
  refuse('module_integrity',
259
- 'module integrity: ' + ref.path + '@' + ref.major +
330
+ 'module integrity: ' + ref.path +
260
331
  ' expected ' + expect + ' got ' + got)
261
332
  }
262
333
  }
@@ -265,10 +336,30 @@ export function resolveModule(
265
336
  }
266
337
 
267
338
 
268
- function moduleMain(file: string, fs: ModuleFs, options: ModuleOptions): string {
339
+ type PackageSelf = { main: string, moved?: string }
340
+
341
+ function packageSelf(file: string, fs: ModuleFs, options: ModuleOptions):
342
+ PackageSelf {
343
+ const gen: any = options.eval(fs.readFileSync(file, 'utf8'), file).gen
344
+ const main = gen?.pkg?.main
345
+ const moved = gen?.moved
346
+ return {
347
+ main: 'string' === typeof main && '' !== main ? main : DEFAULT_MAIN,
348
+ ...('string' === typeof moved && '' !== moved ? { moved } : {}),
349
+ }
350
+ }
351
+
352
+
353
+ function aliasTarget(
354
+ root: string, key: string, fs: ModuleFs, options: ModuleOptions,
355
+ ): string | undefined {
356
+ const file = pathJoin(root, PKG_FILE)
357
+ if (!fs.existsSync(file)) {
358
+ return undefined
359
+ }
269
360
  const gen: any = options.eval(fs.readFileSync(file, 'utf8'), file).gen
270
- const main = gen?.mod?.main
271
- return 'string' === typeof main && '' !== main ? main : DEFAULT_MAIN
361
+ const pkg = gen?.dep?.[key]?.pkg
362
+ return 'string' === typeof pkg && '' !== pkg ? pkg : undefined
272
363
  }
273
364
 
274
365