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.
- package/README.md +1 -1
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +1 -1
- package/dist/aontu.js +1 -79
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +33 -0
- package/dist/aontumodel.js.map +1 -0
- package/dist/cli.js +101 -620
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +1 -1
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +54 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lower.js +11 -61
- package/dist/lower.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -113
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +0 -130
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.js.map +1 -1
- package/dist/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/render.js +20 -135
- package/dist/render.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.js.map +1 -1
- package/dist/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +12 -242
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.js +2 -59
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +8 -42
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +0 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +3 -110
- package/src/aontumodel.ts +32 -0
- package/src/cli.ts +113 -682
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +1 -1
- package/src/hints.ts +66 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lower.ts +12 -62
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -130
- package/src/mod-tool.ts +0 -158
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/render.ts +20 -172
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +13 -251
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +4 -166
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +8 -95
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +0 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/std.ts +0 -683
package/src/lower.ts
CHANGED
|
@@ -1,29 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// THE DECLARATION LOWERING (docs/design/RENDER.0.md D5, D6, P5): the
|
|
4
|
-
// one place in the renderer where "a new language is data" does not
|
|
5
|
-
// hold. A `%record` becoming `export interface X {` or `type X struct
|
|
6
|
-
// {` is a per-language-family function here, selected by the profile's
|
|
7
|
-
// `lowering` field and parameterised by the profile's data -- the
|
|
8
|
-
// indent, the comment forms, the string quote and its escape table, the
|
|
9
|
-
// identifier rules and the type forms. Two families ship, `typescript`
|
|
10
|
-
// and `go`; a third language with declarations is a change here, in
|
|
11
|
-
// both ports (go/lower.go is the twin, function for function).
|
|
12
|
-
//
|
|
13
|
-
// Everything produced is PIECES -- lines at a depth, blanks -- that the
|
|
14
|
-
// fragment fold then turns into bytes, so a lowered declaration and a
|
|
15
|
-
// fragment take the same path to the file: one indent rule, one
|
|
16
|
-
// terminator rule, nothing trimmed.
|
|
17
|
-
//
|
|
18
|
-
// THE PARITY GUARDS (G9 section 3), each a rule both ports keep: case
|
|
19
|
-
// conversion is ASCII-only, and a code point at or above U+0080 rides
|
|
20
|
-
// into the current word verbatim, never split and never converted;
|
|
21
|
-
// string escaping is one pass, per code point, through the profile's
|
|
22
|
-
// table, keyed by decimal code point, with a control character the
|
|
23
|
-
// table does not name spelled as a \u escape; a reserved word is
|
|
24
|
-
// matched by exact equality on the converted name; parenthesisation is
|
|
25
|
-
// by `prec`/`childPrec`; numbers go through `exactJSON`; nothing here
|
|
26
|
-
// sorts, and nothing iterates a map.
|
|
27
3
|
|
|
28
4
|
import { exactJSON } from './exactjson'
|
|
29
5
|
import type { RenderLoss } from './render'
|
|
@@ -40,7 +16,7 @@ export type LowerCtx = {
|
|
|
40
16
|
|
|
41
17
|
|
|
42
18
|
function ln(at: number, text: string): any {
|
|
43
|
-
return { k: 'line', at,
|
|
19
|
+
return { k: 'line', at, n: [text] }
|
|
44
20
|
}
|
|
45
21
|
|
|
46
22
|
const BLANK = { k: 'blank' }
|
|
@@ -64,12 +40,6 @@ function isASCII(c: string): boolean {
|
|
|
64
40
|
return (c.codePointAt(0) as number) < 0x80
|
|
65
41
|
}
|
|
66
42
|
|
|
67
|
-
// THE ASCII WORD SPLITTER. Words break at `_`, `-` and space, at a
|
|
68
|
-
// lower-to-upper boundary (`creditLimit`), at a letter-to-digit
|
|
69
|
-
// boundary in either direction (`utf8String`), and before the last
|
|
70
|
-
// capital of a capital run that a lower case letter follows
|
|
71
|
-
// (`HTTPServer` is HTTP, Server). A code point at or above U+0080
|
|
72
|
-
// rides into the current word and never starts, ends or converts one.
|
|
73
43
|
export function splitWords(name: string): string[] {
|
|
74
44
|
const words: string[] = []
|
|
75
45
|
let cur = ''
|
|
@@ -126,7 +96,7 @@ export function capitalise(word: string, acronyms: string[]): string {
|
|
|
126
96
|
return upperASCII(chars[0]) + chars.slice(1).join('')
|
|
127
97
|
}
|
|
128
98
|
|
|
129
|
-
// THE CASE STYLES of aontu:
|
|
99
|
+
// THE CASE STYLES of aontu:render's %case, over the words.
|
|
130
100
|
export function caseName(name: string, style: string, acronyms: string[]): string {
|
|
131
101
|
if ('as-is' === style) {
|
|
132
102
|
return name
|
|
@@ -152,14 +122,6 @@ export function caseName(name: string, style: string, acronyms: string[]): strin
|
|
|
152
122
|
return lowerASCII(words[0]) + caps.slice(1).join('')
|
|
153
123
|
}
|
|
154
124
|
|
|
155
|
-
// An identifier in a ROLE (record, field, enum, member, const, func,
|
|
156
|
-
// param, alias): the profile's case style for the role, then -- for
|
|
157
|
-
// the names that become bare identifiers, declarations and parameters
|
|
158
|
-
// -- the reserved-word rule on the converted name, exact equality, so
|
|
159
|
-
// Go's `Type` is not `type`. A reserved name is renamed with a trailing
|
|
160
|
-
// underscore and the report says so, since code that named the
|
|
161
|
-
// original will not compile against the rename. A field is a property,
|
|
162
|
-
// and a property may spell what its target's string form admits.
|
|
163
125
|
export function ident(
|
|
164
126
|
name: string, role: string, ctx: LowerCtx, path: string, bare: boolean
|
|
165
127
|
): string {
|
|
@@ -234,14 +196,6 @@ function form(ctx: LowerCtx, name: string): any {
|
|
|
234
196
|
return ctx.profile.types?.[name] ?? { open: '', close: '', prec: 9, childPrec: 0 }
|
|
235
197
|
}
|
|
236
198
|
|
|
237
|
-
// An inner expression under a form, in parens when its precedence is
|
|
238
|
-
// below the form's childPrec: TypeScript's `(string | null)[]`.
|
|
239
|
-
//
|
|
240
|
-
// A FORM ALWAYS CARRIES ITS childPrec, so there is nothing to fall back
|
|
241
|
-
// to: the profile vocabulary declares it `*0`, so every type form of a
|
|
242
|
-
// profile met with `aontu:profile` has one, and `form()`'s own fallback
|
|
243
|
-
// spells it. The default lives in the model, which is the point of
|
|
244
|
-
// putting it there.
|
|
245
199
|
function under(inner: Expr, f: any): string {
|
|
246
200
|
return inner.prec < f.childPrec ? '(' + inner.text + ')' : inner.text
|
|
247
201
|
}
|
|
@@ -289,20 +243,18 @@ export function typeExpr(t: any, ctx: LowerCtx, path: string): Expr {
|
|
|
289
243
|
}
|
|
290
244
|
if ('list' === k) {
|
|
291
245
|
const f = form(ctx, 'list')
|
|
292
|
-
return { text: f.open + under(typeExpr(t.
|
|
246
|
+
return { text: f.open + under(typeExpr(t.n, ctx, path + '.n'), f) + f.close, prec: f.prec }
|
|
293
247
|
}
|
|
294
248
|
if ('map' === k) {
|
|
295
249
|
const f = form(ctx, 'map')
|
|
296
250
|
const key = under(typeExpr(t.key, ctx, path + '.key'), f)
|
|
297
|
-
const of = under(typeExpr(t.
|
|
298
|
-
// The two families spell a map differently between the key and
|
|
299
|
-
// the value: `map[K]V` and `Record<K, V>`.
|
|
251
|
+
const of = under(typeExpr(t.n, ctx, path + '.n'), f)
|
|
300
252
|
const sep = 'go' === ctx.family ? ']' : ', '
|
|
301
253
|
return { text: f.open + key + sep + of + f.close, prec: f.prec }
|
|
302
254
|
}
|
|
303
255
|
if ('opt' === k) {
|
|
304
256
|
const f = form(ctx, 'opt')
|
|
305
|
-
return { text: f.open + under(typeExpr(t.
|
|
257
|
+
return { text: f.open + under(typeExpr(t.n, ctx, path + '.n'), f) + f.close, prec: f.prec }
|
|
306
258
|
}
|
|
307
259
|
if ('union' === k) {
|
|
308
260
|
if ('go' === ctx.family) {
|
|
@@ -310,18 +262,18 @@ export function typeExpr(t: any, ctx: LowerCtx, path: string): Expr {
|
|
|
310
262
|
return { text: prim(ctx, 'any'), prec: 9 }
|
|
311
263
|
}
|
|
312
264
|
const f = form(ctx, 'union')
|
|
313
|
-
const members = t.
|
|
314
|
-
under(typeExpr(m, ctx, path + '.
|
|
265
|
+
const members = t.n.map((m: any, i: number) =>
|
|
266
|
+
under(typeExpr(m, ctx, path + '.n.' + i), f))
|
|
315
267
|
return { text: f.open + members.join(' | ') + f.close, prec: f.prec }
|
|
316
268
|
}
|
|
317
269
|
// lit
|
|
318
270
|
if ('go' === ctx.family) {
|
|
319
|
-
const p = litPrim(t.
|
|
271
|
+
const p = litPrim(t.n)
|
|
320
272
|
loss(ctx, path, 'lit', 'go has no literal type: ' + p)
|
|
321
273
|
return { text: prim(ctx, p), prec: 9 }
|
|
322
274
|
}
|
|
323
275
|
const f = form(ctx, 'lit')
|
|
324
|
-
const lits = t.
|
|
276
|
+
const lits = t.n.map((v: any) => literal(v, ctx))
|
|
325
277
|
return { text: f.open + lits.join(' | ') + f.close, prec: f.prec }
|
|
326
278
|
}
|
|
327
279
|
|
|
@@ -358,8 +310,6 @@ function doc(d: any, at: number, ctx: LowerCtx): any[] {
|
|
|
358
310
|
return out
|
|
359
311
|
}
|
|
360
312
|
|
|
361
|
-
// Every check a target's type system cannot enforce is tier-1 loss,
|
|
362
|
-
// one entry per check, addressed at the check.
|
|
363
313
|
function checks(list: any[] | undefined, path: string, ctx: LowerCtx): void {
|
|
364
314
|
(list ?? []).forEach((c: any, i: number) => {
|
|
365
315
|
loss(ctx, path + '.check.' + i, 'check',
|
|
@@ -409,7 +359,7 @@ function derivedImports(unit: any): any[] {
|
|
|
409
359
|
}
|
|
410
360
|
return
|
|
411
361
|
}
|
|
412
|
-
for (const key of ['type', '
|
|
362
|
+
for (const key of ['type', 'n', 'key', 'returns', 'fields', 'params']) {
|
|
413
363
|
visit(t[key])
|
|
414
364
|
}
|
|
415
365
|
}
|
|
@@ -617,7 +567,7 @@ export function lowerDecl(decl: any, path: string, ctx: LowerCtx): any[] {
|
|
|
617
567
|
out.push(ln(1, 'panic(' + quote('abstract', ctx.profile) + ')'))
|
|
618
568
|
}
|
|
619
569
|
else {
|
|
620
|
-
out.push(...nest(decl.body.
|
|
570
|
+
out.push(...nest(decl.body.n))
|
|
621
571
|
}
|
|
622
572
|
out.push('}')
|
|
623
573
|
}
|
|
@@ -628,7 +578,7 @@ export function lowerDecl(decl: any, path: string, ctx: LowerCtx): any[] {
|
|
|
628
578
|
}
|
|
629
579
|
else {
|
|
630
580
|
out.push('export function ' + name + '(' + sig + ')' + ret + ' {')
|
|
631
|
-
out.push(...nest(decl.body.
|
|
581
|
+
out.push(...nest(decl.body.n))
|
|
632
582
|
out.push('}')
|
|
633
583
|
}
|
|
634
584
|
}
|
package/src/lsp-server.ts
CHANGED
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// Aontu Language Server (stdio).
|
|
4
|
-
//
|
|
5
|
-
// aontu-lsp
|
|
6
|
-
//
|
|
7
|
-
// Speaks LSP over stdio (JSON-RPC with Content-Length framing) and
|
|
8
|
-
// publishes unification diagnostics as `.aontu` files are edited. This
|
|
9
|
-
// binary is intentionally thin: all protocol logic lives in the reusable
|
|
10
|
-
// library ./lsp (LspHandler + computeDiagnostics). See docs/lsp.md for
|
|
11
|
-
// editor configuration.
|
|
12
3
|
|
|
13
4
|
import { LspHandler, Message, OutMessage } from './lsp'
|
|
14
5
|
|
|
15
6
|
|
|
16
|
-
// A byte-level LSP framing codec: feed it incoming chunks, give it a
|
|
17
|
-
// handler, and it decodes Content-Length frames, dispatches them, and
|
|
18
|
-
// writes framed replies. Kept transport-injectable (write/onExit) so it
|
|
19
|
-
// can be unit-tested without real stdio.
|
|
20
7
|
class FrameCodec {
|
|
21
8
|
private buffer = Buffer.alloc(0)
|
|
22
9
|
|
|
@@ -84,9 +71,6 @@ class FrameCodec {
|
|
|
84
71
|
}
|
|
85
72
|
|
|
86
73
|
|
|
87
|
-
// The streams and exit are injectable (defaulting to real stdio) so the
|
|
88
|
-
// full wiring is unit-testable — the same shape as the Go server's
|
|
89
|
-
// serve(in, out, logw).
|
|
90
74
|
function main(
|
|
91
75
|
stdin: NodeJS.ReadableStream = process.stdin,
|
|
92
76
|
write: (chunk: Buffer) => void = (chunk) => void process.stdout.write(chunk),
|
package/src/lsp.ts
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// Aontu Language Server library.
|
|
4
|
-
//
|
|
5
|
-
// This module is the reusable, transport-free core of the language
|
|
6
|
-
// server, deliberately split from the process that serves it:
|
|
7
|
-
//
|
|
8
|
-
// - `computeDiagnostics(src)` turns Aontu source into LSP diagnostics.
|
|
9
|
-
// - `LspHandler` implements the LSP message flow (document sync ->
|
|
10
|
-
// publishDiagnostics) over decoded JSON-RPC *objects*, with no
|
|
11
|
-
// stdin/stdout. It is fully unit-testable and embeddable.
|
|
12
|
-
// - the server (`lsp-server.ts`) is a thin stdio JSON-RPC loop that
|
|
13
|
-
// frames bytes and feeds decoded messages to an `LspHandler`.
|
|
14
|
-
//
|
|
15
|
-
// The Go port mirrors this split in go/lsp (library) and
|
|
16
|
-
// go/cmd/aontu-lsp (server). Diagnostic messages are built the same way
|
|
17
|
-
// in both so editors see identical text regardless of implementation.
|
|
18
3
|
|
|
19
4
|
import type { Val } from './type'
|
|
20
5
|
|
|
@@ -34,18 +19,11 @@ const SEVERITY_WARNING = 2
|
|
|
34
19
|
const SEVERITY_INFORMATION = 3
|
|
35
20
|
const SEVERITY_HINT = 4
|
|
36
21
|
|
|
37
|
-
// Reported to the client in the initialize response. It is the
|
|
38
|
-
// ENGINE's version, not a number of the server's own: a separately
|
|
39
|
-
// maintained one drifts, and had -- the server answered 0.1.0 against
|
|
40
|
-
// a package at 0.52.1, so a client could not tell which engine it was
|
|
41
|
-
// talking to (status-2026-08-21.md section 10).
|
|
42
22
|
const LSP_VERSION = VERSION
|
|
43
23
|
|
|
44
24
|
|
|
45
|
-
// Zero-based line / UTF-16 character offset (LSP Position).
|
|
46
25
|
type Position = { line: number, character: number }
|
|
47
26
|
|
|
48
|
-
// Inclusive-start, exclusive-end span (LSP Range).
|
|
49
27
|
type Range = { start: Position, end: Position }
|
|
50
28
|
|
|
51
29
|
// A single LSP diagnostic.
|
|
@@ -80,18 +58,10 @@ type OutMessage = {
|
|
|
80
58
|
}
|
|
81
59
|
|
|
82
60
|
|
|
83
|
-
// Compute LSP diagnostics for a unit of Aontu source. A valid document —
|
|
84
|
-
// including a non-concrete schema such as `a:string` — returns an empty
|
|
85
|
-
// array; only genuine errors (conflicts, unresolved references, unknown
|
|
86
|
-
// functions, syntax errors) produce diagnostics.
|
|
87
61
|
function computeDiagnostics(
|
|
88
62
|
src: string,
|
|
89
63
|
opts?: { vars?: Record<string, Val>, trust?: any, textExt?: string[] }
|
|
90
64
|
): Diagnostic[] {
|
|
91
|
-
// The trust profile (G5, docs/trust.md): the LSP is the
|
|
92
|
-
// highest-exposure surface — merely OPENING a hostile .aon file in an
|
|
93
|
-
// editor performs its reads — so the handler confines evaluation to
|
|
94
|
-
// the workspace root and threads the profile through here.
|
|
95
65
|
const aontu = new Aontu(includeOpts(opts ?? {}))
|
|
96
66
|
|
|
97
67
|
let root: any
|
|
@@ -114,12 +84,6 @@ function computeDiagnostics(
|
|
|
114
84
|
const seen = new Set()
|
|
115
85
|
const nils: any[] = collectNils(root, seen)
|
|
116
86
|
|
|
117
|
-
// Errors recorded on the context but not present in the tree — e.g. a
|
|
118
|
-
// budget_passes exhaustion nil, which is about the whole evaluation
|
|
119
|
-
// rather than any node — would otherwise be invisible here, and the
|
|
120
|
-
// trust contract forbids silent truncation (docs/trust.md clause 2).
|
|
121
|
-
// Tree nils are already on ctx.err too, so dedup by identity; the
|
|
122
|
-
// transient disjunct-trial sentinel never surfaces.
|
|
123
87
|
for (const e of ac.err) {
|
|
124
88
|
if (e?.isNil && '|:trial-nil' !== e.why && !seen.has(e)) {
|
|
125
89
|
seen.add(e)
|
|
@@ -129,11 +93,6 @@ function computeDiagnostics(
|
|
|
129
93
|
|
|
130
94
|
const out = nils.map(nilToDiagnostic)
|
|
131
95
|
|
|
132
|
-
// Deprecation tags (G3 phase 4): every sited value carrying the
|
|
133
|
-
// deprecate() record — the declaration and, because the record rides
|
|
134
|
-
// meets and reference clones, every use resolving through it — gets
|
|
135
|
-
// the native Deprecated tag (2) at Hint severity, so editors strike
|
|
136
|
-
// it through without shouting.
|
|
137
96
|
for (const { val } of collectDeprecations(root)) {
|
|
138
97
|
const v: any = val
|
|
139
98
|
if (1 > (v.site?.row ?? -1) || 1 > (v.site?.col ?? -1)) {
|
|
@@ -187,38 +146,16 @@ function nilToDiagnostic(nil: any): Diagnostic {
|
|
|
187
146
|
}
|
|
188
147
|
|
|
189
148
|
|
|
190
|
-
// Length (UTF-16 units, like LSP characters) of the offending value's
|
|
191
|
-
// SOURCE TEXT, used to size the diagnostic range (minimum 1).
|
|
192
149
|
function labelLength(nil: any): number {
|
|
193
150
|
return null == nil.primary ? 1 : siteExtent(nil.primary)
|
|
194
151
|
}
|
|
195
152
|
|
|
196
153
|
|
|
197
|
-
// The extent to underline for a value: its SOURCE TEXT's length, with
|
|
198
|
-
// the canon as the fallback and 1 as the floor.
|
|
199
|
-
//
|
|
200
|
-
// CANON IS NOT SOURCE TEXT, which is the whole point of Site.len:
|
|
201
|
-
// `0x1F` has canon `31`, so sizing by canon underlines two characters
|
|
202
|
-
// of a four-character literal — hovering `0x1F` highlighted `0x` and
|
|
203
|
-
// hovering `1F` answered nothing. The Go twin is the same fallback in
|
|
204
|
-
// go/check.go (Problem.Len, ValueSpan.Len), in bytes there because it
|
|
205
|
-
// is added to a byte offset before conversion.
|
|
206
|
-
//
|
|
207
|
-
// The fallback is for a value carrying no stamped span — one propagated
|
|
208
|
-
// onto a result rather than written by a document — where canon is all
|
|
209
|
-
// there is and an approximate underline beats none. A REPORT never
|
|
210
|
-
// guesses this way: vet and why emit len only when it is known, because
|
|
211
|
-
// there a wrong length is a corrupted document rather than a wonky
|
|
212
|
-
// highlight.
|
|
213
154
|
function siteExtent(v: any): number {
|
|
214
155
|
const len = v?.site?.len
|
|
215
156
|
if ('number' === typeof len && len > 0) {
|
|
216
157
|
return len
|
|
217
158
|
}
|
|
218
|
-
// CANON IS READ DEFENSIVELY, as every other reader here does: it is a
|
|
219
|
-
// getter that unifies, and a host-supplied value can throw from it
|
|
220
|
-
// (hover-refuses-bad-input). A hover that cannot measure a value
|
|
221
|
-
// still has to answer for the rest of the document.
|
|
222
159
|
let c = ''
|
|
223
160
|
try { c = v?.canon } catch { c = '' }
|
|
224
161
|
return 'string' === typeof c && c.length > 0 ? c.length : 1
|
|
@@ -268,7 +205,6 @@ function parseErrorDiagnostic(err: any): Diagnostic {
|
|
|
268
205
|
function initializeResult() {
|
|
269
206
|
return {
|
|
270
207
|
capabilities: {
|
|
271
|
-
// 1 = TextDocumentSyncKind.Full
|
|
272
208
|
textDocumentSync: 1,
|
|
273
209
|
hoverProvider: true,
|
|
274
210
|
completionProvider: {},
|
|
@@ -282,14 +218,6 @@ function initializeResult() {
|
|
|
282
218
|
}
|
|
283
219
|
|
|
284
220
|
|
|
285
|
-
// signatureHelp: the declared signature of the ENCLOSING call, served
|
|
286
|
-
// from the registry (docs/design/SIGNATURES.0.md). The enclosing call
|
|
287
|
-
// is found lexically -- scan back from the cursor for the nearest
|
|
288
|
-
// unclosed '(' and read the word before it; commas at that depth
|
|
289
|
-
// count the active parameter, capped at the last slot so a rest tail
|
|
290
|
-
// stays active for every excess argument. Strings are skipped so a
|
|
291
|
-
// paren or comma inside one does not miscount, and the scan stops at
|
|
292
|
-
// the line start, a call being one line in practice.
|
|
293
221
|
function computeSignatureHelp(text: string, pos: any): any {
|
|
294
222
|
// Position to offset, under the full-sync model: lines are exactly
|
|
295
223
|
// the text's newlines.
|
|
@@ -344,21 +272,10 @@ function publishDiagnosticsMsg(uri: string, diagnostics: Diagnostic[]): OutMessa
|
|
|
344
272
|
}
|
|
345
273
|
|
|
346
274
|
|
|
347
|
-
// --- Hover ------------------------------------------------------------
|
|
348
|
-
|
|
349
275
|
type MarkupContent = { kind: 'markdown' | 'plaintext', value: string }
|
|
350
276
|
type Hover = { contents: MarkupContent, range?: Range }
|
|
351
277
|
|
|
352
278
|
|
|
353
|
-
// Resolve the value under the cursor and describe it. Returns null when
|
|
354
|
-
// the position is not over a value with a known source location. Because
|
|
355
|
-
// hover reads the *unified* tree, a literal shows its resolved value and
|
|
356
|
-
// kind (e.g. a reference target resolves to the value it points at).
|
|
357
|
-
// HOVER PROVENANCE (G7 phase 7) is CONFIG-GATED and off by default:
|
|
358
|
-
// the contributions that met at the hovered path, appended to the
|
|
359
|
-
// value's own hover. Hover already re-unifies the whole document per
|
|
360
|
-
// request, so an editor that asks for this pays a second instrumented
|
|
361
|
-
// evaluation knowingly, and one that does not pays nothing.
|
|
362
279
|
function provenanceMarkdown(
|
|
363
280
|
src: string, path: string[], trust?: any): string {
|
|
364
281
|
if (0 === path.length) {
|
|
@@ -372,10 +289,6 @@ function provenanceMarkdown(
|
|
|
372
289
|
}
|
|
373
290
|
|
|
374
291
|
|
|
375
|
-
// The contributions as hover markdown. Exported for the direct test
|
|
376
|
-
// (ADR-002): a siteless contribution and a named file are both shapes
|
|
377
|
-
// the record allows and no hover produces, hover evaluating one
|
|
378
|
-
// unnamed document.
|
|
379
292
|
export function contributionsMarkdown(conjuncts: WhyConjunct[]): string {
|
|
380
293
|
if (0 === conjuncts.length) {
|
|
381
294
|
return ''
|
|
@@ -388,12 +301,6 @@ export function contributionsMarkdown(conjuncts: WhyConjunct[]): string {
|
|
|
388
301
|
}
|
|
389
302
|
|
|
390
303
|
|
|
391
|
-
// HOVER RUNS UNDER THE SAME CAPABILITY AS DIAGNOSTICS. It used to
|
|
392
|
-
// evaluate through `new Aontu()` -- the full system resolver -- BESIDE
|
|
393
|
-
// confined diagnostics in the same server, so a workspace-confined
|
|
394
|
-
// session still resolved an escaping include the moment a cursor rested
|
|
395
|
-
// on it (use-cases/REVIEW.md finding G). One document, two postures, is
|
|
396
|
-
// not a confinement.
|
|
397
304
|
function computeHover(
|
|
398
305
|
src: string, position: Position, provenance?: boolean,
|
|
399
306
|
trust?: any): Hover | null {
|
|
@@ -450,16 +357,6 @@ function collectHoverCandidates(
|
|
|
450
357
|
// one, canon otherwise. See siteExtent.
|
|
451
358
|
const span = siteExtent(v)
|
|
452
359
|
const spanSrc = 'string' === typeof v.site?.src ? v.site.src : ''
|
|
453
|
-
// Hover targets concrete values (scalars, kinds, refs, …), not
|
|
454
|
-
// containers: a map/list source span is not reliably reconstructable
|
|
455
|
-
// from a single site, and the same restriction in the Go port keeps
|
|
456
|
-
// hover behaviour identical across implementations. The walk still
|
|
457
|
-
// recurses into containers below to reach their leaf values. Canon is
|
|
458
|
-
// single-line, so its length approximates the on-line source span.
|
|
459
|
-
// Single-line is decided by the SOURCE TEXT when there is one: a
|
|
460
|
-
// multi-line token cannot be described by one line's start and end,
|
|
461
|
-
// and canon's newlines are not the token's. Falling back to canon
|
|
462
|
-
// keeps the old test for a value with no stamped span.
|
|
463
360
|
const multiline = '' === spanSrc ? canon.includes('\n') : spanSrc.includes('\n')
|
|
464
361
|
if (row >= 1 && col >= 1 && canon.length > 0 && !multiline &&
|
|
465
362
|
!v.isMap && !v.isList) {
|
|
@@ -505,8 +402,6 @@ function valKind(val: any): string {
|
|
|
505
402
|
}
|
|
506
403
|
|
|
507
404
|
|
|
508
|
-
// --- Completion -------------------------------------------------------
|
|
509
|
-
|
|
510
405
|
type CompletionItem = {
|
|
511
406
|
label: string
|
|
512
407
|
kind?: number // LSP CompletionItemKind
|
|
@@ -519,21 +414,20 @@ import { funcSig, renderSig, renderSigArg } from './sig'
|
|
|
519
414
|
const COMPLETION_FUNCTION = 3
|
|
520
415
|
const COMPLETION_KEYWORD = 14
|
|
521
416
|
|
|
522
|
-
// The built-in functions. Kept in sync with the engine by
|
|
523
|
-
// `lsp.test.ts`, which asserts each is recognised and no others are.
|
|
524
|
-
// The Go port derives its list from the engine's own name set
|
|
525
|
-
// (`BuiltinFuncNames`, go/func.go), which is why a name added there
|
|
526
|
-
// and forgotten here diverges silently — as `id` and `refer` did
|
|
527
|
-
// between G4 phases 1/2 and G8 phase 1.
|
|
528
417
|
const BUILTIN_FUNCS = [
|
|
529
|
-
'above', 'acyclic', 'add', 'below', 'close', '
|
|
418
|
+
'abnf', 'above', 'acyclic', 'add', 'below', 'close', 'content', 'copy',
|
|
419
|
+
'copyfiles', 'deprecate',
|
|
420
|
+
'div',
|
|
530
421
|
'each', 'emit', 'esc',
|
|
531
|
-
'filter', 'greatest',
|
|
532
|
-
'hide', 'inverse', 'join', 'key', 'least', 'length', '
|
|
533
|
-
'
|
|
534
|
-
'
|
|
535
|
-
'
|
|
536
|
-
'
|
|
422
|
+
'file', 'filter', 'folder', 'fragment', 'greatest',
|
|
423
|
+
'hide', 'inject', 'inverse', 'join', 'key', 'least', 'length', 'line',
|
|
424
|
+
'list', 'listitems', 'lower',
|
|
425
|
+
'map', 'match', 'max', 'maybe', 'min', 'mod', 'move', 'mul', 'must', 'neq',
|
|
426
|
+
'nom', 'open',
|
|
427
|
+
'pack', 'parse', 'path', 'pick',
|
|
428
|
+
'pref', 'project', 're', 'refer', 'rel', 'rem', 'rep', 'slot', 'sort',
|
|
429
|
+
'split', 'sub', 'sum',
|
|
430
|
+
'super', 'translate', 'type', 'unique', 'upper', 'usc',
|
|
537
431
|
]
|
|
538
432
|
|
|
539
433
|
// Scalar-kind and literal keywords.
|
|
@@ -567,46 +461,11 @@ function computeCompletions(): CompletionItem[] {
|
|
|
567
461
|
}
|
|
568
462
|
|
|
569
463
|
|
|
570
|
-
// Transport-agnostic LSP message dispatcher. Consumes decoded JSON-RPC
|
|
571
|
-
// messages and returns the messages to send back, tracking open document
|
|
572
|
-
// text and recomputing diagnostics on open/change/close. Not safe for
|
|
573
|
-
// concurrent use; drive it from a single loop (as the stdio server does).
|
|
574
|
-
// A file:// uri's filesystem path, for the workspace-root confinement.
|
|
575
|
-
// Percent-decoded; a non-file uri (or none) yields undefined.
|
|
576
|
-
//
|
|
577
|
-
// EXPORTED, inline as contributionsMarkdown is: part of the reusable
|
|
578
|
-
// LSP library surface, and the twin of the package-visible uriToPath in
|
|
579
|
-
// go/lsp/handler.go. Anything driving this module with its own
|
|
580
|
-
// transport has to turn a client's uri into a path the same way the
|
|
581
|
-
// confinement does, and the rules below are not guessable from outside.
|
|
582
|
-
//
|
|
583
|
-
// THE DRIVE-LETTER SLASH. A file uri names an absolute path after the
|
|
584
|
-
// authority, so on Windows the standard spelling every editor sends is
|
|
585
|
-
// file:///C:/Users/me/project — three slashes, and the third belongs to
|
|
586
|
-
// the PATH. Stripping only `file://` leaves `/C:/Users/me/project`,
|
|
587
|
-
// which is not a Windows path at all, so the workspace-root
|
|
588
|
-
// confinement below compared real paths against nonsense and an editor
|
|
589
|
-
// on Windows got no confinement it could rely on. Both ports carried
|
|
590
|
-
// the defect identically (go/lsp/handler.go uriToPath), and no test
|
|
591
|
-
// caught it because both ports' tests built the uri as `'file://' +
|
|
592
|
-
// path` — two slashes, which is not what a client sends and which
|
|
593
|
-
// accidentally produced a usable path.
|
|
594
|
-
//
|
|
595
|
-
// The leading slash is dropped only before a DRIVE LETTER, so a POSIX
|
|
596
|
-
// path keeps the root it needs: file:///tmp/x stays /tmp/x.
|
|
597
464
|
export function uriToPath(uri: unknown): string | undefined {
|
|
598
465
|
if ('string' !== typeof uri || !uri.startsWith('file://')) {
|
|
599
466
|
return undefined
|
|
600
467
|
}
|
|
601
468
|
const path = percentDecode(uri.slice('file://'.length))
|
|
602
|
-
// AN EMPTY PATH IS NOT A ROOT. `file://` on its own yields '', and
|
|
603
|
-
// '' is not nullish — so it won the `??` chain below and arrived as
|
|
604
|
-
// `{ include: { root: '' } }`, a confinement root that then resolves
|
|
605
|
-
// against the process working directory: the same client params made
|
|
606
|
-
// the server allow or deny an include depending on where it was
|
|
607
|
-
// started from. The Go twin never had it, because its chain tests
|
|
608
|
-
// `"" != folder` explicitly (go/lsp/handler.go). Answering undefined
|
|
609
|
-
// is what makes the two chains agree.
|
|
610
469
|
if ('' === path) {
|
|
611
470
|
return undefined
|
|
612
471
|
}
|
|
@@ -614,23 +473,6 @@ export function uriToPath(uri: unknown): string | undefined {
|
|
|
614
473
|
}
|
|
615
474
|
|
|
616
475
|
|
|
617
|
-
// Percent-decoding that CANNOT THROW. `decodeURIComponent` raises a
|
|
618
|
-
// URIError on a malformed escape (`%ZZ`), and this runs on a uri a
|
|
619
|
-
// CLIENT sent — so a stray percent in a workspace path took the
|
|
620
|
-
// exception straight out of the initialize handler, where the Go twin
|
|
621
|
-
// swallowed the same failure and used the raw text
|
|
622
|
-
// (go/lsp/handler.go). Two ports, two behaviours, for an input neither
|
|
623
|
-
// of them controls.
|
|
624
|
-
//
|
|
625
|
-
// The agreement runs the other way for the SECOND way an escape can
|
|
626
|
-
// fail to decode. `%FF` is well-formed and names a raw byte — a
|
|
627
|
-
// perfectly good Linux filename — which Go produced and a JavaScript
|
|
628
|
-
// string cannot hold at all, so the ports derived different workspace
|
|
629
|
-
// roots for a uri a byte-oriented client really sends. Only one
|
|
630
|
-
// direction is reachable from both languages, so Go now declines to
|
|
631
|
-
// unescape a result that is not valid UTF-8 and both ports keep the
|
|
632
|
-
// raw text. An undecodable path is still a path, and refusing to serve
|
|
633
|
-
// a session over it helps nobody.
|
|
634
476
|
function percentDecode(text: string): string {
|
|
635
477
|
try {
|
|
636
478
|
return decodeURIComponent(text)
|
|
@@ -655,12 +497,6 @@ class LspHandler {
|
|
|
655
497
|
private shutdownOK = false
|
|
656
498
|
private exited = false
|
|
657
499
|
|
|
658
|
-
// The trust profile evaluation runs under (G5, docs/trust.md):
|
|
659
|
-
// workspace-root confinement by default, set from the initialize
|
|
660
|
-
// params. An `initializationOptions.aontu.trust.include` of 'system',
|
|
661
|
-
// 'none' or { root } widens or narrows it explicitly. Undefined —
|
|
662
|
-
// no workspace root and no explicit option — falls back to today's
|
|
663
|
-
// unconfined behaviour, which single-file sessions rely on.
|
|
664
500
|
private trust: any = undefined
|
|
665
501
|
|
|
666
502
|
// Hover provenance (G7 phase 7): off unless an editor asks for it
|
|
@@ -687,10 +523,6 @@ class LspHandler {
|
|
|
687
523
|
true === params.initializationOptions?.aontu?.provenance
|
|
688
524
|
const explicit = params.initializationOptions?.aontu?.trust?.include
|
|
689
525
|
if (null != explicit) {
|
|
690
|
-
// An explicit setting wins — validated, and an unrecognised
|
|
691
|
-
// value confines to NOTHING rather than silently widening:
|
|
692
|
-
// deny is the safe reading of a setting the server does not
|
|
693
|
-
// understand. The same rule as the Go handler.
|
|
694
526
|
this.trust =
|
|
695
527
|
'system' === explicit ? undefined :
|
|
696
528
|
'none' === explicit ? { include: 'none' } :
|
package/src/mcp-server.ts
CHANGED
|
@@ -1,27 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// Aontu MCP server (stdio).
|
|
4
|
-
//
|
|
5
|
-
// aontu-mcp [--root <dir>]
|
|
6
|
-
//
|
|
7
|
-
// Speaks the Model Context Protocol over stdio: newline-delimited
|
|
8
|
-
// JSON-RPC 2.0, one message per line. This binary is intentionally
|
|
9
|
-
// thin — every tool and every protocol decision lives in the reusable
|
|
10
|
-
// library ./mcp, the same three-layer split the language server uses
|
|
11
|
-
// (docs/lsp.md).
|
|
12
|
-
//
|
|
13
|
-
// NDJSON, not the LSP's Content-Length framing: MCP stdio transport
|
|
14
|
-
// is line-delimited, and a server that invented its own framing would
|
|
15
|
-
// not be reachable by any client.
|
|
16
|
-
//
|
|
17
|
-
// The one startup decision is the PATH CAPABILITY: `--root <dir>`
|
|
18
|
-
// grants the served evaluation the CLI's `--trust root:<dir>` posture
|
|
19
|
-
// — includes resolve confined below the root, and every tool's
|
|
20
|
-
// document arguments accept `<name>Path` file alternatives, confined
|
|
21
|
-
// the same way. Without it the server denies all includes and refuses
|
|
22
|
-
// path arguments (./mcp). The root is realpath'd HERE, once, so the
|
|
23
|
-
// confinement prefix the library compares against is the real
|
|
24
|
-
// directory, not a spelling of it.
|
|
25
3
|
|
|
26
4
|
import { realpathSync, statSync } from 'node:fs'
|
|
27
5
|
|
|
@@ -40,11 +18,6 @@ const USAGE = 'aontu mcp - aontu MCP server (stdio, NDJSON JSON-RPC)\n' +
|
|
|
40
18
|
' denies every include and refuses path arguments.\n'
|
|
41
19
|
|
|
42
20
|
|
|
43
|
-
// The startup arguments. Parsed here rather than in bin/aontu-mcp.js
|
|
44
|
-
// or the CLI's mcp verb
|
|
45
|
-
// so the parsing is import-testable; unknown options REFUSE rather
|
|
46
|
-
// than warn, because a server whose operator typo'd --root must not
|
|
47
|
-
// come up quietly unconfined.
|
|
48
21
|
export type ServerArgs = {
|
|
49
22
|
root?: string
|
|
50
23
|
help?: boolean
|
|
@@ -152,10 +125,6 @@ function main(
|
|
|
152
125
|
return undefined
|
|
153
126
|
}
|
|
154
127
|
|
|
155
|
-
// FAIL FAST on a root that is not a real directory: every later
|
|
156
|
-
// call would refuse anyway, but a misconfigured server that answers
|
|
157
|
-
// a thousand confusing refusals is worse than one that says so at
|
|
158
|
-
// startup.
|
|
159
128
|
let root: string | undefined
|
|
160
129
|
if (null != args.root) {
|
|
161
130
|
try {
|