@topogram/cli 0.3.34

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 (257) hide show
  1. package/ARCHITECTURE.md +67 -0
  2. package/CHANGELOG.md +240 -0
  3. package/README.md +223 -0
  4. package/package.json +51 -0
  5. package/src/adoption/index.js +3 -0
  6. package/src/adoption/plan.js +702 -0
  7. package/src/adoption/reporting.js +464 -0
  8. package/src/adoption/review-groups.js +313 -0
  9. package/src/agent-ops/query-builders.js +5012 -0
  10. package/src/archive/archive.js +141 -0
  11. package/src/archive/compact.js +26 -0
  12. package/src/archive/jsonl.js +70 -0
  13. package/src/archive/resolver-bridge.js +82 -0
  14. package/src/archive/schema.js +87 -0
  15. package/src/archive/unarchive.js +108 -0
  16. package/src/catalog.js +752 -0
  17. package/src/cli/catalog-alias.js +166 -0
  18. package/src/cli.js +9738 -0
  19. package/src/component-behavior.js +173 -0
  20. package/src/example-implementation.js +91 -0
  21. package/src/format.js +19 -0
  22. package/src/generator/adapters.d.ts +4 -0
  23. package/src/generator/adapters.js +325 -0
  24. package/src/generator/api.d.ts +1 -0
  25. package/src/generator/api.js +1196 -0
  26. package/src/generator/check.js +355 -0
  27. package/src/generator/component-conformance.js +767 -0
  28. package/src/generator/components.js +39 -0
  29. package/src/generator/context/bundle.js +291 -0
  30. package/src/generator/context/diff.js +256 -0
  31. package/src/generator/context/digest.js +182 -0
  32. package/src/generator/context/domain-coverage.js +94 -0
  33. package/src/generator/context/domain-page.js +137 -0
  34. package/src/generator/context/index.js +42 -0
  35. package/src/generator/context/report.js +121 -0
  36. package/src/generator/context/shared.js +1397 -0
  37. package/src/generator/context/slice.js +703 -0
  38. package/src/generator/context/task-mode.js +466 -0
  39. package/src/generator/docs.js +327 -0
  40. package/src/generator/index.js +161 -0
  41. package/src/generator/native/parity-bundle.js +311 -0
  42. package/src/generator/output.js +300 -0
  43. package/src/generator/registry.js +482 -0
  44. package/src/generator/runtime/app-bundle.js +456 -0
  45. package/src/generator/runtime/bundle-shared.js +166 -0
  46. package/src/generator/runtime/compile-check.js +163 -0
  47. package/src/generator/runtime/deployment.js +287 -0
  48. package/src/generator/runtime/environment.js +635 -0
  49. package/src/generator/runtime/index.js +32 -0
  50. package/src/generator/runtime/runtime-check.js +554 -0
  51. package/src/generator/runtime/shared.js +515 -0
  52. package/src/generator/runtime/smoke.js +219 -0
  53. package/src/generator/schema.js +204 -0
  54. package/src/generator/sdlc/board.js +66 -0
  55. package/src/generator/sdlc/doc-page.js +53 -0
  56. package/src/generator/sdlc/index.js +23 -0
  57. package/src/generator/sdlc/release-notes.js +62 -0
  58. package/src/generator/sdlc/traceability-matrix.js +65 -0
  59. package/src/generator/shared.js +29 -0
  60. package/src/generator/surfaces/contracts.js +146 -0
  61. package/src/generator/surfaces/databases/contract.js +40 -0
  62. package/src/generator/surfaces/databases/index.js +84 -0
  63. package/src/generator/surfaces/databases/lifecycle-shared.d.ts +1 -0
  64. package/src/generator/surfaces/databases/lifecycle-shared.js +612 -0
  65. package/src/generator/surfaces/databases/migration-plan.js +281 -0
  66. package/src/generator/surfaces/databases/postgres/capabilities.js +14 -0
  67. package/src/generator/surfaces/databases/postgres/drizzle.js +99 -0
  68. package/src/generator/surfaces/databases/postgres/index.js +9 -0
  69. package/src/generator/surfaces/databases/postgres/lifecycle.js +16 -0
  70. package/src/generator/surfaces/databases/postgres/prisma.js +159 -0
  71. package/src/generator/surfaces/databases/postgres/sql-migration.js +102 -0
  72. package/src/generator/surfaces/databases/postgres/sql-schema.js +34 -0
  73. package/src/generator/surfaces/databases/shared.d.ts +1 -0
  74. package/src/generator/surfaces/databases/shared.js +350 -0
  75. package/src/generator/surfaces/databases/snapshot.js +96 -0
  76. package/src/generator/surfaces/databases/sqlite/capabilities.js +14 -0
  77. package/src/generator/surfaces/databases/sqlite/index.js +8 -0
  78. package/src/generator/surfaces/databases/sqlite/lifecycle.js +16 -0
  79. package/src/generator/surfaces/databases/sqlite/prisma.js +143 -0
  80. package/src/generator/surfaces/databases/sqlite/sql-migration.js +65 -0
  81. package/src/generator/surfaces/databases/sqlite/sql-schema.js +27 -0
  82. package/src/generator/surfaces/index.js +25 -0
  83. package/src/generator/surfaces/native/swiftui-app.js +38 -0
  84. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +20 -0
  85. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +26 -0
  86. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +682 -0
  87. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoAPIClient.swift +156 -0
  88. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoSwiftUIApp.swift +44 -0
  89. package/src/generator/surfaces/native/swiftui-templates/runtime/Visibility.swift +183 -0
  90. package/src/generator/surfaces/services/express.d.ts +1 -0
  91. package/src/generator/surfaces/services/express.js +766 -0
  92. package/src/generator/surfaces/services/hono.d.ts +1 -0
  93. package/src/generator/surfaces/services/hono.js +204 -0
  94. package/src/generator/surfaces/services/index.js +42 -0
  95. package/src/generator/surfaces/services/persistence-wiring.js +240 -0
  96. package/src/generator/surfaces/services/runtime-helpers.js +631 -0
  97. package/src/generator/surfaces/services/server-contract.js +80 -0
  98. package/src/generator/surfaces/services/stateless.d.ts +1 -0
  99. package/src/generator/surfaces/services/stateless.js +97 -0
  100. package/src/generator/surfaces/shared.js +64 -0
  101. package/src/generator/surfaces/web/api-client.js +1 -0
  102. package/src/generator/surfaces/web/forms.js +1 -0
  103. package/src/generator/surfaces/web/index.d.ts +2 -0
  104. package/src/generator/surfaces/web/index.js +53 -0
  105. package/src/generator/surfaces/web/react-components.js +248 -0
  106. package/src/generator/surfaces/web/react.js +538 -0
  107. package/src/generator/surfaces/web/routes.js +1 -0
  108. package/src/generator/surfaces/web/screens.js +1 -0
  109. package/src/generator/surfaces/web/shared.js +369 -0
  110. package/src/generator/surfaces/web/sveltekit-actions.js +28 -0
  111. package/src/generator/surfaces/web/sveltekit-components.js +234 -0
  112. package/src/generator/surfaces/web/sveltekit.js +426 -0
  113. package/src/generator/surfaces/web/ui-web-contract.js +65 -0
  114. package/src/generator/surfaces/web/vanilla.js +239 -0
  115. package/src/generator/verification.js +84 -0
  116. package/src/generator.js +1 -0
  117. package/src/import/core/context.js +52 -0
  118. package/src/import/core/contracts.js +23 -0
  119. package/src/import/core/registry.js +81 -0
  120. package/src/import/core/runner.js +646 -0
  121. package/src/import/core/shared.js +910 -0
  122. package/src/import/enrichers/auth-session.js +18 -0
  123. package/src/import/enrichers/django-rest.js +226 -0
  124. package/src/import/enrichers/doc-linking.js +20 -0
  125. package/src/import/enrichers/rails-controllers.js +246 -0
  126. package/src/import/enrichers/rails-models.js +130 -0
  127. package/src/import/enrichers/workflow-target-state.js +10 -0
  128. package/src/import/extractors/api/aspnet-core.js +304 -0
  129. package/src/import/extractors/api/django-routes.js +318 -0
  130. package/src/import/extractors/api/express.js +154 -0
  131. package/src/import/extractors/api/fastify.js +371 -0
  132. package/src/import/extractors/api/flutter-dio.js +135 -0
  133. package/src/import/extractors/api/generic-route-fallback.js +90 -0
  134. package/src/import/extractors/api/graphql-code-first.js +565 -0
  135. package/src/import/extractors/api/graphql-sdl.js +309 -0
  136. package/src/import/extractors/api/jaxrs.js +303 -0
  137. package/src/import/extractors/api/micronaut.js +213 -0
  138. package/src/import/extractors/api/next-route.js +50 -0
  139. package/src/import/extractors/api/next-server-action.js +51 -0
  140. package/src/import/extractors/api/nextauth.js +52 -0
  141. package/src/import/extractors/api/openapi-code.js +242 -0
  142. package/src/import/extractors/api/openapi.js +232 -0
  143. package/src/import/extractors/api/rails-routes.js +230 -0
  144. package/src/import/extractors/api/react-native-repository.js +128 -0
  145. package/src/import/extractors/api/retrofit.js +103 -0
  146. package/src/import/extractors/api/spring-web.js +372 -0
  147. package/src/import/extractors/api/swift-webapi.js +116 -0
  148. package/src/import/extractors/api/trpc.js +212 -0
  149. package/src/import/extractors/db/django-models.js +232 -0
  150. package/src/import/extractors/db/dotnet-models.js +93 -0
  151. package/src/import/extractors/db/drizzle.js +242 -0
  152. package/src/import/extractors/db/ef-core.js +221 -0
  153. package/src/import/extractors/db/flutter-entities.js +120 -0
  154. package/src/import/extractors/db/jpa.js +120 -0
  155. package/src/import/extractors/db/liquibase.js +180 -0
  156. package/src/import/extractors/db/mybatis-xml.js +145 -0
  157. package/src/import/extractors/db/prisma.js +185 -0
  158. package/src/import/extractors/db/rails-schema.js +175 -0
  159. package/src/import/extractors/db/react-native-entities.js +95 -0
  160. package/src/import/extractors/db/room.js +193 -0
  161. package/src/import/extractors/db/snapshot.js +112 -0
  162. package/src/import/extractors/db/sql.js +180 -0
  163. package/src/import/extractors/db/swiftdata.js +137 -0
  164. package/src/import/extractors/ui/android-compose.js +230 -0
  165. package/src/import/extractors/ui/backend-only.js +70 -0
  166. package/src/import/extractors/ui/blazor.js +227 -0
  167. package/src/import/extractors/ui/flutter-screens.js +152 -0
  168. package/src/import/extractors/ui/maui-xaml.js +135 -0
  169. package/src/import/extractors/ui/next-app-router.js +83 -0
  170. package/src/import/extractors/ui/next-pages-router.js +141 -0
  171. package/src/import/extractors/ui/razor-pages.js +181 -0
  172. package/src/import/extractors/ui/react-native-screens.js +166 -0
  173. package/src/import/extractors/ui/react-router.js +139 -0
  174. package/src/import/extractors/ui/sveltekit.js +123 -0
  175. package/src/import/extractors/ui/swiftui.js +193 -0
  176. package/src/import/extractors/ui/uikit.js +175 -0
  177. package/src/import/extractors/verification/generic.js +290 -0
  178. package/src/import/extractors/workflows/generic.js +137 -0
  179. package/src/import/index.js +7 -0
  180. package/src/import/provenance.js +158 -0
  181. package/src/new-project.js +2107 -0
  182. package/src/parser.js +439 -0
  183. package/src/policy/review-boundaries.js +165 -0
  184. package/src/project-config.js +535 -0
  185. package/src/proofs/backend-parity.js +19 -0
  186. package/src/proofs/contract-audit.js +220 -0
  187. package/src/proofs/ios-parity.js +7 -0
  188. package/src/proofs/issues-parity.js +10 -0
  189. package/src/proofs/web-parity.js +50 -0
  190. package/src/realization/api/build-api-realization.js +5 -0
  191. package/src/realization/api/index.js +1 -0
  192. package/src/realization/backend/build-backend-runtime-realization.js +82 -0
  193. package/src/realization/backend/index.d.ts +1 -0
  194. package/src/realization/backend/index.js +4 -0
  195. package/src/realization/db/build-db-realization.js +17 -0
  196. package/src/realization/db/index.js +3 -0
  197. package/src/realization/db/migration-plan.js +5 -0
  198. package/src/realization/db/snapshot.js +5 -0
  199. package/src/realization/ui/build-ui-shared-realization.js +305 -0
  200. package/src/realization/ui/build-web-realization.js +189 -0
  201. package/src/realization/ui/index.js +2 -0
  202. package/src/reconcile/docs.js +280 -0
  203. package/src/reconcile/index.js +3 -0
  204. package/src/reconcile/journeys.js +441 -0
  205. package/src/resolver/docs.js +1 -0
  206. package/src/resolver/enrich/acceptance-criterion.js +14 -0
  207. package/src/resolver/enrich/bug.js +12 -0
  208. package/src/resolver/enrich/component.js +2 -0
  209. package/src/resolver/enrich/index.js +1 -0
  210. package/src/resolver/enrich/pitch.js +18 -0
  211. package/src/resolver/enrich/requirement.js +20 -0
  212. package/src/resolver/enrich/task.js +16 -0
  213. package/src/resolver/expressions.js +1 -0
  214. package/src/resolver/index.js +2422 -0
  215. package/src/resolver/normalize.js +1 -0
  216. package/src/resolver.js +1 -0
  217. package/src/sdlc/adopt.js +65 -0
  218. package/src/sdlc/check.js +86 -0
  219. package/src/sdlc/dod/acceptance-criterion.js +22 -0
  220. package/src/sdlc/dod/bug.js +26 -0
  221. package/src/sdlc/dod/document.js +23 -0
  222. package/src/sdlc/dod/index.js +25 -0
  223. package/src/sdlc/dod/pitch.js +23 -0
  224. package/src/sdlc/dod/requirement.js +34 -0
  225. package/src/sdlc/dod/task.js +39 -0
  226. package/src/sdlc/explain.js +116 -0
  227. package/src/sdlc/history.js +80 -0
  228. package/src/sdlc/paths.js +11 -0
  229. package/src/sdlc/release.js +106 -0
  230. package/src/sdlc/scaffold.js +89 -0
  231. package/src/sdlc/status-filter.js +54 -0
  232. package/src/sdlc/transition.js +112 -0
  233. package/src/sdlc/transitions/acceptance-criterion.js +28 -0
  234. package/src/sdlc/transitions/bug.js +31 -0
  235. package/src/sdlc/transitions/document.js +29 -0
  236. package/src/sdlc/transitions/index.js +56 -0
  237. package/src/sdlc/transitions/pitch.js +34 -0
  238. package/src/sdlc/transitions/requirement.js +31 -0
  239. package/src/sdlc/transitions/task.js +34 -0
  240. package/src/template-trust.js +597 -0
  241. package/src/validator/expressions.js +1 -0
  242. package/src/validator/index.js +3424 -0
  243. package/src/validator/kinds.js +346 -0
  244. package/src/validator/per-kind/acceptance-criterion.js +91 -0
  245. package/src/validator/per-kind/bug.js +77 -0
  246. package/src/validator/per-kind/component.js +274 -0
  247. package/src/validator/per-kind/domain.js +205 -0
  248. package/src/validator/per-kind/pitch.js +101 -0
  249. package/src/validator/per-kind/requirement.js +75 -0
  250. package/src/validator/per-kind/task.js +96 -0
  251. package/src/validator/registry.js +1 -0
  252. package/src/validator/utils.js +12 -0
  253. package/src/validator.js +1 -0
  254. package/src/workflows.js +7597 -0
  255. package/src/workspace-docs.js +265 -0
  256. package/template-helpers/react.js +5 -0
  257. package/template-helpers/sveltekit.js +5 -0
