checkly 9.5.0 → 10.0.0-prerelease-4f88d60

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 (575) hide show
  1. package/README.md +10 -0
  2. package/bin/launch.cjs +12 -0
  3. package/dist/ai-context/context.js +1 -0
  4. package/dist/ai-context/context.js.map +1 -1
  5. package/dist/ai-context/onboarding-boilerplate/checkly-config-template.ts +2 -2
  6. package/dist/ai-context/public-skills/checkly/SKILL.md +34 -5
  7. package/dist/ai-context/skills-command/references/communicate.md +8 -5
  8. package/dist/ai-context/skills-command/references/configure-api-checks.md +1 -9
  9. package/dist/ai-context/skills-command/references/configure-browser-checks.md +1 -8
  10. package/dist/ai-context/skills-command/references/configure-check-groups.md +1 -0
  11. package/dist/ai-context/skills-command/references/configure-dns-monitors.md +1 -9
  12. package/dist/ai-context/skills-command/references/configure-grpc-monitors.md +24 -0
  13. package/dist/ai-context/skills-command/references/configure-heartbeat-monitors.md +1 -9
  14. package/dist/ai-context/skills-command/references/configure-icmp-monitors.md +1 -9
  15. package/dist/ai-context/skills-command/references/configure-multistep-checks.md +1 -9
  16. package/dist/ai-context/skills-command/references/configure-playwright-checks.md +2 -2
  17. package/dist/ai-context/skills-command/references/configure-supporting-constructs.md +6 -2
  18. package/dist/ai-context/skills-command/references/configure-tcp-monitors.md +1 -10
  19. package/dist/ai-context/skills-command/references/configure-url-monitors.md +1 -10
  20. package/dist/ai-context/skills-command/references/configure.md +17 -5
  21. package/dist/ai-context/skills-command/references/initialize.md +11 -3
  22. package/dist/ai-context/skills-command/references/investigate-checks.md +1 -1
  23. package/dist/auth/api-key.d.ts +24 -0
  24. package/dist/auth/api-key.js +104 -0
  25. package/dist/auth/api-key.js.map +1 -0
  26. package/dist/auth/device-flow.d.ts +58 -0
  27. package/dist/auth/device-flow.js +228 -0
  28. package/dist/auth/device-flow.js.map +1 -0
  29. package/dist/auth/index.d.ts +6 -6
  30. package/dist/auth/index.js +64 -64
  31. package/dist/auth/index.js.map +1 -1
  32. package/dist/commands/account/plan.js +1 -1
  33. package/dist/commands/account/plan.js.map +1 -1
  34. package/dist/commands/account/usage/projections.d.ts +16 -0
  35. package/dist/commands/account/usage/projections.js +40 -0
  36. package/dist/commands/account/usage/projections.js.map +1 -0
  37. package/dist/commands/account/usage/series.d.ts +7 -3
  38. package/dist/commands/account/usage/series.js +30 -87
  39. package/dist/commands/account/usage/series.js.map +1 -1
  40. package/dist/commands/account/usage/summary.d.ts +4 -1
  41. package/dist/commands/account/usage/summary.js +19 -27
  42. package/dist/commands/account/usage/summary.js.map +1 -1
  43. package/dist/commands/account/usage/term.d.ts +15 -0
  44. package/dist/commands/account/usage/term.js +32 -0
  45. package/dist/commands/account/usage/term.js.map +1 -0
  46. package/dist/commands/account/usage/terms.d.ts +4 -0
  47. package/dist/commands/account/usage/terms.js +19 -27
  48. package/dist/commands/account/usage/terms.js.map +1 -1
  49. package/dist/commands/alert-channels/get.js +2 -1
  50. package/dist/commands/alert-channels/get.js.map +1 -1
  51. package/dist/commands/alert-channels/list.js +2 -1
  52. package/dist/commands/alert-channels/list.js.map +1 -1
  53. package/dist/commands/alert-channels/logs.js +2 -1
  54. package/dist/commands/alert-channels/logs.js.map +1 -1
  55. package/dist/commands/api.js +53 -10
  56. package/dist/commands/api.js.map +1 -1
  57. package/dist/commands/assets/download.js +11 -7
  58. package/dist/commands/assets/download.js.map +1 -1
  59. package/dist/commands/assets/list.js +2 -3
  60. package/dist/commands/assets/list.js.map +1 -1
  61. package/dist/commands/authCommand.d.ts +19 -0
  62. package/dist/commands/authCommand.js +118 -12
  63. package/dist/commands/authCommand.js.map +1 -1
  64. package/dist/commands/baseCommand.d.ts +12 -1
  65. package/dist/commands/baseCommand.js +24 -5
  66. package/dist/commands/baseCommand.js.map +1 -1
  67. package/dist/commands/checks/delete.js +1 -1
  68. package/dist/commands/checks/delete.js.map +1 -1
  69. package/dist/commands/checks/run.js +3 -1
  70. package/dist/commands/checks/run.js.map +1 -1
  71. package/dist/commands/debug/parse-project.d.ts +1 -0
  72. package/dist/commands/debug/parse-project.js +5 -2
  73. package/dist/commands/debug/parse-project.js.map +1 -1
  74. package/dist/commands/deploy.d.ts +8 -2
  75. package/dist/commands/deploy.js +735 -274
  76. package/dist/commands/deploy.js.map +1 -1
  77. package/dist/commands/destroy.js +3 -3
  78. package/dist/commands/destroy.js.map +1 -1
  79. package/dist/commands/env/add.js +2 -2
  80. package/dist/commands/env/add.js.map +1 -1
  81. package/dist/commands/env/ls.js +2 -1
  82. package/dist/commands/env/ls.js.map +1 -1
  83. package/dist/commands/env/pull.js +3 -3
  84. package/dist/commands/env/pull.js.map +1 -1
  85. package/dist/commands/env/update.js +2 -2
  86. package/dist/commands/env/update.js.map +1 -1
  87. package/dist/commands/import/apply.js +12 -10
  88. package/dist/commands/import/apply.js.map +1 -1
  89. package/dist/commands/import/cancel.js +4 -4
  90. package/dist/commands/import/cancel.js.map +1 -1
  91. package/dist/commands/import/commit.js +11 -9
  92. package/dist/commands/import/commit.js.map +1 -1
  93. package/dist/commands/import/plan.js +82 -62
  94. package/dist/commands/import/plan.js.map +1 -1
  95. package/dist/commands/init.d.ts +2 -0
  96. package/dist/commands/init.js +62 -41
  97. package/dist/commands/init.js.map +1 -1
  98. package/dist/commands/login.d.ts +38 -2
  99. package/dist/commands/login.js +848 -52
  100. package/dist/commands/login.js.map +1 -1
  101. package/dist/commands/logout.js +10 -4
  102. package/dist/commands/logout.js.map +1 -1
  103. package/dist/commands/pw-test.js +6 -3
  104. package/dist/commands/pw-test.js.map +1 -1
  105. package/dist/commands/rca/get.js +1 -1
  106. package/dist/commands/rca/get.js.map +1 -1
  107. package/dist/commands/rca/run.js +1 -1
  108. package/dist/commands/rca/run.js.map +1 -1
  109. package/dist/commands/skills/index.js +1 -1
  110. package/dist/commands/skills/index.js.map +1 -1
  111. package/dist/commands/skills/install.js +12 -12
  112. package/dist/commands/skills/install.js.map +1 -1
  113. package/dist/commands/switch.d.ts +10 -1
  114. package/dist/commands/switch.js +75 -13
  115. package/dist/commands/switch.js.map +1 -1
  116. package/dist/commands/sync-playwright.js +5 -10
  117. package/dist/commands/sync-playwright.js.map +1 -1
  118. package/dist/commands/test.d.ts +2 -0
  119. package/dist/commands/test.js +58 -29
  120. package/dist/commands/test.js.map +1 -1
  121. package/dist/commands/trigger.d.ts +1 -2
  122. package/dist/commands/trigger.js +27 -20
  123. package/dist/commands/trigger.js.map +1 -1
  124. package/dist/commands/validate.js +1 -0
  125. package/dist/commands/validate.js.map +1 -1
  126. package/dist/commands/whoami.d.ts +8 -0
  127. package/dist/commands/whoami.js +57 -6
  128. package/dist/commands/whoami.js.map +1 -1
  129. package/dist/constants.d.ts +3 -2
  130. package/dist/constants.js +8 -1
  131. package/dist/constants.js.map +1 -1
  132. package/dist/constructs/alert-channel-codegen.d.ts +6 -1
  133. package/dist/constructs/alert-channel-codegen.js +12 -6
  134. package/dist/constructs/alert-channel-codegen.js.map +1 -1
  135. package/dist/constructs/alert-channel-subscription.d.ts +1 -0
  136. package/dist/constructs/alert-channel-subscription.js +4 -0
  137. package/dist/constructs/alert-channel-subscription.js.map +1 -1
  138. package/dist/constructs/alert-channel.js +8 -5
  139. package/dist/constructs/alert-channel.js.map +1 -1
  140. package/dist/constructs/alert-escalation-policy-codegen.d.ts +7 -0
  141. package/dist/constructs/alert-escalation-policy-codegen.js +25 -8
  142. package/dist/constructs/alert-escalation-policy-codegen.js.map +1 -1
  143. package/dist/constructs/api-assertion.d.ts +0 -2
  144. package/dist/constructs/api-assertion.js +0 -4
  145. package/dist/constructs/api-assertion.js.map +1 -1
  146. package/dist/constructs/api-check-bundle.d.ts +4 -124
  147. package/dist/constructs/api-check-codegen.js +1 -1
  148. package/dist/constructs/api-check-codegen.js.map +1 -1
  149. package/dist/constructs/api-check.d.ts +8 -128
  150. package/dist/constructs/api-check.js +15 -17
  151. package/dist/constructs/api-check.js.map +1 -1
  152. package/dist/constructs/api-request-codegen.js +10 -8
  153. package/dist/constructs/api-request-codegen.js.map +1 -1
  154. package/dist/constructs/browser-check-bundle.d.ts +11 -125
  155. package/dist/constructs/browser-check-bundle.js +5 -1
  156. package/dist/constructs/browser-check-bundle.js.map +1 -1
  157. package/dist/constructs/browser-check.d.ts +5 -107
  158. package/dist/constructs/browser-check.js +1 -1
  159. package/dist/constructs/browser-check.js.map +1 -1
  160. package/dist/constructs/check-codegen.d.ts +46 -14
  161. package/dist/constructs/check-codegen.js +118 -34
  162. package/dist/constructs/check-codegen.js.map +1 -1
  163. package/dist/constructs/check-group-codegen.js +23 -9
  164. package/dist/constructs/check-group-codegen.js.map +1 -1
  165. package/dist/constructs/check-group-v1.d.ts +28 -16
  166. package/dist/constructs/check-group-v1.js +48 -17
  167. package/dist/constructs/check-group-v1.js.map +1 -1
  168. package/dist/constructs/check-group-v2.d.ts +2 -21
  169. package/dist/constructs/check-group-v2.js +4 -4
  170. package/dist/constructs/check-group-v2.js.map +1 -1
  171. package/dist/constructs/check.d.ts +68 -220
  172. package/dist/constructs/check.js +106 -37
  173. package/dist/constructs/check.js.map +1 -1
  174. package/dist/constructs/construct-codegen.d.ts +1 -2
  175. package/dist/constructs/construct-diagnostics.d.ts +3 -0
  176. package/dist/constructs/construct-diagnostics.js +11 -0
  177. package/dist/constructs/construct-diagnostics.js.map +1 -1
  178. package/dist/constructs/construct.d.ts +30 -4
  179. package/dist/constructs/construct.js +52 -6
  180. package/dist/constructs/construct.js.map +1 -1
  181. package/dist/constructs/dashboard.js +11 -7
  182. package/dist/constructs/dashboard.js.map +1 -1
  183. package/dist/constructs/dns-monitor.d.ts +4 -100
  184. package/dist/constructs/dns-request-codegen.js +1 -1
  185. package/dist/constructs/dns-request-codegen.js.map +1 -1
  186. package/dist/constructs/email-alert-channel-codegen.js +1 -1
  187. package/dist/constructs/email-alert-channel-codegen.js.map +1 -1
  188. package/dist/constructs/engine.d.ts +4 -2
  189. package/dist/constructs/engine.js +3 -1
  190. package/dist/constructs/engine.js.map +1 -1
  191. package/dist/constructs/frequency-codegen.js +10 -2
  192. package/dist/constructs/frequency-codegen.js.map +1 -1
  193. package/dist/constructs/frequency.d.ts +8 -3
  194. package/dist/constructs/frequency.js +7 -2
  195. package/dist/constructs/frequency.js.map +1 -1
  196. package/dist/constructs/grpc-monitor.d.ts +4 -100
  197. package/dist/constructs/grpc-monitor.js +19 -0
  198. package/dist/constructs/grpc-monitor.js.map +1 -1
  199. package/dist/constructs/grpc-request-codegen.js +13 -4
  200. package/dist/constructs/grpc-request-codegen.js.map +1 -1
  201. package/dist/constructs/grpc-request.d.ts +23 -0
  202. package/dist/constructs/heartbeat-monitor.d.ts +2 -1
  203. package/dist/constructs/heartbeat-monitor.js +2 -2
  204. package/dist/constructs/heartbeat-monitor.js.map +1 -1
  205. package/dist/constructs/icmp-monitor.d.ts +4 -100
  206. package/dist/constructs/icmp-request-codegen.js +1 -1
  207. package/dist/constructs/icmp-request-codegen.js.map +1 -1
  208. package/dist/constructs/incidentio-alert-channel-codegen.d.ts +1 -1
  209. package/dist/constructs/incidentio-alert-channel-codegen.js +14 -8
  210. package/dist/constructs/incidentio-alert-channel-codegen.js.map +1 -1
  211. package/dist/constructs/index.d.ts +1 -1
  212. package/dist/constructs/index.js +1 -1
  213. package/dist/constructs/index.js.map +1 -1
  214. package/dist/constructs/internal/assertion-validation.js +1 -2
  215. package/dist/constructs/internal/assertion-validation.js.map +1 -1
  216. package/dist/constructs/internal/codegen/context.d.ts +73 -6
  217. package/dist/constructs/internal/codegen/context.js +78 -6
  218. package/dist/constructs/internal/codegen/context.js.map +1 -1
  219. package/dist/constructs/internal/codegen/index.d.ts +3 -1
  220. package/dist/constructs/internal/codegen/index.js +2 -1
  221. package/dist/constructs/internal/codegen/index.js.map +1 -1
  222. package/dist/constructs/internal/codegen/render.d.ts +68 -0
  223. package/dist/constructs/internal/codegen/render.js +75 -0
  224. package/dist/constructs/internal/codegen/render.js.map +1 -0
  225. package/dist/constructs/internal/common-diagnostics.d.ts +1 -2
  226. package/dist/constructs/internal/common-diagnostics.js +7 -17
  227. package/dist/constructs/internal/common-diagnostics.js.map +1 -1
  228. package/dist/constructs/internal/declaring-file.d.ts +39 -0
  229. package/dist/constructs/internal/declaring-file.js +139 -0
  230. package/dist/constructs/internal/declaring-file.js.map +1 -0
  231. package/dist/constructs/internal/default-retry-strategy.d.ts +9 -0
  232. package/dist/constructs/internal/default-retry-strategy.js +13 -0
  233. package/dist/constructs/internal/default-retry-strategy.js.map +1 -0
  234. package/dist/constructs/internal/retry-strategy-defaults.d.ts +13 -0
  235. package/dist/constructs/internal/retry-strategy-defaults.js +14 -0
  236. package/dist/constructs/internal/retry-strategy-defaults.js.map +1 -0
  237. package/dist/constructs/key-value-pair-codegen.d.ts +6 -1
  238. package/dist/constructs/key-value-pair-codegen.js +24 -3
  239. package/dist/constructs/key-value-pair-codegen.js.map +1 -1
  240. package/dist/constructs/maintenance-window-codegen.d.ts +7 -0
  241. package/dist/constructs/maintenance-window-codegen.js +36 -19
  242. package/dist/constructs/maintenance-window-codegen.js.map +1 -1
  243. package/dist/constructs/maintenance-window.d.ts +37 -3
  244. package/dist/constructs/maintenance-window.js +34 -1
  245. package/dist/constructs/maintenance-window.js.map +1 -1
  246. package/dist/constructs/monitor.d.ts +6 -119
  247. package/dist/constructs/monitor.js +3 -5
  248. package/dist/constructs/monitor.js.map +1 -1
  249. package/dist/constructs/msteams-alert-channel-codegen.d.ts +1 -1
  250. package/dist/constructs/msteams-alert-channel-codegen.js +8 -18
  251. package/dist/constructs/msteams-alert-channel-codegen.js.map +1 -1
  252. package/dist/constructs/multi-step-check-bundle.d.ts +4 -109
  253. package/dist/constructs/multi-step-check.d.ts +4 -100
  254. package/dist/constructs/opsgenie-alert-channel-codegen.js +1 -1
  255. package/dist/constructs/opsgenie-alert-channel-codegen.js.map +1 -1
  256. package/dist/constructs/pagerduty-alert-channel-codegen.js +1 -1
  257. package/dist/constructs/pagerduty-alert-channel-codegen.js.map +1 -1
  258. package/dist/constructs/phone-call-alert-channel-codegen.js +1 -1
  259. package/dist/constructs/phone-call-alert-channel-codegen.js.map +1 -1
  260. package/dist/constructs/playwright-check-bundle.d.ts +10 -130
  261. package/dist/constructs/playwright-check-bundle.js +3 -3
  262. package/dist/constructs/playwright-check-bundle.js.map +1 -1
  263. package/dist/constructs/playwright-check-codegen.d.ts +85 -0
  264. package/dist/constructs/playwright-check-codegen.js +212 -0
  265. package/dist/constructs/playwright-check-codegen.js.map +1 -0
  266. package/dist/constructs/playwright-check.d.ts +14 -122
  267. package/dist/constructs/playwright-check.js +17 -51
  268. package/dist/constructs/playwright-check.js.map +1 -1
  269. package/dist/constructs/private-location-check-assignment.d.ts +1 -0
  270. package/dist/constructs/private-location-check-assignment.js +4 -0
  271. package/dist/constructs/private-location-check-assignment.js.map +1 -1
  272. package/dist/constructs/private-location-group-assignment.d.ts +1 -0
  273. package/dist/constructs/private-location-group-assignment.js +4 -0
  274. package/dist/constructs/private-location-group-assignment.js.map +1 -1
  275. package/dist/constructs/private-location.js +3 -1
  276. package/dist/constructs/private-location.js.map +1 -1
  277. package/dist/constructs/retry-strategy-codegen.d.ts +7 -1
  278. package/dist/constructs/retry-strategy-codegen.js +41 -32
  279. package/dist/constructs/retry-strategy-codegen.js.map +1 -1
  280. package/dist/constructs/retry-strategy.d.ts +0 -4
  281. package/dist/constructs/retry-strategy.js +5 -8
  282. package/dist/constructs/retry-strategy.js.map +1 -1
  283. package/dist/constructs/session.d.ts +38 -1
  284. package/dist/constructs/session.js +73 -3
  285. package/dist/constructs/session.js.map +1 -1
  286. package/dist/constructs/slack-alert-channel-codegen.js +1 -1
  287. package/dist/constructs/slack-alert-channel-codegen.js.map +1 -1
  288. package/dist/constructs/slack-app-alert-channel-codegen.js +1 -1
  289. package/dist/constructs/slack-app-alert-channel-codegen.js.map +1 -1
  290. package/dist/constructs/sms-alert-channel-codegen.js +1 -1
  291. package/dist/constructs/sms-alert-channel-codegen.js.map +1 -1
  292. package/dist/constructs/ssl-monitor.d.ts +4 -142
  293. package/dist/constructs/ssl-request-codegen.js +4 -3
  294. package/dist/constructs/ssl-request-codegen.js.map +1 -1
  295. package/dist/constructs/status-page-v3-automation-rule-codegen.js +5 -4
  296. package/dist/constructs/status-page-v3-automation-rule-codegen.js.map +1 -1
  297. package/dist/constructs/status-page-v3-codegen.js +3 -2
  298. package/dist/constructs/status-page-v3-codegen.js.map +1 -1
  299. package/dist/constructs/status-page-v3-component-codegen.js +8 -7
  300. package/dist/constructs/status-page-v3-component-codegen.js.map +1 -1
  301. package/dist/constructs/tcp-monitor-codegen.js +3 -2
  302. package/dist/constructs/tcp-monitor-codegen.js.map +1 -1
  303. package/dist/constructs/tcp-monitor.d.ts +8 -103
  304. package/dist/constructs/tcp-monitor.js +3 -2
  305. package/dist/constructs/tcp-monitor.js.map +1 -1
  306. package/dist/constructs/telegram-alert-channel-codegen.d.ts +1 -1
  307. package/dist/constructs/telegram-alert-channel-codegen.js +14 -8
  308. package/dist/constructs/telegram-alert-channel-codegen.js.map +1 -1
  309. package/dist/constructs/time-zone.d.ts +24 -0
  310. package/dist/constructs/time-zone.js +455 -0
  311. package/dist/constructs/time-zone.js.map +1 -0
  312. package/dist/constructs/traceroute-monitor.d.ts +4 -100
  313. package/dist/constructs/traceroute-request-codegen.js +2 -2
  314. package/dist/constructs/traceroute-request-codegen.js.map +1 -1
  315. package/dist/constructs/url-monitor-codegen.js +1 -1
  316. package/dist/constructs/url-monitor-codegen.js.map +1 -1
  317. package/dist/constructs/url-monitor.d.ts +7 -123
  318. package/dist/constructs/url-monitor.js +3 -0
  319. package/dist/constructs/url-monitor.js.map +1 -1
  320. package/dist/constructs/url-request-codegen.js +4 -3
  321. package/dist/constructs/url-request-codegen.js.map +1 -1
  322. package/dist/constructs/webhook-alert-channel-codegen.js +3 -3
  323. package/dist/constructs/webhook-alert-channel-codegen.js.map +1 -1
  324. package/dist/formatters/check-result-detail.js +0 -185
  325. package/dist/formatters/check-result-detail.js.map +1 -1
  326. package/dist/formatters/render.d.ts +2 -1
  327. package/dist/formatters/render.js +2 -2
  328. package/dist/formatters/render.js.map +1 -1
  329. package/dist/formatters/usage.d.ts +18 -12
  330. package/dist/formatters/usage.js +285 -245
  331. package/dist/formatters/usage.js.map +1 -1
  332. package/dist/help/help-extension.js +2 -1
  333. package/dist/help/help-extension.js.map +1 -1
  334. package/dist/helpers/accounts.d.ts +28 -0
  335. package/dist/helpers/accounts.js +50 -0
  336. package/dist/helpers/accounts.js.map +1 -0
  337. package/dist/helpers/activate-account.d.ts +13 -0
  338. package/dist/helpers/activate-account.js +24 -0
  339. package/dist/helpers/activate-account.js.map +1 -0
  340. package/dist/helpers/api-fields.js +3 -2
  341. package/dist/helpers/api-fields.js.map +1 -1
  342. package/dist/helpers/cli-mode.d.ts +24 -0
  343. package/dist/helpers/cli-mode.js +64 -8
  344. package/dist/helpers/cli-mode.js.map +1 -1
  345. package/dist/helpers/command-preview.d.ts +44 -0
  346. package/dist/helpers/command-preview.js +29 -9
  347. package/dist/helpers/command-preview.js.map +1 -1
  348. package/dist/helpers/command-style.d.ts +1 -0
  349. package/dist/helpers/command-style.js +51 -37
  350. package/dist/helpers/command-style.js.map +1 -1
  351. package/dist/helpers/flags.d.ts +10 -0
  352. package/dist/helpers/flags.js +18 -0
  353. package/dist/helpers/flags.js.map +1 -1
  354. package/dist/helpers/import-plan-selection.d.ts +1 -1
  355. package/dist/helpers/import-plan-selection.js +8 -20
  356. package/dist/helpers/import-plan-selection.js.map +1 -1
  357. package/dist/helpers/invalid-input-error.d.ts +15 -0
  358. package/dist/helpers/invalid-input-error.js +17 -0
  359. package/dist/helpers/invalid-input-error.js.map +1 -0
  360. package/dist/helpers/number.js +4 -3
  361. package/dist/helpers/number.js.map +1 -1
  362. package/dist/helpers/onboarding/boilerplate.js +15 -3
  363. package/dist/helpers/onboarding/boilerplate.js.map +1 -1
  364. package/dist/helpers/onboarding/index.d.ts +1 -1
  365. package/dist/helpers/onboarding/index.js +1 -1
  366. package/dist/helpers/onboarding/index.js.map +1 -1
  367. package/dist/helpers/onboarding/messages.d.ts +7 -0
  368. package/dist/helpers/onboarding/messages.js +14 -0
  369. package/dist/helpers/onboarding/messages.js.map +1 -1
  370. package/dist/helpers/onboarding/prompt-display.js +2 -2
  371. package/dist/helpers/onboarding/prompt-display.js.map +1 -1
  372. package/dist/helpers/onboarding/prompts-helpers.js +3 -1
  373. package/dist/helpers/onboarding/prompts-helpers.js.map +1 -1
  374. package/dist/helpers/onboarding/skill-install.js +4 -4
  375. package/dist/helpers/onboarding/skill-install.js.map +1 -1
  376. package/dist/helpers/prompts.d.ts +7 -0
  377. package/dist/helpers/prompts.js +10 -0
  378. package/dist/helpers/prompts.js.map +1 -0
  379. package/dist/helpers/result-assets.js +4 -3
  380. package/dist/helpers/result-assets.js.map +1 -1
  381. package/dist/helpers/test-helper.d.ts +14 -0
  382. package/dist/helpers/test-helper.js +33 -3
  383. package/dist/helpers/test-helper.js.map +1 -1
  384. package/dist/helpers/usage.d.ts +51 -14
  385. package/dist/helpers/usage.js +193 -53
  386. package/dist/helpers/usage.js.map +1 -1
  387. package/dist/helpers/wrap.js +1 -1
  388. package/dist/helpers/wrap.js.map +1 -1
  389. package/dist/messages/common-messages.d.ts +1 -0
  390. package/dist/messages/common-messages.js +4 -2
  391. package/dist/messages/common-messages.js.map +1 -1
  392. package/dist/reporters/abstract-list.d.ts +9 -2
  393. package/dist/reporters/abstract-list.js +105 -32
  394. package/dist/reporters/abstract-list.js.map +1 -1
  395. package/dist/reporters/ci.js +7 -4
  396. package/dist/reporters/ci.js.map +1 -1
  397. package/dist/reporters/dot.d.ts +1 -0
  398. package/dist/reporters/dot.js +19 -7
  399. package/dist/reporters/dot.js.map +1 -1
  400. package/dist/reporters/github.d.ts +2 -0
  401. package/dist/reporters/github.js +29 -4
  402. package/dist/reporters/github.js.map +1 -1
  403. package/dist/reporters/json.d.ts +2 -0
  404. package/dist/reporters/json.js +27 -4
  405. package/dist/reporters/json.js.map +1 -1
  406. package/dist/reporters/list.js +10 -8
  407. package/dist/reporters/list.js.map +1 -1
  408. package/dist/reporters/reporter.d.ts +1 -0
  409. package/dist/reporters/reporter.js.map +1 -1
  410. package/dist/reporters/util.d.ts +5 -2
  411. package/dist/reporters/util.js +25 -61
  412. package/dist/reporters/util.js.map +1 -1
  413. package/dist/rest/analytics.js +0 -4
  414. package/dist/rest/analytics.js.map +1 -1
  415. package/dist/rest/api.d.ts +10 -1
  416. package/dist/rest/api.js +44 -9
  417. package/dist/rest/api.js.map +1 -1
  418. package/dist/rest/check-results.d.ts +1 -50
  419. package/dist/rest/check-results.js.map +1 -1
  420. package/dist/rest/checkly-storage.d.ts +6 -1
  421. package/dist/rest/checkly-storage.js +13 -2
  422. package/dist/rest/checkly-storage.js.map +1 -1
  423. package/dist/rest/errors.d.ts +19 -0
  424. package/dist/rest/errors.js +20 -1
  425. package/dist/rest/errors.js.map +1 -1
  426. package/dist/rest/projects.d.ts +254 -4
  427. package/dist/rest/projects.js +208 -17
  428. package/dist/rest/projects.js.map +1 -1
  429. package/dist/rest/usage.d.ts +164 -73
  430. package/dist/rest/usage.js +21 -10
  431. package/dist/rest/usage.js.map +1 -1
  432. package/dist/services/abstract-check-runner.d.ts +34 -2
  433. package/dist/services/abstract-check-runner.js +183 -56
  434. package/dist/services/abstract-check-runner.js.map +1 -1
  435. package/dist/services/check-parser/bundler.d.ts +26 -0
  436. package/dist/services/check-parser/bundler.js +81 -7
  437. package/dist/services/check-parser/bundler.js.map +1 -1
  438. package/dist/services/check-parser/cache-hash.d.ts +1 -4
  439. package/dist/services/check-parser/cache-hash.js.map +1 -1
  440. package/dist/services/check-parser/package-files/json-text-source-file.d.ts +0 -2
  441. package/dist/services/check-parser/package-files/json-text-source-file.js +6 -6
  442. package/dist/services/check-parser/package-files/json-text-source-file.js.map +1 -1
  443. package/dist/services/check-parser/parser.d.ts +3 -2
  444. package/dist/services/check-parser/parser.js +31 -40
  445. package/dist/services/check-parser/parser.js.map +1 -1
  446. package/dist/services/checkly-config-codegen.js +4 -4
  447. package/dist/services/checkly-config-codegen.js.map +1 -1
  448. package/dist/services/checkly-config-loader.d.ts +18 -33
  449. package/dist/services/checkly-config-loader.js +50 -47
  450. package/dist/services/checkly-config-loader.js.map +1 -1
  451. package/dist/services/config.d.ts +42 -1
  452. package/dist/services/config.js +93 -9
  453. package/dist/services/config.js.map +1 -1
  454. package/dist/services/content-hash.d.ts +14 -0
  455. package/dist/services/content-hash.js +22 -0
  456. package/dist/services/content-hash.js.map +1 -0
  457. package/dist/services/deploy-diff/diff-lines.d.ts +52 -0
  458. package/dist/services/deploy-diff/diff-lines.js +327 -0
  459. package/dist/services/deploy-diff/diff-lines.js.map +1 -0
  460. package/dist/services/deploy-diff/double-check.d.ts +9 -0
  461. package/dist/services/deploy-diff/double-check.js +21 -0
  462. package/dist/services/deploy-diff/double-check.js.map +1 -0
  463. package/dist/services/deploy-diff/import-shape.d.ts +171 -0
  464. package/dist/services/deploy-diff/import-shape.js +633 -0
  465. package/dist/services/deploy-diff/import-shape.js.map +1 -0
  466. package/dist/services/deploy-diff/legacy-payload.d.ts +22 -0
  467. package/dist/services/deploy-diff/legacy-payload.js +51 -0
  468. package/dist/services/deploy-diff/legacy-payload.js.map +1 -0
  469. package/dist/services/deploy-diff/plan-summary.d.ts +81 -0
  470. package/dist/services/deploy-diff/plan-summary.js +262 -0
  471. package/dist/services/deploy-diff/plan-summary.js.map +1 -0
  472. package/dist/services/deploy-diff/preview-output.d.ts +63 -0
  473. package/dist/services/deploy-diff/preview-output.js +403 -0
  474. package/dist/services/deploy-diff/preview-output.js.map +1 -0
  475. package/dist/services/deploy-diff/render.d.ts +59 -0
  476. package/dist/services/deploy-diff/render.js +326 -0
  477. package/dist/services/deploy-diff/render.js.map +1 -0
  478. package/dist/services/deploy-diff/shape-changes.d.ts +2 -0
  479. package/dist/services/deploy-diff/shape-changes.js +48 -0
  480. package/dist/services/deploy-diff/shape-changes.js.map +1 -0
  481. package/dist/services/deploy-diff/source-index.d.ts +18 -0
  482. package/dist/services/deploy-diff/source-index.js +48 -0
  483. package/dist/services/deploy-diff/source-index.js.map +1 -0
  484. package/dist/services/deploy-diff/variable-names.d.ts +30 -0
  485. package/dist/services/deploy-diff/variable-names.js +64 -0
  486. package/dist/services/deploy-diff/variable-names.js.map +1 -0
  487. package/dist/services/engine-resolver.js +3 -0
  488. package/dist/services/engine-resolver.js.map +1 -1
  489. package/dist/services/engine-rules-loader.d.ts +6 -0
  490. package/dist/services/engine-rules-loader.js +12 -3
  491. package/dist/services/engine-rules-loader.js.map +1 -1
  492. package/dist/services/oxc-parser.d.ts +26 -0
  493. package/dist/services/oxc-parser.js +70 -0
  494. package/dist/services/oxc-parser.js.map +1 -0
  495. package/dist/services/project-cli.d.ts +75 -0
  496. package/dist/services/project-cli.js +302 -0
  497. package/dist/services/project-cli.js.map +1 -0
  498. package/dist/services/project-parser.d.ts +5 -0
  499. package/dist/services/project-parser.js +25 -13
  500. package/dist/services/project-parser.js.map +1 -1
  501. package/dist/services/skills.js +3 -3
  502. package/dist/services/skills.js.map +1 -1
  503. package/dist/services/snapshot-service.d.ts +25 -8
  504. package/dist/services/snapshot-service.js +46 -4
  505. package/dist/services/snapshot-service.js.map +1 -1
  506. package/dist/services/socket-client.js +1 -1
  507. package/dist/services/socket-client.js.map +1 -1
  508. package/dist/services/test-filters.d.ts +18 -0
  509. package/dist/services/test-filters.js +10 -0
  510. package/dist/services/test-filters.js.map +1 -1
  511. package/dist/services/test-runner.d.ts +1 -1
  512. package/dist/services/test-runner.js +10 -11
  513. package/dist/services/test-runner.js.map +1 -1
  514. package/dist/services/test-session-cancel.d.ts +5 -0
  515. package/dist/services/test-session-cancel.js +28 -3
  516. package/dist/services/test-session-cancel.js.map +1 -1
  517. package/dist/services/trigger-runner.d.ts +1 -1
  518. package/dist/services/trigger-runner.js +2 -2
  519. package/dist/services/trigger-runner.js.map +1 -1
  520. package/dist/services/two-installs.d.ts +39 -0
  521. package/dist/services/two-installs.js +155 -0
  522. package/dist/services/two-installs.js.map +1 -0
  523. package/dist/services/util.d.ts +17 -2
  524. package/dist/services/util.js +118 -11
  525. package/dist/services/util.js.map +1 -1
  526. package/dist/services/write-back/apply-edits.d.ts +29 -0
  527. package/dist/services/write-back/apply-edits.js +255 -0
  528. package/dist/services/write-back/apply-edits.js.map +1 -0
  529. package/dist/services/write-back/helper-edit.d.ts +135 -0
  530. package/dist/services/write-back/helper-edit.js +343 -0
  531. package/dist/services/write-back/helper-edit.js.map +1 -0
  532. package/dist/services/write-back/imports.d.ts +30 -0
  533. package/dist/services/write-back/imports.js +88 -0
  534. package/dist/services/write-back/imports.js.map +1 -0
  535. package/dist/services/write-back/literal-edit.d.ts +183 -0
  536. package/dist/services/write-back/literal-edit.js +359 -0
  537. package/dist/services/write-back/literal-edit.js.map +1 -0
  538. package/dist/services/write-back/output.d.ts +26 -0
  539. package/dist/services/write-back/output.js +110 -0
  540. package/dist/services/write-back/output.js.map +1 -0
  541. package/dist/services/write-back/plan.d.ts +180 -0
  542. package/dist/services/write-back/plan.js +1046 -0
  543. package/dist/services/write-back/plan.js.map +1 -0
  544. package/dist/services/write-back/source-file.d.ts +133 -0
  545. package/dist/services/write-back/source-file.js +460 -0
  546. package/dist/services/write-back/source-file.js.map +1 -0
  547. package/dist/sourcegen/index.d.ts +1 -1
  548. package/dist/sourcegen/index.js +1 -1
  549. package/dist/sourcegen/index.js.map +1 -1
  550. package/dist/sourcegen/program.d.ts +19 -1
  551. package/dist/sourcegen/program.js +32 -7
  552. package/dist/sourcegen/program.js.map +1 -1
  553. package/dist/testing/clear-git-env.d.ts +1 -0
  554. package/dist/testing/clear-git-env.js +25 -0
  555. package/dist/testing/clear-git-env.js.map +1 -0
  556. package/dist/testing/global-setup.d.ts +1 -0
  557. package/dist/testing/global-setup.js +3 -0
  558. package/dist/testing/global-setup.js.map +1 -1
  559. package/oclif.manifest.json +1113 -884
  560. package/package.json +21 -19
  561. package/dist/commands/rules.d.ts +0 -9
  562. package/dist/commands/rules.js +0 -13
  563. package/dist/commands/rules.js.map +0 -1
  564. package/dist/constructs/agentic-check-codegen.d.ts +0 -22
  565. package/dist/constructs/agentic-check-codegen.js +0 -59
  566. package/dist/constructs/agentic-check-codegen.js.map +0 -1
  567. package/dist/constructs/agentic-check.d.ts +0 -237
  568. package/dist/constructs/agentic-check.js +0 -100
  569. package/dist/constructs/agentic-check.js.map +0 -1
  570. package/dist/services/check-parser/package-files/json-text-source-file-parser.d.ts +0 -7
  571. package/dist/services/check-parser/package-files/json-text-source-file-parser.js +0 -47
  572. package/dist/services/check-parser/package-files/json-text-source-file-parser.js.map +0 -1
  573. package/dist/services/check-parser/package-files/source-file-parser.d.ts +0 -10
  574. package/dist/services/check-parser/package-files/source-file-parser.js +0 -13
  575. package/dist/services/check-parser/package-files/source-file-parser.js.map +0 -1
