@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
@@ -0,0 +1,123 @@
1
+ // Which features a target can actually take.
2
+ //
3
+ // THE ONE PLACE THIS RULE LIVES. `Feature` (what source is copied), the
4
+ // per-language `Config_<lang>` and `Main_<lang>` components (what the
5
+ // generated registry, imports and metadata contain) and `configDefinition`
6
+ // (the embedded config) must all reach the same answer for one
7
+ // (feature, target) pair. They previously each read the raw feature map,
8
+ // which is how a feature with no source for a target still reached that
9
+ // target's imports.
10
+ //
11
+ // The shape is `docs/design/feature-tags.md`: a feature declares what it
12
+ // NEEDS, a target declares what it PROVIDES, and the feature applies when
13
+ // `needs` is a subset of `provides`. Both default to empty, so a feature
14
+ // that needs nothing applies everywhere — every feature that existed
15
+ // before this gate keeps its behaviour with no model edit, which is what
16
+ // makes the change additive.
17
+ //
18
+ // A named target list was the cheaper design and fails on time: it has to
19
+ // be edited for targets the feature's author has never heard of, so a
20
+ // published package starves every target added after its release. A tag is
21
+ // a claim about the feature that stays true as the target set grows.
22
+
23
+ import { KIT, getModelPath } from '@voxgig/apidef'
24
+
25
+
26
+ // Tags are a MAP keyed by tag name, `{ sekreto: true }` — see the schema
27
+ // note on `needs`. A map because aontu unifies maps by key, so a package
28
+ // can add one tag without restating the others; and because a defaulted
29
+ // list disjunction in this position hangs the unifier on a real project's
30
+ // model. A tag counts only when its value is true, so a tag can be turned
31
+ // off by overriding it rather than by deleting the key.
32
+ //
33
+ // A list is still accepted, for a hand-written model that used the shape
34
+ // docs/design/feature-tags.md first proposed.
35
+ function tags(val: any): string[] {
36
+ if (Array.isArray(val)) {
37
+ return val.filter((t: any) => 'string' === typeof t)
38
+ }
39
+
40
+ if (null == val || 'object' !== typeof val) {
41
+ return []
42
+ }
43
+
44
+ return Object.keys(val).filter((name: string) => true === val[name])
45
+ }
46
+
47
+
48
+ // Does this feature apply to this target?
49
+ function featureApplies(feature: any, target: any): boolean {
50
+ const needs = tags(feature && feature.needs)
51
+
52
+ // The common case, and the reason this is additive: no needs, applies
53
+ // anywhere. Checked first so a target with no `provides` is unaffected.
54
+ if (0 === needs.length) {
55
+ return true
56
+ }
57
+
58
+ const provides = tags(target && target.provides)
59
+
60
+ return needs.every((need: string) => provides.includes(need))
61
+ }
62
+
63
+
64
+ // The features that apply to `target`, in the same map shape
65
+ // `getModelPath(model, 'main.<kit>.feature')` returns — already
66
+ // active-filtered by getModelPath, then gated by the tags.
67
+ //
68
+ // `target` may be the target object or its name; components hold the
69
+ // object, `configDefinition` is only given the name.
70
+ function targetFeatures(model: any, target: any): Record<string, any> {
71
+ const feature = getModelPath(model, `main.${KIT}.feature`, { required: false }) || {}
72
+
73
+ const t = 'string' === typeof target ?
74
+ (getModelPath(model, `main.${KIT}.target`, { required: false }) || {})[target] :
75
+ target
76
+
77
+ // A name that names no target gates nothing: a caller that cannot say
78
+ // which target it is generating for must not silently lose features.
79
+ if (null == t) {
80
+ return feature
81
+ }
82
+
83
+ // Keyed off the map, not off `f.name`: the key is what every consumer
84
+ // indexes by, and rebuilding from a field would drop an entry whose
85
+ // model never set one. Order is not preserved on purpose — consumers
86
+ // re-sort with `each`, which is what keeps output byte-stable.
87
+ const applies: Record<string, any> = {}
88
+ for (const [name, f] of Object.entries(feature as Record<string, any>)) {
89
+ if (featureApplies(f, t)) {
90
+ applies[name] = f
91
+ }
92
+ }
93
+
94
+ return applies
95
+ }
96
+
97
+
98
+ // THE CLOSED VOCABULARY. The schema comment says applicability tags are a
99
+ // closed set; without this, `provides: &: boolean` accepts any key, so a
100
+ // typo (`sekrreto`) compiles cleanly and silently makes the feature apply
101
+ // NOWHERE — the worst failure shape, because the feature simply vanishes
102
+ // with no diagnostic.
103
+ //
104
+ // Extended by adding a tag here AND documenting it in model/sdkgen.aon.
105
+ const TAGS = [
106
+ // A vendored sekreto port lives in this target's feature container.
107
+ 'sekreto',
108
+ ]
109
+
110
+
111
+ // The tags named by `needs`/`provides` that are not in the vocabulary.
112
+ function unknownTags(val: any): string[] {
113
+ return tags(val).filter((t: string) => !TAGS.includes(t))
114
+ }
115
+
116
+
117
+ export {
118
+ featureApplies,
119
+ targetFeatures,
120
+ tags as featureTags,
121
+ unknownTags,
122
+ TAGS,
123
+ }
@@ -11,6 +11,8 @@
11
11
  // need extra fields like `dep.replace` (go module replace directives) or
