@neontechspace/fluxerly 1000.0.0-nightly.12
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/CHANGELOG.md +833 -0
- package/LICENSE +201 -0
- package/README.md +129 -0
- package/agents/AGENTS.md +195 -0
- package/dist/api/default/application.d.ts +34 -0
- package/dist/api/default/application.d.ts.map +1 -0
- package/dist/api/default/application.js +2 -0
- package/dist/api/default/application.js.map +1 -0
- package/dist/api/default/attachments.d.ts +89 -0
- package/dist/api/default/attachments.d.ts.map +1 -0
- package/dist/api/default/attachments.js +2 -0
- package/dist/api/default/attachments.js.map +1 -0
- package/dist/api/default/audit-logs.d.ts +48 -0
- package/dist/api/default/audit-logs.d.ts.map +1 -0
- package/dist/api/default/audit-logs.js +2 -0
- package/dist/api/default/audit-logs.js.map +1 -0
- package/dist/api/default/bot.d.ts +200 -0
- package/dist/api/default/bot.d.ts.map +1 -0
- package/dist/api/default/bot.js +309 -0
- package/dist/api/default/bot.js.map +1 -0
- package/dist/api/default/cache.d.ts +105 -0
- package/dist/api/default/cache.d.ts.map +1 -0
- package/dist/api/default/cache.js +2 -0
- package/dist/api/default/cache.js.map +1 -0
- package/dist/api/default/channels.d.ts +249 -0
- package/dist/api/default/channels.d.ts.map +1 -0
- package/dist/api/default/channels.js +2 -0
- package/dist/api/default/channels.js.map +1 -0
- package/dist/api/default/client.d.ts +528 -0
- package/dist/api/default/client.d.ts.map +1 -0
- package/dist/api/default/client.js +301 -0
- package/dist/api/default/client.js.map +1 -0
- package/dist/api/default/collectors.d.ts +76 -0
- package/dist/api/default/collectors.d.ts.map +1 -0
- package/dist/api/default/collectors.js +2 -0
- package/dist/api/default/collectors.js.map +1 -0
- package/dist/api/default/commands.d.ts +91 -0
- package/dist/api/default/commands.d.ts.map +1 -0
- package/dist/api/default/commands.js +92 -0
- package/dist/api/default/commands.js.map +1 -0
- package/dist/api/default/direct-messages.d.ts +111 -0
- package/dist/api/default/direct-messages.d.ts.map +1 -0
- package/dist/api/default/direct-messages.js +2 -0
- package/dist/api/default/direct-messages.js.map +1 -0
- package/dist/api/default/discovery.d.ts +76 -0
- package/dist/api/default/discovery.d.ts.map +1 -0
- package/dist/api/default/discovery.js +2 -0
- package/dist/api/default/discovery.js.map +1 -0
- package/dist/api/default/emojis.d.ts +88 -0
- package/dist/api/default/emojis.d.ts.map +1 -0
- package/dist/api/default/emojis.js +2 -0
- package/dist/api/default/emojis.js.map +1 -0
- package/dist/api/default/events.d.ts +127 -0
- package/dist/api/default/events.d.ts.map +1 -0
- package/dist/api/default/events.js +2 -0
- package/dist/api/default/events.js.map +1 -0
- package/dist/api/default/gateway.d.ts +62 -0
- package/dist/api/default/gateway.d.ts.map +1 -0
- package/dist/api/default/gateway.js +2 -0
- package/dist/api/default/gateway.js.map +1 -0
- package/dist/api/default/guards.d.ts +87 -0
- package/dist/api/default/guards.d.ts.map +1 -0
- package/dist/api/default/guards.js +78 -0
- package/dist/api/default/guards.js.map +1 -0
- package/dist/api/default/guilds.d.ts +230 -0
- package/dist/api/default/guilds.d.ts.map +1 -0
- package/dist/api/default/guilds.js +2 -0
- package/dist/api/default/guilds.js.map +1 -0
- package/dist/api/default/helpers.d.ts +79 -0
- package/dist/api/default/helpers.d.ts.map +1 -0
- package/dist/api/default/helpers.js +79 -0
- package/dist/api/default/helpers.js.map +1 -0
- package/dist/api/default/instance.d.ts +47 -0
- package/dist/api/default/instance.d.ts.map +1 -0
- package/dist/api/default/instance.js +2 -0
- package/dist/api/default/instance.js.map +1 -0
- package/dist/api/default/invites.d.ts +57 -0
- package/dist/api/default/invites.d.ts.map +1 -0
- package/dist/api/default/invites.js +2 -0
- package/dist/api/default/invites.js.map +1 -0
- package/dist/api/default/logging.d.ts +37 -0
- package/dist/api/default/logging.d.ts.map +1 -0
- package/dist/api/default/logging.js +2 -0
- package/dist/api/default/logging.js.map +1 -0
- package/dist/api/default/members.d.ts +442 -0
- package/dist/api/default/members.d.ts.map +1 -0
- package/dist/api/default/members.js +2 -0
- package/dist/api/default/members.js.map +1 -0
- package/dist/api/default/messages.d.ts +898 -0
- package/dist/api/default/messages.d.ts.map +1 -0
- package/dist/api/default/messages.js +2 -0
- package/dist/api/default/messages.js.map +1 -0
- package/dist/api/default/oauth.d.ts +168 -0
- package/dist/api/default/oauth.d.ts.map +1 -0
- package/dist/api/default/oauth.js +84 -0
- package/dist/api/default/oauth.js.map +1 -0
- package/dist/api/default/permissions.d.ts +44 -0
- package/dist/api/default/permissions.d.ts.map +1 -0
- package/dist/api/default/permissions.js +2 -0
- package/dist/api/default/permissions.js.map +1 -0
- package/dist/api/default/presence.d.ts +88 -0
- package/dist/api/default/presence.d.ts.map +1 -0
- package/dist/api/default/presence.js +2 -0
- package/dist/api/default/presence.js.map +1 -0
- package/dist/api/default/rest.d.ts +28 -0
- package/dist/api/default/rest.d.ts.map +1 -0
- package/dist/api/default/rest.js +2 -0
- package/dist/api/default/rest.js.map +1 -0
- package/dist/api/default/results.d.ts +31 -0
- package/dist/api/default/results.d.ts.map +1 -0
- package/dist/api/default/results.js +14 -0
- package/dist/api/default/results.js.map +1 -0
- package/dist/api/default/roles.d.ts +119 -0
- package/dist/api/default/roles.d.ts.map +1 -0
- package/dist/api/default/roles.js +2 -0
- package/dist/api/default/roles.js.map +1 -0
- package/dist/api/default/stickers.d.ts +92 -0
- package/dist/api/default/stickers.d.ts.map +1 -0
- package/dist/api/default/stickers.js +2 -0
- package/dist/api/default/stickers.js.map +1 -0
- package/dist/api/default/supervisor.d.ts +51 -0
- package/dist/api/default/supervisor.d.ts.map +1 -0
- package/dist/api/default/supervisor.js +53 -0
- package/dist/api/default/supervisor.js.map +1 -0
- package/dist/api/default/threads.d.ts +158 -0
- package/dist/api/default/threads.d.ts.map +1 -0
- package/dist/api/default/threads.js +2 -0
- package/dist/api/default/threads.js.map +1 -0
- package/dist/api/default/users.d.ts +73 -0
- package/dist/api/default/users.d.ts.map +1 -0
- package/dist/api/default/users.js +2 -0
- package/dist/api/default/users.js.map +1 -0
- package/dist/api/default/webhook-client.d.ts +149 -0
- package/dist/api/default/webhook-client.d.ts.map +1 -0
- package/dist/api/default/webhook-client.js +69 -0
- package/dist/api/default/webhook-client.js.map +1 -0
- package/dist/api/default/webhooks.d.ts +62 -0
- package/dist/api/default/webhooks.d.ts.map +1 -0
- package/dist/api/default/webhooks.js +2 -0
- package/dist/api/default/webhooks.js.map +1 -0
- package/dist/api/effect/application.d.ts +29 -0
- package/dist/api/effect/application.d.ts.map +1 -0
- package/dist/api/effect/application.js +2 -0
- package/dist/api/effect/application.js.map +1 -0
- package/dist/api/effect/attachments.d.ts +84 -0
- package/dist/api/effect/attachments.d.ts.map +1 -0
- package/dist/api/effect/attachments.js +2 -0
- package/dist/api/effect/attachments.js.map +1 -0
- package/dist/api/effect/audit-logs.d.ts +42 -0
- package/dist/api/effect/audit-logs.d.ts.map +1 -0
- package/dist/api/effect/audit-logs.js +2 -0
- package/dist/api/effect/audit-logs.js.map +1 -0
- package/dist/api/effect/bot.d.ts +233 -0
- package/dist/api/effect/bot.d.ts.map +1 -0
- package/dist/api/effect/bot.js +230 -0
- package/dist/api/effect/bot.js.map +1 -0
- package/dist/api/effect/cache.d.ts +103 -0
- package/dist/api/effect/cache.d.ts.map +1 -0
- package/dist/api/effect/cache.js +2 -0
- package/dist/api/effect/cache.js.map +1 -0
- package/dist/api/effect/channels.d.ts +231 -0
- package/dist/api/effect/channels.d.ts.map +1 -0
- package/dist/api/effect/channels.js +2 -0
- package/dist/api/effect/channels.js.map +1 -0
- package/dist/api/effect/client.d.ts +622 -0
- package/dist/api/effect/client.d.ts.map +1 -0
- package/dist/api/effect/client.js +191 -0
- package/dist/api/effect/client.js.map +1 -0
- package/dist/api/effect/collectors.d.ts +90 -0
- package/dist/api/effect/collectors.d.ts.map +1 -0
- package/dist/api/effect/collectors.js +2 -0
- package/dist/api/effect/collectors.js.map +1 -0
- package/dist/api/effect/commands.d.ts +86 -0
- package/dist/api/effect/commands.d.ts.map +1 -0
- package/dist/api/effect/commands.js +87 -0
- package/dist/api/effect/commands.js.map +1 -0
- package/dist/api/effect/direct-messages.d.ts +102 -0
- package/dist/api/effect/direct-messages.d.ts.map +1 -0
- package/dist/api/effect/direct-messages.js +2 -0
- package/dist/api/effect/direct-messages.js.map +1 -0
- package/dist/api/effect/discovery.d.ts +68 -0
- package/dist/api/effect/discovery.d.ts.map +1 -0
- package/dist/api/effect/discovery.js +2 -0
- package/dist/api/effect/discovery.js.map +1 -0
- package/dist/api/effect/emojis.d.ts +77 -0
- package/dist/api/effect/emojis.d.ts.map +1 -0
- package/dist/api/effect/emojis.js +2 -0
- package/dist/api/effect/emojis.js.map +1 -0
- package/dist/api/effect/events.d.ts +78 -0
- package/dist/api/effect/events.d.ts.map +1 -0
- package/dist/api/effect/events.js +12 -0
- package/dist/api/effect/events.js.map +1 -0
- package/dist/api/effect/failures.d.ts +14 -0
- package/dist/api/effect/failures.d.ts.map +1 -0
- package/dist/api/effect/failures.js +2 -0
- package/dist/api/effect/failures.js.map +1 -0
- package/dist/api/effect/gateway.d.ts +63 -0
- package/dist/api/effect/gateway.d.ts.map +1 -0
- package/dist/api/effect/gateway.js +2 -0
- package/dist/api/effect/gateway.js.map +1 -0
- package/dist/api/effect/guards.d.ts +91 -0
- package/dist/api/effect/guards.d.ts.map +1 -0
- package/dist/api/effect/guards.js +94 -0
- package/dist/api/effect/guards.js.map +1 -0
- package/dist/api/effect/guilds.d.ts +221 -0
- package/dist/api/effect/guilds.d.ts.map +1 -0
- package/dist/api/effect/guilds.js +2 -0
- package/dist/api/effect/guilds.js.map +1 -0
- package/dist/api/effect/helpers.d.ts +300 -0
- package/dist/api/effect/helpers.d.ts.map +1 -0
- package/dist/api/effect/helpers.js +156 -0
- package/dist/api/effect/helpers.js.map +1 -0
- package/dist/api/effect/hierarchy.d.ts +26 -0
- package/dist/api/effect/hierarchy.d.ts.map +1 -0
- package/dist/api/effect/hierarchy.js +26 -0
- package/dist/api/effect/hierarchy.js.map +1 -0
- package/dist/api/effect/instance.d.ts +46 -0
- package/dist/api/effect/instance.d.ts.map +1 -0
- package/dist/api/effect/instance.js +8 -0
- package/dist/api/effect/instance.js.map +1 -0
- package/dist/api/effect/invites.d.ts +49 -0
- package/dist/api/effect/invites.d.ts.map +1 -0
- package/dist/api/effect/invites.js +2 -0
- package/dist/api/effect/invites.js.map +1 -0
- package/dist/api/effect/logging.d.ts +37 -0
- package/dist/api/effect/logging.d.ts.map +1 -0
- package/dist/api/effect/logging.js +2 -0
- package/dist/api/effect/logging.js.map +1 -0
- package/dist/api/effect/members.d.ts +433 -0
- package/dist/api/effect/members.d.ts.map +1 -0
- package/dist/api/effect/members.js +2 -0
- package/dist/api/effect/members.js.map +1 -0
- package/dist/api/effect/messages.d.ts +877 -0
- package/dist/api/effect/messages.d.ts.map +1 -0
- package/dist/api/effect/messages.js +2 -0
- package/dist/api/effect/messages.js.map +1 -0
- package/dist/api/effect/oauth.d.ts +148 -0
- package/dist/api/effect/oauth.d.ts.map +1 -0
- package/dist/api/effect/oauth.js +51 -0
- package/dist/api/effect/oauth.js.map +1 -0
- package/dist/api/effect/permissions.d.ts +41 -0
- package/dist/api/effect/permissions.d.ts.map +1 -0
- package/dist/api/effect/permissions.js +2 -0
- package/dist/api/effect/permissions.js.map +1 -0
- package/dist/api/effect/presence.d.ts +83 -0
- package/dist/api/effect/presence.d.ts.map +1 -0
- package/dist/api/effect/presence.js +2 -0
- package/dist/api/effect/presence.js.map +1 -0
- package/dist/api/effect/rest.d.ts +27 -0
- package/dist/api/effect/rest.d.ts.map +1 -0
- package/dist/api/effect/rest.js +2 -0
- package/dist/api/effect/rest.js.map +1 -0
- package/dist/api/effect/roles.d.ts +104 -0
- package/dist/api/effect/roles.d.ts.map +1 -0
- package/dist/api/effect/roles.js +2 -0
- package/dist/api/effect/roles.js.map +1 -0
- package/dist/api/effect/service.d.ts +76 -0
- package/dist/api/effect/service.d.ts.map +1 -0
- package/dist/api/effect/service.js +79 -0
- package/dist/api/effect/service.js.map +1 -0
- package/dist/api/effect/stickers.d.ts +81 -0
- package/dist/api/effect/stickers.d.ts.map +1 -0
- package/dist/api/effect/stickers.js +2 -0
- package/dist/api/effect/stickers.js.map +1 -0
- package/dist/api/effect/supervisor.d.ts +56 -0
- package/dist/api/effect/supervisor.d.ts.map +1 -0
- package/dist/api/effect/supervisor.js +58 -0
- package/dist/api/effect/supervisor.js.map +1 -0
- package/dist/api/effect/threads.d.ts +154 -0
- package/dist/api/effect/threads.d.ts.map +1 -0
- package/dist/api/effect/threads.js +2 -0
- package/dist/api/effect/threads.js.map +1 -0
- package/dist/api/effect/users.d.ts +62 -0
- package/dist/api/effect/users.d.ts.map +1 -0
- package/dist/api/effect/users.js +2 -0
- package/dist/api/effect/users.js.map +1 -0
- package/dist/api/effect/webhook-client.d.ts +140 -0
- package/dist/api/effect/webhook-client.d.ts.map +1 -0
- package/dist/api/effect/webhook-client.js +50 -0
- package/dist/api/effect/webhook-client.js.map +1 -0
- package/dist/api/effect/webhooks.d.ts +54 -0
- package/dist/api/effect/webhooks.d.ts.map +1 -0
- package/dist/api/effect/webhooks.js +2 -0
- package/dist/api/effect/webhooks.js.map +1 -0
- package/dist/api-errors.d.ts +609 -0
- package/dist/api-errors.d.ts.map +1 -0
- package/dist/api-errors.js +589 -0
- package/dist/api-errors.js.map +1 -0
- package/dist/application.d.ts +85 -0
- package/dist/application.d.ts.map +1 -0
- package/dist/application.js +44 -0
- package/dist/application.js.map +1 -0
- package/dist/assets.d.ts +197 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +419 -0
- package/dist/assets.js.map +1 -0
- package/dist/attachments.d.ts +400 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +88 -0
- package/dist/attachments.js.map +1 -0
- package/dist/audit-logs.d.ts +292 -0
- package/dist/audit-logs.d.ts.map +1 -0
- package/dist/audit-logs.js +46 -0
- package/dist/audit-logs.js.map +1 -0
- package/dist/bot-runner.d.ts +69 -0
- package/dist/bot-runner.d.ts.map +1 -0
- package/dist/bot-runner.js +41 -0
- package/dist/bot-runner.js.map +1 -0
- package/dist/builders.d.ts +153 -0
- package/dist/builders.d.ts.map +1 -0
- package/dist/builders.js +326 -0
- package/dist/builders.js.map +1 -0
- package/dist/cache.d.ts +187 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +2 -0
- package/dist/cache.js.map +1 -0
- package/dist/channels.d.ts +903 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +125 -0
- package/dist/channels.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +645 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/collectors.d.ts +205 -0
- package/dist/collectors.d.ts.map +1 -0
- package/dist/collectors.js +71 -0
- package/dist/collectors.js.map +1 -0
- package/dist/colors.d.ts +55 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +40 -0
- package/dist/colors.js.map +1 -0
- package/dist/command-arguments.d.ts +353 -0
- package/dist/command-arguments.d.ts.map +1 -0
- package/dist/command-arguments.js +2 -0
- package/dist/command-arguments.js.map +1 -0
- package/dist/command-help.d.ts +54 -0
- package/dist/command-help.d.ts.map +1 -0
- package/dist/command-help.js +2 -0
- package/dist/command-help.js.map +1 -0
- package/dist/commands.d.ts +328 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +62 -0
- package/dist/commands.js.map +1 -0
- package/dist/counts.d.ts +124 -0
- package/dist/counts.d.ts.map +1 -0
- package/dist/counts.js +52 -0
- package/dist/counts.js.map +1 -0
- package/dist/default-commands.d.ts +400 -0
- package/dist/default-commands.d.ts.map +1 -0
- package/dist/default-commands.js +488 -0
- package/dist/default-commands.js.map +1 -0
- package/dist/default-supervisor.d.ts +111 -0
- package/dist/default-supervisor.d.ts.map +1 -0
- package/dist/default-supervisor.js +277 -0
- package/dist/default-supervisor.js.map +1 -0
- package/dist/discovery.d.ts +223 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +18 -0
- package/dist/discovery.js.map +1 -0
- package/dist/effect-testing.d.ts +388 -0
- package/dist/effect-testing.d.ts.map +1 -0
- package/dist/effect-testing.js +261 -0
- package/dist/effect-testing.js.map +1 -0
- package/dist/effect.d.ts +220 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +101 -0
- package/dist/effect.js.map +1 -0
- package/dist/embeds.d.ts +243 -0
- package/dist/embeds.d.ts.map +1 -0
- package/dist/embeds.js +2 -0
- package/dist/embeds.js.map +1 -0
- package/dist/error-tools.d.ts +101 -0
- package/dist/error-tools.d.ts.map +1 -0
- package/dist/error-tools.js +125 -0
- package/dist/error-tools.js.map +1 -0
- package/dist/errors.d.ts +386 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +440 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +798 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/expressions.d.ts +188 -0
- package/dist/expressions.d.ts.map +1 -0
- package/dist/expressions.js +2 -0
- package/dist/expressions.js.map +1 -0
- package/dist/failures.d.ts +53 -0
- package/dist/failures.d.ts.map +1 -0
- package/dist/failures.js +2 -0
- package/dist/failures.js.map +1 -0
- package/dist/gateway.d.ts +48 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +60 -0
- package/dist/gateway.js.map +1 -0
- package/dist/guilds.d.ts +923 -0
- package/dist/guilds.d.ts.map +1 -0
- package/dist/guilds.js +204 -0
- package/dist/guilds.js.map +1 -0
- package/dist/helpers.d.ts +423 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +434 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/input-validation.d.ts +51 -0
- package/dist/input-validation.d.ts.map +1 -0
- package/dist/input-validation.js +34 -0
- package/dist/input-validation.js.map +1 -0
- package/dist/instance.d.ts +101 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +2 -0
- package/dist/instance.js.map +1 -0
- package/dist/internal/agents-section.d.ts +22 -0
- package/dist/internal/agents-section.d.ts.map +1 -0
- package/dist/internal/agents-section.js +101 -0
- package/dist/internal/agents-section.js.map +1 -0
- package/dist/internal/application.d.ts +16 -0
- package/dist/internal/application.d.ts.map +1 -0
- package/dist/internal/application.js +33 -0
- package/dist/internal/application.js.map +1 -0
- package/dist/internal/attachment-refresh.d.ts +18 -0
- package/dist/internal/attachment-refresh.d.ts.map +1 -0
- package/dist/internal/attachment-refresh.js +39 -0
- package/dist/internal/attachment-refresh.js.map +1 -0
- package/dist/internal/attachments.d.ts +45 -0
- package/dist/internal/attachments.d.ts.map +1 -0
- package/dist/internal/attachments.js +230 -0
- package/dist/internal/attachments.js.map +1 -0
- package/dist/internal/audit-logs.d.ts +13 -0
- package/dist/internal/audit-logs.d.ts.map +1 -0
- package/dist/internal/audit-logs.js +295 -0
- package/dist/internal/audit-logs.js.map +1 -0
- package/dist/internal/audit.d.ts +12 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +22 -0
- package/dist/internal/audit.js.map +1 -0
- package/dist/internal/binding/default-adapters.d.ts +22 -0
- package/dist/internal/binding/default-adapters.d.ts.map +1 -0
- package/dist/internal/binding/default-adapters.js +304 -0
- package/dist/internal/binding/default-adapters.js.map +1 -0
- package/dist/internal/binding/default.d.ts +45 -0
- package/dist/internal/binding/default.d.ts.map +1 -0
- package/dist/internal/binding/default.js +86 -0
- package/dist/internal/binding/default.js.map +1 -0
- package/dist/internal/binding/execute.d.ts +51 -0
- package/dist/internal/binding/execute.d.ts.map +1 -0
- package/dist/internal/binding/execute.js +123 -0
- package/dist/internal/binding/execute.js.map +1 -0
- package/dist/internal/binding/native-adapters.d.ts +23 -0
- package/dist/internal/binding/native-adapters.d.ts.map +1 -0
- package/dist/internal/binding/native-adapters.js +90 -0
- package/dist/internal/binding/native-adapters.js.map +1 -0
- package/dist/internal/binding/native.d.ts +41 -0
- package/dist/internal/binding/native.d.ts.map +1 -0
- package/dist/internal/binding/native.js +34 -0
- package/dist/internal/binding/native.js.map +1 -0
- package/dist/internal/binding/operations.d.ts +361 -0
- package/dist/internal/binding/operations.d.ts.map +1 -0
- package/dist/internal/binding/operations.js +272 -0
- package/dist/internal/binding/operations.js.map +1 -0
- package/dist/internal/bot-runner.d.ts +65 -0
- package/dist/internal/bot-runner.d.ts.map +1 -0
- package/dist/internal/bot-runner.js +363 -0
- package/dist/internal/bot-runner.js.map +1 -0
- package/dist/internal/cache-changes.d.ts +43 -0
- package/dist/internal/cache-changes.d.ts.map +1 -0
- package/dist/internal/cache-changes.js +320 -0
- package/dist/internal/cache-changes.js.map +1 -0
- package/dist/internal/cache.d.ts +57 -0
- package/dist/internal/cache.d.ts.map +1 -0
- package/dist/internal/cache.js +318 -0
- package/dist/internal/cache.js.map +1 -0
- package/dist/internal/calls.d.ts +16 -0
- package/dist/internal/calls.d.ts.map +1 -0
- package/dist/internal/calls.js +108 -0
- package/dist/internal/calls.js.map +1 -0
- package/dist/internal/channel-cache.d.ts +69 -0
- package/dist/internal/channel-cache.d.ts.map +1 -0
- package/dist/internal/channel-cache.js +413 -0
- package/dist/internal/channel-cache.js.map +1 -0
- package/dist/internal/channel-decode.d.ts +44 -0
- package/dist/internal/channel-decode.d.ts.map +1 -0
- package/dist/internal/channel-decode.js +377 -0
- package/dist/internal/channel-decode.js.map +1 -0
- package/dist/internal/channels.d.ts +60 -0
- package/dist/internal/channels.d.ts.map +1 -0
- package/dist/internal/channels.js +605 -0
- package/dist/internal/channels.js.map +1 -0
- package/dist/internal/client/backoff.d.ts +42 -0
- package/dist/internal/client/backoff.d.ts.map +1 -0
- package/dist/internal/client/backoff.js +36 -0
- package/dist/internal/client/backoff.js.map +1 -0
- package/dist/internal/client/configuration-fields.d.ts +14 -0
- package/dist/internal/client/configuration-fields.d.ts.map +1 -0
- package/dist/internal/client/configuration-fields.js +11 -0
- package/dist/internal/client/configuration-fields.js.map +1 -0
- package/dist/internal/client/drain.d.ts +33 -0
- package/dist/internal/client/drain.d.ts.map +1 -0
- package/dist/internal/client/drain.js +109 -0
- package/dist/internal/client/drain.js.map +1 -0
- package/dist/internal/client/intake.d.ts +27 -0
- package/dist/internal/client/intake.d.ts.map +1 -0
- package/dist/internal/client/intake.js +198 -0
- package/dist/internal/client/intake.js.map +1 -0
- package/dist/internal/client/lifetime.d.ts +35 -0
- package/dist/internal/client/lifetime.d.ts.map +1 -0
- package/dist/internal/client/lifetime.js +109 -0
- package/dist/internal/client/lifetime.js.map +1 -0
- package/dist/internal/client/shard-loop.d.ts +93 -0
- package/dist/internal/client/shard-loop.d.ts.map +1 -0
- package/dist/internal/client/shard-loop.js +452 -0
- package/dist/internal/client/shard-loop.js.map +1 -0
- package/dist/internal/client-registry.d.ts +26 -0
- package/dist/internal/client-registry.d.ts.map +1 -0
- package/dist/internal/client-registry.js +11 -0
- package/dist/internal/client-registry.js.map +1 -0
- package/dist/internal/client.d.ts +189 -0
- package/dist/internal/client.d.ts.map +1 -0
- package/dist/internal/client.js +1935 -0
- package/dist/internal/client.js.map +1 -0
- package/dist/internal/clock.d.ts +9 -0
- package/dist/internal/clock.d.ts.map +1 -0
- package/dist/internal/clock.js +5 -0
- package/dist/internal/clock.js.map +1 -0
- package/dist/internal/code-catalogue.d.ts +712 -0
- package/dist/internal/code-catalogue.d.ts.map +1 -0
- package/dist/internal/code-catalogue.js +655 -0
- package/dist/internal/code-catalogue.js.map +1 -0
- package/dist/internal/collector.d.ts +34 -0
- package/dist/internal/collector.d.ts.map +1 -0
- package/dist/internal/collector.js +400 -0
- package/dist/internal/collector.js.map +1 -0
- package/dist/internal/command-arguments.d.ts +29 -0
- package/dist/internal/command-arguments.d.ts.map +1 -0
- package/dist/internal/command-arguments.js +484 -0
- package/dist/internal/command-arguments.js.map +1 -0
- package/dist/internal/command-guards.d.ts +57 -0
- package/dist/internal/command-guards.d.ts.map +1 -0
- package/dist/internal/command-guards.js +81 -0
- package/dist/internal/command-guards.js.map +1 -0
- package/dist/internal/command-help.d.ts +15 -0
- package/dist/internal/command-help.d.ts.map +1 -0
- package/dist/internal/command-help.js +121 -0
- package/dist/internal/command-help.js.map +1 -0
- package/dist/internal/commands.d.ts +212 -0
- package/dist/internal/commands.d.ts.map +1 -0
- package/dist/internal/commands.js +1004 -0
- package/dist/internal/commands.js.map +1 -0
- package/dist/internal/configuration.d.ts +86 -0
- package/dist/internal/configuration.d.ts.map +1 -0
- package/dist/internal/configuration.js +401 -0
- package/dist/internal/configuration.js.map +1 -0
- package/dist/internal/counts.d.ts +33 -0
- package/dist/internal/counts.d.ts.map +1 -0
- package/dist/internal/counts.js +348 -0
- package/dist/internal/counts.js.map +1 -0
- package/dist/internal/decode/primitives.d.ts +30 -0
- package/dist/internal/decode/primitives.d.ts.map +1 -0
- package/dist/internal/decode/primitives.js +56 -0
- package/dist/internal/decode/primitives.js.map +1 -0
- package/dist/internal/decode/timestamp.d.ts +13 -0
- package/dist/internal/decode/timestamp.d.ts.map +1 -0
- package/dist/internal/decode/timestamp.js +27 -0
- package/dist/internal/decode/timestamp.js.map +1 -0
- package/dist/internal/decode/trace.d.ts +11 -0
- package/dist/internal/decode/trace.d.ts.map +1 -0
- package/dist/internal/decode/trace.js +69 -0
- package/dist/internal/decode/trace.js.map +1 -0
- package/dist/internal/defects.d.ts +48 -0
- package/dist/internal/defects.d.ts.map +1 -0
- package/dist/internal/defects.js +100 -0
- package/dist/internal/defects.js.map +1 -0
- package/dist/internal/effect-failures.d.ts +12 -0
- package/dist/internal/effect-failures.d.ts.map +1 -0
- package/dist/internal/effect-failures.js +45 -0
- package/dist/internal/effect-failures.js.map +1 -0
- package/dist/internal/embeds.d.ts +12 -0
- package/dist/internal/embeds.d.ts.map +1 -0
- package/dist/internal/embeds.js +308 -0
- package/dist/internal/embeds.js.map +1 -0
- package/dist/internal/events.d.ts +156 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +1071 -0
- package/dist/internal/events.js.map +1 -0
- package/dist/internal/expiry-queue.d.ts +42 -0
- package/dist/internal/expiry-queue.d.ts.map +1 -0
- package/dist/internal/expiry-queue.js +138 -0
- package/dist/internal/expiry-queue.js.map +1 -0
- package/dist/internal/expressions.d.ts +35 -0
- package/dist/internal/expressions.d.ts.map +1 -0
- package/dist/internal/expressions.js +311 -0
- package/dist/internal/expressions.js.map +1 -0
- package/dist/internal/failures.d.ts +107 -0
- package/dist/internal/failures.d.ts.map +1 -0
- package/dist/internal/failures.js +416 -0
- package/dist/internal/failures.js.map +1 -0
- package/dist/internal/field-text.d.ts +10 -0
- package/dist/internal/field-text.d.ts.map +1 -0
- package/dist/internal/field-text.js +37 -0
- package/dist/internal/field-text.js.map +1 -0
- package/dist/internal/gateway/commands.d.ts +92 -0
- package/dist/internal/gateway/commands.d.ts.map +1 -0
- package/dist/internal/gateway/commands.js +223 -0
- package/dist/internal/gateway/commands.js.map +1 -0
- package/dist/internal/gateway/dispatch-table.d.ts +42 -0
- package/dist/internal/gateway/dispatch-table.d.ts.map +1 -0
- package/dist/internal/gateway/dispatch-table.js +288 -0
- package/dist/internal/gateway/dispatch-table.js.map +1 -0
- package/dist/internal/gateway/event-dispatches.d.ts +97 -0
- package/dist/internal/gateway/event-dispatches.d.ts.map +1 -0
- package/dist/internal/gateway/event-dispatches.js +172 -0
- package/dist/internal/gateway/event-dispatches.js.map +1 -0
- package/dist/internal/gateway/frames.d.ts +45 -0
- package/dist/internal/gateway/frames.d.ts.map +1 -0
- package/dist/internal/gateway/frames.js +121 -0
- package/dist/internal/gateway/frames.js.map +1 -0
- package/dist/internal/gateway/options.d.ts +24 -0
- package/dist/internal/gateway/options.d.ts.map +1 -0
- package/dist/internal/gateway/options.js +103 -0
- package/dist/internal/gateway/options.js.map +1 -0
- package/dist/internal/gateway/session.d.ts +104 -0
- package/dist/internal/gateway/session.d.ts.map +1 -0
- package/dist/internal/gateway/session.js +420 -0
- package/dist/internal/gateway/session.js.map +1 -0
- package/dist/internal/gateway-requests.d.ts +10 -0
- package/dist/internal/gateway-requests.d.ts.map +1 -0
- package/dist/internal/gateway-requests.js +26 -0
- package/dist/internal/gateway-requests.js.map +1 -0
- package/dist/internal/gateway.d.ts +19 -0
- package/dist/internal/gateway.d.ts.map +1 -0
- package/dist/internal/gateway.js +11 -0
- package/dist/internal/gateway.js.map +1 -0
- package/dist/internal/guild-cache.d.ts +69 -0
- package/dist/internal/guild-cache.d.ts.map +1 -0
- package/dist/internal/guild-cache.js +402 -0
- package/dist/internal/guild-cache.js.map +1 -0
- package/dist/internal/guild-discovery.d.ts +15 -0
- package/dist/internal/guild-discovery.d.ts.map +1 -0
- package/dist/internal/guild-discovery.js +258 -0
- package/dist/internal/guild-discovery.js.map +1 -0
- package/dist/internal/guild-lifecycle.d.ts +18 -0
- package/dist/internal/guild-lifecycle.d.ts.map +1 -0
- package/dist/internal/guild-lifecycle.js +119 -0
- package/dist/internal/guild-lifecycle.js.map +1 -0
- package/dist/internal/guild-settings.d.ts +11 -0
- package/dist/internal/guild-settings.d.ts.map +1 -0
- package/dist/internal/guild-settings.js +150 -0
- package/dist/internal/guild-settings.js.map +1 -0
- package/dist/internal/guilds.d.ts +94 -0
- package/dist/internal/guilds.d.ts.map +1 -0
- package/dist/internal/guilds.js +785 -0
- package/dist/internal/guilds.js.map +1 -0
- package/dist/internal/init-command.d.ts +54 -0
- package/dist/internal/init-command.d.ts.map +1 -0
- package/dist/internal/init-command.js +248 -0
- package/dist/internal/init-command.js.map +1 -0
- package/dist/internal/instance.d.ts +69 -0
- package/dist/internal/instance.d.ts.map +1 -0
- package/dist/internal/instance.js +455 -0
- package/dist/internal/instance.js.map +1 -0
- package/dist/internal/invalid-callback-return.d.ts +3 -0
- package/dist/internal/invalid-callback-return.d.ts.map +1 -0
- package/dist/internal/invalid-callback-return.js +26 -0
- package/dist/internal/invalid-callback-return.js.map +1 -0
- package/dist/internal/invites.d.ts +19 -0
- package/dist/internal/invites.d.ts.map +1 -0
- package/dist/internal/invites.js +182 -0
- package/dist/internal/invites.js.map +1 -0
- package/dist/internal/logging.d.ts +129 -0
- package/dist/internal/logging.d.ts.map +1 -0
- package/dist/internal/logging.js +868 -0
- package/dist/internal/logging.js.map +1 -0
- package/dist/internal/logical-scheduler.d.ts +27 -0
- package/dist/internal/logical-scheduler.d.ts.map +1 -0
- package/dist/internal/logical-scheduler.js +102 -0
- package/dist/internal/logical-scheduler.js.map +1 -0
- package/dist/internal/masking.d.ts +13 -0
- package/dist/internal/masking.d.ts.map +1 -0
- package/dist/internal/masking.js +235 -0
- package/dist/internal/masking.js.map +1 -0
- package/dist/internal/member-chunks.d.ts +96 -0
- package/dist/internal/member-chunks.d.ts.map +1 -0
- package/dist/internal/member-chunks.js +557 -0
- package/dist/internal/member-chunks.js.map +1 -0
- package/dist/internal/member-search-workflow.d.ts +19 -0
- package/dist/internal/member-search-workflow.d.ts.map +1 -0
- package/dist/internal/member-search-workflow.js +143 -0
- package/dist/internal/member-search-workflow.js.map +1 -0
- package/dist/internal/member-search.d.ts +20 -0
- package/dist/internal/member-search.d.ts.map +1 -0
- package/dist/internal/member-search.js +201 -0
- package/dist/internal/member-search.js.map +1 -0
- package/dist/internal/message-cleanup.d.ts +9 -0
- package/dist/internal/message-cleanup.d.ts.map +1 -0
- package/dist/internal/message-cleanup.js +368 -0
- package/dist/internal/message-cleanup.js.map +1 -0
- package/dist/internal/message-fields.d.ts +9 -0
- package/dist/internal/message-fields.d.ts.map +1 -0
- package/dist/internal/message-fields.js +55 -0
- package/dist/internal/message-fields.js.map +1 -0
- package/dist/internal/message-search-workflow.d.ts +14 -0
- package/dist/internal/message-search-workflow.d.ts.map +1 -0
- package/dist/internal/message-search-workflow.js +101 -0
- package/dist/internal/message-search-workflow.js.map +1 -0
- package/dist/internal/message-search.d.ts +19 -0
- package/dist/internal/message-search.d.ts.map +1 -0
- package/dist/internal/message-search.js +317 -0
- package/dist/internal/message-search.js.map +1 -0
- package/dist/internal/message.d.ts +34 -0
- package/dist/internal/message.d.ts.map +1 -0
- package/dist/internal/message.js +723 -0
- package/dist/internal/message.js.map +1 -0
- package/dist/internal/metrics.d.ts +16 -0
- package/dist/internal/metrics.d.ts.map +1 -0
- package/dist/internal/metrics.js +49 -0
- package/dist/internal/metrics.js.map +1 -0
- package/dist/internal/moderation.d.ts +20 -0
- package/dist/internal/moderation.d.ts.map +1 -0
- package/dist/internal/moderation.js +219 -0
- package/dist/internal/moderation.js.map +1 -0
- package/dist/internal/multipart.d.ts +10 -0
- package/dist/internal/multipart.d.ts.map +1 -0
- package/dist/internal/multipart.js +156 -0
- package/dist/internal/multipart.js.map +1 -0
- package/dist/internal/node-version.d.ts +10 -0
- package/dist/internal/node-version.d.ts.map +1 -0
- package/dist/internal/node-version.js +21 -0
- package/dist/internal/node-version.js.map +1 -0
- package/dist/internal/oauth.d.ts +29 -0
- package/dist/internal/oauth.d.ts.map +1 -0
- package/dist/internal/oauth.js +721 -0
- package/dist/internal/oauth.js.map +1 -0
- package/dist/internal/observer.d.ts +21 -0
- package/dist/internal/observer.d.ts.map +1 -0
- package/dist/internal/observer.js +48 -0
- package/dist/internal/observer.js.map +1 -0
- package/dist/internal/operation-signal.d.ts +9 -0
- package/dist/internal/operation-signal.d.ts.map +1 -0
- package/dist/internal/operation-signal.js +19 -0
- package/dist/internal/operation-signal.js.map +1 -0
- package/dist/internal/pagination.d.ts +67 -0
- package/dist/internal/pagination.d.ts.map +1 -0
- package/dist/internal/pagination.js +306 -0
- package/dist/internal/pagination.js.map +1 -0
- package/dist/internal/permission-requirements.d.ts +34 -0
- package/dist/internal/permission-requirements.d.ts.map +1 -0
- package/dist/internal/permission-requirements.js +204 -0
- package/dist/internal/permission-requirements.js.map +1 -0
- package/dist/internal/permissions.d.ts +28 -0
- package/dist/internal/permissions.d.ts.map +1 -0
- package/dist/internal/permissions.js +314 -0
- package/dist/internal/permissions.js.map +1 -0
- package/dist/internal/pins.d.ts +24 -0
- package/dist/internal/pins.d.ts.map +1 -0
- package/dist/internal/pins.js +83 -0
- package/dist/internal/pins.js.map +1 -0
- package/dist/internal/presence.d.ts +101 -0
- package/dist/internal/presence.d.ts.map +1 -0
- package/dist/internal/presence.js +581 -0
- package/dist/internal/presence.js.map +1 -0
- package/dist/internal/protocol/gateway.d.ts +116 -0
- package/dist/internal/protocol/gateway.d.ts.map +1 -0
- package/dist/internal/protocol/gateway.js +258 -0
- package/dist/internal/protocol/gateway.js.map +1 -0
- package/dist/internal/rate-limit-templates.d.ts +2 -0
- package/dist/internal/rate-limit-templates.d.ts.map +1 -0
- package/dist/internal/rate-limit-templates.js +132 -0
- package/dist/internal/rate-limit-templates.js.map +1 -0
- package/dist/internal/rate-limits.d.ts +32 -0
- package/dist/internal/rate-limits.d.ts.map +1 -0
- package/dist/internal/rate-limits.js +306 -0
- package/dist/internal/rate-limits.js.map +1 -0
- package/dist/internal/reaction-collector.d.ts +39 -0
- package/dist/internal/reaction-collector.d.ts.map +1 -0
- package/dist/internal/reaction-collector.js +441 -0
- package/dist/internal/reaction-collector.js.map +1 -0
- package/dist/internal/reaction-pages.d.ts +18 -0
- package/dist/internal/reaction-pages.d.ts.map +1 -0
- package/dist/internal/reaction-pages.js +213 -0
- package/dist/internal/reaction-pages.js.map +1 -0
- package/dist/internal/reactions.d.ts +30 -0
- package/dist/internal/reactions.d.ts.map +1 -0
- package/dist/internal/reactions.js +160 -0
- package/dist/internal/reactions.js.map +1 -0
- package/dist/internal/rejection-scope.d.ts +25 -0
- package/dist/internal/rejection-scope.d.ts.map +1 -0
- package/dist/internal/rejection-scope.js +103 -0
- package/dist/internal/rejection-scope.js.map +1 -0
- package/dist/internal/response-json.d.ts +12 -0
- package/dist/internal/response-json.d.ts.map +1 -0
- package/dist/internal/response-json.js +74 -0
- package/dist/internal/response-json.js.map +1 -0
- package/dist/internal/rest/admission.d.ts +77 -0
- package/dist/internal/rest/admission.d.ts.map +1 -0
- package/dist/internal/rest/admission.js +178 -0
- package/dist/internal/rest/admission.js.map +1 -0
- package/dist/internal/rest/attempt.d.ts +124 -0
- package/dist/internal/rest/attempt.d.ts.map +1 -0
- package/dist/internal/rest/attempt.js +410 -0
- package/dist/internal/rest/attempt.js.map +1 -0
- package/dist/internal/rest/classify.d.ts +93 -0
- package/dist/internal/rest/classify.d.ts.map +1 -0
- package/dist/internal/rest/classify.js +304 -0
- package/dist/internal/rest/classify.js.map +1 -0
- package/dist/internal/rest/download.d.ts +66 -0
- package/dist/internal/rest/download.d.ts.map +1 -0
- package/dist/internal/rest/download.js +552 -0
- package/dist/internal/rest/download.js.map +1 -0
- package/dist/internal/rest/options.d.ts +42 -0
- package/dist/internal/rest/options.d.ts.map +1 -0
- package/dist/internal/rest/options.js +99 -0
- package/dist/internal/rest/options.js.map +1 -0
- package/dist/internal/rest/owner.d.ts +154 -0
- package/dist/internal/rest/owner.d.ts.map +1 -0
- package/dist/internal/rest/owner.js +1292 -0
- package/dist/internal/rest/owner.js.map +1 -0
- package/dist/internal/rest/request.d.ts +31 -0
- package/dist/internal/rest/request.d.ts.map +1 -0
- package/dist/internal/rest/request.js +227 -0
- package/dist/internal/rest/request.js.map +1 -0
- package/dist/internal/rest.d.ts +10 -0
- package/dist/internal/rest.d.ts.map +1 -0
- package/dist/internal/rest.js +9 -0
- package/dist/internal/rest.js.map +1 -0
- package/dist/internal/retention-age.d.ts +11 -0
- package/dist/internal/retention-age.d.ts.map +1 -0
- package/dist/internal/retention-age.js +29 -0
- package/dist/internal/retention-age.js.map +1 -0
- package/dist/internal/role-hierarchy-workflow.d.ts +15 -0
- package/dist/internal/role-hierarchy-workflow.d.ts.map +1 -0
- package/dist/internal/role-hierarchy-workflow.js +122 -0
- package/dist/internal/role-hierarchy-workflow.js.map +1 -0
- package/dist/internal/role-hierarchy.d.ts +11 -0
- package/dist/internal/role-hierarchy.d.ts.map +1 -0
- package/dist/internal/role-hierarchy.js +87 -0
- package/dist/internal/role-hierarchy.js.map +1 -0
- package/dist/internal/scheduled-tasks.d.ts +46 -0
- package/dist/internal/scheduled-tasks.d.ts.map +1 -0
- package/dist/internal/scheduled-tasks.js +190 -0
- package/dist/internal/scheduled-tasks.js.map +1 -0
- package/dist/internal/sharding.d.ts +62 -0
- package/dist/internal/sharding.d.ts.map +1 -0
- package/dist/internal/sharding.js +205 -0
- package/dist/internal/sharding.js.map +1 -0
- package/dist/internal/starter-project.d.ts +27 -0
- package/dist/internal/starter-project.d.ts.map +1 -0
- package/dist/internal/starter-project.js +224 -0
- package/dist/internal/starter-project.js.map +1 -0
- package/dist/internal/suggest.d.ts +12 -0
- package/dist/internal/suggest.d.ts.map +1 -0
- package/dist/internal/suggest.js +51 -0
- package/dist/internal/suggest.js.map +1 -0
- package/dist/internal/supervisor.d.ts +95 -0
- package/dist/internal/supervisor.d.ts.map +1 -0
- package/dist/internal/supervisor.js +1829 -0
- package/dist/internal/supervisor.js.map +1 -0
- package/dist/internal/testing/discovery.d.ts +24 -0
- package/dist/internal/testing/discovery.d.ts.map +1 -0
- package/dist/internal/testing/discovery.js +22 -0
- package/dist/internal/testing/discovery.js.map +1 -0
- package/dist/internal/testing/errors.d.ts +43 -0
- package/dist/internal/testing/errors.d.ts.map +1 -0
- package/dist/internal/testing/errors.js +63 -0
- package/dist/internal/testing/errors.js.map +1 -0
- package/dist/internal/testing/fixtures.d.ts +477 -0
- package/dist/internal/testing/fixtures.d.ts.map +1 -0
- package/dist/internal/testing/fixtures.js +233 -0
- package/dist/internal/testing/fixtures.js.map +1 -0
- package/dist/internal/testing/gateway.d.ts +51 -0
- package/dist/internal/testing/gateway.d.ts.map +1 -0
- package/dist/internal/testing/gateway.js +265 -0
- package/dist/internal/testing/gateway.js.map +1 -0
- package/dist/internal/testing/harness.d.ts +60 -0
- package/dist/internal/testing/harness.d.ts.map +1 -0
- package/dist/internal/testing/harness.js +280 -0
- package/dist/internal/testing/harness.js.map +1 -0
- package/dist/internal/testing/http.d.ts +31 -0
- package/dist/internal/testing/http.d.ts.map +1 -0
- package/dist/internal/testing/http.js +347 -0
- package/dist/internal/testing/http.js.map +1 -0
- package/dist/internal/testing/types.d.ts +222 -0
- package/dist/internal/testing/types.d.ts.map +1 -0
- package/dist/internal/testing/types.js +2 -0
- package/dist/internal/testing/types.js.map +1 -0
- package/dist/internal/testing/wait.d.ts +16 -0
- package/dist/internal/testing/wait.d.ts.map +1 -0
- package/dist/internal/testing/wait.js +55 -0
- package/dist/internal/testing/wait.js.map +1 -0
- package/dist/internal/thread-events.d.ts +40 -0
- package/dist/internal/thread-events.d.ts.map +1 -0
- package/dist/internal/thread-events.js +121 -0
- package/dist/internal/thread-events.js.map +1 -0
- package/dist/internal/threads.d.ts +47 -0
- package/dist/internal/threads.d.ts.map +1 -0
- package/dist/internal/threads.js +587 -0
- package/dist/internal/threads.js.map +1 -0
- package/dist/internal/transfer-source.d.ts +29 -0
- package/dist/internal/transfer-source.d.ts.map +1 -0
- package/dist/internal/transfer-source.js +347 -0
- package/dist/internal/transfer-source.js.map +1 -0
- package/dist/internal/transport/http.d.ts +12 -0
- package/dist/internal/transport/http.d.ts.map +1 -0
- package/dist/internal/transport/http.js +44 -0
- package/dist/internal/transport/http.js.map +1 -0
- package/dist/internal/transport/index.d.ts +21 -0
- package/dist/internal/transport/index.d.ts.map +1 -0
- package/dist/internal/transport/index.js +15 -0
- package/dist/internal/transport/index.js.map +1 -0
- package/dist/internal/transport/socket.d.ts +51 -0
- package/dist/internal/transport/socket.d.ts.map +1 -0
- package/dist/internal/transport/socket.js +27 -0
- package/dist/internal/transport/socket.js.map +1 -0
- package/dist/internal/transport/user-agent.d.ts +7 -0
- package/dist/internal/transport/user-agent.d.ts.map +1 -0
- package/dist/internal/transport/user-agent.js +42 -0
- package/dist/internal/transport/user-agent.js.map +1 -0
- package/dist/internal/uploads.d.ts +23 -0
- package/dist/internal/uploads.d.ts.map +1 -0
- package/dist/internal/uploads.js +89 -0
- package/dist/internal/uploads.js.map +1 -0
- package/dist/internal/user-cache.d.ts +50 -0
- package/dist/internal/user-cache.d.ts.map +1 -0
- package/dist/internal/user-cache.js +273 -0
- package/dist/internal/user-cache.js.map +1 -0
- package/dist/internal/users.d.ts +41 -0
- package/dist/internal/users.d.ts.map +1 -0
- package/dist/internal/users.js +333 -0
- package/dist/internal/users.js.map +1 -0
- package/dist/internal/vanity-url.d.ts +11 -0
- package/dist/internal/vanity-url.d.ts.map +1 -0
- package/dist/internal/vanity-url.js +58 -0
- package/dist/internal/vanity-url.js.map +1 -0
- package/dist/internal/webhooks.d.ts +58 -0
- package/dist/internal/webhooks.d.ts.map +1 -0
- package/dist/internal/webhooks.js +490 -0
- package/dist/internal/webhooks.js.map +1 -0
- package/dist/invites.d.ts +86 -0
- package/dist/invites.d.ts.map +1 -0
- package/dist/invites.js +2 -0
- package/dist/invites.js.map +1 -0
- package/dist/logging.d.ts +228 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +2 -0
- package/dist/logging.js.map +1 -0
- package/dist/member-chunks.d.ts +145 -0
- package/dist/member-chunks.d.ts.map +1 -0
- package/dist/member-chunks.js +68 -0
- package/dist/member-chunks.js.map +1 -0
- package/dist/member-search.d.ts +140 -0
- package/dist/member-search.d.ts.map +1 -0
- package/dist/member-search.js +14 -0
- package/dist/member-search.js.map +1 -0
- package/dist/message-cleanup.d.ts +227 -0
- package/dist/message-cleanup.d.ts.map +1 -0
- package/dist/message-cleanup.js +101 -0
- package/dist/message-cleanup.js.map +1 -0
- package/dist/message-errors.d.ts +172 -0
- package/dist/message-errors.d.ts.map +1 -0
- package/dist/message-errors.js +173 -0
- package/dist/message-errors.js.map +1 -0
- package/dist/message-search.d.ts +201 -0
- package/dist/message-search.d.ts.map +1 -0
- package/dist/message-search.js +2 -0
- package/dist/message-search.js.map +1 -0
- package/dist/messages.d.ts +694 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +37 -0
- package/dist/messages.js.map +1 -0
- package/dist/native-commands.d.ts +451 -0
- package/dist/native-commands.d.ts.map +1 -0
- package/dist/native-commands.js +432 -0
- package/dist/native-commands.js.map +1 -0
- package/dist/native-supervisor.d.ts +125 -0
- package/dist/native-supervisor.d.ts.map +1 -0
- package/dist/native-supervisor.js +126 -0
- package/dist/native-supervisor.js.map +1 -0
- package/dist/oauth.d.ts +298 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +128 -0
- package/dist/oauth.js.map +1 -0
- package/dist/observer.d.ts +111 -0
- package/dist/observer.d.ts.map +1 -0
- package/dist/observer.js +2 -0
- package/dist/observer.js.map +1 -0
- package/dist/pagination.d.ts +117 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +61 -0
- package/dist/pagination.js.map +1 -0
- package/dist/permissions.d.ts +73 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +2 -0
- package/dist/permissions.js.map +1 -0
- package/dist/pins.d.ts +62 -0
- package/dist/pins.d.ts.map +1 -0
- package/dist/pins.js +2 -0
- package/dist/pins.js.map +1 -0
- package/dist/presence.d.ts +84 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +46 -0
- package/dist/presence.js.map +1 -0
- package/dist/reaction-pages.d.ts +99 -0
- package/dist/reaction-pages.d.ts.map +1 -0
- package/dist/reaction-pages.js +2 -0
- package/dist/reaction-pages.js.map +1 -0
- package/dist/reactions.d.ts +118 -0
- package/dist/reactions.d.ts.map +1 -0
- package/dist/reactions.js +2 -0
- package/dist/reactions.js.map +1 -0
- package/dist/rest.d.ts +260 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +46 -0
- package/dist/rest.js.map +1 -0
- package/dist/role-hierarchy.d.ts +81 -0
- package/dist/role-hierarchy.d.ts.map +1 -0
- package/dist/role-hierarchy.js +53 -0
- package/dist/role-hierarchy.js.map +1 -0
- package/dist/sharding.d.ts +219 -0
- package/dist/sharding.d.ts.map +1 -0
- package/dist/sharding.js +86 -0
- package/dist/sharding.js.map +1 -0
- package/dist/supervisor.d.ts +392 -0
- package/dist/supervisor.d.ts.map +1 -0
- package/dist/supervisor.js +101 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/testing.d.ts +258 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +202 -0
- package/dist/testing.js.map +1 -0
- package/dist/text.d.ts +38 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +46 -0
- package/dist/text.js.map +1 -0
- package/dist/threads.d.ts +258 -0
- package/dist/threads.d.ts.map +1 -0
- package/dist/threads.js +2 -0
- package/dist/threads.js.map +1 -0
- package/dist/users.d.ts +204 -0
- package/dist/users.d.ts.map +1 -0
- package/dist/users.js +44 -0
- package/dist/users.js.map +1 -0
- package/dist/webhooks.d.ts +423 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +68 -0
- package/dist/webhooks.js.map +1 -0
- package/esm-only.cjs +3 -0
- package/examples/starter/bot-effect.ts +17 -0
- package/examples/starter/bot.js +13 -0
- package/examples/starter/bot.ts +13 -0
- package/package.json +82 -0
- package/src/api/default/application.ts +40 -0
- package/src/api/default/attachments.ts +106 -0
- package/src/api/default/audit-logs.ts +58 -0
- package/src/api/default/bot.ts +586 -0
- package/src/api/default/cache.ts +106 -0
- package/src/api/default/channels.ts +317 -0
- package/src/api/default/client.ts +829 -0
- package/src/api/default/collectors.ts +81 -0
- package/src/api/default/commands.ts +92 -0
- package/src/api/default/direct-messages.ts +143 -0
- package/src/api/default/discovery.ts +103 -0
- package/src/api/default/emojis.ts +125 -0
- package/src/api/default/events.ts +144 -0
- package/src/api/default/gateway.ts +67 -0
- package/src/api/default/guards.ts +173 -0
- package/src/api/default/guilds.ts +271 -0
- package/src/api/default/helpers.ts +97 -0
- package/src/api/default/instance.ts +49 -0
- package/src/api/default/invites.ts +73 -0
- package/src/api/default/logging.ts +37 -0
- package/src/api/default/members.ts +550 -0
- package/src/api/default/messages.ts +1053 -0
- package/src/api/default/oauth.ts +293 -0
- package/src/api/default/permissions.ts +47 -0
- package/src/api/default/presence.ts +88 -0
- package/src/api/default/rest.ts +30 -0
- package/src/api/default/results.ts +44 -0
- package/src/api/default/roles.ts +154 -0
- package/src/api/default/stickers.ts +129 -0
- package/src/api/default/supervisor.ts +53 -0
- package/src/api/default/threads.ts +233 -0
- package/src/api/default/users.ts +82 -0
- package/src/api/default/webhook-client.ts +266 -0
- package/src/api/default/webhooks.ts +89 -0
- package/src/api/effect/application.ts +29 -0
- package/src/api/effect/attachments.ts +100 -0
- package/src/api/effect/audit-logs.ts +50 -0
- package/src/api/effect/bot.ts +546 -0
- package/src/api/effect/cache.ts +110 -0
- package/src/api/effect/channels.ts +293 -0
- package/src/api/effect/client.ts +792 -0
- package/src/api/effect/collectors.ts +103 -0
- package/src/api/effect/commands.ts +87 -0
- package/src/api/effect/direct-messages.ts +119 -0
- package/src/api/effect/discovery.ts +87 -0
- package/src/api/effect/emojis.ts +108 -0
- package/src/api/effect/events.ts +92 -0
- package/src/api/effect/failures.ts +14 -0
- package/src/api/effect/gateway.ts +63 -0
- package/src/api/effect/guards.ts +194 -0
- package/src/api/effect/guilds.ts +247 -0
- package/src/api/effect/helpers.ts +359 -0
- package/src/api/effect/hierarchy.ts +26 -0
- package/src/api/effect/instance.ts +50 -0
- package/src/api/effect/invites.ts +59 -0
- package/src/api/effect/logging.ts +37 -0
- package/src/api/effect/members.ts +521 -0
- package/src/api/effect/messages.ts +1003 -0
- package/src/api/effect/oauth.ts +208 -0
- package/src/api/effect/permissions.ts +44 -0
- package/src/api/effect/presence.ts +83 -0
- package/src/api/effect/rest.ts +27 -0
- package/src/api/effect/roles.ts +136 -0
- package/src/api/effect/service.ts +104 -0
- package/src/api/effect/stickers.ts +112 -0
- package/src/api/effect/supervisor.ts +58 -0
- package/src/api/effect/threads.ts +221 -0
- package/src/api/effect/users.ts +66 -0
- package/src/api/effect/webhook-client.ts +206 -0
- package/src/api/effect/webhooks.ts +75 -0
- package/src/api-errors.ts +985 -0
- package/src/application.ts +103 -0
- package/src/assets.ts +731 -0
- package/src/attachments.ts +469 -0
- package/src/audit-logs.ts +355 -0
- package/src/bot-runner.ts +91 -0
- package/src/builders.ts +390 -0
- package/src/cache.ts +190 -0
- package/src/channels.ts +1051 -0
- package/src/cli.ts +60 -0
- package/src/client.ts +663 -0
- package/src/collectors.ts +248 -0
- package/src/colors.ts +91 -0
- package/src/command-arguments.ts +432 -0
- package/src/command-help.ts +55 -0
- package/src/commands.ts +403 -0
- package/src/counts.ts +160 -0
- package/src/default-commands.ts +1147 -0
- package/src/default-supervisor.ts +484 -0
- package/src/discovery.ts +241 -0
- package/src/effect-testing.ts +603 -0
- package/src/effect.ts +751 -0
- package/src/embeds.ts +253 -0
- package/src/error-tools.ts +191 -0
- package/src/errors.ts +849 -0
- package/src/events.ts +830 -0
- package/src/expressions.ts +198 -0
- package/src/failures.ts +55 -0
- package/src/gateway.ts +86 -0
- package/src/guilds.ts +1108 -0
- package/src/helpers.ts +836 -0
- package/src/index.ts +796 -0
- package/src/input-validation.ts +94 -0
- package/src/instance.ts +111 -0
- package/src/internal/agents-section.ts +114 -0
- package/src/internal/application.ts +51 -0
- package/src/internal/attachment-refresh.ts +57 -0
- package/src/internal/attachments.ts +313 -0
- package/src/internal/audit-logs.ts +336 -0
- package/src/internal/audit.ts +33 -0
- package/src/internal/binding/default-adapters.ts +482 -0
- package/src/internal/binding/default.ts +155 -0
- package/src/internal/binding/execute.ts +221 -0
- package/src/internal/binding/native-adapters.ts +199 -0
- package/src/internal/binding/native.ts +72 -0
- package/src/internal/binding/operations.ts +1171 -0
- package/src/internal/bot-runner.ts +480 -0
- package/src/internal/cache-changes.ts +363 -0
- package/src/internal/cache.ts +323 -0
- package/src/internal/calls.ts +119 -0
- package/src/internal/channel-cache.ts +435 -0
- package/src/internal/channel-decode.ts +399 -0
- package/src/internal/channels.ts +874 -0
- package/src/internal/client/backoff.ts +69 -0
- package/src/internal/client/configuration-fields.ts +18 -0
- package/src/internal/client/drain.ts +150 -0
- package/src/internal/client/intake.ts +228 -0
- package/src/internal/client/lifetime.ts +165 -0
- package/src/internal/client/shard-loop.ts +586 -0
- package/src/internal/client-registry.ts +35 -0
- package/src/internal/client.ts +2448 -0
- package/src/internal/clock.ts +11 -0
- package/src/internal/code-catalogue.ts +739 -0
- package/src/internal/collector.ts +452 -0
- package/src/internal/command-arguments.ts +585 -0
- package/src/internal/command-guards.ts +96 -0
- package/src/internal/command-help.ts +156 -0
- package/src/internal/commands.ts +1319 -0
- package/src/internal/configuration.ts +529 -0
- package/src/internal/counts.ts +471 -0
- package/src/internal/decode/primitives.ts +67 -0
- package/src/internal/decode/timestamp.ts +29 -0
- package/src/internal/decode/trace.ts +65 -0
- package/src/internal/defects.ts +115 -0
- package/src/internal/effect-failures.ts +65 -0
- package/src/internal/embeds.ts +355 -0
- package/src/internal/events.ts +1345 -0
- package/src/internal/expiry-queue.ts +148 -0
- package/src/internal/expressions.ts +415 -0
- package/src/internal/failures.ts +492 -0
- package/src/internal/field-text.ts +40 -0
- package/src/internal/gateway/commands.ts +328 -0
- package/src/internal/gateway/dispatch-table.ts +383 -0
- package/src/internal/gateway/event-dispatches.ts +201 -0
- package/src/internal/gateway/frames.ts +176 -0
- package/src/internal/gateway/options.ts +144 -0
- package/src/internal/gateway/session.ts +625 -0
- package/src/internal/gateway-requests.ts +26 -0
- package/src/internal/gateway.ts +22 -0
- package/src/internal/guild-cache.ts +424 -0
- package/src/internal/guild-discovery.ts +332 -0
- package/src/internal/guild-lifecycle.ts +128 -0
- package/src/internal/guild-settings.ts +209 -0
- package/src/internal/guilds.ts +949 -0
- package/src/internal/init-command.ts +305 -0
- package/src/internal/instance.ts +645 -0
- package/src/internal/invalid-callback-return.ts +28 -0
- package/src/internal/invites.ts +234 -0
- package/src/internal/logging.ts +1007 -0
- package/src/internal/logical-scheduler.ts +139 -0
- package/src/internal/masking.ts +224 -0
- package/src/internal/member-chunks.ts +649 -0
- package/src/internal/member-search-workflow.ts +237 -0
- package/src/internal/member-search.ts +272 -0
- package/src/internal/message-cleanup.ts +515 -0
- package/src/internal/message-fields.ts +71 -0
- package/src/internal/message-search-workflow.ts +166 -0
- package/src/internal/message-search.ts +476 -0
- package/src/internal/message.ts +879 -0
- package/src/internal/metrics.ts +55 -0
- package/src/internal/moderation.ts +288 -0
- package/src/internal/multipart.ts +171 -0
- package/src/internal/node-version.ts +22 -0
- package/src/internal/oauth.ts +1146 -0
- package/src/internal/observer.ts +62 -0
- package/src/internal/operation-signal.ts +26 -0
- package/src/internal/pagination.ts +494 -0
- package/src/internal/permission-requirements.ts +359 -0
- package/src/internal/permissions.ts +417 -0
- package/src/internal/pins.ts +115 -0
- package/src/internal/presence.ts +745 -0
- package/src/internal/protocol/gateway.ts +299 -0
- package/src/internal/rate-limit-templates.ts +138 -0
- package/src/internal/rate-limits.ts +346 -0
- package/src/internal/reaction-collector.ts +499 -0
- package/src/internal/reaction-pages.ts +260 -0
- package/src/internal/reactions.ts +174 -0
- package/src/internal/rejection-scope.ts +119 -0
- package/src/internal/response-json.ts +76 -0
- package/src/internal/rest/admission.ts +210 -0
- package/src/internal/rest/attempt.ts +617 -0
- package/src/internal/rest/classify.ts +391 -0
- package/src/internal/rest/download.ts +658 -0
- package/src/internal/rest/options.ts +145 -0
- package/src/internal/rest/owner.ts +1828 -0
- package/src/internal/rest/request.ts +289 -0
- package/src/internal/rest.ts +9 -0
- package/src/internal/retention-age.ts +33 -0
- package/src/internal/role-hierarchy-workflow.ts +150 -0
- package/src/internal/role-hierarchy.ts +93 -0
- package/src/internal/scheduled-tasks.ts +217 -0
- package/src/internal/sharding.ts +270 -0
- package/src/internal/starter-project.ts +251 -0
- package/src/internal/suggest.ts +50 -0
- package/src/internal/supervisor.ts +2253 -0
- package/src/internal/testing/discovery.ts +23 -0
- package/src/internal/testing/errors.ts +67 -0
- package/src/internal/testing/fixtures.ts +714 -0
- package/src/internal/testing/gateway.ts +305 -0
- package/src/internal/testing/harness.ts +328 -0
- package/src/internal/testing/http.ts +395 -0
- package/src/internal/testing/types.ts +240 -0
- package/src/internal/testing/wait.ts +63 -0
- package/src/internal/thread-events.ts +128 -0
- package/src/internal/threads.ts +718 -0
- package/src/internal/transfer-source.ts +359 -0
- package/src/internal/transport/http.ts +46 -0
- package/src/internal/transport/index.ts +23 -0
- package/src/internal/transport/socket.ts +71 -0
- package/src/internal/transport/user-agent.ts +46 -0
- package/src/internal/uploads.ts +111 -0
- package/src/internal/user-cache.ts +294 -0
- package/src/internal/users.ts +402 -0
- package/src/internal/vanity-url.ts +80 -0
- package/src/internal/webhooks.ts +690 -0
- package/src/invites.ts +87 -0
- package/src/logging.ts +247 -0
- package/src/member-chunks.ts +195 -0
- package/src/member-search.ts +152 -0
- package/src/message-cleanup.ts +308 -0
- package/src/message-errors.ts +245 -0
- package/src/message-search.ts +213 -0
- package/src/messages.ts +768 -0
- package/src/native-commands.ts +1249 -0
- package/src/native-supervisor.ts +345 -0
- package/src/oauth.ts +413 -0
- package/src/observer.ts +118 -0
- package/src/pagination.ts +172 -0
- package/src/permissions.ts +74 -0
- package/src/pins.ts +65 -0
- package/src/presence.ts +124 -0
- package/src/reaction-pages.ts +106 -0
- package/src/reactions.ts +126 -0
- package/src/rest.ts +282 -0
- package/src/role-hierarchy.ts +127 -0
- package/src/sharding.ts +272 -0
- package/src/supervisor.ts +460 -0
- package/src/testing.ts +424 -0
- package/src/text.ts +76 -0
- package/src/threads.ts +274 -0
- package/src/users.ts +237 -0
- package/src/webhooks.ts +512 -0
|
@@ -0,0 +1,1319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefix-command registry, matching, cooldowns and dispatch over existing message subscriptions.
|
|
3
|
+
* Invariant: Commands use existing subscriptions without owning delivery, and every command failure is reported with the command name.
|
|
4
|
+
* Implements [SDK contracts: User-handler failures](/docs/SDK-CONTRACTS.md#user-handler-failures)
|
|
5
|
+
*/
|
|
6
|
+
import type {
|
|
7
|
+
CommandCooldownClaim,
|
|
8
|
+
CommandCooldownPer,
|
|
9
|
+
CommandCooldownRequest,
|
|
10
|
+
MemoryCooldownOptions,
|
|
11
|
+
PrefixCommandDefinition,
|
|
12
|
+
PrefixCommandGroupDefinition,
|
|
13
|
+
PrefixCommandGroupMetadata,
|
|
14
|
+
PrefixCommandMetadata,
|
|
15
|
+
PrefixCommandParse,
|
|
16
|
+
PrefixCommandParseInput,
|
|
17
|
+
PrefixCommandParseRejection,
|
|
18
|
+
PrefixCommandParsing,
|
|
19
|
+
PrefixCommandRejection,
|
|
20
|
+
PrefixCommandRegistrationOptions,
|
|
21
|
+
PrefixCommandUnmatched,
|
|
22
|
+
} from "#sdk/commands"
|
|
23
|
+
import {
|
|
24
|
+
commandArgumentMetadata,
|
|
25
|
+
type CommandArgumentConversion,
|
|
26
|
+
snapshotCommandArguments,
|
|
27
|
+
} from "#sdk/internal/command-arguments"
|
|
28
|
+
import { ConfigurationError } from "#sdk/errors"
|
|
29
|
+
import { readCaller } from "./defects.js"
|
|
30
|
+
import { MessageType, type Message, type MessageCore } from "#sdk/messages"
|
|
31
|
+
import * as Cause from "effect/Cause"
|
|
32
|
+
import * as Clock from "effect/Clock"
|
|
33
|
+
import * as Effect from "effect/Effect"
|
|
34
|
+
import { nowMs } from "./clock.js"
|
|
35
|
+
import type { ClientLogger } from "./logging.js"
|
|
36
|
+
import { editDistance, unsupportedKeyHint } from "./suggest.js"
|
|
37
|
+
import { identifyCommand } from "./events.js"
|
|
38
|
+
|
|
39
|
+
const commandName = /^[A-Za-z0-9][A-Za-z0-9_-]*$/
|
|
40
|
+
const maxDurationMs = 2_147_483_647
|
|
41
|
+
const emptyCommandMetadata: readonly PrefixCommandMetadata[] = Object.freeze([])
|
|
42
|
+
/** Default key limit of a memory cooldown store, including each router's own store */
|
|
43
|
+
const defaultCooldownEntries = 10_000
|
|
44
|
+
/** How long `onReject: "reply"` stays quiet for one user and command after answering a guard denial */
|
|
45
|
+
const guardFeedbackWindowMs = 5_000
|
|
46
|
+
|
|
47
|
+
export interface PrefixCommandMatch<D extends PrefixCommandDefinition> {
|
|
48
|
+
readonly definition: D
|
|
49
|
+
readonly prefix: string
|
|
50
|
+
readonly parse: PrefixCommandParse
|
|
51
|
+
readonly path?: readonly string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Prefix and frozen unmatched result retained only while an attached router dispatches one message */
|
|
55
|
+
export interface PrefixCommandUnmatchedMatch {
|
|
56
|
+
readonly prefix: string
|
|
57
|
+
readonly unmatched: PrefixCommandUnmatched
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Validated router settings shared by both adapters. The prefix may be a resolver that each adapter runs its own way */
|
|
61
|
+
export interface RouterSettings<M extends MessageCore> extends PrefixCommandParsing<M> {
|
|
62
|
+
readonly prefix: string | readonly string[] | ((message: M) => unknown)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Shared immutable command lookup state. Entry-point adapters own callback and error boundaries */
|
|
66
|
+
export class PrefixCommandRegistry<D extends PrefixCommandDefinition, M extends MessageCore = Message> {
|
|
67
|
+
readonly #definitions: ReadonlyMap<string, D>
|
|
68
|
+
readonly #commands: readonly PrefixCommandMetadata[]
|
|
69
|
+
readonly #groups: readonly PrefixCommandGroupMetadata[]
|
|
70
|
+
readonly #groupLookup: ReadonlyMap<string, PrefixCommandGroupMetadata>
|
|
71
|
+
readonly #entries: readonly PrefixCommandMetadata[]
|
|
72
|
+
readonly #options: Readonly<RouterSettings<M>>
|
|
73
|
+
#visibleEntries: readonly PrefixCommandMetadata[] | undefined
|
|
74
|
+
/** The router-owned cooldown store for commands that do not supply their own, shared by every router derived from one create */
|
|
75
|
+
readonly cooldowns: LocalMemoryCooldownStore
|
|
76
|
+
/** Keys whose `onReject: "reply"` feedback was already sent, shared like the cooldown store and bounded by the same limit */
|
|
77
|
+
readonly feedback: ExpiringKeys
|
|
78
|
+
|
|
79
|
+
constructor(
|
|
80
|
+
options: unknown,
|
|
81
|
+
adapterKeys: readonly string[],
|
|
82
|
+
definitions: ReadonlyMap<string, D> = new Map(),
|
|
83
|
+
commands: readonly PrefixCommandMetadata[] = emptyCommandMetadata,
|
|
84
|
+
groups: readonly PrefixCommandGroupMetadata[] = Object.freeze([]),
|
|
85
|
+
groupLookup: ReadonlyMap<string, PrefixCommandGroupMetadata> = new Map(),
|
|
86
|
+
entries: readonly PrefixCommandMetadata[] = commands,
|
|
87
|
+
shared?: { readonly cooldowns: LocalMemoryCooldownStore; readonly feedback: ExpiringKeys },
|
|
88
|
+
) {
|
|
89
|
+
// Only the reads of the caller options are marked as application input, not building the registry
|
|
90
|
+
this.#options = readCaller(() => copyOptions<M>(options, adapterKeys))
|
|
91
|
+
this.#definitions = definitions
|
|
92
|
+
this.#commands = commands
|
|
93
|
+
this.#groups = groups
|
|
94
|
+
this.#groupLookup = groupLookup
|
|
95
|
+
this.#entries = entries
|
|
96
|
+
// Read the router-level cooldown options once, after the option shape has been validated
|
|
97
|
+
this.cooldowns =
|
|
98
|
+
shared?.cooldowns ??
|
|
99
|
+
createMemoryCooldownStore(readCaller(() => (options as { readonly cooldowns?: unknown }).cooldowns))
|
|
100
|
+
this.feedback = shared?.feedback ?? new ExpiringKeys(this.cooldowns.maxEntries)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The configured prefix or resolver, for the adapter that runs a resolver in its own way */
|
|
104
|
+
get prefix(): RouterSettings<M>["prefix"] {
|
|
105
|
+
return this.#options.prefix
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Whether a mention of the bot also acts as a prefix */
|
|
109
|
+
get mentionPrefix(): boolean {
|
|
110
|
+
return this.#options.mentionPrefix === true
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Whether the router skips a message before any prefix work: One from a bot unless `ignoreBots` is false, or one a person did not type.
|
|
115
|
+
* Only Default and Reply messages carry typed text. Every other type is a notice Fluxer posts itself, such as a new thread's name, so an allow-list keeps future types out too.
|
|
116
|
+
* Fluxer always sends a type, so a message without one, such as a hand-written test payload, counts as typed rather than being dropped silently
|
|
117
|
+
*/
|
|
118
|
+
ignores(message: M): boolean {
|
|
119
|
+
const { type } = message
|
|
120
|
+
if (type !== undefined && type !== MessageType.Default && type !== MessageType.Reply) return true
|
|
121
|
+
return this.#options.ignoreBots !== false && message.author.isBot === true
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private derive(
|
|
125
|
+
definitions: ReadonlyMap<string, D>,
|
|
126
|
+
commands: readonly PrefixCommandMetadata[],
|
|
127
|
+
groups: readonly PrefixCommandGroupMetadata[],
|
|
128
|
+
groupLookup: ReadonlyMap<string, PrefixCommandGroupMetadata>,
|
|
129
|
+
entries: readonly PrefixCommandMetadata[],
|
|
130
|
+
): PrefixCommandRegistry<D, M> {
|
|
131
|
+
return new PrefixCommandRegistry<D, M>(
|
|
132
|
+
this.#options,
|
|
133
|
+
["prefix", "parse", "ignoreBots", "caseSensitive", "mentionPrefix"],
|
|
134
|
+
definitions,
|
|
135
|
+
commands,
|
|
136
|
+
groups,
|
|
137
|
+
groupLookup,
|
|
138
|
+
entries,
|
|
139
|
+
{ cooldowns: this.cooldowns, feedback: this.feedback },
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Immutable registration-order help metadata with no callbacks or local storage references */
|
|
144
|
+
get commands(): readonly PrefixCommandMetadata[] {
|
|
145
|
+
return this.#commands
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get groups(): readonly PrefixCommandGroupMetadata[] {
|
|
149
|
+
return this.#groups
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
get entries(): readonly PrefixCommandMetadata[] {
|
|
153
|
+
return this.#entries
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Commands and groups that help and unknown-name suggestions may reveal, in registration order.
|
|
158
|
+
* Leaves out entries registered with `hidden: true` and every entry inside a hidden group
|
|
159
|
+
*/
|
|
160
|
+
get visibleEntries(): readonly PrefixCommandMetadata[] {
|
|
161
|
+
if (this.#visibleEntries !== undefined) return this.#visibleEntries
|
|
162
|
+
const hiddenGroups = new Set(
|
|
163
|
+
this.#groups.filter((group) => group.hidden === true).map((group) => JSON.stringify(group.path)),
|
|
164
|
+
)
|
|
165
|
+
const visible = this.#entries.filter((entry) => {
|
|
166
|
+
if (entry.hidden === true) return false
|
|
167
|
+
const path = entry.path ?? [entry.name]
|
|
168
|
+
for (let depth = 1; depth < path.length; depth += 1)
|
|
169
|
+
if (hiddenGroups.has(JSON.stringify(path.slice(0, depth)))) return false
|
|
170
|
+
return true
|
|
171
|
+
})
|
|
172
|
+
this.#visibleEntries = visible.length === this.#entries.length ? this.#entries : Object.freeze(visible)
|
|
173
|
+
return this.#visibleEntries
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Retain an adapter-validated owned definition in a separate registry. Existing routers and subscriptions keep their previous definitions */
|
|
177
|
+
register(stored: D, options?: PrefixCommandRegistrationOptions): PrefixCommandRegistry<D, M> {
|
|
178
|
+
return this.registerAtParent(stored, this.registrationParent(options))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Register an already snapshotted batch into one new registry, leaving this registry unchanged if any entry fails */
|
|
182
|
+
registerMany(stored: readonly D[], options?: PrefixCommandRegistrationOptions): PrefixCommandRegistry<D, M> {
|
|
183
|
+
const parent = this.registrationParent(options)
|
|
184
|
+
let registry: PrefixCommandRegistry<D, M> = this
|
|
185
|
+
for (const definition of stored) registry = registry.registerAtParent(definition, parent)
|
|
186
|
+
return registry
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private registerAtParent(stored: D, parent: readonly string[]): PrefixCommandRegistry<D, M> {
|
|
190
|
+
const keys = this.registrationKeys(stored, parent)
|
|
191
|
+
const definitions = new Map(this.#definitions)
|
|
192
|
+
for (const key of keys) definitions.set(key, stored)
|
|
193
|
+
const metadata = commandMetadata(stored, parent)
|
|
194
|
+
return this.derive(
|
|
195
|
+
definitions,
|
|
196
|
+
Object.freeze([...this.#commands, metadata]),
|
|
197
|
+
this.#groups,
|
|
198
|
+
this.#groupLookup,
|
|
199
|
+
Object.freeze([...this.#entries, metadata]),
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
registerGroup(
|
|
204
|
+
value: PrefixCommandGroupDefinition,
|
|
205
|
+
options?: PrefixCommandRegistrationOptions,
|
|
206
|
+
): PrefixCommandRegistry<D, M> {
|
|
207
|
+
const definition = readCaller(() => {
|
|
208
|
+
validateCommandShape(value, ["name", "aliases", "description", "hidden"])
|
|
209
|
+
const aliases = value.aliases
|
|
210
|
+
const description = value.description
|
|
211
|
+
const hidden = value.hidden
|
|
212
|
+
return snapshotCommandDefinition({
|
|
213
|
+
name: value.name,
|
|
214
|
+
...(aliases === undefined ? {} : { aliases }),
|
|
215
|
+
...(description === undefined ? {} : { description }),
|
|
216
|
+
...(hidden === undefined ? {} : { hidden }),
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
const parent = this.registrationParent(options)
|
|
220
|
+
const keys = this.registrationKeys(definition, parent)
|
|
221
|
+
const metadata: PrefixCommandGroupMetadata = Object.freeze({
|
|
222
|
+
...definition,
|
|
223
|
+
kind: "group",
|
|
224
|
+
path: Object.freeze([...parent, definition.name]),
|
|
225
|
+
})
|
|
226
|
+
const lookup = new Map(this.#groupLookup)
|
|
227
|
+
for (const key of keys) lookup.set(key, metadata)
|
|
228
|
+
return this.derive(
|
|
229
|
+
this.#definitions,
|
|
230
|
+
this.#commands,
|
|
231
|
+
Object.freeze([...this.#groups, metadata]),
|
|
232
|
+
lookup,
|
|
233
|
+
Object.freeze([...this.#entries, metadata]),
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Match a message against the prefixes the adapter resolved for it, then consume groups and parse the command */
|
|
238
|
+
resolve(message: M, prefixes: unknown): PrefixCommandMatch<D> | PrefixCommandUnmatchedMatch | undefined {
|
|
239
|
+
if (this.ignores(message)) return undefined
|
|
240
|
+
const prefix = this.matchPrefix(message, prefixes)
|
|
241
|
+
if (prefix === undefined) return undefined
|
|
242
|
+
let source = message.content.slice(prefix.length)
|
|
243
|
+
let parent: readonly string[] = Object.freeze([])
|
|
244
|
+
for (;;) {
|
|
245
|
+
const suffix = source.trimStart()
|
|
246
|
+
const segment = /^(\S+)(?:\s+|$)/.exec(suffix)
|
|
247
|
+
const group = segment === null ? undefined : this.#groupLookup.get(this.lookupKey(parent, segment[1]!))
|
|
248
|
+
if (group === undefined) break
|
|
249
|
+
parent = group.path
|
|
250
|
+
source = suffix.slice(segment![0].length)
|
|
251
|
+
if (source.length === 0)
|
|
252
|
+
return Object.freeze({
|
|
253
|
+
prefix,
|
|
254
|
+
unmatched: Object.freeze({ _tag: "CommandMissingSubcommand", path: parent }),
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
const grouped = parent.length === 0 ? {} : { path: parent }
|
|
258
|
+
const input: PrefixCommandParseInput<M> = Object.freeze({
|
|
259
|
+
message,
|
|
260
|
+
prefix,
|
|
261
|
+
source,
|
|
262
|
+
...grouped,
|
|
263
|
+
})
|
|
264
|
+
const parsed = this.#options.parse === undefined ? defaultParse(input) : this.#options.parse(input)
|
|
265
|
+
if (parsed === undefined || isParseRejection(parsed)) {
|
|
266
|
+
const reason = parsed === undefined ? undefined : copyParseRejection(parsed)
|
|
267
|
+
return Object.freeze({
|
|
268
|
+
prefix,
|
|
269
|
+
unmatched: Object.freeze({
|
|
270
|
+
_tag: "CommandParserRejected",
|
|
271
|
+
...(reason === undefined ? {} : { reason }),
|
|
272
|
+
...grouped,
|
|
273
|
+
}),
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
const parse = copyParse(parsed)
|
|
277
|
+
const definition = this.#definitions.get(this.lookupKey(parent, parse.name))
|
|
278
|
+
if (definition === undefined) {
|
|
279
|
+
const suggestion = this.closestName(parent, parse.name)
|
|
280
|
+
return Object.freeze({
|
|
281
|
+
prefix,
|
|
282
|
+
unmatched: Object.freeze({
|
|
283
|
+
_tag: "CommandUnknownName",
|
|
284
|
+
name: parse.name,
|
|
285
|
+
...(suggestion === undefined ? {} : { suggestion }),
|
|
286
|
+
...grouped,
|
|
287
|
+
}),
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
return Object.freeze({
|
|
291
|
+
definition,
|
|
292
|
+
prefix,
|
|
293
|
+
parse,
|
|
294
|
+
...(parent.length === 0 ? {} : { path: Object.freeze([...parent, definition.name]) }),
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private registrationParent(options: PrefixCommandRegistrationOptions | undefined): readonly string[] {
|
|
299
|
+
if (options === undefined) return Object.freeze([])
|
|
300
|
+
const path = readCaller(() => {
|
|
301
|
+
validateObjectShape(options, ["group"], "command", "registration options")
|
|
302
|
+
const group = options.group
|
|
303
|
+
return group === undefined ? Object.freeze([]) : copyCommandGroupPath(group, "command")
|
|
304
|
+
})
|
|
305
|
+
if (path.length > 0 && !this.#groups.some((group) => sameCommandPath(group.path, path)))
|
|
306
|
+
throw new ConfigurationError(
|
|
307
|
+
"command",
|
|
308
|
+
'The registration option "group" must be the path of a registered group, using group names rather than aliases',
|
|
309
|
+
{ hint: 'Register the parent group first, then pass its full path, such as ["admin", "roles"]' },
|
|
310
|
+
)
|
|
311
|
+
return path
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private registrationKeys(value: PrefixCommandDefinition, parent: readonly string[]): readonly string[] {
|
|
315
|
+
const names = [value.name, ...(value.aliases ?? [])]
|
|
316
|
+
const keys = names.map((name) => this.lookupKey(parent, name))
|
|
317
|
+
if (new Set(keys).size !== keys.length)
|
|
318
|
+
throw new ConfigurationError("aliases", `The name and aliases of ${value.name} must all be different`)
|
|
319
|
+
const taken = keys.findIndex((key) => this.#definitions.has(key) || this.#groupLookup.has(key))
|
|
320
|
+
if (taken !== -1)
|
|
321
|
+
throw new ConfigurationError(
|
|
322
|
+
"aliases",
|
|
323
|
+
`The name or alias ${JSON.stringify(names[taken])} of ${value.name} is already used by another command or group at the same level`,
|
|
324
|
+
{ hint: "Choose a different name or alias" },
|
|
325
|
+
)
|
|
326
|
+
return keys
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private lookupKey(parent: readonly string[], name: string): string {
|
|
330
|
+
return JSON.stringify([...parent, this.normalize(name)])
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The visible registered command or group name in this parent closest to an unknown name, within a bounded edit
|
|
335
|
+
* distance: At most one edit for names up to four characters and two edits for longer names, preferring the earliest
|
|
336
|
+
* registration. Hidden entries and entries inside hidden groups are never suggested
|
|
337
|
+
*/
|
|
338
|
+
private closestName(parent: readonly string[], name: string): string | undefined {
|
|
339
|
+
const wanted = this.normalize(name)
|
|
340
|
+
const limit = wanted.length <= 4 ? 1 : 2
|
|
341
|
+
let best: { readonly name: string; readonly distance: number } | undefined
|
|
342
|
+
for (const entry of this.visibleEntries) {
|
|
343
|
+
const path = entry.path ?? [entry.name]
|
|
344
|
+
if (!sameCommandPath(path.slice(0, -1), parent)) continue
|
|
345
|
+
for (const candidate of [entry.name, ...(entry.aliases ?? [])]) {
|
|
346
|
+
const distance = editDistance(wanted, this.normalize(candidate), limit)
|
|
347
|
+
if (distance <= limit && (best === undefined || distance < best.distance))
|
|
348
|
+
best = { name: entry.name, distance }
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return best?.name
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private matchPrefix(message: M, resolved: unknown): string | undefined {
|
|
355
|
+
const prefixes = typeof resolved === "string" ? [resolved] : resolved
|
|
356
|
+
if (!Array.isArray(prefixes)) {
|
|
357
|
+
if (prefixes === undefined) return undefined
|
|
358
|
+
throw new ConfigurationError("prefix", "A prefix resolver must return a string, string array or undefined")
|
|
359
|
+
}
|
|
360
|
+
const values = copyNonemptyStringArray(prefixes, "prefix")
|
|
361
|
+
let selected: string | undefined
|
|
362
|
+
for (const prefix of values)
|
|
363
|
+
if (message.content.startsWith(prefix) && (selected === undefined || prefix.length > selected.length))
|
|
364
|
+
selected = prefix
|
|
365
|
+
return selected
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private normalize(value: string): string {
|
|
369
|
+
return this.#options.caseSensitive === true ? value : value.toLowerCase()
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** A local cooldown outcome with configuration failures kept as data for the public adapter */
|
|
374
|
+
type LocalMemoryResult<A> =
|
|
375
|
+
{ readonly _tag: "Success"; readonly value: A } | { readonly _tag: "Failure"; readonly error: ConfigurationError }
|
|
376
|
+
|
|
377
|
+
/** Process-local cooldown state shared by default and native adapters */
|
|
378
|
+
export interface LocalMemoryCooldownStore {
|
|
379
|
+
readonly maxEntries: number
|
|
380
|
+
readonly size: number
|
|
381
|
+
claim(input: unknown, now?: number): LocalMemoryResult<CommandCooldownClaim>
|
|
382
|
+
sweep(now?: number): number
|
|
383
|
+
clear(): void
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Create one bounded local cooldown store after validating only its retention limit.
|
|
388
|
+
* Used for `commands.memoryCooldowns(options)` and for a router's own store from its `cooldowns` option
|
|
389
|
+
*/
|
|
390
|
+
export function createMemoryCooldownStore(options: unknown): LocalMemoryCooldownStore {
|
|
391
|
+
// Only reading the caller options is marked as application input, not creating the store
|
|
392
|
+
const maxEntries = readCaller(() => {
|
|
393
|
+
if (options !== undefined) validateObjectShape(options, ["maxEntries"], "maxEntries", "cooldown options")
|
|
394
|
+
const selected = (options as MemoryCooldownOptions | undefined)?.maxEntries
|
|
395
|
+
return selected === undefined ? defaultCooldownEntries : selected
|
|
396
|
+
})
|
|
397
|
+
if (!Number.isSafeInteger(maxEntries) || maxEntries < 1)
|
|
398
|
+
throw new ConfigurationError("maxEntries", 'The cooldown option "maxEntries" must be a positive safe integer')
|
|
399
|
+
return new LocalMemoryCooldownStoreOwner(maxEntries)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Copy a caller claim once, so the validated key and duration are the ones claimed. Other values pass unchanged */
|
|
403
|
+
export function snapshotClaimInput(input: unknown): unknown {
|
|
404
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return input
|
|
405
|
+
const { key, durationMs } = input as { readonly key?: unknown; readonly durationMs?: unknown }
|
|
406
|
+
return { key, durationMs }
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Keys with expiry times in one process, bounded without ever refusing a new key.
|
|
411
|
+
* Making room removes expired keys first and then the key that expires soonest
|
|
412
|
+
*/
|
|
413
|
+
export class ExpiringKeys {
|
|
414
|
+
readonly #entries = new Map<string, number>()
|
|
415
|
+
|
|
416
|
+
constructor(readonly maxEntries: number) {}
|
|
417
|
+
|
|
418
|
+
get size(): number {
|
|
419
|
+
return this.#entries.size
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** The expiry of an unexpired key, or undefined after removing an expired one */
|
|
423
|
+
active(key: string, now: number): number | undefined {
|
|
424
|
+
const expiry = this.#entries.get(key)
|
|
425
|
+
if (expiry === undefined) return undefined
|
|
426
|
+
if (expiry > now) return expiry
|
|
427
|
+
this.#entries.delete(key)
|
|
428
|
+
return undefined
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** Keep a key until expiry, making room first when a new key would exceed the limit */
|
|
432
|
+
set(key: string, expiry: number, now: number): void {
|
|
433
|
+
if (!this.#entries.has(key) && this.#entries.size >= this.maxEntries) {
|
|
434
|
+
this.sweep(now)
|
|
435
|
+
if (this.#entries.size >= this.maxEntries) {
|
|
436
|
+
let soonest: string | undefined
|
|
437
|
+
let soonestExpiry = Infinity
|
|
438
|
+
for (const [candidate, candidateExpiry] of this.#entries)
|
|
439
|
+
if (candidateExpiry < soonestExpiry) {
|
|
440
|
+
soonest = candidate
|
|
441
|
+
soonestExpiry = candidateExpiry
|
|
442
|
+
}
|
|
443
|
+
if (soonest !== undefined) this.#entries.delete(soonest)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
this.#entries.set(key, expiry)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Report whether a key is already active. Otherwise keep it until `until` and report false, so the caller acts once
|
|
451
|
+
* per active key. A time that has already passed is not kept
|
|
452
|
+
*/
|
|
453
|
+
suppress(key: string, until: number, now: number): boolean {
|
|
454
|
+
if (this.active(key, now) !== undefined) return true
|
|
455
|
+
if (until > now) this.set(key, until, now)
|
|
456
|
+
return false
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Remove expired keys and return how many were removed */
|
|
460
|
+
sweep(now: number): number {
|
|
461
|
+
let count = 0
|
|
462
|
+
for (const [key, expiry] of this.#entries)
|
|
463
|
+
if (expiry <= now) {
|
|
464
|
+
this.#entries.delete(key)
|
|
465
|
+
count += 1
|
|
466
|
+
}
|
|
467
|
+
return count
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
clear(): void {
|
|
471
|
+
this.#entries.clear()
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** A command's validated cooldown: The store to claim from, the reservation length and how invocations share a key */
|
|
476
|
+
export interface StoredCooldown {
|
|
477
|
+
/** Application store, or undefined for the router-owned memory store */
|
|
478
|
+
readonly store?: unknown
|
|
479
|
+
readonly durationMs: number
|
|
480
|
+
readonly per: CommandCooldownPer
|
|
481
|
+
readonly key?: unknown
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Snapshot cooldown fields before validation, retaining only the intentional store and key references */
|
|
485
|
+
export function snapshotCommandCooldown(value: unknown): StoredCooldown | undefined {
|
|
486
|
+
if (value === undefined) return
|
|
487
|
+
validateObjectShape(value, ["store", "durationMs", "per", "key"], "cooldown", "cooldown")
|
|
488
|
+
const { durationMs, store, key, per } = value as {
|
|
489
|
+
readonly durationMs?: unknown
|
|
490
|
+
readonly store?: unknown
|
|
491
|
+
readonly key?: unknown
|
|
492
|
+
readonly per?: unknown
|
|
493
|
+
}
|
|
494
|
+
if (
|
|
495
|
+
typeof durationMs !== "number" ||
|
|
496
|
+
!Number.isSafeInteger(durationMs) ||
|
|
497
|
+
durationMs < 1 ||
|
|
498
|
+
durationMs > maxDurationMs
|
|
499
|
+
)
|
|
500
|
+
throw new ConfigurationError(
|
|
501
|
+
"cooldown",
|
|
502
|
+
`The cooldown option "durationMs" must be a positive safe integer of at most ${maxDurationMs} ms`,
|
|
503
|
+
)
|
|
504
|
+
if (
|
|
505
|
+
store !== undefined &&
|
|
506
|
+
(typeof store !== "object" || store === null || typeof (store as { claim?: unknown }).claim !== "function")
|
|
507
|
+
)
|
|
508
|
+
throw new ConfigurationError("cooldown", 'The cooldown option "store" must be an object with a claim method')
|
|
509
|
+
if (key !== undefined && typeof key !== "function")
|
|
510
|
+
throw new ConfigurationError("cooldown", 'The cooldown option "key" must be a function')
|
|
511
|
+
if (per !== undefined && per !== "user" && per !== "channel" && per !== "guild")
|
|
512
|
+
throw new ConfigurationError("cooldown", 'The cooldown option "per" must be "user", "channel" or "guild"')
|
|
513
|
+
return Object.freeze({
|
|
514
|
+
durationMs,
|
|
515
|
+
per: per ?? "user",
|
|
516
|
+
...(store === undefined ? {} : { store }),
|
|
517
|
+
...(key === undefined ? {} : { key }),
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** The default cooldown key suffix for one invocation, selected by per */
|
|
522
|
+
export function cooldownSuffix(per: CommandCooldownPer, message: MessageCore): string {
|
|
523
|
+
if (per === "channel") return `channel:${message.channelId}`
|
|
524
|
+
if (per === "guild")
|
|
525
|
+
return message.guildId === undefined ? `channel:${message.channelId}` : `guild:${message.guildId}`
|
|
526
|
+
return `user:${message.author.id}`
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Validate and copy the shared definition fields once before an adapter adds its callback and cooldown references */
|
|
530
|
+
export function snapshotCommandDefinition(value: PrefixCommandDefinition, keyedName?: string): PrefixCommandDefinition {
|
|
531
|
+
if (typeof value !== "object" || value === null)
|
|
532
|
+
throw new ConfigurationError("command", "The command must be an object")
|
|
533
|
+
const name = keyedName ?? value.name
|
|
534
|
+
if (!isCommandName(name))
|
|
535
|
+
throw new ConfigurationError(
|
|
536
|
+
"command",
|
|
537
|
+
"Command names must use ASCII letters, numbers, `_` or `-` and begin alphanumerically",
|
|
538
|
+
)
|
|
539
|
+
const { aliases: sourceAliases, description, usage, hidden, arguments: sourceArguments } = value
|
|
540
|
+
const aliases = sourceAliases === undefined ? undefined : copyCommandNames(sourceAliases, "aliases")
|
|
541
|
+
if (description !== undefined && typeof description !== "string")
|
|
542
|
+
throw new ConfigurationError("command", "Command description must be a string when supplied")
|
|
543
|
+
if (usage !== undefined && typeof usage !== "string")
|
|
544
|
+
throw new ConfigurationError("command", "Command usage must be a string when supplied")
|
|
545
|
+
if (hidden !== undefined && typeof hidden !== "boolean")
|
|
546
|
+
throw new ConfigurationError("command", "Command hidden must be a boolean when supplied")
|
|
547
|
+
const argumentsSchema = snapshotCommandArguments(sourceArguments)
|
|
548
|
+
return Object.freeze({
|
|
549
|
+
name,
|
|
550
|
+
...(aliases === undefined ? {} : { aliases }),
|
|
551
|
+
...(description === undefined ? {} : { description }),
|
|
552
|
+
...(usage === undefined ? {} : { usage }),
|
|
553
|
+
...(hidden === undefined ? {} : { hidden }),
|
|
554
|
+
...(argumentsSchema === undefined ? {} : { arguments: argumentsSchema }),
|
|
555
|
+
})
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** Validate a finite adapter command object before reading its selected public fields */
|
|
559
|
+
export function validateCommandShape(value: unknown, keys: readonly string[]): void {
|
|
560
|
+
validateObjectShape(value, keys, "command", "command")
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** Reject malformed command cooldown claims without exposing caller-supplied data */
|
|
564
|
+
function validateCooldownClaim(value: unknown): asserts value is CommandCooldownClaim {
|
|
565
|
+
if (typeof value !== "object" || value === null)
|
|
566
|
+
throw new ConfigurationError("cooldown", "The cooldown store returned an invalid claim", { hint: claimHint })
|
|
567
|
+
const claim = value as { _tag?: unknown; retryAtMs?: unknown }
|
|
568
|
+
if (
|
|
569
|
+
(claim._tag === "CooldownAcquired" || claim._tag === "CooldownActive") &&
|
|
570
|
+
typeof claim.retryAtMs === "number" &&
|
|
571
|
+
Number.isSafeInteger(claim.retryAtMs)
|
|
572
|
+
)
|
|
573
|
+
return
|
|
574
|
+
throw new ConfigurationError("cooldown", "The cooldown store returned an invalid claim", { hint: claimHint })
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const claimHint =
|
|
578
|
+
'A cooldown store claim must return { _tag: "CooldownAcquired" } or { _tag: "CooldownActive" } with a safe integer retryAtMs'
|
|
579
|
+
|
|
580
|
+
/** Validate and namespace a caller-selected cooldown key before a store sees it */
|
|
581
|
+
export function cooldownRequest(
|
|
582
|
+
commandName: string | readonly string[],
|
|
583
|
+
key: unknown,
|
|
584
|
+
durationMs: number,
|
|
585
|
+
): CommandCooldownRequest {
|
|
586
|
+
if (typeof key !== "string" || key.length === 0)
|
|
587
|
+
throw new ConfigurationError("cooldown", "A cooldown key must be a nonempty string")
|
|
588
|
+
return Object.freeze({ key: JSON.stringify([commandName, key]), durationMs })
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Convert only expected configuration exceptions into the Effect error channel. Other exceptions remain defects */
|
|
592
|
+
export function configurationEffect<A>(thunk: () => A): Effect.Effect<A, ConfigurationError> {
|
|
593
|
+
return Effect.try({
|
|
594
|
+
try: thunk,
|
|
595
|
+
catch: (error) => {
|
|
596
|
+
if (error instanceof ConfigurationError) return error
|
|
597
|
+
throw error
|
|
598
|
+
},
|
|
599
|
+
})
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** Adapter callbacks for one shared Effect dispatcher */
|
|
603
|
+
export interface CommandDispatchAdapter<
|
|
604
|
+
D extends PrefixCommandDefinition,
|
|
605
|
+
C,
|
|
606
|
+
X = C,
|
|
607
|
+
E = never,
|
|
608
|
+
R = never,
|
|
609
|
+
M extends MessageCore = Message,
|
|
610
|
+
> {
|
|
611
|
+
/** Run a prefix resolver for one message in the adapter's style, producing its raw value for validation */
|
|
612
|
+
readonly prefix: (resolver: (message: M) => unknown, message: M) => Effect.Effect<unknown, E, R>
|
|
613
|
+
/** The bot's user ID for mentionPrefix, or undefined when it is not available yet */
|
|
614
|
+
readonly selfId?: () => Effect.Effect<string | undefined>
|
|
615
|
+
readonly context: (match: PrefixCommandMatch<D>) => C
|
|
616
|
+
/** Evaluate the command's guards, producing a boolean or `{ deny }` verdict */
|
|
617
|
+
readonly guard: (definition: D, context: C) => Effect.Effect<unknown, E, R>
|
|
618
|
+
/** Run router middleware around the rest of a matched command. The rest never runs if middleware does not call it */
|
|
619
|
+
readonly around?: (
|
|
620
|
+
context: C,
|
|
621
|
+
rest: Effect.Effect<void, ConfigurationError | E, R>,
|
|
622
|
+
) => Effect.Effect<void, ConfigurationError | E, R>
|
|
623
|
+
/** Local synchronous conversion after a successful guard and before a cooldown claim */
|
|
624
|
+
readonly convert?: (definition: D, context: C) => CommandArgumentConversion
|
|
625
|
+
readonly executionContext?: (context: C, values: Readonly<Record<string, unknown>>) => X
|
|
626
|
+
/**
|
|
627
|
+
* Claim the command's cooldown, producing the namespaced key and the store's unvalidated claim, or undefined when
|
|
628
|
+
* the command has no cooldown
|
|
629
|
+
*/
|
|
630
|
+
readonly cooldown?: (
|
|
631
|
+
definition: D,
|
|
632
|
+
context: X,
|
|
633
|
+
routerStore: LocalMemoryCooldownStore,
|
|
634
|
+
) => Effect.Effect<{ readonly key: string; readonly claim: unknown } | undefined, E, R>
|
|
635
|
+
/** The command's effective rejection feedback: The router's reply, an application callback or none */
|
|
636
|
+
readonly feedback?: (definition: D) => "reply" | "callback" | undefined
|
|
637
|
+
/**
|
|
638
|
+
* Contextual rejection feedback, called only when feedback is selected and not suppressed.
|
|
639
|
+
* The time comes from the dispatch Clock. It runs through the existing subscription error boundary
|
|
640
|
+
*/
|
|
641
|
+
readonly reject?: (
|
|
642
|
+
definition: D,
|
|
643
|
+
context: C,
|
|
644
|
+
rejection: PrefixCommandRejection,
|
|
645
|
+
now: number,
|
|
646
|
+
) => Effect.Effect<unknown, E, R>
|
|
647
|
+
/** Optional application-owned feedback for a parser decline or unregistered parsed name */
|
|
648
|
+
readonly unmatched?: ((match: PrefixCommandUnmatchedMatch) => Effect.Effect<unknown, E, R>) | undefined
|
|
649
|
+
readonly execute: (definition: D, context: X) => Effect.Effect<unknown, E, R>
|
|
650
|
+
/** Return false after default cancellation so a later callback never starts */
|
|
651
|
+
readonly active?: () => boolean
|
|
652
|
+
/** Client logger for rejected, unmatched and executed command records */
|
|
653
|
+
readonly logger?: ClientLogger | undefined
|
|
654
|
+
/** Report a failed dispatch: A command callback with its command name, or an unmatched callback, prefix or parser
|
|
655
|
+
* failure without one. Interruption is never reported. When omitted, the failure fails the dispatch
|
|
656
|
+
*/
|
|
657
|
+
readonly failed?: (name: string | undefined, cause: Cause.Cause<unknown>) => Effect.Effect<void>
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Why a command did not run, for its Debug record. Never includes the rejected argument text */
|
|
661
|
+
function rejectionText(rejection: PrefixCommandRejection, now: number): string {
|
|
662
|
+
switch (rejection._tag) {
|
|
663
|
+
case "CommandGuardRejected":
|
|
664
|
+
return "a guard denied it"
|
|
665
|
+
case "CommandCooldownActive":
|
|
666
|
+
return `its cooldown is active for another ${Math.max(0, rejection.retryAtMs - now)} ms`
|
|
667
|
+
case "CommandArgumentRejected":
|
|
668
|
+
if (rejection.reason === "Unexpected") return "it received more arguments than it accepts"
|
|
669
|
+
if (rejection.reason === "Missing") return `the argument ${rejection.argument} is missing`
|
|
670
|
+
if (rejection.reason === "Ambiguous")
|
|
671
|
+
return `the argument ${rejection.argument} matches more than one option`
|
|
672
|
+
return `the argument ${rejection.argument} is invalid`
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** Unmatched reasons in plain words for the Debug record */
|
|
677
|
+
const unmatchedText: Record<PrefixCommandUnmatched["_tag"], string> = {
|
|
678
|
+
CommandUnknownName: "A message used the command prefix with a name that matches no command",
|
|
679
|
+
CommandMissingSubcommand: "A message named a command group without a subcommand",
|
|
680
|
+
CommandParserRejected: "A message used the command prefix, but the parser found no command in it",
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Command routers keep up with bursts: Several commands run at once and overflow drops the oldest waiting message */
|
|
684
|
+
const routerDefaults = Object.freeze({ concurrency: 8, overflow: "dropOldest" as const })
|
|
685
|
+
|
|
686
|
+
/** Subscription options for an attached router: Its defaults, then each defined caller setting. An explicit undefined
|
|
687
|
+
* keeps the default. The onError option is removed because the router reports command failures through its own hook queue.
|
|
688
|
+
* A non-object value passes through unchanged so client.on rejects it with its usual ConfigurationError
|
|
689
|
+
*/
|
|
690
|
+
export function routerSubscriptionOptions(options: unknown): unknown {
|
|
691
|
+
if (options === undefined) return routerDefaults
|
|
692
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) return options
|
|
693
|
+
const selected: Record<string, unknown> = { ...routerDefaults }
|
|
694
|
+
for (const [key, value] of Object.entries(options))
|
|
695
|
+
if (key !== "onError" && value !== undefined) selected[key] = value
|
|
696
|
+
return Object.freeze(selected)
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** Validate a guard verdict: `true` allows, `false` denies without a reason and `{ deny }` denies with one */
|
|
700
|
+
function guardVerdict(value: unknown): true | { readonly reason?: string } {
|
|
701
|
+
if (value === true) return true
|
|
702
|
+
if (value === false) return {}
|
|
703
|
+
if (
|
|
704
|
+
typeof value === "object" &&
|
|
705
|
+
value !== null &&
|
|
706
|
+
typeof (value as { deny?: unknown }).deny === "string" &&
|
|
707
|
+
(value as { deny: string }).deny.length > 0
|
|
708
|
+
)
|
|
709
|
+
return { reason: (value as { deny: string }).deny }
|
|
710
|
+
throw new ConfigurationError("command", "A command guard must return a boolean or { deny: string }")
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** Prefixes to try for one message: The configured or resolved prefixes, plus the bot mention when enabled */
|
|
714
|
+
function prefixesFor<D extends PrefixCommandDefinition, C, X, E, R, M extends MessageCore>(
|
|
715
|
+
registry: PrefixCommandRegistry<D, M>,
|
|
716
|
+
message: M,
|
|
717
|
+
adapter: CommandDispatchAdapter<D, C, X, E, R, M>,
|
|
718
|
+
): Effect.Effect<unknown, E, R> {
|
|
719
|
+
return Effect.gen(function* () {
|
|
720
|
+
const source = registry.prefix
|
|
721
|
+
const prefixes = typeof source === "function" ? yield* adapter.prefix(source, message) : source
|
|
722
|
+
if (!registry.mentionPrefix || adapter.selfId === undefined || prefixes === undefined) return prefixes
|
|
723
|
+
if (!message.content.startsWith("<@")) return prefixes
|
|
724
|
+
const id = yield* adapter.selfId()
|
|
725
|
+
if (id === undefined) return prefixes
|
|
726
|
+
const listed = typeof prefixes === "string" ? [prefixes] : Array.isArray(prefixes) ? prefixes : undefined
|
|
727
|
+
// A malformed resolver value stays as it is, so resolve rejects it with the usual ConfigurationError
|
|
728
|
+
return listed === undefined ? prefixes : [...listed, `<@${id}>`, `<@!${id}>`]
|
|
729
|
+
})
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** Dispatch one resolved command through the caller's Effect runtime without a queue, send or retry */
|
|
733
|
+
export function dispatchCommand<D extends PrefixCommandDefinition, C, X, E, R, M extends MessageCore>(
|
|
734
|
+
registry: PrefixCommandRegistry<D, M>,
|
|
735
|
+
message: M,
|
|
736
|
+
adapter: CommandDispatchAdapter<D, C, X, E, R, M>,
|
|
737
|
+
): Effect.Effect<void, ConfigurationError | E, R> {
|
|
738
|
+
const active = (): Effect.Effect<void> =>
|
|
739
|
+
adapter.active === undefined
|
|
740
|
+
? Effect.void
|
|
741
|
+
: (Effect.suspend(() => (adapter.active!() ? Effect.void : Effect.interrupt)) as Effect.Effect<void>)
|
|
742
|
+
/**
|
|
743
|
+
* Log a rejection and give its feedback. A `"reply"` answers once per limit key while that key is active, so repeated
|
|
744
|
+
* attempts do not make the bot repeat itself. Application callbacks receive every rejection
|
|
745
|
+
*/
|
|
746
|
+
const reject = (
|
|
747
|
+
definition: D,
|
|
748
|
+
context: C,
|
|
749
|
+
rejection: PrefixCommandRejection,
|
|
750
|
+
limit?: { readonly key: string; readonly until: (now: number) => number },
|
|
751
|
+
) =>
|
|
752
|
+
Effect.gen(function* () {
|
|
753
|
+
const now = yield* Clock.currentTimeMillis
|
|
754
|
+
const selected =
|
|
755
|
+
adapter.reject === undefined
|
|
756
|
+
? undefined
|
|
757
|
+
: adapter.feedback === undefined
|
|
758
|
+
? "callback"
|
|
759
|
+
: adapter.feedback(definition)
|
|
760
|
+
// A false guard has no automatic reply, but custom callbacks still receive every rejection
|
|
761
|
+
const feedback =
|
|
762
|
+
selected === "reply" && rejection._tag === "CommandGuardRejected" && rejection.reason === undefined
|
|
763
|
+
? undefined
|
|
764
|
+
: selected
|
|
765
|
+
const suppressed =
|
|
766
|
+
feedback === "reply" &&
|
|
767
|
+
limit !== undefined &&
|
|
768
|
+
registry.feedback.suppress(limit.key, limit.until(now), now)
|
|
769
|
+
adapter.logger?.drop("commandRejections", {
|
|
770
|
+
level: "debug",
|
|
771
|
+
category: "commands",
|
|
772
|
+
code: "commands.rejected",
|
|
773
|
+
message: `Command ${definition.name} did not run because ${rejectionText(rejection, now)}${
|
|
774
|
+
feedback === undefined
|
|
775
|
+
? ""
|
|
776
|
+
: suppressed
|
|
777
|
+
? ". No reply was sent because the same rejection was answered recently"
|
|
778
|
+
: feedback === "reply"
|
|
779
|
+
? ". A reply explains why"
|
|
780
|
+
: ". The onReject callback was called"
|
|
781
|
+
}`,
|
|
782
|
+
command: definition.name,
|
|
783
|
+
fields: {
|
|
784
|
+
rejection: rejection._tag,
|
|
785
|
+
...(rejection._tag === "CommandArgumentRejected" ? { argument: rejection.argument } : {}),
|
|
786
|
+
...(suppressed ? { feedbackSuppressed: true } : {}),
|
|
787
|
+
},
|
|
788
|
+
})
|
|
789
|
+
if (feedback === undefined || suppressed) return
|
|
790
|
+
yield* adapter.reject!(definition, context, rejection, now)
|
|
791
|
+
})
|
|
792
|
+
const dispatch = Effect.gen(function* () {
|
|
793
|
+
yield* active()
|
|
794
|
+
if (registry.ignores(message)) return
|
|
795
|
+
const prefixes = yield* prefixesFor(registry, message, adapter)
|
|
796
|
+
const resolved = yield* configurationEffect(() => registry.resolve(message, prefixes))
|
|
797
|
+
if (resolved === undefined) return
|
|
798
|
+
if ("unmatched" in resolved) {
|
|
799
|
+
yield* active()
|
|
800
|
+
const suggestion =
|
|
801
|
+
resolved.unmatched._tag === "CommandUnknownName" ? resolved.unmatched.suggestion : undefined
|
|
802
|
+
const detail =
|
|
803
|
+
resolved.unmatched._tag === "CommandMissingSubcommand"
|
|
804
|
+
? ` (${resolved.unmatched.path.join(" ")})`
|
|
805
|
+
: resolved.unmatched._tag === "CommandParserRejected" && resolved.unmatched.reason !== undefined
|
|
806
|
+
? ` (${resolved.unmatched.reason})`
|
|
807
|
+
: ""
|
|
808
|
+
adapter.logger?.drop("unmatchedCommands", {
|
|
809
|
+
level: "debug",
|
|
810
|
+
category: "commands",
|
|
811
|
+
code: "commands.unmatched",
|
|
812
|
+
message: `${unmatchedText[resolved.unmatched._tag]}${detail}${suggestion === undefined ? "" : `. The closest command is ${suggestion}`}${adapter.unmatched === undefined ? "" : ". The onUnmatched callback was called"}`,
|
|
813
|
+
fields: {
|
|
814
|
+
reason: resolved.unmatched._tag,
|
|
815
|
+
messageId: message.id,
|
|
816
|
+
channelId: message.channelId,
|
|
817
|
+
...(suggestion === undefined ? {} : { suggestion }),
|
|
818
|
+
},
|
|
819
|
+
})
|
|
820
|
+
if (adapter.unmatched !== undefined) yield* adapter.unmatched(resolved)
|
|
821
|
+
return
|
|
822
|
+
}
|
|
823
|
+
const matched = resolved
|
|
824
|
+
yield* identifyCommand(matched.definition.name)
|
|
825
|
+
const run = matchedCommand(matched).pipe(
|
|
826
|
+
Effect.withSpan("fluxerly.command.execute", {
|
|
827
|
+
attributes: { "fluxerly.command": matched.definition.name },
|
|
828
|
+
}),
|
|
829
|
+
)
|
|
830
|
+
if (adapter.failed === undefined) return yield* run
|
|
831
|
+
return yield* run.pipe(
|
|
832
|
+
Effect.catchCause((cause) =>
|
|
833
|
+
Cause.hasInterrupts(cause) ? Effect.failCause(cause) : adapter.failed!(matched.definition.name, cause),
|
|
834
|
+
),
|
|
835
|
+
)
|
|
836
|
+
}) as Effect.Effect<void, ConfigurationError | E, R>
|
|
837
|
+
if (adapter.failed === undefined) return dispatch
|
|
838
|
+
// Unmatched callbacks and custom parsers can fail outside a matched command, and they reach the same report
|
|
839
|
+
return dispatch.pipe(
|
|
840
|
+
Effect.catchCause((cause) =>
|
|
841
|
+
Cause.hasInterrupts(cause) ? Effect.failCause(cause) : adapter.failed!(undefined, cause),
|
|
842
|
+
),
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
function matchedCommand(matched: PrefixCommandMatch<D>) {
|
|
846
|
+
return Effect.gen(function* () {
|
|
847
|
+
const context = adapter.context(matched)
|
|
848
|
+
yield* active()
|
|
849
|
+
let continued = false
|
|
850
|
+
const rest = Effect.gen(function* () {
|
|
851
|
+
continued = true
|
|
852
|
+
yield* checkedCommand(matched, context)
|
|
853
|
+
}) as Effect.Effect<void, ConfigurationError | E, R>
|
|
854
|
+
if (adapter.around === undefined) return yield* rest
|
|
855
|
+
yield* adapter.around(context, rest)
|
|
856
|
+
if (!continued)
|
|
857
|
+
adapter.logger?.log({
|
|
858
|
+
level: "debug",
|
|
859
|
+
category: "commands",
|
|
860
|
+
code: "commands.middlewareStopped",
|
|
861
|
+
message: `Command ${matched.definition.name} did not run because middleware did not continue`,
|
|
862
|
+
command: matched.definition.name,
|
|
863
|
+
})
|
|
864
|
+
})
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function checkedCommand(matched: PrefixCommandMatch<D>, context: C) {
|
|
868
|
+
return Effect.gen(function* () {
|
|
869
|
+
const verdict = guardVerdict(yield* adapter.guard(matched.definition, context))
|
|
870
|
+
if (verdict !== true) {
|
|
871
|
+
yield* active()
|
|
872
|
+
yield* reject(
|
|
873
|
+
matched.definition,
|
|
874
|
+
context,
|
|
875
|
+
Object.freeze({
|
|
876
|
+
_tag: "CommandGuardRejected",
|
|
877
|
+
...(verdict.reason === undefined ? {} : { reason: verdict.reason }),
|
|
878
|
+
}),
|
|
879
|
+
{
|
|
880
|
+
key: JSON.stringify(["guard", matched.path ?? [matched.definition.name], message.author.id]),
|
|
881
|
+
until: (now) => now + guardFeedbackWindowMs,
|
|
882
|
+
},
|
|
883
|
+
)
|
|
884
|
+
return
|
|
885
|
+
}
|
|
886
|
+
const conversion = adapter.convert === undefined ? undefined : adapter.convert(matched.definition, context)
|
|
887
|
+
if (conversion !== undefined && conversion._tag === "Rejected") {
|
|
888
|
+
yield* active()
|
|
889
|
+
yield* reject(
|
|
890
|
+
matched.definition,
|
|
891
|
+
context,
|
|
892
|
+
Object.freeze({
|
|
893
|
+
_tag: "CommandArgumentRejected",
|
|
894
|
+
argument: conversion.argument,
|
|
895
|
+
reason: conversion.reason,
|
|
896
|
+
}),
|
|
897
|
+
)
|
|
898
|
+
return
|
|
899
|
+
}
|
|
900
|
+
const executionContext =
|
|
901
|
+
conversion === undefined || adapter.executionContext === undefined
|
|
902
|
+
? (context as unknown as X)
|
|
903
|
+
: adapter.executionContext(context, conversion.values)
|
|
904
|
+
if (adapter.cooldown !== undefined) {
|
|
905
|
+
yield* active()
|
|
906
|
+
const claimed = yield* adapter.cooldown(matched.definition, executionContext, registry.cooldowns)
|
|
907
|
+
if (claimed !== undefined) {
|
|
908
|
+
yield* configurationEffect(() => validateCooldownClaim(claimed.claim))
|
|
909
|
+
const claim = claimed.claim as CommandCooldownClaim
|
|
910
|
+
if (claim._tag === "CooldownActive") {
|
|
911
|
+
yield* active()
|
|
912
|
+
yield* reject(
|
|
913
|
+
matched.definition,
|
|
914
|
+
context,
|
|
915
|
+
Object.freeze({ _tag: "CommandCooldownActive", retryAtMs: claim.retryAtMs }),
|
|
916
|
+
{ key: JSON.stringify(["cooldown", claimed.key]), until: () => claim.retryAtMs },
|
|
917
|
+
)
|
|
918
|
+
return
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
yield* active()
|
|
923
|
+
// Measured on the dispatch's Effect Clock, so a TestClock controls the reported duration
|
|
924
|
+
const clock = yield* Clock.Clock
|
|
925
|
+
const startedAt = nowMs(clock)
|
|
926
|
+
yield* adapter.execute(matched.definition, executionContext)
|
|
927
|
+
adapter.logger?.log({
|
|
928
|
+
level: "debug",
|
|
929
|
+
category: "commands",
|
|
930
|
+
code: "commands.executed",
|
|
931
|
+
message: `Command ${matched.definition.name} finished`,
|
|
932
|
+
command: matched.definition.name,
|
|
933
|
+
durationMs: Math.round(nowMs(clock) - startedAt),
|
|
934
|
+
})
|
|
935
|
+
}) as Effect.Effect<void, ConfigurationError | E, R>
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Impersonal reply text for a rejected command, used by `onReject: "reply"`.
|
|
941
|
+
* A guard denial without a reason returns undefined, so a false verdict has no generic reply.
|
|
942
|
+
* Argument rejections name the argument, what was expected and the command's usage.
|
|
943
|
+
* Cooldown replies measure the remaining time from `now`, the dispatch Clock's wall time
|
|
944
|
+
*/
|
|
945
|
+
export function rejectionReply(
|
|
946
|
+
rejection: PrefixCommandRejection,
|
|
947
|
+
command: PrefixCommandMetadata | undefined,
|
|
948
|
+
prefix: string,
|
|
949
|
+
now: number,
|
|
950
|
+
): string | undefined {
|
|
951
|
+
switch (rejection._tag) {
|
|
952
|
+
case "CommandGuardRejected":
|
|
953
|
+
return rejection.reason
|
|
954
|
+
case "CommandCooldownActive": {
|
|
955
|
+
const seconds = Math.max(1, Math.ceil((rejection.retryAtMs - now) / 1_000))
|
|
956
|
+
return `This command is on cooldown. Try again in ${seconds} ${seconds === 1 ? "second" : "seconds"}`
|
|
957
|
+
}
|
|
958
|
+
case "CommandArgumentRejected": {
|
|
959
|
+
const usage =
|
|
960
|
+
command === undefined
|
|
961
|
+
? ""
|
|
962
|
+
: `. Usage: ${prefix}${(command.path ?? [command.name]).join(" ")}${commandUsage(command) ? " " + commandUsage(command) : ""}`
|
|
963
|
+
if (rejection.reason === "Unexpected") return `Too many arguments${usage}`
|
|
964
|
+
const argument = command?.arguments?.find((entry) => entry.name === rejection.argument)
|
|
965
|
+
if (rejection.reason === "Missing") return `Missing ${rejection.argument}${usage}`
|
|
966
|
+
if (rejection.reason === "Ambiguous")
|
|
967
|
+
return `The ${rejection.argument} value matches more than one option, so an ID or mention is needed${usage}`
|
|
968
|
+
return `Invalid ${rejection.argument}${argument === undefined ? "" : `. Expected ${expectation(argument)}`}${usage}`
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/** Generated usage syntax such as `<text> [count]`, or the command's explicit usage */
|
|
974
|
+
function commandUsage(command: PrefixCommandMetadata): string {
|
|
975
|
+
return (
|
|
976
|
+
command.usage ??
|
|
977
|
+
command.arguments
|
|
978
|
+
?.map((argument) => {
|
|
979
|
+
const name = argument.name + (argument.rest ? "..." : "")
|
|
980
|
+
return argument.optional ? `[${name}]` : `<${name}>`
|
|
981
|
+
})
|
|
982
|
+
.join(" ") ??
|
|
983
|
+
""
|
|
984
|
+
)
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function expectation(argument: NonNullable<PrefixCommandMetadata["arguments"]>[number]): string {
|
|
988
|
+
const range = (unit: (value: number) => string) =>
|
|
989
|
+
argument.min !== undefined && argument.max !== undefined
|
|
990
|
+
? ` from ${unit(argument.min)} to ${unit(argument.max)}`
|
|
991
|
+
: argument.min !== undefined
|
|
992
|
+
? ` of at least ${unit(argument.min)}`
|
|
993
|
+
: argument.max !== undefined
|
|
994
|
+
? ` of at most ${unit(argument.max)}`
|
|
995
|
+
: ""
|
|
996
|
+
switch (argument.type) {
|
|
997
|
+
case "integer":
|
|
998
|
+
return `a whole number${range(String)}`
|
|
999
|
+
case "number":
|
|
1000
|
+
return `a number${range(String)}`
|
|
1001
|
+
case "duration":
|
|
1002
|
+
return `a duration${argument.wholeSeconds === true ? " in whole seconds" : ""} such as 10m or 1h30m${range((value) => `${value} ms`)}`
|
|
1003
|
+
case "boolean":
|
|
1004
|
+
return "true or false"
|
|
1005
|
+
case "id":
|
|
1006
|
+
return argument.mention === undefined ? "an ID" : `an ID or ${argument.mention} mention`
|
|
1007
|
+
case "choice":
|
|
1008
|
+
return `one of ${(argument.choices ?? []).join(", ")}`
|
|
1009
|
+
case "member":
|
|
1010
|
+
return "a member mention or ID"
|
|
1011
|
+
case "userChoice":
|
|
1012
|
+
return "a listed user"
|
|
1013
|
+
case "channelChoice":
|
|
1014
|
+
return "a listed channel"
|
|
1015
|
+
case "roleChoice":
|
|
1016
|
+
return "a listed role"
|
|
1017
|
+
case "custom":
|
|
1018
|
+
return argument.expected ?? "a valid value"
|
|
1019
|
+
case "text":
|
|
1020
|
+
return "text"
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/** Router option keys shared by both APIs. Each API adds its own callback keys */
|
|
1025
|
+
const routerOptionKeys = ["prefix", "parse", "ignoreBots", "caseSensitive", "mentionPrefix", "cooldowns"] as const
|
|
1026
|
+
|
|
1027
|
+
/** Subscription delivery settings that a runBot commands option passes to the router's attach unchanged */
|
|
1028
|
+
export const botCommandDeliveryKeys = [
|
|
1029
|
+
"concurrency",
|
|
1030
|
+
"partition",
|
|
1031
|
+
"overflow",
|
|
1032
|
+
"maxPendingMessages",
|
|
1033
|
+
"maxPendingBytes",
|
|
1034
|
+
] as const
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Check the keys of a runBot commands option before its router is created, so the hint also lists commands and onError.
|
|
1038
|
+
* A key whose value looks like a command definition is explained as a command placed one level too high.
|
|
1039
|
+
* Values are read through property descriptors, so no getter runs here
|
|
1040
|
+
*/
|
|
1041
|
+
export function checkBotCommandKeys(options: object, adapterKeys: readonly string[]): void {
|
|
1042
|
+
const keys = [...routerOptionKeys, ...adapterKeys, ...botCommandDeliveryKeys, "commands", "onError"]
|
|
1043
|
+
for (const key of Reflect.ownKeys(options)) {
|
|
1044
|
+
if (typeof key !== "string" || keys.includes(key)) continue
|
|
1045
|
+
const value: unknown = Reflect.getOwnPropertyDescriptor(options, key)?.value
|
|
1046
|
+
const command = typeof value === "object" && value !== null && Object.hasOwn(value, "execute")
|
|
1047
|
+
throw new ConfigurationError(
|
|
1048
|
+
"commands",
|
|
1049
|
+
`Unsupported key ${JSON.stringify(key)} in the runBot commands option`,
|
|
1050
|
+
{
|
|
1051
|
+
hint: command
|
|
1052
|
+
? `Put command definitions in the inner commands object, next to prefix, such as commands: { prefix: "!", commands: { ${key.length > 64 ? "name" : key}: { execute } } }`
|
|
1053
|
+
: unsupportedKeyHint(key, keys, "keys"),
|
|
1054
|
+
},
|
|
1055
|
+
)
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Validate the shared router settings. Adapter keys, such as callbacks, are allowed here and validated by the adapter.
|
|
1061
|
+
* The `cooldowns` option is allowed but not copied, because the registry reads it once to create its own store
|
|
1062
|
+
*/
|
|
1063
|
+
function copyOptions<M extends MessageCore>(
|
|
1064
|
+
input: unknown,
|
|
1065
|
+
adapterKeys: readonly string[],
|
|
1066
|
+
): Readonly<RouterSettings<M>> {
|
|
1067
|
+
validateObjectShape(input, [...routerOptionKeys, ...adapterKeys], "commands", "prefix command options")
|
|
1068
|
+
const value = input as RouterSettings<M>
|
|
1069
|
+
if (value.prefix === undefined)
|
|
1070
|
+
throw new ConfigurationError("prefix", 'The option "prefix" is required', {
|
|
1071
|
+
hint: 'Set the text that starts a command, such as prefix: "!"',
|
|
1072
|
+
})
|
|
1073
|
+
if (typeof value.prefix !== "string" && !Array.isArray(value.prefix) && typeof value.prefix !== "function")
|
|
1074
|
+
throw new ConfigurationError(
|
|
1075
|
+
"prefix",
|
|
1076
|
+
'The option "prefix" must be a string, an array of strings or a function',
|
|
1077
|
+
)
|
|
1078
|
+
if (typeof value.prefix === "string" && value.prefix.length === 0)
|
|
1079
|
+
throw new ConfigurationError("prefix", 'The option "prefix" must not be empty')
|
|
1080
|
+
const prefix = Array.isArray(value.prefix) ? copyNonemptyStringArray(value.prefix, "prefix") : value.prefix
|
|
1081
|
+
if (value.parse !== undefined && typeof value.parse !== "function")
|
|
1082
|
+
throw new ConfigurationError("parser", 'The option "parse" must be a function')
|
|
1083
|
+
if (value.ignoreBots !== undefined && typeof value.ignoreBots !== "boolean")
|
|
1084
|
+
throw new ConfigurationError("commands", 'The option "ignoreBots" must be a boolean')
|
|
1085
|
+
if (value.caseSensitive !== undefined && typeof value.caseSensitive !== "boolean")
|
|
1086
|
+
throw new ConfigurationError("commands", 'The option "caseSensitive" must be a boolean')
|
|
1087
|
+
if (value.mentionPrefix !== undefined && typeof value.mentionPrefix !== "boolean")
|
|
1088
|
+
throw new ConfigurationError("commands", 'The option "mentionPrefix" must be a boolean')
|
|
1089
|
+
return Object.freeze({
|
|
1090
|
+
prefix,
|
|
1091
|
+
...(value.parse === undefined ? {} : { parse: value.parse }),
|
|
1092
|
+
...(value.ignoreBots === undefined ? {} : { ignoreBots: value.ignoreBots }),
|
|
1093
|
+
...(value.caseSensitive === undefined ? {} : { caseSensitive: value.caseSensitive }),
|
|
1094
|
+
...(value.mentionPrefix === undefined ? {} : { mentionPrefix: value.mentionPrefix }),
|
|
1095
|
+
})
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/** Project only public metadata from an already validated owned registration snapshot */
|
|
1099
|
+
function commandMetadata(value: PrefixCommandDefinition, parent: readonly string[]): PrefixCommandMetadata {
|
|
1100
|
+
const argumentsMetadata = commandArgumentMetadata(value.arguments)
|
|
1101
|
+
return Object.freeze({
|
|
1102
|
+
name: value.name,
|
|
1103
|
+
...(parent.length === 0 ? {} : { path: Object.freeze([...parent, value.name]) }),
|
|
1104
|
+
...(value.aliases === undefined ? {} : { aliases: value.aliases }),
|
|
1105
|
+
...(value.description === undefined ? {} : { description: value.description }),
|
|
1106
|
+
...(value.usage === undefined ? {} : { usage: value.usage }),
|
|
1107
|
+
...(value.hidden === undefined ? {} : { hidden: value.hidden }),
|
|
1108
|
+
...(argumentsMetadata === undefined ? {} : { arguments: argumentsMetadata }),
|
|
1109
|
+
})
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/** Validate a canonical group selector without retaining rejected caller values */
|
|
1113
|
+
export function copyCommandGroupPath(value: unknown, field: "command" | "help"): readonly string[] {
|
|
1114
|
+
if (!Array.isArray(value))
|
|
1115
|
+
throw new ConfigurationError(field, 'The option "group" must be an array of group names, not aliases')
|
|
1116
|
+
validateArrayShape(value, field)
|
|
1117
|
+
const path: string[] = []
|
|
1118
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1119
|
+
const name = value[index]
|
|
1120
|
+
if (!isCommandName(name))
|
|
1121
|
+
throw new ConfigurationError(
|
|
1122
|
+
field,
|
|
1123
|
+
'The option "group" must contain group names that use ASCII letters, numbers, `_` or `-` and begin alphanumerically',
|
|
1124
|
+
)
|
|
1125
|
+
path.push(name)
|
|
1126
|
+
}
|
|
1127
|
+
return Object.freeze(path)
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/** Exact canonical path comparison, independent of alias matching policy */
|
|
1131
|
+
export function sameCommandPath(left: readonly string[], right: readonly string[]): boolean {
|
|
1132
|
+
return left.length === right.length && left.every((name, index) => name === right[index])
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/** A parser result with a reason and no name, which declines the text with an explanation instead of naming a command */
|
|
1136
|
+
function isParseRejection(value: unknown): value is PrefixCommandParseRejection {
|
|
1137
|
+
return typeof value === "object" && value !== null && "reason" in value && !("name" in value)
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function copyParseRejection(value: PrefixCommandParseRejection): string {
|
|
1141
|
+
validateObjectShape(value, ["reason"], "parser", "parser rejection")
|
|
1142
|
+
const reason = value.reason
|
|
1143
|
+
if (typeof reason !== "string" || reason.length === 0)
|
|
1144
|
+
throw new ConfigurationError("parser", "A parser rejection must have a nonempty string reason")
|
|
1145
|
+
return reason
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function copyParse(value: PrefixCommandParse): PrefixCommandParse {
|
|
1149
|
+
validateObjectShape(value, ["name", "args", "rawArgs"], "parser", "parser result")
|
|
1150
|
+
if (!isCommandName(value.name)) throw new ConfigurationError("parser", "A parser must return a valid command name")
|
|
1151
|
+
if (typeof value.rawArgs !== "string")
|
|
1152
|
+
throw new ConfigurationError("parser", "A parser must return rawArgs as a string")
|
|
1153
|
+
return Object.freeze({ name: value.name, rawArgs: value.rawArgs, args: copyStringArray(value.args, "parser") })
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function defaultParse<M extends MessageCore>(input: PrefixCommandParseInput<M>): PrefixCommandParse | undefined {
|
|
1157
|
+
const source = input.source.trimStart()
|
|
1158
|
+
if (source.length === 0) return undefined
|
|
1159
|
+
const match = /^(\S+)(?:\s+([\s\S]*))?$/.exec(source)
|
|
1160
|
+
const name = match?.[1]
|
|
1161
|
+
if (name === undefined || !isCommandName(name)) return undefined
|
|
1162
|
+
const rawArgs = match?.[2] ?? ""
|
|
1163
|
+
const positional = rawArgs.trim()
|
|
1164
|
+
return { name, rawArgs, args: positional.length === 0 ? [] : positional.split(/\s+/) }
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function copyCommandNames(value: unknown, field: "aliases"): readonly string[] {
|
|
1168
|
+
const names = copyStringArray(value, field)
|
|
1169
|
+
for (const name of names)
|
|
1170
|
+
if (!isCommandName(name))
|
|
1171
|
+
throw new ConfigurationError(
|
|
1172
|
+
"aliases",
|
|
1173
|
+
"Command aliases must use ASCII letters, numbers, `_` or `-` and begin alphanumerically",
|
|
1174
|
+
)
|
|
1175
|
+
return names
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function copyNonemptyStringArray(value: unknown, field: "prefix"): readonly string[] {
|
|
1179
|
+
const values = copyStringArray(value, field)
|
|
1180
|
+
if (values.length === 0) throw new ConfigurationError("prefix", prefixArrayMessage)
|
|
1181
|
+
for (const prefix of values) if (prefix.length === 0) throw new ConfigurationError("prefix", prefixArrayMessage)
|
|
1182
|
+
return values
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const prefixArrayMessage = "A prefix array must contain at least one prefix, and no prefix may be empty"
|
|
1186
|
+
|
|
1187
|
+
/** How array shape messages name the value they check */
|
|
1188
|
+
const shapeSubjects: Record<"aliases" | "prefix" | "parser" | "command" | "help", string> = {
|
|
1189
|
+
aliases: 'The option "aliases"',
|
|
1190
|
+
prefix: "The prefix list",
|
|
1191
|
+
parser: 'The parser result "args"',
|
|
1192
|
+
command: 'The option "group"',
|
|
1193
|
+
help: 'The help option "group"',
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function copyStringArray(value: unknown, field: "aliases" | "prefix" | "parser"): readonly string[] {
|
|
1197
|
+
if (!Array.isArray(value))
|
|
1198
|
+
throw new ConfigurationError(field, `${shapeSubjects[field]} must be an array of strings`)
|
|
1199
|
+
validateArrayShape(value, field)
|
|
1200
|
+
const copied: string[] = []
|
|
1201
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1202
|
+
const entry = value[index]
|
|
1203
|
+
if (typeof entry !== "string")
|
|
1204
|
+
throw new ConfigurationError(field, `${shapeSubjects[field]} must contain only strings`)
|
|
1205
|
+
copied.push(entry)
|
|
1206
|
+
}
|
|
1207
|
+
return Object.freeze(copied)
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function validateObjectShape(
|
|
1211
|
+
value: unknown,
|
|
1212
|
+
keys: readonly string[],
|
|
1213
|
+
field: ConfigurationError["field"],
|
|
1214
|
+
subject: string,
|
|
1215
|
+
): void {
|
|
1216
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
1217
|
+
throw new ConfigurationError(field, `The ${subject} must be an object`)
|
|
1218
|
+
for (const key of Reflect.ownKeys(value))
|
|
1219
|
+
if (typeof key !== "string" || !keys.includes(key))
|
|
1220
|
+
throw new ConfigurationError(
|
|
1221
|
+
field,
|
|
1222
|
+
typeof key === "string"
|
|
1223
|
+
? `Unsupported key ${JSON.stringify(key)} in the ${subject}`
|
|
1224
|
+
: `The ${subject} must not have symbol keys`,
|
|
1225
|
+
{
|
|
1226
|
+
hint:
|
|
1227
|
+
typeof key === "string"
|
|
1228
|
+
? unsupportedKeyHint(key, keys, "keys")
|
|
1229
|
+
: `Supported keys are ${keys.join(", ")}`,
|
|
1230
|
+
},
|
|
1231
|
+
)
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function validateArrayShape(
|
|
1235
|
+
value: readonly unknown[],
|
|
1236
|
+
field: "aliases" | "prefix" | "parser" | "command" | "help",
|
|
1237
|
+
): void {
|
|
1238
|
+
for (let index = 0; index < value.length; index += 1)
|
|
1239
|
+
if (!Object.hasOwn(value, index))
|
|
1240
|
+
throw new ConfigurationError(field, `${shapeSubjects[field]} must not have empty slots`)
|
|
1241
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1242
|
+
if (key === "length") continue
|
|
1243
|
+
if (typeof key !== "string" || !isArrayIndex(key, value.length))
|
|
1244
|
+
throw new ConfigurationError(
|
|
1245
|
+
field,
|
|
1246
|
+
`${shapeSubjects[field]} must be a plain array without extra properties`,
|
|
1247
|
+
)
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function isArrayIndex(key: string, length: number): boolean {
|
|
1252
|
+
if (!/^(?:0|[1-9]\d*)$/.test(key)) return false
|
|
1253
|
+
const index = Number(key)
|
|
1254
|
+
return Number.isSafeInteger(index) && index >= 0 && index < length
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function isCommandName(value: unknown): value is string {
|
|
1258
|
+
return typeof value === "string" && commandName.test(value)
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/** A memory cooldown store that never denies a new key for capacity, evicting as ExpiringKeys describes */
|
|
1262
|
+
class LocalMemoryCooldownStoreOwner implements LocalMemoryCooldownStore {
|
|
1263
|
+
readonly #entries: ExpiringKeys
|
|
1264
|
+
|
|
1265
|
+
constructor(readonly maxEntries: number) {
|
|
1266
|
+
this.#entries = new ExpiringKeys(maxEntries)
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
get size(): number {
|
|
1270
|
+
return this.#entries.size
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
claim(input: unknown, now = Date.now()): LocalMemoryResult<CommandCooldownClaim> {
|
|
1274
|
+
const request = validateMemoryRequest(input)
|
|
1275
|
+
if (request._tag === "Failure") return request
|
|
1276
|
+
const active = this.#entries.active(request.value.key, now)
|
|
1277
|
+
if (active !== undefined) return success(Object.freeze({ _tag: "CooldownActive", retryAtMs: active }))
|
|
1278
|
+
const retryAtMs = now + request.value.durationMs
|
|
1279
|
+
this.#entries.set(request.value.key, retryAtMs, now)
|
|
1280
|
+
return success(Object.freeze({ _tag: "CooldownAcquired", retryAtMs }))
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
sweep(now = Date.now()): number {
|
|
1284
|
+
return this.#entries.sweep(now)
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
clear(): void {
|
|
1288
|
+
this.#entries.clear()
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
function validateMemoryRequest(input: unknown): LocalMemoryResult<CommandCooldownRequest> {
|
|
1293
|
+
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
1294
|
+
return failure(new ConfigurationError("cooldown", "A cooldown claim requires an object"))
|
|
1295
|
+
const request = input as { key?: unknown; durationMs?: unknown }
|
|
1296
|
+
if (typeof request.key !== "string" || request.key.length === 0)
|
|
1297
|
+
return failure(new ConfigurationError("cooldown", "A cooldown key must be a nonempty string"))
|
|
1298
|
+
if (
|
|
1299
|
+
typeof request.durationMs !== "number" ||
|
|
1300
|
+
!Number.isSafeInteger(request.durationMs) ||
|
|
1301
|
+
request.durationMs < 1 ||
|
|
1302
|
+
request.durationMs > maxDurationMs
|
|
1303
|
+
)
|
|
1304
|
+
return failure(
|
|
1305
|
+
new ConfigurationError(
|
|
1306
|
+
"cooldown",
|
|
1307
|
+
`The cooldown option "durationMs" must be a positive safe integer of at most ${maxDurationMs} ms`,
|
|
1308
|
+
),
|
|
1309
|
+
)
|
|
1310
|
+
return success(Object.freeze({ key: request.key, durationMs: request.durationMs }))
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
function success<A>(value: A): LocalMemoryResult<A> {
|
|
1314
|
+
return Object.freeze({ _tag: "Success", value })
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function failure<A = never>(error: ConfigurationError): LocalMemoryResult<A> {
|
|
1318
|
+
return Object.freeze({ _tag: "Failure", error })
|
|
1319
|
+
}
|