@brainfish-ai/devdoc 0.1.41 → 0.1.43

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 (400) hide show
  1. package/ai-agents/.claude/skills/bootstrap-docs/SKILL.md +710 -79
  2. package/ai-agents/.claude/skills/check-docs/SKILL.md +83 -8
  3. package/ai-agents/.claude/skills/create-doc/SKILL.md +267 -55
  4. package/ai-agents/.claude/skills/update-doc/SKILL.md +162 -63
  5. package/ai-agents/.cursor/rules/devdoc-bootstrap.mdc +145 -15
  6. package/ai-agents/.cursor/rules/devdoc-create.mdc +108 -57
  7. package/ai-agents/.cursor/rules/devdoc-update.mdc +93 -70
  8. package/ai-agents/.cursor/rules/devdoc.mdc +21 -0
  9. package/ai-agents/schemas/docs.schema.json +332 -0
  10. package/ai-agents/schemas/theme.schema.json +243 -0
  11. package/dist/cli/commands/create.js +4 -9
  12. package/dist/cli/commands/deploy.js +50 -25
  13. package/dist/cli/commands/dev.js +19 -10
  14. package/package.json +3 -2
  15. package/renderer/app/api/assets/[...path]/route.js +108 -0
  16. package/renderer/app/api/assets/route.js +114 -0
  17. package/renderer/app/api/assets/upload/route.js +163 -0
  18. package/renderer/app/api/auth-schemes/route.js +58 -0
  19. package/renderer/app/api/chat/route.js +759 -0
  20. package/renderer/app/api/codegen/route.js +52 -0
  21. package/renderer/app/api/collections/route.js +675 -0
  22. package/renderer/app/api/debug/route.js +47 -0
  23. package/renderer/app/api/deploy/route.js +199 -0
  24. package/renderer/app/api/device/route.js +36 -0
  25. package/renderer/app/api/docs/route.js +205 -0
  26. package/renderer/app/api/domains/add/route.js +121 -0
  27. package/renderer/app/api/domains/lookup/route.js +43 -0
  28. package/renderer/app/api/domains/remove/route.js +89 -0
  29. package/renderer/app/api/domains/status/route.js +140 -0
  30. package/renderer/app/api/domains/verify/route.js +168 -0
  31. package/renderer/app/api/keys/regenerate/route.js +71 -0
  32. package/renderer/app/api/local-assets/[...path]/route.js +108 -0
  33. package/renderer/app/api/openapi-spec/route.js +73 -0
  34. package/renderer/app/api/projects/[slug]/route.js +129 -0
  35. package/renderer/app/api/projects/[slug]/stats/route.js +80 -0
  36. package/renderer/app/api/projects/register/route.js +176 -0
  37. package/renderer/app/api/proxy/route.js +139 -0
  38. package/renderer/app/api/proxy-stream/route.js +156 -0
  39. package/renderer/app/api/redirects/route.js +35 -0
  40. package/renderer/app/api/schema/route.js +85 -0
  41. package/renderer/app/api/subdomains/check/route.js +158 -0
  42. package/renderer/app/api/suggestions/route.js +175 -0
  43. package/renderer/app/layout.js +47 -0
  44. package/renderer/app/llms-full.txt/route.js +257 -0
  45. package/renderer/app/llms.txt/route.js +219 -0
  46. package/renderer/app/page.js +12 -0
  47. package/renderer/app/robots.txt/route.js +66 -0
  48. package/renderer/app/sitemap.xml/route.js +145 -0
  49. package/renderer/components/docs/index.js +8 -0
  50. package/renderer/components/docs/mdx/accordion.js +113 -0
  51. package/renderer/components/docs/mdx/badge.js +72 -0
  52. package/renderer/components/docs/mdx/callouts.js +137 -0
  53. package/renderer/components/docs/mdx/cards.js +175 -0
  54. package/renderer/components/docs/mdx/changelog.js +100 -0
  55. package/renderer/components/docs/mdx/code-block.js +147 -0
  56. package/renderer/components/docs/mdx/code-group.js +287 -0
  57. package/renderer/components/docs/mdx/file-embeds.js +82 -0
  58. package/renderer/components/docs/mdx/frame.js +59 -0
  59. package/renderer/components/docs/mdx/highlight.js +90 -0
  60. package/renderer/components/docs/mdx/iframe.js +69 -0
  61. package/renderer/components/docs/mdx/image.js +135 -0
  62. package/renderer/components/docs/mdx/index.js +134 -0
  63. package/renderer/components/docs/mdx/landing.js +315 -0
  64. package/renderer/components/docs/mdx/mermaid.js +212 -0
  65. package/renderer/components/docs/mdx/param-field.js +112 -0
  66. package/renderer/components/docs/mdx/steps.js +74 -0
  67. package/renderer/components/docs/mdx/tabs.js +50 -0
  68. package/renderer/components/docs/mdx-renderer.js +77 -0
  69. package/renderer/components/docs/navigation/breadcrumbs.js +64 -0
  70. package/renderer/components/docs/navigation/index.js +6 -0
  71. package/renderer/components/docs/navigation/page-nav.js +57 -0
  72. package/renderer/components/docs/navigation/sidebar.js +375 -0
  73. package/renderer/components/docs/navigation/toc.js +89 -0
  74. package/renderer/components/docs/notice.js +77 -0
  75. package/renderer/components/docs-header.js +202 -0
  76. package/renderer/components/docs-viewer/agent/agent-chat.js +1930 -0
  77. package/renderer/components/docs-viewer/agent/cards/debug-context-card.js +107 -0
  78. package/renderer/components/docs-viewer/agent/cards/endpoint-context-card.js +57 -0
  79. package/renderer/components/docs-viewer/agent/cards/index.js +45 -0
  80. package/renderer/components/docs-viewer/agent/cards/response-options-card.js +154 -0
  81. package/renderer/components/docs-viewer/agent/cards/types.js +22 -0
  82. package/renderer/components/docs-viewer/agent/chat-message.js +2 -0
  83. package/renderer/components/docs-viewer/agent/index.js +4 -0
  84. package/renderer/components/docs-viewer/agent/messages/assistant-message.js +108 -0
  85. package/renderer/components/docs-viewer/agent/messages/chat-message.js +34 -0
  86. package/renderer/components/docs-viewer/agent/messages/index.js +6 -0
  87. package/renderer/components/docs-viewer/agent/messages/tool-call-display.js +1065 -0
  88. package/renderer/components/docs-viewer/agent/messages/types.js +2 -0
  89. package/renderer/components/docs-viewer/agent/messages/typing-indicator.js +26 -0
  90. package/renderer/components/docs-viewer/agent/messages/user-message.js +37 -0
  91. package/renderer/components/docs-viewer/code-editor/{index.tsx → index.js} +1 -1
  92. package/renderer/components/docs-viewer/code-editor/notes-mode.js +1338 -0
  93. package/renderer/components/docs-viewer/content/changelog-page.js +297 -0
  94. package/renderer/components/docs-viewer/content/doc-page.js +264 -0
  95. package/renderer/components/docs-viewer/content/documentation-viewer.js +14 -0
  96. package/renderer/components/docs-viewer/content/index.js +29 -0
  97. package/renderer/components/docs-viewer/content/not-found-page.js +300 -0
  98. package/renderer/components/docs-viewer/content/request-details.js +528 -0
  99. package/renderer/components/docs-viewer/content/sections/auth.js +108 -0
  100. package/renderer/components/docs-viewer/content/sections/body.js +80 -0
  101. package/renderer/components/docs-viewer/content/sections/headers.js +64 -0
  102. package/renderer/components/docs-viewer/content/sections/overview.js +56 -0
  103. package/renderer/components/docs-viewer/content/sections/parameters.js +64 -0
  104. package/renderer/components/docs-viewer/content/sections/responses.js +91 -0
  105. package/renderer/components/docs-viewer/global-auth-modal.js +427 -0
  106. package/renderer/components/docs-viewer/index.js +1552 -0
  107. package/renderer/components/docs-viewer/playground/auth-editor.js +418 -0
  108. package/renderer/components/docs-viewer/playground/body-editor.js +240 -0
  109. package/renderer/components/docs-viewer/playground/code-editor.js +135 -0
  110. package/renderer/components/docs-viewer/playground/code-snippet.js +393 -0
  111. package/renderer/components/docs-viewer/playground/graphql-playground.js +734 -0
  112. package/renderer/components/docs-viewer/playground/index.js +682 -0
  113. package/renderer/components/docs-viewer/playground/key-value-editor.js +317 -0
  114. package/renderer/components/docs-viewer/playground/method-selector.js +65 -0
  115. package/renderer/components/docs-viewer/playground/request-builder.js +181 -0
  116. package/renderer/components/docs-viewer/playground/request-tabs.js +240 -0
  117. package/renderer/components/docs-viewer/playground/response-cards/idle-card.js +42 -0
  118. package/renderer/components/docs-viewer/playground/response-cards/index.js +72 -0
  119. package/renderer/components/docs-viewer/playground/response-cards/loading-card.js +24 -0
  120. package/renderer/components/docs-viewer/playground/response-cards/network-error-card.js +28 -0
  121. package/renderer/components/docs-viewer/playground/response-cards/response-body-card.js +308 -0
  122. package/renderer/components/docs-viewer/playground/response-cards/types.js +9 -0
  123. package/renderer/components/docs-viewer/playground/response-viewer.js +18 -0
  124. package/renderer/components/docs-viewer/search/index.js +2 -0
  125. package/renderer/components/docs-viewer/search/search-dialog.js +367 -0
  126. package/renderer/components/docs-viewer/search/use-search.js +89 -0
  127. package/renderer/components/docs-viewer/shared/markdown-renderer.js +423 -0
  128. package/renderer/components/docs-viewer/shared/method-badge.js +23 -0
  129. package/renderer/components/docs-viewer/shared/schema-viewer.js +321 -0
  130. package/renderer/components/docs-viewer/sidebar/collection-tree.js +222 -0
  131. package/renderer/components/docs-viewer/sidebar/endpoint-options.js +512 -0
  132. package/renderer/components/docs-viewer/sidebar/index.js +196 -0
  133. package/renderer/components/docs-viewer/sidebar/right-sidebar.js +163 -0
  134. package/renderer/components/docs-viewer/sidebar/sidebar-group.js +87 -0
  135. package/renderer/components/docs-viewer/sidebar/sidebar-item.js +172 -0
  136. package/renderer/components/docs-viewer/sidebar/sidebar-section.js +31 -0
  137. package/renderer/components/theme-provider.js +10 -0
  138. package/renderer/components/theme-toggle.js +106 -0
  139. package/renderer/components/ui/badge.js +29 -0
  140. package/renderer/components/ui/button.js +40 -0
  141. package/renderer/components/ui/dialog.js +50 -0
  142. package/renderer/components/ui/dropdown-menu.js +143 -0
  143. package/renderer/components/ui/input.js +12 -0
  144. package/renderer/components/ui/label.js +13 -0
  145. package/renderer/components/ui/navigation-menu.js +83 -0
  146. package/renderer/components/ui/select.js +116 -0
  147. package/renderer/components/ui/spinner.js +92 -0
  148. package/renderer/components/ui/tabs.js +34 -0
  149. package/renderer/components/ui/tooltip.js +43 -0
  150. package/renderer/hooks/use-code-copy.js +76 -0
  151. package/renderer/hooks/use-openapi-title.js +33 -0
  152. package/renderer/lib/api-docs/agent/index.js +4 -0
  153. package/renderer/lib/api-docs/agent/indexer.js +254 -0
  154. package/renderer/lib/api-docs/agent/spec-summary.js +227 -0
  155. package/renderer/lib/api-docs/agent/types.js +5 -0
  156. package/renderer/lib/api-docs/auth/auth-context.js +157 -0
  157. package/renderer/lib/api-docs/auth/auth-storage.js +66 -0
  158. package/renderer/lib/api-docs/auth/crypto.js +64 -0
  159. package/renderer/lib/api-docs/auth/index.js +3 -0
  160. package/renderer/lib/api-docs/code-editor/db.js +145 -0
  161. package/renderer/lib/api-docs/code-editor/hooks.js +254 -0
  162. package/renderer/lib/api-docs/code-editor/{index.ts → index.js} +3 -4
  163. package/renderer/lib/api-docs/code-editor/mode-context.js +154 -0
  164. package/renderer/lib/api-docs/code-editor/types.js +53 -0
  165. package/renderer/lib/api-docs/codegen/definitions.js +258 -0
  166. package/renderer/lib/api-docs/codegen/har.js +171 -0
  167. package/renderer/lib/api-docs/codegen/index.js +118 -0
  168. package/renderer/lib/api-docs/factories.js +136 -0
  169. package/renderer/lib/api-docs/{index.ts → index.js} +5 -10
  170. package/renderer/lib/api-docs/mobile-context.js +79 -0
  171. package/renderer/lib/api-docs/navigation-context.js +62 -0
  172. package/renderer/lib/api-docs/parsers/graphql/index.js +50 -0
  173. package/renderer/lib/api-docs/parsers/graphql/parser.js +350 -0
  174. package/renderer/lib/api-docs/parsers/graphql/transformer.js +215 -0
  175. package/renderer/lib/api-docs/parsers/graphql/types.js +46 -0
  176. package/renderer/lib/api-docs/parsers/openapi/dereferencer.js +43 -0
  177. package/renderer/lib/api-docs/parsers/openapi/extractors/auth.js +486 -0
  178. package/renderer/lib/api-docs/parsers/openapi/extractors/body.js +295 -0
  179. package/renderer/lib/api-docs/parsers/openapi/extractors/index.js +132 -0
  180. package/renderer/lib/api-docs/parsers/openapi/index.js +127 -0
  181. package/renderer/lib/api-docs/parsers/openapi/transformer.js +192 -0
  182. package/renderer/lib/api-docs/parsers/openapi/validator.js +24 -0
  183. package/renderer/lib/api-docs/playground/context.js +65 -0
  184. package/renderer/lib/api-docs/playground/navigation-context.js +74 -0
  185. package/renderer/lib/api-docs/playground/request-builder.js +163 -0
  186. package/renderer/lib/api-docs/playground/request-runner.js +224 -0
  187. package/renderer/lib/api-docs/playground/types.js +5 -0
  188. package/renderer/lib/api-docs/types.js +23 -0
  189. package/renderer/lib/api-docs/utils.js +212 -0
  190. package/renderer/lib/cache.js +157 -0
  191. package/renderer/lib/docs/config/domain-schema.js +161 -0
  192. package/renderer/lib/docs/config/index.js +5 -0
  193. package/renderer/lib/docs/config/loader.js +113 -0
  194. package/renderer/lib/docs/config/schema.js +269 -0
  195. package/renderer/lib/docs/index.js +8 -0
  196. package/renderer/lib/docs/mdx/compiler.js +128 -0
  197. package/renderer/lib/docs/mdx/frontmatter.js +73 -0
  198. package/renderer/lib/docs/mdx/index.js +8 -0
  199. package/renderer/lib/docs/navigation/generator.js +269 -0
  200. package/renderer/lib/docs/navigation/index.js +4 -0
  201. package/renderer/lib/docs/navigation/types.js +9 -0
  202. package/renderer/lib/docs-navigation-context.js +40 -0
  203. package/renderer/lib/multi-tenant/context.js +80 -0
  204. package/renderer/lib/storage/blob.js +767 -0
  205. package/renderer/lib/utils/icons.js +30 -0
  206. package/renderer/lib/utils.js +5 -0
  207. package/renderer/next.config.js +62 -0
  208. package/renderer/tsconfig.json +23 -5
  209. package/renderer/app/api/assets/[...path]/route.ts +0 -123
  210. package/renderer/app/api/assets/route.ts +0 -124
  211. package/renderer/app/api/assets/upload/route.ts +0 -177
  212. package/renderer/app/api/auth-schemes/route.ts +0 -77
  213. package/renderer/app/api/chat/route.ts +0 -858
  214. package/renderer/app/api/codegen/route.ts +0 -72
  215. package/renderer/app/api/collections/route.ts +0 -1002
  216. package/renderer/app/api/debug/route.ts +0 -53
  217. package/renderer/app/api/deploy/route.ts +0 -234
  218. package/renderer/app/api/device/route.ts +0 -42
  219. package/renderer/app/api/docs/route.ts +0 -201
  220. package/renderer/app/api/domains/add/route.ts +0 -132
  221. package/renderer/app/api/domains/lookup/route.ts +0 -43
  222. package/renderer/app/api/domains/remove/route.ts +0 -100
  223. package/renderer/app/api/domains/status/route.ts +0 -158
  224. package/renderer/app/api/domains/verify/route.ts +0 -181
  225. package/renderer/app/api/keys/regenerate/route.ts +0 -80
  226. package/renderer/app/api/local-assets/[...path]/route.ts +0 -122
  227. package/renderer/app/api/openapi-spec/route.ts +0 -151
  228. package/renderer/app/api/projects/[slug]/route.ts +0 -153
  229. package/renderer/app/api/projects/[slug]/stats/route.ts +0 -96
  230. package/renderer/app/api/projects/register/route.ts +0 -152
  231. package/renderer/app/api/proxy/route.ts +0 -149
  232. package/renderer/app/api/proxy-stream/route.ts +0 -168
  233. package/renderer/app/api/redirects/route.ts +0 -47
  234. package/renderer/app/api/schema/route.ts +0 -73
  235. package/renderer/app/api/subdomains/check/route.ts +0 -172
  236. package/renderer/app/api/suggestions/route.ts +0 -144
  237. package/renderer/app/layout.tsx +0 -54
  238. package/renderer/app/llms-full.txt/route.ts +0 -346
  239. package/renderer/app/llms.txt/route.ts +0 -279
  240. package/renderer/app/page.tsx +0 -14
  241. package/renderer/app/robots.txt/route.ts +0 -84
  242. package/renderer/app/sitemap.xml/route.ts +0 -199
  243. package/renderer/components/docs/index.ts +0 -12
  244. package/renderer/components/docs/mdx/accordion.tsx +0 -169
  245. package/renderer/components/docs/mdx/badge.tsx +0 -132
  246. package/renderer/components/docs/mdx/callouts.tsx +0 -154
  247. package/renderer/components/docs/mdx/cards.tsx +0 -241
  248. package/renderer/components/docs/mdx/changelog.tsx +0 -120
  249. package/renderer/components/docs/mdx/code-block.tsx +0 -186
  250. package/renderer/components/docs/mdx/code-group.tsx +0 -421
  251. package/renderer/components/docs/mdx/file-embeds.tsx +0 -105
  252. package/renderer/components/docs/mdx/frame.tsx +0 -112
  253. package/renderer/components/docs/mdx/highlight.tsx +0 -151
  254. package/renderer/components/docs/mdx/iframe.tsx +0 -134
  255. package/renderer/components/docs/mdx/image.tsx +0 -235
  256. package/renderer/components/docs/mdx/index.ts +0 -237
  257. package/renderer/components/docs/mdx/landing.tsx +0 -684
  258. package/renderer/components/docs/mdx/mermaid.tsx +0 -240
  259. package/renderer/components/docs/mdx/param-field.tsx +0 -200
  260. package/renderer/components/docs/mdx/steps.tsx +0 -113
  261. package/renderer/components/docs/mdx/tabs.tsx +0 -86
  262. package/renderer/components/docs/mdx-renderer.tsx +0 -100
  263. package/renderer/components/docs/navigation/breadcrumbs.tsx +0 -76
  264. package/renderer/components/docs/navigation/index.ts +0 -8
  265. package/renderer/components/docs/navigation/page-nav.tsx +0 -64
  266. package/renderer/components/docs/navigation/sidebar.tsx +0 -515
  267. package/renderer/components/docs/navigation/toc.tsx +0 -113
  268. package/renderer/components/docs/notice.tsx +0 -105
  269. package/renderer/components/docs-header.tsx +0 -278
  270. package/renderer/components/docs-viewer/agent/agent-chat.tsx +0 -2076
  271. package/renderer/components/docs-viewer/agent/cards/debug-context-card.tsx +0 -90
  272. package/renderer/components/docs-viewer/agent/cards/endpoint-context-card.tsx +0 -49
  273. package/renderer/components/docs-viewer/agent/cards/index.tsx +0 -50
  274. package/renderer/components/docs-viewer/agent/cards/response-options-card.tsx +0 -212
  275. package/renderer/components/docs-viewer/agent/cards/types.ts +0 -84
  276. package/renderer/components/docs-viewer/agent/chat-message.tsx +0 -17
  277. package/renderer/components/docs-viewer/agent/index.tsx +0 -6
  278. package/renderer/components/docs-viewer/agent/messages/assistant-message.tsx +0 -119
  279. package/renderer/components/docs-viewer/agent/messages/chat-message.tsx +0 -46
  280. package/renderer/components/docs-viewer/agent/messages/index.ts +0 -17
  281. package/renderer/components/docs-viewer/agent/messages/tool-call-display.tsx +0 -721
  282. package/renderer/components/docs-viewer/agent/messages/types.ts +0 -61
  283. package/renderer/components/docs-viewer/agent/messages/typing-indicator.tsx +0 -24
  284. package/renderer/components/docs-viewer/agent/messages/user-message.tsx +0 -51
  285. package/renderer/components/docs-viewer/code-editor/notes-mode.tsx +0 -1283
  286. package/renderer/components/docs-viewer/content/changelog-page.tsx +0 -331
  287. package/renderer/components/docs-viewer/content/doc-page.tsx +0 -367
  288. package/renderer/components/docs-viewer/content/documentation-viewer.tsx +0 -17
  289. package/renderer/components/docs-viewer/content/index.tsx +0 -29
  290. package/renderer/components/docs-viewer/content/not-found-page.tsx +0 -330
  291. package/renderer/components/docs-viewer/content/request-details.tsx +0 -330
  292. package/renderer/components/docs-viewer/content/sections/auth.tsx +0 -69
  293. package/renderer/components/docs-viewer/content/sections/body.tsx +0 -66
  294. package/renderer/components/docs-viewer/content/sections/headers.tsx +0 -43
  295. package/renderer/components/docs-viewer/content/sections/overview.tsx +0 -40
  296. package/renderer/components/docs-viewer/content/sections/parameters.tsx +0 -43
  297. package/renderer/components/docs-viewer/content/sections/responses.tsx +0 -87
  298. package/renderer/components/docs-viewer/global-auth-modal.tsx +0 -352
  299. package/renderer/components/docs-viewer/index.tsx +0 -1662
  300. package/renderer/components/docs-viewer/playground/auth-editor.tsx +0 -280
  301. package/renderer/components/docs-viewer/playground/body-editor.tsx +0 -221
  302. package/renderer/components/docs-viewer/playground/code-editor.tsx +0 -224
  303. package/renderer/components/docs-viewer/playground/code-snippet.tsx +0 -387
  304. package/renderer/components/docs-viewer/playground/graphql-playground.tsx +0 -745
  305. package/renderer/components/docs-viewer/playground/index.tsx +0 -671
  306. package/renderer/components/docs-viewer/playground/key-value-editor.tsx +0 -261
  307. package/renderer/components/docs-viewer/playground/method-selector.tsx +0 -60
  308. package/renderer/components/docs-viewer/playground/request-builder.tsx +0 -179
  309. package/renderer/components/docs-viewer/playground/request-tabs.tsx +0 -237
  310. package/renderer/components/docs-viewer/playground/response-cards/idle-card.tsx +0 -21
  311. package/renderer/components/docs-viewer/playground/response-cards/index.tsx +0 -93
  312. package/renderer/components/docs-viewer/playground/response-cards/loading-card.tsx +0 -16
  313. package/renderer/components/docs-viewer/playground/response-cards/network-error-card.tsx +0 -23
  314. package/renderer/components/docs-viewer/playground/response-cards/response-body-card.tsx +0 -268
  315. package/renderer/components/docs-viewer/playground/response-cards/types.ts +0 -82
  316. package/renderer/components/docs-viewer/playground/response-viewer.tsx +0 -43
  317. package/renderer/components/docs-viewer/search/index.ts +0 -2
  318. package/renderer/components/docs-viewer/search/search-dialog.tsx +0 -331
  319. package/renderer/components/docs-viewer/search/use-search.ts +0 -117
  320. package/renderer/components/docs-viewer/shared/markdown-renderer.tsx +0 -431
  321. package/renderer/components/docs-viewer/shared/method-badge.tsx +0 -41
  322. package/renderer/components/docs-viewer/shared/schema-viewer.tsx +0 -349
  323. package/renderer/components/docs-viewer/sidebar/collection-tree.tsx +0 -259
  324. package/renderer/components/docs-viewer/sidebar/endpoint-options.tsx +0 -316
  325. package/renderer/components/docs-viewer/sidebar/index.tsx +0 -282
  326. package/renderer/components/docs-viewer/sidebar/right-sidebar.tsx +0 -202
  327. package/renderer/components/docs-viewer/sidebar/sidebar-group.tsx +0 -118
  328. package/renderer/components/docs-viewer/sidebar/sidebar-item.tsx +0 -212
  329. package/renderer/components/docs-viewer/sidebar/sidebar-section.tsx +0 -38
  330. package/renderer/components/theme-provider.tsx +0 -11
  331. package/renderer/components/theme-toggle.tsx +0 -76
  332. package/renderer/components/ui/badge.tsx +0 -46
  333. package/renderer/components/ui/button.tsx +0 -59
  334. package/renderer/components/ui/dialog.tsx +0 -118
  335. package/renderer/components/ui/dropdown-menu.tsx +0 -257
  336. package/renderer/components/ui/input.tsx +0 -21
  337. package/renderer/components/ui/label.tsx +0 -24
  338. package/renderer/components/ui/navigation-menu.tsx +0 -168
  339. package/renderer/components/ui/select.tsx +0 -190
  340. package/renderer/components/ui/spinner.tsx +0 -114
  341. package/renderer/components/ui/tabs.tsx +0 -66
  342. package/renderer/components/ui/tooltip.tsx +0 -61
  343. package/renderer/hooks/use-code-copy.ts +0 -88
  344. package/renderer/hooks/use-openapi-title.ts +0 -44
  345. package/renderer/lib/api-docs/agent/index.ts +0 -6
  346. package/renderer/lib/api-docs/agent/indexer.ts +0 -323
  347. package/renderer/lib/api-docs/agent/spec-summary.ts +0 -335
  348. package/renderer/lib/api-docs/agent/types.ts +0 -116
  349. package/renderer/lib/api-docs/auth/auth-context.tsx +0 -225
  350. package/renderer/lib/api-docs/auth/auth-storage.ts +0 -87
  351. package/renderer/lib/api-docs/auth/crypto.ts +0 -89
  352. package/renderer/lib/api-docs/auth/index.ts +0 -4
  353. package/renderer/lib/api-docs/code-editor/db.ts +0 -164
  354. package/renderer/lib/api-docs/code-editor/hooks.ts +0 -266
  355. package/renderer/lib/api-docs/code-editor/mode-context.tsx +0 -207
  356. package/renderer/lib/api-docs/code-editor/types.ts +0 -105
  357. package/renderer/lib/api-docs/codegen/definitions.ts +0 -297
  358. package/renderer/lib/api-docs/codegen/har.ts +0 -251
  359. package/renderer/lib/api-docs/codegen/index.ts +0 -159
  360. package/renderer/lib/api-docs/factories.ts +0 -170
  361. package/renderer/lib/api-docs/mobile-context.tsx +0 -112
  362. package/renderer/lib/api-docs/navigation-context.tsx +0 -88
  363. package/renderer/lib/api-docs/parsers/graphql/README.md +0 -129
  364. package/renderer/lib/api-docs/parsers/graphql/index.ts +0 -91
  365. package/renderer/lib/api-docs/parsers/graphql/parser.ts +0 -491
  366. package/renderer/lib/api-docs/parsers/graphql/transformer.ts +0 -246
  367. package/renderer/lib/api-docs/parsers/graphql/types.ts +0 -283
  368. package/renderer/lib/api-docs/parsers/openapi/README.md +0 -32
  369. package/renderer/lib/api-docs/parsers/openapi/dereferencer.ts +0 -60
  370. package/renderer/lib/api-docs/parsers/openapi/extractors/auth.ts +0 -574
  371. package/renderer/lib/api-docs/parsers/openapi/extractors/body.ts +0 -403
  372. package/renderer/lib/api-docs/parsers/openapi/extractors/index.ts +0 -232
  373. package/renderer/lib/api-docs/parsers/openapi/index.ts +0 -171
  374. package/renderer/lib/api-docs/parsers/openapi/transformer.ts +0 -278
  375. package/renderer/lib/api-docs/parsers/openapi/validator.ts +0 -31
  376. package/renderer/lib/api-docs/playground/context.tsx +0 -107
  377. package/renderer/lib/api-docs/playground/navigation-context.tsx +0 -124
  378. package/renderer/lib/api-docs/playground/request-builder.ts +0 -223
  379. package/renderer/lib/api-docs/playground/request-runner.ts +0 -282
  380. package/renderer/lib/api-docs/playground/types.ts +0 -35
  381. package/renderer/lib/api-docs/types.ts +0 -269
  382. package/renderer/lib/api-docs/utils.ts +0 -311
  383. package/renderer/lib/cache.ts +0 -193
  384. package/renderer/lib/docs/config/domain-schema.ts +0 -260
  385. package/renderer/lib/docs/config/index.ts +0 -43
  386. package/renderer/lib/docs/config/loader.ts +0 -142
  387. package/renderer/lib/docs/config/schema.ts +0 -308
  388. package/renderer/lib/docs/index.ts +0 -12
  389. package/renderer/lib/docs/mdx/compiler.ts +0 -176
  390. package/renderer/lib/docs/mdx/frontmatter.ts +0 -91
  391. package/renderer/lib/docs/mdx/index.ts +0 -26
  392. package/renderer/lib/docs/navigation/generator.ts +0 -348
  393. package/renderer/lib/docs/navigation/index.ts +0 -12
  394. package/renderer/lib/docs/navigation/types.ts +0 -123
  395. package/renderer/lib/docs-navigation-context.tsx +0 -80
  396. package/renderer/lib/multi-tenant/context.ts +0 -105
  397. package/renderer/lib/storage/blob.ts +0 -1083
  398. package/renderer/lib/utils/icons.ts +0 -48
  399. package/renderer/lib/utils.ts +0 -6
  400. package/renderer/next.config.ts +0 -76
