@topogram/cli 0.3.63 → 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 (344) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +703 -0
  3. package/src/adoption/plan.d.ts +6 -0
  4. package/src/adoption/plan.js +12 -703
  5. package/src/adoption/reporting.d.ts +10 -0
  6. package/src/adoption/review-groups.d.ts +6 -0
  7. package/src/agent-brief.d.ts +3 -0
  8. package/src/agent-brief.js +495 -0
  9. package/src/agent-ops/query-builders/auth.js +375 -0
  10. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  12. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  13. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  14. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  15. package/src/agent-ops/query-builders/change-risk.js +25 -0
  16. package/src/agent-ops/query-builders/common.js +149 -0
  17. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  18. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  19. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  20. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  21. package/src/agent-ops/query-builders/work-packets.js +417 -0
  22. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  23. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  24. package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
  25. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  26. package/src/agent-ops/query-builders.d.ts +26 -0
  27. package/src/agent-ops/query-builders.js +42 -5021
  28. package/src/archive/archive.d.ts +2 -0
  29. package/src/archive/compact.d.ts +1 -0
  30. package/src/archive/unarchive.d.ts +1 -0
  31. package/src/catalog/constants.js +10 -0
  32. package/src/catalog/copy.js +60 -0
  33. package/src/catalog/diagnostics.js +15 -0
  34. package/src/catalog/entries.js +42 -0
  35. package/src/catalog/files.js +67 -0
  36. package/src/catalog/provenance.js +122 -0
  37. package/src/catalog/source.js +150 -0
  38. package/src/catalog/validation.js +252 -0
  39. package/src/catalog.d.ts +12 -0
  40. package/src/catalog.js +18 -750
  41. package/src/cli/catalog-alias.d.ts +1 -0
  42. package/src/cli/command-parser.js +38 -0
  43. package/src/cli/command-parsers/core.js +102 -0
  44. package/src/cli/command-parsers/generator.js +39 -0
  45. package/src/cli/command-parsers/import.js +44 -0
  46. package/src/cli/command-parsers/legacy-workflow.js +21 -0
  47. package/src/cli/command-parsers/project.js +47 -0
  48. package/src/cli/command-parsers/sdlc.js +47 -0
  49. package/src/cli/command-parsers/shared.js +51 -0
  50. package/src/cli/command-parsers/template.js +48 -0
  51. package/src/cli/commands/agent.js +47 -0
  52. package/src/cli/commands/catalog/check.js +31 -0
  53. package/src/cli/commands/catalog/copy.js +59 -0
  54. package/src/cli/commands/catalog/doctor.js +248 -0
  55. package/src/cli/commands/catalog/help.js +21 -0
  56. package/src/cli/commands/catalog/list.js +52 -0
  57. package/src/cli/commands/catalog/runner.js +92 -0
  58. package/src/cli/commands/catalog/shared.js +17 -0
  59. package/src/cli/commands/catalog/show.js +134 -0
  60. package/src/cli/commands/catalog.js +32 -0
  61. package/src/cli/commands/check.js +268 -0
  62. package/src/cli/commands/doctor.js +268 -0
  63. package/src/cli/commands/emit.js +149 -0
  64. package/src/cli/commands/generate.js +96 -0
  65. package/src/cli/commands/generator-policy/package-info.js +162 -0
  66. package/src/cli/commands/generator-policy/payloads.js +372 -0
  67. package/src/cli/commands/generator-policy/printers.js +159 -0
  68. package/src/cli/commands/generator-policy/runner.js +81 -0
  69. package/src/cli/commands/generator-policy/shared.js +39 -0
  70. package/src/cli/commands/generator-policy.js +17 -0
  71. package/src/cli/commands/generator.js +443 -0
  72. package/src/cli/commands/import/adopt.js +170 -0
  73. package/src/cli/commands/import/check.js +91 -0
  74. package/src/cli/commands/import/diff.js +84 -0
  75. package/src/cli/commands/import/help.js +47 -0
  76. package/src/cli/commands/import/paths.js +277 -0
  77. package/src/cli/commands/import/plan.js +284 -0
  78. package/src/cli/commands/import/refresh.js +470 -0
  79. package/src/cli/commands/import/status-history.js +196 -0
  80. package/src/cli/commands/import/workspace.js +230 -0
  81. package/src/cli/commands/import-runner.js +157 -0
  82. package/src/cli/commands/import.js +35 -0
  83. package/src/cli/commands/inspect.js +55 -0
  84. package/src/cli/commands/new.js +94 -0
  85. package/src/cli/commands/package/constants.js +17 -0
  86. package/src/cli/commands/package/doctor.js +240 -0
  87. package/src/cli/commands/package/help.js +27 -0
  88. package/src/cli/commands/package/lockfile.js +135 -0
  89. package/src/cli/commands/package/npm.js +97 -0
  90. package/src/cli/commands/package/reporting.js +35 -0
  91. package/src/cli/commands/package/runner.js +33 -0
  92. package/src/cli/commands/package/shared.js +9 -0
  93. package/src/cli/commands/package/update-cli.js +252 -0
  94. package/src/cli/commands/package/versions.js +35 -0
  95. package/src/cli/commands/package.js +31 -0
  96. package/src/cli/commands/query/change-plan.js +68 -0
  97. package/src/cli/commands/query/definitions.js +202 -0
  98. package/src/cli/commands/query/import-adopt.js +121 -0
  99. package/src/cli/commands/query/runner/artifacts.js +102 -0
  100. package/src/cli/commands/query/runner/boundaries.js +211 -0
  101. package/src/cli/commands/query/runner/change.js +182 -0
  102. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  103. package/src/cli/commands/query/runner/index.js +31 -0
  104. package/src/cli/commands/query/runner/output.js +12 -0
  105. package/src/cli/commands/query/runner/workflow.js +241 -0
  106. package/src/cli/commands/query/runner.js +3 -0
  107. package/src/cli/commands/query/workflow-context.js +5 -0
  108. package/src/cli/commands/query/workspace.js +274 -0
  109. package/src/cli/commands/query.js +11 -0
  110. package/src/cli/commands/release-rollout.js +257 -0
  111. package/src/cli/commands/release-shared.js +528 -0
  112. package/src/cli/commands/release-status.js +429 -0
  113. package/src/cli/commands/release.js +107 -0
  114. package/src/cli/commands/sdlc.js +168 -0
  115. package/src/cli/commands/setup.js +76 -0
  116. package/src/cli/commands/source.js +291 -0
  117. package/src/cli/commands/template/baseline.js +100 -0
  118. package/src/cli/commands/template/check.js +466 -0
  119. package/src/cli/commands/template/constants.js +8 -0
  120. package/src/cli/commands/template/diagnostics.js +26 -0
  121. package/src/cli/commands/template/help.js +28 -0
  122. package/src/cli/commands/template/lifecycle.js +404 -0
  123. package/src/cli/commands/template/list-show.js +287 -0
  124. package/src/cli/commands/template/policy.js +422 -0
  125. package/src/cli/commands/template/shared.js +127 -0
  126. package/src/cli/commands/template/updates.js +352 -0
  127. package/src/cli/commands/template-runner.js +198 -0
  128. package/src/cli/commands/template.js +43 -0
  129. package/src/cli/commands/trust.js +219 -0
  130. package/src/cli/commands/version.js +40 -0
  131. package/src/cli/commands/widget.js +168 -0
  132. package/src/cli/commands/workflow.js +63 -0
  133. package/src/cli/dispatcher.js +392 -0
  134. package/src/cli/help-dispatch.js +188 -0
  135. package/src/cli/help.js +296 -0
  136. package/src/cli/migration-guidance.js +59 -0
  137. package/src/cli/options.js +96 -0
  138. package/src/cli/output-safety.js +107 -0
  139. package/src/cli/path-normalization.js +29 -0
  140. package/src/cli.js +47 -11711
  141. package/src/example-implementation.d.ts +2 -0
  142. package/src/format.d.ts +1 -0
  143. package/src/generator/api/contracts.js +497 -0
  144. package/src/generator/api/metadata.js +221 -0
  145. package/src/generator/api/openapi.js +559 -0
  146. package/src/generator/api/schema.js +124 -0
  147. package/src/generator/api/types.d.ts +98 -0
  148. package/src/generator/api.js +3 -1195
  149. package/src/generator/check.d.ts +1 -0
  150. package/src/generator/context/bundle.d.ts +1 -0
  151. package/src/generator/context/shared/domain-sdlc.js +282 -0
  152. package/src/generator/context/shared/maintained-boundary.js +665 -0
  153. package/src/generator/context/shared/metrics.js +85 -0
  154. package/src/generator/context/shared/primitives.js +64 -0
  155. package/src/generator/context/shared/relationships.js +453 -0
  156. package/src/generator/context/shared/summaries.js +263 -0
  157. package/src/generator/context/shared/types.d.ts +207 -0
  158. package/src/generator/context/shared.d.ts +44 -0
  159. package/src/generator/context/shared.js +80 -1390
  160. package/src/generator/context/slice/core.js +397 -0
  161. package/src/generator/context/slice/sdlc.js +417 -0
  162. package/src/generator/context/slice/ui-packets.js +183 -0
  163. package/src/generator/context/slice.js +2 -859
  164. package/src/generator/native/parity-bundle.js +2 -1
  165. package/src/generator/registry/index.js +507 -0
  166. package/src/generator/registry.js +18 -504
  167. package/src/generator/runtime/environment/index.js +666 -0
  168. package/src/generator/runtime/environment.js +4 -666
  169. package/src/generator/runtime/runtime-check/index.js +554 -0
  170. package/src/generator/runtime/runtime-check.js +4 -554
  171. package/src/generator/runtime/shared/index.js +572 -0
  172. package/src/generator/runtime/shared.js +19 -570
  173. package/src/generator/shared.d.ts +2 -0
  174. package/src/generator/surfaces/shared.d.ts +3 -0
  175. package/src/generator/surfaces/web/html-escape.js +22 -0
  176. package/src/generator/surfaces/web/react.js +10 -8
  177. package/src/generator/surfaces/web/sveltekit.js +7 -5
  178. package/src/generator/surfaces/web/vanilla.js +8 -4
  179. package/src/generator/widget-conformance/behavior-report.js +258 -0
  180. package/src/generator/widget-conformance/checks.js +371 -0
  181. package/src/generator/widget-conformance/projection-context.js +200 -0
  182. package/src/generator/widget-conformance/report.js +166 -0
  183. package/src/generator/widget-conformance/types.d.ts +121 -0
  184. package/src/generator/widget-conformance.js +3 -824
  185. package/src/generator.d.ts +2 -0
  186. package/src/github-client.js +520 -0
  187. package/src/import/core/context.d.ts +3 -0
  188. package/src/import/core/contracts.d.ts +1 -0
  189. package/src/import/core/registry.d.ts +4 -0
  190. package/src/import/core/runner/candidates.js +217 -0
  191. package/src/import/core/runner/options.js +22 -0
  192. package/src/import/core/runner/reports.js +50 -0
  193. package/src/import/core/runner/run.js +79 -0
  194. package/src/import/core/runner/tracks.js +150 -0
  195. package/src/import/core/runner/ui-drafts.js +337 -0
  196. package/src/import/core/runner.js +3 -698
  197. package/src/import/core/shared/api-routes.js +221 -0
  198. package/src/import/core/shared/candidates.js +97 -0
  199. package/src/import/core/shared/files.js +177 -0
  200. package/src/import/core/shared/next-app.js +389 -0
  201. package/src/import/core/shared/types.d.ts +51 -0
  202. package/src/import/core/shared/ui-routes.js +230 -0
  203. package/src/import/core/shared.js +67 -910
  204. package/src/import/extractors/api/flutter-dio.js +4 -8
  205. package/src/import/extractors/api/react-native-repository.js +4 -8
  206. package/src/import/index.d.ts +4 -0
  207. package/src/import/provenance.d.ts +4 -0
  208. package/src/new-project/constants.js +128 -0
  209. package/src/new-project/create.js +83 -0
  210. package/src/new-project/json.js +28 -0
  211. package/src/new-project/metadata.js +96 -0
  212. package/src/new-project/package-spec.js +161 -0
  213. package/src/new-project/project-files.js +348 -0
  214. package/src/new-project/template-policy.js +269 -0
  215. package/src/new-project/template-resolution.js +368 -0
  216. package/src/new-project/template-snapshots.js +430 -0
  217. package/src/new-project/template-updates.js +512 -0
  218. package/src/new-project/types.d.ts +83 -0
  219. package/src/new-project.js +6 -2188
  220. package/src/npm-safety.js +79 -0
  221. package/src/parser.d.ts +87 -0
  222. package/src/parser.js +118 -0
  223. package/src/path-helpers.d.ts +1 -0
  224. package/src/path-helpers.js +20 -0
  225. package/src/policy/review-boundaries.d.ts +15 -0
  226. package/src/project-config/index.js +564 -0
  227. package/src/project-config.js +19 -560
  228. package/src/reconcile/docs.d.ts +8 -0
  229. package/src/reconcile/journeys.d.ts +1 -0
  230. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  231. package/src/resolver/enrich/bug.js +2 -0
  232. package/src/resolver/enrich/pitch.js +2 -0
  233. package/src/resolver/enrich/requirement.js +2 -0
  234. package/src/resolver/enrich/task.js +2 -0
  235. package/src/resolver/index.js +19 -2089
  236. package/src/resolver/normalize.js +384 -1
  237. package/src/resolver/plans.js +168 -0
  238. package/src/resolver/projections-api.js +494 -0
  239. package/src/resolver/projections-db.js +133 -0
  240. package/src/resolver/projections-ui.js +317 -0
  241. package/src/resolver/shapes.js +251 -0
  242. package/src/resolver/shared.js +278 -0
  243. package/src/resolver/widgets.js +132 -0
  244. package/src/resolver.d.ts +1 -0
  245. package/src/runtime-support.js +29 -0
  246. package/src/sdlc/adopt.d.ts +1 -0
  247. package/src/sdlc/check.d.ts +1 -0
  248. package/src/sdlc/explain.d.ts +1 -0
  249. package/src/sdlc/release.d.ts +1 -0
  250. package/src/sdlc/scaffold.d.ts +1 -0
  251. package/src/sdlc/transition.d.ts +1 -0
  252. package/src/template-trust/constants.js +62 -0
  253. package/src/template-trust/content.js +258 -0
  254. package/src/template-trust/diff.js +92 -0
  255. package/src/template-trust/policy.js +61 -0
  256. package/src/template-trust/record.js +90 -0
  257. package/src/template-trust/status.js +182 -0
  258. package/src/template-trust.js +24 -687
  259. package/src/text-helpers.d.ts +7 -0
  260. package/src/text-helpers.js +245 -0
  261. package/src/topogram-config.js +306 -0
  262. package/src/topogram-types.d.ts +69 -0
  263. package/src/validator/common.js +488 -0
  264. package/src/validator/data-model.js +237 -0
  265. package/src/validator/docs.js +167 -0
  266. package/src/validator/expressions.js +146 -1
  267. package/src/validator/index.d.ts +23 -0
  268. package/src/validator/index.js +32 -3585
  269. package/src/validator/kinds.d.ts +41 -0
  270. package/src/validator/kinds.js +2 -0
  271. package/src/validator/model-helpers.js +46 -0
  272. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  273. package/src/validator/per-kind/bug.js +6 -0
  274. package/src/validator/per-kind/domain.js +15 -2
  275. package/src/validator/per-kind/pitch.js +7 -0
  276. package/src/validator/per-kind/requirement.js +5 -0
  277. package/src/validator/per-kind/task.js +7 -0
  278. package/src/validator/per-kind/widget.js +14 -0
  279. package/src/validator/projections/api-http-async.js +410 -0
  280. package/src/validator/projections/api-http-authz.js +88 -0
  281. package/src/validator/projections/api-http-core.js +205 -0
  282. package/src/validator/projections/api-http-policies.js +339 -0
  283. package/src/validator/projections/api-http-responses.js +233 -0
  284. package/src/validator/projections/api-http.js +44 -0
  285. package/src/validator/projections/db.js +353 -0
  286. package/src/validator/projections/generator-defaults.js +45 -0
  287. package/src/validator/projections/helpers.js +87 -0
  288. package/src/validator/projections/ui-helpers.js +214 -0
  289. package/src/validator/projections/ui-navigation.js +344 -0
  290. package/src/validator/projections/ui-structure.js +364 -0
  291. package/src/validator/projections/ui-widgets.js +493 -0
  292. package/src/validator/projections/ui.js +46 -0
  293. package/src/validator/registry.js +48 -1
  294. package/src/validator/utils.d.ts +20 -0
  295. package/src/validator/utils.js +115 -12
  296. package/src/validator.d.ts +2 -0
  297. package/src/widget-behavior.d.ts +1 -0
  298. package/src/workflows/adoption/index.js +26 -0
  299. package/src/workflows/docs-generate.js +262 -0
  300. package/src/workflows/docs-scan.js +703 -0
  301. package/src/workflows/docs.js +15 -0
  302. package/src/workflows/import-app/api/collect.js +221 -0
  303. package/src/workflows/import-app/api/openapi.js +257 -0
  304. package/src/workflows/import-app/api/routes.js +327 -0
  305. package/src/workflows/import-app/api/sources.js +22 -0
  306. package/src/workflows/import-app/api.js +4 -0
  307. package/src/workflows/import-app/db.js +538 -0
  308. package/src/workflows/import-app/index.js +30 -0
  309. package/src/workflows/import-app/shared.js +218 -0
  310. package/src/workflows/import-app/ui.js +443 -0
  311. package/src/workflows/import-app/workflow.js +159 -0
  312. package/src/workflows/reconcile/adoption-plan/build.js +208 -0
  313. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  314. package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
  315. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  316. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  317. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  318. package/src/workflows/reconcile/adoption-plan.js +32 -0
  319. package/src/workflows/reconcile/auth/closures.js +115 -0
  320. package/src/workflows/reconcile/auth/formatters.js +142 -0
  321. package/src/workflows/reconcile/auth/inference.js +330 -0
  322. package/src/workflows/reconcile/auth/roles.js +122 -0
  323. package/src/workflows/reconcile/auth.js +37 -0
  324. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  325. package/src/workflows/reconcile/bundle-core.js +14 -0
  326. package/src/workflows/reconcile/bundle-shared.js +75 -0
  327. package/src/workflows/reconcile/candidate-model.js +477 -0
  328. package/src/workflows/reconcile/canonical-surface.js +264 -0
  329. package/src/workflows/reconcile/gap-report.js +333 -0
  330. package/src/workflows/reconcile/ids.js +6 -0
  331. package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
  332. package/src/workflows/reconcile/impacts/indexes.js +101 -0
  333. package/src/workflows/reconcile/impacts/patches.js +252 -0
  334. package/src/workflows/reconcile/impacts/reports.js +80 -0
  335. package/src/workflows/reconcile/impacts.js +16 -0
  336. package/src/workflows/reconcile/index.js +7 -0
  337. package/src/workflows/reconcile/renderers.js +461 -0
  338. package/src/workflows/reconcile/summary.js +90 -0
  339. package/src/workflows/reconcile/workflow.js +309 -0
  340. package/src/workflows/shared.js +189 -0
  341. package/src/workflows/types.d.ts +93 -0
  342. package/src/workflows.d.ts +1 -0
  343. package/src/workflows.js +10 -7652
  344. package/src/workspace-docs.d.ts +29 -0
