@azurioh/discord-kernel 0.1.0 → 1.0.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 (529) hide show
  1. package/README.md +318 -1
  2. package/dist/authz/require-level-guard.d.ts +1 -1
  3. package/dist/authz/require-level-guard.d.ts.map +1 -1
  4. package/dist/color.d.ts +42 -0
  5. package/dist/color.d.ts.map +1 -0
  6. package/dist/color.js +130 -0
  7. package/dist/color.js.map +1 -0
  8. package/dist/discord/command/all-required.d.ts +8 -0
  9. package/dist/discord/command/all-required.d.ts.map +1 -0
  10. package/dist/discord/command/all-required.js +12 -0
  11. package/dist/discord/command/all-required.js.map +1 -0
  12. package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -1
  13. package/dist/discord/command/autocomplete-dispatcher.js +2 -1
  14. package/dist/discord/command/autocomplete-dispatcher.js.map +1 -1
  15. package/dist/discord/command/autocomplete-limits.d.ts +3 -0
  16. package/dist/discord/command/autocomplete-limits.d.ts.map +1 -0
  17. package/dist/discord/command/autocomplete-limits.js +6 -0
  18. package/dist/discord/command/autocomplete-limits.js.map +1 -0
  19. package/dist/discord/command/autocomplete.d.ts +6 -1
  20. package/dist/discord/command/autocomplete.d.ts.map +1 -1
  21. package/dist/discord/command/autocomplete.js +6 -6
  22. package/dist/discord/command/autocomplete.js.map +1 -1
  23. package/dist/discord/command/command-responders.d.ts +26 -0
  24. package/dist/discord/command/command-responders.d.ts.map +1 -0
  25. package/dist/discord/command/command-responders.js +16 -0
  26. package/dist/discord/command/command-responders.js.map +1 -0
  27. package/dist/discord/command/context.d.ts +8 -17
  28. package/dist/discord/command/context.d.ts.map +1 -1
  29. package/dist/discord/command/context.js +8 -37
  30. package/dist/discord/command/context.js.map +1 -1
  31. package/dist/discord/command/cooldown-guard.d.ts +1 -1
  32. package/dist/discord/command/cooldown-guard.d.ts.map +1 -1
  33. package/dist/discord/command/create-command.d.ts +1 -8
  34. package/dist/discord/command/create-command.d.ts.map +1 -1
  35. package/dist/discord/command/create-command.js +17 -65
  36. package/dist/discord/command/create-command.js.map +1 -1
  37. package/dist/discord/command/create-context-menu-command.d.ts +3 -9
  38. package/dist/discord/command/create-context-menu-command.d.ts.map +1 -1
  39. package/dist/discord/command/create-context-menu-command.js +14 -65
  40. package/dist/discord/command/create-context-menu-command.js.map +1 -1
  41. package/dist/discord/command/errors.d.ts +1 -1
  42. package/dist/discord/command/errors.d.ts.map +1 -1
  43. package/dist/discord/command/errors.js +2 -2
  44. package/dist/discord/command/errors.js.map +1 -1
  45. package/dist/discord/command/guard.d.ts +15 -8
  46. package/dist/discord/command/guard.d.ts.map +1 -1
  47. package/dist/discord/command/guard.js +4 -4
  48. package/dist/discord/command/guard.js.map +1 -1
  49. package/dist/discord/command/options.d.ts +1 -10
  50. package/dist/discord/command/options.d.ts.map +1 -1
  51. package/dist/discord/command/options.js +127 -320
  52. package/dist/discord/command/options.js.map +1 -1
  53. package/dist/discord/command/passes-guard.d.ts +14 -0
  54. package/dist/discord/command/passes-guard.d.ts.map +1 -0
  55. package/dist/discord/command/passes-guard.js +27 -0
  56. package/dist/discord/command/passes-guard.js.map +1 -0
  57. package/dist/discord/command/permission-guard.d.ts +1 -1
  58. package/dist/discord/command/permission-guard.d.ts.map +1 -1
  59. package/dist/discord/command/render-command-failure.d.ts +23 -0
  60. package/dist/discord/command/render-command-failure.d.ts.map +1 -0
  61. package/dist/discord/command/render-command-failure.js +40 -0
  62. package/dist/discord/command/render-command-failure.js.map +1 -0
  63. package/dist/discord/command/route-key.d.ts +8 -0
  64. package/dist/discord/command/route-key.d.ts.map +1 -0
  65. package/dist/discord/command/route-key.js +13 -0
  66. package/dist/discord/command/route-key.js.map +1 -0
  67. package/dist/discord/command/router.d.ts +32 -7
  68. package/dist/discord/command/router.d.ts.map +1 -1
  69. package/dist/discord/command/router.js +48 -10
  70. package/dist/discord/command/router.js.map +1 -1
  71. package/dist/discord/command/send-embed.d.ts +14 -0
  72. package/dist/discord/command/send-embed.d.ts.map +1 -0
  73. package/dist/discord/command/send-embed.js +37 -0
  74. package/dist/discord/command/send-embed.js.map +1 -0
  75. package/dist/discord/command/types.d.ts +7 -1
  76. package/dist/discord/command/types.d.ts.map +1 -1
  77. package/dist/discord/components/component-access.d.ts +12 -0
  78. package/dist/discord/components/component-access.d.ts.map +1 -0
  79. package/dist/discord/components/component-access.js +21 -0
  80. package/dist/discord/components/component-access.js.map +1 -0
  81. package/dist/discord/components/component-router.d.ts +37 -15
  82. package/dist/discord/components/component-router.d.ts.map +1 -1
  83. package/dist/discord/components/component-router.js +44 -32
  84. package/dist/discord/components/component-router.js.map +1 -1
  85. package/dist/discord/components/interactive-message/index.d.ts +3 -1
  86. package/dist/discord/components/interactive-message/index.d.ts.map +1 -1
  87. package/dist/discord/components/interactive-message/index.js +5 -3
  88. package/dist/discord/components/interactive-message/index.js.map +1 -1
  89. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +3 -28
  90. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -1
  91. package/dist/discord/components/interactive-message/interactive-message-collector.js +6 -53
  92. package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -1
  93. package/dist/discord/components/interactive-message/message-edit-options.d.ts +18 -0
  94. package/dist/discord/components/interactive-message/message-edit-options.d.ts.map +1 -0
  95. package/dist/discord/components/interactive-message/message-edit-options.js +40 -0
  96. package/dist/discord/components/interactive-message/message-edit-options.js.map +1 -0
  97. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +2 -1
  98. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -1
  99. package/dist/discord/components/interactive-message/mount-interactive-message.js +4 -2
  100. package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -1
  101. package/dist/discord/components/interactive-message/state-store.d.ts +12 -0
  102. package/dist/discord/components/interactive-message/state-store.d.ts.map +1 -0
  103. package/dist/discord/components/interactive-message/state-store.js +14 -0
  104. package/dist/discord/components/interactive-message/state-store.js.map +1 -0
  105. package/dist/discord/components/paginator/mount-paginator.js +7 -5
  106. package/dist/discord/components/paginator/mount-paginator.js.map +1 -1
  107. package/dist/discord/components/paginator/paginator-view.d.ts +1 -1
  108. package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -1
  109. package/dist/discord/components/persistent-paginator.d.ts +1 -1
  110. package/dist/discord/components/persistent-paginator.d.ts.map +1 -1
  111. package/dist/discord/components/persistent-paginator.js +6 -6
  112. package/dist/discord/components/persistent-paginator.js.map +1 -1
  113. package/dist/discord/components/settings-editor/from-declaration-controls.d.ts +67 -0
  114. package/dist/discord/components/settings-editor/from-declaration-controls.d.ts.map +1 -0
  115. package/dist/discord/components/settings-editor/from-declaration-controls.js +245 -0
  116. package/dist/discord/components/settings-editor/from-declaration-controls.js.map +1 -0
  117. package/dist/discord/components/settings-editor/from-declaration-display.d.ts +23 -0
  118. package/dist/discord/components/settings-editor/from-declaration-display.d.ts.map +1 -0
  119. package/dist/discord/components/settings-editor/from-declaration-display.js +114 -0
  120. package/dist/discord/components/settings-editor/from-declaration-display.js.map +1 -0
  121. package/dist/discord/components/settings-editor/from-declaration-layout.d.ts +33 -0
  122. package/dist/discord/components/settings-editor/from-declaration-layout.d.ts.map +1 -0
  123. package/dist/discord/components/settings-editor/from-declaration-layout.js +178 -0
  124. package/dist/discord/components/settings-editor/from-declaration-layout.js.map +1 -0
  125. package/dist/discord/components/settings-editor/from-declaration-search.d.ts +27 -0
  126. package/dist/discord/components/settings-editor/from-declaration-search.d.ts.map +1 -0
  127. package/dist/discord/components/settings-editor/from-declaration-search.js +58 -0
  128. package/dist/discord/components/settings-editor/from-declaration-search.js.map +1 -0
  129. package/dist/discord/components/settings-editor/from-declaration-stored.d.ts +21 -0
  130. package/dist/discord/components/settings-editor/from-declaration-stored.d.ts.map +1 -0
  131. package/dist/discord/components/settings-editor/from-declaration-stored.js +37 -0
  132. package/dist/discord/components/settings-editor/from-declaration-stored.js.map +1 -0
  133. package/dist/discord/components/settings-editor/from-declaration-subject.d.ts +32 -0
  134. package/dist/discord/components/settings-editor/from-declaration-subject.d.ts.map +1 -0
  135. package/dist/discord/components/settings-editor/from-declaration-subject.js +65 -0
  136. package/dist/discord/components/settings-editor/from-declaration-subject.js.map +1 -0
  137. package/dist/discord/components/settings-editor/from-declaration-values.d.ts +57 -0
  138. package/dist/discord/components/settings-editor/from-declaration-values.d.ts.map +1 -0
  139. package/dist/discord/components/settings-editor/from-declaration-values.js +187 -0
  140. package/dist/discord/components/settings-editor/from-declaration-values.js.map +1 -0
  141. package/dist/discord/components/settings-editor/from-declaration.d.ts +39 -0
  142. package/dist/discord/components/settings-editor/from-declaration.d.ts.map +1 -0
  143. package/dist/discord/components/settings-editor/from-declaration.js +132 -0
  144. package/dist/discord/components/settings-editor/from-declaration.js.map +1 -0
  145. package/dist/discord/components/settings-editor/index.d.ts +3 -1
  146. package/dist/discord/components/settings-editor/index.d.ts.map +1 -1
  147. package/dist/discord/components/settings-editor/index.js +6 -3
  148. package/dist/discord/components/settings-editor/index.js.map +1 -1
  149. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +16 -2
  150. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -1
  151. package/dist/discord/components/settings-editor/mount-settings-editor.js +14 -10
  152. package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -1
  153. package/dist/discord/components/settings-editor/navigation.d.ts +20 -0
  154. package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -1
  155. package/dist/discord/components/settings-editor/navigation.js +22 -0
  156. package/dist/discord/components/settings-editor/navigation.js.map +1 -1
  157. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +4 -3
  158. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -1
  159. package/dist/discord/components/settings-editor/settings-editor-card.view.js +60 -34
  160. package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -1
  161. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +2 -1
  162. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -1
  163. package/dist/discord/components/settings-editor/settings-editor-field-modal.js +48 -72
  164. package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -1
  165. package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts +6 -0
  166. package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts.map +1 -0
  167. package/dist/discord/components/settings-editor/settings-editor-field-values.js +13 -0
  168. package/dist/discord/components/settings-editor/settings-editor-field-values.js.map +1 -0
  169. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +6 -5
  170. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -1
  171. package/dist/discord/components/settings-editor/settings-editor-fields.js +0 -10
  172. package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -1
  173. package/dist/discord/components/settings-editor/settings-editor.view.d.ts +3 -2
  174. package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -1
  175. package/dist/discord/components/settings-editor/settings-editor.view.js +4 -21
  176. package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -1
  177. package/dist/discord/events/event-guild-id.d.ts +10 -0
  178. package/dist/discord/events/event-guild-id.d.ts.map +1 -0
  179. package/dist/discord/events/event-guild-id.js +37 -0
  180. package/dist/discord/events/event-guild-id.js.map +1 -0
  181. package/dist/discord/events/event-router.d.ts +17 -3
  182. package/dist/discord/events/event-router.d.ts.map +1 -1
  183. package/dist/discord/events/event-router.js +33 -9
  184. package/dist/discord/events/event-router.js.map +1 -1
  185. package/dist/discord/i18n.d.ts +17 -2
  186. package/dist/discord/i18n.d.ts.map +1 -1
  187. package/dist/discord/i18n.js +43 -6
  188. package/dist/discord/i18n.js.map +1 -1
  189. package/dist/discord/interaction/interaction-locale.d.ts +14 -0
  190. package/dist/discord/interaction/interaction-locale.d.ts.map +1 -0
  191. package/dist/discord/interaction/interaction-locale.js +12 -0
  192. package/dist/discord/interaction/interaction-locale.js.map +1 -0
  193. package/dist/discord/interaction/locale-resolver.d.ts +27 -0
  194. package/dist/discord/interaction/locale-resolver.d.ts.map +1 -0
  195. package/dist/discord/interaction/locale-resolver.js +3 -0
  196. package/dist/discord/interaction/locale-resolver.js.map +1 -0
  197. package/dist/discord/interaction/modal.d.ts.map +1 -1
  198. package/dist/discord/interaction/modal.js +5 -40
  199. package/dist/discord/interaction/modal.js.map +1 -1
  200. package/dist/discord/interaction/reply-locale.d.ts +18 -0
  201. package/dist/discord/interaction/reply-locale.d.ts.map +1 -0
  202. package/dist/discord/interaction/reply-locale.js +25 -0
  203. package/dist/discord/interaction/reply-locale.js.map +1 -0
  204. package/dist/discord/interaction/select-bounds.d.ts +19 -0
  205. package/dist/discord/interaction/select-bounds.d.ts.map +1 -0
  206. package/dist/discord/interaction/select-bounds.js +17 -0
  207. package/dist/discord/interaction/select-bounds.js.map +1 -0
  208. package/dist/discord/interaction/select-menu.d.ts.map +1 -1
  209. package/dist/discord/interaction/select-menu.js +2 -9
  210. package/dist/discord/interaction/select-menu.js.map +1 -1
  211. package/dist/discord/placeholder-registry.d.ts +1 -1
  212. package/dist/discord/placeholder-registry.d.ts.map +1 -1
  213. package/dist/discord/presenter.d.ts +1 -1
  214. package/dist/discord/presenter.d.ts.map +1 -1
  215. package/dist/discord/settings/discord-guild-directory.d.ts +28 -0
  216. package/dist/discord/settings/discord-guild-directory.d.ts.map +1 -0
  217. package/dist/discord/settings/discord-guild-directory.js +171 -0
  218. package/dist/discord/settings/discord-guild-directory.js.map +1 -0
  219. package/dist/discord/settings/guild-locale-resolver.d.ts +25 -0
  220. package/dist/discord/settings/guild-locale-resolver.d.ts.map +1 -0
  221. package/dist/discord/settings/guild-locale-resolver.js +33 -0
  222. package/dist/discord/settings/guild-locale-resolver.js.map +1 -0
  223. package/dist/discord/settings/is-module-disabled.d.ts +16 -0
  224. package/dist/discord/settings/is-module-disabled.d.ts.map +1 -0
  225. package/dist/discord/settings/is-module-disabled.js +20 -0
  226. package/dist/discord/settings/is-module-disabled.js.map +1 -0
  227. package/dist/discord/settings/module-disabled-embed.d.ts +13 -0
  228. package/dist/discord/settings/module-disabled-embed.d.ts.map +1 -0
  229. package/dist/discord/settings/module-disabled-embed.js +16 -0
  230. package/dist/discord/settings/module-disabled-embed.js.map +1 -0
  231. package/dist/discord/settings/require-configured.d.ts +26 -0
  232. package/dist/discord/settings/require-configured.d.ts.map +1 -0
  233. package/dist/discord/settings/require-configured.js +62 -0
  234. package/dist/discord/settings/require-configured.js.map +1 -0
  235. package/dist/discord/ui/color-aliases.d.ts +31 -0
  236. package/dist/discord/ui/color-aliases.d.ts.map +1 -0
  237. package/dist/discord/ui/color-aliases.js +66 -0
  238. package/dist/discord/ui/color-aliases.js.map +1 -0
  239. package/dist/discord/ui/color-input.d.ts +5 -32
  240. package/dist/discord/ui/color-input.d.ts.map +1 -1
  241. package/dist/discord/ui/color-input.js +8 -157
  242. package/dist/discord/ui/color-input.js.map +1 -1
  243. package/dist/discord/ui/colors.d.ts +0 -2
  244. package/dist/discord/ui/colors.d.ts.map +1 -1
  245. package/dist/discord/ui/colors.js +4 -5
  246. package/dist/discord/ui/colors.js.map +1 -1
  247. package/dist/discord/ui/embed.d.ts.map +1 -1
  248. package/dist/discord/ui/embed.js +3 -8
  249. package/dist/discord/ui/embed.js.map +1 -1
  250. package/dist/discord/ui/truncate-text.d.ts +11 -0
  251. package/dist/discord/ui/truncate-text.d.ts.map +1 -0
  252. package/dist/discord/ui/truncate-text.js +19 -0
  253. package/dist/discord/ui/truncate-text.js.map +1 -0
  254. package/dist/errors/error-message.d.ts +9 -0
  255. package/dist/errors/error-message.d.ts.map +1 -0
  256. package/dist/errors/error-message.js +14 -0
  257. package/dist/errors/error-message.js.map +1 -0
  258. package/dist/i18n/business-message.d.ts +1 -1
  259. package/dist/i18n/business-message.d.ts.map +1 -1
  260. package/dist/i18n/catalog.d.ts +9 -0
  261. package/dist/i18n/catalog.d.ts.map +1 -1
  262. package/dist/i18n/catalog.js +4 -0
  263. package/dist/i18n/catalog.js.map +1 -1
  264. package/dist/i18n/locale.d.ts +2 -0
  265. package/dist/i18n/locale.d.ts.map +1 -1
  266. package/dist/i18n/locale.js +3 -1
  267. package/dist/i18n/locale.js.map +1 -1
  268. package/dist/i18n/translator.d.ts +2 -0
  269. package/dist/i18n/translator.d.ts.map +1 -1
  270. package/dist/i18n/translator.js.map +1 -1
  271. package/dist/module/module.d.ts +15 -1
  272. package/dist/module/module.d.ts.map +1 -1
  273. package/dist/resilience/retry.js +2 -2
  274. package/dist/resilience/retry.js.map +1 -1
  275. package/dist/scheduler/scheduler.d.ts.map +1 -1
  276. package/dist/scheduler/scheduler.js +2 -4
  277. package/dist/scheduler/scheduler.js.map +1 -1
  278. package/dist/settings/cache.d.ts +29 -0
  279. package/dist/settings/cache.d.ts.map +1 -0
  280. package/dist/settings/cache.js +47 -0
  281. package/dist/settings/cache.js.map +1 -0
  282. package/dist/settings/choice.d.ts +10 -0
  283. package/dist/settings/choice.d.ts.map +1 -0
  284. package/dist/settings/choice.js +3 -0
  285. package/dist/settings/choice.js.map +1 -0
  286. package/dist/settings/decode-stored.d.ts +20 -0
  287. package/dist/settings/decode-stored.d.ts.map +1 -0
  288. package/dist/settings/decode-stored.js +35 -0
  289. package/dist/settings/decode-stored.js.map +1 -0
  290. package/dist/settings/define-settings.d.ts +52 -0
  291. package/dist/settings/define-settings.d.ts.map +1 -0
  292. package/dist/settings/define-settings.js +141 -0
  293. package/dist/settings/define-settings.js.map +1 -0
  294. package/dist/settings/describe-field.d.ts +19 -0
  295. package/dist/settings/describe-field.d.ts.map +1 -0
  296. package/dist/settings/describe-field.js +131 -0
  297. package/dist/settings/describe-field.js.map +1 -0
  298. package/dist/settings/describe.d.ts +27 -0
  299. package/dist/settings/describe.d.ts.map +1 -0
  300. package/dist/settings/describe.js +62 -0
  301. package/dist/settings/describe.js.map +1 -0
  302. package/dist/settings/duration.d.ts +16 -0
  303. package/dist/settings/duration.d.ts.map +1 -0
  304. package/dist/settings/duration.js +56 -0
  305. package/dist/settings/duration.js.map +1 -0
  306. package/dist/settings/field-order.d.ts +37 -0
  307. package/dist/settings/field-order.d.ts.map +1 -0
  308. package/dist/settings/field-order.js +100 -0
  309. package/dist/settings/field-order.js.map +1 -0
  310. package/dist/settings/field-search.d.ts +10 -0
  311. package/dist/settings/field-search.d.ts.map +1 -0
  312. package/dist/settings/field-search.js +18 -0
  313. package/dist/settings/field-search.js.map +1 -0
  314. package/dist/settings/fields/builders.d.ts +41 -0
  315. package/dist/settings/fields/builders.d.ts.map +1 -0
  316. package/dist/settings/fields/builders.js +83 -0
  317. package/dist/settings/fields/builders.js.map +1 -0
  318. package/dist/settings/fields/field.d.ts +199 -0
  319. package/dist/settings/fields/field.d.ts.map +1 -0
  320. package/dist/settings/fields/field.js +33 -0
  321. package/dist/settings/fields/field.js.map +1 -0
  322. package/dist/settings/fields/zod-schema.d.ts +67 -0
  323. package/dist/settings/fields/zod-schema.d.ts.map +1 -0
  324. package/dist/settings/fields/zod-schema.js +281 -0
  325. package/dist/settings/fields/zod-schema.js.map +1 -0
  326. package/dist/settings/in-memory/in-memory-guild-directory.d.ts +24 -0
  327. package/dist/settings/in-memory/in-memory-guild-directory.d.ts.map +1 -0
  328. package/dist/settings/in-memory/in-memory-guild-directory.js +42 -0
  329. package/dist/settings/in-memory/in-memory-guild-directory.js.map +1 -0
  330. package/dist/settings/in-memory/in-memory-settings-store.d.ts +8 -0
  331. package/dist/settings/in-memory/in-memory-settings-store.d.ts.map +1 -0
  332. package/dist/settings/in-memory/in-memory-settings-store.js +43 -0
  333. package/dist/settings/in-memory/in-memory-settings-store.js.map +1 -0
  334. package/dist/settings/in-memory/in-process-notifier.d.ts +9 -0
  335. package/dist/settings/in-memory/in-process-notifier.d.ts.map +1 -0
  336. package/dist/settings/in-memory/in-process-notifier.js +32 -0
  337. package/dist/settings/in-memory/in-process-notifier.js.map +1 -0
  338. package/dist/settings/index.d.ts +22 -0
  339. package/dist/settings/index.d.ts.map +1 -0
  340. package/dist/settings/index.js +31 -0
  341. package/dist/settings/index.js.map +1 -0
  342. package/dist/settings/is-plain-object.d.ts +6 -0
  343. package/dist/settings/is-plain-object.d.ts.map +1 -0
  344. package/dist/settings/is-plain-object.js +15 -0
  345. package/dist/settings/is-plain-object.js.map +1 -0
  346. package/dist/settings/messages.d.ts +61 -0
  347. package/dist/settings/messages.d.ts.map +1 -0
  348. package/dist/settings/messages.js +155 -0
  349. package/dist/settings/messages.js.map +1 -0
  350. package/dist/settings/migrate.d.ts +35 -0
  351. package/dist/settings/migrate.d.ts.map +1 -0
  352. package/dist/settings/migrate.js +42 -0
  353. package/dist/settings/migrate.js.map +1 -0
  354. package/dist/settings/ports/guild-directory.d.ts +61 -0
  355. package/dist/settings/ports/guild-directory.d.ts.map +1 -0
  356. package/dist/settings/ports/guild-directory.js +3 -0
  357. package/dist/settings/ports/guild-directory.js.map +1 -0
  358. package/dist/settings/ports/settings-changed-notifier.d.ts +20 -0
  359. package/dist/settings/ports/settings-changed-notifier.d.ts.map +1 -0
  360. package/dist/settings/ports/settings-changed-notifier.js +3 -0
  361. package/dist/settings/ports/settings-changed-notifier.js.map +1 -0
  362. package/dist/settings/ports/settings-store.d.ts +76 -0
  363. package/dist/settings/ports/settings-store.d.ts.map +1 -0
  364. package/dist/settings/ports/settings-store.js +3 -0
  365. package/dist/settings/ports/settings-store.js.map +1 -0
  366. package/dist/settings/registry.d.ts +31 -0
  367. package/dist/settings/registry.d.ts.map +1 -0
  368. package/dist/settings/registry.js +102 -0
  369. package/dist/settings/registry.js.map +1 -0
  370. package/dist/settings/settings-declaration-error.d.ts +13 -0
  371. package/dist/settings/settings-declaration-error.d.ts.map +1 -0
  372. package/dist/settings/settings-declaration-error.js +22 -0
  373. package/dist/settings/settings-declaration-error.js.map +1 -0
  374. package/dist/settings/settings-service.d.ts +148 -0
  375. package/dist/settings/settings-service.d.ts.map +1 -0
  376. package/dist/settings/settings-service.js +279 -0
  377. package/dist/settings/settings-service.js.map +1 -0
  378. package/dist/settings/settings-validation-error.d.ts +23 -0
  379. package/dist/settings/settings-validation-error.d.ts.map +1 -0
  380. package/dist/settings/settings-validation-error.js +17 -0
  381. package/dist/settings/settings-validation-error.js.map +1 -0
  382. package/dist/settings/status.d.ts +16 -0
  383. package/dist/settings/status.d.ts.map +1 -0
  384. package/dist/settings/status.js +18 -0
  385. package/dist/settings/status.js.map +1 -0
  386. package/dist/settings/suggest.d.ts +77 -0
  387. package/dist/settings/suggest.d.ts.map +1 -0
  388. package/dist/settings/suggest.js +192 -0
  389. package/dist/settings/suggest.js.map +1 -0
  390. package/dist/settings/system/kernel-settings.d.ts +43 -0
  391. package/dist/settings/system/kernel-settings.d.ts.map +1 -0
  392. package/dist/settings/system/kernel-settings.js +53 -0
  393. package/dist/settings/system/kernel-settings.js.map +1 -0
  394. package/dist/settings/system/module-gate.d.ts +31 -0
  395. package/dist/settings/system/module-gate.d.ts.map +1 -0
  396. package/dist/settings/system/module-gate.js +34 -0
  397. package/dist/settings/system/module-gate.js.map +1 -0
  398. package/dist/settings/testing/index.d.ts +2 -0
  399. package/dist/settings/testing/index.d.ts.map +1 -0
  400. package/dist/settings/testing/index.js +6 -0
  401. package/dist/settings/testing/index.js.map +1 -0
  402. package/dist/settings/testing/settings-store-contract.d.ts +29 -0
  403. package/dist/settings/testing/settings-store-contract.d.ts.map +1 -0
  404. package/dist/settings/testing/settings-store-contract.js +192 -0
  405. package/dist/settings/testing/settings-store-contract.js.map +1 -0
  406. package/dist/settings/timed-search.d.ts +26 -0
  407. package/dist/settings/timed-search.d.ts.map +1 -0
  408. package/dist/settings/timed-search.js +48 -0
  409. package/dist/settings/timed-search.js.map +1 -0
  410. package/dist/settings/types.d.ts +37 -0
  411. package/dist/settings/types.d.ts.map +1 -0
  412. package/dist/settings/types.js +3 -0
  413. package/dist/settings/types.js.map +1 -0
  414. package/dist/settings/validate.d.ts +55 -0
  415. package/dist/settings/validate.d.ts.map +1 -0
  416. package/dist/settings/validate.js +196 -0
  417. package/dist/settings/validate.js.map +1 -0
  418. package/package.json +463 -88
  419. package/src/authz/require-level-guard.ts +1 -1
  420. package/src/color.ts +134 -0
  421. package/src/discord/command/all-required.ts +12 -0
  422. package/src/discord/command/autocomplete-dispatcher.ts +2 -1
  423. package/src/discord/command/autocomplete-limits.ts +2 -0
  424. package/src/discord/command/autocomplete.ts +8 -7
  425. package/src/discord/command/command-responders.ts +39 -0
  426. package/src/discord/command/context.ts +14 -54
  427. package/src/discord/command/cooldown-guard.ts +1 -1
  428. package/src/discord/command/create-command.ts +19 -94
  429. package/src/discord/command/create-context-menu-command.ts +19 -97
  430. package/src/discord/command/errors.ts +1 -1
  431. package/src/discord/command/guard.ts +24 -12
  432. package/src/discord/command/options.ts +202 -412
  433. package/src/discord/command/passes-guard.ts +37 -0
  434. package/src/discord/command/permission-guard.ts +1 -1
  435. package/src/discord/command/render-command-failure.ts +71 -0
  436. package/src/discord/command/route-key.ts +9 -0
  437. package/src/discord/command/router.ts +73 -12
  438. package/src/discord/command/send-embed.ts +42 -0
  439. package/src/discord/command/types.ts +7 -1
  440. package/src/discord/components/component-access.ts +20 -0
  441. package/src/discord/components/component-router.ts +73 -36
  442. package/src/discord/components/interactive-message/index.ts +5 -3
  443. package/src/discord/components/interactive-message/interactive-message-collector.ts +4 -68
  444. package/src/discord/components/interactive-message/message-edit-options.ts +47 -0
  445. package/src/discord/components/interactive-message/mount-interactive-message.ts +5 -3
  446. package/src/discord/components/interactive-message/state-store.ts +20 -0
  447. package/src/discord/components/paginator/mount-paginator.ts +3 -3
  448. package/src/discord/components/paginator/paginator-view.ts +1 -1
  449. package/src/discord/components/persistent-paginator.ts +8 -9
  450. package/src/discord/components/settings-editor/from-declaration-controls.ts +371 -0
  451. package/src/discord/components/settings-editor/from-declaration-display.ts +155 -0
  452. package/src/discord/components/settings-editor/from-declaration-layout.ts +247 -0
  453. package/src/discord/components/settings-editor/from-declaration-search.ts +77 -0
  454. package/src/discord/components/settings-editor/from-declaration-stored.ts +39 -0
  455. package/src/discord/components/settings-editor/from-declaration-subject.ts +110 -0
  456. package/src/discord/components/settings-editor/from-declaration-values.ts +240 -0
  457. package/src/discord/components/settings-editor/from-declaration.ts +218 -0
  458. package/src/discord/components/settings-editor/index.ts +5 -2
  459. package/src/discord/components/settings-editor/mount-settings-editor.ts +28 -10
  460. package/src/discord/components/settings-editor/navigation.ts +41 -0
  461. package/src/discord/components/settings-editor/settings-editor-card.view.ts +79 -36
  462. package/src/discord/components/settings-editor/settings-editor-field-modal.ts +76 -68
  463. package/src/discord/components/settings-editor/settings-editor-field-values.ts +11 -0
  464. package/src/discord/components/settings-editor/settings-editor-fields.ts +6 -11
  465. package/src/discord/components/settings-editor/settings-editor.view.ts +11 -24
  466. package/src/discord/events/event-guild-id.ts +35 -0
  467. package/src/discord/events/event-router.ts +46 -13
  468. package/src/discord/i18n.ts +47 -8
  469. package/src/discord/interaction/interaction-locale.ts +19 -0
  470. package/src/discord/interaction/locale-resolver.ts +28 -0
  471. package/src/discord/interaction/modal.ts +5 -40
  472. package/src/discord/interaction/reply-locale.ts +35 -0
  473. package/src/discord/interaction/select-bounds.ts +33 -0
  474. package/src/discord/interaction/select-menu.ts +2 -9
  475. package/src/discord/placeholder-registry.ts +1 -1
  476. package/src/discord/presenter.ts +1 -1
  477. package/src/discord/settings/discord-guild-directory.ts +214 -0
  478. package/src/discord/settings/guild-locale-resolver.ts +38 -0
  479. package/src/discord/settings/is-module-disabled.ts +22 -0
  480. package/src/discord/settings/module-disabled-embed.ts +19 -0
  481. package/src/discord/settings/require-configured.ts +68 -0
  482. package/src/discord/ui/color-aliases.ts +70 -0
  483. package/src/discord/ui/color-input.ts +8 -173
  484. package/src/discord/ui/colors.ts +2 -3
  485. package/src/discord/ui/embed.ts +3 -9
  486. package/src/discord/ui/truncate-text.ts +15 -0
  487. package/src/errors/error-message.ts +10 -0
  488. package/src/i18n/business-message.ts +1 -1
  489. package/src/i18n/catalog.ts +14 -0
  490. package/src/i18n/locale.ts +3 -0
  491. package/src/i18n/translator.ts +3 -0
  492. package/src/module/module.ts +15 -1
  493. package/src/resilience/retry.ts +1 -1
  494. package/src/scheduler/scheduler.ts +2 -5
  495. package/src/settings/cache.ts +70 -0
  496. package/src/settings/choice.ts +9 -0
  497. package/src/settings/decode-stored.ts +40 -0
  498. package/src/settings/define-settings.ts +214 -0
  499. package/src/settings/describe-field.ts +155 -0
  500. package/src/settings/describe.ts +90 -0
  501. package/src/settings/duration.ts +54 -0
  502. package/src/settings/field-order.ts +128 -0
  503. package/src/settings/field-search.ts +16 -0
  504. package/src/settings/fields/builders.ts +161 -0
  505. package/src/settings/fields/field.ts +205 -0
  506. package/src/settings/fields/zod-schema.ts +300 -0
  507. package/src/settings/in-memory/in-memory-guild-directory.ts +69 -0
  508. package/src/settings/in-memory/in-memory-settings-store.ts +46 -0
  509. package/src/settings/in-memory/in-process-notifier.ts +40 -0
  510. package/src/settings/index.ts +72 -0
  511. package/src/settings/is-plain-object.ts +11 -0
  512. package/src/settings/messages.ts +161 -0
  513. package/src/settings/migrate.ts +58 -0
  514. package/src/settings/ports/guild-directory.ts +48 -0
  515. package/src/settings/ports/settings-changed-notifier.ts +20 -0
  516. package/src/settings/ports/settings-store.ts +74 -0
  517. package/src/settings/registry.ts +137 -0
  518. package/src/settings/settings-declaration-error.ts +16 -0
  519. package/src/settings/settings-service.ts +535 -0
  520. package/src/settings/settings-validation-error.ts +41 -0
  521. package/src/settings/status.ts +25 -0
  522. package/src/settings/suggest.ts +267 -0
  523. package/src/settings/system/kernel-settings.ts +74 -0
  524. package/src/settings/system/module-gate.ts +49 -0
  525. package/src/settings/testing/index.ts +7 -0
  526. package/src/settings/testing/settings-store-contract.ts +278 -0
  527. package/src/settings/timed-search.ts +56 -0
  528. package/src/settings/types.ts +42 -0
  529. package/src/settings/validate.ts +298 -0
