@kurly-growth/growthman 0.1.13 → 0.1.15

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 (300) hide show
  1. package/CLAUDE.md +58 -0
  2. package/app/api/endpoints/[id]/route.ts +63 -0
  3. package/app/api/endpoints/bulk/route.ts +23 -0
  4. package/app/api/endpoints/import/route.ts +62 -0
  5. package/app/api/endpoints/route.ts +39 -0
  6. package/app/api/endpoints/sync/route.ts +89 -0
  7. package/app/api/mock/[...slug]/route.ts +82 -0
  8. package/app/globals.css +125 -0
  9. package/app/layout.tsx +36 -0
  10. package/app/page.tsx +213 -0
  11. package/bin/cli.js +12 -2
  12. package/components/api-test-dialog.tsx +222 -0
  13. package/components/endpoint-edit-dialog.tsx +181 -0
  14. package/components/endpoint-table.tsx +147 -0
  15. package/components/openapi-upload-dialog.tsx +213 -0
  16. package/components/ui/button.tsx +62 -0
  17. package/components/ui/checkbox.tsx +32 -0
  18. package/components/ui/dialog.tsx +143 -0
  19. package/components/ui/input.tsx +21 -0
  20. package/components/ui/label.tsx +24 -0
  21. package/components/ui/sonner.tsx +37 -0
  22. package/components/ui/table.tsx +116 -0
  23. package/components/ui/textarea.tsx +18 -0
  24. package/components.json +22 -0
  25. package/lib/openapi-parser.ts +270 -0
  26. package/lib/prisma.ts +9 -0
  27. package/lib/utils.ts +6 -0
  28. package/lib/validation.ts +19 -0
  29. package/next-env.d.ts +6 -0
  30. package/next.config.ts +7 -0
  31. package/package.json +10 -19
  32. package/pnpm-workspace.yaml +4 -0
  33. package/postcss.config.mjs +7 -0
  34. package/prisma/seed.ts +29 -0
  35. package/tsconfig.json +34 -0
  36. package/types/endpoint.ts +10 -0
  37. package/.next/BUILD_ID +0 -1
  38. package/.next/app-path-routes-manifest.json +0 -12
  39. package/.next/build/chunks/[root-of-the-server]__51225daf._.js +0 -206
  40. package/.next/build/chunks/[root-of-the-server]__51225daf._.js.map +0 -8
  41. package/.next/build/chunks/[root-of-the-server]__974941ed._.js +0 -500
  42. package/.next/build/chunks/[root-of-the-server]__974941ed._.js.map +0 -11
  43. package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_6920245c._.js +0 -13
  44. package/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_6920245c._.js.map +0 -5
  45. package/.next/build/chunks/[turbopack]_runtime.js +0 -795
  46. package/.next/build/chunks/[turbopack]_runtime.js.map +0 -10
  47. package/.next/build/chunks/node_modules_fe693df6._.js +0 -6758
  48. package/.next/build/chunks/node_modules_fe693df6._.js.map +0 -47
  49. package/.next/build/package.json +0 -1
  50. package/.next/build/postcss.js +0 -6
  51. package/.next/build/postcss.js.map +0 -5
  52. package/.next/build-manifest.json +0 -19
  53. package/.next/diagnostics/build-diagnostics.json +0 -6
  54. package/.next/diagnostics/framework.json +0 -1
  55. package/.next/export-marker.json +0 -6
  56. package/.next/fallback-build-manifest.json +0 -12
  57. package/.next/images-manifest.json +0 -66
  58. package/.next/next-minimal-server.js.nft.json +0 -1
  59. package/.next/next-server.js.nft.json +0 -1
  60. package/.next/package.json +0 -1
  61. package/.next/prerender-manifest.json +0 -114
  62. package/.next/required-server-files.js +0 -163
  63. package/.next/required-server-files.json +0 -163
  64. package/.next/routes-manifest.json +0 -109
  65. package/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
  66. package/.next/server/app/_global-error/page/build-manifest.json +0 -16
  67. package/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
  68. package/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
  69. package/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
  70. package/.next/server/app/_global-error/page.js +0 -11
  71. package/.next/server/app/_global-error/page.js.map +0 -5
  72. package/.next/server/app/_global-error/page.js.nft.json +0 -1
  73. package/.next/server/app/_global-error/page_client-reference-manifest.js +0 -2
  74. package/.next/server/app/_global-error.html +0 -2
  75. package/.next/server/app/_global-error.meta +0 -15
  76. package/.next/server/app/_global-error.rsc +0 -13
  77. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  78. package/.next/server/app/_global-error.segments/_full.segment.rsc +0 -13
  79. package/.next/server/app/_global-error.segments/_head.segment.rsc +0 -6
  80. package/.next/server/app/_global-error.segments/_index.segment.rsc +0 -4
  81. package/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  82. package/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
  83. package/.next/server/app/_not-found/page/build-manifest.json +0 -16
  84. package/.next/server/app/_not-found/page/next-font-manifest.json +0 -11
  85. package/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
  86. package/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
  87. package/.next/server/app/_not-found/page.js +0 -14
  88. package/.next/server/app/_not-found/page.js.map +0 -5
  89. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  90. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -2
  91. package/.next/server/app/_not-found.html +0 -1
  92. package/.next/server/app/_not-found.meta +0 -16
  93. package/.next/server/app/_not-found.rsc +0 -15
  94. package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -15
  95. package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  96. package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -6
  97. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  98. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -4
  99. package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  100. package/.next/server/app/api/endpoints/[id]/route/app-paths-manifest.json +0 -3
  101. package/.next/server/app/api/endpoints/[id]/route/build-manifest.json +0 -11
  102. package/.next/server/app/api/endpoints/[id]/route/server-reference-manifest.json +0 -4
  103. package/.next/server/app/api/endpoints/[id]/route.js +0 -7
  104. package/.next/server/app/api/endpoints/[id]/route.js.map +0 -5
  105. package/.next/server/app/api/endpoints/[id]/route.js.nft.json +0 -1
  106. package/.next/server/app/api/endpoints/[id]/route_client-reference-manifest.js +0 -2
  107. package/.next/server/app/api/endpoints/bulk/route/app-paths-manifest.json +0 -3
  108. package/.next/server/app/api/endpoints/bulk/route/build-manifest.json +0 -11
  109. package/.next/server/app/api/endpoints/bulk/route/server-reference-manifest.json +0 -4
  110. package/.next/server/app/api/endpoints/bulk/route.js +0 -7
  111. package/.next/server/app/api/endpoints/bulk/route.js.map +0 -5
  112. package/.next/server/app/api/endpoints/bulk/route.js.nft.json +0 -1
  113. package/.next/server/app/api/endpoints/bulk/route_client-reference-manifest.js +0 -2
  114. package/.next/server/app/api/endpoints/import/route/app-paths-manifest.json +0 -3
  115. package/.next/server/app/api/endpoints/import/route/build-manifest.json +0 -11
  116. package/.next/server/app/api/endpoints/import/route/server-reference-manifest.json +0 -4
  117. package/.next/server/app/api/endpoints/import/route.js +0 -7
  118. package/.next/server/app/api/endpoints/import/route.js.map +0 -5
  119. package/.next/server/app/api/endpoints/import/route.js.nft.json +0 -1
  120. package/.next/server/app/api/endpoints/import/route_client-reference-manifest.js +0 -2
  121. package/.next/server/app/api/endpoints/route/app-paths-manifest.json +0 -3
  122. package/.next/server/app/api/endpoints/route/build-manifest.json +0 -11
  123. package/.next/server/app/api/endpoints/route/server-reference-manifest.json +0 -4
  124. package/.next/server/app/api/endpoints/route.js +0 -7
  125. package/.next/server/app/api/endpoints/route.js.map +0 -5
  126. package/.next/server/app/api/endpoints/route.js.nft.json +0 -1
  127. package/.next/server/app/api/endpoints/route_client-reference-manifest.js +0 -2
  128. package/.next/server/app/api/endpoints/sync/route/app-paths-manifest.json +0 -3
  129. package/.next/server/app/api/endpoints/sync/route/build-manifest.json +0 -11
  130. package/.next/server/app/api/endpoints/sync/route/server-reference-manifest.json +0 -4
  131. package/.next/server/app/api/endpoints/sync/route.js +0 -7
  132. package/.next/server/app/api/endpoints/sync/route.js.map +0 -5
  133. package/.next/server/app/api/endpoints/sync/route.js.nft.json +0 -1
  134. package/.next/server/app/api/endpoints/sync/route_client-reference-manifest.js +0 -2
  135. package/.next/server/app/api/mock/[...slug]/route/app-paths-manifest.json +0 -3
  136. package/.next/server/app/api/mock/[...slug]/route/build-manifest.json +0 -11
  137. package/.next/server/app/api/mock/[...slug]/route/server-reference-manifest.json +0 -4
  138. package/.next/server/app/api/mock/[...slug]/route.js +0 -8
  139. package/.next/server/app/api/mock/[...slug]/route.js.map +0 -5
  140. package/.next/server/app/api/mock/[...slug]/route.js.nft.json +0 -1
  141. package/.next/server/app/api/mock/[...slug]/route_client-reference-manifest.js +0 -2
  142. package/.next/server/app/favicon.ico/route/app-paths-manifest.json +0 -3
  143. package/.next/server/app/favicon.ico/route/build-manifest.json +0 -11
  144. package/.next/server/app/favicon.ico/route.js +0 -7
  145. package/.next/server/app/favicon.ico/route.js.map +0 -5
  146. package/.next/server/app/favicon.ico/route.js.nft.json +0 -1
  147. package/.next/server/app/favicon.ico.meta +0 -1
  148. package/.next/server/app/index.html +0 -1
  149. package/.next/server/app/index.meta +0 -14
  150. package/.next/server/app/index.rsc +0 -21
  151. package/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -9
  152. package/.next/server/app/index.segments/_full.segment.rsc +0 -21
  153. package/.next/server/app/index.segments/_head.segment.rsc +0 -6
  154. package/.next/server/app/index.segments/_index.segment.rsc +0 -6
  155. package/.next/server/app/index.segments/_tree.segment.rsc +0 -4
  156. package/.next/server/app/page/app-paths-manifest.json +0 -3
  157. package/.next/server/app/page/build-manifest.json +0 -16
  158. package/.next/server/app/page/next-font-manifest.json +0 -11
  159. package/.next/server/app/page/react-loadable-manifest.json +0 -8
  160. package/.next/server/app/page/server-reference-manifest.json +0 -4
  161. package/.next/server/app/page.js +0 -16
  162. package/.next/server/app/page.js.map +0 -5
  163. package/.next/server/app/page.js.nft.json +0 -1
  164. package/.next/server/app/page_client-reference-manifest.js +0 -2
  165. package/.next/server/app-paths-manifest.json +0 -12
  166. package/.next/server/chunks/1629d_next_dist_esm_build_templates_app-route_498527d5.js +0 -3
  167. package/.next/server/chunks/1629d_next_dist_esm_build_templates_app-route_498527d5.js.map +0 -1
  168. package/.next/server/chunks/[externals]__cf2ccb51._.js +0 -3
  169. package/.next/server/chunks/[externals]__cf2ccb51._.js.map +0 -1
  170. package/.next/server/chunks/[externals]_next_dist_8dbe5856._.js +0 -3
  171. package/.next/server/chunks/[externals]_next_dist_8dbe5856._.js.map +0 -1
  172. package/.next/server/chunks/[root-of-the-server]__3534fecc._.js +0 -3
  173. package/.next/server/chunks/[root-of-the-server]__3534fecc._.js.map +0 -1
  174. package/.next/server/chunks/[root-of-the-server]__461ea613._.js +0 -21
  175. package/.next/server/chunks/[root-of-the-server]__461ea613._.js.map +0 -1
  176. package/.next/server/chunks/[root-of-the-server]__4929acb0._.js +0 -129
  177. package/.next/server/chunks/[root-of-the-server]__4929acb0._.js.map +0 -1
  178. package/.next/server/chunks/[root-of-the-server]__909218aa._.js +0 -3
  179. package/.next/server/chunks/[root-of-the-server]__909218aa._.js.map +0 -1
  180. package/.next/server/chunks/[root-of-the-server]__a6c01a13._.js +0 -3
  181. package/.next/server/chunks/[root-of-the-server]__a6c01a13._.js.map +0 -1
  182. package/.next/server/chunks/[root-of-the-server]__db1127cf._.js +0 -3
  183. package/.next/server/chunks/[root-of-the-server]__db1127cf._.js.map +0 -1
  184. package/.next/server/chunks/[root-of-the-server]__e46f3e25._.js +0 -3
  185. package/.next/server/chunks/[root-of-the-server]__e46f3e25._.js.map +0 -1
  186. package/.next/server/chunks/[turbopack]_runtime.js +0 -795
  187. package/.next/server/chunks/[turbopack]_runtime.js.map +0 -10
  188. package/.next/server/chunks/_next-internal_server_app_api_endpoints_[id]_route_actions_b91cfc4c.js +0 -3
  189. package/.next/server/chunks/_next-internal_server_app_api_endpoints_[id]_route_actions_b91cfc4c.js.map +0 -1
  190. package/.next/server/chunks/_next-internal_server_app_api_endpoints_bulk_route_actions_560cc6cd.js +0 -3
  191. package/.next/server/chunks/_next-internal_server_app_api_endpoints_bulk_route_actions_560cc6cd.js.map +0 -1
  192. package/.next/server/chunks/_next-internal_server_app_api_endpoints_import_route_actions_f2444950.js +0 -3
  193. package/.next/server/chunks/_next-internal_server_app_api_endpoints_import_route_actions_f2444950.js.map +0 -1
  194. package/.next/server/chunks/_next-internal_server_app_api_endpoints_route_actions_49d8ad56.js +0 -3
  195. package/.next/server/chunks/_next-internal_server_app_api_endpoints_route_actions_49d8ad56.js.map +0 -1
  196. package/.next/server/chunks/_next-internal_server_app_api_endpoints_sync_route_actions_0f446550.js +0 -3
  197. package/.next/server/chunks/_next-internal_server_app_api_endpoints_sync_route_actions_0f446550.js.map +0 -1
  198. package/.next/server/chunks/_next-internal_server_app_api_mock_[___slug]_route_actions_be875f77.js +0 -3
  199. package/.next/server/chunks/_next-internal_server_app_api_mock_[___slug]_route_actions_be875f77.js.map +0 -1
  200. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_353150a5.js +0 -3
  201. package/.next/server/chunks/_next-internal_server_app_favicon_ico_route_actions_353150a5.js.map +0 -1
  202. package/.next/server/chunks/node_modules__pnpm_a61fb769._.js +0 -3
  203. package/.next/server/chunks/node_modules__pnpm_a61fb769._.js.map +0 -1
  204. package/.next/server/chunks/ssr/1629d_next_dist_1a21bde7._.js +0 -6
  205. package/.next/server/chunks/ssr/1629d_next_dist_1a21bde7._.js.map +0 -1
  206. package/.next/server/chunks/ssr/1629d_next_dist_8dc31fba._.js +0 -3
  207. package/.next/server/chunks/ssr/1629d_next_dist_8dc31fba._.js.map +0 -1
  208. package/.next/server/chunks/ssr/1629d_next_dist_client_components_b01b33e4._.js +0 -3
  209. package/.next/server/chunks/ssr/1629d_next_dist_client_components_b01b33e4._.js.map +0 -1
  210. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_forbidden_4cab2078.js +0 -3
  211. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_forbidden_4cab2078.js.map +0 -1
  212. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_global-error_0d5cb623.js +0 -3
  213. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_global-error_0d5cb623.js.map +0 -1
  214. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_unauthorized_b8fbdcad.js +0 -3
  215. package/.next/server/chunks/ssr/1629d_next_dist_client_components_builtin_unauthorized_b8fbdcad.js.map +0 -1
  216. package/.next/server/chunks/ssr/1629d_next_dist_d78dee57._.js +0 -4
  217. package/.next/server/chunks/ssr/1629d_next_dist_d78dee57._.js.map +0 -1
  218. package/.next/server/chunks/ssr/1629d_next_dist_esm_build_templates_app-page_d4e9464b.js +0 -4
  219. package/.next/server/chunks/ssr/1629d_next_dist_esm_build_templates_app-page_d4e9464b.js.map +0 -1
  220. package/.next/server/chunks/ssr/67049_lucide-react_dist_esm_createLucideIcon_22fe2e14.js +0 -3
  221. package/.next/server/chunks/ssr/67049_lucide-react_dist_esm_createLucideIcon_22fe2e14.js.map +0 -1
  222. package/.next/server/chunks/ssr/[externals]_next_dist_server_app-render_work-async-storage_external_1f8eeae7.js +0 -3
  223. package/.next/server/chunks/ssr/[externals]_next_dist_server_app-render_work-async-storage_external_1f8eeae7.js.map +0 -1
  224. package/.next/server/chunks/ssr/[root-of-the-server]__14f4396a._.js +0 -10
  225. package/.next/server/chunks/ssr/[root-of-the-server]__14f4396a._.js.map +0 -1
  226. package/.next/server/chunks/ssr/[root-of-the-server]__3064bf15._.js +0 -3
  227. package/.next/server/chunks/ssr/[root-of-the-server]__3064bf15._.js.map +0 -1
  228. package/.next/server/chunks/ssr/[root-of-the-server]__4ff41ba3._.js +0 -4
  229. package/.next/server/chunks/ssr/[root-of-the-server]__4ff41ba3._.js.map +0 -1
  230. package/.next/server/chunks/ssr/[root-of-the-server]__57c5da8e._.js +0 -3
  231. package/.next/server/chunks/ssr/[root-of-the-server]__57c5da8e._.js.map +0 -1
  232. package/.next/server/chunks/ssr/[root-of-the-server]__67653b38._.js +0 -3
  233. package/.next/server/chunks/ssr/[root-of-the-server]__67653b38._.js.map +0 -1
  234. package/.next/server/chunks/ssr/[root-of-the-server]__7d48410d._.js +0 -3
  235. package/.next/server/chunks/ssr/[root-of-the-server]__7d48410d._.js.map +0 -1
  236. package/.next/server/chunks/ssr/[root-of-the-server]__a49eaf36._.js +0 -3
  237. package/.next/server/chunks/ssr/[root-of-the-server]__a49eaf36._.js.map +0 -1
  238. package/.next/server/chunks/ssr/[root-of-the-server]__b0617f51._.js +0 -3
  239. package/.next/server/chunks/ssr/[root-of-the-server]__b0617f51._.js.map +0 -1
  240. package/.next/server/chunks/ssr/[root-of-the-server]__cc026bde._.js +0 -3
  241. package/.next/server/chunks/ssr/[root-of-the-server]__cc026bde._.js.map +0 -1
  242. package/.next/server/chunks/ssr/[root-of-the-server]__d079898e._.js +0 -3
  243. package/.next/server/chunks/ssr/[root-of-the-server]__d079898e._.js.map +0 -1
  244. package/.next/server/chunks/ssr/[root-of-the-server]__d3649e47._.js +0 -3
  245. package/.next/server/chunks/ssr/[root-of-the-server]__d3649e47._.js.map +0 -1
  246. package/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -795
  247. package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +0 -10
  248. package/.next/server/chunks/ssr/_9b6e3dc4._.js +0 -3
  249. package/.next/server/chunks/ssr/_9b6e3dc4._.js.map +0 -1
  250. package/.next/server/chunks/ssr/_a437ac52._.js +0 -7
  251. package/.next/server/chunks/ssr/_a437ac52._.js.map +0 -1
  252. package/.next/server/chunks/ssr/_b62b070d._.js +0 -4
  253. package/.next/server/chunks/ssr/_b62b070d._.js.map +0 -1
  254. package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_75761787.js +0 -3
  255. package/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_75761787.js.map +0 -1
  256. package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_554ec2bf.js +0 -3
  257. package/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_554ec2bf.js.map +0 -1
  258. package/.next/server/chunks/ssr/_next-internal_server_app_page_actions_39d4fc33.js +0 -3
  259. package/.next/server/chunks/ssr/_next-internal_server_app_page_actions_39d4fc33.js.map +0 -1
  260. package/.next/server/chunks/ssr/app_b9b1292a._.js +0 -3
  261. package/.next/server/chunks/ssr/app_b9b1292a._.js.map +0 -1
  262. package/.next/server/functions-config-manifest.json +0 -4
  263. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  264. package/.next/server/middleware-build-manifest.js +0 -20
  265. package/.next/server/middleware-manifest.json +0 -6
  266. package/.next/server/next-font-manifest.js +0 -1
  267. package/.next/server/next-font-manifest.json +0 -15
  268. package/.next/server/pages/404.html +0 -1
  269. package/.next/server/pages/500.html +0 -2
  270. package/.next/server/pages-manifest.json +0 -4
  271. package/.next/server/server-reference-manifest.js +0 -1
  272. package/.next/server/server-reference-manifest.json +0 -5
  273. package/.next/static/chunks/16403d658c649f0f.js +0 -1
  274. package/.next/static/chunks/2422cfacfdb28c2c.js +0 -5
  275. package/.next/static/chunks/42572c067be8ea0f.js +0 -1
  276. package/.next/static/chunks/5045da71379799ce.js +0 -1
  277. package/.next/static/chunks/6e04dfc4035d7150.js +0 -5
  278. package/.next/static/chunks/8155485116e3ff24.js +0 -1
  279. package/.next/static/chunks/a66e09a7c2336f67.js +0 -1
  280. package/.next/static/chunks/a6dad97d9634a72d.js +0 -1
  281. package/.next/static/chunks/a6dad97d9634a72d.js.map +0 -1
  282. package/.next/static/chunks/da3f3e4f37f68cee.css +0 -3
  283. package/.next/static/chunks/f1cfb69226717279.js +0 -1
  284. package/.next/static/chunks/turbopack-7027959231bb432a.js +0 -4
  285. package/.next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  286. package/.next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  287. package/.next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  288. package/.next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  289. package/.next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  290. package/.next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  291. package/.next/static/media/favicon.de6b30f1.ico +0 -0
  292. package/.next/static/rD3QdLXPN9C3vquucAUu6/_buildManifest.js +0 -11
  293. package/.next/static/rD3QdLXPN9C3vquucAUu6/_clientMiddlewareManifest.json +0 -1
  294. package/.next/static/rD3QdLXPN9C3vquucAUu6/_ssgManifest.js +0 -1
  295. package/.next/trace +0 -1
  296. package/.next/trace-build +0 -1
  297. package/.next/turbopack +0 -0
  298. package/.next/types/routes.d.ts +0 -78
  299. package/.next/types/validator.ts +0 -124
  300. /package/{.next/server/app/favicon.ico.body → app/favicon.ico} +0 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,58 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Development Commands
