@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
@@ -70,7 +70,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
70
70
  ? `\n apikey = env["${PROJECTNAME}_APIKEY"],`
71
71
  : ''
72
72
 
73
- const opnames = Object.keys(entity.op)
73
+ const opnames = Object.keys(entity.op || {})
74
74
  const hasLoad = opnames.includes('load')
75
75
  const hasList = opnames.includes('list')
76
76
 
@@ -78,8 +78,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
78
78
  return
79
79
  }
80
80
 
81
- const loadOp = entity.op.load
82
- const listOp = entity.op.list
81
+ const loadOp = entity.op?.load
82
+ const listOp = entity.op?.list
83
83
 
84
84
  const loadPoint = loadOp?.points?.[0]
85
85
  const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
@@ -50,7 +50,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
50
50
  // it only applies to entities that declare a list op. Others (e.g. Batch =
51
51
  // create/load) have no list endpoint — make_point errors and the stream
52
52
  // yields nothing — so skip the stream test for them.
53
- const hasList = !!(entity.op && (entity.op as any).list)
53
+ const hasList = !!(entity.op && (entity.op as any)?.list)
54
54
 
55
55
  const basicflow: ModelEntityFlow | undefined =
56
56
  getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
@@ -45,8 +45,11 @@ let read_file (path : string) : string option =
45
45
 
46
46
  let split_lines (s : string) : string list = String.split_on_char '\\n' s
47
47
 
48
- (* Extract the body of every \`\`\`ocaml ... \`\`\` fenced block from markdown. *)
49
- let extract_ocaml_blocks (md : string) : string list =
48
+ (* Extract the body of every \`\`\`ocaml ... \`\`\` fenced block from markdown.
49
+ * Returns (blocks, unclosed) where unclosed is true when a fence was opened
50
+ * but never closed before EOF — a malformed doc that must fail rather than
51
+ * silently drop the unterminated block. *)
52
+ let extract_ocaml_blocks (md : string) : string list * bool =
50
53
  let blocks = ref [] in
51
54
  let cur = ref [] in
52
55
  let in_block = ref false in
@@ -61,23 +64,31 @@ let extract_ocaml_blocks (md : string) : string list =
61
64
  end
62
65
  else cur := line :: !cur)
63
66
  (split_lines md);
64
- List.rev !blocks
67
+ (List.rev !blocks, !in_block)
65
68
 
66
- (* Balanced (), [], {} outside of string literals. Naive quote toggle: the
67
- * generated snippets never embed an escaped quote inside a string. *)
69
+ (* Balanced (), [], {} outside of string literals, with each closer matching
70
+ * the most-recent opener (so "([)]" is rejected, not just counted). Naive
71
+ * quote toggle: the generated snippets never embed an escaped quote inside a
72
+ * string. *)
68
73
  let is_balanced (block : string) : bool =
69
74
  let in_str = ref false in
70
- let depth = ref 0 in
75
+ let stack = ref [] in
71
76
  let ok = ref true in
72
77
  String.iter (fun c ->
73
- if c = '"' then in_str := not !in_str
74
- else if not !in_str then
75
- match c with
76
- | '(' | '[' | '{' -> incr depth
77
- | ')' | ']' | '}' -> decr depth; if !depth < 0 then ok := false
78
- | _ -> ())
78
+ if !ok then
79
+ if c = '"' then in_str := not !in_str
80
+ else if not !in_str then
81
+ match c with
82
+ | '(' -> stack := ')' :: !stack
83
+ | '[' -> stack := ']' :: !stack
84
+ | '{' -> stack := '}' :: !stack
85
+ | ')' | ']' | '}' ->
86
+ (match !stack with
87
+ | top :: rest when top = c -> stack := rest
88
+ | _ -> ok := false)
89
+ | _ -> ())
79
90
  block;
80
- !ok && !depth = 0 && not !in_str
91
+ !ok && !stack = [] && not !in_str
81
92
 
82
93
  let docs = [
83
94
  ("root README", "../README.md");
@@ -92,13 +103,17 @@ let () =
92
103
  match read_file path with
93
104
  | None -> () (* doc not generated in this run — tolerate *)
94
105
  | Some src ->
106
+ let (blocks, unclosed) = extract_ocaml_blocks src in
107
+ (* A fence opened but never closed is a malformed doc — fail
108
+ * rather than silently skipping the unterminated block. *)
109
+ check (label ^ " ocaml fence closed") (not unclosed);
95
110
  List.iteri (fun i block ->
96
111
  check (label ^ " block " ^ string_of_int (i + 1) ^ " non-empty")
97
112
  (trim block <> "");
98
113
  check (label ^ " block " ^ string_of_int (i + 1) ^ " balanced delimiters")
99
114
  (is_balanced block);
100
115
  incr total)
101
- (extract_ocaml_blocks src))
116
+ blocks)
102
117
  docs;
103
118
  check "at least one ocaml code block across the docs" (!total > 0))
104
119
  `)
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
4
4
  import {
5
5
  cmp, camelify, names,
6
6
  File, Folder, Fragment,
7
- entityClassName,
7
+ entityClassName, entityCollection,
8
8
  } from '@voxgig/sdkgen'
