@voxgig/sdkgen 4.5.3 → 4.7.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 (248) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/check.js +15 -0
  3. package/dist/action/check.js.map +1 -1
  4. package/dist/cmp/AgentGuide.js +1 -1
  5. package/dist/cmp/AgentGuide.js.map +1 -1
  6. package/dist/cmp/AgentGuideContent.d.ts +1 -1
  7. package/dist/cmp/AgentGuideContent.js +8 -2
  8. package/dist/cmp/AgentGuideContent.js.map +1 -1
  9. package/dist/cmp/Feature.js +22 -0
  10. package/dist/cmp/Feature.js.map +1 -1
  11. package/dist/cmp/FeatureDocs.d.ts +1 -1
  12. package/dist/cmp/FeatureDocs.js +6 -1
  13. package/dist/cmp/FeatureDocs.js.map +1 -1
  14. package/dist/cmp/ReadmeExplanation.js +3 -1
  15. package/dist/cmp/ReadmeExplanation.js.map +1 -1
  16. package/dist/cmp/ReadmeFeatures.js +1 -1
  17. package/dist/cmp/ReadmeFeatures.js.map +1 -1
  18. package/dist/cmp/ReadmeRefFeatures.js +1 -1
  19. package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
  20. package/dist/cmp/TestControl.d.ts +4 -0
  21. package/dist/cmp/TestControl.js +51 -0
  22. package/dist/cmp/TestControl.js.map +1 -0
  23. package/dist/helpers/applicability.d.ts +6 -0
  24. package/dist/helpers/applicability.js +103 -0
  25. package/dist/helpers/applicability.js.map +1 -0
  26. package/dist/helpers/collectDeps.js +4 -2
  27. package/dist/helpers/collectDeps.js.map +1 -1
  28. package/dist/helpers/featureSource.d.ts +3 -1
  29. package/dist/helpers/featureSource.js +105 -0
  30. package/dist/helpers/featureSource.js.map +1 -1
  31. package/dist/helpers/serverVars.d.ts +2 -1
  32. package/dist/helpers/serverVars.js +17 -0
  33. package/dist/helpers/serverVars.js.map +1 -1
  34. package/dist/sdkgen.d.ts +5 -3
  35. package/dist/sdkgen.js +16 -4
  36. package/dist/sdkgen.js.map +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/dist/utility.d.ts +2 -1
  39. package/dist/utility.js +59 -1
  40. package/dist/utility.js.map +1 -1
  41. package/model/sdkgen.aon +97 -0
  42. package/package.json +1 -1
  43. package/project/.sdk/model/feature/feature-index.aon +1 -0
  44. package/project/.sdk/model/feature/secrets.aon +200 -0
  45. package/project/.sdk/model/target/ts.aon +8 -1
  46. package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
  47. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
  48. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
  49. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
  50. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
  51. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
  52. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
  53. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
  54. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
  55. package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
  56. package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
  57. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
  58. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
  59. package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
  60. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
  61. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
  62. package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
  63. package/project/.sdk/src/cmp/go/Main_go.ts +8 -3
  64. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
  65. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
  66. package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
  67. package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
  68. package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
  69. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
  70. package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
  71. package/project/.sdk/src/cmp/java/Main_java.ts +7 -2
  72. package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
  73. package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
  74. package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
  75. package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
  76. package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
  77. package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
  78. package/project/.sdk/src/cmp/js/Main_js.ts +7 -2
  79. package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
  80. package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
  81. package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
  82. package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
  83. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
  84. package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
  85. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
  86. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
  87. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
  88. package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
  89. package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
  90. package/project/.sdk/src/cmp/lua/Main_lua.ts +7 -2
  91. package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
  92. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
  93. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
  94. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
  95. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
  96. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  97. package/project/.sdk/src/cmp/perl/Main_perl.ts +7 -2
  98. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
  99. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
  100. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
  101. package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
  102. package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
  103. package/project/.sdk/src/cmp/php/Main_php.ts +7 -2
  104. package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
  105. package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
  106. package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
  107. package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
  108. package/project/.sdk/src/cmp/py/Main_py.ts +7 -2
  109. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
  110. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
  111. package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
  112. package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
  113. package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
  114. package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
  115. package/project/.sdk/src/cmp/rb/Main_rb.ts +7 -2
  116. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
  117. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
  118. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
  119. package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
  120. package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
  121. package/project/.sdk/src/cmp/rust/Main_rust.ts +7 -2
  122. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
  123. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
  124. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
  125. package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
  126. package/project/.sdk/src/cmp/scala/Main_scala.ts +7 -2
  127. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
  128. package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
  129. package/project/.sdk/src/cmp/ts/Config_ts.ts +55 -9
  130. package/project/.sdk/src/cmp/ts/Main_ts.ts +67 -3
  131. package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
  132. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
  133. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
  134. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
  135. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +75 -8
  136. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +36 -5
  137. package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
  138. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
  139. package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
  140. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
  141. package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
  142. package/project/.sdk/tm/c/utility/make_options.c +22 -0
  143. package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
  144. package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
  145. package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
  146. package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
  147. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
  148. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
  149. package/project/.sdk/tm/dart/test/utility.dart +39 -0
  150. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
  151. package/project/.sdk/tm/go/core/context.go +1 -1
  152. package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
  153. package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
  154. package/project/.sdk/tm/go/feature/test_feature.go +12 -5
  155. package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
  156. package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
  157. package/project/.sdk/tm/go/test/runner_test.go +46 -0
  158. package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
  159. package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
  160. package/project/.sdk/tm/go/utility/fetcher.go +2 -2
  161. package/project/.sdk/tm/go/utility/make_options.go +24 -5
  162. package/project/.sdk/tm/go/utility/make_point.go +1 -1
  163. package/project/.sdk/tm/go/utility/make_spec.go +17 -1
  164. package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
  165. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
  166. package/project/.sdk/tm/go/utility/transform_request.go +29 -1
  167. package/project/.sdk/tm/go/utility/transform_response.go +20 -1
  168. package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
  169. package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
  170. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
  171. package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
  172. package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
  173. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
  174. package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
  175. package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
  176. package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
  177. package/project/.sdk/tm/js/test/utility.js +91 -1
  178. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
  179. package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
  180. package/project/.sdk/tm/lua/test/runner.lua +40 -0
  181. package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
  182. package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
  183. package/project/.sdk/tm/perl/t/runner.pm +30 -0
  184. package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
  185. package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
  186. package/project/.sdk/tm/php/test/Runner.php +36 -0
  187. package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
  188. package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
  189. package/project/.sdk/tm/py/test/runner.py +32 -0
  190. package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
  191. package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
  192. package/project/.sdk/tm/rb/test/runner.rb +25 -0
  193. package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
  194. package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
  195. package/project/.sdk/tm/rust/utility/make_options.rs +26 -1
  196. package/project/.sdk/tm/scala/utility/Make.scala +17 -0
  197. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
  198. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
  199. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +387 -0
  200. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +175 -0
  201. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +51 -0
  202. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
  203. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
  204. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
  205. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
  206. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
  207. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
  208. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
  209. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
  210. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
  211. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
  212. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
  213. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
  214. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
  215. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
  216. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
  217. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
  218. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
  219. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
  220. package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
  221. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
  222. package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
  223. package/project/.sdk/tm/ts/test/omni.ts +86 -0
  224. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
  225. package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
  226. package/project/.sdk/tm/ts/test/utility.ts +122 -0
  227. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +694 -0
  228. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +244 -0
  229. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
  230. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
  231. package/project/.sdk/tm/zig/core/utility.zig +24 -1
  232. package/project/sdkgen-package.json +2 -1
  233. package/src/action/check.ts +18 -0
  234. package/src/cmp/AgentGuide.ts +1 -1
  235. package/src/cmp/AgentGuideContent.ts +9 -2
  236. package/src/cmp/Feature.ts +24 -0
  237. package/src/cmp/FeatureDocs.ts +7 -1
  238. package/src/cmp/ReadmeExplanation.ts +4 -1
  239. package/src/cmp/ReadmeFeatures.ts +1 -1
  240. package/src/cmp/ReadmeRefFeatures.ts +1 -1
  241. package/src/cmp/TestControl.ts +64 -0
  242. package/src/helpers/applicability.ts +123 -0
  243. package/src/helpers/collectDeps.ts +5 -1
  244. package/src/helpers/featureSource.ts +130 -0
  245. package/src/helpers/serverVars.ts +19 -0
  246. package/src/sdkgen.ts +16 -1
  247. package/src/utility.ts +71 -1
  248. package/project/.sdk/tm/ts/test/runner.ts +0 -425
