aontu 0.53.0 → 0.55.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/dist/aontu.d.ts +3 -2
- package/dist/aontu.js +36 -7
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +500 -8
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +5 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/err.js +7 -1
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +2 -5
- package/dist/graph.js +83 -46
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +9 -10
- package/dist/hcanon.js.map +1 -1
- package/dist/hints.js +94 -26
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +34 -0
- package/dist/jsonschema.js.map +1 -1
- package/dist/lang.js +593 -191
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +86 -6
- package/dist/lsp.js.map +1 -1
- package/dist/mcp.js +153 -6
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +42 -8
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +97 -2
- package/dist/mod.js.map +1 -1
- package/dist/patch.d.ts +5 -0
- package/dist/patch.js +25 -25
- package/dist/patch.js.map +1 -1
- package/dist/provenance.d.ts +1 -0
- package/dist/provenance.js +2 -1
- package/dist/provenance.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/reach.d.ts +1 -0
- package/dist/reach.js +49 -21
- package/dist/reach.js.map +1 -1
- package/dist/relation.d.ts +4 -0
- package/dist/relation.js +125 -200
- package/dist/relation.js.map +1 -1
- package/dist/sig.d.ts +25 -0
- package/dist/sig.js +277 -0
- package/dist/sig.js.map +1 -0
- package/dist/sigdecl.d.ts +2 -0
- package/dist/sigdecl.js +11 -0
- package/dist/sigdecl.js.map +1 -0
- package/dist/siggate.d.ts +4 -0
- package/dist/siggate.js +90 -0
- package/dist/siggate.js.map +1 -0
- package/dist/std.js +75 -16
- package/dist/std.js.map +1 -1
- package/dist/subsume.js +57 -10
- package/dist/subsume.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.d.ts +2 -2
- package/dist/unify.js +93 -114
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +1 -2
- package/dist/utility.js +7 -61
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +12 -1
- package/dist/val/AggFuncVal.js +165 -3
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.d.ts +2 -0
- package/dist/val/BagVal.js +56 -8
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +33 -5
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.d.ts +35 -0
- package/dist/val/ContainerKindVal.js +99 -0
- package/dist/val/ContainerKindVal.js.map +1 -0
- package/dist/val/CopyFuncVal.js +0 -7
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +146 -39
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +37 -2
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +19 -8
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.d.ts +39 -0
- package/dist/val/GraphAtomVal.js +184 -0
- package/dist/val/GraphAtomVal.js.map +1 -0
- package/dist/val/JunctionVal.js +22 -5
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/ListVal.js +27 -34
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +82 -35
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/PathFuncVal.d.ts +2 -2
- package/dist/val/PathFuncVal.js +75 -16
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.d.ts +25 -0
- package/dist/val/PathVal.js +150 -0
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +50 -35
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +159 -32
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +19 -0
- package/dist/val/RecurseVal.js +217 -0
- package/dist/val/RecurseVal.js.map +1 -0
- package/dist/val/RefVal.d.ts +2 -1
- package/dist/val/RefVal.js +105 -72
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +30 -7
- package/dist/val/ReferFuncVal.js +395 -94
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +4 -2
- package/dist/val/ScalarKindVal.js +12 -1
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +4 -2
- package/dist/val/SuperFuncVal.js +118 -14
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.d.ts +1 -1
- package/dist/val/Val.d.ts +2 -3
- package/dist/val/Val.js +39 -20
- package/dist/val/Val.js.map +1 -1
- package/dist/val/arith.js +4 -1
- package/dist/val/arith.js.map +1 -1
- package/dist/vet.d.ts +1 -0
- package/dist/vet.js +32 -1
- package/dist/vet.js.map +1 -1
- package/dist/view.d.ts +90 -0
- package/dist/view.js +2168 -0
- package/dist/view.js.map +1 -0
- package/grammar/aontu.gbnf +18 -10
- package/grammar/aontu.lark +15 -10
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +10 -3
- package/skill/grammar-card.md +1 -2
- package/src/aontu.ts +37 -7
- package/src/cli.ts +553 -8
- package/src/ctx.ts +20 -0
- package/src/diff.ts +4 -2
- package/src/err.ts +8 -1
- package/src/graph.ts +125 -75
- package/src/hcanon.ts +9 -11
- package/src/hints.ts +112 -29
- package/src/jsonschema.ts +41 -0
- package/src/lang.ts +642 -203
- package/src/lsp.ts +75 -6
- package/src/mcp.ts +164 -6
- package/src/mod-tool.ts +48 -9
- package/src/mod.ts +110 -1
- package/src/patch.ts +31 -27
- package/src/provenance.ts +8 -1
- package/src/query.ts +4 -2
- package/src/reach.ts +52 -23
- package/src/relation.ts +139 -236
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/std.ts +77 -16
- package/src/subsume.ts +59 -10
- package/src/unify.ts +102 -125
- package/src/utility.ts +7 -67
- package/src/val/AggFuncVal.ts +231 -4
- package/src/val/BagVal.ts +58 -9
- package/src/val/ConstraintVal.ts +34 -5
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -7
- package/src/val/DisjunctVal.ts +152 -40
- package/src/val/ExpectVal.ts +39 -4
- package/src/val/FuncBaseVal.ts +21 -8
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/JunctionVal.ts +23 -6
- package/src/val/ListVal.ts +30 -37
- package/src/val/MapVal.ts +87 -39
- package/src/val/PathFuncVal.ts +107 -19
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlusOpVal.ts +56 -37
- package/src/val/PrefVal.ts +186 -38
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +105 -83
- package/src/val/ReferFuncVal.ts +445 -100
- package/src/val/ScalarKindVal.ts +12 -0
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -1
- package/src/val/Val.ts +44 -35
- package/src/val/arith.ts +4 -1
- package/src/vet.ts +41 -4
- package/src/view.ts +2882 -0
- package/dist/val/IdFuncVal.d.ts +0 -13
- package/dist/val/IdFuncVal.js +0 -54
- package/dist/val/IdFuncVal.js.map +0 -1
- package/src/val/IdFuncVal.ts +0 -91
package/src/err.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* Copyright (c) 2021-2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
import { sep } from 'node:path'
|
|
5
|
+
|
|
4
6
|
import { util } from '@tabnas/jsonic'
|
|
5
7
|
|
|
6
8
|
import { Val, ErrContext } from './type'
|
|
@@ -180,7 +182,12 @@ function descErr<NILS extends NilVal | NilVal[]>(
|
|
|
180
182
|
|
|
181
183
|
function resolveFile(url: string | undefined) {
|
|
182
184
|
const cwd = process.cwd()
|
|
183
|
-
|
|
185
|
+
// The PLATFORM'S separator, not '/': a hardcoded slash never matched
|
|
186
|
+
// a Windows cwd, so every Windows report named the absolute path
|
|
187
|
+
// where the POSIX report (and the Go CLI, which prints the entry as
|
|
188
|
+
// typed) named `clash.aon` -- caught by the docs transcript for
|
|
189
|
+
// reading a conflict error, which pins the relative spelling.
|
|
190
|
+
let out = url?.replace(cwd + sep, '') ?? '<no-file>'
|
|
184
191
|
out = out === cwd || '' === out ? '<no-file>' : out
|
|
185
192
|
return out
|
|
186
193
|
}
|
package/src/graph.ts
CHANGED
|
@@ -1,52 +1,59 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// THE DERIVED
|
|
3
|
+
// THE DERIVED STRUCTURE (G4 phase 3,
|
|
4
4
|
// docs/capability-review/g4-identity-relations.md): an evaluated
|
|
5
|
-
// document has, besides its value, a GRAPH —
|
|
6
|
-
//
|
|
7
|
-
// from one entity to one address).
|
|
5
|
+
// document has, besides its value, a GRAPH — the set of checked links,
|
|
6
|
+
// each from one tree position to another.
|
|
8
7
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
8
|
+
// The graph is PATH-NATIVE (ADR-014). There is no second namespace to
|
|
9
|
+
// index: a node's address is its path, so the entity index the first
|
|
10
|
+
// design carried is exactly the set of paths already in the edges, and
|
|
11
|
+
// the node a link starts at is derived from where the link sits rather
|
|
12
|
+
// than declared by a mark.
|
|
13
|
+
//
|
|
14
|
+
// G4's deliverable is that this exists and is DETERMINISTIC. What is
|
|
15
|
+
// built on it — impact analysis ("what reaches $.services.auth?"),
|
|
16
|
+
// reachability, context-window-sized slices — is a traversal, and its
|
|
17
|
+
// exposure as verbs and projections belongs to G7. Relation properties
|
|
18
|
+
// (acyclicity, inverse consistency) are G4 phase 5's, and consume
|
|
19
|
+
// exactly this edge set.
|
|
15
20
|
|
|
16
21
|
import type { Val } from './type'
|
|
17
22
|
|
|
18
23
|
import { cmpCodePoint } from './keyorder'
|
|
19
24
|
|
|
20
25
|
|
|
21
|
-
export type EntityEntry = {
|
|
22
|
-
// The id, as `id(name)` spelled it.
|
|
23
|
-
id: string
|
|
24
|
-
// Every tree path that holds this entity, in code-point order. More
|
|
25
|
-
// than one is the normal case: the merge puts the entity's value at
|
|
26
|
-
// every position that declared it.
|
|
27
|
-
paths: string[]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
export type Edge = {
|
|
31
|
-
// The
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// of its nearest identified ancestor.
|
|
27
|
+
// The node the link starts at, as a `$.dotted.path`: the link's own
|
|
28
|
+
// position with the relation key and any list indices stripped. `$`
|
|
29
|
+
// when the link sits at the top of the document.
|
|
35
30
|
from: string
|
|
36
|
-
// The RELATION: the
|
|
37
|
-
//
|
|
38
|
-
//
|
|
31
|
+
// The RELATION: the key the link hangs under, so a link inside a
|
|
32
|
+
// list (`dependsOn: [refer() & "$.a"]`) is an edge under `dependsOn`
|
|
33
|
+
// rather than under `0`. A rel()-minted link carries its predicate
|
|
34
|
+
// declared rather than inferred.
|
|
39
35
|
key: string
|
|
40
36
|
// The address, as the link spells it.
|
|
41
37
|
to: string
|
|
42
38
|
// Where the link is, as a `$.dotted.path`, so a report can point at
|
|
43
39
|
// it.
|
|
44
40
|
at: string
|
|
41
|
+
// Set when the link sits inside a `hide()`-marked subtree. The link
|
|
42
|
+
// is still checked and still an edge, but a figure that draws it
|
|
43
|
+
// DISCLOSES what the document hides, so the view extractors skip it
|
|
44
|
+
// and report it as `hidden_contribution`.
|
|
45
|
+
hidden?: true
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export type Graph = {
|
|
48
|
-
entities: EntityEntry[]
|
|
49
49
|
edges: Edge[]
|
|
50
|
+
// The positions of links written under an UNRESOLVED DISJUNCTION.
|
|
51
|
+
// ADR-007: an unresolved disjunction is not a value, so a link
|
|
52
|
+
// beneath one of its arms is not a fact and is not an edge -- but it
|
|
53
|
+
// is not nothing either, and a figure that silently dropped it would
|
|
54
|
+
// be the failure the views exist to avoid. Absent when there are
|
|
55
|
+
// none, so a graph of a decided document is the shape it always was.
|
|
56
|
+
disjunct?: string[]
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
|
|
@@ -54,82 +61,125 @@ const formatPath = (path: string[]): string =>
|
|
|
54
61
|
0 === path.length ? '$' : '$.' + path.join('.')
|
|
55
62
|
|
|
56
63
|
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
// Digits-only segments are list indices, which is exactly how the rest
|
|
65
|
+
// of the engine spells them.
|
|
66
|
+
const isIndex = (seg: string): boolean => /^[0-9]+$/.test(seg)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
// The node a link starts at and the relation it hangs under, derived
|
|
70
|
+
// from the link's own position.
|
|
71
|
+
//
|
|
72
|
+
// A DECLARED predicate (rel()-minted) is authoritative: the link is cut
|
|
73
|
+
// at the key the rel() sat on, wherever that is on the way down, which
|
|
74
|
+
// is what makes a MAP-valued relation report the relation rather than
|
|
75
|
+
// the inner label. Without one the relation is INFERRED: strip the list
|
|
76
|
+
// indices, and the first real key above the link is it.
|
|
77
|
+
const cut = (
|
|
78
|
+
at: string[], relkey: string | undefined
|
|
79
|
+
): { from: string, key: string } => {
|
|
80
|
+
if (undefined !== relkey) {
|
|
81
|
+
for (let i = at.length - 1; 0 <= i; i--) {
|
|
82
|
+
if (at[i] === relkey) {
|
|
83
|
+
return { from: formatPath(at.slice(0, i)), key: relkey }
|
|
84
|
+
}
|
|
65
85
|
}
|
|
66
86
|
}
|
|
67
|
-
|
|
87
|
+
let i = at.length - 1
|
|
88
|
+
for (; 0 <= i && isIndex(at[i]); i--) { }
|
|
89
|
+
return 0 > i
|
|
90
|
+
? { from: formatPath([]), key: relkey ?? '' }
|
|
91
|
+
: { from: formatPath(at.slice(0, i)), key: relkey ?? at[i] }
|
|
68
92
|
}
|
|
69
93
|
|
|
70
94
|
|
|
71
|
-
// The graph of an evaluated tree. Walks POSITIONS, not values:
|
|
72
|
-
//
|
|
73
|
-
// walk guarded by object identity would find the
|
|
74
|
-
// every other place it is
|
|
75
|
-
//
|
|
95
|
+
// The graph of an evaluated tree. Walks POSITIONS, not values: a
|
|
96
|
+
// reference or a spread can put one value object at several positions,
|
|
97
|
+
// and a walk guarded by object identity would find the first and miss
|
|
98
|
+
// every other place it is reached. The guard is therefore the ancestor
|
|
99
|
+
// chain — which is what a cycle actually is.
|
|
76
100
|
export function graphOf(root: Val): Graph {
|
|
77
|
-
const byId = new Map<string, string[]>()
|
|
78
101
|
const edges: Edge[] = []
|
|
102
|
+
const disjunct: string[] = []
|
|
79
103
|
|
|
104
|
+
// ONE WALK, with `undecided` saying which side of ADR-007 it is on:
|
|
105
|
+
// below an unresolved disjunction every link is a position the
|
|
106
|
+
// document has not decided, and nothing there is an edge.
|
|
80
107
|
const visit = (
|
|
81
|
-
node: any, path: string[],
|
|
82
|
-
|
|
108
|
+
node: any, path: string[], ancestors: Set<any>, hidden: boolean,
|
|
109
|
+
undecided: boolean
|
|
83
110
|
): void => {
|
|
84
111
|
if (null == node || true !== node.isVal || ancestors.has(node)) {
|
|
85
112
|
return
|
|
86
113
|
}
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
let below = tail
|
|
90
|
-
const name = node.entity
|
|
91
|
-
if (null != name) {
|
|
92
|
-
let paths = byId.get(name)
|
|
93
|
-
if (undefined === paths) {
|
|
94
|
-
paths = []
|
|
95
|
-
byId.set(name, paths)
|
|
96
|
-
}
|
|
97
|
-
paths.push(formatPath(path))
|
|
98
|
-
// A nested entity is not a component of the one above it: the
|
|
99
|
-
// key path restarts at the identified node.
|
|
100
|
-
inside = name
|
|
101
|
-
below = []
|
|
102
|
-
}
|
|
115
|
+
hidden = hidden || true === node.mark?.hide
|
|
103
116
|
|
|
104
117
|
const link = node.link
|
|
105
118
|
if (null != link) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
if (undecided) {
|
|
120
|
+
disjunct.push(formatPath(path))
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
const { from, key } = cut(path, node.relkey as string | undefined)
|
|
124
|
+
const edge: Edge = { from, key, to: link, at: formatPath(path) }
|
|
125
|
+
if (hidden) {
|
|
126
|
+
edge.hidden = true
|
|
127
|
+
}
|
|
128
|
+
edges.push(edge)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// A graph atom is TRANSPARENT here (RELATIONS P2): it carries the
|
|
133
|
+
// field's value at the field's own position, and the graph is about
|
|
134
|
+
// the value.
|
|
135
|
+
if (true === node.isGraphAtom && undefined !== node.held) {
|
|
136
|
+
visit(node.held, path, ancestors, hidden, undecided)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// An unresolved conjunction (a link waiting on a peer that never
|
|
140
|
+
// came, a constraint still open) holds its terms at the SAME
|
|
141
|
+
// position; every link among them is written there, and is an
|
|
142
|
+
// edge. This is what a match-selected branch or a deferred refer()
|
|
143
|
+
// looks like after evaluation.
|
|
144
|
+
if (true === node.isConjunct && Array.isArray(node.peg)) {
|
|
145
|
+
ancestors.add(node)
|
|
146
|
+
for (const term of node.peg) {
|
|
147
|
+
visit(term, path, ancestors, hidden, undecided)
|
|
148
|
+
}
|
|
149
|
+
ancestors.delete(node)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// AN UNRESOLVED DISJUNCTION IS NOT A VALUE (ADR-007), so a link
|
|
153
|
+
// under one of its arms is not an edge. Its POSITION is collected
|
|
154
|
+
// instead, so a figure can report what the document leaves
|
|
155
|
+
// undecided rather than drawing it or dropping it in silence.
|
|
156
|
+
if (true === node.isDisjunct && Array.isArray(node.peg)) {
|
|
157
|
+
ancestors.add(node)
|
|
158
|
+
for (const arm of node.peg) {
|
|
159
|
+
visit(arm, path, ancestors, hidden, true)
|
|
160
|
+
}
|
|
161
|
+
ancestors.delete(node)
|
|
112
162
|
}
|
|
113
163
|
|
|
114
164
|
if ((true === node.isMap || true === node.isList) && null != node.peg) {
|
|
115
165
|
ancestors.add(node)
|
|
116
166
|
for (const k of Object.keys(node.peg)) {
|
|
117
|
-
visit(node.peg[k], [...path, k],
|
|
167
|
+
visit(node.peg[k], [...path, k], ancestors, hidden, undecided)
|
|
118
168
|
}
|
|
119
169
|
ancestors.delete(node)
|
|
120
170
|
}
|
|
121
171
|
}
|
|
122
172
|
|
|
123
|
-
visit(root, [],
|
|
124
|
-
|
|
125
|
-
// DETERMINISTIC by construction, not by luck: ids in code-point
|
|
126
|
-
// order, each id's paths in code-point order, edges by the position
|
|
127
|
-
// they are written at (which is unique — one link, one place).
|
|
128
|
-
const entities: EntityEntry[] = [...byId.keys()]
|
|
129
|
-
.sort(cmpCodePoint)
|
|
130
|
-
.map((id) => ({ id, paths: (byId.get(id) as string[]).sort(cmpCodePoint) }))
|
|
173
|
+
visit(root, [], new Set(), false, false)
|
|
131
174
|
|
|
175
|
+
// DETERMINISTIC by construction, not by luck: edges by the position
|
|
176
|
+
// they are written at, which is unique — one link, one place. That
|
|
177
|
+
// holds through a conjunction too: its terms share the position, and
|
|
178
|
+
// two links there would have had to unify into one.
|
|
132
179
|
edges.sort((a, b) => cmpCodePoint(a.at, b.at))
|
|
133
180
|
|
|
134
|
-
|
|
181
|
+
if (0 < disjunct.length) {
|
|
182
|
+
return { edges, disjunct: [...new Set(disjunct)].sort(cmpCodePoint) }
|
|
183
|
+
}
|
|
184
|
+
return { edges }
|
|
135
185
|
}
|
package/src/hcanon.ts
CHANGED
|
@@ -67,7 +67,15 @@ function render(v: any, inh: HMarks): string {
|
|
|
67
67
|
|
|
68
68
|
let s: string
|
|
69
69
|
if (true === v.isMap) {
|
|
70
|
-
|
|
70
|
+
// Alias declarations are dropped here for the same reason
|
|
71
|
+
// MapVal.canon drops them, and this is the surface where it counts:
|
|
72
|
+
// `aon1-` pins MEANING, so a document written with aliases and the
|
|
73
|
+
// same document written longhand must hash to one string. Two
|
|
74
|
+
// renderers, one rule -- hcanon is not canon and does not inherit
|
|
75
|
+
// the filter (docs/design/ALIASES.0.md §4).
|
|
76
|
+
const keys = Object.keys(v.peg)
|
|
77
|
+
.filter((k) => !v.aliasKeys.includes(k))
|
|
78
|
+
.sort(cmpCodePoint)
|
|
71
79
|
s = '{' +
|
|
72
80
|
(v.spread.cj ? '&:' + render(v.spread.cj, inner) +
|
|
73
81
|
(0 < keys.length ? ',' : '') : '') +
|
|
@@ -102,16 +110,6 @@ function render(v: any, inh: HMarks): string {
|
|
|
102
110
|
s = v.canon
|
|
103
111
|
}
|
|
104
112
|
|
|
105
|
-
// The IDENTITY is innermost, exactly as canon writes it (G4 phase
|
|
106
|
-
// 1). It is IN the hash — a node declared `id(svc/auth)` and the
|
|
107
|
-
// same node declared `id(svc/billing)` describe different systems,
|
|
108
|
-
// and a pin that could not tell them apart would be a pin on the
|
|
109
|
-
// shape rather than on the meaning.
|
|
110
|
-
const e = v.entity
|
|
111
|
-
if (null != e) {
|
|
112
|
-
s = 'id(' + JSON.stringify(e) + ')&' + s
|
|
113
|
-
}
|
|
114
|
-
|
|
115
113
|
if (mtype && !inh.type) {
|
|
116
114
|
s = 'type(' + s + ')'
|
|
117
115
|
}
|
package/src/hints.ts
CHANGED
|
@@ -67,6 +67,17 @@ const hints: Record<string, string> = {
|
|
|
67
67
|
|
|
68
68
|
unify_cycle: 'Circular reference detected during unification.',
|
|
69
69
|
|
|
70
|
+
pref_implicit_bag:
|
|
71
|
+
'A preference marks a VALUE, and a bare key is not one. Without\n' +
|
|
72
|
+
'braces the `*` took the whole implicit map as its operand, so the\n' +
|
|
73
|
+
'document became a preferred map rather than a map with a preferred\n' +
|
|
74
|
+
'entry. Brace the bag, or move the `*` onto the value it marks.' +
|
|
75
|
+
'\n \nExamples:\n' +
|
|
76
|
+
' *a: 1 -> nil # No braces: the `*` takes the whole map;\n' +
|
|
77
|
+
' a: *1 -> *1 # ... the `*` belongs on the value;\n' +
|
|
78
|
+
' *{a: 1} -> *{"a":1} # ... or brace the bag to prefer it whole;\n' +
|
|
79
|
+
' *{x:1}|*{y:2} -> *{"x":1}|*{"y":2} # which is what disjunction needs.',
|
|
80
|
+
|
|
70
81
|
constraint:
|
|
71
82
|
'This value does not satisfy the constraint. A constraint is the\n' +
|
|
72
83
|
'meet of bound atoms (min, max, above, below) and exclusions (neq)\n' +
|
|
@@ -153,20 +164,51 @@ const hints: Record<string, string> = {
|
|
|
153
164
|
merge_conflict: 'A version-control conflict marker was found in the source. The\nfile still holds an unresolved merge: resolve it and remove the\n`<<<<<<<`, `=======` and `>>>>>>>` lines before unifying.\n \nExamples:\n <<<<<<< HEAD -> nil # A conflict marker, not a `<` operation;\n ======= -> nil # ... nor a chain of `=` characters;\n >>>>>>> other -> nil # ... nor a `>` operation.',
|
|
154
165
|
|
|
155
166
|
include_denied: 'An @"..." include was refused by the active trust profile\n(docs/trust.md). The document asked to read a source the evaluation\'s\ninclude capability does not allow: widen the capability if the read is\nintended, or remove the include if it is not.\n \nExamples:\n a:@"in-root.aon" -> {..} # Inside the confinement root: allowed;\n a:@"../secret.aon" -> nil # ... but escaping the root is denied;\n a:@"/etc/hostname" -> nil # ... and so is an absolute path outside it.',
|
|
167
|
+
include_extension: 'An @\"...\" include named a file the engine does not read. THE\nEXTENSION DECIDES which of two things a file is: `.aon` and\n`.aontu` are Aontu source, with the whole language in them, and\n`.json`, `.jsonld`, `.jsonc`, `.json5`, `.jsonic`, `.jsc`, `.toml`,\n`.yaml`, `.yml` and `.ini` are configuration DATA, read by that\nformat\'s own parser. Anything else -- and a name with no extension\n-- is refused rather than guessed at: a guess produces a document\nthat looks right and is not.\n \nExamples:\n a:@\"model.aon\" -> {..} # Aontu source;\n a:@\"server.toml\" -> {..} # ... config data, read as TOML;\n a:@\"notes.txt\" -> nil # ... but text is not a document;\n a:@\"data\" -> nil # ... and neither is an unnamed kind.',
|
|
156
168
|
|
|
157
169
|
func_arity: 'This function was called with the wrong number of arguments:\n{func} takes {want}, but was given {got}.\n \nExamples:\n upper(\"a\") -> \"A\" # One argument, which is what upper takes;\n upper(\"a\",\"b\") -> nil # ... so two is a mistake in the source;\n key() -> \"\" # key takes none, or one level count;\n neq(1,2,3) -> neq # ... and neq takes one or more exclusions.',
|
|
170
|
+
func_arg: 'This argument does not fit the function\'s signature:\n \n {sig}\n argument {argn} (`{arg}`) was `{got}`.\n \nThe signature is the declared call surface (one line per builtin,\ntest/spec/signature.tsv); an argument slot admits a concrete value\nof its declared kind, `number` admitting every numeric leaf and\n`string` admitting a path.\n \nExamples:\n upper("a") -> "A" # A string fits upper(s: string|number);\n upper(1) -> 1 # ... and so does a number;\n upper(true) -> nil # ... but a boolean fits neither word;\n add([1],2) -> nil # ... and a list is no operand.',
|
|
158
171
|
|
|
159
172
|
elided_value: 'A key or element was written with no value after the colon. An\nelided value is a mistake in the source rather than a null: write\n`null` if that is what was meant, or supply the value.\n \nExamples:\n a:null -> null # An explicit null, which is a value;\n a: -> nil # ... but nothing at all is not;\n a: b:1 -> {..} # A colon chain is not an elision;\n [1,] -> [1] # ... nor is a trailing comma.',
|
|
160
173
|
|
|
161
|
-
id_name: 'The argument to id() is not an entity name. A name is one or\nmore letters, digits, `_`, `-` or `/`, and NO dots: a dot separates\nan entity name from a path inside that entity, so a dotted name\nwould be ambiguous. A `-` must be quoted, because it is not a\nbare-text character.\n \nExamples:\n id(svc/auth) -> id # Letters, digits and `/` may be bare;\n id("team-pay") -> id # ... a `-` name must be quoted;\n id(svc.auth) -> nil # ... a dot is a path separator, not a name;\n id(1) -> nil # ... and a number is not a name at all.',
|
|
162
174
|
|
|
163
|
-
id_conflict: 'One value was declared to be two different entities. An id() says\nwhat a value IS, so two names on one node is a contradiction, not a\nmerge — the same kind of failure as unifying 1 with 2. Give the node\none name, or give the two names to two nodes.\n \nExamples:\n id(a) & id(a) & {} -> {..} # One entity, said twice;\n id(a) & {x:1} -> {..} # ... an entity with content;\n id(a) & id(b) & {} -> nil # ... but a node cannot be both.',
|
|
164
175
|
|
|
165
|
-
id_spread: 'A spread template stamps one id() onto every child. `&: id(x) & …`\nsays that EVERY child of the bag is the entity `x`, and identity\nmerging would then unify all of them into one. Use a\npath-dependent name — `id(key())` — to give each child its own,\nor move the id() to the one child that has it.\n \nExamples:\n {&: id(key()), a:{}, b:{}} -> {..} # A name per child;\n {a: id(x) & {}} -> {..} # ... or one named child;\n {&: id(x), a:{}, b:{}} -> nil # ... but not one name for all.',
|
|
166
176
|
|
|
167
|
-
|
|
177
|
+
recursion_unexpanded: 'A schema refers to itself here, and no data reached this position\nto expand it against. Guard the recursion -- an optional key\n(next?:) drops when nothing arrives, and a preferred alternative\n(*null | $.Node) generates -- or supply the data.\n \nExamples:\n Node: {v: integer, next?: $.Node}\n t: $.Node & {v: 1} -> {..} # next? drops;\n Node: {v: integer, next: $.Node}\n t: $.Node & {v: 1} -> nil # ... required refuses.',
|
|
178
|
+
recursion_budget: 'A recursive schema expanded past the evaluation depth budget\nwithout meeting concrete data. Expansion is driven by the data --\nfinite data always terminates -- so a chain this deep means two\ndefinitions feeding each other, or data deeper than the budget\n(docs/trust.md raises it deliberately).',
|
|
179
|
+
list_length: 'A literal list alternative in a disjunction admits only a list of\nits own length -- a spread (&:) makes it variadic. Outside a\ndisjunction two statements of one list still merge elementwise.\n \nExamples:\n x: [] | [&: integer]\n x: [1, 2] -> [1,2] # The variadic arm;\n x: [] -> [] # ... or exactly empty;\n y: [a] | [b]\n y: [a, extra] -> nil # ... a literal arm is its length.',
|
|
180
|
+
relation_cycle: 'This relation declared acyclic(), and its edges form a cycle. The\nverdict lands at generation, where every edge is known; the error\npoints at an edge on the cycle and names the nodes it runs\nthrough, closing back on the first.\n \nExamples:\n dependsOn: rel() & acyclic()\n a: {dependsOn: ["$.b"]}\n b: {dependsOn: ["$.a"]} -> nil # $.a -> $.b -> $.a;\n b: {dependsOn: []} -> {..} # ... one edge fewer passes.',
|
|
181
|
+
relation_inverse_missing: 'This relation declared inverse(name), and an edge has no mirroring\nedge under that name: A relates to B, and B does not name A back.\nThe declaration asks for the mirror to be WRITTEN, and never writes\nit for the author -- generation is refused until the document says\nboth directions.\n \nExamples:\n a: {dependsOn: rel() & inverse(dependedOnBy) & [b]}\n b: {dependedOnBy: rel() & [a]} -> {..} # Mirrored;\n b: {dependedOnBy: rel() & []} -> nil # ... and this is not.',
|
|
182
|
+
inverse_name: 'The argument to inverse() is not a relation name. The mirroring\npredicate is a D-1 name -- a letter or `_`, then letters, digits,\n`_` or `-` -- spelled bare or quoted.\n \nExamples:\n inverse(dependedOnBy) -> inverse # A name;\n inverse("dep-on") -> inverse # ... quoted when hyphenated;\n inverse(1) -> nil # ... a number is not a name.',
|
|
183
|
+
rel_address: 'A rel() field holds tree addresses: one path value, a list of path\nvalues, or a map whose leaves are path values. A bare string is never\nan address — `path("...")` is the one conversion — and this value can\nnever be one.\n \nExamples:\n dependsOn: rel() & [path($.ledger)] -> {..} # A list of addresses;\n hostedOn: rel() & path($.bastion) -> {..} # ... or one;\n dependsOn: rel() & ["$.ledger"] -> nil # ... a string is not a path;\n dependsOn: rel() & [7] -> nil # ... and neither is 7.',
|
|
184
|
+
refer_address: 'A refer() was given something that is not a tree address. An address\nis a PATH VALUE — `path($.a.b)` from the document root, `path(.b)`\nfrom the link\'s own sibling scope — and only a path value can be one:\na bare string never is, and `path("...")` is the one conversion.\n \nExamples:\n refer() & path($.services.auth) -> ... # From the root;\n refer() & path(.auth) -> ... # ... or beside the link;\n refer() & "$.services.auth" -> nil # ... a string is not a path;\n refer() & 1 -> nil # ... and neither is a number.',
|
|
185
|
+
|
|
186
|
+
path_address: 'This is not a tree address, so it cannot be a path value. An address\nis `$.a.b` from the document root, or `.b` from the sibling scope with\none more leading dot per parent step -- the grammar path() captures.\nString text with no anchor converts as RELATIVE, and a string\nconverts ONLY through the call\'s own argument.\n \nExamples:\n d: path("$.services.auth") -> path($.services.auth) # A converted string;\n d: path("auth") -> path(.auth) # ... anchorless text is relative;\n d: path("a..b") -> nil # ... but this spells nothing;\n d: path("$") -> nil # ... and the root is not addressable.',
|
|
187
|
+
|
|
188
|
+
rel_unresolved: 'The address names no node in this evaluation. Every position in the\ndocument is addressable; nothing outside it is.\n \nExamples:\n p: {}\n q: rel() & "$.p" -> {..} # $.p is a node;\n q: rel() & "$.nosuch" -> nil # ... $.nosuch is not.',
|
|
189
|
+
refer_unresolved: 'A refer() address names no node in this evaluation. Within one\nevaluation the document-set is fixed, so a link to nothing is an error\nrather than something to resolve later: check the spelling, or add the\nnode it was meant to reach. A relative address that climbs off the top\nof the tree lands here too.\n \nExamples:\n a:{p:1} b:refer()&"$.a" -> "$.a" # $.a is a node;\n a:{p:1} b:refer()&"$.a.p" -> "$.a.p" # ... and so is a node inside it;\n b:refer()&"$.nope" -> nil # ... but nothing is here.',
|
|
190
|
+
view_relation_unknown: 'The relation named to the view has no edges in this document, so\nthe figure would be empty -- and an empty figure and a misspelled name are\nthe same file on disk. Check the spelling against the relations the\nnote lists, or drop the relation to draw every relation at once.',
|
|
191
|
+
|
|
192
|
+
view_kind_unknown: 'The figure kind is not one the verb draws. The kinds are tree, matrix,\ngraph, layer, sets, layers, ladder and poset; the note lists them.',
|
|
193
|
+
|
|
194
|
+
view_profile_unknown: 'The figure kind does not render into the profile asked for: there is no\ntext form of a node-link drawing and no Mermaid form of a matrix. The\nnote lists the profiles the kind declares; the first is its default.',
|
|
195
|
+
view_style_profile: 'Each profile has ONE way to carry the meaning of a figure\'s marks:\nSGR escapes for text, CSS classes for svg. Asking for the other one is\na usage error rather than a silent no-op. `none` works everywhere.',
|
|
196
|
+
view_style_unknown: 'The styles are none, ansi and css, plus `auto` at the command line,\nwhich the command resolves before the library runs: whether the\ndestination is a terminal is not something a library can see.',
|
|
197
|
+
|
|
198
|
+
view_rows_exceeded: 'The figure has more rows than --max-rows allows. This is a REFUSAL,\nnot a truncation: a view that quietly omits things is the failure the\nverb exists to avoid. Narrow the figure with --at or --relation, or\nraise the limit.',
|
|
199
|
+
|
|
200
|
+
view_line_break: 'A label the figure would draw holds a line terminator (U+000A, U+000D,\nU+2028 or U+2029), and a figure line cannot carry one. Label the node\nwith another field, or drop --label / --group-by.',
|
|
201
|
+
|
|
202
|
+
view_relation_ambiguous: 'The document has edges under several relations and the matrix (or the\nlayer diagram) draws exactly one. Name it with --relation; the note\nlists the relations with edges.',
|
|
168
203
|
|
|
169
|
-
|
|
204
|
+
view_sets_shape: 'The set panel reads generated values: --sets must name a map whose\nvalues each hold the --member field as a list of strings, and\n--universe a map or a list of strings. The path in the finding is the\nvalue that has another shape.',
|
|
205
|
+
|
|
206
|
+
view_sets_required: 'The set panel needs both --sets (the map whose keys are the sets) and\n--member (the field holding each set\'s members).',
|
|
207
|
+
|
|
208
|
+
view_at_required: 'The meet ladder draws the contributions at ONE path, and none was\nnamed. Pass --at with the path, as `aontu why` takes it.',
|
|
209
|
+
|
|
210
|
+
view_group_required: 'The layer diagram puts each node in the band its --group-by field\nnames, and no field was named. Pass --group-by with the field that\nholds each node\'s layer.',
|
|
211
|
+
view_document_shape: 'A view document declares each figure as a map of view options --\nthe flag names without the dashes -- and every declaration must name\nits `kind` and the `out` file it draws into. This one names an option\nthat is not one, gives a value of the wrong shape, or leaves out what\nevery declaration needs. `aontu view --help` lists the options.',
|
|
170
212
|
|
|
171
213
|
pack_data: 'The first argument to pack() is not a bag. `pack` makes one child\nper child of its DATA, so the data has to have children: a list of\nnames, or a map whose keys are the names.\n \nExamples:\n pack([a,b], {x:1}) -> {..} # A list of names;\n pack({a:1,b:2}, {x:1}) -> {..} # ... or a map, keyed by its keys;\n pack(1, {x:1}) -> nil # ... but a scalar has no children.',
|
|
172
214
|
|
|
@@ -180,8 +222,8 @@ const hints: Record<string, string> = {
|
|
|
180
222
|
|
|
181
223
|
place_pair: 'Two placeholders met, and neither has a value to fill the other.\n`_` is a HOLE: it is filled by whatever the call is unified with, so\na call holding one needs a peer that does not. Give one side a\nvalue.\n \nExamples:\n upper(_) & hello -> "HELLO" # The peer fills the hole;\n _ + 2 & 1 -> 3 # ... whatever the call is;\n upper(_) & lower(_) -> nil # ... but two holes fill nothing.',
|
|
182
224
|
|
|
183
|
-
pipe_target: 'The right-hand side of a `|>` is not a function. A pipe puts the\nvalue on its left in as the FIRST argument of the call on its\nright, so the right side has to be one: a call, or the bare name of\na built-in.\n \nExamples:\n hello |> upper -> "HELLO" # A bare name is the call;\n $.names |> pack({}) -> {..} # ... or a call with more arguments;\n 1 |> 2 -> nil # ... but a value is not a function.',
|
|
184
225
|
|
|
226
|
+
module_path: 'A module import is domain-shaped and carries a major, but its\npath cannot be a directory on every platform the toolchain runs on --\nso it is refused before anything is built from it. An element may not\nbe empty, may not begin or end with `.` (which is what forbids `..`),\nand may not be a reserved device name. These are Go\'s module-path\nrules, for Go\'s reason: a module path becomes a real directory.\n \nExamples:\n @"corp.example/s@1" -> {..} # An ordinary path is fine;\n @"corp.example/../s@1" -> nil # ... `..` would escape the store;\n @"corp.example/nul@1" -> nil # ... and Windows has no such file.',
|
|
185
227
|
module_missing: 'A module import names a module that is not in this project. A\nmodule is resolved from LOCAL stores only -- `aon_vendor/` beside the\nproject\'s mod.aon, then the user cache -- because evaluation never\ntouches the network. Fetching is a separate step, and the message\nnames it.\n \nExamples:\n @"corp.example/s@1" -> nil # Not fetched: run aontu mod get;\n @"./local.aon" -> {..} # ... a local path is not a module;\n @"corp.example/s@1#aon1-…" -> {..} # ... and a pin does not fetch it either.',
|
|
186
228
|
|
|
187
229
|
module_integrity: 'A module resolved locally does not have the MEANING it was pinned\nto. The pin is a canon-hash -- the hash of the module unified\nstandalone -- so it survives comments, formatting and refactoring and\nbreaks on any semantic change in the module\'s transitive closure.\nVerification is always local: the registry\'s annotation is advisory.\n \nExamples:\n @"corp.example/s@1" -> {..} # No pin, no check;\n @"corp.example/s@1#aon1-x" -> nil # ... a pin that disagrees refuses;\n aontu hash <file> # ... and this is what it should be.',
|
|
@@ -248,6 +290,22 @@ const hints: Record<string, string> = {
|
|
|
248
290
|
' pick([{a:1},{b:2}], a) -> nil # ... the second does not;\n' +
|
|
249
291
|
' pick([[9],[8]], 0) -> [9,8] # A list child takes an index.',
|
|
250
292
|
|
|
293
|
+
join_member:
|
|
294
|
+
'A member of this bag is not text and never will be: `{member}`.\n' +
|
|
295
|
+
'`join` folds with `+` seeded with the empty string, and `+` with a\n' +
|
|
296
|
+
'string on the left RESIDUATES on a map, a list or a null rather\n' +
|
|
297
|
+
'than refusing — so folding blindly would report the failure at\n' +
|
|
298
|
+
'generation, naming the whole call instead of the member. It is\n' +
|
|
299
|
+
'refused here, where the member can still be named. A member that\n' +
|
|
300
|
+
'is merely UNRESOLVED is a different thing: the call stays\n' +
|
|
301
|
+
'residual and generation reports it as ordinary incompleteness.' +
|
|
302
|
+
'\n \nExamples:\n' +
|
|
303
|
+
' join([1,2], "-") -> "1-2" # Numbers render as digits;\n' +
|
|
304
|
+
' join([true], ",") -> "true" # ... so do booleans;\n' +
|
|
305
|
+
' join([{a:1}], ",") -> nil # A map is not text;\n' +
|
|
306
|
+
' join([null], ",") -> nil # ... and neither is null;\n' +
|
|
307
|
+
' join(pick($.r, n), ",") -> "a,b" # Project first.',
|
|
308
|
+
|
|
251
309
|
aggregate_data:
|
|
252
310
|
'This aggregate needs a BAG to fold: a list or a map. `sum`,\n' +
|
|
253
311
|
'`least` and `greatest` walk the children of the value they are\n' +
|
|
@@ -363,8 +421,14 @@ const hints: Record<string, string> = {
|
|
|
363
421
|
'required_listelem': 'Required list element is missing. A non-optional list element has no value.',
|
|
364
422
|
|
|
365
423
|
// Junction errors (disjunction/conjection)
|
|
366
|
-
'
|
|
367
|
-
'
|
|
424
|
+
'empty': 'Empty disjunction. The disjunction has no valid alternatives.',
|
|
425
|
+
'empty-dist': 'Empty disjunction distribution. All alternatives in the disjunction are invalid.',
|
|
426
|
+
|
|
427
|
+
// ADR-011 R2: two DEFAULTS of equal rank that cannot agree. The
|
|
428
|
+
// fix is a rank, so the hint names it.
|
|
429
|
+
'pref_rank_clash': 'Two defaults of the same rank disagree.' +
|
|
430
|
+
' Rank one of them (`**x`) to say which is the weaker layer,' +
|
|
431
|
+
' or give them the same value.',
|
|
368
432
|
|
|
369
433
|
'max_depth': 'Input nesting is too deep to process safely.',
|
|
370
434
|
|
|
@@ -406,6 +470,7 @@ const codeClasses: Record<string, string> = {
|
|
|
406
470
|
parse_bad_src: 'parse',
|
|
407
471
|
merge_conflict: 'parse',
|
|
408
472
|
include_denied: 'parse',
|
|
473
|
+
include_extension: 'parse',
|
|
409
474
|
|
|
410
475
|
// G3 -- the subsumption query's report vocabulary (class compat):
|
|
411
476
|
// the compat_* codes are its findings, the sub_* codes its undecided
|
|
@@ -431,24 +496,37 @@ const codeClasses: Record<string, string> = {
|
|
|
431
496
|
patch_ambiguous: 'reference',
|
|
432
497
|
patch_span_mismatch: 'internal',
|
|
433
498
|
|
|
434
|
-
// G4 phase
|
|
435
|
-
// different names on one node. `id_name` is a parse-class refusal
|
|
436
|
-
// of the argument; `id_conflict` is a conflict like any other
|
|
437
|
-
// failed meet, because that is exactly what it is.
|
|
438
|
-
id_name: 'parse',
|
|
439
|
-
id_conflict: 'conflict',
|
|
440
|
-
|
|
441
|
-
// Clearing rule 3: a constant `id()` inside an `&:` template. Class
|
|
442
|
-
// `parse`, because what is wrong is the TEXT of the template rather
|
|
443
|
-
// than any pair of values it brought together.
|
|
444
|
-
id_spread: 'parse',
|
|
445
|
-
|
|
446
|
-
// G4 phase 2 -- the checked link: a string that is not an entity
|
|
499
|
+
// G4 phase 2 -- the checked link: a string that is not a tree
|
|
447
500
|
// address (class `parse`, the text is wrong), and an address that
|
|
448
501
|
// names nothing in this evaluation (class `reference`, the same
|
|
449
502
|
// class as `no_path`, because it is the same kind of miss).
|
|
450
503
|
refer_address: 'parse',
|
|
504
|
+
rel_address: 'parse',
|
|
451
505
|
refer_unresolved: 'reference',
|
|
506
|
+
rel_unresolved: 'reference',
|
|
507
|
+
|
|
508
|
+
// The tree view (docs/design/VIEWS.0.md): a relation that draws
|
|
509
|
+
// nothing is refused rather than drawn empty. Class `reference`: a
|
|
510
|
+
// name that does not resolve.
|
|
511
|
+
view_relation_unknown: 'reference',
|
|
512
|
+
view_kind_unknown: 'reference',
|
|
513
|
+
view_profile_unknown: 'reference',
|
|
514
|
+
view_style_profile: 'reference',
|
|
515
|
+
view_style_unknown: 'reference',
|
|
516
|
+
view_rows_exceeded: 'budget',
|
|
517
|
+
view_line_break: 'parse',
|
|
518
|
+
view_relation_ambiguous: 'reference',
|
|
519
|
+
view_sets_shape: 'reference',
|
|
520
|
+
view_sets_required: 'reference',
|
|
521
|
+
view_at_required: 'reference',
|
|
522
|
+
view_group_required: 'reference',
|
|
523
|
+
view_document_shape: 'reference',
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
// First-class paths (docs/design/PATHS.0.md): text that is not a
|
|
527
|
+
// tree address, met by path()'s capture or promotion. The same
|
|
528
|
+
// class as refer_address, because it is the same mistake.
|
|
529
|
+
path_address: 'parse',
|
|
452
530
|
|
|
453
531
|
// G8 phase 1 -- the generation combinators. All three are class
|
|
454
532
|
// `parse`: what is wrong is the CALL as written (data that is not a
|
|
@@ -471,15 +549,11 @@ const codeClasses: Record<string, string> = {
|
|
|
471
549
|
// conflict is.
|
|
472
550
|
place_pair: 'conflict',
|
|
473
551
|
|
|
474
|
-
// G8 phase 4 -- the pipe. Class `parse`: a pipe is sugar resolved
|
|
475
|
-
// while reading the source, so a pipe into something that is not a
|
|
476
|
-
// call is wrong in the TEXT and no later pass can repair it.
|
|
477
|
-
pipe_target: 'parse',
|
|
478
|
-
|
|
479
552
|
// G6 phase 2 -- modules. Both are class `parse`: a module import is
|
|
480
553
|
// resolved while the source is READ, and neither a module that is
|
|
481
554
|
// absent nor one whose meaning disagrees with its pin can be
|
|
482
555
|
// repaired by any later pass.
|
|
556
|
+
module_path: 'parse',
|
|
483
557
|
module_missing: 'parse',
|
|
484
558
|
module_integrity: 'parse',
|
|
485
559
|
module_depth: 'budget',
|
|
@@ -490,11 +564,17 @@ const codeClasses: Record<string, string> = {
|
|
|
490
564
|
// and a lattice citizen may not be falsified by more information.
|
|
491
565
|
relation_cycle: 'conflict',
|
|
492
566
|
relation_inverse_missing: 'conflict',
|
|
493
|
-
|
|
567
|
+
inverse_name: 'parse',
|
|
568
|
+
list_length: 'conflict',
|
|
569
|
+
recursion_unexpanded: 'incomplete',
|
|
570
|
+
recursion_budget: 'budget',
|
|
494
571
|
func_arity: 'parse',
|
|
495
572
|
elided_value: 'parse',
|
|
496
573
|
unify_no_src: 'parse',
|
|
497
574
|
incomplete_expression: 'parse',
|
|
575
|
+
pref_implicit_bag: 'parse',
|
|
576
|
+
alias_not_toplevel: 'parse',
|
|
577
|
+
alias_in_path: 'parse',
|
|
498
578
|
not_number: 'parse',
|
|
499
579
|
negative: 'parse',
|
|
500
580
|
decimal_syntax: 'parse',
|
|
@@ -516,13 +596,15 @@ const codeClasses: Record<string, string> = {
|
|
|
516
596
|
literal_nil: 'conflict',
|
|
517
597
|
nil_gen: 'conflict',
|
|
518
598
|
unite: 'conflict',
|
|
519
|
-
'
|
|
520
|
-
'
|
|
599
|
+
'empty': 'conflict',
|
|
600
|
+
'empty-dist': 'conflict',
|
|
601
|
+
pref_rank_clash: 'conflict',
|
|
521
602
|
exact_float_mix: 'conflict',
|
|
522
603
|
inexact_integer_sum: 'conflict',
|
|
523
604
|
pick_key: 'conflict',
|
|
524
605
|
aggregate_data: 'conflict',
|
|
525
606
|
aggregate_empty: 'conflict',
|
|
607
|
+
join_member: 'conflict',
|
|
526
608
|
divide_by_zero: 'conflict',
|
|
527
609
|
inexact_divide: 'conflict',
|
|
528
610
|
float_overflow: 'conflict',
|
|
@@ -530,6 +612,7 @@ const codeClasses: Record<string, string> = {
|
|
|
530
612
|
lossy_integer_literal: 'conflict',
|
|
531
613
|
arg: 'conflict',
|
|
532
614
|
'invalid-arg': 'conflict',
|
|
615
|
+
func_arg: 'conflict',
|
|
533
616
|
no_first_arg: 'conflict',
|
|
534
617
|
key_level: 'conflict',
|
|
535
618
|
func: 'conflict',
|
package/src/jsonschema.ts
CHANGED
|
@@ -109,6 +109,10 @@ const KIND_TYPE: Record<string, string> = {
|
|
|
109
109
|
Float: 'number',
|
|
110
110
|
BigDecimal: 'number',
|
|
111
111
|
Number: 'number',
|
|
112
|
+
// A path value is its address string at the JSON boundary
|
|
113
|
+
// (docs/design/PATHS.0.md), so the projection says `string` -- the
|
|
114
|
+
// same lossy-projection rule the exact numeric leaves follow.
|
|
115
|
+
Path: 'string',
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
|
|
@@ -238,7 +242,44 @@ function fromConstraint(ctx: Ctx, path: string[], c: any): any {
|
|
|
238
242
|
// (the marker drops LINES, not branches). Go's twin keeps its `nil ==
|
|
239
243
|
// v` arm because a missing map key there yields a typed nil rather
|
|
240
244
|
// than an absent property.
|
|
245
|
+
// DEPRECATION IS AN ANNOTATION, and 2020-12 has one: `deprecated`.
|
|
246
|
+
// The export used to drop the whole record in SILENCE -- no keyword,
|
|
247
|
+
// and no loss line even under --strict -- which is the one thing the
|
|
248
|
+
// verb's own contract says it never does (use-cases/BUGS.md §56).
|
|
249
|
+
//
|
|
250
|
+
// The boolean crosses faithfully. The record's STRINGS (msg, use,
|
|
251
|
+
// since) have no home in 2020-12, so they are reported as a loss
|
|
252
|
+
// rather than invented into `description`: this exporter emits no
|
|
253
|
+
// `description` anywhere, and quietly making it mean "deprecation
|
|
254
|
+
// note" would be a mapping a consumer cannot undo.
|
|
241
255
|
function fromVal(ctx: Ctx, path: string[], v: any): any {
|
|
256
|
+
const out = fromValInner(ctx, path, v)
|
|
257
|
+
|
|
258
|
+
const dep = v?.deprecation
|
|
259
|
+
if (null != dep && null != out && 'object' === typeof out) {
|
|
260
|
+
const said = DEPRECATION_TEXT.filter((k) => null != dep[k])
|
|
261
|
+
if (0 < said.length) {
|
|
262
|
+
lose(ctx, path, 'deprecate',
|
|
263
|
+
'JSON Schema 2020-12 has the `deprecated` flag and no field for ' +
|
|
264
|
+
'what it SAYS, so ' + said.join('/') + ' cannot cross; the ' +
|
|
265
|
+
'schema marks the property deprecated and a consumer must read ' +
|
|
266
|
+
'the model for the reason')
|
|
267
|
+
}
|
|
268
|
+
return { ...out, deprecated: true }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return out
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
// The record's text keys -- the ones with nothing to carry them.
|
|
276
|
+
// `DEPRECATION_KEYS` in DeprecateFuncVal.ts is the authority on what a
|
|
277
|
+
// record may hold; this is that list, and a key added there without a
|
|
278
|
+
// JSON Schema home belongs here too.
|
|
279
|
+
const DEPRECATION_TEXT = ['msg', 'use', 'since']
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
function fromValInner(ctx: Ctx, path: string[], v: any): any {
|
|
242
283
|
// A preference is its inner value plus a DEFAULT. JSON Schema's
|
|
243
284
|
// `default` is annotation rather than constraint -- it does not
|
|
244
285
|
// validate -- which is exactly what a preference is when something
|