@@ -0,0 +1,47 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { headers } from 'next/headers';
3
+ import { getProjectContent, projectExists } from '@/lib/storage/blob';
4
+ /**
5
+ * Debug endpoint to check multi-tenant routing
6
+ *
7
+ * GET /api/debug?slug=acme-api-docs-i2rjwv
8
+ */ export async function GET(request) {
9
+ const headersList = await headers();
10
+ const { searchParams } = new URL(request.url);
11
+ const testSlug = searchParams.get('slug');
12
+ // Collect all headers
13
+ const allHeaders = {};
14
+ headersList.forEach((value, key)=>{
15
+ allHeaders[key] = value;
16
+ });
17
+ // Check for project header
18
+ const projectSlug = headersList.get('x-devdoc-project');
19
+ // Test Blob storage if slug provided
20
+ let blobTest = null;
21
+ if (testSlug) {
22
+ try {
23
+ const exists = await projectExists(testSlug);
24
+ const content = exists ? await getProjectContent(testSlug) : null;
25
+ blobTest = {
26
+ slug: testSlug,
27
+ exists,
28
+ hasContent: !!content,
29
+ filesCount: content?.files?.length || 0,
30
+ projectName: content?.name || null
31
+ };
32
+ } catch (error) {
33
+ blobTest = {
34
+ slug: testSlug,
35
+ error: String(error)
36
+ };
37
+ }
38
+ }
39
+ return NextResponse.json({
40
+ timestamp: new Date().toISOString(),
41
+ host: headersList.get('host'),
42
+ projectSlug,
43
+ hasBlobToken: !!process.env.BLOB_READ_WRITE_TOKEN,
44
+ blobTest,
45
+ headers: allHeaders
46
+ });
47
+ }
@@ -0,0 +1,199 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { storeProjectContent, updateProjectContent, projectExists, generateProjectSlug, generateApiKey, storeProjectApiKey, validateApiKey } from '@/lib/storage/blob';
3
+ import { getProjectUrl, isValidSlug } from '@/lib/multi-tenant/context';
4
+ /**
5
+ * Deploy API - receives content from CLI and stores in Vercel Blob
6
+ *
7
+ * POST /api/deploy
8
+ * Headers:
9
+ * Authorization: Bearer <api_key> // Required for updates
10
+ * Body: {
11
+ * name: string, // Project name from docs.json
12
+ * slug?: string, // Optional: existing slug for updates
13
+ * docsJson: object, // The docs.json configuration
14
+ * files: Array<{ // MDX and other content files
15
+ * path: string,
16
+ * content: string
17
+ * }>
18
+ * }
19
+ */ export async function POST(request) {
20
+ try {
21
+ const body = await request.json();
22
+ // Validate request body
23
+ const { name, slug: existingSlug, docsJson, files } = body;
24
+ if (!name || typeof name !== 'string') {
25
+ return NextResponse.json({
26
+ error: 'Missing or invalid project name'
27
+ }, {
28
+ status: 400
29
+ });
30
+ }
31
+ if (!docsJson || typeof docsJson !== 'object') {
32
+ return NextResponse.json({
33
+ error: 'Missing or invalid docs.json configuration'
34
+ }, {
35
+ status: 400
36
+ });
37
+ }
38
+ if (!files || !Array.isArray(files)) {
39
+ return NextResponse.json({
40
+ error: 'Missing or invalid files array'
41
+ }, {
42
+ status: 400
43
+ });
44
+ }
45
+ // Validate files structure
46
+ const validFiles = [];
47
+ for (const file of files){
48
+ if (!file.path || typeof file.path !== 'string') {
49
+ return NextResponse.json({
50
+ error: `Invalid file entry: missing path`
51
+ }, {
52
+ status: 400
53
+ });
54
+ }
55
+ if (typeof file.content !== 'string') {
56
+ return NextResponse.json({
57
+ error: `Invalid file entry for ${file.path}: content must be a string`
58
+ }, {
59
+ status: 400
60
+ });
61
+ }
62
+ validFiles.push({
63
+ path: file.path,
64
+ content: file.content
65
+ });
66
+ }
67
+ let slug;
68
+ let isUpdate = false;
69
+ let apiKey = null;
70
+ // Check if this is an update to an existing project
71
+ if (existingSlug) {
72
+ if (!isValidSlug(existingSlug)) {
73
+ return NextResponse.json({
74
+ error: 'Invalid project slug format'
75
+ }, {
76
+ status: 400
77
+ });
78
+ }
79
+ const exists = await projectExists(existingSlug);
80
+ if (!exists) {
81
+ return NextResponse.json({
82
+ error: `Project with slug "${existingSlug}" not found`
83
+ }, {
84
+ status: 404
85
+ });
86
+ }
87
+ // For updates, validate API key
88
+ const authHeader = request.headers.get('Authorization');
89
+ const providedKey = authHeader?.replace('Bearer ', '') || body.apiKey;
90
+ if (!providedKey) {
91
+ return NextResponse.json({
92
+ error: 'API key required for project updates. Provide via Authorization header or apiKey field.'
93
+ }, {
94
+ status: 401
95
+ });
96
+ }
97
+ const validatedSlug = await validateApiKey(providedKey);
98
+ if (validatedSlug !== existingSlug) {
99
+ return NextResponse.json({
100
+ error: 'Invalid API key for this project'
101
+ }, {
102
+ status: 403
103
+ });
104
+ }
105
+ slug = existingSlug;
106
+ isUpdate = true;
107
+ } else {
108
+ // Generate a new unique slug
109
+ slug = generateProjectSlug(name);
110
+ // Ensure it doesn't already exist (very unlikely but possible)
111
+ let attempts = 0;
112
+ while(await projectExists(slug) && attempts < 5){
113
+ slug = generateProjectSlug(name);
114
+ attempts++;
115
+ }
116
+ if (attempts >= 5) {
117
+ return NextResponse.json({
118
+ error: 'Failed to generate unique project slug'
119
+ }, {
120
+ status: 500
121
+ });
122
+ }
123
+ // Generate API key for new project
124
+ apiKey = generateApiKey();
125
+ }
126
+ // Store or update content
127
+ let result;
128
+ if (isUpdate) {
129
+ result = await updateProjectContent(slug, docsJson, validFiles);
130
+ } else {
131
+ result = await storeProjectContent(slug, name, docsJson, validFiles);
132
+ // Store the API key for new projects
133
+ if (apiKey) {
134
+ await storeProjectApiKey(slug, apiKey);
135
+ }
136
+ }
137
+ // Build response
138
+ const projectUrl = getProjectUrl(slug);
139
+ // For new projects, include the API key in response
140
+ const response = {
141
+ success: true,
142
+ slug,
143
+ url: projectUrl,
144
+ blobUrl: result.url,
145
+ isUpdate,
146
+ filesCount: validFiles.length
147
+ };
148
+ if (apiKey) {
149
+ response.apiKey = apiKey;
150
+ }
151
+ return NextResponse.json(response);
152
+ } catch (error) {
153
+ console.error('[Deploy API] Error:', error);
154
+ const message = error instanceof Error ? error.message : String(error);
155
+ // Provide clearer error messages for common issues
156
+ let userMessage = 'Deployment failed';
157
+ if (message.includes('blob already exists')) {
158
+ userMessage = 'Storage conflict - please try again';
159
+ } else if (message.includes('network') || message.includes('fetch')) {
160
+ userMessage = 'Network error connecting to storage';
161
+ } else if (message.includes('unauthorized') || message.includes('401')) {
162
+ userMessage = 'Authentication failed';
163
+ }
164
+ return NextResponse.json({
165
+ error: userMessage,
166
+ details: message
167
+ }, {
168
+ status: 500
169
+ });
170
+ }
171
+ }
172
+ /**
173
+ * GET /api/deploy - Check deployment status or get project info
174
+ */ export async function GET(request) {
175
+ const { searchParams } = new URL(request.url);
176
+ const slug = searchParams.get('slug');
177
+ if (!slug) {
178
+ return NextResponse.json({
179
+ error: 'Missing slug parameter'
180
+ }, {
181
+ status: 400
182
+ });
183
+ }
184
+ const exists = await projectExists(slug);
185
+ if (!exists) {
186
+ return NextResponse.json({
187
+ exists: false,
188
+ slug
189
+ }, {
190
+ status: 404
191
+ });
192
+ }
193
+ const projectUrl = getProjectUrl(slug);
194
+ return NextResponse.json({
195
+ exists: true,
196
+ slug,
197
+ url: projectUrl
198
+ });
199
+ }
@@ -0,0 +1,36 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { cookies } from 'next/headers';
3
+ const DEVICE_COOKIE_NAME = 'bf_device_id';
4
+ const COOKIE_MAX_AGE = 60 * 60 * 24 * 365 // 1 year
5
+ ;
6
+ /**
7
+ * Generate a unique device ID
8
+ * Uses crypto.randomUUID for secure random generation
9
+ */ function generateDeviceId() {
10
+ return crypto.randomUUID();
11
+ }
12
+ /**
13
+ * GET /api/device
14
+ * Returns or creates a unique device ID for the client
15
+ * The device ID is stored in an httpOnly cookie for security
16
+ */ export async function GET() {
17
+ const cookieStore = await cookies();
18
+ let deviceId = cookieStore.get(DEVICE_COOKIE_NAME)?.value;
19
+ // Generate new device ID if not exists
20
+ if (!deviceId) {
21
+ deviceId = generateDeviceId();
22
+ }
23
+ // Create response with device ID
24
+ const response = NextResponse.json({
25
+ deviceId
26
+ });
27
+ // Set/refresh the httpOnly cookie
28
+ response.cookies.set(DEVICE_COOKIE_NAME, deviceId, {
29
+ httpOnly: true,
30
+ secure: process.env.NODE_ENV === 'production',
31
+ sameSite: 'strict',
32
+ maxAge: COOKIE_MAX_AGE,
33
+ path: '/'
34
+ });
35
+ return response;
36
+ }
@@ -0,0 +1,205 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { readFileSync, existsSync } from 'fs';
3
+ import { join, isAbsolute } from 'path';
4
+ import matter from 'gray-matter';
5
+ import { serialize } from 'next-mdx-remote/serialize';
6
+ import remarkGfm from 'remark-gfm';
7
+ import rehypeSlug from 'rehype-slug';
8
+ // Note: rehype-pretty-code is dynamically imported to handle serverless environments
9
+ // where shiki may not be fully available
10
+ import { getProjectFile } from '@/lib/storage/blob';
11
+ const STARTER_PATH = process.env.STARTER_PATH || 'devdoc-docs';
12
+ // Helper to get content root - supports both relative and absolute paths
13
+ function getContentRoot() {
14
+ if (isAbsolute(STARTER_PATH)) {
15
+ return STARTER_PATH;
16
+ }
17
+ return join(process.cwd(), STARTER_PATH);
18
+ }
19
+ // Shiki theme options for syntax highlighting
20
+ const prettyCodeOptions = {
21
+ theme: 'github-dark',
22
+ keepBackground: true,
23
+ defaultLang: 'plaintext'
24
+ };
25
+ export async function GET(request) {
26
+ const searchParams = request.nextUrl.searchParams;
27
+ const slug = searchParams.get('slug');
28
+ const is404Request = searchParams.get('is404') === 'true';
29
+ if (!slug) {
30
+ return NextResponse.json({
31
+ error: 'Missing slug parameter'
32
+ }, {
33
+ status: 400
34
+ });
35
+ }
36
+ // Check for multi-tenant mode (project slug from middleware)
37
+ const projectSlug = request.headers.get('x-devdoc-project');
38
+ // If multi-tenant, fetch from Blob Storage
39
+ if (projectSlug && !projectSlug.startsWith('custom:')) {
40
+ return handleMultiTenantDocs(projectSlug, slug, is404Request);
41
+ }
42
+ try {
43
+ const starterDir = getContentRoot();
44
+ // Try .mdx then .md
45
+ let fullPath = join(starterDir, `${slug}.mdx`);
46
+ if (!existsSync(fullPath)) {
47
+ fullPath = join(starterDir, `${slug}.md`);
48
+ }
49
+ if (!existsSync(fullPath)) {
50
+ // For 404 page requests, return 404 status (not a server error)
51
+ // The client will handle showing the default 404 if custom doesn't exist
52
+ return NextResponse.json({
53
+ error: 'Page not found'
54
+ }, {
55
+ status: 404
56
+ });
57
+ }
58
+ const fileContent = readFileSync(fullPath, 'utf-8');
59
+ const { data: frontmatter, content } = matter(fileContent);
60
+ // Serialize MDX for client-side rendering
61
+ // Note: rehype-pretty-code is dynamically imported to handle cases where shiki
62
+ // may not be available in serverless environments
63
+ let mdxSource;
64
+ try {
65
+ // Try with syntax highlighting first
66
+ const rehypePrettyCodeModule = await import('rehype-pretty-code');
67
+ const rehypePrettyCode = rehypePrettyCodeModule.default;
68
+ mdxSource = await serialize(content, {
69
+ mdxOptions: {
70
+ remarkPlugins: [
71
+ remarkGfm
72
+ ],
73
+ rehypePlugins: [
74
+ rehypeSlug,
75
+ [
76
+ rehypePrettyCode,
77
+ prettyCodeOptions
78
+ ]
79
+ ]
80
+ },
81
+ parseFrontmatter: false
82
+ });
83
+ } catch (syntaxError) {
84
+ // Fallback: serialize without syntax highlighting if shiki is unavailable
85
+ console.warn('[Docs API] Syntax highlighting unavailable, using fallback:', syntaxError);
86
+ mdxSource = await serialize(content, {
87
+ mdxOptions: {
88
+ remarkPlugins: [
89
+ remarkGfm
90
+ ],
91
+ rehypePlugins: [
92
+ rehypeSlug
93
+ ]
94
+ },
95
+ parseFrontmatter: false
96
+ });
97
+ }
98
+ return NextResponse.json({
99
+ slug,
100
+ frontmatter: {
101
+ title: frontmatter.title || slug,
102
+ description: frontmatter.description,
103
+ icon: frontmatter.icon,
104
+ // Page mode and layout options
105
+ mode: frontmatter.mode || 'default',
106
+ hideHeader: frontmatter.hideHeader || false,
107
+ fullWidth: frontmatter.fullWidth || false,
108
+ background: frontmatter.background
109
+ },
110
+ mdxSource,
111
+ rawContent: content
112
+ });
113
+ } catch (error) {
114
+ console.error('[Docs API] Error loading page:', error);
115
+ return NextResponse.json({
116
+ error: 'Failed to load page',
117
+ details: error instanceof Error ? error.message : String(error)
118
+ }, {
119
+ status: 500
120
+ });
121
+ }
122
+ }
123
+ /**
124
+ * Handle multi-tenant docs request - fetch from Blob Storage
125
+ */ async function handleMultiTenantDocs(projectSlug, slug, is404Request = false) {
126
+ try {
127
+ // Try .mdx then .md
128
+ let fileContent = await getProjectFile(projectSlug, `${slug}.mdx`);
129
+ if (!fileContent) {
130
+ fileContent = await getProjectFile(projectSlug, `${slug}.md`);
131
+ }
132
+ if (!fileContent) {
133
+ // For 404 page requests, return 404 status
134
+ return NextResponse.json({
135
+ error: 'Page not found'
136
+ }, {
137
+ status: 404
138
+ });
139
+ }
140
+ const { data: frontmatter, content } = matter(fileContent);
141
+ // Serialize MDX for client-side rendering
142
+ // Note: rehype-pretty-code is dynamically imported to handle cases where shiki
143
+ // may not be available in serverless environments
144
+ let mdxSource;
145
+ try {
146
+ // Try with syntax highlighting first
147
+ const rehypePrettyCodeModule = await import('rehype-pretty-code');
148
+ const rehypePrettyCode = rehypePrettyCodeModule.default;
149
+ mdxSource = await serialize(content, {
150
+ mdxOptions: {
151
+ remarkPlugins: [
152
+ remarkGfm
153
+ ],
154
+ rehypePlugins: [
155
+ rehypeSlug,
156
+ [
157
+ rehypePrettyCode,
158
+ prettyCodeOptions
159
+ ]
160
+ ]
161
+ },
162
+ parseFrontmatter: false
163
+ });
164
+ } catch (syntaxError) {
165
+ // Fallback: serialize without syntax highlighting if shiki is unavailable
166
+ console.warn('[Docs API] Multi-tenant syntax highlighting unavailable:', syntaxError);
167
+ mdxSource = await serialize(content, {
168
+ mdxOptions: {
169
+ remarkPlugins: [
170
+ remarkGfm
171
+ ],
172
+ rehypePlugins: [
173
+ rehypeSlug
174
+ ]
175
+ },
176
+ parseFrontmatter: false
177
+ });
178
+ }
179
+ return NextResponse.json({
180
+ slug,
181
+ frontmatter: {
182
+ title: frontmatter.title || slug,
183
+ description: frontmatter.description,
184
+ icon: frontmatter.icon,
185
+ // Page mode and layout options
186
+ mode: frontmatter.mode || 'default',
187
+ hideHeader: frontmatter.hideHeader || false,
188
+ fullWidth: frontmatter.fullWidth || false,
189
+ background: frontmatter.background
190
+ },
191
+ mdxSource,
192
+ rawContent: content,
193
+ isMultiTenant: true,
194
+ projectSlug
195
+ });
196
+ } catch (error) {
197
+ console.error('[Docs API] Multi-tenant error:', error);
198
+ return NextResponse.json({
199
+ error: 'Failed to load page',
200
+ details: error instanceof Error ? error.message : String(error)
201
+ }, {
202
+ status: 500
203
+ });
204
+ }
205
+ }
@@ -0,0 +1,121 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { validateApiKey, addCustomDomain, isCustomDomainRegistered } from '@/lib/storage/blob';
3
+ import { isValidDomain, normalizeDomain, getDnsInstructions } from '@/lib/docs/config';
4
+ /**
5
+ * POST /api/domains/add
6
+ *
7
+ * Add a custom domain to a project.
8
+ * Each project can have ONE custom domain (free).
9
+ *
10
+ * Headers:
11
+ * Authorization: Bearer <api_key>
12
+ *
13
+ * Body:
14
+ * { customDomain: "docs.example.com" }
15
+ *
16
+ * Response:
17
+ * {
18
+ * success: true,
19
+ * domain: "docs.example.com",
20
+ * status: "pending",
21
+ * verification: {
22
+ * cname: { name: "docs", value: "cname.devdoc-dns.com" },
23
+ * txt: { name: "_devdoc-verify.docs.example.com", value: "devdoc-verify=xxx" }
24
+ * }
25
+ * }
26
+ */ export async function POST(request) {
27
+ try {
28
+ // Validate API key
29
+ const authHeader = request.headers.get('Authorization');
30
+ const apiKey = authHeader?.replace('Bearer ', '');
31
+ if (!apiKey) {
32
+ return NextResponse.json({
33
+ error: 'API key required. Provide via Authorization header.'
34
+ }, {
35
+ status: 401
36
+ });
37
+ }
38
+ const projectSlug = await validateApiKey(apiKey);
39
+ if (!projectSlug) {
40
+ return NextResponse.json({
41
+ error: 'Invalid API key'
42
+ }, {
43
+ status: 403
44
+ });
45
+ }
46
+ // Parse request body
47
+ const body = await request.json();
48
+ const { customDomain: rawDomain } = body;
49
+ if (!rawDomain || typeof rawDomain !== 'string') {
50
+ return NextResponse.json({
51
+ error: 'Missing customDomain in request body'
52
+ }, {
53
+ status: 400
54
+ });
55
+ }
56
+ // Normalize and validate domain
57
+ const customDomain = normalizeDomain(rawDomain);
58
+ const validation = isValidDomain(customDomain);
59
+ if (!validation.valid) {
60
+ return NextResponse.json({
61
+ error: validation.error
62
+ }, {
63
+ status: 400
64
+ });
65
+ }
66
+ // Check if domain is already registered
67
+ const isRegistered = await isCustomDomainRegistered(customDomain);
68
+ if (isRegistered) {
69
+ return NextResponse.json({
70
+ error: `Domain ${customDomain} is already registered to another project.`
71
+ }, {
72
+ status: 409
73
+ });
74
+ }
75
+ // Add the custom domain
76
+ const result = await addCustomDomain(projectSlug, customDomain);
77
+ if (!result.success) {
78
+ return NextResponse.json({
79
+ error: result.error
80
+ }, {
81
+ status: 400
82
+ });
83
+ }
84
+ // Get DNS instructions
85
+ const dnsInstructions = getDnsInstructions(customDomain);
86
+ // Add verification token to TXT record
87
+ dnsInstructions.txt.value = result.entry.verificationToken || '';
88
+ return NextResponse.json({
89
+ success: true,
90
+ domain: customDomain,
91
+ projectSlug,
92
+ status: result.entry.status,
93
+ verification: {
94
+ cname: dnsInstructions.cname,
95
+ txt: dnsInstructions.txt
96
+ },
97
+ instructions: [
98
+ 'Add the following DNS records to your domain:',
99
+ '',
100
+ `1. CNAME Record:`,
101
+ ` Name: ${dnsInstructions.cname.name}`,
102
+ ` Value: ${dnsInstructions.cname.value}`,
103
+ '',
104
+ `2. TXT Record (for verification):`,
105
+ ` Name: ${dnsInstructions.txt.name}`,
106
+ ` Value: ${dnsInstructions.txt.value}`,
107
+ '',
108
+ 'After adding DNS records, run "devdoc domain verify" to verify.'
109
+ ]
110
+ });
111
+ } catch (error) {
112
+ console.error('[Domains API] Error adding domain:', error);
113
+ const message = error instanceof Error ? error.message : String(error);
114
+ return NextResponse.json({
115
+ error: 'Failed to add domain',
116
+ details: message
117
+ }, {
118
+ status: 500
119
+ });
120
+ }
121
+ }
@@ -0,0 +1,43 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { lookupCustomDomain } from '@/lib/storage/blob';
3
+ /**
4
+ * GET /api/domains/lookup
5
+ *
6
+ * Internal API for middleware to look up custom domains.
7
+ * Returns the project slug for an active custom domain.
8
+ *
9
+ * Query:
10
+ * ?domain=docs.example.com
11
+ *
12
+ * Response:
13
+ * { found: true, projectSlug: "my-project" }
14
+ * or
15
+ * { found: false }
16
+ */ export async function GET(request) {
17
+ try {
18
+ const { searchParams } = new URL(request.url);
19
+ const domain = searchParams.get('domain');
20
+ if (!domain) {
21
+ return NextResponse.json({
22
+ found: false,
23
+ error: 'Missing domain parameter'
24
+ });
25
+ }
26
+ const entry = await lookupCustomDomain(domain);
27
+ if (entry) {
28
+ return NextResponse.json({
29
+ found: true,
30
+ projectSlug: entry.projectSlug,
31
+ status: entry.status
32
+ });
33
+ }
34
+ return NextResponse.json({
35
+ found: false
36
+ });
37
+ } catch (error) {
38
+ console.error('[Domains Lookup] Error:', error);
39
+ return NextResponse.json({
40
+ found: false
41
+ });
42
+ }
43
+ }