@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,589 @@
1
+ /**
2
+ * Unit tests for the conversations CRUD module.
3
+ *
4
+ * Uses _resetPool(mockPool) injection pattern from audit.test.ts to
5
+ * avoid mock.module (unreliable in bun's full test suite).
6
+ */
7
+ import { describe, it, expect, beforeEach, afterEach } from "bun:test";
8
+ import { _resetPool, type InternalPool } from "../db/internal";
9
+ import {
10
+ generateTitle,
11
+ createConversation,
12
+ addMessage,
13
+ getConversation,
14
+ listConversations,
15
+ deleteConversation,
16
+ starConversation,
17
+ } from "../conversations";
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Mock pool
21
+ // ---------------------------------------------------------------------------
22
+
23
+ let queryCalls: Array<{ sql: string; params?: unknown[] }> = [];
24
+ let queryResults: Array<{ rows: Record<string, unknown>[] }> = [];
25
+ let queryResultIndex = 0;
26
+ let queryThrow: Error | null = null;
27
+
28
+ const mockPool: InternalPool = {
29
+ query: async (sql: string, params?: unknown[]) => {
30
+ if (queryThrow) throw queryThrow;
31
+ queryCalls.push({ sql, params });
32
+ const result = queryResults[queryResultIndex] ?? { rows: [] };
33
+ queryResultIndex++;
34
+ return result;
35
+ },
36
+ end: async () => {},
37
+ on: () => {},
38
+ };
39
+
40
+ function enableInternalDB() {
41
+ process.env.DATABASE_URL = "postgresql://test:test@localhost:5432/test";
42
+ _resetPool(mockPool);
43
+ }
44
+
45
+ function setResults(...results: Array<{ rows: Record<string, unknown>[] }>) {
46
+ queryResults = results;
47
+ queryResultIndex = 0;
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Setup / teardown
52
+ // ---------------------------------------------------------------------------
53
+
54
+ describe("conversations module", () => {
55
+ const origDbUrl = process.env.DATABASE_URL;
56
+
57
+ beforeEach(() => {
58
+ queryCalls = [];
59
+ queryResults = [];
60
+ queryResultIndex = 0;
61
+ queryThrow = null;
62
+ delete process.env.DATABASE_URL;
63
+ _resetPool(null);
64
+ });
65
+
66
+ afterEach(() => {
67
+ if (origDbUrl) process.env.DATABASE_URL = origDbUrl;
68
+ else delete process.env.DATABASE_URL;
69
+ _resetPool(null);
70
+ });
71
+
72
+ // -------------------------------------------------------------------------
73
+ // generateTitle
74
+ // -------------------------------------------------------------------------
75
+
76
+ describe("generateTitle()", () => {
77
+ it("returns the question when <= 80 chars", () => {
78
+ expect(generateTitle("How many users?")).toBe("How many users?");
79
+ });
80
+
81
+ it("truncates to 80 chars with ellipsis", () => {
82
+ const long = "a".repeat(100);
83
+ const title = generateTitle(long);
84
+ expect(title.length).toBe(80);
85
+ expect(title.endsWith("...")).toBe(true);
86
+ });
87
+
88
+ it("strips newlines", () => {
89
+ expect(generateTitle("line1\nline2\r\nline3")).toBe("line1 line2 line3");
90
+ });
91
+
92
+ it("returns default for empty input", () => {
93
+ expect(generateTitle("")).toBe("New conversation");
94
+ expect(generateTitle(" ")).toBe("New conversation");
95
+ });
96
+
97
+ it("handles long input with ellipsis", () => {
98
+ const long = "How many users signed up in the last quarter and what was their average revenue contribution " + "x".repeat(50);
99
+ const title = generateTitle(long);
100
+ expect(title.length).toBe(80);
101
+ expect(title.endsWith("...")).toBe(true);
102
+ });
103
+
104
+ it("returns the exact string at the 80-char boundary", () => {
105
+ const exactly80 = "a".repeat(80);
106
+ expect(generateTitle(exactly80)).toBe(exactly80);
107
+ });
108
+ });
109
+
110
+ // -------------------------------------------------------------------------
111
+ // createConversation
112
+ // -------------------------------------------------------------------------
113
+
114
+ describe("createConversation()", () => {
115
+ it("returns { id } on success", async () => {
116
+ enableInternalDB();
117
+ setResults({ rows: [{ id: "conv-123" }] });
118
+
119
+ const result = await createConversation({ userId: "u1", title: "Test" });
120
+ expect(result).toEqual({ id: "conv-123" });
121
+ expect(queryCalls[0].sql).toContain("INSERT INTO conversations");
122
+ expect(queryCalls[0].params).toEqual(["u1", "Test", "web", null]);
123
+ });
124
+
125
+ it("returns null when no DB", async () => {
126
+ const result = await createConversation({ userId: "u1" });
127
+ expect(result).toBeNull();
128
+ });
129
+
130
+ it("returns null on DB error", async () => {
131
+ enableInternalDB();
132
+ queryThrow = new Error("connection refused");
133
+
134
+ const result = await createConversation({ userId: "u1" });
135
+ expect(result).toBeNull();
136
+ });
137
+
138
+ it("uses default surface 'web'", async () => {
139
+ enableInternalDB();
140
+ setResults({ rows: [{ id: "conv-456" }] });
141
+
142
+ await createConversation({});
143
+ expect(queryCalls[0].params).toEqual([null, null, "web", null]);
144
+ });
145
+
146
+ it("accepts custom surface and connectionId", async () => {
147
+ enableInternalDB();
148
+ setResults({ rows: [{ id: "conv-789" }] });
149
+
150
+ await createConversation({ surface: "api", connectionId: "wh" });
151
+ expect(queryCalls[0].params).toEqual([null, null, "api", "wh"]);
152
+ });
153
+ });
154
+
155
+ // -------------------------------------------------------------------------
156
+ // addMessage
157
+ // -------------------------------------------------------------------------
158
+
159
+ describe("addMessage()", () => {
160
+ it("fires two queries (INSERT + UPDATE)", () => {
161
+ enableInternalDB();
162
+
163
+ addMessage({ conversationId: "c1", role: "user", content: [{ type: "text", text: "hi" }] });
164
+
165
+ // internalExecute is fire-and-forget — queries are dispatched synchronously
166
+ expect(queryCalls.length).toBe(2);
167
+ expect(queryCalls[0].sql).toContain("INSERT INTO messages");
168
+ expect(queryCalls[1].sql).toContain("UPDATE conversations");
169
+ });
170
+
171
+ it("is a no-op when no DB", () => {
172
+ addMessage({ conversationId: "c1", role: "user", content: "hi" });
173
+ expect(queryCalls.length).toBe(0);
174
+ });
175
+
176
+ it("does not throw on error", () => {
177
+ enableInternalDB();
178
+ queryThrow = new Error("connection lost");
179
+
180
+ // internalExecute catches async errors; the try-catch in addMessage
181
+ // catches the sync throw from getInternalDB if pool is null.
182
+ // Here pool is set, so the throw happens inside the promise.
183
+ expect(() => {
184
+ addMessage({ conversationId: "c1", role: "user", content: "hi" });
185
+ }).not.toThrow();
186
+ });
187
+ });
188
+
189
+ // -------------------------------------------------------------------------
190
+ // getConversation
191
+ // -------------------------------------------------------------------------
192
+
193
+ describe("getConversation()", () => {
194
+ it("returns { ok: true, data } with conversation and messages", async () => {
195
+ enableInternalDB();
196
+ setResults(
197
+ {
198
+ rows: [{
199
+ id: "c1",
200
+ user_id: "u1",
201
+ title: "Test",
202
+ surface: "web",
203
+ connection_id: null,
204
+ created_at: "2024-01-01T00:00:00Z",
205
+ updated_at: "2024-01-01T00:00:00Z",
206
+ }],
207
+ },
208
+ {
209
+ rows: [{
210
+ id: "m1",
211
+ conversation_id: "c1",
212
+ role: "user",
213
+ content: { type: "text", text: "hello" },
214
+ created_at: "2024-01-01T00:00:00Z",
215
+ }],
216
+ },
217
+ );
218
+
219
+ const result = await getConversation("c1", "u1");
220
+ expect(result.ok).toBe(true);
221
+ if (result.ok) {
222
+ expect(result.data.id).toBe("c1");
223
+ expect(result.data.messages).toHaveLength(1);
224
+ expect(result.data.messages[0].role).toBe("user");
225
+ }
226
+ });
227
+
228
+ it("returns { ok: false, reason: 'not_found' } when not found", async () => {
229
+ enableInternalDB();
230
+ setResults({ rows: [] });
231
+
232
+ const result = await getConversation("missing");
233
+ expect(result).toEqual({ ok: false, reason: "not_found" });
234
+ });
235
+
236
+ it("returns { ok: false, reason: 'not_found' } when wrong user", async () => {
237
+ enableInternalDB();
238
+ // Query returns empty because user_id doesn't match
239
+ setResults({ rows: [] });
240
+
241
+ const result = await getConversation("c1", "wrong-user");
242
+ expect(result).toEqual({ ok: false, reason: "not_found" });
243
+ });
244
+
245
+ it("returns { ok: false, reason: 'no_db' } when no DB", async () => {
246
+ const result = await getConversation("c1");
247
+ expect(result).toEqual({ ok: false, reason: "no_db" });
248
+ });
249
+
250
+ it("returns { ok: false, reason: 'error' } on DB error", async () => {
251
+ enableInternalDB();
252
+ queryThrow = new Error("connection lost");
253
+ const result = await getConversation("c1", "u1");
254
+ expect(result).toEqual({ ok: false, reason: "error" });
255
+ });
256
+
257
+ it("queries without user filter when userId is undefined", async () => {
258
+ enableInternalDB();
259
+ setResults({ rows: [] });
260
+
261
+ await getConversation("c1");
262
+ expect(queryCalls[0].sql).not.toContain("AND user_id");
263
+ expect(queryCalls[0].params).toEqual(["c1"]);
264
+ });
265
+
266
+ it("queries with user filter when userId is provided", async () => {
267
+ enableInternalDB();
268
+ setResults({ rows: [] });
269
+
270
+ await getConversation("c1", "u1");
271
+ expect(queryCalls[0].sql).toContain("AND user_id");
272
+ expect(queryCalls[0].params).toEqual(["c1", "u1"]);
273
+ });
274
+ });
275
+
276
+ // -------------------------------------------------------------------------
277
+ // listConversations
278
+ // -------------------------------------------------------------------------
279
+
280
+ describe("listConversations()", () => {
281
+ it("returns conversations and total", async () => {
282
+ enableInternalDB();
283
+ setResults(
284
+ { rows: [{ total: 1 }] },
285
+ {
286
+ rows: [{
287
+ id: "c1",
288
+ user_id: "u1",
289
+ title: "Test",
290
+ surface: "web",
291
+ connection_id: null,
292
+ created_at: "2024-01-01T00:00:00Z",
293
+ updated_at: "2024-01-01T00:00:00Z",
294
+ }],
295
+ },
296
+ );
297
+
298
+ const result = await listConversations({ userId: "u1" });
299
+ expect(result.total).toBe(1);
300
+ expect(result.conversations).toHaveLength(1);
301
+ expect(result.conversations[0].id).toBe("c1");
302
+ });
303
+
304
+ it("returns empty when no DB", async () => {
305
+ const result = await listConversations();
306
+ expect(result).toEqual({ conversations: [], total: 0 });
307
+ });
308
+
309
+ it("respects limit and offset", async () => {
310
+ enableInternalDB();
311
+ setResults({ rows: [{ total: 50 }] }, { rows: [] });
312
+
313
+ await listConversations({ limit: 5, offset: 10 });
314
+ expect(queryCalls[1].params).toEqual([5, 10]);
315
+ });
316
+
317
+ it("scopes by userId when provided", async () => {
318
+ enableInternalDB();
319
+ setResults({ rows: [{ total: 3 }] }, { rows: [] });
320
+
321
+ await listConversations({ userId: "u1", limit: 20, offset: 0 });
322
+ expect(queryCalls[0].sql).toContain("user_id");
323
+ expect(queryCalls[0].params).toEqual(["u1"]);
324
+ });
325
+
326
+ it("does not scope by userId when not provided", async () => {
327
+ enableInternalDB();
328
+ setResults({ rows: [{ total: 3 }] }, { rows: [] });
329
+
330
+ await listConversations();
331
+ expect(queryCalls[0].sql).not.toContain("user_id = $1");
332
+ });
333
+
334
+ it("uses default limit=20, offset=0", async () => {
335
+ enableInternalDB();
336
+ setResults({ rows: [{ total: 0 }] }, { rows: [] });
337
+
338
+ await listConversations();
339
+ expect(queryCalls[1].params).toEqual([20, 0]);
340
+ });
341
+
342
+ it("returns empty on DB error", async () => {
343
+ enableInternalDB();
344
+ queryThrow = new Error("connection lost");
345
+ const result = await listConversations({ userId: "u1" });
346
+ expect(result).toEqual({ conversations: [], total: 0 });
347
+ });
348
+ });
349
+
350
+ // -------------------------------------------------------------------------
351
+ // starConversation
352
+ // -------------------------------------------------------------------------
353
+
354
+ describe("starConversation()", () => {
355
+ it("returns { ok: true } when conversation exists", async () => {
356
+ enableInternalDB();
357
+ setResults({ rows: [{ id: "c1" }] });
358
+
359
+ const result = await starConversation("c1", true);
360
+ expect(result).toEqual({ ok: true });
361
+ expect(queryCalls[0].sql).toContain("UPDATE conversations SET starred");
362
+ expect(queryCalls[0].params).toEqual([true, "c1"]);
363
+ });
364
+
365
+ it("returns { ok: false, reason: 'not_found' } when conversation not found", async () => {
366
+ enableInternalDB();
367
+ setResults({ rows: [] });
368
+
369
+ const result = await starConversation("missing", true);
370
+ expect(result).toEqual({ ok: false, reason: "not_found" });
371
+ });
372
+
373
+ it("returns { ok: false, reason: 'no_db' } when no DB", async () => {
374
+ const result = await starConversation("c1", true);
375
+ expect(result).toEqual({ ok: false, reason: "no_db" });
376
+ });
377
+
378
+ it("returns { ok: false, reason: 'error' } on DB error", async () => {
379
+ enableInternalDB();
380
+ queryThrow = new Error("connection lost");
381
+ const result = await starConversation("c1", true);
382
+ expect(result).toEqual({ ok: false, reason: "error" });
383
+ });
384
+
385
+ it("scopes by userId when provided", async () => {
386
+ enableInternalDB();
387
+ setResults({ rows: [{ id: "c1" }] });
388
+
389
+ await starConversation("c1", true, "u1");
390
+ expect(queryCalls[0].sql).toContain("user_id");
391
+ expect(queryCalls[0].params).toEqual([true, "c1", "u1"]);
392
+ });
393
+
394
+ it("can unstar a conversation", async () => {
395
+ enableInternalDB();
396
+ setResults({ rows: [{ id: "c1" }] });
397
+
398
+ const result = await starConversation("c1", false, "u1");
399
+ expect(result).toEqual({ ok: true });
400
+ expect(queryCalls[0].params).toEqual([false, "c1", "u1"]);
401
+ });
402
+ });
403
+
404
+ // -------------------------------------------------------------------------
405
+ // listConversations with starred filter
406
+ // -------------------------------------------------------------------------
407
+
408
+ describe("listConversations() with starred filter", () => {
409
+ it("filters by starred=true", async () => {
410
+ enableInternalDB();
411
+ setResults({ rows: [{ total: 1 }] }, {
412
+ rows: [{
413
+ id: "c1",
414
+ user_id: "u1",
415
+ title: "Starred conv",
416
+ surface: "web",
417
+ connection_id: null,
418
+ starred: true,
419
+ created_at: "2024-01-01T00:00:00Z",
420
+ updated_at: "2024-01-01T00:00:00Z",
421
+ }],
422
+ });
423
+
424
+ const result = await listConversations({ userId: "u1", starred: true });
425
+ expect(result.total).toBe(1);
426
+ expect(result.conversations[0].starred).toBe(true);
427
+ // Both count and data queries should contain starred filter
428
+ expect(queryCalls[0].sql).toContain("starred");
429
+ expect(queryCalls[0].params).toEqual(["u1", true]);
430
+ expect(queryCalls[1].sql).toContain("starred");
431
+ expect(queryCalls[1].params).toEqual(["u1", true, 20, 0]);
432
+ });
433
+
434
+ it("filters by starred=false", async () => {
435
+ enableInternalDB();
436
+ setResults({ rows: [{ total: 2 }] }, { rows: [] });
437
+
438
+ await listConversations({ starred: false });
439
+ expect(queryCalls[0].sql).toContain("starred");
440
+ expect(queryCalls[0].params).toEqual([false]);
441
+ });
442
+
443
+ it("does not filter when starred is undefined", async () => {
444
+ enableInternalDB();
445
+ setResults({ rows: [{ total: 0 }] }, { rows: [] });
446
+
447
+ await listConversations({ userId: "u1" });
448
+ expect(queryCalls[0].sql).not.toContain("starred");
449
+ });
450
+ });
451
+
452
+ // -------------------------------------------------------------------------
453
+ // starred field in responses
454
+ // -------------------------------------------------------------------------
455
+
456
+ describe("starred field in conversation responses", () => {
457
+ it("getConversation includes starred=true", async () => {
458
+ enableInternalDB();
459
+ setResults(
460
+ {
461
+ rows: [{
462
+ id: "c1",
463
+ user_id: "u1",
464
+ title: "Test",
465
+ surface: "web",
466
+ connection_id: null,
467
+ starred: true,
468
+ created_at: "2024-01-01T00:00:00Z",
469
+ updated_at: "2024-01-01T00:00:00Z",
470
+ }],
471
+ },
472
+ { rows: [] },
473
+ );
474
+
475
+ const result = await getConversation("c1", "u1");
476
+ expect(result.ok).toBe(true);
477
+ if (result.ok) {
478
+ expect(result.data.starred).toBe(true);
479
+ }
480
+ });
481
+
482
+ it("getConversation defaults starred to false for missing column", async () => {
483
+ enableInternalDB();
484
+ setResults(
485
+ {
486
+ rows: [{
487
+ id: "c1",
488
+ user_id: "u1",
489
+ title: "Test",
490
+ surface: "web",
491
+ connection_id: null,
492
+ created_at: "2024-01-01T00:00:00Z",
493
+ updated_at: "2024-01-01T00:00:00Z",
494
+ }],
495
+ },
496
+ { rows: [] },
497
+ );
498
+
499
+ const result = await getConversation("c1", "u1");
500
+ expect(result.ok).toBe(true);
501
+ if (result.ok) {
502
+ expect(result.data.starred).toBe(false);
503
+ }
504
+ });
505
+
506
+ it("listConversations includes starred field", async () => {
507
+ enableInternalDB();
508
+ setResults(
509
+ { rows: [{ total: 1 }] },
510
+ {
511
+ rows: [{
512
+ id: "c1",
513
+ user_id: "u1",
514
+ title: "Test",
515
+ surface: "web",
516
+ connection_id: null,
517
+ starred: false,
518
+ created_at: "2024-01-01T00:00:00Z",
519
+ updated_at: "2024-01-01T00:00:00Z",
520
+ }],
521
+ },
522
+ );
523
+
524
+ const result = await listConversations({ userId: "u1" });
525
+ expect(result.conversations[0].starred).toBe(false);
526
+ });
527
+ });
528
+
529
+ // -------------------------------------------------------------------------
530
+ // deleteConversation
531
+ // -------------------------------------------------------------------------
532
+
533
+ describe("deleteConversation()", () => {
534
+ it("returns { ok: true } on success", async () => {
535
+ enableInternalDB();
536
+ setResults({ rows: [{ id: "c1" }] });
537
+
538
+ const result = await deleteConversation("c1");
539
+ expect(result).toEqual({ ok: true });
540
+ expect(queryCalls[0].sql).toContain("DELETE FROM conversations");
541
+ });
542
+
543
+ it("returns { ok: false, reason: 'not_found' } when not found", async () => {
544
+ enableInternalDB();
545
+ setResults({ rows: [] });
546
+
547
+ const result = await deleteConversation("missing");
548
+ expect(result).toEqual({ ok: false, reason: "not_found" });
549
+ });
550
+
551
+ it("returns { ok: false, reason: 'not_found' } when wrong user", async () => {
552
+ enableInternalDB();
553
+ setResults({ rows: [] });
554
+
555
+ const result = await deleteConversation("c1", "wrong-user");
556
+ expect(result).toEqual({ ok: false, reason: "not_found" });
557
+ });
558
+
559
+ it("returns { ok: false, reason: 'no_db' } when no DB", async () => {
560
+ const result = await deleteConversation("c1");
561
+ expect(result).toEqual({ ok: false, reason: "no_db" });
562
+ });
563
+
564
+ it("scopes by userId when provided", async () => {
565
+ enableInternalDB();
566
+ setResults({ rows: [] });
567
+
568
+ await deleteConversation("c1", "u1");
569
+ expect(queryCalls[0].sql).toContain("user_id");
570
+ expect(queryCalls[0].params).toEqual(["c1", "u1"]);
571
+ });
572
+
573
+ it("does not scope by userId when not provided", async () => {
574
+ enableInternalDB();
575
+ setResults({ rows: [] });
576
+
577
+ await deleteConversation("c1");
578
+ expect(queryCalls[0].sql).not.toContain("user_id");
579
+ expect(queryCalls[0].params).toEqual(["c1"]);
580
+ });
581
+
582
+ it("returns { ok: false, reason: 'error' } on DB error", async () => {
583
+ enableInternalDB();
584
+ queryThrow = new Error("connection lost");
585
+ const result = await deleteConversation("c1");
586
+ expect(result).toEqual({ ok: false, reason: "error" });
587
+ });
588
+ });
589
+ });