@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.
Files changed (198) hide show
  1. package/bin/voxgig-sdkgen +78 -10
  2. package/dist/action/action.d.ts +5 -2
  3. package/dist/action/action.js +113 -6
  4. package/dist/action/action.js.map +1 -1
  5. package/dist/action/check.d.ts +15 -0
  6. package/dist/action/check.js +347 -0
  7. package/dist/action/check.js.map +1 -0
  8. package/dist/action/dispatch.d.ts +8 -0
  9. package/dist/action/dispatch.js +74 -0
  10. package/dist/action/dispatch.js.map +1 -0
  11. package/dist/action/docs.d.ts +4 -0
  12. package/dist/action/docs.js +236 -0
  13. package/dist/action/docs.js.map +1 -0
  14. package/dist/action/doctor.d.ts +5 -2
  15. package/dist/action/doctor.js +526 -74
  16. package/dist/action/doctor.js.map +1 -1
  17. package/dist/action/feature.js +96 -17
  18. package/dist/action/feature.js.map +1 -1
  19. package/dist/action/kind.d.ts +30 -0
  20. package/dist/action/kind.js +258 -0
  21. package/dist/action/kind.js.map +1 -0
  22. package/dist/action/package.d.ts +25 -0
  23. package/dist/action/package.js +852 -0
  24. package/dist/action/package.js.map +1 -0
  25. package/dist/action/resolve.d.ts +18 -0
  26. package/dist/action/resolve.js +280 -0
  27. package/dist/action/resolve.js.map +1 -0
  28. package/dist/action/target.d.ts +7 -1
  29. package/dist/action/target.js +240 -97
  30. package/dist/action/target.js.map +1 -1
  31. package/dist/cmp/Docs.d.ts +4 -0
  32. package/dist/cmp/Docs.js +98 -0
  33. package/dist/cmp/Docs.js.map +1 -0
  34. package/dist/cmp/ExternalDocs.d.ts +2 -0
  35. package/dist/cmp/ExternalDocs.js +43 -0
  36. package/dist/cmp/ExternalDocs.js.map +1 -0
  37. package/dist/cmp/ReadmeTop.js +5 -1
  38. package/dist/cmp/ReadmeTop.js.map +1 -1
  39. package/dist/helpers/definition.d.ts +5 -0
  40. package/dist/helpers/definition.js +72 -0
  41. package/dist/helpers/definition.js.map +1 -0
  42. package/dist/helpers/featureSource.d.ts +8 -2
  43. package/dist/helpers/featureSource.js +61 -12
  44. package/dist/helpers/featureSource.js.map +1 -1
  45. package/dist/helpers/junk.d.ts +11 -0
  46. package/dist/helpers/junk.js +149 -0
  47. package/dist/helpers/junk.js.map +1 -0
  48. package/dist/helpers/manifest.d.ts +50 -0
  49. package/dist/helpers/manifest.js +328 -0
  50. package/dist/helpers/manifest.js.map +1 -0
  51. package/dist/helpers/modelcheck.d.ts +22 -0
  52. package/dist/helpers/modelcheck.js +203 -0
  53. package/dist/helpers/modelcheck.js.map +1 -0
  54. package/dist/helpers/semver.d.ts +4 -0
  55. package/dist/helpers/semver.js +174 -0
  56. package/dist/helpers/semver.js.map +1 -0
  57. package/dist/helpers/shipped.d.ts +4 -0
  58. package/dist/helpers/shipped.js +36 -0
  59. package/dist/helpers/shipped.js.map +1 -0
  60. package/dist/helpers/stdrep.d.ts +9 -1
  61. package/dist/helpers/stdrep.js +22 -0
  62. package/dist/helpers/stdrep.js.map +1 -1
  63. package/dist/sdkgen.d.ts +15 -3
  64. package/dist/sdkgen.js +233 -67
  65. package/dist/sdkgen.js.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/dist/types.d.ts +10 -2
  68. package/dist/utility.js +17 -0
  69. package/dist/utility.js.map +1 -1
  70. package/model/sdkgen.aontu +69 -0
  71. package/package.json +3 -2
  72. package/project/.sdk/model/feature/audit.aontu +1 -0
  73. package/project/.sdk/model/feature/cache.aontu +1 -0
  74. package/project/.sdk/model/feature/clienttrack.aontu +1 -0
  75. package/project/.sdk/model/feature/debug.aontu +1 -0
  76. package/project/.sdk/model/feature/idempotency.aontu +1 -0
  77. package/project/.sdk/model/feature/log.aontu +1 -0
  78. package/project/.sdk/model/feature/metrics.aontu +1 -0
  79. package/project/.sdk/model/feature/netsim.aontu +1 -0
  80. package/project/.sdk/model/feature/paging.aontu +1 -0
  81. package/project/.sdk/model/feature/proxy.aontu +1 -0
  82. package/project/.sdk/model/feature/ratelimit.aontu +1 -0
  83. package/project/.sdk/model/feature/rbac.aontu +1 -0
  84. package/project/.sdk/model/feature/retry.aontu +1 -0
  85. package/project/.sdk/model/feature/streaming.aontu +1 -0
  86. package/project/.sdk/model/feature/telemetry.aontu +1 -0
  87. package/project/.sdk/model/feature/test.aontu +1 -0
  88. package/project/.sdk/model/feature/timeout.aontu +1 -0
  89. package/project/.sdk/model/target/c.aontu +1 -0
  90. package/project/.sdk/model/target/clojure.aontu +1 -0
  91. package/project/.sdk/model/target/cpp.aontu +1 -0
  92. package/project/.sdk/model/target/dart.aontu +1 -0
  93. package/project/.sdk/model/target/elixir.aontu +1 -0
  94. package/project/.sdk/model/target/go-cli.aontu +1 -0
  95. package/project/.sdk/model/target/go-mcp.aontu +1 -0
  96. package/project/.sdk/model/target/go.aontu +1 -0
  97. package/project/.sdk/model/target/haskell.aontu +1 -0
  98. package/project/.sdk/model/target/java.aontu +1 -0
  99. package/project/.sdk/model/target/js.aontu +1 -0
  100. package/project/.sdk/model/target/kotlin.aontu +1 -0
  101. package/project/.sdk/model/target/lean.aontu +1 -0
  102. package/project/.sdk/model/target/lua.aontu +1 -0
  103. package/project/.sdk/model/target/ocaml.aontu +1 -0
  104. package/project/.sdk/model/target/perl.aontu +1 -0
  105. package/project/.sdk/model/target/php.aontu +1 -0
  106. package/project/.sdk/model/target/py-data.aontu +1 -0
  107. package/project/.sdk/model/target/py.aontu +1 -0
  108. package/project/.sdk/model/target/rb.aontu +1 -0
  109. package/project/.sdk/model/target/rust.aontu +1 -0
  110. package/project/.sdk/model/target/scala.aontu +1 -0
  111. package/project/.sdk/model/target/seneca-provider.aontu +1 -0
  112. package/project/.sdk/model/target/zig.aontu +1 -0
  113. package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
  114. package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
  115. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
  116. package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
  117. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
  118. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
  119. package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
  120. package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
  121. package/project/.sdk/src/cmp/dart/Config_dart.ts +56 -3
  122. package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
  123. package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
  124. package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
  125. package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
  126. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
  127. package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
  128. package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
  129. package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
  130. package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
  131. package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
  132. package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
  133. package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
  134. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
  135. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
  136. package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
  137. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
  138. package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
  139. package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
  140. package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
  141. package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
  142. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
  143. package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
  144. package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
  145. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +31 -18
  146. package/project/.sdk/tm/c/core/sdk.h +1 -0
  147. package/project/.sdk/tm/c/utility/make_options.c +5 -1
  148. package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
  149. package/project/.sdk/tm/cpp/core/types.hpp +4 -1
  150. package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
  151. package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
  152. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
  153. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
  154. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
  155. package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
  156. package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
  157. package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
  158. package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
  159. package/project/.sdk/tm/java/core/SdkClient.java +3 -1
  160. package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
  161. package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
  162. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
  163. package/project/.sdk/tm/ocaml/Makefile +3 -2
  164. package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
  165. package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
  166. package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
  167. package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
  168. package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
  169. package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
  170. package/project/.sdk/tm/scala/utility/Make.scala +5 -1
  171. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
  172. package/project/.sdk/tm/zig/core/utility.zig +85 -1
  173. package/project/.sdk/tm/zig/test/gotcha_test.zig +8 -8
  174. package/project/sdkgen-package.json +57 -0
  175. package/src/action/action.ts +136 -6
  176. package/src/action/check.ts +495 -0
  177. package/src/action/dispatch.ts +105 -0
  178. package/src/action/docs.ts +310 -0
  179. package/src/action/doctor.ts +692 -81
  180. package/src/action/feature.ts +109 -17
  181. package/src/action/kind.ts +375 -0
  182. package/src/action/package.ts +1173 -0
  183. package/src/action/resolve.ts +362 -0
  184. package/src/action/target.ts +290 -108
  185. package/src/cmp/Docs.ts +125 -0
  186. package/src/cmp/ExternalDocs.ts +54 -0
  187. package/src/cmp/ReadmeTop.ts +6 -1
  188. package/src/helpers/definition.ts +82 -0
  189. package/src/helpers/featureSource.ts +83 -13
  190. package/src/helpers/junk.ts +168 -0
  191. package/src/helpers/manifest.ts +474 -0
  192. package/src/helpers/modelcheck.ts +262 -0
  193. package/src/helpers/semver.ts +211 -0
  194. package/src/helpers/shipped.ts +41 -0
  195. package/src/helpers/stdrep.ts +70 -0
  196. package/src/sdkgen.ts +294 -66
  197. package/src/types.ts +33 -3
  198. package/src/utility.ts +17 -0
