aontu 0.61.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 +4 -4
- package/dist/agentsmd.d.ts +1 -0
- package/dist/agentsmd.js +7 -28
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.d.ts +23 -0
- package/dist/allow.js +138 -0
- package/dist/allow.js.map +1 -0
- package/dist/aontu.d.ts +4 -2
- package/dist/aontu.js +4 -80
- 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.d.ts +10 -1
- package/dist/cli.js +877 -479
- 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 +55 -189
- 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.d.ts +16 -0
- package/dist/helpdoc.js +59 -0
- package/dist/helpdoc.js.map +1 -0
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +59 -47
- 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 +32 -877
- package/dist/lang.js.map +1 -1
- package/dist/lower.d.ts +3 -0
- package/dist/lower.js +14 -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.d.ts +2 -0
- package/dist/relation.js +3 -58
- package/dist/relation.js.map +1 -1
- package/dist/render.js +33 -143
- 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.d.ts +20 -0
- package/dist/val/CmpFuncVal.js +188 -0
- package/dist/val/CmpFuncVal.js.map +1 -0
- 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.d.ts +1 -2
- package/dist/val/EachFuncVal.js +12 -29
- 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 +11 -6
- 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 +2 -36
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.d.ts +12 -0
- package/dist/val/NomFuncVal.js +153 -0
- package/dist/val/NomFuncVal.js.map +1 -0
- 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.d.ts +12 -0
- package/dist/val/TranslateFuncVal.js +99 -0
- package/dist/val/TranslateFuncVal.js.map +1 -0
- package/dist/val/UpperFuncVal.js +11 -6
- 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.d.ts +3 -0
- package/dist/val/caserange.js +49 -0
- package/dist/val/caserange.js.map +1 -0
- 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.d.ts +12 -0
- package/dist/vet.js +159 -412
- 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 +9 -7
- package/grammar/aontu.gbnf +5 -5
- package/grammar/aontu.lark +5 -5
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/skill/SKILL.md +8 -0
- package/skill/init/check.sh +28 -0
- package/skill/init/data.aon +12 -0
- package/skill/init/model.aon +19 -0
- package/skill/tasks.md +151 -0
- package/src/agentsmd.ts +8 -32
- package/src/alias.ts +0 -39
- package/src/allow.ts +221 -0
- package/src/aontu.ts +10 -108
- package/src/aontumodel.ts +32 -0
- package/src/cli.ts +1009 -540
- 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 +63 -234
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +77 -0
- package/src/hints.ts +72 -49
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +39 -895
- package/src/lower.ts +15 -65
- 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 +7 -71
- package/src/render.ts +33 -180
- 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 +249 -0
- 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 +12 -53
- 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 +12 -7
- 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 +2 -36
- package/src/val/NomFuncVal.ts +200 -0
- 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 +132 -0
- package/src/val/UpperFuncVal.ts +12 -7
- 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 +53 -0
- 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 +209 -504
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -637
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/src/std.ts +0 -648
- package/src/val/FormFuncVal.ts +0 -119
package/src/ctx.ts
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
} from './err'
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
type AontuContextConfig = {
|
|
21
20
|
cc?: number
|
|
22
21
|
err?: any[] // Omit<NilVal[], "push">
|
|
@@ -27,12 +26,6 @@ type AontuContextConfig = {
|
|
|
27
26
|
// error list is, so one run has one record.
|
|
28
27
|
prov?: any
|
|
29
28
|
|
|
30
|
-
// THE READ SET (RENDER.0.md P7), or absent for an uninstrumented
|
|
31
|
-
// run: every tree path a reference resolved to, in one shared set.
|
|
32
|
-
// It is what `render --coverage` measures the model against -- a
|
|
33
|
-
// path no read reached is model the transform never consumed -- and
|
|
34
|
-
// its presence is also what switches the two render riders on
|
|
35
|
-
// (Val.origin, Val.emitted), so one flag turns the whole record on.
|
|
36
29
|
reads?: Set<string>
|
|
37
30
|
fs?: any
|
|
38
31
|
errfs?: any
|
|
@@ -56,24 +49,6 @@ class AontuContext {
|
|
|
56
49
|
vc: number // Val counter to create unique val ids.
|
|
57
50
|
cc: number = -1
|
|
58
51
|
|
|
59
|
-
// THE STAGING RULE (G8 phase 0,
|
|
60
|
-
// docs/capability-review/g8-generation.md). A value whose answer
|
|
61
|
-
// depends on WHERE IT IS -- `key()` today, the generation
|
|
62
|
-
// combinators next -- must not answer while anything is still
|
|
63
|
-
// moving it: resolved early it reports the position it was WRITTEN
|
|
64
|
-
// at rather than the one it ends up at. Such a value RESIDUATES
|
|
65
|
-
// while this is false, and fires exactly once on the pass where it
|
|
66
|
-
// is true.
|
|
67
|
-
//
|
|
68
|
-
// The pass loop (ts/src/unify.ts) sets it on the first pass whose
|
|
69
|
-
// input tree is IDENTICAL to the previous pass's: everything that
|
|
70
|
-
// was going to move has moved, and what is left is the staged
|
|
71
|
-
// values themselves, which is precisely the moment they may answer.
|
|
72
|
-
// It replaces a `ctx.cc < 3` pass count in KeyFuncVal -- a magic
|
|
73
|
-
// number, right for the documents it was tuned on and silently
|
|
74
|
-
// wrong for anything that took a fourth pass to place a value. The
|
|
75
|
-
// comment it replaces said as much: "this delay makes keys in
|
|
76
|
-
// spreads and refs work, but it is a hack - find a better way".
|
|
77
52
|
settle: boolean = false
|
|
78
53
|
vars: Record<string, Val> = {}
|
|
79
54
|
src?: string
|
|
@@ -86,18 +61,6 @@ class AontuContext {
|
|
|
86
61
|
|
|
87
62
|
collect: boolean
|
|
88
63
|
|
|
89
|
-
// THE COMPLETENESS PROBE (the review's finding C). vet detects
|
|
90
|
-
// residue by GENERATING the anchored meet and keeping the
|
|
91
|
-
// incomplete-class failures. Generation honours the OUTPUT marks --
|
|
92
|
-
// `type()` and `hide()` say "do not emit this" -- so a `--at` anchor
|
|
93
|
-
// sitting under a mark generated nothing at all, reported nothing,
|
|
94
|
-
// and vetted VALID for data missing a required field, while the same
|
|
95
|
-
// anchor without the mark answered incomplete (use-cases/BUGS.md
|
|
96
|
-
// §14). A mark is a decision about OUTPUT; it is not a statement
|
|
97
|
-
// about what the data must satisfy, and `--at` names the truth to
|
|
98
|
-
// validate against explicitly. Under this flag the generation walk
|
|
99
|
-
// descends through marked values; nothing else changes, and no
|
|
100
|
-
// output is produced from a probe run -- only its findings are read.
|
|
101
64
|
probe: boolean = false
|
|
102
65
|
|
|
103
66
|
// The provenance recorder (G7 phase 3), or undefined for an
|
|
@@ -113,7 +76,6 @@ class AontuContext {
|
|
|
113
76
|
err: any[]
|
|
114
77
|
explain: any[] | null
|
|
115
78
|
|
|
116
|
-
// TODO: separate options and context!!!
|
|
117
79
|
srcpath?: string
|
|
118
80
|
|
|
119
81
|
deps: Record<string, any>
|
|
@@ -122,49 +84,15 @@ class AontuContext {
|
|
|
122
84
|
_pathstr: string | undefined
|
|
123
85
|
_pathidx: number | undefined
|
|
124
86
|
_pathmap: Map<string, number>
|
|
125
|
-
// Trie keyed by (parentIdx, key) -> { idx, path }. Serves two
|
|
126
|
-
// jobs: (1) assign O(1) pathidx without rebuilding
|
|
127
|
-
// `path.join('\x00')` for cycle-detection; (2) cache the
|
|
128
|
-
// materialised path array so the same (parent, key) visited
|
|
129
|
-
// across fixpoint passes reuses one array instead of re-concat.
|
|
130
87
|
_pathTrie: Map<number, Map<string, { idx: number, path: string[] }>>
|
|
131
88
|
_pathidxNext: { n: number }
|
|
132
89
|
|
|
133
|
-
// Current `unite` recursion depth, checked against the depth budget
|
|
134
|
-
// (ts/src/unify.ts). Held in a shared mutable box, like _pathidxNext,
|
|
135
|
-
// because clone() uses Object.create: the box is inherited by
|
|
136
|
-
// reference, so a nested clone's increments are visible to the frame
|
|
137
|
-
// that will decrement them. The Go port keeps the same counter
|
|
138
|
-
// directly on its Ctx pointer (go/unify.go, maxUniteDepth).
|
|
139
90
|
_depth: { n: number }
|
|
140
91
|
|
|
141
|
-
// The relation declarations this evaluation accumulates (RELATIONS
|
|
142
|
-
// P2): predicate -> what its graph atoms said. One Map per
|
|
143
|
-
// evaluation, created here and inherited by reference through
|
|
144
|
-
// clone()'s prototype chain, exactly as _depth's box is -- a clone
|
|
145
|
-
// made before the first registration must still share the registry.
|
|
146
92
|
_reldecls: Map<string, { acyclic?: boolean, inverses: Set<string> }>
|
|
147
93
|
|
|
148
|
-
// The tree a recursive residual's target resolves against when the
|
|
149
|
-
// meet's own root does not contain it (RECURSION.0.md). Normally
|
|
150
|
-
// undefined: a residual expands by walking ctx.root, and the root
|
|
151
|
-
// holds the definition. An ANCHORED vet run meets a subtree LIFTED
|
|
152
|
-
// out of the settled schema, so `$.spec.Step` names nothing in the
|
|
153
|
-
// meet's root -- the residual held its peer forever and the data
|
|
154
|
-
// under it vetted VALID unchecked. Vet sets this to the settled
|
|
155
|
-
// schema root for anchored runs; RecurseVal.body falls back to it
|
|
156
|
-
// only when the root walk finds nothing. Inherited by clone()
|
|
157
|
-
// through the prototype chain.
|
|
158
94
|
_fixroot: any
|
|
159
95
|
|
|
160
|
-
// The evaluation budgets (G5 trust profile, docs/trust.md): integer
|
|
161
|
-
// counts of engine events, never wall-clock. Always present, defaults
|
|
162
|
-
// from the shared spec-visible constants (test/spec/budget.tsv), so
|
|
163
|
-
// the hot-path reads in unify.ts are plain property loads. Inherited
|
|
164
|
-
// by clone() through the prototype chain. `revisits` is NOT profile
|
|
165
|
-
// surface (the Go port has no revisit counter to configure — see
|
|
166
|
-
// TrustBudget in type.ts); it is carried here so unify.ts reads one
|
|
167
|
-
// budget object, at its fixed spec constant.
|
|
168
96
|
budget: { passes: number, revisits: number, depth: number }
|
|
169
97
|
|
|
170
98
|
// The include manifest sink (G5, docs/trust.md): every include the
|
|
@@ -172,20 +100,8 @@ class AontuContext {
|
|
|
172
100
|
// Aontu.parse() sorts and dedups it onto the result's `deps`.
|
|
173
101
|
manifest: { path: string, capability: string }[]
|
|
174
102
|
|
|
175
|
-
// Trial mode: set by DisjunctVal.unify while each member is tried
|
|
176
|
-
// against the peer. When true, makeNilErr returns the shared
|
|
177
|
-
// TRIAL_NIL sentinel instead of allocating a fresh NilVal, and
|
|
178
|
-
// pushes TRIAL_NIL to ctx.err only once per trial (the caller's
|
|
179
|
-
// `trialErr.length > 0` check still signals failure). See err.ts.
|
|
180
103
|
_trialMode?: boolean
|
|
181
104
|
|
|
182
|
-
// Per-parent descend cache: (key) -> already-descended child ctx.
|
|
183
|
-
// ~48% of descends in foo-sdk repeat the same (parent, key) pair
|
|
184
|
-
// (e.g. a MapVal.unify visits the same peer keys across fixpoint
|
|
185
|
-
// passes). The child's prototype chain, path, and pathidx are
|
|
186
|
-
// identical every time, and no code writes to a descended ctx
|
|
187
|
-
// between visits — nothing mutates per-child state — so the
|
|
188
|
-
// cached child is safe to reuse.
|
|
189
105
|
_childCache?: Map<string, AontuContext>
|
|
190
106
|
|
|
191
107
|
|
|
@@ -258,9 +174,6 @@ class AontuContext {
|
|
|
258
174
|
ctx.explain = Array.isArray(cfg.explain) ? cfg.explain : ctx.explain
|
|
259
175
|
|
|
260
176
|
ctx._pathstr = undefined
|
|
261
|
-
// Path didn't move unless cfg.path was supplied, so pathidx stays
|
|
262
|
-
// valid in the common case. For cfg.path-override (4 calls per
|
|
263
|
-
// run, fixpoint advances) fall back to the join-based lookup.
|
|
264
177
|
if (cfg.path !== undefined) {
|
|
265
178
|
ctx._pathidx = undefined
|
|
266
179
|
}
|
|
@@ -269,14 +182,6 @@ class AontuContext {
|
|
|
269
182
|
}
|
|
270
183
|
|
|
271
184
|
descend(key: string): AontuContext {
|
|
272
|
-
// C3: reuse the child ctx from a previous descend with the same
|
|
273
|
-
// (parent, key). Saves one Object.create + several property
|
|
274
|
-
// writes per hit; ~48% hit rate on foo-sdk.
|
|
275
|
-
//
|
|
276
|
-
// NB: must use hasOwnProperty here — plain `this._childCache`
|
|
277
|
-
// would walk the prototype chain and read the *parent's* cache
|
|
278
|
-
// (ctxs are created via Object.create(parent)), so keys would
|
|
279
|
-
// cross-contaminate between sibling branches.
|
|
280
185
|
let childCache: Map<string, AontuContext> | undefined
|
|
281
186
|
if (Object.prototype.hasOwnProperty.call(this, '_childCache')) {
|
|
282
187
|
childCache = this._childCache
|
|
@@ -291,11 +196,6 @@ class AontuContext {
|
|
|
291
196
|
const ctx = Object.create(this)
|
|
292
197
|
ctx._pathstr = undefined
|
|
293
198
|
|
|
294
|
-
// Trie doubles as both pathidx assignment and path-array cache.
|
|
295
|
-
// (parent_pathidx, key) uniquely identifies a descended path,
|
|
296
|
-
// and is visited many times across fixpoint passes. Caching the
|
|
297
|
-
// materialised array lets descend share references instead of
|
|
298
|
-
// allocating a fresh concat every time.
|
|
299
199
|
const parentIdx = this._pathidx!
|
|
300
200
|
let childMap = this._pathTrie.get(parentIdx)
|
|
301
201
|
if (childMap === undefined) {
|
|
@@ -329,7 +229,6 @@ class AontuContext {
|
|
|
329
229
|
|
|
330
230
|
this.src = ('string' === typeof this.opts.src ? this.opts.src : undefined) ?? this.src
|
|
331
231
|
|
|
332
|
-
// TODO: rename srcpath to file
|
|
333
232
|
this.srcpath = this.opts.path ?? this.srcpath
|
|
334
233
|
}
|
|
335
234
|
|
|
@@ -351,7 +250,6 @@ class AontuContext {
|
|
|
351
250
|
|
|
352
251
|
|
|
353
252
|
errmsg() {
|
|
354
|
-
// return this.errlist
|
|
355
253
|
return this.err
|
|
356
254
|
.map((err: any) => (err && (null == err.msg || '' === err.msg)
|
|
357
255
|
? (descErr(err, this), err.msg)
|
|
@@ -407,7 +305,6 @@ class AontuContext {
|
|
|
407
305
|
} /* node:coverage ignore next 8 */
|
|
408
306
|
|
|
409
307
|
|
|
410
|
-
|
|
411
308
|
export {
|
|
412
309
|
AontuContext,
|
|
413
310
|
AontuContextConfig
|
package/src/diff.ts
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
import { includeOpts } from './utility'
|
|
3
3
|
|
|
4
|
-
// PATH-ADDRESSED DIFF (G7 phase 6,
|
|
5
|
-
// docs/capability-review/g7-machine-access.md): what changed, at which
|
|
6
|
-
// paths, between two documents — the dyff-style answer, which
|
|
7
|
-
// deterministic canon makes possible without phantom noise. Two
|
|
8
|
-
// documents that mean the same thing canon the same way, so a diff of
|
|
9
|
-
// canons reports semantic change and not reformatting.
|
|
10
|
-
//
|
|
11
|
-
// The text compared is the HASH FORM (G6's `hcanon`), not the plain
|
|
12
|
-
// canon, for the reason G6 gives: canon drops closedness and the
|
|
13
|
-
// type/hide marks, so a canon diff calls `close({a:1})` and `{a:1}`
|
|
14
|
-
// identical. A false "changed" costs a needless read; a false
|
|
15
|
-
// "unchanged" is a change nobody reviewed, which is the one direction
|
|
16
|
-
// that must not happen.
|
|
17
|
-
//
|
|
18
|
-
// WHETHER a change is BREAKING is a different question, and it belongs
|
|
19
|
-
// to G3: `subsume` and `breaking` answer it with the lattice's own
|
|
20
|
-
// rules. This verb answers "what moved", which is what a reviewer
|
|
21
|
-
// reads first and what an agent needs before it can ask the other
|
|
22
|
-
// question at all.
|
|
23
4
|
|
|
24
5
|
import { Aontu } from './aontu'
|
|
25
6
|
import type { TrustOptions } from './type'
|
|
@@ -44,7 +25,6 @@ export type DiffReport = {
|
|
|
44
25
|
changes: DiffChange[]
|
|
45
26
|
findings: VetFinding[]
|
|
46
27
|
ok: boolean
|
|
47
|
-
// True when nothing moved: the two documents mean the same thing.
|
|
48
28
|
same: boolean
|
|
49
29
|
}
|
|
50
30
|
|
|
@@ -54,13 +34,6 @@ export type DiffOptions = {
|
|
|
54
34
|
// Compare at this path of both documents, rather than at the root.
|
|
55
35
|
at?: string
|
|
56
36
|
|
|
57
|
-
// The trust profile this run evaluates under (G5, docs/trust.md).
|
|
58
|
-
// The source arrives from a caller, so the caller must be able to
|
|
59
|
-
// say what it may reach: without this the include chain is the
|
|
60
|
-
// default one, and `@"../../etc/passwd.aon"` reads whatever the
|
|
61
|
-
// process can. (`@"x.js"` no longer executes -- ADR-012 refuses the
|
|
62
|
-
// extension -- but reading is enough.) A server passes
|
|
63
|
-
// `{include:'none'}`.
|
|
64
37
|
trust?: TrustOptions
|
|
65
38
|
|
|
66
39
|
// Extensions additionally read as text (the CLI's `--text-ext`).
|
|
@@ -75,11 +48,6 @@ function pathText(parts: string[]): string {
|
|
|
75
48
|
}
|
|
76
49
|
|
|
77
50
|
|
|
78
|
-
// Both sides of one node — never both absent: keys come from the
|
|
79
|
-
// union of the two bags, and list indices run to the longer side, so
|
|
80
|
-
// every walk has at least one value. Bags of the SAME kind recurse,
|
|
81
|
-
// which is what makes the report path-addressed rather than one line
|
|
82
|
-
// saying the whole document changed; everything else compares text.
|
|
83
51
|
function walk(
|
|
84
52
|
left: any, right: any, parts: string[], out: DiffChange[]): void {
|
|
85
53
|
if (null == left) {
|
|
@@ -94,11 +62,6 @@ function walk(
|
|
|
94
62
|
const bothMaps = true === left.isMap && true === right.isMap
|
|
95
63
|
const bothLists = true === left.isList && true === right.isList
|
|
96
64
|
if (bothMaps || bothLists) {
|
|
97
|
-
// The bag's OWN attributes, at pseudo-keys under it: a recursing
|
|
98
|
-
// bag never compares its own text, so what the children do not
|
|
99
|
-
// carry has to be compared here. The spread is part of what a bag
|
|
100
|
-
// MEANS; so are closedness and the marks, which is exactly why the
|
|
101
|
-
// hash form spells them (G6).
|
|
102
65
|
const lc = null == left.spread.cj ? undefined : hcanon(left.spread.cj)
|
|
103
66
|
const rc = null == right.spread.cj ? undefined : hcanon(right.spread.cj)
|
|
104
67
|
if (lc !== rc) {
|
|
@@ -182,9 +145,6 @@ function evalSide(
|
|
|
182
145
|
}
|
|
183
146
|
|
|
184
147
|
|
|
185
|
-
// Diff two documents. Each is evaluated on its own — a document that
|
|
186
|
-
// does not stand up has no meaning to compare, and the report says so
|
|
187
|
-
// rather than diffing a wreck.
|
|
188
148
|
export function diff(
|
|
189
149
|
leftSrc: string, rightSrc: string, opts?: DiffOptions): DiffReport {
|
|
190
150
|
const options = opts ?? {}
|
package/src/err.ts
CHANGED
|
@@ -17,17 +17,6 @@ import { hints } from './hints'
|
|
|
17
17
|
const { errmsg, strinject } = util
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
// COLOUR IS A DECISION ABOUT THE DESTINATION, not about the message.
|
|
21
|
-
// Every error frame hardcoded the ANSI escapes, so a piped report and
|
|
22
|
-
// a `--jsonl` answer carried terminal control codes into whatever read
|
|
23
|
-
// them -- a log file, a CI annotation, an agent's parser (the review's
|
|
24
|
-
// finding F).
|
|
25
|
-
//
|
|
26
|
-
// NO_COLOR (no-color.org: set, to anything, means no colour) turns them
|
|
27
|
-
// off everywhere, library callers included. The CLI additionally turns
|
|
28
|
-
// them off when its stderr is not a terminal, through setColor: a
|
|
29
|
-
// library cannot see the destination, and a caller who has one is the
|
|
30
|
-
// only one who can say.
|
|
31
20
|
let COLOR: boolean | undefined
|
|
32
21
|
|
|
33
22
|
function setColor(on: boolean | undefined): void {
|
|
@@ -38,13 +27,6 @@ function colorActive(): boolean {
|
|
|
38
27
|
if (null != COLOR) {
|
|
39
28
|
return COLOR
|
|
40
29
|
}
|
|
41
|
-
// `globalThis` always exists; `process` need not (this library runs
|
|
42
|
-
// in a browser too), so the optional chain starts at the part that
|
|
43
|
-
// can actually be missing -- and stays a CHAIN rather than becoming
|
|
44
|
-
// an `if`, because the browser arm is unreachable from any test this
|
|
45
|
-
// suite can run and ADR-002 does not accept an arm nothing takes.
|
|
46
|
-
// Set-but-EMPTY is the documented exception and does not disable
|
|
47
|
-
// colour (no-color.org).
|
|
48
30
|
const no = (globalThis as any).process?.env?.NO_COLOR
|
|
49
31
|
return null == no || '' === no
|
|
50
32
|
}
|
|
@@ -68,15 +50,6 @@ function makeNilErr(
|
|
|
68
50
|
attempt?: string,
|
|
69
51
|
details?: Record<string, any>
|
|
70
52
|
): NilVal {
|
|
71
|
-
// C1-inner: when a DisjunctVal trial is in progress, failures are
|
|
72
|
-
// transient markers — none of the NilVal fields (site, path,
|
|
73
|
-
// primary, secondary, details) ever surface to the user because
|
|
74
|
-
// DisjunctVal replaces the oval entry with TRIAL_NIL and filters
|
|
75
|
-
// by isNil. Allocating a fresh NilVal per failure (~60k per
|
|
76
|
-
// foo-sdk run from IntegerVal/BooleanVal/ScalarVal.unify et al.)
|
|
77
|
-
// is pure waste. Short-circuit to the shared sentinel; push once
|
|
78
|
-
// to ctx.err so the caller's `trialErr.length > 0` check still
|
|
79
|
-
// signals failure.
|
|
80
53
|
if (ctx !== undefined && ctx._trialMode === true) {
|
|
81
54
|
if (ctx.err.length === 0) ctx.err.push(TRIAL_NIL)
|
|
82
55
|
return TRIAL_NIL
|
|
@@ -86,7 +59,6 @@ function makeNilErr(
|
|
|
86
59
|
}
|
|
87
60
|
|
|
88
61
|
|
|
89
|
-
// TODO: move to utility?
|
|
90
62
|
function descErr<NILS extends NilVal | NilVal[]>(
|
|
91
63
|
err: NILS | any,
|
|
92
64
|
errctx?: ErrContext,
|
|
@@ -99,15 +71,6 @@ function descErr<NILS extends NilVal | NilVal[]>(
|
|
|
99
71
|
let v1src = resolveSrc(v1, errctx)
|
|
100
72
|
let v2src = resolveSrc(v2, errctx)
|
|
101
73
|
|
|
102
|
-
// STRICT `!==` against the empty string. The loose `!=` here dropped
|
|
103
|
-
// the list index 0, because `'' != 0` is FALSE in JavaScript ('' and
|
|
104
|
-
// 0 are both coerced to 0): `a:[1]&[2]` reported its conflict at
|
|
105
|
-
// `$.a` while `a:[1,5]&[1,6]` reported `$.a.1`, so the one index a
|
|
106
|
-
// reader is most likely to meet was the one silently erased, and a
|
|
107
|
-
// nested `a:[[1]]&[[2]]` lost both segments (issue #37). Numeric
|
|
108
|
-
// segments arrive here as numbers, so only `===`/`!==` compares them
|
|
109
|
-
// for what they are. `null != p` stays loose on purpose -- it is the
|
|
110
|
-
// idiomatic null-and-undefined test.
|
|
111
74
|
let path = ['$', ...err.path].filter((p: any) => null != p && '' !== p)
|
|
112
75
|
|
|
113
76
|
// '$' is neither null nor '', so the filter always leaves it.
|
|
@@ -133,7 +96,6 @@ function descErr<NILS extends NilVal | NilVal[]>(
|
|
|
133
96
|
'\n',
|
|
134
97
|
|
|
135
98
|
(null != v1 && errmsg({
|
|
136
|
-
// TODO: color should come from jsonic config
|
|
137
99
|
color: { active: colorActive(), line: '\x1b[34m' },
|
|
138
100
|
txts: {
|
|
139
101
|
msg: 'Cannot ' + attempt + ' value: ' + v1.canon +
|
|
@@ -149,7 +111,6 @@ function descErr<NILS extends NilVal | NilVal[]>(
|
|
|
149
111
|
})),
|
|
150
112
|
|
|
151
113
|
(null != v2 && errmsg({
|
|
152
|
-
// TODO: color should come from jsonic config
|
|
153
114
|
color: { active: colorActive(), line: '\x1b[34m' },
|
|
154
115
|
txts: {
|
|
155
116
|
msg: 'Cannot ' + attempt + ' value: ' + v2.canon +
|
|
@@ -169,7 +130,6 @@ function descErr<NILS extends NilVal | NilVal[]>(
|
|
|
169
130
|
.filter((n: any) => null != n && false !== n)
|
|
170
131
|
.join('\n')
|
|
171
132
|
|
|
172
|
-
// TODO: update jsonic errmsg to avoid multiple empty lines
|
|
173
133
|
.replace(/\n\n/g, '\n')
|
|
174
134
|
|
|
175
135
|
}
|
package/src/escape.ts
CHANGED
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// THE ESCAPE CONVENTIONS (G9 phase 6, docs/design/TEMPLATE.0.md D4).
|
|
4
|
-
// `esc(s, variant?)` makes a string safe to place inside a literal of
|
|
5
|
-
// the named convention, and `usc` reads it back out.
|
|
6
|
-
//
|
|
7
|
-
// A VARIANT NAMES A CONVENTION, NOT A LANGUAGE, and that is the whole
|
|
8
|
-
// reason it is a variant rather than something a renderer knows:
|
|
9
|
-
// several languages share one convention, and one language has
|
|
10
|
-
// several -- a C-family literal escapes differently in each quote, and
|
|
11
|
-
// SQL spells a literal one way and an identifier another.
|
|
12
|
-
//
|
|
13
|
-
// With no variant it is the C escape, JSON canonical, which covers
|
|
14
|
-
// TypeScript, JavaScript, Java, C, C++, C#, Go, Rust, Swift, Kotlin,
|
|
15
|
-
// Scala and JSON itself. That is why it is the default rather than a
|
|
16
|
-
// lookup.
|
|
17
|
-
//
|
|
18
|
-
// EVERY CONVENTION IS SPELLED OUT HERE rather than borrowed from a
|
|
19
|
-
// host function, and the reason is parity: `JSON.stringify` escapes
|
|
20
|
-
// what Go's `encoding/json` does not (and the reverse), and
|
|
21
|
-
// `encodeURIComponent` leaves `!'()*` alone where RFC 3986 does not.
|
|
22
|
-
// A generated file must be byte-identical whichever engine wrote it,
|
|
23
|
-
// so both ports carry this table and go/escape.go is its twin.
|
|
24
|
-
//
|
|
25
|
-
// `usc` IS THE LEFT INVERSE, AND IT IS PARTIAL. `usc(esc(s))` is `s`
|
|
26
|
-
// for every `s`; `esc(usc(t))` is `t` only for canonically escaped
|
|
27
|
-
// `t`, because several spellings escape to one value. Input with no
|
|
28
|
-
// inverse -- a truncated `\u12`, an undefined `\q`, a lone `'` where
|
|
29
|
-
// the convention doubles it -- is REFUSED (`usc_malformed`), never
|
|
30
|
-
// passed through: a generator that reads a value back out of an
|
|
31
|
-
// artifact and silently gets a different one is the failure this
|
|
32
|
-
// whole pair exists to close.
|
|
33
|
-
|
|
34
3
|
|
|
35
4
|
// The variant names, in the order the reference lists them. `none` is
|
|
36
5
|
// not here: it is the absent argument, and the absent argument is the
|
|
@@ -38,15 +7,9 @@
|
|
|
38
7
|
const ESC_VARIANTS = ['sq', 'sql', 'shell', 'xml', 'uri', 'regex']
|
|
39
8
|
|
|
40
9
|
|
|
41
|
-
// The regex metacharacters, which are exactly the ones the pattern
|
|
42
|
-
// subset admits as escaped-to-mean-themselves (RE_ESCAPE_PUNCT in
|
|
43
|
-
// ConstraintVal). So `esc(s, regex)` always answers a pattern the
|
|
44
|
-
// subset accepts, which is the point of having the variant at all.
|
|
45
10
|
const ESC_REGEX_PUNCT = '\\.+*?()[]{}|^$/'
|
|
46
11
|
|
|
47
12
|
|
|
48
|
-
// The five XML entities, in the order they must be APPLIED: `&` first,
|
|
49
|
-
// or the ampersands of the other four are escaped a second time.
|
|
50
13
|
const ESC_XML: [string, string][] = [
|
|
51
14
|
['&', '&'],
|
|
52
15
|
['<', '<'],
|
|
@@ -56,10 +19,6 @@ const ESC_XML: [string, string][] = [
|
|
|
56
19
|
]
|
|
57
20
|
|
|
58
21
|
|
|
59
|
-
// The characters RFC 3986 calls unreserved. Everything else is
|
|
60
|
-
// percent-encoded, byte by byte of its UTF-8, with UPPERCASE hex --
|
|
61
|
-
// the case the RFC prefers, pinned here because the two ports must
|
|
62
|
-
// agree on it.
|
|
63
22
|
const ESC_URI_UNRESERVED =
|
|
64
23
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
|
|
65
24
|
|
|
@@ -143,7 +102,6 @@ function unescC(src: string, quote: string): [string, boolean] {
|
|
|
143
102
|
}
|
|
144
103
|
|
|
145
104
|
|
|
146
|
-
// Four hex digits at `at`, or -1 when they are not four hex digits.
|
|
147
105
|
function hex4(src: string, at: number): number {
|
|
148
106
|
if (src.length < at + 4) { return -1 }
|
|
149
107
|
let v = 0
|
|
@@ -286,10 +244,6 @@ function unescRegex(src: string): [string, boolean] {
|
|
|
286
244
|
}
|
|
287
245
|
|
|
288
246
|
|
|
289
|
-
// UTF-8 by hand, both ways, because the percent convention is defined
|
|
290
|
-
// over BYTES and the two ports must produce the same ones. TextEncoder
|
|
291
|
-
// would do for the forward direction; the reverse needs the error
|
|
292
|
-
// behaviour spelled out, so both are here.
|
|
293
247
|
function utf8Bytes(ch: string): number[] {
|
|
294
248
|
const c = ch.codePointAt(0) as number
|
|
295
249
|
if (0x80 > c) { return [c] }
|
package/src/exactjson.ts
CHANGED
|
@@ -1,62 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
/*
|
|
4
|
-
* D9 -- THE EXACT JSON EMITTER.
|
|
5
|
-
*
|
|
6
|
-
* `generate()` returns NATIVE values, and once a document opts into the
|
|
7
|
-
* exact leaves two of them are not things `JSON.stringify` can write: a
|
|
8
|
-
* biginteger generates as a native `bigint`, and a bigdecimal generates
|
|
9
|
-
* as a `Decimal`. `JSON.stringify` THROWS on a bigint ("Do not know how
|
|
10
|
-
* to serialize a BigInt") and a `replacer` cannot rescue it, because a
|
|
11
|
-
* replacer may only return another *value* -- everything it returns that
|
|
12
|
-
* is not already a JSON primitive gets quoted, so the exact digits could
|
|
13
|
-
* only come back as a JSON *string*, which is a different document.
|
|
14
|
-
*
|
|
15
|
-
* JSON itself was never the obstacle. A JSON number is arbitrary-
|
|
16
|
-
* precision decimal TEXT: `{"x":9007199254740993}` is a perfectly legal
|
|
17
|
-
* document that every conforming parser reads, and Go's `encoding/json`
|
|
18
|
-
* already emits exactly that for a `*big.Int`. Only JavaScript's
|
|
19
|
-
* serialiser stands in the way, so aontu ships its own.
|
|
20
|
-
*
|
|
21
|
-
* WHY THIS IS PUBLIC API and not a CLI internal: D9 promises that an
|
|
22
|
-
* exact value reaches JSON as exact digits. A library consumer whose
|
|
23
|
-
* document contains `0d9007199254740993` has no other supported way to
|
|
24
|
-
* keep that promise -- hiding the only implementation behind the command
|
|
25
|
-
* line would leave the contract unfulfillable from the library. The
|
|
26
|
-
* `aontu` CLI consumes this same export (with `indent`), and so does the
|
|
27
|
-
* shared suite's byte-exact `gens` mode, so there is exactly one set of
|
|
28
|
-
* bytes to keep in step with the Go port.
|
|
29
|
-
*
|
|
30
|
-
* PARITY WITH GO. The output must match Go's
|
|
31
|
-
* `json.Encoder` + `SetEscapeHTML(false)` (see specGens in
|
|
32
|
-
* go/spec_test.go) byte for byte:
|
|
33
|
-
*
|
|
34
|
-
* - Key order is the order `generate()` produced. Both ports already
|
|
35
|
-
* sort map keys (see the entries sort in BagVal.gen; Go's encoder
|
|
36
|
-
* sorts map keys), so neither has to sort again here.
|
|
37
|
-
* - Strings keep JavaScript's escaping, which is what the `gens` mode
|
|
38
|
-
* settled on when it turned Go's HTML escaping OFF so that `<`, `>`
|
|
39
|
-
* and `&` stay literal in both ports. U+2028/U+2029 are the one
|
|
40
|
-
* place JS and Go disagree by default -- Go escapes them, JS does
|
|
41
|
-
* not -- so they are escaped here too, which is both legal JSON and
|
|
42
|
-
* the safer output (it can be embedded in a JavaScript source).
|
|
43
|
-
* - Numbers keep JavaScript's `Number.prototype.toString` form, which
|
|
44
|
-
* is already what Go's encoder produces: fixed notation inside
|
|
45
|
-
* [1e-6, 1e21) and an unpadded, always-signed exponent outside it
|
|
46
|
-
* (`1e+21`, `1e-7`).
|
|
47
|
-
* - A bigint writes its digits; a Decimal writes its plain digit form
|
|
48
|
-
* (`1000.0`, `0.1`, `-1.5`) -- no `0d` marker, since that belongs to
|
|
49
|
-
* canon and is not JSON, but an integral bigdecimal keeps its `.0`
|
|
50
|
-
* so the JSON still shows a decimal. Go's `*Decimal.MarshalJSON`
|
|
51
|
-
* mirrors this.
|
|
52
|
-
*
|
|
53
|
-
* NOTE that byte-exact output CANNOT check the other half of D9: an
|
|
54
|
-
* integral bigdecimal and a biginteger can serialise to different text,
|
|
55
|
-
* but a biginteger and an ordinary integer serialise to the SAME text
|
|
56
|
-
* while `generate()` returned the wrong runtime type. Canon pins the AST
|
|
57
|
-
* kind, `gens` pins these bytes, and only the per-port API tests
|
|
58
|
-
* (ts/test/exactjson.test.ts here) pin the returned object.
|
|
59
|
-
*/
|
|
60
3
|
|
|
61
4
|
import { Decimal } from './val/Decimal'
|
|
62
5
|
import { AontuError } from './err'
|
|
@@ -68,9 +11,6 @@ import { cmpCodePoint } from './keyorder'
|
|
|
68
11
|
const LSPS_RE = new RegExp('[\u2028\u2029]', 'g')
|
|
69
12
|
|
|
70
13
|
|
|
71
|
-
// JSON.stringify's `space` argument, normalised the way the spec does:
|
|
72
|
-
// a number is clamped to 0..10 spaces, a string is truncated to 10
|
|
73
|
-
// characters, anything else means "compact".
|
|
74
14
|
function indentUnit(indent: number | string | undefined): string {
|
|
75
15
|
if ('number' === typeof indent) {
|
|
76
16
|
const n = Math.min(10, Math.floor(indent))
|
|
@@ -92,18 +32,11 @@ function quote(s: string): string {
|
|
|
92
32
|
}
|
|
93
33
|
|
|
94
34
|
|
|
95
|
-
// True for a value JSON.stringify omits from an object and writes as
|
|
96
|
-
// `null` inside an array. generate() never produces one, but a consumer
|
|
97
|
-
// splicing its own data into the result might.
|
|
98
35
|
function skipped(v: any): boolean {
|
|
99
36
|
return undefined === v || 'function' === typeof v || 'symbol' === typeof v
|
|
100
37
|
}
|
|
101
38
|
|
|
102
39
|
|
|
103
|
-
// `seen` tracks the containers on the CURRENT path only (added on entry,
|
|
104
|
-
// removed on exit), so a shared subtree -- which unification can easily
|
|
105
|
-
// produce -- serialises fine and only a true cycle is refused, exactly
|
|
106
|
-
// as JSON.stringify does it.
|
|
107
40
|
function emit(v: any, unit: string, pad: string, seen: Set<any>): string {
|
|
108
41
|
if (null === v) {
|
|
109
42
|
return 'null'
|
|
@@ -163,35 +96,6 @@ function emit(v: any, unit: string, pad: string, seen: Set<any>): string {
|
|
|
163
96
|
else {
|
|
164
97
|
const colon = '' === unit ? ':' : ': '
|
|
165
98
|
const parts: string[] = []
|
|
166
|
-
// OWN enumerable keys, SORTED. A `for ... in` here would additionally
|
|
167
|
-
// walk the prototype chain.
|
|
168
|
-
//
|
|
169
|
-
// WHY THE SORT IS HERE AND NOT IN gen(). BagVal.gen already sorts map
|
|
170
|
-
// entries lexicographically -- and then writes them into a plain JS
|
|
171
|
-
// object, where ECMAScript throws that sort away. OrdinaryOwnPropertyKeys
|
|
172
|
-
// lists canonical ARRAY-INDEX keys first, in ascending NUMERIC order,
|
|
173
|
-
// and only then the remaining string keys in insertion order. So
|
|
174
|
-
// `{9:1,10:2}` came back out as 9 before 10, and `{"!":1,9:2}` put the
|
|
175
|
-
// digit key ahead of the punctuation one.
|
|
176
|
-
//
|
|
177
|
-
// This is not a V8 quirk and not a JSON.stringify quirk; it is the
|
|
178
|
-
// object's own key order, and it CANNOT be fixed upstream, because no
|
|
179
|
-
// JavaScript object can hold "10" before "9". The emitter is the last
|
|
180
|
-
// point that still has the freedom to choose, so it chooses here.
|
|
181
|
-
//
|
|
182
|
-
// The giveaway that it was never a design rule: only indices up to
|
|
183
|
-
// 2^32-2 are hoisted, so `{4294967295:1,4294967296:2,5:3}` emitted as
|
|
184
|
-
// 5, 4294967295, 4294967296 -- and TypeScript's own CANON, which builds
|
|
185
|
-
// text from a sorted list rather than from an object, was lexicographic
|
|
186
|
-
// all along. The port contradicted itself.
|
|
187
|
-
//
|
|
188
|
-
// Lexicographic by UTF-16 code unit, which is what `<` on JS strings
|
|
189
|
-
// does and what MapVal.canon/BagVal.gen already sort by. Go sorts map
|
|
190
|
-
// keys by UTF-8 byte, and the two orders agree on every key that stays
|
|
191
|
-
// inside the BMP; astral-plane keys are a separate, tracked divergence.
|
|
192
|
-
// CODE POINT order (cmpCodePoint), which is what Go's UTF-8 byte sort
|
|
193
|
-
// produces. A bare .sort() is UTF-16 code-unit order, which puts an
|
|
194
|
-
// astral key ahead of everything in U+E000-U+FFFF.
|
|
195
99
|
for (const k of Object.keys(v).sort(cmpCodePoint)) {
|
|
196
100
|
const cv = v[k]
|
|
197
101
|
if (skipped(cv)) {
|
|
@@ -209,41 +113,6 @@ function emit(v: any, unit: string, pad: string, seen: Set<any>): string {
|
|
|
209
113
|
}
|
|
210
114
|
|
|
211
115
|
|
|
212
|
-
/**
|
|
213
|
-
* Serialise a value produced by `Aontu.generate()` as JSON text,
|
|
214
|
-
* preserving EXACT numbers.
|
|
215
|
-
*
|
|
216
|
-
* Use this instead of `JSON.stringify` on generated output. A document
|
|
217
|
-
* that uses the `0d` exact leaves generates native `bigint` and
|
|
218
|
-
* `Decimal` values; `JSON.stringify` throws on the first and mangles the
|
|
219
|
-
* second, while this emitter writes both as raw JSON numbers with their
|
|
220
|
-
* exact digits -- which is what the D9 generate contract promises, and
|
|
221
|
-
* what the Go port's marshallers produce for the same document.
|
|
222
|
-
*
|
|
223
|
-
* ```ts
|
|
224
|
-
* import { Aontu, exactJSON } from 'aontu'
|
|
225
|
-
*
|
|
226
|
-
* const out = new Aontu().generate('x:0d9007199254740993')
|
|
227
|
-
* typeof out.x // 'bigint'
|
|
228
|
-
* exactJSON(out) // '{"x":9007199254740993}'
|
|
229
|
-
* exactJSON(out, 2) // '{\n "x": 9007199254740993\n}'
|
|
230
|
-
* JSON.stringify(out) // TypeError: Do not know how to serialize a BigInt
|
|
231
|
-
* ```
|
|
232
|
-
*
|
|
233
|
-
* @param value The value to serialise -- normally `generate()` output:
|
|
234
|
-
* `null`, booleans, numbers, strings, `bigint`,
|
|
235
|
-
* `Decimal`, arrays and plain objects. An object with a
|
|
236
|
-
* `toJSON` method is asked for its replacement first
|
|
237
|
-
* (`Decimal` is handled as a number before that check).
|
|
238
|
-
* @param indent Optional indentation, with `JSON.stringify`'s `space`
|
|
239
|
-
* semantics: a number of spaces (clamped to 0..10) or a
|
|
240
|
-
* literal string. Omitted or `0` gives COMPACT output
|
|
241
|
-
* (no spaces, no newlines) -- the form the shared spec
|
|
242
|
-
* suite's `gens` mode compares byte for byte.
|
|
243
|
-
* @returns The JSON text. Unlike `JSON.stringify` this always
|
|
244
|
-
* returns a string: a top-level `undefined` is `null`.
|
|
245
|
-
* @throws {AontuError} if the value contains a reference cycle.
|
|
246
|
-
*/
|
|
247
116
|
function exactJSON(value: any, indent?: number | string): string {
|
|
248
117
|
return emit(value, indentUnit(indent), '', new Set())
|
|
249
118
|
} /* node:coverage ignore next 6 */
|