@useatlas/create 0.0.1

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 (515) hide show
  1. package/README.md +231 -0
  2. package/index.ts +829 -0
  3. package/package.json +38 -0
  4. package/templates/docker/.env.example +67 -0
  5. package/templates/docker/Dockerfile +52 -0
  6. package/templates/docker/bin/__tests__/benchmark.test.ts +598 -0
  7. package/templates/docker/bin/__tests__/duckdb-ingest.test.ts +171 -0
  8. package/templates/docker/bin/__tests__/eval.test.ts +434 -0
  9. package/templates/docker/bin/__tests__/matview-partition.test.ts +615 -0
  10. package/templates/docker/bin/__tests__/multi-source.test.ts +113 -0
  11. package/templates/docker/bin/__tests__/plugin-cli.test.ts +322 -0
  12. package/templates/docker/bin/__tests__/profiler-heuristics.test.ts +608 -0
  13. package/templates/docker/bin/__tests__/query.test.ts +240 -0
  14. package/templates/docker/bin/__tests__/schema-drift.test.ts +542 -0
  15. package/templates/docker/bin/__tests__/view-yaml-generation.test.ts +146 -0
  16. package/templates/docker/bin/atlas.ts +5044 -0
  17. package/templates/docker/bin/benchmark.ts +695 -0
  18. package/templates/docker/bin/enrich.ts +559 -0
  19. package/templates/docker/bin/eval.ts +770 -0
  20. package/templates/docker/bin/smoke.ts +438 -0
  21. package/templates/docker/data/.gitkeep +0 -0
  22. package/templates/docker/data/cybersec.sql +1961 -0
  23. package/templates/docker/data/demo-semantic/catalog.yml +40 -0
  24. package/templates/docker/data/demo-semantic/entities/accounts.yml +170 -0
  25. package/templates/docker/data/demo-semantic/entities/companies.yml +207 -0
  26. package/templates/docker/data/demo-semantic/entities/people.yml +145 -0
  27. package/templates/docker/data/demo-semantic/glossary.yml +22 -0
  28. package/templates/docker/data/demo-semantic/metrics/accounts.yml +38 -0
  29. package/templates/docker/data/demo-semantic/metrics/companies.yml +89 -0
  30. package/templates/docker/data/demo.sql +373 -0
  31. package/templates/docker/data/ecommerce.sql +1690 -0
  32. package/templates/docker/data/init-demo-db.sql +8 -0
  33. package/templates/docker/docker-compose.yml +34 -0
  34. package/templates/docker/docs/deploy.md +390 -0
  35. package/templates/docker/eslint.config.mjs +18 -0
  36. package/templates/docker/gitignore +5 -0
  37. package/templates/docker/next.config.ts +9 -0
  38. package/templates/docker/package.json +59 -0
  39. package/templates/docker/postcss.config.mjs +8 -0
  40. package/templates/docker/public/.gitkeep +0 -0
  41. package/templates/docker/public/favicon.svg +4 -0
  42. package/templates/docker/railway.json +13 -0
  43. package/templates/docker/render.yaml +34 -0
  44. package/templates/docker/semantic/catalog.yml +5 -0
  45. package/templates/docker/semantic/entities/.gitkeep +0 -0
  46. package/templates/docker/semantic/glossary.yml +6 -0
  47. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  48. package/templates/docker/sidecar/Dockerfile +28 -0
  49. package/templates/docker/sidecar/railway.json +14 -0
  50. package/templates/docker/sidecar/server.ts +188 -0
  51. package/templates/docker/src/api/__tests__/actions.test.ts +683 -0
  52. package/templates/docker/src/api/__tests__/admin.test.ts +820 -0
  53. package/templates/docker/src/api/__tests__/auth.test.ts +165 -0
  54. package/templates/docker/src/api/__tests__/chat.test.ts +376 -0
  55. package/templates/docker/src/api/__tests__/conversations.test.ts +555 -0
  56. package/templates/docker/src/api/__tests__/cors.test.ts +135 -0
  57. package/templates/docker/src/api/__tests__/health-plugin.test.ts +169 -0
  58. package/templates/docker/src/api/__tests__/health.test.ts +261 -0
  59. package/templates/docker/src/api/__tests__/query.test.ts +891 -0
  60. package/templates/docker/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  61. package/templates/docker/src/api/__tests__/slack.test.ts +847 -0
  62. package/templates/docker/src/api/index.ts +117 -0
  63. package/templates/docker/src/api/routes/actions.ts +274 -0
  64. package/templates/docker/src/api/routes/admin.ts +757 -0
  65. package/templates/docker/src/api/routes/auth.ts +48 -0
  66. package/templates/docker/src/api/routes/chat.ts +465 -0
  67. package/templates/docker/src/api/routes/conversations.ts +266 -0
  68. package/templates/docker/src/api/routes/health.ts +287 -0
  69. package/templates/docker/src/api/routes/openapi.ts +390 -0
  70. package/templates/docker/src/api/routes/query.ts +318 -0
  71. package/templates/docker/src/api/routes/scheduled-tasks.ts +467 -0
  72. package/templates/docker/src/api/routes/slack.ts +611 -0
  73. package/templates/docker/src/api/server.ts +226 -0
  74. package/templates/docker/src/app/api/[...route]/route.ts +33 -0
  75. package/templates/docker/src/app/error.tsx +24 -0
  76. package/templates/docker/src/app/globals.css +126 -0
  77. package/templates/docker/src/app/layout.tsx +19 -0
  78. package/templates/docker/src/app/page.tsx +14 -0
  79. package/templates/docker/src/global.d.ts +1 -0
  80. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +437 -0
  81. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +114 -0
  82. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  83. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +514 -0
  84. package/templates/docker/src/lib/__tests__/config-actions.test.ts +166 -0
  85. package/templates/docker/src/lib/__tests__/config.test.ts +1063 -0
  86. package/templates/docker/src/lib/__tests__/conversations.test.ts +589 -0
  87. package/templates/docker/src/lib/__tests__/errors.test.ts +256 -0
  88. package/templates/docker/src/lib/__tests__/logger.test.ts +200 -0
  89. package/templates/docker/src/lib/__tests__/providers.test.ts +99 -0
  90. package/templates/docker/src/lib/__tests__/rls.test.ts +435 -0
  91. package/templates/docker/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  92. package/templates/docker/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  93. package/templates/docker/src/lib/__tests__/semantic-index.test.ts +547 -0
  94. package/templates/docker/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  95. package/templates/docker/src/lib/__tests__/semantic.test.ts +363 -0
  96. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +452 -0
  97. package/templates/docker/src/lib/__tests__/startup.test.ts +465 -0
  98. package/templates/docker/src/lib/__tests__/tracing.test.ts +28 -0
  99. package/templates/docker/src/lib/action-types.ts +95 -0
  100. package/templates/docker/src/lib/agent-query.ts +178 -0
  101. package/templates/docker/src/lib/agent.ts +505 -0
  102. package/templates/docker/src/lib/api-url.ts +2 -0
  103. package/templates/docker/src/lib/auth/__tests__/audit.test.ts +418 -0
  104. package/templates/docker/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  105. package/templates/docker/src/lib/auth/__tests__/byot.test.ts +366 -0
  106. package/templates/docker/src/lib/auth/__tests__/detect.test.ts +190 -0
  107. package/templates/docker/src/lib/auth/__tests__/managed.test.ts +173 -0
  108. package/templates/docker/src/lib/auth/__tests__/middleware.test.ts +456 -0
  109. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +201 -0
  110. package/templates/docker/src/lib/auth/__tests__/permissions.test.ts +225 -0
  111. package/templates/docker/src/lib/auth/__tests__/server.test.ts +34 -0
  112. package/templates/docker/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  113. package/templates/docker/src/lib/auth/__tests__/types.test.ts +44 -0
  114. package/templates/docker/src/lib/auth/audit.ts +89 -0
  115. package/templates/docker/src/lib/auth/byot.ts +158 -0
  116. package/templates/docker/src/lib/auth/client.ts +35 -0
  117. package/templates/docker/src/lib/auth/detect.ts +83 -0
  118. package/templates/docker/src/lib/auth/managed.ts +73 -0
  119. package/templates/docker/src/lib/auth/middleware.ts +208 -0
  120. package/templates/docker/src/lib/auth/migrate.ts +111 -0
  121. package/templates/docker/src/lib/auth/permissions.ts +156 -0
  122. package/templates/docker/src/lib/auth/server.ts +142 -0
  123. package/templates/docker/src/lib/auth/simple-key.ts +92 -0
  124. package/templates/docker/src/lib/auth/types.ts +49 -0
  125. package/templates/docker/src/lib/config.ts +704 -0
  126. package/templates/docker/src/lib/conversation-types.ts +29 -0
  127. package/templates/docker/src/lib/conversations.ts +270 -0
  128. package/templates/docker/src/lib/db/__tests__/connection.test.ts +69 -0
  129. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +141 -0
  130. package/templates/docker/src/lib/db/__tests__/internal.test.ts +387 -0
  131. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +207 -0
  132. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  133. package/templates/docker/src/lib/db/__tests__/registry.test.ts +595 -0
  134. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +339 -0
  135. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +217 -0
  136. package/templates/docker/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  137. package/templates/docker/src/lib/db/connection.ts +753 -0
  138. package/templates/docker/src/lib/db/duckdb.ts +122 -0
  139. package/templates/docker/src/lib/db/internal.ts +273 -0
  140. package/templates/docker/src/lib/db/salesforce.ts +342 -0
  141. package/templates/docker/src/lib/db/source-rate-limit.ts +191 -0
  142. package/templates/docker/src/lib/errors.ts +154 -0
  143. package/templates/docker/src/lib/logger.ts +98 -0
  144. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  145. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  146. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  147. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +346 -0
  148. package/templates/docker/src/lib/plugins/__tests__/tools.test.ts +49 -0
  149. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  150. package/templates/docker/src/lib/plugins/hooks.ts +162 -0
  151. package/templates/docker/src/lib/plugins/index.ts +9 -0
  152. package/templates/docker/src/lib/plugins/migrate.ts +309 -0
  153. package/templates/docker/src/lib/plugins/registry.ts +231 -0
  154. package/templates/docker/src/lib/plugins/tools.ts +39 -0
  155. package/templates/docker/src/lib/plugins/wiring.ts +291 -0
  156. package/templates/docker/src/lib/providers.ts +102 -0
  157. package/templates/docker/src/lib/rls.ts +321 -0
  158. package/templates/docker/src/lib/scheduled-task-types.ts +132 -0
  159. package/templates/docker/src/lib/scheduled-tasks.ts +475 -0
  160. package/templates/docker/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  161. package/templates/docker/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  162. package/templates/docker/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  163. package/templates/docker/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  164. package/templates/docker/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  165. package/templates/docker/src/lib/scheduler/delivery.ts +248 -0
  166. package/templates/docker/src/lib/scheduler/engine.ts +317 -0
  167. package/templates/docker/src/lib/scheduler/executor.ts +73 -0
  168. package/templates/docker/src/lib/scheduler/format-email.ts +109 -0
  169. package/templates/docker/src/lib/scheduler/format-slack.ts +35 -0
  170. package/templates/docker/src/lib/scheduler/format-webhook.ts +37 -0
  171. package/templates/docker/src/lib/scheduler/index.ts +7 -0
  172. package/templates/docker/src/lib/security.ts +11 -0
  173. package/templates/docker/src/lib/semantic-index.ts +503 -0
  174. package/templates/docker/src/lib/semantic.ts +387 -0
  175. package/templates/docker/src/lib/sidecar-types.ts +16 -0
  176. package/templates/docker/src/lib/slack/__tests__/api.test.ts +160 -0
  177. package/templates/docker/src/lib/slack/__tests__/format.test.ts +237 -0
  178. package/templates/docker/src/lib/slack/__tests__/store.test.ts +188 -0
  179. package/templates/docker/src/lib/slack/__tests__/threads.test.ts +112 -0
  180. package/templates/docker/src/lib/slack/__tests__/verify.test.ts +111 -0
  181. package/templates/docker/src/lib/slack/api.ts +102 -0
  182. package/templates/docker/src/lib/slack/format.ts +209 -0
  183. package/templates/docker/src/lib/slack/store.ts +107 -0
  184. package/templates/docker/src/lib/slack/threads.ts +64 -0
  185. package/templates/docker/src/lib/slack/verify.ts +71 -0
  186. package/templates/docker/src/lib/startup.ts +730 -0
  187. package/templates/docker/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  188. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  189. package/templates/docker/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  190. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  191. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  192. package/templates/docker/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  193. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  194. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  195. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +235 -0
  196. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  197. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  198. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  199. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  200. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  201. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  202. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +1012 -0
  203. package/templates/docker/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  204. package/templates/docker/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  205. package/templates/docker/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  206. package/templates/docker/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  207. package/templates/docker/src/lib/tools/actions/audit.ts +47 -0
  208. package/templates/docker/src/lib/tools/actions/email.ts +191 -0
  209. package/templates/docker/src/lib/tools/actions/handler.ts +591 -0
  210. package/templates/docker/src/lib/tools/actions/index.ts +23 -0
  211. package/templates/docker/src/lib/tools/actions/jira.ts +220 -0
  212. package/templates/docker/src/lib/tools/explore-nsjail.ts +343 -0
  213. package/templates/docker/src/lib/tools/explore-sandbox.ts +264 -0
  214. package/templates/docker/src/lib/tools/explore-sidecar.ts +163 -0
  215. package/templates/docker/src/lib/tools/explore.ts +379 -0
  216. package/templates/docker/src/lib/tools/registry.ts +221 -0
  217. package/templates/docker/src/lib/tools/salesforce.ts +138 -0
  218. package/templates/docker/src/lib/tools/soql-validation.ts +172 -0
  219. package/templates/docker/src/lib/tools/sql.ts +680 -0
  220. package/templates/docker/src/lib/tracing.ts +40 -0
  221. package/templates/docker/src/lib/utils.ts +6 -0
  222. package/templates/docker/src/test-setup.ts +38 -0
  223. package/templates/docker/src/types/vercel-sandbox.d.ts +54 -0
  224. package/templates/docker/src/ui/components/actions/action-approval-card.tsx +295 -0
  225. package/templates/docker/src/ui/components/actions/action-status-badge.tsx +50 -0
  226. package/templates/docker/src/ui/components/admin/admin-layout.tsx +26 -0
  227. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +96 -0
  228. package/templates/docker/src/ui/components/admin/empty-state.tsx +24 -0
  229. package/templates/docker/src/ui/components/admin/entity-detail.tsx +233 -0
  230. package/templates/docker/src/ui/components/admin/entity-list.tsx +96 -0
  231. package/templates/docker/src/ui/components/admin/error-banner.tsx +22 -0
  232. package/templates/docker/src/ui/components/admin/feature-disabled.tsx +44 -0
  233. package/templates/docker/src/ui/components/admin/health-badge.tsx +30 -0
  234. package/templates/docker/src/ui/components/admin/loading-state.tsx +14 -0
  235. package/templates/docker/src/ui/components/admin/stat-card.tsx +32 -0
  236. package/templates/docker/src/ui/components/atlas-chat.tsx +370 -0
  237. package/templates/docker/src/ui/components/chart/chart-detection.ts +261 -0
  238. package/templates/docker/src/ui/components/chart/result-chart.tsx +375 -0
  239. package/templates/docker/src/ui/components/chat/api-key-bar.tsx +66 -0
  240. package/templates/docker/src/ui/components/chat/copy-button.tsx +25 -0
  241. package/templates/docker/src/ui/components/chat/data-table.tsx +102 -0
  242. package/templates/docker/src/ui/components/chat/error-banner.tsx +32 -0
  243. package/templates/docker/src/ui/components/chat/explore-card.tsx +41 -0
  244. package/templates/docker/src/ui/components/chat/loading-card.tsx +10 -0
  245. package/templates/docker/src/ui/components/chat/managed-auth-card.tsx +116 -0
  246. package/templates/docker/src/ui/components/chat/markdown.tsx +72 -0
  247. package/templates/docker/src/ui/components/chat/sql-block.tsx +30 -0
  248. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +144 -0
  249. package/templates/docker/src/ui/components/chat/starter-prompts.ts +6 -0
  250. package/templates/docker/src/ui/components/chat/tool-part.tsx +40 -0
  251. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +19 -0
  252. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +120 -0
  253. package/templates/docker/src/ui/components/conversations/conversation-list.tsx +66 -0
  254. package/templates/docker/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  255. package/templates/docker/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  256. package/templates/docker/src/ui/context.tsx +78 -0
  257. package/templates/docker/src/ui/hooks/use-admin-fetch.ts +104 -0
  258. package/templates/docker/src/ui/hooks/use-conversations.ts +184 -0
  259. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -0
  260. package/templates/docker/src/ui/lib/action-types.ts +63 -0
  261. package/templates/docker/src/ui/lib/helpers.ts +104 -0
  262. package/templates/docker/src/ui/lib/types.ts +145 -0
  263. package/templates/docker/tsconfig.json +41 -0
  264. package/templates/docker/vercel.json +3 -0
  265. package/templates/nextjs-standalone/.env.example +68 -0
  266. package/templates/nextjs-standalone/bin/__tests__/benchmark.test.ts +598 -0
  267. package/templates/nextjs-standalone/bin/__tests__/duckdb-ingest.test.ts +171 -0
  268. package/templates/nextjs-standalone/bin/__tests__/eval.test.ts +434 -0
  269. package/templates/nextjs-standalone/bin/__tests__/matview-partition.test.ts +615 -0
  270. package/templates/nextjs-standalone/bin/__tests__/multi-source.test.ts +113 -0
  271. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +322 -0
  272. package/templates/nextjs-standalone/bin/__tests__/profiler-heuristics.test.ts +608 -0
  273. package/templates/nextjs-standalone/bin/__tests__/query.test.ts +240 -0
  274. package/templates/nextjs-standalone/bin/__tests__/schema-drift.test.ts +542 -0
  275. package/templates/nextjs-standalone/bin/__tests__/view-yaml-generation.test.ts +146 -0
  276. package/templates/nextjs-standalone/bin/atlas.ts +5044 -0
  277. package/templates/nextjs-standalone/bin/benchmark.ts +695 -0
  278. package/templates/nextjs-standalone/bin/enrich.ts +559 -0
  279. package/templates/nextjs-standalone/bin/eval.ts +770 -0
  280. package/templates/nextjs-standalone/bin/smoke.ts +438 -0
  281. package/templates/nextjs-standalone/data/.gitkeep +0 -0
  282. package/templates/nextjs-standalone/data/cybersec.sql +1961 -0
  283. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +40 -0
  284. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +170 -0
  285. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +207 -0
  286. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +145 -0
  287. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +22 -0
  288. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +38 -0
  289. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +89 -0
  290. package/templates/nextjs-standalone/data/demo.sql +373 -0
  291. package/templates/nextjs-standalone/data/ecommerce.sql +1690 -0
  292. package/templates/nextjs-standalone/data/init-demo-db.sql +8 -0
  293. package/templates/nextjs-standalone/docs/deploy.md +390 -0
  294. package/templates/nextjs-standalone/eslint.config.mjs +18 -0
  295. package/templates/nextjs-standalone/gitignore +5 -0
  296. package/templates/nextjs-standalone/next.config.ts +10 -0
  297. package/templates/nextjs-standalone/package.json +63 -0
  298. package/templates/nextjs-standalone/postcss.config.mjs +8 -0
  299. package/templates/nextjs-standalone/semantic/catalog.yml +5 -0
  300. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  301. package/templates/nextjs-standalone/semantic/glossary.yml +6 -0
  302. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  303. package/templates/nextjs-standalone/src/api/__tests__/actions.test.ts +683 -0
  304. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +820 -0
  305. package/templates/nextjs-standalone/src/api/__tests__/auth.test.ts +165 -0
  306. package/templates/nextjs-standalone/src/api/__tests__/chat.test.ts +376 -0
  307. package/templates/nextjs-standalone/src/api/__tests__/conversations.test.ts +555 -0
  308. package/templates/nextjs-standalone/src/api/__tests__/cors.test.ts +135 -0
  309. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +169 -0
  310. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +261 -0
  311. package/templates/nextjs-standalone/src/api/__tests__/query.test.ts +891 -0
  312. package/templates/nextjs-standalone/src/api/__tests__/scheduled-tasks.test.ts +601 -0
  313. package/templates/nextjs-standalone/src/api/__tests__/slack.test.ts +847 -0
  314. package/templates/nextjs-standalone/src/api/index.ts +117 -0
  315. package/templates/nextjs-standalone/src/api/routes/actions.ts +274 -0
  316. package/templates/nextjs-standalone/src/api/routes/admin.ts +757 -0
  317. package/templates/nextjs-standalone/src/api/routes/auth.ts +48 -0
  318. package/templates/nextjs-standalone/src/api/routes/chat.ts +465 -0
  319. package/templates/nextjs-standalone/src/api/routes/conversations.ts +266 -0
  320. package/templates/nextjs-standalone/src/api/routes/health.ts +287 -0
  321. package/templates/nextjs-standalone/src/api/routes/openapi.ts +390 -0
  322. package/templates/nextjs-standalone/src/api/routes/query.ts +318 -0
  323. package/templates/nextjs-standalone/src/api/routes/scheduled-tasks.ts +467 -0
  324. package/templates/nextjs-standalone/src/api/routes/slack.ts +611 -0
  325. package/templates/nextjs-standalone/src/api/server.ts +226 -0
  326. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +33 -0
  327. package/templates/nextjs-standalone/src/app/error.tsx +24 -0
  328. package/templates/nextjs-standalone/src/app/global-error.tsx +68 -0
  329. package/templates/nextjs-standalone/src/app/globals.css +126 -0
  330. package/templates/nextjs-standalone/src/app/layout.tsx +19 -0
  331. package/templates/nextjs-standalone/src/app/page.tsx +14 -0
  332. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +437 -0
  333. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +114 -0
  334. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +164 -0
  335. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +514 -0
  336. package/templates/nextjs-standalone/src/lib/__tests__/config-actions.test.ts +166 -0
  337. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +1063 -0
  338. package/templates/nextjs-standalone/src/lib/__tests__/conversations.test.ts +589 -0
  339. package/templates/nextjs-standalone/src/lib/__tests__/errors.test.ts +256 -0
  340. package/templates/nextjs-standalone/src/lib/__tests__/logger.test.ts +200 -0
  341. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +99 -0
  342. package/templates/nextjs-standalone/src/lib/__tests__/rls.test.ts +435 -0
  343. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-task-types.test.ts +124 -0
  344. package/templates/nextjs-standalone/src/lib/__tests__/scheduled-tasks.test.ts +550 -0
  345. package/templates/nextjs-standalone/src/lib/__tests__/semantic-index.test.ts +547 -0
  346. package/templates/nextjs-standalone/src/lib/__tests__/semantic-multisource.test.ts +544 -0
  347. package/templates/nextjs-standalone/src/lib/__tests__/semantic.test.ts +363 -0
  348. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +452 -0
  349. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +465 -0
  350. package/templates/nextjs-standalone/src/lib/__tests__/tracing.test.ts +28 -0
  351. package/templates/nextjs-standalone/src/lib/action-types.ts +95 -0
  352. package/templates/nextjs-standalone/src/lib/agent-query.ts +178 -0
  353. package/templates/nextjs-standalone/src/lib/agent.ts +505 -0
  354. package/templates/nextjs-standalone/src/lib/api-url.ts +3 -0
  355. package/templates/nextjs-standalone/src/lib/auth/__tests__/audit.test.ts +418 -0
  356. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot-integration.test.ts +222 -0
  357. package/templates/nextjs-standalone/src/lib/auth/__tests__/byot.test.ts +366 -0
  358. package/templates/nextjs-standalone/src/lib/auth/__tests__/detect.test.ts +190 -0
  359. package/templates/nextjs-standalone/src/lib/auth/__tests__/managed.test.ts +173 -0
  360. package/templates/nextjs-standalone/src/lib/auth/__tests__/middleware.test.ts +456 -0
  361. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +201 -0
  362. package/templates/nextjs-standalone/src/lib/auth/__tests__/permissions.test.ts +225 -0
  363. package/templates/nextjs-standalone/src/lib/auth/__tests__/server.test.ts +34 -0
  364. package/templates/nextjs-standalone/src/lib/auth/__tests__/simple-key.test.ts +176 -0
  365. package/templates/nextjs-standalone/src/lib/auth/__tests__/types.test.ts +44 -0
  366. package/templates/nextjs-standalone/src/lib/auth/audit.ts +89 -0
  367. package/templates/nextjs-standalone/src/lib/auth/byot.ts +158 -0
  368. package/templates/nextjs-standalone/src/lib/auth/client.ts +23 -0
  369. package/templates/nextjs-standalone/src/lib/auth/detect.ts +83 -0
  370. package/templates/nextjs-standalone/src/lib/auth/managed.ts +73 -0
  371. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +208 -0
  372. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +111 -0
  373. package/templates/nextjs-standalone/src/lib/auth/permissions.ts +156 -0
  374. package/templates/nextjs-standalone/src/lib/auth/server.ts +142 -0
  375. package/templates/nextjs-standalone/src/lib/auth/simple-key.ts +92 -0
  376. package/templates/nextjs-standalone/src/lib/auth/types.ts +49 -0
  377. package/templates/nextjs-standalone/src/lib/config.ts +704 -0
  378. package/templates/nextjs-standalone/src/lib/conversation-types.ts +29 -0
  379. package/templates/nextjs-standalone/src/lib/conversations.ts +270 -0
  380. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +69 -0
  381. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +141 -0
  382. package/templates/nextjs-standalone/src/lib/db/__tests__/internal.test.ts +387 -0
  383. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +207 -0
  384. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +156 -0
  385. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +595 -0
  386. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +339 -0
  387. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +217 -0
  388. package/templates/nextjs-standalone/src/lib/db/__tests__/source-rate-limit.test.ts +130 -0
  389. package/templates/nextjs-standalone/src/lib/db/connection.ts +753 -0
  390. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +122 -0
  391. package/templates/nextjs-standalone/src/lib/db/internal.ts +273 -0
  392. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +342 -0
  393. package/templates/nextjs-standalone/src/lib/db/source-rate-limit.ts +191 -0
  394. package/templates/nextjs-standalone/src/lib/errors.ts +154 -0
  395. package/templates/nextjs-standalone/src/lib/logger.ts +98 -0
  396. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +202 -0
  397. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +529 -0
  398. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +521 -0
  399. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +346 -0
  400. package/templates/nextjs-standalone/src/lib/plugins/__tests__/tools.test.ts +49 -0
  401. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +585 -0
  402. package/templates/nextjs-standalone/src/lib/plugins/hooks.ts +162 -0
  403. package/templates/nextjs-standalone/src/lib/plugins/index.ts +9 -0
  404. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +309 -0
  405. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +231 -0
  406. package/templates/nextjs-standalone/src/lib/plugins/tools.ts +39 -0
  407. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +291 -0
  408. package/templates/nextjs-standalone/src/lib/providers.ts +102 -0
  409. package/templates/nextjs-standalone/src/lib/rls.ts +321 -0
  410. package/templates/nextjs-standalone/src/lib/scheduled-task-types.ts +132 -0
  411. package/templates/nextjs-standalone/src/lib/scheduled-tasks.ts +475 -0
  412. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/delivery.test.ts +192 -0
  413. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/engine.test.ts +248 -0
  414. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-email.test.ts +96 -0
  415. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-slack.test.ts +78 -0
  416. package/templates/nextjs-standalone/src/lib/scheduler/__tests__/format-webhook.test.ts +78 -0
  417. package/templates/nextjs-standalone/src/lib/scheduler/delivery.ts +248 -0
  418. package/templates/nextjs-standalone/src/lib/scheduler/engine.ts +317 -0
  419. package/templates/nextjs-standalone/src/lib/scheduler/executor.ts +73 -0
  420. package/templates/nextjs-standalone/src/lib/scheduler/format-email.ts +109 -0
  421. package/templates/nextjs-standalone/src/lib/scheduler/format-slack.ts +35 -0
  422. package/templates/nextjs-standalone/src/lib/scheduler/format-webhook.ts +37 -0
  423. package/templates/nextjs-standalone/src/lib/scheduler/index.ts +7 -0
  424. package/templates/nextjs-standalone/src/lib/security.ts +11 -0
  425. package/templates/nextjs-standalone/src/lib/semantic-index.ts +503 -0
  426. package/templates/nextjs-standalone/src/lib/semantic.ts +387 -0
  427. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +16 -0
  428. package/templates/nextjs-standalone/src/lib/slack/__tests__/api.test.ts +160 -0
  429. package/templates/nextjs-standalone/src/lib/slack/__tests__/format.test.ts +237 -0
  430. package/templates/nextjs-standalone/src/lib/slack/__tests__/store.test.ts +188 -0
  431. package/templates/nextjs-standalone/src/lib/slack/__tests__/threads.test.ts +112 -0
  432. package/templates/nextjs-standalone/src/lib/slack/__tests__/verify.test.ts +111 -0
  433. package/templates/nextjs-standalone/src/lib/slack/api.ts +102 -0
  434. package/templates/nextjs-standalone/src/lib/slack/format.ts +209 -0
  435. package/templates/nextjs-standalone/src/lib/slack/store.ts +107 -0
  436. package/templates/nextjs-standalone/src/lib/slack/threads.ts +64 -0
  437. package/templates/nextjs-standalone/src/lib/slack/verify.ts +71 -0
  438. package/templates/nextjs-standalone/src/lib/startup.ts +730 -0
  439. package/templates/nextjs-standalone/src/lib/tools/__tests__/action-permissions.test.ts +594 -0
  440. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +238 -0
  441. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-backend.test.ts +267 -0
  442. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +492 -0
  443. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +374 -0
  444. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sdk-compat.test.ts +82 -0
  445. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +208 -0
  446. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +144 -0
  447. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +235 -0
  448. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +154 -0
  449. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +303 -0
  450. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +225 -0
  451. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +98 -0
  452. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +233 -0
  453. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +225 -0
  454. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +1012 -0
  455. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/audit.test.ts +211 -0
  456. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/email.test.ts +378 -0
  457. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/handler.test.ts +681 -0
  458. package/templates/nextjs-standalone/src/lib/tools/actions/__tests__/jira.test.ts +427 -0
  459. package/templates/nextjs-standalone/src/lib/tools/actions/audit.ts +47 -0
  460. package/templates/nextjs-standalone/src/lib/tools/actions/email.ts +191 -0
  461. package/templates/nextjs-standalone/src/lib/tools/actions/handler.ts +591 -0
  462. package/templates/nextjs-standalone/src/lib/tools/actions/index.ts +23 -0
  463. package/templates/nextjs-standalone/src/lib/tools/actions/jira.ts +220 -0
  464. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +343 -0
  465. package/templates/nextjs-standalone/src/lib/tools/explore-sandbox.ts +264 -0
  466. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +163 -0
  467. package/templates/nextjs-standalone/src/lib/tools/explore.ts +379 -0
  468. package/templates/nextjs-standalone/src/lib/tools/registry.ts +221 -0
  469. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +138 -0
  470. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +172 -0
  471. package/templates/nextjs-standalone/src/lib/tools/sql.ts +680 -0
  472. package/templates/nextjs-standalone/src/lib/tracing.ts +40 -0
  473. package/templates/nextjs-standalone/src/lib/utils.ts +6 -0
  474. package/templates/nextjs-standalone/src/test-setup.ts +38 -0
  475. package/templates/nextjs-standalone/src/ui/components/actions/action-approval-card.tsx +295 -0
  476. package/templates/nextjs-standalone/src/ui/components/actions/action-status-badge.tsx +50 -0
  477. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +26 -0
  478. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +96 -0
  479. package/templates/nextjs-standalone/src/ui/components/admin/empty-state.tsx +24 -0
  480. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +233 -0
  481. package/templates/nextjs-standalone/src/ui/components/admin/entity-list.tsx +96 -0
  482. package/templates/nextjs-standalone/src/ui/components/admin/error-banner.tsx +22 -0
  483. package/templates/nextjs-standalone/src/ui/components/admin/feature-disabled.tsx +44 -0
  484. package/templates/nextjs-standalone/src/ui/components/admin/health-badge.tsx +30 -0
  485. package/templates/nextjs-standalone/src/ui/components/admin/loading-state.tsx +14 -0
  486. package/templates/nextjs-standalone/src/ui/components/admin/stat-card.tsx +32 -0
  487. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +370 -0
  488. package/templates/nextjs-standalone/src/ui/components/chart/chart-detection.ts +261 -0
  489. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +375 -0
  490. package/templates/nextjs-standalone/src/ui/components/chat/api-key-bar.tsx +66 -0
  491. package/templates/nextjs-standalone/src/ui/components/chat/copy-button.tsx +25 -0
  492. package/templates/nextjs-standalone/src/ui/components/chat/data-table.tsx +102 -0
  493. package/templates/nextjs-standalone/src/ui/components/chat/error-banner.tsx +32 -0
  494. package/templates/nextjs-standalone/src/ui/components/chat/explore-card.tsx +41 -0
  495. package/templates/nextjs-standalone/src/ui/components/chat/loading-card.tsx +10 -0
  496. package/templates/nextjs-standalone/src/ui/components/chat/managed-auth-card.tsx +116 -0
  497. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +72 -0
  498. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +30 -0
  499. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +144 -0
  500. package/templates/nextjs-standalone/src/ui/components/chat/starter-prompts.ts +6 -0
  501. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +40 -0
  502. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +19 -0
  503. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +120 -0
  504. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-list.tsx +66 -0
  505. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-sidebar.tsx +78 -0
  506. package/templates/nextjs-standalone/src/ui/components/conversations/delete-confirmation.tsx +27 -0
  507. package/templates/nextjs-standalone/src/ui/context.tsx +78 -0
  508. package/templates/nextjs-standalone/src/ui/hooks/use-admin-fetch.ts +104 -0
  509. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +184 -0
  510. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -0
  511. package/templates/nextjs-standalone/src/ui/lib/action-types.ts +63 -0
  512. package/templates/nextjs-standalone/src/ui/lib/helpers.ts +104 -0
  513. package/templates/nextjs-standalone/src/ui/lib/types.ts +145 -0
  514. package/templates/nextjs-standalone/tsconfig.json +32 -0
  515. package/templates/nextjs-standalone/vercel.json +4 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Semantic layer utilities.
