@voxgig/sdkgen 1.3.17 → 1.3.18

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 (220) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Entity.js +5 -1
  3. package/dist/cmp/Entity.js.map +1 -1
  4. package/dist/cmp/Feature.js +2 -1
  5. package/dist/cmp/Feature.js.map +1 -1
  6. package/dist/helpers/canonType.js +8 -3
  7. package/dist/helpers/canonType.js.map +1 -1
  8. package/dist/helpers/collectDeps.d.ts +1 -1
  9. package/dist/helpers/collectDeps.js +36 -13
  10. package/dist/helpers/collectDeps.js.map +1 -1
  11. package/dist/helpers/naming.d.ts +3 -1
  12. package/dist/helpers/naming.js +53 -0
  13. package/dist/helpers/naming.js.map +1 -1
  14. package/dist/helpers/opShape.d.ts +3 -1
  15. package/dist/helpers/opShape.js +87 -8
  16. package/dist/helpers/opShape.js.map +1 -1
  17. package/dist/sdkgen.d.ts +3 -3
  18. package/dist/sdkgen.js +6 -2
  19. package/dist/sdkgen.js.map +1 -1
  20. package/dist/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +15 -10
  22. package/project/.sdk/model/target/cpp.aontu +4 -4
  23. package/project/.sdk/model/target/go-cli.aontu +17 -17
  24. package/project/.sdk/model/target/go-mcp.aontu +11 -11
  25. package/project/.sdk/model/target/go.aontu +1 -1
  26. package/project/.sdk/model/target/java.aontu +4 -4
  27. package/project/.sdk/model/target/kotlin.aontu +4 -4
  28. package/project/.sdk/model/target/lean.aontu +51 -0
  29. package/project/.sdk/model/target/scala.aontu +4 -4
  30. package/project/.sdk/src/cmp/c/EntityTypes_c.ts +3 -4
  31. package/project/.sdk/src/cmp/c/Entity_c.ts +3 -3
  32. package/project/.sdk/src/cmp/c/ReadmeExamplesTest_c.ts +24 -7
  33. package/project/.sdk/src/cmp/c/TestDirect_c.ts +3 -3
  34. package/project/.sdk/src/cmp/c/TestEntity_c.ts +1 -1
  35. package/project/.sdk/src/cmp/clojure/Entity_clojure.ts +1 -1
  36. package/project/.sdk/src/cmp/clojure/ReadmeExamplesTest_clojure.ts +19 -8
  37. package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +2 -2
  38. package/project/.sdk/src/cmp/cpp/Entity_cpp.ts +3 -3
  39. package/project/.sdk/src/cmp/cpp/MainEntity_cpp.ts +2 -2
  40. package/project/.sdk/src/cmp/cpp/ReadmeExamplesTest_cpp.ts +136 -0
  41. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +3 -3
  42. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -0
  43. package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +2 -3
  44. package/project/.sdk/src/cmp/csharp/Entity_csharp.ts +3 -3
  45. package/project/.sdk/src/cmp/csharp/MainEntity_csharp.ts +2 -2
  46. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
  47. package/project/.sdk/src/cmp/csharp/ReadmeExamplesTest_csharp.ts +13 -1
  48. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +3 -3
  49. package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +2 -3
  50. package/project/.sdk/src/cmp/dart/Entity_dart.ts +3 -3
  51. package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +2 -2
  52. package/project/.sdk/src/cmp/dart/Main_dart.ts +3 -3
  53. package/project/.sdk/src/cmp/dart/Package_dart.ts +1 -1
  54. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +4 -4
  55. package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +2 -3
  56. package/project/.sdk/src/cmp/elixir/Entity_elixir.ts +1 -1
  57. package/project/.sdk/src/cmp/elixir/ReadmeExamplesTest_elixir.ts +21 -3
  58. package/project/.sdk/src/cmp/go/Config_go.ts +4 -1
  59. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +110 -18
  60. package/project/.sdk/src/cmp/go/Entity_go.ts +3 -3
  61. package/project/.sdk/src/cmp/go/Main_go.ts +7 -6
  62. package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
  63. package/project/.sdk/src/cmp/go/TestDirect_go.ts +3 -3
  64. package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
  65. package/project/.sdk/src/cmp/go/utility_go.ts +25 -0
  66. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +9 -1
  67. package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +9 -1
  68. package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +28 -18
  69. package/project/.sdk/src/cmp/java/EntityTypes_java.ts +2 -3
  70. package/project/.sdk/src/cmp/java/Entity_java.ts +3 -3
  71. package/project/.sdk/src/cmp/java/MainEntity_java.ts +2 -2
  72. package/project/.sdk/src/cmp/java/Package_java.ts +1 -1
  73. package/project/.sdk/src/cmp/java/ReadmeExamplesTest_java.ts +23 -1
  74. package/project/.sdk/src/cmp/java/TestDirect_java.ts +3 -3
  75. package/project/.sdk/src/cmp/js/EntityTypes_js.ts +2 -3
  76. package/project/.sdk/src/cmp/js/Entity_js.ts +3 -3
  77. package/project/.sdk/src/cmp/js/MainEntity_js.ts +2 -2
  78. package/project/.sdk/src/cmp/js/Main_js.ts +2 -2
  79. package/project/.sdk/src/cmp/js/TestDirect_js.ts +4 -4
  80. package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +2 -3
  81. package/project/.sdk/src/cmp/kotlin/Entity_kotlin.ts +3 -3
  82. package/project/.sdk/src/cmp/kotlin/MainEntity_kotlin.ts +2 -2
  83. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +1 -1
  84. package/project/.sdk/src/cmp/kotlin/ReadmeExamplesTest_kotlin.ts +27 -2
  85. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +3 -3
  86. package/project/.sdk/src/cmp/lean/Config_lean.ts +88 -0
  87. package/project/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  88. package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  89. package/project/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  90. package/project/.sdk/src/cmp/lean/Package_lean.ts +68 -0
  91. package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  92. package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  93. package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  94. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +38 -0
  95. package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  96. package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  97. package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  98. package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  99. package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +16 -0
  100. package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  101. package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  102. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +18 -0
  103. package/project/.sdk/src/cmp/lean/Test_lean.ts +207 -0
  104. package/project/.sdk/src/cmp/lean/utility_lean.ts +78 -0
  105. package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +2 -3
  106. package/project/.sdk/src/cmp/lua/Entity_lua.ts +3 -3
  107. package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -1
  108. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +3 -3
  109. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
  110. package/project/.sdk/src/cmp/ocaml/ReadmeExamplesTest_ocaml.ts +29 -14
  111. package/project/.sdk/src/cmp/perl/Entity_perl.ts +3 -3
  112. package/project/.sdk/src/cmp/perl/MainEntity_perl.ts +2 -2
  113. package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
  114. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +3 -3
  115. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +2 -3
  116. package/project/.sdk/src/cmp/php/Entity_php.ts +3 -3
  117. package/project/.sdk/src/cmp/php/MainEntity_php.ts +2 -2
  118. package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
  119. package/project/.sdk/src/cmp/php/TestDirect_php.ts +3 -3
  120. package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
  121. package/project/.sdk/src/cmp/py/EntityTypes_py.ts +2 -3
  122. package/project/.sdk/src/cmp/py/Entity_py.ts +3 -3
  123. package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -2
  124. package/project/.sdk/src/cmp/py/Main_py.ts +2 -2
  125. package/project/.sdk/src/cmp/py/Package_py.ts +1 -1
  126. package/project/.sdk/src/cmp/py/TestDirect_py.ts +3 -3
  127. package/project/.sdk/src/cmp/py/TestEntity_py.ts +1 -1
  128. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +15 -5
  129. package/project/.sdk/src/cmp/rb/Entity_rb.ts +3 -3
  130. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +2 -2
  131. package/project/.sdk/src/cmp/rb/Package_rb.ts +2 -2
  132. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +3 -3
  133. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
  134. package/project/.sdk/src/cmp/rust/EntityBase_rust.ts +2 -2
  135. package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +3 -4
  136. package/project/.sdk/src/cmp/rust/Entity_rust.ts +3 -3
  137. package/project/.sdk/src/cmp/rust/MainEntity_rust.ts +2 -2
  138. package/project/.sdk/src/cmp/rust/Package_rust.ts +1 -1
  139. package/project/.sdk/src/cmp/rust/ReadmeExamplesTest_rust.ts +22 -10
  140. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +3 -3
  141. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +2 -3
  142. package/project/.sdk/src/cmp/scala/Entity_scala.ts +3 -3
  143. package/project/.sdk/src/cmp/scala/MainEntity_scala.ts +2 -2
  144. package/project/.sdk/src/cmp/scala/ReadmeExamplesTest_scala.ts +22 -3
  145. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +3 -3
  146. package/project/.sdk/src/cmp/scala/Test_scala.ts +1 -1
  147. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +3 -4
  148. package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -3
  149. package/project/.sdk/src/cmp/swift/MainEntity_swift.ts +2 -2
  150. package/project/.sdk/src/cmp/swift/Package_swift.ts +1 -1
  151. package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +18 -1
  152. package/project/.sdk/src/cmp/swift/TestDirect_swift.ts +1 -1
  153. package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +2 -3
  154. package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -3
  155. package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +2 -2
  156. package/project/.sdk/src/cmp/ts/Main_ts.ts +2 -2
  157. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +4 -4
  158. package/project/.sdk/src/cmp/zig/Entity_zig.ts +3 -3
  159. package/project/.sdk/src/cmp/zig/MainEntity_zig.ts +2 -2
  160. package/project/.sdk/src/cmp/zig/Package_zig.ts +10 -2
  161. package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +8 -16
  162. package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +10 -28
  163. package/project/.sdk/tm/dart/test/primary_test.dart +7 -0
  164. package/project/.sdk/tm/go/feature/test_feature.go +33 -1
  165. package/project/.sdk/tm/go/test/primary_utility_test.go +41 -42
  166. package/project/.sdk/tm/go/test/runner_test.go +30 -1
  167. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +7 -24
  168. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +18 -1
  169. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +8 -0
  170. package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +7 -20
  171. package/project/.sdk/tm/lean/LICENSE +21 -0
  172. package/project/.sdk/tm/lean/VERSION +1 -0
  173. package/project/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  174. package/project/.sdk/tm/lean/src/SdkFeatures.lean +602 -0
  175. package/project/.sdk/tm/lean/src/SdkJson.lean +134 -0
  176. package/project/.sdk/tm/lean/src/SdkRuntime.lean +356 -0
  177. package/project/.sdk/tm/lean/src/SdkUtility.lean +617 -0
  178. package/project/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  179. package/project/.sdk/tm/lean/src/Vregex.lean +412 -0
  180. package/project/.sdk/tm/lean/src/feature/README.md +5 -0
  181. package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  182. package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  183. package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  184. package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  185. package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  186. package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  187. package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  188. package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  189. package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  190. package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  191. package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  192. package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  193. package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  194. package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  195. package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  196. package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  197. package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  198. package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  199. package/project/.sdk/tm/lean/test/StructCorpus.lean +697 -0
  200. package/project/.sdk/tm/lean/test/TFeature.lean +268 -0
  201. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +349 -0
  202. package/project/.sdk/tm/lua/test/primary_utility_test.lua +10 -8
  203. package/project/.sdk/tm/perl/t/primary_utility.t +8 -17
  204. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +9 -7
  205. package/project/.sdk/tm/py/feature/test_feature.py +28 -1
  206. package/project/.sdk/tm/py/test/test_primary_utility.py +13 -18
  207. package/project/.sdk/tm/rb/feature/test_feature.rb +24 -1
  208. package/project/.sdk/tm/rb/test/primary_utility_test.rb +7 -8
  209. package/project/.sdk/tm/rust/tests/common/mod.rs +37 -1
  210. package/project/.sdk/tm/rust/tests/primary_utility_test.rs +12 -26
  211. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +7 -8
  212. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +18 -1
  213. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +65 -21
  214. package/src/cmp/Entity.ts +5 -1
  215. package/src/cmp/Feature.ts +2 -1
  216. package/src/helpers/canonType.ts +8 -3
  217. package/src/helpers/collectDeps.ts +39 -12
  218. package/src/helpers/naming.ts +59 -0
  219. package/src/helpers/opShape.ts +99 -12
  220. package/src/sdkgen.ts +7 -3
