@specverse/engines 4.1.30 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) 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/dist/inference/comprehensive-engine.d.ts.map +1 -1
  5. package/dist/inference/comprehensive-engine.js +3 -19
  6. package/dist/inference/comprehensive-engine.js.map +1 -1
  7. package/dist/inference/core/rule-engine.d.ts +31 -0
  8. package/dist/inference/core/rule-engine.d.ts.map +1 -1
  9. package/dist/inference/core/rule-engine.js +117 -33
  10. package/dist/inference/core/rule-engine.js.map +1 -1
  11. package/dist/inference/core/rule-file-types.d.ts +0 -2
  12. package/dist/inference/core/rule-file-types.d.ts.map +1 -1
  13. package/dist/inference/core/rule-file-types.js +3 -6
  14. package/dist/inference/core/rule-file-types.js.map +1 -1
  15. package/dist/inference/core/rule-loader.d.ts +5 -15
  16. package/dist/inference/core/rule-loader.d.ts.map +1 -1
  17. package/dist/inference/core/rule-loader.js +43 -132
  18. package/dist/inference/core/rule-loader.js.map +1 -1
  19. package/dist/inference/core/types.d.ts +0 -6
  20. package/dist/inference/core/types.d.ts.map +1 -1
  21. package/dist/inference/core/types.js +0 -4
  22. package/dist/inference/core/types.js.map +1 -1
  23. package/dist/inference/logical/generators/component-type-resolver.d.ts +0 -26
  24. package/dist/inference/logical/generators/component-type-resolver.d.ts.map +1 -1
  25. package/dist/inference/logical/generators/component-type-resolver.js +0 -19
  26. package/dist/inference/logical/generators/component-type-resolver.js.map +1 -1
  27. package/dist/inference/logical/generators/specialist-view-expander.d.ts +1 -17
  28. package/dist/inference/logical/generators/specialist-view-expander.d.ts.map +1 -1
  29. package/dist/inference/logical/generators/specialist-view-expander.js +0 -15
  30. package/dist/inference/logical/generators/specialist-view-expander.js.map +1 -1
  31. package/dist/inference/logical/generators/view-generator.d.ts +4 -14
  32. package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
  33. package/dist/inference/logical/generators/view-generator.js +6 -26
  34. package/dist/inference/logical/generators/view-generator.js.map +1 -1
  35. package/dist/inference/logical/index.d.ts +2 -2
  36. package/dist/inference/logical/index.d.ts.map +1 -1
  37. package/dist/inference/logical/logical-engine.d.ts.map +1 -1
  38. package/dist/inference/logical/logical-engine.js +17 -80
  39. package/dist/inference/logical/logical-engine.js.map +1 -1
  40. package/dist/inference/quint-transpiler.d.ts +5 -3
  41. package/dist/inference/quint-transpiler.d.ts.map +1 -1
  42. package/dist/inference/quint-transpiler.js +11 -6
  43. package/dist/inference/quint-transpiler.js.map +1 -1
  44. package/dist/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.js +110 -0
  45. package/dist/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.js +121 -0
  46. package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +78 -0
  47. package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +190 -0
  48. package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +45 -0
  49. package/dist/libs/instance-factories/applications/templates/react-starter/html-to-jsx.js +192 -0
  50. package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +46 -0
  51. package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +30 -0
  52. package/dist/libs/instance-factories/applications/templates/react-starter/package-json-generator.js +38 -0
  53. package/dist/libs/instance-factories/applications/templates/react-starter/regen-safety.js +89 -0
  54. package/dist/libs/instance-factories/applications/templates/react-starter/view-emitter.js +56 -0
  55. package/dist/libs/instance-factories/applications/templates/react-starter/views-generator.js +66 -0
  56. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +14 -11
  57. package/dist/realize/index.d.ts.map +1 -1
  58. package/dist/realize/index.js +15 -22
  59. package/dist/realize/index.js.map +1 -1
  60. package/dist/registry/utils/manifest-adapter.d.ts +8 -1
  61. package/dist/registry/utils/manifest-adapter.d.ts.map +1 -1
  62. package/dist/registry/utils/manifest-adapter.js +8 -1
  63. package/dist/registry/utils/manifest-adapter.js.map +1 -1
  64. package/libs/instance-factories/applications/react-app-starter.yaml +150 -0
  65. package/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
  66. package/libs/instance-factories/applications/templates/react-starter/__tests__/dashboard-body-composer.test.ts +153 -0
  67. package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +145 -0
  68. package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +175 -0
  69. package/libs/instance-factories/applications/templates/react-starter/__tests__/helpers-emitter.test.ts +55 -0
  70. package/libs/instance-factories/applications/templates/react-starter/__tests__/html-to-jsx.test.ts +140 -0
  71. package/libs/instance-factories/applications/templates/react-starter/__tests__/list-body-composer.test.ts +146 -0
  72. package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +163 -0
  73. package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p2-factory-imports.test.ts +116 -0
  74. package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p3-rendered-output.test.ts +183 -0
  75. package/libs/instance-factories/applications/templates/react-starter/__tests__/regen-safety.test.ts +144 -0
  76. package/libs/instance-factories/applications/templates/react-starter/__tests__/starter-generators.test.ts +114 -0
  77. package/libs/instance-factories/applications/templates/react-starter/__tests__/view-emitter.test.ts +107 -0
  78. package/libs/instance-factories/applications/templates/react-starter/__tests__/views-generator.test.ts +139 -0
  79. package/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.ts +141 -0
  80. package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts +174 -0
  81. package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +135 -0
  82. package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +306 -0
  83. package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +60 -0
  84. package/libs/instance-factories/applications/templates/react-starter/html-to-jsx.ts +334 -0
  85. package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +120 -0
  86. package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +80 -0
  87. package/libs/instance-factories/applications/templates/react-starter/package-json-generator.ts +57 -0
  88. package/libs/instance-factories/applications/templates/react-starter/regen-safety.ts +157 -0
  89. package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +47 -0
  90. package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +94 -0
  91. package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +114 -0
  92. package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +72 -0
  93. package/libs/instance-factories/applications/templates/react-starter/view-emitter.ts +151 -0
  94. package/libs/instance-factories/applications/templates/react-starter/views-generator.ts +137 -0
  95. package/libs/instance-factories/cli/templates/commander/command-generator.ts +14 -11
  96. package/package.json +3 -3
  97. package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +0 -530
  98. package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +0 -73
  99. package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +0 -99
  100. package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +0 -49
  101. package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +0 -156
  102. package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +0 -935
  103. package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +0 -143
  104. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +0 -646
  105. package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +0 -65
  106. package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +0 -143
  107. package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +0 -143
  108. package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +0 -355
  109. package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +0 -91
  110. package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +0 -79
  111. package/dist/libs/instance-factories/views/index.js +0 -48
  112. package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +0 -742
  113. package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +0 -824
  114. package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +0 -719
  115. package/dist/libs/instance-factories/views/templates/react/app-generator.js +0 -45
  116. package/dist/libs/instance-factories/views/templates/react/components-generator.js +0 -820
  117. package/dist/libs/instance-factories/views/templates/react/forms-generator.js +0 -275
  118. package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +0 -46
  119. package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +0 -81
  120. package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +0 -9
  121. package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +0 -23
  122. package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +0 -21
  123. package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +0 -299
  124. package/dist/libs/instance-factories/views/templates/react/router-generator.js +0 -136
  125. package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +0 -107
  126. package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +0 -187
  127. package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +0 -7
  128. package/dist/libs/instance-factories/views/templates/react/types-generator.js +0 -56
  129. package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +0 -27
  130. package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +0 -29
  131. package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +0 -261
  132. package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +0 -34
  133. package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -800
  134. package/dist/libs/instance-factories/views/templates/shared/base-generator.js +0 -305
  135. package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +0 -517
  136. package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
  137. package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +0 -445
  138. package/dist/libs/instance-factories/views/templates/shared/index.js +0 -80
  139. package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +0 -210
  140. package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +0 -492
  141. package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -321
  142. package/dist/realize/index.js.bak +0 -758
  143. package/libs/instance-factories/applications/react-app.yaml +0 -186
  144. package/libs/instance-factories/applications/templates/react/_view-components-source.ts +0 -555
  145. package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +0 -94
  146. package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +0 -106
  147. package/libs/instance-factories/applications/templates/react/package-json-generator.ts +0 -57
  148. package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +0 -179
  149. package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +0 -1347
  150. package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +0 -150
  151. package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +0 -704
  152. package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +0 -84
  153. package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +0 -150
  154. package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +0 -150
  155. package/libs/instance-factories/applications/templates/react/view-form-generator.ts +0 -362
  156. package/libs/instance-factories/applications/templates/react/view-list-generator.ts +0 -98
  157. package/libs/instance-factories/applications/templates/react/view-router-generator.ts +0 -89
  158. package/libs/instance-factories/views/README.md +0 -62
  159. package/libs/instance-factories/views/index.d.ts +0 -13
  160. package/libs/instance-factories/views/index.d.ts.map +0 -1
  161. package/libs/instance-factories/views/index.js +0 -18
  162. package/libs/instance-factories/views/index.js.map +0 -1
  163. package/libs/instance-factories/views/index.ts +0 -45
  164. package/libs/instance-factories/views/react-components.yaml +0 -129
  165. package/libs/instance-factories/views/templates/ARCHITECTURE.md +0 -198
  166. package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +0 -869
  167. package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +0 -953
  168. package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +0 -806
  169. package/libs/instance-factories/views/templates/react/app-generator.ts +0 -55
  170. package/libs/instance-factories/views/templates/react/components-generator.ts +0 -938
  171. package/libs/instance-factories/views/templates/react/forms-generator.ts +0 -325
  172. package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +0 -57
  173. package/libs/instance-factories/views/templates/react/hooks-generator.ts +0 -106
  174. package/libs/instance-factories/views/templates/react/index-css-generator.ts +0 -14
  175. package/libs/instance-factories/views/templates/react/index-html-generator.ts +0 -34
  176. package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +0 -29
  177. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +0 -152
  178. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +0 -1
  179. package/libs/instance-factories/views/templates/react/react-component-generator.js +0 -398
  180. package/libs/instance-factories/views/templates/react/react-component-generator.js.map +0 -1
  181. package/libs/instance-factories/views/templates/react/react-component-generator.ts +0 -533
  182. package/libs/instance-factories/views/templates/react/router-generator.ts +0 -197
  183. package/libs/instance-factories/views/templates/react/router-generic-generator.ts +0 -132
  184. package/libs/instance-factories/views/templates/react/shared-utils-generator.ts +0 -196
  185. package/libs/instance-factories/views/templates/react/spec-json-generator.ts +0 -17
  186. package/libs/instance-factories/views/templates/react/types-generator.ts +0 -76
  187. package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +0 -42
  188. package/libs/instance-factories/views/templates/react/vite-config-generator.ts +0 -38
  189. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +0 -1
  190. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +0 -1
  191. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +0 -474
  192. package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +0 -242
  193. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +0 -77
  194. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +0 -1
  195. package/libs/instance-factories/views/templates/shared/adapter-types.js +0 -47
  196. package/libs/instance-factories/views/templates/shared/adapter-types.js.map +0 -1
  197. package/libs/instance-factories/views/templates/shared/adapter-types.ts +0 -142
  198. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +0 -63
  199. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +0 -1
  200. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -822
  201. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +0 -1
  202. package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +0 -908
  203. package/libs/instance-factories/views/templates/shared/base-generator.d.ts +0 -247
  204. package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +0 -1
  205. package/libs/instance-factories/views/templates/shared/base-generator.js +0 -363
  206. package/libs/instance-factories/views/templates/shared/base-generator.js.map +0 -1
  207. package/libs/instance-factories/views/templates/shared/base-generator.ts +0 -608
  208. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +0 -254
  209. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +0 -1
  210. package/libs/instance-factories/views/templates/shared/component-metadata.js +0 -602
  211. package/libs/instance-factories/views/templates/shared/component-metadata.js.map +0 -1
  212. package/libs/instance-factories/views/templates/shared/component-metadata.ts +0 -803
  213. package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +0 -250
  214. package/libs/instance-factories/views/templates/shared/composite-patterns.ts +0 -535
  215. package/libs/instance-factories/views/templates/shared/index.ts +0 -68
  216. package/libs/instance-factories/views/templates/shared/pattern-validator.ts +0 -279
  217. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +0 -149
  218. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +0 -1
  219. package/libs/instance-factories/views/templates/shared/property-mapper.js +0 -580
  220. package/libs/instance-factories/views/templates/shared/property-mapper.js.map +0 -1
  221. package/libs/instance-factories/views/templates/shared/property-mapper.ts +0 -700
  222. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +0 -143
  223. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +0 -1
  224. package/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -420
  225. package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +0 -1
  226. package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +0 -539
