@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,240 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ControlShape,
|
|
3
|
+
DeclarationControl,
|
|
4
|
+
} from "@/discord/components/settings-editor/from-declaration-controls";
|
|
5
|
+
import {
|
|
6
|
+
isToggledOn,
|
|
7
|
+
secretStateKey,
|
|
8
|
+
storedIds,
|
|
9
|
+
} from "@/discord/components/settings-editor/from-declaration-stored";
|
|
10
|
+
import type { DeclarationSubject } from "@/discord/components/settings-editor/from-declaration-subject";
|
|
11
|
+
import type { SettingsEditorGroupMemberSubmission } from "@/discord/components/settings-editor/settings-editor-field-modal";
|
|
12
|
+
import {
|
|
13
|
+
pickedFieldValue,
|
|
14
|
+
textFieldValue,
|
|
15
|
+
} from "@/discord/components/settings-editor/settings-editor-field-values";
|
|
16
|
+
import {
|
|
17
|
+
isPickerField,
|
|
18
|
+
type SettingsEditorFieldValue,
|
|
19
|
+
} from "@/discord/components/settings-editor/settings-editor-fields";
|
|
20
|
+
import type { TranslateKey } from "@/i18n/translator";
|
|
21
|
+
import { formatDuration } from "@/settings/duration";
|
|
22
|
+
import { SETTINGS_MESSAGES } from "@/settings/messages";
|
|
23
|
+
|
|
24
|
+
/** A decimal number as typed: optional sign, digits with an optional fraction, optional exponent. */
|
|
25
|
+
const DECIMAL_PATTERN = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
26
|
+
|
|
27
|
+
/** Separates the items of a list typed one per line. */
|
|
28
|
+
const LINE_BREAK = /\r?\n/;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* What the modal of a control opens on: the stored value as text for a typed
|
|
32
|
+
* control, whether a secret is set (never its value), the picked ids for a
|
|
33
|
+
* picker, minus the entities the guild no longer has, which the text then
|
|
34
|
+
* reports as unavailable.
|
|
35
|
+
*
|
|
36
|
+
* @param params.control - the control being opened.
|
|
37
|
+
* @param params.subject - the screen's current subject.
|
|
38
|
+
* @param params.translate - translates the texts shown in place of a value.
|
|
39
|
+
* @returns the control's prefill.
|
|
40
|
+
*/
|
|
41
|
+
export function controlValue(params: {
|
|
42
|
+
control: DeclarationControl;
|
|
43
|
+
subject: DeclarationSubject;
|
|
44
|
+
translate: TranslateKey;
|
|
45
|
+
}): SettingsEditorFieldValue {
|
|
46
|
+
const { control, subject, translate } = params;
|
|
47
|
+
const value = subject.values[control.fieldKey];
|
|
48
|
+
const { shape } = control;
|
|
49
|
+
switch (shape.kind) {
|
|
50
|
+
case "text":
|
|
51
|
+
return textFieldValue(typeof value === "string" ? value : null);
|
|
52
|
+
case "number":
|
|
53
|
+
return textFieldValue(typeof value === "number" ? String(value) : null);
|
|
54
|
+
case "duration":
|
|
55
|
+
return textFieldValue(typeof value === "number" ? formatDuration(value) : null);
|
|
56
|
+
case "secret":
|
|
57
|
+
return textFieldValue(translate(secretStateKey(value)));
|
|
58
|
+
case "lines":
|
|
59
|
+
return textFieldValue(Array.isArray(value) && value.length > 0 ? value.join("\n") : null);
|
|
60
|
+
case "boolean":
|
|
61
|
+
return pickedFieldValue(typeof value === "boolean" ? [String(value)] : []);
|
|
62
|
+
case "single":
|
|
63
|
+
case "many":
|
|
64
|
+
return pickedValue({ control, subject, translate });
|
|
65
|
+
case "toggles":
|
|
66
|
+
return pickedFieldValue(shape.keys.filter((key) => isToggledOn({ value, key })));
|
|
67
|
+
case "search":
|
|
68
|
+
return pickedFieldValue(
|
|
69
|
+
typeof value === "string" || typeof value === "number" ? [String(value)] : [],
|
|
70
|
+
);
|
|
71
|
+
case "other":
|
|
72
|
+
// Opens empty: the value it would repeat is already picked in the select beside it.
|
|
73
|
+
return textFieldValue(null);
|
|
74
|
+
default:
|
|
75
|
+
return shape satisfies never;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The picked ids the guild still has, with a notice when any stored one is gone. */
|
|
80
|
+
function pickedValue(params: {
|
|
81
|
+
control: DeclarationControl;
|
|
82
|
+
subject: DeclarationSubject;
|
|
83
|
+
translate: TranslateKey;
|
|
84
|
+
}): SettingsEditorFieldValue {
|
|
85
|
+
const { control, subject, translate } = params;
|
|
86
|
+
const stored = storedIds(subject.values[control.fieldKey]);
|
|
87
|
+
const missing = subject.unavailable[control.fieldKey] ?? [];
|
|
88
|
+
const picked = pickedFieldValue(stored.filter((id) => !missing.includes(id)));
|
|
89
|
+
if (missing.length === 0) {
|
|
90
|
+
return picked;
|
|
91
|
+
}
|
|
92
|
+
return { ...picked, text: translate(SETTINGS_MESSAGES.valueUnavailable) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether a submission leaves a control as its modal opened: the same picked
|
|
97
|
+
* ids in the same order for a picker (whatever notice its text carries), the
|
|
98
|
+
* same text for a typed control (an empty box and no value alike).
|
|
99
|
+
*
|
|
100
|
+
* @param params.control - the control submitted.
|
|
101
|
+
* @param params.submission - what it came back with.
|
|
102
|
+
* @param params.current - what its modal opened on.
|
|
103
|
+
* @returns `true` when nothing was changed.
|
|
104
|
+
*/
|
|
105
|
+
export function isUnchanged(params: {
|
|
106
|
+
control: DeclarationControl;
|
|
107
|
+
submission: SettingsEditorGroupMemberSubmission;
|
|
108
|
+
current: SettingsEditorFieldValue;
|
|
109
|
+
}): boolean {
|
|
110
|
+
const { control, submission, current } = params;
|
|
111
|
+
if (isPickerField(control.entry)) {
|
|
112
|
+
const ids = submission.ids ?? [];
|
|
113
|
+
return (
|
|
114
|
+
ids.length === current.picked.length && ids.every((id, index) => current.picked[index] === id)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return (submission.value ?? "") === (current.text ?? "");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Add what one control submitted to a settings patch, in the form the
|
|
122
|
+
* settings service takes: typed text converted to the value it denotes and
|
|
123
|
+
* left as text when it denotes none (the service then refuses it with the same
|
|
124
|
+
* issue as any surface), `null` for a cleared control, and a toggles select
|
|
125
|
+
* merged into the full record of its field.
|
|
126
|
+
*
|
|
127
|
+
* @param params.control - the control submitted.
|
|
128
|
+
* @param params.submission - what it came back with.
|
|
129
|
+
* @param params.subject - the screen's current subject, for the toggles the
|
|
130
|
+
* select does not show.
|
|
131
|
+
* @param params.patch - the patch built so far.
|
|
132
|
+
* @returns the patch with the control's value.
|
|
133
|
+
*/
|
|
134
|
+
export function withSubmission(params: {
|
|
135
|
+
control: DeclarationControl;
|
|
136
|
+
submission: SettingsEditorGroupMemberSubmission;
|
|
137
|
+
subject: DeclarationSubject;
|
|
138
|
+
patch: Readonly<Record<string, unknown>>;
|
|
139
|
+
}): Readonly<Record<string, unknown>> {
|
|
140
|
+
const { control, submission, subject, patch } = params;
|
|
141
|
+
const { fieldKey, shape } = control;
|
|
142
|
+
const current = Object.hasOwn(patch, fieldKey) ? patch[fieldKey] : subject.values[fieldKey];
|
|
143
|
+
return { ...patch, [fieldKey]: submittedValue({ shape, submission, current }) };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** What one control submitted, as the settings service takes it; `current` is its field's value so far. */
|
|
147
|
+
function submittedValue(params: {
|
|
148
|
+
shape: ControlShape;
|
|
149
|
+
submission: SettingsEditorGroupMemberSubmission;
|
|
150
|
+
current: unknown;
|
|
151
|
+
}): unknown {
|
|
152
|
+
const { shape, submission } = params;
|
|
153
|
+
const { value, ids } = submission;
|
|
154
|
+
switch (shape.kind) {
|
|
155
|
+
case "text":
|
|
156
|
+
case "duration":
|
|
157
|
+
case "secret":
|
|
158
|
+
return value;
|
|
159
|
+
case "number":
|
|
160
|
+
return value === null ? null : typedNumber(value);
|
|
161
|
+
case "lines":
|
|
162
|
+
return value === null ? null : typedLines({ text: value, numeric: shape.numeric });
|
|
163
|
+
case "boolean":
|
|
164
|
+
return ids === null || ids.length === 0 ? null : pickedBoolean(ids[0]);
|
|
165
|
+
case "single":
|
|
166
|
+
return ids === null || ids.length === 0 ? null : singleId(ids);
|
|
167
|
+
case "many":
|
|
168
|
+
return ids === null ? null : [...ids];
|
|
169
|
+
case "toggles":
|
|
170
|
+
return { ...toggleRecord(params.current), ...pickedToggles({ keys: shape.keys, ids }) };
|
|
171
|
+
case "search":
|
|
172
|
+
if (ids === null || ids.length === 0) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return shape.numeric ? singleNumber(ids) : singleId(ids);
|
|
176
|
+
case "other":
|
|
177
|
+
if (value === null) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return shape.numeric ? typedNumber(value) : value;
|
|
181
|
+
default:
|
|
182
|
+
return shape satisfies never;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The one number picked, from its option's value; anything else is left for the service to refuse. */
|
|
187
|
+
function singleNumber(ids: readonly string[]): unknown {
|
|
188
|
+
const picked = singleId(ids);
|
|
189
|
+
return typeof picked === "string" ? typedNumber(picked) : picked;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** A toggles value as a record, or an empty one when the field holds none. */
|
|
193
|
+
function toggleRecord(value: unknown): Readonly<Record<string, unknown>> {
|
|
194
|
+
return typeof value === "object" && value !== null ? { ...value } : {};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Each key of one toggles select, on when picked. */
|
|
198
|
+
function pickedToggles(params: {
|
|
199
|
+
keys: readonly string[];
|
|
200
|
+
ids: readonly string[] | null;
|
|
201
|
+
}): Record<string, boolean> {
|
|
202
|
+
const picked = params.ids ?? [];
|
|
203
|
+
// A picked id the select does not offer is kept, for the service to refuse.
|
|
204
|
+
const keys = [...params.keys, ...picked.filter((id) => !params.keys.includes(id))];
|
|
205
|
+
return Object.fromEntries(keys.map((key) => [key, picked.includes(key)]));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The number typed, or the text itself when it is not one. */
|
|
209
|
+
function typedNumber(text: string): number | string {
|
|
210
|
+
const trimmed = text.trim();
|
|
211
|
+
return DECIMAL_PATTERN.test(trimmed) ? Number(trimmed) : text;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Each non-empty trimmed line as one item; `null` when no line holds one. */
|
|
215
|
+
function typedLines(params: { text: string; numeric: boolean }): unknown[] | null {
|
|
216
|
+
const lines = params.text
|
|
217
|
+
.split(LINE_BREAK)
|
|
218
|
+
.map((line) => line.trim())
|
|
219
|
+
.filter((line) => line !== "");
|
|
220
|
+
if (lines.length === 0) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
return params.numeric ? lines.map(typedNumber) : lines;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** `true` and `false` from their options; any other id is left for the service to refuse. */
|
|
227
|
+
function pickedBoolean(id: string | undefined): boolean | string | undefined {
|
|
228
|
+
if (id === String(true)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (id === String(false)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return id;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** The one id a single picker took; several are left as a list for the service to refuse. */
|
|
238
|
+
function singleId(ids: readonly string[]): string | readonly string[] {
|
|
239
|
+
return ids.length === 1 && ids[0] !== undefined ? ids[0] : ids;
|
|
240
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DeclarationControl,
|
|
3
|
+
declarationControls,
|
|
4
|
+
isFreeEntry,
|
|
5
|
+
type SearchOptions,
|
|
6
|
+
} from "@/discord/components/settings-editor/from-declaration-controls";
|
|
7
|
+
import { controlDisplay } from "@/discord/components/settings-editor/from-declaration-display";
|
|
8
|
+
import { layoutDeclaration } from "@/discord/components/settings-editor/from-declaration-layout";
|
|
9
|
+
import { loadSearchOptions } from "@/discord/components/settings-editor/from-declaration-search";
|
|
10
|
+
import {
|
|
11
|
+
type DeclarationSubject,
|
|
12
|
+
loadDeclarationSubject,
|
|
13
|
+
} from "@/discord/components/settings-editor/from-declaration-subject";
|
|
14
|
+
import {
|
|
15
|
+
controlValue,
|
|
16
|
+
isUnchanged,
|
|
17
|
+
withSubmission,
|
|
18
|
+
} from "@/discord/components/settings-editor/from-declaration-values";
|
|
19
|
+
import type {
|
|
20
|
+
SettingsEditorOptions,
|
|
21
|
+
SettingsEditorWrite,
|
|
22
|
+
} from "@/discord/components/settings-editor/mount-settings-editor";
|
|
23
|
+
import type {
|
|
24
|
+
SettingsEditorGroupMemberSubmission,
|
|
25
|
+
SettingsEditorGroupSubmission,
|
|
26
|
+
SettingsEditorSubmission,
|
|
27
|
+
} from "@/discord/components/settings-editor/settings-editor-field-modal";
|
|
28
|
+
import { NotFoundError } from "@/errors/business-error";
|
|
29
|
+
import type { Locale } from "@/i18n/locale";
|
|
30
|
+
import type { TranslateKey, Translator } from "@/i18n/translator";
|
|
31
|
+
import type { SettingsDeclaration } from "@/settings/define-settings";
|
|
32
|
+
import { displayOrder } from "@/settings/field-order";
|
|
33
|
+
import type { RequestContext, SettingsService } from "@/settings/settings-service";
|
|
34
|
+
|
|
35
|
+
/** What the adapter hands `mountSettingsEditor`; the caller adds its own ids, chrome, clock and logger. */
|
|
36
|
+
type DeclarationEditorOptions = Pick<
|
|
37
|
+
SettingsEditorOptions<unknown, never, string>,
|
|
38
|
+
"fields" | "levels" | "initial" | "currentValue" | "displayValue" | "save" | "saveGroup" | "reset"
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
/** A settings patch, keyed by field. */
|
|
42
|
+
type Patch = Readonly<Record<string, unknown>>;
|
|
43
|
+
|
|
44
|
+
/** What every hook of one screen reads and writes through. */
|
|
45
|
+
interface AdapterScope {
|
|
46
|
+
readonly declaration: SettingsDeclaration;
|
|
47
|
+
readonly service: SettingsService;
|
|
48
|
+
readonly ctx: RequestContext;
|
|
49
|
+
readonly translate: TranslateKey;
|
|
50
|
+
readonly controls: ReadonlyMap<string, DeclarationControl>;
|
|
51
|
+
readonly groups: ReadonlyMap<string, readonly DeclarationControl[]>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build the settings-editor options of a declaration for one guild: its
|
|
56
|
+
* fields and levels, the subject it opens on, and the hooks that read and
|
|
57
|
+
* write through the settings service. The caller spreads the result into
|
|
58
|
+
* `mountSettingsEditor` with its own `ids`, `chrome`, `clock` and `logger`.
|
|
59
|
+
*
|
|
60
|
+
* Every write goes through `service.set` as the administrator, so the screen
|
|
61
|
+
* accepts and refuses exactly what every other surface does; a group's modal
|
|
62
|
+
* is one write carrying only the members the administrator changed.
|
|
63
|
+
*
|
|
64
|
+
* A searchable field (FR-026a) is a select of the first 25 results of its
|
|
65
|
+
* search for an empty query, read when the screen opens, and shows its
|
|
66
|
+
* search's label as its current value. A non-strict one also gets an "Other
|
|
67
|
+
* value" text entry in the same modal: a value typed there replaces the pick
|
|
68
|
+
* and is validated on save like any other. A field whose search offered
|
|
69
|
+
* nothing is typed instead.
|
|
70
|
+
*
|
|
71
|
+
* @param declaration - the module's settings declaration.
|
|
72
|
+
* @param service - the settings service the screen reads and writes through.
|
|
73
|
+
* @param context - the guild, the administrator, their locale and the
|
|
74
|
+
* translator of the texts shown in place of a value.
|
|
75
|
+
* @returns the editor options, the initial subject read from the service.
|
|
76
|
+
* @throws SettingsDeclarationError when a list holds an item kind lists cannot hold.
|
|
77
|
+
*/
|
|
78
|
+
export async function settingsEditorFromDeclaration<D extends SettingsDeclaration>(
|
|
79
|
+
declaration: D,
|
|
80
|
+
service: SettingsService,
|
|
81
|
+
context: {
|
|
82
|
+
readonly guildId: string;
|
|
83
|
+
readonly userId: string;
|
|
84
|
+
readonly locale: Locale;
|
|
85
|
+
readonly translator: Translator;
|
|
86
|
+
},
|
|
87
|
+
): Promise<DeclarationEditorOptions> {
|
|
88
|
+
const { guildId, userId, locale, translator } = context;
|
|
89
|
+
const ctx: RequestContext = { guildId, userId, locale };
|
|
90
|
+
const subject = await loadDeclarationSubject({ declaration, service, ctx });
|
|
91
|
+
const searchOptions = await loadSearchOptions({ declaration, service, ctx, subject });
|
|
92
|
+
const controls = displayedControls(declaration, searchOptions);
|
|
93
|
+
const layout = layoutDeclaration({ declaration, controls });
|
|
94
|
+
const scope: AdapterScope = {
|
|
95
|
+
declaration,
|
|
96
|
+
service,
|
|
97
|
+
ctx,
|
|
98
|
+
translate: (key) => translator.translate(locale, key),
|
|
99
|
+
controls: new Map(controls.map((control) => [control.entry.key, control])),
|
|
100
|
+
groups: layout.groups,
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
fields: layout.fields,
|
|
104
|
+
levels: layout.levels,
|
|
105
|
+
initial: { subject, assets: [] },
|
|
106
|
+
currentValue: (subject: DeclarationSubject, key: string) =>
|
|
107
|
+
controlValue({ translate: scope.translate, subject, control: controlAt({ scope, key }) }),
|
|
108
|
+
displayValue: (subject: DeclarationSubject, key: string) => {
|
|
109
|
+
const control = controlAt({ scope, key });
|
|
110
|
+
// The free entry holds nothing of its own: its field shows beside the select.
|
|
111
|
+
return isFreeEntry(control)
|
|
112
|
+
? null
|
|
113
|
+
: controlDisplay({ translate: scope.translate, subject, control });
|
|
114
|
+
},
|
|
115
|
+
save: (subject: DeclarationSubject, key: string, submission: SettingsEditorSubmission) =>
|
|
116
|
+
saveControl({ scope, subject, control: controlAt({ scope, key }), submission }),
|
|
117
|
+
saveGroup: (
|
|
118
|
+
subject: DeclarationSubject,
|
|
119
|
+
groupKey: string,
|
|
120
|
+
submissions: SettingsEditorGroupSubmission<string>,
|
|
121
|
+
) => saveGroupControls({ scope, subject, groupKey, submissions }),
|
|
122
|
+
reset: async () => {
|
|
123
|
+
await service.reset(declaration, guildId, "all", scope.ctx);
|
|
124
|
+
return reload(scope);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The controls of a declaration in its display order, the one every surface shows. */
|
|
130
|
+
function displayedControls(
|
|
131
|
+
declaration: SettingsDeclaration,
|
|
132
|
+
searchOptions: SearchOptions,
|
|
133
|
+
): DeclarationControl[] {
|
|
134
|
+
const rank = new Map(
|
|
135
|
+
displayOrder(declaration)
|
|
136
|
+
.entries.flatMap((entry) => (entry.kind === "field" ? [entry.key] : entry.fields))
|
|
137
|
+
.map((key, index) => [key, index]),
|
|
138
|
+
);
|
|
139
|
+
const place = (control: DeclarationControl): number => rank.get(control.fieldKey) ?? rank.size;
|
|
140
|
+
return declarationControls(declaration, searchOptions).sort(
|
|
141
|
+
(left, right) => place(left) - place(right),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The screen's subject, read back from the service. */
|
|
146
|
+
async function reload(
|
|
147
|
+
scope: AdapterScope,
|
|
148
|
+
): Promise<SettingsEditorWrite<DeclarationSubject, never>> {
|
|
149
|
+
const { declaration, service, ctx } = scope;
|
|
150
|
+
return { subject: await loadDeclarationSubject({ declaration, service, ctx }), assets: [] };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Write `patch` in one `service.set` (nothing when it is empty), then read the screen back. */
|
|
154
|
+
async function write(params: {
|
|
155
|
+
scope: AdapterScope;
|
|
156
|
+
patch: Patch;
|
|
157
|
+
}): Promise<SettingsEditorWrite<DeclarationSubject, never>> {
|
|
158
|
+
const { scope, patch } = params;
|
|
159
|
+
if (Object.keys(patch).length > 0) {
|
|
160
|
+
await scope.service.set(scope.declaration, scope.ctx.guildId, patch, scope.ctx);
|
|
161
|
+
}
|
|
162
|
+
return reload(scope);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function controlAt(params: { scope: AdapterScope; key: string }): DeclarationControl {
|
|
166
|
+
const { scope, key } = params;
|
|
167
|
+
const control = scope.controls.get(key);
|
|
168
|
+
if (control === undefined) {
|
|
169
|
+
throw new NotFoundError(`Settings screen of "${scope.declaration.id}" has no field "${key}"`);
|
|
170
|
+
}
|
|
171
|
+
return control;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Save one entry as submitted. A secret opens on whether it is set, not on
|
|
176
|
+
* its value: sent back untouched, that text must not replace the secret.
|
|
177
|
+
*/
|
|
178
|
+
function saveControl(params: {
|
|
179
|
+
scope: AdapterScope;
|
|
180
|
+
subject: DeclarationSubject;
|
|
181
|
+
control: DeclarationControl;
|
|
182
|
+
submission: SettingsEditorSubmission;
|
|
183
|
+
}): Promise<SettingsEditorWrite<DeclarationSubject, never>> {
|
|
184
|
+
const { scope, subject, control, submission } = params;
|
|
185
|
+
const untouchedSecret =
|
|
186
|
+
control.shape.kind === "secret" &&
|
|
187
|
+
isUnchanged({
|
|
188
|
+
control,
|
|
189
|
+
submission,
|
|
190
|
+
current: controlValue({ translate: scope.translate, subject, control }),
|
|
191
|
+
});
|
|
192
|
+
const patch = untouchedSecret ? {} : withSubmission({ control, submission, subject, patch: {} });
|
|
193
|
+
return write({ scope, patch });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Save a group's modal in one write carrying only the members changed from
|
|
198
|
+
* what the modal opened on: a picker whose stored entity is gone, or a
|
|
199
|
+
* secret showing whether it is set, left untouched keeps its stored value.
|
|
200
|
+
*/
|
|
201
|
+
function saveGroupControls(params: {
|
|
202
|
+
scope: AdapterScope;
|
|
203
|
+
subject: DeclarationSubject;
|
|
204
|
+
groupKey: string;
|
|
205
|
+
submissions: SettingsEditorGroupSubmission<string>;
|
|
206
|
+
}): Promise<SettingsEditorWrite<DeclarationSubject, never>> {
|
|
207
|
+
const { scope, subject, groupKey, submissions } = params;
|
|
208
|
+
let patch: Patch = {};
|
|
209
|
+
for (const control of scope.groups.get(groupKey) ?? []) {
|
|
210
|
+
const submission: SettingsEditorGroupMemberSubmission | undefined =
|
|
211
|
+
submissions[control.entry.key];
|
|
212
|
+
const current = controlValue({ translate: scope.translate, subject, control });
|
|
213
|
+
if (submission !== undefined && !isUnchanged({ control, submission, current })) {
|
|
214
|
+
patch = withSubmission({ control, submission, subject, patch });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return write({ scope, patch });
|
|
218
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { settingsEditorFromDeclaration } from "@/discord/components/settings-editor/from-declaration";
|
|
1
2
|
export {
|
|
2
3
|
mountSettingsEditor,
|
|
3
4
|
type SettingsEditorOptions,
|
|
@@ -27,15 +28,17 @@ export type {
|
|
|
27
28
|
SettingsEditorGroupSubmission,
|
|
28
29
|
SettingsEditorSubmission,
|
|
29
30
|
} from "@/discord/components/settings-editor/settings-editor-field-modal";
|
|
31
|
+
export {
|
|
32
|
+
pickedFieldValue,
|
|
33
|
+
textFieldValue,
|
|
34
|
+
} from "@/discord/components/settings-editor/settings-editor-field-values";
|
|
30
35
|
export {
|
|
31
36
|
MAX_MODAL_COMPONENTS,
|
|
32
37
|
MAX_MODAL_INPUT_LENGTH,
|
|
33
|
-
pickedFieldValue,
|
|
34
38
|
type SettingsEditorChoice,
|
|
35
39
|
type SettingsEditorField,
|
|
36
40
|
type SettingsEditorFieldValue,
|
|
37
41
|
type SettingsEditorGroupField,
|
|
38
42
|
type SettingsEditorPickerField,
|
|
39
43
|
type SettingsEditorValueField,
|
|
40
|
-
textFieldValue,
|
|
41
44
|
} from "@/discord/components/settings-editor/settings-editor-fields";
|
|
@@ -4,14 +4,14 @@ import type {
|
|
|
4
4
|
ModalMessageModalSubmitInteraction,
|
|
5
5
|
} from "discord.js";
|
|
6
6
|
import type { Clock } from "@/clock";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "@/discord/components/interactive-message";
|
|
7
|
+
import type {
|
|
8
|
+
InteractiveMessageHandle,
|
|
9
|
+
InteractiveSelect,
|
|
10
|
+
InteractiveView,
|
|
11
|
+
} from "@/discord/components/interactive-message/interactive-message-collector";
|
|
12
|
+
import { toMessageEditOptions } from "@/discord/components/interactive-message/message-edit-options";
|
|
13
|
+
import { mountInteractiveMessage } from "@/discord/components/interactive-message/mount-interactive-message";
|
|
14
|
+
import type { StateStore } from "@/discord/components/interactive-message/state-store";
|
|
15
15
|
import {
|
|
16
16
|
popLevel,
|
|
17
17
|
pushLevel,
|
|
@@ -50,8 +50,11 @@ import {
|
|
|
50
50
|
} from "@/discord/components/settings-editor/settings-editor-fields";
|
|
51
51
|
import { SETTINGS_EDITOR_MESSAGES } from "@/discord/i18n";
|
|
52
52
|
import { type Button, type ComponentContext, createButton } from "@/discord/interaction/button";
|
|
53
|
-
import { BusinessError
|
|
54
|
-
import {
|
|
53
|
+
import { BusinessError } from "@/errors/business-error";
|
|
54
|
+
import { describeError } from "@/errors/describe-error";
|
|
55
|
+
import { resolveBusinessMessage } from "@/i18n/business-message";
|
|
56
|
+
import type { Locale } from "@/i18n/locale";
|
|
57
|
+
import type { Translator } from "@/i18n/translator";
|
|
55
58
|
import type { Logger } from "@/logger";
|
|
56
59
|
|
|
57
60
|
/**
|
|
@@ -83,6 +86,18 @@ export interface SettingsEditorOptions<S, A, F extends string> {
|
|
|
83
86
|
readonly initial: SettingsEditorWrite<S, A>;
|
|
84
87
|
/** What the modal for `field` opens on. */
|
|
85
88
|
currentValue(subject: S, field: F): SettingsEditorFieldValue;
|
|
89
|
+
/**
|
|
90
|
+
* What `field` currently holds, already translated, shown on the card under
|
|
91
|
+
* the entry's hint as "Current: …" so an administrator reads it without
|
|
92
|
+
* opening the modal. Rendered from the state's subject, so it follows every
|
|
93
|
+
* write. `null` shows no line for that field; a group shows one
|
|
94
|
+
* `label: value` per member answering one; a level shows nothing. A long
|
|
95
|
+
* value is cut with an ellipsis.
|
|
96
|
+
*
|
|
97
|
+
* Read by the card layout only: the embeds layout ignores it. Left out, the
|
|
98
|
+
* card renders exactly as it did without it.
|
|
99
|
+
*/
|
|
100
|
+
displayValue?(subject: S, field: F): string | null;
|
|
86
101
|
/**
|
|
87
102
|
* Persist one field — its typed value, its upload, or what a picker's select
|
|
88
103
|
* came back with, whichever `submission` carries.
|
|
@@ -574,6 +589,9 @@ export async function mountSettingsEditor<S, A, F extends string>(
|
|
|
574
589
|
translator,
|
|
575
590
|
locale,
|
|
576
591
|
dressing,
|
|
592
|
+
options.displayValue === undefined
|
|
593
|
+
? undefined
|
|
594
|
+
: (subject, field) => options.displayValue?.(subject, field) ?? null,
|
|
577
595
|
);
|
|
578
596
|
} else {
|
|
579
597
|
view = createEditorView(
|
|
@@ -65,3 +65,44 @@ export function pushLevel(path: SettingsEditorPath, key: string): SettingsEditor
|
|
|
65
65
|
export function popLevel(path: SettingsEditorPath): SettingsEditorPath {
|
|
66
66
|
return canReturn(path) ? path.slice(0, -1) : path;
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
/** Separates the levels in a screen's heading trail. */
|
|
70
|
+
const BREADCRUMB_SEPARATOR = " › ";
|
|
71
|
+
|
|
72
|
+
/** Every level a screen declares, root first, and how to find one by key. */
|
|
73
|
+
export interface SettingsEditorLevels<F extends string> {
|
|
74
|
+
readonly all: readonly SettingsEditorLevel<F>[];
|
|
75
|
+
/**
|
|
76
|
+
* The level `key` names. A path entry naming no declared level cannot happen
|
|
77
|
+
* — only a level field puts one there — but falling back to the root keeps a
|
|
78
|
+
* corrupted state showing a usable screen rather than an empty one.
|
|
79
|
+
*/
|
|
80
|
+
at(key: string): SettingsEditorLevel<F>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Index a screen's levels. The root is a level like any other, named after the
|
|
85
|
+
* screen itself, so rendering never special-cases "no depth declared".
|
|
86
|
+
*/
|
|
87
|
+
export function indexLevels<F extends string>(
|
|
88
|
+
root: SettingsEditorLevel<F>,
|
|
89
|
+
levels: readonly SettingsEditorLevel<F>[] | undefined,
|
|
90
|
+
): SettingsEditorLevels<F> {
|
|
91
|
+
const all = [root, ...(levels ?? [])];
|
|
92
|
+
return {
|
|
93
|
+
all,
|
|
94
|
+
at: (key) => all.find((level) => level.key === key) ?? root,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Where the reader is, as a trail rather than only the current name: a level
|
|
100
|
+
* called "Roles" says nothing on its own about which type it belongs to.
|
|
101
|
+
*/
|
|
102
|
+
export function levelTrail<F extends string>(
|
|
103
|
+
path: SettingsEditorPath,
|
|
104
|
+
levels: SettingsEditorLevels<F>,
|
|
105
|
+
titleOf: (level: SettingsEditorLevel<F>) => string,
|
|
106
|
+
): string {
|
|
107
|
+
return path.map((key) => titleOf(levels.at(key))).join(BREADCRUMB_SEPARATOR);
|
|
108
|
+
}
|