gitnexus 1.6.8-rc.2 → 1.6.8-rc.21

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 (193) hide show
  1. package/README.md +20 -0
  2. package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
  3. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
  4. package/dist/_shared/scope-resolution/symbol-definition.d.ts +4 -0
  5. package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
  6. package/dist/cli/ai-context.js +1 -0
  7. package/dist/cli/analyze-config.js +40 -0
  8. package/dist/cli/analyze.d.ts +22 -0
  9. package/dist/cli/analyze.js +36 -6
  10. package/dist/cli/clean.d.ts +1 -0
  11. package/dist/cli/clean.js +43 -1
  12. package/dist/cli/eval-server.js +43 -0
  13. package/dist/cli/help-i18n.js +11 -0
  14. package/dist/cli/i18n/en.d.ts +13 -0
  15. package/dist/cli/i18n/en.js +13 -0
  16. package/dist/cli/i18n/resources.d.ts +26 -0
  17. package/dist/cli/i18n/zh-CN.d.ts +13 -0
  18. package/dist/cli/i18n/zh-CN.js +13 -0
  19. package/dist/cli/index.js +19 -0
  20. package/dist/cli/list.js +12 -0
  21. package/dist/cli/optional-grammars.d.ts +6 -8
  22. package/dist/cli/optional-grammars.js +8 -11
  23. package/dist/cli/skill-gen.js +1 -0
  24. package/dist/cli/status.js +26 -5
  25. package/dist/cli/tool.d.ts +12 -1
  26. package/dist/cli/tool.js +41 -1
  27. package/dist/core/embeddings/embedder.js +4 -0
  28. package/dist/core/embeddings/embedding-pipeline.js +27 -16
  29. package/dist/core/embeddings/onnxruntime-common-resolver.d.ts +6 -0
  30. package/dist/core/embeddings/onnxruntime-common-resolver.js +130 -0
  31. package/dist/core/graph/import-cycles.d.ts +10 -0
  32. package/dist/core/graph/import-cycles.js +103 -0
  33. package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
  34. package/dist/core/group/extractors/http-patterns/java.js +3 -50
  35. package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
  36. package/dist/core/group/extractors/include-extractor.js +7 -7
  37. package/dist/core/ingestion/cfg/cfg-builder.d.ts +64 -0
  38. package/dist/core/ingestion/cfg/cfg-builder.js +129 -0
  39. package/dist/core/ingestion/cfg/collect.d.ts +30 -0
  40. package/dist/core/ingestion/cfg/collect.js +34 -0
  41. package/dist/core/ingestion/cfg/control-flow-context.d.ts +97 -0
  42. package/dist/core/ingestion/cfg/control-flow-context.js +113 -0
  43. package/dist/core/ingestion/cfg/emit.d.ts +144 -0
  44. package/dist/core/ingestion/cfg/emit.js +315 -0
  45. package/dist/core/ingestion/cfg/reaching-defs.d.ts +90 -0
  46. package/dist/core/ingestion/cfg/reaching-defs.js +364 -0
  47. package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
  48. package/dist/core/ingestion/cfg/traversal-result.js +2 -0
  49. package/dist/core/ingestion/cfg/types.d.ts +231 -0
  50. package/dist/core/ingestion/cfg/types.js +13 -0
  51. package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +144 -0
  52. package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1032 -0
  53. package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
  54. package/dist/core/ingestion/cfg/visitors/typescript.js +584 -0
  55. package/dist/core/ingestion/language-provider.d.ts +36 -2
  56. package/dist/core/ingestion/languages/c-cpp.js +11 -4
  57. package/dist/core/ingestion/languages/cpp/arity-metadata.js +6 -2
  58. package/dist/core/ingestion/languages/cpp/captures.js +24 -1
  59. package/dist/core/ingestion/languages/cpp/query.js +23 -0
  60. package/dist/core/ingestion/languages/java.js +3 -0
  61. package/dist/core/ingestion/languages/kotlin/query.js +3 -2
  62. package/dist/core/ingestion/languages/kotlin.js +5 -1
  63. package/dist/core/ingestion/languages/typescript.js +5 -0
  64. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
  65. package/dist/core/ingestion/method-extractors/generic.js +1 -0
  66. package/dist/core/ingestion/method-types.d.ts +2 -0
  67. package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
  68. package/dist/core/ingestion/model/symbol-table.js +1 -0
  69. package/dist/core/ingestion/parsing-processor.js +22 -0
  70. package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
  71. package/dist/core/ingestion/pipeline-phases/routes.js +64 -14
  72. package/dist/core/ingestion/pipeline.d.ts +57 -0
  73. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +50 -0
  74. package/dist/core/ingestion/route-extractors/spring-shared.js +80 -0
  75. package/dist/core/ingestion/route-extractors/spring.d.ts +35 -0
  76. package/dist/core/ingestion/route-extractors/spring.js +136 -0
  77. package/dist/core/ingestion/scope-extractor.js +3 -0
  78. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +12 -0
  79. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +145 -24
  80. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +6 -0
  81. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
  82. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +21 -0
  83. package/dist/core/ingestion/scope-resolution/pipeline/run.js +207 -0
  84. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  85. package/dist/core/ingestion/taint/emit.d.ts +124 -0
  86. package/dist/core/ingestion/taint/emit.js +204 -0
  87. package/dist/core/ingestion/taint/match.d.ts +153 -0
  88. package/dist/core/ingestion/taint/match.js +278 -0
  89. package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
  90. package/dist/core/ingestion/taint/path-codec.js +190 -0
  91. package/dist/core/ingestion/taint/propagate.d.ts +216 -0
  92. package/dist/core/ingestion/taint/propagate.js +664 -0
  93. package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
  94. package/dist/core/ingestion/taint/site-safety.js +98 -0
  95. package/dist/core/ingestion/taint/source-sink-config.d.ts +94 -23
  96. package/dist/core/ingestion/taint/source-sink-config.js +11 -11
  97. package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
  98. package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
  99. package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
  100. package/dist/core/ingestion/taint/typescript-model.js +102 -0
  101. package/dist/core/ingestion/utils/method-props.js +1 -0
  102. package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
  103. package/dist/core/ingestion/workers/clone-safety.js +465 -0
  104. package/dist/core/ingestion/workers/parse-worker.d.ts +11 -0
  105. package/dist/core/ingestion/workers/parse-worker.js +70 -53
  106. package/dist/core/ingestion/workers/post-result.d.ts +22 -0
  107. package/dist/core/ingestion/workers/post-result.js +87 -0
  108. package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
  109. package/dist/core/ingestion/workers/result-merge.js +43 -0
  110. package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
  111. package/dist/core/ingestion/workers/worker-pool.js +33 -16
  112. package/dist/core/lbug/lbug-adapter.d.ts +19 -0
  113. package/dist/core/lbug/lbug-adapter.js +56 -1
  114. package/dist/core/run-analyze.d.ts +87 -0
  115. package/dist/core/run-analyze.js +280 -25
  116. package/dist/core/tree-sitter/parser-loader.js +5 -4
  117. package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
  118. package/dist/core/tree-sitter/vendored-grammars.js +57 -0
  119. package/dist/mcp/core/embedder.js +4 -0
  120. package/dist/mcp/local/local-backend.d.ts +86 -2
  121. package/dist/mcp/local/local-backend.js +657 -27
  122. package/dist/mcp/resources.js +1 -0
  123. package/dist/mcp/tools.d.ts +9 -0
  124. package/dist/mcp/tools.js +109 -0
  125. package/dist/server/analyze-launch.d.ts +29 -0
  126. package/dist/server/analyze-launch.js +137 -0
  127. package/dist/server/analyze-upload.d.ts +33 -0
  128. package/dist/server/analyze-upload.js +123 -0
  129. package/dist/server/analyze-worker-ipc.d.ts +58 -0
  130. package/dist/server/analyze-worker-ipc.js +16 -0
  131. package/dist/server/analyze-worker.d.ts +17 -1
  132. package/dist/server/analyze-worker.js +7 -1
  133. package/dist/server/api.js +46 -140
  134. package/dist/server/git-clone.d.ts +1 -0
  135. package/dist/server/git-clone.js +1 -1
  136. package/dist/server/middleware.d.ts +11 -0
  137. package/dist/server/middleware.js +27 -0
  138. package/dist/server/upload-ingest.d.ts +56 -0
  139. package/dist/server/upload-ingest.js +276 -0
  140. package/dist/server/upload-paths.d.ts +31 -0
  141. package/dist/server/upload-paths.js +51 -0
  142. package/dist/server/upload-sweep.d.ts +20 -0
  143. package/dist/server/upload-sweep.js +57 -0
  144. package/dist/storage/branch-index.d.ts +52 -0
  145. package/dist/storage/branch-index.js +65 -0
  146. package/dist/storage/git.d.ts +11 -0
  147. package/dist/storage/git.js +28 -0
  148. package/dist/storage/parse-cache.d.ts +22 -1
  149. package/dist/storage/parse-cache.js +32 -10
  150. package/dist/storage/repo-manager.d.ts +122 -10
  151. package/dist/storage/repo-manager.js +162 -21
  152. package/hooks/antigravity/gitnexus-antigravity-hook.cjs +42 -8
  153. package/hooks/claude/gitnexus-hook.cjs +36 -8
  154. package/hooks/claude/hook-db-lock-probe.cjs +123 -2
  155. package/package.json +4 -2
  156. package/scripts/assert-publish-grammar-coverage.cjs +31 -0
  157. package/scripts/build-tree-sitter-grammars.cjs +16 -10
  158. package/skills/gitnexus-guide.md +11 -0
  159. package/vendor/tree-sitter-c/package.json +1 -1
  160. package/vendor/tree-sitter-dart/package.json +1 -1
  161. package/vendor/tree-sitter-kotlin/package.json +1 -1
  162. package/vendor/tree-sitter-proto/package.json +1 -1
  163. package/vendor/tree-sitter-swift/package.json +1 -1
  164. package/web/assets/{agent-CKPMqImC.js → agent-ay4LD70X.js} +1 -1
  165. package/web/assets/{architectureDiagram-UL44E2DR-D1EXI0zA.js → architectureDiagram-UL44E2DR-Dc-viYhd.js} +1 -1
  166. package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-4rpojOyj.js} +1 -1
  167. package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-BnOy944n.js} +1 -1
  168. package/web/assets/{classDiagram-KGZ6W3CR-Bv93af_b.js → classDiagram-KGZ6W3CR-Bsgpy98Q.js} +1 -1
  169. package/web/assets/{classDiagram-v2-72OJOZXJ-CLfEqHUa.js → classDiagram-v2-72OJOZXJ-D5atDGjc.js} +1 -1
  170. package/web/assets/{diagram-3NCE3AQN-BqAtKUpW.js → diagram-3NCE3AQN-Cz1OEMVi.js} +1 -1
  171. package/web/assets/{diagram-GF46GFSD-CaBG6n6o.js → diagram-GF46GFSD-CWYwzfP8.js} +1 -1
  172. package/web/assets/{diagram-QXG6HAR7-CZ-O3rcV.js → diagram-QXG6HAR7-DtBObr8L.js} +1 -1
  173. package/web/assets/{diagram-WEQXMOUZ-P4lSL4GH.js → diagram-WEQXMOUZ-BUV44Ov_.js} +1 -1
  174. package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-Ds9s-sRF.js} +1 -1
  175. package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-CupStHQb.js} +1 -1
  176. package/web/assets/index-COMMmbxW.css +2 -0
  177. package/web/assets/{index-CG6q8eTs.js → index-Do8AE5yF.js} +85 -85
  178. package/web/assets/{infoDiagram-3YFTVSEB-DmLICZx1.js → infoDiagram-3YFTVSEB-DPTXSbgu.js} +1 -1
  179. package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CeK-Qhet.js} +1 -1
  180. package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-D4tx6QQ-.js} +1 -1
  181. package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-Bcto_kGa.js} +1 -1
  182. package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-DPIeBTG6.js} +1 -1
  183. package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-DDGXjVKO.js} +1 -1
  184. package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-BzDqpcVW.js} +1 -1
  185. package/web/assets/{stateDiagram-7D4R322I-CB2nABwH.js → stateDiagram-7D4R322I-Bx__als3.js} +1 -1
  186. package/web/assets/{stateDiagram-v2-36443NZ5-COBGd2RL.js → stateDiagram-v2-36443NZ5-QqOh2yO-.js} +1 -1
  187. package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-C1eogTOG.js} +1 -1
  188. package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-D20F4rSW.js} +1 -1
  189. package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-BeBHg7ST.js} +1 -1
  190. package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-CdOWeoNI.js} +1 -1
  191. package/web/index.html +2 -2
  192. package/scripts/materialize-vendor-grammars.cjs +0 -97
  193. package/web/assets/index-BKWA-m7o.css +0 -2
