@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.
- package/CHANGELOG.md +18 -0
- package/package.json +1 -1
- package/src/adoption/review-groups.js +3 -3
- package/src/agent-ops/query-builders.js +34 -34
- package/src/archive/schema.js +1 -1
- package/src/cli.js +173 -20
- package/src/generator/adapters.js +23 -7
- package/src/generator/context/domain-coverage.js +2 -2
- package/src/generator/context/shared.js +6 -22
- package/src/generator/context/slice.js +10 -10
- package/src/generator/docs.js +1 -1
- package/src/generator/registry.js +1 -1
- package/src/generator/runtime/app-bundle.js +8 -6
- package/src/generator/runtime/compile-check.js +7 -5
- package/src/generator/runtime/deployment.js +6 -4
- package/src/generator/runtime/environment.js +31 -28
- package/src/generator/runtime/shared.js +53 -39
- package/src/generator/surfaces/contracts.js +1 -1
- package/src/generator/surfaces/databases/index.js +5 -3
- package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
- package/src/generator/surfaces/databases/postgres/drizzle.js +1 -1
- package/src/generator/surfaces/databases/postgres/prisma.js +1 -1
- package/src/generator/surfaces/databases/shared.js +3 -3
- package/src/generator/surfaces/databases/sqlite/prisma.js +1 -1
- package/src/generator/surfaces/index.js +5 -3
- package/src/generator/surfaces/services/index.js +10 -6
- package/src/generator/surfaces/services/persistence-wiring.js +1 -1
- package/src/generator/surfaces/services/server-contract.js +1 -1
- package/src/generator/surfaces/shared.js +1 -1
- package/src/generator/surfaces/web/index.js +5 -3
- package/src/generator/surfaces/web/ui-surface-contract.js +1 -1
- package/src/generator/widget-conformance.js +6 -6
- package/src/generator/widgets.js +1 -1
- package/src/generator-policy.js +10 -10
- package/src/import/core/runner.js +60 -50
- package/src/project-config.js +5 -42
- package/src/proofs/contract-audit.js +1 -1
- package/src/realization/backend/build-backend-runtime-realization.js +3 -3
- package/src/realization/ui/build-ui-shared-realization.js +9 -9
- package/src/realization/ui/build-web-realization.js +3 -3
- package/src/reconcile/journeys.js +1 -1
- package/src/resolver/enrich/widget.js +2 -2
- package/src/resolver/index.js +4 -23
- package/src/validator/index.js +10 -10
- package/src/workflows.js +49 -49
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
- Install package-backed generator dependencies during `topogram template check`
|
|
6
6
|
before starter validation.
|
|
7
7
|
|
|
8
|
+
## 0.3.55 - 2026-05-07
|
|
9
|
+
|
|
10
|
+
- Complete the public DSL cleanup from `component` to `widget`: normalized
|
|
11
|
+
graph/context output now uses `widget`, `widgetBindings`, `widgetContract`,
|
|
12
|
+
and widget report target names without publishing component compatibility
|
|
13
|
+
aliases.
|
|
14
|
+
- Reconcile/adoption import reports now emit `widgets` in candidate model
|
|
15
|
+
bundles, evidence, summaries, and promoted item plans. Existing imported
|
|
16
|
+
workspaces with `candidates.ui.components` are still accepted as a read-only
|
|
17
|
+
fallback, but new import output writes `candidates.ui.widgets`.
|
|
18
|
+
- Old CLI names fail with explicit rename guidance instead of silently running:
|
|
19
|
+
`topogram component`, `--component`, `ui-component-contract`,
|
|
20
|
+
`component-conformance-report`, and `component-behavior-report`.
|
|
21
|
+
- Generator policy diagnostics now describe topology entries as runtimes
|
|
22
|
+
(`runtimeId`) instead of components.
|
|
23
|
+
- Tighten boundary tests so old public DSL vocabulary is allowed only in
|
|
24
|
+
migration guidance and explicit rename diagnostics.
|
|
25
|
+
|
|
8
26
|
## 0.3.16 - 2026-05-03
|
|
9
27
|
|
|
10
28
|
- Install package-backed generator dependencies during `topogram template check`
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export function buildProjectionReviewGroups(items) {
|
|
|
10
10
|
id: dependency.id,
|
|
11
11
|
projection_id: dependency.projection_id,
|
|
12
12
|
kind: dependency.kind,
|
|
13
|
-
|
|
13
|
+
projection_type: dependency.projection_type,
|
|
14
14
|
reason: dependency.reason,
|
|
15
15
|
items: []
|
|
16
16
|
});
|
|
@@ -47,7 +47,7 @@ export function buildUiReviewGroups(items) {
|
|
|
47
47
|
id: dependency.id,
|
|
48
48
|
projection_id: dependency.projection_id,
|
|
49
49
|
kind: dependency.kind,
|
|
50
|
-
|
|
50
|
+
projection_type: dependency.projection_type,
|
|
51
51
|
reason: dependency.reason,
|
|
52
52
|
items: []
|
|
53
53
|
});
|
|
@@ -172,7 +172,7 @@ export function buildBundleAdoptionPriorities(report, confidenceRank) {
|
|
|
172
172
|
enums: bundle.enums.length,
|
|
173
173
|
capabilities: bundle.capabilities.length,
|
|
174
174
|
shapes: bundle.shapes.length,
|
|
175
|
-
|
|
175
|
+
widgets: bundle.widgets?.length || 0,
|
|
176
176
|
screens: bundle.screens.length,
|
|
177
177
|
workflows: bundle.workflows.length,
|
|
178
178
|
docs: bundle.docs.length
|
|
@@ -2,16 +2,16 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
componentById,
|
|
6
5
|
getJourneyDoc,
|
|
7
6
|
getStatement,
|
|
8
7
|
summarizeById,
|
|
9
8
|
getWorkflowDoc,
|
|
10
|
-
relatedComponentsForProjection,
|
|
11
9
|
relatedProjectionsForCapability,
|
|
12
|
-
relatedProjectionsForComponent,
|
|
13
10
|
relatedProjectionsForEntity,
|
|
14
|
-
|
|
11
|
+
relatedProjectionsForWidget,
|
|
12
|
+
relatedShapesForProjection,
|
|
13
|
+
relatedWidgetsForProjection,
|
|
14
|
+
widgetById
|
|
15
15
|
} from "../generator/context/shared.js";
|
|
16
16
|
import { generatorDefaultsMap } from "../generator/surfaces/shared.js";
|
|
17
17
|
|
|
@@ -1337,7 +1337,7 @@ function buildMaintainedOutputGroups(outputs = [], seams = [], {
|
|
|
1337
1337
|
|
|
1338
1338
|
function projectionKindForImpact(projection) {
|
|
1339
1339
|
if (!projection) return "unknown";
|
|
1340
|
-
if ((projection.http || []).length > 0 || projection.
|
|
1340
|
+
if ((projection.http || []).length > 0 || projection.type === "api" || projection.type === "backend") {
|
|
1341
1341
|
return "api";
|
|
1342
1342
|
}
|
|
1343
1343
|
if (
|
|
@@ -1345,9 +1345,9 @@ function projectionKindForImpact(projection) {
|
|
|
1345
1345
|
(projection.uiWeb || []).length > 0 ||
|
|
1346
1346
|
(projection.uiIos || []).length > 0 ||
|
|
1347
1347
|
(projection.uiScreens || []).length > 0 ||
|
|
1348
|
-
projection.
|
|
1349
|
-
projection.
|
|
1350
|
-
projection.
|
|
1348
|
+
projection.type === "web_surface" ||
|
|
1349
|
+
projection.type === "ios_surface" ||
|
|
1350
|
+
projection.type === "ui_contract"
|
|
1351
1351
|
) {
|
|
1352
1352
|
return "ui";
|
|
1353
1353
|
}
|
|
@@ -1355,7 +1355,7 @@ function projectionKindForImpact(projection) {
|
|
|
1355
1355
|
(projection.dbTables || []).length > 0 ||
|
|
1356
1356
|
(projection.dbColumns || []).length > 0 ||
|
|
1357
1357
|
(projection.dbRelations || []).length > 0 ||
|
|
1358
|
-
String(projection.
|
|
1358
|
+
String(projection.type || "").startsWith("db_")
|
|
1359
1359
|
) {
|
|
1360
1360
|
return "db";
|
|
1361
1361
|
}
|
|
@@ -1367,7 +1367,7 @@ function projectionSummaryForImpact(projection) {
|
|
|
1367
1367
|
return {
|
|
1368
1368
|
projection_id: projection.id,
|
|
1369
1369
|
kind: projectionKindForImpact(projection),
|
|
1370
|
-
|
|
1370
|
+
type: projection.type || null,
|
|
1371
1371
|
outputs: stableSortedStrings(projection.outputs || [])
|
|
1372
1372
|
};
|
|
1373
1373
|
}
|
|
@@ -1408,7 +1408,7 @@ function addImpact(map, impact) {
|
|
|
1408
1408
|
"changed_capability",
|
|
1409
1409
|
"changed_entity",
|
|
1410
1410
|
"changed_shape",
|
|
1411
|
-
"
|
|
1411
|
+
"changed_widget",
|
|
1412
1412
|
"changed_rule",
|
|
1413
1413
|
"changed_workflow",
|
|
1414
1414
|
"changed_journey",
|
|
@@ -1551,20 +1551,20 @@ function projectionImpactsFromShape(graph, shapeId) {
|
|
|
1551
1551
|
];
|
|
1552
1552
|
}
|
|
1553
1553
|
|
|
1554
|
-
function
|
|
1555
|
-
const
|
|
1556
|
-
if (!
|
|
1554
|
+
function projectionImpactsFromWidget(graph, widgetId) {
|
|
1555
|
+
const widget = widgetById(graph, widgetId);
|
|
1556
|
+
if (!widget) {
|
|
1557
1557
|
return [];
|
|
1558
1558
|
}
|
|
1559
1559
|
|
|
1560
1560
|
return impactsFromProjectionIds(
|
|
1561
1561
|
graph,
|
|
1562
|
-
|
|
1563
|
-
"
|
|
1564
|
-
(projection) => `Projection ${projection.id} is affected because
|
|
1562
|
+
relatedProjectionsForWidget(graph, widgetId),
|
|
1563
|
+
"changed_widget",
|
|
1564
|
+
(projection) => `Projection ${projection.id} is affected because widget ${widgetId} is used by that UI surface.`
|
|
1565
1565
|
).map((impact) => ({
|
|
1566
1566
|
...impact,
|
|
1567
|
-
|
|
1567
|
+
widget_ids: stableSortedStrings([widgetId])
|
|
1568
1568
|
}));
|
|
1569
1569
|
}
|
|
1570
1570
|
|
|
@@ -1602,8 +1602,8 @@ function buildProjectionImpacts(graph, { sliceArtifact, diffArtifact }) {
|
|
|
1602
1602
|
for (const impact of projectionImpactsFromShape(graph, entry.id)) addImpact(impactMap, impact);
|
|
1603
1603
|
}
|
|
1604
1604
|
|
|
1605
|
-
for (const entry of diffArtifact.components || []) {
|
|
1606
|
-
for (const impact of
|
|
1605
|
+
for (const entry of diffArtifact.widgets || diffArtifact.components || []) {
|
|
1606
|
+
for (const impact of projectionImpactsFromWidget(graph, entry.id)) addImpact(impactMap, impact);
|
|
1607
1607
|
}
|
|
1608
1608
|
|
|
1609
1609
|
for (const entry of diffArtifact.rules || []) {
|
|
@@ -1640,7 +1640,7 @@ function buildProjectionImpacts(graph, { sliceArtifact, diffArtifact }) {
|
|
|
1640
1640
|
(projection) => `Projection ${projection.id} is in scope because selected entity ${id} participates in that projection.`
|
|
1641
1641
|
)) addImpact(impactMap, impact);
|
|
1642
1642
|
} else if (kind === "widget") {
|
|
1643
|
-
for (const impact of
|
|
1643
|
+
for (const impact of projectionImpactsFromWidget(graph, id)) addImpact(impactMap, impact);
|
|
1644
1644
|
} else if (kind === "workflow") {
|
|
1645
1645
|
for (const impact of projectionImpactsFromWorkflow(graph, id, true)) addImpact(impactMap, impact);
|
|
1646
1646
|
} else if (kind === "journey") {
|
|
@@ -1675,7 +1675,7 @@ function buildGeneratorTargets(graph, projectionImpacts = [], diffArtifact = nul
|
|
|
1675
1675
|
}
|
|
1676
1676
|
};
|
|
1677
1677
|
|
|
1678
|
-
for (const entry of diffArtifact?.components || []) {
|
|
1678
|
+
for (const entry of diffArtifact?.widgets || diffArtifact?.components || []) {
|
|
1679
1679
|
addTarget({
|
|
1680
1680
|
target: "ui-widget-contract",
|
|
1681
1681
|
widget_id: entry.id,
|
|
@@ -1717,7 +1717,7 @@ function buildGeneratorTargets(graph, projectionImpacts = [], diffArtifact = nul
|
|
|
1717
1717
|
});
|
|
1718
1718
|
}
|
|
1719
1719
|
|
|
1720
|
-
if (projection.
|
|
1720
|
+
if (projection.type === "ui_contract") {
|
|
1721
1721
|
addTarget({
|
|
1722
1722
|
target: "ui-contract-graph",
|
|
1723
1723
|
projection_id: impact.projection_id,
|
|
@@ -1732,29 +1732,29 @@ function buildGeneratorTargets(graph, projectionImpacts = [], diffArtifact = nul
|
|
|
1732
1732
|
});
|
|
1733
1733
|
}
|
|
1734
1734
|
|
|
1735
|
-
const
|
|
1736
|
-
...(impact.
|
|
1737
|
-
...(impact.kind === "ui" ?
|
|
1735
|
+
const widgetIds = stableSortedStrings([
|
|
1736
|
+
...(impact.widget_ids || []),
|
|
1737
|
+
...(impact.kind === "ui" ? relatedWidgetsForProjection(graph, projection) : [])
|
|
1738
1738
|
]);
|
|
1739
1739
|
|
|
1740
|
-
for (const
|
|
1740
|
+
for (const widgetId of widgetIds) {
|
|
1741
1741
|
addTarget({
|
|
1742
1742
|
target: "ui-widget-contract",
|
|
1743
|
-
widget_id:
|
|
1743
|
+
widget_id: widgetId,
|
|
1744
1744
|
projection_id: impact.projection_id,
|
|
1745
1745
|
required: true,
|
|
1746
|
-
reason: `Projection ${impact.projection_id} is affected by widget ${
|
|
1746
|
+
reason: `Projection ${impact.projection_id} is affected by widget ${widgetId}, so the widget contract should be refreshed.`
|
|
1747
1747
|
});
|
|
1748
1748
|
addTarget({
|
|
1749
1749
|
target: "widget-behavior-report",
|
|
1750
|
-
widget_id:
|
|
1750
|
+
widget_id: widgetId,
|
|
1751
1751
|
projection_id: impact.projection_id,
|
|
1752
1752
|
required: true,
|
|
1753
|
-
reason: `Projection ${impact.projection_id} is affected by widget ${
|
|
1753
|
+
reason: `Projection ${impact.projection_id} is affected by widget ${widgetId}, so behavior data/event/action wiring should be reviewed.`
|
|
1754
1754
|
});
|
|
1755
1755
|
}
|
|
1756
1756
|
|
|
1757
|
-
if (projection.
|
|
1757
|
+
if (projection.type === "web_surface") {
|
|
1758
1758
|
addTarget({
|
|
1759
1759
|
target: "ui-surface-contract",
|
|
1760
1760
|
projection_id: impact.projection_id,
|
|
@@ -1777,7 +1777,7 @@ function buildGeneratorTargets(graph, projectionImpacts = [], diffArtifact = nul
|
|
|
1777
1777
|
}
|
|
1778
1778
|
}
|
|
1779
1779
|
|
|
1780
|
-
if (projection.
|
|
1780
|
+
if (projection.type === "ios_surface") {
|
|
1781
1781
|
addTarget({
|
|
1782
1782
|
target: "swiftui-app",
|
|
1783
1783
|
projection_id: impact.projection_id,
|
|
@@ -1786,7 +1786,7 @@ function buildGeneratorTargets(graph, projectionImpacts = [], diffArtifact = nul
|
|
|
1786
1786
|
});
|
|
1787
1787
|
}
|
|
1788
1788
|
|
|
1789
|
-
if (String(projection.
|
|
1789
|
+
if (String(projection.type || "").startsWith("db_")) {
|
|
1790
1790
|
addTarget({
|
|
1791
1791
|
target: "db-contract-graph",
|
|
1792
1792
|
projection_id: impact.projection_id,
|
package/src/archive/schema.js
CHANGED
package/src/cli.js
CHANGED
|
@@ -771,7 +771,7 @@ function printReleaseHelp() {
|
|
|
771
771
|
console.log(" topogram release status");
|
|
772
772
|
console.log(" topogram release status --json");
|
|
773
773
|
console.log(" topogram release status --strict");
|
|
774
|
-
console.log(" topogram release status --strict --write-report ./release-
|
|
774
|
+
console.log(" topogram release status --strict --write-report ./docs/release-matrix.md");
|
|
775
775
|
console.log(" topogram release roll-consumers 0.3.46 --watch");
|
|
776
776
|
console.log(" topogram release roll-consumers --latest --watch");
|
|
777
777
|
console.log("");
|
|
@@ -1037,8 +1037,8 @@ function targetRequiresImplementationProvider(target) {
|
|
|
1037
1037
|
|
|
1038
1038
|
function topologyComponentReferences(component) {
|
|
1039
1039
|
return {
|
|
1040
|
-
uses_api: component.uses_api ||
|
|
1041
|
-
uses_database: component.uses_database ||
|
|
1040
|
+
uses_api: component.uses_api || null,
|
|
1041
|
+
uses_database: component.uses_database || null
|
|
1042
1042
|
};
|
|
1043
1043
|
}
|
|
1044
1044
|
|
|
@@ -1054,10 +1054,10 @@ function summarizeProjectTopology(config) {
|
|
|
1054
1054
|
ownership: output?.ownership || null
|
|
1055
1055
|
}))
|
|
1056
1056
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
1057
|
-
const runtimes = (config?.topology?.runtimes ||
|
|
1057
|
+
const runtimes = (config?.topology?.runtimes || [])
|
|
1058
1058
|
.map((component) => ({
|
|
1059
1059
|
id: component.id,
|
|
1060
|
-
kind: component.kind
|
|
1060
|
+
kind: component.kind,
|
|
1061
1061
|
projection: component.projection,
|
|
1062
1062
|
generator: {
|
|
1063
1063
|
id: component.generator?.id || null,
|
|
@@ -1097,7 +1097,7 @@ function publicProjectTopology(topology) {
|
|
|
1097
1097
|
return topology || null;
|
|
1098
1098
|
}
|
|
1099
1099
|
return {
|
|
1100
|
-
...Object.fromEntries(Object.entries(topology).filter(([key]) =>
|
|
1100
|
+
...Object.fromEntries(Object.entries(topology).filter(([key]) => key !== "components")),
|
|
1101
1101
|
runtimes: topology.runtimes || []
|
|
1102
1102
|
};
|
|
1103
1103
|
}
|
|
@@ -1818,7 +1818,7 @@ function annotateGeneratorPolicyDiagnostics(diagnostics, bindings) {
|
|
|
1818
1818
|
return diagnostics.map((diagnostic) => {
|
|
1819
1819
|
const binding = bindings.find((item) => (
|
|
1820
1820
|
item.packageName === diagnostic.packageName &&
|
|
1821
|
-
(!diagnostic.
|
|
1821
|
+
(!diagnostic.runtimeId || item.runtimeId === diagnostic.runtimeId)
|
|
1822
1822
|
));
|
|
1823
1823
|
if (!binding) {
|
|
1824
1824
|
return diagnostic;
|
|
@@ -1846,11 +1846,11 @@ function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
|
1846
1846
|
diagnostics.push({
|
|
1847
1847
|
code: "generator_package_dependency_missing",
|
|
1848
1848
|
severity: "warning",
|
|
1849
|
-
message: `
|
|
1849
|
+
message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is not declared in package.json dependencies.`,
|
|
1850
1850
|
path: binding.packageInfo.installedPackageJsonPath,
|
|
1851
1851
|
suggestedFix: `Declare '${binding.packageName}' in package.json devDependencies so generator adoption is visible in package review.`,
|
|
1852
1852
|
step: "generator-policy",
|
|
1853
|
-
|
|
1853
|
+
runtimeId: binding.runtimeId,
|
|
1854
1854
|
generatorId: binding.generatorId,
|
|
1855
1855
|
packageName: binding.packageName,
|
|
1856
1856
|
version: binding.version,
|
|
@@ -1870,11 +1870,11 @@ function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
|
1870
1870
|
diagnostics.push({
|
|
1871
1871
|
code: "generator_package_version_drift",
|
|
1872
1872
|
severity: "warning",
|
|
1873
|
-
message: `
|
|
1873
|
+
message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is installed at '${binding.packageInfo.installedVersion}', but package-lock records '${binding.packageInfo.lockfileVersion}'.`,
|
|
1874
1874
|
path: binding.packageInfo.lockfilePath,
|
|
1875
1875
|
suggestedFix: "Run the package manager install command and review the resulting lockfile before pinning generator policy.",
|
|
1876
1876
|
step: "generator-policy",
|
|
1877
|
-
|
|
1877
|
+
runtimeId: binding.runtimeId,
|
|
1878
1878
|
generatorId: binding.generatorId,
|
|
1879
1879
|
packageName: binding.packageName,
|
|
1880
1880
|
version: binding.version,
|
|
@@ -1975,7 +1975,7 @@ function buildGeneratorPolicyExplainPayload(projectPath) {
|
|
|
1975
1975
|
`scopes=${policy.allowedPackageScopes.join(", ") || "(none)"}`,
|
|
1976
1976
|
`packages=${policy.allowedPackages.join(", ") || "(none)"}`
|
|
1977
1977
|
].join("; "),
|
|
1978
|
-
`
|
|
1978
|
+
`Runtime '${binding.runtimeId}' package-backed generator must be from an allowed package or scope.`,
|
|
1979
1979
|
`Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after reviewing the generator package.`
|
|
1980
1980
|
));
|
|
1981
1981
|
const pinnedVersion = policy.pinnedVersions[binding.packageName] || policy.pinnedVersions[binding.generatorId] || null;
|
|
@@ -1984,7 +1984,7 @@ function buildGeneratorPolicyExplainPayload(projectPath) {
|
|
|
1984
1984
|
!pinnedVersion || pinnedVersion === binding.version,
|
|
1985
1985
|
binding.version,
|
|
1986
1986
|
pinnedVersion || "(unpinned)",
|
|
1987
|
-
`
|
|
1987
|
+
`Runtime '${binding.runtimeId}' generator version must match its policy pin when one exists.`,
|
|
1988
1988
|
`Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after review.`
|
|
1989
1989
|
));
|
|
1990
1990
|
}
|
|
@@ -2024,7 +2024,7 @@ function printGeneratorPolicyCheckPayload(payload) {
|
|
|
2024
2024
|
console.log(`Defaulted: ${payload.defaulted ? "yes" : "no"}`);
|
|
2025
2025
|
console.log(`Package-backed generators: ${payload.bindings.length}`);
|
|
2026
2026
|
for (const binding of payload.bindings) {
|
|
2027
|
-
console.log(`- ${binding.
|
|
2027
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2028
2028
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2029
2029
|
if (binding.packageInfo.dependencySpec) {
|
|
2030
2030
|
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
@@ -2066,7 +2066,7 @@ function printGeneratorPolicyStatusPayload(payload) {
|
|
|
2066
2066
|
console.log("Generator packages:");
|
|
2067
2067
|
}
|
|
2068
2068
|
for (const binding of payload.bindings) {
|
|
2069
|
-
console.log(`- ${binding.
|
|
2069
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2070
2070
|
console.log(` allowed: ${binding.allowed ? "yes" : "no"}`);
|
|
2071
2071
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2072
2072
|
console.log(` dependency: ${binding.packageInfo.dependencyField && binding.packageInfo.dependencySpec ? `${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}` : "(not declared)"}`);
|
|
@@ -2107,7 +2107,7 @@ function printGeneratorPolicyExplainPayload(payload) {
|
|
|
2107
2107
|
console.log("");
|
|
2108
2108
|
console.log("Package-backed generators:");
|
|
2109
2109
|
for (const binding of payload.bindings) {
|
|
2110
|
-
console.log(`- ${binding.
|
|
2110
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2111
2111
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2112
2112
|
if (binding.packageInfo.dependencySpec) {
|
|
2113
2113
|
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
@@ -2944,10 +2944,15 @@ function printReleaseStatus(payload) {
|
|
|
2944
2944
|
* @returns {string}
|
|
2945
2945
|
*/
|
|
2946
2946
|
function renderReleaseStatusMarkdown(payload) {
|
|
2947
|
+
const matrix = buildReleaseMatrixCatalogPayload();
|
|
2948
|
+
const catalogConsumer = payload.consumers.find((consumer) => consumer.name === "topograms");
|
|
2949
|
+
const demoConsumer = payload.consumers.find((consumer) => consumer.name === "topogram-demo-todo");
|
|
2947
2950
|
const lines = [
|
|
2948
|
-
|
|
2951
|
+
"# Known-Good Release Matrix",
|
|
2949
2952
|
"",
|
|
2950
|
-
`
|
|
2953
|
+
"This matrix is generated by `topogram release status --strict --write-report`.",
|
|
2954
|
+
`Date checked: ${new Date().toISOString().slice(0, 10)}.`,
|
|
2955
|
+
"Treat it as a dated release audit, not a floating compatibility promise.",
|
|
2951
2956
|
"",
|
|
2952
2957
|
"## Summary",
|
|
2953
2958
|
"",
|
|
@@ -2957,12 +2962,48 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2957
2962
|
`- Consumer pins: ${payload.consumerPins.matching}/${payload.consumerPins.known} matching`,
|
|
2958
2963
|
`- Consumer CI: ${payload.consumerCi.passing}/${payload.consumerCi.checked} passing`,
|
|
2959
2964
|
`- Strict status: ${payload.ok ? "passed" : "failed"}`,
|
|
2965
|
+
"",
|
|
2966
|
+
"## Core",
|
|
2967
|
+
"",
|
|
2968
|
+
"| Package or Repo | Version or Commit | Verification |",
|
|
2969
|
+
"| --- | --- | --- |",
|
|
2970
|
+
`| \`${payload.packageName}\` | \`${payload.localVersion}\` | Publish CLI Package, strict release status, fresh npmjs smoke, and installed CLI smoke passed |`,
|
|
2971
|
+
`| \`attebury/topograms\` catalog | \`${releaseMatrixConsumerCommit(catalogConsumer)}\` | ${releaseMatrixConsumerVerification(catalogConsumer, "Catalog Verification", payload.localVersion)} |`,
|
|
2972
|
+
`| \`topogram-demo-todo\` | \`${releaseMatrixConsumerCommit(demoConsumer)}\` | ${releaseMatrixConsumerVerification(demoConsumer, "Demo Verification", payload.localVersion)} |`,
|
|
2973
|
+
"",
|
|
2974
|
+
"## Catalog Entries",
|
|
2975
|
+
"",
|
|
2976
|
+
"| Catalog ID | Kind | Package | Version | Stack |",
|
|
2977
|
+
"| --- | --- | --- | --- | --- |"
|
|
2978
|
+
];
|
|
2979
|
+
if (matrix.entries.length > 0) {
|
|
2980
|
+
for (const entry of matrix.entries) {
|
|
2981
|
+
lines.push(`| \`${entry.id}\` | ${entry.kind} | \`${entry.package}\` | \`${entry.defaultVersion}\` | ${escapeMarkdownTableCell(entry.stack || "not declared")} |`);
|
|
2982
|
+
}
|
|
2983
|
+
} else {
|
|
2984
|
+
lines.push("| unavailable | unavailable | unavailable | unavailable | Catalog could not be loaded for this report |");
|
|
2985
|
+
}
|
|
2986
|
+
lines.push(
|
|
2987
|
+
"",
|
|
2988
|
+
"## Generator Packages",
|
|
2989
|
+
"",
|
|
2990
|
+
"| Generator Package | Surface | Catalog usage |",
|
|
2991
|
+
"| --- | --- | --- |"
|
|
2992
|
+
);
|
|
2993
|
+
if (matrix.generators.length > 0) {
|
|
2994
|
+
for (const generator of matrix.generators) {
|
|
2995
|
+
lines.push(`| \`${generator.package}\` | ${escapeMarkdownTableCell(generator.surface)} | ${escapeMarkdownTableCell(generator.catalogIds.join(", "))} |`);
|
|
2996
|
+
}
|
|
2997
|
+
} else {
|
|
2998
|
+
lines.push("| unavailable | unavailable | Catalog generator metadata could not be loaded for this report |");
|
|
2999
|
+
}
|
|
3000
|
+
lines.push(
|
|
2960
3001
|
"",
|
|
2961
3002
|
"## Consumers",
|
|
2962
3003
|
"",
|
|
2963
3004
|
"| Repo | Pin | Workflow | Status | Run |",
|
|
2964
3005
|
"| --- | --- | --- | --- | --- |"
|
|
2965
|
-
|
|
3006
|
+
);
|
|
2966
3007
|
for (const consumer of payload.consumers) {
|
|
2967
3008
|
const workflow = consumer.workflow || consumer.ci?.expectedWorkflow || "";
|
|
2968
3009
|
const run = consumer.ci?.run;
|
|
@@ -2970,6 +3011,32 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2970
3011
|
const url = run?.url ? `[${run.databaseId || "run"}](${run.url})` : "";
|
|
2971
3012
|
lines.push(`| \`${consumer.name}\` | \`${consumer.version || "missing"}\` | ${escapeMarkdownTableCell(workflow)} | ${escapeMarkdownTableCell(status)} | ${url} |`);
|
|
2972
3013
|
}
|
|
3014
|
+
lines.push(
|
|
3015
|
+
"",
|
|
3016
|
+
"## Consumer Proofs",
|
|
3017
|
+
"",
|
|
3018
|
+
"The external Todo demo is the canonical end-to-end consumer proof for the current catalog-backed workflow:",
|
|
3019
|
+
"",
|
|
3020
|
+
"```bash",
|
|
3021
|
+
"topogram new ./todo-demo --template todo",
|
|
3022
|
+
"cd ./todo-demo",
|
|
3023
|
+
"npm install",
|
|
3024
|
+
"npm run check",
|
|
3025
|
+
"npm run generate",
|
|
3026
|
+
"npm run app:compile",
|
|
3027
|
+
"npm run verify",
|
|
3028
|
+
"npm run app:runtime",
|
|
3029
|
+
"```",
|
|
3030
|
+
"",
|
|
3031
|
+
"The demo CI also verifies `topogram new` from the default public catalog and from the repo-local catalog fixture. That prevents local fixtures from masking a broken published catalog alias."
|
|
3032
|
+
);
|
|
3033
|
+
const reportDiagnostics = [...matrix.diagnostics];
|
|
3034
|
+
if (reportDiagnostics.length > 0) {
|
|
3035
|
+
lines.push("", "## Report Diagnostics", "");
|
|
3036
|
+
for (const diagnostic of reportDiagnostics) {
|
|
3037
|
+
lines.push(`- **${diagnostic.severity || "warning"}** \`${diagnostic.code || "release_report_catalog_unavailable"}\`: ${diagnostic.message}`);
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
2973
3040
|
if (payload.diagnostics.length > 0) {
|
|
2974
3041
|
lines.push("", "## Diagnostics", "");
|
|
2975
3042
|
for (const diagnostic of payload.diagnostics) {
|
|
@@ -2987,6 +3054,92 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2987
3054
|
return `${lines.join("\n")}\n`;
|
|
2988
3055
|
}
|
|
2989
3056
|
|
|
3057
|
+
/**
|
|
3058
|
+
* @returns {{ entries: any[], generators: Array<{ package: string, surface: string, catalogIds: string[] }>, diagnostics: Array<Record<string, any>> }}
|
|
3059
|
+
*/
|
|
3060
|
+
function buildReleaseMatrixCatalogPayload() {
|
|
3061
|
+
try {
|
|
3062
|
+
const loaded = loadCatalog(null);
|
|
3063
|
+
const entries = [...loaded.catalog.entries].sort((left, right) => {
|
|
3064
|
+
if (left.kind !== right.kind) {
|
|
3065
|
+
return left.kind === "template" ? -1 : 1;
|
|
3066
|
+
}
|
|
3067
|
+
return left.id.localeCompare(right.id);
|
|
3068
|
+
});
|
|
3069
|
+
const generatorMap = new Map();
|
|
3070
|
+
for (const entry of entries) {
|
|
3071
|
+
for (const packageName of Array.isArray(entry.generators) ? entry.generators : []) {
|
|
3072
|
+
if (!generatorMap.has(packageName)) {
|
|
3073
|
+
generatorMap.set(packageName, {
|
|
3074
|
+
package: packageName,
|
|
3075
|
+
surface: releaseMatrixGeneratorSurface(packageName),
|
|
3076
|
+
catalogIds: []
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
generatorMap.get(packageName).catalogIds.push(entry.id);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
const generators = [...generatorMap.values()].sort((left, right) => left.package.localeCompare(right.package));
|
|
3083
|
+
return {
|
|
3084
|
+
entries,
|
|
3085
|
+
generators,
|
|
3086
|
+
diagnostics: loaded.diagnostics || []
|
|
3087
|
+
};
|
|
3088
|
+
} catch (error) {
|
|
3089
|
+
return {
|
|
3090
|
+
entries: [],
|
|
3091
|
+
generators: [],
|
|
3092
|
+
diagnostics: [{
|
|
3093
|
+
code: "release_report_catalog_unavailable",
|
|
3094
|
+
severity: "warning",
|
|
3095
|
+
message: messageFromError(error)
|
|
3096
|
+
}]
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
/**
|
|
3102
|
+
* @param {string} packageName
|
|
3103
|
+
* @returns {string}
|
|
3104
|
+
*/
|
|
3105
|
+
function releaseMatrixGeneratorSurface(packageName) {
|
|
3106
|
+
if (packageName.includes("-web")) return "web";
|
|
3107
|
+
if (packageName.includes("-api")) return "api";
|
|
3108
|
+
if (packageName.includes("-db")) return "database";
|
|
3109
|
+
if (packageName.includes("-native")) return "native";
|
|
3110
|
+
return "not declared";
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
/**
|
|
3114
|
+
* @param {any} consumer
|
|
3115
|
+
* @returns {string}
|
|
3116
|
+
*/
|
|
3117
|
+
function releaseMatrixConsumerCommit(consumer) {
|
|
3118
|
+
return shortSha(consumer?.ci?.headSha || consumer?.ci?.run?.headSha || null) || "unknown";
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
/**
|
|
3122
|
+
* @param {any} consumer
|
|
3123
|
+
* @param {string} workflowName
|
|
3124
|
+
* @param {string} version
|
|
3125
|
+
* @returns {string}
|
|
3126
|
+
*/
|
|
3127
|
+
function releaseMatrixConsumerVerification(consumer, workflowName, version) {
|
|
3128
|
+
const status = consumer?.ci?.run
|
|
3129
|
+
? `${consumer.ci.run.status || "unknown"}/${consumer.ci.run.conclusion || "unknown"}`
|
|
3130
|
+
: "not checked";
|
|
3131
|
+
return `${workflowName}: ${status}; pinned ${CLI_PACKAGE_NAME}@${version}`;
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
/**
|
|
3135
|
+
* @param {string|null|undefined} value
|
|
3136
|
+
* @returns {string|null}
|
|
3137
|
+
*/
|
|
3138
|
+
function shortSha(value) {
|
|
3139
|
+
const text = String(value || "").trim();
|
|
3140
|
+
return text ? text.slice(0, 7) : null;
|
|
3141
|
+
}
|
|
3142
|
+
|
|
2990
3143
|
/**
|
|
2991
3144
|
* @param {string|null|undefined} value
|
|
2992
3145
|
* @returns {string}
|
|
@@ -5533,7 +5686,7 @@ function importCandidateCounts(summary) {
|
|
|
5533
5686
|
apiRoutes: candidates.api?.routes?.length || 0,
|
|
5534
5687
|
uiScreens: candidates.ui?.screens?.length || 0,
|
|
5535
5688
|
uiRoutes: candidates.ui?.routes?.length || 0,
|
|
5536
|
-
|
|
5689
|
+
uiWidgets: candidates.ui?.widgets?.length || candidates.ui?.components?.length || 0,
|
|
5537
5690
|
workflows: candidates.workflows?.workflows?.length || 0,
|
|
5538
5691
|
verifications: candidates.verification?.verifications?.length || 0
|
|
5539
5692
|
};
|
|
@@ -41,7 +41,7 @@ import { generateVanillaWebApp } from "./surfaces/web/vanilla.js";
|
|
|
41
41
|
* @property {Record<string, any>} graph
|
|
42
42
|
* @property {Record<string, any>} projection
|
|
43
43
|
* @property {Record<string, any>} runtime
|
|
44
|
-
* @property {Record<string, any>} component
|
|
44
|
+
* @property {Record<string, any>} [component] Legacy runtime alias for existing generator packages.
|
|
45
45
|
* @property {Record<string, any>|null} [topology]
|
|
46
46
|
* @property {Record<string, any>} [contracts]
|
|
47
47
|
* @property {Record<string, any>|null} [implementation]
|
|
@@ -88,7 +88,23 @@ function requiredManifest(generatorId) {
|
|
|
88
88
|
* @returns {string}
|
|
89
89
|
*/
|
|
90
90
|
function runtimeFor(context) {
|
|
91
|
-
return context.runtime || context.component || {};
|
|
91
|
+
return normalizeRuntimeForGenerator(context.runtime || context.component || {});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Keep package-backed generator context canonical while preserving aliases for already-published adapters.
|
|
96
|
+
*
|
|
97
|
+
* @param {Record<string, any>} runtime
|
|
98
|
+
* @returns {Record<string, any>}
|
|
99
|
+
*/
|
|
100
|
+
function normalizeRuntimeForGenerator(runtime) {
|
|
101
|
+
const apiRuntime = runtime.apiRuntime || runtime.apiComponent || null;
|
|
102
|
+
const databaseRuntime = runtime.databaseRuntime || runtime.databaseComponent || null;
|
|
103
|
+
return {
|
|
104
|
+
...runtime,
|
|
105
|
+
...(apiRuntime ? { apiRuntime, apiComponent: apiRuntime } : {}),
|
|
106
|
+
...(databaseRuntime ? { databaseRuntime, databaseComponent: databaseRuntime } : {})
|
|
107
|
+
};
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
/**
|
|
@@ -108,7 +124,7 @@ function projectionIdFor(context) {
|
|
|
108
124
|
function serverOptions(context, profile) {
|
|
109
125
|
const projectionId = projectionIdFor(context);
|
|
110
126
|
const runtime = runtimeFor(context);
|
|
111
|
-
const dbProjectionId = runtime.databaseComponent?.projection?.id || context.options?.dbProjectionId;
|
|
127
|
+
const dbProjectionId = runtime.databaseRuntime?.projection?.id || runtime.databaseComponent?.projection?.id || context.options?.dbProjectionId;
|
|
112
128
|
return {
|
|
113
129
|
...(context.options || {}),
|
|
114
130
|
projectionId,
|
|
@@ -178,7 +194,7 @@ export const BUNDLED_GENERATOR_ADAPTERS = [
|
|
|
178
194
|
manifest: requiredManifest("topogram/hono"),
|
|
179
195
|
generate(context) {
|
|
180
196
|
const runtime = runtimeFor(context);
|
|
181
|
-
if (runtime && !runtime.
|
|
197
|
+
if (runtime && !runtime.databaseRuntime) {
|
|
182
198
|
return fileResult(generateStatelessServer(context.graph, serverOptions(context, "hono")));
|
|
183
199
|
}
|
|
184
200
|
return fileResult(generateHonoServer(context.graph, serverOptions(context, "hono")));
|
|
@@ -188,7 +204,7 @@ export const BUNDLED_GENERATOR_ADAPTERS = [
|
|
|
188
204
|
manifest: requiredManifest("topogram/express"),
|
|
189
205
|
generate(context) {
|
|
190
206
|
const runtime = runtimeFor(context);
|
|
191
|
-
if (runtime && !runtime.
|
|
207
|
+
if (runtime && !runtime.databaseRuntime) {
|
|
192
208
|
return fileResult(generateStatelessServer(context.graph, serverOptions(context, "express")));
|
|
193
209
|
}
|
|
194
210
|
return fileResult(generateExpressServer(context.graph, serverOptions(context, "express")));
|
|
@@ -277,8 +293,8 @@ function loadPackageGeneratorAdapter(manifest, runtime, options = {}) {
|
|
|
277
293
|
const diagnostics = generatorPolicyDiagnosticsForBindings(
|
|
278
294
|
loadGeneratorPolicy(rootDir),
|
|
279
295
|
[{
|
|
280
|
-
|
|
281
|
-
|
|
296
|
+
runtimeId: String(runtime?.id || "unknown"),
|
|
297
|
+
runtimeKind: String(runtime?.kind || runtime?.type || manifest.surface || "unknown"),
|
|
282
298
|
projection: String(runtime?.projection?.id || runtime?.projection || "unknown"),
|
|
283
299
|
generatorId: String(runtime?.generator?.id || manifest.id),
|
|
284
300
|
version: String(runtime?.generator?.version || manifest.version),
|