@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.
- package/README.md +318 -1
- package/dist/authz/require-level-guard.d.ts +1 -1
- package/dist/authz/require-level-guard.d.ts.map +1 -1
- package/dist/color.d.ts +42 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +130 -0
- package/dist/color.js.map +1 -0
- package/dist/discord/command/all-required.d.ts +8 -0
- package/dist/discord/command/all-required.d.ts.map +1 -0
- package/dist/discord/command/all-required.js +12 -0
- package/dist/discord/command/all-required.js.map +1 -0
- package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -1
- package/dist/discord/command/autocomplete-dispatcher.js +2 -1
- package/dist/discord/command/autocomplete-dispatcher.js.map +1 -1
- package/dist/discord/command/autocomplete-limits.d.ts +3 -0
- package/dist/discord/command/autocomplete-limits.d.ts.map +1 -0
- package/dist/discord/command/autocomplete-limits.js +6 -0
- package/dist/discord/command/autocomplete-limits.js.map +1 -0
- package/dist/discord/command/autocomplete.d.ts +6 -1
- package/dist/discord/command/autocomplete.d.ts.map +1 -1
- package/dist/discord/command/autocomplete.js +6 -6
- package/dist/discord/command/autocomplete.js.map +1 -1
- package/dist/discord/command/command-responders.d.ts +26 -0
- package/dist/discord/command/command-responders.d.ts.map +1 -0
- package/dist/discord/command/command-responders.js +16 -0
- package/dist/discord/command/command-responders.js.map +1 -0
- package/dist/discord/command/context.d.ts +8 -17
- package/dist/discord/command/context.d.ts.map +1 -1
- package/dist/discord/command/context.js +8 -37
- package/dist/discord/command/context.js.map +1 -1
- package/dist/discord/command/cooldown-guard.d.ts +1 -1
- package/dist/discord/command/cooldown-guard.d.ts.map +1 -1
- package/dist/discord/command/create-command.d.ts +1 -8
- package/dist/discord/command/create-command.d.ts.map +1 -1
- package/dist/discord/command/create-command.js +17 -65
- package/dist/discord/command/create-command.js.map +1 -1
- package/dist/discord/command/create-context-menu-command.d.ts +3 -9
- package/dist/discord/command/create-context-menu-command.d.ts.map +1 -1
- package/dist/discord/command/create-context-menu-command.js +14 -65
- package/dist/discord/command/create-context-menu-command.js.map +1 -1
- package/dist/discord/command/errors.d.ts +1 -1
- package/dist/discord/command/errors.d.ts.map +1 -1
- package/dist/discord/command/errors.js +2 -2
- package/dist/discord/command/errors.js.map +1 -1
- package/dist/discord/command/guard.d.ts +15 -8
- package/dist/discord/command/guard.d.ts.map +1 -1
- package/dist/discord/command/guard.js +4 -4
- package/dist/discord/command/guard.js.map +1 -1
- package/dist/discord/command/options.d.ts +1 -10
- package/dist/discord/command/options.d.ts.map +1 -1
- package/dist/discord/command/options.js +127 -320
- package/dist/discord/command/options.js.map +1 -1
- package/dist/discord/command/passes-guard.d.ts +14 -0
- package/dist/discord/command/passes-guard.d.ts.map +1 -0
- package/dist/discord/command/passes-guard.js +27 -0
- package/dist/discord/command/passes-guard.js.map +1 -0
- package/dist/discord/command/permission-guard.d.ts +1 -1
- package/dist/discord/command/permission-guard.d.ts.map +1 -1
- package/dist/discord/command/render-command-failure.d.ts +23 -0
- package/dist/discord/command/render-command-failure.d.ts.map +1 -0
- package/dist/discord/command/render-command-failure.js +40 -0
- package/dist/discord/command/render-command-failure.js.map +1 -0
- package/dist/discord/command/route-key.d.ts +8 -0
- package/dist/discord/command/route-key.d.ts.map +1 -0
- package/dist/discord/command/route-key.js +13 -0
- package/dist/discord/command/route-key.js.map +1 -0
- package/dist/discord/command/router.d.ts +32 -7
- package/dist/discord/command/router.d.ts.map +1 -1
- package/dist/discord/command/router.js +48 -10
- package/dist/discord/command/router.js.map +1 -1
- package/dist/discord/command/send-embed.d.ts +14 -0
- package/dist/discord/command/send-embed.d.ts.map +1 -0
- package/dist/discord/command/send-embed.js +37 -0
- package/dist/discord/command/send-embed.js.map +1 -0
- package/dist/discord/command/types.d.ts +7 -1
- package/dist/discord/command/types.d.ts.map +1 -1
- package/dist/discord/components/component-access.d.ts +12 -0
- package/dist/discord/components/component-access.d.ts.map +1 -0
- package/dist/discord/components/component-access.js +21 -0
- package/dist/discord/components/component-access.js.map +1 -0
- package/dist/discord/components/component-router.d.ts +37 -15
- package/dist/discord/components/component-router.d.ts.map +1 -1
- package/dist/discord/components/component-router.js +44 -32
- package/dist/discord/components/component-router.js.map +1 -1
- package/dist/discord/components/interactive-message/index.d.ts +3 -1
- package/dist/discord/components/interactive-message/index.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/index.js +5 -3
- package/dist/discord/components/interactive-message/index.js.map +1 -1
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +3 -28
- package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/interactive-message-collector.js +6 -53
- package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -1
- package/dist/discord/components/interactive-message/message-edit-options.d.ts +18 -0
- package/dist/discord/components/interactive-message/message-edit-options.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/message-edit-options.js +40 -0
- package/dist/discord/components/interactive-message/message-edit-options.js.map +1 -0
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +2 -1
- package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -1
- package/dist/discord/components/interactive-message/mount-interactive-message.js +4 -2
- package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -1
- package/dist/discord/components/interactive-message/state-store.d.ts +12 -0
- package/dist/discord/components/interactive-message/state-store.d.ts.map +1 -0
- package/dist/discord/components/interactive-message/state-store.js +14 -0
- package/dist/discord/components/interactive-message/state-store.js.map +1 -0
- package/dist/discord/components/paginator/mount-paginator.js +7 -5
- package/dist/discord/components/paginator/mount-paginator.js.map +1 -1
- package/dist/discord/components/paginator/paginator-view.d.ts +1 -1
- package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -1
- package/dist/discord/components/persistent-paginator.d.ts +1 -1
- package/dist/discord/components/persistent-paginator.d.ts.map +1 -1
- package/dist/discord/components/persistent-paginator.js +6 -6
- package/dist/discord/components/persistent-paginator.js.map +1 -1
- package/dist/discord/components/settings-editor/from-declaration-controls.d.ts +67 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.js +245 -0
- package/dist/discord/components/settings-editor/from-declaration-controls.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-display.d.ts +23 -0
- package/dist/discord/components/settings-editor/from-declaration-display.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-display.js +114 -0
- package/dist/discord/components/settings-editor/from-declaration-display.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.d.ts +33 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.js +178 -0
- package/dist/discord/components/settings-editor/from-declaration-layout.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-search.d.ts +27 -0
- package/dist/discord/components/settings-editor/from-declaration-search.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-search.js +58 -0
- package/dist/discord/components/settings-editor/from-declaration-search.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.d.ts +21 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.js +37 -0
- package/dist/discord/components/settings-editor/from-declaration-stored.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.d.ts +32 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.js +65 -0
- package/dist/discord/components/settings-editor/from-declaration-subject.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-values.d.ts +57 -0
- package/dist/discord/components/settings-editor/from-declaration-values.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration-values.js +187 -0
- package/dist/discord/components/settings-editor/from-declaration-values.js.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration.d.ts +39 -0
- package/dist/discord/components/settings-editor/from-declaration.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/from-declaration.js +132 -0
- package/dist/discord/components/settings-editor/from-declaration.js.map +1 -0
- package/dist/discord/components/settings-editor/index.d.ts +3 -1
- package/dist/discord/components/settings-editor/index.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/index.js +6 -3
- package/dist/discord/components/settings-editor/index.js.map +1 -1
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +16 -2
- package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/mount-settings-editor.js +14 -10
- package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -1
- package/dist/discord/components/settings-editor/navigation.d.ts +20 -0
- package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/navigation.js +22 -0
- package/dist/discord/components/settings-editor/navigation.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +4 -3
- package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-card.view.js +60 -34
- package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +2 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js +48 -72
- package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts +6 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.js +13 -0
- package/dist/discord/components/settings-editor/settings-editor-field-values.js.map +1 -0
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +6 -5
- package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor-fields.js +0 -10
- package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts +3 -2
- package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -1
- package/dist/discord/components/settings-editor/settings-editor.view.js +4 -21
- package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -1
- package/dist/discord/events/event-guild-id.d.ts +10 -0
- package/dist/discord/events/event-guild-id.d.ts.map +1 -0
- package/dist/discord/events/event-guild-id.js +37 -0
- package/dist/discord/events/event-guild-id.js.map +1 -0
- package/dist/discord/events/event-router.d.ts +17 -3
- package/dist/discord/events/event-router.d.ts.map +1 -1
- package/dist/discord/events/event-router.js +33 -9
- package/dist/discord/events/event-router.js.map +1 -1
- package/dist/discord/i18n.d.ts +17 -2
- package/dist/discord/i18n.d.ts.map +1 -1
- package/dist/discord/i18n.js +43 -6
- package/dist/discord/i18n.js.map +1 -1
- package/dist/discord/interaction/interaction-locale.d.ts +14 -0
- package/dist/discord/interaction/interaction-locale.d.ts.map +1 -0
- package/dist/discord/interaction/interaction-locale.js +12 -0
- package/dist/discord/interaction/interaction-locale.js.map +1 -0
- package/dist/discord/interaction/locale-resolver.d.ts +27 -0
- package/dist/discord/interaction/locale-resolver.d.ts.map +1 -0
- package/dist/discord/interaction/locale-resolver.js +3 -0
- package/dist/discord/interaction/locale-resolver.js.map +1 -0
- package/dist/discord/interaction/modal.d.ts.map +1 -1
- package/dist/discord/interaction/modal.js +5 -40
- package/dist/discord/interaction/modal.js.map +1 -1
- package/dist/discord/interaction/reply-locale.d.ts +18 -0
- package/dist/discord/interaction/reply-locale.d.ts.map +1 -0
- package/dist/discord/interaction/reply-locale.js +25 -0
- package/dist/discord/interaction/reply-locale.js.map +1 -0
- package/dist/discord/interaction/select-bounds.d.ts +19 -0
- package/dist/discord/interaction/select-bounds.d.ts.map +1 -0
- package/dist/discord/interaction/select-bounds.js +17 -0
- package/dist/discord/interaction/select-bounds.js.map +1 -0
- package/dist/discord/interaction/select-menu.d.ts.map +1 -1
- package/dist/discord/interaction/select-menu.js +2 -9
- package/dist/discord/interaction/select-menu.js.map +1 -1
- package/dist/discord/placeholder-registry.d.ts +1 -1
- package/dist/discord/placeholder-registry.d.ts.map +1 -1
- package/dist/discord/presenter.d.ts +1 -1
- package/dist/discord/presenter.d.ts.map +1 -1
- package/dist/discord/settings/discord-guild-directory.d.ts +28 -0
- package/dist/discord/settings/discord-guild-directory.d.ts.map +1 -0
- package/dist/discord/settings/discord-guild-directory.js +171 -0
- package/dist/discord/settings/discord-guild-directory.js.map +1 -0
- package/dist/discord/settings/guild-locale-resolver.d.ts +25 -0
- package/dist/discord/settings/guild-locale-resolver.d.ts.map +1 -0
- package/dist/discord/settings/guild-locale-resolver.js +33 -0
- package/dist/discord/settings/guild-locale-resolver.js.map +1 -0
- package/dist/discord/settings/is-module-disabled.d.ts +16 -0
- package/dist/discord/settings/is-module-disabled.d.ts.map +1 -0
- package/dist/discord/settings/is-module-disabled.js +20 -0
- package/dist/discord/settings/is-module-disabled.js.map +1 -0
- package/dist/discord/settings/module-disabled-embed.d.ts +13 -0
- package/dist/discord/settings/module-disabled-embed.d.ts.map +1 -0
- package/dist/discord/settings/module-disabled-embed.js +16 -0
- package/dist/discord/settings/module-disabled-embed.js.map +1 -0
- package/dist/discord/settings/require-configured.d.ts +26 -0
- package/dist/discord/settings/require-configured.d.ts.map +1 -0
- package/dist/discord/settings/require-configured.js +62 -0
- package/dist/discord/settings/require-configured.js.map +1 -0
- package/dist/discord/ui/color-aliases.d.ts +31 -0
- package/dist/discord/ui/color-aliases.d.ts.map +1 -0
- package/dist/discord/ui/color-aliases.js +66 -0
- package/dist/discord/ui/color-aliases.js.map +1 -0
- package/dist/discord/ui/color-input.d.ts +5 -32
- package/dist/discord/ui/color-input.d.ts.map +1 -1
- package/dist/discord/ui/color-input.js +8 -157
- package/dist/discord/ui/color-input.js.map +1 -1
- package/dist/discord/ui/colors.d.ts +0 -2
- package/dist/discord/ui/colors.d.ts.map +1 -1
- package/dist/discord/ui/colors.js +4 -5
- package/dist/discord/ui/colors.js.map +1 -1
- package/dist/discord/ui/embed.d.ts.map +1 -1
- package/dist/discord/ui/embed.js +3 -8
- package/dist/discord/ui/embed.js.map +1 -1
- package/dist/discord/ui/truncate-text.d.ts +11 -0
- package/dist/discord/ui/truncate-text.d.ts.map +1 -0
- package/dist/discord/ui/truncate-text.js +19 -0
- package/dist/discord/ui/truncate-text.js.map +1 -0
- package/dist/errors/error-message.d.ts +9 -0
- package/dist/errors/error-message.d.ts.map +1 -0
- package/dist/errors/error-message.js +14 -0
- package/dist/errors/error-message.js.map +1 -0
- package/dist/i18n/business-message.d.ts +1 -1
- package/dist/i18n/business-message.d.ts.map +1 -1
- package/dist/i18n/catalog.d.ts +9 -0
- package/dist/i18n/catalog.d.ts.map +1 -1
- package/dist/i18n/catalog.js +4 -0
- package/dist/i18n/catalog.js.map +1 -1
- package/dist/i18n/locale.d.ts +2 -0
- package/dist/i18n/locale.d.ts.map +1 -1
- package/dist/i18n/locale.js +3 -1
- package/dist/i18n/locale.js.map +1 -1
- package/dist/i18n/translator.d.ts +2 -0
- package/dist/i18n/translator.d.ts.map +1 -1
- package/dist/i18n/translator.js.map +1 -1
- package/dist/module/module.d.ts +15 -1
- package/dist/module/module.d.ts.map +1 -1
- package/dist/resilience/retry.js +2 -2
- package/dist/resilience/retry.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/scheduler/scheduler.js +2 -4
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/settings/cache.d.ts +29 -0
- package/dist/settings/cache.d.ts.map +1 -0
- package/dist/settings/cache.js +47 -0
- package/dist/settings/cache.js.map +1 -0
- package/dist/settings/choice.d.ts +10 -0
- package/dist/settings/choice.d.ts.map +1 -0
- package/dist/settings/choice.js +3 -0
- package/dist/settings/choice.js.map +1 -0
- package/dist/settings/decode-stored.d.ts +20 -0
- package/dist/settings/decode-stored.d.ts.map +1 -0
- package/dist/settings/decode-stored.js +35 -0
- package/dist/settings/decode-stored.js.map +1 -0
- package/dist/settings/define-settings.d.ts +52 -0
- package/dist/settings/define-settings.d.ts.map +1 -0
- package/dist/settings/define-settings.js +141 -0
- package/dist/settings/define-settings.js.map +1 -0
- package/dist/settings/describe-field.d.ts +19 -0
- package/dist/settings/describe-field.d.ts.map +1 -0
- package/dist/settings/describe-field.js +131 -0
- package/dist/settings/describe-field.js.map +1 -0
- package/dist/settings/describe.d.ts +27 -0
- package/dist/settings/describe.d.ts.map +1 -0
- package/dist/settings/describe.js +62 -0
- package/dist/settings/describe.js.map +1 -0
- package/dist/settings/duration.d.ts +16 -0
- package/dist/settings/duration.d.ts.map +1 -0
- package/dist/settings/duration.js +56 -0
- package/dist/settings/duration.js.map +1 -0
- package/dist/settings/field-order.d.ts +37 -0
- package/dist/settings/field-order.d.ts.map +1 -0
- package/dist/settings/field-order.js +100 -0
- package/dist/settings/field-order.js.map +1 -0
- package/dist/settings/field-search.d.ts +10 -0
- package/dist/settings/field-search.d.ts.map +1 -0
- package/dist/settings/field-search.js +18 -0
- package/dist/settings/field-search.js.map +1 -0
- package/dist/settings/fields/builders.d.ts +41 -0
- package/dist/settings/fields/builders.d.ts.map +1 -0
- package/dist/settings/fields/builders.js +83 -0
- package/dist/settings/fields/builders.js.map +1 -0
- package/dist/settings/fields/field.d.ts +199 -0
- package/dist/settings/fields/field.d.ts.map +1 -0
- package/dist/settings/fields/field.js +33 -0
- package/dist/settings/fields/field.js.map +1 -0
- package/dist/settings/fields/zod-schema.d.ts +67 -0
- package/dist/settings/fields/zod-schema.d.ts.map +1 -0
- package/dist/settings/fields/zod-schema.js +281 -0
- package/dist/settings/fields/zod-schema.js.map +1 -0
- package/dist/settings/in-memory/in-memory-guild-directory.d.ts +24 -0
- package/dist/settings/in-memory/in-memory-guild-directory.d.ts.map +1 -0
- package/dist/settings/in-memory/in-memory-guild-directory.js +42 -0
- package/dist/settings/in-memory/in-memory-guild-directory.js.map +1 -0
- package/dist/settings/in-memory/in-memory-settings-store.d.ts +8 -0
- package/dist/settings/in-memory/in-memory-settings-store.d.ts.map +1 -0
- package/dist/settings/in-memory/in-memory-settings-store.js +43 -0
- package/dist/settings/in-memory/in-memory-settings-store.js.map +1 -0
- package/dist/settings/in-memory/in-process-notifier.d.ts +9 -0
- package/dist/settings/in-memory/in-process-notifier.d.ts.map +1 -0
- package/dist/settings/in-memory/in-process-notifier.js +32 -0
- package/dist/settings/in-memory/in-process-notifier.js.map +1 -0
- package/dist/settings/index.d.ts +22 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +31 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/is-plain-object.d.ts +6 -0
- package/dist/settings/is-plain-object.d.ts.map +1 -0
- package/dist/settings/is-plain-object.js +15 -0
- package/dist/settings/is-plain-object.js.map +1 -0
- package/dist/settings/messages.d.ts +61 -0
- package/dist/settings/messages.d.ts.map +1 -0
- package/dist/settings/messages.js +155 -0
- package/dist/settings/messages.js.map +1 -0
- package/dist/settings/migrate.d.ts +35 -0
- package/dist/settings/migrate.d.ts.map +1 -0
- package/dist/settings/migrate.js +42 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/settings/ports/guild-directory.d.ts +61 -0
- package/dist/settings/ports/guild-directory.d.ts.map +1 -0
- package/dist/settings/ports/guild-directory.js +3 -0
- package/dist/settings/ports/guild-directory.js.map +1 -0
- package/dist/settings/ports/settings-changed-notifier.d.ts +20 -0
- package/dist/settings/ports/settings-changed-notifier.d.ts.map +1 -0
- package/dist/settings/ports/settings-changed-notifier.js +3 -0
- package/dist/settings/ports/settings-changed-notifier.js.map +1 -0
- package/dist/settings/ports/settings-store.d.ts +76 -0
- package/dist/settings/ports/settings-store.d.ts.map +1 -0
- package/dist/settings/ports/settings-store.js +3 -0
- package/dist/settings/ports/settings-store.js.map +1 -0
- package/dist/settings/registry.d.ts +31 -0
- package/dist/settings/registry.d.ts.map +1 -0
- package/dist/settings/registry.js +102 -0
- package/dist/settings/registry.js.map +1 -0
- package/dist/settings/settings-declaration-error.d.ts +13 -0
- package/dist/settings/settings-declaration-error.d.ts.map +1 -0
- package/dist/settings/settings-declaration-error.js +22 -0
- package/dist/settings/settings-declaration-error.js.map +1 -0
- package/dist/settings/settings-service.d.ts +148 -0
- package/dist/settings/settings-service.d.ts.map +1 -0
- package/dist/settings/settings-service.js +279 -0
- package/dist/settings/settings-service.js.map +1 -0
- package/dist/settings/settings-validation-error.d.ts +23 -0
- package/dist/settings/settings-validation-error.d.ts.map +1 -0
- package/dist/settings/settings-validation-error.js +17 -0
- package/dist/settings/settings-validation-error.js.map +1 -0
- package/dist/settings/status.d.ts +16 -0
- package/dist/settings/status.d.ts.map +1 -0
- package/dist/settings/status.js +18 -0
- package/dist/settings/status.js.map +1 -0
- package/dist/settings/suggest.d.ts +77 -0
- package/dist/settings/suggest.d.ts.map +1 -0
- package/dist/settings/suggest.js +192 -0
- package/dist/settings/suggest.js.map +1 -0
- package/dist/settings/system/kernel-settings.d.ts +43 -0
- package/dist/settings/system/kernel-settings.d.ts.map +1 -0
- package/dist/settings/system/kernel-settings.js +53 -0
- package/dist/settings/system/kernel-settings.js.map +1 -0
- package/dist/settings/system/module-gate.d.ts +31 -0
- package/dist/settings/system/module-gate.d.ts.map +1 -0
- package/dist/settings/system/module-gate.js +34 -0
- package/dist/settings/system/module-gate.js.map +1 -0
- package/dist/settings/testing/index.d.ts +2 -0
- package/dist/settings/testing/index.d.ts.map +1 -0
- package/dist/settings/testing/index.js +6 -0
- package/dist/settings/testing/index.js.map +1 -0
- package/dist/settings/testing/settings-store-contract.d.ts +29 -0
- package/dist/settings/testing/settings-store-contract.d.ts.map +1 -0
- package/dist/settings/testing/settings-store-contract.js +192 -0
- package/dist/settings/testing/settings-store-contract.js.map +1 -0
- package/dist/settings/timed-search.d.ts +26 -0
- package/dist/settings/timed-search.d.ts.map +1 -0
- package/dist/settings/timed-search.js +48 -0
- package/dist/settings/timed-search.js.map +1 -0
- package/dist/settings/types.d.ts +37 -0
- package/dist/settings/types.d.ts.map +1 -0
- package/dist/settings/types.js +3 -0
- package/dist/settings/types.js.map +1 -0
- package/dist/settings/validate.d.ts +55 -0
- package/dist/settings/validate.d.ts.map +1 -0
- package/dist/settings/validate.js +196 -0
- package/dist/settings/validate.js.map +1 -0
- package/package.json +463 -88
- package/src/authz/require-level-guard.ts +1 -1
- package/src/color.ts +134 -0
- package/src/discord/command/all-required.ts +12 -0
- package/src/discord/command/autocomplete-dispatcher.ts +2 -1
- package/src/discord/command/autocomplete-limits.ts +2 -0
- package/src/discord/command/autocomplete.ts +8 -7
- package/src/discord/command/command-responders.ts +39 -0
- package/src/discord/command/context.ts +14 -54
- package/src/discord/command/cooldown-guard.ts +1 -1
- package/src/discord/command/create-command.ts +19 -94
- package/src/discord/command/create-context-menu-command.ts +19 -97
- package/src/discord/command/errors.ts +1 -1
- package/src/discord/command/guard.ts +24 -12
- package/src/discord/command/options.ts +202 -412
- package/src/discord/command/passes-guard.ts +37 -0
- package/src/discord/command/permission-guard.ts +1 -1
- package/src/discord/command/render-command-failure.ts +71 -0
- package/src/discord/command/route-key.ts +9 -0
- package/src/discord/command/router.ts +73 -12
- package/src/discord/command/send-embed.ts +42 -0
- package/src/discord/command/types.ts +7 -1
- package/src/discord/components/component-access.ts +20 -0
- package/src/discord/components/component-router.ts +73 -36
- package/src/discord/components/interactive-message/index.ts +5 -3
- package/src/discord/components/interactive-message/interactive-message-collector.ts +4 -68
- package/src/discord/components/interactive-message/message-edit-options.ts +47 -0
- package/src/discord/components/interactive-message/mount-interactive-message.ts +5 -3
- package/src/discord/components/interactive-message/state-store.ts +20 -0
- package/src/discord/components/paginator/mount-paginator.ts +3 -3
- package/src/discord/components/paginator/paginator-view.ts +1 -1
- package/src/discord/components/persistent-paginator.ts +8 -9
- package/src/discord/components/settings-editor/from-declaration-controls.ts +371 -0
- package/src/discord/components/settings-editor/from-declaration-display.ts +155 -0
- package/src/discord/components/settings-editor/from-declaration-layout.ts +247 -0
- package/src/discord/components/settings-editor/from-declaration-search.ts +77 -0
- package/src/discord/components/settings-editor/from-declaration-stored.ts +39 -0
- package/src/discord/components/settings-editor/from-declaration-subject.ts +110 -0
- package/src/discord/components/settings-editor/from-declaration-values.ts +240 -0
- package/src/discord/components/settings-editor/from-declaration.ts +218 -0
- package/src/discord/components/settings-editor/index.ts +5 -2
- package/src/discord/components/settings-editor/mount-settings-editor.ts +28 -10
- package/src/discord/components/settings-editor/navigation.ts +41 -0
- package/src/discord/components/settings-editor/settings-editor-card.view.ts +79 -36
- package/src/discord/components/settings-editor/settings-editor-field-modal.ts +76 -68
- package/src/discord/components/settings-editor/settings-editor-field-values.ts +11 -0
- package/src/discord/components/settings-editor/settings-editor-fields.ts +6 -11
- package/src/discord/components/settings-editor/settings-editor.view.ts +11 -24
- package/src/discord/events/event-guild-id.ts +35 -0
- package/src/discord/events/event-router.ts +46 -13
- package/src/discord/i18n.ts +47 -8
- package/src/discord/interaction/interaction-locale.ts +19 -0
- package/src/discord/interaction/locale-resolver.ts +28 -0
- package/src/discord/interaction/modal.ts +5 -40
- package/src/discord/interaction/reply-locale.ts +35 -0
- package/src/discord/interaction/select-bounds.ts +33 -0
- package/src/discord/interaction/select-menu.ts +2 -9
- package/src/discord/placeholder-registry.ts +1 -1
- package/src/discord/presenter.ts +1 -1
- package/src/discord/settings/discord-guild-directory.ts +214 -0
- package/src/discord/settings/guild-locale-resolver.ts +38 -0
- package/src/discord/settings/is-module-disabled.ts +22 -0
- package/src/discord/settings/module-disabled-embed.ts +19 -0
- package/src/discord/settings/require-configured.ts +68 -0
- package/src/discord/ui/color-aliases.ts +70 -0
- package/src/discord/ui/color-input.ts +8 -173
- package/src/discord/ui/colors.ts +2 -3
- package/src/discord/ui/embed.ts +3 -9
- package/src/discord/ui/truncate-text.ts +15 -0
- package/src/errors/error-message.ts +10 -0
- package/src/i18n/business-message.ts +1 -1
- package/src/i18n/catalog.ts +14 -0
- package/src/i18n/locale.ts +3 -0
- package/src/i18n/translator.ts +3 -0
- package/src/module/module.ts +15 -1
- package/src/resilience/retry.ts +1 -1
- package/src/scheduler/scheduler.ts +2 -5
- package/src/settings/cache.ts +70 -0
- package/src/settings/choice.ts +9 -0
- package/src/settings/decode-stored.ts +40 -0
- package/src/settings/define-settings.ts +214 -0
- package/src/settings/describe-field.ts +155 -0
- package/src/settings/describe.ts +90 -0
- package/src/settings/duration.ts +54 -0
- package/src/settings/field-order.ts +128 -0
- package/src/settings/field-search.ts +16 -0
- package/src/settings/fields/builders.ts +161 -0
- package/src/settings/fields/field.ts +205 -0
- package/src/settings/fields/zod-schema.ts +300 -0
- package/src/settings/in-memory/in-memory-guild-directory.ts +69 -0
- package/src/settings/in-memory/in-memory-settings-store.ts +46 -0
- package/src/settings/in-memory/in-process-notifier.ts +40 -0
- package/src/settings/index.ts +72 -0
- package/src/settings/is-plain-object.ts +11 -0
- package/src/settings/messages.ts +161 -0
- package/src/settings/migrate.ts +58 -0
- package/src/settings/ports/guild-directory.ts +48 -0
- package/src/settings/ports/settings-changed-notifier.ts +20 -0
- package/src/settings/ports/settings-store.ts +74 -0
- package/src/settings/registry.ts +137 -0
- package/src/settings/settings-declaration-error.ts +16 -0
- package/src/settings/settings-service.ts +535 -0
- package/src/settings/settings-validation-error.ts +41 -0
- package/src/settings/status.ts +25 -0
- package/src/settings/suggest.ts +267 -0
- package/src/settings/system/kernel-settings.ts +74 -0
- package/src/settings/system/module-gate.ts +49 -0
- package/src/settings/testing/index.ts +7 -0
- package/src/settings/testing/settings-store-contract.ts +278 -0
- package/src/settings/timed-search.ts +56 -0
- package/src/settings/types.ts +42 -0
- package/src/settings/validate.ts +298 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { errorMessage } from "@/errors/error-message";
|
|
2
|
+
import type { Logger } from "@/logger";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How long a suggestion search may run (FR-024): past it the search is
|
|
6
|
+
* abandoned, so an autocomplete still answers within Discord's 3 s window.
|
|
7
|
+
*/
|
|
8
|
+
export const SUGGESTION_TIMEOUT_MS = 2_500;
|
|
9
|
+
|
|
10
|
+
/** Marks the timer winning the race, whatever the search resolves to. */
|
|
11
|
+
const TIMED_OUT: unique symbol = Symbol("timed out");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Run a suggestion search within {@link SUGGESTION_TIMEOUT_MS}. A search that
|
|
15
|
+
* runs longer or fails yields `fallback` and a `warn` log naming `at`; it never
|
|
16
|
+
* throws. The timer is cleared as soon as the race settles, so no timer
|
|
17
|
+
* outlives the call. It runs on the global timers, which a test drives with
|
|
18
|
+
* fake timers.
|
|
19
|
+
*
|
|
20
|
+
* @param params.search - the search; may throw or reject.
|
|
21
|
+
* @param params.fallback - what a slow or failed search yields.
|
|
22
|
+
* @param params.logger - where a slow or failed search is reported.
|
|
23
|
+
* @param params.at - what the log names: module, field, guild.
|
|
24
|
+
* @returns the search's result, or `fallback`.
|
|
25
|
+
*/
|
|
26
|
+
export async function timedSearch<T>(params: {
|
|
27
|
+
search: () => Promise<T>;
|
|
28
|
+
fallback: T;
|
|
29
|
+
logger: Logger;
|
|
30
|
+
at: Readonly<Record<string, unknown>>;
|
|
31
|
+
}): Promise<T> {
|
|
32
|
+
const { search, fallback, logger, at } = params;
|
|
33
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
34
|
+
const timeout = new Promise<typeof TIMED_OUT>((resolve) => {
|
|
35
|
+
timer = setTimeout(() => resolve(TIMED_OUT), SUGGESTION_TIMEOUT_MS);
|
|
36
|
+
});
|
|
37
|
+
try {
|
|
38
|
+
const result = await Promise.race([(async () => search())(), timeout]);
|
|
39
|
+
if (result === TIMED_OUT) {
|
|
40
|
+
logger.warn(
|
|
41
|
+
{ ...at, timeoutMs: SUGGESTION_TIMEOUT_MS },
|
|
42
|
+
"Settings suggestion search timed out; offering nothing",
|
|
43
|
+
);
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.warn(
|
|
49
|
+
{ ...at, err: errorMessage(error) },
|
|
50
|
+
"Settings suggestion search failed; offering nothing",
|
|
51
|
+
);
|
|
52
|
+
return fallback;
|
|
53
|
+
} finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Field, FieldPresence } from "@/settings/fields/field";
|
|
2
|
+
|
|
3
|
+
/** The fields of a declaration, by key. */
|
|
4
|
+
type FieldsOf<D> = D extends { readonly fields: infer F } ? F : never;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The value type module logic reads for one field: `T` when the field has a
|
|
8
|
+
* default (a toggles field always has one), `T | undefined` otherwise, even
|
|
9
|
+
* when required: a guild may not have configured it yet.
|
|
10
|
+
*/
|
|
11
|
+
export type FieldValue<F> =
|
|
12
|
+
F extends Field<infer T, "defined">
|
|
13
|
+
? T
|
|
14
|
+
: F extends Field<infer T, FieldPresence>
|
|
15
|
+
? T | undefined
|
|
16
|
+
: never;
|
|
17
|
+
|
|
18
|
+
/** What a surface shows for a secret: whether it is set, never its value. */
|
|
19
|
+
export interface SecretState {
|
|
20
|
+
readonly isSet: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The value type a surface reads for one field: secrets become {@link SecretState}. */
|
|
24
|
+
export type SurfaceFieldValue<F> = F extends { readonly spec: { readonly kind: "secret" } }
|
|
25
|
+
? SecretState
|
|
26
|
+
: FieldValue<F>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The values module logic reads from a declaration, typed from it: one entry
|
|
30
|
+
* per declared field, secrets included.
|
|
31
|
+
*/
|
|
32
|
+
export type SettingsValues<D> = {
|
|
33
|
+
readonly [K in keyof FieldsOf<D>]: FieldValue<FieldsOf<D>[K]>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The values a configuration surface reads from a declaration: like
|
|
38
|
+
* {@link SettingsValues}, with each secret replaced by {@link SecretState}.
|
|
39
|
+
*/
|
|
40
|
+
export type SurfaceValues<D> = {
|
|
41
|
+
readonly [K in keyof FieldsOf<D>]: SurfaceFieldValue<FieldsOf<D>[K]>;
|
|
42
|
+
};
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { ValidationError } from "@/errors/business-error";
|
|
2
|
+
import type { SettingsDeclaration } from "@/settings/define-settings";
|
|
3
|
+
import { fieldSearch } from "@/settings/field-search";
|
|
4
|
+
import { type AnyField, type FieldSpec, unhandledFieldKind } from "@/settings/fields/field";
|
|
5
|
+
import { type FieldValueIssue, parseFieldValue } from "@/settings/fields/zod-schema";
|
|
6
|
+
import { isPlainObject } from "@/settings/is-plain-object";
|
|
7
|
+
import { SETTINGS_ISSUE_MESSAGES } from "@/settings/messages";
|
|
8
|
+
import type { GuildDirectory } from "@/settings/ports/guild-directory";
|
|
9
|
+
import type { SettingsIssue, SettingsIssueCode } from "@/settings/settings-validation-error";
|
|
10
|
+
import { dynamicLabel, type SuggestionPorts } from "@/settings/suggest";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Outcome of {@link validateSettings}: the submitted fields in their stored
|
|
14
|
+
* form, or every issue found. A field submitted as `null` or `undefined` is
|
|
15
|
+
* unset: it appears in `values` with the value `undefined`.
|
|
16
|
+
*/
|
|
17
|
+
export type SettingsValidation =
|
|
18
|
+
| { readonly ok: true; readonly values: Readonly<Record<string, unknown>> }
|
|
19
|
+
| { readonly ok: false; readonly issues: readonly SettingsIssue[] };
|
|
20
|
+
|
|
21
|
+
interface EntryOutcome {
|
|
22
|
+
readonly value: unknown;
|
|
23
|
+
readonly issues: readonly SettingsIssue[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A field value checked on its own (stage 1), before the guild and strict checks. */
|
|
27
|
+
type ParsedEntry =
|
|
28
|
+
| {
|
|
29
|
+
readonly ok: true;
|
|
30
|
+
readonly key: string;
|
|
31
|
+
readonly declared: AnyField;
|
|
32
|
+
readonly value: unknown;
|
|
33
|
+
}
|
|
34
|
+
| { readonly ok: false; readonly outcome: EntryOutcome };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Who submits and what stage 2 reads: the guild's directory, the ports a
|
|
38
|
+
* strict field's search runs with, the requester, and the guild's current
|
|
39
|
+
* values, read only when a strict search needs them.
|
|
40
|
+
*/
|
|
41
|
+
export interface ValidationScope {
|
|
42
|
+
readonly ports: SuggestionPorts;
|
|
43
|
+
readonly guildId: string;
|
|
44
|
+
readonly userId: string;
|
|
45
|
+
readonly locale: string;
|
|
46
|
+
/** The guild's current values as a surface reads them (secrets as whether they are set). */
|
|
47
|
+
readonly currentValues: () => Promise<Readonly<Record<string, unknown>>>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Validate a settings submission against its declaration, in two stages.
|
|
52
|
+
* Stage 1 checks each submitted field on its own (kind, bounds, lengths, list
|
|
53
|
+
* size, duplicates, enum membership, required fields being unset). Stage 2 runs
|
|
54
|
+
* only on the fields that passed stage 1: it asks the guild directory whether
|
|
55
|
+
* each referenced channel, role and member exists, and whether a channel is of
|
|
56
|
+
* an allowed type; and it asks a strict field's search whether it knows the
|
|
57
|
+
* value (FR-023): `label(value)` must name it, or, without `label`, a result
|
|
58
|
+
* of `resolve(String(value))` must hold exactly that value. A strict search
|
|
59
|
+
* that runs past its budget or fails knows nothing (logged): the value is
|
|
60
|
+
* refused as `unknownChoice` rather than stored unchecked. Keys the
|
|
61
|
+
* declaration lacks are rejected.
|
|
62
|
+
*
|
|
63
|
+
* @param params.declaration - the module's settings declaration.
|
|
64
|
+
* @param params.patch - the submitted fields, keyed by field key; any value.
|
|
65
|
+
* @param params.scope - the guild, the requester and the ports stage 2 reads.
|
|
66
|
+
* @returns the stored form of every submitted field, or every issue found, in
|
|
67
|
+
* submission order, with catalog keys from {@link SETTINGS_ISSUE_MESSAGES}.
|
|
68
|
+
* @throws ValidationError when the patch is not a plain object (a malformed
|
|
69
|
+
* request, not an invalid value).
|
|
70
|
+
*/
|
|
71
|
+
export async function validateSettings(params: {
|
|
72
|
+
declaration: SettingsDeclaration;
|
|
73
|
+
patch: unknown;
|
|
74
|
+
scope: ValidationScope;
|
|
75
|
+
}): Promise<SettingsValidation> {
|
|
76
|
+
const { declaration, patch, scope } = params;
|
|
77
|
+
if (!isPlainObject(patch)) {
|
|
78
|
+
throw new ValidationError(
|
|
79
|
+
`Settings patch for module "${declaration.id}" must be an object keyed by field`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const entries = Object.entries(patch);
|
|
83
|
+
const parsed = entries.map(([key, value]) => parseEntry({ declaration, key, value }));
|
|
84
|
+
// A secret stays out of what a search sees, as it stays out of every surface read.
|
|
85
|
+
const submitted = Object.fromEntries(
|
|
86
|
+
parsed.flatMap((entry) =>
|
|
87
|
+
entry.ok && entry.declared.spec.kind !== "secret" ? [[entry.key, entry.value]] : [],
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
const values = lazyValues({ scope, submitted });
|
|
91
|
+
const outcomes = await Promise.all(
|
|
92
|
+
parsed.map((entry) =>
|
|
93
|
+
entry.ok ? checkEntry({ scope, declaration, entry, values }) : entry.outcome,
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
const issues = outcomes.flatMap((entry) => entry.issues);
|
|
97
|
+
if (issues.length > 0) {
|
|
98
|
+
return { ok: false, issues };
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
values: Object.fromEntries(entries.map(([key], index) => [key, outcomes[index]?.value])),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The values a strict search sees as "currently entered": the guild's current
|
|
108
|
+
* values with the submitted ones over them. Read once, and only when a strict
|
|
109
|
+
* search asks.
|
|
110
|
+
*/
|
|
111
|
+
function lazyValues(params: {
|
|
112
|
+
scope: ValidationScope;
|
|
113
|
+
submitted: Readonly<Record<string, unknown>>;
|
|
114
|
+
}): () => Promise<Readonly<Record<string, unknown>>> {
|
|
115
|
+
let values: Promise<Readonly<Record<string, unknown>>> | undefined;
|
|
116
|
+
return () => {
|
|
117
|
+
values ??= params.scope
|
|
118
|
+
.currentValues()
|
|
119
|
+
.then((current) => ({ ...current, ...params.submitted }));
|
|
120
|
+
return values;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Stage 1: one submitted field checked on its own. */
|
|
125
|
+
function parseEntry(params: {
|
|
126
|
+
declaration: SettingsDeclaration;
|
|
127
|
+
key: string;
|
|
128
|
+
value: unknown;
|
|
129
|
+
}): ParsedEntry {
|
|
130
|
+
const { declaration, key, value } = params;
|
|
131
|
+
const declared = Object.hasOwn(declaration.fields, key) ? declaration.fields[key] : undefined;
|
|
132
|
+
if (declared === undefined) {
|
|
133
|
+
return failed({ key, issues: [{ path: [], code: "unknownField", params: {} }] });
|
|
134
|
+
}
|
|
135
|
+
if (value === null || value === undefined) {
|
|
136
|
+
if (declared.required && declared.default === undefined) {
|
|
137
|
+
return failed({ key, issues: [{ path: [], code: "required", params: {} }] });
|
|
138
|
+
}
|
|
139
|
+
return { ok: true, key, declared, value: undefined };
|
|
140
|
+
}
|
|
141
|
+
const parsed = parseFieldValue({ field: declared, value });
|
|
142
|
+
if (!parsed.ok) {
|
|
143
|
+
return failed({ key, issues: parsed.issues });
|
|
144
|
+
}
|
|
145
|
+
return { ok: true, key, declared, value: parsed.value };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function failed(params: { key: string; issues: readonly FieldValueIssue[] }): ParsedEntry {
|
|
149
|
+
return { ok: false, outcome: outcome(params) };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** What stage 2 checks one field that passed stage 1 with. */
|
|
153
|
+
interface EntryCheck {
|
|
154
|
+
readonly scope: ValidationScope;
|
|
155
|
+
readonly declaration: SettingsDeclaration;
|
|
156
|
+
readonly entry: Extract<ParsedEntry, { ok: true }>;
|
|
157
|
+
/** The values a strict search sees, read on first use. */
|
|
158
|
+
readonly values: () => Promise<Readonly<Record<string, unknown>>>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** One value of that field to check: the field's own, or one list item and the item's spec. */
|
|
162
|
+
interface ValueCheck extends EntryCheck {
|
|
163
|
+
readonly spec: FieldSpec;
|
|
164
|
+
readonly value: unknown;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Stage 2 on a field that passed stage 1; an unset field has nothing to check. */
|
|
168
|
+
async function checkEntry(params: EntryCheck): Promise<EntryOutcome> {
|
|
169
|
+
const { entry } = params;
|
|
170
|
+
if (entry.value === undefined) {
|
|
171
|
+
return { value: undefined, issues: [] };
|
|
172
|
+
}
|
|
173
|
+
const issues = await checkReferences({
|
|
174
|
+
...params,
|
|
175
|
+
spec: entry.declared.spec,
|
|
176
|
+
value: entry.value,
|
|
177
|
+
});
|
|
178
|
+
return outcome({ key: entry.key, issues, value: entry.value });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function outcome(params: {
|
|
182
|
+
key: string;
|
|
183
|
+
issues: readonly FieldValueIssue[];
|
|
184
|
+
value?: unknown;
|
|
185
|
+
}): EntryOutcome {
|
|
186
|
+
const { key, issues, value } = params;
|
|
187
|
+
return { value, issues: issues.map((issue) => toSettingsIssue(key, issue)) };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Stage 2 on a value, item by item for a list. */
|
|
191
|
+
async function checkReferences(params: ValueCheck): Promise<FieldValueIssue[]> {
|
|
192
|
+
const { spec, value } = params;
|
|
193
|
+
if (spec.kind === "list" && Array.isArray(value)) {
|
|
194
|
+
const codes = await Promise.all(
|
|
195
|
+
value.map((item: unknown) => checkValue({ ...params, spec: spec.item.spec, value: item })),
|
|
196
|
+
);
|
|
197
|
+
return codes.flatMap((code, index) =>
|
|
198
|
+
code === undefined ? [] : [{ path: [index], code, params: {} }],
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const code = await checkValue(params);
|
|
202
|
+
return code === undefined ? [] : [{ path: [], code, params: {} }];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** The guild check, or the strict check, one value needs; `undefined` when it passes. */
|
|
206
|
+
async function checkValue(params: ValueCheck): Promise<SettingsIssueCode | undefined> {
|
|
207
|
+
const { scope, declaration, entry, values, spec, value } = params;
|
|
208
|
+
const search = fieldSearch(spec);
|
|
209
|
+
if (search?.strict === true) {
|
|
210
|
+
const label = await dynamicLabel({
|
|
211
|
+
scope: {
|
|
212
|
+
...scope.ports,
|
|
213
|
+
moduleId: declaration.id,
|
|
214
|
+
key: entry.key,
|
|
215
|
+
ctx: {
|
|
216
|
+
guildId: scope.guildId,
|
|
217
|
+
userId: scope.userId,
|
|
218
|
+
locale: scope.locale,
|
|
219
|
+
values: withoutKey(await values(), entry.key),
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
suggest: search,
|
|
223
|
+
value,
|
|
224
|
+
});
|
|
225
|
+
return label === undefined ? "unknownChoice" : undefined;
|
|
226
|
+
}
|
|
227
|
+
return checkEntity({ guilds: scope.ports.guilds, guildId: scope.guildId, spec, value });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** The other values: a search is told what else is entered, not its own field's value. */
|
|
231
|
+
function withoutKey(
|
|
232
|
+
values: Readonly<Record<string, unknown>>,
|
|
233
|
+
key: string,
|
|
234
|
+
): Readonly<Record<string, unknown>> {
|
|
235
|
+
const { [key]: _own, ...others } = values;
|
|
236
|
+
return others;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Whether the channel, role or member a value references is usable on the
|
|
241
|
+
* guild. Stage 1 makes such a value a snowflake string; any other value
|
|
242
|
+
* references no guild entity and needs no check.
|
|
243
|
+
*/
|
|
244
|
+
async function checkEntity(params: {
|
|
245
|
+
guilds: GuildDirectory;
|
|
246
|
+
guildId: string;
|
|
247
|
+
spec: FieldSpec;
|
|
248
|
+
value: unknown;
|
|
249
|
+
}): Promise<SettingsIssueCode | undefined> {
|
|
250
|
+
const { guilds, guildId, spec, value } = params;
|
|
251
|
+
if (typeof value !== "string") {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
switch (spec.kind) {
|
|
255
|
+
case "channel": {
|
|
256
|
+
const channel = await guilds.channel(guildId, value);
|
|
257
|
+
if (channel === null) {
|
|
258
|
+
return "notFound";
|
|
259
|
+
}
|
|
260
|
+
if (spec.types !== undefined && !spec.types.includes(channel.kind)) {
|
|
261
|
+
return "channelType";
|
|
262
|
+
}
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
case "role":
|
|
266
|
+
return (await guilds.role(guildId, value)) === null ? "notFound" : undefined;
|
|
267
|
+
case "user":
|
|
268
|
+
return (await guilds.member(guildId, value)) === null ? "notFound" : undefined;
|
|
269
|
+
case "color":
|
|
270
|
+
case "duration":
|
|
271
|
+
case "enum":
|
|
272
|
+
case "integer":
|
|
273
|
+
case "number":
|
|
274
|
+
case "text":
|
|
275
|
+
case "boolean":
|
|
276
|
+
case "secret":
|
|
277
|
+
case "list":
|
|
278
|
+
case "toggles":
|
|
279
|
+
return undefined;
|
|
280
|
+
default:
|
|
281
|
+
return unhandledFieldKind(spec);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** `[i]` for a list index, `.name` for a toggle key. */
|
|
286
|
+
function pathSuffix(segment: number | string): string {
|
|
287
|
+
return typeof segment === "number" ? `[${segment}]` : `.${segment}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function toSettingsIssue(key: string, issue: FieldValueIssue): SettingsIssue {
|
|
291
|
+
const translationKey = SETTINGS_ISSUE_MESSAGES[issue.code];
|
|
292
|
+
const field = `${key}${issue.path.map(pathSuffix).join("")}`;
|
|
293
|
+
const translation =
|
|
294
|
+
Object.keys(issue.params).length === 0
|
|
295
|
+
? { key: translationKey }
|
|
296
|
+
: { key: translationKey, params: issue.params };
|
|
297
|
+
return { field, code: issue.code, translation };
|
|
298
|
+
}
|