@topogram/cli 0.3.53 → 0.3.55
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/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 +177 -22
- package/src/generator/adapters.js +2 -2
- 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 +7 -5
- package/src/generator/runtime/compile-check.js +3 -1
- package/src/generator/runtime/deployment.js +3 -1
- package/src/generator/runtime/environment.js +22 -19
- package/src/generator/runtime/shared.js +7 -7
- package/src/generator/surfaces/contracts.js +1 -1
- 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/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/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/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
|
@@ -135,7 +135,8 @@ const KNOWN_CLI_CONSUMER_REPOS = [
|
|
|
135
135
|
"topogram-starters",
|
|
136
136
|
"topogram-template-todo",
|
|
137
137
|
"topogram-demo-todo",
|
|
138
|
-
"topogram-hello"
|
|
138
|
+
"topogram-hello",
|
|
139
|
+
"topograms"
|
|
139
140
|
];
|
|
140
141
|
const KNOWN_CLI_CONSUMER_WORKFLOWS = {
|
|
141
142
|
"topogram-generator-express-api": "Generator Verification",
|
|
@@ -149,7 +150,8 @@ const KNOWN_CLI_CONSUMER_WORKFLOWS = {
|
|
|
149
150
|
"topogram-starters": "Starter Verification",
|
|
150
151
|
"topogram-template-todo": "Template Verification",
|
|
151
152
|
"topogram-demo-todo": "Demo Verification",
|
|
152
|
-
"topogram-hello": "Topogram Package Verification"
|
|
153
|
+
"topogram-hello": "Topogram Package Verification",
|
|
154
|
+
"topograms": "Catalog Verification"
|
|
153
155
|
};
|
|
154
156
|
const PACKAGE_UPDATE_CLI_CHECK_SCRIPTS = [
|
|
155
157
|
"cli:surface",
|
|
@@ -769,7 +771,7 @@ function printReleaseHelp() {
|
|
|
769
771
|
console.log(" topogram release status");
|
|
770
772
|
console.log(" topogram release status --json");
|
|
771
773
|
console.log(" topogram release status --strict");
|
|
772
|
-
console.log(" topogram release status --strict --write-report ./release-
|
|
774
|
+
console.log(" topogram release status --strict --write-report ./docs/release-matrix.md");
|
|
773
775
|
console.log(" topogram release roll-consumers 0.3.46 --watch");
|
|
774
776
|
console.log(" topogram release roll-consumers --latest --watch");
|
|
775
777
|
console.log("");
|
|
@@ -1035,8 +1037,8 @@ function targetRequiresImplementationProvider(target) {
|
|
|
1035
1037
|
|
|
1036
1038
|
function topologyComponentReferences(component) {
|
|
1037
1039
|
return {
|
|
1038
|
-
uses_api: component.uses_api ||
|
|
1039
|
-
uses_database: component.uses_database ||
|
|
1040
|
+
uses_api: component.uses_api || null,
|
|
1041
|
+
uses_database: component.uses_database || null
|
|
1040
1042
|
};
|
|
1041
1043
|
}
|
|
1042
1044
|
|
|
@@ -1052,10 +1054,10 @@ function summarizeProjectTopology(config) {
|
|
|
1052
1054
|
ownership: output?.ownership || null
|
|
1053
1055
|
}))
|
|
1054
1056
|
.sort((left, right) => left.name.localeCompare(right.name));
|
|
1055
|
-
const runtimes = (config?.topology?.runtimes ||
|
|
1057
|
+
const runtimes = (config?.topology?.runtimes || [])
|
|
1056
1058
|
.map((component) => ({
|
|
1057
1059
|
id: component.id,
|
|
1058
|
-
kind: component.kind
|
|
1060
|
+
kind: component.kind,
|
|
1059
1061
|
projection: component.projection,
|
|
1060
1062
|
generator: {
|
|
1061
1063
|
id: component.generator?.id || null,
|
|
@@ -1095,7 +1097,7 @@ function publicProjectTopology(topology) {
|
|
|
1095
1097
|
return topology || null;
|
|
1096
1098
|
}
|
|
1097
1099
|
return {
|
|
1098
|
-
...Object.fromEntries(Object.entries(topology).filter(([key]) =>
|
|
1100
|
+
...Object.fromEntries(Object.entries(topology).filter(([key]) => key !== "components")),
|
|
1099
1101
|
runtimes: topology.runtimes || []
|
|
1100
1102
|
};
|
|
1101
1103
|
}
|
|
@@ -1816,7 +1818,7 @@ function annotateGeneratorPolicyDiagnostics(diagnostics, bindings) {
|
|
|
1816
1818
|
return diagnostics.map((diagnostic) => {
|
|
1817
1819
|
const binding = bindings.find((item) => (
|
|
1818
1820
|
item.packageName === diagnostic.packageName &&
|
|
1819
|
-
(!diagnostic.
|
|
1821
|
+
(!diagnostic.runtimeId || item.runtimeId === diagnostic.runtimeId)
|
|
1820
1822
|
));
|
|
1821
1823
|
if (!binding) {
|
|
1822
1824
|
return diagnostic;
|
|
@@ -1844,11 +1846,11 @@ function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
|
1844
1846
|
diagnostics.push({
|
|
1845
1847
|
code: "generator_package_dependency_missing",
|
|
1846
1848
|
severity: "warning",
|
|
1847
|
-
message: `
|
|
1849
|
+
message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is not declared in package.json dependencies.`,
|
|
1848
1850
|
path: binding.packageInfo.installedPackageJsonPath,
|
|
1849
1851
|
suggestedFix: `Declare '${binding.packageName}' in package.json devDependencies so generator adoption is visible in package review.`,
|
|
1850
1852
|
step: "generator-policy",
|
|
1851
|
-
|
|
1853
|
+
runtimeId: binding.runtimeId,
|
|
1852
1854
|
generatorId: binding.generatorId,
|
|
1853
1855
|
packageName: binding.packageName,
|
|
1854
1856
|
version: binding.version,
|
|
@@ -1868,11 +1870,11 @@ function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
|
1868
1870
|
diagnostics.push({
|
|
1869
1871
|
code: "generator_package_version_drift",
|
|
1870
1872
|
severity: "warning",
|
|
1871
|
-
message: `
|
|
1873
|
+
message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is installed at '${binding.packageInfo.installedVersion}', but package-lock records '${binding.packageInfo.lockfileVersion}'.`,
|
|
1872
1874
|
path: binding.packageInfo.lockfilePath,
|
|
1873
1875
|
suggestedFix: "Run the package manager install command and review the resulting lockfile before pinning generator policy.",
|
|
1874
1876
|
step: "generator-policy",
|
|
1875
|
-
|
|
1877
|
+
runtimeId: binding.runtimeId,
|
|
1876
1878
|
generatorId: binding.generatorId,
|
|
1877
1879
|
packageName: binding.packageName,
|
|
1878
1880
|
version: binding.version,
|
|
@@ -1973,7 +1975,7 @@ function buildGeneratorPolicyExplainPayload(projectPath) {
|
|
|
1973
1975
|
`scopes=${policy.allowedPackageScopes.join(", ") || "(none)"}`,
|
|
1974
1976
|
`packages=${policy.allowedPackages.join(", ") || "(none)"}`
|
|
1975
1977
|
].join("; "),
|
|
1976
|
-
`
|
|
1978
|
+
`Runtime '${binding.runtimeId}' package-backed generator must be from an allowed package or scope.`,
|
|
1977
1979
|
`Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after reviewing the generator package.`
|
|
1978
1980
|
));
|
|
1979
1981
|
const pinnedVersion = policy.pinnedVersions[binding.packageName] || policy.pinnedVersions[binding.generatorId] || null;
|
|
@@ -1982,7 +1984,7 @@ function buildGeneratorPolicyExplainPayload(projectPath) {
|
|
|
1982
1984
|
!pinnedVersion || pinnedVersion === binding.version,
|
|
1983
1985
|
binding.version,
|
|
1984
1986
|
pinnedVersion || "(unpinned)",
|
|
1985
|
-
`
|
|
1987
|
+
`Runtime '${binding.runtimeId}' generator version must match its policy pin when one exists.`,
|
|
1986
1988
|
`Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after review.`
|
|
1987
1989
|
));
|
|
1988
1990
|
}
|
|
@@ -2022,7 +2024,7 @@ function printGeneratorPolicyCheckPayload(payload) {
|
|
|
2022
2024
|
console.log(`Defaulted: ${payload.defaulted ? "yes" : "no"}`);
|
|
2023
2025
|
console.log(`Package-backed generators: ${payload.bindings.length}`);
|
|
2024
2026
|
for (const binding of payload.bindings) {
|
|
2025
|
-
console.log(`- ${binding.
|
|
2027
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2026
2028
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2027
2029
|
if (binding.packageInfo.dependencySpec) {
|
|
2028
2030
|
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
@@ -2064,7 +2066,7 @@ function printGeneratorPolicyStatusPayload(payload) {
|
|
|
2064
2066
|
console.log("Generator packages:");
|
|
2065
2067
|
}
|
|
2066
2068
|
for (const binding of payload.bindings) {
|
|
2067
|
-
console.log(`- ${binding.
|
|
2069
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2068
2070
|
console.log(` allowed: ${binding.allowed ? "yes" : "no"}`);
|
|
2069
2071
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2070
2072
|
console.log(` dependency: ${binding.packageInfo.dependencyField && binding.packageInfo.dependencySpec ? `${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}` : "(not declared)"}`);
|
|
@@ -2105,7 +2107,7 @@ function printGeneratorPolicyExplainPayload(payload) {
|
|
|
2105
2107
|
console.log("");
|
|
2106
2108
|
console.log("Package-backed generators:");
|
|
2107
2109
|
for (const binding of payload.bindings) {
|
|
2108
|
-
console.log(`- ${binding.
|
|
2110
|
+
console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
|
|
2109
2111
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
2110
2112
|
if (binding.packageInfo.dependencySpec) {
|
|
2111
2113
|
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
@@ -2942,10 +2944,15 @@ function printReleaseStatus(payload) {
|
|
|
2942
2944
|
* @returns {string}
|
|
2943
2945
|
*/
|
|
2944
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");
|
|
2945
2950
|
const lines = [
|
|
2946
|
-
|
|
2951
|
+
"# Known-Good Release Matrix",
|
|
2947
2952
|
"",
|
|
2948
|
-
`
|
|
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.",
|
|
2949
2956
|
"",
|
|
2950
2957
|
"## Summary",
|
|
2951
2958
|
"",
|
|
@@ -2955,12 +2962,48 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2955
2962
|
`- Consumer pins: ${payload.consumerPins.matching}/${payload.consumerPins.known} matching`,
|
|
2956
2963
|
`- Consumer CI: ${payload.consumerCi.passing}/${payload.consumerCi.checked} passing`,
|
|
2957
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(
|
|
2958
3001
|
"",
|
|
2959
3002
|
"## Consumers",
|
|
2960
3003
|
"",
|
|
2961
3004
|
"| Repo | Pin | Workflow | Status | Run |",
|
|
2962
3005
|
"| --- | --- | --- | --- | --- |"
|
|
2963
|
-
|
|
3006
|
+
);
|
|
2964
3007
|
for (const consumer of payload.consumers) {
|
|
2965
3008
|
const workflow = consumer.workflow || consumer.ci?.expectedWorkflow || "";
|
|
2966
3009
|
const run = consumer.ci?.run;
|
|
@@ -2968,6 +3011,32 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2968
3011
|
const url = run?.url ? `[${run.databaseId || "run"}](${run.url})` : "";
|
|
2969
3012
|
lines.push(`| \`${consumer.name}\` | \`${consumer.version || "missing"}\` | ${escapeMarkdownTableCell(workflow)} | ${escapeMarkdownTableCell(status)} | ${url} |`);
|
|
2970
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
|
+
}
|
|
2971
3040
|
if (payload.diagnostics.length > 0) {
|
|
2972
3041
|
lines.push("", "## Diagnostics", "");
|
|
2973
3042
|
for (const diagnostic of payload.diagnostics) {
|
|
@@ -2985,6 +3054,92 @@ function renderReleaseStatusMarkdown(payload) {
|
|
|
2985
3054
|
return `${lines.join("\n")}\n`;
|
|
2986
3055
|
}
|
|
2987
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
|
+
|
|
2988
3143
|
/**
|
|
2989
3144
|
* @param {string|null|undefined} value
|
|
2990
3145
|
* @returns {string}
|
|
@@ -5531,7 +5686,7 @@ function importCandidateCounts(summary) {
|
|
|
5531
5686
|
apiRoutes: candidates.api?.routes?.length || 0,
|
|
5532
5687
|
uiScreens: candidates.ui?.screens?.length || 0,
|
|
5533
5688
|
uiRoutes: candidates.ui?.routes?.length || 0,
|
|
5534
|
-
|
|
5689
|
+
uiWidgets: candidates.ui?.widgets?.length || candidates.ui?.components?.length || 0,
|
|
5535
5690
|
workflows: candidates.workflows?.workflows?.length || 0,
|
|
5536
5691
|
verifications: candidates.verification?.verifications?.length || 0
|
|
5537
5692
|
};
|
|
@@ -277,8 +277,8 @@ function loadPackageGeneratorAdapter(manifest, runtime, options = {}) {
|
|
|
277
277
|
const diagnostics = generatorPolicyDiagnosticsForBindings(
|
|
278
278
|
loadGeneratorPolicy(rootDir),
|
|
279
279
|
[{
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
runtimeId: String(runtime?.id || "unknown"),
|
|
281
|
+
runtimeKind: String(runtime?.kind || runtime?.type || manifest.surface || "unknown"),
|
|
282
282
|
projection: String(runtime?.projection?.id || runtime?.projection || "unknown"),
|
|
283
283
|
generatorId: String(runtime?.generator?.id || manifest.id),
|
|
284
284
|
version: String(runtime?.generator?.version || manifest.version),
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
function projectionTypesFromProjections(projections) {
|
|
13
13
|
const projectionTypes = new Set();
|
|
14
14
|
for (const projection of projections) {
|
|
15
|
-
const projectionType = projection?.type || projection?.
|
|
15
|
+
const projectionType = projection?.type || projection?.type;
|
|
16
16
|
if (projectionType) {
|
|
17
17
|
projectionTypes.add(projectionType);
|
|
18
18
|
}
|
|
@@ -53,7 +53,7 @@ export function generateDomainCoverage(graph, options = {}) {
|
|
|
53
53
|
for (const projectionType of projectionTypes) {
|
|
54
54
|
const realized = projectionStatements.some(
|
|
55
55
|
(projection) =>
|
|
56
|
-
(projection.type || projection.
|
|
56
|
+
(projection.type || projection.type) === projectionType &&
|
|
57
57
|
(projection.realizes || []).some((entry) => entry.id === capabilityId)
|
|
58
58
|
);
|
|
59
59
|
matrix[capabilityId][projectionType] = realized;
|