kirograph 0.12.2 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -87
- package/dist/architecture/layers/index.js +9 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/architecture/layers/ocaml.js +105 -0
- package/dist/architecture/layers/ocaml.js.map +7 -0
- package/dist/architecture/layers/scala.js +120 -0
- package/dist/architecture/layers/scala.js.map +7 -0
- package/dist/architecture/layers/solidity.js +105 -0
- package/dist/architecture/layers/solidity.js.map +7 -0
- package/dist/architecture/layers/vue.js +111 -0
- package/dist/architecture/layers/vue.js.map +7 -0
- package/dist/architecture/manifest/elm.js +91 -0
- package/dist/architecture/manifest/elm.js.map +7 -0
- package/dist/architecture/manifest/index.js +13 -2
- package/dist/architecture/manifest/index.js.map +2 -2
- package/dist/architecture/manifest/ocaml.js +166 -0
- package/dist/architecture/manifest/ocaml.js.map +7 -0
- package/dist/architecture/manifest/scala.js +117 -0
- package/dist/architecture/manifest/scala.js.map +7 -0
- package/dist/bin/commands/caveman.js +12 -0
- package/dist/bin/commands/caveman.js.map +2 -2
- package/dist/bin/commands/help.js +6 -4
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/install.js +8 -2
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/serve.js +2 -2
- package/dist/bin/commands/serve.js.map +2 -2
- package/dist/bin/commands/uninit.js +65 -41
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/installer/cli-agent.js +5 -25
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.js +154 -0
- package/dist/bin/installer/common.js.map +7 -0
- package/dist/bin/installer/hooks.js +21 -1
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.js +99 -86
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +60 -0
- package/dist/bin/installer/instructions.js.map +7 -0
- package/dist/bin/installer/mcp.js +6 -36
- package/dist/bin/installer/mcp.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +79 -0
- package/dist/bin/installer/targets/claude.js.map +7 -0
- package/dist/bin/installer/targets/codex.js +77 -0
- package/dist/bin/installer/targets/codex.js.map +7 -0
- package/dist/bin/installer/targets/index.js +57 -0
- package/dist/bin/installer/targets/index.js.map +7 -0
- package/dist/bin/installer/targets/kiro.js +61 -0
- package/dist/bin/installer/targets/kiro.js.map +7 -0
- package/dist/bin/kirograph.js +1 -1
- package/dist/extraction/extractor.js +65 -2
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.js +22 -0
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.js +39 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/wasm/tree-sitter-hcl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-scss.wasm +0 -0
- package/dist/frameworks/amplify.js +175 -0
- package/dist/frameworks/amplify.js.map +7 -0
- package/dist/frameworks/angular.js +132 -0
- package/dist/frameworks/angular.js.map +7 -0
- package/dist/frameworks/ansible.js +151 -0
- package/dist/frameworks/ansible.js.map +7 -0
- package/dist/frameworks/cloudformation.js +148 -0
- package/dist/frameworks/cloudformation.js.map +7 -0
- package/dist/frameworks/docker.js +149 -0
- package/dist/frameworks/docker.js.map +7 -0
- package/dist/frameworks/iac.js +401 -0
- package/dist/frameworks/iac.js.map +7 -0
- package/dist/frameworks/index.js +81 -3
- package/dist/frameworks/index.js.map +3 -3
- package/dist/frameworks/kubernetes.js +176 -0
- package/dist/frameworks/kubernetes.js.map +7 -0
- package/dist/frameworks/pulumi.js +93 -0
- package/dist/frameworks/pulumi.js.map +7 -0
- package/dist/frameworks/scala.js +124 -0
- package/dist/frameworks/scala.js.map +7 -0
- package/dist/frameworks/solidity.js +93 -0
- package/dist/frameworks/solidity.js.map +7 -0
- package/dist/frameworks/terraform.js +278 -0
- package/dist/frameworks/terraform.js.map +7 -0
- package/dist/frameworks/vue.js +163 -0
- package/dist/frameworks/vue.js.map +7 -0
- package/dist/graph/queries.js +1 -1
- package/dist/graph/queries.js.map +1 -1
- package/dist/mcp/tool-names.js +48 -0
- package/dist/mcp/tool-names.js.map +7 -0
- package/dist/mcp/tools.js +3 -0
- package/dist/mcp/tools.js.map +2 -2
- package/dist/types.js.map +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var cloudformation_exports = {};
|
|
20
|
+
__export(cloudformation_exports, {
|
|
21
|
+
cloudformationResolver: () => cloudformationResolver
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(cloudformation_exports);
|
|
24
|
+
const cloudformationResolver = {
|
|
25
|
+
name: "cloudformation",
|
|
26
|
+
detect(context) {
|
|
27
|
+
for (const name of ["template.yaml", "template.yml", "template.json", "cloudformation.yaml", "cloudformation.yml"]) {
|
|
28
|
+
const content = context.readFile(name);
|
|
29
|
+
if (content && content.includes("AWSTemplateFormatVersion") && !content.includes("AWS::Serverless")) return true;
|
|
30
|
+
}
|
|
31
|
+
return context.getAllFiles().some(
|
|
32
|
+
(f) => (f.includes("cloudformation/") || f.includes("cfn/") || f.includes("stacks/")) && (f.endsWith(".yaml") || f.endsWith(".yml")) && !f.includes("node_modules")
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
resolve(ref, context) {
|
|
36
|
+
if (/^[A-Z][a-zA-Z0-9]+$/.test(ref.referenceName)) {
|
|
37
|
+
const id = resolveResource(ref.referenceName, context);
|
|
38
|
+
if (id) return { original: ref, targetNodeId: id, confidence: 0.85, resolvedBy: "framework" };
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
},
|
|
42
|
+
extractNodes(filePath, content) {
|
|
43
|
+
const nodes = [];
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
if (!content.includes("AWSTemplateFormatVersion") && !content.includes("Resources:")) return nodes;
|
|
46
|
+
const resourcePattern = /^\s{2}(\w+):\s*\n\s+Type:\s*['"]?([^\s'"]+)/gm;
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = resourcePattern.exec(content)) !== null) {
|
|
49
|
+
const line = content.slice(0, match.index).split("\n").length;
|
|
50
|
+
const logicalId = match[1];
|
|
51
|
+
const resourceType = match[2];
|
|
52
|
+
nodes.push({
|
|
53
|
+
id: `cfn:${filePath}:resource:${logicalId}:${line}`,
|
|
54
|
+
kind: "class",
|
|
55
|
+
name: logicalId,
|
|
56
|
+
qualifiedName: `${filePath}::${logicalId}`,
|
|
57
|
+
filePath,
|
|
58
|
+
startLine: line,
|
|
59
|
+
endLine: line,
|
|
60
|
+
startColumn: 0,
|
|
61
|
+
endColumn: match[0].length,
|
|
62
|
+
language: "yaml",
|
|
63
|
+
updatedAt: now,
|
|
64
|
+
signature: resourceType
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const paramSection = content.match(/^Parameters:\s*\n((?:\s{2}\w+:[\s\S]*?)(?=^\w|\Z))/m);
|
|
68
|
+
if (paramSection) {
|
|
69
|
+
const paramPattern = /^\s{2}(\w+):/gm;
|
|
70
|
+
let paramMatch;
|
|
71
|
+
const sectionStart = content.indexOf(paramSection[0]);
|
|
72
|
+
while ((paramMatch = paramPattern.exec(paramSection[1])) !== null) {
|
|
73
|
+
const line = content.slice(0, sectionStart + paramMatch.index).split("\n").length;
|
|
74
|
+
const paramName = paramMatch[1];
|
|
75
|
+
nodes.push({
|
|
76
|
+
id: `cfn:${filePath}:param:${paramName}:${line}`,
|
|
77
|
+
kind: "variable",
|
|
78
|
+
name: paramName,
|
|
79
|
+
qualifiedName: `${filePath}::param.${paramName}`,
|
|
80
|
+
filePath,
|
|
81
|
+
startLine: line,
|
|
82
|
+
endLine: line,
|
|
83
|
+
startColumn: 0,
|
|
84
|
+
endColumn: paramMatch[0].length,
|
|
85
|
+
language: "yaml",
|
|
86
|
+
updatedAt: now
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const outputSection = content.match(/^Outputs:\s*\n((?:\s{2}\w+:[\s\S]*?)(?=^\w|\Z))/m);
|
|
91
|
+
if (outputSection) {
|
|
92
|
+
const outputPattern = /^\s{2}(\w+):/gm;
|
|
93
|
+
let outputMatch;
|
|
94
|
+
const sectionStart = content.indexOf(outputSection[0]);
|
|
95
|
+
while ((outputMatch = outputPattern.exec(outputSection[1])) !== null) {
|
|
96
|
+
const line = content.slice(0, sectionStart + outputMatch.index).split("\n").length;
|
|
97
|
+
const outputName = outputMatch[1];
|
|
98
|
+
nodes.push({
|
|
99
|
+
id: `cfn:${filePath}:output:${outputName}:${line}`,
|
|
100
|
+
kind: "variable",
|
|
101
|
+
name: `output.${outputName}`,
|
|
102
|
+
qualifiedName: `${filePath}::output.${outputName}`,
|
|
103
|
+
filePath,
|
|
104
|
+
startLine: line,
|
|
105
|
+
endLine: line,
|
|
106
|
+
startColumn: 0,
|
|
107
|
+
endColumn: outputMatch[0].length,
|
|
108
|
+
language: "yaml",
|
|
109
|
+
updatedAt: now,
|
|
110
|
+
isExported: true
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const apiMethodPattern = /HttpMethod:\s*['"]?(\w+)['"]?/g;
|
|
115
|
+
while ((match = apiMethodPattern.exec(content)) !== null) {
|
|
116
|
+
if (!content.slice(Math.max(0, match.index - 200), match.index).includes("ApiGateway")) continue;
|
|
117
|
+
const line = content.slice(0, match.index).split("\n").length;
|
|
118
|
+
const method = match[1].toUpperCase();
|
|
119
|
+
nodes.push({
|
|
120
|
+
id: `route:${filePath}:${method}:cfn:${line}`,
|
|
121
|
+
kind: "route",
|
|
122
|
+
name: `${method} (CloudFormation)`,
|
|
123
|
+
qualifiedName: `${filePath}::${method}`,
|
|
124
|
+
filePath,
|
|
125
|
+
startLine: line,
|
|
126
|
+
endLine: line,
|
|
127
|
+
startColumn: 0,
|
|
128
|
+
endColumn: match[0].length,
|
|
129
|
+
language: "yaml",
|
|
130
|
+
updatedAt: now
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return nodes;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
function resolveResource(logicalId, context) {
|
|
137
|
+
for (const file of context.getAllFiles()) {
|
|
138
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
139
|
+
const node = context.getNodesInFile(file).find((n) => n.name === logicalId && n.kind === "class");
|
|
140
|
+
if (node) return node.id;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
145
|
+
0 && (module.exports = {
|
|
146
|
+
cloudformationResolver
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=cloudformation.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/frameworks/cloudformation.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * AWS CloudFormation Framework Resolver\n *\n * Handles raw CloudFormation templates (non-SAM).\n * Extracts resources, parameters, outputs, and cross-references.\n */\n\nimport type { Node } from '../types';\nimport type { FrameworkResolver, UnresolvedRef, ResolvedRef, ResolutionContext } from './types';\n\nexport const cloudformationResolver: FrameworkResolver = {\n name: 'cloudformation',\n detect(context: ResolutionContext): boolean {\n // Look for CloudFormation templates that are NOT SAM\n for (const name of ['template.yaml', 'template.yml', 'template.json', 'cloudformation.yaml', 'cloudformation.yml']) {\n const content = context.readFile(name);\n if (content && content.includes('AWSTemplateFormatVersion') && !content.includes('AWS::Serverless')) return true;\n }\n // Check for nested stacks or cfn templates in common directories\n return context.getAllFiles().some(f =>\n (f.includes('cloudformation/') || f.includes('cfn/') || f.includes('stacks/')) &&\n (f.endsWith('.yaml') || f.endsWith('.yml')) &&\n !f.includes('node_modules')\n );\n },\n resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null {\n // Resolve !Ref and !GetAtt references to resources\n if (/^[A-Z][a-zA-Z0-9]+$/.test(ref.referenceName)) {\n const id = resolveResource(ref.referenceName, context);\n if (id) return { original: ref, targetNodeId: id, confidence: 0.85, resolvedBy: 'framework' };\n }\n return null;\n },\n extractNodes(filePath: string, content: string): Node[] {\n const nodes: Node[] = [];\n const now = Date.now();\n\n if (!content.includes('AWSTemplateFormatVersion') && !content.includes('Resources:')) return nodes;\n\n // Extract Resources\n const resourcePattern = /^\\s{2}(\\w+):\\s*\\n\\s+Type:\\s*['\"]?([^\\s'\"]+)/gm;\n let match: RegExpExecArray | null;\n while ((match = resourcePattern.exec(content)) !== null) {\n const line = content.slice(0, match.index).split('\\n').length;\n const logicalId = match[1]!;\n const resourceType = match[2]!;\n nodes.push({\n id: `cfn:${filePath}:resource:${logicalId}:${line}`,\n kind: 'class',\n name: logicalId,\n qualifiedName: `${filePath}::${logicalId}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: match[0].length,\n language: 'yaml', updatedAt: now,\n signature: resourceType,\n });\n }\n\n // Extract Parameters\n const paramSection = content.match(/^Parameters:\\s*\\n((?:\\s{2}\\w+:[\\s\\S]*?)(?=^\\w|\\Z))/m);\n if (paramSection) {\n const paramPattern = /^\\s{2}(\\w+):/gm;\n let paramMatch: RegExpExecArray | null;\n const sectionStart = content.indexOf(paramSection[0]);\n while ((paramMatch = paramPattern.exec(paramSection[1])) !== null) {\n const line = content.slice(0, sectionStart + paramMatch.index).split('\\n').length;\n const paramName = paramMatch[1]!;\n nodes.push({\n id: `cfn:${filePath}:param:${paramName}:${line}`,\n kind: 'variable',\n name: paramName,\n qualifiedName: `${filePath}::param.${paramName}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: paramMatch[0].length,\n language: 'yaml', updatedAt: now,\n });\n }\n }\n\n // Extract Outputs\n const outputSection = content.match(/^Outputs:\\s*\\n((?:\\s{2}\\w+:[\\s\\S]*?)(?=^\\w|\\Z))/m);\n if (outputSection) {\n const outputPattern = /^\\s{2}(\\w+):/gm;\n let outputMatch: RegExpExecArray | null;\n const sectionStart = content.indexOf(outputSection[0]);\n while ((outputMatch = outputPattern.exec(outputSection[1])) !== null) {\n const line = content.slice(0, sectionStart + outputMatch.index).split('\\n').length;\n const outputName = outputMatch[1]!;\n nodes.push({\n id: `cfn:${filePath}:output:${outputName}:${line}`,\n kind: 'variable',\n name: `output.${outputName}`,\n qualifiedName: `${filePath}::output.${outputName}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: outputMatch[0].length,\n language: 'yaml', updatedAt: now,\n isExported: true,\n });\n }\n }\n\n // Extract API Gateway routes from AWS::ApiGateway::Method or AWS::ApiGatewayV2::Route\n const apiMethodPattern = /HttpMethod:\\s*['\"]?(\\w+)['\"]?/g;\n while ((match = apiMethodPattern.exec(content)) !== null) {\n if (!content.slice(Math.max(0, match.index - 200), match.index).includes('ApiGateway')) continue;\n const line = content.slice(0, match.index).split('\\n').length;\n const method = match[1]!.toUpperCase();\n nodes.push({\n id: `route:${filePath}:${method}:cfn:${line}`,\n kind: 'route',\n name: `${method} (CloudFormation)`,\n qualifiedName: `${filePath}::${method}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: match[0].length,\n language: 'yaml', updatedAt: now,\n });\n }\n\n return nodes;\n },\n};\n\nfunction resolveResource(logicalId: string, context: ResolutionContext): string | null {\n for (const file of context.getAllFiles()) {\n if (!file.endsWith('.yaml') && !file.endsWith('.yml')) continue;\n const node = context.getNodesInFile(file).find(n => n.name === logicalId && n.kind === 'class');\n if (node) return node.id;\n }\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var docker_exports = {};
|
|
20
|
+
__export(docker_exports, {
|
|
21
|
+
dockerComposeResolver: () => dockerComposeResolver
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(docker_exports);
|
|
24
|
+
const dockerComposeResolver = {
|
|
25
|
+
name: "docker-compose",
|
|
26
|
+
detect(context) {
|
|
27
|
+
return context.fileExists("docker-compose.yml") || context.fileExists("docker-compose.yaml") || context.fileExists("compose.yml") || context.fileExists("compose.yaml");
|
|
28
|
+
},
|
|
29
|
+
resolve(ref, context) {
|
|
30
|
+
if (/^[a-z][a-z0-9_-]*$/.test(ref.referenceName)) {
|
|
31
|
+
const id = resolveService(ref.referenceName, context);
|
|
32
|
+
if (id) return { original: ref, targetNodeId: id, confidence: 0.8, resolvedBy: "framework" };
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
},
|
|
36
|
+
extractNodes(filePath, content) {
|
|
37
|
+
const nodes = [];
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const basename = filePath.split("/").pop() ?? "";
|
|
40
|
+
if (!basename.match(/^(docker-)?compose\.(ya?ml)$/)) return nodes;
|
|
41
|
+
const servicesMatch = content.match(/^services:\s*\n([\s\S]*?)(?=^(?:networks|volumes|configs|secrets):|$(?![\s\S]))/m);
|
|
42
|
+
if (servicesMatch) {
|
|
43
|
+
const servicePattern = /^\s{2}([a-zA-Z0-9_-]+):\s*$/gm;
|
|
44
|
+
let match;
|
|
45
|
+
const sectionStart = content.indexOf(servicesMatch[0]);
|
|
46
|
+
while ((match = servicePattern.exec(servicesMatch[1])) !== null) {
|
|
47
|
+
const line = content.slice(0, sectionStart + match.index).split("\n").length;
|
|
48
|
+
const serviceName = match[1];
|
|
49
|
+
nodes.push({
|
|
50
|
+
id: `docker:${filePath}:service:${serviceName}:${line}`,
|
|
51
|
+
kind: "component",
|
|
52
|
+
name: serviceName,
|
|
53
|
+
qualifiedName: `${filePath}::service.${serviceName}`,
|
|
54
|
+
filePath,
|
|
55
|
+
startLine: line,
|
|
56
|
+
endLine: line,
|
|
57
|
+
startColumn: 0,
|
|
58
|
+
endColumn: match[0].length,
|
|
59
|
+
language: "yaml",
|
|
60
|
+
updatedAt: now,
|
|
61
|
+
signature: `service "${serviceName}"`
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const networksMatch = content.match(/^networks:\s*\n([\s\S]*?)(?=^(?:services|volumes|configs|secrets):|$(?![\s\S]))/m);
|
|
66
|
+
if (networksMatch) {
|
|
67
|
+
const networkPattern = /^\s{2}([a-zA-Z0-9_-]+):\s*$/gm;
|
|
68
|
+
let match;
|
|
69
|
+
const sectionStart = content.indexOf(networksMatch[0]);
|
|
70
|
+
while ((match = networkPattern.exec(networksMatch[1])) !== null) {
|
|
71
|
+
const line = content.slice(0, sectionStart + match.index).split("\n").length;
|
|
72
|
+
const networkName = match[1];
|
|
73
|
+
nodes.push({
|
|
74
|
+
id: `docker:${filePath}:network:${networkName}:${line}`,
|
|
75
|
+
kind: "variable",
|
|
76
|
+
name: `network.${networkName}`,
|
|
77
|
+
qualifiedName: `${filePath}::network.${networkName}`,
|
|
78
|
+
filePath,
|
|
79
|
+
startLine: line,
|
|
80
|
+
endLine: line,
|
|
81
|
+
startColumn: 0,
|
|
82
|
+
endColumn: match[0].length,
|
|
83
|
+
language: "yaml",
|
|
84
|
+
updatedAt: now
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const volumesMatch = content.match(/^volumes:\s*\n([\s\S]*?)(?=^(?:services|networks|configs|secrets):|$(?![\s\S]))/m);
|
|
89
|
+
if (volumesMatch) {
|
|
90
|
+
const volumePattern = /^\s{2}([a-zA-Z0-9_-]+):\s*$/gm;
|
|
91
|
+
let match;
|
|
92
|
+
const sectionStart = content.indexOf(volumesMatch[0]);
|
|
93
|
+
while ((match = volumePattern.exec(volumesMatch[1])) !== null) {
|
|
94
|
+
const line = content.slice(0, sectionStart + match.index).split("\n").length;
|
|
95
|
+
const volumeName = match[1];
|
|
96
|
+
nodes.push({
|
|
97
|
+
id: `docker:${filePath}:volume:${volumeName}:${line}`,
|
|
98
|
+
kind: "variable",
|
|
99
|
+
name: `volume.${volumeName}`,
|
|
100
|
+
qualifiedName: `${filePath}::volume.${volumeName}`,
|
|
101
|
+
filePath,
|
|
102
|
+
startLine: line,
|
|
103
|
+
endLine: line,
|
|
104
|
+
startColumn: 0,
|
|
105
|
+
endColumn: match[0].length,
|
|
106
|
+
language: "yaml",
|
|
107
|
+
updatedAt: now
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const portsPattern = /^\s{4}-\s*["']?(\d+):(\d+)["']?\s*$/gm;
|
|
112
|
+
let portMatch;
|
|
113
|
+
while ((portMatch = portsPattern.exec(content)) !== null) {
|
|
114
|
+
const line = content.slice(0, portMatch.index).split("\n").length;
|
|
115
|
+
const hostPort = portMatch[1];
|
|
116
|
+
const containerPort = portMatch[2];
|
|
117
|
+
const preceding = content.slice(0, portMatch.index);
|
|
118
|
+
const serviceMatch = preceding.match(/^\s{2}([a-zA-Z0-9_-]+):\s*$/gm);
|
|
119
|
+
const serviceName = serviceMatch ? serviceMatch[serviceMatch.length - 1].trim().replace(":", "") : "unknown";
|
|
120
|
+
nodes.push({
|
|
121
|
+
id: `docker:${filePath}:port:${hostPort}:${line}`,
|
|
122
|
+
kind: "route",
|
|
123
|
+
name: `${serviceName.trim()} :${hostPort}\u2192:${containerPort}`,
|
|
124
|
+
qualifiedName: `${filePath}::port.${hostPort}`,
|
|
125
|
+
filePath,
|
|
126
|
+
startLine: line,
|
|
127
|
+
endLine: line,
|
|
128
|
+
startColumn: 0,
|
|
129
|
+
endColumn: portMatch[0].length,
|
|
130
|
+
language: "yaml",
|
|
131
|
+
updatedAt: now
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return nodes;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function resolveService(name, context) {
|
|
138
|
+
for (const file of context.getAllFiles()) {
|
|
139
|
+
if (!file.match(/compose\.(ya?ml)$/)) continue;
|
|
140
|
+
const node = context.getNodesInFile(file).find((n) => n.name === name && n.kind === "component");
|
|
141
|
+
if (node) return node.id;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
146
|
+
0 && (module.exports = {
|
|
147
|
+
dockerComposeResolver
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/frameworks/docker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Docker Compose Framework Resolver\n *\n * Extracts services, networks, volumes from docker-compose files.\n * Maps depends_on relationships as edges.\n */\n\nimport type { Node } from '../types';\nimport type { FrameworkResolver, UnresolvedRef, ResolvedRef, ResolutionContext } from './types';\n\nexport const dockerComposeResolver: FrameworkResolver = {\n name: 'docker-compose',\n detect(context: ResolutionContext): boolean {\n return (\n context.fileExists('docker-compose.yml') ||\n context.fileExists('docker-compose.yaml') ||\n context.fileExists('compose.yml') ||\n context.fileExists('compose.yaml')\n );\n },\n resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null {\n // Resolve service name references\n if (/^[a-z][a-z0-9_-]*$/.test(ref.referenceName)) {\n const id = resolveService(ref.referenceName, context);\n if (id) return { original: ref, targetNodeId: id, confidence: 0.8, resolvedBy: 'framework' };\n }\n return null;\n },\n extractNodes(filePath: string, content: string): Node[] {\n const nodes: Node[] = [];\n const now = Date.now();\n\n const basename = filePath.split('/').pop() ?? '';\n if (!basename.match(/^(docker-)?compose\\.(ya?ml)$/)) return nodes;\n\n // Extract services\n const servicesMatch = content.match(/^services:\\s*\\n([\\s\\S]*?)(?=^(?:networks|volumes|configs|secrets):|$(?![\\s\\S]))/m);\n if (servicesMatch) {\n const servicePattern = /^\\s{2}([a-zA-Z0-9_-]+):\\s*$/gm;\n let match: RegExpExecArray | null;\n const sectionStart = content.indexOf(servicesMatch[0]);\n while ((match = servicePattern.exec(servicesMatch[1])) !== null) {\n const line = content.slice(0, sectionStart + match.index).split('\\n').length;\n const serviceName = match[1]!;\n nodes.push({\n id: `docker:${filePath}:service:${serviceName}:${line}`,\n kind: 'component',\n name: serviceName,\n qualifiedName: `${filePath}::service.${serviceName}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: match[0].length,\n language: 'yaml', updatedAt: now,\n signature: `service \"${serviceName}\"`,\n });\n }\n }\n\n // Extract networks\n const networksMatch = content.match(/^networks:\\s*\\n([\\s\\S]*?)(?=^(?:services|volumes|configs|secrets):|$(?![\\s\\S]))/m);\n if (networksMatch) {\n const networkPattern = /^\\s{2}([a-zA-Z0-9_-]+):\\s*$/gm;\n let match: RegExpExecArray | null;\n const sectionStart = content.indexOf(networksMatch[0]);\n while ((match = networkPattern.exec(networksMatch[1])) !== null) {\n const line = content.slice(0, sectionStart + match.index).split('\\n').length;\n const networkName = match[1]!;\n nodes.push({\n id: `docker:${filePath}:network:${networkName}:${line}`,\n kind: 'variable',\n name: `network.${networkName}`,\n qualifiedName: `${filePath}::network.${networkName}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: match[0].length,\n language: 'yaml', updatedAt: now,\n });\n }\n }\n\n // Extract volumes\n const volumesMatch = content.match(/^volumes:\\s*\\n([\\s\\S]*?)(?=^(?:services|networks|configs|secrets):|$(?![\\s\\S]))/m);\n if (volumesMatch) {\n const volumePattern = /^\\s{2}([a-zA-Z0-9_-]+):\\s*$/gm;\n let match: RegExpExecArray | null;\n const sectionStart = content.indexOf(volumesMatch[0]);\n while ((match = volumePattern.exec(volumesMatch[1])) !== null) {\n const line = content.slice(0, sectionStart + match.index).split('\\n').length;\n const volumeName = match[1]!;\n nodes.push({\n id: `docker:${filePath}:volume:${volumeName}:${line}`,\n kind: 'variable',\n name: `volume.${volumeName}`,\n qualifiedName: `${filePath}::volume.${volumeName}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: match[0].length,\n language: 'yaml', updatedAt: now,\n });\n }\n }\n\n // Extract exposed ports as route-like nodes\n const portsPattern = /^\\s{4}-\\s*[\"']?(\\d+):(\\d+)[\"']?\\s*$/gm;\n let portMatch: RegExpExecArray | null;\n while ((portMatch = portsPattern.exec(content)) !== null) {\n const line = content.slice(0, portMatch.index).split('\\n').length;\n const hostPort = portMatch[1]!;\n const containerPort = portMatch[2]!;\n // Find which service this port belongs to\n const preceding = content.slice(0, portMatch.index);\n const serviceMatch = preceding.match(/^\\s{2}([a-zA-Z0-9_-]+):\\s*$/gm);\n const serviceName = serviceMatch ? serviceMatch[serviceMatch.length - 1]!.trim().replace(':', '') : 'unknown';\n nodes.push({\n id: `docker:${filePath}:port:${hostPort}:${line}`,\n kind: 'route',\n name: `${serviceName.trim()} :${hostPort}\u2192:${containerPort}`,\n qualifiedName: `${filePath}::port.${hostPort}`,\n filePath, startLine: line, endLine: line, startColumn: 0, endColumn: portMatch[0].length,\n language: 'yaml', updatedAt: now,\n });\n }\n\n return nodes;\n },\n};\n\nfunction resolveService(name: string, context: ResolutionContext): string | null {\n for (const file of context.getAllFiles()) {\n if (!file.match(/compose\\.(ya?ml)$/)) continue;\n const node = context.getNodesInFile(file).find(n => n.name === name && n.kind === 'component');\n if (node) return node.id;\n }\n return null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|