@topogram/cli 0.3.54 → 0.3.56

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 (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +1 -1
  3. package/src/adoption/review-groups.js +3 -3
  4. package/src/agent-ops/query-builders.js +34 -34
  5. package/src/archive/schema.js +1 -1
  6. package/src/cli.js +173 -20
  7. package/src/generator/adapters.js +23 -7
  8. package/src/generator/context/domain-coverage.js +2 -2
  9. package/src/generator/context/shared.js +6 -22
  10. package/src/generator/context/slice.js +10 -10
  11. package/src/generator/docs.js +1 -1
  12. package/src/generator/registry.js +1 -1
  13. package/src/generator/runtime/app-bundle.js +8 -6
  14. package/src/generator/runtime/compile-check.js +7 -5
  15. package/src/generator/runtime/deployment.js +6 -4
  16. package/src/generator/runtime/environment.js +31 -28
  17. package/src/generator/runtime/shared.js +53 -39
  18. package/src/generator/surfaces/contracts.js +1 -1
  19. package/src/generator/surfaces/databases/index.js +5 -3
  20. package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
  21. package/src/generator/surfaces/databases/postgres/drizzle.js +1 -1
  22. package/src/generator/surfaces/databases/postgres/prisma.js +1 -1
  23. package/src/generator/surfaces/databases/shared.js +3 -3
  24. package/src/generator/surfaces/databases/sqlite/prisma.js +1 -1
  25. package/src/generator/surfaces/index.js +5 -3
  26. package/src/generator/surfaces/services/index.js +10 -6
  27. package/src/generator/surfaces/services/persistence-wiring.js +1 -1
  28. package/src/generator/surfaces/services/server-contract.js +1 -1
  29. package/src/generator/surfaces/shared.js +1 -1
  30. package/src/generator/surfaces/web/index.js +5 -3
  31. package/src/generator/surfaces/web/ui-surface-contract.js +1 -1
  32. package/src/generator/widget-conformance.js +6 -6
  33. package/src/generator/widgets.js +1 -1
  34. package/src/generator-policy.js +10 -10
  35. package/src/import/core/runner.js +60 -50
  36. package/src/project-config.js +5 -42
  37. package/src/proofs/contract-audit.js +1 -1
  38. package/src/realization/backend/build-backend-runtime-realization.js +3 -3
  39. package/src/realization/ui/build-ui-shared-realization.js +9 -9
  40. package/src/realization/ui/build-web-realization.js +3 -3
  41. package/src/reconcile/journeys.js +1 -1
  42. package/src/resolver/enrich/widget.js +2 -2
  43. package/src/resolver/index.js +4 -23
  44. package/src/validator/index.js +10 -10
  45. package/src/workflows.js +49 -49
@@ -27,8 +27,10 @@ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../proje
27
27
  * @property {string|null} [api]
28
28
  * @property {string|null} [database]
29
29
  * @property {Record<string, string>} [env]
30
- * @property {RuntimeComponent|null} [apiComponent]
31
- * @property {RuntimeComponent|null} [databaseComponent]
30
+ * @property {RuntimeComponent|null} [apiRuntime]
31
+ * @property {RuntimeComponent|null} [databaseRuntime]
32
+ * @property {RuntimeComponent|null} [apiComponent] Legacy adapter alias for apiRuntime.
33
+ * @property {RuntimeComponent|null} [databaseComponent] Legacy adapter alias for databaseRuntime.
32
34
  */
33
35
 
34
36
  /**
@@ -39,10 +41,13 @@ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../proje
39
41
  * @typedef {Object} RuntimeTopology
40
42
  * @property {import("../../project-config.js").ProjectConfig} config
41
43
  * @property {RuntimeComponent[]} runtimes
42
- * @property {RuntimeComponent[]} components
43
- * @property {RuntimeComponent[]} apiComponents
44
- * @property {RuntimeComponent[]} webComponents
45
- * @property {RuntimeComponent[]} dbComponents
44
+ * @property {RuntimeComponent[]} apiRuntimes
45
+ * @property {RuntimeComponent[]} webRuntimes
46
+ * @property {RuntimeComponent[]} dbRuntimes
47
+ * @property {RuntimeComponent[]} components Legacy alias for runtimes.
48
+ * @property {RuntimeComponent[]} apiComponents Legacy alias for apiRuntimes.
49
+ * @property {RuntimeComponent[]} webComponents Legacy alias for webRuntimes.
50
+ * @property {RuntimeComponent[]} dbComponents Legacy alias for dbRuntimes.
46
51
  * @property {RuntimeComponent|null} primaryApi
47
52
  * @property {RuntimeComponent|null} primaryWeb
48
53
  * @property {RuntimeComponent|null} primaryDb
@@ -65,7 +70,8 @@ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../proje
65
70
  * @property {string} [dbProjectionId]
66
71
  * @property {string} [configDir]
67
72
  * @property {string} [projectRoot]
68
- * @property {RuntimeComponent} [component]
73
+ * @property {RuntimeComponent} [runtime]
74
+ * @property {RuntimeComponent} [component] Legacy alias for runtime.
69
75
  */
70
76
 
71
77
  /**
@@ -229,7 +235,7 @@ function apiProjectionCandidates(graph) {
229
235
  */
230
236
  function uiWebProjectionCandidates(graph) {
231
237
  return (graph.byKind.projection || []).filter(
232
- (projection) => projection.platform === "web_surface" && (projection.uiRoutes || []).length > 0
238
+ (projection) => projection.type === "web_surface" && (projection.uiRoutes || []).length > 0
233
239
  );
234
240
  }
235
241
 
@@ -247,7 +253,7 @@ const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["proj_ios_surface__swiftui"];
247
253
  */
248
254
  function uiIosProjectionCandidates(graph) {
249
255
  return (graph.byKind.projection || []).filter(
250
- (projection) => projection.platform === "ios_surface" && (projection.uiRoutes || []).length > 0
256
+ (projection) => projection.type === "ios_surface" && (projection.uiRoutes || []).length > 0
251
257
  );
252
258
  }
253
259
 
@@ -300,7 +306,7 @@ export function pickDefaultUiWebProjection(graph) {
300
306
  */
301
307
  export function getDefaultEnvironmentProjections(graph, options = {}) {
302
308
  const topology = resolveRuntimeTopology(graph, options);
303
- const dbCandidates = graph.byKind.projection?.filter((projection) => ["db_contract", "db_contract"].includes(projection.platform)) || [];
309
+ const dbCandidates = graph.byKind.projection?.filter((projection) => ["db_contract", "db_contract"].includes(projection.type)) || [];
304
310
  const apiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryApi?.projection ||
305
311
  (options.projectionId ? getProjection(graph, options.projectionId) : null) ||
306
312
  apiProjectionCandidates(graph).find((projection) => projection.id === "proj_api") ||
@@ -327,14 +333,15 @@ export function getDefaultEnvironmentProjections(graph, options = {}) {
327
333
  */
328
334
  export function generateServerBundle(graph, projectionId, options = {}) {
329
335
  const topology = resolveRuntimeTopology(graph, options);
330
- const component = options.component || topology.apiComponents.find((entry) => entry.projection.id === projectionId);
331
- if (!component) {
332
- throw new Error(`No api topology component found for projection '${projectionId}'`);
336
+ const runtime = options.runtime || options.component || topology.apiRuntimes.find((entry) => entry.projection.id === projectionId);
337
+ if (!runtime) {
338
+ throw new Error(`No api runtime found for projection '${projectionId}'`);
333
339
  }
334
340
  return generateWithComponentGenerator({
335
341
  graph,
336
- projection: component.projection,
337
- component,
342
+ projection: runtime.projection,
343
+ runtime,
344
+ component: runtime,
338
345
  topology,
339
346
  implementation: options.implementation || null,
340
347
  options: { ...options, projectionId }
@@ -349,14 +356,15 @@ export function generateServerBundle(graph, projectionId, options = {}) {
349
356
  */
350
357
  export function generateWebBundle(graph, projectionId, options = {}) {
351
358
  const topology = resolveRuntimeTopology(graph, options);
352
- const component = options.component || topology.webComponents.find((entry) => entry.projection.id === projectionId);
353
- if (!component) {
354
- throw new Error(`No web topology component found for projection '${projectionId}'`);
359
+ const runtime = options.runtime || options.component || topology.webRuntimes.find((entry) => entry.projection.id === projectionId);
360
+ if (!runtime) {
361
+ throw new Error(`No web runtime found for projection '${projectionId}'`);
355
362
  }
356
363
  return generateWithComponentGenerator({
357
364
  graph,
358
- projection: component.projection,
359
- component,
365
+ projection: runtime.projection,
366
+ runtime,
367
+ component: runtime,
360
368
  topology,
361
369
  implementation: options.implementation || null,
362
370
  options: { ...options, projectionId }
@@ -371,14 +379,15 @@ export function generateWebBundle(graph, projectionId, options = {}) {
371
379
  */
372
380
  export function generateDbBundle(graph, projectionId, options = {}) {
373
381
  const topology = resolveRuntimeTopology(graph, options);
374
- const component = options.component || topology.dbComponents.find((entry) => entry.projection.id === projectionId);
375
- if (!component) {
376
- throw new Error(`No database topology component found for projection '${projectionId}'`);
382
+ const runtime = options.runtime || options.component || topology.dbRuntimes.find((entry) => entry.projection.id === projectionId);
383
+ if (!runtime) {
384
+ throw new Error(`No database runtime found for projection '${projectionId}'`);
377
385
  }
378
386
  return generateWithComponentGenerator({
379
387
  graph,
380
388
  projection: getProjection(graph, projectionId),
381
- component,
389
+ runtime,
390
+ component: runtime,
382
391
  topology,
383
392
  implementation: options.implementation || null,
384
393
  options: { ...options, projectionId }
@@ -426,22 +435,24 @@ export function dbEnvVarsForComponent(component, options = {}) {
426
435
  */
427
436
  function decorateRuntimes(graph, config) {
428
437
  const byProjectionId = new Map((graph.byKind.projection || []).map((projection) => [projection.id, projection]));
429
- const rawRuntimes = config.topology?.runtimes || config.topology?.components || [];
438
+ const rawRuntimes = config.topology?.runtimes || [];
430
439
  /** @type {RuntimeComponent[]} */
431
440
  const runtimes = rawRuntimes.map((runtime) => ({
432
441
  ...runtime,
433
- kind: runtime.kind || runtime.type || null,
434
- api: runtime.uses_api ?? runtime.api ?? null,
435
- database: runtime.uses_database ?? runtime.database ?? null,
442
+ kind: runtime.kind || null,
443
+ api: runtime.uses_api ?? null,
444
+ database: runtime.uses_database ?? null,
436
445
  projection: byProjectionId.get(runtime.projection) || {}
437
446
  }));
438
447
  const byId = new Map(runtimes.map((runtime) => [runtime.id, runtime]));
439
448
  for (const runtime of runtimes) {
440
449
  if (runtime.kind === "api_service" && runtime.database) {
441
- runtime.databaseComponent = byId.get(runtime.database) || null;
450
+ runtime.databaseRuntime = byId.get(runtime.database) || null;
451
+ runtime.databaseComponent = runtime.databaseRuntime;
442
452
  }
443
453
  if (runtime.kind === "web_surface" && runtime.api) {
444
- runtime.apiComponent = byId.get(runtime.api) || null;
454
+ runtime.apiRuntime = byId.get(runtime.api) || null;
455
+ runtime.apiComponent = runtime.apiRuntime;
445
456
  }
446
457
  }
447
458
  return runtimes;
@@ -462,20 +473,23 @@ export function resolveRuntimeTopology(graph, options = {}) {
462
473
  throw new Error(validation.errors.map((error) => error.message).join("\n"));
463
474
  }
464
475
  const runtimes = decorateRuntimes(graph, config);
465
- const apiComponents = runtimes.filter((runtime) => runtime.kind === "api_service");
466
- const webComponents = runtimes.filter((runtime) => runtime.kind === "web_surface");
467
- const dbComponents = runtimes.filter((runtime) => runtime.kind === "database");
468
- const primaryApi = apiComponents[0] || null;
469
- const primaryWeb = webComponents[0] || null;
470
- const primaryDb = primaryApi?.databaseComponent || dbComponents[0] || null;
476
+ const apiRuntimes = runtimes.filter((runtime) => runtime.kind === "api_service");
477
+ const webRuntimes = runtimes.filter((runtime) => runtime.kind === "web_surface");
478
+ const dbRuntimes = runtimes.filter((runtime) => runtime.kind === "database");
479
+ const primaryApi = apiRuntimes[0] || null;
480
+ const primaryWeb = webRuntimes[0] || null;
481
+ const primaryDb = primaryApi?.databaseRuntime || dbRuntimes[0] || null;
471
482
 
472
483
  return {
473
484
  config,
474
485
  runtimes,
475
486
  components: runtimes,
476
- apiComponents,
477
- webComponents,
478
- dbComponents,
487
+ apiRuntimes,
488
+ webRuntimes,
489
+ dbRuntimes,
490
+ apiComponents: apiRuntimes,
491
+ webComponents: webRuntimes,
492
+ dbComponents: dbRuntimes,
479
493
  primaryApi,
480
494
  primaryWeb,
481
495
  primaryDb,
@@ -17,7 +17,7 @@ export function generateUiContractDebug(graph, options = {}) {
17
17
  for (const contract of contracts) {
18
18
  lines.push(`## \`${contract.projection.id}\` - ${contract.projection.name}`);
19
19
  lines.push("");
20
- lines.push(`Platform: \`${contract.projection.platform}\``);
20
+ lines.push(`Projection type: \`${contract.projection.type}\``);
21
21
  lines.push(`Realizes: ${refList(contract.realizes)}`);
22
22
  lines.push(`Outputs: ${symbolList(contract.outputs)}`);
23
23
  if (contract.appShell) {
@@ -57,11 +57,13 @@ export function generateDbTarget(target, graph, options = {}) {
57
57
  : generatePostgresDbLifecyclePlan(graph, options);
58
58
  }
59
59
  if (target === "db-lifecycle-bundle") {
60
- if (options.component?.generator?.id) {
60
+ const runtime = options.runtime || options.component;
61
+ if (runtime?.generator?.id) {
61
62
  return generateWithComponentGenerator({
62
63
  graph,
63
- projection: options.component.projection,
64
- component: options.component,
64
+ projection: runtime.projection,
65
+ runtime,
66
+ component: runtime,
65
67
  topology: options.topology || null,
66
68
  implementation: options.implementation || null,
67
69
  options
@@ -87,7 +87,7 @@ function renderEmptySnapshotForProjection(projection) {
87
87
  projection: {
88
88
  id: projection.id,
89
89
  name: projection.name || projection.id,
90
- type: projection.type || projection.platform
90
+ type: projection.type || projection.type
91
91
  },
92
92
  profile,
93
93
  generatorDefaults: generatorDefaultsMap(projection),
@@ -50,7 +50,7 @@ function drizzleColumnBuilder(column, relation, targetTableVar) {
50
50
  export function generatePostgresDrizzleSchema(graph, options = {}) {
51
51
  resolvePostgresCapabilities(options.profileId);
52
52
  const projection = getProjection(graph, options.projectionId);
53
- const projectionType = projection.type || projection.platform;
53
+ const projectionType = projection.type || projection.type;
54
54
  if (projectionType !== "db_contract") {
55
55
  throw new Error(`Drizzle schema generation currently supports db_contract projections only, found '${projectionType}'`);
56
56
  }
@@ -40,7 +40,7 @@ function prismaDefaultForColumn(column, byId) {
40
40
  export function generatePostgresPrismaSchema(graph, options = {}) {
41
41
  resolvePostgresCapabilities(options.profileId);
42
42
  const projection = getProjection(graph, options.projectionId);
43
- const projectionType = projection.type || projection.platform;
43
+ const projectionType = projection.type || projection.type;
44
44
  if (projectionType !== "db_contract") {
45
45
  throw new Error(`Prisma schema generation currently supports db_contract projections only, found '${projectionType}'`);
46
46
  }
@@ -42,8 +42,8 @@ export function indexGraphStatements(graph) {
42
42
  export function dbProjectionCandidates(graph) {
43
43
  return (graph.byKind.projection || []).filter(
44
44
  (projection) =>
45
- (projection.type || projection.platform) === "db_contract" ||
46
- projection.platform?.startsWith("db_") ||
45
+ (projection.type || projection.type) === "db_contract" ||
46
+ projection.type?.startsWith("db_") ||
47
47
  (projection.dbTables || []).length > 0 ||
48
48
  (projection.dbColumns || []).length > 0 ||
49
49
  (projection.dbRelations || []).length > 0
@@ -278,7 +278,7 @@ export function buildDbProjectionContract(graph, projection) {
278
278
  projection: {
279
279
  id: projection.id,
280
280
  name: projection.name || projection.id,
281
- type: projection.type || projection.platform
281
+ type: projection.type || projection.type
282
282
  },
283
283
  profile: dbProfileForProjection(projection),
284
284
  generatorDefaults: generatorDefaultsMap(projection),
@@ -45,7 +45,7 @@ function prismaDefaultForColumn(column) {
45
45
  export function generateSqlitePrismaSchema(graph, options = {}) {
46
46
  resolveSqliteCapabilities(options.profileId);
47
47
  const projection = getProjection(graph, options.projectionId);
48
- const projectionType = projection.type || projection.platform;
48
+ const projectionType = projection.type || projection.type;
49
49
  if (projectionType !== "db_contract") {
50
50
  throw new Error(`Prisma schema generation currently supports db_contract projections only, found '${projectionType}'`);
51
51
  }
@@ -8,11 +8,13 @@ export function generateAppTarget(target, graph, options = {}) {
8
8
  return generateBackendTarget(target, graph, options);
9
9
  }
10
10
  if (target === "swiftui-app") {
11
- if (options.component?.generator?.id) {
11
+ const runtime = options.runtime || options.component;
12
+ if (runtime?.generator?.id) {
12
13
  return generateWithComponentGenerator({
13
14
  graph,
14
- projection: options.component.projection,
15
- component: options.component,
15
+ projection: runtime.projection,
16
+ runtime,
17
+ component: runtime,
16
18
  topology: options.topology || null,
17
19
  implementation: options.implementation || null,
18
20
  options
@@ -12,11 +12,13 @@ export function generateBackendTarget(target, graph, options = {}) {
12
12
  return generatePersistenceScaffold(graph, options);
13
13
  }
14
14
  if (target === "hono-server") {
15
- if (options.component?.generator?.id) {
15
+ const runtime = options.runtime || options.component;
16
+ if (runtime?.generator?.id) {
16
17
  return generateWithComponentGenerator({
17
18
  graph,
18
- projection: options.component.projection,
19
- component: options.component,
19
+ projection: runtime.projection,
20
+ runtime,
21
+ component: runtime,
20
22
  topology: options.topology || null,
21
23
  implementation: options.implementation || null,
22
24
  options
@@ -25,11 +27,13 @@ export function generateBackendTarget(target, graph, options = {}) {
25
27
  return generateHonoServer(graph, options);
26
28
  }
27
29
  if (target === "express-server") {
28
- if (options.component?.generator?.id) {
30
+ const runtime = options.runtime || options.component;
31
+ if (runtime?.generator?.id) {
29
32
  return generateWithComponentGenerator({
30
33
  graph,
31
- projection: options.component.projection,
32
- component: options.component,
34
+ projection: runtime.projection,
35
+ runtime,
36
+ component: runtime,
33
37
  topology: options.topology || null,
34
38
  implementation: options.implementation || null,
35
39
  options
@@ -155,7 +155,7 @@ export function generatePersistenceScaffold(graph, options = {}) {
155
155
  const drizzleRepositoryClassName = repositoryReference.drizzleRepositoryClassName;
156
156
  const drizzleHint = repositoryReference.drizzleHint;
157
157
  const projection = getProjection(graph, options.projectionId);
158
- const projectionType = projection.type || projection.platform;
158
+ const projectionType = projection.type || projection.type;
159
159
  if (projectionType !== "db_contract") {
160
160
  throw new Error(`Persistence scaffold generation currently supports db_contract projections only, found '${projectionType}'`);
161
161
  }
@@ -30,7 +30,7 @@ function buildServerContract(graph, projection) {
30
30
  projection: {
31
31
  id: projection.id,
32
32
  name: projection.name || projection.id,
33
- type: projection.type || projection.platform
33
+ type: projection.type || projection.type
34
34
  },
35
35
  routes: realizedCapabilities.map((capability) => {
36
36
  const apiContract = generateApiContractGraph(graph, { capabilityId: capability.id });
@@ -38,7 +38,7 @@ export function uiProjectionCandidates(graph) {
38
38
  (projection.uiAppShell || []).length > 0 ||
39
39
  (projection.uiNavigation || []).length > 0 ||
40
40
  (projection.uiScreenRegions || []).length > 0 ||
41
- (projection.uiComponents || []).length > 0 ||
41
+ (projection.widgetBindings || []).length > 0 ||
42
42
  (projection.uiDesign || []).length > 0
43
43
  );
44
44
  }
@@ -15,17 +15,19 @@ import {
15
15
 
16
16
  export function generateWebApp(graph, options = {}) {
17
17
  const projection = getProjection(graph, options.projectionId);
18
- if (options.component?.generator?.id) {
18
+ const runtime = options.runtime || options.component;
19
+ if (runtime?.generator?.id) {
19
20
  return generateWithComponentGenerator({
20
21
  graph,
21
22
  projection,
22
- component: options.component,
23
+ runtime,
24
+ component: runtime,
23
25
  topology: options.topology || null,
24
26
  implementation: options.implementation || null,
25
27
  options: { ...options, projectionId: projection.id }
26
28
  }).files;
27
29
  }
28
- const profile = generatorProfile(options.component?.generator?.id, null) || generatorDefaultsMap(projection).profile || "sveltekit";
30
+ const profile = generatorProfile(runtime?.generator?.id, null) || generatorDefaultsMap(projection).profile || "sveltekit";
29
31
  if (profile === "vanilla") {
30
32
  return generateVanillaWebApp(graph, options);
31
33
  }
@@ -3,7 +3,7 @@ import { buildWebRealization } from "../../../realization/ui/index.js";
3
3
  export function generateUiSurfaceContract(graph, options = {}) {
4
4
  if (!options.projectionId) {
5
5
  const output = {};
6
- for (const projection of (graph.byKind.projection || []).filter((entry) => (entry.type || entry.platform) === "web_surface")) {
6
+ for (const projection of (graph.byKind.projection || []).filter((entry) => (entry.type || entry.type) === "web_surface")) {
7
7
  output[projection.id] = buildWebRealization(graph, { ...options, projectionId: projection.id }).contract;
8
8
  }
9
9
  return output;
@@ -22,7 +22,7 @@ function summarizeProjection(projection) {
22
22
  ? {
23
23
  id: projection.id,
24
24
  name: projection.name || projection.id,
25
- type: projection.type || projection.platform || null,
25
+ type: projection.type || projection.type || null,
26
26
  status: projection.status || null,
27
27
  source_path: sourcePath(projection)
28
28
  }
@@ -479,14 +479,14 @@ function projectionUsageEntries(graph, projection) {
479
479
  const sharedProjection = sharedUiProjectionForWeb(graph, projection);
480
480
  const entries = [];
481
481
  if (sharedProjection) {
482
- entries.push(...(sharedProjection.uiComponents || []).map((usage, index) => ({
482
+ entries.push(...(sharedProjection.widgetBindings || []).map((usage, index) => ({
483
483
  projection,
484
484
  sourceProjection: sharedProjection,
485
485
  usage,
486
486
  index
487
487
  })));
488
488
  }
489
- entries.push(...(projection.uiComponents || []).map((usage, index) => ({
489
+ entries.push(...(projection.widgetBindings || []).map((usage, index) => ({
490
490
  projection,
491
491
  sourceProjection: projection,
492
492
  usage,
@@ -499,10 +499,10 @@ function candidateProjections(graph, projectionId) {
499
499
  if (projectionId) {
500
500
  return [getProjection(graph, projectionId)];
501
501
  }
502
- const direct = uiProjectionCandidates(graph).filter((projection) => (projection.uiComponents || []).length > 0);
502
+ const direct = uiProjectionCandidates(graph).filter((projection) => (projection.widgetBindings || []).length > 0);
503
503
  const inherited = (graph.byKind.projection || []).filter((projection) => {
504
- if ((projection.uiComponents || []).length > 0) return false;
505
- return Boolean(sharedUiProjectionForWeb(graph, projection)?.uiComponents?.length);
504
+ if ((projection.widgetBindings || []).length > 0) return false;
505
+ return Boolean(sharedUiProjectionForWeb(graph, projection)?.widgetBindings?.length);
506
506
  });
507
507
  return [...direct, ...inherited].sort((a, b) => a.id.localeCompare(b.id));
508
508
  }
@@ -27,7 +27,7 @@ function widgetContract(widget) {
27
27
  export function generateUiWidgetContract(graph, options = {}) {
28
28
  const widgetId = options.widgetId || options.componentId;
29
29
  if (widgetId) {
30
- const widget = (graph?.byKind?.widget || graph?.byKind?.component || []).find((entry) => entry.id === widgetId);
30
+ const widget = (graph?.byKind?.widget || []).find((entry) => entry.id === widgetId);
31
31
  if (!widget) {
32
32
  throw new Error(`No widget found with id '${widgetId}'`);
33
33
  }
@@ -31,7 +31,7 @@ export const GENERATOR_POLICY_FILE = "topogram.generator-policy.json";
31
31
  * @property {string|null} path
32
32
  * @property {string|null} suggestedFix
33
33
  * @property {string|null} step
34
- * @property {string|null} [componentId]
34
+ * @property {string|null} [runtimeId]
35
35
  * @property {string|null} [generatorId]
36
36
  * @property {string|null} [packageName]
37
37
  * @property {string|null} [version]
@@ -39,8 +39,8 @@ export const GENERATOR_POLICY_FILE = "topogram.generator-policy.json";
39
39
 
40
40
  /**
41
41
  * @typedef {Object} PackageGeneratorBinding
42
- * @property {string} componentId
43
- * @property {string} componentType
42
+ * @property {string} runtimeId
43
+ * @property {string} runtimeKind
44
44
  * @property {string} projection
45
45
  * @property {string} generatorId
46
46
  * @property {string} version
@@ -59,7 +59,7 @@ function generatorPolicyDiagnostic(input) {
59
59
  path: typeof input.path === "string" ? input.path : null,
60
60
  suggestedFix: typeof input.suggestedFix === "string" ? input.suggestedFix : null,
61
61
  step: typeof input.step === "string" ? input.step : null,
62
- componentId: typeof input.componentId === "string" ? input.componentId : null,
62
+ runtimeId: typeof input.runtimeId === "string" ? input.runtimeId : null,
63
63
  generatorId: typeof input.generatorId === "string" ? input.generatorId : null,
64
64
  packageName: typeof input.packageName === "string" ? input.packageName : null,
65
65
  version: typeof input.version === "string" ? input.version : null
@@ -179,8 +179,8 @@ export function packageBackedGeneratorBindings(projectConfig) {
179
179
  return runtimes
180
180
  .filter((runtime) => typeof runtime?.generator?.package === "string" && runtime.generator.package.length > 0)
181
181
  .map((runtime) => ({
182
- componentId: String(runtime.id || "unknown"),
183
- componentType: String(runtime.kind || runtime.type || "unknown"),
182
+ runtimeId: String(runtime.id || "unknown"),
183
+ runtimeKind: String(runtime.kind || "unknown"),
184
184
  projection: String(runtime.projection || "unknown"),
185
185
  generatorId: String(runtime.generator.id || "unknown"),
186
186
  version: String(runtime.generator.version || "unknown"),
@@ -263,11 +263,11 @@ export function generatorPolicyDiagnosticsForBindings(policyInfo, bindings, step
263
263
  const allowedPackages = policy.allowedPackages.join(", ") || "(none)";
264
264
  diagnostics.push(generatorPolicyDiagnostic({
265
265
  code: "generator_package_denied",
266
- message: `Component '${binding.componentId}' generator package '${binding.packageName}' is not allowed by ${GENERATOR_POLICY_FILE}.`,
266
+ message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is not allowed by ${GENERATOR_POLICY_FILE}.`,
267
267
  path: policyInfo.path,
268
268
  suggestedFix: `Review '${binding.packageName}', then run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` or add '${scope || binding.packageName}' to ${GENERATOR_POLICY_FILE}.`,
269
269
  step,
270
- componentId: binding.componentId,
270
+ runtimeId: binding.runtimeId,
271
271
  generatorId: binding.generatorId,
272
272
  packageName: binding.packageName,
273
273
  version: binding.version
@@ -278,11 +278,11 @@ export function generatorPolicyDiagnosticsForBindings(policyInfo, bindings, step
278
278
  if (pinnedVersion && pinnedVersion !== binding.version) {
279
279
  diagnostics.push(generatorPolicyDiagnostic({
280
280
  code: "generator_version_mismatch",
281
- message: `Component '${binding.componentId}' generator '${binding.generatorId}' uses version '${binding.version}', but ${GENERATOR_POLICY_FILE} pins '${binding.packageName}' to '${pinnedVersion}'.`,
281
+ message: `Runtime '${binding.runtimeId}' generator '${binding.generatorId}' uses version '${binding.version}', but ${GENERATOR_POLICY_FILE} pins '${binding.packageName}' to '${pinnedVersion}'.`,
282
282
  path: policyInfo.path,
283
283
  suggestedFix: `Use generator version '${pinnedVersion}', or run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after review.`,
284
284
  step,
285
- componentId: binding.componentId,
285
+ runtimeId: binding.runtimeId,
286
286
  generatorId: binding.generatorId,
287
287
  packageName: binding.packageName,
288
288
  version: binding.version