@useatlas/create 0.0.2 → 0.0.3

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 (296) hide show
  1. package/README.md +4 -18
  2. package/index.ts +191 -31
  3. package/package.json +1 -1
  4. package/templates/docker/.env.example +3 -3
  5. package/templates/docker/Dockerfile.sidecar +28 -0
  6. package/templates/docker/bin/__tests__/plugin-cli.test.ts +9 -9
  7. package/templates/docker/bin/atlas.ts +108 -44
  8. package/templates/docker/data/demo-semantic/catalog.yml +51 -27
  9. package/templates/docker/data/demo-semantic/entities/accounts.yml +95 -103
  10. package/templates/docker/data/demo-semantic/entities/companies.yml +88 -152
  11. package/templates/docker/data/demo-semantic/entities/people.yml +82 -95
  12. package/templates/docker/data/demo-semantic/glossary.yml +104 -8
  13. package/templates/docker/data/demo-semantic/metrics/accounts.yml +62 -23
  14. package/templates/docker/data/demo-semantic/metrics/companies.yml +52 -78
  15. package/templates/docker/docker-compose.yml +1 -1
  16. package/templates/docker/docs/deploy.md +2 -39
  17. package/templates/docker/package.json +17 -1
  18. package/templates/docker/semantic/catalog.yml +62 -3
  19. package/templates/docker/semantic/entities/accounts.yml +162 -0
  20. package/templates/docker/semantic/entities/companies.yml +143 -0
  21. package/templates/docker/semantic/entities/people.yml +132 -0
  22. package/templates/docker/semantic/glossary.yml +116 -4
  23. package/templates/docker/semantic/metrics/accounts.yml +77 -0
  24. package/templates/docker/semantic/metrics/companies.yml +63 -0
  25. package/templates/docker/sidecar/Dockerfile +5 -6
  26. package/templates/docker/sidecar/railway.json +1 -2
  27. package/templates/docker/src/api/__tests__/admin.test.ts +7 -7
  28. package/templates/docker/src/api/__tests__/health-plugin.test.ts +7 -0
  29. package/templates/docker/src/api/__tests__/health.test.ts +30 -8
  30. package/templates/docker/src/api/routes/admin.ts +549 -8
  31. package/templates/docker/src/api/routes/chat.ts +5 -20
  32. package/templates/docker/src/api/routes/health.ts +39 -27
  33. package/templates/docker/src/api/routes/openapi.ts +1329 -74
  34. package/templates/docker/src/api/routes/query.ts +2 -1
  35. package/templates/docker/src/api/server.ts +27 -0
  36. package/templates/docker/src/app/api/[...route]/route.ts +2 -2
  37. package/templates/docker/src/app/globals.css +13 -12
  38. package/templates/docker/src/app/layout.tsx +9 -2
  39. package/templates/docker/src/components/ui/alert-dialog.tsx +196 -0
  40. package/templates/docker/src/components/ui/badge.tsx +48 -0
  41. package/templates/docker/src/components/ui/button.tsx +64 -0
  42. package/templates/docker/src/components/ui/card.tsx +92 -0
  43. package/templates/docker/src/components/ui/collapsible.tsx +33 -0
  44. package/templates/docker/src/components/ui/command.tsx +184 -0
  45. package/templates/docker/src/components/ui/dialog.tsx +158 -0
  46. package/templates/docker/src/components/ui/dropdown-menu.tsx +257 -0
  47. package/templates/docker/src/components/ui/input.tsx +21 -0
  48. package/templates/docker/src/components/ui/scroll-area.tsx +58 -0
  49. package/templates/docker/src/components/ui/select.tsx +190 -0
  50. package/templates/docker/src/components/ui/separator.tsx +28 -0
  51. package/templates/docker/src/components/ui/sheet.tsx +143 -0
  52. package/templates/docker/src/components/ui/sidebar.tsx +726 -0
  53. package/templates/docker/src/components/ui/skeleton.tsx +13 -0
  54. package/templates/docker/src/components/ui/table.tsx +116 -0
  55. package/templates/docker/src/components/ui/tabs.tsx +91 -0
  56. package/templates/docker/src/components/ui/toggle-group.tsx +83 -0
  57. package/templates/docker/src/components/ui/toggle.tsx +47 -0
  58. package/templates/docker/src/components/ui/tooltip.tsx +57 -0
  59. package/templates/docker/src/hooks/use-mobile.ts +19 -0
  60. package/templates/docker/src/lib/__tests__/agent-cache.test.ts +2 -0
  61. package/templates/docker/src/lib/__tests__/agent-dialect.test.ts +17 -0
  62. package/templates/docker/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  63. package/templates/docker/src/lib/__tests__/agent-integration.test.ts +2 -0
  64. package/templates/docker/src/lib/__tests__/config.test.ts +69 -19
  65. package/templates/docker/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  66. package/templates/docker/src/lib/__tests__/providers.test.ts +32 -1
  67. package/templates/docker/src/lib/__tests__/startup-actions.test.ts +9 -0
  68. package/templates/docker/src/lib/__tests__/startup-first-run.test.ts +429 -0
  69. package/templates/docker/src/lib/__tests__/startup.test.ts +5 -0
  70. package/templates/docker/src/lib/agent-query.ts +5 -23
  71. package/templates/docker/src/lib/agent.ts +32 -112
  72. package/templates/docker/src/lib/auth/__tests__/migrate.test.ts +5 -3
  73. package/templates/docker/src/lib/auth/middleware.ts +30 -4
  74. package/templates/docker/src/lib/auth/migrate.ts +97 -0
  75. package/templates/docker/src/lib/auth/server.ts +12 -1
  76. package/templates/docker/src/lib/config.ts +37 -39
  77. package/templates/docker/src/lib/db/__tests__/connection.test.ts +89 -14
  78. package/templates/docker/src/lib/db/__tests__/registry-health.test.ts +1 -18
  79. package/templates/docker/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  80. package/templates/docker/src/lib/db/__tests__/registry.test.ts +11 -208
  81. package/templates/docker/src/lib/db/connection.ts +87 -265
  82. package/templates/docker/src/lib/db/internal.ts +6 -1
  83. package/templates/docker/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  84. package/templates/docker/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  85. package/templates/docker/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  86. package/templates/docker/src/lib/plugins/__tests__/registry.test.ts +32 -5
  87. package/templates/docker/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  88. package/templates/docker/src/lib/plugins/index.ts +4 -1
  89. package/templates/docker/src/lib/plugins/migrate.ts +103 -0
  90. package/templates/docker/src/lib/plugins/registry.ts +12 -6
  91. package/templates/docker/src/lib/plugins/wiring.ts +113 -4
  92. package/templates/docker/src/lib/providers.ts +6 -1
  93. package/templates/docker/src/lib/security.ts +24 -0
  94. package/templates/docker/src/lib/semantic.ts +2 -0
  95. package/templates/docker/src/lib/sidecar-types.ts +12 -1
  96. package/templates/docker/src/lib/startup.ts +71 -101
  97. package/templates/docker/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  98. package/templates/docker/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  99. package/templates/docker/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  100. package/templates/docker/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  101. package/templates/docker/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  102. package/templates/docker/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  103. package/templates/docker/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  104. package/templates/docker/src/lib/tools/__tests__/python.test.ts +331 -0
  105. package/templates/docker/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  106. package/templates/docker/src/lib/tools/__tests__/registry.test.ts +38 -31
  107. package/templates/docker/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  108. package/templates/docker/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  109. package/templates/docker/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  110. package/templates/docker/src/lib/tools/__tests__/sql.test.ts +5 -308
  111. package/templates/docker/src/lib/tools/explore-nsjail.ts +17 -12
  112. package/templates/docker/src/lib/tools/explore-sidecar.ts +25 -0
  113. package/templates/docker/src/lib/tools/explore.ts +28 -32
  114. package/templates/docker/src/lib/tools/python-nsjail.ts +396 -0
  115. package/templates/docker/src/lib/tools/python-sandbox.ts +476 -0
  116. package/templates/docker/src/lib/tools/python-sidecar.ts +150 -0
  117. package/templates/docker/src/lib/tools/python.ts +367 -0
  118. package/templates/docker/src/lib/tools/registry.ts +49 -22
  119. package/templates/docker/src/lib/tools/sql.ts +88 -88
  120. package/templates/docker/src/types/vercel-sandbox.d.ts +7 -0
  121. package/templates/docker/src/ui/components/admin/admin-layout.tsx +77 -8
  122. package/templates/docker/src/ui/components/admin/admin-sidebar.tsx +25 -17
  123. package/templates/docker/src/ui/components/admin/change-password-dialog.tsx +128 -0
  124. package/templates/docker/src/ui/components/admin/entity-detail.tsx +3 -3
  125. package/templates/docker/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  126. package/templates/docker/src/ui/components/atlas-chat.tsx +64 -12
  127. package/templates/docker/src/ui/components/chart/result-chart.tsx +25 -15
  128. package/templates/docker/src/ui/components/chat/markdown.tsx +88 -42
  129. package/templates/docker/src/ui/components/chat/python-result-card.tsx +244 -0
  130. package/templates/docker/src/ui/components/chat/sql-block.tsx +39 -15
  131. package/templates/docker/src/ui/components/chat/sql-result-card.tsx +6 -1
  132. package/templates/docker/src/ui/components/chat/tool-part.tsx +12 -3
  133. package/templates/docker/src/ui/components/chat/typing-indicator.tsx +5 -2
  134. package/templates/docker/src/ui/components/conversations/conversation-item.tsx +25 -20
  135. package/templates/docker/src/ui/context.tsx +1 -1
  136. package/templates/docker/src/ui/hooks/use-conversations.ts +3 -3
  137. package/templates/docker/src/ui/hooks/use-dark-mode.ts +17 -10
  138. package/templates/docker/tsconfig.json +2 -2
  139. package/templates/nextjs-standalone/.env.example +1 -1
  140. package/templates/nextjs-standalone/bin/__tests__/plugin-cli.test.ts +9 -9
  141. package/templates/nextjs-standalone/bin/atlas.ts +108 -44
  142. package/templates/nextjs-standalone/data/demo-semantic/catalog.yml +51 -27
  143. package/templates/nextjs-standalone/data/demo-semantic/entities/accounts.yml +95 -103
  144. package/templates/nextjs-standalone/data/demo-semantic/entities/companies.yml +88 -152
  145. package/templates/nextjs-standalone/data/demo-semantic/entities/people.yml +82 -95
  146. package/templates/nextjs-standalone/data/demo-semantic/glossary.yml +104 -8
  147. package/templates/nextjs-standalone/data/demo-semantic/metrics/accounts.yml +62 -23
  148. package/templates/nextjs-standalone/data/demo-semantic/metrics/companies.yml +52 -78
  149. package/templates/nextjs-standalone/docs/deploy.md +2 -39
  150. package/templates/nextjs-standalone/package.json +11 -2
  151. package/templates/nextjs-standalone/scripts/migrate-auth.ts +25 -0
  152. package/templates/nextjs-standalone/scripts/seed-demo.ts +94 -0
  153. package/templates/nextjs-standalone/semantic/catalog.yml +62 -3
  154. package/templates/nextjs-standalone/semantic/entities/accounts.yml +162 -0
  155. package/templates/nextjs-standalone/semantic/entities/companies.yml +143 -0
  156. package/templates/nextjs-standalone/semantic/entities/people.yml +132 -0
  157. package/templates/nextjs-standalone/semantic/glossary.yml +116 -4
  158. package/templates/nextjs-standalone/semantic/metrics/accounts.yml +77 -0
  159. package/templates/nextjs-standalone/semantic/metrics/companies.yml +63 -0
  160. package/templates/nextjs-standalone/src/api/__tests__/admin.test.ts +7 -7
  161. package/templates/nextjs-standalone/src/api/__tests__/health-plugin.test.ts +7 -0
  162. package/templates/nextjs-standalone/src/api/__tests__/health.test.ts +30 -8
  163. package/templates/nextjs-standalone/src/api/routes/admin.ts +549 -8
  164. package/templates/nextjs-standalone/src/api/routes/chat.ts +5 -20
  165. package/templates/nextjs-standalone/src/api/routes/health.ts +39 -27
  166. package/templates/nextjs-standalone/src/api/routes/openapi.ts +1329 -74
  167. package/templates/nextjs-standalone/src/api/routes/query.ts +2 -1
  168. package/templates/nextjs-standalone/src/api/server.ts +27 -0
  169. package/templates/nextjs-standalone/src/app/api/[...route]/route.ts +2 -2
  170. package/templates/nextjs-standalone/src/app/globals.css +13 -12
  171. package/templates/nextjs-standalone/src/app/layout.tsx +9 -2
  172. package/templates/nextjs-standalone/src/components/ui/alert-dialog.tsx +196 -0
  173. package/templates/nextjs-standalone/src/components/ui/badge.tsx +48 -0
  174. package/templates/nextjs-standalone/src/components/ui/button.tsx +64 -0
  175. package/templates/nextjs-standalone/src/components/ui/card.tsx +92 -0
  176. package/templates/nextjs-standalone/src/components/ui/collapsible.tsx +33 -0
  177. package/templates/nextjs-standalone/src/components/ui/command.tsx +184 -0
  178. package/templates/nextjs-standalone/src/components/ui/dialog.tsx +158 -0
  179. package/templates/nextjs-standalone/src/components/ui/dropdown-menu.tsx +257 -0
  180. package/templates/nextjs-standalone/src/components/ui/input.tsx +21 -0
  181. package/templates/nextjs-standalone/src/components/ui/scroll-area.tsx +58 -0
  182. package/templates/nextjs-standalone/src/components/ui/select.tsx +190 -0
  183. package/templates/nextjs-standalone/src/components/ui/separator.tsx +28 -0
  184. package/templates/nextjs-standalone/src/components/ui/sheet.tsx +143 -0
  185. package/templates/nextjs-standalone/src/components/ui/sidebar.tsx +726 -0
  186. package/templates/nextjs-standalone/src/components/ui/skeleton.tsx +13 -0
  187. package/templates/nextjs-standalone/src/components/ui/table.tsx +116 -0
  188. package/templates/nextjs-standalone/src/components/ui/tabs.tsx +91 -0
  189. package/templates/nextjs-standalone/src/components/ui/toggle-group.tsx +83 -0
  190. package/templates/nextjs-standalone/src/components/ui/toggle.tsx +47 -0
  191. package/templates/nextjs-standalone/src/components/ui/tooltip.tsx +57 -0
  192. package/templates/nextjs-standalone/src/hooks/use-mobile.ts +19 -0
  193. package/templates/nextjs-standalone/src/lib/__tests__/agent-cache.test.ts +2 -0
  194. package/templates/nextjs-standalone/src/lib/__tests__/agent-dialect.test.ts +17 -0
  195. package/templates/nextjs-standalone/src/lib/__tests__/agent-health-annotations.test.ts +2 -0
  196. package/templates/nextjs-standalone/src/lib/__tests__/agent-integration.test.ts +2 -0
  197. package/templates/nextjs-standalone/src/lib/__tests__/config.test.ts +69 -19
  198. package/templates/nextjs-standalone/src/lib/__tests__/plugin-aware-validation.test.ts +321 -0
  199. package/templates/nextjs-standalone/src/lib/__tests__/providers.test.ts +32 -1
  200. package/templates/nextjs-standalone/src/lib/__tests__/startup-actions.test.ts +9 -0
  201. package/templates/nextjs-standalone/src/lib/__tests__/startup-first-run.test.ts +429 -0
  202. package/templates/nextjs-standalone/src/lib/__tests__/startup.test.ts +5 -0
  203. package/templates/nextjs-standalone/src/lib/agent-query.ts +5 -23
  204. package/templates/nextjs-standalone/src/lib/agent.ts +32 -112
  205. package/templates/nextjs-standalone/src/lib/auth/__tests__/migrate.test.ts +5 -3
  206. package/templates/nextjs-standalone/src/lib/auth/middleware.ts +30 -4
  207. package/templates/nextjs-standalone/src/lib/auth/migrate.ts +97 -0
  208. package/templates/nextjs-standalone/src/lib/auth/server.ts +12 -1
  209. package/templates/nextjs-standalone/src/lib/config.ts +37 -39
  210. package/templates/nextjs-standalone/src/lib/db/__tests__/connection.test.ts +89 -14
  211. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-health.test.ts +1 -18
  212. package/templates/nextjs-standalone/src/lib/db/__tests__/registry-pool-limits.test.ts +0 -19
  213. package/templates/nextjs-standalone/src/lib/db/__tests__/registry.test.ts +11 -208
  214. package/templates/nextjs-standalone/src/lib/db/connection.ts +87 -265
  215. package/templates/nextjs-standalone/src/lib/db/internal.ts +6 -1
  216. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks-integration.test.ts +3 -1
  217. package/templates/nextjs-standalone/src/lib/plugins/__tests__/hooks.test.ts +2 -2
  218. package/templates/nextjs-standalone/src/lib/plugins/__tests__/migrate.test.ts +355 -1
  219. package/templates/nextjs-standalone/src/lib/plugins/__tests__/registry.test.ts +32 -5
  220. package/templates/nextjs-standalone/src/lib/plugins/__tests__/wiring.test.ts +228 -14
  221. package/templates/nextjs-standalone/src/lib/plugins/index.ts +4 -1
  222. package/templates/nextjs-standalone/src/lib/plugins/migrate.ts +103 -0
  223. package/templates/nextjs-standalone/src/lib/plugins/registry.ts +12 -6
  224. package/templates/nextjs-standalone/src/lib/plugins/wiring.ts +113 -4
  225. package/templates/nextjs-standalone/src/lib/providers.ts +6 -1
  226. package/templates/nextjs-standalone/src/lib/security.ts +24 -0
  227. package/templates/nextjs-standalone/src/lib/semantic.ts +2 -0
  228. package/templates/nextjs-standalone/src/lib/sidecar-types.ts +12 -1
  229. package/templates/nextjs-standalone/src/lib/startup.ts +71 -101
  230. package/templates/nextjs-standalone/src/lib/tools/__tests__/custom-validation.test.ts +2 -0
  231. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-nsjail.test.ts +32 -18
  232. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-plugin.test.ts +14 -14
  233. package/templates/nextjs-standalone/src/lib/tools/__tests__/explore-sidecar.test.ts +5 -3
  234. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-nsjail.test.ts +515 -0
  235. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sandbox.test.ts +397 -0
  236. package/templates/nextjs-standalone/src/lib/tools/__tests__/python-sidecar.test.ts +365 -0
  237. package/templates/nextjs-standalone/src/lib/tools/__tests__/python.test.ts +331 -0
  238. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry-actions.test.ts +1 -13
  239. package/templates/nextjs-standalone/src/lib/tools/__tests__/registry.test.ts +38 -31
  240. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-audit.test.ts +2 -0
  241. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-connection-whitelist.test.ts +2 -0
  242. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-ratelimit.test.ts +2 -0
  243. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql.test.ts +5 -308
  244. package/templates/nextjs-standalone/src/lib/tools/explore-nsjail.ts +17 -12
  245. package/templates/nextjs-standalone/src/lib/tools/explore-sidecar.ts +25 -0
  246. package/templates/nextjs-standalone/src/lib/tools/explore.ts +28 -32
  247. package/templates/nextjs-standalone/src/lib/tools/python-nsjail.ts +396 -0
  248. package/templates/nextjs-standalone/src/lib/tools/python-sandbox.ts +476 -0
  249. package/templates/nextjs-standalone/src/lib/tools/python-sidecar.ts +150 -0
  250. package/templates/nextjs-standalone/src/lib/tools/python.ts +367 -0
  251. package/templates/nextjs-standalone/src/lib/tools/registry.ts +49 -22
  252. package/templates/nextjs-standalone/src/lib/tools/sql.ts +88 -88
  253. package/templates/nextjs-standalone/src/ui/components/admin/admin-layout.tsx +77 -8
  254. package/templates/nextjs-standalone/src/ui/components/admin/admin-sidebar.tsx +25 -17
  255. package/templates/nextjs-standalone/src/ui/components/admin/change-password-dialog.tsx +128 -0
  256. package/templates/nextjs-standalone/src/ui/components/admin/entity-detail.tsx +3 -3
  257. package/templates/nextjs-standalone/src/ui/components/admin/semantic-file-tree.tsx +159 -0
  258. package/templates/nextjs-standalone/src/ui/components/atlas-chat.tsx +64 -12
  259. package/templates/nextjs-standalone/src/ui/components/chart/result-chart.tsx +25 -15
  260. package/templates/nextjs-standalone/src/ui/components/chat/markdown.tsx +88 -42
  261. package/templates/nextjs-standalone/src/ui/components/chat/python-result-card.tsx +244 -0
  262. package/templates/nextjs-standalone/src/ui/components/chat/sql-block.tsx +39 -15
  263. package/templates/nextjs-standalone/src/ui/components/chat/sql-result-card.tsx +6 -1
  264. package/templates/nextjs-standalone/src/ui/components/chat/tool-part.tsx +12 -3
  265. package/templates/nextjs-standalone/src/ui/components/chat/typing-indicator.tsx +5 -2
  266. package/templates/nextjs-standalone/src/ui/components/conversations/conversation-item.tsx +25 -20
  267. package/templates/nextjs-standalone/src/ui/context.tsx +1 -1
  268. package/templates/nextjs-standalone/src/ui/hooks/use-conversations.ts +3 -3
  269. package/templates/nextjs-standalone/src/ui/hooks/use-dark-mode.ts +17 -10
  270. package/templates/nextjs-standalone/tsconfig.json +0 -1
  271. package/templates/nextjs-standalone/vercel.json +4 -1
  272. package/templates/docker/render.yaml +0 -34
  273. package/templates/docker/semantic/entities/.gitkeep +0 -0
  274. package/templates/docker/semantic/metrics/.gitkeep +0 -0
  275. package/templates/docker/src/lib/db/__tests__/duckdb.test.ts +0 -141
  276. package/templates/docker/src/lib/db/__tests__/salesforce.test.ts +0 -339
  277. package/templates/docker/src/lib/db/__tests__/snowflake.test.ts +0 -217
  278. package/templates/docker/src/lib/db/duckdb.ts +0 -122
  279. package/templates/docker/src/lib/db/salesforce.ts +0 -342
  280. package/templates/docker/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  281. package/templates/docker/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  282. package/templates/docker/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  283. package/templates/docker/src/lib/tools/salesforce.ts +0 -138
  284. package/templates/docker/src/lib/tools/soql-validation.ts +0 -172
  285. package/templates/nextjs-standalone/semantic/entities/.gitkeep +0 -0
  286. package/templates/nextjs-standalone/semantic/metrics/.gitkeep +0 -0
  287. package/templates/nextjs-standalone/src/lib/db/__tests__/duckdb.test.ts +0 -141
  288. package/templates/nextjs-standalone/src/lib/db/__tests__/salesforce.test.ts +0 -339
  289. package/templates/nextjs-standalone/src/lib/db/__tests__/snowflake.test.ts +0 -217
  290. package/templates/nextjs-standalone/src/lib/db/duckdb.ts +0 -122
  291. package/templates/nextjs-standalone/src/lib/db/salesforce.ts +0 -342
  292. package/templates/nextjs-standalone/src/lib/tools/__tests__/salesforce-tool.test.ts +0 -154
  293. package/templates/nextjs-standalone/src/lib/tools/__tests__/soql-validation.test.ts +0 -303
  294. package/templates/nextjs-standalone/src/lib/tools/__tests__/sql-duckdb.test.ts +0 -233
  295. package/templates/nextjs-standalone/src/lib/tools/salesforce.ts +0 -138
  296. package/templates/nextjs-standalone/src/lib/tools/soql-validation.ts +0 -172
