@voxgig/sdkgen 1.3.17 → 1.3.19

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 (267) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Deploy.js +16 -16
  3. package/dist/cmp/Deploy.js.map +1 -1
  4. package/dist/cmp/Entity.js +5 -1
  5. package/dist/cmp/Entity.js.map +1 -1
  6. package/dist/cmp/Feature.js +2 -1
  7. package/dist/cmp/Feature.js.map +1 -1
  8. package/dist/cmp/ReadmeTop.js +13 -11
  9. package/dist/cmp/ReadmeTop.js.map +1 -1
  10. package/dist/helpers/canonType.js +8 -3
  11. package/dist/helpers/canonType.js.map +1 -1
  12. package/dist/helpers/collectDeps.d.ts +1 -1
  13. package/dist/helpers/collectDeps.js +36 -13
  14. package/dist/helpers/collectDeps.js.map +1 -1
  15. package/dist/helpers/naming.d.ts +5 -1
  16. package/dist/helpers/naming.js +118 -0
  17. package/dist/helpers/naming.js.map +1 -1
  18. package/dist/helpers/opShape.d.ts +3 -1
  19. package/dist/helpers/opShape.js +87 -8
  20. package/dist/helpers/opShape.js.map +1 -1
  21. package/dist/helpers/serverVars.d.ts +10 -0
  22. package/dist/helpers/serverVars.js +87 -0
  23. package/dist/helpers/serverVars.js.map +1 -0
  24. package/dist/sdkgen.d.ts +4 -3
  25. package/dist/sdkgen.js +11 -2
  26. package/dist/sdkgen.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +15 -10
  29. package/project/.sdk/model/target/cpp.aontu +4 -4
  30. package/project/.sdk/model/target/go-cli.aontu +17 -17
  31. package/project/.sdk/model/target/go-mcp.aontu +11 -11
  32. package/project/.sdk/model/target/go.aontu +1 -1
  33. package/project/.sdk/model/target/java.aontu +4 -4
  34. package/project/.sdk/model/target/kotlin.aontu +4 -4
  35. package/project/.sdk/model/target/lean.aontu +51 -0
  36. package/project/.sdk/model/target/scala.aontu +4 -4
  37. package/project/.sdk/src/cmp/c/EntityTypes_c.ts +3 -4
  38. package/project/.sdk/src/cmp/c/Entity_c.ts +3 -3
  39. package/project/.sdk/src/cmp/c/ReadmeExamplesTest_c.ts +24 -7
  40. package/project/.sdk/src/cmp/c/TestDirect_c.ts +3 -3
  41. package/project/.sdk/src/cmp/c/TestEntity_c.ts +1 -1
  42. package/project/.sdk/src/cmp/clojure/Entity_clojure.ts +1 -1
  43. package/project/.sdk/src/cmp/clojure/ReadmeExamplesTest_clojure.ts +19 -8
  44. package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +2 -2
  45. package/project/.sdk/src/cmp/cpp/Entity_cpp.ts +3 -3
  46. package/project/.sdk/src/cmp/cpp/MainEntity_cpp.ts +2 -2
  47. package/project/.sdk/src/cmp/cpp/ReadmeExamplesTest_cpp.ts +136 -0
  48. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +3 -3
  49. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -0
  50. package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +2 -3
  51. package/project/.sdk/src/cmp/csharp/Entity_csharp.ts +3 -3
  52. package/project/.sdk/src/cmp/csharp/MainEntity_csharp.ts +2 -2
  53. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
  54. package/project/.sdk/src/cmp/csharp/ReadmeExamplesTest_csharp.ts +13 -1
  55. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +3 -3
  56. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +22 -15
  57. package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +2 -3
  58. package/project/.sdk/src/cmp/dart/Entity_dart.ts +3 -3
  59. package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +2 -2
  60. package/project/.sdk/src/cmp/dart/Main_dart.ts +3 -3
  61. package/project/.sdk/src/cmp/dart/Package_dart.ts +1 -1
  62. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +4 -4
  63. package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +2 -3
  64. package/project/.sdk/src/cmp/elixir/Entity_elixir.ts +1 -1
  65. package/project/.sdk/src/cmp/elixir/ReadmeExamplesTest_elixir.ts +21 -3
  66. package/project/.sdk/src/cmp/go/Config_go.ts +14 -2
  67. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +110 -18
  68. package/project/.sdk/src/cmp/go/Entity_go.ts +3 -3
  69. package/project/.sdk/src/cmp/go/Main_go.ts +7 -6
  70. package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
  71. package/project/.sdk/src/cmp/go/TestDirect_go.ts +3 -3
  72. package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
  73. package/project/.sdk/src/cmp/go/utility_go.ts +25 -0
  74. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +62 -35
  75. package/project/.sdk/src/cmp/go-cli/fragment/main.fragment.go +7 -7
  76. package/project/.sdk/src/cmp/go-cli/fragment/words.fragment.go +21 -18
  77. package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +9 -1
  78. package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +28 -18
  79. package/project/.sdk/src/cmp/java/EntityTypes_java.ts +2 -3
  80. package/project/.sdk/src/cmp/java/Entity_java.ts +3 -3
  81. package/project/.sdk/src/cmp/java/MainEntity_java.ts +2 -2
  82. package/project/.sdk/src/cmp/java/Package_java.ts +1 -1
  83. package/project/.sdk/src/cmp/java/ReadmeExamplesTest_java.ts +23 -1
  84. package/project/.sdk/src/cmp/java/TestDirect_java.ts +3 -3
  85. package/project/.sdk/src/cmp/js/Config_js.ts +11 -0
  86. package/project/.sdk/src/cmp/js/EntityTypes_js.ts +2 -3
  87. package/project/.sdk/src/cmp/js/Entity_js.ts +3 -3
  88. package/project/.sdk/src/cmp/js/MainEntity_js.ts +2 -2
  89. package/project/.sdk/src/cmp/js/Main_js.ts +2 -2
  90. package/project/.sdk/src/cmp/js/TestDirect_js.ts +4 -4
  91. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +2 -2
  92. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +4 -0
  93. package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +2 -3
  94. package/project/.sdk/src/cmp/kotlin/Entity_kotlin.ts +3 -3
  95. package/project/.sdk/src/cmp/kotlin/MainEntity_kotlin.ts +2 -2
  96. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +1 -1
  97. package/project/.sdk/src/cmp/kotlin/ReadmeExamplesTest_kotlin.ts +27 -2
  98. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +3 -3
  99. package/project/.sdk/src/cmp/lean/Config_lean.ts +88 -0
  100. package/project/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  101. package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  102. package/project/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  103. package/project/.sdk/src/cmp/lean/Package_lean.ts +68 -0
  104. package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  105. package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  106. package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  107. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +38 -0
  108. package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  109. package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  110. package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  111. package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  112. package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +16 -0
  113. package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  114. package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  115. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +18 -0
  116. package/project/.sdk/src/cmp/lean/Test_lean.ts +207 -0
  117. package/project/.sdk/src/cmp/lean/utility_lean.ts +78 -0
  118. package/project/.sdk/src/cmp/lua/Config_lua.ts +10 -1
  119. package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +2 -3
  120. package/project/.sdk/src/cmp/lua/Entity_lua.ts +3 -3
  121. package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -1
  122. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +3 -3
  123. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
  124. package/project/.sdk/src/cmp/ocaml/ReadmeExamplesTest_ocaml.ts +29 -14
  125. package/project/.sdk/src/cmp/perl/Entity_perl.ts +3 -3
  126. package/project/.sdk/src/cmp/perl/MainEntity_perl.ts +2 -2
  127. package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
  128. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +3 -3
  129. package/project/.sdk/src/cmp/php/Config_php.ts +13 -2
  130. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +2 -3
  131. package/project/.sdk/src/cmp/php/Entity_php.ts +3 -3
  132. package/project/.sdk/src/cmp/php/MainEntity_php.ts +2 -2
  133. package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
  134. package/project/.sdk/src/cmp/php/TestDirect_php.ts +3 -3
  135. package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
  136. package/project/.sdk/src/cmp/py/Config_py.ts +10 -1
  137. package/project/.sdk/src/cmp/py/EntityTypes_py.ts +2 -3
  138. package/project/.sdk/src/cmp/py/Entity_py.ts +3 -3
  139. package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -2
  140. package/project/.sdk/src/cmp/py/Main_py.ts +2 -2
  141. package/project/.sdk/src/cmp/py/Package_py.ts +1 -1
  142. package/project/.sdk/src/cmp/py/TestDirect_py.ts +3 -3
  143. package/project/.sdk/src/cmp/py/TestEntity_py.ts +1 -1
  144. package/project/.sdk/src/cmp/rb/Config_rb.ts +12 -1
  145. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +15 -5
  146. package/project/.sdk/src/cmp/rb/Entity_rb.ts +3 -3
  147. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +2 -2
  148. package/project/.sdk/src/cmp/rb/Package_rb.ts +2 -2
  149. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +3 -3
  150. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
  151. package/project/.sdk/src/cmp/rust/EntityBase_rust.ts +2 -2
  152. package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +3 -4
  153. package/project/.sdk/src/cmp/rust/Entity_rust.ts +3 -3
  154. package/project/.sdk/src/cmp/rust/MainEntity_rust.ts +2 -2
  155. package/project/.sdk/src/cmp/rust/Package_rust.ts +1 -1
  156. package/project/.sdk/src/cmp/rust/ReadmeExamplesTest_rust.ts +22 -10
  157. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +3 -3
  158. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +25 -18
  159. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +2 -3
  160. package/project/.sdk/src/cmp/scala/Entity_scala.ts +3 -3
  161. package/project/.sdk/src/cmp/scala/MainEntity_scala.ts +2 -2
  162. package/project/.sdk/src/cmp/scala/ReadmeExamplesTest_scala.ts +22 -3
  163. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +3 -3
  164. package/project/.sdk/src/cmp/scala/Test_scala.ts +1 -1
  165. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +12 -6
  166. package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -3
  167. package/project/.sdk/src/cmp/swift/MainEntity_swift.ts +2 -2
  168. package/project/.sdk/src/cmp/swift/Package_swift.ts +1 -1
  169. package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +18 -1
  170. package/project/.sdk/src/cmp/swift/TestDirect_swift.ts +1 -1
  171. package/project/.sdk/src/cmp/ts/Config_ts.ts +11 -0
  172. package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +2 -3
  173. package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -3
  174. package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +2 -2
  175. package/project/.sdk/src/cmp/ts/Main_ts.ts +2 -2
  176. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +4 -4
  177. package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +2 -2
  178. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +4 -0
  179. package/project/.sdk/src/cmp/zig/Entity_zig.ts +3 -3
  180. package/project/.sdk/src/cmp/zig/MainEntity_zig.ts +2 -2
  181. package/project/.sdk/src/cmp/zig/Package_zig.ts +10 -2
  182. package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +8 -16
  183. package/project/.sdk/tm/csharp/Makefile +6 -6
  184. package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +10 -28
  185. package/project/.sdk/tm/dart/Makefile +3 -3
  186. package/project/.sdk/tm/dart/test/primary_test.dart +7 -0
  187. package/project/.sdk/tm/go/Makefile +6 -6
  188. package/project/.sdk/tm/go/feature/test_feature.go +33 -1
  189. package/project/.sdk/tm/go/test/primary_utility_test.go +46 -42
  190. package/project/.sdk/tm/go/test/runner_test.go +30 -1
  191. package/project/.sdk/tm/go/utility/make_options.go +48 -0
  192. package/project/.sdk/tm/java/Makefile +6 -6
  193. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +7 -24
  194. package/project/.sdk/tm/js/Makefile +6 -6
  195. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +18 -1
  196. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -1
  197. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +13 -0
  198. package/project/.sdk/tm/kotlin/Makefile +6 -6
  199. package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +7 -20
  200. package/project/.sdk/tm/lean/LICENSE +21 -0
  201. package/project/.sdk/tm/lean/VERSION +1 -0
  202. package/project/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  203. package/project/.sdk/tm/lean/src/SdkFeatures.lean +602 -0
  204. package/project/.sdk/tm/lean/src/SdkJson.lean +134 -0
  205. package/project/.sdk/tm/lean/src/SdkRuntime.lean +356 -0
  206. package/project/.sdk/tm/lean/src/SdkUtility.lean +617 -0
  207. package/project/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  208. package/project/.sdk/tm/lean/src/Vregex.lean +412 -0
  209. package/project/.sdk/tm/lean/src/feature/README.md +5 -0
  210. package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  211. package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  212. package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  213. package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  214. package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  215. package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  216. package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  217. package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  218. package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  219. package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  220. package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  221. package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  222. package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  223. package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  224. package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  225. package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  226. package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  227. package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  228. package/project/.sdk/tm/lean/test/StructCorpus.lean +697 -0
  229. package/project/.sdk/tm/lean/test/TFeature.lean +268 -0
  230. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +349 -0
  231. package/project/.sdk/tm/lua/Makefile +7 -7
  232. package/project/.sdk/tm/lua/feature/test_feature.lua +29 -1
  233. package/project/.sdk/tm/lua/test/primary_utility_test.lua +14 -8
  234. package/project/.sdk/tm/lua/utility/make_options.lua +39 -0
  235. package/project/.sdk/tm/perl/Makefile +6 -6
  236. package/project/.sdk/tm/perl/t/primary_utility.t +8 -17
  237. package/project/.sdk/tm/php/Makefile +6 -6
  238. package/project/.sdk/tm/php/feature/TestFeature.php +30 -2
  239. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +14 -7
  240. package/project/.sdk/tm/php/utility/MakeOptions.php +44 -0
  241. package/project/.sdk/tm/py/Makefile +6 -6
  242. package/project/.sdk/tm/py/feature/test_feature.py +28 -1
  243. package/project/.sdk/tm/py/test/test_primary_utility.py +18 -18
  244. package/project/.sdk/tm/py/utility/make_options.py +42 -0
  245. package/project/.sdk/tm/rb/Makefile +6 -6
  246. package/project/.sdk/tm/rb/feature/test_feature.rb +24 -1
  247. package/project/.sdk/tm/rb/test/primary_utility_test.rb +11 -8
  248. package/project/.sdk/tm/rb/utility/make_options.rb +37 -0
  249. package/project/.sdk/tm/rust/Makefile +6 -6
  250. package/project/.sdk/tm/rust/tests/common/mod.rs +37 -1
  251. package/project/.sdk/tm/rust/tests/primary_utility_test.rs +12 -26
  252. package/project/.sdk/tm/swift/Makefile +6 -6
  253. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +7 -8
  254. package/project/.sdk/tm/ts/Makefile +6 -6
  255. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +18 -1
  256. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +39 -1
  257. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +69 -21
  258. package/src/cmp/Deploy.ts +16 -16
  259. package/src/cmp/Entity.ts +5 -1
  260. package/src/cmp/Feature.ts +2 -1
  261. package/src/cmp/ReadmeTop.ts +13 -11
  262. package/src/helpers/canonType.ts +8 -3
  263. package/src/helpers/collectDeps.ts +39 -12
  264. package/src/helpers/naming.ts +130 -0
  265. package/src/helpers/opShape.ts +99 -12
  266. package/src/helpers/serverVars.ts +105 -0
  267. package/src/sdkgen.ts +12 -3
