@yesprasad/fluent-graph 0.2.2 → 0.2.4

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.
@@ -1,296 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.blastCommand = void 0;
40
- const commander_1 = require("commander");
41
- const fs = __importStar(require("fs"));
42
- const path = __importStar(require("path"));
43
- const chalk_1 = __importDefault(require("chalk"));
44
- const cli_table3_1 = __importDefault(require("cli-table3"));
45
- const registry_1 = require("../../sdk/registry");
46
- const detector_1 = require("../../sdk/detector");
47
- const traversal_1 = require("../../blast/traversal");
48
- exports.blastCommand = new commander_1.Command('blast')
49
- .description('Analyze blast radius for a target table')
50
- .argument('<target>', 'Target table name (e.g., incident, x_bank_account)')
51
- .option('-g, --graph <path>', 'Path to graph JSON file', 'fluent-graph.json')
52
- .option('-f, --format <type>', 'Output format: table, json, csv', 'table')
53
- .option('--depth <n>', 'Maximum traversal depth', '10')
54
- .option('--show-paths', 'Show the full dependency chain for each transitive artifact')
55
- .option('--direct-only', 'Show only hop-1 (direct) impacts')
56
- .option('--include-deleted', 'Include deleted artifacts in the analysis')
57
- .option('--no-banner', 'Skip banner (inherited)')
58
- .action(async (targetTableName, options) => {
59
- const isJson = options.format === 'json';
60
- try {
61
- const graphFilePath = path.isAbsolute(options.graph)
62
- ? options.graph
63
- : path.join(process.cwd(), options.graph);
64
- // Load registry for type name derivation (best-effort; falls back gracefully)
65
- const registry = new registry_1.ArtifactTypeRegistry();
66
- const sdkDetection = await (0, detector_1.detectSDK)(process.cwd());
67
- if (sdkDetection.found && sdkDetection.sdkPath) {
68
- registry.load(sdkDetection.sdkPath);
69
- }
70
- if (!fs.existsSync(graphFilePath)) {
71
- if (isJson) {
72
- console.log(JSON.stringify({ error: `Graph file not found at ${graphFilePath}`, code: 'GRAPH_NOT_FOUND' }));
73
- }
74
- else {
75
- console.error(chalk_1.default.red(`\n❌ Error: Graph file not found at ${graphFilePath}`));
76
- }
77
- process.exit(1);
78
- }
79
- const lineageGraph = JSON.parse(fs.readFileSync(graphFilePath, 'utf8'));
80
- // ── STEP 1: Validate target exists or is referenced ─────────────────────
81
- const targetNode = lineageGraph.nodes.find(node => node.name === targetTableName);
82
- const targetNodeId = targetNode ? targetNode.id : `platform:${targetTableName}`;
83
- const isBaseTable = !targetNode;
84
- const isTargetReferenced = lineageGraph.edges.some(edge => edge.to === targetNodeId || edge.targetTable === targetTableName);
85
- if (!targetNode && !isTargetReferenced) {
86
- const customTables = lineageGraph.nodes
87
- .filter(node => registry.getPluginName(node.table) === 'TablePlugin' && !registry.isChild(node.table) && node.action !== 'DELETE')
88
- .map(node => node.name)
89
- .sort();
90
- const platformTables = [...new Set(lineageGraph.edges
91
- .filter(edge => edge.to.startsWith('platform:'))
92
- .map(edge => edge.to.split(':')[1]))].sort();
93
- if (isJson) {
94
- console.log(JSON.stringify({
95
- error: `Target "${targetTableName}" not found or referenced`,
96
- code: 'TARGET_NOT_FOUND',
97
- availableCustomTables: customTables,
98
- availablePlatformTables: platformTables,
99
- }));
100
- }
101
- else {
102
- console.log(chalk_1.default.red(`\n❌ Target "${targetTableName}" not found or referenced`));
103
- console.log(chalk_1.default.yellow('\n💡 Available custom tables:'));
104
- customTables.forEach(tableName => console.log(chalk_1.default.gray(' • ') + chalk_1.default.white(tableName)));
105
- console.log(chalk_1.default.yellow('\n💡 Referenced platform tables:'));
106
- platformTables.forEach(tableName => console.log(chalk_1.default.gray(' • ') + chalk_1.default.magenta(tableName)));
107
- }
108
- process.exit(1);
109
- }
110
- // ── STEP 2: Display header ───────────────────────────────────────────────
111
- if (!isJson) {
112
- console.log(chalk_1.default.yellow.bold('\n🔥 BLAST RADIUS ANALYSIS'));
113
- console.log(chalk_1.default.gray('═'.repeat(80)));
114
- console.log(chalk_1.default.gray('Target: ') + chalk_1.default.white(targetTableName) + (isBaseTable ? chalk_1.default.yellow(' [Base Table]') : ''));
115
- if (!isBaseTable) {
116
- console.log(chalk_1.default.gray('File: ') + chalk_1.default.gray(targetNode.source.file));
117
- }
118
- console.log(chalk_1.default.gray('═'.repeat(80)));
119
- }
120
- // ── STEP 3: BFS traversal ────────────────────────────────────────────────
121
- const maxDepth = options.directOnly ? 1 : Math.max(1, parseInt(options.depth, 10) || 10);
122
- const traversal = (0, traversal_1.traverseBlastRadius)(lineageGraph, targetTableName, maxDepth);
123
- // ── STEP 4: Apply display filter ─────────────────────────────────────────
124
- // Show a node if it has a source file OR is a non-deleted local node.
125
- // Suppress SDK-managed child records that have no user-authored source.
126
- const isDisplayable = (tn) => {
127
- if (!options.includeDeleted && tn.node.action === 'DELETE')
128
- return false;
129
- if (tn.node.source.file)
130
- return true;
131
- // Suppress isChild records with no source file (intermediate composition nodes)
132
- if (registry.isChild(tn.node.table))
133
- return false;
134
- return true;
135
- };
136
- const displayed = traversal.hops.filter(isDisplayable);
137
- const directImpact = displayed.filter(tn => tn.hop === 1);
138
- const transitiveImpact = displayed.filter(tn => tn.hop > 1);
139
- if (displayed.length === 0) {
140
- if (isJson) {
141
- console.log(JSON.stringify({
142
- target: targetTableName,
143
- isBaseTable,
144
- totalImpacted: 0,
145
- direct: 0,
146
- transitive: 0,
147
- maxHopReached: 0,
148
- cyclesPrevented: traversal.cyclesPrevented,
149
- riskLevel: 'LOW',
150
- impacts: [],
151
- }, null, 2));
152
- }
153
- else {
154
- console.log(chalk_1.default.green('\n✅ Zero dependencies detected in local project.\n'));
155
- }
156
- return;
157
- }
158
- // ── STEP 5: Output ───────────────────────────────────────────────────────
159
- switch (options.format) {
160
- case 'json': {
161
- const totalCount = displayed.length;
162
- const riskLevel = totalCount < 3 ? 'LOW' : totalCount < 8 ? 'MEDIUM' : 'HIGH';
163
- console.log(JSON.stringify({
164
- target: targetTableName,
165
- isBaseTable,
166
- totalImpacted: totalCount,
167
- direct: directImpact.length,
168
- transitive: transitiveImpact.length,
169
- maxHopReached: traversal.maxHopReached,
170
- cyclesPrevented: traversal.cyclesPrevented,
171
- riskLevel,
172
- impacts: displayed.map(tn => ({
173
- artifact: tn.node.name,
174
- type: registry.getDisplayGroupName(tn.node.table).toLowerCase(),
175
- file: tn.node.source.file,
176
- hop: tn.hop,
177
- path: tn.path,
178
- via: tn.viaEdge.via ?? tn.viaEdge.type,
179
- edgeType: tn.viaEdge.type,
180
- state: tn.node.action,
181
- })),
182
- }, null, 2));
183
- break;
184
- }
185
- case 'csv': {
186
- console.log('Artifact,Type,File,Hop,Via,State,Path');
187
- displayed.forEach(tn => {
188
- const via = tn.viaEdge.via ?? tn.viaEdge.type;
189
- const pathStr = tn.path.join(' > ');
190
- console.log(`"${tn.node.name}","${registry.getDisplayGroupName(tn.node.table).toLowerCase()}","${tn.node.source.file}",${tn.hop},"${via}","${tn.node.action}","${pathStr}"`);
191
- });
192
- break;
193
- }
194
- case 'table':
195
- default: {
196
- // ── Direct Impact section ────────────────────────────────────────────
197
- if (directImpact.length > 0) {
198
- console.log(chalk_1.default.bold('\n⚡ DIRECT IMPACT') + chalk_1.default.gray(' (Hop 1)'));
199
- const directTable = new cli_table3_1.default({
200
- head: [
201
- chalk_1.default.cyan('Artifact'),
202
- chalk_1.default.cyan('Type'),
203
- chalk_1.default.cyan('File'),
204
- chalk_1.default.cyan('Via'),
205
- chalk_1.default.cyan('State'),
206
- ],
207
- wordWrap: true,
208
- style: { head: [], border: ['gray'] },
209
- });
210
- directImpact.forEach(tn => {
211
- const via = formatVia(tn);
212
- const stateColor = tn.node.action === 'DELETE' ? chalk_1.default.red : chalk_1.default.green;
213
- directTable.push([
214
- chalk_1.default.white(tn.node.name),
215
- chalk_1.default.gray(registry.getDisplayGroupName(tn.node.table).toLowerCase()),
216
- chalk_1.default.gray(tn.node.source.file || '—'),
217
- chalk_1.default.gray(via),
218
- stateColor(tn.node.action === 'DELETE' ? 'DELETE' : 'ACTIVE'),
219
- ]);
220
- });
221
- console.log(directTable.toString());
222
- }
223
- // ── Transitive Impact section ────────────────────────────────────────
224
- if (transitiveImpact.length > 0 && !options.directOnly) {
225
- console.log(chalk_1.default.bold('\n🌊 TRANSITIVE IMPACT') + chalk_1.default.gray(' (Hop 2+)'));
226
- const transitiveHead = [
227
- chalk_1.default.cyan('Artifact'),
228
- chalk_1.default.cyan('Type'),
229
- chalk_1.default.cyan('File'),
230
- chalk_1.default.cyan('Hop'),
231
- chalk_1.default.cyan('State'),
232
- ];
233
- if (options.showPaths) {
234
- transitiveHead.push(chalk_1.default.cyan('Path'));
235
- }
236
- const transitiveTable = new cli_table3_1.default({
237
- head: transitiveHead,
238
- wordWrap: true,
239
- style: { head: [], border: ['gray'] },
240
- });
241
- transitiveImpact.forEach(tn => {
242
- const stateColor = tn.node.action === 'DELETE' ? chalk_1.default.red : chalk_1.default.green;
243
- const row = [
244
- chalk_1.default.white(tn.node.name),
245
- chalk_1.default.gray(registry.getDisplayGroupName(tn.node.table).toLowerCase()),
246
- chalk_1.default.gray(tn.node.source.file || '—'),
247
- chalk_1.default.yellow(String(tn.hop)),
248
- stateColor(tn.node.action === 'DELETE' ? 'DELETE' : 'ACTIVE'),
249
- ];
250
- if (options.showPaths) {
251
- row.push(chalk_1.default.gray(tn.path.join(' → ')));
252
- }
253
- transitiveTable.push(row);
254
- });
255
- console.log(transitiveTable.toString());
256
- }
257
- // ── Impact Summary ───────────────────────────────────────────────────
258
- console.log(chalk_1.default.yellow('\n📊 IMPACT SUMMARY'));
259
- const totalCount = displayed.length;
260
- const riskLevel = totalCount < 3 ? 'LOW' : totalCount < 8 ? 'MEDIUM' : 'HIGH';
261
- const riskColor = riskLevel === 'LOW' ? chalk_1.default.green
262
- : riskLevel === 'MEDIUM' ? chalk_1.default.yellow
263
- : chalk_1.default.red;
264
- console.log(chalk_1.default.gray('Total Impacted: ') + chalk_1.default.cyan(totalCount) +
265
- chalk_1.default.gray(` (${directImpact.length} direct, ${transitiveImpact.length} transitive)`));
266
- console.log(chalk_1.default.gray('Max Depth Reached: ') + chalk_1.default.cyan(traversal.maxHopReached));
267
- console.log(chalk_1.default.gray('Risk Level: ') + riskColor.bold(riskLevel));
268
- console.log(chalk_1.default.gray('─'.repeat(80)) + '\n');
269
- if (riskLevel === 'HIGH') {
270
- console.log(chalk_1.default.yellow('⚠️ High impact — review carefully before deploying'));
271
- }
272
- else if (riskLevel === 'MEDIUM') {
273
- console.log(chalk_1.default.yellow('💡 Moderate impact'));
274
- }
275
- break;
276
- }
277
- }
278
- }
279
- catch (error) {
280
- if (isJson) {
281
- console.log(JSON.stringify({ error: error.message ?? 'Unexpected error', code: 'UNEXPECTED_ERROR' }));
282
- }
283
- else {
284
- console.error(chalk_1.default.red('\n❌ Error:'), error.message);
285
- }
286
- process.exit(1);
287
- }
288
- });
289
- /** Format the "Via" column label from a traversal node's edge. */
290
- function formatVia(tn) {
291
- const edge = tn.viaEdge;
292
- if (edge.type === 'schema_relationship') {
293
- return `${edge.via ?? 'ref'} (ref field)`;
294
- }
295
- return edge.via ?? edge.type;
296
- }
@@ -1,6 +0,0 @@
1
- import { Command } from 'commander';
2
- /**
3
- * The Extract Command: Orchestrates the transformation of a ServiceNow Fluent
4
- * project into a formal directed graph.
5
- */
6
- export declare const extractCommand: Command;
@@ -1,125 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.extractCommand = void 0;
7
- const commander_1 = require("commander");
8
- const path_1 = __importDefault(require("path"));
9
- const chalk_1 = __importDefault(require("chalk"));
10
- const ora_1 = __importDefault(require("ora"));
11
- const detector_1 = require("../../sdk/detector");
12
- const loader_1 = require("../../sdk/loader");
13
- const extractor_1 = require("../../extractor");
14
- const writer_1 = require("../../output/writer");
15
- const display_1 = require("../utils/display");
16
- /**
17
- * The Extract Command: Orchestrates the transformation of a ServiceNow Fluent
18
- * project into a formal directed graph.
19
- */
20
- exports.extractCommand = new commander_1.Command('analyze')
21
- .description('Analyze and extract dependency graph from the current project')
22
- .option('-o, --output <path>', 'Output file path', 'fluent-graph.json')
23
- .option('-f, --format <type>', 'Output format: table, json', 'table')
24
- .option('-q, --quiet', 'Suppress console output')
25
- .option('--no-display', 'Skip visual display')
26
- .option('--no-banner', 'Skip banner (inherited)')
27
- .action(async (options) => {
28
- const isJson = options.format === 'json';
29
- let spinner;
30
- try {
31
- const currentWorkingDir = process.cwd();
32
- // Step 1: Detect ServiceNow SDK Environment
33
- if (!options.quiet && !isJson) {
34
- spinner = (0, ora_1.default)('Detecting ServiceNow SDK...').start();
35
- }
36
- const sdkDetectionResult = await (0, detector_1.detectSDK)(currentWorkingDir);
37
- if (!sdkDetectionResult.found) {
38
- if (spinner)
39
- spinner.fail('SDK not found');
40
- if (isJson) {
41
- console.log(JSON.stringify({ error: sdkDetectionResult.error, code: 'SDK_NOT_FOUND' }));
42
- }
43
- else {
44
- console.error(chalk_1.default.red('\n❌ Error:'), sdkDetectionResult.error);
45
- console.log(chalk_1.default.yellow('\n💡 Make sure you\'re in a ServiceNow Fluent project directory'));
46
- }
47
- process.exit(1);
48
- }
49
- if (spinner) {
50
- spinner.succeed(`✅ SDK detected at ${chalk_1.default.gray(sdkDetectionResult.sdkPath)}`);
51
- }
52
- // Step 2: Dynamically Load Workspace SDK
53
- if (!options.quiet && !isJson) {
54
- spinner = (0, ora_1.default)('Loading workspace SDK...').start();
55
- }
56
- const workspaceSDK = (0, loader_1.loadWorkspaceSDK)(sdkDetectionResult.sdkPath);
57
- if (spinner) {
58
- spinner.succeed('Workspace SDK loaded');
59
- }
60
- // Step 3: Instantiate the Project Proxy
61
- if (!options.quiet && !isJson) {
62
- spinner = (0, ora_1.default)('Loading project configuration...').start();
63
- }
64
- const projectInstance = new workspaceSDK.sdkapi.Project({ rootDir: currentWorkingDir });
65
- if (spinner) {
66
- spinner.succeed('Project loaded');
67
- }
68
- // Step 4: Extract Lineage Graph
69
- if (!options.quiet && !isJson) {
70
- spinner = (0, ora_1.default)('Building dependency graph...').start();
71
- }
72
- // In JSON mode, redirect SDK diagnostic noise (e.g. "Record defined N times")
73
- // from stdout to stderr so stdout stays pure JSON for agent/tool consumption.
74
- let restoreStdout;
75
- if (isJson) {
76
- const original = process.stdout.write.bind(process.stdout);
77
- process.stdout.write = process.stderr.write.bind(process.stderr);
78
- restoreStdout = () => { process.stdout.write = original; };
79
- }
80
- const lineageGraph = await (0, extractor_1.extractLineageGraph)(projectInstance, workspaceSDK.registry);
81
- restoreStdout?.();
82
- if (spinner) {
83
- spinner.succeed(`Graph built: ${chalk_1.default.cyan(lineageGraph.nodes.length)} nodes, ${chalk_1.default.cyan(lineageGraph.edges.length)} edges`);
84
- }
85
- // Step 5: Persist the Graph
86
- const outputFilePath = path_1.default.isAbsolute(options.output)
87
- ? options.output
88
- : path_1.default.join(currentWorkingDir, options.output);
89
- (0, writer_1.writeGraph)(lineageGraph, outputFilePath);
90
- if (isJson) {
91
- // Pure JSON on stdout: the full lineage graph, ready for agent/tool consumption.
92
- console.log(JSON.stringify(lineageGraph, null, 2));
93
- }
94
- else {
95
- if (!options.quiet) {
96
- console.log(chalk_1.default.green('\n✅ Analysis complete'));
97
- console.log(chalk_1.default.gray(' Location: ') + chalk_1.default.white(outputFilePath));
98
- console.log(chalk_1.default.gray(' Complexity: ') + chalk_1.default.cyan((lineageGraph.edges.length / lineageGraph.nodes.length).toFixed(2)) + chalk_1.default.gray(' edges/node'));
99
- console.log(chalk_1.default.gray(' Generated at: ') + chalk_1.default.gray(new Date().toLocaleTimeString()));
100
- }
101
- // Step 6: Visual Lineage Map
102
- if (options.display && !options.quiet) {
103
- (0, display_1.displayLineageMap)(lineageGraph, workspaceSDK.registry);
104
- }
105
- }
106
- }
107
- catch (error) {
108
- if (isJson) {
109
- const msg = error instanceof Error ? error.message : 'Unexpected error';
110
- console.log(JSON.stringify({ error: msg, code: 'EXTRACTION_FAILED' }));
111
- }
112
- else if (error instanceof Error) {
113
- console.error(chalk_1.default.red('\n❌ Error:'), error.message);
114
- if (process.env.DEBUG) {
115
- console.error(chalk_1.default.gray('\nStack trace:'));
116
- console.error(error.stack);
117
- }
118
- }
119
- else {
120
- console.error(chalk_1.default.red('\n❌ Unknown error occurred'));
121
- console.error(error);
122
- }
123
- process.exit(1);
124
- }
125
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,2 +0,0 @@
1
- export declare function displayBanner(): void;
2
- export declare function displayVersion(): void;
@@ -1,25 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.displayBanner = displayBanner;
7
- exports.displayVersion = displayVersion;
8
- const chalk_1 = __importDefault(require("chalk"));
9
- function displayBanner() {
10
- const banner = `
11
- ███████╗██╗ ██╗ ██╗███████╗███╗ ██╗████████╗ ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗
12
- ██╔════╝██║ ██║ ██║██╔════╝████╗ ██║╚══██╔══╝██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║
13
- █████╗ ██║ ██║ ██║█████╗ ██╔██╗ ██║ ██║ ██║ ███╗██████╔╝███████║██████╔╝███████║
14
- ██╔══╝ ██║ ██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║ ██║██╔══██╗██╔══██║██╔════╝██╔══██║
15
- ██║ ███████╗╚██████╔╝███████╗██║ ╚████║ ██║ ╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║
16
- ╚═╝ ╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
17
- `;
18
- console.log(chalk_1.default.cyan(banner));
19
- console.log(chalk_1.default.gray(' Dependency visibility and impact analysis for ServiceNow Fluent SDK projects'));
20
- console.log(chalk_1.default.gray(' ────────────────────────────────────────────────────────────────────────────\n'));
21
- }
22
- function displayVersion() {
23
- console.log(chalk_1.default.cyan.bold('Fluent Graph') + chalk_1.default.gray(' v0.1.0'));
24
- console.log(chalk_1.default.gray('Dependency visibility and impact analysis for ServiceNow Fluent SDK projects\n'));
25
- }
@@ -1,3 +0,0 @@
1
- import { LineageGraph } from '../../types/graphs';
2
- import { ArtifactTypeRegistry } from '../../sdk/registry';
3
- export declare function displayLineageMap(graph: LineageGraph, registry: ArtifactTypeRegistry): void;
@@ -1,137 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.displayLineageMap = displayLineageMap;
7
- const path_1 = __importDefault(require("path"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const cli_table3_1 = __importDefault(require("cli-table3"));
10
- function displayLineageMap(graph, registry) {
11
- console.log(chalk_1.default.yellow.bold('\n📊 LINEAGE MAP'));
12
- console.log(chalk_1.default.gray('═'.repeat(80)));
13
- // ===== DYNAMIC PLUGIN GROUPS =====
14
- const pluginGroups = new Map();
15
- for (const node of graph.nodes) {
16
- if (node.action === 'DELETE')
17
- continue;
18
- const groupName = registry.getDisplayGroupName(node.table);
19
- if (!pluginGroups.has(groupName)) {
20
- pluginGroups.set(groupName, []);
21
- }
22
- pluginGroups.get(groupName).push(node);
23
- }
24
- for (const [groupName, nodes] of pluginGroups) {
25
- // Only show primary (non-child) nodes in the section
26
- const primaryNodes = nodes.filter(n => !registry.isChild(n.table) && n.source.file);
27
- if (primaryNodes.length === 0)
28
- continue;
29
- console.log(chalk_1.default.cyan.bold(`\n${groupName}`));
30
- const isBusinessRuleGroup = primaryNodes.some(n => registry.getPluginName(n.table) === 'BusinessRulePlugin');
31
- if (isBusinessRuleGroup) {
32
- const table = new cli_table3_1.default({
33
- head: [
34
- chalk_1.default.cyan('Name'),
35
- chalk_1.default.cyan('When'),
36
- chalk_1.default.cyan('Table'),
37
- chalk_1.default.cyan('On'),
38
- chalk_1.default.cyan('Order'),
39
- chalk_1.default.cyan('Status'),
40
- chalk_1.default.cyan('File'),
41
- ],
42
- colWidths: [32, 8, 28, 16, 7, 8, 28],
43
- wordWrap: true,
44
- style: { head: [], border: ['gray'] },
45
- });
46
- primaryNodes.forEach(node => {
47
- const statusLabel = node.action === 'DELETE' ? chalk_1.default.red('DELETE') : chalk_1.default.green('ACTIVE');
48
- const when = node.attributes.when || '';
49
- const depEdge = graph.edges.find(e => e.from === node.id && e.type === 'dependency' && e.targetTable);
50
- const targetTable = depEdge?.targetTable || '';
51
- const ops = [];
52
- if (node.attributes.action_insert)
53
- ops.push('Insert');
54
- if (node.attributes.action_update)
55
- ops.push('Update');
56
- if (node.attributes.action_delete)
57
- ops.push('Delete');
58
- if (node.attributes.action_query)
59
- ops.push('Query');
60
- table.push([
61
- chalk_1.default.white(node.name),
62
- chalk_1.default.yellow(when),
63
- chalk_1.default.magenta(targetTable),
64
- ops.length ? ops.join(', ') : chalk_1.default.gray('—'),
65
- String(node.attributes.order ?? ''),
66
- statusLabel,
67
- chalk_1.default.gray(path_1.default.basename(node.source.file)),
68
- ]);
69
- });
70
- console.log(table.toString());
71
- }
72
- else {
73
- const table = new cli_table3_1.default({
74
- head: [
75
- chalk_1.default.cyan('Name'),
76
- chalk_1.default.cyan('Status'),
77
- chalk_1.default.cyan('Source File')
78
- ],
79
- wordWrap: true,
80
- style: { head: [], border: ['gray'] }
81
- });
82
- primaryNodes.forEach(node => {
83
- const statusLabel = node.action === 'DELETE' ? chalk_1.default.red('DELETE') : chalk_1.default.green('ACTIVE');
84
- table.push([
85
- chalk_1.default.white(node.name),
86
- statusLabel,
87
- chalk_1.default.gray(node.source.file)
88
- ]);
89
- });
90
- console.log(table.toString());
91
- }
92
- }
93
- // ===== SCHEMA RELATIONSHIPS =====
94
- const schemaEdges = graph.edges.filter(edge => edge.type === 'schema_relationship');
95
- if (schemaEdges.length > 0) {
96
- console.log(chalk_1.default.cyan.bold('\n🔗 SCHEMA RELATIONSHIPS (Reference Fields)'));
97
- const table = new cli_table3_1.default({
98
- head: [
99
- chalk_1.default.cyan('Field'),
100
- chalk_1.default.cyan('From Table'),
101
- chalk_1.default.cyan('Target Table')
102
- ],
103
- wordWrap: true,
104
- style: { head: [], border: ['gray'] }
105
- });
106
- schemaEdges.forEach(edge => {
107
- const fromNode = graph.nodes.find(node => node.id === edge.from);
108
- const fieldName = fromNode?.attributes?.element || edge.via || 'unknown';
109
- const fromTableName = fromNode?.attributes?.name || 'unknown';
110
- const targetTableName = edge.to.startsWith('platform:')
111
- ? edge.to.split(':')[1]
112
- : graph.nodes.find(node => node.id === edge.to)?.name || edge.to;
113
- table.push([chalk_1.default.white(fieldName), chalk_1.default.gray(fromTableName), chalk_1.default.magenta(targetTableName)]);
114
- });
115
- console.log(table.toString());
116
- }
117
- // ===== PROJECT INSIGHTS =====
118
- console.log(chalk_1.default.cyan.bold('\n🧠 PROJECT INSIGHTS'));
119
- const platformDeps = graph.edges.filter(edge => edge.to.startsWith('platform:')).length;
120
- const internalDeps = graph.edges.filter(edge => !edge.to.startsWith('platform:')).length;
121
- const userArtifacts = graph.nodes.filter(node => node.action !== 'DELETE' &&
122
- node.source.file &&
123
- !node.id.startsWith('platform:') &&
124
- !registry.isChild(node.table));
125
- console.log(` • Total User Artifacts: ${chalk_1.default.cyan(userArtifacts.length)}`);
126
- const countsByType = userArtifacts.reduce((acc, node) => {
127
- const groupName = registry.getDisplayGroupName(node.table);
128
- acc[groupName] = (acc[groupName] || 0) + 1;
129
- return acc;
130
- }, {});
131
- console.log(` • User Artifacts Breakdown: ${JSON.stringify(countsByType, null, 2)}`);
132
- console.log(` • Platform Dependencies: ${chalk_1.default.magenta(platformDeps)}`);
133
- console.log(` • Internal References: ${chalk_1.default.white(internalDeps)}`);
134
- console.log(` • Total Artifacts: ${chalk_1.default.cyan(graph.nodes.length)}`);
135
- console.log(` • Total Relationships: ${chalk_1.default.cyan(graph.edges.length)}`);
136
- console.log(chalk_1.default.gray('═'.repeat(80)) + '\n');
137
- }
@@ -1,13 +0,0 @@
1
- import type { ArtifactEdge, ArtifactNode } from '../types/graphs';
2
- import type { ArtifactTypeRegistry } from '../sdk/registry';
3
- /**
4
- * UNIVERSAL EDGE EXTRACTOR
5
- *
6
- * Strategy:
7
- * 1. Property-based schema relationship detection (internal_type=reference)
8
- * 2. Logic attachment via table/collection property
9
- * 3. ACL name parsing — detected via registry plugin name
10
- * 4. Universal RecordId scan across all properties
11
- */
12
- export declare function extractReferenceEdges(records: any, allNodes: ArtifactNode[], registry: ArtifactTypeRegistry): ArtifactEdge[];
13
- export declare function extractCompositionEdges(records: any): ArtifactEdge[];