3
+ *
4
+ * Reads the semantic/ directory to extract metadata used by the SQL tool
5
+ * (table whitelist) and the CLI (schema profiling).
6
+ *
7
+ * Table whitelists are partitioned by connection ID when entity YAMLs use
8
+ * the `connection` field or when per-source subdirectories exist under the
9
+ * semantic root (e.g. `semantic/warehouse/entities/` infers connection ID
10
+ * `warehouse`). When no entity specifies a connection and no per-source
11
+ * subdirectories are present, all connections share the same whitelist
12
+ * (backward compat with single-DB).
13
+ */
14
+
15
+ import * as fs from "fs";
16
+ import * as path from "path";
17
+ import * as yaml from "js-yaml";
18
+ import { z } from "zod";
19
+ import { createLogger } from "@atlas/api/lib/logger";
20
+ import { invalidateSemanticIndex } from "@atlas/api/lib/semantic-index";
21
+
22
+ const log = createLogger("semantic");
23
+
24
+ const CrossSourceJoinShape = z.object({
25
+ source: z.string().min(1),
26
+ target_table: z.string().min(1),
27
+ on: z.string().min(1),
28
+ relationship: z.enum(["many_to_one", "one_to_many", "one_to_one", "many_to_many"]),
29
+ description: z.string().optional(),
30
+ });
31
+
32
+ type CrossSourceJoinRelationship = z.infer<typeof CrossSourceJoinShape>["relationship"];
33
+
34
+ /** Core entity shape — validates table name and connection only. */
35
+ const EntityShape = z.object({
36
+ table: z.string(),
37
+ connection: z.string().optional(),
38
+ }).passthrough();
39
+
40
+ export interface CrossSourceJoin {
41
+ fromSource: string;
42
+ fromTable: string;
43
+ toSource: string;
44
+ toTable: string;
45
+ on: string;
46
+ relationship: CrossSourceJoinRelationship;
47
+ description?: string;
48
+ }
49
+
50
+ const _whitelists = new Map<string, Set<string>>();
51
+ let _tablesByConnection: Map<string, Set<string>> | null = null;
52
+ let _crossSourceJoins: CrossSourceJoin[] | null = null;
53
+
54
+ /** Plugin-provided entity tables, keyed by connection ID. */
55
+ const _pluginEntities = new Map<string, Set<string>>();
56
+
57
+ /**
58
+ * Load entity YAMLs from a single directory into the connection map.
59
+ *
60
+ * @param dir - Directory containing *.yml entity files.
61
+ * @param defaultConnectionId - Connection ID for entities that don't specify
62
+ * an explicit `connection` field. When loading from a per-source subdirectory
63
+ * (e.g. `semantic/warehouse/entities/`), this is the subdirectory name.
64
+ * @param byConnection - Accumulator map to populate.
65
+ * @param crossJoins - Optional accumulator for cross-source join hints.
66
+ * When provided, valid `cross_source_joins` entries from each entity are
67
+ * appended here. Invalid individual join entries are skipped with a warning
68
+ * without affecting the entity's whitelist membership.
69
+ */
70
+ function loadEntitiesFromDir(
71
+ dir: string,
72
+ defaultConnectionId: string,
73
+ byConnection: Map<string, Set<string>>,
74
+ crossJoins?: CrossSourceJoin[],
75
+ ): void {
76
+ if (!fs.existsSync(dir)) return;
77
+
78
+ let files: string[];
79
+ try {
80
+ files = fs.readdirSync(dir).filter((f) => f.endsWith(".yml"));
81
+ } catch (err) {
82
+ log.error({ dir, err: err instanceof Error ? err.message : String(err) }, "Failed to read entities directory — skipping");
83
+ return;
84
+ }
85
+
86
+ for (const file of files) {
87
+ try {
88
+ const content = fs.readFileSync(path.join(dir, file), "utf-8");
89
+ const raw = yaml.load(content);
90
+ const parsed = EntityShape.safeParse(raw);
91
+ if (!parsed.success) {
92
+ // Try to extract the table name from raw YAML for a more useful log message
93
+ const tableName = raw && typeof raw === "object" && "table" in raw && typeof (raw as Record<string, unknown>).table === "string"
94
+ ? (raw as Record<string, unknown>).table as string
95
+ : undefined;
96
+ log.warn({ file, table: tableName, err: parsed.error.message }, "Skipping entity file — failed to validate");
97
+ continue;
98
+ }
99
+ const entity = parsed.data;
100
+
101
+ // Explicit connection field takes precedence over directory-based inference
102
+ const connId = entity.connection ?? defaultConnectionId;
103
+ if (!byConnection.has(connId)) byConnection.set(connId, new Set());
104
+ const tables = byConnection.get(connId)!;
105
+
106
+ // Extract table name (may include schema prefix like "public.users")
107
+ const parts = entity.table.split(".");
108
+ tables.add(parts[parts.length - 1].toLowerCase());
109
+ // Also add the full qualified name
110
+ tables.add(entity.table.toLowerCase());
111
+
112
+ // Validate and collect cross-source joins separately from core entity parsing.
113
+ // Invalid join entries are skipped individually without dropping the entity.
114
+ const rawJoins = (raw as Record<string, unknown>).cross_source_joins;
115
+ if (crossJoins && Array.isArray(rawJoins)) {
116
+ for (let i = 0; i < rawJoins.length; i++) {
117
+ const joinParsed = CrossSourceJoinShape.safeParse(rawJoins[i]);
118
+ if (!joinParsed.success) {
119
+ log.warn(
120
+ { file, table: entity.table, index: i, err: joinParsed.error.message },
121
+ "Skipping invalid cross_source_joins entry",
122
+ );
123
+ continue;
124
+ }
125
+ const j = joinParsed.data;
126
+ crossJoins.push({
127
+ fromSource: connId,
128
+ fromTable: entity.table,
129
+ toSource: j.source,
130
+ toTable: j.target_table,
131
+ on: j.on,
132
+ relationship: j.relationship,
133
+ description: j.description,
134
+ });
135
+ }
136
+ }
137
+ } catch (err) {
138
+ log.warn({ file, err: err instanceof Error ? err.message : String(err) }, "Skipping entity file — failed to parse");
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Directory names at the semantic root that are part of the default connection's
145
+ * structure, not per-source subdirectories. These are skipped when scanning for
146
+ * source-specific directories.
147
+ */
148
+ const RESERVED_DIRS = new Set(["entities", "metrics"]);
149
+
150
+ /**
151
+ * Load entity YAMLs and partition tables by connection ID.
152
+ *
153
+ * Supports two directory layouts:
154
+ * - **Flat (legacy):** `entitiesDir` points to a single directory of *.yml files.
155
+ * - **Multi-source:** `semanticRoot` points to the semantic root which contains
156
+ * `entities/` (default connection) and per-source subdirectories like
157
+ * `warehouse/entities/` whose name becomes the connection ID.
158
+ *
159
+ * @param semanticRoot - Semantic layer root directory (scans subdirectories).
160
+ * @param entitiesDir - Override for a single flat entities directory (DI for tests).
161
+ */
162
+ function loadTablesByConnection(
163
+ semanticRoot?: string,
164
+ entitiesDir?: string,
165
+ crossJoins?: CrossSourceJoin[],
166
+ ): Map<string, Set<string>> {
167
+ const byConnection = new Map<string, Set<string>>();
168
+
169
+ // Legacy flat-directory path (existing tests use this)
170
+ if (entitiesDir) {
171
+ loadEntitiesFromDir(entitiesDir, "default", byConnection, crossJoins);
172
+ return byConnection;
173
+ }
174
+
175
+ const root = semanticRoot ?? path.resolve(process.cwd(), "semantic");
176
+
177
+ // 1. Default entities (backward compat — semantic/entities/*.yml)
178
+ loadEntitiesFromDir(path.join(root, "entities"), "default", byConnection, crossJoins);
179
+
180
+ // 2. Per-source subdirectories (e.g. semantic/warehouse/entities/*.yml)
181
+ if (fs.existsSync(root)) {
182
+ let entries: fs.Dirent[];
183
+ try {
184
+ entries = fs.readdirSync(root, { withFileTypes: true });
185
+ } catch (err) {
186
+ log.error({ root, err: err instanceof Error ? err.message : String(err) }, "Failed to scan semantic root — skipping per-source discovery");
187
+ return byConnection;
188
+ }
189
+ for (const entry of entries) {
190
+ if (!entry.isDirectory()) continue;
191
+ if (RESERVED_DIRS.has(entry.name)) continue;
192
+ const subEntities = path.join(root, entry.name, "entities");
193
+ if (fs.existsSync(subEntities)) {
194
+ log.info({ source: entry.name, dir: subEntities }, "Discovered per-source entities directory");
195
+ loadEntitiesFromDir(subEntities, entry.name, byConnection, crossJoins);
196
+ }
197
+ }
198
+ }
199
+
200
+ const hasPartitioned = Array.from(byConnection.keys()).some((k) => k !== "default");
201
+ if (hasPartitioned) {
202
+ log.info({ connections: Array.from(byConnection.keys()) }, "Partitioned table whitelist mode");
203
+ }
204
+
205
+ return byConnection;
206
+ }
207
+
208
+ function getTablesByConnection(semanticRoot?: string, entitiesDir?: string): Map<string, Set<string>> {
209
+ if (!_tablesByConnection) {
210
+ const crossJoins: CrossSourceJoin[] = [];
211
+ _tablesByConnection = loadTablesByConnection(semanticRoot, entitiesDir, crossJoins);
212
+ _crossSourceJoins = crossJoins;
213
+ }
214
+ return _tablesByConnection;
215
+ }
216
+
217
+ /**
218
+ * Get the set of whitelisted table names for a given connection.
219
+ *
220
+ * The system switches to partitioned mode (each connection only sees its
221
+ * own tables) when either:
222
+ * - An entity YAML uses the `connection` field, or
223
+ * - Per-source subdirectories exist under the semantic root (e.g.
224
+ * `semantic/warehouse/entities/` infers connection `warehouse`).
225
+ *
226
+ * When neither trigger is present, all connections share the same table
227
+ * set (identical to pre-v0.7 behavior).
228
+ *
229
+ * @param connectionId - Connection to get tables for. Defaults to "default".
230
+ * @param entitiesDir - Override for a single flat entities directory (DI for tests).
231
+ * @param semanticRoot - Override for the semantic root directory (DI for tests).
232
+ * When provided, scans both `root/entities/` and per-source subdirectories.
233
+ */
234
+ export function getWhitelistedTables(
235
+ connectionId: string = "default",
236
+ entitiesDir?: string,
237
+ semanticRoot?: string,
238
+ ): Set<string> {
239
+ // When using custom paths (tests), bypass the global cache
240
+ if (entitiesDir || semanticRoot) {
241
+ const byConnection = loadTablesByConnection(semanticRoot, entitiesDir);
242
+ const hasNonDefaultConnection = Array.from(byConnection.keys()).some((k) => k !== "default");
243
+
244
+ let tables: Set<string>;
245
+ if (!hasNonDefaultConnection) {
246
+ tables = new Set(byConnection.get("default") ?? []);
247
+ } else {
248
+ tables = new Set(byConnection.get(connectionId) ?? []);
249
+ if (tables.size === 0) {
250
+ log.warn(
251
+ { connectionId, knownConnections: Array.from(byConnection.keys()) },
252
+ "No entities found for connection — whitelist is empty; all queries will be rejected",
253
+ );
254
+ }
255
+ }
256
+ // Merge plugin-provided entities even in custom-path mode
257
+ const pluginTables = _pluginEntities.get(connectionId);
258
+ if (pluginTables && pluginTables.size > 0) {
259
+ for (const t of pluginTables) tables.add(t);
260
+ }
261
+ return tables;
262
+ }
263
+
264
+ const cached = _whitelists.get(connectionId);
265
+ if (cached) return cached;
266
+
267
+ const byConnection = getTablesByConnection();
268
+
269
+ // Backward compat: if no entity uses `connection:` and no per-source
270
+ // subdirectories exist, all connections share the full set (pre-v0.7).
271
+ const hasNonDefaultConnection = Array.from(byConnection.keys()).some((k) => k !== "default");
272
+
273
+ let tables: Set<string>;
274
+ if (!hasNonDefaultConnection) {
275
+ tables = new Set(byConnection.get("default") ?? []);
276
+ } else {
277
+ tables = new Set(byConnection.get(connectionId) ?? []);
278
+ if (tables.size === 0) {
279
+ log.warn(
280
+ { connectionId, knownConnections: Array.from(byConnection.keys()) },
281
+ "No entities found for connection — whitelist is empty; all queries will be rejected",
282
+ );
283
+ }
284
+ }
285
+
286
+ // Merge plugin-provided entities for this connection
287
+ const pluginTables = _pluginEntities.get(connectionId);
288
+ if (pluginTables && pluginTables.size > 0) {
289
+ for (const t of pluginTables) tables.add(t);
290
+ }
291
+
292
+ _whitelists.set(connectionId, tables);
293
+ return tables;
294
+ }
295
+
296
+ /**
297
+ * Get all cross-source join hints parsed from entity YAMLs.
298
+ *
299
+ * When called with a `semanticRoot`, loads fresh from disk and returns a new
300
+ * array without affecting the global cache. When called without arguments,
301
+ * uses the global cache populated by `getTablesByConnection`.
302
+ */
303
+ export function getCrossSourceJoins(semanticRoot?: string): readonly CrossSourceJoin[] {
304
+ if (semanticRoot) {
305
+ const crossJoins: CrossSourceJoin[] = [];
306
+ loadTablesByConnection(semanticRoot, undefined, crossJoins);
307
+ return crossJoins;
308
+ }
309
+ // Ensure global cache is populated
310
+ getTablesByConnection();
311
+ return _crossSourceJoins ?? [];
312
+ }
313
+
314
+ /** Clears cached whitelists, table-by-connection mappings, cross-source joins, and semantic index. */
315
+ export function _resetWhitelists(): void {
316
+ _whitelists.clear();
317
+ _tablesByConnection = null;
318
+ _crossSourceJoins = null;
319
+ invalidateSemanticIndex();
320
+ }
321
+
322
+ /**
323
+ * Register plugin-provided entity definitions into the table whitelist.
324
+ *
325
+ * Parses each entity's YAML content using the same validation as disk-based
326
+ * entities. Tables are stored in a separate in-memory map that is merged
327
+ * into the whitelist on read. No files are written to disk.
328
+ *
329
+ * @param connectionId - Connection ID the entities belong to (usually the plugin ID).
330
+ * @param entities - Array of `{ name, yaml }` entity definitions.
331
+ */
332
+ export function registerPluginEntities(
333
+ connectionId: string,
334
+ entities: Array<{ name: string; yaml: string }>,
335
+ ): void {
336
+ if (!_pluginEntities.has(connectionId)) {
337
+ _pluginEntities.set(connectionId, new Set());
338
+ }
339
+ const tables = _pluginEntities.get(connectionId)!;
340
+
341
+ let skippedCount = 0;
342
+ for (const entity of entities) {
343
+ try {
344
+ const raw = yaml.load(entity.yaml);
345
+ const parsed = EntityShape.safeParse(raw);
346
+ if (!parsed.success) {
347
+ log.warn(
348
+ { connectionId, entity: entity.name, err: parsed.error.message },
349
+ "Skipping plugin entity — failed to validate YAML",
350
+ );
351
+ skippedCount++;
352
+ continue;
353
+ }
354
+ const tableName = parsed.data.table;
355
+ const parts = tableName.split(".");
356
+ tables.add(parts[parts.length - 1].toLowerCase());
357
+ tables.add(tableName.toLowerCase());
358
+ } catch (err) {
359
+ log.warn(
360
+ { connectionId, entity: entity.name, err: err instanceof Error ? err.message : String(err) },
361
+ "Skipping plugin entity — failed to parse YAML",
362
+ );
363
+ skippedCount++;
364
+ }
365
+ }
366
+
367
+ // Clear the merged whitelist cache so the next read picks up plugin entities
368
+ _whitelists.clear();
369
+
370
+ if (skippedCount === entities.length && entities.length > 0) {
371
+ log.error(
372
+ { connectionId, entityCount: entities.length, skippedCount, tableCount: tables.size },
373
+ "All plugin entities failed to register",
374
+ );
375
+ } else {
376
+ log.info(
377
+ { connectionId, entityCount: entities.length, skippedCount, tableCount: tables.size },
378
+ "Registered plugin entities",
379
+ );
380
+ }
381
+ }
382
+
383
+ /** Clears plugin-provided entity registrations. For testing. */
384
+ export function _resetPluginEntities(): void {
385
+ _pluginEntities.clear();
386
+ _whitelists.clear();
387
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Types for the sidecar HTTP contract.
3
+ * Used by both the sidecar server (packages/sandbox-sidecar) and
4
+ * the sidecar client (packages/api/src/lib/tools/explore-sidecar.ts).
5
+ */
6
+
7
+ export interface SidecarExecRequest {
8
+ command: string;
9
+ timeout?: number;
10
+ }
11
+
12
+ export interface SidecarExecResponse {
13
+ stdout: string;
14
+ stderr: string;
15
+ exitCode: number;
16
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Tests for the thin Slack Web API client (api.ts).
3
+ *
4
+ * Mocks global fetch to isolate HTTP behavior.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, afterEach, mock, type Mock } from "bun:test";
8
+
9
+ mock.module("@atlas/api/lib/logger", () => ({
10
+ createLogger: () => ({
11
+ info: () => {},
12
+ warn: () => {},
13
+ error: () => {},
14
+ debug: () => {},
15
+ }),
16
+ }));
17
+
18
+ const { slackAPI, postMessage, updateMessage } = await import("../api");
19
+
20
+ describe("api", () => {
21
+ const originalFetch = globalThis.fetch;
22
+ let mockFetch: Mock<typeof globalThis.fetch>;
23
+
24
+ beforeEach(() => {
25
+ mockFetch = mock() as unknown as Mock<typeof globalThis.fetch>;
26
+ globalThis.fetch = mockFetch as unknown as typeof globalThis.fetch;
27
+ });
28
+
29
+ afterEach(() => {
30
+ globalThis.fetch = originalFetch;
31
+ });
32
+
33
+ describe("slackAPI", () => {
34
+ it("successful call returns parsed response", async () => {
35
+ mockFetch.mockResolvedValue(
36
+ new Response(JSON.stringify({ ok: true, ts: "1234.5678", channel: "C123" }), {
37
+ status: 200,
38
+ headers: { "Content-Type": "application/json" },
39
+ }),
40
+ );
41
+
42
+ const result = await slackAPI("chat.postMessage", "xoxb-token", { channel: "C123", text: "hi" });
43
+ expect(result.ok).toBe(true);
44
+ if (result.ok) {
45
+ expect(result.ts).toBe("1234.5678");
46
+ expect(result.channel).toBe("C123");
47
+ }
48
+
49
+ // Verify fetch was called with correct URL and headers
50
+ expect(mockFetch).toHaveBeenCalledTimes(1);
51
+ const [url, init] = mockFetch.mock.calls[0];
52
+ expect(url).toBe("https://slack.com/api/chat.postMessage");
53
+ expect((init as RequestInit).method).toBe("POST");
54
+ expect((init as RequestInit).headers).toEqual(
55
+ expect.objectContaining({
56
+ Authorization: "Bearer xoxb-token",
57
+ }),
58
+ );
59
+ });
60
+
61
+ it("HTTP error (non-2xx) returns { ok: false, error }", async () => {
62
+ mockFetch.mockResolvedValue(
63
+ new Response("Internal Server Error", { status: 500 }),
64
+ );
65
+
66
+ const result = await slackAPI("chat.postMessage", "xoxb-token", { channel: "C123" });
67
+ expect(result.ok).toBe(false);
68
+ if (!result.ok) {
69
+ expect(result.error).toBe("HTTP 500");
70
+ }
71
+ });
72
+
73
+ it("network error (fetch throws) returns { ok: false, error }", async () => {
74
+ mockFetch.mockRejectedValue(new Error("ECONNREFUSED"));
75
+
76
+ const result = await slackAPI("chat.postMessage", "xoxb-token", { channel: "C123" });
77
+ expect(result.ok).toBe(false);
78
+ if (!result.ok) {
79
+ expect(result.error).toContain("request_failed");
80
+ expect(result.error).toContain("ECONNREFUSED");
81
+ }
82
+ });
83
+
84
+ it("malformed JSON response returns error", async () => {
85
+ mockFetch.mockResolvedValue(
86
+ new Response("not json at all{{{", {
87
+ status: 200,
88
+ headers: { "Content-Type": "application/json" },
89
+ }),
90
+ );
91
+
92
+ const result = await slackAPI("chat.postMessage", "xoxb-token", { channel: "C123" });
93
+ expect(result.ok).toBe(false);
94
+ if (!result.ok) {
95
+ expect(result.error).toContain("request_failed");
96
+ }
97
+ });
98
+
99
+ it("Slack-level error (HTTP 200, ok: false) returns the error", async () => {
100
+ mockFetch.mockResolvedValue(
101
+ new Response(JSON.stringify({ ok: false, error: "channel_not_found" }), {
102
+ status: 200,
103
+ headers: { "Content-Type": "application/json" },
104
+ }),
105
+ );
106
+
107
+ const result = await slackAPI("chat.postMessage", "xoxb-token", { channel: "CXXX" });
108
+ expect(result.ok).toBe(false);
109
+ if (!result.ok) {
110
+ expect(result.error).toBe("channel_not_found");
111
+ }
112
+ });
113
+ });
114
+
115
+ describe("postMessage", () => {
116
+ it("delegates to slackAPI with correct method", async () => {
117
+ mockFetch.mockResolvedValue(
118
+ new Response(JSON.stringify({ ok: true, ts: "1111.2222" }), { status: 200 }),
119
+ );
120
+
121
+ const result = await postMessage("xoxb-token", {
122
+ channel: "C123",
123
+ text: "hello",
124
+ thread_ts: "1000.0001",
125
+ });
126
+
127
+ expect(result.ok).toBe(true);
128
+ expect(mockFetch).toHaveBeenCalledTimes(1);
129
+ const [url, init] = mockFetch.mock.calls[0];
130
+ expect(url).toBe("https://slack.com/api/chat.postMessage");
131
+ const body = JSON.parse((init as RequestInit).body as string);
132
+ expect(body.channel).toBe("C123");
133
+ expect(body.text).toBe("hello");
134
+ expect(body.thread_ts).toBe("1000.0001");
135
+ });
136
+ });
137
+
138
+ describe("updateMessage", () => {
139
+ it("delegates to slackAPI with correct method", async () => {
140
+ mockFetch.mockResolvedValue(
141
+ new Response(JSON.stringify({ ok: true }), { status: 200 }),
142
+ );
143
+
144
+ const result = await updateMessage("xoxb-token", {
145
+ channel: "C123",
146
+ ts: "1111.2222",
147
+ text: "updated text",
148
+ });
149
+
150
+ expect(result.ok).toBe(true);
151
+ expect(mockFetch).toHaveBeenCalledTimes(1);
152
+ const [url, init] = mockFetch.mock.calls[0];
153
+ expect(url).toBe("https://slack.com/api/chat.update");
154
+ const body = JSON.parse((init as RequestInit).body as string);
155
+ expect(body.channel).toBe("C123");
156
+ expect(body.ts).toBe("1111.2222");
157
+ expect(body.text).toBe("updated text");
158
+ });
159
+ });
160
+ });