sverklo 0.2.12 → 0.2.14
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.
- package/README.md +102 -13
- package/dist/bin/sverklo.js +69 -1
- package/dist/bin/sverklo.js.map +1 -1
- package/dist/src/audit-prompt.d.ts +2 -0
- package/dist/src/audit-prompt.js +91 -0
- package/dist/src/audit-prompt.js.map +1 -0
- package/dist/src/doctor.js +55 -0
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/indexer/embedding-providers.d.ts +34 -0
- package/dist/src/indexer/embedding-providers.js +215 -0
- package/dist/src/indexer/embedding-providers.js.map +1 -0
- package/dist/src/indexer/embedding-providers.test.d.ts +1 -0
- package/dist/src/indexer/embedding-providers.test.js +83 -0
- package/dist/src/indexer/embedding-providers.test.js.map +1 -0
- package/dist/src/indexer/indexer-freshness.test.d.ts +1 -0
- package/dist/src/indexer/indexer-freshness.test.js +109 -0
- package/dist/src/indexer/indexer-freshness.test.js.map +1 -0
- package/dist/src/indexer/indexer-provider-integration.test.d.ts +1 -0
- package/dist/src/indexer/indexer-provider-integration.test.js +142 -0
- package/dist/src/indexer/indexer-provider-integration.test.js.map +1 -0
- package/dist/src/indexer/indexer.d.ts +44 -0
- package/dist/src/indexer/indexer.js +153 -10
- package/dist/src/indexer/indexer.js.map +1 -1
- package/dist/src/indexer/migration.test.d.ts +1 -0
- package/dist/src/indexer/migration.test.js +144 -0
- package/dist/src/indexer/migration.test.js.map +1 -0
- package/dist/src/indexer/symbol-extractor.js +17 -5
- package/dist/src/indexer/symbol-extractor.js.map +1 -1
- package/dist/src/indexer/symbol-extractor.test.d.ts +1 -0
- package/dist/src/indexer/symbol-extractor.test.js +88 -0
- package/dist/src/indexer/symbol-extractor.test.js.map +1 -0
- package/dist/src/indexer/watcher.js +4 -0
- package/dist/src/indexer/watcher.js.map +1 -1
- package/dist/src/memory/journal.d.ts +62 -0
- package/dist/src/memory/journal.js +136 -0
- package/dist/src/memory/journal.js.map +1 -0
- package/dist/src/memory/journal.test.d.ts +1 -0
- package/dist/src/memory/journal.test.js +98 -0
- package/dist/src/memory/journal.test.js.map +1 -0
- package/dist/src/modes.d.ts +16 -0
- package/dist/src/modes.js +104 -0
- package/dist/src/modes.js.map +1 -0
- package/dist/src/modes.test.d.ts +1 -0
- package/dist/src/modes.test.js +48 -0
- package/dist/src/modes.test.js.map +1 -0
- package/dist/src/search/hybrid-search.d.ts +22 -0
- package/dist/src/search/hybrid-search.js +169 -7
- package/dist/src/search/hybrid-search.js.map +1 -1
- package/dist/src/search/hybrid-search.test.d.ts +1 -0
- package/dist/src/search/hybrid-search.test.js +112 -0
- package/dist/src/search/hybrid-search.test.js.map +1 -0
- package/dist/src/search/token-budget.d.ts +1 -0
- package/dist/src/search/token-budget.js +34 -6
- package/dist/src/search/token-budget.js.map +1 -1
- package/dist/src/server/mcp-server.js +32 -25
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/server/tool-overrides.d.ts +16 -0
- package/dist/src/server/tool-overrides.js +102 -0
- package/dist/src/server/tool-overrides.js.map +1 -0
- package/dist/src/server/tool-overrides.test.d.ts +1 -0
- package/dist/src/server/tool-overrides.test.js +124 -0
- package/dist/src/server/tool-overrides.test.js.map +1 -0
- package/dist/src/server/tools/context-budget.test.d.ts +1 -0
- package/dist/src/server/tools/context-budget.test.js +150 -0
- package/dist/src/server/tools/context-budget.test.js.map +1 -0
- package/dist/src/server/tools/context.d.ts +11 -1
- package/dist/src/server/tools/context.js +202 -8
- package/dist/src/server/tools/context.js.map +1 -1
- package/dist/src/server/tools/diff-heuristics.d.ts +28 -0
- package/dist/src/server/tools/diff-heuristics.js +213 -0
- package/dist/src/server/tools/diff-heuristics.js.map +1 -0
- package/dist/src/server/tools/diff-heuristics.test.d.ts +1 -0
- package/dist/src/server/tools/diff-heuristics.test.js +151 -0
- package/dist/src/server/tools/diff-heuristics.test.js.map +1 -0
- package/dist/src/server/tools/find-references.d.ts +4 -0
- package/dist/src/server/tools/find-references.js +46 -6
- package/dist/src/server/tools/find-references.js.map +1 -1
- package/dist/src/server/tools/find-references.test.d.ts +1 -0
- package/dist/src/server/tools/find-references.test.js +98 -0
- package/dist/src/server/tools/find-references.test.js.map +1 -0
- package/dist/src/server/tools/forget.js +3 -0
- package/dist/src/server/tools/forget.js.map +1 -1
- package/dist/src/server/tools/index-status.js +16 -0
- package/dist/src/server/tools/index-status.js.map +1 -1
- package/dist/src/server/tools/lookup.js +21 -13
- package/dist/src/server/tools/lookup.js.map +1 -1
- package/dist/src/server/tools/lookup.test.d.ts +1 -0
- package/dist/src/server/tools/lookup.test.js +110 -0
- package/dist/src/server/tools/lookup.test.js.map +1 -0
- package/dist/src/server/tools/recall.d.ts +5 -1
- package/dist/src/server/tools/recall.js +68 -6
- package/dist/src/server/tools/recall.js.map +1 -1
- package/dist/src/server/tools/recall.test.d.ts +1 -0
- package/dist/src/server/tools/recall.test.js +116 -0
- package/dist/src/server/tools/recall.test.js.map +1 -0
- package/dist/src/server/tools/remember.js +16 -2
- package/dist/src/server/tools/remember.js.map +1 -1
- package/dist/src/server/tools/review-diff.js +31 -0
- package/dist/src/server/tools/review-diff.js.map +1 -1
- package/dist/src/server/tools/search.js +29 -4
- package/dist/src/server/tools/search.js.map +1 -1
- package/dist/src/server/tools/search.test.d.ts +1 -0
- package/dist/src/server/tools/search.test.js +118 -0
- package/dist/src/server/tools/search.test.js.map +1 -0
- package/dist/src/server/tools/tier.js +25 -1
- package/dist/src/server/tools/tier.js.map +1 -1
- package/dist/src/storage/chunk-store.d.ts +12 -0
- package/dist/src/storage/chunk-store.js +16 -0
- package/dist/src/storage/chunk-store.js.map +1 -1
- package/dist/src/storage/chunk-store.test.d.ts +1 -0
- package/dist/src/storage/chunk-store.test.js +69 -0
- package/dist/src/storage/chunk-store.test.js.map +1 -0
- package/dist/src/storage/database.d.ts +13 -0
- package/dist/src/storage/database.js +47 -0
- package/dist/src/storage/database.js.map +1 -1
- package/package.json +5 -2
|
@@ -12,39 +12,65 @@
|
|
|
12
12
|
// full — normal + dependency neighbours of top results
|
|
13
13
|
import { hybridSearch } from "../../search/hybrid-search.js";
|
|
14
14
|
import { handleRecall } from "./recall.js";
|
|
15
|
+
import { estimateTokens } from "../../utils/tokens.js";
|
|
15
16
|
export const contextTool = {
|
|
16
17
|
name: "sverklo_context",
|
|
17
18
|
description: "Umbrella context bundler. Give a task description and get a single curated bundle: " +
|
|
18
19
|
"codebase overview header, semantically relevant code, related symbols, and matching " +
|
|
19
20
|
"saved memories — in one round trip. Use this as the FIRST call when you start working " +
|
|
20
|
-
"on a new task and want to orient quickly.
|
|
21
|
-
"
|
|
21
|
+
"on a new task and want to orient quickly. " +
|
|
22
|
+
"PASS `budget` for a PageRank-pruned repo map fit to a token budget — the ideal way " +
|
|
23
|
+
"to give an agent a complete mental model of an unfamiliar codebase in one call.",
|
|
22
24
|
inputSchema: {
|
|
23
25
|
type: "object",
|
|
24
26
|
properties: {
|
|
25
27
|
task: {
|
|
26
28
|
type: "string",
|
|
27
|
-
description: "Free-form description of what you're trying to do, e.g. 'add rate limiting to the login endpoint' or 'understand how billing webhooks are processed'.",
|
|
29
|
+
description: "Free-form description of what you're trying to do, e.g. 'add rate limiting to the login endpoint' or 'understand how billing webhooks are processed'. When `budget` is set and no task is given, returns a pure PageRank-ordered repo map.",
|
|
28
30
|
},
|
|
29
31
|
detail_level: {
|
|
30
32
|
type: "string",
|
|
31
33
|
enum: ["minimal", "normal", "full"],
|
|
32
|
-
description: "How much to return. minimal=fast/cheap (good for snap orientation); normal=balanced (default); full=adds dependency neighbours.",
|
|
34
|
+
description: "How much to return. minimal=fast/cheap (good for snap orientation); normal=balanced (default); full=adds dependency neighbours. Ignored when `budget` is set.",
|
|
33
35
|
},
|
|
34
36
|
scope: {
|
|
35
37
|
type: "string",
|
|
36
38
|
description: "Optional path prefix to constrain the search (e.g. 'src/api/').",
|
|
37
39
|
},
|
|
40
|
+
budget: {
|
|
41
|
+
type: "number",
|
|
42
|
+
description: "When set, returns a PageRank-pruned repo map greedily filled to this token budget (inspired by aider's repo-map). Files are ordered by PageRank importance, optionally biased toward `task`. Only symbol signatures are rendered — use the atomic tools for full bodies. Typical values: 4000 (snap map), 8000 (full mental model), 16000 (deep context).",
|
|
43
|
+
},
|
|
44
|
+
exclude: {
|
|
45
|
+
type: "array",
|
|
46
|
+
items: { type: "string" },
|
|
47
|
+
description: "Path substrings to exclude from the repo map (e.g. ['test', 'migration']).",
|
|
48
|
+
},
|
|
38
49
|
},
|
|
39
|
-
required: ["task"],
|
|
40
50
|
},
|
|
41
51
|
};
|
|
42
52
|
export async function handleContext(indexer, args) {
|
|
43
|
-
const task = args.task?.trim();
|
|
44
|
-
if (!task)
|
|
45
|
-
return "Error: `task` is required.";
|
|
53
|
+
const task = args.task?.trim() || "";
|
|
46
54
|
const detail = (args.detail_level || "normal");
|
|
47
55
|
const scope = args.scope;
|
|
56
|
+
const budget = args.budget;
|
|
57
|
+
const exclude = args.exclude || [];
|
|
58
|
+
// ── Budget mode: PageRank-pruned repo map (issue #8) ──────────────
|
|
59
|
+
// When budget is set, we skip the standard search-based bundler and
|
|
60
|
+
// instead build a dense structural map greedily filled to the target
|
|
61
|
+
// token count. This is the "give the agent a complete mental model
|
|
62
|
+
// of an unfamiliar repo in one call" path — aider's repo-map pattern.
|
|
63
|
+
if (typeof budget === "number" && budget > 0) {
|
|
64
|
+
return await buildPrunedRepoMap(indexer, {
|
|
65
|
+
budget,
|
|
66
|
+
task,
|
|
67
|
+
scope,
|
|
68
|
+
exclude,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (!task) {
|
|
72
|
+
return "Error: `task` is required (unless you pass `budget` for a pure repo map).";
|
|
73
|
+
}
|
|
48
74
|
const searchLimit = detail === "minimal" ? 3 : detail === "normal" ? 5 : 8;
|
|
49
75
|
const memoryLimit = detail === "minimal" ? 2 : 5;
|
|
50
76
|
const tokenBudget = detail === "minimal" ? 1500 : detail === "normal" ? 3000 : 5000;
|
|
@@ -159,4 +185,172 @@ export async function handleContext(indexer, args) {
|
|
|
159
185
|
}
|
|
160
186
|
return parts.join("\n");
|
|
161
187
|
}
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
189
|
+
// Issue #8 — PageRank-pruned repo map fit to a token budget.
|
|
190
|
+
//
|
|
191
|
+
// Design (mirrors aider's repo-map, adapted to sverklo's symbol graph):
|
|
192
|
+
//
|
|
193
|
+
// 1. Rank files by PageRank DESC.
|
|
194
|
+
// 2. If a task was provided, run a semantic search and upweight files
|
|
195
|
+
// that contain strong matches. This lets the map center on the
|
|
196
|
+
// subsystem the user is working in, instead of always the global
|
|
197
|
+
// hub files.
|
|
198
|
+
// 3. Walk files in ranked order; for each, render signatures of its
|
|
199
|
+
// most important symbols (also ordered by PageRank proxy).
|
|
200
|
+
// 4. Greedily fill the budget. Stop the instant adding the next file
|
|
201
|
+
// would exceed the cap. The output is deterministic given the
|
|
202
|
+
// same index state and budget — important for caching.
|
|
203
|
+
//
|
|
204
|
+
// Determinism note: we break PageRank ties by file path to keep output
|
|
205
|
+
// stable across runs of the same input. This matters for caching and
|
|
206
|
+
// snapshot-testing downstream.
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
208
|
+
// Upper bound on the repo-map budget. Anything above 32k tokens is
|
|
209
|
+
// almost certainly a mistake — the resulting map won't fit in any
|
|
210
|
+
// current model's context window and the wall-clock cost of walking
|
|
211
|
+
// every file climbs linearly. We cap and emit a warning in the
|
|
212
|
+
// output so the caller knows we trimmed.
|
|
213
|
+
const MAX_REPO_MAP_BUDGET = 32000;
|
|
214
|
+
const MIN_REPO_MAP_BUDGET = 500;
|
|
215
|
+
async function buildPrunedRepoMap(indexer, opts) {
|
|
216
|
+
let { budget } = opts;
|
|
217
|
+
const { task, scope, exclude } = opts;
|
|
218
|
+
// Clamp the budget to sane bounds. Sub-500 can't even fit a header
|
|
219
|
+
// + one file meaningfully; over 32k is nearly always unintentional.
|
|
220
|
+
let budgetWarning = "";
|
|
221
|
+
if (budget > MAX_REPO_MAP_BUDGET) {
|
|
222
|
+
budgetWarning = `_Budget ${budget} exceeded max ${MAX_REPO_MAP_BUDGET} — clamped. For larger maps, use multiple scoped calls._\n\n`;
|
|
223
|
+
budget = MAX_REPO_MAP_BUDGET;
|
|
224
|
+
}
|
|
225
|
+
else if (budget < MIN_REPO_MAP_BUDGET) {
|
|
226
|
+
return (`Budget too small: ${budget}. Minimum is ${MIN_REPO_MAP_BUDGET} tokens — ` +
|
|
227
|
+
`below that the repo map can't fit a useful number of files. ` +
|
|
228
|
+
`Try \`budget: ${MIN_REPO_MAP_BUDGET}\` for a snap view.`);
|
|
229
|
+
}
|
|
230
|
+
// Pull all files sorted by PageRank. file_store.getAll() already
|
|
231
|
+
// returns them in pagerank DESC order — see storage/file-store.ts.
|
|
232
|
+
let files = indexer.fileStore.getAll();
|
|
233
|
+
// Apply scope prefix and exclude substrings.
|
|
234
|
+
if (scope) {
|
|
235
|
+
files = files.filter((f) => f.path.startsWith(scope));
|
|
236
|
+
}
|
|
237
|
+
if (exclude.length > 0) {
|
|
238
|
+
files = files.filter((f) => !exclude.some((ex) => f.path.includes(ex)));
|
|
239
|
+
}
|
|
240
|
+
// Task-biased reranking: if the caller gave us a task, run a search
|
|
241
|
+
// and boost files that contain strong matches. We keep the PageRank
|
|
242
|
+
// signal as the primary order and only use the task signal as a
|
|
243
|
+
// tiebreaker / boost — so the output is still dominated by the
|
|
244
|
+
// structural importance of the codebase, not by query noise.
|
|
245
|
+
const taskBoost = new Map();
|
|
246
|
+
if (task) {
|
|
247
|
+
try {
|
|
248
|
+
const hits = await hybridSearch(indexer, {
|
|
249
|
+
query: task,
|
|
250
|
+
tokenBudget: 50000, // ask for a lot so boost is meaningful
|
|
251
|
+
scope,
|
|
252
|
+
type: "any",
|
|
253
|
+
});
|
|
254
|
+
// Accumulate score per file across all hits.
|
|
255
|
+
for (const h of hits) {
|
|
256
|
+
taskBoost.set(h.chunk.file_id, (taskBoost.get(h.chunk.file_id) || 0) + h.score);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Fall back to pure PageRank if search fails — never block the map.
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// Stable ranking: compose PageRank with the task boost, break ties
|
|
264
|
+
// by path so the same input always produces the same output.
|
|
265
|
+
const ranked = [...files].sort((a, b) => {
|
|
266
|
+
const ba = (taskBoost.get(a.id) || 0) * 0.5 + a.pagerank;
|
|
267
|
+
const bb = (taskBoost.get(b.id) || 0) * 0.5 + b.pagerank;
|
|
268
|
+
if (bb !== ba)
|
|
269
|
+
return bb - ba;
|
|
270
|
+
return a.path.localeCompare(b.path);
|
|
271
|
+
});
|
|
272
|
+
// Header first so the caller can still see the budget and ordering
|
|
273
|
+
// even when the budget is tiny and zero files fit.
|
|
274
|
+
const parts = [];
|
|
275
|
+
parts.push(`# Repo map${task ? ` · centered on: ${task}` : ""}${scope ? ` · scope: ${scope}` : ""}`);
|
|
276
|
+
parts.push(`_Budget: ${budget} tokens · ordered by PageRank${task ? " + task relevance" : ""}._`);
|
|
277
|
+
if (budgetWarning)
|
|
278
|
+
parts.push(budgetWarning);
|
|
279
|
+
parts.push("");
|
|
280
|
+
// Remaining budget after the header. 10% cushion for the trailing
|
|
281
|
+
// footer text and any rendering slack. Token estimates are rough —
|
|
282
|
+
// we use the same estimator as the indexer (chars / 3.5).
|
|
283
|
+
const headerCost = estimateTokens(parts.join("\n"));
|
|
284
|
+
let remaining = budget - headerCost - 100;
|
|
285
|
+
let filesRendered = 0;
|
|
286
|
+
let filesSkipped = 0;
|
|
287
|
+
for (const file of ranked) {
|
|
288
|
+
if (remaining <= 0) {
|
|
289
|
+
filesSkipped = ranked.length - filesRendered;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
// Pull the file's symbols. We only render symbol signatures
|
|
293
|
+
// (cheap) plus the symbol's name and type. Full bodies would
|
|
294
|
+
// blow the budget instantly — the point of a map is to be a
|
|
295
|
+
// legend, not the terrain.
|
|
296
|
+
const chunks = indexer.chunkStore
|
|
297
|
+
.getByFile(file.id)
|
|
298
|
+
.filter((c) => c.name);
|
|
299
|
+
if (chunks.length === 0)
|
|
300
|
+
continue;
|
|
301
|
+
// Render the file's entry. Symbols are ordered as they appear in
|
|
302
|
+
// the file (by start_line). Top-level types/classes come first in
|
|
303
|
+
// most codebases so this matches how a human skims.
|
|
304
|
+
const fileHeader = `## \`${file.path}\` · PR ${file.pagerank.toFixed(3)} · ${file.language || "?"}`;
|
|
305
|
+
const fileLines = [fileHeader];
|
|
306
|
+
// Soft cap on symbols-per-file so one massive file doesn't eat the
|
|
307
|
+
// whole budget. Files with >40 symbols render as "+ N more".
|
|
308
|
+
const CAP_PER_FILE = 40;
|
|
309
|
+
const shown = chunks.slice(0, CAP_PER_FILE);
|
|
310
|
+
for (const c of shown) {
|
|
311
|
+
const sig = c.signature ? c.signature.trim() : `${c.type} ${c.name}`;
|
|
312
|
+
// Keep each line short — the map should scan like an outline.
|
|
313
|
+
const truncated = sig.length > 110 ? sig.slice(0, 107) + "..." : sig;
|
|
314
|
+
fileLines.push(`- ${truncated}`);
|
|
315
|
+
}
|
|
316
|
+
if (chunks.length > CAP_PER_FILE) {
|
|
317
|
+
fileLines.push(`- _...and ${chunks.length - CAP_PER_FILE} more symbols_`);
|
|
318
|
+
}
|
|
319
|
+
const entry = fileLines.join("\n") + "\n";
|
|
320
|
+
const cost = estimateTokens(entry);
|
|
321
|
+
// If a single file's entry exceeds the remaining budget, render a
|
|
322
|
+
// truncated version (just the header + symbol count) so the caller
|
|
323
|
+
// still knows the file exists. Never render a partial entry with
|
|
324
|
+
// half its symbols cut off — that's confusing.
|
|
325
|
+
if (cost > remaining) {
|
|
326
|
+
const fallback = `## \`${file.path}\` · PR ${file.pagerank.toFixed(3)}\n` +
|
|
327
|
+
`- _${chunks.length} symbols (budget exhausted — use sverklo_lookup for details)_\n`;
|
|
328
|
+
const fallbackCost = estimateTokens(fallback);
|
|
329
|
+
if (fallbackCost <= remaining) {
|
|
330
|
+
parts.push(fallback);
|
|
331
|
+
remaining -= fallbackCost;
|
|
332
|
+
filesRendered++;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
filesSkipped = ranked.length - filesRendered;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
parts.push(entry);
|
|
341
|
+
remaining -= cost;
|
|
342
|
+
filesRendered++;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Footer
|
|
346
|
+
parts.push("");
|
|
347
|
+
parts.push(`_Rendered ${filesRendered} of ${ranked.length} files` +
|
|
348
|
+
(filesSkipped > 0
|
|
349
|
+
? ` (${filesSkipped} skipped — budget exhausted; raise \`budget\` to see more)_`
|
|
350
|
+
: "_"));
|
|
351
|
+
if (filesRendered > 0) {
|
|
352
|
+
parts.push("_Use `sverklo_lookup name:<symbol>` or `sverklo_search query:<...>` to drill into any symbol._");
|
|
353
|
+
}
|
|
354
|
+
return parts.join("\n");
|
|
355
|
+
}
|
|
162
356
|
//# sourceMappingURL=context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../src/server/tools/context.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../src/server/tools/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface DiffHunk {
|
|
2
|
+
filePath: string;
|
|
3
|
+
oldStart: number;
|
|
4
|
+
newStart: number;
|
|
5
|
+
/** Every line in the hunk, including context. Each entry is "+ ", "- ", " " followed by content. */
|
|
6
|
+
lines: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface HeuristicFinding {
|
|
9
|
+
heuristic: string;
|
|
10
|
+
severity: "high" | "medium" | "low";
|
|
11
|
+
file: string;
|
|
12
|
+
line: number;
|
|
13
|
+
snippet: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function findUnguardedStreamCalls(hunks: DiffHunk[]): HeuristicFinding[];
|
|
17
|
+
export declare const ALL_HEURISTICS: ((hunks: DiffHunk[]) => HeuristicFinding[])[];
|
|
18
|
+
export declare function runAllHeuristics(hunks: DiffHunk[]): HeuristicFinding[];
|
|
19
|
+
/**
|
|
20
|
+
* Parse a unified-diff string (output of `git diff -U5 ref`) into hunks.
|
|
21
|
+
* Very permissive — anything that doesn't match the expected header is
|
|
22
|
+
* skipped rather than raising.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseUnifiedDiff(diffText: string): DiffHunk[];
|
|
25
|
+
/**
|
|
26
|
+
* Convenience: pull a unified diff from git and parse it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getDiffHunks(rootPath: string, ref: string): DiffHunk[];
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Structural heuristics that scan the actual diff text for specific classes
|
|
2
|
+
// of risk that symbol-level analysis alone will not catch. Kept as a
|
|
3
|
+
// separate module so new heuristics can be added without touching the
|
|
4
|
+
// main review-diff handler, and so each heuristic is unit-testable in
|
|
5
|
+
// isolation.
|
|
6
|
+
//
|
|
7
|
+
// Heuristics are *heuristics* — they trade some false positives for
|
|
8
|
+
// recall on real-world bugs. Each one attaches a short "why" so a human
|
|
9
|
+
// reviewer can quickly dismiss a false flag.
|
|
10
|
+
//
|
|
11
|
+
// Current heuristics:
|
|
12
|
+
// - unguarded-stream-call: a new call site introduced inside a stream
|
|
13
|
+
// pipeline (.map / .forEach / .flatMap / .filter / .reduce / etc.)
|
|
14
|
+
// where the enclosing function has no visible try-catch. One
|
|
15
|
+
// uncaught RuntimeException on a single element will abort the
|
|
16
|
+
// entire stream — a real outage risk on production read paths.
|
|
17
|
+
// Tracked in github.com/sverklo/sverklo/issues/5.
|
|
18
|
+
//
|
|
19
|
+
// Adding a heuristic:
|
|
20
|
+
// 1. Write a pure function that takes a DiffHunk[] and returns
|
|
21
|
+
// HeuristicFinding[]. No I/O, no git, no filesystem — the caller
|
|
22
|
+
// is responsible for producing hunks.
|
|
23
|
+
// 2. Register it in ALL_HEURISTICS below.
|
|
24
|
+
// 3. Unit-test it with representative fixtures.
|
|
25
|
+
import { execSync } from "node:child_process";
|
|
26
|
+
// ────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Heuristic 1 — unguarded stream-pipeline call
|
|
28
|
+
// ────────────────────────────────────────────────────────────────────
|
|
29
|
+
// Stream pipeline markers we care about. The list is deliberately
|
|
30
|
+
// multi-language: Java / TS / Kotlin / Scala all share the same shape.
|
|
31
|
+
const STREAM_METHODS = [
|
|
32
|
+
"map",
|
|
33
|
+
"forEach",
|
|
34
|
+
"flatMap",
|
|
35
|
+
"filter",
|
|
36
|
+
"reduce",
|
|
37
|
+
"mapToInt",
|
|
38
|
+
"mapToLong",
|
|
39
|
+
"mapToDouble",
|
|
40
|
+
"peek",
|
|
41
|
+
"collect",
|
|
42
|
+
];
|
|
43
|
+
// A line is "entering a stream pipeline" if it matches `.<method>(`
|
|
44
|
+
// where <method> is one of the above. We accept optional whitespace.
|
|
45
|
+
const STREAM_ENTRY_RE = new RegExp(`\\.(${STREAM_METHODS.join("|")})\\s*\\(`);
|
|
46
|
+
// A line looks like a call that may throw if it contains `.<name>(`
|
|
47
|
+
// for an identifier-shaped name. This is broad on purpose — most method
|
|
48
|
+
// calls *can* throw, so any introduced call inside a stream pipeline
|
|
49
|
+
// without a try-catch is the pattern we want to flag.
|
|
50
|
+
const ANY_CALL_RE = /\b\w+\s*\(/;
|
|
51
|
+
// A try-catch is "visible" in the hunk if we see a `try` or `catch`
|
|
52
|
+
// token on any context line. This is a conservative proxy for "the
|
|
53
|
+
// enclosing method catches its exceptions" — an AST-based check would
|
|
54
|
+
// be more accurate but would require parsing the entire file per diff.
|
|
55
|
+
const TRY_OR_CATCH_RE = /\b(try|catch)\b/;
|
|
56
|
+
export function findUnguardedStreamCalls(hunks) {
|
|
57
|
+
const findings = [];
|
|
58
|
+
for (const hunk of hunks) {
|
|
59
|
+
// Is there a visible try / catch anywhere in the hunk's context?
|
|
60
|
+
// If so, skip the whole hunk — the enclosing method probably
|
|
61
|
+
// catches. False negatives here are fine; we care about loud wins.
|
|
62
|
+
const hasTryCatch = hunk.lines.some((l) => TRY_OR_CATCH_RE.test(l));
|
|
63
|
+
if (hasTryCatch)
|
|
64
|
+
continue;
|
|
65
|
+
// Walk the hunk line by line. Track whether we've seen a stream
|
|
66
|
+
// entry on a preceding context/added line, and whether we're still
|
|
67
|
+
// "inside" it (approximated by nesting depth on the same or next
|
|
68
|
+
// lines in the hunk).
|
|
69
|
+
let insideStreamDepth = 0;
|
|
70
|
+
let streamFileLine = hunk.newStart;
|
|
71
|
+
let currentNewLine = hunk.newStart;
|
|
72
|
+
for (const rawLine of hunk.lines) {
|
|
73
|
+
const prefix = rawLine.charAt(0);
|
|
74
|
+
const content = rawLine.slice(1);
|
|
75
|
+
// Track whether we're in a stream block, using a crude brace count
|
|
76
|
+
// scoped to the hunk. This misses pipelines that span more than the
|
|
77
|
+
// hunk window — we accept that as a recall tradeoff.
|
|
78
|
+
if (STREAM_ENTRY_RE.test(content)) {
|
|
79
|
+
insideStreamDepth = 1;
|
|
80
|
+
streamFileLine = currentNewLine;
|
|
81
|
+
}
|
|
82
|
+
else if (insideStreamDepth > 0) {
|
|
83
|
+
const opens = (content.match(/\(/g) || []).length;
|
|
84
|
+
const closes = (content.match(/\)/g) || []).length;
|
|
85
|
+
insideStreamDepth += opens - closes;
|
|
86
|
+
if (insideStreamDepth < 0)
|
|
87
|
+
insideStreamDepth = 0;
|
|
88
|
+
}
|
|
89
|
+
// Only flag on added lines (new risks), not context. Context
|
|
90
|
+
// lines merely help us track state.
|
|
91
|
+
if (prefix === "+" && insideStreamDepth > 0 && ANY_CALL_RE.test(content)) {
|
|
92
|
+
// Skip the stream-entry line itself — we only flag the *body*
|
|
93
|
+
// calls inside the pipeline, not the pipeline declaration.
|
|
94
|
+
if (!STREAM_ENTRY_RE.test(content)) {
|
|
95
|
+
findings.push({
|
|
96
|
+
heuristic: "unguarded-stream-call",
|
|
97
|
+
severity: "medium",
|
|
98
|
+
file: hunk.filePath,
|
|
99
|
+
line: currentNewLine,
|
|
100
|
+
snippet: content.trim().slice(0, 120),
|
|
101
|
+
message: "New call inside a stream pipeline with no visible try-catch in the hunk. " +
|
|
102
|
+
"A single RuntimeException on one element will abort the entire pipeline — " +
|
|
103
|
+
"on a production read path this is an outage. Wrap the lambda body in " +
|
|
104
|
+
"try-catch or pre-filter elements that could throw.",
|
|
105
|
+
});
|
|
106
|
+
// Flag once per stream block to avoid spamming — a block with
|
|
107
|
+
// ten calls is one finding, not ten.
|
|
108
|
+
insideStreamDepth = 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Advance the running new-file line counter. Context and added
|
|
112
|
+
// lines both consume new-file line numbers; removed lines do not.
|
|
113
|
+
if (prefix === "+" || prefix === " ")
|
|
114
|
+
currentNewLine++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return findings;
|
|
118
|
+
}
|
|
119
|
+
// ────────────────────────────────────────────────────────────────────
|
|
120
|
+
// Registry + driver
|
|
121
|
+
// ────────────────────────────────────────────────────────────────────
|
|
122
|
+
export const ALL_HEURISTICS = [
|
|
123
|
+
findUnguardedStreamCalls,
|
|
124
|
+
];
|
|
125
|
+
export function runAllHeuristics(hunks) {
|
|
126
|
+
const all = [];
|
|
127
|
+
for (const fn of ALL_HEURISTICS) {
|
|
128
|
+
try {
|
|
129
|
+
all.push(...fn(hunks));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// One broken heuristic must not take down review. Swallow and move on.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return all;
|
|
136
|
+
}
|
|
137
|
+
// ────────────────────────────────────────────────────────────────────
|
|
138
|
+
// Parser — turn `git diff --unified=N` text into DiffHunk[]
|
|
139
|
+
// ────────────────────────────────────────────────────────────────────
|
|
140
|
+
/**
|
|
141
|
+
* Parse a unified-diff string (output of `git diff -U5 ref`) into hunks.
|
|
142
|
+
* Very permissive — anything that doesn't match the expected header is
|
|
143
|
+
* skipped rather than raising.
|
|
144
|
+
*/
|
|
145
|
+
export function parseUnifiedDiff(diffText) {
|
|
146
|
+
const hunks = [];
|
|
147
|
+
const lines = diffText.split("\n");
|
|
148
|
+
let currentFile = null;
|
|
149
|
+
let currentHunk = null;
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
// New file header: "diff --git a/foo b/foo"
|
|
152
|
+
if (line.startsWith("diff --git ")) {
|
|
153
|
+
if (currentHunk) {
|
|
154
|
+
hunks.push(currentHunk);
|
|
155
|
+
currentHunk = null;
|
|
156
|
+
}
|
|
157
|
+
currentFile = null;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// "+++ b/path" gives us the new file path
|
|
161
|
+
if (line.startsWith("+++ ")) {
|
|
162
|
+
const path = line.slice(4).replace(/^b\//, "").trim();
|
|
163
|
+
currentFile = path === "/dev/null" ? null : path;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Hunk header: "@@ -12,5 +34,7 @@ optional-context"
|
|
167
|
+
if (line.startsWith("@@ ")) {
|
|
168
|
+
if (currentHunk)
|
|
169
|
+
hunks.push(currentHunk);
|
|
170
|
+
const match = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
171
|
+
if (match && currentFile) {
|
|
172
|
+
currentHunk = {
|
|
173
|
+
filePath: currentFile,
|
|
174
|
+
oldStart: parseInt(match[1], 10),
|
|
175
|
+
newStart: parseInt(match[2], 10),
|
|
176
|
+
lines: [],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
currentHunk = null;
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
// Body lines: "+", "-", or " " prefixed
|
|
185
|
+
if (currentHunk && (line.startsWith("+") || line.startsWith("-") || line.startsWith(" "))) {
|
|
186
|
+
// Skip the file-metadata lines "+++" / "---" which we already handled above
|
|
187
|
+
if (line.startsWith("+++") || line.startsWith("---"))
|
|
188
|
+
continue;
|
|
189
|
+
currentHunk.lines.push(line);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (currentHunk)
|
|
193
|
+
hunks.push(currentHunk);
|
|
194
|
+
return hunks;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Convenience: pull a unified diff from git and parse it.
|
|
198
|
+
*/
|
|
199
|
+
export function getDiffHunks(rootPath, ref) {
|
|
200
|
+
try {
|
|
201
|
+
const out = execSync(`git diff --unified=10 ${ref}`, {
|
|
202
|
+
cwd: rootPath,
|
|
203
|
+
encoding: "utf-8",
|
|
204
|
+
timeout: 8000,
|
|
205
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
206
|
+
});
|
|
207
|
+
return parseUnifiedDiff(out);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=diff-heuristics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-heuristics.js","sourceRoot":"","sources":["../../../../src/server/tools/diff-heuristics.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,qEAAqE;AACrE,sEAAsE;AACtE,sEAAsE;AACtE,aAAa;AACb,EAAE;AACF,oEAAoE;AACpE,wEAAwE;AACxE,6CAA6C;AAC7C,EAAE;AACF,sBAAsB;AACtB,wEAAwE;AACxE,uEAAuE;AACvE,iEAAiE;AACjE,mEAAmE;AACnE,mEAAmE;AACnE,sDAAsD;AACtD,EAAE;AACF,sBAAsB;AACtB,iEAAiE;AACjE,sEAAsE;AACtE,2CAA2C;AAC3C,4CAA4C;AAC5C,kDAAkD;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAmB9C,uEAAuE;AACvE,+CAA+C;AAC/C,uEAAuE;AAEvE,kEAAkE;AAClE,uEAAuE;AACvE,MAAM,cAAc,GAAG;IACrB,KAAK;IACL,SAAS;IACT,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,WAAW;IACX,aAAa;IACb,MAAM;IACN,SAAS;CACV,CAAC;AAEF,oEAAoE;AACpE,qEAAqE;AACrE,MAAM,eAAe,GAAG,IAAI,MAAM,CAChC,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAC1C,CAAC;AAEF,oEAAoE;AACpE,wEAAwE;AACxE,qEAAqE;AACrE,sDAAsD;AACtD,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,oEAAoE;AACpE,mEAAmE;AACnE,sEAAsE;AACtE,uEAAuE;AACvE,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAE1C,MAAM,UAAU,wBAAwB,CAAC,KAAiB;IACxD,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,iEAAiE;QACjE,6DAA6D;QAC7D,mEAAmE;QACnE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,WAAW;YAAE,SAAS;QAE1B,gEAAgE;QAChE,mEAAmE;QACnE,iEAAiE;QACjE,sBAAsB;QACtB,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAC1B,IAAI,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC;QACnC,IAAI,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC;QAEnC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEjC,mEAAmE;YACnE,oEAAoE;YACpE,qDAAqD;YACrD,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAClC,iBAAiB,GAAG,CAAC,CAAC;gBACtB,cAAc,GAAG,cAAc,CAAC;YAClC,CAAC;iBAAM,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;gBACjC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAClD,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBACnD,iBAAiB,IAAI,KAAK,GAAG,MAAM,CAAC;gBACpC,IAAI,iBAAiB,GAAG,CAAC;oBAAE,iBAAiB,GAAG,CAAC,CAAC;YACnD,CAAC;YAED,6DAA6D;YAC7D,oCAAoC;YACpC,IAAI,MAAM,KAAK,GAAG,IAAI,iBAAiB,GAAG,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzE,8DAA8D;gBAC9D,2DAA2D;gBAC3D,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBACnC,QAAQ,CAAC,IAAI,CAAC;wBACZ,SAAS,EAAE,uBAAuB;wBAClC,QAAQ,EAAE,QAAQ;wBAClB,IAAI,EAAE,IAAI,CAAC,QAAQ;wBACnB,IAAI,EAAE,cAAc;wBACpB,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;wBACrC,OAAO,EACL,2EAA2E;4BAC3E,4EAA4E;4BAC5E,uEAAuE;4BACvE,oDAAoD;qBACvD,CAAC,CAAC;oBACH,8DAA8D;oBAC9D,qCAAqC;oBACrC,iBAAiB,GAAG,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;YAED,+DAA+D;YAC/D,kEAAkE;YAClE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;gBAAE,cAAc,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uEAAuE;AACvE,oBAAoB;AACpB,uEAAuE;AAEvE,MAAM,CAAC,MAAM,cAAc,GAAkD;IAC3E,wBAAwB;CACzB,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,GAAG,GAAuB,EAAE,CAAC;IACnC,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,4DAA4D;AAC5D,uEAAuE;AAEvE;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,WAAW,GAAoB,IAAI,CAAC;IAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,4CAA4C;QAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,IAAI,WAAW,EAAE,CAAC;gBAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACxB,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YACD,WAAW,GAAG,IAAI,CAAC;YACnB,SAAS;QACX,CAAC;QAED,0CAA0C;QAC1C,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtD,WAAW,GAAG,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACjD,SAAS;QACX,CAAC;QAED,oDAAoD;QACpD,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,IAAI,WAAW;gBAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACpE,IAAI,KAAK,IAAI,WAAW,EAAE,CAAC;gBACzB,WAAW,GAAG;oBACZ,QAAQ,EAAE,WAAW;oBACrB,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAChC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAChC,KAAK,EAAE,EAAE;iBACV,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YACD,SAAS;QACX,CAAC;QAED,wCAAwC;QACxC,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC1F,4EAA4E;YAC5E,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC/D,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAEzC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,GAAW;IACxD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,yBAAyB,GAAG,EAAE,EAAE;YACnD,GAAG,EAAE,QAAQ;YACb,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;SAC5B,CAAC,CAAC;QACH,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|