@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,3 @@
1
+ export function createImportContext(...args: any[]): any;
2
+ export function findNearestGitRoot(...args: any[]): any;
3
+ export function normalizeWorkspacePaths(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export const IMPORT_TRACKS: Set<string>;
@@ -0,0 +1,4 @@
1
+ export const extractorRegistry: Record<string, any[]>;
2
+ export const enricherRegistry: Record<string, any[]>;
3
+ export function getExtractorsForTrack(...args: any[]): any[];
4
+ export function getEnrichersForTrack(...args: any[]): any[];
@@ -0,0 +1,217 @@
1
+ // @ts-check
2
+
3
+ import { collectionPatternFromPresentations } from "../../../ui/taxonomy.js";
4
+ import { dedupeCandidateRecords, idHintify, makeCandidateRecord } from "../shared.js";
5
+
6
+ /**
7
+ * @param {Record<string, any>} allCandidates
8
+ * @returns {string[]}
9
+ */
10
+ export function importedApiCapabilityIds(allCandidates) {
11
+ return [...(allCandidates?.api?.capabilities || [])]
12
+ .map((capability) => capability.id_hint)
13
+ .filter(Boolean)
14
+ .sort();
15
+ }
16
+
17
+ /**
18
+ * @param {any} screen
19
+ * @returns {string|null}
20
+ */
21
+ export function loadCapabilityForScreen(screen) {
22
+ return capabilityHintsForScreen(screen).find((hint) => /^cap_(list|get)_/.test(hint)) || null;
23
+ }
24
+
25
+ /**
26
+ * @param {any} value
27
+ * @returns {string|null}
28
+ */
29
+ function normalizeCapabilityHint(value) {
30
+ if (typeof value === "string") {
31
+ return value;
32
+ }
33
+ if (value && typeof value === "object") {
34
+ return value.id_hint || value.id || value.capability_hint || value.capability?.id || null;
35
+ }
36
+ return null;
37
+ }
38
+
39
+ /**
40
+ * @param {any} screen
41
+ * @returns {string[]}
42
+ */
43
+ export function capabilityHintsForScreen(screen) {
44
+ const rawHints = Array.isArray(screen.capability_hints)
45
+ ? screen.capability_hints
46
+ : [screen.capability_hints].filter(Boolean);
47
+ return rawHints.map(normalizeCapabilityHint).filter(Boolean);
48
+ }
49
+
50
+ /**
51
+ * @param {any} candidates
52
+ * @returns {any[]}
53
+ */
54
+ export function uiWidgetCandidates(candidates) {
55
+ return [
56
+ ...(Array.isArray(candidates?.widgets) ? candidates.widgets : []),
57
+ ...(Array.isArray(candidates?.components) ? candidates.components : [])
58
+ ];
59
+ }
60
+
61
+ /**
62
+ * @param {any} widget
63
+ * @param {Record<string, any>} allCandidates
64
+ * @returns {string|null}
65
+ */
66
+ export function inferredDataSourceForWidget(widget, allCandidates) {
67
+ if (widget.data_source) {
68
+ return widget.data_source;
69
+ }
70
+ const capabilityIds = importedApiCapabilityIds(allCandidates);
71
+ const screenStem = String(widget.screen_id || "")
72
+ .replace(/_(list|index|table|grid|results)$/, "")
73
+ .replace(/^list_/, "");
74
+ return capabilityIds.find((id) => /^cap_(list|get)_/.test(id) && id.includes(screenStem)) ||
75
+ capabilityIds.find((id) => /^cap_(list|get)_/.test(id)) ||
76
+ null;
77
+ }
78
+
79
+ /**
80
+ * @param {any} candidates
81
+ * @returns {any[]}
82
+ */
83
+ function deriveUiWidgetCandidates(candidates) {
84
+ /** @type {any[]} */
85
+ const screens = candidates.screens || [];
86
+ /** @type {any[]} */
87
+ const actions = candidates.actions || [];
88
+ const presentations = [...new Set(actions
89
+ .filter((entry) => entry.kind === "ui_presentation")
90
+ .map((entry) => entry.presentation)
91
+ .filter(Boolean))].sort();
92
+ const widgetScreens = screens.filter((screen) => ["list", "dashboard", "analytics", "report", "feed", "inbox"].includes(screen.screen_kind));
93
+
94
+ return widgetScreens.map((screen) => {
95
+ const pattern = collectionPatternFromPresentations(presentations);
96
+ const widgetStem = idHintify(`${screen.id_hint}_results`);
97
+ const loadCapability = loadCapabilityForScreen(screen);
98
+ return makeCandidateRecord({
99
+ kind: "widget",
100
+ idHint: `widget_${widgetStem}`,
101
+ label: `${screen.label || screen.id_hint} results`,
102
+ confidence: presentations.length > 0 ? "medium" : "low",
103
+ sourceKind: "ui_projection_inference",
104
+ sourceOfTruth: "candidate",
105
+ provenance: screen.provenance || [],
106
+ screen_id: screen.id_hint,
107
+ region: "results",
108
+ pattern,
109
+ data_prop: "rows",
110
+ data_source: loadCapability,
111
+ inferred_props: [{ name: "rows", type: "array", required: true, source: loadCapability }],
112
+ inferred_events: [],
113
+ inferred_region: "results",
114
+ inferred_pattern: pattern,
115
+ evidence: screen.provenance || [],
116
+ missing_decisions: [
117
+ "confirm widget reuse boundary",
118
+ "confirm prop names and data source",
119
+ "confirm events and behavior",
120
+ "confirm supported regions and patterns"
121
+ ],
122
+ notes: [
123
+ "Imported widget candidates are review-only.",
124
+ "Confirm props, behavior, events, and reuse before adoption."
125
+ ]
126
+ });
127
+ });
128
+ }
129
+
130
+ /**
131
+ * @param {string} track
132
+ * @param {any} candidates
133
+ * @returns {any}
134
+ */
135
+ export function normalizeCandidatesForTrack(track, candidates) {
136
+ /** @param {any} record */
137
+ const idHint = (record) => record.id_hint;
138
+ /** @param {any} record */
139
+ const apiRouteKey = (record) => `${record.method}:${record.path}:${record.source_kind}`;
140
+ /** @param {any} entry */
141
+ const withoutIdHint = ({ id_hint, ...entry }) => entry;
142
+ if (track === "db") {
143
+ return {
144
+ entities: dedupeCandidateRecords(candidates.entities || [], idHint),
145
+ enums: dedupeCandidateRecords(candidates.enums || [], idHint),
146
+ relations: dedupeCandidateRecords(candidates.relations || [], idHint),
147
+ indexes: dedupeCandidateRecords(candidates.indexes || [], idHint)
148
+ };
149
+ }
150
+ if (track === "api") {
151
+ return {
152
+ capabilities: dedupeCandidateRecords(candidates.capabilities || [], idHint),
153
+ routes: dedupeCandidateRecords(
154
+ /** @type {any[]} */ (candidates.routes || []).map((route) => ({ ...route, id_hint: route.id_hint || `${route.method}_${route.path}` })),
155
+ apiRouteKey
156
+ ).map(withoutIdHint),
157
+ stacks: [...new Set(candidates.stacks || [])].sort()
158
+ };
159
+ }
160
+ if (track === "ui") {
161
+ const explicitWidgets = uiWidgetCandidates(candidates);
162
+ const derivedWidgets = deriveUiWidgetCandidates(candidates);
163
+ return {
164
+ screens: dedupeCandidateRecords(candidates.screens || [], idHint),
165
+ routes: dedupeCandidateRecords(candidates.routes || [], idHint),
166
+ actions: dedupeCandidateRecords(candidates.actions || [], idHint),
167
+ widgets: dedupeCandidateRecords([...explicitWidgets, ...derivedWidgets], idHint),
168
+ stacks: [...new Set(candidates.stacks || [])].sort()
169
+ };
170
+ }
171
+ if (track === "verification") {
172
+ return {
173
+ verifications: dedupeCandidateRecords(candidates.verifications || [], idHint),
174
+ scenarios: dedupeCandidateRecords(candidates.scenarios || [], idHint),
175
+ frameworks: [...new Set(candidates.frameworks || [])].sort(),
176
+ scripts: dedupeCandidateRecords(
177
+ /** @type {any[]} */ (candidates.scripts || []).map((script) => ({
178
+ ...script,
179
+ id_hint: script.id_hint || `${script.file || "package.json"}:${script.name || "test"}`
180
+ })),
181
+ idHint
182
+ ).map(withoutIdHint)
183
+ };
184
+ }
185
+ return {
186
+ workflows: dedupeCandidateRecords(candidates.workflows || [], idHint),
187
+ workflow_states: dedupeCandidateRecords(candidates.workflow_states || [], idHint),
188
+ workflow_transitions: dedupeCandidateRecords(candidates.workflow_transitions || [], idHint)
189
+ };
190
+ }
191
+
192
+ /**
193
+ * @param {any} uiCandidates
194
+ * @param {Record<string, any>} allCandidates
195
+ * @returns {any}
196
+ */
197
+ export function enrichUiWidgetDataSources(uiCandidates, allCandidates) {
198
+ if (!uiCandidates) {
199
+ return uiCandidates;
200
+ }
201
+ const widgets = uiWidgetCandidates(uiCandidates);
202
+ const { components, ...canonicalCandidates } = uiCandidates;
203
+ return {
204
+ ...canonicalCandidates,
205
+ widgets: widgets.map((widget) => {
206
+ const dataSource = inferredDataSourceForWidget(widget, allCandidates);
207
+ const dataProp = widget.data_prop || "rows";
208
+ return {
209
+ ...widget,
210
+ data_source: widget.data_source || dataSource,
211
+ inferred_props: /** @type {any[]} */ (widget.inferred_props || []).map((prop) =>
212
+ prop.name === dataProp ? { ...prop, source: prop.source || dataSource } : prop
213
+ )
214
+ };
215
+ })
216
+ };
217
+ }
@@ -0,0 +1,22 @@
1
+ // @ts-check
2
+
3
+ import { IMPORT_TRACKS } from "../contracts.js";
4
+
5
+ /**
6
+ * @param {unknown} fromValue
7
+ * @returns {string[]}
8
+ */
9
+ export function parseImportTracks(fromValue) {
10
+ if (!fromValue) {
11
+ return ["db", "api", "ui", "workflows", "verification"];
12
+ }
13
+ const tracks = String(fromValue).split(",").map((track) => track.trim().toLowerCase()).filter(Boolean);
14
+ if (tracks.length === 0) {
15
+ throw new Error("Expected --from to include at least one import track");
16
+ }
17
+ const invalid = tracks.filter((track) => !IMPORT_TRACKS.has(track));
18
+ if (invalid.length > 0) {
19
+ throw new Error(`Unsupported import track(s): ${invalid.join(", ")}`);
20
+ }
21
+ return [...new Set(tracks)];
22
+ }
@@ -0,0 +1,50 @@
1
+ // @ts-check
2
+
3
+ import { ensureTrailingNewline } from "../shared.js";
4
+ import { uiWidgetCandidates } from "./candidates.js";
5
+
6
+ /**
7
+ * @param {string} track
8
+ * @param {any} candidates
9
+ * @returns {string}
10
+ */
11
+ export function reportMarkdown(track, candidates) {
12
+ if (track === "db") {
13
+ return ensureTrailingNewline(
14
+ `# DB Import Report\n\n- Entities: ${candidates.entities.length}\n- Enums: ${candidates.enums.length}\n- Relations: ${candidates.relations.length}\n- Indexes: ${candidates.indexes.length}\n`
15
+ );
16
+ }
17
+ if (track === "api") {
18
+ return ensureTrailingNewline(
19
+ `# API Import Report\n\n- Capabilities: ${candidates.capabilities.length}\n- Routes: ${candidates.routes.length}\n- Stacks: ${candidates.stacks.length ? candidates.stacks.join(", ") : "none"}\n`
20
+ );
21
+ }
22
+ if (track === "ui") {
23
+ const widgets = uiWidgetCandidates(candidates);
24
+ const widgetLines = widgets.map((widget) =>
25
+ `- \`${widget.id_hint}\` confidence ${widget.confidence || "unknown"} pattern \`${widget.pattern || widget.inferred_pattern || "unknown"}\` region \`${widget.region || widget.inferred_region || "unknown"}\` evidence ${(widget.evidence || widget.provenance || []).length} missing decisions ${(widget.missing_decisions || []).length}`
26
+ );
27
+ return ensureTrailingNewline(
28
+ `# UI Import Report\n\n- Screens: ${candidates.screens.length}\n- Routes: ${candidates.routes.length}\n- Actions: ${candidates.actions.length}\n- Widgets: ${widgets.length}\n- Stacks: ${candidates.stacks.length ? candidates.stacks.join(", ") : "none"}\n\n## Widget Candidates\n\n${widgetLines.length ? widgetLines.join("\n") : "- none"}\n\n## Next Validation\n\n- Review candidates under \`topogram/candidates/app/ui/drafts/widgets/**\`.\n- Run \`topogram import plan <path>\` before adoption.\n- After adoption, run \`topogram check <path>\`, \`topogram widget check <path>\`, and \`topogram widget behavior <path>\`.\n`
29
+ );
30
+ }
31
+ if (track === "verification") {
32
+ return ensureTrailingNewline(
33
+ `# Verification Import Report\n\n- Verifications: ${candidates.verifications.length}\n- Scenarios: ${candidates.scenarios.length}\n- Frameworks: ${candidates.frameworks.length ? candidates.frameworks.join(", ") : "none"}\n- Scripts: ${candidates.scripts.length}\n`
34
+ );
35
+ }
36
+ return ensureTrailingNewline(
37
+ `# Workflow Import Report\n\n- Workflows: ${candidates.workflows.length}\n- States: ${candidates.workflow_states.length}\n- Transitions: ${candidates.workflow_transitions.length}\n`
38
+ );
39
+ }
40
+
41
+ /**
42
+ * @param {Record<string, any>} candidates
43
+ * @param {string[]} tracks
44
+ * @returns {string}
45
+ */
46
+ export function appReportMarkdown(candidates, tracks) {
47
+ return ensureTrailingNewline(
48
+ `# App Import Report\n\nTracks: ${tracks.join(", ")}\n\n## DB\n\n- Entities: ${candidates.db?.entities?.length || 0}\n- Enums: ${candidates.db?.enums?.length || 0}\n- Relations: ${candidates.db?.relations?.length || 0}\n\n## API\n\n- Capabilities: ${candidates.api?.capabilities?.length || 0}\n- Routes: ${candidates.api?.routes?.length || 0}\n- Stacks: ${candidates.api?.stacks?.length ? candidates.api.stacks.join(", ") : "none"}\n\n## UI\n\n- Screens: ${candidates.ui?.screens?.length || 0}\n- Routes: ${candidates.ui?.routes?.length || 0}\n- Actions: ${candidates.ui?.actions?.length || 0}\n- Widgets: ${uiWidgetCandidates(candidates.ui).length}\n- Stacks: ${candidates.ui?.stacks?.length ? candidates.ui.stacks.join(", ") : "none"}\n\n## Workflows\n\n- Workflows: ${candidates.workflows?.workflows?.length || 0}\n- States: ${candidates.workflows?.workflow_states?.length || 0}\n- Transitions: ${candidates.workflows?.workflow_transitions?.length || 0}\n\n## Verification\n\n- Verifications: ${candidates.verification?.verifications?.length || 0}\n- Scenarios: ${candidates.verification?.scenarios?.length || 0}\n- Frameworks: ${candidates.verification?.frameworks?.length ? candidates.verification.frameworks.join(", ") : "none"}\n- Scripts: ${candidates.verification?.scripts?.length || 0}\n`
49
+ );
50
+ }
@@ -0,0 +1,79 @@
1
+ // @ts-check
2
+
3
+ import { createImportContext } from "../context.js";
4
+ import { enrichUiWidgetDataSources } from "./candidates.js";
5
+ import { parseImportTracks } from "./options.js";
6
+ import { appReportMarkdown, reportMarkdown } from "./reports.js";
7
+ import { runTrack } from "./tracks.js";
8
+ import { draftUiProjectionFiles } from "./ui-drafts.js";
9
+
10
+ /**
11
+ * @param {string} inputPath
12
+ * @param {Record<string, any>} [options]
13
+ * @returns {{ summary: any, files: Record<string, string>, defaultOutDir: string }}
14
+ */
15
+ export function runImportApp(inputPath, options = {}) {
16
+ const tracks = parseImportTracks(options.from);
17
+ const context = createImportContext(inputPath, options);
18
+ /** @type {Record<string, ReturnType<typeof runTrack>>} */
19
+ const resultsByTrack = {};
20
+ context.priorResults = resultsByTrack;
21
+ context.scanDocsSummary = options.scanDocsSummary || null;
22
+ /** @type {Record<string, any[]>} */
23
+ const findings = {};
24
+ /** @type {Record<string, any>} */
25
+ const candidates = {};
26
+ /** @type {Record<string, string>} */
27
+ const files = {};
28
+
29
+ for (const track of tracks) {
30
+ if (track === "workflows") {
31
+ if (!resultsByTrack.db) {
32
+ resultsByTrack.db = runTrack(context, "db");
33
+ }
34
+ if (!resultsByTrack.api) {
35
+ resultsByTrack.api = runTrack(context, "api");
36
+ }
37
+ }
38
+ if (track === "verification") {
39
+ if (!resultsByTrack.api) {
40
+ resultsByTrack.api = runTrack(context, "api");
41
+ }
42
+ }
43
+ const result = runTrack(context, track);
44
+ resultsByTrack[track] = result;
45
+ findings[track] = result.findings;
46
+ candidates[track] = result.candidates;
47
+ files[`candidates/app/${track}/findings.json`] = `${JSON.stringify(result.findings, null, 2)}\n`;
48
+ files[`candidates/app/${track}/candidates.json`] = `${JSON.stringify(result.candidates, null, 2)}\n`;
49
+ files[`candidates/app/${track}/report.md`] = reportMarkdown(track, result.candidates);
50
+ }
51
+
52
+ if (candidates.ui) {
53
+ candidates.ui = enrichUiWidgetDataSources(candidates.ui, candidates);
54
+ files["candidates/app/ui/candidates.json"] = `${JSON.stringify(candidates.ui, null, 2)}\n`;
55
+ files["candidates/app/ui/report.md"] = reportMarkdown("ui", candidates.ui);
56
+ Object.assign(files, draftUiProjectionFiles(context, candidates.ui, candidates));
57
+ }
58
+
59
+ const summary = {
60
+ type: "import_app_report",
61
+ workspace: context.paths.workspaceRoot,
62
+ topogram_root: context.paths.topogramRoot,
63
+ bootstrapped_topogram_root: context.paths.bootstrappedTopogramRoot,
64
+ tracks,
65
+ findings_count: Object.values(findings).reduce((total, entries) => total + entries.length, 0),
66
+ extractor_detections: Object.fromEntries(Object.entries(resultsByTrack).map(([track, result]) => [track, result.extractor_detections])),
67
+ candidates
68
+ };
69
+
70
+ files["candidates/app/findings.json"] = `${JSON.stringify(findings, null, 2)}\n`;
71
+ files["candidates/app/candidates.json"] = `${JSON.stringify(candidates, null, 2)}\n`;
72
+ files["candidates/app/report.md"] = appReportMarkdown(candidates, tracks);
73
+
74
+ return {
75
+ summary,
76
+ files,
77
+ defaultOutDir: context.paths.topogramRoot
78
+ };
79
+ }
@@ -0,0 +1,150 @@
1
+ // @ts-check
2
+
3
+ import { getEnrichersForTrack, getExtractorsForTrack } from "../registry.js";
4
+ import { normalizeCandidatesForTrack } from "./candidates.js";
5
+
6
+ /**
7
+ * @param {any} context
8
+ * @param {any[]} extractors
9
+ * @returns {Array<{ extractor: any, detection: any }>}
10
+ */
11
+ function sortExtractors(context, extractors) {
12
+ return extractors
13
+ .map((extractor) => ({ extractor, detection: extractor.detect(context) || { score: 0, reasons: [] } }))
14
+ .filter((entry) => entry.detection.score > 0)
15
+ .sort((a, b) => b.detection.score - a.detection.score || a.extractor.id.localeCompare(b.extractor.id));
16
+ }
17
+
18
+ /**
19
+ * @param {string} track
20
+ * @param {Array<{ extractor: any, detection: any }>} detections
21
+ * @returns {Array<{ extractor: any, detection: any }>}
22
+ */
23
+ function selectDetectionsForTrack(track, detections) {
24
+ if (track === "db") {
25
+ const prisma = detections.find((entry) => entry.extractor.id === "db.prisma");
26
+ if (prisma) return [prisma];
27
+ const djangoModels = detections.find((entry) => entry.extractor.id === "db.django-models");
28
+ if (djangoModels) return [djangoModels];
29
+ const efCore = detections.find((entry) => entry.extractor.id === "db.ef-core");
30
+ if (efCore) return [efCore];
31
+ const room = detections.find((entry) => entry.extractor.id === "db.room");
32
+ if (room) return [room];
33
+ const swiftData = detections.find((entry) => entry.extractor.id === "db.swiftdata");
34
+ if (swiftData) return [swiftData];
35
+ const dotnetModels = detections.find((entry) => entry.extractor.id === "db.dotnet-models");
36
+ if (dotnetModels) return [dotnetModels];
37
+ const flutterEntities = detections.find((entry) => entry.extractor.id === "db.flutter-entities");
38
+ if (flutterEntities) return [flutterEntities];
39
+ const reactNativeEntities = detections.find((entry) => entry.extractor.id === "db.react-native-entities");
40
+ if (reactNativeEntities) return [reactNativeEntities];
41
+ const railsSchema = detections.find((entry) => entry.extractor.id === "db.rails-schema");
42
+ if (railsSchema) return [railsSchema];
43
+ const liquibase = detections.find((entry) => entry.extractor.id === "db.liquibase");
44
+ if (liquibase) return [liquibase];
45
+ const myBatisXml = detections.find((entry) => entry.extractor.id === "db.mybatis-xml");
46
+ if (myBatisXml) return [myBatisXml];
47
+ const jpa = detections.find((entry) => entry.extractor.id === "db.jpa");
48
+ if (jpa) return [jpa];
49
+ const drizzle = detections.find((entry) => entry.extractor.id === "db.drizzle");
50
+ if (drizzle) return [drizzle];
51
+ const sql = detections.find((entry) => entry.extractor.id === "db.sql");
52
+ if (sql) return [sql];
53
+ const snapshot = detections.find((entry) => entry.extractor.id === "db.snapshot");
54
+ return snapshot ? [snapshot] : [];
55
+ }
56
+ if (track === "api") {
57
+ const openApi = detections.find((entry) => entry.extractor.id === "api.openapi");
58
+ if (openApi) return [openApi];
59
+ const openApiCode = detections.find((entry) => entry.extractor.id === "api.openapi-code");
60
+ if (openApiCode) return [openApiCode];
61
+ const graphQlSdl = detections.find((entry) => entry.extractor.id === "api.graphql-sdl");
62
+ if (graphQlSdl) return [graphQlSdl];
63
+ const trpc = detections.find((entry) => entry.extractor.id === "api.trpc");
64
+ if (trpc) return [trpc];
65
+ const aspNetCore = detections.find((entry) => entry.extractor.id === "api.aspnet-core");
66
+ if (aspNetCore) return [aspNetCore];
67
+ const retrofit = detections.find((entry) => entry.extractor.id === "api.retrofit");
68
+ if (retrofit) return [retrofit];
69
+ const swiftWebApi = detections.find((entry) => entry.extractor.id === "api.swift-webapi");
70
+ if (swiftWebApi) return [swiftWebApi];
71
+ const flutterDio = detections.find((entry) => entry.extractor.id === "api.flutter-dio");
72
+ if (flutterDio) return [flutterDio];
73
+ const reactNativeRepository = detections.find((entry) => entry.extractor.id === "api.react-native-repository");
74
+ if (reactNativeRepository) return [reactNativeRepository];
75
+ const fastify = detections.find((entry) => entry.extractor.id === "api.fastify");
76
+ if (fastify) return [fastify];
77
+ const express = detections.find((entry) => entry.extractor.id === "api.express");
78
+ if (express) return [express];
79
+ const djangoRoutes = detections.find((entry) => entry.extractor.id === "api.django-routes");
80
+ if (djangoRoutes) return [djangoRoutes];
81
+ const railsRoutes = detections.find((entry) => entry.extractor.id === "api.rails-routes");
82
+ if (railsRoutes) return [railsRoutes];
83
+ const micronaut = detections.find((entry) => entry.extractor.id === "api.micronaut");
84
+ if (micronaut) return [micronaut];
85
+ const jaxrs = detections.find((entry) => entry.extractor.id === "api.jaxrs");
86
+ if (jaxrs) return [jaxrs];
87
+ const springWeb = detections.find((entry) => entry.extractor.id === "api.spring-web");
88
+ if (springWeb) return [springWeb];
89
+ }
90
+ return detections;
91
+ }
92
+
93
+ /**
94
+ * @param {string} track
95
+ * @returns {any}
96
+ */
97
+ function initialCandidatesForTrack(track) {
98
+ if (track === "db") {
99
+ return { entities: [], enums: [], relations: [], indexes: [] };
100
+ }
101
+ if (track === "api") {
102
+ return { capabilities: [], routes: [], stacks: [] };
103
+ }
104
+ if (track === "ui") {
105
+ return { screens: [], routes: [], actions: [], stacks: [] };
106
+ }
107
+ if (track === "verification") {
108
+ return { verifications: [], scenarios: [], frameworks: [], scripts: [] };
109
+ }
110
+ return { workflows: [], workflow_states: [], workflow_transitions: [] };
111
+ }
112
+
113
+ /**
114
+ * @param {any} context
115
+ * @param {string} track
116
+ * @returns {{ findings: any[], candidates: any, extractor_detections: any[] }}
117
+ */
118
+ export function runTrack(context, track) {
119
+ const findings = [];
120
+ const rawCandidates = initialCandidatesForTrack(track);
121
+
122
+ for (const { extractor, detection } of selectDetectionsForTrack(track, sortExtractors(context, getExtractorsForTrack(track)))) {
123
+ const result = extractor.extract(context) || { findings: [], candidates: {} };
124
+ findings.push({
125
+ extractor: extractor.id,
126
+ detection,
127
+ findings: result.findings || []
128
+ });
129
+ for (const [key, value] of Object.entries(result.candidates || {})) {
130
+ if (Array.isArray(rawCandidates[key])) {
131
+ rawCandidates[key].push(...value);
132
+ } else if (Array.isArray(value)) {
133
+ rawCandidates[key] = [...value];
134
+ }
135
+ }
136
+ }
137
+
138
+ let candidates = normalizeCandidatesForTrack(track, rawCandidates);
139
+ for (const enricher of getEnrichersForTrack(track)) {
140
+ const applies = enricher.applies(context, candidates);
141
+ if (!applies) continue;
142
+ candidates = normalizeCandidatesForTrack(track, enricher.enrich(context, candidates) || candidates);
143
+ }
144
+
145
+ return {
146
+ findings: findings.flatMap((entry) => entry.findings || []),
147
+ candidates,
148
+ extractor_detections: findings.map(({ extractor, detection }) => ({ extractor, ...detection }))
149
+ };
150
+ }