@libredb/studio 0.9.7

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 (572) hide show
  1. package/.claude/settings.local.json +127 -0
  2. package/.cursorrules +426 -0
  3. package/.devin/wiki.json +143 -0
  4. package/.dockerignore +80 -0
  5. package/.env.example +159 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +49 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
  8. package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
  9. package/.github/workflows/ci.yml +185 -0
  10. package/.github/workflows/codeql.yml +57 -0
  11. package/.github/workflows/docker-build-push.yml +118 -0
  12. package/.github/workflows/helm-release.yml +113 -0
  13. package/CLAUDE.md +265 -0
  14. package/CODE_OF_CONDUCT.md +124 -0
  15. package/CONTRIBUTING.md +154 -0
  16. package/Dockerfile +73 -0
  17. package/LICENSE +21 -0
  18. package/README.md +614 -0
  19. package/SECURITY.md +107 -0
  20. package/artifacthub-repo.yml +4 -0
  21. package/bun.lock +1714 -0
  22. package/bunfig.toml +3 -0
  23. package/charts/libredb-studio/.helmignore +11 -0
  24. package/charts/libredb-studio/Chart.lock +6 -0
  25. package/charts/libredb-studio/Chart.yaml +50 -0
  26. package/charts/libredb-studio/README.md +206 -0
  27. package/charts/libredb-studio/templates/NOTES.txt +59 -0
  28. package/charts/libredb-studio/templates/_helpers.tpl +135 -0
  29. package/charts/libredb-studio/templates/configmap.yaml +37 -0
  30. package/charts/libredb-studio/templates/deployment.yaml +184 -0
  31. package/charts/libredb-studio/templates/hpa.yaml +32 -0
  32. package/charts/libredb-studio/templates/ingress.yaml +41 -0
  33. package/charts/libredb-studio/templates/networkpolicy.yaml +50 -0
  34. package/charts/libredb-studio/templates/pdb.yaml +18 -0
  35. package/charts/libredb-studio/templates/pvc.yaml +23 -0
  36. package/charts/libredb-studio/templates/secret.yaml +30 -0
  37. package/charts/libredb-studio/templates/seed-configmap.yaml +11 -0
  38. package/charts/libredb-studio/templates/service.yaml +22 -0
  39. package/charts/libredb-studio/templates/serviceaccount.yaml +13 -0
  40. package/charts/libredb-studio/values.schema.json +246 -0
  41. package/charts/libredb-studio/values.yaml +286 -0
  42. package/components.json +22 -0
  43. package/conductor/code_styleguides/typescript.md +43 -0
  44. package/conductor/product-guidelines.md +43 -0
  45. package/conductor/product.md +3 -0
  46. package/conductor/setup_state.json +1 -0
  47. package/conductor/tech-stack.md +39 -0
  48. package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +8 -0
  49. package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +44 -0
  50. package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +31 -0
  51. package/conductor/tracks.md +8 -0
  52. package/conductor/workflow.md +333 -0
  53. package/database-compose.yml +55 -0
  54. package/docker/postgres-init/01-extensions.sql +10 -0
  55. package/docker/postgres-init/02-sample-data.sql +585 -0
  56. package/docker/postgres.yml +68 -0
  57. package/docker-compose.yml +38 -0
  58. package/docs/AI_PLAN.md +74 -0
  59. package/docs/API_DOCS.md +875 -0
  60. package/docs/ARCHITECTURE.md +218 -0
  61. package/docs/DATABASE_PROVIDERS.md +358 -0
  62. package/docs/FEATURES.md +116 -0
  63. package/docs/HELM_CHART.md +252 -0
  64. package/docs/LOGIN_PAGE.md +178 -0
  65. package/docs/MONACO_EDITOR_PERFORMANCE.md +315 -0
  66. package/docs/OIDC_ARCH.md +681 -0
  67. package/docs/OIDC_SETUP.md +322 -0
  68. package/docs/POSTGRES_METRICS.md +516 -0
  69. package/docs/QUERY_OPTIMIZATION.md +370 -0
  70. package/docs/SEED_CONNECTIONS.md +468 -0
  71. package/docs/SQL_ALIAS_COMPLETION.md +190 -0
  72. package/docs/STORAGE_ARCHITECTURE.md +565 -0
  73. package/docs/STORAGE_QUICK_SETUP.md +419 -0
  74. package/docs/TECHNICAL_PLAN.md +36 -0
  75. package/docs/THEMING.md +345 -0
  76. package/docs/adding-a-new-database-provider.md +642 -0
  77. package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +360 -0
  78. package/docs/backlogs/001-INLINE_DATA_EDITING.md +118 -0
  79. package/docs/backlogs/002-DATA_IMPORT.md +215 -0
  80. package/docs/backlogs/003-QUERY_TIME_MACHINE.md +183 -0
  81. package/docs/backlogs/004-AI_DATA_STORYTELLER.md +292 -0
  82. package/docs/backlogs/005-QUERY_PLAYGROUND.md +352 -0
  83. package/docs/backlogs/006-DATA_MASKING.md +418 -0
  84. package/docs/enterprise-features.md +718 -0
  85. package/docs/kubernetes-helm-chart-artifacthub-plan.md +803 -0
  86. package/docs/medium-koyeb-article-en.md +215 -0
  87. package/docs/plans/test-plans.md +445 -0
  88. package/docs/releases/RELEASE.V0.3.0.md +22 -0
  89. package/docs/releases/RELEASE.V0.4.0.md +154 -0
  90. package/docs/releases/RELEASE.V0.5.0.md +252 -0
  91. package/docs/releases/RELEASE_v0.5.6.md +145 -0
  92. package/docs/releases/RELEASE_v0.6.1.md +303 -0
  93. package/docs/releases/RELEASE_v0.6.7.md +292 -0
  94. package/docs/releases/RELEASE_v0.7.0.md +332 -0
  95. package/docs/releases/RELEASE_v0.8.0.md +521 -0
  96. package/docs/sampledb/titanic.sql +1379 -0
  97. package/docs/superpowers/plans/2026-03-25-seed-connections.md +1362 -0
  98. package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +590 -0
  99. package/e2e/admin-dashboard.spec.ts +64 -0
  100. package/e2e/connection-management.spec.ts +58 -0
  101. package/e2e/export.spec.ts +34 -0
  102. package/e2e/login.spec.ts +85 -0
  103. package/e2e/query-execution.spec.ts +35 -0
  104. package/e2e/tab-management.spec.ts +64 -0
  105. package/eslint.config.mjs +28 -0
  106. package/fly.toml +43 -0
  107. package/next.config.ts +32 -0
  108. package/package.json +130 -0
  109. package/playwright.config.ts +34 -0
  110. package/postcss.config.mjs +7 -0
  111. package/public/favicon-32x32.png +0 -0
  112. package/public/favicon.ico +0 -0
  113. package/public/file.svg +1 -0
  114. package/public/globe.svg +1 -0
  115. package/public/logo.svg +32 -0
  116. package/public/next.svg +1 -0
  117. package/public/screenshots/code-generator.png +0 -0
  118. package/public/screenshots/connection-modal.png +0 -0
  119. package/public/screenshots/data-profiler.png +0 -0
  120. package/public/screenshots/erd-diagram.png +0 -0
  121. package/public/screenshots/hero-editor.png +0 -0
  122. package/public/screenshots/nl2sql.png +0 -0
  123. package/public/vercel.svg +1 -0
  124. package/public/window.svg +1 -0
  125. package/render.yaml +58 -0
  126. package/scripts/merge-lcov.mjs +239 -0
  127. package/sonar-project.properties +16 -0
  128. package/src/app/admin/error.tsx +46 -0
  129. package/src/app/admin/page.tsx +10 -0
  130. package/src/app/api/admin/audit/route.ts +52 -0
  131. package/src/app/api/admin/fleet-health/route.ts +81 -0
  132. package/src/app/api/ai/autopilot/route.ts +105 -0
  133. package/src/app/api/ai/chat/route.ts +132 -0
  134. package/src/app/api/ai/describe-schema/route.ts +52 -0
  135. package/src/app/api/ai/explain/route.ts +86 -0
  136. package/src/app/api/ai/impact/route.ts +97 -0
  137. package/src/app/api/ai/index-advisor/route.ts +98 -0
  138. package/src/app/api/ai/nl2sql/route.ts +87 -0
  139. package/src/app/api/ai/query-safety/route.ts +87 -0
  140. package/src/app/api/auth/login/route.ts +62 -0
  141. package/src/app/api/auth/logout/route.ts +25 -0
  142. package/src/app/api/auth/me/route.ts +10 -0
  143. package/src/app/api/auth/oidc/callback/route.ts +82 -0
  144. package/src/app/api/auth/oidc/login/route.ts +43 -0
  145. package/src/app/api/connections/managed/route.ts +35 -0
  146. package/src/app/api/db/cancel/route.ts +42 -0
  147. package/src/app/api/db/disconnect/route.ts +28 -0
  148. package/src/app/api/db/health/route.ts +49 -0
  149. package/src/app/api/db/maintenance/route.ts +72 -0
  150. package/src/app/api/db/monitoring/route.ts +62 -0
  151. package/src/app/api/db/multi-query/route.ts +116 -0
  152. package/src/app/api/db/pool-stats/route.ts +37 -0
  153. package/src/app/api/db/profile/route.ts +144 -0
  154. package/src/app/api/db/provider-meta/route.ts +49 -0
  155. package/src/app/api/db/query/route.ts +50 -0
  156. package/src/app/api/db/schema/route.ts +47 -0
  157. package/src/app/api/db/schema-snapshot/route.ts +42 -0
  158. package/src/app/api/db/test-connection/route.ts +55 -0
  159. package/src/app/api/db/transaction/route.ts +111 -0
  160. package/src/app/api/storage/[collection]/route.ts +67 -0
  161. package/src/app/api/storage/config/route.ts +17 -0
  162. package/src/app/api/storage/migrate/route.ts +45 -0
  163. package/src/app/api/storage/route.ts +32 -0
  164. package/src/app/error.tsx +49 -0
  165. package/src/app/global-error.tsx +55 -0
  166. package/src/app/globals.css +146 -0
  167. package/src/app/icon.svg +42 -0
  168. package/src/app/layout.tsx +34 -0
  169. package/src/app/login/login-form.tsx +301 -0
  170. package/src/app/login/page.tsx +11 -0
  171. package/src/app/monitoring/page.tsx +8 -0
  172. package/src/app/not-found.tsx +29 -0
  173. package/src/app/page.tsx +5 -0
  174. package/src/components/AIAutopilotPanel.tsx +238 -0
  175. package/src/components/CodeGenerator.tsx +271 -0
  176. package/src/components/CommandPalette.tsx +227 -0
  177. package/src/components/ConnectionModal.tsx +759 -0
  178. package/src/components/CreateTableModal.tsx +281 -0
  179. package/src/components/DataCharts.tsx +962 -0
  180. package/src/components/DataImportModal.tsx +582 -0
  181. package/src/components/DataProfiler.tsx +335 -0
  182. package/src/components/DatabaseDocs.tsx +251 -0
  183. package/src/components/MaskingSettings.tsx +414 -0
  184. package/src/components/MobileNav.tsx +50 -0
  185. package/src/components/NL2SQLPanel.tsx +281 -0
  186. package/src/components/PivotTable.tsx +257 -0
  187. package/src/components/QueryEditor.tsx +760 -0
  188. package/src/components/QueryHistory.tsx +344 -0
  189. package/src/components/QuerySafetyDialog.tsx +290 -0
  190. package/src/components/ResultsGrid.tsx +644 -0
  191. package/src/components/SaveQueryModal.tsx +104 -0
  192. package/src/components/SavedQueries.tsx +128 -0
  193. package/src/components/SchemaDiagram.tsx +473 -0
  194. package/src/components/SchemaDiff.tsx +473 -0
  195. package/src/components/SnapshotTimeline.tsx +116 -0
  196. package/src/components/Studio.tsx +639 -0
  197. package/src/components/TestDataGenerator.tsx +261 -0
  198. package/src/components/VisualExplain.tsx +820 -0
  199. package/src/components/admin/AdminDashboard.tsx +163 -0
  200. package/src/components/admin/tabs/AuditTab.tsx +531 -0
  201. package/src/components/admin/tabs/MonitoringEmbed.tsx +11 -0
  202. package/src/components/admin/tabs/OperationsTab.tsx +646 -0
  203. package/src/components/admin/tabs/OverviewTab.tsx +1328 -0
  204. package/src/components/admin/tabs/SecurityTab.tsx +284 -0
  205. package/src/components/community-section.tsx +92 -0
  206. package/src/components/icons/db-icons.tsx +84 -0
  207. package/src/components/libredb-logo.tsx +61 -0
  208. package/src/components/monitoring/MonitoringDashboard.tsx +345 -0
  209. package/src/components/monitoring/tabs/MetricChart.tsx +82 -0
  210. package/src/components/monitoring/tabs/OverviewTab.tsx +263 -0
  211. package/src/components/monitoring/tabs/PerformanceTab.tsx +254 -0
  212. package/src/components/monitoring/tabs/PoolTab.tsx +174 -0
  213. package/src/components/monitoring/tabs/QueriesTab.tsx +287 -0
  214. package/src/components/monitoring/tabs/SessionsTab.tsx +316 -0
  215. package/src/components/monitoring/tabs/StorageTab.tsx +335 -0
  216. package/src/components/monitoring/tabs/TablesTab.tsx +300 -0
  217. package/src/components/results-grid/ResultCard.tsx +111 -0
  218. package/src/components/results-grid/RowDetailSheet.tsx +178 -0
  219. package/src/components/results-grid/StatsBar.tsx +201 -0
  220. package/src/components/results-grid/index.ts +1 -0
  221. package/src/components/results-grid/utils.ts +23 -0
  222. package/src/components/schema-explorer/ColumnList.tsx +53 -0
  223. package/src/components/schema-explorer/SchemaExplorer.tsx +182 -0
  224. package/src/components/schema-explorer/TableItem.tsx +210 -0
  225. package/src/components/schema-explorer/index.ts +1 -0
  226. package/src/components/sidebar/ConnectionItem.tsx +105 -0
  227. package/src/components/sidebar/ConnectionsList.tsx +62 -0
  228. package/src/components/sidebar/Sidebar.tsx +130 -0
  229. package/src/components/sidebar/index.ts +2 -0
  230. package/src/components/studio/BottomPanel.tsx +286 -0
  231. package/src/components/studio/QueryToolbar.tsx +180 -0
  232. package/src/components/studio/StudioDesktopHeader.tsx +114 -0
  233. package/src/components/studio/StudioMobileHeader.tsx +340 -0
  234. package/src/components/studio/StudioTabBar.tsx +82 -0
  235. package/src/components/studio/index.ts +5 -0
  236. package/src/components/ui/accordion.tsx +66 -0
  237. package/src/components/ui/alert-dialog.tsx +157 -0
  238. package/src/components/ui/alert.tsx +66 -0
  239. package/src/components/ui/aspect-ratio.tsx +11 -0
  240. package/src/components/ui/avatar.tsx +53 -0
  241. package/src/components/ui/badge.tsx +46 -0
  242. package/src/components/ui/breadcrumb.tsx +109 -0
  243. package/src/components/ui/button-group.tsx +83 -0
  244. package/src/components/ui/button.tsx +60 -0
  245. package/src/components/ui/calendar.tsx +216 -0
  246. package/src/components/ui/card.tsx +92 -0
  247. package/src/components/ui/carousel.tsx +241 -0
  248. package/src/components/ui/chart.tsx +357 -0
  249. package/src/components/ui/checkbox.tsx +32 -0
  250. package/src/components/ui/collapsible.tsx +33 -0
  251. package/src/components/ui/command.tsx +184 -0
  252. package/src/components/ui/context-menu.tsx +252 -0
  253. package/src/components/ui/dialog.tsx +143 -0
  254. package/src/components/ui/drawer.tsx +135 -0
  255. package/src/components/ui/dropdown-menu.tsx +257 -0
  256. package/src/components/ui/empty.tsx +104 -0
  257. package/src/components/ui/field.tsx +248 -0
  258. package/src/components/ui/form.tsx +167 -0
  259. package/src/components/ui/hover-card.tsx +44 -0
  260. package/src/components/ui/input-group.tsx +170 -0
  261. package/src/components/ui/input-otp.tsx +77 -0
  262. package/src/components/ui/input.tsx +21 -0
  263. package/src/components/ui/item.tsx +193 -0
  264. package/src/components/ui/kbd.tsx +28 -0
  265. package/src/components/ui/label.tsx +24 -0
  266. package/src/components/ui/menubar.tsx +276 -0
  267. package/src/components/ui/navigation-menu.tsx +168 -0
  268. package/src/components/ui/pagination.tsx +127 -0
  269. package/src/components/ui/popover.tsx +48 -0
  270. package/src/components/ui/progress.tsx +31 -0
  271. package/src/components/ui/radio-group.tsx +45 -0
  272. package/src/components/ui/resizable.tsx +56 -0
  273. package/src/components/ui/scroll-area.tsx +58 -0
  274. package/src/components/ui/select.tsx +187 -0
  275. package/src/components/ui/separator.tsx +28 -0
  276. package/src/components/ui/sheet.tsx +139 -0
  277. package/src/components/ui/sidebar.tsx +726 -0
  278. package/src/components/ui/skeleton.tsx +13 -0
  279. package/src/components/ui/slider.tsx +63 -0
  280. package/src/components/ui/sonner.tsx +40 -0
  281. package/src/components/ui/spinner.tsx +16 -0
  282. package/src/components/ui/switch.tsx +31 -0
  283. package/src/components/ui/table.tsx +116 -0
  284. package/src/components/ui/tabs.tsx +66 -0
  285. package/src/components/ui/textarea.tsx +18 -0
  286. package/src/components/ui/toggle-group.tsx +83 -0
  287. package/src/components/ui/toggle.tsx +47 -0
  288. package/src/components/ui/tooltip.tsx +61 -0
  289. package/src/exports/components.ts +15 -0
  290. package/src/exports/index.ts +4 -0
  291. package/src/exports/providers.ts +4 -0
  292. package/src/exports/types.ts +26 -0
  293. package/src/hooks/use-ai-chat.ts +182 -0
  294. package/src/hooks/use-all-connections.ts +66 -0
  295. package/src/hooks/use-api-call.ts +71 -0
  296. package/src/hooks/use-auth.ts +51 -0
  297. package/src/hooks/use-connection-form.ts +349 -0
  298. package/src/hooks/use-connection-manager.ts +169 -0
  299. package/src/hooks/use-connection-payload.ts +15 -0
  300. package/src/hooks/use-inline-editing.ts +109 -0
  301. package/src/hooks/use-mobile.ts +20 -0
  302. package/src/hooks/use-monitoring-data.ts +270 -0
  303. package/src/hooks/use-provider-metadata.ts +62 -0
  304. package/src/hooks/use-query-execution.ts +478 -0
  305. package/src/hooks/use-storage-sync.ts +259 -0
  306. package/src/hooks/use-tab-manager.ts +231 -0
  307. package/src/hooks/use-toast.ts +20 -0
  308. package/src/hooks/use-transaction-control.ts +64 -0
  309. package/src/lib/api/error-codes.ts +30 -0
  310. package/src/lib/api/errors.ts +236 -0
  311. package/src/lib/api/with-error-handler.ts +41 -0
  312. package/src/lib/audit.ts +105 -0
  313. package/src/lib/auth.ts +87 -0
  314. package/src/lib/connection-string-parser.ts +172 -0
  315. package/src/lib/data-masking.ts +385 -0
  316. package/src/lib/db/base-provider.ts +325 -0
  317. package/src/lib/db/errors.ts +317 -0
  318. package/src/lib/db/factory.ts +324 -0
  319. package/src/lib/db/index.ts +123 -0
  320. package/src/lib/db/providers/document/index.ts +6 -0
  321. package/src/lib/db/providers/document/mongodb.ts +992 -0
  322. package/src/lib/db/providers/keyvalue/redis.ts +554 -0
  323. package/src/lib/db/providers/sql/index.ts +11 -0
  324. package/src/lib/db/providers/sql/mssql.ts +1065 -0
  325. package/src/lib/db/providers/sql/mysql.ts +978 -0
  326. package/src/lib/db/providers/sql/oracle.ts +1044 -0
  327. package/src/lib/db/providers/sql/postgres.ts +1179 -0
  328. package/src/lib/db/providers/sql/sql-base.ts +174 -0
  329. package/src/lib/db/providers/sql/sqlite.ts +721 -0
  330. package/src/lib/db/types.ts +437 -0
  331. package/src/lib/db/utils/pool-manager.ts +287 -0
  332. package/src/lib/db/utils/query-limiter.ts +239 -0
  333. package/src/lib/db-ui-config.ts +86 -0
  334. package/src/lib/editor/mongodb-completions.ts +172 -0
  335. package/src/lib/editor/sql-completions.ts +280 -0
  336. package/src/lib/llm/base-provider.ts +117 -0
  337. package/src/lib/llm/factory.ts +102 -0
  338. package/src/lib/llm/index.ts +90 -0
  339. package/src/lib/llm/providers/custom.ts +181 -0
  340. package/src/lib/llm/providers/gemini.ts +126 -0
  341. package/src/lib/llm/providers/ollama.ts +154 -0
  342. package/src/lib/llm/providers/openai.ts +146 -0
  343. package/src/lib/llm/types.ts +173 -0
  344. package/src/lib/llm/utils/config.ts +187 -0
  345. package/src/lib/llm/utils/retry.ts +119 -0
  346. package/src/lib/llm/utils/streaming.ts +202 -0
  347. package/src/lib/logger.ts +127 -0
  348. package/src/lib/monitoring-thresholds.ts +44 -0
  349. package/src/lib/oidc.ts +262 -0
  350. package/src/lib/query-generators.ts +61 -0
  351. package/src/lib/schema-diff/diff-engine.ts +273 -0
  352. package/src/lib/schema-diff/migration-generator.ts +208 -0
  353. package/src/lib/schema-diff/types.ts +55 -0
  354. package/src/lib/seed/config-loader.ts +79 -0
  355. package/src/lib/seed/connection-filter.ts +49 -0
  356. package/src/lib/seed/credential-resolver.ts +62 -0
  357. package/src/lib/seed/index.ts +40 -0
  358. package/src/lib/seed/resolve-connection.ts +57 -0
  359. package/src/lib/seed/types.ts +69 -0
  360. package/src/lib/sql/alias-extractor.ts +267 -0
  361. package/src/lib/sql/index.ts +8 -0
  362. package/src/lib/sql/statement-splitter.ts +167 -0
  363. package/src/lib/sql/types.ts +40 -0
  364. package/src/lib/ssh/tunnel.ts +142 -0
  365. package/src/lib/storage/factory.ts +84 -0
  366. package/src/lib/storage/index.ts +14 -0
  367. package/src/lib/storage/local-storage.ts +99 -0
  368. package/src/lib/storage/providers/postgres.ts +225 -0
  369. package/src/lib/storage/providers/sqlite.ts +153 -0
  370. package/src/lib/storage/storage-facade.ts +272 -0
  371. package/src/lib/storage/types.ts +75 -0
  372. package/src/lib/time-series-buffer.ts +58 -0
  373. package/src/lib/types.ts +173 -0
  374. package/src/lib/utils.ts +6 -0
  375. package/src/proxy.ts +104 -0
  376. package/src/types/db-drivers.d.ts +23 -0
  377. package/src/types/html2canvas.d.ts +9 -0
  378. package/tests/api/admin/audit.test.ts +178 -0
  379. package/tests/api/admin/fleet-health.test.ts +183 -0
  380. package/tests/api/ai/autopilot.test.ts +174 -0
  381. package/tests/api/ai/chat.test.ts +250 -0
  382. package/tests/api/ai/describe-schema.test.ts +266 -0
  383. package/tests/api/ai/explain.test.ts +199 -0
  384. package/tests/api/ai/impact.test.ts +168 -0
  385. package/tests/api/ai/index-advisor.test.ts +171 -0
  386. package/tests/api/ai/nl2sql.test.ts +202 -0
  387. package/tests/api/ai/query-safety.test.ts +196 -0
  388. package/tests/api/auth/login.test.ts +170 -0
  389. package/tests/api/auth/logout.test.ts +140 -0
  390. package/tests/api/auth/me.test.ts +73 -0
  391. package/tests/api/auth/oidc-callback.test.ts +215 -0
  392. package/tests/api/auth/oidc-login.test.ts +127 -0
  393. package/tests/api/db/cancel.test.ts +198 -0
  394. package/tests/api/db/disconnect.test.ts +124 -0
  395. package/tests/api/db/health.test.ts +222 -0
  396. package/tests/api/db/maintenance.test.ts +263 -0
  397. package/tests/api/db/monitoring.test.ts +221 -0
  398. package/tests/api/db/multi-query.test.ts +316 -0
  399. package/tests/api/db/pool-stats.test.ts +135 -0
  400. package/tests/api/db/profile.test.ts +330 -0
  401. package/tests/api/db/provider-meta.test.ts +193 -0
  402. package/tests/api/db/query.test.ts +314 -0
  403. package/tests/api/db/schema-snapshot.test.ts +170 -0
  404. package/tests/api/db/schema.test.ts +191 -0
  405. package/tests/api/db/test-connection.test.ts +185 -0
  406. package/tests/api/db/transaction.test.ts +314 -0
  407. package/tests/api/proxy.test.ts +191 -0
  408. package/tests/api/seed/managed-route.test.ts +113 -0
  409. package/tests/api/storage/config.test.ts +42 -0
  410. package/tests/api/storage/storage-routes.test.ts +309 -0
  411. package/tests/components/AIAutopilotPanel.test.tsx +756 -0
  412. package/tests/components/AdminPage.test.tsx +33 -0
  413. package/tests/components/CodeGenerator.test.tsx +182 -0
  414. package/tests/components/CommandPalette.test.tsx +428 -0
  415. package/tests/components/CommunitySection.test.tsx +91 -0
  416. package/tests/components/ConnectionModal.mobile.test.tsx +284 -0
  417. package/tests/components/ConnectionModal.test.tsx +570 -0
  418. package/tests/components/CreateTableModal.test.tsx +383 -0
  419. package/tests/components/DataCharts.test.tsx +739 -0
  420. package/tests/components/DataImportModal.test.tsx +751 -0
  421. package/tests/components/DataProfiler.test.tsx +589 -0
  422. package/tests/components/DatabaseDocs.test.tsx +353 -0
  423. package/tests/components/LoginPage.test.tsx +163 -0
  424. package/tests/components/LoginPageOIDC.test.tsx +92 -0
  425. package/tests/components/MaskingSettings.test.tsx +498 -0
  426. package/tests/components/MobileNav.test.tsx +30 -0
  427. package/tests/components/MonitoringPage.test.tsx +32 -0
  428. package/tests/components/NL2SQLPanel.test.tsx +621 -0
  429. package/tests/components/Page.test.tsx +33 -0
  430. package/tests/components/PivotTable.test.tsx +350 -0
  431. package/tests/components/QueryEditor.test.tsx +1730 -0
  432. package/tests/components/QueryHistory.test.tsx +572 -0
  433. package/tests/components/QuerySafetyDialog.test.tsx +586 -0
  434. package/tests/components/ResultsGrid.test.tsx +804 -0
  435. package/tests/components/RootLayout.test.tsx +83 -0
  436. package/tests/components/SaveQueryModal.test.tsx +25 -0
  437. package/tests/components/SavedQueries.test.tsx +43 -0
  438. package/tests/components/SchemaDiagram.test.tsx +1034 -0
  439. package/tests/components/SchemaDiff.test.tsx +906 -0
  440. package/tests/components/SnapshotTimeline.test.tsx +174 -0
  441. package/tests/components/Studio.test.tsx +1030 -0
  442. package/tests/components/TestDataGenerator.test.tsx +291 -0
  443. package/tests/components/VisualExplain.test.tsx +704 -0
  444. package/tests/components/admin/AdminDashboard.test.tsx +205 -0
  445. package/tests/components/admin/AuditTab.test.tsx +220 -0
  446. package/tests/components/admin/MonitoringEmbed.test.tsx +58 -0
  447. package/tests/components/admin/OperationsTab.test.tsx +975 -0
  448. package/tests/components/admin/OverviewTab.test.tsx +254 -0
  449. package/tests/components/admin/SecurityTab.test.tsx +467 -0
  450. package/tests/components/monitoring/MetricChart.test.tsx +111 -0
  451. package/tests/components/monitoring/MonitoringDashboard.test.tsx +259 -0
  452. package/tests/components/monitoring/OverviewTab.test.tsx +78 -0
  453. package/tests/components/monitoring/PerformanceTab.test.tsx +87 -0
  454. package/tests/components/monitoring/PoolTab.test.tsx +42 -0
  455. package/tests/components/monitoring/QueriesTab.test.tsx +80 -0
  456. package/tests/components/monitoring/SessionsTab.test.tsx +154 -0
  457. package/tests/components/monitoring/StorageTab.test.tsx +127 -0
  458. package/tests/components/monitoring/TablesTab.test.tsx +153 -0
  459. package/tests/components/results-grid/ResultCard.test.tsx +105 -0
  460. package/tests/components/results-grid/RowDetailSheet.test.tsx +308 -0
  461. package/tests/components/results-grid/StatsBar.test.tsx +162 -0
  462. package/tests/components/schema-explorer/ColumnList.test.tsx +151 -0
  463. package/tests/components/schema-explorer/SchemaExplorer.test.tsx +461 -0
  464. package/tests/components/schema-explorer/TableItem.test.tsx +415 -0
  465. package/tests/components/sidebar/ConnectionItem.test.tsx +201 -0
  466. package/tests/components/sidebar/ConnectionsList.test.tsx +176 -0
  467. package/tests/components/sidebar/Sidebar.test.tsx +187 -0
  468. package/tests/components/studio/BottomPanel.test.tsx +383 -0
  469. package/tests/components/studio/QueryToolbar.test.tsx +321 -0
  470. package/tests/components/studio/StudioDesktopHeader.test.tsx +377 -0
  471. package/tests/components/studio/StudioMobileHeader.test.tsx +198 -0
  472. package/tests/components/studio/StudioTabBar.test.tsx +331 -0
  473. package/tests/fixtures/connections.ts +96 -0
  474. package/tests/fixtures/masking-configs.ts +86 -0
  475. package/tests/fixtures/query-results.ts +71 -0
  476. package/tests/fixtures/schemas.ts +64 -0
  477. package/tests/fixtures/seed-connections/invalid-config.yaml +7 -0
  478. package/tests/fixtures/seed-connections/minimal-config.yaml +8 -0
  479. package/tests/fixtures/seed-connections/mixed-credentials.yaml +23 -0
  480. package/tests/fixtures/seed-connections/multi-role-config.yaml +30 -0
  481. package/tests/fixtures/seed-connections/valid-config.json +15 -0
  482. package/tests/fixtures/seed-connections/valid-config.yaml +51 -0
  483. package/tests/helpers/mock-fetch.ts +59 -0
  484. package/tests/helpers/mock-monaco.ts +112 -0
  485. package/tests/helpers/mock-navigation.ts +28 -0
  486. package/tests/helpers/mock-next.ts +80 -0
  487. package/tests/helpers/mock-provider.ts +133 -0
  488. package/tests/helpers/mock-sonner.ts +29 -0
  489. package/tests/helpers/render-with-providers.tsx +19 -0
  490. package/tests/hooks/use-ai-chat.test.ts +600 -0
  491. package/tests/hooks/use-auth.test.ts +371 -0
  492. package/tests/hooks/use-connection-form.test.ts +743 -0
  493. package/tests/hooks/use-connection-manager.test.ts +466 -0
  494. package/tests/hooks/use-inline-editing.test.ts +321 -0
  495. package/tests/hooks/use-mobile.test.ts +177 -0
  496. package/tests/hooks/use-monitoring-data.test.ts +819 -0
  497. package/tests/hooks/use-provider-metadata.test.ts +228 -0
  498. package/tests/hooks/use-query-execution.test.ts +1212 -0
  499. package/tests/hooks/use-tab-manager.test.ts +756 -0
  500. package/tests/hooks/use-toast.test.ts +74 -0
  501. package/tests/hooks/use-transaction-control.test.ts +211 -0
  502. package/tests/integration/db/mongodb-provider.test.ts +698 -0
  503. package/tests/integration/db/mssql-provider.test.ts +840 -0
  504. package/tests/integration/db/mysql-provider.test.ts +872 -0
  505. package/tests/integration/db/oracle-provider.test.ts +843 -0
  506. package/tests/integration/db/postgres-provider.test.ts +1382 -0
  507. package/tests/integration/db/redis-provider.test.ts +526 -0
  508. package/tests/integration/db/sqlite-provider.test.ts +480 -0
  509. package/tests/integration/seed/seed-pipeline.test.ts +102 -0
  510. package/tests/isolated/factory-singleton.test.ts +150 -0
  511. package/tests/isolated/use-storage-sync.test.ts +389 -0
  512. package/tests/run-components.sh +196 -0
  513. package/tests/setup-dom.ts +58 -0
  514. package/tests/setup.ts +40 -0
  515. package/tests/unit/api-errors.test.ts +210 -0
  516. package/tests/unit/code-generator-functions.test.ts +271 -0
  517. package/tests/unit/components/column-list.test.tsx +190 -0
  518. package/tests/unit/components/data-import-modal.test.tsx +441 -0
  519. package/tests/unit/components/studio-mobile-header.test.tsx +327 -0
  520. package/tests/unit/data-charts-functions.test.ts +496 -0
  521. package/tests/unit/data-import-functions.test.ts +320 -0
  522. package/tests/unit/data-import-utils.test.ts +125 -0
  523. package/tests/unit/db/base-provider.test.ts +517 -0
  524. package/tests/unit/db/errors.test.ts +403 -0
  525. package/tests/unit/db/factory.test.ts +436 -0
  526. package/tests/unit/db/pool-manager.test.ts +440 -0
  527. package/tests/unit/db/query-limiter.test.ts +387 -0
  528. package/tests/unit/db/sql-base.test.ts +438 -0
  529. package/tests/unit/lib/api/error-codes.test.ts +39 -0
  530. package/tests/unit/lib/audit.test.ts +326 -0
  531. package/tests/unit/lib/auth.test.ts +146 -0
  532. package/tests/unit/lib/connection-string-parser.test.ts +424 -0
  533. package/tests/unit/lib/data-masking.test.ts +583 -0
  534. package/tests/unit/lib/db-icons.test.tsx +41 -0
  535. package/tests/unit/lib/monitoring-thresholds.test.ts +133 -0
  536. package/tests/unit/lib/oidc.test.ts +509 -0
  537. package/tests/unit/lib/query-generators.test.ts +127 -0
  538. package/tests/unit/lib/storage/factory.test.ts +71 -0
  539. package/tests/unit/lib/storage/local-storage.test.ts +114 -0
  540. package/tests/unit/lib/storage/providers/postgres.test.ts +312 -0
  541. package/tests/unit/lib/storage/providers/sqlite.test.ts +232 -0
  542. package/tests/unit/lib/storage/storage-facade-extended.test.ts +331 -0
  543. package/tests/unit/lib/storage/storage-facade.test.ts +184 -0
  544. package/tests/unit/lib/storage.test.ts +317 -0
  545. package/tests/unit/lib/time-series-buffer.test.ts +212 -0
  546. package/tests/unit/lib/utils.test.ts +24 -0
  547. package/tests/unit/llm/base-provider.test.ts +238 -0
  548. package/tests/unit/llm/config.test.ts +262 -0
  549. package/tests/unit/llm/custom-provider.test.ts +281 -0
  550. package/tests/unit/llm/gemini-provider.test.ts +248 -0
  551. package/tests/unit/llm/llm-factory.test.ts +155 -0
  552. package/tests/unit/llm/ollama-provider.test.ts +288 -0
  553. package/tests/unit/llm/openai-provider.test.ts +324 -0
  554. package/tests/unit/llm/retry.test.ts +180 -0
  555. package/tests/unit/llm/streaming.test.ts +355 -0
  556. package/tests/unit/logger.test.ts +198 -0
  557. package/tests/unit/mongodb-completions.test.ts +516 -0
  558. package/tests/unit/pivot-table-functions.test.ts +76 -0
  559. package/tests/unit/query-cancelled-error.test.ts +81 -0
  560. package/tests/unit/schema-diff/diff-engine.test.ts +367 -0
  561. package/tests/unit/schema-diff/migration-generator.test.ts +513 -0
  562. package/tests/unit/seed/config-loader.test.ts +73 -0
  563. package/tests/unit/seed/connection-filter.test.ts +91 -0
  564. package/tests/unit/seed/credential-resolver.test.ts +85 -0
  565. package/tests/unit/seed/index.test.ts +72 -0
  566. package/tests/unit/seed/resolve-connection.test.ts +74 -0
  567. package/tests/unit/seed/types.test.ts +129 -0
  568. package/tests/unit/sql/alias-extractor.test.ts +444 -0
  569. package/tests/unit/sql/statement-splitter.test.ts +348 -0
  570. package/tests/unit/sql-completions.test.ts +463 -0
  571. package/tests/unit/ssh-tunnel.test.ts +465 -0
  572. package/tsconfig.json +42 -0
