@libredb/studio 0.9.7 → 0.9.13
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.
- package/dist/chunk-34YQUUCM.mjs +319 -0
- package/dist/chunk-34YQUUCM.mjs.map +1 -0
- package/dist/chunk-4LVB3K53.mjs +37 -0
- package/dist/chunk-4LVB3K53.mjs.map +1 -0
- package/dist/chunk-6DRZXXNT.mjs +100 -0
- package/dist/chunk-6DRZXXNT.mjs.map +1 -0
- package/dist/chunk-CPF7XWV5.mjs +1289 -0
- package/dist/chunk-CPF7XWV5.mjs.map +1 -0
- package/dist/chunk-CZVV3JJB.mjs +160 -0
- package/dist/chunk-CZVV3JJB.mjs.map +1 -0
- package/dist/chunk-D4WVWWWF.js +332 -0
- package/dist/chunk-D4WVWWWF.js.map +1 -0
- package/dist/chunk-DY3KXE44.mjs +3 -0
- package/dist/chunk-DY3KXE44.mjs.map +1 -0
- package/dist/chunk-FYSE52VB.js +242 -0
- package/dist/chunk-FYSE52VB.js.map +1 -0
- package/dist/chunk-G3S66G64.mjs +6673 -0
- package/dist/chunk-G3S66G64.mjs.map +1 -0
- package/dist/chunk-G4WYE6TI.js +4 -0
- package/dist/chunk-G4WYE6TI.js.map +1 -0
- package/dist/chunk-HGPD6PWV.js +1310 -0
- package/dist/chunk-HGPD6PWV.js.map +1 -0
- package/dist/chunk-JZO5KRZN.js +165 -0
- package/dist/chunk-JZO5KRZN.js.map +1 -0
- package/dist/chunk-KV356UXJ.js +253 -0
- package/dist/chunk-KV356UXJ.js.map +1 -0
- package/dist/chunk-PPODO6HX.mjs +237 -0
- package/dist/chunk-PPODO6HX.mjs.map +1 -0
- package/dist/chunk-PTIRB2JO.js +258 -0
- package/dist/chunk-PTIRB2JO.js.map +1 -0
- package/dist/chunk-Q6LRDBK7.js +42 -0
- package/dist/chunk-Q6LRDBK7.js.map +1 -0
- package/dist/chunk-QJP5FZRY.mjs +255 -0
- package/dist/chunk-QJP5FZRY.mjs.map +1 -0
- package/dist/chunk-R3POCJK6.mjs +248 -0
- package/dist/chunk-R3POCJK6.mjs.map +1 -0
- package/dist/chunk-RCQB4FCE.js +186 -0
- package/dist/chunk-RCQB4FCE.js.map +1 -0
- package/dist/chunk-SR5DRGBX.mjs +174 -0
- package/dist/chunk-SR5DRGBX.mjs.map +1 -0
- package/dist/chunk-VLCRUZX7.js +102 -0
- package/dist/chunk-VLCRUZX7.js.map +1 -0
- package/dist/chunk-Y52UIFEX.js +6741 -0
- package/dist/chunk-Y52UIFEX.js.map +1 -0
- package/dist/components.d.mts +273 -0
- package/dist/components.d.ts +273 -0
- package/dist/components.js +59 -0
- package/dist/components.js.map +1 -0
- package/dist/components.mjs +6 -0
- package/dist/components.mjs.map +1 -0
- package/dist/custom-BNDOYC5P.js +134 -0
- package/dist/custom-BNDOYC5P.js.map +1 -0
- package/dist/custom-S2EKFMP3.mjs +132 -0
- package/dist/custom-S2EKFMP3.mjs.map +1 -0
- package/dist/gemini-4ASHNK4H.js +81 -0
- package/dist/gemini-4ASHNK4H.js.map +1 -0
- package/dist/gemini-C5RBLQEJ.mjs +79 -0
- package/dist/gemini-C5RBLQEJ.mjs.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mongodb-XMZEZA4A.mjs +748 -0
- package/dist/mongodb-XMZEZA4A.mjs.map +1 -0
- package/dist/mongodb-YQJJTLX3.js +750 -0
- package/dist/mongodb-YQJJTLX3.js.map +1 -0
- package/dist/mssql-PMOU4D36.js +916 -0
- package/dist/mssql-PMOU4D36.js.map +1 -0
- package/{src/lib/db/providers/sql/mssql.ts → dist/mssql-ZH5VP2C5.mjs} +268 -423
- package/dist/mssql-ZH5VP2C5.mjs.map +1 -0
- package/{src/lib/db/providers/sql/mysql.ts → dist/mysql-I3WJQXN2.mjs} +277 -428
- package/dist/mysql-I3WJQXN2.mjs.map +1 -0
- package/dist/mysql-Y3MSA5QY.js +833 -0
- package/dist/mysql-Y3MSA5QY.js.map +1 -0
- package/dist/ollama-26BYLVEV.mjs +115 -0
- package/dist/ollama-26BYLVEV.mjs.map +1 -0
- package/dist/ollama-HVWAGKQC.js +117 -0
- package/dist/ollama-HVWAGKQC.js.map +1 -0
- package/dist/openai-4U56KPG7.mjs +111 -0
- package/dist/openai-4U56KPG7.mjs.map +1 -0
- package/dist/openai-AK3R37BS.js +113 -0
- package/dist/openai-AK3R37BS.js.map +1 -0
- package/dist/oracle-L6VEAVXO.js +917 -0
- package/dist/oracle-L6VEAVXO.js.map +1 -0
- package/{src/lib/db/providers/sql/oracle.ts → dist/oracle-P2G7T4P4.mjs} +321 -454
- package/dist/oracle-P2G7T4P4.mjs.map +1 -0
- package/{src/lib/db/providers/sql/postgres.ts → dist/postgres-O5KOQUVP.mjs} +261 -471
- package/dist/postgres-O5KOQUVP.mjs.map +1 -0
- package/dist/postgres-RLCWNFFX.js +971 -0
- package/dist/postgres-RLCWNFFX.js.map +1 -0
- package/dist/providers.d.mts +149 -0
- package/dist/providers.d.ts +149 -0
- package/dist/providers.js +44 -0
- package/dist/providers.js.map +1 -0
- package/dist/providers.mjs +7 -0
- package/dist/providers.mjs.map +1 -0
- package/dist/redis-4WMQOVLX.mjs +435 -0
- package/dist/redis-4WMQOVLX.mjs.map +1 -0
- package/dist/redis-QVQ6YU62.js +441 -0
- package/dist/redis-QVQ6YU62.js.map +1 -0
- package/dist/sqlite-4I2P2OGQ.js +554 -0
- package/dist/sqlite-4I2P2OGQ.js.map +1 -0
- package/dist/sqlite-OA4YJX5S.mjs +531 -0
- package/dist/sqlite-OA4YJX5S.mjs.map +1 -0
- package/dist/types-BJvJfxSY.d.mts +141 -0
- package/dist/types-BJvJfxSY.d.ts +141 -0
- package/dist/types-ClAg_v5k.d.mts +343 -0
- package/dist/types-Der_X8E8.d.ts +343 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +3 -0
- package/dist/types.mjs.map +1 -0
- package/dist/workspace.d.mts +80 -0
- package/dist/workspace.d.ts +80 -0
- package/dist/workspace.js +4174 -0
- package/dist/workspace.js.map +1 -0
- package/dist/workspace.mjs +4147 -0
- package/dist/workspace.mjs.map +1 -0
- package/package.json +60 -5
- package/.claude/settings.local.json +0 -127
- package/.cursorrules +0 -426
- package/.devin/wiki.json +0 -143
- package/.dockerignore +0 -80
- package/.env.example +0 -159
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -49
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -29
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -57
- package/.github/workflows/ci.yml +0 -185
- package/.github/workflows/codeql.yml +0 -57
- package/.github/workflows/docker-build-push.yml +0 -118
- package/.github/workflows/helm-release.yml +0 -113
- package/CLAUDE.md +0 -265
- package/CODE_OF_CONDUCT.md +0 -124
- package/CONTRIBUTING.md +0 -154
- package/Dockerfile +0 -73
- package/SECURITY.md +0 -107
- package/artifacthub-repo.yml +0 -4
- package/bun.lock +0 -1714
- package/bunfig.toml +0 -3
- package/charts/libredb-studio/.helmignore +0 -11
- package/charts/libredb-studio/Chart.lock +0 -6
- package/charts/libredb-studio/Chart.yaml +0 -50
- package/charts/libredb-studio/README.md +0 -206
- package/charts/libredb-studio/templates/NOTES.txt +0 -59
- package/charts/libredb-studio/templates/_helpers.tpl +0 -135
- package/charts/libredb-studio/templates/configmap.yaml +0 -37
- package/charts/libredb-studio/templates/deployment.yaml +0 -184
- package/charts/libredb-studio/templates/hpa.yaml +0 -32
- package/charts/libredb-studio/templates/ingress.yaml +0 -41
- package/charts/libredb-studio/templates/networkpolicy.yaml +0 -50
- package/charts/libredb-studio/templates/pdb.yaml +0 -18
- package/charts/libredb-studio/templates/pvc.yaml +0 -23
- package/charts/libredb-studio/templates/secret.yaml +0 -30
- package/charts/libredb-studio/templates/seed-configmap.yaml +0 -11
- package/charts/libredb-studio/templates/service.yaml +0 -22
- package/charts/libredb-studio/templates/serviceaccount.yaml +0 -13
- package/charts/libredb-studio/values.schema.json +0 -246
- package/charts/libredb-studio/values.yaml +0 -286
- package/components.json +0 -22
- package/conductor/code_styleguides/typescript.md +0 -43
- package/conductor/product-guidelines.md +0 -43
- package/conductor/product.md +0 -3
- package/conductor/setup_state.json +0 -1
- package/conductor/tech-stack.md +0 -39
- package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +0 -8
- package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +0 -44
- package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +0 -31
- package/conductor/tracks.md +0 -8
- package/conductor/workflow.md +0 -333
- package/database-compose.yml +0 -55
- package/docker/postgres-init/01-extensions.sql +0 -10
- package/docker/postgres-init/02-sample-data.sql +0 -585
- package/docker/postgres.yml +0 -68
- package/docker-compose.yml +0 -38
- package/docs/AI_PLAN.md +0 -74
- package/docs/API_DOCS.md +0 -875
- package/docs/ARCHITECTURE.md +0 -218
- package/docs/DATABASE_PROVIDERS.md +0 -358
- package/docs/FEATURES.md +0 -116
- package/docs/HELM_CHART.md +0 -252
- package/docs/LOGIN_PAGE.md +0 -178
- package/docs/MONACO_EDITOR_PERFORMANCE.md +0 -315
- package/docs/OIDC_ARCH.md +0 -681
- package/docs/OIDC_SETUP.md +0 -322
- package/docs/POSTGRES_METRICS.md +0 -516
- package/docs/QUERY_OPTIMIZATION.md +0 -370
- package/docs/SEED_CONNECTIONS.md +0 -468
- package/docs/SQL_ALIAS_COMPLETION.md +0 -190
- package/docs/STORAGE_ARCHITECTURE.md +0 -565
- package/docs/STORAGE_QUICK_SETUP.md +0 -419
- package/docs/TECHNICAL_PLAN.md +0 -36
- package/docs/THEMING.md +0 -345
- package/docs/adding-a-new-database-provider.md +0 -642
- package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +0 -360
- package/docs/backlogs/001-INLINE_DATA_EDITING.md +0 -118
- package/docs/backlogs/002-DATA_IMPORT.md +0 -215
- package/docs/backlogs/003-QUERY_TIME_MACHINE.md +0 -183
- package/docs/backlogs/004-AI_DATA_STORYTELLER.md +0 -292
- package/docs/backlogs/005-QUERY_PLAYGROUND.md +0 -352
- package/docs/backlogs/006-DATA_MASKING.md +0 -418
- package/docs/enterprise-features.md +0 -718
- package/docs/kubernetes-helm-chart-artifacthub-plan.md +0 -803
- package/docs/medium-koyeb-article-en.md +0 -215
- package/docs/plans/test-plans.md +0 -445
- package/docs/releases/RELEASE.V0.3.0.md +0 -22
- package/docs/releases/RELEASE.V0.4.0.md +0 -154
- package/docs/releases/RELEASE.V0.5.0.md +0 -252
- package/docs/releases/RELEASE_v0.5.6.md +0 -145
- package/docs/releases/RELEASE_v0.6.1.md +0 -303
- package/docs/releases/RELEASE_v0.6.7.md +0 -292
- package/docs/releases/RELEASE_v0.7.0.md +0 -332
- package/docs/releases/RELEASE_v0.8.0.md +0 -521
- package/docs/sampledb/titanic.sql +0 -1379
- package/docs/superpowers/plans/2026-03-25-seed-connections.md +0 -1362
- package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +0 -590
- package/e2e/admin-dashboard.spec.ts +0 -64
- package/e2e/connection-management.spec.ts +0 -58
- package/e2e/export.spec.ts +0 -34
- package/e2e/login.spec.ts +0 -85
- package/e2e/query-execution.spec.ts +0 -35
- package/e2e/tab-management.spec.ts +0 -64
- package/eslint.config.mjs +0 -28
- package/fly.toml +0 -43
- package/next.config.ts +0 -32
- package/playwright.config.ts +0 -34
- package/postcss.config.mjs +0 -7
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/logo.svg +0 -32
- package/public/next.svg +0 -1
- package/public/screenshots/code-generator.png +0 -0
- package/public/screenshots/connection-modal.png +0 -0
- package/public/screenshots/data-profiler.png +0 -0
- package/public/screenshots/erd-diagram.png +0 -0
- package/public/screenshots/hero-editor.png +0 -0
- package/public/screenshots/nl2sql.png +0 -0
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- package/render.yaml +0 -58
- package/scripts/merge-lcov.mjs +0 -239
- package/sonar-project.properties +0 -16
- package/src/app/admin/error.tsx +0 -46
- package/src/app/admin/page.tsx +0 -10
- package/src/app/api/admin/audit/route.ts +0 -52
- package/src/app/api/admin/fleet-health/route.ts +0 -81
- package/src/app/api/ai/autopilot/route.ts +0 -105
- package/src/app/api/ai/chat/route.ts +0 -132
- package/src/app/api/ai/describe-schema/route.ts +0 -52
- package/src/app/api/ai/explain/route.ts +0 -86
- package/src/app/api/ai/impact/route.ts +0 -97
- package/src/app/api/ai/index-advisor/route.ts +0 -98
- package/src/app/api/ai/nl2sql/route.ts +0 -87
- package/src/app/api/ai/query-safety/route.ts +0 -87
- package/src/app/api/auth/login/route.ts +0 -62
- package/src/app/api/auth/logout/route.ts +0 -25
- package/src/app/api/auth/me/route.ts +0 -10
- package/src/app/api/auth/oidc/callback/route.ts +0 -82
- package/src/app/api/auth/oidc/login/route.ts +0 -43
- package/src/app/api/connections/managed/route.ts +0 -35
- package/src/app/api/db/cancel/route.ts +0 -42
- package/src/app/api/db/disconnect/route.ts +0 -28
- package/src/app/api/db/health/route.ts +0 -49
- package/src/app/api/db/maintenance/route.ts +0 -72
- package/src/app/api/db/monitoring/route.ts +0 -62
- package/src/app/api/db/multi-query/route.ts +0 -116
- package/src/app/api/db/pool-stats/route.ts +0 -37
- package/src/app/api/db/profile/route.ts +0 -144
- package/src/app/api/db/provider-meta/route.ts +0 -49
- package/src/app/api/db/query/route.ts +0 -50
- package/src/app/api/db/schema/route.ts +0 -47
- package/src/app/api/db/schema-snapshot/route.ts +0 -42
- package/src/app/api/db/test-connection/route.ts +0 -55
- package/src/app/api/db/transaction/route.ts +0 -111
- package/src/app/api/storage/[collection]/route.ts +0 -67
- package/src/app/api/storage/config/route.ts +0 -17
- package/src/app/api/storage/migrate/route.ts +0 -45
- package/src/app/api/storage/route.ts +0 -32
- package/src/app/error.tsx +0 -49
- package/src/app/global-error.tsx +0 -55
- package/src/app/globals.css +0 -146
- package/src/app/icon.svg +0 -42
- package/src/app/layout.tsx +0 -34
- package/src/app/login/login-form.tsx +0 -301
- package/src/app/login/page.tsx +0 -11
- package/src/app/monitoring/page.tsx +0 -8
- package/src/app/not-found.tsx +0 -29
- package/src/app/page.tsx +0 -5
- package/src/components/AIAutopilotPanel.tsx +0 -238
- package/src/components/CodeGenerator.tsx +0 -271
- package/src/components/CommandPalette.tsx +0 -227
- package/src/components/ConnectionModal.tsx +0 -759
- package/src/components/CreateTableModal.tsx +0 -281
- package/src/components/DataCharts.tsx +0 -962
- package/src/components/DataImportModal.tsx +0 -582
- package/src/components/DataProfiler.tsx +0 -335
- package/src/components/DatabaseDocs.tsx +0 -251
- package/src/components/MaskingSettings.tsx +0 -414
- package/src/components/MobileNav.tsx +0 -50
- package/src/components/NL2SQLPanel.tsx +0 -281
- package/src/components/PivotTable.tsx +0 -257
- package/src/components/QueryEditor.tsx +0 -760
- package/src/components/QueryHistory.tsx +0 -344
- package/src/components/QuerySafetyDialog.tsx +0 -290
- package/src/components/ResultsGrid.tsx +0 -644
- package/src/components/SaveQueryModal.tsx +0 -104
- package/src/components/SavedQueries.tsx +0 -128
- package/src/components/SchemaDiagram.tsx +0 -473
- package/src/components/SchemaDiff.tsx +0 -473
- package/src/components/SnapshotTimeline.tsx +0 -116
- package/src/components/Studio.tsx +0 -639
- package/src/components/TestDataGenerator.tsx +0 -261
- package/src/components/VisualExplain.tsx +0 -820
- package/src/components/admin/AdminDashboard.tsx +0 -163
- package/src/components/admin/tabs/AuditTab.tsx +0 -531
- package/src/components/admin/tabs/MonitoringEmbed.tsx +0 -11
- package/src/components/admin/tabs/OperationsTab.tsx +0 -646
- package/src/components/admin/tabs/OverviewTab.tsx +0 -1328
- package/src/components/admin/tabs/SecurityTab.tsx +0 -284
- package/src/components/community-section.tsx +0 -92
- package/src/components/icons/db-icons.tsx +0 -84
- package/src/components/libredb-logo.tsx +0 -61
- package/src/components/monitoring/MonitoringDashboard.tsx +0 -345
- package/src/components/monitoring/tabs/MetricChart.tsx +0 -82
- package/src/components/monitoring/tabs/OverviewTab.tsx +0 -263
- package/src/components/monitoring/tabs/PerformanceTab.tsx +0 -254
- package/src/components/monitoring/tabs/PoolTab.tsx +0 -174
- package/src/components/monitoring/tabs/QueriesTab.tsx +0 -287
- package/src/components/monitoring/tabs/SessionsTab.tsx +0 -316
- package/src/components/monitoring/tabs/StorageTab.tsx +0 -335
- package/src/components/monitoring/tabs/TablesTab.tsx +0 -300
- package/src/components/results-grid/ResultCard.tsx +0 -111
- package/src/components/results-grid/RowDetailSheet.tsx +0 -178
- package/src/components/results-grid/StatsBar.tsx +0 -201
- package/src/components/results-grid/index.ts +0 -1
- package/src/components/results-grid/utils.ts +0 -23
- package/src/components/schema-explorer/ColumnList.tsx +0 -53
- package/src/components/schema-explorer/SchemaExplorer.tsx +0 -182
- package/src/components/schema-explorer/TableItem.tsx +0 -210
- package/src/components/schema-explorer/index.ts +0 -1
- package/src/components/sidebar/ConnectionItem.tsx +0 -105
- package/src/components/sidebar/ConnectionsList.tsx +0 -62
- package/src/components/sidebar/Sidebar.tsx +0 -130
- package/src/components/sidebar/index.ts +0 -2
- package/src/components/studio/BottomPanel.tsx +0 -286
- package/src/components/studio/QueryToolbar.tsx +0 -180
- package/src/components/studio/StudioDesktopHeader.tsx +0 -114
- package/src/components/studio/StudioMobileHeader.tsx +0 -340
- package/src/components/studio/StudioTabBar.tsx +0 -82
- package/src/components/studio/index.ts +0 -5
- package/src/components/ui/accordion.tsx +0 -66
- package/src/components/ui/alert-dialog.tsx +0 -157
- package/src/components/ui/alert.tsx +0 -66
- package/src/components/ui/aspect-ratio.tsx +0 -11
- package/src/components/ui/avatar.tsx +0 -53
- package/src/components/ui/badge.tsx +0 -46
- package/src/components/ui/breadcrumb.tsx +0 -109
- package/src/components/ui/button-group.tsx +0 -83
- package/src/components/ui/button.tsx +0 -60
- package/src/components/ui/calendar.tsx +0 -216
- package/src/components/ui/card.tsx +0 -92
- package/src/components/ui/carousel.tsx +0 -241
- package/src/components/ui/chart.tsx +0 -357
- package/src/components/ui/checkbox.tsx +0 -32
- package/src/components/ui/collapsible.tsx +0 -33
- package/src/components/ui/command.tsx +0 -184
- package/src/components/ui/context-menu.tsx +0 -252
- package/src/components/ui/dialog.tsx +0 -143
- package/src/components/ui/drawer.tsx +0 -135
- package/src/components/ui/dropdown-menu.tsx +0 -257
- package/src/components/ui/empty.tsx +0 -104
- package/src/components/ui/field.tsx +0 -248
- package/src/components/ui/form.tsx +0 -167
- package/src/components/ui/hover-card.tsx +0 -44
- package/src/components/ui/input-group.tsx +0 -170
- package/src/components/ui/input-otp.tsx +0 -77
- package/src/components/ui/input.tsx +0 -21
- package/src/components/ui/item.tsx +0 -193
- package/src/components/ui/kbd.tsx +0 -28
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/menubar.tsx +0 -276
- package/src/components/ui/navigation-menu.tsx +0 -168
- package/src/components/ui/pagination.tsx +0 -127
- package/src/components/ui/popover.tsx +0 -48
- package/src/components/ui/progress.tsx +0 -31
- package/src/components/ui/radio-group.tsx +0 -45
- package/src/components/ui/resizable.tsx +0 -56
- package/src/components/ui/scroll-area.tsx +0 -58
- package/src/components/ui/select.tsx +0 -187
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -726
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/slider.tsx +0 -63
- package/src/components/ui/sonner.tsx +0 -40
- package/src/components/ui/spinner.tsx +0 -16
- package/src/components/ui/switch.tsx +0 -31
- package/src/components/ui/table.tsx +0 -116
- package/src/components/ui/tabs.tsx +0 -66
- package/src/components/ui/textarea.tsx +0 -18
- package/src/components/ui/toggle-group.tsx +0 -83
- package/src/components/ui/toggle.tsx +0 -47
- package/src/components/ui/tooltip.tsx +0 -61
- package/src/exports/components.ts +0 -15
- package/src/exports/index.ts +0 -4
- package/src/exports/providers.ts +0 -4
- package/src/exports/types.ts +0 -26
- package/src/hooks/use-ai-chat.ts +0 -182
- package/src/hooks/use-all-connections.ts +0 -66
- package/src/hooks/use-api-call.ts +0 -71
- package/src/hooks/use-auth.ts +0 -51
- package/src/hooks/use-connection-form.ts +0 -349
- package/src/hooks/use-connection-manager.ts +0 -169
- package/src/hooks/use-connection-payload.ts +0 -15
- package/src/hooks/use-inline-editing.ts +0 -109
- package/src/hooks/use-mobile.ts +0 -20
- package/src/hooks/use-monitoring-data.ts +0 -270
- package/src/hooks/use-provider-metadata.ts +0 -62
- package/src/hooks/use-query-execution.ts +0 -478
- package/src/hooks/use-storage-sync.ts +0 -259
- package/src/hooks/use-tab-manager.ts +0 -231
- package/src/hooks/use-toast.ts +0 -20
- package/src/hooks/use-transaction-control.ts +0 -64
- package/src/lib/api/error-codes.ts +0 -30
- package/src/lib/api/errors.ts +0 -236
- package/src/lib/api/with-error-handler.ts +0 -41
- package/src/lib/audit.ts +0 -105
- package/src/lib/auth.ts +0 -87
- package/src/lib/connection-string-parser.ts +0 -172
- package/src/lib/data-masking.ts +0 -385
- package/src/lib/db/base-provider.ts +0 -325
- package/src/lib/db/errors.ts +0 -317
- package/src/lib/db/factory.ts +0 -324
- package/src/lib/db/index.ts +0 -123
- package/src/lib/db/providers/document/index.ts +0 -6
- package/src/lib/db/providers/document/mongodb.ts +0 -992
- package/src/lib/db/providers/keyvalue/redis.ts +0 -554
- package/src/lib/db/providers/sql/index.ts +0 -11
- package/src/lib/db/providers/sql/sql-base.ts +0 -174
- package/src/lib/db/providers/sql/sqlite.ts +0 -721
- package/src/lib/db/types.ts +0 -437
- package/src/lib/db/utils/pool-manager.ts +0 -287
- package/src/lib/db/utils/query-limiter.ts +0 -239
- package/src/lib/db-ui-config.ts +0 -86
- package/src/lib/editor/mongodb-completions.ts +0 -172
- package/src/lib/editor/sql-completions.ts +0 -280
- package/src/lib/llm/base-provider.ts +0 -117
- package/src/lib/llm/factory.ts +0 -102
- package/src/lib/llm/index.ts +0 -90
- package/src/lib/llm/providers/custom.ts +0 -181
- package/src/lib/llm/providers/gemini.ts +0 -126
- package/src/lib/llm/providers/ollama.ts +0 -154
- package/src/lib/llm/providers/openai.ts +0 -146
- package/src/lib/llm/types.ts +0 -173
- package/src/lib/llm/utils/config.ts +0 -187
- package/src/lib/llm/utils/retry.ts +0 -119
- package/src/lib/llm/utils/streaming.ts +0 -202
- package/src/lib/logger.ts +0 -127
- package/src/lib/monitoring-thresholds.ts +0 -44
- package/src/lib/oidc.ts +0 -262
- package/src/lib/query-generators.ts +0 -61
- package/src/lib/schema-diff/diff-engine.ts +0 -273
- package/src/lib/schema-diff/migration-generator.ts +0 -208
- package/src/lib/schema-diff/types.ts +0 -55
- package/src/lib/seed/config-loader.ts +0 -79
- package/src/lib/seed/connection-filter.ts +0 -49
- package/src/lib/seed/credential-resolver.ts +0 -62
- package/src/lib/seed/index.ts +0 -40
- package/src/lib/seed/resolve-connection.ts +0 -57
- package/src/lib/seed/types.ts +0 -69
- package/src/lib/sql/alias-extractor.ts +0 -267
- package/src/lib/sql/index.ts +0 -8
- package/src/lib/sql/statement-splitter.ts +0 -167
- package/src/lib/sql/types.ts +0 -40
- package/src/lib/ssh/tunnel.ts +0 -142
- package/src/lib/storage/factory.ts +0 -84
- package/src/lib/storage/index.ts +0 -14
- package/src/lib/storage/local-storage.ts +0 -99
- package/src/lib/storage/providers/postgres.ts +0 -225
- package/src/lib/storage/providers/sqlite.ts +0 -153
- package/src/lib/storage/storage-facade.ts +0 -272
- package/src/lib/storage/types.ts +0 -75
- package/src/lib/time-series-buffer.ts +0 -58
- package/src/lib/types.ts +0 -173
- package/src/lib/utils.ts +0 -6
- package/src/proxy.ts +0 -104
- package/src/types/db-drivers.d.ts +0 -23
- package/src/types/html2canvas.d.ts +0 -9
- package/tests/api/admin/audit.test.ts +0 -178
- package/tests/api/admin/fleet-health.test.ts +0 -183
- package/tests/api/ai/autopilot.test.ts +0 -174
- package/tests/api/ai/chat.test.ts +0 -250
- package/tests/api/ai/describe-schema.test.ts +0 -266
- package/tests/api/ai/explain.test.ts +0 -199
- package/tests/api/ai/impact.test.ts +0 -168
- package/tests/api/ai/index-advisor.test.ts +0 -171
- package/tests/api/ai/nl2sql.test.ts +0 -202
- package/tests/api/ai/query-safety.test.ts +0 -196
- package/tests/api/auth/login.test.ts +0 -170
- package/tests/api/auth/logout.test.ts +0 -140
- package/tests/api/auth/me.test.ts +0 -73
- package/tests/api/auth/oidc-callback.test.ts +0 -215
- package/tests/api/auth/oidc-login.test.ts +0 -127
- package/tests/api/db/cancel.test.ts +0 -198
- package/tests/api/db/disconnect.test.ts +0 -124
- package/tests/api/db/health.test.ts +0 -222
- package/tests/api/db/maintenance.test.ts +0 -263
- package/tests/api/db/monitoring.test.ts +0 -221
- package/tests/api/db/multi-query.test.ts +0 -316
- package/tests/api/db/pool-stats.test.ts +0 -135
- package/tests/api/db/profile.test.ts +0 -330
- package/tests/api/db/provider-meta.test.ts +0 -193
- package/tests/api/db/query.test.ts +0 -314
- package/tests/api/db/schema-snapshot.test.ts +0 -170
- package/tests/api/db/schema.test.ts +0 -191
- package/tests/api/db/test-connection.test.ts +0 -185
- package/tests/api/db/transaction.test.ts +0 -314
- package/tests/api/proxy.test.ts +0 -191
- package/tests/api/seed/managed-route.test.ts +0 -113
- package/tests/api/storage/config.test.ts +0 -42
- package/tests/api/storage/storage-routes.test.ts +0 -309
- package/tests/components/AIAutopilotPanel.test.tsx +0 -756
- package/tests/components/AdminPage.test.tsx +0 -33
- package/tests/components/CodeGenerator.test.tsx +0 -182
- package/tests/components/CommandPalette.test.tsx +0 -428
- package/tests/components/CommunitySection.test.tsx +0 -91
- package/tests/components/ConnectionModal.mobile.test.tsx +0 -284
- package/tests/components/ConnectionModal.test.tsx +0 -570
- package/tests/components/CreateTableModal.test.tsx +0 -383
- package/tests/components/DataCharts.test.tsx +0 -739
- package/tests/components/DataImportModal.test.tsx +0 -751
- package/tests/components/DataProfiler.test.tsx +0 -589
- package/tests/components/DatabaseDocs.test.tsx +0 -353
- package/tests/components/LoginPage.test.tsx +0 -163
- package/tests/components/LoginPageOIDC.test.tsx +0 -92
- package/tests/components/MaskingSettings.test.tsx +0 -498
- package/tests/components/MobileNav.test.tsx +0 -30
- package/tests/components/MonitoringPage.test.tsx +0 -32
- package/tests/components/NL2SQLPanel.test.tsx +0 -621
- package/tests/components/Page.test.tsx +0 -33
- package/tests/components/PivotTable.test.tsx +0 -350
- package/tests/components/QueryEditor.test.tsx +0 -1730
- package/tests/components/QueryHistory.test.tsx +0 -572
- package/tests/components/QuerySafetyDialog.test.tsx +0 -586
- package/tests/components/ResultsGrid.test.tsx +0 -804
- package/tests/components/RootLayout.test.tsx +0 -83
- package/tests/components/SaveQueryModal.test.tsx +0 -25
- package/tests/components/SavedQueries.test.tsx +0 -43
- package/tests/components/SchemaDiagram.test.tsx +0 -1034
- package/tests/components/SchemaDiff.test.tsx +0 -906
- package/tests/components/SnapshotTimeline.test.tsx +0 -174
- package/tests/components/Studio.test.tsx +0 -1030
- package/tests/components/TestDataGenerator.test.tsx +0 -291
- package/tests/components/VisualExplain.test.tsx +0 -704
- package/tests/components/admin/AdminDashboard.test.tsx +0 -205
- package/tests/components/admin/AuditTab.test.tsx +0 -220
- package/tests/components/admin/MonitoringEmbed.test.tsx +0 -58
- package/tests/components/admin/OperationsTab.test.tsx +0 -975
- package/tests/components/admin/OverviewTab.test.tsx +0 -254
- package/tests/components/admin/SecurityTab.test.tsx +0 -467
- package/tests/components/monitoring/MetricChart.test.tsx +0 -111
- package/tests/components/monitoring/MonitoringDashboard.test.tsx +0 -259
- package/tests/components/monitoring/OverviewTab.test.tsx +0 -78
- package/tests/components/monitoring/PerformanceTab.test.tsx +0 -87
- package/tests/components/monitoring/PoolTab.test.tsx +0 -42
- package/tests/components/monitoring/QueriesTab.test.tsx +0 -80
- package/tests/components/monitoring/SessionsTab.test.tsx +0 -154
- package/tests/components/monitoring/StorageTab.test.tsx +0 -127
- package/tests/components/monitoring/TablesTab.test.tsx +0 -153
- package/tests/components/results-grid/ResultCard.test.tsx +0 -105
- package/tests/components/results-grid/RowDetailSheet.test.tsx +0 -308
- package/tests/components/results-grid/StatsBar.test.tsx +0 -162
- package/tests/components/schema-explorer/ColumnList.test.tsx +0 -151
- package/tests/components/schema-explorer/SchemaExplorer.test.tsx +0 -461
- package/tests/components/schema-explorer/TableItem.test.tsx +0 -415
- package/tests/components/sidebar/ConnectionItem.test.tsx +0 -201
- package/tests/components/sidebar/ConnectionsList.test.tsx +0 -176
- package/tests/components/sidebar/Sidebar.test.tsx +0 -187
- package/tests/components/studio/BottomPanel.test.tsx +0 -383
- package/tests/components/studio/QueryToolbar.test.tsx +0 -321
- package/tests/components/studio/StudioDesktopHeader.test.tsx +0 -377
- package/tests/components/studio/StudioMobileHeader.test.tsx +0 -198
- package/tests/components/studio/StudioTabBar.test.tsx +0 -331
- package/tests/fixtures/connections.ts +0 -96
- package/tests/fixtures/masking-configs.ts +0 -86
- package/tests/fixtures/query-results.ts +0 -71
- package/tests/fixtures/schemas.ts +0 -64
- package/tests/fixtures/seed-connections/invalid-config.yaml +0 -7
- package/tests/fixtures/seed-connections/minimal-config.yaml +0 -8
- package/tests/fixtures/seed-connections/mixed-credentials.yaml +0 -23
- package/tests/fixtures/seed-connections/multi-role-config.yaml +0 -30
- package/tests/fixtures/seed-connections/valid-config.json +0 -15
- package/tests/fixtures/seed-connections/valid-config.yaml +0 -51
- package/tests/helpers/mock-fetch.ts +0 -59
- package/tests/helpers/mock-monaco.ts +0 -112
- package/tests/helpers/mock-navigation.ts +0 -28
- package/tests/helpers/mock-next.ts +0 -80
- package/tests/helpers/mock-provider.ts +0 -133
- package/tests/helpers/mock-sonner.ts +0 -29
- package/tests/helpers/render-with-providers.tsx +0 -19
- package/tests/hooks/use-ai-chat.test.ts +0 -600
- package/tests/hooks/use-auth.test.ts +0 -371
- package/tests/hooks/use-connection-form.test.ts +0 -743
- package/tests/hooks/use-connection-manager.test.ts +0 -466
- package/tests/hooks/use-inline-editing.test.ts +0 -321
- package/tests/hooks/use-mobile.test.ts +0 -177
- package/tests/hooks/use-monitoring-data.test.ts +0 -819
- package/tests/hooks/use-provider-metadata.test.ts +0 -228
- package/tests/hooks/use-query-execution.test.ts +0 -1212
- package/tests/hooks/use-tab-manager.test.ts +0 -756
- package/tests/hooks/use-toast.test.ts +0 -74
- package/tests/hooks/use-transaction-control.test.ts +0 -211
- package/tests/integration/db/mongodb-provider.test.ts +0 -698
- package/tests/integration/db/mssql-provider.test.ts +0 -840
- package/tests/integration/db/mysql-provider.test.ts +0 -872
- package/tests/integration/db/oracle-provider.test.ts +0 -843
- package/tests/integration/db/postgres-provider.test.ts +0 -1382
- package/tests/integration/db/redis-provider.test.ts +0 -526
- package/tests/integration/db/sqlite-provider.test.ts +0 -480
- package/tests/integration/seed/seed-pipeline.test.ts +0 -102
- package/tests/isolated/factory-singleton.test.ts +0 -150
- package/tests/isolated/use-storage-sync.test.ts +0 -389
- package/tests/run-components.sh +0 -196
- package/tests/setup-dom.ts +0 -58
- package/tests/setup.ts +0 -40
- package/tests/unit/api-errors.test.ts +0 -210
- package/tests/unit/code-generator-functions.test.ts +0 -271
- package/tests/unit/components/column-list.test.tsx +0 -190
- package/tests/unit/components/data-import-modal.test.tsx +0 -441
- package/tests/unit/components/studio-mobile-header.test.tsx +0 -327
- package/tests/unit/data-charts-functions.test.ts +0 -496
- package/tests/unit/data-import-functions.test.ts +0 -320
- package/tests/unit/data-import-utils.test.ts +0 -125
- package/tests/unit/db/base-provider.test.ts +0 -517
- package/tests/unit/db/errors.test.ts +0 -403
- package/tests/unit/db/factory.test.ts +0 -436
- package/tests/unit/db/pool-manager.test.ts +0 -440
- package/tests/unit/db/query-limiter.test.ts +0 -387
- package/tests/unit/db/sql-base.test.ts +0 -438
- package/tests/unit/lib/api/error-codes.test.ts +0 -39
- package/tests/unit/lib/audit.test.ts +0 -326
- package/tests/unit/lib/auth.test.ts +0 -146
- package/tests/unit/lib/connection-string-parser.test.ts +0 -424
- package/tests/unit/lib/data-masking.test.ts +0 -583
- package/tests/unit/lib/db-icons.test.tsx +0 -41
- package/tests/unit/lib/monitoring-thresholds.test.ts +0 -133
- package/tests/unit/lib/oidc.test.ts +0 -509
- package/tests/unit/lib/query-generators.test.ts +0 -127
- package/tests/unit/lib/storage/factory.test.ts +0 -71
- package/tests/unit/lib/storage/local-storage.test.ts +0 -114
- package/tests/unit/lib/storage/providers/postgres.test.ts +0 -312
- package/tests/unit/lib/storage/providers/sqlite.test.ts +0 -232
- package/tests/unit/lib/storage/storage-facade-extended.test.ts +0 -331
- package/tests/unit/lib/storage/storage-facade.test.ts +0 -184
- package/tests/unit/lib/storage.test.ts +0 -317
- package/tests/unit/lib/time-series-buffer.test.ts +0 -212
- package/tests/unit/lib/utils.test.ts +0 -24
- package/tests/unit/llm/base-provider.test.ts +0 -238
- package/tests/unit/llm/config.test.ts +0 -262
- package/tests/unit/llm/custom-provider.test.ts +0 -281
- package/tests/unit/llm/gemini-provider.test.ts +0 -248
- package/tests/unit/llm/llm-factory.test.ts +0 -155
- package/tests/unit/llm/ollama-provider.test.ts +0 -288
- package/tests/unit/llm/openai-provider.test.ts +0 -324
- package/tests/unit/llm/retry.test.ts +0 -180
- package/tests/unit/llm/streaming.test.ts +0 -355
- package/tests/unit/logger.test.ts +0 -198
- package/tests/unit/mongodb-completions.test.ts +0 -516
- package/tests/unit/pivot-table-functions.test.ts +0 -76
- package/tests/unit/query-cancelled-error.test.ts +0 -81
- package/tests/unit/schema-diff/diff-engine.test.ts +0 -367
- package/tests/unit/schema-diff/migration-generator.test.ts +0 -513
- package/tests/unit/seed/config-loader.test.ts +0 -73
- package/tests/unit/seed/connection-filter.test.ts +0 -91
- package/tests/unit/seed/credential-resolver.test.ts +0 -85
- package/tests/unit/seed/index.test.ts +0 -72
- package/tests/unit/seed/resolve-connection.test.ts +0 -74
- package/tests/unit/seed/types.test.ts +0 -129
- package/tests/unit/sql/alias-extractor.test.ts +0 -444
- package/tests/unit/sql/statement-splitter.test.ts +0 -348
- package/tests/unit/sql-completions.test.ts +0 -463
- package/tests/unit/ssh-tunnel.test.ts +0 -465
- package/tsconfig.json +0 -42
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
# Storage Architecture — LibreDB Studio
|
|
2
|
-
|
|
3
|
-
This document describes the **Storage Abstraction Layer**, a pluggable persistence system that allows LibreDB Studio to operate in two modes:
|
|
4
|
-
|
|
5
|
-
- **Local mode** (default): Zero-config, all data lives in the browser's `localStorage`. Ideal for single-user / open-source usage.
|
|
6
|
-
- **Server mode**: Data is persisted to a server-side database (SQLite or PostgreSQL) with per-user scoping. Ideal for teams and enterprise deployments.
|
|
7
|
-
|
|
8
|
-
Switching between modes requires **only one environment variable** — no code changes, no rebuild.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Table of Contents
|
|
13
|
-
|
|
14
|
-
1. [Design Goals](#1-design-goals)
|
|
15
|
-
2. [Architecture Overview](#2-architecture-overview)
|
|
16
|
-
3. [Data Model](#3-data-model)
|
|
17
|
-
4. [Module Structure](#4-module-structure)
|
|
18
|
-
5. [Local Storage Layer](#5-local-storage-layer)
|
|
19
|
-
6. [Storage Facade](#6-storage-facade)
|
|
20
|
-
7. [Server Storage Providers](#7-server-storage-providers)
|
|
21
|
-
8. [API Routes](#8-api-routes)
|
|
22
|
-
9. [Write-Through Cache & Sync Hook](#9-write-through-cache--sync-hook)
|
|
23
|
-
10. [Migration Flow](#10-migration-flow)
|
|
24
|
-
11. [Configuration](#11-configuration)
|
|
25
|
-
12. [User Scoping & Security](#12-user-scoping--security)
|
|
26
|
-
13. [Docker Deployment](#13-docker-deployment)
|
|
27
|
-
14. [Adding a New Provider](#14-adding-a-new-provider)
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## 1. Design Goals
|
|
32
|
-
|
|
33
|
-
| Goal | Approach |
|
|
34
|
-
|------|----------|
|
|
35
|
-
| **Zero breaking changes** | All 16+ consumer components keep the same synchronous `storage.*` API |
|
|
36
|
-
| **Zero-config default** | `localStorage` works out of the box — no database, no env vars needed |
|
|
37
|
-
| **Single image, all modes** | Runtime config via env var, not build-time `NEXT_PUBLIC_*` |
|
|
38
|
-
| **Per-user isolation** | Server storage scoped by JWT `username` — no cross-user leaks |
|
|
39
|
-
| **Graceful degradation** | If server is unreachable, `localStorage` continues to work |
|
|
40
|
-
| **Extensible** | Adding a new backend (e.g., MySQL, DynamoDB) requires one file implementing `ServerStorageProvider` |
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## 2. Architecture Overview
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
┌──────────────────────────────┐
|
|
48
|
-
│ 16+ Consumer Components │ ← Unchanged, same sync API
|
|
49
|
-
│ storage.getConnections() │
|
|
50
|
-
│ storage.saveConnection() │
|
|
51
|
-
└──────────────┬───────────────┘
|
|
52
|
-
│ sync read/write
|
|
53
|
-
┌──────────────▼───────────────┐
|
|
54
|
-
│ Storage Facade │ ← localStorage read/write + CustomEvent dispatch
|
|
55
|
-
│ src/lib/storage/ │
|
|
56
|
-
│ storage-facade.ts │
|
|
57
|
-
└──────────────┬───────────────┘
|
|
58
|
-
│ CustomEvent: 'libredb-storage-change'
|
|
59
|
-
┌──────────────▼───────────────┐
|
|
60
|
-
│ useStorageSync Hook │ ← Mounted in Studio.tsx (server mode only)
|
|
61
|
-
│ src/hooks/ │
|
|
62
|
-
│ use-storage-sync.ts │
|
|
63
|
-
└──────────────┬───────────────┘
|
|
64
|
-
│ fetch (debounced 500ms)
|
|
65
|
-
┌──────────────▼───────────────┐
|
|
66
|
-
│ API Routes │ ← JWT auth + user scoping
|
|
67
|
-
│ /api/storage/* │
|
|
68
|
-
└──────────────┬───────────────┘
|
|
69
|
-
│
|
|
70
|
-
┌──────────────▼───────────────┐
|
|
71
|
-
│ ServerStorageProvider │ ← Strategy Pattern
|
|
72
|
-
│ ┌─────────┐ ┌────────────┐ │
|
|
73
|
-
│ │ SQLite │ │ PostgreSQL │ │
|
|
74
|
-
│ └─────────┘ └────────────┘ │
|
|
75
|
-
└──────────────────────────────┘
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**Key insight:** `localStorage` is always the **rendering source** (L1 cache). The server database is the **persistent source of truth** (L2). The sync hook keeps them in sync via a write-through cache pattern.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## 3. Data Model
|
|
83
|
-
|
|
84
|
-
### 3.1 Collections
|
|
85
|
-
|
|
86
|
-
All application state is organized into **9 collections**, each stored as a JSON blob:
|
|
87
|
-
|
|
88
|
-
| Collection | Type | Description | Max Items |
|
|
89
|
-
|-----------|------|-------------|-----------|
|
|
90
|
-
| `connections` | `DatabaseConnection[]` | Saved database connections | — |
|
|
91
|
-
| `history` | `QueryHistoryItem[]` | Query execution history | 500 |
|
|
92
|
-
| `saved_queries` | `SavedQuery[]` | User-saved SQL/JSON queries | — |
|
|
93
|
-
| `schema_snapshots` | `SchemaSnapshot[]` | Schema diff snapshots | 50 |
|
|
94
|
-
| `saved_charts` | `SavedChartConfig[]` | Saved chart configurations | — |
|
|
95
|
-
| `active_connection_id` | `string \| null` | Currently active connection | — |
|
|
96
|
-
| `audit_log` | `AuditEvent[]` | Audit trail events | 1000 |
|
|
97
|
-
| `masking_config` | `MaskingConfig` | Data masking rules and RBAC | — |
|
|
98
|
-
| `threshold_config` | `ThresholdConfig[]` | Monitoring alert thresholds | — |
|
|
99
|
-
|
|
100
|
-
### 3.2 Server Database Schema
|
|
101
|
-
|
|
102
|
-
Both SQLite and PostgreSQL use the same logical schema — a single table with collection-based JSON blobs:
|
|
103
|
-
|
|
104
|
-
```sql
|
|
105
|
-
CREATE TABLE IF NOT EXISTS user_storage (
|
|
106
|
-
user_id TEXT NOT NULL, -- JWT username (email)
|
|
107
|
-
collection TEXT NOT NULL, -- 'connections', 'history', etc.
|
|
108
|
-
data TEXT NOT NULL, -- JSON serialized
|
|
109
|
-
updated_at TEXT/TIMESTAMPTZ NOT NULL, -- Last modification time
|
|
110
|
-
PRIMARY KEY (user_id, collection)
|
|
111
|
-
);
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
This design is intentionally simple:
|
|
115
|
-
- **No schema migrations** needed when adding new collections
|
|
116
|
-
- **One row per user per collection** — efficient upsert
|
|
117
|
-
- **JSON blobs** keep the server storage schema-agnostic
|
|
118
|
-
|
|
119
|
-
### 3.3 localStorage Keys
|
|
120
|
-
|
|
121
|
-
Each collection maps to a `libredb_`-prefixed localStorage key:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
connections → libredb_connections
|
|
125
|
-
history → libredb_history
|
|
126
|
-
saved_queries → libredb_saved_queries
|
|
127
|
-
schema_snapshots → libredb_schema_snapshots
|
|
128
|
-
saved_charts → libredb_saved_charts
|
|
129
|
-
active_connection_id → libredb_active_connection_id
|
|
130
|
-
audit_log → libredb_audit_log
|
|
131
|
-
masking_config → libredb_masking_config
|
|
132
|
-
threshold_config → libredb_threshold_config
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## 4. Module Structure
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
src/lib/storage/
|
|
141
|
-
├── index.ts # Barrel export — preserves @/lib/storage import path
|
|
142
|
-
├── types.ts # StorageData, StorageCollection, ServerStorageProvider
|
|
143
|
-
├── local-storage.ts # Pure localStorage CRUD (SSR-safe)
|
|
144
|
-
├── storage-facade.ts # Public storage object with domain methods
|
|
145
|
-
├── factory.ts # Env-based provider instantiation (singleton)
|
|
146
|
-
└── providers/
|
|
147
|
-
├── sqlite.ts # better-sqlite3 implementation
|
|
148
|
-
└── postgres.ts # pg (Pool) implementation
|
|
149
|
-
|
|
150
|
-
src/hooks/
|
|
151
|
-
└── use-storage-sync.ts # Write-through cache hook
|
|
152
|
-
|
|
153
|
-
src/app/api/storage/
|
|
154
|
-
├── config/route.ts # GET: storage mode discovery (public)
|
|
155
|
-
├── route.ts # GET: fetch all user data (auth required)
|
|
156
|
-
├── [collection]/route.ts # PUT: update single collection (auth required)
|
|
157
|
-
└── migrate/route.ts # POST: localStorage → server migration (auth required)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## 5. Local Storage Layer
|
|
163
|
-
|
|
164
|
-
**File:** `src/lib/storage/local-storage.ts`
|
|
165
|
-
|
|
166
|
-
Pure, side-effect-free localStorage CRUD with SSR safety:
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
// All operations check isClient() before accessing localStorage
|
|
170
|
-
export function readJSON<T>(collection: string): T | null;
|
|
171
|
-
export function writeJSON(collection: string, data: unknown): void;
|
|
172
|
-
export function readString(collection: string): string | null;
|
|
173
|
-
export function writeString(collection: string, value: string): void;
|
|
174
|
-
export function remove(collection: string): void;
|
|
175
|
-
export function getKey(collection: string): string; // → 'libredb_' + collection
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
- Every function is guarded by `isClient()` — safe to call during SSR (returns `null` / no-op)
|
|
179
|
-
- JSON parse failures return `null` instead of throwing
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
## 6. Storage Facade
|
|
184
|
-
|
|
185
|
-
**File:** `src/lib/storage/storage-facade.ts`
|
|
186
|
-
|
|
187
|
-
The public `storage` object provides the same **synchronous API** that all 16+ consumer components use. Every mutation method:
|
|
188
|
-
|
|
189
|
-
1. Writes to `localStorage` (immediate)
|
|
190
|
-
2. Dispatches a `CustomEvent('libredb-storage-change')` with the collection name and data
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
// Example: saving a connection
|
|
194
|
-
storage.saveConnection(conn);
|
|
195
|
-
// 1. Reads existing connections from localStorage
|
|
196
|
-
// 2. Upserts by ID
|
|
197
|
-
// 3. Writes back to localStorage
|
|
198
|
-
// 4. Dispatches CustomEvent({ collection: 'connections', data: updatedList })
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### Public API
|
|
202
|
-
|
|
203
|
-
| Category | Methods |
|
|
204
|
-
|----------|---------|
|
|
205
|
-
| **Connections** | `getConnections()`, `saveConnection(conn)`, `deleteConnection(id)` |
|
|
206
|
-
| **History** | `getHistory()`, `addToHistory(item)`, `clearHistory()` |
|
|
207
|
-
| **Saved Queries** | `getSavedQueries()`, `saveQuery(query)`, `deleteSavedQuery(id)` |
|
|
208
|
-
| **Schema Snapshots** | `getSchemaSnapshots(connId?)`, `saveSchemaSnapshot(snap)`, `deleteSchemaSnapshot(id)` |
|
|
209
|
-
| **Charts** | `getSavedCharts()`, `saveChart(chart)`, `deleteChart(id)` |
|
|
210
|
-
| **Active Connection** | `getActiveConnectionId()`, `setActiveConnectionId(id)` |
|
|
211
|
-
| **Audit Log** | `getAuditLog()`, `saveAuditLog(events)` |
|
|
212
|
-
| **Masking Config** | `getMaskingConfig()`, `saveMaskingConfig(config)` |
|
|
213
|
-
| **Threshold Config** | `getThresholdConfig()`, `saveThresholdConfig(thresholds)` |
|
|
214
|
-
|
|
215
|
-
All read methods are **synchronous** — they read from `localStorage` only. No network calls.
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## 7. Server Storage Providers
|
|
220
|
-
|
|
221
|
-
### 7.1 Provider Interface
|
|
222
|
-
|
|
223
|
-
**File:** `src/lib/storage/types.ts`
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
interface ServerStorageProvider {
|
|
227
|
-
initialize(): Promise<void>;
|
|
228
|
-
getAllData(userId: string): Promise<Partial<StorageData>>;
|
|
229
|
-
getCollection<K extends StorageCollection>(
|
|
230
|
-
userId: string, collection: K
|
|
231
|
-
): Promise<StorageData[K] | null>;
|
|
232
|
-
setCollection<K extends StorageCollection>(
|
|
233
|
-
userId: string, collection: K, data: StorageData[K]
|
|
234
|
-
): Promise<void>;
|
|
235
|
-
mergeData(userId: string, data: Partial<StorageData>): Promise<void>;
|
|
236
|
-
isHealthy(): Promise<boolean>;
|
|
237
|
-
close(): Promise<void>;
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### 7.2 SQLite Provider
|
|
242
|
-
|
|
243
|
-
**File:** `src/lib/storage/providers/sqlite.ts`
|
|
244
|
-
**Package:** `better-sqlite3` (Node.js compatible, not `bun:sqlite`)
|
|
245
|
-
|
|
246
|
-
| Feature | Detail |
|
|
247
|
-
|---------|--------|
|
|
248
|
-
| **WAL mode** | Enabled for concurrent read performance |
|
|
249
|
-
| **Auto-create** | Directory and database file created on `initialize()` |
|
|
250
|
-
| **Upsert** | `INSERT OR REPLACE INTO user_storage` |
|
|
251
|
-
| **Transactions** | `mergeData()` wraps all inserts in a single transaction |
|
|
252
|
-
| **Health check** | `SELECT 1 AS ok` |
|
|
253
|
-
|
|
254
|
-
```env
|
|
255
|
-
STORAGE_PROVIDER=sqlite
|
|
256
|
-
STORAGE_SQLITE_PATH=./data/libredb-storage.db # default
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 7.3 PostgreSQL Provider
|
|
260
|
-
|
|
261
|
-
**File:** `src/lib/storage/providers/postgres.ts`
|
|
262
|
-
**Package:** `pg` (connection pool)
|
|
263
|
-
|
|
264
|
-
| Feature | Detail |
|
|
265
|
-
|---------|--------|
|
|
266
|
-
| **Pool config** | max: 5, idleTimeoutMillis: 30000 |
|
|
267
|
-
| **SSL behavior** | `sslmode=disable` for local/non-SSL servers, `sslmode=require` for cloud servers |
|
|
268
|
-
| **Upsert** | `INSERT ... ON CONFLICT (user_id, collection) DO UPDATE` |
|
|
269
|
-
| **Transactions** | `mergeData()` uses `BEGIN`/`COMMIT`/`ROLLBACK` with client checkout |
|
|
270
|
-
| **Health check** | `SELECT 1 AS ok` |
|
|
271
|
-
|
|
272
|
-
```env
|
|
273
|
-
STORAGE_PROVIDER=postgres
|
|
274
|
-
STORAGE_POSTGRES_URL=postgresql://user:pass@localhost:5432/libredb?sslmode=disable
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### 7.4 Factory
|
|
278
|
-
|
|
279
|
-
**File:** `src/lib/storage/factory.ts`
|
|
280
|
-
|
|
281
|
-
The factory uses the **Singleton pattern** — one provider instance per process, lazy-initialized on first access:
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
getStorageProviderType() // → 'local' | 'sqlite' | 'postgres'
|
|
285
|
-
isServerStorageEnabled() // → true if not 'local'
|
|
286
|
-
getStorageConfig() // → { provider, serverMode }
|
|
287
|
-
getStorageProvider() // → ServerStorageProvider | null (singleton)
|
|
288
|
-
closeStorageProvider() // → cleanup for testing
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
Provider classes are **dynamically imported** — SQLite and PostgreSQL dependencies are only loaded when their provider is selected.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## 8. API Routes
|
|
296
|
-
|
|
297
|
-
All routes (except `/config`) require JWT authentication. The authenticated user's `username` (email) is used as the `user_id` for storage scoping.
|
|
298
|
-
|
|
299
|
-
| Endpoint | Method | Auth | Purpose |
|
|
300
|
-
|----------|--------|------|---------|
|
|
301
|
-
| `/api/storage/config` | GET | Public | Runtime storage mode discovery |
|
|
302
|
-
| `/api/storage` | GET | JWT | Fetch all collections for the authenticated user |
|
|
303
|
-
| `/api/storage/[collection]` | PUT | JWT | Update a single collection |
|
|
304
|
-
| `/api/storage/migrate` | POST | JWT | Merge localStorage dump into server storage |
|
|
305
|
-
|
|
306
|
-
### Response Examples
|
|
307
|
-
|
|
308
|
-
**GET /api/storage/config**
|
|
309
|
-
```json
|
|
310
|
-
{ "provider": "sqlite", "serverMode": true }
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
**GET /api/storage**
|
|
314
|
-
```json
|
|
315
|
-
{
|
|
316
|
-
"connections": [{ "id": "c1", "name": "Prod DB", ... }],
|
|
317
|
-
"history": [{ "id": "h1", "query": "SELECT ...", ... }],
|
|
318
|
-
...
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
**PUT /api/storage/connections**
|
|
323
|
-
```json
|
|
324
|
-
// Request: { "data": [{ "id": "c1", "name": "Prod DB", ... }] }
|
|
325
|
-
// Response: { "ok": true }
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
**POST /api/storage/migrate**
|
|
329
|
-
```json
|
|
330
|
-
// Request: { "connections": [...], "history": [...], ... }
|
|
331
|
-
// Response: { "ok": true, "migrated": ["connections", "history"] }
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
When `STORAGE_PROVIDER=local`, all data routes return `404 Not Found` (config route always works).
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
## 9. Write-Through Cache & Sync Hook
|
|
339
|
-
|
|
340
|
-
**File:** `src/hooks/use-storage-sync.ts`
|
|
341
|
-
|
|
342
|
-
The hook is mounted in `Studio.tsx` after `useAuth()` and orchestrates all client-server synchronization.
|
|
343
|
-
|
|
344
|
-
### Sync States
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
interface StorageSyncState {
|
|
348
|
-
isServerMode: boolean; // Server storage active?
|
|
349
|
-
isSyncing: boolean; // Currently transferring data?
|
|
350
|
-
lastSyncedAt: Date | null; // Last successful sync timestamp
|
|
351
|
-
syncError: string | null; // Last error message (null = healthy)
|
|
352
|
-
}
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### Lifecycle
|
|
356
|
-
|
|
357
|
-
```
|
|
358
|
-
App Mount
|
|
359
|
-
│
|
|
360
|
-
├─ GET /api/storage/config
|
|
361
|
-
│ ├─ serverMode: false → done (localStorage only)
|
|
362
|
-
│ └─ serverMode: true ──┐
|
|
363
|
-
│ │
|
|
364
|
-
│ ┌──────────────────────▼──────────────────────┐
|
|
365
|
-
│ │ Check libredb_server_migrated flag │
|
|
366
|
-
│ │ ├─ Not migrated → POST /api/storage/migrate│
|
|
367
|
-
│ │ │ (send all localStorage → server merge) │
|
|
368
|
-
│ │ │ Set flag in localStorage │
|
|
369
|
-
│ │ └─ Already migrated → skip │
|
|
370
|
-
│ └──────────────────────┬──────────────────────┘
|
|
371
|
-
│ │
|
|
372
|
-
│ ┌──────────────────────▼──────────────────────┐
|
|
373
|
-
│ │ Pull: GET /api/storage │
|
|
374
|
-
│ │ → Write server data into localStorage │
|
|
375
|
-
│ │ → Components re-render from localStorage │
|
|
376
|
-
│ └──────────────────────┬──────────────────────┘
|
|
377
|
-
│ │
|
|
378
|
-
│ ┌──────────────────────▼──────────────────────┐
|
|
379
|
-
│ │ Listen: 'libredb-storage-change' events │
|
|
380
|
-
│ │ → Collect pending collections │
|
|
381
|
-
│ │ → Debounce 500ms │
|
|
382
|
-
│ │ → PUT /api/storage/[collection] for each │
|
|
383
|
-
│ └─────────────────────────────────────────────┘
|
|
384
|
-
│
|
|
385
|
-
▼ (ongoing)
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Push Behavior (Debounced)
|
|
389
|
-
|
|
390
|
-
When any `storage.*` mutation fires:
|
|
391
|
-
|
|
392
|
-
1. Facade writes to `localStorage` (immediate, synchronous)
|
|
393
|
-
2. Facade dispatches `CustomEvent('libredb-storage-change', { collection, data })`
|
|
394
|
-
3. Hook captures event, adds collection to pending set
|
|
395
|
-
4. After 500ms of no new mutations, hook flushes:
|
|
396
|
-
- Reads each pending collection from `localStorage`
|
|
397
|
-
- Sends `PUT /api/storage/[collection]` for each
|
|
398
|
-
|
|
399
|
-
### Graceful Degradation
|
|
400
|
-
|
|
401
|
-
- If `/api/storage/config` fails → stays in localStorage-only mode
|
|
402
|
-
- If push fails → logs warning, sets `syncError`, does **not** block the UI
|
|
403
|
-
- Components always read from `localStorage` — no loading states for storage
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## 10. Migration Flow
|
|
408
|
-
|
|
409
|
-
When a user first enables server mode (or a new user logs in for the first time):
|
|
410
|
-
|
|
411
|
-
```
|
|
412
|
-
1. Hook detects serverMode = true
|
|
413
|
-
2. Checks localStorage('libredb_server_migrated') flag
|
|
414
|
-
3. If not migrated:
|
|
415
|
-
a. Reads all 9 collections from localStorage
|
|
416
|
-
b. POST /api/storage/migrate with full payload
|
|
417
|
-
c. Server calls provider.mergeData() — ID-based deduplication
|
|
418
|
-
d. Sets 'libredb_server_migrated' flag in localStorage
|
|
419
|
-
4. Pull: GET /api/storage → overwrite localStorage with server data
|
|
420
|
-
5. Subsequent mutations sync normally via push
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
This ensures existing localStorage data is preserved when transitioning to server mode.
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
## 11. Configuration
|
|
428
|
-
|
|
429
|
-
### Environment Variables
|
|
430
|
-
|
|
431
|
-
| Variable | Default | Required | Description |
|
|
432
|
-
|----------|---------|----------|-------------|
|
|
433
|
-
| `STORAGE_PROVIDER` | `local` | No | Storage backend: `local`, `sqlite`, or `postgres` |
|
|
434
|
-
| `STORAGE_SQLITE_PATH` | `./data/libredb-storage.db` | No | Path to SQLite database file |
|
|
435
|
-
| `STORAGE_POSTGRES_URL` | — | If `postgres` | PostgreSQL connection string (`sslmode=disable` local, `sslmode=require` cloud) |
|
|
436
|
-
|
|
437
|
-
### Why Not `NEXT_PUBLIC_*`?
|
|
438
|
-
|
|
439
|
-
Next.js `NEXT_PUBLIC_*` variables are **inlined at build time** as static strings. This means:
|
|
440
|
-
- Every storage mode would require a separate Docker build
|
|
441
|
-
- Cannot change storage mode without rebuilding
|
|
442
|
-
|
|
443
|
-
Instead, the client discovers the storage mode at **runtime** via `GET /api/storage/config`. One Docker image supports all modes.
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## 12. User Scoping & Security
|
|
448
|
-
|
|
449
|
-
### Per-User Isolation
|
|
450
|
-
|
|
451
|
-
Every row in `user_storage` is scoped by `user_id`:
|
|
452
|
-
|
|
453
|
-
```
|
|
454
|
-
(admin@libredb.org, connections) → [{"id":"c1", "name":"Prod DB"...}]
|
|
455
|
-
(admin@libredb.org, history) → [{"id":"h1", "query":"SELECT..."...}]
|
|
456
|
-
(user@libredb.org, connections) → [{"id":"c2", "name":"Dev DB"...}]
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
- `user_id` = JWT session `username` (email address)
|
|
460
|
-
- **Client never sends `user_id`** — server always extracts from JWT cookie
|
|
461
|
-
- Every query includes `WHERE user_id = $username` — no cross-user access possible
|
|
462
|
-
|
|
463
|
-
### Authentication
|
|
464
|
-
|
|
465
|
-
- `/api/storage/config` is **public** — returns only `{ provider, serverMode }`, no sensitive data
|
|
466
|
-
- All other `/api/storage/*` routes require a valid JWT session via `getSession()`
|
|
467
|
-
- Unauthorized requests receive `401 Unauthorized`
|
|
468
|
-
|
|
469
|
-
### OIDC Users
|
|
470
|
-
|
|
471
|
-
OIDC users (Auth0, Keycloak, Okta, Azure AD) have their `preferred_username` or email claim mapped to the same `username` field used as `user_id`.
|
|
472
|
-
|
|
473
|
-
---
|
|
474
|
-
|
|
475
|
-
## 13. Docker Deployment
|
|
476
|
-
|
|
477
|
-
### SQLite Mode
|
|
478
|
-
|
|
479
|
-
```yaml
|
|
480
|
-
# docker-compose.yml
|
|
481
|
-
services:
|
|
482
|
-
libredb-studio:
|
|
483
|
-
environment:
|
|
484
|
-
STORAGE_PROVIDER: sqlite
|
|
485
|
-
STORAGE_SQLITE_PATH: /app/data/libredb-storage.db
|
|
486
|
-
volumes:
|
|
487
|
-
- storage-data:/app/data
|
|
488
|
-
|
|
489
|
-
volumes:
|
|
490
|
-
storage-data:
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
The Dockerfile includes `better-sqlite3` native bindings and creates the `/app/data` directory.
|
|
494
|
-
|
|
495
|
-
### PostgreSQL Mode
|
|
496
|
-
|
|
497
|
-
```yaml
|
|
498
|
-
services:
|
|
499
|
-
libredb-studio:
|
|
500
|
-
environment:
|
|
501
|
-
STORAGE_PROVIDER: postgres
|
|
502
|
-
STORAGE_POSTGRES_URL: postgresql://user:pass@db:5432/libredb
|
|
503
|
-
depends_on:
|
|
504
|
-
- db
|
|
505
|
-
db:
|
|
506
|
-
image: postgres:16-alpine
|
|
507
|
-
environment:
|
|
508
|
-
POSTGRES_DB: libredb
|
|
509
|
-
POSTGRES_USER: user
|
|
510
|
-
POSTGRES_PASSWORD: pass
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
No volume needed on the app container — data lives in PostgreSQL.
|
|
514
|
-
|
|
515
|
-
---
|
|
516
|
-
|
|
517
|
-
## 14. Adding a New Provider
|
|
518
|
-
|
|
519
|
-
To add a new storage backend (e.g., MySQL, DynamoDB):
|
|
520
|
-
|
|
521
|
-
### Step 1: Implement the Interface
|
|
522
|
-
|
|
523
|
-
Create `src/lib/storage/providers/your-provider.ts`:
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
import type { ServerStorageProvider, StorageData, StorageCollection } from '../types';
|
|
527
|
-
|
|
528
|
-
export class YourStorageProvider implements ServerStorageProvider {
|
|
529
|
-
async initialize(): Promise<void> { /* create table */ }
|
|
530
|
-
async getAllData(userId: string): Promise<Partial<StorageData>> { /* ... */ }
|
|
531
|
-
async getCollection<K extends StorageCollection>(
|
|
532
|
-
userId: string, collection: K
|
|
533
|
-
): Promise<StorageData[K] | null> { /* ... */ }
|
|
534
|
-
async setCollection<K extends StorageCollection>(
|
|
535
|
-
userId: string, collection: K, data: StorageData[K]
|
|
536
|
-
): Promise<void> { /* upsert */ }
|
|
537
|
-
async mergeData(
|
|
538
|
-
userId: string, data: Partial<StorageData>
|
|
539
|
-
): Promise<void> { /* batch upsert in transaction */ }
|
|
540
|
-
async isHealthy(): Promise<boolean> { /* SELECT 1 */ }
|
|
541
|
-
async close(): Promise<void> { /* cleanup */ }
|
|
542
|
-
}
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Step 2: Register in Factory
|
|
546
|
-
|
|
547
|
-
Update `src/lib/storage/factory.ts`:
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
// Add to StorageProviderType
|
|
551
|
-
type StorageProviderType = 'local' | 'sqlite' | 'postgres' | 'your-provider';
|
|
552
|
-
|
|
553
|
-
// Add dynamic import in getStorageProvider()
|
|
554
|
-
case 'your-provider': {
|
|
555
|
-
const { YourStorageProvider } = await import('./providers/your-provider');
|
|
556
|
-
instance = new YourStorageProvider(process.env.STORAGE_YOUR_URL!);
|
|
557
|
-
break;
|
|
558
|
-
}
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
### Step 3: Add Tests
|
|
562
|
-
|
|
563
|
-
Create `tests/unit/lib/storage/providers/your-provider.test.ts` with mocked driver.
|
|
564
|
-
|
|
565
|
-
That's it — no changes needed to the facade, API routes, sync hook, or any consumer components.
|