@@ -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
 
@@ -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|
@@ -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`)
@@ -5,7 +5,7 @@ import {
5
5
  Folder,
6
6
  cmp,
7
7
  each,
8
- entityClassName,
8
+ entityClassName, entityCollection,
9
9
  } from '@voxgig/sdkgen'
10
10
 
11
11
 
@@ -27,7 +27,7 @@ const EntityBase = cmp(async function EntityBase(props: any) {
27
27
  const { model } = props.ctx$
28
28
 
29
29
  const entity = getModelPath(model, `main.${KIT}.entity`)
30
- const entityColl = entity
30
+ const entityColl = entityCollection(model)
31
31
 
32
32
  Folder({ name: 'entity' }, () => {
33
33
  File({ name: 'mod.' + target.ext }, () => {
@@ -25,11 +25,11 @@
25
25
  // types yet (parity intent, not dead-code churn).
26
26
 
27
27
  import {
28
- cmp, each, names,
28
+ cmp, each,
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,
@@ -85,8 +85,7 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
85
85
  // Emit for every entity that gets an entity file (filter on `name`, always
86
86
  // present; derive `Name` here so the struct set is deterministic — parity
87
87
  // with the go emitter's fix).
88
- const entityList = each(entity).filter((e: any) => e && null != e.name)
89
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
88
+ const entityList = deriveEntityNames(entity)
90
89
 
91
90
  // Surface duplicate generated type names (two entities with the same
92
91
  // 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,
6
6
  File, Content, 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
  // Collision-free entity CLASS name (see entityClassName): normally
24
24
  // `<Name>Entity`, but disambiguated when it would clash with another
25
25
  // entity's data-type name.
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 = {
@@ -37,7 +37,7 @@ const Entity = cmp(function Entity(props: any) {
37
37
 
38
38
  File({ name: rustVarName(entity.name) + '.' + target.ext }, () => {
39
39
 
40
- const opnames = Object.keys(entity.op)
40
+ const opnames = Object.keys(entity.op || {})
41
41
 
42
42
  // For each CRUD op: if the spec defines it, splice in the real
43
43
  // implementation. Otherwise emit a stub that satisfies the static
@@ -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,
@@ -17,7 +17,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
17
17
  const { entity } = props
18
18
  const { model } = props.ctx$
19
19
 
20
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
20
+ const entityColl = entityCollection(model)
21
21
  const cls = entityClassName(entity, entityColl)
22
22
  const method = rustVarName(entity.name)
23
23
  const mod = rustVarName(entity.name)
@@ -43,7 +43,7 @@ path = "lib.rs"
43
43
 
44
44
  const prod: Record<string, string> = {}
45
45
  const dev: Record<string, string> = {}
46
- for (const d of collectDeps(model, target.name, target.deps)) {
46
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
47
47
  // Target-level deps default to '*' when version is absent; feature
48
48
  // deps require an explicit version.
49
49
  const version = d.source === 'target' ? (d.version || '*') : d.version
@@ -33,7 +33,10 @@ use std::fs;
33
33
  use std::path::PathBuf;
34
34
 
35
35
  // Extract the body of every \`\`\`rust ... \`\`\` fenced block from markdown.
36
- fn extract_rust_blocks(md: &str) -> Vec<String> {
36
+ // Returns the blocks plus a flag that is true when the scan reached EOF with a
37
+ // fence still open (an unterminated \`\`\`rust block) — the caller fails on it
38
+ // rather than silently dropping the malformed block.
39
+ fn extract_rust_blocks(md: &str) -> (Vec<String>, bool) {
37
40
  let mut blocks: Vec<String> = Vec::new();
38
41
  let mut in_block = false;
39
42
  let mut cur: Vec<&str> = Vec::new();
@@ -53,14 +56,15 @@ fn extract_rust_blocks(md: &str) -> Vec<String> {
53
56
  }
54
57
  cur.push(line);
55
58
  }
56
- blocks
59
+ (blocks, in_block)
57
60
  }
58
61
 
59
- // Balanced (), [], {} outside of string literals. Naive quote toggle: the
60
- // generated snippets never embed an escaped quote inside a string.
62
+ // Balanced (), [], {} outside of string literals, with each closer matching the
63
+ // most-recent opener (so "([)]" is rejected, not just counted). Naive quote
64
+ // toggle: the generated snippets never embed an escaped quote inside a string.
61
65
  fn is_balanced(block: &str) -> bool {
62
66
  let mut in_str = false;
63
- let mut depth: i32 = 0;
67
+ let mut stack: Vec<char> = Vec::new();
64
68
  for c in block.chars() {
65
69
  if c == '"' {
66
70
  in_str = !in_str;
@@ -70,17 +74,18 @@ fn is_balanced(block: &str) -> bool {
70
74
  continue;
71
75
  }
72
76
  match c {
73
- '(' | '[' | '{' => depth += 1,
77
+ '(' => stack.push(')'),
78
+ '[' => stack.push(']'),
79
+ '{' => stack.push('}'),
74
80
  ')' | ']' | '}' => {
75
- depth -= 1;
76
- if depth < 0 {
81
+ if stack.pop() != Some(c) {
77
82
  return false;
78
83
  }
79
84
  }
80
85
  _ => {}
81
86
  }
82
87
  }
83
- depth == 0 && !in_str
88
+ stack.is_empty() && !in_str
84
89
  }
85
90
 
86
91
  #[test]
@@ -98,7 +103,14 @@ fn readme_rust_snippets_wellformed() {
98
103
  Ok(s) => s,
99
104
  Err(_) => continue, // doc not generated in this run — tolerate
100
105
  };
101
- let blocks = extract_rust_blocks(&src);
106
+ let (blocks, unterminated) = extract_rust_blocks(&src);
107
+ // A fence opened but never closed is a malformed doc — fail rather than
108
+ // silently skipping the unterminated block.
109
+ assert!(
110
+ !unterminated,
111
+ "{}: unterminated \`\`\`rust fence (block never closed)",
112
+ label
113
+ );
102
114
  for (i, block) in blocks.iter().enumerate() {
103
115
  assert!(
104
116
  !block.trim().is_empty(),
@@ -73,7 +73,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
73
73
  ? `("apikey", getp(&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 as any).load
85
- const listOp = (entity.op as any).list
84
+ const loadOp = (entity.op as any)?.load
85
+ const listOp = (entity.op as any)?.list
86
86
 
87
87
  // Load point info.
88
88
  const loadPoint = loadOp?.points?.[0]
@@ -38,7 +38,7 @@ import {
38
38
  File, Content,
39
39
  } from '@voxgig/sdkgen'
40
40
 
41
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
41
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
42
42
 
43
43
  import {
44
44
  KIT,
@@ -133,11 +133,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
133
133
  // filter, so inactive entities still get class files referencing these
134
134
  // typed names. Filter on `name` (always present), NOT `active` — parity
135
135
  // with the go emitter's fix.
136
- const entityList = each(entity).filter((e: any) => e && null != e.name)
136
+ const entityList = deriveEntityNames(entity)
137
137
  // Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
138
138
  // entity not yet named (e.g. a fieldless placeholder) would otherwise read
139
139
  // `Name = undefined` below. Parity with the go/py/java/csharp emitter's fix.
140
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
141
140
 
142
141
  // Surface duplicate generated type names (two entities with the same
143
142
  // 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
  File, Content, 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
  const scalapackage = scalaPackage(model)
24
24
 
25
25
  // Collision-free entity CLASS name.
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 = {
@@ -39,7 +39,7 @@ const Entity = cmp(function Entity(props: any) {
39
39
 
40
40
  File({ name: cls + '.' + target.ext }, () => {
41
41
 
42
- const opnames = Object.keys(entity.op)
42
+ const opnames = Object.keys(entity.op || {})
43
43
 
44
44
  // For each CRUD op: if the spec defines it, splice in the real
45
45
  // implementation. Otherwise emit a stub that satisfies the SdkEntity
@@ -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,
@@ -18,7 +18,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
18
18
  const { model } = props.ctx$
19
19
 
20
20
  const scalapackage = scalaPackage(model)
21
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
21
+ const entityColl = entityCollection(model)
22
22
  const cls = entityClassName(entity, entityColl)
23
23
  const accessor = scalaVarName(entity.name)
24
24
 
@@ -43,8 +43,11 @@ const ReadmeExamplesTest = cmp(function ReadmeExamplesTest(props: any) {
43
43
  //
44
44
  // Checks, PER DOCUMENT:
45
45
  // 1. PRESENCE: the doc exists and holds at least one scala block.
46
- // 2. STRUCTURE: every scala block's braces / parens / brackets balance, so a
47
- // truncated or malformed generated snippet fails loudly.
46
+ // 2. STRUCTURE: every fence is closed (an odd number of triple-backtick
47
+ // markers means a fence was opened but never closed — the doc is truncated
48
+ // and split-based extraction would mis-segment the tail), AND every scala
49
+ // block's braces / parens / brackets balance, so a truncated or malformed
50
+ // generated snippet fails loudly.
48
51
  //
49
52
  // GENERATED by @voxgig/sdkgen — do not edit by hand.
50
53
 
@@ -85,6 +88,17 @@ object ReadmeExamplesTest {
85
88
  private def readDoc(path: Path): String =
86
89
  new String(Files.readAllBytes(path), "UTF-8")
87
90
 
91
+ // True when every triple-backtick fence in the document is paired. split(-1)
92
+ // yields (fenceCount + 1) parts, so an even fence count (all fences closed)
93
+ // gives an odd number of parts. An odd fence count means a fence was opened
94
+ // but never closed: the block-extraction above would then treat everything
95
+ // after it as one big "block", and a truncated snippet that happens to
96
+ // balance would pass silently. Detecting it here lets the gate fail loudly.
97
+ private def fencesClosed(text: String): Boolean = {
98
+ val fence = 96.toChar.toString.repeat(3)
99
+ text.split(java.util.regex.Pattern.quote(fence), -1).length % 2 == 1
100
+ }
101
+
88
102
  // True when every open/close delimiter balances (ignores the subtlety of
89
103
  // delimiters inside string literals — a generated snippet never nests an
90
104
  // unbalanced delimiter in a string, and this catches truncation).
@@ -135,7 +149,12 @@ object ReadmeExamplesTest {
135
149
  val label = ds(dj)(0)
136
150
  val path = Paths.get(ds(dj)(1))
137
151
  if (Files.exists(path)) {
138
- val blocks = scalaBlocks(readDoc(path))
152
+ val text = readDoc(path)
153
+ // A fence opened but never closed leaves an odd number of fence
154
+ // markers; fail loudly rather than trusting the mis-segmented tail.
155
+ if (!fencesClosed(text))
156
+ failures.add(label + " has an unclosed scala code fence")
157
+ val blocks = scalaBlocks(text)
139
158
  var balancedCount = 0
140
159
  var bi = 0
141
160
  while (bi < blocks.size()) {
@@ -65,7 +65,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
65
65
  const EntityName = nom(entity, 'Name')
66
66
  const ENTLOWER = entity.name
67
67
 
68
- const opnames = Object.keys(entity.op)
68
+ const opnames = Object.keys(entity.op || {})
69
69
  const hasLoad = opnames.includes('load')
70
70
  const hasList = opnames.includes('list')
71
71
 
@@ -73,8 +73,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
73
73
  return
74
74
  }
75
75
 
76
- const loadOp = (entity.op as any).load
77
- const listOp = (entity.op as any).list
76
+ const loadOp = (entity.op as any)?.load
77
+ const listOp = (entity.op as any)?.list
78
78
 
79
79
  // Load point info.
80
80
  const loadPoint = loadOp?.points?.[0]
@@ -40,7 +40,7 @@ const Test = cmp(function Test(props: any) {
40
40
  getModelPath(model, `main.${KIT}.flow.Basic${EntityName}Flow`)
41
41
  const hasEntity = null != basicflow && true === basicflow.active
42
42
 
43
- const opnames = Object.keys(entity.op)
43
+ const opnames = Object.keys(entity.op || {})
44
44
  const hasDirect = opnames.includes('load') || opnames.includes('list')
45
45
 
46
46
  if (hasEntity) {
@@ -21,11 +21,11 @@
21
21
  // the runtime's open shape.
22
22
 
23
23
  import {
24
- cmp, each, names,
24
+ cmp, each,
25
25
  File, Content, Folder,
26
26
  } from '@voxgig/sdkgen'
27
27
 
28
- import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
28
+ import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
29
29
 
30
30
  import {
31
31
  KIT,
@@ -81,8 +81,7 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
81
81
  // Emit for every entity that gets an entity file (filter on `name`, always
82
82
  // present; derive `Name` here so the struct set is deterministic — parity
83
83
  // with the rust/go emitter's fix).
84
- const entityList = each(entity).filter((e: any) => e && null != e.name)
85
- entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
84
+ const entityList = deriveEntityNames(entity)
86
85
 
87
86
  // Surface duplicate generated type names (two entities with the same
88
87
  // 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 {
@@ -20,7 +20,7 @@ const Entity = cmp(function Entity(props: any) {
20
20
  const { target, entity } = props
21
21
 
22
22
  // Collision-free entity CLASS name (see entityClassName).
23
- const entityColl = getModelPath(model, `main.${KIT}.entity`)
23
+ const entityColl = entityCollection(model)
24
24
  const cls = entityClassName(entity, entityColl)
25
25
 
26
26
  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
  // For each CRUD op: if the spec defines it, splice in the real
44
44
  // implementation (an override). Otherwise leave the base-class