@tappify/cli 1.3.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/README.md +106 -31
  2. package/dist/src/app.module.js +6 -2
  3. package/dist/src/app.module.js.map +1 -1
  4. package/dist/src/core/api/api.service.d.ts +28 -2
  5. package/dist/src/core/api/api.service.js +151 -27
  6. package/dist/src/core/api/api.service.js.map +1 -1
  7. package/dist/src/core/auth/auth.service.d.ts +2 -0
  8. package/dist/src/core/auth/auth.service.js +31 -3
  9. package/dist/src/core/auth/auth.service.js.map +1 -1
  10. package/dist/src/core/config/config.service.d.ts +11 -2
  11. package/dist/src/core/config/config.service.js +13 -3
  12. package/dist/src/core/config/config.service.js.map +1 -1
  13. package/dist/src/core/core.module.js +33 -2
  14. package/dist/src/core/core.module.js.map +1 -1
  15. package/dist/src/core/environment/environment.service.d.ts +24 -0
  16. package/dist/src/core/environment/environment.service.js +93 -0
  17. package/dist/src/core/environment/environment.service.js.map +1 -0
  18. package/dist/src/core/environment/index.d.ts +1 -0
  19. package/dist/src/core/environment/index.js +18 -0
  20. package/dist/src/core/environment/index.js.map +1 -0
  21. package/dist/src/core/image/image-size.d.ts +5 -0
  22. package/dist/src/core/image/image-size.js +37 -0
  23. package/dist/src/core/image/image-size.js.map +1 -0
  24. package/dist/src/core/image/index.d.ts +1 -0
  25. package/dist/src/{domains/init/runners → core/image}/index.js +1 -1
  26. package/dist/src/core/image/index.js.map +1 -0
  27. package/dist/src/core/index.d.ts +10 -0
  28. package/dist/src/core/index.js +10 -0
  29. package/dist/src/core/index.js.map +1 -1
  30. package/dist/src/core/link/project-link.service.d.ts +13 -0
  31. package/dist/src/{domains/init/runners/init.runner.js → core/link/project-link.service.js} +54 -44
  32. package/dist/src/core/link/project-link.service.js.map +1 -0
  33. package/dist/src/core/manifest/index.d.ts +2 -0
  34. package/dist/src/core/manifest/index.js +19 -0
  35. package/dist/src/core/manifest/index.js.map +1 -0
  36. package/dist/src/core/manifest/manifest.service.d.ts +14 -0
  37. package/dist/src/core/manifest/manifest.service.js +141 -0
  38. package/dist/src/core/manifest/manifest.service.js.map +1 -0
  39. package/dist/src/core/manifest/scope-rules.d.ts +8 -0
  40. package/dist/src/core/manifest/scope-rules.js +95 -0
  41. package/dist/src/core/manifest/scope-rules.js.map +1 -0
  42. package/dist/src/core/output/colors.d.ts +6 -0
  43. package/dist/src/core/output/colors.js +27 -0
  44. package/dist/src/core/output/colors.js.map +1 -0
  45. package/dist/src/core/output/index.d.ts +2 -0
  46. package/dist/src/core/output/index.js +19 -0
  47. package/dist/src/core/output/index.js.map +1 -0
  48. package/dist/src/core/output/output.service.d.ts +37 -0
  49. package/dist/src/core/output/output.service.js +119 -0
  50. package/dist/src/core/output/output.service.js.map +1 -0
  51. package/dist/src/core/project/index.d.ts +1 -0
  52. package/dist/src/core/project/index.js +18 -0
  53. package/dist/src/core/project/index.js.map +1 -0
  54. package/dist/src/core/project/package-manager.d.ts +6 -0
  55. package/dist/src/core/project/package-manager.js +47 -0
  56. package/dist/src/core/project/package-manager.js.map +1 -0
  57. package/dist/src/core/prompt/prompt.service.d.ts +17 -0
  58. package/dist/src/core/prompt/prompt.service.js +299 -0
  59. package/dist/src/core/prompt/prompt.service.js.map +1 -0
  60. package/dist/src/core/registry/index.d.ts +1 -0
  61. package/dist/src/core/registry/index.js +18 -0
  62. package/dist/src/core/registry/index.js.map +1 -0
  63. package/dist/src/core/registry/registry.service.d.ts +55 -0
  64. package/dist/src/core/registry/registry.service.js +119 -0
  65. package/dist/src/core/registry/registry.service.js.map +1 -0
  66. package/dist/src/core/shell/shell.service.d.ts +10 -5
  67. package/dist/src/core/shell/shell.service.js +109 -15
  68. package/dist/src/core/shell/shell.service.js.map +1 -1
  69. package/dist/src/core/shutdown/teardown.d.ts +3 -0
  70. package/dist/src/core/shutdown/teardown.js +22 -0
  71. package/dist/src/core/shutdown/teardown.js.map +1 -0
  72. package/dist/src/core/templates/index.d.ts +1 -0
  73. package/dist/src/core/templates/index.js +18 -0
  74. package/dist/src/core/templates/index.js.map +1 -0
  75. package/dist/src/core/templates/template.service.d.ts +10 -0
  76. package/dist/src/core/templates/template.service.js +91 -0
  77. package/dist/src/core/templates/template.service.js.map +1 -0
  78. package/dist/src/core/update/update-notifier.d.ts +6 -0
  79. package/dist/src/core/update/update-notifier.js +90 -28
  80. package/dist/src/core/update/update-notifier.js.map +1 -1
  81. package/dist/src/core/version.d.ts +5 -0
  82. package/dist/src/core/version.js +21 -0
  83. package/dist/src/core/version.js.map +1 -0
  84. package/dist/src/domains/auth/runners/login.runner.js +5 -1
  85. package/dist/src/domains/auth/runners/login.runner.js.map +1 -1
  86. package/dist/src/domains/auth/runners/logout.runner.js +1 -1
  87. package/dist/src/domains/auth/runners/logout.runner.js.map +1 -1
  88. package/dist/src/domains/deployments/runners/configure.runner.d.ts +6 -1
  89. package/dist/src/domains/deployments/runners/configure.runner.js +231 -200
  90. package/dist/src/domains/deployments/runners/configure.runner.js.map +1 -1
  91. package/dist/src/domains/environment/environment.module.d.ts +2 -0
  92. package/dist/src/domains/environment/environment.module.js +25 -0
  93. package/dist/src/domains/environment/environment.module.js.map +1 -0
  94. package/dist/src/domains/environment/index.d.ts +2 -0
  95. package/dist/src/domains/{deploy → environment}/index.js +1 -1
  96. package/dist/src/domains/environment/index.js.map +1 -0
  97. package/dist/src/domains/environment/runners/environment-current.runner.d.ts +7 -0
  98. package/dist/src/domains/environment/runners/environment-current.runner.js +35 -0
  99. package/dist/src/domains/environment/runners/environment-current.runner.js.map +1 -0
  100. package/dist/src/domains/environment/runners/environment-switcher.service.d.ts +11 -0
  101. package/dist/src/domains/environment/runners/environment-switcher.service.js +74 -0
  102. package/dist/src/domains/environment/runners/environment-switcher.service.js.map +1 -0
  103. package/dist/src/domains/environment/runners/environment-use.runner.d.ts +11 -0
  104. package/dist/src/domains/environment/runners/environment-use.runner.js +46 -0
  105. package/dist/src/domains/environment/runners/environment-use.runner.js.map +1 -0
  106. package/dist/src/domains/environment/runners/environment.command.d.ts +12 -0
  107. package/dist/src/domains/environment/runners/environment.command.js +53 -0
  108. package/dist/src/domains/environment/runners/environment.command.js.map +1 -0
  109. package/dist/src/domains/environment/runners/index.d.ts +4 -0
  110. package/dist/src/domains/environment/runners/index.js +21 -0
  111. package/dist/src/domains/environment/runners/index.js.map +1 -0
  112. package/dist/src/domains/extension/containers.d.ts +13 -0
  113. package/dist/src/domains/extension/containers.js +239 -0
  114. package/dist/src/domains/extension/containers.js.map +1 -0
  115. package/dist/src/domains/extension/extension.module.d.ts +2 -0
  116. package/dist/src/domains/extension/extension.module.js +66 -0
  117. package/dist/src/domains/extension/extension.module.js.map +1 -0
  118. package/dist/src/domains/extension/index.d.ts +1 -0
  119. package/dist/src/domains/extension/index.js +18 -0
  120. package/dist/src/domains/extension/index.js.map +1 -0
  121. package/dist/src/domains/extension/kinds.assistant.d.ts +2 -0
  122. package/dist/src/domains/extension/kinds.assistant.js +296 -0
  123. package/dist/src/domains/extension/kinds.assistant.js.map +1 -0
  124. package/dist/src/domains/extension/kinds.d.ts +8 -0
  125. package/dist/src/domains/extension/kinds.js +42 -0
  126. package/dist/src/domains/extension/kinds.js.map +1 -0
  127. package/dist/src/domains/extension/kinds.server.d.ts +3 -0
  128. package/dist/src/domains/extension/kinds.server.js +221 -0
  129. package/dist/src/domains/extension/kinds.server.js.map +1 -0
  130. package/dist/src/domains/extension/kinds.types.d.ts +65 -0
  131. package/dist/src/domains/extension/kinds.types.js +98 -0
  132. package/dist/src/domains/extension/kinds.types.js.map +1 -0
  133. package/dist/src/domains/extension/kinds.ui.d.ts +4 -0
  134. package/dist/src/domains/extension/kinds.ui.js +309 -0
  135. package/dist/src/domains/extension/kinds.ui.js.map +1 -0
  136. package/dist/src/domains/extension/labels.d.ts +27 -0
  137. package/dist/src/domains/extension/labels.js +143 -0
  138. package/dist/src/domains/extension/labels.js.map +1 -0
  139. package/dist/src/domains/extension/runners/add.runner.d.ts +69 -0
  140. package/dist/src/domains/extension/runners/add.runner.js +453 -0
  141. package/dist/src/domains/extension/runners/add.runner.js.map +1 -0
  142. package/dist/src/domains/extension/runners/dev.runner.d.ts +33 -0
  143. package/dist/src/domains/extension/runners/dev.runner.js +257 -0
  144. package/dist/src/domains/extension/runners/dev.runner.js.map +1 -0
  145. package/dist/src/domains/extension/runners/doctor.runner.d.ts +18 -0
  146. package/dist/src/domains/extension/runners/doctor.runner.js +107 -0
  147. package/dist/src/domains/extension/runners/doctor.runner.js.map +1 -0
  148. package/dist/src/domains/extension/runners/events-add.runner.d.ts +12 -0
  149. package/dist/src/domains/extension/runners/events-add.runner.js +97 -0
  150. package/dist/src/domains/extension/runners/events-add.runner.js.map +1 -0
  151. package/dist/src/domains/extension/runners/events-remove.runner.d.ts +10 -0
  152. package/dist/src/domains/extension/runners/events-remove.runner.js +75 -0
  153. package/dist/src/domains/extension/runners/events-remove.runner.js.map +1 -0
  154. package/dist/src/domains/extension/runners/events.runner.d.ts +6 -0
  155. package/dist/src/domains/extension/runners/events.runner.js +40 -0
  156. package/dist/src/domains/extension/runners/events.runner.js.map +1 -0
  157. package/dist/src/domains/extension/runners/extension.command.d.ts +10 -0
  158. package/dist/src/domains/extension/runners/extension.command.js +71 -0
  159. package/dist/src/domains/extension/runners/extension.command.js.map +1 -0
  160. package/dist/src/domains/extension/runners/extension.runner.d.ts +17 -0
  161. package/dist/src/domains/extension/runners/extension.runner.js +53 -0
  162. package/dist/src/domains/extension/runners/extension.runner.js.map +1 -0
  163. package/dist/src/domains/extension/runners/index.d.ts +20 -0
  164. package/dist/src/domains/extension/runners/index.js +37 -0
  165. package/dist/src/domains/extension/runners/index.js.map +1 -0
  166. package/dist/src/domains/extension/runners/init.runner.d.ts +31 -0
  167. package/dist/src/domains/extension/runners/init.runner.js +276 -0
  168. package/dist/src/domains/extension/runners/init.runner.js.map +1 -0
  169. package/dist/src/domains/extension/runners/list.runner.d.ts +10 -0
  170. package/dist/src/domains/extension/runners/list.runner.js +79 -0
  171. package/dist/src/domains/extension/runners/list.runner.js.map +1 -0
  172. package/dist/src/domains/extension/runners/publish.runner.d.ts +38 -0
  173. package/dist/src/domains/extension/runners/publish.runner.js +178 -0
  174. package/dist/src/domains/extension/runners/publish.runner.js.map +1 -0
  175. package/dist/src/domains/extension/runners/remove.runner.d.ts +20 -0
  176. package/dist/src/domains/extension/runners/remove.runner.js +105 -0
  177. package/dist/src/domains/extension/runners/remove.runner.js.map +1 -0
  178. package/dist/src/domains/extension/runners/retire.runner.d.ts +18 -0
  179. package/dist/src/domains/extension/runners/retire.runner.js +80 -0
  180. package/dist/src/domains/extension/runners/retire.runner.js.map +1 -0
  181. package/dist/src/domains/extension/runners/scopes-add.runner.d.ts +16 -0
  182. package/dist/src/domains/extension/runners/scopes-add.runner.js +122 -0
  183. package/dist/src/domains/extension/runners/scopes-add.runner.js.map +1 -0
  184. package/dist/src/domains/extension/runners/scopes-remove.runner.d.ts +12 -0
  185. package/dist/src/domains/extension/runners/scopes-remove.runner.js +73 -0
  186. package/dist/src/domains/extension/runners/scopes-remove.runner.js.map +1 -0
  187. package/dist/src/domains/extension/runners/scopes.runner.d.ts +6 -0
  188. package/dist/src/domains/extension/runners/scopes.runner.js +40 -0
  189. package/dist/src/domains/extension/runners/scopes.runner.js.map +1 -0
  190. package/dist/src/domains/extension/runners/set.runner.d.ts +17 -0
  191. package/dist/src/domains/extension/runners/set.runner.js +157 -0
  192. package/dist/src/domains/extension/runners/set.runner.js.map +1 -0
  193. package/dist/src/domains/extension/runners/status.runner.d.ts +12 -0
  194. package/dist/src/domains/extension/runners/status.runner.js +88 -0
  195. package/dist/src/domains/extension/runners/status.runner.js.map +1 -0
  196. package/dist/src/domains/extension/runners/types.runner.d.ts +10 -0
  197. package/dist/src/domains/extension/runners/types.runner.js +43 -0
  198. package/dist/src/domains/extension/runners/types.runner.js.map +1 -0
  199. package/dist/src/domains/extension/runners/whoami.runner.d.ts +10 -0
  200. package/dist/src/domains/extension/runners/whoami.runner.js +71 -0
  201. package/dist/src/domains/extension/runners/whoami.runner.js.map +1 -0
  202. package/dist/src/domains/extension/services/add.service.d.ts +26 -0
  203. package/dist/src/domains/extension/services/add.service.js +263 -0
  204. package/dist/src/domains/extension/services/add.service.js.map +1 -0
  205. package/dist/src/domains/extension/services/checks/a11y-check.d.ts +3 -0
  206. package/dist/src/domains/extension/services/checks/a11y-check.js +84 -0
  207. package/dist/src/domains/extension/services/checks/a11y-check.js.map +1 -0
  208. package/dist/src/domains/extension/services/checks/bundle-checks.d.ts +3 -0
  209. package/dist/src/domains/extension/services/checks/bundle-checks.js +234 -0
  210. package/dist/src/domains/extension/services/checks/bundle-checks.js.map +1 -0
  211. package/dist/src/domains/extension/services/checks/bundle-files.d.ts +14 -0
  212. package/dist/src/domains/extension/services/checks/bundle-files.js +139 -0
  213. package/dist/src/domains/extension/services/checks/bundle-files.js.map +1 -0
  214. package/dist/src/domains/extension/services/checks/file-checks.d.ts +6 -0
  215. package/dist/src/domains/extension/services/checks/file-checks.js +128 -0
  216. package/dist/src/domains/extension/services/checks/file-checks.js.map +1 -0
  217. package/dist/src/domains/extension/services/checks/index.d.ts +7 -0
  218. package/dist/src/domains/extension/services/checks/index.js +24 -0
  219. package/dist/src/domains/extension/services/checks/index.js.map +1 -0
  220. package/dist/src/domains/extension/services/checks/manifest-checks.d.ts +7 -0
  221. package/dist/src/domains/extension/services/checks/manifest-checks.js +95 -0
  222. package/dist/src/domains/extension/services/checks/manifest-checks.js.map +1 -0
  223. package/dist/src/domains/extension/services/checks/project-checks.d.ts +7 -0
  224. package/dist/src/domains/extension/services/checks/project-checks.js +86 -0
  225. package/dist/src/domains/extension/services/checks/project-checks.js.map +1 -0
  226. package/dist/src/domains/extension/services/checks/types.d.ts +23 -0
  227. package/dist/src/domains/extension/services/checks/types.js +50 -0
  228. package/dist/src/domains/extension/services/checks/types.js.map +1 -0
  229. package/dist/src/domains/extension/services/dev.service.d.ts +26 -0
  230. package/dist/src/domains/extension/services/dev.service.js +58 -0
  231. package/dist/src/domains/extension/services/dev.service.js.map +1 -0
  232. package/dist/src/domains/extension/services/doctor.service.d.ts +36 -0
  233. package/dist/src/domains/extension/services/doctor.service.js +213 -0
  234. package/dist/src/domains/extension/services/doctor.service.js.map +1 -0
  235. package/dist/src/domains/extension/services/failures.d.ts +8 -0
  236. package/dist/src/domains/extension/services/failures.js +112 -0
  237. package/dist/src/domains/extension/services/failures.js.map +1 -0
  238. package/dist/src/domains/extension/services/guided.service.d.ts +36 -0
  239. package/dist/src/domains/extension/services/guided.service.js +133 -0
  240. package/dist/src/domains/extension/services/guided.service.js.map +1 -0
  241. package/dist/src/domains/extension/services/publish.service.d.ts +14 -0
  242. package/dist/src/domains/extension/services/publish.service.js +93 -0
  243. package/dist/src/domains/extension/services/publish.service.js.map +1 -0
  244. package/dist/src/domains/extension/services/registration.service.d.ts +7 -0
  245. package/dist/src/domains/extension/services/registration.service.js +43 -0
  246. package/dist/src/domains/extension/services/registration.service.js.map +1 -0
  247. package/dist/src/domains/extension/services/remove.service.d.ts +22 -0
  248. package/dist/src/domains/extension/services/remove.service.js +137 -0
  249. package/dist/src/domains/extension/services/remove.service.js.map +1 -0
  250. package/dist/src/domains/extension/services/types.service.d.ts +21 -0
  251. package/dist/src/domains/extension/services/types.service.js +81 -0
  252. package/dist/src/domains/extension/services/types.service.js.map +1 -0
  253. package/dist/src/domains/index.d.ts +6 -2
  254. package/dist/src/domains/index.js +6 -2
  255. package/dist/src/domains/index.js.map +1 -1
  256. package/dist/src/domains/{init → link}/index.d.ts +1 -1
  257. package/dist/src/domains/{init → link}/index.js +1 -1
  258. package/dist/src/domains/link/index.js.map +1 -0
  259. package/dist/src/domains/link/link.module.d.ts +2 -0
  260. package/dist/src/domains/{init/init.module.js → link/link.module.js} +8 -8
  261. package/dist/src/domains/link/link.module.js.map +1 -0
  262. package/dist/src/domains/link/runners/index.d.ts +1 -0
  263. package/dist/src/domains/{deploy → link}/runners/index.js +1 -1
  264. package/dist/src/domains/link/runners/index.js.map +1 -0
  265. package/dist/src/domains/link/runners/link.runner.d.ts +20 -0
  266. package/dist/src/domains/link/runners/link.runner.js +129 -0
  267. package/dist/src/domains/link/runners/link.runner.js.map +1 -0
  268. package/dist/src/domains/mcp/index.d.ts +5 -0
  269. package/dist/src/domains/mcp/index.js +22 -0
  270. package/dist/src/domains/mcp/index.js.map +1 -0
  271. package/dist/src/domains/mcp/mcp.module.d.ts +2 -0
  272. package/dist/src/domains/mcp/mcp.module.js +25 -0
  273. package/dist/src/domains/mcp/mcp.module.js.map +1 -0
  274. package/dist/src/domains/mcp/runners/mcp.runner.d.ts +7 -0
  275. package/dist/src/domains/mcp/runners/mcp.runner.js +45 -0
  276. package/dist/src/domains/mcp/runners/mcp.runner.js.map +1 -0
  277. package/dist/src/domains/mcp/services/docs-search.service.d.ts +23 -0
  278. package/dist/src/domains/mcp/services/docs-search.service.js +164 -0
  279. package/dist/src/domains/mcp/services/docs-search.service.js.map +1 -0
  280. package/dist/src/domains/mcp/services/mcp-server.factory.d.ts +42 -0
  281. package/dist/src/domains/mcp/services/mcp-server.factory.js +438 -0
  282. package/dist/src/domains/mcp/services/mcp-server.factory.js.map +1 -0
  283. package/dist/src/domains/mcp/services/validate.service.d.ts +17 -0
  284. package/dist/src/domains/mcp/services/validate.service.js +57 -0
  285. package/dist/src/domains/mcp/services/validate.service.js.map +1 -0
  286. package/dist/src/domains/upgrade/index.d.ts +1 -0
  287. package/dist/src/domains/upgrade/index.js +18 -0
  288. package/dist/src/domains/upgrade/index.js.map +1 -0
  289. package/dist/src/domains/upgrade/runners/index.d.ts +1 -0
  290. package/dist/src/domains/upgrade/runners/index.js +18 -0
  291. package/dist/src/domains/upgrade/runners/index.js.map +1 -0
  292. package/dist/src/domains/upgrade/runners/upgrade.runner.d.ts +12 -0
  293. package/dist/src/domains/upgrade/runners/upgrade.runner.js +191 -0
  294. package/dist/src/domains/upgrade/runners/upgrade.runner.js.map +1 -0
  295. package/dist/src/domains/upgrade/upgrade.module.d.ts +2 -0
  296. package/dist/src/domains/{deploy/deploy.module.js → upgrade/upgrade.module.js} +8 -8
  297. package/dist/src/domains/upgrade/upgrade.module.js.map +1 -0
  298. package/dist/src/domains/vendor/index.d.ts +1 -0
  299. package/dist/src/domains/vendor/index.js +18 -0
  300. package/dist/src/domains/vendor/index.js.map +1 -0
  301. package/dist/src/domains/vendor/runners/index.d.ts +2 -0
  302. package/dist/src/domains/vendor/runners/index.js +19 -0
  303. package/dist/src/domains/vendor/runners/index.js.map +1 -0
  304. package/dist/src/domains/vendor/runners/vendor-create.runner.d.ts +20 -0
  305. package/dist/src/domains/vendor/runners/vendor-create.runner.js +121 -0
  306. package/dist/src/domains/vendor/runners/vendor-create.runner.js.map +1 -0
  307. package/dist/src/domains/vendor/runners/vendor.command.d.ts +10 -0
  308. package/dist/src/domains/vendor/runners/vendor.command.js +42 -0
  309. package/dist/src/domains/vendor/runners/vendor.command.js.map +1 -0
  310. package/dist/src/domains/vendor/vendor.module.d.ts +2 -0
  311. package/dist/src/domains/vendor/vendor.module.js +22 -0
  312. package/dist/src/domains/vendor/vendor.module.js.map +1 -0
  313. package/dist/src/main.js +16 -2
  314. package/dist/src/main.js.map +1 -1
  315. package/dist/tsconfig.build.tsbuildinfo +1 -1
  316. package/package.json +43 -7
  317. package/templates/contributions/action/schemas/__file__.input.json +14 -0
  318. package/templates/contributions/action/server/actions/__file__.ts +8 -0
  319. package/templates/contributions/connector/server/metrics.ts +19 -0
  320. package/templates/contributions/context/server/context/__file__.ts +10 -0
  321. package/templates/contributions/knowledge/knowledge/__file__.md +6 -0
  322. package/templates/contributions/mention/server/mentions/__file__.ts +10 -0
  323. package/templates/contributions/page/src/pages/__file__.tsx +21 -0
  324. package/templates/contributions/page/test/__file__.test.tsx +12 -0
  325. package/templates/contributions/procedure/schemas/__file__.input.json +13 -0
  326. package/templates/contributions/procedure/schemas/__file__.output.json +13 -0
  327. package/templates/contributions/procedure/server/procedures/__file__.ts +13 -0
  328. package/templates/contributions/prompt/schemas/__file__.input.json +14 -0
  329. package/templates/contributions/row-action/src/row-actions/__file__.tsx +20 -0
  330. package/templates/contributions/row-action/test/__file__.test.tsx +17 -0
  331. package/templates/contributions/settings/schemas/settings.json +14 -0
  332. package/templates/contributions/settings/src/settings.tsx +24 -0
  333. package/templates/contributions/settings/test/settings.test.tsx +12 -0
  334. package/templates/contributions/skill/skills/__file__.md +14 -0
  335. package/templates/contributions/storage/schemas/__file__.json +14 -0
  336. package/templates/contributions/tab/src/tabs/__file__.tsx +21 -0
  337. package/templates/contributions/tab/test/__file__.test.tsx +12 -0
  338. package/templates/contributions/tool/schemas/__file__.input.json +14 -0
  339. package/templates/contributions/tool/server/tools/__file__.ts +8 -0
  340. package/templates/contributions/webhook/schemas/__file__.payload.json +13 -0
  341. package/templates/contributions/widget/src/widgets/__file__.tsx +48 -0
  342. package/templates/contributions/widget/test/__file__.test.tsx +16 -0
  343. package/templates/contributions/work/server/work.ts +14 -0
  344. package/templates/starter/README.md +75 -0
  345. package/templates/starter/api/tappify/[...path].ts +6 -0
  346. package/templates/starter/gitignore +6 -0
  347. package/templates/starter/icon.svg +4 -0
  348. package/templates/starter/package.json +37 -0
  349. package/templates/starter/pnpm-workspace.yaml +3 -0
  350. package/templates/starter/schemas/get-summary.input.json +11 -0
  351. package/templates/starter/schemas/get-summary.output.json +12 -0
  352. package/templates/starter/schemas/preferences.json +18 -0
  353. package/templates/starter/schemas/settings.json +19 -0
  354. package/templates/starter/server/actions/index.ts +1 -0
  355. package/templates/starter/server/context/index.ts +1 -0
  356. package/templates/starter/server/handler.ts +25 -0
  357. package/templates/starter/server/mentions/index.ts +1 -0
  358. package/templates/starter/server/procedures/get-summary.ts +32 -0
  359. package/templates/starter/server/procedures/index.ts +5 -0
  360. package/templates/starter/server/tools/index.ts +1 -0
  361. package/templates/starter/server/worker.ts +5 -0
  362. package/templates/starter/src/settings.tsx +24 -0
  363. package/templates/starter/src/styles.css +16 -0
  364. package/templates/starter/src/tappify.d.ts +59 -0
  365. package/templates/starter/src/widgets/summary.tsx +122 -0
  366. package/templates/starter/tappify.extension.json +68 -0
  367. package/templates/starter/test/matchers.d.ts +2 -0
  368. package/templates/starter/test/server.test.ts +69 -0
  369. package/templates/starter/test/settings.test.tsx +46 -0
  370. package/templates/starter/test/summary.test.tsx +109 -0
  371. package/templates/starter/tsconfig.json +20 -0
  372. package/templates/starter/vite.config.ts +8 -0
  373. package/templates/starter/vitest.config.ts +29 -0
  374. package/templates/starter/wrangler.toml +7 -0
  375. package/dist/src/domains/deploy/deploy.module.d.ts +0 -2
  376. package/dist/src/domains/deploy/deploy.module.js.map +0 -1
  377. package/dist/src/domains/deploy/index.d.ts +0 -2
  378. package/dist/src/domains/deploy/index.js.map +0 -1
  379. package/dist/src/domains/deploy/runners/deploy.runner.d.ts +0 -10
  380. package/dist/src/domains/deploy/runners/deploy.runner.js +0 -132
  381. package/dist/src/domains/deploy/runners/deploy.runner.js.map +0 -1
  382. package/dist/src/domains/deploy/runners/index.d.ts +0 -1
  383. package/dist/src/domains/deploy/runners/index.js.map +0 -1
  384. package/dist/src/domains/init/index.js.map +0 -1
  385. package/dist/src/domains/init/init.module.d.ts +0 -2
  386. package/dist/src/domains/init/init.module.js.map +0 -1
  387. package/dist/src/domains/init/runners/index.d.ts +0 -1
  388. package/dist/src/domains/init/runners/index.js.map +0 -1
  389. package/dist/src/domains/init/runners/init.runner.d.ts +0 -4
  390. package/dist/src/domains/init/runners/init.runner.js.map +0 -1
