dsh-codegraph-visualizer 0.1.1 → 0.3.1

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 (58) hide show
  1. package/README.md +391 -226
  2. package/assets/icon-180.png +0 -0
  3. package/assets/icon.svg +30 -0
  4. package/cordis.patch.yml +22 -17
  5. package/dist/CodeGraphAdapter-BNIK0xHU.js +422 -0
  6. package/dist/CodeGraphAdapter-BNIK0xHU.js.map +1 -0
  7. package/dist/CodeGraphAdapter-CXj1hbdm.js +10 -0
  8. package/dist/FileSystemAdapter-CgasSGbB.js +524 -0
  9. package/dist/FileSystemAdapter-CgasSGbB.js.map +1 -0
  10. package/dist/LensAdapter-C9Saf_i_.js +89 -0
  11. package/dist/LensAdapter-C9Saf_i_.js.map +1 -0
  12. package/dist/Logger-DUIJSeNu.js +80 -0
  13. package/dist/Logger-DUIJSeNu.js.map +1 -0
  14. package/dist/TreeSitterAdapter-C-EWy8br.js +631 -0
  15. package/dist/TreeSitterAdapter-C-EWy8br.js.map +1 -0
  16. package/dist/TreeSitterAdapter-C6FcO2_s.js +8 -0
  17. package/dist/client/index.css +8963 -1724
  18. package/dist/client/index.css.map +1 -0
  19. package/dist/client/index.d.ts +634 -0
  20. package/dist/client/index.d.ts.map +1 -0
  21. package/dist/client/index.esm.js +57197 -20713
  22. package/dist/client/index.js.map +1 -0
  23. package/dist/client/{index.js → worker/layoutWorker.js} +7657 -8787
  24. package/dist/client.js +90426 -0
  25. package/dist/codeGraphDb-B-vpZmpm.js +9 -0
  26. package/dist/codeGraphDb-T6K3dK5D.js +357 -0
  27. package/dist/codeGraphDb-T6K3dK5D.js.map +1 -0
  28. package/dist/codeGraphRows-BhZX34uB.js +6 -0
  29. package/dist/codeGraphRows-C7hqu35E.js +246 -0
  30. package/dist/codeGraphRows-C7hqu35E.js.map +1 -0
  31. package/dist/constants-CQq93l3e.js +399 -0
  32. package/dist/constants-CQq93l3e.js.map +1 -0
  33. package/dist/graphFingerprint-CeARo7jG.js +4 -0
  34. package/dist/graphFingerprint-PuxORZUF.js +277 -0
  35. package/dist/graphFingerprint-PuxORZUF.js.map +1 -0
  36. package/dist/graphMetrics-C0F30730.js +555 -0
  37. package/dist/graphMetrics-C0F30730.js.map +1 -0
  38. package/dist/index.d.ts +572 -9
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +14819 -2009
  41. package/dist/index.js.map +1 -1
  42. package/dist/inheritanceEnhancer-Bi3UoYJS.js +291 -0
  43. package/dist/inheritanceEnhancer-Bi3UoYJS.js.map +1 -0
  44. package/dist/layerUtils-DDm-EsaD.js +72 -0
  45. package/dist/layerUtils-DDm-EsaD.js.map +1 -0
  46. package/dist/nativeIndex-CABbgERW.js +347 -0
  47. package/dist/nativeIndex-CABbgERW.js.map +1 -0
  48. package/dist/nativeIndex-esaDOsq8.js +5 -0
  49. package/dist/treeSitterExtractors-B1ybKNrC.js +394 -0
  50. package/dist/treeSitterExtractors-B1ybKNrC.js.map +1 -0
  51. package/dist/types-BUDxI_00.js +23 -0
  52. package/dist/types-BUDxI_00.js.map +1 -0
  53. package/locale/en.json +6 -0
  54. package/locale/zh-CN.json +6 -0
  55. package/locale/zh.json +6 -0
  56. package/package.json +100 -20
  57. package/dist/client-cjs/index.cjs +0 -44283
  58. package/dist/client-cjs/index.css +0 -2404