@@ -1,154 +0,0 @@
1
- /**
2
- * Tests for the querySalesforce agent tool.
3
- */
4
- import { describe, it, expect, beforeEach, mock } from "bun:test";
5
-
6
- // Mock Salesforce registry
7
- const mockQuery = mock(() =>
8
- Promise.resolve({
9
- columns: ["Id", "Name"],
10
- rows: [
11
- { Id: "001", Name: "Acme" },
12
- { Id: "002", Name: "Widget Co" },
13
- ],
14
- }),
15
- );
16
-
17
- const mockSources = new Map<string, { query: typeof mockQuery; close: () => Promise<void> }>();
18
-
19
- mock.module("@atlas/api/lib/db/salesforce", () => ({
20
- getSalesforceSource: (id: string) => {
21
- const source = mockSources.get(id);
22
- if (!source) throw new Error(`Salesforce source "${id}" is not registered.`);
23
- return source;
24
- },
25
- listSalesforceSources: () => Array.from(mockSources.keys()),
26
- }));
27
-
28
- // Mock semantic layer
29
- mock.module("@atlas/api/lib/semantic", () => ({
30
- getWhitelistedTables: () => new Set(["account", "contact", "opportunity"]),
31
- _resetWhitelists: () => {},
32
- }));
33
-
34
- // Mock audit log (no-op)
35
- mock.module("@atlas/api/lib/auth/audit", () => ({
36
- logQueryAudit: () => {},
37
- }));
38
-
39
- const { querySalesforce } = await import("@atlas/api/lib/tools/salesforce");
40
-
41
- // Helper to call the tool's execute function
42
- async function executeTool(params: {
43
- soql: string;
44
- explanation: string;
45
- connectionId?: string;
46
- }) {
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- return (querySalesforce as any).execute(params);
49
- }
50
-
51
- describe("querySalesforce tool", () => {
52
- beforeEach(() => {
53
- mockQuery.mockClear();
54
- mockQuery.mockImplementation(() =>
55
- Promise.resolve({
56
- columns: ["Id", "Name"],
57
- rows: [
58
- { Id: "001", Name: "Acme" },
59
- { Id: "002", Name: "Widget Co" },
60
- ],
61
- }),
62
- );
63
- mockSources.clear();
64
- mockSources.set("default", {
65
- query: mockQuery,
66
- close: async () => {},
67
- });
68
- });
69
-
70
- it("executes a valid query and returns results", async () => {
71
- const result = await executeTool({
72
- soql: "SELECT Id, Name FROM Account",
73
- explanation: "Get all accounts",
74
- });
75
- expect(result.success).toBe(true);
76
- expect(result.columns).toEqual(["Id", "Name"]);
77
- expect(result.rows).toHaveLength(2);
78
- expect(result.row_count).toBe(2);
79
- });
80
-
81
- it("rejects invalid SOQL (mutation)", async () => {
82
- const result = await executeTool({
83
- soql: "DELETE FROM Account",
84
- explanation: "Delete accounts",
85
- });
86
- expect(result.success).toBe(false);
87
- expect(result.error).toContain("Forbidden");
88
- });
89
-
90
- it("rejects queries against non-whitelisted objects", async () => {
91
- const result = await executeTool({
92
- soql: "SELECT Id FROM CustomObject__c",
93
- explanation: "Get custom objects",
94
- });
95
- expect(result.success).toBe(false);
96
- expect(result.error).toContain("not in the allowed list");
97
- });
98
-
99
- it("returns error for unregistered connection", async () => {
100
- const result = await executeTool({
101
- soql: "SELECT Id FROM Account",
102
- explanation: "test",
103
- connectionId: "nonexistent",
104
- });
105
- expect(result.success).toBe(false);
106
- expect(result.error).toContain("not registered");
107
- });
108
-
109
- it("appends LIMIT when not present", async () => {
110
- await executeTool({
111
- soql: "SELECT Id FROM Account",
112
- explanation: "test",
113
- });
114
- // The mockQuery should have been called with a SOQL that includes LIMIT
115
- const calledWith = (mockQuery.mock.calls as unknown as string[][])[0][0];
116
- expect(calledWith).toContain("LIMIT");
117
- });
118
-
119
- it("does not double-append LIMIT", async () => {
120
- await executeTool({
121
- soql: "SELECT Id FROM Account LIMIT 5",
122
- explanation: "test",
123
- });
124
- const calledWith = (mockQuery.mock.calls as unknown as string[][])[0][0];
125
- // Should not have two LIMIT clauses
126
- const limitCount = (calledWith.match(/LIMIT/gi) ?? []).length;
127
- expect(limitCount).toBe(1);
128
- });
129
-
130
- it("scrubs sensitive error messages", async () => {
131
- mockQuery.mockImplementationOnce(() =>
132
- Promise.reject(new Error("INVALID_SESSION_ID: Session expired")),
133
- );
134
- const result = await executeTool({
135
- soql: "SELECT Id FROM Account",
136
- explanation: "test",
137
- });
138
- expect(result.success).toBe(false);
139
- expect(result.error).toContain("check server logs");
140
- expect(result.error).not.toContain("INVALID_SESSION_ID");
141
- });
142
-
143
- it("surfaces non-sensitive errors to the agent", async () => {
144
- mockQuery.mockImplementationOnce(() =>
145
- Promise.reject(new Error("INVALID_FIELD: No such column 'Foo'")),
146
- );
147
- const result = await executeTool({
148
- soql: "SELECT Foo FROM Account",
149
- explanation: "test",
150
- });
151
- expect(result.success).toBe(false);
152
- expect(result.error).toContain("INVALID_FIELD");
153
- });
154
- });
@@ -1,303 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import { validateSOQL, appendSOQLLimit } from "../soql-validation";
3
-
4
- const ALLOWED = new Set(["Account", "Contact", "Opportunity", "Lead"]);
5
-
6
- describe("validateSOQL", () => {
7
- describe("Layer 0: Empty check", () => {
8
- it("rejects empty string", () => {
9
- const result = validateSOQL("", ALLOWED);
10
- expect(result.valid).toBe(false);
11
- expect(result.error).toContain("Empty");
12
- });
13
-
14
- it("rejects whitespace-only", () => {
15
- const result = validateSOQL(" \n\t ", ALLOWED);
16
- expect(result.valid).toBe(false);
17
- expect(result.error).toContain("Empty");
18
- });
19
- });
20
-
21
- describe("Layer 1: Mutation guard", () => {
22
- for (const keyword of ["INSERT", "UPDATE", "DELETE", "UPSERT", "MERGE", "UNDELETE"]) {
23
- it(`rejects ${keyword}`, () => {
24
- const result = validateSOQL(`${keyword} INTO Account`, ALLOWED);
25
- expect(result.valid).toBe(false);
26
- expect(result.error).toContain("Forbidden");
27
- });
28
-
29
- it(`rejects ${keyword.toLowerCase()}`, () => {
30
- const result = validateSOQL(`${keyword.toLowerCase()} into account`, ALLOWED);
31
- expect(result.valid).toBe(false);
32
- expect(result.error).toContain("Forbidden");
33
- });
34
- }
35
- });
36
-
37
- describe("Layer 2: SELECT-only", () => {
38
- it("accepts SELECT query", () => {
39
- const result = validateSOQL("SELECT Id FROM Account", ALLOWED);
40
- expect(result.valid).toBe(true);
41
- });
42
-
43
- it("rejects non-SELECT query", () => {
44
- const result = validateSOQL("DESCRIBE Account", ALLOWED);
45
- expect(result.valid).toBe(false);
46
- expect(result.error).toContain("Only SELECT");
47
- });
48
-
49
- it("rejects semicolons", () => {
50
- const result = validateSOQL("SELECT Id FROM Account;", ALLOWED);
51
- expect(result.valid).toBe(false);
52
- expect(result.error).toContain("Semicolons");
53
- });
54
-
55
- it("rejects multiple statements", () => {
56
- const result = validateSOQL("SELECT Id FROM Account; SELECT Id FROM Contact", ALLOWED);
57
- expect(result.valid).toBe(false);
58
- expect(result.error).toContain("Semicolons");
59
- });
60
- });
61
-
62
- describe("Layer 3: Object whitelist", () => {
63
- it("allows whitelisted objects", () => {
64
- const result = validateSOQL("SELECT Id, Name FROM Account", ALLOWED);
65
- expect(result.valid).toBe(true);
66
- });
67
-
68
- it("rejects non-whitelisted objects", () => {
69
- const result = validateSOQL("SELECT Id FROM CustomObject__c", ALLOWED);
70
- expect(result.valid).toBe(false);
71
- expect(result.error).toContain("not in the allowed list");
72
- });
73
-
74
- it("checks subquery objects", () => {
75
- const result = validateSOQL(
76
- "SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM CustomObject__c)",
77
- ALLOWED,
78
- );
79
- expect(result.valid).toBe(false);
80
- expect(result.error).toContain("CustomObject__c");
81
- });
82
-
83
- it("allows subquery with whitelisted objects", () => {
84
- const result = validateSOQL(
85
- "SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Contact)",
86
- ALLOWED,
87
- );
88
- expect(result.valid).toBe(true);
89
- });
90
-
91
- it("is case-insensitive", () => {
92
- const result = validateSOQL("SELECT Id FROM account", ALLOWED);
93
- expect(result.valid).toBe(true);
94
- });
95
-
96
- it("rejects queries with no FROM clause", () => {
97
- const result = validateSOQL("SELECT 1", ALLOWED);
98
- expect(result.valid).toBe(false);
99
- expect(result.error).toContain("No FROM");
100
- });
101
- });
102
-
103
- describe("Relationship subquery whitelist bypass (parent-to-child)", () => {
104
- it("accepts parent-to-child relationship subquery with plural relationship name", () => {
105
- // "Contacts" is the relationship name (plural), not in the whitelist.
106
- // Only "Contact" (singular) is whitelisted. This should pass because
107
- // relationship subqueries in SELECT are not whitelist-checked.
108
- const result = validateSOQL(
109
- "SELECT Id, Name, (SELECT LastName FROM Contacts) FROM Account",
110
- ALLOWED,
111
- );
112
- expect(result.valid).toBe(true);
113
- });
114
-
115
- it("accepts multiple relationship subqueries in SELECT", () => {
116
- const result = validateSOQL(
117
- "SELECT Id, (SELECT LastName FROM Contacts), (SELECT Amount FROM Opportunities) FROM Account",
118
- ALLOWED,
119
- );
120
- expect(result.valid).toBe(true);
121
- });
122
-
123
- it("accepts relationship subquery with unknown relationship name", () => {
124
- // Custom relationship names like "Cases" won't be in the whitelist
125
- const result = validateSOQL(
126
- "SELECT Id, (SELECT Subject FROM Cases) FROM Account",
127
- ALLOWED,
128
- );
129
- expect(result.valid).toBe(true);
130
- });
131
-
132
- it("still rejects non-whitelisted objects in WHERE semi-join subqueries", () => {
133
- // Semi-join subqueries in WHERE reference real object names — must be checked
134
- const result = validateSOQL(
135
- "SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM CustomObject__c)",
136
- ALLOWED,
137
- );
138
- expect(result.valid).toBe(false);
139
- expect(result.error).toContain("CustomObject__c");
140
- });
141
-
142
- it("allows whitelisted objects in WHERE semi-join subqueries", () => {
143
- const result = validateSOQL(
144
- "SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Contact)",
145
- ALLOWED,
146
- );
147
- expect(result.valid).toBe(true);
148
- });
149
-
150
- it("accepts relationship subquery AND valid WHERE subquery together", () => {
151
- const result = validateSOQL(
152
- "SELECT Id, (SELECT LastName FROM Contacts) FROM Account WHERE Id IN (SELECT AccountId FROM Opportunity)",
153
- ALLOWED,
154
- );
155
- expect(result.valid).toBe(true);
156
- });
157
-
158
- it("rejects relationship subquery with invalid WHERE subquery", () => {
159
- const result = validateSOQL(
160
- "SELECT Id, (SELECT LastName FROM Contacts) FROM Account WHERE Id IN (SELECT AccountId FROM Forbidden__c)",
161
- ALLOWED,
162
- );
163
- expect(result.valid).toBe(false);
164
- expect(result.error).toContain("Forbidden__c");
165
- });
166
-
167
- it("still checks top-level FROM object", () => {
168
- const result = validateSOQL(
169
- "SELECT Id, (SELECT LastName FROM Contacts) FROM NotAllowed__c",
170
- ALLOWED,
171
- );
172
- expect(result.valid).toBe(false);
173
- expect(result.error).toContain("NotAllowed__c");
174
- });
175
- });
176
-
177
- describe("String literal false positives in mutation guard", () => {
178
- it("allows 'delete' inside a string literal", () => {
179
- const result = validateSOQL(
180
- "SELECT Id FROM Account WHERE Name = 'delete this'",
181
- ALLOWED,
182
- );
183
- expect(result.valid).toBe(true);
184
- });
185
-
186
- it("allows 'update' inside a string literal", () => {
187
- const result = validateSOQL(
188
- "SELECT Id FROM Account WHERE Description = 'please update record'",
189
- ALLOWED,
190
- );
191
- expect(result.valid).toBe(true);
192
- });
193
-
194
- it("allows 'insert' inside a string literal", () => {
195
- const result = validateSOQL(
196
- "SELECT Id FROM Contact WHERE Name = 'insert coin'",
197
- ALLOWED,
198
- );
199
- expect(result.valid).toBe(true);
200
- });
201
-
202
- it("allows 'merge' inside a string literal", () => {
203
- const result = validateSOQL(
204
- "SELECT Id FROM Lead WHERE Status = 'merge pending'",
205
- ALLOWED,
206
- );
207
- expect(result.valid).toBe(true);
208
- });
209
-
210
- it("allows 'upsert' inside a string literal", () => {
211
- const result = validateSOQL(
212
- "SELECT Id FROM Account WHERE Name = 'upsert test'",
213
- ALLOWED,
214
- );
215
- expect(result.valid).toBe(true);
216
- });
217
-
218
- it("allows LIKE pattern with forbidden keyword", () => {
219
- const result = validateSOQL(
220
- "SELECT Id FROM Account WHERE Name LIKE '%delete%'",
221
- ALLOWED,
222
- );
223
- expect(result.valid).toBe(true);
224
- });
225
-
226
- it("still rejects actual DELETE statements", () => {
227
- const result = validateSOQL("DELETE FROM Account", ALLOWED);
228
- expect(result.valid).toBe(false);
229
- expect(result.error).toContain("Forbidden");
230
- });
231
-
232
- it("still rejects forbidden keyword outside string literal even with strings present", () => {
233
- // The keyword DELETE appears outside the string
234
- const result = validateSOQL(
235
- "DELETE FROM Account WHERE Name = 'safe string'",
236
- ALLOWED,
237
- );
238
- expect(result.valid).toBe(false);
239
- expect(result.error).toContain("Forbidden");
240
- });
241
-
242
- it("handles multiple string literals with forbidden keywords", () => {
243
- const result = validateSOQL(
244
- "SELECT Id FROM Account WHERE Name = 'delete' AND Type = 'update this'",
245
- ALLOWED,
246
- );
247
- expect(result.valid).toBe(true);
248
- });
249
-
250
- it("handles empty string literals", () => {
251
- const result = validateSOQL(
252
- "SELECT Id FROM Account WHERE Name = ''",
253
- ALLOWED,
254
- );
255
- expect(result.valid).toBe(true);
256
- });
257
- });
258
-
259
- describe("valid queries", () => {
260
- it("accepts basic query", () => {
261
- const result = validateSOQL("SELECT Id, Name FROM Account LIMIT 10", ALLOWED);
262
- expect(result.valid).toBe(true);
263
- });
264
-
265
- it("accepts query with WHERE clause", () => {
266
- const result = validateSOQL(
267
- "SELECT Id, Name FROM Account WHERE Name = 'Test'",
268
- ALLOWED,
269
- );
270
- expect(result.valid).toBe(true);
271
- });
272
-
273
- it("accepts query with aggregate functions", () => {
274
- const result = validateSOQL(
275
- "SELECT COUNT(Id) FROM Opportunity GROUP BY StageName",
276
- ALLOWED,
277
- );
278
- expect(result.valid).toBe(true);
279
- });
280
- });
281
- });
282
-
283
- describe("appendSOQLLimit", () => {
284
- it("appends LIMIT when not present", () => {
285
- const result = appendSOQLLimit("SELECT Id FROM Account", 100);
286
- expect(result).toBe("SELECT Id FROM Account LIMIT 100");
287
- });
288
-
289
- it("does not append LIMIT when already present", () => {
290
- const result = appendSOQLLimit("SELECT Id FROM Account LIMIT 50", 100);
291
- expect(result).toBe("SELECT Id FROM Account LIMIT 50");
292
- });
293
-
294
- it("is case-insensitive for existing LIMIT", () => {
295
- const result = appendSOQLLimit("SELECT Id FROM Account limit 50", 100);
296
- expect(result).toBe("SELECT Id FROM Account limit 50");
297
- });
298
-
299
- it("trims whitespace", () => {
300
- const result = appendSOQLLimit(" SELECT Id FROM Account ", 100);
301
- expect(result).toBe("SELECT Id FROM Account LIMIT 100");
302
- });
303
- });
@@ -1,233 +0,0 @@
1
- /**
2
- * SQL validation tests specific to DuckDB queries.
3
- *
4
- * Verifies that:
5
- * - Standard SELECT queries pass validation in DuckDB mode
6
- * - DuckDB-specific forbidden operations (PRAGMA, ATTACH, etc.) are blocked
7
- * - Common DuckDB query patterns work with the PostgreSQL parser mode
8
- */
9
- import { describe, expect, it, beforeEach, afterEach, mock } from "bun:test";
10
-
11
- // Mock semantic layer
12
- mock.module("@atlas/api/lib/semantic", () => ({
13
- getWhitelistedTables: () =>
14
- new Set(["sales", "customers", "products", "orders"]),
15
- _resetWhitelists: () => {},
16
- }));
17
-
18
- // Mock the DB connection
19
- const mockDBConnection = {
20
- query: async () => ({ columns: [], rows: [] }),
21
- close: async () => {},
22
- };
23
-
24
- const mockDetectDBType = () => {
25
- const url = process.env.ATLAS_DATASOURCE_URL ?? "";
26
- if (url.startsWith("duckdb://")) return "duckdb";
27
- if (url.startsWith("postgresql://")) return "postgres";
28
- throw new Error(`Unsupported: ${url}`);
29
- };
30
-
31
- mock.module("@atlas/api/lib/db/connection", () => ({
32
- getDB: () => mockDBConnection,
33
- connections: {
34
- get: () => mockDBConnection,
35
- getDefault: () => mockDBConnection,
36
- getDBType: () => mockDetectDBType(),
37
- getValidator: () => undefined,
38
- list: () => ["default"],
39
- },
40
- detectDBType: mockDetectDBType,
41
- }));
42
-
43
- const { validateSQL } = await import("@atlas/api/lib/tools/sql");
44
-
45
- const origEnv = { ...process.env };
46
-
47
- describe("validateSQL — DuckDB mode", () => {
48
- beforeEach(() => {
49
- process.env.ATLAS_DATASOURCE_URL = "duckdb://:memory:";
50
- });
51
-
52
- afterEach(() => {
53
- if (origEnv.ATLAS_DATASOURCE_URL === undefined) {
54
- delete process.env.ATLAS_DATASOURCE_URL;
55
- } else {
56
- process.env.ATLAS_DATASOURCE_URL = origEnv.ATLAS_DATASOURCE_URL;
57
- }
58
- });
59
-
60
- // --- Valid queries ---
61
-
62
- it("allows simple SELECT", () => {
63
- const result = validateSQL("SELECT * FROM sales");
64
- expect(result.valid).toBe(true);
65
- });
66
-
67
- it("allows SELECT with aggregate functions", () => {
68
- const result = validateSQL(
69
- "SELECT COUNT(*), SUM(amount) FROM sales GROUP BY product_id"
70
- );
71
- expect(result.valid).toBe(true);
72
- });
73
-
74
- it("allows SELECT with CTE", () => {
75
- const result = validateSQL(
76
- "WITH totals AS (SELECT customer_id, SUM(amount) AS total FROM sales GROUP BY customer_id) SELECT * FROM totals"
77
- );
78
- expect(result.valid).toBe(true);
79
- });
80
-
81
- it("allows SELECT with window function", () => {
82
- const result = validateSQL(
83
- "SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS rn FROM sales"
84
- );
85
- expect(result.valid).toBe(true);
86
- });
87
-
88
- it("allows SELECT with JOIN", () => {
89
- const result = validateSQL(
90
- "SELECT s.*, c.name FROM sales s JOIN customers c ON s.customer_id = c.id"
91
- );
92
- expect(result.valid).toBe(true);
93
- });
94
-
95
- it("allows CAST expressions", () => {
96
- const result = validateSQL(
97
- "SELECT CAST(amount AS VARCHAR) FROM sales"
98
- );
99
- expect(result.valid).toBe(true);
100
- });
101
-
102
- it("allows COALESCE and CASE", () => {
103
- const result = validateSQL(
104
- "SELECT COALESCE(name, 'unknown'), CASE WHEN amount > 100 THEN 'high' ELSE 'low' END FROM sales"
105
- );
106
- expect(result.valid).toBe(true);
107
- });
108
-
109
- // --- Blocked operations ---
110
-
111
- it("blocks PRAGMA", () => {
112
- const result = validateSQL("PRAGMA database_list");
113
- expect(result.valid).toBe(false);
114
- expect(result.error).toContain("Forbidden");
115
- });
116
-
117
- it("blocks ATTACH", () => {
118
- const result = validateSQL("ATTACH '/tmp/other.duckdb' AS other");
119
- expect(result.valid).toBe(false);
120
- expect(result.error).toContain("Forbidden");
121
- });
122
-
123
- it("blocks DETACH", () => {
124
- const result = validateSQL("DETACH other");
125
- expect(result.valid).toBe(false);
126
- expect(result.error).toContain("Forbidden");
127
- });
128
-
129
- it("blocks INSTALL", () => {
130
- const result = validateSQL("INSTALL httpfs");
131
- expect(result.valid).toBe(false);
132
- expect(result.error).toContain("Forbidden");
133
- });
134
-
135
- it("blocks EXPORT", () => {
136
- const result = validateSQL("EXPORT DATABASE '/tmp/backup'");
137
- expect(result.valid).toBe(false);
138
- expect(result.error).toContain("Forbidden");
139
- });
140
-
141
- it("blocks IMPORT", () => {
142
- const result = validateSQL("IMPORT DATABASE '/tmp/backup'");
143
- expect(result.valid).toBe(false);
144
- expect(result.error).toContain("Forbidden");
145
- });
146
-
147
- it("blocks CHECKPOINT", () => {
148
- const result = validateSQL("CHECKPOINT");
149
- expect(result.valid).toBe(false);
150
- expect(result.error).toContain("Forbidden");
151
- });
152
-
153
- it("blocks INSERT", () => {
154
- const result = validateSQL("INSERT INTO sales VALUES (1, 100)");
155
- expect(result.valid).toBe(false);
156
- expect(result.error).toContain("Forbidden");
157
- });
158
-
159
- it("blocks CREATE TABLE", () => {
160
- const result = validateSQL("CREATE TABLE foo (id INT)");
161
- expect(result.valid).toBe(false);
162
- expect(result.error).toContain("Forbidden");
163
- });
164
-
165
- it("blocks DROP TABLE", () => {
166
- const result = validateSQL("DROP TABLE sales");
167
- expect(result.valid).toBe(false);
168
- expect(result.error).toContain("Forbidden");
169
- });
170
-
171
- it("blocks DESCRIBE", () => {
172
- const result = validateSQL("DESCRIBE sales");
173
- expect(result.valid).toBe(false);
174
- expect(result.error).toContain("Forbidden");
175
- });
176
-
177
- it("blocks SHOW", () => {
178
- const result = validateSQL("SHOW TABLES");
179
- expect(result.valid).toBe(false);
180
- expect(result.error).toContain("Forbidden");
181
- });
182
-
183
- // --- Table whitelist ---
184
-
185
- it("rejects queries on non-whitelisted tables", () => {
186
- const result = validateSQL("SELECT * FROM secret_data");
187
- expect(result.valid).toBe(false);
188
- expect(result.error).toContain("not in the allowed list");
189
- });
190
-
191
- it("allows queries on whitelisted tables", () => {
192
- const result = validateSQL("SELECT * FROM customers");
193
- expect(result.valid).toBe(true);
194
- });
195
-
196
- // --- File-reading function blocks ---
197
-
198
- it("blocks read_csv_auto", () => {
199
- const result = validateSQL("SELECT * FROM read_csv_auto('/etc/passwd')");
200
- expect(result.valid).toBe(false);
201
- expect(result.error).toContain("Forbidden");
202
- });
203
-
204
- it("blocks read_parquet", () => {
205
- const result = validateSQL("SELECT * FROM read_parquet('/data/secret.parquet')");
206
- expect(result.valid).toBe(false);
207
- expect(result.error).toContain("Forbidden");
208
- });
209
-
210
- it("blocks read_json_auto", () => {
211
- const result = validateSQL("SELECT * FROM read_json_auto('/tmp/data.json')");
212
- expect(result.valid).toBe(false);
213
- expect(result.error).toContain("Forbidden");
214
- });
215
-
216
- it("blocks SET", () => {
217
- const result = validateSQL("SET memory_limit='100GB'");
218
- expect(result.valid).toBe(false);
219
- expect(result.error).toContain("Forbidden");
220
- });
221
-
222
- it("blocks COPY ... TO (via base patterns)", () => {
223
- const result = validateSQL("COPY sales TO '/tmp/exfil.csv'");
224
- expect(result.valid).toBe(false);
225
- expect(result.error).toContain("Forbidden");
226
- });
227
-
228
- it("blocks LOAD (via base patterns)", () => {
229
- const result = validateSQL("LOAD httpfs");
230
- expect(result.valid).toBe(false);
231
- expect(result.error).toContain("Forbidden");
232
- });
233
- });