@@ -0,0 +1,6 @@
1
+ import { createTappifyHandler } from '@tappify/extension-sdk/server';
2
+ import { handlerOptions } from '../../server/handler';
3
+
4
+ export const config = { runtime: 'edge' };
5
+
6
+ export default createTappifyHandler({ ...handlerOptions, basePath: '/api' });
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ coverage
4
+ .wrangler
5
+ .vercel
6
+ *.tgz
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="__title__">
2
+ <rect width="32" height="32" rx="7" fill="#4f46e5" />
3
+ <path d="M9 21V15M16 21V11M23 21V17" stroke="#ffffff" stroke-width="3" stroke-linecap="round" />
4
+ </svg>
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "__id__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "__description__",
7
+ "scripts": {
8
+ "dev": "vite",
9
+ "build": "vite build",
10
+ "typecheck": "tsc --noEmit",
11
+ "test": "vitest run",
12
+ "types": "tappify extension types",
13
+ "dev:worker": "wrangler dev",
14
+ "deploy:worker": "wrangler deploy"
15
+ },
16
+ "dependencies": {
17
+ "@tanstack/react-query": "^5.100.14",
18
+ "@tappify/extension-sdk": "^2.0.0",
19
+ "react": "^19.2.6",
20
+ "react-dom": "^19.2.6"
21
+ },
22
+ "devDependencies": {
23
+ "@module-federation/vite": "~1.16.0",
24
+ "@testing-library/jest-dom": "^7.0.0",
25
+ "@testing-library/react": "^16.1.0",
26
+ "@testing-library/user-event": "^14.5.2",
27
+ "@types/node": "^22.13.0",
28
+ "@types/react": "^19.2.15",
29
+ "@types/react-dom": "^19.2.3",
30
+ "@vitejs/plugin-react": "^6.0.2",
31
+ "happy-dom": "^17.0.0",
32
+ "typescript": "^5.7.0",
33
+ "vite": "^8.0.14",
34
+ "vitest": "^4.1.0",
35
+ "wrangler": "^4.0.0"
36
+ }
37
+ }
@@ -0,0 +1,3 @@
1
+ allowBuilds:
2
+ esbuild: true
3
+ workerd: true
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "GetSummaryInput",
4
+ "type": "object",
5
+ "properties": {
6
+ "days": { "type": "number" },
7
+ "platform": { "type": "string", "enum": ["ios", "android", "all"] }
8
+ },
9
+ "required": ["days", "platform"],
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "GetSummaryOutput",
4
+ "type": "object",
5
+ "properties": {
6
+ "installs": { "type": "number" },
7
+ "delta": { "type": "number" },
8
+ "topKeyword": { "type": "string" }
9
+ },
10
+ "required": ["installs", "delta"],
11
+ "additionalProperties": false
12
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Preferences",
4
+ "type": "object",
5
+ "properties": {
6
+ "compact": {
7
+ "type": "boolean",
8
+ "description": "Render the summary on one line"
9
+ },
10
+ "metric": {
11
+ "type": "string",
12
+ "enum": ["downloads", "impressions"],
13
+ "description": "Which series the summary reports"
14
+ }
15
+ },
16
+ "required": ["compact", "metric"],
17
+ "additionalProperties": false
18
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Settings",
4
+ "type": "object",
5
+ "properties": {
6
+ "refreshMinutes": {
7
+ "type": "number",
8
+ "title": "Refresh minutes",
9
+ "description": "How often the summary asks the server for new numbers"
10
+ },
11
+ "showDelta": {
12
+ "type": "boolean",
13
+ "title": "Show change",
14
+ "description": "Show the change against the previous period"
15
+ }
16
+ },
17
+ "required": ["refreshMinutes"],
18
+ "additionalProperties": false
19
+ }
@@ -0,0 +1 @@
1
+ export const actions = {};
@@ -0,0 +1 @@
1
+ export const context = {};
@@ -0,0 +1,25 @@
1
+ import {
2
+ createTappifyHandler,
3
+ type TappifyFetchHandler,
4
+ type TappifyHandlerOptions,
5
+ } from '@tappify/extension-sdk/server';
6
+ import { actions } from './actions';
7
+ import { context } from './context';
8
+ import { mentions } from './mentions';
9
+ import { procedures } from './procedures';
10
+ import { tools } from './tools';
11
+
12
+ export const handlerOptions: TappifyHandlerOptions = {
13
+ extensionId: '__id__',
14
+
15
+ health: () => ({ ok: true, version: '0.1.0' }),
16
+
17
+ procedures,
18
+ tools,
19
+ actions,
20
+ mentions,
21
+ context,
22
+ };
23
+
24
+ export const handler: TappifyFetchHandler =
25
+ createTappifyHandler(handlerOptions);
@@ -0,0 +1 @@
1
+ export const mentions = {};
@@ -0,0 +1,32 @@
1
+ import type {
2
+ TapProcedureInput,
3
+ TapProcedureOutput,
4
+ } from '@tappify/extension-sdk';
5
+ import { TapServerError } from '@tappify/extension-sdk/server';
6
+ import type { TappifyRequest } from '@tappify/extension-sdk/server';
7
+
8
+ const BASELINE_INSTALLS_PER_DAY = 180;
9
+
10
+ export function getSummary(
11
+ request: TappifyRequest<TapProcedureInput<'getSummary'>>,
12
+ ): TapProcedureOutput<'getSummary'> {
13
+ const { days, platform } = request.input;
14
+
15
+ if (days < 1) {
16
+ throw new TapServerError(
17
+ 'RANGE_TOO_SHORT',
18
+ 'getSummary needs a range of at least one day. Widen the date picker, then retry.',
19
+ 400,
20
+ );
21
+ }
22
+
23
+ const multiplier = platform === 'all' ? 1 : 0.55;
24
+ const installs = Math.round(BASELINE_INSTALLS_PER_DAY * days * multiplier);
25
+ const previous = Math.round(installs * 0.92);
26
+
27
+ return {
28
+ installs,
29
+ delta: (installs - previous) / previous,
30
+ topKeyword: 'photo editor',
31
+ };
32
+ }
@@ -0,0 +1,5 @@
1
+ import { getSummary } from './get-summary';
2
+
3
+ export const procedures = {
4
+ getSummary,
5
+ };
@@ -0,0 +1 @@
1
+ export const tools = {};
@@ -0,0 +1,5 @@
1
+ import { handler } from './handler';
2
+
3
+ export default {
4
+ fetch: handler,
5
+ };
@@ -0,0 +1,24 @@
1
+ import {
2
+ TapForm,
3
+ TapPageHeader,
4
+ type TapSettingsProps,
5
+ } from '@tappify/extension-sdk';
6
+ import '@tappify/extension-sdk/styles.css';
7
+ import schema from '../schemas/settings.json';
8
+
9
+ export default function Settings({ values, onChange }: TapSettingsProps) {
10
+ return (
11
+ <div className="tap-stack">
12
+ <TapPageHeader
13
+ title="__title__"
14
+ description="These values reach your server as documents.settings on every call."
15
+ />
16
+ <TapForm
17
+ schema={schema}
18
+ value={values}
19
+ submitLabel="Save"
20
+ onSubmit={next => onChange(next)}
21
+ />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,16 @@
1
+ .starter-summary {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 4px;
5
+ }
6
+
7
+ .starter-summary--compact {
8
+ flex-direction: row;
9
+ align-items: baseline;
10
+ gap: 8px;
11
+ }
12
+
13
+ .starter-summary__footnote {
14
+ color: var(--fg-muted, rgba(18, 18, 18, 0.6));
15
+ font-size: 11px;
16
+ }
@@ -0,0 +1,59 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated by `tappify extension types`. Do not edit by hand.
4
+ * Re-run the command after changing tappify.extension.json or any schema it points at.
5
+ */
6
+
7
+ export interface Preferences {
8
+ /**
9
+ * Render the summary on one line
10
+ */
11
+ compact: boolean;
12
+ /**
13
+ * Which series the summary reports
14
+ */
15
+ metric: 'downloads' | 'impressions';
16
+ }
17
+
18
+ export interface GetSummaryInput {
19
+ days: number;
20
+ platform: 'ios' | 'android' | 'all';
21
+ }
22
+
23
+ export interface GetSummaryOutput {
24
+ installs: number;
25
+ delta: number;
26
+ topKeyword?: string;
27
+ }
28
+
29
+ /**
30
+ * How often the summary asks the server for new numbers
31
+ */
32
+ export type RefreshMinutes = number;
33
+ /**
34
+ * Show the change against the previous period
35
+ */
36
+ export type ShowChange = boolean;
37
+
38
+ export interface Settings {
39
+ refreshMinutes: RefreshMinutes;
40
+ showDelta?: ShowChange;
41
+ }
42
+
43
+ declare module '@tappify/extension-sdk' {
44
+ interface TapStorageMap {
45
+ 'preferences': { kind: 'singleton'; scope: 'user'; document: Preferences };
46
+ }
47
+
48
+ interface TapProcedureMap {
49
+ 'getSummary': { input: GetSummaryInput; output: GetSummaryOutput };
50
+ }
51
+
52
+ interface TapTelemetryEvents {
53
+ 'summary_viewed': true;
54
+ }
55
+
56
+ interface TapSettings extends Settings {}
57
+ }
58
+
59
+ export {};
@@ -0,0 +1,122 @@
1
+ import {
2
+ TapButton,
3
+ TapCard,
4
+ TapErrorState,
5
+ TapSkeleton,
6
+ TapStat,
7
+ cn,
8
+ useTap,
9
+ useTapFilters,
10
+ useTapQuery,
11
+ useTapServer,
12
+ useTapStorage,
13
+ type TapSeriesPoint,
14
+ type TapWidgetProps,
15
+ } from '@tappify/extension-sdk';
16
+ import '@tappify/extension-sdk/styles.css';
17
+ import { useEffect } from 'react';
18
+ import '../styles.css';
19
+
20
+ function daysBetween(from: string, to: string): number {
21
+ const start = new Date(from).getTime();
22
+ const end = new Date(to).getTime();
23
+ return Math.max(1, Math.round((end - start) / 86_400_000));
24
+ }
25
+
26
+ function bestDay(points: TapSeriesPoint[]): number {
27
+ return points.reduce((highest, point) => Math.max(highest, point.value), 0);
28
+ }
29
+
30
+ export default function Summary(_props: TapWidgetProps) {
31
+ const tap = useTap();
32
+ const filters = useTapFilters();
33
+ const preferences = useTapStorage('preferences');
34
+
35
+ const downloads = useTapQuery({
36
+ kind: 'series',
37
+ metric: 'downloads',
38
+ range: filters.range,
39
+ });
40
+
41
+ const summary = useTapServer(
42
+ 'getSummary',
43
+ {
44
+ days: daysBetween(filters.range.from, filters.range.to),
45
+ platform: filters.platform,
46
+ },
47
+ { invalidateOn: ['settings.changed'] },
48
+ );
49
+
50
+ const refetchSummary = summary.refetch;
51
+
52
+ useEffect(() => {
53
+ return tap.data.subscribe('release.shipped', () => {
54
+ tap.ui.toast('A release shipped — refreshing the summary.', 'neutral');
55
+ refetchSummary();
56
+ });
57
+ }, [tap, refetchSummary]);
58
+
59
+ useEffect(() => {
60
+ tap.telemetry.event('summary_viewed', { size: tap.ui.size });
61
+ }, [tap]);
62
+
63
+ const compact = preferences.data?.compact ?? false;
64
+ const failure = summary.error ?? downloads.error;
65
+
66
+ return (
67
+ <TapCard
68
+ title="Install summary"
69
+ action={
70
+ <TapButton
71
+ variant="ghost"
72
+ size="sm"
73
+ onClick={() => void preferences.patch({ compact: !compact })}
74
+ >
75
+ {compact ? 'Expand' : 'Compact'}
76
+ </TapButton>
77
+ }
78
+ footer={
79
+ <span className="starter-summary__footnote">
80
+ {tap.format.date(filters.range.from)} to{' '}
81
+ {tap.format.date(filters.range.to)}
82
+ </span>
83
+ }
84
+ >
85
+ {(summary.isLoading || downloads.isLoading) && <TapSkeleton lines={2} />}
86
+
87
+ {failure !== null && (
88
+ <TapErrorState
89
+ title="The summary did not load"
90
+ description={failure.message}
91
+ onRetry={() => {
92
+ summary.refetch();
93
+ downloads.refetch();
94
+ }}
95
+ />
96
+ )}
97
+
98
+ {failure === null && summary.data !== undefined && (
99
+ <div
100
+ className={cn('starter-summary', compact && 'starter-summary--compact')}
101
+ >
102
+ <TapStat
103
+ label="Installs"
104
+ value={tap.format.number(summary.data.installs)}
105
+ delta={summary.data.delta}
106
+ />
107
+ {downloads.data !== undefined && (
108
+ <span className="starter-summary__footnote">
109
+ Best day: {tap.format.number(bestDay(downloads.data.points))}{' '}
110
+ downloads
111
+ </span>
112
+ )}
113
+ {summary.data.topKeyword !== undefined && (
114
+ <span className="starter-summary__footnote">
115
+ Top keyword: {summary.data.topKeyword}
116
+ </span>
117
+ )}
118
+ </div>
119
+ )}
120
+ </TapCard>
121
+ );
122
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "$schema": "https://schema.tappify.ai/extension/v2.json",
3
+ "id": "__id__",
4
+ "name": "__title__",
5
+ "description": "__description__",
6
+ "icon": "icon.svg",
7
+ "category": "product_analytics",
8
+ "sdk": "^2.0.0",
9
+ "visibility": "private",
10
+ "installScope": "project",
11
+ "scopes": [
12
+ {
13
+ "key": "ui:render"
14
+ },
15
+ {
16
+ "key": "analytics:read"
17
+ },
18
+ {
19
+ "key": "storage:write"
20
+ }
21
+ ],
22
+ "telemetry": [
23
+ "summary_viewed"
24
+ ],
25
+ "contributes": {
26
+ "widgets": [
27
+ {
28
+ "id": "summary",
29
+ "title": "Install summary",
30
+ "entry": "src/widgets/summary.tsx",
31
+ "page": "analytics",
32
+ "slot": "kpi-row",
33
+ "size": "1x1",
34
+ "expandable": true
35
+ }
36
+ ],
37
+ "settings": {
38
+ "entry": "src/settings.tsx",
39
+ "schema": {
40
+ "$ref": "./schemas/settings.json"
41
+ }
42
+ },
43
+ "storage": {
44
+ "preferences": {
45
+ "scope": "user",
46
+ "singleton": true,
47
+ "schema": {
48
+ "$ref": "./schemas/preferences.json"
49
+ }
50
+ }
51
+ }
52
+ },
53
+ "server": {
54
+ "baseUrl": "https://__id__.example.com",
55
+ "procedures": {
56
+ "getSummary": {
57
+ "input": {
58
+ "$ref": "./schemas/get-summary.input.json"
59
+ },
60
+ "output": {
61
+ "$ref": "./schemas/get-summary.output.json"
62
+ },
63
+ "cache": "5m",
64
+ "kind": "read"
65
+ }
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,2 @@
1
+ import '@tappify/extension-sdk/testing/vitest';
2
+ import '@testing-library/jest-dom/vitest';
@@ -0,0 +1,69 @@
1
+ import { createTestClient } from '@tappify/extension-sdk/testing';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { handlerOptions } from '../server/handler';
4
+
5
+ const client = createTestClient(handlerOptions);
6
+ const vercelClient = createTestClient({ ...handlerOptions, basePath: '/api' });
7
+
8
+ describe('the extension server', () => {
9
+ it('answers the publish health check', async () => {
10
+ await expect(client.health()).resolves.toEqual({
11
+ ok: true,
12
+ version: '0.1.0',
13
+ });
14
+ });
15
+
16
+ it('answers getSummary with the declared output shape', async () => {
17
+ const summary = await client.procedure('getSummary', {
18
+ days: 7,
19
+ platform: 'all',
20
+ });
21
+
22
+ expect(summary).toMatchObject({
23
+ installs: expect.any(Number),
24
+ delta: expect.any(Number),
25
+ topKeyword: 'photo editor',
26
+ });
27
+ });
28
+
29
+ it('scales the answer with the platform filter', async () => {
30
+ const all = await client.procedure('getSummary', {
31
+ days: 7,
32
+ platform: 'all',
33
+ });
34
+ const ios = await client.procedure('getSummary', {
35
+ days: 7,
36
+ platform: 'ios',
37
+ });
38
+
39
+ expect(all).toMatchObject({ installs: 1260 });
40
+ expect(ios).toMatchObject({ installs: 693 });
41
+ });
42
+
43
+ it('returns a coded error a widget can show', async () => {
44
+ await expect(
45
+ client.procedure('getSummary', { days: 0, platform: 'all' }),
46
+ ).rejects.toMatchObject({
47
+ code: 'RANGE_TOO_SHORT',
48
+ status: 400,
49
+ message:
50
+ 'getSummary needs a range of at least one day. Widen the date picker, then retry.',
51
+ });
52
+ });
53
+
54
+ it('404s a procedure the manifest does not declare', async () => {
55
+ const response = await client.raw('POST', '/tappify/procedures/ghost', {});
56
+
57
+ expect(response.status).toBe(404);
58
+ });
59
+
60
+ it('answers under the /api prefix the Vercel function is mounted at', async () => {
61
+ await expect(vercelClient.health()).resolves.toEqual({
62
+ ok: true,
63
+ version: '0.1.0',
64
+ });
65
+
66
+ const response = await vercelClient.raw('GET', '/api/tappify/health');
67
+ expect(response.status).toBe(200);
68
+ });
69
+ });
@@ -0,0 +1,46 @@
1
+ import { renderWithTap } from '@tappify/extension-sdk/testing';
2
+ import { screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+ import Settings from '../src/settings';
6
+
7
+ describe('the settings panel', () => {
8
+ it('renders one control per schema field', () => {
9
+ renderWithTap(
10
+ <Settings values={{ refreshMinutes: 15 }} onChange={vi.fn()} />,
11
+ );
12
+
13
+ expect(screen.getByLabelText('Refresh minutes')).toHaveValue(15);
14
+ expect(screen.getByLabelText('Show change')).toBeInTheDocument();
15
+ });
16
+
17
+ it('reports the new values on save', async () => {
18
+ const onChange = vi.fn();
19
+ renderWithTap(
20
+ <Settings values={{ refreshMinutes: 15 }} onChange={onChange} />,
21
+ );
22
+
23
+ await userEvent.clear(screen.getByLabelText('Refresh minutes'));
24
+ await userEvent.type(screen.getByLabelText('Refresh minutes'), '30');
25
+ await userEvent.click(screen.getByLabelText('Show change'));
26
+ await userEvent.click(screen.getByRole('button', { name: 'Save' }));
27
+
28
+ expect(onChange).toHaveBeenCalledWith({
29
+ refreshMinutes: 30,
30
+ showDelta: true,
31
+ });
32
+ });
33
+
34
+ it('refuses to save when a required value is missing', async () => {
35
+ const onChange = vi.fn();
36
+ renderWithTap(
37
+ <Settings values={{ refreshMinutes: 15 }} onChange={onChange} />,
38
+ );
39
+
40
+ await userEvent.clear(screen.getByLabelText('Refresh minutes'));
41
+ await userEvent.click(screen.getByRole('button', { name: 'Save' }));
42
+
43
+ expect(screen.getByText('Refresh minutes is required.')).toBeInTheDocument();
44
+ expect(onChange).not.toHaveBeenCalled();
45
+ });
46
+ });