@@ -5,45 +5,110 @@ import { Flags } from '@oclif/core';
5
5
  import { AuthCommand } from './authCommand.js';
6
6
  import { parseProject } from '../services/project-parser.js';
7
7
  import { loadChecklyConfig, resolveDependencyCacheVersion } from '../services/checkly-config-loader.js';
8
- import { Check, AlertChannelSubscription, AlertChannel, CheckGroup, Dashboard, MaintenanceWindow, PrivateLocation, PrivateLocationCheckAssignment, PrivateLocationGroupAssignment, Session, StatusPage, StatusPageService, StatusPageV3Component, StatusPageV3AutomationRule, } from '../constructs/index.js';
8
+ import { Session } from '../constructs/index.js';
9
9
  import chalk from 'chalk';
10
10
  import { splitConfigFilePath, getGitInformation, getGitRepoRoot } from '../services/util.js';
11
11
  import commonMessages from '../messages/common-messages.js';
12
- import { forceFlag } from '../helpers/flags.js';
13
- import { ProjectDeployCancelledError } from '../rest/projects.js';
14
- import { ConflictError } from '../rest/errors.js';
12
+ import { autoOrWholeNumberFlag, dryRunFlag, forceFlag } from '../helpers/flags.js';
13
+ import { formatPreview, NON_REPORTED_TYPES, PRETTY_RESOURCE_TYPES, ResourceDeployStatus, } from '../services/deploy-diff/preview-output.js';
14
+ import { DEPLOY_PLAN_DISABLED, ProjectDeployCancelledError, ProjectPlanStaleError, ProjectPlanDisabledError, ProjectPreviewNotSupportedError, } from '../rest/projects.js';
15
+ import { ConflictError, ValidationError } from '../rest/errors.js';
16
+ import { stripUnsupportedDeployFields } from '../services/deploy-diff/legacy-payload.js';
17
+ import { driftLines, findDrift, planChangeLines, planHasNoChanges, reducePlanForAgent, unbaselinedResources, } from '../services/deploy-diff/plan-summary.js';
18
+ import { formatWriteBackSkipped, formatWriteBackUpdated } from '../services/write-back/output.js';
19
+ import { applyWriteBack, hasWritableChanges, planWriteBack } from '../services/write-back/plan.js';
15
20
  import { uploadSnapshots } from '../services/snapshot-service.js';
