convex 1.40.0 → 1.41.0

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 (290) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/browser.bundle.js +1 -1
  3. package/dist/browser.bundle.js.map +1 -1
  4. package/dist/cjs/cli/aiFiles.js +2 -2
  5. package/dist/cjs/cli/aiFiles.js.map +1 -1
  6. package/dist/cjs/cli/configure.js +1 -4
  7. package/dist/cjs/cli/configure.js.map +2 -2
  8. package/dist/cjs/cli/convexExport.js +3 -3
  9. package/dist/cjs/cli/convexExport.js.map +1 -1
  10. package/dist/cjs/cli/convexImport.js +2 -2
  11. package/dist/cjs/cli/convexImport.js.map +1 -1
  12. package/dist/cjs/cli/dashboard.js +19 -6
  13. package/dist/cjs/cli/dashboard.js.map +3 -3
  14. package/dist/cjs/cli/data.js +2 -2
  15. package/dist/cjs/cli/data.js.map +1 -1
  16. package/dist/cjs/cli/deploy.js +5 -5
  17. package/dist/cjs/cli/deploy.js.map +2 -2
  18. package/dist/cjs/cli/deploymentCreate.js +11 -5
  19. package/dist/cjs/cli/deploymentCreate.js.map +2 -2
  20. package/dist/cjs/cli/deploymentSelect.js +5 -5
  21. package/dist/cjs/cli/deploymentSelect.js.map +1 -1
  22. package/dist/cjs/cli/deploymentTokenCreate.js +5 -13
  23. package/dist/cjs/cli/deploymentTokenCreate.js.map +2 -2
  24. package/dist/cjs/cli/deploymentTokenDelete.js +4 -11
  25. package/dist/cjs/cli/deploymentTokenDelete.js.map +2 -2
  26. package/dist/cjs/cli/dev.js +6 -5
  27. package/dist/cjs/cli/dev.js.map +2 -2
  28. package/dist/cjs/cli/env.js +16 -16
  29. package/dist/cjs/cli/env.js.map +2 -2
  30. package/dist/cjs/cli/envDefault.js +10 -10
  31. package/dist/cjs/cli/envDefault.js.map +1 -1
  32. package/dist/cjs/cli/insights.js +3 -3
  33. package/dist/cjs/cli/insights.js.map +1 -1
  34. package/dist/cjs/cli/lib/aiFiles/skills.js +2 -2
  35. package/dist/cjs/cli/lib/aiFiles/skills.js.map +2 -2
  36. package/dist/cjs/cli/lib/command.js +1 -1
  37. package/dist/cjs/cli/lib/command.js.map +1 -1
  38. package/dist/cjs/cli/lib/deployment.js.map +1 -1
  39. package/dist/cjs/cli/lib/deploymentSelection.js +39 -0
  40. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  41. package/dist/cjs/cli/lib/dev.js +31 -0
  42. package/dist/cjs/cli/lib/dev.js.map +2 -2
  43. package/dist/cjs/cli/lib/generateDocs.js +256 -0
  44. package/dist/cjs/cli/lib/generateDocs.js.map +7 -0
  45. package/dist/cjs/cli/lib/localDeployment/anonymous.js +24 -49
  46. package/dist/cjs/cli/lib/localDeployment/anonymous.js.map +3 -3
  47. package/dist/cjs/cli/lib/localDeployment/bigBrain.js +0 -9
  48. package/dist/cjs/cli/lib/localDeployment/bigBrain.js.map +2 -2
  49. package/dist/cjs/cli/lib/localDeployment/dashboard.js +30 -68
  50. package/dist/cjs/cli/lib/localDeployment/dashboard.js.map +2 -2
  51. package/dist/cjs/cli/lib/localDeployment/download.js +14 -1
  52. package/dist/cjs/cli/lib/localDeployment/download.js.map +2 -2
  53. package/dist/cjs/cli/lib/localDeployment/filePaths.js +33 -4
  54. package/dist/cjs/cli/lib/localDeployment/filePaths.js.map +2 -2
  55. package/dist/cjs/cli/lib/localDeployment/localDeployment.js +37 -126
  56. package/dist/cjs/cli/lib/localDeployment/localDeployment.js.map +3 -3
  57. package/dist/cjs/cli/lib/localDeployment/secrets.js +91 -0
  58. package/dist/cjs/cli/lib/localDeployment/secrets.js.map +7 -0
  59. package/dist/cjs/cli/lib/localDeployment/upgrade.js +43 -28
  60. package/dist/cjs/cli/lib/localDeployment/upgrade.js.map +3 -3
  61. package/dist/cjs/cli/lib/localDeployment/utils.js +0 -19
  62. package/dist/cjs/cli/lib/localDeployment/utils.js.map +3 -3
  63. package/dist/cjs/cli/lib/runTestFunction.js +3 -3
  64. package/dist/cjs/cli/lib/runTestFunction.js.map +1 -1
  65. package/dist/cjs/cli/run.js +5 -5
  66. package/dist/cjs/cli/run.js.map +1 -1
  67. package/dist/cjs/index.js +1 -1
  68. package/dist/cjs/index.js.map +1 -1
  69. package/dist/cjs/server/impl/registration_impl.js +0 -1
  70. package/dist/cjs/server/impl/registration_impl.js.map +2 -2
  71. package/dist/cjs/server/index.js.map +2 -2
  72. package/dist/cjs/server/meta.js.map +1 -1
  73. package/dist/cjs/server/registration.js.map +1 -1
  74. package/dist/cjs-types/cli/configure.d.ts +3 -2
  75. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  76. package/dist/cjs-types/cli/dashboard.d.ts.map +1 -1
  77. package/dist/cjs-types/cli/deploymentCreate.d.ts.map +1 -1
  78. package/dist/cjs-types/cli/deploymentTokenCreate.d.ts.map +1 -1
  79. package/dist/cjs-types/cli/deploymentTokenDelete.d.ts.map +1 -1
  80. package/dist/cjs-types/cli/dev.d.ts.map +1 -1
  81. package/dist/cjs-types/cli/lib/deployApi/definitionConfig.d.ts +4 -4
  82. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts +16 -16
  83. package/dist/cjs-types/cli/lib/deployment.d.ts +0 -2
  84. package/dist/cjs-types/cli/lib/deployment.d.ts.map +1 -1
  85. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +7 -0
  86. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  87. package/dist/cjs-types/cli/lib/dev.d.ts +2 -1
  88. package/dist/cjs-types/cli/lib/dev.d.ts.map +1 -1
  89. package/dist/cjs-types/cli/lib/generateDocs.d.ts +20 -0
  90. package/dist/cjs-types/cli/lib/generateDocs.d.ts.map +1 -0
  91. package/dist/cjs-types/cli/lib/generateDocs.test.d.ts +2 -0
  92. package/dist/cjs-types/cli/lib/generateDocs.test.d.ts.map +1 -0
  93. package/dist/cjs-types/cli/lib/localDeployment/anonymous.d.ts +1 -1
  94. package/dist/cjs-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  95. package/dist/cjs-types/cli/lib/localDeployment/bigBrain.d.ts +2 -4
  96. package/dist/cjs-types/cli/lib/localDeployment/bigBrain.d.ts.map +1 -1
  97. package/dist/cjs-types/cli/lib/localDeployment/dashboard.d.ts +9 -4
  98. package/dist/cjs-types/cli/lib/localDeployment/dashboard.d.ts.map +1 -1
  99. package/dist/cjs-types/cli/lib/localDeployment/download.d.ts +11 -1
  100. package/dist/cjs-types/cli/lib/localDeployment/download.d.ts.map +1 -1
  101. package/dist/cjs-types/cli/lib/localDeployment/filePaths.d.ts +16 -5
  102. package/dist/cjs-types/cli/lib/localDeployment/filePaths.d.ts.map +1 -1
  103. package/dist/cjs-types/cli/lib/localDeployment/localDeployment.d.ts +1 -9
  104. package/dist/cjs-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  105. package/dist/cjs-types/cli/lib/localDeployment/secrets.d.ts +31 -0
  106. package/dist/cjs-types/cli/lib/localDeployment/secrets.d.ts.map +1 -0
  107. package/dist/cjs-types/cli/lib/localDeployment/secrets.test.d.ts +2 -0
  108. package/dist/cjs-types/cli/lib/localDeployment/secrets.test.d.ts.map +1 -0
  109. package/dist/cjs-types/cli/lib/localDeployment/upgrade.d.ts +6 -3
  110. package/dist/cjs-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  111. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts +0 -2
  112. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  113. package/dist/cjs-types/cli/lib/versionApi.d.ts +2 -2
  114. package/dist/cjs-types/index.d.ts +1 -1
  115. package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
  116. package/dist/cjs-types/server/index.d.ts +1 -1
  117. package/dist/cjs-types/server/index.d.ts.map +1 -1
  118. package/dist/cjs-types/server/meta.d.ts +16 -0
  119. package/dist/cjs-types/server/meta.d.ts.map +1 -1
  120. package/dist/cjs-types/server/registration.d.ts +11 -5
  121. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  122. package/dist/cli.bundle.cjs +66405 -67923
  123. package/dist/cli.bundle.cjs.map +4 -4
  124. package/dist/esm/cli/aiFiles.js +2 -2
  125. package/dist/esm/cli/aiFiles.js.map +1 -1
  126. package/dist/esm/cli/configure.js +1 -4
  127. package/dist/esm/cli/configure.js.map +2 -2
  128. package/dist/esm/cli/convexExport.js +3 -3
  129. package/dist/esm/cli/convexExport.js.map +1 -1
  130. package/dist/esm/cli/convexImport.js +2 -2
  131. package/dist/esm/cli/convexImport.js.map +1 -1
  132. package/dist/esm/cli/dashboard.js +16 -3
  133. package/dist/esm/cli/dashboard.js.map +2 -2
  134. package/dist/esm/cli/data.js +2 -2
  135. package/dist/esm/cli/data.js.map +1 -1
  136. package/dist/esm/cli/deploy.js +5 -5
  137. package/dist/esm/cli/deploy.js.map +2 -2
  138. package/dist/esm/cli/deploymentCreate.js +13 -9
  139. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  140. package/dist/esm/cli/deploymentSelect.js +5 -5
  141. package/dist/esm/cli/deploymentSelect.js.map +1 -1
  142. package/dist/esm/cli/deploymentTokenCreate.js +9 -15
  143. package/dist/esm/cli/deploymentTokenCreate.js.map +2 -2
  144. package/dist/esm/cli/deploymentTokenDelete.js +8 -16
  145. package/dist/esm/cli/deploymentTokenDelete.js.map +2 -2
  146. package/dist/esm/cli/dev.js +6 -5
  147. package/dist/esm/cli/dev.js.map +2 -2
  148. package/dist/esm/cli/env.js +16 -16
  149. package/dist/esm/cli/env.js.map +2 -2
  150. package/dist/esm/cli/envDefault.js +10 -10
  151. package/dist/esm/cli/envDefault.js.map +1 -1
  152. package/dist/esm/cli/insights.js +3 -3
  153. package/dist/esm/cli/insights.js.map +1 -1
  154. package/dist/esm/cli/lib/aiFiles/skills.js +2 -2
  155. package/dist/esm/cli/lib/aiFiles/skills.js.map +2 -2
  156. package/dist/esm/cli/lib/command.js +1 -1
  157. package/dist/esm/cli/lib/command.js.map +1 -1
  158. package/dist/esm/cli/lib/deployment.js.map +1 -1
  159. package/dist/esm/cli/lib/deploymentSelection.js +38 -0
  160. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  161. package/dist/esm/cli/lib/dev.js +31 -0
  162. package/dist/esm/cli/lib/dev.js.map +2 -2
  163. package/dist/esm/cli/lib/generateDocs.js +233 -0
  164. package/dist/esm/cli/lib/generateDocs.js.map +7 -0
  165. package/dist/esm/cli/lib/localDeployment/anonymous.js +30 -61
  166. package/dist/esm/cli/lib/localDeployment/anonymous.js.map +3 -3
  167. package/dist/esm/cli/lib/localDeployment/bigBrain.js +0 -8
  168. package/dist/esm/cli/lib/localDeployment/bigBrain.js.map +2 -2
  169. package/dist/esm/cli/lib/localDeployment/dashboard.js +36 -69
  170. package/dist/esm/cli/lib/localDeployment/dashboard.js.map +2 -2
  171. package/dist/esm/cli/lib/localDeployment/download.js +15 -2
  172. package/dist/esm/cli/lib/localDeployment/download.js.map +2 -2
  173. package/dist/esm/cli/lib/localDeployment/filePaths.js +29 -2
  174. package/dist/esm/cli/lib/localDeployment/filePaths.js.map +2 -2
  175. package/dist/esm/cli/lib/localDeployment/localDeployment.js +40 -134
  176. package/dist/esm/cli/lib/localDeployment/localDeployment.js.map +3 -3
  177. package/dist/esm/cli/lib/localDeployment/secrets.js +57 -0
  178. package/dist/esm/cli/lib/localDeployment/secrets.js.map +7 -0
  179. package/dist/esm/cli/lib/localDeployment/upgrade.js +45 -28
  180. package/dist/esm/cli/lib/localDeployment/upgrade.js.map +3 -3
  181. package/dist/esm/cli/lib/localDeployment/utils.js +0 -7
  182. package/dist/esm/cli/lib/localDeployment/utils.js.map +2 -2
  183. package/dist/esm/cli/lib/runTestFunction.js +3 -3
  184. package/dist/esm/cli/lib/runTestFunction.js.map +1 -1
  185. package/dist/esm/cli/run.js +5 -5
  186. package/dist/esm/cli/run.js.map +1 -1
  187. package/dist/esm/index.js +1 -1
  188. package/dist/esm/index.js.map +1 -1
  189. package/dist/esm/server/impl/registration_impl.js +0 -1
  190. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  191. package/dist/esm/server/index.js.map +2 -2
  192. package/dist/esm-types/cli/configure.d.ts +3 -2
  193. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  194. package/dist/esm-types/cli/dashboard.d.ts.map +1 -1
  195. package/dist/esm-types/cli/deploymentCreate.d.ts.map +1 -1
  196. package/dist/esm-types/cli/deploymentTokenCreate.d.ts.map +1 -1
  197. package/dist/esm-types/cli/deploymentTokenDelete.d.ts.map +1 -1
  198. package/dist/esm-types/cli/dev.d.ts.map +1 -1
  199. package/dist/esm-types/cli/lib/deployApi/definitionConfig.d.ts +4 -4
  200. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts +16 -16
  201. package/dist/esm-types/cli/lib/deployment.d.ts +0 -2
  202. package/dist/esm-types/cli/lib/deployment.d.ts.map +1 -1
  203. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +7 -0
  204. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  205. package/dist/esm-types/cli/lib/dev.d.ts +2 -1
  206. package/dist/esm-types/cli/lib/dev.d.ts.map +1 -1
  207. package/dist/esm-types/cli/lib/generateDocs.d.ts +20 -0
  208. package/dist/esm-types/cli/lib/generateDocs.d.ts.map +1 -0
  209. package/dist/esm-types/cli/lib/generateDocs.test.d.ts +2 -0
  210. package/dist/esm-types/cli/lib/generateDocs.test.d.ts.map +1 -0
  211. package/dist/esm-types/cli/lib/localDeployment/anonymous.d.ts +1 -1
  212. package/dist/esm-types/cli/lib/localDeployment/anonymous.d.ts.map +1 -1
  213. package/dist/esm-types/cli/lib/localDeployment/bigBrain.d.ts +2 -4
  214. package/dist/esm-types/cli/lib/localDeployment/bigBrain.d.ts.map +1 -1
  215. package/dist/esm-types/cli/lib/localDeployment/dashboard.d.ts +9 -4
  216. package/dist/esm-types/cli/lib/localDeployment/dashboard.d.ts.map +1 -1
  217. package/dist/esm-types/cli/lib/localDeployment/download.d.ts +11 -1
  218. package/dist/esm-types/cli/lib/localDeployment/download.d.ts.map +1 -1
  219. package/dist/esm-types/cli/lib/localDeployment/filePaths.d.ts +16 -5
  220. package/dist/esm-types/cli/lib/localDeployment/filePaths.d.ts.map +1 -1
  221. package/dist/esm-types/cli/lib/localDeployment/localDeployment.d.ts +1 -9
  222. package/dist/esm-types/cli/lib/localDeployment/localDeployment.d.ts.map +1 -1
  223. package/dist/esm-types/cli/lib/localDeployment/secrets.d.ts +31 -0
  224. package/dist/esm-types/cli/lib/localDeployment/secrets.d.ts.map +1 -0
  225. package/dist/esm-types/cli/lib/localDeployment/secrets.test.d.ts +2 -0
  226. package/dist/esm-types/cli/lib/localDeployment/secrets.test.d.ts.map +1 -0
  227. package/dist/esm-types/cli/lib/localDeployment/upgrade.d.ts +6 -3
  228. package/dist/esm-types/cli/lib/localDeployment/upgrade.d.ts.map +1 -1
  229. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts +0 -2
  230. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  231. package/dist/esm-types/cli/lib/versionApi.d.ts +2 -2
  232. package/dist/esm-types/index.d.ts +1 -1
  233. package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
  234. package/dist/esm-types/server/index.d.ts +1 -1
  235. package/dist/esm-types/server/index.d.ts.map +1 -1
  236. package/dist/esm-types/server/meta.d.ts +16 -0
  237. package/dist/esm-types/server/meta.d.ts.map +1 -1
  238. package/dist/esm-types/server/registration.d.ts +11 -5
  239. package/dist/esm-types/server/registration.d.ts.map +1 -1
  240. package/dist/react.bundle.js +1 -1
  241. package/dist/react.bundle.js.map +1 -1
  242. package/package.json +4 -2
  243. package/src/browser/sync/request_manager.test.ts +2 -2
  244. package/src/cli/aiFiles.ts +2 -2
  245. package/src/cli/configure.ts +4 -6
  246. package/src/cli/convexExport.ts +3 -3
  247. package/src/cli/convexImport.ts +2 -2
  248. package/src/cli/dashboard.ts +29 -3
  249. package/src/cli/data.ts +2 -2
  250. package/src/cli/deploy.ts +5 -5
  251. package/src/cli/deploymentCreate.test.ts +151 -24
  252. package/src/cli/deploymentCreate.ts +21 -11
  253. package/src/cli/deploymentSelect.ts +5 -5
  254. package/src/cli/deploymentSelection.test.ts +0 -3
  255. package/src/cli/deploymentToken.test.ts +34 -23
  256. package/src/cli/deploymentTokenCreate.ts +9 -21
  257. package/src/cli/deploymentTokenDelete.ts +8 -23
  258. package/src/cli/dev.ts +5 -4
  259. package/src/cli/env.ts +16 -16
  260. package/src/cli/envDefault.ts +10 -10
  261. package/src/cli/insights.ts +3 -3
  262. package/src/cli/lib/aiFiles/integration.test.ts +2 -0
  263. package/src/cli/lib/aiFiles/skills.ts +3 -3
  264. package/src/cli/lib/command.ts +2 -2
  265. package/src/cli/lib/deployment.ts +0 -5
  266. package/src/cli/lib/deploymentSelection.ts +67 -0
  267. package/src/cli/lib/dev.ts +39 -0
  268. package/src/cli/lib/generateDocs.test.ts +326 -0
  269. package/src/cli/lib/generateDocs.ts +393 -0
  270. package/src/cli/lib/localDeployment/anonymous.ts +48 -72
  271. package/src/cli/lib/localDeployment/bigBrain.ts +7 -15
  272. package/src/cli/lib/localDeployment/dashboard.ts +48 -80
  273. package/src/cli/lib/localDeployment/download.ts +34 -3
  274. package/src/cli/lib/localDeployment/filePaths.ts +66 -6
  275. package/src/cli/lib/localDeployment/localDeployment.ts +46 -184
  276. package/src/cli/lib/localDeployment/run.test.ts +6 -6
  277. package/src/cli/lib/localDeployment/secrets.test.ts +53 -0
  278. package/src/cli/lib/localDeployment/secrets.ts +93 -0
  279. package/src/cli/lib/localDeployment/tests/keygenFailure.mjs +9 -0
  280. package/src/cli/lib/localDeployment/tests/keygenSuccess.mjs +31 -0
  281. package/src/cli/lib/localDeployment/upgrade.ts +52 -38
  282. package/src/cli/lib/localDeployment/utils.ts +0 -10
  283. package/src/cli/lib/runTestFunction.ts +3 -3
  284. package/src/cli/run.ts +5 -5
  285. package/src/index.ts +1 -1
  286. package/src/server/impl/registration_impl.ts +0 -2
  287. package/src/server/index.ts +1 -0
  288. package/src/server/meta.ts +17 -0
  289. package/src/server/registration.test.ts +2 -35
  290. package/src/server/registration.ts +10 -19