package/render.yaml ADDED
@@ -0,0 +1,58 @@
1
+ # Render Blueprint - Infrastructure as Code
2
+ # https://render.com/docs/blueprint-spec
3
+
4
+ services:
5
+ - type: web
6
+ name: libredb-studio
7
+ runtime: docker
8
+ # Use Dockerfile for building
9
+ dockerfilePath: ./Dockerfile
10
+ # Health check endpoint
11
+ healthCheckPath: /api/db/health
12
+ # Auto-deploy on push to main
13
+ autoDeploy: true
14
+ # Environment variables (set in Render Dashboard for secrets)
15
+ envVars:
16
+ - key: NODE_ENV
17
+ value: production
18
+ - key: PORT
19
+ value: 3000
20
+ # Authentication - Set these in Render Dashboard
21
+ - key: ADMIN_EMAIL
22
+ sync: false
23
+ - key: ADMIN_PASSWORD
24
+ sync: false # Must be set manually in dashboard
25
+ - key: USER_EMAIL
26
+ sync: false
27
+ - key: USER_PASSWORD
28
+ sync: false
29
+ - key: JWT_SECRET
30
+ sync: false # Must be at least 32 characters
31
+ # OIDC / SSO Configuration - Set in Dashboard
32
+ - key: NEXT_PUBLIC_AUTH_PROVIDER
33
+ value: oidc # or "local" for password-based auth
34
+ - key: OIDC_ISSUER
35
+ sync: false
36
+ - key: OIDC_CLIENT_ID
37
+ sync: false
38
+ - key: OIDC_CLIENT_SECRET
39
+ sync: false
40
+ - key: OIDC_SCOPE
41
+ value: openid profile email
42
+ - key: OIDC_ROLE_CLAIM
43
+ sync: false
44
+ - key: OIDC_ADMIN_ROLES
45
+ value: admin
46
+ # AI Configuration (Optional) - Set in Dashboard
47
+ - key: LLM_PROVIDER
48
+ value: gemini
49
+ - key: LLM_API_KEY
50
+ sync: false
51
+ - key: LLM_MODEL
52
+ value: gemini-2.5-flash
53
+ # Disk for persistent storage (optional)
54
+ # disk:
55
+ # name: libredb-data
56
+ # mountPath: /app/data
57
+ # sizeGB: 1
58
+
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ function ensureParentDir(filePath) {
7
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
8
+ }
9
+
10
+ function parseLcov(content) {
11
+ const records = [];
12
+ const rawRecords = content.split("end_of_record");
13
+
14
+ for (const raw of rawRecords) {
15
+ const lines = raw
16
+ .split("\n")
17
+ .map((line) => line.trim())
18
+ .filter(Boolean);
19
+
20
+ if (lines.length === 0) {
21
+ continue;
22
+ }
23
+
24
+ const record = {
25
+ sf: "",
26
+ functions: new Map(),
27
+ functionHits: new Map(),
28
+ lines: new Map(),
29
+ branches: new Map(),
30
+ };
31
+
32
+ for (const line of lines) {
33
+ if (line.startsWith("SF:")) {
34
+ record.sf = line.slice(3);
35
+ continue;
36
+ }
37
+
38
+ if (line.startsWith("FN:")) {
39
+ const payload = line.slice(3);
40
+ const commaIndex = payload.indexOf(",");
41
+ if (commaIndex >= 0) {
42
+ const lineNumber = Number(payload.slice(0, commaIndex));
43
+ const functionName = payload.slice(commaIndex + 1);
44
+ record.functions.set(functionName, Number.isFinite(lineNumber) ? lineNumber : 0);
45
+ }
46
+ continue;
47
+ }
48
+
49
+ if (line.startsWith("FNDA:")) {
50
+ const payload = line.slice(5);
51
+ const commaIndex = payload.indexOf(",");
52
+ if (commaIndex >= 0) {
53
+ const hits = Number(payload.slice(0, commaIndex)) || 0;
54
+ const functionName = payload.slice(commaIndex + 1);
55
+ record.functionHits.set(functionName, hits);
56
+ }
57
+ continue;
58
+ }
59
+
60
+ if (line.startsWith("DA:")) {
61
+ const payload = line.slice(3);
62
+ const commaIndex = payload.indexOf(",");
63
+ if (commaIndex >= 0) {
64
+ const lineNumber = Number(payload.slice(0, commaIndex));
65
+ const hits = Number(payload.slice(commaIndex + 1)) || 0;
66
+ if (Number.isFinite(lineNumber)) {
67
+ record.lines.set(lineNumber, hits);
68
+ }
69
+ }
70
+ continue;
71
+ }
72
+
73
+ if (line.startsWith("BRDA:")) {
74
+ const payload = line.slice(5);
75
+ const [lineNoRaw, blockNoRaw, branchNoRaw, takenRaw] = payload.split(",");
76
+ const lineNo = Number(lineNoRaw);
77
+ const blockNo = Number(blockNoRaw);
78
+ const branchNo = Number(branchNoRaw);
79
+ const key = `${lineNo},${blockNo},${branchNo}`;
80
+ const taken = takenRaw === "-" ? -1 : Number(takenRaw) || 0;
81
+ if (Number.isFinite(lineNo) && Number.isFinite(blockNo) && Number.isFinite(branchNo)) {
82
+ record.branches.set(key, taken);
83
+ }
84
+ }
85
+ }
86
+
87
+ if (record.sf) {
88
+ records.push(record);
89
+ }
90
+ }
91
+
92
+ return records;
93
+ }
94
+
95
+ function mergeRecords(inputRecords) {
96
+ const byFile = new Map();
97
+
98
+ for (const record of inputRecords) {
99
+ const existing = byFile.get(record.sf) || {
100
+ sf: record.sf,
101
+ functions: new Map(),
102
+ functionHits: new Map(),
103
+ lines: new Map(),
104
+ branches: new Map(),
105
+ };
106
+
107
+ for (const [fnName, fnLine] of record.functions.entries()) {
108
+ if (!existing.functions.has(fnName)) {
109
+ existing.functions.set(fnName, fnLine);
110
+ }
111
+ }
112
+
113
+ for (const [fnName, hits] of record.functionHits.entries()) {
114
+ const prevHits = existing.functionHits.get(fnName) || 0;
115
+ existing.functionHits.set(fnName, Math.max(prevHits, hits));
116
+ }
117
+
118
+ for (const [lineNo, hits] of record.lines.entries()) {
119
+ const prevHits = existing.lines.get(lineNo) || 0;
120
+ existing.lines.set(lineNo, Math.max(prevHits, hits));
121
+ }
122
+
123
+ for (const [key, taken] of record.branches.entries()) {
124
+ const prevTaken = existing.branches.has(key) ? existing.branches.get(key) : -1;
125
+ if (prevTaken === -1 && taken !== -1) {
126
+ existing.branches.set(key, taken);
127
+ } else if (prevTaken !== -1 && taken === -1) {
128
+ existing.branches.set(key, prevTaken);
129
+ } else {
130
+ existing.branches.set(key, Math.max(prevTaken, taken));
131
+ }
132
+ }
133
+
134
+ byFile.set(record.sf, existing);
135
+ }
136
+
137
+ return [...byFile.values()].sort((a, b) => a.sf.localeCompare(b.sf));
138
+ }
139
+
140
+ function serializeRecords(records) {
141
+ const chunks = [];
142
+
143
+ for (const record of records) {
144
+ const lines = [];
145
+ lines.push(`SF:${record.sf}`);
146
+
147
+ const sortedFunctions = [...record.functions.entries()].sort((a, b) => a[1] - b[1]);
148
+ for (const [fnName, fnLine] of sortedFunctions) {
149
+ lines.push(`FN:${fnLine},${fnName}`);
150
+ }
151
+
152
+ for (const [fnName] of sortedFunctions) {
153
+ const hits = record.functionHits.get(fnName) || 0;
154
+ lines.push(`FNDA:${hits},${fnName}`);
155
+ }
156
+
157
+ const fnf = sortedFunctions.length;
158
+ const fnh = sortedFunctions.reduce((acc, [fnName]) => acc + ((record.functionHits.get(fnName) || 0) > 0 ? 1 : 0), 0);
159
+ lines.push(`FNF:${fnf}`);
160
+ lines.push(`FNH:${fnh}`);
161
+
162
+ const sortedLineEntries = [...record.lines.entries()].sort((a, b) => a[0] - b[0]);
163
+ for (const [lineNo, hits] of sortedLineEntries) {
164
+ lines.push(`DA:${lineNo},${hits}`);
165
+ }
166
+
167
+ const lf = sortedLineEntries.length;
168
+ const lh = sortedLineEntries.reduce((acc, [, hits]) => acc + (hits > 0 ? 1 : 0), 0);
169
+ lines.push(`LF:${lf}`);
170
+ lines.push(`LH:${lh}`);
171
+
172
+ if (record.branches.size > 0) {
173
+ const sortedBranchEntries = [...record.branches.entries()].sort((a, b) => {
174
+ const [aLine, aBlock, aBranch] = a[0].split(",").map(Number);
175
+ const [bLine, bBlock, bBranch] = b[0].split(",").map(Number);
176
+ if (aLine !== bLine) return aLine - bLine;
177
+ if (aBlock !== bBlock) return aBlock - bBlock;
178
+ return aBranch - bBranch;
179
+ });
180
+
181
+ for (const [key, taken] of sortedBranchEntries) {
182
+ const takenValue = taken < 0 ? "-" : String(taken);
183
+ lines.push(`BRDA:${key},${takenValue}`);
184
+ }
185
+
186
+ const brf = sortedBranchEntries.length;
187
+ const brh = sortedBranchEntries.reduce((acc, [, taken]) => acc + (taken > 0 ? 1 : 0), 0);
188
+ lines.push(`BRF:${brf}`);
189
+ lines.push(`BRH:${brh}`);
190
+ }
191
+
192
+ lines.push("end_of_record");
193
+ chunks.push(lines.join("\n"));
194
+ }
195
+
196
+ return chunks.join("\n");
197
+ }
198
+
199
+ function main() {
200
+ const [, , ...args] = process.argv;
201
+ if (args.length < 3) {
202
+ console.error("Usage: node scripts/merge-lcov.mjs <input1> <input2> [moreInputs...] <output>");
203
+ process.exit(1);
204
+ }
205
+
206
+ const outputPath = args[args.length - 1];
207
+ const inputPaths = args.slice(0, -1);
208
+ const allRecords = [];
209
+
210
+ for (const inputPath of inputPaths) {
211
+ const content = fs.readFileSync(inputPath, "utf8");
212
+ allRecords.push(...parseLcov(content));
213
+ }
214
+
215
+ const merged = mergeRecords(allRecords);
216
+
217
+ // Filter out non-source files that SonarCloud can't resolve:
218
+ // - tests/ and e2e/ directories (test infrastructure, not source)
219
+ // - src/components/ui/ (excluded in sonar.exclusions)
220
+ const filtered = merged.filter((r) => {
221
+ if (!r.sf.startsWith("src/")) return false;
222
+ if (r.sf.startsWith("src/components/ui/")) return false;
223
+ return true;
224
+ });
225
+
226
+ const skipped = merged.length - filtered.length;
227
+ if (skipped > 0) {
228
+ console.log(`Filtered ${skipped} non-source record(s) (tests/, src/components/ui/)`);
229
+ }
230
+
231
+ const serialized = serializeRecords(filtered);
232
+
233
+ ensureParentDir(outputPath);
234
+ fs.writeFileSync(outputPath, serialized ? `${serialized}\n` : "", "utf8");
235
+ console.log(`Merged ${inputPaths.length} LCOV file(s) into ${outputPath}`);
236
+ console.log(`Records: ${merged.length}`);
237
+ }
238
+
239
+ main();
@@ -0,0 +1,16 @@
1
+ sonar.projectKey=libredb_libredb-studio
2
+ sonar.organization=libredb
3
+
4
+ sonar.projectName=libredb-studio
5
+ sonar.projectVersion=0.6.38
6
+
7
+ sonar.sources=src
8
+ sonar.tests=tests,e2e
9
+ sonar.test.inclusions=tests/**/*.test.ts,tests/**/*.test.tsx,e2e/**/*.spec.ts
10
+
11
+ # Exclude only runtime/generated code, NOT test files
12
+ sonar.exclusions=**/node_modules/**,**/.next/**,**/build/**,**/out/**,src/components/ui/**
13
+
14
+ sonar.javascript.lcov.reportPaths=coverage/lcov.info
15
+ sonar.typescript.tsconfigPath=tsconfig.json
16
+ sonar.sourceEncoding=UTF-8
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+
5
+ export default function AdminError({
6
+ error,
7
+ reset,
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ useEffect(() => {
13
+ console.error('[AdminErrorBoundary]', error.message, error.digest);
14
+ }, [error]);
15
+
16
+ return (
17
+ <div className="flex min-h-screen items-center justify-center bg-zinc-950 text-white">
18
+ <div className="max-w-md text-center px-6">
19
+ <h1 className="text-xl font-semibold mb-2">Admin Dashboard Error</h1>
20
+ <p className="text-zinc-400 mb-6 text-sm">
21
+ The admin dashboard encountered an error. You can try again or return
22
+ to the main studio.
23
+ </p>
24
+ {error.digest && (
25
+ <p className="text-zinc-600 text-xs mb-4">
26
+ Error ID: {error.digest}
27
+ </p>
28
+ )}
29
+ <div className="flex gap-3 justify-center">
30
+ <button
31
+ onClick={reset}
32
+ className="px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium transition-colors"
33
+ >
34
+ Try Again
35
+ </button>
36
+ <button
37
+ onClick={() => { window.location.href = '/'; }}
38
+ className="px-5 py-2.5 border border-zinc-700 hover:border-zinc-500 text-zinc-300 rounded-lg text-sm font-medium transition-colors"
39
+ >
40
+ Back to Studio
41
+ </button>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,10 @@
1
+ import { Suspense } from 'react';
2
+ import AdminDashboard from '@/components/admin/AdminDashboard';
3
+
4
+ export default function AdminPage() {
5
+ return (
6
+ <Suspense>
7
+ <AdminDashboard />
8
+ </Suspense>
9
+ );
10
+ }
@@ -0,0 +1,52 @@
1
+ import { getSession } from '@/lib/auth';
2
+ import { NextResponse } from 'next/server';
3
+ import { getServerAuditBuffer, type AuditEventType } from '@/lib/audit';
4
+ import { createErrorResponse } from '@/lib/api/errors';
5
+
6
+ export async function GET(request: Request) {
7
+ try {
8
+ const session = await getSession();
9
+ if (!session || session.role !== 'admin') {
10
+ return NextResponse.json(
11
+ { error: 'Unauthorized. Admin access required.' },
12
+ { status: 403 }
13
+ );
14
+ }
15
+
16
+ const { searchParams } = new URL(request.url);
17
+ const type = searchParams.get('type') as AuditEventType | null;
18
+ const limit = parseInt(searchParams.get('limit') || '100', 10);
19
+
20
+ const buffer = getServerAuditBuffer();
21
+ const events = type
22
+ ? buffer.filter({ type })
23
+ : buffer.getRecent(limit);
24
+
25
+ return NextResponse.json({ events, total: buffer.size });
26
+ } catch (error) {
27
+ return createErrorResponse(error, { route: 'GET /api/admin/audit' });
28
+ }
29
+ }
30
+
31
+ export async function POST(request: Request) {
32
+ const session = await getSession();
33
+ if (!session || session.role !== 'admin') {
34
+ return NextResponse.json(
35
+ { error: 'Unauthorized. Admin access required.' },
36
+ { status: 403 }
37
+ );
38
+ }
39
+
40
+ try {
41
+ const event = await request.json();
42
+ const buffer = getServerAuditBuffer();
43
+ const created = buffer.push({
44
+ ...event,
45
+ user: session.username || 'admin',
46
+ });
47
+
48
+ return NextResponse.json({ event: created });
49
+ } catch (error) {
50
+ return createErrorResponse(error, { route: 'POST /api/admin/audit' });
51
+ }
52
+ }
@@ -0,0 +1,81 @@
1
+ import { getSession } from '@/lib/auth';
2
+ import { NextResponse } from 'next/server';
3
+ import { getOrCreateProvider } from '@/lib/db';
4
+ import type { DatabaseConnection } from '@/lib/types';
5
+ import { createErrorResponse } from '@/lib/api/errors';
6
+ import { resolveConnection } from '@/lib/seed/resolve-connection';
7
+
8
+ export interface FleetHealthItem {
9
+ connectionId: string;
10
+ connectionName: string;
11
+ type: string;
12
+ environment?: string;
13
+ status: 'healthy' | 'degraded' | 'error';
14
+ latencyMs: number;
15
+ activeConnections?: number;
16
+ databaseSize?: string;
17
+ error?: string;
18
+ }
19
+
20
+ export async function POST(request: Request) {
21
+ const session = await getSession();
22
+ if (!session || session.role !== 'admin') {
23
+ return NextResponse.json(
24
+ { error: 'Unauthorized. Admin access required.' },
25
+ { status: 403 }
26
+ );
27
+ }
28
+
29
+ try {
30
+ const { connections } = (await request.json()) as {
31
+ connections: DatabaseConnection[];
32
+ };
33
+
34
+ if (!connections || !Array.isArray(connections)) {
35
+ return NextResponse.json(
36
+ { error: 'connections array is required' },
37
+ { status: 400 }
38
+ );
39
+ }
40
+
41
+ const results: FleetHealthItem[] = await Promise.all(
42
+ connections.map(async (conn): Promise<FleetHealthItem> => {
43
+ const start = Date.now();
44
+ try {
45
+ // Resolve managed seed connections (server-side credential injection)
46
+ const resolved = conn.managed && conn.seedId
47
+ ? await resolveConnection({ connectionId: `seed:${conn.seedId}` }, session!)
48
+ : conn;
49
+ const provider = await getOrCreateProvider(resolved);
50
+ const health = await provider.getHealth();
51
+ const latencyMs = Date.now() - start;
52
+
53
+ return {
54
+ connectionId: conn.id,
55
+ connectionName: conn.name,
56
+ type: conn.type,
57
+ environment: conn.environment,
58
+ status: latencyMs > 5000 ? 'degraded' : 'healthy',
59
+ latencyMs,
60
+ activeConnections: health.activeConnections,
61
+ databaseSize: health.databaseSize,
62
+ };
63
+ } catch (err) {
64
+ return {
65
+ connectionId: conn.id,
66
+ connectionName: conn.name,
67
+ type: conn.type,
68
+ environment: conn.environment,
69
+ status: 'error',
70
+ latencyMs: Date.now() - start,
71
+ error: err instanceof Error ? err.message : 'Connection failed',
72
+ };
73
+ }
74
+ })
75
+ );
76
+
77
+ return NextResponse.json({ results });
78
+ } catch (error) {
79
+ return createErrorResponse(error, { route: 'POST /api/admin/fleet-health' });
80
+ }
81
+ }
@@ -0,0 +1,105 @@
1
+ import { NextRequest } from 'next/server';
2
+ import { createLLMProvider } from '@/lib/llm';
3
+ import { createErrorResponse } from '@/lib/api/errors';
4
+
5
+ function buildAutopilotPrompt(databaseType: string): string {
6
+ return `You are a Database Performance Autopilot for ${databaseType || 'PostgreSQL'}. You combine slow query analysis, execution plans, table statistics, and index usage into a comprehensive optimization report.
7
+
8
+ OUTPUT FORMAT (markdown):
9
+
10
+ ## Performance Score
11
+ Give a score out of 100 based on the overall health metrics. Use emoji: >=80 green, 60-79 yellow, <60 red.
12
+
13
+ ## Critical Issues
14
+ Issues that need immediate attention (blocking queries, high bloat, cache miss rates).
15
+
16
+ ## Top Slow Queries
17
+ For each slow query (top 5):
18
+ ### Query #N
19
+ - **Avg Time:** Xms (Y calls)
20
+ - **Issue:** What's making it slow
21
+ - **Fix:**
22
+ \`\`\`sql
23
+ -- Optimized version or CREATE INDEX suggestion
24
+ \`\`\`
25
+
26
+ ## Index Recommendations
27
+ Consolidated index suggestions from the slow query analysis.
28
+
29
+ ## Maintenance Tasks
30
+ Recommended VACUUM, ANALYZE, REINDEX operations with priority.
31
+
32
+ ## Configuration Suggestions
33
+ Any ${databaseType} configuration parameters that might improve performance based on the metrics.
34
+
35
+ ## Action Plan
36
+ Numbered priority list of what to do first, second, third.
37
+
38
+ GUIDELINES:
39
+ - Be actionable: every recommendation should have a concrete SQL command
40
+ - Prioritize by impact: fix the biggest bottleneck first
41
+ - Consider trade-offs: new indexes use space, VACUUM needs downtime
42
+ - For ${databaseType}, use database-specific features and best practices
43
+ `;
44
+ }
45
+
46
+ export async function POST(req: NextRequest) {
47
+ try {
48
+ const {
49
+ slowQueries,
50
+ indexStats,
51
+ tableStats,
52
+ performanceMetrics,
53
+ overview,
54
+ schemaContext,
55
+ databaseType
56
+ } = await req.json();
57
+
58
+ const provider = await createLLMProvider();
59
+ const systemPrompt = buildAutopilotPrompt(databaseType);
60
+
61
+ const parts: string[] = [];
62
+
63
+ if (overview) {
64
+ parts.push('## Database Overview\n' + JSON.stringify(overview, null, 2));
65
+ }
66
+
67
+ if (performanceMetrics) {
68
+ parts.push('## Performance Metrics\n' + JSON.stringify(performanceMetrics, null, 2));
69
+ }
70
+
71
+ if (slowQueries?.length) {
72
+ parts.push('## Slow Queries (Top 20)\n' + JSON.stringify(slowQueries.slice(0, 20), null, 2));
73
+ }
74
+
75
+ if (indexStats?.length) {
76
+ parts.push('## Index Statistics\n' + JSON.stringify(indexStats.slice(0, 50), null, 2));
77
+ }
78
+
79
+ if (tableStats?.length) {
80
+ parts.push('## Table Statistics\n' + JSON.stringify(tableStats.slice(0, 30), null, 2));
81
+ }
82
+
83
+ if (schemaContext) {
84
+ parts.push('## Schema\n' + schemaContext.substring(0, 3000));
85
+ }
86
+
87
+ const userMessage = `Analyze the following database metrics and provide a comprehensive performance optimization report:\n\n${parts.join('\n\n')}`;
88
+
89
+ const stream = await provider.stream({
90
+ messages: [
91
+ { role: 'system', content: systemPrompt },
92
+ { role: 'user', content: userMessage },
93
+ ],
94
+ });
95
+
96
+ return new Response(stream, {
97
+ headers: {
98
+ 'Content-Type': 'text/plain; charset=utf-8',
99
+ 'Transfer-Encoding': 'chunked',
100
+ },
101
+ });
102
+ } catch (error) {
103
+ return createErrorResponse(error, { route: 'api/ai/autopilot' });
104
+ }
105
+ }