@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
@@ -0,0 +1,300 @@
1
+ import * as z from "zod";
2
+ import { parseColor } from "@/color";
3
+ import { parseDuration } from "@/settings/duration";
4
+ import {
5
+ type AnyField,
6
+ type BoundsOptions,
7
+ FIELD_LIMITS,
8
+ type FieldChoice,
9
+ unhandledFieldKind,
10
+ } from "@/settings/fields/field";
11
+ import { isPlainObject } from "@/settings/is-plain-object";
12
+ import type { SettingsIssueCode } from "@/settings/settings-validation-error";
13
+
14
+ /** A Discord snowflake: 17 to 20 digits. */
15
+ const SNOWFLAKE_PATTERN = /^\d{17,20}$/;
16
+
17
+ /** A stored colour: `#rrggbb`, lowercase. */
18
+ const STORED_COLOR_PATTERN = /^#[0-9a-f]{6}$/;
19
+
20
+ /** Custom Zod issue parameter carrying the kernel issue code. */
21
+ const ISSUE_CODE_PARAM = "settingsCode";
22
+
23
+ /** Why one value failed its field's own validation, before any guild check. */
24
+ export interface FieldValueIssue {
25
+ /**
26
+ * Path inside the value: `[]` for the value, `[i]` for a list item, `[key]`
27
+ * for a toggle.
28
+ */
29
+ readonly path: readonly (number | string)[];
30
+ readonly code: SettingsIssueCode;
31
+ /** `{ min }` or `{ max }` for bound codes, empty otherwise. */
32
+ readonly params: Readonly<Record<string, number>>;
33
+ }
34
+
35
+ /** Outcome of {@link parseFieldValue}: the stored form of the value, or its issues. */
36
+ export type FieldParseResult =
37
+ | { readonly ok: true; readonly value: unknown }
38
+ | { readonly ok: false; readonly issues: readonly FieldValueIssue[] };
39
+
40
+ const SCHEMAS = new WeakMap<AnyField, z.ZodType>();
41
+
42
+ /**
43
+ * Check a value against its field's kind and constraints (kind, bounds,
44
+ * lengths, list size, duplicates, enum membership, toggle keys) and turn it
45
+ * into its stored form: a colour becomes `#rrggbb`, a duration whole seconds,
46
+ * toggles a boolean for every declared key (a missing key takes its default).
47
+ *
48
+ * @param params.field - the field the value belongs to.
49
+ * @param params.value - the raw value, of any type.
50
+ * @returns the stored value, or every issue found.
51
+ */
52
+ export function parseFieldValue(params: { field: AnyField; value: unknown }): FieldParseResult {
53
+ const result = schemaOf(params.field).safeParse(params.value);
54
+ if (result.success) {
55
+ return { ok: true, value: result.data };
56
+ }
57
+ return { ok: false, issues: result.error.issues.map(toFieldValueIssue) };
58
+ }
59
+
60
+ /**
61
+ * Drop from a stored value what its field no longer declares, so it reads as
62
+ * if the key had never been stored (FR-017): a toggle whose key left `keys`.
63
+ * Any other value is returned as is. Submissions skip this step, so an
64
+ * undeclared toggle there stays `unknownChoice`.
65
+ *
66
+ * @param params.field - the field the stored value belongs to.
67
+ * @param params.value - the value as read from storage, of any type.
68
+ * @returns the value without its undeclared toggles.
69
+ */
70
+ export function pruneStoredValue(params: { field: AnyField; value: unknown }): unknown {
71
+ const { field: target, value } = params;
72
+ if (target.spec.kind !== "toggles" || !isPlainObject(value)) {
73
+ return value;
74
+ }
75
+ const { keys } = target.spec;
76
+ return Object.fromEntries(Object.entries(value).filter(([key]) => keys.includes(key)));
77
+ }
78
+
79
+ /** One JSON Schema object, open to in-place annotation. */
80
+ export type JsonSchemaNode = Record<string, unknown>;
81
+
82
+ /**
83
+ * Describe the stored values of a set of fields as a JSON Schema draft 2020-12
84
+ * object: each field's kind and constraints, a field without `required` being
85
+ * optional, and no undeclared key. The field's own texts and hints are added
86
+ * by `annotate`, called once for each field and each list item on the schema
87
+ * that describes it.
88
+ *
89
+ * @param params.fields - the fields, by key.
90
+ * @param params.annotate - adds a field's annotations to its schema, in place.
91
+ * @returns the JSON Schema document, with `$schema`.
92
+ */
93
+ export function fieldsJsonSchema(params: {
94
+ fields: Readonly<Record<string, AnyField>>;
95
+ annotate: (field: AnyField, schema: JsonSchemaNode) => void;
96
+ }): JsonSchemaNode {
97
+ const described = new Map<object, AnyField>();
98
+ const shape: Record<string, z.ZodType> = {};
99
+ for (const [key, declared] of Object.entries(params.fields)) {
100
+ const schema = declared.required ? schemaOf(declared) : schemaOf(declared).optional();
101
+ described.set(schema, declared);
102
+ if (declared.spec.kind === "list") {
103
+ described.set(schemaOf(declared.spec.item), declared.spec.item);
104
+ }
105
+ shape[key] = schema;
106
+ }
107
+ return z.toJSONSchema(z.object(shape), {
108
+ target: "draft-2020-12",
109
+ override: ({ zodSchema, jsonSchema }) => {
110
+ const declared = described.get(zodSchema);
111
+ if (declared === undefined) {
112
+ return;
113
+ }
114
+ if (declared.spec.kind === "list") {
115
+ // Duplicates are rejected by a refinement, which Zod cannot describe.
116
+ jsonSchema.uniqueItems = true;
117
+ }
118
+ params.annotate(declared, jsonSchema);
119
+ },
120
+ });
121
+ }
122
+
123
+ function schemaOf(target: AnyField): z.ZodType {
124
+ const cached = SCHEMAS.get(target);
125
+ if (cached !== undefined) {
126
+ return cached;
127
+ }
128
+ const schema = compileSchema(target);
129
+ SCHEMAS.set(target, schema);
130
+ return schema;
131
+ }
132
+
133
+ /**
134
+ * Length checks (text length, list size) are gated behind a bare type check
135
+ * with `pipe`: Zod keeps running them after a type failure whenever the input
136
+ * has a `length`, which would report a length issue on top of `type`.
137
+ *
138
+ * Every schema ends on the stored form of its value, never on a transform:
139
+ * {@link fieldsJsonSchema} describes that output, and a transform has no JSON
140
+ * Schema equivalent.
141
+ */
142
+ function compileSchema(target: AnyField): z.ZodType {
143
+ const { spec } = target;
144
+ switch (spec.kind) {
145
+ case "channel":
146
+ case "role":
147
+ case "user":
148
+ return z.string().regex(SNOWFLAKE_PATTERN);
149
+ case "color":
150
+ return z.string().transform(toStoredColor).pipe(z.string().regex(STORED_COLOR_PATTERN));
151
+ case "duration":
152
+ return z.preprocess(toSeconds, withBounds(z.int().min(0), spec));
153
+ case "enum":
154
+ return z.enum(spec.choices.map(choiceValue));
155
+ case "integer":
156
+ return withBounds(z.int(), spec);
157
+ case "number":
158
+ return withBounds(z.number(), spec);
159
+ case "text":
160
+ return z.string().pipe(
161
+ z
162
+ .string()
163
+ .min(spec.minLength ?? 0)
164
+ .max(spec.maxLength ?? FIELD_LIMITS.textMaxLength),
165
+ );
166
+ case "boolean":
167
+ return z.boolean();
168
+ case "secret":
169
+ return z.string();
170
+ case "list":
171
+ return z.array(z.unknown()).pipe(
172
+ z
173
+ .array(schemaOf(spec.item))
174
+ .min(spec.minItems ?? 0)
175
+ .max(spec.maxItems ?? FIELD_LIMITS.listMaxItems)
176
+ .superRefine(rejectDuplicates),
177
+ );
178
+ case "toggles":
179
+ return z
180
+ .record(z.string(), z.unknown())
181
+ .superRefine(checkToggles(spec.keys))
182
+ .transform((toggles) => ({ ...(target.default as object), ...toggles }))
183
+ .pipe(storedToggles(spec.keys));
184
+ default:
185
+ return unhandledFieldKind(spec);
186
+ }
187
+ }
188
+
189
+ /** Stored toggles: a boolean per declared key, a missing key taking its default. */
190
+ function storedToggles(
191
+ keys: readonly string[],
192
+ ): z.ZodObject<Record<string, z.ZodOptional<z.ZodBoolean>>, z.core.$strict> {
193
+ return z.strictObject(Object.fromEntries(keys.map((key) => [key, z.boolean().optional()])));
194
+ }
195
+
196
+ function withBounds(schema: z.ZodNumber, bounds: BoundsOptions): z.ZodNumber {
197
+ const withMin = bounds.min === undefined ? schema : schema.min(bounds.min);
198
+ return bounds.max === undefined ? withMin : withMin.max(bounds.max);
199
+ }
200
+
201
+ function choiceValue(choice: FieldChoice<string>): string {
202
+ return choice.value;
203
+ }
204
+
205
+ function toStoredColor(raw: string, ctx: z.core.$RefinementCtx): string {
206
+ const hex = parseColor(raw);
207
+ if (hex === null) {
208
+ ctx.addIssue({ code: "custom", input: raw, params: { [ISSUE_CODE_PARAM]: "type" } });
209
+ return z.NEVER;
210
+ }
211
+ return hex;
212
+ }
213
+
214
+ /** A duration string becomes seconds; anything else is left for the schema to judge. */
215
+ function toSeconds(input: unknown): unknown {
216
+ if (typeof input !== "string") {
217
+ return input;
218
+ }
219
+ return parseDuration(input) ?? input;
220
+ }
221
+
222
+ function rejectDuplicates(values: readonly unknown[], ctx: z.core.$RefinementCtx): void {
223
+ const seen = new Set<unknown>();
224
+ for (const [index, value] of values.entries()) {
225
+ if (seen.has(value)) {
226
+ ctx.addIssue({
227
+ code: "custom",
228
+ input: value,
229
+ path: [index],
230
+ params: { [ISSUE_CODE_PARAM]: "duplicate" },
231
+ });
232
+ }
233
+ seen.add(value);
234
+ }
235
+ }
236
+
237
+ /** Every toggle must be a declared key holding a boolean. */
238
+ function checkToggles(
239
+ keys: readonly string[],
240
+ ): (toggles: Record<string, unknown>, ctx: z.core.$RefinementCtx) => void {
241
+ return (toggles, ctx) => {
242
+ for (const [key, value] of Object.entries(toggles)) {
243
+ if (!keys.includes(key)) {
244
+ ctx.addIssue({
245
+ code: "custom",
246
+ input: value,
247
+ path: [key],
248
+ params: { [ISSUE_CODE_PARAM]: "unknownChoice" },
249
+ });
250
+ } else if (typeof value !== "boolean") {
251
+ ctx.addIssue({ code: "custom", input: value, path: [key] });
252
+ }
253
+ }
254
+ };
255
+ }
256
+
257
+ function toFieldValueIssue(issue: z.core.$ZodIssue): FieldValueIssue {
258
+ const path = issue.path.filter(isPathSegment);
259
+ switch (issue.code) {
260
+ case "too_small":
261
+ return { path, code: minCode(issue.origin), params: { min: Number(issue.minimum) } };
262
+ case "too_big":
263
+ return { path, code: maxCode(issue.origin), params: { max: Number(issue.maximum) } };
264
+ case "invalid_value":
265
+ return { path, code: "unknownChoice", params: {} };
266
+ case "custom":
267
+ return { path, code: customCode(issue.params), params: {} };
268
+ default:
269
+ return { path, code: "type", params: {} };
270
+ }
271
+ }
272
+
273
+ function isPathSegment(segment: PropertyKey): segment is number | string {
274
+ return typeof segment !== "symbol";
275
+ }
276
+
277
+ function minCode(origin: string): SettingsIssueCode {
278
+ if (origin === "array") {
279
+ return "minItems";
280
+ }
281
+ if (origin === "string") {
282
+ return "minLength";
283
+ }
284
+ return "min";
285
+ }
286
+
287
+ function maxCode(origin: string): SettingsIssueCode {
288
+ if (origin === "array") {
289
+ return "maxItems";
290
+ }
291
+ if (origin === "string") {
292
+ return "maxLength";
293
+ }
294
+ return "max";
295
+ }
296
+
297
+ function customCode(params: Readonly<Record<string, unknown>> | undefined): SettingsIssueCode {
298
+ const code = params?.[ISSUE_CODE_PARAM];
299
+ return code === "duplicate" || code === "unknownChoice" ? code : "type";
300
+ }
@@ -0,0 +1,69 @@
1
+ import type { ChannelKind, GuildDirectory } from "@/settings/ports/guild-directory";
2
+
3
+ interface NamedEntity {
4
+ readonly id: string;
5
+ readonly name: string;
6
+ }
7
+
8
+ interface ChannelEntity extends NamedEntity {
9
+ readonly kind: ChannelKind;
10
+ }
11
+
12
+ /** Entities of one guild; omitted lists are empty. */
13
+ export interface GuildDirectorySeedEntry {
14
+ readonly channels?: readonly ChannelEntity[];
15
+ readonly roles?: readonly NamedEntity[];
16
+ readonly members?: readonly NamedEntity[];
17
+ }
18
+
19
+ /** Seeded guild contents, keyed by guild id. */
20
+ export type GuildDirectorySeed = Readonly<Record<string, GuildDirectorySeedEntry>>;
21
+
22
+ function findById<T extends NamedEntity>(entities: readonly T[] | undefined, id: string): T | null {
23
+ return entities?.find((entity) => entity.id === id) ?? null;
24
+ }
25
+
26
+ function searchByPrefix<T extends NamedEntity>(
27
+ entities: readonly T[] | undefined,
28
+ query: string,
29
+ ): T[] {
30
+ const prefix = query.toLowerCase();
31
+ return (entities ?? []).filter((entity) => entity.name.toLowerCase().startsWith(prefix));
32
+ }
33
+
34
+ /**
35
+ * {@link GuildDirectory} over a fixed seed: the test twin for settings logic
36
+ * that checks or suggests channels, roles and members. Search matches a
37
+ * case-insensitive name prefix, in seed order.
38
+ */
39
+ export function createInMemoryGuildDirectory(seed: GuildDirectorySeed): GuildDirectory {
40
+ return {
41
+ async channel(guildId, id) {
42
+ return findById(seed[guildId]?.channels, id);
43
+ },
44
+
45
+ async role(guildId, id) {
46
+ return findById(seed[guildId]?.roles, id);
47
+ },
48
+
49
+ async member(guildId, id) {
50
+ return findById(seed[guildId]?.members, id);
51
+ },
52
+
53
+ async searchChannels(guildId, query, kinds) {
54
+ const matches = searchByPrefix(seed[guildId]?.channels, query);
55
+ if (kinds === undefined) {
56
+ return matches;
57
+ }
58
+ return matches.filter((channel) => kinds.includes(channel.kind));
59
+ },
60
+
61
+ async searchRoles(guildId, query) {
62
+ return searchByPrefix(seed[guildId]?.roles, query);
63
+ },
64
+
65
+ async searchMembers(guildId, query) {
66
+ return searchByPrefix(seed[guildId]?.members, query);
67
+ },
68
+ };
69
+ }
@@ -0,0 +1,46 @@
1
+ import { ConflictError } from "@/errors/business-error";
2
+ import type { SettingsStore, StoredSettings } from "@/settings/ports/settings-store";
3
+
4
+ /**
5
+ * {@link SettingsStore} held in process memory: the test twin and the default
6
+ * for a bot that does not need settings to survive a restart. Records are
7
+ * cloned on the way in and out so callers never share state with the store.
8
+ */
9
+ export function createInMemorySettingsStore(): SettingsStore {
10
+ const guilds = new Map<string, Map<string, StoredSettings>>();
11
+
12
+ return {
13
+ async read(guildId, moduleId) {
14
+ const stored = guilds.get(guildId)?.get(moduleId);
15
+ return stored === undefined ? null : structuredClone(stored);
16
+ },
17
+
18
+ async write(record, { expectedRevision }) {
19
+ const current = guilds.get(record.guildId)?.get(record.moduleId);
20
+ // The port speaks `null` for "no record yet", so an absent record compares as `null`.
21
+ const currentRevision = current === undefined ? null : current.revision;
22
+ if (currentRevision !== expectedRevision) {
23
+ throw new ConflictError(
24
+ `Settings of module "${record.moduleId}" in guild ${record.guildId} changed concurrently (expected revision ${expectedRevision}, found ${currentRevision})`,
25
+ );
26
+ }
27
+ let modules = guilds.get(record.guildId);
28
+ if (modules === undefined) {
29
+ modules = new Map<string, StoredSettings>();
30
+ guilds.set(record.guildId, modules);
31
+ }
32
+ modules.set(record.moduleId, structuredClone(record));
33
+ },
34
+
35
+ async delete(guildId, moduleId) {
36
+ const modules = guilds.get(guildId);
37
+ if (modules === undefined) {
38
+ return;
39
+ }
40
+ modules.delete(moduleId);
41
+ if (modules.size === 0) {
42
+ guilds.delete(guildId);
43
+ }
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,40 @@
1
+ import { describeError } from "@/errors/describe-error";
2
+ import type { Logger } from "@/logger";
3
+ import type {
4
+ SettingsChangedEvent,
5
+ SettingsChangedNotifier,
6
+ } from "@/settings/ports/settings-changed-notifier";
7
+
8
+ type Listener = (event: SettingsChangedEvent) => void;
9
+
10
+ /**
11
+ * {@link SettingsChangedNotifier} that delivers events synchronously to
12
+ * listeners in the same process. A throwing listener never stops the others
13
+ * nor reaches the writer that notified: its error is always logged.
14
+ */
15
+ export function createInProcessNotifier(logger: Logger): SettingsChangedNotifier {
16
+ const listeners = new Set<Listener>();
17
+
18
+ return {
19
+ notify(event) {
20
+ // Snapshot, so a listener that unsubscribes during delivery cannot skip another.
21
+ for (const listener of [...listeners]) {
22
+ try {
23
+ listener(event);
24
+ } catch (error) {
25
+ logger.error(
26
+ { guildId: event.guildId, moduleId: event.moduleId, err: describeError(error) },
27
+ "Settings change listener failed",
28
+ );
29
+ }
30
+ }
31
+ },
32
+
33
+ subscribe(listener) {
34
+ listeners.add(listener);
35
+ return () => {
36
+ listeners.delete(listener);
37
+ };
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,72 @@
1
+ export type { Choice } from "@/settings/choice";
2
+ export {
3
+ defineSettings,
4
+ type SettingsDeclaration,
5
+ type SettingsDeclarationInput,
6
+ type SettingsGroup,
7
+ type SettingsMigration,
8
+ type SettingsUi,
9
+ } from "@/settings/define-settings";
10
+ export type { JsonValue, SettingsSchema } from "@/settings/describe";
11
+ export { type FieldBuilder, type FieldBuilders, field } from "@/settings/fields/builders";
12
+ export type {
13
+ AnyField,
14
+ DynamicSuggestions,
15
+ Field,
16
+ FieldChoice,
17
+ FieldKind,
18
+ FieldOptions,
19
+ FieldPresence,
20
+ FieldSpec,
21
+ FieldUi,
22
+ SecretField,
23
+ SecretOptions,
24
+ StaticSuggestions,
25
+ SuggestionContext,
26
+ Suggestions,
27
+ TogglesOptions,
28
+ ToggleValues,
29
+ } from "@/settings/fields/field";
30
+ export {
31
+ createInMemoryGuildDirectory,
32
+ type GuildDirectorySeed,
33
+ type GuildDirectorySeedEntry,
34
+ } from "@/settings/in-memory/in-memory-guild-directory";
35
+ export { createInMemorySettingsStore } from "@/settings/in-memory/in-memory-settings-store";
36
+ export { createInProcessNotifier } from "@/settings/in-memory/in-process-notifier";
37
+ export { SETTINGS_CATALOG } from "@/settings/messages";
38
+ export type { ChannelKind, GuildDirectory } from "@/settings/ports/guild-directory";
39
+ export type {
40
+ SettingsChangedEvent,
41
+ SettingsChangedNotifier,
42
+ } from "@/settings/ports/settings-changed-notifier";
43
+ export type { SettingsStore, StoredSettings } from "@/settings/ports/settings-store";
44
+ export { createSettingsRegistry, type SettingsRegistry } from "@/settings/registry";
45
+ export { SettingsDeclarationError } from "@/settings/settings-declaration-error";
46
+ export {
47
+ createSettingsService,
48
+ type RequestContext,
49
+ type SettingsService,
50
+ type ValidationResult,
51
+ } from "@/settings/settings-service";
52
+ export {
53
+ type SettingsIssue,
54
+ type SettingsIssueCode,
55
+ SettingsValidationError,
56
+ } from "@/settings/settings-validation-error";
57
+ export type { SettingsStatus } from "@/settings/status";
58
+ export {
59
+ KERNEL_SETTINGS_ID,
60
+ type KernelSettings,
61
+ kernelSettings,
62
+ type ModuleEnablement,
63
+ } from "@/settings/system/kernel-settings";
64
+ export { createModuleGate, type ModuleGate } from "@/settings/system/module-gate";
65
+ export { SUGGESTION_TIMEOUT_MS } from "@/settings/timed-search";
66
+ export type {
67
+ FieldValue,
68
+ SecretState,
69
+ SettingsValues,
70
+ SurfaceFieldValue,
71
+ SurfaceValues,
72
+ } from "@/settings/types";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Whether `value` is a plain object keyed by string, as JSON gives back: not
3
+ * `null`, an array, a class instance or a promise.
4
+ */
5
+ export function isPlainObject(value: unknown): value is Readonly<Record<string, unknown>> {
6
+ if (typeof value !== "object" || value === null) {
7
+ return false;
8
+ }
9
+ const prototype: unknown = Object.getPrototypeOf(value);
10
+ return prototype === Object.prototype || prototype === null;
11
+ }