@@ -1,20 +1,21 @@
1
1
  import { Context } from "../../../bundler/context.js";
2
2
  import {
3
3
  dashboardOutDir,
4
- loadDashboardConfig,
4
+ loadProjectDashboardConfig,
5
5
  loadUuidForAnonymousUser,
6
- saveDashboardConfig,
6
+ saveProjectDashboardConfig,
7
7
  } from "./filePaths.js";
8
8
  import { choosePorts } from "./utils.js";
9
9
  import { startServer } from "./serve.js";
10
- import { listExistingAnonymousDeployments } from "./anonymous.js";
11
10
  import { localDeploymentUrl, selfHostedEventTag } from "./run.js";
12
11
  import serveHandler from "serve-handler";
13
- import { ensureDashboardDownloaded } from "./download.js";
12
+ import {
13
+ ensureBackendBinaryDownloaded,
14
+ ensureDashboardDownloaded,
15
+ } from "./download.js";
14
16
  import { bigBrainAPIMaybeThrows } from "../utils/utils.js";
15
17
 
16
18
  export const DEFAULT_LOCAL_DASHBOARD_PORT = 6790;
17
- export const DEFAULT_LOCAL_DASHBOARD_API_PORT = 6791;
18
19
 
19
20
  /**
20
21
  * This runs the `dashboard-self-hosted` app locally.
@@ -22,32 +23,59 @@ export const DEFAULT_LOCAL_DASHBOARD_API_PORT = 6791;
22
23
  * uses `dashboard.convex.dev`, and some of the code below is written
23
24
  * assuming this is only used for `anonymous`.
24
25
  */