16
21
  import { BrowserCheckBundle } from '../constructs/browser-check-bundle.js';
17
22
  import { Bundler } from '../services/check-parser/bundler.js';
18
- // eslint-disable-next-line no-restricted-syntax
19
- var ResourceDeployStatus;
20
- (function (ResourceDeployStatus) {
21
- ResourceDeployStatus["UPDATE"] = "UPDATE";
22
- ResourceDeployStatus["CREATE"] = "CREATE";
23
- ResourceDeployStatus["DELETE"] = "DELETE";
24
- // Returned by newer backends for resources removed from code that are kept in
25
- // the account (now managed from the Checkly web app) instead of deleted.
26
- ResourceDeployStatus["DETACHED"] = "DETACHED";
27
- })(ResourceDeployStatus || (ResourceDeployStatus = {}));
28
- const PRETTY_RESOURCE_TYPES = {
29
- [Check.__checklyType]: 'Check',
30
- [AlertChannel.__checklyType]: 'AlertChannel',
31
- [CheckGroup.__checklyType]: 'CheckGroup',
32
- [MaintenanceWindow.__checklyType]: 'MaintenanceWindow',
33
- [PrivateLocation.__checklyType]: 'PrivateLocation',
34
- [Dashboard.__checklyType]: 'Dashboard',
35
- [StatusPage.__checklyType]: 'StatusPage',
36
- [StatusPageService.__checklyType]: 'StatusPageService',
37
- [StatusPageV3Component.__checklyType]: 'StatusPageV3Component',
38
- [StatusPageV3AutomationRule.__checklyType]: 'StatusPageV3AutomationRule',
39
- };
40
- // Internal resources that users don't create directly. They are reported as
41
- // part of their owning check, so we exclude them from delete previews/guards.
42
- const NON_REPORTED_TYPES = [
43
- AlertChannelSubscription.__checklyType,
44
- PrivateLocationCheckAssignment.__checklyType,
45
- PrivateLocationGroupAssignment.__checklyType,
46
- ];
23
+ /**
24
+ * The deploy payload fields that arrived with the preview endpoint, which a
25
+ * deploy schema older than it rejects by name (`"sourceFile" is not allowed`).
26
+ * Kept in step with `stripUnsupportedDeployFields`.
27
+ */
28
+ const PREVIEW_ERA_FIELDS = ['sourceFile', 'codeBundleSha256', 'sha256'];
29
+ /**
30
+ * Whether the API refused the payload because of a field an older deploy
31
+ * schema does not know: a 400 whose message names the field as not allowed.
32
+ * Only such a refusal is worth repeating in the older form: any other 400,
33
+ * including a bad value for one of these very fields, describes a problem
34
+ * the older form would not fix, or would hide. A refusal whose body the
35
+ * client recognises arrives as a {@link ValidationError}; one it does not
36
+ * arrives as the raw HTTP error.
37
+ */
38
+ function rejectsPreviewEraField(err) {
39
+ const status = err instanceof ValidationError ? 400 : err?.response?.status ?? err?.data?.statusCode;
40
+ if (status !== 400) {
41
+ return false;
42
+ }
43
+ const message = String(err?.data?.message ?? err?.response?.data?.message ?? err?.message ?? '');
44
+ return message.includes('is not allowed') && PREVIEW_ERA_FIELDS.some(field => message.includes(field));
45
+ }
46
+ /**
47
+ * Thrown when a `--fail-on-drift` run plans again after its plan went stale
48
+ * and the new plan would overwrite changes made in Checkly. Nothing has been
49
+ * written at that point.
50
+ */
51
+ class DriftDetectedError extends Error {
52
+ drift;
53
+ constructor(drift) {
54
+ super('The plan would overwrite changes made in Checkly.');
55
+ this.drift = drift;
56
+ }
57
+ }
58
+ /**
59
+ * The further choice a terminal gets when the plan shows a resource edited
60
+ * outside the project and the code can take the edit: write the account's
61
+ * current values into the code and deploy nothing, so the user reviews the
62
+ * diff and deploys again rather than overwriting the edit. Only the
63
+ * properties the write-back knows the construct's spelling of (a literal,
64
+ * or a helper such as `Frequency.EVERY_5M`) can be written; everything
65
+ * else is listed with its reason.
66
+ */
67
+ function writeBackAlternatives(diff, project, command) {
68
+ if (!hasWritableChanges(diff, project)) {
69
+ return [];
70
+ }
71
+ return [{
72
+ title: 'Update my code with the changes made in Checkly (deploys nothing)',
73
+ run: async () => {
74
+ const cwd = process.cwd();
75
+ const writeBack = await planWriteBack({ diff, project, cwd });
76
+ const skipped = writeBack.skipped.length > 0 ? formatWriteBackSkipped({ writeBack, project, cwd }) : undefined;
77
+ // Sets the outcome apart from the question above it, on stderr.
78
+ command.logToStderr();
79
+ if (writeBack.applied.length === 0) {
80
+ if (skipped !== undefined) {
81
+ command.log(skipped);
82
+ }
83
+ command.log('Nothing in the code could be updated automatically, so nothing was changed.');
84
+ command.log('Nothing was deployed.');
85
+ return;
86
+ }
87
+ try {
88
+ await applyWriteBack(writeBack);
89
+ }
90
+ catch (err) {
91
+ // What has to be edited by hand is true whether or not the write
92
+ // went through, so it is still said, as part of the error report.
93
+ if (skipped !== undefined) {
94
+ command.logToStderr(skipped);
95
+ }
96
+ // The error names the files already rewritten, if any.
97
+ command.style.longError('Could not update your code.', err.message);
98
+ command.logToStderr('Nothing was deployed.');
99
+ command.exit(1);
100
+ }
101
+ // Reported once the files hold it, not as an intention; what could not
102
+ // be written follows what was.
103
+ command.log(formatWriteBackUpdated({ writeBack, project, cwd }));
104
+ if (skipped !== undefined) {
105
+ command.log(skipped);
106
+ }
107
+ command.log('Nothing was deployed.');
108
+ command.logToStderr('Review with `git diff`, then run `npx checkly deploy` again.');
109
+ },
110
+ }];
111
+ }
47
112
  export default class Deploy extends AuthCommand {
48
113
  static coreCommand = true;
49
114
  static hidden = false;
@@ -66,15 +131,61 @@ export default class Deploy extends AuthCommand {
66
131
  default: false,
67
132
  }),
