@useatlas/create 0.0.2 → 0.0.4
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/README.md +4 -18
- package/index.ts +191 -31
- package/package.json +1 -1
- package/templates/docker/.env.example +3 -3
- package/templates/docker/Dockerfile.sidecar +28 -0
- package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/docker/bin/atlas.ts +108 -44
- package/templates/docker/data/demo-semantic/catalog.yml +51 -27
- package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
- package/templates/docker/data/demo-semantic/glossary.yml +104 -8
- package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/docker/docker-compose.yml +1 -1
- package/templates/docker/docs/deploy.md +2 -39
- package/templates/docker/package.json +17 -1
- package/templates/docker/semantic/catalog.yml +62 -3
- package/templates/docker/semantic/entities/accounts.yml +162 -0
- package/templates/docker/semantic/entities/companies.yml +143 -0
- package/templates/docker/semantic/entities/people.yml +132 -0
- package/templates/docker/semantic/glossary.yml +116 -4
- package/templates/docker/semantic/metrics/accounts.yml +77 -0
- package/templates/docker/semantic/metrics/companies.yml +63 -0
- package/templates/docker/sidecar/Dockerfile +5 -6
- package/templates/docker/sidecar/railway.json +1 -2
- package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/docker/src/api/__tests__/health.test.ts +30 -8
- package/templates/docker/src/api/routes/admin.ts +549 -8
- package/templates/docker/src/api/routes/chat.ts +5 -20
- package/templates/docker/src/api/routes/health.ts +39 -27
- package/templates/docker/src/api/routes/openapi.ts +1329 -74
- package/templates/docker/src/api/routes/query.ts +2 -1
- package/templates/docker/src/api/server.ts +27 -0
- package/templates/docker/src/app/api/[...route]/route.ts +2 -2
- package/templates/docker/src/app/globals.css +13 -12
- package/templates/docker/src/app/layout.tsx +9 -2
- package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/docker/src/components/ui/badge.tsx +48 -0
- package/templates/docker/src/components/ui/button.tsx +64 -0
- package/templates/docker/src/components/ui/card.tsx +92 -0
- package/templates/docker/src/components/ui/collapsible.tsx +33 -0
- package/templates/docker/src/components/ui/command.tsx +184 -0
- package/templates/docker/src/components/ui/dialog.tsx +158 -0
- package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/docker/src/components/ui/input.tsx +21 -0
- package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
- package/templates/docker/src/components/ui/select.tsx +190 -0
- package/templates/docker/src/components/ui/separator.tsx +28 -0
- package/templates/docker/src/components/ui/sheet.tsx +143 -0
- package/templates/docker/src/components/ui/sidebar.tsx +726 -0
- package/templates/docker/src/components/ui/skeleton.tsx +13 -0
- package/templates/docker/src/components/ui/table.tsx +116 -0
- package/templates/docker/src/components/ui/tabs.tsx +91 -0
- package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
- package/templates/docker/src/components/ui/toggle.tsx +47 -0
- package/templates/docker/src/components/ui/tooltip.tsx +57 -0
- package/templates/docker/src/hooks/use-mobile.ts +19 -0
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
- package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/docker/src/lib/agent-query.ts +5 -23
- package/templates/docker/src/lib/agent.ts +32 -112
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/docker/src/lib/auth/middleware.ts +30 -4
- package/templates/docker/src/lib/auth/migrate.ts +97 -0
- package/templates/docker/src/lib/auth/server.ts +12 -1
- package/templates/docker/src/lib/config.ts +37 -39
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/docker/src/lib/db/connection.ts +87 -265
- package/templates/docker/src/lib/db/internal.ts +6 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/docker/src/lib/plugins/index.ts +4 -1
- package/templates/docker/src/lib/plugins/migrate.ts +103 -0
- package/templates/docker/src/lib/plugins/registry.ts +12 -6
- package/templates/docker/src/lib/plugins/wiring.ts +113 -4
- package/templates/docker/src/lib/providers.ts +6 -1
- package/templates/docker/src/lib/security.ts +24 -0
- package/templates/docker/src/lib/semantic.ts +2 -0
- package/templates/docker/src/lib/sidecar-types.ts +12 -1
- package/templates/docker/src/lib/startup.ts +71 -101
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/docker/src/lib/tools/explore.ts +28 -32
- package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/docker/src/lib/tools/python.ts +367 -0
- package/templates/docker/src/lib/tools/registry.ts +49 -22
- package/templates/docker/src/lib/tools/sql.ts +88 -88
- package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/docker/src/ui/context.tsx +1 -1
- package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/docker/tsconfig.json +2 -2
- package/templates/nextjs-standalone/.env.example +1 -1
- package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
- package/templates/nextjs-standalone/bin/atlas.ts +108 -44
- package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
- package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
- package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
- package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
- package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
- package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
- package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
- package/templates/nextjs-standalone/docs/deploy.md +2 -39
- package/templates/nextjs-standalone/package.json +11 -2
- package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
- package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
- package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
- package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
- package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
- package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
- package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
- package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
- package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
- package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
- package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
- package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
- package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
- package/templates/nextjs-standalone/src/api/server.ts +27 -0
- package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
- package/templates/nextjs-standalone/src/app/globals.css +13 -12
- package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
- package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
- package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
- package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
- package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
- package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
- package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
- package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
- package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
- package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
- package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
- package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
- package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
- package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
- package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
- package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
- package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
- package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
- package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
- package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
- package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
- package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
- package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
- package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
- package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
- package/templates/nextjs-standalone/src/lib/config.ts +37 -39
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
- package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
- package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
- package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
- package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
- package/templates/nextjs-standalone/src/lib/security.ts +24 -0
- package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
- package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
- package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
- package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
- package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
- package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
- package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
- package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
- package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
- package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
- package/templates/nextjs-standalone/tsconfig.json +0 -1
- package/templates/nextjs-standalone/vercel.json +4 -1
- package/templates/docker/render.yaml +0 -34
- package/templates/docker/semantic/entities/.gitkeep +0 -0
- package/templates/docker/semantic/metrics/.gitkeep +0 -0
- package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/docker/src/lib/db/duckdb.ts +0 -122
- package/templates/docker/src/lib/db/salesforce.ts +0 -342
- package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/docker/src/lib/tools/salesforce.ts +0 -138
- package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
- package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
- package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
- package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
- package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
- package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
- package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
- package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
- package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
- package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
- package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
- package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Database connection factory and registry.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Core adapters: PostgreSQL (via `pg` Pool) and MySQL (via `mysql2/promise`).
|
|
5
|
+
* Additional databases (ClickHouse, Snowflake, DuckDB, Salesforce) are
|
|
6
|
+
* supported via datasource plugins — see `plugins/` directory.
|
|
7
|
+
*
|
|
8
8
|
* Database type is detected from the connection URL format:
|
|
9
9
|
* - `postgresql://` or `postgres://` → PostgreSQL
|
|
10
10
|
* - `mysql://` or `mysql2://` → MySQL
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - `duckdb://` → DuckDB (in-process)
|
|
14
|
-
* - `salesforce://` → Salesforce (SOQL — uses separate DataSource API)
|
|
11
|
+
*
|
|
12
|
+
* Non-core URL schemes require a registered datasource plugin.
|
|
15
13
|
*
|
|
16
14
|
* Connections are managed via ConnectionRegistry. The default connection
|
|
17
15
|
* auto-initializes from ATLAS_DATASOURCE_URL on first access.
|
|
@@ -19,10 +17,27 @@
|
|
|
19
17
|
|
|
20
18
|
import { createLogger } from "@atlas/api/lib/logger";
|
|
21
19
|
import { _resetWhitelists } from "@atlas/api/lib/semantic";
|
|
22
|
-
import { createDuckDBConnection, parseDuckDBUrl } from "./duckdb";
|
|
23
20
|
|
|
24
21
|
const log = createLogger("db");
|
|
25
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the analytics datasource URL from env vars.
|
|
25
|
+
*
|
|
26
|
+
* Priority:
|
|
27
|
+
* 1. ATLAS_DATASOURCE_URL (explicit — always wins)
|
|
28
|
+
* 2. DATABASE_URL_UNPOOLED / DATABASE_URL (when ATLAS_DEMO_DATA=true — share
|
|
29
|
+
* the Neon-provisioned DB for both internal and analytics)
|
|
30
|
+
*
|
|
31
|
+
* Returns undefined when no datasource is configured.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveDatasourceUrl(): string | undefined {
|
|
34
|
+
if (process.env.ATLAS_DATASOURCE_URL) return process.env.ATLAS_DATASOURCE_URL;
|
|
35
|
+
if (process.env.ATLAS_DEMO_DATA === "true") {
|
|
36
|
+
return process.env.DATABASE_URL_UNPOOLED || process.env.DATABASE_URL;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
export interface QueryResult {
|
|
27
42
|
columns: string[];
|
|
28
43
|
rows: Record<string, unknown>[];
|
|
@@ -33,7 +48,7 @@ export interface DBConnection {
|
|
|
33
48
|
close(): Promise<void>;
|
|
34
49
|
}
|
|
35
50
|
|
|
36
|
-
export type DBType = "postgres" | "mysql" |
|
|
51
|
+
export type DBType = "postgres" | "mysql" | (string & {});
|
|
37
52
|
|
|
38
53
|
export type HealthStatus = "healthy" | "degraded" | "unhealthy";
|
|
39
54
|
|
|
@@ -65,7 +80,8 @@ export function extractTargetHost(url: string): string {
|
|
|
65
80
|
try {
|
|
66
81
|
// Normalize known schemes to http:// so URL parser can handle them
|
|
67
82
|
const normalized = url
|
|
68
|
-
.replace(/^(postgresql|postgres|mysql|mysql2
|
|
83
|
+
.replace(/^(postgresql|postgres|mysql|mysql2):\/\//, "http://")
|
|
84
|
+
.replace(/^[a-z][a-z0-9+.-]*:\/\//, "http://");
|
|
69
85
|
const parsed = new URL(normalized);
|
|
70
86
|
return parsed.hostname || "(unknown)";
|
|
71
87
|
} catch {
|
|
@@ -73,50 +89,18 @@ export function extractTargetHost(url: string): string {
|
|
|
73
89
|
}
|
|
74
90
|
}
|
|
75
91
|
|
|
76
|
-
/**
|
|
77
|
-
* Rewrite a `clickhouse://` or `clickhouses://` URL to `http://` or `https://`
|
|
78
|
-
* for the @clickhouse/client HTTP transport.
|
|
79
|
-
*
|
|
80
|
-
* - `clickhouses://` → `https://` (TLS)
|
|
81
|
-
* - `clickhouse://` → `http://` (plain)
|
|
82
|
-
*
|
|
83
|
-
* Warns if port 8443 (the conventional ClickHouse TLS port) is used with
|
|
84
|
-
* the plain `clickhouse://` scheme, since TLS is likely intended.
|
|
85
|
-
*/
|
|
86
|
-
export function rewriteClickHouseUrl(url: string): string {
|
|
87
|
-
if (url.startsWith("clickhouses://")) {
|
|
88
|
-
return url.replace(/^clickhouses:\/\//, "https://");
|
|
89
|
-
}
|
|
90
|
-
// Warn on probable TLS-port + plain-scheme mismatch
|
|
91
|
-
try {
|
|
92
|
-
const parsed = new URL(url.replace(/^clickhouse:\/\//, "http://"));
|
|
93
|
-
if (parsed.port === "8443") {
|
|
94
|
-
log.warn(
|
|
95
|
-
"clickhouse:// with port 8443 detected — did you mean clickhouses:// (TLS)? " +
|
|
96
|
-
"Port 8443 is the conventional ClickHouse TLS port."
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
} catch {
|
|
100
|
-
// URL parsing failure is handled downstream; skip warning
|
|
101
|
-
}
|
|
102
|
-
return url.replace(/^clickhouse:\/\//, "http://");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
92
|
/**
|
|
106
93
|
* Detect database type from a connection string or ATLAS_DATASOURCE_URL.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* DuckDB URLs start with `duckdb://`.
|
|
112
|
-
* Salesforce URLs start with `salesforce://`.
|
|
113
|
-
* Throws if the URL does not match a supported database type.
|
|
94
|
+
*
|
|
95
|
+
* Core types: `postgresql://` or `postgres://` → "postgres", `mysql://` or
|
|
96
|
+
* `mysql2://` → "mysql". Unknown URL schemes throw with a migration hint
|
|
97
|
+
* suggesting the appropriate datasource plugin.
|
|
114
98
|
*/
|
|
115
99
|
export function detectDBType(url?: string): DBType {
|
|
116
|
-
const connStr = url ??
|
|
100
|
+
const connStr = url ?? resolveDatasourceUrl() ?? "";
|
|
117
101
|
if (!connStr) {
|
|
118
102
|
throw new Error(
|
|
119
|
-
"No database URL provided. Set ATLAS_DATASOURCE_URL to a PostgreSQL (postgresql://...)
|
|
103
|
+
"No database URL provided. Set ATLAS_DATASOURCE_URL to a PostgreSQL (postgresql://...) or MySQL (mysql://...) connection string, or register a datasource plugin for other databases."
|
|
120
104
|
);
|
|
121
105
|
}
|
|
122
106
|
if (connStr.startsWith("postgresql://") || connStr.startsWith("postgres://")) {
|
|
@@ -125,29 +109,22 @@ export function detectDBType(url?: string): DBType {
|
|
|
125
109
|
if (connStr.startsWith("mysql://") || connStr.startsWith("mysql2://")) {
|
|
126
110
|
return "mysql";
|
|
127
111
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (connStr.startsWith("snowflake://")) {
|
|
132
|
-
return "snowflake";
|
|
133
|
-
}
|
|
134
|
-
if (connStr.startsWith("duckdb://")) {
|
|
135
|
-
return "duckdb";
|
|
136
|
-
}
|
|
137
|
-
if (connStr.startsWith("salesforce://")) {
|
|
138
|
-
return "salesforce";
|
|
139
|
-
}
|
|
140
|
-
const scheme = connStr.split("://")[0] || "(empty)";
|
|
112
|
+
const rawScheme = connStr.split("://")[0] || "(empty)";
|
|
113
|
+
// Normalize TLS variants (e.g. clickhouses → clickhouse) for the plugin hint
|
|
114
|
+
const baseScheme = rawScheme.replace(/s$/, "");
|
|
141
115
|
throw new Error(
|
|
142
|
-
`Unsupported database URL scheme "${
|
|
143
|
-
|
|
116
|
+
`Unsupported database URL scheme "${rawScheme}://". ` +
|
|
117
|
+
`This adapter is now a plugin. Install the appropriate datasource plugin ` +
|
|
118
|
+
`(e.g. @useatlas/${baseScheme}) and add it to the plugins array in atlas.config.ts. ` +
|
|
119
|
+
`Ensure the plugin is listed before any datasources that use it. ` +
|
|
120
|
+
`Core adapters support postgresql:// and mysql:// only.`
|
|
144
121
|
);
|
|
145
122
|
}
|
|
146
123
|
|
|
147
124
|
export interface ConnectionConfig {
|
|
148
|
-
/** Database connection string (postgresql
|
|
125
|
+
/** Database connection string (postgresql:// or mysql:// for core; other schemes via plugins). */
|
|
149
126
|
url: string;
|
|
150
|
-
/** PostgreSQL schema name (sets search_path). Ignored for MySQL
|
|
127
|
+
/** PostgreSQL schema name (sets search_path). Ignored for MySQL and plugin-managed connections. */
|
|
151
128
|
schema?: string;
|
|
152
129
|
/** Human-readable description shown in the agent system prompt. */
|
|
153
130
|
description?: string;
|
|
@@ -173,8 +150,18 @@ function createPostgresDB(config: ConnectionConfig): DBConnection {
|
|
|
173
150
|
);
|
|
174
151
|
}
|
|
175
152
|
|
|
153
|
+
// Normalize sslmode: pg v8 treats 'require' as 'verify-full' but warns about it.
|
|
154
|
+
// Explicitly rewrite to 'verify-full' to suppress the deprecation warning.
|
|
155
|
+
let connString = config.url;
|
|
156
|
+
if (connString) {
|
|
157
|
+
connString = connString.replace(
|
|
158
|
+
/([?&])sslmode=require(?=&|$)/,
|
|
159
|
+
"$1sslmode=verify-full",
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
176
163
|
const pool = new Pool({
|
|
177
|
-
connectionString:
|
|
164
|
+
connectionString: connString,
|
|
178
165
|
max: config.maxConnections ?? 10,
|
|
179
166
|
idleTimeoutMillis: config.idleTimeoutMs ?? 30000,
|
|
180
167
|
});
|
|
@@ -267,207 +254,31 @@ function createMySQLDB(config: ConnectionConfig): DBConnection {
|
|
|
267
254
|
};
|
|
268
255
|
}
|
|
269
256
|
|
|
270
|
-
function createClickHouseDB(config: ConnectionConfig): DBConnection {
|
|
271
|
-
let createClient: (opts: Record<string, unknown>) => unknown;
|
|
272
|
-
try {
|
|
273
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
274
|
-
({ createClient } = require("@clickhouse/client"));
|
|
275
|
-
} catch {
|
|
276
|
-
throw new Error(
|
|
277
|
-
"ClickHouse support requires the @clickhouse/client package. Install it with: bun add @clickhouse/client"
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const httpUrl = rewriteClickHouseUrl(config.url);
|
|
282
|
-
|
|
283
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
284
|
-
const client = (createClient as any)({ url: httpUrl });
|
|
285
|
-
|
|
286
|
-
return {
|
|
287
|
-
async query(sql: string, timeoutMs = 30000) {
|
|
288
|
-
let result;
|
|
289
|
-
try {
|
|
290
|
-
result = await client.query({
|
|
291
|
-
query: sql,
|
|
292
|
-
format: "JSON",
|
|
293
|
-
clickhouse_settings: {
|
|
294
|
-
max_execution_time: Math.ceil(timeoutMs / 1000),
|
|
295
|
-
readonly: 1,
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
} catch (err) {
|
|
299
|
-
throw new Error(`ClickHouse query failed: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
300
|
-
}
|
|
301
|
-
const json = await result.json();
|
|
302
|
-
if (!json.meta || !Array.isArray(json.meta)) {
|
|
303
|
-
throw new Error(
|
|
304
|
-
"ClickHouse query returned an unexpected response: missing or invalid 'meta' field. " +
|
|
305
|
-
"Ensure the query uses JSON format and returns a valid result set."
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
const columns = (json.meta as { name: string }[]).map((m: { name: string }) => m.name);
|
|
309
|
-
return { columns, rows: json.data as Record<string, unknown>[] };
|
|
310
|
-
},
|
|
311
|
-
async close() {
|
|
312
|
-
try {
|
|
313
|
-
await client.close();
|
|
314
|
-
} catch (err) {
|
|
315
|
-
log.warn(
|
|
316
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
317
|
-
"Failed to close ClickHouse client"
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Parse a Snowflake connection URL into SDK ConnectionOptions.
|
|
326
|
-
* Format: snowflake://user:pass@account/database/schema?warehouse=WH&role=ROLE
|
|
327
|
-
*
|
|
328
|
-
* - `account` can be a plain account identifier (e.g. `xy12345`) or a
|
|
329
|
-
* fully-qualified account locator (e.g. `xy12345.us-east-1`).
|
|
330
|
-
* - `/database` and `/database/schema` path segments are optional.
|
|
331
|
-
* - Query parameters: `warehouse`, `role` (case-insensitive).
|
|
332
|
-
*/
|
|
333
|
-
export function parseSnowflakeURL(url: string): {
|
|
334
|
-
account: string;
|
|
335
|
-
username: string;
|
|
336
|
-
password: string;
|
|
337
|
-
database?: string;
|
|
338
|
-
schema?: string;
|
|
339
|
-
warehouse?: string;
|
|
340
|
-
role?: string;
|
|
341
|
-
} {
|
|
342
|
-
const parsed = new URL(url);
|
|
343
|
-
if (parsed.protocol !== "snowflake:") {
|
|
344
|
-
throw new Error(`Invalid Snowflake URL: expected snowflake:// scheme, got "${parsed.protocol}"`);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const account = parsed.hostname;
|
|
348
|
-
if (!account) {
|
|
349
|
-
throw new Error("Invalid Snowflake URL: missing account identifier in hostname.");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const username = decodeURIComponent(parsed.username);
|
|
353
|
-
const password = decodeURIComponent(parsed.password);
|
|
354
|
-
if (!username) {
|
|
355
|
-
throw new Error("Invalid Snowflake URL: missing username.");
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Path segments: /database or /database/schema
|
|
359
|
-
const pathSegments = parsed.pathname.split("/").filter(Boolean);
|
|
360
|
-
const database = pathSegments[0] || undefined;
|
|
361
|
-
const schema = pathSegments[1] || undefined;
|
|
362
|
-
|
|
363
|
-
const warehouse = parsed.searchParams.get("warehouse") ?? undefined;
|
|
364
|
-
const role = parsed.searchParams.get("role") ?? undefined;
|
|
365
|
-
|
|
366
|
-
return { account, username, password, database, schema, warehouse, role };
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function createSnowflakeDB(config: ConnectionConfig): DBConnection {
|
|
370
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
371
|
-
const snowflake = require("snowflake-sdk") as typeof import("snowflake-sdk");
|
|
372
|
-
|
|
373
|
-
// Suppress noisy SDK logging
|
|
374
|
-
snowflake.configure({ logLevel: "ERROR" });
|
|
375
|
-
|
|
376
|
-
const opts = parseSnowflakeURL(config.url);
|
|
377
|
-
|
|
378
|
-
const pool = snowflake.createPool(
|
|
379
|
-
{
|
|
380
|
-
account: opts.account,
|
|
381
|
-
username: opts.username,
|
|
382
|
-
password: opts.password,
|
|
383
|
-
database: opts.database,
|
|
384
|
-
schema: opts.schema,
|
|
385
|
-
warehouse: opts.warehouse,
|
|
386
|
-
role: opts.role,
|
|
387
|
-
application: "Atlas",
|
|
388
|
-
},
|
|
389
|
-
{ max: config.maxConnections ?? 10, min: 0 },
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
log.warn(
|
|
393
|
-
"Snowflake has no session-level read-only mode — Atlas enforces SELECT-only " +
|
|
394
|
-
"via SQL validation (regex + AST). For defense-in-depth, configure the " +
|
|
395
|
-
"Snowflake connection with a role granted SELECT privileges only " +
|
|
396
|
-
"(e.g. GRANT SELECT ON ALL TABLES IN SCHEMA <schema> TO ROLE atlas_readonly).",
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
async query(sql: string, timeoutMs = 30000) {
|
|
401
|
-
const timeoutSec = Math.max(1, Math.floor(timeoutMs / 1000));
|
|
402
|
-
return pool.use(async (conn) => {
|
|
403
|
-
// Set session-level statement timeout (seconds)
|
|
404
|
-
await new Promise<void>((resolve, reject) => {
|
|
405
|
-
conn.execute({
|
|
406
|
-
sqlText: `ALTER SESSION SET STATEMENT_TIMEOUT_IN_SECONDS = ${timeoutSec}`,
|
|
407
|
-
complete: (err) => (err ? reject(err) : resolve()),
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
// Tag all Atlas queries for audit trail in QUERY_HISTORY (best-effort —
|
|
412
|
-
// insufficient privileges or transient errors should not block the query)
|
|
413
|
-
try {
|
|
414
|
-
await new Promise<void>((resolve, reject) => {
|
|
415
|
-
conn.execute({
|
|
416
|
-
sqlText: `ALTER SESSION SET QUERY_TAG = 'atlas:readonly'`,
|
|
417
|
-
complete: (err) => (err ? reject(err) : resolve()),
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
} catch (tagErr) {
|
|
421
|
-
log.warn(`Failed to set QUERY_TAG on Snowflake session — query will proceed without audit tag: ${tagErr instanceof Error ? tagErr.message : String(tagErr)}`);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Execute the actual query
|
|
425
|
-
return new Promise<QueryResult>((resolve, reject) => {
|
|
426
|
-
conn.execute({
|
|
427
|
-
sqlText: sql,
|
|
428
|
-
complete: (err, stmt, rows) => {
|
|
429
|
-
if (err) return reject(err);
|
|
430
|
-
const columns = (stmt?.getColumns() ?? []).map((c) => c.getName());
|
|
431
|
-
const resultRows = (rows ?? []) as Record<string, unknown>[];
|
|
432
|
-
resolve({ columns, rows: resultRows });
|
|
433
|
-
},
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
},
|
|
438
|
-
async close() {
|
|
439
|
-
await pool.drain();
|
|
440
|
-
await pool.clear();
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
257
|
function createConnection(dbType: DBType, config: ConnectionConfig): DBConnection {
|
|
446
258
|
switch (dbType) {
|
|
447
259
|
case "postgres":
|
|
448
260
|
return createPostgresDB(config);
|
|
449
261
|
case "mysql":
|
|
450
262
|
return createMySQLDB(config);
|
|
451
|
-
|
|
452
|
-
return createClickHouseDB(config);
|
|
453
|
-
case "snowflake":
|
|
454
|
-
return createSnowflakeDB(config);
|
|
455
|
-
case "duckdb":
|
|
456
|
-
return createDuckDBConnection(parseDuckDBUrl(config.url));
|
|
457
|
-
case "salesforce":
|
|
263
|
+
default:
|
|
458
264
|
throw new Error(
|
|
459
|
-
|
|
460
|
-
|
|
265
|
+
`Unsupported database type "${dbType}". ` +
|
|
266
|
+
`This adapter is now a plugin. Install the appropriate datasource plugin ` +
|
|
267
|
+
`and add it to the plugins array in atlas.config.ts.`
|
|
461
268
|
);
|
|
462
|
-
default: {
|
|
463
|
-
const _exhaustive: never = dbType;
|
|
464
|
-
throw new Error(`Unknown database type: ${_exhaustive}`);
|
|
465
|
-
}
|
|
466
269
|
}
|
|
467
270
|
}
|
|
468
271
|
|
|
469
272
|
// --- Connection Registry ---
|
|
470
273
|
|
|
274
|
+
/** Optional plugin metadata for parser dialect and forbidden patterns. */
|
|
275
|
+
export interface ConnectionPluginMeta {
|
|
276
|
+
/** node-sql-parser dialect string (e.g. "PostgresQL", "BigQuery"). */
|
|
277
|
+
parserDialect?: string;
|
|
278
|
+
/** Additional regex patterns to block beyond the base DML/DDL guard. */
|
|
279
|
+
forbiddenPatterns?: RegExp[];
|
|
280
|
+
}
|
|
281
|
+
|
|
471
282
|
interface RegistryEntry {
|
|
472
283
|
conn: DBConnection;
|
|
473
284
|
dbType: DBType;
|
|
@@ -480,6 +291,8 @@ interface RegistryEntry {
|
|
|
480
291
|
firstFailureAt: number | null;
|
|
481
292
|
/** Custom query validator (mirrors QueryValidationResult from plugin-sdk). */
|
|
482
293
|
validate?: (query: string) => { valid: boolean; reason?: string };
|
|
294
|
+
/** Plugin-provided metadata for SQL validation. */
|
|
295
|
+
pluginMeta?: ConnectionPluginMeta;
|
|
483
296
|
}
|
|
484
297
|
|
|
485
298
|
/**
|
|
@@ -500,13 +313,9 @@ export class ConnectionRegistry {
|
|
|
500
313
|
private _totalPoolSlots(): number {
|
|
501
314
|
let total = 0;
|
|
502
315
|
for (const entry of this.entries.values()) {
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
|
|
506
|
-
total += 1;
|
|
507
|
-
} else {
|
|
508
|
-
total += entry.config?.maxConnections ?? 10;
|
|
509
|
-
}
|
|
316
|
+
// Direct-registered connections (plugins) don't have config and manage
|
|
317
|
+
// their own pooling — count as 1 slot instead of the default 10.
|
|
318
|
+
total += entry.config?.maxConnections ?? (entry.targetHost === "(direct)" ? 1 : 10);
|
|
510
319
|
}
|
|
511
320
|
return total;
|
|
512
321
|
}
|
|
@@ -568,6 +377,7 @@ export class ConnectionRegistry {
|
|
|
568
377
|
dbType: DBType,
|
|
569
378
|
description?: string,
|
|
570
379
|
validate?: (query: string) => { valid: boolean; reason?: string },
|
|
380
|
+
meta?: ConnectionPluginMeta,
|
|
571
381
|
): void {
|
|
572
382
|
const existing = this.entries.get(id);
|
|
573
383
|
this.entries.set(id, {
|
|
@@ -580,6 +390,7 @@ export class ConnectionRegistry {
|
|
|
580
390
|
lastHealth: null,
|
|
581
391
|
firstFailureAt: null,
|
|
582
392
|
validate,
|
|
393
|
+
pluginMeta: meta,
|
|
583
394
|
});
|
|
584
395
|
if (existing) {
|
|
585
396
|
existing.conn.close().catch((err) => {
|
|
@@ -615,15 +426,26 @@ export class ConnectionRegistry {
|
|
|
615
426
|
return this.entries.get(id)?.validate;
|
|
616
427
|
}
|
|
617
428
|
|
|
429
|
+
/** Return the plugin-provided parser dialect for a connection, if any. */
|
|
430
|
+
getParserDialect(id: string): string | undefined {
|
|
431
|
+
return this.entries.get(id)?.pluginMeta?.parserDialect;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Return plugin-provided forbidden patterns for a connection. Empty array if none. */
|
|
435
|
+
getForbiddenPatterns(id: string): RegExp[] {
|
|
436
|
+
return this.entries.get(id)?.pluginMeta?.forbiddenPatterns ?? [];
|
|
437
|
+
}
|
|
438
|
+
|
|
618
439
|
getDefault(): DBConnection {
|
|
619
440
|
if (!this.entries.has("default")) {
|
|
620
|
-
|
|
441
|
+
const url = resolveDatasourceUrl();
|
|
442
|
+
if (!url) {
|
|
621
443
|
throw new Error(
|
|
622
|
-
"No analytics datasource configured. Set ATLAS_DATASOURCE_URL to a PostgreSQL
|
|
444
|
+
"No analytics datasource configured. Set ATLAS_DATASOURCE_URL to a PostgreSQL or MySQL connection string, or register a datasource plugin."
|
|
623
445
|
);
|
|
624
446
|
}
|
|
625
447
|
this.register("default", {
|
|
626
|
-
url
|
|
448
|
+
url,
|
|
627
449
|
schema: process.env.ATLAS_SCHEMA,
|
|
628
450
|
});
|
|
629
451
|
}
|
|
@@ -34,8 +34,13 @@ export function getInternalDB(): InternalPool {
|
|
|
34
34
|
}
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
36
|
const { Pool } = require("pg");
|
|
37
|
+
// Normalize sslmode: pg v8 treats 'require' as 'verify-full' but warns.
|
|
38
|
+
const connString = process.env.DATABASE_URL!.replace(
|
|
39
|
+
/([?&])sslmode=require(?=&|$)/,
|
|
40
|
+
"$1sslmode=verify-full",
|
|
41
|
+
);
|
|
37
42
|
_pool = new Pool({
|
|
38
|
-
connectionString:
|
|
43
|
+
connectionString: connString,
|
|
39
44
|
max: 5,
|
|
40
45
|
idleTimeoutMillis: 30000,
|
|
41
46
|
}) as InternalPool;
|
|
@@ -35,6 +35,8 @@ mock.module("@atlas/api/lib/db/connection", () => ({
|
|
|
35
35
|
getDBType: () => "postgres",
|
|
36
36
|
getTargetHost: () => "localhost",
|
|
37
37
|
getValidator: () => undefined,
|
|
38
|
+
getParserDialect: () => undefined,
|
|
39
|
+
getForbiddenPatterns: () => [],
|
|
38
40
|
list: () => ["default"],
|
|
39
41
|
},
|
|
40
42
|
detectDBType: () => "postgres",
|
|
@@ -94,7 +96,7 @@ function makeHookPlugin(
|
|
|
94
96
|
): PluginLike {
|
|
95
97
|
return {
|
|
96
98
|
id,
|
|
97
|
-
|
|
99
|
+
types: ["context"] as PluginLike["types"],
|
|
98
100
|
version: "1.0.0",
|
|
99
101
|
hooks,
|
|
100
102
|
};
|
|
@@ -18,7 +18,7 @@ function makeHookPlugin(
|
|
|
18
18
|
): PluginLike {
|
|
19
19
|
return {
|
|
20
20
|
id,
|
|
21
|
-
|
|
21
|
+
types: [opts?.type ?? "context"] as PluginLike["types"],
|
|
22
22
|
version: "1.0.0",
|
|
23
23
|
hooks,
|
|
24
24
|
...(opts?.unhealthy
|
|
@@ -140,7 +140,7 @@ describe("dispatchHook", () => {
|
|
|
140
140
|
test("plugins without hooks object are silently skipped", async () => {
|
|
141
141
|
registry.register({
|
|
142
142
|
id: "no-hooks",
|
|
143
|
-
|
|
143
|
+
types: ["context"],
|
|
144
144
|
version: "1.0.0",
|
|
145
145
|
});
|
|
146
146
|
await registry.initializeAll(minimalCtx);
|