github-to-mcp-monorepo 1.0.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 (388) hide show
  1. package/.env.example +8 -0
  2. package/.github/CODEOWNERS +6 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc +7 -0
  7. package/.vscode/settings.json +4 -0
  8. package/ARCHITECTURE.md +1429 -0
  9. package/CHANGELOG.md +167 -0
  10. package/CONTRIBUTING.md +327 -0
  11. package/LICENSE +201 -0
  12. package/README.md +1028 -0
  13. package/SECURITY.md +248 -0
  14. package/VISUAL_GUIDE.md +437 -0
  15. package/apps/vscode/IMPLEMENTATION.md +480 -0
  16. package/apps/vscode/README.md +248 -0
  17. package/apps/vscode/package.json +381 -0
  18. package/apps/vscode/resources/icon.png +0 -0
  19. package/apps/vscode/resources/icon.svg +5 -0
  20. package/apps/vscode/src/commands/browseRegistry.ts +211 -0
  21. package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
  22. package/apps/vscode/src/commands/convert.ts +82 -0
  23. package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
  24. package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
  25. package/apps/vscode/src/commands/index.ts +121 -0
  26. package/apps/vscode/src/commands/validate.ts +197 -0
  27. package/apps/vscode/src/extension.ts +464 -0
  28. package/apps/vscode/src/global.d.ts +36 -0
  29. package/apps/vscode/src/test/extension.test.ts +73 -0
  30. package/apps/vscode/src/utils/file-generator.ts +529 -0
  31. package/apps/vscode/src/utils/github-api.ts +335 -0
  32. package/apps/vscode/src/utils/index.ts +29 -0
  33. package/apps/vscode/src/utils/mcp-config.ts +334 -0
  34. package/apps/vscode/src/utils/storage.ts +87 -0
  35. package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
  36. package/apps/vscode/src/views/OutputChannelView.ts +195 -0
  37. package/apps/vscode/src/views/StatusBarItem.ts +251 -0
  38. package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
  39. package/apps/vscode/src/views/historyProvider.ts +75 -0
  40. package/apps/vscode/src/views/index.ts +12 -0
  41. package/apps/vscode/src/views/resultsPanel.ts +330 -0
  42. package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
  43. package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
  44. package/apps/vscode/src/webviews/index.ts +9 -0
  45. package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
  46. package/apps/vscode/tsconfig.json +20 -0
  47. package/apps/web/PLAYGROUND_GUIDE.md +499 -0
  48. package/apps/web/README.md +505 -0
  49. package/apps/web/app/api/convert/route.ts +100 -0
  50. package/apps/web/app/api/convert/stream/route.ts +198 -0
  51. package/apps/web/app/api/deploy/route.ts +157 -0
  52. package/apps/web/app/api/edge/route.ts +308 -0
  53. package/apps/web/app/api/export-docker/route.ts +284 -0
  54. package/apps/web/app/api/generate-openapi/route.ts +119 -0
  55. package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
  56. package/apps/web/app/api/playground/connect/route.ts +143 -0
  57. package/apps/web/app/api/playground/disconnect/route.ts +78 -0
  58. package/apps/web/app/api/playground/execute/route.ts +135 -0
  59. package/apps/web/app/api/playground/sessions/route.ts +103 -0
  60. package/apps/web/app/api/playground/tools/route.ts +117 -0
  61. package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
  62. package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
  63. package/apps/web/app/api/playground/v2/health/route.ts +80 -0
  64. package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
  65. package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
  66. package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
  67. package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
  68. package/apps/web/app/api/stream/route.ts +232 -0
  69. package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
  70. package/apps/web/app/batch/page.tsx +37 -0
  71. package/apps/web/app/convert/page.tsx +269 -0
  72. package/apps/web/app/dashboard/page.tsx +380 -0
  73. package/apps/web/app/globals.css +622 -0
  74. package/apps/web/app/layout.tsx +120 -0
  75. package/apps/web/app/manifest.ts +31 -0
  76. package/apps/web/app/opengraph-image.tsx +112 -0
  77. package/apps/web/app/page.old.tsx +924 -0
  78. package/apps/web/app/page.tsx +77 -0
  79. package/apps/web/app/playground/page.tsx +306 -0
  80. package/apps/web/app/playground/v2/error.tsx +163 -0
  81. package/apps/web/app/playground/v2/layout.tsx +58 -0
  82. package/apps/web/app/playground/v2/loading.tsx +152 -0
  83. package/apps/web/app/playground/v2/page.tsx +644 -0
  84. package/apps/web/app/playground/v2/providers.tsx +214 -0
  85. package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
  86. package/apps/web/app/playground/v2/use-url-state.ts +296 -0
  87. package/apps/web/app/providers.tsx +22 -0
  88. package/apps/web/app/sitemap.ts +32 -0
  89. package/apps/web/app/twitter-image.tsx +112 -0
  90. package/apps/web/components/BranchSelector.tsx +401 -0
  91. package/apps/web/components/ClaudeConfigExport.tsx +226 -0
  92. package/apps/web/components/Features.tsx +84 -0
  93. package/apps/web/components/Footer.tsx +119 -0
  94. package/apps/web/components/GenerationProgress.tsx +248 -0
  95. package/apps/web/components/GithubUrlInput.tsx +483 -0
  96. package/apps/web/components/Header.tsx +175 -0
  97. package/apps/web/components/Hero.tsx +117 -0
  98. package/apps/web/components/HowItWorks.tsx +119 -0
  99. package/apps/web/components/InstallBanner.tsx +158 -0
  100. package/apps/web/components/Logo.tsx +116 -0
  101. package/apps/web/components/ParticleBackground.tsx +105 -0
  102. package/apps/web/components/Playground.tsx +472 -0
  103. package/apps/web/components/PlaygroundToolTester.tsx +410 -0
  104. package/apps/web/components/ProductCards.tsx +179 -0
  105. package/apps/web/components/SplitView.tsx +194 -0
  106. package/apps/web/components/ToolFilter.tsx +260 -0
  107. package/apps/web/components/ToolList.tsx +325 -0
  108. package/apps/web/components/batch/BatchConvert.tsx +785 -0
  109. package/apps/web/components/batch/index.ts +7 -0
  110. package/apps/web/components/convert/ConfigTabs.tsx +230 -0
  111. package/apps/web/components/convert/ConversionResult.tsx +482 -0
  112. package/apps/web/components/convert/InlinePlayground.tsx +259 -0
  113. package/apps/web/components/convert/LoadingSteps.tsx +311 -0
  114. package/apps/web/components/convert/OneClickInstall.tsx +224 -0
  115. package/apps/web/components/convert/ToolCard.tsx +189 -0
  116. package/apps/web/components/convert/TryInPlayground.tsx +242 -0
  117. package/apps/web/components/convert/index.ts +12 -0
  118. package/apps/web/components/deploy/DeployButton.tsx +369 -0
  119. package/apps/web/components/deploy/index.ts +7 -0
  120. package/apps/web/components/docker/DockerExport.tsx +690 -0
  121. package/apps/web/components/docker/index.ts +7 -0
  122. package/apps/web/components/install/OneClickInstall.tsx +676 -0
  123. package/apps/web/components/install/index.ts +7 -0
  124. package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
  125. package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
  126. package/apps/web/components/playground/EmptyStates.tsx +305 -0
  127. package/apps/web/components/playground/ExecutionLog.tsx +260 -0
  128. package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
  129. package/apps/web/components/playground/JsonViewer.tsx +388 -0
  130. package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
  131. package/apps/web/components/playground/PromptsPanel.tsx +385 -0
  132. package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
  133. package/apps/web/components/playground/SchemaForm.tsx +477 -0
  134. package/apps/web/components/playground/ServerStatus.tsx +151 -0
  135. package/apps/web/components/playground/ShareButton.tsx +239 -0
  136. package/apps/web/components/playground/ToolsPanel.tsx +309 -0
  137. package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
  138. package/apps/web/components/playground/index.ts +74 -0
  139. package/apps/web/components/playground/types.ts +202 -0
  140. package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
  141. package/apps/web/components/streaming/index.ts +7 -0
  142. package/apps/web/components/ui/badge.tsx +42 -0
  143. package/apps/web/components/ui/button.tsx +88 -0
  144. package/apps/web/components/ui/card.tsx +75 -0
  145. package/apps/web/components/ui/code-block.tsx +122 -0
  146. package/apps/web/components/ui/index.ts +12 -0
  147. package/apps/web/components/ui/input.tsx +55 -0
  148. package/apps/web/components/ui/tabs.tsx +61 -0
  149. package/apps/web/hooks/index.ts +85 -0
  150. package/apps/web/hooks/types.ts +1173 -0
  151. package/apps/web/hooks/use-conversion.ts +133 -0
  152. package/apps/web/hooks/use-execution-history.ts +376 -0
  153. package/apps/web/hooks/use-generation-progress.ts +147 -0
  154. package/apps/web/hooks/use-local-storage.ts +88 -0
  155. package/apps/web/hooks/use-mcp-client.ts +623 -0
  156. package/apps/web/hooks/use-mcp-connection.ts +500 -0
  157. package/apps/web/hooks/use-mcp-execution.ts +282 -0
  158. package/apps/web/hooks/use-mcp-prompts.ts +441 -0
  159. package/apps/web/hooks/use-mcp-resources.ts +430 -0
  160. package/apps/web/hooks/use-mcp-tools.ts +540 -0
  161. package/apps/web/hooks/use-playground-store.ts +299 -0
  162. package/apps/web/hooks/use-playground.ts +184 -0
  163. package/apps/web/hooks/use-streaming-conversion.ts +227 -0
  164. package/apps/web/hooks/useBatchConversion.ts +271 -0
  165. package/apps/web/hooks/useDockerConfig.ts +161 -0
  166. package/apps/web/hooks/usePlatformDetection.ts +80 -0
  167. package/apps/web/hooks/useStreaming.ts +199 -0
  168. package/apps/web/lib/api/errors.ts +386 -0
  169. package/apps/web/lib/api/index.ts +137 -0
  170. package/apps/web/lib/api/logger.ts +187 -0
  171. package/apps/web/lib/api/middleware.ts +364 -0
  172. package/apps/web/lib/api/openapi.ts +977 -0
  173. package/apps/web/lib/api/session-manager.ts +594 -0
  174. package/apps/web/lib/api/types.ts +433 -0
  175. package/apps/web/lib/api/validation.ts +523 -0
  176. package/apps/web/lib/constants.ts +114 -0
  177. package/apps/web/lib/mcp/client.ts +1137 -0
  178. package/apps/web/lib/mcp/events.ts +651 -0
  179. package/apps/web/lib/mcp/index.ts +347 -0
  180. package/apps/web/lib/mcp/logger.ts +428 -0
  181. package/apps/web/lib/mcp/metrics.ts +703 -0
  182. package/apps/web/lib/mcp/retry.ts +616 -0
  183. package/apps/web/lib/mcp/session-manager.ts +779 -0
  184. package/apps/web/lib/mcp/transports.ts +988 -0
  185. package/apps/web/lib/mcp/types.ts +594 -0
  186. package/apps/web/lib/mcp-client-enhanced.ts +871 -0
  187. package/apps/web/lib/mcp-client.ts +778 -0
  188. package/apps/web/lib/mcp-errors.ts +489 -0
  189. package/apps/web/lib/mcp-sandbox.ts +593 -0
  190. package/apps/web/lib/mcp-testing.ts +428 -0
  191. package/apps/web/lib/mcp-types.ts +448 -0
  192. package/apps/web/lib/playground-store.tsx +1147 -0
  193. package/apps/web/lib/utils.ts +439 -0
  194. package/apps/web/next-env.d.ts +5 -0
  195. package/apps/web/next.config.js +23 -0
  196. package/apps/web/package.json +55 -0
  197. package/apps/web/postcss.config.js +6 -0
  198. package/apps/web/public/.well-known/ai-plugin.json +17 -0
  199. package/apps/web/public/logo.svg +6 -0
  200. package/apps/web/public/robots.txt +22 -0
  201. package/apps/web/public/schema.json +27 -0
  202. package/apps/web/tailwind.config.js +26 -0
  203. package/apps/web/tailwind.config.ts +123 -0
  204. package/apps/web/tsconfig.json +20 -0
  205. package/apps/web/types/deploy.ts +139 -0
  206. package/apps/web/types/index.ts +247 -0
  207. package/apps/web/vercel.json +39 -0
  208. package/eslint.config.mjs +23 -0
  209. package/llms.txt +102 -0
  210. package/mkdocs/docs/api/core.md +318 -0
  211. package/mkdocs/docs/api/index.md +128 -0
  212. package/mkdocs/docs/api/mcp-server.md +301 -0
  213. package/mkdocs/docs/api/openapi-parser.md +254 -0
  214. package/mkdocs/docs/assets/logo.svg +7 -0
  215. package/mkdocs/docs/changelog.md +118 -0
  216. package/mkdocs/docs/cli/generate.md +148 -0
  217. package/mkdocs/docs/cli/index.md +52 -0
  218. package/mkdocs/docs/cli/inspect.md +164 -0
  219. package/mkdocs/docs/cli/serve.md +136 -0
  220. package/mkdocs/docs/concepts/classification.md +254 -0
  221. package/mkdocs/docs/concepts/how-it-works.md +299 -0
  222. package/mkdocs/docs/concepts/index.md +77 -0
  223. package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
  224. package/mkdocs/docs/concepts/tool-types.md +382 -0
  225. package/mkdocs/docs/contributing/architecture.md +262 -0
  226. package/mkdocs/docs/contributing/development.md +245 -0
  227. package/mkdocs/docs/contributing/index.md +73 -0
  228. package/mkdocs/docs/contributing/testing.md +320 -0
  229. package/mkdocs/docs/getting-started/configuration.md +235 -0
  230. package/mkdocs/docs/getting-started/index.md +54 -0
  231. package/mkdocs/docs/getting-started/installation.md +145 -0
  232. package/mkdocs/docs/getting-started/quickstart.md +160 -0
  233. package/mkdocs/docs/guides/batch.md +375 -0
  234. package/mkdocs/docs/guides/claude-desktop.md +227 -0
  235. package/mkdocs/docs/guides/cursor.md +188 -0
  236. package/mkdocs/docs/guides/custom-tools.md +367 -0
  237. package/mkdocs/docs/guides/index.md +78 -0
  238. package/mkdocs/docs/guides/private-repos.md +221 -0
  239. package/mkdocs/docs/guides/vscode.md +247 -0
  240. package/mkdocs/docs/index.md +175 -0
  241. package/mkdocs/docs/reference/config.md +223 -0
  242. package/mkdocs/docs/reference/env.md +192 -0
  243. package/mkdocs/docs/reference/index.md +102 -0
  244. package/mkdocs/docs/reference/tools.md +309 -0
  245. package/mkdocs/docs/stylesheets/extra.css +231 -0
  246. package/mkdocs/mkdocs.yml +204 -0
  247. package/mkdocs/overrides/.gitkeep +1 -0
  248. package/mkdocs/overrides/main.html +7 -0
  249. package/mkdocs/python-deps.txt +7 -0
  250. package/mkdocs/vercel.json +11 -0
  251. package/package.json +63 -0
  252. package/packages/core/package.json +61 -0
  253. package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
  254. package/packages/core/src/__tests__/cli.test.ts +235 -0
  255. package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
  256. package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
  257. package/packages/core/src/__tests__/github-client.test.ts +390 -0
  258. package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
  259. package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
  260. package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
  261. package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
  262. package/packages/core/src/__tests__/plugins.test.ts +467 -0
  263. package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
  264. package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
  265. package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
  266. package/packages/core/src/__tests__/streaming.test.ts +251 -0
  267. package/packages/core/src/additional-extractors.ts +333 -0
  268. package/packages/core/src/cache/cache-interface.ts +179 -0
  269. package/packages/core/src/cache/index.ts +210 -0
  270. package/packages/core/src/cache/redis-cache.ts +291 -0
  271. package/packages/core/src/cache/upstash-cache.ts +379 -0
  272. package/packages/core/src/cache.ts +251 -0
  273. package/packages/core/src/cli.ts +822 -0
  274. package/packages/core/src/code-extractor.ts +696 -0
  275. package/packages/core/src/docker-generator.ts +470 -0
  276. package/packages/core/src/edge-compatible.ts +491 -0
  277. package/packages/core/src/extractors/go-extractor.ts +791 -0
  278. package/packages/core/src/extractors/index.ts +9 -0
  279. package/packages/core/src/extractors/java-extractor.ts +937 -0
  280. package/packages/core/src/extractors/rust-extractor.ts +744 -0
  281. package/packages/core/src/github-client.ts +319 -0
  282. package/packages/core/src/go-generator.ts +356 -0
  283. package/packages/core/src/graphql-extractor.ts +358 -0
  284. package/packages/core/src/index.ts +797 -0
  285. package/packages/core/src/langchain-exporter.ts +617 -0
  286. package/packages/core/src/language-parsers.ts +1114 -0
  287. package/packages/core/src/mcp-introspector.ts +279 -0
  288. package/packages/core/src/monorepo-detector.ts +378 -0
  289. package/packages/core/src/plugins/index.ts +370 -0
  290. package/packages/core/src/plugins/registry.ts +404 -0
  291. package/packages/core/src/plugins/types.ts +215 -0
  292. package/packages/core/src/providers/base-provider.ts +246 -0
  293. package/packages/core/src/providers/bitbucket-client.ts +464 -0
  294. package/packages/core/src/providers/gitlab-client.ts +388 -0
  295. package/packages/core/src/providers/index.ts +176 -0
  296. package/packages/core/src/python-generator.ts +260 -0
  297. package/packages/core/src/queue/index.ts +100 -0
  298. package/packages/core/src/queue/memory-queue.ts +445 -0
  299. package/packages/core/src/queue/redis-queue.ts +578 -0
  300. package/packages/core/src/queue/types.ts +251 -0
  301. package/packages/core/src/readme-extractor.ts +409 -0
  302. package/packages/core/src/schema-generator.ts +638 -0
  303. package/packages/core/src/streaming.ts +999 -0
  304. package/packages/core/src/types.ts +289 -0
  305. package/packages/core/tsconfig.json +9 -0
  306. package/packages/core/tsup.config.ts +25 -0
  307. package/packages/mcp-server/README.md +297 -0
  308. package/packages/mcp-server/package.json +55 -0
  309. package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
  310. package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
  311. package/packages/mcp-server/src/index.ts +1206 -0
  312. package/packages/mcp-server/src/prompts/index.ts +601 -0
  313. package/packages/mcp-server/src/tools/export-docker.ts +362 -0
  314. package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
  315. package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
  316. package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
  317. package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
  318. package/packages/mcp-server/tsconfig.json +12 -0
  319. package/packages/mcp-server/tsup.config.ts +14 -0
  320. package/packages/openapi-parser/package-lock.json +3028 -0
  321. package/packages/openapi-parser/package.json +41 -0
  322. package/packages/openapi-parser/src/analyzer.ts +700 -0
  323. package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
  324. package/packages/openapi-parser/src/cli.ts +302 -0
  325. package/packages/openapi-parser/src/generator.ts +570 -0
  326. package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
  327. package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
  328. package/packages/openapi-parser/src/generators/index.ts +200 -0
  329. package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
  330. package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
  331. package/packages/openapi-parser/src/generators/types.ts +298 -0
  332. package/packages/openapi-parser/src/graphql-parser.ts +462 -0
  333. package/packages/openapi-parser/src/grpc-parser.ts +649 -0
  334. package/packages/openapi-parser/src/har-parser.ts +723 -0
  335. package/packages/openapi-parser/src/index.ts +635 -0
  336. package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
  337. package/packages/openapi-parser/src/parser.ts +231 -0
  338. package/packages/openapi-parser/src/postman-parser.ts +611 -0
  339. package/packages/openapi-parser/src/ref-resolver.ts +313 -0
  340. package/packages/openapi-parser/src/transformer.ts +459 -0
  341. package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
  342. package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
  343. package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
  344. package/packages/openapi-parser/tests/parsers.test.ts +847 -0
  345. package/packages/openapi-parser/tsconfig.json +9 -0
  346. package/packages/openapi-parser/tsup.config.ts +11 -0
  347. package/packages/registry/package.json +59 -0
  348. package/packages/registry/src/cli.ts +456 -0
  349. package/packages/registry/src/index.ts +44 -0
  350. package/packages/registry/src/popular/github.json +47 -0
  351. package/packages/registry/src/popular/index.ts +55 -0
  352. package/packages/registry/src/popular/linear.json +42 -0
  353. package/packages/registry/src/popular/notion.json +42 -0
  354. package/packages/registry/src/popular/openai.json +40 -0
  355. package/packages/registry/src/popular/resend.json +38 -0
  356. package/packages/registry/src/popular/slack.json +42 -0
  357. package/packages/registry/src/popular/stripe.json +163 -0
  358. package/packages/registry/src/popular/supabase.json +42 -0
  359. package/packages/registry/src/popular/twilio.json +40 -0
  360. package/packages/registry/src/popular/vercel.json +40 -0
  361. package/packages/registry/src/registry.ts +492 -0
  362. package/packages/registry/src/storage.ts +334 -0
  363. package/packages/registry/src/types.ts +275 -0
  364. package/packages/registry/src/updater.ts +208 -0
  365. package/packages/registry/tsconfig.json +10 -0
  366. package/packages/registry/tsup.config.ts +11 -0
  367. package/pnpm-workspace.yaml +3 -0
  368. package/scripts/build-docs.sh +16 -0
  369. package/server.json +9 -0
  370. package/templates/Dockerfile.python.template +60 -0
  371. package/templates/Dockerfile.typescript.template +60 -0
  372. package/templates/docker-compose.template.yml +68 -0
  373. package/tests/fixtures/express-app/index.js +34 -0
  374. package/tests/fixtures/express-app/routes/posts.js +43 -0
  375. package/tests/fixtures/express-app/routes/users.js +58 -0
  376. package/tests/fixtures/fastapi-app/main.py +125 -0
  377. package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
  378. package/tests/fixtures/graphql/simple-schema.graphql +65 -0
  379. package/tests/fixtures/mocks/github-api-responses.json +63 -0
  380. package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
  381. package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
  382. package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
  383. package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
  384. package/tests/fixtures/openapi/petstore.yaml +179 -0
  385. package/tests/integration/langchain-export.test.ts +405 -0
  386. package/tests/integration/openapi-conversion.test.ts +221 -0
  387. package/tsconfig.json +18 -0
  388. package/vitest.config.ts +32 -0