68
133
  'schedule-on-deploy': Flags.boolean({
69
- description: 'Enables automatic check scheduling after a deploy.',
70
- default: true,
134
+ description: 'Schedule checks to run right after the deploy instead of waiting for their next scheduled run. '
135
+ + 'The other --schedule-on-deploy-* flags refine which checks it schedules.',
136
+ default: false,
71
137
  allowNo: true,
72
138
  }),
139
+ 'schedule-on-deploy-scope': Flags.string({
140
+ description: 'Which checks to schedule after the deploy: "changed" schedules the checks the deploy created or '
141
+ + 'updated, "all" also the ones it left unchanged.',
142
+ options: ['changed', 'all'],
143
+ default: 'changed',
144
+ }),
145
+ 'schedule-on-deploy-threshold': autoOrWholeNumberFlag({
146
+ description: 'Schedule no checks after the deploy if it would schedule more than this many, not counting the '
147
+ + 'ones --schedule-on-deploy-scope and --schedule-on-deploy-min-frequency leave out. '
148
+ + '"auto" leaves the threshold to Checkly, which also caps the number you can set.',
149
+ default: 'auto',
150
+ }),
151
+ 'schedule-on-deploy-min-frequency': autoOrWholeNumberFlag({
152
+ description: 'After the deploy, schedule only the checks that run every this many minutes or less often; '
153
+ + 'more frequent checks wait for their next scheduled run. Goes by the frequency Checkly resolves for each '
154
+ + 'check, its defaults included. 0 schedules every check. "auto" leaves the minimum to Checkly, which also '
155
+ + 'caps the number you can set.',
156
+ default: 'auto',
157
+ }),
73
158
  'preserve-resources': Flags.boolean({
74
159
  description: 'Keep resources removed from code (and their run history) in your Checkly account instead of deleting them.',
75
160
  default: false,
76
161
  }),
77
162
  'force': forceFlag(),
