@lowgular/code-graph 0.1.1 → 0.1.2

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.
Files changed (44) hide show
  1. package/bin/cli.js +2458 -5
  2. package/lib.js +2430 -0
  3. package/package.json +3 -2
  4. package/code-graph-v2/code.graph.js +0 -37
  5. package/code-graph-v2/config-from-query.js +0 -131
  6. package/code-graph-v2/extractors/extractor.js +0 -27
  7. package/code-graph-v2/extractors/index.js +0 -1
  8. package/code-graph-v2/graph-builder/code-graph.builder.js +0 -49
  9. package/code-graph-v2/graph-builder/index.js +0 -1
  10. package/code-graph-v2/graph-builder/node.processor.js +0 -22
  11. package/code-graph-v2/graph-builder/relationship.processor.js +0 -55
  12. package/code-graph-v2/graph-builder/type.processor.js +0 -21
  13. package/code-graph-v2/index.js +0 -4
  14. package/code-graph-v2/tools/build-code-graph.tool.js +0 -19
  15. package/code-graph-v2/utils.js +0 -34
  16. package/codegular/index.js +0 -5
  17. package/codegular/node.js +0 -71
  18. package/codegular/program.js +0 -100
  19. package/codegular/string.js +0 -121
  20. package/codegular/type-checker.js +0 -133
  21. package/codegular/type.js +0 -356
  22. package/codegular/utils.js +0 -335
  23. package/cypher/index.js +0 -1
  24. package/cypher/lib/executor/condition-evaluator.js +0 -135
  25. package/cypher/lib/executor/executor.js +0 -60
  26. package/cypher/lib/executor/graph.js +0 -0
  27. package/cypher/lib/executor/match-engine.js +0 -130
  28. package/cypher/lib/executor/pattern-matcher.js +0 -86
  29. package/cypher/lib/executor/relationship-navigator.js +0 -41
  30. package/cypher/lib/executor/result-formatter.js +0 -149
  31. package/cypher/lib/executor/traverse-engine.js +0 -141
  32. package/cypher/lib/executor/utils.js +0 -14
  33. package/cypher/lib/graph.stub.js +0 -38
  34. package/cypher/lib/index.js +0 -32
  35. package/cypher/lib/lexer.js +0 -376
  36. package/cypher/lib/parser.js +0 -586
  37. package/cypher/lib/validator/query-validator.js +0 -75
  38. package/cypher/lib/validator/supported-features.config.js +0 -83
  39. package/cypher/lib/validator/unsupported-features.config.js +0 -124
  40. package/cypher-cli.js +0 -41
  41. package/infra/code-graph.js +0 -147
  42. package/main.js +0 -0
  43. package/resources-cli.js +0 -75
  44. package/run-cli.js +0 -43