6
+
7
+ ```bash
8
+ pnpm dev # Start dev server (http://localhost:3100)
9
+ pnpm dev:clean # Reset DB with seed data and start dev server
10
+ pnpm build # Build for production
11
+ pnpm lint # Run ESLint
12
+ pnpm db:seed # Seed the database
13
+ pnpm db:reset # Reset database with migrations
14
+ ```
15
+
16
+ ## Architecture Overview
17
+
18
+ This is a **Mock Server Dashboard** - a Next.js 16 application that allows users to create, manage, and serve mock API endpoints stored in PostgreSQL via Prisma.
19
+
20
+ ### Core Concept
21
+
22
+ 1. Users define mock endpoints (path, method, status code, response body) through a web UI
23
+ 2. These endpoints are stored in the `MockEndpoint` table
24
+ 3. Requests to `/api/mock/*` are dynamically matched against stored endpoints using `path-to-regexp`
25
+
26
+ ### Key Components
27
+
28
+ - **`/app/api/mock/[...slug]/route.ts`** - Catch-all route that handles all mock requests. Matches incoming requests against stored endpoints using pattern matching (supports path parameters like `:id`)
29
+
30
+ - **`/app/api/endpoints/`** - CRUD API for managing mock endpoints
31
+ - `route.ts` - GET (list all), POST (create)
32
+ - `[id]/route.ts` - PUT (update), DELETE
33
+ - `import/route.ts` - Bulk import from OpenAPI spec
34
+ - `sync/route.ts` - Sync endpoints with external spec
35
+
36
+ - **`/lib/openapi-parser.ts`** - Parses OpenAPI 3.x specs and generates mock response data based on schema definitions. Handles `$ref`, `allOf`, `oneOf`, `anyOf`, and generates type-appropriate mock values.
37
+
38
+ - **`/components/`** - React components for the dashboard UI
39
+ - `endpoint-table.tsx` - Display list of endpoints
40
+ - `endpoint-edit-dialog.tsx` - Create/edit endpoint with Monaco editor for JSON
41
+ - `openapi-upload-dialog.tsx` - Import endpoints from OpenAPI spec
42
+
43
+ ### Database Schema
44
+
45
+ Single table `MockEndpoint` with unique constraint on `(path, method)`:
46
+ - `path` - Express-style route (e.g., `/v1/users/:id`)
47
+ - `method` - HTTP method
48
+ - `statusCode` - Response status code
49
+ - `responseBody` - JSON response data
50
+
51
+ ### Tech Stack
52
+
53
+ - Next.js 16 (App Router)
54
+ - React 19
55
+ - Prisma with PostgreSQL
56
+ - Tailwind CSS 4
57
+ - Radix UI + shadcn/ui components
58
+ - Monaco Editor for JSON editing
@@ -0,0 +1,63 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { prisma } from '@/lib/prisma'
3
+ import { validatePath, validateMethod, validateStatusCode } from '@/lib/validation'
4
+
5
+ type RouteParams = {
6
+ params: Promise<{ id: string }>
7
+ }
8
+
9
+ // GET: 단일 엔드포인트 조회
10
+ export async function GET(_request: NextRequest, { params }: RouteParams) {
11
+ const { id } = await params
12
+
13
+ const endpoint = await prisma.mockEndpoint.findUnique({
14
+ where: { id },
15
+ })
16
+
17
+ if (!endpoint) {
18
+ return NextResponse.json({ error: 'Endpoint not found' }, { status: 404 })
19
+ }
20
+
21
+ return NextResponse.json(endpoint)
22
+ }
23
+
24
+ // PUT: 엔드포인트 수정
25
+ export async function PUT(request: NextRequest, { params }: RouteParams) {
26
+ const { id } = await params
27
+ const body = await request.json()
28
+ const { path, method, description, statusCode, responseBody } = body
29
+
30
+ if (path !== undefined && !validatePath(path)) {
31
+ return NextResponse.json({ error: 'Invalid path' }, { status: 400 })
32
+ }
33
+ if (method !== undefined && !validateMethod(method)) {
34
+ return NextResponse.json({ error: 'Invalid method' }, { status: 400 })
35
+ }
36
+ if (!validateStatusCode(statusCode)) {
37
+ return NextResponse.json({ error: 'Invalid status code' }, { status: 400 })
38
+ }
39
+
40
+ const endpoint = await prisma.mockEndpoint.update({
41
+ where: { id },
42
+ data: {
43
+ path,
44
+ method: method?.toUpperCase(),
45
+ description,
46
+ statusCode,
47
+ responseBody,
48
+ },
49
+ })
50
+
51
+ return NextResponse.json(endpoint)
52
+ }
53
+
54
+ // DELETE: 엔드포인트 삭제
55
+ export async function DELETE(_request: NextRequest, { params }: RouteParams) {
56
+ const { id } = await params
57
+
58
+ await prisma.mockEndpoint.delete({
59
+ where: { id },
60
+ })
61
+
62
+ return NextResponse.json({ success: true })
63
+ }
@@ -0,0 +1,23 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { prisma } from '@/lib/prisma'
3
+
4
+ // DELETE: 여러 엔드포인트 일괄 삭제
5
+ export async function DELETE(request: NextRequest) {
6
+ const body = await request.json()
7
+ const { ids } = body
8
+
9
+ if (!Array.isArray(ids) || ids.length === 0) {
10
+ return NextResponse.json(
11
+ { error: 'ids must be a non-empty array' },
12
+ { status: 400 }
13
+ )
14
+ }
15
+
16
+ const result = await prisma.mockEndpoint.deleteMany({
17
+ where: {
18
+ id: { in: ids },
19
+ },
20
+ })
21
+
22
+ return NextResponse.json({ deleted: result.count })
23
+ }
@@ -0,0 +1,62 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { Prisma } from '@/generated/prisma'
3
+ import { prisma } from '@/lib/prisma'
4
+ import { parseOpenAPISpec } from '@/lib/openapi-parser'
5
+
6
+ export async function POST(request: NextRequest) {
7
+ const body = await request.json()
8
+ const { spec, pathPrefix } = body
9
+
10
+ // Handle both old format (spec directly) and new format ({ spec, pathPrefix })
11
+ const actualSpec = spec ?? body
12
+ const endpoints = parseOpenAPISpec(actualSpec)
13
+
14
+ // Apply path prefix if provided
15
+ const normalizedPrefix = pathPrefix ? pathPrefix.replace(/\/+$/, '') : ''
16
+ const processedEndpoints = endpoints.map((ep) => ({
17
+ ...ep,
18
+ path: normalizedPrefix ? `${normalizedPrefix}${ep.path}` : ep.path,
19
+ }))
20
+
21
+ if (processedEndpoints.length === 0) {
22
+ return NextResponse.json(
23
+ { error: 'No valid endpoints found in OpenAPI spec' },
24
+ { status: 400 }
25
+ )
26
+ }
27
+
28
+ const results = await Promise.allSettled(
29
+ processedEndpoints.map((ep) =>
30
+ prisma.mockEndpoint.upsert({
31
+ where: {
32
+ path_method: {
33
+ path: ep.path,
34
+ method: ep.method,
35
+ },
36
+ },
37
+ update: {
38
+ description: ep.description,
39
+ statusCode: ep.statusCode,
40
+ responseBody: ep.responseBody as Prisma.InputJsonValue,
41
+ },
42
+ create: {
43
+ path: ep.path,
44
+ method: ep.method,
45
+ description: ep.description,
46
+ statusCode: ep.statusCode,
47
+ responseBody: ep.responseBody as Prisma.InputJsonValue,
48
+ },
49
+ })
50
+ )
51
+ )
52
+
53
+ const created = results.filter((r) => r.status === 'fulfilled').length
54
+ const failed = results.filter((r) => r.status === 'rejected').length
55
+
56
+ return NextResponse.json({
57
+ message: `Imported ${created} endpoints`,
58
+ created,
59
+ failed,
60
+ total: processedEndpoints.length,
61
+ })
62
+ }
@@ -0,0 +1,39 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { prisma } from '@/lib/prisma'
3
+ import { validatePath, validateMethod, validateStatusCode } from '@/lib/validation'
4
+
5
+ // GET: 모든 엔드포인트 조회
6
+ export async function GET() {
7
+ const endpoints = await prisma.mockEndpoint.findMany({
8
+ orderBy: { createdAt: 'desc' },
9
+ })
10
+ return NextResponse.json(endpoints)
11
+ }
12
+
13
+ // POST: 새 엔드포인트 생성
14
+ export async function POST(request: NextRequest) {
15
+ const body = await request.json()
16
+ const { path, method, description, statusCode, responseBody } = body
17
+
18
+ if (!validatePath(path)) {
19
+ return NextResponse.json({ error: 'Invalid path' }, { status: 400 })
20
+ }
21
+ if (!validateMethod(method)) {
22
+ return NextResponse.json({ error: 'Invalid method' }, { status: 400 })
23
+ }
24
+ if (!validateStatusCode(statusCode)) {
25
+ return NextResponse.json({ error: 'Invalid status code' }, { status: 400 })
26
+ }
27
+
28
+ const endpoint = await prisma.mockEndpoint.create({
29
+ data: {
30
+ path,
31
+ method: method.toUpperCase(),
32
+ description,
33
+ statusCode: statusCode ?? 200,
34
+ responseBody,
35
+ },
36
+ })
37
+
38
+ return NextResponse.json(endpoint, { status: 201 })
39
+ }
@@ -0,0 +1,89 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { Prisma } from '@/generated/prisma'
3
+ import { prisma } from '@/lib/prisma'
4
+ import { parseOpenAPISpec } from '@/lib/openapi-parser'
5
+
6
+ export async function POST(request: NextRequest) {
7
+ const { url, pathPrefix } = await request.json()
8
+
9
+ if (!url) {
10
+ return NextResponse.json({ error: 'URL is required' }, { status: 400 })
11
+ }
12
+
13
+ // Normalize path prefix (remove trailing slashes)
14
+ const normalizedPrefix = pathPrefix ? pathPrefix.replace(/\/+$/, '') : ''
15
+
16
+ try {
17
+ // OpenAPI JSON 가져오기
18
+ const response = await fetch(url, {
19
+ headers: {
20
+ Accept: 'application/json',
21
+ },
22
+ })
23
+
24
+ if (!response.ok) {
25
+ return NextResponse.json(
26
+ { error: `Failed to fetch OpenAPI spec: ${response.statusText}` },
27
+ { status: 400 }
28
+ )
29
+ }
30
+
31
+ const spec = await response.json()
32
+ const endpoints = parseOpenAPISpec(spec)
33
+
34
+ // Apply path prefix if provided
35
+ const processedEndpoints = endpoints.map((ep) => ({
36
+ ...ep,
37
+ path: normalizedPrefix ? `${normalizedPrefix}${ep.path}` : ep.path,
38
+ }))
39
+
40
+ if (processedEndpoints.length === 0) {
41
+ return NextResponse.json(
42
+ { error: 'No valid endpoints found in OpenAPI spec' },
43
+ { status: 400 }
44
+ )
45
+ }
46
+
47
+ // Upsert로 새로 추가하거나 업데이트
48
+ const results = await Promise.allSettled(
49
+ processedEndpoints.map((ep) =>
50
+ prisma.mockEndpoint.upsert({
51
+ where: {
52
+ path_method: {
53
+ path: ep.path,
54
+ method: ep.method,
55
+ },
56
+ },
57
+ update: {
58
+ description: ep.description,
59
+ statusCode: ep.statusCode,
60
+ responseBody: ep.responseBody as Prisma.InputJsonValue,
61
+ },
62
+ create: {
63
+ path: ep.path,
64
+ method: ep.method,
65
+ description: ep.description,
66
+ statusCode: ep.statusCode,
67
+ responseBody: ep.responseBody as Prisma.InputJsonValue,
68
+ },
69
+ })
70
+ )
71
+ )
72
+
73
+ const succeeded = results.filter((r) => r.status === 'fulfilled').length
74
+ const failed = results.filter((r) => r.status === 'rejected').length
75
+
76
+ return NextResponse.json({
77
+ message: `Synced ${succeeded} endpoints`,
78
+ succeeded,
79
+ failed,
80
+ total: processedEndpoints.length,
81
+ })
82
+ } catch (error) {
83
+ console.error('Sync error:', error)
84
+ return NextResponse.json(
85
+ { error: error instanceof Error ? error.message : 'Sync failed' },
86
+ { status: 500 }
87
+ )
88
+ }
89
+ }
@@ -0,0 +1,82 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { match } from 'path-to-regexp'
3
+ import { prisma } from '@/lib/prisma'
4
+
5
+ type RouteParams = {
6
+ params: Promise<{ slug: string[] }>
7
+ }
8
+
9
+ async function findMatchingEndpoint(requestPath: string, method: string) {
10
+ // DB에서 해당 method의 모든 엔드포인트 조회
11
+ const endpoints = await prisma.mockEndpoint.findMany({
12
+ where: { method },
13
+ })
14
+
15
+ // 정확히 일치하는 경로 먼저 확인
16
+ const exactMatch = endpoints.find((ep) => ep.path === requestPath)
17
+ if (exactMatch) {
18
+ return { endpoint: exactMatch, params: {} }
19
+ }
20
+
21
+ // path-to-regexp를 사용한 패턴 매칭
22
+ for (const endpoint of endpoints) {
23
+ try {
24
+ const matchFn = match(endpoint.path, { decode: decodeURIComponent })
25
+ const result = matchFn(requestPath)
26
+
27
+ if (result) {
28
+ return { endpoint, params: result.params }
29
+ }
30
+ } catch {
31
+ // 잘못된 패턴은 무시
32
+ continue
33
+ }
34
+ }
35
+
36
+ return null
37
+ }
38
+
39
+ async function handleRequest(request: NextRequest, { params }: RouteParams) {
40
+ const { slug } = await params
41
+ const requestPath = '/' + slug.join('/')
42
+ const method = request.method
43
+
44
+ const result = await findMatchingEndpoint(requestPath, method)
45
+
46
+ if (!result) {
47
+ return NextResponse.json(
48
+ { error: 'Endpoint not found', path: requestPath, method },
49
+ { status: 404 }
50
+ )
51
+ }
52
+
53
+ const { endpoint, params: pathParams } = result
54
+
55
+ // responseBody에 pathParams를 포함하여 반환 (선택적)
56
+ const responseData =
57
+ Object.keys(pathParams).length > 0
58
+ ? { ...endpoint.responseBody as object, _pathParams: pathParams }
59
+ : endpoint.responseBody
60
+
61
+ return NextResponse.json(responseData, { status: endpoint.statusCode })
62
+ }
63
+
64
+ export async function GET(request: NextRequest, context: RouteParams) {
65
+ return handleRequest(request, context)
66
+ }
67
+
68
+ export async function POST(request: NextRequest, context: RouteParams) {
69
+ return handleRequest(request, context)
70
+ }
71
+
72
+ export async function PUT(request: NextRequest, context: RouteParams) {
73
+ return handleRequest(request, context)
74
+ }
75
+
76
+ export async function PATCH(request: NextRequest, context: RouteParams) {
77
+ return handleRequest(request, context)
78
+ }
79
+
80
+ export async function DELETE(request: NextRequest, context: RouteParams) {
81
+ return handleRequest(request, context)
82
+ }
@@ -0,0 +1,125 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --color-background: var(--background);
8
+ --color-foreground: var(--foreground);
9
+ --font-sans: var(--font-geist-sans);
10
+ --font-mono: var(--font-geist-mono);
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --radius-sm: calc(var(--radius) - 4px);
41
+ --radius-md: calc(var(--radius) - 2px);
42
+ --radius-lg: var(--radius);
43
+ --radius-xl: calc(var(--radius) + 4px);
44
+ --radius-2xl: calc(var(--radius) + 8px);
45
+ --radius-3xl: calc(var(--radius) + 12px);
46
+ --radius-4xl: calc(var(--radius) + 16px);
47
+ }
48
+
49
+ :root {
50
+ --radius: 0.625rem;
51
+ --background: oklch(1 0 0);
52
+ --foreground: oklch(0.145 0 0);
53
+ --card: oklch(1 0 0);
54
+ --card-foreground: oklch(0.145 0 0);
55
+ --popover: oklch(1 0 0);
56
+ --popover-foreground: oklch(0.145 0 0);
57
+ --primary: oklch(0.205 0 0);
58
+ --primary-foreground: oklch(0.985 0 0);
59
+ --secondary: oklch(0.97 0 0);
60
+ --secondary-foreground: oklch(0.205 0 0);
61
+ --muted: oklch(0.97 0 0);
62
+ --muted-foreground: oklch(0.556 0 0);
63
+ --accent: oklch(0.97 0 0);
64
+ --accent-foreground: oklch(0.205 0 0);
65
+ --destructive: oklch(0.577 0.245 27.325);
66
+ --border: oklch(0.922 0 0);
67
+ --input: oklch(0.922 0 0);
68
+ --ring: oklch(0.708 0 0);
69
+ --chart-1: oklch(0.646 0.222 41.116);
70
+ --chart-2: oklch(0.6 0.118 184.704);
71
+ --chart-3: oklch(0.398 0.07 227.392);
72
+ --chart-4: oklch(0.828 0.189 84.429);
73
+ --chart-5: oklch(0.769 0.188 70.08);
74
+ --sidebar: oklch(0.985 0 0);
75
+ --sidebar-foreground: oklch(0.145 0 0);
76
+ --sidebar-primary: oklch(0.205 0 0);
77
+ --sidebar-primary-foreground: oklch(0.985 0 0);
78
+ --sidebar-accent: oklch(0.97 0 0);
79
+ --sidebar-accent-foreground: oklch(0.205 0 0);
80
+ --sidebar-border: oklch(0.922 0 0);
81
+ --sidebar-ring: oklch(0.708 0 0);
82
+ }
83
+
84
+ .dark {
85
+ --background: oklch(0.145 0 0);
86
+ --foreground: oklch(0.985 0 0);
87
+ --card: oklch(0.205 0 0);
88
+ --card-foreground: oklch(0.985 0 0);
89
+ --popover: oklch(0.205 0 0);
90
+ --popover-foreground: oklch(0.985 0 0);
91
+ --primary: oklch(0.922 0 0);
92
+ --primary-foreground: oklch(0.205 0 0);
93
+ --secondary: oklch(0.269 0 0);
94
+ --secondary-foreground: oklch(0.985 0 0);
95
+ --muted: oklch(0.269 0 0);
96
+ --muted-foreground: oklch(0.708 0 0);
97
+ --accent: oklch(0.269 0 0);
98
+ --accent-foreground: oklch(0.985 0 0);
99
+ --destructive: oklch(0.704 0.191 22.216);
100
+ --border: oklch(1 0 0 / 10%);
101
+ --input: oklch(1 0 0 / 15%);
102
+ --ring: oklch(0.556 0 0);
103
+ --chart-1: oklch(0.488 0.243 264.376);
104
+ --chart-2: oklch(0.696 0.17 162.48);
105
+ --chart-3: oklch(0.769 0.188 70.08);
106
+ --chart-4: oklch(0.627 0.265 303.9);
107
+ --chart-5: oklch(0.645 0.246 16.439);
108
+ --sidebar: oklch(0.205 0 0);
109
+ --sidebar-foreground: oklch(0.985 0 0);
110
+ --sidebar-primary: oklch(0.488 0.243 264.376);
111
+ --sidebar-primary-foreground: oklch(0.985 0 0);
112
+ --sidebar-accent: oklch(0.269 0 0);
113
+ --sidebar-accent-foreground: oklch(0.985 0 0);
114
+ --sidebar-border: oklch(1 0 0 / 10%);
115
+ --sidebar-ring: oklch(0.556 0 0);
116
+ }
117
+
118
+ @layer base {
119
+ * {
120
+ @apply border-border outline-ring/50;
121
+ }
122
+ body {
123
+ @apply bg-background text-foreground;
124
+ }
125
+ }
package/app/layout.tsx ADDED
@@ -0,0 +1,36 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import { Toaster } from "@/components/ui/sonner";
4
+ import "./globals.css";
5
+
6
+ const geistSans = Geist({
7
+ variable: "--font-geist-sans",
8
+ subsets: ["latin"],
9
+ });
10
+
11
+ const geistMono = Geist_Mono({
12
+ variable: "--font-geist-mono",
13
+ subsets: ["latin"],
14
+ });
15
+
16
+ export const metadata: Metadata = {
17
+ title: "Growthman",
18
+ description: "Local mock API server with UI dashboard",
19
+ };
20
+
21
+ export default function RootLayout({
22
+ children,
23
+ }: Readonly<{
24
+ children: React.ReactNode;
25
+ }>) {
26
+ return (
27
+ <html lang="en">
28
+ <body
29
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
30
+ >
31
+ {children}
32
+ <Toaster />
33
+ </body>
34
+ </html>
35
+ );
36
+ }