@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
@@ -0,0 +1,317 @@
1
+ import { blockEntries, getFieldValue } from "../validator.js";
2
+ import { normalizeSequence, tokenValue } from "./shared.js";
3
+
4
+ export function parseProjectionUiScreensBlock(statement, registry) {
5
+ return blockEntries(getFieldValue(statement, "screens")).map((entry) => {
6
+ const directives = {};
7
+
8
+ for (let i = 2; i < entry.items.length; i += 2) {
9
+ const key = tokenValue(entry.items[i]);
10
+ const value = tokenValue(entry.items[i + 1]);
11
+ if (key && value != null) {
12
+ directives[key] = value;
13
+ }
14
+ }
15
+
16
+ return {
17
+ type: "ui_screen",
18
+ id: tokenValue(entry.items[1]),
19
+ kind: directives.kind || null,
20
+ title: directives.title || null,
21
+ load: directives.load ? { id: directives.load, kind: registry.get(directives.load)?.kind || null } : null,
22
+ itemShape: directives.item_shape ? { id: directives.item_shape, kind: registry.get(directives.item_shape)?.kind || null } : null,
23
+ viewShape: directives.view_shape ? { id: directives.view_shape, kind: registry.get(directives.view_shape)?.kind || null } : null,
24
+ inputShape: directives.input_shape ? { id: directives.input_shape, kind: registry.get(directives.input_shape)?.kind || null } : null,
25
+ submit: directives.submit ? { id: directives.submit, kind: registry.get(directives.submit)?.kind || null } : null,
26
+ detailCapability: directives.detail_capability
27
+ ? { id: directives.detail_capability, kind: registry.get(directives.detail_capability)?.kind || null }
28
+ : null,
29
+ primaryAction: directives.primary_action
30
+ ? { id: directives.primary_action, kind: registry.get(directives.primary_action)?.kind || null }
31
+ : null,
32
+ secondaryAction: directives.secondary_action
33
+ ? { id: directives.secondary_action, kind: registry.get(directives.secondary_action)?.kind || null }
34
+ : null,
35
+ destructiveAction: directives.destructive_action
36
+ ? { id: directives.destructive_action, kind: registry.get(directives.destructive_action)?.kind || null }
37
+ : null,
38
+ terminalAction: directives.terminal_action
39
+ ? { id: directives.terminal_action, kind: registry.get(directives.terminal_action)?.kind || null }
40
+ : null,
41
+ successNavigate: directives.success_navigate || null,
42
+ successRefresh: directives.success_refresh || null,
43
+ emptyTitle: directives.empty_title || null,
44
+ emptyBody: directives.empty_body || null,
45
+ loadingState: directives.loading_state || null,
46
+ errorState: directives.error_state || null,
47
+ unauthorizedState: directives.unauthorized_state || null,
48
+ notFoundState: directives.not_found_state || null,
49
+ successState: directives.success_state || null,
50
+ raw: normalizeSequence(entry.items),
51
+ loc: entry.loc
52
+ };
53
+ });
54
+ }
55
+
56
+ export function parseProjectionUiCollectionsBlock(statement) {
57
+ return blockEntries(getFieldValue(statement, "collection_views")).map((entry) => {
58
+ const operation = tokenValue(entry.items[2]);
59
+ const primaryValue = tokenValue(entry.items[3]) || null;
60
+ const secondaryValue = tokenValue(entry.items[4]) || null;
61
+
62
+ return {
63
+ type: "ui_collection_binding",
64
+ screenId: tokenValue(entry.items[1]),
65
+ operation,
66
+ field: ["filter", "search", "sort", "group"].includes(operation) ? primaryValue : null,
67
+ direction: operation === "sort" ? secondaryValue : null,
68
+ value: primaryValue,
69
+ extra: secondaryValue,
70
+ raw: normalizeSequence(entry.items),
71
+ loc: entry.loc
72
+ };
73
+ });
74
+ }
75
+
76
+ export function parseProjectionUiActionsBlock(statement, registry) {
77
+ return blockEntries(getFieldValue(statement, "screen_actions")).map((entry) => ({
78
+ type: "ui_action_binding",
79
+ screenId: tokenValue(entry.items[1]),
80
+ capability: tokenValue(entry.items[3])
81
+ ? {
82
+ id: tokenValue(entry.items[3]),
83
+ kind: registry.get(tokenValue(entry.items[3]))?.kind || null
84
+ }
85
+ : null,
86
+ prominence: tokenValue(entry.items[5]) || null,
87
+ placement: tokenValue(entry.items[6]) === "placement" ? tokenValue(entry.items[7]) || null : null,
88
+ raw: normalizeSequence(entry.items),
89
+ loc: entry.loc
90
+ }));
91
+ }
92
+
93
+ export function parseProjectionUiVisibilityBlock(statement, registry) {
94
+ return blockEntries(getFieldValue(statement, "visibility_rules")).map((entry) => {
95
+ const directives = {};
96
+ for (let i = 5; i < entry.items.length; i += 2) {
97
+ const key = tokenValue(entry.items[i]);
98
+ const value = tokenValue(entry.items[i + 1]);
99
+ if (key && value != null) {
100
+ directives[key] = value;
101
+ }
102
+ }
103
+
104
+ return {
105
+ type: "visibility_rules_rule",
106
+ capability: tokenValue(entry.items[1])
107
+ ? {
108
+ id: tokenValue(entry.items[1]),
109
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
110
+ }
111
+ : null,
112
+ predicate: tokenValue(entry.items[3]) || null,
113
+ value: tokenValue(entry.items[4]) || null,
114
+ claimValue: directives.claim_value || null,
115
+ raw: normalizeSequence(entry.items),
116
+ loc: entry.loc
117
+ };
118
+ });
119
+ }
120
+
121
+ export function parseProjectionUiLookupsBlock(statement, registry) {
122
+ return blockEntries(getFieldValue(statement, "field_lookups")).map((entry) => ({
123
+ type: "ui_lookup_binding",
124
+ screenId: tokenValue(entry.items[1]),
125
+ field: tokenValue(entry.items[3]) || null,
126
+ entity: tokenValue(entry.items[5])
127
+ ? {
128
+ id: tokenValue(entry.items[5]),
129
+ kind: registry.get(tokenValue(entry.items[5]))?.kind || null
130
+ }
131
+ : null,
132
+ labelField: tokenValue(entry.items[7]) || null,
133
+ emptyLabel: tokenValue(entry.items[9]) || null,
134
+ raw: normalizeSequence(entry.items),
135
+ loc: entry.loc
136
+ }));
137
+ }
138
+
139
+ export function parseProjectionUiRoutesBlock(statement) {
140
+ return blockEntries(getFieldValue(statement, "screen_routes")).map((entry) => ({
141
+ type: "ui_route",
142
+ screenId: tokenValue(entry.items[1]),
143
+ path: tokenValue(entry.items[3]) || null,
144
+ raw: normalizeSequence(entry.items),
145
+ loc: entry.loc
146
+ }));
147
+ }
148
+
149
+ export function parseProjectionUiIosBlock(statement, registry) {
150
+ return blockEntries(getFieldValue(statement, "ios_hints")).map((entry) => ({
151
+ type: "ios_hint_binding",
152
+ targetKind: tokenValue(entry.items[0]),
153
+ targetId: tokenValue(entry.items[1]),
154
+ capability:
155
+ tokenValue(entry.items[0]) === "action" && tokenValue(entry.items[1])
156
+ ? {
157
+ id: tokenValue(entry.items[1]),
158
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
159
+ }
160
+ : null,
161
+ directive: tokenValue(entry.items[2]) || null,
162
+ value: tokenValue(entry.items[3]) || null,
163
+ raw: normalizeSequence(entry.items),
164
+ loc: entry.loc
165
+ }));
166
+ }
167
+
168
+ export function parseProjectionUiWebBlock(statement, registry) {
169
+ return blockEntries(getFieldValue(statement, "web_hints")).map((entry) => ({
170
+ type: "web_hint_binding",
171
+ targetKind: tokenValue(entry.items[0]),
172
+ targetId: tokenValue(entry.items[1]),
173
+ capability:
174
+ tokenValue(entry.items[0]) === "action" && tokenValue(entry.items[1])
175
+ ? {
176
+ id: tokenValue(entry.items[1]),
177
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
178
+ }
179
+ : null,
180
+ directive: tokenValue(entry.items[2]) || null,
181
+ value: tokenValue(entry.items[3]) || null,
182
+ raw: normalizeSequence(entry.items),
183
+ loc: entry.loc
184
+ }));
185
+ }
186
+
187
+ export function parseProjectionUiAppShellBlock(statement) {
188
+ return blockEntries(getFieldValue(statement, "app_shell")).map((entry) => ({
189
+ type: "app_shell_binding",
190
+ key: tokenValue(entry.items[0]) || null,
191
+ value: tokenValue(entry.items[1]) || null,
192
+ raw: normalizeSequence(entry.items),
193
+ loc: entry.loc
194
+ }));
195
+ }
196
+
197
+ export function parseProjectionUiDesignBlock(statement) {
198
+ return blockEntries(getFieldValue(statement, "design_tokens")).map((entry) => ({
199
+ type: "design_tokens_token",
200
+ key: tokenValue(entry.items[0]) || null,
201
+ role: tokenValue(entry.items[1]) || null,
202
+ value: tokenValue(entry.items[2]) || null,
203
+ raw: normalizeSequence(entry.items),
204
+ loc: entry.loc
205
+ }));
206
+ }
207
+
208
+ export function parseProjectionUiNavigationBlock(statement) {
209
+ return blockEntries(getFieldValue(statement, "navigation")).map((entry) => {
210
+ const directives = {};
211
+ for (let i = 2; i < entry.items.length; i += 2) {
212
+ const key = tokenValue(entry.items[i]);
213
+ const value = tokenValue(entry.items[i + 1]);
214
+ if (key && value != null) {
215
+ directives[key] = value;
216
+ }
217
+ }
218
+
219
+ return {
220
+ type: "navigation_binding",
221
+ targetKind: tokenValue(entry.items[0]) || null,
222
+ targetId: tokenValue(entry.items[1]) || null,
223
+ directives,
224
+ raw: normalizeSequence(entry.items),
225
+ loc: entry.loc
226
+ };
227
+ });
228
+ }
229
+
230
+ export function parseProjectionUiScreenRegionsBlock(statement) {
231
+ return blockEntries(getFieldValue(statement, "screen_regions")).map((entry) => {
232
+ const directives = {};
233
+ for (let i = 4; i < entry.items.length; i += 2) {
234
+ const key = tokenValue(entry.items[i]);
235
+ const value = tokenValue(entry.items[i + 1]);
236
+ if (key && value != null) {
237
+ directives[key] = value;
238
+ }
239
+ }
240
+
241
+ return {
242
+ type: "ui_screen_region_binding",
243
+ screenId: tokenValue(entry.items[1]) || null,
244
+ region: tokenValue(entry.items[3]) || null,
245
+ pattern: directives.pattern || null,
246
+ placement: directives.placement || null,
247
+ title: directives.title || null,
248
+ state: directives.state || null,
249
+ variant: directives.variant || null,
250
+ raw: normalizeSequence(entry.items),
251
+ loc: entry.loc
252
+ };
253
+ });
254
+ }
255
+
256
+ export function parseProjectionWidgetBindingsBlock(statement, registry) {
257
+ return blockEntries(getFieldValue(statement, "widget_bindings")).map((entry) => {
258
+ const dataBindings = [];
259
+ const eventBindings = [];
260
+
261
+ for (let i = 6; i < entry.items.length;) {
262
+ const directive = tokenValue(entry.items[i]);
263
+ if (directive === "data") {
264
+ const prop = tokenValue(entry.items[i + 1]);
265
+ const sourceId = tokenValue(entry.items[i + 3]);
266
+ dataBindings.push({
267
+ prop,
268
+ source: sourceId
269
+ ? {
270
+ id: sourceId,
271
+ kind: registry.get(sourceId)?.kind || null
272
+ }
273
+ : null
274
+ });
275
+ i += 4;
276
+ continue;
277
+ }
278
+ if (directive === "event") {
279
+ const event = tokenValue(entry.items[i + 1]);
280
+ const action = tokenValue(entry.items[i + 2]);
281
+ const targetId = tokenValue(entry.items[i + 3]);
282
+ eventBindings.push({
283
+ event,
284
+ action,
285
+ target: targetId
286
+ ? {
287
+ id: targetId,
288
+ kind: action === "navigate" ? "screen" : registry.get(targetId)?.kind || null
289
+ }
290
+ : null
291
+ });
292
+ i += 4;
293
+ continue;
294
+ }
295
+ i += 1;
296
+ }
297
+
298
+ const widgetId = tokenValue(entry.items[5]);
299
+ const widgetRef = widgetId
300
+ ? {
301
+ id: widgetId,
302
+ kind: registry.get(widgetId)?.kind || null
303
+ }
304
+ : null;
305
+ const binding = {
306
+ type: "widget_binding",
307
+ screenId: tokenValue(entry.items[1]) || null,
308
+ region: tokenValue(entry.items[3]) || null,
309
+ widget: widgetRef,
310
+ dataBindings,
311
+ eventBindings,
312
+ raw: normalizeSequence(entry.items),
313
+ loc: entry.loc
314
+ };
315
+ return binding;
316
+ });
317
+ }
@@ -0,0 +1,251 @@
1
+ import { blockEntries, getFieldValue } from "../validator.js";
2
+ import { normalizeSequence, parseInvariantEntry, parseSymbolNodes, tokenValue } from "./shared.js";
3
+
4
+ export function parseKeyBlock(statement) {
5
+ return blockEntries(getFieldValue(statement, "keys")).map((entry) => ({
6
+ type: tokenValue(entry.items[0]),
7
+ fields:
8
+ entry.items[1]?.type === "list"
9
+ ? entry.items[1].items.map((item) => item.value)
10
+ : [],
11
+ raw: normalizeSequence(entry.items),
12
+ loc: entry.loc
13
+ }));
14
+ }
15
+
16
+ export function parseRelationBlock(statement, registry) {
17
+ return blockEntries(getFieldValue(statement, "relations")).map((entry) => {
18
+ const sourceField = tokenValue(entry.items[0]);
19
+ const targetRef = tokenValue(entry.items[2]);
20
+ const [entityId, fieldName] = (targetRef || "").split(".");
21
+ const targetStatement = entityId ? registry.get(entityId) : null;
22
+
23
+ return {
24
+ type: "reference",
25
+ sourceField,
26
+ target: entityId
27
+ ? {
28
+ id: entityId,
29
+ kind: targetStatement?.kind || null,
30
+ field: fieldName || null
31
+ }
32
+ : null,
33
+ raw: normalizeSequence(entry.items),
34
+ loc: entry.loc
35
+ };
36
+ });
37
+ }
38
+
39
+ export function parseInvariantBlock(statement) {
40
+ return blockEntries(getFieldValue(statement, "invariants")).map((entry) => parseInvariantEntry(entry));
41
+ }
42
+
43
+ export function parseRenameBlock(statement) {
44
+ return blockEntries(getFieldValue(statement, "rename")).map((entry) => ({
45
+ from: entry.items[0]?.value ?? null,
46
+ to: entry.items[1]?.value ?? null,
47
+ loc: entry.loc
48
+ }));
49
+ }
50
+
51
+ export function parseOverridesBlock(statement) {
52
+ return blockEntries(getFieldValue(statement, "overrides")).map((entry) => {
53
+ const [fieldName, ...rest] = entry.items;
54
+ const override = {
55
+ field: fieldName?.value ?? null,
56
+ requiredness: null,
57
+ fieldType: null,
58
+ defaultValue: undefined,
59
+ loc: entry.loc
60
+ };
61
+
62
+ for (let i = 0; i < rest.length; i += 1) {
63
+ const token = rest[i];
64
+ if (!token || token.type !== "symbol") {
65
+ continue;
66
+ }
67
+
68
+ if (token.value === "required" || token.value === "optional") {
69
+ override.requiredness = token.value;
70
+ continue;
71
+ }
72
+
73
+ if (token.value === "type") {
74
+ override.fieldType = rest[i + 1]?.value ?? null;
75
+ i += 1;
76
+ continue;
77
+ }
78
+
79
+ if (token.value === "default") {
80
+ override.defaultValue = rest[i + 1]?.value ?? null;
81
+ i += 1;
82
+ }
83
+ }
84
+
85
+ return override;
86
+ });
87
+ }
88
+
89
+ export function cloneField(field) {
90
+ return {
91
+ ...field,
92
+ sourceName: field.sourceName ?? field.name
93
+ };
94
+ }
95
+
96
+ export function fieldRef(field) {
97
+ return {
98
+ name: field.name,
99
+ sourceName: field.sourceName ?? field.name,
100
+ fieldType: field.fieldType,
101
+ requiredness: field.requiredness,
102
+ defaultValue: field.defaultValue ?? null
103
+ };
104
+ }
105
+
106
+ export function applyRename(fields, renameRules) {
107
+ const renameBySource = new Map(renameRules.map((rule) => [rule.from, rule.to]));
108
+ return fields.map((field) => {
109
+ const renamed = cloneField(field);
110
+ const nextName = renameBySource.get(field.name);
111
+ if (nextName) {
112
+ renamed.name = nextName;
113
+ }
114
+ return renamed;
115
+ });
116
+ }
117
+
118
+ export function applyOverrides(fields, overrideRules) {
119
+ const byCurrentName = new Map(fields.map((field) => [field.name, field]));
120
+ const bySourceName = new Map(fields.map((field) => [field.sourceName, field]));
121
+
122
+ for (const rule of overrideRules) {
123
+ const target = byCurrentName.get(rule.field) || bySourceName.get(rule.field);
124
+ if (!target) {
125
+ continue;
126
+ }
127
+
128
+ if (rule.requiredness) {
129
+ target.requiredness = rule.requiredness;
130
+ }
131
+ if (rule.fieldType) {
132
+ target.fieldType = rule.fieldType;
133
+ }
134
+ if (rule.defaultValue !== undefined) {
135
+ target.defaultValue = rule.defaultValue;
136
+ }
137
+ }
138
+
139
+ return fields;
140
+ }
141
+
142
+ export function buildShapeSelection(shape, byId) {
143
+ const explicitFields = shape.fields.length > 0;
144
+ const selectedFields =
145
+ explicitFields
146
+ ? shape.fields.map((field) => ({
147
+ ...cloneField(field),
148
+ sourceName: field.name
149
+ }))
150
+ : deriveShapeFields(shape, byId).map((field) => ({
151
+ ...cloneField(field),
152
+ sourceName: field.name
153
+ }));
154
+
155
+ return {
156
+ type: "shape_selection",
157
+ mode: explicitFields ? "explicit_fields" : shape.from?.id ? "derived_from_entity" : "empty",
158
+ source: shape.from?.target || null,
159
+ include: parseSymbolNodes(shape.include),
160
+ exclude: parseSymbolNodes(shape.exclude),
161
+ selectedFields: selectedFields.map((field) => fieldRef(field))
162
+ };
163
+ }
164
+
165
+ export function buildRenameTransforms(renameRules) {
166
+ return renameRules.map((rule, index) => ({
167
+ type: "rename_field",
168
+ order: index,
169
+ from: rule.from,
170
+ to: rule.to,
171
+ loc: rule.loc
172
+ }));
173
+ }
174
+
175
+ export function buildOverrideTransforms(overrideRules) {
176
+ return overrideRules.map((rule, index) => ({
177
+ type: "override_field",
178
+ order: index,
179
+ field: rule.field,
180
+ changes: {
181
+ requiredness: rule.requiredness,
182
+ fieldType: rule.fieldType,
183
+ defaultValue: rule.defaultValue ?? null
184
+ },
185
+ loc: rule.loc
186
+ }));
187
+ }
188
+
189
+ export function buildShapeTransformGraph(shape, byId) {
190
+ const selection = buildShapeSelection(shape, byId);
191
+ return {
192
+ type: "shape_transform_graph",
193
+ selection,
194
+ transforms: [
195
+ ...buildRenameTransforms(shape.rename),
196
+ ...buildOverrideTransforms(shape.overrides)
197
+ ],
198
+ resultFields: (shape.projectedFields || []).map((field) => fieldRef(field))
199
+ };
200
+ }
201
+
202
+ export function projectShapeFields(shape, byId) {
203
+ const baseFields =
204
+ shape.fields.length > 0
205
+ ? shape.fields.map((field) => ({
206
+ ...cloneField(field),
207
+ sourceName: field.name
208
+ }))
209
+ : deriveShapeFields(shape, byId).map((field) => ({
210
+ ...cloneField(field),
211
+ sourceName: field.name
212
+ }));
213
+
214
+ const renamedFields = applyRename(baseFields, shape.rename);
215
+ const overriddenFields = applyOverrides(renamedFields, shape.overrides);
216
+
217
+ return overriddenFields.map((field) => ({
218
+ name: field.name,
219
+ sourceName: field.sourceName,
220
+ fieldType: field.fieldType,
221
+ requiredness: field.requiredness,
222
+ defaultValue: field.defaultValue ?? null,
223
+ raw: field.raw,
224
+ loc: field.loc
225
+ }));
226
+ }
227
+
228
+ export function deriveShapeFields(shape, byId) {
229
+ if (shape.fields.length > 0) {
230
+ return shape.fields;
231
+ }
232
+
233
+ if (!shape.from?.target?.id) {
234
+ return [];
235
+ }
236
+
237
+ const entity = byId.get(shape.from.target.id);
238
+ if (!entity || entity.kind !== "entity") {
239
+ return [];
240
+ }
241
+
242
+ const sourceFields = new Map(entity.fields.map((field) => [field.name, field]));
243
+ const includes = shape.include.length > 0 ? shape.include : [...sourceFields.keys()];
244
+ const excludes = new Set(shape.exclude);
245
+
246
+ return includes
247
+ .filter((fieldName) => !excludes.has(fieldName))
248
+ .map((fieldName) => sourceFields.get(fieldName))
249
+ .filter(Boolean)
250
+ .map((field) => cloneField(field));
251
+ }