code-graph-context 2.4.4 → 2.5.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.
@@ -1,590 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import path from 'node:path';
3
- import { minimatch } from 'minimatch';
4
- import { Project, Node } from 'ts-morph';
5
- import { v4 as uuidv4 } from 'uuid';
6
- import { CoreNodeType, CORE_TYPESCRIPT_SCHEMA, DEFAULT_PARSE_OPTIONS, NESTJS_FRAMEWORK_SCHEMA, CoreEdgeType, } from '../config/graph-v2.js';
7
- export class TypeScriptParser {
8
- workspacePath;
9
- tsConfigPath;
10
- project;
11
- coreSchema;
12
- parseConfig;
13
- frameworkSchemas;
14
- parsedNodes = new Map();
15
- parsedEdges = new Map();
16
- sharedContext = new Map(); // Shared context for custom data
17
- constructor(workspacePath, tsConfigPath = 'tsconfig.json', coreSchema = CORE_TYPESCRIPT_SCHEMA, frameworkSchemas = [NESTJS_FRAMEWORK_SCHEMA], parseConfig = DEFAULT_PARSE_OPTIONS) {
18
- this.workspacePath = workspacePath;
19
- this.tsConfigPath = tsConfigPath;
20
- this.coreSchema = coreSchema;
21
- this.frameworkSchemas = frameworkSchemas;
22
- this.parseConfig = parseConfig;
23
- // Initialize with proper compiler options for NestJS
24
- this.project = new Project({
25
- tsConfigFilePath: tsConfigPath,
26
- skipAddingFilesFromTsConfig: false,
27
- compilerOptions: {
28
- experimentalDecorators: true,
29
- emitDecoratorMetadata: true,
30
- target: 7,
31
- module: 1,
32
- esModuleInterop: true,
33
- },
34
- });
35
- this.project.addSourceFilesAtPaths(path.join(workspacePath, '**/*.ts'));
36
- }
37
- async parseWorkspace() {
38
- console.log('added', this.project.getSourceFiles().length, 'files to project');
39
- console.log('🚀 Starting workspace parsing v2...');
40
- const sourceFiles = this.project.getSourceFiles();
41
- console.log(`📁 Found ${sourceFiles.length} TypeScript files`);
42
- // Phase 1: Core parsing for ALL files
43
- for (const sourceFile of sourceFiles) {
44
- if (this.shouldSkipFile(sourceFile))
45
- continue;
46
- console.log(`📄 Parsing file: ${sourceFile.getFilePath()}`);
47
- await this.parseCoreTypeScript(sourceFile);
48
- }
49
- // Phase 2: Apply context extractors
50
- console.log('🔧 Applying context extractors...');
51
- await this.applyContextExtractors();
52
- // Phase 3: Framework enhancements
53
- if (this.frameworkSchemas.length > 0) {
54
- console.log('🎯 Applying framework enhancements...');
55
- await this.applyFrameworkEnhancements();
56
- }
57
- // Phase 4: Edge enhancements
58
- console.log('🔗 Applying edge enhancements...');
59
- await this.applyEdgeEnhancements();
60
- console.log(`✅ Parsing complete: ${this.parsedNodes.size} nodes, ${this.parsedEdges.size} edges`);
61
- // Convert to Neo4j format
62
- const neo4jNodes = Array.from(this.parsedNodes.values()).map(this.toNeo4jNode);
63
- const neo4jEdges = Array.from(this.parsedEdges.values()).map(this.toNeo4jEdge);
64
- return { nodes: neo4jNodes, edges: neo4jEdges };
65
- }
66
- /**
67
- * Check if variable declarations should be parsed for this file
68
- * based on framework schema configurations
69
- */
70
- shouldParseVariables(filePath) {
71
- for (const schema of this.frameworkSchemas) {
72
- const parsePatterns = schema.metadata.parseVariablesFrom;
73
- if (parsePatterns) {
74
- for (const pattern of parsePatterns) {
75
- if (minimatch(filePath, pattern)) {
76
- return true;
77
- }
78
- }
79
- }
80
- }
81
- return false;
82
- }
83
- async parseCoreTypeScript(sourceFile) {
84
- try {
85
- // Create source file node
86
- const sourceFileNode = this.createCoreNode(sourceFile, CoreNodeType.SOURCE_FILE);
87
- this.addNode(sourceFileNode);
88
- // Parse classes
89
- for (const classDecl of sourceFile.getClasses()) {
90
- const classNode = this.createCoreNode(classDecl, CoreNodeType.CLASS_DECLARATION);
91
- this.addNode(classNode);
92
- // File contains class relationship
93
- const containsEdge = this.createCoreEdge(CoreEdgeType.CONTAINS, sourceFileNode.id, classNode.id);
94
- this.addEdge(containsEdge);
95
- // Parse class decorators
96
- for (const decorator of classDecl.getDecorators()) {
97
- const decoratorNode = this.createCoreNode(decorator, CoreNodeType.DECORATOR);
98
- this.addNode(decoratorNode);
99
- // Class decorated with decorator relationship
100
- const decoratedEdge = this.createCoreEdge(CoreEdgeType.DECORATED_WITH, classNode.id, decoratorNode.id);
101
- this.addEdge(decoratedEdge);
102
- }
103
- // Parse methods
104
- for (const method of classDecl.getMethods()) {
105
- const methodNode = this.createCoreNode(method, CoreNodeType.METHOD_DECLARATION);
106
- this.addNode(methodNode);
107
- // Class has method relationship
108
- const hasMethodEdge = this.createCoreEdge(CoreEdgeType.HAS_MEMBER, classNode.id, methodNode.id);
109
- this.addEdge(hasMethodEdge);
110
- // Parse method decorators
111
- for (const decorator of method.getDecorators()) {
112
- const decoratorNode = this.createCoreNode(decorator, CoreNodeType.DECORATOR);
113
- this.addNode(decoratorNode);
114
- // Method decorated with decorator relationship
115
- const decoratedEdge = this.createCoreEdge(CoreEdgeType.DECORATED_WITH, methodNode.id, decoratorNode.id);
116
- this.addEdge(decoratedEdge);
117
- }
118
- // Parse method parameters
119
- for (const param of method.getParameters()) {
120
- const paramNode = this.createCoreNode(param, CoreNodeType.PARAMETER_DECLARATION);
121
- this.addNode(paramNode);
122
- // Method has parameter relationship
123
- const hasParamEdge = this.createCoreEdge(CoreEdgeType.HAS_PARAMETER, methodNode.id, paramNode.id);
124
- this.addEdge(hasParamEdge);
125
- // Parse parameter decorators
126
- for (const decorator of param.getDecorators()) {
127
- const decoratorNode = this.createCoreNode(decorator, CoreNodeType.DECORATOR);
128
- this.addNode(decoratorNode);
129
- // Parameter decorated with decorator relationship
130
- const decoratedEdge = this.createCoreEdge(CoreEdgeType.DECORATED_WITH, paramNode.id, decoratorNode.id);
131
- this.addEdge(decoratedEdge);
132
- }
133
- }
134
- }
135
- // Parse properties
136
- for (const property of classDecl.getProperties()) {
137
- const propertyNode = this.createCoreNode(property, CoreNodeType.PROPERTY_DECLARATION);
138
- this.addNode(propertyNode);
139
- // Class has property relationship
140
- const hasPropertyEdge = this.createCoreEdge(CoreEdgeType.HAS_MEMBER, classNode.id, propertyNode.id);
141
- this.addEdge(hasPropertyEdge);
142
- // Parse property decorators
143
- for (const decorator of property.getDecorators()) {
144
- const decoratorNode = this.createCoreNode(decorator, CoreNodeType.DECORATOR);
145
- this.addNode(decoratorNode);
146
- // Property decorated with decorator relationship
147
- const decoratedEdge = this.createCoreEdge(CoreEdgeType.DECORATED_WITH, propertyNode.id, decoratorNode.id);
148
- this.addEdge(decoratedEdge);
149
- }
150
- }
151
- }
152
- // Parse interfaces
153
- for (const interfaceDecl of sourceFile.getInterfaces()) {
154
- const interfaceNode = this.createCoreNode(interfaceDecl, CoreNodeType.INTERFACE_DECLARATION);
155
- this.addNode(interfaceNode);
156
- // File contains interface relationship
157
- const containsEdge = this.createCoreEdge(CoreEdgeType.CONTAINS, sourceFileNode.id, interfaceNode.id);
158
- this.addEdge(containsEdge);
159
- }
160
- // Parse functions
161
- for (const funcDecl of sourceFile.getFunctions()) {
162
- const functionNode = this.createCoreNode(funcDecl, CoreNodeType.FUNCTION_DECLARATION);
163
- this.addNode(functionNode);
164
- // File contains function relationship
165
- const containsEdge = this.createCoreEdge(CoreEdgeType.CONTAINS, sourceFileNode.id, functionNode.id);
166
- this.addEdge(containsEdge);
167
- // Parse function parameters
168
- for (const param of funcDecl.getParameters()) {
169
- const paramNode = this.createCoreNode(param, CoreNodeType.PARAMETER_DECLARATION);
170
- this.addNode(paramNode);
171
- // Function has parameter relationship
172
- const hasParamEdge = this.createCoreEdge(CoreEdgeType.HAS_PARAMETER, functionNode.id, paramNode.id);
173
- this.addEdge(hasParamEdge);
174
- }
175
- }
176
- // Parse imports
177
- for (const importDecl of sourceFile.getImportDeclarations()) {
178
- const importNode = this.createCoreNode(importDecl, CoreNodeType.IMPORT_DECLARATION);
179
- this.addNode(importNode);
180
- // File contains import relationship
181
- const containsEdge = this.createCoreEdge(CoreEdgeType.CONTAINS, sourceFileNode.id, importNode.id);
182
- this.addEdge(containsEdge);
183
- }
184
- // Parse variable declarations if framework schema specifies this file should have them parsed
185
- if (this.shouldParseVariables(sourceFile.getFilePath())) {
186
- for (const varStatement of sourceFile.getVariableStatements()) {
187
- for (const varDecl of varStatement.getDeclarations()) {
188
- const variableNode = this.createCoreNode(varDecl, CoreNodeType.VARIABLE_DECLARATION);
189
- this.addNode(variableNode);
190
- // File contains variable relationship
191
- const containsEdge = this.createCoreEdge(CoreEdgeType.CONTAINS, sourceFileNode.id, variableNode.id);
192
- this.addEdge(containsEdge);
193
- }
194
- }
195
- }
196
- }
197
- catch (error) {
198
- console.error(`Error parsing file ${sourceFile.getFilePath()}:`, error);
199
- }
200
- }
201
- createCoreNode(astNode, coreType) {
202
- const nodeId = `${coreType}:${uuidv4()}`;
203
- // Extract base properties using schema
204
- const properties = {
205
- id: nodeId,
206
- name: this.extractNodeName(astNode, coreType),
207
- coreType,
208
- filePath: astNode.getSourceFile().getFilePath(),
209
- startLine: astNode.getStartLineNumber(),
210
- endLine: astNode.getEndLineNumber(),
211
- sourceCode: astNode.getText(),
212
- createdAt: new Date().toISOString(),
213
- };
214
- // Extract schema-defined properties
215
- const coreNodeDef = this.coreSchema.nodeTypes[coreType];
216
- if (coreNodeDef) {
217
- for (const propDef of coreNodeDef.properties) {
218
- try {
219
- const value = this.extractProperty(astNode, propDef);
220
- if (value !== undefined && propDef.name !== 'context') {
221
- properties[propDef.name] = value;
222
- }
223
- }
224
- catch (error) {
225
- console.warn(`Failed to extract core property ${propDef.name}:`, error);
226
- }
227
- }
228
- }
229
- return {
230
- id: nodeId,
231
- coreType,
232
- labels: [...(coreNodeDef?.neo4j.labels || [])],
233
- properties,
234
- sourceNode: astNode,
235
- skipEmbedding: coreNodeDef?.neo4j.skipEmbedding ?? false,
236
- };
237
- }
238
- async applyContextExtractors() {
239
- console.log('🔧 Applying context extractors...');
240
- // Apply global context extractors from framework schemas
241
- for (const frameworkSchema of this.frameworkSchemas) {
242
- for (const extractor of frameworkSchema.contextExtractors) {
243
- await this.applyContextExtractor(extractor);
244
- }
245
- }
246
- }
247
- async applyContextExtractor(extractor) {
248
- for (const [nodeId, node] of this.parsedNodes) {
249
- // Check if this extractor applies to this node
250
- if (node.coreType !== extractor.nodeType)
251
- continue;
252
- if (extractor.semanticType && node.semanticType !== extractor.semanticType)
253
- continue;
254
- try {
255
- const context = extractor.extractor(node, this.parsedNodes, this.sharedContext);
256
- if (context && Object.keys(context).length > 0) {
257
- // Merge context into node properties
258
- node.properties.context ??= {};
259
- Object.assign(node.properties.context, context);
260
- }
261
- }
262
- catch (error) {
263
- console.warn(`Failed to apply context extractor for ${nodeId}:`, error);
264
- }
265
- }
266
- }
267
- createCoreEdge(relationshipType, sourceNodeId, targetNodeId) {
268
- return {
269
- id: `${relationshipType}:${uuidv4()}`,
270
- relationshipType,
271
- sourceNodeId,
272
- targetNodeId,
273
- properties: {
274
- coreType: relationshipType,
275
- source: 'ast',
276
- confidence: 1.0,
277
- filePath: '',
278
- createdAt: new Date().toISOString(),
279
- },
280
- };
281
- }
282
- async applyFrameworkEnhancements() {
283
- console.log('🎯 Starting framework enhancements...');
284
- for (const frameworkSchema of this.frameworkSchemas) {
285
- console.log(`📦 Applying framework schema: ${frameworkSchema.name}`);
286
- await this.applyFrameworkSchema(frameworkSchema);
287
- }
288
- console.log('✅ Framework enhancements complete');
289
- }
290
- async applyFrameworkSchema(schema) {
291
- // Sort enhancements by priority (highest first)
292
- const sortedEnhancements = Object.values(schema.enhancements).sort((a, b) => b.priority - a.priority);
293
- for (const [nodeId, coreNode] of this.parsedNodes) {
294
- // Find applicable enhancements for this core node type
295
- const applicableEnhancements = sortedEnhancements.filter((enhancement) => enhancement.targetCoreType === coreNode.coreType);
296
- for (const enhancement of applicableEnhancements) {
297
- if (this.matchesDetectionPatterns(coreNode, enhancement.detectionPatterns)) {
298
- // Enhance the node!
299
- this.enhanceNode(coreNode, enhancement);
300
- break; // First match wins (highest priority)
301
- }
302
- }
303
- }
304
- }
305
- matchesDetectionPatterns(node, patterns) {
306
- return patterns.some((pattern) => {
307
- try {
308
- switch (pattern.type) {
309
- case 'decorator':
310
- return this.hasMatchingDecorator(node, pattern.pattern);
311
- case 'filename':
312
- if (pattern.pattern instanceof RegExp) {
313
- return pattern.pattern.test(node.properties.filePath);
314
- }
315
- else {
316
- return node.properties.filePath.includes(pattern.pattern);
317
- }
318
- case 'function':
319
- if (typeof pattern.pattern === 'function') {
320
- return pattern.pattern(node);
321
- }
322
- return false;
323
- case 'classname':
324
- if (pattern.pattern instanceof RegExp) {
325
- return pattern.pattern.test(node.properties.name);
326
- }
327
- else {
328
- return node.properties.name.includes(pattern.pattern);
329
- }
330
- default:
331
- return false;
332
- }
333
- }
334
- catch (error) {
335
- console.warn(`Error matching detection pattern:`, error);
336
- return false;
337
- }
338
- });
339
- }
340
- hasMatchingDecorator(node, decoratorName) {
341
- try {
342
- const context = node.properties.context;
343
- const decoratorNames = context?.decoratorNames;
344
- return decoratorNames?.includes(decoratorName) || false;
345
- }
346
- catch (error) {
347
- console.warn(`Error checking decorator ${decoratorName}:`, error);
348
- return false;
349
- }
350
- }
351
- enhanceNode(coreNode, enhancement) {
352
- try {
353
- // Set semantic type (single, not array)
354
- coreNode.semanticType = enhancement.semanticType;
355
- coreNode.properties.semanticType = enhancement.semanticType;
356
- // Add framework labels
357
- enhancement.neo4j.additionalLabels.forEach((label) => {
358
- if (!coreNode.labels.includes(label)) {
359
- coreNode.labels.unshift(label);
360
- }
361
- });
362
- // Override primary label if specified
363
- if (enhancement.neo4j.primaryLabel) {
364
- const oldPrimaryIndex = coreNode.labels.findIndex((label) => label === enhancement.neo4j.primaryLabel);
365
- if (oldPrimaryIndex > -1) {
366
- coreNode.labels.splice(oldPrimaryIndex, 1);
367
- }
368
- coreNode.labels.unshift(enhancement.neo4j.primaryLabel);
369
- }
370
- // Apply context extractors specific to this enhancement
371
- for (const extractor of enhancement.contextExtractors) {
372
- try {
373
- const context = extractor.extractor(coreNode, this.parsedNodes, this.sharedContext);
374
- if (context && Object.keys(context).length > 0) {
375
- coreNode.properties.context ??= {};
376
- Object.assign(coreNode.properties.context, context);
377
- }
378
- }
379
- catch (error) {
380
- console.warn(`Failed to apply enhancement context extractor:`, error);
381
- }
382
- }
383
- }
384
- catch (error) {
385
- console.error(`Error enhancing node ${coreNode.id}:`, error);
386
- }
387
- }
388
- async applyEdgeEnhancements() {
389
- console.log('🔗 Applying edge enhancements...');
390
- for (const frameworkSchema of this.frameworkSchemas) {
391
- for (const edgeEnhancement of Object.values(frameworkSchema.edgeEnhancements)) {
392
- await this.applyEdgeEnhancement(edgeEnhancement);
393
- }
394
- }
395
- }
396
- async applyEdgeEnhancement(edgeEnhancement) {
397
- try {
398
- for (const [sourceId, sourceNode] of this.parsedNodes) {
399
- for (const [targetId, targetNode] of this.parsedNodes) {
400
- if (sourceId === targetId)
401
- continue;
402
- if (edgeEnhancement.detectionPattern(sourceNode, targetNode, this.parsedNodes, this.sharedContext)) {
403
- // Extract context for this edge
404
- let context = {};
405
- if (edgeEnhancement.contextExtractor) {
406
- context = edgeEnhancement.contextExtractor(sourceNode, targetNode, this.parsedNodes, this.sharedContext);
407
- }
408
- const edge = this.createFrameworkEdge(edgeEnhancement.semanticType, edgeEnhancement.neo4j.relationshipType, sourceId, targetId, context);
409
- this.addEdge(edge);
410
- }
411
- }
412
- }
413
- }
414
- catch (error) {
415
- console.error(`Error applying edge enhancement ${edgeEnhancement.name}:`, error);
416
- }
417
- }
418
- createFrameworkEdge(semanticType, relationshipType, sourceNodeId, targetNodeId, context = {}) {
419
- const edgeId = `${semanticType}:${uuidv4()}`;
420
- const properties = {
421
- coreType: semanticType, // This might need adjustment based on schema
422
- semanticType,
423
- source: 'pattern',
424
- confidence: 0.8,
425
- filePath: '',
426
- createdAt: new Date().toISOString(),
427
- context,
428
- };
429
- return {
430
- id: edgeId,
431
- relationshipType,
432
- sourceNodeId,
433
- targetNodeId,
434
- properties,
435
- };
436
- }
437
- extractProperty(astNode, propDef) {
438
- const { method, source, defaultValue } = propDef.extraction;
439
- try {
440
- switch (method) {
441
- case 'ast':
442
- if (typeof source === 'string') {
443
- const fn = astNode[source];
444
- return typeof fn === 'function' ? fn.call(astNode) : defaultValue;
445
- }
446
- return defaultValue;
447
- case 'function':
448
- if (typeof source === 'function') {
449
- return source(astNode);
450
- }
451
- return defaultValue;
452
- case 'static':
453
- return defaultValue;
454
- case 'context':
455
- // Context properties are handled by context extractors
456
- return undefined;
457
- default:
458
- return defaultValue;
459
- }
460
- }
461
- catch (error) {
462
- console.warn(`Failed to extract property ${propDef.name}:`, error);
463
- return defaultValue;
464
- }
465
- }
466
- extractNodeName(astNode, coreType) {
467
- try {
468
- switch (coreType) {
469
- case CoreNodeType.SOURCE_FILE:
470
- if (Node.isSourceFile(astNode)) {
471
- return astNode.getBaseName();
472
- }
473
- break;
474
- case CoreNodeType.CLASS_DECLARATION:
475
- if (Node.isClassDeclaration(astNode)) {
476
- return astNode.getName() || 'AnonymousClass';
477
- }
478
- break;
479
- case CoreNodeType.METHOD_DECLARATION:
480
- if (Node.isMethodDeclaration(astNode)) {
481
- return astNode.getName();
482
- }
483
- break;
484
- case CoreNodeType.FUNCTION_DECLARATION:
485
- if (Node.isFunctionDeclaration(astNode)) {
486
- return astNode.getName() || 'AnonymousFunction';
487
- }
488
- break;
489
- case CoreNodeType.INTERFACE_DECLARATION:
490
- if (Node.isInterfaceDeclaration(astNode)) {
491
- return astNode.getName();
492
- }
493
- break;
494
- case CoreNodeType.PROPERTY_DECLARATION:
495
- if (Node.isPropertyDeclaration(astNode)) {
496
- return astNode.getName();
497
- }
498
- break;
499
- case CoreNodeType.PARAMETER_DECLARATION:
500
- if (Node.isParameterDeclaration(astNode)) {
501
- return astNode.getName();
502
- }
503
- break;
504
- case CoreNodeType.IMPORT_DECLARATION:
505
- if (Node.isImportDeclaration(astNode)) {
506
- return astNode.getModuleSpecifierValue();
507
- }
508
- break;
509
- case CoreNodeType.DECORATOR:
510
- if (Node.isDecorator(astNode)) {
511
- return astNode.getName();
512
- }
513
- break;
514
- default:
515
- return astNode.getKindName();
516
- }
517
- }
518
- catch (error) {
519
- console.warn(`Error extracting name for ${coreType}:`, error);
520
- }
521
- return 'Unknown';
522
- }
523
- shouldSkipFile(sourceFile) {
524
- const filePath = sourceFile.getFilePath();
525
- const excludedPatterns = this.parseConfig.excludePatterns ?? [];
526
- for (const pattern of excludedPatterns) {
527
- if (filePath.includes(pattern) || filePath.match(new RegExp(pattern))) {
528
- console.log(`⏭️ Skipping excluded file: ${filePath}`);
529
- return true;
530
- }
531
- }
532
- return false;
533
- }
534
- toNeo4jNode(parsedNode) {
535
- return {
536
- id: parsedNode.id,
537
- labels: parsedNode.labels,
538
- properties: parsedNode.properties,
539
- skipEmbedding: parsedNode.skipEmbedding ?? false,
540
- };
541
- }
542
- toNeo4jEdge(parsedEdge) {
543
- return {
544
- id: parsedEdge.id,
545
- type: parsedEdge.relationshipType,
546
- startNodeId: parsedEdge.sourceNodeId,
547
- endNodeId: parsedEdge.targetNodeId,
548
- properties: parsedEdge.properties,
549
- };
550
- }
551
- addNode(node) {
552
- this.parsedNodes.set(node.id, node);
553
- }
554
- addEdge(edge) {
555
- this.parsedEdges.set(edge.id, edge);
556
- }
557
- // Helper methods for statistics and debugging
558
- getStats() {
559
- const nodesByType = {};
560
- const nodesBySemanticType = {};
561
- for (const node of this.parsedNodes.values()) {
562
- nodesByType[node.coreType] = (nodesByType[node.coreType] || 0) + 1;
563
- if (node.semanticType) {
564
- nodesBySemanticType[node.semanticType] = (nodesBySemanticType[node.semanticType] || 0) + 1;
565
- }
566
- }
567
- return {
568
- totalNodes: this.parsedNodes.size,
569
- totalEdges: this.parsedEdges.size,
570
- nodesByType,
571
- nodesBySemanticType,
572
- };
573
- }
574
- exportToJson() {
575
- const nodes = Array.from(this.parsedNodes.values()).map((node) => ({
576
- id: node.id,
577
- labels: node.labels,
578
- properties: node.properties,
579
- skipEmbedding: node.skipEmbedding ?? false,
580
- }));
581
- const edges = Array.from(this.parsedEdges.values()).map((edge) => ({
582
- id: edge.id,
583
- type: edge.relationshipType,
584
- startNodeId: edge.sourceNodeId,
585
- endNodeId: edge.targetNodeId,
586
- properties: edge.properties,
587
- }));
588
- return { nodes, edges };
589
- }
590
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * Edge Factory
3
- * Shared utilities for creating framework edges with consistent ID generation and properties
4
- */
5
- import crypto from 'crypto';
6
- /**
7
- * Generate a deterministic edge ID based on semantic type, source, and target.
8
- * Uses SHA256 hash truncated to 16 characters for uniqueness.
9
- */
10
- export const generateFrameworkEdgeId = (semanticType, sourceNodeId, targetNodeId) => {
11
- const edgeIdentity = `${semanticType}::${sourceNodeId}::${targetNodeId}`;
12
- const edgeHash = crypto.createHash('sha256').update(edgeIdentity).digest('hex').substring(0, 16);
13
- return `${semanticType}:${edgeHash}`;
14
- };
15
- /**
16
- * Create framework edge ID and properties.
17
- * Returns common edge data that can be used to construct either ParsedEdge or Neo4jEdge.
18
- *
19
- * @param params - Edge parameters
20
- * @returns Edge ID and properties object
21
- */
22
- export const createFrameworkEdgeData = (params) => {
23
- const { semanticType, sourceNodeId, targetNodeId, projectId, context = {}, relationshipWeight = 0.5 } = params;
24
- const id = generateFrameworkEdgeId(semanticType, sourceNodeId, targetNodeId);
25
- const properties = {
26
- coreType: semanticType,
27
- projectId,
28
- semanticType,
29
- source: 'pattern',
30
- confidence: 0.8,
31
- relationshipWeight,
32
- filePath: '',
33
- createdAt: new Date().toISOString(),
34
- context,
35
- };
36
- return { id, properties };
37
- };