@topogram/cli 0.3.51 → 0.3.52

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 +61 -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 +92 -42
  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 +473 -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,4 +1,5 @@
1
1
  export const BUNDLED_GENERATOR_ADAPTERS: any[];
2
2
  export function getBundledGeneratorAdapter(generatorId: string): any;
3
3
  export function resolveGeneratorForComponent(component: any, options?: { rootDir?: string | null; configDir?: string | null }): { manifest: any; adapter: any };
4
+ export function generateWithRuntimeGenerator(context: any): { files: Record<string, string>; artifacts?: any; diagnostics?: any[] };
4
5
  export function generateWithComponentGenerator(context: any): { files: Record<string, string>; artifacts?: any; diagnostics?: any[] };
@@ -29,7 +29,7 @@ import { generateServerContract } from "./surfaces/services/server-contract.js";
29
29
  import { generateStatelessServer } from "./surfaces/services/stateless.js";
30
30
  import { generateReactApp } from "./surfaces/web/react.js";
31
31
  import { generateSvelteKitApp } from "./surfaces/web/sveltekit.js";
32
- import { generateUiWebContract } from "./surfaces/web/ui-web-contract.js";
32
+ import { generateUiSurfaceContract } from "./surfaces/web/ui-surface-contract.js";
33
33
  import { generateVanillaWebApp } from "./surfaces/web/vanilla.js";
34
34
 
35
35
  /**
@@ -40,7 +40,8 @@ import { generateVanillaWebApp } from "./surfaces/web/vanilla.js";
40
40
  * @typedef {Object} GeneratorContext
41
41
  * @property {Record<string, any>} graph
42
42
  * @property {Record<string, any>} projection
43
- * @property {Record<string, any>} component
43
+ * @property {Record<string, any>} runtime
44
+ * @property {Record<string, any>} component Internal runtime alias.
44
45
  * @property {Record<string, any>|null} [topology]
45
46
  * @property {Record<string, any>} [contracts]
46
47
  * @property {Record<string, any>|null} [implementation]
@@ -82,12 +83,21 @@ function requiredManifest(generatorId) {
82
83
  return manifest;
83
84
  }
84
85
 
86
+ /**
87
+ * @param {GeneratorContext} context
88
+ * @returns {string}
89
+ */
90
+ function runtimeFor(context) {
91
+ return context.runtime || context.component || {};
92
+ }
93
+
85
94
  /**
86
95
  * @param {GeneratorContext} context
87
96
  * @returns {string}
88
97
  */
89
98
  function projectionIdFor(context) {
90
- return context.projection?.id || context.component?.projection?.id || context.options?.projectionId;
99
+ const runtime = runtimeFor(context);
100
+ return context.projection?.id || runtime.projection?.id || context.options?.projectionId;
91
101
  }
92
102
 
93
103
  /**
@@ -97,12 +107,14 @@ function projectionIdFor(context) {
97
107
  */
