@useatlas/create 0.0.6 → 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.
Files changed (952) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/index.ts +253 -36
  4. package/package.json +4 -4
  5. package/templates/docker/Dockerfile +1 -1
  6. package/templates/docker/Dockerfile.sidecar +1 -1
  7. package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +17 -14
  8. package/templates/docker/bin/__tests__/failure-threshold.test.ts +148 -0
  9. package/templates/docker/bin/__tests__/fatal-error-propagation.test.ts +267 -0
  10. package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +5 -5
  11. package/templates/docker/bin/__tests__/schema-drift.test.ts +39 -0
  12. package/templates/docker/bin/atlas.ts +981 -1819
  13. package/templates/docker/bin/benchmark.ts +14 -16
  14. package/templates/docker/bin/enrich.ts +7 -2
  15. package/templates/docker/brand.css +13 -0
  16. package/templates/docker/data/cybersec-semantic/catalog.yml +222 -0
  17. package/templates/docker/data/cybersec-semantic/entities/alerts.yml +195 -0
  18. package/templates/docker/data/cybersec-semantic/entities/assets.yml +191 -0
  19. package/templates/docker/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
  20. package/templates/docker/data/cybersec-semantic/entities/incidents.yml +219 -0
  21. package/templates/docker/data/cybersec-semantic/entities/organizations.yml +136 -0
  22. package/templates/docker/data/cybersec-semantic/entities/plans.yml +114 -0
  23. package/templates/docker/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
  24. package/templates/docker/data/cybersec-semantic/entities/scan_results.yml +215 -0
  25. package/templates/docker/data/cybersec-semantic/entities/scans.yml +180 -0
  26. package/templates/docker/data/cybersec-semantic/entities/subscriptions.yml +184 -0
  27. package/templates/docker/data/cybersec-semantic/entities/users.yml +140 -0
  28. package/templates/docker/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
  29. package/templates/docker/data/cybersec-semantic/glossary.yml +207 -0
  30. package/templates/docker/data/cybersec-semantic/metrics/business.yml +148 -0
  31. package/templates/docker/data/cybersec-semantic/metrics/compliance.yml +138 -0
  32. package/templates/docker/data/cybersec-semantic/metrics/security.yml +181 -0
  33. package/templates/docker/data/cybersec.sql +8 -8
  34. package/templates/docker/data/demo.sql +3 -0
  35. package/templates/docker/data/ecommerce-semantic/catalog.yml +221 -0
  36. package/templates/docker/data/ecommerce-semantic/entities/categories.yml +91 -0
  37. package/templates/docker/data/ecommerce-semantic/entities/customers.yml +133 -0
  38. package/templates/docker/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
  39. package/templates/docker/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
  40. package/templates/docker/data/ecommerce-semantic/entities/order_items.yml +159 -0
  41. package/templates/docker/data/ecommerce-semantic/entities/orders.yml +199 -0
  42. package/templates/docker/data/ecommerce-semantic/entities/payments.yml +140 -0
  43. package/templates/docker/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
  44. package/templates/docker/data/ecommerce-semantic/entities/products.yml +178 -0
  45. package/templates/docker/data/ecommerce-semantic/entities/promotions.yml +171 -0
  46. package/templates/docker/data/ecommerce-semantic/entities/returns.yml +144 -0
  47. package/templates/docker/data/ecommerce-semantic/entities/sellers.yml +124 -0
  48. package/templates/docker/data/ecommerce-semantic/entities/shipments.yml +159 -0
  49. package/templates/docker/data/ecommerce-semantic/glossary.yml +193 -0
  50. package/templates/docker/data/ecommerce-semantic/metrics/customers.yml +116 -0
  51. package/templates/docker/data/ecommerce-semantic/metrics/operations.yml +131 -0
  52. package/templates/docker/data/ecommerce-semantic/metrics/revenue.yml +120 -0
  53. package/templates/docker/docs/deploy.md +2 -1
  54. package/templates/docker/ee/src/__mocks__/internal.ts +170 -0
  55. package/templates/docker/ee/src/audit/purge-scheduler.ts +113 -0
  56. package/templates/docker/ee/src/audit/retention.ts +467 -0
  57. package/templates/docker/ee/src/auth/ip-allowlist.ts +367 -0
  58. package/templates/docker/ee/src/auth/roles.ts +562 -0
  59. package/templates/docker/ee/src/auth/scim.ts +343 -0
  60. package/templates/docker/ee/src/auth/sso.ts +538 -0
  61. package/templates/docker/ee/src/backups/engine.ts +355 -0
  62. package/templates/docker/ee/src/backups/index.ts +26 -0
  63. package/templates/docker/ee/src/backups/restore.ts +169 -0
  64. package/templates/docker/ee/src/backups/scheduler.ts +153 -0
  65. package/templates/docker/ee/src/backups/verify.ts +124 -0
  66. package/templates/docker/ee/src/branding/white-label.ts +228 -0
  67. package/templates/docker/ee/src/compliance/masking.ts +477 -0
  68. package/templates/docker/ee/src/compliance/patterns.ts +16 -0
  69. package/templates/docker/ee/src/compliance/pii-detection.ts +217 -0
  70. package/templates/docker/ee/src/compliance/reports.ts +402 -0
  71. package/templates/docker/ee/src/deploy-mode.ts +37 -0
  72. package/templates/docker/ee/src/governance/approval.ts +699 -0
  73. package/templates/docker/ee/src/index.ts +74 -0
  74. package/templates/docker/ee/src/platform/domains.ts +562 -0
  75. package/templates/docker/ee/src/platform/model-routing.ts +382 -0
  76. package/templates/docker/ee/src/platform/residency.ts +265 -0
  77. package/templates/docker/ee/src/sla/alerting.ts +382 -0
  78. package/templates/docker/ee/src/sla/index.ts +12 -0
  79. package/templates/docker/ee/src/sla/metrics.ts +275 -0
  80. package/templates/docker/ee/src/test-setup.ts +1 -0
  81. package/templates/docker/next.config.ts +4 -1
  82. package/templates/docker/package.json +49 -29
  83. package/templates/docker/sidecar/Dockerfile +1 -1
  84. package/templates/docker/src/api/index.ts +336 -24
  85. package/templates/docker/src/api/routes/actions.ts +443 -176
  86. package/templates/docker/src/api/routes/admin-abuse.ts +219 -0
  87. package/templates/docker/src/api/routes/admin-approval.ts +418 -0
  88. package/templates/docker/src/api/routes/admin-audit-retention.ts +405 -0
  89. package/templates/docker/src/api/routes/admin-auth.ts +122 -0
  90. package/templates/docker/src/api/routes/admin-branding.ts +252 -0
  91. package/templates/docker/src/api/routes/admin-compliance.ts +352 -0
  92. package/templates/docker/src/api/routes/admin-domains.ts +334 -0
  93. package/templates/docker/src/api/routes/admin-integrations.ts +2667 -0
  94. package/templates/docker/src/api/routes/admin-ip-allowlist.ts +261 -0
  95. package/templates/docker/src/api/routes/admin-learned-patterns.ts +525 -0
  96. package/templates/docker/src/api/routes/admin-model-config.ts +252 -0
  97. package/templates/docker/src/api/routes/admin-onboarding-emails.ts +145 -0
  98. package/templates/docker/src/api/routes/admin-orgs.ts +710 -0
  99. package/templates/docker/src/api/routes/admin-prompts.ts +694 -0
  100. package/templates/docker/src/api/routes/admin-residency.ts +570 -0
  101. package/templates/docker/src/api/routes/admin-roles.ts +296 -0
  102. package/templates/docker/src/api/routes/admin-router.ts +120 -0
  103. package/templates/docker/src/api/routes/admin-sandbox.ts +417 -0
  104. package/templates/docker/src/api/routes/admin-scim.ts +262 -0
  105. package/templates/docker/src/api/routes/admin-sso.ts +545 -0
  106. package/templates/docker/src/api/routes/admin-suggestions.ts +176 -0
  107. package/templates/docker/src/api/routes/admin-usage.ts +310 -0
  108. package/templates/docker/src/api/routes/admin.ts +4156 -898
  109. package/templates/docker/src/api/routes/auth-preamble.ts +105 -0
  110. package/templates/docker/src/api/routes/billing.ts +397 -0
  111. package/templates/docker/src/api/routes/chat.ts +597 -334
  112. package/templates/docker/src/api/routes/conversations.ts +987 -132
  113. package/templates/docker/src/api/routes/demo.ts +673 -0
  114. package/templates/docker/src/api/routes/discord.ts +274 -0
  115. package/templates/docker/src/api/routes/ee-error-handler.ts +32 -0
  116. package/templates/docker/src/api/routes/health.ts +129 -14
  117. package/templates/docker/src/api/routes/middleware.ts +244 -0
  118. package/templates/docker/src/api/routes/onboarding-emails.ts +134 -0
  119. package/templates/docker/src/api/routes/onboarding.ts +1109 -0
  120. package/templates/docker/src/api/routes/openapi.ts +184 -1597
  121. package/templates/docker/src/api/routes/platform-admin.ts +760 -0
  122. package/templates/docker/src/api/routes/platform-backups.ts +436 -0
  123. package/templates/docker/src/api/routes/platform-domains.ts +235 -0
  124. package/templates/docker/src/api/routes/platform-residency.ts +257 -0
  125. package/templates/docker/src/api/routes/platform-sla.ts +379 -0
  126. package/templates/docker/src/api/routes/prompts.ts +221 -0
  127. package/templates/docker/src/api/routes/public-branding.ts +106 -0
  128. package/templates/docker/src/api/routes/query.ts +330 -219
  129. package/templates/docker/src/api/routes/scheduled-tasks.ts +393 -297
  130. package/templates/docker/src/api/routes/semantic.ts +179 -0
  131. package/templates/docker/src/api/routes/sessions.ts +210 -0
  132. package/templates/docker/src/api/routes/shared-domains.ts +98 -0
  133. package/templates/docker/src/api/routes/shared-schemas.ts +139 -0
  134. package/templates/docker/src/api/routes/slack.ts +209 -52
  135. package/templates/docker/src/api/routes/suggestions.ts +233 -0
  136. package/templates/docker/src/api/routes/tables.ts +67 -0
  137. package/templates/docker/src/api/routes/teams.ts +222 -0
  138. package/templates/docker/src/api/routes/validate-sql.ts +188 -0
  139. package/templates/docker/src/api/routes/validation-hook.ts +62 -0
  140. package/templates/docker/src/api/routes/widget-loader.ts +356 -0
  141. package/templates/docker/src/api/routes/widget.ts +428 -0
  142. package/templates/docker/src/api/routes/wizard.ts +852 -0
  143. package/templates/docker/src/api/server.ts +187 -69
  144. package/templates/docker/src/app/error.tsx +5 -2
  145. package/templates/docker/src/app/globals.css +1 -1
  146. package/templates/docker/src/app/layout.tsx +7 -2
  147. package/templates/docker/src/app/page.tsx +39 -5
  148. package/templates/docker/src/components/data-table/data-table-column-header.tsx +99 -0
  149. package/templates/docker/src/components/data-table/data-table-date-filter.tsx +225 -0
  150. package/templates/docker/src/components/data-table/data-table-expandable.tsx +125 -0
  151. package/templates/docker/src/components/data-table/data-table-faceted-filter.tsx +189 -0
  152. package/templates/docker/src/components/data-table/data-table-pagination.tsx +112 -0
  153. package/templates/docker/src/components/data-table/data-table-range-filter.tsx +122 -0
  154. package/templates/docker/src/components/data-table/data-table-slider-filter.tsx +256 -0
  155. package/templates/docker/src/components/data-table/data-table-sort-list.tsx +407 -0
  156. package/templates/docker/src/components/data-table/data-table-toolbar.tsx +149 -0
  157. package/templates/docker/src/components/data-table/data-table-view-options.tsx +89 -0
  158. package/templates/docker/src/components/data-table/data-table.tsx +105 -0
  159. package/templates/docker/src/components/form-dialog.tsx +135 -0
  160. package/templates/docker/src/components/ui/accordion.tsx +66 -0
  161. package/templates/docker/src/components/ui/calendar.tsx +220 -0
  162. package/templates/docker/src/components/ui/checkbox.tsx +32 -0
  163. package/templates/docker/src/components/ui/faceted.tsx +283 -0
  164. package/templates/docker/src/components/ui/form.tsx +167 -0
  165. package/templates/docker/src/components/ui/label.tsx +24 -0
  166. package/templates/docker/src/components/ui/popover.tsx +89 -0
  167. package/templates/docker/src/components/ui/progress.tsx +31 -0
  168. package/templates/docker/src/components/ui/scroll-area.tsx +6 -2
  169. package/templates/docker/src/components/ui/slider.tsx +63 -0
  170. package/templates/docker/src/components/ui/sortable.tsx +581 -0
  171. package/templates/docker/src/components/ui/switch.tsx +35 -0
  172. package/templates/docker/src/components/ui/textarea.tsx +18 -0
  173. package/templates/docker/src/config/data-table.ts +82 -0
  174. package/templates/docker/src/env-check.ts +74 -0
  175. package/templates/docker/src/hooks/use-callback-ref.ts +27 -0
  176. package/templates/docker/src/hooks/use-data-table.ts +316 -0
  177. package/templates/docker/src/hooks/use-debounced-callback.ts +28 -0
  178. package/templates/docker/src/lib/action-types.ts +7 -41
  179. package/templates/docker/src/lib/agent-query.ts +4 -2
  180. package/templates/docker/src/lib/agent.ts +363 -31
  181. package/templates/docker/src/lib/auth/admin-permissions.ts +38 -0
  182. package/templates/docker/src/lib/auth/audit.ts +19 -4
  183. package/templates/docker/src/lib/auth/byot.ts +3 -3
  184. package/templates/docker/src/lib/auth/client.ts +33 -3
  185. package/templates/docker/src/lib/auth/detect.ts +29 -8
  186. package/templates/docker/src/lib/auth/managed.ts +104 -14
  187. package/templates/docker/src/lib/auth/middleware.ts +53 -6
  188. package/templates/docker/src/lib/auth/migrate.ts +140 -15
  189. package/templates/docker/src/lib/auth/oauth-state.ts +123 -0
  190. package/templates/docker/src/lib/auth/org-permissions.ts +55 -0
  191. package/templates/docker/src/lib/auth/permissions.ts +26 -19
  192. package/templates/docker/src/lib/auth/server.ts +355 -9
  193. package/templates/docker/src/lib/auth/simple-key.ts +3 -3
  194. package/templates/docker/src/lib/auth/types.ts +15 -21
  195. package/templates/docker/src/lib/billing/enforcement.ts +368 -0
  196. package/templates/docker/src/lib/billing/plans.ts +155 -0
  197. package/templates/docker/src/lib/cache/index.ts +92 -0
  198. package/templates/docker/src/lib/cache/keys.ts +30 -0
  199. package/templates/docker/src/lib/cache/lru.ts +79 -0
  200. package/templates/docker/src/lib/cache/types.ts +31 -0
  201. package/templates/docker/src/lib/compose-refs.ts +62 -0
  202. package/templates/docker/src/lib/config.ts +563 -11
  203. package/templates/docker/src/lib/connection-types.ts +9 -0
  204. package/templates/docker/src/lib/conversation-types.ts +1 -25
  205. package/templates/docker/src/lib/conversations.ts +345 -14
  206. package/templates/docker/src/lib/data-table.ts +61 -0
  207. package/templates/docker/src/lib/db/connection.ts +793 -39
  208. package/templates/docker/src/lib/db/internal.ts +985 -139
  209. package/templates/docker/src/lib/db/migrate.ts +295 -0
  210. package/templates/docker/src/lib/db/migrations/0000_baseline.sql +703 -0
  211. package/templates/docker/src/lib/db/migrations/0001_teams_installations.sql +14 -0
  212. package/templates/docker/src/lib/db/migrations/0002_discord_installations.sql +14 -0
  213. package/templates/docker/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
  214. package/templates/docker/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
  215. package/templates/docker/src/lib/db/migrations/0005_oauth_state.sql +16 -0
  216. package/templates/docker/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
  217. package/templates/docker/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
  218. package/templates/docker/src/lib/db/migrations/0008_github_installations.sql +14 -0
  219. package/templates/docker/src/lib/db/migrations/0009_linear_installations.sql +15 -0
  220. package/templates/docker/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
  221. package/templates/docker/src/lib/db/migrations/0011_email_installations.sql +16 -0
  222. package/templates/docker/src/lib/db/migrations/0012_region_migrations.sql +25 -0
  223. package/templates/docker/src/lib/db/schema.ts +1120 -0
  224. package/templates/docker/src/lib/db/source-rate-limit.ts +89 -139
  225. package/templates/docker/src/lib/demo.ts +308 -0
  226. package/templates/docker/src/lib/discord/store.ts +225 -0
  227. package/templates/docker/src/lib/effect/ai.ts +243 -0
  228. package/templates/docker/src/lib/effect/errors.ts +234 -0
  229. package/templates/docker/src/lib/effect/hono.ts +454 -0
  230. package/templates/docker/src/lib/effect/index.ts +137 -0
  231. package/templates/docker/src/lib/effect/layers.ts +496 -0
  232. package/templates/docker/src/lib/effect/services.ts +776 -0
  233. package/templates/docker/src/lib/effect/sql.ts +178 -0
  234. package/templates/docker/src/lib/effect/toolkit.ts +123 -0
  235. package/templates/docker/src/lib/email/delivery.ts +232 -0
  236. package/templates/docker/src/lib/email/engine.ts +349 -0
  237. package/templates/docker/src/lib/email/hooks.ts +107 -0
  238. package/templates/docker/src/lib/email/index.ts +16 -0
  239. package/templates/docker/src/lib/email/scheduler.ts +72 -0
  240. package/templates/docker/src/lib/email/sequence.ts +73 -0
  241. package/templates/docker/src/lib/email/store.ts +163 -0
  242. package/templates/docker/src/lib/email/templates.ts +215 -0
  243. package/templates/docker/src/lib/format.ts +67 -0
  244. package/templates/docker/src/lib/gchat/store.ts +202 -0
  245. package/templates/docker/src/lib/github/store.ts +197 -0
  246. package/templates/docker/src/lib/id.ts +29 -0
  247. package/templates/docker/src/lib/integrations/types.ts +166 -0
  248. package/templates/docker/src/lib/learn/pattern-analyzer.ts +224 -0
  249. package/templates/docker/src/lib/learn/pattern-cache.ts +229 -0
  250. package/templates/docker/src/lib/learn/pattern-proposer.ts +87 -0
  251. package/templates/docker/src/lib/learn/suggestion-helpers.ts +34 -0
  252. package/templates/docker/src/lib/learn/suggestions.ts +139 -0
  253. package/templates/docker/src/lib/linear/store.ts +200 -0
  254. package/templates/docker/src/lib/logger.ts +35 -3
  255. package/templates/docker/src/lib/metering.ts +272 -0
  256. package/templates/docker/src/lib/parsers.ts +99 -0
  257. package/templates/docker/src/lib/plugins/hooks.ts +13 -11
  258. package/templates/docker/src/lib/plugins/index.ts +3 -1
  259. package/templates/docker/src/lib/plugins/registry.ts +58 -6
  260. package/templates/docker/src/lib/plugins/settings.ts +147 -0
  261. package/templates/docker/src/lib/plugins/wiring.ts +6 -9
  262. package/templates/docker/src/lib/profiler.ts +1665 -0
  263. package/templates/docker/src/lib/providers.ts +188 -13
  264. package/templates/docker/src/lib/rls.ts +172 -60
  265. package/templates/docker/src/lib/sandbox/credentials.ts +206 -0
  266. package/templates/docker/src/lib/sandbox/validate.ts +179 -0
  267. package/templates/docker/src/lib/scheduled-task-types.ts +26 -94
  268. package/templates/docker/src/lib/scheduled-tasks.ts +174 -34
  269. package/templates/docker/src/lib/scheduler/delivery.ts +248 -150
  270. package/templates/docker/src/lib/scheduler/engine.ts +190 -154
  271. package/templates/docker/src/lib/scheduler/executor.ts +74 -23
  272. package/templates/docker/src/lib/scheduler/preview.ts +72 -0
  273. package/templates/docker/src/lib/security/abuse.ts +463 -0
  274. package/templates/docker/src/lib/semantic/diff.ts +267 -0
  275. package/templates/docker/src/lib/semantic/entities.ts +167 -0
  276. package/templates/docker/src/lib/semantic/files.ts +283 -0
  277. package/templates/docker/src/lib/semantic/index.ts +27 -0
  278. package/templates/docker/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
  279. package/templates/docker/src/lib/semantic/sync.ts +581 -0
  280. package/templates/docker/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
  281. package/templates/docker/src/lib/settings.ts +817 -0
  282. package/templates/docker/src/lib/sidecar-types.ts +13 -0
  283. package/templates/docker/src/lib/slack/store.ts +134 -25
  284. package/templates/docker/src/lib/startup.ts +528 -362
  285. package/templates/docker/src/lib/teams/store.ts +216 -0
  286. package/templates/docker/src/lib/telegram/store.ts +202 -0
  287. package/templates/docker/src/lib/telemetry.ts +40 -0
  288. package/templates/docker/src/lib/tools/actions/audit.ts +8 -5
  289. package/templates/docker/src/lib/tools/actions/email.ts +3 -1
  290. package/templates/docker/src/lib/tools/actions/handler.ts +276 -93
  291. package/templates/docker/src/lib/tools/actions/jira.ts +2 -2
  292. package/templates/docker/src/lib/tools/backends/detect.ts +16 -0
  293. package/templates/docker/src/lib/tools/backends/index.ts +11 -0
  294. package/templates/docker/src/lib/tools/backends/nsjail.ts +213 -0
  295. package/templates/docker/src/lib/tools/backends/shared.ts +103 -0
  296. package/templates/docker/src/lib/tools/backends/types.ts +26 -0
  297. package/templates/docker/src/lib/tools/explore-nsjail.ts +7 -228
  298. package/templates/docker/src/lib/tools/explore-sandbox.ts +4 -29
  299. package/templates/docker/src/lib/tools/explore-sidecar.ts +18 -2
  300. package/templates/docker/src/lib/tools/explore.ts +246 -54
  301. package/templates/docker/src/lib/tools/index.ts +17 -0
  302. package/templates/docker/src/lib/tools/python-nsjail.ts +11 -139
  303. package/templates/docker/src/lib/tools/python-sandbox.ts +9 -132
  304. package/templates/docker/src/lib/tools/python-sidecar.ts +184 -3
  305. package/templates/docker/src/lib/tools/python-stream.ts +33 -0
  306. package/templates/docker/src/lib/tools/python-wrapper.ts +129 -0
  307. package/templates/docker/src/lib/tools/python.ts +115 -15
  308. package/templates/docker/src/lib/tools/registry.ts +14 -2
  309. package/templates/docker/src/lib/tools/sql.ts +778 -362
  310. package/templates/docker/src/lib/tracing.ts +16 -0
  311. package/templates/docker/src/lib/whatsapp/store.ts +198 -0
  312. package/templates/docker/src/lib/workspace.ts +89 -0
  313. package/templates/docker/src/progress.ts +121 -0
  314. package/templates/docker/src/types/data-table.ts +48 -0
  315. package/templates/docker/src/ui/atlas-chat-reexport.ts +3 -0
  316. package/templates/docker/src/ui/components/actions/action-approval-card.tsx +26 -19
  317. package/templates/docker/src/ui/components/actions/action-status-badge.tsx +3 -3
  318. package/templates/docker/src/ui/components/admin/admin-layout.tsx +57 -39
  319. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +213 -35
  320. package/templates/docker/src/ui/components/admin/delivery-status-badge.tsx +53 -0
  321. package/templates/docker/src/ui/components/admin/empty-state.tsx +27 -6
  322. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -52
  323. package/templates/docker/src/ui/components/admin/error-banner.tsx +2 -2
  324. package/templates/docker/src/ui/components/admin/feature-disabled.tsx +28 -5
  325. package/templates/docker/src/ui/components/admin-content-wrapper.tsx +87 -0
  326. package/templates/docker/src/ui/components/atlas-chat.tsx +449 -166
  327. package/templates/docker/src/ui/components/branding-head.tsx +41 -0
  328. package/templates/docker/src/ui/components/chart/chart-detection.ts +62 -5
  329. package/templates/docker/src/ui/components/chart/result-chart.tsx +316 -125
  330. package/templates/docker/src/ui/components/chat/api-key-bar.tsx +4 -4
  331. package/templates/docker/src/ui/components/chat/data-table.tsx +45 -4
  332. package/templates/docker/src/ui/components/chat/error-banner.tsx +86 -5
  333. package/templates/docker/src/ui/components/chat/follow-up-chips.tsx +29 -0
  334. package/templates/docker/src/ui/components/chat/markdown.tsx +24 -0
  335. package/templates/docker/src/ui/components/chat/prompt-library.tsx +206 -0
  336. package/templates/docker/src/ui/components/chat/python-result-card.tsx +106 -78
  337. package/templates/docker/src/ui/components/chat/result-card-base.tsx +101 -0
  338. package/templates/docker/src/ui/components/chat/share-dialog.tsx +377 -0
  339. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +94 -73
  340. package/templates/docker/src/ui/components/chat/suggestion-chips.tsx +46 -0
  341. package/templates/docker/src/ui/components/chat/tool-part.tsx +16 -4
  342. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +48 -17
  343. package/templates/docker/src/ui/components/conversations/conversation-list.tsx +38 -24
  344. package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
  345. package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +9 -2
  346. package/templates/docker/src/ui/components/error-boundary.tsx +66 -0
  347. package/templates/docker/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
  348. package/templates/docker/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
  349. package/templates/docker/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
  350. package/templates/docker/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
  351. package/templates/docker/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
  352. package/templates/docker/src/ui/components/notebook/notebook-cell.tsx +119 -0
  353. package/templates/docker/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
  354. package/templates/docker/src/ui/components/notebook/notebook-export.ts +287 -0
  355. package/templates/docker/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
  356. package/templates/docker/src/ui/components/notebook/notebook-shell.tsx +266 -0
  357. package/templates/docker/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
  358. package/templates/docker/src/ui/components/notebook/types.ts +39 -0
  359. package/templates/docker/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
  360. package/templates/docker/src/ui/components/notebook/use-notebook.ts +684 -0
  361. package/templates/docker/src/ui/components/org-switcher.tsx +111 -0
  362. package/templates/docker/src/ui/components/region-picker.tsx +103 -0
  363. package/templates/docker/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
  364. package/templates/docker/src/ui/components/social-icons.tsx +26 -0
  365. package/templates/docker/src/ui/components/tour/guided-tour.tsx +81 -0
  366. package/templates/docker/src/ui/components/tour/index.ts +5 -0
  367. package/templates/docker/src/ui/components/tour/nav-bar.tsx +100 -0
  368. package/templates/docker/src/ui/components/tour/tour-overlay.tsx +298 -0
  369. package/templates/docker/src/ui/components/tour/tour-steps.ts +43 -0
  370. package/templates/docker/src/ui/components/tour/types.ts +21 -0
  371. package/templates/docker/src/ui/components/tour/use-tour.ts +193 -0
  372. package/templates/docker/src/ui/context-reexport.ts +3 -0
  373. package/templates/docker/src/ui/hooks/theme-init-script.ts +17 -0
  374. package/templates/docker/src/ui/hooks/use-admin-fetch.ts +38 -30
  375. package/templates/docker/src/ui/hooks/use-admin-mutation.ts +188 -0
  376. package/templates/docker/src/ui/hooks/use-atlas-transport.ts +225 -0
  377. package/templates/docker/src/ui/hooks/use-branding.ts +68 -0
  378. package/templates/docker/src/ui/hooks/use-conversations.ts +106 -83
  379. package/templates/docker/src/ui/hooks/use-dark-mode.ts +134 -10
  380. package/templates/docker/src/ui/hooks/use-deploy-mode.ts +36 -0
  381. package/templates/docker/src/ui/hooks/use-platform-admin-guard.ts +49 -0
  382. package/templates/docker/src/ui/lib/action-types.ts +11 -63
  383. package/templates/docker/src/ui/lib/admin-schemas.ts +744 -0
  384. package/templates/docker/src/ui/lib/fetch-client.ts +84 -0
  385. package/templates/docker/src/ui/lib/fetch-error.ts +54 -0
  386. package/templates/docker/src/ui/lib/helpers.ts +94 -1
  387. package/templates/docker/src/ui/lib/types.ts +149 -140
  388. package/templates/docker/tsconfig.json +4 -2
  389. package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +17 -14
  390. package/templates/nextjs-standalone/bin/__tests__/failure-threshold.test.ts +148 -0
  391. package/templates/nextjs-standalone/bin/__tests__/fatal-error-propagation.test.ts +267 -0
  392. package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +5 -5
  393. package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +39 -0
  394. package/templates/nextjs-standalone/bin/atlas.ts +981 -1819
  395. package/templates/nextjs-standalone/bin/benchmark.ts +14 -16
  396. package/templates/nextjs-standalone/bin/enrich.ts +7 -2
  397. package/templates/nextjs-standalone/brand.css +13 -0
  398. package/templates/nextjs-standalone/data/cybersec-semantic/catalog.yml +222 -0
  399. package/templates/nextjs-standalone/data/cybersec-semantic/entities/alerts.yml +195 -0
  400. package/templates/nextjs-standalone/data/cybersec-semantic/entities/assets.yml +191 -0
  401. package/templates/nextjs-standalone/data/cybersec-semantic/entities/compliance_assessments.yml +170 -0
  402. package/templates/nextjs-standalone/data/cybersec-semantic/entities/incidents.yml +219 -0
  403. package/templates/nextjs-standalone/data/cybersec-semantic/entities/organizations.yml +136 -0
  404. package/templates/nextjs-standalone/data/cybersec-semantic/entities/plans.yml +114 -0
  405. package/templates/nextjs-standalone/data/cybersec-semantic/entities/remediation_actions.yml +212 -0
  406. package/templates/nextjs-standalone/data/cybersec-semantic/entities/scan_results.yml +215 -0
  407. package/templates/nextjs-standalone/data/cybersec-semantic/entities/scans.yml +180 -0
  408. package/templates/nextjs-standalone/data/cybersec-semantic/entities/subscriptions.yml +184 -0
  409. package/templates/nextjs-standalone/data/cybersec-semantic/entities/users.yml +140 -0
  410. package/templates/nextjs-standalone/data/cybersec-semantic/entities/vulnerabilities.yml +154 -0
  411. package/templates/nextjs-standalone/data/cybersec-semantic/glossary.yml +207 -0
  412. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/business.yml +148 -0
  413. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/compliance.yml +138 -0
  414. package/templates/nextjs-standalone/data/cybersec-semantic/metrics/security.yml +181 -0
  415. package/templates/nextjs-standalone/data/cybersec.sql +8 -8
  416. package/templates/nextjs-standalone/data/demo.sql +3 -0
  417. package/templates/nextjs-standalone/data/ecommerce-semantic/catalog.yml +221 -0
  418. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/categories.yml +91 -0
  419. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/customers.yml +133 -0
  420. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/email_campaigns.yml +119 -0
  421. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/inventory_levels.yml +153 -0
  422. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/order_items.yml +159 -0
  423. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/orders.yml +199 -0
  424. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/payments.yml +140 -0
  425. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/product_reviews.yml +155 -0
  426. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/products.yml +178 -0
  427. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/promotions.yml +171 -0
  428. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/returns.yml +144 -0
  429. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/sellers.yml +124 -0
  430. package/templates/nextjs-standalone/data/ecommerce-semantic/entities/shipments.yml +159 -0
  431. package/templates/nextjs-standalone/data/ecommerce-semantic/glossary.yml +193 -0
  432. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/customers.yml +116 -0
  433. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/operations.yml +131 -0
  434. package/templates/nextjs-standalone/data/ecommerce-semantic/metrics/revenue.yml +120 -0
  435. package/templates/nextjs-standalone/docs/deploy.md +2 -1
  436. package/templates/nextjs-standalone/ee/src/__mocks__/internal.ts +170 -0
  437. package/templates/nextjs-standalone/ee/src/audit/purge-scheduler.ts +113 -0
  438. package/templates/nextjs-standalone/ee/src/audit/retention.ts +467 -0
  439. package/templates/nextjs-standalone/ee/src/auth/ip-allowlist.ts +367 -0
  440. package/templates/nextjs-standalone/ee/src/auth/roles.ts +562 -0
  441. package/templates/nextjs-standalone/ee/src/auth/scim.ts +343 -0
  442. package/templates/nextjs-standalone/ee/src/auth/sso.ts +538 -0
  443. package/templates/nextjs-standalone/ee/src/backups/engine.ts +355 -0
  444. package/templates/nextjs-standalone/ee/src/backups/index.ts +26 -0
  445. package/templates/nextjs-standalone/ee/src/backups/restore.ts +169 -0
  446. package/templates/nextjs-standalone/ee/src/backups/scheduler.ts +153 -0
  447. package/templates/nextjs-standalone/ee/src/backups/verify.ts +124 -0
  448. package/templates/nextjs-standalone/ee/src/branding/white-label.ts +228 -0
  449. package/templates/nextjs-standalone/ee/src/compliance/masking.ts +477 -0
  450. package/templates/nextjs-standalone/ee/src/compliance/patterns.ts +16 -0
  451. package/templates/nextjs-standalone/ee/src/compliance/pii-detection.ts +217 -0
  452. package/templates/nextjs-standalone/ee/src/compliance/reports.ts +402 -0
  453. package/templates/nextjs-standalone/ee/src/deploy-mode.ts +37 -0
  454. package/templates/nextjs-standalone/ee/src/governance/approval.ts +699 -0
  455. package/templates/nextjs-standalone/ee/src/index.ts +74 -0
  456. package/templates/nextjs-standalone/ee/src/platform/domains.ts +562 -0
  457. package/templates/nextjs-standalone/ee/src/platform/model-routing.ts +382 -0
  458. package/templates/nextjs-standalone/ee/src/platform/residency.ts +265 -0
  459. package/templates/nextjs-standalone/ee/src/sla/alerting.ts +382 -0
  460. package/templates/nextjs-standalone/ee/src/sla/index.ts +12 -0
  461. package/templates/nextjs-standalone/ee/src/sla/metrics.ts +275 -0
  462. package/templates/nextjs-standalone/ee/src/test-setup.ts +1 -0
  463. package/templates/nextjs-standalone/next.config.ts +1 -1
  464. package/templates/nextjs-standalone/package.json +50 -30
  465. package/templates/nextjs-standalone/src/api/index.ts +336 -24
  466. package/templates/nextjs-standalone/src/api/routes/actions.ts +443 -176
  467. package/templates/nextjs-standalone/src/api/routes/admin-abuse.ts +219 -0
  468. package/templates/nextjs-standalone/src/api/routes/admin-approval.ts +418 -0
  469. package/templates/nextjs-standalone/src/api/routes/admin-audit-retention.ts +405 -0
  470. package/templates/nextjs-standalone/src/api/routes/admin-auth.ts +122 -0
  471. package/templates/nextjs-standalone/src/api/routes/admin-branding.ts +252 -0
  472. package/templates/nextjs-standalone/src/api/routes/admin-compliance.ts +352 -0
  473. package/templates/nextjs-standalone/src/api/routes/admin-domains.ts +334 -0
  474. package/templates/nextjs-standalone/src/api/routes/admin-integrations.ts +2667 -0
  475. package/templates/nextjs-standalone/src/api/routes/admin-ip-allowlist.ts +261 -0
  476. package/templates/nextjs-standalone/src/api/routes/admin-learned-patterns.ts +525 -0
  477. package/templates/nextjs-standalone/src/api/routes/admin-model-config.ts +252 -0
  478. package/templates/nextjs-standalone/src/api/routes/admin-onboarding-emails.ts +145 -0
  479. package/templates/nextjs-standalone/src/api/routes/admin-orgs.ts +710 -0
  480. package/templates/nextjs-standalone/src/api/routes/admin-prompts.ts +694 -0
  481. package/templates/nextjs-standalone/src/api/routes/admin-residency.ts +570 -0
  482. package/templates/nextjs-standalone/src/api/routes/admin-roles.ts +296 -0
  483. package/templates/nextjs-standalone/src/api/routes/admin-router.ts +120 -0
  484. package/templates/nextjs-standalone/src/api/routes/admin-sandbox.ts +417 -0
  485. package/templates/nextjs-standalone/src/api/routes/admin-scim.ts +262 -0
  486. package/templates/nextjs-standalone/src/api/routes/admin-sso.ts +545 -0
  487. package/templates/nextjs-standalone/src/api/routes/admin-suggestions.ts +176 -0
  488. package/templates/nextjs-standalone/src/api/routes/admin-usage.ts +310 -0
  489. package/templates/nextjs-standalone/src/api/routes/admin.ts +4156 -898
  490. package/templates/nextjs-standalone/src/api/routes/auth-preamble.ts +105 -0
  491. package/templates/nextjs-standalone/src/api/routes/billing.ts +397 -0
  492. package/templates/nextjs-standalone/src/api/routes/chat.ts +597 -334
  493. package/templates/nextjs-standalone/src/api/routes/conversations.ts +987 -132
  494. package/templates/nextjs-standalone/src/api/routes/demo.ts +673 -0
  495. package/templates/nextjs-standalone/src/api/routes/discord.ts +274 -0
  496. package/templates/nextjs-standalone/src/api/routes/ee-error-handler.ts +32 -0
  497. package/templates/nextjs-standalone/src/api/routes/health.ts +129 -14
  498. package/templates/nextjs-standalone/src/api/routes/middleware.ts +244 -0
  499. package/templates/nextjs-standalone/src/api/routes/onboarding-emails.ts +134 -0
  500. package/templates/nextjs-standalone/src/api/routes/onboarding.ts +1109 -0
  501. package/templates/nextjs-standalone/src/api/routes/openapi.ts +184 -1597
  502. package/templates/nextjs-standalone/src/api/routes/platform-admin.ts +760 -0
  503. package/templates/nextjs-standalone/src/api/routes/platform-backups.ts +436 -0
  504. package/templates/nextjs-standalone/src/api/routes/platform-domains.ts +235 -0
  505. package/templates/nextjs-standalone/src/api/routes/platform-residency.ts +257 -0
  506. package/templates/nextjs-standalone/src/api/routes/platform-sla.ts +379 -0
  507. package/templates/nextjs-standalone/src/api/routes/prompts.ts +221 -0
  508. package/templates/nextjs-standalone/src/api/routes/public-branding.ts +106 -0
  509. package/templates/nextjs-standalone/src/api/routes/query.ts +330 -219
  510. package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +393 -297
  511. package/templates/nextjs-standalone/src/api/routes/semantic.ts +179 -0
  512. package/templates/nextjs-standalone/src/api/routes/sessions.ts +210 -0
  513. package/templates/nextjs-standalone/src/api/routes/shared-domains.ts +98 -0
  514. package/templates/nextjs-standalone/src/api/routes/shared-schemas.ts +139 -0
  515. package/templates/nextjs-standalone/src/api/routes/slack.ts +209 -52
  516. package/templates/nextjs-standalone/src/api/routes/suggestions.ts +233 -0
  517. package/templates/nextjs-standalone/src/api/routes/tables.ts +67 -0
  518. package/templates/nextjs-standalone/src/api/routes/teams.ts +222 -0
  519. package/templates/nextjs-standalone/src/api/routes/validate-sql.ts +188 -0
  520. package/templates/nextjs-standalone/src/api/routes/validation-hook.ts +62 -0
  521. package/templates/nextjs-standalone/src/api/routes/widget-loader.ts +356 -0
  522. package/templates/nextjs-standalone/src/api/routes/widget.ts +428 -0
  523. package/templates/nextjs-standalone/src/api/routes/wizard.ts +852 -0
  524. package/templates/nextjs-standalone/src/api/server.ts +187 -69
  525. package/templates/nextjs-standalone/src/app/error.tsx +5 -2
  526. package/templates/nextjs-standalone/src/app/globals.css +1 -1
  527. package/templates/nextjs-standalone/src/app/layout.tsx +7 -2
  528. package/templates/nextjs-standalone/src/app/page.tsx +39 -5
  529. package/templates/nextjs-standalone/src/components/data-table/data-table-column-header.tsx +99 -0
  530. package/templates/nextjs-standalone/src/components/data-table/data-table-date-filter.tsx +225 -0
  531. package/templates/nextjs-standalone/src/components/data-table/data-table-expandable.tsx +125 -0
  532. package/templates/nextjs-standalone/src/components/data-table/data-table-faceted-filter.tsx +189 -0
  533. package/templates/nextjs-standalone/src/components/data-table/data-table-pagination.tsx +112 -0
  534. package/templates/nextjs-standalone/src/components/data-table/data-table-range-filter.tsx +122 -0
  535. package/templates/nextjs-standalone/src/components/data-table/data-table-slider-filter.tsx +256 -0
  536. package/templates/nextjs-standalone/src/components/data-table/data-table-sort-list.tsx +407 -0
  537. package/templates/nextjs-standalone/src/components/data-table/data-table-toolbar.tsx +149 -0
  538. package/templates/nextjs-standalone/src/components/data-table/data-table-view-options.tsx +89 -0
  539. package/templates/nextjs-standalone/src/components/data-table/data-table.tsx +105 -0
  540. package/templates/nextjs-standalone/src/components/form-dialog.tsx +135 -0
  541. package/templates/nextjs-standalone/src/components/ui/accordion.tsx +66 -0
  542. package/templates/nextjs-standalone/src/components/ui/calendar.tsx +220 -0
  543. package/templates/nextjs-standalone/src/components/ui/checkbox.tsx +32 -0
  544. package/templates/nextjs-standalone/src/components/ui/faceted.tsx +283 -0
  545. package/templates/nextjs-standalone/src/components/ui/form.tsx +167 -0
  546. package/templates/nextjs-standalone/src/components/ui/label.tsx +24 -0
  547. package/templates/nextjs-standalone/src/components/ui/popover.tsx +89 -0
  548. package/templates/nextjs-standalone/src/components/ui/progress.tsx +31 -0
  549. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +6 -2
  550. package/templates/nextjs-standalone/src/components/ui/slider.tsx +63 -0
  551. package/templates/nextjs-standalone/src/components/ui/sortable.tsx +581 -0
  552. package/templates/nextjs-standalone/src/components/ui/switch.tsx +35 -0
  553. package/templates/nextjs-standalone/src/components/ui/textarea.tsx +18 -0
  554. package/templates/nextjs-standalone/src/config/data-table.ts +82 -0
  555. package/templates/nextjs-standalone/src/env-check.ts +74 -0
  556. package/templates/nextjs-standalone/src/hooks/use-callback-ref.ts +27 -0
  557. package/templates/nextjs-standalone/src/hooks/use-data-table.ts +316 -0
  558. package/templates/nextjs-standalone/src/hooks/use-debounced-callback.ts +28 -0
  559. package/templates/nextjs-standalone/src/lib/action-types.ts +7 -41
  560. package/templates/nextjs-standalone/src/lib/agent-query.ts +4 -2
  561. package/templates/nextjs-standalone/src/lib/agent.ts +363 -31
  562. package/templates/nextjs-standalone/src/lib/api-url.ts +2 -3
  563. package/templates/nextjs-standalone/src/lib/auth/admin-permissions.ts +38 -0
  564. package/templates/nextjs-standalone/src/lib/auth/audit.ts +19 -4
  565. package/templates/nextjs-standalone/src/lib/auth/byot.ts +3 -3
  566. package/templates/nextjs-standalone/src/lib/auth/detect.ts +29 -8
  567. package/templates/nextjs-standalone/src/lib/auth/managed.ts +104 -14
  568. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +53 -6
  569. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +140 -15
  570. package/templates/nextjs-standalone/src/lib/auth/oauth-state.ts +123 -0
  571. package/templates/nextjs-standalone/src/lib/auth/org-permissions.ts +55 -0
  572. package/templates/nextjs-standalone/src/lib/auth/permissions.ts +26 -19
  573. package/templates/nextjs-standalone/src/lib/auth/server.ts +355 -9
  574. package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +3 -3
  575. package/templates/nextjs-standalone/src/lib/auth/types.ts +15 -21
  576. package/templates/nextjs-standalone/src/lib/billing/enforcement.ts +368 -0
  577. package/templates/nextjs-standalone/src/lib/billing/plans.ts +155 -0
  578. package/templates/nextjs-standalone/src/lib/cache/index.ts +92 -0
  579. package/templates/nextjs-standalone/src/lib/cache/keys.ts +30 -0
  580. package/templates/nextjs-standalone/src/lib/cache/lru.ts +79 -0
  581. package/templates/nextjs-standalone/src/lib/cache/types.ts +31 -0
  582. package/templates/nextjs-standalone/src/lib/compose-refs.ts +62 -0
  583. package/templates/nextjs-standalone/src/lib/config.ts +563 -11
  584. package/templates/nextjs-standalone/src/lib/connection-types.ts +9 -0
  585. package/templates/nextjs-standalone/src/lib/conversation-types.ts +1 -25
  586. package/templates/nextjs-standalone/src/lib/conversations.ts +345 -14
  587. package/templates/nextjs-standalone/src/lib/data-table.ts +61 -0
  588. package/templates/nextjs-standalone/src/lib/db/connection.ts +793 -39
  589. package/templates/nextjs-standalone/src/lib/db/internal.ts +985 -139
  590. package/templates/nextjs-standalone/src/lib/db/migrate.ts +295 -0
  591. package/templates/nextjs-standalone/src/lib/db/migrations/0000_baseline.sql +703 -0
  592. package/templates/nextjs-standalone/src/lib/db/migrations/0001_teams_installations.sql +14 -0
  593. package/templates/nextjs-standalone/src/lib/db/migrations/0002_discord_installations.sql +14 -0
  594. package/templates/nextjs-standalone/src/lib/db/migrations/0003_telegram_installations.sql +15 -0
  595. package/templates/nextjs-standalone/src/lib/db/migrations/0004_sandbox_credentials.sql +18 -0
  596. package/templates/nextjs-standalone/src/lib/db/migrations/0005_oauth_state.sql +16 -0
  597. package/templates/nextjs-standalone/src/lib/db/migrations/0006_byot_credentials.sql +14 -0
  598. package/templates/nextjs-standalone/src/lib/db/migrations/0007_gchat_installations.sql +15 -0
  599. package/templates/nextjs-standalone/src/lib/db/migrations/0008_github_installations.sql +14 -0
  600. package/templates/nextjs-standalone/src/lib/db/migrations/0009_linear_installations.sql +15 -0
  601. package/templates/nextjs-standalone/src/lib/db/migrations/0010_whatsapp_installations.sql +14 -0
  602. package/templates/nextjs-standalone/src/lib/db/migrations/0011_email_installations.sql +16 -0
  603. package/templates/nextjs-standalone/src/lib/db/migrations/0012_region_migrations.sql +25 -0
  604. package/templates/nextjs-standalone/src/lib/db/schema.ts +1120 -0
  605. package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +89 -139
  606. package/templates/nextjs-standalone/src/lib/demo.ts +308 -0
  607. package/templates/nextjs-standalone/src/lib/discord/store.ts +225 -0
  608. package/templates/nextjs-standalone/src/lib/effect/ai.ts +243 -0
  609. package/templates/nextjs-standalone/src/lib/effect/errors.ts +234 -0
  610. package/templates/nextjs-standalone/src/lib/effect/hono.ts +454 -0
  611. package/templates/nextjs-standalone/src/lib/effect/index.ts +137 -0
  612. package/templates/nextjs-standalone/src/lib/effect/layers.ts +496 -0
  613. package/templates/nextjs-standalone/src/lib/effect/services.ts +776 -0
  614. package/templates/nextjs-standalone/src/lib/effect/sql.ts +178 -0
  615. package/templates/nextjs-standalone/src/lib/effect/toolkit.ts +123 -0
  616. package/templates/nextjs-standalone/src/lib/email/delivery.ts +232 -0
  617. package/templates/nextjs-standalone/src/lib/email/engine.ts +349 -0
  618. package/templates/nextjs-standalone/src/lib/email/hooks.ts +107 -0
  619. package/templates/nextjs-standalone/src/lib/email/index.ts +16 -0
  620. package/templates/nextjs-standalone/src/lib/email/scheduler.ts +72 -0
  621. package/templates/nextjs-standalone/src/lib/email/sequence.ts +73 -0
  622. package/templates/nextjs-standalone/src/lib/email/store.ts +163 -0
  623. package/templates/nextjs-standalone/src/lib/email/templates.ts +215 -0
  624. package/templates/nextjs-standalone/src/lib/format.test.ts +117 -0
  625. package/templates/nextjs-standalone/src/lib/format.ts +67 -0
  626. package/templates/nextjs-standalone/src/lib/gchat/store.ts +202 -0
  627. package/templates/nextjs-standalone/src/lib/github/store.ts +197 -0
  628. package/templates/nextjs-standalone/src/lib/id.ts +29 -0
  629. package/templates/nextjs-standalone/src/lib/integrations/types.ts +166 -0
  630. package/templates/nextjs-standalone/src/lib/learn/pattern-analyzer.ts +224 -0
  631. package/templates/nextjs-standalone/src/lib/learn/pattern-cache.ts +229 -0
  632. package/templates/nextjs-standalone/src/lib/learn/pattern-proposer.ts +87 -0
  633. package/templates/nextjs-standalone/src/lib/learn/suggestion-helpers.ts +34 -0
  634. package/templates/nextjs-standalone/src/lib/learn/suggestions.ts +139 -0
  635. package/templates/nextjs-standalone/src/lib/linear/store.ts +200 -0
  636. package/templates/nextjs-standalone/src/lib/logger.ts +35 -3
  637. package/templates/nextjs-standalone/src/lib/metering.ts +272 -0
  638. package/templates/nextjs-standalone/src/lib/parsers.ts +99 -0
  639. package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +13 -11
  640. package/templates/nextjs-standalone/src/lib/plugins/index.ts +3 -1
  641. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +58 -6
  642. package/templates/nextjs-standalone/src/lib/plugins/settings.ts +147 -0
  643. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +6 -9
  644. package/templates/nextjs-standalone/src/lib/profiler.ts +1665 -0
  645. package/templates/nextjs-standalone/src/lib/providers.ts +188 -13
  646. package/templates/nextjs-standalone/src/lib/rls.ts +172 -60
  647. package/templates/nextjs-standalone/src/lib/sandbox/credentials.ts +206 -0
  648. package/templates/nextjs-standalone/src/lib/sandbox/validate.ts +179 -0
  649. package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +26 -94
  650. package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +174 -34
  651. package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -150
  652. package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +190 -154
  653. package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +74 -23
  654. package/templates/nextjs-standalone/src/lib/scheduler/preview.ts +72 -0
  655. package/templates/nextjs-standalone/src/lib/security/abuse.ts +463 -0
  656. package/templates/nextjs-standalone/src/lib/semantic/diff.ts +267 -0
  657. package/templates/nextjs-standalone/src/lib/semantic/entities.ts +167 -0
  658. package/templates/nextjs-standalone/src/lib/semantic/files.ts +283 -0
  659. package/templates/nextjs-standalone/src/lib/semantic/index.ts +27 -0
  660. package/templates/nextjs-standalone/src/lib/{semantic-index.ts → semantic/search.ts} +80 -9
  661. package/templates/nextjs-standalone/src/lib/semantic/sync.ts +581 -0
  662. package/templates/nextjs-standalone/src/lib/{semantic.ts → semantic/whitelist.ts} +189 -3
  663. package/templates/nextjs-standalone/src/lib/settings.ts +817 -0
  664. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +13 -0
  665. package/templates/nextjs-standalone/src/lib/slack/store.ts +134 -25
  666. package/templates/nextjs-standalone/src/lib/startup.ts +528 -362
  667. package/templates/nextjs-standalone/src/lib/teams/store.ts +216 -0
  668. package/templates/nextjs-standalone/src/lib/telegram/store.ts +202 -0
  669. package/templates/nextjs-standalone/src/lib/telemetry.ts +40 -0
  670. package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +8 -5
  671. package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +3 -1
  672. package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +276 -93
  673. package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +2 -2
  674. package/templates/nextjs-standalone/src/lib/tools/backends/detect.ts +16 -0
  675. package/templates/nextjs-standalone/src/lib/tools/backends/index.ts +11 -0
  676. package/templates/nextjs-standalone/src/lib/tools/backends/nsjail.ts +213 -0
  677. package/templates/nextjs-standalone/src/lib/tools/backends/shared.ts +103 -0
  678. package/templates/nextjs-standalone/src/lib/tools/backends/types.ts +26 -0
  679. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +7 -228
  680. package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +4 -29
  681. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +18 -2
  682. package/templates/nextjs-standalone/src/lib/tools/explore.ts +246 -54
  683. package/templates/nextjs-standalone/src/lib/tools/index.ts +17 -0
  684. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +11 -139
  685. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +9 -132
  686. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +184 -3
  687. package/templates/nextjs-standalone/src/lib/tools/python-stream.ts +33 -0
  688. package/templates/nextjs-standalone/src/lib/tools/python-wrapper.ts +129 -0
  689. package/templates/nextjs-standalone/src/lib/tools/python.ts +115 -15
  690. package/templates/nextjs-standalone/src/lib/tools/registry.ts +14 -2
  691. package/templates/nextjs-standalone/src/lib/tools/sql.ts +778 -362
  692. package/templates/nextjs-standalone/src/lib/tracing.ts +16 -0
  693. package/templates/nextjs-standalone/src/lib/whatsapp/store.ts +198 -0
  694. package/templates/nextjs-standalone/src/lib/workspace.ts +89 -0
  695. package/templates/nextjs-standalone/src/progress.ts +121 -0
  696. package/templates/nextjs-standalone/src/types/data-table.ts +48 -0
  697. package/templates/nextjs-standalone/src/ui/atlas-chat-reexport.ts +3 -0
  698. package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +26 -19
  699. package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +3 -3
  700. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +57 -39
  701. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +213 -35
  702. package/templates/nextjs-standalone/src/ui/components/admin/delivery-status-badge.tsx +53 -0
  703. package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +27 -6
  704. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -52
  705. package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +2 -2
  706. package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +28 -5
  707. package/templates/nextjs-standalone/src/ui/components/admin-content-wrapper.tsx +87 -0
  708. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +449 -166
  709. package/templates/nextjs-standalone/src/ui/components/branding-head.tsx +41 -0
  710. package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +62 -5
  711. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +316 -125
  712. package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +4 -4
  713. package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +45 -4
  714. package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +86 -5
  715. package/templates/nextjs-standalone/src/ui/components/chat/follow-up-chips.tsx +29 -0
  716. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +24 -0
  717. package/templates/nextjs-standalone/src/ui/components/chat/prompt-library.tsx +206 -0
  718. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +106 -78
  719. package/templates/nextjs-standalone/src/ui/components/chat/result-card-base.tsx +101 -0
  720. package/templates/nextjs-standalone/src/ui/components/chat/share-dialog.tsx +377 -0
  721. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +94 -73
  722. package/templates/nextjs-standalone/src/ui/components/chat/suggestion-chips.tsx +46 -0
  723. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +16 -4
  724. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +48 -17
  725. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +38 -24
  726. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +66 -7
  727. package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +9 -2
  728. package/templates/nextjs-standalone/src/ui/components/error-boundary.tsx +66 -0
  729. package/templates/nextjs-standalone/src/ui/components/notebook/delete-cell-dialog.tsx +48 -0
  730. package/templates/nextjs-standalone/src/ui/components/notebook/fork-branch-selector.tsx +68 -0
  731. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-input.tsx +76 -0
  732. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-output.tsx +58 -0
  733. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell-toolbar.tsx +91 -0
  734. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-cell.tsx +119 -0
  735. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-empty-state.tsx +19 -0
  736. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-export.ts +287 -0
  737. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-input-bar.tsx +49 -0
  738. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-shell.tsx +266 -0
  739. package/templates/nextjs-standalone/src/ui/components/notebook/notebook-text-cell.tsx +152 -0
  740. package/templates/nextjs-standalone/src/ui/components/notebook/types.ts +39 -0
  741. package/templates/nextjs-standalone/src/ui/components/notebook/use-keyboard-nav.ts +109 -0
  742. package/templates/nextjs-standalone/src/ui/components/notebook/use-notebook.ts +684 -0
  743. package/templates/nextjs-standalone/src/ui/components/org-switcher.tsx +111 -0
  744. package/templates/nextjs-standalone/src/ui/components/region-picker.tsx +103 -0
  745. package/templates/nextjs-standalone/src/ui/components/schema-explorer/schema-explorer.tsx +522 -0
  746. package/templates/nextjs-standalone/src/ui/components/social-icons.tsx +26 -0
  747. package/templates/nextjs-standalone/src/ui/components/tour/guided-tour.tsx +81 -0
  748. package/templates/nextjs-standalone/src/ui/components/tour/index.ts +5 -0
  749. package/templates/nextjs-standalone/src/ui/components/tour/nav-bar.tsx +100 -0
  750. package/templates/nextjs-standalone/src/ui/components/tour/tour-overlay.tsx +298 -0
  751. package/templates/nextjs-standalone/src/ui/components/tour/tour-steps.ts +43 -0
  752. package/templates/nextjs-standalone/src/ui/components/tour/types.ts +21 -0
  753. package/templates/nextjs-standalone/src/ui/components/tour/use-tour.ts +193 -0
  754. package/templates/nextjs-standalone/src/ui/context-reexport.ts +3 -0
  755. package/templates/nextjs-standalone/src/ui/hooks/theme-init-script.ts +17 -0
  756. package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +38 -30
  757. package/templates/nextjs-standalone/src/ui/hooks/use-admin-mutation.ts +188 -0
  758. package/templates/nextjs-standalone/src/ui/hooks/use-atlas-transport.ts +225 -0
  759. package/templates/nextjs-standalone/src/ui/hooks/use-branding.ts +68 -0
  760. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +106 -83
  761. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +134 -10
  762. package/templates/nextjs-standalone/src/ui/hooks/use-deploy-mode.ts +36 -0
  763. package/templates/nextjs-standalone/src/ui/hooks/use-platform-admin-guard.ts +49 -0
  764. package/templates/nextjs-standalone/src/ui/lib/action-types.ts +11 -63
  765. package/templates/nextjs-standalone/src/ui/lib/admin-schemas.ts +744 -0
  766. package/templates/nextjs-standalone/src/ui/lib/fetch-client.ts +84 -0
  767. package/templates/nextjs-standalone/src/ui/lib/fetch-error.ts +54 -0
  768. package/templates/nextjs-standalone/src/ui/lib/helpers.ts +94 -1
  769. package/templates/nextjs-standalone/src/ui/lib/types.ts +149 -140
  770. package/templates/nextjs-standalone/tsconfig.json +3 -2
  771. package/templates/docker/src/api/__tests__/actions.test.ts +0 -683
  772. package/templates/docker/src/api/__tests__/admin.test.ts +0 -820
  773. package/templates/docker/src/api/__tests__/auth.test.ts +0 -165
  774. package/templates/docker/src/api/__tests__/chat.test.ts +0 -376
  775. package/templates/docker/src/api/__tests__/conversations.test.ts +0 -555
  776. package/templates/docker/src/api/__tests__/cors.test.ts +0 -135
  777. package/templates/docker/src/api/__tests__/health-plugin.test.ts +0 -176
  778. package/templates/docker/src/api/__tests__/health.test.ts +0 -283
  779. package/templates/docker/src/api/__tests__/query.test.ts +0 -891
  780. package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +0 -601
  781. package/templates/docker/src/api/__tests__/slack.test.ts +0 -847
  782. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +0 -439
  783. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +0 -131
  784. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
  785. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +0 -516
  786. package/templates/docker/src/lib/__tests__/config-actions.test.ts +0 -166
  787. package/templates/docker/src/lib/__tests__/config.test.ts +0 -1113
  788. package/templates/docker/src/lib/__tests__/conversations.test.ts +0 -589
  789. package/templates/docker/src/lib/__tests__/errors.test.ts +0 -256
  790. package/templates/docker/src/lib/__tests__/logger.test.ts +0 -200
  791. package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
  792. package/templates/docker/src/lib/__tests__/providers.test.ts +0 -130
  793. package/templates/docker/src/lib/__tests__/rls.test.ts +0 -435
  794. package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
  795. package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
  796. package/templates/docker/src/lib/__tests__/semantic-index.test.ts +0 -547
  797. package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +0 -544
  798. package/templates/docker/src/lib/__tests__/semantic.test.ts +0 -363
  799. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +0 -461
  800. package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +0 -429
  801. package/templates/docker/src/lib/__tests__/startup.test.ts +0 -470
  802. package/templates/docker/src/lib/__tests__/tracing.test.ts +0 -28
  803. package/templates/docker/src/lib/auth/__tests__/audit.test.ts +0 -418
  804. package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
  805. package/templates/docker/src/lib/auth/__tests__/byot.test.ts +0 -366
  806. package/templates/docker/src/lib/auth/__tests__/detect.test.ts +0 -190
  807. package/templates/docker/src/lib/auth/__tests__/managed.test.ts +0 -173
  808. package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +0 -456
  809. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +0 -203
  810. package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +0 -225
  811. package/templates/docker/src/lib/auth/__tests__/server.test.ts +0 -34
  812. package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +0 -176
  813. package/templates/docker/src/lib/auth/__tests__/types.test.ts +0 -44
  814. package/templates/docker/src/lib/db/__tests__/connection.test.ts +0 -144
  815. package/templates/docker/src/lib/db/__tests__/internal.test.ts +0 -387
  816. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +0 -190
  817. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
  818. package/templates/docker/src/lib/db/__tests__/registry.test.ts +0 -398
  819. package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
  820. package/templates/docker/src/lib/errors.ts +0 -154
  821. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
  822. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +0 -529
  823. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +0 -875
  824. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +0 -373
  825. package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +0 -49
  826. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +0 -799
  827. package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
  828. package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +0 -248
  829. package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
  830. package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
  831. package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
  832. package/templates/docker/src/lib/scheduler/index.ts +0 -7
  833. package/templates/docker/src/lib/slack/__tests__/api.test.ts +0 -160
  834. package/templates/docker/src/lib/slack/__tests__/format.test.ts +0 -237
  835. package/templates/docker/src/lib/slack/__tests__/store.test.ts +0 -188
  836. package/templates/docker/src/lib/slack/__tests__/threads.test.ts +0 -112
  837. package/templates/docker/src/lib/slack/__tests__/verify.test.ts +0 -111
  838. package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
  839. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
  840. package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
  841. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
  842. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
  843. package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
  844. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
  845. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
  846. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
  847. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
  848. package/templates/docker/src/lib/tools/__tests__/python.test.ts +0 -331
  849. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
  850. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +0 -242
  851. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
  852. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
  853. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
  854. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +0 -709
  855. package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
  856. package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +0 -378
  857. package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
  858. package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
  859. package/templates/docker/src/test-setup.ts +0 -38
  860. package/templates/docker/src/types/vercel-sandbox.d.ts +0 -61
  861. package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +0 -116
  862. package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +0 -683
  863. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +0 -820
  864. package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +0 -165
  865. package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +0 -376
  866. package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +0 -555
  867. package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +0 -135
  868. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +0 -176
  869. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +0 -283
  870. package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +0 -891
  871. package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +0 -601
  872. package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +0 -847
  873. package/templates/nextjs-standalone/src/app/global-error.tsx +0 -68
  874. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +0 -439
  875. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +0 -131
  876. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +0 -166
  877. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +0 -516
  878. package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +0 -166
  879. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +0 -1113
  880. package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +0 -589
  881. package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +0 -256
  882. package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +0 -200
  883. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +0 -321
  884. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +0 -130
  885. package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +0 -435
  886. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +0 -124
  887. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +0 -550
  888. package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +0 -547
  889. package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +0 -544
  890. package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +0 -363
  891. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +0 -461
  892. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +0 -429
  893. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +0 -470
  894. package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +0 -28
  895. package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +0 -418
  896. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +0 -222
  897. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +0 -366
  898. package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +0 -190
  899. package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +0 -173
  900. package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +0 -456
  901. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +0 -203
  902. package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +0 -225
  903. package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +0 -34
  904. package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +0 -176
  905. package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +0 -44
  906. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +0 -144
  907. package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +0 -387
  908. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +0 -190
  909. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -137
  910. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +0 -398
  911. package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +0 -130
  912. package/templates/nextjs-standalone/src/lib/errors.ts +0 -154
  913. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +0 -204
  914. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +0 -529
  915. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +0 -875
  916. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +0 -373
  917. package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +0 -49
  918. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +0 -799
  919. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +0 -192
  920. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +0 -248
  921. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +0 -96
  922. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +0 -78
  923. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +0 -78
  924. package/templates/nextjs-standalone/src/lib/scheduler/index.ts +0 -7
  925. package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +0 -160
  926. package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +0 -237
  927. package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +0 -188
  928. package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +0 -112
  929. package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +0 -111
  930. package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +0 -594
  931. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +0 -240
  932. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +0 -267
  933. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +0 -506
  934. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +0 -374
  935. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +0 -82
  936. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +0 -210
  937. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +0 -515
  938. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +0 -397
  939. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +0 -365
  940. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +0 -331
  941. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +0 -132
  942. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +0 -242
  943. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +0 -227
  944. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +0 -100
  945. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +0 -227
  946. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +0 -709
  947. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +0 -211
  948. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +0 -378
  949. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +0 -681
  950. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +0 -427
  951. package/templates/nextjs-standalone/src/test-setup.ts +0 -38
  952. 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. Check ATLAS_SCHEMA in your .env file.`
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. Connections can be created from a ConnectionConfig
300
- * (URL + optional schema) via register(), or injected as pre-built DBConnection
301
- * instances via registerDirect(). The "default" connection auto-initializes from
302
- * ATLAS_DATASOURCE_URL on first access via getDefault().
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 healthCheckInterval: ReturnType<typeof setInterval> | null = null;
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
- log.info({ connectionId: oldest.id }, "Evicting LRU connection to free pool capacity");
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: oldest!.id }, "Failed to close evicted connection");
735
+ log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: oldestId }, "Failed to close evicted connection");
335
736
  });
336
- this.entries.delete(oldest.id);
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 Error(`Connection "${id}" is not registered.`);
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 Error(`Connection "${id}" is not registered.`);
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 Error(
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 Error(`Connection "${id}" is not registered.`);
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: err instanceof Error ? err.message : String(err),
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 for all connections. Idempotent. */
990
+ /** Start periodic health checks via Effect.repeat + Schedule.spaced. Idempotent. */
518
991
  startHealthChecks(intervalMs = 60_000): void {
519
- if (this.healthCheckInterval) return;
520
- this.healthCheckInterval = setInterval(() => {
521
- for (const id of this.entries.keys()) {
522
- this.healthCheck(id).catch((err) => {
523
- log.warn({ err: err instanceof Error ? err.message : String(err), connectionId: id }, "Periodic health check failed");
524
- });
525
- }
526
- }, intervalMs);
527
- this.healthCheckInterval.unref();
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.healthCheckInterval) {
533
- clearInterval(this.healthCheckInterval);
534
- this.healthCheckInterval = null;
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. Use this in production shutdown paths instead of _reset().
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
+ }