@azurioh/discord-kernel 0.1.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 (373) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/authz/authorizer.d.ts +45 -0
  4. package/dist/authz/authorizer.d.ts.map +1 -0
  5. package/dist/authz/authorizer.js +31 -0
  6. package/dist/authz/authorizer.js.map +1 -0
  7. package/dist/authz/require-level-guard.d.ts +23 -0
  8. package/dist/authz/require-level-guard.d.ts.map +1 -0
  9. package/dist/authz/require-level-guard.js +37 -0
  10. package/dist/authz/require-level-guard.js.map +1 -0
  11. package/dist/clock.d.ts +13 -0
  12. package/dist/clock.d.ts.map +1 -0
  13. package/dist/clock.js +13 -0
  14. package/dist/clock.js.map +1 -0
  15. package/dist/concurrency/keyed-queue.d.ts +18 -0
  16. package/dist/concurrency/keyed-queue.d.ts.map +1 -0
  17. package/dist/concurrency/keyed-queue.js +37 -0
  18. package/dist/concurrency/keyed-queue.js.map +1 -0
  19. package/dist/concurrency/run-with-concurrency.d.ts +15 -0
  20. package/dist/concurrency/run-with-concurrency.d.ts.map +1 -0
  21. package/dist/concurrency/run-with-concurrency.js +40 -0
  22. package/dist/concurrency/run-with-concurrency.js.map +1 -0
  23. package/dist/config/env.d.ts +17 -0
  24. package/dist/config/env.d.ts.map +1 -0
  25. package/dist/config/env.js +49 -0
  26. package/dist/config/env.js.map +1 -0
  27. package/dist/config/errors.d.ts +14 -0
  28. package/dist/config/errors.d.ts.map +1 -0
  29. package/dist/config/errors.js +25 -0
  30. package/dist/config/errors.js.map +1 -0
  31. package/dist/datetime.d.ts +34 -0
  32. package/dist/datetime.d.ts.map +1 -0
  33. package/dist/datetime.js +188 -0
  34. package/dist/datetime.js.map +1 -0
  35. package/dist/discord/api-errors.d.ts +20 -0
  36. package/dist/discord/api-errors.d.ts.map +1 -0
  37. package/dist/discord/api-errors.js +90 -0
  38. package/dist/discord/api-errors.js.map +1 -0
  39. package/dist/discord/channel-exporter.d.ts +42 -0
  40. package/dist/discord/channel-exporter.d.ts.map +1 -0
  41. package/dist/discord/channel-exporter.js +3 -0
  42. package/dist/discord/channel-exporter.js.map +1 -0
  43. package/dist/discord/command/autocomplete-dispatcher.d.ts +26 -0
  44. package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -0
  45. package/dist/discord/command/autocomplete-dispatcher.js +41 -0
  46. package/dist/discord/command/autocomplete-dispatcher.js.map +1 -0
  47. package/dist/discord/command/autocomplete.d.ts +18 -0
  48. package/dist/discord/command/autocomplete.d.ts.map +1 -0
  49. package/dist/discord/command/autocomplete.js +65 -0
  50. package/dist/discord/command/autocomplete.js.map +1 -0
  51. package/dist/discord/command/context.d.ts +33 -0
  52. package/dist/discord/command/context.d.ts.map +1 -0
  53. package/dist/discord/command/context.js +48 -0
  54. package/dist/discord/command/context.js.map +1 -0
  55. package/dist/discord/command/cooldown-guard.d.ts +25 -0
  56. package/dist/discord/command/cooldown-guard.d.ts.map +1 -0
  57. package/dist/discord/command/cooldown-guard.js +60 -0
  58. package/dist/discord/command/cooldown-guard.js.map +1 -0
  59. package/dist/discord/command/create-command.d.ts +100 -0
  60. package/dist/discord/command/create-command.d.ts.map +1 -0
  61. package/dist/discord/command/create-command.js +245 -0
  62. package/dist/discord/command/create-command.js.map +1 -0
  63. package/dist/discord/command/create-context-menu-command.d.ts +51 -0
  64. package/dist/discord/command/create-context-menu-command.d.ts.map +1 -0
  65. package/dist/discord/command/create-context-menu-command.js +128 -0
  66. package/dist/discord/command/create-context-menu-command.js.map +1 -0
  67. package/dist/discord/command/errors.d.ts +19 -0
  68. package/dist/discord/command/errors.d.ts.map +1 -0
  69. package/dist/discord/command/errors.js +27 -0
  70. package/dist/discord/command/errors.js.map +1 -0
  71. package/dist/discord/command/guard.d.ts +30 -0
  72. package/dist/discord/command/guard.d.ts.map +1 -0
  73. package/dist/discord/command/guard.js +34 -0
  74. package/dist/discord/command/guard.js.map +1 -0
  75. package/dist/discord/command/localization.d.ts +9 -0
  76. package/dist/discord/command/localization.d.ts.map +1 -0
  77. package/dist/discord/command/localization.js +13 -0
  78. package/dist/discord/command/localization.js.map +1 -0
  79. package/dist/discord/command/options.d.ts +113 -0
  80. package/dist/discord/command/options.d.ts.map +1 -0
  81. package/dist/discord/command/options.js +509 -0
  82. package/dist/discord/command/options.js.map +1 -0
  83. package/dist/discord/command/permission-guard.d.ts +21 -0
  84. package/dist/discord/command/permission-guard.d.ts.map +1 -0
  85. package/dist/discord/command/permission-guard.js +69 -0
  86. package/dist/discord/command/permission-guard.js.map +1 -0
  87. package/dist/discord/command/router.d.ts +48 -0
  88. package/dist/discord/command/router.d.ts.map +1 -0
  89. package/dist/discord/command/router.js +118 -0
  90. package/dist/discord/command/router.js.map +1 -0
  91. package/dist/discord/command/types.d.ts +51 -0
  92. package/dist/discord/command/types.d.ts.map +1 -0
  93. package/dist/discord/command/types.js +3 -0
  94. package/dist/discord/command/types.js.map +1 -0
  95. package/dist/discord/components/component-router.d.ts +116 -0
  96. package/dist/discord/components/component-router.d.ts.map +1 -0
  97. package/dist/discord/components/component-router.js +123 -0
  98. package/dist/discord/components/component-router.js.map +1 -0
  99. package/dist/discord/components/interactive-message/index.d.ts +3 -0
  100. package/dist/discord/components/interactive-message/index.d.ts.map +1 -0
  101. package/dist/discord/components/interactive-message/index.js +11 -0
  102. package/dist/discord/components/interactive-message/index.js.map +1 -0
  103. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +195 -0
  104. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -0
  105. package/dist/discord/components/interactive-message/interactive-message-collector.js +151 -0
  106. package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -0
  107. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +30 -0
  108. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -0
  109. package/dist/discord/components/interactive-message/mount-interactive-message.js +44 -0
  110. package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -0
  111. package/dist/discord/components/paginator/index.d.ts +5 -0
  112. package/dist/discord/components/paginator/index.d.ts.map +1 -0
  113. package/dist/discord/components/paginator/index.js +15 -0
  114. package/dist/discord/components/paginator/index.js.map +1 -0
  115. package/dist/discord/components/paginator/mount-paginator.d.ts +25 -0
  116. package/dist/discord/components/paginator/mount-paginator.d.ts.map +1 -0
  117. package/dist/discord/components/paginator/mount-paginator.js +41 -0
  118. package/dist/discord/components/paginator/mount-paginator.js.map +1 -0
  119. package/dist/discord/components/paginator/page-state.d.ts +21 -0
  120. package/dist/discord/components/paginator/page-state.d.ts.map +1 -0
  121. package/dist/discord/components/paginator/page-state.js +44 -0
  122. package/dist/discord/components/paginator/page-state.js.map +1 -0
  123. package/dist/discord/components/paginator/paginator-buttons.d.ts +17 -0
  124. package/dist/discord/components/paginator/paginator-buttons.d.ts.map +1 -0
  125. package/dist/discord/components/paginator/paginator-buttons.js +45 -0
  126. package/dist/discord/components/paginator/paginator-buttons.js.map +1 -0
  127. package/dist/discord/components/paginator/paginator-view.d.ts +26 -0
  128. package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -0
  129. package/dist/discord/components/paginator/paginator-view.js +28 -0
  130. package/dist/discord/components/paginator/paginator-view.js.map +1 -0
  131. package/dist/discord/components/persistent-paginator.d.ts +47 -0
  132. package/dist/discord/components/persistent-paginator.d.ts.map +1 -0
  133. package/dist/discord/components/persistent-paginator.js +137 -0
  134. package/dist/discord/components/persistent-paginator.js.map +1 -0
  135. package/dist/discord/components/settings-editor/index.d.ts +7 -0
  136. package/dist/discord/components/settings-editor/index.d.ts.map +1 -0
  137. package/dist/discord/components/settings-editor/index.js +22 -0
  138. package/dist/discord/components/settings-editor/index.js.map +1 -0
  139. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +140 -0
  140. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -0
  141. package/dist/discord/components/settings-editor/mount-settings-editor.js +344 -0
  142. package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -0
  143. package/dist/discord/components/settings-editor/navigation.d.ts +49 -0
  144. package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -0
  145. package/dist/discord/components/settings-editor/navigation.js +39 -0
  146. package/dist/discord/components/settings-editor/navigation.js.map +1 -0
  147. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +56 -0
  148. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -0
  149. package/dist/discord/components/settings-editor/settings-editor-card.view.js +284 -0
  150. package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -0
  151. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +69 -0
  152. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -0
  153. package/dist/discord/components/settings-editor/settings-editor-field-modal.js +353 -0
  154. package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -0
  155. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +216 -0
  156. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -0
  157. package/dist/discord/components/settings-editor/settings-editor-fields.js +66 -0
  158. package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -0
  159. package/dist/discord/components/settings-editor/settings-editor.view.d.ts +303 -0
  160. package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -0
  161. package/dist/discord/components/settings-editor/settings-editor.view.js +316 -0
  162. package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -0
  163. package/dist/discord/events/create-event.d.ts +5 -0
  164. package/dist/discord/events/create-event.d.ts.map +1 -0
  165. package/dist/discord/events/create-event.js +8 -0
  166. package/dist/discord/events/create-event.js.map +1 -0
  167. package/dist/discord/events/event-router.d.ts +16 -0
  168. package/dist/discord/events/event-router.d.ts.map +1 -0
  169. package/dist/discord/events/event-router.js +51 -0
  170. package/dist/discord/events/event-router.js.map +1 -0
  171. package/dist/discord/events/types.d.ts +16 -0
  172. package/dist/discord/events/types.d.ts.map +1 -0
  173. package/dist/discord/events/types.js +3 -0
  174. package/dist/discord/events/types.js.map +1 -0
  175. package/dist/discord/i18n.d.ts +58 -0
  176. package/dist/discord/i18n.d.ts.map +1 -0
  177. package/dist/discord/i18n.js +173 -0
  178. package/dist/discord/i18n.js.map +1 -0
  179. package/dist/discord/interaction/button.d.ts +39 -0
  180. package/dist/discord/interaction/button.d.ts.map +1 -0
  181. package/dist/discord/interaction/button.js +36 -0
  182. package/dist/discord/interaction/button.js.map +1 -0
  183. package/dist/discord/interaction/interaction-router.d.ts +21 -0
  184. package/dist/discord/interaction/interaction-router.d.ts.map +1 -0
  185. package/dist/discord/interaction/interaction-router.js +24 -0
  186. package/dist/discord/interaction/interaction-router.js.map +1 -0
  187. package/dist/discord/interaction/modal.d.ts +171 -0
  188. package/dist/discord/interaction/modal.d.ts.map +1 -0
  189. package/dist/discord/interaction/modal.js +264 -0
  190. package/dist/discord/interaction/modal.js.map +1 -0
  191. package/dist/discord/interaction/select-menu.d.ts +104 -0
  192. package/dist/discord/interaction/select-menu.d.ts.map +1 -0
  193. package/dist/discord/interaction/select-menu.js +126 -0
  194. package/dist/discord/interaction/select-menu.js.map +1 -0
  195. package/dist/discord/permissions.d.ts +32 -0
  196. package/dist/discord/permissions.d.ts.map +1 -0
  197. package/dist/discord/permissions.js +40 -0
  198. package/dist/discord/permissions.js.map +1 -0
  199. package/dist/discord/placeholder-registry-errors.d.ts +10 -0
  200. package/dist/discord/placeholder-registry-errors.d.ts.map +1 -0
  201. package/dist/discord/placeholder-registry-errors.js +18 -0
  202. package/dist/discord/placeholder-registry-errors.js.map +1 -0
  203. package/dist/discord/placeholder-registry.d.ts +53 -0
  204. package/dist/discord/placeholder-registry.d.ts.map +1 -0
  205. package/dist/discord/placeholder-registry.js +52 -0
  206. package/dist/discord/placeholder-registry.js.map +1 -0
  207. package/dist/discord/presenter.d.ts +26 -0
  208. package/dist/discord/presenter.d.ts.map +1 -0
  209. package/dist/discord/presenter.js +3 -0
  210. package/dist/discord/presenter.js.map +1 -0
  211. package/dist/discord/ui/card.d.ts +114 -0
  212. package/dist/discord/ui/card.d.ts.map +1 -0
  213. package/dist/discord/ui/card.js +162 -0
  214. package/dist/discord/ui/card.js.map +1 -0
  215. package/dist/discord/ui/color-input-errors.d.ts +12 -0
  216. package/dist/discord/ui/color-input-errors.d.ts.map +1 -0
  217. package/dist/discord/ui/color-input-errors.js +21 -0
  218. package/dist/discord/ui/color-input-errors.js.map +1 -0
  219. package/dist/discord/ui/color-input.d.ts +37 -0
  220. package/dist/discord/ui/color-input.d.ts.map +1 -0
  221. package/dist/discord/ui/color-input.js +166 -0
  222. package/dist/discord/ui/color-input.js.map +1 -0
  223. package/dist/discord/ui/colors.d.ts +44 -0
  224. package/dist/discord/ui/colors.d.ts.map +1 -0
  225. package/dist/discord/ui/colors.js +45 -0
  226. package/dist/discord/ui/colors.js.map +1 -0
  227. package/dist/discord/ui/embed.d.ts +16 -0
  228. package/dist/discord/ui/embed.d.ts.map +1 -0
  229. package/dist/discord/ui/embed.js +53 -0
  230. package/dist/discord/ui/embed.js.map +1 -0
  231. package/dist/discord/ui/message-attachments.d.ts +40 -0
  232. package/dist/discord/ui/message-attachments.d.ts.map +1 -0
  233. package/dist/discord/ui/message-attachments.js +27 -0
  234. package/dist/discord/ui/message-attachments.js.map +1 -0
  235. package/dist/errors/business-error.d.ts +41 -0
  236. package/dist/errors/business-error.d.ts.map +1 -0
  237. package/dist/errors/business-error.js +39 -0
  238. package/dist/errors/business-error.js.map +1 -0
  239. package/dist/errors/describe-error.d.ts +29 -0
  240. package/dist/errors/describe-error.d.ts.map +1 -0
  241. package/dist/errors/describe-error.js +49 -0
  242. package/dist/errors/describe-error.js.map +1 -0
  243. package/dist/errors/incident-ref.d.ts +7 -0
  244. package/dist/errors/incident-ref.d.ts.map +1 -0
  245. package/dist/errors/incident-ref.js +13 -0
  246. package/dist/errors/incident-ref.js.map +1 -0
  247. package/dist/errors/index.d.ts +4 -0
  248. package/dist/errors/index.d.ts.map +1 -0
  249. package/dist/errors/index.js +15 -0
  250. package/dist/errors/index.js.map +1 -0
  251. package/dist/i18n/business-message.d.ts +14 -0
  252. package/dist/i18n/business-message.d.ts.map +1 -0
  253. package/dist/i18n/business-message.js +19 -0
  254. package/dist/i18n/business-message.js.map +1 -0
  255. package/dist/i18n/catalog.d.ts +27 -0
  256. package/dist/i18n/catalog.d.ts.map +1 -0
  257. package/dist/i18n/catalog.js +25 -0
  258. package/dist/i18n/catalog.js.map +1 -0
  259. package/dist/i18n/errors.d.ts +10 -0
  260. package/dist/i18n/errors.d.ts.map +1 -0
  261. package/dist/i18n/errors.js +18 -0
  262. package/dist/i18n/errors.js.map +1 -0
  263. package/dist/i18n/index.d.ts +6 -0
  264. package/dist/i18n/index.d.ts.map +1 -0
  265. package/dist/i18n/index.js +16 -0
  266. package/dist/i18n/index.js.map +1 -0
  267. package/dist/i18n/locale.d.ts +22 -0
  268. package/dist/i18n/locale.d.ts.map +1 -0
  269. package/dist/i18n/locale.js +40 -0
  270. package/dist/i18n/locale.js.map +1 -0
  271. package/dist/i18n/translator.d.ts +35 -0
  272. package/dist/i18n/translator.d.ts.map +1 -0
  273. package/dist/i18n/translator.js +40 -0
  274. package/dist/i18n/translator.js.map +1 -0
  275. package/dist/logger.d.ts +28 -0
  276. package/dist/logger.d.ts.map +1 -0
  277. package/dist/logger.js +3 -0
  278. package/dist/logger.js.map +1 -0
  279. package/dist/module/module.d.ts +42 -0
  280. package/dist/module/module.d.ts.map +1 -0
  281. package/dist/module/module.js +3 -0
  282. package/dist/module/module.js.map +1 -0
  283. package/dist/persistence/database.d.ts +24 -0
  284. package/dist/persistence/database.d.ts.map +1 -0
  285. package/dist/persistence/database.js +3 -0
  286. package/dist/persistence/database.js.map +1 -0
  287. package/dist/resilience/retry.d.ts +36 -0
  288. package/dist/resilience/retry.d.ts.map +1 -0
  289. package/dist/resilience/retry.js +78 -0
  290. package/dist/resilience/retry.js.map +1 -0
  291. package/dist/scheduler/errors.d.ts +25 -0
  292. package/dist/scheduler/errors.d.ts.map +1 -0
  293. package/dist/scheduler/errors.js +45 -0
  294. package/dist/scheduler/errors.js.map +1 -0
  295. package/dist/scheduler/scheduler.d.ts +68 -0
  296. package/dist/scheduler/scheduler.d.ts.map +1 -0
  297. package/dist/scheduler/scheduler.js +162 -0
  298. package/dist/scheduler/scheduler.js.map +1 -0
  299. package/package.json +89 -0
  300. package/src/authz/authorizer.ts +76 -0
  301. package/src/authz/require-level-guard.ts +42 -0
  302. package/src/clock.ts +18 -0
  303. package/src/concurrency/keyed-queue.ts +50 -0
  304. package/src/concurrency/run-with-concurrency.ts +44 -0
  305. package/src/config/env.ts +58 -0
  306. package/src/config/errors.ts +21 -0
  307. package/src/datetime.ts +221 -0
  308. package/src/discord/api-errors.ts +91 -0
  309. package/src/discord/channel-exporter.ts +48 -0
  310. package/src/discord/command/autocomplete-dispatcher.ts +54 -0
  311. package/src/discord/command/autocomplete.ts +76 -0
  312. package/src/discord/command/context.ts +85 -0
  313. package/src/discord/command/cooldown-guard.ts +81 -0
  314. package/src/discord/command/create-command.ts +431 -0
  315. package/src/discord/command/create-context-menu-command.ts +227 -0
  316. package/src/discord/command/errors.ts +22 -0
  317. package/src/discord/command/guard.ts +54 -0
  318. package/src/discord/command/localization.ts +11 -0
  319. package/src/discord/command/options.ts +847 -0
  320. package/src/discord/command/permission-guard.ts +74 -0
  321. package/src/discord/command/router.ts +142 -0
  322. package/src/discord/command/types.ts +52 -0
  323. package/src/discord/components/component-router.ts +213 -0
  324. package/src/discord/components/interactive-message/index.ts +18 -0
  325. package/src/discord/components/interactive-message/interactive-message-collector.ts +374 -0
  326. package/src/discord/components/interactive-message/mount-interactive-message.ts +71 -0
  327. package/src/discord/components/paginator/index.ts +17 -0
  328. package/src/discord/components/paginator/mount-paginator.ts +69 -0
  329. package/src/discord/components/paginator/page-state.ts +45 -0
  330. package/src/discord/components/paginator/paginator-buttons.ts +60 -0
  331. package/src/discord/components/paginator/paginator-view.ts +61 -0
  332. package/src/discord/components/persistent-paginator.ts +197 -0
  333. package/src/discord/components/settings-editor/index.ts +41 -0
  334. package/src/discord/components/settings-editor/mount-settings-editor.ts +646 -0
  335. package/src/discord/components/settings-editor/navigation.ts +67 -0
  336. package/src/discord/components/settings-editor/settings-editor-card.view.ts +344 -0
  337. package/src/discord/components/settings-editor/settings-editor-field-modal.ts +538 -0
  338. package/src/discord/components/settings-editor/settings-editor-fields.ts +290 -0
  339. package/src/discord/components/settings-editor/settings-editor.view.ts +654 -0
  340. package/src/discord/events/create-event.ts +7 -0
  341. package/src/discord/events/event-router.ts +53 -0
  342. package/src/discord/events/types.ts +17 -0
  343. package/src/discord/i18n.ts +173 -0
  344. package/src/discord/interaction/button.ts +75 -0
  345. package/src/discord/interaction/interaction-router.ts +28 -0
  346. package/src/discord/interaction/modal.ts +505 -0
  347. package/src/discord/interaction/select-menu.ts +246 -0
  348. package/src/discord/permissions.ts +57 -0
  349. package/src/discord/placeholder-registry-errors.ts +11 -0
  350. package/src/discord/placeholder-registry.ts +77 -0
  351. package/src/discord/presenter.ts +26 -0
  352. package/src/discord/ui/card.ts +303 -0
  353. package/src/discord/ui/color-input-errors.ts +17 -0
  354. package/src/discord/ui/color-input.ts +179 -0
  355. package/src/discord/ui/colors.ts +58 -0
  356. package/src/discord/ui/embed.ts +64 -0
  357. package/src/discord/ui/message-attachments.ts +57 -0
  358. package/src/errors/business-error.ts +49 -0
  359. package/src/errors/describe-error.ts +73 -0
  360. package/src/errors/incident-ref.ts +10 -0
  361. package/src/errors/index.ts +12 -0
  362. package/src/i18n/business-message.ts +23 -0
  363. package/src/i18n/catalog.ts +41 -0
  364. package/src/i18n/errors.ts +11 -0
  365. package/src/i18n/index.ts +11 -0
  366. package/src/i18n/locale.ts +41 -0
  367. package/src/i18n/translator.ts +78 -0
  368. package/src/logger.ts +28 -0
  369. package/src/module/module.ts +42 -0
  370. package/src/persistence/database.ts +23 -0
  371. package/src/resilience/retry.ts +102 -0
  372. package/src/scheduler/errors.ts +40 -0
  373. package/src/scheduler/scheduler.ts +248 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A single log call. Mirrors the pino calling convention so call sites can pass