98
108
  function serverOptions(context, profile) {
99
109
  const projectionId = projectionIdFor(context);
100
- const dbProjectionId = context.component?.databaseComponent?.projection?.id || context.options?.dbProjectionId;
110
+ const runtime = runtimeFor(context);
111
+ const dbProjectionId = runtime.databaseComponent?.projection?.id || context.options?.dbProjectionId;
101
112
  return {
102
113
  ...(context.options || {}),
103
114
  projectionId,
104
115
  dbProjectionId,
105
- component: context.component,
116
+ component: runtime,
117
+ runtime,
106
118
  topology: context.topology,
107
119
  contracts: context.contracts,
108
120
  implementation: context.implementation,
@@ -115,10 +127,12 @@ function serverOptions(context, profile) {
115
127
  * @returns {Record<string, any>}
116
128
  */
117
129
  function commonOptions(context) {
130
+ const runtime = runtimeFor(context);
118
131
  return {
119
132
  ...(context.options || {}),
120
133
  projectionId: projectionIdFor(context),
121
- component: context.component,
134
+ component: runtime,
135
+ runtime,
122
136
  topology: context.topology,
123
137
  contracts: context.contracts,
124
138
  implementation: context.implementation
@@ -131,26 +145,28 @@ function commonOptions(context) {
131
145
  */
132
146
  function buildContractsForContext(context) {
133
147
  const projectionId = projectionIdFor(context);
134
- if (context.component.type === "web") {
148
+ const runtime = runtimeFor(context);
149
+ const surface = runtime.type || runtime.kind;
150
+ if (surface === "web" || surface === "web_surface") {
135
151
  return {
136
- uiWeb: generateUiWebContract(context.graph, { ...(context.options || {}), projectionId })
152
+ uiSurface: generateUiSurfaceContract(context.graph, { ...(context.options || {}), projectionId })
137
153
  };
138
154
  }
139
- if (context.component.type === "api") {
155
+ if (surface === "api" || surface === "api_service") {
140
156
  return {
141
157
  server: generateServerContract(context.graph, { ...(context.options || {}), projectionId }),
142
158
  api: generateApiContractGraph(context.graph, {})
143
159
  };
144
160
  }
145
- if (context.component.type === "database") {
161
+ if (surface === "database") {
146
162
  return {
147
163
  db: generateDbContractGraph(context.graph, { ...(context.options || {}), projectionId }),
148
164
  lifecyclePlan: generateDbLifecyclePlan(context.graph, { ...(context.options || {}), projectionId })
149
165
  };
150
166
  }
151
- if (context.component.type === "native") {
167
+ if (surface === "native" || surface === "ios_surface" || surface === "android_surface") {
152
168
  return {
153
- uiWeb: generateUiWebContract(context.graph, { ...(context.options || {}), projectionId })
169
+ uiSurface: generateUiSurfaceContract(context.graph, { ...(context.options || {}), projectionId })
154
170
  };
155
171
  }
156
172
  return {};
@@ -161,7 +177,8 @@ export const BUNDLED_GENERATOR_ADAPTERS = [
161
177
  {
162
178
  manifest: requiredManifest("topogram/hono"),
163
179
  generate(context) {
164
- if (context.component && !context.component.databaseComponent) {
180
+ const runtime = runtimeFor(context);
181
+ if (runtime && !runtime.databaseComponent) {
165
182
  return fileResult(generateStatelessServer(context.graph, serverOptions(context, "hono")));
166
183
  }
167
184
  return fileResult(generateHonoServer(context.graph, serverOptions(context, "hono")));
@@ -170,7 +187,8 @@ export const BUNDLED_GENERATOR_ADAPTERS = [
170
187
  {
171
188
  manifest: requiredManifest("topogram/express"),
172
189
  generate(context) {
173
- if (context.component && !context.component.databaseComponent) {
190
+ const runtime = runtimeFor(context);
191
+ if (runtime && !runtime.databaseComponent) {
174
192
  return fileResult(generateStatelessServer(context.graph, serverOptions(context, "express")));
175
193
  }
176
194
  return fileResult(generateExpressServer(context.graph, serverOptions(context, "express")));
@@ -246,24 +264,24 @@ function selectPackageExport(moduleValue, exportName) {
246
264
 
247
265
  /**
248
266
  * @param {GeneratorManifest} manifest
249
- * @param {Record<string, any>} component
267
+ * @param {Record<string, any>} runtime
250
268
  * @param {{ rootDir?: string|null, configDir?: string|null }} [options]
251
269
  * @returns {GeneratorAdapter}
252
270
  */
253
- function loadPackageGeneratorAdapter(manifest, component, options = {}) {
254
- const packageName = manifest.package || component?.generator?.package;
271
+ function loadPackageGeneratorAdapter(manifest, runtime, options = {}) {
272
+ const packageName = manifest.package || runtime?.generator?.package;
255
273
  if (!packageName) {
256
- throw new Error(`Component '${component?.id || "unknown"}' generator '${manifest.id}@${manifest.version}' is package-backed but does not declare a package.`);
274
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator '${manifest.id}@${manifest.version}' is package-backed but does not declare a package.`);
257
275
  }
258
276
  const rootDir = options.configDir || options.rootDir || process.cwd();
259
277
  const diagnostics = generatorPolicyDiagnosticsForBindings(
260
278
  loadGeneratorPolicy(rootDir),
261
279
  [{
262
- componentId: String(component?.id || "unknown"),
263
- componentType: String(component?.type || manifest.surface || "unknown"),
264
- projection: String(component?.projection?.id || component?.projection || "unknown"),
265
- generatorId: String(component?.generator?.id || manifest.id),
266
- version: String(component?.generator?.version || manifest.version),
280
+ componentId: String(runtime?.id || "unknown"),
281
+ componentType: String(runtime?.kind || runtime?.type || manifest.surface || "unknown"),
282
+ projection: String(runtime?.projection?.id || runtime?.projection || "unknown"),
283
+ generatorId: String(runtime?.generator?.id || manifest.id),
284
+ version: String(runtime?.generator?.version || manifest.version),
267
285
  packageName
268
286
  }],
269
287
  "generator-adapter"
@@ -281,15 +299,15 @@ function loadPackageGeneratorAdapter(manifest, component, options = {}) {
281
299
  moduleValue = requireFromProject(rootDir)(packageName);
282
300
  } catch (error) {
283
301
  const installHint = packageGeneratorInstallHint(packageName);
284
- throw new Error(`Component '${component?.id || "unknown"}' generator package '${packageName}' could not be loaded from '${rootDir}': ${error instanceof Error ? error.message : String(error)}${installHint ? `. ${installHint}` : ""}`);
302
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator package '${packageName}' could not be loaded from '${rootDir}': ${error instanceof Error ? error.message : String(error)}${installHint ? `. ${installHint}` : ""}`);
285
303
  }
286
304
  const adapter = selectPackageExport(moduleValue, manifest.export);
287
305
  if (!adapter || typeof adapter.generate !== "function") {
288
- throw new Error(`Component '${component?.id || "unknown"}' generator package '${packageName}' must export an adapter with a generate(context) function.`);
306
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator package '${packageName}' must export an adapter with a generate(context) function.`);
289
307
  }
290
308
  const adapterManifest = adapter.manifest || manifest;
291
309
  if (adapterManifest.id !== manifest.id || adapterManifest.version !== manifest.version) {
292
- throw new Error(`Component '${component?.id || "unknown"}' generator package '${packageName}' adapter manifest '${adapterManifest.id}@${adapterManifest.version}' does not match '${manifest.id}@${manifest.version}'.`);
310
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator package '${packageName}' adapter manifest '${adapterManifest.id}@${adapterManifest.version}' does not match '${manifest.id}@${manifest.version}'.`);
293
311
  }
294
312
  return {
295
313
  manifest,
@@ -300,35 +318,35 @@ function loadPackageGeneratorAdapter(manifest, component, options = {}) {
300
318
  }
301
319
 
302
320
  /**
303
- * @param {Record<string, any>} component
321
+ * @param {Record<string, any>} runtime
304
322
  * @param {{ rootDir?: string|null, configDir?: string|null }} [options]
305
323
  * @returns {{ manifest: GeneratorManifest, adapter: GeneratorAdapter }}
306
324
  */
307
- export function resolveGeneratorForComponent(component, options = {}) {
308
- const generatorId = component?.generator?.id;
309
- const resolved = resolveGeneratorManifestForBinding(component?.generator, options);
325
+ export function resolveGeneratorForComponent(runtime, options = {}) {
326
+ const generatorId = runtime?.generator?.id;
327
+ const resolved = resolveGeneratorManifestForBinding(runtime?.generator, options);
310
328
  const manifest = resolved.manifest || getGeneratorManifest(generatorId);
311
329
  if (!manifest) {
312
330
  const detail = resolved.errors.length > 0 ? ` ${resolved.errors.join(" ")}` : "";
313
- throw new Error(`Component '${component?.id || "unknown"}' uses unknown generator '${generatorId || "unknown"}'.${detail}`);
331
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' uses unknown generator '${generatorId || "unknown"}'.${detail}`);
314
332
  }
315
333
  if (manifest.planned) {
316
- throw new Error(`Component '${component?.id || "unknown"}' uses planned generator '${manifest.id}', which is not implemented yet.`);
334
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' uses planned generator '${manifest.id}', which is not implemented yet.`);
317
335
  }
318
- if (component.generator?.version !== manifest.version) {
319
- throw new Error(`Component '${component?.id || "unknown"}' generator '${manifest.id}' version '${component.generator?.version}' is unsupported; expected '${manifest.version}'.`);
336
+ if (runtime.generator?.version !== manifest.version) {
337
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator '${manifest.id}' version '${runtime.generator?.version}' is unsupported; expected '${manifest.version}'.`);
320
338
  }
321
339
  const manifestValidation = validateGeneratorManifest(manifest);
322
340
  if (!manifestValidation.ok) {
323
341
  throw new Error(manifestValidation.errors.join("\n"));
324
342
  }
325
343
  if (manifest.source === "package") {
326
- return { manifest, adapter: loadPackageGeneratorAdapter(manifest, component, options) };
344
+ return { manifest, adapter: loadPackageGeneratorAdapter(manifest, runtime, options) };
327
345
  }
328
346
  const adapter = getBundledGeneratorAdapter(manifest.id);
329
347
  if (!adapter) {
330
- const installHint = packageGeneratorInstallHint(component?.generator?.package || manifest.package);
331
- throw new Error(`Component '${component?.id || "unknown"}' generator '${manifest.id}@${manifest.version}' is not available. Package-backed generators must be installed before generation.${installHint ? ` ${installHint}` : ""}`);
348
+ const installHint = packageGeneratorInstallHint(runtime?.generator?.package || manifest.package);
349
+ throw new Error(`Runtime '${runtime?.id || "unknown"}' generator '${manifest.id}@${manifest.version}' is not available. Package-backed generators must be installed before generation.${installHint ? ` ${installHint}` : ""}`);
332
350
  }
333
351
  return { manifest, adapter };
334
352
  }
@@ -337,13 +355,20 @@ export function resolveGeneratorForComponent(component, options = {}) {
337
355
  * @param {Omit<GeneratorContext, "contracts"> & { contracts?: Record<string, any> }} context
338
356
  * @returns {GeneratorResult}
339
357
  */
340
- export function generateWithComponentGenerator(context) {
341
- const { manifest, adapter } = resolveGeneratorForComponent(context.component, context.options || {});
358
+ export function generateWithRuntimeGenerator(context) {
359
+ const runtime = runtimeFor(context);
360
+ const { manifest, adapter } = resolveGeneratorForComponent(runtime, context.options || {});
342
361
  const contracts = context.contracts || buildContractsForContext(context);
343
362
  return adapter.generate({
344
363
  ...context,
364
+ runtime,
365
+ component: runtime,
345
366
  projection: context.projection,
346
367
  manifest,
347
368
  contracts
348
369
  });
349
370
  }
371
+
372
+ export function generateWithComponentGenerator(context) {
373
+ return generateWithRuntimeGenerator(context);
374
+ }
@@ -109,17 +109,17 @@ function loadInstalledAdapter(packageName, rootDir, manifest) {
109
109
  * @returns {Record<string, any>}
110
110
  */
111
111
  function smokeProjection(manifest) {
112
- const platform = manifest.projectionPlatforms[0] || "";
112
+ const type = manifest.projectionTypes[0] || "";
113
113
  if (manifest.surface === "api") {
114
114
  return {
115
115
  id: "proj_generator_check_api",
116
- platform: "api",
117
- http: [{ method: "GET", path: "/generator-check", capabilityId: "cap_generator_check", success: 200 }]
116
+ type: "api_contract",
117
+ endpoints: [{ method: "GET", path: "/generator-check", capabilityId: "cap_generator_check", success: 200 }]
118
118
  };
119
119
  }
120
120
  return {
121
121
  id: `proj_generator_check_${manifest.surface}`,
122
- platform
122
+ type
123
123
  };
124
124
  }
125
125
 
@@ -131,8 +131,8 @@ function smokeProjection(manifest) {
131
131
  function smokeContracts(manifest, projection) {
132
132
  if (manifest.surface === "web" || manifest.surface === "native") {
133
133
  return {
134
- uiWeb: {
135
- projection: { id: projection.id, platform: projection.platform },
134
+ uiSurface: {
135
+ projection: { id: projection.id, type: projection.type },
136
136
  appShell: { brand: "Generator Check" },
137
137
  screens: [
138
138
  { id: "screen_generator_check_home", title: "Generator Check", route: "/" },
@@ -148,22 +148,22 @@ function smokeContracts(manifest, projection) {
148
148
  if (manifest.surface === "api") {
149
149
  return {
150
150
  server: {
151
- projection: { id: projection.id, platform: "api" },
152
- routes: projection.http,
151
+ projection: { id: projection.id, type: "api_contract" },
152
+ routes: projection.endpoints,
153
153
  preconditions: [],
154
154
  responses: []
155
155
  },
156
- api: { projections: [{ id: projection.id, http: projection.http }] }
156
+ api: { projections: [{ id: projection.id, endpoints: projection.endpoints }] }
157
157
  };
158
158
  }
159
159
  if (manifest.surface === "database") {
160
160
  return {
161
161
  db: {
162
- projection: { id: projection.id, platform: projection.platform },
162
+ projection: { id: projection.id, type: projection.type },
163
163
  tables: []
164
164
  },
165
165
  lifecyclePlan: {
166
- projection: { id: projection.id, platform: projection.platform },
166
+ projection: { id: projection.id, type: projection.type },
167
167
  migrations: [],
168
168
  seeds: []
169
169
  }
@@ -177,8 +177,15 @@ function smokeContracts(manifest, projection) {
177
177
  * @returns {Record<string, any>}
178
178
  */
179
179
  function smokeComponent(manifest) {
180
+ const runtimeKindBySurface = {
181
+ api: "api_service",
182
+ web: "web_surface",
183
+ native: "ios_surface",
184
+ database: "database"
185
+ };
180
186
  return {
181
- id: `component_generator_check_${manifest.surface}`,
187
+ id: `runtime_generator_check_${manifest.surface}`,
188
+ kind: runtimeKindBySurface[manifest.surface] || manifest.surface,
182
189
  type: manifest.surface,
183
190
  projection: `proj_generator_check_${manifest.surface}`,
184
191
  generator: {
@@ -6,7 +6,7 @@ import {
6
6
  maintainedProofMetadata,
7
7
  relatedCapabilitiesForEntity,
8
8
  relatedProjectionsForCapability,
9
- relatedProjectionsForComponent,
9
+ relatedProjectionsForWidget,
10
10
  relatedProjectionsForEntity,
11
11
  stableSortedStrings,
12
12
  summarizeById,
@@ -79,7 +79,7 @@ function changedIdsForDiffSections(diff) {
79
79
  "rules",
80
80
  "workflows",
81
81
  "projections",
82
- "components",
82
+ "widgets",
83
83
  "journeys",
84
84
  "verifications",
85
85
  "domains",
@@ -100,16 +100,16 @@ function collectAffectedProjectionIds(graph, baselineGraph, diff) {
100
100
  const changedCapabilities = stableSortedStrings((diff.capabilities || []).map((entry) => entry.id));
101
101
  const changedEntities = stableSortedStrings((diff.entities || []).map((entry) => entry.id));
102
102
  const changedProjections = stableSortedStrings((diff.projections || []).map((entry) => entry.id));
103
- const changedComponentProjections = stableSortedStrings((diff.components || []).flatMap((entry) => {
103
+ const changedWidgetProjections = stableSortedStrings((diff.widgets || []).flatMap((entry) => {
104
104
  if (entry.classification === "additive") {
105
- return relatedProjectionsForComponent(graph, entry.id);
105
+ return relatedProjectionsForWidget(graph, entry.id);
106
106
  }
107
107
  if (entry.classification === "removed") {
108
- return relatedProjectionsForComponent(baselineGraph, entry.id);
108
+ return relatedProjectionsForWidget(baselineGraph, entry.id);
109
109
  }
110
110
  return [
111
- ...relatedProjectionsForComponent(graph, entry.id),
112
- ...relatedProjectionsForComponent(baselineGraph, entry.id)
111
+ ...relatedProjectionsForWidget(graph, entry.id),
112
+ ...relatedProjectionsForWidget(baselineGraph, entry.id)
113
113
  ];
114
114
  }));
115
115
 
@@ -117,7 +117,7 @@ function collectAffectedProjectionIds(graph, baselineGraph, diff) {
117
117
  ...changedProjections,
118
118
  ...changedCapabilities.flatMap((id) => relatedProjectionsForCapability(graph, id)),
119
119
  ...changedEntities.flatMap((id) => relatedProjectionsForEntity(graph, id)),
120
- ...changedComponentProjections
120
+ ...changedWidgetProjections
121
121
  ]);
122
122
  }
123
123
 
@@ -183,7 +183,7 @@ export function generateContextDiff(graph, options = {}) {
183
183
  rules: diffMaps(normalizeTargetMap(graph, "rule"), normalizeTargetMap(baselineGraph, "rule")),
184
184
  workflows: diffMaps(normalizeTargetMap(graph, "workflow"), normalizeTargetMap(baselineGraph, "workflow")),
185
185
  projections: diffMaps(normalizeTargetMap(graph, "projection"), normalizeTargetMap(baselineGraph, "projection")),
186
- components: diffMaps(normalizeTargetMap(graph, "component"), normalizeTargetMap(baselineGraph, "component")),
186
+ widgets: diffMaps(normalizeTargetMap(graph, "widget"), normalizeTargetMap(baselineGraph, "widget")),
187
187
  journeys: diffMaps(normalizeTargetMap(graph, "journey"), normalizeTargetMap(baselineGraph, "journey")),
188
188
  verifications: diffMaps(normalizeTargetMap(graph, "verification"), normalizeTargetMap(baselineGraph, "verification")),
189
189
  domains: diffMaps(normalizeTargetMap(graph, "domain"), normalizeTargetMap(baselineGraph, "domain")),
@@ -9,14 +9,15 @@ import {
9
9
  summarizeDomain
10
10
  } from "./shared.js";
11
11
 
12
- function platformsFromProjections(projections) {
13
- const platforms = new Set();
12
+ function projectionTypesFromProjections(projections) {
13
+ const projectionTypes = new Set();
14
14
  for (const projection of projections) {
15
- if (projection?.platform) {
16
- platforms.add(projection.platform);
15
+ const projectionType = projection?.type || projection?.platform;
16
+ if (projectionType) {
17
+ projectionTypes.add(projectionType);
17
18
  }
18
19
  }
19
- return [...platforms].sort();
20
+ return [...projectionTypes].sort();
20
21
  }
21
22
 
22
23
  export function generateDomainCoverage(graph, options = {}) {
@@ -45,17 +46,17 @@ export function generateDomainCoverage(graph, options = {}) {
45
46
  );
46
47
  }
47
48
 
48
- const platforms = platformsFromProjections(projectionStatements);
49
+ const projectionTypes = projectionTypesFromProjections(projectionStatements);
49
50
  const matrix = {};
50
51
  for (const capabilityId of capabilities) {
51
52
  matrix[capabilityId] = {};
52
- for (const platform of platforms) {
53
+ for (const projectionType of projectionTypes) {
53
54
  const realized = projectionStatements.some(
54
55
  (projection) =>
55
- projection.platform === platform &&
56
+ (projection.type || projection.platform) === projectionType &&
56
57
  (projection.realizes || []).some((entry) => entry.id === capabilityId)
57
58
  );
58
- matrix[capabilityId][platform] = realized;
59
+ matrix[capabilityId][projectionType] = realized;
59
60
  }
60
61
  }
61
62
 
@@ -64,7 +65,7 @@ export function generateDomainCoverage(graph, options = {}) {
64
65
  version: 1,
65
66
  focus: { kind: "domain", id: domain.id },
66
67
  summary: summarizeDomain(domain),
67
- platforms,
68
+ projectionTypes,
68
69
  capabilities,
69
70
  entities,
70
71
  rules,
@@ -94,13 +94,13 @@ export function generateDomainPage(graph, options = {}) {
94
94
  lines.push(renderMembersSection(graph, "Verifications", relatedVerificationsForDomain(graph, domain.id), "verification"));
95
95
  lines.push("");
96
96
 
97
- if (coverage.platforms.length) {
98
- lines.push("## Per-platform coverage");
97
+ if (coverage.projectionTypes.length) {
98
+ lines.push("## Per-type coverage");
99
99
  lines.push("");
100
- const headers = ["Capability", ...coverage.platforms];
100
+ const headers = ["Capability", ...coverage.projectionTypes];
101
101
  const rows = coverage.capabilities.map((capabilityId) => {
102
- const cells = coverage.platforms.map((platform) =>
103
- coverage.coverage_matrix[capabilityId]?.[platform] ? "✓" : "—"
102
+ const cells = coverage.projectionTypes.map((projectionType) =>
103
+ coverage.coverage_matrix[capabilityId]?.[projectionType] ? "✓" : "—"
104
104
  );
105
105
  return [`\`${capabilityId}\``, ...cells];
106
106
  });
@@ -108,7 +108,7 @@ export function generateDomainPage(graph, options = {}) {
108
108
  lines.push("");
109
109
  lines.push(`Projections involved: ${coverage.projections.map((id) => `\`${id}\``).join(", ") || "none"}`);
110
110
  } else {
111
- lines.push("## Per-platform coverage");
111
+ lines.push("## Per-type coverage");
112
112
  lines.push("");
113
113
  lines.push("No projections currently realize capabilities in this domain.");
114
114
  }
@@ -168,7 +168,7 @@ function summarizeStatement(statement) {
168
168
  reviewBoundary: reviewBoundaryForEntityPolicy(statement),
169
169
  ownership_boundary: defaultOwnershipBoundary()
170
170
  };
171
- case "component":
171
+ case "widget":
172
172
  return summarizeComponent(statement);
173
173
  case "shape":
174
174
  return {
@@ -396,8 +396,12 @@ export function relatedProjectionsForShape(graph, shapeId) {
396
396
  return stableSortedStrings([...directProjectionIds, ...viaCapabilities]);
397
397
  }
398
398
 
399
+ export function widgetById(graph, widgetId) {
400
+ return (graph?.byKind?.widget || graph?.byKind?.component || []).find((widget) => widget.id === widgetId) || null;
401
+ }
402
+
399
403
  export function componentById(graph, componentId) {
400
- return (graph?.byKind?.component || []).find((component) => component.id === componentId) || null;
404
+ return widgetById(graph, componentId);
401
405
  }
402
406
 
403
407
  function projectionById(graph, projectionId) {
@@ -430,36 +434,44 @@ function downstreamProjectionIds(graph, projectionIds) {
430
434
  return stableSortedStrings([...visited]);
431
435
  }
432
436
 
433
- export function relatedComponentsForProjection(graph, projection) {
437
+ export function relatedWidgetsForProjection(graph, projection) {
434
438
  const directIds = (projection?.uiComponents || []).map((entry) => entry.component?.id).filter(Boolean);
435
439
  const inheritedIds = realizedProjectionIds(projection)
436
440
  .map((projectionId) => projectionById(graph, projectionId))
437
441
  .filter(Boolean)
438
- .flatMap((realizedProjection) => relatedComponentsForProjection(graph, realizedProjection));
442
+ .flatMap((realizedProjection) => relatedWidgetsForProjection(graph, realizedProjection));
439
443
  return stableSortedStrings([...directIds, ...inheritedIds]);
440
444
  }
441
445
 
442
- export function relatedShapesForComponent(component) {
443
- if (!component) return [];
446
+ export function relatedComponentsForProjection(graph, projection) {
447
+ return relatedWidgetsForProjection(graph, projection);
448
+ }
449
+
450
+ export function relatedShapesForWidget(widget) {
451
+ if (!widget) return [];
444
452
  const ids = [
445
- ...(component.events || []).map((event) => event.shape?.id),
446
- ...(component.lookups || [])
453
+ ...(widget.events || []).map((event) => event.shape?.id),
454
+ ...(widget.lookups || [])
447
455
  .filter((lookup) => lookup?.target?.kind === "shape" || String(lookup?.id || "").startsWith("shape_"))
448
456
  .map((lookup) => lookup.id),
449
- ...(component.dependencies || [])
457
+ ...(widget.dependencies || [])
450
458
  .filter((dependency) => referenceKind(dependency) === "shape")
451
459
  .map((dependency) => dependency.id)
452
460
  ];
453
461
  return stableSortedStrings(ids);
454
462
  }
455
463
 
456
- export function relatedProjectionsForComponent(graph, componentId) {
457
- const component = componentById(graph, componentId);
458
- if (!component) return [];
464
+ export function relatedShapesForComponent(component) {
465
+ return relatedShapesForWidget(component);
466
+ }
467
+
468
+ export function relatedProjectionsForWidget(graph, widgetId) {
469
+ const widget = widgetById(graph, widgetId);
470
+ if (!widget) return [];
459
471
  const explicitProjectionIds = stableSortedStrings((graph?.byKind?.projection || [])
460
- .filter((projection) => (projection.uiComponents || []).some((entry) => entry.component?.id === componentId))
472
+ .filter((projection) => (projection.uiComponents || []).some((entry) => entry.component?.id === widgetId))
461
473
  .map((projection) => projection.id));
462
- const dependencyProjectionIds = stableSortedStrings((component.dependencies || []).flatMap((dependency) => {
474
+ const dependencyProjectionIds = stableSortedStrings((widget.dependencies || []).flatMap((dependency) => {
463
475
  const kind = referenceKind(dependency);
464
476
  if (kind === "projection") {
465
477
  return [dependency.id];
@@ -475,18 +487,22 @@ export function relatedProjectionsForComponent(graph, componentId) {
475
487
  }
476
488
  return [];
477
489
  }));
478
- const componentPatterns = new Set(component.patterns || []);
479
- const componentRegions = new Set(component.regions || []);
490
+ const widgetPatterns = new Set(widget.patterns || []);
491
+ const widgetRegions = new Set(widget.regions || []);
480
492
  const viaUiRegions = stableSortedStrings((graph?.byKind?.projection || [])
481
493
  .filter((projection) => (projection.uiScreenRegions || []).some((region) =>
482
- (region.pattern && componentPatterns.has(region.pattern)) ||
483
- (region.region && componentRegions.has(region.region))
494
+ (region.pattern && widgetPatterns.has(region.pattern)) ||
495
+ (region.region && widgetRegions.has(region.region))
484
496
  ))
485
497
  .map((projection) => projection.id));
486
498
 
487
499
  return downstreamProjectionIds(graph, stableSortedStrings([...explicitProjectionIds, ...dependencyProjectionIds, ...viaUiRegions]));
488
500
  }
489
501
 
502
+ export function relatedProjectionsForComponent(graph, componentId) {
503
+ return relatedProjectionsForWidget(graph, componentId);
504
+ }
505
+
490
506
  function referenceKind(reference) {
491
507
  if (reference?.target?.kind) {
492
508
  return reference.target.kind;
@@ -539,7 +555,7 @@ export function workspaceInventory(graph) {
539
555
  journeys: stableSortedStrings((graph.docs || []).filter((doc) => doc.kind === "journey").map((doc) => doc.id)),
540
556
  entities: stableSortedStrings((graph.byKind.entity || []).map((item) => item.id)),
541
557
  projections: stableSortedStrings((graph.byKind.projection || []).map((item) => item.id)),
542
- components: stableSortedStrings((graph.byKind.component || []).map((item) => item.id)),
558
+ widgets: stableSortedStrings((graph.byKind.widget || graph.byKind.component || []).map((item) => item.id)),
543
559
  verifications: stableSortedStrings((graph.byKind.verification || []).map((item) => item.id)),
544
560
  domains: stableSortedStrings((graph.byKind.domain || []).map((item) => item.id)),
545
561
  pitches: stableSortedStrings((graph.byKind.pitch || []).map((item) => item.id)),
@@ -556,7 +572,7 @@ export function ensureContextSelection(options = {}) {
556
572
  options.capabilityId ? ["capability", options.capabilityId] : null,
557
573
  options.workflowId ? ["workflow", options.workflowId] : null,
558
574
  options.projectionId ? ["projection", options.projectionId] : null,
559
- options.componentId ? ["component", options.componentId] : null,
575
+ (options.widgetId || options.componentId) ? ["widget", options.widgetId || options.componentId] : null,
560
576
  options.entityId ? ["entity", options.entityId] : null,
561
577
  options.journeyId ? ["journey", options.journeyId] : null,
562
578
  options.surfaceId ? ["surface", options.surfaceId] : null,
@@ -571,7 +587,7 @@ export function ensureContextSelection(options = {}) {
571
587
 
572
588
  if (selectors.length !== 1) {
573
589
  throw new Error(
574
- "Context selection requires exactly one of --capability, --workflow, --projection, --component, --entity, --journey, --surface, --domain, --pitch, --requirement, --acceptance, --task, --bug, or --document"
590
+ "Context selection requires exactly one of --capability, --workflow, --projection, --widget, --entity, --journey, --surface, --domain, --pitch, --requirement, --acceptance, --task, --bug, or --document"
575
591
  );
576
592
  }
577
593