@@ -0,0 +1,362 @@
1
+ // Where a `<kind> add <ref>` gets its definition from.
2
+ //
3
+ // One resolver for every KIND of thing an add can install — targets today,
4
+ // features as of this change, more later — because the ref grammar is the
5
+ // same whatever is being added, and it had drifted: `target add` accepted a
6
+ // path ref and an alias, while `feature add` accepted only a bare name and
7
+ // read from a hardcoded `node_modules/@voxgig/sdkgen`. A feature defined
8
+ // anywhere else could not be added at all.
9
+ //
10
+ // It lives in its own module rather than in `target.ts` because `target.ts`
11
+ // imports `feature_add`, so a resolver there would put `feature.ts` and
12
+ // `target.ts` in a require cycle.
13
+
14
+ import Path from 'node:path'
15
+
16
+ import { getelem } from '@voxgig/struct'
17
+
18
+ import { KIT } from '../types'
19
+
20
+ import { definitionPath } from '../helpers/definition'
21
+ import { readManifest, checkShape, ITEM_NAME_RE } from '../helpers/manifest'
22
+
23
+
24
+ // The bundled scaffold: what a bare name resolves to.
25
+ const BUNDLED = 'node_modules/@voxgig/sdkgen/project/.sdk'
26
+
27
+
28
+ type Source = {
29
+ // The name it is INSTALLED as (the alias, when one was given).
30
+ name: string
31
+
32
+ // The name it has in the SOURCE. Differs from `name` only for an alias.
33
+ origname: string
34
+
35
+ // The resolved `.sdk` folder, as a path to read from.
36
+ folder: string
37
+
38
+ // The same folder relative to the project, '/'-normalised — the value
39
+ // recorded as provenance. See helpers/stdrep.
40
+ base: string
41
+
42
+ // The definition file inside that folder, for this kind.
43
+ model: string
44
+
45
+ // The sdkgen package that provided it, when the source has a manifest.
46
+ // Undefined for a bare `.sdk`-shaped folder, which stays legal.
47
+ package?: string
48
+ }
49
+
50
+
51
+ // Last path segment of a ref. A ref may be a bare name ('go'), a
52
+ // package-relative path ('@acme/kit/go'), or an ABSOLUTE path — and on Windows
53
+ // an absolute path is separated by `\`, so splitting on '/' alone hands back
54
+ // the whole path as the name and every lookup below then misses. On POSIX
55
+ // Path.sep IS '/', so this is the same split it always was.
56
+ function lastSegment(ref: string): string {
57
+ return getelem(ref.split('/').flatMap((p: string) => p.split(Path.sep)), -1)
58
+ }
59
+
60
+
61
+ function resolveSource(ref: string, kind: string, ctx$: any): Source {
62
+ const root = ctx$.folder
63
+ const fs = ctx$.fs()
64
+
65
+ let folder = Path.normalize(Path.join(root, BUNDLED))
66
+ let name = lastSegment(ref)
67
+
68
+ // `<ref>~<alias>` installs the ref's definition under a different name.
69
+ //
70
+ // The `~` is only an alias separator in the LAST SEGMENT, which is where a
71
+ // name can appear. Splitting the whole ref on it broke any ref whose PATH
72
+ // contains a tilde — and on Windows that is routine: an 8.3 short name like
73
+ // `C:\Users\RUNNER~1\AppData\Local\Temp\pkg\circuitbreaker` was read as
74
+ // "install `C:\Users\RUNNER` under the alias `1\AppData\...`", which then
75
+ // looked for `C:\Users\.sdk`. Legal on POSIX too — a directory may simply
76
+ // be called `foo~bar`.
77
+ const sep = Math.max(ref.lastIndexOf('/'), ref.lastIndexOf(Path.sep))
78
+ const dir = sep < 0 ? '' : ref.slice(0, sep + 1)
79
+ const last = sep < 0 ? ref : ref.slice(sep + 1)
80
+
81
+ const aliasing = last.split('~')
82
+ const origlast = aliasing[0]
83
+
84
+ let aliasref = dir + origlast
85
+ let origname = origlast
86
+ if (1 < aliasing.length) {
87
+ name = aliasing.slice(1).join('~')
88
+
89
+ // The alias becomes a DESTINATION: `model/<kind>/<name>.aontu`,
90
+ // `src/cmp/<name>/`, `tm/<name>/` — all written, and `target add`
91
+ // overwrites what it writes. `go~..` therefore redirects the copy out of
92
+ // the target-specific directory and over unrelated scaffold. Checked
93
+ // against the same grammar the manifest applies to a name, because it is
94
+ // the same thing: what the item is called once installed.
95
+ if (!ITEM_NAME_RE.test(name)) {
96
+ throw new Error(
97
+ 'Invalid ' + kind + ' alias: ' + JSON.stringify(name) +
98
+ ' in ' + ref + '\n an alias is a NAME (matching ' +
99
+ ITEM_NAME_RE.source + '), not a path — it becomes the directory the ' +
100
+ kind + ' is installed into')
101
+ }
102
+ }
103
+
104
+ const search: string[] = []
105
+ let found = false
106
+
107
+ // Windows: an absolute ref is `D:\a\...` or `D:/a/...`, and a Path.join'd
108
+ // one carries backslashes, so neither `includes('/')` nor `startsWith('/')`
109
+ // recognises it. Path.isAbsolute and Path.sep are platform-correct and
110
+ // reduce to the same answers on POSIX.
111
+ if (aliasref.includes('/') || aliasref.includes(Path.sep)) {
112
+ // NOTE: the last path element of the ref is the name, not a folder.
113
+ const aliasbase = Path.dirname(aliasref)
114
+
115
+ if (!Path.isAbsolute(aliasref)) {
116
+ folder = Path.normalize(Path.join(root, 'node_modules', aliasbase, '.sdk'))
117
+ search.push(folder)
118
+ found = fs.existsSync(folder)
119
+
120
+ if (!found) {
121
+ folder = Path.normalize(Path.join(root, aliasbase, '.sdk'))
122
+ search.push(folder)
123
+ found = fs.existsSync(folder)
124
+ }
125
+ }
126
+ else {
127
+ folder = Path.normalize(Path.join(aliasbase, '.sdk'))
128
+ search.push(folder)
129
+ found = fs.existsSync(folder)
130
+ }
131
+ }
132
+ else {
133
+ search.push(folder)
134
+ found = fs.existsSync(folder)
135
+ }
136
+
137
+ if (!found) {
138
+ throw new Error(
139
+ capitalise(kind) + ' folder not found in:\n' + search.join('\n '))
140
+ }
141
+
142
+ // `base` is the folder relative to the project root. Compare with the
143
+ // PLATFORM separator: on Windows `root + '/'` never prefixes a normalised
144
+ // absolute path, so the root would not be stripped and `base` would stay
145
+ // absolute. Normalise both sides first for the same reason.
146
+ const nroot = Path.normalize(root)
147
+ const rootslash = nroot.endsWith(Path.sep) ? nroot : nroot + Path.sep
148
+
149
+ return {
150
+ name,
151
+ origname,
152
+ folder,
153
+ // '/'-normalised, unlike `folder`. `base` is the one value here that gets
154
+ // WRITTEN INTO A COMMITTED FILE (the provenance stamp), so it must not
155
+ // depend on the OS that ran the add: on Windows Path.join yields
156
+ // `node_modules\@voxgig\sdkgen\project\.sdk`, so the same project resynced
157
+ // on Linux and on Windows produced two different model files and each
158
+ // churned the other's. Forward slashes are accepted by every Node path API
159
+ // on Windows, so the readers are unaffected.
160
+ base: (folder.startsWith(rootslash)
161
+ ? folder.slice(rootslash.length)
162
+ : folder).split(Path.sep).join('/'),
163
+ // Path.join, not concatenation: an absolute Windows ref makes `folder`
164
+ // backslash-separated, and appending '/model/...' produced a mixed-
165
+ // separator path that some readers handle and others do not.
166
+ model: definitionPath(folder, kind, origname),
167
+
168
+ // The package that provided it, when the source declares a manifest.
169
+ // Read here rather than by the callers so a DIRECT ref
170
+ // (`target add ../pkg/iot-go`) records the same provenance
171
+ // `package add @acme/sdkgen-iot` would — the two spellings install the
172
+ // same thing, so they must record the same thing.
173
+ //
174
+ // A manifest is OPTIONAL for a direct ref and its absence is not a
175
+ // finding here: a bare `.sdk`-shaped folder is still a valid source, and
176
+ // every consumer's existing fixtures are exactly that. It is `package
177
+ // add` that requires one.
178
+ package: sourcePackage(fs, folder, kind, origname, ctx$),
179
+ }
180
+ }
181
+
182
+
183
+ // The manifest's package name, or undefined.
184
+ //
185
+ // TOLERANT BY DESIGN, on this path. A malformed manifest must not break
186
+ // `target add` — the definition, the components and the templates are all
187
+ // present and correct, and refusing to install them because a JSON file
188
+ // beside them has a trailing comma would be a worse outcome than installing
189
+ // them with one provenance line missing. `package add` validates properly and
190
+ // refuses; this only reads a name.
191
+ //
192
+ // Tolerant is not the same as credulous. The SHAPE is checked before the name
193
+ // is believed, because `package:` provenance is what `package update` later
194
+ // acts on: recording a project as belonging to `@acme/sdkgen-iot` on the
195
+ // strength of a file that declares no schema version and no `provides` would
196
+ // point a future update at a package that cannot be added at all. A manifest
197
+ // that would fail validation is treated exactly like one that would not
198
+ // parse — warn, and record nothing.
199
+ function sourcePackage(
200
+ fs: any, folder: string, kind: string, origname: string, ctx$: any,
201
+ ): string | undefined {
202
+ const read = readManifest(fs, folder)
203
+
204
+ if (null != read.err) {
205
+ warnManifest(ctx$, read.file, read.err)
206
+ return undefined
207
+ }
208
+
209
+ if (null == read.manifest) {
210
+ return undefined
211
+ }
212
+
213
+ const shape = checkShape(read.manifest, read.file)
214
+
215
+ if (0 < shape.length) {
216
+ warnManifest(ctx$, read.file,
217
+ shape.map((f: any) => f.note).join('; '))
218
+ return undefined
219
+ }
220
+
221
+ // The manifest must actually CLAIM this item. A package may carry a
222
+ // definition it deliberately does not list — that is what the
223
+ // `manifest-item-unclaimed` warning is about, "nothing will install it" —
224
+ // and stamping the package name onto one anyway would record that
225
+ // `@acme/sdkgen-iot` supplied something `package add @acme/sdkgen-iot`
226
+ // would never install, which is exactly the equivalence the comment at the
227
+ // call site rests on. `package update` would then act on it.
228
+ const claimed = read.manifest.provides?.[kind]
229
+
230
+ if (!Array.isArray(claimed) || !claimed.includes(origname)) {
231
+ ctx$.log?.info({
232
+ point: 'package-item-unclaimed', file: read.file, kind, name: origname,
233
+ note: read.file + ': ' + kind + ' `' + origname + '` is not listed in ' +
234
+ '`provides.' + kind + '`, so the copy records no `package` ' +
235
+ 'provenance — add it to the manifest if the package supplies it'
236
+ })
237
+ return undefined
238
+ }
239
+
240
+ return read.manifest.name
241
+ }
242
+
243
+
244
+ function warnManifest(ctx$: any, file: string, err: string) {
245
+ ctx$.log?.warn({
246
+ point: 'package-manifest-unreadable', file, err,
247
+ note: file + ': ignoring an unusable package manifest (' + err +
248
+ '); the copy records no `package` provenance'
249
+ })
250
+ }
251
+
252
+
253
+ // Teach the IN-MEMORY model about items that have just been installed.
254
+ //
255
+ // An action reads `actx.model`, compiled from `model/sdk.aontu` before the
256
+ // run. Writing `model/target/iotgo.aontu` does not change it and nothing
257
+ // recompiles mid-process, so anything later in the SAME command behaves as if
258
+ // the item is not installed.
259
+ //
260
+ // That is not cosmetic. `feature add` copies a feature's source into every
261
+ // target IN THE MODEL, using the two-tree lookup that reaches a feature
262
+ // package's overlay — so a target missing from the model gets source only
263
+ // from its own tree. For a target and an active feature that come from
264
+ // DIFFERENT packages, that means no source at all: the overlay is never
265
+ // consulted for it, and the target's own tree does not have it. Silent, bar
266
+ // one `feature-source-missing` warning.
267
+ //
268
+ // ONE definition, called by `target_add` before its own fan-out and by
269
+ // `package add` after each kind, because two places computing what to record
270
+ // is how the recorded values drift from the stamped ones.
271
+ function registerInstalled(kind: string, refs: string[], ctx$: any) {
272
+ const kit: any = ctx$.model?.main?.[KIT]
273
+
274
+ if (null == kit) {
275
+ return
276
+ }
277
+
278
+ const items = kit[kind] = kit[kind] ?? {}
279
+
280
+ for (const ref of refs) {
281
+ let source: Source
282
+ try {
283
+ source = resolveSource(ref, kind, ctx$)
284
+ }
285
+ catch (err: any) {
286
+ ctx$.log?.warn({
287
+ point: 'model-register-failed', kind, ref, err: err.message,
288
+ note: ref + ': could not be added to the in-memory model (' +
289
+ err.message + '); anything later in this command will behave as ' +
290
+ 'if it is not installed'
291
+ })
292
+ continue
293
+ }
294
+
295
+ // Merge, never replace: the model may already carry the project's own
296
+ // configuration for this item, which is the project's, not the source's.
297
+ items[source.name] = {
298
+ ...(items[source.name] ?? {}),
299
+ name: source.name,
300
+ base: source.base,
301
+ origname: source.origname,
302
+ ...(null == source.package ? {} : { package: source.package }),
303
+ }
304
+ }
305
+ }
306
+
307
+
308
+ // Is this name already installed FROM SOMEWHERE ELSE?
309
+ //
310
+ // `add` is overwrite, deliberately — that is how a resync works. But
311
+ // overwriting one package's target with a different package's target of the
312
+ // same name is not a resync, it is a collision, and doing it silently
313
+ // replaces a working target's model, components and templates with another's.
314
+ //
315
+ // Returns the conflicting record, or undefined when the name is free or is
316
+ // the SAME source (which is a resync and must keep working).
317
+ function nameConflict(
318
+ kind: string, source: Source, ctx$: any,
319
+ ): { package?: string, base?: string } | undefined {
320
+ const declared: any = ctx$.model?.main?.[KIT]?.[kind]?.[source.name]
321
+
322
+ if (null == declared || 'object' !== typeof declared) {
323
+ return undefined
324
+ }
325
+
326
+ // Nothing recorded — a copy predating provenance. It cannot be shown to be
327
+ // a different source, and refusing on a suspicion would block every
328
+ // pre-provenance project from adopting a package.
329
+ if (null == declared.base || '' === declared.base) {
330
+ return undefined
331
+ }
332
+
333
+ const samePackage = null != source.package && '' !== source.package &&
334
+ declared.package === source.package
335
+
336
+ const sameBase = normaliseBase(declared.base) === normaliseBase(source.base)
337
+
338
+ return (samePackage || sameBase) ? undefined : declared
339
+ }
340
+
341
+
342
+ function normaliseBase(base: string): string {
343
+ return Path.normalize(String(base ?? '')).split(Path.sep).join('/')
344
+ }
345
+
346
+
347
+ function capitalise(s: string): string {
348
+ return s.charAt(0).toUpperCase() + s.slice(1)
349
+ }
350
+
351
+
352
+ export type {
353
+ Source,
354
+ }
355
+
356
+ export {
357
+ resolveSource,
358
+ registerInstalled,
359
+ nameConflict,
360
+ lastSegment,
361
+ BUNDLED,
362
+ }