@voxgig/sdkgen 4.5.3 → 4.6.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 (174) 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 +17 -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/helpers/applicability.d.ts +6 -0
  21. package/dist/helpers/applicability.js +103 -0
  22. package/dist/helpers/applicability.js.map +1 -0
  23. package/dist/helpers/collectDeps.js +4 -2
  24. package/dist/helpers/collectDeps.js.map +1 -1
  25. package/dist/helpers/serverVars.d.ts +2 -1
  26. package/dist/helpers/serverVars.js +17 -0
  27. package/dist/helpers/serverVars.js.map +1 -1
  28. package/dist/sdkgen.d.ts +3 -2
  29. package/dist/sdkgen.js +9 -2
  30. package/dist/sdkgen.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/utility.js +7 -1
  33. package/dist/utility.js.map +1 -1
  34. package/model/sdkgen.aon +39 -0
  35. package/package.json +1 -1
  36. package/project/.sdk/model/feature/feature-index.aon +1 -0
  37. package/project/.sdk/model/feature/secrets.aon +118 -0
  38. package/project/.sdk/model/target/ts.aon +8 -1
  39. package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
  40. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
  41. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
  42. package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
  43. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
  44. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
  45. package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
  46. package/project/.sdk/src/cmp/go/Main_go.ts +6 -2
  47. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
  48. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
  49. package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
  50. package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
  51. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
  52. package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
  53. package/project/.sdk/src/cmp/java/Main_java.ts +5 -1
  54. package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
  55. package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
  56. package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
  57. package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
  58. package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
  59. package/project/.sdk/src/cmp/js/Main_js.ts +5 -1
  60. package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
  61. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
  62. package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
  63. package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
  64. package/project/.sdk/src/cmp/lua/Main_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
  66. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
  67. package/project/.sdk/src/cmp/perl/Main_perl.ts +5 -1
  68. package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
  69. package/project/.sdk/src/cmp/php/Main_php.ts +5 -1
  70. package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
  71. package/project/.sdk/src/cmp/py/Main_py.ts +5 -1
  72. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
  73. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
  74. package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
  75. package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
  76. package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
  77. package/project/.sdk/src/cmp/rb/Main_rb.ts +5 -1
  78. package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
  79. package/project/.sdk/src/cmp/rust/Main_rust.ts +5 -1
  80. package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
  81. package/project/.sdk/src/cmp/scala/Main_scala.ts +5 -1
  82. package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
  83. package/project/.sdk/src/cmp/ts/Config_ts.ts +5 -1
  84. package/project/.sdk/src/cmp/ts/Main_ts.ts +57 -1
  85. package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
  86. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
  87. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
  88. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
  89. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +67 -7
  90. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +27 -4
  91. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
  92. package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
  93. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
  94. package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
  95. package/project/.sdk/tm/c/utility/make_options.c +22 -0
  96. package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
  97. package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
  98. package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
  99. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
  100. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
  101. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
  102. package/project/.sdk/tm/go/core/context.go +1 -1
  103. package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
  104. package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
  105. package/project/.sdk/tm/go/feature/test_feature.go +12 -5
  106. package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
  107. package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
  108. package/project/.sdk/tm/go/test/runner_test.go +46 -0
  109. package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
  110. package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
  111. package/project/.sdk/tm/go/utility/fetcher.go +2 -2
  112. package/project/.sdk/tm/go/utility/make_options.go +24 -5
  113. package/project/.sdk/tm/go/utility/make_point.go +1 -1
  114. package/project/.sdk/tm/go/utility/make_spec.go +17 -1
  115. package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
  116. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
  117. package/project/.sdk/tm/go/utility/transform_request.go +29 -1
  118. package/project/.sdk/tm/go/utility/transform_response.go +20 -1
  119. package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
  120. package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
  121. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
  122. package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
  123. package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
  124. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
  125. package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
  126. package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
  127. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
  128. package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
  129. package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
  130. package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
  131. package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
  132. package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
  133. package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
  134. package/project/.sdk/tm/py/test/runner.py +32 -0
  135. package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
  136. package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
  137. package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
  138. package/project/.sdk/tm/rust/utility/make_options.rs +25 -0
  139. package/project/.sdk/tm/scala/utility/Make.scala +17 -0
  140. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
  141. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
  142. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +1221 -0
  143. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +381 -0
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +151 -0
  145. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +42 -0
  146. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
  147. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
  148. package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
  149. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
  150. package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
  151. package/project/.sdk/tm/ts/test/omni.ts +86 -0
  152. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
  153. package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
  154. package/project/.sdk/tm/ts/test/utility.ts +122 -0
  155. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +692 -0
  156. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +238 -0
  157. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
  158. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
  159. package/project/.sdk/tm/zig/core/utility.zig +23 -0
  160. package/project/sdkgen-package.json +2 -1
  161. package/src/action/check.ts +18 -0
  162. package/src/cmp/AgentGuide.ts +1 -1
  163. package/src/cmp/AgentGuideContent.ts +9 -2
  164. package/src/cmp/Feature.ts +19 -0
  165. package/src/cmp/FeatureDocs.ts +7 -1
  166. package/src/cmp/ReadmeExplanation.ts +4 -1
  167. package/src/cmp/ReadmeFeatures.ts +1 -1
  168. package/src/cmp/ReadmeRefFeatures.ts +1 -1
  169. package/src/helpers/applicability.ts +123 -0
  170. package/src/helpers/collectDeps.ts +5 -1
  171. package/src/helpers/serverVars.ts +19 -0
  172. package/src/sdkgen.ts +9 -1
  173. package/src/utility.ts +10 -1
  174. package/project/.sdk/tm/ts/test/runner.ts +0 -425
