@specverse/engines 4.1.30 → 4.2.1

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 (285) hide show
  1. package/assets/examples/manifests/frontend-only.yaml +3 -6
  2. package/assets/examples/manifests/fullstack-app.yaml +5 -7
  3. package/assets/examples/manifests/fullstack-monorepo.yaml +3 -6
  4. package/assets/templates/default/specs/main.specly +65 -0
  5. package/dist/inference/comprehensive-engine.d.ts.map +1 -1
  6. package/dist/inference/comprehensive-engine.js +3 -19
  7. package/dist/inference/comprehensive-engine.js.map +1 -1
  8. package/dist/inference/core/rule-engine.d.ts +31 -0
  9. package/dist/inference/core/rule-engine.d.ts.map +1 -1
  10. package/dist/inference/core/rule-engine.js +117 -33
  11. package/dist/inference/core/rule-engine.js.map +1 -1
  12. package/dist/inference/core/rule-file-types.d.ts +0 -2
  13. package/dist/inference/core/rule-file-types.d.ts.map +1 -1
  14. package/dist/inference/core/rule-file-types.js +3 -6
  15. package/dist/inference/core/rule-file-types.js.map +1 -1
  16. package/dist/inference/core/rule-loader.d.ts +5 -15
  17. package/dist/inference/core/rule-loader.d.ts.map +1 -1
  18. package/dist/inference/core/rule-loader.js +43 -132
  19. package/dist/inference/core/rule-loader.js.map +1 -1
  20. package/dist/inference/core/types.d.ts +0 -6
  21. package/dist/inference/core/types.d.ts.map +1 -1
  22. package/dist/inference/core/types.js +0 -4
  23. package/dist/inference/core/types.js.map +1 -1
  24. package/dist/inference/logical/generators/component-type-resolver.d.ts +0 -26
  25. package/dist/inference/logical/generators/component-type-resolver.d.ts.map +1 -1
  26. package/dist/inference/logical/generators/component-type-resolver.js +0 -19
  27. package/dist/inference/logical/generators/component-type-resolver.js.map +1 -1
  28. package/dist/inference/logical/generators/specialist-view-expander.d.ts +1 -17
  29. package/dist/inference/logical/generators/specialist-view-expander.d.ts.map +1 -1
  30. package/dist/inference/logical/generators/specialist-view-expander.js +0 -15
  31. package/dist/inference/logical/generators/specialist-view-expander.js.map +1 -1
  32. package/dist/inference/logical/generators/view-generator.d.ts +4 -14
  33. package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
  34. package/dist/inference/logical/generators/view-generator.js +6 -26
  35. package/dist/inference/logical/generators/view-generator.js.map +1 -1
  36. package/dist/inference/logical/index.d.ts +2 -2
  37. package/dist/inference/logical/index.d.ts.map +1 -1
  38. package/dist/inference/logical/logical-engine.d.ts.map +1 -1
  39. package/dist/inference/logical/logical-engine.js +17 -80
  40. package/dist/inference/logical/logical-engine.js.map +1 -1
  41. package/dist/inference/quint-transpiler.d.ts +5 -3
  42. package/dist/inference/quint-transpiler.d.ts.map +1 -1
  43. package/dist/inference/quint-transpiler.js +11 -6
  44. package/dist/inference/quint-transpiler.js.map +1 -1
  45. package/dist/libs/instance-factories/CURVED-INTERFACE.md +278 -0
  46. package/dist/libs/instance-factories/README.md +73 -0
  47. package/dist/libs/instance-factories/applications/README.md +51 -0
  48. package/dist/libs/instance-factories/applications/generic-app.yaml +52 -0
  49. package/{libs/instance-factories/applications/react-app.yaml → dist/libs/instance-factories/applications/react-app-runtime.yaml} +30 -77
  50. package/dist/libs/instance-factories/applications/react-app-starter.yaml +143 -0
  51. package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +24 -2
  52. package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +54 -33
  53. package/dist/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
  54. package/dist/libs/instance-factories/applications/templates/react-starter/api-types-starter-generator.js +69 -0
  55. package/dist/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.js +110 -0
  56. package/dist/libs/instance-factories/applications/templates/react-starter/belongs-to.js +40 -0
  57. package/dist/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.js +129 -0
  58. package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +80 -0
  59. package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +217 -0
  60. package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +51 -0
  61. package/dist/libs/instance-factories/applications/templates/react-starter/html-to-jsx.js +192 -0
  62. package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +56 -0
  63. package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +41 -0
  64. package/dist/libs/instance-factories/applications/templates/react-starter/package-json-generator.js +38 -0
  65. package/dist/libs/instance-factories/applications/templates/react-starter/regen-safety.js +89 -0
  66. package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +49 -0
  67. package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +96 -0
  68. package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +116 -0
  69. package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +74 -0
  70. package/dist/libs/instance-factories/applications/templates/react-starter/use-api-hooks-starter-generator.js +95 -0
  71. package/dist/libs/instance-factories/applications/templates/react-starter/view-emitter.js +81 -0
  72. package/dist/libs/instance-factories/applications/templates/react-starter/views-generator.js +66 -0
  73. package/dist/libs/instance-factories/archived/fastify-prisma.yaml +104 -0
  74. package/dist/libs/instance-factories/cli/README.md +43 -0
  75. package/dist/libs/instance-factories/cli/commander-js.yaml +55 -0
  76. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +63 -12
  77. package/dist/libs/instance-factories/communication/README.md +47 -0
  78. package/dist/libs/instance-factories/communication/event-emitter.yaml +60 -0
  79. package/dist/libs/instance-factories/communication/rabbitmq-events.yaml +87 -0
  80. package/dist/libs/instance-factories/controllers/README.md +42 -0
  81. package/dist/libs/instance-factories/controllers/fastify.yaml +139 -0
  82. package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +29 -2
  83. package/dist/libs/instance-factories/infrastructure/README.md +29 -0
  84. package/dist/libs/instance-factories/infrastructure/docker-k8s.yaml +61 -0
  85. package/dist/libs/instance-factories/orms/README.md +54 -0
  86. package/dist/libs/instance-factories/orms/prisma.yaml +89 -0
  87. package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +2 -2
  88. package/dist/libs/instance-factories/scaffolding/README.md +49 -0
  89. package/dist/libs/instance-factories/scaffolding/generic-scaffold.yaml +65 -0
  90. package/dist/libs/instance-factories/sdks/README.md +28 -0
  91. package/dist/libs/instance-factories/sdks/python-sdk.yaml +66 -0
  92. package/dist/libs/instance-factories/sdks/typescript-sdk.yaml +59 -0
  93. package/dist/libs/instance-factories/services/README.md +55 -0
  94. package/dist/libs/instance-factories/services/prisma-services.yaml +71 -0
  95. package/dist/libs/instance-factories/storage/README.md +34 -0
  96. package/dist/libs/instance-factories/storage/mongodb.yaml +79 -0
  97. package/dist/libs/instance-factories/storage/postgresql.yaml +75 -0
  98. package/dist/libs/instance-factories/storage/redis.yaml +79 -0
  99. package/dist/libs/instance-factories/testing/README.md +40 -0
  100. package/dist/libs/instance-factories/testing/vitest-tests.yaml +63 -0
  101. package/dist/libs/instance-factories/tools/README.md +70 -0
  102. package/dist/libs/instance-factories/tools/mcp.yaml +36 -0
  103. package/dist/libs/instance-factories/tools/vscode.yaml +35 -0
  104. package/dist/libs/instance-factories/validation/README.md +38 -0
  105. package/dist/libs/instance-factories/validation/zod.yaml +56 -0
  106. package/dist/realize/engines/code-generator.d.ts.map +1 -1
  107. package/dist/realize/engines/code-generator.js +3 -0
  108. package/dist/realize/engines/code-generator.js.map +1 -1
  109. package/dist/realize/index.d.ts.map +1 -1
  110. package/dist/realize/index.js +15 -22
  111. package/dist/realize/index.js.map +1 -1
  112. package/dist/registry/utils/manifest-adapter.d.ts +8 -1
  113. package/dist/registry/utils/manifest-adapter.d.ts.map +1 -1
  114. package/dist/registry/utils/manifest-adapter.js +8 -1
  115. package/dist/registry/utils/manifest-adapter.js.map +1 -1
  116. package/libs/instance-factories/applications/react-app-starter.yaml +143 -0
  117. package/libs/instance-factories/applications/templates/react/env-example-generator.ts +24 -2
  118. package/libs/instance-factories/applications/templates/react/vite-config-generator.ts +54 -33
  119. package/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
  120. package/libs/instance-factories/applications/templates/react-starter/__tests__/dashboard-body-composer.test.ts +153 -0
  121. package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +146 -0
  122. package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +188 -0
  123. package/libs/instance-factories/applications/templates/react-starter/__tests__/helpers-emitter.test.ts +55 -0
  124. package/libs/instance-factories/applications/templates/react-starter/__tests__/html-to-jsx.test.ts +140 -0
  125. package/libs/instance-factories/applications/templates/react-starter/__tests__/list-body-composer.test.ts +146 -0
  126. package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +184 -0
  127. package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p2-factory-imports.test.ts +116 -0
  128. package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p3-rendered-output.test.ts +183 -0
  129. package/libs/instance-factories/applications/templates/react-starter/__tests__/regen-safety.test.ts +144 -0
  130. package/libs/instance-factories/applications/templates/react-starter/__tests__/starter-generators.test.ts +114 -0
  131. package/libs/instance-factories/applications/templates/react-starter/__tests__/view-emitter.test.ts +107 -0
  132. package/libs/instance-factories/applications/templates/react-starter/__tests__/views-generator.test.ts +139 -0
  133. package/libs/instance-factories/applications/templates/react-starter/api-types-starter-generator.ts +98 -0
  134. package/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.ts +141 -0
  135. package/libs/instance-factories/applications/templates/react-starter/belongs-to.ts +82 -0
  136. package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts +189 -0
  137. package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +135 -0
  138. package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +383 -0
  139. package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +66 -0
  140. package/libs/instance-factories/applications/templates/react-starter/html-to-jsx.ts +334 -0
  141. package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +146 -0
  142. package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +95 -0
  143. package/libs/instance-factories/applications/templates/react-starter/package-json-generator.ts +57 -0
  144. package/libs/instance-factories/applications/templates/react-starter/regen-safety.ts +157 -0
  145. package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +49 -0
  146. package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +96 -0
  147. package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +116 -0
  148. package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +74 -0
  149. package/libs/instance-factories/applications/templates/react-starter/use-api-hooks-starter-generator.ts +124 -0
  150. package/libs/instance-factories/applications/templates/react-starter/view-emitter.ts +209 -0
  151. package/libs/instance-factories/applications/templates/react-starter/views-generator.ts +137 -0
  152. package/libs/instance-factories/cli/templates/commander/command-generator.ts +63 -12
  153. package/libs/instance-factories/controllers/fastify.yaml +7 -0
  154. package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +36 -2
  155. package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +11 -4
  156. package/package.json +3 -3
  157. package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +0 -530
  158. package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +0 -73
  159. package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +0 -99
  160. package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +0 -49
  161. package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +0 -156
  162. package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +0 -935
  163. package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +0 -143
  164. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +0 -646
  165. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +0 -65
  166. package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +0 -143
  167. package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +0 -143
  168. package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +0 -355
  169. package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +0 -91
  170. package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +0 -79
  171. package/dist/libs/instance-factories/views/index.js +0 -48
  172. package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +0 -742
  173. package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +0 -824
  174. package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +0 -719
  175. package/dist/libs/instance-factories/views/templates/react/app-generator.js +0 -45
  176. package/dist/libs/instance-factories/views/templates/react/components-generator.js +0 -820
  177. package/dist/libs/instance-factories/views/templates/react/forms-generator.js +0 -275
  178. package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +0 -46
  179. package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +0 -81
  180. package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +0 -9
  181. package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +0 -23
  182. package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +0 -21
  183. package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +0 -299
  184. package/dist/libs/instance-factories/views/templates/react/router-generator.js +0 -136
  185. package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +0 -107
  186. package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +0 -187
  187. package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +0 -7
  188. package/dist/libs/instance-factories/views/templates/react/types-generator.js +0 -56
  189. package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +0 -27
  190. package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +0 -29
  191. package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +0 -261
  192. package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +0 -34
  193. package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -800
  194. package/dist/libs/instance-factories/views/templates/shared/base-generator.js +0 -305
  195. package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +0 -517
  196. package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
  197. package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +0 -445
  198. package/dist/libs/instance-factories/views/templates/shared/index.js +0 -80
  199. package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +0 -210
  200. package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +0 -492
  201. package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -321
  202. package/dist/realize/index.js.bak +0 -758
  203. package/libs/instance-factories/applications/templates/react/_view-components-source.ts +0 -555
  204. package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +0 -94
  205. package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +0 -106
  206. package/libs/instance-factories/applications/templates/react/package-json-generator.ts +0 -57
  207. package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +0 -179
  208. package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +0 -1347
  209. package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +0 -150
  210. package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +0 -704
  211. package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +0 -84
  212. package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +0 -150
  213. package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +0 -150
  214. package/libs/instance-factories/applications/templates/react/view-form-generator.ts +0 -362
  215. package/libs/instance-factories/applications/templates/react/view-list-generator.ts +0 -98
  216. package/libs/instance-factories/applications/templates/react/view-router-generator.ts +0 -89
  217. package/libs/instance-factories/views/README.md +0 -62
  218. package/libs/instance-factories/views/index.d.ts +0 -13
  219. package/libs/instance-factories/views/index.d.ts.map +0 -1
  220. package/libs/instance-factories/views/index.js +0 -18
  221. package/libs/instance-factories/views/index.js.map +0 -1
  222. package/libs/instance-factories/views/index.ts +0 -45
  223. package/libs/instance-factories/views/react-components.yaml +0 -129
  224. package/libs/instance-factories/views/templates/ARCHITECTURE.md +0 -198
  225. package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +0 -869
  226. package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +0 -953
  227. package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +0 -806
  228. package/libs/instance-factories/views/templates/react/app-generator.ts +0 -55
  229. package/libs/instance-factories/views/templates/react/components-generator.ts +0 -938
  230. package/libs/instance-factories/views/templates/react/forms-generator.ts +0 -325
  231. package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +0 -57
  232. package/libs/instance-factories/views/templates/react/hooks-generator.ts +0 -106
  233. package/libs/instance-factories/views/templates/react/index-css-generator.ts +0 -14
  234. package/libs/instance-factories/views/templates/react/index-html-generator.ts +0 -34
  235. package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +0 -29
  236. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +0 -152
  237. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +0 -1
  238. package/libs/instance-factories/views/templates/react/react-component-generator.js +0 -398
  239. package/libs/instance-factories/views/templates/react/react-component-generator.js.map +0 -1
  240. package/libs/instance-factories/views/templates/react/react-component-generator.ts +0 -533
  241. package/libs/instance-factories/views/templates/react/router-generator.ts +0 -197
  242. package/libs/instance-factories/views/templates/react/router-generic-generator.ts +0 -132
  243. package/libs/instance-factories/views/templates/react/shared-utils-generator.ts +0 -196
  244. package/libs/instance-factories/views/templates/react/spec-json-generator.ts +0 -17
  245. package/libs/instance-factories/views/templates/react/types-generator.ts +0 -76
  246. package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +0 -42
  247. package/libs/instance-factories/views/templates/react/vite-config-generator.ts +0 -38
  248. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +0 -1
  249. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +0 -1
  250. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +0 -474
  251. package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +0 -242
  252. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +0 -77
  253. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +0 -1
  254. package/libs/instance-factories/views/templates/shared/adapter-types.js +0 -47
  255. package/libs/instance-factories/views/templates/shared/adapter-types.js.map +0 -1
  256. package/libs/instance-factories/views/templates/shared/adapter-types.ts +0 -142
  257. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +0 -63
  258. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +0 -1
  259. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -822
  260. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +0 -1
  261. package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +0 -908
  262. package/libs/instance-factories/views/templates/shared/base-generator.d.ts +0 -247
  263. package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +0 -1
  264. package/libs/instance-factories/views/templates/shared/base-generator.js +0 -363
  265. package/libs/instance-factories/views/templates/shared/base-generator.js.map +0 -1
  266. package/libs/instance-factories/views/templates/shared/base-generator.ts +0 -608
  267. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +0 -254
  268. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +0 -1
  269. package/libs/instance-factories/views/templates/shared/component-metadata.js +0 -602
  270. package/libs/instance-factories/views/templates/shared/component-metadata.js.map +0 -1
  271. package/libs/instance-factories/views/templates/shared/component-metadata.ts +0 -803
  272. package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +0 -250
  273. package/libs/instance-factories/views/templates/shared/composite-patterns.ts +0 -535
  274. package/libs/instance-factories/views/templates/shared/index.ts +0 -68
  275. package/libs/instance-factories/views/templates/shared/pattern-validator.ts +0 -279
  276. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +0 -149
  277. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +0 -1
  278. package/libs/instance-factories/views/templates/shared/property-mapper.js +0 -580
  279. package/libs/instance-factories/views/templates/shared/property-mapper.js.map +0 -1
  280. package/libs/instance-factories/views/templates/shared/property-mapper.ts +0 -700
  281. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +0 -143
  282. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +0 -1
  283. package/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -420
  284. package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +0 -1
  285. package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +0 -539