@@ -0,0 +1,371 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ byId,
5
+ projectionContextRealizesIds,
6
+ projectionRegionKeys,
7
+ projectionScreenMap,
8
+ widgetContract
9
+ } from "./projection-context.js";
10
+
11
+ /**
12
+ * @param {any} arg1
13
+ * @returns {any}
14
+ */
15
+ export function checkRecord(arg1) {
16
+ const {
17
+ code,
18
+ severity,
19
+ message,
20
+ projection,
21
+ sourceProjection,
22
+ widget,
23
+ usage,
24
+ prop = null,
25
+ event = null,
26
+ behavior = null,
27
+ suggestedFix
28
+ } = arg1;
29
+ return {
30
+ code,
31
+ severity,
32
+ message,
33
+ projection: projection?.id || null,
34
+ source_projection: sourceProjection?.id || null,
35
+ widget: widget?.id || usage.widget?.id || null,
36
+ screen: usage.screenId || null,
37
+ region: usage.region || null,
38
+ prop,
39
+ event,
40
+ behavior,
41
+ suggested_fix: suggestedFix
42
+ };
43
+ }
44
+
45
+ /**
46
+ * @param {import("./types.d.ts").WidgetProjection} projection
47
+ * @param {import("./types.d.ts").WidgetProjection} sourceProjection
48
+ * @param {import("./types.d.ts").WidgetUsage} usage
49
+ * @param {any} index
50
+ * @returns {any}
51
+ */
52
+ export function widgetUsageKey(projection, sourceProjection, usage, index) {
53
+ return [
54
+ projection.id,
55
+ sourceProjection?.id || projection.id,
56
+ usage.screenId || "screen",
57
+ usage.region || "region",
58
+ usage.widget?.id || "widget",
59
+ String(index)
60
+ ].join(":");
61
+ }
62
+
63
+ /**
64
+ * @param {any} arg1
65
+ * @returns {any}
66
+ */
67
+ export function collectUsageChecks(arg1) {
68
+ const { graph, projection, sourceProjection, usage, widget } = arg1;
69
+ const checks = /** @type {any[]} */ ([]);
70
+ const contract = widgetContract(widget);
71
+ const props = contract?.props || [];
72
+ const events = contract?.events || [];
73
+ const propNames = new Set(props.map(/** @param {any} prop */ (prop) => prop.name));
74
+ const eventNames = new Set(events.map(/** @param {any} event */ (event) => event.id));
75
+ const boundProps = new Set((usage.dataBindings || []).map(/** @param {any} binding */ (binding) => binding.prop).filter(Boolean));
76
+ const statements = byId(graph.statements || []);
77
+ const screens = projectionScreenMap(graph, sourceProjection);
78
+ const regionKeys = projectionRegionKeys(graph, sourceProjection);
79
+ const realizedIds = projectionContextRealizesIds(graph, sourceProjection);
80
+
81
+ if (!widget) {
82
+ checks.push(checkRecord({
83
+ code: "widget_missing",
84
+ severity: "error",
85
+ message: `Widget '${usage.widget?.id || "(missing)"}' could not be resolved.`,
86
+ projection,
87
+ sourceProjection,
88
+ widget,
89
+ usage,
90
+ suggestedFix: "Create the widget or update the projection widget_bindings binding."
91
+ }));
92
+ return checks;
93
+ }
94
+
95
+ if (projection.status === "active" && widget.status && widget.status !== "active") {
96
+ checks.push(checkRecord({
97
+ code: "widget_status_not_active",
98
+ severity: "warning",
99
+ message: `Active projection '${projection.id}' uses widget '${widget.id}' with status '${widget.status}'.`,
100
+ projection,
101
+ sourceProjection,
102
+ widget,
103
+ usage,
104
+ suggestedFix: "Promote the widget to active or move the usage behind an explicit review boundary."
105
+ }));
106
+ }
107
+
108
+ if (!screens.has(usage.screenId)) {
109
+ checks.push(checkRecord({
110
+ code: "widget_usage_screen_missing",
111
+ severity: "error",
112
+ message: `Widget usage references missing screen '${usage.screenId}'.`,
113
+ projection,
114
+ sourceProjection,
115
+ widget,
116
+ usage,
117
+ suggestedFix: "Add the screen to screens or update the widget usage screen id."
118
+ }));
119
+ }
120
+
121
+ if (!regionKeys.has(`${usage.screenId}:${usage.region}`)) {
122
+ checks.push(checkRecord({
123
+ code: "widget_usage_region_missing",
124
+ severity: "error",
125
+ message: `Widget usage references undeclared region '${usage.region}' on screen '${usage.screenId}'.`,
126
+ projection,
127
+ sourceProjection,
128
+ widget,
129
+ usage,
130
+ suggestedFix: "Add the region to screen_regions or update the widget usage region."
131
+ }));
132
+ }
133
+
134
+ for (const prop of props.filter(/** @param {any} entry */ (entry) => entry.requiredness === "required")) {
135
+ if (!boundProps.has(prop.name)) {
136
+ checks.push(checkRecord({
137
+ code: "widget_required_prop_missing",
138
+ severity: "error",
139
+ message: `Required prop '${prop.name}' is not bound for widget '${widget.id}'.`,
140
+ projection,
141
+ sourceProjection,
142
+ widget,
143
+ usage,
144
+ prop: prop.name,
145
+ suggestedFix: `Add 'data ${prop.name} from <source>' to the projection widget_bindings entry.`
146
+ }));
147
+ }
148
+ }
149
+
150
+ for (const binding of usage.dataBindings || []) {
151
+ if (!propNames.has(binding.prop)) {
152
+ checks.push(checkRecord({
153
+ code: "widget_prop_unknown",
154
+ severity: "error",
155
+ message: `Prop '${binding.prop}' is not declared by widget '${widget.id}'.`,
156
+ projection,
157
+ sourceProjection,
158
+ widget,
159
+ usage,
160
+ prop: binding.prop || null,
161
+ suggestedFix: "Declare the prop on the widget or update the projection binding."
162
+ }));
163
+ }
164
+ if (!binding.source?.id || !statements.has(binding.source.id)) {
165
+ checks.push(checkRecord({
166
+ code: "widget_data_source_missing",
167
+ severity: "error",
168
+ message: `Data binding for prop '${binding.prop}' references a missing source.`,
169
+ projection,
170
+ sourceProjection,
171
+ widget,
172
+ usage,
173
+ prop: binding.prop || null,
174
+ suggestedFix: "Bind the prop to an existing capability, projection, shape, or entity."
175
+ }));
176
+ }
177
+ }
178
+
179
+ for (const binding of usage.eventBindings || []) {
180
+ if (!eventNames.has(binding.event)) {
181
+ checks.push(checkRecord({
182
+ code: "widget_event_unknown",
183
+ severity: "error",
184
+ message: `Event '${binding.event}' is not declared by widget '${widget.id}'.`,
185
+ projection,
186
+ sourceProjection,
187
+ widget,
188
+ usage,
189
+ event: binding.event || null,
190
+ suggestedFix: "Declare the event on the widget or update the projection binding."
191
+ }));
192
+ }
193
+ if (binding.action === "navigate") {
194
+ if (!screens.has(binding.target?.id)) {
195
+ checks.push(checkRecord({
196
+ code: "widget_event_navigation_target_missing",
197
+ severity: "error",
198
+ message: `Event '${binding.event}' navigates to missing screen '${binding.target?.id || "(missing)"}'.`,
199
+ projection,
200
+ sourceProjection,
201
+ widget,
202
+ usage,
203
+ event: binding.event || null,
204
+ suggestedFix: "Add the target screen or update the event navigation target."
205
+ }));
206
+ }
207
+ } else if (binding.action === "action") {
208
+ const target = binding.target?.id ? statements.get(binding.target.id) : null;
209
+ if (!target || target.kind !== "capability") {
210
+ checks.push(checkRecord({
211
+ code: "widget_event_action_missing",
212
+ severity: "error",
213
+ message: `Event '${binding.event}' targets missing capability '${binding.target?.id || "(missing)"}'.`,
214
+ projection,
215
+ sourceProjection,
216
+ widget,
217
+ usage,
218
+ event: binding.event || null,
219
+ suggestedFix: "Bind the event to an existing capability."
220
+ }));
221
+ } else if (!realizedIds.has(target.id)) {
222
+ checks.push(checkRecord({
223
+ code: "widget_event_action_not_in_projection",
224
+ severity: "error",
225
+ message: `Event '${binding.event}' targets capability '${target.id}', but projection '${sourceProjection.id}' does not realize it through its UI context.`,
226
+ projection,
227
+ sourceProjection,
228
+ widget,
229
+ usage,
230
+ event: binding.event || null,
231
+ suggestedFix: `Add '${target.id}' to projection '${sourceProjection.id}' or an inherited shared projection realizes list, or choose a capability already in this projection context.`
232
+ }));
233
+ }
234
+ } else {
235
+ checks.push(checkRecord({
236
+ code: "widget_event_action_unsupported",
237
+ severity: "error",
238
+ message: `Event '${binding.event}' uses unsupported action '${binding.action}'.`,
239
+ projection,
240
+ sourceProjection,
241
+ widget,
242
+ usage,
243
+ event: binding.event || null,
244
+ suggestedFix: "Use 'navigate' or 'action'."
245
+ }));
246
+ }
247
+ }
248
+
249
+ for (const behavior of contract?.behaviors || []) {
250
+ const stateProp = behavior.directives?.state;
251
+ if (stateProp && !propNames.has(stateProp)) {
252
+ checks.push(checkRecord({
253
+ code: "widget_behavior_prop_missing",
254
+ severity: "error",
255
+ message: `Behavior '${behavior.kind}' references missing prop '${stateProp}'.`,
256
+ projection,
257
+ sourceProjection,
258
+ widget,
259
+ usage,
260
+ prop: stateProp,
261
+ behavior: behavior.kind,
262
+ suggestedFix: "Update the behavior directive or declare the referenced prop."
263
+ }));
264
+ }
265
+ const emits = Array.isArray(behavior.directives?.emits)
266
+ ? behavior.directives.emits
267
+ : [behavior.directives?.emits].filter(Boolean);
268
+ for (const eventName of emits) {
269
+ if (!eventNames.has(eventName)) {
270
+ checks.push(checkRecord({
271
+ code: "widget_behavior_event_missing",
272
+ severity: "error",
273
+ message: `Behavior '${behavior.kind}' references missing event '${eventName}'.`,
274
+ projection,
275
+ sourceProjection,
276
+ widget,
277
+ usage,
278
+ event: eventName,
279
+ behavior: behavior.kind,
280
+ suggestedFix: "Update the behavior directive or declare the referenced event."
281
+ }));
282
+ continue;
283
+ }
284
+ if (!(usage.eventBindings || []).some(/** @param {any} binding */ (binding) => binding.event === eventName)) {
285
+ checks.push(checkRecord({
286
+ code: "widget_behavior_event_unbound",
287
+ severity: "warning",
288
+ message: `Behavior '${behavior.kind}' emits event '${eventName}', but this projection usage does not bind that event to navigation or an action.`,
289
+ projection,
290
+ sourceProjection,
291
+ widget,
292
+ usage,
293
+ event: eventName,
294
+ behavior: behavior.kind,
295
+ suggestedFix: `Add 'event ${eventName} navigate <screen>' or 'event ${eventName} action <capability>' to the projection widget_bindings entry.`
296
+ }));
297
+ }
298
+ }
299
+ const declaredActions = [
300
+ ...(Array.isArray(behavior.directives?.actions) ? behavior.directives.actions : [behavior.directives?.actions].filter(Boolean)),
301
+ ...(Array.isArray(behavior.directives?.submit) ? behavior.directives.submit : [behavior.directives?.submit].filter(Boolean))
302
+ ];
303
+ for (const actionTarget of declaredActions) {
304
+ if (eventNames.has(actionTarget)) {
305
+ if (!(usage.eventBindings || []).some(/** @param {any} binding */ (binding) => binding.event === actionTarget)) {
306
+ checks.push(checkRecord({
307
+ code: "widget_behavior_action_unbound",
308
+ severity: "warning",
309
+ message: `Behavior '${behavior.kind}' declares action event '${actionTarget}', but this projection usage does not bind that event to navigation or an action.`,
310
+ projection,
311
+ sourceProjection,
312
+ widget,
313
+ usage,
314
+ event: actionTarget,
315
+ behavior: behavior.kind,
316
+ suggestedFix: `Add 'event ${actionTarget} action <capability>' or 'event ${actionTarget} navigate <screen>' to the projection widget_bindings entry.`
317
+ }));
318
+ }
319
+ continue;
320
+ }
321
+
322
+ const target = statements.get(actionTarget);
323
+ if (!target || target.kind !== "capability") {
324
+ checks.push(checkRecord({
325
+ code: "widget_behavior_action_missing",
326
+ severity: "error",
327
+ message: `Behavior '${behavior.kind}' references missing capability action '${actionTarget}'.`,
328
+ projection,
329
+ sourceProjection,
330
+ widget,
331
+ usage,
332
+ behavior: behavior.kind,
333
+ suggestedFix: "Update the behavior directive or declare the referenced capability."
334
+ }));
335
+ continue;
336
+ }
337
+ if (!realizedIds.has(actionTarget)) {
338
+ checks.push(checkRecord({
339
+ code: "widget_behavior_action_not_in_projection",
340
+ severity: "error",
341
+ message: `Behavior '${behavior.kind}' references capability '${actionTarget}', but projection '${sourceProjection.id}' does not realize it.`,
342
+ projection,
343
+ sourceProjection,
344
+ widget,
345
+ usage,
346
+ behavior: behavior.kind,
347
+ suggestedFix: `Add '${actionTarget}' to projection '${sourceProjection.id}' realizes or choose a capability already in this projection context.`
348
+ }));
349
+ }
350
+ if (!(usage.eventBindings || []).some(/** @param {any} binding */ (binding) =>
351
+ binding.action === "action" &&
352
+ binding.target?.id === actionTarget &&
353
+ binding.target?.kind === "capability"
354
+ )) {
355
+ checks.push(checkRecord({
356
+ code: "widget_behavior_action_unbound",
357
+ severity: "warning",
358
+ message: `Behavior '${behavior.kind}' declares capability action '${actionTarget}', but this projection usage does not bind any widget event to that capability.`,
359
+ projection,
360
+ sourceProjection,
361
+ widget,
362
+ usage,
363
+ behavior: behavior.kind,
364
+ suggestedFix: `Add 'event <widget_event> action ${actionTarget}' to the projection widget_bindings entry.`
365
+ }));
366
+ }
367
+ }
368
+ }
369
+
370
+ return checks;
371
+ }
@@ -0,0 +1,200 @@
1
+ // @ts-check
2
+
3
+ import { sharedUiProjectionForWeb } from "../surfaces/shared.js";
4
+
5
+ /**
6
+ * @param {any} entries
7
+ * @returns {any}
8
+ */
9
+ export function byId(entries = []) {
10
+ return new Map(entries.map(/** @param {any} entry */ (entry) => [entry.id, entry]));
11
+ }
12
+
13
+ /**
14
+ * @param {any} values
15
+ * @returns {any}
16
+ */
17
+ export function stableUnique(values) {
18
+ return [...new Set(values.filter(Boolean))].sort();
19
+ }
20
+
21
+ /**
22
+ * @param {any} entry
23
+ * @returns {any}
24
+ */
25
+ export function sourcePath(entry) {
26
+ return entry?.loc?.file || null;
27
+ }
28
+
29
+ /**
30
+ * @param {import("./types.d.ts").WidgetStatement} widget
31
+ * @returns {any}
32
+ */
33
+ export function widgetContract(widget) {
34
+ return widget?.widgetContract || null;
35
+ }
36
+
37
+ /**
38
+ * @param {import("./types.d.ts").WidgetProjection} projection
39
+ * @returns {any}
40
+ */
41
+ export function summarizeProjection(projection) {
42
+ return projection
43
+ ? {
44
+ id: projection.id,
45
+ name: projection.name || projection.id,
46
+ type: projection.type || projection.type || null,
47
+ status: projection.status || null,
48
+ source_path: sourcePath(projection)
49
+ }
50
+ : null;
51
+ }
52
+
53
+ /**
54
+ * @param {import("./types.d.ts").WidgetStatement} widget
55
+ * @returns {any}
56
+ */
57
+ export function summarizeWidget(widget) {
58
+ return widget
59
+ ? {
60
+ id: widget.id,
61
+ name: widget.name || widget.id,
62
+ category: widget.category || null,
63
+ version: widget.version || null,
64
+ status: widget.status || null,
65
+ source_path: sourcePath(widget)
66
+ }
67
+ : null;
68
+ }
69
+
70
+ /**
71
+ * @param {import("./types.d.ts").WidgetStatement} widget
72
+ * @returns {any}
73
+ */
74
+ export function summarizeWidgetContract(widget) {
75
+ const contract = widgetContract(widget);
76
+ if (!contract) return null;
77
+ return {
78
+ id: contract.id,
79
+ name: contract.name,
80
+ category: contract.category || null,
81
+ version: contract.version || null,
82
+ status: contract.status || null,
83
+ props: contract.props || [],
84
+ events: contract.events || [],
85
+ behaviors: contract.behaviors || [],
86
+ behavior: contract.behavior || [],
87
+ approvals: contract.approvals || [],
88
+ dependencies: contract.dependencies || [],
89
+ source_path: sourcePath(widget)
90
+ };
91
+ }
92
+
93
+ /**
94
+ * @param {import("./types.d.ts").WidgetProjection} projection
95
+ * @returns {any}
96
+ */
97
+ export function projectionRealizesIds(projection) {
98
+ return new Set((projection?.realizes || []).map(/** @param {any} ref */ (ref) => ref.id).filter(Boolean));
99
+ }
100
+
101
+ /**
102
+ * @param {import("./types.d.ts").WidgetProjection} projection
103
+ * @returns {any}
104
+ */
105
+ export function ownProjectionScreenMap(projection) {
106
+ return new Map((projection?.uiScreens || []).map(/** @param {any} screen */ (screen) => [screen.id, screen]));
107
+ }
108
+
109
+ /**
110
+ * @param {import("./types.d.ts").WidgetProjection} projection
111
+ * @returns {any}
112
+ */
113
+ export function ownProjectionRegionKeys(projection) {
114
+ return new Set((projection?.uiScreenRegions || []).map(/** @param {any} entry */ (entry) => `${entry.screenId}:${entry.region}`));
115
+ }
116
+
117
+ /**
118
+ * @param {import("./types.d.ts").WidgetGraph} graph
119
+ * @returns {any}
120
+ */
121
+ export function projectionById(graph) {
122
+ return byId(graph.byKind.projection || []);
123
+ }
124
+
125
+ /**
126
+ * @param {import("./types.d.ts").WidgetGraph} graph
127
+ * @param {import("./types.d.ts").WidgetProjection} projection
128
+ * @returns {any}
129
+ */
130
+ export function projectionContext(graph, projection) {
131
+ const projections = /** @type {any[]} */ ([]);
132
+ const seen = new Set();
133
+ const projectionsById = projectionById(graph);
134
+
135
+ /**
136
+ * @param {any} current
137
+ * @returns {void}
138
+ */
139
+ function visit(current) {
140
+ if (!current || seen.has(current.id)) {
141
+ return;
142
+ }
143
+ seen.add(current.id);
144
+ projections.push(current);
145
+ for (const ref of current.realizes || []) {
146
+ const target = projectionsById.get(ref.id);
147
+ if (target) {
148
+ visit(target);
149
+ }
150
+ }
151
+ }
152
+
153
+ visit(projection);
154
+ return projections;
155
+ }
156
+
157
+ /**
158
+ * @param {import("./types.d.ts").WidgetGraph} graph
159
+ * @param {import("./types.d.ts").WidgetProjection} projection
160
+ * @returns {any}
161
+ */
162
+ export function projectionScreenMap(graph, projection) {
163
+ const screens = new Map();
164
+ for (const contextProjection of projectionContext(graph, projection).reverse()) {
165
+ for (const [id, screen] of ownProjectionScreenMap(contextProjection)) {
166
+ screens.set(id, screen);
167
+ }
168
+ }
169
+ return screens;
170
+ }
171
+
172
+ /**
173
+ * @param {import("./types.d.ts").WidgetGraph} graph
174
+ * @param {import("./types.d.ts").WidgetProjection} projection
175
+ * @returns {any}
176
+ */
177
+ export function projectionRegionKeys(graph, projection) {
178
+ const regions = new Set();
179
+ for (const contextProjection of projectionContext(graph, projection)) {
180
+ for (const key of ownProjectionRegionKeys(contextProjection)) {
181
+ regions.add(key);
182
+ }
183
+ }
184
+ return regions;
185
+ }
186
+
187
+ /**
188
+ * @param {import("./types.d.ts").WidgetGraph} graph
189
+ * @param {import("./types.d.ts").WidgetProjection} projection
190
+ * @returns {any}
191
+ */
192
+ export function projectionContextRealizesIds(graph, projection) {
193
+ const ids = new Set();
194
+ for (const contextProjection of projectionContext(graph, projection)) {
195
+ for (const id of projectionRealizesIds(contextProjection)) {
196
+ ids.add(id);
197
+ }
198
+ }
199
+ return ids;
200
+ }