9
9
 
10
10
  import {
@@ -22,7 +22,7 @@ const Entity = cmp(function Entity(props: any) {
22
22
  // Collision-free entity CLASS name (see entityClassName): normally
23
23
  // `<Name>Entity`, disambiguated when it would clash with another entity's
24
24
  // data-type name. The snake-cased source-file name is unaffected.
25
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
25
+ const entityColl = entityCollection(model)
26
26
  const cls = entityClassName(entity, entityColl)
27
27
 
28
28
  const entrep = {
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
38
38
 
39
39
  File({ name: entity.name + '_entity.pm' }, () => {
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,
@@ -14,7 +14,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
14
14
 
15
15
  // Return the collision-free class (entityClassName); the accessor METHOD
16
16
  // name (entity.Name) is unchanged so callers still write $client->{Name}.
17
- const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
17
+ const cls = entityClassName(entity, entityCollection(model))
18
18
 
19
19
  Content(`
20
20
  # Canonical facade: $client->${entity.Name}->list / ->load({ 'id' => ... })
@@ -27,7 +27,7 @@ const Package = cmp(async function Package(props: any) {
27
27
  // (e.g. "Voxgig::SDK::Solar" -> dist voxgig-sdk-solar).
28
28
  const Name = model.const.Name
29
29
 
30
- const deps = collectDeps(model, target.name, target.deps)
30
+ const deps = collectDeps(model, target.name, target.deps, ctx$.log)
31
31
  const prereq = deps
32
32
  .map((d: any) => ` '${d.name}' => '${d.version || '0'}',`)
33
33
  .join('\n')
@@ -80,7 +80,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
80
80
  ? `\n 'apikey' => $env->{'${PROJECTNAME}_APIKEY'},`
81
81
  : ''
82
82
 
83
- const opnames = Object.keys(entity.op)
83
+ const opnames = Object.keys(entity.op || {})
84
84
  const hasLoad = opnames.includes('load')
85
85
  const hasList = opnames.includes('list')
86
86
 
@@ -88,8 +88,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
88
88
  return
89
89
  }
90
90
 
91
- const loadOp = entity.op.load
92
- const listOp = entity.op.list
91
+ const loadOp = entity.op?.load
92
+ const listOp = entity.op?.list
93
93
 
94
94
  const loadPoint = loadOp?.points?.[0]
95
95
  const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
@@ -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
 
@@ -46,6 +47,16 @@ const Config = cmp(async function Config(props: any) {
46
47
  let baseUrl = ''
47
48
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
49
 
50
+ // Templated server URL: emit the spec's server-variable defaults so the
51
+ // runtime can substitute {name} placeholders in base (see MakeOptions).
52
+ // `$` is escaped so a default can never open a PHP interpolation.
53
+ const svars = serverVariables(model)
54
+ const phps = (s: string) => JSON.stringify(s).replace(/\$/g, '\\$')
55
+ const serverBlock = 0 === svars.length ? '' :
56
+ ' "server" => [\n' +
57
+ svars.map((v: any) => ` ${phps(v.name)} => ${phps(v.dflt)},\n`).join('') +
58
+ ' ],\n'
59
+
49
60
  const authBlock = authActive
50
61
  ? ` "auth" => [
51
62
  "prefix" => "${authPrefix}",
@@ -84,7 +95,7 @@ class ${model.const.Name}Config
84
95
  Content(` ],
85
96
  "options" => [
86
97
  "base" => "${baseUrl}",
87
- ${authBlock} "headers" => ${formatPhpArray(headers, 4)},
98
+ ${serverBlock}${authBlock} "headers" => ${formatPhpArray(headers, 4)},
88
99
  "entity" => (object)[],
89
100
  ],
90
101
  "entity" => (object)[],
@@ -94,7 +105,7 @@ ${authBlock} "headers" => ${formatPhpArray(headers, 4)},
94
105
  Content(` ],
95
106
  "options" => [
96
107
  "base" => "${baseUrl}",
97
- ${authBlock} "headers" => ${formatPhpArray(headers, 4)},
108
+ ${serverBlock}${authBlock} "headers" => ${formatPhpArray(headers, 4)},
98
109
  "entity" => [
99
110
  `)
100
111
 
@@ -29,7 +29,7 @@ import {
29
29
  File, Content, Folder,
30
30
  } from '@voxgig/sdkgen'
31
31
 
32
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
32
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
33
33
 
34
34
  import {
35
35
  KIT,
@@ -79,11 +79,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
79
79
  // filter, so inactive entities still get class files referencing these
80
80
  // typed names. Filter on `name` (always present), NOT `active` — parity
81
81
  // with the go emitter's fix.
82
- const entityList = each(entity).filter((e: any) => e && null != e.name)
82
+ const entityList = deriveEntityNames(entity)
83
83
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
84
84
  // entity not yet named (e.g. a fieldless placeholder) would otherwise read
85
85
  // `Name = undefined` below. Parity with the go emitter's fix.
86
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
87
86
 
88
87
  // Surface duplicate generated type names (two entities with the same
89
88
  // 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, 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 {
@@ -22,7 +22,7 @@ const Entity = cmp(function Entity(props: any) {
22
22
  // Collision-free entity CLASS name (see entityClassName): normally
23
23
  // `<Name>Entity`, disambiguated when it would clash with another entity's
24
24
  // data-type name. The snake-cased source-file name is unaffected.
25
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
25
+ const entityColl = entityCollection(model)
26
26
  const cls = entityClassName(entity, entityColl)
27
27
 
28
28
  const entrep = {
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
38
38
 
39
39
  File({ name: entity.name + '_entity.' + 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,
@@ -20,7 +20,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
20
20
 
21
21
  // Collision-free entity CLASS name (entityClassName); the accessor METHOD name
22
22
  // (below) is unchanged so callers still write $client->${entity.Name}().
23
- const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
23
+ const cls = entityClassName(entity, entityCollection(model))
24
24
 
25
25
  // Canonical facade method name is the PascalCase entity Name
26
26
  // (`$client->${entity.Name}()`). PHP method names are case-insensitive, so
@@ -49,7 +49,7 @@ const Package = cmp(async function Package(props: any) {
49
49
  "require": {
50
50
  "php": ">=8.2"`)
51
51
 
52
- for (const d of collectDeps(model, target.name, target.deps)) {
52
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
53
53
  const v = d.source === 'target' ? (d.version || '0.0') : d.version
54
54
  Content(`,
55
55
  "${d.name}": "^${v}"`)
@@ -70,7 +70,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
70
70
  ? `\n "apikey" => $env["${PROJECTNAME}_APIKEY"],`
71
71
  : ''
72
72
 
73
- const opnames = Object.keys(entity.op)
73
+ const opnames = Object.keys(entity.op || {})
74
74
  const hasLoad = opnames.includes('load')
75
75
  const hasList = opnames.includes('list')
76
76
 
@@ -78,8 +78,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
78
78
  return
79
79
  }
80
80
 
81
- const loadOp = entity.op.load
82
- const listOp = entity.op.list
81
+ const loadOp = entity.op?.load
82
+ const listOp = entity.op?.list
83
83
 
84
84
  const loadPoint = loadOp?.points?.[0]
85
85
  const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
@@ -53,7 +53,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
53
53
  // it only applies to entities that declare a list op. Others (e.g. Batch =
54
54
  // create/load) have no list endpoint — make_point errors and the stream
55
55
  // yields nothing — so skip the stream test for them.
56
- const hasList = !!(entity.op && (entity.op as any).list)
56
+ const hasList = !!(entity.op && (entity.op as any)?.list)
57
57
 
58
58
  const basicflow: ModelEntityFlow | undefined =
59
59
  getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
@@ -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
 
@@ -46,6 +47,14 @@ const Config = cmp(async function Config(props: any) {
46
47
  let baseUrl = ''
47
48
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
49
 
50
+ // Templated server URL: emit the spec's server-variable defaults so the
51
+ // runtime can substitute {name} placeholders in base (see make_options).
52
+ const svars = serverVariables(model)
53
+ const serverBlock = 0 === svars.length ? '' :
54
+ ' "server": {\n' +
55
+ svars.map((v: any) => ` ${JSON.stringify(v.name)}: ${JSON.stringify(v.dflt)},\n`).join('') +
56
+ ' },\n'
57
+
49
58
  const authBlock = authActive
50
59
  ? ` "auth": {
51
60
  "prefix": "${authPrefix}",
@@ -74,7 +83,7 @@ def make_config():
74
83
  Content(` },
75
84
  "options": {
76
85
  "base": "${baseUrl}",
77
- ${authBlock} "headers": ${formatPyDict(headers, 3)},
86
+ ${serverBlock}${authBlock} "headers": ${formatPyDict(headers, 3)},
78
87
  "entity": {
79
88
  `)
80
89
 
@@ -36,7 +36,7 @@ import {
36
36
  File, Content,
37
37
  } from '@voxgig/sdkgen'
38
38
 
39
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
39
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
40
40
 
41
41
  import {
42
42
  KIT,
@@ -142,11 +142,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
142
142
  // filter, so inactive entities still get class files referencing these
143
143
  // typed names. Filter on `name` (always present), NOT `active` — parity
144
144
  // with the go emitter's fix.
145
- const entityList = each(entity).filter((e: any) => e && null != e.name)
145
+ const entityList = deriveEntityNames(entity)
146
146
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
147
147
  // entity not yet named (e.g. a fieldless placeholder) would otherwise read
148
148
  // `Name = undefined` below. Parity with the go emitter's fix.
149
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
150
149
 
151
150
  // Surface duplicate generated type names (two entities with the same
152
151
  // 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, each, camelify, names,
6
6
  File, Content, Folder, Fragment, Line, FeatureHook, Slot,
7
- entityClassName, opTypeName,
7
+ entityClassName, entityCollection, opTypeName,
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 DATA type stays `<Name>`. The snake-cased source-file
25
25
  // name is unaffected; only the class identifier changes.
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 = {
@@ -53,7 +53,7 @@ const Entity = cmp(function Entity(props: any) {
53
53
 
54
54
  File({ name: entity.name + '_entity.' + target.ext }, () => {
55
55
 
56
- const opnames = Object.keys(entity.op)
56
+ const opnames = Object.keys(entity.op || {})
57
57
 
58
58
  const opfrags =
59
59
  (['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 (entityClassName); the accessor METHOD name
16
16
  // (entity.Name) is unchanged so callers still write client.<Name>(). The
17
17
  // source module is snake-cased, so only the imported class identifier changes.
18
- const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
18
+ const cls = entityClassName(entity, entityCollection(model))
19
19
 
20
20
  Content(`
21
21
  def ${entity.Name}(self, data=None) -> "${cls}":
@@ -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
 
@@ -94,7 +94,7 @@ from typing import TYPE_CHECKING
94
94
  if TYPE_CHECKING:
95
95
  `)
96
96
  each(entity, (ent: any) => {
97
- Content(` from entity.${ent.name}_entity import ${entityClassName(ent, entity)}
97
+ Content(` from entity.${ent.name}_entity import ${entityClassName(ent, entityCollection(model))}
98
98
  `)
99
99
  })
100
100
  }
@@ -47,7 +47,7 @@ dependencies = [
47
47
  `)
48
48
 
49
49
  const seen = new Set<string>()
50
- for (const d of collectDeps(model, target.name, target.deps)) {
50
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
51
51
  if (seen.has(d.name)) continue
52
52
  seen.add(d.name)
53
53
  const v = d.source === 'target' ? (d.version || '0.0') : d.version
@@ -70,7 +70,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
70
70
  ? `\n "apikey": env.get("${PROJECTNAME}_APIKEY"),`
71
71
  : ''
72
72
 
73
- const opnames = Object.keys(entity.op)
73
+ const opnames = Object.keys(entity.op || {})
74
74
  const hasLoad = opnames.includes('load')
75
75
  const hasList = opnames.includes('list')
76
76
 
@@ -78,8 +78,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
78
78
  return
79
79
  }
80
80
 
81
- const loadOp = entity.op.load
82
- const listOp = entity.op.list
81
+ const loadOp = entity.op?.load
82
+ const listOp = entity.op?.list
83
83
 
84
84
  const loadPoint = loadOp?.points?.[0]
85
85
  const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
@@ -82,7 +82,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
82
82
  // it only applies to entities that actually declare a `list` op. Others
83
83
  // (e.g. Batch = create/load) have no list endpoint — make_point would error
84
84
  // and the stream would yield nothing — so skip the test for them.
85
- const hasList = !!(entity.op && (entity.op as any).list)
85
+ const hasList = !!(entity.op && (entity.op as any)?.list)
86
86
 
87
87
  File({ name: 'test_' + entity.name + '_entity.' + target.ext }, () => {
88
88
 
@@ -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
 
@@ -46,6 +47,16 @@ const Config = cmp(async function Config(props: any) {
46
47
  let baseUrl = ''
47
48
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
48
49
 
50
+ // Templated server URL: emit the spec's server-variable defaults so the
51
+ // runtime can substitute {name} placeholders in base (see make_options).
52
+ // `#` is escaped so a default can never open a Ruby interpolation.
53
+ const svars = serverVariables(model)
54
+ const rbs = (s: string) => JSON.stringify(s).replace(/#/g, '\\#')
55
+ const serverBlock = 0 === svars.length ? '' :
56
+ ' "server" => {\n' +
57
+ svars.map((v: any) => ` ${rbs(v.name)} => ${rbs(v.dflt)},\n`).join('') +
58
+ ' },\n'
59
+
49
60
  const authBlock = authActive
50
61
  ? ` "auth" => {
51
62
  "prefix" => "${authPrefix}",
@@ -74,7 +85,7 @@ module ${model.const.Name}Config
74
85
  Content(` },
75
86
  "options" => {
76
87
  "base" => "${baseUrl}",
77
- ${authBlock} "headers" => ${formatRubyHash(headers, 4)},
88
+ ${serverBlock}${authBlock} "headers" => ${formatRubyHash(headers, 4)},
78
89
  "entity" => {
79
90
  `)
80
91
 
@@ -12,7 +12,10 @@
12
12
  // $STRING -> String, $INTEGER -> Integer, $NUMBER -> Float, $BOOLEAN -> Boolean,
13
13
  // $OBJECT -> Hash, $ARRAY -> Array, unknown -> Object.
14
14
  //
15
- // Type-name scheme (IDENTICAL to TS): data type = <Name>; per-op request
15
+ // Type-name scheme (as TS, with one Ruby-only guard): data type = <Name>,
16
+ // except where Ruby core already owns that constant — `File`, `Time`, `Data`,
17
+ // … — which would be silently REPLACED by the assignment; those become
18
+ // <Name>Type (see rbSafeTypeName). Per-op request
16
19
  // <Name>LoadMatch / <Name>ListMatch / <Name>RemoveMatch (query/id ops),
17
20
  // <Name>CreateData / <Name>UpdateData (body ops). An op WITH params -> a Struct of
18
21
  // those params (reqd:false -> annotated `[Type, nil]`). An op WITHOUT params -> a
@@ -27,7 +30,7 @@ import {
27
30
  File, Content,
28
31
  } from '@voxgig/sdkgen'
29
32
 
30
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
33
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames, rbSafeTypeName } from '@voxgig/sdkgen'
31
34
 
32
35
  import {
33
36
  KIT,
@@ -89,11 +92,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
89
92
  // filter, so inactive entities still get class files referencing these
90
93
  // typed names. Filter on `name` (always present), NOT `active` — parity
91
94
  // with the go emitter's fix.
92
- const entityList = each(entity).filter((e: any) => e && null != e.name)
95
+ const entityList = deriveEntityNames(entity)
93
96
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
94
97
  // entity not yet named (e.g. a fieldless placeholder) would otherwise read
95
98
  // `Name = undefined` below. Parity with the go emitter's fix.
96
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
97
99
 
98
100
  // Surface duplicate generated type names (two entities with the same
99
101
  // PascalCase Name) — they would redeclare a type in statically-typed
@@ -116,12 +118,20 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
116
118
 
117
119
  entityList.forEach((ent: any) => {
118
120
  const Name = ent.Name
121
+ // Ruby constants share ONE namespace with the core classes, and
122
+ // assigning one silently replaces it — an entity named `File` emitting
123
+ // `File = Struct.new(...)` clobbers ::File and every later `File.join`
124
+ // raises NoMethodError. Only the bare data-type constant needs the
125
+ // guard; the per-op names below always carry a suffix that no core
126
+ // constant matches. The `client.File(...)` accessor is a METHOD, a
127
+ // separate namespace, so the public surface is unchanged.
128
+ const TypeName = rbSafeTypeName(Name)
119
129
  const fields = (ent.fields ? each(ent.fields) : [])
120
130
  .filter((f: any) => f.active !== false)
121
131
 
122
132
  // Entity data model: one member per field (`req:false` -> nilable).
123
133
  emitStruct(
124
- Name,
134
+ TypeName,
125
135
  `${Name} entity data model.`,
126
136
  fields.map((f: any) => ({ name: f.name, type: f.type, optional: false === f.req })),
127
137
  )
@@ -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 {
@@ -22,7 +22,7 @@ const Entity = cmp(function Entity(props: any) {
22
22
  // Collision-free entity CLASS name (see entityClassName): normally
23
23
  // `<Name>Entity`, disambiguated when it would clash with another entity's
24
24
  // data-type name. The snake-cased source-file name is unaffected.
25
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
25
+ const entityColl = entityCollection(model)
26
26
  const cls = entityClassName(entity, entityColl)
27
27
 
28
28
  const entrep = {
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
38
38
 
39
39
  File({ name: entity.name + '_entity.' + 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,
@@ -14,7 +14,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
14
14
 
15
15
  // Return the collision-free class (entityClassName); the accessor METHOD name
16
16
  // (entity.Name) is unchanged so callers still write client.<Name>.
17
- const cls = entityClassName(entity, getModelPath(model, `main.${KIT}.entity`))
17
+ const cls = entityClassName(entity, entityCollection(model))
18
18
 
19
19
  Content(`
20
20
  # Canonical facade: client.${entity.Name}.list / client.${entity.Name}.load({ "id" => ... })
@@ -39,7 +39,7 @@ gemspec
39
39
 
40
40
  `)
41
41
 
42
- for (const d of collectDeps(model, target.name, target.deps)) {
42
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
43
43
  Content(`gem "${d.name}", "~> ${versionOf(d)}"
44
44
  `)
45
45
  }
@@ -51,7 +51,7 @@ gemspec
51
51
  // twice (Gem::InvalidSpecificationException at `gem build`), so the
52
52
  // unconstrained json fallback is only emitted when the model's own
53
53
  // dependency list doesn't already declare json.
54
- const deps = collectDeps(model, target.name, target.deps)
54
+ const deps = collectDeps(model, target.name, target.deps, ctx$.log)
55
55
  const hasJson = deps.some((d: any) => 'json' === d.name)
56
56
 
57
57
  Content(`Gem::Specification.new do |spec|