@@ -160,13 +160,25 @@ public class ReadmeExamplesTest
160
160
  public void AllCsharpBlocksAreStructurallyBalanced()
161
161
  {
162
162
  var failures = new List<string>();
163
+ var fence = new string((char)96, 3);
163
164
  foreach (var doc in Docs())
164
165
  {
165
166
  if (!System.IO.File.Exists(doc.path))
166
167
  {
167
168
  continue;
168
169
  }
169
- var blocks = CsharpBlocks(System.IO.File.ReadAllText(doc.path));
170
+ var text = System.IO.File.ReadAllText(doc.path);
171
+ // A well-formed document has an EVEN number of triple-backtick fence
172
+ // markers — every block that opens also closes. An odd count means a
173
+ // fence was opened but never closed at end-of-document; because block
174
+ // extraction keys off fence parity, such an unterminated csharp block
175
+ // can silently vanish instead of being checked. Fail loudly.
176
+ if ((text.Split(fence).Length - 1) % 2 != 0)
177
+ {
178
+ failures.Add(doc.label +
179
+ " has an unclosed code fence (odd number of triple-backtick markers)");
180
+ }
181
+ var blocks = CsharpBlocks(text);
170
182
  var balanced = 0;
171
183
  for (var i = 0; i < blocks.Count; i++)
172
184
  {
@@ -73,7 +73,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
73
73
  ? `\n ["apikey"] = env["${PROJECTNAME}_APIKEY"],`
74
74
  : ''
75
75
 
76
- const opnames = Object.keys(entity.op)
76
+ const opnames = Object.keys(entity.op || {})
77
77
  const hasLoad = opnames.includes('load')
78
78
  const hasList = opnames.includes('list')
79
79
 
@@ -81,8 +81,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
81
81
  return
82
82
  }
83
83
 
84
- const loadOp = entity.op.load
85
- const listOp = entity.op.list
84
+ const loadOp = entity.op?.load
85
+ const listOp = entity.op?.list
86
86
 
87
87
  // Get load point info
88
88
  const loadPoint = loadOp?.points?.[0]
@@ -78,9 +78,28 @@ const TestEntity = cmp(function TestEntity(props: any) {
78
78
 
79
79
  const genCtx: GenCtx = { model, entity, flow: basicflow, PROJUPPER }
80
80
 
81
- const opsList = Array.from(new Set(
81
+ const opNames = Array.from(new Set(
82
82
  (allSteps as any[]).map((s: any) => s.op).filter(Boolean)))
83
- .map(o => `"${o}"`).join(', ')
83
+ const opsList = opNames.map(o => `"${o}"`).join(', ')
84
+
85
+ // An entity whose basic flow has no ops must not emit the per-op skip
86
+ // loop. `new[] { }` is an empty implicitly-typed array and C# cannot infer
87
+ // its element type — CS0826, which fails the whole test project to build.
88
+ // The loop is dead code in that case anyway, and so is the `_mode` local
89
+ // that only it reads (CS0219 unused-variable otherwise).
90
+ const skipBlock = 0 === opNames.length ? '' : ` // Per-op sdk-test-control.json skip - basic test exercises a flow
91
+ // with multiple ops; skipping any op skips the whole flow.
92
+ var _mode = setup.Live ? "live" : "unit";
93
+ foreach (var _op in new[] { ${opsList} })
94
+ {
95
+ var (_shouldSkip, _) = TestRunner.IsControlSkipped(
96
+ "entityOp", "${entity.name}." + _op, _mode);
97
+ if (_shouldSkip)
98
+ {
99
+ return; // skipped via sdk-test-control.json
100
+ }
101
+ }
102
+ `
84
103
 
85
104
  File({ name: entity.Name + 'EntityTest.' + target.ext }, () => {
86
105
 
@@ -107,19 +126,7 @@ public class ${entity.Name}EntityTest
107
126
  public void Basic()
108
127
  {
109
128
  var setup = ${entity.Name}BasicSetup(null);
110
- // Per-op sdk-test-control.json skip - basic test exercises a flow
111
- // with multiple ops; skipping any op skips the whole flow.
112
- var _mode = setup.Live ? "live" : "unit";
113
- foreach (var _op in new[] { ${opsList} })
114
- {
115
- var (_shouldSkip, _) = TestRunner.IsControlSkipped(
116
- "entityOp", "${entity.name}." + _op, _mode);
117
- if (_shouldSkip)
118
- {
119
- return; // skipped via sdk-test-control.json
120
- }
121
- }
122
- // The basic flow consumes synthetic IDs from the fixture. In live
129
+ ${skipBlock} // The basic flow consumes synthetic IDs from the fixture. In live
123
130
  // mode without an *_ENTID env override, those IDs hit the live API
124
131
  // and 4xx; set ${PROJUPPER}_TEST_${ENTUPPER}_ENTID JSON to run live.
125
132
  if (setup.SyntheticOnly)
@@ -23,7 +23,7 @@ import {
23
23
  File, Content,
24
24
  } from '@voxgig/sdkgen'
25
25
 
26
- import { canonKey, canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
26
+ import { canonKey, canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions, deriveEntityNames } from '@voxgig/sdkgen'
27
27
 
28
28
  import {
29
29
  KIT,
@@ -147,11 +147,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
147
147
  // filter, so inactive entities still get class files referencing these
148
148
  // typed names. Filter on `name` (always present), NOT `active` — parity
149
149
  // with the go emitter's fix.
150
- const entityList = each(entity).filter((e: any) => e && null != e.name)
150
+ const entityList = deriveEntityNames(entity)
151
151
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
152
152
  // entity not yet named (e.g. a fieldless placeholder) would otherwise read
153
153
  // `Name = undefined` below. Parity with the go emitter's fix.
154
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
155
154
 
156
155
  // Surface duplicate generated type names (two entities with the same
157
156
  // PascalCase Name) — they would redeclare a type in statically-typed
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, camelify, names,
6
6
  Content, File, Folder, Fragment,
7
- entityClassName,
7
+ entityClassName, entityCollection,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
  import {
@@ -23,7 +23,7 @@ const Entity = cmp(function Entity(props: any) {
23
23
  // `<Name>Entity`, disambiguated when it would clash with another entity's
24
24
  // data-type name. The class file name and the Main accessor both use
25
25
  // this, so they always agree.
26
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
26
+ const entityColl = entityCollection(model)
27
27
  const cls = entityClassName(entity, entityColl)
28
28
 
29
29
  const entrep = {
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
38
38
 
39
39
  File({ name: cls + '.' + target.ext }, () => {
40
40
 
41
- const opnames = Object.keys(entity.op)
41
+ const opnames = Object.keys(entity.op || {})
42
42
 
43
43
  const opfrags =
44
44
  (['load', 'list', 'create', 'update', 'remove']
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- import { cmp, Content, entityClassName } from '@voxgig/sdkgen'
3
+ import { cmp, Content, entityClassName, entityCollection } from '@voxgig/sdkgen'
4
4
 
5
5
  import {
6
6
  KIT,
@@ -15,7 +15,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
15
15
  // Return the collision-free class TYPE (entityClassName); the accessor
16
16
  // METHOD name (entity.Name) is unchanged so callers still write
17
17
  // client.<Name>().
18
- const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
18
+ const cls = entityClassName(entity, entityCollection(model))
19
19
 
20
20
  Content(`
21
21
  // Entity access: \`client.${entity.Name}().list()\` / \`client.${entity.Name}().load({'id': ...})\`.
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each,
6
6
  List, File, Copy, Folder, Fragment, Line,
7
- entityClassName,
7
+ entityClassName, entityCollection,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
 
@@ -61,7 +61,7 @@ const Main = cmp(async function Main(props: any) {
61
61
  Line(``)
62
62
 
63
63
  List({ item: entity }, ({ item }: any) => {
64
- const cls = entityClassName(item, entity)
64
+ const cls = entityClassName(item, entityCollection(model))
65
65
  return Line(`import 'entity/${cls}.dart';`)
66
66
  })
67
67
 
@@ -70,7 +70,7 @@ const Main = cmp(async function Main(props: any) {
70
70
  Line(``)
71
71
  Line(`export '${model.const.Name}Types.dart';`)
72
72
  List({ item: entity }, ({ item }: any) => {
73
- const cls = entityClassName(item, entity)
73
+ const cls = entityClassName(item, entityCollection(model))
74
74
  return Line(`export 'entity/${cls}.dart';`)
75
75
  })
76
76
 
@@ -59,7 +59,7 @@ topics:
59
59
  // Runtime is dependency-free (dart:io + dart:convert + vendored struct);
60
60
  // target/feature deps, when declared, land here.
61
61
  const deps: Record<string, string> = {}
62
- for (const d of collectDeps(model, target.name, target.deps)) {
62
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
63
63
  deps[d.name] = d.source === 'target' ? (d.version || 'any') : d.version
64
64
  }
65
65
 
@@ -47,7 +47,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
47
47
  'apikey': env['${PROJECTNAME}_APIKEY'],`
48
48
  : ''
49
49
 
50
- const opnames = Object.keys(entity.op)
50
+ const opnames = Object.keys(entity.op || {})
51
51
  const hasLoad = opnames.includes('load')
52
52
  const hasList = opnames.includes('list')
53
53
 
@@ -157,7 +157,7 @@ dynamic unwrapListData(dynamic data) {
157
157
 
158
158
 
159
159
  function generateDirectLoad(model: Model, entity: ModelEntity) {
160
- const loadOp = entity.op.load
160
+ const loadOp = entity.op?.load
161
161
  const loadPoint: ModelPoint | undefined = loadOp?.points?.[0]
162
162
 
163
163
  if (null == loadPoint) {
@@ -197,7 +197,7 @@ function generateDirectLoad(model: Model, entity: ModelEntity) {
197
197
  .join('\n')
198
198
 
199
199
  // Get list info for live mode bootstrapping
200
- const listOp = entity.op.list
200
+ const listOp = entity.op?.list
201
201
  const listPoint = listOp?.points?.[0]
202
202
  const listParams = listPoint?.args?.params || []
203
203
  const listPath = listPoint ? normalizePathParams(listPoint.parts || [], listParams, listPoint.rename?.param) : ''
@@ -350,7 +350,7 @@ ${paramAsserts} }
350
350
 
351
351
 
352
352
  function generateDirectList(model: Model, entity: ModelEntity) {
353
- const listOp = entity.op.list
353
+ const listOp = entity.op?.list
354
354
  const listPoint: ModelPoint | undefined = listOp?.points?.[0]
355
355
 
356
356
  if (null == listPoint) {
@@ -29,7 +29,7 @@ import {
29
29
  File, Folder, Content,
30
30
  } from '@voxgig/sdkgen'
31
31
 
32
- import { opRequestShape, OP_SUFFIX, warnEntityTypeCollisions } from '@voxgig/sdkgen'
32
+ import { opRequestShape, OP_SUFFIX, warnEntityTypeCollisions, deriveEntityNames } from '@voxgig/sdkgen'
33
33
 
34
34
  import {
35
35
  KIT,
@@ -89,10 +89,9 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
89
89
  // each generated module carries @specs referencing its Types aliases, so an
90
90
  // alias is required for each or the project won't compile. Filter on `name`
91
91
  // (always present), NOT `active` — parity with the go emitter's fix.
92
- const entityList = each(entity).filter((e: any) => e && null != e.name)
92
+ const entityList = deriveEntityNames(entity)
93
93
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
94
94
  // entity not yet named would otherwise read `Name = undefined` below.
95
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
96
95
 
97
96
  // Surface duplicate generated type names (two entities with the same
98
97
  // PascalCase Name) — they would redeclare a type in statically-typed
@@ -23,7 +23,7 @@ const Entity = cmp(function Entity(props: any) {
23
23
  Folder({ name: 'entity' }, () => {
24
24
  File({ name: entity.name + '_entity.' + target.ext }, () => {
25
25
 
26
- const opnames = Object.keys(entity.op)
26
+ const opnames = Object.keys(entity.op || {})
27
27
 
28
28
  const opfrags =
29
29
  (['load', 'list', 'create', 'update', 'remove']
@@ -71,9 +71,18 @@ defmodule ${Name}.ReadmeExamplesTest do
71
71
  end)
72
72
  end
73
73
 
74
+ # True when every opening fence in \`text\` has a matching closing fence.
75
+ # Splitting on the fence yields (fence_count + 1) segments, so an even fence
76
+ # count (odd segment count) means all fences are paired. An unclosed
77
+ # \`\`\`elixir fence at EOF would otherwise leave a truncated/empty block that
78
+ # parses cleanly and slips through the syntax gate silently.
79
+ defp fences_balanced?(text) do
80
+ rem(length(String.split(text, @fence)) - 1, 2) == 0
81
+ end
82
+
74
83
  defp doc_blocks(path) do
75
84
  case File.read(path) do
76
- {:ok, text} -> {:present, elixir_blocks(text)}
85
+ {:ok, text} -> {:present, elixir_blocks(text), fences_balanced?(text)}
77
86
  _ -> :absent
78
87
  end
79
88
  end
@@ -85,7 +94,16 @@ defmodule ${Name}.ReadmeExamplesTest do
85
94
  :absent ->
86
95
  {fails, present, total}
87
96
 
88
- {:present, blocks} ->
97
+ {:present, blocks, balanced?} ->
98
+ # An unclosed fence is a malformed doc — fail rather than silently
99
+ # scanning past the truncated block.
100
+ fence_fails =
101
+ if balanced? do
102
+ []
103
+ else
104
+ [label <> " has an unclosed " <> @fence <> "elixir fence (odd fence count)"]
105
+ end
106
+
89
107
  block_fails =
90
108
  blocks
91
109
  |> Enum.with_index()
@@ -105,7 +123,7 @@ defmodule ${Name}.ReadmeExamplesTest do
105
123
  end
106
124
  end)
107
125
 
108
- {fails ++ block_fails, present + 1, total + length(blocks)}
126
+ {fails ++ fence_fails ++ block_fails, present + 1, total + length(blocks)}
109
127
  end
110
128
  end)
111
129
 
@@ -11,6 +11,7 @@ import {
11
11
  each,
12
12
  isAuthActive,
13
13
  resolveAuthPrefix,
14
+ serverVariables,
14
15
  } from '@voxgig/sdkgen'
15
16
 
16
17
 
@@ -25,6 +26,7 @@ import {
25
26
  import {
26
27
  clean,
27
28
  formatGoMap,
29
+ goFeatureName,
28
30
  } from './utility_go'
29
31
 
30
32
 
@@ -46,6 +48,14 @@ const Config = cmp(async function Config(props: any) {
46
48
  let baseUrl = ''
47
49
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
50
 
51
+ // Templated server URL: emit the spec's server-variable defaults so the
52
+ // runtime can substitute {name} placeholders in base (see make_options).
53
+ const svars = serverVariables(model)
54
+ const serverBlock = 0 === svars.length ? '' :
55
+ '\t\t\t"server": map[string]any{\n' +
56
+ svars.map((v: any) => `\t\t\t\t${JSON.stringify(v.name)}: ${JSON.stringify(v.dflt)},\n`).join('') +
57
+ '\t\t\t},\n'
58
+
49
59
  const authBlock = authActive
50
60
  ? ` "auth": map[string]any{
51
61
  "prefix": "${authPrefix}",
@@ -76,7 +86,7 @@ const Config = cmp(async function Config(props: any) {
76
86
  Content(` },
77
87
  "options": map[string]any{
78
88
  "base": "${baseUrl}",
79
- ${authBlock} "headers": ${formatGoMap(headers, 3)},
89
+ ${serverBlock}${authBlock} "headers": ${formatGoMap(headers, 3)},
80
90
  "entity": map[string]any{
81
91
  `)
82
92
 
@@ -102,7 +112,9 @@ func makeFeature(name string) Feature {
102
112
  `)
103
113
 
104
114
  each(feature, (f: any) => {
105
- const fname = f.name.charAt(0).toUpperCase() + f.name.slice(1)
115
+ // MUST match Main_go.ts, which DECLARES these identifiers in registry.go
116
+ // and the root init(); see goFeatureName.
117
+ const fname = goFeatureName(f)
106
118
  if (f.name !== 'base') {
107
119
  Content(` case "${f.name}":
108
120
  if New${fname}FeatureFunc != nil {
@@ -27,11 +27,11 @@
27
27
  // with every field optional (Go's analog of TS `Partial<Name>`).
28
28
 
29
29
  import {
30
- cmp, each, names,
30
+ cmp, each,
31
31
  File, Content, Folder,
32
32
  } from '@voxgig/sdkgen'
33
33
 
34
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
34
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
35
35
 
36
36
  import {
37
37
  KIT,
@@ -62,16 +62,105 @@ function goField(name: string): string {
62
62
  }
63
63
 
64
64
 
65
+ // The json struct tag is the WIRE CONTRACT: it decides the key requests
66
+ // marshal under and the key responses populate from.
67
+ //
68
+ // Can this spec-derived name be carried in a Go json struct tag AT ALL?
69
+ //
70
+ // It cannot always be. `encoding/json`'s isValidTag accepts letters, digits and
71
+ // a fixed punctuation set; a name containing a double quote, a backtick or a
72
+ // backslash is REJECTED, and Go then silently falls back to the Go FIELD name.
73
+ // Verified against go1.24 — for a field named `na"me`:
74
+ //
75
+ // raw literal `json:"na"me"` -> marshals as {"na": ...} (truncated)
76
+ // escaped literal "json:\"na\\\"me\"" -> marshals as {"NaMe": ...} (tag ignored)
77
+ //
78
+ // So there is no encoding that preserves such a name. An earlier version here
79
+ // stripped the offending characters, which is worse than either: it silently
80
+ // changed the wire contract, marshalling under a key the API never uses and
81
+ // never populating from the real response key. Names that cannot be
82
+ // represented are skipped with a warning instead — the same treatment
83
+ // csharp/java give names with no legal identifier form.
84
+ const GO_TAG_PUNCT = '!#$%&()*+-./:;<=>?@[]^_{|}~ '
85
+
86
+ function goTaggable(name: string): boolean {
87
+ const s = String(name)
88
+ if ('' === s) {
89
+ return false
90
+ }
91
+ for (const c of s) {
92
+ if (GO_TAG_PUNCT.includes(c)) continue
93
+ if (/[\p{L}\p{Nd}]/u.test(c)) continue
94
+ return false
95
+ }
96
+ return true
97
+ }
98
+
99
+
100
+ function goTag(name: string, omitempty: boolean): string {
101
+ const jsonName = String(name) + (omitempty ? ',omitempty' : '')
102
+ return '`json:"' + jsonName + '"`'
103
+ }
104
+
105
+
65
106
  // One Go struct field line. `optional` -> pointer + ,omitempty (Go's closest
66
- // analog to an optional/absent field).
67
- function fieldLine(name: string, sentinel: any, optional: boolean): string {
107
+ // analog to an optional/absent field). `ident` overrides the derived field
108
+ // identifier when goField() would collide with a sibling (see uniqueGoFields).
109
+ function fieldLine(name: string, sentinel: any, optional: boolean, ident?: string): string {
68
110
  const gt = canonToType(sentinel, LANG)
69
111
  const typ = optional ? ('*' + gt) : gt
70
- const tag = optional ? `\`json:"${name},omitempty"\`` : `\`json:"${name}"\``
71
- return `\t${goField(name)} ${typ} ${tag}\n`
112
+ return `\t${ident || goField(name)} ${typ} ${goTag(name, optional)}\n`
72
113
  }
73
114
 
74
115
 
116
+ // Collision-free Go field identifiers for one struct's members.
117
+ //
118
+ // goField() is lossy — it strips every non-alphanumeric character — so
119
+ // `some_field`, `some-field` and `someField` all map to `SomeField`. Emitting
120
+ // them into the same struct is a "field redeclared" compile error, and Go is
121
+ // the only target exposed to it (ts/py/… keep the raw key). Later duplicates
122
+ // get a numeric suffix; the FIRST occurrence keeps the natural name, and the
123
+ // json tag always keeps the original wire name, so behaviour is unchanged.
124
+ // Deterministic: members arrive in sorted-key order from opRequestShape/each.
125
+ function uniqueGoFields(members: { name: string }[]): string[] {
126
+ const taken: Record<string, boolean> = {}
127
+ return members.map((m) => {
128
+ const base = goField(m.name)
129
+ let ident = base
130
+ let n = 1
131
+ while (taken[ident]) {
132
+ n++
133
+ ident = base + n
134
+ }
135
+ taken[ident] = true
136
+ return ident
137
+ })
138
+ }
139
+
140
+
141
+ // Keep a member only when its name survives into a Go json tag; warn once per
142
+ // dropped member so the omission is visible rather than a silent contract change.
143
+ function skipUntaggable(member: any, typeName: string, log: any): boolean {
144
+ if (null == member || null == member.name) {
145
+ return false
146
+ }
147
+ if (goTaggable(member.name)) {
148
+ return true
149
+ }
150
+ if (log && log.warn) {
151
+ log.warn({
152
+ point: 'entity-types-skip-field', typeName, field: member.name,
153
+ note: `go: field "${member.name}" of ${typeName} cannot be carried in a ` +
154
+ `Go json struct tag (encoding/json rejects the name, and Go would ` +
155
+ `silently marshal under the field name instead); omitted from the ` +
156
+ `typed model (still reachable via the runtime map)`,
157
+ })
158
+ }
159
+ return false
160
+ }
161
+
162
+
163
+
75
164
  const EntityTypes = cmp(function EntityTypes(props: any) {
76
165
  const { target } = props
77
166
  const { model, log } = props.ctx$
@@ -84,14 +173,11 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
84
173
  // Emit for every entity that gets an entity file. Main_go.ts / Entity_go.ts
85
174
  // iterate entities WITHOUT an `active` filter and reference the typed data
86
175
  // type `<Name>` in every *_entity.go, so a struct is required for each or the
87
- // package won't compile. Filter on `name` (always present), NOT `Name`:
88
- // `Name` is the PascalCase variant derived LAZILY by `names()`, so filtering
89
- // on it silently drops any entity whose `Name` hasn't been derived yet by an
90
- // earlier component (order-dependent — e.g. fieldless placeholder entities),
91
- // producing `undefined: <Name>` in the generated Go. Derive `Name` here so
92
- // the struct set is deterministic and matches the *_entity.go set.
93
- const entityList = each(entity).filter((e: any) => e && null != e.name)
94
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
176
+ // package won't compile. deriveEntityNames() selects on `name` (always
177
+ // present) and derives the lazily-set PascalCase `Name` up front, so the
178
+ // struct set is deterministic and matches the *_entity.go set regardless of
179
+ // which component runs first.
180
+ const entityList = deriveEntityNames(entity)
95
181
 
96
182
  // Surface duplicate generated type names (two entities with the same
97
183
  // PascalCase Name) — they would redeclare a type in statically-typed
@@ -123,8 +209,11 @@ import "encoding/json"
123
209
  Content(`// ${Name} is the typed data model for the ${ent.name} entity.
124
210
  type ${Name} struct {
125
211
  `)
126
- fields.forEach((f: any) => {
127
- Content(fieldLine(f.name, f.type, false === f.req))
212
+ const taggable = fields.filter(
213
+ (f: any) => skipUntaggable(f, Name, log))
214
+ const fieldIdents = uniqueGoFields(taggable)
215
+ taggable.forEach((f: any, i: number) => {
216
+ Content(fieldLine(f.name, f.type, false === f.req, fieldIdents[i]))
128
217
  })
129
218
  Content(`}
130
219
 
@@ -146,8 +235,11 @@ type ${Name} struct {
146
235
  Content(`// ${typeName} is the typed request payload for ${Name}.${cap(opname)}Typed.
147
236
  type ${typeName} struct {
148
237
  `)
149
- items.forEach((it: any) => {
150
- Content(fieldLine(it.name, it.type, it.optional))
238
+ const taggableItems = items.filter(
239
+ (it: any) => skipUntaggable(it, typeName, log))
240
+ const itemIdents = uniqueGoFields(taggableItems)
241
+ taggableItems.forEach((it: any, i: number) => {
242
+ Content(fieldLine(it.name, it.type, it.optional, itemIdents[i]))
151
243
  })
152
244
  Content(`}
153
245
 
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, each, camelify, names,
6
6
  File, Content, Folder, Fragment, Line, FeatureHook, Slot,
7
- entityClassName,
7
+ entityClassName, entityCollection,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
  import {
@@ -26,7 +26,7 @@ const Entity = cmp(function Entity(props: any) {
26
26
  // Collision-free entity CLASS name (see entityClassName): normally
27
27
  // `<Name>Entity`, but disambiguated (e.g. `<Name>EntityClient`) when it would
28
28
  // clash with another entity's data-type name. The DATA type stays `<Name>`.
29
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
29
+ const entityColl = entityCollection(model)
30
30
  const cls = entityClassName(entity, entityColl)
31
31
 
32
32
  const entrep = {
@@ -42,7 +42,7 @@ const Entity = cmp(function Entity(props: any) {
42
42
 
43
43
  File({ name: entity.name + '_entity.' + target.ext }, () => {
44
44
 
45
- const opnames = Object.keys(entity.op)
45
+ const opnames = Object.keys(entity.op || {})
46
46
 
47
47
  // For each CRUD op: if the spec defines it, splice in the real
48
48
  // implementation. Otherwise emit a stub that satisfies the static
@@ -4,7 +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
- entityClassName,
7
+ entityClassName, entityCollection,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
 
@@ -19,6 +19,7 @@ import {
19
19
  } from '@voxgig/apidef'
20
20
 
21
21
 
22
+ import { goFeatureName } from './utility_go'
22
23
  import { Package } from './Package_go'
23
24
  import { Config } from './Config_go'
24
25
  import { Gitignore } from './Gitignore_go'
@@ -82,7 +83,7 @@ const Main = cmp(async function Main(props: any) {
82
83
 
83
84
  '#BuildFeatures': ({ indent }: any) => {
84
85
  each(feature, (feat: any) => {
85
- const fname = feat.name.charAt(0).toUpperCase() + feat.name.slice(1)
86
+ const fname = goFeatureName(feat)
86
87
  Content({ indent }, `u.FeatureAdd(s.rootctx, New${fname}FeatureFunc())
87
88
  `)
88
89
  })
@@ -119,7 +120,7 @@ var NewBaseFeatureFunc func() Feature
119
120
  // Feature constructor function vars (non-base)
120
121
  each(feature, (feat: any) => {
121
122
  if (feat.name !== 'base') {
122
- const fname = feat.name.charAt(0).toUpperCase() + feat.name.slice(1)
123
+ const fname = goFeatureName(feat)
123
124
  Content(`var New${fname}FeatureFunc func() Feature
124
125
 
125
126
  `)
@@ -177,7 +178,7 @@ func init() {
177
178
  // Register non-base feature constructors
178
179
  each(feature, (feat: any) => {
179
180
  if (feat.name !== 'base') {
180
- const fname = feat.name.charAt(0).toUpperCase() + feat.name.slice(1)
181
+ const fname = goFeatureName(feat)
181
182
  Content(` core.New${fname}FeatureFunc = func() core.Feature {
182
183
  return feature.New${fname}Feature()
183
184
  }
@@ -188,7 +189,7 @@ func init() {
188
189
  // Register entity constructors
189
190
  each(entity, (ent: any) => {
190
191
  Content(` core.New${ent.Name}EntityFunc = func(client *core.${model.const.Name}SDK, entopts map[string]any) core.${model.const.Name}Entity {
191
- return entity.New${entityClassName(ent, entity)}(client, entopts)
192
+ return entity.New${entityClassName(ent, entityCollection(model))}(client, entopts)
192
193
  }
193
194
  `)
194
195
  })
@@ -219,7 +220,7 @@ func Test() *${model.const.Name}SDK { return TestSDK(nil, nil) }
219
220
 
220
221
  each(feature, (feat: any) => {
221
222
  if (feat.name !== 'base') {
222
- const fname = feat.name.charAt(0).toUpperCase() + feat.name.slice(1)
223
+ const fname = goFeatureName(feat)
223
224
  Content(`var New${fname}Feature = feature.New${fname}Feature
224
225
  `)
225
226
  }
@@ -31,7 +31,7 @@ go 1.20
31
31
 
32
32
  const deps: Record<string, string> = {}
33
33
  const replaceDirs: Record<string, string> = {}
34
- for (const d of collectDeps(model, target.name, target.deps)) {
34
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
35
35
  // Target-level deps default to 'v0.0.0' when version is absent;
36
36
  // feature deps require an explicit version.
37
37
  deps[d.name] = d.source === 'target' ? (d.version || 'v0.0.0') : d.version
@@ -75,7 +75,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
75
75
  ? `\n\t\t\t"apikey": env["${PROJECTNAME}_APIKEY"],`
76
76
  : ''
77
77
 
78
- const opnames = Object.keys(entity.op)
78
+ const opnames = Object.keys(entity.op || {})
79
79
  const hasLoad = opnames.includes('load')
80
80
  const hasList = opnames.includes('list')
81
81
 
@@ -83,8 +83,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
83
83
  return
84
84
  }
85
85
 
86
- const loadOp = entity.op.load
87
- const listOp = entity.op.list
86
+ const loadOp = entity.op?.load
87
+ const listOp = entity.op?.list
88
88
 
89
89
  // Get load point info
90
90
  const loadPoint = loadOp?.points?.[0]