@@ -20,6 +20,7 @@ import type {
20
20
  } from "discord.js";
21
21
  import { isValidWallClock } from "@/datetime";
22
22
  import { OptionValidationError } from "@/discord/command/errors";
23
+ import type { Choice } from "@/settings/choice";
23
24
 
24
25
  /**
25
26
  * Translated name/description for a single option, keyed by Discord locale
@@ -32,14 +33,28 @@ export interface OptionLocalizations {
32
33
  descriptionLocalizations?: LocalizationMap;
33
34
  }
34
35
 
35
- /** Applies the localization maps to a builder, if any were provided. */
36
- function applyOptionLocalizations(
37
- option: {
38
- setNameLocalizations(map: LocalizationMap): unknown;
39
- setDescriptionLocalizations(map: LocalizationMap): unknown;
40
- },
36
+ /** The setters every discord.js option builder shares, whatever its type. */
37
+ interface OptionBuilder {
38
+ setName(name: string): unknown;
39
+ setDescription(description: string): unknown;
40
+ setRequired(required: boolean): unknown;
41
+ setNameLocalizations(map: LocalizationMap): unknown;
42
+ setDescriptionLocalizations(map: LocalizationMap): unknown;
43
+ }
44
+
45
+ /**
46
+ * The block every option type opens with: its name, description, whether it
47
+ * is required, and its translated labels.
48
+ */
49
+ function describeOption(
50
+ option: OptionBuilder,
51
+ name: string,
52
+ declared: { readonly description: string; readonly isRequired: boolean },
41
53
  localizations: OptionLocalizations,
42
54
  ): void {
55
+ option.setName(name);
56
+ option.setDescription(declared.description);
57
+ option.setRequired(declared.isRequired);
43
58
  if (localizations.nameLocalizations) {
44
59
  option.setNameLocalizations(localizations.nameLocalizations);
45
60
  }
@@ -75,12 +90,6 @@ export interface OptionContainer {
75
90
  ): unknown;
76
91
  }
77
92
 
78
- /** A single suggestion returned to Discord while the user types. */
79
- export interface Choice {
80
- readonly name: string;
81
- readonly value: string | number;
82
- }
83
-
84
93
  /**
85
94
  * Computes the suggestions for a focused option. Sync resolvers are allowed so a
86
95
  * static list does not have to pay for a promise.
@@ -199,8 +208,7 @@ class StringOption<TValue extends string, TRequired extends boolean>
199
208
  apply(container: OptionContainer, name: string): void {
200
209
  assertChoicesAndAutocompleteExclusive(this.choiceValues !== null, this.autocomplete);
201
210
  container.addStringOption((option) => {
202
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
203
- applyOptionLocalizations(option, this.localizations);
211
+ describeOption(option, name, this, this.localizations);
204
212
  if (this.choiceValues) {
205
213
  option.addChoices(...this.choiceValues.map((value) => ({ name: value, value })));
206
214
  }
@@ -245,85 +253,86 @@ export function createStringOption(
245
253
  );
246
254
  }
247
255
 
248
- class UserOption<TRequired extends boolean> implements Option<User, TRequired> {
256
+ /**
257
+ * What an option type contributes on top of the shared name/description/
258
+ * required/localization block: which builder it registers through (and any
259
+ * extra setter it applies there), how it reads the submitted value, and the
260
+ * name its `choices()` refusal gives it.
261
+ */
262
+ interface OptionKind<TValue, TBuilder> {
263
+ readonly label: string;
264
+ add(container: OptionContainer, configure: (option: TBuilder) => void): void;
265
+ read(interaction: ChatInputCommandInteraction, name: string, required: boolean): TValue | null;
266
+ }
267
+
268
+ /** Adapt a `configure` step to the builder callback discord.js expects back. */
269
+ function configuring<TBase>(
270
+ configure: (option: TBase) => void,
271
+ ): <TBuilder extends TBase>(option: TBuilder) => TBuilder {
272
+ return (option) => {
273
+ configure(option);
274
+ return option;
275
+ };
276
+ }
277
+
278
+ /**
279
+ * An option type that supports neither choices nor autocomplete: every
280
+ * difference between two of them lives in their {@link OptionKind}.
281
+ */
282
+ class PlainOption<TValue, TRequired extends boolean> implements Option<TValue, TRequired> {
249
283
  constructor(
284
+ private readonly kind: OptionKind<TValue, OptionBuilder>,
250
285
  readonly description: string,
251
286
  readonly isRequired: TRequired,
252
287
  private readonly localizations: OptionLocalizations = {},
253
288
  ) {}
254
289
 
255
- required(): Option<User, true> {
256
- return new UserOption<true>(this.description, true, this.localizations);
290
+ required(): Option<TValue, true> {
291
+ return new PlainOption<TValue, true>(this.kind, this.description, true, this.localizations);
257
292
  }
258
293
 
259
- choices<const C extends readonly User[]>(_values: C): Option<C[number], TRequired> {
260
- throw new OptionValidationError("User options do not support choices.");
294
+ choices<const C extends readonly TValue[]>(_values: C): Option<C[number], TRequired> {
295
+ throw new OptionValidationError(`${this.kind.label} options do not support choices.`);
261
296
  }
262
297
 
263
298
  apply(container: OptionContainer, name: string): void {
264
- container.addUserOption((option) => {
265
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
266
- applyOptionLocalizations(option, this.localizations);
267
- return option;
268
- });
299
+ this.kind.add(container, (option) => describeOption(option, name, this, this.localizations));
269
300
  }
270
301
 
271
302
  read(
272
303
  interaction: ChatInputCommandInteraction,
273
304
  name: string,
274
- ): TRequired extends true ? User : User | null {
275
- return interaction.options.getUser(name, this.isRequired as boolean) as TRequired extends true
276
- ? User
277
- : User | null;
305
+ ): TRequired extends true ? TValue : TValue | null {
306
+ return this.kind.read(interaction, name, this.isRequired) as TRequired extends true
307
+ ? TValue
308
+ : TValue | null;
278
309
  }
279
310
  }
280
311
 
312
+ const USER_KIND: OptionKind<User, OptionBuilder> = {
313
+ label: "User",
314
+ add: (container, configure) => container.addUserOption(configuring(configure)),
315
+ read: (interaction, name, required) => interaction.options.getUser(name, required),
316
+ };
317
+
281
318
  export function createUserOption(
282
319
  description: string,
283
320
  localizations: OptionLocalizations = {},
284
321
  ): Option<User, false> {
285
- return new UserOption<false>(description, false, localizations);
322
+ return new PlainOption<User, false>(USER_KIND, description, false, localizations);
286
323
  }
287
324
 
288
- class BooleanOption<TRequired extends boolean> implements Option<boolean, TRequired> {
289
- constructor(
290
- readonly description: string,
291
- readonly isRequired: TRequired,
292
- private readonly localizations: OptionLocalizations = {},
293
- ) {}
294
-
295
- required(): Option<boolean, true> {
296
- return new BooleanOption<true>(this.description, true, this.localizations);
297
- }
298
-
299
- choices<const C extends readonly boolean[]>(_values: C): Option<C[number], TRequired> {
300
- throw new OptionValidationError("Boolean options do not support choices.");
301
- }
302
-
303
- apply(container: OptionContainer, name: string): void {
304
- container.addBooleanOption((option) => {
305
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
306
- applyOptionLocalizations(option, this.localizations);
307
- return option;
308
- });
309
- }
310
-
311
- read(
312
- interaction: ChatInputCommandInteraction,
313
- name: string,
314
- ): TRequired extends true ? boolean : boolean | null {
315
- return interaction.options.getBoolean(
316
- name,
317
- this.isRequired as boolean,
318
- ) as TRequired extends true ? boolean : boolean | null;
319
- }
320
- }
325
+ const BOOLEAN_KIND: OptionKind<boolean, OptionBuilder> = {
326
+ label: "Boolean",
327
+ add: (container, configure) => container.addBooleanOption(configuring(configure)),
328
+ read: (interaction, name, required) => interaction.options.getBoolean(name, required),
329
+ };
321
330
 
322
331
  export function createBooleanOption(
323
332
  description: string,
324
333
  localizations: OptionLocalizations = {},
325
334
  ): Option<boolean, false> {
326
- return new BooleanOption<false>(description, false, localizations);
335
+ return new PlainOption<boolean, false>(BOOLEAN_KIND, description, false, localizations);
327
336
  }
328
337
 
329
338
  interface IntegerBounds {
@@ -331,8 +340,20 @@ interface IntegerBounds {
331
340
  max?: number;
332
341
  }
333
342
 
334
- class IntegerOption<TRequired extends boolean> implements AutocompletableOption<number, TRequired> {
343
+ /** The setters the integer and number builders share on top of the plain ones. */
344
+ interface NumericOptionBuilder extends OptionBuilder {
345
+ setMinValue(min: number): unknown;
346
+ setMaxValue(max: number): unknown;
347
+ setAutocomplete(autocomplete: boolean): unknown;
348
+ }
349
+
350
+ /**
351
+ * An integer or a number option: bounded, autocompletable, never offered as a
352
+ * fixed choice list. The two differ only in their {@link OptionKind}.
353
+ */
354
+ class NumericOption<TRequired extends boolean> implements AutocompletableOption<number, TRequired> {
335
355
  constructor(
356
+ private readonly kind: OptionKind<number, NumericOptionBuilder>,
336
357
  readonly description: string,
337
358
  readonly isRequired: TRequired,
338
359
  private readonly bounds: IntegerBounds,
@@ -341,7 +362,8 @@ class IntegerOption<TRequired extends boolean> implements AutocompletableOption<
341
362
  ) {}
342
363
 
343
364
  required(): AutocompletableOption<number, true> {
344
- return new IntegerOption<true>(
365
+ return new NumericOption<true>(
366
+ this.kind,
345
367
  this.description,
346
368
  true,
347
369
  this.bounds,
@@ -351,11 +373,12 @@ class IntegerOption<TRequired extends boolean> implements AutocompletableOption<
351
373
  }
352
374
 
353
375
  choices<const C extends readonly number[]>(_values: C): Option<C[number], TRequired> {
354
- throw new OptionValidationError("Integer options do not support choices.");
376
+ throw new OptionValidationError(`${this.kind.label} options do not support choices.`);
355
377
  }
356
378
 
357
379
  withAutocomplete(resolver: AutocompleteResolver): AutocompletableOption<number, TRequired> {
358
- return new IntegerOption<TRequired>(
380
+ return new NumericOption<TRequired>(
381
+ this.kind,
359
382
  this.description,
360
383
  this.isRequired,
361
384
  this.bounds,
@@ -365,9 +388,8 @@ class IntegerOption<TRequired extends boolean> implements AutocompletableOption<
365
388
  }
366
389
 
367
390
  apply(container: OptionContainer, name: string): void {
368
- container.addIntegerOption((option) => {
369
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
370
- applyOptionLocalizations(option, this.localizations);
391
+ this.kind.add(container, (option) => {
392
+ describeOption(option, name, this, this.localizations);
371
393
  if (this.bounds.min !== undefined) {
372
394
  option.setMinValue(this.bounds.min);
373
395
  }
@@ -377,7 +399,6 @@ class IntegerOption<TRequired extends boolean> implements AutocompletableOption<
377
399
  if (this.autocomplete !== undefined) {
378
400
  option.setAutocomplete(true);
379
401
  }
380
- return option;
381
402
  });
382
403
  }
383
404
 
@@ -385,139 +406,84 @@ class IntegerOption<TRequired extends boolean> implements AutocompletableOption<
385
406
  interaction: ChatInputCommandInteraction,
386
407
  name: string,
387
408
  ): TRequired extends true ? number : number | null {
388
- return interaction.options.getInteger(
389
- name,
390
- this.isRequired as boolean,
391
- ) as TRequired extends true ? number : number | null;
409
+ return this.kind.read(interaction, name, this.isRequired) as TRequired extends true
410
+ ? number
411
+ : number | null;
392
412
  }
393
413
  }
394
414
 
415
+ const INTEGER_KIND: OptionKind<number, NumericOptionBuilder> = {
416
+ label: "Integer",
417
+ add: (container, configure) => container.addIntegerOption(configuring(configure)),
418
+ read: (interaction, name, required) => interaction.options.getInteger(name, required),
419
+ };
420
+
395
421
  export function createIntegerOption(
396
422
  description: string,
397
423
  bounds: IntegerBounds = {},
398
424
  localizations: OptionLocalizations = {},
399
425
  ): AutocompletableOption<number, false> {
400
- return new IntegerOption<false>(description, false, bounds, undefined, localizations);
426
+ return new NumericOption<false>(
427
+ INTEGER_KIND,
428
+ description,
429
+ false,
430
+ bounds,
431
+ undefined,
432
+ localizations,
433
+ );
401
434
  }
402
435
 
403
- class NumberOption<TRequired extends boolean> implements AutocompletableOption<number, TRequired> {
404
- constructor(
405
- readonly description: string,
406
- readonly isRequired: TRequired,
407
- private readonly bounds: IntegerBounds,
408
- readonly autocomplete?: AutocompleteResolver,
409
- private readonly localizations: OptionLocalizations = {},
410
- ) {}
411
-
412
- required(): AutocompletableOption<number, true> {
413
- return new NumberOption<true>(
414
- this.description,
415
- true,
416
- this.bounds,
417
- this.autocomplete,
418
- this.localizations,
419
- );
420
- }
421
-
422
- choices<const C extends readonly number[]>(_values: C): Option<C[number], TRequired> {
423
- throw new OptionValidationError("Number options do not support choices.");
424
- }
425
-
426
- withAutocomplete(resolver: AutocompleteResolver): AutocompletableOption<number, TRequired> {
427
- return new NumberOption<TRequired>(
428
- this.description,
429
- this.isRequired,
430
- this.bounds,
431
- resolver,
432
- this.localizations,
433
- );
434
- }
435
-
436
- apply(container: OptionContainer, name: string): void {
437
- container.addNumberOption((option) => {
438
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
439
- applyOptionLocalizations(option, this.localizations);
440
- if (this.bounds.min !== undefined) {
441
- option.setMinValue(this.bounds.min);
442
- }
443
- if (this.bounds.max !== undefined) {
444
- option.setMaxValue(this.bounds.max);
445
- }
446
- if (this.autocomplete !== undefined) {
447
- option.setAutocomplete(true);
448
- }
449
- return option;
450
- });
451
- }
452
-
453
- read(
454
- interaction: ChatInputCommandInteraction,
455
- name: string,
456
- ): TRequired extends true ? number : number | null {
457
- return interaction.options.getNumber(name, this.isRequired as boolean) as TRequired extends true
458
- ? number
459
- : number | null;
460
- }
461
- }
436
+ const NUMBER_KIND: OptionKind<number, NumericOptionBuilder> = {
437
+ label: "Number",
438
+ add: (container, configure) => container.addNumberOption(configuring(configure)),
439
+ read: (interaction, name, required) => interaction.options.getNumber(name, required),
440
+ };
462
441
 
463
442
  export function createNumberOption(
464
443
  description: string,
465
444
  bounds: IntegerBounds = {},
466
445
  localizations: OptionLocalizations = {},
467
446
  ): AutocompletableOption<number, false> {
468
- return new NumberOption<false>(description, false, bounds, undefined, localizations);
447
+ return new NumericOption<false>(
448
+ NUMBER_KIND,
449
+ description,
450
+ false,
451
+ bounds,
452
+ undefined,
453
+ localizations,
454
+ );
469
455
  }
470
456
 
471
- const INVALID_DATETIME_MESSAGE =
472
- "Invalid date format. Use `YYYY-MM-DD HH:MM` (e.g. `2025-03-15 14:00`).";
473
-
474
- function assertValidDate(value: string, message: string): void {
475
- if (Number.isNaN(Date.parse(value))) {
476
- throw new OptionValidationError(message);
477
- }
457
+ /** A plain string input whose raw text is validated, then converted, by `parse`. */
458
+ function textKind<TValue>(
459
+ label: string,
460
+ parse: (raw: string) => TValue,
461
+ ): OptionKind<TValue, OptionBuilder> {
462
+ return {
463
+ label,
464
+ add: (container, configure) => container.addStringOption(configuring(configure)),
465
+ read: (interaction, name, required) => {
466
+ const raw = interaction.options.getString(name, required);
467
+ return raw === null ? null : parse(raw);
468
+ },
469
+ };
478
470
  }
479
471
 
480
- class DateTimeOption<TRequired extends boolean> implements Option<Date, TRequired> {
481
- constructor(
482
- readonly description: string,
483
- readonly isRequired: TRequired,
484
- private readonly localizations: OptionLocalizations = {},
485
- ) {}
486
-
487
- required(): Option<Date, true> {
488
- return new DateTimeOption<true>(this.description, true, this.localizations);
489
- }
490
-
491
- choices<const C extends readonly Date[]>(_values: C): Option<C[number], TRequired> {
492
- throw new OptionValidationError("Date options do not support choices.");
493
- }
494
-
495
- apply(container: OptionContainer, name: string): void {
496
- container.addStringOption((option) => {
497
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
498
- applyOptionLocalizations(option, this.localizations);
499
- return option;
500
- });
501
- }
472
+ const INVALID_DATETIME_MESSAGE =
473
+ "Invalid date format. Use `YYYY-MM-DD HH:MM` (e.g. `2025-03-15 14:00`).";
502
474
 
503
- read(
504
- interaction: ChatInputCommandInteraction,
505
- name: string,
506
- ): TRequired extends true ? Date : Date | null {
507
- const raw = interaction.options.getString(name, this.isRequired as boolean);
508
- if (raw === null) {
509
- return null as TRequired extends true ? Date : Date | null;
510
- }
511
- assertValidDate(raw, INVALID_DATETIME_MESSAGE);
512
- return new Date(raw.replace(" ", "T")) as TRequired extends true ? Date : Date | null;
475
+ const DATE_TIME_KIND = textKind<Date>("Date", (raw) => {
476
+ if (Number.isNaN(Date.parse(raw))) {
477
+ throw new OptionValidationError(INVALID_DATETIME_MESSAGE);
513
478
  }
514
- }
479
+ return new Date(raw.replace(" ", "T"));
480
+ });
515
481
 
516
482
  export function createDateTimeOption(
517
483
  description: string,
518
484
  localizations: OptionLocalizations = {},
519
485
  ): Option<Date, false> {
520
- return new DateTimeOption<false>(description, false, localizations);
486
+ return new PlainOption<Date, false>(DATE_TIME_KIND, description, false, localizations);
521
487
  }
522
488
 
523
489
  const INVALID_WALL_CLOCK_MESSAGE =
@@ -532,43 +498,12 @@ const DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
532
498
  * IANA zone applies. This is what lets the same option be reused by any module
533
499
  * that needs a wall-clock input.
534
500
  */
535
- class WallClockOption<TRequired extends boolean> implements Option<string, TRequired> {
536
- constructor(
537
- readonly description: string,
538
- readonly isRequired: TRequired,
539
- private readonly localizations: OptionLocalizations = {},
540
- ) {}
541
-
542
- required(): Option<string, true> {
543
- return new WallClockOption<true>(this.description, true, this.localizations);
544
- }
545
-
546
- choices<const C extends readonly string[]>(_values: C): Option<C[number], TRequired> {
547
- throw new OptionValidationError("Wall-clock options do not support choices.");
548
- }
549
-
550
- apply(container: OptionContainer, name: string): void {
551
- container.addStringOption((option) => {
552
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
553
- applyOptionLocalizations(option, this.localizations);
554
- return option;
555
- });
556
- }
557
-
558
- read(
559
- interaction: ChatInputCommandInteraction,
560
- name: string,
561
- ): TRequired extends true ? string : string | null {
562
- const raw = interaction.options.getString(name, this.isRequired as boolean);
563
- if (raw === null) {
564
- return null as TRequired extends true ? string : string | null;
565
- }
566
- if (!isValidWallClock(raw)) {
567
- throw new OptionValidationError(INVALID_WALL_CLOCK_MESSAGE);
568
- }
569
- return raw as TRequired extends true ? string : string | null;
501
+ const WALL_CLOCK_KIND = textKind<string>("Wall-clock", (raw) => {
502
+ if (!isValidWallClock(raw)) {
503
+ throw new OptionValidationError(INVALID_WALL_CLOCK_MESSAGE);
570
504
  }
571
- }
505
+ return raw;
506
+ });
572
507
 
573
508
  /**
574
509
  * A `YYYY-MM-DD[ HH:MM[:SS]]` string option, validated against the wall-clock
@@ -579,49 +514,18 @@ export function createWallClockOption(
579
514
  description: string,
580
515
  localizations: OptionLocalizations = {},
581
516
  ): Option<string, false> {
582
- return new WallClockOption<false>(description, false, localizations);
517
+ return new PlainOption<string, false>(WALL_CLOCK_KIND, description, false, localizations);
583
518
  }
584
519
 
585
520
  /** A strict, date-only (`YYYY-MM-DD`) option — rejects any time component. */
586
- class DateOption<TRequired extends boolean> implements Option<Date, TRequired> {
587
- constructor(
588
- readonly description: string,
589
- readonly isRequired: TRequired,
590
- private readonly localizations: OptionLocalizations = {},
591
- ) {}
592
-
593
- required(): Option<Date, true> {
594
- return new DateOption<true>(this.description, true, this.localizations);
595
- }
596
-
597
- choices<const C extends readonly Date[]>(_values: C): Option<C[number], TRequired> {
598
- throw new OptionValidationError("Date options do not support choices.");
599
- }
600
-
601
- apply(container: OptionContainer, name: string): void {
602
- container.addStringOption((option) => {
603
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
604
- applyOptionLocalizations(option, this.localizations);
605
- return option;
606
- });
521
+ const DATE_KIND = textKind<Date>("Date", (raw) => {
522
+ // Strict `YYYY-MM-DD` with a real-calendar check (rejects `2026/03/15`,
523
+ // time-bearing input, and impossible dates like `2026-02-30`).
524
+ if (!DATE_ONLY_PATTERN.test(raw) || !isValidWallClock(raw)) {
525
+ throw new OptionValidationError(INVALID_DATE_ONLY_MESSAGE);
607
526
  }
608
-
609
- read(
610
- interaction: ChatInputCommandInteraction,
611
- name: string,
612
- ): TRequired extends true ? Date : Date | null {
613
- const raw = interaction.options.getString(name, this.isRequired as boolean);
614
- if (raw === null) {
615
- return null as TRequired extends true ? Date : Date | null;
616
- }
617
- // Strict `YYYY-MM-DD` with a real-calendar check (rejects `2026/03/15`,
618
- // time-bearing input, and impossible dates like `2026-02-30`).
619
- if (!DATE_ONLY_PATTERN.test(raw) || !isValidWallClock(raw)) {
620
- throw new OptionValidationError(INVALID_DATE_ONLY_MESSAGE);
621
- }
622
- return new Date(raw) as TRequired extends true ? Date : Date | null;
623
- }
624
- }
527
+ return new Date(raw);
528
+ });
625
529
 
626
530
  /**
627
531
  * A strict date-only (`YYYY-MM-DD`) option. The returned `Date` is UTC
@@ -633,7 +537,7 @@ export function createDateOption(
633
537
  description: string,
634
538
  localizations: OptionLocalizations = {},
635
539
  ): Option<Date, false> {
636
- return new DateOption<false>(description, false, localizations);
540
+ return new PlainOption<Date, false>(DATE_KIND, description, false, localizations);
637
541
  }
638
542
 
639
543
  /** The channel types Discord accepts on a channel option (guild channels only). */
@@ -655,54 +559,28 @@ export type ChannelOptionValue<TType extends AllowedChannelType> = Extract<
655
559
  }
656
560
  >;
657
561
 
658
- class ChannelOption<TType extends AllowedChannelType, TRequired extends boolean>
659
- implements Option<ChannelOptionValue<TType>, TRequired>
660
- {
661
- constructor(
662
- readonly description: string,
663
- readonly isRequired: TRequired,
664
- private readonly channelTypes: readonly TType[] | undefined,
665
- private readonly localizations: OptionLocalizations = {},
666
- ) {}
667
-
668
- required(): Option<ChannelOptionValue<TType>, true> {
669
- return new ChannelOption<TType, true>(
670
- this.description,
671
- true,
672
- this.channelTypes,
673
- this.localizations,
674
- );
675
- }
676
-
677
- choices<const C extends readonly ChannelOptionValue<TType>[]>(
678
- _values: C,
679
- ): Option<C[number], TRequired> {
680
- throw new OptionValidationError("Channel options do not support choices.");
681
- }
682
-
683
- apply(container: OptionContainer, name: string): void {
684
- container.addChannelOption((option) => {
685
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
686
- applyOptionLocalizations(option, this.localizations);
687
- if (this.channelTypes !== undefined) {
688
- option.addChannelTypes(...this.channelTypes);
689
- }
690
- return option;
691
- });
692
- }
693
-
694
- read(
695
- interaction: ChatInputCommandInteraction,
696
- name: string,
697
- ): TRequired extends true ? ChannelOptionValue<TType> : ChannelOptionValue<TType> | null {
562
+ function channelKind<TType extends AllowedChannelType>(
563
+ channelTypes: readonly TType[] | undefined,
564
+ ): OptionKind<ChannelOptionValue<TType>, OptionBuilder> {
565
+ return {
566
+ label: "Channel",
567
+ add: (container, configure) =>
568
+ container.addChannelOption((option) => {
569
+ configure(option);
570
+ if (channelTypes !== undefined) {
571
+ option.addChannelTypes(...channelTypes);
572
+ }
573
+ return option;
574
+ }),
698
575
  // The declared types are re-sent at read time: `addChannelTypes` only drives
699
576
  // the client-side picker, so Discord can still deliver another channel kind.
700
- return interaction.options.getChannel(
701
- name,
702
- this.isRequired as boolean,
703
- this.channelTypes,
704
- ) as TRequired extends true ? ChannelOptionValue<TType> : ChannelOptionValue<TType> | null;
705
- }
577
+ read: (interaction, name, required) =>
578
+ interaction.options.getChannel(
579
+ name,
580
+ required,
581
+ channelTypes,
582
+ ) as ChannelOptionValue<TType> | null,
583
+ };
706
584
  }
707
585
 
708
586
  export function createChannelOption<const TType extends AllowedChannelType = AllowedChannelType>(
@@ -710,138 +588,50 @@ export function createChannelOption<const TType extends AllowedChannelType = All
710
588
  channelTypes?: readonly TType[],
711
589
  localizations: OptionLocalizations = {},
712
590
  ): Option<ChannelOptionValue<TType>, false> {
713
- return new ChannelOption<TType, false>(description, false, channelTypes, localizations);
591
+ return new PlainOption<ChannelOptionValue<TType>, false>(
592
+ channelKind(channelTypes),
593
+ description,
594
+ false,
595
+ localizations,
596
+ );
714
597
  }
715
598
 
716
- class RoleOption<TRequired extends boolean> implements Option<Role, TRequired> {
717
- constructor(
718
- readonly description: string,
719
- readonly isRequired: TRequired,
720
- private readonly localizations: OptionLocalizations = {},
721
- ) {}
722
-
723
- required(): Option<Role, true> {
724
- return new RoleOption<true>(this.description, true, this.localizations);
725
- }
726
-
727
- choices<const C extends readonly Role[]>(_values: C): Option<C[number], TRequired> {
728
- throw new OptionValidationError("Role options do not support choices.");
729
- }
730
-
731
- apply(container: OptionContainer, name: string): void {
732
- container.addRoleOption((option) => {
733
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
734
- applyOptionLocalizations(option, this.localizations);
735
- return option;
736
- });
737
- }
738
-
739
- read(
740
- interaction: ChatInputCommandInteraction,
741
- name: string,
742
- ): TRequired extends true ? Role : Role | null {
743
- return interaction.options.getRole(name, this.isRequired as boolean) as TRequired extends true
744
- ? Role
745
- : Role | null;
746
- }
747
- }
599
+ const ROLE_KIND: OptionKind<Role, OptionBuilder> = {
600
+ label: "Role",
601
+ add: (container, configure) => container.addRoleOption(configuring(configure)),
602
+ read: (interaction, name, required) => interaction.options.getRole(name, required) as Role | null,
603
+ };
748
604
 
749
605
  export function createRoleOption(
750
606
  description: string,
751
607
  localizations: OptionLocalizations = {},
752
608
  ): Option<Role, false> {
753
- return new RoleOption<false>(description, false, localizations);
609
+ return new PlainOption<Role, false>(ROLE_KIND, description, false, localizations);
754
610
  }
755
611
 
756
- class MentionableOption<TRequired extends boolean> implements Option<Role | User, TRequired> {
757
- constructor(
758
- readonly description: string,
759
- readonly isRequired: TRequired,
760
- private readonly localizations: OptionLocalizations = {},
761
- ) {}
762
-
763
- required(): Option<Role | User, true> {
764
- return new MentionableOption<true>(this.description, true, this.localizations);
765
- }
766
-
767
- choices<const C extends readonly (Role | User)[]>(_values: C): Option<C[number], TRequired> {
768
- throw new OptionValidationError("Mentionable options do not support choices.");
769
- }
770
-
771
- apply(container: OptionContainer, name: string): void {
772
- container.addMentionableOption((option) => {
773
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
774
- applyOptionLocalizations(option, this.localizations);
775
- return option;
776
- });
777
- }
778
-
779
- read(
780
- interaction: ChatInputCommandInteraction,
781
- name: string,
782
- ): TRequired extends true ? Role | User : Role | User | null {
783
- return interaction.options.getMentionable(
784
- name,
785
- this.isRequired as boolean,
786
- ) as TRequired extends true ? Role | User : Role | User | null;
787
- }
788
- }
612
+ const MENTIONABLE_KIND: OptionKind<Role | User, OptionBuilder> = {
613
+ label: "Mentionable",
614
+ add: (container, configure) => container.addMentionableOption(configuring(configure)),
615
+ read: (interaction, name, required) =>
616
+ interaction.options.getMentionable(name, required) as Role | User | null,
617
+ };
789
618
 
790
619
  export function createMentionableOption(
791
620
  description: string,
792
621
  localizations: OptionLocalizations = {},
793
622
  ): Option<Role | User, false> {
794
- return new MentionableOption<false>(description, false, localizations);
623
+ return new PlainOption<Role | User, false>(MENTIONABLE_KIND, description, false, localizations);
795
624
  }
796
625
 
797
- class AttachmentOption<TRequired extends boolean> implements Option<Attachment, TRequired> {
798
- constructor(
799
- readonly description: string,
800
- readonly isRequired: TRequired,
801
- private readonly localizations: OptionLocalizations = {},
802
- ) {}
803
-
804
- required(): Option<Attachment, true> {
805
- return new AttachmentOption<true>(this.description, true, this.localizations);
806
- }
807
-
808
- choices<const C extends readonly Attachment[]>(_values: C): Option<C[number], TRequired> {
809
- throw new OptionValidationError("Attachment options do not support choices.");
810
- }
811
-
812
- apply(container: OptionContainer, name: string): void {
813
- container.addAttachmentOption((option) => {
814
- option.setName(name).setDescription(this.description).setRequired(this.isRequired);
815
- applyOptionLocalizations(option, this.localizations);
816
- return option;
817
- });
818
- }
819
-
820
- read(
821
- interaction: ChatInputCommandInteraction,
822
- name: string,
823
- ): TRequired extends true ? Attachment : Attachment | null {
824
- return interaction.options.getAttachment(
825
- name,
826
- this.isRequired as boolean,
827
- ) as TRequired extends true ? Attachment : Attachment | null;
828
- }
829
- }
626
+ const ATTACHMENT_KIND: OptionKind<Attachment, OptionBuilder> = {
627
+ label: "Attachment",
628
+ add: (container, configure) => container.addAttachmentOption(configuring(configure)),
629
+ read: (interaction, name, required) => interaction.options.getAttachment(name, required),
630
+ };
830
631
 
831
632
  export function createAttachmentOption(
832
633
  description: string,
833
634
  localizations: OptionLocalizations = {},
834
635
  ): Option<Attachment, false> {
835
- return new AttachmentOption<false>(description, false, localizations);
836
- }
837
-
838
- type AsRequired<O> = O extends Option<infer V, boolean> ? Option<V, true> : never;
839
-
840
- /** Turn every option in a record into its required variant. */
841
- export function allRequired<O extends Options>(fields: O): { [K in keyof O]: AsRequired<O[K]> } {
842
- const out = {} as { [K in keyof O]: AsRequired<O[K]> };
843
- for (const [key, option] of Object.entries(fields)) {
844
- out[key as keyof O] = option.required() as AsRequired<O[keyof O]>;
845
- }
846
- return out;
636
+ return new PlainOption<Attachment, false>(ATTACHMENT_KIND, description, false, localizations);
847
637
  }