@shrkcrft/graph 0.1.0-alpha.10

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 (91) hide show
  1. package/dist/index.d.ts +30 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +32 -0
  4. package/dist/indexer/detect-workspace.d.ts +18 -0
  5. package/dist/indexer/detect-workspace.d.ts.map +1 -0
  6. package/dist/indexer/detect-workspace.js +80 -0
  7. package/dist/indexer/extract-csharp-file.d.ts +27 -0
  8. package/dist/indexer/extract-csharp-file.d.ts.map +1 -0
  9. package/dist/indexer/extract-csharp-file.js +163 -0
  10. package/dist/indexer/extract-dart-file.d.ts +28 -0
  11. package/dist/indexer/extract-dart-file.d.ts.map +1 -0
  12. package/dist/indexer/extract-dart-file.js +167 -0
  13. package/dist/indexer/extract-elixir-file.d.ts +27 -0
  14. package/dist/indexer/extract-elixir-file.d.ts.map +1 -0
  15. package/dist/indexer/extract-elixir-file.js +164 -0
  16. package/dist/indexer/extract-go-file.d.ts +28 -0
  17. package/dist/indexer/extract-go-file.d.ts.map +1 -0
  18. package/dist/indexer/extract-go-file.js +156 -0
  19. package/dist/indexer/extract-java-file.d.ts +25 -0
  20. package/dist/indexer/extract-java-file.d.ts.map +1 -0
  21. package/dist/indexer/extract-java-file.js +140 -0
  22. package/dist/indexer/extract-kotlin-file.d.ts +20 -0
  23. package/dist/indexer/extract-kotlin-file.d.ts.map +1 -0
  24. package/dist/indexer/extract-kotlin-file.js +158 -0
  25. package/dist/indexer/extract-php-file.d.ts +26 -0
  26. package/dist/indexer/extract-php-file.d.ts.map +1 -0
  27. package/dist/indexer/extract-php-file.js +161 -0
  28. package/dist/indexer/extract-python-file.d.ts +30 -0
  29. package/dist/indexer/extract-python-file.d.ts.map +1 -0
  30. package/dist/indexer/extract-python-file.js +196 -0
  31. package/dist/indexer/extract-ruby-file.d.ts +29 -0
  32. package/dist/indexer/extract-ruby-file.d.ts.map +1 -0
  33. package/dist/indexer/extract-ruby-file.js +151 -0
  34. package/dist/indexer/extract-rust-file.d.ts +27 -0
  35. package/dist/indexer/extract-rust-file.d.ts.map +1 -0
  36. package/dist/indexer/extract-rust-file.js +186 -0
  37. package/dist/indexer/extract-swift-file.d.ts +27 -0
  38. package/dist/indexer/extract-swift-file.d.ts.map +1 -0
  39. package/dist/indexer/extract-swift-file.js +168 -0
  40. package/dist/indexer/extract-ts-file.d.ts +79 -0
  41. package/dist/indexer/extract-ts-file.d.ts.map +1 -0
  42. package/dist/indexer/extract-ts-file.js +403 -0
  43. package/dist/indexer/incremental-updater.d.ts +41 -0
  44. package/dist/indexer/incremental-updater.d.ts.map +1 -0
  45. package/dist/indexer/incremental-updater.js +395 -0
  46. package/dist/indexer/index-builder.d.ts +23 -0
  47. package/dist/indexer/index-builder.d.ts.map +1 -0
  48. package/dist/indexer/index-builder.js +289 -0
  49. package/dist/indexer/resolve-imports.d.ts +36 -0
  50. package/dist/indexer/resolve-imports.d.ts.map +1 -0
  51. package/dist/indexer/resolve-imports.js +144 -0
  52. package/dist/indexer/unresolved-imports.d.ts +20 -0
  53. package/dist/indexer/unresolved-imports.d.ts.map +1 -0
  54. package/dist/indexer/unresolved-imports.js +32 -0
  55. package/dist/query/cycle-detection.d.ts +40 -0
  56. package/dist/query/cycle-detection.d.ts.map +1 -0
  57. package/dist/query/cycle-detection.js +135 -0
  58. package/dist/query/query-api.d.ts +87 -0
  59. package/dist/query/query-api.d.ts.map +1 -0
  60. package/dist/query/query-api.js +232 -0
  61. package/dist/schema/edge-kind.d.ts +31 -0
  62. package/dist/schema/edge-kind.d.ts.map +1 -0
  63. package/dist/schema/edge-kind.js +35 -0
  64. package/dist/schema/edge.d.ts +22 -0
  65. package/dist/schema/edge.d.ts.map +1 -0
  66. package/dist/schema/edge.js +1 -0
  67. package/dist/schema/file-fingerprint.d.ts +22 -0
  68. package/dist/schema/file-fingerprint.d.ts.map +1 -0
  69. package/dist/schema/file-fingerprint.js +1 -0
  70. package/dist/schema/graph-snapshot.d.ts +18 -0
  71. package/dist/schema/graph-snapshot.d.ts.map +1 -0
  72. package/dist/schema/graph-snapshot.js +1 -0
  73. package/dist/schema/manifest.d.ts +47 -0
  74. package/dist/schema/manifest.d.ts.map +1 -0
  75. package/dist/schema/manifest.js +1 -0
  76. package/dist/schema/node-kind.d.ts +21 -0
  77. package/dist/schema/node-kind.d.ts.map +1 -0
  78. package/dist/schema/node-kind.js +27 -0
  79. package/dist/schema/node.d.ts +26 -0
  80. package/dist/schema/node.d.ts.map +1 -0
  81. package/dist/schema/node.js +1 -0
  82. package/dist/schema/schema-version.d.ts +10 -0
  83. package/dist/schema/schema-version.d.ts.map +1 -0
  84. package/dist/schema/schema-version.js +8 -0
  85. package/dist/store/file-fingerprint.d.ts +8 -0
  86. package/dist/store/file-fingerprint.d.ts.map +1 -0
  87. package/dist/store/file-fingerprint.js +64 -0
  88. package/dist/store/graph-store.d.ts +48 -0
  89. package/dist/store/graph-store.d.ts.map +1 -0
  90. package/dist/store/graph-store.js +194 -0
  91. package/package.json +54 -0