3
+ * either a bare message or a structured record followed by a message:
4
+ * `logger.error({ err }, "Failed to do the thing")`.
5
+ */
6
+ export interface LogFn {
7
+ (record: unknown, message?: string, ...args: unknown[]): void;
8
+ (message: string, ...args: unknown[]): void;
9
+ }
10
+ /**
11
+ * Structured application logger port.
12
+ *
13
+ * Declared structurally on purpose: `core` owns the contract and stays free of
14
+ * any vendor SDK, so the concrete implementation (pino, Sentry, a test double)
15
+ * lives in `infrastructure/logging` and can be swapped without touching call
16
+ * sites.
17
+ */
18
+ export interface Logger {
19
+ trace: LogFn;
20
+ debug: LogFn;
21
+ info: LogFn;
22
+ warn: LogFn;
23
+ error: LogFn;
24
+ fatal: LogFn;
25
+ /** Derive a logger that stamps `bindings` onto every record it emits. */
26
+ child(bindings: Record<string, unknown>): Logger;
27
+ }
28
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACrB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9D,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,MAAM;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,yEAAyE;IACzE,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CACjD"}
package/dist/logger.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import type { ContextMenuCommand, SlashCommand } from "../discord/command/types";
2
+ import type { ComponentHandler } from "../discord/components/component-router";
3
+ import type { DiscordEvent } from "../discord/events/types";
4
+ import type { Catalog } from "../i18n";
5
+ import type { ScheduledJob } from "../scheduler/scheduler";
6
+ /**
7
+ * A self-contained vertical feature slice. A module bundles everything it
8
+ * contributes to the bot — commands, gateway events, scheduled jobs — and an
9
+ * optional async `setup` for one-off work (creating indexes, migrations…).
10
+ *
11
+ * Modules are produced by factories that receive the application container (see
12
+ * `bootstrap/container.ts`), so all dependencies are injected — never imported
13
+ * as singletons. The core depends only on this output contract, never on how a
14
+ * module is wired.
15
+ */
16
+ export interface BotModule {
17
+ /** Stable identifier, used in logs. */
18
+ readonly name: string;
19
+ readonly commands?: readonly SlashCommand[];
20
+ /** Right-click entries on a member or a message (the Apps submenu). */
21
+ readonly contextMenuCommands?: readonly ContextMenuCommand[];
22
+ /** Persistent component/modal handlers (routed by `customId`). */
23
+ readonly components?: readonly ComponentHandler[];
24
+ readonly events?: readonly DiscordEvent[];
25
+ readonly jobs?: readonly ScheduledJob[];
26
+ /**
27
+ * The module's translation catalog (keys namespaced by module name). The
28
+ * composition root registers it into the shared registry at boot — a
29
+ * duplicate key across modules fails the boot, never a runtime lookup.
30
+ */
31
+ readonly translations?: Catalog;
32
+ /** One-off initialisation run once at boot, after the client is ready. */
33
+ setup?(): Promise<void> | void;
34
+ /**
35
+ * Symmetric counterpart of {@link setup}, awaited during graceful shutdown
36
+ * before the client and the database are closed. Release what the module owns
37
+ * and the framework cannot see — timers, collectors, third-party connections.
38
+ * A rejecting teardown is logged and never aborts the shutdown of the others.
39
+ */
40
+ teardown?(): Promise<void> | void;
41
+ }
42
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../src/module/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,uEAAuE;IACvE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC7D,kEAAkE;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,0EAA0E;IAC1E,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAClC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../src/module/module.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Driver-agnostic persistence port. It exists so the framework (boot, graceful
3
+ * shutdown) can own a database lifecycle without ever naming a vendor: adding a
4
+ * SQL or Redis driver must not require touching this file, only adding an
5
+ * adapter that satisfies this contract.
6
+ *
7
+ * Deliberately narrow: querying is not part of it. Query APIs differ too much
8
+ * between engines to be usefully abstracted here, so each adapter exposes its
9
+ * own (Mongo collections, SQL statements…) and repositories in the module
10
+ * `infrastructure/` layer are the only code allowed to use it.
11
+ */
12
+ export interface DatabaseConnection {
13
+ /**
14
+ * Discriminant identifying the concrete driver. Adapters narrow it to a
15
+ * string literal so consumers can select an implementation by comparison
16
+ * rather than by `instanceof`, which would force a vendor import.
17
+ */
18
+ readonly driver: string;
19
+ /** Open the connection. Idempotent: safe to call from several call sites. */
20
+ connect(): Promise<void>;
21
+ /** Release the connection on graceful shutdown. */
22
+ close(): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/persistence/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,mDAAmD;IACnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/persistence/database.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ export interface RetryOptions {
2
+ /** Total number of tries, first attempt included. */
3
+ attempts?: number;
4
+ initialDelayMs?: number;
5
+ maxDelayMs?: number;
6
+ backoffFactor?: number;
7
+ jitterRatio?: number;
8
+ /** Decide whether a failure is worth another try. Default: anything but a permanent failure. */
9
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
10
+ /** Injected so tests advance instantly instead of waiting on real timers. */
11
+ sleep?: (delayMs: number) => Promise<void>;
12
+ /** Injected randomness keeps the jitter deterministic under test. */
13
+ random?: () => number;
14
+ }
15
+ /** Everything the backoff needs once caller defaults have been resolved. */
16
+ export interface BackoffOptions {
17
+ initialDelayMs: number;
18
+ maxDelayMs: number;
19
+ backoffFactor: number;
20
+ jitterRatio: number;
21
+ random: () => number;
22
+ }
23
+ /**
24
+ * Exponential backoff with jitter. The jitter matters more than the curve itself:
25
+ * without it every caller that failed during the same outage retries in lockstep
26
+ * and hammers the dependency back down.
27
+ */
28
+ export declare function computeBackoffDelay(attempt: number, options: BackoffOptions): number;
29
+ /**
30
+ * Run `task`, retrying transient failures until the attempt budget is spent, then
31
+ * rethrowing the last error. Vendor-free by design: callers pass their own
32
+ * `shouldRetry` (e.g. built on the Discord API predicates) so this stays usable
33
+ * for any I/O.
34
+ */
35
+ export declare function withRetry<T>(task: () => Promise<T>, options?: RetryOptions): Promise<T>;
36
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/resilience/retry.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,6EAA6E;IAC7E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,MAAM,CAAC;CACrB;AAiBD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAKpF;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAkCjG"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeBackoffDelay = computeBackoffDelay;
4
+ exports.withRetry = withRetry;
5
+ const errors_1 = require("../errors");
6
+ const MIN_ATTEMPTS = 1;
7
+ const DEFAULT_ATTEMPTS = 3;
8
+ const DEFAULT_INITIAL_DELAY_MS = 200;
9
+ const DEFAULT_MAX_DELAY_MS = 5_000;
10
+ const DEFAULT_BACKOFF_FACTOR = 2;
11
+ /** Share of the computed delay kept fixed; the rest is randomised (full jitter on 1 - RATIO). */
12
+ const DEFAULT_JITTER_RATIO = 0.5;
13
+ /**
14
+ * A {@link BusinessError} describes an expected, user-facing failure (bad input,
15
+ * missing entity, conflict): repeating the call cannot change the outcome, so it
16
+ * is never retried. Everything else is assumed transient until proven otherwise.
17
+ */
18
+ function isTransient(error) {
19
+ return !(error instanceof errors_1.BusinessError);
20
+ }
21
+ function defaultSleep(delayMs) {
22
+ return new Promise((resolve) => {
23
+ setTimeout(resolve, delayMs);
24
+ });
25
+ }
26
+ /**
27
+ * Exponential backoff with jitter. The jitter matters more than the curve itself:
28
+ * without it every caller that failed during the same outage retries in lockstep
29
+ * and hammers the dependency back down.
30
+ */
31
+ function computeBackoffDelay(attempt, options) {
32
+ const exponential = options.initialDelayMs * options.backoffFactor ** (attempt - 1);
33
+ const capped = Math.min(exponential, options.maxDelayMs);
34
+ const fixed = capped * options.jitterRatio;
35
+ return Math.round(fixed + (capped - fixed) * options.random());
36
+ }
37
+ /**
38
+ * Run `task`, retrying transient failures until the attempt budget is spent, then
39
+ * rethrowing the last error. Vendor-free by design: callers pass their own
40
+ * `shouldRetry` (e.g. built on the Discord API predicates) so this stays usable
41
+ * for any I/O.
42
+ */
43
+ async function withRetry(task, options = {}) {
44
+ // `Math.max` propagates NaN, and `attempt <= NaN` is false on the first pass:
45
+ // an unusable budget would skip the task entirely and reject with `undefined`.
46
+ // Floor to a valid count so a bad config still runs the task once.
47
+ const requested = options.attempts ?? DEFAULT_ATTEMPTS;
48
+ const attempts = Number.isFinite(requested)
49
+ ? Math.max(MIN_ATTEMPTS, Math.floor(requested))
50
+ : DEFAULT_ATTEMPTS;
51
+ const shouldRetry = options.shouldRetry ?? isTransient;
52
+ const sleep = options.sleep ?? defaultSleep;
53
+ const backoff = {
54
+ initialDelayMs: options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS,
55
+ maxDelayMs: options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
56
+ backoffFactor: options.backoffFactor ?? DEFAULT_BACKOFF_FACTOR,
57
+ jitterRatio: options.jitterRatio ?? DEFAULT_JITTER_RATIO,
58
+ random: options.random ?? Math.random,
59
+ };
60
+ let lastError;
61
+ for (let attempt = MIN_ATTEMPTS; attempt <= attempts; attempt += 1) {
62
+ try {
63
+ return await task();
64
+ }
65
+ catch (error) {
66
+ lastError = error;
67
+ const isLastAttempt = attempt === attempts;
68
+ if (isLastAttempt || !shouldRetry(error, attempt)) {
69
+ throw error;
70
+ }
71
+ await sleep(computeBackoffDelay(attempt, backoff));
72
+ }
73
+ }
74
+ // Unreachable — the loop always returns or throws — but the compiler cannot
75
+ // prove the budget is non-empty, and a silent `undefined` would be worse.
76
+ throw lastError;
77
+ }
78
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/resilience/retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Failures raised while a job's schedule is resolved. They live apart from the
3
+ * scheduler so a composition root can catch or re-map them without importing the
4
+ * scheduling logic, and so the scheduler file stays focused on behaviour.
5
+ */
6
+ /** Base class for every malformed-schedule failure, so callers can catch the family. */
7
+ export declare abstract class JobScheduleError extends Error {
8
+ }
9
+ /** Raised when a job declares both a cron expression and a fixed interval. */
10
+ export declare class AmbiguousJobScheduleError extends JobScheduleError {
11
+ constructor(jobName: string);
12
+ }
13
+ /** Raised when a job declares neither a cron expression nor a fixed interval. */
14
+ export declare class MissingJobScheduleError extends JobScheduleError {
15
+ constructor(jobName: string);
16
+ }
17
+ /** Raised when a fixed interval is not a usable delay (non-finite or non-positive). */
18
+ export declare class InvalidJobIntervalError extends JobScheduleError {
19
+ constructor(jobName: string, intervalMs: number);
20
+ }
21
+ /** Raised when two jobs declare the same stable identifier. */
22
+ export declare class DuplicateJobNameError extends JobScheduleError {
23
+ constructor(jobName: string);
24
+ }
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/scheduler/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wFAAwF;AACxF,8BAAsB,gBAAiB,SAAQ,KAAK;CAAG;AAEvD,8EAA8E;AAC9E,qBAAa,yBAA0B,SAAQ,gBAAgB;gBAClD,OAAO,EAAE,MAAM;CAI3B;AAED,iFAAiF;AACjF,qBAAa,uBAAwB,SAAQ,gBAAgB;gBAChD,OAAO,EAAE,MAAM;CAI3B;AAED,uFAAuF;AACvF,qBAAa,uBAAwB,SAAQ,gBAAgB;gBAChD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAI/C;AAED,+DAA+D;AAC/D,qBAAa,qBAAsB,SAAQ,gBAAgB;gBAC9C,OAAO,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Failures raised while a job's schedule is resolved. They live apart from the
4
+ * scheduler so a composition root can catch or re-map them without importing the
5
+ * scheduling logic, and so the scheduler file stays focused on behaviour.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.DuplicateJobNameError = exports.InvalidJobIntervalError = exports.MissingJobScheduleError = exports.AmbiguousJobScheduleError = exports.JobScheduleError = void 0;
9
+ /** Base class for every malformed-schedule failure, so callers can catch the family. */
10
+ class JobScheduleError extends Error {
11
+ }
12
+ exports.JobScheduleError = JobScheduleError;
13
+ /** Raised when a job declares both a cron expression and a fixed interval. */
14
+ class AmbiguousJobScheduleError extends JobScheduleError {
15
+ constructor(jobName) {
16
+ super(`Job ${jobName} declares both "cron" and "intervalMs": pick exactly one`);
17
+ this.name = "AmbiguousJobScheduleError";
18
+ }
19
+ }
20
+ exports.AmbiguousJobScheduleError = AmbiguousJobScheduleError;
21
+ /** Raised when a job declares neither a cron expression nor a fixed interval. */
22
+ class MissingJobScheduleError extends JobScheduleError {
23
+ constructor(jobName) {
24
+ super(`Job ${jobName} declares no schedule: it needs either "cron" or "intervalMs"`);
25
+ this.name = "MissingJobScheduleError";
26
+ }
27
+ }
28
+ exports.MissingJobScheduleError = MissingJobScheduleError;
29
+ /** Raised when a fixed interval is not a usable delay (non-finite or non-positive). */
30
+ class InvalidJobIntervalError extends JobScheduleError {
31
+ constructor(jobName, intervalMs) {
32
+ super(`Job ${jobName} declares an invalid "intervalMs" (${intervalMs}): expected > 0`);
33
+ this.name = "InvalidJobIntervalError";
34
+ }
35
+ }
36
+ exports.InvalidJobIntervalError = InvalidJobIntervalError;
37
+ /** Raised when two jobs declare the same stable identifier. */
38
+ class DuplicateJobNameError extends JobScheduleError {
39
+ constructor(jobName) {
40
+ super(`Job ${jobName} is declared more than once: scheduled job names must be unique`);
41
+ this.name = "DuplicateJobNameError";
42
+ }
43
+ }
44
+ exports.DuplicateJobNameError = DuplicateJobNameError;
45
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/scheduler/errors.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,wFAAwF;AACxF,MAAsB,gBAAiB,SAAQ,KAAK;CAAG;AAAvD,4CAAuD;AAEvD,8EAA8E;AAC9E,MAAa,yBAA0B,SAAQ,gBAAgB;IAC9D,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,OAAO,0DAA0D,CAAC,CAAC;QAChF,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IACzC,CAAC;CACD;AALD,8DAKC;AAED,iFAAiF;AACjF,MAAa,uBAAwB,SAAQ,gBAAgB;IAC5D,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,OAAO,+DAA+D,CAAC,CAAC;QACrF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACvC,CAAC;CACD;AALD,0DAKC;AAED,uFAAuF;AACvF,MAAa,uBAAwB,SAAQ,gBAAgB;IAC5D,YAAY,OAAe,EAAE,UAAkB;QAC9C,KAAK,CAAC,OAAO,OAAO,sCAAsC,UAAU,iBAAiB,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACvC,CAAC;CACD;AALD,0DAKC;AAED,+DAA+D;AAC/D,MAAa,qBAAsB,SAAQ,gBAAgB;IAC1D,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,OAAO,iEAAiE,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACrC,CAAC;CACD;AALD,sDAKC"}
@@ -0,0 +1,68 @@
1
+ import type { Logger } from "../logger";
2
+ /** What every job carries, whatever schedule drives it. */
3
+ interface BaseScheduledJob {
4
+ /** Stable identifier, used in logs. */
5
+ readonly name: string;
6
+ /**
7
+ * Also run once when the scheduler starts, on top of the recurring schedule —
8
+ * the usual need for state reconciliation after a restart.
9
+ */
10
+ readonly runOnStart?: boolean;
11
+ run(): Promise<void> | void;
12
+ }
13
+ /** A job driven by a cron expression. */
14
+ export interface CronScheduledJob extends BaseScheduledJob {
15
+ /** Standard cron expression (5 or 6 fields). */
16
+ readonly cron: string;
17
+ readonly intervalMs?: never;
18
+ }
19
+ /** A job driven by a plain fixed delay between two runs. */
20
+ export interface IntervalScheduledJob extends BaseScheduledJob {
21
+ /** Delay between two runs, in milliseconds. */
22
+ readonly intervalMs: number;
23
+ readonly cron?: never;
24
+ }
25
+ /**
26
+ * A recurring job declared by a module. Kept free of any scheduling-library
27
+ * type so modules depend only on this contract, not on node-cron. The union
28
+ * makes `cron` and `intervalMs` mutually exclusive at compile time; the
29
+ * scheduler re-checks at runtime because a job may be built from untyped config.
30
+ */
31
+ export type ScheduledJob = CronScheduledJob | IntervalScheduledJob;
32
+ export interface SchedulerOptions {
33
+ /** IANA timezone the cron expressions are evaluated against. */
34
+ timezone?: string;
35
+ }
36
+ /** Scheduler control exposed to modules through dependency injection. */
37
+ export interface Scheduler {
38
+ readonly started: boolean;
39
+ start(jobs: Iterable<ScheduledJob>): void;
40
+ isValidCron(expression: string): boolean;
41
+ rescheduleCron(jobName: string, expression: string): boolean;
42
+ stop(): void;
43
+ }
44
+ /**
45
+ * Schedules {@link ScheduledJob}s via node-cron, wrapping each run so a thrown
46
+ * error is logged rather than crashing the process. The concrete library is an
47
+ * implementation detail confined to this file.
48
+ */
49
+ export declare class CronScheduler implements Scheduler {
50
+ private readonly logger;
51
+ private readonly options;
52
+ private readonly tasks;
53
+ private running;
54
+ constructor(logger: Logger, options?: SchedulerOptions);
55
+ get started(): boolean;
56
+ start(jobs: Iterable<ScheduledJob>): void;
57
+ isValidCron(expression: string): boolean;
58
+ /** Replace one running cron task without restarting the scheduler or the bot. */
59
+ rescheduleCron(jobName: string, expression: string): boolean;
60
+ private scheduleCron;
61
+ private scheduleInterval;
62
+ /** Run a job so a thrown error is logged instead of reaching the process. */
63
+ private runIsolated;
64
+ /** Stop every scheduled task (used on graceful shutdown). */
65
+ stop(): void;
66
+ }
67
+ export {};
68
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/scheduler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,2DAA2D;AAC3D,UAAU,gBAAgB;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5B;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACzD,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;CAC5B;AAED,4DAA4D;AAC5D,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC7D,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAEnE,MAAM,WAAW,gBAAgB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;IAC1C,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,IAAI,IAAI,IAAI,CAAC;CACb;AAwCD;;;;GAIG;AACH,qBAAa,aAAc,YAAW,SAAS;IAK7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,OAAO,CAAS;gBAGN,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB;IAGhD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,IAAI;IA4BzC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIxC,iFAAiF;IACjF,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAyB5D,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,gBAAgB;IA+BxB,6EAA6E;YAC/D,WAAW;IAQzB,6DAA6D;IAC7D,IAAI,IAAI,IAAI;CAOZ"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CronScheduler = void 0;
7
+ const node_cron_1 = __importDefault(require("node-cron"));
8
+ const errors_1 = require("../scheduler/errors");
9
+ function describeError(error) {
10
+ return error instanceof Error ? error.message : String(error);
11
+ }
12
+ /**
13
+ * The schedule failure of a single job, or `null` when it is well-formed.
14
+ * Returned rather than thrown: `start()` runs inside the `ready` handler, on an
15
+ * already-online bot, so one malformed job must not cancel every other module's
16
+ * jobs. The caller logs it and skips that job alone.
17
+ */
18
+ function scheduleFailure(job) {
19
+ const { name, cron: expression, intervalMs } = job;
20
+ const hasCron = expression !== undefined;
21
+ const hasInterval = intervalMs !== undefined;
22
+ if (hasCron && hasInterval) {
23
+ return new errors_1.AmbiguousJobScheduleError(name);
24
+ }
25
+ if (!hasCron && !hasInterval) {
26
+ return new errors_1.MissingJobScheduleError(name);
27
+ }
28
+ if (intervalMs !== undefined && (!Number.isFinite(intervalMs) || intervalMs <= 0)) {
29
+ return new errors_1.InvalidJobIntervalError(name, intervalMs);
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Schedules {@link ScheduledJob}s via node-cron, wrapping each run so a thrown
35
+ * error is logged rather than crashing the process. The concrete library is an
36
+ * implementation detail confined to this file.
37
+ */
38
+ class CronScheduler {
39
+ logger;
40
+ options;
41
+ tasks = new Map();
42
+ running = false;
43
+ constructor(logger, options = {}) {
44
+ this.logger = logger;
45
+ this.options = options;
46
+ }
47
+ get started() {
48
+ return this.running;
49
+ }
50
+ start(jobs) {
51
+ this.running = true;
52
+ const declared = [...jobs];
53
+ for (const job of declared) {
54
+ if (this.tasks.has(job.name)) {
55
+ const failure = new errors_1.DuplicateJobNameError(job.name);
56
+ this.logger.error({ job: job.name, err: failure.message }, "Job schedule invalid, skipped");
57
+ continue;
58
+ }
59
+ // A malformed schedule disables that job only. Skipping is logged at
60
+ // error level so it is not mistaken for a working job.
61
+ const failure = scheduleFailure(job);
62
+ if (failure !== null) {
63
+ this.logger.error({ job: job.name, err: failure.message }, "Job schedule invalid, skipped");
64
+ continue;
65
+ }
66
+ const stopTask = job.cron !== undefined ? this.scheduleCron(job) : this.scheduleInterval(job);
67
+ // An unusable schedule already disabled the job, so a boot run would
68
+ // fire something the caller has been told is not running.
69
+ if (stopTask !== null) {
70
+ this.tasks.set(job.name, { job, stop: stopTask });
71
+ }
72
+ if (stopTask !== null && job.runOnStart === true) {
73
+ void this.runIsolated(job);
74
+ }
75
+ }
76
+ }
77
+ isValidCron(expression) {
78
+ return node_cron_1.default.validate(expression);
79
+ }
80
+ /** Replace one running cron task without restarting the scheduler or the bot. */
81
+ rescheduleCron(jobName, expression) {
82
+ const current = this.tasks.get(jobName);
83
+ if (current === undefined || current.job.cron === undefined) {
84
+ this.logger.error({ job: jobName }, "Cron job not found, reschedule skipped");
85
+ return false;
86
+ }
87
+ if (!this.isValidCron(expression)) {
88
+ this.logger.error({ job: jobName, cron: expression }, "Invalid cron expression, reschedule skipped");
89
+ return false;
90
+ }
91
+ const replacement = { ...current.job, cron: expression };
92
+ const replacementStop = this.scheduleCron(replacement, false);
93
+ if (replacementStop === null) {
94
+ return false;
95
+ }
96
+ current.stop();
97
+ this.tasks.set(jobName, { job: replacement, stop: replacementStop });
98
+ this.logger.info({ job: jobName, cron: expression }, "Scheduled cron job updated");
99
+ return true;
100
+ }
101
+ scheduleCron(job, logRegistration = true) {
102
+ if (!this.isValidCron(job.cron)) {
103
+ this.logger.error({ job: job.name, cron: job.cron }, "Invalid cron expression, job skipped");
104
+ return null;
105
+ }
106
+ const task = node_cron_1.default.schedule(job.cron, () => this.runIsolated(job), this.options.timezone ? { timezone: this.options.timezone } : undefined);
107
+ const stop = () => {
108
+ task.stop();
109
+ };
110
+ if (logRegistration) {
111
+ this.logger.info({ job: job.name, cron: job.cron }, "Scheduled job registered");
112
+ }
113
+ return stop;
114
+ }
115
+ scheduleInterval(job) {
116
+ // Rescheduled after each run rather than `setInterval`: a run slower than
117
+ // its own interval would otherwise overlap itself, and a reconciliation job
118
+ // racing a previous pass is exactly the interleaving the KeyedQueue exists
119
+ // to prevent. The contract is "at most one run at a time".
120
+ let timer = null;
121
+ let stopped = false;
122
+ const scheduleNext = () => {
123
+ if (stopped) {
124
+ return;
125
+ }
126
+ timer = setTimeout(() => {
127
+ void this.runIsolated(job).finally(scheduleNext);
128
+ }, job.intervalMs);
129
+ // Nothing here should hold the event loop open by itself; `stop()` clears
130
+ // the timer on shutdown, and unref keeps a forgotten job from doing so.
131
+ timer.unref?.();
132
+ };
133
+ scheduleNext();
134
+ const stop = () => {
135
+ stopped = true;
136
+ if (timer !== null) {
137
+ clearTimeout(timer);
138
+ }
139
+ };
140
+ this.logger.info({ job: job.name, intervalMs: job.intervalMs }, "Scheduled job registered");
141
+ return stop;
142
+ }
143
+ /** Run a job so a thrown error is logged instead of reaching the process. */
144
+ async runIsolated(job) {
145
+ try {
146
+ await job.run();
147
+ }
148
+ catch (error) {
149
+ this.logger.error({ job: job.name, err: describeError(error) }, "Scheduled job failed");
150
+ }
151
+ }
152
+ /** Stop every scheduled task (used on graceful shutdown). */
153
+ stop() {
154
+ for (const task of this.tasks.values()) {
155
+ task.stop();
156
+ }
157
+ this.tasks.clear();
158
+ this.running = false;
159
+ }
160
+ }
161
+ exports.CronScheduler = CronScheduler;
162
+ //# sourceMappingURL=scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/scheduler/scheduler.ts"],"names":[],"mappings":"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"}