@topogram/cli 0.3.64 → 0.3.66

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 (278) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +716 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/adoption/reporting.js +1 -1
  5. package/src/agent-brief.js +7 -21
  6. package/src/agent-ops/query-builders/auth.js +375 -0
  7. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  8. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  9. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  10. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  12. package/src/agent-ops/query-builders/change-risk.js +25 -0
  13. package/src/agent-ops/query-builders/common.js +149 -0
  14. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  15. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  16. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  17. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  18. package/src/agent-ops/query-builders/work-packets.js +417 -0
  19. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  20. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  21. package/src/agent-ops/query-builders/workflow-presets-core.js +677 -0
  22. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  23. package/src/agent-ops/query-builders.d.ts +26 -26
  24. package/src/agent-ops/query-builders.js +42 -5021
  25. package/src/archive/jsonl.js +2 -2
  26. package/src/archive/resolver-bridge.js +1 -1
  27. package/src/archive/unarchive.js +2 -1
  28. package/src/catalog/constants.js +10 -0
  29. package/src/catalog/copy.js +65 -0
  30. package/src/catalog/diagnostics.js +15 -0
  31. package/src/catalog/entries.js +42 -0
  32. package/src/catalog/files.js +67 -0
  33. package/src/catalog/provenance.js +123 -0
  34. package/src/catalog/source.js +150 -0
  35. package/src/catalog/validation.js +252 -0
  36. package/src/catalog.d.ts +2 -0
  37. package/src/catalog.js +18 -746
  38. package/src/cli/command-parsers/project.js +3 -0
  39. package/src/cli/command-parsers/shared.js +1 -1
  40. package/src/cli/commands/agent.js +2 -2
  41. package/src/cli/commands/catalog/check.js +31 -0
  42. package/src/cli/commands/catalog/copy.js +59 -0
  43. package/src/cli/commands/catalog/doctor.js +248 -0
  44. package/src/cli/commands/catalog/help.js +21 -0
  45. package/src/cli/commands/catalog/list.js +52 -0
  46. package/src/cli/commands/catalog/runner.js +92 -0
  47. package/src/cli/commands/catalog/shared.js +17 -0
  48. package/src/cli/commands/catalog/show.js +134 -0
  49. package/src/cli/commands/catalog.js +30 -615
  50. package/src/cli/commands/check.js +3 -3
  51. package/src/cli/commands/doctor.js +2 -9
  52. package/src/cli/commands/generator-policy/package-info.js +162 -0
  53. package/src/cli/commands/generator-policy/payloads.js +372 -0
  54. package/src/cli/commands/generator-policy/printers.js +159 -0
  55. package/src/cli/commands/generator-policy/runner.js +81 -0
  56. package/src/cli/commands/generator-policy/shared.js +39 -0
  57. package/src/cli/commands/generator-policy.js +15 -783
  58. package/src/cli/commands/import/adopt.js +170 -0
  59. package/src/cli/commands/import/check.js +91 -0
  60. package/src/cli/commands/import/diff.js +84 -0
  61. package/src/cli/commands/import/help.js +47 -0
  62. package/src/cli/commands/import/paths.js +269 -0
  63. package/src/cli/commands/import/plan.js +292 -0
  64. package/src/cli/commands/import/refresh.js +471 -0
  65. package/src/cli/commands/import/status-history.js +196 -0
  66. package/src/cli/commands/import/workspace.js +233 -0
  67. package/src/cli/commands/import.js +33 -1732
  68. package/src/cli/commands/migrate.js +153 -0
  69. package/src/cli/commands/package/constants.js +17 -0
  70. package/src/cli/commands/package/doctor.js +240 -0
  71. package/src/cli/commands/package/help.js +27 -0
  72. package/src/cli/commands/package/lockfile.js +135 -0
  73. package/src/cli/commands/package/npm.js +97 -0
  74. package/src/cli/commands/package/reporting.js +35 -0
  75. package/src/cli/commands/package/runner.js +33 -0
  76. package/src/cli/commands/package/shared.js +9 -0
  77. package/src/cli/commands/package/update-cli.js +252 -0
  78. package/src/cli/commands/package/versions.js +35 -0
  79. package/src/cli/commands/package.js +29 -813
  80. package/src/cli/commands/query/change-plan.js +68 -0
  81. package/src/cli/commands/query/definitions.js +202 -0
  82. package/src/cli/commands/query/import-adopt.js +121 -0
  83. package/src/cli/commands/query/runner/artifacts.js +102 -0
  84. package/src/cli/commands/query/runner/boundaries.js +211 -0
  85. package/src/cli/commands/query/runner/change.js +182 -0
  86. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  87. package/src/cli/commands/query/runner/index.js +31 -0
  88. package/src/cli/commands/query/runner/output.js +12 -0
  89. package/src/cli/commands/query/runner/workflow.js +241 -0
  90. package/src/cli/commands/query/runner.js +3 -0
  91. package/src/cli/commands/query/workflow-context.js +5 -0
  92. package/src/cli/commands/query/workspace.js +270 -0
  93. package/src/cli/commands/query.js +9 -1300
  94. package/src/cli/commands/source.js +3 -12
  95. package/src/cli/commands/template/baseline.js +100 -0
  96. package/src/cli/commands/template/check.js +467 -0
  97. package/src/cli/commands/template/constants.js +8 -0
  98. package/src/cli/commands/template/diagnostics.js +26 -0
  99. package/src/cli/commands/template/help.js +28 -0
  100. package/src/cli/commands/template/lifecycle.js +404 -0
  101. package/src/cli/commands/template/list-show.js +287 -0
  102. package/src/cli/commands/template/policy.js +422 -0
  103. package/src/cli/commands/template/shared.js +127 -0
  104. package/src/cli/commands/template/updates.js +352 -0
  105. package/src/cli/commands/template-runner.js +6 -6
  106. package/src/cli/commands/template.js +41 -2143
  107. package/src/cli/commands/trust.js +1 -1
  108. package/src/cli/commands/workflow.js +6 -1
  109. package/src/cli/dispatcher.js +6 -1
  110. package/src/cli/help.js +15 -14
  111. package/src/cli/migration-guidance.js +1 -1
  112. package/src/cli/output-safety.js +2 -1
  113. package/src/cli/path-normalization.js +3 -13
  114. package/src/generator/api/contracts.js +497 -0
  115. package/src/generator/api/metadata.js +221 -0
  116. package/src/generator/api/openapi.js +559 -0
  117. package/src/generator/api/schema.js +124 -0
  118. package/src/generator/api/types.d.ts +98 -0
  119. package/src/generator/api.js +3 -1195
  120. package/src/generator/context/domain-page.js +1 -1
  121. package/src/generator/context/shared/domain-sdlc.js +282 -0
  122. package/src/generator/context/shared/maintained-boundary.js +665 -0
  123. package/src/generator/context/shared/metrics.js +85 -0
  124. package/src/generator/context/shared/primitives.js +64 -0
  125. package/src/generator/context/shared/relationships.js +453 -0
  126. package/src/generator/context/shared/summaries.js +263 -0
  127. package/src/generator/context/shared/types.d.ts +207 -0
  128. package/src/generator/context/shared.d.ts +42 -0
  129. package/src/generator/context/shared.js +80 -1390
  130. package/src/generator/context/slice/core.js +397 -0
  131. package/src/generator/context/slice/sdlc.js +417 -0
  132. package/src/generator/context/slice/ui-packets.js +183 -0
  133. package/src/generator/context/slice.js +2 -859
  134. package/src/generator/context/task-mode.js +2 -2
  135. package/src/generator/registry/index.js +507 -0
  136. package/src/generator/registry.js +18 -504
  137. package/src/generator/runtime/environment/index.js +666 -0
  138. package/src/generator/runtime/environment.js +4 -666
  139. package/src/generator/runtime/runtime-check/index.js +554 -0
  140. package/src/generator/runtime/runtime-check.js +4 -554
  141. package/src/generator/runtime/shared/index.js +572 -0
  142. package/src/generator/runtime/shared.js +19 -570
  143. package/src/generator/sdlc/doc-page.js +1 -1
  144. package/src/generator/shared.d.ts +2 -0
  145. package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
  146. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
  147. package/src/generator/surfaces/shared.d.ts +3 -0
  148. package/src/generator/widget-conformance/behavior-report.js +258 -0
  149. package/src/generator/widget-conformance/checks.js +371 -0
  150. package/src/generator/widget-conformance/projection-context.js +200 -0
  151. package/src/generator/widget-conformance/report.js +166 -0
  152. package/src/generator/widget-conformance/types.d.ts +121 -0
  153. package/src/generator/widget-conformance.js +3 -824
  154. package/src/import/core/context.d.ts +3 -0
  155. package/src/import/core/context.js +5 -7
  156. package/src/import/core/contracts.d.ts +1 -0
  157. package/src/import/core/registry.d.ts +4 -0
  158. package/src/import/core/runner/candidates.js +337 -0
  159. package/src/import/core/runner/options.js +22 -0
  160. package/src/import/core/runner/reports.js +51 -0
  161. package/src/import/core/runner/run.js +79 -0
  162. package/src/import/core/runner/tracks.js +150 -0
  163. package/src/import/core/runner/ui-drafts.js +393 -0
  164. package/src/import/core/runner.js +3 -698
  165. package/src/import/core/shared/api-routes.js +221 -0
  166. package/src/import/core/shared/candidates.js +97 -0
  167. package/src/import/core/shared/files.js +177 -0
  168. package/src/import/core/shared/next-app.js +389 -0
  169. package/src/import/core/shared/types.d.ts +51 -0
  170. package/src/import/core/shared/ui-routes.js +230 -0
  171. package/src/import/core/shared.js +60 -861
  172. package/src/new-project/constants.js +128 -0
  173. package/src/new-project/create.js +90 -0
  174. package/src/new-project/json.js +28 -0
  175. package/src/new-project/metadata.js +96 -0
  176. package/src/new-project/package-spec.js +161 -0
  177. package/src/new-project/project-files.js +351 -0
  178. package/src/new-project/template-policy.js +269 -0
  179. package/src/new-project/template-resolution.js +370 -0
  180. package/src/new-project/template-snapshots.js +442 -0
  181. package/src/new-project/template-updates.js +512 -0
  182. package/src/new-project/types.d.ts +83 -0
  183. package/src/new-project.js +6 -2277
  184. package/src/parser.d.ts +87 -1
  185. package/src/parser.js +118 -0
  186. package/src/policy/review-boundaries.d.ts +15 -0
  187. package/src/project-config/index.js +591 -0
  188. package/src/project-config.js +19 -561
  189. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  190. package/src/resolver/enrich/bug.js +2 -0
  191. package/src/resolver/enrich/pitch.js +2 -0
  192. package/src/resolver/enrich/requirement.js +2 -0
  193. package/src/resolver/enrich/task.js +2 -0
  194. package/src/resolver/index.js +19 -2089
  195. package/src/resolver/normalize.js +384 -1
  196. package/src/resolver/plans.js +168 -0
  197. package/src/resolver/projections-api.js +494 -0
  198. package/src/resolver/projections-db.js +133 -0
  199. package/src/resolver/projections-ui.js +317 -0
  200. package/src/resolver/shapes.js +251 -0
  201. package/src/resolver/shared.js +278 -0
  202. package/src/resolver/widgets.js +132 -0
  203. package/src/sdlc/adopt.js +6 -5
  204. package/src/sdlc/paths.js +3 -5
  205. package/src/sdlc/scaffold.js +2 -1
  206. package/src/template-trust/constants.js +62 -0
  207. package/src/template-trust/content.js +258 -0
  208. package/src/template-trust/diff.js +92 -0
  209. package/src/template-trust/policy.js +61 -0
  210. package/src/template-trust/record.js +90 -0
  211. package/src/template-trust/status.js +182 -0
  212. package/src/template-trust.js +24 -687
  213. package/src/text-helpers.d.ts +1 -0
  214. package/src/topogram-types.d.ts +69 -0
  215. package/src/validator/common.js +488 -0
  216. package/src/validator/data-model.js +237 -0
  217. package/src/validator/docs.js +167 -0
  218. package/src/validator/expressions.js +146 -1
  219. package/src/validator/index.d.ts +23 -0
  220. package/src/validator/index.js +32 -3585
  221. package/src/validator/kinds.d.ts +41 -0
  222. package/src/validator/kinds.js +2 -0
  223. package/src/validator/model-helpers.js +46 -0
  224. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  225. package/src/validator/per-kind/bug.js +6 -0
  226. package/src/validator/per-kind/domain.js +15 -2
  227. package/src/validator/per-kind/pitch.js +7 -0
  228. package/src/validator/per-kind/requirement.js +5 -0
  229. package/src/validator/per-kind/task.js +7 -0
  230. package/src/validator/per-kind/widget.js +14 -0
  231. package/src/validator/projections/api-http-async.js +410 -0
  232. package/src/validator/projections/api-http-authz.js +88 -0
  233. package/src/validator/projections/api-http-core.js +205 -0
  234. package/src/validator/projections/api-http-policies.js +339 -0
  235. package/src/validator/projections/api-http-responses.js +233 -0
  236. package/src/validator/projections/api-http.js +44 -0
  237. package/src/validator/projections/db.js +353 -0
  238. package/src/validator/projections/generator-defaults.js +45 -0
  239. package/src/validator/projections/helpers.js +87 -0
  240. package/src/validator/projections/ui-helpers.js +214 -0
  241. package/src/validator/projections/ui-navigation.js +344 -0
  242. package/src/validator/projections/ui-structure.js +364 -0
  243. package/src/validator/projections/ui-widgets.js +493 -0
  244. package/src/validator/projections/ui.js +46 -0
  245. package/src/validator/registry.js +48 -1
  246. package/src/validator/utils.d.ts +20 -0
  247. package/src/validator/utils.js +115 -12
  248. package/src/widget-behavior.d.ts +1 -0
  249. package/src/workflows/import-app/api/collect.js +221 -0
  250. package/src/workflows/import-app/api/openapi.js +257 -0
  251. package/src/workflows/import-app/api/routes.js +327 -0
  252. package/src/workflows/import-app/api/sources.js +22 -0
  253. package/src/workflows/import-app/api.js +2 -797
  254. package/src/workflows/reconcile/adoption-plan/build.js +212 -0
  255. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  256. package/src/workflows/reconcile/adoption-plan/outputs.js +153 -0
  257. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  258. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  259. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  260. package/src/workflows/reconcile/adoption-plan.js +30 -740
  261. package/src/workflows/reconcile/auth/closures.js +115 -0
  262. package/src/workflows/reconcile/auth/formatters.js +142 -0
  263. package/src/workflows/reconcile/auth/inference.js +330 -0
  264. package/src/workflows/reconcile/auth/roles.js +122 -0
  265. package/src/workflows/reconcile/auth.js +35 -690
  266. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  267. package/src/workflows/reconcile/bundle-core.js +12 -598
  268. package/src/workflows/reconcile/candidate-model.js +18 -2
  269. package/src/workflows/reconcile/canonical-surface.js +1 -1
  270. package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
  271. package/src/workflows/reconcile/impacts/indexes.js +105 -0
  272. package/src/workflows/reconcile/impacts/patches.js +252 -0
  273. package/src/workflows/reconcile/impacts/reports.js +80 -0
  274. package/src/workflows/reconcile/impacts.js +14 -623
  275. package/src/workflows/reconcile/renderers.js +41 -6
  276. package/src/workflows/shared.js +5 -11
  277. package/src/workspace-docs.d.ts +29 -0
  278. package/src/workspace-paths.js +328 -0
