@donartcha/openlag 0.1.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/LICENSE +373 -0
- package/README.md +82 -0
- package/bin/openlag.js +2 -0
- package/dist/assets/arc-4YUHkXo3.js +1 -0
- package/dist/assets/architectureDiagram-3BPJPVTR-WeGmL7HM.js +36 -0
- package/dist/assets/blockDiagram-GPEHLZMM-CtV7ubAx.js +132 -0
- package/dist/assets/c4Diagram-AAUBKEIU-DqYDW5c3.js +10 -0
- package/dist/assets/channel-Tsel3-MK.js +1 -0
- package/dist/assets/chunk-2J33WTMH-BE8P9tjh.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Bi7oLGF5.js +1 -0
- package/dist/assets/chunk-55IACEB6-D9Xhxp_r.js +1 -0
- package/dist/assets/chunk-727SXJPM-Dz8jKE60.js +206 -0
- package/dist/assets/chunk-AQP2D5EJ-BzmM0IeH.js +231 -0
- package/dist/assets/chunk-FMBD7UC4-Cvl5dpcx.js +15 -0
- package/dist/assets/chunk-ND2GUHAM-Dz2efqnq.js +1 -0
- package/dist/assets/chunk-QZHKN3VN-CwblgSnQ.js +1 -0
- package/dist/assets/classDiagram-4FO5ZUOK-Bgm-_cW8.js +1 -0
- package/dist/assets/classDiagram-v2-Q7XG4LA2-Bgm-_cW8.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-h_A3nZUx.js +1 -0
- package/dist/assets/cytoscape.esm-D_LviqZs.js +331 -0
- package/dist/assets/dagre-BM42HDAG-CN_B2Doz.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-2AECGRRQ-C9TAFwjG.js +43 -0
- package/dist/assets/diagram-5GNKFQAL-BThljQLo.js +10 -0
- package/dist/assets/diagram-KO2AKTUF-bRPq25Se.js +3 -0
- package/dist/assets/diagram-LMA3HP47-BubLCIus.js +24 -0
- package/dist/assets/diagram-OG6HWLK6-CJpfhIsS.js +24 -0
- package/dist/assets/erDiagram-TEJ5UH35-6Xkza9wL.js +85 -0
- package/dist/assets/flowDiagram-I6XJVG4X-Bq_to3hX.js +162 -0
- package/dist/assets/ganttDiagram-6RSMTGT7-C3CmvYl7.js +292 -0
- package/dist/assets/gitGraphDiagram-PVQCEYII-C93LTfrl.js +106 -0
- package/dist/assets/graph-CAnANduQ.js +1 -0
- package/dist/assets/index-0RMQQ34p.css +1 -0
- package/dist/assets/index-ByxguSZe.js +729 -0
- package/dist/assets/infoDiagram-5YYISTIA-CMfuwygl.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-YF4QCWOH-CbJ5ojDF.js +70 -0
- package/dist/assets/journeyDiagram-JHISSGLW-C_Xz8YyT.js +139 -0
- package/dist/assets/kanban-definition-UN3LZRKU-GVv_iRMq.js +89 -0
- package/dist/assets/katex-DkKDou_j.js +257 -0
- package/dist/assets/layout-DGIYPm2g.js +1 -0
- package/dist/assets/linear-BNEtUH2J.js +1 -0
- package/dist/assets/mindmap-definition-RKZ34NQL-DIsL0XSF.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pieDiagram-4H26LBE5-CSCTSOjk.js +30 -0
- package/dist/assets/quadrantDiagram-W4KKPZXB-CQQ9OaFY.js +7 -0
- package/dist/assets/requirementDiagram-4Y6WPE33-Cjn3la_S.js +84 -0
- package/dist/assets/sankeyDiagram-5OEKKPKP-DoVspvVc.js +40 -0
- package/dist/assets/sequenceDiagram-3UESZ5HK-UsoGmL4w.js +162 -0
- package/dist/assets/stateDiagram-AJRCARHV-DLmf7Dc8.js +1 -0
- package/dist/assets/stateDiagram-v2-BHNVJYJU-jkiDZ_3u.js +1 -0
- package/dist/assets/timeline-definition-PNZ67QCA-HfyRxZ8p.js +120 -0
- package/dist/assets/vennDiagram-CIIHVFJN-B6pM3L33.js +34 -0
- package/dist/assets/wardley-L42UT6IY-B-LdKtrI.js +173 -0
- package/dist/assets/wardleyDiagram-YWT4CUSO-BD45zhOu.js +78 -0
- package/dist/assets/xychartDiagram-2RQKCTM6-zsDMbUiS.js +7 -0
- package/dist/cli/openlag.js +1793 -0
- package/dist/index.html +14 -0
- package/index.html +13 -0
- package/package.json +84 -0
- package/scripts/cli/build.ts +34 -0
- package/scripts/cli/dev.ts +35 -0
- package/scripts/cli/generate.ts +92 -0
- package/scripts/cli/init.ts +427 -0
- package/scripts/cli/lint.ts +29 -0
- package/scripts/cli/openlag.ts +110 -0
- package/scripts/cli/vite-bin.ts +8 -0
- package/scripts/core/parser/diagnostic.ts +34 -0
- package/scripts/core/parser/normalizer.ts +27 -0
- package/scripts/core/parser/scanner.ts +30 -0
- package/scripts/core/parser/schemas.ts +23 -0
- package/scripts/core/parser/types.ts +30 -0
- package/scripts/core/parser.ts +127 -0
- package/scripts/generate-relations.ts +53 -0
- package/scripts/lint/lint-engine.ts +85 -0
- package/scripts/lint/lint-profiles.ts +49 -0
- package/scripts/lint/lint-rules.ts +174 -0
- package/scripts/lint/lint-types.ts +43 -0
- package/src/App.tsx +164 -0
- package/src/components/DocumentationView.tsx +905 -0
- package/src/components/GraphView.tsx +529 -0
- package/src/components/GuideView.tsx +535 -0
- package/src/components/ImpactView.tsx +365 -0
- package/src/components/MarkdownRenderer.tsx +120 -0
- package/src/components/OrphansView.tsx +360 -0
- package/src/components/SettingsView.tsx +146 -0
- package/src/core/generated/relation-definitions.ts +622 -0
- package/src/core/graph/GraphQueryLayer.ts +194 -0
- package/src/core/registry/ArtifactRegistry.ts +19 -0
- package/src/core/registry/RelationRegistry.ts +27 -0
- package/src/core/semantic/artifact-layers.ts +43 -0
- package/src/core/semantic/ownership-rules.ts +13 -0
- package/src/core/semantic/types.ts +11 -0
- package/src/index.css +121 -0
- package/src/lib/reportUtils.ts +59 -0
- package/src/main.tsx +10 -0
- package/src/store.ts +146 -0
- package/src/types.ts +77 -0
- package/vite.config.ts +31 -0
|
@@ -0,0 +1,1793 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// scripts/cli/openlag.ts
|
|
4
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
5
|
+
import path8 from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
|
+
import chalk6 from "chalk";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
|
|
10
|
+
// scripts/cli/build.ts
|
|
11
|
+
import { execFileSync } from "child_process";
|
|
12
|
+
import path4 from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import chalk2 from "chalk";
|
|
15
|
+
|
|
16
|
+
// scripts/cli/generate.ts
|
|
17
|
+
import fs2 from "fs";
|
|
18
|
+
import path2 from "path";
|
|
19
|
+
|
|
20
|
+
// scripts/core/parser/scanner.ts
|
|
21
|
+
import fs from "fs";
|
|
22
|
+
import path from "path";
|
|
23
|
+
function scanDocs(docsDir) {
|
|
24
|
+
const documents = [];
|
|
25
|
+
const traverse = (dir) => {
|
|
26
|
+
if (!fs.existsSync(dir)) return;
|
|
27
|
+
const items = fs.readdirSync(dir);
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
const fullPath = path.join(dir, item);
|
|
30
|
+
const stat = fs.statSync(fullPath);
|
|
31
|
+
if (stat.isDirectory()) {
|
|
32
|
+
traverse(fullPath);
|
|
33
|
+
} else if (item.endsWith(".md")) {
|
|
34
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
35
|
+
documents.push({
|
|
36
|
+
file: fullPath,
|
|
37
|
+
content
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
traverse(docsDir);
|
|
43
|
+
return documents;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// scripts/core/parser/schemas.ts
|
|
47
|
+
import { z } from "zod";
|
|
48
|
+
var ArtifactSchema = z.object({
|
|
49
|
+
id: z.string(),
|
|
50
|
+
type: z.string(),
|
|
51
|
+
subType: z.string().optional(),
|
|
52
|
+
title: z.string(),
|
|
53
|
+
version: z.string(),
|
|
54
|
+
description: z.string(),
|
|
55
|
+
systemVersionId: z.string().optional(),
|
|
56
|
+
status: z.enum(["draft", "in_progress", "ready", "closed", "deprecated"]).optional(),
|
|
57
|
+
layer: z.string().optional(),
|
|
58
|
+
ownership: z.object({
|
|
59
|
+
owner: z.string().optional(),
|
|
60
|
+
team: z.string().optional(),
|
|
61
|
+
domain: z.string().optional(),
|
|
62
|
+
maintainers: z.array(z.string()).optional(),
|
|
63
|
+
reviewers: z.array(z.string()).optional(),
|
|
64
|
+
steward: z.string().optional()
|
|
65
|
+
}).optional(),
|
|
66
|
+
file: z.string(),
|
|
67
|
+
schemaVersion: z.string().optional()
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// src/core/semantic/artifact-layers.ts
|
|
71
|
+
var LayerInferenceRules = {
|
|
72
|
+
PROJECT: "BUSINESS",
|
|
73
|
+
EPIC: "BUSINESS",
|
|
74
|
+
FEATURE: "BUSINESS",
|
|
75
|
+
REQUIREMENT: "BUSINESS",
|
|
76
|
+
BUSINESS_RULE: "BUSINESS",
|
|
77
|
+
USE_CASE: "BUSINESS",
|
|
78
|
+
DESIGN: "ARCHITECTURE",
|
|
79
|
+
DECISION: "ARCHITECTURE",
|
|
80
|
+
COMPONENT: "ARCHITECTURE",
|
|
81
|
+
API: "ARCHITECTURE",
|
|
82
|
+
CODE_ENTITY: "IMPLEMENTATION",
|
|
83
|
+
DATABASE_ENTITY: "IMPLEMENTATION",
|
|
84
|
+
TEST_CASE: "IMPLEMENTATION",
|
|
85
|
+
TEST: "IMPLEMENTATION",
|
|
86
|
+
CHANGE: "IMPLEMENTATION",
|
|
87
|
+
BUG: "IMPLEMENTATION",
|
|
88
|
+
RISK: "IMPLEMENTATION",
|
|
89
|
+
INCIDENT: "OPERATIONS",
|
|
90
|
+
INFRASTRUCTURE: "OPERATIONS",
|
|
91
|
+
DEPLOYMENT: "OPERATIONS",
|
|
92
|
+
MONITORING: "OPERATIONS",
|
|
93
|
+
MAINTENANCE: "OPERATIONS",
|
|
94
|
+
SYSTEM_VERSION: "OPERATIONS",
|
|
95
|
+
GLOSSARY_TERM: "DOCUMENTATION",
|
|
96
|
+
DOCUMENTATION: "DOCUMENTATION",
|
|
97
|
+
VERSION: "DOCUMENTATION",
|
|
98
|
+
LIBRARY: "IMPLEMENTATION",
|
|
99
|
+
ENVIRONMENT: "OPERATIONS",
|
|
100
|
+
CHECK: "OPERATIONS",
|
|
101
|
+
PROCESS: "OPERATIONS",
|
|
102
|
+
PIPELINE: "OPERATIONS"
|
|
103
|
+
};
|
|
104
|
+
function inferLayer(artifactType) {
|
|
105
|
+
return LayerInferenceRules[artifactType];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// scripts/core/parser/normalizer.ts
|
|
109
|
+
function normalizeArtifact(parsed, fullPath, body) {
|
|
110
|
+
const p = { ...parsed };
|
|
111
|
+
if (!p.schemaVersion) {
|
|
112
|
+
p.schemaVersion = "1.0.0";
|
|
113
|
+
}
|
|
114
|
+
const typeValue = p.type || p.Type;
|
|
115
|
+
return {
|
|
116
|
+
id: String(p.id || p.ID || ""),
|
|
117
|
+
type: typeValue,
|
|
118
|
+
subType: p.subType || p.subtype || p.SubType,
|
|
119
|
+
title: String(p.title || p.Title || (p.id || p.ID || "")),
|
|
120
|
+
version: String(p.version || p.Version || "v-1"),
|
|
121
|
+
description: body,
|
|
122
|
+
systemVersionId: p.systemVersionId || p.systemversionid,
|
|
123
|
+
component: p.component,
|
|
124
|
+
releaseDate: p.releaseDate || p.timestamp,
|
|
125
|
+
status: p.status,
|
|
126
|
+
layer: inferLayer(typeValue),
|
|
127
|
+
ownership: p.ownership || p.owner ? { owner: p.owner, ...p.ownership } : void 0,
|
|
128
|
+
file: fullPath,
|
|
129
|
+
schemaVersion: p.schemaVersion
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// scripts/core/parser/diagnostic.ts
|
|
134
|
+
var DiagnosticEngine = class {
|
|
135
|
+
constructor() {
|
|
136
|
+
this.diagnostics = [];
|
|
137
|
+
}
|
|
138
|
+
add(file, message, severity) {
|
|
139
|
+
this.diagnostics.push({ file, message, severity });
|
|
140
|
+
}
|
|
141
|
+
getDiagnostics() {
|
|
142
|
+
return this.diagnostics;
|
|
143
|
+
}
|
|
144
|
+
getErrors() {
|
|
145
|
+
return this.diagnostics.map((d) => ({ file: d.file, message: `[${d.severity}] ${d.message}` }));
|
|
146
|
+
}
|
|
147
|
+
hasCritical() {
|
|
148
|
+
return this.diagnostics.some((d) => d.severity === "CRITICAL" /* CRITICAL */);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/core/generated/relation-definitions.ts
|
|
153
|
+
var GENERATED_RELATIONS = [
|
|
154
|
+
{
|
|
155
|
+
"type": "BLOCKS",
|
|
156
|
+
"description": "Descripci\xF3n de impedimentos directos.",
|
|
157
|
+
"category": "DEPENDENCY",
|
|
158
|
+
"allowedFrom": [
|
|
159
|
+
"BUG",
|
|
160
|
+
"RISK",
|
|
161
|
+
"INCIDENT",
|
|
162
|
+
"CHANGE",
|
|
163
|
+
"DECISION",
|
|
164
|
+
"REQUIREMENT",
|
|
165
|
+
"FEATURE",
|
|
166
|
+
"EPIC"
|
|
167
|
+
],
|
|
168
|
+
"allowedTo": [
|
|
169
|
+
"PROJECT",
|
|
170
|
+
"EPIC",
|
|
171
|
+
"FEATURE",
|
|
172
|
+
"REQUIREMENT",
|
|
173
|
+
"BUG",
|
|
174
|
+
"CHANGE",
|
|
175
|
+
"DEPLOYMENT",
|
|
176
|
+
"DEPLOYMENT"
|
|
177
|
+
],
|
|
178
|
+
"multiplicity": {
|
|
179
|
+
"from": "many",
|
|
180
|
+
"to": "many"
|
|
181
|
+
},
|
|
182
|
+
"validation": {
|
|
183
|
+
"severity": "error"
|
|
184
|
+
},
|
|
185
|
+
"strength": "STRONG"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"type": "BREAKS",
|
|
189
|
+
"description": "Aver\xEDas o rupturas confirmadas.",
|
|
190
|
+
"category": "OPERATIONAL",
|
|
191
|
+
"allowedFrom": [
|
|
192
|
+
"CHANGE",
|
|
193
|
+
"CODE_ENTITY",
|
|
194
|
+
"COMPONENT",
|
|
195
|
+
"SYSTEM_VERSION"
|
|
196
|
+
],
|
|
197
|
+
"allowedTo": [
|
|
198
|
+
"TEST_CASE",
|
|
199
|
+
"TEST",
|
|
200
|
+
"API",
|
|
201
|
+
"COMPONENT",
|
|
202
|
+
"REQUIREMENT",
|
|
203
|
+
"FEATURE"
|
|
204
|
+
],
|
|
205
|
+
"multiplicity": {
|
|
206
|
+
"from": "many",
|
|
207
|
+
"to": "many"
|
|
208
|
+
},
|
|
209
|
+
"validation": {
|
|
210
|
+
"severity": "error"
|
|
211
|
+
},
|
|
212
|
+
"strength": "STRONG"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"type": "CALLS",
|
|
216
|
+
"description": "Trazabilidad de invocaci\xF3n a nivel c\xF3digo.",
|
|
217
|
+
"category": "STRUCTURAL",
|
|
218
|
+
"allowedFrom": [
|
|
219
|
+
"CODE_ENTITY"
|
|
220
|
+
],
|
|
221
|
+
"allowedTo": [
|
|
222
|
+
"CODE_ENTITY",
|
|
223
|
+
"API"
|
|
224
|
+
],
|
|
225
|
+
"multiplicity": {
|
|
226
|
+
"from": "many",
|
|
227
|
+
"to": "many"
|
|
228
|
+
},
|
|
229
|
+
"validation": {
|
|
230
|
+
"severity": "warn"
|
|
231
|
+
},
|
|
232
|
+
"strength": "MEDIUM"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"type": "DEFINES",
|
|
236
|
+
"description": "Entidades que instauran glosarios o normas.",
|
|
237
|
+
"category": "SEMANTIC",
|
|
238
|
+
"allowedFrom": [
|
|
239
|
+
"BUSINESS_RULE",
|
|
240
|
+
"DECISION",
|
|
241
|
+
"DOCUMENTATION",
|
|
242
|
+
"GLOSSARY_TERM"
|
|
243
|
+
],
|
|
244
|
+
"allowedTo": [
|
|
245
|
+
"REQUIREMENT",
|
|
246
|
+
"FEATURE",
|
|
247
|
+
"DESIGN",
|
|
248
|
+
"PROJECT",
|
|
249
|
+
"COMPONENT",
|
|
250
|
+
"API",
|
|
251
|
+
"DATABASE_ENTITY"
|
|
252
|
+
],
|
|
253
|
+
"multiplicity": {
|
|
254
|
+
"from": "many",
|
|
255
|
+
"to": "many"
|
|
256
|
+
},
|
|
257
|
+
"validation": {
|
|
258
|
+
"severity": "info"
|
|
259
|
+
},
|
|
260
|
+
"strength": "WEAK"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"type": "DEPENDS_ON",
|
|
264
|
+
"description": "Acoplamiento est\xE1tico arquitect\xF3nico o de empaquetado.",
|
|
265
|
+
"category": "STRUCTURAL",
|
|
266
|
+
"allowedFrom": [
|
|
267
|
+
"CODE_ENTITY",
|
|
268
|
+
"COMPONENT",
|
|
269
|
+
"API",
|
|
270
|
+
"DATABASE_ENTITY",
|
|
271
|
+
"LIBRARY",
|
|
272
|
+
"VERSION",
|
|
273
|
+
"SYSTEM_VERSION"
|
|
274
|
+
],
|
|
275
|
+
"allowedTo": [
|
|
276
|
+
"CODE_ENTITY",
|
|
277
|
+
"COMPONENT",
|
|
278
|
+
"API",
|
|
279
|
+
"DATABASE_ENTITY",
|
|
280
|
+
"LIBRARY",
|
|
281
|
+
"VERSION",
|
|
282
|
+
"SYSTEM_VERSION"
|
|
283
|
+
],
|
|
284
|
+
"multiplicity": {
|
|
285
|
+
"from": "many",
|
|
286
|
+
"to": "many"
|
|
287
|
+
},
|
|
288
|
+
"validation": {
|
|
289
|
+
"severity": "warn"
|
|
290
|
+
},
|
|
291
|
+
"strength": "MEDIUM"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"type": "DEPLOYS",
|
|
295
|
+
"description": "Instanciaci\xF3n de componentes o release en infraestructura.",
|
|
296
|
+
"category": "OPERATIONAL",
|
|
297
|
+
"allowedFrom": [
|
|
298
|
+
"DEPLOYMENT",
|
|
299
|
+
"PIPELINE",
|
|
300
|
+
"SYSTEM_VERSION"
|
|
301
|
+
],
|
|
302
|
+
"allowedTo": [
|
|
303
|
+
"COMPONENT",
|
|
304
|
+
"INFRASTRUCTURE",
|
|
305
|
+
"ENVIRONMENT",
|
|
306
|
+
"DATABASE_ENTITY"
|
|
307
|
+
],
|
|
308
|
+
"multiplicity": {
|
|
309
|
+
"from": "many",
|
|
310
|
+
"to": "many"
|
|
311
|
+
},
|
|
312
|
+
"validation": {
|
|
313
|
+
"severity": "error"
|
|
314
|
+
},
|
|
315
|
+
"strength": "STRONG"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"type": "DERIVES_FROM",
|
|
319
|
+
"description": "Evoluci\xF3n conceptual gen\xE9rica.",
|
|
320
|
+
"category": "SEMANTIC",
|
|
321
|
+
"allowedFrom": [
|
|
322
|
+
"REQUIREMENT",
|
|
323
|
+
"FEATURE",
|
|
324
|
+
"DESIGN",
|
|
325
|
+
"BUSINESS_RULE",
|
|
326
|
+
"DECISION",
|
|
327
|
+
"USE_CASE"
|
|
328
|
+
],
|
|
329
|
+
"allowedTo": [
|
|
330
|
+
"REQUIREMENT",
|
|
331
|
+
"FEATURE",
|
|
332
|
+
"DESIGN",
|
|
333
|
+
"BUSINESS_RULE",
|
|
334
|
+
"DECISION",
|
|
335
|
+
"USE_CASE"
|
|
336
|
+
],
|
|
337
|
+
"multiplicity": {
|
|
338
|
+
"from": "many",
|
|
339
|
+
"to": "many"
|
|
340
|
+
},
|
|
341
|
+
"validation": {
|
|
342
|
+
"severity": "info"
|
|
343
|
+
},
|
|
344
|
+
"strength": "WEAK"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"type": "DOCUMENTS",
|
|
348
|
+
"description": "Conecta documentaci\xF3n con el artefacto descrito.",
|
|
349
|
+
"category": "SEMANTIC",
|
|
350
|
+
"allowedFrom": [
|
|
351
|
+
"DOCUMENTATION",
|
|
352
|
+
"GLOSSARY_TERM"
|
|
353
|
+
],
|
|
354
|
+
"allowedTo": [
|
|
355
|
+
"PROJECT",
|
|
356
|
+
"EPIC",
|
|
357
|
+
"FEATURE",
|
|
358
|
+
"REQUIREMENT",
|
|
359
|
+
"BUSINESS_RULE",
|
|
360
|
+
"USE_CASE",
|
|
361
|
+
"DESIGN",
|
|
362
|
+
"DECISION",
|
|
363
|
+
"CODE_ENTITY",
|
|
364
|
+
"TEST_CASE",
|
|
365
|
+
"CHANGE",
|
|
366
|
+
"BUG",
|
|
367
|
+
"RISK",
|
|
368
|
+
"GLOSSARY_TERM",
|
|
369
|
+
"COMPONENT",
|
|
370
|
+
"API",
|
|
371
|
+
"DATABASE_ENTITY",
|
|
372
|
+
"TEST",
|
|
373
|
+
"DOCUMENTATION",
|
|
374
|
+
"INCIDENT",
|
|
375
|
+
"INFRASTRUCTURE",
|
|
376
|
+
"DEPLOYMENT",
|
|
377
|
+
"MONITORING",
|
|
378
|
+
"MAINTENANCE",
|
|
379
|
+
"SYSTEM_VERSION",
|
|
380
|
+
"VERSION"
|
|
381
|
+
],
|
|
382
|
+
"multiplicity": {
|
|
383
|
+
"from": "many",
|
|
384
|
+
"to": "many"
|
|
385
|
+
},
|
|
386
|
+
"validation": {
|
|
387
|
+
"severity": "info"
|
|
388
|
+
},
|
|
389
|
+
"strength": "WEAK"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"type": "FIXES",
|
|
393
|
+
"description": "Conecta correcciones con bugs o incidentes.",
|
|
394
|
+
"category": "TRACEABILITY",
|
|
395
|
+
"allowedFrom": [
|
|
396
|
+
"CHANGE",
|
|
397
|
+
"CODE_ENTITY",
|
|
398
|
+
"COMPONENT",
|
|
399
|
+
"SYSTEM_VERSION"
|
|
400
|
+
],
|
|
401
|
+
"allowedTo": [
|
|
402
|
+
"BUG",
|
|
403
|
+
"INCIDENT",
|
|
404
|
+
"RISK"
|
|
405
|
+
],
|
|
406
|
+
"multiplicity": {
|
|
407
|
+
"from": "many",
|
|
408
|
+
"to": "many"
|
|
409
|
+
},
|
|
410
|
+
"validation": {
|
|
411
|
+
"severity": "error"
|
|
412
|
+
},
|
|
413
|
+
"strength": "STRONG"
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"type": "IMPACTS",
|
|
417
|
+
"description": "Descripci\xF3n de posibles efectos colaterales.",
|
|
418
|
+
"category": "SEMANTIC",
|
|
419
|
+
"allowedFrom": [
|
|
420
|
+
"CHANGE",
|
|
421
|
+
"RISK",
|
|
422
|
+
"BUG",
|
|
423
|
+
"INCIDENT",
|
|
424
|
+
"DECISION"
|
|
425
|
+
],
|
|
426
|
+
"allowedTo": [
|
|
427
|
+
"PROJECT",
|
|
428
|
+
"EPIC",
|
|
429
|
+
"FEATURE",
|
|
430
|
+
"REQUIREMENT",
|
|
431
|
+
"DESIGN",
|
|
432
|
+
"CODE_ENTITY",
|
|
433
|
+
"COMPONENT",
|
|
434
|
+
"API",
|
|
435
|
+
"DATABASE_ENTITY",
|
|
436
|
+
"SYSTEM_VERSION"
|
|
437
|
+
],
|
|
438
|
+
"multiplicity": {
|
|
439
|
+
"from": "many",
|
|
440
|
+
"to": "many"
|
|
441
|
+
},
|
|
442
|
+
"validation": {
|
|
443
|
+
"severity": "warn"
|
|
444
|
+
},
|
|
445
|
+
"strength": "MEDIUM"
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
"type": "IMPLEMENTS",
|
|
449
|
+
"description": "Conecta implementaci\xF3n con necesidad funcional/t\xE9cnica.",
|
|
450
|
+
"category": "TRACEABILITY",
|
|
451
|
+
"allowedFrom": [
|
|
452
|
+
"CODE_ENTITY",
|
|
453
|
+
"COMPONENT",
|
|
454
|
+
"API",
|
|
455
|
+
"DATABASE_ENTITY",
|
|
456
|
+
"SYSTEM_VERSION"
|
|
457
|
+
],
|
|
458
|
+
"allowedTo": [
|
|
459
|
+
"REQUIREMENT",
|
|
460
|
+
"FEATURE",
|
|
461
|
+
"EPIC",
|
|
462
|
+
"DESIGN",
|
|
463
|
+
"USE_CASE",
|
|
464
|
+
"BUSINESS_RULE"
|
|
465
|
+
],
|
|
466
|
+
"multiplicity": {
|
|
467
|
+
"from": "many",
|
|
468
|
+
"to": "many"
|
|
469
|
+
},
|
|
470
|
+
"validation": {
|
|
471
|
+
"severity": "error"
|
|
472
|
+
},
|
|
473
|
+
"strength": "STRONG"
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
"type": "IMPORTS",
|
|
477
|
+
"description": "Trazabilidad de importaci\xF3n est\xE1tica a nivel de c\xF3digo.",
|
|
478
|
+
"category": "STRUCTURAL",
|
|
479
|
+
"allowedFrom": [
|
|
480
|
+
"CODE_ENTITY"
|
|
481
|
+
],
|
|
482
|
+
"allowedTo": [
|
|
483
|
+
"CODE_ENTITY",
|
|
484
|
+
"LIBRARY"
|
|
485
|
+
],
|
|
486
|
+
"multiplicity": {
|
|
487
|
+
"from": "many",
|
|
488
|
+
"to": "many"
|
|
489
|
+
},
|
|
490
|
+
"validation": {
|
|
491
|
+
"severity": "warn"
|
|
492
|
+
},
|
|
493
|
+
"strength": "MEDIUM"
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
"type": "JUSTIFIES",
|
|
497
|
+
"description": "Conecta decisiones con aquello que justifican.",
|
|
498
|
+
"category": "SEMANTIC",
|
|
499
|
+
"allowedFrom": [
|
|
500
|
+
"DECISION",
|
|
501
|
+
"BUSINESS_RULE",
|
|
502
|
+
"DOCUMENTATION",
|
|
503
|
+
"RISK"
|
|
504
|
+
],
|
|
505
|
+
"allowedTo": [
|
|
506
|
+
"DESIGN",
|
|
507
|
+
"REQUIREMENT",
|
|
508
|
+
"FEATURE",
|
|
509
|
+
"EPIC",
|
|
510
|
+
"PROJECT",
|
|
511
|
+
"CODE_ENTITY",
|
|
512
|
+
"COMPONENT",
|
|
513
|
+
"INFRASTRUCTURE",
|
|
514
|
+
"DEPLOYMENT",
|
|
515
|
+
"MAINTENANCE"
|
|
516
|
+
],
|
|
517
|
+
"multiplicity": {
|
|
518
|
+
"from": "many",
|
|
519
|
+
"to": "many"
|
|
520
|
+
},
|
|
521
|
+
"validation": {
|
|
522
|
+
"severity": "warn"
|
|
523
|
+
},
|
|
524
|
+
"strength": "MEDIUM"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
"type": "MONITORS",
|
|
528
|
+
"description": "Relaci\xF3n de observabilidad.",
|
|
529
|
+
"category": "OPERATIONAL",
|
|
530
|
+
"allowedFrom": [
|
|
531
|
+
"MONITORING",
|
|
532
|
+
"CHECK"
|
|
533
|
+
],
|
|
534
|
+
"allowedTo": [
|
|
535
|
+
"COMPONENT",
|
|
536
|
+
"INFRASTRUCTURE",
|
|
537
|
+
"DEPLOYMENT",
|
|
538
|
+
"DATABASE_ENTITY",
|
|
539
|
+
"API"
|
|
540
|
+
],
|
|
541
|
+
"multiplicity": {
|
|
542
|
+
"from": "many",
|
|
543
|
+
"to": "many"
|
|
544
|
+
},
|
|
545
|
+
"validation": {
|
|
546
|
+
"severity": "info"
|
|
547
|
+
},
|
|
548
|
+
"strength": "WEAK"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"type": "REFINES",
|
|
552
|
+
"description": "Descompone artefactos en otros m\xE1s concretos.",
|
|
553
|
+
"category": "TRACEABILITY",
|
|
554
|
+
"allowedFrom": [
|
|
555
|
+
"FEATURE",
|
|
556
|
+
"REQUIREMENT",
|
|
557
|
+
"BUG",
|
|
558
|
+
"RISK",
|
|
559
|
+
"DESIGN"
|
|
560
|
+
],
|
|
561
|
+
"allowedTo": [
|
|
562
|
+
"EPIC",
|
|
563
|
+
"FEATURE",
|
|
564
|
+
"PROJECT",
|
|
565
|
+
"BUSINESS_RULE",
|
|
566
|
+
"REQUIREMENT",
|
|
567
|
+
"DESIGN"
|
|
568
|
+
],
|
|
569
|
+
"multiplicity": {
|
|
570
|
+
"from": "many",
|
|
571
|
+
"to": "many"
|
|
572
|
+
},
|
|
573
|
+
"validation": {
|
|
574
|
+
"severity": "warn"
|
|
575
|
+
},
|
|
576
|
+
"strength": "MEDIUM"
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
"type": "RELATES_TO",
|
|
580
|
+
"description": 'Uso gen\xE9rico propenso a poluci\xF3n sem\xE1ntica. (DISCOURAGED)\nPara mantener un grafo limpio, solo \xFAsala aportando rationale expl\xEDcito:\n\nrelations:\n - to: mi-otro-artefacto\n type: RELATES_TO\n rationale: "No encaja con USES o DEPENDS_ON debido al contexto X."\n',
|
|
581
|
+
"category": "SEMANTIC",
|
|
582
|
+
"allowedFrom": [
|
|
583
|
+
"PROJECT",
|
|
584
|
+
"EPIC",
|
|
585
|
+
"FEATURE",
|
|
586
|
+
"REQUIREMENT",
|
|
587
|
+
"BUSINESS_RULE",
|
|
588
|
+
"USE_CASE",
|
|
589
|
+
"DESIGN",
|
|
590
|
+
"DECISION",
|
|
591
|
+
"CODE_ENTITY",
|
|
592
|
+
"TEST_CASE",
|
|
593
|
+
"CHANGE",
|
|
594
|
+
"BUG",
|
|
595
|
+
"RISK",
|
|
596
|
+
"GLOSSARY_TERM",
|
|
597
|
+
"COMPONENT",
|
|
598
|
+
"API",
|
|
599
|
+
"DATABASE_ENTITY",
|
|
600
|
+
"TEST",
|
|
601
|
+
"DOCUMENTATION",
|
|
602
|
+
"INCIDENT",
|
|
603
|
+
"INFRASTRUCTURE",
|
|
604
|
+
"DEPLOYMENT",
|
|
605
|
+
"MONITORING",
|
|
606
|
+
"MAINTENANCE",
|
|
607
|
+
"SYSTEM_VERSION",
|
|
608
|
+
"VERSION"
|
|
609
|
+
],
|
|
610
|
+
"allowedTo": [
|
|
611
|
+
"PROJECT",
|
|
612
|
+
"EPIC",
|
|
613
|
+
"FEATURE",
|
|
614
|
+
"REQUIREMENT",
|
|
615
|
+
"BUSINESS_RULE",
|
|
616
|
+
"USE_CASE",
|
|
617
|
+
"DESIGN",
|
|
618
|
+
"DECISION",
|
|
619
|
+
"CODE_ENTITY",
|
|
620
|
+
"TEST_CASE",
|
|
621
|
+
"CHANGE",
|
|
622
|
+
"BUG",
|
|
623
|
+
"RISK",
|
|
624
|
+
"GLOSSARY_TERM",
|
|
625
|
+
"COMPONENT",
|
|
626
|
+
"API",
|
|
627
|
+
"DATABASE_ENTITY",
|
|
628
|
+
"TEST",
|
|
629
|
+
"DOCUMENTATION",
|
|
630
|
+
"INCIDENT",
|
|
631
|
+
"INFRASTRUCTURE",
|
|
632
|
+
"DEPLOYMENT",
|
|
633
|
+
"MONITORING",
|
|
634
|
+
"MAINTENANCE",
|
|
635
|
+
"SYSTEM_VERSION",
|
|
636
|
+
"VERSION"
|
|
637
|
+
],
|
|
638
|
+
"multiplicity": {
|
|
639
|
+
"from": "many",
|
|
640
|
+
"to": "many"
|
|
641
|
+
},
|
|
642
|
+
"validation": {
|
|
643
|
+
"severity": "warn"
|
|
644
|
+
},
|
|
645
|
+
"strength": "MEDIUM"
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
"type": "REPLACES",
|
|
649
|
+
"description": "Evoluci\xF3n e hist\xF3rico, deprecando versiones anteriores.",
|
|
650
|
+
"category": "EVOLUTIONARY",
|
|
651
|
+
"allowedFrom": [
|
|
652
|
+
"COMPONENT",
|
|
653
|
+
"API",
|
|
654
|
+
"SYSTEM_VERSION",
|
|
655
|
+
"VERSION",
|
|
656
|
+
"REQUIREMENT",
|
|
657
|
+
"FEATURE",
|
|
658
|
+
"DESIGN",
|
|
659
|
+
"DECISION"
|
|
660
|
+
],
|
|
661
|
+
"allowedTo": [
|
|
662
|
+
"COMPONENT",
|
|
663
|
+
"API",
|
|
664
|
+
"SYSTEM_VERSION",
|
|
665
|
+
"VERSION",
|
|
666
|
+
"REQUIREMENT",
|
|
667
|
+
"FEATURE",
|
|
668
|
+
"DESIGN",
|
|
669
|
+
"DECISION"
|
|
670
|
+
],
|
|
671
|
+
"multiplicity": {
|
|
672
|
+
"from": "many",
|
|
673
|
+
"to": "one"
|
|
674
|
+
},
|
|
675
|
+
"validation": {
|
|
676
|
+
"severity": "info"
|
|
677
|
+
},
|
|
678
|
+
"strength": "WEAK"
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
"type": "TESTS",
|
|
682
|
+
"description": "Conecta tests con comportamiento validado.",
|
|
683
|
+
"category": "TRACEABILITY",
|
|
684
|
+
"allowedFrom": [
|
|
685
|
+
"TEST_CASE",
|
|
686
|
+
"TEST"
|
|
687
|
+
],
|
|
688
|
+
"allowedTo": [
|
|
689
|
+
"REQUIREMENT",
|
|
690
|
+
"FEATURE",
|
|
691
|
+
"EPIC",
|
|
692
|
+
"CODE_ENTITY",
|
|
693
|
+
"COMPONENT",
|
|
694
|
+
"API",
|
|
695
|
+
"DATABASE_ENTITY",
|
|
696
|
+
"BUG",
|
|
697
|
+
"INCIDENT"
|
|
698
|
+
],
|
|
699
|
+
"multiplicity": {
|
|
700
|
+
"from": "many",
|
|
701
|
+
"to": "many"
|
|
702
|
+
},
|
|
703
|
+
"validation": {
|
|
704
|
+
"severity": "error"
|
|
705
|
+
},
|
|
706
|
+
"strength": "STRONG"
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
"type": "USES",
|
|
710
|
+
"description": "Llamada funcional, invocaci\xF3n o flujo en tiempo de ejecuci\xF3n.",
|
|
711
|
+
"category": "OPERATIONAL",
|
|
712
|
+
"allowedFrom": [
|
|
713
|
+
"CODE_ENTITY",
|
|
714
|
+
"COMPONENT",
|
|
715
|
+
"API",
|
|
716
|
+
"FEATURE",
|
|
717
|
+
"USE_CASE"
|
|
718
|
+
],
|
|
719
|
+
"allowedTo": [
|
|
720
|
+
"CODE_ENTITY",
|
|
721
|
+
"COMPONENT",
|
|
722
|
+
"API",
|
|
723
|
+
"SYSTEM_VERSION"
|
|
724
|
+
],
|
|
725
|
+
"multiplicity": {
|
|
726
|
+
"from": "many",
|
|
727
|
+
"to": "many"
|
|
728
|
+
},
|
|
729
|
+
"validation": {
|
|
730
|
+
"severity": "warn"
|
|
731
|
+
},
|
|
732
|
+
"strength": "MEDIUM"
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
"type": "VALIDATES",
|
|
736
|
+
"description": "Validaci\xF3n emp\xEDrica/humana (QA Manual).",
|
|
737
|
+
"category": "TRACEABILITY",
|
|
738
|
+
"allowedFrom": [
|
|
739
|
+
"PROCESS",
|
|
740
|
+
"CHECK",
|
|
741
|
+
"DOCUMENTATION"
|
|
742
|
+
],
|
|
743
|
+
"allowedTo": [
|
|
744
|
+
"REQUIREMENT",
|
|
745
|
+
"FEATURE",
|
|
746
|
+
"USE_CASE",
|
|
747
|
+
"DEPLOYMENT"
|
|
748
|
+
],
|
|
749
|
+
"multiplicity": {
|
|
750
|
+
"from": "many",
|
|
751
|
+
"to": "many"
|
|
752
|
+
},
|
|
753
|
+
"validation": {
|
|
754
|
+
"severity": "info"
|
|
755
|
+
},
|
|
756
|
+
"strength": "WEAK"
|
|
757
|
+
}
|
|
758
|
+
];
|
|
759
|
+
|
|
760
|
+
// src/core/registry/RelationRegistry.ts
|
|
761
|
+
var RelationRegistry = class {
|
|
762
|
+
static {
|
|
763
|
+
this.relationsMap = new Map(
|
|
764
|
+
GENERATED_RELATIONS.map((rel) => [rel.type, rel])
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
static getValidTypes() {
|
|
768
|
+
return Array.from(this.relationsMap.keys());
|
|
769
|
+
}
|
|
770
|
+
static getContract(type) {
|
|
771
|
+
return this.relationsMap.get(type);
|
|
772
|
+
}
|
|
773
|
+
static isValid(type) {
|
|
774
|
+
return this.relationsMap.has(type);
|
|
775
|
+
}
|
|
776
|
+
static getRelationsByCategory(category) {
|
|
777
|
+
return GENERATED_RELATIONS.filter((rel) => rel.category === category);
|
|
778
|
+
}
|
|
779
|
+
static getAllContracts() {
|
|
780
|
+
return GENERATED_RELATIONS;
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// scripts/core/parser.ts
|
|
785
|
+
import yaml from "js-yaml";
|
|
786
|
+
function parseOpenLagDocs(docsDir) {
|
|
787
|
+
const diag = new DiagnosticEngine();
|
|
788
|
+
const state = {
|
|
789
|
+
versions: [],
|
|
790
|
+
systemVersions: [],
|
|
791
|
+
changes: [],
|
|
792
|
+
artifacts: [],
|
|
793
|
+
relations: [],
|
|
794
|
+
errors: []
|
|
795
|
+
};
|
|
796
|
+
const documents = scanDocs(docsDir);
|
|
797
|
+
for (const doc of documents) {
|
|
798
|
+
const { content, file: fullPath } = doc;
|
|
799
|
+
const sections = content.split(/---+\r?\n/);
|
|
800
|
+
let i = 0;
|
|
801
|
+
if (content.trim().startsWith("---")) i = 1;
|
|
802
|
+
for (; i < sections.length; i += 1) {
|
|
803
|
+
const section = sections[i].trim();
|
|
804
|
+
if (section.length < 5) continue;
|
|
805
|
+
let parsed;
|
|
806
|
+
try {
|
|
807
|
+
const yamlCandidate = sections[i].split("\n").map((l) => l.replace(/^##\s?/, "")).join("\n").replace(/---$/, "").trim();
|
|
808
|
+
parsed = yaml.load(yamlCandidate);
|
|
809
|
+
} catch (e) {
|
|
810
|
+
throw new Error(`CRITICAL: Invalid YAML block in ${fullPath}: ${e.message}`, { cause: e });
|
|
811
|
+
}
|
|
812
|
+
if (parsed && typeof parsed === "object" && (parsed.id || parsed.ID) && (parsed.type || parsed.Type)) {
|
|
813
|
+
const body = i + 1 < sections.length ? sections[i + 1].trim() : "";
|
|
814
|
+
if (i + 1 < sections.length) i++;
|
|
815
|
+
const normalized = normalizeArtifact(parsed, fullPath, body);
|
|
816
|
+
try {
|
|
817
|
+
ArtifactSchema.parse(normalized);
|
|
818
|
+
const artifact = normalized;
|
|
819
|
+
state.artifacts.push(artifact);
|
|
820
|
+
const typeValue = artifact.type;
|
|
821
|
+
if (typeValue === "VERSION") {
|
|
822
|
+
state.versions.push({
|
|
823
|
+
id: artifact.id,
|
|
824
|
+
name: String(parsed.name || ""),
|
|
825
|
+
timestamp: String(parsed.timestamp || ""),
|
|
826
|
+
parentVersion: parsed.parentVersion || null
|
|
827
|
+
});
|
|
828
|
+
} else if (typeValue === "SYSTEM_VERSION") {
|
|
829
|
+
state.systemVersions.push({
|
|
830
|
+
id: artifact.id,
|
|
831
|
+
component: String(parsed.component || ""),
|
|
832
|
+
version: String(parsed.version || ""),
|
|
833
|
+
releaseDate: String(parsed.releaseDate || "")
|
|
834
|
+
});
|
|
835
|
+
} else if (typeValue === "CHANGE") {
|
|
836
|
+
state.changes.push({
|
|
837
|
+
id: artifact.id,
|
|
838
|
+
type: parsed.changeType || "FEATURE",
|
|
839
|
+
title: artifact.title,
|
|
840
|
+
description: artifact.description,
|
|
841
|
+
affects: Array.isArray(parsed.affects) ? parsed.affects : [],
|
|
842
|
+
versionFrom: String(parsed.versionFrom || ""),
|
|
843
|
+
versionTo: String(parsed.versionTo || "")
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
const rels = parsed.relations || parsed.Relations;
|
|
847
|
+
if (rels) {
|
|
848
|
+
const relArray = Array.isArray(rels) ? rels : [rels];
|
|
849
|
+
relArray.forEach((rel, idx) => {
|
|
850
|
+
const to = typeof rel === "string" ? rel : rel.to || rel.id || rel.ID;
|
|
851
|
+
if (to) {
|
|
852
|
+
const relType = rel.type;
|
|
853
|
+
if (!relType) {
|
|
854
|
+
diag.add(fullPath, `Relation targeting ${to} missing 'type' in artifact ${artifact.id}`, "WARNING" /* WARNING */);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
const contract = RelationRegistry.getContract(relType);
|
|
858
|
+
state.relations.push({
|
|
859
|
+
id: `rel-${artifact.id}-${idx}`,
|
|
860
|
+
from: artifact.id,
|
|
861
|
+
to: String(to),
|
|
862
|
+
type: relType,
|
|
863
|
+
category: contract?.category,
|
|
864
|
+
strength: contract?.strength,
|
|
865
|
+
file: fullPath
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
} catch (e) {
|
|
871
|
+
diag.add(fullPath, `Schema validation error: ${e.message}`, "INVALID" /* INVALID */);
|
|
872
|
+
}
|
|
873
|
+
} else if (parsed && typeof parsed === "object") {
|
|
874
|
+
if (!(parsed.id || parsed.ID)) {
|
|
875
|
+
diag.add(fullPath, `Missing ID in artifact`, "INVALID" /* INVALID */);
|
|
876
|
+
} else if (!(parsed.type || parsed.Type)) {
|
|
877
|
+
diag.add(fullPath, `Missing Type in artifact`, "INVALID" /* INVALID */);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
state.errors = diag.getErrors();
|
|
883
|
+
return state;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// scripts/cli/generate.ts
|
|
887
|
+
import chokidar from "chokidar";
|
|
888
|
+
import chalk from "chalk";
|
|
889
|
+
function isDescendant(currentVersionId, artifactVersionId, versions) {
|
|
890
|
+
const artifactVersion = versions.find((v) => v.id === artifactVersionId);
|
|
891
|
+
if (!artifactVersion) return false;
|
|
892
|
+
let temp = versions.find((v) => v.id === currentVersionId);
|
|
893
|
+
let depth = 0;
|
|
894
|
+
while (temp && temp.parentVersion) {
|
|
895
|
+
depth++;
|
|
896
|
+
if (depth > 50) return false;
|
|
897
|
+
if (temp.parentVersion === artifactVersionId) return true;
|
|
898
|
+
temp = versions.find((v) => v.id === temp.parentVersion);
|
|
899
|
+
}
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
function generateData(docsDir, outputDir, silent = false) {
|
|
903
|
+
if (!silent) console.log(chalk.blue("\u{1F680} Generating OpenLAG Static Data..."));
|
|
904
|
+
const parsedData = parseOpenLagDocs(docsDir);
|
|
905
|
+
const state = {
|
|
906
|
+
versions: parsedData.versions,
|
|
907
|
+
systemVersions: parsedData.systemVersions,
|
|
908
|
+
graphs: {},
|
|
909
|
+
changes: parsedData.changes
|
|
910
|
+
};
|
|
911
|
+
const allArtifacts = parsedData.artifacts;
|
|
912
|
+
const allRelations = parsedData.relations;
|
|
913
|
+
state.versions.forEach((v) => {
|
|
914
|
+
state.graphs[v.id] = {
|
|
915
|
+
artifacts: allArtifacts.filter((a) => a.type === "SYSTEM_VERSION" || a.type === "VERSION" || a.version === v.id || isDescendant(v.id, a.version, state.versions)),
|
|
916
|
+
relations: allRelations.filter((r) => {
|
|
917
|
+
const fromArt = allArtifacts.find((a) => a.id === r.from);
|
|
918
|
+
return fromArt && (fromArt.type === "SYSTEM_VERSION" || fromArt.type === "VERSION" || fromArt.version === v.id || isDescendant(v.id, fromArt.version, state.versions));
|
|
919
|
+
})
|
|
920
|
+
};
|
|
921
|
+
});
|
|
922
|
+
if (!fs2.existsSync(outputDir)) fs2.mkdirSync(outputDir, { recursive: true });
|
|
923
|
+
fs2.writeFileSync(
|
|
924
|
+
path2.join(outputDir, "graph-data.json"),
|
|
925
|
+
JSON.stringify(state, null, 2)
|
|
926
|
+
);
|
|
927
|
+
if (!silent) console.log(chalk.green("\u2705 Data generated at"), chalk.cyan(path2.join(outputDir, "graph-data.json")));
|
|
928
|
+
}
|
|
929
|
+
function watchData(docsDir, outputDir) {
|
|
930
|
+
console.log(chalk.yellow("\u{1F440} Watching for changes in"), chalk.cyan(docsDir));
|
|
931
|
+
const watcher = chokidar.watch(docsDir, {
|
|
932
|
+
ignoreInitial: true,
|
|
933
|
+
persistent: true
|
|
934
|
+
});
|
|
935
|
+
const runCleanly = () => {
|
|
936
|
+
try {
|
|
937
|
+
generateData(docsDir, outputDir, true);
|
|
938
|
+
console.log(chalk.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] `) + chalk.green("Regenerated graph-data.json"));
|
|
939
|
+
} catch (e) {
|
|
940
|
+
console.error(chalk.red("\u274C Error during regeneration:"), e);
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
let timeout;
|
|
944
|
+
const debouncedRun = () => {
|
|
945
|
+
clearTimeout(timeout);
|
|
946
|
+
timeout = setTimeout(runCleanly, 300);
|
|
947
|
+
};
|
|
948
|
+
watcher.on("all", (event, path9) => {
|
|
949
|
+
debouncedRun();
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// scripts/cli/vite-bin.ts
|
|
954
|
+
import path3 from "path";
|
|
955
|
+
import { createRequire } from "module";
|
|
956
|
+
function resolveViteBin(importMetaUrl) {
|
|
957
|
+
const require2 = createRequire(importMetaUrl);
|
|
958
|
+
const viteEntry = require2.resolve("vite");
|
|
959
|
+
return path3.resolve(path3.dirname(viteEntry), "../../bin/vite.js");
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// scripts/cli/build.ts
|
|
963
|
+
function buildPortal() {
|
|
964
|
+
const packageRoot2 = path4.resolve(path4.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
965
|
+
const viteBin = resolveViteBin(import.meta.url);
|
|
966
|
+
const viteConfig = path4.join(packageRoot2, "vite.config.ts");
|
|
967
|
+
const docsDir = path4.join(process.cwd(), "docs");
|
|
968
|
+
const outputDir = path4.join(process.cwd(), "public");
|
|
969
|
+
generateData(docsDir, outputDir);
|
|
970
|
+
console.log(chalk2.blue("Building OpenLAG portal..."));
|
|
971
|
+
try {
|
|
972
|
+
execFileSync(process.execPath, [viteBin, "build", "--config", viteConfig], {
|
|
973
|
+
cwd: packageRoot2,
|
|
974
|
+
env: {
|
|
975
|
+
...process.env,
|
|
976
|
+
OPENLAG_PROJECT_ROOT: process.cwd()
|
|
977
|
+
},
|
|
978
|
+
stdio: "inherit"
|
|
979
|
+
});
|
|
980
|
+
console.log(chalk2.green("Portal build complete."));
|
|
981
|
+
} catch {
|
|
982
|
+
console.error(chalk2.red("Build failed."));
|
|
983
|
+
process.exit(1);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// scripts/cli/dev.ts
|
|
988
|
+
import { spawn } from "child_process";
|
|
989
|
+
import path5 from "path";
|
|
990
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
991
|
+
import chalk3 from "chalk";
|
|
992
|
+
function runDevServer() {
|
|
993
|
+
const packageRoot2 = path5.resolve(path5.dirname(fileURLToPath2(import.meta.url)), "../..");
|
|
994
|
+
const viteBin = resolveViteBin(import.meta.url);
|
|
995
|
+
const viteConfig = path5.join(packageRoot2, "vite.config.ts");
|
|
996
|
+
const projectRoot = process.cwd();
|
|
997
|
+
const docsDir = path5.join(projectRoot, "docs");
|
|
998
|
+
const outputDir = path5.join(projectRoot, "public");
|
|
999
|
+
generateData(docsDir, outputDir);
|
|
1000
|
+
watchData(docsDir, outputDir);
|
|
1001
|
+
console.log(chalk3.blue("Starting OpenLAG portal dev server..."));
|
|
1002
|
+
const vite = spawn(process.execPath, [viteBin, "--config", viteConfig], {
|
|
1003
|
+
cwd: packageRoot2,
|
|
1004
|
+
env: {
|
|
1005
|
+
...process.env,
|
|
1006
|
+
OPENLAG_PROJECT_ROOT: projectRoot
|
|
1007
|
+
},
|
|
1008
|
+
stdio: "inherit"
|
|
1009
|
+
});
|
|
1010
|
+
vite.on("close", (code) => {
|
|
1011
|
+
process.exit(code || 0);
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// scripts/cli/init.ts
|
|
1016
|
+
import fs3 from "fs";
|
|
1017
|
+
import path6 from "path";
|
|
1018
|
+
import chalk4 from "chalk";
|
|
1019
|
+
async function initProject(projectName, projectDesc, includeAllRelations) {
|
|
1020
|
+
const name = projectName || process.env.PROJECT_NAME || "My OpenLAG Project";
|
|
1021
|
+
const desc = projectDesc || process.env.PROJECT_DESCRIPTION || "Living Architecture documentation for my system.";
|
|
1022
|
+
const ROOT_DIR = process.cwd();
|
|
1023
|
+
console.log(chalk4.blue(`\u{1F6E0}\uFE0F Initializing OpenLAG for: `) + chalk4.bold(name));
|
|
1024
|
+
const metadataPath = path6.join(ROOT_DIR, "metadata.json");
|
|
1025
|
+
if (fs3.existsSync(metadataPath)) {
|
|
1026
|
+
const metadata = JSON.parse(fs3.readFileSync(metadataPath, "utf-8"));
|
|
1027
|
+
metadata.name = name;
|
|
1028
|
+
metadata.description = desc;
|
|
1029
|
+
fs3.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
1030
|
+
console.log(chalk4.green("\u2705 Updated metadata.json"));
|
|
1031
|
+
}
|
|
1032
|
+
const htmlPath = path6.join(ROOT_DIR, "index.html");
|
|
1033
|
+
if (fs3.existsSync(htmlPath)) {
|
|
1034
|
+
let html = fs3.readFileSync(htmlPath, "utf-8");
|
|
1035
|
+
html = html.replace(/<title>.*?<\/title>/, `<title>${name} | OpenLAG</title>`);
|
|
1036
|
+
fs3.writeFileSync(htmlPath, html);
|
|
1037
|
+
console.log(chalk4.green("\u2705 Updated index.html title"));
|
|
1038
|
+
}
|
|
1039
|
+
const docsDir = path6.join(ROOT_DIR, "docs");
|
|
1040
|
+
if (!fs3.existsSync(docsDir)) {
|
|
1041
|
+
fs3.mkdirSync(docsDir);
|
|
1042
|
+
console.log(chalk4.green("\u2705 Created /docs directory"));
|
|
1043
|
+
}
|
|
1044
|
+
const relationsDir = path6.join(docsDir, "relations");
|
|
1045
|
+
if (!fs3.existsSync(relationsDir)) {
|
|
1046
|
+
fs3.mkdirSync(relationsDir);
|
|
1047
|
+
console.log(chalk4.green("\u2705 Created /docs/relations directory"));
|
|
1048
|
+
} else {
|
|
1049
|
+
const files = fs3.readdirSync(relationsDir);
|
|
1050
|
+
for (const file of files) {
|
|
1051
|
+
if (file.endsWith(".yaml")) {
|
|
1052
|
+
fs3.unlinkSync(path6.join(relationsDir, file));
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
const mandatoryRelations = [
|
|
1057
|
+
{
|
|
1058
|
+
name: "IMPLEMENTS.yaml",
|
|
1059
|
+
content: `relation: IMPLEMENTS
|
|
1060
|
+
description: "Conecta implementaci\xF3n con necesidad funcional/t\xE9cnica."
|
|
1061
|
+
category: TRACEABILITY
|
|
1062
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, SYSTEM_VERSION]
|
|
1063
|
+
allowedTo: [REQUIREMENT, FEATURE, EPIC, DESIGN, USE_CASE, BUSINESS_RULE]
|
|
1064
|
+
multiplicity:
|
|
1065
|
+
from: many
|
|
1066
|
+
to: many
|
|
1067
|
+
validation:
|
|
1068
|
+
severity: error`
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
name: "TESTS.yaml",
|
|
1072
|
+
content: `relation: TESTS
|
|
1073
|
+
description: "Conecta tests con comportamiento validado."
|
|
1074
|
+
category: TRACEABILITY
|
|
1075
|
+
allowedFrom: [TEST_CASE, TEST]
|
|
1076
|
+
allowedTo: [REQUIREMENT, FEATURE, EPIC, CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, BUG, INCIDENT]
|
|
1077
|
+
multiplicity:
|
|
1078
|
+
from: many
|
|
1079
|
+
to: many
|
|
1080
|
+
validation:
|
|
1081
|
+
severity: error`
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
name: "REFINES.yaml",
|
|
1085
|
+
content: `relation: REFINES
|
|
1086
|
+
description: "Descompone artefactos en otros m\xE1s concretos."
|
|
1087
|
+
category: TRACEABILITY
|
|
1088
|
+
allowedFrom: [FEATURE, REQUIREMENT, BUG, RISK, DESIGN]
|
|
1089
|
+
allowedTo: [EPIC, FEATURE, PROJECT, BUSINESS_RULE, REQUIREMENT, DESIGN]
|
|
1090
|
+
multiplicity:
|
|
1091
|
+
from: many
|
|
1092
|
+
to: many
|
|
1093
|
+
validation:
|
|
1094
|
+
severity: warn`
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
name: "FIXES.yaml",
|
|
1098
|
+
content: `relation: FIXES
|
|
1099
|
+
description: "Conecta correcciones con bugs o incidentes."
|
|
1100
|
+
category: TRACEABILITY
|
|
1101
|
+
allowedFrom: [CHANGE, CODE_ENTITY, COMPONENT, SYSTEM_VERSION]
|
|
1102
|
+
allowedTo: [BUG, INCIDENT, RISK]
|
|
1103
|
+
multiplicity:
|
|
1104
|
+
from: many
|
|
1105
|
+
to: many
|
|
1106
|
+
validation:
|
|
1107
|
+
severity: error`
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
name: "DOCUMENTS.yaml",
|
|
1111
|
+
content: `relation: DOCUMENTS
|
|
1112
|
+
description: "Conecta documentaci\xF3n con el artefacto descrito."
|
|
1113
|
+
category: SEMANTIC
|
|
1114
|
+
allowedFrom: [DOCUMENTATION, GLOSSARY_TERM]
|
|
1115
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, BUSINESS_RULE, USE_CASE, DESIGN, DECISION, CODE_ENTITY, TEST_CASE, CHANGE, BUG, RISK, GLOSSARY_TERM, COMPONENT, API, DATABASE_ENTITY, TEST, DOCUMENTATION, INCIDENT, INFRASTRUCTURE, DEPLOYMENT, MONITORING, MAINTENANCE, SYSTEM_VERSION, VERSION]
|
|
1116
|
+
multiplicity:
|
|
1117
|
+
from: many
|
|
1118
|
+
to: many
|
|
1119
|
+
validation:
|
|
1120
|
+
severity: info`
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
name: "JUSTIFIES.yaml",
|
|
1124
|
+
content: `relation: JUSTIFIES
|
|
1125
|
+
description: "Conecta decisiones con aquello que justifican."
|
|
1126
|
+
category: SEMANTIC
|
|
1127
|
+
allowedFrom: [DECISION, BUSINESS_RULE, DOCUMENTATION, RISK]
|
|
1128
|
+
allowedTo: [DESIGN, REQUIREMENT, FEATURE, EPIC, PROJECT, CODE_ENTITY, COMPONENT, INFRASTRUCTURE, DEPLOYMENT, MAINTENANCE]
|
|
1129
|
+
multiplicity:
|
|
1130
|
+
from: many
|
|
1131
|
+
to: many
|
|
1132
|
+
validation:
|
|
1133
|
+
severity: warn`
|
|
1134
|
+
}
|
|
1135
|
+
];
|
|
1136
|
+
mandatoryRelations.forEach((rel) => {
|
|
1137
|
+
fs3.writeFileSync(path6.join(relationsDir, rel.name), rel.content);
|
|
1138
|
+
});
|
|
1139
|
+
console.log(chalk4.green("\u2705 Created Mandatory Core Relations (IMPLEMENTS, TESTS, REFINES, FIXES, DOCUMENTS, JUSTIFIES)"));
|
|
1140
|
+
if (includeAllRelations) {
|
|
1141
|
+
const optionalRelations = [
|
|
1142
|
+
{
|
|
1143
|
+
name: "DEPENDS_ON.yaml",
|
|
1144
|
+
content: `relation: DEPENDS_ON
|
|
1145
|
+
description: "Acoplamiento est\xE1tico arquitect\xF3nico o de empaquetado."
|
|
1146
|
+
category: STRUCTURAL
|
|
1147
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, LIBRARY, VERSION, SYSTEM_VERSION]
|
|
1148
|
+
allowedTo: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, LIBRARY, VERSION, SYSTEM_VERSION]
|
|
1149
|
+
multiplicity:
|
|
1150
|
+
from: many
|
|
1151
|
+
to: many
|
|
1152
|
+
validation:
|
|
1153
|
+
severity: warn`
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
name: "USES.yaml",
|
|
1157
|
+
content: `relation: USES
|
|
1158
|
+
description: "Llamada funcional, invocaci\xF3n o flujo en tiempo de ejecuci\xF3n."
|
|
1159
|
+
category: OPERATIONAL
|
|
1160
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, FEATURE, USE_CASE]
|
|
1161
|
+
allowedTo: [CODE_ENTITY, COMPONENT, API, SYSTEM_VERSION]
|
|
1162
|
+
multiplicity:
|
|
1163
|
+
from: many
|
|
1164
|
+
to: many
|
|
1165
|
+
validation:
|
|
1166
|
+
severity: warn`
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
name: "DEPLOYS.yaml",
|
|
1170
|
+
content: `relation: DEPLOYS
|
|
1171
|
+
description: "Instanciaci\xF3n de componentes o release en infraestructura."
|
|
1172
|
+
category: OPERATIONAL
|
|
1173
|
+
allowedFrom: [DEPLOYMENT, PIPELINE, SYSTEM_VERSION]
|
|
1174
|
+
allowedTo: [COMPONENT, INFRASTRUCTURE, ENVIRONMENT, DATABASE_ENTITY]
|
|
1175
|
+
multiplicity:
|
|
1176
|
+
from: many
|
|
1177
|
+
to: many
|
|
1178
|
+
validation:
|
|
1179
|
+
severity: error`
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
name: "MONITORS.yaml",
|
|
1183
|
+
content: `relation: MONITORS
|
|
1184
|
+
description: "Relaci\xF3n de observabilidad."
|
|
1185
|
+
category: OPERATIONAL
|
|
1186
|
+
allowedFrom: [MONITORING, CHECK]
|
|
1187
|
+
allowedTo: [COMPONENT, INFRASTRUCTURE, DEPLOYMENT, DATABASE_ENTITY, API]
|
|
1188
|
+
multiplicity:
|
|
1189
|
+
from: many
|
|
1190
|
+
to: many
|
|
1191
|
+
validation:
|
|
1192
|
+
severity: info`
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
name: "IMPACTS.yaml",
|
|
1196
|
+
content: `relation: IMPACTS
|
|
1197
|
+
description: "Descripci\xF3n de posibles efectos colaterales."
|
|
1198
|
+
category: SEMANTIC
|
|
1199
|
+
allowedFrom: [CHANGE, RISK, BUG, INCIDENT, DECISION]
|
|
1200
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, DESIGN, CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, SYSTEM_VERSION]
|
|
1201
|
+
multiplicity:
|
|
1202
|
+
from: many
|
|
1203
|
+
to: many
|
|
1204
|
+
validation:
|
|
1205
|
+
severity: warn`
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
name: "BLOCKS.yaml",
|
|
1209
|
+
content: `relation: BLOCKS
|
|
1210
|
+
description: "Descripci\xF3n de impedimentos directos."
|
|
1211
|
+
category: DEPENDENCY
|
|
1212
|
+
allowedFrom: [BUG, RISK, INCIDENT, CHANGE, DECISION, REQUIREMENT, FEATURE, EPIC]
|
|
1213
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, BUG, CHANGE, DEPLOYMENT, DEPLOYMENT]
|
|
1214
|
+
multiplicity:
|
|
1215
|
+
from: many
|
|
1216
|
+
to: many
|
|
1217
|
+
validation:
|
|
1218
|
+
severity: error`
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
name: "BREAKS.yaml",
|
|
1222
|
+
content: `relation: BREAKS
|
|
1223
|
+
description: "Aver\xEDas o rupturas confirmadas."
|
|
1224
|
+
category: OPERATIONAL
|
|
1225
|
+
allowedFrom: [CHANGE, CODE_ENTITY, COMPONENT, SYSTEM_VERSION]
|
|
1226
|
+
allowedTo: [TEST_CASE, TEST, API, COMPONENT, REQUIREMENT, FEATURE]
|
|
1227
|
+
multiplicity:
|
|
1228
|
+
from: many
|
|
1229
|
+
to: many
|
|
1230
|
+
validation:
|
|
1231
|
+
severity: error`
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
name: "REPLACES.yaml",
|
|
1235
|
+
content: `relation: REPLACES
|
|
1236
|
+
description: "Evoluci\xF3n e hist\xF3rico, deprecando versiones anteriores."
|
|
1237
|
+
category: EVOLUTIONARY
|
|
1238
|
+
allowedFrom: [COMPONENT, API, SYSTEM_VERSION, VERSION, REQUIREMENT, FEATURE, DESIGN, DECISION]
|
|
1239
|
+
allowedTo: [COMPONENT, API, SYSTEM_VERSION, VERSION, REQUIREMENT, FEATURE, DESIGN, DECISION]
|
|
1240
|
+
multiplicity:
|
|
1241
|
+
from: many
|
|
1242
|
+
to: one
|
|
1243
|
+
validation:
|
|
1244
|
+
severity: info`
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
name: "DERIVES_FROM.yaml",
|
|
1248
|
+
content: `relation: DERIVES_FROM
|
|
1249
|
+
description: "Evoluci\xF3n conceptual gen\xE9rica."
|
|
1250
|
+
category: SEMANTIC
|
|
1251
|
+
allowedFrom: [REQUIREMENT, FEATURE, DESIGN, BUSINESS_RULE, DECISION, USE_CASE]
|
|
1252
|
+
allowedTo: [REQUIREMENT, FEATURE, DESIGN, BUSINESS_RULE, DECISION, USE_CASE]
|
|
1253
|
+
multiplicity:
|
|
1254
|
+
from: many
|
|
1255
|
+
to: many
|
|
1256
|
+
validation:
|
|
1257
|
+
severity: info`
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
name: "VALIDATES.yaml",
|
|
1261
|
+
content: `relation: VALIDATES
|
|
1262
|
+
description: "Validaci\xF3n emp\xEDrica/humana (QA Manual)."
|
|
1263
|
+
category: TRACEABILITY
|
|
1264
|
+
allowedFrom: [PROCESS, CHECK, DOCUMENTATION]
|
|
1265
|
+
allowedTo: [REQUIREMENT, FEATURE, USE_CASE, DEPLOYMENT]
|
|
1266
|
+
multiplicity:
|
|
1267
|
+
from: many
|
|
1268
|
+
to: many
|
|
1269
|
+
validation:
|
|
1270
|
+
severity: info`
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
name: "DEFINES.yaml",
|
|
1274
|
+
content: `relation: DEFINES
|
|
1275
|
+
description: "Entidades que instauran glosarios o normas."
|
|
1276
|
+
category: SEMANTIC
|
|
1277
|
+
allowedFrom: [BUSINESS_RULE, DECISION, DOCUMENTATION, GLOSSARY_TERM]
|
|
1278
|
+
allowedTo: [REQUIREMENT, FEATURE, DESIGN, PROJECT, COMPONENT, API, DATABASE_ENTITY]
|
|
1279
|
+
multiplicity:
|
|
1280
|
+
from: many
|
|
1281
|
+
to: many
|
|
1282
|
+
validation:
|
|
1283
|
+
severity: info`
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
name: "CALLS.yaml",
|
|
1287
|
+
content: `relation: CALLS
|
|
1288
|
+
description: "Trazabilidad de invocaci\xF3n a nivel c\xF3digo."
|
|
1289
|
+
category: STRUCTURAL
|
|
1290
|
+
allowedFrom: [CODE_ENTITY]
|
|
1291
|
+
allowedTo: [CODE_ENTITY, API]
|
|
1292
|
+
multiplicity:
|
|
1293
|
+
from: many
|
|
1294
|
+
to: many
|
|
1295
|
+
validation:
|
|
1296
|
+
severity: warn`
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
name: "IMPORTS.yaml",
|
|
1300
|
+
content: `relation: IMPORTS
|
|
1301
|
+
description: "Trazabilidad de importaci\xF3n est\xE1tica a nivel de c\xF3digo."
|
|
1302
|
+
category: STRUCTURAL
|
|
1303
|
+
allowedFrom: [CODE_ENTITY]
|
|
1304
|
+
allowedTo: [CODE_ENTITY, LIBRARY]
|
|
1305
|
+
multiplicity:
|
|
1306
|
+
from: many
|
|
1307
|
+
to: many
|
|
1308
|
+
validation:
|
|
1309
|
+
severity: warn`
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
name: "RELATES_TO.yaml",
|
|
1313
|
+
content: `relation: RELATES_TO
|
|
1314
|
+
description: |
|
|
1315
|
+
Uso gen\xE9rico propenso a poluci\xF3n sem\xE1ntica. (DISCOURAGED)
|
|
1316
|
+
Para mantener un grafo limpio, solo \xFAsala aportando rationale expl\xEDcito:
|
|
1317
|
+
|
|
1318
|
+
relations:
|
|
1319
|
+
- to: mi-otro-artefacto
|
|
1320
|
+
type: RELATES_TO
|
|
1321
|
+
rationale: "No encaja con USES o DEPENDS_ON debido al contexto X."
|
|
1322
|
+
category: SEMANTIC
|
|
1323
|
+
allowedFrom: [PROJECT, EPIC, FEATURE, REQUIREMENT, BUSINESS_RULE, USE_CASE, DESIGN, DECISION, CODE_ENTITY, TEST_CASE, CHANGE, BUG, RISK, GLOSSARY_TERM, COMPONENT, API, DATABASE_ENTITY, TEST, DOCUMENTATION, INCIDENT, INFRASTRUCTURE, DEPLOYMENT, MONITORING, MAINTENANCE, SYSTEM_VERSION, VERSION]
|
|
1324
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, BUSINESS_RULE, USE_CASE, DESIGN, DECISION, CODE_ENTITY, TEST_CASE, CHANGE, BUG, RISK, GLOSSARY_TERM, COMPONENT, API, DATABASE_ENTITY, TEST, DOCUMENTATION, INCIDENT, INFRASTRUCTURE, DEPLOYMENT, MONITORING, MAINTENANCE, SYSTEM_VERSION, VERSION]
|
|
1325
|
+
multiplicity:
|
|
1326
|
+
from: many
|
|
1327
|
+
to: many
|
|
1328
|
+
validation:
|
|
1329
|
+
severity: warn`
|
|
1330
|
+
}
|
|
1331
|
+
];
|
|
1332
|
+
optionalRelations.forEach((rel) => {
|
|
1333
|
+
fs3.writeFileSync(path6.join(relationsDir, rel.name), rel.content);
|
|
1334
|
+
});
|
|
1335
|
+
console.log(chalk4.green("\u2705 Created Official Optional Relations"));
|
|
1336
|
+
}
|
|
1337
|
+
const versionsDir = path6.join(docsDir, "versions");
|
|
1338
|
+
if (!fs3.existsSync(versionsDir)) {
|
|
1339
|
+
fs3.mkdirSync(versionsDir);
|
|
1340
|
+
console.log(chalk4.green("\u2705 Created /docs/versions directory"));
|
|
1341
|
+
}
|
|
1342
|
+
const versionPath = path6.join(versionsDir, "v-1.0.0.md");
|
|
1343
|
+
if (!fs3.existsSync(versionPath)) {
|
|
1344
|
+
const versionsContent = `---
|
|
1345
|
+
id: v-1.0.0
|
|
1346
|
+
type: VERSION
|
|
1347
|
+
name: "Baseline"
|
|
1348
|
+
timestamp: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
|
|
1349
|
+
parentVersion: null
|
|
1350
|
+
layer: DOCUMENTATION
|
|
1351
|
+
title: "Project Release v1.0.0"
|
|
1352
|
+
description: "Initial release"
|
|
1353
|
+
ownership:
|
|
1354
|
+
owner: architecture
|
|
1355
|
+
team: architecture
|
|
1356
|
+
relations: []
|
|
1357
|
+
---
|
|
1358
|
+
`;
|
|
1359
|
+
fs3.writeFileSync(versionPath, versionsContent);
|
|
1360
|
+
console.log(chalk4.green("\u2705 Created initial docs/versions/v-1.0.0.md"));
|
|
1361
|
+
}
|
|
1362
|
+
const componentsVersionsPath = path6.join(versionsDir, "sys-core-1.0.md");
|
|
1363
|
+
if (!fs3.existsSync(componentsVersionsPath)) {
|
|
1364
|
+
const cvContent = `---
|
|
1365
|
+
id: sys-core-1.0
|
|
1366
|
+
type: SYSTEM_VERSION
|
|
1367
|
+
component: "Core System"
|
|
1368
|
+
version: "1.0.0"
|
|
1369
|
+
releaseDate: "${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}"
|
|
1370
|
+
layer: OPERATIONS
|
|
1371
|
+
title: "Core System v1.0"
|
|
1372
|
+
description: "Base framework."
|
|
1373
|
+
ownership:
|
|
1374
|
+
owner: architecture
|
|
1375
|
+
team: architecture
|
|
1376
|
+
relations: []
|
|
1377
|
+
---
|
|
1378
|
+
`;
|
|
1379
|
+
fs3.writeFileSync(componentsVersionsPath, cvContent);
|
|
1380
|
+
console.log(chalk4.green("\u2705 Created initial docs/versions/sys-core-1.0.md"));
|
|
1381
|
+
}
|
|
1382
|
+
const architectureDir = path6.join(docsDir, "architecture");
|
|
1383
|
+
if (!fs3.existsSync(architectureDir)) {
|
|
1384
|
+
fs3.mkdirSync(architectureDir);
|
|
1385
|
+
console.log(chalk4.green("\u2705 Created /docs/architecture directory"));
|
|
1386
|
+
}
|
|
1387
|
+
const sampleDocPath = path6.join(architectureDir, "architecture-overview.md");
|
|
1388
|
+
if (!fs3.existsSync(sampleDocPath)) {
|
|
1389
|
+
const sampleContent = `---
|
|
1390
|
+
id: arch-overview
|
|
1391
|
+
type: DOCUMENT
|
|
1392
|
+
status: draft
|
|
1393
|
+
title: "Architecture Overview"
|
|
1394
|
+
description: "High-level description of the system architecture."
|
|
1395
|
+
---
|
|
1396
|
+
|
|
1397
|
+
# Architecture Overview
|
|
1398
|
+
|
|
1399
|
+
This document describes the high-level architecture of ${name}.
|
|
1400
|
+
|
|
1401
|
+
## Key Principles
|
|
1402
|
+
- Traceability first.
|
|
1403
|
+
- Documentation as Code.
|
|
1404
|
+
- Graph-based relationships.
|
|
1405
|
+
`;
|
|
1406
|
+
fs3.writeFileSync(sampleDocPath, sampleContent);
|
|
1407
|
+
console.log(chalk4.green("\u2705 Created initial docs/architecture/architecture-overview.md"));
|
|
1408
|
+
}
|
|
1409
|
+
console.log("\n" + chalk4.cyan("\u{1F680} Project initialized successfully!"));
|
|
1410
|
+
console.log("Next steps:");
|
|
1411
|
+
console.log("1. Edit " + chalk4.cyan("docs/") + " to reflect your architecture.");
|
|
1412
|
+
console.log("2. Run " + chalk4.cyan("openlag dev") + " to see your portal.");
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// scripts/cli/lint.ts
|
|
1416
|
+
import path7 from "path";
|
|
1417
|
+
|
|
1418
|
+
// scripts/lint/lint-engine.ts
|
|
1419
|
+
import fs4 from "fs";
|
|
1420
|
+
import yaml2 from "js-yaml";
|
|
1421
|
+
import chalk5 from "chalk";
|
|
1422
|
+
|
|
1423
|
+
// src/core/registry/ArtifactRegistry.ts
|
|
1424
|
+
var CORE_ARTIFACT_TYPES = [
|
|
1425
|
+
"PROJECT",
|
|
1426
|
+
"EPIC",
|
|
1427
|
+
"FEATURE",
|
|
1428
|
+
"REQUIREMENT",
|
|
1429
|
+
"BUSINESS_RULE",
|
|
1430
|
+
"USE_CASE",
|
|
1431
|
+
"DESIGN",
|
|
1432
|
+
"DECISION",
|
|
1433
|
+
"CODE_ENTITY",
|
|
1434
|
+
"TEST_CASE",
|
|
1435
|
+
"CHANGE",
|
|
1436
|
+
"BUG",
|
|
1437
|
+
"RISK",
|
|
1438
|
+
"GLOSSARY_TERM",
|
|
1439
|
+
"COMPONENT",
|
|
1440
|
+
"API",
|
|
1441
|
+
"DATABASE_ENTITY",
|
|
1442
|
+
"TEST",
|
|
1443
|
+
"DOCUMENTATION",
|
|
1444
|
+
"INCIDENT",
|
|
1445
|
+
"INFRASTRUCTURE",
|
|
1446
|
+
"DEPLOYMENT",
|
|
1447
|
+
"MONITORING",
|
|
1448
|
+
"MAINTENANCE",
|
|
1449
|
+
"SYSTEM_VERSION",
|
|
1450
|
+
"VERSION",
|
|
1451
|
+
"LIBRARY",
|
|
1452
|
+
"ENVIRONMENT",
|
|
1453
|
+
"CHECK",
|
|
1454
|
+
"PROCESS",
|
|
1455
|
+
"PIPELINE"
|
|
1456
|
+
];
|
|
1457
|
+
var ArtifactRegistry = class {
|
|
1458
|
+
static getValidTypes() {
|
|
1459
|
+
return CORE_ARTIFACT_TYPES;
|
|
1460
|
+
}
|
|
1461
|
+
static isValid(type) {
|
|
1462
|
+
return CORE_ARTIFACT_TYPES.includes(type);
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
// scripts/lint/lint-rules.ts
|
|
1467
|
+
function runLintRules(data, profile) {
|
|
1468
|
+
const issues = [];
|
|
1469
|
+
const getSeverity = (rule, artifactStatus) => {
|
|
1470
|
+
let severity = profile[rule];
|
|
1471
|
+
if (!severity || severity === "off") return "off";
|
|
1472
|
+
if (artifactStatus === "draft") {
|
|
1473
|
+
if (rule !== "brokenRelation" && rule !== "invalidYaml" && rule !== "duplicateId") {
|
|
1474
|
+
severity = "info";
|
|
1475
|
+
}
|
|
1476
|
+
} else if (artifactStatus === "in_progress") {
|
|
1477
|
+
if (rule !== "brokenRelation" && rule !== "invalidYaml" && rule !== "duplicateId") {
|
|
1478
|
+
if (severity === "error") severity = "warning";
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return severity;
|
|
1482
|
+
};
|
|
1483
|
+
const addIssue = (rule, message, file, artifactId, artifactStatus) => {
|
|
1484
|
+
const severity = getSeverity(rule, artifactStatus);
|
|
1485
|
+
if (severity && severity !== "off") {
|
|
1486
|
+
issues.push({ severity, rule, message, file, artifactId });
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
for (const error of data.errors) {
|
|
1490
|
+
addIssue("invalidYaml", error.message, error.file);
|
|
1491
|
+
}
|
|
1492
|
+
const artifactMap = /* @__PURE__ */ new Map();
|
|
1493
|
+
for (const artifact of data.artifacts) {
|
|
1494
|
+
const list = artifactMap.get(artifact.id) || [];
|
|
1495
|
+
list.push(artifact);
|
|
1496
|
+
artifactMap.set(artifact.id, list);
|
|
1497
|
+
}
|
|
1498
|
+
for (const [id, artifacts] of artifactMap.entries()) {
|
|
1499
|
+
if (artifacts.length > 1) {
|
|
1500
|
+
addIssue("duplicateId", `${id} appears in ${artifacts.length} files`, artifacts[0].file, id);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
for (const artifact of data.artifacts) {
|
|
1504
|
+
if (!ArtifactRegistry.isValid(artifact.type)) {
|
|
1505
|
+
addIssue("invalidArtifactType", `Invalid artifact type: ${artifact.type}`, artifact.file, artifact.id, artifact.status);
|
|
1506
|
+
}
|
|
1507
|
+
if (!artifact.type || !artifact.title) {
|
|
1508
|
+
addIssue("missingRequiredFields", `Artifact missing type or title`, artifact.file, artifact.id, artifact.status);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const targets = new Set(data.artifacts.map((a) => a.id));
|
|
1512
|
+
const implementationsByReq = /* @__PURE__ */ new Map();
|
|
1513
|
+
const testsByReq = /* @__PURE__ */ new Map();
|
|
1514
|
+
const reqsByCode = /* @__PURE__ */ new Map();
|
|
1515
|
+
const reqsByTest = /* @__PURE__ */ new Map();
|
|
1516
|
+
for (const relation of data.relations) {
|
|
1517
|
+
if (!RelationRegistry.isValid(relation.type)) {
|
|
1518
|
+
addIssue("invalidRelationType", `Invalid relation type: ${relation.type}`, relation.file, relation.from);
|
|
1519
|
+
}
|
|
1520
|
+
if (!targets.has(relation.to)) {
|
|
1521
|
+
addIssue("brokenRelation", `${relation.from} -> ${relation.to} target does not exist`, relation.file, relation.from);
|
|
1522
|
+
}
|
|
1523
|
+
if (relation.type === "IMPLEMENTS") {
|
|
1524
|
+
const list = implementationsByReq.get(relation.to) || [];
|
|
1525
|
+
list.push(relation);
|
|
1526
|
+
implementationsByReq.set(relation.to, list);
|
|
1527
|
+
const listCode = reqsByCode.get(relation.from) || [];
|
|
1528
|
+
listCode.push(relation);
|
|
1529
|
+
reqsByCode.set(relation.from, listCode);
|
|
1530
|
+
}
|
|
1531
|
+
if (relation.type === "TESTS" || relation.type === "VALIDATES") {
|
|
1532
|
+
const list = testsByReq.get(relation.to) || [];
|
|
1533
|
+
list.push(relation);
|
|
1534
|
+
testsByReq.set(relation.to, list);
|
|
1535
|
+
const listTest = reqsByTest.get(relation.from) || [];
|
|
1536
|
+
listTest.push(relation);
|
|
1537
|
+
reqsByTest.set(relation.from, listTest);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
for (const artifact of data.artifacts) {
|
|
1541
|
+
const isDraft = artifact.status === "draft";
|
|
1542
|
+
const isClosed = artifact.status === "closed";
|
|
1543
|
+
const isDeprecated = artifact.status === "deprecated";
|
|
1544
|
+
if (isDeprecated) continue;
|
|
1545
|
+
if (artifact.type === "REQUIREMENT") {
|
|
1546
|
+
const hasImplementation = implementationsByReq.has(artifact.id);
|
|
1547
|
+
const hasTest = testsByReq.has(artifact.id);
|
|
1548
|
+
if (!hasImplementation) {
|
|
1549
|
+
addIssue("requirementWithoutImplementation", `${artifact.id} lacks implementation`, artifact.file, artifact.id, artifact.status);
|
|
1550
|
+
}
|
|
1551
|
+
if (!hasTest) {
|
|
1552
|
+
addIssue("requirementWithoutTest", `${artifact.id} has no tests linked`, artifact.file, artifact.id, artifact.status);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
if (artifact.type === "CODE_ENTITY") {
|
|
1556
|
+
const hasReq = reqsByCode.has(artifact.id);
|
|
1557
|
+
if (!hasReq) {
|
|
1558
|
+
addIssue("codeWithoutRequirement", `${artifact.id} has no requirement associated`, artifact.file, artifact.id, artifact.status);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (artifact.type === "TEST") {
|
|
1562
|
+
const hasReq = reqsByTest.has(artifact.id);
|
|
1563
|
+
if (!hasReq) {
|
|
1564
|
+
addIssue("orphanArtifact", `${artifact.id} is a test without associated requirement`, artifact.file, artifact.id, artifact.status);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (isClosed) {
|
|
1568
|
+
const outgoing = data.relations.filter((r) => r.from === artifact.id);
|
|
1569
|
+
for (const rel of outgoing) {
|
|
1570
|
+
const targetArts = artifactMap.get(rel.to);
|
|
1571
|
+
if (targetArts && targetArts[0]) {
|
|
1572
|
+
const targetStatus = targetArts[0].status;
|
|
1573
|
+
if (targetStatus === "draft" || targetStatus === "in_progress") {
|
|
1574
|
+
if (rel.type !== "RELATES_TO" && rel.type !== "DOCUMENTS" && rel.type !== "JUSTIFIES") {
|
|
1575
|
+
addIssue("closedArtifactWithPendingRelations", `${artifact.id} is closed but links to ${targetStatus} artifact ${rel.to} via ${rel.type}`, artifact.file, artifact.id, artifact.status);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
if (!artifact.ownership || Object.keys(artifact.ownership).length === 0) {
|
|
1581
|
+
addIssue("missingOwnership", `${artifact.id} is closed but has no ownership defined`, artifact.file, artifact.id, artifact.status);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (artifact.type === "API" && (!artifact.ownership || Object.keys(artifact.ownership).length === 0)) {
|
|
1585
|
+
addIssue("missingOwnership", `API ${artifact.id} should have ownership defined`, artifact.file, artifact.id, artifact.status);
|
|
1586
|
+
}
|
|
1587
|
+
if (artifact.layer === "BUSINESS") {
|
|
1588
|
+
const outgoing = data.relations.filter((r) => r.from === artifact.id);
|
|
1589
|
+
for (const rel of outgoing) {
|
|
1590
|
+
const relSemantics = rel.category;
|
|
1591
|
+
if (relSemantics === "OPERATIONAL") {
|
|
1592
|
+
addIssue("invalidLayerRelation", `Business layer artifact ${artifact.id} should not have OPERATIONAL relations (${rel.type})`, artifact.file, artifact.id, artifact.status);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return issues;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// scripts/lint/lint-profiles.ts
|
|
1601
|
+
var defaultProfiles = {
|
|
1602
|
+
feature: {
|
|
1603
|
+
duplicateId: "error",
|
|
1604
|
+
invalidYaml: "error",
|
|
1605
|
+
brokenRelation: "error",
|
|
1606
|
+
missingRequiredFields: "warning",
|
|
1607
|
+
requirementWithoutImplementation: "info",
|
|
1608
|
+
requirementWithoutTest: "info",
|
|
1609
|
+
codeWithoutRequirement: "info",
|
|
1610
|
+
closedArtifactWithPendingRelations: "warning",
|
|
1611
|
+
orphanArtifact: "info",
|
|
1612
|
+
invalidRelationType: "error",
|
|
1613
|
+
invalidArtifactType: "error",
|
|
1614
|
+
invalidLayerRelation: "info",
|
|
1615
|
+
missingOwnership: "info"
|
|
1616
|
+
},
|
|
1617
|
+
develop: {
|
|
1618
|
+
duplicateId: "error",
|
|
1619
|
+
invalidYaml: "error",
|
|
1620
|
+
brokenRelation: "error",
|
|
1621
|
+
missingRequiredFields: "error",
|
|
1622
|
+
requirementWithoutImplementation: "warning",
|
|
1623
|
+
requirementWithoutTest: "warning",
|
|
1624
|
+
codeWithoutRequirement: "warning",
|
|
1625
|
+
closedArtifactWithPendingRelations: "warning",
|
|
1626
|
+
orphanArtifact: "warning",
|
|
1627
|
+
invalidRelationType: "error",
|
|
1628
|
+
invalidArtifactType: "error",
|
|
1629
|
+
invalidLayerRelation: "warning",
|
|
1630
|
+
missingOwnership: "warning"
|
|
1631
|
+
},
|
|
1632
|
+
release: {
|
|
1633
|
+
duplicateId: "error",
|
|
1634
|
+
invalidYaml: "error",
|
|
1635
|
+
brokenRelation: "error",
|
|
1636
|
+
missingRequiredFields: "error",
|
|
1637
|
+
requirementWithoutImplementation: "error",
|
|
1638
|
+
requirementWithoutTest: "error",
|
|
1639
|
+
codeWithoutRequirement: "error",
|
|
1640
|
+
closedArtifactWithPendingRelations: "error",
|
|
1641
|
+
orphanArtifact: "error",
|
|
1642
|
+
invalidRelationType: "error",
|
|
1643
|
+
invalidArtifactType: "error",
|
|
1644
|
+
invalidLayerRelation: "error",
|
|
1645
|
+
missingOwnership: "error"
|
|
1646
|
+
}
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
// scripts/lint/lint-engine.ts
|
|
1650
|
+
function loadConfig(configPath) {
|
|
1651
|
+
const defaultConfig = {
|
|
1652
|
+
defaultProfile: "develop",
|
|
1653
|
+
failOnWarnings: false,
|
|
1654
|
+
profiles: {}
|
|
1655
|
+
};
|
|
1656
|
+
if (fs4.existsSync(configPath)) {
|
|
1657
|
+
try {
|
|
1658
|
+
const fileContent = fs4.readFileSync(configPath, "utf8");
|
|
1659
|
+
const parsed = yaml2.load(fileContent);
|
|
1660
|
+
if (parsed && parsed.lint) {
|
|
1661
|
+
return {
|
|
1662
|
+
...defaultConfig,
|
|
1663
|
+
...parsed.lint
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
} catch (e) {
|
|
1667
|
+
console.warn("\u26A0\uFE0F Could not parse openlag.config.yml, using defaults.");
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return defaultConfig;
|
|
1671
|
+
}
|
|
1672
|
+
function runLint(docsDir, configPath, profileName) {
|
|
1673
|
+
const config = loadConfig(configPath);
|
|
1674
|
+
const selectedProfileName = profileName || config.defaultProfile;
|
|
1675
|
+
const baseProfile = defaultProfiles[selectedProfileName] || defaultProfiles["develop"];
|
|
1676
|
+
const customProfileOverrides = config.profiles[selectedProfileName] || {};
|
|
1677
|
+
const activeProfile = { ...baseProfile, ...customProfileOverrides };
|
|
1678
|
+
const parsedData = parseOpenLagDocs(docsDir);
|
|
1679
|
+
const issues = runLintRules(parsedData, activeProfile);
|
|
1680
|
+
const summary = {
|
|
1681
|
+
errors: 0,
|
|
1682
|
+
warnings: 0,
|
|
1683
|
+
info: 0
|
|
1684
|
+
};
|
|
1685
|
+
for (const issue of issues) {
|
|
1686
|
+
if (issue.severity === "error") summary.errors++;
|
|
1687
|
+
if (issue.severity === "warning") summary.warnings++;
|
|
1688
|
+
if (issue.severity === "info") summary.info++;
|
|
1689
|
+
}
|
|
1690
|
+
return {
|
|
1691
|
+
profile: selectedProfileName,
|
|
1692
|
+
summary,
|
|
1693
|
+
issues
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
function printHumanReport(report) {
|
|
1697
|
+
console.log(`
|
|
1698
|
+
` + chalk5.bold(`OpenLAG Lint Report`));
|
|
1699
|
+
console.log(chalk5.dim(`Profile: ${report.profile}
|
|
1700
|
+
`));
|
|
1701
|
+
if (report.issues.length === 0) {
|
|
1702
|
+
console.log(chalk5.green(`\u2705 No issues found!
|
|
1703
|
+
`));
|
|
1704
|
+
} else {
|
|
1705
|
+
for (const issue of report.issues) {
|
|
1706
|
+
const sevColor = issue.severity === "error" ? chalk5.red : issue.severity === "warning" ? chalk5.yellow : chalk5.cyan;
|
|
1707
|
+
const severityLabel = issue.severity.toUpperCase().padEnd(7);
|
|
1708
|
+
const ruleLabel = issue.rule.padEnd(30);
|
|
1709
|
+
console.log(`${sevColor(severityLabel)} ${chalk5.dim(ruleLabel)} ${issue.message}`);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
console.log(`
|
|
1713
|
+
` + chalk5.bold(`Summary:`));
|
|
1714
|
+
console.log(chalk5.red(`Errors: ${report.summary.errors}`));
|
|
1715
|
+
console.log(chalk5.yellow(`Warnings: ${report.summary.warnings}`));
|
|
1716
|
+
console.log(chalk5.cyan(`Info: ${report.summary.info}
|
|
1717
|
+
`));
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// scripts/cli/lint.ts
|
|
1721
|
+
function lintDocs(profile, jsonOutput = false, strictWarnings = false) {
|
|
1722
|
+
const docsDir = path7.join(process.cwd(), "docs");
|
|
1723
|
+
const configPath = path7.join(process.cwd(), "openlag.config.yml");
|
|
1724
|
+
const config = loadConfig(configPath);
|
|
1725
|
+
const report = runLint(docsDir, configPath, profile);
|
|
1726
|
+
if (jsonOutput) {
|
|
1727
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1728
|
+
} else {
|
|
1729
|
+
printHumanReport(report);
|
|
1730
|
+
}
|
|
1731
|
+
const failOnWarnings = strictWarnings || config.failOnWarnings;
|
|
1732
|
+
let hasErrors = report.summary.errors > 0;
|
|
1733
|
+
if (failOnWarnings && report.summary.warnings > 0) {
|
|
1734
|
+
hasErrors = true;
|
|
1735
|
+
}
|
|
1736
|
+
if (hasErrors) {
|
|
1737
|
+
process.exit(1);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
// scripts/cli/openlag.ts
|
|
1742
|
+
var program = new Command();
|
|
1743
|
+
var packageRoot = path8.resolve(path8.dirname(fileURLToPath3(import.meta.url)), "../..");
|
|
1744
|
+
function runVitePreview() {
|
|
1745
|
+
const viteBin = resolveViteBin(import.meta.url);
|
|
1746
|
+
execFileSync2(process.execPath, [viteBin, "preview", "--outDir", path8.join(process.cwd(), "dist")], {
|
|
1747
|
+
cwd: packageRoot,
|
|
1748
|
+
env: {
|
|
1749
|
+
...process.env,
|
|
1750
|
+
OPENLAG_PROJECT_ROOT: process.cwd()
|
|
1751
|
+
},
|
|
1752
|
+
stdio: "inherit"
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
program.name("openlag").description("Architecture as Code traceability graph generator").version("0.1.0");
|
|
1756
|
+
program.command("init").description("Initialize a new OpenLAG project").option("-n, --name <name>", "Project name").option("-d, --desc <description>", "Project description").option("--all", "Include optional synthetic relations").action((options) => {
|
|
1757
|
+
initProject(options.name, options.desc, options.all);
|
|
1758
|
+
});
|
|
1759
|
+
program.command("generate").description("Generate static graph data from markdown docs").option("-w, --watch", "Watch mode").action((options) => {
|
|
1760
|
+
const docsDir = path8.join(process.cwd(), "docs");
|
|
1761
|
+
const outputDir = path8.join(process.cwd(), "public");
|
|
1762
|
+
if (options.watch) {
|
|
1763
|
+
watchData(docsDir, outputDir);
|
|
1764
|
+
} else {
|
|
1765
|
+
generateData(docsDir, outputDir);
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
program.command("dev").description("Start development server with live data refresh").action(() => {
|
|
1769
|
+
runDevServer();
|
|
1770
|
+
});
|
|
1771
|
+
program.command("build").description("Build the OpenLAG portal for production").action(() => {
|
|
1772
|
+
buildPortal();
|
|
1773
|
+
});
|
|
1774
|
+
program.command("lint").description("Validate architecture documentation").option("-p, --profile <profile>", "Lint profile (develop, feature, release)", "develop").option("--json", "Output report in JSON format").option("--strict", "Fail on warnings").action((options) => {
|
|
1775
|
+
lintDocs(options.profile, options.json, options.strict);
|
|
1776
|
+
});
|
|
1777
|
+
program.command("preview").description("Preview the production build").action(() => {
|
|
1778
|
+
runVitePreview();
|
|
1779
|
+
});
|
|
1780
|
+
program.command("check").description("Generate graph data and validate architecture documentation").option("-p, --profile <profile>", "Lint profile (develop, feature, release)", "develop").option("--strict", "Fail on warnings").action((options) => {
|
|
1781
|
+
console.log(chalk6.blue("Running OpenLAG checks..."));
|
|
1782
|
+
const docsDir = path8.join(process.cwd(), "docs");
|
|
1783
|
+
const outputDir = path8.join(process.cwd(), "public");
|
|
1784
|
+
try {
|
|
1785
|
+
generateData(docsDir, outputDir);
|
|
1786
|
+
lintDocs(options.profile, false, options.strict);
|
|
1787
|
+
console.log(chalk6.green("\nOpenLAG checks passed."));
|
|
1788
|
+
} catch {
|
|
1789
|
+
console.error(chalk6.red("\nOpenLAG checks failed."));
|
|
1790
|
+
process.exit(1);
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
program.parse();
|