convex 1.48.0-alpha.0 → 1.48.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 (353) hide show
  1. package/CHANGELOG.md +28 -4
  2. package/README.md +1 -1
  3. package/dist/browser.bundle.js +1 -1
  4. package/dist/browser.bundle.js.map +1 -1
  5. package/dist/cjs/bundler/context.js +5 -7
  6. package/dist/cjs/bundler/context.js.map +2 -2
  7. package/dist/cjs/bundler/fs.js +6 -47
  8. package/dist/cjs/bundler/fs.js.map +2 -2
  9. package/dist/cjs/cli/aiFiles.js +26 -4
  10. package/dist/cjs/cli/aiFiles.js.map +2 -2
  11. package/dist/cjs/cli/configure.js +2 -15
  12. package/dist/cjs/cli/configure.js.map +2 -2
  13. package/dist/cjs/cli/env.js +6 -3
  14. package/dist/cjs/cli/env.js.map +2 -2
  15. package/dist/cjs/cli/envDefault.js +5 -2
  16. package/dist/cjs/cli/envDefault.js.map +2 -2
  17. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js +2 -1
  18. package/dist/cjs/cli/lib/aiFiles/guidelinesmd.js.map +2 -2
  19. package/dist/cjs/cli/lib/aiFiles/index.js +13 -2
  20. package/dist/cjs/cli/lib/aiFiles/index.js.map +2 -2
  21. package/dist/cjs/cli/lib/aiFiles/skills.js +6 -5
  22. package/dist/cjs/cli/lib/aiFiles/skills.js.map +2 -2
  23. package/dist/cjs/cli/lib/aiFiles/state.js +1 -1
  24. package/dist/cjs/cli/lib/aiFiles/state.js.map +1 -1
  25. package/dist/cjs/cli/lib/components.js +12 -2
  26. package/dist/cjs/cli/lib/components.js.map +2 -2
  27. package/dist/cjs/cli/lib/deployApi/startPush.js +42 -1
  28. package/dist/cjs/cli/lib/deployApi/startPush.js.map +2 -2
  29. package/dist/cjs/cli/lib/deployment.js +0 -2
  30. package/dist/cjs/cli/lib/deployment.js.map +2 -2
  31. package/dist/cjs/cli/lib/deploymentSelection.js +18 -27
  32. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  33. package/dist/cjs/cli/lib/env.js +9 -1
  34. package/dist/cjs/cli/lib/env.js.map +2 -2
  35. package/dist/cjs/cli/lib/envvars.js +0 -32
  36. package/dist/cjs/cli/lib/envvars.js.map +2 -2
  37. package/dist/cjs/cli/lib/fsUtils.js +1 -1
  38. package/dist/cjs/cli/lib/fsUtils.js.map +2 -2
  39. package/dist/cjs/cli/lib/generateDocs.js +1 -1
  40. package/dist/cjs/cli/lib/generateDocs.js.map +1 -1
  41. package/dist/cjs/cli/lib/localDeployment/dashboard.js +11 -1
  42. package/dist/cjs/cli/lib/localDeployment/dashboard.js.map +2 -2
  43. package/dist/cjs/cli/lib/localDeployment/download.js +16 -1
  44. package/dist/cjs/cli/lib/localDeployment/download.js.map +2 -2
  45. package/dist/cjs/cli/lib/localDeployment/utils.js +4 -0
  46. package/dist/cjs/cli/lib/localDeployment/utils.js.map +2 -2
  47. package/dist/cjs/cli/lib/login.js +4 -28
  48. package/dist/cjs/cli/lib/login.js.map +2 -2
  49. package/dist/cjs/cli/lib/mcp/requestContext.js +1 -5
  50. package/dist/cjs/cli/lib/mcp/requestContext.js.map +2 -2
  51. package/dist/cjs/cli/lib/utils/aiAgent.js +32 -0
  52. package/dist/cjs/cli/lib/utils/aiAgent.js.map +7 -0
  53. package/dist/cjs/cli/lib/utils/utils.js +9 -18
  54. package/dist/cjs/cli/lib/utils/utils.js.map +2 -2
  55. package/dist/cjs/cli/login.js +114 -213
  56. package/dist/cjs/cli/login.js.map +3 -3
  57. package/dist/cjs/cli/logout.js +12 -113
  58. package/dist/cjs/cli/logout.js.map +3 -3
  59. package/dist/cjs/cli/mcp.js +10 -43
  60. package/dist/cjs/cli/mcp.js.map +3 -3
  61. package/dist/cjs/cli/program.js +1 -2
  62. package/dist/cjs/cli/program.js.map +2 -2
  63. package/dist/cjs/index.js +1 -1
  64. package/dist/cjs/index.js.map +1 -1
  65. package/dist/cjs/server/authentication.js.map +1 -1
  66. package/dist/cjs/server/components/index.js.map +2 -2
  67. package/dist/cjs/server/impl/authentication_impl.js +15 -1
  68. package/dist/cjs/server/impl/authentication_impl.js.map +2 -2
  69. package/dist/cjs/server/impl/registration_impl.js +1 -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/server/schema.js +0 -2
  75. package/dist/cjs/server/schema.js.map +2 -2
  76. package/dist/cjs/server/storage.js.map +1 -1
  77. package/dist/cjs-types/bundler/context.d.ts +0 -5
  78. package/dist/cjs-types/bundler/context.d.ts.map +1 -1
  79. package/dist/cjs-types/bundler/fs.d.ts +2 -7
  80. package/dist/cjs-types/bundler/fs.d.ts.map +1 -1
  81. package/dist/cjs-types/cli/aiFiles.d.ts.map +1 -1
  82. package/dist/cjs-types/cli/configure.d.ts.map +1 -1
  83. package/dist/cjs-types/cli/env.d.ts.map +1 -1
  84. package/dist/cjs-types/cli/envDefault.d.ts.map +1 -1
  85. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts +3 -1
  86. package/dist/cjs-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -1
  87. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts +11 -1
  88. package/dist/cjs-types/cli/lib/aiFiles/index.d.ts.map +1 -1
  89. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts +4 -1
  90. package/dist/cjs-types/cli/lib/aiFiles/skills.d.ts.map +1 -1
  91. package/dist/cjs-types/cli/lib/components.d.ts.map +1 -1
  92. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts +13 -0
  93. package/dist/cjs-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  94. package/dist/cjs-types/cli/lib/deployment.d.ts +0 -2
  95. package/dist/cjs-types/cli/lib/deployment.d.ts.map +1 -1
  96. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +1 -2
  97. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  98. package/dist/cjs-types/cli/lib/env.d.ts +1 -0
  99. package/dist/cjs-types/cli/lib/env.d.ts.map +1 -1
  100. package/dist/cjs-types/cli/lib/envvars.d.ts +0 -6
  101. package/dist/cjs-types/cli/lib/envvars.d.ts.map +1 -1
  102. package/dist/cjs-types/cli/lib/fsUtils.d.ts +0 -1
  103. package/dist/cjs-types/cli/lib/fsUtils.d.ts.map +1 -1
  104. package/dist/cjs-types/cli/lib/localDeployment/dashboard.d.ts +6 -0
  105. package/dist/cjs-types/cli/lib/localDeployment/dashboard.d.ts.map +1 -1
  106. package/dist/cjs-types/cli/lib/localDeployment/download.d.ts.map +1 -1
  107. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts +1 -0
  108. package/dist/cjs-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  109. package/dist/cjs-types/cli/lib/login.d.ts +1 -3
  110. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  111. package/dist/cjs-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
  112. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  113. package/dist/cjs-types/cli/lib/utils/aiAgent.d.ts +8 -0
  114. package/dist/cjs-types/cli/lib/utils/aiAgent.d.ts.map +1 -0
  115. package/dist/cjs-types/cli/lib/utils/utils.d.ts +0 -5
  116. package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
  117. package/dist/cjs-types/cli/login.d.ts +0 -21
  118. package/dist/cjs-types/cli/login.d.ts.map +1 -1
  119. package/dist/cjs-types/cli/logout.d.ts +1 -8
  120. package/dist/cjs-types/cli/logout.d.ts.map +1 -1
  121. package/dist/cjs-types/cli/mcp.d.ts.map +1 -1
  122. package/dist/cjs-types/cli/program.d.ts.map +1 -1
  123. package/dist/cjs-types/index.d.ts +1 -1
  124. package/dist/cjs-types/index.d.ts.map +1 -1
  125. package/dist/cjs-types/server/authentication.d.ts +65 -0
  126. package/dist/cjs-types/server/authentication.d.ts.map +1 -1
  127. package/dist/cjs-types/server/components/index.d.ts +6 -0
  128. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  129. package/dist/cjs-types/server/impl/authentication_impl.d.ts +2 -1
  130. package/dist/cjs-types/server/impl/authentication_impl.d.ts.map +1 -1
  131. package/dist/cjs-types/server/index.d.ts +2 -2
  132. package/dist/cjs-types/server/index.d.ts.map +1 -1
  133. package/dist/cjs-types/server/meta.d.ts +8 -0
  134. package/dist/cjs-types/server/meta.d.ts.map +1 -1
  135. package/dist/cjs-types/server/registration.d.ts +5 -3
  136. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  137. package/dist/cjs-types/server/schema.d.ts +0 -2
  138. package/dist/cjs-types/server/schema.d.ts.map +1 -1
  139. package/dist/cjs-types/server/storage.d.ts +28 -25
  140. package/dist/cjs-types/server/storage.d.ts.map +1 -1
  141. package/dist/cli.bundle.cjs +1379 -2142
  142. package/dist/cli.bundle.cjs.map +4 -4
  143. package/dist/esm/bundler/context.js +5 -7
  144. package/dist/esm/bundler/context.js.map +2 -2
  145. package/dist/esm/bundler/fs.js +6 -47
  146. package/dist/esm/bundler/fs.js.map +2 -2
  147. package/dist/esm/cli/aiFiles.js +26 -4
  148. package/dist/esm/cli/aiFiles.js.map +2 -2
  149. package/dist/esm/cli/configure.js +2 -15
  150. package/dist/esm/cli/configure.js.map +2 -2
  151. package/dist/esm/cli/env.js +6 -3
  152. package/dist/esm/cli/env.js.map +2 -2
  153. package/dist/esm/cli/envDefault.js +5 -2
  154. package/dist/esm/cli/envDefault.js.map +2 -2
  155. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js +2 -1
  156. package/dist/esm/cli/lib/aiFiles/guidelinesmd.js.map +2 -2
  157. package/dist/esm/cli/lib/aiFiles/index.js +13 -2
  158. package/dist/esm/cli/lib/aiFiles/index.js.map +2 -2
  159. package/dist/esm/cli/lib/aiFiles/skills.js +6 -5
  160. package/dist/esm/cli/lib/aiFiles/skills.js.map +2 -2
  161. package/dist/esm/cli/lib/aiFiles/state.js +1 -1
  162. package/dist/esm/cli/lib/aiFiles/state.js.map +1 -1
  163. package/dist/esm/cli/lib/components.js +15 -2
  164. package/dist/esm/cli/lib/components.js.map +2 -2
  165. package/dist/esm/cli/lib/deployApi/startPush.js +39 -0
  166. package/dist/esm/cli/lib/deployApi/startPush.js.map +2 -2
  167. package/dist/esm/cli/lib/deployment.js +2 -2
  168. package/dist/esm/cli/lib/deployment.js.map +2 -2
  169. package/dist/esm/cli/lib/deploymentSelection.js +20 -35
  170. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  171. package/dist/esm/cli/lib/env.js +9 -1
  172. package/dist/esm/cli/lib/env.js.map +2 -2
  173. package/dist/esm/cli/lib/envvars.js +1 -35
  174. package/dist/esm/cli/lib/envvars.js.map +2 -2
  175. package/dist/esm/cli/lib/fsUtils.js +1 -1
  176. package/dist/esm/cli/lib/fsUtils.js.map +2 -2
  177. package/dist/esm/cli/lib/generateDocs.js +1 -1
  178. package/dist/esm/cli/lib/generateDocs.js.map +1 -1
  179. package/dist/esm/cli/lib/localDeployment/dashboard.js +9 -0
  180. package/dist/esm/cli/lib/localDeployment/dashboard.js.map +2 -2
  181. package/dist/esm/cli/lib/localDeployment/download.js +16 -1
  182. package/dist/esm/cli/lib/localDeployment/download.js.map +2 -2
  183. package/dist/esm/cli/lib/localDeployment/utils.js +3 -0
  184. package/dist/esm/cli/lib/localDeployment/utils.js.map +2 -2
  185. package/dist/esm/cli/lib/login.js +4 -36
  186. package/dist/esm/cli/lib/login.js.map +2 -2
  187. package/dist/esm/cli/lib/mcp/requestContext.js +1 -5
  188. package/dist/esm/cli/lib/mcp/requestContext.js.map +2 -2
  189. package/dist/esm/cli/lib/utils/aiAgent.js +10 -0
  190. package/dist/esm/cli/lib/utils/aiAgent.js.map +7 -0
  191. package/dist/esm/cli/lib/utils/utils.js +9 -16
  192. package/dist/esm/cli/lib/utils/utils.js.map +2 -2
  193. package/dist/esm/cli/login.js +112 -225
  194. package/dist/esm/cli/login.js.map +3 -3
  195. package/dist/esm/cli/logout.js +13 -110
  196. package/dist/esm/cli/logout.js.map +2 -2
  197. package/dist/esm/cli/mcp.js +10 -34
  198. package/dist/esm/cli/mcp.js.map +2 -2
  199. package/dist/esm/cli/program.js +1 -2
  200. package/dist/esm/cli/program.js.map +2 -2
  201. package/dist/esm/index.js +1 -1
  202. package/dist/esm/index.js.map +1 -1
  203. package/dist/esm/server/components/index.js.map +2 -2
  204. package/dist/esm/server/impl/authentication_impl.js +13 -0
  205. package/dist/esm/server/impl/authentication_impl.js.map +2 -2
  206. package/dist/esm/server/impl/registration_impl.js +2 -2
  207. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  208. package/dist/esm/server/index.js.map +2 -2
  209. package/dist/esm/server/schema.js +0 -2
  210. package/dist/esm/server/schema.js.map +2 -2
  211. package/dist/esm-types/bundler/context.d.ts +0 -5
  212. package/dist/esm-types/bundler/context.d.ts.map +1 -1
  213. package/dist/esm-types/bundler/fs.d.ts +2 -7
  214. package/dist/esm-types/bundler/fs.d.ts.map +1 -1
  215. package/dist/esm-types/cli/aiFiles.d.ts.map +1 -1
  216. package/dist/esm-types/cli/configure.d.ts.map +1 -1
  217. package/dist/esm-types/cli/env.d.ts.map +1 -1
  218. package/dist/esm-types/cli/envDefault.d.ts.map +1 -1
  219. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts +3 -1
  220. package/dist/esm-types/cli/lib/aiFiles/guidelinesmd.d.ts.map +1 -1
  221. package/dist/esm-types/cli/lib/aiFiles/index.d.ts +11 -1
  222. package/dist/esm-types/cli/lib/aiFiles/index.d.ts.map +1 -1
  223. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts +4 -1
  224. package/dist/esm-types/cli/lib/aiFiles/skills.d.ts.map +1 -1
  225. package/dist/esm-types/cli/lib/components.d.ts.map +1 -1
  226. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts +13 -0
  227. package/dist/esm-types/cli/lib/deployApi/startPush.d.ts.map +1 -1
  228. package/dist/esm-types/cli/lib/deployment.d.ts +0 -2
  229. package/dist/esm-types/cli/lib/deployment.d.ts.map +1 -1
  230. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +1 -2
  231. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  232. package/dist/esm-types/cli/lib/env.d.ts +1 -0
  233. package/dist/esm-types/cli/lib/env.d.ts.map +1 -1
  234. package/dist/esm-types/cli/lib/envvars.d.ts +0 -6
  235. package/dist/esm-types/cli/lib/envvars.d.ts.map +1 -1
  236. package/dist/esm-types/cli/lib/fsUtils.d.ts +0 -1
  237. package/dist/esm-types/cli/lib/fsUtils.d.ts.map +1 -1
  238. package/dist/esm-types/cli/lib/localDeployment/dashboard.d.ts +6 -0
  239. package/dist/esm-types/cli/lib/localDeployment/dashboard.d.ts.map +1 -1
  240. package/dist/esm-types/cli/lib/localDeployment/download.d.ts.map +1 -1
  241. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts +1 -0
  242. package/dist/esm-types/cli/lib/localDeployment/utils.d.ts.map +1 -1
  243. package/dist/esm-types/cli/lib/login.d.ts +1 -3
  244. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  245. package/dist/esm-types/cli/lib/mcp/requestContext.d.ts.map +1 -1
  246. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +2 -2
  247. package/dist/esm-types/cli/lib/utils/aiAgent.d.ts +8 -0
  248. package/dist/esm-types/cli/lib/utils/aiAgent.d.ts.map +1 -0
  249. package/dist/esm-types/cli/lib/utils/utils.d.ts +0 -5
  250. package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
  251. package/dist/esm-types/cli/login.d.ts +0 -21
  252. package/dist/esm-types/cli/login.d.ts.map +1 -1
  253. package/dist/esm-types/cli/logout.d.ts +1 -8
  254. package/dist/esm-types/cli/logout.d.ts.map +1 -1
  255. package/dist/esm-types/cli/mcp.d.ts.map +1 -1
  256. package/dist/esm-types/cli/program.d.ts.map +1 -1
  257. package/dist/esm-types/index.d.ts +1 -1
  258. package/dist/esm-types/index.d.ts.map +1 -1
  259. package/dist/esm-types/server/authentication.d.ts +65 -0
  260. package/dist/esm-types/server/authentication.d.ts.map +1 -1
  261. package/dist/esm-types/server/components/index.d.ts +6 -0
  262. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  263. package/dist/esm-types/server/impl/authentication_impl.d.ts +2 -1
  264. package/dist/esm-types/server/impl/authentication_impl.d.ts.map +1 -1
  265. package/dist/esm-types/server/index.d.ts +2 -2
  266. package/dist/esm-types/server/index.d.ts.map +1 -1
  267. package/dist/esm-types/server/meta.d.ts +8 -0
  268. package/dist/esm-types/server/meta.d.ts.map +1 -1
  269. package/dist/esm-types/server/registration.d.ts +5 -3
  270. package/dist/esm-types/server/registration.d.ts.map +1 -1
  271. package/dist/esm-types/server/schema.d.ts +0 -2
  272. package/dist/esm-types/server/schema.d.ts.map +1 -1
  273. package/dist/esm-types/server/storage.d.ts +28 -25
  274. package/dist/esm-types/server/storage.d.ts.map +1 -1
  275. package/dist/react.bundle.js +1 -1
  276. package/dist/react.bundle.js.map +1 -1
  277. package/package.json +3 -2
  278. package/schemas/convex.schema.json +1 -2
  279. package/src/bundler/context.ts +5 -9
  280. package/src/bundler/fs.ts +5 -54
  281. package/src/cli/aiFiles.ts +30 -4
  282. package/src/cli/configure.ts +2 -18
  283. package/src/cli/env.ts +4 -1
  284. package/src/cli/envDefault.ts +4 -1
  285. package/src/cli/lib/aiFiles/guidelinesmd.ts +7 -2
  286. package/src/cli/lib/aiFiles/index.ts +23 -3
  287. package/src/cli/lib/aiFiles/skills.ts +11 -7
  288. package/src/cli/lib/aiFiles/state.ts +1 -1
  289. package/src/cli/lib/components.ts +18 -3
  290. package/src/cli/lib/deployApi/startPush.ts +64 -0
  291. package/src/cli/lib/deployment.ts +2 -2
  292. package/src/cli/lib/deploymentSelection.ts +18 -37
  293. package/src/cli/lib/env.ts +13 -2
  294. package/src/cli/lib/envvars.ts +1 -43
  295. package/src/cli/lib/fsUtils.ts +2 -5
  296. package/src/cli/lib/generateDocs.ts +1 -1
  297. package/src/cli/lib/localDeployment/dashboard.ts +19 -0
  298. package/src/cli/lib/localDeployment/download.ts +17 -1
  299. package/src/cli/lib/localDeployment/utils.ts +4 -0
  300. package/src/cli/lib/login.ts +3 -47
  301. package/src/cli/lib/mcp/requestContext.ts +1 -7
  302. package/src/cli/lib/utils/aiAgent.ts +15 -0
  303. package/src/cli/lib/utils/utils.ts +15 -19
  304. package/src/cli/login.ts +168 -293
  305. package/src/cli/logout.ts +17 -142
  306. package/src/cli/mcp.ts +11 -48
  307. package/src/cli/program.ts +0 -2
  308. package/src/index.ts +1 -1
  309. package/src/server/authentication.ts +79 -0
  310. package/src/server/components/index.ts +11 -0
  311. package/src/server/impl/authentication_impl.ts +15 -1
  312. package/src/server/impl/registration_impl.ts +2 -2
  313. package/src/server/index.ts +6 -0
  314. package/src/server/meta.ts +0 -8
  315. package/src/server/registration.ts +5 -2
  316. package/src/server/schema.ts +0 -2
  317. package/src/server/storage.ts +29 -42
  318. package/dist/cjs/cli/account.js +0 -237
  319. package/dist/cjs/cli/account.js.map +0 -7
  320. package/dist/cjs/cli/lib/deployKeyWarning.js +0 -43
  321. package/dist/cjs/cli/lib/deployKeyWarning.js.map +0 -7
  322. package/dist/cjs/cli/lib/profileManagement.js +0 -196
  323. package/dist/cjs/cli/lib/profileManagement.js.map +0 -7
  324. package/dist/cjs/cli/lib/profiles.js +0 -333
  325. package/dist/cjs/cli/lib/profiles.js.map +0 -7
  326. package/dist/cjs-types/cli/account.d.ts +0 -6
  327. package/dist/cjs-types/cli/account.d.ts.map +0 -1
  328. package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts +0 -3
  329. package/dist/cjs-types/cli/lib/deployKeyWarning.d.ts.map +0 -1
  330. package/dist/cjs-types/cli/lib/profileManagement.d.ts +0 -10
  331. package/dist/cjs-types/cli/lib/profileManagement.d.ts.map +0 -1
  332. package/dist/cjs-types/cli/lib/profiles.d.ts +0 -59
  333. package/dist/cjs-types/cli/lib/profiles.d.ts.map +0 -1
  334. package/dist/esm/cli/account.js +0 -221
  335. package/dist/esm/cli/account.js.map +0 -7
  336. package/dist/esm/cli/lib/deployKeyWarning.js +0 -27
  337. package/dist/esm/cli/lib/deployKeyWarning.js.map +0 -7
  338. package/dist/esm/cli/lib/profileManagement.js +0 -179
  339. package/dist/esm/cli/lib/profileManagement.js.map +0 -7
  340. package/dist/esm/cli/lib/profiles.js +0 -288
  341. package/dist/esm/cli/lib/profiles.js.map +0 -7
  342. package/dist/esm-types/cli/account.d.ts +0 -6
  343. package/dist/esm-types/cli/account.d.ts.map +0 -1
  344. package/dist/esm-types/cli/lib/deployKeyWarning.d.ts +0 -3
  345. package/dist/esm-types/cli/lib/deployKeyWarning.d.ts.map +0 -1
  346. package/dist/esm-types/cli/lib/profileManagement.d.ts +0 -10
  347. package/dist/esm-types/cli/lib/profileManagement.d.ts.map +0 -1
  348. package/dist/esm-types/cli/lib/profiles.d.ts +0 -59
  349. package/dist/esm-types/cli/lib/profiles.d.ts.map +0 -1
  350. package/src/cli/account.ts +0 -320
  351. package/src/cli/lib/deployKeyWarning.ts +0 -43
  352. package/src/cli/lib/profileManagement.ts +0 -262
  353. package/src/cli/lib/profiles.ts +0 -398
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/lib/envvars.ts"],
4
- "sourcesContent": ["/**\n * Help the developer store the CONVEX_URL environment variable.\n */\nimport { chalkStderr } from \"chalk\";\nimport * as dotenv from \"dotenv\";\n\nimport { Context } from \"../../bundler/context.js\";\nimport { logWarning } from \"../../bundler/log.js\";\nimport {\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME,\n readDeployKeyEnvVar,\n ENV_VAR_FILE_PATH,\n loadPackageJson,\n} from \"./utils/utils.js\";\n\nexport const CONVEX_ACCOUNT_ENV_VAR = \"CONVEX_ACCOUNT\";\n\nconst loadedEnvironment = new Map<string, { value: string; source: string }>();\n\nexport function loadProjectEnvironmentVariables() {\n // Only remove values supplied by our previous load; later shell overrides win.\n for (const [name, { value }] of loadedEnvironment) {\n if (process.env[name] === value) delete process.env[name];\n }\n loadedEnvironment.clear();\n const environment: Record<string, string> = {};\n for (const [name, value] of Object.entries(process.env)) {\n if (value !== undefined) environment[name] = value;\n }\n for (const file of [ENV_VAR_FILE_PATH, \".env\"]) {\n dotenv.config({ path: file, processEnv: environment });\n for (const [name, value] of Object.entries(environment)) {\n // Account selection reads the current project's files directly.\n if (name !== CONVEX_ACCOUNT_ENV_VAR && process.env[name] === undefined) {\n process.env[name] = value;\n loadedEnvironment.set(name, { value, source: file });\n }\n }\n }\n}\n\nexport function getDeployKeySource(key: string) {\n const envVarName =\n readDeployKeyEnvVar((name) => process.env[name])?.name ??\n CONVEX_DEPLOY_KEY_ENV_VAR_NAME;\n const loaded = loadedEnvironment.get(envVarName);\n return {\n envVarName,\n source: loaded?.value === key ? loaded.source : \"shell environment\",\n };\n}\n\nconst _FRAMEWORKS = [\n \"create-react-app\",\n \"Next.js\",\n \"Vite\",\n \"Remix\",\n \"SvelteKit\",\n \"Expo\",\n \"TanStackStart\",\n] as const;\ntype Framework = (typeof _FRAMEWORKS)[number];\n\n/**\n * A configuration for writing the actual (framework specific) `CONVEX_URL`\n * and `CONVEX_SITE_URL` environment variables to a \".env\" type file.\n *\n * May be `null` if there was an error determining any of the field values.\n */\ntype EnvFileUrlConfig = {\n /** The name of the file - typically `.env.local` */\n envFile: string;\n /**\n * The framework specific `CONVEX_URL`\n *\n * If `null`, ignore and don't update that environment variable.\n */\n convexUrlEnvVar: string | null;\n /**\n * The framework specific `CONVEX_SITE_URL`\n *\n * If `null`, ignore and don't update that environment variable.\n */\n siteUrlEnvVar: string | null;\n /** Existing content loaded from the `envFile`, if it exists */\n existingFileContent: string | null;\n} | null;\n\nexport async function writeUrlsToEnvFile(\n ctx: Context,\n options: {\n convexUrl: string;\n siteUrl?: string | null | undefined;\n },\n): Promise<EnvFileUrlConfig> {\n const envFileConfig = await loadEnvFileUrlConfig(ctx, options);\n\n if (envFileConfig === null) {\n return null;\n }\n\n const { envFile, convexUrlEnvVar, siteUrlEnvVar, existingFileContent } =\n envFileConfig;\n let updatedFileContent: string | null = null;\n if (convexUrlEnvVar) {\n updatedFileContent = changedEnvVarFile({\n existingFileContent,\n envVarName: convexUrlEnvVar,\n envVarValue: options.convexUrl,\n commentAfterValue: null,\n commentOnPreviousLine: null,\n })!;\n }\n if (siteUrlEnvVar && options.siteUrl) {\n updatedFileContent = changedEnvVarFile({\n existingFileContent: updatedFileContent ?? existingFileContent,\n envVarName: siteUrlEnvVar,\n envVarValue: options.siteUrl,\n commentAfterValue: null,\n commentOnPreviousLine: null,\n })!;\n }\n if (updatedFileContent) {\n ctx.fs.writeUtf8File(envFile, updatedFileContent);\n }\n\n return envFileConfig;\n}\n\nexport function changedEnvVarFile({\n existingFileContent,\n envVarName,\n envVarValue,\n commentAfterValue,\n commentOnPreviousLine,\n}: {\n existingFileContent: string | null;\n envVarName: string;\n envVarValue: string;\n commentAfterValue: string | null;\n commentOnPreviousLine: string | null;\n}): string | null {\n const varAssignment = `${envVarName}=${envVarValue}${\n commentAfterValue === null ? \"\" : ` # ${commentAfterValue}`\n }`;\n const commentOnPreviousLineWithLineBreak =\n commentOnPreviousLine === null ? \"\" : `${commentOnPreviousLine}\\n`;\n if (existingFileContent === null) {\n return `${commentOnPreviousLineWithLineBreak}${varAssignment}\\n`;\n }\n const config = dotenv.parse(existingFileContent);\n const existing = config[envVarName];\n if (existing === envVarValue) {\n return null;\n }\n if (existing !== undefined) {\n return existingFileContent.replace(\n getEnvVarRegex(envVarName),\n `${varAssignment}`,\n );\n } else {\n const doubleLineBreak = existingFileContent.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n return (\n existingFileContent +\n doubleLineBreak +\n commentOnPreviousLineWithLineBreak +\n varAssignment +\n \"\\n\"\n );\n }\n}\n\nexport function getEnvVarRegex(envVarName: string) {\n return new RegExp(`^${envVarName}.*$`, \"m\");\n}\n\nexport async function suggestedEnvVarNames(ctx: Context): Promise<{\n detectedFramework?: Framework;\n convexUrlEnvVar: ConvexUrlEnvVar;\n convexSiteEnvVar: ConvexSiteUrlEnvVar;\n frontendDevUrl?: string;\n publicPrefix?: string;\n}> {\n // no package.json, that's fine, just guess\n if (!ctx.fs.exists(\"package.json\")) {\n return {\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n };\n }\n\n const packages = await loadPackageJson(ctx);\n\n // Is it create-react-app?\n const isCreateReactApp = \"react-scripts\" in packages;\n if (isCreateReactApp) {\n return {\n detectedFramework: \"create-react-app\",\n convexUrlEnvVar: \"REACT_APP_CONVEX_URL\",\n convexSiteEnvVar: \"REACT_APP_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"REACT_APP_\",\n };\n }\n\n const isNextJs = \"next\" in packages;\n if (isNextJs) {\n return {\n detectedFramework: \"Next.js\",\n convexUrlEnvVar: \"NEXT_PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"NEXT_PUBLIC_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"NEXT_PUBLIC_\",\n };\n }\n\n const isExpo = \"expo\" in packages;\n if (isExpo) {\n return {\n detectedFramework: \"Expo\",\n convexUrlEnvVar: \"EXPO_PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"EXPO_PUBLIC_CONVEX_SITE_URL\",\n publicPrefix: \"EXPO_PUBLIC_\",\n };\n }\n\n const isSvelteKit = \"@sveltejs/kit\" in packages;\n if (isSvelteKit) {\n return {\n detectedFramework: \"SvelteKit\",\n convexUrlEnvVar: \"PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"PUBLIC_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:5173\",\n publicPrefix: \"PUBLIC_\",\n };\n }\n\n // TanStackStart currently supports VITE_FOO for browser-side envvars.\n const isTanStackStart =\n \"@tanstack/start\" in packages || \"@tanstack/react-start\" in packages;\n\n if (isTanStackStart) {\n return {\n detectedFramework: \"TanStackStart\",\n convexUrlEnvVar: \"VITE_CONVEX_URL\",\n convexSiteEnvVar: \"VITE_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"VITE_\",\n };\n }\n\n // Vite is a dependency of a lot of things; vite appearing in dependencies is not a strong indicator.\n const isVite = \"vite\" in packages;\n\n if (isVite) {\n return {\n detectedFramework: \"Vite\",\n convexUrlEnvVar: \"VITE_CONVEX_URL\",\n convexSiteEnvVar: \"VITE_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:5173\",\n publicPrefix: \"VITE_\",\n };\n }\n\n // We detect Remix after Vite because when using Remix as a plugin of Vite\n // (Remix Vite), we want to use Vite-style environment variables.\n const isRemix = \"@remix-run/dev\" in packages;\n if (isRemix) {\n return {\n detectedFramework: \"Remix\",\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n };\n }\n\n return {\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n };\n}\n\nasync function loadEnvFileUrlConfig(\n ctx: Context,\n options: {\n convexUrl: string;\n siteUrl?: string | null | undefined;\n },\n): Promise<EnvFileUrlConfig> {\n const { detectedFramework, convexUrlEnvVar, convexSiteEnvVar } =\n await suggestedEnvVarNames(ctx);\n\n const { envFile, existing } = suggestedDevEnvFile(ctx, detectedFramework);\n\n if (!existing) {\n return {\n envFile,\n convexUrlEnvVar,\n siteUrlEnvVar: convexSiteEnvVar,\n existingFileContent: null,\n };\n }\n\n const existingFileContent = ctx.fs.readUtf8File(envFile);\n const config = dotenv.parse(existingFileContent);\n\n const resolvedConvexUrlEnvVar = resolveEnvVarName(\n convexUrlEnvVar,\n options.convexUrl,\n envFile,\n config,\n EXPECTED_CONVEX_URL_NAMES,\n );\n const resolvedSiteUrlEnvVar = resolveEnvVarName(\n convexSiteEnvVar,\n options.siteUrl ?? \"\",\n envFile,\n config,\n EXPECTED_SITE_URL_NAMES,\n );\n if (\n resolvedConvexUrlEnvVar.kind === \"invalid\" ||\n resolvedSiteUrlEnvVar.kind === \"invalid\"\n ) {\n return null;\n }\n return {\n envFile,\n convexUrlEnvVar: resolvedConvexUrlEnvVar.envVarName,\n siteUrlEnvVar: resolvedSiteUrlEnvVar.envVarName,\n existingFileContent,\n };\n}\n\nfunction resolveEnvVarName(\n envVarName: string,\n envVarValue: string,\n envFile: string,\n config: dotenv.DotenvParseOutput,\n expectedNames: Set<string>,\n):\n | {\n kind: \"invalid\";\n }\n | {\n kind: \"valid\";\n envVarName: string | null;\n } {\n const matching = Object.keys(config).filter((key) => expectedNames.has(key));\n if (matching.length > 1) {\n logWarning(\n chalkStderr.yellow(\n `Found multiple ${envVarName} environment variables in ${envFile} so cannot update automatically.`,\n ),\n );\n return { kind: \"invalid\" };\n }\n if (matching.length === 1) {\n const [existingEnvVarName, oldValue] = [matching[0], config[matching[0]]];\n if (oldValue === envVarValue) {\n // Set envVarName to null to indicate that it shouldn't be updated.\n return { kind: \"valid\", envVarName: null };\n }\n if (\n oldValue !== \"\" &&\n Object.values(config).filter((v) => v === oldValue).length !== 1\n ) {\n logWarning(\n chalkStderr.yellow(\n `Can't safely modify ${envFile} for ${envVarName}, please edit manually.`,\n ),\n );\n return { kind: \"invalid\" };\n }\n return { kind: \"valid\", envVarName: existingEnvVarName };\n }\n return { kind: \"valid\", envVarName };\n}\n\nfunction suggestedDevEnvFile(\n ctx: Context,\n framework?: Framework,\n): {\n existing: boolean;\n envFile: string;\n} {\n // If a .env.local file exists, that's unequivocally the right file\n if (ctx.fs.exists(\".env.local\")) {\n return {\n existing: true,\n envFile: \".env.local\",\n };\n }\n\n // Remix is on team \"don't commit the .env file,\" so .env is for dev.\n if (framework === \"Remix\") {\n return {\n existing: ctx.fs.exists(\".env\"),\n envFile: \".env\",\n };\n }\n\n // The most dev-looking env file that exists, or .env.local\n return {\n existing: ctx.fs.exists(\".env.local\"),\n envFile: \".env.local\",\n };\n}\n\nexport const EXPECTED_CONVEX_URL_NAMES = new Set([\n \"CONVEX_URL\" as const,\n \"PUBLIC_CONVEX_URL\" as const,\n \"NEXT_PUBLIC_CONVEX_URL\" as const,\n \"VITE_CONVEX_URL\" as const,\n \"REACT_APP_CONVEX_URL\" as const,\n \"EXPO_PUBLIC_CONVEX_URL\" as const,\n]);\ntype ConvexUrlEnvVar =\n typeof EXPECTED_CONVEX_URL_NAMES extends Set<infer T> ? T : never;\n\nexport const EXPECTED_SITE_URL_NAMES = new Set([\n \"CONVEX_SITE_URL\" as const,\n \"PUBLIC_CONVEX_SITE_URL\" as const,\n \"NEXT_PUBLIC_CONVEX_SITE_URL\" as const,\n \"VITE_CONVEX_SITE_URL\" as const,\n \"REACT_APP_CONVEX_SITE_URL\" as const,\n \"EXPO_PUBLIC_CONVEX_SITE_URL\" as const,\n]);\ntype ConvexSiteUrlEnvVar =\n typeof EXPECTED_SITE_URL_NAMES extends Set<infer T> ? T : never;\n\n// Crash or warn on\n// CONVEX_DEPLOY_KEY=project:me:new-project|eyABCD0= npx convex\n// which parses as\n// CONVEX_DEPLOY_KEY=project:me:new-project | eyABCD0='' npx convex\n// when what was intended was\n// CONVEX_DEPLOY_KEY=project:me:new-project|eyABCD0= npx convex\n// This only fails so catastrophically when the key ends with `=`.\nexport async function detectSuspiciousEnvironmentVariables(\n ctx: Context,\n ignoreSuspiciousEnvVars = false,\n) {\n for (const [key, value] of Object.entries(process.env)) {\n if (value === \"\" && key.startsWith(\"ey\")) {\n try {\n // add a \"=\" to the end and try to base64 decode (expected format of Convex keys)\n const decoded = JSON.parse(\n Buffer.from(key + \"=\", \"base64\").toString(\"utf8\"),\n );\n // Only parseable v2 tokens to be sure this is a Convex token before complaining.\n if (!(\"v2\" in decoded)) {\n continue;\n }\n } catch {\n continue;\n }\n\n if (ignoreSuspiciousEnvVars) {\n logWarning(\n `ignoring suspicious environment variable ${key}, did you mean to use quotes like CONVEX_DEPLOY_KEY='...'?`,\n );\n } else {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Quotes are required around environment variable values by your shell: CONVEX_DEPLOY_KEY='project:name:project|${key.slice(0, 4)}...${key.slice(key.length - 4)}=' npx convex dev`,\n });\n }\n }\n }\n}\n\nexport function getBuildEnvironment(): string | false {\n return process.env.VERCEL\n ? \"Vercel\"\n : process.env.NETLIFY\n ? \"Netlify\"\n : process.env.CF_PAGES || process.env.WORKERS_CI\n ? \"Cloudflare\"\n : false;\n}\n\nexport function getDefaultDeployMessage(): string | null {\n const platforms: Array<{\n name: string;\n detect: () => boolean;\n commitShaVar: string;\n }> = [\n {\n name: \"GitHub Actions\",\n detect: () => !!process.env.GITHUB_ACTIONS,\n commitShaVar: \"GITHUB_SHA\",\n },\n {\n name: \"Vercel\",\n detect: () => !!process.env.VERCEL,\n commitShaVar: \"VERCEL_GIT_COMMIT_SHA\",\n },\n {\n name: \"Netlify\",\n detect: () => !!process.env.NETLIFY,\n commitShaVar: \"COMMIT_REF\",\n },\n {\n name: \"Cloudflare Pages\",\n detect: () => !!process.env.CF_PAGES,\n commitShaVar: \"CF_PAGES_COMMIT_SHA\",\n },\n {\n name: \"Cloudflare Workers\",\n detect: () => !!process.env.WORKERS_CI,\n commitShaVar: \"WORKERS_CI_COMMIT_SHA\",\n },\n {\n name: \"Render\",\n detect: () => !!process.env.RENDER,\n commitShaVar: \"RENDER_GIT_COMMIT\",\n },\n {\n name: \"Railway\",\n detect: () => !!process.env.RAILWAY_ENVIRONMENT,\n commitShaVar: \"RAILWAY_GIT_COMMIT_SHA\",\n },\n {\n name: \"GitLab CI\",\n detect: () => !!process.env.GITLAB_CI,\n commitShaVar: \"CI_COMMIT_SHA\",\n },\n {\n name: \"CircleCI\",\n detect: () => !!process.env.CIRCLECI,\n commitShaVar: \"CIRCLE_SHA1\",\n },\n {\n name: \"Google Cloud Build\",\n detect: () =>\n !!process.env.BUILD_ID &&\n !!process.env.PROJECT_ID &&\n !!process.env.PROJECT_NUMBER &&\n !!process.env.LOCATION,\n commitShaVar: \"SHORT_SHA\",\n },\n {\n name: \"Heroku\",\n detect: () => !!process.env.HEROKU_APP_NAME || !!process.env.DYNO,\n commitShaVar: \"HEROKU_BUILD_COMMIT\",\n },\n ];\n\n const platform = platforms.find((p) => p.detect());\n if (!platform) {\n return null;\n }\n\n const commitSha = process.env[platform.commitShaVar];\n if (!commitSha) {\n return `Deployed from ${platform.name}`;\n }\n\n const shortSha = commitSha.slice(0, 7);\n return `Deployed from ${platform.name} \u2022 ${shortSha}`;\n}\n\nexport function gitBranchFromEnvironment(): string | null {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/projects/environment-variables/system-environment-variables\n return process.env.VERCEL_GIT_COMMIT_REF ?? null;\n }\n if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/\n return process.env.HEAD ?? null;\n }\n if (process.env.CF_PAGES || process.env.WORKERS_CI) {\n // https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables\n // https://developers.cloudflare.com/workers/ci-cd/builds/configuration/#environment-variables\n return process.env.CF_PAGES_BRANCH ?? process.env.WORKERS_CI_BRANCH ?? null;\n }\n\n if (process.env.CI) {\n // https://docs.github.com/en/actions/learn-github-actions/variables\n // https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n return (\n process.env.GITHUB_HEAD_REF ?? process.env.CI_COMMIT_REF_NAME ?? null\n );\n }\n\n return null;\n}\n\nexport function isNonProdBuildEnvironment(): boolean {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/projects/environment-variables/system-environment-variables\n return process.env.VERCEL_ENV !== \"production\";\n }\n if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/\n return process.env.CONTEXT !== \"production\";\n }\n if (process.env.CF_PAGES || process.env.WORKERS_CI) {\n // https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables\n // https://developers.cloudflare.com/workers/ci-cd/builds/configuration/#environment-variables\n // Branch !== \"main\" is the closest heuristic; Cloudflare Pages\n // does not expose a dedicated production/preview flag.\n const branch = process.env.CF_PAGES_BRANCH ?? process.env.WORKERS_CI_BRANCH;\n return branch !== \"main\";\n }\n return false;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Help the developer store the CONVEX_URL environment variable.\n */\nimport { chalkStderr } from \"chalk\";\nimport * as dotenv from \"dotenv\";\n\nimport { Context } from \"../../bundler/context.js\";\nimport { logWarning } from \"../../bundler/log.js\";\nimport { loadPackageJson } from \"./utils/utils.js\";\n\nconst _FRAMEWORKS = [\n \"create-react-app\",\n \"Next.js\",\n \"Vite\",\n \"Remix\",\n \"SvelteKit\",\n \"Expo\",\n \"TanStackStart\",\n] as const;\ntype Framework = (typeof _FRAMEWORKS)[number];\n\n/**\n * A configuration for writing the actual (framework specific) `CONVEX_URL`\n * and `CONVEX_SITE_URL` environment variables to a \".env\" type file.\n *\n * May be `null` if there was an error determining any of the field values.\n */\ntype EnvFileUrlConfig = {\n /** The name of the file - typically `.env.local` */\n envFile: string;\n /**\n * The framework specific `CONVEX_URL`\n *\n * If `null`, ignore and don't update that environment variable.\n */\n convexUrlEnvVar: string | null;\n /**\n * The framework specific `CONVEX_SITE_URL`\n *\n * If `null`, ignore and don't update that environment variable.\n */\n siteUrlEnvVar: string | null;\n /** Existing content loaded from the `envFile`, if it exists */\n existingFileContent: string | null;\n} | null;\n\nexport async function writeUrlsToEnvFile(\n ctx: Context,\n options: {\n convexUrl: string;\n siteUrl?: string | null | undefined;\n },\n): Promise<EnvFileUrlConfig> {\n const envFileConfig = await loadEnvFileUrlConfig(ctx, options);\n\n if (envFileConfig === null) {\n return null;\n }\n\n const { envFile, convexUrlEnvVar, siteUrlEnvVar, existingFileContent } =\n envFileConfig;\n let updatedFileContent: string | null = null;\n if (convexUrlEnvVar) {\n updatedFileContent = changedEnvVarFile({\n existingFileContent,\n envVarName: convexUrlEnvVar,\n envVarValue: options.convexUrl,\n commentAfterValue: null,\n commentOnPreviousLine: null,\n })!;\n }\n if (siteUrlEnvVar && options.siteUrl) {\n updatedFileContent = changedEnvVarFile({\n existingFileContent: updatedFileContent ?? existingFileContent,\n envVarName: siteUrlEnvVar,\n envVarValue: options.siteUrl,\n commentAfterValue: null,\n commentOnPreviousLine: null,\n })!;\n }\n if (updatedFileContent) {\n ctx.fs.writeUtf8File(envFile, updatedFileContent);\n }\n\n return envFileConfig;\n}\n\nexport function changedEnvVarFile({\n existingFileContent,\n envVarName,\n envVarValue,\n commentAfterValue,\n commentOnPreviousLine,\n}: {\n existingFileContent: string | null;\n envVarName: string;\n envVarValue: string;\n commentAfterValue: string | null;\n commentOnPreviousLine: string | null;\n}): string | null {\n const varAssignment = `${envVarName}=${envVarValue}${\n commentAfterValue === null ? \"\" : ` # ${commentAfterValue}`\n }`;\n const commentOnPreviousLineWithLineBreak =\n commentOnPreviousLine === null ? \"\" : `${commentOnPreviousLine}\\n`;\n if (existingFileContent === null) {\n return `${commentOnPreviousLineWithLineBreak}${varAssignment}\\n`;\n }\n const config = dotenv.parse(existingFileContent);\n const existing = config[envVarName];\n if (existing === envVarValue) {\n return null;\n }\n if (existing !== undefined) {\n return existingFileContent.replace(\n getEnvVarRegex(envVarName),\n `${varAssignment}`,\n );\n } else {\n const doubleLineBreak = existingFileContent.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n return (\n existingFileContent +\n doubleLineBreak +\n commentOnPreviousLineWithLineBreak +\n varAssignment +\n \"\\n\"\n );\n }\n}\n\nexport function getEnvVarRegex(envVarName: string) {\n return new RegExp(`^${envVarName}.*$`, \"m\");\n}\n\nexport async function suggestedEnvVarNames(ctx: Context): Promise<{\n detectedFramework?: Framework;\n convexUrlEnvVar: ConvexUrlEnvVar;\n convexSiteEnvVar: ConvexSiteUrlEnvVar;\n frontendDevUrl?: string;\n publicPrefix?: string;\n}> {\n // no package.json, that's fine, just guess\n if (!ctx.fs.exists(\"package.json\")) {\n return {\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n };\n }\n\n const packages = await loadPackageJson(ctx);\n\n // Is it create-react-app?\n const isCreateReactApp = \"react-scripts\" in packages;\n if (isCreateReactApp) {\n return {\n detectedFramework: \"create-react-app\",\n convexUrlEnvVar: \"REACT_APP_CONVEX_URL\",\n convexSiteEnvVar: \"REACT_APP_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"REACT_APP_\",\n };\n }\n\n const isNextJs = \"next\" in packages;\n if (isNextJs) {\n return {\n detectedFramework: \"Next.js\",\n convexUrlEnvVar: \"NEXT_PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"NEXT_PUBLIC_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"NEXT_PUBLIC_\",\n };\n }\n\n const isExpo = \"expo\" in packages;\n if (isExpo) {\n return {\n detectedFramework: \"Expo\",\n convexUrlEnvVar: \"EXPO_PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"EXPO_PUBLIC_CONVEX_SITE_URL\",\n publicPrefix: \"EXPO_PUBLIC_\",\n };\n }\n\n const isSvelteKit = \"@sveltejs/kit\" in packages;\n if (isSvelteKit) {\n return {\n detectedFramework: \"SvelteKit\",\n convexUrlEnvVar: \"PUBLIC_CONVEX_URL\",\n convexSiteEnvVar: \"PUBLIC_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:5173\",\n publicPrefix: \"PUBLIC_\",\n };\n }\n\n // TanStackStart currently supports VITE_FOO for browser-side envvars.\n const isTanStackStart =\n \"@tanstack/start\" in packages || \"@tanstack/react-start\" in packages;\n\n if (isTanStackStart) {\n return {\n detectedFramework: \"TanStackStart\",\n convexUrlEnvVar: \"VITE_CONVEX_URL\",\n convexSiteEnvVar: \"VITE_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n publicPrefix: \"VITE_\",\n };\n }\n\n // Vite is a dependency of a lot of things; vite appearing in dependencies is not a strong indicator.\n const isVite = \"vite\" in packages;\n\n if (isVite) {\n return {\n detectedFramework: \"Vite\",\n convexUrlEnvVar: \"VITE_CONVEX_URL\",\n convexSiteEnvVar: \"VITE_CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:5173\",\n publicPrefix: \"VITE_\",\n };\n }\n\n // We detect Remix after Vite because when using Remix as a plugin of Vite\n // (Remix Vite), we want to use Vite-style environment variables.\n const isRemix = \"@remix-run/dev\" in packages;\n if (isRemix) {\n return {\n detectedFramework: \"Remix\",\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n frontendDevUrl: \"http://localhost:3000\",\n };\n }\n\n return {\n convexUrlEnvVar: \"CONVEX_URL\",\n convexSiteEnvVar: \"CONVEX_SITE_URL\",\n };\n}\n\nasync function loadEnvFileUrlConfig(\n ctx: Context,\n options: {\n convexUrl: string;\n siteUrl?: string | null | undefined;\n },\n): Promise<EnvFileUrlConfig> {\n const { detectedFramework, convexUrlEnvVar, convexSiteEnvVar } =\n await suggestedEnvVarNames(ctx);\n\n const { envFile, existing } = suggestedDevEnvFile(ctx, detectedFramework);\n\n if (!existing) {\n return {\n envFile,\n convexUrlEnvVar,\n siteUrlEnvVar: convexSiteEnvVar,\n existingFileContent: null,\n };\n }\n\n const existingFileContent = ctx.fs.readUtf8File(envFile);\n const config = dotenv.parse(existingFileContent);\n\n const resolvedConvexUrlEnvVar = resolveEnvVarName(\n convexUrlEnvVar,\n options.convexUrl,\n envFile,\n config,\n EXPECTED_CONVEX_URL_NAMES,\n );\n const resolvedSiteUrlEnvVar = resolveEnvVarName(\n convexSiteEnvVar,\n options.siteUrl ?? \"\",\n envFile,\n config,\n EXPECTED_SITE_URL_NAMES,\n );\n if (\n resolvedConvexUrlEnvVar.kind === \"invalid\" ||\n resolvedSiteUrlEnvVar.kind === \"invalid\"\n ) {\n return null;\n }\n return {\n envFile,\n convexUrlEnvVar: resolvedConvexUrlEnvVar.envVarName,\n siteUrlEnvVar: resolvedSiteUrlEnvVar.envVarName,\n existingFileContent,\n };\n}\n\nfunction resolveEnvVarName(\n envVarName: string,\n envVarValue: string,\n envFile: string,\n config: dotenv.DotenvParseOutput,\n expectedNames: Set<string>,\n):\n | {\n kind: \"invalid\";\n }\n | {\n kind: \"valid\";\n envVarName: string | null;\n } {\n const matching = Object.keys(config).filter((key) => expectedNames.has(key));\n if (matching.length > 1) {\n logWarning(\n chalkStderr.yellow(\n `Found multiple ${envVarName} environment variables in ${envFile} so cannot update automatically.`,\n ),\n );\n return { kind: \"invalid\" };\n }\n if (matching.length === 1) {\n const [existingEnvVarName, oldValue] = [matching[0], config[matching[0]]];\n if (oldValue === envVarValue) {\n // Set envVarName to null to indicate that it shouldn't be updated.\n return { kind: \"valid\", envVarName: null };\n }\n if (\n oldValue !== \"\" &&\n Object.values(config).filter((v) => v === oldValue).length !== 1\n ) {\n logWarning(\n chalkStderr.yellow(\n `Can't safely modify ${envFile} for ${envVarName}, please edit manually.`,\n ),\n );\n return { kind: \"invalid\" };\n }\n return { kind: \"valid\", envVarName: existingEnvVarName };\n }\n return { kind: \"valid\", envVarName };\n}\n\nfunction suggestedDevEnvFile(\n ctx: Context,\n framework?: Framework,\n): {\n existing: boolean;\n envFile: string;\n} {\n // If a .env.local file exists, that's unequivocally the right file\n if (ctx.fs.exists(\".env.local\")) {\n return {\n existing: true,\n envFile: \".env.local\",\n };\n }\n\n // Remix is on team \"don't commit the .env file,\" so .env is for dev.\n if (framework === \"Remix\") {\n return {\n existing: ctx.fs.exists(\".env\"),\n envFile: \".env\",\n };\n }\n\n // The most dev-looking env file that exists, or .env.local\n return {\n existing: ctx.fs.exists(\".env.local\"),\n envFile: \".env.local\",\n };\n}\n\nexport const EXPECTED_CONVEX_URL_NAMES = new Set([\n \"CONVEX_URL\" as const,\n \"PUBLIC_CONVEX_URL\" as const,\n \"NEXT_PUBLIC_CONVEX_URL\" as const,\n \"VITE_CONVEX_URL\" as const,\n \"REACT_APP_CONVEX_URL\" as const,\n \"EXPO_PUBLIC_CONVEX_URL\" as const,\n]);\ntype ConvexUrlEnvVar =\n typeof EXPECTED_CONVEX_URL_NAMES extends Set<infer T> ? T : never;\n\nexport const EXPECTED_SITE_URL_NAMES = new Set([\n \"CONVEX_SITE_URL\" as const,\n \"PUBLIC_CONVEX_SITE_URL\" as const,\n \"NEXT_PUBLIC_CONVEX_SITE_URL\" as const,\n \"VITE_CONVEX_SITE_URL\" as const,\n \"REACT_APP_CONVEX_SITE_URL\" as const,\n \"EXPO_PUBLIC_CONVEX_SITE_URL\" as const,\n]);\ntype ConvexSiteUrlEnvVar =\n typeof EXPECTED_SITE_URL_NAMES extends Set<infer T> ? T : never;\n\n// Crash or warn on\n// CONVEX_DEPLOY_KEY=project:me:new-project|eyABCD0= npx convex\n// which parses as\n// CONVEX_DEPLOY_KEY=project:me:new-project | eyABCD0='' npx convex\n// when what was intended was\n// CONVEX_DEPLOY_KEY=project:me:new-project|eyABCD0= npx convex\n// This only fails so catastrophically when the key ends with `=`.\nexport async function detectSuspiciousEnvironmentVariables(\n ctx: Context,\n ignoreSuspiciousEnvVars = false,\n) {\n for (const [key, value] of Object.entries(process.env)) {\n if (value === \"\" && key.startsWith(\"ey\")) {\n try {\n // add a \"=\" to the end and try to base64 decode (expected format of Convex keys)\n const decoded = JSON.parse(\n Buffer.from(key + \"=\", \"base64\").toString(\"utf8\"),\n );\n // Only parseable v2 tokens to be sure this is a Convex token before complaining.\n if (!(\"v2\" in decoded)) {\n continue;\n }\n } catch {\n continue;\n }\n\n if (ignoreSuspiciousEnvVars) {\n logWarning(\n `ignoring suspicious environment variable ${key}, did you mean to use quotes like CONVEX_DEPLOY_KEY='...'?`,\n );\n } else {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Quotes are required around environment variable values by your shell: CONVEX_DEPLOY_KEY='project:name:project|${key.slice(0, 4)}...${key.slice(key.length - 4)}=' npx convex dev`,\n });\n }\n }\n }\n}\n\nexport function getBuildEnvironment(): string | false {\n return process.env.VERCEL\n ? \"Vercel\"\n : process.env.NETLIFY\n ? \"Netlify\"\n : process.env.CF_PAGES || process.env.WORKERS_CI\n ? \"Cloudflare\"\n : false;\n}\n\nexport function getDefaultDeployMessage(): string | null {\n const platforms: Array<{\n name: string;\n detect: () => boolean;\n commitShaVar: string;\n }> = [\n {\n name: \"GitHub Actions\",\n detect: () => !!process.env.GITHUB_ACTIONS,\n commitShaVar: \"GITHUB_SHA\",\n },\n {\n name: \"Vercel\",\n detect: () => !!process.env.VERCEL,\n commitShaVar: \"VERCEL_GIT_COMMIT_SHA\",\n },\n {\n name: \"Netlify\",\n detect: () => !!process.env.NETLIFY,\n commitShaVar: \"COMMIT_REF\",\n },\n {\n name: \"Cloudflare Pages\",\n detect: () => !!process.env.CF_PAGES,\n commitShaVar: \"CF_PAGES_COMMIT_SHA\",\n },\n {\n name: \"Cloudflare Workers\",\n detect: () => !!process.env.WORKERS_CI,\n commitShaVar: \"WORKERS_CI_COMMIT_SHA\",\n },\n {\n name: \"Render\",\n detect: () => !!process.env.RENDER,\n commitShaVar: \"RENDER_GIT_COMMIT\",\n },\n {\n name: \"Railway\",\n detect: () => !!process.env.RAILWAY_ENVIRONMENT,\n commitShaVar: \"RAILWAY_GIT_COMMIT_SHA\",\n },\n {\n name: \"GitLab CI\",\n detect: () => !!process.env.GITLAB_CI,\n commitShaVar: \"CI_COMMIT_SHA\",\n },\n {\n name: \"CircleCI\",\n detect: () => !!process.env.CIRCLECI,\n commitShaVar: \"CIRCLE_SHA1\",\n },\n {\n name: \"Google Cloud Build\",\n detect: () =>\n !!process.env.BUILD_ID &&\n !!process.env.PROJECT_ID &&\n !!process.env.PROJECT_NUMBER &&\n !!process.env.LOCATION,\n commitShaVar: \"SHORT_SHA\",\n },\n {\n name: \"Heroku\",\n detect: () => !!process.env.HEROKU_APP_NAME || !!process.env.DYNO,\n commitShaVar: \"HEROKU_BUILD_COMMIT\",\n },\n ];\n\n const platform = platforms.find((p) => p.detect());\n if (!platform) {\n return null;\n }\n\n const commitSha = process.env[platform.commitShaVar];\n if (!commitSha) {\n return `Deployed from ${platform.name}`;\n }\n\n const shortSha = commitSha.slice(0, 7);\n return `Deployed from ${platform.name} \u2022 ${shortSha}`;\n}\n\nexport function gitBranchFromEnvironment(): string | null {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/projects/environment-variables/system-environment-variables\n return process.env.VERCEL_GIT_COMMIT_REF ?? null;\n }\n if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/\n return process.env.HEAD ?? null;\n }\n if (process.env.CF_PAGES || process.env.WORKERS_CI) {\n // https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables\n // https://developers.cloudflare.com/workers/ci-cd/builds/configuration/#environment-variables\n return process.env.CF_PAGES_BRANCH ?? process.env.WORKERS_CI_BRANCH ?? null;\n }\n\n if (process.env.CI) {\n // https://docs.github.com/en/actions/learn-github-actions/variables\n // https://docs.gitlab.com/ee/ci/variables/predefined_variables.html\n return (\n process.env.GITHUB_HEAD_REF ?? process.env.CI_COMMIT_REF_NAME ?? null\n );\n }\n\n return null;\n}\n\nexport function isNonProdBuildEnvironment(): boolean {\n if (process.env.VERCEL) {\n // https://vercel.com/docs/projects/environment-variables/system-environment-variables\n return process.env.VERCEL_ENV !== \"production\";\n }\n if (process.env.NETLIFY) {\n // https://docs.netlify.com/configure-builds/environment-variables/\n return process.env.CONTEXT !== \"production\";\n }\n if (process.env.CF_PAGES || process.env.WORKERS_CI) {\n // https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables\n // https://developers.cloudflare.com/workers/ci-cd/builds/configuration/#environment-variables\n // Branch !== \"main\" is the closest heuristic; Cloudflare Pages\n // does not expose a dedicated production/preview flag.\n const branch = process.env.CF_PAGES_BRANCH ?? process.env.WORKERS_CI_BRANCH;\n return branch !== \"main\";\n }\n return false;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,7 @@ export function recursivelyDelete(ctx, deletePath, opts) {
5
5
  const dryRun = !!opts?.dryRun;
6
6
  let st;
7
7
  try {
8
- st = opts?.followSymlinks === false ? ctx.fs.lstat(deletePath) : ctx.fs.stat(deletePath);
8
+ st = ctx.fs.stat(deletePath);
9
9
  } catch (err) {
10
10
  if (err.code === "ENOENT" && opts?.force) {
11
11
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/lib/fsUtils.ts"],
4
- "sourcesContent": ["import { Context } from \"../../bundler/context.js\";\nimport { logOutput } from \"../../bundler/log.js\";\nimport path from \"path\";\nimport { NodeFs } from \"../../bundler/fs.js\";\n\nexport function recursivelyDelete(\n ctx: Context,\n deletePath: string,\n opts?: { force?: boolean; dryRun?: boolean; followSymlinks?: boolean },\n) {\n const dryRun = !!opts?.dryRun;\n let st;\n try {\n st =\n opts?.followSymlinks === false\n ? ctx.fs.lstat(deletePath)\n : ctx.fs.stat(deletePath);\n } catch (err: any) {\n if (err.code === \"ENOENT\" && opts?.force) {\n return;\n }\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n if (st.isDirectory()) {\n for (const entry of ctx.fs.listDir(deletePath)) {\n recursivelyDelete(ctx, path.join(deletePath, entry.name), opts);\n }\n if (dryRun) {\n logOutput(`Command would delete directory: ${deletePath}`);\n return;\n }\n try {\n ctx.fs.rmdir(deletePath);\n } catch (err: any) {\n if (err.code !== \"ENOENT\") {\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n }\n } else {\n if (dryRun) {\n logOutput(`Command would delete file: ${deletePath}`);\n return;\n }\n try {\n ctx.fs.unlink(deletePath);\n } catch (err: any) {\n if (err.code !== \"ENOENT\") {\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n }\n }\n}\n\nexport async function recursivelyCopy(\n ctx: Context,\n nodeFs: NodeFs,\n src: string,\n dest: string,\n) {\n const st = nodeFs.stat(src);\n if (st.isDirectory()) {\n nodeFs.mkdir(dest, { recursive: true });\n for (const entry of nodeFs.listDir(src)) {\n await recursivelyCopy(\n ctx,\n nodeFs,\n path.join(src, entry.name),\n path.join(dest, entry.name),\n );\n }\n } else {\n // Don't use writeUtf8File to allow copying arbitrary files\n await nodeFs.writeFileStream(dest, nodeFs.createReadStream(src, {}));\n }\n}\n"],
5
- "mappings": ";AACA,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AAGV,gBAAS,kBACd,KACA,YACA,MACA;AACA,QAAM,SAAS,CAAC,CAAC,MAAM;AACvB,MAAI;AACJ,MAAI;AACF,SACE,MAAM,mBAAmB,QACrB,IAAI,GAAG,MAAM,UAAU,IACvB,IAAI,GAAG,KAAK,UAAU;AAAA,EAC9B,SAAS,KAAU;AACjB,QAAI,IAAI,SAAS,YAAY,MAAM,OAAO;AACxC;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACA,MAAI,GAAG,YAAY,GAAG;AACpB,eAAW,SAAS,IAAI,GAAG,QAAQ,UAAU,GAAG;AAC9C,wBAAkB,KAAK,KAAK,KAAK,YAAY,MAAM,IAAI,GAAG,IAAI;AAAA,IAChE;AACA,QAAI,QAAQ;AACV,gBAAU,mCAAmC,UAAU,EAAE;AACzD;AAAA,IACF;AACA,QAAI;AACF,UAAI,GAAG,MAAM,UAAU;AAAA,IACzB,SAAS,KAAU;AACjB,UAAI,IAAI,SAAS,UAAU;AAEzB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,OAAO;AACL,QAAI,QAAQ;AACV,gBAAU,8BAA8B,UAAU,EAAE;AACpD;AAAA,IACF;AACA,QAAI;AACF,UAAI,GAAG,OAAO,UAAU;AAAA,IAC1B,SAAS,KAAU;AACjB,UAAI,IAAI,SAAS,UAAU;AAEzB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,sBAAsB,gBACpB,KACA,QACA,KACA,MACA;AACA,QAAM,KAAK,OAAO,KAAK,GAAG;AAC1B,MAAI,GAAG,YAAY,GAAG;AACpB,WAAO,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,eAAW,SAAS,OAAO,QAAQ,GAAG,GAAG;AACvC,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,KAAK,KAAK,KAAK,MAAM,IAAI;AAAA,QACzB,KAAK,KAAK,MAAM,MAAM,IAAI;AAAA,MAC5B;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,OAAO,gBAAgB,MAAM,OAAO,iBAAiB,KAAK,CAAC,CAAC,CAAC;AAAA,EACrE;AACF;",
4
+ "sourcesContent": ["import { Context } from \"../../bundler/context.js\";\nimport { logOutput } from \"../../bundler/log.js\";\nimport path from \"path\";\nimport { NodeFs } from \"../../bundler/fs.js\";\n\nexport function recursivelyDelete(\n ctx: Context,\n deletePath: string,\n opts?: { force?: boolean; dryRun?: boolean },\n) {\n const dryRun = !!opts?.dryRun;\n let st;\n try {\n st = ctx.fs.stat(deletePath);\n } catch (err: any) {\n if (err.code === \"ENOENT\" && opts?.force) {\n return;\n }\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n if (st.isDirectory()) {\n for (const entry of ctx.fs.listDir(deletePath)) {\n recursivelyDelete(ctx, path.join(deletePath, entry.name), opts);\n }\n if (dryRun) {\n logOutput(`Command would delete directory: ${deletePath}`);\n return;\n }\n try {\n ctx.fs.rmdir(deletePath);\n } catch (err: any) {\n if (err.code !== \"ENOENT\") {\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n }\n } else {\n if (dryRun) {\n logOutput(`Command would delete file: ${deletePath}`);\n return;\n }\n try {\n ctx.fs.unlink(deletePath);\n } catch (err: any) {\n if (err.code !== \"ENOENT\") {\n // eslint-disable-next-line no-restricted-syntax\n throw err;\n }\n }\n }\n}\n\nexport async function recursivelyCopy(\n ctx: Context,\n nodeFs: NodeFs,\n src: string,\n dest: string,\n) {\n const st = nodeFs.stat(src);\n if (st.isDirectory()) {\n nodeFs.mkdir(dest, { recursive: true });\n for (const entry of nodeFs.listDir(src)) {\n await recursivelyCopy(\n ctx,\n nodeFs,\n path.join(src, entry.name),\n path.join(dest, entry.name),\n );\n }\n } else {\n // Don't use writeUtf8File to allow copying arbitrary files\n await nodeFs.writeFileStream(dest, nodeFs.createReadStream(src, {}));\n }\n}\n"],
5
+ "mappings": ";AACA,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AAGV,gBAAS,kBACd,KACA,YACA,MACA;AACA,QAAM,SAAS,CAAC,CAAC,MAAM;AACvB,MAAI;AACJ,MAAI;AACF,SAAK,IAAI,GAAG,KAAK,UAAU;AAAA,EAC7B,SAAS,KAAU;AACjB,QAAI,IAAI,SAAS,YAAY,MAAM,OAAO;AACxC;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AACA,MAAI,GAAG,YAAY,GAAG;AACpB,eAAW,SAAS,IAAI,GAAG,QAAQ,UAAU,GAAG;AAC9C,wBAAkB,KAAK,KAAK,KAAK,YAAY,MAAM,IAAI,GAAG,IAAI;AAAA,IAChE;AACA,QAAI,QAAQ;AACV,gBAAU,mCAAmC,UAAU,EAAE;AACzD;AAAA,IACF;AACA,QAAI;AACF,UAAI,GAAG,MAAM,UAAU;AAAA,IACzB,SAAS,KAAU;AACjB,UAAI,IAAI,SAAS,UAAU;AAEzB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,OAAO;AACL,QAAI,QAAQ;AACV,gBAAU,8BAA8B,UAAU,EAAE;AACpD;AAAA,IACF;AACA,QAAI;AACF,UAAI,GAAG,OAAO,UAAU;AAAA,IAC1B,SAAS,KAAU;AACjB,UAAI,IAAI,SAAS,UAAU;AAEzB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,sBAAsB,gBACpB,KACA,QACA,KACA,MACA;AACA,QAAM,KAAK,OAAO,KAAK,GAAG;AAC1B,MAAI,GAAG,YAAY,GAAG;AACpB,WAAO,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,eAAW,SAAS,OAAO,QAAQ,GAAG,GAAG;AACvC,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,KAAK,KAAK,KAAK,MAAM,IAAI;AAAA,QACzB,KAAK,KAAK,MAAM,MAAM,IAAI;AAAA,MAC5B;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,OAAO,gBAAgB,MAAM,OAAO,iBAAiB,KAAK,CAAC,CAAC,CAAC;AAAA,EACrE;AACF;",
6
6
  "names": []
7
7
  }
@@ -178,7 +178,7 @@ function escapeMdx(text) {
178
178
  function renderCopyableCommands(text) {
179
179
  return text.split("\n").map(
180
180
  (line) => /^- /.test(line) ? line.replace(
181
- /`(npx convex(?:@[^\s`]+)? [^`]*)`/g,
181
+ /`(npx convex [^`]*)`/g,
182
182
  (_, command) => `<CodeWithCopyButton text={${JSON.stringify(command)}} />`
183
183
  ) : line
184
184
  ).join("\n");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/cli/lib/generateDocs.ts"],
4
- "sourcesContent": ["import type { CommandUnknownOpts } from \"@commander-js/extra-typings\";\n\nexport type GeneratedDocs = Record<string, string>;\n\ntype CommandInfo = {\n command: CommandUnknownOpts;\n // Full path of command names from the root, e.g. [\"convex\", \"env\", \"set\"].\n path: string[];\n};\n\n/**\n * Generate Markdown reference docs for a Commander main command.\n *\n * Returns a map of file paths (relative, e.g. `env.mdx`) to Markdown\n * contents. One file is generated for each visible main command (a direct\n * child of the root); the root itself gets no page, so \"Command Reference\"\n * is a sidebar section with no landing page. Descendant subcommands are\n * rendered as sections inside their main command's file, with their heading\n * level reflecting their depth: a direct subcommand (`env default`) is an\n * `## h2`, a sub-subcommand (`env default set`) an `### h3`, and so on.\n *\n * Each page carries a `description` frontmatter equal to the command's\n * summary; the `/cli` page reads these back via Docusaurus metadata to render\n * the list of commands dynamically.\n */\nexport function generateDocs(root: CommandUnknownOpts): GeneratedDocs {\n const docs: GeneratedDocs = {};\n const all = collectCommands(root, [root.name()]);\n\n let mainCommandPosition = 0;\n for (const entry of all) {\n // Skip the root command (no landing page) and any descendant beyond a\n // main command (those are rendered inside their main command's file).\n if (entry.path.length !== 2) continue;\n const filePath = `${entry.path[1]}.mdx`;\n const sidebarPosition = ++mainCommandPosition;\n const summary =\n entry.command.summary() || entry.command.description() || \"\";\n // Collapse to a single line so it's a valid YAML frontmatter scalar, and\n // quote it safely (summaries contain `[BETA]`, `:`, etc.).\n const description = JSON.stringify(summary.replace(/\\s+/g, \" \").trim());\n\n const lines: string[] = [];\n const sidebarLabel = `npx ${entry.path.join(\" \")}`;\n lines.push(\"---\");\n lines.push(`sidebar_position: ${sidebarPosition}`);\n // Set an explicit `title` so the Docusaurus <title> (and browser tab) reads\n // the full command, e.g. `npx convex dev | \u2026`. Without it Docusaurus\n // derives the title from the doc id (the file name), which would be just\n // `dev`.\n lines.push(`title: \"${sidebarLabel}\"`);\n lines.push(`sidebar_label: \"${sidebarLabel}\"`);\n lines.push(`description: ${description}`);\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(\n \"{/* @generated from the command definitions, do not edit manually (run `just regenerate-cli-docs` to regenerate) */}\",\n );\n lines.push(\"\");\n lines.push(renderCommand(entry, all, { headingLevel: 1 }));\n\n const descendants = all.filter(\n (e) =>\n e.path.length > entry.path.length &&\n entry.path.every((p, i) => e.path[i] === p),\n );\n for (const d of descendants) {\n // Render the heading hierarchically by depth below the root: a direct\n // subcommand (`env default`, path length 3) is an h2, a sub-subcommand\n // (`env default set`, path length 4) is an h3, and so on. This nests\n // sub-subcommands under their parent group instead of flattening every\n // descendant to the same h2 level.\n lines.push(\n renderCommand(d, all, {\n headingLevel: d.path.length - 1,\n includeSubcommandsList: false,\n }),\n );\n }\n\n docs[filePath] = lines.join(\"\\n\");\n }\n\n return docs;\n}\n\nfunction collectCommands(\n command: CommandUnknownOpts,\n path: string[],\n): CommandInfo[] {\n const result: CommandInfo[] = [{ command, path }];\n for (const sub of command.commands) {\n // Skip hidden commands and Commander's built-in help command.\n if ((sub as any)._hidden) continue;\n if (sub.name() === \"help\") continue;\n result.push(...collectCommands(sub, [...path, sub.name()]));\n }\n return result;\n}\n\nfunction displayName(path: string[]): string {\n return `npx ${path.join(\" \")}`;\n}\n\n// Build the usage suffix (everything after the command name) for the Syntax\n// code block. We derive the argument portion from the command's registered\n// arguments rather than trusting a manual `.usage()` override, so the Syntax\n// line always agrees with the Arguments section below. For example `env set`\n// overrides `.usage()` to show `<name> <value>` (pretending the args are\n// required) for nicer `--help` output, even though it registers them as\n// optional `[name] [value]`; reproducing the override verbatim would make the\n// generated page contradict itself. Commands with no registered arguments keep\n// their `.usage()` string verbatim (e.g. the root's `<command> [options]`).\nfunction commandUsage(command: CommandUnknownOpts): string {\n const args = (command as any).registeredArguments ?? [];\n if (args.length === 0) {\n return command.usage();\n }\n // Mirror Commander's default usage order: options, then a subcommand\n // placeholder, then the positional arguments. `[options]` is always present\n // because the built-in `--help` option is always registered.\n const parts = [\"[options]\"];\n if (command.commands.length > 0) {\n parts.push(\"[command]\");\n }\n for (const arg of args) {\n const name = `${arg._name}${arg.variadic ? \"...\" : \"\"}`;\n parts.push(arg.required ? `<${name}>` : `[${name}]`);\n }\n return parts.join(\" \");\n}\n\n// Anchor id for a descendant command rendered as an h2 section inside its\n// main command's file. Built from the path segments below the main command,\n// joined with `-`, so sub-subcommands stay unique. For example, both\n// `deployment create` and `deployment token create` would collide on `#create`\n// if we used only the leaf name; instead they become `#create` and\n// `#token-create`.\nfunction anchorSlug(path: string[]): string {\n return path.slice(2).join(\"-\");\n}\n\n// Link target for a subcommand listed inside a command's \"Subcommands\"\n// section. From the root index, main commands live in sibling files. From a\n// main command page, descendants are rendered as h2 sections in the same\n// file, anchored by their path below the main command.\nfunction subcommandLink(parentPath: string[], subPath: string[]): string {\n if (parentPath.length === 1) {\n return `./${subPath[1]}`;\n }\n return `#${anchorSlug(subPath)}`;\n}\n\nfunction renderCommand(\n entry: CommandInfo,\n all: CommandInfo[],\n options: { headingLevel: number; includeSubcommandsList?: boolean },\n): string {\n const { headingLevel, includeSubcommandsList = true } = options;\n const h1 = \"#\".repeat(headingLevel);\n const h2 = \"#\".repeat(headingLevel + 1);\n\n const { command, path } = entry;\n const display = displayName(path);\n const description = renderCopyableCommands(\n escapeMdx(\n formatExampleLines(\n replaceBullets(command.description() || command.summary() || \"\"),\n ),\n ),\n );\n\n const lines: string[] = [];\n // For nested subcommand sections, pin the heading id to the path below the\n // main command so anchors like `#set` and `#token-create` stay unique even\n // when leaf names collide across different parents.\n const headingSuffix = headingLevel > 1 ? ` \\\\{#${anchorSlug(path)}}` : \"\";\n lines.push(`${h1} \\`${display}\\`${headingSuffix}`);\n lines.push(\"\");\n if (description) {\n lines.push(description);\n lines.push(\"\");\n }\n\n const usage = commandUsage(command);\n lines.push(`${h2} Syntax`);\n lines.push(\"\");\n lines.push(\"```sh\");\n lines.push(`${display} ${usage}`.trim());\n lines.push(\"```\");\n lines.push(\"\");\n\n const aliases = command.aliases();\n if (aliases.length > 0) {\n lines.push(`${h2} Aliases`);\n lines.push(\"\");\n for (const alias of aliases) {\n lines.push(`- \\`${alias}\\``);\n }\n lines.push(\"\");\n }\n\n const args = (command as any).registeredArguments ?? [];\n if (args.length > 0) {\n lines.push(`${h2} Arguments`);\n lines.push(\"\");\n lines.push(\"<dl>\");\n for (const arg of args) {\n const name = arg.required ? `<${arg._name}>` : `[${arg._name}]`;\n const desc = escapeMdx(replaceBullets(arg.description || \"\"));\n lines.push(`<dt>\\`${name}\\`</dt>`);\n lines.push(`<dd>`);\n lines.push(\"\");\n lines.push(desc);\n lines.push(\"\");\n lines.push(`</dd>`);\n }\n lines.push(\"</dl>\");\n lines.push(\"\");\n }\n\n const opts = command.options.filter((o: any) => !o.hidden);\n if (opts.length > 0) {\n lines.push(`${h2} Options`);\n lines.push(\"\");\n lines.push(\"<dl>\");\n for (const opt of opts) {\n const flags = (opt as any).flags as string;\n const desc = escapeMdx(replaceBullets(opt.description || \"\"));\n lines.push(`<dt>\\`${flags}\\`</dt>`);\n lines.push(`<dd>`);\n lines.push(\"\");\n lines.push(desc);\n lines.push(\"\");\n lines.push(`</dd>`);\n }\n lines.push(\"</dl>\");\n lines.push(\"\");\n }\n\n if (includeSubcommandsList) {\n const subEntries = all.filter(\n (e) =>\n e.path.length === path.length + 1 &&\n path.every((p, i) => e.path[i] === p),\n );\n if (subEntries.length > 0) {\n lines.push(`${h2} Subcommands`);\n lines.push(\"\");\n for (const sub of subEntries) {\n const subDisplay = displayName(sub.path);\n const target = subcommandLink(path, sub.path);\n const subDesc = indentContinuation(\n escapeMdx(sub.command.summary() || sub.command.description() || \"\"),\n );\n lines.push(`- [\\`${subDisplay}\\`](${target}) \u2014 ${subDesc}`.trimEnd());\n }\n lines.push(\"\");\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n// Escape characters that MDX would otherwise parse as JSX. Backslash-escaping\n// `<` and `{` keeps placeholder text like `<team_slug>` rendering as literal\n// text instead of being interpreted as a tag or expression.\n//\n// MDX only parses `<` and `{` specially in prose \u2014 inside an inline code span\n// (backticks) both are already literal, and a backslash there renders verbatim\n// (e.g. `` `\\<nameOrToken>` `` shows the backslash). So leave code spans\n// untouched and escape only the surrounding prose.\nfunction escapeMdx(text: string): string {\n return text\n .split(/(`[^`]*`)/)\n .map((segment, i) =>\n // Odd indices are the captured code spans; even indices are prose.\n i % 2 === 1 ? segment : segment.replace(/[<{]/g, (c) => `\\\\${c}`),\n )\n .join(\"\");\n}\n\n// Turn inline code spans that are full `npx convex ...` commands into a\n// CodeWithCopyButton component so readers can copy them with one click. The\n// component is registered globally in the docs site's MDXComponents, so no\n// import is needed in the generated page. This runs after escapeMdx, which\n// leaves code spans verbatim, so the command text inside the backticks is\n// unescaped and drops straight into the `text` attribute.\n//\n// We pass the command as a JSX expression containing a JSON-encoded string\n// (`text={\"...\"}`) rather than a plain attribute (`text=\"...\"`) because\n// commands routinely contain double quotes (e.g. JSON arguments like\n// `'{\"body\": \"hello\"}'`), which would otherwise terminate the attribute value\n// and produce invalid MDX. JSON.stringify escapes quotes and backslashes for\n// us.\nfunction renderCopyableCommands(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) =>\n /^- /.test(line)\n ? line.replace(\n /`(npx convex(?:@[^\\s`]+)? [^`]*)`/g,\n (_, command) =>\n `<CodeWithCopyButton text={${JSON.stringify(command)}} />`,\n )\n : line,\n )\n .join(\"\\n\");\n}\n\n// Replace \"\u2022\" bullet points with markdown \"-\" bullet points. Every line that is\n// (maybe leading spaces +) \"\u2022 \" becomes the same spaces + \"- \". This runs before\n// formatExampleLines and renderCopyableCommands so that `npx convex ...` commands\n// in bulleted help text are recognized as list items and get a copy button.\nexport function replaceBullets(text: string): string {\n return text.replace(/^( *)\u2022 /gm, \"$1- \");\n}\n\n// Convert indented example lines (e.g., from CLI help text) into markdown lists\n// so they render with proper line breaks instead of collapsing into one line.\n// Consecutive indented lines are gathered into a block; any line indented by at\n// least two spaces belongs to the block, regardless of how deeply it nests.\n//\n// How much the block is dedented depends on what precedes it:\n// - When the block follows a list item, its indentation encodes real nesting\n// (a sub-list under that item), so it is preserved verbatim. This keeps a\n// 3-level list like the deploy command's preview-key description intact\n// instead of flattening the 2-space sub-item to a top-level bullet.\n// - When the block follows prose (or starts the text), the indentation is\n// just help-text formatting, so the block is dedented by its smallest\n// indent to the left margin. Otherwise an indented list wouldn't interrupt\n// the preceding paragraph and would collapse into it.\nfunction formatExampleLines(text: string): string {\n const lines = text.split(\"\\n\");\n const result: string[] = [];\n let blockLines: string[] = [];\n\n const flushBlock = () => {\n if (blockLines.length === 0) {\n return;\n }\n const prev = result[result.length - 1];\n const followsListItem = prev !== undefined && isListItem(prev);\n const dedent = followsListItem\n ? 0\n : Math.min(...blockLines.map(indentWidth));\n for (const blockLine of blockLines) {\n result.push(toListItem(blockLine.substring(dedent)));\n }\n blockLines = [];\n };\n\n for (const line of lines) {\n // Any line indented by at least two spaces (and not blank) is part of an\n // example block, regardless of nesting depth.\n if (/^ {2,}\\S/.test(line)) {\n blockLines.push(line);\n } else {\n flushBlock();\n result.push(line);\n }\n }\n flushBlock();\n\n return result.join(\"\\n\");\n}\n\nfunction indentWidth(line: string): number {\n return line.length - line.trimStart().length;\n}\n\nfunction isListItem(line: string): boolean {\n const content = line.trimStart();\n return content.startsWith(\"- \") || /^\\d+\\.\\s/.test(content);\n}\n\n// Render an example line as a markdown list item, preserving any leading indent\n// so nested items stay nested. Lines that are already list items \u2014 either a\n// bullet (`- `) or an ordered-list item (`1. `) \u2014 are kept verbatim so they\n// render as their intended list type. Prefixing an ordered-list item with `- `\n// would nest it inside a bullet, which renders as roman numerals instead of the\n// numbered steps.\nfunction toListItem(line: string): string {\n if (isListItem(line)) {\n return line;\n }\n const indent = line.slice(0, indentWidth(line));\n return `${indent}- ${line.trimStart()}`;\n}\n\nfunction indentContinuation(text: string): string {\n return text.replace(/\\n/g, \"\\n \");\n}\n"],
4
+ "sourcesContent": ["import type { CommandUnknownOpts } from \"@commander-js/extra-typings\";\n\nexport type GeneratedDocs = Record<string, string>;\n\ntype CommandInfo = {\n command: CommandUnknownOpts;\n // Full path of command names from the root, e.g. [\"convex\", \"env\", \"set\"].\n path: string[];\n};\n\n/**\n * Generate Markdown reference docs for a Commander main command.\n *\n * Returns a map of file paths (relative, e.g. `env.mdx`) to Markdown\n * contents. One file is generated for each visible main command (a direct\n * child of the root); the root itself gets no page, so \"Command Reference\"\n * is a sidebar section with no landing page. Descendant subcommands are\n * rendered as sections inside their main command's file, with their heading\n * level reflecting their depth: a direct subcommand (`env default`) is an\n * `## h2`, a sub-subcommand (`env default set`) an `### h3`, and so on.\n *\n * Each page carries a `description` frontmatter equal to the command's\n * summary; the `/cli` page reads these back via Docusaurus metadata to render\n * the list of commands dynamically.\n */\nexport function generateDocs(root: CommandUnknownOpts): GeneratedDocs {\n const docs: GeneratedDocs = {};\n const all = collectCommands(root, [root.name()]);\n\n let mainCommandPosition = 0;\n for (const entry of all) {\n // Skip the root command (no landing page) and any descendant beyond a\n // main command (those are rendered inside their main command's file).\n if (entry.path.length !== 2) continue;\n const filePath = `${entry.path[1]}.mdx`;\n const sidebarPosition = ++mainCommandPosition;\n const summary =\n entry.command.summary() || entry.command.description() || \"\";\n // Collapse to a single line so it's a valid YAML frontmatter scalar, and\n // quote it safely (summaries contain `[BETA]`, `:`, etc.).\n const description = JSON.stringify(summary.replace(/\\s+/g, \" \").trim());\n\n const lines: string[] = [];\n const sidebarLabel = `npx ${entry.path.join(\" \")}`;\n lines.push(\"---\");\n lines.push(`sidebar_position: ${sidebarPosition}`);\n // Set an explicit `title` so the Docusaurus <title> (and browser tab) reads\n // the full command, e.g. `npx convex dev | \u2026`. Without it Docusaurus\n // derives the title from the doc id (the file name), which would be just\n // `dev`.\n lines.push(`title: \"${sidebarLabel}\"`);\n lines.push(`sidebar_label: \"${sidebarLabel}\"`);\n lines.push(`description: ${description}`);\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(\n \"{/* @generated from the command definitions, do not edit manually (run `just regenerate-cli-docs` to regenerate) */}\",\n );\n lines.push(\"\");\n lines.push(renderCommand(entry, all, { headingLevel: 1 }));\n\n const descendants = all.filter(\n (e) =>\n e.path.length > entry.path.length &&\n entry.path.every((p, i) => e.path[i] === p),\n );\n for (const d of descendants) {\n // Render the heading hierarchically by depth below the root: a direct\n // subcommand (`env default`, path length 3) is an h2, a sub-subcommand\n // (`env default set`, path length 4) is an h3, and so on. This nests\n // sub-subcommands under their parent group instead of flattening every\n // descendant to the same h2 level.\n lines.push(\n renderCommand(d, all, {\n headingLevel: d.path.length - 1,\n includeSubcommandsList: false,\n }),\n );\n }\n\n docs[filePath] = lines.join(\"\\n\");\n }\n\n return docs;\n}\n\nfunction collectCommands(\n command: CommandUnknownOpts,\n path: string[],\n): CommandInfo[] {\n const result: CommandInfo[] = [{ command, path }];\n for (const sub of command.commands) {\n // Skip hidden commands and Commander's built-in help command.\n if ((sub as any)._hidden) continue;\n if (sub.name() === \"help\") continue;\n result.push(...collectCommands(sub, [...path, sub.name()]));\n }\n return result;\n}\n\nfunction displayName(path: string[]): string {\n return `npx ${path.join(\" \")}`;\n}\n\n// Build the usage suffix (everything after the command name) for the Syntax\n// code block. We derive the argument portion from the command's registered\n// arguments rather than trusting a manual `.usage()` override, so the Syntax\n// line always agrees with the Arguments section below. For example `env set`\n// overrides `.usage()` to show `<name> <value>` (pretending the args are\n// required) for nicer `--help` output, even though it registers them as\n// optional `[name] [value]`; reproducing the override verbatim would make the\n// generated page contradict itself. Commands with no registered arguments keep\n// their `.usage()` string verbatim (e.g. the root's `<command> [options]`).\nfunction commandUsage(command: CommandUnknownOpts): string {\n const args = (command as any).registeredArguments ?? [];\n if (args.length === 0) {\n return command.usage();\n }\n // Mirror Commander's default usage order: options, then a subcommand\n // placeholder, then the positional arguments. `[options]` is always present\n // because the built-in `--help` option is always registered.\n const parts = [\"[options]\"];\n if (command.commands.length > 0) {\n parts.push(\"[command]\");\n }\n for (const arg of args) {\n const name = `${arg._name}${arg.variadic ? \"...\" : \"\"}`;\n parts.push(arg.required ? `<${name}>` : `[${name}]`);\n }\n return parts.join(\" \");\n}\n\n// Anchor id for a descendant command rendered as an h2 section inside its\n// main command's file. Built from the path segments below the main command,\n// joined with `-`, so sub-subcommands stay unique. For example, both\n// `deployment create` and `deployment token create` would collide on `#create`\n// if we used only the leaf name; instead they become `#create` and\n// `#token-create`.\nfunction anchorSlug(path: string[]): string {\n return path.slice(2).join(\"-\");\n}\n\n// Link target for a subcommand listed inside a command's \"Subcommands\"\n// section. From the root index, main commands live in sibling files. From a\n// main command page, descendants are rendered as h2 sections in the same\n// file, anchored by their path below the main command.\nfunction subcommandLink(parentPath: string[], subPath: string[]): string {\n if (parentPath.length === 1) {\n return `./${subPath[1]}`;\n }\n return `#${anchorSlug(subPath)}`;\n}\n\nfunction renderCommand(\n entry: CommandInfo,\n all: CommandInfo[],\n options: { headingLevel: number; includeSubcommandsList?: boolean },\n): string {\n const { headingLevel, includeSubcommandsList = true } = options;\n const h1 = \"#\".repeat(headingLevel);\n const h2 = \"#\".repeat(headingLevel + 1);\n\n const { command, path } = entry;\n const display = displayName(path);\n const description = renderCopyableCommands(\n escapeMdx(\n formatExampleLines(\n replaceBullets(command.description() || command.summary() || \"\"),\n ),\n ),\n );\n\n const lines: string[] = [];\n // For nested subcommand sections, pin the heading id to the path below the\n // main command so anchors like `#set` and `#token-create` stay unique even\n // when leaf names collide across different parents.\n const headingSuffix = headingLevel > 1 ? ` \\\\{#${anchorSlug(path)}}` : \"\";\n lines.push(`${h1} \\`${display}\\`${headingSuffix}`);\n lines.push(\"\");\n if (description) {\n lines.push(description);\n lines.push(\"\");\n }\n\n const usage = commandUsage(command);\n lines.push(`${h2} Syntax`);\n lines.push(\"\");\n lines.push(\"```sh\");\n lines.push(`${display} ${usage}`.trim());\n lines.push(\"```\");\n lines.push(\"\");\n\n const aliases = command.aliases();\n if (aliases.length > 0) {\n lines.push(`${h2} Aliases`);\n lines.push(\"\");\n for (const alias of aliases) {\n lines.push(`- \\`${alias}\\``);\n }\n lines.push(\"\");\n }\n\n const args = (command as any).registeredArguments ?? [];\n if (args.length > 0) {\n lines.push(`${h2} Arguments`);\n lines.push(\"\");\n lines.push(\"<dl>\");\n for (const arg of args) {\n const name = arg.required ? `<${arg._name}>` : `[${arg._name}]`;\n const desc = escapeMdx(replaceBullets(arg.description || \"\"));\n lines.push(`<dt>\\`${name}\\`</dt>`);\n lines.push(`<dd>`);\n lines.push(\"\");\n lines.push(desc);\n lines.push(\"\");\n lines.push(`</dd>`);\n }\n lines.push(\"</dl>\");\n lines.push(\"\");\n }\n\n const opts = command.options.filter((o: any) => !o.hidden);\n if (opts.length > 0) {\n lines.push(`${h2} Options`);\n lines.push(\"\");\n lines.push(\"<dl>\");\n for (const opt of opts) {\n const flags = (opt as any).flags as string;\n const desc = escapeMdx(replaceBullets(opt.description || \"\"));\n lines.push(`<dt>\\`${flags}\\`</dt>`);\n lines.push(`<dd>`);\n lines.push(\"\");\n lines.push(desc);\n lines.push(\"\");\n lines.push(`</dd>`);\n }\n lines.push(\"</dl>\");\n lines.push(\"\");\n }\n\n if (includeSubcommandsList) {\n const subEntries = all.filter(\n (e) =>\n e.path.length === path.length + 1 &&\n path.every((p, i) => e.path[i] === p),\n );\n if (subEntries.length > 0) {\n lines.push(`${h2} Subcommands`);\n lines.push(\"\");\n for (const sub of subEntries) {\n const subDisplay = displayName(sub.path);\n const target = subcommandLink(path, sub.path);\n const subDesc = indentContinuation(\n escapeMdx(sub.command.summary() || sub.command.description() || \"\"),\n );\n lines.push(`- [\\`${subDisplay}\\`](${target}) \u2014 ${subDesc}`.trimEnd());\n }\n lines.push(\"\");\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n// Escape characters that MDX would otherwise parse as JSX. Backslash-escaping\n// `<` and `{` keeps placeholder text like `<team_slug>` rendering as literal\n// text instead of being interpreted as a tag or expression.\n//\n// MDX only parses `<` and `{` specially in prose \u2014 inside an inline code span\n// (backticks) both are already literal, and a backslash there renders verbatim\n// (e.g. `` `\\<nameOrToken>` `` shows the backslash). So leave code spans\n// untouched and escape only the surrounding prose.\nfunction escapeMdx(text: string): string {\n return text\n .split(/(`[^`]*`)/)\n .map((segment, i) =>\n // Odd indices are the captured code spans; even indices are prose.\n i % 2 === 1 ? segment : segment.replace(/[<{]/g, (c) => `\\\\${c}`),\n )\n .join(\"\");\n}\n\n// Turn inline code spans that are full `npx convex ...` commands into a\n// CodeWithCopyButton component so readers can copy them with one click. The\n// component is registered globally in the docs site's MDXComponents, so no\n// import is needed in the generated page. This runs after escapeMdx, which\n// leaves code spans verbatim, so the command text inside the backticks is\n// unescaped and drops straight into the `text` attribute.\n//\n// We pass the command as a JSX expression containing a JSON-encoded string\n// (`text={\"...\"}`) rather than a plain attribute (`text=\"...\"`) because\n// commands routinely contain double quotes (e.g. JSON arguments like\n// `'{\"body\": \"hello\"}'`), which would otherwise terminate the attribute value\n// and produce invalid MDX. JSON.stringify escapes quotes and backslashes for\n// us.\nfunction renderCopyableCommands(text: string): string {\n return text\n .split(\"\\n\")\n .map((line) =>\n /^- /.test(line)\n ? line.replace(\n /`(npx convex [^`]*)`/g,\n (_, command) =>\n `<CodeWithCopyButton text={${JSON.stringify(command)}} />`,\n )\n : line,\n )\n .join(\"\\n\");\n}\n\n// Replace \"\u2022\" bullet points with markdown \"-\" bullet points. Every line that is\n// (maybe leading spaces +) \"\u2022 \" becomes the same spaces + \"- \". This runs before\n// formatExampleLines and renderCopyableCommands so that `npx convex ...` commands\n// in bulleted help text are recognized as list items and get a copy button.\nexport function replaceBullets(text: string): string {\n return text.replace(/^( *)\u2022 /gm, \"$1- \");\n}\n\n// Convert indented example lines (e.g., from CLI help text) into markdown lists\n// so they render with proper line breaks instead of collapsing into one line.\n// Consecutive indented lines are gathered into a block; any line indented by at\n// least two spaces belongs to the block, regardless of how deeply it nests.\n//\n// How much the block is dedented depends on what precedes it:\n// - When the block follows a list item, its indentation encodes real nesting\n// (a sub-list under that item), so it is preserved verbatim. This keeps a\n// 3-level list like the deploy command's preview-key description intact\n// instead of flattening the 2-space sub-item to a top-level bullet.\n// - When the block follows prose (or starts the text), the indentation is\n// just help-text formatting, so the block is dedented by its smallest\n// indent to the left margin. Otherwise an indented list wouldn't interrupt\n// the preceding paragraph and would collapse into it.\nfunction formatExampleLines(text: string): string {\n const lines = text.split(\"\\n\");\n const result: string[] = [];\n let blockLines: string[] = [];\n\n const flushBlock = () => {\n if (blockLines.length === 0) {\n return;\n }\n const prev = result[result.length - 1];\n const followsListItem = prev !== undefined && isListItem(prev);\n const dedent = followsListItem\n ? 0\n : Math.min(...blockLines.map(indentWidth));\n for (const blockLine of blockLines) {\n result.push(toListItem(blockLine.substring(dedent)));\n }\n blockLines = [];\n };\n\n for (const line of lines) {\n // Any line indented by at least two spaces (and not blank) is part of an\n // example block, regardless of nesting depth.\n if (/^ {2,}\\S/.test(line)) {\n blockLines.push(line);\n } else {\n flushBlock();\n result.push(line);\n }\n }\n flushBlock();\n\n return result.join(\"\\n\");\n}\n\nfunction indentWidth(line: string): number {\n return line.length - line.trimStart().length;\n}\n\nfunction isListItem(line: string): boolean {\n const content = line.trimStart();\n return content.startsWith(\"- \") || /^\\d+\\.\\s/.test(content);\n}\n\n// Render an example line as a markdown list item, preserving any leading indent\n// so nested items stay nested. Lines that are already list items \u2014 either a\n// bullet (`- `) or an ordered-list item (`1. `) \u2014 are kept verbatim so they\n// render as their intended list type. Prefixing an ordered-list item with `- `\n// would nest it inside a bullet, which renders as roman numerals instead of the\n// numbered steps.\nfunction toListItem(line: string): string {\n if (isListItem(line)) {\n return line;\n }\n const indent = line.slice(0, indentWidth(line));\n return `${indent}- ${line.trimStart()}`;\n}\n\nfunction indentContinuation(text: string): string {\n return text.replace(/\\n/g, \"\\n \");\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -36,6 +36,11 @@ export async function handleDashboard(ctx, deployment, options) {
36
36
  ctx,
37
37
  dashboardPort,
38
38
  async (request, response) => {
39
+ if (!isLoopbackHost(request.headers.host, dashboardPort)) {
40
+ response.statusCode = 403;
41
+ response.end();
42
+ return;
43
+ }
39
44
  const pathname = new URL(
40
45
  request.url ?? "/",
41
46
  // We only want to extract the pathname so the base doesn’t matter
@@ -72,6 +77,10 @@ export async function handleDashboard(ctx, deployment, options) {
72
77
  cleanupHandle
73
78
  };
74
79
  }
80
+ export function isLoopbackHost(host, port) {
81
+ const normalizedHost = host?.toLowerCase();
82
+ return normalizedHost === `127.0.0.1:${port}` || normalizedHost === `localhost:${port}`;
83
+ }
75
84
  async function reportSelfHostedEvent(ctx, {
76
85
  anonymousId,
77
86
  eventName,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/cli/lib/localDeployment/dashboard.ts"],
4
- "sourcesContent": ["import { Context } from \"../../../bundler/context.js\";\nimport {\n dashboardOutDir,\n loadProjectDashboardConfig,\n loadUuidForAnonymousUser,\n saveProjectDashboardConfig,\n} from \"./filePaths.js\";\nimport { choosePorts } from \"./utils.js\";\nimport { startServer } from \"./serve.js\";\nimport { localDeploymentUrl, selfHostedEventTag } from \"./run.js\";\nimport serveHandler from \"serve-handler\";\nimport {\n ensureBackendBinaryDownloaded,\n ensureDashboardDownloaded,\n} from \"./download.js\";\nimport { bigBrainAPIMaybeThrows } from \"../utils/utils.js\";\n\nexport const DEFAULT_LOCAL_DASHBOARD_PORT = 6790;\n\n/**\n * This runs the `dashboard-self-hosted` app locally.\n * It's currently just used for the `anonymous` flow, while everything else\n * uses `dashboard.convex.dev`, and some of the code below is written\n * assuming this is only used for `anonymous`.\n */\nexport async function handleDashboard(\n ctx: Context,\n deployment: { name: string; cloudPort: number; adminKey: string },\n options: {\n /** The backend version to use if the user overrides the default version with the --local-backend-version flag */\n backendVersion: string | undefined;\n },\n) {\n // We call `ensureBackendBinaryDownloaded` here to get the version,\n // but `handleAnonymousDeployment` has already downloaded it.\n const { version } = await ensureBackendBinaryDownloaded(\n ctx,\n options.backendVersion === undefined\n ? { kind: \"latest\" }\n : { kind: \"version\", version: options.backendVersion },\n );\n const anonymousId = loadUuidForAnonymousUser(ctx) ?? undefined;\n await ensureDashboardDownloaded(ctx, version);\n const [dashboardPort] = await choosePorts(ctx, {\n count: 1,\n startPort: DEFAULT_LOCAL_DASHBOARD_PORT,\n requestedPorts: [null, null],\n });\n saveProjectDashboardConfig(ctx, deployment.name, {\n port: dashboardPort,\n });\n\n let hasReportedSelfHostedEvent = false;\n\n const serverOptions = { cors: false } as const;\n const { cleanupHandle } = await startServer(\n ctx,\n dashboardPort,\n async (request, response) => {\n const pathname = new URL(\n request.url ?? \"/\",\n // We only want to extract the pathname so the base doesn\u2019t matter\n \"http://localhost\",\n ).pathname;\n\n if (pathname === \"/api/current_deployment\") {\n serverOptions satisfies { cors: false };\n response.setHeader(\"Content-Type\", \"application/json\");\n response.end(\n JSON.stringify({\n name: deployment.name,\n url: localDeploymentUrl(deployment.cloudPort),\n adminKey: deployment.adminKey,\n }),\n );\n return;\n }\n\n if (!hasReportedSelfHostedEvent) {\n hasReportedSelfHostedEvent = true;\n void reportSelfHostedEvent(ctx, {\n anonymousId,\n eventName: \"self_host_dashboard_connected\",\n tag: selfHostedEventTag(\"anonymous\"),\n });\n }\n await serveHandler(request, response, {\n public: dashboardOutDir(),\n });\n },\n serverOptions,\n );\n return {\n dashboardPort,\n cleanupHandle,\n };\n}\n\nasync function reportSelfHostedEvent(\n ctx: Context,\n {\n anonymousId,\n eventName,\n eventFields,\n tag,\n }: {\n anonymousId: string | undefined;\n eventName: string;\n eventFields?: Record<string, unknown>;\n tag: string | undefined;\n },\n) {\n try {\n await bigBrainAPIMaybeThrows({\n ctx,\n method: \"POST\",\n path: \"self_hosted_event\",\n data: {\n selfHostedUuid: anonymousId,\n eventName,\n eventFields,\n tag,\n },\n });\n } catch {\n // ignore\n }\n}\n\nexport function dashboardUrl(ctx: Context, deploymentName: string) {\n const dashboardConfig = loadProjectDashboardConfig(ctx, deploymentName);\n if (dashboardConfig === null) {\n return null;\n }\n\n return `http://127.0.0.1:${dashboardConfig.port}/`;\n}\n"],
5
- "mappings": ";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB,0BAA0B;AACvD,OAAO,kBAAkB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,8BAA8B;AAEhC,aAAM,+BAA+B;AAQ5C,sBAAsB,gBACpB,KACA,YACA,SAIA;AAGA,QAAM,EAAE,QAAQ,IAAI,MAAM;AAAA,IACxB;AAAA,IACA,QAAQ,mBAAmB,SACvB,EAAE,MAAM,SAAS,IACjB,EAAE,MAAM,WAAW,SAAS,QAAQ,eAAe;AAAA,EACzD;AACA,QAAM,cAAc,yBAAyB,GAAG,KAAK;AACrD,QAAM,0BAA0B,KAAK,OAAO;AAC5C,QAAM,CAAC,aAAa,IAAI,MAAM,YAAY,KAAK;AAAA,IAC7C,OAAO;AAAA,IACP,WAAW;AAAA,IACX,gBAAgB,CAAC,MAAM,IAAI;AAAA,EAC7B,CAAC;AACD,6BAA2B,KAAK,WAAW,MAAM;AAAA,IAC/C,MAAM;AAAA,EACR,CAAC;AAED,MAAI,6BAA6B;AAEjC,QAAM,gBAAgB,EAAE,MAAM,MAAM;AACpC,QAAM,EAAE,cAAc,IAAI,MAAM;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,OAAO,SAAS,aAAa;AAC3B,YAAM,WAAW,IAAI;AAAA,QACnB,QAAQ,OAAO;AAAA;AAAA,QAEf;AAAA,MACF,EAAE;AAEF,UAAI,aAAa,2BAA2B;AAC1C;AACA,iBAAS,UAAU,gBAAgB,kBAAkB;AACrD,iBAAS;AAAA,UACP,KAAK,UAAU;AAAA,YACb,MAAM,WAAW;AAAA,YACjB,KAAK,mBAAmB,WAAW,SAAS;AAAA,YAC5C,UAAU,WAAW;AAAA,UACvB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAEA,UAAI,CAAC,4BAA4B;AAC/B,qCAA6B;AAC7B,aAAK,sBAAsB,KAAK;AAAA,UAC9B;AAAA,UACA,WAAW;AAAA,UACX,KAAK,mBAAmB,WAAW;AAAA,QACrC,CAAC;AAAA,MACH;AACA,YAAM,aAAa,SAAS,UAAU;AAAA,QACpC,QAAQ,gBAAgB;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,IACA;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,sBACb,KACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMA;AACA,MAAI;AACF,UAAM,uBAAuB;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAEO,gBAAS,aAAa,KAAc,gBAAwB;AACjE,QAAM,kBAAkB,2BAA2B,KAAK,cAAc;AACtE,MAAI,oBAAoB,MAAM;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,gBAAgB,IAAI;AACjD;",
4
+ "sourcesContent": ["import { Context } from \"../../../bundler/context.js\";\nimport {\n dashboardOutDir,\n loadProjectDashboardConfig,\n loadUuidForAnonymousUser,\n saveProjectDashboardConfig,\n} from \"./filePaths.js\";\nimport { choosePorts } from \"./utils.js\";\nimport { startServer } from \"./serve.js\";\nimport { localDeploymentUrl, selfHostedEventTag } from \"./run.js\";\nimport serveHandler from \"serve-handler\";\nimport {\n ensureBackendBinaryDownloaded,\n ensureDashboardDownloaded,\n} from \"./download.js\";\nimport { bigBrainAPIMaybeThrows } from \"../utils/utils.js\";\n\nexport const DEFAULT_LOCAL_DASHBOARD_PORT = 6790;\n\n/**\n * This runs the `dashboard-self-hosted` app locally.\n * It's currently just used for the `anonymous` flow, while everything else\n * uses `dashboard.convex.dev`, and some of the code below is written\n * assuming this is only used for `anonymous`.\n */\nexport async function handleDashboard(\n ctx: Context,\n deployment: { name: string; cloudPort: number; adminKey: string },\n options: {\n /** The backend version to use if the user overrides the default version with the --local-backend-version flag */\n backendVersion: string | undefined;\n },\n) {\n // We call `ensureBackendBinaryDownloaded` here to get the version,\n // but `handleAnonymousDeployment` has already downloaded it.\n const { version } = await ensureBackendBinaryDownloaded(\n ctx,\n options.backendVersion === undefined\n ? { kind: \"latest\" }\n : { kind: \"version\", version: options.backendVersion },\n );\n const anonymousId = loadUuidForAnonymousUser(ctx) ?? undefined;\n await ensureDashboardDownloaded(ctx, version);\n const [dashboardPort] = await choosePorts(ctx, {\n count: 1,\n startPort: DEFAULT_LOCAL_DASHBOARD_PORT,\n requestedPorts: [null, null],\n });\n saveProjectDashboardConfig(ctx, deployment.name, {\n port: dashboardPort,\n });\n\n let hasReportedSelfHostedEvent = false;\n\n const serverOptions = { cors: false } as const;\n const { cleanupHandle } = await startServer(\n ctx,\n dashboardPort,\n async (request, response) => {\n if (!isLoopbackHost(request.headers.host, dashboardPort)) {\n response.statusCode = 403;\n response.end();\n return;\n }\n\n const pathname = new URL(\n request.url ?? \"/\",\n // We only want to extract the pathname so the base doesn\u2019t matter\n \"http://localhost\",\n ).pathname;\n\n if (pathname === \"/api/current_deployment\") {\n serverOptions satisfies { cors: false };\n response.setHeader(\"Content-Type\", \"application/json\");\n response.end(\n JSON.stringify({\n name: deployment.name,\n url: localDeploymentUrl(deployment.cloudPort),\n adminKey: deployment.adminKey,\n }),\n );\n return;\n }\n\n if (!hasReportedSelfHostedEvent) {\n hasReportedSelfHostedEvent = true;\n void reportSelfHostedEvent(ctx, {\n anonymousId,\n eventName: \"self_host_dashboard_connected\",\n tag: selfHostedEventTag(\"anonymous\"),\n });\n }\n await serveHandler(request, response, {\n public: dashboardOutDir(),\n });\n },\n serverOptions,\n );\n return {\n dashboardPort,\n cleanupHandle,\n };\n}\n\n/**\n * The dashboard is only meant to be opened at the URL the CLI prints, so\n * requests addressed to any other hostname are not served, even if that\n * hostname resolves to 127.0.0.1.\n */\nexport function isLoopbackHost(host: string | undefined, port: number) {\n const normalizedHost = host?.toLowerCase();\n return (\n normalizedHost === `127.0.0.1:${port}` ||\n normalizedHost === `localhost:${port}`\n );\n}\n\nasync function reportSelfHostedEvent(\n ctx: Context,\n {\n anonymousId,\n eventName,\n eventFields,\n tag,\n }: {\n anonymousId: string | undefined;\n eventName: string;\n eventFields?: Record<string, unknown>;\n tag: string | undefined;\n },\n) {\n try {\n await bigBrainAPIMaybeThrows({\n ctx,\n method: \"POST\",\n path: \"self_hosted_event\",\n data: {\n selfHostedUuid: anonymousId,\n eventName,\n eventFields,\n tag,\n },\n });\n } catch {\n // ignore\n }\n}\n\nexport function dashboardUrl(ctx: Context, deploymentName: string) {\n const dashboardConfig = loadProjectDashboardConfig(ctx, deploymentName);\n if (dashboardConfig === null) {\n return null;\n }\n\n return `http://127.0.0.1:${dashboardConfig.port}/`;\n}\n"],
5
+ "mappings": ";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB,0BAA0B;AACvD,OAAO,kBAAkB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,8BAA8B;AAEhC,aAAM,+BAA+B;AAQ5C,sBAAsB,gBACpB,KACA,YACA,SAIA;AAGA,QAAM,EAAE,QAAQ,IAAI,MAAM;AAAA,IACxB;AAAA,IACA,QAAQ,mBAAmB,SACvB,EAAE,MAAM,SAAS,IACjB,EAAE,MAAM,WAAW,SAAS,QAAQ,eAAe;AAAA,EACzD;AACA,QAAM,cAAc,yBAAyB,GAAG,KAAK;AACrD,QAAM,0BAA0B,KAAK,OAAO;AAC5C,QAAM,CAAC,aAAa,IAAI,MAAM,YAAY,KAAK;AAAA,IAC7C,OAAO;AAAA,IACP,WAAW;AAAA,IACX,gBAAgB,CAAC,MAAM,IAAI;AAAA,EAC7B,CAAC;AACD,6BAA2B,KAAK,WAAW,MAAM;AAAA,IAC/C,MAAM;AAAA,EACR,CAAC;AAED,MAAI,6BAA6B;AAEjC,QAAM,gBAAgB,EAAE,MAAM,MAAM;AACpC,QAAM,EAAE,cAAc,IAAI,MAAM;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,OAAO,SAAS,aAAa;AAC3B,UAAI,CAAC,eAAe,QAAQ,QAAQ,MAAM,aAAa,GAAG;AACxD,iBAAS,aAAa;AACtB,iBAAS,IAAI;AACb;AAAA,MACF;AAEA,YAAM,WAAW,IAAI;AAAA,QACnB,QAAQ,OAAO;AAAA;AAAA,QAEf;AAAA,MACF,EAAE;AAEF,UAAI,aAAa,2BAA2B;AAC1C;AACA,iBAAS,UAAU,gBAAgB,kBAAkB;AACrD,iBAAS;AAAA,UACP,KAAK,UAAU;AAAA,YACb,MAAM,WAAW;AAAA,YACjB,KAAK,mBAAmB,WAAW,SAAS;AAAA,YAC5C,UAAU,WAAW;AAAA,UACvB,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAEA,UAAI,CAAC,4BAA4B;AAC/B,qCAA6B;AAC7B,aAAK,sBAAsB,KAAK;AAAA,UAC9B;AAAA,UACA,WAAW;AAAA,UACX,KAAK,mBAAmB,WAAW;AAAA,QACrC,CAAC;AAAA,MACH;AACA,YAAM,aAAa,SAAS,UAAU;AAAA,QACpC,QAAQ,gBAAgB;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,IACA;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAOO,gBAAS,eAAe,MAA0B,MAAc;AACrE,QAAM,iBAAiB,MAAM,YAAY;AACzC,SACE,mBAAmB,aAAa,IAAI,MACpC,mBAAmB,aAAa,IAAI;AAExC;AAEA,eAAe,sBACb,KACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMA;AACA,MAAI;AACF,UAAM,uBAAuB;AAAA,MAC3B;AAAA,MACA,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAEO,gBAAS,aAAa,KAAc,gBAAwB;AACjE,QAAM,kBAAkB,2BAA2B,KAAK,cAAc;AACtE,MAAI,oBAAoB,MAAM;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,gBAAgB,IAAI;AACjD;",
6
6
  "names": []
7
7
  }
@@ -26,6 +26,7 @@ import { nodeFs, withTmpDir } from "../../../bundler/fs.js";
26
26
  import { recursivelyDelete, recursivelyCopy } from "../fsUtils.js";
27
27
  import { LocalDeploymentError } from "./errors.js";
28
28
  import path from "path";
29
+ import { isValidBackendVersion } from "./utils.js";
29
30
  async function makeExecutable(p) {
30
31
  switch (process.platform) {
31
32
  case "darwin":
@@ -55,6 +56,7 @@ export async function ensureBackendBinaryDownloaded(ctx, version) {
55
56
  return _ensureBackendBinaryDownloaded(ctx, latestVersionWithBinary);
56
57
  }
57
58
  async function _ensureBackendBinaryDownloaded(ctx, version) {
59
+ await assertValidBackendVersion(ctx, version);
58
60
  logVerbose(`Ensuring backend binary downloaded for version ${version}`);
59
61
  const existingDownload = await checkForExistingDownload(ctx, version);
60
62
  if (existingDownload !== null) {
@@ -67,6 +69,18 @@ async function _ensureBackendBinaryDownloaded(ctx, version) {
67
69
  const binaryPath = await downloadBackendBinary(ctx, version);
68
70
  return { version, binaryPath };
69
71
  }
72
+ async function assertValidBackendVersion(ctx, version) {
73
+ if (!isValidBackendVersion(version)) {
74
+ return await ctx.crash({
75
+ exitCode: 1,
76
+ errorType: "fatal",
77
+ printedMessage: "Invalid local backend version",
78
+ errForSentry: new LocalDeploymentError(
79
+ `Invalid local backend version: ${JSON.stringify(version)}`
80
+ )
81
+ });
82
+ }
83
+ }
70
84
  let cachedLatestVersion = null;
71
85
  export async function findLatestVersionWithBinary(ctx, requireSuccess) {
72
86
  if (cachedLatestVersion !== null) {
@@ -190,7 +204,7 @@ function getDownloadPath() {
190
204
  return null;
191
205
  }
192
206
  function getGithubDownloadUrl(version, filename) {
193
- return `https://github.com/get-convex/convex-backend/releases/download/${version}/${filename}`;
207
+ return `https://github.com/convex/convex-backend/releases/download/${version}/${filename}`;
194
208
  }
195
209
  async function downloadZipFile(ctx, args) {
196
210
  const { version, filename, nameForLogging } = args;
@@ -245,6 +259,7 @@ async function downloadZipFile(ctx, args) {
245
259
  return executablePath(version);
246
260
  }
247
261
  export async function ensureDashboardDownloaded(ctx, version) {
262
+ await assertValidBackendVersion(ctx, version);
248
263
  const config = loadGlobalDashboardConfig(ctx);
249
264
  if (config !== null && config.version === version) {
250
265
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/cli/lib/localDeployment/download.ts"],
4
- "sourcesContent": ["import { version as npmVersion } from \"../../version.js\";\nimport AdmZip from \"adm-zip\";\nimport { Context } from \"../../../bundler/context.js\";\nimport {\n logFinishedStep,\n startLogProgress,\n logVerbose,\n logMessage,\n logError,\n logWarning,\n} from \"../../../bundler/log.js\";\nimport {\n dashboardZip,\n executablePath,\n versionedBinaryDir,\n dashboardOutDir,\n resetDashboardDir,\n loadGlobalDashboardConfig,\n saveGlobalDashboardConfig,\n executableName,\n} from \"./filePaths.js\";\nimport child_process from \"child_process\";\nimport { promisify } from \"util\";\nimport { Readable } from \"stream\";\nimport { TempPath, nodeFs, withTmpDir } from \"../../../bundler/fs.js\";\nimport { recursivelyDelete, recursivelyCopy } from \"../fsUtils.js\";\nimport { LocalDeploymentError } from \"./errors.js\";\nimport type { ProgressBarInstance } from \"../../../vendor/progress/index.js\";\nimport path from \"path\";\n\nasync function makeExecutable(p: string) {\n switch (process.platform) {\n case \"darwin\":\n case \"linux\": {\n await promisify(child_process.exec)(`chmod +x ${p}`);\n }\n }\n}\n\nexport async function ensureBackendBinaryDownloaded(\n ctx: Context,\n version:\n | { kind: \"latest\"; allowedVersion?: string | undefined }\n | { kind: \"version\"; version: string },\n): Promise<{ binaryPath: string; version: string }> {\n if (version.kind === \"version\") {\n return _ensureBackendBinaryDownloaded(ctx, version.version);\n }\n if (version.allowedVersion) {\n const latestVersionWithBinary = await findLatestVersionWithBinary(\n ctx,\n false,\n );\n if (latestVersionWithBinary === null) {\n logWarning(\n `Failed to get latest version from GitHub, using downloaded version ${version.allowedVersion}`,\n );\n return _ensureBackendBinaryDownloaded(ctx, version.allowedVersion);\n }\n return _ensureBackendBinaryDownloaded(ctx, latestVersionWithBinary);\n }\n const latestVersionWithBinary = await findLatestVersionWithBinary(ctx, true);\n return _ensureBackendBinaryDownloaded(ctx, latestVersionWithBinary);\n}\n\nasync function _ensureBackendBinaryDownloaded(\n ctx: Context,\n version: string,\n): Promise<{ binaryPath: string; version: string }> {\n logVerbose(`Ensuring backend binary downloaded for version ${version}`);\n const existingDownload = await checkForExistingDownload(ctx, version);\n if (existingDownload !== null) {\n logVerbose(`Using existing download at ${existingDownload}`);\n return {\n binaryPath: existingDownload,\n version,\n };\n }\n const binaryPath = await downloadBackendBinary(ctx, version);\n return { version, binaryPath };\n}\n\nlet cachedLatestVersion: string | null = null;\n\n/**\n * Finds the latest version of the Convex local backend through\n * version.convex.dev, caching the resolved version for the lifetime of the\n * process so repeated lookups hit the network at most once.\n *\n * Wraps {@link _findLatestVersionWithBinary}. Only successful (non-null)\n * results are cached, so a failed `requireSuccess: false` lookup can be retried\n * by a later call.\n */\nexport async function findLatestVersionWithBinary<\n RequireSuccess extends boolean,\n>(\n ctx: Context,\n requireSuccess: RequireSuccess,\n): Promise<RequireSuccess extends true ? string : string | null> {\n if (cachedLatestVersion !== null) {\n return cachedLatestVersion;\n }\n const result = await _findLatestVersionWithBinary(ctx, requireSuccess);\n if (result !== null) {\n cachedLatestVersion = result;\n }\n return result;\n}\n\n/**\n * Finds the latest version of the Convex local backend\n * through version.convex.dev\n */\nexport async function _findLatestVersionWithBinary<\n RequireSuccess extends boolean,\n>(\n ctx: Context,\n requireSuccess: RequireSuccess,\n): Promise<RequireSuccess extends true ? string : string | null> {\n // These shouldn't crash when there's a perfectly good binary already available.\n async function maybeCrash(\n ...args: Parameters<typeof ctx.crash>\n ): Promise<RequireSuccess extends true ? never : null> {\n if (requireSuccess) {\n return await ctx.crash(...args);\n }\n if (args[0].printedMessage) {\n logError(args[0].printedMessage);\n } else {\n logError(\"Error fetching latest version\");\n }\n return null as RequireSuccess extends true ? never : null;\n }\n\n logVerbose(\"Fetching latest backend version from version API\");\n\n try {\n const response = await fetch(\n \"https://version.convex.dev/v1/local_backend_version\",\n {\n headers: { \"Convex-Client\": `npm-cli-${npmVersion}` },\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n return await maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `version.convex.dev returned ${response.status}: ${text}`,\n errForSentry: new LocalDeploymentError(\n `version.convex.dev returned ${response.status}: ${text}`,\n ),\n });\n }\n\n const data = (await response.json()) as { version: string };\n if (!data.version) {\n return await maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Invalid response missing version field\",\n errForSentry: new LocalDeploymentError(\n \"Invalid response missing version field\",\n ),\n });\n }\n\n logVerbose(`Latest backend version is ${data.version}`);\n return data.version;\n } catch (e) {\n return maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Failed to fetch latest backend version\",\n errForSentry: new LocalDeploymentError(e?.toString()),\n });\n }\n}\n\n/**\n *\n * @param ctx\n * @param version\n * @returns The binary path if it exists, or null\n */\nasync function checkForExistingDownload(\n ctx: Context,\n version: string,\n): Promise<string | null> {\n const destDir = versionedBinaryDir(version);\n if (!ctx.fs.exists(destDir)) {\n return null;\n }\n const p = executablePath(version);\n if (!ctx.fs.exists(p)) {\n // This directory isn't what we expected. Remove it.\n recursivelyDelete(ctx, destDir, { force: true });\n return null;\n }\n await makeExecutable(p);\n return p;\n}\n\nasync function downloadBackendBinary(\n ctx: Context,\n version: string,\n): Promise<string> {\n const downloadPath = getDownloadPath();\n // Note: We validate earlier that there's a binary for this platform at the specified version,\n // so in practice, we should never hit errors here.\n if (downloadPath === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Unsupported platform ${process.platform} and architecture ${process.arch} for local deployment.`,\n });\n }\n await downloadZipFile(ctx, {\n version,\n filename: downloadPath,\n nameForLogging: \"Convex backend binary\",\n onDownloadComplete: async (ctx, unzippedPath) => {\n const name = executableName();\n const tempExecPath = path.join(unzippedPath, name);\n await makeExecutable(tempExecPath);\n logVerbose(\"Marked as executable\");\n ctx.fs.mkdir(versionedBinaryDir(version), { recursive: true });\n ctx.fs.swapTmpFile(tempExecPath as TempPath, executablePath(version));\n },\n });\n return executablePath(version);\n}\n\n/**\n * Get the artifact name, composed of the target convex-local-backend and\n * the Rust \"target triple\" appropriate for the current machine.\n **/\nfunction getDownloadPath() {\n switch (process.platform) {\n case \"darwin\":\n if (process.arch === \"arm64\") {\n return \"convex-local-backend-aarch64-apple-darwin.zip\";\n } else if (process.arch === \"x64\") {\n return \"convex-local-backend-x86_64-apple-darwin.zip\";\n }\n break;\n case \"linux\":\n if (process.arch === \"arm64\") {\n return \"convex-local-backend-aarch64-unknown-linux-gnu.zip\";\n } else if (process.arch === \"x64\") {\n return \"convex-local-backend-x86_64-unknown-linux-gnu.zip\";\n }\n break;\n case \"win32\":\n return \"convex-local-backend-x86_64-pc-windows-msvc.zip\";\n }\n return null;\n}\n\nfunction getGithubDownloadUrl(version: string, filename: string) {\n return `https://github.com/get-convex/convex-backend/releases/download/${version}/${filename}`;\n}\n\nasync function downloadZipFile(\n ctx: Context,\n args: {\n version: string;\n filename: string;\n nameForLogging: string;\n onDownloadComplete: (ctx: Context, unzippedPath: TempPath) => Promise<void>;\n },\n) {\n const { version, filename, nameForLogging } = args;\n const url = getGithubDownloadUrl(version, filename);\n const response = await fetch(url);\n const contentLength = parseInt(\n response.headers.get(\"content-length\") ?? \"\",\n 10,\n );\n let progressBar: ProgressBarInstance | null = null;\n if (!isNaN(contentLength) && contentLength !== 0 && process.stdout.isTTY) {\n progressBar = startLogProgress(\n `Downloading ${nameForLogging} [:bar] :percent :etas`,\n {\n width: 40,\n total: contentLength,\n clear: true,\n },\n );\n } else {\n logMessage(`Downloading ${nameForLogging}`);\n }\n if (response.status !== 200) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `File not found at ${url}.`,\n });\n }\n await withTmpDir(async (tmpDir) => {\n logVerbose(`Created tmp dir ${tmpDir.path}`);\n // Create a file in the tmp dir\n const zipLocation = tmpDir.registerTempPath(null);\n const readable = Readable.fromWeb(response.body! as any);\n await tmpDir.writeFileStream(zipLocation, readable, (chunk: any) => {\n if (progressBar !== null) {\n progressBar.tick(chunk.length);\n }\n });\n if (progressBar) {\n progressBar.terminate();\n logFinishedStep(`Downloaded ${nameForLogging}`);\n }\n logVerbose(\"Downloaded zip file\");\n\n const zip = new AdmZip(zipLocation);\n await withTmpDir(async (versionDir) => {\n logVerbose(`Created tmp dir ${versionDir.path}`);\n zip.extractAllTo(versionDir.path, true);\n logVerbose(\"Extracted from zip file\");\n await args.onDownloadComplete(ctx, versionDir.path);\n });\n });\n return executablePath(version);\n}\n\nexport async function ensureDashboardDownloaded(ctx: Context, version: string) {\n const config = loadGlobalDashboardConfig(ctx);\n if (config !== null && config.version === version) {\n return;\n }\n await resetDashboardDir(ctx);\n await _ensureDashboardDownloaded(ctx, version);\n // Record which version is now in `dashboardOutDir()`\n // so we can skip the download next time.\n saveGlobalDashboardConfig(ctx, { version });\n}\nasync function _ensureDashboardDownloaded(ctx: Context, version: string) {\n const zipLocation = dashboardZip();\n if (ctx.fs.exists(zipLocation)) {\n ctx.fs.unlink(zipLocation);\n }\n const outDir = dashboardOutDir();\n await downloadZipFile(ctx, {\n version,\n filename: \"dashboard.zip\",\n nameForLogging: \"Convex dashboard\",\n onDownloadComplete: async (ctx, unzippedPath) => {\n await recursivelyCopy(ctx, nodeFs, unzippedPath, outDir);\n logVerbose(\"Copied into out dir\");\n },\n });\n return outDir;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { version as npmVersion } from \"../../version.js\";\nimport AdmZip from \"adm-zip\";\nimport { Context } from \"../../../bundler/context.js\";\nimport {\n logFinishedStep,\n startLogProgress,\n logVerbose,\n logMessage,\n logError,\n logWarning,\n} from \"../../../bundler/log.js\";\nimport {\n dashboardZip,\n executablePath,\n versionedBinaryDir,\n dashboardOutDir,\n resetDashboardDir,\n loadGlobalDashboardConfig,\n saveGlobalDashboardConfig,\n executableName,\n} from \"./filePaths.js\";\nimport child_process from \"child_process\";\nimport { promisify } from \"util\";\nimport { Readable } from \"stream\";\nimport { TempPath, nodeFs, withTmpDir } from \"../../../bundler/fs.js\";\nimport { recursivelyDelete, recursivelyCopy } from \"../fsUtils.js\";\nimport { LocalDeploymentError } from \"./errors.js\";\nimport type { ProgressBarInstance } from \"../../../vendor/progress/index.js\";\nimport path from \"path\";\nimport { isValidBackendVersion } from \"./utils.js\";\n\nasync function makeExecutable(p: string) {\n switch (process.platform) {\n case \"darwin\":\n case \"linux\": {\n await promisify(child_process.exec)(`chmod +x ${p}`);\n }\n }\n}\n\nexport async function ensureBackendBinaryDownloaded(\n ctx: Context,\n version:\n | { kind: \"latest\"; allowedVersion?: string | undefined }\n | { kind: \"version\"; version: string },\n): Promise<{ binaryPath: string; version: string }> {\n if (version.kind === \"version\") {\n return _ensureBackendBinaryDownloaded(ctx, version.version);\n }\n if (version.allowedVersion) {\n const latestVersionWithBinary = await findLatestVersionWithBinary(\n ctx,\n false,\n );\n if (latestVersionWithBinary === null) {\n logWarning(\n `Failed to get latest version from GitHub, using downloaded version ${version.allowedVersion}`,\n );\n return _ensureBackendBinaryDownloaded(ctx, version.allowedVersion);\n }\n return _ensureBackendBinaryDownloaded(ctx, latestVersionWithBinary);\n }\n const latestVersionWithBinary = await findLatestVersionWithBinary(ctx, true);\n return _ensureBackendBinaryDownloaded(ctx, latestVersionWithBinary);\n}\n\nasync function _ensureBackendBinaryDownloaded(\n ctx: Context,\n version: string,\n): Promise<{ binaryPath: string; version: string }> {\n await assertValidBackendVersion(ctx, version);\n logVerbose(`Ensuring backend binary downloaded for version ${version}`);\n const existingDownload = await checkForExistingDownload(ctx, version);\n if (existingDownload !== null) {\n logVerbose(`Using existing download at ${existingDownload}`);\n return {\n binaryPath: existingDownload,\n version,\n };\n }\n const binaryPath = await downloadBackendBinary(ctx, version);\n return { version, binaryPath };\n}\n\nasync function assertValidBackendVersion(ctx: Context, version: string) {\n if (!isValidBackendVersion(version)) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Invalid local backend version\",\n errForSentry: new LocalDeploymentError(\n `Invalid local backend version: ${JSON.stringify(version)}`,\n ),\n });\n }\n}\n\nlet cachedLatestVersion: string | null = null;\n\n/**\n * Finds the latest version of the Convex local backend through\n * version.convex.dev, caching the resolved version for the lifetime of the\n * process so repeated lookups hit the network at most once.\n *\n * Wraps {@link _findLatestVersionWithBinary}. Only successful (non-null)\n * results are cached, so a failed `requireSuccess: false` lookup can be retried\n * by a later call.\n */\nexport async function findLatestVersionWithBinary<\n RequireSuccess extends boolean,\n>(\n ctx: Context,\n requireSuccess: RequireSuccess,\n): Promise<RequireSuccess extends true ? string : string | null> {\n if (cachedLatestVersion !== null) {\n return cachedLatestVersion;\n }\n const result = await _findLatestVersionWithBinary(ctx, requireSuccess);\n if (result !== null) {\n cachedLatestVersion = result;\n }\n return result;\n}\n\n/**\n * Finds the latest version of the Convex local backend\n * through version.convex.dev\n */\nexport async function _findLatestVersionWithBinary<\n RequireSuccess extends boolean,\n>(\n ctx: Context,\n requireSuccess: RequireSuccess,\n): Promise<RequireSuccess extends true ? string : string | null> {\n // These shouldn't crash when there's a perfectly good binary already available.\n async function maybeCrash(\n ...args: Parameters<typeof ctx.crash>\n ): Promise<RequireSuccess extends true ? never : null> {\n if (requireSuccess) {\n return await ctx.crash(...args);\n }\n if (args[0].printedMessage) {\n logError(args[0].printedMessage);\n } else {\n logError(\"Error fetching latest version\");\n }\n return null as RequireSuccess extends true ? never : null;\n }\n\n logVerbose(\"Fetching latest backend version from version API\");\n\n try {\n const response = await fetch(\n \"https://version.convex.dev/v1/local_backend_version\",\n {\n headers: { \"Convex-Client\": `npm-cli-${npmVersion}` },\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n return await maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `version.convex.dev returned ${response.status}: ${text}`,\n errForSentry: new LocalDeploymentError(\n `version.convex.dev returned ${response.status}: ${text}`,\n ),\n });\n }\n\n const data = (await response.json()) as { version: string };\n if (!data.version) {\n return await maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Invalid response missing version field\",\n errForSentry: new LocalDeploymentError(\n \"Invalid response missing version field\",\n ),\n });\n }\n\n logVerbose(`Latest backend version is ${data.version}`);\n return data.version;\n } catch (e) {\n return maybeCrash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: \"Failed to fetch latest backend version\",\n errForSentry: new LocalDeploymentError(e?.toString()),\n });\n }\n}\n\n/**\n *\n * @param ctx\n * @param version\n * @returns The binary path if it exists, or null\n */\nasync function checkForExistingDownload(\n ctx: Context,\n version: string,\n): Promise<string | null> {\n const destDir = versionedBinaryDir(version);\n if (!ctx.fs.exists(destDir)) {\n return null;\n }\n const p = executablePath(version);\n if (!ctx.fs.exists(p)) {\n // This directory isn't what we expected. Remove it.\n recursivelyDelete(ctx, destDir, { force: true });\n return null;\n }\n await makeExecutable(p);\n return p;\n}\n\nasync function downloadBackendBinary(\n ctx: Context,\n version: string,\n): Promise<string> {\n const downloadPath = getDownloadPath();\n // Note: We validate earlier that there's a binary for this platform at the specified version,\n // so in practice, we should never hit errors here.\n if (downloadPath === null) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Unsupported platform ${process.platform} and architecture ${process.arch} for local deployment.`,\n });\n }\n await downloadZipFile(ctx, {\n version,\n filename: downloadPath,\n nameForLogging: \"Convex backend binary\",\n onDownloadComplete: async (ctx, unzippedPath) => {\n const name = executableName();\n const tempExecPath = path.join(unzippedPath, name);\n await makeExecutable(tempExecPath);\n logVerbose(\"Marked as executable\");\n ctx.fs.mkdir(versionedBinaryDir(version), { recursive: true });\n ctx.fs.swapTmpFile(tempExecPath as TempPath, executablePath(version));\n },\n });\n return executablePath(version);\n}\n\n/**\n * Get the artifact name, composed of the target convex-local-backend and\n * the Rust \"target triple\" appropriate for the current machine.\n **/\nfunction getDownloadPath() {\n switch (process.platform) {\n case \"darwin\":\n if (process.arch === \"arm64\") {\n return \"convex-local-backend-aarch64-apple-darwin.zip\";\n } else if (process.arch === \"x64\") {\n return \"convex-local-backend-x86_64-apple-darwin.zip\";\n }\n break;\n case \"linux\":\n if (process.arch === \"arm64\") {\n return \"convex-local-backend-aarch64-unknown-linux-gnu.zip\";\n } else if (process.arch === \"x64\") {\n return \"convex-local-backend-x86_64-unknown-linux-gnu.zip\";\n }\n break;\n case \"win32\":\n return \"convex-local-backend-x86_64-pc-windows-msvc.zip\";\n }\n return null;\n}\n\nfunction getGithubDownloadUrl(version: string, filename: string) {\n return `https://github.com/convex/convex-backend/releases/download/${version}/${filename}`;\n}\n\nasync function downloadZipFile(\n ctx: Context,\n args: {\n version: string;\n filename: string;\n nameForLogging: string;\n onDownloadComplete: (ctx: Context, unzippedPath: TempPath) => Promise<void>;\n },\n) {\n const { version, filename, nameForLogging } = args;\n const url = getGithubDownloadUrl(version, filename);\n const response = await fetch(url);\n const contentLength = parseInt(\n response.headers.get(\"content-length\") ?? \"\",\n 10,\n );\n let progressBar: ProgressBarInstance | null = null;\n if (!isNaN(contentLength) && contentLength !== 0 && process.stdout.isTTY) {\n progressBar = startLogProgress(\n `Downloading ${nameForLogging} [:bar] :percent :etas`,\n {\n width: 40,\n total: contentLength,\n clear: true,\n },\n );\n } else {\n logMessage(`Downloading ${nameForLogging}`);\n }\n if (response.status !== 200) {\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `File not found at ${url}.`,\n });\n }\n await withTmpDir(async (tmpDir) => {\n logVerbose(`Created tmp dir ${tmpDir.path}`);\n // Create a file in the tmp dir\n const zipLocation = tmpDir.registerTempPath(null);\n const readable = Readable.fromWeb(response.body! as any);\n await tmpDir.writeFileStream(zipLocation, readable, (chunk: any) => {\n if (progressBar !== null) {\n progressBar.tick(chunk.length);\n }\n });\n if (progressBar) {\n progressBar.terminate();\n logFinishedStep(`Downloaded ${nameForLogging}`);\n }\n logVerbose(\"Downloaded zip file\");\n\n const zip = new AdmZip(zipLocation);\n await withTmpDir(async (versionDir) => {\n logVerbose(`Created tmp dir ${versionDir.path}`);\n zip.extractAllTo(versionDir.path, true);\n logVerbose(\"Extracted from zip file\");\n await args.onDownloadComplete(ctx, versionDir.path);\n });\n });\n return executablePath(version);\n}\n\nexport async function ensureDashboardDownloaded(ctx: Context, version: string) {\n await assertValidBackendVersion(ctx, version);\n const config = loadGlobalDashboardConfig(ctx);\n if (config !== null && config.version === version) {\n return;\n }\n await resetDashboardDir(ctx);\n await _ensureDashboardDownloaded(ctx, version);\n // Record which version is now in `dashboardOutDir()`\n // so we can skip the download next time.\n saveGlobalDashboardConfig(ctx, { version });\n}\nasync function _ensureDashboardDownloaded(ctx: Context, version: string) {\n const zipLocation = dashboardZip();\n if (ctx.fs.exists(zipLocation)) {\n ctx.fs.unlink(zipLocation);\n }\n const outDir = dashboardOutDir();\n await downloadZipFile(ctx, {\n version,\n filename: \"dashboard.zip\",\n nameForLogging: \"Convex dashboard\",\n onDownloadComplete: async (ctx, unzippedPath) => {\n await recursivelyCopy(ctx, nodeFs, unzippedPath, outDir);\n logVerbose(\"Copied into out dir\");\n },\n });\n return outDir;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["latestVersionWithBinary", "ctx"]
7
7
  }
@@ -67,4 +67,7 @@ export const LOCAL_BACKEND_INSTANCE_SECRET = "4361726e697461732c206c69746572616c
67
67
  export function releaseDate(version) {
68
68
  return /^precompiled-(\d{4}-\d{2}-\d{2})-/.exec(version)?.[1] ?? null;
69
69
  }
70
+ export function isValidBackendVersion(version) {
71
+ return /^precompiled-\d{4}-\d{2}-\d{2}-[\w.-]+$/.test(version);
72
+ }
70
73
  //# sourceMappingURL=utils.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/cli/lib/localDeployment/utils.ts"],
4
- "sourcesContent": ["import { Context } from \"../../../bundler/context.js\";\nimport { logMessage } from \"../../../bundler/log.js\";\nimport { detect } from \"detect-port\";\nimport { chalkStderr } from \"chalk\";\n\nexport async function choosePorts(\n ctx: Context,\n {\n count,\n requestedPorts,\n suggestedPorts,\n startPort,\n }: {\n count: number;\n /** Ports that must mandatorily be used when provided. */\n requestedPorts?: Array<number | null>;\n /** Ports that will be tried preferentially when provided, but are not required. */\n suggestedPorts?: Array<number | null>;\n startPort: number;\n },\n): Promise<Array<number>> {\n const ports: Array<number> = [];\n for (let i = 0; i < count; i++) {\n const requestedPort = requestedPorts?.[i];\n if (requestedPort !== null) {\n const port = await detect(requestedPort);\n if (port !== requestedPort) {\n return ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Requested port ${requestedPort} is not available`,\n });\n }\n ports.push(port);\n } else {\n const suggestedPort = suggestedPorts?.[ports.length] ?? null;\n if (suggestedPort !== null) {\n const port = await detect(suggestedPort);\n if (port === suggestedPort) {\n ports.push(suggestedPort);\n continue;\n }\n }\n\n const portToTry =\n ports.length > 0 ? ports[ports.length - 1] + 1 : startPort;\n const port = await detect(portToTry);\n ports.push(port);\n }\n }\n return ports;\n}\n\nexport async function chooseLocalBackendPorts(\n ctx: Context,\n options?: {\n suggestedPorts?:\n | { cloud?: number | undefined; site?: number | undefined }\n | undefined;\n requestedPorts?:\n | { cloud?: number | undefined; site?: number | undefined }\n | undefined;\n },\n): Promise<{ cloudPort: number; sitePort: number }> {\n const { suggestedPorts, requestedPorts } = options ?? {};\n\n const [cloudPort, sitePort] = await choosePorts(ctx, {\n count: 2,\n startPort: 3210,\n requestedPorts: [\n requestedPorts?.cloud ?? null,\n requestedPorts?.site ?? null,\n ],\n suggestedPorts: [\n suggestedPorts?.cloud ?? null,\n suggestedPorts?.site ?? null,\n ],\n });\n return { cloudPort, sitePort };\n}\n\nexport function printLocalDeploymentWelcomeMessage() {\n logMessage(\n chalkStderr.cyan(\"You're trying out the beta local deployment feature!\"),\n );\n logMessage(\n chalkStderr.cyan(\n \"To learn more, read the docs: https://docs.convex.dev/cli/local-deployments\",\n ),\n );\n}\n\nexport const LOCAL_BACKEND_INSTANCE_SECRET =\n \"4361726e697461732c206c69746572616c6c79206d65616e696e6720226c6974\";\n\n// Backend releases are tagged `precompiled-YYYY-MM-DD-<commit>`, so the dates\n// sort lexicographically.\nexport function releaseDate(version: string): string | null {\n return /^precompiled-(\\d{4}-\\d{2}-\\d{2})-/.exec(version)?.[1] ?? null;\n}\n"],
5
- "mappings": ";AACA,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAE5B,sBAAsB,YACpB,KACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQwB;AACxB,QAAM,QAAuB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,gBAAgB,iBAAiB,CAAC;AACxC,QAAI,kBAAkB,MAAM;AAC1B,YAAM,OAAO,MAAM,OAAO,aAAa;AACvC,UAAI,SAAS,eAAe;AAC1B,eAAO,IAAI,MAAM;AAAA,UACf,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB,kBAAkB,aAAa;AAAA,QACjD,CAAC;AAAA,MACH;AACA,YAAM,KAAK,IAAI;AAAA,IACjB,OAAO;AACL,YAAM,gBAAgB,iBAAiB,MAAM,MAAM,KAAK;AACxD,UAAI,kBAAkB,MAAM;AAC1B,cAAMA,QAAO,MAAM,OAAO,aAAa;AACvC,YAAIA,UAAS,eAAe;AAC1B,gBAAM,KAAK,aAAa;AACxB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,YACJ,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI,IAAI;AACnD,YAAM,OAAO,MAAM,OAAO,SAAS;AACnC,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,sBAAsB,wBACpB,KACA,SAQkD;AAClD,QAAM,EAAE,gBAAgB,eAAe,IAAI,WAAW,CAAC;AAEvD,QAAM,CAAC,WAAW,QAAQ,IAAI,MAAM,YAAY,KAAK;AAAA,IACnD,OAAO;AAAA,IACP,WAAW;AAAA,IACX,gBAAgB;AAAA,MACd,gBAAgB,SAAS;AAAA,MACzB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgB,SAAS;AAAA,MACzB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,EACF,CAAC;AACD,SAAO,EAAE,WAAW,SAAS;AAC/B;AAEO,gBAAS,qCAAqC;AACnD;AAAA,IACE,YAAY,KAAK,sDAAsD;AAAA,EACzE;AACA;AAAA,IACE,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAEO,aAAM,gCACX;AAIK,gBAAS,YAAY,SAAgC;AAC1D,SAAO,oCAAoC,KAAK,OAAO,IAAI,CAAC,KAAK;AACnE;",
4
+ "sourcesContent": ["import { Context } from \"../../../bundler/context.js\";\nimport { logMessage } from \"../../../bundler/log.js\";\nimport { detect } from \"detect-port\";\nimport { chalkStderr } from \"chalk\";\n\nexport async function choosePorts(\n ctx: Context,\n {\n count,\n requestedPorts,\n suggestedPorts,\n startPort,\n }: {\n count: number;\n /** Ports that must mandatorily be used when provided. */\n requestedPorts?: Array<number | null>;\n /** Ports that will be tried preferentially when provided, but are not required. */\n suggestedPorts?: Array<number | null>;\n startPort: number;\n },\n): Promise<Array<number>> {\n const ports: Array<number> = [];\n for (let i = 0; i < count; i++) {\n const requestedPort = requestedPorts?.[i];\n if (requestedPort !== null) {\n const port = await detect(requestedPort);\n if (port !== requestedPort) {\n return ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage: `Requested port ${requestedPort} is not available`,\n });\n }\n ports.push(port);\n } else {\n const suggestedPort = suggestedPorts?.[ports.length] ?? null;\n if (suggestedPort !== null) {\n const port = await detect(suggestedPort);\n if (port === suggestedPort) {\n ports.push(suggestedPort);\n continue;\n }\n }\n\n const portToTry =\n ports.length > 0 ? ports[ports.length - 1] + 1 : startPort;\n const port = await detect(portToTry);\n ports.push(port);\n }\n }\n return ports;\n}\n\nexport async function chooseLocalBackendPorts(\n ctx: Context,\n options?: {\n suggestedPorts?:\n | { cloud?: number | undefined; site?: number | undefined }\n | undefined;\n requestedPorts?:\n | { cloud?: number | undefined; site?: number | undefined }\n | undefined;\n },\n): Promise<{ cloudPort: number; sitePort: number }> {\n const { suggestedPorts, requestedPorts } = options ?? {};\n\n const [cloudPort, sitePort] = await choosePorts(ctx, {\n count: 2,\n startPort: 3210,\n requestedPorts: [\n requestedPorts?.cloud ?? null,\n requestedPorts?.site ?? null,\n ],\n suggestedPorts: [\n suggestedPorts?.cloud ?? null,\n suggestedPorts?.site ?? null,\n ],\n });\n return { cloudPort, sitePort };\n}\n\nexport function printLocalDeploymentWelcomeMessage() {\n logMessage(\n chalkStderr.cyan(\"You're trying out the beta local deployment feature!\"),\n );\n logMessage(\n chalkStderr.cyan(\n \"To learn more, read the docs: https://docs.convex.dev/cli/local-deployments\",\n ),\n );\n}\n\nexport const LOCAL_BACKEND_INSTANCE_SECRET =\n \"4361726e697461732c206c69746572616c6c79206d65616e696e6720226c6974\";\n\n// Backend releases are tagged `precompiled-YYYY-MM-DD-<commit>`, so the dates\n// sort lexicographically.\nexport function releaseDate(version: string): string | null {\n return /^precompiled-(\\d{4}-\\d{2}-\\d{2})-/.exec(version)?.[1] ?? null;\n}\n\nexport function isValidBackendVersion(version: string): boolean {\n return /^precompiled-\\d{4}-\\d{2}-\\d{2}-[\\w.-]+$/.test(version);\n}\n"],
5
+ "mappings": ";AACA,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAE5B,sBAAsB,YACpB,KACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQwB;AACxB,QAAM,QAAuB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,gBAAgB,iBAAiB,CAAC;AACxC,QAAI,kBAAkB,MAAM;AAC1B,YAAM,OAAO,MAAM,OAAO,aAAa;AACvC,UAAI,SAAS,eAAe;AAC1B,eAAO,IAAI,MAAM;AAAA,UACf,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBAAgB,kBAAkB,aAAa;AAAA,QACjD,CAAC;AAAA,MACH;AACA,YAAM,KAAK,IAAI;AAAA,IACjB,OAAO;AACL,YAAM,gBAAgB,iBAAiB,MAAM,MAAM,KAAK;AACxD,UAAI,kBAAkB,MAAM;AAC1B,cAAMA,QAAO,MAAM,OAAO,aAAa;AACvC,YAAIA,UAAS,eAAe;AAC1B,gBAAM,KAAK,aAAa;AACxB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,YACJ,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI,IAAI;AACnD,YAAM,OAAO,MAAM,OAAO,SAAS;AACnC,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,sBAAsB,wBACpB,KACA,SAQkD;AAClD,QAAM,EAAE,gBAAgB,eAAe,IAAI,WAAW,CAAC;AAEvD,QAAM,CAAC,WAAW,QAAQ,IAAI,MAAM,YAAY,KAAK;AAAA,IACnD,OAAO;AAAA,IACP,WAAW;AAAA,IACX,gBAAgB;AAAA,MACd,gBAAgB,SAAS;AAAA,MACzB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgB,SAAS;AAAA,MACzB,gBAAgB,QAAQ;AAAA,IAC1B;AAAA,EACF,CAAC;AACD,SAAO,EAAE,WAAW,SAAS;AAC/B;AAEO,gBAAS,qCAAqC;AACnD;AAAA,IACE,YAAY,KAAK,sDAAsD;AAAA,EACzE;AACA;AAAA,IACE,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAEO,aAAM,gCACX;AAIK,gBAAS,YAAY,SAAgC;AAC1D,SAAO,oCAAoC,KAAK,OAAO,IAAI,CAAC,KAAK;AACnE;AAEO,gBAAS,sBAAsB,SAA0B;AAC9D,SAAO,0CAA0C,KAAK,OAAO;AAC/D;",
6
6
  "names": ["port"]
7
7
  }
@@ -32,16 +32,6 @@ import {
32
32
  modifyGlobalConfig
33
33
  } from "./utils/globalConfig.js";
34
34
  import { updateBigBrainAuthAfterLogin } from "./deploymentSelection.js";
35
- import {
36
- getProfileSelection,
37
- profileConfigPath,
38
- writeProfileCredentials,
39
- readProfileCredentials
40
- } from "./profiles.js";
41
- import {
42
- profileLoginMetadata,
43
- revokeReplacedProfileToken
44
- } from "./profileManagement.js";
45
35
  custom.setHttpOptionsDefaults({
46
36
  timeout: parseInt(process.env.OPENID_CLIENT_TIMEOUT || "10000")
47
37
  });
@@ -208,11 +198,8 @@ export async function performLogin(ctx, {
208
198
  deviceName: deviceNameOverride,
209
199
  anonymousId,
210
200
  vercel,
211
- vercelOverride,
212
- profile: selectedProfile
201
+ vercelOverride
213
202
  } = {}) {
214
- const auth = ctx.bigBrainAuth();
215
- const profile = selectedProfile !== void 0 ? selectedProfile : auth?.kind === "accessToken" && auth.profile !== void 0 ? auth.profile : await getProfileSelection(ctx);
216
203
  loginFlow = loginFlow || "auto";
217
204
  let deviceName = deviceNameOverride ?? "";
218
205
  if (!deviceName && process.platform === "darwin") {
@@ -285,8 +272,6 @@ export async function performLogin(ctx, {
285
272
  printedMessage: null
286
273
  });
287
274
  }
288
- const previous = profile === null ? null : await readProfileCredentials(ctx, profile, true);
289
- const metadata = profile === null ? {} : await profileLoginMetadata(ctx, profile, accessToken, previous);
290
275
  ctx._updateBigBrainAuth({
291
276
  accessToken,
292
277
  kind: "accessToken",
@@ -304,18 +289,8 @@ export async function performLogin(ctx, {
304
289
  const newAccessToken = response.data.accessToken;
305
290
  const globalConfig = { accessToken: newAccessToken };
306
291
  try {
307
- if (profile !== null) {
308
- await writeProfileCredentials(
309
- ctx,
310
- profile.name,
311
- newAccessToken,
312
- metadata,
313
- previous
314
- );
315
- } else {
316
- await modifyGlobalConfig(ctx, globalConfig);
317
- }
318
- const path = profile !== null ? profileConfigPath(profile.name) : globalConfigPath();
292
+ await modifyGlobalConfig(ctx, globalConfig);
293
+ const path = globalConfigPath();
319
294
  logFinishedStep(`Saved credentials to ${formatPathForPrinting(path)}`);
320
295
  } catch (err) {
321
296
  return await ctx.crash({
@@ -325,15 +300,8 @@ export async function performLogin(ctx, {
325
300
  printedMessage: null
326
301
  });
327
302
  }
328
- if (profile !== null) {
329
- await revokeReplacedProfileToken(
330
- ctx,
331
- previous?.accessToken,
332
- newAccessToken
333
- );
334
- }
335
303
  logVerbose(`performLogin: updating big brain auth after login`);
336
- await updateBigBrainAuthAfterLogin(ctx, newAccessToken, profile ?? void 0);
304
+ await updateBigBrainAuthAfterLogin(ctx, newAccessToken);
337
305
  logVerbose(`performLogin: checking opt ins, acceptOptIns: ${acceptOptIns}`);
338
306
  const shouldContinue = await optins(ctx, acceptOptIns ?? false);
339
307
  if (!shouldContinue) {