@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,238 @@
1
+ /**
2
+ * Tests for custom query validation (non-SQL datasource plugins).
3
+ *
4
+ * When a datasource plugin registers a `validate` function on its connection,
5
+ * executeSQL uses that validator instead of the standard SQL validation pipeline.
6
+ */
7
+
8
+ import { describe, expect, it, beforeEach, mock, type Mock } from "bun:test";
9
+
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ type AnyResult = any;
12
+
13
+ // --- Mock dependencies ---
14
+
15
+ mock.module("@atlas/api/lib/semantic", () => ({
16
+ getWhitelistedTables: () => new Set(["companies", "people"]),
17
+ _resetWhitelists: () => {},
18
+ }));
19
+
20
+ mock.module("@atlas/api/lib/auth/audit", () => ({
21
+ logQueryAudit: () => {},
22
+ }));
23
+
24
+ mock.module("@atlas/api/lib/tracing", () => ({
25
+ withSpan: async (
26
+ _name: string,
27
+ _attrs: Record<string, unknown>,
28
+ fn: () => Promise<unknown>,
29
+ ) => fn(),
30
+ }));
31
+
32
+ mock.module("@atlas/api/lib/db/source-rate-limit", () => ({
33
+ acquireSourceSlot: () => ({ acquired: true }),
34
+ decrementSourceConcurrency: () => {},
35
+ }));
36
+
37
+ mock.module("@atlas/api/lib/plugins/hooks", () => ({
38
+ dispatchHook: async () => {},
39
+ dispatchMutableHook: async (
40
+ _hookName: string,
41
+ ctx: { sql: string },
42
+ _field: string,
43
+ ) => ctx.sql,
44
+ }));
45
+
46
+ // --- Query mock ---
47
+
48
+ let queryFn: Mock<(sql: string, timeout: number) => Promise<{ columns: string[]; rows: Record<string, unknown>[] }>>;
49
+
50
+ const mockConn = {
51
+ query: (...args: [string, number]) => queryFn(...args),
52
+ close: async () => {},
53
+ };
54
+
55
+ // Custom validator: accepts SOQL-like "SELECT ... FROM ..." but rejects anything with DELETE/INSERT
56
+ function soqlValidator(query: string): { valid: boolean; reason?: string } {
57
+ if (/\b(DELETE|INSERT|UPDATE)\b/i.test(query)) {
58
+ return { valid: false, reason: "SOQL only supports SELECT queries" };
59
+ }
60
+ return { valid: true };
61
+ }
62
+
63
+ // Connections mock with getValidator support
64
+ let validatorMap: Map<string, ((q: string) => { valid: boolean; reason?: string }) | undefined>;
65
+
66
+ mock.module("@atlas/api/lib/db/connection", () => ({
67
+ getDB: () => mockConn,
68
+ connections: {
69
+ get: () => mockConn,
70
+ getDefault: () => mockConn,
71
+ getDBType: () => "postgres",
72
+ getTargetHost: () => "localhost",
73
+ list: () => ["default", "salesforce-plugin"],
74
+ getValidator: (id: string) => validatorMap.get(id),
75
+ },
76
+ detectDBType: () => "postgres",
77
+ }));
78
+
79
+ // Import after mocks
80
+ const { executeSQL } = await import("@atlas/api/lib/tools/sql");
81
+
82
+ const exec = (sql: string, connectionId?: string) =>
83
+ executeSQL.execute!(
84
+ { sql, explanation: "test", connectionId },
85
+ { toolCallId: "test", messages: [], abortSignal: undefined as never },
86
+ ) as Promise<AnyResult>;
87
+
88
+ describe("custom query validation", () => {
89
+ beforeEach(() => {
90
+ validatorMap = new Map();
91
+ queryFn = mock(() =>
92
+ Promise.resolve({ columns: ["id", "name"], rows: [{ id: 1, name: "Acme" }] }),
93
+ );
94
+ });
95
+
96
+ it("plugin with custom validator accepts valid queries", async () => {
97
+ validatorMap.set("salesforce-plugin", soqlValidator);
98
+
99
+ const result = await exec("SELECT Id, Name FROM Account", "salesforce-plugin");
100
+
101
+ expect(result.success).toBe(true);
102
+ expect(queryFn).toHaveBeenCalledTimes(1);
103
+ });
104
+
105
+ it("plugin with custom validator rejects invalid queries", async () => {
106
+ validatorMap.set("salesforce-plugin", soqlValidator);
107
+
108
+ const result = await exec("DELETE FROM Account WHERE Id = '001'", "salesforce-plugin");
109
+
110
+ expect(result.success).toBe(false);
111
+ expect(result.error).toContain("SOQL only supports SELECT queries");
112
+ expect(queryFn).not.toHaveBeenCalled();
113
+ });
114
+
115
+ it("falls back to validateSQL when no custom validator", async () => {
116
+ // No custom validator for default — standard SQL validation applies
117
+ validatorMap.set("default", undefined);
118
+
119
+ const result = await exec("SELECT * FROM companies");
120
+
121
+ expect(result.success).toBe(true);
122
+ expect(queryFn).toHaveBeenCalledTimes(1);
123
+ });
124
+
125
+ it("standard SQL validation rejects DML when no custom validator", async () => {
126
+ validatorMap.set("default", undefined);
127
+
128
+ const result = await exec("DROP TABLE companies");
129
+
130
+ expect(result.success).toBe(false);
131
+ expect(result.error).toContain("Forbidden SQL operation");
132
+ expect(queryFn).not.toHaveBeenCalled();
133
+ });
134
+
135
+ it("auto-LIMIT is skipped for custom-validated connections", async () => {
136
+ validatorMap.set("salesforce-plugin", soqlValidator);
137
+
138
+ await exec("SELECT Id FROM Account", "salesforce-plugin");
139
+
140
+ // The query passed to db.query should NOT have LIMIT appended
141
+ const executedQuery = queryFn.mock.calls[0]![0];
142
+ expect(executedQuery).not.toContain("LIMIT");
143
+ });
144
+
145
+ it("auto-LIMIT is applied for standard SQL connections", async () => {
146
+ validatorMap.set("default", undefined);
147
+
148
+ await exec("SELECT * FROM companies");
149
+
150
+ const executedQuery = queryFn.mock.calls[0]![0];
151
+ expect(executedQuery).toContain("LIMIT");
152
+ });
153
+
154
+ it("handles validator that throws an exception", async () => {
155
+ validatorMap.set("broken-plugin", () => { throw new Error("validator crashed"); });
156
+
157
+ const result = await exec("SELECT 1", "broken-plugin");
158
+
159
+ expect(result.success).toBe(false);
160
+ expect(result.error).toContain("internal validator error");
161
+ expect(queryFn).not.toHaveBeenCalled();
162
+ });
163
+
164
+ it("handles validator that returns undefined", async () => {
165
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
+ validatorMap.set("bad-plugin", (() => undefined) as any);
167
+
168
+ const result = await exec("SELECT 1", "bad-plugin");
169
+
170
+ expect(result.success).toBe(false);
171
+ expect(result.error).toContain("misconfigured");
172
+ expect(queryFn).not.toHaveBeenCalled();
173
+ });
174
+
175
+ it("handles validator that returns non-boolean valid field", async () => {
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ validatorMap.set("bad-valid", (() => ({ valid: "yes" })) as any);
178
+
179
+ const result = await exec("SELECT 1", "bad-valid");
180
+
181
+ expect(result.success).toBe(false);
182
+ expect(result.error).toContain("misconfigured");
183
+ expect(queryFn).not.toHaveBeenCalled();
184
+ });
185
+
186
+ it("normalizes SQL before passing to custom validator (trims whitespace and trailing semicolons)", async () => {
187
+ let receivedQuery = "";
188
+ validatorMap.set("trim-check", (q: string) => {
189
+ receivedQuery = q;
190
+ return { valid: true };
191
+ });
192
+
193
+ await exec(" SELECT Id FROM Account ; ", "trim-check");
194
+
195
+ expect(receivedQuery).toBe("SELECT Id FROM Account");
196
+ expect(queryFn).toHaveBeenCalledTimes(1);
197
+ });
198
+
199
+ it("custom validator used for re-validation after hook mutation", async () => {
200
+ // Install a custom validator that rejects queries containing "FORBIDDEN"
201
+ const strictValidator = (q: string): { valid: boolean; reason?: string } => {
202
+ if (q.includes("FORBIDDEN")) return { valid: false, reason: "contains forbidden keyword" };
203
+ return { valid: true };
204
+ };
205
+ validatorMap.set("custom-conn", strictValidator);
206
+
207
+ // The hook rewrites to include "FORBIDDEN" — custom validator should catch it
208
+ mock.module("@atlas/api/lib/plugins/hooks", () => ({
209
+ dispatchHook: async () => {},
210
+ dispatchMutableHook: async (
211
+ _hookName: string,
212
+ _ctx: { sql: string },
213
+ _field: string,
214
+ ) => "SELECT FORBIDDEN FROM data",
215
+ }));
216
+
217
+ // Re-import to pick up the new mock
218
+ const { executeSQL: executeSQLv2 } = await import("@atlas/api/lib/tools/sql");
219
+
220
+ const result = await (executeSQLv2.execute!(
221
+ { sql: "SELECT Id FROM data", explanation: "query data", connectionId: "custom-conn" },
222
+ { toolCallId: "t7", messages: [], abortSignal: undefined as never },
223
+ ) as Promise<AnyResult>);
224
+
225
+ expect(result.success).toBe(false);
226
+ expect(result.error).toContain("contains forbidden keyword");
227
+
228
+ // Restore original mock
229
+ mock.module("@atlas/api/lib/plugins/hooks", () => ({
230
+ dispatchHook: async () => {},
231
+ dispatchMutableHook: async (
232
+ _hookName: string,
233
+ ctx: { sql: string },
234
+ _field: string,
235
+ ) => ctx.sql,
236
+ }));
237
+ });
238
+ });
@@ -0,0 +1,267 @@
1
+ import { describe, expect, it, beforeEach, afterEach, spyOn } from "bun:test";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers: We need fresh module state for each test since explore.ts has
5
+ // module-level variables (_nsjailAvailable, _nsjailFailed, backendPromise).
6
+ // We use dynamic imports with cache-busting to get fresh module instances.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ let testCounter = 0;
10
+
11
+ /** Import a fresh copy of explore.ts with all module state reset. */
12
+ async function freshExploreModule() {
13
+ // Bun caches modules by resolved path. We can't bust the cache directly,
14
+ // so we rely on mock.module and re-import. Instead, we'll test the exported
15
+ // functions by understanding their stateful behavior and resetting env vars.
16
+ //
17
+ // Since we can't easily reset module-level let bindings from outside,
18
+ // we'll structure tests to work with the module's caching behavior.
19
+ testCounter++;
20
+ // Use a unique query param to bust the module cache
21
+ const mod = await import(
22
+ `@atlas/api/lib/tools/explore?t=${testCounter}`
23
+ );
24
+ return mod;
25
+ }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Tests for useNsjail / getExploreBackendType / getExploreBackend
29
+ // ---------------------------------------------------------------------------
30
+
31
+ describe("explore backend selection", () => {
32
+ const originalEnv = { ...process.env };
33
+
34
+ beforeEach(() => {
35
+ // Clean env for each test
36
+ delete process.env.ATLAS_RUNTIME;
37
+ delete process.env.VERCEL;
38
+ delete process.env.ATLAS_SANDBOX;
39
+ delete process.env.ATLAS_SANDBOX_URL;
40
+ delete process.env.ATLAS_NSJAIL_PATH;
41
+ });
42
+
43
+ afterEach(() => {
44
+ process.env = { ...originalEnv };
45
+ });
46
+
47
+ describe("useNsjail via getExploreBackendType", () => {
48
+ it("returns 'nsjail' when ATLAS_SANDBOX=nsjail (regardless of binary)", async () => {
49
+ process.env.ATLAS_SANDBOX = "nsjail";
50
+ const mod = await freshExploreModule();
51
+ expect(mod.getExploreBackendType()).toBe("nsjail");
52
+ });
53
+
54
+ it("returns 'nsjail' when nsjail binary is available on PATH", async () => {
55
+ // Set a PATH that has nsjail available
56
+ process.env.ATLAS_NSJAIL_PATH = "/usr/local/bin/nsjail";
57
+ // Mock fs.accessSync to report nsjail exists
58
+ const fs = await import("fs");
59
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {});
60
+
61
+ const mod = await freshExploreModule();
62
+ expect(mod.getExploreBackendType()).toBe("nsjail");
63
+
64
+ spy.mockRestore();
65
+ });
66
+
67
+ it("returns 'just-bash' when nsjail is not available", async () => {
68
+ delete process.env.ATLAS_SANDBOX;
69
+ delete process.env.ATLAS_NSJAIL_PATH;
70
+ process.env.PATH = "/usr/bin:/bin";
71
+ // Mock fs.accessSync to always throw (nsjail not found)
72
+ const fs = await import("fs");
73
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {
74
+ throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
75
+ });
76
+
77
+ const mod = await freshExploreModule();
78
+ expect(mod.getExploreBackendType()).toBe("just-bash");
79
+
80
+ spy.mockRestore();
81
+ });
82
+
83
+ it("returns 'vercel-sandbox' when ATLAS_RUNTIME=vercel", async () => {
84
+ process.env.ATLAS_RUNTIME = "vercel";
85
+ const mod = await freshExploreModule();
86
+ expect(mod.getExploreBackendType()).toBe("vercel-sandbox");
87
+ });
88
+
89
+ it("returns 'vercel-sandbox' when VERCEL env is set", async () => {
90
+ process.env.VERCEL = "1";
91
+ const mod = await freshExploreModule();
92
+ expect(mod.getExploreBackendType()).toBe("vercel-sandbox");
93
+ });
94
+
95
+ it("vercel-sandbox takes priority over nsjail", async () => {
96
+ process.env.ATLAS_RUNTIME = "vercel";
97
+ process.env.ATLAS_SANDBOX = "nsjail";
98
+ const mod = await freshExploreModule();
99
+ expect(mod.getExploreBackendType()).toBe("vercel-sandbox");
100
+ });
101
+ });
102
+
103
+ describe("useSidecar via getExploreBackendType", () => {
104
+ it("returns 'sidecar' when ATLAS_SANDBOX_URL is set and nsjail unavailable", async () => {
105
+ process.env.ATLAS_SANDBOX_URL = "http://localhost:8080";
106
+ delete process.env.ATLAS_NSJAIL_PATH;
107
+ process.env.PATH = "/usr/bin:/bin";
108
+ const fs = await import("fs");
109
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {
110
+ throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
111
+ });
112
+
113
+ const mod = await freshExploreModule();
114
+ expect(mod.getExploreBackendType()).toBe("sidecar");
115
+
116
+ spy.mockRestore();
117
+ });
118
+
119
+ it("sidecar takes priority over nsjail auto-detect when ATLAS_SANDBOX_URL is set", async () => {
120
+ process.env.ATLAS_SANDBOX_URL = "http://localhost:8080";
121
+ process.env.ATLAS_NSJAIL_PATH = "/usr/local/bin/nsjail";
122
+ const fs = await import("fs");
123
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {});
124
+
125
+ const mod = await freshExploreModule();
126
+ expect(mod.getExploreBackendType()).toBe("sidecar");
127
+
128
+ spy.mockRestore();
129
+ });
130
+
131
+ it("explicit nsjail (ATLAS_SANDBOX=nsjail) still beats sidecar", async () => {
132
+ process.env.ATLAS_SANDBOX = "nsjail";
133
+ process.env.ATLAS_SANDBOX_URL = "http://localhost:8080";
134
+ const mod = await freshExploreModule();
135
+ expect(mod.getExploreBackendType()).toBe("nsjail");
136
+ });
137
+
138
+ it("nsjail auto-detect works when no ATLAS_SANDBOX_URL is set", async () => {
139
+ delete process.env.ATLAS_SANDBOX_URL;
140
+ process.env.ATLAS_NSJAIL_PATH = "/usr/local/bin/nsjail";
141
+ const fs = await import("fs");
142
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {});
143
+
144
+ const mod = await freshExploreModule();
145
+ expect(mod.getExploreBackendType()).toBe("nsjail");
146
+
147
+ spy.mockRestore();
148
+ });
149
+
150
+ it("vercel-sandbox takes priority over sidecar", async () => {
151
+ process.env.ATLAS_RUNTIME = "vercel";
152
+ process.env.ATLAS_SANDBOX_URL = "http://localhost:8080";
153
+ const mod = await freshExploreModule();
154
+ expect(mod.getExploreBackendType()).toBe("vercel-sandbox");
155
+ });
156
+ });
157
+
158
+ describe("nsjail failure handling", () => {
159
+ it("falls back to just-bash after nsjail init failure (_nsjailFailed)", async () => {
160
+ // We can't directly set _nsjailFailed, but we can trigger the fallback
161
+ // by having nsjail available but failing to initialize.
162
+ // The getExploreBackendType checks _nsjailFailed flag.
163
+ // Since each fresh import resets the flag, we need to trigger the
164
+ // failure path through getExploreBackend first.
165
+
166
+ process.env.ATLAS_NSJAIL_PATH = "/usr/local/bin/nsjail";
167
+ const fs = await import("fs");
168
+ const spy = spyOn(fs, "accessSync").mockImplementation(
169
+ (p: import("fs").PathLike) => {
170
+ const path = String(p);
171
+ if (path === "/usr/local/bin/nsjail") return;
172
+ // Semantic root not readable — causes createNsjailBackend to throw
173
+ throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
174
+ },
175
+ );
176
+
177
+ const mod = await freshExploreModule();
178
+ // Initially nsjail is detected as available
179
+ expect(mod.getExploreBackendType()).toBe("nsjail");
180
+
181
+ // Trigger the explore tool to attempt nsjail init (it will fail)
182
+ // We access the internal getExploreBackend via the tool's execute.
183
+ // The tool.execute wraps getExploreBackend and returns error string on failure.
184
+ const result = await mod.explore.execute(
185
+ { command: "ls" },
186
+ { toolCallId: "test", messages: [], abortSignal: new AbortController().signal },
187
+ );
188
+ // The error should mention the nsjail failure or backend issue
189
+ expect(typeof result).toBe("string");
190
+
191
+ // After failure with auto-detected nsjail (not ATLAS_SANDBOX=nsjail),
192
+ // _nsjailFailed is set to true, so it falls back to just-bash
193
+ // But the next getExploreBackendType should reflect just-bash
194
+ // Note: this only works if the module state is shared
195
+ // Since _nsjailFailed is set in getExploreBackend's catch block
196
+ expect(mod.getExploreBackendType()).toBe("just-bash");
197
+
198
+ spy.mockRestore();
199
+ });
200
+
201
+ it("throws (does NOT fall back) when ATLAS_SANDBOX=nsjail and binary is missing", async () => {
202
+ process.env.ATLAS_SANDBOX = "nsjail";
203
+ process.env.PATH = "";
204
+ delete process.env.ATLAS_NSJAIL_PATH;
205
+
206
+ const fs = await import("fs");
207
+ const spy = spyOn(fs, "accessSync").mockImplementation(() => {
208
+ throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
209
+ });
210
+
211
+ const mod = await freshExploreModule();
212
+ // getExploreBackendType says nsjail because ATLAS_SANDBOX=nsjail
213
+ // bypasses binary check
214
+ expect(mod.getExploreBackendType()).toBe("nsjail");
215
+
216
+ // But actually trying to create the backend will fail with a throw,
217
+ // NOT fall back to just-bash
218
+ const result = await mod.explore.execute(
219
+ { command: "ls" },
220
+ { toolCallId: "test", messages: [], abortSignal: new AbortController().signal },
221
+ );
222
+ expect(typeof result).toBe("string");
223
+ expect(result).toContain("nsjail was explicitly requested");
224
+
225
+ spy.mockRestore();
226
+ });
227
+ });
228
+
229
+ describe("useNsjail unexpected error logging", () => {
230
+ it("logs unexpected errors that are not MODULE_NOT_FOUND", async () => {
231
+ // We need to make the require("./explore-nsjail") throw a non-MODULE_NOT_FOUND error.
232
+ // This is hard to test with real modules, but we can verify the code path exists
233
+ // by checking that _nsjailAvailable is set to false on unexpected errors.
234
+ // The console.error call is the observable side effect.
235
+
236
+ const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
237
+
238
+ // Remove ATLAS_SANDBOX so useNsjail goes through auto-detection
239
+ delete process.env.ATLAS_SANDBOX;
240
+ delete process.env.ATLAS_NSJAIL_PATH;
241
+ process.env.PATH = "";
242
+
243
+ const fs = await import("fs");
244
+ const fsSpy = spyOn(fs, "accessSync").mockImplementation(() => {
245
+ throw Object.assign(new Error("ENOENT"), { code: "ENOENT" });
246
+ });
247
+
248
+ const mod = await freshExploreModule();
249
+ // This should exercise the useNsjail auto-detection path.
250
+ // Since nsjail is not found, it returns just-bash.
251
+ const result = mod.getExploreBackendType();
252
+ expect(result).toBe("just-bash");
253
+
254
+ consoleSpy.mockRestore();
255
+ fsSpy.mockRestore();
256
+ });
257
+ });
258
+
259
+ describe("invalidateExploreBackend", () => {
260
+ it("clears cached backend so next call recreates it", async () => {
261
+ const mod = await freshExploreModule();
262
+ // Just verify the function exists and is callable
263
+ expect(typeof mod.invalidateExploreBackend).toBe("function");
264
+ mod.invalidateExploreBackend(); // Should not throw
265
+ });
266
+ });
267
+ });