@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
@@ -9,6 +9,7 @@ import {
9
9
  isAuthActive,
10
10
  isConfigData,
11
11
  resolveAuthPrefix,
12
+ targetFeatures,
12
13
  } from '@voxgig/sdkgen'
13
14
 
14
15
 
@@ -35,7 +36,10 @@ const Config = cmp(async function Config(props: any) {
35
36
  const model: Model = ctx$.model
36
37
 
37
38
  const entity = getModelPath(model, `main.${KIT}.entity`)
38
- 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)
39
43
 
40
44
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
41
45
 
@@ -5,6 +5,8 @@ import {
5
5
  cmp, each,
6
6
  File, Content, Copy, Folder, Fragment,
7
7
  entityClassName,
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
  // The rust crate identifier (RUSTCRATE placeholder), e.g. solar_sdk —
41
46
  // used in every `use <crate>::...` path in the test templates.
@@ -50,7 +55,7 @@ const Main = cmp(async function Main(props: any) {
50
55
  // here exactly like the go target.
51
56
  Copy({
52
57
  from: 'tm/' + target.name,
53
- exclude: [/src\//],
58
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
54
59
  replace: {
55
60
  ...props.ctx$.stdrep,
56
61
  RUSTCRATE: rustcrate,
@@ -11,7 +11,9 @@ import {
11
11
  File,
12
12
  cmp,
13
13
  snakify,
14
- isAuthActive, envName, envToken
14
+ isAuthActive, envName, envToken,
15
+ serverVarEnv,
16
+ serverVariables
15
17
  } from '@voxgig/sdkgen'
16
18
 
17
19
 
@@ -67,12 +69,28 @@ const TestDirect = cmp(function TestDirect(props: any) {
67
69
 
68
70
  const authActive = isAuthActive(model)
69
71
  const apikeyEnvEntry = authActive
70
- ? `\n ("${PROJECTNAME}_APIKEY", Value::str("NONE")),`
72
+ ? `\n ("${PROJECTNAME}_APIKEY", Value::str("")),`
71
73
  : ''
72
74
  const apikeyLiveField = authActive
73
75
  ? `("apikey", getp(&env, "${PROJECTNAME}_APIKEY"))`
74
76
  : ''
75
77
 
78
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
79
+ // impossible to construct without values: makeOptions raises rather than
80
+ // request a URL with a literal `{account_id}` in it. So the live suite
81
+ // takes them from the environment the same way it takes the apikey.
82
+ //
83
+ // Emitted as a jo() tuple entry, so it carries its own leading comma only
84
+ // when an apikey entry precedes it - otherwise `jo(vec![, ...])`.
85
+ const svars = serverVariables(model)
86
+ const serverEnvEntry = svars
87
+ .map((v: any) => `\n ("${serverVarEnv(PROJECTNAME, v.name)}", Value::str(${JSON.stringify(v.dflt)})),`).join('')
88
+ const serverLiveEntry = 0 === svars.length ? '' :
89
+ `("server", jo(vec![${svars
90
+ .map((v: any) => `("${v.name}", getp(&env, "${serverVarEnv(PROJECTNAME, v.name)}"))`).join(', ')}]))`
91
+ const serverLiveField = '' === serverLiveEntry ? '' :
92
+ ('' === apikeyLiveField ? serverLiveEntry : ', ' + serverLiveEntry)
93
+
76
94
  const opnames = Object.keys(entity.op || {})
77
95
  const hasLoad = opnames.includes('load')
78
96
  const hasList = opnames.includes('list')
@@ -148,13 +166,22 @@ fn ${evar}_direct_setup(mockres: Value) -> ${entity.Name}DirectSetup {
148
166
 
149
167
  let env = env_override(jo(vec![
150
168
  ("${entidEnvVar}", Value::empty_map()),
151
- ("${PROJECTNAME}_TEST_LIVE", Value::str("FALSE")),${apikeyEnvEntry}
169
+ ("${PROJECTNAME}_TEST_LIVE", Value::str("FALSE")),${apikeyEnvEntry}${serverEnvEntry}
152
170
  ]));
153
171
 
154
172
  let live = getp(&env, "${PROJECTNAME}_TEST_LIVE") == Value::str("TRUE");
155
173
 
156
174
  if live {
157
- let client = ${model.const.Name}SDK::new(jo(vec![${apikeyLiveField}]));
175
+ // live_client_options() FIRST, so the generated entries below win:
176
+ // sdk-test-control.json's test.client.options adds to the live
177
+ // client, it does not redirect it.
178
+ let client = ${model.const.Name}SDK::new(to_map(&vs::merge(
179
+ &ja(vec![
180
+ live_client_options(),
181
+ jo(vec![${apikeyLiveField}${serverLiveField}]),
182
+ ]),
183
+ None,
184
+ )));
158
185
  let idmap = match to_map(&getp(&env, "${entidEnvVar}")) {
159
186
  Value::Map(m) => Value::Map(m),
160
187
  _ => Value::empty_map(),
@@ -17,7 +17,9 @@ import {
17
17
  each,
18
18
  buildIdNames,
19
19
  getMatchEntries,
20
- isAuthActive, envName, envToken
20
+ isAuthActive, envName, envToken,
21
+ serverVarEnv,
22
+ serverVariables
21
23
  } from '@voxgig/sdkgen'
22
24
 
23
25
 
@@ -56,12 +58,28 @@ const TestEntity = cmp(function TestEntity(props: any) {
56
58
 
57
59
  const authActive = isAuthActive(model)
58
60
  const apikeyEnvEntry = authActive
59
- ? `\n ("${PROJUPPER}_APIKEY", Value::str("NONE")),`
61
+ ? `\n ("${PROJUPPER}_APIKEY", Value::str("")),`
60
62
  : ''
61
63
  const apikeyLiveField = authActive
62
64
  ? `("apikey", getp(&env, "${PROJUPPER}_APIKEY"))`
63
65
  : ''
64
66
 
67
+ // A templated server URL (OpenAPI server variables) makes a LIVE client
68
+ // impossible to construct without values: makeOptions raises rather than
69
+ // request a URL with a literal `{account_id}` in it. So the live suite
70
+ // takes them from the environment the same way it takes the apikey.
71
+ //
72
+ // Emitted as a jo() tuple entry, so it carries its own leading comma only
73
+ // when an apikey entry precedes it - otherwise `jo(vec![, ...])`.
74
+ const svars = serverVariables(model)
75
+ const serverEnvEntry = svars
76
+ .map((v: any) => `\n ("${serverVarEnv(PROJUPPER, v.name)}", Value::str(${JSON.stringify(v.dflt)})),`).join('')
77
+ const serverLiveEntry = 0 === svars.length ? '' :
78
+ `("server", jo(vec![${svars
79
+ .map((v: any) => `("${v.name}", getp(&env, "${serverVarEnv(PROJUPPER, v.name)}"))`).join(', ')}]))`
80
+ const serverLiveField = '' === serverLiveEntry ? '' :
81
+ ('' === apikeyLiveField ? serverLiveEntry : ', ' + serverLiveEntry)
82
+
65
83
  const idnames = buildIdNames(entity, basicflow)
66
84
  const idnamesStr = idnames.map(n => `Value::str("${n}")`).join(', ')
67
85
 
@@ -268,7 +286,7 @@ ${allSteps.length > 0 ? ' let client = setup.client.clone();\n' : ''}`)
268
286
  let env = env_override(jo(vec![
269
287
  ("${PROJUPPER}_TEST_${ENTUPPER}_ENTID", idmap.clone()),
270
288
  ("${PROJUPPER}_TEST_LIVE", Value::str("FALSE")),
271
- ("${PROJUPPER}_TEST_EXPLAIN", Value::str("FALSE")),${apikeyEnvEntry}
289
+ ("${PROJUPPER}_TEST_EXPLAIN", Value::str("FALSE")),${apikeyEnvEntry}${serverEnvEntry}
272
290
  ]));
273
291
 
274
292
  let idmap_resolved = match to_map(&getp(&env, "${PROJUPPER}_TEST_${ENTUPPER}_ENTID")) {
@@ -293,7 +311,22 @@ ${allSteps.length > 0 ? ' let client = setup.client.clone();\n' : ''}`)
293
311
 
294
312
  let client = if live {
295
313
  let merged = vs::merge(
296
- &ja(vec![jo(vec![${apikeyLiveField}]), extra]),
314
+ // live_client_options() FIRST, so the generated entries below win:
315
+ // sdk-test-control.json's test.client.options adds to the live
316
+ // client, it does not redirect it.
317
+ &ja(vec![
318
+ live_client_options(),
319
+ jo(vec![${apikeyLiveField}${serverLiveField}]),
320
+ // A NON-NODE later entry REPLACES the accumulated map in
321
+ // vs::merge, and the normal call passes Value::Noval - so a
322
+ // a bare extra discarded live_client_options() and the
323
+ // apikey/server map above it, and the live client was
324
+ // constructed with nothing.
325
+ match extra {
326
+ Value::Map(m) => Value::Map(m),
327
+ _ => Value::empty_map(),
328
+ },
329
+ ]),
297
330
  None,
298
331
  );
299
332
  ${model.const.Name}SDK::new(to_map(&merged))
@@ -3,7 +3,8 @@ import type {
3
3
  ModelEntity
4
4
  } from '@voxgig/apidef'
5
5
 
6
- import { cmp, each, Folder, File, Content, entityCollection } from '@voxgig/sdkgen'
6
+ import { cmp, each, Folder, File, Content, entityCollection,
7
+ TestControl } from '@voxgig/sdkgen'
7
8
 
8
9
 
9
10
  import { TestEntity } from './TestEntity_rust'
@@ -20,6 +21,9 @@ const Test = cmp(function Test(props: any) {
20
21
 
21
22
  Folder({ name: 'tests' }, () => {
22
23
 
24
+ // Write-once: a project's edited control file survives regeneration.
25
+ TestControl({ target, dir: 'tests' })
26
+
23
27
  // exists test — the SDK constructs in test mode.
24
28
  File({ name: 'exists_test.' + target.ext }, () => {
25
29
  Content(`// Generated existence test: the SDK constructs in test mode.
@@ -6,6 +6,7 @@ import {
6
6
  each,
7
7
  isAuthActive,
8
8
  resolveAuthPrefix,
9
+ targetFeatures,
9
10
  } from '@voxgig/sdkgen'
10
11
 
11
12
 
@@ -35,7 +36,10 @@ const Config = cmp(async function Config(props: any) {
35
36
  const scalapackage = scalaPackage(model)
36
37
 
37
38
  const entity = getModelPath(model, `main.${KIT}.entity`)
38
- 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)
39
43
 
40
44
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
41
45
 
@@ -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
 
@@ -33,7 +35,10 @@ const Main = cmp(async function Main(props: any) {
33
35
  const { model } = props.ctx$
34
36
 
35
37
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
36
- const feature = getModelPath(model, `main.${KIT}.feature`)
38
+ // Gated by the applicability tags, so this target never imports or
39
+ // registers a feature it has no source for. One rule, one place:
40
+ // helpers/applicability.
41
+ const feature = targetFeatures(model, target)
37
42
 
38
43
  // The Scala package root for every runtime piece (like GOMODULE for go).
39
44
  const scalapackage = scalaPackage(model)
@@ -47,7 +52,7 @@ const Main = cmp(async function Main(props: any) {
47
52
  // ProjectName carries the SDK name into vendored template strings.
48
53
  Copy({
49
54
  from: 'tm/' + target.name,
50
- exclude: [/src\//],
55
+ exclude: [/src\//, TEST_CONTROL_EXCLUDE],
51
56
  replace: {
52
57
  ...props.ctx$.stdrep,
53
58
  ProjectName: model.const.Name,
@@ -9,7 +9,8 @@ import type {
9
9
  ModelEntity
10
10
  } from '@voxgig/apidef'
11
11
 
12
- import { cmp, each, Folder, File, Content, entityCollection } from '@voxgig/sdkgen'
12
+ import { cmp, each, Folder, File, Content, entityCollection,
13
+ TestControl } from '@voxgig/sdkgen'
13
14
 
14
15
 
15
16
  import { TestEntity } from './TestEntity_scala'
@@ -31,6 +32,9 @@ const Test = cmp(function Test(props: any) {
31
32
  // template, and are driven by the generated SdkEntityTestMain aggregator.
32
33
  Folder({ name: 'sdktest' }, () => {
33
34
 
35
+ // Write-once: a project's edited control file survives regeneration.
36
+ TestControl({ target, dir: 'sdktest' })
37
+
34
38
  const entities: { Name: string; entity: boolean; direct: boolean }[] = []
35
39
 
36
40
  const entity = each(entityCollection(model))
@@ -5,6 +5,7 @@ import {
5
5
  cmp,
6
6
  configDefinition,
7
7
  each,
8
+ targetFeatures,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -26,7 +27,10 @@ const Config = cmp(async function Config(props: any) {
26
27
 
27
28
  const model: Model = ctx$.model
28
29
 
29
- const feature = getModelPath(model, `main.${KIT}.feature`)
30
+ // Gated by the applicability tags, so this target never imports or
31
+ // registers a feature it has no source for. One rule, one place:
32
+ // helpers/applicability.
33
+ const feature = targetFeatures(model, target)
30
34
 
31
35
  // The config as data, built by the shared helper so every target embeds
32
36
  // the same model by construction. Passing target.name opts this target
@@ -18,6 +18,7 @@ import {
18
18
  isHttpBasicAuth,
19
19
  resolveAuthPrefix,
20
20
  serverVariables,
21
+ targetFeatures,
21
22
  } from '@voxgig/sdkgen'
22
23
 
23
24
 
@@ -41,7 +42,10 @@ const Config = cmp(async function Config(props: any) {
41
42
  const model: Model = ctx$.model
42
43
 
43
44
  const entity = getModelPath(model, `main.${KIT}.entity`)
44
- const feature = getModelPath(model, `main.${KIT}.feature`)
45
+ // Gated by the applicability tags, so this target never imports or
46
+ // registers a feature it has no source for. One rule, one place:
47
+ // helpers/applicability.
48
+ const feature = targetFeatures(model, target)
45
49
 
46
50
  const ff = Path.normalize(__dirname + '/../../../src/cmp/ts/fragment/')
47
51
 
@@ -95,10 +99,13 @@ const Config = cmp(async function Config(props: any) {
95
99
 
96
100
  replace: {
97
101
 
98
- '// #ImportFeatures': () => each(feature, (f: any) => {
99
- Line(`import { ${nom(f, 'Name')}Feature } from ` +
100
- `'./feature/${f.name}/${nom(f, 'Name')}Feature'`)
101
- }),
102
+ '// #ImportFeatures': () => {
103
+ each(feature, (f: any) => {
104
+ Line(`import { ${nom(f, 'Name')}Feature } from ` +
105
+ `'./feature/${f.name}/${nom(f, 'Name')}Feature'`)
106
+ })
107
+ pluginImports(feature)
108
+ },
102
109
 
103
110
  '// #FeatureClasses': () => each(feature, (f: any) => {
104
111
  Line(` ${f.name}: ${nom(f, 'Name')}Feature,`)
@@ -126,10 +133,13 @@ const Config = cmp(async function Config(props: any) {
126
133
 
127
134
  "'HEADERS'": indent(JSON.stringify(headers, null, 2), 4).trim(),
128
135
 
129
- '// #ImportFeatures': () => each(feature, (f: any) => {
130
- Line(`import { ${nom(f, 'Name')}Feature } from ` +
131
- `'./feature/${f.name}/${nom(f, 'Name')}Feature'`)
132
- }),
136
+ '// #ImportFeatures': () => {
137
+ each(feature, (f: any) => {
138
+ Line(`import { ${nom(f, 'Name')}Feature } from ` +
139
+ `'./feature/${f.name}/${nom(f, 'Name')}Feature'`)
140
+ })
141
+ pluginImports(feature)
142
+ },
133
143
 
134
144
  // Values from configDefinition's def, not re-derived here, so the
135
145
  // literal rep and the data rep cannot disagree on identity.
@@ -173,6 +183,42 @@ const Config = cmp(async function Config(props: any) {
173
183
  })
174
184
 
175
185
 
186
+
187
+ // PLUGIN REGISTRATION IMPORTS.
188
+ //
189
+ // A sekreto provider module registers itself when it is imported, and
190
+ // nothing else imports it: the core surface deliberately does not reach a
191
+ // platform-dependent provider, and the full-set barrel — the file whose
192
+ // job was to reach all of them — is not vendored into an SDK at all.
193
+ //
194
+ // So without these lines an SDK generates `provider/dotenv.ts`, never
195
+ // imports it, and throws `unknown provider kind: dotenv` the first time a
196
+ // chain names it. Emitted here because Config already imports every active
197
+ // feature from the model, and this is the same list one level down.
198
+ //
199
+ // Import for SIDE EFFECT only — `import '...'`, no bindings. The module's
200
+ // top-level `register()` call is the whole point.
201
+ function pluginImports(feature: any) {
202
+ each(feature, (f: any) => {
203
+ each(f.plugin, (plugin: any) => {
204
+ // Filter on `active` HERE rather than trusting the feature object to
205
+ // arrive filtered. Whether a model path was read with `only_active`
206
+ // varies by call site, and getting it wrong in this direction emits
207
+ // an import for a module the trim just deleted — an SDK that does
208
+ // not compile, rather than one that merely carries too much.
209
+ if (false === plugin.active || null == plugin.active) return
210
+
211
+ for (const one of (plugin.path || [])) {
212
+ const path = String(one)
213
+ // Only a provider MODULE registers a kind; a plugin may own other
214
+ // files (Sigv4.ts is signing support, not a provider).
215
+ if (!/\/provider\/[^/]+\.ts$/.test(path)) continue
216
+ Line(`import '${'./' + path.replace(/^src\//, '').replace(/\.ts$/, '')}'`)
217
+ }
218
+ })
219
+ })
220
+ }
221
+
176
222
  export {
177
223
  Config
178
224
  }
@@ -4,7 +4,10 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
- entityClassName, entityCollection, srcFeatureExcludes, stationLibrary,
7
+ entityClassName, entityCollection, srcFeatureExcludes, pluginExcludes,
8
+ stationLibrary,
9
+ targetFeatures,
10
+ TEST_CONTROL_EXCLUDE
8
11
  } from '@voxgig/sdkgen'
9
12
 
10
13
 
@@ -35,7 +38,14 @@ const Main = cmp(async function Main(props: any) {
35
38
  const { model } = props.ctx$
36
39
 
37
40
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
38
- const feature = getModelPath(model, `main.${KIT}.feature`)
41
+ // Gated by the applicability tags, so this target never imports or
42
+ // registers a feature it has no source for. One rule, one place:
43
+ // helpers/applicability.
44
+ const feature = targetFeatures(model, target)
45
+
46
+ // Does the secrets feature apply here and is it switched on? Both, since
47
+ // targetFeatures already dropped it for a target with no sekreto port.
48
+ const secrets = null != feature.secrets
39
49
 
40
50
  Package({ target })
41
51
 
@@ -45,7 +55,13 @@ const Main = cmp(async function Main(props: any) {
45
55
  from: 'tm/' + target.name,
46
56
  // Root copies src/feature/<name>/ per ACTIVE feature; keep this blanket
47
57
  // copy from restoring one that was switched off after `target add`.
48
- exclude: srcFeatureExcludes(model),
58
+ // A feature's inactive plugins go too - same rule, one level
59
+ // deeper. See helpers/featureSource.pluginExcludes.
60
+ exclude: [
61
+ ...srcFeatureExcludes(model),
62
+ ...pluginExcludes(model),
63
+ TEST_CONTROL_EXCLUDE,
64
+ ],
49
65
  replace: {
50
66
  ...props.ctx$.stdrep,
51
67
  }
@@ -74,6 +90,54 @@ const Main = cmp(async function Main(props: any) {
74
90
  replace: {
75
91
  ...props.ctx$.stdrep,
76
92
 
93
+ // SECRETS. All five slots are emitted only when the secrets
94
+ // feature applies to this target AND the model activates it.
95
+ // An unconditional edit here would land in every generated SDK
96
+ // and break the inactive-output gate: a model without the
97
+ // feature must generate byte-identically to pre-migration.
98
+ //
99
+ // `feature` is already gated by targetFeatures, so a target
100
+ // that does not provide 'sekreto' never reaches these.
101
+ '// #SecretsImport': () => secrets ?
102
+ Line(`import * as sekreto from './feature/secrets/sekreto'`) : undefined,
103
+
104
+ '// #SecretsField': ({ indent }: any) => secrets ?
105
+ Line({ indent }, '_secrets?: any') : undefined,
106
+
107
+ // The LIVE instance, not a clone: sekreto holds provider and
108
+ // cache state, so a clone would resolve into a copy that
109
+ // prepareAuth never sees.
110
+ '// #SecretsAccessor': ({ indent }: any) => secrets ?
111
+ Content({ indent }, `
112
+ secrets() {
113
+ return this._secrets && this._secrets.sekreto()
114
+ }
115
+ `) : undefined,
116
+
117
+ // prepare() bypasses the feature hook pipeline, so the PreSpec
118
+ // hook that resolves the secret for entity ops never runs on
119
+ // this path and the resolve has to be explicit.
120
+ //
121
+ // It RETURNS the Error rather than rejecting: _rawRequest
122
+ // awaits prepare() outside its try, and direct()/graphql() are
123
+ // documented to return a value or an Error, never reject. The
124
+ // prototype rejected here, which would have turned a broken
125
+ // vault into an unhandled rejection on the direct path.
126
+ '// #SecretsResolve': ({ indent }: any) => secrets ?
127
+ Content({ indent }, `
128
+ if (null != this._secrets) {
129
+ try {
130
+ await this._secrets.resolve()
131
+ }
132
+ catch (err: any) {
133
+ return err instanceof Error ? err : new Error(String(err))
134
+ }
135
+ }
136
+ `) : undefined,
137
+
138
+ '// #SecretsExport': ({ indent }: any) => secrets ?
139
+ Line({ indent }, 'sekreto,') : undefined,
140
+
77
141
  '#BuildFeatures': ({ indent }: any) => {
78
142
  List({ item: feature, line: false }, ({ item }: any) =>
79
143
  Line({ indent },
@@ -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])
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, isAuthActive, isHttpBasicAuth } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive, isHttpBasicAuth, serverVariables } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -61,12 +61,24 @@ const ReadmeModel = cmp(function ReadmeModel(props: any) {
61
61
  ? '| `secret` | `string` | API secret for authentication. |\n'
62
62
  : ''
63
63
 
64
+ // Server variables are not one option among many: without them the
65
+ // constructor THROWS, because the base URL is a template. Documented
66
+ // first for that reason, and named individually so a reader knows what
67
+ // to supply without going back to the spec.
68
+ const svars = serverVariables(model)
69
+ const serverOptionType = 0 === svars.length ? '' :
70
+ `\n server?: { ${svars.map((v: any) => `${v.name}: string`).join(', ')} }`
71
+ const serverOptionRow = 0 === svars.length ? '' :
72
+ '| `server` | `object` | **Required.** Values for the server-URL variables: ' +
73
+ svars.map((v: any) => '`' + v.name + '`').join(', ') +
74
+ '. The API base URL is a template over them. |\n'
75
+
64
76
  Content(`### ${model.const.Name}SDK
65
77
 
66
78
  #### Constructor
67
79
 
68
80
  \`\`\`ts
69
- new ${model.const.Name}SDK(options?: {${apikeyOptionType}${secretOptionType}
81
+ new ${model.const.Name}SDK(options?: {${apikeyOptionType}${secretOptionType}${serverOptionType}
70
82
  base?: string
71
83
  prefix?: string
72
84
  suffix?: string
@@ -77,7 +89,7 @@ new ${model.const.Name}SDK(options?: {${apikeyOptionType}${secretOptionType}
77
89
 
78
90
  | Option | Type | Description |
79
91
  | --- | --- | --- |
80
- ${apikeyOptionRow}${secretOptionRow}| \`base\` | \`string\` | Base URL of the API server. |
92
+ ${serverOptionRow}${apikeyOptionRow}${secretOptionRow}| \`base\` | \`string\` | Base URL of the API server. |
81
93
  | \`prefix\` | \`string\` | URL path prefix prepended to all requests. |
82
94
  | \`suffix\` | \`string\` | URL path suffix appended to all requests. |
83
95
  | \`feature\` | \`object\` | Feature activation flags (e.g. \`{ test: { active: true } }\`). |
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, isAuthActive, isHttpBasicAuth, packageName, envName, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName, exampleVarName, jsKey, matchArg, idLiteral } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive, isHttpBasicAuth, packageName, envName, serverVariables, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName, exampleVarName, jsKey, matchArg, idLiteral } from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -40,10 +40,23 @@ const ReadmeQuick = cmp(function ReadmeQuick(props: any) {
40
40
  !it.optional && it.name !== entityIdField(e))
41
41
  ) as any
42
42
 
43
- const ctor = isAuthActive(model)
44
- ? `new ${model.const.Name}SDK({\n apikey: process.env.${envName(model)}_APIKEY,${
45
- isHttpBasicAuth(model) ? `\n secret: process.env.${envName(model)}_SECRET,` : ''}\n})`
46
- : `new ${model.const.Name}SDK()`
43
+ // Server variables (a templated server URL) are REQUIRED at construction
44
+ // makeOptions refuses rather than request a URL with a literal
45
+ // `{account_id}` in it — so a quickstart that omits them is a quickstart
46
+ // that throws on its first line.
47
+ const svarLines = serverVariables(model)
48
+ .map((v: any) => `\n ${v.name}: '<${v.name}>',`).join('')
49
+ const serverField = '' === svarLines ? '' :
50
+ `\n // Required: this API's server URL is templated on these.\n server: {${svarLines}\n },`
51
+
52
+ const ctorFields = (isAuthActive(model)
53
+ ? `\n apikey: process.env.${envName(model)}_APIKEY,${
54
+ isHttpBasicAuth(model) ? `\n secret: process.env.${envName(model)}_SECRET,` : ''}`
55
+ : '') + serverField
56
+
57
+ const ctor = '' === ctorFields
58
+ ? `new ${model.const.Name}SDK()`
59
+ : `new ${model.const.Name}SDK({${ctorFields}\n})`
47
60
 
48
61
  Content(`### 1. Create a client
49
62
 
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, Content, isAuthActive, isHttpBasicAuth, packageName, envName, entityIdField, entityOps, opRequestShape, safeVarName, exampleVarName, jsKey, matchArg, idLiteral } from '@voxgig/sdkgen'
2
+ import { cmp, Content, isAuthActive, isHttpBasicAuth, packageName, envName, entityIdField, entityOps, opRequestShape, safeVarName, exampleVarName, jsKey, matchArg, idLiteral , serverVariables} from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -28,10 +28,24 @@ const ReadmeTopQuick = cmp(function ReadmeTopQuick(props: any) {
28
28
  const exampleEntity = Object.values(entity).find((e: any) => e.active !== false) as any
29
29
 
30
30
  const authActive = isAuthActive(model)
31
- const ctor = authActive
32
- ? `new ${model.const.Name}SDK({\n apikey: process.env.${envName(model)}_APIKEY,${
33
- isHttpBasicAuth(model) ? `\n secret: process.env.${envName(model)}_SECRET,` : ''}\n})`
34
- : `new ${model.const.Name}SDK()`
31
+
32
+ // Server variables (a templated server URL) are REQUIRED at construction
33
+ // - makeOptions refuses rather than request a URL with a literal
34
+ // {account_id} in it - so a quickstart that omits them is a quickstart
35
+ // that throws on its first line.
36
+ const svarLines = serverVariables(model)
37
+ .map((v: any) => `\n ${v.name}: '<${v.name}>',`).join('')
38
+ const serverField = '' === svarLines ? '' :
39
+ `\n // Required: this API's server URL is templated on these.\n server: {${svarLines}\n },`
40
+
41
+ const ctorFields = (authActive
42
+ ? `\n apikey: process.env.${envName(model)}_APIKEY,${
43
+ isHttpBasicAuth(model) ? `\n secret: process.env.${envName(model)}_SECRET,` : ''}`
44
+ : '') + serverField
45
+
46
+ const ctor = '' === ctorFields
47
+ ? `new ${model.const.Name}SDK()`
48
+ : `new ${model.const.Name}SDK({${ctorFields}\n})`
35
49
 
36
50
  Content(`\`\`\`ts
37
51
  import { ${model.const.Name}SDK } from '${packageName(model, target.name)}'