@topogram/cli 0.3.51 → 0.3.53

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 (77) hide show
  1. package/ARCHITECTURE.md +4 -4
  2. package/CHANGELOG.md +11 -11
  3. package/package.json +1 -1
  4. package/src/adoption/plan.js +2 -2
  5. package/src/agent-ops/query-builders.js +42 -33
  6. package/src/cli.js +174 -129
  7. package/src/generator/adapters.d.ts +1 -0
  8. package/src/generator/adapters.js +64 -39
  9. package/src/generator/check.js +19 -12
  10. package/src/generator/context/diff.js +9 -9
  11. package/src/generator/context/domain-coverage.js +11 -10
  12. package/src/generator/context/domain-page.js +6 -6
  13. package/src/generator/context/shared.js +37 -21
  14. package/src/generator/context/slice.js +70 -65
  15. package/src/generator/index.js +12 -12
  16. package/src/generator/output.js +21 -20
  17. package/src/generator/registry.js +71 -49
  18. package/src/generator/runtime/app-bundle.js +15 -15
  19. package/src/generator/runtime/compile-check.js +7 -7
  20. package/src/generator/runtime/deployment.js +9 -9
  21. package/src/generator/runtime/environment.js +39 -39
  22. package/src/generator/runtime/runtime-check.js +5 -5
  23. package/src/generator/runtime/shared.js +40 -38
  24. package/src/generator/runtime/smoke.js +5 -5
  25. package/src/generator/surfaces/databases/contract.js +1 -1
  26. package/src/generator/surfaces/databases/lifecycle-shared.js +6 -5
  27. package/src/generator/surfaces/databases/postgres/drizzle.js +3 -2
  28. package/src/generator/surfaces/databases/postgres/prisma.js +3 -2
  29. package/src/generator/surfaces/databases/shared.js +3 -2
  30. package/src/generator/surfaces/databases/snapshot.js +1 -1
  31. package/src/generator/surfaces/databases/sqlite/prisma.js +3 -2
  32. package/src/generator/surfaces/native/swiftui-app.js +3 -3
  33. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +1 -1
  34. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +3 -3
  35. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +3 -3
  36. package/src/generator/surfaces/services/persistence-wiring.js +3 -2
  37. package/src/generator/surfaces/services/server-contract.js +4 -4
  38. package/src/generator/surfaces/shared.js +2 -2
  39. package/src/generator/surfaces/web/design-intent.js +1 -1
  40. package/src/generator/surfaces/web/index.js +7 -7
  41. package/src/generator/surfaces/web/{react-components.js → react-widgets.js} +53 -53
  42. package/src/generator/surfaces/web/react.js +36 -36
  43. package/src/generator/surfaces/web/{sveltekit-components.js → sveltekit-widgets.js} +53 -53
  44. package/src/generator/surfaces/web/sveltekit.js +34 -34
  45. package/src/generator/surfaces/web/{ui-web-contract.js → ui-surface-contract.js} +8 -8
  46. package/src/generator/surfaces/web/vanilla.js +6 -6
  47. package/src/generator/{component-conformance.js → widget-conformance.js} +129 -128
  48. package/src/generator/widgets.js +40 -0
  49. package/src/generator-policy.js +10 -12
  50. package/src/import/core/runner.js +34 -34
  51. package/src/import/core/shared.js +1 -1
  52. package/src/import/extractors/ui/android-compose.js +1 -1
  53. package/src/import/extractors/ui/blazor.js +1 -1
  54. package/src/import/extractors/ui/razor-pages.js +1 -1
  55. package/src/import/extractors/ui/react-router.js +4 -4
  56. package/src/import/extractors/ui/sveltekit.js +4 -4
  57. package/src/import/extractors/ui/swiftui.js +1 -1
  58. package/src/import/extractors/ui/uikit.js +1 -1
  59. package/src/new-project.js +19 -18
  60. package/src/project-config.js +104 -44
  61. package/src/proofs/contract-audit.js +1 -1
  62. package/src/proofs/ios-parity.js +1 -1
  63. package/src/proofs/issues-parity.js +1 -1
  64. package/src/realization/backend/build-backend-runtime-realization.js +2 -2
  65. package/src/realization/ui/build-ui-shared-realization.js +33 -33
  66. package/src/realization/ui/build-web-realization.js +23 -20
  67. package/src/reconcile/journeys.js +1 -1
  68. package/src/resolver/index.js +148 -65
  69. package/src/validator/index.js +509 -423
  70. package/src/validator/kinds.js +36 -36
  71. package/src/validator/per-kind/{component.js → widget.js} +47 -47
  72. package/src/{component-behavior.js → widget-behavior.js} +3 -3
  73. package/src/workflows.js +39 -38
  74. package/template-helpers/react.js +4 -4
  75. package/template-helpers/sveltekit.js +4 -4
  76. package/src/generator/components.js +0 -39
  77. /package/src/resolver/enrich/{component.js → widget.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { getProjection, sharedUiProjectionForWeb, uiProjectionCandidates } from "./surfaces/shared.js";
2
- import { buildComponentBehaviorRealizations } from "../component-behavior.js";
2
+ import { buildWidgetBehaviorRealizations } from "../widget-behavior.js";
3
3
 
4
4
  function byId(entries = []) {
5
5
  return new Map(entries.map((entry) => [entry.id, entry]));
@@ -13,8 +13,8 @@ function sourcePath(entry) {
13
13
  return entry?.loc?.file || null;
14
14
  }
15
15
 
16
- function componentContract(component) {
17
- return component?.componentContract || null;
16
+ function widgetContract(widget) {
17
+ return widget?.widgetContract || null;
18
18
  }
19
19
 
20
20
  function summarizeProjection(projection) {
@@ -22,28 +22,28 @@ function summarizeProjection(projection) {
22
22
  ? {
23
23
  id: projection.id,
24
24
  name: projection.name || projection.id,
25
- platform: projection.platform || null,
25
+ type: projection.type || projection.platform || null,
26
26
  status: projection.status || null,
27
27
  source_path: sourcePath(projection)
28
28
  }
29
29
  : null;
30
30
  }
31
31
 
32
- function summarizeComponent(component) {
33
- return component
32
+ function summarizeWidget(widget) {
33
+ return widget
34
34
  ? {
35
- id: component.id,
36
- name: component.name || component.id,
37
- category: component.category || null,
38
- version: component.version || null,
39
- status: component.status || null,
40
- source_path: sourcePath(component)
35
+ id: widget.id,
36
+ name: widget.name || widget.id,
37
+ category: widget.category || null,
38
+ version: widget.version || null,
39
+ status: widget.status || null,
40
+ source_path: sourcePath(widget)
41
41
  }
42
42
  : null;
43
43
  }
44
44
 
45
- function summarizeComponentContract(component) {
46
- const contract = componentContract(component);
45
+ function summarizeWidgetContract(widget) {
46
+ const contract = widgetContract(widget);
47
47
  if (!contract) return null;
48
48
  return {
49
49
  id: contract.id,
@@ -57,7 +57,7 @@ function summarizeComponentContract(component) {
57
57
  behavior: contract.behavior || [],
58
58
  approvals: contract.approvals || [],
59
59
  dependencies: contract.dependencies || [],
60
- source_path: sourcePath(component)
60
+ source_path: sourcePath(widget)
61
61
  };
62
62
  }
63
63
 
@@ -136,7 +136,7 @@ function checkRecord({
136
136
  message,
137
137
  projection,
138
138
  sourceProjection,
139
- component,
139
+ widget,
140
140
  usage,
141
141
  prop = null,
142
142
  event = null,
@@ -149,7 +149,7 @@ function checkRecord({
149
149
  message,
150
150
  projection: projection?.id || null,
151
151
  source_projection: sourceProjection?.id || null,
152
- component: component?.id || usage.component?.id || null,
152
+ widget: widget?.id || usage.widget?.id || null,
153
153
  screen: usage.screenId || null,
154
154
  region: usage.region || null,
155
155
  prop,
@@ -159,20 +159,20 @@ function checkRecord({
159
159
  };
160
160
  }
161
161
 
162
- function componentUsageKey(projection, sourceProjection, usage, index) {
162
+ function widgetUsageKey(projection, sourceProjection, usage, index) {
163
163
  return [
164
164
  projection.id,
165
165
  sourceProjection?.id || projection.id,
166
166
  usage.screenId || "screen",
167
167
  usage.region || "region",
168
- usage.component?.id || "component",
168
+ usage.widget?.id || "widget",
169
169
  String(index)
170
170
  ].join(":");
171
171
  }
172
172
 
173
- function collectUsageChecks({ graph, projection, sourceProjection, usage, component }) {
173
+ function collectUsageChecks({ graph, projection, sourceProjection, usage, widget }) {
174
174
  const checks = [];
175
- const contract = componentContract(component);
175
+ const contract = widgetContract(widget);
176
176
  const props = contract?.props || [];
177
177
  const events = contract?.events || [];
178
178
  const propNames = new Set(props.map((prop) => prop.name));
@@ -183,71 +183,71 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
183
183
  const regionKeys = projectionRegionKeys(graph, sourceProjection);
184
184
  const realizedIds = projectionContextRealizesIds(graph, sourceProjection);
185
185
 
186
- if (!component) {
186
+ if (!widget) {
187
187
  checks.push(checkRecord({
188
- code: "component_missing",
188
+ code: "widget_missing",
189
189
  severity: "error",
190
- message: `Component '${usage.component?.id || "(missing)"}' could not be resolved.`,
190
+ message: `Widget '${usage.widget?.id || "(missing)"}' could not be resolved.`,
191
191
  projection,
192
192
  sourceProjection,
193
- component,
193
+ widget,
194
194
  usage,
195
- suggestedFix: "Create the component or update the projection ui_components binding."
195
+ suggestedFix: "Create the widget or update the projection widget_bindings binding."
196
196
  }));
197
197
  return checks;
198
198
  }
199
199
 
200
- if (projection.status === "active" && component.status && component.status !== "active") {
200
+ if (projection.status === "active" && widget.status && widget.status !== "active") {
201
201
  checks.push(checkRecord({
202
- code: "component_status_not_active",
202
+ code: "widget_status_not_active",
203
203
  severity: "warning",
204
- message: `Active projection '${projection.id}' uses component '${component.id}' with status '${component.status}'.`,
204
+ message: `Active projection '${projection.id}' uses widget '${widget.id}' with status '${widget.status}'.`,
205
205
  projection,
206
206
  sourceProjection,
207
- component,
207
+ widget,
208
208
  usage,
209
- suggestedFix: "Promote the component to active or move the usage behind an explicit review boundary."
209
+ suggestedFix: "Promote the widget to active or move the usage behind an explicit review boundary."
210
210
  }));
211
211
  }
212
212
 
213
213
  if (!screens.has(usage.screenId)) {
214
214
  checks.push(checkRecord({
215
- code: "component_usage_screen_missing",
215
+ code: "widget_usage_screen_missing",
216
216
  severity: "error",
217
- message: `Component usage references missing screen '${usage.screenId}'.`,
217
+ message: `Widget usage references missing screen '${usage.screenId}'.`,
218
218
  projection,
219
219
  sourceProjection,
220
- component,
220
+ widget,
221
221
  usage,
222
- suggestedFix: "Add the screen to ui_screens or update the component usage screen id."
222
+ suggestedFix: "Add the screen to screens or update the widget usage screen id."
223
223
  }));
224
224
  }
225
225
 
226
226
  if (!regionKeys.has(`${usage.screenId}:${usage.region}`)) {
227
227
  checks.push(checkRecord({
228
- code: "component_usage_region_missing",
228
+ code: "widget_usage_region_missing",
229
229
  severity: "error",
230
- message: `Component usage references undeclared region '${usage.region}' on screen '${usage.screenId}'.`,
230
+ message: `Widget usage references undeclared region '${usage.region}' on screen '${usage.screenId}'.`,
231
231
  projection,
232
232
  sourceProjection,
233
- component,
233
+ widget,
234
234
  usage,
235
- suggestedFix: "Add the region to ui_screen_regions or update the component usage region."
235
+ suggestedFix: "Add the region to screen_regions or update the widget usage region."
236
236
  }));
237
237
  }
238
238
 
239
239
  for (const prop of props.filter((entry) => entry.requiredness === "required")) {
240
240
  if (!boundProps.has(prop.name)) {
241
241
  checks.push(checkRecord({
242
- code: "component_required_prop_missing",
242
+ code: "widget_required_prop_missing",
243
243
  severity: "error",
244
- message: `Required prop '${prop.name}' is not bound for component '${component.id}'.`,
244
+ message: `Required prop '${prop.name}' is not bound for widget '${widget.id}'.`,
245
245
  projection,
246
246
  sourceProjection,
247
- component,
247
+ widget,
248
248
  usage,
249
249
  prop: prop.name,
250
- suggestedFix: `Add 'data ${prop.name} from <source>' to the projection ui_components entry.`
250
+ suggestedFix: `Add 'data ${prop.name} from <source>' to the projection widget_bindings entry.`
251
251
  }));
252
252
  }
253
253
  }
@@ -255,25 +255,25 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
255
255
  for (const binding of usage.dataBindings || []) {
256
256
  if (!propNames.has(binding.prop)) {
257
257
  checks.push(checkRecord({
258
- code: "component_prop_unknown",
258
+ code: "widget_prop_unknown",
259
259
  severity: "error",
260
- message: `Prop '${binding.prop}' is not declared by component '${component.id}'.`,
260
+ message: `Prop '${binding.prop}' is not declared by widget '${widget.id}'.`,
261
261
  projection,
262
262
  sourceProjection,
263
- component,
263
+ widget,
264
264
  usage,
265
265
  prop: binding.prop || null,
266
- suggestedFix: "Declare the prop on the component or update the projection binding."
266
+ suggestedFix: "Declare the prop on the widget or update the projection binding."
267
267
  }));
268
268
  }
269
269
  if (!binding.source?.id || !statements.has(binding.source.id)) {
270
270
  checks.push(checkRecord({
271
- code: "component_data_source_missing",
271
+ code: "widget_data_source_missing",
272
272
  severity: "error",
273
273
  message: `Data binding for prop '${binding.prop}' references a missing source.`,
274
274
  projection,
275
275
  sourceProjection,
276
- component,
276
+ widget,
277
277
  usage,
278
278
  prop: binding.prop || null,
279
279
  suggestedFix: "Bind the prop to an existing capability, projection, shape, or entity."
@@ -284,26 +284,26 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
284
284
  for (const binding of usage.eventBindings || []) {
285
285
  if (!eventNames.has(binding.event)) {
286
286
  checks.push(checkRecord({
287
- code: "component_event_unknown",
287
+ code: "widget_event_unknown",
288
288
  severity: "error",
289
- message: `Event '${binding.event}' is not declared by component '${component.id}'.`,
289
+ message: `Event '${binding.event}' is not declared by widget '${widget.id}'.`,
290
290
  projection,
291
291
  sourceProjection,
292
- component,
292
+ widget,
293
293
  usage,
294
294
  event: binding.event || null,
295
- suggestedFix: "Declare the event on the component or update the projection binding."
295
+ suggestedFix: "Declare the event on the widget or update the projection binding."
296
296
  }));
297
297
  }
298
298
  if (binding.action === "navigate") {
299
299
  if (!screens.has(binding.target?.id)) {
300
300
  checks.push(checkRecord({
301
- code: "component_event_navigation_target_missing",
301
+ code: "widget_event_navigation_target_missing",
302
302
  severity: "error",
303
303
  message: `Event '${binding.event}' navigates to missing screen '${binding.target?.id || "(missing)"}'.`,
304
304
  projection,
305
305
  sourceProjection,
306
- component,
306
+ widget,
307
307
  usage,
308
308
  event: binding.event || null,
309
309
  suggestedFix: "Add the target screen or update the event navigation target."
@@ -313,24 +313,24 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
313
313
  const target = binding.target?.id ? statements.get(binding.target.id) : null;
314
314
  if (!target || target.kind !== "capability") {
315
315
  checks.push(checkRecord({
316
- code: "component_event_action_missing",
316
+ code: "widget_event_action_missing",
317
317
  severity: "error",
318
318
  message: `Event '${binding.event}' targets missing capability '${binding.target?.id || "(missing)"}'.`,
319
319
  projection,
320
320
  sourceProjection,
321
- component,
321
+ widget,
322
322
  usage,
323
323
  event: binding.event || null,
324
324
  suggestedFix: "Bind the event to an existing capability."
325
325
  }));
326
326
  } else if (!realizedIds.has(target.id)) {
327
327
  checks.push(checkRecord({
328
- code: "component_event_action_not_in_projection",
328
+ code: "widget_event_action_not_in_projection",
329
329
  severity: "error",
330
330
  message: `Event '${binding.event}' targets capability '${target.id}', but projection '${sourceProjection.id}' does not realize it through its UI context.`,
331
331
  projection,
332
332
  sourceProjection,
333
- component,
333
+ widget,
334
334
  usage,
335
335
  event: binding.event || null,
336
336
  suggestedFix: `Add '${target.id}' to projection '${sourceProjection.id}' or an inherited shared projection realizes list, or choose a capability already in this projection context.`
@@ -338,12 +338,12 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
338
338
  }
339
339
  } else {
340
340
  checks.push(checkRecord({
341
- code: "component_event_action_unsupported",
341
+ code: "widget_event_action_unsupported",
342
342
  severity: "error",
343
343
  message: `Event '${binding.event}' uses unsupported action '${binding.action}'.`,
344
344
  projection,
345
345
  sourceProjection,
346
- component,
346
+ widget,
347
347
  usage,
348
348
  event: binding.event || null,
349
349
  suggestedFix: "Use 'navigate' or 'action'."
@@ -355,12 +355,12 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
355
355
  const stateProp = behavior.directives?.state;
356
356
  if (stateProp && !propNames.has(stateProp)) {
357
357
  checks.push(checkRecord({
358
- code: "component_behavior_prop_missing",
358
+ code: "widget_behavior_prop_missing",
359
359
  severity: "error",
360
360
  message: `Behavior '${behavior.kind}' references missing prop '${stateProp}'.`,
361
361
  projection,
362
362
  sourceProjection,
363
- component,
363
+ widget,
364
364
  usage,
365
365
  prop: stateProp,
366
366
  behavior: behavior.kind,
@@ -373,12 +373,12 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
373
373
  for (const eventName of emits) {
374
374
  if (!eventNames.has(eventName)) {
375
375
  checks.push(checkRecord({
376
- code: "component_behavior_event_missing",
376
+ code: "widget_behavior_event_missing",
377
377
  severity: "error",
378
378
  message: `Behavior '${behavior.kind}' references missing event '${eventName}'.`,
379
379
  projection,
380
380
  sourceProjection,
381
- component,
381
+ widget,
382
382
  usage,
383
383
  event: eventName,
384
384
  behavior: behavior.kind,
@@ -388,16 +388,16 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
388
388
  }
389
389
  if (!(usage.eventBindings || []).some((binding) => binding.event === eventName)) {
390
390
  checks.push(checkRecord({
391
- code: "component_behavior_event_unbound",
391
+ code: "widget_behavior_event_unbound",
392
392
  severity: "warning",
393
393
  message: `Behavior '${behavior.kind}' emits event '${eventName}', but this projection usage does not bind that event to navigation or an action.`,
394
394
  projection,
395
395
  sourceProjection,
396
- component,
396
+ widget,
397
397
  usage,
398
398
  event: eventName,
399
399
  behavior: behavior.kind,
400
- suggestedFix: `Add 'event ${eventName} navigate <screen>' or 'event ${eventName} action <capability>' to the projection ui_components entry.`
400
+ suggestedFix: `Add 'event ${eventName} navigate <screen>' or 'event ${eventName} action <capability>' to the projection widget_bindings entry.`
401
401
  }));
402
402
  }
403
403
  }
@@ -409,16 +409,16 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
409
409
  if (eventNames.has(actionTarget)) {
410
410
  if (!(usage.eventBindings || []).some((binding) => binding.event === actionTarget)) {
411
411
  checks.push(checkRecord({
412
- code: "component_behavior_action_unbound",
412
+ code: "widget_behavior_action_unbound",
413
413
  severity: "warning",
414
414
  message: `Behavior '${behavior.kind}' declares action event '${actionTarget}', but this projection usage does not bind that event to navigation or an action.`,
415
415
  projection,
416
416
  sourceProjection,
417
- component,
417
+ widget,
418
418
  usage,
419
419
  event: actionTarget,
420
420
  behavior: behavior.kind,
421
- suggestedFix: `Add 'event ${actionTarget} action <capability>' or 'event ${actionTarget} navigate <screen>' to the projection ui_components entry.`
421
+ suggestedFix: `Add 'event ${actionTarget} action <capability>' or 'event ${actionTarget} navigate <screen>' to the projection widget_bindings entry.`
422
422
  }));
423
423
  }
424
424
  continue;
@@ -427,12 +427,12 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
427
427
  const target = statements.get(actionTarget);
428
428
  if (!target || target.kind !== "capability") {
429
429
  checks.push(checkRecord({
430
- code: "component_behavior_action_missing",
430
+ code: "widget_behavior_action_missing",
431
431
  severity: "error",
432
432
  message: `Behavior '${behavior.kind}' references missing capability action '${actionTarget}'.`,
433
433
  projection,
434
434
  sourceProjection,
435
- component,
435
+ widget,
436
436
  usage,
437
437
  behavior: behavior.kind,
438
438
  suggestedFix: "Update the behavior directive or declare the referenced capability."
@@ -441,12 +441,12 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
441
441
  }
442
442
  if (!realizedIds.has(actionTarget)) {
443
443
  checks.push(checkRecord({
444
- code: "component_behavior_action_not_in_projection",
444
+ code: "widget_behavior_action_not_in_projection",
445
445
  severity: "error",
446
446
  message: `Behavior '${behavior.kind}' references capability '${actionTarget}', but projection '${sourceProjection.id}' does not realize it.`,
447
447
  projection,
448
448
  sourceProjection,
449
- component,
449
+ widget,
450
450
  usage,
451
451
  behavior: behavior.kind,
452
452
  suggestedFix: `Add '${actionTarget}' to projection '${sourceProjection.id}' realizes or choose a capability already in this projection context.`
@@ -458,15 +458,15 @@ function collectUsageChecks({ graph, projection, sourceProjection, usage, compon
458
458
  binding.target?.kind === "capability"
459
459
  )) {
460
460
  checks.push(checkRecord({
461
- code: "component_behavior_action_unbound",
461
+ code: "widget_behavior_action_unbound",
462
462
  severity: "warning",
463
- message: `Behavior '${behavior.kind}' declares capability action '${actionTarget}', but this projection usage does not bind any component event to that capability.`,
463
+ message: `Behavior '${behavior.kind}' declares capability action '${actionTarget}', but this projection usage does not bind any widget event to that capability.`,
464
464
  projection,
465
465
  sourceProjection,
466
- component,
466
+ widget,
467
467
  usage,
468
468
  behavior: behavior.kind,
469
- suggestedFix: `Add 'event <component_event> action ${actionTarget}' to the projection ui_components entry.`
469
+ suggestedFix: `Add 'event <widget_event> action ${actionTarget}' to the projection widget_bindings entry.`
470
470
  }));
471
471
  }
472
472
  }
@@ -507,35 +507,36 @@ function candidateProjections(graph, projectionId) {
507
507
  return [...direct, ...inherited].sort((a, b) => a.id.localeCompare(b.id));
508
508
  }
509
509
 
510
- function relatedVerificationFiles(graph, componentIds, projectionIds) {
511
- const ids = new Set([...componentIds, ...projectionIds]);
510
+ function relatedVerificationFiles(graph, widgetIds, projectionIds) {
511
+ const ids = new Set([...widgetIds, ...projectionIds]);
512
512
  return stableUnique((graph.byKind.verification || [])
513
513
  .filter((verification) => (verification.validates || []).some((ref) => ids.has(ref.id)))
514
514
  .map((verification) => sourcePath(verification)));
515
515
  }
516
516
 
517
- export function generateComponentConformanceReport(graph, options = {}) {
518
- const components = byId(graph.byKind.component || []);
519
- if (options.componentId && !components.has(options.componentId)) {
520
- throw new Error(`No component found with id '${options.componentId}'`);
517
+ export function generateWidgetConformanceReport(graph, options = {}) {
518
+ const selectedWidgetId = options.widgetId || options.componentId || null;
519
+ const widgets = byId(graph.byKind.widget || []);
520
+ if (selectedWidgetId && !widgets.has(selectedWidgetId)) {
521
+ throw new Error(`No widget found with id '${selectedWidgetId}'`);
521
522
  }
522
523
 
523
524
  const projectionUsageRecords = [];
524
525
  const checks = [];
525
- const referencedComponentIds = new Set();
526
+ const referencedWidgetIds = new Set();
526
527
  const affectedProjectionIds = new Set();
527
528
 
528
529
  for (const projection of candidateProjections(graph, options.projectionId)) {
529
530
  for (const entry of projectionUsageEntries(graph, projection)) {
530
- const componentId = entry.usage.component?.id || null;
531
- if (options.componentId && componentId !== options.componentId) {
531
+ const widgetId = entry.usage.widget?.id || null;
532
+ if (selectedWidgetId && widgetId !== selectedWidgetId) {
532
533
  continue;
533
534
  }
534
- const component = componentId ? components.get(componentId) : null;
535
- if (componentId) referencedComponentIds.add(componentId);
535
+ const widget = widgetId ? widgets.get(widgetId) : null;
536
+ if (widgetId) referencedWidgetIds.add(widgetId);
536
537
  affectedProjectionIds.add(entry.projection.id);
537
538
  if (entry.sourceProjection?.id) affectedProjectionIds.add(entry.sourceProjection.id);
538
- const usageChecks = collectUsageChecks({ graph, projection: entry.projection, sourceProjection: entry.sourceProjection, usage: entry.usage, component });
539
+ const usageChecks = collectUsageChecks({ graph, projection: entry.projection, sourceProjection: entry.sourceProjection, usage: entry.usage, widget });
539
540
  checks.push(...usageChecks);
540
541
  const outcome = usageChecks.some((check) => check.severity === "error")
541
542
  ? "error"
@@ -543,7 +544,7 @@ export function generateComponentConformanceReport(graph, options = {}) {
543
544
  ? "warning"
544
545
  : "pass";
545
546
  projectionUsageRecords.push({
546
- key: componentUsageKey(entry.projection, entry.sourceProjection, entry.usage, entry.index),
547
+ key: widgetUsageKey(entry.projection, entry.sourceProjection, entry.usage, entry.index),
547
548
  projection: summarizeProjection(entry.projection),
548
549
  source_projection: entry.sourceProjection.id === entry.projection.id ? null : summarizeProjection(entry.sourceProjection),
549
550
  screen: {
@@ -552,29 +553,29 @@ export function generateComponentConformanceReport(graph, options = {}) {
552
553
  title: projectionScreenMap(graph, entry.sourceProjection).get(entry.usage.screenId)?.title || null
553
554
  },
554
555
  region: entry.usage.region || null,
555
- component: summarizeComponent(component) || { id: componentId, name: componentId, category: null, version: null, status: null, source_path: null },
556
+ widget: summarizeWidget(widget) || { id: widgetId, name: widgetId, category: null, version: null, status: null, source_path: null },
556
557
  data_bindings: entry.usage.dataBindings || [],
557
558
  event_bindings: entry.usage.eventBindings || [],
558
- behavior_realizations: buildComponentBehaviorRealizations(componentContract(component), entry.usage),
559
+ behavior_realizations: buildWidgetBehaviorRealizations(widgetContract(widget), entry.usage),
559
560
  outcome,
560
561
  check_codes: usageChecks.map((check) => check.code)
561
562
  });
562
563
  }
563
564
  }
564
565
 
565
- const componentFiles = stableUnique([...referencedComponentIds].map((id) => sourcePath(components.get(id))));
566
+ const widgetFiles = stableUnique([...referencedWidgetIds].map((id) => sourcePath(widgets.get(id))));
566
567
  const projectionFiles = stableUnique(
567
568
  [...affectedProjectionIds].map((id) => sourcePath((graph.byKind.projection || []).find((projection) => projection.id === id)))
568
569
  );
569
- const verificationFiles = relatedVerificationFiles(graph, referencedComponentIds, affectedProjectionIds);
570
+ const verificationFiles = relatedVerificationFiles(graph, referencedWidgetIds, affectedProjectionIds);
570
571
  const errors = checks.filter((check) => check.severity === "error");
571
572
  const warnings = checks.filter((check) => check.severity === "warning");
572
573
 
573
574
  return {
574
- type: "component_conformance_report",
575
+ type: "widget_conformance_report",
575
576
  filters: {
576
577
  projection: options.projectionId || null,
577
- component: options.componentId || null
578
+ widget: selectedWidgetId
578
579
  },
579
580
  summary: {
580
581
  total_usages: projectionUsageRecords.length,
@@ -584,22 +585,22 @@ export function generateComponentConformanceReport(graph, options = {}) {
584
585
  warnings: warnings.length,
585
586
  errors: errors.length,
586
587
  affected_projections: stableUnique([...affectedProjectionIds]),
587
- affected_components: stableUnique([...referencedComponentIds])
588
+ affected_widgets: stableUnique([...referencedWidgetIds])
588
589
  },
589
590
  projection_usages: projectionUsageRecords,
590
591
  checks,
591
- component_contracts: stableUnique([...referencedComponentIds])
592
- .map((id) => summarizeComponentContract(components.get(id)))
592
+ widget_contracts: stableUnique([...referencedWidgetIds])
593
+ .map((id) => summarizeWidgetContract(widgets.get(id)))
593
594
  .filter(Boolean),
594
595
  write_scope: {
595
- component_files: componentFiles,
596
+ widget_files: widgetFiles,
596
597
  projection_files: projectionFiles,
597
598
  verification_files: verificationFiles,
598
- paths: stableUnique([...componentFiles, ...projectionFiles, ...verificationFiles])
599
+ paths: stableUnique([...widgetFiles, ...projectionFiles, ...verificationFiles])
599
600
  },
600
601
  impact: {
601
602
  projections: stableUnique([...affectedProjectionIds]),
602
- components: stableUnique([...referencedComponentIds])
603
+ widgets: stableUnique([...referencedWidgetIds])
603
604
  }
604
605
  };
605
606
  }
@@ -644,9 +645,9 @@ function effectTypesFromBehavior(behavior) {
644
645
  function checksForBehavior(conformanceReport, usage, behavior) {
645
646
  return (conformanceReport.checks || [])
646
647
  .filter((check) =>
647
- check.code?.startsWith("component_behavior_") &&
648
+ check.code?.startsWith("widget_behavior_") &&
648
649
  check.projection === usage.projection?.id &&
649
- check.component === usage.component?.id &&
650
+ check.widget === usage.widget?.id &&
650
651
  check.screen === usage.screen?.id &&
651
652
  check.region === usage.region &&
652
653
  (!check.behavior || check.behavior === behavior.kind)
@@ -667,29 +668,29 @@ function behaviorHighlights(behaviorRows) {
667
668
  if (row.behavior.status === "partial") {
668
669
  highlights.push({
669
670
  severity: "warning",
670
- code: "component_behavior_partial",
671
- message: `Behavior '${row.behavior.kind}' is partially realized for component '${row.component.id}' on screen '${row.screen.id}'.`,
671
+ code: "widget_behavior_partial",
672
+ message: `Behavior '${row.behavior.kind}' is partially realized for widget '${row.widget.id}' on screen '${row.screen.id}'.`,
672
673
  projection: row.projection.id,
673
- component: row.component.id,
674
+ widget: row.widget.id,
674
675
  screen: row.screen.id,
675
676
  region: row.region,
676
677
  behavior: row.behavior.kind,
677
- suggested_fix: "Bind the required behavior data, events, or capability actions in the projection ui_components entry."
678
+ suggested_fix: "Bind the required behavior data, events, or capability actions in the projection widget_bindings entry."
678
679
  });
679
680
  }
680
681
  for (const emittedEvent of row.emits || []) {
681
682
  if (!emittedEvent.bound) {
682
683
  highlights.push({
683
684
  severity: "warning",
684
- code: "component_behavior_event_unbound",
685
- message: `Behavior '${row.behavior.kind}' emits event '${emittedEvent.event}', but this component usage does not bind it.`,
685
+ code: "widget_behavior_event_unbound",
686
+ message: `Behavior '${row.behavior.kind}' emits event '${emittedEvent.event}', but this widget usage does not bind it.`,
686
687
  projection: row.projection.id,
687
- component: row.component.id,
688
+ widget: row.widget.id,
688
689
  screen: row.screen.id,
689
690
  region: row.region,
690
691
  event: emittedEvent.event || null,
691
692
  behavior: row.behavior.kind,
692
- suggested_fix: `Add 'event ${emittedEvent.event} navigate <screen>' or 'event ${emittedEvent.event} action <capability>' to the projection ui_components entry.`
693
+ suggested_fix: `Add 'event ${emittedEvent.event} navigate <screen>' or 'event ${emittedEvent.event} action <capability>' to the projection widget_bindings entry.`
693
694
  });
694
695
  }
695
696
  }
@@ -698,18 +699,18 @@ function behaviorHighlights(behaviorRows) {
698
699
  const target = action.capability?.id || action.event || "(unknown)";
699
700
  highlights.push({
700
701
  severity: "warning",
701
- code: "component_behavior_action_unbound",
702
- message: `Behavior '${row.behavior.kind}' declares action '${target}', but this component usage does not bind it.`,
702
+ code: "widget_behavior_action_unbound",
703
+ message: `Behavior '${row.behavior.kind}' declares action '${target}', but this widget usage does not bind it.`,
703
704
  projection: row.projection.id,
704
- component: row.component.id,
705
+ widget: row.widget.id,
705
706
  screen: row.screen.id,
706
707
  region: row.region,
707
708
  event: action.event || null,
708
709
  capability: action.capability?.id || null,
709
710
  behavior: row.behavior.kind,
710
711
  suggested_fix: action.capability?.id
711
- ? `Add 'event <component_event> action ${action.capability.id}' to the projection ui_components entry.`
712
- : `Add 'event ${action.event} action <capability>' or 'event ${action.event} navigate <screen>' to the projection ui_components entry.`
712
+ ? `Add 'event <widget_event> action ${action.capability.id}' to the projection widget_bindings entry.`
713
+ : `Add 'event ${action.event} action <capability>' or 'event ${action.event} navigate <screen>' to the projection widget_bindings entry.`
713
714
  });
714
715
  }
715
716
  }
@@ -739,8 +740,8 @@ function groupBehaviorRows(rows, keyFn, itemFn = null) {
739
740
  }));
740
741
  }
741
742
 
742
- export function generateComponentBehaviorReport(graph, options = {}) {
743
- const conformanceReport = generateComponentConformanceReport(graph, options);
743
+ export function generateWidgetBehaviorReport(graph, options = {}) {
744
+ const conformanceReport = generateWidgetConformanceReport(graph, options);
744
745
  const behaviorRows = [];
745
746
 
746
747
  for (const usage of conformanceReport.projection_usages || []) {
@@ -752,7 +753,7 @@ export function generateComponentBehaviorReport(graph, options = {}) {
752
753
  source_projection: usage.source_projection,
753
754
  screen: usage.screen,
754
755
  region: usage.region,
755
- component: usage.component,
756
+ widget: usage.widget,
756
757
  behavior: {
757
758
  kind: behavior.kind || null,
758
759
  source: behavior.source || null,
@@ -775,7 +776,7 @@ export function generateComponentBehaviorReport(graph, options = {}) {
775
776
  const affectedCapabilities = stableUnique(behaviorRows.flatMap((row) => row.capabilities));
776
777
 
777
778
  return {
778
- type: "component_behavior_report",
779
+ type: "widget_behavior_report",
779
780
  filters: conformanceReport.filters,
780
781
  summary: {
781
782
  total_usages: conformanceReport.summary.total_usages,
@@ -785,14 +786,14 @@ export function generateComponentBehaviorReport(graph, options = {}) {
785
786
  declared: behaviorRows.filter((row) => row.behavior.status === "declared").length,
786
787
  warnings: conformanceReport.summary.warnings,
787
788
  errors: conformanceReport.summary.errors,
788
- affected_components: conformanceReport.summary.affected_components,
789
+ affected_widgets: conformanceReport.summary.affected_widgets,
789
790
  affected_projections: conformanceReport.summary.affected_projections,
790
791
  affected_capabilities: affectedCapabilities
791
792
  },
792
793
  groups: {
793
- components: groupBehaviorRows(
794
+ widgets: groupBehaviorRows(
794
795
  behaviorRows,
795
- (row) => [row.component.id].filter(Boolean),
796
+ (row) => [row.widget.id].filter(Boolean),
796
797
  (row) => row.key
797
798
  ),
798
799
  screens: groupBehaviorRows(
@@ -813,11 +814,11 @@ export function generateComponentBehaviorReport(graph, options = {}) {
813
814
  },
814
815
  behaviors: behaviorRows,
815
816
  highlights,
816
- checks: conformanceReport.checks.filter((check) => check.code?.startsWith("component_behavior_")),
817
+ checks: conformanceReport.checks.filter((check) => check.code?.startsWith("widget_behavior_")),
817
818
  write_scope: conformanceReport.write_scope,
818
819
  impact: {
819
820
  projections: conformanceReport.impact.projections,
820
- components: conformanceReport.impact.components,
821
+ widgets: conformanceReport.impact.widgets,
821
822
  capabilities: affectedCapabilities
822
823
  }
823
824
  };