12
12
  // `dep.kind` (prod/dev/peer).
13
13
 
14
+ import { targetFeatures } from './applicability'
15
+
14
16
  import { each } from 'jostraca'
15
17
  import { KIT, getModelPath } from '@voxgig/apidef'
16
18
 
@@ -30,7 +32,9 @@ function collectDeps(
30
32
  log?: any,
31
33
  ): DepEntry[] {
32
34
  const out: DepEntry[] = []
33
- const feature = getModelPath(model, `main.${KIT}.feature`)
35
+ // Gated: a feature that does not apply to this target must not flow a
36
+ // dependency into its generated manifest.
37
+ const feature = targetFeatures(model, targetName)
34
38
 
35
39
  // Deduplicate by package name. Two features can require the same package
36
40
  // (or a feature and the target itself can), and every Package_<lang>.ts
@@ -88,6 +88,24 @@ function serverVariables(model: any): ServerVar[] {
88
88
  }
89
89
 
90
90
 
91
+ // The environment variable a generated LIVE test reads one server variable
92
+ // from: `<PROJ>_SERVER_<NAME>`, e.g. ELEMENTDEMO_SERVER_ACCOUNT_ID.
93
+ //
94
+ // A live client CANNOT BE CONSTRUCTED without every required server
95
+ // variable — makeOptions raises rather than issue requests to a URL with a
96
+ // literal `{account_id}` in it — so an SDK whose spec templates its server
97
+ // URL had no runnable live suite at all until the generated tests could be
98
+ // told the values. The apikey has had `<PROJ>_APIKEY` since the beginning;
99
+ // this is the same idea for the other half of "where do I point".
100
+ //
101
+ // One variable per NAME rather than one JSON blob: the names come from the
102
+ // spec, they are few, and a shell export per name is what a CI secret store
103
+ // and a .env file both hold naturally.
104
+ function serverVarEnv(projenvname: string, name: string): string {
105
+ return projenvname + '_SERVER_' + String(name).toUpperCase()
106
+ }
107
+
108
+
91
109
  // Does the model's server URL contain any {name} placeholders at all?
92
110
  function hasServerVariables(model: any): boolean {
93
111
  const info = model?.main?.kit?.info || model?.main?.KIT?.info
@@ -100,6 +118,7 @@ function hasServerVariables(model: any): boolean {
100
118
  export {
101
119
  serverVariables,
102
120
  hasServerVariables,
121
+ serverVarEnv,
103
122
  }
104
123
 
105
124
  export type { ServerVar }
package/src/sdkgen.ts CHANGED
@@ -22,6 +22,7 @@ import { SdkGenError, requirePath, isAuthActive, resolveAuthPrefix, isHttpBasicA
22
22
  configReprSetting, configDefinition, clean, rawStringLiteral } from './utility'
23
23
 
24
24
  import { Main } from './cmp/Main'
25
+ import { featureApplies, targetFeatures, featureTags, unknownTags, TAGS } from './helpers/applicability'
25
26
  import { ExternalTarget } from './cmp/ExternalTarget'
26
27
  import { Docs, DocsItem } from './cmp/Docs'
27
28
  import { ExternalDocs } from './cmp/ExternalDocs'
@@ -61,7 +62,7 @@ import type { DepEntry } from './helpers/collectDeps'
61
62
  import { canonToType, canonToDtype, canonKey, canonScalarKey } from './helpers/canonType'
62
63
  import { OP_SUFFIX, opTypeName, opParams, ownPoint, opActions, entityActions, entityPath, opRequestShape, entityIdField, entityDataIdField, entityOps, entityPrimaryOp, pickExampleEntity, entityClassName, entityTypeCollisions, warnEntityTypeCollisions, deriveEntityNames, entityCollection } from './helpers/opShape'
63
64
  import { isReservedName, safeVarName, exampleVarName, phpEntityAccessor, entityCacheField, isRbCoreConstant, isRbSdkConstant, rbSafeTypeName, isSwiftSdkType, swiftSafeTypeName, isPhpReservedType, isPhpSdkClass, phpSafeTypeName, isTsReservedType, tsSafeTypeName, jsProp, jsOptProp, jsKey } from './helpers/naming'
64
- import { serverVariables, hasServerVariables } from './helpers/serverVars'
65
+ import { serverVariables, hasServerVariables, serverVarEnv } from './helpers/serverVars'
65
66
  import { primaryOpCall, idLiteral, matchArg, dataArg, litFor } from './helpers/opExample'
66
67
  import type { ExampleLang } from './helpers/opExample'
67
68
  import { liveStrict } from './helpers/testPolicy'
@@ -1153,6 +1154,7 @@ export {
1153
1154
  tsSafeTypeName,
1154
1155
  serverVariables,
1155
1156
  hasServerVariables,
1157
+ serverVarEnv,
1156
1158
  liveStrict,
1157
1159
  primaryOpCall,
1158
1160
  idLiteral,
@@ -1167,6 +1169,12 @@ export {
1167
1169
  srcFeatureExcludes,
1168
1170
  stationLibrary,
1169
1171
 
1172
+ featureApplies,
1173
+ targetFeatures,
1174
+ featureTags,
1175
+ unknownTags,
1176
+ TAGS,
1177
+
1170
1178
  definitionPath,
1171
1179
  definitionFolder,
1172
1180
  definitionNames,
package/src/utility.ts CHANGED
@@ -5,6 +5,8 @@ import { JostracaResult, each } from 'jostraca'
5
5
 
6
6
  import { KIT, getModelPath } from '@voxgig/apidef'
7
7
 
8
+ import { targetFeatures } from './helpers/applicability'
9
+
8
10
  import { serverVariables } from './helpers/serverVars'
9
11
  import { packageVersion } from './helpers/packageMeta'
10
12
 
@@ -312,7 +314,14 @@ function rawStringLiteral(s: string): string {
312
314
  // across runs exactly like the literal it replaces.
313
315
  function configDefinition(model: any, targetname?: string): { def: any, json: string } {
314
316
  const entity = getModelPath(model, `main.${KIT}.entity`)
315
- const feature = getModelPath(model, `main.${KIT}.feature`)
317
+
318
+ // Gated by the target when one is named, so the embedded config cannot
319
+ // advertise a feature this target has no implementation for — the exact
320
+ // hybrid state the applicability gate exists to prevent. With no
321
+ // targetname (a caller that cannot say which target it is building) the
322
+ // helper returns every active feature, i.e. the old behaviour.
323
+ const feature = targetFeatures(model, targetname)
324
+
316
325
  const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
317
326
 
318
327
  const authActive = isAuthActive(model)
@@ -1,425 +0,0 @@
1
- // VERSION: @voxgig/struct 0.0.10
2
- // This test utility runs the JSON-specified tests in build/test/test.json.
3
- // (or .sdk/test/test.json if used in a @voxgig/sdkgen project)
4
-
5
- import { readFileSync } from 'node:fs'
6
- import { join } from 'node:path'
7
- import { deepStrictEqual, fail, AssertionError } from 'node:assert'
8
-
9
- const NULLMARK = '__NULL__' // Value is JSON null
10
- const UNDEFMARK = '__UNDEF__' // Value is not present (thus, undefined).
11
- const EXISTSMARK = '__EXISTS__' // Value exists (not undefined).
12
-
13
-
14
- type Subject = (...args: any[]) => any
15
- type RunSet = (testspec: any, testsubject: Function) => Promise<any>
16
- type RunSetFlags = (testspec: any, flags: Record<string, boolean>, testsubject: Function)
17
- => Promise<any>
18
-
19
-
20
- type RunPack = {
21
- spec: Record<string, any>
22
- runset: RunSet
23
- runsetflags: RunSetFlags
24
- subject: Subject
25
- client: any
26
- }
27
-
28
-
29
- type TestPack = {
30
- name?: string
31
- client: any
32
- subject: Subject
33
- utility: any
34
- }
35
-
36
- type Flags = Record<string, boolean>
37
-
38
-
39
- type Utility = {
40
- struct: any
41
- makeContext: (ctxmap: Record<string, any>, basectx?: any) => any
42
- }
43
-
44
-
45
- type Client = {
46
- utility: () => Utility
47
- }
48
-
49
-
50
- async function makeRunner(testfile: string, client: Client) {
51
-
52
- return async function runner(
53
- name: string,
54
- store?: any,
55
- ): Promise<RunPack> {
56
- store = store || {}
57
-
58
- const utility = client.utility()
59
- const structUtils = utility.struct
60
-
61
- let spec = resolveSpec(name, testfile)
62
- let clients = await resolveClients(client, spec, store, structUtils)
63
- let subject = resolveSubject(name, utility)
64
-
65
- let runsetflags: RunSetFlags = async (
66
- testspec: any,
67
- flags: Flags,
68
- testsubject: Function
69
- ) => {
70
- subject = testsubject || subject
71
- flags = resolveFlags(flags)
72
- const testspecmap = fixJSON(testspec, flags)
73
-
74
- const testset: any[] = testspecmap.set
75
- for (let entry of testset) {
76
- try {
77
- entry = resolveEntry(entry, flags)
78
-
79
- let testpack = resolveTestPack(name, entry, subject, client, clients)
80
- let args = resolveArgs(entry, testpack, utility, structUtils)
81
-
82
- let res = await testpack.subject(...args)
83
- res = fixJSON(res, flags)
84
- entry.res = res
85
-
86
- checkResult(entry, args, res, structUtils)
87
- }
88
- catch (err: any) {
89
- if (err instanceof AssertionError) {
90
- throw err
91
- }
92
- handleError(entry, err, structUtils)
93
- }
94
- }
95
- }
96
-
97
- let runset: RunSet = async (
98
- testspec: any,
99
- testsubject: Function
100
- ) => runsetflags(testspec, {}, testsubject)
101
-
102
- const runpack: RunPack = {
103
- spec,
104
- runset,
105
- runsetflags,
106
- subject,
107
- client,
108
- }
109
-
110
- return runpack
111
- }
112
- }
113
-
114
- function resolveSpec(name: string, testfile: string): Record<string, any> {
115
- const alltests =
116
- JSON.parse(readFileSync(join(__dirname, testfile), 'utf8'))
117
-
118
- let spec = alltests.primary?.[name] || alltests[name] || alltests
119
- return spec
120
- }
121
-
122
-
123
- async function resolveClients(
124
- client: any,
125
- spec: Record<string, any>,
126
- store: any,
127
- structUtils: Record<string, any>
128
- ):
129
- Promise<Record<string, any>> {
130
-
131
- const clients: Record<string, any> = {}
132
- if (spec.DEF && spec.DEF.client) {
133
- for (let cn in spec.DEF.client) {
134
- const cdef = spec.DEF.client[cn]
135
- const copts = cdef.test.options || {}
136
- if ('object' === typeof store && structUtils?.inject) {
137
- structUtils.inject(copts, store)
138
- }
139
-
140
- clients[cn] = await client.tester(copts)
141
- }
142
- }
143
- return clients
144
- }
145
-
146
-
147
- function resolveSubject(name: string, container: any) {
148
- const subject = container[name] || container.struct[name]
149
- return subject
150
- }
151
-
152
-
153
- function resolveFlags(flags?: Flags): Flags {
154
- if (null == flags) {
155
- flags = {}
156
- }
157
- flags.null = null == flags.null ? true : !!flags.null
158
- return flags
159
- }
160
-
161
-
162
- function resolveEntry(entry: any, flags: Flags): any {
163
- entry.out = null == entry.out && flags.null ? NULLMARK : entry.out
164
- return entry
165
- }
166
-
167
-
168
- function checkResult(entry: any, args: any[], res: any, structUtils: Record<string, any>) {
169
- let matched = false
170
-
171
- if (entry.err) {
172
- return fail('Expected error did not occur: ' + entry.err +
173
- '\n\nENTRY: ' + safeStringify(entry))
174
- }
175
-
176
- if (entry.match) {
177
- const result = { in: entry.in, args, out: entry.res, ctx: entry.ctx }
178
- match(
179
- entry.match,
180
- result,
181
- structUtils
182
- )
183
-
184
- matched = true
185
- }
186
-
187
- const out = entry.out
188
-
189
- if (out === res) {
190
- return
191
- }
192
-
193
- // NOTE: allow match with no out.
194
- if (matched && (NULLMARK === out || null == out)) {
195
- return
196
- }
197
-
198
- deepStrictEqual(null != res ? JSON.parse(safeStringify(res)) : res, entry.out)
199
- }
200
-
201
-
202
- // Handle errors from test execution
203
- function handleError(entry: any, err: any, structUtils: Record<string, any>) {
204
- entry.thrown = err
205
-
206
- const entry_err = entry.err
207
-
208
- if (null != entry_err) {
209
- if (true === entry_err || matchval(entry_err, err.message, structUtils)) {
210
- if (entry.match) {
211
- match(
212
- entry.match,
213
- { in: entry.in, out: entry.res, ctx: entry.ctx, err: fixJSON(err, { null: true }) },
214
- structUtils
215
- )
216
- }
217
- return
218
- }
219
-
220
- fail('ERROR MATCH: [' + structUtils.stringify(entry_err) +
221
- '] <=> [' + err.message + ']')
222
- }
223
-
224
- // Unexpected error (test didn't specify an error expectation)
225
- else if (err instanceof AssertionError) {
226
- fail(err.message + '\n\nENTRY: ' + safeStringify(entry))
227
- }
228
- else {
229
- fail(err.stack + '\n\nENTRY: ' + safeStringify(entry))
230
- }
231
- }
232
-
233
-
234
- function resolveArgs(
235
- entry: any,
236
- testpack: TestPack,
237
- utility: Utility,
238
- structUtils: Record<string, any>
239
- ): any[] {
240
- let args: any[] = []
241
-
242
- if (entry.ctx) {
243
- args = [entry.ctx]
244
- }
245
- else if (entry.args) {
246
- args = entry.args
247
- }
248
- else {
249
- args = [structUtils.clone(entry.in)]
250
- }
251
-
252
- if (entry.ctx || entry.args) {
253
- let first = args[0]
254
- if (structUtils.ismap(first)) {
255
- first = structUtils.clone(first)
256
- first = utility.makeContext(first)
257
- args[0] = first
258
- entry.ctx = first
259
-
260
- first.client = testpack.client
261
- first.utility = testpack.utility
262
- }
263
- }
264
-
265
- return args
266
- }
267
-
268
-
269
- function resolveTestPack(
270
- name: string,
271
- entry: any,
272
- subject: Subject,
273
- client: any,
274
- clients: Record<string, any>
275
- ) {
276
- const testpack: TestPack = {
277
- name,
278
- client,
279
- subject,
280
- utility: client.utility(),
281
- }
282
-
283
- if (entry.client) {
284
- testpack.client = clients[entry.client]
285
- testpack.utility = testpack.client.utility()
286
- testpack.subject = resolveSubject(name, testpack.utility)
287
- }
288
-
289
- return testpack
290
- }
291
-
292
-
293
- function match(
294
- check: any,
295
- basex: any,
296
- structUtils: Record<string, any>
297
- ) {
298
- const cbase = structUtils.clone(basex)
299
-
300
- structUtils.walk(check, (_key: any, val: any, _parent: any, path: any) => {
301
- if (!structUtils.isnode(val)) {
302
- let baseval = structUtils.getpath(cbase, path)
303
-
304
- if (baseval === val) {
305
- return val
306
- }
307
-
308
- // Explicit undefined expected
309
- if (UNDEFMARK === val && undefined === baseval) {
310
- return val
311
- }
312
-
313
- // Explicit defined expected
314
- if (EXISTSMARK === val && null != baseval) {
315
- return val
316
- }
317
-
318
- if (!matchval(val, baseval, structUtils)) {
319
- fail('MATCH: ' + path.join('.') +
320
- ': [' + structUtils.stringify(val) +
321
- '] <=> [' + structUtils.stringify(baseval) + ']')
322
- }
323
- }
324
-
325
- return val
326
- })
327
- }
328
-
329
-
330
- function matchval(
331
- check: any,
332
- base: any,
333
- structUtils: Record<string, any>
334
- ) {
335
- let pass = check === base
336
-
337
- if (!pass) {
338
-
339
- if ('string' === typeof check) {
340
- let basestr = structUtils.stringify(base)
341
-
342
- let rem = check.match(/^\/(.+)\/$/)
343
- if (rem) {
344
- pass = new RegExp(rem[1]).test(basestr)
345
- }
346
- else {
347
- pass = basestr.toLowerCase().includes(structUtils.stringify(check).toLowerCase())
348
- }
349
- }
350
- else if ('function' === typeof check) {
351
- pass = true
352
- }
353
- }
354
-
355
- return pass
356
- }
357
-
358
-
359
- function safeStringify(val: any): string {
360
- const seen = new WeakSet()
361
- return JSON.stringify(val, (_k, v) => {
362
- if ('object' === typeof v && null !== v) {
363
- if (seen.has(v)) return '[Circular]'
364
- seen.add(v)
365
- }
366
- if (v instanceof Error) {
367
- return { name: v.name, message: v.message }
368
- }
369
- return v
370
- }, 2)
371
- }
372
-
373
-
374
- function fixJSON(val: any, flags?: Flags): any {
375
- if (null == val) {
376
- return flags?.null ? NULLMARK : val
377
- }
378
-
379
- const seen = new WeakSet()
380
- const replacer = (_k: string, v: any) => {
381
- if (null == v && flags?.null) {
382
- return NULLMARK
383
- }
384
-
385
- if (v instanceof Error) {
386
- return {
387
- ...v,
388
- name: v.name,
389
- message: v.message,
390
- }
391
- }
392
-
393
- if ('object' === typeof v && null !== v) {
394
- if (seen.has(v)) return '[Circular]'
395
- seen.add(v)
396
- }
397
-
398
- return v
399
- }
400
-
401
- return JSON.parse(JSON.stringify(val, replacer))
402
- }
403
-
404
-
405
- function nullModifier(
406
- val: any,
407
- key: any,
408
- parent: any
409
- ) {
410
- if ("__NULL__" === val) {
411
- parent[key] = null
412
- }
413
- else if ('string' === typeof val) {
414
- parent[key] = val.replaceAll('__NULL__', 'null')
415
- }
416
- }
417
-
418
-
419
- export {
420
- NULLMARK,
421
- EXISTSMARK,
422
- nullModifier,
423
- makeRunner,
424
- }
425
-