@@ -0,0 +1,209 @@
1
+ /**
2
+ * ReactAppStarter view emitter
3
+ *
4
+ * Given a view spec + model spec + expanded spec, emits a complete
5
+ * idiomatic React component source file as a string.
6
+ *
7
+ * Strategy 3 ("skeleton + rendered interior"): a hand-written
8
+ * skeleton template provides the idiomatic React outer structure
9
+ * (imports, hooks, props, layout). The canonical Tailwind adapter
10
+ * renders the pattern's interior as HTML. The html-to-jsx transformer
11
+ * converts the HTML to JSX. The emitter composes these.
12
+ *
13
+ * See README.md in this directory for the architecture.
14
+ */
15
+
16
+ import { readFileSync } from 'fs';
17
+ import { join, dirname } from 'path';
18
+ import { fileURLToPath } from 'url';
19
+ import { extractBelongsToTargets, pluralize as pluralizeShared } from './belongs-to.js';
20
+
21
+ /**
22
+ * The minimal shape of a view spec this emitter needs. Matches the
23
+ * inferred spec format — `view.type` drives skeleton selection,
24
+ * `view.model` identifies the primary model, `view.uiComponents` (if
25
+ * present) overrides inference defaults.
26
+ */
27
+ export interface ViewSpec {
28
+ type: string;
29
+ model?: string;
30
+ uiComponents?: Record<string, unknown>;
31
+ [key: string]: unknown;
32
+ }
33
+
34
+ /**
35
+ * The minimal shape of a model spec this emitter needs. Field details
36
+ * flow through to the Tailwind adapter which decides column selection.
37
+ */
38
+ export interface ModelSpec {
39
+ name: string;
40
+ attributes: Record<string, unknown>;
41
+ relationships?: Record<string, unknown>;
42
+ lifecycles?: Record<string, unknown>;
43
+ }
44
+
45
+ export interface EmitContext {
46
+ view: ViewSpec;
47
+ viewName: string; // e.g. "PostListView"
48
+ model: ModelSpec;
49
+ modelSchemas: Record<string, ModelSpec>;
50
+ /** Pluggable so we can stub the renderer in unit tests. */
51
+ renderBody: RenderBodyFn;
52
+ }
53
+
54
+ /**
55
+ * Render the body of a view as JSX-safe source — the exact string
56
+ * that is dropped at `{{BODY}}` in the skeleton.
57
+ *
58
+ * The renderBody implementation is responsible for the whole body
59
+ * pipeline: invoke the canonical Tailwind adapter for static shell
60
+ * rendering, run that HTML through `htmlToJsx`, and inject any JSX
61
+ * expressions (`.map()`, event handlers). Keeping it here means
62
+ * view-emitter stays a pure orchestrator and can't accidentally
63
+ * double-transform JSX that's already JSX.
64
+ */
65
+ export type RenderBodyFn = (context: EmitContext) => string;
66
+
67
+ /**
68
+ * Emit a complete .tsx file for a single view. Pure function — all
69
+ * inputs come in via the context argument so it's easy to test.
70
+ */
71
+ export function emitView(context: EmitContext): string {
72
+ const skeleton = loadSkeleton(context.view.type);
73
+ const bodyJsx = context.renderBody(context);
74
+
75
+ const substitutions = buildSubstitutions(context, bodyJsx);
76
+ return applySubstitutions(skeleton, substitutions);
77
+ }
78
+
79
+ // ──────────────────────────────────────────────────────────────────────
80
+ // Skeleton loading
81
+ // ──────────────────────────────────────────────────────────────────────
82
+
83
+ const SKELETON_BY_VIEW_TYPE: Record<string, string> = {
84
+ list: 'list.tsx.template',
85
+ detail: 'detail.tsx.template',
86
+ form: 'form.tsx.template',
87
+ dashboard: 'dashboard.tsx.template',
88
+ // Specialist types (board, timeline, calendar, analytics, workflow,
89
+ // wizard, comparison, settings, map, feed, profile) come online in
90
+ // Phase 2e.
91
+ };
92
+
93
+ function loadSkeleton(viewType: string): string {
94
+ const filename = SKELETON_BY_VIEW_TYPE[viewType.toLowerCase()];
95
+ if (!filename) {
96
+ throw new Error(
97
+ `No skeleton registered for view type "${viewType}". ` +
98
+ `Known types: ${Object.keys(SKELETON_BY_VIEW_TYPE).join(', ')}.`
99
+ );
100
+ }
101
+
102
+ const here = dirname(fileURLToPath(import.meta.url));
103
+ const path = join(here, 'skeletons', filename);
104
+ return readFileSync(path, 'utf8');
105
+ }
106
+
107
+ // ──────────────────────────────────────────────────────────────────────
108
+ // Substitution
109
+ // ──────────────────────────────────────────────────────────────────────
110
+
111
+ interface Substitutions {
112
+ MODEL_NAME: string;
113
+ PLURAL_MODEL: string;
114
+ PLURAL_LOWER: string;
115
+ SINGULAR_LOWER: string;
116
+ BODY: string;
117
+ /**
118
+ * For form views: extra import lines that pull in the hooks and
119
+ * display-name helper needed to render belongsTo <select>s. Empty
120
+ * string for views / models with no belongsTo relationships.
121
+ */
122
+ RELATED_IMPORTS: string;
123
+ /**
124
+ * For form views: extra hook calls inside the component body —
125
+ * one per belongsTo target — that populate the `${relName}Options`
126
+ * array each <select> iterates. Empty string when there are none.
127
+ */
128
+ RELATED_HOOKS: string;
129
+ }
130
+
131
+ function buildSubstitutions(context: EmitContext, body: string): Substitutions {
132
+ const modelName = context.model.name;
133
+ const pluralModel = pluralize(modelName);
134
+ const { imports, hooks } = buildBelongsToWiring(context);
135
+ return {
136
+ MODEL_NAME: modelName,
137
+ PLURAL_MODEL: pluralModel,
138
+ PLURAL_LOWER: pluralModel.toLowerCase(),
139
+ SINGULAR_LOWER: modelName.toLowerCase(),
140
+ BODY: body,
141
+ RELATED_IMPORTS: imports,
142
+ RELATED_HOOKS: hooks,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Compute the belongsTo wiring for a view: one import line per unique
148
+ * target hook, one hook call per relationship (even if two relations
149
+ * share a target — each needs its own options variable).
150
+ *
151
+ * The entity-display helper import set is view-type-dependent to
152
+ * avoid unused-import TS errors:
153
+ * - form uses `getEntityDisplayName(opt)` (has whole entity in hand)
154
+ * - list / detail / dashboard use `resolveEntityDisplayName(id, list)`
155
+ * (has FK id, looks up in the list)
156
+ */
157
+ function buildBelongsToWiring(context: EmitContext): { imports: string; hooks: string } {
158
+ const rels = extractBelongsToTargets(context.model);
159
+ if (rels.length === 0) return { imports: '', hooks: '' };
160
+
161
+ // Dedupe hook imports by target — two belongsTo to the same model
162
+ // share the same query hook.
163
+ const hookImportNames = new Set<string>();
164
+ for (const rel of rels) {
165
+ hookImportNames.add(`use${pluralizeShared(rel.target)}Query`);
166
+ }
167
+
168
+ const helperImport = context.view.type.toLowerCase() === 'form'
169
+ ? 'getEntityDisplayName'
170
+ : 'resolveEntityDisplayName';
171
+
172
+ const importLines: string[] = [];
173
+ importLines.push(
174
+ `import { ${[...hookImportNames].join(', ')} } from '../hooks/useApi';`
175
+ );
176
+ importLines.push(`import { ${helperImport} } from '../lib/entity-display';`);
177
+
178
+ const hookLines = rels.map(rel =>
179
+ ` const { data: ${rel.name}Options = [] } = use${pluralizeShared(rel.target)}Query();`
180
+ );
181
+
182
+ return {
183
+ imports: importLines.join('\n'),
184
+ hooks: hookLines.join('\n'),
185
+ };
186
+ }
187
+
188
+ function applySubstitutions(template: string, subs: Substitutions): string {
189
+ let out = template;
190
+ for (const [key, value] of Object.entries(subs)) {
191
+ // Replace every occurrence of {{KEY}} — plain string substitution,
192
+ // no expression evaluation. Escape regex special chars in the key
193
+ // (defensive; keys are ASCII constants).
194
+ const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
195
+ out = out.replace(pattern, value);
196
+ }
197
+ return out;
198
+ }
199
+
200
+ /**
201
+ * Minimal English pluralizer matching the adapter's conventions.
202
+ * Good enough for generated code — users can edit the output if they
203
+ * want a different pluralization.
204
+ */
205
+ function pluralize(s: string): string {
206
+ if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + 'ies';
207
+ if (/(s|x|z|ch|sh)$/i.test(s)) return s + 'es';
208
+ return s + 's';
209
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * View files generator for ReactAppStarter
3
+ *
4
+ * Called by the realize engine. Iterates over the expanded spec's
5
+ * models × view types, dispatches to the right composer, and returns
6
+ * a filename → source map. Realize writes each file.
7
+ *
8
+ * Also emits the local helpers library (src/lib/entity-display.ts)
9
+ * so generated projects have zero @specverse/runtime runtime deps.
10
+ */
11
+
12
+ import { emitView, type EmitContext, type ModelSpec, type ViewSpec, type RenderBodyFn } from './view-emitter.js';
13
+ import { composeListBody } from './list-body-composer.js';
14
+ import { composeDetailBody } from './detail-body-composer.js';
15
+ import { composeFormBody } from './form-body-composer.js';
16
+ import { composeDashboardBody } from './dashboard-body-composer.js';
17
+ import { emitEntityDisplay } from './helpers-emitter.js';
18
+
19
+ /**
20
+ * The four primary view types Factory B currently emits. Specialist
21
+ * types (board, timeline, calendar, etc.) are not yet implemented —
22
+ * if the expanded spec declares one, we skip it and log a TODO.
23
+ * These users can add composers under Phase 2e.
24
+ */
25
+ const PRIMARY_VIEW_TYPES = ['list', 'detail', 'form', 'dashboard'] as const;
26
+ type PrimaryViewType = typeof PRIMARY_VIEW_TYPES[number];
27
+
28
+ const COMPOSER_BY_TYPE: Record<PrimaryViewType, RenderBodyFn> = {
29
+ list: composeListBody,
30
+ detail: composeDetailBody,
31
+ form: composeFormBody,
32
+ dashboard: composeDashboardBody,
33
+ };
34
+
35
+ /** Generator contract — the shape realize expects. */
36
+ export interface ViewsGeneratorContext {
37
+ /** The expanded spec (post-inference). */
38
+ spec: ExpandedSpec;
39
+ /** The resolved manifest configuration. Not currently read by this
40
+ * generator but passed by realize for parity with other generators. */
41
+ manifest?: unknown;
42
+ }
43
+
44
+ export interface ExpandedSpec {
45
+ /** Models keyed by name. */
46
+ models?: Record<string, ModelSpec>;
47
+ /** Views keyed by view name (e.g. "PostListView"). */
48
+ views?: Record<string, ViewSpec & { type: string; model?: string }>;
49
+ }
50
+
51
+ export interface GeneratedFiles {
52
+ [relativePath: string]: string;
53
+ }
54
+
55
+ /**
56
+ * Generate all view files + local helpers.
57
+ *
58
+ * Paths are RELATIVE to the frontend root (`frontendDir` in the
59
+ * manifest). Realize prepends the absolute output path when writing.
60
+ */
61
+ export async function generate(context: ViewsGeneratorContext): Promise<GeneratedFiles> {
62
+ const files: GeneratedFiles = {};
63
+ const spec = context.spec;
64
+ const models = spec.models ?? {};
65
+ const viewsBySpec = spec.views ?? {};
66
+
67
+ // Group the spec's views by model+type so we can decide, for each
68
+ // (model, type) pair, whether to use the user's view spec or emit
69
+ // a default.
70
+ const viewIndex = indexViews(viewsBySpec);
71
+ const skippedSpecialists: string[] = [];
72
+
73
+ for (const [modelName, model] of Object.entries(models)) {
74
+ for (const viewType of PRIMARY_VIEW_TYPES) {
75
+ const viewName = `${modelName}${capitalize(viewType)}View`;
76
+ // Prefer a user-defined view of that shape if one exists; fall
77
+ // back to a synthesized default.
78
+ const view = viewIndex.get(viewName) ?? synthesizeDefault(modelName, viewType);
79
+ const source = emitView({
80
+ view,
81
+ viewName,
82
+ model,
83
+ modelSchemas: models,
84
+ renderBody: COMPOSER_BY_TYPE[viewType],
85
+ });
86
+ files[`src/views/${viewName}.tsx`] = source;
87
+ }
88
+ }
89
+
90
+ // Surface any specialist-type views the user declared but we can't
91
+ // emit yet. The runtime (ReactAppRuntime) handles these; in starter
92
+ // kit mode the user has to write them manually for now.
93
+ for (const [name, view] of Object.entries(viewsBySpec)) {
94
+ if (!PRIMARY_VIEW_TYPES.includes(view.type as PrimaryViewType)) {
95
+ skippedSpecialists.push(`${name} (type=${view.type})`);
96
+ }
97
+ }
98
+ if (skippedSpecialists.length > 0) {
99
+ console.warn(
100
+ `[ReactAppStarter] Skipped ${skippedSpecialists.length} specialist view(s) — ` +
101
+ `implement composers for: ${[...new Set(Object.values(viewsBySpec)
102
+ .filter(v => !PRIMARY_VIEW_TYPES.includes(v.type as PrimaryViewType))
103
+ .map(v => v.type))].join(', ')}. ` +
104
+ `Skipped: ${skippedSpecialists.join(', ')}`
105
+ );
106
+ }
107
+
108
+ // Local helpers
109
+ files['src/lib/entity-display.ts'] = emitEntityDisplay();
110
+
111
+ return files;
112
+ }
113
+
114
+ // ──────────────────────────────────────────────────────────────────────
115
+ // Helpers
116
+ // ──────────────────────────────────────────────────────────────────────
117
+
118
+ function indexViews(
119
+ views: Record<string, ViewSpec & { type: string; model?: string }>
120
+ ): Map<string, ViewSpec & { type: string; model?: string }> {
121
+ const out = new Map<string, ViewSpec & { type: string; model?: string }>();
122
+ for (const [name, view] of Object.entries(views)) {
123
+ out.set(name, view);
124
+ }
125
+ return out;
126
+ }
127
+
128
+ function synthesizeDefault(modelName: string, viewType: PrimaryViewType): ViewSpec {
129
+ return {
130
+ type: viewType,
131
+ model: modelName,
132
+ };
133
+ }
134
+
135
+ function capitalize(s: string): string {
136
+ return s.charAt(0).toUpperCase() + s.slice(1);
137
+ }
@@ -393,22 +393,25 @@ import type { ParserEngine, InferenceEngine, RealizeEngine } from '@specverse/ty
393
393
  process.exit(1);
394
394
  }
395
395
 
396
- // Runtime mode (default): swap ReactApp ReactAppRuntime in manifest
396
+ // --static ejects to a standalone starter kit (ReactAppStarter).
397
+ // Default: whatever the manifest declares (ReactAppRuntime in the
398
+ // shipped templates). The swap is a convenience only — users can
399
+ // achieve the same by editing their manifest directly.
397
400
  let effectiveManifestPath = manifestPath;
398
- if (!options.static) {
401
+ if (options.static) {
399
402
  const manifestContent = readFileSync(manifestPath, 'utf8');
400
- if (/instanceFactory:\\s*["']?ReactApp["']?\\s*$/m.test(manifestContent)) {
403
+ if (/instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/m.test(manifestContent)) {
401
404
  const { tmpdir } = await import('os');
402
- const runtimeManifest = manifestContent.replace(
403
- /instanceFactory:\\s*["']?ReactApp["']?\\s*$/gm,
404
- 'instanceFactory: "ReactAppRuntime"'
405
+ const staticManifest = manifestContent.replace(
406
+ /instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/gm,
407
+ 'instanceFactory: "ReactAppStarter"'
405
408
  );
406
- effectiveManifestPath = join(tmpdir(), 'specverse-runtime-manifest.yaml');
407
- writeFileSync(effectiveManifestPath, runtimeManifest, 'utf8');
408
- console.log(' Using runtime mode (slim frontend with @specverse/runtime)');
409
+ effectiveManifestPath = join(tmpdir(), 'specverse-static-manifest.yaml');
410
+ writeFileSync(effectiveManifestPath, staticManifest, 'utf8');
411
+ console.log(' Using static mode (standalone starter kit — ReactAppStarter)');
412
+ } else {
413
+ console.log(' Using static mode (manifest factory unchanged)');
409
414
  }
410
- } else {
411
- console.log(' Using static mode (full frontend generation)');
412
415
  }
413
416
 
414
417
  const realizeEngine = registry.getEngineForCapability('realize') as RealizeEngine;
@@ -557,8 +560,56 @@ import { fileURLToPath } from 'url';`,
557
560
  }
558
561
 
559
562
  copyDir(templateDir, destDir);
563
+
564
+ // If the copied template didn't ship a specs/main.specly, load
565
+ // the canonical default spec from @specverse/engines/assets —
566
+ // same file app-demo's Server Manager "new spec" action uses,
567
+ // so both entry points start a user on the same footing. The
568
+ // template can opt out of this by shipping its own spec.
569
+ const specDestPath = join(destDir, 'specs', 'main.specly');
570
+ if (!existsSync(specDestPath)) {
571
+ try {
572
+ const { createRequire } = await import('module');
573
+ const require = createRequire(import.meta.url);
574
+ const enginesPkg = require.resolve('@specverse/engines/package.json');
575
+ const canonicalSpec = join(
576
+ dirname(enginesPkg),
577
+ 'assets', 'templates', 'default', 'specs', 'main.specly'
578
+ );
579
+ if (existsSync(canonicalSpec)) {
580
+ let content = readFileSync(canonicalSpec, 'utf8');
581
+ for (const [key, val] of Object.entries(vars)) {
582
+ content = content.split(key).join(val);
583
+ }
584
+ mkdirSync(join(destDir, 'specs'), { recursive: true });
585
+ writeFileSync(specDestPath, content);
586
+ }
587
+ } catch { /* engines not installed or template missing — proceed */ }
588
+ }
589
+
590
+ // --static: flip any ReactAppRuntime mappings in the copied
591
+ // manifests to ReactAppStarter so the user gets standalone-starter
592
+ // output on first \`spv realize\` without having to re-pass the
593
+ // flag. Same semantic as \`spv realize --static\` — just applied
594
+ // at init time. Idempotent; no-op for templates without a frontend.
595
+ if (options.static) {
596
+ const manifestsDir = join(destDir, 'manifests');
597
+ if (existsSync(manifestsDir)) {
598
+ for (const f of readdirSync(manifestsDir)) {
599
+ if (!f.endsWith('.yaml') && !f.endsWith('.yml')) continue;
600
+ const p = join(manifestsDir, f);
601
+ const before = readFileSync(p, 'utf8');
602
+ const after = before.replace(
603
+ /instanceFactory:\\s*["']?ReactAppRuntime["']?/g,
604
+ 'instanceFactory: "ReactAppStarter"'
605
+ );
606
+ if (after !== before) writeFileSync(p, after, 'utf8');
607
+ }
608
+ }
609
+ }
610
+
560
611
  console.log('Project created: ' + destDir);
561
- console.log('Template: ' + templateName);
612
+ console.log('Template: ' + templateName + (options.static ? ' (static / ReactAppStarter)' : ''));
562
613
  console.log('');
563
614
 
564
615
  // Build the "Next steps" hint from the actual scripts the
@@ -37,6 +37,12 @@ dependencies:
37
37
  version: "^11.1.0"
38
38
  - name: "@fastify/rate-limit"
39
39
  version: "^9.0.0"
40
+ # .env loading — main.ts calls `import 'dotenv/config'` first so
41
+ # PORT and other env vars populate before any module reads them.
42
+ # Mirrors vite's loadEnv on the frontend side, so `npm run
43
+ # dev:backend` honours the project's .env without a set -a dance.
44
+ - name: "dotenv"
45
+ version: "^16.4.0"
40
46
 
41
47
  dev:
42
48
  - name: "@types/node"
@@ -99,6 +105,7 @@ requirements:
99
105
  "fastify": "^5.8.3"
100
106
  "@fastify/cors": "^10.0.0"
101
107
  "yaml": "^2.3.0"
108
+ "dotenv": "^16.4.0"
102
109
  devDependencies:
103
110
  "tsx": "^4.0.0"
104
111
  scripts:
@@ -49,6 +49,27 @@ export default function generateFastifyServer(context: TemplateContext): string
49
49
  * Generated from SpecVerse specification
50
50
  */
51
51
 
52
+ // Load .env from the closest ancestor that has one. Walks up from
53
+ // this file's directory so the backend picks up the project-root
54
+ // .env whether it's running in the monorepo layout
55
+ // (generated/code/backend/src/main.ts with .env at generated/code/)
56
+ // or the standalone layout (generated/code/src/main.ts with .env at
57
+ // generated/code/). dotenv's default cwd-based behaviour doesn't
58
+ // handle the monorepo case because npm workspaces \`cd\`s into the
59
+ // workspace before running the script.
60
+ import { config as loadEnv } from 'dotenv';
61
+ import { existsSync } from 'fs';
62
+ import { resolve as resolvePath, dirname, join } from 'path';
63
+ import { fileURLToPath } from 'url';
64
+ {
65
+ let dir = dirname(fileURLToPath(import.meta.url));
66
+ while (dir !== '/' && dir !== dirname(dir)) {
67
+ const candidate = join(dir, '.env');
68
+ if (existsSync(candidate)) { loadEnv({ path: candidate }); break; }
69
+ dir = dirname(dir);
70
+ }
71
+ }
72
+
52
73
  import Fastify from 'fastify';
53
74
  import cors from '@fastify/cors';
54
75
  import { PrismaClient } from '@prisma/client';
@@ -112,9 +133,9 @@ ${hasEvents ? ` // Register WebSocket bridge for real-time frontend events
112
133
  // fall back to 127.0.0.1 cleanly. Binding :: fixes both cases.
113
134
  await fastify.listen({ port, host: '::' });
114
135
  console.log(\`Server running at http://localhost:\${port}\`);
115
- console.log(\`API endpoints: ${modelNames.map((n: string) => `/api/${n.toLowerCase()}s`).join(', ')}\`);
136
+ console.log(\`API endpoints: ${modelNames.map((n: string) => `/api/${pluralizeLower(n)}`).join(', ')}\`);
116
137
  ${hasEvents ? ` console.log(\`WebSocket: ws://localhost:\${port}/ws\`);
117
- console.log(\`Events: ${specEvents.join(', ')}\`);` : ''}
138
+ console.log(\`Events: ${specEvents.length} wired (GET /api/runtime/events for the full list)\`);` : ''}
118
139
  } catch (err) {
119
140
  fastify.log.error(err);
120
141
  process.exit(1);
@@ -124,3 +145,16 @@ ${hasEvents ? ` console.log(\`WebSocket: ws://localhost:\${port}/ws\`);
124
145
  start();
125
146
  `;
126
147
  }
148
+
149
+ /**
150
+ * Minimal English pluralizer — matches the same rules the routes
151
+ * generator uses so the startup banner's route list agrees with the
152
+ * actual routes the server exposes. "Category" → "categories", not
153
+ * "categorys"; "Box" → "boxes"; "Item" → "items".
154
+ */
155
+ function pluralizeLower(s: string): string {
156
+ const lower = s.toLowerCase();
157
+ if (/[^aeiou]y$/.test(lower)) return lower.slice(0, -1) + 'ies';
158
+ if (/(s|x|z|ch|sh)$/.test(lower)) return lower + 'es';
159
+ return lower + 's';
160
+ }
@@ -168,9 +168,11 @@ function buildMissingBackRefs(
168
168
  r.target === target && (r.type === 'hasMany' || r.type === 'hasOne')
169
169
  );
170
170
  const needsFieldInFk = parentRelsToSameTarget.length > 1;
171
+ // Back-ref FK column — same camelCase convention as the forward
172
+ // belongsTo path above (see line ~455).
171
173
  const fkSuffix = needsFieldInFk
172
- ? camelToSnake(fieldName) + '_id'
173
- : camelToSnake(model.name.charAt(0).toLowerCase() + model.name.slice(1)) + '_id';
174
+ ? fieldName + 'Id'
175
+ : model.name.charAt(0).toLowerCase() + model.name.slice(1) + 'Id';
174
176
  const fkName = fkSuffix;
175
177
  const fkPadding = ' '.repeat(Math.max(1, 15 - fkName.length));
176
178
  const refFieldName = needsFieldInFk
@@ -451,8 +453,13 @@ function generateRelationship(rel: any, model: any, relationMap: Map<string, str
451
453
 
452
454
  switch (rel.type) {
453
455
  case 'belongsTo':
454
- // Foreign key field — use snake_case for the FK column name
455
- const fkBase = rel.foreignKey || camelToSnake(name) + '_id';
456
+ // Foreign key field — use camelCase, matching the casing of
457
+ // every other attribute the generator emits. Earlier revisions
458
+ // used snake_case here, which inconsistently mixed
459
+ // `createdAt` (camel) with `category_id` (snake) in the same
460
+ // schema and leaked through to API responses, breaking
461
+ // camelCase FK lookups on the frontend.
462
+ const fkBase = rel.foreignKey || name + 'Id';
456
463
  const fkPadding = ' '.repeat(Math.max(1, 15 - fkBase.length));
457
464
  // Add @unique if the parent has a hasOne relation pointing to this model
458
465
  const isUniqueFK = hasOneTargets.has(`${rel.target}->${model.name}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@specverse/engines",
3
- "version": "4.1.30",
4
- "description": "SpecVerse toolchain \u2014 parser, inference, realize, generators, AI, registry",
3
+ "version": "4.2.1",
4
+ "description": "SpecVerse toolchain parser, inference, realize, generators, AI, registry",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -65,4 +65,4 @@
65
65
  "access": "public"
66
66
  },
67
67
  "license": "MIT"
68
- }
68
+ }