@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
@@ -0,0 +1,721 @@
1
+ /**
2
+ * SQLite Database Provider
3
+ * File-based SQLite support - Uses bun:sqlite when available
4
+ *
5
+ * Note: SQLite is primarily for local development. Cloud deployments
6
+ * typically use PostgreSQL or MySQL instead.
7
+ */
8
+
9
+ import { SQLBaseProvider } from './sql-base';
10
+ import {
11
+ type DatabaseConnection,
12
+ type TableSchema,
13
+ type QueryResult,
14
+ type HealthInfo,
15
+ type MaintenanceType,
16
+ type MaintenanceResult,
17
+ type ProviderOptions,
18
+ type ProviderCapabilities,
19
+ type DatabaseOverview,
20
+ type PerformanceMetrics,
21
+ type SlowQueryStats,
22
+ type ActiveSessionDetails,
23
+ type TableStats,
24
+ type IndexStats,
25
+ type StorageStats,
26
+ } from '../../types';
27
+ import {
28
+ DatabaseConfigError,
29
+ ConnectionError,
30
+ QueryError,
31
+ mapDatabaseError,
32
+ } from '../../errors';
33
+ import { formatBytes } from '../../utils/pool-manager';
34
+ import * as fs from 'fs';
35
+ import * as path from 'path';
36
+
37
+ // ============================================================================
38
+ // Dynamic SQLite Import (for Bun runtime compatibility)
39
+ // ============================================================================
40
+
41
+ type SQLiteDatabase = {
42
+ exec(sql: string): void;
43
+ prepare(sql: string): {
44
+ all(...params: unknown[]): unknown[];
45
+ get(...params: unknown[]): unknown;
46
+ run(...params: unknown[]): { changes: number };
47
+ };
48
+ close(): void;
49
+ };
50
+
51
+ type SQLiteConstructor = new (path: string, options?: { create?: boolean; readwrite?: boolean }) => SQLiteDatabase;
52
+
53
+ let Database: SQLiteConstructor | null = null;
54
+ let sqliteLoadError: Error | null = null;
55
+
56
+ // Try to load bun:sqlite at runtime
57
+ async function loadSQLite(): Promise<SQLiteConstructor> {
58
+ if (Database) return Database;
59
+ if (sqliteLoadError) throw sqliteLoadError;
60
+
61
+ try {
62
+ // Dynamic import for bun:sqlite
63
+ const sqlite = await import('bun:sqlite');
64
+ Database = sqlite.Database as unknown as SQLiteConstructor;
65
+ return Database;
66
+ } catch {
67
+ sqliteLoadError = new DatabaseConfigError(
68
+ 'SQLite is not available in this environment. SQLite requires Bun runtime. For cloud deployments, use PostgreSQL or MySQL instead.',
69
+ 'sqlite'
70
+ );
71
+ throw sqliteLoadError;
72
+ }
73
+ }
74
+
75
+ // ============================================================================
76
+ // SQLite Provider
77
+ // ============================================================================
78
+
79
+ export class SQLiteProvider extends SQLBaseProvider {
80
+ private db: SQLiteDatabase | null = null;
81
+
82
+ constructor(config: DatabaseConnection, options: ProviderOptions = {}) {
83
+ super(config, options);
84
+ this.validate();
85
+ }
86
+
87
+ // ============================================================================
88
+ // Provider Metadata
89
+ // ============================================================================
90
+
91
+ public override getCapabilities(): ProviderCapabilities {
92
+ return {
93
+ ...super.getCapabilities(),
94
+ defaultPort: null,
95
+ supportsExplain: false,
96
+ supportsConnectionString: false,
97
+ maintenanceOperations: ['vacuum', 'analyze', 'reindex', 'check'],
98
+ };
99
+ }
100
+
101
+ // ============================================================================
102
+ // Validation
103
+ // ============================================================================
104
+
105
+ public validate(): void {
106
+ super.validate();
107
+
108
+ if (!this.config.database && !this.config.connectionString) {
109
+ throw new DatabaseConfigError(
110
+ 'Database file path is required for SQLite (use "database" field or ":memory:" for in-memory)',
111
+ 'sqlite'
112
+ );
113
+ }
114
+ }
115
+
116
+ // ============================================================================
117
+ // Connection Management
118
+ // ============================================================================
119
+
120
+ public async connect(): Promise<void> {
121
+ if (this.db) {
122
+ return;
123
+ }
124
+
125
+ try {
126
+ // Dynamically load SQLite
127
+ const SQLiteDB = await loadSQLite();
128
+
129
+ const dbPath = this.getDatabasePath();
130
+
131
+ if (dbPath !== ':memory:') {
132
+ const dir = path.dirname(dbPath);
133
+ if (!fs.existsSync(dir)) {
134
+ fs.mkdirSync(dir, { recursive: true });
135
+ }
136
+ }
137
+
138
+ this.db = new SQLiteDB(dbPath, {
139
+ create: true,
140
+ readwrite: true,
141
+ });
142
+
143
+ // Enable WAL mode and foreign keys
144
+ this.db.exec('PRAGMA foreign_keys = ON');
145
+ this.db.exec('PRAGMA journal_mode = WAL');
146
+ this.db.exec('PRAGMA synchronous = NORMAL');
147
+
148
+ this.setConnected(true);
149
+ } catch (error) {
150
+ this.setError(error instanceof Error ? error : new Error(String(error)));
151
+
152
+ if (error instanceof DatabaseConfigError) {
153
+ throw error;
154
+ }
155
+
156
+ throw new ConnectionError(
157
+ `Failed to open SQLite database: ${error instanceof Error ? error.message : error}`,
158
+ 'sqlite'
159
+ );
160
+ }
161
+ }
162
+
163
+ public async disconnect(): Promise<void> {
164
+ if (this.db) {
165
+ this.db.close();
166
+ this.db = null;
167
+ this.setConnected(false);
168
+ }
169
+ }
170
+
171
+ private getDatabasePath(): string {
172
+ let dbPath: string;
173
+ if (this.config.connectionString) {
174
+ dbPath = this.config.connectionString.startsWith('file:')
175
+ ? this.config.connectionString.replace('file:', '')
176
+ : this.config.connectionString;
177
+ } else {
178
+ dbPath = this.config.database || ':memory:';
179
+ }
180
+
181
+ // Allow :memory: without path validation
182
+ if (dbPath === ':memory:') return dbPath;
183
+
184
+ // Resolve to absolute path and reject path traversal attempts
185
+ const resolved = path.resolve(dbPath);
186
+ if (resolved !== path.normalize(resolved) || dbPath.includes('\0')) {
187
+ throw new DatabaseConfigError(
188
+ 'Invalid database path: path traversal is not allowed',
189
+ 'sqlite'
190
+ );
191
+ }
192
+
193
+ return resolved;
194
+ }
195
+
196
+ // ============================================================================
197
+ // Query Execution
198
+ // ============================================================================
199
+
200
+ public async query(sql: string, params?: unknown[]): Promise<QueryResult> {
201
+ this.ensureConnected();
202
+
203
+ return this.trackQuery(async () => {
204
+ const { result, executionTime } = await this.measureExecution(async () => {
205
+ try {
206
+ const isSelect = this.isReadOnlyQuery(sql);
207
+
208
+ if (isSelect) {
209
+ const stmt = this.db!.prepare(sql);
210
+ const rows = params ? stmt.all(...params) : stmt.all();
211
+ const fields = rows.length > 0 ? Object.keys(rows[0] as object) : [];
212
+ return {
213
+ rows: (rows as unknown[]).map(row => row as Record<string, unknown>) as Record<string, unknown>[],
214
+ fields,
215
+ changes: 0,
216
+ };
217
+ } else {
218
+ const stmt = this.db!.prepare(sql);
219
+ const info = params ? stmt.run(...params) : stmt.run();
220
+ return {
221
+ rows: [],
222
+ fields: [],
223
+ changes: info.changes,
224
+ };
225
+ }
226
+ } catch (error) {
227
+ throw mapDatabaseError(error, 'sqlite', sql);
228
+ }
229
+ });
230
+
231
+ return {
232
+ rows: result.rows,
233
+ fields: result.fields,
234
+ rowCount: result.rows.length || result.changes,
235
+ executionTime,
236
+ };
237
+ });
238
+ }
239
+
240
+ // ============================================================================
241
+ // Schema Operations
242
+ // ============================================================================
243
+
244
+ public async getSchema(): Promise<TableSchema[]> {
245
+ this.ensureConnected();
246
+
247
+ const tablesStmt = this.db!.prepare(`
248
+ SELECT name FROM sqlite_master
249
+ WHERE type = 'table'
250
+ AND name NOT LIKE 'sqlite_%'
251
+ ORDER BY name;
252
+ `);
253
+ const tables = tablesStmt.all() as { name: string }[];
254
+
255
+ const schemas: TableSchema[] = [];
256
+
257
+ for (const { name: tableName } of tables) {
258
+ const countStmt = this.db!.prepare(`SELECT COUNT(*) as count FROM "${tableName}"`);
259
+ const countResult = countStmt.get() as { count: number };
260
+ const rowCount = countResult?.count || 0;
261
+
262
+ const columnsStmt = this.db!.prepare(`PRAGMA table_info("${tableName}")`);
263
+ const columns = columnsStmt.all() as Array<{
264
+ cid: number;
265
+ name: string;
266
+ type: string;
267
+ notnull: number;
268
+ dflt_value: string | null;
269
+ pk: number;
270
+ }>;
271
+
272
+ const fkStmt = this.db!.prepare(`PRAGMA foreign_key_list("${tableName}")`);
273
+ const foreignKeys = fkStmt.all() as Array<{
274
+ id: number;
275
+ seq: number;
276
+ table: string;
277
+ from: string;
278
+ to: string;
279
+ }>;
280
+
281
+ const indexStmt = this.db!.prepare(`PRAGMA index_list("${tableName}")`);
282
+ const indexList = indexStmt.all() as Array<{
283
+ seq: number;
284
+ name: string;
285
+ unique: number;
286
+ }>;
287
+
288
+ const indexes = [];
289
+ for (const idx of indexList) {
290
+ if (idx.name.startsWith('sqlite_')) continue;
291
+
292
+ const indexInfoStmt = this.db!.prepare(`PRAGMA index_info("${idx.name}")`);
293
+ const indexCols = indexInfoStmt.all() as Array<{ seqno: number; cid: number; name: string }>;
294
+
295
+ indexes.push({
296
+ name: idx.name,
297
+ columns: indexCols.map((c) => c.name),
298
+ unique: idx.unique === 1,
299
+ });
300
+ }
301
+
302
+ let sizeBytes = 0;
303
+ try {
304
+ const pageCountStmt = this.db!.prepare(`
305
+ SELECT (SELECT page_count FROM pragma_page_count()) *
306
+ (SELECT page_size FROM pragma_page_size()) as size
307
+ `);
308
+ const sizeResult = pageCountStmt.get() as { size: number };
309
+ sizeBytes = sizeResult?.size || 0;
310
+ } catch {
311
+ // Ignore size calculation errors
312
+ }
313
+
314
+ schemas.push({
315
+ name: tableName,
316
+ rowCount,
317
+ size: formatBytes(sizeBytes),
318
+ columns: columns.map((col) => ({
319
+ name: col.name,
320
+ type: col.type || 'TEXT',
321
+ nullable: col.notnull === 0,
322
+ isPrimary: col.pk === 1,
323
+ defaultValue: col.dflt_value ?? undefined,
324
+ })),
325
+ indexes,
326
+ foreignKeys: foreignKeys.map((fk) => ({
327
+ columnName: fk.from,
328
+ referencedTable: fk.table,
329
+ referencedColumn: fk.to,
330
+ })),
331
+ });
332
+ }
333
+
334
+ return schemas;
335
+ }
336
+
337
+ // ============================================================================
338
+ // Health & Monitoring
339
+ // ============================================================================
340
+
341
+ public async getHealth(): Promise<HealthInfo> {
342
+ this.ensureConnected();
343
+
344
+ const dbPath = this.getDatabasePath();
345
+
346
+ let databaseSize = 'N/A';
347
+ if (dbPath !== ':memory:') {
348
+ try {
349
+ const stats = fs.statSync(dbPath);
350
+ databaseSize = formatBytes(stats.size);
351
+ } catch {
352
+ databaseSize = 'Unknown';
353
+ }
354
+ } else {
355
+ try {
356
+ const sizeStmt = this.db!.prepare(`
357
+ SELECT (page_count * page_size) as size
358
+ FROM pragma_page_count(), pragma_page_size()
359
+ `);
360
+ const result = sizeStmt.get() as { size: number };
361
+ databaseSize = formatBytes(result?.size || 0);
362
+ } catch {
363
+ databaseSize = 'N/A';
364
+ }
365
+ }
366
+
367
+ let isHealthy = true;
368
+ try {
369
+ const integrityStmt = this.db!.prepare('PRAGMA integrity_check');
370
+ const integrityResult = integrityStmt.get() as { integrity_check: string };
371
+ isHealthy = integrityResult?.integrity_check === 'ok';
372
+ } catch {
373
+ isHealthy = false;
374
+ }
375
+
376
+ let journalMode = 'unknown';
377
+ try {
378
+ const journalStmt = this.db!.prepare('PRAGMA journal_mode');
379
+ const journalResult = journalStmt.get() as { journal_mode: string };
380
+ journalMode = journalResult?.journal_mode || 'unknown';
381
+ } catch {
382
+ // Ignore
383
+ }
384
+
385
+ return {
386
+ activeConnections: 1,
387
+ databaseSize,
388
+ cacheHitRatio: 'N/A',
389
+ slowQueries: [
390
+ {
391
+ query: `Integrity: ${isHealthy ? 'OK' : 'FAILED'}`,
392
+ calls: 0,
393
+ avgTime: 'N/A',
394
+ },
395
+ {
396
+ query: `Journal Mode: ${journalMode}`,
397
+ calls: 0,
398
+ avgTime: 'N/A',
399
+ },
400
+ ],
401
+ activeSessions: [
402
+ {
403
+ pid: process.pid,
404
+ user: 'sqlite',
405
+ database: path.basename(dbPath),
406
+ state: 'active',
407
+ query: '',
408
+ duration: 'N/A',
409
+ },
410
+ ],
411
+ };
412
+ }
413
+
414
+ // ============================================================================
415
+ // Maintenance Operations
416
+ // ============================================================================
417
+
418
+ public async runMaintenance(
419
+ type: MaintenanceType,
420
+ target?: string
421
+ ): Promise<MaintenanceResult> {
422
+ this.ensureConnected();
423
+
424
+ const { result, executionTime } = await this.measureExecution(async () => {
425
+ let sql = '';
426
+
427
+ switch (type) {
428
+ case 'vacuum':
429
+ sql = 'VACUUM';
430
+ break;
431
+ case 'analyze':
432
+ sql = target ? `ANALYZE "${target}"` : 'ANALYZE';
433
+ break;
434
+ case 'reindex':
435
+ sql = target ? `REINDEX "${target}"` : 'REINDEX';
436
+ break;
437
+ case 'check':
438
+ const checkStmt = this.db!.prepare('PRAGMA integrity_check');
439
+ const checkResult = checkStmt.get() as { integrity_check: string };
440
+ return {
441
+ success: checkResult?.integrity_check === 'ok',
442
+ message: checkResult?.integrity_check || 'Unknown',
443
+ };
444
+ default:
445
+ throw new QueryError(`Unsupported maintenance type for SQLite: ${type}`, 'sqlite');
446
+ }
447
+
448
+ this.db!.exec(sql);
449
+ return { success: true, message: `${type.toUpperCase()} completed successfully` };
450
+ });
451
+
452
+ return {
453
+ success: result.success,
454
+ executionTime,
455
+ message: result.message,
456
+ };
457
+ }
458
+
459
+ // ============================================================================
460
+ // Monitoring Operations
461
+ // ============================================================================
462
+
463
+ public async getOverview(): Promise<DatabaseOverview> {
464
+ this.ensureConnected();
465
+
466
+ // Get SQLite version
467
+ const versionStmt = this.db!.prepare('SELECT sqlite_version() as version');
468
+ const versionResult = versionStmt.get() as { version: string };
469
+ const version = `SQLite ${versionResult?.version || 'Unknown'}`;
470
+
471
+ // Get database size
472
+ const dbPath = this.getDatabasePath();
473
+ let databaseSizeBytes = 0;
474
+
475
+ if (dbPath !== ':memory:') {
476
+ try {
477
+ const stats = fs.statSync(dbPath);
478
+ databaseSizeBytes = stats.size;
479
+ } catch {
480
+ // File might not exist yet
481
+ }
482
+ } else {
483
+ try {
484
+ const sizeStmt = this.db!.prepare(`
485
+ SELECT (page_count * page_size) as size
486
+ FROM pragma_page_count(), pragma_page_size()
487
+ `);
488
+ const result = sizeStmt.get() as { size: number };
489
+ databaseSizeBytes = result?.size || 0;
490
+ } catch {
491
+ // Ignore
492
+ }
493
+ }
494
+
495
+ // Get table count
496
+ const tableCountStmt = this.db!.prepare(`
497
+ SELECT COUNT(*) as count FROM sqlite_master
498
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
499
+ `);
500
+ const tableCountResult = tableCountStmt.get() as { count: number };
501
+ const tableCount = tableCountResult?.count || 0;
502
+
503
+ // Get index count
504
+ const indexCountStmt = this.db!.prepare(`
505
+ SELECT COUNT(*) as count FROM sqlite_master
506
+ WHERE type = 'index' AND name NOT LIKE 'sqlite_%'
507
+ `);
508
+ const indexCountResult = indexCountStmt.get() as { count: number };
509
+ const indexCount = indexCountResult?.count || 0;
510
+
511
+ return {
512
+ version,
513
+ uptime: 'N/A',
514
+ activeConnections: 1,
515
+ maxConnections: 1,
516
+ databaseSize: formatBytes(databaseSizeBytes),
517
+ databaseSizeBytes,
518
+ tableCount,
519
+ indexCount,
520
+ };
521
+ }
522
+
523
+ public async getPerformanceMetrics(): Promise<PerformanceMetrics> {
524
+ this.ensureConnected();
525
+
526
+ let cacheHitRatio = 99;
527
+
528
+ try {
529
+ // Get cache stats
530
+ const cacheStmt = this.db!.prepare('PRAGMA cache_size');
531
+ const cacheResult = cacheStmt.get() as { cache_size: number };
532
+
533
+ // SQLite doesn't provide detailed cache hit stats, estimate high ratio
534
+ if (cacheResult?.cache_size) {
535
+ cacheHitRatio = 95; // Reasonable estimate for in-memory cache
536
+ }
537
+ } catch {
538
+ // Ignore
539
+ }
540
+
541
+ return {
542
+ cacheHitRatio,
543
+ // SQLite doesn't provide these metrics
544
+ queriesPerSecond: undefined,
545
+ bufferPoolUsage: undefined,
546
+ deadlocks: 0,
547
+ };
548
+ }
549
+
550
+ public async getSlowQueries(): Promise<SlowQueryStats[]> {
551
+ // SQLite doesn't have built-in query statistics
552
+ return [];
553
+ }
554
+
555
+ public async getActiveSessions(): Promise<ActiveSessionDetails[]> {
556
+ this.ensureConnected();
557
+
558
+ const dbPath = this.getDatabasePath();
559
+
560
+ // SQLite is single-connection, return current session
561
+ return [{
562
+ pid: process.pid,
563
+ user: 'sqlite',
564
+ database: path.basename(dbPath),
565
+ state: 'active',
566
+ query: '',
567
+ duration: 'N/A',
568
+ durationMs: 0,
569
+ }];
570
+ }
571
+
572
+ public async getTableStats(): Promise<TableStats[]> {
573
+ this.ensureConnected();
574
+
575
+ const tablesStmt = this.db!.prepare(`
576
+ SELECT name FROM sqlite_master
577
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
578
+ ORDER BY name
579
+ `);
580
+ const tables = tablesStmt.all() as { name: string }[];
581
+
582
+ const stats: TableStats[] = [];
583
+
584
+ for (const { name: tableName } of tables) {
585
+ // Get row count
586
+ const countStmt = this.db!.prepare(`SELECT COUNT(*) as count FROM "${tableName}"`);
587
+ const countResult = countStmt.get() as { count: number };
588
+ const rowCount = countResult?.count || 0;
589
+
590
+ // Estimate table size (SQLite doesn't provide per-table sizes)
591
+ // Use page count approximation
592
+ let tableSizeBytes = 0;
593
+ try {
594
+ // Rough estimate: rows * average row size
595
+ tableSizeBytes = rowCount * 100; // Assume 100 bytes average per row
596
+ } catch {
597
+ // Ignore
598
+ }
599
+
600
+ stats.push({
601
+ schemaName: 'main',
602
+ tableName,
603
+ rowCount,
604
+ tableSize: formatBytes(tableSizeBytes),
605
+ tableSizeBytes,
606
+ totalSize: formatBytes(tableSizeBytes),
607
+ totalSizeBytes: tableSizeBytes,
608
+ });
609
+ }
610
+
611
+ return stats;
612
+ }
613
+
614
+ public async getIndexStats(): Promise<IndexStats[]> {
615
+ this.ensureConnected();
616
+
617
+ const indexesStmt = this.db!.prepare(`
618
+ SELECT name, tbl_name FROM sqlite_master
619
+ WHERE type = 'index' AND name NOT LIKE 'sqlite_%'
620
+ ORDER BY tbl_name, name
621
+ `);
622
+ const indexes = indexesStmt.all() as { name: string; tbl_name: string }[];
623
+
624
+ const stats: IndexStats[] = [];
625
+
626
+ for (const { name: indexName, tbl_name: tableName } of indexes) {
627
+ // Get index info
628
+ const indexInfoStmt = this.db!.prepare(`PRAGMA index_info("${indexName}")`);
629
+ const indexCols = indexInfoStmt.all() as { seqno: number; cid: number; name: string }[];
630
+
631
+ // Get index uniqueness
632
+ const indexListStmt = this.db!.prepare(`PRAGMA index_list("${tableName}")`);
633
+ const indexList = indexListStmt.all() as { name: string; unique: number }[];
634
+ const indexMeta = indexList.find((i) => i.name === indexName);
635
+
636
+ stats.push({
637
+ schemaName: 'main',
638
+ tableName,
639
+ indexName,
640
+ columns: indexCols.map((c) => c.name),
641
+ isUnique: indexMeta?.unique === 1,
642
+ isPrimary: false, // SQLite auto-creates rowid, explicit PKs are shown differently
643
+ indexSize: 'N/A',
644
+ indexSizeBytes: 0,
645
+ scans: 0, // SQLite doesn't track index usage
646
+ });
647
+ }
648
+
649
+ return stats;
650
+ }
651
+
652
+ public async getStorageStats(): Promise<StorageStats[]> {
653
+ this.ensureConnected();
654
+
655
+ const stats: StorageStats[] = [];
656
+ const dbPath = this.getDatabasePath();
657
+
658
+ // Main database file
659
+ let mainSizeBytes = 0;
660
+ if (dbPath !== ':memory:') {
661
+ try {
662
+ const fileStats = fs.statSync(dbPath);
663
+ mainSizeBytes = fileStats.size;
664
+ } catch {
665
+ // File might not exist
666
+ }
667
+ } else {
668
+ try {
669
+ const sizeStmt = this.db!.prepare(`
670
+ SELECT (page_count * page_size) as size
671
+ FROM pragma_page_count(), pragma_page_size()
672
+ `);
673
+ const result = sizeStmt.get() as { size: number };
674
+ mainSizeBytes = result?.size || 0;
675
+ } catch {
676
+ // Ignore
677
+ }
678
+ }
679
+
680
+ stats.push({
681
+ name: 'Main Database',
682
+ location: dbPath === ':memory:' ? ':memory:' : path.basename(dbPath),
683
+ size: formatBytes(mainSizeBytes),
684
+ sizeBytes: mainSizeBytes,
685
+ });
686
+
687
+ // WAL file (if exists)
688
+ if (dbPath !== ':memory:') {
689
+ const walPath = `${dbPath}-wal`;
690
+ try {
691
+ const walStats = fs.statSync(walPath);
692
+ stats.push({
693
+ name: 'WAL',
694
+ location: path.basename(walPath),
695
+ size: formatBytes(walStats.size),
696
+ sizeBytes: walStats.size,
697
+ walSize: formatBytes(walStats.size),
698
+ walSizeBytes: walStats.size,
699
+ });
700
+ } catch {
701
+ // WAL might not exist
702
+ }
703
+
704
+ // SHM file (if exists)
705
+ const shmPath = `${dbPath}-shm`;
706
+ try {
707
+ const shmStats = fs.statSync(shmPath);
708
+ stats.push({
709
+ name: 'Shared Memory',
710
+ location: path.basename(shmPath),
711
+ size: formatBytes(shmStats.size),
712
+ sizeBytes: shmStats.size,
713
+ });
714
+ } catch {
715
+ // SHM might not exist
716
+ }
717
+ }
718
+
719
+ return stats;
720
+ }
721
+ }