@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,364 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ IDENTIFIER_PATTERN,
5
+ UI_APP_SHELL_KINDS,
6
+ UI_COLLECTION_PRESENTATIONS,
7
+ UI_DESIGN_ACCESSIBILITY_VALUES,
8
+ UI_DESIGN_ACTION_ROLES,
9
+ UI_DESIGN_COLOR_ROLES,
10
+ UI_DESIGN_DENSITIES,
11
+ UI_DESIGN_RADIUS_SCALES,
12
+ UI_DESIGN_TONES,
13
+ UI_DESIGN_TYPOGRAPHY_ROLES,
14
+ UI_NAVIGATION_PATTERNS,
15
+ UI_PATTERN_KINDS,
16
+ UI_REGION_KINDS,
17
+ UI_SCREEN_KINDS,
18
+ UI_STATE_KINDS,
19
+ UI_WINDOWING_MODES
20
+ } from "../kinds.js";
21
+ import {
22
+ blockSymbolItems,
23
+ getFieldValue,
24
+ pushError,
25
+ symbolValue,
26
+ symbolValues
27
+ } from "../utils.js";
28
+ import {
29
+ collectAvailableUiScreenIds,
30
+ collectProjectionUiScreens,
31
+ resolveProjectionUiScreenFieldNames,
32
+ SHARED_UI_SEMANTIC_BLOCKS
33
+ } from "./ui-helpers.js";
34
+ import {
35
+ parseUiDirectiveMap,
36
+ resolveCapabilityContractFields,
37
+ resolveCapabilityOutputShape
38
+ } from "./helpers.js";
39
+
40
+ /**
41
+ * @param {ValidationErrors} errors
42
+ * @param {TopogramStatement} statement
43
+ * @param {TopogramFieldMap} fieldMap
44
+ * @returns {void}
45
+ */
46
+ export function validateProjectionUiOwnership(errors, statement, fieldMap) {
47
+ if (statement.kind !== "projection") {
48
+ return;
49
+ }
50
+
51
+ const projectionType = symbolValue(getFieldValue(statement, "type"));
52
+ for (const key of SHARED_UI_SEMANTIC_BLOCKS) {
53
+ const field = fieldMap.get(key)?.[0];
54
+ if (!field || field.value.type !== "block") {
55
+ continue;
56
+ }
57
+ if (projectionType !== "ui_contract") {
58
+ pushError(
59
+ errors,
60
+ `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define screen_routes and surface hints only`,
61
+ field.loc
62
+ );
63
+ }
64
+ }
65
+
66
+ const routesField = fieldMap.get("screen_routes")?.[0];
67
+ if (routesField?.value.type === "block" && !["web_surface", "ios_surface"].includes(projectionType || "")) {
68
+ pushError(
69
+ errors,
70
+ `Projection ${statement.id} screen_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
71
+ routesField.loc
72
+ );
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @param {ValidationErrors} errors
78
+ * @param {TopogramStatement} statement
79
+ * @param {TopogramFieldMap} fieldMap
80
+ * @param {TopogramRegistry} registry
81
+ * @returns {void}
82
+ */
83
+
84
+ export function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
85
+ if (statement.kind !== "projection") {
86
+ return;
87
+ }
88
+
89
+ const screensField = fieldMap.get("screens")?.[0];
90
+ if (!screensField || screensField.value.type !== "block") {
91
+ return;
92
+ }
93
+
94
+ const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
95
+ const seenScreens = new Set();
96
+
97
+ for (const entry of screensField.value.entries) {
98
+ const tokens = blockSymbolItems(entry).map((item) => item.value);
99
+ const [keyword, screenId] = tokens;
100
+
101
+ if (keyword !== "screen") {
102
+ pushError(errors, `Projection ${statement.id} screens entries must start with 'screen'`, entry.loc);
103
+ continue;
104
+ }
105
+ if (!screenId) {
106
+ pushError(errors, `Projection ${statement.id} screens entries must include a screen id`, entry.loc);
107
+ continue;
108
+ }
109
+ if (!IDENTIFIER_PATTERN.test(screenId)) {
110
+ pushError(errors, `Projection ${statement.id} screens has invalid screen id '${screenId}'`, entry.loc);
111
+ }
112
+ if (seenScreens.has(screenId)) {
113
+ pushError(errors, `Projection ${statement.id} screens has duplicate screen id '${screenId}'`, entry.loc);
114
+ }
115
+ seenScreens.add(screenId);
116
+
117
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `screens for '${screenId}'`);
118
+ const kind = directives.get("kind");
119
+ if (!kind) {
120
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must include 'kind'`, entry.loc);
121
+ }
122
+ if (kind && !UI_SCREEN_KINDS.has(kind)) {
123
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid kind '${kind}'`, entry.loc);
124
+ }
125
+
126
+ for (const key of directives.keys()) {
127
+ if (!["kind", "title", "load", "item_shape", "view_shape", "input_shape", "submit", "detail_capability", "primary_action", "secondary_action", "destructive_action", "success_navigate", "success_refresh", "empty_title", "empty_body", "terminal_action", "loading_state", "error_state", "unauthorized_state", "not_found_state", "success_state"].includes(key)) {
128
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has unknown directive '${key}'`, entry.loc);
129
+ }
130
+ }
131
+
132
+ for (const [key, expectedKind] of [
133
+ ["load", "capability"],
134
+ ["submit", "capability"],
135
+ ["detail_capability", "capability"],
136
+ ["primary_action", "capability"],
137
+ ["secondary_action", "capability"],
138
+ ["destructive_action", "capability"],
139
+ ["terminal_action", "capability"],
140
+ ["item_shape", "shape"],
141
+ ["view_shape", "shape"],
142
+ ["input_shape", "shape"]
143
+ ]) {
144
+ const targetId = directives.get(key);
145
+ if (!targetId) {
146
+ continue;
147
+ }
148
+ const target = registry.get(targetId);
149
+ if (!target) {
150
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references missing ${expectedKind} '${targetId}' for '${key}'`, entry.loc);
151
+ continue;
152
+ }
153
+ if (target.kind !== expectedKind) {
154
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must reference a ${expectedKind} for '${key}', found ${target.kind} '${target.id}'`, entry.loc);
155
+ }
156
+ if (expectedKind === "capability" && !realized.has(targetId)) {
157
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' capability '${targetId}' for '${key}' must also appear in 'realizes'`, entry.loc);
158
+ }
159
+ }
160
+
161
+ const successNavigate = directives.get("success_navigate");
162
+ const successRefresh = directives.get("success_refresh");
163
+ if (successNavigate && !IDENTIFIER_PATTERN.test(successNavigate)) {
164
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successNavigate}' for 'success_navigate'`, entry.loc);
165
+ }
166
+ if (successRefresh && !IDENTIFIER_PATTERN.test(successRefresh)) {
167
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successRefresh}' for 'success_refresh'`, entry.loc);
168
+ }
169
+
170
+ if (kind === "list" && !directives.get("load")) {
171
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'list' requires 'load'`, entry.loc);
172
+ }
173
+ if (kind === "detail") {
174
+ if (!directives.get("load")) {
175
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'load'`, entry.loc);
176
+ }
177
+ if (!directives.get("view_shape")) {
178
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'view_shape'`, entry.loc);
179
+ }
180
+ }
181
+ if (kind === "form") {
182
+ if (!directives.get("input_shape")) {
183
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'input_shape'`, entry.loc);
184
+ }
185
+ if (!directives.get("submit")) {
186
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'submit'`, entry.loc);
187
+ }
188
+ }
189
+ }
190
+
191
+ for (const entry of screensField.value.entries) {
192
+ const tokens = blockSymbolItems(entry).map((item) => item.value);
193
+ const screenId = tokens[1];
194
+ if (!screenId) {
195
+ continue;
196
+ }
197
+ const directives = parseUiDirectiveMap(tokens, 2, [], statement, entry, "");
198
+ for (const key of ["success_navigate", "success_refresh"]) {
199
+ const targetScreenId = directives.get(key);
200
+ if (targetScreenId && !seenScreens.has(targetScreenId)) {
201
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references unknown screen '${targetScreenId}' for '${key}'`, entry.loc);
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @param {ValidationErrors} errors
209
+ * @param {TopogramStatement} statement
210
+ * @param {TopogramFieldMap} fieldMap
211
+ * @returns {void}
212
+ */
213
+
214
+ export function validateProjectionUiAppShell(errors, statement, fieldMap) {
215
+ if (statement.kind !== "projection") {
216
+ return;
217
+ }
218
+
219
+ const shellField = fieldMap.get("app_shell")?.[0];
220
+ if (!shellField || shellField.value.type !== "block") {
221
+ return;
222
+ }
223
+
224
+ const seenKeys = new Set();
225
+ for (const entry of shellField.value.entries) {
226
+ const tokens = blockSymbolItems(entry).map((item) => item.value);
227
+ const [key, value, extra] = tokens;
228
+ if (!["brand", "shell", "primary_nav", "secondary_nav", "utility_nav", "footer", "global_search", "notifications", "account_menu", "workspace_switcher", "windowing"].includes(key || "")) {
229
+ pushError(errors, `Projection ${statement.id} app_shell has unknown key '${key}'`, entry.loc);
230
+ continue;
231
+ }
232
+ if (!value) {
233
+ pushError(errors, `Projection ${statement.id} app_shell is missing a value for '${key}'`, entry.loc);
234
+ continue;
235
+ }
236
+ if (extra) {
237
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' accepts exactly one value`, entry.loc);
238
+ }
239
+ if (seenKeys.has(key)) {
240
+ pushError(errors, `Projection ${statement.id} app_shell has duplicate key '${key}'`, entry.loc);
241
+ }
242
+ seenKeys.add(key);
243
+
244
+ if (key === "shell" && !UI_APP_SHELL_KINDS.has(value)) {
245
+ pushError(errors, `Projection ${statement.id} app_shell has invalid shell '${value}'`, entry.loc);
246
+ }
247
+ if (["global_search", "notifications", "account_menu", "workspace_switcher"].includes(key) && !["true", "false"].includes(value)) {
248
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' must be true or false`, entry.loc);
249
+ }
250
+ if (key === "windowing" && !UI_WINDOWING_MODES.has(value)) {
251
+ pushError(errors, `Projection ${statement.id} app_shell has invalid windowing '${value}'`, entry.loc);
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * @param {ValidationErrors} errors
258
+ * @param {TopogramStatement} statement
259
+ * @param {TopogramFieldMap} fieldMap
260
+ * @returns {void}
261
+ */
262
+
263
+ export function validateProjectionUiDesign(errors, statement, fieldMap) {
264
+ if (statement.kind !== "projection") {
265
+ return;
266
+ }
267
+
268
+ const designField = fieldMap.get("design_tokens")?.[0];
269
+ if (!designField || designField.value.type !== "block") {
270
+ return;
271
+ }
272
+
273
+ if (symbolValue(getFieldValue(statement, "type")) !== "ui_contract") {
274
+ pushError(errors, `Projection ${statement.id} design_tokens belongs on shared UI projections; concrete UI projections inherit semantic design intent through 'realizes'`, designField.loc);
275
+ }
276
+
277
+ for (const entry of designField.value.entries) {
278
+ const tokens = blockSymbolItems(entry).map((item) => item.value);
279
+ const [key, value, extra] = tokens;
280
+
281
+ if (key === "density") {
282
+ if (!UI_DESIGN_DENSITIES.has(value || "")) {
283
+ pushError(errors, `Projection ${statement.id} design_tokens density has invalid value '${value}'`, entry.loc);
284
+ }
285
+ if (tokens.length !== 2) {
286
+ pushError(errors, `Projection ${statement.id} design_tokens density accepts exactly one value`, entry.loc);
287
+ }
288
+ continue;
289
+ }
290
+
291
+ if (key === "tone") {
292
+ if (!UI_DESIGN_TONES.has(value || "")) {
293
+ pushError(errors, `Projection ${statement.id} design_tokens tone has invalid value '${value}'`, entry.loc);
294
+ }
295
+ if (tokens.length !== 2) {
296
+ pushError(errors, `Projection ${statement.id} design_tokens tone accepts exactly one value`, entry.loc);
297
+ }
298
+ continue;
299
+ }
300
+
301
+ if (key === "radius_scale") {
302
+ if (!UI_DESIGN_RADIUS_SCALES.has(value || "")) {
303
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale has invalid value '${value}'`, entry.loc);
304
+ }
305
+ if (tokens.length !== 2) {
306
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale accepts exactly one value`, entry.loc);
307
+ }
308
+ continue;
309
+ }
310
+
311
+ if (key === "color_role") {
312
+ if (!UI_DESIGN_COLOR_ROLES.has(value || "")) {
313
+ pushError(errors, `Projection ${statement.id} design_tokens color_role has invalid role '${value}'`, entry.loc);
314
+ }
315
+ if (tokens.length !== 3) {
316
+ pushError(errors, `Projection ${statement.id} design_tokens color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
317
+ }
318
+ continue;
319
+ }
320
+
321
+ if (key === "typography_role") {
322
+ if (!UI_DESIGN_TYPOGRAPHY_ROLES.has(value || "")) {
323
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role has invalid role '${value}'`, entry.loc);
324
+ }
325
+ if (tokens.length !== 3) {
326
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
327
+ }
328
+ continue;
329
+ }
330
+
331
+ if (key === "action_role") {
332
+ if (!UI_DESIGN_ACTION_ROLES.has(value || "")) {
333
+ pushError(errors, `Projection ${statement.id} design_tokens action_role has invalid role '${value}'`, entry.loc);
334
+ }
335
+ if (tokens.length !== 3) {
336
+ pushError(errors, `Projection ${statement.id} design_tokens action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
337
+ }
338
+ continue;
339
+ }
340
+
341
+ if (key === "accessibility") {
342
+ const values = UI_DESIGN_ACCESSIBILITY_VALUES[value];
343
+ if (tokens.length !== 3) {
344
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility must use 'accessibility <setting> <value>'`, entry.loc);
345
+ }
346
+ if (!values) {
347
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility has invalid setting '${value}'`, entry.loc);
348
+ } else if (!values.has(extra || "")) {
349
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility '${value}' has invalid value '${extra}'`, entry.loc);
350
+ }
351
+ continue;
352
+ }
353
+
354
+ pushError(errors, `Projection ${statement.id} design_tokens has unknown key '${key}'`, entry.loc);
355
+ }
356
+ }
357
+
358
+ /**
359
+ * @param {ValidationErrors} errors
360
+ * @param {TopogramStatement} statement
361
+ * @param {TopogramFieldMap} fieldMap
362
+ * @param {TopogramRegistry} registry
363
+ * @returns {void}
364
+ */