@crownpeak/dqm-react-component-dev-mcp 1.2.0

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 (444) hide show
  1. package/README.md +138 -0
  2. package/data/.env.example +22 -0
  3. package/data/.gitattributes +47 -0
  4. package/data/.glfrc.json +7 -0
  5. package/data/.husky/pre-commit +5 -0
  6. package/data/.nvmrc +1 -0
  7. package/data/CHANGELOG.md +75 -0
  8. package/data/CODE_OF_CONDUCT.md +129 -0
  9. package/data/CONTRIBUTING.md +203 -0
  10. package/data/DOCS-STRUCTURE.md +307 -0
  11. package/data/I18N.md +292 -0
  12. package/data/LICENSE +22 -0
  13. package/data/README.md +315 -0
  14. package/data/SECURITY.md +125 -0
  15. package/data/WIKI-DEPLOYMENT.md +348 -0
  16. package/data/docs/AI-FEATURES.md +610 -0
  17. package/data/docs/API-REFERENCE.md +1022 -0
  18. package/data/docs/AUTHENTICATION.md +301 -0
  19. package/data/docs/BACKEND-API.md +468 -0
  20. package/data/docs/DEVELOPMENT.md +375 -0
  21. package/data/docs/EXAMPLES.md +622 -0
  22. package/data/docs/MCP-SERVER.md +307 -0
  23. package/data/docs/MIGRATION-GUIDE.md +367 -0
  24. package/data/docs/NPM-PUBLISH.md +193 -0
  25. package/data/docs/QUICKSTART.md +206 -0
  26. package/data/docs/REDIS-SETUP.md +162 -0
  27. package/data/docs/SERVER.md +228 -0
  28. package/data/docs/TROUBLESHOOTING.md +657 -0
  29. package/data/docs/WIDGET-GUIDE.md +638 -0
  30. package/data/docs/WIKI-HOME.md +58 -0
  31. package/data/docs/WIKI-SIDEBAR.md +39 -0
  32. package/data/package.json +171 -0
  33. package/data/playwright.config.ts +64 -0
  34. package/data/probe/.cargo/config.toml +10 -0
  35. package/data/probe/.claude/commands/performance-review.md +15 -0
  36. package/data/probe/.clinerules +288 -0
  37. package/data/probe/.dockerignore +57 -0
  38. package/data/probe/.githooks/post-commit +11 -0
  39. package/data/probe/.githooks/pre-commit +99 -0
  40. package/data/probe/.githooks/pre-commit-vow +9 -0
  41. package/data/probe/.prompts/engineer.md +41 -0
  42. package/data/probe/.roomodes +28 -0
  43. package/data/probe/.windsurfrules +0 -0
  44. package/data/probe/BASH_TOOL_SUMMARY.md +148 -0
  45. package/data/probe/BENCHMARKING.md +256 -0
  46. package/data/probe/CLAUDE.md +226 -0
  47. package/data/probe/CODE_OF_CONDUCT.md +128 -0
  48. package/data/probe/CONTRIBUTING.md +193 -0
  49. package/data/probe/Cargo.toml +120 -0
  50. package/data/probe/Cross.toml +10 -0
  51. package/data/probe/DOCKER-README.md +224 -0
  52. package/data/probe/Dockerfile +32 -0
  53. package/data/probe/ENHANCED_DEBUG_TELEMETRY.md +188 -0
  54. package/data/probe/LICENSE +201 -0
  55. package/data/probe/Makefile +210 -0
  56. package/data/probe/README.md +824 -0
  57. package/data/probe/SECURITY.md +67 -0
  58. package/data/probe/WINDOWS-GUIDE.md +294 -0
  59. package/data/probe/benches/parsing_benchmarks.rs +370 -0
  60. package/data/probe/benches/search_benchmarks.rs +599 -0
  61. package/data/probe/benches/simd_benchmarks.rs +372 -0
  62. package/data/probe/benches/timing_benchmarks.rs +287 -0
  63. package/data/probe/build-windows.bat +229 -0
  64. package/data/probe/codex-config/config.toml +6 -0
  65. package/data/probe/docs/PERFORMANCE_OPTIMIZATION.md +161 -0
  66. package/data/probe/examples/cache_demo.rs +46 -0
  67. package/data/probe/examples/chat/.dockerignore +37 -0
  68. package/data/probe/examples/chat/ChatSessionManager.js +295 -0
  69. package/data/probe/examples/chat/Dockerfile +98 -0
  70. package/data/probe/examples/chat/LICENSE +201 -0
  71. package/data/probe/examples/chat/LOCAL_IMAGE_SUPPORT.md +195 -0
  72. package/data/probe/examples/chat/MCP_INTEGRATION.md +400 -0
  73. package/data/probe/examples/chat/README.md +338 -0
  74. package/data/probe/examples/chat/TRACING.md +226 -0
  75. package/data/probe/examples/chat/appTracer.js +968 -0
  76. package/data/probe/examples/chat/auth.js +76 -0
  77. package/data/probe/examples/chat/bin/probe-chat.js +13 -0
  78. package/data/probe/examples/chat/build.js +104 -0
  79. package/data/probe/examples/chat/cancelRequest.js +84 -0
  80. package/data/probe/examples/chat/demo-agentic-image-flow.js +88 -0
  81. package/data/probe/examples/chat/demo-local-images.js +128 -0
  82. package/data/probe/examples/chat/fileSpanExporter.js +181 -0
  83. package/data/probe/examples/chat/implement/README.md +228 -0
  84. package/data/probe/examples/chat/implement/backends/AiderBackend.js +750 -0
  85. package/data/probe/examples/chat/implement/backends/BaseBackend.js +276 -0
  86. package/data/probe/examples/chat/implement/backends/ClaudeCodeBackend.js +767 -0
  87. package/data/probe/examples/chat/implement/backends/MockBackend.js +237 -0
  88. package/data/probe/examples/chat/implement/backends/registry.js +85 -0
  89. package/data/probe/examples/chat/implement/core/BackendManager.js +567 -0
  90. package/data/probe/examples/chat/implement/core/ImplementTool.js +354 -0
  91. package/data/probe/examples/chat/implement/core/config.js +428 -0
  92. package/data/probe/examples/chat/implement/core/timeouts.js +58 -0
  93. package/data/probe/examples/chat/implement/core/utils.js +496 -0
  94. package/data/probe/examples/chat/implement/types/BackendTypes.js +126 -0
  95. package/data/probe/examples/chat/index.js +669 -0
  96. package/data/probe/examples/chat/mcpServer.js +341 -0
  97. package/data/probe/examples/chat/npm/LICENSE +15 -0
  98. package/data/probe/examples/chat/npm/README.md +168 -0
  99. package/data/probe/examples/chat/npm/bin/probe-chat.js +156 -0
  100. package/data/probe/examples/chat/npm/index.js +259 -0
  101. package/data/probe/examples/chat/npm/package.json +54 -0
  102. package/data/probe/examples/chat/package.json +102 -0
  103. package/data/probe/examples/chat/probeChat.js +456 -0
  104. package/data/probe/examples/chat/probeTool.js +491 -0
  105. package/data/probe/examples/chat/storage/JsonChatStorage.js +476 -0
  106. package/data/probe/examples/chat/telemetry.js +281 -0
  107. package/data/probe/examples/chat/test/integration/chatFlows.test.js +320 -0
  108. package/data/probe/examples/chat/test/integration/toolCalling.test.js +471 -0
  109. package/data/probe/examples/chat/test/mocks/mockLLMProvider.js +269 -0
  110. package/data/probe/examples/chat/test/test-backends.js +90 -0
  111. package/data/probe/examples/chat/test/testUtils.js +530 -0
  112. package/data/probe/examples/chat/test/unit/backendTimeout.test.js +161 -0
  113. package/data/probe/examples/chat/test/unit/packageFiles.test.js +120 -0
  114. package/data/probe/examples/chat/test/verify-tests.js +118 -0
  115. package/data/probe/examples/chat/test-agentic-image-loading.js +294 -0
  116. package/data/probe/examples/chat/test-ai-sdk-telemetry.js +204 -0
  117. package/data/probe/examples/chat/test-chat-tracing.js +38 -0
  118. package/data/probe/examples/chat/test-direct-function.js +49 -0
  119. package/data/probe/examples/chat/test-file-size-validation.js +103 -0
  120. package/data/probe/examples/chat/test-full-mcp-integration.js +258 -0
  121. package/data/probe/examples/chat/test-github-context.txt +12 -0
  122. package/data/probe/examples/chat/test-hierarchy.js +203 -0
  123. package/data/probe/examples/chat/test-image-spans.js +37 -0
  124. package/data/probe/examples/chat/test-local-image-reading.js +176 -0
  125. package/data/probe/examples/chat/test-mcp-integration.js +136 -0
  126. package/data/probe/examples/chat/test-mcp-probe-server.js +161 -0
  127. package/data/probe/examples/chat/test-mcp-with-ai.js +279 -0
  128. package/data/probe/examples/chat/test-multiple-allowed-dirs.js +111 -0
  129. package/data/probe/examples/chat/test-probe-mcp-server.js +110 -0
  130. package/data/probe/examples/chat/test-security-validation.js +145 -0
  131. package/data/probe/examples/chat/test-simple-tracing.js +32 -0
  132. package/data/probe/examples/chat/test-trace-verification.js +235 -0
  133. package/data/probe/examples/chat/test-tracing.js +114 -0
  134. package/data/probe/examples/chat/tokenCounter.js +419 -0
  135. package/data/probe/examples/chat/tokenUsageDisplay.js +134 -0
  136. package/data/probe/examples/chat/webServer.js +1103 -0
  137. package/data/probe/examples/reranker/Cargo.toml +33 -0
  138. package/data/probe/examples/reranker/DEBUG_OUTPUT_ANALYSIS.md +71 -0
  139. package/data/probe/examples/reranker/MODELS.md +66 -0
  140. package/data/probe/examples/reranker/MODEL_COMPARISON.md +60 -0
  141. package/data/probe/examples/reranker/MULTI_MODEL_ANALYSIS.md +176 -0
  142. package/data/probe/examples/reranker/PERFORMANCE_SUMMARY.md +156 -0
  143. package/data/probe/examples/reranker/README.md +347 -0
  144. package/data/probe/examples/reranker/RUST_BERT_COMPARISON.md +82 -0
  145. package/data/probe/examples/reranker/TOKENIZATION_GUIDE.md +120 -0
  146. package/data/probe/examples/reranker/check_rust_tokenizer.py +108 -0
  147. package/data/probe/examples/reranker/convert_to_torchscript.py +109 -0
  148. package/data/probe/examples/reranker/debug_scoring.py +189 -0
  149. package/data/probe/examples/reranker/debug_tokenization.py +154 -0
  150. package/data/probe/examples/reranker/download_models.sh +73 -0
  151. package/data/probe/examples/reranker/requirements.txt +13 -0
  152. package/data/probe/examples/reranker/run_comprehensive_benchmark.sh +83 -0
  153. package/data/probe/examples/reranker/rust_bert_test/Cargo.toml +12 -0
  154. package/data/probe/examples/reranker/rust_bert_test/README.md +54 -0
  155. package/data/probe/examples/reranker/simple_test.py +50 -0
  156. package/data/probe/examples/reranker/test_all_models.sh +63 -0
  157. package/data/probe/examples/reranker/test_bert_results.sh +44 -0
  158. package/data/probe/examples/reranker/test_cross_encoder.py +334 -0
  159. package/data/probe/examples/reranker/test_cross_encoder.sh +80 -0
  160. package/data/probe/examples/reranker/test_exact_comparison.py +151 -0
  161. package/data/probe/examples/reranker/test_parallel_performance.sh +56 -0
  162. package/data/probe/examples/reranker/test_scores.py +132 -0
  163. package/data/probe/install.ps1 +508 -0
  164. package/data/probe/install.sh +460 -0
  165. package/data/probe/npm/CLONE_METHOD_EXAMPLES.md +596 -0
  166. package/data/probe/npm/CONTEXT_COMPACTION.md +303 -0
  167. package/data/probe/npm/DELEGATE_TOOL_README.md +166 -0
  168. package/data/probe/npm/MAID_INTEGRATION.md +313 -0
  169. package/data/probe/npm/MCP_INTEGRATION_SUMMARY.md +241 -0
  170. package/data/probe/npm/README.md +824 -0
  171. package/data/probe/npm/bin/.gitignore +7 -0
  172. package/data/probe/npm/bin/.gitkeep +0 -0
  173. package/data/probe/npm/bin/README.md +12 -0
  174. package/data/probe/npm/bin/probe +167 -0
  175. package/data/probe/npm/docs/CLAUDE_CODE_INTEGRATION.md +414 -0
  176. package/data/probe/npm/docs/CODEX_INTEGRATION.md +502 -0
  177. package/data/probe/npm/docs/EDIT_CREATE_TOOLS.md +233 -0
  178. package/data/probe/npm/docs/RETRY_AND_FALLBACK.md +674 -0
  179. package/data/probe/npm/example-usage.js +335 -0
  180. package/data/probe/npm/examples/multi-engine-demo.js +117 -0
  181. package/data/probe/npm/examples/probe-agent-cli.js +113 -0
  182. package/data/probe/npm/examples/test-agent-edit.js +114 -0
  183. package/data/probe/npm/examples/test-edit-create.js +120 -0
  184. package/data/probe/npm/examples/test-edit-direct.js +114 -0
  185. package/data/probe/npm/index.d.ts +744 -0
  186. package/data/probe/npm/jest.config.js +52 -0
  187. package/data/probe/npm/package.json +117 -0
  188. package/data/probe/npm/scripts/build-agent.cjs +75 -0
  189. package/data/probe/npm/scripts/build-cjs.js +124 -0
  190. package/data/probe/npm/scripts/build-mcp.cjs +36 -0
  191. package/data/probe/npm/scripts/postinstall.js +216 -0
  192. package/data/probe/npm/test-codex-e2e.js +78 -0
  193. package/data/probe/npm/test-download-lock.js +109 -0
  194. package/data/probe/npm/test-grep-security.js +94 -0
  195. package/data/probe/npm/test-grep-simplified.js +63 -0
  196. package/data/probe/npm/test-grep.js +51 -0
  197. package/data/probe/npm/tests/README.md +96 -0
  198. package/data/probe/npm/tests/agent-compact-history.test.js +174 -0
  199. package/data/probe/npm/tests/allow-tests-default.test.js +151 -0
  200. package/data/probe/npm/tests/contextCompactor.test.js +498 -0
  201. package/data/probe/npm/tests/delegate-config.test.js +353 -0
  202. package/data/probe/npm/tests/delegate-integration.test.js +348 -0
  203. package/data/probe/npm/tests/extractor-integration.test.js +162 -0
  204. package/data/probe/npm/tests/extractor.test.js +317 -0
  205. package/data/probe/npm/tests/fixtures/sampleDiagrams.js +267 -0
  206. package/data/probe/npm/tests/integration/claude-code-auto-fallback.spec.js +148 -0
  207. package/data/probe/npm/tests/integration/claude-code-multi-step.spec.js +127 -0
  208. package/data/probe/npm/tests/integration/claude-code-tool-events.spec.js +163 -0
  209. package/data/probe/npm/tests/integration/codex-auto-fallback.spec.js +191 -0
  210. package/data/probe/npm/tests/integration/codex-tool-events.spec.js +147 -0
  211. package/data/probe/npm/tests/integration/examplesChatMcp.test.js +402 -0
  212. package/data/probe/npm/tests/integration/mcpDotenvSupport.test.js +174 -0
  213. package/data/probe/npm/tests/integration/mcpErrorHandling.test.js +566 -0
  214. package/data/probe/npm/tests/integration/mcpRobustness.test.js +564 -0
  215. package/data/probe/npm/tests/integration/mcpStdoutPurity.test.js +355 -0
  216. package/data/probe/npm/tests/integration/probeAgentMcp.test.js +398 -0
  217. package/data/probe/npm/tests/integration/retryFallback.test.js +368 -0
  218. package/data/probe/npm/tests/integration/schema-in-initial-message.test.js +318 -0
  219. package/data/probe/npm/tests/integration/schema-validation-loop-prevention.test.js +244 -0
  220. package/data/probe/npm/tests/integration/schemaRetryLogic.test.js +94 -0
  221. package/data/probe/npm/tests/integration/validationFlow.test.js +329 -0
  222. package/data/probe/npm/tests/manual/test-codex-basic.js +110 -0
  223. package/data/probe/npm/tests/mcp/mcpClientManager.test.js +614 -0
  224. package/data/probe/npm/tests/mcp/mcpConfig.test.js +359 -0
  225. package/data/probe/npm/tests/mcp/mcpXmlBridge.test.js +436 -0
  226. package/data/probe/npm/tests/mcp/mockMcpServer.js +510 -0
  227. package/data/probe/npm/tests/mcp-strict-syntax.test.js +319 -0
  228. package/data/probe/npm/tests/mermaidQuoteEscaping.test.js +214 -0
  229. package/data/probe/npm/tests/nestedQuoteFix.test.js +40 -0
  230. package/data/probe/npm/tests/setup.js +46 -0
  231. package/data/probe/npm/tests/unit/allowed-tools.test.js +513 -0
  232. package/data/probe/npm/tests/unit/attempt-completion-closing-tag-in-content.test.js +188 -0
  233. package/data/probe/npm/tests/unit/attemptCompletionJsonFix.test.js +238 -0
  234. package/data/probe/npm/tests/unit/attemptCompletionJsonIssue.test.js +128 -0
  235. package/data/probe/npm/tests/unit/backtickAutoFix.test.js +35 -0
  236. package/data/probe/npm/tests/unit/bash-probe-agent-integration.test.js +389 -0
  237. package/data/probe/npm/tests/unit/bash-simple-commands.test.js +324 -0
  238. package/data/probe/npm/tests/unit/bash-tool-comprehensive.test.js +371 -0
  239. package/data/probe/npm/tests/unit/bash-tool-integration.test.js +310 -0
  240. package/data/probe/npm/tests/unit/bash-tool.test.js +341 -0
  241. package/data/probe/npm/tests/unit/completion-prompt.test.js +379 -0
  242. package/data/probe/npm/tests/unit/cwd-path-options.test.js +287 -0
  243. package/data/probe/npm/tests/unit/delegate-limits.test.js +422 -0
  244. package/data/probe/npm/tests/unit/direct-content-attempt-completion.test.js +235 -0
  245. package/data/probe/npm/tests/unit/edit-create-tools.test.js +609 -0
  246. package/data/probe/npm/tests/unit/enhancedMermaidValidation.test.js +577 -0
  247. package/data/probe/npm/tests/unit/extract-content.test.js +83 -0
  248. package/data/probe/npm/tests/unit/extract-multiple-targets.test.js +89 -0
  249. package/data/probe/npm/tests/unit/fallbackManager.test.js +442 -0
  250. package/data/probe/npm/tests/unit/githubCompatibilityValidation.test.js +258 -0
  251. package/data/probe/npm/tests/unit/imageConfig.test.js +149 -0
  252. package/data/probe/npm/tests/unit/imagePathResolution.test.js +345 -0
  253. package/data/probe/npm/tests/unit/json-fixing-agent.test.js +238 -0
  254. package/data/probe/npm/tests/unit/json-validation-enhanced-errors.test.js +199 -0
  255. package/data/probe/npm/tests/unit/jsonValidationInfiniteLoopFix.test.js +228 -0
  256. package/data/probe/npm/tests/unit/maidIntegration.test.js +139 -0
  257. package/data/probe/npm/tests/unit/maxIterationsWarning.test.js +195 -0
  258. package/data/probe/npm/tests/unit/mermaidEdgeLabelFix.test.js +161 -0
  259. package/data/probe/npm/tests/unit/mermaidHtmlEntities.test.js +76 -0
  260. package/data/probe/npm/tests/unit/mermaidInfiniteLoopFix.test.js +64 -0
  261. package/data/probe/npm/tests/unit/mermaidValidation.test.js +723 -0
  262. package/data/probe/npm/tests/unit/mermaidValidationVisorExample.test.js +309 -0
  263. package/data/probe/npm/tests/unit/probe-agent-clone-realistic.test.js +643 -0
  264. package/data/probe/npm/tests/unit/probe-agent-clone.test.js +476 -0
  265. package/data/probe/npm/tests/unit/probe-agent-delegate.test.js +400 -0
  266. package/data/probe/npm/tests/unit/probe-agent-model-option.test.js +118 -0
  267. package/data/probe/npm/tests/unit/probeTool-security.test.js +283 -0
  268. package/data/probe/npm/tests/unit/readImageTool.test.js +418 -0
  269. package/data/probe/npm/tests/unit/retryManager.test.js +317 -0
  270. package/data/probe/npm/tests/unit/schema-aware-reminders.test.js +288 -0
  271. package/data/probe/npm/tests/unit/schemaDefinitionDetection.test.js +115 -0
  272. package/data/probe/npm/tests/unit/schemaUtils.test.js +1268 -0
  273. package/data/probe/npm/tests/unit/simpleTelemetry.test.js +282 -0
  274. package/data/probe/npm/tests/unit/simplified-attempt-completion.test.js +274 -0
  275. package/data/probe/npm/tests/unit/single-quote-json-bug.test.js +231 -0
  276. package/data/probe/npm/tests/unit/subgraphAutoFix.test.js +110 -0
  277. package/data/probe/npm/tests/unit/system-prompt.test.js +32 -0
  278. package/data/probe/npm/tests/unit/types-probe-agent-options.test.js +42 -0
  279. package/data/probe/npm/tests/unit/xmlParsing.test.js +720 -0
  280. package/data/probe/npm/tsconfig.json +21 -0
  281. package/data/probe/result1.txt +19 -0
  282. package/data/probe/result2.txt +26 -0
  283. package/data/probe/scripts/benchmark.sh +270 -0
  284. package/data/probe/scripts/cache_memory_analysis.rs +844 -0
  285. package/data/probe/scripts/claude-hook-wrapper.sh +56 -0
  286. package/data/probe/site/.env.example +10 -0
  287. package/data/probe/site/DEPLOYMENT.md +86 -0
  288. package/data/probe/site/README.md +183 -0
  289. package/data/probe/site/adding-languages.md +135 -0
  290. package/data/probe/site/ai-chat.md +427 -0
  291. package/data/probe/site/ai-integration.md +1488 -0
  292. package/data/probe/site/blog/agentic-flow-custom-xml-protocol.md +407 -0
  293. package/data/probe/site/blog/index.md +118 -0
  294. package/data/probe/site/blog/v0.6.0-release.md +426 -0
  295. package/data/probe/site/blog.md +8 -0
  296. package/data/probe/site/changelog.md +200 -0
  297. package/data/probe/site/cli-mode.md +437 -0
  298. package/data/probe/site/code-extraction.md +436 -0
  299. package/data/probe/site/contributing/README.md +9 -0
  300. package/data/probe/site/contributing/documentation-cross-references.md +215 -0
  301. package/data/probe/site/contributing/documentation-maintenance.md +275 -0
  302. package/data/probe/site/contributing/documentation-structure.md +75 -0
  303. package/data/probe/site/documentation-cross-references.md +215 -0
  304. package/data/probe/site/documentation-guide.md +132 -0
  305. package/data/probe/site/documentation-maintenance.md +275 -0
  306. package/data/probe/site/features.md +147 -0
  307. package/data/probe/site/how-it-works.md +118 -0
  308. package/data/probe/site/index.md +175 -0
  309. package/data/probe/site/index.md.bak +133 -0
  310. package/data/probe/site/installation.md +235 -0
  311. package/data/probe/site/integrations/docker.md +248 -0
  312. package/data/probe/site/integrations/github-actions.md +413 -0
  313. package/data/probe/site/language-support-overview.md +168 -0
  314. package/data/probe/site/mcp-integration.md +587 -0
  315. package/data/probe/site/mcp-server.md +304 -0
  316. package/data/probe/site/navigation-structure.md +76 -0
  317. package/data/probe/site/nodejs-sdk.md +798 -0
  318. package/data/probe/site/output-formats.md +625 -0
  319. package/data/probe/site/package.json +21 -0
  320. package/data/probe/site/public/_headers +28 -0
  321. package/data/probe/site/public/_redirects +11 -0
  322. package/data/probe/site/quick-start.md +289 -0
  323. package/data/probe/site/search-functionality.md +291 -0
  324. package/data/probe/site/search-reference.md +291 -0
  325. package/data/probe/site/supported-languages.md +215 -0
  326. package/data/probe/site/use-cases/README.md +8 -0
  327. package/data/probe/site/use-cases/advanced-cli.md +253 -0
  328. package/data/probe/site/use-cases/ai-code-editors.md +239 -0
  329. package/data/probe/site/use-cases/building-ai-tools.md +529 -0
  330. package/data/probe/site/use-cases/cli-ai-workflows.md +285 -0
  331. package/data/probe/site/use-cases/deploying-probe-web-interface.md +255 -0
  332. package/data/probe/site/use-cases/integrating-probe-into-ai-code-editors.md +161 -0
  333. package/data/probe/site/use-cases/nodejs-sdk.md +596 -0
  334. package/data/probe/site/use-cases/team-chat.md +350 -0
  335. package/data/probe/site/web-interface.md +434 -0
  336. package/data/probe/site/wrangler.toml +9 -0
  337. package/data/probe/test-api-key.sh +1 -0
  338. package/data/probe/test-probe-implementation/hello.js +7 -0
  339. package/data/probe/test_cases/demonstrate_early_termination_issues.sh +176 -0
  340. package/data/probe/test_cases/early_termination_issues.rs +533 -0
  341. package/data/probe/test_data/test_nested_struct.go +26 -0
  342. package/data/probe/tests/README.md +286 -0
  343. package/data/probe/tests/README_search_determinism_tests.md +116 -0
  344. package/data/probe/tests/adjacent_comment_test.rs +152 -0
  345. package/data/probe/tests/apostrophe_handling_tests.rs +132 -0
  346. package/data/probe/tests/block_filtering_with_ast_tests.rs +669 -0
  347. package/data/probe/tests/block_merging_tests.rs +396 -0
  348. package/data/probe/tests/c_outline_format_tests.rs +2179 -0
  349. package/data/probe/tests/cache_invalidation_issues.rs.disabled +682 -0
  350. package/data/probe/tests/cache_order_tests.rs +147 -0
  351. package/data/probe/tests/cache_query_scoping_tests.rs +221 -0
  352. package/data/probe/tests/cli_tests.rs +680 -0
  353. package/data/probe/tests/comment_context_integration_test.rs +240 -0
  354. package/data/probe/tests/common.rs +33 -0
  355. package/data/probe/tests/complex_block_merging_tests.rs +599 -0
  356. package/data/probe/tests/complex_query_block_filtering_tests.rs +422 -0
  357. package/data/probe/tests/control_flow_closing_braces_test.rs +91 -0
  358. package/data/probe/tests/cpp_outline_format_tests.rs +1507 -0
  359. package/data/probe/tests/csharp_outline_format_tests.rs +941 -0
  360. package/data/probe/tests/elastic_query_integration_tests.rs +922 -0
  361. package/data/probe/tests/extract_command_tests.rs +1848 -0
  362. package/data/probe/tests/extract_deduplication_tests.rs +146 -0
  363. package/data/probe/tests/extract_input_file_tests.rs +84 -0
  364. package/data/probe/tests/extract_prompt_tests.rs +102 -0
  365. package/data/probe/tests/filename_search_tests.rs +96 -0
  366. package/data/probe/tests/fixtures/user/AssemblyInfo.cs +3 -0
  367. package/data/probe/tests/github_extract_tests.rs +234 -0
  368. package/data/probe/tests/go_comment_test.rs +253 -0
  369. package/data/probe/tests/go_outline_format_tests.rs +2587 -0
  370. package/data/probe/tests/go_path_resolver_tests.rs +96 -0
  371. package/data/probe/tests/html_outline_format_tests.rs +637 -0
  372. package/data/probe/tests/integration_tests.rs +837 -0
  373. package/data/probe/tests/ip_whitelist_test.rs +148 -0
  374. package/data/probe/tests/java_outline_format_tests.rs +1611 -0
  375. package/data/probe/tests/javascript_extract_tests.rs +315 -0
  376. package/data/probe/tests/javascript_outline_format_tests.rs +1464 -0
  377. package/data/probe/tests/json_format_tests.rs +436 -0
  378. package/data/probe/tests/json_schema_validation_tests.rs +450 -0
  379. package/data/probe/tests/lib_usage.rs +60 -0
  380. package/data/probe/tests/line_comment_context_extension_test.rs +459 -0
  381. package/data/probe/tests/line_map_cache_tests.rs +114 -0
  382. package/data/probe/tests/markdown_integration_tests.rs +190 -0
  383. package/data/probe/tests/mocks/test_ip_whitelist.go +11 -0
  384. package/data/probe/tests/mocks/test_object.js +27 -0
  385. package/data/probe/tests/mocks/test_struct.go +50 -0
  386. package/data/probe/tests/multi_keyword_pattern_tests.rs +464 -0
  387. package/data/probe/tests/multi_language_syntax_integration_tests.rs +218 -0
  388. package/data/probe/tests/multiple_capture_groups_tests.rs +169 -0
  389. package/data/probe/tests/negative_compound_word_tests.rs +246 -0
  390. package/data/probe/tests/nested_symbol_extraction_tests.rs +99 -0
  391. package/data/probe/tests/outline_cross_file_interference_test.rs +335 -0
  392. package/data/probe/tests/outline_keyword_preservation_test.rs +67 -0
  393. package/data/probe/tests/output_format_edge_cases_tests.rs +693 -0
  394. package/data/probe/tests/parallel_extraction_tests.rs +178 -0
  395. package/data/probe/tests/parallel_search_tests.rs +355 -0
  396. package/data/probe/tests/path_resolver_tests.rs +698 -0
  397. package/data/probe/tests/php_outline_format_extended_tests.rs +928 -0
  398. package/data/probe/tests/php_outline_format_tests.rs +768 -0
  399. package/data/probe/tests/property_tests.proptest-regressions +9 -0
  400. package/data/probe/tests/property_tests.rs +118 -0
  401. package/data/probe/tests/python_outline_format_tests.rs +1538 -0
  402. package/data/probe/tests/query_command_json_tests.rs +438 -0
  403. package/data/probe/tests/query_command_tests.rs +232 -0
  404. package/data/probe/tests/query_command_xml_tests.rs +569 -0
  405. package/data/probe/tests/quoted_term_with_negative_keyword_tests.rs +216 -0
  406. package/data/probe/tests/required_terms_filename_tests.rs +116 -0
  407. package/data/probe/tests/ruby_outline_format_tests.rs +1011 -0
  408. package/data/probe/tests/rust_line_comment_context_test.rs +151 -0
  409. package/data/probe/tests/rust_outline_format_enhanced_tests.rs +725 -0
  410. package/data/probe/tests/rust_outline_format_tests.rs +843 -0
  411. package/data/probe/tests/schemas/xml_output_schema.xsd +38 -0
  412. package/data/probe/tests/search_determinism_tests.rs +451 -0
  413. package/data/probe/tests/search_hints_tests.rs +253 -0
  414. package/data/probe/tests/special_character_escaping_tests.rs +417 -0
  415. package/data/probe/tests/stemming_compound_word_filtering_tests.rs +535 -0
  416. package/data/probe/tests/strict_elastic_syntax_tests.rs +404 -0
  417. package/data/probe/tests/swift_outline_format_tests.rs +3319 -0
  418. package/data/probe/tests/symbols_tests.rs +166 -0
  419. package/data/probe/tests/test_file.rs +45 -0
  420. package/data/probe/tests/test_tokenize.rs +28 -0
  421. package/data/probe/tests/timeout_tests.rs +82 -0
  422. package/data/probe/tests/tokenization_tests.rs +195 -0
  423. package/data/probe/tests/tokenized_block_filtering_tests.rs +174 -0
  424. package/data/probe/tests/typescript_extract_tests.rs +214 -0
  425. package/data/probe/tests/typescript_outline_format_tests.rs +2188 -0
  426. package/data/probe/tests/xml_format_tests.rs +568 -0
  427. package/data/probe/tests/xml_schema_validation_tests.rs +497 -0
  428. package/data/scripts/postinstall.mjs +9 -0
  429. package/data/scripts/set-version.js +0 -0
  430. package/data/scripts/wiki-build.sh +111 -0
  431. package/data/scripts/wiki-deploy.sh +73 -0
  432. package/data/serve.json +12 -0
  433. package/data/test/demo-dynamic.html +134 -0
  434. package/data/test/demo-esm.html +105 -0
  435. package/data/test/demo-iife.html +78 -0
  436. package/data/tsconfig.json +7 -0
  437. package/data/vite.server.ts +483 -0
  438. package/data/vitest.config.ts +40 -0
  439. package/data/wiki/Home.md +58 -0
  440. package/data/wiki/_Sidebar.md +39 -0
  441. package/docs-mcp.config.json +20 -0
  442. package/package.json +56 -0
  443. package/src/config.js +111 -0
  444. package/src/index.js +395 -0