@@ -338,7 +338,7 @@ function importAdoptMode(graph, options = {}) {
338
338
  write_scope: {
339
339
  safe_to_edit: ["candidates/**"],
340
340
  generator_owned: ["artifacts/**", "apps/**"],
341
- human_owned_review_required: ["topogram/**"],
341
+ human_owned_review_required: ["topo/**"],
342
342
  out_of_bounds: [".git/**", "node_modules/**"]
343
343
  },
344
344
  verification_targets: {
@@ -384,7 +384,7 @@ function diffReviewMode(graph, options = {}) {
384
384
  write_scope: {
385
385
  safe_to_edit: [],
386
386
  generator_owned: ["artifacts/**", "apps/**"],
387
- human_owned_review_required: ["topogram/**", "examples/maintained/proof-app/**"],
387
+ human_owned_review_required: ["topo/**", "examples/maintained/proof-app/**"],
388
388
  out_of_bounds: [".git/**", "node_modules/**"]
389
389
  },
390
390
  verification_targets: slice?.verification_targets || recommendedVerificationTargets(graph, [], {
@@ -0,0 +1,507 @@
1
+ // @ts-check
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { createRequire } from "node:module";
6
+ import { UI_GENERATOR_RENDERED_COMPONENT_PATTERNS } from "../../ui/taxonomy.js";
7
+
8
+ /**
9
+ * @typedef {Object} GeneratorManifest
10
+ * @property {string} id
11
+ * @property {string} version
12
+ * @property {"api"|"web"|"database"|"native"} surface
13
+ * @property {string[]} runtimeKinds
14
+ * @property {string[]} projectionTypes
15
+ * @property {string[]} inputs
16
+ * @property {string[]} outputs
17
+ * @property {Record<string, string>} stack
18
+ * @property {Record<string, boolean>} capabilities
19
+ * @property {{ patterns?: string[], behaviors?: string[], unsupported?: "error"|"warning"|"contract-only" }} [widgetSupport]
20
+ * @property {"bundled"|"package"} source
21
+ * @property {string} [profile]
22
+ * @property {string} [package]
23
+ * @property {string} [export]
24
+ * @property {boolean} [planned]
25
+ */
26
+
27
+ const RENDERED_COMPONENT_PATTERNS = [...UI_GENERATOR_RENDERED_COMPONENT_PATTERNS];
28
+
29
+ /** @type {GeneratorManifest[]} */
30
+ export const GENERATOR_MANIFESTS = [
31
+ {
32
+ id: "topogram/hono",
33
+ version: "1",
34
+ surface: "api",
35
+ runtimeKinds: ["api_service"],
36
+ projectionTypes: ["api_contract"],
37
+ inputs: ["server-contract", "api-contracts"],
38
+ outputs: ["api-service"],
39
+ stack: { runtime: "node", framework: "hono", language: "typescript" },
40
+ capabilities: { http: true, stateless: true, persistence: true },
41
+ source: "bundled",
42
+ profile: "hono"
43
+ },
44
+ {
45
+ id: "topogram/express",
46
+ version: "1",
47
+ surface: "api",
48
+ runtimeKinds: ["api_service"],
49
+ projectionTypes: ["api_contract"],
50
+ inputs: ["server-contract", "api-contracts"],
51
+ outputs: ["api-service"],
52
+ stack: { runtime: "node", framework: "express", language: "typescript" },
53
+ capabilities: { http: true, stateless: true, persistence: true },
54
+ source: "bundled",
55
+ profile: "express"
56
+ },
57
+ {
58
+ id: "topogram/vanilla-web",
59
+ version: "1",
60
+ surface: "web",
61
+ runtimeKinds: ["web_surface"],
62
+ projectionTypes: ["web_surface"],
63
+ inputs: ["ui-surface-contract"],
64
+ outputs: ["web-app", "generation-coverage"],
65
+ stack: { runtime: "browser", framework: "vanilla", language: "javascript" },
66
+ capabilities: { routes: true, components: false, coverage: true },
67
+ widgetSupport: { patterns: [], behaviors: [], unsupported: "contract-only" },
68
+ source: "bundled",
69
+ profile: "vanilla"
70
+ },
71
+ {
72
+ id: "topogram/sveltekit",
73
+ version: "1",
74
+ surface: "web",
75
+ runtimeKinds: ["web_surface"],
76
+ projectionTypes: ["web_surface"],
77
+ inputs: ["ui-surface-contract", "api-contracts"],
78
+ outputs: ["web-app", "generation-coverage"],
79
+ stack: { runtime: "node", framework: "sveltekit", language: "typescript" },
80
+ capabilities: { routes: true, components: true, coverage: true },
81
+ widgetSupport: {
82
+ patterns: RENDERED_COMPONENT_PATTERNS,
83
+ behaviors: ["selection", "sorting", "filtering", "search", "pagination", "bulk_action", "optimistic_update"],
84
+ unsupported: "warning"
85
+ },
86
+ source: "bundled",
87
+ profile: "sveltekit"
88
+ },
89
+ {
90
+ id: "topogram/react",
91
+ version: "1",
92
+ surface: "web",
93
+ runtimeKinds: ["web_surface"],
94
+ projectionTypes: ["web_surface"],
95
+ inputs: ["ui-surface-contract", "api-contracts"],
96
+ outputs: ["web-app", "generation-coverage"],
97
+ stack: { runtime: "browser", framework: "react", language: "typescript" },
98
+ capabilities: { routes: true, components: true, coverage: true },
99
+ widgetSupport: {
100
+ patterns: RENDERED_COMPONENT_PATTERNS,
101
+ behaviors: ["selection", "sorting", "filtering", "search", "pagination", "bulk_action", "optimistic_update"],
102
+ unsupported: "warning"
103
+ },
104
+ source: "bundled",
105
+ profile: "react"
106
+ },
107
+ {
108
+ id: "topogram/swiftui",
109
+ version: "1",
110
+ surface: "native",
111
+ runtimeKinds: ["ios_surface"],
112
+ projectionTypes: ["ios_surface"],
113
+ inputs: ["ui-surface-contract", "api-contracts"],
114
+ outputs: ["native-app"],
115
+ stack: { platform: "ios", framework: "swiftui", language: "swift" },
116
+ capabilities: { routes: true, components: false, coverage: false },
117
+ widgetSupport: { patterns: [], behaviors: [], unsupported: "contract-only" },
118
+ source: "bundled",
119
+ profile: "swiftui"
120
+ },
121
+ {
122
+ id: "topogram/postgres",
123
+ version: "1",
124
+ surface: "database",
125
+ runtimeKinds: ["database"],
126
+ projectionTypes: ["db_contract"],
127
+ inputs: ["db-contract", "db-lifecycle-plan"],
128
+ outputs: ["db-lifecycle-bundle", "sql-schema", "sql-migration", "prisma-schema", "drizzle-schema"],
129
+ stack: { database: "postgres", language: "sql" },
130
+ capabilities: { lifecycle: true, migrations: true, prisma: true, drizzle: true },
131
+ source: "bundled",
132
+ profile: "postgres"
133
+ },
134
+ {
135
+ id: "topogram/sqlite",
136
+ version: "1",
137
+ surface: "database",
138
+ runtimeKinds: ["database"],
139
+ projectionTypes: ["db_contract"],
140
+ inputs: ["db-contract", "db-lifecycle-plan"],
141
+ outputs: ["db-lifecycle-bundle", "sql-schema", "sql-migration", "prisma-schema"],
142
+ stack: { database: "sqlite", language: "sql" },
143
+ capabilities: { lifecycle: true, migrations: true, prisma: true, drizzle: false },
144
+ source: "bundled",
145
+ profile: "sqlite"
146
+ },
147
+ {
148
+ id: "topogram/android-compose",
149
+ version: "1",
150
+ surface: "native",
151
+ runtimeKinds: ["android_surface"],
152
+ projectionTypes: ["android_surface"],
153
+ inputs: ["ui-surface-contract", "api-contracts"],
154
+ outputs: ["native-app"],
155
+ stack: { platform: "android", framework: "compose", language: "kotlin" },
156
+ capabilities: { routes: true, components: false, coverage: false },
157
+ widgetSupport: { patterns: [], behaviors: [], unsupported: "contract-only" },
158
+ source: "bundled",
159
+ profile: "compose",
160
+ planned: true
161
+ }
162
+ ];
163
+
164
+ const GENERATOR_BY_ID = new Map(GENERATOR_MANIFESTS.map((manifest) => [manifest.id, manifest]));
165
+
166
+ /**
167
+ * @typedef {Object} GeneratorBinding
168
+ * @property {string} id
169
+ * @property {string} version
170
+ * @property {string} [package]
171
+ */
172
+
173
+ /**
174
+ * @typedef {Object} ResolvedGeneratorManifest
175
+ * @property {GeneratorManifest|null} manifest
176
+ * @property {string[]} errors
177
+ * @property {"bundled"|"package"|null} source
178
+ * @property {string|null} manifestPath
179
+ * @property {string|null} packageRoot
180
+ */
181
+
182
+ /**
183
+ * @param {any} value
184
+ * @param {boolean} [nonEmpty]
185
+ * @returns {boolean}
186
+ */
187
+ function isStringArray(value, nonEmpty = false) {
188
+ return Array.isArray(value) &&
189
+ (!nonEmpty || value.length > 0) &&
190
+ value.every((entry) => typeof entry === "string" && entry.length > 0);
191
+ }
192
+
193
+ /**
194
+ * @param {string} oldName
195
+ * @param {string} newName
196
+ * @param {string} example
197
+ * @returns {string}
198
+ */
199
+ function renameDiagnostic(oldName, newName, example) {
200
+ return `${oldName} was renamed to ${newName}. Example fix: ${example}`;
201
+ }
202
+
203
+ /**
204
+ * @param {string} generatorId
205
+ * @returns {GeneratorManifest|null}
206
+ */
207
+ export function getGeneratorManifest(generatorId) {
208
+ return GENERATOR_BY_ID.get(generatorId) || null;
209
+ }
210
+
211
+ /**
212
+ * @param {string|null|undefined} rootDir
213
+ * @returns {string}
214
+ */
215
+ function packageResolutionBase(rootDir) {
216
+ return path.join(rootDir || process.cwd(), "package.json");
217
+ }
218
+
219
+ /**
220
+ * @param {string|null|undefined} packageName
221
+ * @returns {string|null}
222
+ */
223
+ export function packageGeneratorInstallCommand(packageName) {
224
+ return packageName ? `npm install -D ${packageName}` : null;
225
+ }
226
+
227
+ /**
228
+ * @param {string|null|undefined} packageName
229
+ * @returns {string|null}
230
+ */
231
+ export function packageGeneratorInstallHint(packageName) {
232
+ const command = packageGeneratorInstallCommand(packageName);
233
+ return command ? `Install it from the project root with: ${command}` : null;
234
+ }
235
+
236
+ /**
237
+ * @param {string} packageName
238
+ * @param {string|null|undefined} rootDir
239
+ * @returns {{ manifestPath: string|null, packageRoot: string|null, error: string|null }}
240
+ */
241
+ export function resolvePackageGeneratorManifestPath(packageName, rootDir = process.cwd()) {
242
+ const requireFromRoot = createRequire(packageResolutionBase(rootDir));
243
+ try {
244
+ const manifestPath = requireFromRoot.resolve(`${packageName}/topogram-generator.json`);
245
+ return {
246
+ manifestPath,
247
+ packageRoot: path.dirname(manifestPath),
248
+ error: null
249
+ };
250
+ } catch (manifestError) {
251
+ try {
252
+ const packageJsonPath = requireFromRoot.resolve(`${packageName}/package.json`);
253
+ const packageRoot = path.dirname(packageJsonPath);
254
+ const manifestPath = path.join(packageRoot, "topogram-generator.json");
255
+ if (!fs.existsSync(manifestPath)) {
256
+ return {
257
+ manifestPath: null,
258
+ packageRoot,
259
+ error: `Generator package '${packageName}' is missing topogram-generator.json`
260
+ };
261
+ }
262
+ return {
263
+ manifestPath,
264
+ packageRoot,
265
+ error: null
266
+ };
267
+ } catch {
268
+ const detail = manifestError instanceof Error ? manifestError.message : String(manifestError);
269
+ const installHint = packageGeneratorInstallHint(packageName);
270
+ return {
271
+ manifestPath: null,
272
+ packageRoot: null,
273
+ error: `Generator package '${packageName}' could not be resolved from '${rootDir || process.cwd()}': ${detail}${installHint ? `. ${installHint}` : ""}`
274
+ };
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * @param {string} packageName
281
+ * @param {string|null|undefined} rootDir
282
+ * @returns {{ manifest: GeneratorManifest|null, errors: string[], manifestPath: string|null, packageRoot: string|null }}
283
+ */
284
+ export function loadPackageGeneratorManifest(packageName, rootDir = process.cwd()) {
285
+ const resolved = resolvePackageGeneratorManifestPath(packageName, rootDir);
286
+ if (!resolved.manifestPath) {
287
+ return {
288
+ manifest: null,
289
+ errors: [resolved.error || `Generator package '${packageName}' could not be resolved`],
290
+ manifestPath: null,
291
+ packageRoot: resolved.packageRoot
292
+ };
293
+ }
294
+ try {
295
+ const manifest = JSON.parse(fs.readFileSync(resolved.manifestPath, "utf8"));
296
+ const validation = validateGeneratorManifest(manifest);
297
+ return {
298
+ manifest: validation.ok ? manifest : null,
299
+ errors: validation.errors,
300
+ manifestPath: resolved.manifestPath,
301
+ packageRoot: resolved.packageRoot
302
+ };
303
+ } catch (error) {
304
+ return {
305
+ manifest: null,
306
+ errors: [`Generator package '${packageName}' manifest could not be read: ${error instanceof Error ? error.message : String(error)}`],
307
+ manifestPath: resolved.manifestPath,
308
+ packageRoot: resolved.packageRoot
309
+ };
310
+ }
311
+ }
312
+
313
+ /**
314
+ * @param {GeneratorBinding|string|null|undefined} bindingOrId
315
+ * @param {{ rootDir?: string|null, configDir?: string|null }} [options]
316
+ * @returns {ResolvedGeneratorManifest}
317
+ */
318
+ export function resolveGeneratorManifestForBinding(bindingOrId, options = {}) {
319
+ const binding = typeof bindingOrId === "string"
320
+ ? { id: bindingOrId, version: "" }
321
+ : bindingOrId;
322
+ const generatorId = binding?.id || "";
323
+ const bundled = getGeneratorManifest(generatorId);
324
+ if (bundled) {
325
+ return {
326
+ manifest: bundled,
327
+ errors: [],
328
+ source: "bundled",
329
+ manifestPath: null,
330
+ packageRoot: null
331
+ };
332
+ }
333
+ if (!binding?.package) {
334
+ return {
335
+ manifest: null,
336
+ errors: [`Generator '${generatorId || "unknown"}' is not bundled and does not declare a package`],
337
+ source: null,
338
+ manifestPath: null,
339
+ packageRoot: null
340
+ };
341
+ }
342
+ const rootDir = options.configDir || options.rootDir || process.cwd();
343
+ const loaded = loadPackageGeneratorManifest(binding.package, rootDir);
344
+ if (!loaded.manifest) {
345
+ return {
346
+ manifest: null,
347
+ errors: loaded.errors,
348
+ source: "package",
349
+ manifestPath: loaded.manifestPath,
350
+ packageRoot: loaded.packageRoot
351
+ };
352
+ }
353
+ /** @type {string[]} */
354
+ const errors = [];
355
+ if (loaded.manifest.source !== "package") {
356
+ errors.push(`Generator package '${binding.package}' manifest source must be package`);
357
+ }
358
+ if (loaded.manifest.package !== binding.package) {
359
+ errors.push(`Generator package '${binding.package}' manifest package must match '${binding.package}'`);
360
+ }
361
+ if (loaded.manifest.id !== binding.id) {
362
+ errors.push(`Generator package '${binding.package}' manifest id '${loaded.manifest.id}' does not match binding '${binding.id}'`);
363
+ }
364
+ if (binding.version && loaded.manifest.version !== binding.version) {
365
+ errors.push(`Generator package '${binding.package}' manifest version '${loaded.manifest.version}' does not match binding '${binding.version}'`);
366
+ }
367
+ return {
368
+ manifest: errors.length === 0 ? loaded.manifest : null,
369
+ errors,
370
+ source: "package",
371
+ manifestPath: loaded.manifestPath,
372
+ packageRoot: loaded.packageRoot
373
+ };
374
+ }
375
+
376
+ /**
377
+ * @param {string|undefined|null} generatorId
378
+ * @param {string|null} [fallback]
379
+ * @returns {string|null}
380
+ */
381
+ export function generatorProfile(generatorId, fallback = null) {
382
+ return generatorId ? getGeneratorManifest(generatorId)?.profile || fallback : fallback;
383
+ }
384
+
385
+ /**
386
+ * @param {any} manifest
387
+ * @returns {{ ok: boolean, errors: string[] }}
388
+ */
389
+ export function validateGeneratorManifest(manifest) {
390
+ /** @type {string[]} */
391
+ const errors = [];
392
+ const label = manifest?.id ? `Generator '${manifest.id}'` : "Generator manifest";
393
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
394
+ return { ok: false, errors: ["Generator manifest must be an object"] };
395
+ }
396
+ if (typeof manifest.id !== "string" || manifest.id.length === 0) {
397
+ errors.push(`${label} id must be a non-empty string`);
398
+ }
399
+ if (typeof manifest.version !== "string" || manifest.version.length === 0) {
400
+ errors.push(`${label} version must be a non-empty string`);
401
+ }
402
+ if (!["api", "web", "database", "native"].includes(manifest.surface)) {
403
+ errors.push(`${label} surface must be api, web, database, or native`);
404
+ }
405
+ if (manifest.targetKind != null) {
406
+ errors.push(`${label} ${renameDiagnostic("'targetKind'", "'runtimeKinds'", `"runtimeKinds": ["web_surface"]`)}`);
407
+ }
408
+ if (!isStringArray(manifest.runtimeKinds, true)) {
409
+ errors.push(`${label} runtimeKinds must be a non-empty string array`);
410
+ }
411
+ if (manifest["projectionPlatforms"] != null) {
412
+ errors.push(`${label} ${renameDiagnostic("'projectionPlatforms'", "'projectionTypes'", `"projectionTypes": ["web_surface"]`)}`);
413
+ }
414
+ if (!isStringArray(manifest.projectionTypes, true)) {
415
+ errors.push(`${label} projectionTypes must be a non-empty string array`);
416
+ }
417
+ if (!isStringArray(manifest.inputs)) {
418
+ errors.push(`${label} inputs must be a string array`);
419
+ }
420
+ if (!isStringArray(manifest.outputs)) {
421
+ errors.push(`${label} outputs must be a string array`);
422
+ }
423
+ if (!manifest.stack || typeof manifest.stack !== "object" || Array.isArray(manifest.stack)) {
424
+ errors.push(`${label} stack must be an object`);
425
+ }
426
+ if (!manifest.capabilities || typeof manifest.capabilities !== "object" || Array.isArray(manifest.capabilities)) {
427
+ errors.push(`${label} capabilities must be an object`);
428
+ }
429
+ if (manifest["componentSupport"] != null) {
430
+ errors.push(`${label} ${renameDiagnostic("'componentSupport'", "'widgetSupport'", `"widgetSupport": { "patterns": ["resource_table"] }`)}`);
431
+ }
432
+ if (manifest.widgetSupport != null) {
433
+ if (typeof manifest.widgetSupport !== "object" || Array.isArray(manifest.widgetSupport)) {
434
+ errors.push(`${label} widgetSupport must be an object when present`);
435
+ } else {
436
+ if (manifest.widgetSupport.patterns != null && !isStringArray(manifest.widgetSupport.patterns)) {
437
+ errors.push(`${label} widgetSupport.patterns must be a string array`);
438
+ }
439
+ if (manifest.widgetSupport.behaviors != null && !isStringArray(manifest.widgetSupport.behaviors)) {
440
+ errors.push(`${label} widgetSupport.behaviors must be a string array`);
441
+ }
442
+ if (
443
+ manifest.widgetSupport.unsupported != null &&
444
+ !["error", "warning", "contract-only"].includes(manifest.widgetSupport.unsupported)
445
+ ) {
446
+ errors.push(`${label} widgetSupport.unsupported must be error, warning, or contract-only`);
447
+ }
448
+ }
449
+ }
450
+ if (!["bundled", "package"].includes(manifest.source)) {
451
+ errors.push(`${label} source must be bundled or package`);
452
+ }
453
+ if (manifest.source === "package" && (typeof manifest.package !== "string" || manifest.package.length === 0)) {
454
+ errors.push(`${label} package source must include package`);
455
+ }
456
+ return { ok: errors.length === 0, errors };
457
+ }
458
+
459
+ /**
460
+ * @returns {{ ok: boolean, errors: string[] }}
461
+ */
462
+ export function validateGeneratorRegistry() {
463
+ const errors = [];
464
+ const seen = new Set();
465
+ for (const manifest of GENERATOR_MANIFESTS) {
466
+ const result = validateGeneratorManifest(manifest);
467
+ errors.push(...result.errors);
468
+ const key = `${manifest.id}@${manifest.version}`;
469
+ if (seen.has(key)) {
470
+ errors.push(`Duplicate generator manifest '${key}'`);
471
+ }
472
+ seen.add(key);
473
+ }
474
+ return { ok: errors.length === 0, errors };
475
+ }
476
+
477
+ /**
478
+ * @param {Record<string, any>|null|undefined} projection
479
+ * @returns {boolean}
480
+ */
481
+ export function isApiProjection(projection) {
482
+ return Array.isArray(projection?.http) && projection.http.length > 0;
483
+ }
484
+
485
+ /**
486
+ * @param {Record<string, any>|null|undefined} projection
487
+ * @returns {string}
488
+ */
489
+ export function projectionCompatibilityKey(projection) {
490
+ if (isApiProjection(projection)) {
491
+ return "api_contract";
492
+ }
493
+ return projection?.type || projection?.type || "";
494
+ }
495
+
496
+ /**
497
+ * @param {GeneratorManifest|null|undefined} manifest
498
+ * @param {string} runtimeKind
499
+ * @param {Record<string, any>|null|undefined} projection
500
+ * @returns {boolean}
501
+ */
502
+ export function isGeneratorCompatible(manifest, runtimeKind, projection) {
503
+ if (!manifest || manifest.planned || !manifest.runtimeKinds.includes(runtimeKind)) {
504
+ return false;
505
+ }
506
+ return manifest.projectionTypes.includes(projectionCompatibilityKey(projection));
507
+ }