@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,505 @@
1
+ import {
2
+ type Attachment,
3
+ ChannelSelectMenuBuilder,
4
+ type ChannelType,
5
+ CheckboxBuilder,
6
+ FileUploadBuilder,
7
+ LabelBuilder,
8
+ ModalBuilder,
9
+ type ModalSubmitInteraction,
10
+ RoleSelectMenuBuilder,
11
+ StringSelectMenuBuilder,
12
+ StringSelectMenuOptionBuilder,
13
+ TextInputBuilder,
14
+ TextInputStyle,
15
+ UserSelectMenuBuilder,
16
+ } from "discord.js";
17
+
18
+ export type TextFieldStyleName = "short" | "paragraph";
19
+
20
+ const TEXT_FIELD_STYLES: Record<TextFieldStyleName, TextInputStyle> = {
21
+ short: TextInputStyle.Short,
22
+ paragraph: TextInputStyle.Paragraph,
23
+ };
24
+
25
+ /** Separates a modal's static id from the dynamic state appended for routing. */
26
+ const CUSTOM_ID_SEPARATOR = ":";
27
+
28
+ /**
29
+ * A single text field. `required` defaults to Discord's own default (`true`); set
30
+ * it to `false` to make {@link Modal.read} return `null` when left empty.
31
+ */
32
+ export interface TextFieldDef {
33
+ kind?: "text";
34
+ label: string;
35
+ /** Helper text rendered under the label. */
36
+ description?: string;
37
+ style?: TextFieldStyleName;
38
+ required?: boolean;
39
+ minLength?: number;
40
+ maxLength?: number;
41
+ placeholder?: string;
42
+ /** Prefilled content, e.g. when editing an existing value. */
43
+ value?: string;
44
+ }
45
+
46
+ /** A single checkbox field — always optional from Discord's own perspective. */
47
+ export interface CheckboxFieldDef {
48
+ kind: "checkbox";
49
+ label: string;
50
+ /** Helper text rendered under the label. */
51
+ description?: string;
52
+ /** Pre-ticked state. Defaults to unticked. */
53
+ default?: boolean;
54
+ }
55
+
56
+ /**
57
+ * A single file upload. `required` defaults to Discord's own default (`true`);
58
+ * set it to `false` to make {@link Modal.read} return `null` when nothing was
59
+ * uploaded.
60
+ */
61
+ export interface FileFieldDef {
62
+ kind: "file";
63
+ label: string;
64
+ /** Helper text rendered under the label. */
65
+ description?: string;
66
+ required?: boolean;
67
+ /** How many files may be uploaded at once. Discord's own default is one of each. */
68
+ minValues?: number;
69
+ maxValues?: number;
70
+ }
71
+
72
+ /** One choice offered by a {@link SelectFieldDef}. */
73
+ export interface SelectFieldOption {
74
+ label: string;
75
+ value: string;
76
+ description?: string;
77
+ default?: boolean;
78
+ }
79
+
80
+ /**
81
+ * A single string select menu. `required` defaults to Discord's own default
82
+ * (`true`); set it to `false` to make {@link Modal.read} return `null` when
83
+ * nothing was picked.
84
+ */
85
+ export interface SelectFieldDef {
86
+ kind: "select";
87
+ label: string;
88
+ /** Helper text rendered under the label. */
89
+ description?: string;
90
+ options: readonly SelectFieldOption[];
91
+ placeholder?: string;
92
+ required?: boolean;
93
+ minValues?: number;
94
+ maxValues?: number;
95
+ }
96
+
97
+ /**
98
+ * A single role select menu. `required` defaults to Discord's own default
99
+ * (`true`); set it to `false` to make {@link Modal.read} return `null` when
100
+ * nothing was picked.
101
+ */
102
+ export interface RoleFieldDef {
103
+ kind: "role";
104
+ label: string;
105
+ /** Helper text rendered under the label. */
106
+ description?: string;
107
+ defaultRoleIds?: readonly string[];
108
+ placeholder?: string;
109
+ required?: boolean;
110
+ minValues?: number;
111
+ maxValues?: number;
112
+ }
113
+
114
+ /**
115
+ * A single user select menu. `required` defaults to Discord's own default
116
+ * (`true`); set it to `false` to make {@link Modal.read} return `null` when
117
+ * nothing was picked.
118
+ */
119
+ export interface UserFieldDef {
120
+ kind: "user";
121
+ label: string;
122
+ /** Helper text rendered under the label. */
123
+ description?: string;
124
+ defaultUserIds?: readonly string[];
125
+ placeholder?: string;
126
+ required?: boolean;
127
+ minValues?: number;
128
+ maxValues?: number;
129
+ }
130
+
131
+ /**
132
+ * A single channel select menu. `required` defaults to Discord's own default
133
+ * (`true`); set it to `false` to make {@link Modal.read} return `null` when
134
+ * nothing was picked.
135
+ */
136
+ export interface ChannelFieldDef {
137
+ kind: "channel";
138
+ label: string;
139
+ /** Helper text rendered under the label. */
140
+ description?: string;
141
+ defaultChannelIds?: readonly string[];
142
+ /** Restricts which channel kinds may be picked, e.g. to categories only. */
143
+ channelTypes?: readonly ChannelType[];
144
+ placeholder?: string;
145
+ required?: boolean;
146
+ minValues?: number;
147
+ maxValues?: number;
148
+ }
149
+
150
+ export type FieldDef =
151
+ | TextFieldDef
152
+ | CheckboxFieldDef
153
+ | FileFieldDef
154
+ | SelectFieldDef
155
+ | RoleFieldDef
156
+ | UserFieldDef
157
+ | ChannelFieldDef;
158
+
159
+ /** Field declarations keyed by custom id — the same key `read` returns values under. */
160
+ export type ModalFields = Record<string, FieldDef>;
161
+
162
+ /**
163
+ * Submitted values, keyed like the declaration. A checkbox field always resolves
164
+ * to a `boolean`; an optional text, file, select, role, user or channel field
165
+ * widens to `| null` so a caller cannot forget the empty case. A file field's
166
+ * uploads come back as a list because the field itself may allow several, and
167
+ * so do a select/role/user/channel field's picks, as the identifiers chosen.
168
+ * This only resolves correctly because `createModal` infers its fields as
169
+ * `const`.
170
+ */
171
+ export type ModalValues<F extends ModalFields> = {
172
+ [K in keyof F]: F[K] extends CheckboxFieldDef
173
+ ? boolean
174
+ : F[K] extends FileFieldDef
175
+ ? F[K]["required"] extends false
176
+ ? readonly Attachment[] | null
177
+ : readonly Attachment[]
178
+ : F[K] extends SelectFieldDef | RoleFieldDef | UserFieldDef | ChannelFieldDef
179
+ ? F[K]["required"] extends false
180
+ ? readonly string[] | null
181
+ : readonly string[]
182
+ : F[K] extends TextFieldDef
183
+ ? F[K]["required"] extends false
184
+ ? string | null
185
+ : string
186
+ : never;
187
+ };
188
+
189
+ export interface ModalDef<F extends ModalFields> {
190
+ id: string;
191
+ title: string;
192
+ /**
193
+ * Text shown above the fields, via a Text Display component — a legend whose
194
+ * content is not one of the fields themselves. Counts toward the same
195
+ * five-component budget `fields` does: Discord's own modal object carries
196
+ * "between 1 and 5 (inclusive) components", label and text display alike.
197
+ */
198
+ legend?: string;
199
+ fields: F;
200
+ }
201
+
202
+ /**
203
+ * Compiled modal: it knows how to render itself (`build`) and how to decode what
204
+ * came back (`read`) — the same construction/behaviour pairing as a compiled
205
+ * {@link import("@/discord/interaction/button").Button}. Without it a module
206
+ * has to hand-roll raw discord.js builders, which the template forbids everywhere
207
+ * else in the DSL.
208
+ */
209
+ export interface Modal<F extends ModalFields> {
210
+ readonly id: string;
211
+ /**
212
+ * Build the modal to show. `state` is appended after a colon so the persistent
213
+ * {@link import("@/discord/components/component-router").ComponentRouter}
214
+ * still routes it to the handler registered under `id`.
215
+ */
216
+ build(state?: string): ModalBuilder;
217
+ read(interaction: ModalSubmitInteraction): ModalValues<F>;
218
+ }
219
+
220
+ function createTextInput(name: string, field: TextFieldDef): TextInputBuilder {
221
+ const input = new TextInputBuilder()
222
+ .setCustomId(name)
223
+ .setStyle(TEXT_FIELD_STYLES[field.style ?? "short"])
224
+ .setRequired(field.required ?? true);
225
+ if (field.minLength !== undefined) {
226
+ input.setMinLength(field.minLength);
227
+ }
228
+ if (field.maxLength !== undefined) {
229
+ input.setMaxLength(field.maxLength);
230
+ }
231
+ if (field.placeholder !== undefined) {
232
+ input.setPlaceholder(field.placeholder);
233
+ }
234
+ if (field.value !== undefined) {
235
+ input.setValue(field.value);
236
+ }
237
+ return input;
238
+ }
239
+
240
+ function createFileUpload(name: string, field: FileFieldDef): FileUploadBuilder {
241
+ const upload = new FileUploadBuilder().setCustomId(name).setRequired(field.required ?? true);
242
+ if (field.minValues !== undefined) {
243
+ upload.setMinValues(field.minValues);
244
+ }
245
+ if (field.maxValues !== undefined) {
246
+ upload.setMaxValues(field.maxValues);
247
+ }
248
+ return upload;
249
+ }
250
+
251
+ function createSelectOption(option: SelectFieldOption): StringSelectMenuOptionBuilder {
252
+ const built = new StringSelectMenuOptionBuilder()
253
+ .setLabel(option.label)
254
+ .setValue(option.value)
255
+ .setDefault(option.default ?? false);
256
+ if (option.description !== undefined) {
257
+ built.setDescription(option.description);
258
+ }
259
+ return built;
260
+ }
261
+
262
+ function createStringSelect(name: string, field: SelectFieldDef): StringSelectMenuBuilder {
263
+ const select = new StringSelectMenuBuilder()
264
+ .setCustomId(name)
265
+ .setRequired(field.required ?? true)
266
+ .addOptions(field.options.map(createSelectOption));
267
+ if (field.placeholder !== undefined) {
268
+ select.setPlaceholder(field.placeholder);
269
+ }
270
+ if (field.minValues !== undefined) {
271
+ select.setMinValues(field.minValues);
272
+ }
273
+ if (field.maxValues !== undefined) {
274
+ select.setMaxValues(field.maxValues);
275
+ }
276
+ return select;
277
+ }
278
+
279
+ function createRoleSelect(name: string, field: RoleFieldDef): RoleSelectMenuBuilder {
280
+ const select = new RoleSelectMenuBuilder().setCustomId(name).setRequired(field.required ?? true);
281
+ if (field.defaultRoleIds !== undefined) {
282
+ select.setDefaultRoles(...field.defaultRoleIds);
283
+ }
284
+ if (field.placeholder !== undefined) {
285
+ select.setPlaceholder(field.placeholder);
286
+ }
287
+ if (field.minValues !== undefined) {
288
+ select.setMinValues(field.minValues);
289
+ }
290
+ if (field.maxValues !== undefined) {
291
+ select.setMaxValues(field.maxValues);
292
+ }
293
+ return select;
294
+ }
295
+
296
+ function createUserSelect(name: string, field: UserFieldDef): UserSelectMenuBuilder {
297
+ const select = new UserSelectMenuBuilder().setCustomId(name).setRequired(field.required ?? true);
298
+ if (field.defaultUserIds !== undefined) {
299
+ select.setDefaultUsers(...field.defaultUserIds);
300
+ }
301
+ if (field.placeholder !== undefined) {
302
+ select.setPlaceholder(field.placeholder);
303
+ }
304
+ if (field.minValues !== undefined) {
305
+ select.setMinValues(field.minValues);
306
+ }
307
+ if (field.maxValues !== undefined) {
308
+ select.setMaxValues(field.maxValues);
309
+ }
310
+ return select;
311
+ }
312
+
313
+ function createChannelSelect(name: string, field: ChannelFieldDef): ChannelSelectMenuBuilder {
314
+ const select = new ChannelSelectMenuBuilder()
315
+ .setCustomId(name)
316
+ .setRequired(field.required ?? true);
317
+ if (field.channelTypes !== undefined) {
318
+ select.setChannelTypes(...field.channelTypes);
319
+ }
320
+ if (field.defaultChannelIds !== undefined) {
321
+ select.setDefaultChannels(...field.defaultChannelIds);
322
+ }
323
+ if (field.placeholder !== undefined) {
324
+ select.setPlaceholder(field.placeholder);
325
+ }
326
+ if (field.minValues !== undefined) {
327
+ select.setMinValues(field.minValues);
328
+ }
329
+ if (field.maxValues !== undefined) {
330
+ select.setMaxValues(field.maxValues);
331
+ }
332
+ return select;
333
+ }
334
+
335
+ function createLabel(name: string, field: FieldDef): LabelBuilder {
336
+ const label = new LabelBuilder().setLabel(field.label);
337
+ if (field.description !== undefined) {
338
+ label.setDescription(field.description);
339
+ }
340
+ if (field.kind === "checkbox") {
341
+ return label.setCheckboxComponent(
342
+ new CheckboxBuilder().setCustomId(name).setDefault(field.default ?? false),
343
+ );
344
+ }
345
+ if (field.kind === "file") {
346
+ return label.setFileUploadComponent(createFileUpload(name, field));
347
+ }
348
+ if (field.kind === "select") {
349
+ return label.setStringSelectMenuComponent(createStringSelect(name, field));
350
+ }
351
+ if (field.kind === "role") {
352
+ return label.setRoleSelectMenuComponent(createRoleSelect(name, field));
353
+ }
354
+ if (field.kind === "user") {
355
+ return label.setUserSelectMenuComponent(createUserSelect(name, field));
356
+ }
357
+ if (field.kind === "channel") {
358
+ return label.setChannelSelectMenuComponent(createChannelSelect(name, field));
359
+ }
360
+ return label.setTextInputComponent(createTextInput(name, field));
361
+ }
362
+
363
+ /**
364
+ * An optional field left blank comes back as an empty string (or not at all when
365
+ * Discord omits it), two shapes for the same "nothing entered" outcome — collapse
366
+ * both to `null` so callers branch once.
367
+ */
368
+ function readOptionalField(interaction: ModalSubmitInteraction, name: string): string | null {
369
+ if (!interaction.fields.fields.has(name)) {
370
+ return null;
371
+ }
372
+ const value = interaction.fields.getTextInputValue(name);
373
+ return value.length > 0 ? value : null;
374
+ }
375
+
376
+ /**
377
+ * The same "nothing entered" collapsing for an upload: an optional file field
378
+ * left alone comes back absent, or present with no attachment at all.
379
+ */
380
+ function readOptionalFiles(
381
+ interaction: ModalSubmitInteraction,
382
+ name: string,
383
+ ): readonly Attachment[] | null {
384
+ if (!interaction.fields.fields.has(name)) {
385
+ return null;
386
+ }
387
+ const uploaded = interaction.fields.getUploadedFiles(name);
388
+ if (uploaded === null || uploaded.size === 0) {
389
+ return null;
390
+ }
391
+ return [...uploaded.values()];
392
+ }
393
+
394
+ /**
395
+ * The same "nothing entered" collapsing for a string select: an optional select
396
+ * field left alone comes back absent, or present with no option chosen.
397
+ */
398
+ function readOptionalSelectValues(
399
+ interaction: ModalSubmitInteraction,
400
+ name: string,
401
+ ): readonly string[] | null {
402
+ if (!interaction.fields.fields.has(name)) {
403
+ return null;
404
+ }
405
+ const values = interaction.fields.getStringSelectValues(name);
406
+ return values.length > 0 ? values : null;
407
+ }
408
+
409
+ /**
410
+ * The same "nothing entered" collapsing for a role/user/channel select: an
411
+ * optional field left alone comes back absent, or present with an empty (or
412
+ * `null`) selection. The identifiers are returned as the keys of the
413
+ * `Collection` the underlying getter resolves.
414
+ */
415
+ function readOptionalIds(
416
+ interaction: ModalSubmitInteraction,
417
+ name: string,
418
+ getSelected: (
419
+ interaction: ModalSubmitInteraction,
420
+ name: string,
421
+ ) => ReadonlyMap<string, unknown> | null,
422
+ ): readonly string[] | null {
423
+ if (!interaction.fields.fields.has(name)) {
424
+ return null;
425
+ }
426
+ const selected = getSelected(interaction, name);
427
+ if (selected === null || selected.size === 0) {
428
+ return null;
429
+ }
430
+ return [...selected.keys()];
431
+ }
432
+
433
+ /**
434
+ * Declare a modal: a title, a custom id and typed fields (text, checkbox,
435
+ * file, select, role, user or channel).
436
+ *
437
+ * Discord caps a modal at five components, so keep `fields` — plus one more if
438
+ * `legend` is set — within that budget. The `const` type parameter is what
439
+ * preserves each field's literal `kind`/`required`, and therefore the shape of
440
+ * the values `read` returns.
441
+ */
442
+ export function createModal<const F extends ModalFields>(def: ModalDef<F>): Modal<F> {
443
+ const entries = Object.entries(def.fields) as [keyof F & string, FieldDef][];
444
+
445
+ return {
446
+ id: def.id,
447
+ build(state) {
448
+ const customId = state === undefined ? def.id : `${def.id}${CUSTOM_ID_SEPARATOR}${state}`;
449
+ const modal = new ModalBuilder().setCustomId(customId).setTitle(def.title);
450
+ if (def.legend !== undefined) {
451
+ modal.addTextDisplayComponents((textDisplay) =>
452
+ textDisplay.setContent(def.legend as string),
453
+ );
454
+ }
455
+ return modal.addLabelComponents(...entries.map(([name, field]) => createLabel(name, field)));
456
+ },
457
+ read(interaction) {
458
+ const values = {} as Record<
459
+ string,
460
+ string | null | boolean | readonly Attachment[] | readonly string[]
461
+ >;
462
+ for (const [name, field] of entries) {
463
+ if (field.kind === "checkbox") {
464
+ values[name] = interaction.fields.getCheckbox(name);
465
+ continue;
466
+ }
467
+ const isRequired = field.required ?? true;
468
+ if (field.kind === "file") {
469
+ values[name] = isRequired
470
+ ? [...interaction.fields.getUploadedFiles(name, true).values()]
471
+ : readOptionalFiles(interaction, name);
472
+ continue;
473
+ }
474
+ if (field.kind === "select") {
475
+ values[name] = isRequired
476
+ ? interaction.fields.getStringSelectValues(name)
477
+ : readOptionalSelectValues(interaction, name);
478
+ continue;
479
+ }
480
+ if (field.kind === "role") {
481
+ values[name] = isRequired
482
+ ? [...interaction.fields.getSelectedRoles(name, true).keys()]
483
+ : readOptionalIds(interaction, name, (i, n) => i.fields.getSelectedRoles(n, false));
484
+ continue;
485
+ }
486
+ if (field.kind === "user") {
487
+ values[name] = isRequired
488
+ ? [...interaction.fields.getSelectedUsers(name, true).keys()]
489
+ : readOptionalIds(interaction, name, (i, n) => i.fields.getSelectedUsers(n, false));
490
+ continue;
491
+ }
492
+ if (field.kind === "channel") {
493
+ values[name] = isRequired
494
+ ? [...interaction.fields.getSelectedChannels(name, true).keys()]
495
+ : readOptionalIds(interaction, name, (i, n) => i.fields.getSelectedChannels(n, false));
496
+ continue;
497
+ }
498
+ values[name] = isRequired
499
+ ? interaction.fields.getTextInputValue(name)
500
+ : readOptionalField(interaction, name);
501
+ }
502
+ return values as ModalValues<F>;
503
+ },
504
+ };
505
+ }