@voxgig/sdkgen 3.4.6 → 3.5.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/bin/voxgig-sdkgen +78 -10
- package/dist/action/action.d.ts +5 -2
- package/dist/action/action.js +113 -6
- package/dist/action/action.js.map +1 -1
- package/dist/action/check.d.ts +15 -0
- package/dist/action/check.js +347 -0
- package/dist/action/check.js.map +1 -0
- package/dist/action/dispatch.d.ts +8 -0
- package/dist/action/dispatch.js +74 -0
- package/dist/action/dispatch.js.map +1 -0
- package/dist/action/docs.d.ts +4 -0
- package/dist/action/docs.js +236 -0
- package/dist/action/docs.js.map +1 -0
- package/dist/action/doctor.d.ts +5 -2
- package/dist/action/doctor.js +526 -74
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/feature.js +96 -17
- package/dist/action/feature.js.map +1 -1
- package/dist/action/kind.d.ts +30 -0
- package/dist/action/kind.js +258 -0
- package/dist/action/kind.js.map +1 -0
- package/dist/action/package.d.ts +25 -0
- package/dist/action/package.js +852 -0
- package/dist/action/package.js.map +1 -0
- package/dist/action/resolve.d.ts +18 -0
- package/dist/action/resolve.js +280 -0
- package/dist/action/resolve.js.map +1 -0
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +240 -97
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Docs.d.ts +4 -0
- package/dist/cmp/Docs.js +98 -0
- package/dist/cmp/Docs.js.map +1 -0
- package/dist/cmp/ExternalDocs.d.ts +2 -0
- package/dist/cmp/ExternalDocs.js +43 -0
- package/dist/cmp/ExternalDocs.js.map +1 -0
- package/dist/cmp/ReadmeTop.js +5 -1
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/definition.d.ts +5 -0
- package/dist/helpers/definition.js +72 -0
- package/dist/helpers/definition.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +8 -2
- package/dist/helpers/featureSource.js +61 -12
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/junk.d.ts +11 -0
- package/dist/helpers/junk.js +149 -0
- package/dist/helpers/junk.js.map +1 -0
- package/dist/helpers/manifest.d.ts +50 -0
- package/dist/helpers/manifest.js +328 -0
- package/dist/helpers/manifest.js.map +1 -0
- package/dist/helpers/modelcheck.d.ts +22 -0
- package/dist/helpers/modelcheck.js +203 -0
- package/dist/helpers/modelcheck.js.map +1 -0
- package/dist/helpers/semver.d.ts +4 -0
- package/dist/helpers/semver.js +174 -0
- package/dist/helpers/semver.js.map +1 -0
- package/dist/helpers/shipped.d.ts +4 -0
- package/dist/helpers/shipped.js +36 -0
- package/dist/helpers/shipped.js.map +1 -0
- package/dist/helpers/stdrep.d.ts +9 -1
- package/dist/helpers/stdrep.js +22 -0
- package/dist/helpers/stdrep.js.map +1 -1
- package/dist/sdkgen.d.ts +15 -3
- package/dist/sdkgen.js +233 -67
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/utility.js +17 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +69 -0
- package/package.json +3 -2
- package/project/.sdk/model/feature/audit.aontu +1 -0
- package/project/.sdk/model/feature/cache.aontu +1 -0
- package/project/.sdk/model/feature/clienttrack.aontu +1 -0
- package/project/.sdk/model/feature/debug.aontu +1 -0
- package/project/.sdk/model/feature/idempotency.aontu +1 -0
- package/project/.sdk/model/feature/log.aontu +1 -0
- package/project/.sdk/model/feature/metrics.aontu +1 -0
- package/project/.sdk/model/feature/netsim.aontu +1 -0
- package/project/.sdk/model/feature/paging.aontu +1 -0
- package/project/.sdk/model/feature/proxy.aontu +1 -0
- package/project/.sdk/model/feature/ratelimit.aontu +1 -0
- package/project/.sdk/model/feature/rbac.aontu +1 -0
- package/project/.sdk/model/feature/retry.aontu +1 -0
- package/project/.sdk/model/feature/streaming.aontu +1 -0
- package/project/.sdk/model/feature/telemetry.aontu +1 -0
- package/project/.sdk/model/feature/test.aontu +1 -0
- package/project/.sdk/model/feature/timeout.aontu +1 -0
- package/project/.sdk/model/target/c.aontu +1 -0
- package/project/.sdk/model/target/clojure.aontu +1 -0
- package/project/.sdk/model/target/cpp.aontu +1 -0
- package/project/.sdk/model/target/dart.aontu +1 -0
- package/project/.sdk/model/target/elixir.aontu +1 -0
- package/project/.sdk/model/target/go-cli.aontu +1 -0
- package/project/.sdk/model/target/go-mcp.aontu +1 -0
- package/project/.sdk/model/target/go.aontu +1 -0
- package/project/.sdk/model/target/haskell.aontu +1 -0
- package/project/.sdk/model/target/java.aontu +1 -0
- package/project/.sdk/model/target/js.aontu +1 -0
- package/project/.sdk/model/target/kotlin.aontu +1 -0
- package/project/.sdk/model/target/lean.aontu +1 -0
- package/project/.sdk/model/target/lua.aontu +1 -0
- package/project/.sdk/model/target/ocaml.aontu +1 -0
- package/project/.sdk/model/target/perl.aontu +1 -0
- package/project/.sdk/model/target/php.aontu +1 -0
- package/project/.sdk/model/target/py-data.aontu +1 -0
- package/project/.sdk/model/target/py.aontu +1 -0
- package/project/.sdk/model/target/rb.aontu +1 -0
- package/project/.sdk/model/target/rust.aontu +1 -0
- package/project/.sdk/model/target/scala.aontu +1 -0
- package/project/.sdk/model/target/seneca-provider.aontu +1 -0
- package/project/.sdk/model/target/zig.aontu +1 -0
- package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +56 -3
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
- package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
- package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
- package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
- package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +31 -18
- package/project/.sdk/tm/c/core/sdk.h +1 -0
- package/project/.sdk/tm/c/utility/make_options.c +5 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
- package/project/.sdk/tm/cpp/core/types.hpp +4 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
- package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
- package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
- package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
- package/project/.sdk/tm/java/core/SdkClient.java +3 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
- package/project/.sdk/tm/ocaml/Makefile +3 -2
- package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
- package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
- package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
- package/project/.sdk/tm/scala/utility/Make.scala +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
- package/project/.sdk/tm/zig/core/utility.zig +85 -1
- package/project/.sdk/tm/zig/test/gotcha_test.zig +8 -8
- package/project/sdkgen-package.json +57 -0
- package/src/action/action.ts +136 -6
- package/src/action/check.ts +495 -0
- package/src/action/dispatch.ts +105 -0
- package/src/action/docs.ts +310 -0
- package/src/action/doctor.ts +692 -81
- package/src/action/feature.ts +109 -17
- package/src/action/kind.ts +375 -0
- package/src/action/package.ts +1173 -0
- package/src/action/resolve.ts +362 -0
- package/src/action/target.ts +290 -108
- package/src/cmp/Docs.ts +125 -0
- package/src/cmp/ExternalDocs.ts +54 -0
- package/src/cmp/ReadmeTop.ts +6 -1
- package/src/helpers/definition.ts +82 -0
- package/src/helpers/featureSource.ts +83 -13
- package/src/helpers/junk.ts +168 -0
- package/src/helpers/manifest.ts +474 -0
- package/src/helpers/modelcheck.ts +262 -0
- package/src/helpers/semver.ts +211 -0
- package/src/helpers/shipped.ts +41 -0
- package/src/helpers/stdrep.ts +70 -0
- package/src/sdkgen.ts +294 -66
- package/src/types.ts +33 -3
- package/src/utility.ts +17 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// THE MODEL RULES A PACKAGE'S `.aontu` FILES MUST SATISFY.
|
|
2
|
+
//
|
|
3
|
+
// See docs/design/sdkgen-packages.md §10 (the rules) and §14 (the battery).
|
|
4
|
+
//
|
|
5
|
+
// WHY THIS IS A MODULE AND NOT A TEST
|
|
6
|
+
//
|
|
7
|
+
// Every rule below was already enforced — on the BUNDLED scaffold only, and
|
|
8
|
+
// from inside `ts/test/model-compile.test.ts`, where nothing else can call
|
|
9
|
+
// it. `package check` has to apply the same rules to a package this
|
|
10
|
+
// generator has never seen, and a rule written twice diverges: this
|
|
11
|
+
// workstream has produced that exact defect five times (path refs, replace
|
|
12
|
+
// maps, provenance reconstruction, the tilde parse, the installed-registry
|
|
13
|
+
// call). So the rules move here and BOTH callers read them — the guard suite
|
|
14
|
+
// over `ts/project`, which is itself an sdkgen package, and the verb.
|
|
15
|
+
//
|
|
16
|
+
// WHAT MAKES A MODEL FILE WRONG
|
|
17
|
+
//
|
|
18
|
+
// Three failure modes, and they surface at three different times, which is
|
|
19
|
+
// why all three are checked:
|
|
20
|
+
//
|
|
21
|
+
// 1. It does not PARSE under the parser a consumer actually uses. Aontu
|
|
22
|
+
// takes `#` comments; the npm engine's jsonic enables `//` and `/* */`
|
|
23
|
+
// by default and `@voxgig/model` switches them back off, so a `//` line
|
|
24
|
+
// compiles here and fails in the consumer's build. Seven shipped
|
|
25
|
+
// targets went out broken that way.
|
|
26
|
+
// 2. It parses but does not UNIFY with the base schema — a missing
|
|
27
|
+
// non-defaulted key (`ext`, `comment.line`, `module.name`, a feature's
|
|
28
|
+
// `title`). The file alone is fine, so nothing at add time notices; the
|
|
29
|
+
// consumer's whole model compile is what fails.
|
|
30
|
+
// 3. It unifies but PINS a key the project owns (`publish.version`,
|
|
31
|
+
// `publish.registry.package`). Concrete-vs-concrete is a conflict in
|
|
32
|
+
// aontu, so the project cannot override it — and the failure names the
|
|
33
|
+
// project's own file, not the package's.
|
|
34
|
+
|
|
35
|
+
import { Aontu } from 'aontu'
|
|
36
|
+
|
|
37
|
+
import { schemaFile } from './shipped'
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// The provenance anchor. The literal line a shipped definition carries so the
|
|
41
|
+
// stamp has somewhere to hang (helpers/stdrep), and therefore the one thing
|
|
42
|
+
// a package's definition must not lose.
|
|
43
|
+
const ANCHOR = "base: 'BASE'"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// Matched as a WHOLE LINE, never as a substring — the same rule doctor
|
|
47
|
+
// learned the hard way (see AGENTS.md, "Provenance is matched by EXACT LINE").
|
|
48
|
+
//
|
|
49
|
+
// `database: 'BASE'` contains the anchor text, and so does a comment
|
|
50
|
+
// mentioning it. Either would satisfy a substring test while leaving the
|
|
51
|
+
// definition with no anchor of its own: the stamp then rewrites that
|
|
52
|
+
// unrelated occurrence, the item records no usable `base`, and `doctor` and
|
|
53
|
+
// `package update` can never locate its source — with `package check` having
|
|
54
|
+
// reported the file as fine.
|
|
55
|
+
const ANCHOR_RE = /^[ \t]*base: 'BASE'[ \t]*$/m
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// An aontu map key that is safe unquoted. Everything else — a hyphen
|
|
59
|
+
// (`go-cli`), a dot (`go.v2`), a leading digit (`2go`) — has to be quoted or
|
|
60
|
+
// the file does not parse, and the ITEM name grammar admits all three.
|
|
61
|
+
const BARE_KEY_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// The key as it must be WRITTEN in a model file.
|
|
65
|
+
function aontuKey(name: string): string {
|
|
66
|
+
return BARE_KEY_RE.test(name) ? name : "'" + name + "'"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// Blank out quoted spans before looking for comment markers, so a `//` inside
|
|
71
|
+
// a string — a url, or the `comment: line: '//'` every C-family target model
|
|
72
|
+
// legitimately declares — is not mistaken for a comment.
|
|
73
|
+
function unquoted(line: string): string {
|
|
74
|
+
return line.replace(/'[^']*'|"[^"]*"|`[^`]*`/g, '')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// What the PARSER sees on this line: quoted spans blanked, then everything
|
|
79
|
+
// from the first `#` dropped.
|
|
80
|
+
//
|
|
81
|
+
// Both steps, in that order. Without the second, a perfectly valid comment
|
|
82
|
+
// that happens to mention the token — `# some languages use // comments` —
|
|
83
|
+
// is reported as a parse error and the CLI exits non-zero on a correct
|
|
84
|
+
// package. Without the first, a `#` inside a string would truncate the line
|
|
85
|
+
// early and hide a real `//` after it.
|
|
86
|
+
function code(line: string): string {
|
|
87
|
+
return unquoted(line).split('#')[0]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// Every line carrying a slash comment, 1-based, as a consumer's parser would
|
|
92
|
+
// see it.
|
|
93
|
+
function slashComments(text: string): { line: number, text: string }[] {
|
|
94
|
+
const found: { line: number, text: string }[] = []
|
|
95
|
+
|
|
96
|
+
String(text).split('\n').forEach((line: string, i: number) => {
|
|
97
|
+
if (/(^|\s)(\/\/|\/\*)/.test(code(line))) {
|
|
98
|
+
found.push({ line: i + 1, text: line.trim() })
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return found
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
// An Aontu configured the way `@voxgig/model` configures it — which is what
|
|
107
|
+
// actually compiles a consumer's models. A plain `new Aontu()` accepts `//`
|
|
108
|
+
// and `/* */`; the Go engine has no such extension, so the npm build switches
|
|
109
|
+
// them off to match, and this must too or the check is checking a parser
|
|
110
|
+
// nobody runs.
|
|
111
|
+
//
|
|
112
|
+
// A FRESH INSTANCE each time: the option call mutates the instance's jsonic,
|
|
113
|
+
// so a cached one would leak its configuration into the bare compile that is
|
|
114
|
+
// deliberately paired with it.
|
|
115
|
+
function strictAontu(): any {
|
|
116
|
+
const aontu: any = new Aontu()
|
|
117
|
+
aontu.lang.jsonic.options({ comment: { def: { slash: null, multi: null } } })
|
|
118
|
+
return aontu
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
// An `@` include line for a path, quoted so a path containing a quote or a
|
|
123
|
+
// backslash — a Windows path, or the pathological ones the provenance tests
|
|
124
|
+
// exercise — cannot end the string early.
|
|
125
|
+
function includeLine(file: string): string {
|
|
126
|
+
return "@'" + String(file).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
type CompileResult = {
|
|
131
|
+
model?: any
|
|
132
|
+
errors: string[]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// aontu reports a resolution problem through the `errs` collector and THROWS
|
|
137
|
+
// a parse problem, so both routes have to be handled or the interesting
|
|
138
|
+
// failure escapes as an opaque AontuError. Its message carries a source
|
|
139
|
+
// excerpt with ANSI colouring and line numbers that refer to the text
|
|
140
|
+
// COMPILED, which is not the file on disk once a schema include is prepended
|
|
141
|
+
// — so the excerpt is cut and only the diagnosis is kept.
|
|
142
|
+
function tidy(msg: string): string {
|
|
143
|
+
const lines = String(msg)
|
|
144
|
+
.replace(new RegExp(String.fromCharCode(27) + "\\[[0-9;]*m", 'g'), '')
|
|
145
|
+
.split('\n')
|
|
146
|
+
|
|
147
|
+
const cut = lines.findIndex((l: string) => /^\s*-->/.test(l))
|
|
148
|
+
|
|
149
|
+
return (cut < 0 ? lines : lines.slice(0, cut))
|
|
150
|
+
.map((l: string) => l.trim())
|
|
151
|
+
.filter((l: string) => '' !== l)
|
|
152
|
+
.join(' ')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
// Compile one model file's TEXT.
|
|
157
|
+
//
|
|
158
|
+
// strict: under the parser @voxgig/model configures (the consumer's
|
|
159
|
+
// reality) rather than a bare Aontu().
|
|
160
|
+
// schema: unified with the base schema, which is what makes a missing
|
|
161
|
+
// non-defaulted key a failure instead of an absent key nobody
|
|
162
|
+
// notices until the consumer compiles.
|
|
163
|
+
//
|
|
164
|
+
// `path` must be the file's real path even when the text is synthesised:
|
|
165
|
+
// aontu resolves includes relative to it, and stats it.
|
|
166
|
+
function compileModel(
|
|
167
|
+
src: string,
|
|
168
|
+
path: string,
|
|
169
|
+
opts?: { strict?: boolean, schema?: boolean },
|
|
170
|
+
): CompileResult {
|
|
171
|
+
// `errs`, NOT `err`. They are different options with opposite behaviour:
|
|
172
|
+
//
|
|
173
|
+
// errs — aontu THROWS on the first problem it cannot resolve.
|
|
174
|
+
// err — aontu COLLECTS problems and returns no model, which sounds
|
|
175
|
+
// better and is not: a PARSE problem is then swallowed entirely.
|
|
176
|
+
// `// nope` under the strict parser yields a model and an EMPTY
|
|
177
|
+
// collector, and a slash comment is the single most common thing
|
|
178
|
+
// this check exists to catch.
|
|
179
|
+
//
|
|
180
|
+
// So the throw is the signal, and it is caught below. The collector is
|
|
181
|
+
// still passed and still read: it costs nothing, and an aontu that one day
|
|
182
|
+
// collects instead of throwing must not report "no errors" here.
|
|
183
|
+
const errs: any[] = []
|
|
184
|
+
|
|
185
|
+
const text = true === opts?.schema ?
|
|
186
|
+
includeLine(schemaFile()) + '\n' + src : src
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const aontu = false === opts?.strict ? new Aontu() : strictAontu()
|
|
190
|
+
const model = aontu.generate(text, { path, errs })
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
model,
|
|
194
|
+
errors: errs.map((e: any) =>
|
|
195
|
+
tidy((null == e.why ? '' : '[' + e.why + '] ') + (e.msg ?? String(e)))),
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (err: any) {
|
|
199
|
+
return { errors: [tidy(err.message ?? String(err))] }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
// Every key the schema DEFAULTS and a project therefore expects to set, with
|
|
205
|
+
// TWO values to try setting it to. A target model that declares any of them
|
|
206
|
+
// makes the project's own declaration a concrete-vs-concrete conflict — which
|
|
207
|
+
// fails the consumer's entire model compile, naming the consumer's file.
|
|
208
|
+
//
|
|
209
|
+
// Two values because aontu unifies two EQUAL concrete scalars happily: a
|
|
210
|
+
// target pinning `publish: tag: active: false` — which is one of only two
|
|
211
|
+
// values that key can take — passed a single-sentinel probe while still
|
|
212
|
+
// making `true` impossible for the consumer. One alternative value per key is
|
|
213
|
+
// enough: a pin can equal one sentinel or the other, never both.
|
|
214
|
+
const PUBLISH_OVERRIDES: [string, string, string][] = [
|
|
215
|
+
['publish: version', "'9.9.9'", "'8.8.8'"],
|
|
216
|
+
['publish: tag: active', 'false', 'true'],
|
|
217
|
+
['publish: registry: state', "'active'", "'inactive'"],
|
|
218
|
+
['publish: registry: active', 'true', 'false'],
|
|
219
|
+
['publish: registry: package', "'@acme/pinned'", "'@acme/other'"],
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
// The probe: unify the target model with a project that sets each of them,
|
|
224
|
+
// once per sentinel set. EITHER conflicting means the key is pinned.
|
|
225
|
+
function publishOverrideProbe(
|
|
226
|
+
src: string, path: string, tname: string,
|
|
227
|
+
): CompileResult {
|
|
228
|
+
const key = aontuKey(tname)
|
|
229
|
+
|
|
230
|
+
const run = (pick: (o: [string, string, string]) => string): CompileResult =>
|
|
231
|
+
compileModel(
|
|
232
|
+
[src, ...PUBLISH_OVERRIDES.map(
|
|
233
|
+
(o) => 'main: kit: target: ' + key + ': ' + o[0] + ': ' + pick(o))]
|
|
234
|
+
.join('\n'),
|
|
235
|
+
path)
|
|
236
|
+
|
|
237
|
+
const first = run((o) => o[1])
|
|
238
|
+
const second = run((o) => o[2])
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
model: first.model ?? second.model,
|
|
242
|
+
errors: [...first.errors, ...second.errors],
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
export type {
|
|
248
|
+
CompileResult,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export {
|
|
252
|
+
ANCHOR,
|
|
253
|
+
ANCHOR_RE,
|
|
254
|
+
PUBLISH_OVERRIDES,
|
|
255
|
+
aontuKey,
|
|
256
|
+
compileModel,
|
|
257
|
+
includeLine,
|
|
258
|
+
publishOverrideProbe,
|
|
259
|
+
slashComments,
|
|
260
|
+
strictAontu,
|
|
261
|
+
unquoted,
|
|
262
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// A DELIBERATELY SMALL semver range check, for `engines.sdkgen`.
|
|
2
|
+
//
|
|
3
|
+
// WHY NOT node-semver
|
|
4
|
+
//
|
|
5
|
+
// This package has no runtime dependencies (`dependencies: {}`), and adding
|
|
6
|
+
// one to read a single optional manifest field is a poor trade. What the field
|
|
7
|
+
// actually carries is a floor — `">=3.5"` — so the subset below covers the
|
|
8
|
+
// spellings that occur, and REFUSES TO GUESS at anything else.
|
|
9
|
+
//
|
|
10
|
+
// THE FAILURE DIRECTION MATTERS MORE THAN THE COVERAGE
|
|
11
|
+
//
|
|
12
|
+
// Getting this wrong in the strict direction refuses a package that would
|
|
13
|
+
// have worked, which is worse than the incompatibility it is trying to
|
|
14
|
+
// prevent. So `satisfies` has three outcomes, not two: yes, no, and
|
|
15
|
+
// `undefined` for "this range is outside the subset I understand". A caller
|
|
16
|
+
// that gets `undefined` must let the install proceed and say so, never treat
|
|
17
|
+
// it as a failure.
|
|
18
|
+
//
|
|
19
|
+
// SUPPORTED
|
|
20
|
+
// * x X '' any version
|
|
21
|
+
// >=1.2.3 >1.2 <=2 <2 =1.2.3 1.2.3
|
|
22
|
+
// ^1.2.3 ~1.2.3
|
|
23
|
+
// space-separated conjunction: ">=3.5 <4"
|
|
24
|
+
// ||-separated alternatives: "^3 || ^4"
|
|
25
|
+
//
|
|
26
|
+
// NOT SUPPORTED (returns undefined)
|
|
27
|
+
// hyphen ranges (`1.2.3 - 2.3.4`), prerelease/build metadata comparison,
|
|
28
|
+
// and anything else. Prereleases are compared by their numeric core only,
|
|
29
|
+
// which is why a range mentioning one is rejected rather than mishandled.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
type Parts = [number, number, number]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const COMPARATOR_RE = /^(>=|<=|>|<|=|\^|~)?v?(\d+)(?:\.(\d+))?(?:\.(\d+))?$/
|
|
36
|
+
|
|
37
|
+
const ANY_RE = /^(\*|x|X)$/
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// A version as three numbers, or undefined if it is not a plain `X.Y.Z`.
|
|
41
|
+
// A prerelease or build suffix returns undefined deliberately: comparing them
|
|
42
|
+
// properly is most of what makes semver hard, and this subset does not.
|
|
43
|
+
function parseVersion(v: string): Parts | undefined {
|
|
44
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(String(v).trim())
|
|
45
|
+
|
|
46
|
+
return null == m ? undefined :
|
|
47
|
+
[Number(m[1]), Number(m[2]), Number(m[3])]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
function compare(a: Parts, b: Parts): number {
|
|
52
|
+
for (let i = 0; i < 3; i++) {
|
|
53
|
+
if (a[i] !== b[i]) {
|
|
54
|
+
return a[i] < b[i] ? -1 : 1
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// The upper bound `^` and `~` imply, exclusive.
|
|
62
|
+
//
|
|
63
|
+
// ^1.2.3 -> <2.0.0 ^0.2.3 -> <0.3.0 ^0.0.3 -> <0.0.4
|
|
64
|
+
// ~1.2.3 -> <1.3.0 ~1.2 -> <1.3.0 ~1 -> <2.0.0
|
|
65
|
+
//
|
|
66
|
+
// `^0.x` is the case worth stating: npm treats a leading zero as unstable, so
|
|
67
|
+
// the caret narrows to the first NON-ZERO component. Getting that wrong would
|
|
68
|
+
// admit `0.3.0` for `^0.2.3`, which is a breaking change by that convention.
|
|
69
|
+
function caretBound(p: Parts): Parts {
|
|
70
|
+
if (0 !== p[0]) return [p[0] + 1, 0, 0]
|
|
71
|
+
if (0 !== p[1]) return [0, p[1] + 1, 0]
|
|
72
|
+
return [0, 0, p[2] + 1]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
function tildeBound(p: Parts, given: number): Parts {
|
|
77
|
+
return nextBound(p, given)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// The version just past everything a PARTIAL version covers: `3.4` covers all
|
|
82
|
+
// of 3.4.x, so the next bound is 3.5.0; `3` covers all of 3.x, so 4.0.0.
|
|
83
|
+
// Shared by `~` and by the two strict comparators that need it, so they
|
|
84
|
+
// cannot disagree about where a partial version ends.
|
|
85
|
+
function nextBound(p: Parts, given: number): Parts {
|
|
86
|
+
return 1 === given ? [p[0] + 1, 0, 0] : [p[0], p[1] + 1, 0]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
// One comparator against one version. `undefined` if the comparator is
|
|
91
|
+
// outside the subset.
|
|
92
|
+
function satisfiesOne(version: Parts, comparator: string): boolean | undefined {
|
|
93
|
+
const c = comparator.trim()
|
|
94
|
+
|
|
95
|
+
if ('' === c || ANY_RE.test(c)) {
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const m = COMPARATOR_RE.exec(c)
|
|
100
|
+
|
|
101
|
+
if (null == m) {
|
|
102
|
+
return undefined
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const op = m[1] ?? '='
|
|
106
|
+
|
|
107
|
+
// How many components were actually written — `~1.2` and `~1` bound
|
|
108
|
+
// differently, and a partial version means "any" in its missing places.
|
|
109
|
+
const given = null != m[4] ? 3 : null != m[3] ? 2 : 1
|
|
110
|
+
const p: Parts = [Number(m[2]), Number(m[3] ?? 0), Number(m[4] ?? 0)]
|
|
111
|
+
|
|
112
|
+
const cmp = compare(version, p)
|
|
113
|
+
|
|
114
|
+
// A PARTIAL version is a RANGE, not a version with zeroes in it, and two of
|
|
115
|
+
// the four strict comparators have to move their bound to say so:
|
|
116
|
+
//
|
|
117
|
+
// >3.4 means ">= all of 3.4.x" -> >=3.5.0 (not >3.4.0)
|
|
118
|
+
// <=3.4 means "<= all of 3.4.x" -> <3.5.0 (not <=3.4.0)
|
|
119
|
+
// >=3.4 -> >=3.4.0 <3.4 -> <3.4.0 (already right)
|
|
120
|
+
//
|
|
121
|
+
// Read as literal zeroes, `>3.4` admitted 3.4.8 (an incompatible package
|
|
122
|
+
// let through) and `<=3.4` rejected it (a compatible one refused) — the two
|
|
123
|
+
// failures this module's whole shape is meant to avoid, in one comparator.
|
|
124
|
+
const bound = 3 === given ? p : nextBound(p, given)
|
|
125
|
+
|
|
126
|
+
if ('>=' === op) return 0 <= cmp
|
|
127
|
+
if ('<' === op) return 0 > cmp
|
|
128
|
+
if ('>' === op) return 3 === given ? 0 < cmp : 0 <= compare(version, bound)
|
|
129
|
+
if ('<=' === op) return 3 === given ? 0 >= cmp : 0 > compare(version, bound)
|
|
130
|
+
|
|
131
|
+
if ('^' === op) {
|
|
132
|
+
return 0 <= cmp && 0 > compare(version, caretBound(p))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if ('~' === op) {
|
|
136
|
+
return 0 <= cmp && 0 > compare(version, tildeBound(p, given))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// `=` / bare. A partial version is a PREFIX match: `1.2` admits any 1.2.x,
|
|
140
|
+
// which is what every other tool means by it.
|
|
141
|
+
if (3 === given) return 0 === cmp
|
|
142
|
+
if (2 === given) return version[0] === p[0] && version[1] === p[1]
|
|
143
|
+
return version[0] === p[0]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
// Does `version` satisfy `range`?
|
|
148
|
+
//
|
|
149
|
+
// `true` / `false` / `undefined` — see the note at the top: undefined means
|
|
150
|
+
// "not understood", and the caller must not treat it as a failure.
|
|
151
|
+
function satisfies(version: string, range: string): boolean | undefined {
|
|
152
|
+
const v = parseVersion(version)
|
|
153
|
+
|
|
154
|
+
if (null == v) {
|
|
155
|
+
return undefined
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const text = String(range ?? '').trim()
|
|
159
|
+
|
|
160
|
+
if ('' === text || ANY_RE.test(text)) {
|
|
161
|
+
return true
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// A hyphen range is not supported, and its parts would otherwise be read as
|
|
165
|
+
// a conjunction of two bare versions — which is never satisfiable, i.e. a
|
|
166
|
+
// false REFUSAL. Rejected explicitly rather than by accident.
|
|
167
|
+
if (/\s-\s/.test(text)) {
|
|
168
|
+
return undefined
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// An alternative that MATCHES wins immediately, whatever the others say.
|
|
172
|
+
// But a `false` is only reportable if every comparator was understood:
|
|
173
|
+
// otherwise the honest answer is "do not know", and refusing on it would be
|
|
174
|
+
// the strict-direction failure this module is shaped to avoid.
|
|
175
|
+
let unknown = false
|
|
176
|
+
|
|
177
|
+
for (const alt of text.split('||')) {
|
|
178
|
+
const comparators = alt.trim().split(/\s+/).filter((s) => '' !== s)
|
|
179
|
+
|
|
180
|
+
if (0 === comparators.length) {
|
|
181
|
+
unknown = true
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let all = true
|
|
186
|
+
|
|
187
|
+
for (const c of comparators) {
|
|
188
|
+
const one = satisfiesOne(v, c)
|
|
189
|
+
|
|
190
|
+
if (null == one) {
|
|
191
|
+
unknown = true
|
|
192
|
+
all = false
|
|
193
|
+
}
|
|
194
|
+
else if (!one) {
|
|
195
|
+
all = false
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (all) {
|
|
200
|
+
return true
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return unknown ? undefined : false
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
export {
|
|
209
|
+
satisfies,
|
|
210
|
+
parseVersion,
|
|
211
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// WHERE THIS GENERATOR'S OWN FILES ARE, resolved from the compiled code.
|
|
2
|
+
//
|
|
3
|
+
// Every other path in `action/` is relative to the PROJECT being operated on
|
|
4
|
+
// — `node_modules/@voxgig/sdkgen/project/.sdk` is how a consumer reaches the
|
|
5
|
+
// bundled scaffold, and that is correct there. `package check` has no project:
|
|
6
|
+
// it is run by an AUTHOR against a package that may sit anywhere, and it
|
|
7
|
+
// still needs this generator's base schema (to unify against) and its feature
|
|
8
|
+
// catalogue (to know which names denote features). Those come from the
|
|
9
|
+
// installation doing the checking, which is this one.
|
|
10
|
+
//
|
|
11
|
+
// `__dirname` is `dist/helpers` in the shipped package, so the package root is
|
|
12
|
+
// two levels up. npm can only ship files under that root, which is why the
|
|
13
|
+
// canonical `model/` is mirrored into `ts/model/` (the model-mirror guard
|
|
14
|
+
// fails on drift).
|
|
15
|
+
|
|
16
|
+
import Path from 'node:path'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function packageRoot(): string {
|
|
20
|
+
return Path.resolve(__dirname, '..', '..')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// The base model schema — `@voxgig/sdkgen/model/sdkgen.aontu`.
|
|
25
|
+
function schemaFile(): string {
|
|
26
|
+
return Path.join(packageRoot(), 'model', 'sdkgen.aontu')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// The bundled scaffold — the `.sdk` of `ts/project`, which is itself an
|
|
31
|
+
// sdkgen package.
|
|
32
|
+
function scaffoldFolder(): string {
|
|
33
|
+
return Path.join(packageRoot(), 'project', '.sdk')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
packageRoot,
|
|
39
|
+
schemaFile,
|
|
40
|
+
scaffoldFolder,
|
|
41
|
+
}
|
package/src/helpers/stdrep.ts
CHANGED
|
@@ -50,7 +50,77 @@ function templateReplacements(model: any, tname: string): Record<string, string>
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
// PROVENANCE — where a copied target or feature came from.
|
|
54
|
+
//
|
|
55
|
+
// Every shipped model file carries a `base: 'BASE'` line as an ANCHOR, and
|
|
56
|
+
// the add actions rewrite that one line into the block the copy keeps:
|
|
57
|
+
//
|
|
58
|
+
// base: 'BASE' -> base: 'node_modules/@acme/sdkgen-iot/.sdk'
|
|
59
|
+
// origname: 'iot-go'
|
|
60
|
+
//
|
|
61
|
+
// The anchor is why every shipped model needs the line even though most
|
|
62
|
+
// installs are unaliased from the bundled scaffold: a `replace` map can only
|
|
63
|
+
// rewrite text that is already there, and there is nowhere else to hang the
|
|
64
|
+
// keys that do not appear in the source.
|
|
65
|
+
//
|
|
66
|
+
// ONE definition, for the same reason templateReplacements is one: `target
|
|
67
|
+
// add` and `feature add` WRITE with this map and `doctor` re-applies it to
|
|
68
|
+
// the scaffold before comparing. A writer and a reader that disagree by a
|
|
69
|
+
// single character make every project read as forked.
|
|
70
|
+
//
|
|
71
|
+
// `origname` and `package` are emitted only when they apply, so an ordinary
|
|
72
|
+
// install still records exactly one line and its model file is unchanged
|
|
73
|
+
// from what earlier versions wrote — apart from the base value itself.
|
|
74
|
+
type Provenance = {
|
|
75
|
+
// The `.sdk` folder the copy came from, project-relative and
|
|
76
|
+
// '/'-normalised (see resolveTarget).
|
|
77
|
+
base: string
|
|
78
|
+
|
|
79
|
+
// The name in the SOURCE and the name it was INSTALLED as. Equal for an
|
|
80
|
+
// ordinary add; different for `target add go~go2`.
|
|
81
|
+
origname?: string
|
|
82
|
+
name?: string
|
|
83
|
+
|
|
84
|
+
// The sdkgen package that provided it, when the source declared one.
|
|
85
|
+
package?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Two spaces, matching every shipped model file's indentation for these keys.
|
|
89
|
+
const PROVENANCE_INDENT = ' '
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// A value as a single-quoted aontu string. These are PATHS and NAMES from the
|
|
93
|
+
// filesystem, so they can legally contain a quote — `/home/o'connor/pkg` is a
|
|
94
|
+
// valid directory — and concatenating one between quotes closes the string
|
|
95
|
+
// early and leaves the copied model unparsable. Aontu accepts a backslash
|
|
96
|
+
// escape, which keeps the quoting style every shipped model already uses.
|
|
97
|
+
function aontuString(value: string): string {
|
|
98
|
+
return "'" + String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
function provenanceReplace(prov: Provenance): Record<string, string> {
|
|
103
|
+
const lines = ['base: ' + aontuString(prov.base)]
|
|
104
|
+
|
|
105
|
+
if (null != prov.origname && null != prov.name &&
|
|
106
|
+
prov.origname !== prov.name) {
|
|
107
|
+
lines.push('origname: ' + aontuString(prov.origname))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (null != prov.package && '' !== prov.package) {
|
|
111
|
+
lines.push('package: ' + aontuString(prov.package))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { "base: 'BASE'": lines.join('\n' + PROVENANCE_INDENT) }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
export type {
|
|
119
|
+
Provenance,
|
|
120
|
+
}
|
|
121
|
+
|
|
53
122
|
export {
|
|
54
123
|
ensureStdrep,
|
|
55
124
|
templateReplacements,
|
|
125
|
+
provenanceReplace,
|
|
56
126
|
}
|