25
- export async function handleDashboard(ctx: Context, version: string) {
26
+ export async function handleDashboard(
27
+ ctx: Context,
28
+ deployment: { name: string; cloudPort: number; adminKey: string },
29
+ options: {
30
+ /** The backend version to use if the user overrides the default version with the --local-backend-version flag */
31
+ backendVersion: string | undefined;
32
+ },
33
+ ) {
34
+ // We call `ensureBackendBinaryDownloaded` here to get the version,
35
+ // but `handleAnonymousDeployment` has already downloaded it.
36
+ const { version } = await ensureBackendBinaryDownloaded(
37
+ ctx,
38
+ options.backendVersion === undefined
39
+ ? { kind: "latest" }
40
+ : { kind: "version", version: options.backendVersion },
41
+ );
26
42
  const anonymousId = loadUuidForAnonymousUser(ctx) ?? undefined;
27
- const isRunning = await checkIfDashboardIsRunning(ctx);
28
- if (isRunning) {
29
- // It's possible this is running with a different version, but
30
- // let's not worry about that for now.
31
- return;
32
- }
33
43
  await ensureDashboardDownloaded(ctx, version);
34
- const [dashboardPort, apiPort] = await choosePorts(ctx, {
35
- count: 2,
44
+ const [dashboardPort] = await choosePorts(ctx, {
45
+ count: 1,
36
46
  startPort: DEFAULT_LOCAL_DASHBOARD_PORT,
37
47
  requestedPorts: [null, null],
38
48
  });
39
- await saveDashboardConfig(ctx, {
49
+ saveProjectDashboardConfig(ctx, deployment.name, {
40
50
  port: dashboardPort,
41
- apiPort,
42
- version,
43
51
  });
44
52
 
45
53
  let hasReportedSelfHostedEvent = false;
46
54
 
55
+ const serverOptions = { cors: false } as const;
47
56
  const { cleanupHandle } = await startServer(
48
57
  ctx,
49
58
  dashboardPort,
50
59
  async (request, response) => {
60
+ const pathname = new URL(
61
+ request.url ?? "/",
62
+ // We only want to extract the pathname so the base doesn’t matter
63
+ "http://localhost",
64
+ ).pathname;
65
+
66
+ if (pathname === "/api/current_deployment") {
67
+ serverOptions satisfies { cors: false };
68
+ response.setHeader("Content-Type", "application/json");
69
+ response.end(
70
+ JSON.stringify({
71
+ name: deployment.name,
72
+ url: localDeploymentUrl(deployment.cloudPort),
73
+ adminKey: deployment.adminKey,
74
+ }),
75
+ );
76
+ return;
77
+ }
78
+
51
79
  if (!hasReportedSelfHostedEvent) {
52
80
  hasReportedSelfHostedEvent = true;
53
81
  void reportSelfHostedEvent(ctx, {
@@ -60,9 +88,8 @@ export async function handleDashboard(ctx: Context, version: string) {
60
88
  public: dashboardOutDir(),
61
89
  });
62
90
  },
63
- {},
91
+ serverOptions,
64
92
  );
65
- await startServingListDeploymentsApi(ctx, apiPort);
66
93
  return {
67
94
  dashboardPort,
68
95
  cleanupHandle,
@@ -100,70 +127,11 @@ async function reportSelfHostedEvent(
100
127
  }
101
128
  }
102
129
 
103
- /**
104
- * This serves a really basic API that just returns a JSON blob with the deployments
105
- * and their credentials.
106
- * The locally running dashboard can hit this API.
107
- */
108
- async function startServingListDeploymentsApi(ctx: Context, port: number) {
109
- await startServer(
110
- ctx,
111
- port,
112
- async (request, response) => {
113
- const deployments = await listExistingAnonymousDeployments(ctx);
114
- const deploymentsJson = deployments.map((d) => ({
115
- name: d.deploymentName,
116
- url: localDeploymentUrl(d.config.ports.cloud),
117
- adminKey: d.config.adminKey,
118
- }));
119
- response.setHeader("Content-Type", "application/json");
120
- response.end(JSON.stringify({ deployments: deploymentsJson }));
121
- },
122
- {
123
- cors: true,
124
- },
125
- );
126
- }
127
-
128
- export async function checkIfDashboardIsRunning(ctx: Context) {
129
- const dashboardConfig = loadDashboardConfig(ctx);
130
- if (dashboardConfig === null) {
131
- return false;
132
- }
133
- // We're checking if the mini API server is running and has a response that
134
- // looks like a list of deployments, since it's easier than checking the
135
- // dashboard UI + won't trigger the event for the developer opening the dashboard.
136
- let resp: Response;
137
- try {
138
- resp = await fetch(`http://127.0.0.1:${dashboardConfig.apiPort}`);
139
- } catch {
140
- return false;
141
- }
142
- if (!resp.ok) {
143
- return false;
144
- }
145
- let data: { deployments: { name: string; url: string; adminKey: string }[] };
146
- try {
147
- data = await resp.json();
148
- } catch {
149
- return false;
150
- }
151
- return Array.isArray(data.deployments);
152
- }
153
-
154
130
  export function dashboardUrl(ctx: Context, deploymentName: string) {
155
- const dashboardConfig = loadDashboardConfig(ctx);
131
+ const dashboardConfig = loadProjectDashboardConfig(ctx, deploymentName);
156
132
  if (dashboardConfig === null) {
157
133
  return null;
158
134
  }
159
135
 
160
- const queryParams = new URLSearchParams();
161
- if (dashboardConfig.apiPort !== DEFAULT_LOCAL_DASHBOARD_API_PORT) {
162
- queryParams.set("a", dashboardConfig.apiPort.toString());
163
- }
164
- queryParams.set("d", deploymentName);
165
- const queryString = queryParams.toString();
166
- const url = new URL(`http://127.0.0.1:${dashboardConfig.port}`);
167
- url.search = queryString;
168
- return url.href;
136
+ return `http://127.0.0.1:${dashboardConfig.port}/`;
169
137
  }
@@ -15,7 +15,8 @@ import {
15
15
  versionedBinaryDir,
16
16
  dashboardOutDir,
17
17
  resetDashboardDir,
18
- loadDashboardConfig,
18
+ loadGlobalDashboardConfig,
19
+ saveGlobalDashboardConfig,
19
20
  executableName,
20
21
  } from "./filePaths.js";
21
22
  import child_process from "child_process";
@@ -79,11 +80,38 @@ async function _ensureBackendBinaryDownloaded(
79
80
  return { version, binaryPath };
80
81
  }
81
82
 
83
+ let cachedLatestVersion: string | null = null;
84
+
85
+ /**
86
+ * Finds the latest version of the Convex local backend through
87
+ * version.convex.dev, caching the resolved version for the lifetime of the
88
+ * process so repeated lookups hit the network at most once.
89
+ *
90
+ * Wraps {@link _findLatestVersionWithBinary}. Only successful (non-null)
91
+ * results are cached, so a failed `requireSuccess: false` lookup can be retried
92
+ * by a later call.
93
+ */
94
+ export async function findLatestVersionWithBinary<
95
+ RequireSuccess extends boolean,
96
+ >(
97
+ ctx: Context,
98
+ requireSuccess: RequireSuccess,
99
+ ): Promise<RequireSuccess extends true ? string : string | null> {
100
+ if (cachedLatestVersion !== null) {
101
+ return cachedLatestVersion;
102
+ }
103
+ const result = await _findLatestVersionWithBinary(ctx, requireSuccess);
104
+ if (result !== null) {
105
+ cachedLatestVersion = result;
106
+ }
107
+ return result;
108
+ }
109
+
82
110
  /**
83
111
  * Finds the latest version of the Convex local backend
84
112
  * through version.convex.dev
85
113
  */
86
- export async function findLatestVersionWithBinary<
114
+ export async function _findLatestVersionWithBinary<
87
115
  RequireSuccess extends boolean,
88
116
  >(
89
117
  ctx: Context,
@@ -298,12 +326,15 @@ async function downloadZipFile(
298
326
  }
299
327
 
300
328
  export async function ensureDashboardDownloaded(ctx: Context, version: string) {
301
- const config = loadDashboardConfig(ctx);
329
+ const config = loadGlobalDashboardConfig(ctx);
302
330
  if (config !== null && config.version === version) {
303
331
  return;
304
332
  }
305
333
  await resetDashboardDir(ctx);
306
334
  await _ensureDashboardDownloaded(ctx, version);
335
+ // Record which version is now in `dashboardOutDir()`
336
+ // so we can skip the download next time.
337
+ saveGlobalDashboardConfig(ctx, { version });
307
338
  }
308
339
  async function _ensureDashboardDownloaded(ctx: Context, version: string) {
309
340
  const zipLocation = dashboardZip();
@@ -6,7 +6,7 @@
6
6
  0.0.2
7
7
  convex-local-backend[.exe]
8
8
  dashboard
9
- config.json
9
+ config.json // `GlobalDashboardConfig`
10
10
  out
11
11
  // if present, output files from building the self-hosted dashboard which can
12
12
  // be served using `npx serve`
@@ -24,6 +24,10 @@ Legacy (home directory) - used for backward compatibility if data already exists
24
24
  - For "local" deployments: ~/.convex/convex-backend-state/local-{team}-{project}/
25
25
  - For "anonymous" deployments: ~/.convex/anonymous-convex-backend-state/{anonymous-deployment-name}/
26
26
 
27
+ For anonymous deployments, the locally running dashboard's `{ port }`
28
+ is stored as `dashboard.json` next to that deployment's `config.json` (see
29
+ `ProjectDashboardConfig`), wherever the deployment lives.
30
+
27
31
 
28
32
  ~/.convex
29
33
  convex-backend-state
@@ -331,12 +335,19 @@ export function dashboardOutDir() {
331
335
  return path.join(dashboardDir(), "out");
332
336
  }
333
337
 
334
- export type DashboardConfig = {
335
- port: number;
336
- apiPort: number;
338
+ /**
339
+ * Global dashboard config in ~/.cache/convex/dashboard/config.json.
340
+ */
341
+ export type GlobalDashboardConfig = {
337
342
  version: string;
343
+
344
+ // Previous versions of the Convex CLI would also store
345
+ // fields here for ports. They are now in ProjectDashboardConfig.
338
346
  };
339
- export function loadDashboardConfig(ctx: Context) {
347
+
348
+ export function loadGlobalDashboardConfig(
349
+ ctx: Context,
350
+ ): GlobalDashboardConfig | null {
340
351
  const configFile = path.join(dashboardDir(), "config.json");
341
352
  if (!ctx.fs.exists(configFile)) {
342
353
  return null;
@@ -350,7 +361,10 @@ export function loadDashboardConfig(ctx: Context) {
350
361
  }
351
362
  }
352
363
 
353
- export function saveDashboardConfig(ctx: Context, config: DashboardConfig) {
364
+ export function saveGlobalDashboardConfig(
365
+ ctx: Context,
366
+ config: GlobalDashboardConfig,
367
+ ) {
354
368
  const configFile = path.join(dashboardDir(), "config.json");
355
369
  if (!ctx.fs.exists(dashboardDir())) {
356
370
  ctx.fs.mkdir(dashboardDir(), { recursive: true });
@@ -358,6 +372,52 @@ export function saveDashboardConfig(ctx: Context, config: DashboardConfig) {
358
372
  ctx.fs.writeUtf8File(configFile, JSON.stringify(config));
359
373
  }
360
374
 
375
+ /**
376
+ * Project-scoped dashboard config (for anonymous dev) in
377
+ * project/.convex/local/default/dashboard.json
378
+ * (or legacyDeploymentStateDir/dashboard.json)
379
+ */
380
+ export type ProjectDashboardConfig = {
381
+ port: number;
382
+ };
383
+
384
+ function dashboardConfigPath(ctx: Context, deploymentName: string) {
385
+ return path.join(
386
+ deploymentStateDir(ctx, "anonymous", deploymentName),
387
+ "dashboard.json",
388
+ );
389
+ }
390
+
391
+ export function loadProjectDashboardConfig(
392
+ ctx: Context,
393
+ deploymentName: string,
394
+ ): ProjectDashboardConfig | null {
395
+ const configFile = dashboardConfigPath(ctx, deploymentName);
396
+ if (!ctx.fs.exists(configFile)) {
397
+ return null;
398
+ }
399
+ const content = ctx.fs.readUtf8File(configFile);
400
+ try {
401
+ return JSON.parse(content);
402
+ } catch (e) {
403
+ logVerbose(`Failed to parse project dashboard config: ${e as any}`);
404
+ return null;
405
+ }
406
+ }
407
+
408
+ export function saveProjectDashboardConfig(
409
+ ctx: Context,
410
+ deploymentName: string,
411
+ config: ProjectDashboardConfig,
412
+ ) {
413
+ const configPath = dashboardConfigPath(ctx, deploymentName);
414
+ const directoryPath = path.dirname(configPath);
415
+ if (!ctx.fs.exists(directoryPath)) {
416
+ ctx.fs.mkdir(directoryPath, { recursive: true });
417
+ }
418
+ ctx.fs.writeUtf8File(configPath, JSON.stringify(config));
419
+ }
420
+
361
421
  export function loadUuidForAnonymousUser(ctx: Context) {
362
422
  const configFile = path.join(
363
423
  rootDeploymentStateDir("anonymous"),
@@ -19,38 +19,24 @@ import {
19
19
  loadProjectLocalConfig,
20
20
  legacyDeploymentStateDir,
21
21
  rootDeploymentStateDir,
22
- saveDeploymentConfig,
23
22
  } from "./filePaths.js";
24
23
  import {
25
- ensureBackendRunning,
26
24
  ensureBackendStopped,
27
25
  localDeploymentUrl,
28
- runLocalBackend,
29
26
  withRunningBackend,
30
27
  } from "./run.js";
31
- import { handlePotentialUpgrade } from "./upgrade.js";
32
- import { OnDeploymentActivityFunc } from "../deployment.js";
33
- import { promptSearch } from "../utils/prompts.js";
28
+ import { handlePotentialUpgradeAndStart } from "./upgrade.js";
34
29
  import { LocalDeploymentError, printLocalDeploymentOnError } from "./errors.js";
35
30
  import {
36
31
  chooseLocalBackendPorts,
37
32
  printLocalDeploymentWelcomeMessage,
38
- isOffline,
39
- LOCAL_BACKEND_INSTANCE_SECRET,
40
33
  } from "./utils.js";
41
34
  import { ensureBackendBinaryDownloaded } from "./download.js";
42
35
  import { defaultEnvBackend } from "../defaultEnv.js";
43
36
  import { deploymentEnvBackend, EnvVar } from "../env.js";
44
37
  import { getProjectDetails } from "../deploymentSelection.js";
45
-
46
- export type DeploymentDetails = {
47
- deploymentName: string;
48
- deploymentUrl: string;
49
- adminKey: string;
50
- reference: string | null;
51
- isDefault: boolean;
52
- onActivity: OnDeploymentActivityFunc;
53
- };
38
+ import { DeploymentDetails } from "../deployment.js";
39
+ import { LEGACY_LOCAL_BACKEND_INSTANCE_SECRET } from "./secrets.js";
54
40
 
55
41
  export async function handleLocalDeployment(
56
42
  ctx: Context,
@@ -65,10 +51,6 @@ export async function handleLocalDeployment(
65
51
  forceUpgrade: boolean;
66
52
  },
67
53
  ): Promise<DeploymentDetails> {
68
- if (await isOffline()) {
69
- return handleOffline(ctx, options);
70
- }
71
-
72
54
  const existingDeploymentForProject = await getExistingDeployment(ctx, {
73
55
  projectSlug: options.projectSlug,
74
56
  teamSlug: options.teamSlug,
@@ -110,38 +92,34 @@ export async function handleLocalDeployment(
110
92
  requestedPorts: options.ports,
111
93
  suggestedPorts: existingDeploymentForProject?.config.ports,
112
94
  });
113
- const { deploymentName, adminKey, projectId } = await bigBrainStart(ctx, {
95
+ const { deploymentName, projectId } = await bigBrainStart(ctx, {
114
96
  port: cloudPort,
115
97
  projectSlug: options.projectSlug,
116
98
  teamSlug: options.teamSlug,
117
99
  instanceName: existingDeploymentForProject?.deploymentName ?? null,
118
100
  });
119
- const onActivity = async (isOffline: boolean, _wasOffline: boolean) => {
120
- await ensureBackendRunning(ctx, {
121
- cloudPort,
122
- deploymentName,
123
- maxTimeSecs: 5,
124
- });
125
- if (isOffline) {
126
- return;
127
- }
128
- await bigBrainRecordActivity(ctx, {
129
- instanceName: deploymentName,
130
- });
131
- };
132
101
 
133
- const { cleanupHandle } = await handlePotentialUpgrade(ctx, {
134
- deploymentKind: "local",
135
- deploymentName,
136
- oldVersion: existingDeploymentForProject?.config.backendVersion ?? null,
137
- newBinaryPath: binaryPath,
138
- newVersion: version,
139
- ports: { cloud: cloudPort, site: sitePort },
140
- adminKey,
141
- instanceSecret: LOCAL_BACKEND_INSTANCE_SECRET,
142
- forceUpgrade: options.forceUpgrade,
143
- cloudProjectId: projectId,
144
- });
102
+ const { cleanupHandle, adminKey } = await handlePotentialUpgradeAndStart(
103
+ ctx,
104
+ {
105
+ deploymentKind: "local",
106
+ deploymentName,
107
+ oldVersion: existingDeploymentForProject?.config.backendVersion ?? null,
108
+ newBinaryPath: binaryPath,
109
+ newVersion: version,
110
+ ports: { cloud: cloudPort, site: sitePort },
111
+ existingCredentials: existingDeploymentForProject?.config
112
+ ? {
113
+ adminKey: existingDeploymentForProject?.config.adminKey,
114
+ instanceSecret:
115
+ existingDeploymentForProject?.config.instanceSecret ??
116
+ LEGACY_LOCAL_BACKEND_INSTANCE_SECRET,
117
+ }
118
+ : null,
119
+ forceUpgrade: options.forceUpgrade,
120
+ cloudProjectId: projectId,
121
+ },
122
+ );
145
123
 
146
124
  if (isFirstTime) {
147
125
  await importDefaultEnvVars(ctx, {
@@ -156,22 +134,32 @@ export async function handleLocalDeployment(
156
134
  // Periodically report activity to BigBrain every 60 seconds.
157
135
  // Uses self-scheduling setTimeout to avoid overlapping requests.
158
136
  let activityTimeout: ReturnType<typeof setTimeout> | null = null;
159
- const scheduleActivityPing = () => {
137
+ let activityPingStopped = false;
138
+ async function activityPing() {
139
+ if (activityPingStopped) {
140
+ return;
141
+ }
142
+ try {
143
+ await bigBrainRecordActivity(ctx, {
144
+ instanceName: deploymentName,
145
+ adminKey,
146
+ });
147
+ } catch {
148
+ // Best-effort: don't crash on failed pings
149
+ }
150
+
151
+ if (activityPingStopped) {
152
+ return;
153
+ }
160
154
  activityTimeout = setTimeout(async () => {
161
- try {
162
- await bigBrainRecordActivity(ctx, {
163
- instanceName: deploymentName,
164
- });
165
- } catch {
166
- // Best-effort: don't crash on failed pings
167
- }
168
- scheduleActivityPing();
155
+ void activityPing();
169
156
  }, 60_000);
170
- };
171
- scheduleActivityPing();
157
+ }
158
+ void activityPing();
172
159
 
173
160
  const cleanupFunc = ctx.removeCleanup(cleanupHandle);
174
161
  ctx.registerCleanup(async (exitCode, err) => {
162
+ activityPingStopped = true;
175
163
  if (activityTimeout !== null) {
176
164
  clearTimeout(activityTimeout);
177
165
  }
@@ -190,7 +178,6 @@ export async function handleLocalDeployment(
190
178
  deploymentUrl: localDeploymentUrl(cloudPort),
191
179
  reference: null,
192
180
  isDefault: false,
193
- onActivity,
194
181
  };
195
182
  }
196
183
 
@@ -218,64 +205,6 @@ export async function loadLocalDeploymentCredentials(
218
205
  };
219
206
  }
220
207
 
221
- async function handleOffline(
222
- ctx: Context,
223
- options: {
224
- teamSlug: string;
225
- projectSlug: string;
226
- ports: { cloud: number | undefined; site: number | undefined };
227
- },
228
- ): Promise<DeploymentDetails> {
229
- const { deploymentName, config } =
230
- await chooseFromExistingLocalDeployments(ctx);
231
- const { binaryPath } = await ensureBackendBinaryDownloaded(ctx, {
232
- kind: "version",
233
- version: config.backendVersion,
234
- });
235
- const { cloudPort, sitePort } = await chooseLocalBackendPorts(ctx, {
236
- requestedPorts: options.ports,
237
- // FIXME: This doesn’t try to reuse the ports already assigned in the config.
238
- // Please update this if we ever support offline mode (currently this is dead code).
239
- });
240
- saveDeploymentConfig(ctx, "local", deploymentName, config);
241
- await runLocalBackend(ctx, {
242
- binaryPath,
243
- ports: { cloud: cloudPort, site: sitePort },
244
- deploymentName,
245
- deploymentKind: "local",
246
- instanceSecret: LOCAL_BACKEND_INSTANCE_SECRET,
247
- isLatestVersion: false,
248
- });
249
- return {
250
- adminKey: config.adminKey,
251
- deploymentName,
252
- deploymentUrl: localDeploymentUrl(cloudPort),
253
- reference: null,
254
- isDefault: false,
255
- onActivity: async (isOffline: boolean, wasOffline: boolean) => {
256
- await ensureBackendRunning(ctx, {
257
- cloudPort,
258
- deploymentName,
259
- maxTimeSecs: 5,
260
- });
261
- if (isOffline) {
262
- return;
263
- }
264
- if (wasOffline) {
265
- await bigBrainStart(ctx, {
266
- port: cloudPort,
267
- projectSlug: options.projectSlug,
268
- teamSlug: options.teamSlug,
269
- instanceName: deploymentName,
270
- });
271
- }
272
- await bigBrainRecordActivity(ctx, {
273
- instanceName: deploymentName,
274
- });
275
- },
276
- };
277
- }
278
-
279
208
  async function getExistingDeployment(
280
209
  ctx: Context,
281
210
  options: {
@@ -341,73 +270,6 @@ async function getLegacyLocalDeployments(ctx: Context): Promise<
341
270
  });
342
271
  }
343
272
 
344
- /**
345
- * Get all local deployments from both project-local and legacy locations.
346
- */
347
- async function getLocalDeployments(ctx: Context): Promise<
348
- Array<{
349
- deploymentName: string;
350
- config: LocalDeploymentConfig;
351
- }>
352
- > {
353
- const deployments: Array<{
354
- deploymentName: string;
355
- config: LocalDeploymentConfig;
356
- }> = [];
357
-
358
- // Check project-local storage
359
- const projectLocal = loadProjectLocalConfig(ctx);
360
- if (
361
- projectLocal !== null &&
362
- projectLocal.deploymentName.startsWith("local-")
363
- ) {
364
- deployments.push(projectLocal);
365
- }
366
-
367
- // Also include legacy deployments (but avoid duplicates)
368
- const legacyDeployments = await getLegacyLocalDeployments(ctx);
369
- for (const legacy of legacyDeployments) {
370
- if (!deployments.some((d) => d.deploymentName === legacy.deploymentName)) {
371
- deployments.push(legacy);
372
- }
373
- }
374
-
375
- return deployments;
376
- }
377
-
378
- async function chooseFromExistingLocalDeployments(ctx: Context): Promise<{
379
- deploymentName: string;
380
- config: LocalDeploymentConfig;
381
- }> {
382
- const localDeployments = await getLocalDeployments(ctx);
383
-
384
- if (localDeployments.length === 0) {
385
- return ctx.crash({
386
- exitCode: 1,
387
- errorType: "fatal",
388
- printedMessage:
389
- "No local deployments found. Please run `npx convex dev` while online first.",
390
- });
391
- }
392
-
393
- // Auto-select if there's only one deployment
394
- if (localDeployments.length === 1) {
395
- logVerbose(
396
- `Auto-selecting the only local deployment: ${localDeployments[0].deploymentName}`,
397
- );
398
- return localDeployments[0];
399
- }
400
-
401
- // Multiple deployments (legacy) - prompt user to choose
402
- return promptSearch(ctx, {
403
- message: "Choose from an existing local deployment:",
404
- choices: localDeployments.map((d) => ({
405
- name: d.deploymentName,
406
- value: d,
407
- })),
408
- });
409
- }
410
-
411
273
  /** Copies the default dev env vars from big brain the first time the local dev backend is started */
412
274
  export async function importDefaultEnvVars(
413
275
  ctx: Context,