@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,515 @@
1
+ // @ts-check
2
+
3
+ import { generateApiContractGraph } from "../api.js";
4
+ import { generateWithComponentGenerator } from "../adapters.js";
5
+ import { getProjection } from "../surfaces/databases/shared.js";
6
+ import { getDefaultBackendDbProjection } from "../../realization/backend/index.js";
7
+ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../project-config.js";
8
+
9
+ /**
10
+ * @typedef {Object} ResolvedGraph
11
+ * @property {Record<string, Array<Record<string, any>>>} byKind
12
+ * @property {string} [root]
13
+ */
14
+
15
+ /**
16
+ * @typedef {Record<string, any>} RuntimeStatement
17
+ */
18
+
19
+ /**
20
+ * @typedef {Object} RuntimeComponent
21
+ * @property {string} id
22
+ * @property {"api"|"web"|"database"|"native"} type
23
+ * @property {RuntimeStatement} projection
24
+ * @property {import("../../project-config.js").GeneratorBinding} generator
25
+ * @property {number|null} [port]
26
+ * @property {string} [api]
27
+ * @property {string} [database]
28
+ * @property {Record<string, string>} [env]
29
+ * @property {RuntimeComponent|null} [apiComponent]
30
+ * @property {RuntimeComponent|null} [databaseComponent]
31
+ */
32
+
33
+ /**
34
+ * @typedef {import("../../project-config.js").RuntimeTopologyComponent} RuntimeTopologyComponent
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} RuntimeTopology
39
+ * @property {import("../../project-config.js").ProjectConfig} config
40
+ * @property {RuntimeComponent[]} components
41
+ * @property {RuntimeComponent[]} apiComponents
42
+ * @property {RuntimeComponent[]} webComponents
43
+ * @property {RuntimeComponent[]} dbComponents
44
+ * @property {RuntimeComponent|null} primaryApi
45
+ * @property {RuntimeComponent|null} primaryWeb
46
+ * @property {RuntimeComponent|null} primaryDb
47
+ * @property {(component: RuntimeComponent) => string} serviceDir
48
+ * @property {(component: RuntimeComponent) => string} webDir
49
+ * @property {(component: RuntimeComponent) => string} dbDir
50
+ */
51
+
52
+ /**
53
+ * @typedef {Object} VerificationSelectionOptions
54
+ * @property {boolean} [keepLookupChecks]
55
+ * @property {boolean} [keepWebChecks]
56
+ */
57
+
58
+ /**
59
+ * @typedef {Object} RuntimeGenerationOptions
60
+ * @property {import("../../project-config.js").ProjectConfig} [projectConfig]
61
+ * @property {Record<string, any>|null} [implementation]
62
+ * @property {string} [projectionId]
63
+ * @property {string} [dbProjectionId]
64
+ * @property {string} [configDir]
65
+ * @property {string} [projectRoot]
66
+ * @property {RuntimeComponent} [component]
67
+ */
68
+
69
+ /**
70
+ * @typedef {Object} EnvVarOptions
71
+ * @property {boolean} [primary]
72
+ */
73
+
74
+ /**
75
+ * @param {any} item
76
+ * @returns {string|null}
77
+ */
78
+ function verificationScenarioValue(item) {
79
+ if (!item) {
80
+ return null;
81
+ }
82
+ if (typeof item === "string") {
83
+ return item;
84
+ }
85
+ return item.value || null;
86
+ }
87
+
88
+ /**
89
+ * @param {any} scenario
90
+ * @returns {string}
91
+ */
92
+ function scenarioLabel(scenario) {
93
+ return String(scenario || "")
94
+ .replace(/^verify_/, "")
95
+ .replaceAll("_", " ")
96
+ .trim();
97
+ }
98
+
99
+ /**
100
+ * @param {ResolvedGraph} graph
101
+ * @param {string[]} [methods]
102
+ * @returns {RuntimeStatement[]}
103
+ */
104
+ export function getVerificationEntries(graph, methods = []) {
105
+ const methodSet = new Set(methods);
106
+ return (graph.byKind.verification || [])
107
+ .filter((verification) => methodSet.size === 0 || methodSet.has(verification.method))
108
+ .sort((left, right) => left.id.localeCompare(right.id));
109
+ }
110
+
111
+ /**
112
+ * @param {ResolvedGraph} graph
113
+ * @param {string[]} [methods]
114
+ * @returns {Record<string, any>|null}
115
+ */
116
+ export function buildVerificationSummary(graph, methods = []) {
117
+ const verifications = getVerificationEntries(graph, methods);
118
+ if (verifications.length === 0) {
119
+ return null;
120
+ }
121
+
122
+ const scenarioMap = new Map();
123
+ for (const verification of verifications) {
124
+ const rawScenarios = Array.isArray(verification.scenarios)
125
+ ? verification.scenarios
126
+ : Array.isArray(verification.plan?.scenarios)
127
+ ? /** @type {Array<Record<string, any>>} */ (verification.plan.scenarios).map((entry) => entry?.target?.id || null)
128
+ : [];
129
+ for (const raw of rawScenarios) {
130
+ const id = verificationScenarioValue(raw);
131
+ if (!id || scenarioMap.has(id)) {
132
+ continue;
133
+ }
134
+ scenarioMap.set(id, {
135
+ id,
136
+ label: scenarioLabel(id)
137
+ });
138
+ }
139
+ }
140
+
141
+ return {
142
+ methods: [...new Set(verifications.map((verification) => verification.method))],
143
+ sources: verifications.map((verification) => ({
144
+ id: verification.id,
145
+ name: verification.name || verification.id,
146
+ method: verification.method,
147
+ validates: /** @type {Array<Record<string, any>>} */ (verification.validates || []).map((item) => item.id)
148
+ })),
149
+ scenarios: [...scenarioMap.values()]
150
+ };
151
+ }
152
+
153
+ /**
154
+ * @param {ResolvedGraph} graph
155
+ * @param {string[]} [methods]
156
+ * @returns {Set<string>}
157
+ */
158
+ export function getVerifiedCapabilityIds(graph, methods = []) {
159
+ const verifications = getVerificationEntries(graph, methods);
160
+ const capabilityIds = new Set();
161
+ for (const verification of verifications) {
162
+ for (const target of verification.validates || []) {
163
+ if (target.kind === "capability") {
164
+ capabilityIds.add(target.id);
165
+ }
166
+ }
167
+ }
168
+ return capabilityIds;
169
+ }
170
+
171
+ /**
172
+ * @param {ResolvedGraph} graph
173
+ * @param {Array<Record<string, any>>} checks
174
+ * @param {string[]} [methods]
175
+ * @param {VerificationSelectionOptions} [options]
176
+ * @returns {{ checks: Array<Record<string, any>>, selection: Record<string, any>|null }}
177
+ */
178
+ export function selectChecksByVerification(graph, checks, methods = [], options = {}) {
179
+ const capabilityIds = getVerifiedCapabilityIds(graph, methods);
180
+ if (capabilityIds.size === 0) {
181
+ return {
182
+ checks,
183
+ selection: null
184
+ };
185
+ }
186
+
187
+ const selected = [];
188
+ const selectedCheckIds = [];
189
+ const omittedCheckIds = [];
190
+
191
+ for (const check of checks) {
192
+ const keepLookup = options.keepLookupChecks && check.kind === "lookup_contract";
193
+ const keepWeb = options.keepWebChecks && (check.kind === "web_contract" || check.type === "web_get");
194
+ const matchesCapability = check.capabilityId && capabilityIds.has(check.capabilityId);
195
+ const keep = keepLookup || keepWeb || matchesCapability;
196
+
197
+ if (keep) {
198
+ selected.push(check);
199
+ selectedCheckIds.push(check.id);
200
+ } else {
201
+ omittedCheckIds.push(check.id);
202
+ }
203
+ }
204
+
205
+ return {
206
+ checks: selected.length > 0 ? selected : checks,
207
+ selection: {
208
+ methods,
209
+ capabilityIds: [...capabilityIds].sort(),
210
+ selectedCheckIds,
211
+ omittedCheckIds
212
+ }
213
+ };
214
+ }
215
+
216
+ /**
217
+ * @param {ResolvedGraph} graph
218
+ * @returns {RuntimeStatement[]}
219
+ */
220
+ function apiProjectionCandidates(graph) {
221
+ return (graph.byKind.projection || []).filter((projection) => (projection.http || []).length > 0);
222
+ }
223
+
224
+ /**
225
+ * @param {ResolvedGraph} graph
226
+ * @returns {RuntimeStatement[]}
227
+ */
228
+ function uiWebProjectionCandidates(graph) {
229
+ return (graph.byKind.projection || []).filter(
230
+ (projection) => projection.platform === "ui_web" && (projection.uiRoutes || []).length > 0
231
+ );
232
+ }
233
+
234
+ const WEB_UI_FAMILY_PREFIX = "proj_ui_web__";
235
+ const NATIVE_UI_FAMILY_PREFIX = "proj_ui_native__";
236
+
237
+ /** Prefer canonical ids when multiple shipped web stacks exist (deterministic, not lexicographic). */
238
+ const DEFAULT_WEB_UI_STACK_ORDER = ["proj_ui_web__sveltekit", "proj_ui_web__react"];
239
+
240
+ const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["proj_ui_native__ios"];
241
+
242
+ /**
243
+ * @param {ResolvedGraph} graph
244
+ * @returns {RuntimeStatement[]}
245
+ */
246
+ function uiIosProjectionCandidates(graph) {
247
+ return (graph.byKind.projection || []).filter(
248
+ (projection) => projection.platform === "ui_ios" && (projection.uiRoutes || []).length > 0
249
+ );
250
+ }
251
+
252
+ /**
253
+ * Prefer canonical native projections (`proj_ui_native__{platform}`); otherwise first routed ui_ios projection.
254
+ *
255
+ * @param {ResolvedGraph} graph
256
+ * @returns {RuntimeStatement|undefined}
257
+ */
258
+ export function pickDefaultIosUiProjection(graph) {
259
+ const candidates = uiIosProjectionCandidates(graph);
260
+ const hierarchical = candidates.filter((projection) => projection.id.startsWith(NATIVE_UI_FAMILY_PREFIX));
261
+ if (hierarchical.length > 0) {
262
+ for (const id of DEFAULT_NATIVE_UI_PLATFORM_ORDER) {
263
+ const match = hierarchical.find((projection) => projection.id === id);
264
+ if (match) {
265
+ return match;
266
+ }
267
+ }
268
+ return hierarchical.sort((a, b) => a.id.localeCompare(b.id))[0];
269
+ }
270
+ return candidates[0];
271
+ }
272
+
273
+ /**
274
+ * Prefer canonical shipped web projections (`proj_ui_web__{stack}`); otherwise first routed ui_web projection.
275
+ *
276
+ * @param {ResolvedGraph} graph
277
+ * @returns {RuntimeStatement|undefined}
278
+ */
279
+ export function pickDefaultUiWebProjection(graph) {
280
+ const candidates = uiWebProjectionCandidates(graph);
281
+ const hierarchical = candidates.filter((projection) => projection.id.startsWith(WEB_UI_FAMILY_PREFIX));
282
+ if (hierarchical.length > 0) {
283
+ for (const id of DEFAULT_WEB_UI_STACK_ORDER) {
284
+ const match = hierarchical.find((projection) => projection.id === id);
285
+ if (match) {
286
+ return match;
287
+ }
288
+ }
289
+ return hierarchical.sort((a, b) => a.id.localeCompare(b.id))[0];
290
+ }
291
+ const legacySvelteKitProjection = candidates.find((projection) => projection.id === "proj_ui_web");
292
+ if (legacySvelteKitProjection) {
293
+ return legacySvelteKitProjection;
294
+ }
295
+ return candidates[0];
296
+ }
297
+
298
+ /**
299
+ * @param {ResolvedGraph} graph
300
+ * @param {RuntimeGenerationOptions} [options]
301
+ * @returns {{ apiProjection: RuntimeStatement|null, uiProjection: RuntimeStatement|null, dbProjection: RuntimeStatement|null }}
302
+ */
303
+ export function getDefaultEnvironmentProjections(graph, options = {}) {
304
+ const topology = resolveRuntimeTopology(graph, options);
305
+ const dbCandidates = graph.byKind.projection?.filter((projection) => ["db_postgres", "db_sqlite"].includes(projection.platform)) || [];
306
+ const apiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryApi?.projection ||
307
+ (options.projectionId ? getProjection(graph, options.projectionId) : null) ||
308
+ apiProjectionCandidates(graph).find((projection) => projection.id === "proj_api") ||
309
+ apiProjectionCandidates(graph)[0] ||
310
+ null);
311
+ const uiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryWeb?.projection || pickDefaultUiWebProjection(graph) || null);
312
+ let dbProjection = /** @type {RuntimeStatement|null} */ (topology.primaryDb?.projection || null);
313
+ if (!dbProjection && dbCandidates.length > 0) {
314
+ try {
315
+ dbProjection = getDefaultBackendDbProjection(graph, options);
316
+ } catch {
317
+ dbProjection = /** @type {RuntimeStatement|null} */ (dbCandidates[0] || null);
318
+ }
319
+ }
320
+
321
+ return { apiProjection, uiProjection, dbProjection };
322
+ }
323
+
324
+ /**
325
+ * @param {ResolvedGraph} graph
326
+ * @param {string} projectionId
327
+ * @param {RuntimeGenerationOptions} [options]
328
+ * @returns {any}
329
+ */
330
+ export function generateServerBundle(graph, projectionId, options = {}) {
331
+ const topology = resolveRuntimeTopology(graph, options);
332
+ const component = options.component || topology.apiComponents.find((entry) => entry.projection.id === projectionId);
333
+ if (!component) {
334
+ throw new Error(`No api topology component found for projection '${projectionId}'`);
335
+ }
336
+ return generateWithComponentGenerator({
337
+ graph,
338
+ projection: component.projection,
339
+ component,
340
+ topology,
341
+ implementation: options.implementation || null,
342
+ options: { ...options, projectionId }
343
+ }).files;
344
+ }
345
+
346
+ /**
347
+ * @param {ResolvedGraph} graph
348
+ * @param {string} projectionId
349
+ * @param {RuntimeGenerationOptions} [options]
350
+ * @returns {any}
351
+ */
352
+ export function generateWebBundle(graph, projectionId, options = {}) {
353
+ const topology = resolveRuntimeTopology(graph, options);
354
+ const component = options.component || topology.webComponents.find((entry) => entry.projection.id === projectionId);
355
+ if (!component) {
356
+ throw new Error(`No web topology component found for projection '${projectionId}'`);
357
+ }
358
+ return generateWithComponentGenerator({
359
+ graph,
360
+ projection: component.projection,
361
+ component,
362
+ topology,
363
+ implementation: options.implementation || null,
364
+ options: { ...options, projectionId }
365
+ }).files;
366
+ }
367
+
368
+ /**
369
+ * @param {ResolvedGraph} graph
370
+ * @param {string} projectionId
371
+ * @param {RuntimeGenerationOptions} [options]
372
+ * @returns {any}
373
+ */
374
+ export function generateDbBundle(graph, projectionId, options = {}) {
375
+ const topology = resolveRuntimeTopology(graph, options);
376
+ const component = options.component || topology.dbComponents.find((entry) => entry.projection.id === projectionId);
377
+ if (!component) {
378
+ throw new Error(`No database topology component found for projection '${projectionId}'`);
379
+ }
380
+ return generateWithComponentGenerator({
381
+ graph,
382
+ projection: getProjection(graph, projectionId),
383
+ component,
384
+ topology,
385
+ implementation: options.implementation || null,
386
+ options: { ...options, projectionId }
387
+ }).files;
388
+ }
389
+
390
+ /**
391
+ * @param {ResolvedGraph} graph
392
+ * @returns {any}
393
+ */
394
+ export function generateRuntimeApiContracts(graph) {
395
+ return generateApiContractGraph(graph, {});
396
+ }
397
+
398
+ /**
399
+ * @param {string} componentId
400
+ * @param {EnvVarOptions} [options]
401
+ * @returns {string}
402
+ */
403
+ function envVarPrefix(componentId, options = {}) {
404
+ return options.primary || componentId === "db"
405
+ ? ""
406
+ : `${componentId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_`;
407
+ }
408
+
409
+ /**
410
+ * @param {RuntimeComponent} component
411
+ * @param {EnvVarOptions} [options]
412
+ * @returns {{ databaseUrl: string, databaseAdminUrl: string, dbPort: string, postgresDb: string }}
413
+ */
414
+ export function dbEnvVarsForComponent(component, options = {}) {
415
+ const prefix = envVarPrefix(component.id, options);
416
+ return {
417
+ databaseUrl: component.env?.databaseUrl || `${prefix}DATABASE_URL`,
418
+ databaseAdminUrl: component.env?.databaseAdminUrl || `${prefix}DATABASE_ADMIN_URL`,
419
+ dbPort: component.env?.dbPort || `${prefix}DB_PORT`,
420
+ postgresDb: component.env?.postgresDb || `${prefix}POSTGRES_DB`
421
+ };
422
+ }
423
+
424
+ /**
425
+ * @param {ResolvedGraph} graph
426
+ * @param {import("../../project-config.js").ProjectConfig} config
427
+ * @returns {RuntimeComponent[]}
428
+ */
429
+ function decorateComponents(graph, config) {
430
+ const byProjectionId = new Map((graph.byKind.projection || []).map((projection) => [projection.id, projection]));
431
+ const rawComponents = config.topology?.components || [];
432
+ /** @type {RuntimeComponent[]} */
433
+ const components = rawComponents.map((component) => ({
434
+ ...component,
435
+ projection: byProjectionId.get(component.projection) || {}
436
+ }));
437
+ const byId = new Map(components.map((component) => [component.id, component]));
438
+ for (const component of components) {
439
+ if (component.type === "api" && component.database) {
440
+ component.databaseComponent = byId.get(component.database) || null;
441
+ }
442
+ if (component.type === "web" && component.api) {
443
+ component.apiComponent = byId.get(component.api) || null;
444
+ }
445
+ }
446
+ return components;
447
+ }
448
+
449
+ /**
450
+ * @param {ResolvedGraph} graph
451
+ * @param {RuntimeGenerationOptions} [options]
452
+ * @returns {RuntimeTopology}
453
+ */
454
+ export function resolveRuntimeTopology(graph, options = {}) {
455
+ const config = options.projectConfig || defaultProjectConfigForGraph(graph, options.implementation || null);
456
+ const validation = validateProjectConfig(config, graph, {
457
+ configDir: options.configDir || options.projectRoot || null,
458
+ rootDir: options.projectRoot || options.configDir || null
459
+ });
460
+ if (!validation.ok) {
461
+ throw new Error(validation.errors.map((error) => error.message).join("\n"));
462
+ }
463
+ const components = decorateComponents(graph, config);
464
+ const apiComponents = components.filter((component) => component.type === "api");
465
+ const webComponents = components.filter((component) => component.type === "web");
466
+ const dbComponents = components.filter((component) => component.type === "database");
467
+ const primaryApi = apiComponents[0] || null;
468
+ const primaryWeb = webComponents[0] || null;
469
+ const primaryDb = primaryApi?.databaseComponent || dbComponents[0] || null;
470
+
471
+ return {
472
+ config,
473
+ components,
474
+ apiComponents,
475
+ webComponents,
476
+ dbComponents,
477
+ primaryApi,
478
+ primaryWeb,
479
+ primaryDb,
480
+ serviceDir(component) {
481
+ return `services/${component.id}`;
482
+ },
483
+ webDir(component) {
484
+ return `web/${component.id}`;
485
+ },
486
+ dbDir(component) {
487
+ return `db/${component.id}`;
488
+ }
489
+ };
490
+ }
491
+
492
+ /**
493
+ * @param {Record<string, any>|null|undefined} runtimeReference
494
+ * @param {RuntimeTopology|null} [topology]
495
+ * @returns {{ server: number, web: number }}
496
+ */
497
+ export function runtimePorts(runtimeReference, topology = null) {
498
+ return {
499
+ server: topology?.primaryApi?.port || runtimeReference?.ports?.server || 3000,
500
+ web: topology?.primaryWeb?.port || runtimeReference?.ports?.web || 5173
501
+ };
502
+ }
503
+
504
+ /**
505
+ * @param {Record<string, any>|null|undefined} runtimeReference
506
+ * @param {RuntimeTopology|null} [topology]
507
+ * @returns {{ api: string, web: string }}
508
+ */
509
+ export function runtimeUrls(runtimeReference, topology = null) {
510
+ const ports = runtimePorts(runtimeReference, topology);
511
+ return {
512
+ api: `http://localhost:${ports.server}`,
513
+ web: `http://localhost:${ports.web}`
514
+ };
515
+ }