@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
package/dist/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>My OpenLAG Project | OpenLAG</title>
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-ByxguSZe.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-0RMQQ34p.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
14
|
+
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>My OpenLAG Project | OpenLAG</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
13
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@donartcha/openlag",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Architecture as Code traceability graph generator",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MPL-2.0",
|
|
7
|
+
"bin": {
|
|
8
|
+
"openlag": "./bin/openlag.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"scripts",
|
|
13
|
+
"src",
|
|
14
|
+
"dist",
|
|
15
|
+
"index.html",
|
|
16
|
+
"vite.config.ts",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "tsx scripts/cli/openlag.ts dev",
|
|
22
|
+
"build": "npm run generate-relations && npm run build:web && npm run build:cli",
|
|
23
|
+
"build:web": "vite build",
|
|
24
|
+
"build:cli": "tsup scripts/cli/openlag.ts --format esm --platform node --target node18 --out-dir dist/cli --clean false",
|
|
25
|
+
"check": "npm run typecheck && npm run lint && npm run test && npm run pack:dry-run",
|
|
26
|
+
"generate": "tsx scripts/cli/openlag.ts generate",
|
|
27
|
+
"generate-relations": "tsx scripts/generate-relations.ts",
|
|
28
|
+
"lint": "eslint .",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"test": "node --import tsx --test tests/*.test.ts",
|
|
31
|
+
"test:watch": "node --import tsx --test --watch tests/*.test.ts",
|
|
32
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
33
|
+
"clean": "rimraf dist public/graph-data.json",
|
|
34
|
+
"prepack": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@tailwindcss/vite": "^4.1.14",
|
|
38
|
+
"@vitejs/plugin-react": "^5.0.4",
|
|
39
|
+
"@xyflow/react": "^12.10.2",
|
|
40
|
+
"chalk": "^5.4.1",
|
|
41
|
+
"chokidar": "^5.0.0",
|
|
42
|
+
"clsx": "^2.1.1",
|
|
43
|
+
"commander": "^14.0.3",
|
|
44
|
+
"dagre": "^0.8.5",
|
|
45
|
+
"gray-matter": "^4.0.3",
|
|
46
|
+
"html-to-image": "^1.11.13",
|
|
47
|
+
"html2canvas": "^1.4.1",
|
|
48
|
+
"js-yaml": "^4.1.1",
|
|
49
|
+
"jspdf": "^4.2.1",
|
|
50
|
+
"lucide-react": "^0.546.0",
|
|
51
|
+
"mermaid": "^11.15.0",
|
|
52
|
+
"motion": "^12.23.24",
|
|
53
|
+
"ora": "^8.2.0",
|
|
54
|
+
"react": "^19.0.1",
|
|
55
|
+
"react-dom": "^19.0.1",
|
|
56
|
+
"react-markdown": "^10.1.0",
|
|
57
|
+
"tailwind-merge": "^3.6.0",
|
|
58
|
+
"vite": "^6.2.3",
|
|
59
|
+
"zod": "^4.4.3",
|
|
60
|
+
"zustand": "^5.0.13"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@eslint/js": "^10.0.1",
|
|
64
|
+
"@types/dagre": "^0.7.54",
|
|
65
|
+
"@types/js-yaml": "^4.0.9",
|
|
66
|
+
"@types/node": "^22.14.0",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
|
68
|
+
"@typescript-eslint/parser": "^8.59.3",
|
|
69
|
+
"autoprefixer": "^10.4.21",
|
|
70
|
+
"dotenv": "^17.2.3",
|
|
71
|
+
"esbuild": "^0.25.0",
|
|
72
|
+
"eslint": "^10.4.0",
|
|
73
|
+
"globals": "^17.6.0",
|
|
74
|
+
"rimraf": "^6.1.3",
|
|
75
|
+
"tailwindcss": "^4.1.14",
|
|
76
|
+
"tsup": "^8.5.1",
|
|
77
|
+
"tsx": "^4.21.0",
|
|
78
|
+
"typescript": "~5.8.2",
|
|
79
|
+
"vitest": "^4.1.6"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=18"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
import { generateData } from './generate.js';
|
|
8
|
+
import { resolveViteBin } from './vite-bin.js';
|
|
9
|
+
|
|
10
|
+
export function buildPortal() {
|
|
11
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
12
|
+
const viteBin = resolveViteBin(import.meta.url);
|
|
13
|
+
const viteConfig = path.join(packageRoot, 'vite.config.ts');
|
|
14
|
+
const docsDir = path.join(process.cwd(), 'docs');
|
|
15
|
+
const outputDir = path.join(process.cwd(), 'public');
|
|
16
|
+
|
|
17
|
+
generateData(docsDir, outputDir);
|
|
18
|
+
|
|
19
|
+
console.log(chalk.blue('Building OpenLAG portal...'));
|
|
20
|
+
try {
|
|
21
|
+
execFileSync(process.execPath, [viteBin, 'build', '--config', viteConfig], {
|
|
22
|
+
cwd: packageRoot,
|
|
23
|
+
env: {
|
|
24
|
+
...process.env,
|
|
25
|
+
OPENLAG_PROJECT_ROOT: process.cwd(),
|
|
26
|
+
},
|
|
27
|
+
stdio: 'inherit',
|
|
28
|
+
});
|
|
29
|
+
console.log(chalk.green('Portal build complete.'));
|
|
30
|
+
} catch {
|
|
31
|
+
console.error(chalk.red('Build failed.'));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
import { generateData, watchData } from './generate.js';
|
|
8
|
+
import { resolveViteBin } from './vite-bin.js';
|
|
9
|
+
|
|
10
|
+
export function runDevServer() {
|
|
11
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
12
|
+
const viteBin = resolveViteBin(import.meta.url);
|
|
13
|
+
const viteConfig = path.join(packageRoot, 'vite.config.ts');
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
const docsDir = path.join(projectRoot, 'docs');
|
|
16
|
+
const outputDir = path.join(projectRoot, 'public');
|
|
17
|
+
|
|
18
|
+
generateData(docsDir, outputDir);
|
|
19
|
+
watchData(docsDir, outputDir);
|
|
20
|
+
|
|
21
|
+
console.log(chalk.blue('Starting OpenLAG portal dev server...'));
|
|
22
|
+
|
|
23
|
+
const vite = spawn(process.execPath, [viteBin, '--config', viteConfig], {
|
|
24
|
+
cwd: packageRoot,
|
|
25
|
+
env: {
|
|
26
|
+
...process.env,
|
|
27
|
+
OPENLAG_PROJECT_ROOT: projectRoot,
|
|
28
|
+
},
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
vite.on('close', (code) => {
|
|
33
|
+
process.exit(code || 0);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { parseOpenLagDocs } from "../core/parser.js";
|
|
4
|
+
import { Version, Change, SystemVersion, GraphSnapshot } from "../../src/types.js";
|
|
5
|
+
import chokidar from "chokidar";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
|
|
8
|
+
interface StaticState {
|
|
9
|
+
versions: Version[];
|
|
10
|
+
systemVersions: SystemVersion[];
|
|
11
|
+
graphs: Record<string, GraphSnapshot>;
|
|
12
|
+
changes: Change[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isDescendant(currentVersionId: string, artifactVersionId: string, versions: Version[]): boolean {
|
|
16
|
+
const artifactVersion = versions.find(v => v.id === artifactVersionId);
|
|
17
|
+
if (!artifactVersion) return false;
|
|
18
|
+
|
|
19
|
+
let temp = versions.find(v => v.id === currentVersionId);
|
|
20
|
+
let depth = 0;
|
|
21
|
+
while (temp && temp.parentVersion) {
|
|
22
|
+
depth++;
|
|
23
|
+
if (depth > 50) return false;
|
|
24
|
+
if (temp.parentVersion === artifactVersionId) return true;
|
|
25
|
+
temp = versions.find(v => v.id === temp!.parentVersion);
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function generateData(docsDir: string, outputDir: string, silent = false) {
|
|
31
|
+
if (!silent) console.log(chalk.blue("🚀 Generating OpenLAG Static Data..."));
|
|
32
|
+
|
|
33
|
+
const parsedData = parseOpenLagDocs(docsDir);
|
|
34
|
+
|
|
35
|
+
const state: StaticState = {
|
|
36
|
+
versions: parsedData.versions,
|
|
37
|
+
systemVersions: parsedData.systemVersions,
|
|
38
|
+
graphs: {},
|
|
39
|
+
changes: parsedData.changes
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const allArtifacts = parsedData.artifacts;
|
|
43
|
+
const allRelations = parsedData.relations;
|
|
44
|
+
|
|
45
|
+
state.versions.forEach(v => {
|
|
46
|
+
state.graphs[v.id] = {
|
|
47
|
+
artifacts: allArtifacts.filter(a => a.type === 'SYSTEM_VERSION' || a.type === 'VERSION' || a.version === v.id || isDescendant(v.id, a.version, state.versions)),
|
|
48
|
+
relations: allRelations.filter(r => {
|
|
49
|
+
const fromArt = allArtifacts.find(a => a.id === r.from);
|
|
50
|
+
return fromArt && (fromArt.type === 'SYSTEM_VERSION' || fromArt.type === 'VERSION' || fromArt.version === v.id || isDescendant(v.id, fromArt.version, state.versions));
|
|
51
|
+
})
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
path.join(outputDir, 'graph-data.json'),
|
|
59
|
+
JSON.stringify(state, null, 2)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!silent) console.log(chalk.green("✅ Data generated at"), chalk.cyan(path.join(outputDir, 'graph-data.json')));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function watchData(docsDir: string, outputDir: string) {
|
|
66
|
+
console.log(chalk.yellow("👀 Watching for changes in"), chalk.cyan(docsDir));
|
|
67
|
+
|
|
68
|
+
const watcher = chokidar.watch(docsDir, {
|
|
69
|
+
ignoreInitial: true,
|
|
70
|
+
persistent: true
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const runCleanly = () => {
|
|
74
|
+
try {
|
|
75
|
+
generateData(docsDir, outputDir, true);
|
|
76
|
+
console.log(chalk.dim(`[${new Date().toLocaleTimeString()}] `) + chalk.green("Regenerated graph-data.json"));
|
|
77
|
+
} catch (e) {
|
|
78
|
+
console.error(chalk.red("❌ Error during regeneration:"), e);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Debounce to avoid multiple rapid regenerations
|
|
83
|
+
let timeout: NodeJS.Timeout;
|
|
84
|
+
const debouncedRun = () => {
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
timeout = setTimeout(runCleanly, 300);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
watcher.on('all', (event, path) => {
|
|
90
|
+
debouncedRun();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* OpenLAG Project Initializer
|
|
7
|
+
* Purpose: Configures the OpenLAG portal for a specific project.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export async function initProject(projectName?: string, projectDesc?: string, includeAllRelations?: boolean) {
|
|
11
|
+
const name = projectName || process.env.PROJECT_NAME || 'My OpenLAG Project';
|
|
12
|
+
const desc = projectDesc || process.env.PROJECT_DESCRIPTION || 'Living Architecture documentation for my system.';
|
|
13
|
+
|
|
14
|
+
const ROOT_DIR = process.cwd();
|
|
15
|
+
|
|
16
|
+
console.log(chalk.blue(`🛠️ Initializing OpenLAG for: `) + chalk.bold(name));
|
|
17
|
+
|
|
18
|
+
// 1. Update metadata.json
|
|
19
|
+
const metadataPath = path.join(ROOT_DIR, 'metadata.json');
|
|
20
|
+
if (fs.existsSync(metadataPath)) {
|
|
21
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
22
|
+
metadata.name = name;
|
|
23
|
+
metadata.description = desc;
|
|
24
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
25
|
+
console.log(chalk.green('✅ Updated metadata.json'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Update index.html title
|
|
29
|
+
const htmlPath = path.join(ROOT_DIR, 'index.html');
|
|
30
|
+
if (fs.existsSync(htmlPath)) {
|
|
31
|
+
let html = fs.readFileSync(htmlPath, 'utf-8');
|
|
32
|
+
html = html.replace(/<title>.*?<\/title>/, `<title>${name} | OpenLAG</title>`);
|
|
33
|
+
fs.writeFileSync(htmlPath, html);
|
|
34
|
+
console.log(chalk.green('✅ Updated index.html title'));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. Initialize /docs if empty or missing
|
|
38
|
+
const docsDir = path.join(ROOT_DIR, 'docs');
|
|
39
|
+
if (!fs.existsSync(docsDir)) {
|
|
40
|
+
fs.mkdirSync(docsDir);
|
|
41
|
+
console.log(chalk.green('✅ Created /docs directory'));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 3.1 Initialize /docs/relations with Mandatory Core Relations
|
|
45
|
+
const relationsDir = path.join(docsDir, 'relations');
|
|
46
|
+
if (!fs.existsSync(relationsDir)) {
|
|
47
|
+
fs.mkdirSync(relationsDir);
|
|
48
|
+
console.log(chalk.green('✅ Created /docs/relations directory'));
|
|
49
|
+
} else {
|
|
50
|
+
// Purge existing relations to ensure only mandatory ones exist on init
|
|
51
|
+
const files = fs.readdirSync(relationsDir);
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
if (file.endsWith('.yaml')) {
|
|
54
|
+
fs.unlinkSync(path.join(relationsDir, file));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const mandatoryRelations = [
|
|
60
|
+
{
|
|
61
|
+
name: 'IMPLEMENTS.yaml',
|
|
62
|
+
content: `relation: IMPLEMENTS
|
|
63
|
+
description: "Conecta implementación con necesidad funcional/técnica."
|
|
64
|
+
category: TRACEABILITY
|
|
65
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, SYSTEM_VERSION]
|
|
66
|
+
allowedTo: [REQUIREMENT, FEATURE, EPIC, DESIGN, USE_CASE, BUSINESS_RULE]
|
|
67
|
+
multiplicity:
|
|
68
|
+
from: many
|
|
69
|
+
to: many
|
|
70
|
+
validation:
|
|
71
|
+
severity: error`
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'TESTS.yaml',
|
|
75
|
+
content: `relation: TESTS
|
|
76
|
+
description: "Conecta tests con comportamiento validado."
|
|
77
|
+
category: TRACEABILITY
|
|
78
|
+
allowedFrom: [TEST_CASE, TEST]
|
|
79
|
+
allowedTo: [REQUIREMENT, FEATURE, EPIC, CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, BUG, INCIDENT]
|
|
80
|
+
multiplicity:
|
|
81
|
+
from: many
|
|
82
|
+
to: many
|
|
83
|
+
validation:
|
|
84
|
+
severity: error`
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'REFINES.yaml',
|
|
88
|
+
content: `relation: REFINES
|
|
89
|
+
description: "Descompone artefactos en otros más concretos."
|
|
90
|
+
category: TRACEABILITY
|
|
91
|
+
allowedFrom: [FEATURE, REQUIREMENT, BUG, RISK, DESIGN]
|
|
92
|
+
allowedTo: [EPIC, FEATURE, PROJECT, BUSINESS_RULE, REQUIREMENT, DESIGN]
|
|
93
|
+
multiplicity:
|
|
94
|
+
from: many
|
|
95
|
+
to: many
|
|
96
|
+
validation:
|
|
97
|
+
severity: warn`
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'FIXES.yaml',
|
|
101
|
+
content: `relation: FIXES
|
|
102
|
+
description: "Conecta correcciones con bugs o incidentes."
|
|
103
|
+
category: TRACEABILITY
|
|
104
|
+
allowedFrom: [CHANGE, CODE_ENTITY, COMPONENT, SYSTEM_VERSION]
|
|
105
|
+
allowedTo: [BUG, INCIDENT, RISK]
|
|
106
|
+
multiplicity:
|
|
107
|
+
from: many
|
|
108
|
+
to: many
|
|
109
|
+
validation:
|
|
110
|
+
severity: error`
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'DOCUMENTS.yaml',
|
|
114
|
+
content: `relation: DOCUMENTS
|
|
115
|
+
description: "Conecta documentación con el artefacto descrito."
|
|
116
|
+
category: SEMANTIC
|
|
117
|
+
allowedFrom: [DOCUMENTATION, GLOSSARY_TERM]
|
|
118
|
+
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]
|
|
119
|
+
multiplicity:
|
|
120
|
+
from: many
|
|
121
|
+
to: many
|
|
122
|
+
validation:
|
|
123
|
+
severity: info`
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'JUSTIFIES.yaml',
|
|
127
|
+
content: `relation: JUSTIFIES
|
|
128
|
+
description: "Conecta decisiones con aquello que justifican."
|
|
129
|
+
category: SEMANTIC
|
|
130
|
+
allowedFrom: [DECISION, BUSINESS_RULE, DOCUMENTATION, RISK]
|
|
131
|
+
allowedTo: [DESIGN, REQUIREMENT, FEATURE, EPIC, PROJECT, CODE_ENTITY, COMPONENT, INFRASTRUCTURE, DEPLOYMENT, MAINTENANCE]
|
|
132
|
+
multiplicity:
|
|
133
|
+
from: many
|
|
134
|
+
to: many
|
|
135
|
+
validation:
|
|
136
|
+
severity: warn`
|
|
137
|
+
}
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
mandatoryRelations.forEach(rel => {
|
|
141
|
+
fs.writeFileSync(path.join(relationsDir, rel.name), rel.content);
|
|
142
|
+
});
|
|
143
|
+
console.log(chalk.green('✅ Created Mandatory Core Relations (IMPLEMENTS, TESTS, REFINES, FIXES, DOCUMENTS, JUSTIFIES)'));
|
|
144
|
+
|
|
145
|
+
if (includeAllRelations) {
|
|
146
|
+
const optionalRelations = [
|
|
147
|
+
{
|
|
148
|
+
name: 'DEPENDS_ON.yaml',
|
|
149
|
+
content: `relation: DEPENDS_ON
|
|
150
|
+
description: "Acoplamiento estático arquitectónico o de empaquetado."
|
|
151
|
+
category: STRUCTURAL
|
|
152
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, LIBRARY, VERSION, SYSTEM_VERSION]
|
|
153
|
+
allowedTo: [CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, LIBRARY, VERSION, SYSTEM_VERSION]
|
|
154
|
+
multiplicity:
|
|
155
|
+
from: many
|
|
156
|
+
to: many
|
|
157
|
+
validation:
|
|
158
|
+
severity: warn`
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'USES.yaml',
|
|
162
|
+
content: `relation: USES
|
|
163
|
+
description: "Llamada funcional, invocación o flujo en tiempo de ejecución."
|
|
164
|
+
category: OPERATIONAL
|
|
165
|
+
allowedFrom: [CODE_ENTITY, COMPONENT, API, FEATURE, USE_CASE]
|
|
166
|
+
allowedTo: [CODE_ENTITY, COMPONENT, API, SYSTEM_VERSION]
|
|
167
|
+
multiplicity:
|
|
168
|
+
from: many
|
|
169
|
+
to: many
|
|
170
|
+
validation:
|
|
171
|
+
severity: warn`
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'DEPLOYS.yaml',
|
|
175
|
+
content: `relation: DEPLOYS
|
|
176
|
+
description: "Instanciación de componentes o release en infraestructura."
|
|
177
|
+
category: OPERATIONAL
|
|
178
|
+
allowedFrom: [DEPLOYMENT, PIPELINE, SYSTEM_VERSION]
|
|
179
|
+
allowedTo: [COMPONENT, INFRASTRUCTURE, ENVIRONMENT, DATABASE_ENTITY]
|
|
180
|
+
multiplicity:
|
|
181
|
+
from: many
|
|
182
|
+
to: many
|
|
183
|
+
validation:
|
|
184
|
+
severity: error`
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'MONITORS.yaml',
|
|
188
|
+
content: `relation: MONITORS
|
|
189
|
+
description: "Relación de observabilidad."
|
|
190
|
+
category: OPERATIONAL
|
|
191
|
+
allowedFrom: [MONITORING, CHECK]
|
|
192
|
+
allowedTo: [COMPONENT, INFRASTRUCTURE, DEPLOYMENT, DATABASE_ENTITY, API]
|
|
193
|
+
multiplicity:
|
|
194
|
+
from: many
|
|
195
|
+
to: many
|
|
196
|
+
validation:
|
|
197
|
+
severity: info`
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'IMPACTS.yaml',
|
|
201
|
+
content: `relation: IMPACTS
|
|
202
|
+
description: "Descripción de posibles efectos colaterales."
|
|
203
|
+
category: SEMANTIC
|
|
204
|
+
allowedFrom: [CHANGE, RISK, BUG, INCIDENT, DECISION]
|
|
205
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, DESIGN, CODE_ENTITY, COMPONENT, API, DATABASE_ENTITY, SYSTEM_VERSION]
|
|
206
|
+
multiplicity:
|
|
207
|
+
from: many
|
|
208
|
+
to: many
|
|
209
|
+
validation:
|
|
210
|
+
severity: warn`
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'BLOCKS.yaml',
|
|
214
|
+
content: `relation: BLOCKS
|
|
215
|
+
description: "Descripción de impedimentos directos."
|
|
216
|
+
category: DEPENDENCY
|
|
217
|
+
allowedFrom: [BUG, RISK, INCIDENT, CHANGE, DECISION, REQUIREMENT, FEATURE, EPIC]
|
|
218
|
+
allowedTo: [PROJECT, EPIC, FEATURE, REQUIREMENT, BUG, CHANGE, DEPLOYMENT, DEPLOYMENT]
|
|
219
|
+
multiplicity:
|
|
220
|
+
from: many
|
|
221
|
+
to: many
|
|
222
|
+
validation:
|
|
223
|
+
severity: error`
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'BREAKS.yaml',
|
|
227
|
+
content: `relation: BREAKS
|
|
228
|
+
description: "Averías o rupturas confirmadas."
|
|
229
|
+
category: OPERATIONAL
|
|
230
|
+
allowedFrom: [CHANGE, CODE_ENTITY, COMPONENT, SYSTEM_VERSION]
|
|
231
|
+
allowedTo: [TEST_CASE, TEST, API, COMPONENT, REQUIREMENT, FEATURE]
|
|
232
|
+
multiplicity:
|
|
233
|
+
from: many
|
|
234
|
+
to: many
|
|
235
|
+
validation:
|
|
236
|
+
severity: error`
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'REPLACES.yaml',
|
|
240
|
+
content: `relation: REPLACES
|
|
241
|
+
description: "Evolución e histórico, deprecando versiones anteriores."
|
|
242
|
+
category: EVOLUTIONARY
|
|
243
|
+
allowedFrom: [COMPONENT, API, SYSTEM_VERSION, VERSION, REQUIREMENT, FEATURE, DESIGN, DECISION]
|
|
244
|
+
allowedTo: [COMPONENT, API, SYSTEM_VERSION, VERSION, REQUIREMENT, FEATURE, DESIGN, DECISION]
|
|
245
|
+
multiplicity:
|
|
246
|
+
from: many
|
|
247
|
+
to: one
|
|
248
|
+
validation:
|
|
249
|
+
severity: info`
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: 'DERIVES_FROM.yaml',
|
|
253
|
+
content: `relation: DERIVES_FROM
|
|
254
|
+
description: "Evolución conceptual genérica."
|
|
255
|
+
category: SEMANTIC
|
|
256
|
+
allowedFrom: [REQUIREMENT, FEATURE, DESIGN, BUSINESS_RULE, DECISION, USE_CASE]
|
|
257
|
+
allowedTo: [REQUIREMENT, FEATURE, DESIGN, BUSINESS_RULE, DECISION, USE_CASE]
|
|
258
|
+
multiplicity:
|
|
259
|
+
from: many
|
|
260
|
+
to: many
|
|
261
|
+
validation:
|
|
262
|
+
severity: info`
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'VALIDATES.yaml',
|
|
266
|
+
content: `relation: VALIDATES
|
|
267
|
+
description: "Validación empírica/humana (QA Manual)."
|
|
268
|
+
category: TRACEABILITY
|
|
269
|
+
allowedFrom: [PROCESS, CHECK, DOCUMENTATION]
|
|
270
|
+
allowedTo: [REQUIREMENT, FEATURE, USE_CASE, DEPLOYMENT]
|
|
271
|
+
multiplicity:
|
|
272
|
+
from: many
|
|
273
|
+
to: many
|
|
274
|
+
validation:
|
|
275
|
+
severity: info`
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: 'DEFINES.yaml',
|
|
279
|
+
content: `relation: DEFINES
|
|
280
|
+
description: "Entidades que instauran glosarios o normas."
|
|
281
|
+
category: SEMANTIC
|
|
282
|
+
allowedFrom: [BUSINESS_RULE, DECISION, DOCUMENTATION, GLOSSARY_TERM]
|
|
283
|
+
allowedTo: [REQUIREMENT, FEATURE, DESIGN, PROJECT, COMPONENT, API, DATABASE_ENTITY]
|
|
284
|
+
multiplicity:
|
|
285
|
+
from: many
|
|
286
|
+
to: many
|
|
287
|
+
validation:
|
|
288
|
+
severity: info`
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'CALLS.yaml',
|
|
292
|
+
content: `relation: CALLS
|
|
293
|
+
description: "Trazabilidad de invocación a nivel código."
|
|
294
|
+
category: STRUCTURAL
|
|
295
|
+
allowedFrom: [CODE_ENTITY]
|
|
296
|
+
allowedTo: [CODE_ENTITY, API]
|
|
297
|
+
multiplicity:
|
|
298
|
+
from: many
|
|
299
|
+
to: many
|
|
300
|
+
validation:
|
|
301
|
+
severity: warn`
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: 'IMPORTS.yaml',
|
|
305
|
+
content: `relation: IMPORTS
|
|
306
|
+
description: "Trazabilidad de importación estática a nivel de código."
|
|
307
|
+
category: STRUCTURAL
|
|
308
|
+
allowedFrom: [CODE_ENTITY]
|
|
309
|
+
allowedTo: [CODE_ENTITY, LIBRARY]
|
|
310
|
+
multiplicity:
|
|
311
|
+
from: many
|
|
312
|
+
to: many
|
|
313
|
+
validation:
|
|
314
|
+
severity: warn`
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'RELATES_TO.yaml',
|
|
318
|
+
content: `relation: RELATES_TO
|
|
319
|
+
description: |
|
|
320
|
+
Uso genérico propenso a polución semántica. (DISCOURAGED)
|
|
321
|
+
Para mantener un grafo limpio, solo úsala aportando rationale explícito:
|
|
322
|
+
|
|
323
|
+
relations:
|
|
324
|
+
- to: mi-otro-artefacto
|
|
325
|
+
type: RELATES_TO
|
|
326
|
+
rationale: "No encaja con USES o DEPENDS_ON debido al contexto X."
|
|
327
|
+
category: SEMANTIC
|
|
328
|
+
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]
|
|
329
|
+
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]
|
|
330
|
+
multiplicity:
|
|
331
|
+
from: many
|
|
332
|
+
to: many
|
|
333
|
+
validation:
|
|
334
|
+
severity: warn`
|
|
335
|
+
}
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
optionalRelations.forEach(rel => {
|
|
339
|
+
fs.writeFileSync(path.join(relationsDir, rel.name), rel.content);
|
|
340
|
+
});
|
|
341
|
+
console.log(chalk.green('✅ Created Official Optional Relations'));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
const versionsDir = path.join(docsDir, 'versions');
|
|
346
|
+
if (!fs.existsSync(versionsDir)) {
|
|
347
|
+
fs.mkdirSync(versionsDir);
|
|
348
|
+
console.log(chalk.green('✅ Created /docs/versions directory'));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const versionPath = path.join(versionsDir, 'v-1.0.0.md');
|
|
352
|
+
if (!fs.existsSync(versionPath)) {
|
|
353
|
+
const versionsContent = `---
|
|
354
|
+
id: v-1.0.0
|
|
355
|
+
type: VERSION
|
|
356
|
+
name: "Baseline"
|
|
357
|
+
timestamp: "${new Date().toISOString().split('T')[0]}"
|
|
358
|
+
parentVersion: null
|
|
359
|
+
layer: DOCUMENTATION
|
|
360
|
+
title: "Project Release v1.0.0"
|
|
361
|
+
description: "Initial release"
|
|
362
|
+
ownership:
|
|
363
|
+
owner: architecture
|
|
364
|
+
team: architecture
|
|
365
|
+
relations: []
|
|
366
|
+
---
|
|
367
|
+
`;
|
|
368
|
+
fs.writeFileSync(versionPath, versionsContent);
|
|
369
|
+
console.log(chalk.green('✅ Created initial docs/versions/v-1.0.0.md'));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const componentsVersionsPath = path.join(versionsDir, 'sys-core-1.0.md');
|
|
373
|
+
if (!fs.existsSync(componentsVersionsPath)) {
|
|
374
|
+
const cvContent = `---
|
|
375
|
+
id: sys-core-1.0
|
|
376
|
+
type: SYSTEM_VERSION
|
|
377
|
+
component: "Core System"
|
|
378
|
+
version: "1.0.0"
|
|
379
|
+
releaseDate: "${new Date().toISOString().split('T')[0]}"
|
|
380
|
+
layer: OPERATIONS
|
|
381
|
+
title: "Core System v1.0"
|
|
382
|
+
description: "Base framework."
|
|
383
|
+
ownership:
|
|
384
|
+
owner: architecture
|
|
385
|
+
team: architecture
|
|
386
|
+
relations: []
|
|
387
|
+
---
|
|
388
|
+
`;
|
|
389
|
+
fs.writeFileSync(componentsVersionsPath, cvContent);
|
|
390
|
+
console.log(chalk.green('✅ Created initial docs/versions/sys-core-1.0.md'));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// 4. Create a sample doc if empty
|
|
394
|
+
const architectureDir = path.join(docsDir, 'architecture');
|
|
395
|
+
if (!fs.existsSync(architectureDir)) {
|
|
396
|
+
fs.mkdirSync(architectureDir);
|
|
397
|
+
console.log(chalk.green('✅ Created /docs/architecture directory'));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const sampleDocPath = path.join(architectureDir, 'architecture-overview.md');
|
|
401
|
+
if (!fs.existsSync(sampleDocPath)) {
|
|
402
|
+
const sampleContent = `---
|
|
403
|
+
id: arch-overview
|
|
404
|
+
type: DOCUMENT
|
|
405
|
+
status: draft
|
|
406
|
+
title: "Architecture Overview"
|
|
407
|
+
description: "High-level description of the system architecture."
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
# Architecture Overview
|
|
411
|
+
|
|
412
|
+
This document describes the high-level architecture of ${name}.
|
|
413
|
+
|
|
414
|
+
## Key Principles
|
|
415
|
+
- Traceability first.
|
|
416
|
+
- Documentation as Code.
|
|
417
|
+
- Graph-based relationships.
|
|
418
|
+
`;
|
|
419
|
+
fs.writeFileSync(sampleDocPath, sampleContent);
|
|
420
|
+
console.log(chalk.green('✅ Created initial docs/architecture/architecture-overview.md'));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.log('\n' + chalk.cyan('🚀 Project initialized successfully!'));
|
|
424
|
+
console.log('Next steps:');
|
|
425
|
+
console.log('1. Edit ' + chalk.cyan('docs/') + ' to reflect your architecture.');
|
|
426
|
+
console.log('2. Run ' + chalk.cyan('openlag dev') + ' to see your portal.');
|
|
427
|
+
}
|