163
+ 'dry-run': dryRunFlag(),
164
+ 'plan': Flags.boolean({
165
+ description: 'Plan the deploy first: Checkly says what would change, property by property, compared '
166
+ + 'with what is deployed. The deploy then applies that plan, leaves unchanged resources alone and '
167
+ + 'refuses to run if your Checkly account changed in between. --no-plan deploys without a plan '
168
+ + 'and writes every resource.',
169
+ default: true,
170
+ allowNo: true,
171
+ }),
172
+ 'plan-token': Flags.string({
173
+ description: 'Deploy only if the plan still matches this token from an earlier run. '
174
+ + 'Aborts if anything changed in your Checkly account since then. Not available with --no-plan.',
175
+ }),
176
+ 'prune-unmanaged-relations': Flags.boolean({
177
+ description: 'Delete the alert channel subscriptions and private location assignments on this project\'s '
178
+ + 'checks and groups that the project does not manage. Not available with --no-plan.',
179
+ default: false,
180
+ }),
181
+ 'fail-on-drift': Flags.boolean({
182
+ description: 'Deploy nothing and fail if any resource of this project was changed or deleted in Checkly '
183
+ + 'since the last planned deploy, which this deploy would overwrite. Ignored with --dry-run, whose output '
184
+ + 'reports those changes. Not available with --no-plan.',
185
+ default: false,
186
+ allowNo: true,
187
+ env: 'CHECKLY_FAIL_ON_DRIFT',
188
+ }),
78
189
  'cancel-in-progress-deployment': Flags.boolean({
79
190
  description: 'If a deployment for this project is already in progress, cancel it instead of waiting for it to finish.',
80
191
  default: false,
@@ -102,33 +213,53 @@ export default class Deploy extends AuthCommand {
102
213
  };
103
214
  async run() {
104
215
  const { flags, metadata } = await this.parse(Deploy);
105
- const { force, preview, 'cancel-in-progress-deployment': cancelInProgress, 'schedule-on-deploy': scheduleOnDeploy, 'preserve-resources': preserveResources, output: outputFlag, verbose, config: configFilename, 'verify-runtime-dependencies': verifyRuntimeDependencies, 'debug-bundle': debugBundle, 'debug-bundle-output-file': debugBundleOutputFile, } = flags;
216
+ const { force, preview, 'dry-run': dryRun,
217
+ // Named for what it is, the user's request: `plan` further down is the
218
+ // plan Checkly answered with, which a run that asked may still not have.
219
+ 'plan': planRequested, 'plan-token': requestedPlanToken, 'prune-unmanaged-relations': pruneRelations, 'fail-on-drift': failOnDriftFlag, 'cancel-in-progress-deployment': cancelInProgress, 'schedule-on-deploy': scheduleOnDeploy, 'schedule-on-deploy-scope': scheduleScope, 'schedule-on-deploy-threshold': scheduleThreshold, 'schedule-on-deploy-min-frequency': scheduleMinFrequency, 'preserve-resources': preserveResources, output: outputFlag, verbose, config: configFilename, 'verify-runtime-dependencies': verifyRuntimeDependencies, 'debug-bundle': debugBundle, 'debug-bundle-output-file': debugBundleOutputFile, } = flags;
106
220
  const output = outputFlag || verbose;
221
+ // A dry run deploys nothing, and its envelope is read by scripts the same
222
+ // way whatever the plan holds, the changes made in Checkly included (each
223
+ // change carries its origin). So the drift check, which a pipeline may
224
+ // switch on for every step with CHECKLY_FAIL_ON_DRIFT, leaves it alone.
225
+ const failOnDrift = failOnDriftFlag && !dryRun;
226
+ // All are promises only a plan can keep: the token, that nothing is
227
+ // deployed unless the account still is what was reviewed; pruning, that
228
+ // what gets deleted was listed first; and the drift check, which reads
229
+ // what was changed in Checkly off the plan. Checked here rather than with
230
+ // the flags' `dependsOn`, which a flag that has a default always satisfies.
231
+ if (!planRequested) {
232
+ const needsPlan = requestedPlanToken !== undefined
233
+ ? '--plan-token'
234
+ : pruneRelations
235
+ ? '--prune-unmanaged-relations'
236
+ : failOnDrift ? '--fail-on-drift' : undefined;
237
+ if (needsPlan !== undefined) {
238
+ this.style.longError(`${needsPlan} applies to a planned deploy only.`, `Re-run without --no-plan, or without ${needsPlan}.`);
239
+ this.exit(2);
240
+ }
241
+ }
242
+ // The flags that refine scheduling mean nothing on a deploy that schedules
243
+ // no checks. Passing one is taken as having expected scheduling, which is
244
+ // opt-in, so the deploy stops rather than quietly scheduling nothing.
245
+ if (!scheduleOnDeploy) {
246
+ const refiningFlag = [
247
+ 'schedule-on-deploy-scope',
248
+ 'schedule-on-deploy-threshold',
249
+ 'schedule-on-deploy-min-frequency',
250
+ ].find(name => metadata.flags[name]?.setFromDefault !== true);
251
+ if (refiningFlag !== undefined) {
252
+ this.style.longError(`--${refiningFlag} applies only when checks are scheduled after the deploy.`, `Add --schedule-on-deploy to schedule them, or drop --${refiningFlag}.`);
253
+ this.exit(2);
254
+ }
255
+ }
107
256
  const { configDirectory, configFilenames } = splitConfigFilePath(configFilename);
108
257
  const { config: checklyConfig, constructs: checklyConfigConstructs, diagnostics: configDiagnostics, } = await loadChecklyConfig(configDirectory, configFilenames);
109
258
  const account = this.account;
110
- if (!preview) {
111
- await this.confirmOrAbort({
112
- command: 'deploy',
113
- description: 'Deploy project to Checkly',
114
- changes: [
115
- `Deploy project "${checklyConfig.projectName}" to account "${account.name}"`,
116
- scheduleOnDeploy
117
- ? 'Schedule checks after deploy'
118
- : 'Checks will NOT be scheduled after deploy',
119
- preserveResources
120
- ? 'Keep any resources removed from code (and their run history) in your Checkly account, where you can manage them from the Checkly web app'
121
- : 'Delete any resources removed from code, losing their run history. Pass --preserve-resources to keep them in your Checkly account instead',
122
- ],
123
- flags,
124
- flagMetadata: metadata.flags,
125
- classification: {
126
- readOnly: Deploy.readOnly,
127
- destructive: Deploy.destructive,
128
- idempotent: Deploy.idempotent,
129
- },
130
- }, { force });
131
- }
259
+ // The confirmation happens further down, once the project has been parsed
260
+ // and Checkly has said what the deploy would change, so that one prompt
261
+ // can show the actual plan instead of asking about a deploy nobody has
262
+ // seen yet.
132
263
  this.style.actionStart('Parsing your project');
133
264
  const availableRuntimes = await api.runtimes.getAll();
134
265
  const project = await parseProject({
@@ -153,6 +284,7 @@ export default class Deploy extends AuthCommand {
153
284
  include: checklyConfig.checks?.include,
154
285
  embeddedPackages: checklyConfig.bundle?.packages?.embed,
155
286
  playwrightChecks: checklyConfig.checks?.playwrightChecks,
287
+ warnOnMissingCheckLocations: true,
156
288
  });
157
289
  const repoInfo = getGitInformation(project.repoUrl);
158
290
  const repoRoot = getGitRepoRoot();
@@ -179,48 +311,65 @@ export default class Deploy extends AuthCommand {
179
311
  })();
180
312
  const archive = await bundler.finalize();
181
313
  bundler.updateMarker(archive.archiveFile);
182
- // The remote code bundle is only consumed by Playwright check suites (via
183
- // bundler.marker). If nothing registered files to bundle (e.g. a project of
184
- // only uptime monitors), there is nothing to upload — skip the store() to
185
- // avoid an unnecessary code-bundle upload.
186
- if (!bundler.isEmpty) {
187
- this.style.actionStart('Uploading Playwright tests');
188
- try {
189
- const storedArchive = await archive.store();
190
- bundler.updateMarker(storedArchive.key);
191
- this.style.actionSuccess();
192
- }
193
- catch (err) {
194
- this.style.actionFailure();
195
- throw err;
196
- }
197
- }
198
- const bundledChecksByType = {
199
- browser: [],
200
- };
201
- for (const [logicalId, { bundle }] of Object.entries(projectBundle.data.check)) {
202
- if (bundle instanceof BrowserCheckBundle) {
203
- bundledChecksByType.browser.push(logicalId);
314
+ const browserBundles = Object.values(projectBundle.data.check)
315
+ .map(({ bundle }) => bundle)
316
+ .filter((bundle) => bundle instanceof BrowserCheckBundle);
317
+ // Uploading is what produces the storage keys a deploy needs, and it is
318
+ // deferred until the deploy has been confirmed: a preview describes the
319
+ // code bundle and every snapshot by content hash, and a dry-run deploy
320
+ // accepts a payload without keys, so finding out what a deploy would
321
+ // change costs no uploads. `uploaded` tells `deployPayload` whether the
322
+ // snapshots carry keys yet.
323
+ let uploaded = false;
324
+ const uploadArtifacts = async () => {
325
+ uploaded = true;
326
+ // The remote code bundle is only consumed by Playwright check suites (via
327
+ // bundler.marker). If nothing registered files to bundle (e.g. a project of
328
+ // only uptime monitors), there is nothing to upload — skip the store() to
329
+ // avoid an unnecessary code-bundle upload.
330
+ if (!bundler.isEmpty) {
331
+ this.style.actionStart('Uploading Playwright tests');
332
+ try {
333
+ const storedArchive = await archive.store();
334
+ bundler.updateMarker(storedArchive.key);
335
+ this.style.actionSuccess();
336
+ }
337
+ catch (err) {
338
+ this.style.actionFailure();
339
+ throw err;
340
+ }
204
341
  }
205
- }
206
- if (!preview && bundledChecksByType.browser.length) {
207
- this.style.actionStart('Uploading Playwright snapshots');
208
- try {
209
- for (const logicalId of bundledChecksByType.browser) {
210
- const bundle = projectBundle.data.check[logicalId].bundle;
211
- bundle.snapshots = await uploadSnapshots(bundle.rawSnapshots);
342
+ // The step is only announced when there is a snapshot to upload. Every
343
+ // browser check gets its snapshot list either way, an empty one included.
344
+ if (browserBundles.some(bundle => bundle.rawSnapshots?.length)) {
345
+ this.style.actionStart('Uploading Playwright snapshots');
346
+ try {
347
+ for (const bundle of browserBundles) {
348
+ bundle.snapshots = await uploadSnapshots(bundle.rawSnapshots);
349
+ }
350
+ this.style.actionSuccess();
351
+ }
352
+ catch (err) {
353
+ this.style.actionFailure();
354
+ throw err;
212
355
  }
213
- this.style.actionSuccess();
214
356
  }
215
- catch (err) {
216
- this.style.actionFailure();
217
- throw err;
357
+ else {
358
+ for (const bundle of browserBundles) {
359
+ bundle.snapshots = [];
360
+ }
218
361
  }
219
- }
220
- const projectPayload = projectBundle.synthesize({ repoRoot });
362
+ };
363
+ // Synthesized on demand rather than once: the snapshot entries are copied
364
+ // into the payload as values, so the payload sent after the upload has to
365
+ // be built after it to carry the keys.
366
+ const synthesize = () => ({ ...projectBundle.synthesize({ repoRoot }), repoInfo });
367
+ const projectPayload = synthesize();
221
368
  if (!projectPayload.resources.length) {
222
369
  if (preview) {
223
- this.log('\nNo checks were detected. More information on how to set up a Checkly CLI project is available at https://checklyhq.com/docs/cli/.\n');
370
+ // The empty preview is the result; where to read up is advice.
371
+ this.log('No checks were detected.');
372
+ this.logToStderr('More information on how to set up a Checkly CLI project is available at https://checklyhq.com/docs/cli/.');
224
373
  return;
225
374
  }
226
375
  else {
@@ -233,16 +382,176 @@ export default class Deploy extends AuthCommand {
233
382
  this.log(`Successfully wrote debug bundle to "${debugBundleOutputFile}".`);
234
383
  return;
235
384
  }
236
- // Preflight destructive-delete guard. Deletions are only known from the diff,
237
- // which we don't have until after a deploy call. For a non-preview, non-preserve
238
- // run that isn't already forced, do a dry-run first to surface resources that
239
- // would be permanently deleted and require an explicit confirmation.
240
- if (!preview && !preserveResources && !force) {
241
- let deletions = [];
385
+ const summaryOptions = { prettyTypes: PRETTY_RESOURCE_TYPES, foldedTypes: NON_REPORTED_TYPES };
386
+ // The refusal of a run whose plan would overwrite changes made in Checkly.
387
+ // The way forward depends on which side should win, so both are named.
388
+ const reportDrift = (drift, title) => this.style.longError(title, [
389
+ ...driftLines(drift, summaryOptions).map(line => `- ${line}`),
390
+ '',
391
+ 'To keep these changes, add them to your code: `checkly deploy --no-fail-on-drift` in a terminal '
392
+ + 'shows them and can update the code for many of them. To discard them, deploy with --no-fail-on-drift.',
393
+ ].join('\n'));
394
+ const resourceCount = (count) => count === 1 ? '1 resource' : `${count} resources`;
395
+ const classification = {
396
+ readOnly: Deploy.readOnly,
397
+ destructive: Deploy.destructive,
398
+ idempotent: Deploy.idempotent,
399
+ };
400
+ // Ask Checkly what this payload would change. Writes nothing, and needs no
401
+ // upload: the payload describes the code bundle and every snapshot by
402
+ // content hash.
403
+ // `full` buys each changed resource's current state, which is what the
404
+ // rendered construct diff (`--preview`, `--output`, the plan an interactive
405
+ // run shows before asking) and the machine-readable envelope (`--dry-run`,
406
+ // the `confirmation_required` an agent or CI run prints) show. Only a
407
+ // forced deploy prints nothing of the kind, so only it skips paying for
408
+ // the state.
409
+ const detail = dryRun || preview || output || !force ? 'full' : 'changes';
410
+ let plan;
411
+ // Set when the API has no preview endpoint, which also means it rejects the
412
+ // payload fields that arrived with it.
413
+ let previewNotSupported = false;
414
+ // With --no-plan the deploy applies to whatever the account looks like
415
+ // when it runs: no plan, no token, and nothing to render before the prompt.
416
+ if (planRequested) {
417
+ this.style.actionStart('Checking what would change');
418
+ try {
419
+ plan = await api.projects.preview(projectPayload, {
420
+ detail,
421
+ preserveResources,
422
+ pruneRelations,
423
+ onStatus: message => this.style.actionStatus(message),
424
+ });
425
+ this.style.actionSuccess();
426
+ }
427
+ catch (err) {
428
+ this.style.actionFailure();
429
+ // An API that has plans switched off is not one that predates them: it
430
+ // knows the payload fields that arrived with the preview endpoint, so
431
+ // the payload is not stripped of them.
432
+ const plansDisabled = err instanceof ProjectPlanDisabledError;
433
+ previewNotSupported = err instanceof ProjectPreviewNotSupportedError;
434
+ const previewSupported = !previewNotSupported && !plansDisabled;
435
+ // --prune-unmanaged-relations deletes data, and without a plan nothing
436
+ // can say what: an API that predates the preview endpoint would not
437
+ // prune at all, and an API that would prune cannot be asked what it is
438
+ // about to delete. Both are refused rather than silently downgraded.
439
+ if (pruneRelations) {
440
+ this.style.longError(previewSupported
441
+ ? 'Could not check which relations --prune-unmanaged-relations would delete, so nothing was deployed.'
442
+ : plansDisabled
443
+ ? 'Checkly has deploy plans switched off at the moment, so relations cannot be pruned.'
444
+ : 'This Checkly API cannot prune unmanaged relations yet.', previewSupported ? 'Try again in a moment.' : 'Re-run without --prune-unmanaged-relations.');
445
+ this.exit(1);
446
+ }
447
+ // A deploy pinned to a plan cannot proceed without knowing the plan.
448
+ if (requestedPlanToken !== undefined) {
449
+ this.style.longError('Could not check the plan this deploy is pinned to.', previewSupported
450
+ ? err.message
451
+ : plansDisabled
452
+ ? 'Checkly has deploy plans switched off at the moment; re-run without --plan-token.'
453
+ : 'This Checkly API does not support deploy previews; re-run without --plan-token.');
454
+ this.exit(1);
455
+ }
456
+ // Without a plan nothing says what was changed in Checkly, and a run
457
+ // that asked to be stopped by such changes must not overwrite them
458
+ // unchecked.
459
+ if (failOnDrift) {
460
+ this.style.longError(previewSupported
461
+ ? `Could not check whether anything was changed in Checkly, so nothing was deployed: ${err.message}`
462
+ : plansDisabled
463
+ ? 'Checkly has deploy plans switched off at the moment, '
464
+ + 'so changes made in Checkly cannot be detected and nothing was deployed.'
465
+ : 'This Checkly API cannot detect changes made in Checkly, so nothing was deployed.', previewSupported ? 'Try again in a moment.' : 'Re-run without --fail-on-drift.');
466
+ this.exit(1);
467
+ }
468
+ // Deploying without a reviewed plan beats not deploying at all: one
469
+ // resource Checkly cannot read, or an API that is a version behind, must
470
+ // not make a project undeployable. The run falls back to the coarser
471
+ // dry-run diff and its delete guard, and sends no plan token.
472
+ this.style.longWarning(previewSupported
473
+ // Say which failure it was: the user is about to get a coarser answer
474
+ // than a planned deploy gives and deserves to know why.
475
+ ? `Could not check what this deploy would change: ${err.message}`
476
+ : plansDisabled
477
+ ? err.message
478
+ // A 404 from this path means the endpoint is not there; whether that
479
+ // is an API predating it or something else in the way, the CLI cannot
480
+ // tell, so it says what it observed.
481
+ : 'This Checkly API answered 404 for the deploy preview endpoint.', 'Falling back to a summary of created, updated and deleted resources.');
482
+ }
483
+ if (plan !== undefined && requestedPlanToken !== undefined && requestedPlanToken !== plan.planToken) {
484
+ this.style.longError('Your Checkly account no longer matches the plan this deploy is pinned to, so nothing was deployed.', 'Re-run `checkly deploy --preview` to see the current plan.');
485
+ this.exit(1);
486
+ }
487
+ }
488
+ // The payload goes out in the form the deploy route accepted before the
489
+ // preview endpoint existed in exactly the two cases where the full one
490
+ // cannot be sent: an API that does not have the endpoint rejects the fields
491
+ // that arrived with it, and a run that skipped the uploads describes
492
+ // snapshots it has no storage key for, which every write route requires.
493
+ //
494
+ // Not for every missing plan: a preview that failed transiently against a
495
+ // current API leaves the fields perfectly acceptable, and stripping them
496
+ // would blank the stored content hashes — making the NEXT deploy report
497
+ // every Playwright suite and every snapshot-bearing check as changed.
498
+ const deployPayload = () => previewNotSupported || !uploaded ? stripUnsupportedDeployFields(synthesize()) : synthesize();
499
+ // A planned run learns from the preview call whether the API has the
500
+ // endpoint, and with it whether the deploy route knows the fields that
501
+ // arrived with it. A run without a plan — one started with --no-plan, or
502
+ // a preview that failed for a reason other than a missing endpoint — does not, so a
503
+ // payload the route refuses because of one of those fields is sent once
504
+ // more in the older form; the rest of the run then sends that form too.
505
+ // If the older form is refused as well, the refusal of the payload the
506
+ // user asked for is the one that surfaces; any other failure of the
507
+ // second attempt is its own and keeps its own handling. The older form
508
+ // blanks the stored content hashes, which the next planned deploy reports
509
+ // as a change to every Playwright check suite and every check with
510
+ // snapshots — once, and worth a warning once a deploy has gone out that
511
+ // way.
512
+ let sentLegacyPayload = false;
513
+ const deployOrRetryLegacy = async (options) => {
514
+ try {
515
+ return await api.projects.deploy(deployPayload(), options);
516
+ }
517
+ catch (err) {
518
+ if (plan !== undefined || previewNotSupported || !rejectsPreviewEraField(err)) {
519
+ throw err;
520
+ }
521
+ previewNotSupported = true;
522
+ try {
523
+ const result = await api.projects.deploy(deployPayload(), options);
524
+ sentLegacyPayload = true;
525
+ return result;
526
+ }
527
+ catch (retryErr) {
528
+ if (retryErr instanceof ValidationError) {
529
+ throw err;
530
+ }
531
+ throw retryErr;
532
+ }
533
+ }
534
+ };
535
+ // Without a plan, deletions are only visible in a dry-run deploy. It runs
536
+ // before the uploads, like the preview: a run that is about to ask for
537
+ // confirmation, or that only reports, must not leave a code bundle and
538
+ // snapshots in storage for an answer that may be no. The dry run validates
539
+ // the payload it is given, in which a code bundle it has not been handed a
540
+ // key for is described by its path on disk and snapshots without a key are
541
+ // left out, as it was before the preview endpoint existed. A storage key
542
+ // the deploy route refuses is therefore caught by the deploy itself, after
543
+ // the uploads, not by this dry run.
544
+ let fallbackDiff;
545
+ if (plan === undefined && (preview || dryRun || (!preserveResources && !force))) {
242
546
  this.style.actionStart('Verifying deployed state');
243
547
  try {
244
- const { data: dryRunData } = await api.projects.deploy({ ...projectPayload, repoInfo }, { dryRun: true, scheduleOnDeploy, preserveResources });
245
- deletions = this.collectDeletions(dryRunData);
548
+ const { data } = await deployOrRetryLegacy({
549
+ dryRun: true,
550
+ scheduleOnDeploy,
551
+ preserveResources,
552
+ onStatus: message => this.style.actionStatus(message),
553
+ });
554
+ fallbackDiff = data;
246
555
  this.style.actionSuccess();
247
556
  }
248
557
  catch (err) {
@@ -250,65 +559,315 @@ export default class Deploy extends AuthCommand {
250
559
  this.style.longError(`Your project could not be deployed.`, err);
251
560
  this.exit(1);
252
561
  }
253
- if (deletions.length) {
254
- this.log(chalk.bold.red('The following resources were removed from code and will be DELETED, losing their run history:'));
255
- for (const { resourceType, logicalId } of deletions) {
256
- this.log(chalk.red(` ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`));
562
+ }
563
+ // The plan as `--preview` prints it, which is also what a terminal sees
564
+ // before it is asked. Without a plan, the dry run's findings are listed.
565
+ const renderPlan = (planToken) => formatPreview({
566
+ heading: { title: 'Deploy preview', projectName: project.name, accountName: account.name },
567
+ diff: plan?.diff ?? fallbackDiff?.diff ?? [],
568
+ project,
569
+ verbose,
570
+ pruneRelations,
571
+ rendering: plan !== undefined ? { plan: plan.diff, local: projectPayload.resources } : undefined,
572
+ planToken,
573
+ });
574
+ // Checked before anything is asked or uploaded, in every mode but a dry
575
+ // run (see `failOnDrift`): a run that asked to be stopped by changes made
576
+ // in Checkly is stopped whoever runs it. A plan exists here, since a run with --fail-on-drift that could not
577
+ // get one has already failed. `--preview --fail-on-drift` doubles as a
578
+ // drift check that deploys nothing: it prints the plan, then fails.
579
+ if (failOnDrift && plan !== undefined) {
580
+ const drift = findDrift(plan.diff, summaryOptions);
581
+ if (drift.length > 0) {
582
+ if (preview) {
583
+ this.log(renderPlan());
257
584
  }
258
- this.log(chalk.yellow('\nPass --preserve-resources to keep them (and their run history) in your Checkly account instead.\n'));
259
- await this.confirmOrAbort({
260
- command: 'deploy',
261
- description: 'Delete resources removed from code',
262
- changes: [
263
- `Permanently delete ${deletions.length} resource(s) removed from code, losing their run history`,
264
- ...deletions.map(({ resourceType, logicalId }) => `Delete ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`),
265
- ],
266
- flags,
267
- flagMetadata: metadata.flags,
268
- classification: {
269
- readOnly: Deploy.readOnly,
270
- destructive: Deploy.destructive,
271
- idempotent: Deploy.idempotent,
272
- },
273
- }, { force });
585
+ reportDrift(drift, preview
586
+ ? `This deploy would overwrite changes made in Checkly to ${resourceCount(drift.length)}.`
587
+ : `This deploy would overwrite changes made in Checkly to ${resourceCount(drift.length)}, `
588
+ + 'so nothing was deployed.');
589
+ this.exit(1);
590
+ }
591
+ const unbaselined = unbaselinedResources(plan.diff, summaryOptions);
592
+ if (unbaselined > 0) {
593
+ this.style.longWarning(`${resourceCount(unbaselined)} could not be checked for changes made in Checkly.`, 'Checkly has no earlier planned deploy of them to compare with. '
594
+ + 'A planned deploy records one, so later deploys can check them.');
274
595
  }
275
596
  }
597
+ if (preview && !dryRun) {
598
+ this.log(renderPlan(plan?.planToken));
599
+ return;
600
+ }
601
+ // Resources removed from code that the deploy deletes, as the plan or,
602
+ // without one, the dry run found them. Undefined when neither ran (a
603
+ // forced run without a plan), so nothing is known about them.
604
+ const knownDiff = plan?.diff ?? fallbackDiff?.diff;
605
+ const deletions = knownDiff !== undefined ? collectDeletions(knownDiff) : undefined;
606
+ // What the deploy does as a whole, worded as the confirmation prompt words
607
+ // it. The plan's own lines follow these. The scheduling threshold is left
608
+ // out: few deploys reach it, and the deploy reports it when one does.
609
+ const optionLines = [
610
+ `Deploy project "${checklyConfig.projectName}" to account "${account.name}"`,
611
+ scheduleOnDeploy
612
+ ? `Schedule ${scheduleScope} checks after deploy`
613
+ : 'Leave checks to run at their next scheduled time. '
614
+ + 'Pass --schedule-on-deploy to schedule them right after the deploy instead',
615
+ // Saying resources will be deleted when none are reads as a warning
616
+ // about data loss that is not going to happen.
617
+ ...preserveResources
618
+ ? ['Keep any resources removed from code (and their run history) in your Checkly account, where you can manage them from the Checkly web app']
619
+ : deletions === undefined || deletions.length > 0
620
+ ? ['Delete any resources removed from code, losing their run history. Pass --preserve-resources to keep them in your Checkly account instead']
621
+ : [],
622
+ ...pruneRelations
623
+ ? ['Delete the alert channel subscriptions and private location assignments on this project\'s checks '
624
+ + 'and groups that the project does not manage']
625
+ : [],
626
+ ];
627
+ // With a plan, every touched resource has a line; without one, only the
628
+ // deletions the dry run found are known.
629
+ const planLines = plan !== undefined
630
+ ? planChangeLines(plan.diff, summaryOptions)
631
+ : (deletions ?? [])
632
+ .map(({ resourceType, logicalId }) => `Permanently delete ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}, `
633
+ + 'losing its run history');
634
+ // A plan with nothing to write has nothing to approve, so nobody is asked:
635
+ // a terminal, an agent and a forced run all read that there are no changes
636
+ // and carry on. The deploy is still sent, pinned to the plan's token like
637
+ // any other. It writes no resource, but it records the deployment, renews
638
+ // the deployed state the next plan compares against (which is what clears
639
+ // the mark of a resource edited outside code once the code agrees with
640
+ // it), and schedules the checks unless told not to. --dry-run keeps its
641
+ // envelope, which a script reads the same way whatever the plan holds.
642
+ const nothingToApply = plan !== undefined && planHasNoChanges(plan.diff, summaryOptions);
643
+ if (nothingToApply && !dryRun) {
644
+ this.log(renderPlan());
645
+ }
646
+ else {
647
+ // The one confirmation of the command: the plan is known by now, so the
648
+ // prompt, the agent envelope and --dry-run all describe the deploy that is
649
+ // about to run rather than a deploy nobody has seen. A terminal gets the
650
+ // plan rendered as `--preview` prints it, with the options under it; the
651
+ // plan lines are not repeated there since the overview names every
652
+ // resource. No plan-token footer: this run pins the token itself.
653
+ await this.confirmOrAbort({
654
+ command: 'deploy',
655
+ description: 'Deploy project to Checkly',
656
+ changes: [...optionLines, ...planLines],
657
+ ...plan !== undefined
658
+ ? {
659
+ terminal: {
660
+ plan: renderPlan,
661
+ changes: optionLines,
662
+ alternatives: writeBackAlternatives(plan.diff, project, this),
663
+ },
664
+ question: 'Apply these changes?',
665
+ }
666
+ : {},
667
+ // The token rides along in the echoed command, so the confirming run
668
+ // deploys the plan that was shown here and refuses a different one.
669
+ flags: plan !== undefined ? { ...flags, 'plan-token': plan.planToken } : flags,
670
+ flagMetadata: metadata.flags,
671
+ classification,
672
+ ...plan !== undefined
673
+ ? { preview: { planToken: plan.planToken, diff: reducePlanForAgent(plan.diff) } }
674
+ : {},
675
+ }, { force, dryRun });
676
+ }
677
+ await uploadArtifacts();
678
+ const runDeploy = () => deployOrRetryLegacy({
679
+ scheduleOnDeploy,
680
+ scheduleOnDeployScope: scheduleScope,
681
+ scheduleOnDeployThreshold: scheduleThreshold === 'auto' ? undefined : scheduleThreshold,
682
+ scheduleOnDeployMinFrequency: scheduleMinFrequency === 'auto' ? undefined : scheduleMinFrequency,
683
+ preserveResources,
684
+ pruneRelations,
685
+ // Read when the deploy is sent, so that a run which planned again after
686
+ // a stale plan is covered. Only a run that holds a plan asks the deploy
687
+ // to apply one: without it the deploy writes every resource, as it did
688
+ // before plans existed.
689
+ plan: plan !== undefined,
690
+ planToken: plan?.planToken,
691
+ cancelInProgress,
692
+ onProgress: progress => this.style.actionStatus(`${progress}% complete`),
693
+ onStatus: message => this.style.actionStatus(message),
694
+ });
276
695
  try {
277
- if (!preview) {
278
- this.style.actionStart('Deploying project');
279
- }
280
- const { data } = await api.projects.deploy({ ...projectPayload, repoInfo }, {
281
- dryRun: preview,
282
- scheduleOnDeploy,
283
- preserveResources,
284
- cancelInProgress,
285
- onProgress: preview ? undefined : progress => this.style.actionStatus(`${progress}% complete`),
286
- onStatus: preview ? undefined : message => this.style.actionStatus(message),
287
- });
288
- if (!preview) {
289
- this.style.actionSuccess();
696
+ // "Deploying" right under "No changes" would read as a contradiction.
697
+ this.style.actionStart(nothingToApply ? 'Recording the deployment' : 'Deploying project');
698
+ let data;
699
+ try {
700
+ ({ data } = await runDeploy());
701
+ }
702
+ catch (err) {
703
+ // A run that showed nobody a plan has nothing to protect: rather than
704
+ // failing a pipeline because someone touched the account while the code
705
+ // bundle was uploading, it plans again and deploys that. A pinned run,
706
+ // or one a person confirmed, is refused instead — see the catch below.
707
+ // A run without a plan sent no token, so it cannot be told its plan
708
+ // went stale; the guard keeps it out of the re-plan regardless.
709
+ if (!(err instanceof ProjectPlanStaleError) || !force || requestedPlanToken !== undefined || !planRequested) {
710
+ throw err;
711
+ }
712
+ this.style.actionStatus('Your Checkly account changed; checking again and deploying the current plan');
713
+ plan = await api.projects.preview(deployPayload(), { detail, preserveResources, pruneRelations });
714
+ // An account that moved under the deploy most likely moved because
715
+ // someone changed it in Checkly, which is what the drift check is for.
716
+ const drift = failOnDrift ? findDrift(plan.diff, summaryOptions) : [];
717
+ if (drift.length > 0) {
718
+ throw new DriftDetectedError(drift);
719
+ }
720
+ ;
721
+ ({ data } = await runDeploy());
722
+ }
723
+ this.style.actionSuccess();
724
+ if (sentLegacyPayload) {
725
+ this.style.longWarning('This Checkly API does not know the fields the preview endpoint added, so the deploy was sent without them.', 'The next planned deploy reports every Playwright check suite and every check with snapshots as changed.');
726
+ }
727
+ // Judged on what the deploy answered, not on the plan it started from: a
728
+ // forced run that planned again after the account moved may have written
729
+ // something after all.
730
+ const wroteNothing = plan !== undefined && planHasNoChanges(data.diff, summaryOptions);
731
+ if (output && !wroteNothing) {
732
+ // The deploy response names every resource with its id; the plan the
733
+ // deploy was confirmed against is where each one's deployed state is.
734
+ // No heading: the success line that follows names the project and account.
735
+ this.log(formatPreview({
736
+ done: true,
737
+ diff: data.diff,
738
+ project,
739
+ verbose,
740
+ pruneRelations,
741
+ rendering: plan !== undefined ? { plan: plan.diff, local: projectPayload.resources } : undefined,
742
+ }));
290
743
  }
291
- if (preview || output) {
292
- this.log(this.formatPreview(data, project, verbose));
744
+ await setTimeout(500);
745
+ // The checks Checkly marked with whether it scheduled them; none from an
746
+ // API that predates the marks.
747
+ const markedChecks = data.diff.filter(entry => entry.scheduled !== undefined);
748
+ if (wroteNothing) {
749
+ // Scheduling is the one thing such a deploy visibly does, so it is said.
750
+ // Without marks it is counted on what was sent and can be scheduled: a
751
+ // deploy that changed nothing schedules only under the `all` scope, a
752
+ // testOnly check is in the project but not in the deploy, and a
753
+ // heartbeat monitor waits for pings instead of running.
754
+ const heartbeats = project.getHeartbeatLogicalIds();
755
+ const scheduled = markedChecks.length > 0
756
+ ? markedChecks.some(entry => entry.scheduled)
757
+ : (data.scheduled ?? scheduleOnDeploy)
758
+ && scheduleScope === 'all'
759
+ && Object.keys(projectBundle.data.check).some(logicalId => !heartbeats.includes(logicalId));
760
+ this.log(`Project "${project.name}" is up to date.${scheduled ? ' Checks were scheduled to run.' : ''}`);
293
761
  }
294
- if (!preview) {
295
- await setTimeout(500);
762
+ else {
296
763
  this.log(`Successfully deployed project "${project.name}" to account "${account.name}".`);
297
- // Print the ping URL for heartbeat checks.
298
- const heartbeatLogicalIds = project.getHeartbeatLogicalIds();
299
- const heartbeatCheckIds = data.diff.filter(check => heartbeatLogicalIds.includes(check.logicalId))
300
- .map(check => check?.physicalId);
301
- heartbeatCheckIds.forEach(async (id) => {
302
- const { data: { pingUrl, name } } = await api.heartbeatCheck.get(id);
764
+ }
765
+ // Checkly schedules none of a deploy's checks when there are more than
766
+ // the threshold allows, which is worth a warning. Otherwise it may still
767
+ // leave some out, such as the ones that already run often; a notice
768
+ // lists those, kept short so that it does not push people into relaxing
769
+ // the limits. A deploy that asked for no scheduling hears about neither,
770
+ // and the checks a deploy left unchanged are what the scope leaves out
771
+ // by design, not something to report.
772
+ const notScheduled = scheduleOnDeploy
773
+ ? markedChecks.filter(entry => entry.scheduled === false
774
+ && entry.scheduleSkippedReason !== 'NOT_REQUESTED'
775
+ && entry.scheduleSkippedReason !== 'UNCHANGED')
776
+ : [];
777
+ if (scheduleOnDeploy && data.scheduled === false) {
778
+ // Set apart from the line above, which ends without one. The blank
779
+ // line belongs to the warning, so it goes to stderr with it.
780
+ this.logToStderr();
781
+ this.style.longWarning('Checks were not scheduled', [
782
+ 'This deploy would have scheduled more checks than the scheduling threshold allows.',
783
+ '',
784
+ 'All checks will run at their next scheduled time.',
785
+ '',
786
+ 'Helpful options:',
787
+ ...scheduleScope === 'all'
788
+ ? [
789
+ ' --schedule-on-deploy-scope=changed',
790
+ ' Only schedule the checks the deploy changed.',
791
+ '',
792
+ ]
793
+ : [],
794
+ ' --schedule-on-deploy-threshold=<number>',
795
+ ' Change the threshold to a suitable number of checks.',
796
+ '',
797
+ ' --schedule-on-deploy-min-frequency=<number>',
798
+ ' Only schedule checks that run less often than the specified frequency',
799
+ ' (in minutes), reducing the number of scheduled checks.',
800
+ '',
801
+ ' For a full list of options, pass --help.',
802
+ ].join('\n'));
803
+ }
804
+ else if (notScheduled.length > 0) {
805
+ this.logToStderr();
806
+ this.style.longInfo('Some checks were not scheduled', [
807
+ 'The checks will run at their next scheduled time.',
808
+ '',
809
+ 'Not scheduled:',
810
+ ...Object.entries(countBy(notScheduled, entry => entry.scheduleSkippedReason ?? 'UNKNOWN'))
811
+ .map(([reason, count]) => `- ${describeNotScheduled(reason, count)}`),
812
+ ].join('\n'));
813
+ }
814
+ // Print the ping URL for heartbeat checks. Awaited, so that the lines are
815
+ // part of the run's output. The deploy has been written by now: a failed
816
+ // fetch is a warning about that one check, never a failed deploy.
817
+ const heartbeatLogicalIds = project.getHeartbeatLogicalIds();
818
+ const heartbeatChecks = data.diff.filter(check => heartbeatLogicalIds.includes(check.logicalId));
819
+ for (const { logicalId, physicalId } of heartbeatChecks) {
820
+ try {
821
+ const { data: { pingUrl, name } } = await api.heartbeatCheck.get(physicalId);
303
822
  this.log(`Ping URL of heartbeat check ${chalk.green(name)} is ${chalk.italic.underline.blue(pingUrl)}.`);
304
- });
823
+ }
824
+ catch (err) {
825
+ this.style.longWarning(`The project was deployed, but the ping URL of heartbeat check "${logicalId}" could not be retrieved.`, err);
826
+ }
305
827
  }
306
828
  }
307
829
  catch (err) {
308
- if (!preview) {
309
- this.style.actionFailure();
830
+ this.style.actionFailure();
831
+ if (err instanceof DriftDetectedError) {
832
+ reportDrift(err.drift, 'Your Checkly account changed while this deploy was being prepared, and the current '
833
+ + `plan would overwrite changes made in Checkly to ${resourceCount(err.drift.length)}, `
834
+ + 'so nothing was deployed.');
310
835
  }
311
- if (err instanceof ProjectDeployCancelledError) {
836
+ else if (err instanceof ProjectPlanStaleError) {
837
+ // Nothing was written. The way out is another run, which previews
838
+ // afresh: this run's token describes a state Checkly has left behind,
839
+ // so sending it again would be refused again.
840
+ if (err.diff.length) {
841
+ // The current plan is the failure report's context: stderr.
842
+ this.logToStderr(formatPreview({
843
+ heading: { title: 'Current plan', projectName: project.name, accountName: account.name },
844
+ diff: err.diff,
845
+ project,
846
+ verbose,
847
+ pruneRelations,
848
+ }));
849
+ this.style.longError(
850
+ // A plan with nothing to apply was confirmed by nobody.
851
+ nothingToApply
852
+ ? 'Your Checkly account changed after the plan found no changes, so nothing was deployed.'
853
+ : 'Your Checkly account changed while this deploy was being confirmed, so nothing was deployed.', 'The plan above is the current one. Re-run `checkly deploy` to review and deploy it.');
854
+ }
855
+ else {
856
+ // A refusal with no plan attached: the account moved for a reason the
857
+ // error itself explains, and there is nothing to print above.
858
+ this.style.longError(`${err.message} Nothing was deployed.`, 'Re-run `checkly deploy` to see the current plan and deploy it.');
859
+ }
860
+ }
861
+ else if (plan !== undefined && err instanceof ValidationError && err.data.code === DEPLOY_PLAN_DISABLED) {
862
+ // The plan was made, and plans were switched off before the deploy
863
+ // that applies it arrived. The refusal is worded for whoever sent a
864
+ // token; this run pinned its own.
865
+ this.style.longError('Checkly switched deploy plans off while this deploy was being prepared, so nothing was deployed.', requestedPlanToken !== undefined || pruneRelations
866
+ ? 'Re-run the command without --plan-token and --prune-unmanaged-relations. '
867
+ + 'While plans are switched off it deploys without one.'
868
+ : 'Re-run the command. While plans are switched off it deploys without one.');
869
+ }
870
+ else if (err instanceof ProjectDeployCancelledError) {
312
871
  this.style.longError('Your deployment was cancelled.', err.message);
313
872
  }
314
873
  else if (err instanceof ConflictError) {
@@ -323,132 +882,34 @@ export default class Deploy extends AuthCommand {
323
882
  this.exit(1);
324
883
  }
325
884
  }
326
- collectDeletions(previewData) {
327
- return (previewData?.diff ?? [])
328
- .filter(change => change.action === ResourceDeployStatus.DELETE
329
- && !NON_REPORTED_TYPES.some(t => t === change.type))
330
- .map(({ type, logicalId }) => ({ resourceType: type, logicalId }))
331
- .sort((a, b) => a.resourceType.localeCompare(b.resourceType) || a.logicalId.localeCompare(b.logicalId));
885
+ }
886
+ function collectDeletions(diff) {
887
+ return diff
888
+ .filter(change => change.action === ResourceDeployStatus.DELETE
889
+ // A resource the project no longer declares, not a relation it never
890
+ // managed: pruning those is reported under its own heading.
891
+ && change.origin !== 'unmanaged'
892
+ && !NON_REPORTED_TYPES.some(t => t === change.type))
893
+ .map(({ type, logicalId }) => ({ resourceType: type, logicalId }))
894
+ .sort((a, b) => a.resourceType.localeCompare(b.resourceType) || a.logicalId.localeCompare(b.logicalId));
895
+ }
896
+ function countBy(items, key) {
897
+ const counts = {};
898
+ for (const item of items) {
899
+ counts[key(item)] = (counts[key(item)] ?? 0) + 1;
332
900
  }
333
- formatPreview(previewData, project, verbose = false) {
334
- // Current format of the data is: { checks: { logical-id-1: 'UPDATE' }, groups: { another-logical-id: 'CREATE' } }
335
- // We convert it into update: [{ logicalId, resourceType, construct }, ...], create: [], delete: []
336
- // This makes it easier to display.
337
- const updating = [];
338
- const creating = [];
339
- const deleting = [];
340
- const detaching = [];
341
- for (const change of previewData?.diff ?? []) {
342
- const { type, logicalId, physicalId, action } = change;
343
- if ([
344
- AlertChannelSubscription.__checklyType,
345
- PrivateLocationCheckAssignment.__checklyType,
346
- PrivateLocationGroupAssignment.__checklyType,
347
- ].some(t => t === type)) {
348
- // Don't report changes to alert channel subscriptions or private location assignments.
349
- // Users don't create these directly, so it's more intuitive to consider it as part of the check.
350
- continue;
351
- }
352
- const construct = project.data[type][logicalId];
353
- if (action === ResourceDeployStatus.UPDATE) {
354
- updating.push({ resourceType: type, logicalId, physicalId, construct });
355
- }
356
- else if (action === ResourceDeployStatus.CREATE) {
357
- creating.push({ resourceType: type, logicalId, physicalId, construct });
358
- }
359
- else if (action === ResourceDeployStatus.DELETE) {
360
- // Since the resource is being deleted, the construct isn't in the project.
361
- deleting.push({ resourceType: type, logicalId });
362
- }
363
- else if (action === ResourceDeployStatus.DETACHED) {
364
- // Newer backends report detached resources explicitly. The construct
365
- // isn't in the project since it was removed from code.
366
- detaching.push({ resourceType: type, logicalId });
367
- }
368
- }
369
- // testOnly checks weren't sent to the BE and won't be in previewData.
370
- // We load them from the `project` instead.
371
- const skipping = project
372
- .getTestOnlyConstructs().map(construct => ({
373
- logicalId: construct.logicalId,
374
- resourceType: construct.type,
375
- construct,
376
- }))
377
- // There is an edge case when the check already exists in Checkly, but `testOnly: true` was just added.
378
- // In this case, the check will be included in both `deleting` and `skipping`.
379
- // To avoid displaying the check twice, we detect this case and only show the check in `deleting`.
380
- // This implementation is O(n^2), but could be sped up with a map or set.
381
- .filter(skip => !deleting.find(deletion => deletion.logicalId === skip.logicalId && deletion.resourceType === skip.resourceType));
382
- // Having some order will make the output easier to read.
383
- const compareEntries = (a, b) => a.resourceType.localeCompare(b.resourceType)
384
- || a.logicalId.localeCompare(b.logicalId);
385
- // filter resources without contructs that are created dynamically
386
- // on the flight (i.e. a non project member private-location)
387
- const sortedUpdating = updating
388
- .filter(({ construct }) => Boolean(construct))
389
- .sort(compareEntries);
390
- // filter resources without contructs that are created dynamically
391
- // on the flight (i.e. a non project member private-location)
392
- const sortedCreating = creating
393
- .filter(({ construct }) => Boolean(construct))
394
- .sort(compareEntries);
395
- const sortedDeleting = deleting
396
- .sort(compareEntries);
397
- const sortedDetaching = detaching
398
- .sort(compareEntries);
399
- if (!sortedCreating.length && !sortedDeleting.length && !sortedDetaching.length
400
- && !sortedUpdating.length && !skipping.length) {
401
- return '\nNo checks were detected. More information on how to set up a Checkly CLI project is available at https://checklyhq.com/docs/cli/.\n';
402
- }
403
- const output = [];
404
- if (sortedCreating.filter(({ construct }) => Boolean(construct)).length) {
405
- output.push(chalk.bold.green('Create:'));
406
- for (const { logicalId, physicalId, construct } of sortedCreating) {
407
- output.push(` ${construct.constructor.name}: ${logicalId}`);
408
- if (verbose && construct.name) {
409
- output.push(` name: ${construct.name}`);
410
- }
411
- if (verbose && physicalId) {
412
- output.push(` id: ${physicalId}`);
413
- }
414
- }
415
- output.push('');
416
- }
417
- if (sortedDeleting.length) {
418
- output.push(chalk.bold.red('Delete:'));
419
- for (const { resourceType, logicalId } of sortedDeleting) {
420
- output.push(` ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`);
421
- }
422
- output.push('');
423
- }
424
- if (sortedDetaching.length) {
425
- output.push(chalk.bold.yellow('Kept in your Checkly account (removed from code, now managed from the Checkly web app):'));
426
- for (const { resourceType, logicalId } of sortedDetaching) {
427
- output.push(` ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`);
428
- }
429
- output.push('');
430
- }
431
- if (sortedUpdating.length) {
432
- output.push(chalk.bold.magenta('Update and Unchanged:'));
433
- for (const { logicalId, physicalId, construct } of sortedUpdating) {
434
- output.push(` ${construct.constructor.name}: ${logicalId}`);
435
- if (verbose && construct.name) {
436
- output.push(` name: ${construct.name}`);
437
- }
438
- if (verbose && physicalId) {
439
- output.push(` id: ${physicalId}`);
440
- }
441
- }
442
- output.push('');
443
- }
444
- if (skipping.length) {
445
- output.push(chalk.bold.grey('Skip (testOnly):'));
446
- for (const { logicalId, construct } of skipping) {
447
- output.push(` ${construct.constructor.name}: ${logicalId}`);
448
- }
449
- output.push('');
450
- }
451
- return output.join('\n');
901
+ return counts;
902
+ }
903
+ /** One line of the not-scheduled notice: how many checks a skip reason left out. */
904
+ function describeNotScheduled(reason, count) {
905
+ const checks = count === 1 ? '1 check' : `${count} checks`;
906
+ switch (reason) {
907
+ case 'MIN_FREQUENCY':
908
+ return `${checks} that ${count === 1 ? 'runs' : 'run'} more often than the minimum frequency allows`;
909
+ default:
910
+ // A reason this CLI does not know yet, from a newer Checkly API: named
911
+ // as the API reports it.
912
+ return `${checks} for another reason (${reason})`;
452
913
  }
453
914
  }
454
915
  //# sourceMappingURL=deploy.js.map