@@ -0,0 +1,239 @@
1
+ // @ts-check
2
+
3
+ import { getProjection } from "../shared.js";
4
+
5
+ function slugify(value) {
6
+ return String(value || "page")
7
+ .toLowerCase()
8
+ .replace(/[^a-z0-9]+/g, "-")
9
+ .replace(/^-+|-+$/g, "") || "page";
10
+ }
11
+
12
+ function titleForScreen(graph, screenId) {
13
+ for (const projection of graph.byKind.projection || []) {
14
+ const screen = (projection.uiScreens || []).find((entry) => entry.id === screenId);
15
+ if (screen?.title) {
16
+ return screen.title;
17
+ }
18
+ }
19
+ return screenId
20
+ .split(/[_\-\s]+/)
21
+ .filter(Boolean)
22
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
23
+ .join(" ");
24
+ }
25
+
26
+ function routeFileName(routePath) {
27
+ if (!routePath || routePath === "/") {
28
+ return "index.html";
29
+ }
30
+ return `${slugify(routePath)}.html`;
31
+ }
32
+
33
+ function renderHtml({ title, nav, body }) {
34
+ return `<!doctype html>
35
+ <html lang="en">
36
+ <head>
37
+ <meta charset="utf-8" />
38
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
39
+ <title>${title}</title>
40
+ <link rel="stylesheet" href="./styles.css" />
41
+ </head>
42
+ <body>
43
+ <header class="app-header">
44
+ <a class="brand" href="./index.html">Topogram Hello</a>
45
+ <nav>
46
+ ${nav.map((item) => ` <a href="./${item.file}">${item.title}</a>`).join("\n")}
47
+ </nav>
48
+ </header>
49
+ <main>
50
+ ${body}
51
+ </main>
52
+ <script src="./app.js" type="module"></script>
53
+ </body>
54
+ </html>
55
+ `;
56
+ }
57
+
58
+ function renderStyles() {
59
+ return `:root {
60
+ color: #182026;
61
+ background: #f6f8fb;
62
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
63
+ }
64
+
65
+ body {
66
+ margin: 0;
67
+ }
68
+
69
+ .app-header {
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: space-between;
73
+ gap: 1rem;
74
+ padding: 1rem 1.25rem;
75
+ border-bottom: 1px solid #d8e0ea;
76
+ background: #ffffff;
77
+ }
78
+
79
+ .brand {
80
+ color: #182026;
81
+ font-weight: 700;
82
+ text-decoration: none;
83
+ }
84
+
85
+ nav {
86
+ display: flex;
87
+ flex-wrap: wrap;
88
+ gap: 0.85rem;
89
+ }
90
+
91
+ nav a {
92
+ color: #0f5cc0;
93
+ text-decoration: none;
94
+ }
95
+
96
+ main {
97
+ display: grid;
98
+ gap: 1rem;
99
+ max-width: 56rem;
100
+ margin: 0 auto;
101
+ padding: 2rem 1.25rem 4rem;
102
+ }
103
+
104
+ .panel {
105
+ border: 1px solid #d8e0ea;
106
+ border-radius: 8px;
107
+ background: #ffffff;
108
+ padding: 1.25rem;
109
+ }
110
+
111
+ .muted {
112
+ color: #607284;
113
+ }
114
+ `;
115
+ }
116
+
117
+ function renderBrowserScript() {
118
+ return `const stamp = document.querySelector("[data-generated-at]");
119
+ if (stamp) {
120
+ stamp.textContent = new Date().toLocaleString();
121
+ }
122
+ `;
123
+ }
124
+
125
+ function renderBuildScript() {
126
+ return `import fs from "node:fs";
127
+ import path from "node:path";
128
+
129
+ const root = path.resolve(new URL("..", import.meta.url).pathname);
130
+ const dist = path.join(root, "dist");
131
+ fs.rmSync(dist, { recursive: true, force: true });
132
+ fs.mkdirSync(dist, { recursive: true });
133
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
134
+ if (entry.isFile() && [".html", ".css", ".js"].includes(path.extname(entry.name))) {
135
+ fs.copyFileSync(path.join(root, entry.name), path.join(dist, entry.name));
136
+ }
137
+ }
138
+ console.log("Built vanilla web app to dist/.");
139
+ `;
140
+ }
141
+
142
+ function renderCheckScript() {
143
+ return `import fs from "node:fs";
144
+ import path from "node:path";
145
+
146
+ const root = path.resolve(new URL("..", import.meta.url).pathname);
147
+ const htmlFiles = fs.readdirSync(root).filter((entry) => entry.endsWith(".html"));
148
+ if (htmlFiles.length < 1) {
149
+ throw new Error("Expected at least one HTML page.");
150
+ }
151
+ for (const file of htmlFiles) {
152
+ const html = fs.readFileSync(path.join(root, file), "utf8");
153
+ if (!html.includes("<main>") || !html.includes("./styles.css") || !html.includes("./app.js")) {
154
+ throw new Error(\`\${file} is missing the expected page shell.\`);
155
+ }
156
+ }
157
+ console.log(\`Checked \${htmlFiles.length} vanilla page(s).\`);
158
+ `;
159
+ }
160
+
161
+ function renderDevScript() {
162
+ return `import http from "node:http";
163
+ import fs from "node:fs";
164
+ import path from "node:path";
165
+
166
+ const root = path.resolve(new URL("..", import.meta.url).pathname);
167
+ const port = Number(process.env.PORT || process.env.WEB_PORT || 5173);
168
+ const types = new Map([
169
+ [".html", "text/html; charset=utf-8"],
170
+ [".css", "text/css; charset=utf-8"],
171
+ [".js", "text/javascript; charset=utf-8"]
172
+ ]);
173
+
174
+ http.createServer((req, res) => {
175
+ const url = new URL(req.url || "/", \`http://localhost:\${port}\`);
176
+ const requested = url.pathname === "/" ? "/index.html" : url.pathname;
177
+ const filePath = path.normalize(path.join(root, requested));
178
+ if (!filePath.startsWith(root)) {
179
+ res.writeHead(403).end("Forbidden");
180
+ return;
181
+ }
182
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
183
+ res.writeHead(404).end("Not found");
184
+ return;
185
+ }
186
+ res.writeHead(200, { "content-type": types.get(path.extname(filePath)) || "application/octet-stream" });
187
+ fs.createReadStream(filePath).pipe(res);
188
+ }).listen(port, () => {
189
+ console.log(\`Vanilla web app listening on http://localhost:\${port}\`);
190
+ });
191
+ `;
192
+ }
193
+
194
+ export function generateVanillaWebApp(graph, options = {}) {
195
+ const projection = getProjection(graph, options.projectionId);
196
+ const routeEntries = projection.uiRoutes?.length
197
+ ? projection.uiRoutes
198
+ : [{ screenId: "home", path: "/" }];
199
+ const routes = routeEntries.map((route) => ({
200
+ screenId: route.screenId,
201
+ path: route.path,
202
+ title: titleForScreen(graph, route.screenId),
203
+ file: routeFileName(route.path)
204
+ }));
205
+ const nav = routes.map(({ title, file }) => ({ title, file }));
206
+ const files = {
207
+ "package.json": `${JSON.stringify({
208
+ name: projection.id,
209
+ private: true,
210
+ version: "0.1.0",
211
+ type: "module",
212
+ scripts: {
213
+ dev: "node ./scripts/dev.mjs",
214
+ build: "node ./scripts/build.mjs",
215
+ check: "node ./scripts/check.mjs"
216
+ }
217
+ }, null, 2)}\n`,
218
+ "styles.css": renderStyles(),
219
+ "app.js": renderBrowserScript(),
220
+ "scripts/build.mjs": renderBuildScript(),
221
+ "scripts/check.mjs": renderCheckScript(),
222
+ "scripts/dev.mjs": renderDevScript()
223
+ };
224
+
225
+ routes.forEach((route, index) => {
226
+ files[route.file] = renderHtml({
227
+ title: route.title,
228
+ nav,
229
+ body: ` <section class="panel">
230
+ <p class="muted">Page ${index + 1} of ${routes.length}</p>
231
+ <h1>${route.title}</h1>
232
+ <p>This page was generated from the <code>${projection.id}</code> Topogram web projection.</p>
233
+ <p class="muted">Generated timestamp: <span data-generated-at>pending</span></p>
234
+ </section>`
235
+ });
236
+ });
237
+
238
+ return files;
239
+ }
@@ -0,0 +1,84 @@
1
+ import { refList } from "./shared.js";
2
+
3
+ function normalizeScenarioLabel(scenario) {
4
+ return String(scenario || "")
5
+ .replace(/^verify_/, "")
6
+ .replaceAll("_", " ")
7
+ .trim();
8
+ }
9
+
10
+ function normalizeScenario(scenario, order) {
11
+ const id = typeof scenario === "string" ? scenario : scenario?.value || `scenario_${order + 1}`;
12
+ return {
13
+ order,
14
+ id,
15
+ label: normalizeScenarioLabel(id)
16
+ };
17
+ }
18
+
19
+ function normalizeVerification(verification) {
20
+ return {
21
+ id: verification.id,
22
+ name: verification.name || verification.id,
23
+ description: verification.description || "",
24
+ method: verification.method,
25
+ status: verification.status,
26
+ validates: (verification.validates || []).map((item) => ({
27
+ id: item.id,
28
+ kind: item.kind
29
+ })),
30
+ scenarios: (verification.scenarios || []).map((scenario, index) => normalizeScenario(scenario, index))
31
+ };
32
+ }
33
+
34
+ export function generateVerificationPlan(graph) {
35
+ const verifications = (graph.byKind.verification || [])
36
+ .map(normalizeVerification)
37
+ .sort((left, right) => left.id.localeCompare(right.id));
38
+
39
+ return {
40
+ type: "verification_plan_bundle",
41
+ name: "Topogram Verification Plan",
42
+ root: graph.root,
43
+ summary: {
44
+ verificationCount: verifications.length,
45
+ methods: [...new Set(verifications.map((entry) => entry.method))].sort(),
46
+ scenarioCount: verifications.reduce((total, entry) => total + entry.scenarios.length, 0)
47
+ },
48
+ verifications
49
+ };
50
+ }
51
+
52
+ export function generateVerificationChecklist(graph) {
53
+ const verifications = (graph.byKind.verification || []).sort((left, right) => left.id.localeCompare(right.id));
54
+ const lines = [];
55
+ lines.push("# Verification Checklist");
56
+ lines.push("");
57
+ lines.push(`Generated from \`${graph.root}\``);
58
+ lines.push("");
59
+
60
+ if (verifications.length === 0) {
61
+ lines.push("No canonical verification entries are defined.");
62
+ lines.push("");
63
+ return `${lines.join("\n")}`;
64
+ }
65
+
66
+ for (const verification of verifications) {
67
+ lines.push(`## \`${verification.id}\` - ${verification.name || verification.id}`);
68
+ lines.push("");
69
+ if (verification.description) {
70
+ lines.push(verification.description);
71
+ lines.push("");
72
+ }
73
+ lines.push(`Method: \`${verification.method}\``);
74
+ lines.push(`Status: \`${verification.status}\``);
75
+ lines.push(`Validates: ${refList(verification.validates)}`);
76
+ lines.push("");
77
+ for (const scenario of verification.scenarios || []) {
78
+ lines.push(`- [ ] ${normalizeScenarioLabel(scenario)}`);
79
+ }
80
+ lines.push("");
81
+ }
82
+
83
+ return `${lines.join("\n").trimEnd()}\n`;
84
+ }
@@ -0,0 +1 @@
1
+ export { buildOutputFiles, generateWorkspace } from "./generator/index.js";
@@ -0,0 +1,52 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import { readJsonIfExists, readTextIfExists } from "./shared.js";
5
+
6
+ export function findNearestGitRoot(startDir) {
7
+ let currentDir = path.resolve(startDir);
8
+ while (true) {
9
+ const gitPath = path.join(currentDir, ".git");
10
+ if (fs.existsSync(gitPath)) {
11
+ return currentDir;
12
+ }
13
+ const parentDir = path.dirname(currentDir);
14
+ if (parentDir === currentDir) {
15
+ return path.resolve(startDir);
16
+ }
17
+ currentDir = parentDir;
18
+ }
19
+ }
20
+
21
+ export function normalizeWorkspacePaths(inputPath) {
22
+ const absolute = path.resolve(inputPath);
23
+ const inputExists = fs.existsSync(absolute);
24
+ const topogramChild = path.join(absolute, "topogram");
25
+ const hasTopogramChild = fs.existsSync(topogramChild) && fs.statSync(topogramChild).isDirectory();
26
+ const isTopogramDir = path.basename(absolute) === "topogram" && inputExists;
27
+ const topogramRoot = isTopogramDir ? absolute : hasTopogramChild ? topogramChild : path.join(absolute, "topogram");
28
+ const workspaceRoot = isTopogramDir ? path.dirname(topogramRoot) : absolute;
29
+ const repoRoot = findNearestGitRoot(workspaceRoot);
30
+ return {
31
+ inputRoot: absolute,
32
+ topogramRoot,
33
+ workspaceRoot,
34
+ exampleRoot: workspaceRoot,
35
+ repoRoot,
36
+ bootstrappedTopogramRoot: !fs.existsSync(topogramRoot)
37
+ };
38
+ }
39
+
40
+ export function createImportContext(inputPath, options = {}) {
41
+ const paths = normalizeWorkspacePaths(inputPath);
42
+ return {
43
+ paths,
44
+ options,
45
+ helpers: {
46
+ fs,
47
+ path,
48
+ readTextIfExists,
49
+ readJsonIfExists
50
+ }
51
+ };
52
+ }
@@ -0,0 +1,23 @@
1
+ export const IMPORT_TRACKS = new Set(["db", "api", "ui", "workflows", "verification"]);
2
+
3
+ /**
4
+ * @typedef {{score:number, reasons:string[]}} DetectionResult
5
+ * @typedef {{findings:any[], candidates:any}} ExtractResult
6
+ * @typedef {{
7
+ * id:string,
8
+ * track:"db"|"api"|"ui"|"workflows"|"verification",
9
+ * detect:(context:any)=>DetectionResult,
10
+ * extract:(context:any)=>ExtractResult
11
+ * }} ImportExtractor
12
+ * @typedef {{
13
+ * id:string,
14
+ * track:"db"|"api"|"ui"|"workflows"|"verification"|"docs",
15
+ * applies:(context:any, candidates:any)=>boolean|number,
16
+ * enrich:(context:any, candidates:any)=>any
17
+ * }} ImportEnricher
18
+ * @typedef {{
19
+ * paths:any,
20
+ * helpers:any,
21
+ * options:any
22
+ * }} ImportContext
23
+ */
@@ -0,0 +1,81 @@
1
+ import { prismaExtractor } from "../extractors/db/prisma.js";
2
+ import { djangoModelsExtractor } from "../extractors/db/django-models.js";
3
+ import { efCoreExtractor } from "../extractors/db/ef-core.js";
4
+ import { roomExtractor } from "../extractors/db/room.js";
5
+ import { swiftDataExtractor } from "../extractors/db/swiftdata.js";
6
+ import { dotnetModelsExtractor } from "../extractors/db/dotnet-models.js";
7
+ import { flutterEntitiesExtractor } from "../extractors/db/flutter-entities.js";
8
+ import { reactNativeEntitiesExtractor } from "../extractors/db/react-native-entities.js";
9
+ import { railsSchemaExtractor } from "../extractors/db/rails-schema.js";
10
+ import { liquibaseExtractor } from "../extractors/db/liquibase.js";
11
+ import { myBatisXmlExtractor } from "../extractors/db/mybatis-xml.js";
12
+ import { jpaExtractor } from "../extractors/db/jpa.js";
13
+ import { drizzleExtractor } from "../extractors/db/drizzle.js";
14
+ import { sqlExtractor } from "../extractors/db/sql.js";
15
+ import { snapshotExtractor } from "../extractors/db/snapshot.js";
16
+ import { openApiExtractor } from "../extractors/api/openapi.js";
17
+ import { openApiCodeExtractor } from "../extractors/api/openapi-code.js";
18
+ import { graphQlSdlExtractor } from "../extractors/api/graphql-sdl.js";
19
+ import { graphQlCodeFirstExtractor } from "../extractors/api/graphql-code-first.js";
20
+ import { trpcExtractor } from "../extractors/api/trpc.js";
21
+ import { aspNetCoreExtractor } from "../extractors/api/aspnet-core.js";
22
+ import { retrofitExtractor } from "../extractors/api/retrofit.js";
23
+ import { swiftWebApiExtractor } from "../extractors/api/swift-webapi.js";
24
+ import { flutterDioExtractor } from "../extractors/api/flutter-dio.js";
25
+ import { reactNativeRepositoryExtractor } from "../extractors/api/react-native-repository.js";
26
+ import { fastifyExtractor } from "../extractors/api/fastify.js";
27
+ import { expressExtractor } from "../extractors/api/express.js";
28
+ import { djangoRoutesExtractor } from "../extractors/api/django-routes.js";
29
+ import { railsRoutesExtractor } from "../extractors/api/rails-routes.js";
30
+ import { springWebExtractor } from "../extractors/api/spring-web.js";
31
+ import { micronautExtractor } from "../extractors/api/micronaut.js";
32
+ import { jaxRsExtractor } from "../extractors/api/jaxrs.js";
33
+ import { nextRouteExtractor } from "../extractors/api/next-route.js";
34
+ import { genericRouteFallbackExtractor } from "../extractors/api/generic-route-fallback.js";
35
+ import { nextServerActionExtractor } from "../extractors/api/next-server-action.js";
36
+ import { nextAuthExtractor } from "../extractors/api/nextauth.js";
37
+ import { nextAppRouterUiExtractor } from "../extractors/ui/next-app-router.js";
38
+ import { nextPagesRouterUiExtractor } from "../extractors/ui/next-pages-router.js";
39
+ import { androidComposeUiExtractor } from "../extractors/ui/android-compose.js";
40
+ import { blazorUiExtractor } from "../extractors/ui/blazor.js";
41
+ import { razorPagesUiExtractor } from "../extractors/ui/razor-pages.js";
42
+ import { swiftUiExtractor } from "../extractors/ui/swiftui.js";
43
+ import { uiKitExtractor } from "../extractors/ui/uikit.js";
44
+ import { mauiXamlUiExtractor } from "../extractors/ui/maui-xaml.js";
45
+ import { flutterScreensUiExtractor } from "../extractors/ui/flutter-screens.js";
46
+ import { reactNativeScreensExtractor } from "../extractors/ui/react-native-screens.js";
47
+ import { reactRouterUiExtractor } from "../extractors/ui/react-router.js";
48
+ import { svelteKitUiExtractor } from "../extractors/ui/sveltekit.js";
49
+ import { backendOnlyUiExtractor } from "../extractors/ui/backend-only.js";
50
+ import { genericWorkflowExtractor } from "../extractors/workflows/generic.js";
51
+ import { genericVerificationExtractor } from "../extractors/verification/generic.js";
52
+ import { authSessionEnricher } from "../enrichers/auth-session.js";
53
+ import { djangoRestEnricher } from "../enrichers/django-rest.js";
54
+ import { railsModelEnricher } from "../enrichers/rails-models.js";
55
+ import { railsControllerEnricher } from "../enrichers/rails-controllers.js";
56
+ import { workflowTargetStateEnricher } from "../enrichers/workflow-target-state.js";
57
+ import { docLinkingEnricher } from "../enrichers/doc-linking.js";
58
+
59
+ export const extractorRegistry = {
60
+ db: [prismaExtractor, djangoModelsExtractor, efCoreExtractor, roomExtractor, swiftDataExtractor, dotnetModelsExtractor, flutterEntitiesExtractor, reactNativeEntitiesExtractor, railsSchemaExtractor, liquibaseExtractor, myBatisXmlExtractor, jpaExtractor, drizzleExtractor, sqlExtractor, snapshotExtractor],
61
+ api: [openApiExtractor, openApiCodeExtractor, graphQlSdlExtractor, graphQlCodeFirstExtractor, trpcExtractor, aspNetCoreExtractor, retrofitExtractor, swiftWebApiExtractor, flutterDioExtractor, reactNativeRepositoryExtractor, fastifyExtractor, expressExtractor, djangoRoutesExtractor, railsRoutesExtractor, micronautExtractor, jaxRsExtractor, springWebExtractor, nextRouteExtractor, genericRouteFallbackExtractor, nextServerActionExtractor, nextAuthExtractor],
62
+ ui: [nextAppRouterUiExtractor, nextPagesRouterUiExtractor, androidComposeUiExtractor, blazorUiExtractor, razorPagesUiExtractor, swiftUiExtractor, uiKitExtractor, mauiXamlUiExtractor, flutterScreensUiExtractor, reactNativeScreensExtractor, reactRouterUiExtractor, svelteKitUiExtractor, backendOnlyUiExtractor],
63
+ workflows: [genericWorkflowExtractor],
64
+ verification: [genericVerificationExtractor]
65
+ };
66
+
67
+ export const enricherRegistry = {
68
+ db: [railsModelEnricher],
69
+ api: [djangoRestEnricher, railsControllerEnricher, authSessionEnricher],
70
+ ui: [],
71
+ workflows: [workflowTargetStateEnricher, docLinkingEnricher],
72
+ verification: []
73
+ };
74
+
75
+ export function getExtractorsForTrack(track) {
76
+ return extractorRegistry[track] || [];
77
+ }
78
+
79
+ export function getEnrichersForTrack(track) {
80
+ return enricherRegistry[track] || [];
81
+ }