@@ -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,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each,
6
6
  File, Content, Copy, Folder, Fragment,
7
+ targetFeatures,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -34,7 +35,10 @@ const Main = cmp(async function Main(props: any) {
34
35
  const { model } = props.ctx$
35
36
 
36
37
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
37
- 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)
38
42
 
39
43
  // The Java package root for every runtime piece (like GOMODULE for go):
40
44
  // e.g. voxgig.solardemosdk -> voxgig.solardemosdk.core etc.
@@ -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
 
@@ -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,7 @@ 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,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -35,7 +36,10 @@ const Main = cmp(async function Main(props: any) {
35
36
  const { model } = props.ctx$
36
37
 
37
38
  const entity: ModelEntity = 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
  Package({ target })
41
45
 
@@ -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])
@@ -7,6 +7,7 @@ import {
7
7
  each,
8
8
  isAuthActive,
9
9
  resolveAuthPrefix,
10
+ targetFeatures,
10
11
  } from '@voxgig/sdkgen'
11
12
 
12
13
 
@@ -36,7 +37,10 @@ const Config = cmp(async function Config(props: any) {
36
37
  const kotlinpackage = kotlinPackage(model)
37
38
 
38
39
  const entity = getModelPath(model, `main.${KIT}.entity`)
39
- 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)
40
44
 
41
45
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
42
46
 
@@ -5,6 +5,7 @@ import {
5
5
  each,
6
6
  isAuthActive,
7
7
  resolveAuthPrefix,
8
+ targetFeatures,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
  import {
@@ -29,7 +30,10 @@ const Config = cmp(async function Config(props: any) {
29
30
  const model: Model = ctx$.model
30
31
 
31
32
  const entity = getModelPath(model, `main.${KIT}.entity`)
32
- const feature = getModelPath(model, `main.${KIT}.feature`)
33
+ // Gated by the applicability tags, so this target never imports or
34
+ // registers a feature it has no source for. One rule, one place:
35
+ // helpers/applicability.
36
+ const feature = targetFeatures(model, target)
33
37
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
34
38
 
35
39
  const authActive = isAuthActive(model)
@@ -15,6 +15,7 @@ import {
15
15
  isConfigData,
16
16
  resolveAuthPrefix,
17
17
  serverVariables,
18
+ targetFeatures,
18
19
  } from '@voxgig/sdkgen'
19
20
 
20
21
 
@@ -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 headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
46
50
 
@@ -4,6 +4,7 @@ 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
+ targetFeatures,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -31,7 +32,10 @@ const Main = cmp(async function Main(props: any) {
31
32
  const { model } = props.ctx$
32
33
 
33
34
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
34
- const feature = getModelPath(model, `main.${KIT}.feature`)
35
+ // Gated by the applicability tags, so this target never imports or
36
+ // registers a feature it has no source for. One rule, one place:
37
+ // helpers/applicability.
38
+ const feature = targetFeatures(model, target)
35
39
 
36
40
  Package({ target })
37
41
 
@@ -9,6 +9,7 @@ import {
9
9
  keywords,
10
10
  repoInfo, packageName,
11
11
  packageVersion,
12
+ targetFeatures,
12
13
  } from '@voxgig/sdkgen'
13
14
 
14
15
 
@@ -78,7 +79,9 @@ dependencies = {
78
79
  // factory falls back to. The station feature additionally carries the
79
80
  // VENDORED voxgig_station library beside its adapter (no voxgig-station
80
81
  // rock exists to depend on - station design 9.2's registry-less tier).
81
- const feature = getModelPath(model, `main.${KIT}.feature`)
82
+ // Gated: a feature that does not apply to lua must not be listed
83
+ // as a rockspec module — the require would not resolve.
84
+ const feature = targetFeatures(model, target)
82
85
  let featureModules = ` ["feature.base_feature"] = "feature/base_feature.lua",\n`
83
86
  each(feature, (f: any) => {
84
87
  featureModules +=
@@ -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
 
@@ -34,7 +35,10 @@ const Config = cmp(async function Config(props: any) {
34
35
 
35
36
  const model: Model = ctx$.model
36
37
 
37
- 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)
38
42
 
39
43
  // The canonical config OBJECT and its JSON, from the shared helper. Both
40
44
  // representations render from the same `def`, so they cannot describe
@@ -4,6 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each, names,
6
6
  File, Content, Copy, Folder, Fragment,
7
+ targetFeatures,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -36,7 +37,10 @@ const Main = cmp(async function Main(props: any) {
36
37
  const { model } = props.ctx$
37
38
 
38
39
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
39
- 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)
40
44
 
41
45
  Package({ target })
42
46
 
@@ -16,6 +16,7 @@ import {
16
16
  rawStringLiteral,
17
17
  resolveAuthPrefix,
18
18
  serverVariables,
19
+ targetFeatures,
19
20
  } from '@voxgig/sdkgen'
20
21
 
21
22
 
@@ -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 headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
46
50
 
@@ -4,6 +4,7 @@ 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
+ targetFeatures,
7
8
  } from '@voxgig/sdkgen'
8
9
 
9
10
 
@@ -31,7 +32,10 @@ const Main = cmp(async function Main(props: any) {
31
32
  const { model } = props.ctx$
32
33
 
33
34
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
34
- const feature = getModelPath(model, `main.${KIT}.feature`)
35
+ // Gated by the applicability tags, so this target never imports or
36
+ // registers a feature it has no source for. One rule, one place:
37
+ // helpers/applicability.
38
+ const feature = targetFeatures(model, target)
35
39
 
36
40
  Package({ target })
37
41
 
@@ -15,6 +15,7 @@ import {
15
15
  isConfigData,
16
16
  resolveAuthPrefix,
17
17
  serverVariables,
18
+ targetFeatures,
18
19
  } from '@voxgig/sdkgen'
19
20
 
20
21
 
@@ -39,7 +40,10 @@ const Config = cmp(async function Config(props: any) {
39
40
  const model: Model = ctx$.model
40
41
 
41
42
  const entity = getModelPath(model, `main.${KIT}.entity`)
42
- const feature = getModelPath(model, `main.${KIT}.feature`)
43
+ // Gated by the applicability tags, so this target never imports or
44
+ // registers a feature it has no source for. One rule, one place:
45
+ // helpers/applicability.
46
+ const feature = targetFeatures(model, target)
43
47
 
44
48
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
45
49
 
@@ -5,6 +5,7 @@ import {
5
5
  cmp, each, names, cmap,
6
6
  List, File, Content, Copy, Folder, Fragment, Line, FeatureHook,
7
7
  entityClassName, entityCollection,
8
+ targetFeatures,
8
9
  } from '@voxgig/sdkgen'
9
10
 
10
11
 
@@ -32,7 +33,10 @@ const Main = cmp(async function Main(props: any) {
32
33
  const { model } = props.ctx$
33
34
 
34
35
  const entity: ModelEntity = getModelPath(model, `main.${KIT}.entity`)
35
- const feature = getModelPath(model, `main.${KIT}.feature`)
36
+ // Gated by the applicability tags, so this target never imports or
37
+ // registers a feature it has no source for. One rule, one place:
38
+ // helpers/applicability.
39
+ const feature = targetFeatures(model, target)
36
40
 
37
41
  // The one package directory everything the SDK owns lives in.
38
42
  const pkgdir = model.const.Name.toLowerCase() + '_sdk'
@@ -1,5 +1,5 @@
1
1
 
2
- import { cmp, each, Content, isAuthActive, envName, canonKey, canonScalarKey, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName, exampleVarName, matchArg, idLiteral } from '@voxgig/sdkgen'
2
+ import { cmp, each, Content, isAuthActive, envName, canonKey, canonScalarKey, opRequestShape, entityIdField, entityDataIdField, entityOps, safeVarName, exampleVarName, matchArg, idLiteral , serverVariables} from '@voxgig/sdkgen'
3
3
 
4
4
  import {
5
5
  KIT,
@@ -38,10 +38,23 @@ const ReadmeQuick = cmp(function ReadmeQuick(props: any) {
38
38
  ) as any
39
39
 
40
40
  const authActive = isAuthActive(model)
41
+
42
+ // Server variables (a templated server URL) are REQUIRED at construction
43
+ // - the SDK refuses rather than request a URL with a literal
44
+ // {account_id} in it - so a quickstart that omits them is a quickstart
45
+ // that fails on its first line.
46
+ const svars = serverVariables(model)
47
+ const pyServerField = 0 === svars.length ? '' :
48
+ `\n "server": {` +
49
+ svars.map((v: any) => `\n "${v.name}": "<${v.name}>",`).join('') +
50
+ `\n },`
51
+
41
52
  const apikeyImport = authActive ? `import os\n` : ''
42
53
  const ctor = authActive
43
- ? `${model.const.Name}SDK({\n "apikey": os.environ.get("${envName(model)}_APIKEY"),\n})`
44
- : `${model.const.Name}SDK()`
54
+ ? `${model.const.Name}SDK({\n "apikey": os.environ.get("${envName(model)}_APIKEY"),${pyServerField}\n})`
55
+ : ('' === pyServerField
56
+ ? `${model.const.Name}SDK()`
57
+ : `${model.const.Name}SDK({${pyServerField}\n})`)
45
58
 
46
59
  Content(`### 1. Create a client
47
60