@@ -0,0 +1,72 @@
1
+ /**
2
+ * {{MODEL_NAME}}ListView — generated by @specverse/realize (ReactAppStarter)
3
+ *
4
+ * Safe to edit. Edits are preserved across regeneration via content
5
+ * hashing (see .specverse-gen/hashes.json). To accept an upstream
6
+ * regeneration of this file, delete it first, then run `spv realize`.
7
+ */
8
+ import { useState, useMemo } from 'react';
9
+ import { use{{PLURAL_MODEL}}Query, useDelete{{MODEL_NAME}}Mutation } from '../hooks/useApi';
10
+ import type { {{MODEL_NAME}} } from '../types/api';
11
+
12
+ interface {{MODEL_NAME}}ListViewProps {
13
+ onSelect?: (item: {{MODEL_NAME}}) => void;
14
+ onCreate?: () => void;
15
+ }
16
+
17
+ export function {{MODEL_NAME}}ListView({ onSelect, onCreate }: {{MODEL_NAME}}ListViewProps) {
18
+ const { data: items = [], isLoading, error } = use{{PLURAL_MODEL}}Query();
19
+ const deleteItem = useDelete{{MODEL_NAME}}Mutation();
20
+ const [searchTerm, setSearchTerm] = useState('');
21
+
22
+ const filtered = useMemo(
23
+ () =>
24
+ items.filter((item: {{MODEL_NAME}}) =>
25
+ // Cast to `any` inside JSX context — `Record<string, unknown>`
26
+ // looks like a JSX opening tag to the TSX parser when this
27
+ // expression ends up inside JSX. `any` is safe here: we only
28
+ // read values for substring-matching against the search term.
29
+ Object.values(item as any).some(v =>
30
+ String(v ?? '').toLowerCase().includes(searchTerm.toLowerCase())
31
+ )
32
+ ),
33
+ [items, searchTerm]
34
+ );
35
+
36
+ if (isLoading) return <div className="p-4 text-gray-500">Loading {{PLURAL_LOWER}}…</div>;
37
+ if (error) return <div className="p-4 text-red-600">Error loading {{PLURAL_LOWER}}: {String(error)}</div>;
38
+
39
+ return (
40
+ <div className="p-6 space-y-4">
41
+ <div className="flex items-center justify-between">
42
+ <input
43
+ type="search"
44
+ placeholder="Search {{PLURAL_LOWER}}…"
45
+ value={searchTerm}
46
+ onChange={e => setSearchTerm(e.target.value)}
47
+ className="w-64 rounded border border-gray-300 px-3 py-2 text-sm"
48
+ />
49
+ <button
50
+ type="button"
51
+ onClick={onCreate}
52
+ className="rounded bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700"
53
+ >
54
+ + New {{MODEL_NAME}}
55
+ </button>
56
+ </div>
57
+
58
+ {/* Pattern-rendered list body (table + rows). Edit freely. */}
59
+ {{BODY}}
60
+
61
+ {filtered.length === 0 && (
62
+ <div className="p-8 text-center text-gray-400">No {{PLURAL_LOWER}} yet.</div>
63
+ )}
64
+
65
+ {deleteItem.isError && (
66
+ <div className="p-2 text-sm text-red-600">
67
+ Delete failed: {String(deleteItem.error)}
68
+ </div>
69
+ )}
70
+ </div>
71
+ );
72
+ }
@@ -0,0 +1,151 @@
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
+
20
+ /**
21
+ * The minimal shape of a view spec this emitter needs. Matches the
22
+ * inferred spec format — `view.type` drives skeleton selection,
23
+ * `view.model` identifies the primary model, `view.uiComponents` (if
24
+ * present) overrides inference defaults.
25
+ */
26
+ export interface ViewSpec {
27
+ type: string;
28
+ model?: string;
29
+ uiComponents?: Record<string, unknown>;
30
+ [key: string]: unknown;
31
+ }
32
+
33
+ /**
34
+ * The minimal shape of a model spec this emitter needs. Field details
35
+ * flow through to the Tailwind adapter which decides column selection.
36
+ */
37
+ export interface ModelSpec {
38
+ name: string;
39
+ attributes: Record<string, unknown>;
40
+ relationships?: Record<string, unknown>;
41
+ lifecycles?: Record<string, unknown>;
42
+ }
43
+
44
+ export interface EmitContext {
45
+ view: ViewSpec;
46
+ viewName: string; // e.g. "PostListView"
47
+ model: ModelSpec;
48
+ modelSchemas: Record<string, ModelSpec>;
49
+ /** Pluggable so we can stub the renderer in unit tests. */
50
+ renderBody: RenderBodyFn;
51
+ }
52
+
53
+ /**
54
+ * Render the body of a view as JSX-safe source — the exact string
55
+ * that is dropped at `{{BODY}}` in the skeleton.
56
+ *
57
+ * The renderBody implementation is responsible for the whole body
58
+ * pipeline: invoke the canonical Tailwind adapter for static shell
59
+ * rendering, run that HTML through `htmlToJsx`, and inject any JSX
60
+ * expressions (`.map()`, event handlers). Keeping it here means
61
+ * view-emitter stays a pure orchestrator and can't accidentally
62
+ * double-transform JSX that's already JSX.
63
+ */
64
+ export type RenderBodyFn = (context: EmitContext) => string;
65
+
66
+ /**
67
+ * Emit a complete .tsx file for a single view. Pure function — all
68
+ * inputs come in via the context argument so it's easy to test.
69
+ */
70
+ export function emitView(context: EmitContext): string {
71
+ const skeleton = loadSkeleton(context.view.type);
72
+ const bodyJsx = context.renderBody(context);
73
+
74
+ const substitutions = buildSubstitutions(context, bodyJsx);
75
+ return applySubstitutions(skeleton, substitutions);
76
+ }
77
+
78
+ // ──────────────────────────────────────────────────────────────────────
79
+ // Skeleton loading
80
+ // ──────────────────────────────────────────────────────────────────────
81
+
82
+ const SKELETON_BY_VIEW_TYPE: Record<string, string> = {
83
+ list: 'list.tsx.template',
84
+ detail: 'detail.tsx.template',
85
+ form: 'form.tsx.template',
86
+ dashboard: 'dashboard.tsx.template',
87
+ // Specialist types (board, timeline, calendar, analytics, workflow,
88
+ // wizard, comparison, settings, map, feed, profile) come online in
89
+ // Phase 2e.
90
+ };
91
+
92
+ function loadSkeleton(viewType: string): string {
93
+ const filename = SKELETON_BY_VIEW_TYPE[viewType.toLowerCase()];
94
+ if (!filename) {
95
+ throw new Error(
96
+ `No skeleton registered for view type "${viewType}". ` +
97
+ `Known types: ${Object.keys(SKELETON_BY_VIEW_TYPE).join(', ')}.`
98
+ );
99
+ }
100
+
101
+ const here = dirname(fileURLToPath(import.meta.url));
102
+ const path = join(here, 'skeletons', filename);
103
+ return readFileSync(path, 'utf8');
104
+ }
105
+
106
+ // ──────────────────────────────────────────────────────────────────────
107
+ // Substitution
108
+ // ──────────────────────────────────────────────────────────────────────
109
+
110
+ interface Substitutions {
111
+ MODEL_NAME: string;
112
+ PLURAL_MODEL: string;
113
+ PLURAL_LOWER: string;
114
+ SINGULAR_LOWER: string;
115
+ BODY: string;
116
+ }
117
+
118
+ function buildSubstitutions(context: EmitContext, body: string): Substitutions {
119
+ const modelName = context.model.name;
120
+ const pluralModel = pluralize(modelName);
121
+ return {
122
+ MODEL_NAME: modelName,
123
+ PLURAL_MODEL: pluralModel,
124
+ PLURAL_LOWER: pluralModel.toLowerCase(),
125
+ SINGULAR_LOWER: modelName.toLowerCase(),
126
+ BODY: body,
127
+ };
128
+ }
129
+
130
+ function applySubstitutions(template: string, subs: Substitutions): string {
131
+ let out = template;
132
+ for (const [key, value] of Object.entries(subs)) {
133
+ // Replace every occurrence of {{KEY}} — plain string substitution,
134
+ // no expression evaluation. Escape regex special chars in the key
135
+ // (defensive; keys are ASCII constants).
136
+ const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
137
+ out = out.replace(pattern, value);
138
+ }
139
+ return out;
140
+ }
141
+
142
+ /**
143
+ * Minimal English pluralizer matching the adapter's conventions.
144
+ * Good enough for generated code — users can edit the output if they
145
+ * want a different pluralization.
146
+ */
147
+ function pluralize(s: string): string {
148
+ if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + 'ies';
149
+ if (/(s|x|z|ch|sh)$/i.test(s)) return s + 'es';
150
+ return s + 's';
151
+ }
@@ -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;
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.0",
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
+ }