@@ -0,0 +1,246 @@
1
+ import { EdgeId, NodeId } from "./types-BUDxI_00.js";
2
+ import { CODEGRAPH_ADAPTER } from "./constants-CQq93l3e.js";
3
+ import { inferLayerFromPath } from "./layerUtils-DDm-EsaD.js";
4
+
5
+ //#region src/adapters/codeGraphRows.ts
6
+ /**
7
+
8
+ * edges 行的共享 SELECT 列清单(不含 WHERE/ORDER BY,调用方按需拼接)。
9
+
10
+ *
11
+
12
+ * 单一来源的理由:8 处 `SELECT … FROM edges` 曾各自手写列清单——cycle-119 就是因为
13
+
14
+ * 「读侧漏了 `metadata` 列」而 `'top-k-weight'` 契约空转两轮。列变了改一处,
15
+
16
+ * 各读取面跟着编译期报错(解构不存在的键)而不是静默丢数据。
17
+
18
+ */
19
+ const EDGE_ROW_COLUMNS = "id, source, target, kind, metadata, line";
20
+ /**
21
+
22
+ * 从 `metadata` JSON 文本解析置信度([0,1] 数值),任何不合法形态返回 `undefined`。
23
+
24
+ *
25
+
26
+ * 为什么不 throw:单行 metadata 损坏不该让整页边读取失败(validateEdgeRows 的
27
+
28
+ * 「跳过坏行」是同一哲学);也不该把 0.9 解析成 `undefined` 再被 `?? 1` 兜底成
29
+
30
+ * 「完全可信」——所以越界值与缺失值**同型**(undefined),消费侧用
31
+
32
+ * `weight === undefined` 显式分支,没有 `?? 默认值` 的静默兜底路径。
33
+
34
+ */
35
+ function parseEdgeConfidence(metadata) {
36
+ if (typeof metadata !== "string" || metadata.length === 0) return void 0;
37
+ let parsed;
38
+ try {
39
+ parsed = JSON.parse(metadata);
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ if (typeof parsed !== "object" || parsed === null) return void 0;
44
+ const confidence = parsed.confidence;
45
+ if (typeof confidence !== "number" || !Number.isFinite(confidence)) return void 0;
46
+ if (confidence < 0 || confidence > 1) return void 0;
47
+ return confidence;
48
+ }
49
+ const NODE_KIND_MAP = {
50
+ function: "function",
51
+ method: "function",
52
+ class: "class",
53
+ interface: "interface",
54
+ type_alias: "type",
55
+ constant: "variable",
56
+ variable: "variable",
57
+ property: "variable",
58
+ file: "module",
59
+ import: "module",
60
+ module: "module"
61
+ };
62
+ const EDGE_KIND_MAP = {
63
+ call: "call",
64
+ calls: "call",
65
+ import: "import",
66
+ imports: "import",
67
+ extend: "extend",
68
+ extends: "extend",
69
+ implement: "implement",
70
+ implements: "implement",
71
+ contains: "contains",
72
+ dependency: "dependency",
73
+ depends: "dependency"
74
+ };
75
+ const FILTERED_EDGE_KINDS = new Set(["uses_var", "func_imports_local"]);
76
+ const NOISE_PATH_SEGMENTS = [
77
+ "node_modules",
78
+ ".git",
79
+ "vendor",
80
+ "__pycache__",
81
+ ".venv",
82
+ "dist",
83
+ "build",
84
+ ".next",
85
+ ".nuxt",
86
+ ".cache",
87
+ ".turbo",
88
+ "coverage",
89
+ ".pytest_cache",
90
+ ".cycles",
91
+ ".codegraph-visualizer"
92
+ ];
93
+ function isNoisePath(filePath) {
94
+ const segments = filePath.split(/[\\/]/);
95
+ for (const seg of NOISE_PATH_SEGMENTS) if (segments.includes(seg)) return true;
96
+ return false;
97
+ }
98
+ /** 导出噪音段列表——供 SQL 侧预过滤(readTopLevelNodesFromDb 的 LIMIT 前过滤)。 */
99
+ const NOISE_PATH_SEGMENTS_EXPORT = NOISE_PATH_SEGMENTS;
100
+ /**
101
+
102
+ * 索引行 → 图节点。这是**唯一**的节点映射实现:全量路径(`toAdapterResult`)与增量路径
103
+
104
+ * (`buildIncrementalUpdate`)都必须走这里,否则两条路径产出的节点形状会漂移。
105
+
106
+ *
107
+
108
+ * 父节点按**每个节点自己的 `file_path`** 解析(`kind` 为 file/module/import 的节点本身没有父),
109
+
110
+ * 而不是整批共用一个父 id —— 增量定向读会一次拿回多个文件的节点,整批单父会把它们全挂到同一个
111
+
112
+ * 文件下。
113
+
114
+ *
115
+
116
+ * `source` 给出时给 `properties.__source` 打标记,取值必须与 `GraphDataMerger.merge` 用的一致;
117
+
118
+ * 增量路径必须打(它要和已经过 merger 的 `current` 逐字段比对),全量路径不打(merger 会打,
119
+
120
+ * 打了也只是重复覆盖同一个值,但会让 adapter 的原始输出测试多出一个字段)。
121
+
122
+ */
123
+ function mapRowsToGraphNodes(dbNodes, source) {
124
+ const fileNodeIds = new Map();
125
+ for (const n of dbNodes) if (n.kind === "file" || n.kind === "module") fileNodeIds.set(n.file_path, n.id);
126
+ return dbNodes.filter((n) => n.name && n.file_path && !isNoisePath(n.file_path)).map((n) => {
127
+ const parentId = n.kind !== "file" && n.kind !== "module" && n.kind !== "import" ? fileNodeIds.get(n.file_path) ?? void 0 : void 0;
128
+ return {
129
+ id: NodeId(n.id),
130
+ label: n.name,
131
+ type: mapNodeKind(n.kind),
132
+ filePath: n.file_path,
133
+ lineNumber: n.start_line ?? 1,
134
+ properties: {
135
+ kind: n.kind,
136
+ ...n.signature ? { signature: n.signature } : {},
137
+ ...n.docstring ? { docstring: n.docstring } : {},
138
+ exported: n.is_exported === 1,
139
+ ...source ? { __source: source } : {}
140
+ },
141
+ ...parentId ? { parentId: NodeId(parentId) } : {},
142
+ layer: inferLayerFromPath(n.file_path)
143
+ };
144
+ });
145
+ }
146
+ /**
147
+
148
+ * 索引行 → 图边。同样是唯一的边映射实现,全量/增量两条路径共用。
149
+
150
+ *
151
+
152
+ * 三条过滤规则必须和全量路径完全一致,否则增量路径会把全量路径早已丢弃的边重新塞回图里:
153
+
154
+ * 两端都必须存在于 `validNodeIds`、丢弃自环、丢弃 `FILTERED_EDGE_KINDS`,最后按**每个来源**
155
+
156
+ * 截断到 import 上限条 import 边。
157
+
158
+ *
159
+
160
+ * 截断前先按边 id 升序排序:全量路径的边来自 `ORDER BY id` 的分页读,增量路径的边来自
161
+
162
+ * 分批 IN 查询(SQL 不保证顺序),不排序的话同一个来源保留下来的 20 条会不一样。
163
+
164
+ *
165
+
166
+ * `keptImportIds` 是增量路径必须提供的东西,也是这里唯一不显然的地方。一次定向读只拿回**某个
167
+
168
+ * 来源的部分** import 边:改动的是 `b.ts` 时,`a.ts -> b.ts` 这条边会因为"目标在改动集合内"被
169
+
170
+ * 返回,但 `a.ts` 自己的 57 条 import 里哪些进了前 20 名,局部集合根本看不出来。若只对局部集合
171
+
172
+ * 数到 20,这条本应被全量路径丢弃的边就会当成新边加进图里(实测本仓库多出 10 条)。
173
+
174
+ *
175
+
176
+ * 因此截断要在"当前图里该来源已保留的 import 边 id" ∪ "本次读回的 import 边 id" 上做:边 id 随
177
+
178
+ * 插入单调递增,所以两者的并集按 id 排序后取前 `cap` 条,就是全量路径会保留的那一批。来源在改动
179
+
180
+ * 集合内时局部集合本就是完整的(并集不变);来源不在改动集合内时,当前图里已有 20 条、新边的 id
181
+
182
+ * 更大,自然落选 —— 这正是想要的结论。
183
+
184
+ */
185
+ function mapRowsToGraphEdges(dbEdges, validNodeIds, opts = {}) {
186
+ const importCap = opts.importCap ?? CODEGRAPH_ADAPTER.MAX_IMPORTS_PER_SOURCE;
187
+ const kept = dbEdges.filter((e) => validNodeIds.has(e.source) && validNodeIds.has(e.target) && e.source !== e.target && !FILTERED_EDGE_KINDS.has(e.kind)).sort((a, b) => a.id - b.id);
188
+ const importIdsBySource = new Map();
189
+ for (const e of kept) {
190
+ if (mapEdgeKind(e.kind) !== "import") continue;
191
+ const list = importIdsBySource.get(e.source);
192
+ if (list) list.push(e.id);
193
+ else importIdsBySource.set(e.source, [e.id]);
194
+ }
195
+ const admittedImportIds = new Set();
196
+ for (const [src, ids] of importIdsBySource) {
197
+ const merged = [...new Set([...opts.keptImportIds?.get(src) ?? [], ...ids])].sort((a, b) => a - b);
198
+ for (const id of merged.slice(0, importCap)) admittedImportIds.add(id);
199
+ }
200
+ const edges = [];
201
+ for (const e of kept) {
202
+ if (mapEdgeKind(e.kind) === "import" && !admittedImportIds.has(e.id)) continue;
203
+ const locatable = typeof e.line === "number";
204
+ edges.push({
205
+ id: EdgeId(String(e.id)),
206
+ source: NodeId(e.source),
207
+ target: NodeId(e.target),
208
+ type: mapEdgeKind(e.kind),
209
+ properties: {
210
+ ...locatable ? { line: e.line } : {},
211
+ ...opts.source ? { __source: opts.source } : {},
212
+ edgeOrigin: "db",
213
+ edgeLocatable: locatable,
214
+ rawKind: e.kind
215
+ },
216
+ ...parseEdgeConfidence(e.metadata) !== void 0 ? { weight: parseEdgeConfidence(e.metadata) } : {}
217
+ });
218
+ }
219
+ return edges;
220
+ }
221
+ function mapNodeKind(kind) {
222
+ return NODE_KIND_MAP[kind] ?? "module";
223
+ }
224
+ function mapEdgeKind(kind) {
225
+ return EDGE_KIND_MAP[kind] ?? "dependency";
226
+ }
227
+ /**
228
+
229
+ * Normalize a repo-relative path for comparison against the index's own `file_path` values.
230
+
231
+ *
232
+
233
+ * The watcher reports platform separators while the indexer stores whatever it was handed, so the
234
+
235
+ * two forms have to be reconciled before any lookup. Callers that need both spellings should ask
236
+
237
+ * for them explicitly rather than relying on the index to pick one.
238
+
239
+ */
240
+ function normalizeRepoPath(filePath) {
241
+ return filePath.replace(/\\/g, "/").replace(/^\.\//, "");
242
+ }
243
+
244
+ //#endregion
245
+ export { EDGE_ROW_COLUMNS, FILTERED_EDGE_KINDS, NOISE_PATH_SEGMENTS_EXPORT, isNoisePath, mapEdgeKind, mapNodeKind, mapRowsToGraphEdges, mapRowsToGraphNodes, normalizeRepoPath, parseEdgeConfidence };
246
+ //# sourceMappingURL=codeGraphRows-C7hqu35E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codeGraphRows-C7hqu35E.js","names":["metadata: string | null | undefined","parsed: unknown","NODE_KIND_MAP: Record<string, GraphNode['type']>","EDGE_KIND_MAP: Record<string, GraphEdge['type']>","filePath: string","dbNodes: DbNodeRow[]","source?: DataSourceType","dbEdges: DbEdgeRow[]","validNodeIds: Set<string>","opts: { source?: DataSourceType; importCap?: number; keptImportIds?: Map<string, number[]> }","edges: GraphEdge[]","kind: string"],"sources":["../src/adapters/codeGraphRows.ts"],"sourcesContent":["/**\r\n * codegraph.db 索引行 → 图节点/图边的**唯一**映射层(cycle-110 Q7 行为等价提取)。\r\n *\r\n * 抽取契约(这里是语义最重的一块,改动前必须逐条确认):\r\n * · `mapRowsToGraphNodes` / `mapRowsToGraphEdges` 是全量与增量两条取数路径**共用**的映射实现,\r\n * 任何一侧另写一份都会让两条路径产出的形状漂移(历史缺陷)。本文件是它们的唯一住址。\r\n * · 父节点按**每个节点自己的 `file_path`** 解析(`kind` 为 file/module/import 的节点无父),\r\n * 不允许整批共用一个父 id。\r\n * · 边的三条过滤规则(两端都在 `validNodeIds`、去自环、丢 `FILTERED_EDGE_KINDS`)与\r\n * import 上限截断必须完全一致;截断前按边 id 升序排序,否则分批 IN 查询的顺序差异会改变保留集合。\r\n * · `edgeOrigin` / `edgeLocatable` / `rawKind` 只写**可证明**的事实:上游 edges 表没有\r\n * confidence 列,因此这里绝不产出 EXTRACTED/INFERRED 之类凭空推断的取值。\r\n *\r\n * 本文件无模块级可变状态(三张 Map 都是常量表),因此可以安全地按需重复 import。\r\n */\r\nimport type { GraphNode, GraphEdge, DataSourceType } from '../types/index.ts';\r\nimport { NodeId, EdgeId } from '../types/index.ts';\r\nimport { inferLayerFromPath } from '../shared/layerUtils.ts';\r\nimport { CODEGRAPH_ADAPTER } from '../shared/constants.ts';\r\n\r\n// ---- codegraph.db schema (upstream @colbymchenry/codegraph) ----------------\r\n\r\nexport interface DbNodeRow {\r\n id: string;\r\n kind: string;\r\n name: string;\r\n file_path: string;\r\n start_line: number;\r\n signature: string | null;\r\n docstring: string | null;\r\n is_exported: number;\r\n}\r\n\r\nexport interface DbEdgeRow {\r\n id: number;\r\n source: string;\r\n target: string;\r\n kind: string;\r\n /**\r\n * 上游 `edges.metadata` 列(JSON 文本,可空)。cycle-119 weight 数据面:\r\n * `{\"confidence\":0.9,\"resolvedBy\":\"exact-match\"}` 是实测形态(本仓 .codegraph\r\n * 6,556 条非空 / 10,869 条边)。解析为 `weight` 的规则见 {@link parseEdgeConfidence}:\r\n * **保守降级**——列缺失 / 非 JSON / 非数 / 越界 [0,1] 一律不产出 weight,\r\n * 宁可回到「无权重、DFS 序截断」,也不喂给 `'top-k-weight'` 一个编造的置信值。\r\n */\r\n metadata: string | null;\r\n line: number | null;\r\n}\r\n\r\n/**\r\n * edges 行的共享 SELECT 列清单(不含 WHERE/ORDER BY,调用方按需拼接)。\r\n *\r\n * 单一来源的理由:8 处 `SELECT … FROM edges` 曾各自手写列清单——cycle-119 就是因为\r\n * 「读侧漏了 `metadata` 列」而 `'top-k-weight'` 契约空转两轮。列变了改一处,\r\n * 各读取面跟着编译期报错(解构不存在的键)而不是静默丢数据。\r\n */\r\nexport const EDGE_ROW_COLUMNS = 'id, source, target, kind, metadata, line' as const;\r\n\r\n/**\r\n * 从 `metadata` JSON 文本解析置信度([0,1] 数值),任何不合法形态返回 `undefined`。\r\n *\r\n * 为什么不 throw:单行 metadata 损坏不该让整页边读取失败(validateEdgeRows 的\r\n * 「跳过坏行」是同一哲学);也不该把 0.9 解析成 `undefined` 再被 `?? 1` 兜底成\r\n * 「完全可信」——所以越界值与缺失值**同型**(undefined),消费侧用\r\n * `weight === undefined` 显式分支,没有 `?? 默认值` 的静默兜底路径。\r\n */\r\nexport function parseEdgeConfidence(metadata: string | null | undefined): number | undefined {\r\n if (typeof metadata !== 'string' || metadata.length === 0) return undefined;\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(metadata);\r\n } catch {\r\n return undefined;\r\n }\r\n if (typeof parsed !== 'object' || parsed === null) return undefined;\r\n const confidence = (parsed as Record<string, unknown>).confidence;\r\n if (typeof confidence !== 'number' || !Number.isFinite(confidence)) return undefined;\r\n if (confidence < 0 || confidence > 1) return undefined;\r\n return confidence;\r\n}\r\n\r\nconst NODE_KIND_MAP: Record<string, GraphNode['type']> = {\r\n function: 'function',\r\n method: 'function',\r\n class: 'class',\r\n interface: 'interface',\r\n type_alias: 'type',\r\n constant: 'variable',\r\n variable: 'variable',\r\n property: 'variable',\r\n file: 'module',\r\n import: 'module',\r\n module: 'module',\r\n};\r\n\r\nconst EDGE_KIND_MAP: Record<string, GraphEdge['type']> = {\r\n call: 'call',\r\n calls: 'call',\r\n import: 'import',\r\n imports: 'import',\r\n extend: 'extend',\r\n extends: 'extend',\r\n implement: 'implement',\r\n implements: 'implement',\r\n contains: 'contains',\r\n dependency: 'dependency',\r\n depends: 'dependency',\r\n};\r\n\r\nexport const FILTERED_EDGE_KINDS = new Set([\r\n 'uses_var',\r\n 'func_imports_local',\r\n]);\r\n\r\nconst NOISE_PATH_SEGMENTS = [\r\n 'node_modules', '.git', 'vendor', '__pycache__', '.venv', 'dist', 'build',\r\n '.next', '.nuxt', '.cache', '.turbo', 'coverage', '.pytest_cache',\r\n // cycle-141:.cycles/ 是 AI 迭代循环的归档目录(测试报告、规划文档、\r\n // 审计日志等),不是源码。不加入噪音列表会导致 bootstrap 的 LIMIT 50\r\n // 选出的顶层节点全是 .cycles/ 内容(字母序 . < s),用户看到的图谱\r\n // 只有归档目录而没有 src/ 源码。\r\n '.cycles',\r\n // cycle-141:.codegraph-visualizer/ 是插件自己的索引目录,不应出现在图谱中。\r\n '.codegraph-visualizer',\r\n];\r\n\r\nexport function isNoisePath(filePath: string): boolean {\r\n const segments = filePath.split(/[\\\\/]/);\r\n for (const seg of NOISE_PATH_SEGMENTS) {\r\n if (segments.includes(seg)) return true;\r\n }\r\n return false;\r\n}\r\n\r\n/** 导出噪音段列表——供 SQL 侧预过滤(readTopLevelNodesFromDb 的 LIMIT 前过滤)。 */\r\nexport const NOISE_PATH_SEGMENTS_EXPORT = NOISE_PATH_SEGMENTS;\r\n\r\n/**\r\n * 索引行 → 图节点。这是**唯一**的节点映射实现:全量路径(`toAdapterResult`)与增量路径\r\n * (`buildIncrementalUpdate`)都必须走这里,否则两条路径产出的节点形状会漂移。\r\n *\r\n * 父节点按**每个节点自己的 `file_path`** 解析(`kind` 为 file/module/import 的节点本身没有父),\r\n * 而不是整批共用一个父 id —— 增量定向读会一次拿回多个文件的节点,整批单父会把它们全挂到同一个\r\n * 文件下。\r\n *\r\n * `source` 给出时给 `properties.__source` 打标记,取值必须与 `GraphDataMerger.merge` 用的一致;\r\n * 增量路径必须打(它要和已经过 merger 的 `current` 逐字段比对),全量路径不打(merger 会打,\r\n * 打了也只是重复覆盖同一个值,但会让 adapter 的原始输出测试多出一个字段)。\r\n */\r\nexport function mapRowsToGraphNodes(dbNodes: DbNodeRow[], source?: DataSourceType): GraphNode[] {\r\n const fileNodeIds = new Map<string, string>();\r\n for (const n of dbNodes) {\r\n if (n.kind === 'file' || n.kind === 'module') {\r\n fileNodeIds.set(n.file_path, n.id);\r\n }\r\n }\r\n\r\n return dbNodes\r\n .filter((n) => n.name && n.file_path && !isNoisePath(n.file_path))\r\n .map((n) => {\r\n const parentId = (n.kind !== 'file' && n.kind !== 'module' && n.kind !== 'import')\r\n ? fileNodeIds.get(n.file_path) ?? undefined\r\n : undefined;\r\n return {\r\n id: NodeId(n.id),\r\n label: n.name,\r\n type: mapNodeKind(n.kind),\r\n filePath: n.file_path,\r\n lineNumber: n.start_line ?? 1,\r\n properties: {\r\n kind: n.kind,\r\n ...(n.signature ? { signature: n.signature } : {}),\r\n ...(n.docstring ? { docstring: n.docstring } : {}),\r\n exported: n.is_exported === 1,\r\n ...(source ? { __source: source } : {}),\r\n },\r\n ...(parentId ? { parentId: NodeId(parentId) } : {}),\r\n layer: inferLayerFromPath(n.file_path),\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * 索引行 → 图边。同样是唯一的边映射实现,全量/增量两条路径共用。\r\n *\r\n * 三条过滤规则必须和全量路径完全一致,否则增量路径会把全量路径早已丢弃的边重新塞回图里:\r\n * 两端都必须存在于 `validNodeIds`、丢弃自环、丢弃 `FILTERED_EDGE_KINDS`,最后按**每个来源**\r\n * 截断到 import 上限条 import 边。\r\n *\r\n * 截断前先按边 id 升序排序:全量路径的边来自 `ORDER BY id` 的分页读,增量路径的边来自\r\n * 分批 IN 查询(SQL 不保证顺序),不排序的话同一个来源保留下来的 20 条会不一样。\r\n *\r\n * `keptImportIds` 是增量路径必须提供的东西,也是这里唯一不显然的地方。一次定向读只拿回**某个\r\n * 来源的部分** import 边:改动的是 `b.ts` 时,`a.ts -> b.ts` 这条边会因为\"目标在改动集合内\"被\r\n * 返回,但 `a.ts` 自己的 57 条 import 里哪些进了前 20 名,局部集合根本看不出来。若只对局部集合\r\n * 数到 20,这条本应被全量路径丢弃的边就会当成新边加进图里(实测本仓库多出 10 条)。\r\n *\r\n * 因此截断要在\"当前图里该来源已保留的 import 边 id\" ∪ \"本次读回的 import 边 id\" 上做:边 id 随\r\n * 插入单调递增,所以两者的并集按 id 排序后取前 `cap` 条,就是全量路径会保留的那一批。来源在改动\r\n * 集合内时局部集合本就是完整的(并集不变);来源不在改动集合内时,当前图里已有 20 条、新边的 id\r\n * 更大,自然落选 —— 这正是想要的结论。\r\n */\r\nexport function mapRowsToGraphEdges(\r\n dbEdges: DbEdgeRow[],\r\n validNodeIds: Set<string>,\r\n opts: { source?: DataSourceType; importCap?: number; keptImportIds?: Map<string, number[]> } = {},\r\n): GraphEdge[] {\r\n const importCap = opts.importCap ?? CODEGRAPH_ADAPTER.MAX_IMPORTS_PER_SOURCE;\r\n const kept = dbEdges\r\n .filter((e) =>\r\n validNodeIds.has(e.source) && validNodeIds.has(e.target) &&\r\n e.source !== e.target &&\r\n !FILTERED_EDGE_KINDS.has(e.kind))\r\n .sort((a, b) => a.id - b.id);\r\n\r\n const importIdsBySource = new Map<string, number[]>();\r\n for (const e of kept) {\r\n if (mapEdgeKind(e.kind) !== 'import') continue;\r\n const list = importIdsBySource.get(e.source);\r\n if (list) list.push(e.id);\r\n else importIdsBySource.set(e.source, [e.id]);\r\n }\r\n\r\n const admittedImportIds = new Set<number>();\r\n for (const [src, ids] of importIdsBySource) {\r\n const merged = [...new Set([...(opts.keptImportIds?.get(src) ?? []), ...ids])].sort((a, b) => a - b);\r\n for (const id of merged.slice(0, importCap)) admittedImportIds.add(id);\r\n }\r\n\r\n const edges: GraphEdge[] = [];\r\n for (const e of kept) {\r\n if (mapEdgeKind(e.kind) === 'import' && !admittedImportIds.has(e.id)) continue;\r\n // 边来源归因:只写**可证明**的三件事,不写置信度。\r\n //\r\n // 上游 `codegraph.db` 的 edges 表只有 `id/source/target/kind/line`,**没有 confidence 列**\r\n // (`DbEdgeRow` 是本文件对它的逐字段转写)。因此这里刻意**不**产出 `EXTRACTED`/`INFERRED`\r\n // 之类的取值 —— 那会是凭空编造的证据。能证明的只有:\r\n // - `edgeOrigin: 'db'`:这条边来自 SQLite 索引的 edges 行。`ast` 档位(同一条 `contains`\r\n // 边由语法树父子关系直接给出)现在由 `TreeSitterAdapter` 生产(cycle-111 P1-N4)。\r\n // 两个档位互斥:本文件只写 `db`,**不冒充** `ast`;\r\n // - `edgeLocatable`:这一行有没有 `line`。有 line 才能回溯到源码位置,`null` 只能信其存在;\r\n // - `rawKind`:被 `EDGE_KIND_MAP` 压平**之前**的上游 kind。`type` 已经丢失了这个信息\r\n // (未知 kind 一律压成 `dependency`),而冲突裁决需要区分\"具体关系\"与兜底值。\r\n // 消费者必须以 `type` 判断关系语义,`rawKind` 只用于归因与裁决(两者可能不一致,\r\n // 例如 `uses_var` 在过滤前被丢弃,而 `calls` 会压平成 `call`)。\r\n const locatable = typeof e.line === 'number';\r\n edges.push({\r\n id: EdgeId(String(e.id)),\r\n source: NodeId(e.source),\r\n target: NodeId(e.target),\r\n type: mapEdgeKind(e.kind),\r\n properties: {\r\n ...(locatable ? { line: e.line } : {}),\r\n ...(opts.source ? { __source: opts.source } : {}),\r\n edgeOrigin: 'db',\r\n edgeLocatable: locatable,\r\n rawKind: e.kind,\r\n },\r\n // cycle-119 weight 数据面(§62.6 #4 / X-07 数据侧):`edges.metadata.confidence`\r\n // → `GraphEdge.weight`。解析失败/越界/缺失 → undefined → 键不产出(保守降级:\r\n // 消费侧 `'top-k-weight'` 见到无权重边按 DFS 序兜底,绝不把编造值当真)。\r\n // 注意:weight 在 GraphEdge 顶层而不在 properties 里——`graphFingerprint` 对\r\n // 两者都计入哈希(指纹影响见交付清单的 re-fingerprint 披露),但 XuanjiAdapter\r\n // 的 `xjEdge` 与 GraphologyAdapter 只读顶层 `edge.weight`。\r\n ...(parseEdgeConfidence(e.metadata) !== undefined\r\n ? { weight: parseEdgeConfidence(e.metadata) }\r\n : {}),\r\n });\r\n }\r\n return edges;\r\n}\r\n\r\nexport function mapNodeKind(kind: string): GraphNode['type'] {\r\n return NODE_KIND_MAP[kind] ?? 'module';\r\n}\r\n\r\nexport function mapEdgeKind(kind: string): GraphEdge['type'] {\r\n return EDGE_KIND_MAP[kind] ?? 'dependency';\r\n}\r\n\r\n/**\r\n * Normalize a repo-relative path for comparison against the index's own `file_path` values.\r\n *\r\n * The watcher reports platform separators while the indexer stores whatever it was handed, so the\r\n * two forms have to be reconciled before any lookup. Callers that need both spellings should ask\r\n * for them explicitly rather than relying on the index to pick one.\r\n */\r\nexport function normalizeRepoPath(filePath: string): string {\r\n return filePath.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,399 @@
1
+ //#region src/shared/constants.ts
2
+ /**
3
+
4
+ * The layout worker has to be fetched over HTTP because a browser Worker cannot be given an
5
+
6
+ * in-memory module. The plugin's own host serves it, so the client and the host share this one
7
+
8
+ * path: the client builds the Worker URL from it and the host registers the route for it.
9
+
10
+ *
11
+
12
+ * `ARTIFACT` is the file the build produces; `tests/unit/workerContract.test.ts` keeps the two in
13
+
14
+ * step, because a route that serves nothing (or a client that asks for a path nobody registered)
15
+
16
+ * is silent at runtime: layouts just fall back to the main thread.
17
+
18
+ */
19
+ const LAYOUT_WORKER = {
20
+ ROUTE_PATH: "/api/v1/codegraph/worker/layoutWorker.js",
21
+ ARTIFACT: "dist/client/worker/layoutWorker.js",
22
+ SOURCE_ENTRY: "src/client/worker/layoutWorker.ts"
23
+ };
24
+ const PATH_FINDER = { MAX_NODES_FOR_BFS: 5e4 };
25
+ const API = {
26
+ DEFAULT_TIMEOUT_MS: 3e4,
27
+ RATE_LIMIT_WINDOW_MS: 6e4,
28
+ RATE_LIMIT_DEFAULT: 60,
29
+ RATE_LIMIT_HEAVY: 10,
30
+ RATE_LIMIT_MAP_MAX: 1e4,
31
+ SCAN_TIMEOUT_MS: 6e4,
32
+ DATA_RESPONSE_MAX_BYTES: 5 * 1024 * 1024,
33
+ DATA_RESPONSE_SLIM_NODES: 500,
34
+ INVOKE_DEFAULT_TIMEOUT_MS: 5e3
35
+ };
36
+ const HEALTH_SCORE = {
37
+ WEIGHT_COUPLING: .25,
38
+ WEIGHT_COHESION: .25,
39
+ WEIGHT_CYCLIC: .3,
40
+ WEIGHT_MODULARITY: .2,
41
+ DEFAULT_MAX_MODULE_DEPTH: 3
42
+ };
43
+ const LAYOUT = {
44
+ DENSITY_FACTOR_SMALL: 1.5,
45
+ DENSITY_FACTOR_MEDIUM: 1,
46
+ DENSITY_FACTOR_LARGE: .7,
47
+ SMALL_NODE_THRESHOLD: 50,
48
+ MEDIUM_NODE_THRESHOLD: 200,
49
+ LARGE_NODE_THRESHOLD: 500,
50
+ VERY_LARGE_NODE_THRESHOLD: 2e3,
51
+ LAYOUT_SAMPLE_SIZE: 200,
52
+ COSE_NUM_ITER: 2500,
53
+ COSE_LARGE_NUM_ITER: 15e3,
54
+ MAX_RANK_SEP: 80,
55
+ MIN_RANK_SEP: 30,
56
+ MIN_NODE_SPACING: 20,
57
+ SMALL_SPACING_FACTOR: 1.2,
58
+ MEDIUM_SPACING_FACTOR: 1,
59
+ LARGE_SPACING_FACTOR: .8,
60
+ SMALL_NODE_COUNT_THRESHOLD: 100,
61
+ MEDIUM_NODE_COUNT_THRESHOLD: 300,
62
+ INCREMENTAL_RADIUS_BASE: 100,
63
+ GRID_FALLBACK_NODE_THRESHOLD: 5e3,
64
+ OVERLAP_MAX_ITERATIONS: 15,
65
+ OVERLAP_SKIP_THRESHOLD: 5e3,
66
+ OVERLAP_CELL_SIZE_MULTIPLIER: 4,
67
+ GOLDEN_ANGLE: Math.PI * (3 - Math.sqrt(5)),
68
+ OVERLAP_PUSH_COEFFICIENT: .15,
69
+ OVERLAP_MIN_PUSH: 2,
70
+ FCOSE_LARGE_NODE_THRESHOLD: 5e3,
71
+ FCOSE_LARGE_NUM_ITER: 1e3,
72
+ FCOSE_LARGE_QUALITY: "draft",
73
+ NODE_REPULSE_MAX: 2e4
74
+ };
75
+ const MCP = {
76
+ MAX_MESSAGE_LENGTH: 10 * 1024 * 1024,
77
+ MAX_IMPACT_RESULTS: 500
78
+ };
79
+ const CACHE = { GRAPH_CACHE_LIMIT: 8 };
80
+ const BOOTSTRAP = {
81
+ TOP_LEVEL_NODE_LIMIT: 50,
82
+ CHILDREN_PAGE_SIZE: 50
83
+ };
84
+ const SYMBOL_AST = {
85
+ MAX_NODES: 800,
86
+ MAX_SOURCE_LINES: 1500
87
+ };
88
+ const FILE_OUTLINE = { MAX_SYMBOLS: 800 };
89
+ const SQLITE = { MAX_IN_PARAMS: 900 };
90
+ const EDGES_AGGREGATION = {
91
+ THRESHOLD: 20,
92
+ PAGE_SIZE: 500
93
+ };
94
+ const VALIDATION = {
95
+ MAX_PATH_LENGTH: 4096,
96
+ MAX_GIT_REF_LENGTH: 256,
97
+ MAX_FILEPATH_LENGTH: 512
98
+ };
99
+ const AST = {
100
+ MAX_CONTENT_BYTES: 512 * 1024,
101
+ MAX_TREE_SITTER_CONTENT_BYTES: 512 * 1024
102
+ };
103
+ const WATCHER = {
104
+ MAX_RETRIES: 3,
105
+ RETRY_BASE_DELAY_MS: 1e3
106
+ };
107
+ /** Structural-fingerprint incremental detection (P0-6). Bump VERSION when the canonical record
108
+
109
+ * shape changes, so previously stored snapshot hashes are not mistaken for current ones. */
110
+ const FINGERPRINT = {
111
+ VERSION: 1,
112
+ SNAPSHOT_KEEP: 3
113
+ };
114
+ /** Targeted (file-scoped) graph refresh driven by the watcher (P2-21). */
115
+ const INCREMENTAL = {
116
+ MAX_CHANGED_FILES: 50,
117
+ MAX_FILE_BATCH: 200
118
+ };
119
+ /**
120
+
121
+ * `/manage/metrics` 的采样边界(cycle-141,P0 可用性)。
122
+
123
+ *
124
+
125
+ * ## 节点与边必须**各自**设上界
126
+
127
+ *
128
+
129
+ * 旧实现的循环条件只界节点(`allDbNodes.length < 10000`),边侧完全没有界:只要节点数
130
+
131
+ * 没到上限,循环就把**全库边**读进内存。代码图谱的边数通常是节点数的 2–5 倍,一个
132
+
133
+ * 9 千节点 / 4 万边的仓库就足以让这条路由 OOM。节点与边是**同一处内存护栏的两半**,
134
+
135
+ * 缺一即回到旧缺陷——所以这里两个上界缺一不可。
136
+
137
+ *
138
+
139
+ * ## 边界只影响「读多少」,不影响「算得对不对」
140
+
141
+ *
142
+
143
+ * 触到上界时路由如实披露 `truncated: true` 与 `nodesTotal/edgesTotal`,客户端据此知道
144
+
145
+ * 这不是全量图。上界不是精度承诺,是可用性护栏。
146
+
147
+ */
148
+ const METRICS = {
149
+ MAX_NODES: 1e4,
150
+ MAX_EDGES: 5e4,
151
+ PAGE_SIZE: 500
152
+ };
153
+ const TOOL_MESSAGES = {
154
+ NO_PATH_FOUND: {
155
+ en: "No path found between the specified symbols.",
156
+ zh: "未找到指定符号之间的路径。"
157
+ },
158
+ PATH_PATTERN_FAILED: {
159
+ en: "Path pattern evaluation failed.",
160
+ zh: "路径模式求值失败。"
161
+ },
162
+ GRAPH_TOO_LARGE_FOR_PATH: {
163
+ en: "Graph too large (>50000 nodes) for path finding. Try filtering first.",
164
+ zh: "图谱过大(>50000 节点),无法进行路径查找。请先尝试筛选。"
165
+ },
166
+ PATH_FOUND: {
167
+ en: "Path found",
168
+ zh: "路径已找到"
169
+ },
170
+ HOPS: {
171
+ en: "hops",
172
+ zh: "跳"
173
+ },
174
+ INSUFFICIENT_DATA: {
175
+ en: "Insufficient data for health scoring.",
176
+ zh: "数据不足,无法进行健康评分。"
177
+ },
178
+ NO_VIOLATIONS: {
179
+ en: "No rule violations detected.",
180
+ zh: "未检测到规则违规。"
181
+ },
182
+ RULE_VIOLATIONS: {
183
+ en: "rule violations:",
184
+ zh: "条规则违规:"
185
+ },
186
+ NO_CYCLES: {
187
+ en: "No dependency cycles detected.",
188
+ zh: "未检测到依赖环。"
189
+ }
190
+ };
191
+ const ERROR_CODES = {
192
+ PATH_NOT_ALLOWED: "PATH_NOT_ALLOWED",
193
+ WORKSPACE_PATH_NOT_ALLOWED: "WORKSPACE_PATH_NOT_ALLOWED",
194
+ SYMLINK_ESCAPE: "SYMLINK_ESCAPE",
195
+ NO_WORKSPACE_PATH: "NO_WORKSPACE_PATH",
196
+ PATH_OUTSIDE_WORKSPACE: "PATH_OUTSIDE_WORKSPACE",
197
+ SCAN_FAILED: "SCAN_FAILED",
198
+ INIT_FAILED: "INIT_FAILED",
199
+ WATCH_TOGGLE_FAILED: "WATCH_TOGGLE_FAILED",
200
+ WATCH_SETUP_FAILED: "WATCH_SETUP_FAILED",
201
+ INTERNAL_ERROR: "INTERNAL_ERROR",
202
+ COMMITS_REQUIRED: "COMMITS_REQUIRED",
203
+ INVALID_GIT_REF: "INVALID_GIT_REF",
204
+ GRAPH_DIFF_FAILED: "GRAPH_DIFF_FAILED",
205
+ FILEPATH_REQUIRED: "FILEPATH_REQUIRED",
206
+ FILE_TOO_LARGE: "FILE_TOO_LARGE",
207
+ AST_PARSE_FAILED: "AST_PARSE_FAILED",
208
+ FILEPATH_AND_CONTENT_REQUIRED: "FILEPATH_AND_CONTENT_REQUIRED",
209
+ INVALID_FILEPATH: "INVALID_FILEPATH",
210
+ CONTENT_TOO_LARGE: "CONTENT_TOO_LARGE",
211
+ METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
212
+ RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
213
+ SYMBOL_NOT_FOUND: "SYMBOL_NOT_FOUND",
214
+ BODY_TOO_LARGE: "BODY_TOO_LARGE",
215
+ INVALID_JSON_BODY: "INVALID_JSON_BODY",
216
+ INVALID_CONFIG: "INVALID_CONFIG",
217
+ INDEX_NOT_FOUND: "INDEX_NOT_FOUND",
218
+ DELETE_FAILED: "DELETE_FAILED",
219
+ METRICS_FAILED: "METRICS_FAILED",
220
+ SYNC_FAILED: "SYNC_FAILED"
221
+ };
222
+ const ERROR_MESSAGES = {
223
+ PATH_NOT_ALLOWED: {
224
+ en: "Path not allowed",
225
+ zh: "路径不允许访问"
226
+ },
227
+ WORKSPACE_PATH_NOT_ALLOWED: {
228
+ en: "Workspace path not allowed",
229
+ zh: "工作区路径不允许访问"
230
+ },
231
+ SYMLINK_ESCAPE: {
232
+ en: "Access denied: symlink escape",
233
+ zh: "访问被拒绝:符号链接逃逸"
234
+ },
235
+ NO_WORKSPACE_PATH: {
236
+ en: "No workspace path configured",
237
+ zh: "未配置工作区路径"
238
+ },
239
+ PATH_OUTSIDE_WORKSPACE: {
240
+ en: "Access denied: path outside workspace",
241
+ zh: "访问被拒绝:路径超出工作区范围"
242
+ },
243
+ SCAN_FAILED: {
244
+ en: "Scan failed — check codegraph installation",
245
+ zh: "扫描失败 — 请检查 codegraph 安装"
246
+ },
247
+ INIT_FAILED: {
248
+ en: "Initialization failed — is dsh-codegraph installed?",
249
+ zh: "初始化失败 — dsh-codegraph 是否已安装?"
250
+ },
251
+ WATCH_TOGGLE_FAILED: {
252
+ en: "Watch toggle failed",
253
+ zh: "监听切换失败"
254
+ },
255
+ WATCH_SETUP_FAILED: {
256
+ en: "Watch setup failed after retries",
257
+ zh: "监听设置在重试后失败"
258
+ },
259
+ INTERNAL_ERROR: {
260
+ en: "Internal server error",
261
+ zh: "服务器内部错误"
262
+ },
263
+ COMMITS_REQUIRED: {
264
+ en: "from and to commits are required",
265
+ zh: "需要 from 和 to 提交参数"
266
+ },
267
+ INVALID_GIT_REF: {
268
+ en: "Invalid git ref format",
269
+ zh: "无效的 Git 引用格式"
270
+ },
271
+ GRAPH_DIFF_FAILED: {
272
+ en: "Graph diff failed — check git refs",
273
+ zh: "图谱差异计算失败 — 请检查 Git 引用"
274
+ },
275
+ FILEPATH_REQUIRED: {
276
+ en: "filePath is required",
277
+ zh: "filePath 参数为必填"
278
+ },
279
+ FILE_TOO_LARGE: {
280
+ en: "File too large for AST parsing",
281
+ zh: "文件过大,无法进行 AST 解析"
282
+ },
283
+ AST_PARSE_FAILED: {
284
+ en: "AST parsing failed",
285
+ zh: "AST 解析失败"
286
+ },
287
+ FILEPATH_AND_CONTENT_REQUIRED: {
288
+ en: "filePath and content are required",
289
+ zh: "filePath 和 content 参数为必填"
290
+ },
291
+ INVALID_FILEPATH: {
292
+ en: "Invalid filePath",
293
+ zh: "无效的 filePath"
294
+ },
295
+ CONTENT_TOO_LARGE: {
296
+ en: "Content too large for tree-sitter parsing",
297
+ zh: "内容过大,无法进行 tree-sitter 解析"
298
+ },
299
+ METHOD_NOT_ALLOWED: {
300
+ en: "Method not allowed",
301
+ zh: "请求方法不被允许"
302
+ },
303
+ RATE_LIMIT_EXCEEDED: {
304
+ en: "Rate limit exceeded",
305
+ zh: "请求频率超限"
306
+ },
307
+ SYMBOL_NOT_FOUND: {
308
+ en: "Symbol not found",
309
+ zh: "未找到符号"
310
+ },
311
+ BODY_TOO_LARGE: {
312
+ en: "Request body too large",
313
+ zh: "请求体过大"
314
+ },
315
+ INVALID_JSON_BODY: {
316
+ en: "Invalid JSON body: expected object",
317
+ zh: "无效的 JSON 请求体:应为对象"
318
+ },
319
+ INVALID_CONFIG: {
320
+ en: "Invalid configuration",
321
+ zh: "无效的配置"
322
+ },
323
+ INDEX_NOT_FOUND: {
324
+ en: "Index not found",
325
+ zh: "未找到索引"
326
+ },
327
+ DELETE_FAILED: {
328
+ en: "Failed to delete index",
329
+ zh: "删除索引失败"
330
+ },
331
+ METRICS_FAILED: {
332
+ en: "Graph metrics computation failed",
333
+ zh: "图指标计算失败"
334
+ },
335
+ SYNC_FAILED: {
336
+ en: "Upstream sync failed",
337
+ zh: "上游同步失败"
338
+ }
339
+ };
340
+ const IMPORT = { MAX_PASTE_BYTES: 10 * 1024 * 1024 };
341
+ const DIFF_ENGINE = { MAX_DEEP_EQUAL_DEPTH: 50 };
342
+ const FILE_SYSTEM = { MAX_FILES: 500 };
343
+ const CODEGRAPH_ADAPTER = { MAX_IMPORTS_PER_SOURCE: 20 };
344
+ const TREE_SITTER = { BATCH_SIZE: 20 };
345
+ const INDEXED_DB = {
346
+ DB_VERSION: 2,
347
+ TTL: {
348
+ CLUSTER_MS: 30 * 60 * 1e3,
349
+ DIAGRAM_MS: 60 * 60 * 1e3,
350
+ OUTLINE_MS: 24 * 60 * 60 * 1e3,
351
+ AST_MS: 24 * 60 * 60 * 1e3,
352
+ EDGES_MS: 30 * 60 * 1e3
353
+ },
354
+ CAPACITY: {
355
+ HIGH_WATERMARK: .85,
356
+ LOW_WATERMARK: .7
357
+ },
358
+ BATCH: { MAX_ENTRIES: 500 }
359
+ };
360
+ const LOGGER = { RING_SIZE: 200 };
361
+ /**
362
+
363
+ * 运行时层预算(cycle-88,P2-1 三层独立面板)。
364
+
365
+ *
366
+
367
+ * 验收原文 `docs/图谱按需加载.md:196-200` 的第三层一行:
368
+
369
+ * 「运行时层 | Trace、日志、性能排查 | 独立面板,按时间窗口、请求 ID、Trace ID 加载
370
+
371
+ * | 单次时间窗口 ≤ 5 分钟或 1000 条」,并在 `:265` 复述为
372
+
373
+ * 「运行时层单次时间窗口 ≤ 5 分钟或 1000 条」。
374
+
375
+ *
376
+
377
+ * 两个上限是**同一层预算的两个维度**,取交集而不是并集:窗口先裁掉过老的数据,
378
+
379
+ * 再按条数截断,于是「最近 5 分钟」与「最多 1000 条」同时成立。若只实现其中一个,
380
+
381
+ * 高频日志场景仍能一次渲染上百万条——正是风险 9(`:243-246` 节点爆炸)想避免的。
382
+
383
+ *
384
+
385
+ * `MAX_ENTRIES` 刻意与 `LOGGER.RING_SIZE`(200)区分:环形缓冲是**保留**策略,
386
+
387
+ * 这里是**单次展示**策略,两者不能互相替代(缓冲可能远大于一次要看的量)。
388
+
389
+ */
390
+ const RUNTIME = {
391
+ WINDOW_MS: 5 * 60 * 1e3,
392
+ MAX_ENTRIES: 1e3,
393
+ PREVIEW_ENTRIES: 50,
394
+ MAX_METRICS: 100
395
+ };
396
+
397
+ //#endregion
398
+ export { API, AST, BOOTSTRAP, CACHE, CODEGRAPH_ADAPTER, DIFF_ENGINE, EDGES_AGGREGATION, ERROR_CODES, ERROR_MESSAGES, FILE_OUTLINE, FILE_SYSTEM, FINGERPRINT, HEALTH_SCORE, INCREMENTAL, LAYOUT_WORKER, LOGGER, MCP, METRICS, PATH_FINDER, SQLITE, SYMBOL_AST, TOOL_MESSAGES, TREE_SITTER, VALIDATION, WATCHER };
399
+ //# sourceMappingURL=constants-CQq93l3e.js.map