@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alan Cunin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @azurioh/discord-kernel
2
+
3
+ The framework half of a Discord bot, without the bot: a command DSL, guards,
4
+ interaction routing, composed components, localisation, and the ports an
5
+ application implements. It knows discord.js and nothing else — no database, no
6
+ logger implementation, no opinion about what your bot does.
7
+
8
+ Built for Clean Architecture with vertically-sliced feature modules: the kernel
9
+ is the innermost ring, and the fact that it is a package rather than a folder is
10
+ what enforces that — an outer ring simply is not in its dependency graph.
11
+
12
+ ```bash
13
+ pnpm add @azurioh/discord-kernel discord.js
14
+ ```
15
+
16
+ `discord.js` is a **peer dependency**, never a dependency: two copies of the
17
+ library in one tree would break every `instanceof` the gateway relies on.
18
+
19
+ ## What is in it
20
+
21
+ | Area | What it gives you |
22
+ | ---- | ----------------- |
23
+ | `discord/command/` | `createCommand` (flat or grouped), typed options, guards (`allOf`/`anyOf`, permissions, roles, cooldowns), the per-interaction `Context`, autocomplete, the `CommandRouter` |
24
+ | `discord/interaction/` | `createButton`, `createModal`, the four typed select menus (`createStringSelect`, `createChannelSelect`, `createRoleSelect`, `createUserSelect`), the `InteractionRouter`/`InteractionDispatcher` seam |
25
+ | `discord/components/` | `ComponentRouter` for persistent `customId` routing, and collector-backed screens: `paginator`, `interactive-message`, `settings-editor` |
26
+ | `discord/events/` | `createEvent` and the `EventRouter` — the one place `client.on` is called |
27
+ | `discord/ui/` | Embeds with Discord's size limits handled, `createCard` containers, a configurable palette, a colour-input parser |
28
+ | `i18n/` | Catalogs (`{ en, fr? }`, `en` mandatory), a registry that fails the boot on a duplicate key, per-interaction locale resolution |
29
+ | `authz/` | An `Authorizer` port with `viewer`/`editor` grants and a `requireLevel` guard |
30
+ | `errors/` | `BusinessError` and friends, carrying an optional translation key beside the English source |
31
+ | `scheduler/` | A `Scheduler` port and a node-cron implementation |
32
+ | Ports | `Logger`, `DatabaseConnection`, `Presenter`, `Clock`, `ChannelExporter` — declared here, implemented by your app |
33
+
34
+ Every path is imported directly; there is no root barrel:
35
+
36
+ ```ts
37
+ import { createCommand } from "@azurioh/discord-kernel/discord/command/create-command";
38
+ import type { Logger } from "@azurioh/discord-kernel/logger";
39
+ import { type Catalog, createTranslator } from "@azurioh/discord-kernel/i18n";
40
+ ```
41
+
42
+ ## What is deliberately not in it
43
+
44
+ - **A database.** `DatabaseConnection` is a lifecycle port — connect, close — and
45
+ nothing more. Query APIs differ too much between engines to abstract usefully,
46
+ so an adapter exposes its own and your repositories are the only code that
47
+ touches it.
48
+ - **A logger.** `Logger` is a port; pino, Sentry or `console` live in your app.
49
+ - **A configuration shape.** The kernel ships env-reading primitives
50
+ (`requireEnv`, `enumEnv`, …); which keys exist is your composition root's call.
51
+ - **A palette.** Embeds render in Discord's blurple until you say otherwise, so a
52
+ bot never starts out wearing someone else's identity.
53
+
54
+ ## Making it yours
55
+
56
+ Two things are global by nature — a bot has one look, decided once — and are set
57
+ from the composition root before any module is constructed:
58
+
59
+ ```ts
60
+ import { configureEmbedColors } from "@azurioh/discord-kernel/discord/ui/colors";
61
+ import { registerColorAliases } from "@azurioh/discord-kernel/discord/ui/color-input";
62
+
63
+ configureEmbedColors({ brand: 0xf3c909 });
64
+ registerColorAliases({ gold: "#f3c909", or: "#f3c909" });
65
+ ```
66
+
67
+ Everything else is injected: your modules are factories receiving a container
68
+ you define, so nothing in the kernel is reachable as a singleton.
69
+
70
+ ## Requirements
71
+
72
+ Node 22.12+, discord.js 14.27+, TypeScript 5.9. The published build is
73
+ CommonJS with declaration files, resolved through explicit subpath `exports`.
74
+
75
+ ## License
76
+
77
+ MIT
@@ -0,0 +1,45 @@
1
+ /** Permission tiers, lowest to highest. `editor` implies `viewer`. */
2
+ export type PermissionLevel = "viewer" | "editor";
3
+ /** Numeric rank for comparing levels (`editor` ≥ `viewer`). */
4
+ export declare const LEVEL_RANK: Record<PermissionLevel, number>;
5
+ /** A single grant: either a role or a user, at a given level. */
6
+ export interface PermissionGrant {
7
+ readonly targetType: "role" | "user";
8
+ readonly targetId: string;
9
+ readonly level: PermissionLevel;
10
+ }
11
+ /** The full set of grants, split by target kind for cheap resolution. */
12
+ export interface PermissionGrants {
13
+ readonly roles: Record<string, PermissionLevel>;
14
+ readonly users: Record<string, PermissionLevel>;
15
+ }
16
+ /**
17
+ * Cross-cutting authorization port for feature-level permission tiers (as
18
+ * opposed to Discord's own per-command `defaultMemberPermissions`/guild
19
+ * permissions, which gate *visibility* and native capabilities). A module
20
+ * grants a role or a user a level; a guard checks whether a member meets the
21
+ * level required for an action.
22
+ *
23
+ * Deliberately has no notion of "admin": a bot-wide administrator bypass is a
24
+ * Discord permission concern, composed at the guard level with
25
+ * `anyOf(createPermissionGuard([Administrator]), requireLevel(...))` — keeping
26
+ * this port focused on the one thing it owns, feature-level grants.
27
+ */
28
+ export interface Authorizer {
29
+ /** Whether the user (directly, or via one of their roles) meets `required`. */
30
+ meetsLevel(userId: string, roleIds: readonly string[], required: PermissionLevel): Promise<boolean>;
31
+ grantRole(roleId: string, level: PermissionLevel): Promise<void>;
32
+ grantUser(userId: string, level: PermissionLevel): Promise<void>;
33
+ /** Returns whether a grant existed to remove. */
34
+ revokeRole(roleId: string): Promise<boolean>;
35
+ /** Returns whether a grant existed to remove. */
36
+ revokeUser(userId: string): Promise<boolean>;
37
+ listGrants(): Promise<PermissionGrants>;
38
+ }
39
+ /**
40
+ * Shared resolution used by every adapter: a direct user grant wins over role
41
+ * grants; otherwise the highest matching role level applies. Returns whether
42
+ * the resolved level meets `required`.
43
+ */
44
+ export declare function meetsResolvedLevel(grants: PermissionGrants, userId: string, roleIds: readonly string[], required: PermissionLevel): boolean;
45
+ //# sourceMappingURL=authorizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorizer.d.ts","sourceRoot":"","sources":["../../src/authz/authorizer.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAElD,+DAA+D;AAC/D,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAGtD,CAAC;AAEF,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CAChC;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IAC1B,+EAA+E;IAC/E,UAAU,CACT,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,eAAe,GACvB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,iDAAiD;IACjD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,iDAAiD;IACjD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,eAAe,GACvB,OAAO,CAeT"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LEVEL_RANK = void 0;
4
+ exports.meetsResolvedLevel = meetsResolvedLevel;
5
+ /** Numeric rank for comparing levels (`editor` ≥ `viewer`). */
6
+ exports.LEVEL_RANK = {
7
+ viewer: 1,
8
+ editor: 2,
9
+ };
10
+ /**
11
+ * Shared resolution used by every adapter: a direct user grant wins over role
12
+ * grants; otherwise the highest matching role level applies. Returns whether
13
+ * the resolved level meets `required`.
14
+ */
15
+ function meetsResolvedLevel(grants, userId, roleIds, required) {
16
+ const userLevel = grants.users[userId];
17
+ let resolved = userLevel ?? null;
18
+ if (!resolved) {
19
+ for (const roleId of roleIds) {
20
+ const roleLevel = grants.roles[roleId];
21
+ if (roleLevel && (!resolved || exports.LEVEL_RANK[roleLevel] > exports.LEVEL_RANK[resolved])) {
22
+ resolved = roleLevel;
23
+ }
24
+ }
25
+ }
26
+ if (!resolved) {
27
+ return false;
28
+ }
29
+ return exports.LEVEL_RANK[resolved] >= exports.LEVEL_RANK[required];
30
+ }
31
+ //# sourceMappingURL=authorizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorizer.js","sourceRoot":"","sources":["../../src/authz/authorizer.ts"],"names":[],"mappings":";;;AAuDA,gDAoBC;AAxED,+DAA+D;AAClD,QAAA,UAAU,GAAoC;IAC1D,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACT,CAAC;AA2CF;;;;GAIG;AACH,SAAgB,kBAAkB,CACjC,MAAwB,EACxB,MAAc,EACd,OAA0B,EAC1B,QAAyB;IAEzB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,QAAQ,GAA2B,SAAS,IAAI,IAAI,CAAC;IACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,SAAS,IAAI,CAAC,CAAC,QAAQ,IAAI,kBAAU,CAAC,SAAS,CAAC,GAAG,kBAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBAC9E,QAAQ,GAAG,SAAS,CAAC;YACtB,CAAC;QACF,CAAC;IACF,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,kBAAU,CAAC,QAAQ,CAAC,IAAI,kBAAU,CAAC,QAAQ,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { BaseInteraction } from "discord.js";
2
+ import type { Authorizer, PermissionLevel } from "../authz/authorizer";
3
+ import type { Guard } from "../discord/command/guard";
4
+ import type { LocalizedText } from "../i18n";
5
+ /**
6
+ * Role IDs held by the interacting member, resolved without a network call:
7
+ * `interaction.member.roles` is either the cached `GuildMemberRoleManager` (a
8
+ * cached-guild interaction) or a plain array of IDs (the raw API shape for an
9
+ * uncached one) — both are handled so the guard never has to fetch the member.
10
+ *
11
+ * Typed on the base `Interaction`, not on the command flavour a guard takes: a
12
+ * control clicked inside a message carries the member exactly the same way, and
13
+ * a rule that has to hold on both paths must be able to ask both.
14
+ */
15
+ export declare function memberRoleIds(interaction: BaseInteraction): string[];
16
+ /**
17
+ * Deny unless the member meets `required` on the injected {@link Authorizer}
18
+ * (directly, or via one of their roles). Compose with a Discord-permission
19
+ * guard through `anyOf(...)` when a bot-wide administrator bypass is wanted —
20
+ * this guard alone knows nothing about Discord permissions.
21
+ */
22
+ export declare function requireLevel(authz: Authorizer, required: PermissionLevel, denyMessage: LocalizedText): Guard;
23
+ //# sourceMappingURL=require-level-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-level-guard.d.ts","sourceRoot":"","sources":["../../src/authz/require-level-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM,EAAE,CAOpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,eAAe,EACzB,WAAW,EAAE,aAAa,GACxB,KAAK,CAOP"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.memberRoleIds = memberRoleIds;
4
+ exports.requireLevel = requireLevel;
5
+ /**
6
+ * Role IDs held by the interacting member, resolved without a network call:
7
+ * `interaction.member.roles` is either the cached `GuildMemberRoleManager` (a
8
+ * cached-guild interaction) or a plain array of IDs (the raw API shape for an
9
+ * uncached one) — both are handled so the guard never has to fetch the member.
10
+ *
11
+ * Typed on the base `Interaction`, not on the command flavour a guard takes: a
12
+ * control clicked inside a message carries the member exactly the same way, and
13
+ * a rule that has to hold on both paths must be able to ask both.
14
+ */
15
+ function memberRoleIds(interaction) {
16
+ const member = interaction.member;
17
+ if (!member || typeof member !== "object" || !("roles" in member)) {
18
+ return [];
19
+ }
20
+ const roles = member.roles;
21
+ return Array.isArray(roles) ? roles : Array.from(roles.cache.keys());
22
+ }
23
+ /**
24
+ * Deny unless the member meets `required` on the injected {@link Authorizer}
25
+ * (directly, or via one of their roles). Compose with a Discord-permission
26
+ * guard through `anyOf(...)` when a bot-wide administrator bypass is wanted —
27
+ * this guard alone knows nothing about Discord permissions.
28
+ */
29
+ function requireLevel(authz, required, denyMessage) {
30
+ return {
31
+ async check(interaction) {
32
+ const ok = await authz.meetsLevel(interaction.user.id, memberRoleIds(interaction), required);
33
+ return ok ? { ok: true } : { ok: false, message: denyMessage };
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=require-level-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-level-guard.js","sourceRoot":"","sources":["../../src/authz/require-level-guard.ts"],"names":[],"mappings":";;AAeA,sCAOC;AAQD,oCAWC;AApCD;;;;;;;;;GASG;AACH,SAAgB,aAAa,CAAC,WAA4B;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;IAClC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,YAAY,CAC3B,KAAiB,EACjB,QAAyB,EACzB,WAA0B;IAE1B,OAAO;QACN,KAAK,CAAC,KAAK,CAAC,WAA+B;YAC1C,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,CAAC;YAC7F,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAChE,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Abstraction over the current time. Injecting a clock (instead of calling
3
+ * `new Date()` directly) lets domain/application code be tested deterministically
4
+ * and keeps those layers free of ambient I/O.
5
+ */
6
+ export interface Clock {
7
+ now(): Date;
8
+ }
9
+ /** The real clock, backed by the system time. */
10
+ export declare const systemClock: Clock;
11
+ /** A fixed clock for tests. */
12
+ export declare function fixedClock(instant: Date): Clock;
13
+ //# sourceMappingURL=clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACrB,GAAG,IAAI,IAAI,CAAC;CACZ;AAED,iDAAiD;AACjD,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,+BAA+B;AAC/B,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,KAAK,CAE/C"}
package/dist/clock.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.systemClock = void 0;
4
+ exports.fixedClock = fixedClock;
5
+ /** The real clock, backed by the system time. */
6
+ exports.systemClock = {
7
+ now: () => new Date(),
8
+ };
9
+ /** A fixed clock for tests. */
10
+ function fixedClock(instant) {
11
+ return { now: () => new Date(instant) };
12
+ }
13
+ //# sourceMappingURL=clock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":";;;AAeA,gCAEC;AARD,iDAAiD;AACpC,QAAA,WAAW,GAAU;IACjC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;CACrB,CAAC;AAEF,+BAA+B;AAC/B,SAAgB,UAAU,CAAC,OAAa;IACvC,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;AACzC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Serialises asynchronous work per key while leaving distinct keys concurrent.
3
+ */
4
+ export interface KeyedQueue {
5
+ /** Run `task` after every task already queued for `key` has settled. */
6
+ run<T>(key: string, task: () => Promise<T>): Promise<T>;
7
+ /** Number of keys with work in flight — exposed so tests can assert no leak. */
8
+ readonly size: number;
9
+ }
10
+ /**
11
+ * Gateway handlers fire concurrently for the same entity (two rapid events on one
12
+ * channel, a member joining twice, an event replay after a resume), so
13
+ * read-modify-write logic keyed on that entity interleaves and corrupts state.
14
+ * Chaining per key restores ordering without a global lock, which would
15
+ * needlessly serialise unrelated guilds.
16
+ */
17
+ export declare function createKeyedQueue(): KeyedQueue;
18
+ //# sourceMappingURL=keyed-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyed-queue.d.ts","sourceRoot":"","sources":["../../src/concurrency/keyed-queue.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,wEAAwE;IACxE,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAgC7C"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createKeyedQueue = createKeyedQueue;
4
+ /**
5
+ * Gateway handlers fire concurrently for the same entity (two rapid events on one
6
+ * channel, a member joining twice, an event replay after a resume), so
7
+ * read-modify-write logic keyed on that entity interleaves and corrupts state.
8
+ * Chaining per key restores ordering without a global lock, which would
9
+ * needlessly serialise unrelated guilds.
10
+ */
11
+ function createKeyedQueue() {
12
+ const chains = new Map();
13
+ return {
14
+ get size() {
15
+ return chains.size;
16
+ },
17
+ run(key, task) {
18
+ const previous = chains.get(key) ?? Promise.resolve();
19
+ // The chain continues on the *settled* tail: a rejected task must not
20
+ // poison the queue for the tasks behind it, while `result` keeps the
21
+ // rejection observable by this caller alone.
22
+ const settledTail = previous.then(() => undefined, () => undefined);
23
+ const result = settledTail.then(task);
24
+ const tail = result.then(() => undefined, () => undefined);
25
+ chains.set(key, tail);
26
+ // Only the task that is still the tail may drop the entry, otherwise a
27
+ // later enqueue would be forgotten and the Map would grow unbounded.
28
+ void tail.then(() => {
29
+ if (chains.get(key) === tail) {
30
+ chains.delete(key);
31
+ }
32
+ });
33
+ return result;
34
+ },
35
+ };
36
+ }
37
+ //# sourceMappingURL=keyed-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyed-queue.js","sourceRoot":"","sources":["../../src/concurrency/keyed-queue.ts"],"names":[],"mappings":";;AAiBA,4CAgCC;AAvCD;;;;;;GAMG;AACH,SAAgB,gBAAgB;IAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IAEnD,OAAO;QACN,IAAI,IAAI;YACP,OAAO,MAAM,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,GAAG,CAAI,GAAW,EAAE,IAAsB;YACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACtD,sEAAsE;YACtE,qEAAqE;YACrE,6CAA6C;YAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAChC,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;YACF,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CACvB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACtB,uEAAuE;YACvE,qEAAqE;YACrE,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;gBACnB,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;oBAC9B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACpB,CAAC;YACF,CAAC,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QACf,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Runs every task in `items` through `worker`, never letting more than `limit`
3
+ * run at once, and resolves once all have settled. Results are returned in the
4
+ * same order as `items`, regardless of completion order.
5
+ *
6
+ * Exists so callers that must fan out many independent async calls (e.g.
7
+ * fetching a reaction's user list per distinct emoji across thousands of
8
+ * messages) can bound how many are in flight without going fully sequential —
9
+ * without pulling in an external dependency for what is, in essence, a small
10
+ * counter and a queue (the same shape of problem `keyed-queue.ts` already
11
+ * solves for *serialised* per-key work, just bounding *parallel* work here
12
+ * instead).
13
+ */
14
+ export declare function runWithConcurrency<T, R>(items: readonly T[], limit: number, worker: (item: T, index: number) => Promise<R>): Promise<R[]>;
15
+ //# sourceMappingURL=run-with-concurrency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-with-concurrency.d.ts","sourceRoot":"","sources":["../../src/concurrency/run-with-concurrency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAC5C,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5C,OAAO,CAAC,CAAC,EAAE,CAAC,CA0Bd"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runWithConcurrency = runWithConcurrency;
4
+ /**
5
+ * Runs every task in `items` through `worker`, never letting more than `limit`
6
+ * run at once, and resolves once all have settled. Results are returned in the
7
+ * same order as `items`, regardless of completion order.
8
+ *
9
+ * Exists so callers that must fan out many independent async calls (e.g.
10
+ * fetching a reaction's user list per distinct emoji across thousands of
11
+ * messages) can bound how many are in flight without going fully sequential —
12
+ * without pulling in an external dependency for what is, in essence, a small
13
+ * counter and a queue (the same shape of problem `keyed-queue.ts` already
14
+ * solves for *serialised* per-key work, just bounding *parallel* work here
15
+ * instead).
16
+ */
17
+ async function runWithConcurrency(items, limit, worker) {
18
+ if (items.length === 0) {
19
+ return [];
20
+ }
21
+ // `noUncheckedIndexedAccess` makes every `results[i]` a possible `undefined`
22
+ // at the type level; asserting it away here is correct because every index
23
+ // is written exactly once below, before this array is ever returned.
24
+ const results = new Array(items.length);
25
+ let nextIndex = 0;
26
+ async function runNext() {
27
+ const currentIndex = nextIndex;
28
+ nextIndex += 1;
29
+ if (currentIndex >= items.length) {
30
+ return;
31
+ }
32
+ const item = items[currentIndex];
33
+ results[currentIndex] = await worker(item, currentIndex);
34
+ await runNext();
35
+ }
36
+ const workerCount = Math.max(1, Math.min(limit, items.length));
37
+ await Promise.all(Array.from({ length: workerCount }, runNext));
38
+ return results;
39
+ }
40
+ //# sourceMappingURL=run-with-concurrency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-with-concurrency.js","sourceRoot":"","sources":["../../src/concurrency/run-with-concurrency.ts"],"names":[],"mappings":";;AAaA,gDA8BC;AA3CD;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,kBAAkB,CACvC,KAAmB,EACnB,KAAa,EACb,MAA8C;IAE9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACX,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,qEAAqE;IACrE,MAAM,OAAO,GAAG,IAAI,KAAK,CAAI,KAAK,CAAC,MAAM,CAAQ,CAAC;IAClD,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,UAAU,OAAO;QACrB,MAAM,YAAY,GAAG,SAAS,CAAC;QAC/B,SAAS,IAAI,CAAC,CAAC;QACf,IAAI,YAAY,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO;QACR,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAM,CAAC;QACtC,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACzD,MAAM,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IAEhE,OAAO,OAAO,CAAC;AAChB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Generic, dependency-free helpers for reading and validating environment
3
+ * variables. The concrete application config (which keys exist, their defaults)
4
+ * is assembled by the composition root in `bootstrap/config.ts` — the core only
5
+ * provides the primitives so any bot can declare its own shape.
6
+ */
7
+ type Env = Record<string, string | undefined>;
8
+ /** Read a required variable, throwing {@link MissingEnvError} when absent. */
9
+ export declare function requireEnv(key: string, env?: Env): string;
10
+ /** Read an optional variable, returning `fallback` (or `undefined`) when absent. */
11
+ export declare function optionalEnv(key: string, fallback?: string, env?: Env): string | undefined;
12
+ /** Read a boolean variable (`true`/`1`/`yes` are truthy). */
13
+ export declare function boolEnv(key: string, fallback: boolean, env?: Env): boolean;
14
+ /** Read a variable constrained to a fixed set of values. */
15
+ export declare function enumEnv<const T extends readonly string[]>(key: string, allowed: T, fallback: T[number], env?: Env): T[number];
16
+ export {};
17
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,GAAiB,GAAG,MAAM,CAMtE;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAC1B,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,EACjB,GAAG,GAAE,GAAiB,GACpB,MAAM,GAAG,SAAS,CAMpB;AAED,6DAA6D;AAC7D,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAE,GAAiB,GAAG,OAAO,CAMvF;AAED,4DAA4D;AAC5D,wBAAgB,OAAO,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EACxD,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,EACV,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EACnB,GAAG,GAAE,GAAiB,GACpB,CAAC,CAAC,MAAM,CAAC,CASX"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * Generic, dependency-free helpers for reading and validating environment
4
+ * variables. The concrete application config (which keys exist, their defaults)
5
+ * is assembled by the composition root in `bootstrap/config.ts` — the core only
6
+ * provides the primitives so any bot can declare its own shape.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.requireEnv = requireEnv;
10
+ exports.optionalEnv = optionalEnv;
11
+ exports.boolEnv = boolEnv;
12
+ exports.enumEnv = enumEnv;
13
+ const errors_1 = require("../config/errors");
14
+ /** Read a required variable, throwing {@link MissingEnvError} when absent. */
15
+ function requireEnv(key, env = process.env) {
16
+ const value = env[key];
17
+ if (value === undefined || value.trim() === "") {
18
+ throw new errors_1.MissingEnvError(key);
19
+ }
20
+ return value;
21
+ }
22
+ /** Read an optional variable, returning `fallback` (or `undefined`) when absent. */
23
+ function optionalEnv(key, fallback, env = process.env) {
24
+ const value = env[key];
25
+ if (value === undefined || value.trim() === "") {
26
+ return fallback;
27
+ }
28
+ return value;
29
+ }
30
+ /** Read a boolean variable (`true`/`1`/`yes` are truthy). */
31
+ function boolEnv(key, fallback, env = process.env) {
32
+ const value = env[key]?.trim().toLowerCase();
33
+ if (value === undefined || value === "") {
34
+ return fallback;
35
+ }
36
+ return value === "true" || value === "1" || value === "yes";
37
+ }
38
+ /** Read a variable constrained to a fixed set of values. */
39
+ function enumEnv(key, allowed, fallback, env = process.env) {
40
+ const value = env[key]?.trim();
41
+ if (value === undefined || value === "") {
42
+ return fallback;
43
+ }
44
+ if (!allowed.includes(value)) {
45
+ throw new errors_1.InvalidEnvError(key, `one of ${allowed.join(", ")}`);
46
+ }
47
+ return value;
48
+ }
49
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAOH,gCAMC;AAGD,kCAUC;AAGD,0BAMC;AAGD,0BAcC;AAlDD,4CAAmE;AAInE,8EAA8E;AAC9E,SAAgB,UAAU,CAAC,GAAW,EAAE,MAAW,OAAO,CAAC,GAAG;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAe,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,oFAAoF;AACpF,SAAgB,WAAW,CAC1B,GAAW,EACX,QAAiB,EACjB,MAAW,OAAO,CAAC,GAAG;IAEtB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChD,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,6DAA6D;AAC7D,SAAgB,OAAO,CAAC,GAAW,EAAE,QAAiB,EAAE,MAAW,OAAO,CAAC,GAAG;IAC7E,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,KAAK,CAAC;AAC7D,CAAC;AAED,4DAA4D;AAC5D,SAAgB,OAAO,CACtB,GAAW,EACX,OAAU,EACV,QAAmB,EACnB,MAAW,OAAO,CAAC,GAAG;IAEtB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,wBAAe,CAAC,GAAG,EAAE,UAAU,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAkB,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Failures raised while reading environment variables. They live apart from the
3
+ * reading logic so a consumer can catch or re-map them without pulling in the
4
+ * `env` helpers, and so the helpers file stays focused on behaviour.
5
+ */
6
+ /** Raised when a required environment variable is missing or empty. */
7
+ export declare class MissingEnvError extends Error {
8
+ constructor(key: string);
9
+ }
10
+ /** Raised when an environment variable holds an invalid value. */
11
+ export declare class InvalidEnvError extends Error {
12
+ constructor(key: string, expected: string);
13
+ }
14
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/config/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,uEAAuE;AACvE,qBAAa,eAAgB,SAAQ,KAAK;gBAC7B,GAAG,EAAE,MAAM;CAIvB;AAED,kEAAkE;AAClE,qBAAa,eAAgB,SAAQ,KAAK;gBAC7B,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAIzC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Failures raised while reading environment variables. They live apart from the
4
+ * reading logic so a consumer can catch or re-map them without pulling in the
5
+ * `env` helpers, and so the helpers file stays focused on behaviour.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.InvalidEnvError = exports.MissingEnvError = void 0;
9
+ /** Raised when a required environment variable is missing or empty. */
10
+ class MissingEnvError extends Error {
11
+ constructor(key) {
12
+ super(`Missing required environment variable: ${key}`);
13
+ this.name = "MissingEnvError";
14
+ }
15
+ }
16
+ exports.MissingEnvError = MissingEnvError;
17
+ /** Raised when an environment variable holds an invalid value. */
18
+ class InvalidEnvError extends Error {
19
+ constructor(key, expected) {
20
+ super(`Invalid environment variable ${key}: expected ${expected}`);
21
+ this.name = "InvalidEnvError";
22
+ }
23
+ }
24
+ exports.InvalidEnvError = InvalidEnvError;
25
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/config/errors.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,uEAAuE;AACvE,MAAa,eAAgB,SAAQ,KAAK;IACzC,YAAY,GAAW;QACtB,KAAK,CAAC,0CAA0C,GAAG,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAC/B,CAAC;CACD;AALD,0CAKC;AAED,kEAAkE;AAClE,MAAa,eAAgB,SAAQ,KAAK;IACzC,YAAY,GAAW,EAAE,QAAgB;QACxC,KAAK,CAAC,gCAAgC,GAAG,cAAc,QAAQ,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAC/B,CAAC;CACD;AALD,0CAKC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Interpret a wall-clock datetime string as local time in `timeZone` and return
3
+ * the matching UTC instant. Discord renders stored timestamps in each viewer's
4
+ * own timezone, so an event entered as "18:00" guild-local must be stored as the
5
+ * equivalent UTC instant — otherwise it drifts by the guild's UTC offset.
6
+ * @throws RangeError When the input does not match the wall-clock grammar.
7
+ */
8
+ export declare function zonedWallClockToUtc(input: string, timeZone: string): Date;
9
+ /** Whether `input` is a valid wall-clock datetime string. */
10
+ export declare function isValidWallClock(input: string): boolean;
11
+ /** `YYYY-MM-DD` of a date-only value, read in UTC (matches `Date`-only options). */
12
+ export declare function dayString(date: Date): string;
13
+ /** Start of `date`'s calendar day (00:00:00) anchored to `timeZone`. */
14
+ export declare function startOfGuildDay(date: Date, timeZone: string): Date;
15
+ /** End of `date`'s calendar day (23:59:59) anchored to `timeZone`. */
16
+ export declare function endOfGuildDay(date: Date, timeZone: string): Date;
17
+ /**
18
+ * Monday 00:00 (guild-local) of the week following `reference`'s week. Used to
19
+ * anchor "next week"/"week after next" ranges to the guild's own calendar
20
+ * rather than to UTC day boundaries, which can disagree with the guild's local
21
+ * week near midnight around the timezone offset.
22
+ */
23
+ export declare function startOfNextGuildWeek(reference: Date, timeZone: string): Date;
24
+ /**
25
+ * Add `days` guild-local calendar days to `instant`, re-anchoring to
26
+ * `timeZone`'s midnight. `instant` is assumed to already be a guild-local
27
+ * midnight (e.g. the result of {@link startOfGuildDay} or
28
+ * {@link startOfNextGuildWeek}); adding a fixed millisecond offset instead
29
+ * would drift by the DST delta whenever the interval crosses a transition —
30
+ * this reads the calendar date in `timeZone` first, so the result is always
31
+ * that timezone's actual midnight `days` later.
32
+ */
33
+ export declare function addGuildDays(instant: Date, days: number, timeZone: string): Date;
34
+ //# sourceMappingURL=datetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AAiGA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsBzE;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE5C;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAElE;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEhE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsC5E;AACD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAkBhF"}