@@ -1,86 +0,0 @@
1
- class PatternMatcher {
2
- /**
3
- * Check if an edge type matches the relationship pattern's edge type(s)
4
- * Supports both single edge type (string) and multiple edge types (array)
5
- */
6
- matchesEdgeType(edgeType, patternEdgeType) {
7
- if (!patternEdgeType) {
8
- return true;
9
- }
10
- if (typeof patternEdgeType === "string") {
11
- return edgeType === patternEdgeType;
12
- }
13
- return patternEdgeType.includes(edgeType);
14
- }
15
- /**
16
- * Deep equality check for arrays
17
- */
18
- arraysEqual(a, b) {
19
- if (a.length !== b.length) {
20
- return false;
21
- }
22
- for (let i = 0; i < a.length; i++) {
23
- if (Array.isArray(a[i]) && Array.isArray(b[i])) {
24
- if (!this.arraysEqual(a[i], b[i])) {
25
- return false;
26
- }
27
- } else if (a[i] !== b[i]) {
28
- return false;
29
- }
30
- }
31
- return true;
32
- }
33
- /**
34
- * Check if two values are equal (handles arrays with deep equality)
35
- */
36
- valuesEqual(expected, actual) {
37
- if (Array.isArray(expected) && Array.isArray(actual)) {
38
- return this.arraysEqual(expected, actual);
39
- }
40
- return expected === actual;
41
- }
42
- /**
43
- * Check if a node matches a pattern (labels and properties)
44
- */
45
- doesNodeMatchPattern(node, pattern) {
46
- if (pattern.labels.length > 0) {
47
- const labelOperator = pattern.labelOperator || "AND";
48
- if (labelOperator === "AND") {
49
- if (!pattern.labels.every((label) => node.labels.includes(label))) {
50
- return false;
51
- }
52
- } else {
53
- if (!pattern.labels.some((label) => node.labels.includes(label))) {
54
- return false;
55
- }
56
- }
57
- }
58
- if (pattern.properties) {
59
- const notFound = Object.entries(pattern.properties).find(
60
- ([key, expectedValue]) => !this.valuesEqual(expectedValue, node[key])
61
- ) !== void 0;
62
- if (notFound) {
63
- return false;
64
- }
65
- }
66
- return true;
67
- }
68
- findMatchingNodes(graph, pattern) {
69
- return Object.values(graph.nodesById).find(
70
- (node) => this.doesNodeMatchPattern(node, pattern)
71
- );
72
- }
73
- /**
74
- * Match a single node pattern
75
- * Finds all nodes in the graph that match the given pattern
76
- * Reuses matchesPattern to avoid code duplication
77
- */
78
- filterMatchingNodes(graph, pattern) {
79
- return Object.values(graph.nodesById).filter(
80
- (node) => this.doesNodeMatchPattern(node, pattern)
81
- );
82
- }
83
- }
84
- export {
85
- PatternMatcher
86
- };
@@ -1,41 +0,0 @@
1
- class RelationshipNavigator {
2
- constructor(patternMatcher) {
3
- this.patternMatcher = patternMatcher;
4
- }
5
- /**
6
- * Find nodes and edges connected via a relationship from the given node
7
- * Returns both the connected node and the edge that connects them
8
- */
9
- findConnectedNodesAndEdges(graph, node, relationship) {
10
- const connections = [];
11
- const seenNodeIds = /* @__PURE__ */ new Set();
12
- if (relationship.direction === "outgoing" || relationship.direction === "both") {
13
- const outgoingEdges = graph.edgesBySource[node.id] || [];
14
- for (const edge of outgoingEdges) {
15
- if (this.patternMatcher.matchesEdgeType(edge.type, relationship.edgeType)) {
16
- const targetNode = graph.nodesById[edge.target];
17
- if (targetNode && !seenNodeIds.has(targetNode.id)) {
18
- seenNodeIds.add(targetNode.id);
19
- connections.push({ node: targetNode, edge });
20
- }
21
- }
22
- }
23
- }
24
- if (relationship.direction === "incoming" || relationship.direction === "both") {
25
- const incomingEdges = graph.edgesByTarget[node.id] || [];
26
- for (const edge of incomingEdges) {
27
- if (this.patternMatcher.matchesEdgeType(edge.type, relationship.edgeType)) {
28
- const sourceNode = graph.nodesById[edge.source];
29
- if (sourceNode && !seenNodeIds.has(sourceNode.id)) {
30
- seenNodeIds.add(sourceNode.id);
31
- connections.push({ node: sourceNode, edge });
32
- }
33
- }
34
- }
35
- }
36
- return connections;
37
- }
38
- }
39
- export {
40
- RelationshipNavigator
41
- };
@@ -1,149 +0,0 @@
1
- import { Annonymizer } from "./utils.js";
2
- const extractGraphNodesFromResult = (results) => {
3
- return Object.values(results).filter(
4
- (value) => value !== null && typeof value === "object" && "id" in value && "labels" in value
5
- );
6
- };
7
- class ResultFormatter {
8
- constructor() {
9
- this.annonymizer = new Annonymizer();
10
- }
11
- /**
12
- * Format a single match value (node or edge) based on extractData flag
13
- */
14
- formatValue(value) {
15
- if (value === null || value === void 0) {
16
- return null;
17
- }
18
- if ("id" in value && "labels" in value) {
19
- const node = value;
20
- return node;
21
- } else {
22
- const edge = value;
23
- return edge;
24
- }
25
- }
26
- /**
27
- * Sort results based on ORDER BY clause
28
- */
29
- sortResults(results, orderBy) {
30
- return [...results].sort((a, b) => {
31
- for (const item of orderBy.items) {
32
- const valA = this.getPropertyValue(a[item.variable], item.property);
33
- const valB = this.getPropertyValue(b[item.variable], item.property);
34
- const comparison = this.compareValues(valA, valB, item.direction);
35
- if (comparison !== 0)
36
- return comparison;
37
- }
38
- return 0;
39
- });
40
- }
41
- /**
42
- * Get property value from an object, handling nested property access
43
- */
44
- getPropertyValue(obj, property) {
45
- if (obj === null || obj === void 0)
46
- return null;
47
- return property ? obj[property] : obj;
48
- }
49
- /**
50
- * Compare two values with Neo4j-style NULL handling
51
- * ASC: NULLs first, then ascending values
52
- * DESC: Values descending, then NULLs last
53
- */
54
- compareValues(a, b, direction) {
55
- const aIsNull = a === null || a === void 0;
56
- const bIsNull = b === null || b === void 0;
57
- if (aIsNull && bIsNull)
58
- return 0;
59
- if (aIsNull)
60
- return direction === "ASC" ? -1 : 1;
61
- if (bIsNull)
62
- return direction === "ASC" ? 1 : -1;
63
- let result;
64
- if (typeof a === "string" && typeof b === "string") {
65
- result = a.localeCompare(b);
66
- } else {
67
- result = a < b ? -1 : a > b ? 1 : 0;
68
- }
69
- return direction === "DESC" ? -result : result;
70
- }
71
- /**
72
- * Format results with RETURN clause
73
- * Only includes variables specified in RETURN clause, with optional aliases
74
- * If returnAll is true (RETURN *), returns all variables
75
- * If distinct is true, deduplicate results based on returned variables' ids
76
- */
77
- formatResults(results, returnClause) {
78
- let processedResults = returnClause.limit ? results.slice(0, returnClause.limit) : results;
79
- if (returnClause.distinct) {
80
- processedResults = this.deduplicateResults(processedResults, returnClause);
81
- }
82
- if (returnClause.returnAll) {
83
- return this.formatDefault(processedResults);
84
- }
85
- return processedResults.map((match) => {
86
- const result = {};
87
- for (const item of returnClause.items) {
88
- const variable = item.expression;
89
- if (this.annonymizer.isAnonymous(variable)) {
90
- continue;
91
- }
92
- const alias = item.alias || variable;
93
- if (variable in match) {
94
- const value = match[variable];
95
- result[alias] = this.formatValue(value);
96
- } else {
97
- throw new Error(`Variable ${variable} not found in match results`);
98
- }
99
- }
100
- return result;
101
- });
102
- }
103
- /**
104
- * Deduplicate results based on returned variables' ids
105
- */
106
- deduplicateResults(results, returnClause) {
107
- const seen = /* @__PURE__ */ new Map();
108
- for (const match of results) {
109
- const key = returnClause.items.map((item) => this.getValueId(match[item.expression])).join("|");
110
- if (!seen.has(key)) {
111
- seen.set(key, match);
112
- }
113
- }
114
- return Array.from(seen.values());
115
- }
116
- /**
117
- * Get a unique identifier for a match result value
118
- */
119
- getValueId(value) {
120
- if (value === null || value === void 0) {
121
- return "null";
122
- }
123
- if ("id" in value && "labels" in value) {
124
- return value.id;
125
- }
126
- const edge = value;
127
- return `edge:${edge.source}|${edge.target}|${edge.type}`;
128
- }
129
- /**
130
- * Format results without RETURN clause (default)
131
- * Returns all matched variables keyed by their variable names
132
- */
133
- formatDefault(matches) {
134
- return matches.map((match) => {
135
- const result = {};
136
- for (const [varName, value] of Object.entries(match)) {
137
- if (this.annonymizer.isAnonymous(varName)) {
138
- continue;
139
- }
140
- result[varName] = this.formatValue(value);
141
- }
142
- return result;
143
- });
144
- }
145
- }
146
- export {
147
- ResultFormatter,
148
- extractGraphNodesFromResult
149
- };
@@ -1,141 +0,0 @@
1
- import { Annonymizer } from "./utils.js";
2
- class TraverseEngine {
3
- constructor(patternMatcher, relationshipNavigator) {
4
- this.patternMatcher = patternMatcher;
5
- this.relationshipNavigator = relationshipNavigator;
6
- this.annonymizer = new Annonymizer();
7
- }
8
- /**
9
- * Recursively traverse relationships to match subsequent patterns
10
- * Supports returning edges as variables when relationship variable is specified
11
- */
12
- traverseRelationships(graph, currentNode, currentVarName, remainingPatterns, relationships) {
13
- if (remainingPatterns.length === 0) {
14
- return [{ [currentVarName]: currentNode }];
15
- }
16
- const results = [];
17
- const nextPattern = remainingPatterns[0];
18
- const nextRelationship = relationships[0];
19
- const nextVarName = nextPattern.variable || this.annonymizer.generate(currentNode);
20
- const remainingPatterns_ = remainingPatterns.slice(1);
21
- const remainingRelationships = relationships.slice(1);
22
- if (nextRelationship.variableLength) {
23
- return this.traverseVariableLengthPath(
24
- graph,
25
- currentNode,
26
- currentVarName,
27
- nextRelationship,
28
- nextPattern,
29
- nextVarName,
30
- remainingPatterns_,
31
- remainingRelationships
32
- );
33
- }
34
- const connections = this.relationshipNavigator.findConnectedNodesAndEdges(
35
- graph,
36
- currentNode,
37
- nextRelationship
38
- );
39
- for (const { node: connectedNode, edge } of connections) {
40
- if (this.patternMatcher.doesNodeMatchPattern(connectedNode, nextPattern)) {
41
- const subMatches = this.traverseRelationships(
42
- graph,
43
- connectedNode,
44
- nextVarName,
45
- remainingPatterns_,
46
- remainingRelationships
47
- );
48
- for (const subMatch of subMatches) {
49
- const result = {
50
- [currentVarName]: currentNode,
51
- [nextVarName]: connectedNode,
52
- // Always include the connected node
53
- ...subMatch
54
- };
55
- if (nextRelationship.variable && edge) {
56
- result[nextRelationship.variable] = edge;
57
- }
58
- results.push(result);
59
- }
60
- }
61
- }
62
- return results;
63
- }
64
- /**
65
- * Traverse variable-length paths (zero or more edges)
66
- * Handles cycles by tracking visited nodes
67
- */
68
- traverseVariableLengthPath(graph, startNode, startVarName, relationship, targetPattern, targetVarName, remainingPatterns, remainingRelationships) {
69
- const results = [];
70
- const minLength = relationship.minLength ?? 0;
71
- const maxLength = relationship.maxLength ?? Infinity;
72
- const traverse = (currentNode, pathLength, visited, pathEdges) => {
73
- if (pathLength >= minLength && this.patternMatcher.doesNodeMatchPattern(currentNode, targetPattern)) {
74
- const subMatches = this.traverseRelationships(
75
- graph,
76
- currentNode,
77
- targetVarName,
78
- remainingPatterns,
79
- remainingRelationships
80
- );
81
- for (const subMatch of subMatches) {
82
- const result = {
83
- [startVarName]: startNode,
84
- [targetVarName]: currentNode,
85
- ...subMatch
86
- };
87
- if (relationship.variable && pathEdges.length > 0) {
88
- result[relationship.variable] = pathEdges[pathEdges.length - 1];
89
- }
90
- results.push(result);
91
- }
92
- }
93
- if (pathLength < maxLength) {
94
- const connections2 = this.relationshipNavigator.findConnectedNodesAndEdges(
95
- graph,
96
- currentNode,
97
- relationship
98
- );
99
- for (const { node: nextNode, edge } of connections2) {
100
- if (!visited.has(nextNode.id)) {
101
- const newVisited = new Set(visited);
102
- newVisited.add(nextNode.id);
103
- traverse(nextNode, pathLength + 1, newVisited, [
104
- ...pathEdges,
105
- edge
106
- ]);
107
- }
108
- }
109
- }
110
- };
111
- if (minLength === 0 && this.patternMatcher.doesNodeMatchPattern(startNode, targetPattern)) {
112
- const subMatches = this.traverseRelationships(
113
- graph,
114
- startNode,
115
- targetVarName,
116
- remainingPatterns,
117
- remainingRelationships
118
- );
119
- for (const subMatch of subMatches) {
120
- const result = {
121
- [startVarName]: startNode,
122
- [targetVarName]: startNode,
123
- ...subMatch
124
- };
125
- results.push(result);
126
- }
127
- }
128
- const connections = this.relationshipNavigator.findConnectedNodesAndEdges(
129
- graph,
130
- startNode,
131
- relationship
132
- );
133
- for (const { node: nextNode, edge } of connections) {
134
- traverse(nextNode, 1, /* @__PURE__ */ new Set([startNode.id, nextNode.id]), [edge]);
135
- }
136
- return results;
137
- }
138
- }
139
- export {
140
- TraverseEngine
141
- };
@@ -1,14 +0,0 @@
1
- class Annonymizer {
2
- constructor() {
3
- this.anonymousVariablePrefix = "annonymous_";
4
- }
5
- generate(node) {
6
- return this.anonymousVariablePrefix + node.id;
7
- }
8
- isAnonymous(variable) {
9
- return variable.startsWith(this.anonymousVariablePrefix);
10
- }
11
- }
12
- export {
13
- Annonymizer
14
- };
@@ -1,38 +0,0 @@
1
- class GraphBuilder {
2
- constructor() {
3
- this.graph = {
4
- nodesById: {},
5
- edgesBySource: {},
6
- edgesByTarget: {}
7
- };
8
- }
9
- addNode(node) {
10
- this.graph.nodesById[node.id] = node;
11
- return this;
12
- }
13
- addEdge(edge) {
14
- if (!this.graph.edgesBySource[edge.source]) {
15
- this.graph.edgesBySource[edge.source] = [];
16
- }
17
- if (!this.graph.edgesByTarget[edge.target]) {
18
- this.graph.edgesByTarget[edge.target] = [];
19
- }
20
- this.graph.edgesBySource[edge.source].push(edge);
21
- this.graph.edgesByTarget[edge.target].push(edge);
22
- return this;
23
- }
24
- build() {
25
- return this.graph;
26
- }
27
- }
28
- const stubNode = (id, label = "A", extras = {}) => ({ id, labels: Array.isArray(label) ? label : [label], ...extras });
29
- const stubEdge = (source, target, type = "A") => ({
30
- source,
31
- target,
32
- type
33
- });
34
- export {
35
- GraphBuilder,
36
- stubEdge,
37
- stubNode
38
- };
@@ -1,32 +0,0 @@
1
- import { CypherExecutor } from "./executor/executor.js";
2
- import { Lexer } from "./lexer.js";
3
- import { Parser } from "./parser.js";
4
- import { validateQuery } from "./validator/query-validator.js";
5
- const buildQueryObject = (query2) => {
6
- const joinedQuery = Array.isArray(query2) ? query2.join(" ") : query2;
7
- validateQuery(joinedQuery);
8
- const lexer = new Lexer(joinedQuery);
9
- const tokens = lexer.tokenize();
10
- const parser = new Parser(tokens);
11
- return parser.parse();
12
- };
13
- const query = (queryStatements, graph) => {
14
- const queryObject = buildQueryObject(queryStatements);
15
- const executor = new CypherExecutor(graph);
16
- return executor.execute(queryObject);
17
- };
18
- const matchAll = (query2, graph) => {
19
- const queryObject = buildQueryObject(query2);
20
- queryObject.statements.push({
21
- type: "ReturnStatement",
22
- returnAll: true,
23
- items: []
24
- });
25
- const executor = new CypherExecutor(graph);
26
- return executor.execute(queryObject);
27
- };
28
- export {
29
- buildQueryObject,
30
- matchAll,
31
- query
32
- };