@topogram/cli 0.3.64 → 0.3.65

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 (245) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/agent-ops/query-builders/auth.js +375 -0
  5. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  6. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  7. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  8. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  9. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  10. package/src/agent-ops/query-builders/change-risk.js +25 -0
  11. package/src/agent-ops/query-builders/common.js +149 -0
  12. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  13. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  14. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  15. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  16. package/src/agent-ops/query-builders/work-packets.js +417 -0
  17. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  18. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  19. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  20. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  21. package/src/agent-ops/query-builders.d.ts +26 -26
  22. package/src/agent-ops/query-builders.js +42 -5021
  23. package/src/catalog/constants.js +10 -0
  24. package/src/catalog/copy.js +60 -0
  25. package/src/catalog/diagnostics.js +15 -0
  26. package/src/catalog/entries.js +42 -0
  27. package/src/catalog/files.js +67 -0
  28. package/src/catalog/provenance.js +122 -0
  29. package/src/catalog/source.js +150 -0
  30. package/src/catalog/validation.js +252 -0
  31. package/src/catalog.d.ts +2 -0
  32. package/src/catalog.js +18 -746
  33. package/src/cli/commands/catalog/check.js +31 -0
  34. package/src/cli/commands/catalog/copy.js +59 -0
  35. package/src/cli/commands/catalog/doctor.js +248 -0
  36. package/src/cli/commands/catalog/help.js +21 -0
  37. package/src/cli/commands/catalog/list.js +52 -0
  38. package/src/cli/commands/catalog/runner.js +92 -0
  39. package/src/cli/commands/catalog/shared.js +17 -0
  40. package/src/cli/commands/catalog/show.js +134 -0
  41. package/src/cli/commands/catalog.js +30 -615
  42. package/src/cli/commands/generator-policy/package-info.js +162 -0
  43. package/src/cli/commands/generator-policy/payloads.js +372 -0
  44. package/src/cli/commands/generator-policy/printers.js +159 -0
  45. package/src/cli/commands/generator-policy/runner.js +81 -0
  46. package/src/cli/commands/generator-policy/shared.js +39 -0
  47. package/src/cli/commands/generator-policy.js +15 -783
  48. package/src/cli/commands/import/adopt.js +170 -0
  49. package/src/cli/commands/import/check.js +91 -0
  50. package/src/cli/commands/import/diff.js +84 -0
  51. package/src/cli/commands/import/help.js +47 -0
  52. package/src/cli/commands/import/paths.js +277 -0
  53. package/src/cli/commands/import/plan.js +284 -0
  54. package/src/cli/commands/import/refresh.js +470 -0
  55. package/src/cli/commands/import/status-history.js +196 -0
  56. package/src/cli/commands/import/workspace.js +230 -0
  57. package/src/cli/commands/import.js +33 -1732
  58. package/src/cli/commands/package/constants.js +17 -0
  59. package/src/cli/commands/package/doctor.js +240 -0
  60. package/src/cli/commands/package/help.js +27 -0
  61. package/src/cli/commands/package/lockfile.js +135 -0
  62. package/src/cli/commands/package/npm.js +97 -0
  63. package/src/cli/commands/package/reporting.js +35 -0
  64. package/src/cli/commands/package/runner.js +33 -0
  65. package/src/cli/commands/package/shared.js +9 -0
  66. package/src/cli/commands/package/update-cli.js +252 -0
  67. package/src/cli/commands/package/versions.js +35 -0
  68. package/src/cli/commands/package.js +29 -813
  69. package/src/cli/commands/query/change-plan.js +68 -0
  70. package/src/cli/commands/query/definitions.js +202 -0
  71. package/src/cli/commands/query/import-adopt.js +121 -0
  72. package/src/cli/commands/query/runner/artifacts.js +102 -0
  73. package/src/cli/commands/query/runner/boundaries.js +211 -0
  74. package/src/cli/commands/query/runner/change.js +182 -0
  75. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  76. package/src/cli/commands/query/runner/index.js +31 -0
  77. package/src/cli/commands/query/runner/output.js +12 -0
  78. package/src/cli/commands/query/runner/workflow.js +241 -0
  79. package/src/cli/commands/query/runner.js +3 -0
  80. package/src/cli/commands/query/workflow-context.js +5 -0
  81. package/src/cli/commands/query/workspace.js +274 -0
  82. package/src/cli/commands/query.js +9 -1300
  83. package/src/cli/commands/template/baseline.js +100 -0
  84. package/src/cli/commands/template/check.js +466 -0
  85. package/src/cli/commands/template/constants.js +8 -0
  86. package/src/cli/commands/template/diagnostics.js +26 -0
  87. package/src/cli/commands/template/help.js +28 -0
  88. package/src/cli/commands/template/lifecycle.js +404 -0
  89. package/src/cli/commands/template/list-show.js +287 -0
  90. package/src/cli/commands/template/policy.js +422 -0
  91. package/src/cli/commands/template/shared.js +127 -0
  92. package/src/cli/commands/template/updates.js +352 -0
  93. package/src/cli/commands/template.js +41 -2143
  94. package/src/generator/api/contracts.js +497 -0
  95. package/src/generator/api/metadata.js +221 -0
  96. package/src/generator/api/openapi.js +559 -0
  97. package/src/generator/api/schema.js +124 -0
  98. package/src/generator/api/types.d.ts +98 -0
  99. package/src/generator/api.js +3 -1195
  100. package/src/generator/context/shared/domain-sdlc.js +282 -0
  101. package/src/generator/context/shared/maintained-boundary.js +665 -0
  102. package/src/generator/context/shared/metrics.js +85 -0
  103. package/src/generator/context/shared/primitives.js +64 -0
  104. package/src/generator/context/shared/relationships.js +453 -0
  105. package/src/generator/context/shared/summaries.js +263 -0
  106. package/src/generator/context/shared/types.d.ts +207 -0
  107. package/src/generator/context/shared.d.ts +42 -0
  108. package/src/generator/context/shared.js +80 -1390
  109. package/src/generator/context/slice/core.js +397 -0
  110. package/src/generator/context/slice/sdlc.js +417 -0
  111. package/src/generator/context/slice/ui-packets.js +183 -0
  112. package/src/generator/context/slice.js +2 -859
  113. package/src/generator/registry/index.js +507 -0
  114. package/src/generator/registry.js +18 -504
  115. package/src/generator/runtime/environment/index.js +666 -0
  116. package/src/generator/runtime/environment.js +4 -666
  117. package/src/generator/runtime/runtime-check/index.js +554 -0
  118. package/src/generator/runtime/runtime-check.js +4 -554
  119. package/src/generator/runtime/shared/index.js +572 -0
  120. package/src/generator/runtime/shared.js +19 -570
  121. package/src/generator/shared.d.ts +2 -0
  122. package/src/generator/surfaces/shared.d.ts +3 -0
  123. package/src/generator/widget-conformance/behavior-report.js +258 -0
  124. package/src/generator/widget-conformance/checks.js +371 -0
  125. package/src/generator/widget-conformance/projection-context.js +200 -0
  126. package/src/generator/widget-conformance/report.js +166 -0
  127. package/src/generator/widget-conformance/types.d.ts +121 -0
  128. package/src/generator/widget-conformance.js +3 -824
  129. package/src/import/core/context.d.ts +3 -0
  130. package/src/import/core/contracts.d.ts +1 -0
  131. package/src/import/core/registry.d.ts +4 -0
  132. package/src/import/core/runner/candidates.js +217 -0
  133. package/src/import/core/runner/options.js +22 -0
  134. package/src/import/core/runner/reports.js +50 -0
  135. package/src/import/core/runner/run.js +79 -0
  136. package/src/import/core/runner/tracks.js +150 -0
  137. package/src/import/core/runner/ui-drafts.js +337 -0
  138. package/src/import/core/runner.js +3 -698
  139. package/src/import/core/shared/api-routes.js +221 -0
  140. package/src/import/core/shared/candidates.js +97 -0
  141. package/src/import/core/shared/files.js +177 -0
  142. package/src/import/core/shared/next-app.js +389 -0
  143. package/src/import/core/shared/types.d.ts +51 -0
  144. package/src/import/core/shared/ui-routes.js +230 -0
  145. package/src/import/core/shared.js +60 -861
  146. package/src/new-project/constants.js +128 -0
  147. package/src/new-project/create.js +83 -0
  148. package/src/new-project/json.js +28 -0
  149. package/src/new-project/metadata.js +96 -0
  150. package/src/new-project/package-spec.js +161 -0
  151. package/src/new-project/project-files.js +348 -0
  152. package/src/new-project/template-policy.js +269 -0
  153. package/src/new-project/template-resolution.js +368 -0
  154. package/src/new-project/template-snapshots.js +430 -0
  155. package/src/new-project/template-updates.js +512 -0
  156. package/src/new-project/types.d.ts +83 -0
  157. package/src/new-project.js +6 -2277
  158. package/src/parser.d.ts +87 -1
  159. package/src/parser.js +118 -0
  160. package/src/policy/review-boundaries.d.ts +15 -0
  161. package/src/project-config/index.js +564 -0
  162. package/src/project-config.js +19 -561
  163. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  164. package/src/resolver/enrich/bug.js +2 -0
  165. package/src/resolver/enrich/pitch.js +2 -0
  166. package/src/resolver/enrich/requirement.js +2 -0
  167. package/src/resolver/enrich/task.js +2 -0
  168. package/src/resolver/index.js +19 -2089
  169. package/src/resolver/normalize.js +384 -1
  170. package/src/resolver/plans.js +168 -0
  171. package/src/resolver/projections-api.js +494 -0
  172. package/src/resolver/projections-db.js +133 -0
  173. package/src/resolver/projections-ui.js +317 -0
  174. package/src/resolver/shapes.js +251 -0
  175. package/src/resolver/shared.js +278 -0
  176. package/src/resolver/widgets.js +132 -0
  177. package/src/template-trust/constants.js +62 -0
  178. package/src/template-trust/content.js +258 -0
  179. package/src/template-trust/diff.js +92 -0
  180. package/src/template-trust/policy.js +61 -0
  181. package/src/template-trust/record.js +90 -0
  182. package/src/template-trust/status.js +182 -0
  183. package/src/template-trust.js +24 -687
  184. package/src/text-helpers.d.ts +1 -0
  185. package/src/topogram-types.d.ts +69 -0
  186. package/src/validator/common.js +488 -0
  187. package/src/validator/data-model.js +237 -0
  188. package/src/validator/docs.js +167 -0
  189. package/src/validator/expressions.js +146 -1
  190. package/src/validator/index.d.ts +23 -0
  191. package/src/validator/index.js +32 -3585
  192. package/src/validator/kinds.d.ts +41 -0
  193. package/src/validator/kinds.js +2 -0
  194. package/src/validator/model-helpers.js +46 -0
  195. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  196. package/src/validator/per-kind/bug.js +6 -0
  197. package/src/validator/per-kind/domain.js +15 -2
  198. package/src/validator/per-kind/pitch.js +7 -0
  199. package/src/validator/per-kind/requirement.js +5 -0
  200. package/src/validator/per-kind/task.js +7 -0
  201. package/src/validator/per-kind/widget.js +14 -0
  202. package/src/validator/projections/api-http-async.js +410 -0
  203. package/src/validator/projections/api-http-authz.js +88 -0
  204. package/src/validator/projections/api-http-core.js +205 -0
  205. package/src/validator/projections/api-http-policies.js +339 -0
  206. package/src/validator/projections/api-http-responses.js +233 -0
  207. package/src/validator/projections/api-http.js +44 -0
  208. package/src/validator/projections/db.js +353 -0
  209. package/src/validator/projections/generator-defaults.js +45 -0
  210. package/src/validator/projections/helpers.js +87 -0
  211. package/src/validator/projections/ui-helpers.js +214 -0
  212. package/src/validator/projections/ui-navigation.js +344 -0
  213. package/src/validator/projections/ui-structure.js +364 -0
  214. package/src/validator/projections/ui-widgets.js +493 -0
  215. package/src/validator/projections/ui.js +46 -0
  216. package/src/validator/registry.js +48 -1
  217. package/src/validator/utils.d.ts +20 -0
  218. package/src/validator/utils.js +115 -12
  219. package/src/widget-behavior.d.ts +1 -0
  220. package/src/workflows/import-app/api/collect.js +221 -0
  221. package/src/workflows/import-app/api/openapi.js +257 -0
  222. package/src/workflows/import-app/api/routes.js +327 -0
  223. package/src/workflows/import-app/api/sources.js +22 -0
  224. package/src/workflows/import-app/api.js +2 -797
  225. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  226. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  227. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  228. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  229. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  230. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  231. package/src/workflows/reconcile/adoption-plan.js +30 -740
  232. package/src/workflows/reconcile/auth/closures.js +115 -0
  233. package/src/workflows/reconcile/auth/formatters.js +142 -0
  234. package/src/workflows/reconcile/auth/inference.js +330 -0
  235. package/src/workflows/reconcile/auth/roles.js +122 -0
  236. package/src/workflows/reconcile/auth.js +35 -690
  237. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  238. package/src/workflows/reconcile/bundle-core.js +12 -598
  239. package/src/workflows/reconcile/canonical-surface.js +1 -1
  240. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  241. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  242. package/src/workflows/reconcile/impacts/patches.js +252 -0
  243. package/src/workflows/reconcile/impacts/reports.js +80 -0
  244. package/src/workflows/reconcile/impacts.js +14 -623
  245. package/src/workspace-docs.d.ts +29 -0