@@ -0,0 +1,164 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { EdgeKind } from "../schema/edge-kind.js";
4
+ import { NodeKind } from "../schema/node-kind.js";
5
+ export const EXTRACT_ELIXIR_FILE_SOURCE = 'extract-elixir-file@v1';
6
+ /**
7
+ * Regex-based Elixir extractor.
8
+ *
9
+ * Top-level constructs only (column-0):
10
+ * - `defmodule Path.To.Mod do` → module
11
+ * - `def name(...)` → function (exported)
12
+ * - `defp name(...)` → function (local)
13
+ * - `defstruct …` and `defprotocol` / `defimpl` headers → struct / protocol
14
+ *
15
+ * Nested `def`s under a `defmodule` show up at column 0 in this
16
+ * extractor's view; that's intentional because they're the publicly
17
+ * callable functions on that module.
18
+ *
19
+ * Imports: `alias Foo.Bar`, `alias Foo.{A, B}`, `import Enum`,
20
+ * `require Logger`, `use MyAppWeb, :controller`. Aliases inside
21
+ * `{...}` are expanded.
22
+ *
23
+ * Out of scope:
24
+ * - Pipe operator chains.
25
+ * - Macro definitions (`defmacro`/`defmacrop`).
26
+ * - Inline guard clauses (`when ...`).
27
+ */
28
+ export function extractElixirFile(fingerprint, absPath, content) {
29
+ const text = content ?? readFileSync(absPath, 'utf8');
30
+ const fileNode = makeFileNode(fingerprint, text);
31
+ const symbolNodes = [];
32
+ const edges = [];
33
+ const lines = text.split('\n');
34
+ for (let i = 0; i < lines.length; i += 1) {
35
+ const raw = lines[i];
36
+ if (raw.length === 0)
37
+ continue;
38
+ const trimmed = raw.trimStart();
39
+ if (trimmed.startsWith('#'))
40
+ continue;
41
+ // defmodule (can be nested under another defmodule, but we still
42
+ // emit it as a symbol; the file may declare multiple).
43
+ let m = /^\s*defmodule\s+([A-Z][\w.]*)\s+do/.exec(raw);
44
+ if (m) {
45
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'module', i + 1, true);
46
+ continue;
47
+ }
48
+ // def / defp (top-of-line or under any indentation — we still want them).
49
+ m = /^\s*def\s+([a-z_][\w?!]*)\s*[\(,\s]/.exec(raw);
50
+ if (m) {
51
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'function', i + 1, true);
52
+ continue;
53
+ }
54
+ m = /^\s*defp\s+([a-z_][\w?!]*)\s*[\(,\s]/.exec(raw);
55
+ if (m) {
56
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'function', i + 1, false);
57
+ continue;
58
+ }
59
+ m = /^\s*defprotocol\s+([A-Z][\w.]*)/.exec(raw);
60
+ if (m) {
61
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'interface', i + 1, true);
62
+ continue;
63
+ }
64
+ m = /^\s*defstruct\s+/.exec(raw);
65
+ if (m) {
66
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, '__struct__', 'class', i + 1, true);
67
+ }
68
+ }
69
+ return {
70
+ fileNode,
71
+ symbolNodes,
72
+ edges,
73
+ rawImportSpecifiers: scanElixirImports(text),
74
+ importBindings: [],
75
+ identifierReferences: [],
76
+ };
77
+ }
78
+ function pushSymbol(fp, nodes, edges, fileId, name, declKind, line, isExported) {
79
+ const sym = {
80
+ id: `symbol:${fp.path}#${name}`,
81
+ kind: NodeKind.Symbol,
82
+ label: name,
83
+ path: fp.path,
84
+ line,
85
+ data: { declKind, visibility: isExported ? 'export' : 'local', isExported, language: 'elixir' },
86
+ };
87
+ nodes.push(sym);
88
+ edges.push({
89
+ id: createHash('sha1').update(`${fileId}|${sym.id}|${EdgeKind.DeclaresSymbol}`).digest('hex'),
90
+ from: fileId,
91
+ to: sym.id,
92
+ kind: EdgeKind.DeclaresSymbol,
93
+ source: EXTRACT_ELIXIR_FILE_SOURCE,
94
+ data: { visibility: isExported ? 'export' : 'local', declKind, line },
95
+ });
96
+ }
97
+ function makeFileNode(fp, text) {
98
+ const label = fp.path.split('/').pop() ?? fp.path;
99
+ const tags = ['elixir'];
100
+ if (isElixirTestPath(fp.path))
101
+ tags.push('test');
102
+ // Capture the first defmodule name for convenience.
103
+ const modMatch = /^\s*defmodule\s+([A-Z][\w.]*)\s+do/m.exec(text);
104
+ return {
105
+ id: fp.nodeId,
106
+ kind: NodeKind.File,
107
+ label,
108
+ path: fp.path,
109
+ tags,
110
+ data: {
111
+ language: 'elixir',
112
+ sizeBytes: fp.sizeBytes,
113
+ sha1: fp.sha1,
114
+ hasDefaultExport: false,
115
+ exportCount: 0,
116
+ localCount: 0,
117
+ reExportCount: 0,
118
+ ...(modMatch ? { elixirModule: modMatch[1] } : {}),
119
+ },
120
+ };
121
+ }
122
+ function isElixirTestPath(rel) {
123
+ return (/(?:^|\/)test\//.test(rel) ||
124
+ /(?:^|\/)[\w-]+_test\.exs?$/.test(rel));
125
+ }
126
+ function scanElixirImports(text) {
127
+ const out = [];
128
+ // `alias Foo.Bar`, `alias Foo.Bar.{A, B}`, `import Foo`, `require Foo`, `use Foo, :opt`
129
+ const re = /^\s*(alias|import|require|use)\s+([A-Z][\w.]*)(?:\.\{([^}]+)\})?/gm;
130
+ let m;
131
+ while ((m = re.exec(text)) !== null) {
132
+ const kind = `elixir-${m[1]}`;
133
+ const base = m[2];
134
+ const line = lineFromOffset(text, m.index);
135
+ if (m[3]) {
136
+ for (const item of m[3].split(',')) {
137
+ const part = item.trim();
138
+ if (part)
139
+ out.push({ specifier: `${base}.${part}`, line, kind });
140
+ }
141
+ }
142
+ else {
143
+ out.push({ specifier: base, line, kind });
144
+ }
145
+ }
146
+ const seen = new Set();
147
+ const deduped = [];
148
+ for (const it of out) {
149
+ const k = `${it.kind}|${it.specifier}|${it.line}`;
150
+ if (seen.has(k))
151
+ continue;
152
+ seen.add(k);
153
+ deduped.push(it);
154
+ }
155
+ return deduped;
156
+ }
157
+ function lineFromOffset(text, offset) {
158
+ let line = 1;
159
+ for (let i = 0; i < offset && i < text.length; i++) {
160
+ if (text.charCodeAt(i) === 10)
161
+ line += 1;
162
+ }
163
+ return line;
164
+ }
@@ -0,0 +1,28 @@
1
+ import type { IFileFingerprint } from '../schema/file-fingerprint.js';
2
+ import type { IExtractedFile } from './extract-ts-file.js';
3
+ export declare const EXTRACT_GO_FILE_SOURCE = "extract-go-file@v1";
4
+ /**
5
+ * Regex-based Go extractor.
6
+ *
7
+ * Top-level constructs only:
8
+ * - `func Name(...)` → symbol (function). Methods (`func (r *T) Name`)
9
+ * are also captured with the bare method name.
10
+ * - `type Name struct {...}` → symbol (struct).
11
+ * - `type Name interface {...}` → symbol (interface).
12
+ * - `type Name = ...` → symbol (type-alias).
13
+ *
14
+ * Imports: handles both `import "path"` and the block form
15
+ * `import (\n "a"\n "b"\n)`. Specifiers stored without quotes.
16
+ *
17
+ * Visibility: Go uses leading-uppercase for exported. We tag symbols
18
+ * with `isExported: true` when the name starts with an uppercase
19
+ * letter, false otherwise.
20
+ *
21
+ * Out of scope:
22
+ * - Constants / vars at the package level (would need a separate pass
23
+ * for `var (...)` and `const (...)` blocks). MVP keeps the symbol
24
+ * count tight; can be added later behind a flag.
25
+ * - Embedded types and generic type parameters.
26
+ */
27
+ export declare function extractGoFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
28
+ //# sourceMappingURL=extract-go-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-go-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-go-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CAoChB"}
@@ -0,0 +1,156 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { EdgeKind } from "../schema/edge-kind.js";
4
+ import { NodeKind } from "../schema/node-kind.js";
5
+ export const EXTRACT_GO_FILE_SOURCE = 'extract-go-file@v1';
6
+ /**
7
+ * Regex-based Go extractor.
8
+ *
9
+ * Top-level constructs only:
10
+ * - `func Name(...)` → symbol (function). Methods (`func (r *T) Name`)
11
+ * are also captured with the bare method name.
12
+ * - `type Name struct {...}` → symbol (struct).
13
+ * - `type Name interface {...}` → symbol (interface).
14
+ * - `type Name = ...` → symbol (type-alias).
15
+ *
16
+ * Imports: handles both `import "path"` and the block form
17
+ * `import (\n "a"\n "b"\n)`. Specifiers stored without quotes.
18
+ *
19
+ * Visibility: Go uses leading-uppercase for exported. We tag symbols
20
+ * with `isExported: true` when the name starts with an uppercase
21
+ * letter, false otherwise.
22
+ *
23
+ * Out of scope:
24
+ * - Constants / vars at the package level (would need a separate pass
25
+ * for `var (...)` and `const (...)` blocks). MVP keeps the symbol
26
+ * count tight; can be added later behind a flag.
27
+ * - Embedded types and generic type parameters.
28
+ */
29
+ export function extractGoFile(fingerprint, absPath, content) {
30
+ const text = content ?? readFileSync(absPath, 'utf8');
31
+ const fileNode = makeFileNode(fingerprint, text);
32
+ const symbolNodes = [];
33
+ const edges = [];
34
+ const lines = text.split('\n');
35
+ for (let i = 0; i < lines.length; i += 1) {
36
+ const raw = lines[i];
37
+ const line = i + 1;
38
+ if (raw.length === 0)
39
+ continue;
40
+ if (raw.trimStart().startsWith('//'))
41
+ continue;
42
+ // func — with optional receiver `(r *T)`.
43
+ let m = /^func(?:\s*\([^)]*\))?\s+([A-Za-z_][\w]*)\s*[\(\[]/.exec(raw);
44
+ if (m) {
45
+ const name = m[1];
46
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, name, 'function', line);
47
+ continue;
48
+ }
49
+ // type Name struct { … } / interface { … } / = alias.
50
+ m = /^type\s+([A-Za-z_][\w]*)\s+(struct|interface|=)/.exec(raw);
51
+ if (m) {
52
+ const name = m[1];
53
+ const decl = m[2] === 'struct' ? 'class' : m[2] === 'interface' ? 'interface' : 'type-alias';
54
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, name, decl, line);
55
+ }
56
+ }
57
+ return {
58
+ fileNode,
59
+ symbolNodes,
60
+ edges,
61
+ rawImportSpecifiers: scanGoImports(text),
62
+ importBindings: [],
63
+ identifierReferences: [],
64
+ };
65
+ }
66
+ function pushSymbol(fp, nodes, edges, fileId, name, declKind, line) {
67
+ const isExported = /^[A-Z]/.test(name);
68
+ const sym = {
69
+ id: `symbol:${fp.path}#${name}`,
70
+ kind: NodeKind.Symbol,
71
+ label: name,
72
+ path: fp.path,
73
+ line,
74
+ data: { declKind, visibility: isExported ? 'export' : 'local', isExported, language: 'go' },
75
+ };
76
+ nodes.push(sym);
77
+ edges.push({
78
+ id: createHash('sha1').update(`${fileId}|${sym.id}|${EdgeKind.DeclaresSymbol}`).digest('hex'),
79
+ from: fileId,
80
+ to: sym.id,
81
+ kind: EdgeKind.DeclaresSymbol,
82
+ source: EXTRACT_GO_FILE_SOURCE,
83
+ data: { visibility: isExported ? 'export' : 'local', declKind, line },
84
+ });
85
+ }
86
+ function makeFileNode(fp, text) {
87
+ const label = fp.path.split('/').pop() ?? fp.path;
88
+ const tags = ['go'];
89
+ if (isGoTestPath(fp.path))
90
+ tags.push('test');
91
+ const packageMatch = /^package\s+(\w+)/m.exec(text);
92
+ return {
93
+ id: fp.nodeId,
94
+ kind: NodeKind.File,
95
+ label,
96
+ path: fp.path,
97
+ tags,
98
+ data: {
99
+ language: 'go',
100
+ sizeBytes: fp.sizeBytes,
101
+ sha1: fp.sha1,
102
+ hasDefaultExport: false,
103
+ exportCount: 0,
104
+ localCount: 0,
105
+ reExportCount: 0,
106
+ ...(packageMatch ? { goPackage: packageMatch[1] } : {}),
107
+ },
108
+ };
109
+ }
110
+ function isGoTestPath(rel) {
111
+ return /(?:^|\/)[\w-]+_test\.go$/.test(rel);
112
+ }
113
+ function scanGoImports(text) {
114
+ const out = [];
115
+ // Single-line: import "path" or import alias "path"
116
+ const single = /^import\s+(?:[\w.]+\s+)?"([^"\n]+)"/gm;
117
+ let m;
118
+ while ((m = single.exec(text)) !== null) {
119
+ const line = lineFromOffset(text, m.index);
120
+ out.push({ specifier: m[1], line, kind: 'go-import' });
121
+ }
122
+ // Block form: import (\n "a"\n alias "b"\n)
123
+ const block = /^import\s*\(([^)]*)\)/gms;
124
+ while ((m = block.exec(text)) !== null) {
125
+ const body = m[1];
126
+ const startLine = lineFromOffset(text, m.index);
127
+ const bodyLines = body.split('\n');
128
+ for (let i = 0; i < bodyLines.length; i += 1) {
129
+ const ln = bodyLines[i].trim();
130
+ if (!ln || ln.startsWith('//'))
131
+ continue;
132
+ const inner = /^(?:[\w.]+\s+)?"([^"]+)"/.exec(ln);
133
+ if (inner)
134
+ out.push({ specifier: inner[1], line: startLine + i, kind: 'go-import' });
135
+ }
136
+ }
137
+ // De-dupe.
138
+ const seen = new Set();
139
+ const deduped = [];
140
+ for (const it of out) {
141
+ const k = `${it.specifier}|${it.line}`;
142
+ if (seen.has(k))
143
+ continue;
144
+ seen.add(k);
145
+ deduped.push(it);
146
+ }
147
+ return deduped;
148
+ }
149
+ function lineFromOffset(text, offset) {
150
+ let line = 1;
151
+ for (let i = 0; i < offset && i < text.length; i++) {
152
+ if (text.charCodeAt(i) === 10)
153
+ line += 1;
154
+ }
155
+ return line;
156
+ }
@@ -0,0 +1,25 @@
1
+ import type { IFileFingerprint } from '../schema/file-fingerprint.js';
2
+ import type { IExtractedFile } from './extract-ts-file.js';
3
+ export declare const EXTRACT_JAVA_FILE_SOURCE = "extract-java-file@v1";
4
+ /**
5
+ * Regex-based Java extractor.
6
+ *
7
+ * Top-level constructs only — we don't track nested classes or class
8
+ * members. For a fuller view a future round can layer a real Java
9
+ * parser (`tree-sitter-java`) without changing the schema.
10
+ *
11
+ * - `public class Name { … }` (and `final` / `abstract` / `sealed`
12
+ * modifiers in any order) → symbol (class).
13
+ * - `interface Name { … }` → symbol (interface).
14
+ * - `enum Name { … }` → symbol (enum).
15
+ * - `record Name(...)` → symbol (class).
16
+ *
17
+ * Visibility is derived from the modifier: `public` → exported,
18
+ * anything else → local (package-private / private / protected).
19
+ *
20
+ * Imports: classic Java `import a.b.C;` / `import a.b.*;` /
21
+ * `import static a.b.C.*;`. The specifier is the fully-qualified path
22
+ * (without trailing `;`).
23
+ */
24
+ export declare function extractJavaFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
25
+ //# sourceMappingURL=extract-java-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-java-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-java-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CAoChB"}
@@ -0,0 +1,140 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { EdgeKind } from "../schema/edge-kind.js";
4
+ import { NodeKind } from "../schema/node-kind.js";
5
+ export const EXTRACT_JAVA_FILE_SOURCE = 'extract-java-file@v1';
6
+ /**
7
+ * Regex-based Java extractor.
8
+ *
9
+ * Top-level constructs only — we don't track nested classes or class
10
+ * members. For a fuller view a future round can layer a real Java
11
+ * parser (`tree-sitter-java`) without changing the schema.
12
+ *
13
+ * - `public class Name { … }` (and `final` / `abstract` / `sealed`
14
+ * modifiers in any order) → symbol (class).
15
+ * - `interface Name { … }` → symbol (interface).
16
+ * - `enum Name { … }` → symbol (enum).
17
+ * - `record Name(...)` → symbol (class).
18
+ *
19
+ * Visibility is derived from the modifier: `public` → exported,
20
+ * anything else → local (package-private / private / protected).
21
+ *
22
+ * Imports: classic Java `import a.b.C;` / `import a.b.*;` /
23
+ * `import static a.b.C.*;`. The specifier is the fully-qualified path
24
+ * (without trailing `;`).
25
+ */
26
+ export function extractJavaFile(fingerprint, absPath, content) {
27
+ const text = content ?? readFileSync(absPath, 'utf8');
28
+ const fileNode = makeFileNode(fingerprint, text);
29
+ const symbolNodes = [];
30
+ const edges = [];
31
+ // Capture top-level type declarations. Modifiers can appear in any
32
+ // order and may include annotations (which we discard for the MVP).
33
+ // We require the declaration starts at column 0 to keep nested
34
+ // declarations (class bodies) out of the symbol list.
35
+ const lines = text.split('\n');
36
+ for (let i = 0; i < lines.length; i += 1) {
37
+ const raw = lines[i];
38
+ if (raw.length === 0)
39
+ continue;
40
+ if (raw.startsWith(' ') || raw.startsWith('\t'))
41
+ continue;
42
+ if (raw.trimStart().startsWith('//'))
43
+ continue;
44
+ // Strip leading annotations like `@Override`.
45
+ const stripped = raw.replace(/^(?:@\w+(?:\([^)]*\))?\s+)+/, '');
46
+ const m = /^(?:public\s+|protected\s+|private\s+|static\s+|final\s+|abstract\s+|sealed\s+|non-sealed\s+)*\s*(class|interface|enum|record)\s+([A-Za-z_][\w]*)/.exec(stripped);
47
+ if (!m)
48
+ continue;
49
+ const declKind = m[1] === 'class' || m[1] === 'record' ? 'class'
50
+ : m[1] === 'interface' ? 'interface'
51
+ : 'enum';
52
+ const name = m[2];
53
+ const isExported = /\bpublic\b/.test(stripped);
54
+ pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, name, declKind, i + 1, isExported);
55
+ }
56
+ return {
57
+ fileNode,
58
+ symbolNodes,
59
+ edges,
60
+ rawImportSpecifiers: scanJavaImports(text),
61
+ importBindings: [],
62
+ identifierReferences: [],
63
+ };
64
+ }
65
+ function pushSymbol(fp, nodes, edges, fileId, name, declKind, line, isExported) {
66
+ const sym = {
67
+ id: `symbol:${fp.path}#${name}`,
68
+ kind: NodeKind.Symbol,
69
+ label: name,
70
+ path: fp.path,
71
+ line,
72
+ data: { declKind, visibility: isExported ? 'export' : 'local', isExported, language: 'java' },
73
+ };
74
+ nodes.push(sym);
75
+ edges.push({
76
+ id: createHash('sha1').update(`${fileId}|${sym.id}|${EdgeKind.DeclaresSymbol}`).digest('hex'),
77
+ from: fileId,
78
+ to: sym.id,
79
+ kind: EdgeKind.DeclaresSymbol,
80
+ source: EXTRACT_JAVA_FILE_SOURCE,
81
+ data: { visibility: isExported ? 'export' : 'local', declKind, line },
82
+ });
83
+ }
84
+ function makeFileNode(fp, text) {
85
+ const label = fp.path.split('/').pop() ?? fp.path;
86
+ const tags = ['java'];
87
+ if (isJavaTestPath(fp.path))
88
+ tags.push('test');
89
+ const packageMatch = /^package\s+([\w.]+)\s*;/m.exec(text);
90
+ return {
91
+ id: fp.nodeId,
92
+ kind: NodeKind.File,
93
+ label,
94
+ path: fp.path,
95
+ tags,
96
+ data: {
97
+ language: 'java',
98
+ sizeBytes: fp.sizeBytes,
99
+ sha1: fp.sha1,
100
+ hasDefaultExport: false,
101
+ exportCount: 0,
102
+ localCount: 0,
103
+ reExportCount: 0,
104
+ ...(packageMatch ? { javaPackage: packageMatch[1] } : {}),
105
+ },
106
+ };
107
+ }
108
+ function isJavaTestPath(rel) {
109
+ return (/(?:^|\/)src\/test\//.test(rel) ||
110
+ /(?:^|\/)[\w-]+Test\.java$/.test(rel) ||
111
+ /(?:^|\/)[\w-]+Tests\.java$/.test(rel));
112
+ }
113
+ function scanJavaImports(text) {
114
+ const out = [];
115
+ const re = /^import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;/gm;
116
+ let m;
117
+ while ((m = re.exec(text)) !== null) {
118
+ const line = lineFromOffset(text, m.index);
119
+ out.push({ specifier: m[1], line, kind: 'java-import' });
120
+ }
121
+ // De-dupe.
122
+ const seen = new Set();
123
+ const deduped = [];
124
+ for (const it of out) {
125
+ const k = `${it.specifier}|${it.line}`;
126
+ if (seen.has(k))
127
+ continue;
128
+ seen.add(k);
129
+ deduped.push(it);
130
+ }
131
+ return deduped;
132
+ }
133
+ function lineFromOffset(text, offset) {
134
+ let line = 1;
135
+ for (let i = 0; i < offset && i < text.length; i++) {
136
+ if (text.charCodeAt(i) === 10)
137
+ line += 1;
138
+ }
139
+ return line;
140
+ }
@@ -0,0 +1,20 @@
1
+ import type { IFileFingerprint } from '../schema/file-fingerprint.js';
2
+ import type { IExtractedFile } from './extract-ts-file.js';
3
+ export declare const EXTRACT_KOTLIN_FILE_SOURCE = "extract-kotlin-file@v1";
4
+ /**
5
+ * Regex-based Kotlin extractor.
6
+ *
7
+ * Top-level constructs only. Captured:
8
+ * - `fun name(...)`, `inline fun`, `suspend fun` → function
9
+ * - `class Name`, `data class Name`, `value class Name`, `inline class Name`, `sealed class Name`, `abstract class Name`, `open class Name` → class
10
+ * - `interface Name`, `sealed interface Name` → interface
11
+ * - `object Name` → object (rendered as `class`)
12
+ * - `enum class Name` → enum
13
+ * - `typealias Name = ...` → type-alias
14
+ * - `val NAME: T`, `var NAME: T`, `const val NAME` → const
15
+ *
16
+ * Visibility: Kotlin's default is `public`. `private`, `internal`,
17
+ * and `protected` mark a symbol as local; everything else is exported.
18
+ */
19
+ export declare function extractKotlinFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
20
+ //# sourceMappingURL=extract-kotlin-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-kotlin-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-kotlin-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CA8DhB"}