@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,403 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import * as nodePath from 'node:path';
4
+ import * as ts from 'typescript';
5
+ import { buildSymbolIndex, SymbolVisibility, } from '@shrkcrft/inspector';
6
+ import { EdgeKind } from "../schema/edge-kind.js";
7
+ import { NodeKind } from "../schema/node-kind.js";
8
+ export const EXTRACT_TS_FILE_SOURCE = 'extract-ts-file@v1';
9
+ /**
10
+ * Per-file import scan. The boundaries package owns the project-wide
11
+ * `scanImports`; for the graph extractor we need a per-file pass that
12
+ * runs against a buffer we already have in hand. The regexes mirror
13
+ * `boundaries/scan-imports.ts` deliberately — keeping them in sync is a
14
+ * test obligation, not a runtime one.
15
+ */
16
+ const IMPORT_RE = /(?:^|\s)(?:import|export)\s+[^'"`]*?from\s+['"]([^'"`]+)['"]/g;
17
+ const SIDE_EFFECT_IMPORT_RE = /(?:^|\s)import\s+['"]([^'"`]+)['"]/g;
18
+ const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*['"]([^'"`]+)['"]\s*\)/g;
19
+ const REQUIRE_RE = /\brequire\s*\(\s*['"]([^'"`]+)['"]\s*\)/g;
20
+ /**
21
+ * Extract graph entities from a single TS/TSX/JS/JSX file.
22
+ *
23
+ * Re-uses `buildSymbolIndex` for symbol detection (per-file AST, no full
24
+ * program). Import edges carry the literal specifier; resolution to the
25
+ * target file id happens in `resolve-imports.ts` (R64).
26
+ */
27
+ export function extractTsFile(fingerprint, absPath, content) {
28
+ const text = content ?? readFileSync(absPath, 'utf8');
29
+ // Single-file-component languages (Vue, Svelte, Astro) are not pure TS.
30
+ // We produce a minimal File node and let `@shrkcrft/framework-scanners`
31
+ // detect component-level structure. Imports still extracted from
32
+ // `<script>` blocks via the regex pass below.
33
+ if (fingerprint.language === 'vue' ||
34
+ fingerprint.language === 'svelte' ||
35
+ fingerprint.language === 'astro' ||
36
+ fingerprint.language === 'graphql') {
37
+ const fileNode = makeFileNodeForNonTs(fingerprint);
38
+ return {
39
+ fileNode,
40
+ symbolNodes: [],
41
+ edges: [],
42
+ rawImportSpecifiers: fingerprint.language === 'graphql' ? [] : scanFileImports(text),
43
+ importBindings: [],
44
+ identifierReferences: [],
45
+ };
46
+ }
47
+ const idx = buildSymbolIndex(absPath, text);
48
+ const fileNode = makeFileNode(fingerprint, idx);
49
+ const symbolNodes = [];
50
+ const edges = [];
51
+ for (const e of idx.exports) {
52
+ const sym = makeSymbolNode(fingerprint, e.name, e.kind, e.visibility, e.line);
53
+ symbolNodes.push(sym);
54
+ edges.push(buildEdge(fileNode.id, sym.id, EdgeKind.DeclaresSymbol, {
55
+ visibility: e.visibility,
56
+ declKind: e.kind,
57
+ line: e.line,
58
+ }));
59
+ }
60
+ for (const l of idx.locals) {
61
+ const sym = makeSymbolNode(fingerprint, l.name, l.kind, l.visibility, l.line);
62
+ symbolNodes.push(sym);
63
+ edges.push(buildEdge(fileNode.id, sym.id, EdgeKind.DeclaresSymbol, {
64
+ visibility: l.visibility,
65
+ declKind: l.kind,
66
+ line: l.line,
67
+ }));
68
+ }
69
+ for (const re of idx.reExports) {
70
+ // The target file is unresolved at extract-time; we record the spec.
71
+ // The indexer post-pass adds the resolved symbol edge once imports
72
+ // resolve. The placeholder symbol id below intentionally has no
73
+ // matching node — the resolver replaces it.
74
+ const placeholderTarget = `symbol:unresolved:${re.from}#${re.name}`;
75
+ edges.push(buildEdge(fileNode.id, placeholderTarget, EdgeKind.ReExportsSymbol, {
76
+ specifier: re.from,
77
+ name: re.name,
78
+ star: re.star,
79
+ line: re.line,
80
+ }));
81
+ }
82
+ const rawImportSpecifiers = scanFileImports(text);
83
+ const { importBindings, identifierReferences } = walkAst(absPath, text);
84
+ return {
85
+ fileNode,
86
+ symbolNodes,
87
+ edges,
88
+ rawImportSpecifiers,
89
+ importBindings,
90
+ identifierReferences,
91
+ };
92
+ }
93
+ function pickScriptKind(ext) {
94
+ switch (ext) {
95
+ case '.tsx': return ts.ScriptKind.TSX;
96
+ case '.jsx': return ts.ScriptKind.JSX;
97
+ case '.js':
98
+ case '.mjs':
99
+ case '.cjs': return ts.ScriptKind.JS;
100
+ default: return ts.ScriptKind.TS;
101
+ }
102
+ }
103
+ /**
104
+ * AST walk that collects:
105
+ * - Named + default import bindings (skip namespace + type-only).
106
+ * - Identifier references in the file body, flagged as `isCall` when
107
+ * the identifier is the callee of a `CallExpression`.
108
+ *
109
+ * Identifiers inside import declarations themselves are not collected —
110
+ * they're declaration sites, not uses.
111
+ */
112
+ function walkAst(absPath, text) {
113
+ const ext = nodePath.extname(absPath).toLowerCase();
114
+ let sf;
115
+ try {
116
+ sf = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, pickScriptKind(ext));
117
+ }
118
+ catch {
119
+ return { importBindings: [], identifierReferences: [] };
120
+ }
121
+ const bindings = [];
122
+ const refs = [];
123
+ for (const stmt of sf.statements) {
124
+ if (!ts.isImportDeclaration(stmt))
125
+ continue;
126
+ if (!ts.isStringLiteral(stmt.moduleSpecifier))
127
+ continue;
128
+ const specifier = stmt.moduleSpecifier.text;
129
+ const clause = stmt.importClause;
130
+ if (!clause)
131
+ continue;
132
+ if (clause.isTypeOnly)
133
+ continue;
134
+ if (clause.name) {
135
+ bindings.push({
136
+ localName: clause.name.text,
137
+ importedName: 'default',
138
+ specifier,
139
+ isDefault: true,
140
+ line: lineOf(sf, clause.name),
141
+ });
142
+ }
143
+ if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
144
+ for (const elem of clause.namedBindings.elements) {
145
+ if (elem.isTypeOnly)
146
+ continue;
147
+ bindings.push({
148
+ localName: elem.name.text,
149
+ importedName: elem.propertyName ? elem.propertyName.text : elem.name.text,
150
+ specifier,
151
+ isDefault: false,
152
+ line: lineOf(sf, elem),
153
+ });
154
+ }
155
+ }
156
+ // NamespaceImport (`import * as X`) intentionally skipped.
157
+ }
158
+ function visit(node) {
159
+ // Skip the declaration sites we already harvested.
160
+ if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node))
161
+ return;
162
+ if (ts.isIdentifier(node) && !isDeclarationName(node)) {
163
+ refs.push({
164
+ name: node.text,
165
+ line: lineOf(sf, node),
166
+ isCall: isCallCallee(node),
167
+ });
168
+ }
169
+ ts.forEachChild(node, visit);
170
+ }
171
+ ts.forEachChild(sf, visit);
172
+ // De-dupe identical (name, line, isCall) triples; a single AST often
173
+ // visits the same identifier twice (e.g. in computed property names).
174
+ const seen = new Set();
175
+ const dedupedRefs = [];
176
+ for (const r of refs) {
177
+ const k = `${r.name}|${r.line}|${r.isCall ? 1 : 0}`;
178
+ if (seen.has(k))
179
+ continue;
180
+ seen.add(k);
181
+ dedupedRefs.push(r);
182
+ }
183
+ return { importBindings: bindings, identifierReferences: dedupedRefs };
184
+ }
185
+ function isDeclarationName(id) {
186
+ const parent = id.parent;
187
+ if (!parent)
188
+ return false;
189
+ // The `name` of various declarations; we don't want to record those.
190
+ if ((ts.isVariableDeclaration(parent) ||
191
+ ts.isFunctionDeclaration(parent) ||
192
+ ts.isClassDeclaration(parent) ||
193
+ ts.isInterfaceDeclaration(parent) ||
194
+ ts.isTypeAliasDeclaration(parent) ||
195
+ ts.isEnumDeclaration(parent) ||
196
+ ts.isParameter(parent) ||
197
+ ts.isPropertyDeclaration(parent) ||
198
+ ts.isPropertySignature(parent) ||
199
+ ts.isMethodDeclaration(parent) ||
200
+ ts.isMethodSignature(parent) ||
201
+ ts.isEnumMember(parent) ||
202
+ ts.isBindingElement(parent)) &&
203
+ parent.name === id) {
204
+ return true;
205
+ }
206
+ // Property accesses: `foo.bar` — `bar` is a property name, not a free
207
+ // identifier. We DO want to capture `foo`.
208
+ if (ts.isPropertyAccessExpression(parent) && parent.name === id)
209
+ return true;
210
+ if (ts.isQualifiedName(parent) && parent.right === id)
211
+ return true;
212
+ // Property assignment in object literal: `{ bar: x }` — `bar` is a key.
213
+ if (ts.isPropertyAssignment(parent) && parent.name === id)
214
+ return true;
215
+ if (ts.isShorthandPropertyAssignment(parent) && parent.name === id) {
216
+ // `{ foo }` — `foo` is BOTH key and value; we want it.
217
+ return false;
218
+ }
219
+ return false;
220
+ }
221
+ function isCallCallee(id) {
222
+ const parent = id.parent;
223
+ if (!parent)
224
+ return false;
225
+ if (ts.isCallExpression(parent) && parent.expression === id)
226
+ return true;
227
+ // `new Foo(...)` — semantically an invocation; count it as a call.
228
+ if (ts.isNewExpression(parent) && parent.expression === id)
229
+ return true;
230
+ return false;
231
+ }
232
+ function lineOf(sf, node) {
233
+ return sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
234
+ }
235
+ /**
236
+ * Stitch identifier references + import bindings into edges.
237
+ *
238
+ * Called by the indexer after per-file extraction + import resolution.
239
+ * Emits `references-symbol` / `calls-symbol` edges from a file to the
240
+ * symbol(s) it uses. Same-file references resolve against the file's
241
+ * own declared symbols; cross-file references resolve via the import
242
+ * bindings + the resolver's spec → targetPath map.
243
+ *
244
+ * Default imports target `symbol:<targetPath>#<defaultExportName>`
245
+ * when the default export name is known (via the file node's
246
+ * `defaultExportName` data), or `#default` as a placeholder when not.
247
+ */
248
+ export function stitchPerFileReferences(input) {
249
+ const { fileNodeId, extracted, resolvedSpec, defaultExportNameByPath, localSymbolNamesInThisFile } = input;
250
+ const bindings = new Map();
251
+ for (const b of extracted.importBindings) {
252
+ const targetPath = resolvedSpec.get(b.specifier);
253
+ if (!targetPath)
254
+ continue;
255
+ if (b.isDefault) {
256
+ const defName = defaultExportNameByPath.get(targetPath);
257
+ bindings.set(b.localName, `symbol:${targetPath}#${defName ?? 'default'}`);
258
+ }
259
+ else {
260
+ bindings.set(b.localName, `symbol:${targetPath}#${b.importedName}`);
261
+ }
262
+ }
263
+ const out = [];
264
+ const seen = new Set();
265
+ for (const r of extracted.identifierReferences) {
266
+ let target = bindings.get(r.name);
267
+ if (!target)
268
+ target = localSymbolNamesInThisFile.get(r.name);
269
+ if (!target)
270
+ continue;
271
+ if (target === fileNodeId)
272
+ continue; // ignore self-loops
273
+ const kind = r.isCall ? EdgeKind.CallsSymbol : EdgeKind.ReferencesSymbol;
274
+ // De-dupe (target, kind) — many call sites of the same function on
275
+ // different lines collapse to a single file-level edge. We keep
276
+ // line info via the data of the first occurrence; the schema is
277
+ // ready for symbol-level edges in Wave 3 proper.
278
+ const edgeKey = `${target}|${kind}`;
279
+ if (seen.has(edgeKey))
280
+ continue;
281
+ seen.add(edgeKey);
282
+ out.push(buildEdge(fileNodeId, target, kind, { line: r.line }));
283
+ }
284
+ return out;
285
+ }
286
+ function makeFileNodeForNonTs(fp) {
287
+ const label = fp.path.split('/').pop() ?? fp.path;
288
+ const tags = [fp.language];
289
+ if (isTestPath(fp.path))
290
+ tags.push('test');
291
+ return {
292
+ id: fp.nodeId,
293
+ kind: NodeKind.File,
294
+ label,
295
+ path: fp.path,
296
+ tags,
297
+ data: {
298
+ language: fp.language,
299
+ sizeBytes: fp.sizeBytes,
300
+ sha1: fp.sha1,
301
+ hasDefaultExport: false,
302
+ exportCount: 0,
303
+ localCount: 0,
304
+ reExportCount: 0,
305
+ },
306
+ };
307
+ }
308
+ function makeFileNode(fp, idx) {
309
+ const label = fp.path.split('/').pop() ?? fp.path;
310
+ const tags = [];
311
+ if (isTestPath(fp.path))
312
+ tags.push('test');
313
+ if (isGenerated(idx))
314
+ tags.push('generated');
315
+ return {
316
+ id: fp.nodeId,
317
+ kind: NodeKind.File,
318
+ label,
319
+ path: fp.path,
320
+ tags: tags.length > 0 ? tags : undefined,
321
+ data: {
322
+ language: fp.language,
323
+ sizeBytes: fp.sizeBytes,
324
+ sha1: fp.sha1,
325
+ hasDefaultExport: idx.hasDefaultExport,
326
+ ...(idx.defaultExportName ? { defaultExportName: idx.defaultExportName } : {}),
327
+ exportCount: idx.exports.length,
328
+ localCount: idx.locals.length,
329
+ reExportCount: idx.reExports.length,
330
+ },
331
+ };
332
+ }
333
+ function makeSymbolNode(fp, name, declKind, visibility, line) {
334
+ return {
335
+ id: `symbol:${fp.path}#${name}`,
336
+ kind: NodeKind.Symbol,
337
+ label: name,
338
+ path: fp.path,
339
+ line,
340
+ data: {
341
+ declKind,
342
+ visibility,
343
+ isExported: visibility === SymbolVisibility.Export || visibility === SymbolVisibility.Default,
344
+ },
345
+ };
346
+ }
347
+ function buildEdge(from, to, kind, data) {
348
+ const id = createHash('sha1').update(`${from}|${to}|${kind}`).digest('hex');
349
+ return {
350
+ id,
351
+ from,
352
+ to,
353
+ kind,
354
+ source: EXTRACT_TS_FILE_SOURCE,
355
+ ...(data ? { data } : {}),
356
+ };
357
+ }
358
+ function isTestPath(rel) {
359
+ return /(?:^|\/)(?:__tests__|__mocks__)\//.test(rel) || /\.(?:test|spec)\.[tj]sx?$/.test(rel);
360
+ }
361
+ function isGenerated(idx) {
362
+ // Symbol-index doesn't preserve raw text; the file-level header check
363
+ // happens in the indexer where the buffer is in hand. Stays false here
364
+ // and is corrected by the indexer when needed.
365
+ return false;
366
+ }
367
+ function scanFileImports(text) {
368
+ const out = [];
369
+ collect(text, IMPORT_RE, 'static', out);
370
+ collect(text, SIDE_EFFECT_IMPORT_RE, 'side-effect', out);
371
+ collect(text, DYNAMIC_IMPORT_RE, 'dynamic', out);
372
+ collect(text, REQUIRE_RE, 'require', out);
373
+ // Dedupe identical (specifier, line, kind).
374
+ const seen = new Set();
375
+ const deduped = [];
376
+ for (const it of out) {
377
+ const k = `${it.kind}|${it.specifier}|${it.line}`;
378
+ if (seen.has(k))
379
+ continue;
380
+ seen.add(k);
381
+ deduped.push(it);
382
+ }
383
+ return deduped;
384
+ }
385
+ function collect(text, re, kind, out) {
386
+ re.lastIndex = 0;
387
+ let m;
388
+ while ((m = re.exec(text)) !== null) {
389
+ const specifier = m[1];
390
+ if (!specifier)
391
+ continue;
392
+ const line = lineFromOffset(text, m.index);
393
+ out.push({ specifier, line, kind });
394
+ }
395
+ }
396
+ function lineFromOffset(text, offset) {
397
+ let line = 1;
398
+ for (let i = 0; i < offset && i < text.length; i++) {
399
+ if (text.charCodeAt(i) === 10)
400
+ line += 1;
401
+ }
402
+ return line;
403
+ }
@@ -0,0 +1,41 @@
1
+ import type { IGraphManifest } from '../schema/manifest.js';
2
+ export interface IIncrementalUpdateOptions {
3
+ projectRoot: string;
4
+ /** Project-relative file paths (POSIX). */
5
+ changedFiles?: readonly string[];
6
+ /** Project-relative file paths (POSIX). */
7
+ deletedFiles?: readonly string[];
8
+ }
9
+ export interface IIncrementalUpdateResult {
10
+ manifest: IGraphManifest;
11
+ durationMs: number;
12
+ /** Files actually re-extracted (skipped == fingerprint unchanged). */
13
+ updated: readonly string[];
14
+ /** Files removed from the index. */
15
+ deleted: readonly string[];
16
+ /** Files marked as changed but whose fingerprint matched (no-op). */
17
+ skipped: readonly string[];
18
+ }
19
+ /**
20
+ * Apply a delta to the on-disk index.
21
+ *
22
+ * Strategy (MVP): load the snapshot, mutate in memory, rewrite the full
23
+ * store. Cheap for SharkCraft-sized indexes (a few MB on disk). Per-kind
24
+ * append/compact is the optimisation when the cold-rewrite cost is felt.
25
+ */
26
+ export declare function updateChanged(options: IIncrementalUpdateOptions): IIncrementalUpdateResult;
27
+ /**
28
+ * Helper: detect changed files by walking the project and comparing
29
+ * fingerprints against the stored snapshot. Used by
30
+ * `shrk graph index --changed` when no explicit file list is provided.
31
+ */
32
+ export declare function detectChangedAndDeleted(projectRoot: string): {
33
+ changed: readonly string[];
34
+ deleted: readonly string[];
35
+ };
36
+ /**
37
+ * Get the list of files changed since a git ref (e.g. `main`, `HEAD~5`,
38
+ * a tag). Returns project-relative POSIX paths. Errors → empty list.
39
+ */
40
+ export declare function changedFilesSince(projectRoot: string, ref: string): readonly string[];
41
+ //# sourceMappingURL=incremental-updater.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-updater.d.ts","sourceRoot":"","sources":["../../src/indexer/incremental-updater.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAyC5D,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,2CAA2C;IAC3C,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,oCAAoC;IACpC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,qEAAqE;IACrE,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,yBAAyB,GACjC,wBAAwB,CAwM1B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG;IAC5D,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,CAqEA;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAcrF"}