@documentation-robotics/cli 0.1.13 → 0.2.0
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/README.md +38 -0
- package/dist/analyzers/verify-engine.js +22 -19
- package/dist/analyzers/verify-engine.js.map +2 -2
- package/dist/cli.js +101 -27
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +3 -1
- package/dist/coding-agents/agents/claude-code.js.map +2 -2
- package/dist/coding-agents/claude-code-client.js +2 -1
- package/dist/coding-agents/claude-code-client.js.map +2 -2
- package/dist/coding-agents/copilot-client.js +17 -12
- package/dist/coding-agents/copilot-client.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +122 -0
- package/dist/coding-agents/default-permissions.js.map +7 -0
- package/dist/commands/add.js +10 -6
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +18 -10
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/audit.js +6 -6
- package/dist/commands/audit.js.map +2 -2
- package/dist/commands/changeset.js +238 -145
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +45 -18
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/element.js +3 -2
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/farm.js +657 -0
- package/dist/commands/farm.js.map +7 -0
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/mcp.js +107 -0
- package/dist/commands/mcp.js.map +7 -0
- package/dist/commands/relationship.js +53 -30
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +2 -2
- package/dist/commands/update.js +19 -6
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/validate.js +41 -3
- package/dist/commands/validate.js.map +2 -2
- package/dist/commands/version.js +2 -2
- package/dist/commands/version.js.map +2 -2
- package/dist/config.js +83 -0
- package/dist/config.js.map +7 -0
- package/dist/core/annotation-store.js +138 -0
- package/dist/core/annotation-store.js.map +7 -0
- package/dist/core/farm-manifest.js +134 -0
- package/dist/core/farm-manifest.js.map +7 -0
- package/dist/core/farm-sync-engine.js +346 -0
- package/dist/core/farm-sync-engine.js.map +7 -0
- package/dist/core/farm-sync-state.js +159 -0
- package/dist/core/farm-sync-state.js.map +7 -0
- package/dist/core/manifest.js +6 -3
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +55 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model.js +177 -16
- package/dist/core/model.js.map +3 -3
- package/dist/core/virtual-projection.js +1 -0
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/generated/node-types.js +1 -1
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +45 -9
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +55 -0
- package/dist/integrations/claude_code/docs/README.md +4 -0
- package/dist/mcp/api-key-manager.js +98 -0
- package/dist/mcp/api-key-manager.js.map +7 -0
- package/dist/mcp/http-transport.js +176 -0
- package/dist/mcp/http-transport.js.map +7 -0
- package/dist/mcp/resource-registry.js +97 -0
- package/dist/mcp/resource-registry.js.map +7 -0
- package/dist/mcp/tool-registry.js +44 -0
- package/dist/mcp/tool-registry.js.map +7 -0
- package/dist/mcp/tools/annotation-create.js +39 -0
- package/dist/mcp/tools/annotation-create.js.map +7 -0
- package/dist/mcp/tools/annotation-delete.js +29 -0
- package/dist/mcp/tools/annotation-delete.js.map +7 -0
- package/dist/mcp/tools/annotation-get.js +29 -0
- package/dist/mcp/tools/annotation-get.js.map +7 -0
- package/dist/mcp/tools/annotation-list.js +24 -0
- package/dist/mcp/tools/annotation-list.js.map +7 -0
- package/dist/mcp/tools/annotation-reply.js +34 -0
- package/dist/mcp/tools/annotation-reply.js.map +7 -0
- package/dist/mcp/tools/annotation-shared.js +10 -0
- package/dist/mcp/tools/annotation-shared.js.map +7 -0
- package/dist/mcp/tools/annotation-update.js +39 -0
- package/dist/mcp/tools/annotation-update.js.map +7 -0
- package/dist/mcp/tools/changeset-list.js +33 -0
- package/dist/mcp/tools/changeset-list.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +39 -0
- package/dist/mcp/tools/changeset-show.js.map +7 -0
- package/dist/mcp/tools/chat-launch.js +50 -0
- package/dist/mcp/tools/chat-launch.js.map +7 -0
- package/dist/mcp/tools/chat-status.js +26 -0
- package/dist/mcp/tools/chat-status.js.map +7 -0
- package/dist/mcp/tools/index.js +47 -0
- package/dist/mcp/tools/index.js.map +7 -0
- package/dist/mcp/tools/model-add.js +106 -0
- package/dist/mcp/tools/model-add.js.map +7 -0
- package/dist/mcp/tools/model-delete.js +99 -0
- package/dist/mcp/tools/model-delete.js.map +7 -0
- package/dist/mcp/tools/model-export.js +72 -0
- package/dist/mcp/tools/model-export.js.map +7 -0
- package/dist/mcp/tools/model-info.js +51 -0
- package/dist/mcp/tools/model-info.js.map +7 -0
- package/dist/mcp/tools/model-list.js +53 -0
- package/dist/mcp/tools/model-list.js.map +7 -0
- package/dist/mcp/tools/model-reload.js +34 -0
- package/dist/mcp/tools/model-reload.js.map +7 -0
- package/dist/mcp/tools/model-search.js +88 -0
- package/dist/mcp/tools/model-search.js.map +7 -0
- package/dist/mcp/tools/model-show.js +41 -0
- package/dist/mcp/tools/model-show.js.map +7 -0
- package/dist/mcp/tools/model-stats.js +22 -0
- package/dist/mcp/tools/model-stats.js.map +7 -0
- package/dist/mcp/tools/model-trace.js +55 -0
- package/dist/mcp/tools/model-trace.js.map +7 -0
- package/dist/mcp/tools/model-update.js +105 -0
- package/dist/mcp/tools/model-update.js.map +7 -0
- package/dist/mcp/tools/model-validate.js +78 -0
- package/dist/mcp/tools/model-validate.js.map +7 -0
- package/dist/mcp/tools/shared.js +103 -0
- package/dist/mcp/tools/shared.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +5 -3
- package/dist/reports/model-layer-report-generator.js.map +2 -2
- package/dist/reports/model-readme-generator.js +116 -0
- package/dist/reports/model-readme-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +37 -1
- package/dist/reports/model-report-data.js.map +2 -2
- package/dist/reports/model-report-orchestrator.js +29 -6
- package/dist/reports/model-report-orchestrator.js.map +2 -2
- package/dist/reports/model-report-utils.js +9 -0
- package/dist/reports/model-report-utils.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +2 -2
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1 -1
- package/dist/schemas/bundled/apm.json +1 -1
- package/dist/schemas/bundled/application.json +12 -1
- package/dist/schemas/bundled/base.json +1 -1
- package/dist/schemas/bundled/business.json +1 -1
- package/dist/schemas/bundled/data-model.json +38 -2
- package/dist/schemas/bundled/data-store.json +1 -1
- package/dist/schemas/bundled/manifest.json +4 -4
- package/dist/schemas/bundled/motivation.json +1 -1
- package/dist/schemas/bundled/navigation.json +1 -1
- package/dist/schemas/bundled/product.json +1 -1
- package/dist/schemas/bundled/security.json +1 -1
- package/dist/schemas/bundled/technology.json +1 -1
- package/dist/schemas/bundled/testing.json +1 -1
- package/dist/schemas/bundled/ux.json +1 -1
- package/dist/server/server.js +22 -3
- package/dist/server/server.js.map +2 -2
- package/dist/telemetry/config.js +2 -40
- package/dist/telemetry/config.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/types/index.js.map +2 -2
- package/dist/utils/ansi-suppressor.js +25 -0
- package/dist/utils/ansi-suppressor.js.map +7 -0
- package/dist/utils/color-utils.js +34 -0
- package/dist/utils/color-utils.js.map +7 -0
- package/dist/utils/element-reference-shape.js +15 -1
- package/dist/utils/element-reference-shape.js.map +3 -3
- package/dist/utils/errors.js +69 -10
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/globals.js +17 -1
- package/dist/utils/globals.js.map +2 -2
- package/dist/utils/project-paths.js +24 -2
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/reference-path-parser.js +49 -0
- package/dist/utils/reference-path-parser.js.map +7 -0
- package/dist/utils/source-reference.js +5 -0
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +10 -2
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/validators/composed-reference-validator.js +262 -0
- package/dist/validators/composed-reference-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +59 -8
- package/dist/validators/reference-validator.js.map +3 -3
- package/dist/validators/relationship-schema-validator.js +30 -2
- package/dist/validators/relationship-schema-validator.js.map +3 -3
- package/package.json +6 -3
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { formatLayerName } from "../utils/layer-name-formatter.js";
|
|
2
|
+
import { formatMarkdownTable } from "../utils/markdown-table.js";
|
|
3
|
+
import { escapeMarkdown } from "../export/markdown-utils.js";
|
|
4
|
+
class ModelReadmeGenerator {
|
|
5
|
+
generatedAt;
|
|
6
|
+
constructor(generatedAt) {
|
|
7
|
+
this.generatedAt = generatedAt;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generate complete model README markdown
|
|
11
|
+
*/
|
|
12
|
+
generate(data) {
|
|
13
|
+
const lines = [];
|
|
14
|
+
return lines.push(this.generateHeader(data)), lines.push(this.generateStatistics(data)), lines.push(this.generateProjectSummary(data)), lines.push(this.generateIntroduction()), lines.push(this.generateLayerIndex(data)), lines.push(this.generateFooter(data)), lines.join("");
|
|
15
|
+
}
|
|
16
|
+
generateHeader(data) {
|
|
17
|
+
const lines = [];
|
|
18
|
+
return lines.push(`# ${escapeMarkdown(data.projectName)}
|
|
19
|
+
`), lines.push(`
|
|
20
|
+
`), lines.join("");
|
|
21
|
+
}
|
|
22
|
+
generateStatistics(data) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push(`## Model Statistics
|
|
25
|
+
`), lines.push(`
|
|
26
|
+
`);
|
|
27
|
+
const headers = ["Metric", "Value"], rows = [
|
|
28
|
+
["Total Elements", String(data.totalElements)],
|
|
29
|
+
["Total Relationships", String(data.totalRelationships)],
|
|
30
|
+
["Populated Layers", String(data.populatedLayerCount)],
|
|
31
|
+
["Last Updated", data.lastUpdated],
|
|
32
|
+
["CLI Version", data.cliVersion]
|
|
33
|
+
];
|
|
34
|
+
data.specVersion && rows.push(["Spec Version", data.specVersion]);
|
|
35
|
+
const table = formatMarkdownTable(headers, rows);
|
|
36
|
+
return lines.push(table), lines.push(`
|
|
37
|
+
`), lines.join("");
|
|
38
|
+
}
|
|
39
|
+
generateProjectSummary(data) {
|
|
40
|
+
const lines = [];
|
|
41
|
+
lines.push(`## Project Summary
|
|
42
|
+
`), lines.push(`
|
|
43
|
+
`), data.projectDescription && lines.push(`${escapeMarkdown(data.projectDescription)}
|
|
44
|
+
|
|
45
|
+
`);
|
|
46
|
+
const populatedLayers = data.layers.filter((l) => l.elementCount > 0);
|
|
47
|
+
if (populatedLayers.length > 0) {
|
|
48
|
+
lines.push(`**Populated Layers**:
|
|
49
|
+
|
|
50
|
+
`);
|
|
51
|
+
const layerNames = populatedLayers.map((l) => `- ${formatLayerName(l.layerName)}`).join(`
|
|
52
|
+
`);
|
|
53
|
+
lines.push(`${layerNames}
|
|
54
|
+
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
return lines.join("");
|
|
58
|
+
}
|
|
59
|
+
generateIntroduction() {
|
|
60
|
+
const lines = [];
|
|
61
|
+
lines.push(`## About Documentation Robotics
|
|
62
|
+
`), lines.push(`
|
|
63
|
+
`), lines.push(`Documentation Robotics is a toolkit for managing federated architecture data models across 13 interconnected layers. It provides a structured approach to documenting systems architecture by organizing elements and relationships across distinct layers that represent different concerns: from business motivation and strategy through to technology infrastructure and testing.
|
|
64
|
+
|
|
65
|
+
`), lines.push(`### The 13-Layer Architecture Model
|
|
66
|
+
|
|
67
|
+
`);
|
|
68
|
+
const layerDescriptions = [
|
|
69
|
+
"1. **Motivation** - Goals, requirements, drivers, and strategic outcomes",
|
|
70
|
+
"2. **Business** - Business processes, functions, roles, and services",
|
|
71
|
+
"3. **Product** - Product features, capabilities, personas, and milestones",
|
|
72
|
+
"4. **Security** - Authentication, authorization, threats, and controls",
|
|
73
|
+
"5. **Application** - Application components, services, and interactions",
|
|
74
|
+
"6. **Technology** - Infrastructure, platforms, and technology components",
|
|
75
|
+
"7. **API** - REST APIs, operations, and endpoints",
|
|
76
|
+
"8. **Data Model** - Data entities, relationships, and structure definitions",
|
|
77
|
+
"9. **Data Store** - Databases and persistence mechanisms",
|
|
78
|
+
"10. **UX** - User interface components and user experience elements",
|
|
79
|
+
"11. **Navigation** - Application routing and navigation flows",
|
|
80
|
+
"12. **APM** - Observability, monitoring, metrics, and tracing",
|
|
81
|
+
"13. **Testing** - Test strategies, test cases, and test coverage"
|
|
82
|
+
];
|
|
83
|
+
return lines.push(layerDescriptions.join(`
|
|
84
|
+
`)), lines.push(`
|
|
85
|
+
|
|
86
|
+
`), lines.push(`Each layer is independently managed but interconnected, allowing elements in higher layers to reference elements in lower layers, creating a comprehensive dependency graph that ensures architectural coherence and traceability across all concerns.
|
|
87
|
+
|
|
88
|
+
`), lines.join("");
|
|
89
|
+
}
|
|
90
|
+
generateLayerIndex(data) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
lines.push(`## Layer Reports
|
|
93
|
+
`), lines.push(`
|
|
94
|
+
`);
|
|
95
|
+
const headers = ["Layer", "Elements", "Report"], rows = [], sortedLayers = [...data.layers].sort((a, b) => a.layerNumber - b.layerNumber);
|
|
96
|
+
for (const layer of sortedLayers) {
|
|
97
|
+
const layerName = formatLayerName(layer.layerName), elementCount = String(layer.elementCount);
|
|
98
|
+
let report;
|
|
99
|
+
layer.elementCount > 0 ? report = `[${layer.reportFileName}](./reports/${layer.reportFileName})` : report = "\u2014", rows.push([layerName, elementCount, report]);
|
|
100
|
+
}
|
|
101
|
+
const table = formatMarkdownTable(headers, rows);
|
|
102
|
+
return lines.push(table), lines.push(`
|
|
103
|
+
`), lines.join("");
|
|
104
|
+
}
|
|
105
|
+
generateFooter(data) {
|
|
106
|
+
const lines = [];
|
|
107
|
+
return lines.push(`---
|
|
108
|
+
`), lines.push(`
|
|
109
|
+
`), lines.push(`Generated: ${this.generatedAt} | Model Version: ${data.modelVersion}
|
|
110
|
+
`), lines.join("");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
ModelReadmeGenerator
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=model-readme-generator.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/reports/model-readme-generator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Model README Generator\n *\n * Generates the main README.md for the model documentation with:\n * - Model statistics (elements, relationships, versions)\n * - Project summary\n * - Introduction to documentation robotics\n * - Per-layer report index table\n */\n\nimport type { ModelReadmeData } from './model-report-data.js';\nimport { formatLayerName } from '../utils/layer-name-formatter.js';\nimport { formatMarkdownTable } from '../utils/markdown-table.js';\nimport { escapeMarkdown } from '../export/markdown-utils.js';\n\nexport class ModelReadmeGenerator {\n constructor(private generatedAt: string) {}\n\n /**\n * Generate complete model README markdown\n */\n generate(data: ModelReadmeData): string {\n const lines: string[] = [];\n\n // Header with project name\n lines.push(this.generateHeader(data));\n\n // Statistics section\n lines.push(this.generateStatistics(data));\n\n // Project summary\n lines.push(this.generateProjectSummary(data));\n\n // Documentation robotics introduction\n lines.push(this.generateIntroduction());\n\n // Layer index table\n lines.push(this.generateLayerIndex(data));\n\n // Footer\n lines.push(this.generateFooter(data));\n\n return lines.join('');\n }\n\n private generateHeader(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push(`# ${escapeMarkdown(data.projectName)}\\n`);\n lines.push('\\n');\n return lines.join('');\n }\n\n private generateStatistics(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Model Statistics\\n');\n lines.push('\\n');\n\n const headers = ['Metric', 'Value'];\n const rows = [\n ['Total Elements', String(data.totalElements)],\n ['Total Relationships', String(data.totalRelationships)],\n ['Populated Layers', String(data.populatedLayerCount)],\n ['Last Updated', data.lastUpdated],\n ['CLI Version', data.cliVersion],\n ];\n\n // Add spec version if available\n if (data.specVersion) {\n rows.push(['Spec Version', data.specVersion]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateProjectSummary(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Project Summary\\n');\n lines.push('\\n');\n\n // Project description if available\n if (data.projectDescription) {\n lines.push(`${escapeMarkdown(data.projectDescription)}\\n\\n`);\n }\n\n // Layer population summary\n const populatedLayers = data.layers.filter(l => l.elementCount > 0);\n if (populatedLayers.length > 0) {\n lines.push('**Populated Layers**:\\n\\n');\n const layerNames = populatedLayers\n .map(l => `- ${formatLayerName(l.layerName)}`)\n .join('\\n');\n lines.push(`${layerNames}\\n\\n`);\n }\n\n return lines.join('');\n }\n\n private generateIntroduction(): string {\n const lines: string[] = [];\n lines.push('## About Documentation Robotics\\n');\n lines.push('\\n');\n lines.push(\n 'Documentation Robotics is a toolkit for managing federated architecture data models across 13 interconnected layers. ' +\n 'It provides a structured approach to documenting systems architecture by organizing elements and relationships across ' +\n 'distinct layers that represent different concerns: from business motivation and strategy through to technology infrastructure and testing.\\n\\n'\n );\n\n lines.push('### The 13-Layer Architecture Model\\n\\n');\n\n const layerDescriptions = [\n '1. **Motivation** - Goals, requirements, drivers, and strategic outcomes',\n '2. **Business** - Business processes, functions, roles, and services',\n '3. **Product** - Product features, capabilities, personas, and milestones',\n '4. **Security** - Authentication, authorization, threats, and controls',\n '5. **Application** - Application components, services, and interactions',\n '6. **Technology** - Infrastructure, platforms, and technology components',\n '7. **API** - REST APIs, operations, and endpoints',\n '8. **Data Model** - Data entities, relationships, and structure definitions',\n '9. **Data Store** - Databases and persistence mechanisms',\n '10. **UX** - User interface components and user experience elements',\n '11. **Navigation** - Application routing and navigation flows',\n '12. **APM** - Observability, monitoring, metrics, and tracing',\n '13. **Testing** - Test strategies, test cases, and test coverage',\n ];\n\n lines.push(layerDescriptions.join('\\n'));\n lines.push('\\n\\n');\n\n lines.push(\n 'Each layer is independently managed but interconnected, allowing elements in higher layers to reference elements in lower layers, ' +\n 'creating a comprehensive dependency graph that ensures architectural coherence and traceability across all concerns.\\n\\n'\n );\n\n return lines.join('');\n }\n\n private generateLayerIndex(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('## Layer Reports\\n');\n lines.push('\\n');\n\n const headers = ['Layer', 'Elements', 'Report'];\n const rows: string[][] = [];\n\n // Sort layers by layer number for consistent ordering\n const sortedLayers = [...data.layers].sort((a, b) => a.layerNumber - b.layerNumber);\n\n for (const layer of sortedLayers) {\n const layerName = formatLayerName(layer.layerName);\n const elementCount = String(layer.elementCount);\n\n let report: string;\n if (layer.elementCount > 0) {\n // Create link to layer report (in reports/ subdirectory)\n report = `[${layer.reportFileName}](./reports/${layer.reportFileName})`;\n } else {\n // No link for empty layers\n report = '\u2014';\n }\n\n rows.push([layerName, elementCount, report]);\n }\n\n const table = formatMarkdownTable(headers, rows);\n lines.push(table);\n lines.push('\\n');\n\n return lines.join('');\n }\n\n private generateFooter(data: ModelReadmeData): string {\n const lines: string[] = [];\n lines.push('---\\n');\n lines.push('\\n');\n lines.push(`Generated: ${this.generatedAt} | Model Version: ${data.modelVersion}\\n`);\n return lines.join('');\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Element } from "../core/element.js";
|
|
2
|
-
import { getLayerOrder, isValidLayerName } from "../core/layers.js";
|
|
2
|
+
import { getLayerOrder, CANONICAL_LAYER_NAMES, isValidLayerName } from "../core/layers.js";
|
|
3
|
+
import { getCliVersion } from "../utils/spec-version.js";
|
|
4
|
+
import { getLayerReportFileName } from "./model-report-utils.js";
|
|
3
5
|
class ModelReportDataCollector {
|
|
4
6
|
/**
|
|
5
7
|
* Collect report data for a specific layer
|
|
@@ -50,6 +52,40 @@ class ModelReportDataCollector {
|
|
|
50
52
|
statistics
|
|
51
53
|
};
|
|
52
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Collect aggregated model-wide data for the model README
|
|
57
|
+
*
|
|
58
|
+
* @param model - The live Model instance
|
|
59
|
+
* @returns Aggregated model-wide data including project metadata, per-layer summaries, and statistics
|
|
60
|
+
*/
|
|
61
|
+
collectModelData(model) {
|
|
62
|
+
const layers = [];
|
|
63
|
+
let totalElements = 0;
|
|
64
|
+
for (const layerName of CANONICAL_LAYER_NAMES) {
|
|
65
|
+
const elementCount = model.graph.getNodesByLayer(layerName).length;
|
|
66
|
+
totalElements += elementCount;
|
|
67
|
+
const layerNumber = getLayerOrder(layerName), reportFileName = getLayerReportFileName(layerName);
|
|
68
|
+
layers.push({
|
|
69
|
+
layerName,
|
|
70
|
+
layerNumber,
|
|
71
|
+
elementCount,
|
|
72
|
+
reportFileName
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const totalRelationships = model.relationships.getAll().length, populatedLayerCount = layers.filter((l) => l.elementCount > 0).length;
|
|
76
|
+
return {
|
|
77
|
+
projectName: model.manifest.name,
|
|
78
|
+
projectDescription: model.manifest.description,
|
|
79
|
+
modelVersion: model.manifest.version,
|
|
80
|
+
cliVersion: model.manifest.cliVersion || getCliVersion(),
|
|
81
|
+
specVersion: model.manifest.specVersion,
|
|
82
|
+
lastUpdated: model.manifest.modified,
|
|
83
|
+
totalElements,
|
|
84
|
+
totalRelationships,
|
|
85
|
+
populatedLayerCount,
|
|
86
|
+
layers
|
|
87
|
+
};
|
|
88
|
+
}
|
|
53
89
|
}
|
|
54
90
|
export {
|
|
55
91
|
ModelReportDataCollector
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/reports/model-report-data.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Model } from '../core/model.js';\nimport { Element } from '../core/element.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { getLayerOrder, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\n\n/**\n * Statistics about relationships in a layer\n */\nexport interface ModelLayerStatistics {\n elementCount: number;\n intraRelationshipCount: number;\n interRelationshipCount: number;\n inboundRelationshipCount: number; // inter-layer rels targeting this layer\n outboundRelationshipCount: number; // inter-layer rels sourced from this layer\n}\n\n/**\n * Report-friendly data structure for a single layer\n * Analogous to ReportDataModel in scripts/generate-layer-reports.ts\n */\nexport interface ModelLayerReportData {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elements: Element[]; // sorted by element.path\n intraRelationships: Relationship[]; // both source and target in same layer\n interRelationships: Relationship[]; // source or target in a different layer\n upstreamLayers: CanonicalLayerName[]; // canonical layer names referencing INTO this layer\n downstreamLayers: CanonicalLayerName[]; // canonical layer names this layer references OUT TO\n statistics: ModelLayerStatistics;\n}\n\n/**\n * Data collector for generating layer report data from a live Model instance\n * Reads from Model/Layer/Relationships and transforms into report-friendly shapes\n */\nexport class ModelReportDataCollector {\n /**\n * Collect report data for a specific layer\n *\n * @param model - The live Model instance\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns Report-friendly data structure for the layer\n */\n collectLayerData(model: Model, layerName: CanonicalLayerName): ModelLayerReportData {\n // Get the layer number for file naming\n const layerNumber = getLayerOrder(layerName);\n\n // Get all elements in this layer using GraphModel's indexed lookup for O(1) layer-scoped access\n // Convert GraphNodes to Elements for report-friendly shape\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elements: Element[] = graphNodes\n .map(\n (node) =>\n new Element({\n id: node.uuid || node.id,\n path: node.id,\n spec_node_id: node.spec_node_id || '',\n type: node.type,\n layer_id: node.layer,\n name: node.name,\n description: node.description,\n attributes: node.attributes,\n source_reference: node.source_reference,\n metadata: node.metadata,\n })\n )\n .sort((a, b) => (a.path || a.id).localeCompare(b.path || b.id));\n\n // Get all relationships and classify them\n const allRelationships = model.relationships.getAll();\n\n // Filter relationships involving this layer\n const allRelatingRelationships = allRelationships.filter(\n r => r.layer === layerName || r.targetLayer === layerName\n );\n\n // Intra-layer relationships: both source and target in same layer\n // Note: Relationships with missing targetLayer are treated as intra-layer because targetLayer is\n // typically only populated for inter-layer relationships during persistence and loading.\n const intraRelationships = allRelatingRelationships.filter(\n r => r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer)\n );\n\n // Inter-layer relationships: source or target in different layer (or other endpoint exists)\n const interRelationships = allRelatingRelationships.filter(\n r => !(r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer))\n );\n\n // Compute upstream and downstream layers from inter-layer relationships\n const upstreamLayerSet = new Set<CanonicalLayerName>();\n const downstreamLayerSet = new Set<CanonicalLayerName>();\n let inboundRelationshipCount = 0;\n let outboundRelationshipCount = 0;\n\n for (const rel of interRelationships) {\n // Upstream: layers that have relationships targeting THIS layer\n if (rel.targetLayer === layerName && rel.layer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n upstreamLayerSet.add(rel.layer);\n inboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.layer}`);\n }\n }\n // Downstream: layers that THIS layer references\n if (rel.layer === layerName && rel.targetLayer && rel.targetLayer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n downstreamLayerSet.add(rel.targetLayer);\n outboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.targetLayer}`);\n }\n }\n }\n\n // Sort upstream and downstream layers\n const upstreamLayers = Array.from(upstreamLayerSet).sort();\n const downstreamLayers = Array.from(downstreamLayerSet).sort();\n\n // Sort relationships deterministically by \"${source}-${predicate}-${target}\"\n intraRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n interRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n // Build statistics\n const statistics: ModelLayerStatistics = {\n elementCount: elements.length,\n intraRelationshipCount: intraRelationships.length,\n interRelationshipCount: interRelationships.length,\n inboundRelationshipCount,\n outboundRelationshipCount,\n };\n\n return {\n layerName,\n layerNumber,\n elements,\n intraRelationships,\n interRelationships,\n upstreamLayers,\n downstreamLayers,\n statistics,\n };\n }\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,eAAe;AAExB,SAAS,
|
|
4
|
+
"sourcesContent": ["import type { Model } from '../core/model.js';\nimport { Element } from '../core/element.js';\nimport type { Relationship } from '../core/relationships.js';\nimport { getLayerOrder, CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getCliVersion } from '../utils/spec-version.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\n\n/**\n * Statistics about relationships in a layer\n */\nexport interface ModelLayerStatistics {\n elementCount: number;\n intraRelationshipCount: number;\n interRelationshipCount: number;\n inboundRelationshipCount: number; // inter-layer rels targeting this layer\n outboundRelationshipCount: number; // inter-layer rels sourced from this layer\n}\n\n/**\n * Report-friendly data structure for a single layer\n * Analogous to ReportDataModel in scripts/generate-layer-reports.ts\n */\nexport interface ModelLayerReportData {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elements: Element[]; // sorted by element.path\n intraRelationships: Relationship[]; // both source and target in same layer\n interRelationships: Relationship[]; // source or target in a different layer\n upstreamLayers: CanonicalLayerName[]; // canonical layer names referencing INTO this layer\n downstreamLayers: CanonicalLayerName[]; // canonical layer names this layer references OUT TO\n statistics: ModelLayerStatistics;\n}\n\n/**\n * Per-layer summary for the model README\n */\nexport interface ModelReadmeLayerSummary {\n layerName: CanonicalLayerName;\n layerNumber: number;\n elementCount: number;\n reportFileName: string;\n}\n\n/**\n * Aggregated model-wide data for the model README\n */\nexport interface ModelReadmeData {\n projectName: string;\n projectDescription?: string;\n modelVersion: string;\n cliVersion: string;\n specVersion?: string;\n lastUpdated: string;\n totalElements: number;\n totalRelationships: number;\n populatedLayerCount: number;\n layers: ModelReadmeLayerSummary[];\n}\n\n/**\n * Data collector for generating layer report data from a live Model instance\n * Reads from Model/Layer/Relationships and transforms into report-friendly shapes\n */\nexport class ModelReportDataCollector {\n /**\n * Collect report data for a specific layer\n *\n * @param model - The live Model instance\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns Report-friendly data structure for the layer\n */\n collectLayerData(model: Model, layerName: CanonicalLayerName): ModelLayerReportData {\n // Get the layer number for file naming\n const layerNumber = getLayerOrder(layerName);\n\n // Get all elements in this layer using GraphModel's indexed lookup for O(1) layer-scoped access\n // Convert GraphNodes to Elements for report-friendly shape\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elements: Element[] = graphNodes\n .map(\n (node) =>\n new Element({\n id: node.uuid || node.id,\n path: node.id,\n spec_node_id: node.spec_node_id || '',\n type: node.type,\n layer_id: node.layer,\n name: node.name,\n description: node.description,\n attributes: node.attributes,\n source_reference: node.source_reference,\n metadata: node.metadata,\n })\n )\n .sort((a, b) => (a.path || a.id).localeCompare(b.path || b.id));\n\n // Get all relationships and classify them\n const allRelationships = model.relationships.getAll();\n\n // Filter relationships involving this layer\n const allRelatingRelationships = allRelationships.filter(\n r => r.layer === layerName || r.targetLayer === layerName\n );\n\n // Intra-layer relationships: both source and target in same layer\n // Note: Relationships with missing targetLayer are treated as intra-layer because targetLayer is\n // typically only populated for inter-layer relationships during persistence and loading.\n const intraRelationships = allRelatingRelationships.filter(\n r => r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer)\n );\n\n // Inter-layer relationships: source or target in different layer (or other endpoint exists)\n const interRelationships = allRelatingRelationships.filter(\n r => !(r.layer === layerName && (r.targetLayer === layerName || !r.targetLayer))\n );\n\n // Compute upstream and downstream layers from inter-layer relationships\n const upstreamLayerSet = new Set<CanonicalLayerName>();\n const downstreamLayerSet = new Set<CanonicalLayerName>();\n let inboundRelationshipCount = 0;\n let outboundRelationshipCount = 0;\n\n for (const rel of interRelationships) {\n // Upstream: layers that have relationships targeting THIS layer\n if (rel.targetLayer === layerName && rel.layer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n upstreamLayerSet.add(rel.layer);\n inboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.layer}`);\n }\n }\n // Downstream: layers that THIS layer references\n if (rel.layer === layerName && rel.targetLayer && rel.targetLayer !== layerName) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n downstreamLayerSet.add(rel.targetLayer);\n outboundRelationshipCount++;\n } else {\n console.warn(`Skipping invalid layer name in relationship: ${rel.targetLayer}`);\n }\n }\n }\n\n // Sort upstream and downstream layers\n const upstreamLayers = Array.from(upstreamLayerSet).sort();\n const downstreamLayers = Array.from(downstreamLayerSet).sort();\n\n // Sort relationships deterministically by \"${source}-${predicate}-${target}\"\n intraRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n interRelationships.sort((a, b) => {\n const aKey = `${a.source}-${a.predicate}-${a.target}`;\n const bKey = `${b.source}-${b.predicate}-${b.target}`;\n return aKey.localeCompare(bKey);\n });\n\n // Build statistics\n const statistics: ModelLayerStatistics = {\n elementCount: elements.length,\n intraRelationshipCount: intraRelationships.length,\n interRelationshipCount: interRelationships.length,\n inboundRelationshipCount,\n outboundRelationshipCount,\n };\n\n return {\n layerName,\n layerNumber,\n elements,\n intraRelationships,\n interRelationships,\n upstreamLayers,\n downstreamLayers,\n statistics,\n };\n }\n\n /**\n * Collect aggregated model-wide data for the model README\n *\n * @param model - The live Model instance\n * @returns Aggregated model-wide data including project metadata, per-layer summaries, and statistics\n */\n collectModelData(model: Model): ModelReadmeData {\n // Collect per-layer element counts\n const layers: ModelReadmeLayerSummary[] = [];\n let totalElements = 0;\n\n for (const layerName of CANONICAL_LAYER_NAMES) {\n const graphNodes = model.graph.getNodesByLayer(layerName);\n const elementCount = graphNodes.length;\n totalElements += elementCount;\n\n const layerNumber = getLayerOrder(layerName);\n const reportFileName = getLayerReportFileName(layerName);\n\n layers.push({\n layerName,\n layerNumber,\n elementCount,\n reportFileName,\n });\n }\n\n // Count total relationships\n const totalRelationships = model.relationships.getAll().length;\n\n // Count populated layers (layers with at least one element)\n const populatedLayerCount = layers.filter(l => l.elementCount > 0).length;\n\n return {\n projectName: model.manifest.name,\n projectDescription: model.manifest.description,\n modelVersion: model.manifest.version,\n cliVersion: model.manifest.cliVersion || getCliVersion(),\n specVersion: model.manifest.specVersion,\n lastUpdated: model.manifest.modified,\n totalElements,\n totalRelationships,\n populatedLayerCount,\n layers,\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ModelReportDataCollector } from "./model-report-data.js";
|
|
2
2
|
import { ModelLayerReportGenerator } from "./model-layer-report-generator.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ModelReadmeGenerator } from "./model-readme-generator.js";
|
|
4
|
+
import { CANONICAL_LAYER_NAMES, isValidLayerName } from "../core/layers.js";
|
|
4
5
|
import { getErrorMessage } from "../utils/errors.js";
|
|
6
|
+
import { getLayerReportFileName } from "./model-report-utils.js";
|
|
5
7
|
import * as fs from "fs/promises";
|
|
6
8
|
import * as path from "path";
|
|
7
9
|
class ModelReportOrchestrator {
|
|
@@ -9,19 +11,23 @@ class ModelReportOrchestrator {
|
|
|
9
11
|
rootPath;
|
|
10
12
|
collector;
|
|
11
13
|
generator;
|
|
14
|
+
readmeGenerator;
|
|
12
15
|
constructor(model, rootPath) {
|
|
13
16
|
this.model = model, this.rootPath = rootPath, this.collector = new ModelReportDataCollector();
|
|
14
17
|
const modelVersion = model.manifest.version || "unknown", generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15
|
-
this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);
|
|
18
|
+
this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt), this.readmeGenerator = new ModelReadmeGenerator(generatedAt);
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
18
21
|
* Regenerate reports for a specific set of affected layers.
|
|
19
22
|
* On first invocation or when reports don't exist, regenerates all 13 reports.
|
|
20
23
|
* Invalid layer names in the affected set are skipped with a warning.
|
|
24
|
+
* Also regenerates the README since it contains model-wide stats that may have changed.
|
|
25
|
+
* README is generated first so it exists even if a layer report throws.
|
|
21
26
|
*/
|
|
22
27
|
async regenerate(affectedLayers) {
|
|
23
28
|
if (!await this.isInitialized())
|
|
24
29
|
return this.regenerateAll();
|
|
30
|
+
await this.generateReadme();
|
|
25
31
|
for (const layerName of affectedLayers) {
|
|
26
32
|
if (!isValidLayerName(layerName)) {
|
|
27
33
|
console.warn(`Skipping invalid layer name: ${layerName}`);
|
|
@@ -31,12 +37,13 @@ class ModelReportOrchestrator {
|
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
/**
|
|
34
|
-
* Regenerate all 13 layer reports.
|
|
40
|
+
* Regenerate all 13 layer reports and the README.
|
|
35
41
|
* Lets mkdir errors propagate so callers can handle them with proper telemetry.
|
|
42
|
+
* README is generated first so it exists even if a layer report throws.
|
|
36
43
|
*/
|
|
37
44
|
async regenerateAll() {
|
|
38
45
|
const reportDir = this.getReportDir();
|
|
39
|
-
await fs.mkdir(reportDir, { recursive: !0 });
|
|
46
|
+
await fs.mkdir(reportDir, { recursive: !0 }), await this.generateReadme();
|
|
40
47
|
for (const layerName of CANONICAL_LAYER_NAMES)
|
|
41
48
|
await this.generateLayerReport(layerName);
|
|
42
49
|
}
|
|
@@ -78,7 +85,7 @@ class ModelReportOrchestrator {
|
|
|
78
85
|
* Get the full file path for a layer report.
|
|
79
86
|
*/
|
|
80
87
|
getReportFilePath(layerName) {
|
|
81
|
-
const
|
|
88
|
+
const filename = getLayerReportFileName(layerName);
|
|
82
89
|
return path.join(this.getReportDir(), filename);
|
|
83
90
|
}
|
|
84
91
|
/**
|
|
@@ -91,11 +98,27 @@ class ModelReportOrchestrator {
|
|
|
91
98
|
try {
|
|
92
99
|
data = this.collector.collectLayerData(this.model, layerName), markdown = this.generator.generate(data);
|
|
93
100
|
} catch (error) {
|
|
94
|
-
throw new Error(`Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}
|
|
101
|
+
throw new Error(`Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
|
|
95
102
|
}
|
|
96
103
|
const filePath = this.getReportFilePath(layerName);
|
|
97
104
|
await fs.writeFile(filePath, markdown, "utf-8");
|
|
98
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Generate and write the model README.
|
|
108
|
+
* Separates concerns to distinguish programming bugs from I/O errors.
|
|
109
|
+
* Lets generation and write errors propagate so callers can handle them with proper telemetry.
|
|
110
|
+
* README is written to documentation-robotics/README.md (outside reports/ folder).
|
|
111
|
+
*/
|
|
112
|
+
async generateReadme() {
|
|
113
|
+
let data, markdown;
|
|
114
|
+
try {
|
|
115
|
+
data = this.collector.collectModelData(this.model), markdown = this.readmeGenerator.generate(data);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw new Error(`Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`, { cause: error });
|
|
118
|
+
}
|
|
119
|
+
const filePath = path.join(this.rootPath, "documentation-robotics", "README.md");
|
|
120
|
+
await fs.writeFile(filePath, markdown, "utf-8");
|
|
121
|
+
}
|
|
99
122
|
}
|
|
100
123
|
export {
|
|
101
124
|
ModelReportOrchestrator
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/reports/model-report-orchestrator.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName,
|
|
5
|
-
"mappings": "AAQA,SAAS,gCAAgC;AACzC,SAAS,iCAAiC;AAC1C,SAAS,uBAAgD,
|
|
4
|
+
"sourcesContent": ["/**\n * Model Report Orchestrator\n *\n * Coordinates report generation across all 13 architecture layers.\n * Determines which reports need regeneration and manages the report directory.\n */\n\nimport type { Model } from '../core/model.js';\nimport { ModelReportDataCollector } from './model-report-data.js';\nimport { ModelLayerReportGenerator } from './model-layer-report-generator.js';\nimport { ModelReadmeGenerator } from './model-readme-generator.js';\nimport { CANONICAL_LAYER_NAMES, type CanonicalLayerName, isValidLayerName } from '../core/layers.js';\nimport { getErrorMessage } from '../utils/errors.js';\nimport { getLayerReportFileName } from './model-report-utils.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\nexport class ModelReportOrchestrator {\n private collector: ModelReportDataCollector;\n private generator: ModelLayerReportGenerator;\n private readmeGenerator: ModelReadmeGenerator;\n\n constructor(private model: Model, private rootPath: string) {\n this.collector = new ModelReportDataCollector();\n const modelVersion = model.manifest.version || 'unknown';\n const generatedAt = new Date().toISOString();\n this.generator = new ModelLayerReportGenerator(modelVersion, generatedAt);\n this.readmeGenerator = new ModelReadmeGenerator(generatedAt);\n }\n\n /**\n * Regenerate reports for a specific set of affected layers.\n * On first invocation or when reports don't exist, regenerates all 13 reports.\n * Invalid layer names in the affected set are skipped with a warning.\n * Also regenerates the README since it contains model-wide stats that may have changed.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerate(affectedLayers: Set<string>): Promise<void> {\n if (!(await this.isInitialized())) {\n return this.regenerateAll();\n }\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n for (const layerName of affectedLayers) {\n // Type predicate narrows layerName to CanonicalLayerName automatically\n if (!isValidLayerName(layerName)) {\n console.warn(`Skipping invalid layer name: ${layerName}`);\n continue;\n }\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Regenerate all 13 layer reports and the README.\n * Lets mkdir errors propagate so callers can handle them with proper telemetry.\n * README is generated first so it exists even if a layer report throws.\n */\n async regenerateAll(): Promise<void> {\n // Ensure report directory exists\n const reportDir = this.getReportDir();\n await fs.mkdir(reportDir, { recursive: true });\n\n // Generate README first so it exists even if a layer report throws\n await this.generateReadme();\n\n // Generate all 13 layer reports\n for (const layerName of CANONICAL_LAYER_NAMES) {\n await this.generateLayerReport(layerName);\n }\n }\n\n /**\n * Compute the set of layers affected by a change to a primary layer.\n * Returns the primary layer plus any layers that have cross-layer relationships with it.\n * Throws if the primary layer is not a recognized canonical layer name.\n */\n computeAffectedLayers(primaryLayer: CanonicalLayerName): Set<CanonicalLayerName> {\n // Defense-in-depth: validate despite type signature (guards against as casts from callers)\n if (!isValidLayerName(primaryLayer)) {\n throw new Error(\n `Invalid primary layer name: '${primaryLayer}' is not a recognized canonical layer. ` +\n `Valid layers are: ${CANONICAL_LAYER_NAMES.join(', ')}`\n );\n }\n\n const affected = new Set<CanonicalLayerName>([primaryLayer]);\n\n for (const rel of this.model.relationships.getAll()) {\n // Outbound: this layer references another layer\n if (rel.layer === primaryLayer && rel.targetLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.targetLayer)) {\n affected.add(rel.targetLayer);\n }\n }\n // Inbound: another layer references this layer\n if (rel.targetLayer === primaryLayer) {\n // Validate layer name before casting (defensive: guards against user-editable YAML with invalid layer names)\n if (isValidLayerName(rel.layer)) {\n affected.add(rel.layer);\n }\n }\n }\n\n return affected;\n }\n\n /**\n * Check if all 13 report files exist and are initialized.\n * Returns false only if files don't exist (ENOENT); propagates other errors like EACCES, EMFILE, EIO.\n */\n private async isInitialized(): Promise<boolean> {\n for (const layerName of CANONICAL_LAYER_NAMES) {\n try {\n await fs.access(this.getReportFilePath(layerName));\n } catch (error: unknown) {\n const err = error as NodeJS.ErrnoException;\n // Only ENOENT (file not found) should return false; other errors propagate\n if (err.code === 'ENOENT') {\n return false;\n }\n // Re-throw permission errors, I/O errors, etc.\n throw error;\n }\n }\n return true;\n }\n\n /**\n * Get the reports directory path.\n */\n private getReportDir(): string {\n return path.join(this.rootPath, 'documentation-robotics', 'reports');\n }\n\n /**\n * Get the full file path for a layer report.\n */\n private getReportFilePath(layerName: CanonicalLayerName): string {\n const filename = getLayerReportFileName(layerName);\n return path.join(this.getReportDir(), filename);\n }\n\n /**\n * Generate and write a single layer report.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n */\n private async generateLayerReport(layerName: CanonicalLayerName): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect layer data (may throw if programming bug or model is corrupted)\n data = this.collector.collectLayerData(this.model, layerName);\n\n // Generate markdown report (may throw if programming bug)\n markdown = this.generator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate report content for layer: ${layerName} (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = this.getReportFilePath(layerName);\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n\n /**\n * Generate and write the model README.\n * Separates concerns to distinguish programming bugs from I/O errors.\n * Lets generation and write errors propagate so callers can handle them with proper telemetry.\n * README is written to documentation-robotics/README.md (outside reports/ folder).\n */\n private async generateReadme(): Promise<void> {\n let data;\n let markdown;\n\n try {\n // Collect model-wide data (may throw if programming bug or model is corrupted)\n data = this.collector.collectModelData(this.model);\n\n // Generate markdown README (may throw if programming bug)\n markdown = this.readmeGenerator.generate(data);\n } catch (error) {\n throw new Error(\n `Failed to generate README content (programming error or corrupted model) - ${getErrorMessage(error)}`,\n { cause: error }\n );\n }\n\n // Write to file (may throw for I/O errors like ENOSPC, EACCES, EIO)\n // Let write errors propagate so callers can handle them with proper telemetry\n const filePath = path.join(this.rootPath, 'documentation-robotics', 'README.md');\n await fs.writeFile(filePath, markdown, 'utf-8');\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getLayerOrder } from "../core/layers.js";
|
|
2
|
+
function getLayerReportFileName(layerName) {
|
|
3
|
+
const layerNumber = getLayerOrder(layerName);
|
|
4
|
+
return `${String(layerNumber).padStart(2, "0")}-${layerName}-layer-report.md`;
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
getLayerReportFileName
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=model-report-utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/reports/model-report-utils.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CanonicalLayerName } from '../core/layers.js';\nimport { getLayerOrder } from '../core/layers.js';\n\n/**\n * Generate the report filename for a layer using the shared pattern: {NN}-{layerName}-layer-report.md\n *\n * @param layerName - The canonical layer name (e.g., 'api', 'data-model')\n * @returns The filename string with zero-padded layer number\n */\nexport function getLayerReportFileName(layerName: CanonicalLayerName): string {\n const layerNumber = getLayerOrder(layerName);\n const paddedNumber = String(layerNumber).padStart(2, '0');\n return `${paddedNumber}-${layerName}-layer-report.md`;\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qBAAqB;AAQxB,SAAU,uBAAuB,WAA6B;AAClE,QAAM,cAAc,cAAc,SAAS;AAE3C,SAAO,GADc,OAAO,WAAW,EAAE,SAAS,GAAG,GAAG,CAClC,IAAI,SAAS;AACrC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"specVersion": "0.
|
|
2
|
+
"specVersion": "0.10.0",
|
|
3
3
|
"layer": {
|
|
4
4
|
"id": "application",
|
|
5
5
|
"number": 5,
|
|
@@ -1073,6 +1073,17 @@
|
|
|
1073
1073
|
"strength": "medium",
|
|
1074
1074
|
"required": false
|
|
1075
1075
|
},
|
|
1076
|
+
"application.applicationservice.accesses.application.dataobject": {
|
|
1077
|
+
"id": "application.applicationservice.accesses.application.dataobject",
|
|
1078
|
+
"source_spec_node_id": "application.applicationservice",
|
|
1079
|
+
"source_layer": "application",
|
|
1080
|
+
"destination_spec_node_id": "application.dataobject",
|
|
1081
|
+
"destination_layer": "application",
|
|
1082
|
+
"predicate": "accesses",
|
|
1083
|
+
"cardinality": "many-to-many",
|
|
1084
|
+
"strength": "medium",
|
|
1085
|
+
"required": false
|
|
1086
|
+
},
|
|
1076
1087
|
"application.applicationservice.constrained-by.security.accesscondition": {
|
|
1077
1088
|
"id": "application.applicationservice.constrained-by.security.accesscondition",
|
|
1078
1089
|
"source_spec_node_id": "application.applicationservice",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"specVersion": "0.
|
|
2
|
+
"specVersion": "0.10.0",
|
|
3
3
|
"layer": {
|
|
4
4
|
"id": "data-model",
|
|
5
5
|
"number": 8,
|
|
@@ -202,7 +202,10 @@
|
|
|
202
202
|
"description": "Schema applied to all property names of the object instance for name validation"
|
|
203
203
|
},
|
|
204
204
|
"required": {
|
|
205
|
-
"type": "
|
|
205
|
+
"type": "array",
|
|
206
|
+
"items": {
|
|
207
|
+
"type": "string"
|
|
208
|
+
}
|
|
206
209
|
},
|
|
207
210
|
"additionalProperties": {
|
|
208
211
|
"type": "string"
|
|
@@ -777,6 +780,17 @@
|
|
|
777
780
|
"strength": "medium",
|
|
778
781
|
"required": false
|
|
779
782
|
},
|
|
783
|
+
"data-model.objectschema.maps-to.data-store.collection": {
|
|
784
|
+
"id": "data-model.objectschema.maps-to.data-store.collection",
|
|
785
|
+
"source_spec_node_id": "data-model.objectschema",
|
|
786
|
+
"source_layer": "data-model",
|
|
787
|
+
"destination_spec_node_id": "data-store.collection",
|
|
788
|
+
"destination_layer": "data-store",
|
|
789
|
+
"predicate": "maps-to",
|
|
790
|
+
"cardinality": "many-to-many",
|
|
791
|
+
"strength": "medium",
|
|
792
|
+
"required": false
|
|
793
|
+
},
|
|
780
794
|
"data-model.objectschema.maps-to.api.requestbody": {
|
|
781
795
|
"id": "data-model.objectschema.maps-to.api.requestbody",
|
|
782
796
|
"source_spec_node_id": "data-model.objectschema",
|
|
@@ -887,6 +901,28 @@
|
|
|
887
901
|
"strength": "medium",
|
|
888
902
|
"required": false
|
|
889
903
|
},
|
|
904
|
+
"data-model.objectschema.serves.application.applicationcomponent": {
|
|
905
|
+
"id": "data-model.objectschema.serves.application.applicationcomponent",
|
|
906
|
+
"source_spec_node_id": "data-model.objectschema",
|
|
907
|
+
"source_layer": "data-model",
|
|
908
|
+
"destination_spec_node_id": "application.applicationcomponent",
|
|
909
|
+
"destination_layer": "application",
|
|
910
|
+
"predicate": "serves",
|
|
911
|
+
"cardinality": "many-to-many",
|
|
912
|
+
"strength": "medium",
|
|
913
|
+
"required": false
|
|
914
|
+
},
|
|
915
|
+
"data-model.objectschema.serves.application.applicationfunction": {
|
|
916
|
+
"id": "data-model.objectschema.serves.application.applicationfunction",
|
|
917
|
+
"source_spec_node_id": "data-model.objectschema",
|
|
918
|
+
"source_layer": "data-model",
|
|
919
|
+
"destination_spec_node_id": "application.applicationfunction",
|
|
920
|
+
"destination_layer": "application",
|
|
921
|
+
"predicate": "serves",
|
|
922
|
+
"cardinality": "many-to-many",
|
|
923
|
+
"strength": "medium",
|
|
924
|
+
"required": false
|
|
925
|
+
},
|
|
890
926
|
"data-model.reference.maps-to.application.applicationservice": {
|
|
891
927
|
"id": "data-model.reference.maps-to.application.applicationservice",
|
|
892
928
|
"source_spec_node_id": "data-model.reference",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"specVersion": "0.
|
|
2
|
+
"specVersion": "0.10.0",
|
|
3
3
|
"layers": [
|
|
4
4
|
{
|
|
5
5
|
"id": "motivation",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"number": 5,
|
|
35
35
|
"name": "Application Layer",
|
|
36
36
|
"nodeTypeCount": 9,
|
|
37
|
-
"relationshipCount":
|
|
37
|
+
"relationshipCount": 80
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"id": "technology",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"number": 8,
|
|
56
56
|
"name": "Data Model Layer",
|
|
57
57
|
"nodeTypeCount": 9,
|
|
58
|
-
"relationshipCount":
|
|
58
|
+
"relationshipCount": 74
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
"id": "data-store",
|
|
@@ -95,6 +95,6 @@
|
|
|
95
95
|
],
|
|
96
96
|
"totals": {
|
|
97
97
|
"nodeTypes": 191,
|
|
98
|
-
"relationships":
|
|
98
|
+
"relationships": 1589
|
|
99
99
|
}
|
|
100
100
|
}
|