@@ -19,7 +19,10 @@ import type { MethodExtractor } from './method-types.js';
19
19
  import type { VariableExtractor } from './variable-types.js';
20
20
  import type { ImportResolverFn } from './import-resolvers/types.js';
21
21
  import type { SyntaxNode } from './utils/ast-helpers.js';
22
+ import type { CfgVisitor } from './cfg/types.js';
22
23
  import type { NodeLabel } from '../../_shared/index.js';
24
+ import type Parser from 'tree-sitter';
25
+ import type { ExtractedDecoratorRoute } from './workers/parse-worker.js';
23
26
  /** Tree-sitter query captures: capture name → AST node (or undefined if not captured). */
24
27
  export type CaptureMap = Record<string, SyntaxNode | undefined>;
25
28
  /** Configuration for AST-based framework detection patterns. */
@@ -145,6 +148,12 @@ interface LanguageProviderConfig {
145
148
  * `undefined` when no constraints exist / the node isn't a templated
146
149
  * function. Languages without SFINAE / concept semantics leave this
147
150
  * undefined and the disambiguation is a pass-through.
151
+ *
152
+ * Cloneability contract: the returned payload crosses the worker boundary
153
+ * via structured clone, so it MUST be structured-clone-safe (no functions,
154
+ * symbols, or tree-sitter `SyntaxNode`s — only plain data). Wrap the return
155
+ * with `assertCloneable` from `workers/clone-safety.ts` so a future leak is a
156
+ * compile error at the source instead of a runtime DataCloneError (#2143).
148
157
  */
149
158
  readonly extractTemplateConstraints?: (definitionNode: SyntaxNode) => unknown;
150
159
  /** Override the default node label for definition.function captures.
@@ -185,6 +194,17 @@ interface LanguageProviderConfig {
185
194
  * When true, the worker extracts routes via the language's route extraction logic.
186
195
  * Default: undefined (no route files). */
187
196
  readonly isRouteFile?: (filePath: string) => boolean;
197
+ /**
198
+ * Extract decorator-style route annotations from a parsed file.
199
+ *
200
+ * When defined, the parse worker calls this after per-file capture processing
201
+ * to extract framework route definitions that require AST-level analysis beyond
202
+ * generic `@decorator` captures (e.g., Java Spring class-level prefix joining,
203
+ * multi-class handling). The returned routes are appended to `decoratorRoutes`.
204
+ *
205
+ * Default: undefined (no language-specific decorator route extraction).
206
+ */
207
+ readonly extractDecoratorRoutes?: (tree: Parser.Tree, filePath: string, lineOffset: number) => ExtractedDecoratorRoute[];
188
208
  /** Built-in/stdlib names that should be filtered from the call graph for this language.
189
209
  * Default: undefined (no language-specific filtering). */
190
210
  readonly builtInNames?: ReadonlySet<string>;
@@ -255,12 +275,26 @@ interface LanguageProviderConfig {
255
275
  * disk store WITHOUT a main-thread re-parse. The main thread restores them
256
276
  * via the matching `ScopeResolver.applyCaptureSideChannel` hook.
257
277
  *
258
- * MUST return plain data (objects / arrays / primitives) so it round-trips
259
- * through `JSON.stringify` + the parsedfile-store interning reviver.
278
+ * Cloneability contract: MUST return plain data (objects / arrays /
279
+ * primitives no functions, symbols, or tree-sitter `SyntaxNode`s) so it
280
+ * survives BOTH the worker→main structured clone AND `JSON.stringify` + the
281
+ * parsedfile-store interning reviver. Wrap the return with `assertCloneable`
282
+ * from `workers/clone-safety.ts` so a future non-serializable leak is a
283
+ * compile error at the source instead of a runtime DataCloneError (#2143).
260
284
  *
261
285
  * Default: undefined (provider has no capture-time module-level side effects).
262
286
  */
263
287
  readonly collectCaptureSideChannel?: (filePath: string) => unknown;
288
+ /**
289
+ * Per-language control-flow-graph builder (#2081 M1, PDG/taint substrate).
290
+ * Invoked IN THE PARSE WORKER (where the AST lives) for each function node,
291
+ * gated on the `--pdg` opt-in; the resulting per-function CFGs are serialized
292
+ * onto `ParsedFile.cfgSideChannel` and emitted as BasicBlock nodes + CFG
293
+ * edges during scope-resolution. `TNode` is `SyntaxNode` for the tree-sitter
294
+ * languages. Default: undefined (language has no CFG support yet — TS/JS are
295
+ * the M1 set).
296
+ */
297
+ readonly cfgVisitor?: CfgVisitor<SyntaxNode>;
264
298
  /**
265
299
  * Interpret a raw `@import.statement` capture group into a `ParsedImport`.
266
300
  * The central finalize algorithm resolves `ParsedImport.targetRaw` to a
@@ -38,7 +38,8 @@ import { cCallConfig, cppCallConfig } from '../call-extractors/configs/c-cpp.js'
38
38
  import { stripUeMacros } from '../cpp-ue-preprocessor.js';
39
39
  import { emitCScopeCaptures, interpretCImport, interpretCTypeBinding, cArityCompatibility, cBindingScopeFor, cImportOwningScope, cReceiverBinding, collectCStaticLinkageSideChannel, } from './c/index.js';
40
40
  import { emitCppScopeCaptures, interpretCppImport, interpretCppTypeBinding, cppArityCompatibility, cppBindingScopeFor, cppImportOwningScope, cppReceiverBinding, collectCppCaptureSideChannel, } from './cpp/index.js';
41
- import { extractCppTemplateConstraints } from './cpp/constraint-extractor.js';
41
+ import { extractCppTemplateConstraints, } from './cpp/constraint-extractor.js';
42
+ import { assertCloneable } from '../workers/clone-safety.js';
42
43
  const C_BUILT_INS = new Set([
43
44
  'printf',
44
45
  'fprintf',
@@ -358,7 +359,10 @@ export const cProvider = defineLanguage({
358
359
  // `static` functions look non-file-local on the main thread and leak into
359
360
  // cross-file global free-call resolution / wildcard imports. See
360
361
  // `c/capture-side-channel.ts`.
361
- collectCaptureSideChannel: collectCStaticLinkageSideChannel,
362
+ // `assertCloneable` is a runtime identity; it makes a future non-serializable
363
+ // value in the side-channel payload a compile error here, at the source, rather
364
+ // than a DataCloneError at the worker boundary (#2143).
365
+ collectCaptureSideChannel: (filePath) => assertCloneable(collectCStaticLinkageSideChannel(filePath)),
362
366
  interpretImport: interpretCImport,
363
367
  interpretTypeBinding: interpretCTypeBinding,
364
368
  bindingScopeFor: cBindingScopeFor,
@@ -431,7 +435,7 @@ export const cppProvider = defineLanguage({
431
435
  // just populated for this file into plain data on `ParsedFile.captureSideChannel`,
432
436
  // so the main thread can restore them via `applyCaptureSideChannel` WITHOUT a
433
437
  // re-parse (#1983). See `cpp/capture-side-channel.ts`.
434
- collectCaptureSideChannel: collectCppCaptureSideChannel,
438
+ collectCaptureSideChannel: (filePath) => assertCloneable(collectCppCaptureSideChannel(filePath)),
435
439
  interpretImport: interpretCppImport,
436
440
  interpretTypeBinding: interpretCppTypeBinding,
437
441
  bindingScopeFor: cppBindingScopeFor,
@@ -482,5 +486,8 @@ function extractCppTemplateConstraintsForProvider(definitionNode) {
482
486
  }
483
487
  break;
484
488
  }
485
- return extractCppTemplateConstraints(templateDecl, declarator);
489
+ // Guard the boundary at the source: a future non-cloneable member of the
490
+ // constraint payload becomes a compile error here, not a runtime
491
+ // DataCloneError at the worker post (#2143).
492
+ return assertCloneable(extractCppTemplateConstraints(templateDecl, declarator));
486
493
  }
@@ -193,8 +193,12 @@ function findFuncDeclarator(node) {
193
193
  }
194
194
  return null;
195
195
  }
196
- // Unwrap pointer_declarator / reference_declarator
197
- while (decl.type === 'pointer_declarator' || decl.type === 'reference_declarator') {
196
+ // Unwrap declarator wrappers. Deleted free functions are represented as
197
+ // `init_declarator(function_declarator, delete_expression)` by
198
+ // tree-sitter-cpp 0.23.
199
+ while (decl.type === 'pointer_declarator' ||
200
+ decl.type === 'reference_declarator' ||
201
+ decl.type === 'init_declarator') {
198
202
  const next = decl.childForFieldName('declarator');
199
203
  if (next === null) {
200
204
  // reference_declarator may not use field name
@@ -113,6 +113,9 @@ export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
113
113
  if (hasExplicitSpecifier(fnNode)) {
114
114
  grouped['@declaration.is-explicit'] = syntheticCapture('@declaration.is-explicit', fnNode, 'true');
115
115
  }
116
+ if (hasDeletedMethodClause(fnNode, grouped['@declaration.name']?.text)) {
117
+ grouped['@declaration.is-deleted'] = syntheticCapture('@declaration.is-deleted', fnNode, 'true');
118
+ }
116
119
  // Detect static storage class (file-local linkage)
117
120
  if (hasStaticStorageClass(fnNode)) {
118
121
  const nameText = grouped['@declaration.name']?.text;
@@ -1562,8 +1565,11 @@ function extractDeclaratorLeafName(node) {
1562
1565
  let cur = node;
1563
1566
  let safety = 16;
1564
1567
  while (safety-- > 0) {
1565
- if (cur.type === 'identifier' || cur.type === 'type_identifier')
1568
+ if (cur.type === 'identifier' ||
1569
+ cur.type === 'type_identifier' ||
1570
+ cur.type === 'operator_name') {
1566
1571
  return cur.text;
1572
+ }
1567
1573
  // Common wrapper nodes — follow the 'declarator' field when present.
1568
1574
  const next = cur.childForFieldName('declarator') ??
1569
1575
  // parenthesized_declarator: single named child
@@ -1590,6 +1596,23 @@ function hasExplicitSpecifier(node) {
1590
1596
  }
1591
1597
  return /\bexplicit\b/.test(node.text.slice(0, 128));
1592
1598
  }
1599
+ function hasDeletedMethodClause(node, callableName) {
1600
+ for (let i = 0; i < node.namedChildCount; i++) {
1601
+ const child = node.namedChild(i);
1602
+ if (child?.type === 'delete_method_clause')
1603
+ return true;
1604
+ // tree-sitter-cpp 0.23 parses a deleted free-function declaration as
1605
+ // `declaration > init_declarator > delete_expression`, while class
1606
+ // members use the dedicated `delete_method_clause`.
1607
+ if (child?.type === 'init_declarator' &&
1608
+ child.childForFieldName('value')?.type === 'delete_expression' &&
1609
+ callableName !== undefined &&
1610
+ extractDeclaratorLeafName(child.childForFieldName('declarator') ?? child) === callableName) {
1611
+ return true;
1612
+ }
1613
+ }
1614
+ return false;
1615
+ }
1593
1616
  /**
1594
1617
  * Check if a C++ function_definition or declaration has `static` storage class.
1595
1618
  */
@@ -193,6 +193,29 @@ const CPP_SCOPE_QUERY = `
193
193
  declarator: (function_declarator
194
194
  declarator: (identifier) @declaration.name)) @declaration.function
195
195
 
196
+ ;; tree-sitter-cpp 0.23 represents a deleted free function as an
197
+ ;; init_declarator whose value is a delete_expression.
198
+ (declaration
199
+ declarator: (init_declarator
200
+ declarator: (function_declarator
201
+ declarator: (identifier) @declaration.name)
202
+ value: (delete_expression))) @declaration.function
203
+
204
+ ;; Deleted free operator declaration.
205
+ (declaration
206
+ declarator: (init_declarator
207
+ declarator: (function_declarator
208
+ declarator: (operator_name) @declaration.name)
209
+ value: (delete_expression))) @declaration.function
210
+
211
+ ;; Deleted free function with a pointer return type.
212
+ (declaration
213
+ declarator: (init_declarator
214
+ declarator: (pointer_declarator
215
+ declarator: (function_declarator
216
+ declarator: (identifier) @declaration.name))
217
+ value: (delete_expression))) @declaration.function
218
+
196
219
  ;; Free operator prototype: std::ostream& operator<<(std::ostream&, T)
197
220
  (declaration
198
221
  declarator: (function_declarator
@@ -11,6 +11,7 @@ import { createClassExtractor } from '../class-extractors/generic.js';
11
11
  import { javaClassConfig } from '../class-extractors/configs/jvm.js';
12
12
  import { defineLanguage } from '../language-provider.js';
13
13
  import { javaTypeConfig } from '../type-extractors/jvm.js';
14
+ import { extractSpringRoutes } from '../route-extractors/spring.js';
14
15
  import { javaExportChecker } from '../export-detection.js';
15
16
  import { createImportResolver } from '../import-resolvers/resolver-factory.js';
16
17
  import { javaImportConfig } from '../import-resolvers/configs/jvm.js';
@@ -103,4 +104,6 @@ export const javaProvider = defineLanguage({
103
104
  arityCompatibility: javaArityCompatibility,
104
105
  resolveImportTarget: resolveJavaImportTarget,
105
106
  orderSameNameTypeCandidates: orderJavaSameNameTypeCandidates,
107
+ // ── Route extraction ──
108
+ extractDecoratorRoutes: extractSpringRoutes,
106
109
  });
@@ -1,7 +1,8 @@
1
1
  import Parser from 'tree-sitter';
2
2
  import { SupportedLanguages } from '../../../../_shared/index.js';
3
- // `tree-sitter-kotlin` is an optionalDependency that may be absent on a default
4
- // install (or fail its native build). Loaded lazily + guarded via parser-loader
3
+ // `tree-sitter-kotlin` is a vendored grammar (loaded from vendor/ by absolute
4
+ // path, never node_modules vendored-grammars.ts / #2111) that may be absent on
5
+ // a platform without a matching prebuild. Loaded lazily + guarded via parser-loader
5
6
  // rather than statically imported: this module is pulled onto the main thread
6
7
  // eagerly by the scope-resolution registry and the language-provider index, so
7
8
  // a top-level `import Kotlin from 'tree-sitter-kotlin'` would throw
@@ -10,6 +10,7 @@ import { SupportedLanguages } from '../../../_shared/index.js';
10
10
  import { createClassExtractor } from '../class-extractors/generic.js';
11
11
  import { kotlinClassConfig } from '../class-extractors/configs/jvm.js';
12
12
  import { defineLanguage } from '../language-provider.js';
13
+ import { assertCloneable } from '../workers/clone-safety.js';
13
14
  import { kotlinTypeConfig } from '../type-extractors/jvm.js';
14
15
  import { kotlinExportChecker } from '../export-detection.js';
15
16
  import { createImportResolver } from '../import-resolvers/resolver-factory.js';
@@ -166,7 +167,10 @@ export const kotlinProvider = defineLanguage({
166
167
  // so the main thread can restore them via `applyCaptureSideChannel` WITHOUT a
167
168
  // re-parse (#1983). Without this, companion/static dispatch emits no CALLS
168
169
  // edges on the worker path. See `kotlin/capture-side-channel.ts`.
169
- collectCaptureSideChannel: collectKotlinCaptureSideChannel,
170
+ // `assertCloneable` is a runtime identity; it makes a future non-serializable
171
+ // value in the side-channel payload a compile error here, at the source, rather
172
+ // than a DataCloneError at the worker boundary (#2143).
173
+ collectCaptureSideChannel: (filePath) => assertCloneable(collectKotlinCaptureSideChannel(filePath)),
170
174
  interpretImport: interpretKotlinImport,
171
175
  interpretTypeBinding: interpretKotlinTypeBinding,
172
176
  bindingScopeFor: kotlinBindingScopeFor,
@@ -9,6 +9,7 @@ import { SupportedLanguages } from '../../../_shared/index.js';
9
9
  import { defineLanguage } from '../language-provider.js';
10
10
  import { createClassExtractor } from '../class-extractors/generic.js';
11
11
  import { typescriptClassConfig, javascriptClassConfig, } from '../class-extractors/configs/typescript-javascript.js';
12
+ import { createTypeScriptCfgVisitor } from '../cfg/visitors/typescript.js';
12
13
  import { typeConfig as typescriptConfig } from '../type-extractors/typescript.js';
13
14
  import { tsExportChecker } from '../export-detection.js';
14
15
  import { createImportResolver } from '../import-resolvers/resolver-factory.js';
@@ -293,6 +294,8 @@ export const typescriptProvider = defineLanguage({
293
294
  // canonical capture vocabulary in ./typescript/query.ts
294
295
  // (TYPESCRIPT_SCOPE_QUERY constant).
295
296
  emitScopeCaptures: emitTsScopeCaptures,
297
+ // CFG/PDG substrate (#2081 M1) — runs in the worker on a --pdg run.
298
+ cfgVisitor: createTypeScriptCfgVisitor(),
296
299
  interpretImport: interpretTsImport,
297
300
  interpretTypeBinding: interpretTsTypeBinding,
298
301
  bindingScopeFor: tsBindingScopeFor,
@@ -352,6 +355,8 @@ export const javascriptProvider = defineLanguage({
352
355
  // JSDoc type bindings) live in ./javascript/captures.ts.
353
356
  // See ./javascript/index.ts for the full per-module rationale.
354
357
  emitScopeCaptures: emitJsScopeCaptures,
358
+ // CFG/PDG substrate (#2081 M1) — TS and JS share the same grammar family.
359
+ cfgVisitor: createTypeScriptCfgVisitor(),
355
360
  interpretImport: interpretJsImport,
356
361
  interpretTypeBinding: interpretJsTypeBinding,
357
362
  bindingScopeFor: jsBindingScopeFor,
@@ -33,18 +33,12 @@ function findFunctionDeclarator(node) {
33
33
  }
34
34
  return null;
35
35
  }
36
- /**
37
- * Detect `= delete` and `= default` special member function declarations.
38
- * These are not callable methods and should be suppressed from extraction.
39
- * tree-sitter-cpp ^0.23.4 emits `delete_method_clause` / `default_method_clause`
40
- * as named children of the function_definition node.
41
- */
42
- function isDeletedOrDefaulted(node) {
36
+ /** Detect a C++ special member clause by its tree-sitter node type. */
37
+ function hasSpecialMethodClause(node, clauseType) {
43
38
  for (let i = 0; i < node.namedChildCount; i++) {
44
39
  const child = node.namedChild(i);
45
- if (child?.type === 'delete_method_clause' || child?.type === 'default_method_clause') {
40
+ if (child?.type === clauseType)
46
41
  return true;
47
- }
48
42
  }
49
43
  return false;
50
44
  }
@@ -57,10 +51,6 @@ function extractCppMethodName(node) {
57
51
  const funcDecl = findFunctionDeclarator(node);
58
52
  if (!funcDecl)
59
53
  return undefined;
60
- // Suppress `= delete` and `= default` special members — these are not callable
61
- // methods and should not appear in HAS_METHOD edges.
62
- if (isDeletedOrDefaulted(node))
63
- return undefined;
64
54
  const nameNode = funcDecl.childForFieldName('declarator');
65
55
  if (!nameNode)
66
56
  return undefined;
@@ -360,6 +350,9 @@ export const cppMethodConfig = {
360
350
  }
361
351
  return false;
362
352
  },
353
+ isDeleted(node) {
354
+ return hasSpecialMethodClause(node, 'delete_method_clause');
355
+ },
363
356
  };
364
357
  // ---------------------------------------------------------------------------
365
358
  // C config (minimal — C has no classes/methods, only struct function pointers)
@@ -198,6 +198,7 @@ function buildMethod(node, ownerNode, context, config) {
198
198
  ...(config.isAsync?.(node) ? { isAsync: true } : {}),
199
199
  ...(config.isPartial?.(node) ? { isPartial: true } : {}),
200
200
  ...(config.isConst?.(node) ? { isConst: true } : {}),
201
+ ...(config.isDeleted?.(node) ? { isDeleted: true } : {}),
201
202
  annotations: config.extractAnnotations?.(node) ?? [],
202
203
  sourceFile: context.filePath,
203
204
  line: node.startPosition.row + 1,
@@ -27,6 +27,7 @@ export interface MethodInfo {
27
27
  isAsync?: boolean;
28
28
  isPartial?: boolean;
29
29
  isConst?: boolean;
30
+ isDeleted?: boolean;
30
31
  annotations: string[];
31
32
  sourceFile: string;
32
33
  line: number;
@@ -74,6 +75,7 @@ export interface MethodExtractionConfig {
74
75
  isAsync?: (node: SyntaxNode) => boolean;
75
76
  isPartial?: (node: SyntaxNode) => boolean;
76
77
  isConst?: (node: SyntaxNode) => boolean;
78
+ isDeleted?: (node: SyntaxNode) => boolean;
77
79
  /** Owner node types where member functions are effectively static (e.g.
78
80
  * Ruby singleton_class, Kotlin companion_object / object_declaration).
79
81
  * When the ownerNode matches one of these types, isStatic is forced true. */
@@ -102,6 +102,7 @@ export interface AddMetadata {
102
102
  templateArguments?: string[];
103
103
  ownerId?: string;
104
104
  qualifiedName?: string;
105
+ isDeleted?: boolean;
105
106
  }
106
107
  /**
107
108
  * Pure read-only view over the file and callable indexes. Does NOT
@@ -136,6 +136,7 @@ export const createSymbolTable = () => {
136
136
  ? { templateArguments: metadata.templateArguments }
137
137
  : {}),
138
138
  ...(metadata?.ownerId !== undefined ? { ownerId: metadata.ownerId } : {}),
139
+ ...(metadata?.isDeleted === true ? { isDeleted: true } : {}),
139
140
  };
140
141
  // A. File Index — unconditional.
141
142
  if (!fileIndex.has(filePath)) {
@@ -49,6 +49,7 @@ export const mergeChunkResults = (graph, symbolTable, chunkResults, exportedType
49
49
  templateArguments: sym.templateArguments,
50
50
  ownerId: sym.ownerId,
51
51
  qualifiedName: sym.qualifiedName,
52
+ isDeleted: sym.isDeleted,
52
53
  });
53
54
  }
54
55
  if (exportedTypeMap) {
@@ -144,6 +145,27 @@ chunkHash) => {
144
145
  .join(', ');
145
146
  logger.warn(` Skipped unsupported languages: ${summary}`);
146
147
  }
148
+ // Clone-safety telemetry (#2112): files whose parse output carried a value
149
+ // the structured-clone algorithm couldn't serialize across the worker
150
+ // boundary. The worker sanitized/dropped the offending value so the run
151
+ // could complete; surface the (rare) data loss so it's visible and the
152
+ // offending extractor can be fixed at source.
153
+ const skippedPaths = [];
154
+ for (const result of chunkResults) {
155
+ for (const entry of result.skippedPaths ?? [])
156
+ skippedPaths.push(entry);
157
+ }
158
+ if (skippedPaths.length > 0) {
159
+ // Keep the per-file reason ("stripped N value(s) from nodes" /
160
+ // "dropped non-serializable parsedFiles entry") — it distinguishes a
161
+ // recoverable strip from a whole-record drop, which a path-only line loses.
162
+ const shown = skippedPaths
163
+ .slice(0, 10)
164
+ .map((e) => `${e.path} (${e.reason})`)
165
+ .join(', ');
166
+ const more = skippedPaths.length > 10 ? ` …and ${skippedPaths.length - 10} more` : '';
167
+ logger.warn(` Sanitized ${skippedPaths.length} file(s) with non-serializable parse output: ${shown}${more}`);
168
+ }
147
169
  onFileProgress?.(total, total, 'done');
148
170
  return chunkResults;
149
171
  };
@@ -18,6 +18,7 @@ import { BindingAccumulator, enrichExportedTypeMap, } from '../binding-accumulat
18
18
  import { mergeChunkResults, dispatchChunkParse } from '../parsing-processor.js';
19
19
  import { fileContentHash, computeChunkHash, loadParseCacheChunk, persistParseCacheChunk, PARSE_CACHE_VERSION, } from '../../../storage/parse-cache.js';
20
20
  import { clearParsedFileStore, persistParsedFileChunk, getDurableParsedFileDir, loadDurableParsedFileIndex, restoreDurableParsedFileShard, } from '../../../storage/parsedfile-store.js';
21
+ import { DEFAULT_PDG_MAX_FUNCTION_LINES } from '../cfg/collect.js';
21
22
  import { processRoutesFromExtracted, buildExportedTypeMapFromGraph, } from '../call-processor.js';
22
23
  import { createSemanticModel } from '../model/index.js';
23
24
  import { getLanguageFromFilename, } from '../../../_shared/index.js';
@@ -319,6 +320,10 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
319
320
  // Initialized below before the chunk loop (same deferred-init pattern
320
321
  // as `parsedFileStorePath`); this closure only runs from the loop.
321
322
  durableParsedFileStoragePath: durableParsedFileDir,
323
+ // CFG/PDG opt-in (#2081 M1) — baked into each worker's workerData so the
324
+ // worker builds + attaches cfgSideChannel. Off by default.
325
+ pdg: options?.pdg === true,
326
+ pdgMaxFunctionLines: options?.pdgMaxFunctionLines,
322
327
  // Fan each chunk across the whole pool (#worker-idle): without this a
323
328
  // chunk smaller than the 8 MB sub-batch cap became a single job on a
324
329
  // single worker. Honors an explicit `subBatchMaxBytes` / env override.
@@ -566,7 +571,19 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
566
571
  filePath: f.path,
567
572
  contentHash: fileContentHash(f.content),
568
573
  }));
569
- chunkHash = computeChunkHash(entries);
574
+ chunkHash = computeChunkHash(entries,
575
+ // Only worker-visible pdg config participates in the key —
576
+ // pdgMaxEdgesPerFunction is emit-time-only and deliberately
577
+ // excluded (see PdgCacheKey in parse-cache.ts; #2099 F3). The line
578
+ // cap is RESOLVED to the worker's default before folding so an
579
+ // explicit-default run shares the default run's keys (the worker
580
+ // output is byte-identical either way).
581
+ options?.pdg === true
582
+ ? {
583
+ pdg: true,
584
+ maxFunctionLines: options?.pdgMaxFunctionLines ?? DEFAULT_PDG_MAX_FUNCTION_LINES,
585
+ }
586
+ : false);
570
587
  }
571
588
  const cachedRaw = chunkHash && parseCache ? await loadParseCacheChunk(parseCache, chunkHash) : undefined;
572
589
  // Track every chunk hash we touched so the orchestrator can
@@ -297,22 +297,72 @@ export const routesPhase = {
297
297
  // scan JS/TS consumer files for calls to those wrapper functions with
298
298
  // URL-like string arguments and add them to allFetchCalls so
299
299
  // processNextjsFetchRoutes can create FETCHES edges.
300
- if (allFetchWrapperDefs && allFetchWrapperDefs.length > 0 && routeRegistry.size > 0) {
301
- const wrapperNames = new Set(allFetchWrapperDefs.map((d) => d.functionName));
300
+ // Wrapper names come from two sources: functions the parse phase
301
+ // auto-detected as calling the bare global `fetch()`, plus any names the
302
+ // user declared in `.gitnexusrc` `fetchWrappers` (#1589/#1852 residual).
303
+ // Config names let an axios/custom-client wrapper — or one named outside the
304
+ // built-in convention — still produce route_map consumers; without them it
305
+ // silently falls back to `consumers: []`. Configured names alone are enough
306
+ // to run the scan even when nothing was auto-detected.
307
+ // Configured names are already validated/trimmed/de-duped/capped by
308
+ // analyze-config.ts — trusted as-is (#1589/#1852 review F9, dropped the
309
+ // redundant re-trim/re-filter). The single filter below guards only the
310
+ // auto-detected `functionName`s, which have no shape guarantee.
311
+ const configuredWrappers = ctx.options?.fetchWrappers ?? [];
312
+ const wrapperNames = new Set([...(allFetchWrapperDefs ?? []).map((d) => d.functionName), ...configuredWrappers].filter((n) => typeof n === 'string' && n.trim().length > 0));
313
+ if (wrapperNames.size > 0 && routeRegistry.size > 0) {
302
314
  const jsFiles = allPaths.filter((p) => /\.[jt]sx?$/.test(p));
303
- if (jsFiles.length > 0 && wrapperNames.size > 0) {
304
- const jsContents = await readFileContents(ctx.repoPath, jsFiles);
305
- for (const [filePath, content] of jsContents) {
306
- for (const name of wrapperNames) {
307
- const regex = new RegExp(`\\b${escapeRegex(name)}\\s*\\(\\s*['"\`](/[^'"\`\\s)]+)['"\`]`, 'g');
308
- let match;
309
- while ((match = regex.exec(content)) !== null) {
310
- allFetchCalls.push({
311
- filePath,
312
- fetchURL: match[1],
313
- lineNumber: content.substring(0, match.index).split('\n').length,
314
- });
315
+ if (jsFiles.length > 0) {
316
+ // Reuse contents already read for handler extraction; only read the
317
+ // remainder (mirrors the Expo block above). Avoids a second full read of
318
+ // files we already have in memory.
319
+ const unreadJsFiles = jsFiles.filter((p) => !handlerContents?.has(p));
320
+ const extraContents = unreadJsFiles.length > 0
321
+ ? await readFileContents(ctx.repoPath, unreadJsFiles)
322
+ : new Map();
323
+ // One alternation regex over every wrapper name per file — O(files), not
324
+ // O(files × wrappers) (#1852 review F3). Names are escaped and grouped
325
+ // non-capturing so capture group 1 stays the URL. The left boundary is a
326
+ // negative lookbehind, not `\b`: a bare configured name like `get` must
327
+ // match the free call `get('/x')` but NOT a member access `client.get(`
328
+ // (a `.get(` on an unrelated object), and `apiFetch` must not match
329
+ // `myApiFetch`. Member-style wrappers are configured with the dot
330
+ // (`client.get`), where the `.` is part of the pattern. The `u` flag +
331
+ // Unicode property classes make the boundary cover non-ASCII identifier
332
+ // characters too — ASCII `\w` would let `caféget('/x')` match `get`
333
+ // (#1852 review F10).
334
+ const alternation = [...wrapperNames].map(escapeRegex).join('|');
335
+ const wrapperCallRegex = new RegExp(`(?<![.\\p{L}\\p{N}_$])(?:${alternation})\\s*\\(\\s*['"\`](/[^'"\`\\s)]+)['"\`]`, 'gu');
336
+ const scanContent = (filePath, content) => {
337
+ wrapperCallRegex.lastIndex = 0;
338
+ // 1-based line number via a running newline counter: matches arrive in
339
+ // ascending index, so accumulate newlines incrementally instead of
340
+ // re-allocating `content.substring(0, match.index).split('\n')` on
341
+ // every match (#1852 review F12). Output is identical.
342
+ let line = 1;
343
+ let scanned = 0;
344
+ let match;
345
+ while ((match = wrapperCallRegex.exec(content)) !== null) {
346
+ for (; scanned < match.index; scanned++) {
347
+ if (content.charCodeAt(scanned) === 10 /* '\n' */)
348
+ line++;
315
349
  }
350
+ allFetchCalls.push({
351
+ filePath,
352
+ fetchURL: match[1],
353
+ lineNumber: line,
354
+ });
355
+ }
356
+ };
357
+ for (const [filePath, content] of extraContents)
358
+ scanContent(filePath, content);
359
+ // Also scan already-read JS/TS handler files (a handler can itself
360
+ // consume another route through a wrapper).
361
+ if (handlerContents) {
362
+ for (const p of jsFiles) {
363
+ const cached = handlerContents.get(p);
364
+ if (cached !== undefined)
365
+ scanContent(p, cached);
316
366
  }
317
367
  }
318
368
  }
@@ -25,6 +25,54 @@ export interface PipelineOptions {
25
25
  * to retain those nodes under `skipGraphPhases`.
26
26
  */
27
27
  skipGraphPhases?: boolean;
28
+ /**
29
+ * Build the control-flow-graph / PDG substrate (#2081 M1, opt-in via `--pdg`).
30
+ * Off by default: workers skip all CFG work and emit no `cfgSideChannel`, and
31
+ * scope-resolution emits no BasicBlock nodes or CFG edges — so the default
32
+ * graph is byte-identical to a pre-#2081 run. Folded into the parse-cache key
33
+ * so a pdg-off warm cache is not reused on a `--pdg` run.
34
+ */
35
+ pdg?: boolean;
36
+ /**
37
+ * Per-function source-line cap for worker-side CFG construction.
38
+ * `undefined` ⇒ the worker applies `DEFAULT_PDG_MAX_FUNCTION_LINES`; `0` ⇒ no
39
+ * cap (unlimited). Bounds the cost of a pathological mega-function; over-cap
40
+ * functions are skipped (no CFG emitted for them). No CLI flag in M1 —
41
+ * programmatic / server analyze-worker path only.
42
+ */
43
+ pdgMaxFunctionLines?: number;
44
+ /**
45
+ * Per-function CFG edge cap for the scope-resolution emit step.
46
+ * `undefined` ⇒ `DEFAULT_MAX_CFG_EDGES_PER_FUNCTION`; `0` ⇒ no cap (unlimited).
47
+ * Over-cap functions stop at the cap and log a structured drop warning (no
48
+ * silent truncation). No CLI flag in M1 — programmatic / server path only.
49
+ */
50
+ pdgMaxEdgesPerFunction?: number;
51
+ /**
52
+ * Per-function REACHING_DEF edge cap for the scope-resolution emit step
53
+ * (#2082 M2). `undefined` ⇒ `DEFAULT_PDG_MAX_REACHING_DEF_EDGES_PER_FUNCTION`
54
+ * (4000); `0` ⇒ no cap (unlimited). Emit-time-only — NOT folded into the
55
+ * parse-cache chunk key (the worker never sees it); recorded in
56
+ * `RepoMeta.pdg` so a cap change forces a full writeback. No CLI flag —
57
+ * programmatic / server path only, like the M1 caps.
58
+ */
59
+ pdgMaxReachingDefEdgesPerFunction?: number;
60
+ /**
61
+ * Per-function taint findings cap for the scope-resolution taint pass
62
+ * (#2083 M3). `undefined` ⇒ `DEFAULT_PDG_MAX_TAINT_FINDINGS_PER_FUNCTION`
63
+ * (200); `0` ⇒ no cap (unlimited). Emit-time-only — NOT folded into the
64
+ * parse-cache chunk key; recorded resolved in `RepoMeta.pdg` so a cap
65
+ * change forces a full writeback. No CLI flag or rc key (KTD8) —
66
+ * programmatic / server path only, like the other pdg caps.
67
+ */
68
+ pdgMaxTaintFindingsPerFunction?: number;
69
+ /**
70
+ * Per-finding taint hop cap (#2083 M3, KTD6 — bounds the persisted
71
+ * hop-encoded `reason`). `undefined` ⇒ `DEFAULT_PDG_MAX_TAINT_HOPS` (32);
72
+ * `0` ⇒ no cap (unlimited). Same emit-time-only / RepoMeta-stamped /
73
+ * no-CLI-flag discipline as `pdgMaxTaintFindingsPerFunction`.
74
+ */
75
+ pdgMaxTaintHops?: number;
28
76
  /**
29
77
  * Request parsing with the worker pool disabled. The sequential parser was
30
78
  * removed — the worker pool is the sole parse path — so setting this now
@@ -104,6 +152,15 @@ export interface PipelineOptions {
104
152
  * `process.env` state across invocations. When undefined, the env var decides.
105
153
  */
106
154
  keepLocalValueSymbols?: boolean;
155
+ /**
156
+ * Extra fetch-wrapper function names to treat as HTTP consumers, threaded
157
+ * from `.gitnexusrc` `fetchWrappers` via `AnalyzeOptions` (#1589/#1852
158
+ * residual). The routes phase unions these with the auto-detected `fetch()`
159
+ * wrappers when scanning for `route_map` consumers, so a wrapper named outside
160
+ * the built-in convention (or built on axios / a custom client) is still
161
+ * traced. Empty/undefined leaves behavior unchanged.
162
+ */
163
+ fetchWrappers?: readonly string[];
107
164
  }
108
165
  /**
109
166
  * All pipeline phases with their dependency relationships.