@@ -0,0 +1,2188 @@
1
+ use anyhow::Result;
2
+ use std::fs;
3
+ use tempfile::TempDir;
4
+
5
+ mod common;
6
+ use common::TestContext;
7
+
8
+ #[test]
9
+ fn test_typescript_outline_basic_symbols() -> Result<()> {
10
+ let temp_dir = TempDir::new()?;
11
+ let test_file = temp_dir.path().join("basic.ts");
12
+
13
+ let content = r#"// TypeScript classes, interfaces, and types for testing
14
+
15
+ import { Observable } from 'rxjs';
16
+
17
+ /**
18
+ * Interface for calculator operations.
19
+ */
20
+ interface CalculatorOperations {
21
+ add(x: number, y: number): number;
22
+ subtract(x: number, y: number): number;
23
+ multiply(x: number, y: number): number;
24
+ divide(x: number, y: number): number;
25
+ }
26
+
27
+ /**
28
+ * Type alias for calculation result.
29
+ */
30
+ type CalculationResult = {
31
+ value: number;
32
+ operation: string;
33
+ timestamp: Date;
34
+ };
35
+
36
+ /**
37
+ * Generic type for API responses.
38
+ */
39
+ type ApiResponse<T> = {
40
+ data: T;
41
+ success: boolean;
42
+ message?: string;
43
+ };
44
+
45
+ /**
46
+ * Enum for calculation operations.
47
+ */
48
+ enum OperationType {
49
+ ADDITION = 'ADD',
50
+ SUBTRACTION = 'SUB',
51
+ MULTIPLICATION = 'MUL',
52
+ DIVISION = 'DIV',
53
+ }
54
+
55
+ /**
56
+ * Abstract base class for calculators.
57
+ */
58
+ abstract class BaseCalculator {
59
+ protected history: CalculationResult[] = [];
60
+
61
+ abstract calculate(operation: OperationType, x: number, y: number): number;
62
+
63
+ getHistory(): readonly CalculationResult[] {
64
+ return this.history;
65
+ }
66
+
67
+ protected recordOperation(operation: OperationType, result: number): void {
68
+ this.history.push({
69
+ value: result,
70
+ operation: operation.toString(),
71
+ timestamp: new Date(),
72
+ });
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Advanced calculator implementation.
78
+ */
79
+ class AdvancedCalculator extends BaseCalculator implements CalculatorOperations {
80
+ private precision: number;
81
+
82
+ constructor(precision: number = 2) {
83
+ super();
84
+ this.precision = precision;
85
+ }
86
+
87
+ add(x: number, y: number): number {
88
+ const result = this.roundToPrecision(x + y);
89
+ this.recordOperation(OperationType.ADDITION, result);
90
+ return result;
91
+ }
92
+
93
+ subtract(x: number, y: number): number {
94
+ const result = this.roundToPrecision(x - y);
95
+ this.recordOperation(OperationType.SUBTRACTION, result);
96
+ return result;
97
+ }
98
+
99
+ multiply(x: number, y: number): number {
100
+ const result = this.roundToPrecision(x * y);
101
+ this.recordOperation(OperationType.MULTIPLICATION, result);
102
+ return result;
103
+ }
104
+
105
+ divide(x: number, y: number): number {
106
+ if (y === 0) {
107
+ throw new Error('Division by zero is not allowed');
108
+ }
109
+ const result = this.roundToPrecision(x / y);
110
+ this.recordOperation(OperationType.DIVISION, result);
111
+ return result;
112
+ }
113
+
114
+ calculate(operation: OperationType, x: number, y: number): number {
115
+ switch (operation) {
116
+ case OperationType.ADDITION:
117
+ return this.add(x, y);
118
+ case OperationType.SUBTRACTION:
119
+ return this.subtract(x, y);
120
+ case OperationType.MULTIPLICATION:
121
+ return this.multiply(x, y);
122
+ case OperationType.DIVISION:
123
+ return this.divide(x, y);
124
+ default:
125
+ throw new Error(`Unsupported operation: ${operation}`);
126
+ }
127
+ }
128
+
129
+ private roundToPrecision(value: number): number {
130
+ return Math.round(value * Math.pow(10, this.precision)) / Math.pow(10, this.precision);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Factory function for creating calculators.
136
+ */
137
+ function createCalculator<T extends BaseCalculator>(
138
+ CalculatorClass: new (...args: any[]) => T,
139
+ ...args: any[]
140
+ ): T {
141
+ return new CalculatorClass(...args);
142
+ }
143
+
144
+ /**
145
+ * Async function for fetching calculator settings.
146
+ */
147
+ async function fetchCalculatorSettings(): Promise<ApiResponse<{ precision: number }>> {
148
+ return new Promise((resolve) => {
149
+ setTimeout(() => {
150
+ resolve({
151
+ data: { precision: 4 },
152
+ success: true,
153
+ message: 'Settings loaded successfully',
154
+ });
155
+ }, 100);
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Generic utility function with constraints.
161
+ */
162
+ function processResults<T extends { value: number }>(results: T[]): T[] {
163
+ return results.filter(result => result.value !== 0);
164
+ }
165
+
166
+ // Export all types and classes
167
+ export {
168
+ CalculatorOperations,
169
+ CalculationResult,
170
+ ApiResponse,
171
+ OperationType,
172
+ BaseCalculator,
173
+ AdvancedCalculator,
174
+ createCalculator,
175
+ fetchCalculatorSettings,
176
+ processResults,
177
+ };
178
+ "#;
179
+
180
+ fs::write(&test_file, content)?;
181
+
182
+ let ctx = TestContext::new();
183
+ let output = ctx.run_probe(&[
184
+ "search",
185
+ "Calculator", // Search for Calculator-related symbols
186
+ test_file.to_str().unwrap(),
187
+ "--format",
188
+ "outline",
189
+ "--allow-tests",
190
+ "--exact",
191
+ ])?;
192
+
193
+ let output2 = ctx.run_probe(&[
194
+ "search",
195
+ "interface", // Search for interface declarations
196
+ test_file.to_str().unwrap(),
197
+ "--format",
198
+ "outline",
199
+ "--allow-tests",
200
+ "--exact",
201
+ ])?;
202
+
203
+ // Verify TypeScript symbols are extracted in outline format
204
+ assert!(
205
+ output.contains("Calculator") || output2.contains("CalculatorOperations"),
206
+ "Missing Calculator-related symbols - output: {} | output2: {}",
207
+ output,
208
+ output2
209
+ );
210
+
211
+ // Test interface search
212
+ assert!(
213
+ output2.contains("interface"),
214
+ "Missing interface declarations - output2: {}",
215
+ output2
216
+ );
217
+
218
+ Ok(())
219
+ }
220
+
221
+ #[test]
222
+ fn test_typescript_outline_generic_types() -> Result<()> {
223
+ let temp_dir = TempDir::new()?;
224
+ let test_file = temp_dir.path().join("generics.ts");
225
+
226
+ let content = r#"// Advanced TypeScript generics and conditional types
227
+
228
+ /**
229
+ * Generic repository interface with CRUD operations.
230
+ */
231
+ interface Repository<T, K = string> {
232
+ create(entity: Omit<T, 'id'>): Promise<T>;
233
+ findById(id: K): Promise<T | null>;
234
+ findAll(filter?: Partial<T>): Promise<T[]>;
235
+ update(id: K, updates: Partial<T>): Promise<T>;
236
+ delete(id: K): Promise<boolean>;
237
+ }
238
+
239
+ /**
240
+ * Conditional types for API responses.
241
+ */
242
+ type ApiResult<T, E = Error> = T extends string
243
+ ? { message: T; success: true }
244
+ : T extends Error
245
+ ? { error: T; success: false }
246
+ : { data: T; success: boolean; error?: E };
247
+
248
+ /**
249
+ * Mapped types for form validation.
250
+ */
251
+ type ValidationRules<T> = {
252
+ [K in keyof T]: {
253
+ required?: boolean;
254
+ minLength?: number;
255
+ maxLength?: number;
256
+ pattern?: RegExp;
257
+ custom?: (value: T[K]) => string | null;
258
+ };
259
+ };
260
+
261
+ /**
262
+ * Utility types for object manipulation.
263
+ */
264
+ type DeepPartial<T> = {
265
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
266
+ };
267
+
268
+ type DeepRequired<T> = {
269
+ [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
270
+ };
271
+
272
+ /**
273
+ * Generic service class with dependency injection.
274
+ */
275
+ abstract class BaseService<TEntity, TRepository extends Repository<TEntity>> {
276
+ protected repository: TRepository;
277
+
278
+ constructor(repository: TRepository) {
279
+ this.repository = repository;
280
+ }
281
+
282
+ async create(entityData: Omit<TEntity, 'id'>): Promise<ApiResult<TEntity>> {
283
+ try {
284
+ const entity = await this.repository.create(entityData);
285
+ return { data: entity, success: true };
286
+ } catch (error) {
287
+ return { data: {} as TEntity, success: false, error: error as Error };
288
+ }
289
+ }
290
+
291
+ abstract validate(entity: Partial<TEntity>): ValidationResult<TEntity>;
292
+ }
293
+
294
+ /**
295
+ * Validation result type.
296
+ */
297
+ interface ValidationResult<T> {
298
+ isValid: boolean;
299
+ errors: Partial<Record<keyof T, string>>;
300
+ }
301
+
302
+ /**
303
+ * Generic validator class.
304
+ */
305
+ class Validator<T extends Record<string, any>> {
306
+ private rules: ValidationRules<T>;
307
+
308
+ constructor(rules: ValidationRules<T>) {
309
+ this.rules = rules;
310
+ }
311
+
312
+ validate(data: Partial<T>): ValidationResult<T> {
313
+ const errors: Partial<Record<keyof T, string>> = {};
314
+
315
+ for (const [key, rule] of Object.entries(this.rules) as [keyof T, any][]) {
316
+ const value = data[key];
317
+
318
+ if (rule.required && (value === undefined || value === null || value === '')) {
319
+ errors[key] = `${String(key)} is required`;
320
+ continue;
321
+ }
322
+
323
+ if (value && typeof value === 'string') {
324
+ if (rule.minLength && value.length < rule.minLength) {
325
+ errors[key] = `${String(key)} must be at least ${rule.minLength} characters`;
326
+ }
327
+
328
+ if (rule.maxLength && value.length > rule.maxLength) {
329
+ errors[key] = `${String(key)} must not exceed ${rule.maxLength} characters`;
330
+ }
331
+
332
+ if (rule.pattern && !rule.pattern.test(value)) {
333
+ errors[key] = `${String(key)} format is invalid`;
334
+ }
335
+ }
336
+
337
+ if (rule.custom && value !== undefined) {
338
+ const customError = rule.custom(value);
339
+ if (customError) {
340
+ errors[key] = customError;
341
+ }
342
+ }
343
+ }
344
+
345
+ return {
346
+ isValid: Object.keys(errors).length === 0,
347
+ errors,
348
+ };
349
+ }
350
+ }
351
+
352
+ /**
353
+ * User entity type for demonstration.
354
+ */
355
+ interface User {
356
+ id: string;
357
+ username: string;
358
+ email: string;
359
+ firstName: string;
360
+ lastName: string;
361
+ age: number;
362
+ isActive: boolean;
363
+ createdAt: Date;
364
+ updatedAt: Date;
365
+ }
366
+
367
+ /**
368
+ * User service implementation.
369
+ */
370
+ class UserService extends BaseService<User, Repository<User>> {
371
+ private validator: Validator<User>;
372
+
373
+ constructor(repository: Repository<User>) {
374
+ super(repository);
375
+ this.validator = new Validator<User>({
376
+ username: {
377
+ required: true,
378
+ minLength: 3,
379
+ maxLength: 20,
380
+ pattern: /^[a-zA-Z0-9_]+$/,
381
+ },
382
+ email: {
383
+ required: true,
384
+ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
385
+ },
386
+ firstName: { required: true, minLength: 1 },
387
+ lastName: { required: true, minLength: 1 },
388
+ age: {
389
+ custom: (age: number) => {
390
+ if (age < 0 || age > 150) {
391
+ return 'Age must be between 0 and 150';
392
+ }
393
+ return null;
394
+ },
395
+ },
396
+ });
397
+ }
398
+
399
+ validate(entity: Partial<User>): ValidationResult<User> {
400
+ return this.validator.validate(entity);
401
+ }
402
+
403
+ async createUser(userData: Omit<User, 'id' | 'createdAt' | 'updatedAt'>): Promise<ApiResult<User>> {
404
+ const validation = this.validate(userData);
405
+
406
+ if (!validation.isValid) {
407
+ return {
408
+ data: {} as User,
409
+ success: false,
410
+ error: new Error(`Validation failed: ${JSON.stringify(validation.errors)}`),
411
+ };
412
+ }
413
+
414
+ const userWithTimestamps = {
415
+ ...userData,
416
+ createdAt: new Date(),
417
+ updatedAt: new Date(),
418
+ };
419
+
420
+ return this.create(userWithTimestamps);
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Generic factory function with complex constraints.
426
+ */
427
+ function createService<
428
+ TEntity extends { id: string },
429
+ TRepo extends Repository<TEntity>
430
+ >(
431
+ ServiceClass: new (repo: TRepo) => BaseService<TEntity, TRepo>,
432
+ repository: TRepo
433
+ ): BaseService<TEntity, TRepo> {
434
+ return new ServiceClass(repository);
435
+ }
436
+
437
+ export {
438
+ Repository,
439
+ ApiResult,
440
+ ValidationRules,
441
+ DeepPartial,
442
+ DeepRequired,
443
+ BaseService,
444
+ ValidationResult,
445
+ Validator,
446
+ User,
447
+ UserService,
448
+ createService,
449
+ };
450
+ "#;
451
+
452
+ fs::write(&test_file, content)?;
453
+
454
+ let ctx = TestContext::new();
455
+ let output = ctx.run_probe(&[
456
+ "search",
457
+ "Repository", // Search for Repository-related symbols
458
+ test_file.to_str().unwrap(),
459
+ "--format",
460
+ "outline",
461
+ "--allow-tests",
462
+ "--exact",
463
+ ])?;
464
+
465
+ let output2 = ctx.run_probe(&[
466
+ "search",
467
+ "type", // Search for type declarations
468
+ test_file.to_str().unwrap(),
469
+ "--format",
470
+ "outline",
471
+ "--allow-tests",
472
+ "--exact",
473
+ ])?;
474
+
475
+ // Verify complex TypeScript features
476
+ assert!(
477
+ output.contains("Repository") || output2.contains("Repository"),
478
+ "Missing Repository-related symbols - output: {} | output2: {}",
479
+ output,
480
+ output2
481
+ );
482
+
483
+ // Test type search
484
+ assert!(
485
+ output2.contains("type"),
486
+ "Missing type declarations - output2: {}",
487
+ output2
488
+ );
489
+
490
+ Ok(())
491
+ }
492
+
493
+ #[test]
494
+ fn test_typescript_outline_react_components() -> Result<()> {
495
+ let temp_dir = TempDir::new()?;
496
+ let test_file = temp_dir.path().join("react_components.tsx");
497
+
498
+ let content = r#"import React, { useState, useEffect, useCallback, useMemo, ReactNode } from 'react';
499
+
500
+ // TypeScript interfaces for props
501
+ interface BaseProps {
502
+ className?: string;
503
+ children?: ReactNode;
504
+ }
505
+
506
+ interface UserData {
507
+ id: string;
508
+ name: string;
509
+ email: string;
510
+ avatar?: string;
511
+ }
512
+
513
+ interface UserCardProps extends BaseProps {
514
+ user: UserData;
515
+ onEdit?: (user: UserData) => void;
516
+ onDelete?: (userId: string) => void;
517
+ }
518
+
519
+ interface UserListProps extends BaseProps {
520
+ users: UserData[];
521
+ loading?: boolean;
522
+ onUserAction?: (action: 'edit' | 'delete', user: UserData) => void;
523
+ }
524
+
525
+ // Custom hook with TypeScript
526
+ function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {
527
+ const [storedValue, setStoredValue] = useState<T>(() => {
528
+ try {
529
+ const item = window.localStorage.getItem(key);
530
+ return item ? JSON.parse(item) : initialValue;
531
+ } catch (error) {
532
+ console.error(`Error reading localStorage key "${key}":`, error);
533
+ return initialValue;
534
+ }
535
+ });
536
+
537
+ const setValue = useCallback((value: T) => {
538
+ try {
539
+ setStoredValue(value);
540
+ window.localStorage.setItem(key, JSON.stringify(value));
541
+ } catch (error) {
542
+ console.error(`Error setting localStorage key "${key}":`, error);
543
+ }
544
+ }, [key]);
545
+
546
+ return [storedValue, setValue];
547
+ }
548
+
549
+ // Generic component with constraints
550
+ interface ListProps<T> extends BaseProps {
551
+ items: T[];
552
+ renderItem: (item: T, index: number) => ReactNode;
553
+ keyExtractor: (item: T) => string;
554
+ emptyMessage?: string;
555
+ }
556
+
557
+ function List<T>({
558
+ items,
559
+ renderItem,
560
+ keyExtractor,
561
+ emptyMessage = "No items to display",
562
+ className,
563
+ children
564
+ }: ListProps<T>): JSX.Element {
565
+ if (items.length === 0) {
566
+ return (
567
+ <div className={`empty-list ${className || ''}`}>
568
+ {emptyMessage}
569
+ {children}
570
+ </div>
571
+ );
572
+ }
573
+
574
+ return (
575
+ <div className={`list ${className || ''}`}>
576
+ {items.map((item, index) => (
577
+ <div key={keyExtractor(item)} className="list-item">
578
+ {renderItem(item, index)}
579
+ </div>
580
+ ))}
581
+ {children}
582
+ </div>
583
+ );
584
+ }
585
+
586
+ // Functional component with complex state management
587
+ const UserCard: React.FC<UserCardProps> = ({
588
+ user,
589
+ onEdit,
590
+ onDelete,
591
+ className,
592
+ children
593
+ }) => {
594
+ const [isHovered, setIsHovered] = useState(false);
595
+ const [isExpanded, setIsExpanded] = useState(false);
596
+
597
+ const handleEdit = useCallback(() => {
598
+ onEdit?.(user);
599
+ }, [user, onEdit]);
600
+
601
+ const handleDelete = useCallback(() => {
602
+ if (window.confirm(`Are you sure you want to delete ${user.name}?`)) {
603
+ onDelete?.(user.id);
604
+ }
605
+ }, [user, onDelete]);
606
+
607
+ const displayName = useMemo(() => {
608
+ return user.name.length > 20
609
+ ? `${user.name.substring(0, 20)}...`
610
+ : user.name;
611
+ }, [user.name]);
612
+
613
+ return (
614
+ <div
615
+ className={`user-card ${className || ''} ${isHovered ? 'hovered' : ''}`}
616
+ onMouseEnter={() => setIsHovered(true)}
617
+ onMouseLeave={() => setIsHovered(false)}
618
+ >
619
+ <div className="user-header">
620
+ {user.avatar && (
621
+ <img
622
+ src={user.avatar}
623
+ alt={`${user.name}'s avatar`}
624
+ className="user-avatar"
625
+ />
626
+ )}
627
+ <div className="user-info">
628
+ <h3 className="user-name" title={user.name}>
629
+ {displayName}
630
+ </h3>
631
+ <p className="user-email">{user.email}</p>
632
+ </div>
633
+ </div>
634
+
635
+ {isExpanded && (
636
+ <div className="user-details">
637
+ <p>ID: {user.id}</p>
638
+ {children}
639
+ </div>
640
+ )}
641
+
642
+ <div className="user-actions">
643
+ <button
644
+ onClick={() => setIsExpanded(!isExpanded)}
645
+ className="expand-button"
646
+ >
647
+ {isExpanded ? 'Collapse' : 'Expand'}
648
+ </button>
649
+
650
+ {onEdit && (
651
+ <button onClick={handleEdit} className="edit-button">
652
+ Edit
653
+ </button>
654
+ )}
655
+
656
+ {onDelete && (
657
+ <button onClick={handleDelete} className="delete-button">
658
+ Delete
659
+ </button>
660
+ )}
661
+ </div>
662
+ </div>
663
+ );
664
+ };
665
+
666
+ // Component with complex effects and error handling
667
+ const UserList: React.FC<UserListProps> = ({
668
+ users,
669
+ loading = false,
670
+ onUserAction,
671
+ className,
672
+ children
673
+ }) => {
674
+ const [filteredUsers, setFilteredUsers] = useState<UserData[]>([]);
675
+ const [searchTerm, setSearchTerm] = useLocalStorage<string>('userSearch', '');
676
+ const [error, setError] = useState<string | null>(null);
677
+
678
+ // Effect for filtering users
679
+ useEffect(() => {
680
+ try {
681
+ if (!searchTerm.trim()) {
682
+ setFilteredUsers(users);
683
+ } else {
684
+ const filtered = users.filter(user =>
685
+ user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
686
+ user.email.toLowerCase().includes(searchTerm.toLowerCase())
687
+ );
688
+ setFilteredUsers(filtered);
689
+ }
690
+ setError(null);
691
+ } catch (err) {
692
+ setError('Failed to filter users');
693
+ console.error('Filter error:', err);
694
+ }
695
+ }, [users, searchTerm]);
696
+
697
+ const handleUserAction = useCallback((action: 'edit' | 'delete', user: UserData) => {
698
+ try {
699
+ onUserAction?.(action, user);
700
+ } catch (err) {
701
+ setError(`Failed to ${action} user`);
702
+ console.error(`${action} error:`, err);
703
+ }
704
+ }, [onUserAction]);
705
+
706
+ if (loading) {
707
+ return (
708
+ <div className={`user-list loading ${className || ''}`}>
709
+ <div className="loading-spinner">Loading users...</div>
710
+ {children}
711
+ </div>
712
+ );
713
+ }
714
+
715
+ if (error) {
716
+ return (
717
+ <div className={`user-list error ${className || ''}`}>
718
+ <div className="error-message">Error: {error}</div>
719
+ <button onClick={() => setError(null)}>Dismiss</button>
720
+ {children}
721
+ </div>
722
+ );
723
+ }
724
+
725
+ return (
726
+ <div className={`user-list ${className || ''}`}>
727
+ <div className="search-container">
728
+ <input
729
+ type="text"
730
+ value={searchTerm}
731
+ onChange={(e) => setSearchTerm(e.target.value)}
732
+ placeholder="Search users..."
733
+ className="search-input"
734
+ />
735
+ </div>
736
+
737
+ <List
738
+ items={filteredUsers}
739
+ keyExtractor={(user) => user.id}
740
+ renderItem={(user) => (
741
+ <UserCard
742
+ user={user}
743
+ onEdit={(user) => handleUserAction('edit', user)}
744
+ onDelete={(userId) => {
745
+ const user = filteredUsers.find(u => u.id === userId);
746
+ if (user) handleUserAction('delete', user);
747
+ }}
748
+ />
749
+ )}
750
+ emptyMessage="No users found"
751
+ />
752
+
753
+ {children}
754
+ </div>
755
+ );
756
+ };
757
+
758
+ // Higher-order component with TypeScript
759
+ function withErrorBoundary<P extends object>(
760
+ WrappedComponent: React.ComponentType<P>
761
+ ): React.FC<P> {
762
+ return function WithErrorBoundaryComponent(props: P) {
763
+ const [hasError, setHasError] = useState(false);
764
+ const [error, setError] = useState<Error | null>(null);
765
+
766
+ useEffect(() => {
767
+ const handleError = (error: ErrorEvent) => {
768
+ setHasError(true);
769
+ setError(new Error(error.message));
770
+ };
771
+
772
+ window.addEventListener('error', handleError);
773
+ return () => window.removeEventListener('error', handleError);
774
+ }, []);
775
+
776
+ if (hasError) {
777
+ return (
778
+ <div className="error-boundary">
779
+ <h2>Something went wrong</h2>
780
+ <p>{error?.message}</p>
781
+ <button onClick={() => {
782
+ setHasError(false);
783
+ setError(null);
784
+ }}>
785
+ Try again
786
+ </button>
787
+ </div>
788
+ );
789
+ }
790
+
791
+ return <WrappedComponent {...props} />;
792
+ };
793
+ }
794
+
795
+ export {
796
+ UserData,
797
+ UserCardProps,
798
+ UserListProps,
799
+ useLocalStorage,
800
+ List,
801
+ UserCard,
802
+ UserList,
803
+ withErrorBoundary,
804
+ };
805
+
806
+ export default UserList;
807
+ "#;
808
+
809
+ fs::write(&test_file, content)?;
810
+
811
+ let ctx = TestContext::new();
812
+ let output = ctx.run_probe(&[
813
+ "search",
814
+ "React", // Search for React-related symbols
815
+ test_file.to_str().unwrap(),
816
+ "--format",
817
+ "outline",
818
+ "--allow-tests",
819
+ "--exact",
820
+ ])?;
821
+
822
+ let output2 = ctx.run_probe(&[
823
+ "search",
824
+ "function", // Search for function declarations
825
+ test_file.to_str().unwrap(),
826
+ "--format",
827
+ "outline",
828
+ "--allow-tests",
829
+ "--exact",
830
+ ])?;
831
+
832
+ // Verify React component structures with TypeScript
833
+ // For now, just verify we get some results - React content might not be strongly matched
834
+ assert!(
835
+ !output.is_empty() || !output2.is_empty(),
836
+ "Should find some content in React components - output: {} | output2: {}",
837
+ output,
838
+ output2
839
+ );
840
+
841
+ // Test function search
842
+ assert!(
843
+ output2.contains("function"),
844
+ "Missing function declarations - output2: {}",
845
+ output2
846
+ );
847
+
848
+ Ok(())
849
+ }
850
+
851
+ #[test]
852
+ fn test_typescript_outline_test_patterns() -> Result<()> {
853
+ let temp_dir = TempDir::new()?;
854
+ let test_file = temp_dir.path().join("test_patterns.ts");
855
+
856
+ let content = r#"// TypeScript test patterns with Jest and type safety
857
+
858
+ import { Calculator } from './calculator';
859
+
860
+ // Test types and interfaces
861
+ interface TestUser {
862
+ id: string;
863
+ name: string;
864
+ email: string;
865
+ }
866
+
867
+ type MockedFunction<T extends (...args: any[]) => any> = jest.MockedFunction<T>;
868
+
869
+ describe('Calculator', () => {
870
+ let calculator: Calculator;
871
+
872
+ beforeEach(() => {
873
+ calculator = new Calculator('Test Calculator');
874
+ });
875
+
876
+ afterEach(() => {
877
+ calculator = null as any;
878
+ });
879
+
880
+ describe('type safety', () => {
881
+ test('should handle number types correctly', () => {
882
+ const result: number = calculator.add(2, 3);
883
+ expect(result).toBe(5);
884
+ expect(typeof result).toBe('number');
885
+ });
886
+
887
+ test('should enforce parameter types', () => {
888
+ // TypeScript would catch these errors at compile time
889
+ expect(() => {
890
+ // @ts-ignore - Testing runtime behavior
891
+ calculator.add('2' as any, '3' as any);
892
+ }).toThrow('Invalid input type');
893
+ });
894
+ });
895
+
896
+ describe('generic methods', () => {
897
+ test('should work with generic operations', () => {
898
+ const operations = calculator.getOperations<'add' | 'subtract'>();
899
+ expect(operations).toContain('add');
900
+ expect(operations).toContain('subtract');
901
+ });
902
+ });
903
+ });
904
+
905
+ // Mock with proper TypeScript types
906
+ const mockFetchUser = jest.fn() as MockedFunction<
907
+ (id: string) => Promise<TestUser>
908
+ >;
909
+
910
+ describe('async operations with types', () => {
911
+ beforeEach(() => {
912
+ mockFetchUser.mockClear();
913
+ });
914
+
915
+ test('should fetch user data with proper types', async () => {
916
+ const mockUser: TestUser = {
917
+ id: '123',
918
+ name: 'John Doe',
919
+ email: 'john@example.com',
920
+ };
921
+
922
+ mockFetchUser.mockResolvedValue(mockUser);
923
+
924
+ const result = await mockFetchUser('123');
925
+
926
+ expect(result).toEqual(mockUser);
927
+ expect(result.id).toBe('123');
928
+ expect(typeof result.name).toBe('string');
929
+
930
+ // TypeScript ensures these properties exist
931
+ expect(result.id).toBeDefined();
932
+ expect(result.name).toBeDefined();
933
+ expect(result.email).toBeDefined();
934
+ });
935
+
936
+ test('should handle async errors with proper types', async () => {
937
+ const mockError = new Error('Network error');
938
+ mockFetchUser.mockRejectedValue(mockError);
939
+
940
+ await expect(mockFetchUser('123')).rejects.toThrow('Network error');
941
+ await expect(mockFetchUser('123')).rejects.toBeInstanceOf(Error);
942
+ });
943
+ });
944
+
945
+ // Generic test helper functions
946
+ function createMockUser(overrides: Partial<TestUser> = {}): TestUser {
947
+ return {
948
+ id: '123',
949
+ name: 'Test User',
950
+ email: 'test@example.com',
951
+ ...overrides,
952
+ };
953
+ }
954
+
955
+ function expectTypeOf<T>(value: T): jest.Matchers<T> {
956
+ return expect(value);
957
+ }
958
+
959
+ // Test class with TypeScript features
960
+ class TestHelpers {
961
+ static createCalculator(precision: number = 2): Calculator {
962
+ return new Calculator('Test', precision);
963
+ }
964
+
965
+ static async waitFor<T>(
966
+ condition: () => T | Promise<T>,
967
+ timeout: number = 5000
968
+ ): Promise<T> {
969
+ const startTime = Date.now();
970
+
971
+ while (Date.now() - startTime < timeout) {
972
+ try {
973
+ const result = await condition();
974
+ if (result) {
975
+ return result;
976
+ }
977
+ } catch (error) {
978
+ // Continue waiting
979
+ }
980
+
981
+ await new Promise(resolve => setTimeout(resolve, 100));
982
+ }
983
+
984
+ throw new Error(`Condition not met within ${timeout}ms`);
985
+ }
986
+
987
+ static createPartialMock<T>(partial: Partial<T>): T {
988
+ return partial as T;
989
+ }
990
+ }
991
+
992
+ // Parameterized tests with types
993
+ interface CalculationTestCase {
994
+ a: number;
995
+ b: number;
996
+ expected: number;
997
+ operation: 'add' | 'subtract' | 'multiply' | 'divide';
998
+ }
999
+
1000
+ const calculationTestCases: CalculationTestCase[] = [
1001
+ { a: 2, b: 3, expected: 5, operation: 'add' },
1002
+ { a: 5, b: 2, expected: 3, operation: 'subtract' },
1003
+ { a: 3, b: 4, expected: 12, operation: 'multiply' },
1004
+ { a: 8, b: 2, expected: 4, operation: 'divide' },
1005
+ ];
1006
+
1007
+ describe.each(calculationTestCases)(
1008
+ 'calculator operations',
1009
+ ({ a, b, expected, operation }) => {
1010
+ test(`${operation}(${a}, ${b}) should equal ${expected}`, () => {
1011
+ const calculator = TestHelpers.createCalculator();
1012
+ const result = calculator[operation](a, b);
1013
+ expect(result).toBe(expected);
1014
+ });
1015
+ }
1016
+ );
1017
+
1018
+ // Mock classes with TypeScript
1019
+ class MockUserService {
1020
+ private users: Map<string, TestUser> = new Map();
1021
+
1022
+ async getUser(id: string): Promise<TestUser | null> {
1023
+ return this.users.get(id) || null;
1024
+ }
1025
+
1026
+ async createUser(userData: Omit<TestUser, 'id'>): Promise<TestUser> {
1027
+ const user: TestUser = {
1028
+ ...userData,
1029
+ id: Math.random().toString(36).substr(2, 9),
1030
+ };
1031
+
1032
+ this.users.set(user.id, user);
1033
+ return user;
1034
+ }
1035
+
1036
+ async updateUser(id: string, updates: Partial<TestUser>): Promise<TestUser> {
1037
+ const existing = this.users.get(id);
1038
+ if (!existing) {
1039
+ throw new Error('User not found');
1040
+ }
1041
+
1042
+ const updated = { ...existing, ...updates };
1043
+ this.users.set(id, updated);
1044
+ return updated;
1045
+ }
1046
+
1047
+ clear(): void {
1048
+ this.users.clear();
1049
+ }
1050
+ }
1051
+
1052
+ describe('UserService integration tests', () => {
1053
+ let userService: MockUserService;
1054
+
1055
+ beforeEach(() => {
1056
+ userService = new MockUserService();
1057
+ });
1058
+
1059
+ afterEach(() => {
1060
+ userService.clear();
1061
+ });
1062
+
1063
+ test('should create and retrieve users', async () => {
1064
+ const userData = { name: 'Alice', email: 'alice@example.com' };
1065
+ const createdUser = await userService.createUser(userData);
1066
+
1067
+ expect(createdUser.id).toBeDefined();
1068
+ expect(createdUser.name).toBe(userData.name);
1069
+ expect(createdUser.email).toBe(userData.email);
1070
+
1071
+ const retrievedUser = await userService.getUser(createdUser.id);
1072
+ expect(retrievedUser).toEqual(createdUser);
1073
+ });
1074
+
1075
+ test('should update user data', async () => {
1076
+ const user = await userService.createUser({
1077
+ name: 'Bob',
1078
+ email: 'bob@example.com',
1079
+ });
1080
+
1081
+ const updates = { name: 'Robert' };
1082
+ const updatedUser = await userService.updateUser(user.id, updates);
1083
+
1084
+ expect(updatedUser.name).toBe('Robert');
1085
+ expect(updatedUser.email).toBe('bob@example.com');
1086
+ expect(updatedUser.id).toBe(user.id);
1087
+ });
1088
+ });
1089
+
1090
+ export {
1091
+ TestUser,
1092
+ MockedFunction,
1093
+ createMockUser,
1094
+ expectTypeOf,
1095
+ TestHelpers,
1096
+ CalculationTestCase,
1097
+ MockUserService,
1098
+ };
1099
+ "#;
1100
+
1101
+ fs::write(&test_file, content)?;
1102
+
1103
+ let ctx = TestContext::new();
1104
+ let output = ctx.run_probe(&[
1105
+ "search",
1106
+ "test", // Search for test-related symbols
1107
+ test_file.to_str().unwrap(),
1108
+ "--format",
1109
+ "outline",
1110
+ "--allow-tests",
1111
+ "--exact",
1112
+ ])?;
1113
+
1114
+ let output2 = ctx.run_probe(&[
1115
+ "search",
1116
+ "function", // Search for function declarations
1117
+ test_file.to_str().unwrap(),
1118
+ "--format",
1119
+ "outline",
1120
+ "--allow-tests",
1121
+ "--exact",
1122
+ ])?;
1123
+
1124
+ // Verify TypeScript test patterns - should find describe blocks or test functions
1125
+ assert!(
1126
+ output.contains("test") || output.contains("describe"),
1127
+ "Should find test-related symbols - output: {}",
1128
+ output
1129
+ );
1130
+
1131
+ // Test function search
1132
+ assert!(
1133
+ output2.contains("function"),
1134
+ "Missing function declarations - output2: {}",
1135
+ output2
1136
+ );
1137
+
1138
+ Ok(())
1139
+ }
1140
+
1141
+ #[test]
1142
+ fn test_typescript_outline_large_function_closing_braces() -> Result<()> {
1143
+ let temp_dir = TempDir::new()?;
1144
+ let test_file = temp_dir.path().join("large_function.ts");
1145
+
1146
+ let content = r#"/**
1147
+ * Large TypeScript function with complex types and nested blocks.
1148
+ */
1149
+ function complexDataProcessor<T extends { value: number; category?: string }>(
1150
+ data: T[],
1151
+ options: {
1152
+ threshold: number;
1153
+ categorizer?: (item: T) => string;
1154
+ validator?: (item: T) => boolean;
1155
+ }
1156
+ ): { processed: T[]; summary: Record<string, number> } {
1157
+ const results: T[] = [];
1158
+ const categories = new Map<string, T[]>();
1159
+ const summary: Record<string, number> = {};
1160
+
1161
+ // Phase 1: Validation and categorization
1162
+ for (const [index, item] of data.entries()) {
1163
+ // Validation phase
1164
+ if (options.validator) {
1165
+ if (!options.validator(item)) {
1166
+ console.warn(`Item at index ${index} failed validation:`, item);
1167
+ continue;
1168
+ }
1169
+ }
1170
+
1171
+ // Default validation
1172
+ if (typeof item.value !== 'number' || isNaN(item.value)) {
1173
+ console.warn(`Item at index ${index} has invalid value:`, item);
1174
+ continue;
1175
+ }
1176
+
1177
+ // Categorization
1178
+ let category: string;
1179
+ if (options.categorizer) {
1180
+ category = options.categorizer(item);
1181
+ } else {
1182
+ if (item.value < 0) {
1183
+ category = 'negative';
1184
+ } else if (item.value === 0) {
1185
+ category = 'zero';
1186
+ } else if (item.value < options.threshold) {
1187
+ category = 'below_threshold';
1188
+ } else {
1189
+ category = 'above_threshold';
1190
+ }
1191
+ }
1192
+
1193
+ // Store in category map
1194
+ if (!categories.has(category)) {
1195
+ categories.set(category, []);
1196
+ }
1197
+ categories.get(category)!.push(item);
1198
+ }
1199
+
1200
+ // Phase 2: Processing each category
1201
+ for (const [categoryName, items] of categories) {
1202
+ summary[categoryName] = items.length;
1203
+
1204
+ switch (categoryName) {
1205
+ case 'negative':
1206
+ for (const item of items) {
1207
+ const processedItem: T = {
1208
+ ...item,
1209
+ value: Math.abs(item.value),
1210
+ category: 'processed_negative',
1211
+ } as T;
1212
+ results.push(processedItem);
1213
+ }
1214
+ break;
1215
+
1216
+ case 'zero':
1217
+ for (const item of items) {
1218
+ const processedItem: T = {
1219
+ ...item,
1220
+ value: 0.1, // Convert zero to small positive
1221
+ category: 'processed_zero',
1222
+ } as T;
1223
+ results.push(processedItem);
1224
+ }
1225
+ break;
1226
+
1227
+ case 'below_threshold':
1228
+ for (const item of items) {
1229
+ const multiplier = options.threshold / item.value;
1230
+ const processedItem: T = {
1231
+ ...item,
1232
+ value: item.value * multiplier,
1233
+ category: 'normalized',
1234
+ } as T;
1235
+ results.push(processedItem);
1236
+ }
1237
+ break;
1238
+
1239
+ case 'above_threshold':
1240
+ for (const item of items) {
1241
+ const processedItem: T = {
1242
+ ...item,
1243
+ category: 'above_threshold',
1244
+ } as T;
1245
+ results.push(processedItem);
1246
+ }
1247
+ break;
1248
+
1249
+ default:
1250
+ // Custom category processing
1251
+ for (const item of items) {
1252
+ const processedItem: T = {
1253
+ ...item,
1254
+ category: `custom_${categoryName}`,
1255
+ } as T;
1256
+ results.push(processedItem);
1257
+ }
1258
+ break;
1259
+ }
1260
+ }
1261
+
1262
+ // Phase 3: Final sorting and validation
1263
+ results.sort((a, b) => {
1264
+ if (a.category !== b.category) {
1265
+ return a.category!.localeCompare(b.category!);
1266
+ }
1267
+ return a.value - b.value;
1268
+ });
1269
+
1270
+ // Final validation pass
1271
+ const validatedResults: T[] = [];
1272
+ for (const result of results) {
1273
+ if (result.value > 0 && result.category) {
1274
+ validatedResults.push(result);
1275
+ }
1276
+ }
1277
+
1278
+ return {
1279
+ processed: validatedResults,
1280
+ summary,
1281
+ };
1282
+ }
1283
+ "#;
1284
+
1285
+ fs::write(&test_file, content)?;
1286
+
1287
+ let ctx = TestContext::new();
1288
+ let output = ctx.run_probe(&[
1289
+ "search",
1290
+ "complexDataProcessor", // Search for the specific large function
1291
+ test_file.to_str().unwrap(),
1292
+ "--format",
1293
+ "outline",
1294
+ "--allow-tests",
1295
+ "--exact",
1296
+ ])?;
1297
+
1298
+ // Verify large function is shown with closing braces
1299
+ assert!(
1300
+ output.contains("complexDataProcessor"),
1301
+ "Missing complexDataProcessor function - output: {}",
1302
+ output
1303
+ );
1304
+
1305
+ // Should have closing braces for large blocks (if the function is found and formatted)
1306
+ if output.contains("complexDataProcessor") {
1307
+ let closing_braces_count = output.matches("}").count();
1308
+ assert!(
1309
+ closing_braces_count >= 1,
1310
+ "Should have closing braces for nested blocks - output: {}",
1311
+ output
1312
+ );
1313
+ }
1314
+
1315
+ Ok(())
1316
+ }
1317
+
1318
+ #[test]
1319
+ fn test_typescript_outline_search_command() -> Result<()> {
1320
+ let temp_dir = TempDir::new()?;
1321
+ let test_file = temp_dir.path().join("search_test.ts");
1322
+
1323
+ let content = r#"interface DataProcessor {
1324
+ processData<T>(data: T[]): T[];
1325
+ getProcessedCount(): number;
1326
+ }
1327
+
1328
+ class AdvancedDataProcessor implements DataProcessor {
1329
+ private processedCount: number = 0;
1330
+
1331
+ processData<T>(data: T[]): T[] {
1332
+ this.processedCount++;
1333
+ return data.filter(item => item !== null && item !== undefined);
1334
+ }
1335
+
1336
+ getProcessedCount(): number {
1337
+ return this.processedCount;
1338
+ }
1339
+ }
1340
+
1341
+ function processFile(filename: string): Promise<string> {
1342
+ return Promise.resolve(`Processed ${filename}`);
1343
+ }
1344
+
1345
+ async function processAsync<T>(data: T): Promise<{ processed: boolean } & T> {
1346
+ return { processed: true, ...data };
1347
+ }
1348
+
1349
+ function testDataProcessing(): void {
1350
+ const processor: DataProcessor = new AdvancedDataProcessor();
1351
+ const result = processor.processData([1, 2, null, 3]);
1352
+ console.assert(result.length === 3);
1353
+ }
1354
+ "#;
1355
+
1356
+ fs::write(&test_file, content)?;
1357
+
1358
+ let ctx = TestContext::new();
1359
+ let output = ctx.run_probe(&[
1360
+ "search",
1361
+ "process",
1362
+ temp_dir.path().to_str().unwrap(),
1363
+ "--format",
1364
+ "outline",
1365
+ "--allow-tests",
1366
+ ])?;
1367
+
1368
+ // Should find symbols containing "process"
1369
+ assert!(
1370
+ output.contains("DataProcessor")
1371
+ || output.contains("processData")
1372
+ || output.contains("processFile")
1373
+ || output.contains("processAsync"),
1374
+ "Should find process-related symbols - output: {}",
1375
+ output
1376
+ );
1377
+
1378
+ Ok(())
1379
+ }
1380
+
1381
+ #[test]
1382
+ fn test_typescript_outline_small_function_no_closing_braces() -> Result<()> {
1383
+ let temp_dir = TempDir::new()?;
1384
+ let test_file = temp_dir.path().join("small_functions.ts");
1385
+
1386
+ let content = r#"/**
1387
+ * Small TypeScript functions that should NOT get closing braces.
1388
+ */
1389
+ interface Point {
1390
+ x: number;
1391
+ y: number;
1392
+ }
1393
+
1394
+ type Vector = {
1395
+ start: Point;
1396
+ end: Point;
1397
+ };
1398
+
1399
+ function add(a: number, b: number): number {
1400
+ return a + b;
1401
+ }
1402
+
1403
+ function multiply(x: number, y: number): number {
1404
+ return x * y;
1405
+ }
1406
+
1407
+ const subtract = (a: number, b: number): number => {
1408
+ return a - b;
1409
+ };
1410
+
1411
+ const divide: (x: number, y: number) => number = (x, y) => {
1412
+ if (y === 0) throw new Error('Division by zero');
1413
+ return x / y;
1414
+ };
1415
+
1416
+ function getDistance(p1: Point, p2: Point): number {
1417
+ const dx = p2.x - p1.x;
1418
+ const dy = p2.y - p1.y;
1419
+ return Math.sqrt(dx * dx + dy * dy);
1420
+ }
1421
+
1422
+ async function fetchData<T>(url: string): Promise<T> {
1423
+ const response = await fetch(url);
1424
+ return response.json();
1425
+ }
1426
+
1427
+ class SmallClass {
1428
+ private value: number;
1429
+
1430
+ constructor(value: number) {
1431
+ this.value = value;
1432
+ }
1433
+
1434
+ getValue(): number {
1435
+ return this.value;
1436
+ }
1437
+ }
1438
+ "#;
1439
+
1440
+ fs::write(&test_file, content)?;
1441
+
1442
+ let ctx = TestContext::new();
1443
+ let output = ctx.run_probe(&[
1444
+ "search",
1445
+ "function", // Search for function declarations
1446
+ test_file.to_str().unwrap(),
1447
+ "--format",
1448
+ "outline",
1449
+ "--allow-tests",
1450
+ "--exact",
1451
+ ])?;
1452
+
1453
+ // Verify small functions are found but don't have excessive closing braces
1454
+ assert!(
1455
+ output.contains("function") || output.contains("add") || output.contains("multiply"),
1456
+ "Should find small functions - output: {}",
1457
+ output
1458
+ );
1459
+
1460
+ // Small functions should have minimal closing braces (ideally none for outline)
1461
+ let closing_braces_count = output.matches("} //").count(); // closing brace comments
1462
+ assert!(
1463
+ closing_braces_count <= 2, // Allow some, but not excessive
1464
+ "Small functions should not have many closing brace comments - output: {}",
1465
+ output
1466
+ );
1467
+
1468
+ Ok(())
1469
+ }
1470
+
1471
+ #[test]
1472
+ fn test_typescript_outline_keyword_highlighting() -> Result<()> {
1473
+ let temp_dir = TempDir::new()?;
1474
+ let test_file = temp_dir.path().join("keyword_highlighting.ts");
1475
+
1476
+ let content = r#"// TypeScript with various keywords for highlighting tests
1477
+ interface DatabaseConfig {
1478
+ host: string;
1479
+ port: number;
1480
+ database: string;
1481
+ username: string;
1482
+ password: string;
1483
+ }
1484
+
1485
+ type ConnectionStatus = 'connected' | 'disconnected' | 'connecting' | 'error';
1486
+
1487
+ enum LogLevel {
1488
+ DEBUG = 'debug',
1489
+ INFO = 'info',
1490
+ WARN = 'warn',
1491
+ ERROR = 'error',
1492
+ }
1493
+
1494
+ abstract class BaseLogger {
1495
+ protected level: LogLevel;
1496
+
1497
+ constructor(level: LogLevel = LogLevel.INFO) {
1498
+ this.level = level;
1499
+ }
1500
+
1501
+ abstract log(message: string, level: LogLevel): void;
1502
+ }
1503
+
1504
+ class ConsoleLogger extends BaseLogger {
1505
+ log(message: string, level: LogLevel): void {
1506
+ if (this.shouldLog(level)) {
1507
+ console.log(`[${level}] ${message}`);
1508
+ }
1509
+ }
1510
+
1511
+ private shouldLog(level: LogLevel): boolean {
1512
+ const levels = Object.values(LogLevel);
1513
+ return levels.indexOf(level) >= levels.indexOf(this.level);
1514
+ }
1515
+ }
1516
+
1517
+ function createLogger(config: DatabaseConfig): BaseLogger {
1518
+ return new ConsoleLogger();
1519
+ }
1520
+
1521
+ async function connectDatabase(config: DatabaseConfig): Promise<ConnectionStatus> {
1522
+ try {
1523
+ // Simulate connection logic
1524
+ await new Promise(resolve => setTimeout(resolve, 100));
1525
+ return 'connected';
1526
+ } catch (error) {
1527
+ return 'error';
1528
+ }
1529
+ }
1530
+
1531
+ // Generic function with constraints
1532
+ function processData<T extends { id: string }>(data: T[]): T[] {
1533
+ return data.filter(item => item.id && item.id.length > 0);
1534
+ }
1535
+ "#;
1536
+
1537
+ fs::write(&test_file, content)?;
1538
+
1539
+ let ctx = TestContext::new();
1540
+
1541
+ // Test various TypeScript keywords
1542
+ for keyword in [
1543
+ "interface",
1544
+ "type",
1545
+ "enum",
1546
+ "abstract",
1547
+ "extends",
1548
+ "async",
1549
+ "function",
1550
+ ] {
1551
+ let output = ctx.run_probe(&[
1552
+ "search",
1553
+ keyword,
1554
+ test_file.to_str().unwrap(),
1555
+ "--format",
1556
+ "outline",
1557
+ "--allow-tests",
1558
+ ])?;
1559
+
1560
+ // Should find the keyword in outline format
1561
+ assert!(
1562
+ output.contains(keyword) || !output.is_empty(),
1563
+ "Should find keyword '{}' - output: {}",
1564
+ keyword,
1565
+ output
1566
+ );
1567
+ }
1568
+
1569
+ Ok(())
1570
+ }
1571
+
1572
+ #[test]
1573
+ fn test_typescript_outline_array_object_truncation() -> Result<()> {
1574
+ let temp_dir = TempDir::new()?;
1575
+ let test_file = temp_dir.path().join("array_object_truncation.ts");
1576
+
1577
+ let content = r#"// TypeScript with large arrays and objects for truncation testing
1578
+ interface UserProfile {
1579
+ id: string;
1580
+ name: string;
1581
+ email: string;
1582
+ preferences: {
1583
+ theme: 'light' | 'dark';
1584
+ language: string;
1585
+ notifications: boolean;
1586
+ keywords: string[];
1587
+ };
1588
+ }
1589
+
1590
+ const largeArray: string[] = [
1591
+ 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth',
1592
+ 'ninth', 'tenth', 'eleventh', 'twelfth', 'thirteenth', 'fourteenth', 'fifteenth',
1593
+ 'sixteenth', 'seventeenth', 'eighteenth', 'nineteenth', 'twentieth', 'keyword',
1594
+ 'twenty-first', 'twenty-second', 'twenty-third', 'twenty-fourth', 'twenty-fifth',
1595
+ ];
1596
+
1597
+ const complexObject: UserProfile = {
1598
+ id: '12345',
1599
+ name: 'John Doe',
1600
+ email: 'john@example.com',
1601
+ preferences: {
1602
+ theme: 'dark',
1603
+ language: 'en',
1604
+ notifications: true,
1605
+ keywords: ['typescript', 'javascript', 'react', 'node', 'keyword', 'development'],
1606
+ },
1607
+ };
1608
+
1609
+ type LargeTypeDefinition = {
1610
+ field1: string;
1611
+ field2: number;
1612
+ field3: boolean;
1613
+ field4: string[];
1614
+ field5: {
1615
+ nested1: string;
1616
+ nested2: number;
1617
+ nested3: boolean;
1618
+ nested4: string;
1619
+ nested5: number;
1620
+ keyword: string;
1621
+ };
1622
+ field6: Date;
1623
+ field7: RegExp;
1624
+ field8: Function;
1625
+ field9: any;
1626
+ field10: unknown;
1627
+ };
1628
+
1629
+ function processLargeData(data: LargeTypeDefinition[]): LargeTypeDefinition[] {
1630
+ return data.filter(item =>
1631
+ item.field1 &&
1632
+ item.field2 > 0 &&
1633
+ item.field3 !== null &&
1634
+ item.field4.length > 0 &&
1635
+ item.field5.keyword.includes('keyword')
1636
+ );
1637
+ }
1638
+ "#;
1639
+
1640
+ fs::write(&test_file, content)?;
1641
+
1642
+ let ctx = TestContext::new();
1643
+ let output = ctx.run_probe(&[
1644
+ "search",
1645
+ "keyword", // Search for keyword that should be preserved even in truncated arrays/objects
1646
+ test_file.to_str().unwrap(),
1647
+ "--format",
1648
+ "outline",
1649
+ "--allow-tests",
1650
+ "--exact",
1651
+ ])?;
1652
+
1653
+ // Should find the keyword even in truncated content
1654
+ assert!(
1655
+ output.contains("keyword"),
1656
+ "Should preserve keyword in truncated content - output: {}",
1657
+ output
1658
+ );
1659
+
1660
+ let output2 = ctx.run_probe(&[
1661
+ "search",
1662
+ "largeArray",
1663
+ test_file.to_str().unwrap(),
1664
+ "--format",
1665
+ "outline",
1666
+ "--allow-tests",
1667
+ "--exact",
1668
+ ])?;
1669
+
1670
+ // Should contain array/object content, possibly with truncation
1671
+ assert!(
1672
+ output2.contains("largeArray") || output2.contains("string[]"),
1673
+ "Should find large array definition - output2: {}",
1674
+ output2
1675
+ );
1676
+
1677
+ Ok(())
1678
+ }
1679
+
1680
+ #[test]
1681
+ fn test_typescript_outline_advanced_features() -> Result<()> {
1682
+ let temp_dir = TempDir::new()?;
1683
+ let test_file = temp_dir.path().join("advanced_features.ts");
1684
+
1685
+ let content = r#"// Advanced TypeScript features: decorators, utility types, conditional types
1686
+ import { Component } from '@angular/core';
1687
+
1688
+ // Decorators
1689
+ @Component({
1690
+ selector: 'app-user',
1691
+ template: '<div>{{user.name}}</div>'
1692
+ })
1693
+ class UserComponent {
1694
+ @Input() user: User;
1695
+
1696
+ @Output() userSelected = new EventEmitter<User>();
1697
+
1698
+ @HostListener('click', ['$event'])
1699
+ onClick(event: MouseEvent): void {
1700
+ this.userSelected.emit(this.user);
1701
+ }
1702
+ }
1703
+
1704
+ // Utility types
1705
+ type PartialUser = Partial<User>;
1706
+ type RequiredUser = Required<User>;
1707
+ type PickedUser = Pick<User, 'id' | 'name'>;
1708
+ type OmittedUser = Omit<User, 'password'>;
1709
+ type RecordType = Record<string, number>;
1710
+
1711
+ // Conditional types
1712
+ type NonNullable<T> = T extends null | undefined ? never : T;
1713
+ type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
1714
+ type Parameters<T> = T extends (...args: infer P) => any ? P : never;
1715
+
1716
+ // Mapped types
1717
+ type Readonly<T> = {
1718
+ readonly [P in keyof T]: T[P];
1719
+ };
1720
+
1721
+ type Optional<T> = {
1722
+ [P in keyof T]?: T[P];
1723
+ };
1724
+
1725
+ // Template literal types
1726
+ type EventName<T extends string> = `on${Capitalize<T>}`;
1727
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
1728
+ type ApiEndpoint<T extends HttpMethod> = `/${Lowercase<T>}/api`;
1729
+
1730
+ // Advanced generic constraints
1731
+ interface Identifiable {
1732
+ id: string;
1733
+ }
1734
+
1735
+ interface Timestamped {
1736
+ createdAt: Date;
1737
+ updatedAt: Date;
1738
+ }
1739
+
1740
+ type Entity<T> = T & Identifiable & Timestamped;
1741
+
1742
+ class Repository<T extends Identifiable> {
1743
+ private items: Map<string, T> = new Map();
1744
+
1745
+ save(item: T): void {
1746
+ this.items.set(item.id, item);
1747
+ }
1748
+
1749
+ findById(id: string): T | undefined {
1750
+ return this.items.get(id);
1751
+ }
1752
+
1753
+ findAll(): T[] {
1754
+ return Array.from(this.items.values());
1755
+ }
1756
+ }
1757
+
1758
+ // Function overloads
1759
+ function createElement(tagName: 'div'): HTMLDivElement;
1760
+ function createElement(tagName: 'span'): HTMLSpanElement;
1761
+ function createElement(tagName: 'input'): HTMLInputElement;
1762
+ function createElement(tagName: string): HTMLElement;
1763
+ function createElement(tagName: string): HTMLElement {
1764
+ return document.createElement(tagName);
1765
+ }
1766
+
1767
+ // Namespace and module augmentation
1768
+ namespace Utils {
1769
+ export function formatDate(date: Date): string {
1770
+ return date.toISOString().split('T')[0];
1771
+ }
1772
+
1773
+ export namespace Math {
1774
+ export function clamp(value: number, min: number, max: number): number {
1775
+ return Math.min(Math.max(value, min), max);
1776
+ }
1777
+ }
1778
+ }
1779
+
1780
+ // Module augmentation
1781
+ declare global {
1782
+ interface Window {
1783
+ customProperty: string;
1784
+ }
1785
+ }
1786
+
1787
+ declare module 'express' {
1788
+ interface Request {
1789
+ user?: User;
1790
+ }
1791
+ }
1792
+ "#;
1793
+
1794
+ fs::write(&test_file, content)?;
1795
+
1796
+ let ctx = TestContext::new();
1797
+
1798
+ // Test decorator patterns
1799
+ let output1 = ctx.run_probe(&[
1800
+ "search",
1801
+ "Component",
1802
+ test_file.to_str().unwrap(),
1803
+ "--format",
1804
+ "outline",
1805
+ "--allow-tests",
1806
+ "--exact",
1807
+ ])?;
1808
+
1809
+ // Test utility types
1810
+ let output2 = ctx.run_probe(&[
1811
+ "search",
1812
+ "Partial",
1813
+ test_file.to_str().unwrap(),
1814
+ "--format",
1815
+ "outline",
1816
+ "--allow-tests",
1817
+ "--exact",
1818
+ ])?;
1819
+
1820
+ // Test conditional types
1821
+ let output3 = ctx.run_probe(&[
1822
+ "search",
1823
+ "extends",
1824
+ test_file.to_str().unwrap(),
1825
+ "--format",
1826
+ "outline",
1827
+ "--allow-tests",
1828
+ ])?;
1829
+
1830
+ // Should find advanced TypeScript features
1831
+ assert!(
1832
+ !output1.is_empty() || !output2.is_empty() || !output3.is_empty(),
1833
+ "Should find advanced TypeScript features - output1: {} | output2: {} | output3: {}",
1834
+ output1,
1835
+ output2,
1836
+ output3
1837
+ );
1838
+
1839
+ Ok(())
1840
+ }
1841
+
1842
+ #[test]
1843
+ fn test_typescript_outline_control_flow_statements() -> Result<()> {
1844
+ let temp_dir = TempDir::new()?;
1845
+ let test_file = temp_dir.path().join("control_flow.ts");
1846
+
1847
+ let content = r#"/**
1848
+ * TypeScript control flow structures for testing outline format.
1849
+ */
1850
+ interface User {
1851
+ id: string;
1852
+ name: string;
1853
+ age: number;
1854
+ role: 'admin' | 'user' | 'guest';
1855
+ }
1856
+
1857
+ function validateUser(user: User): boolean {
1858
+ // Simple if statement
1859
+ if (!user.id || user.id.length === 0) {
1860
+ return false;
1861
+ }
1862
+
1863
+ // Switch statement with TypeScript types
1864
+ switch (user.role) {
1865
+ case 'admin':
1866
+ return user.age >= 21;
1867
+ case 'user':
1868
+ return user.age >= 13;
1869
+ case 'guest':
1870
+ return user.age >= 0;
1871
+ default:
1872
+ return false;
1873
+ }
1874
+ }
1875
+
1876
+ function processUsers(users: User[]): User[] {
1877
+ const validUsers: User[] = [];
1878
+
1879
+ // For loop with TypeScript
1880
+ for (let i = 0; i < users.length; i++) {
1881
+ const user = users[i];
1882
+
1883
+ // Nested if-else
1884
+ if (validateUser(user)) {
1885
+ if (user.role === 'admin') {
1886
+ // Complex nested logic
1887
+ if (user.age > 30) {
1888
+ validUsers.push({ ...user, role: 'admin' });
1889
+ } else {
1890
+ validUsers.push({ ...user, role: 'user' });
1891
+ }
1892
+ } else {
1893
+ validUsers.push(user);
1894
+ }
1895
+ }
1896
+ }
1897
+
1898
+ return validUsers;
1899
+ }
1900
+
1901
+ async function fetchAndProcessUsers(): Promise<User[]> {
1902
+ try {
1903
+ const response = await fetch('/api/users');
1904
+ const users: User[] = await response.json();
1905
+
1906
+ // While loop
1907
+ let retries = 3;
1908
+ while (retries > 0 && users.length === 0) {
1909
+ await new Promise(resolve => setTimeout(resolve, 1000));
1910
+ retries--;
1911
+ }
1912
+
1913
+ return processUsers(users);
1914
+ } catch (error) {
1915
+ console.error('Failed to fetch users:', error);
1916
+ return [];
1917
+ } finally {
1918
+ console.log('User fetch operation completed');
1919
+ }
1920
+ }
1921
+
1922
+ class UserManager {
1923
+ private users: Map<string, User> = new Map();
1924
+
1925
+ addUser(user: User): void {
1926
+ // TypeScript type guards
1927
+ if (this.isValidUser(user)) {
1928
+ this.users.set(user.id, user);
1929
+ } else {
1930
+ throw new Error('Invalid user');
1931
+ }
1932
+ }
1933
+
1934
+ private isValidUser(user: any): user is User {
1935
+ return (
1936
+ typeof user === 'object' &&
1937
+ typeof user.id === 'string' &&
1938
+ typeof user.name === 'string' &&
1939
+ typeof user.age === 'number' &&
1940
+ ['admin', 'user', 'guest'].includes(user.role)
1941
+ );
1942
+ }
1943
+
1944
+ getUsersByRole(role: User['role']): User[] {
1945
+ const result: User[] = [];
1946
+
1947
+ // For-of loop with Map
1948
+ for (const [id, user] of this.users.entries()) {
1949
+ if (user.role === role) {
1950
+ result.push(user);
1951
+ }
1952
+ }
1953
+
1954
+ return result;
1955
+ }
1956
+ }
1957
+ "#;
1958
+
1959
+ fs::write(&test_file, content)?;
1960
+
1961
+ let ctx = TestContext::new();
1962
+ let output = ctx.run_probe(&[
1963
+ "search",
1964
+ "function", // Search for function declarations
1965
+ test_file.to_str().unwrap(),
1966
+ "--format",
1967
+ "outline",
1968
+ "--allow-tests",
1969
+ "--exact",
1970
+ ])?;
1971
+
1972
+ // Verify control flow structures are shown with proper formatting
1973
+ assert!(
1974
+ output.contains("function")
1975
+ || output.contains("validateUser")
1976
+ || output.contains("processUsers"),
1977
+ "Should find control flow functions - output: {}",
1978
+ output
1979
+ );
1980
+
1981
+ // Test class search
1982
+ let output2 = ctx.run_probe(&[
1983
+ "search",
1984
+ "class", // Search for class declarations
1985
+ test_file.to_str().unwrap(),
1986
+ "--format",
1987
+ "outline",
1988
+ "--allow-tests",
1989
+ "--exact",
1990
+ ])?;
1991
+
1992
+ assert!(
1993
+ output2.contains("class") || output2.contains("UserManager"),
1994
+ "Should find class with control flow - output2: {}",
1995
+ output2
1996
+ );
1997
+
1998
+ Ok(())
1999
+ }
2000
+
2001
+ #[test]
2002
+ fn test_typescript_tsx_file_support() -> Result<()> {
2003
+ let temp_dir = TempDir::new()?;
2004
+ let test_file = temp_dir.path().join("react_component.tsx"); // Note: .tsx extension
2005
+
2006
+ let content = r#"import React, { useState, useEffect, FC, ReactElement } from 'react';
2007
+
2008
+ interface Props {
2009
+ title: string;
2010
+ count?: number;
2011
+ onIncrement?: () => void;
2012
+ }
2013
+
2014
+ interface State {
2015
+ value: number;
2016
+ loading: boolean;
2017
+ }
2018
+
2019
+ // Functional component with TypeScript
2020
+ const Counter: FC<Props> = ({ title, count = 0, onIncrement }): ReactElement => {
2021
+ const [state, setState] = useState<State>({
2022
+ value: count,
2023
+ loading: false,
2024
+ });
2025
+
2026
+ useEffect(() => {
2027
+ setState(prev => ({ ...prev, value: count }));
2028
+ }, [count]);
2029
+
2030
+ const handleIncrement = (): void => {
2031
+ setState(prev => ({ ...prev, loading: true }));
2032
+
2033
+ setTimeout(() => {
2034
+ setState(prev => ({
2035
+ value: prev.value + 1,
2036
+ loading: false,
2037
+ }));
2038
+ onIncrement?.();
2039
+ }, 100);
2040
+ };
2041
+
2042
+ return (
2043
+ <div className="counter">
2044
+ <h2>{title}</h2>
2045
+ <div className="counter-display">
2046
+ Count: {state.loading ? '...' : state.value}
2047
+ </div>
2048
+ <button
2049
+ onClick={handleIncrement}
2050
+ disabled={state.loading}
2051
+ type="button"
2052
+ >
2053
+ {state.loading ? 'Loading...' : 'Increment'}
2054
+ </button>
2055
+ </div>
2056
+ );
2057
+ };
2058
+
2059
+ // Class component with TypeScript
2060
+ interface ClassCounterProps {
2061
+ initialValue: number;
2062
+ step?: number;
2063
+ }
2064
+
2065
+ interface ClassCounterState {
2066
+ count: number;
2067
+ history: number[];
2068
+ }
2069
+
2070
+ class ClassCounter extends React.Component<ClassCounterProps, ClassCounterState> {
2071
+ constructor(props: ClassCounterProps) {
2072
+ super(props);
2073
+ this.state = {
2074
+ count: props.initialValue,
2075
+ history: [props.initialValue],
2076
+ };
2077
+ }
2078
+
2079
+ increment = (): void => {
2080
+ const step = this.props.step || 1;
2081
+ this.setState(prevState => ({
2082
+ count: prevState.count + step,
2083
+ history: [...prevState.history, prevState.count + step],
2084
+ }));
2085
+ };
2086
+
2087
+ reset = (): void => {
2088
+ this.setState({
2089
+ count: this.props.initialValue,
2090
+ history: [this.props.initialValue],
2091
+ });
2092
+ };
2093
+
2094
+ render(): ReactElement {
2095
+ const { count, history } = this.state;
2096
+
2097
+ return (
2098
+ <div className="class-counter">
2099
+ <div>Count: {count}</div>
2100
+ <div>History: {history.join(', ')}</div>
2101
+ <button onClick={this.increment}>Increment</button>
2102
+ <button onClick={this.reset}>Reset</button>
2103
+ </div>
2104
+ );
2105
+ }
2106
+ }
2107
+
2108
+ // Higher-order component with TypeScript
2109
+ function withLoading<P extends object>(
2110
+ WrappedComponent: React.ComponentType<P>
2111
+ ): React.FC<P & { loading?: boolean }> {
2112
+ return function WithLoadingComponent({ loading = false, ...props }) {
2113
+ if (loading) {
2114
+ return <div>Loading...</div>;
2115
+ }
2116
+ return <WrappedComponent {...props as P} />;
2117
+ };
2118
+ }
2119
+
2120
+ // Custom hook with TypeScript
2121
+ function useCounter(initialValue: number = 0): [number, () => void, () => void] {
2122
+ const [count, setCount] = useState<number>(initialValue);
2123
+
2124
+ const increment = (): void => {
2125
+ setCount(prev => prev + 1);
2126
+ };
2127
+
2128
+ const reset = (): void => {
2129
+ setCount(initialValue);
2130
+ };
2131
+
2132
+ return [count, increment, reset];
2133
+ }
2134
+
2135
+ export { Counter, ClassCounter, withLoading, useCounter };
2136
+ export type { Props, State, ClassCounterProps, ClassCounterState };
2137
+ "#;
2138
+
2139
+ fs::write(&test_file, content)?;
2140
+
2141
+ let ctx = TestContext::new();
2142
+ let output = ctx.run_probe(&[
2143
+ "search",
2144
+ "React", // Search for React-related symbols
2145
+ test_file.to_str().unwrap(),
2146
+ "--format",
2147
+ "outline",
2148
+ "--allow-tests",
2149
+ "--exact",
2150
+ ])?;
2151
+
2152
+ let output2 = ctx.run_probe(&[
2153
+ "search",
2154
+ "function", // Search for function declarations
2155
+ test_file.to_str().unwrap(),
2156
+ "--format",
2157
+ "outline",
2158
+ "--allow-tests",
2159
+ "--exact",
2160
+ ])?;
2161
+
2162
+ // Verify TSX file support - should handle both TypeScript and JSX
2163
+ assert!(
2164
+ !output.is_empty() || !output2.is_empty(),
2165
+ "Should process .tsx files correctly - output: {} | output2: {}",
2166
+ output,
2167
+ output2
2168
+ );
2169
+
2170
+ // Test component search
2171
+ let output3 = ctx.run_probe(&[
2172
+ "search",
2173
+ "Counter",
2174
+ test_file.to_str().unwrap(),
2175
+ "--format",
2176
+ "outline",
2177
+ "--allow-tests",
2178
+ "--exact",
2179
+ ])?;
2180
+
2181
+ assert!(
2182
+ output3.contains("Counter") || !output3.is_empty(),
2183
+ "Should find Counter component in TSX - output3: {}",
2184
+ output3
2185
+ );
2186
+
2187
+ Ok(())
2188
+ }