@@ -24,6 +24,8 @@ import {
24
24
  buildIdNames,
25
25
  getMatchEntries,
26
26
  isAuthActive,
27
+ serverVarEnv,
28
+ serverVariables,
27
29
  entityDataIdField, envName, envToken
28
30
  } from '@voxgig/sdkgen'
29
31
 
@@ -71,12 +73,26 @@ const TestEntity = cmp(function TestEntity(props: any) {
71
73
 
72
74
  const authActive = isAuthActive(model)
73
75
  const apikeyEnvEntry = authActive
74
- ? `\n\t\t"${PROJUPPER}_APIKEY": "NONE",`
76
+ ? `\n\t\t"${PROJUPPER}_APIKEY": "",`
75
77
  : ''
76
78
  const apikeyLiveField = authActive
77
79
  ? `\n\t\t\t\t"apikey": env["${PROJUPPER}_APIKEY"],`
78
80
  : ''
79
81
 
82
+
83
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
84
+ // impossible to construct without values: makeOptions refuses rather than
85
+ // request a URL with a literal `{account_id}` in it. Taken from the
86
+ // environment, the same way the apikey is.
87
+ const svars = serverVariables(model)
88
+ const serverEnvEntry = svars
89
+ .map((v: any) => `\n\t\t"${serverVarEnv(PROJUPPER, v.name)}": ${JSON.stringify(v.dflt)},`).join('')
90
+ const serverLiveField = 0 === svars.length ? '' :
91
+ `\n\t\t\t\t"server": map[string]any{` +
92
+ svars.map((v: any) =>
93
+ `\n\t\t\t\t\t"${v.name}": env["${serverVarEnv(PROJUPPER, v.name)}"],`).join('') +
94
+ `\n\t\t\t\t},`
95
+
80
96
  const idnames = buildIdNames(entity, basicflow)
81
97
  const idnamesStr = idnames.map(n => `"${n}"`).join(', ')
82
98
 
@@ -205,7 +221,7 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
205
221
  const preambleRef = entity.name + '_ref01'
206
222
  const preambleVar = goVar(preambleRef)
207
223
  Content(` // Bootstrap entity data from existing test data (no create step in flow).
208
- ${preambleVar}DataRaw := vs.Items(core.ToMapAny(vs.GetPath("existing.${entity.name}", setup.data)))
224
+ ${preambleVar}DataRaw := vs.Items(core.ToMapAny(vs.GetPath(setup.data, "existing.${entity.name}")))
209
225
  var ${preambleVar}Data map[string]any
210
226
  if len(${preambleVar}DataRaw) > 0 {
211
227
  ${preambleVar}Data = core.ToMapAny(${preambleVar}DataRaw[0][1])
@@ -265,9 +281,14 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
265
281
 
266
282
  `)
267
283
 
268
- // Generate idmap via vs.Transform
284
+ // Generate idmap via vs.Transform.
285
+ //
286
+ // The error return Transform gained in struct go 0.1.3 is discarded
287
+ // deliberately: this transforms the entity's own id names, which the
288
+ // generator produced, so an error would be a generator bug rather than
289
+ // anything the generated test can act on.
269
290
  Content('\t// Generate idmap via transform, matching TS pattern.\n')
270
- Content('\tidmap := vs.Transform(\n')
291
+ Content('\tidmap, _ := vs.Transform(\n')
271
292
  Content('\t\t[]any{' + idnamesStr + '},\n')
272
293
  Content('\t\tmap[string]any{\n')
273
294
  Content('\t\t\t"`$PACK`": []any{"", map[string]any{\n')
@@ -287,7 +308,7 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
287
308
  env := envOverride(map[string]any{
288
309
  "${PROJUPPER}_TEST_${envToken(entity.name)}_ENTID": idmap,
289
310
  "${PROJUPPER}_TEST_LIVE": "FALSE",
290
- "${PROJUPPER}_TEST_EXPLAIN": "FALSE",${apikeyEnvEntry}
311
+ "${PROJUPPER}_TEST_EXPLAIN": "FALSE",${apikeyEnvEntry}${serverEnvEntry}
291
312
  })
292
313
 
293
314
  idmapResolved := core.ToMapAny(env["${PROJUPPER}_TEST_${envToken(entity.name)}_ENTID"])
@@ -307,10 +328,22 @@ ${allSteps.length > 0 ? '\t\tclient := setup.client\n\n' : ''}`)
307
328
 
308
329
  Content(`
309
330
  if env["${PROJUPPER}_TEST_LIVE"] == "TRUE" {
331
+ // An empty map, not a nil one: Merge returns nil when its last entry
332
+ // is nil, and BasicSetup is normally called with no extras - so a
333
+ // bare nil silently discarded the apikey and server values below.
334
+ extraOpts := extra
335
+ if extraOpts == nil {
336
+ extraOpts = map[string]any{}
337
+ }
338
+
310
339
  mergedOpts := vs.Merge([]any{
311
- map[string]any{${apikeyLiveField}
340
+ // liveClientOptions() FIRST, so the generated fields below win:
341
+ // sdk-test-control.json's test.client.options adds to the live
342
+ // client, it does not redirect it.
343
+ liveClientOptions(),
344
+ map[string]any{${apikeyLiveField}${serverLiveField}
312
345
  },
313
- extra,
346
+ extraOpts,
314
347
  })
315
348
  client = sdk.New${model.const.Name}SDK(core.ToMapAny(mergedOpts))
316
349
  }
@@ -361,11 +394,11 @@ const generateCreate: OpGen = (ctx, step, index) => {
361
394
  // Load data from test data file
362
395
  if (hasDatvar) {
363
396
  Content(` ${datavar} = core.ToMapAny(vs.GetProp(
364
- vs.GetPath([]any{"new", "${entity.name}"}, setup.data), "${ref}"))
397
+ vs.GetPath(setup.data, []any{"new", "${entity.name}"}), "${ref}"))
365
398
  `)
366
399
  } else {
367
400
  Content(` ${datavar} := core.ToMapAny(vs.GetProp(
368
- vs.GetPath([]any{"new", "${entity.name}"}, setup.data), "${ref}"))
401
+ vs.GetPath(setup.data, []any{"new", "${entity.name}"}), "${ref}"))
369
402
  `)
370
403
  }
371
404
 
@@ -606,7 +639,7 @@ const generateLoad: OpGen = (ctx, step, index) => {
606
639
  `)
607
640
  }
608
641
  if (!hasSrcData && hasEntId) {
609
- Content(` ${srcdatavar}Raw := vs.Items(core.ToMapAny(vs.GetPath("existing.${entity.name}", setup.data)))
642
+ Content(` ${srcdatavar}Raw := vs.Items(core.ToMapAny(vs.GetPath(setup.data, "existing.${entity.name}")))
610
643
  var ${srcdatavar} map[string]any
611
644
  if len(${srcdatavar}Raw) > 0 {
612
645
  ${srcdatavar} = core.ToMapAny(${srcdatavar}Raw[0][1])
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, File, Content, goModule, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, File, Content, goModule, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_go'
@@ -21,6 +22,9 @@ const Test = cmp(function Test(props: any) {
21
22
 
22
23
  Folder({ name: 'test' }, () => {
23
24
 
25
+ // Write-once: a project's edited control file survives regeneration.
26
+ TestControl({ target, dir: 'test' })
27
+
24
28
  // Generate exists_test.go programmatically to avoid duplication
25
29
  // with any scaffolded test file (e.g. projectname_sdk_test.go).
26
30
  File({ name: 'exists_test.' + target.ext }, () => {
@@ -35,7 +35,7 @@ func NewProjectNameSDK(options map[string]any) *ProjectNameSDK {
35
35
 
36
36
  sdk.options = sdk.utility.MakeOptions(sdk.rootctx)
37
37
 
38
- if vs.GetPath([]any{"feature", "test", "active"}, sdk.options) == true {
38
+ if vs.GetPath(sdk.options, []any{"feature", "test", "active"}) == true {
39
39
  sdk.Mode = "test"
40
40
  }
41
41
 
@@ -48,7 +48,7 @@ func NewProjectNameSDK(options map[string]any) *ProjectNameSDK {
48
48
  // must be added before them to sit at the base of the chain.
49
49
  featureOpts := ToMapAny(vs.GetProp(sdk.options, "feature"))
50
50
  if featureOpts != nil {
51
- if fo, ok := vs.GetPath([]any{"__derived__", "featureorder"}, sdk.options).([]any); ok {
51
+ if fo, ok := vs.GetPath(sdk.options, []any{"__derived__", "featureorder"}).([]any); ok {
52
52
  for _, n := range fo {
53
53
  fname, _ := n.(string)
54
54
  fopts := ToMapAny(featureOpts[fname])
@@ -188,12 +188,12 @@ func (sdk *ProjectNameSDK) Direct(fetchargs map[string]any) (map[string]any, err
188
188
 
189
189
  // Is this raw-access op permitted by the SDK's allow.op option?
190
190
  func (sdk *ProjectNameSDK) opAllowed(op string) bool {
191
- allowOp, _ := vs.GetPath([]any{"allow", "op"}, sdk.options).(string)
191
+ allowOp, _ := vs.GetPath(sdk.options, []any{"allow", "op"}).(string)
192
192
  return strings.Contains(allowOp, op)
193
193
  }
194
194
 
195
195
  func (sdk *ProjectNameSDK) opDenied(op string) map[string]any {
196
- allowOp, _ := vs.GetPath([]any{"allow", "op"}, sdk.options).(string)
196
+ allowOp, _ := vs.GetPath(sdk.options, []any{"allow", "op"}).(string)
197
197
  return map[string]any{
198
198
  "ok": false,
199
199
  "err": fmt.Errorf("ProjectNameSDK: %s: operation not allowed by"+
@@ -322,7 +322,7 @@ func (sdk *ProjectNameSDK) Graphql(
322
322
  // body, and the raw path represents a non-2xx as ok:false with no err —
323
323
  // so returning early on status would discard the server's own
324
324
  // diagnostics, which are the only useful part of that response.
325
- errors, _ := vs.GetPath([]any{"data", "errors"}, res).([]any)
325
+ errors, _ := vs.GetPath(res, []any{"data", "errors"}).([]any)
326
326
 
327
327
  if 0 < len(errors) {
328
328
  msg, _ := vs.GetProp(errors[0], "message").(string)
@@ -7,6 +7,12 @@ import {
7
7
  each,
8
8
  isAuthActive,
9
9
  resolveAuthPrefix,
10
+ targetFeatures,
11
+ } from '@voxgig/sdkgen'
12
+
13
+
14
+ import {
15
+ serverVariables,
10
16
  } from '@voxgig/sdkgen'
11
17
 
12
18
 
@@ -37,7 +43,10 @@ const Config = cmp(async function Config(props: any) {
37
43
  const javapackage = javaPackage(model)
38
44
 
39
45
  const entity = getModelPath(model, `main.${KIT}.entity`)
40
- const feature = getModelPath(model, `main.${KIT}.feature`)
46
+ // Gated by the applicability tags, so this target never imports or
47
+ // registers a feature it has no source for. One rule, one place:
48
+ // helpers/applicability.
49
+ const feature = targetFeatures(model, target)
41
50
 
42
51
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
43
52
 
@@ -71,6 +80,18 @@ const Config = cmp(async function Config(props: any) {
71
80
  const options: Record<string, any> = {
72
81
  base: baseUrl,
73
82
  }
83
+
84
+ // Templated server URL: emit the spec's server-variable defaults so the
85
+ // runtime can substitute {name} placeholders in base (see MakeOptions).
86
+ // Without this block the placeholder reaches the wire verbatim — java
87
+ // shipped `http://host/api/{account_id}/element` as a real URL, which is
88
+ // worse than the construction error every other target raises.
89
+ const svars = serverVariables(model)
90
+ if (0 < svars.length) {
91
+ options.server = svars.reduce(
92
+ (a: any, v: any) => (a[v.name] = v.dflt, a), {} as Record<string, string>)
93
+ }
94
+
74
95
  if (authActive) {
75
96
  options.auth = { prefix: authPrefix }
76
97
  }
@@ -4,6 +4,8 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each,
6
6
  File, Content, Copy, Folder, Fragment,
7
+ targetFeatures,
8
+ TEST_CONTROL_EXCLUDE
7
9
  } from '@voxgig/sdkgen'
8
10
 
9
11
 
@@ -34,7 +36,10 @@ const Main = cmp(async function Main(props: any) {
34
36
  const { model } = props.ctx$
35
37
 
36
38
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
37
- const feature = getModelPath(model, `main.${KIT}.feature`)
39
+ // Gated by the applicability tags, so this target never imports or
40
+ // registers a feature it has no source for. One rule, one place:
41
+ // helpers/applicability.
42
+ const feature = targetFeatures(model, target)
38
43
 
39
44
  // The Java package root for every runtime piece (like GOMODULE for go):
40
45
  // e.g. voxgig.solardemosdk -> voxgig.solardemosdk.core etc.
@@ -48,7 +53,7 @@ const Main = cmp(async function Main(props: any) {
48
53
  // token used throughout the templates (package/import statements).
49
54
  Copy({
50
55
  from: 'tm/' + target.name,
51
- exclude: [/src\//],
56
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
52
57
  replace: {
53
58
  ...props.ctx$.stdrep,
54
59
  JAVAPACKAGE: javapackage,
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, envName, canonKey, canonScalarKey, opRequestShape, entityIdField, entityOps } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, envName, canonKey, canonScalarKey, opRequestShape, entityIdField, entityOps , serverVariables} from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -32,6 +32,18 @@ const ReadmeQuick = cmp(function ReadmeQuick(props: any) {
32
32
 
33
33
  const authActive = isAuthActive(model)
34
34
 
35
+ // Server variables (a templated server URL) are REQUIRED at construction
36
+ // - the SDK throws rather than request a URL with a literal
37
+ // {account_id} in it - so a quickstart that omits them is a quickstart
38
+ // that fails on its first line.
39
+ const svars = serverVariables(model)
40
+ const javaServerLines = 0 === svars.length ? '' :
41
+ 'Map<String, Object> server = new java.util.LinkedHashMap<>();\n' +
42
+ svars.map((v: any) =>
43
+ `server.put("${v.name}", "<${v.name}>");\n`).join('') +
44
+ 'options.put("server", server);\n'
45
+
46
+
35
47
  // A type-correct Java literal for a param — the loose object model means
36
48
  // all values live in a Map<String, Object>.
37
49
  const javaLit = (type: any, placeholder: string = 'example'): string => {
@@ -52,7 +64,7 @@ import ${javaPackage(model)}.core.${SDK};
52
64
 
53
65
  Map<String, Object> options = new java.util.LinkedHashMap<>();
54
66
  options.put("apikey", System.getenv("${envName(model)}_APIKEY"));
55
- ${SDK} client = new ${SDK}(options);
67
+ ${javaServerLines}${SDK} client = new ${SDK}(options);
56
68
  \`\`\`
57
69
 
58
70
  `)
@@ -63,7 +75,9 @@ ${SDK} client = new ${SDK}(options);
63
75
  \`\`\`java
64
76
  import ${javaPackage(model)}.core.${SDK};
65
77
 
66
- ${SDK} client = new ${SDK}();
78
+ ${'' === javaServerLines
79
+ ? `${SDK} client = new ${SDK}();`
80
+ : `Map<String, Object> options = new java.util.LinkedHashMap<>();\n${javaServerLines}${SDK} client = new ${SDK}(options);`}
67
81
  \`\`\`
68
82
 
69
83
  `)
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, envName, canonKey, canonScalarKey, entityIdField, opRequestShape } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, envName, canonKey, canonScalarKey, entityIdField, opRequestShape , serverVariables} from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -35,6 +35,18 @@ const ReadmeTopQuick = cmp(function ReadmeTopQuick(props: any) {
35
35
 
36
36
  const authActive = isAuthActive(model)
37
37
 
38
+ // Server variables (a templated server URL) are REQUIRED at construction
39
+ // - the SDK throws rather than request a URL with a literal
40
+ // {account_id} in it - so a quickstart that omits them is a quickstart
41
+ // that fails on its first line.
42
+ const svars = serverVariables(model)
43
+ const javaServerLines = 0 === svars.length ? '' :
44
+ 'Map<String, Object> server = new java.util.LinkedHashMap<>();\n' +
45
+ svars.map((v: any) =>
46
+ `server.put("${v.name}", "<${v.name}>");\n`).join('') +
47
+ 'options.put("server", server);\n'
48
+
49
+
38
50
  Content(`\`\`\`java
39
51
  import ${javaPackage(model)}.core.${SDK};
40
52
 
@@ -43,12 +55,14 @@ import ${javaPackage(model)}.core.${SDK};
43
55
  if (authActive) {
44
56
  Content(`Map<String, Object> options = new java.util.LinkedHashMap<>();
45
57
  options.put("apikey", System.getenv("${envName(model)}_APIKEY"));
46
- ${SDK} client = new ${SDK}(options);
58
+ ${javaServerLines}${SDK} client = new ${SDK}(options);
47
59
 
48
60
  `)
49
61
  }
50
62
  else {
51
- Content(`${SDK} client = new ${SDK}();
63
+ Content(`${'' === javaServerLines
64
+ ? `${SDK} client = new ${SDK}();`
65
+ : `Map<String, Object> options = new java.util.LinkedHashMap<>();\n${javaServerLines}${SDK} client = new ${SDK}(options);`}
52
66
 
53
67
  `)
54
68
  }
@@ -11,7 +11,9 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive,
15
+ serverVarEnv,
16
+ serverVariables, envName, envToken
15
17
  } from '@voxgig/sdkgen'
16
18
 
17
19
 
@@ -87,6 +89,20 @@ const TestDirect = cmp(function TestDirect(props: any) {
87
89
 
88
90
  const authActive = isAuthActive(model)
89
91
 
92
+
93
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
94
+ // impossible to construct without values: MakeOptions throws rather than
95
+ // request a URL with a literal `{account_id}` in it. Taken from the
96
+ // environment, the same way the apikey is.
97
+ const svars = serverVariables(model)
98
+ const serverEnvEntry = svars
99
+ .map((v: any) => ` envm.put("${serverVarEnv(PROJECTNAME, v.name)}", ${JSON.stringify(v.dflt)});\n`).join('')
100
+ const serverLiveField = 0 === svars.length ? '' :
101
+ ` Map<String, Object> serveropt = new LinkedHashMap<>();\n` +
102
+ svars.map((v: any) =>
103
+ ` serveropt.put("${v.name}", env.get("${serverVarEnv(PROJECTNAME, v.name)}"));\n`).join('') +
104
+ ` mergedOpts.put("server", serveropt);\n`
105
+
90
106
  const opnames = Object.keys(entity.op || {})
91
107
  const hasLoad = opnames.includes('load')
92
108
  const hasList = opnames.includes('list')
@@ -465,7 +481,7 @@ ${loadSkipBlock} ${SDK} client = setup.client;
465
481
  Map<String, Object> envm = new LinkedHashMap<>();
466
482
  envm.put("${entidEnvVar}", new LinkedHashMap<>());
467
483
  envm.put("${PROJECTNAME}_TEST_LIVE", "FALSE");
468
- ${authActive ? ` envm.put("${PROJECTNAME}_APIKEY", "NONE");\n` : ''} Map<String, Object> env = RunnerSupport.envOverride(envm);
484
+ ${authActive ? ` envm.put("${PROJECTNAME}_APIKEY", "");\n` : ''}${serverEnvEntry} Map<String, Object> env = RunnerSupport.envOverride(envm);
469
485
 
470
486
  boolean live = "TRUE".equals(env.get("${PROJECTNAME}_TEST_LIVE"));
471
487
 
@@ -473,8 +489,11 @@ ${authActive ? ` envm.put("${PROJECTNAME}_APIKEY", "NONE");\n` : ''} Map<S
473
489
  setup.calls = calls;
474
490
 
475
491
  if (live) {
476
- Map<String, Object> mergedOpts = new LinkedHashMap<>();
477
- ${authActive ? ` mergedOpts.put("apikey", env.get("${PROJECTNAME}_APIKEY"));\n` : ''} setup.client = new ${SDK}(mergedOpts);
492
+ // sdk-test-control.json's test.client.options seeds the live
493
+ // client; the generated fields below overwrite anything they name.
494
+ Map<String, Object> mergedOpts =
495
+ new LinkedHashMap<>(RunnerSupport.liveClientOptions());
496
+ ${authActive ? ` mergedOpts.put("apikey", env.get("${PROJECTNAME}_APIKEY"));\n` : ''}${serverLiveField} setup.client = new ${SDK}(mergedOpts);
478
497
  setup.live = true;
479
498
 
480
499
  Map<String, Object> idmap = new LinkedHashMap<>();
@@ -17,7 +17,9 @@ import {
17
17
  each,
18
18
  buildIdNames,
19
19
  getMatchEntries,
20
- isAuthActive, envName, envToken
20
+ isAuthActive,
21
+ serverVarEnv,
22
+ serverVariables, envName, envToken
21
23
  } from '@voxgig/sdkgen'
22
24
 
23
25
 
@@ -67,6 +69,20 @@ const TestEntity = cmp(function TestEntity(props: any) {
67
69
 
68
70
  const authActive = isAuthActive(model)
69
71
 
72
+
73
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
74
+ // impossible to construct without values: MakeOptions throws rather than
75
+ // request a URL with a literal `{account_id}` in it. Taken from the
76
+ // environment, the same way the apikey is.
77
+ const svars = serverVariables(model)
78
+ const serverEnvEntry = svars
79
+ .map((v: any) => ` envm.put("${serverVarEnv(PROJUPPER, v.name)}", ${JSON.stringify(v.dflt)});\n`).join('')
80
+ const serverLiveField = 0 === svars.length ? '' :
81
+ ` Map<String, Object> serveropt = new LinkedHashMap<>();\n` +
82
+ svars.map((v: any) =>
83
+ ` serveropt.put("${v.name}", env.get("${serverVarEnv(PROJUPPER, v.name)}"));\n`).join('') +
84
+ ` liveOpts.put("server", serveropt);\n`
85
+
70
86
  const idnames = buildIdNames(entity, basicflow)
71
87
 
72
88
  // Get all update data entries for alias generation
@@ -256,7 +272,7 @@ ${allSteps.length > 0 ? ` ${SDK} client = setup.client;\n\n` : ''}`)
256
272
  envm.put("${entidEnvVar}", idmap);
257
273
  envm.put("${PROJUPPER}_TEST_LIVE", "FALSE");
258
274
  envm.put("${PROJUPPER}_TEST_EXPLAIN", "FALSE");
259
- ${authActive ? ` envm.put("${PROJUPPER}_APIKEY", "NONE");\n` : ''} Map<String, Object> env = RunnerSupport.envOverride(envm);
275
+ ${authActive ? ` envm.put("${PROJUPPER}_APIKEY", "");\n` : ''}${serverEnvEntry} Map<String, Object> env = RunnerSupport.envOverride(envm);
260
276
 
261
277
  Map<String, Object> idmapResolved = Helpers.toMapAny(env.get("${entidEnvVar}"));
262
278
  if (idmapResolved == null) {
@@ -276,8 +292,17 @@ ${authActive ? ` envm.put("${PROJUPPER}_APIKEY", "NONE");\n` : ''} Map<Str
276
292
  Content(`
277
293
  boolean live = "TRUE".equals(env.get("${PROJUPPER}_TEST_LIVE"));
278
294
  if (live) {
279
- Map<String, Object> liveOpts = new LinkedHashMap<>();
280
- ${authActive ? ` liveOpts.put("apikey", env.get("${PROJUPPER}_APIKEY"));\n` : ''} Object mergedOpts = Struct.merge(Struct.jt(liveOpts, extra));
295
+ // sdk-test-control.json's test.client.options seeds the live
296
+ // client; the generated fields below overwrite anything they name.
297
+ Map<String, Object> liveOpts =
298
+ new LinkedHashMap<>(RunnerSupport.liveClientOptions());
299
+ ${authActive ? ` liveOpts.put("apikey", env.get("${PROJUPPER}_APIKEY"));\n` : ''}${serverLiveField} // An empty map, not a null one: merge answers null when its last
300
+ // entry is null, and basicSetup is normally called with no extras -
301
+ // so a bare null silently discarded the apikey and server values
302
+ // above.
303
+ Map<String, Object> extraOpts =
304
+ extra == null ? new LinkedHashMap<>() : extra;
305
+ Object mergedOpts = Struct.merge(Struct.jt(liveOpts, extraOpts));
281
306
  client = new ${SDK}(Helpers.toMapAny(mergedOpts));
282
307
  }
283
308
 
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_java'
@@ -20,6 +21,9 @@ const Test = cmp(function Test(props: any) {
20
21
 
21
22
  Folder({ name: 'test' }, () => {
22
23
 
24
+ // Write-once: a project's edited control file survives regeneration.
25
+ TestControl({ target, dir: 'test' })
26
+
23
27
  // The shared test infrastructure (RunnerSupport, FeatureHarness,
24
28
  // PipelineTest, FeatureTest, NetsimTest, PrimaryUtilityTest,
25
29
  // CustomUtilityTest, StructRunner/StructCorpusTest, ExistsTest and
@@ -17,6 +17,7 @@ import {
17
17
  isConfigData,
18
18
  resolveAuthPrefix,
19
19
  serverVariables,
20
+ targetFeatures,
20
21
  } from '@voxgig/sdkgen'
21
22
 
22
23
 
@@ -40,7 +41,10 @@ const Config = cmp(async function Config(props: any) {
40
41
  const model: Model = ctx$.model
41
42
 
42
43
  const entity = getModelPath(model, `main.${KIT}.entity`)
43
- const feature = getModelPath(model, `main.${KIT}.feature`)
44
+ // Gated by the applicability tags, so this target never imports or
45
+ // registers a feature it has no source for. One rule, one place:
46
+ // helpers/applicability.
47
+ const feature = targetFeatures(model, target)
44
48
 
45
49
  const ff = Path.normalize(__dirname + '/../../../src/cmp/js/fragment/')
46
50
 
@@ -5,6 +5,8 @@ import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  entityClassName, entityCollection, srcFeatureExcludes, stationLibrary,
8
+ targetFeatures,
9
+ TEST_CONTROL_EXCLUDE
8
10
  } from '@voxgig/sdkgen'
9
11
 
10
12
 
@@ -35,7 +37,10 @@ const Main = cmp(async function Main(props: any) {
35
37
  const { model } = props.ctx$
36
38
 
37
39
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
38
- const feature = getModelPath(model, `main.${KIT}.feature`)
40
+ // Gated by the applicability tags, so this target never imports or
41
+ // registers a feature it has no source for. One rule, one place:
42
+ // helpers/applicability.
43
+ const feature = targetFeatures(model, target)
39
44
 
40
45
  Package({ target })
41
46
 
@@ -45,7 +50,7 @@ const Main = cmp(async function Main(props: any) {
45
50
  from: 'tm/' + target.name,
46
51
  // Root copies src/feature/<name>/ per ACTIVE feature; keep this blanket
47
52
  // copy from restoring one that was switched off after `target add`.
48
- exclude: srcFeatureExcludes(model),
53
+ exclude: [...srcFeatureExcludes(model), TEST_CONTROL_EXCLUDE],
49
54
  replace: {
50
55
  ...props.ctx$.stdrep,
51
56
  }
@@ -13,6 +13,7 @@ import {
13
13
  PUBLISHER_URL,
14
14
  packageVersion,
15
15
  authorInfo,
16
+ targetFeatures,
16
17
  } from '@voxgig/sdkgen'
17
18
 
18
19
 
@@ -36,7 +37,9 @@ const Package = cmp(async function Package(props: any) {
36
37
  // constant here did.
37
38
  const author = authorInfo(model, target.name)
38
39
 
39
- const feature = getModelPath(model, `main.${KIT}.feature`)
40
+ // Gated by applicability: a feature that does not apply to this
41
+ // target must not inject its deps into the generated manifest.
42
+ const feature = targetFeatures(model, target)
40
43
 
41
44
  const only = (kind: string, deps: any) =>
42
45
  omap(deps, ([k, v]: any) => [v.active && kind === v.kind ? k : undefined, v.version])
@@ -17,7 +17,10 @@ import {
17
17
  cmp,
18
18
  snakify,
19
19
  isAuthActive,
20
- jsProp, envName, envToken
20
+ serverVarEnv,
21
+ serverVariables,
22
+ jsProp, envName, envToken,
23
+ jsKey
21
24
  } from '@voxgig/sdkgen'
22
25
 
23
26
 
@@ -41,13 +44,32 @@ const TestDirect = cmp(function TestDirect(props: any) {
41
44
 
42
45
  const authActive = isAuthActive(model)
43
46
  const apikeyEnvEntry = authActive
44
- ? `\n '${PROJECTNAME}_APIKEY': 'NONE',`
47
+ ? `\n '${PROJECTNAME}_APIKEY': '',`
45
48
  : ''
46
49
  const apikeyLiveField = authActive
47
50
  ? `
48
51
  apikey: env.${PROJECTNAME}_APIKEY,`
49
52
  : ''
50
53
 
54
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
55
+ // impossible to construct without values: makeOptions raises rather than
56
+ // request a URL with a literal `{account_id}` in it. Taken from the
57
+ // environment, the same way the apikey is.
58
+ //
59
+ // Keys are quoted and the env read is bracketed via jsKey/jsProp: a server
60
+ // variable name is spec-derived and need not be a JS identifier — the URL
61
+ // grammar admits a leading digit ({2fa}), and a declared-but-unreferenced
62
+ // variable ({edge-zone}) is not constrained at all. Bare `name:` and
63
+ // `env.PROJ_SERVER_EDGE-ZONE` are both syntax errors.
64
+ const svars = serverVariables(model)
65
+ const serverEnvEntry = svars
66
+ .map((v: any) => `\n '${serverVarEnv(PROJECTNAME, v.name)}': ${JSON.stringify(v.dflt)},`).join('')
67
+ const serverLiveField = 0 === svars.length ? '' : `
68
+ server: {${svars
69
+ .map((v: any) => `
70
+ ${jsKey(v.name)}: ${jsProp('env', serverVarEnv(PROJECTNAME, v.name))},`).join('')}
71
+ },`
72
+
51
73
  const opnames = Object.keys(entity.op || {})
52
74
  const hasLoad = opnames.includes('load')
53
75
  const hasList = opnames.includes('list')
@@ -78,14 +100,17 @@ function directSetup(mockres) {
78
100
 
79
101
  const env = envOverride({
80
102
  '${entidEnvVar}': {},
81
- '${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}
103
+ '${PROJECTNAME}_TEST_LIVE': 'FALSE',${apikeyEnvEntry}${serverEnvEntry}
82
104
  })
83
105
 
84
106
  const live = 'TRUE' === env.${PROJECTNAME}_TEST_LIVE
85
107
 
86
108
  if (live) {
87
- const client = new ${nom(model.const, 'Name')}SDK({${apikeyLiveField}
88
- })
109
+ // Merged so the generated fields win: sdk-test-control.json's
110
+ // test.client.options adds to the live client, it does not redirect it.
111
+ const client = new ${nom(model.const, 'Name')}SDK(
112
+ Object.assign({}, liveClientOptions(), {${apikeyLiveField}${serverLiveField}
113
+ }))
89
114
 
90
115
  let idmap = env['${entidEnvVar}']
91
116
  if ('string' === typeof idmap && idmap.startsWith('{')) {