@@ -0,0 +1,572 @@
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_service"|"web_surface"|"ios_surface"|"android_surface"|"database"} kind
23
+ * @property {string} [type]
24
+ * @property {RuntimeStatement} projection
25
+ * @property {import("../../../project-config.js").GeneratorBinding} generator
26
+ * @property {number|null} [port]
27
+ * @property {string|null} [api]
28
+ * @property {string|null} [database]
29
+ * @property {Record<string, string>} [env]
30
+ * @property {RuntimeComponent|null} [apiRuntime]
31
+ * @property {RuntimeComponent|null} [databaseRuntime]
32
+ * @property {RuntimeComponent|null} [apiComponent] Legacy adapter alias for apiRuntime.
33
+ * @property {RuntimeComponent|null} [databaseComponent] Legacy adapter alias for databaseRuntime.
34
+ */
35
+
36
+ /**
37
+ * @typedef {import("../../../project-config.js").RuntimeTopologyRuntime} RuntimeTopologyComponent
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} RuntimeTopology
42
+ * @property {import("../../../project-config.js").ProjectConfig} config
43
+ * @property {RuntimeComponent[]} runtimes
44
+ * @property {RuntimeComponent[]} apiRuntimes
45
+ * @property {RuntimeComponent[]} webRuntimes
46
+ * @property {RuntimeComponent[]} nativeRuntimes
47
+ * @property {RuntimeComponent[]} dbRuntimes
48
+ * @property {RuntimeComponent[]} components Legacy alias for runtimes.
49
+ * @property {RuntimeComponent[]} apiComponents Legacy alias for apiRuntimes.
50
+ * @property {RuntimeComponent[]} webComponents Legacy alias for webRuntimes.
51
+ * @property {RuntimeComponent[]} nativeComponents Legacy alias for nativeRuntimes.
52
+ * @property {RuntimeComponent[]} dbComponents Legacy alias for dbRuntimes.
53
+ * @property {RuntimeComponent|null} primaryApi
54
+ * @property {RuntimeComponent|null} primaryWeb
55
+ * @property {RuntimeComponent|null} primaryDb
56
+ * @property {(component: RuntimeComponent) => string} serviceDir
57
+ * @property {(component: RuntimeComponent) => string} webDir
58
+ * @property {(component: RuntimeComponent) => string} nativeDir
59
+ * @property {(component: RuntimeComponent) => string} dbDir
60
+ */
61
+
62
+ /**
63
+ * @typedef {Object} VerificationSelectionOptions
64
+ * @property {boolean} [keepLookupChecks]
65
+ * @property {boolean} [keepWebChecks]
66
+ */
67
+
68
+ /**
69
+ * @typedef {Object} RuntimeGenerationOptions
70
+ * @property {import("../../../project-config.js").ProjectConfig} [projectConfig]
71
+ * @property {Record<string, any>|null} [implementation]
72
+ * @property {string} [projectionId]
73
+ * @property {string} [dbProjectionId]
74
+ * @property {string} [configDir]
75
+ * @property {string} [projectRoot]
76
+ * @property {RuntimeComponent} [runtime]
77
+ * @property {RuntimeComponent} [component] Legacy alias for runtime.
78
+ */
79
+
80
+ /**
81
+ * @typedef {Object} EnvVarOptions
82
+ * @property {boolean} [primary]
83
+ */
84
+
85
+ /**
86
+ * @param {any} item
87
+ * @returns {string|null}
88
+ */
89
+ function verificationScenarioValue(item) {
90
+ if (!item) {
91
+ return null;
92
+ }
93
+ if (typeof item === "string") {
94
+ return item;
95
+ }
96
+ return item.value || null;
97
+ }
98
+
99
+ /**
100
+ * @param {any} scenario
101
+ * @returns {string}
102
+ */
103
+ function scenarioLabel(scenario) {
104
+ return String(scenario || "")
105
+ .replace(/^verify_/, "")
106
+ .replaceAll("_", " ")
107
+ .trim();
108
+ }
109
+
110
+ /**
111
+ * @param {ResolvedGraph} graph
112
+ * @param {string[]} [methods]
113
+ * @returns {RuntimeStatement[]}
114
+ */
115
+ export function getVerificationEntries(graph, methods = []) {
116
+ const methodSet = new Set(methods);
117
+ return (graph.byKind.verification || [])
118
+ .filter((verification) => methodSet.size === 0 || methodSet.has(verification.method))
119
+ .sort((left, right) => left.id.localeCompare(right.id));
120
+ }
121
+
122
+ /**
123
+ * @param {ResolvedGraph} graph
124
+ * @param {string[]} [methods]
125
+ * @returns {Record<string, any>|null}
126
+ */
127
+ export function buildVerificationSummary(graph, methods = []) {
128
+ const verifications = getVerificationEntries(graph, methods);
129
+ if (verifications.length === 0) {
130
+ return null;
131
+ }
132
+
133
+ const scenarioMap = new Map();
134
+ for (const verification of verifications) {
135
+ const rawScenarios = Array.isArray(verification.scenarios)
136
+ ? verification.scenarios
137
+ : Array.isArray(verification.plan?.scenarios)
138
+ ? /** @type {Array<Record<string, any>>} */ (verification.plan.scenarios).map((entry) => entry?.target?.id || null)
139
+ : [];
140
+ for (const raw of rawScenarios) {
141
+ const id = verificationScenarioValue(raw);
142
+ if (!id || scenarioMap.has(id)) {
143
+ continue;
144
+ }
145
+ scenarioMap.set(id, {
146
+ id,
147
+ label: scenarioLabel(id)
148
+ });
149
+ }
150
+ }
151
+
152
+ return {
153
+ methods: [...new Set(verifications.map((verification) => verification.method))],
154
+ sources: verifications.map((verification) => ({
155
+ id: verification.id,
156
+ name: verification.name || verification.id,
157
+ method: verification.method,
158
+ validates: /** @type {Array<Record<string, any>>} */ (verification.validates || []).map((item) => item.id)
159
+ })),
160
+ scenarios: [...scenarioMap.values()]
161
+ };
162
+ }
163
+
164
+ /**
165
+ * @param {ResolvedGraph} graph
166
+ * @param {string[]} [methods]
167
+ * @returns {Set<string>}
168
+ */
169
+ export function getVerifiedCapabilityIds(graph, methods = []) {
170
+ const verifications = getVerificationEntries(graph, methods);
171
+ const capabilityIds = new Set();
172
+ for (const verification of verifications) {
173
+ for (const target of verification.validates || []) {
174
+ if (target.kind === "capability") {
175
+ capabilityIds.add(target.id);
176
+ }
177
+ }
178
+ }
179
+ return capabilityIds;
180
+ }
181
+
182
+ /**
183
+ * @param {ResolvedGraph} graph
184
+ * @param {Array<Record<string, any>>} checks
185
+ * @param {string[]} [methods]
186
+ * @param {VerificationSelectionOptions} [options]
187
+ * @returns {{ checks: Array<Record<string, any>>, selection: Record<string, any>|null }}
188
+ */
189
+ export function selectChecksByVerification(graph, checks, methods = [], options = {}) {
190
+ const capabilityIds = getVerifiedCapabilityIds(graph, methods);
191
+ if (capabilityIds.size === 0) {
192
+ return {
193
+ checks,
194
+ selection: null
195
+ };
196
+ }
197
+
198
+ const selected = [];
199
+ const selectedCheckIds = [];
200
+ const omittedCheckIds = [];
201
+
202
+ for (const check of checks) {
203
+ const keepLookup = options.keepLookupChecks && check.kind === "lookup_contract";
204
+ const keepWeb = options.keepWebChecks && (check.kind === "web_contract" || check.type === "web_get");
205
+ const matchesCapability = check.capabilityId && capabilityIds.has(check.capabilityId);
206
+ const keep = keepLookup || keepWeb || matchesCapability;
207
+
208
+ if (keep) {
209
+ selected.push(check);
210
+ selectedCheckIds.push(check.id);
211
+ } else {
212
+ omittedCheckIds.push(check.id);
213
+ }
214
+ }
215
+
216
+ return {
217
+ checks: selected.length > 0 ? selected : checks,
218
+ selection: {
219
+ methods,
220
+ capabilityIds: [...capabilityIds].sort(),
221
+ selectedCheckIds,
222
+ omittedCheckIds
223
+ }
224
+ };
225
+ }
226
+
227
+ /**
228
+ * @param {ResolvedGraph} graph
229
+ * @returns {RuntimeStatement[]}
230
+ */
231
+ function apiProjectionCandidates(graph) {
232
+ return (graph.byKind.projection || []).filter((projection) => (projection.http || []).length > 0);
233
+ }
234
+
235
+ /**
236
+ * @param {ResolvedGraph} graph
237
+ * @returns {RuntimeStatement[]}
238
+ */
239
+ function uiWebProjectionCandidates(graph) {
240
+ return (graph.byKind.projection || []).filter(
241
+ (projection) => projection.type === "web_surface" && (projection.uiRoutes || []).length > 0
242
+ );
243
+ }
244
+
245
+ const WEB_UI_FAMILY_PREFIX = "proj_web_surface__";
246
+ const NATIVE_UI_FAMILY_PREFIX = "proj_ios_surface__";
247
+
248
+ /** Prefer canonical ids when multiple shipped web stacks exist (deterministic, not lexicographic). */
249
+ const DEFAULT_WEB_UI_STACK_ORDER = ["proj_web_surface__sveltekit", "proj_web_surface__react"];
250
+
251
+ const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["proj_ios_surface__swiftui"];
252
+
253
+ /**
254
+ * @param {ResolvedGraph} graph
255
+ * @returns {RuntimeStatement[]}
256
+ */
257
+ function uiIosProjectionCandidates(graph) {
258
+ return (graph.byKind.projection || []).filter(
259
+ (projection) => projection.type === "ios_surface" && (projection.uiRoutes || []).length > 0
260
+ );
261
+ }
262
+
263
+ /**
264
+ * Prefer canonical native projections (`proj_ios_surface__{stack}`); otherwise first routed iOS surface projection.
265
+ *
266
+ * @param {ResolvedGraph} graph
267
+ * @returns {RuntimeStatement|undefined}
268
+ */
269
+ export function pickDefaultIosUiProjection(graph) {
270
+ const candidates = uiIosProjectionCandidates(graph);
271
+ const hierarchical = candidates.filter((projection) => projection.id.startsWith(NATIVE_UI_FAMILY_PREFIX));
272
+ if (hierarchical.length > 0) {
273
+ for (const id of DEFAULT_NATIVE_UI_PLATFORM_ORDER) {
274
+ const match = hierarchical.find((projection) => projection.id === id);
275
+ if (match) {
276
+ return match;
277
+ }
278
+ }
279
+ return hierarchical.sort((a, b) => a.id.localeCompare(b.id))[0];
280
+ }
281
+ return candidates[0];
282
+ }
283
+
284
+ /**
285
+ * Prefer canonical shipped web projections (`proj_web_surface__{stack}`); otherwise first routed web surface projection.
286
+ *
287
+ * @param {ResolvedGraph} graph
288
+ * @returns {RuntimeStatement|undefined}
289
+ */
290
+ export function pickDefaultUiWebProjection(graph) {
291
+ const candidates = uiWebProjectionCandidates(graph);
292
+ const hierarchical = candidates.filter((projection) => projection.id.startsWith(WEB_UI_FAMILY_PREFIX));
293
+ if (hierarchical.length > 0) {
294
+ for (const id of DEFAULT_WEB_UI_STACK_ORDER) {
295
+ const match = hierarchical.find((projection) => projection.id === id);
296
+ if (match) {
297
+ return match;
298
+ }
299
+ }
300
+ return hierarchical.sort((a, b) => a.id.localeCompare(b.id))[0];
301
+ }
302
+ return candidates[0];
303
+ }
304
+
305
+ /**
306
+ * @param {ResolvedGraph} graph
307
+ * @param {RuntimeGenerationOptions} [options]
308
+ * @returns {{ apiProjection: RuntimeStatement|null, uiProjection: RuntimeStatement|null, dbProjection: RuntimeStatement|null }}
309
+ */
310
+ export function getDefaultEnvironmentProjections(graph, options = {}) {
311
+ const topology = resolveRuntimeTopology(graph, options);
312
+ const dbCandidates = graph.byKind.projection?.filter((projection) => ["db_contract", "db_contract"].includes(projection.type)) || [];
313
+ const apiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryApi?.projection ||
314
+ (options.projectionId ? getProjection(graph, options.projectionId) : null) ||
315
+ apiProjectionCandidates(graph).find((projection) => projection.id === "proj_api") ||
316
+ apiProjectionCandidates(graph)[0] ||
317
+ null);
318
+ const uiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryWeb?.projection || pickDefaultUiWebProjection(graph) || null);
319
+ let dbProjection = /** @type {RuntimeStatement|null} */ (topology.primaryDb?.projection || null);
320
+ if (!dbProjection && dbCandidates.length > 0) {
321
+ try {
322
+ dbProjection = getDefaultBackendDbProjection(graph, options);
323
+ } catch {
324
+ dbProjection = /** @type {RuntimeStatement|null} */ (dbCandidates[0] || null);
325
+ }
326
+ }
327
+
328
+ return { apiProjection, uiProjection, dbProjection };
329
+ }
330
+
331
+ /**
332
+ * @param {ResolvedGraph} graph
333
+ * @param {string} projectionId
334
+ * @param {RuntimeGenerationOptions} [options]
335
+ * @returns {any}
336
+ */
337
+ export function generateServerBundle(graph, projectionId, options = {}) {
338
+ const topology = resolveRuntimeTopology(graph, options);
339
+ const runtime = options.runtime || options.component || topology.apiRuntimes.find((entry) => entry.projection.id === projectionId);
340
+ if (!runtime) {
341
+ throw new Error(`No api runtime found for projection '${projectionId}'`);
342
+ }
343
+ return generateWithComponentGenerator({
344
+ graph,
345
+ projection: runtime.projection,
346
+ runtime,
347
+ component: runtime,
348
+ topology,
349
+ implementation: options.implementation || null,
350
+ options: { ...options, projectionId }
351
+ }).files;
352
+ }
353
+
354
+ /**
355
+ * @param {ResolvedGraph} graph
356
+ * @param {string} projectionId
357
+ * @param {RuntimeGenerationOptions} [options]
358
+ * @returns {any}
359
+ */
360
+ export function generateWebBundle(graph, projectionId, options = {}) {
361
+ const topology = resolveRuntimeTopology(graph, options);
362
+ const runtime = options.runtime || options.component || topology.webRuntimes.find((entry) => entry.projection.id === projectionId);
363
+ if (!runtime) {
364
+ throw new Error(`No web runtime found for projection '${projectionId}'`);
365
+ }
366
+ return generateWithComponentGenerator({
367
+ graph,
368
+ projection: runtime.projection,
369
+ runtime,
370
+ component: runtime,
371
+ topology,
372
+ implementation: options.implementation || null,
373
+ options: { ...options, projectionId }
374
+ }).files;
375
+ }
376
+
377
+ /**
378
+ * @param {ResolvedGraph} graph
379
+ * @param {string} projectionId
380
+ * @param {RuntimeGenerationOptions} [options]
381
+ * @returns {any}
382
+ */
383
+ export function generateNativeBundle(graph, projectionId, options = {}) {
384
+ const topology = resolveRuntimeTopology(graph, options);
385
+ const runtime = options.runtime || options.component || topology.nativeRuntimes.find((entry) => entry.projection.id === projectionId);
386
+ if (!runtime) {
387
+ throw new Error(`No native runtime found for projection '${projectionId}'`);
388
+ }
389
+ return generateWithComponentGenerator({
390
+ graph,
391
+ projection: runtime.projection,
392
+ runtime,
393
+ component: runtime,
394
+ topology,
395
+ implementation: options.implementation || null,
396
+ options: { ...options, projectionId }
397
+ }).files;
398
+ }
399
+
400
+ /**
401
+ * @param {ResolvedGraph} graph
402
+ * @param {string} projectionId
403
+ * @param {RuntimeGenerationOptions} [options]
404
+ * @returns {any}
405
+ */
406
+ export function generateDbBundle(graph, projectionId, options = {}) {
407
+ const topology = resolveRuntimeTopology(graph, options);
408
+ const runtime = options.runtime || options.component || topology.dbRuntimes.find((entry) => entry.projection.id === projectionId);
409
+ if (!runtime) {
410
+ throw new Error(`No database runtime found for projection '${projectionId}'`);
411
+ }
412
+ return generateWithComponentGenerator({
413
+ graph,
414
+ projection: getProjection(graph, projectionId),
415
+ runtime,
416
+ component: runtime,
417
+ topology,
418
+ implementation: options.implementation || null,
419
+ options: { ...options, projectionId }
420
+ }).files;
421
+ }
422
+
423
+ /**
424
+ * @param {ResolvedGraph} graph
425
+ * @returns {any}
426
+ */
427
+ export function generateRuntimeApiContracts(graph) {
428
+ return generateApiContractGraph(graph, {});
429
+ }
430
+
431
+ /**
432
+ * @param {string} runtimeId
433
+ * @param {EnvVarOptions} [options]
434
+ * @returns {string}
435
+ */
436
+ function envVarPrefix(runtimeId, options = {}) {
437
+ return options.primary || runtimeId === "db"
438
+ ? ""
439
+ : `${runtimeId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_`;
440
+ }
441
+
442
+ /**
443
+ * @param {RuntimeComponent} component
444
+ * @param {EnvVarOptions} [options]
445
+ * @returns {{ databaseUrl: string, databaseAdminUrl: string, dbPort: string, postgresDb: string }}
446
+ */
447
+ export function dbEnvVarsForComponent(component, options = {}) {
448
+ const prefix = envVarPrefix(component.id, options);
449
+ return {
450
+ databaseUrl: component.env?.databaseUrl || `${prefix}DATABASE_URL`,
451
+ databaseAdminUrl: component.env?.databaseAdminUrl || `${prefix}DATABASE_ADMIN_URL`,
452
+ dbPort: component.env?.dbPort || `${prefix}DB_PORT`,
453
+ postgresDb: component.env?.postgresDb || `${prefix}POSTGRES_DB`
454
+ };
455
+ }
456
+
457
+ /**
458
+ * @param {ResolvedGraph} graph
459
+ * @param {import("../../../project-config.js").ProjectConfig} config
460
+ * @returns {RuntimeComponent[]}
461
+ */
462
+ function decorateRuntimes(graph, config) {
463
+ const byProjectionId = new Map((graph.byKind.projection || []).map((projection) => [projection.id, projection]));
464
+ const rawRuntimes = config.topology?.runtimes || [];
465
+ /** @type {RuntimeComponent[]} */
466
+ const runtimes = rawRuntimes.map((runtime) => ({
467
+ ...runtime,
468
+ kind: runtime.kind || null,
469
+ api: runtime.uses_api ?? null,
470
+ database: runtime.uses_database ?? null,
471
+ projection: byProjectionId.get(runtime.projection) || {}
472
+ }));
473
+ const byId = new Map(runtimes.map((runtime) => [runtime.id, runtime]));
474
+ for (const runtime of runtimes) {
475
+ if (runtime.kind === "api_service" && runtime.database) {
476
+ runtime.databaseRuntime = byId.get(runtime.database) || null;
477
+ runtime.databaseComponent = runtime.databaseRuntime;
478
+ }
479
+ if (["web_surface", "ios_surface", "android_surface"].includes(runtime.kind) && runtime.api) {
480
+ runtime.apiRuntime = byId.get(runtime.api) || null;
481
+ runtime.apiComponent = runtime.apiRuntime;
482
+ }
483
+ }
484
+ return runtimes;
485
+ }
486
+
487
+ /**
488
+ * @param {ResolvedGraph} graph
489
+ * @param {RuntimeGenerationOptions} [options]
490
+ * @returns {RuntimeTopology}
491
+ */
492
+ export function resolveRuntimeTopology(graph, options = {}) {
493
+ const config = options.projectConfig || defaultProjectConfigForGraph(graph, options.implementation || null);
494
+ const validation = validateProjectConfig(config, graph, {
495
+ configDir: options.configDir || options.projectRoot || null,
496
+ rootDir: options.projectRoot || options.configDir || null
497
+ });
498
+ if (!validation.ok) {
499
+ throw new Error(validation.errors.map((error) => error.message).join("\n"));
500
+ }
501
+ const runtimes = decorateRuntimes(graph, config);
502
+ const apiRuntimes = runtimes.filter((runtime) => runtime.kind === "api_service");
503
+ const webRuntimes = runtimes.filter((runtime) => runtime.kind === "web_surface");
504
+ const nativeRuntimes = runtimes.filter((runtime) => runtime.kind === "ios_surface" || runtime.kind === "android_surface");
505
+ const dbRuntimes = runtimes.filter((runtime) => runtime.kind === "database");
506
+ const primaryApi = apiRuntimes[0] || null;
507
+ const primaryWeb = webRuntimes[0] || null;
508
+ const primaryDb = primaryApi?.databaseRuntime || dbRuntimes[0] || null;
509
+
510
+ return {
511
+ config,
512
+ runtimes,
513
+ components: runtimes,
514
+ apiRuntimes,
515
+ webRuntimes,
516
+ nativeRuntimes,
517
+ dbRuntimes,
518
+ apiComponents: apiRuntimes,
519
+ webComponents: webRuntimes,
520
+ nativeComponents: nativeRuntimes,
521
+ dbComponents: dbRuntimes,
522
+ primaryApi,
523
+ primaryWeb,
524
+ primaryDb,
525
+ serviceDir(component) {
526
+ return `services/${component.id}`;
527
+ },
528
+ webDir(component) {
529
+ return `web/${component.id}`;
530
+ },
531
+ nativeDir(component) {
532
+ return `native/${component.id}`;
533
+ },
534
+ dbDir(component) {
535
+ return `db/${component.id}`;
536
+ }
537
+ };
538
+ }
539
+
540
+ /**
541
+ * @param {Record<string, any>|null|undefined} runtimeReference
542
+ * @param {RuntimeTopology|null} [topology]
543
+ * @returns {{ server: number, web: number }}
544
+ */
545
+ export function runtimePorts(runtimeReference, topology = null) {
546
+ return {
547
+ server: topology?.primaryApi?.port || runtimeReference?.ports?.server || 3000,
548
+ web: topology?.primaryWeb?.port || runtimeReference?.ports?.web || 5173
549
+ };
550
+ }
551
+
552
+ /**
553
+ * @param {Record<string, any>|null|undefined} runtimeReference
554
+ * @param {RuntimeTopology|null} [topology]
555
+ * @returns {{ api: string, web: string }}
556
+ */
557
+ export function runtimeUrls(runtimeReference, topology = null) {
558
+ const ports = runtimePorts(runtimeReference, topology);
559
+ return {
560
+ api: `http://localhost:${ports.server}`,
561
+ web: `http://localhost:${ports.web}`
562
+ };
563
+ }
564
+
565
+ /**
566
+ * @param {Record<string, any>|null|undefined} runtimeReference
567
+ * @returns {string}
568
+ */
569
+ export function runtimeDemoUserId(runtimeReference) {
570
+ const demo = runtimeReference?.demoEnv || {};
571
+ return demo.userId || demo.memberId || demo.ownerId || demo.primaryActorId || "";
572
+ }