checkly 9.5.0 → 10.0.0-rc.1

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