@@ -0,0 +1,638 @@
1
+ /**
2
+ * @fileoverview JSON Schema generation from function signatures
3
+ * @copyright Copyright (c) 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ /**
8
+ * Represents a parameter for schema generation
9
+ */
10
+ interface SchemaParameter {
11
+ name: string;
12
+ type: string;
13
+ required: boolean;
14
+ description?: string;
15
+ defaultValue?: any;
16
+ enumValues?: string[];
17
+ }
18
+
19
+ /**
20
+ * JSON Schema type definitions
21
+ */
22
+ interface JsonSchemaProperty {
23
+ type: string | string[];
24
+ description?: string;
25
+ default?: any;
26
+ enum?: any[];
27
+ items?: JsonSchemaProperty;
28
+ properties?: Record<string, JsonSchemaProperty>;
29
+ required?: string[];
30
+ additionalProperties?: boolean | JsonSchemaProperty;
31
+ oneOf?: JsonSchemaProperty[];
32
+ anyOf?: JsonSchemaProperty[];
33
+ minimum?: number;
34
+ maximum?: number;
35
+ minLength?: number;
36
+ maxLength?: number;
37
+ pattern?: string;
38
+ format?: string;
39
+ }
40
+
41
+ /**
42
+ * Generated JSON Schema
43
+ */
44
+ interface GeneratedSchema {
45
+ type: 'object';
46
+ properties: Record<string, JsonSchemaProperty>;
47
+ required: string[];
48
+ additionalProperties?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Schema Generator - converts function signatures to JSON Schema
53
+ */
54
+ export class SchemaGenerator {
55
+ /**
56
+ * Generate JSON Schema from parameters
57
+ */
58
+ generateFromParameters(params: SchemaParameter[]): GeneratedSchema {
59
+ const properties: Record<string, JsonSchemaProperty> = {};
60
+ const required: string[] = [];
61
+
62
+ for (const param of params) {
63
+ properties[param.name] = this.parameterToProperty(param);
64
+ if (param.required && param.defaultValue === undefined) {
65
+ required.push(param.name);
66
+ }
67
+ }
68
+
69
+ return {
70
+ type: 'object',
71
+ properties,
72
+ required
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Convert a parameter to a JSON Schema property
78
+ */
79
+ private parameterToProperty(param: SchemaParameter): JsonSchemaProperty {
80
+ const property = this.typeToJsonSchema(param.type);
81
+
82
+ if (param.description) {
83
+ property.description = param.description;
84
+ }
85
+
86
+ if (param.defaultValue !== undefined) {
87
+ property.default = param.defaultValue;
88
+ }
89
+
90
+ if (param.enumValues && param.enumValues.length > 0) {
91
+ property.enum = param.enumValues;
92
+ }
93
+
94
+ return property;
95
+ }
96
+
97
+ /**
98
+ * Convert a type string to JSON Schema property
99
+ */
100
+ typeToJsonSchema(typeStr: string): JsonSchemaProperty {
101
+ const normalized = typeStr.trim();
102
+
103
+ // Handle TypeScript types
104
+ if (this.isTypeScriptType(normalized)) {
105
+ return this.parseTypeScriptType(normalized);
106
+ }
107
+
108
+ // Handle Python types
109
+ if (this.isPythonType(normalized)) {
110
+ return this.parsePythonType(normalized);
111
+ }
112
+
113
+ // Handle Go types
114
+ if (this.isGoType(normalized)) {
115
+ return this.parseGoType(normalized);
116
+ }
117
+
118
+ // Handle Rust types
119
+ if (this.isRustType(normalized)) {
120
+ return this.parseRustType(normalized);
121
+ }
122
+
123
+ // Default fallback
124
+ return { type: 'string' };
125
+ }
126
+
127
+ // ===================== TypeScript Type Parsing =====================
128
+
129
+ private isTypeScriptType(type: string): boolean {
130
+ const tsIndicators = ['string', 'number', 'boolean', 'object', 'any', 'unknown',
131
+ 'Array<', 'Record<', 'Map<', 'Set<', '[]', '|', '&',
132
+ 'Promise<', 'Partial<', 'Required<', 'Readonly<'];
133
+ return tsIndicators.some(i => type.includes(i)) || /^[A-Z]/.test(type);
134
+ }
135
+
136
+ private parseTypeScriptType(type: string): JsonSchemaProperty {
137
+ const normalized = type.trim();
138
+
139
+ // Primitive types
140
+ if (normalized === 'string') return { type: 'string' };
141
+ if (normalized === 'number') return { type: 'number' };
142
+ if (normalized === 'boolean') return { type: 'boolean' };
143
+ if (normalized === 'null' || normalized === 'undefined') return { type: 'null' };
144
+ if (normalized === 'any' || normalized === 'unknown') return { type: 'string' };
145
+ if (normalized === 'object') return { type: 'object' };
146
+
147
+ // Array types: string[] or Array<string>
148
+ if (normalized.endsWith('[]')) {
149
+ const itemType = normalized.slice(0, -2);
150
+ return {
151
+ type: 'array',
152
+ items: this.parseTypeScriptType(itemType)
153
+ };
154
+ }
155
+
156
+ const arrayMatch = normalized.match(/^Array<(.+)>$/);
157
+ if (arrayMatch) {
158
+ return {
159
+ type: 'array',
160
+ items: this.parseTypeScriptType(arrayMatch[1])
161
+ };
162
+ }
163
+
164
+ // Record<string, T> -> object with additionalProperties
165
+ const recordMatch = normalized.match(/^Record<string,\s*(.+)>$/);
166
+ if (recordMatch) {
167
+ return {
168
+ type: 'object',
169
+ additionalProperties: this.parseTypeScriptType(recordMatch[1])
170
+ };
171
+ }
172
+
173
+ // Map<K, V> -> object
174
+ if (normalized.startsWith('Map<')) {
175
+ return { type: 'object' };
176
+ }
177
+
178
+ // Set<T> -> array with unique items
179
+ const setMatch = normalized.match(/^Set<(.+)>$/);
180
+ if (setMatch) {
181
+ return {
182
+ type: 'array',
183
+ items: this.parseTypeScriptType(setMatch[1])
184
+ };
185
+ }
186
+
187
+ // Union types: string | number
188
+ if (normalized.includes(' | ')) {
189
+ const types = this.splitUnion(normalized);
190
+
191
+ // Check for nullable: T | null
192
+ const nullableIdx = types.findIndex(t => t === 'null' || t === 'undefined');
193
+ if (nullableIdx !== -1) {
194
+ types.splice(nullableIdx, 1);
195
+ if (types.length === 1) {
196
+ const baseSchema = this.parseTypeScriptType(types[0]);
197
+ baseSchema.type = [baseSchema.type as string, 'null'];
198
+ return baseSchema;
199
+ }
200
+ }
201
+
202
+ // Multiple non-null types
203
+ return {
204
+ type: 'string', // Fallback
205
+ anyOf: types.map(t => this.parseTypeScriptType(t))
206
+ };
207
+ }
208
+
209
+ // Literal types: "value" or 'value' or number literals
210
+ if (normalized.startsWith('"') || normalized.startsWith("'")) {
211
+ return {
212
+ type: 'string',
213
+ enum: [normalized.slice(1, -1)]
214
+ };
215
+ }
216
+ if (/^\d+$/.test(normalized)) {
217
+ return {
218
+ type: 'number',
219
+ enum: [parseInt(normalized)]
220
+ };
221
+ }
222
+
223
+ // Object type with properties: { name: string; age: number }
224
+ if (normalized.startsWith('{') && normalized.endsWith('}')) {
225
+ return this.parseInlineObjectType(normalized);
226
+ }
227
+
228
+ // Interface/Type reference - treat as object
229
+ return { type: 'object' };
230
+ }
231
+
232
+ private splitUnion(type: string): string[] {
233
+ const parts: string[] = [];
234
+ let depth = 0;
235
+ let current = '';
236
+
237
+ for (const char of type) {
238
+ if (char === '<' || char === '(' || char === '[' || char === '{') depth++;
239
+ if (char === '>' || char === ')' || char === ']' || char === '}') depth--;
240
+ if (char === '|' && depth === 0) {
241
+ parts.push(current.trim());
242
+ current = '';
243
+ } else {
244
+ current += char;
245
+ }
246
+ }
247
+ if (current.trim()) parts.push(current.trim());
248
+
249
+ return parts;
250
+ }
251
+
252
+ private parseInlineObjectType(type: string): JsonSchemaProperty {
253
+ const content = type.slice(1, -1).trim();
254
+ const properties: Record<string, JsonSchemaProperty> = {};
255
+ const required: string[] = [];
256
+
257
+ // Split by ; or ,
258
+ const parts = content.split(/[;,]/).map(p => p.trim()).filter(p => p);
259
+
260
+ for (const part of parts) {
261
+ const match = part.match(/^(\w+)(\??):\s*(.+)$/);
262
+ if (match) {
263
+ const [, name, optional, propType] = match;
264
+ properties[name] = this.parseTypeScriptType(propType.trim());
265
+ if (!optional) {
266
+ required.push(name);
267
+ }
268
+ }
269
+ }
270
+
271
+ return {
272
+ type: 'object',
273
+ properties,
274
+ required
275
+ };
276
+ }
277
+
278
+ // ===================== Python Type Parsing =====================
279
+
280
+ private isPythonType(type: string): boolean {
281
+ const pyIndicators = ['str', 'int', 'float', 'bool', 'None', 'List[', 'Dict[',
282
+ 'Optional[', 'Union[', 'Tuple[', 'Set[', 'Any', 'Callable'];
283
+ return pyIndicators.some(i => type.includes(i));
284
+ }
285
+
286
+ private parsePythonType(type: string): JsonSchemaProperty {
287
+ const normalized = type.trim();
288
+
289
+ // Primitive types
290
+ if (normalized === 'str') return { type: 'string' };
291
+ if (normalized === 'int') return { type: 'integer' };
292
+ if (normalized === 'float') return { type: 'number' };
293
+ if (normalized === 'bool') return { type: 'boolean' };
294
+ if (normalized === 'None') return { type: 'null' };
295
+ if (normalized === 'Any') return { type: 'string' };
296
+
297
+ // Optional[T] -> nullable T
298
+ const optionalMatch = normalized.match(/^Optional\[(.+)\]$/);
299
+ if (optionalMatch) {
300
+ const baseSchema = this.parsePythonType(optionalMatch[1]);
301
+ baseSchema.type = [baseSchema.type as string, 'null'];
302
+ return baseSchema;
303
+ }
304
+
305
+ // List[T] -> array
306
+ const listMatch = normalized.match(/^(?:List|list)\[(.+)\]$/);
307
+ if (listMatch) {
308
+ return {
309
+ type: 'array',
310
+ items: this.parsePythonType(listMatch[1])
311
+ };
312
+ }
313
+
314
+ // Dict[K, V] -> object
315
+ const dictMatch = normalized.match(/^(?:Dict|dict)\[(?:str),\s*(.+)\]$/);
316
+ if (dictMatch) {
317
+ return {
318
+ type: 'object',
319
+ additionalProperties: this.parsePythonType(dictMatch[1])
320
+ };
321
+ }
322
+
323
+ // Set[T] -> array
324
+ const setMatch = normalized.match(/^(?:Set|set)\[(.+)\]$/);
325
+ if (setMatch) {
326
+ return {
327
+ type: 'array',
328
+ items: this.parsePythonType(setMatch[1])
329
+ };
330
+ }
331
+
332
+ // Tuple[T, ...] -> array
333
+ if (normalized.startsWith('Tuple[') || normalized.startsWith('tuple[')) {
334
+ return { type: 'array' };
335
+ }
336
+
337
+ // Union[T1, T2] -> anyOf
338
+ const unionMatch = normalized.match(/^Union\[(.+)\]$/);
339
+ if (unionMatch) {
340
+ const types = this.splitGenericArgs(unionMatch[1]);
341
+
342
+ // Check for Optional pattern: Union[T, None]
343
+ const noneIdx = types.findIndex(t => t === 'None');
344
+ if (noneIdx !== -1 && types.length === 2) {
345
+ types.splice(noneIdx, 1);
346
+ const baseSchema = this.parsePythonType(types[0]);
347
+ baseSchema.type = [baseSchema.type as string, 'null'];
348
+ return baseSchema;
349
+ }
350
+
351
+ return {
352
+ type: 'string',
353
+ anyOf: types.map(t => this.parsePythonType(t))
354
+ };
355
+ }
356
+
357
+ // Literal['a', 'b'] -> enum
358
+ const literalMatch = normalized.match(/^Literal\[(.+)\]$/);
359
+ if (literalMatch) {
360
+ const values = literalMatch[1]
361
+ .split(',')
362
+ .map(v => v.trim())
363
+ .map(v => v.startsWith("'") || v.startsWith('"') ? v.slice(1, -1) : v);
364
+ return {
365
+ type: 'string',
366
+ enum: values
367
+ };
368
+ }
369
+
370
+ // Class/TypedDict reference -> object
371
+ return { type: 'object' };
372
+ }
373
+
374
+ private splitGenericArgs(argsStr: string): string[] {
375
+ const args: string[] = [];
376
+ let depth = 0;
377
+ let current = '';
378
+
379
+ for (const char of argsStr) {
380
+ if (char === '[') depth++;
381
+ if (char === ']') depth--;
382
+ if (char === ',' && depth === 0) {
383
+ args.push(current.trim());
384
+ current = '';
385
+ } else {
386
+ current += char;
387
+ }
388
+ }
389
+ if (current.trim()) args.push(current.trim());
390
+
391
+ return args;
392
+ }
393
+
394
+ // ===================== Go Type Parsing =====================
395
+
396
+ private isGoType(type: string): boolean {
397
+ const goIndicators = ['string', 'int', 'int32', 'int64', 'float32', 'float64',
398
+ 'bool', 'byte', 'rune', 'error', 'interface{}',
399
+ '[]', 'map[', '*'];
400
+ return goIndicators.some(i => type.includes(i));
401
+ }
402
+
403
+ private parseGoType(type: string): JsonSchemaProperty {
404
+ const normalized = type.trim();
405
+
406
+ // Pointer types: *T -> T (nullable in schema)
407
+ if (normalized.startsWith('*')) {
408
+ return this.parseGoType(normalized.slice(1));
409
+ }
410
+
411
+ // Primitives
412
+ if (normalized === 'string') return { type: 'string' };
413
+ if (normalized.match(/^u?int\d*$/) || normalized === 'byte' || normalized === 'rune') {
414
+ return { type: 'integer' };
415
+ }
416
+ if (normalized.match(/^float\d+$/)) return { type: 'number' };
417
+ if (normalized === 'bool') return { type: 'boolean' };
418
+
419
+ // Slice: []T
420
+ if (normalized.startsWith('[]')) {
421
+ return {
422
+ type: 'array',
423
+ items: this.parseGoType(normalized.slice(2))
424
+ };
425
+ }
426
+
427
+ // Variadic: ...T -> array
428
+ if (normalized.startsWith('...')) {
429
+ return {
430
+ type: 'array',
431
+ items: this.parseGoType(normalized.slice(3))
432
+ };
433
+ }
434
+
435
+ // Map: map[K]V
436
+ const mapMatch = normalized.match(/^map\[(\w+)\](.+)$/);
437
+ if (mapMatch) {
438
+ return {
439
+ type: 'object',
440
+ additionalProperties: this.parseGoType(mapMatch[2])
441
+ };
442
+ }
443
+
444
+ // interface{} or any -> any type
445
+ if (normalized === 'interface{}' || normalized === 'any') {
446
+ return { type: 'string' };
447
+ }
448
+
449
+ // Struct reference -> object
450
+ return { type: 'object' };
451
+ }
452
+
453
+ // ===================== Rust Type Parsing =====================
454
+
455
+ private isRustType(type: string): boolean {
456
+ const rustIndicators = ['String', '&str', 'i32', 'i64', 'u32', 'u64', 'f32', 'f64',
457
+ 'bool', 'Vec<', 'HashMap<', 'Option<', 'Result<',
458
+ 'Box<', '&', '\''];
459
+ return rustIndicators.some(i => type.includes(i));
460
+ }
461
+
462
+ private parseRustType(type: string): JsonSchemaProperty {
463
+ const normalized = type.trim();
464
+
465
+ // References: &T, &mut T -> T
466
+ if (normalized.startsWith('&')) {
467
+ const inner = normalized.replace(/^&(?:mut\s+)?/, '');
468
+ return this.parseRustType(inner);
469
+ }
470
+
471
+ // Primitives
472
+ if (normalized === 'String' || normalized === '&str') return { type: 'string' };
473
+ if (normalized.match(/^[iu]\d+$/) || normalized === 'usize' || normalized === 'isize') {
474
+ return { type: 'integer' };
475
+ }
476
+ if (normalized.match(/^f\d+$/)) return { type: 'number' };
477
+ if (normalized === 'bool') return { type: 'boolean' };
478
+ if (normalized === '()') return { type: 'null' };
479
+
480
+ // Option<T> -> nullable T
481
+ const optionMatch = normalized.match(/^Option<(.+)>$/);
482
+ if (optionMatch) {
483
+ const baseSchema = this.parseRustType(optionMatch[1]);
484
+ baseSchema.type = [baseSchema.type as string, 'null'];
485
+ return baseSchema;
486
+ }
487
+
488
+ // Vec<T> -> array
489
+ const vecMatch = normalized.match(/^Vec<(.+)>$/);
490
+ if (vecMatch) {
491
+ return {
492
+ type: 'array',
493
+ items: this.parseRustType(vecMatch[1])
494
+ };
495
+ }
496
+
497
+ // HashMap<K, V> -> object
498
+ const hashMapMatch = normalized.match(/^(?:HashMap|BTreeMap)<String,\s*(.+)>$/);
499
+ if (hashMapMatch) {
500
+ return {
501
+ type: 'object',
502
+ additionalProperties: this.parseRustType(hashMapMatch[1])
503
+ };
504
+ }
505
+
506
+ // Result<T, E> -> T (ignore error type for schema)
507
+ const resultMatch = normalized.match(/^Result<(.+?),/);
508
+ if (resultMatch) {
509
+ return this.parseRustType(resultMatch[1]);
510
+ }
511
+
512
+ // Box<T>, Rc<T>, Arc<T> -> T
513
+ const boxMatch = normalized.match(/^(?:Box|Rc|Arc)<(.+)>$/);
514
+ if (boxMatch) {
515
+ return this.parseRustType(boxMatch[1]);
516
+ }
517
+
518
+ // Struct reference -> object
519
+ return { type: 'object' };
520
+ }
521
+
522
+ // ===================== Struct/Interface Parsing =====================
523
+
524
+ /**
525
+ * Parse TypeScript interface/type definition to JSON Schema
526
+ */
527
+ parseTypeScriptInterface(code: string): GeneratedSchema | null {
528
+ const interfaceMatch = code.match(/(?:interface|type)\s+\w+\s*(?:=\s*)?\{([\s\S]+)\}/);
529
+ if (!interfaceMatch) return null;
530
+
531
+ const body = interfaceMatch[1];
532
+ const properties: Record<string, JsonSchemaProperty> = {};
533
+ const required: string[] = [];
534
+
535
+ // Match property definitions
536
+ const propPattern = /(\w+)(\??):\s*([^;]+);?/g;
537
+ let match;
538
+
539
+ while ((match = propPattern.exec(body)) !== null) {
540
+ const [, name, optional, type] = match;
541
+ properties[name] = this.parseTypeScriptType(type.trim());
542
+ if (!optional) {
543
+ required.push(name);
544
+ }
545
+ }
546
+
547
+ return { type: 'object', properties, required };
548
+ }
549
+
550
+ /**
551
+ * Parse Python TypedDict or dataclass to JSON Schema
552
+ */
553
+ parsePythonClass(code: string): GeneratedSchema | null {
554
+ const properties: Record<string, JsonSchemaProperty> = {};
555
+ const required: string[] = [];
556
+
557
+ // Match field definitions: name: Type or name: Type = default
558
+ const fieldPattern = /(\w+):\s*([^=\n]+)(?:\s*=\s*([^\n]+))?/g;
559
+ let match;
560
+
561
+ while ((match = fieldPattern.exec(code)) !== null) {
562
+ const [, name, type, defaultVal] = match;
563
+ if (name === 'class' || name === 'def') continue;
564
+
565
+ properties[name] = this.parsePythonType(type.trim());
566
+ if (!defaultVal && !type.includes('Optional')) {
567
+ required.push(name);
568
+ }
569
+ }
570
+
571
+ if (Object.keys(properties).length === 0) return null;
572
+
573
+ return { type: 'object', properties, required };
574
+ }
575
+
576
+ /**
577
+ * Parse Go struct to JSON Schema
578
+ */
579
+ parseGoStruct(code: string): GeneratedSchema | null {
580
+ const structMatch = code.match(/type\s+\w+\s+struct\s*\{([\s\S]+?)\}/);
581
+ if (!structMatch) return null;
582
+
583
+ const body = structMatch[1];
584
+ const properties: Record<string, JsonSchemaProperty> = {};
585
+ const required: string[] = [];
586
+
587
+ // Match field definitions: Name Type `json:"name"`
588
+ const fieldPattern = /(\w+)\s+([^\s`]+)(?:\s+`json:"([^"]+)"`)?/g;
589
+ let match;
590
+
591
+ while ((match = fieldPattern.exec(body)) !== null) {
592
+ const [, fieldName, type, jsonTag] = match;
593
+ const name = jsonTag?.split(',')[0] || this.camelToSnake(fieldName);
594
+ const isOptional = jsonTag?.includes('omitempty');
595
+
596
+ properties[name] = this.parseGoType(type.trim());
597
+ if (!isOptional && !type.startsWith('*')) {
598
+ required.push(name);
599
+ }
600
+ }
601
+
602
+ if (Object.keys(properties).length === 0) return null;
603
+
604
+ return { type: 'object', properties, required };
605
+ }
606
+
607
+ /**
608
+ * Parse Rust struct to JSON Schema
609
+ */
610
+ parseRustStruct(code: string): GeneratedSchema | null {
611
+ const structMatch = code.match(/struct\s+\w+\s*\{([\s\S]+?)\}/);
612
+ if (!structMatch) return null;
613
+
614
+ const body = structMatch[1];
615
+ const properties: Record<string, JsonSchemaProperty> = {};
616
+ const required: string[] = [];
617
+
618
+ // Match field definitions: name: Type,
619
+ const fieldPattern = /(\w+):\s*([^,\n]+)/g;
620
+ let match;
621
+
622
+ while ((match = fieldPattern.exec(body)) !== null) {
623
+ const [, name, type] = match;
624
+ properties[name] = this.parseRustType(type.trim());
625
+ if (!type.includes('Option<')) {
626
+ required.push(name);
627
+ }
628
+ }
629
+
630
+ if (Object.keys(properties).length === 0) return null;
631
+
632
+ return { type: 'object', properties, required };
633
+ }
634
+
635
+ private camelToSnake(str: string): string {
636
+ return str.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
637
+ }
638
+ }