@useatlas/create 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/index.ts +253 -36
- package/package.json +4 -4
- package/templates/docker/Dockerfile +1 -1
- package/templates/docker/Dockerfile.sidecar +1 -1
- package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +17 -14
- package/templates/docker/bin/__tests__/failure-threshold.test.ts +148 -0
- package/templates/docker/bin/__tests__/fatal-error-propagation.test.ts +267 -0
- package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +5 -5
- package/templates/docker/bin/__tests__/schema-drift.test.ts +39 -0
- package/templates/docker/bin/atlas.ts +981 -1819
- package/templates/docker/bin/benchmark.ts +14 -16
- package/templates/docker/bin/enrich.ts +7 -2
- package/templates/docker/brand.css +13 -0
- package/templates/docker/data/cybersec-semantic/catalog.yml +222 -0
- package/templates/docker/data/cybersec-semantic/entities/alerts.yml +195 -0
- package/templates/docker/data/cybersec-semantic/entities/assets.yml +191 -0
- package/templates/docker/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
- package/templates/docker/data/cybersec-semantic/entities/incidents.yml +219 -0
- package/templates/docker/data/cybersec-semantic/entities/organizations.yml +136 -0
- package/templates/docker/data/cybersec-semantic/entities/plans.yml +114 -0
- package/templates/docker/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
- package/templates/docker/data/cybersec-semantic/entities/scan_results.yml +215 -0
- package/templates/docker/data/cybersec-semantic/entities/scans.yml +180 -0
- package/templates/docker/data/cybersec-semantic/entities/subscriptions.yml +184 -0
- package/templates/docker/data/cybersec-semantic/entities/users.yml +140 -0
- package/templates/docker/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
- package/templates/docker/data/cybersec-semantic/glossary.yml +207 -0
- package/templates/docker/data/cybersec-semantic/metrics/business.yml +148 -0
- package/templates/docker/data/cybersec-semantic/metrics/compliance.yml +138 -0
- package/templates/docker/data/cybersec-semantic/metrics/security.yml +181 -0
- package/templates/docker/data/cybersec.sql +8 -8
- package/templates/docker/data/demo.sql +3 -0
- package/templates/docker/data/ecommerce-semantic/catalog.yml +221 -0
- package/templates/docker/data/ecommerce-semantic/entities/categories.yml +91 -0
- package/templates/docker/data/ecommerce-semantic/entities/customers.yml +133 -0
- package/templates/docker/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
- package/templates/docker/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
- package/templates/docker/data/ecommerce-semantic/entities/order_items.yml +159 -0
- package/templates/docker/data/ecommerce-semantic/entities/orders.yml +199 -0
- package/templates/docker/data/ecommerce-semantic/entities/payments.yml +140 -0
- package/templates/docker/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
- package/templates/docker/data/ecommerce-semantic/entities/products.yml +178 -0
- package/templates/docker/data/ecommerce-semantic/entities/promotions.yml +171 -0
- package/templates/docker/data/ecommerce-semantic/entities/returns.yml +144 -0
- package/templates/docker/data/ecommerce-semantic/entities/sellers.yml +124 -0
- package/templates/docker/data/ecommerce-semantic/entities/shipments.yml +159 -0
- package/templates/docker/data/ecommerce-semantic/glossary.yml +193 -0
- package/templates/docker/data/ecommerce-semantic/metrics/customers.yml +116 -0
- package/templates/docker/data/ecommerce-semantic/metrics/operations.yml +131 -0
- package/templates/docker/data/ecommerce-semantic/metrics/revenue.yml +120 -0
- package/templates/docker/docs/deploy.md +2 -1
- package/templates/docker/ee/src/__mocks__/internal.ts +170 -0
- package/templates/docker/ee/src/audit/purge-scheduler.ts +113 -0
- package/templates/docker/ee/src/audit/retention.ts +467 -0
- package/templates/docker/ee/src/auth/ip-allowlist.ts +367 -0
- package/templates/docker/ee/src/auth/roles.ts +562 -0
- package/templates/docker/ee/src/auth/scim.ts +343 -0
- package/templates/docker/ee/src/auth/sso.ts +538 -0
- package/templates/docker/ee/src/backups/engine.ts +355 -0
- package/templates/docker/ee/src/backups/index.ts +26 -0
- package/templates/docker/ee/src/backups/restore.ts +169 -0
- package/templates/docker/ee/src/backups/scheduler.ts +153 -0
- package/templates/docker/ee/src/backups/verify.ts +124 -0
- package/templates/docker/ee/src/branding/white-label.ts +228 -0
- package/templates/docker/ee/src/compliance/masking.ts +477 -0
- package/templates/docker/ee/src/compliance/patterns.ts +16 -0
- package/templates/docker/ee/src/compliance/pii-detection.ts +217 -0
- package/templates/docker/ee/src/compliance/reports.ts +402 -0
- package/templates/docker/ee/src/deploy-mode.ts +37 -0
- package/templates/docker/ee/src/governance/approval.ts +699 -0
- package/templates/docker/ee/src/index.ts +74 -0
- package/templates/docker/ee/src/platform/domains.ts +562 -0
- package/templates/docker/ee/src/platform/model-routing.ts +382 -0
- package/templates/docker/ee/src/platform/residency.ts +265 -0
- package/templates/docker/ee/src/sla/alerting.ts +382 -0
- package/templates/docker/ee/src/sla/index.ts +12 -0
- package/templates/docker/ee/src/sla/metrics.ts +275 -0
- package/templates/docker/ee/src/test-setup.ts +1 -0
- package/templates/docker/next.config.ts +4 -1
- package/templates/docker/package.json +49 -29
- package/templates/docker/sidecar/Dockerfile +1 -1
- package/templates/docker/src/api/index.ts +336 -24
- package/templates/docker/src/api/routes/actions.ts +443 -176
- package/templates/docker/src/api/routes/admin-abuse.ts +219 -0
- package/templates/docker/src/api/routes/admin-approval.ts +418 -0
- package/templates/docker/src/api/routes/admin-audit-retention.ts +405 -0
- package/templates/docker/src/api/routes/admin-auth.ts +122 -0
- package/templates/docker/src/api/routes/admin-branding.ts +252 -0
- package/templates/docker/src/api/routes/admin-compliance.ts +352 -0
- package/templates/docker/src/api/routes/admin-domains.ts +334 -0
- package/templates/docker/src/api/routes/admin-integrations.ts +2667 -0
- package/templates/docker/src/api/routes/admin-ip-allowlist.ts +261 -0
- package/templates/docker/src/api/routes/admin-learned-patterns.ts +525 -0
- package/templates/docker/src/api/routes/admin-model-config.ts +252 -0
- package/templates/docker/src/api/routes/admin-onboarding-emails.ts +145 -0
- package/templates/docker/src/api/routes/admin-orgs.ts +710 -0
- package/templates/docker/src/api/routes/admin-prompts.ts +694 -0
- package/templates/docker/src/api/routes/admin-residency.ts +570 -0
- package/templates/docker/src/api/routes/admin-roles.ts +296 -0
- package/templates/docker/src/api/routes/admin-router.ts +120 -0
- package/templates/docker/src/api/routes/admin-sandbox.ts +417 -0
- package/templates/docker/src/api/routes/admin-scim.ts +262 -0
- package/templates/docker/src/api/routes/admin-sso.ts +545 -0
- package/templates/docker/src/api/routes/admin-suggestions.ts +176 -0
- package/templates/docker/src/api/routes/admin-usage.ts +310 -0
- package/templates/docker/src/api/routes/admin.ts +4156 -898
- package/templates/docker/src/api/routes/auth-preamble.ts +105 -0
- package/templates/docker/src/api/routes/billing.ts +397 -0
- package/templates/docker/src/api/routes/chat.ts +597 -334
- package/templates/docker/src/api/routes/conversations.ts +987 -132
- package/templates/docker/src/api/routes/demo.ts +673 -0
- package/templates/docker/src/api/routes/discord.ts +274 -0
- package/templates/docker/src/api/routes/ee-error-handler.ts +32 -0
- package/templates/docker/src/api/routes/health.ts +129 -14
- package/templates/docker/src/api/routes/middleware.ts +244 -0
- package/templates/docker/src/api/routes/onboarding-emails.ts +134 -0
- package/templates/docker/src/api/routes/onboarding.ts +1109 -0
- package/templates/docker/src/api/routes/openapi.ts +184 -1597
- package/templates/docker/src/api/routes/platform-admin.ts +760 -0
- package/templates/docker/src/api/routes/platform-backups.ts +436 -0
- package/templates/docker/src/api/routes/platform-domains.ts +235 -0
- package/templates/docker/src/api/routes/platform-residency.ts +257 -0
- package/templates/docker/src/api/routes/platform-sla.ts +379 -0
- package/templates/docker/src/api/routes/prompts.ts +221 -0
- package/templates/docker/src/api/routes/public-branding.ts +106 -0
- package/templates/docker/src/api/routes/query.ts +330 -219
- package/templates/docker/src/api/routes/scheduled-tasks.ts +393 -297
- package/templates/docker/src/api/routes/semantic.ts +179 -0
- package/templates/docker/src/api/routes/sessions.ts +210 -0
- package/templates/docker/src/api/routes/shared-domains.ts +98 -0
- package/templates/docker/src/api/routes/shared-schemas.ts +139 -0
- package/templates/docker/src/api/routes/slack.ts +209 -52
- package/templates/docker/src/api/routes/suggestions.ts +233 -0
- package/templates/docker/src/api/routes/tables.ts +67 -0
- package/templates/docker/src/api/routes/teams.ts +222 -0
- package/templates/docker/src/api/routes/validate-sql.ts +188 -0
- package/templates/docker/src/api/routes/validation-hook.ts +62 -0
- package/templates/docker/src/api/routes/widget-loader.ts +356 -0
- package/templates/docker/src/api/routes/widget.ts +428 -0
- package/templates/docker/src/api/routes/wizard.ts +852 -0
- package/templates/docker/src/api/server.ts +187 -69
- package/templates/docker/src/app/error.tsx +5 -2
- package/templates/docker/src/app/globals.css +1 -1
- package/templates/docker/src/app/layout.tsx +7 -2
- package/templates/docker/src/app/page.tsx +39 -5
- package/templates/docker/src/components/data-table/data-table-column-header.tsx +99 -0
- package/templates/docker/src/components/data-table/data-table-date-filter.tsx +225 -0
- package/templates/docker/src/components/data-table/data-table-expandable.tsx +125 -0
- package/templates/docker/src/components/data-table/data-table-faceted-filter.tsx +189 -0
- package/templates/docker/src/components/data-table/data-table-pagination.tsx +112 -0
- package/templates/docker/src/components/data-table/data-table-range-filter.tsx +122 -0
- package/templates/docker/src/components/data-table/data-table-slider-filter.tsx +256 -0
- package/templates/docker/src/components/data-table/data-table-sort-list.tsx +407 -0
- package/templates/docker/src/components/data-table/data-table-toolbar.tsx +149 -0
- package/templates/docker/src/components/data-table/data-table-view-options.tsx +89 -0
- package/templates/docker/src/components/data-table/data-table.tsx +105 -0
- package/templates/docker/src/components/form-dialog.tsx +135 -0
- package/templates/docker/src/components/ui/accordion.tsx +66 -0
- package/templates/docker/src/components/ui/calendar.tsx +220 -0
- package/templates/docker/src/components/ui/checkbox.tsx +32 -0
- package/templates/docker/src/components/ui/faceted.tsx +283 -0
- package/templates/docker/src/components/ui/form.tsx +167 -0
- package/templates/docker/src/components/ui/label.tsx +24 -0
- package/templates/docker/src/components/ui/popover.tsx +89 -0
- package/templates/docker/src/components/ui/progress.tsx +31 -0
- package/templates/docker/src/components/ui/scroll-area.tsx +6 -2
- package/templates/docker/src/components/ui/slider.tsx +63 -0
- package/templates/docker/src/components/ui/sortable.tsx +581 -0
- package/templates/docker/src/components/ui/switch.tsx +35 -0
- package/templates/docker/src/components/ui/textarea.tsx +18 -0
- package/templates/docker/src/config/data-table.ts +82 -0
- package/templates/docker/src/env-check.ts +74 -0
- package/templates/docker/src/hooks/use-callback-ref.ts +27 -0
- package/templates/docker/src/hooks/use-data-table.ts +316 -0
- package/templates/docker/src/hooks/use-debounced-callback.ts +28 -0
- package/templates/docker/src/lib/action-types.ts +7 -41
- package/templates/docker/src/lib/agent-query.ts +4 -2
- package/templates/docker/src/lib/agent.ts +363 -31
- package/templates/docker/src/lib/auth/admin-permissions.ts +38 -0
- package/templates/docker/src/lib/auth/audit.ts +19 -4
- package/templates/docker/src/lib/auth/byot.ts +3 -3
- package/templates/docker/src/lib/auth/client.ts +33 -3
- package/templates/docker/src/lib/auth/detect.ts +29 -8
- package/templates/docker/src/lib/auth/managed.ts +104 -14
- package/templates/docker/src/lib/auth/middleware.ts +53 -6
- package/templates/docker/src/lib/auth/migrate.ts +140 -15
- package/templates/docker/src/lib/auth/oauth-state.ts +123 -0
- package/templates/docker/src/lib/auth/org-permissions.ts +55 -0
- package/templates/docker/src/lib/auth/permissions.ts +26 -19
- package/templates/docker/src/lib/auth/server.ts +355 -9
- package/templates/docker/src/lib/auth/simple-key.ts +3 -3
- package/templates/docker/src/lib/auth/types.ts +15 -21
- package/templates/docker/src/lib/billing/enforcement.ts +368 -0
- package/templates/docker/src/lib/billing/plans.ts +155 -0
- package/templates/docker/src/lib/cache/index.ts +92 -0
- package/templates/docker/src/lib/cache/keys.ts +30 -0
- package/templates/docker/src/lib/cache/lru.ts +79 -0
- package/templates/docker/src/lib/cache/types.ts +31 -0
- package/templates/docker/src/lib/compose-refs.ts +62 -0
- package/templates/docker/src/lib/config.ts +563 -11
- package/templates/docker/src/lib/connection-types.ts +9 -0
- package/templates/docker/src/lib/conversation-types.ts +1 -25
- package/templates/docker/src/lib/conversations.ts +345 -14
- package/templates/docker/src/lib/data-table.ts +61 -0
- package/templates/docker/src/lib/db/connection.ts +793 -39
- package/templates/docker/src/lib/db/internal.ts +985 -139
- package/templates/docker/src/lib/db/migrate.ts +295 -0
- package/templates/docker/src/lib/db/migrations/0000_baseline.sql +703 -0
- package/templates/docker/src/lib/db/migrations/0001_teams_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0002_discord_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
- package/templates/docker/src/lib/db/migrations/0005_oauth_state.sql +16 -0
- package/templates/docker/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0008_github_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0009_linear_installations.sql +15 -0
- package/templates/docker/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
- package/templates/docker/src/lib/db/migrations/0011_email_installations.sql +16 -0
- package/templates/docker/src/lib/db/migrations/0012_region_migrations.sql +25 -0
- package/templates/docker/src/lib/db/schema.ts +1120 -0
- package/templates/docker/src/lib/db/source-rate-limit.ts +89 -139
- package/templates/docker/src/lib/demo.ts +308 -0
- package/templates/docker/src/lib/discord/store.ts +225 -0
- package/templates/docker/src/lib/effect/ai.ts +243 -0
- package/templates/docker/src/lib/effect/errors.ts +234 -0
- package/templates/docker/src/lib/effect/hono.ts +454 -0
- package/templates/docker/src/lib/effect/index.ts +137 -0
- package/templates/docker/src/lib/effect/layers.ts +496 -0
- package/templates/docker/src/lib/effect/services.ts +776 -0
- package/templates/docker/src/lib/effect/sql.ts +178 -0
- package/templates/docker/src/lib/effect/toolkit.ts +123 -0
- package/templates/docker/src/lib/email/delivery.ts +232 -0
- package/templates/docker/src/lib/email/engine.ts +349 -0
- package/templates/docker/src/lib/email/hooks.ts +107 -0
- package/templates/docker/src/lib/email/index.ts +16 -0
- package/templates/docker/src/lib/email/scheduler.ts +72 -0
- package/templates/docker/src/lib/email/sequence.ts +73 -0
- package/templates/docker/src/lib/email/store.ts +163 -0
- package/templates/docker/src/lib/email/templates.ts +215 -0
- package/templates/docker/src/lib/format.ts +67 -0
- package/templates/docker/src/lib/gchat/store.ts +202 -0
- package/templates/docker/src/lib/github/store.ts +197 -0
- package/templates/docker/src/lib/id.ts +29 -0
- package/templates/docker/src/lib/integrations/types.ts +166 -0
- package/templates/docker/src/lib/learn/pattern-analyzer.ts +224 -0
- package/templates/docker/src/lib/learn/pattern-cache.ts +229 -0
- package/templates/docker/src/lib/learn/pattern-proposer.ts +87 -0
- package/templates/docker/src/lib/learn/suggestion-helpers.ts +34 -0
- package/templates/docker/src/lib/learn/suggestions.ts +139 -0
- package/templates/docker/src/lib/linear/store.ts +200 -0
- package/templates/docker/src/lib/logger.ts +35 -3
- package/templates/docker/src/lib/metering.ts +272 -0
- package/templates/docker/src/lib/parsers.ts +99 -0
- package/templates/docker/src/lib/plugins/hooks.ts +13 -11
- package/templates/docker/src/lib/plugins/index.ts +3 -1
- package/templates/docker/src/lib/plugins/registry.ts +58 -6
- package/templates/docker/src/lib/plugins/settings.ts +147 -0
- package/templates/docker/src/lib/plugins/wiring.ts +6 -9
- package/templates/docker/src/lib/profiler.ts +1665 -0
- package/templates/docker/src/lib/providers.ts +188 -13
- package/templates/docker/src/lib/rls.ts +172 -60
- package/templates/docker/src/lib/sandbox/credentials.ts +206 -0
- package/templates/docker/src/lib/sandbox/validate.ts +179 -0
- package/templates/docker/src/lib/scheduled-task-types.ts +26 -94
- package/templates/docker/src/lib/scheduled-tasks.ts +174 -34
- package/templates/docker/src/lib/scheduler/delivery.ts +248 -150
- package/templates/docker/src/lib/scheduler/engine.ts +190 -154
- package/templates/docker/src/lib/scheduler/executor.ts +74 -23
- package/templates/docker/src/lib/scheduler/preview.ts +72 -0
- package/templates/docker/src/lib/security/abuse.ts +463 -0
- package/templates/docker/src/lib/semantic/diff.ts +267 -0
- package/templates/docker/src/lib/semantic/entities.ts +167 -0
- package/templates/docker/src/lib/semantic/files.ts +283 -0
- package/templates/docker/src/lib/semantic/index.ts +27 -0
- package/templates/docker/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
- package/templates/docker/src/lib/semantic/sync.ts +581 -0
- package/templates/docker/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
- package/templates/docker/src/lib/settings.ts +817 -0
- package/templates/docker/src/lib/sidecar-types.ts +13 -0
- package/templates/docker/src/lib/slack/store.ts +134 -25
- package/templates/docker/src/lib/startup.ts +528 -362
- package/templates/docker/src/lib/teams/store.ts +216 -0
- package/templates/docker/src/lib/telegram/store.ts +202 -0
- package/templates/docker/src/lib/telemetry.ts +40 -0
- package/templates/docker/src/lib/tools/actions/audit.ts +8 -5
- package/templates/docker/src/lib/tools/actions/email.ts +3 -1
- package/templates/docker/src/lib/tools/actions/handler.ts +276 -93
- package/templates/docker/src/lib/tools/actions/jira.ts +2 -2
- package/templates/docker/src/lib/tools/backends/detect.ts +16 -0
- package/templates/docker/src/lib/tools/backends/index.ts +11 -0
- package/templates/docker/src/lib/tools/backends/nsjail.ts +213 -0
- package/templates/docker/src/lib/tools/backends/shared.ts +103 -0
- package/templates/docker/src/lib/tools/backends/types.ts +26 -0
- package/templates/docker/src/lib/tools/explore-nsjail.ts +7 -228
- package/templates/docker/src/lib/tools/explore-sandbox.ts +4 -29
- package/templates/docker/src/lib/tools/explore-sidecar.ts +18 -2
- package/templates/docker/src/lib/tools/explore.ts +246 -54
- package/templates/docker/src/lib/tools/index.ts +17 -0
- package/templates/docker/src/lib/tools/python-nsjail.ts +11 -139
- package/templates/docker/src/lib/tools/python-sandbox.ts +9 -132
- package/templates/docker/src/lib/tools/python-sidecar.ts +184 -3
- package/templates/docker/src/lib/tools/python-stream.ts +33 -0
- package/templates/docker/src/lib/tools/python-wrapper.ts +129 -0
- package/templates/docker/src/lib/tools/python.ts +115 -15
- package/templates/docker/src/lib/tools/registry.ts +14 -2
- package/templates/docker/src/lib/tools/sql.ts +778 -362
- package/templates/docker/src/lib/tracing.ts +16 -0
- package/templates/docker/src/lib/whatsapp/store.ts +198 -0
- package/templates/docker/src/lib/workspace.ts +89 -0
- package/templates/docker/src/progress.ts +121 -0
- package/templates/docker/src/types/data-table.ts +48 -0
- package/templates/docker/src/ui/atlas-chat-reexport.ts +3 -0
- package/templates/docker/src/ui/components/actions/action-approval-card.tsx +26 -19
- package/templates/docker/src/ui/components/actions/action-status-badge.tsx +3 -3
- package/templates/docker/src/ui/components/admin/admin-layout.tsx +57 -39
- package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +213 -35
- package/templates/docker/src/ui/components/admin/delivery-status-badge.tsx +53 -0
- package/templates/docker/src/ui/components/admin/empty-state.tsx +27 -6
- package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -52
- package/templates/docker/src/ui/components/admin/error-banner.tsx +2 -2
- package/templates/docker/src/ui/components/admin/feature-disabled.tsx +28 -5
- package/templates/docker/src/ui/components/admin-content-wrapper.tsx +87 -0
- package/templates/docker/src/ui/components/atlas-chat.tsx +449 -166
- package/templates/docker/src/ui/components/branding-head.tsx +41 -0
- package/templates/docker/src/ui/components/chart/chart-detection.ts +62 -5
- package/templates/docker/src/ui/components/chart/result-chart.tsx +316 -125
- package/templates/docker/src/ui/components/chat/api-key-bar.tsx +4 -4
- package/templates/docker/src/ui/components/chat/data-table.tsx +45 -4
- package/templates/docker/src/ui/components/chat/error-banner.tsx +86 -5
- package/templates/docker/src/ui/components/chat/follow-up-chips.tsx +29 -0
- package/templates/docker/src/ui/components/chat/markdown.tsx +24 -0
- package/templates/docker/src/ui/components/chat/prompt-library.tsx +206 -0
- package/templates/docker/src/ui/components/chat/python-result-card.tsx +106 -78
- package/templates/docker/src/ui/components/chat/result-card-base.tsx +101 -0
- package/templates/docker/src/ui/components/chat/share-dialog.tsx +377 -0
- package/templates/docker/src/ui/components/chat/sql-result-card.tsx +94 -73
- package/templates/docker/src/ui/components/chat/suggestion-chips.tsx +46 -0
- package/templates/docker/src/ui/components/chat/tool-part.tsx +16 -4
- package/templates/docker/src/ui/components/conversations/conversation-item.tsx +48 -17
- package/templates/docker/src/ui/components/conversations/conversation-list.tsx +38 -24
- package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
- package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +9 -2
- package/templates/docker/src/ui/components/error-boundary.tsx +66 -0
- package/templates/docker/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
- package/templates/docker/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
- package/templates/docker/src/ui/components/notebook/notebook-cell.tsx +119 -0
- package/templates/docker/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
- package/templates/docker/src/ui/components/notebook/notebook-export.ts +287 -0
- package/templates/docker/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
- package/templates/docker/src/ui/components/notebook/notebook-shell.tsx +266 -0
- package/templates/docker/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
- package/templates/docker/src/ui/components/notebook/types.ts +39 -0
- package/templates/docker/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
- package/templates/docker/src/ui/components/notebook/use-notebook.ts +684 -0
- package/templates/docker/src/ui/components/org-switcher.tsx +111 -0
- package/templates/docker/src/ui/components/region-picker.tsx +103 -0
- package/templates/docker/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
- package/templates/docker/src/ui/components/social-icons.tsx +26 -0
- package/templates/docker/src/ui/components/tour/guided-tour.tsx +81 -0
- package/templates/docker/src/ui/components/tour/index.ts +5 -0
- package/templates/docker/src/ui/components/tour/nav-bar.tsx +100 -0
- package/templates/docker/src/ui/components/tour/tour-overlay.tsx +298 -0
- package/templates/docker/src/ui/components/tour/tour-steps.ts +43 -0
- package/templates/docker/src/ui/components/tour/types.ts +21 -0
- package/templates/docker/src/ui/components/tour/use-tour.ts +193 -0
- package/templates/docker/src/ui/context-reexport.ts +3 -0
- package/templates/docker/src/ui/hooks/theme-init-script.ts +17 -0
- package/templates/docker/src/ui/hooks/use-admin-fetch.ts +38 -30
- package/templates/docker/src/ui/hooks/use-admin-mutation.ts +188 -0
- package/templates/docker/src/ui/hooks/use-atlas-transport.ts +225 -0
- package/templates/docker/src/ui/hooks/use-branding.ts +68 -0
- package/templates/docker/src/ui/hooks/use-conversations.ts +106 -83
- package/templates/docker/src/ui/hooks/use-dark-mode.ts +134 -10
- package/templates/docker/src/ui/hooks/use-deploy-mode.ts +36 -0
- package/templates/docker/src/ui/hooks/use-platform-admin-guard.ts +49 -0
- package/templates/docker/src/ui/lib/action-types.ts +11 -63
- package/templates/docker/src/ui/lib/admin-schemas.ts +744 -0
- package/templates/docker/src/ui/lib/fetch-client.ts +84 -0
- package/templates/docker/src/ui/lib/fetch-error.ts +54 -0
- package/templates/docker/src/ui/lib/helpers.ts +94 -1
- package/templates/docker/src/ui/lib/types.ts +149 -140
- package/templates/docker/tsconfig.json +4 -2
- package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +17 -14
- package/templates/nextjs-standalone/bin/__tests__/failure-threshold.test.ts +148 -0
- package/templates/nextjs-standalone/bin/__tests__/fatal-error-propagation.test.ts +267 -0
- package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +5 -5
- package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +39 -0
- package/templates/nextjs-standalone/bin/atlas.ts +981 -1819
- package/templates/nextjs-standalone/bin/benchmark.ts +14 -16
- package/templates/nextjs-standalone/bin/enrich.ts +7 -2
- package/templates/nextjs-standalone/brand.css +13 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/catalog.yml +222 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/alerts.yml +195 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/assets.yml +191 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/incidents.yml +219 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/organizations.yml +136 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/plans.yml +114 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/scan_results.yml +215 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/scans.yml +180 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/subscriptions.yml +184 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/users.yml +140 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/glossary.yml +207 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/business.yml +148 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/compliance.yml +138 -0
- package/templates/nextjs-standalone/data/cybersec-semantic/metrics/security.yml +181 -0
- package/templates/nextjs-standalone/data/cybersec.sql +8 -8
- package/templates/nextjs-standalone/data/demo.sql +3 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/catalog.yml +221 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/categories.yml +91 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/customers.yml +133 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/order_items.yml +159 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/orders.yml +199 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/payments.yml +140 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/products.yml +178 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/promotions.yml +171 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/returns.yml +144 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/sellers.yml +124 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/entities/shipments.yml +159 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/glossary.yml +193 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/customers.yml +116 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/operations.yml +131 -0
- package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/revenue.yml +120 -0
- package/templates/nextjs-standalone/docs/deploy.md +2 -1
- package/templates/nextjs-standalone/ee/src/__mocks__/internal.ts +170 -0
- package/templates/nextjs-standalone/ee/src/audit/purge-scheduler.ts +113 -0
- package/templates/nextjs-standalone/ee/src/audit/retention.ts +467 -0
- package/templates/nextjs-standalone/ee/src/auth/ip-allowlist.ts +367 -0
- package/templates/nextjs-standalone/ee/src/auth/roles.ts +562 -0
- package/templates/nextjs-standalone/ee/src/auth/scim.ts +343 -0
- package/templates/nextjs-standalone/ee/src/auth/sso.ts +538 -0
- package/templates/nextjs-standalone/ee/src/backups/engine.ts +355 -0
- package/templates/nextjs-standalone/ee/src/backups/index.ts +26 -0
- package/templates/nextjs-standalone/ee/src/backups/restore.ts +169 -0
- package/templates/nextjs-standalone/ee/src/backups/scheduler.ts +153 -0
- package/templates/nextjs-standalone/ee/src/backups/verify.ts +124 -0
- package/templates/nextjs-standalone/ee/src/branding/white-label.ts +228 -0
- package/templates/nextjs-standalone/ee/src/compliance/masking.ts +477 -0
- package/templates/nextjs-standalone/ee/src/compliance/patterns.ts +16 -0
- package/templates/nextjs-standalone/ee/src/compliance/pii-detection.ts +217 -0
- package/templates/nextjs-standalone/ee/src/compliance/reports.ts +402 -0
- package/templates/nextjs-standalone/ee/src/deploy-mode.ts +37 -0
- package/templates/nextjs-standalone/ee/src/governance/approval.ts +699 -0
- package/templates/nextjs-standalone/ee/src/index.ts +74 -0
- package/templates/nextjs-standalone/ee/src/platform/domains.ts +562 -0
- package/templates/nextjs-standalone/ee/src/platform/model-routing.ts +382 -0
- package/templates/nextjs-standalone/ee/src/platform/residency.ts +265 -0
- package/templates/nextjs-standalone/ee/src/sla/alerting.ts +382 -0
- package/templates/nextjs-standalone/ee/src/sla/index.ts +12 -0
- package/templates/nextjs-standalone/ee/src/sla/metrics.ts +275 -0
- package/templates/nextjs-standalone/ee/src/test-setup.ts +1 -0
- package/templates/nextjs-standalone/next.config.ts +1 -1
- package/templates/nextjs-standalone/package.json +50 -30
- package/templates/nextjs-standalone/src/api/index.ts +336 -24
- package/templates/nextjs-standalone/src/api/routes/actions.ts +443 -176
- package/templates/nextjs-standalone/src/api/routes/admin-abuse.ts +219 -0
- package/templates/nextjs-standalone/src/api/routes/admin-approval.ts +418 -0
- package/templates/nextjs-standalone/src/api/routes/admin-audit-retention.ts +405 -0
- package/templates/nextjs-standalone/src/api/routes/admin-auth.ts +122 -0
- package/templates/nextjs-standalone/src/api/routes/admin-branding.ts +252 -0
- package/templates/nextjs-standalone/src/api/routes/admin-compliance.ts +352 -0
- package/templates/nextjs-standalone/src/api/routes/admin-domains.ts +334 -0
- package/templates/nextjs-standalone/src/api/routes/admin-integrations.ts +2667 -0
- package/templates/nextjs-standalone/src/api/routes/admin-ip-allowlist.ts +261 -0
- package/templates/nextjs-standalone/src/api/routes/admin-learned-patterns.ts +525 -0
- package/templates/nextjs-standalone/src/api/routes/admin-model-config.ts +252 -0
- package/templates/nextjs-standalone/src/api/routes/admin-onboarding-emails.ts +145 -0
- package/templates/nextjs-standalone/src/api/routes/admin-orgs.ts +710 -0
- package/templates/nextjs-standalone/src/api/routes/admin-prompts.ts +694 -0
- package/templates/nextjs-standalone/src/api/routes/admin-residency.ts +570 -0
- package/templates/nextjs-standalone/src/api/routes/admin-roles.ts +296 -0
- package/templates/nextjs-standalone/src/api/routes/admin-router.ts +120 -0
- package/templates/nextjs-standalone/src/api/routes/admin-sandbox.ts +417 -0
- package/templates/nextjs-standalone/src/api/routes/admin-scim.ts +262 -0
- package/templates/nextjs-standalone/src/api/routes/admin-sso.ts +545 -0
- package/templates/nextjs-standalone/src/api/routes/admin-suggestions.ts +176 -0
- package/templates/nextjs-standalone/src/api/routes/admin-usage.ts +310 -0
- package/templates/nextjs-standalone/src/api/routes/admin.ts +4156 -898
- package/templates/nextjs-standalone/src/api/routes/auth-preamble.ts +105 -0
- package/templates/nextjs-standalone/src/api/routes/billing.ts +397 -0
- package/templates/nextjs-standalone/src/api/routes/chat.ts +597 -334
- package/templates/nextjs-standalone/src/api/routes/conversations.ts +987 -132
- package/templates/nextjs-standalone/src/api/routes/demo.ts +673 -0
- package/templates/nextjs-standalone/src/api/routes/discord.ts +274 -0
- package/templates/nextjs-standalone/src/api/routes/ee-error-handler.ts +32 -0
- package/templates/nextjs-standalone/src/api/routes/health.ts +129 -14
- package/templates/nextjs-standalone/src/api/routes/middleware.ts +244 -0
- package/templates/nextjs-standalone/src/api/routes/onboarding-emails.ts +134 -0
- package/templates/nextjs-standalone/src/api/routes/onboarding.ts +1109 -0
- package/templates/nextjs-standalone/src/api/routes/openapi.ts +184 -1597
- package/templates/nextjs-standalone/src/api/routes/platform-admin.ts +760 -0
- package/templates/nextjs-standalone/src/api/routes/platform-backups.ts +436 -0
- package/templates/nextjs-standalone/src/api/routes/platform-domains.ts +235 -0
- package/templates/nextjs-standalone/src/api/routes/platform-residency.ts +257 -0
- package/templates/nextjs-standalone/src/api/routes/platform-sla.ts +379 -0
- package/templates/nextjs-standalone/src/api/routes/prompts.ts +221 -0
- package/templates/nextjs-standalone/src/api/routes/public-branding.ts +106 -0
- package/templates/nextjs-standalone/src/api/routes/query.ts +330 -219
- package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +393 -297
- package/templates/nextjs-standalone/src/api/routes/semantic.ts +179 -0
- package/templates/nextjs-standalone/src/api/routes/sessions.ts +210 -0
- package/templates/nextjs-standalone/src/api/routes/shared-domains.ts +98 -0
- package/templates/nextjs-standalone/src/api/routes/shared-schemas.ts +139 -0
- package/templates/nextjs-standalone/src/api/routes/slack.ts +209 -52
- package/templates/nextjs-standalone/src/api/routes/suggestions.ts +233 -0
- package/templates/nextjs-standalone/src/api/routes/tables.ts +67 -0
- package/templates/nextjs-standalone/src/api/routes/teams.ts +222 -0
- package/templates/nextjs-standalone/src/api/routes/validate-sql.ts +188 -0
- package/templates/nextjs-standalone/src/api/routes/validation-hook.ts +62 -0
- package/templates/nextjs-standalone/src/api/routes/widget-loader.ts +356 -0
- package/templates/nextjs-standalone/src/api/routes/widget.ts +428 -0
- package/templates/nextjs-standalone/src/api/routes/wizard.ts +852 -0
- package/templates/nextjs-standalone/src/api/server.ts +187 -69
- package/templates/nextjs-standalone/src/app/error.tsx +5 -2
- package/templates/nextjs-standalone/src/app/globals.css +1 -1
- package/templates/nextjs-standalone/src/app/layout.tsx +7 -2
- package/templates/nextjs-standalone/src/app/page.tsx +39 -5
- package/templates/nextjs-standalone/src/components/data-table/data-table-column-header.tsx +99 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-date-filter.tsx +225 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-expandable.tsx +125 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-faceted-filter.tsx +189 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-pagination.tsx +112 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-range-filter.tsx +122 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-slider-filter.tsx +256 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-sort-list.tsx +407 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-toolbar.tsx +149 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table-view-options.tsx +89 -0
- package/templates/nextjs-standalone/src/components/data-table/data-table.tsx +105 -0
- package/templates/nextjs-standalone/src/components/form-dialog.tsx +135 -0
- package/templates/nextjs-standalone/src/components/ui/accordion.tsx +66 -0
- package/templates/nextjs-standalone/src/components/ui/calendar.tsx +220 -0
- package/templates/nextjs-standalone/src/components/ui/checkbox.tsx +32 -0
- package/templates/nextjs-standalone/src/components/ui/faceted.tsx +283 -0
- package/templates/nextjs-standalone/src/components/ui/form.tsx +167 -0
- package/templates/nextjs-standalone/src/components/ui/label.tsx +24 -0
- package/templates/nextjs-standalone/src/components/ui/popover.tsx +89 -0
- package/templates/nextjs-standalone/src/components/ui/progress.tsx +31 -0
- package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +6 -2
- package/templates/nextjs-standalone/src/components/ui/slider.tsx +63 -0
- package/templates/nextjs-standalone/src/components/ui/sortable.tsx +581 -0
- package/templates/nextjs-standalone/src/components/ui/switch.tsx +35 -0
- package/templates/nextjs-standalone/src/components/ui/textarea.tsx +18 -0
- package/templates/nextjs-standalone/src/config/data-table.ts +82 -0
- package/templates/nextjs-standalone/src/env-check.ts +74 -0
- package/templates/nextjs-standalone/src/hooks/use-callback-ref.ts +27 -0
- package/templates/nextjs-standalone/src/hooks/use-data-table.ts +316 -0
- package/templates/nextjs-standalone/src/hooks/use-debounced-callback.ts +28 -0
- package/templates/nextjs-standalone/src/lib/action-types.ts +7 -41
- package/templates/nextjs-standalone/src/lib/agent-query.ts +4 -2
- package/templates/nextjs-standalone/src/lib/agent.ts +363 -31
- package/templates/nextjs-standalone/src/lib/api-url.ts +2 -3
- package/templates/nextjs-standalone/src/lib/auth/admin-permissions.ts +38 -0
- package/templates/nextjs-standalone/src/lib/auth/audit.ts +19 -4
- package/templates/nextjs-standalone/src/lib/auth/byot.ts +3 -3
- package/templates/nextjs-standalone/src/lib/auth/detect.ts +29 -8
- package/templates/nextjs-standalone/src/lib/auth/managed.ts +104 -14
- package/templates/nextjs-standalone/src/lib/auth/middleware.ts +53 -6
- package/templates/nextjs-standalone/src/lib/auth/migrate.ts +140 -15
- package/templates/nextjs-standalone/src/lib/auth/oauth-state.ts +123 -0
- package/templates/nextjs-standalone/src/lib/auth/org-permissions.ts +55 -0
- package/templates/nextjs-standalone/src/lib/auth/permissions.ts +26 -19
- package/templates/nextjs-standalone/src/lib/auth/server.ts +355 -9
- package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +3 -3
- package/templates/nextjs-standalone/src/lib/auth/types.ts +15 -21
- package/templates/nextjs-standalone/src/lib/billing/enforcement.ts +368 -0
- package/templates/nextjs-standalone/src/lib/billing/plans.ts +155 -0
- package/templates/nextjs-standalone/src/lib/cache/index.ts +92 -0
- package/templates/nextjs-standalone/src/lib/cache/keys.ts +30 -0
- package/templates/nextjs-standalone/src/lib/cache/lru.ts +79 -0
- package/templates/nextjs-standalone/src/lib/cache/types.ts +31 -0
- package/templates/nextjs-standalone/src/lib/compose-refs.ts +62 -0
- package/templates/nextjs-standalone/src/lib/config.ts +563 -11
- package/templates/nextjs-standalone/src/lib/connection-types.ts +9 -0
- package/templates/nextjs-standalone/src/lib/conversation-types.ts +1 -25
- package/templates/nextjs-standalone/src/lib/conversations.ts +345 -14
- package/templates/nextjs-standalone/src/lib/data-table.ts +61 -0
- package/templates/nextjs-standalone/src/lib/db/connection.ts +793 -39
- package/templates/nextjs-standalone/src/lib/db/internal.ts +985 -139
- package/templates/nextjs-standalone/src/lib/db/migrate.ts +295 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0000_baseline.sql +703 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0001_teams_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0002_discord_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0005_oauth_state.sql +16 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0008_github_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0009_linear_installations.sql +15 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0011_email_installations.sql +16 -0
- package/templates/nextjs-standalone/src/lib/db/migrations/0012_region_migrations.sql +25 -0
- package/templates/nextjs-standalone/src/lib/db/schema.ts +1120 -0
- package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +89 -139
- package/templates/nextjs-standalone/src/lib/demo.ts +308 -0
- package/templates/nextjs-standalone/src/lib/discord/store.ts +225 -0
- package/templates/nextjs-standalone/src/lib/effect/ai.ts +243 -0
- package/templates/nextjs-standalone/src/lib/effect/errors.ts +234 -0
- package/templates/nextjs-standalone/src/lib/effect/hono.ts +454 -0
- package/templates/nextjs-standalone/src/lib/effect/index.ts +137 -0
- package/templates/nextjs-standalone/src/lib/effect/layers.ts +496 -0
- package/templates/nextjs-standalone/src/lib/effect/services.ts +776 -0
- package/templates/nextjs-standalone/src/lib/effect/sql.ts +178 -0
- package/templates/nextjs-standalone/src/lib/effect/toolkit.ts +123 -0
- package/templates/nextjs-standalone/src/lib/email/delivery.ts +232 -0
- package/templates/nextjs-standalone/src/lib/email/engine.ts +349 -0
- package/templates/nextjs-standalone/src/lib/email/hooks.ts +107 -0
- package/templates/nextjs-standalone/src/lib/email/index.ts +16 -0
- package/templates/nextjs-standalone/src/lib/email/scheduler.ts +72 -0
- package/templates/nextjs-standalone/src/lib/email/sequence.ts +73 -0
- package/templates/nextjs-standalone/src/lib/email/store.ts +163 -0
- package/templates/nextjs-standalone/src/lib/email/templates.ts +215 -0
- package/templates/nextjs-standalone/src/lib/format.test.ts +117 -0
- package/templates/nextjs-standalone/src/lib/format.ts +67 -0
- package/templates/nextjs-standalone/src/lib/gchat/store.ts +202 -0
- package/templates/nextjs-standalone/src/lib/github/store.ts +197 -0
- package/templates/nextjs-standalone/src/lib/id.ts +29 -0
- package/templates/nextjs-standalone/src/lib/integrations/types.ts +166 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-analyzer.ts +224 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-cache.ts +229 -0
- package/templates/nextjs-standalone/src/lib/learn/pattern-proposer.ts +87 -0
- package/templates/nextjs-standalone/src/lib/learn/suggestion-helpers.ts +34 -0
- package/templates/nextjs-standalone/src/lib/learn/suggestions.ts +139 -0
- package/templates/nextjs-standalone/src/lib/linear/store.ts +200 -0
- package/templates/nextjs-standalone/src/lib/logger.ts +35 -3
- package/templates/nextjs-standalone/src/lib/metering.ts +272 -0
- package/templates/nextjs-standalone/src/lib/parsers.ts +99 -0
- package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +13 -11
- package/templates/nextjs-standalone/src/lib/plugins/index.ts +3 -1
- package/templates/nextjs-standalone/src/lib/plugins/registry.ts +58 -6
- package/templates/nextjs-standalone/src/lib/plugins/settings.ts +147 -0
- package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +6 -9
- package/templates/nextjs-standalone/src/lib/profiler.ts +1665 -0
- package/templates/nextjs-standalone/src/lib/providers.ts +188 -13
- package/templates/nextjs-standalone/src/lib/rls.ts +172 -60
- package/templates/nextjs-standalone/src/lib/sandbox/credentials.ts +206 -0
- package/templates/nextjs-standalone/src/lib/sandbox/validate.ts +179 -0
- package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +26 -94
- package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +174 -34
- package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -150
- package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +190 -154
- package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +74 -23
- package/templates/nextjs-standalone/src/lib/scheduler/preview.ts +72 -0
- package/templates/nextjs-standalone/src/lib/security/abuse.ts +463 -0
- package/templates/nextjs-standalone/src/lib/semantic/diff.ts +267 -0
- package/templates/nextjs-standalone/src/lib/semantic/entities.ts +167 -0
- package/templates/nextjs-standalone/src/lib/semantic/files.ts +283 -0
- package/templates/nextjs-standalone/src/lib/semantic/index.ts +27 -0
- package/templates/nextjs-standalone/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
- package/templates/nextjs-standalone/src/lib/semantic/sync.ts +581 -0
- package/templates/nextjs-standalone/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
- package/templates/nextjs-standalone/src/lib/settings.ts +817 -0
- package/templates/nextjs-standalone/src/lib/sidecar-types.ts +13 -0
- package/templates/nextjs-standalone/src/lib/slack/store.ts +134 -25
- package/templates/nextjs-standalone/src/lib/startup.ts +528 -362
- package/templates/nextjs-standalone/src/lib/teams/store.ts +216 -0
- package/templates/nextjs-standalone/src/lib/telegram/store.ts +202 -0
- package/templates/nextjs-standalone/src/lib/telemetry.ts +40 -0
- package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +8 -5
- package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +3 -1
- package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +276 -93
- package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +2 -2
- package/templates/nextjs-standalone/src/lib/tools/backends/detect.ts +16 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/index.ts +11 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/nsjail.ts +213 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/shared.ts +103 -0
- package/templates/nextjs-standalone/src/lib/tools/backends/types.ts +26 -0
- package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +7 -228
- package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +4 -29
- package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +18 -2
- package/templates/nextjs-standalone/src/lib/tools/explore.ts +246 -54
- package/templates/nextjs-standalone/src/lib/tools/index.ts +17 -0
- package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +11 -139
- package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +9 -132
- package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +184 -3
- package/templates/nextjs-standalone/src/lib/tools/python-stream.ts +33 -0
- package/templates/nextjs-standalone/src/lib/tools/python-wrapper.ts +129 -0
- package/templates/nextjs-standalone/src/lib/tools/python.ts +115 -15
- package/templates/nextjs-standalone/src/lib/tools/registry.ts +14 -2
- package/templates/nextjs-standalone/src/lib/tools/sql.ts +778 -362
- package/templates/nextjs-standalone/src/lib/tracing.ts +16 -0
- package/templates/nextjs-standalone/src/lib/whatsapp/store.ts +198 -0
- package/templates/nextjs-standalone/src/lib/workspace.ts +89 -0
- package/templates/nextjs-standalone/src/progress.ts +121 -0
- package/templates/nextjs-standalone/src/types/data-table.ts +48 -0
- package/templates/nextjs-standalone/src/ui/atlas-chat-reexport.ts +3 -0
- package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +26 -19
- package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +3 -3
- package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +57 -39
- package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +213 -35
- package/templates/nextjs-standalone/src/ui/components/admin/delivery-status-badge.tsx +53 -0
- package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +27 -6
- package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -52
- package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +2 -2
- package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +28 -5
- package/templates/nextjs-standalone/src/ui/components/admin-content-wrapper.tsx +87 -0
- package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +449 -166
- package/templates/nextjs-standalone/src/ui/components/branding-head.tsx +41 -0
- package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +62 -5
- package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +316 -125
- package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +4 -4
- package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +45 -4
- package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +86 -5
- package/templates/nextjs-standalone/src/ui/components/chat/follow-up-chips.tsx +29 -0
- package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +24 -0
- package/templates/nextjs-standalone/src/ui/components/chat/prompt-library.tsx +206 -0
- package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +106 -78
- package/templates/nextjs-standalone/src/ui/components/chat/result-card-base.tsx +101 -0
- package/templates/nextjs-standalone/src/ui/components/chat/share-dialog.tsx +377 -0
- package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +94 -73
- package/templates/nextjs-standalone/src/ui/components/chat/suggestion-chips.tsx +46 -0
- package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +16 -4
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +48 -17
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +38 -24
- package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
- package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +9 -2
- package/templates/nextjs-standalone/src/ui/components/error-boundary.tsx +66 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell.tsx +119 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-export.ts +287 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-shell.tsx +266 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/types.ts +39 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
- package/templates/nextjs-standalone/src/ui/components/notebook/use-notebook.ts +684 -0
- package/templates/nextjs-standalone/src/ui/components/org-switcher.tsx +111 -0
- package/templates/nextjs-standalone/src/ui/components/region-picker.tsx +103 -0
- package/templates/nextjs-standalone/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
- package/templates/nextjs-standalone/src/ui/components/social-icons.tsx +26 -0
- package/templates/nextjs-standalone/src/ui/components/tour/guided-tour.tsx +81 -0
- package/templates/nextjs-standalone/src/ui/components/tour/index.ts +5 -0
- package/templates/nextjs-standalone/src/ui/components/tour/nav-bar.tsx +100 -0
- package/templates/nextjs-standalone/src/ui/components/tour/tour-overlay.tsx +298 -0
- package/templates/nextjs-standalone/src/ui/components/tour/tour-steps.ts +43 -0
- package/templates/nextjs-standalone/src/ui/components/tour/types.ts +21 -0
- package/templates/nextjs-standalone/src/ui/components/tour/use-tour.ts +193 -0
- package/templates/nextjs-standalone/src/ui/context-reexport.ts +3 -0
- package/templates/nextjs-standalone/src/ui/hooks/theme-init-script.ts +17 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +38 -30
- package/templates/nextjs-standalone/src/ui/hooks/use-admin-mutation.ts +188 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-atlas-transport.ts +225 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-branding.ts +68 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +106 -83
- package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +134 -10
- package/templates/nextjs-standalone/src/ui/hooks/use-deploy-mode.ts +36 -0
- package/templates/nextjs-standalone/src/ui/hooks/use-platform-admin-guard.ts +49 -0
- package/templates/nextjs-standalone/src/ui/lib/action-types.ts +11 -63
- package/templates/nextjs-standalone/src/ui/lib/admin-schemas.ts +744 -0
- package/templates/nextjs-standalone/src/ui/lib/fetch-client.ts +84 -0
- package/templates/nextjs-standalone/src/ui/lib/fetch-error.ts +54 -0
- package/templates/nextjs-standalone/src/ui/lib/helpers.ts +94 -1
- package/templates/nextjs-standalone/src/ui/lib/types.ts +149 -140
- package/templates/nextjs-standalone/tsconfig.json +3 -2
- package/templates/docker/src/api/__tests__/actions.test.ts +0 -683
- package/templates/docker/src/api/__tests__/admin.test.ts +0 -820
- package/templates/docker/src/api/__tests__/auth.test.ts +0 -165
- package/templates/docker/src/api/__tests__/chat.test.ts +0 -376
- package/templates/docker/src/api/__tests__/conversations.test.ts +0 -555
- package/templates/docker/src/api/__tests__/cors.test.ts +0 -135
- package/templates/docker/src/api/__tests__/health-plugin.test.ts +0 -176
- package/templates/docker/src/api/__tests__/health.test.ts +0 -283
- package/templates/docker/src/api/__tests__/query.test.ts +0 -891
- package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +0 -601
- package/templates/docker/src/api/__tests__/slack.test.ts +0 -847
- package/templates/docker/src/lib/__tests__/agent-cache.test.ts +0 -439
- package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +0 -131
- package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
- package/templates/docker/src/lib/__tests__/agent-integration.test.ts +0 -516
- package/templates/docker/src/lib/__tests__/config-actions.test.ts +0 -166
- package/templates/docker/src/lib/__tests__/config.test.ts +0 -1113
- package/templates/docker/src/lib/__tests__/conversations.test.ts +0 -589
- package/templates/docker/src/lib/__tests__/errors.test.ts +0 -256
- package/templates/docker/src/lib/__tests__/logger.test.ts +0 -200
- package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
- package/templates/docker/src/lib/__tests__/providers.test.ts +0 -130
- package/templates/docker/src/lib/__tests__/rls.test.ts +0 -435
- package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
- package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
- package/templates/docker/src/lib/__tests__/semantic-index.test.ts +0 -547
- package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +0 -544
- package/templates/docker/src/lib/__tests__/semantic.test.ts +0 -363
- package/templates/docker/src/lib/__tests__/startup-actions.test.ts +0 -461
- package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +0 -429
- package/templates/docker/src/lib/__tests__/startup.test.ts +0 -470
- package/templates/docker/src/lib/__tests__/tracing.test.ts +0 -28
- package/templates/docker/src/lib/auth/__tests__/audit.test.ts +0 -418
- package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
- package/templates/docker/src/lib/auth/__tests__/byot.test.ts +0 -366
- package/templates/docker/src/lib/auth/__tests__/detect.test.ts +0 -190
- package/templates/docker/src/lib/auth/__tests__/managed.test.ts +0 -173
- package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +0 -456
- package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +0 -203
- package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +0 -225
- package/templates/docker/src/lib/auth/__tests__/server.test.ts +0 -34
- package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +0 -176
- package/templates/docker/src/lib/auth/__tests__/types.test.ts +0 -44
- package/templates/docker/src/lib/db/__tests__/connection.test.ts +0 -144
- package/templates/docker/src/lib/db/__tests__/internal.test.ts +0 -387
- package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +0 -190
- package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
- package/templates/docker/src/lib/db/__tests__/registry.test.ts +0 -398
- package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
- package/templates/docker/src/lib/errors.ts +0 -154
- package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
- package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +0 -529
- package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +0 -875
- package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +0 -373
- package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +0 -49
- package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +0 -799
- package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
- package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +0 -248
- package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
- package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
- package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
- package/templates/docker/src/lib/scheduler/index.ts +0 -7
- package/templates/docker/src/lib/slack/__tests__/api.test.ts +0 -160
- package/templates/docker/src/lib/slack/__tests__/format.test.ts +0 -237
- package/templates/docker/src/lib/slack/__tests__/store.test.ts +0 -188
- package/templates/docker/src/lib/slack/__tests__/threads.test.ts +0 -112
- package/templates/docker/src/lib/slack/__tests__/verify.test.ts +0 -111
- package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
- package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
- package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
- package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
- package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
- package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
- package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
- package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
- package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
- package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
- package/templates/docker/src/lib/tools/__tests__/python.test.ts +0 -331
- package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
- package/templates/docker/src/lib/tools/__tests__/registry.test.ts +0 -242
- package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
- package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
- package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
- package/templates/docker/src/lib/tools/__tests__/sql.test.ts +0 -709
- package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
- package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +0 -378
- package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
- package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
- package/templates/docker/src/test-setup.ts +0 -38
- package/templates/docker/src/types/vercel-sandbox.d.ts +0 -61
- package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +0 -116
- package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +0 -683
- package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +0 -820
- package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +0 -165
- package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +0 -376
- package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +0 -555
- package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +0 -135
- package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +0 -176
- package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +0 -283
- package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +0 -891
- package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +0 -601
- package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +0 -847
- package/templates/nextjs-standalone/src/app/global-error.tsx +0 -68
- package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +0 -439
- package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +0 -131
- package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
- package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +0 -516
- package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +0 -166
- package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +0 -1113
- package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +0 -589
- package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +0 -256
- package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +0 -200
- package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
- package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +0 -130
- package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +0 -435
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
- package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +0 -547
- package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +0 -544
- package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +0 -363
- package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +0 -461
- package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +0 -429
- package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +0 -470
- package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +0 -28
- package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +0 -418
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
- package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +0 -366
- package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +0 -190
- package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +0 -173
- package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +0 -456
- package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +0 -203
- package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +0 -225
- package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +0 -34
- package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +0 -176
- package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +0 -44
- package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +0 -144
- package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +0 -387
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +0 -190
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
- package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +0 -398
- package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
- package/templates/nextjs-standalone/src/lib/errors.ts +0 -154
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +0 -529
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +0 -875
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +0 -373
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +0 -49
- package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +0 -799
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +0 -248
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
- package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
- package/templates/nextjs-standalone/src/lib/scheduler/index.ts +0 -7
- package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +0 -160
- package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +0 -237
- package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +0 -188
- package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +0 -112
- package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +0 -111
- package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
- package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
- package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
- package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +0 -331
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
- package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +0 -242
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
- package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +0 -709
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +0 -378
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
- package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
- package/templates/nextjs-standalone/src/test-setup.ts +0 -38
- package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +0 -116
|
@@ -15,11 +15,51 @@
|
|
|
15
15
|
* auto-initializes from ATLAS_DATASOURCE_URL on first access.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { matchError } from "@useatlas/types";
|
|
19
|
+
import { Effect, Schedule, Duration, Fiber } from "effect";
|
|
18
20
|
import { createLogger } from "@atlas/api/lib/logger";
|
|
19
21
|
import { _resetWhitelists } from "@atlas/api/lib/semantic";
|
|
22
|
+
import type { HealthStatus } from "@atlas/api/lib/connection-types";
|
|
23
|
+
|
|
24
|
+
export type { HealthStatus } from "@atlas/api/lib/connection-types";
|
|
20
25
|
|
|
21
26
|
const log = createLogger("db");
|
|
22
27
|
|
|
28
|
+
// --- Typed error classes for connection lookup/configuration ---
|
|
29
|
+
|
|
30
|
+
/** Thrown when a connection ID is not found in the registry. */
|
|
31
|
+
export class ConnectionNotRegisteredError extends Error {
|
|
32
|
+
constructor(id: string) {
|
|
33
|
+
super(`Connection "${id}" is not registered.`);
|
|
34
|
+
this.name = "ConnectionNotRegisteredError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Thrown when creating an org pool would exceed maxTotalConnections. */
|
|
39
|
+
export class PoolCapacityExceededError extends Error {
|
|
40
|
+
constructor(
|
|
41
|
+
public readonly currentSlots: number,
|
|
42
|
+
public readonly requestedSlots: number,
|
|
43
|
+
public readonly maxTotalConnections: number,
|
|
44
|
+
) {
|
|
45
|
+
super(
|
|
46
|
+
`Cannot create org pool: would use ${currentSlots + requestedSlots} connection slots, exceeding maxTotalConnections (${maxTotalConnections}). ` +
|
|
47
|
+
`Reduce pool.perOrg.maxConnections, pool.perOrg.maxOrgs, or increase maxTotalConnections.`
|
|
48
|
+
);
|
|
49
|
+
this.name = "PoolCapacityExceededError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Thrown when no analytics datasource URL is configured. */
|
|
54
|
+
export class NoDatasourceConfiguredError extends Error {
|
|
55
|
+
constructor() {
|
|
56
|
+
super(
|
|
57
|
+
"No analytics datasource configured. Set ATLAS_DATASOURCE_URL to a PostgreSQL or MySQL connection string, or register a datasource plugin."
|
|
58
|
+
);
|
|
59
|
+
this.name = "NoDatasourceConfiguredError";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
23
63
|
/**
|
|
24
64
|
* Resolve the analytics datasource URL from env vars.
|
|
25
65
|
*
|
|
@@ -43,15 +83,17 @@ export interface QueryResult {
|
|
|
43
83
|
rows: Record<string, unknown>[];
|
|
44
84
|
}
|
|
45
85
|
|
|
86
|
+
export type { PoolStats, OrgPoolMetrics } from "@useatlas/types";
|
|
87
|
+
|
|
46
88
|
export interface DBConnection {
|
|
47
89
|
query(sql: string, timeoutMs?: number): Promise<QueryResult>;
|
|
48
90
|
close(): Promise<void>;
|
|
91
|
+
/** Return real-time pool counters, or null if not available. Postgres returns live stats; MySQL and plugin connections return null. */
|
|
92
|
+
getPoolStats?(): import("@useatlas/types").PoolStats | null;
|
|
49
93
|
}
|
|
50
94
|
|
|
51
95
|
export type DBType = "postgres" | "mysql" | (string & {});
|
|
52
96
|
|
|
53
|
-
export type HealthStatus = "healthy" | "degraded" | "unhealthy";
|
|
54
|
-
|
|
55
97
|
export interface HealthCheckResult {
|
|
56
98
|
status: HealthStatus;
|
|
57
99
|
latencyMs: number;
|
|
@@ -72,6 +114,21 @@ const UNHEALTHY_WINDOW_MS = 5 * 60 * 1000;
|
|
|
72
114
|
/** Number of consecutive failures before marking unhealthy (must also span UNHEALTHY_WINDOW_MS). */
|
|
73
115
|
const UNHEALTHY_THRESHOLD = 3;
|
|
74
116
|
|
|
117
|
+
/** Number of warmup probes per connection at startup. Configurable via ATLAS_POOL_WARMUP. */
|
|
118
|
+
function getPoolWarmup(): number {
|
|
119
|
+
const raw = parseInt(process.env.ATLAS_POOL_WARMUP ?? "", 10);
|
|
120
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 2;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Consecutive error threshold before auto-drain. Configurable via ATLAS_POOL_DRAIN_THRESHOLD. */
|
|
124
|
+
function getPoolDrainThreshold(): number {
|
|
125
|
+
const raw = parseInt(process.env.ATLAS_POOL_DRAIN_THRESHOLD ?? "", 10);
|
|
126
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 5;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Cooldown between drain operations to prevent drain storms. */
|
|
130
|
+
const DRAIN_COOLDOWN_MS = 30_000;
|
|
131
|
+
|
|
75
132
|
/**
|
|
76
133
|
* Extract the hostname from a database URL for audit purposes.
|
|
77
134
|
* Never exposes credentials. Returns "(unknown)" on parse failure.
|
|
@@ -84,7 +141,8 @@ export function extractTargetHost(url: string): string {
|
|
|
84
141
|
.replace(/^[a-z][a-z0-9+.-]*:\/\//, "http://");
|
|
85
142
|
const parsed = new URL(normalized);
|
|
86
143
|
return parsed.hostname || "(unknown)";
|
|
87
|
-
} catch {
|
|
144
|
+
} catch (err) {
|
|
145
|
+
log.debug({ err, url: url.slice(0, 50) }, "Failed to extract target host from URL");
|
|
88
146
|
return "(unknown)";
|
|
89
147
|
}
|
|
90
148
|
}
|
|
@@ -191,8 +249,26 @@ function createPostgresDB(config: ConnectionConfig): DBConnection {
|
|
|
191
249
|
);
|
|
192
250
|
if (check.rows.length === 0) {
|
|
193
251
|
schemaCheckPromise = null; // allow retry after error
|
|
252
|
+
let schemaHint = "";
|
|
253
|
+
try {
|
|
254
|
+
const schemasResult = await client.query(
|
|
255
|
+
"SELECT schema_name FROM information_schema.schemata " +
|
|
256
|
+
"WHERE schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast') " +
|
|
257
|
+
"AND schema_name NOT LIKE 'pg_temp_%' AND schema_name NOT LIKE 'pg_toast_temp_%' " +
|
|
258
|
+
"ORDER BY schema_name"
|
|
259
|
+
);
|
|
260
|
+
const schemas = schemasResult.rows.map(
|
|
261
|
+
(r: { schema_name: string }) => r.schema_name
|
|
262
|
+
);
|
|
263
|
+
if (schemas.length > 0) {
|
|
264
|
+
schemaHint = ` Available schemas: ${schemas.join(", ")}.`;
|
|
265
|
+
}
|
|
266
|
+
} catch (schemaListErr) {
|
|
267
|
+
// fallback: schema listing may fail due to permissions — generic message is sufficient
|
|
268
|
+
log.warn({ err: schemaListErr instanceof Error ? schemaListErr.message : String(schemaListErr) }, "Schema listing failed during schema validation — falling back to generic error message");
|
|
269
|
+
}
|
|
194
270
|
throw new Error(
|
|
195
|
-
`Schema "${pgSchema}" does not exist in the database
|
|
271
|
+
`Schema "${pgSchema}" does not exist in the database.${schemaHint} Check ATLAS_SCHEMA in your .env file.`
|
|
196
272
|
);
|
|
197
273
|
}
|
|
198
274
|
})();
|
|
@@ -218,6 +294,14 @@ function createPostgresDB(config: ConnectionConfig): DBConnection {
|
|
|
218
294
|
async close() {
|
|
219
295
|
await pool.end();
|
|
220
296
|
},
|
|
297
|
+
getPoolStats(): import("@useatlas/types").PoolStats | null {
|
|
298
|
+
return {
|
|
299
|
+
totalSize: pool.totalCount ?? 0,
|
|
300
|
+
activeCount: (pool.totalCount ?? 0) - (pool.idleCount ?? 0),
|
|
301
|
+
idleCount: pool.idleCount ?? 0,
|
|
302
|
+
waitingCount: pool.waitingCount ?? 0,
|
|
303
|
+
};
|
|
304
|
+
},
|
|
221
305
|
};
|
|
222
306
|
}
|
|
223
307
|
|
|
@@ -251,6 +335,10 @@ function createMySQLDB(config: ConnectionConfig): DBConnection {
|
|
|
251
335
|
async close() {
|
|
252
336
|
await pool.end();
|
|
253
337
|
},
|
|
338
|
+
getPoolStats(): import("@useatlas/types").PoolStats | null {
|
|
339
|
+
// mysql2 pool internals are not part of the public API — return null
|
|
340
|
+
return null;
|
|
341
|
+
},
|
|
254
342
|
};
|
|
255
343
|
}
|
|
256
344
|
|
|
@@ -289,27 +377,335 @@ interface RegistryEntry {
|
|
|
289
377
|
consecutiveFailures: number;
|
|
290
378
|
lastHealth: HealthCheckResult | null;
|
|
291
379
|
firstFailureAt: number | null;
|
|
292
|
-
/** Custom query validator (mirrors QueryValidationResult from plugin-sdk). */
|
|
293
|
-
validate?: (query: string) => { valid: boolean; reason?: string };
|
|
380
|
+
/** Custom query validator (mirrors QueryValidationResult from plugin-sdk). May be sync or async. */
|
|
381
|
+
validate?: (query: string) => { valid: boolean; reason?: string } | Promise<{ valid: boolean; reason?: string }>;
|
|
294
382
|
/** Plugin-provided metadata for SQL validation. */
|
|
295
383
|
pluginMeta?: ConnectionPluginMeta;
|
|
384
|
+
/** Total queries executed through this connection (lifetime, survives drain). */
|
|
385
|
+
totalQueries: number;
|
|
386
|
+
/** Total query errors (lifetime, survives drain). */
|
|
387
|
+
totalErrors: number;
|
|
388
|
+
/** Cumulative query wall-clock time in ms (lifetime, survives drain). */
|
|
389
|
+
totalQueryTimeMs: number;
|
|
390
|
+
/** Epoch ms of last drain, or null if never drained. Converted to ISO string in wire format. */
|
|
391
|
+
lastDrainAt: number | null;
|
|
392
|
+
/** Consecutive query failures — separate from consecutiveFailures (which includes health checks). Used for auto-drain threshold. */
|
|
393
|
+
consecutiveQueryFailures: number;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Configuration for per-org pool isolation. */
|
|
397
|
+
export interface OrgPoolSettings {
|
|
398
|
+
/** Whether org-scoped pooling is active. Only true when pool.perOrg is explicitly configured. */
|
|
399
|
+
enabled: boolean;
|
|
400
|
+
maxConnections: number;
|
|
401
|
+
idleTimeoutMs: number;
|
|
402
|
+
maxOrgs: number;
|
|
403
|
+
warmupProbes: number;
|
|
404
|
+
drainThreshold: number;
|
|
296
405
|
}
|
|
297
406
|
|
|
407
|
+
const DEFAULT_ORG_POOL_SETTINGS: OrgPoolSettings = {
|
|
408
|
+
enabled: false,
|
|
409
|
+
maxConnections: 5,
|
|
410
|
+
idleTimeoutMs: 30000,
|
|
411
|
+
maxOrgs: 50,
|
|
412
|
+
warmupProbes: 2,
|
|
413
|
+
drainThreshold: 5,
|
|
414
|
+
};
|
|
415
|
+
|
|
298
416
|
/**
|
|
299
|
-
* Named connection registry
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
417
|
+
* Named connection registry with tenant-scoped pool isolation.
|
|
418
|
+
*
|
|
419
|
+
* Base connections are created from a ConnectionConfig (URL + optional schema) via
|
|
420
|
+
* register(), or injected as pre-built DBConnection instances via registerDirect().
|
|
421
|
+
* The "default" connection auto-initializes from ATLAS_DATASOURCE_URL on first access.
|
|
422
|
+
*
|
|
423
|
+
* When an orgId is provided via getForOrg(), the registry creates an isolated pool
|
|
424
|
+
* instance for that org+connection pair, using the same URL/config as the base
|
|
425
|
+
* connection but with org-specific pool limits. This prevents noisy-neighbor issues
|
|
426
|
+
* in SaaS mode. When no orgId is present (self-hosted), the existing single pool
|
|
427
|
+
* is used unchanged.
|
|
303
428
|
*/
|
|
304
429
|
export class ConnectionRegistry {
|
|
305
430
|
private entries = new Map<string, RegistryEntry>();
|
|
306
431
|
private maxTotalConnections = 100;
|
|
307
|
-
private
|
|
432
|
+
private healthFiber: Fiber.RuntimeFiber<void, never> | null = null;
|
|
433
|
+
/** Connections currently in drain cooldown — managed via Effect.sleep. */
|
|
434
|
+
private drainCooldownSet = new Set<string>();
|
|
435
|
+
/** Tracks cooldown expiry timestamps for remaining-time messages. */
|
|
436
|
+
private drainCooldownExpiry = new Map<string, number>();
|
|
437
|
+
|
|
438
|
+
// --- Org-scoped pool isolation ---
|
|
439
|
+
/** Org pool entries keyed by "orgId:connectionId". */
|
|
440
|
+
private orgEntries = new Map<string, RegistryEntry>();
|
|
441
|
+
/** Monotonic access counter per orgId — used for LRU eviction. Monotonic counter
|
|
442
|
+
* avoids issues with Date.now() returning the same value for synchronous calls. */
|
|
443
|
+
private orgAccessSeq = new Map<string, number>();
|
|
444
|
+
/** Next sequence number for org access ordering. */
|
|
445
|
+
private _orgSeq = 0;
|
|
446
|
+
/** Per-org pool configuration. */
|
|
447
|
+
private orgPoolSettings: OrgPoolSettings = { ...DEFAULT_ORG_POOL_SETTINGS };
|
|
308
448
|
|
|
309
449
|
setMaxTotalConnections(n: number): void {
|
|
310
450
|
this.maxTotalConnections = n;
|
|
311
451
|
}
|
|
312
452
|
|
|
453
|
+
/** Configure per-org pool settings. Called from applyDatasources when pool.perOrg is set. Marks org pooling as enabled. */
|
|
454
|
+
setOrgPoolConfig(config: Partial<Omit<OrgPoolSettings, "enabled">>): void {
|
|
455
|
+
const merged = { ...this.orgPoolSettings, ...config, enabled: true };
|
|
456
|
+
if (merged.maxConnections < 1 || merged.maxOrgs < 1 || merged.drainThreshold < 1) {
|
|
457
|
+
throw new Error("Invalid org pool config: maxConnections, maxOrgs, and drainThreshold must be >= 1");
|
|
458
|
+
}
|
|
459
|
+
this.orgPoolSettings = merged;
|
|
460
|
+
|
|
461
|
+
// Warn at startup if theoretical org pool capacity exceeds maxTotalConnections.
|
|
462
|
+
// datasource count = base entries (or 1 if none registered yet)
|
|
463
|
+
const numDatasources = Math.max(this.entries.size, 1);
|
|
464
|
+
const theoreticalSlots = merged.maxOrgs * merged.maxConnections * numDatasources;
|
|
465
|
+
if (theoreticalSlots > this.maxTotalConnections) {
|
|
466
|
+
const severity = theoreticalSlots > this.maxTotalConnections * 2 ? "error" : "warn";
|
|
467
|
+
const logFn = severity === "error" ? log.error.bind(log) : log.warn.bind(log);
|
|
468
|
+
logFn(
|
|
469
|
+
{
|
|
470
|
+
maxOrgs: merged.maxOrgs,
|
|
471
|
+
maxConnections: merged.maxConnections,
|
|
472
|
+
numDatasources,
|
|
473
|
+
theoreticalSlots,
|
|
474
|
+
maxTotalConnections: this.maxTotalConnections,
|
|
475
|
+
},
|
|
476
|
+
"Org pool capacity (%d orgs × %d conns × %d datasources = %d slots) exceeds maxTotalConnections (%d). " +
|
|
477
|
+
"LRU eviction and capacity checks will prevent exceeding the limit, but consider adjusting pool.perOrg or maxTotalConnections.",
|
|
478
|
+
merged.maxOrgs,
|
|
479
|
+
merged.maxConnections,
|
|
480
|
+
numDatasources,
|
|
481
|
+
theoreticalSlots,
|
|
482
|
+
this.maxTotalConnections,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Whether org-scoped pooling is enabled (pool.perOrg configured). */
|
|
488
|
+
isOrgPoolingEnabled(): boolean {
|
|
489
|
+
return this.orgPoolSettings.enabled;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** Return the current org pool settings (for admin API / diagnostics). */
|
|
493
|
+
getOrgPoolConfig(): Readonly<OrgPoolSettings> {
|
|
494
|
+
return this.orgPoolSettings;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Return pool capacity warnings for the admin health check. Empty array when healthy. */
|
|
498
|
+
getPoolWarnings(): string[] {
|
|
499
|
+
const warnings: string[] = [];
|
|
500
|
+
if (!this.orgPoolSettings.enabled) return warnings;
|
|
501
|
+
|
|
502
|
+
const numDatasources = Math.max(this.entries.size, 1);
|
|
503
|
+
const theoreticalSlots = this.orgPoolSettings.maxOrgs * this.orgPoolSettings.maxConnections * numDatasources;
|
|
504
|
+
if (theoreticalSlots > this.maxTotalConnections) {
|
|
505
|
+
const ratio = Math.round(theoreticalSlots / this.maxTotalConnections * 10) / 10;
|
|
506
|
+
warnings.push(
|
|
507
|
+
`Org pool capacity (${this.orgPoolSettings.maxOrgs} orgs × ${this.orgPoolSettings.maxConnections} conns × ${numDatasources} datasources = ${theoreticalSlots} slots) ` +
|
|
508
|
+
`exceeds maxTotalConnections (${this.maxTotalConnections}) by ${ratio}×. ` +
|
|
509
|
+
`LRU eviction prevents exceeding the limit, but tenants may hit PoolCapacityExceededError under load.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
return warnings;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
private _orgKey(orgId: string, connectionId: string): string {
|
|
516
|
+
if (process.env.NODE_ENV !== "production") {
|
|
517
|
+
if (orgId.includes(":") || connectionId.includes(":")) {
|
|
518
|
+
throw new Error(`orgId/connectionId must not contain ':' — got orgId="${orgId}", connectionId="${connectionId}"`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return `${orgId}:${connectionId}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private _parseOrgKey(key: string): { orgId: string; connectionId: string } {
|
|
525
|
+
const sepIdx = key.indexOf(":");
|
|
526
|
+
return { orgId: key.slice(0, sepIdx), connectionId: key.slice(sepIdx + 1) };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Get an org-scoped connection pool. Lazy-creates on first access.
|
|
531
|
+
*
|
|
532
|
+
* Each org gets its own pool instance using the same URL/config as the base
|
|
533
|
+
* connection but with org-specific pool limits (maxConnections, idleTimeoutMs).
|
|
534
|
+
* Plugin-managed connections (no config) are returned directly since plugins
|
|
535
|
+
* manage their own pooling.
|
|
536
|
+
*
|
|
537
|
+
* Warmup probes fire asynchronously in the background after pool creation.
|
|
538
|
+
* LRU eviction removes the least recently used org's pools when maxOrgs is exceeded.
|
|
539
|
+
*/
|
|
540
|
+
getForOrg(orgId: string, connectionId: string = "default"): DBConnection {
|
|
541
|
+
const key = this._orgKey(orgId, connectionId);
|
|
542
|
+
const existing = this.orgEntries.get(key);
|
|
543
|
+
if (existing) {
|
|
544
|
+
existing.lastQueryAt = Date.now();
|
|
545
|
+
this.orgAccessSeq.set(orgId, ++this._orgSeq);
|
|
546
|
+
return existing.conn;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Ensure the base connection exists (trigger lazy init for "default")
|
|
550
|
+
if (connectionId === "default" && !this.entries.has("default")) {
|
|
551
|
+
this.getDefault();
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const baseEntry = this.entries.get(connectionId);
|
|
555
|
+
if (!baseEntry) {
|
|
556
|
+
throw new ConnectionNotRegisteredError(connectionId);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Plugin-managed connections don't have config — return base directly
|
|
560
|
+
if (!baseEntry.config) {
|
|
561
|
+
return baseEntry.conn;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Evict LRU org if at org-count capacity
|
|
565
|
+
this._evictLRUOrg();
|
|
566
|
+
|
|
567
|
+
// Evict LRU orgs if at total connection slot capacity.
|
|
568
|
+
// Mirrors the while-loop in register() for base connections.
|
|
569
|
+
const newSlots = this.orgPoolSettings.maxConnections;
|
|
570
|
+
while (this._totalPoolSlots() + newSlots > this.maxTotalConnections && this.orgAccessSeq.size > 0) {
|
|
571
|
+
const before = this.orgAccessSeq.size;
|
|
572
|
+
this._evictLRUOrgUnconditional();
|
|
573
|
+
if (this.orgAccessSeq.size === before) break; // no more evictable orgs
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Hard check after all eviction attempts
|
|
577
|
+
const currentSlots = this._totalPoolSlots();
|
|
578
|
+
if (currentSlots + newSlots > this.maxTotalConnections) {
|
|
579
|
+
throw new PoolCapacityExceededError(currentSlots, newSlots, this.maxTotalConnections);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Create org-scoped pool with org-specific limits
|
|
583
|
+
const orgConfig: ConnectionConfig = {
|
|
584
|
+
...baseEntry.config,
|
|
585
|
+
maxConnections: this.orgPoolSettings.maxConnections,
|
|
586
|
+
idleTimeoutMs: this.orgPoolSettings.idleTimeoutMs,
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
let newConn: DBConnection;
|
|
590
|
+
try {
|
|
591
|
+
newConn = createConnection(baseEntry.dbType, orgConfig);
|
|
592
|
+
} catch (err) {
|
|
593
|
+
log.error(
|
|
594
|
+
{ orgId, connectionId, err: err instanceof Error ? err.message : String(err) },
|
|
595
|
+
"Failed to create org-scoped pool after LRU eviction",
|
|
596
|
+
);
|
|
597
|
+
throw err;
|
|
598
|
+
}
|
|
599
|
+
const entry: RegistryEntry = {
|
|
600
|
+
conn: newConn,
|
|
601
|
+
dbType: baseEntry.dbType,
|
|
602
|
+
description: baseEntry.description,
|
|
603
|
+
lastQueryAt: Date.now(),
|
|
604
|
+
config: orgConfig,
|
|
605
|
+
targetHost: baseEntry.targetHost,
|
|
606
|
+
consecutiveFailures: 0,
|
|
607
|
+
lastHealth: null,
|
|
608
|
+
firstFailureAt: null,
|
|
609
|
+
validate: baseEntry.validate,
|
|
610
|
+
pluginMeta: baseEntry.pluginMeta,
|
|
611
|
+
totalQueries: 0,
|
|
612
|
+
totalErrors: 0,
|
|
613
|
+
totalQueryTimeMs: 0,
|
|
614
|
+
lastDrainAt: null,
|
|
615
|
+
consecutiveQueryFailures: 0,
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
this.orgEntries.set(key, entry);
|
|
619
|
+
this.orgAccessSeq.set(orgId, ++this._orgSeq);
|
|
620
|
+
log.info({ orgId, connectionId }, "Created org-scoped connection pool");
|
|
621
|
+
|
|
622
|
+
// Fire warmup probes in background (don't block the first request)
|
|
623
|
+
if (this.orgPoolSettings.warmupProbes > 0) {
|
|
624
|
+
this._warmupEntry(entry, this.orgPoolSettings.warmupProbes, { orgId, connectionId });
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return newConn;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Check if an org-scoped pool exists for the given org + connection. */
|
|
631
|
+
hasOrgPool(orgId: string, connectionId: string = "default"): boolean {
|
|
632
|
+
return this.orgEntries.has(this._orgKey(orgId, connectionId));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/** Return all org IDs that have active pools. */
|
|
636
|
+
listOrgs(): string[] {
|
|
637
|
+
return Array.from(this.orgAccessSeq.keys());
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** Return connection IDs with active pools for a specific org. */
|
|
641
|
+
listOrgConnections(orgId: string): string[] {
|
|
642
|
+
const prefix = `${orgId}:`;
|
|
643
|
+
const connections: string[] = [];
|
|
644
|
+
for (const key of this.orgEntries.keys()) {
|
|
645
|
+
if (key.startsWith(prefix)) {
|
|
646
|
+
connections.push(key.slice(prefix.length));
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return connections;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** Evict the least recently used org's pools when maxOrgs is exceeded. */
|
|
653
|
+
private _evictLRUOrg(): void {
|
|
654
|
+
if (this.orgAccessSeq.size < this.orgPoolSettings.maxOrgs) return;
|
|
655
|
+
this._evictLRUOrgUnconditional();
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** Unconditionally evict the least recently used org's pools. */
|
|
659
|
+
private _evictLRUOrgUnconditional(): void {
|
|
660
|
+
let lruOrg: string | null = null;
|
|
661
|
+
let lruSeq = Infinity;
|
|
662
|
+
for (const [orgId, seq] of this.orgAccessSeq) {
|
|
663
|
+
if (seq < lruSeq) {
|
|
664
|
+
lruSeq = seq;
|
|
665
|
+
lruOrg = orgId;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (lruOrg) {
|
|
670
|
+
log.info({ orgId: lruOrg }, "Evicting LRU org pools to free capacity");
|
|
671
|
+
this._closeOrgPools(lruOrg);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** Close all pools for a specific org (used by LRU eviction and drainOrg). */
|
|
676
|
+
private _closeOrgPools(orgId: string): void {
|
|
677
|
+
const prefix = `${orgId}:`;
|
|
678
|
+
const keysToDelete: string[] = [];
|
|
679
|
+
for (const [key, entry] of this.orgEntries) {
|
|
680
|
+
if (key.startsWith(prefix)) {
|
|
681
|
+
keysToDelete.push(key);
|
|
682
|
+
entry.conn.close().catch((err) => {
|
|
683
|
+
log.error({ key, err: err instanceof Error ? err.message : String(err) }, "Failed to close org pool — connections may be leaked");
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const key of keysToDelete) {
|
|
688
|
+
this.orgEntries.delete(key);
|
|
689
|
+
}
|
|
690
|
+
this.orgAccessSeq.delete(orgId);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** Run warmup probes on a single entry (used for org pool warmup). Never rejects — logs failures. */
|
|
694
|
+
private async _warmupEntry(entry: RegistryEntry, count: number, context?: { orgId?: string; connectionId?: string }): Promise<void> {
|
|
695
|
+
let failures = 0;
|
|
696
|
+
for (let i = 0; i < count; i++) {
|
|
697
|
+
try {
|
|
698
|
+
await entry.conn.query("SELECT 1", 5000);
|
|
699
|
+
} catch (err) {
|
|
700
|
+
failures++;
|
|
701
|
+
log.warn({ probe: i + 1, total: count, err: err instanceof Error ? err.message : String(err), ...context }, "Warmup probe failed");
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (failures === count && count > 0) {
|
|
705
|
+
log.error({ failures, total: count, ...context }, "All warmup probes failed — pool may be unhealthy");
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
313
709
|
private _totalPoolSlots(): number {
|
|
314
710
|
let total = 0;
|
|
315
711
|
for (const entry of this.entries.values()) {
|
|
@@ -317,6 +713,10 @@ export class ConnectionRegistry {
|
|
|
317
713
|
// their own pooling — count as 1 slot instead of the default 10.
|
|
318
714
|
total += entry.config?.maxConnections ?? (entry.targetHost === "(direct)" ? 1 : 10);
|
|
319
715
|
}
|
|
716
|
+
// Include org pool slots — each org pool uses its own maxConnections setting
|
|
717
|
+
for (const entry of this.orgEntries.values()) {
|
|
718
|
+
total += entry.config?.maxConnections ?? 1;
|
|
719
|
+
}
|
|
320
720
|
return total;
|
|
321
721
|
}
|
|
322
722
|
|
|
@@ -329,11 +729,12 @@ export class ConnectionRegistry {
|
|
|
329
729
|
}
|
|
330
730
|
}
|
|
331
731
|
if (oldest) {
|
|
332
|
-
|
|
732
|
+
const oldestId = oldest.id;
|
|
733
|
+
log.info({ connectionId: oldestId }, "Evicting LRU connection to free pool capacity");
|
|
333
734
|
oldest.entry.conn.close().catch((err) => {
|
|
334
|
-
log.warn({ err: err instanceof Error ? err.message : String(err), connectionId:
|
|
735
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: oldestId }, "Failed to close evicted connection");
|
|
335
736
|
});
|
|
336
|
-
this.entries.delete(
|
|
737
|
+
this.entries.delete(oldestId);
|
|
337
738
|
}
|
|
338
739
|
}
|
|
339
740
|
|
|
@@ -361,6 +762,11 @@ export class ConnectionRegistry {
|
|
|
361
762
|
consecutiveFailures: 0,
|
|
362
763
|
lastHealth: null,
|
|
363
764
|
firstFailureAt: null,
|
|
765
|
+
totalQueries: 0,
|
|
766
|
+
totalErrors: 0,
|
|
767
|
+
totalQueryTimeMs: 0,
|
|
768
|
+
lastDrainAt: null,
|
|
769
|
+
consecutiveQueryFailures: 0,
|
|
364
770
|
});
|
|
365
771
|
|
|
366
772
|
if (existing) {
|
|
@@ -376,7 +782,7 @@ export class ConnectionRegistry {
|
|
|
376
782
|
conn: DBConnection,
|
|
377
783
|
dbType: DBType,
|
|
378
784
|
description?: string,
|
|
379
|
-
validate?: (query: string) => { valid: boolean; reason?: string }
|
|
785
|
+
validate?: (query: string) => { valid: boolean; reason?: string } | Promise<{ valid: boolean; reason?: string }>,
|
|
380
786
|
meta?: ConnectionPluginMeta,
|
|
381
787
|
): void {
|
|
382
788
|
const existing = this.entries.get(id);
|
|
@@ -391,6 +797,11 @@ export class ConnectionRegistry {
|
|
|
391
797
|
firstFailureAt: null,
|
|
392
798
|
validate,
|
|
393
799
|
pluginMeta: meta,
|
|
800
|
+
totalQueries: 0,
|
|
801
|
+
totalErrors: 0,
|
|
802
|
+
totalQueryTimeMs: 0,
|
|
803
|
+
lastDrainAt: null,
|
|
804
|
+
consecutiveQueryFailures: 0,
|
|
394
805
|
});
|
|
395
806
|
if (existing) {
|
|
396
807
|
existing.conn.close().catch((err) => {
|
|
@@ -399,18 +810,79 @@ export class ConnectionRegistry {
|
|
|
399
810
|
}
|
|
400
811
|
}
|
|
401
812
|
|
|
813
|
+
has(id: string): boolean {
|
|
814
|
+
return this.entries.has(id);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
unregister(id: string): boolean {
|
|
818
|
+
if (id === "default") return false;
|
|
819
|
+
const entry = this.entries.get(id);
|
|
820
|
+
if (!entry) return false;
|
|
821
|
+
entry.conn.close().catch((err) => {
|
|
822
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: id }, "Failed to close connection during unregister");
|
|
823
|
+
});
|
|
824
|
+
this.entries.delete(id);
|
|
825
|
+
_resetWhitelists();
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
|
|
402
829
|
get(id: string): DBConnection {
|
|
403
830
|
const entry = this.entries.get(id);
|
|
404
831
|
if (!entry) {
|
|
405
|
-
throw new
|
|
832
|
+
throw new ConnectionNotRegisteredError(id);
|
|
406
833
|
}
|
|
407
834
|
entry.lastQueryAt = Date.now();
|
|
408
835
|
return entry.conn;
|
|
409
836
|
}
|
|
410
837
|
|
|
838
|
+
/** Record a query execution (success or failure) for metrics tracking. When orgId is provided, records against the org pool entry. */
|
|
839
|
+
recordQuery(id: string, durationMs: number, orgId?: string): void {
|
|
840
|
+
const entry = orgId
|
|
841
|
+
? this.orgEntries.get(this._orgKey(orgId, id))
|
|
842
|
+
: this.entries.get(id);
|
|
843
|
+
if (!entry) return;
|
|
844
|
+
entry.totalQueries++;
|
|
845
|
+
entry.totalQueryTimeMs += durationMs;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/** Record a query error for metrics tracking and auto-drain evaluation. When orgId is provided, operates on the org pool entry. */
|
|
849
|
+
recordError(id: string, orgId?: string): void {
|
|
850
|
+
const entry = orgId
|
|
851
|
+
? this.orgEntries.get(this._orgKey(orgId, id))
|
|
852
|
+
: this.entries.get(id);
|
|
853
|
+
if (!entry) return;
|
|
854
|
+
entry.totalErrors++;
|
|
855
|
+
entry.consecutiveQueryFailures++;
|
|
856
|
+
|
|
857
|
+
// Auto-drain when consecutive query failures exceed threshold
|
|
858
|
+
const threshold = orgId ? this.orgPoolSettings.drainThreshold : getPoolDrainThreshold();
|
|
859
|
+
if (entry.consecutiveQueryFailures >= threshold && entry.config) {
|
|
860
|
+
const drainKey = orgId ? this._orgKey(orgId, id) : id;
|
|
861
|
+
if (this.drainCooldownSet.has(drainKey)) {
|
|
862
|
+
log.debug({ connectionId: id, orgId }, "Pool drain skipped — cooldown active");
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
log.warn({ connectionId: id, orgId, consecutiveQueryFailures: entry.consecutiveQueryFailures }, "Pool drain triggered: consecutive error threshold exceeded");
|
|
866
|
+
this._drainAndRecreate(drainKey, entry);
|
|
867
|
+
this._startDrainCooldown(drainKey);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/** Reset consecutive failure counters (called on successful query). When orgId is provided, operates on the org pool entry. */
|
|
872
|
+
recordSuccess(id: string, orgId?: string): void {
|
|
873
|
+
const entry = orgId
|
|
874
|
+
? this.orgEntries.get(this._orgKey(orgId, id))
|
|
875
|
+
: this.entries.get(id);
|
|
876
|
+
if (entry) {
|
|
877
|
+
entry.consecutiveFailures = 0;
|
|
878
|
+
entry.consecutiveQueryFailures = 0;
|
|
879
|
+
entry.firstFailureAt = null;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
411
883
|
getDBType(id: string): DBType {
|
|
412
884
|
const entry = this.entries.get(id);
|
|
413
|
-
if (!entry) throw new
|
|
885
|
+
if (!entry) throw new ConnectionNotRegisteredError(id);
|
|
414
886
|
return entry.dbType;
|
|
415
887
|
}
|
|
416
888
|
|
|
@@ -422,7 +894,7 @@ export class ConnectionRegistry {
|
|
|
422
894
|
}
|
|
423
895
|
|
|
424
896
|
/** Return the custom query validator for a connection, if one was registered. Callers must verify connection existence first. */
|
|
425
|
-
getValidator(id: string): ((query: string) => { valid: boolean; reason?: string }) | undefined {
|
|
897
|
+
getValidator(id: string): ((query: string) => { valid: boolean; reason?: string } | Promise<{ valid: boolean; reason?: string }>) | undefined {
|
|
426
898
|
return this.entries.get(id)?.validate;
|
|
427
899
|
}
|
|
428
900
|
|
|
@@ -440,9 +912,7 @@ export class ConnectionRegistry {
|
|
|
440
912
|
if (!this.entries.has("default")) {
|
|
441
913
|
const url = resolveDatasourceUrl();
|
|
442
914
|
if (!url) {
|
|
443
|
-
throw new
|
|
444
|
-
"No analytics datasource configured. Set ATLAS_DATASOURCE_URL to a PostgreSQL or MySQL connection string, or register a datasource plugin."
|
|
445
|
-
);
|
|
915
|
+
throw new NoDatasourceConfiguredError();
|
|
446
916
|
}
|
|
447
917
|
this.register("default", {
|
|
448
918
|
url,
|
|
@@ -472,7 +942,7 @@ export class ConnectionRegistry {
|
|
|
472
942
|
async healthCheck(id: string): Promise<HealthCheckResult> {
|
|
473
943
|
const entry = this.entries.get(id);
|
|
474
944
|
if (!entry) {
|
|
475
|
-
throw new
|
|
945
|
+
throw new ConnectionNotRegisteredError(id);
|
|
476
946
|
}
|
|
477
947
|
|
|
478
948
|
const start = performance.now();
|
|
@@ -490,6 +960,8 @@ export class ConnectionRegistry {
|
|
|
490
960
|
return result;
|
|
491
961
|
} catch (err) {
|
|
492
962
|
const latencyMs = Math.round(performance.now() - start);
|
|
963
|
+
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
964
|
+
log.warn({ err: err instanceof Error ? err : new Error(rawMessage), connectionId: id, latencyMs }, "Health check failed");
|
|
493
965
|
entry.consecutiveFailures++;
|
|
494
966
|
if (entry.firstFailureAt === null) {
|
|
495
967
|
entry.firstFailureAt = Date.now();
|
|
@@ -503,10 +975,11 @@ export class ConnectionRegistry {
|
|
|
503
975
|
status = "degraded";
|
|
504
976
|
}
|
|
505
977
|
|
|
978
|
+
const matched = matchError(err);
|
|
506
979
|
const result: HealthCheckResult = {
|
|
507
980
|
status,
|
|
508
981
|
latencyMs,
|
|
509
|
-
message:
|
|
982
|
+
message: matched?.message ?? rawMessage,
|
|
510
983
|
checkedAt: new Date(),
|
|
511
984
|
};
|
|
512
985
|
entry.lastHealth = result;
|
|
@@ -514,33 +987,252 @@ export class ConnectionRegistry {
|
|
|
514
987
|
}
|
|
515
988
|
}
|
|
516
989
|
|
|
517
|
-
/** Start periodic health checks
|
|
990
|
+
/** Start periodic health checks via Effect.repeat + Schedule.spaced. Idempotent. */
|
|
518
991
|
startHealthChecks(intervalMs = 60_000): void {
|
|
519
|
-
if (this.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
992
|
+
if (this.healthFiber) return;
|
|
993
|
+
|
|
994
|
+
const listFn = () => this.list();
|
|
995
|
+
const checkFn = (id: string) => this.healthCheck(id);
|
|
996
|
+
const healthCheckAll = Effect.gen(function* () {
|
|
997
|
+
const ids = listFn();
|
|
998
|
+
yield* Effect.forEach(
|
|
999
|
+
ids,
|
|
1000
|
+
(id) =>
|
|
1001
|
+
Effect.tryPromise({
|
|
1002
|
+
try: () => checkFn(id),
|
|
1003
|
+
catch: (err) => (err instanceof Error ? err.message : String(err)),
|
|
1004
|
+
}).pipe(
|
|
1005
|
+
Effect.catchAll((errMsg) => {
|
|
1006
|
+
log.warn({ connectionId: id, err: errMsg }, "Periodic health check failed");
|
|
1007
|
+
return Effect.void;
|
|
1008
|
+
}),
|
|
1009
|
+
),
|
|
1010
|
+
{ concurrency: "unbounded" },
|
|
1011
|
+
);
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
this.healthFiber = Effect.runFork(
|
|
1015
|
+
healthCheckAll.pipe(
|
|
1016
|
+
// Catch expected failures but let defects (programming errors) crash the fiber.
|
|
1017
|
+
// The inner forEach already catches individual health check failures, so this
|
|
1018
|
+
// outer handler is a safety net for unexpected errors in the cycle itself.
|
|
1019
|
+
Effect.catchAllCause((cause) => {
|
|
1020
|
+
const msg = cause.toString();
|
|
1021
|
+
log.warn({ err: msg }, "Health check cycle failed");
|
|
1022
|
+
return Effect.void;
|
|
1023
|
+
}),
|
|
1024
|
+
Effect.repeat(Schedule.spaced(Duration.millis(intervalMs))),
|
|
1025
|
+
Effect.asVoid,
|
|
1026
|
+
),
|
|
1027
|
+
);
|
|
528
1028
|
}
|
|
529
1029
|
|
|
530
|
-
/** Stop periodic health checks. */
|
|
1030
|
+
/** Stop periodic health checks by interrupting the Effect fiber. */
|
|
531
1031
|
stopHealthChecks(): void {
|
|
532
|
-
if (this.
|
|
533
|
-
|
|
534
|
-
this.
|
|
1032
|
+
if (this.healthFiber) {
|
|
1033
|
+
Effect.runFork(Fiber.interrupt(this.healthFiber));
|
|
1034
|
+
this.healthFiber = null;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Pre-warm connections by running SELECT 1 on each registered pool.
|
|
1040
|
+
* Probes across different connections run in parallel.
|
|
1041
|
+
* @param count Number of warmup probes per connection (default: ATLAS_POOL_WARMUP env var, or 2 if unset).
|
|
1042
|
+
*/
|
|
1043
|
+
async warmup(count?: number): Promise<void> {
|
|
1044
|
+
const n = count ?? getPoolWarmup();
|
|
1045
|
+
if (n <= 0) return;
|
|
1046
|
+
const ids = this.list();
|
|
1047
|
+
if (ids.length === 0) return;
|
|
1048
|
+
|
|
1049
|
+
let total = 0;
|
|
1050
|
+
let ready = 0;
|
|
1051
|
+
await Promise.all(ids.map(async (id) => {
|
|
1052
|
+
const entry = this.entries.get(id);
|
|
1053
|
+
if (!entry) return;
|
|
1054
|
+
for (let i = 0; i < n; i++) {
|
|
1055
|
+
total++;
|
|
1056
|
+
try {
|
|
1057
|
+
await entry.conn.query("SELECT 1", 5000);
|
|
1058
|
+
ready++;
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
log.warn({ connectionId: id, probe: i + 1, err: err instanceof Error ? err.message : String(err) }, "Pool warmup probe failed");
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}));
|
|
1064
|
+
if (ready === 0 && total > 0) {
|
|
1065
|
+
log.error({ ready, total }, "Pool warmup failed: no connections ready");
|
|
1066
|
+
} else if (ready < total) {
|
|
1067
|
+
log.warn({ ready, total }, "Pool warmup partial: some probes failed");
|
|
1068
|
+
} else {
|
|
1069
|
+
log.info({ ready, total }, "Pool warmed: all connections ready");
|
|
535
1070
|
}
|
|
536
1071
|
}
|
|
537
1072
|
|
|
1073
|
+
/**
|
|
1074
|
+
* Drain a connection pool and recreate it from stored config.
|
|
1075
|
+
* Only works for config-registered connections (not plugin/direct connections).
|
|
1076
|
+
* The old pool is closed asynchronously in the background; the returned
|
|
1077
|
+
* promise resolves once the new pool is created, not when the old finishes closing.
|
|
1078
|
+
*
|
|
1079
|
+
* Drain cooldown is managed via Effect.sleep — no Date.now arithmetic.
|
|
1080
|
+
*/
|
|
1081
|
+
async drain(id: string): Promise<{ drained: boolean; message: string }> {
|
|
1082
|
+
const entry = this.entries.get(id);
|
|
1083
|
+
if (!entry) throw new ConnectionNotRegisteredError(id);
|
|
1084
|
+
|
|
1085
|
+
if (!entry.config) {
|
|
1086
|
+
return { drained: false, message: "Cannot drain plugin-managed connection — plugin must re-register it" };
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (this.drainCooldownSet.has(id)) {
|
|
1090
|
+
const expiresAt = this.drainCooldownExpiry.get(id) ?? 0;
|
|
1091
|
+
const remainingSec = Math.max(1, Math.ceil((expiresAt - Date.now()) / 1000));
|
|
1092
|
+
return { drained: false, message: `Drain cooldown active — wait ${remainingSec}s` };
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
this._drainAndRecreate(id, entry);
|
|
1096
|
+
this._startDrainCooldown(id);
|
|
1097
|
+
return { drained: true, message: "Pool drained and recreated" };
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/** Internal: close and recreate a pool from config. On failure, keeps the existing connection. */
|
|
1101
|
+
private _drainAndRecreate(id: string, entry: RegistryEntry): void {
|
|
1102
|
+
if (!entry.config) return;
|
|
1103
|
+
|
|
1104
|
+
const config = entry.config;
|
|
1105
|
+
const dbType = entry.dbType;
|
|
1106
|
+
const oldConn = entry.conn;
|
|
1107
|
+
|
|
1108
|
+
let newConn: DBConnection;
|
|
1109
|
+
try {
|
|
1110
|
+
newConn = createConnection(dbType, config);
|
|
1111
|
+
} catch (err) {
|
|
1112
|
+
log.error(
|
|
1113
|
+
{ err: err instanceof Error ? err : new Error(String(err)), connectionId: id },
|
|
1114
|
+
"Failed to recreate pool during drain — keeping existing connection",
|
|
1115
|
+
);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
entry.conn = newConn;
|
|
1120
|
+
// Don't reset consecutiveFailures — let recordSuccess() do it on actual recovery.
|
|
1121
|
+
// This prevents masking ongoing outages in admin metrics.
|
|
1122
|
+
entry.consecutiveQueryFailures = 0;
|
|
1123
|
+
entry.firstFailureAt = null;
|
|
1124
|
+
entry.lastDrainAt = Date.now();
|
|
1125
|
+
|
|
1126
|
+
oldConn.close().catch((err) => {
|
|
1127
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: id }, "Failed to close drained pool");
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Manages drain cooldown by forking an Effect.sleep fiber that removes
|
|
1133
|
+
* the connection ID from drainCooldownSet after DRAIN_COOLDOWN_MS.
|
|
1134
|
+
* The fiber is fire-and-forget — if the process exits before the timer
|
|
1135
|
+
* fires, the cooldown is implicitly cleared via shutdown()/reset().
|
|
1136
|
+
*/
|
|
1137
|
+
private _startDrainCooldown(id: string): void {
|
|
1138
|
+
this.drainCooldownSet.add(id);
|
|
1139
|
+
this.drainCooldownExpiry.set(id, Date.now() + DRAIN_COOLDOWN_MS);
|
|
1140
|
+
Effect.runFork(
|
|
1141
|
+
Effect.sleep(Duration.millis(DRAIN_COOLDOWN_MS)).pipe(
|
|
1142
|
+
Effect.andThen(
|
|
1143
|
+
Effect.sync(() => {
|
|
1144
|
+
this.drainCooldownSet.delete(id);
|
|
1145
|
+
this.drainCooldownExpiry.delete(id);
|
|
1146
|
+
}),
|
|
1147
|
+
),
|
|
1148
|
+
),
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Return pool metrics for a specific connection. */
|
|
1153
|
+
getPoolMetrics(id: string): import("@useatlas/types").PoolMetrics {
|
|
1154
|
+
const entry = this.entries.get(id);
|
|
1155
|
+
if (!entry) throw new ConnectionNotRegisteredError(id);
|
|
1156
|
+
|
|
1157
|
+
return {
|
|
1158
|
+
connectionId: id,
|
|
1159
|
+
dbType: entry.dbType,
|
|
1160
|
+
pool: entry.conn.getPoolStats?.() ?? null,
|
|
1161
|
+
totalQueries: entry.totalQueries,
|
|
1162
|
+
totalErrors: entry.totalErrors,
|
|
1163
|
+
avgQueryTimeMs: entry.totalQueries > 0 ? Math.round(entry.totalQueryTimeMs / entry.totalQueries) : 0,
|
|
1164
|
+
consecutiveFailures: entry.consecutiveQueryFailures,
|
|
1165
|
+
lastDrainAt: entry.lastDrainAt ? new Date(entry.lastDrainAt).toISOString() : null,
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/** Return pool metrics for all registered connections. */
|
|
1170
|
+
getAllPoolMetrics(): import("@useatlas/types").PoolMetrics[] {
|
|
1171
|
+
return Array.from(this.entries.keys()).map((id) => this.getPoolMetrics(id));
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* Return pool metrics for org-scoped pools.
|
|
1176
|
+
* When orgId is provided, returns only that org's pools.
|
|
1177
|
+
* When omitted, returns metrics for all org pools.
|
|
1178
|
+
*/
|
|
1179
|
+
getOrgPoolMetrics(orgId?: string): import("@useatlas/types").OrgPoolMetrics[] {
|
|
1180
|
+
const results: import("@useatlas/types").OrgPoolMetrics[] = [];
|
|
1181
|
+
for (const [key, entry] of this.orgEntries) {
|
|
1182
|
+
const { orgId: entryOrgId, connectionId } = this._parseOrgKey(key);
|
|
1183
|
+
if (orgId && entryOrgId !== orgId) continue;
|
|
1184
|
+
results.push({
|
|
1185
|
+
orgId: entryOrgId,
|
|
1186
|
+
connectionId,
|
|
1187
|
+
dbType: entry.dbType,
|
|
1188
|
+
pool: entry.conn.getPoolStats?.() ?? null,
|
|
1189
|
+
totalQueries: entry.totalQueries,
|
|
1190
|
+
totalErrors: entry.totalErrors,
|
|
1191
|
+
avgQueryTimeMs: entry.totalQueries > 0 ? Math.round(entry.totalQueryTimeMs / entry.totalQueries) : 0,
|
|
1192
|
+
consecutiveFailures: entry.consecutiveQueryFailures,
|
|
1193
|
+
lastDrainAt: entry.lastDrainAt ? new Date(entry.lastDrainAt).toISOString() : null,
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
return results;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Gracefully drain all pools for an org. In-flight queries complete on
|
|
1201
|
+
* the old pool before it is closed. Used when an org is deactivated or
|
|
1202
|
+
* when an admin needs to force-recycle an org's connections.
|
|
1203
|
+
*/
|
|
1204
|
+
async drainOrg(orgId: string): Promise<{ drained: number }> {
|
|
1205
|
+
const prefix = `${orgId}:`;
|
|
1206
|
+
let drained = 0;
|
|
1207
|
+
const closing: Promise<void>[] = [];
|
|
1208
|
+
|
|
1209
|
+
for (const [key, entry] of this.orgEntries) {
|
|
1210
|
+
if (key.startsWith(prefix)) {
|
|
1211
|
+
closing.push(
|
|
1212
|
+
entry.conn.close().catch((err) => {
|
|
1213
|
+
log.warn({ key, err: err instanceof Error ? err.message : String(err) }, "Failed to close org pool during drain");
|
|
1214
|
+
}),
|
|
1215
|
+
);
|
|
1216
|
+
this.orgEntries.delete(key);
|
|
1217
|
+
drained++;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
this.orgAccessSeq.delete(orgId);
|
|
1222
|
+
await Promise.all(closing);
|
|
1223
|
+
log.info({ orgId, drained }, "Org pools drained");
|
|
1224
|
+
return { drained };
|
|
1225
|
+
}
|
|
1226
|
+
|
|
538
1227
|
/**
|
|
539
1228
|
* Graceful shutdown: stop health checks, close all connections (awaited), and
|
|
540
|
-
* reset whitelists.
|
|
1229
|
+
* reset whitelists. When managed by Effect scope, this is called automatically
|
|
1230
|
+
* via the scope finalizer — no manual ordering needed.
|
|
541
1231
|
*/
|
|
542
1232
|
async shutdown(): Promise<void> {
|
|
543
1233
|
this.stopHealthChecks();
|
|
1234
|
+
this.drainCooldownSet.clear();
|
|
1235
|
+
this.drainCooldownExpiry.clear();
|
|
544
1236
|
const closing: Promise<void>[] = [];
|
|
545
1237
|
for (const [id, entry] of this.entries.entries()) {
|
|
546
1238
|
closing.push(
|
|
@@ -549,20 +1241,38 @@ export class ConnectionRegistry {
|
|
|
549
1241
|
}),
|
|
550
1242
|
);
|
|
551
1243
|
}
|
|
1244
|
+
for (const [key, entry] of this.orgEntries.entries()) {
|
|
1245
|
+
closing.push(
|
|
1246
|
+
entry.conn.close().catch((err) => {
|
|
1247
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), orgPoolKey: key }, "Failed to close org pool during shutdown");
|
|
1248
|
+
}),
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
552
1251
|
await Promise.all(closing);
|
|
553
1252
|
this.entries.clear();
|
|
1253
|
+
this.orgEntries.clear();
|
|
1254
|
+
this.orgAccessSeq.clear();
|
|
554
1255
|
_resetWhitelists();
|
|
555
1256
|
}
|
|
556
1257
|
|
|
557
|
-
/** Clears all registered connections and resets the table whitelist cache. Used during graceful shutdown, tests, and the benchmark harness. */
|
|
1258
|
+
/** Clears all registered connections (base + org) and resets the table whitelist cache. Used during graceful shutdown, tests, and the benchmark harness. */
|
|
558
1259
|
_reset(): void {
|
|
559
1260
|
this.stopHealthChecks();
|
|
1261
|
+
this.drainCooldownSet.clear();
|
|
1262
|
+
this.drainCooldownExpiry.clear();
|
|
560
1263
|
for (const [id, entry] of this.entries.entries()) {
|
|
561
1264
|
entry.conn.close().catch((err) => {
|
|
562
1265
|
log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: id }, "Failed to close connection during registry reset");
|
|
563
1266
|
});
|
|
564
1267
|
}
|
|
1268
|
+
for (const [key, entry] of this.orgEntries.entries()) {
|
|
1269
|
+
entry.conn.close().catch((err) => {
|
|
1270
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), orgPoolKey: key }, "Failed to close org pool during registry reset");
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
565
1273
|
this.entries.clear();
|
|
1274
|
+
this.orgEntries.clear();
|
|
1275
|
+
this.orgAccessSeq.clear();
|
|
566
1276
|
_resetWhitelists();
|
|
567
1277
|
}
|
|
568
1278
|
}
|
|
@@ -573,3 +1283,47 @@ export const connections = new ConnectionRegistry();
|
|
|
573
1283
|
export function getDB(): DBConnection {
|
|
574
1284
|
return connections.getDefault();
|
|
575
1285
|
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Resolve a region-aware connection for a workspace.
|
|
1289
|
+
*
|
|
1290
|
+
* If the workspace has a region assigned and residency is configured,
|
|
1291
|
+
* registers (or reuses) a region-specific analytics datasource and returns
|
|
1292
|
+
* the org-scoped pool for that region. Falls back to the default connection
|
|
1293
|
+
* if the ee module is unavailable, residency is not configured, or the
|
|
1294
|
+
* workspace has no region.
|
|
1295
|
+
*
|
|
1296
|
+
* Note: this routes the analytics datasource only. Internal database routing
|
|
1297
|
+
* (conversations, audit logs) is not yet implemented.
|
|
1298
|
+
*/
|
|
1299
|
+
export async function getRegionAwareConnection(
|
|
1300
|
+
orgId: string,
|
|
1301
|
+
connectionId: string = "default",
|
|
1302
|
+
): Promise<DBConnection> {
|
|
1303
|
+
let resolveRegionDatabaseUrl: Awaited<typeof import("@atlas/ee/platform/residency")>["resolveRegionDatabaseUrl"];
|
|
1304
|
+
try {
|
|
1305
|
+
({ resolveRegionDatabaseUrl } = await import("@atlas/ee/platform/residency"));
|
|
1306
|
+
} catch (err) {
|
|
1307
|
+
// ee module not installed — non-enterprise deployment, use default
|
|
1308
|
+
if (err instanceof Error && "code" in err && (err as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND") {
|
|
1309
|
+
return connections.getForOrg(orgId, connectionId);
|
|
1310
|
+
}
|
|
1311
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), orgId }, "Failed to load residency module");
|
|
1312
|
+
return connections.getForOrg(orgId, connectionId);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
const regionInfo = await resolveRegionDatabaseUrl(orgId);
|
|
1316
|
+
if (regionInfo?.datasourceUrl) {
|
|
1317
|
+
const regionConnId = `region:${regionInfo.region}`;
|
|
1318
|
+
if (!connections.has(regionConnId)) {
|
|
1319
|
+
connections.register(regionConnId, {
|
|
1320
|
+
url: regionInfo.datasourceUrl,
|
|
1321
|
+
description: `Region ${regionInfo.region} datasource`,
|
|
1322
|
+
});
|
|
1323
|
+
log.info({ connectionId: regionConnId, region: regionInfo.region }, "Registered region datasource");
|
|
1324
|
+
}
|
|
1325
|
+
return connections.getForOrg(orgId, regionConnId);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
return connections.getForOrg(orgId, connectionId);
|
|
1329
|
+
}
|