@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,61 @@
1
+ import type { ActionRowBuilder, ButtonBuilder, EmbedBuilder } from "discord.js";
2
+ import type {
3
+ InteractiveMessagePayload,
4
+ InteractiveView,
5
+ } from "@/discord/components/interactive-message";
6
+ import { type PageState, selectPageItems } from "@/discord/components/paginator/page-state";
7
+ import { type Button, createActionRow } from "@/discord/interaction/button";
8
+
9
+ /** Builds the embed shown for one page; supplied by the caller. */
10
+ export type PaginatorRender<T> = (
11
+ pageItems: T[],
12
+ page: number,
13
+ count: number,
14
+ total: number,
15
+ ) => EmbedBuilder;
16
+
17
+ /**
18
+ * The message payload shape shared by the initial reply and every update — the
19
+ * generic interactive-message payload, named here for the paginator's callers.
20
+ */
21
+ export type PaginatorPayload = InteractiveMessagePayload<PageState>;
22
+
23
+ export interface PaginatorViewDeps<T> {
24
+ items: T[];
25
+ pageSize: number;
26
+ count: number;
27
+ render: PaginatorRender<T>;
28
+ buttons: Button<PageState>[];
29
+ }
30
+
31
+ /**
32
+ * Turns a {@link PageState} into a message payload. Everything the paginator
33
+ * sends to Discord goes through here, so the collector never builds a payload of
34
+ * its own and the two renders can never drift apart.
35
+ */
36
+ export type PaginatorView = InteractiveView<PageState>;
37
+
38
+ export function createPaginatorView<T>(deps: PaginatorViewDeps<T>): PaginatorView {
39
+ const { items, pageSize, count, render, buttons } = deps;
40
+
41
+ function controls(state: PageState, disableAll: boolean): ActionRowBuilder<ButtonBuilder>[] {
42
+ // A single page has nothing to navigate to, so it carries no action row at all.
43
+ if (count <= 1) {
44
+ return [];
45
+ }
46
+ return [createActionRow(buttons, state, { disableAll })];
47
+ }
48
+
49
+ return {
50
+ render(state) {
51
+ const pageItems = selectPageItems(items, state, pageSize);
52
+ return {
53
+ embeds: [render(pageItems, state.page, count, items.length)],
54
+ components: controls(state, false),
55
+ };
56
+ },
57
+ disabledControls(state) {
58
+ return { components: controls(state, true) };
59
+ },
60
+ };
61
+ }
@@ -0,0 +1,197 @@
1
+ import type { ActionRowBuilder, ButtonBuilder, EmbedBuilder } from "discord.js";
2
+ import {
3
+ type ComponentHandler,
4
+ type ComponentRuntime,
5
+ createComponentHandler,
6
+ openToAnyone,
7
+ type RoutableInteraction,
8
+ } from "@/discord/components/component-router";
9
+ import { toMessageEditOptions } from "@/discord/components/interactive-message";
10
+ import {
11
+ clampPage,
12
+ isFirstPage,
13
+ isLastPage,
14
+ nextPage,
15
+ type PageState,
16
+ previousPage,
17
+ } from "@/discord/components/paginator/page-state";
18
+ import {
19
+ type PaginatorLabels,
20
+ resolveLabels,
21
+ } from "@/discord/components/paginator/paginator-buttons";
22
+ import type { PaginatorPayload } from "@/discord/components/paginator/paginator-view";
23
+ import { createActionRow, createButton } from "@/discord/interaction/button";
24
+
25
+ /** Separator the {@link ComponentRouter} already matches a handler prefix on. */
26
+ const ID_SEPARATOR = ":";
27
+ /** Marks the segment holding the encoded page, so other state can be added later. */
28
+ const PAGE_SEGMENT = "page";
29
+ /** Pages are 1-based, mirroring {@link PageState}. */
30
+ const FIRST_PAGE = 1;
31
+
32
+ /**
33
+ * A page total is whatever the source reports, which may be 0 for an emptied
34
+ * dataset or non-finite from a miscounted query. Both would push the 1-based
35
+ * page index out of range, so treat them as a single empty page.
36
+ */
37
+ function normalisePageCount(pageCount: number): number {
38
+ if (!Number.isFinite(pageCount)) {
39
+ return FIRST_PAGE;
40
+ }
41
+ return Math.max(FIRST_PAGE, Math.floor(pageCount));
42
+ }
43
+
44
+ /** One rendered page: what the caller's data source produces for a page index. */
45
+ export interface PersistentPage {
46
+ embed: EmbedBuilder;
47
+ /** Total number of pages, re-evaluated on every fetch since the data may have changed. */
48
+ pageCount: number;
49
+ }
50
+
51
+ /**
52
+ * Produces the page at `page` (1-based). Asynchronous and injected because a
53
+ * persistent paginator cannot keep the dataset in memory: after a restart the
54
+ * only surviving state is the customId, so every click re-reads the source.
55
+ */
56
+ export type PersistentPageFetcher = (page: number) => Promise<PersistentPage>;
57
+
58
+ export interface PersistentPaginatorOptions {
59
+ /** Handler prefix; navigation buttons extend it with the target page. */
60
+ customId: string;
61
+ fetchPage: PersistentPageFetcher;
62
+ labels?: Partial<PaginatorLabels>;
63
+ }
64
+
65
+ export interface PersistentPaginator {
66
+ readonly customId: string;
67
+ /** Payload for the first message; the caller sends it however it replies. */
68
+ render(page?: number): Promise<PaginatorPayload>;
69
+ /** Register this on the module's `components` so clicks keep working after a restart. */
70
+ readonly handler: ComponentHandler;
71
+ }
72
+
73
+ /** Build the customId a navigation button carries: `<prefix>:page:<n>`. */
74
+ export function encodePaginatorPage(customId: string, page: number): string {
75
+ return [customId, PAGE_SEGMENT, String(page)].join(ID_SEPARATOR);
76
+ }
77
+
78
+ /**
79
+ * Read the page back out of a clicked customId. Returns `null` for anything that
80
+ * is not a well-formed page reference — a message from an older deploy may carry
81
+ * a shape this build no longer writes, and that must not throw.
82
+ */
83
+ export function decodePaginatorPage(customId: string, clickedId: string): number | null {
84
+ const prefix = `${customId}${ID_SEPARATOR}${PAGE_SEGMENT}${ID_SEPARATOR}`;
85
+ if (!clickedId.startsWith(prefix)) {
86
+ return null;
87
+ }
88
+ const raw = clickedId.slice(prefix.length);
89
+ // `Number` alone accepts "", " " and "1e3"; only plain digits are ours.
90
+ if (!/^\d+$/.test(raw)) {
91
+ return null;
92
+ }
93
+ const page = Number(raw);
94
+ if (page < FIRST_PAGE) {
95
+ return null;
96
+ }
97
+ return page;
98
+ }
99
+
100
+ /**
101
+ * A paginator whose whole state lives in the component customId, routed by the
102
+ * {@link ComponentRouter}. Unlike `mountPaginator` — a collector holding items
103
+ * and page in a closure, owner-scoped and dead after an idle timeout — this one
104
+ * has no memory: it survives restarts and never expires, at the cost of refetching
105
+ * the page on every click. Use it for long-lived, shared messages; use the
106
+ * collector for transient, single-user flows.
107
+ */
108
+ export function createPersistentPaginator(
109
+ options: PersistentPaginatorOptions,
110
+ ): PersistentPaginator {
111
+ const { customId, fetchPage } = options;
112
+ const labels = resolveLabels(options.labels);
113
+
114
+ function controls(state: PageState, count: number): ActionRowBuilder<ButtonBuilder>[] {
115
+ // A single page has nothing to navigate to, so it carries no action row at all.
116
+ if (count <= 1) {
117
+ return [];
118
+ }
119
+ // No `onClick`: behaviour is carried by the encoded customId and resolved by
120
+ // the router, which is what lets these buttons outlive the process.
121
+ const buttons = [
122
+ createButton<PageState>({
123
+ id: encodePaginatorPage(customId, previousPage(state, count).page),
124
+ label: labels.previous,
125
+ emoji: labels.previousEmoji,
126
+ style: "secondary",
127
+ disabled: (current) => isFirstPage(current),
128
+ }),
129
+ createButton<PageState>({
130
+ id: encodePaginatorPage(customId, nextPage(state, count).page),
131
+ label: labels.next,
132
+ emoji: labels.nextEmoji,
133
+ style: "secondary",
134
+ disabled: (current) => isLastPage(current, count),
135
+ }),
136
+ ];
137
+ return [createActionRow(buttons, state)];
138
+ }
139
+
140
+ /**
141
+ * Fetch a page, then clamp it against the freshly returned total. The total is
142
+ * only known after a fetch, so an out-of-range request (a stale button, a
143
+ * shrunken dataset) costs one extra read rather than an empty page.
144
+ */
145
+ async function loadPage(requested: number): Promise<PaginatorPayload> {
146
+ const asked = Math.max(requested, FIRST_PAGE);
147
+ const fetched = await fetchPage(asked);
148
+ // A source that reports no page (emptied dataset) or a nonsensical total
149
+ // must not clamp the index to 0: the contract is 1-based, and refetching an
150
+ // index the caller was never told it could receive is worse than rendering
151
+ // the empty first page it already returned.
152
+ const pageCount = normalisePageCount(fetched.pageCount);
153
+ const page = clampPage(asked, pageCount);
154
+ const resolved = page === asked ? fetched : await fetchPage(page);
155
+ return {
156
+ embeds: [resolved.embed],
157
+ components: controls({ page }, normalisePageCount(resolved.pageCount)),
158
+ };
159
+ }
160
+
161
+ async function handleClick(
162
+ interaction: RoutableInteraction,
163
+ runtime: ComponentRuntime,
164
+ ): Promise<void> {
165
+ if (!interaction.isButton()) {
166
+ return;
167
+ }
168
+ const decoded = decodePaginatorPage(customId, interaction.customId);
169
+ if (decoded === null) {
170
+ runtime.logger.warn(
171
+ { customId: interaction.customId },
172
+ "Unreadable paginator page, falling back to the first page",
173
+ );
174
+ }
175
+ // Acknowledge before fetching: a persistent paginator reads its source on
176
+ // every click by design, and Discord invalidates an interaction left
177
+ // unanswered for 3s — the click would be lost with the user still looking
178
+ // at the stale page. `deferUpdate` buys the full edit window instead.
179
+ await interaction.deferUpdate();
180
+ const payload = await loadPage(decoded ?? FIRST_PAGE);
181
+ await interaction.editReply(
182
+ toMessageEditOptions(payload, [...interaction.message.attachments.values()]),
183
+ );
184
+ }
185
+
186
+ return {
187
+ customId,
188
+ render: (page = FIRST_PAGE) => loadPage(page),
189
+ handler: createComponentHandler({
190
+ customId,
191
+ // Navigation only re-renders what the message already showed to
192
+ // whoever can see the channel; it grants no access of its own.
193
+ authorize: openToAnyone("paging a message reveals nothing its reader could not already see"),
194
+ handle: handleClick,
195
+ }),
196
+ };
197
+ }
@@ -0,0 +1,41 @@
1
+ export {
2
+ mountSettingsEditor,
3
+ type SettingsEditorOptions,
4
+ type SettingsEditorWrite,
5
+ } from "@/discord/components/settings-editor/mount-settings-editor";
6
+ export {
7
+ currentLevelKey,
8
+ type SettingsEditorLevel,
9
+ } from "@/discord/components/settings-editor/navigation";
10
+ export {
11
+ editorComponentIds,
12
+ MAX_SELECT_OPTION_TEXT,
13
+ MAX_SELECT_OPTIONS,
14
+ SETTINGS_EDITOR_RESET_CONFIRMING,
15
+ type SettingsEditorCardChrome,
16
+ type SettingsEditorChrome,
17
+ type SettingsEditorEmbedsChrome,
18
+ type SettingsEditorIds,
19
+ type SettingsEditorState,
20
+ } from "@/discord/components/settings-editor/settings-editor.view";
21
+ export {
22
+ fieldButtonId,
23
+ MAX_CARD_BUTTON_LEVEL_ENTRIES,
24
+ MAX_CARD_LEVEL_ENTRIES,
25
+ } from "@/discord/components/settings-editor/settings-editor-card.view";
26
+ export type {
27
+ SettingsEditorGroupSubmission,
28
+ SettingsEditorSubmission,
29
+ } from "@/discord/components/settings-editor/settings-editor-field-modal";
30
+ export {
31
+ MAX_MODAL_COMPONENTS,
32
+ MAX_MODAL_INPUT_LENGTH,
33
+ pickedFieldValue,
34
+ type SettingsEditorChoice,
35
+ type SettingsEditorField,
36
+ type SettingsEditorFieldValue,
37
+ type SettingsEditorGroupField,
38
+ type SettingsEditorPickerField,
39
+ type SettingsEditorValueField,
40
+ textFieldValue,
41
+ } from "@/discord/components/settings-editor/settings-editor-fields";