@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,2448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client owner: Configuration, caches, REST, instance discovery, shard state and the one client lifetime, split into
|
|
3
|
+
* [shard loop](/projects/sdk/src/internal/client/shard-loop.ts), [intake](/projects/sdk/src/internal/client/intake.ts),
|
|
4
|
+
* [backoff](/projects/sdk/src/internal/client/backoff.ts) and [lifetime](/projects/sdk/src/internal/client/lifetime.ts).
|
|
5
|
+
* Invariant: The client manages its assigned sessions as one lifetime, spaces out Identify sends, coordinates initial state
|
|
6
|
+
* delivery with subscription setup and retains one lifetime outcome. Implements [SDK contracts: Connection and recovery](/docs/SDK-CONTRACTS.md#connection-and-recovery)
|
|
7
|
+
*/
|
|
8
|
+
import * as Cause from "effect/Cause"
|
|
9
|
+
import * as Clock from "effect/Clock"
|
|
10
|
+
import type * as Context from "effect/Context"
|
|
11
|
+
import * as Deferred from "effect/Deferred"
|
|
12
|
+
import * as Effect from "effect/Effect"
|
|
13
|
+
import * as Exit from "effect/Exit"
|
|
14
|
+
import * as Fiber from "effect/Fiber"
|
|
15
|
+
import * as Queue from "effect/Queue"
|
|
16
|
+
import * as Redacted from "effect/Redacted"
|
|
17
|
+
import * as Scope from "effect/Scope"
|
|
18
|
+
import * as Semaphore from "effect/Semaphore"
|
|
19
|
+
import * as Stream from "effect/Stream"
|
|
20
|
+
import type {
|
|
21
|
+
CacheDiagnostic,
|
|
22
|
+
CacheEntriesOptions,
|
|
23
|
+
CachedResources,
|
|
24
|
+
CacheKind,
|
|
25
|
+
ClientDiagnostics,
|
|
26
|
+
ConnectionState,
|
|
27
|
+
} from "#sdk/client"
|
|
28
|
+
import type { SessionSnapshot, ShardState } from "#sdk/sharding"
|
|
29
|
+
import {
|
|
30
|
+
admitReshard,
|
|
31
|
+
automaticShardPlan,
|
|
32
|
+
guildShardId,
|
|
33
|
+
guildsPerShard,
|
|
34
|
+
largerShardPlan,
|
|
35
|
+
type ShardPlan,
|
|
36
|
+
} from "./sharding.js"
|
|
37
|
+
import { guildList } from "./guild-lifecycle.js"
|
|
38
|
+
import type { IdentifyFields } from "./gateway/commands.js"
|
|
39
|
+
import { automaticIgnoredEvents, suppressedRegistrations } from "./gateway/event-dispatches.js"
|
|
40
|
+
import { maxClientPayloadBytes, Opcode, reservedClientOpcodes } from "./protocol/gateway.js"
|
|
41
|
+
import { MessageError, MessageOperationError, type MessageOperationFailure, type SendError } from "#sdk/message-errors"
|
|
42
|
+
import { replyInput, snapshotReference } from "./message.js"
|
|
43
|
+
import { identifier, record } from "./decode/primitives.js"
|
|
44
|
+
import { MessageCache } from "./cache.js"
|
|
45
|
+
import { unsupportedKeyHint } from "./suggest.js"
|
|
46
|
+
import { GuildCache, type ResourceKind, type Resources } from "./guild-cache.js"
|
|
47
|
+
import { ChannelCache } from "./channel-cache.js"
|
|
48
|
+
import { CacheChangeHub } from "./cache-changes.js"
|
|
49
|
+
import type { DefaultRestRequest, RestRequest, RestRequestFailure, RestResponse } from "#sdk/rest"
|
|
50
|
+
import { GatewaySendError, type GatewaySendFailure } from "#sdk/gateway"
|
|
51
|
+
import { UserCache, type UserResources } from "./user-cache.js"
|
|
52
|
+
import { PresenceOwner } from "./presence.js"
|
|
53
|
+
import { PresenceError, type PresenceInput, type PresenceFailure } from "#sdk/presence"
|
|
54
|
+
import {
|
|
55
|
+
UserOperationError,
|
|
56
|
+
type UserOperation,
|
|
57
|
+
type UserOperationOptions,
|
|
58
|
+
type User,
|
|
59
|
+
type DirectMessageChannel,
|
|
60
|
+
} from "#sdk/users"
|
|
61
|
+
import { decodeUser, directMessageFetch, userFetch, type UserRequest } from "./users.js"
|
|
62
|
+
import {
|
|
63
|
+
ChannelOperationError,
|
|
64
|
+
type ChannelOperation,
|
|
65
|
+
type ChannelOperationOptions,
|
|
66
|
+
type GuildChannel,
|
|
67
|
+
} from "#sdk/channels"
|
|
68
|
+
import { channelList, type ChannelRequest } from "./channels.js"
|
|
69
|
+
import type { WebhookRequest } from "./webhooks.js"
|
|
70
|
+
import type { WebhookOperation, WebhookOperationOptions } from "#sdk/webhooks"
|
|
71
|
+
import { FailureReporter, messageIds, primaryError, publicReport, throwIfErr, type InternalReport } from "./failures.js"
|
|
72
|
+
import {
|
|
73
|
+
AuthenticationError,
|
|
74
|
+
ClientBusyError,
|
|
75
|
+
ClientClosedError,
|
|
76
|
+
ConnectionError,
|
|
77
|
+
ConfigurationError,
|
|
78
|
+
ConnectionTimeoutError,
|
|
79
|
+
RateLimitError,
|
|
80
|
+
type ConnectError,
|
|
81
|
+
type ConnectionFailure,
|
|
82
|
+
} from "#sdk/errors"
|
|
83
|
+
import { type Configuration, validateConfiguration } from "./configuration.js"
|
|
84
|
+
import { InstanceResolver } from "./instance.js"
|
|
85
|
+
import type { LinkHelpers } from "#sdk/helpers"
|
|
86
|
+
import { CountOwner } from "./counts.js"
|
|
87
|
+
import { GatewayRequestBudget } from "./gateway-requests.js"
|
|
88
|
+
import { MemberChunkOwner, type MemberRequestGate } from "./member-chunks.js"
|
|
89
|
+
import { LogicalScheduler, type LogicalTimer, makeLogicalScheduler } from "./logical-scheduler.js"
|
|
90
|
+
import { AttemptFailure } from "./gateway.js"
|
|
91
|
+
import { sdkVersion } from "./logging.js"
|
|
92
|
+
import { EventBus } from "./events.js"
|
|
93
|
+
import { nowMs } from "./clock.js"
|
|
94
|
+
import { defaultTransport, type Transport } from "./transport/index.js"
|
|
95
|
+
import { identifySpacingMs, startupBudgetMs } from "./client/backoff.js"
|
|
96
|
+
import { clearCaches } from "./client/intake.js"
|
|
97
|
+
import { drainWork } from "./client/drain.js"
|
|
98
|
+
import { ScheduledTasks } from "./scheduled-tasks.js"
|
|
99
|
+
import {
|
|
100
|
+
closeAll,
|
|
101
|
+
defectsOnly,
|
|
102
|
+
formatDuration,
|
|
103
|
+
logShutdownEnd,
|
|
104
|
+
logShutdownStart,
|
|
105
|
+
superviseShards,
|
|
106
|
+
} from "./client/lifetime.js"
|
|
107
|
+
import { readCaller, suspendInput, suspendMarked } from "./defects.js"
|
|
108
|
+
import { runShardLoop, type ShardLoopHost, type ShardRuntime } from "./client/shard-loop.js"
|
|
109
|
+
import type { MessageCollector } from "./collector.js"
|
|
110
|
+
import type { ReactionCollector } from "./reaction-collector.js"
|
|
111
|
+
import {
|
|
112
|
+
GuildOperationError,
|
|
113
|
+
type GuildOperation,
|
|
114
|
+
type GuildOperationOptions,
|
|
115
|
+
type MemberReference,
|
|
116
|
+
type RoleReference,
|
|
117
|
+
} from "#sdk/guilds"
|
|
118
|
+
import { guildFetch, roleList, type GuildRequest } from "./guilds.js"
|
|
119
|
+
import { guildDeleteOwnMessages, guildLeave, ownDeletionOptions } from "./guild-lifecycle.js"
|
|
120
|
+
import { RestOwner } from "./rest.js"
|
|
121
|
+
import type { BotApplicationOperation, BotApplicationOperationOptions } from "#sdk/application"
|
|
122
|
+
import type { BotApplicationRequest } from "./application.js"
|
|
123
|
+
import type { ReactionEmojiInput, ReactionUsersQuery } from "#sdk/reactions"
|
|
124
|
+
import type { MessagePinsQuery } from "#sdk/pins"
|
|
125
|
+
import type { MessageSearchContext, MessageSearchQuery } from "#sdk/message-search"
|
|
126
|
+
import type { ClientLogger } from "./logging.js"
|
|
127
|
+
import type {
|
|
128
|
+
Message,
|
|
129
|
+
MessageCore,
|
|
130
|
+
MessageFields,
|
|
131
|
+
SelectedMessage,
|
|
132
|
+
EditMessageInput,
|
|
133
|
+
ForwardMessageInput,
|
|
134
|
+
MessageHistoryQuery,
|
|
135
|
+
MessageInput,
|
|
136
|
+
MessageOperationOptions,
|
|
137
|
+
MessageReference,
|
|
138
|
+
SendOptions,
|
|
139
|
+
} from "#sdk/messages"
|
|
140
|
+
import type {
|
|
141
|
+
Attachment,
|
|
142
|
+
AttachmentDownloadFailure,
|
|
143
|
+
AttachmentDownloadOptions,
|
|
144
|
+
AttachmentRefreshFailure,
|
|
145
|
+
AttachmentRefreshOptions,
|
|
146
|
+
RefreshedAttachmentUrl,
|
|
147
|
+
} from "#sdk/attachments"
|
|
148
|
+
import type { AttachmentDownloadSource } from "./rest.js"
|
|
149
|
+
import { InputValidationFailure, inputValidationFailure, unsupportedKeyFailure } from "#sdk/input-validation"
|
|
150
|
+
|
|
151
|
+
const cacheKinds = [
|
|
152
|
+
"messages",
|
|
153
|
+
"guilds",
|
|
154
|
+
"members",
|
|
155
|
+
"roles",
|
|
156
|
+
"channels",
|
|
157
|
+
"users",
|
|
158
|
+
"directMessages",
|
|
159
|
+
"emojis",
|
|
160
|
+
"stickers",
|
|
161
|
+
] as const satisfies readonly CacheKind[]
|
|
162
|
+
const emptyEntries = Object.freeze([])
|
|
163
|
+
/** The documented two-part CacheChange key format of each kind that has one */
|
|
164
|
+
const cachePairKeys = {
|
|
165
|
+
messages: "channelId:messageId",
|
|
166
|
+
members: "guildId:userId",
|
|
167
|
+
roles: "guildId:id",
|
|
168
|
+
emojis: "guildId:id",
|
|
169
|
+
stickers: "guildId:id",
|
|
170
|
+
} as const
|
|
171
|
+
/** The cache kind, or ConfigurationError for misuse */
|
|
172
|
+
function validCacheKind(kind: unknown): CacheKind {
|
|
173
|
+
if (!(cacheKinds as readonly unknown[]).includes(kind))
|
|
174
|
+
throw new ConfigurationError("kind", `The cache kind must be one of ${cacheKinds.join(", ")}`)
|
|
175
|
+
return kind as CacheKind
|
|
176
|
+
}
|
|
177
|
+
const disabledCacheDiagnostic: CacheDiagnostic = Object.freeze({
|
|
178
|
+
configured: false,
|
|
179
|
+
retainedEntries: 0,
|
|
180
|
+
accountedBytes: 0,
|
|
181
|
+
maxEntries: null,
|
|
182
|
+
maxBytes: null,
|
|
183
|
+
hits: 0,
|
|
184
|
+
misses: 0,
|
|
185
|
+
evictions: Object.freeze({ capacity: 0, expiry: 0 }),
|
|
186
|
+
})
|
|
187
|
+
const freezeCacheDiagnostic = (diagnostic: CacheDiagnostic | undefined): CacheDiagnostic =>
|
|
188
|
+
diagnostic === undefined
|
|
189
|
+
? disabledCacheDiagnostic
|
|
190
|
+
: Object.freeze({
|
|
191
|
+
configured: diagnostic.configured,
|
|
192
|
+
retainedEntries: diagnostic.retainedEntries,
|
|
193
|
+
accountedBytes: diagnostic.accountedBytes,
|
|
194
|
+
maxEntries: diagnostic.maxEntries,
|
|
195
|
+
maxBytes: diagnostic.maxBytes,
|
|
196
|
+
hits: diagnostic.hits,
|
|
197
|
+
misses: diagnostic.misses,
|
|
198
|
+
evictions: Object.freeze({
|
|
199
|
+
capacity: diagnostic.evictions.capacity,
|
|
200
|
+
expiry: diagnostic.evictions.expiry,
|
|
201
|
+
}),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const typingRefreshMs = 8_000
|
|
205
|
+
/** Time allowed for each session store save during shutdown */
|
|
206
|
+
const sessionSaveTimeoutMs = 5_000
|
|
207
|
+
/** Guild list page size used to count guilds for automatic sharding, the largest Fluxer accepts */
|
|
208
|
+
const guildCountPageSize = 200
|
|
209
|
+
/** Communities whose cache refill may fail before the refill stops */
|
|
210
|
+
const refillFailureLimit = 10
|
|
211
|
+
|
|
212
|
+
type GuildBuild<A> = () => GuildRequest<A> | InputValidationFailure
|
|
213
|
+
|
|
214
|
+
/** Marks a JSON encoding failure that this module detected itself, so it is not mistaken for a caller throw */
|
|
215
|
+
class CommandDataUnencodable {
|
|
216
|
+
constructor(readonly cause: unknown) {}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Encode gateway command data as JSON. A circular structure, a BigInt or nesting too deep for the stack returns the
|
|
221
|
+
* encoding failure. Any other throw comes from caller code run while encoding, such as a getter, a proxy trap or a
|
|
222
|
+
* toJSON method, and propagates unchanged
|
|
223
|
+
*/
|
|
224
|
+
function encodeCommandData(data: unknown): { readonly json: string | undefined } | { readonly failure: unknown } {
|
|
225
|
+
// Objects on the path from the root to the value being encoded, the same path JSON.stringify checks for cycles
|
|
226
|
+
const path: unknown[] = []
|
|
227
|
+
try {
|
|
228
|
+
const json = JSON.stringify(data, function (this: unknown, _key, value: unknown) {
|
|
229
|
+
path.length = path.lastIndexOf(this) + 1
|
|
230
|
+
if (typeof value === "bigint")
|
|
231
|
+
throw new CommandDataUnencodable(new TypeError("BigInt values cannot be encoded as JSON"))
|
|
232
|
+
if (typeof value === "object" && value !== null) {
|
|
233
|
+
if (path.includes(value))
|
|
234
|
+
throw new CommandDataUnencodable(new TypeError("Circular structures cannot be encoded as JSON"))
|
|
235
|
+
path.push(value)
|
|
236
|
+
}
|
|
237
|
+
return value
|
|
238
|
+
})
|
|
239
|
+
return { json }
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (error instanceof CommandDataUnencodable) return { failure: error.cause }
|
|
242
|
+
if (error instanceof RangeError) return { failure: error }
|
|
243
|
+
throw error
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Translate a failed guild-count read into the startup failure that connect and run report */
|
|
248
|
+
function automaticPlanFailure(
|
|
249
|
+
error: GuildOperationError | ClientClosedError,
|
|
250
|
+
startupTimeoutMs: number,
|
|
251
|
+
): ConnectionFailure {
|
|
252
|
+
// Closure during startup surfaces as ClientClosedError from connect, as for discovery
|
|
253
|
+
if (error instanceof ClientClosedError) return new ConnectionError("discovery", "closed")
|
|
254
|
+
if (error.status === 401) return new AuthenticationError()
|
|
255
|
+
if (error.reason === "rateLimit") return new RateLimitError("http", error.retryAfterMs)
|
|
256
|
+
if (error.reason === "timeout") return new ConnectionTimeoutError(startupTimeoutMs)
|
|
257
|
+
return new ConnectionError("discovery", error.reason === "response" ? "protocol" : "network", error.status, {
|
|
258
|
+
cause: error,
|
|
259
|
+
details: { detail: "the community count for automatic sharding could not be read" },
|
|
260
|
+
hint: "Check that the bot token can list the bot's communities, or set sharding.totalShards to a number",
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Internal fresh-Identify coordination used only by supervisor.child.run */
|
|
265
|
+
export interface IdentifyGate {
|
|
266
|
+
permit(shardId: number, send: () => void): Effect.Effect<void, ConnectionError>
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const identifyGates = new WeakMap<object, IdentifyGate>()
|
|
270
|
+
|
|
271
|
+
/** Attach a non-public gateway permit to the helper-owned client configuration object */
|
|
272
|
+
export function attachIdentifyGate<T extends object>(options: T, gate: IdentifyGate): T {
|
|
273
|
+
identifyGates.set(options, gate)
|
|
274
|
+
return options
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Internal sharing of Fluxer's account-wide limits with the other children of a supervisor, used only by supervisor.child.run */
|
|
278
|
+
export interface AccountLimits {
|
|
279
|
+
/** Pass a global pause this client learned from Fluxer, lasting waitMs from now, to the other children */
|
|
280
|
+
readonly shareGlobalPause: (waitMs: number) => void
|
|
281
|
+
/** Apply global pauses the other children learned. A pause that is still running reaches the listener at once */
|
|
282
|
+
readonly onGlobalPause: (listener: (waitMs: number) => void) => void
|
|
283
|
+
/** The parent's member request window */
|
|
284
|
+
readonly members: MemberRequestGate
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const accountLimits = new WeakMap<object, AccountLimits>()
|
|
288
|
+
|
|
289
|
+
/** Attach non-public account limit sharing to the helper-owned client configuration object */
|
|
290
|
+
export function attachAccountLimits<T extends object>(options: T, limits: AccountLimits): T {
|
|
291
|
+
accountLimits.set(options, limits)
|
|
292
|
+
return options
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export class ClientOwner<M extends MessageCore = Message> {
|
|
296
|
+
readonly presence: PresenceOwner
|
|
297
|
+
readonly #gatewayRequests = new GatewayRequestBudget()
|
|
298
|
+
readonly #identify = Semaphore.makeUnsafe(1)
|
|
299
|
+
/** SDK-private owner for fresh correlated gateway-count replies, with no count cache */
|
|
300
|
+
readonly counts: CountOwner
|
|
301
|
+
readonly memberChunks: MemberChunkOwner
|
|
302
|
+
|
|
303
|
+
setPresence(input: PresenceInput) {
|
|
304
|
+
// The presence owner marks its reads of the caller input, so its own faults stay SDK faults
|
|
305
|
+
return suspendMarked((): Effect.Effect<void, PresenceFailure> => {
|
|
306
|
+
if (this.#state === "Closing" || this.#state === "Closed") return Effect.fail(new ClientClosedError())
|
|
307
|
+
const failure = this.presence.set(input)
|
|
308
|
+
if (failure === undefined) return Effect.void
|
|
309
|
+
if (failure === false) return Effect.fail(new ClientClosedError())
|
|
310
|
+
return Effect.fail(new PresenceError({ reason: "input", inputValidation: failure.detail }))
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
setPresenceMembers(guildId: string, memberIds: readonly string[]) {
|
|
314
|
+
return suspendMarked((): Effect.Effect<void, PresenceFailure> => {
|
|
315
|
+
if (this.#state === "Closing" || this.#state === "Closed") return Effect.fail(new ClientClosedError())
|
|
316
|
+
const failure = this.presence.setMembers(guildId, memberIds)
|
|
317
|
+
if (failure === undefined) return Effect.void
|
|
318
|
+
if (failure === "limit") return Effect.fail(new PresenceError({ reason: "limit" }))
|
|
319
|
+
if (failure === false) return Effect.fail(new ClientClosedError())
|
|
320
|
+
return Effect.fail(new PresenceError({ reason: "input", inputValidation: failure.detail }))
|
|
321
|
+
})
|
|
322
|
+
}
|
|
323
|
+
#messageCollectors = new Set<MessageCollector<M>>()
|
|
324
|
+
trackMessageCollector(collector: MessageCollector<M>) {
|
|
325
|
+
this.#messageCollectors.add(collector)
|
|
326
|
+
// A collector that ends while the client closes stays until shutdown has collected its cleanup outcome
|
|
327
|
+
return () => void (this.#closingOrClosed() || this.#messageCollectors.delete(collector))
|
|
328
|
+
}
|
|
329
|
+
#reactionCollectors = new Set<ReactionCollector>()
|
|
330
|
+
trackReactionCollector(collector: ReactionCollector) {
|
|
331
|
+
this.#reactionCollectors.add(collector)
|
|
332
|
+
return () => void (this.#closingOrClosed() || this.#reactionCollectors.delete(collector))
|
|
333
|
+
}
|
|
334
|
+
#closingOrClosed() {
|
|
335
|
+
return this.#state === "Closing" || this.#state === "Closed"
|
|
336
|
+
}
|
|
337
|
+
readonly logging: ClientLogger
|
|
338
|
+
/** Client-wide failure reports, delivered to onError or logged */
|
|
339
|
+
readonly failures: FailureReporter
|
|
340
|
+
/** Tasks started with client.schedule, which end with this client */
|
|
341
|
+
readonly tasks: ScheduledTasks
|
|
342
|
+
readonly events = new EventBus<M>(
|
|
343
|
+
() => this.logging,
|
|
344
|
+
() => this.failures,
|
|
345
|
+
)
|
|
346
|
+
readonly decodeMessage: Configuration<M>["decodeMessage"]
|
|
347
|
+
readonly rest: RestOwner<M>
|
|
348
|
+
/** One owner-scoped immutable discovery result, independent of credentials and REST admission state */
|
|
349
|
+
readonly instance: InstanceResolver
|
|
350
|
+
readonly cache: MessageCache<M> | undefined
|
|
351
|
+
readonly resources: GuildCache | undefined
|
|
352
|
+
readonly channelCache: ChannelCache | undefined
|
|
353
|
+
readonly userCache: UserCache
|
|
354
|
+
/** Client-wide cache change notifications, shared by every cache this client owns */
|
|
355
|
+
readonly cacheChanges = new CacheChangeHub()
|
|
356
|
+
#configuration: Configuration<M> | undefined
|
|
357
|
+
#state: ConnectionState = "Disconnected"
|
|
358
|
+
/** Known shard plan. An automatic plan stays undefined until the first connect computes it */
|
|
359
|
+
#plan: ShardPlan | undefined
|
|
360
|
+
readonly #shards = new Map<number, ShardRuntime>()
|
|
361
|
+
readonly #shardListeners = new Map<number, Set<(state: ConnectionState) => void>>()
|
|
362
|
+
readonly #shardHost: ShardLoopHost<M>
|
|
363
|
+
#groupReady = false
|
|
364
|
+
/** Shards whose sessions shutdown saves, captured when shutdown begins */
|
|
365
|
+
#saveOnClose: ReadonlySet<number> | undefined
|
|
366
|
+
/** Logical time before which the next fresh Identify may not be sent */
|
|
367
|
+
#nextIdentifyAt = 0
|
|
368
|
+
#worker: Fiber.Fiber<void> | undefined
|
|
369
|
+
#workerExit: Exit.Exit<void, ConnectionFailure> | undefined
|
|
370
|
+
/** The service cleanup the worker ran when an explicit shutdown interrupted it, which that shutdown logs */
|
|
371
|
+
#workerCleanup: Exit.Exit<void> | undefined
|
|
372
|
+
#terminal = Deferred.makeUnsafe<void, ConnectionFailure>()
|
|
373
|
+
#shutdown = Deferred.makeUnsafe<void>()
|
|
374
|
+
#typingClosed = Deferred.makeUnsafe<void>()
|
|
375
|
+
#typing = new Map<symbol, Fiber.Fiber<never, MessageOperationFailure>>()
|
|
376
|
+
#listeners = new Set<(state: ConnectionState) => void>()
|
|
377
|
+
#managed = false
|
|
378
|
+
#shutdownStarted = false
|
|
379
|
+
/** Whether connect or run began, so shutdown records describe a client that did something */
|
|
380
|
+
#everStarted = false
|
|
381
|
+
/** Logical times of recent moves to a larger automatic plan, oldest first */
|
|
382
|
+
#reshards: number[] = []
|
|
383
|
+
/** The shard whose 4011 closure started a move to a larger plan, until the new plan is adopted */
|
|
384
|
+
#reshardShard: number | undefined
|
|
385
|
+
/** Shards that resumed a session from the session store, whose guild caches wait for a refill */
|
|
386
|
+
readonly #restoredShards = new Set<number>()
|
|
387
|
+
|
|
388
|
+
constructor(
|
|
389
|
+
configuration: Configuration<M>,
|
|
390
|
+
readonly scope: Scope.Scope,
|
|
391
|
+
failures: FailureReporter,
|
|
392
|
+
readonly logical: LogicalScheduler,
|
|
393
|
+
private readonly identifyGate: IdentifyGate | undefined,
|
|
394
|
+
/** Account limits shared with a supervisor's other children, only in a supervised child */
|
|
395
|
+
limits: AccountLimits | undefined,
|
|
396
|
+
/** Network implementations for REST, discovery and the gateway */
|
|
397
|
+
readonly transport: Transport = defaultTransport,
|
|
398
|
+
) {
|
|
399
|
+
this.decodeMessage = configuration.decodeMessage
|
|
400
|
+
this.#configuration = configuration
|
|
401
|
+
const route = (guildId: string) => this.shardIdForGuild(guildId)
|
|
402
|
+
this.presence = new PresenceOwner(
|
|
403
|
+
{
|
|
404
|
+
now: () => logical.now(),
|
|
405
|
+
set: (callback, delay) => logical.set(callback, delay, "presence"),
|
|
406
|
+
clear: (timer) => logical.clear(timer as LogicalTimer),
|
|
407
|
+
},
|
|
408
|
+
route,
|
|
409
|
+
)
|
|
410
|
+
// The configured initial presence is the first presence intent: Identify carries it and READY publishes it
|
|
411
|
+
if (configuration.gateway.presence !== undefined) this.presence.set(configuration.gateway.presence)
|
|
412
|
+
this.counts = new CountOwner(this.#gatewayRequests, route)
|
|
413
|
+
this.memberChunks = new MemberChunkOwner(
|
|
414
|
+
this.#gatewayRequests,
|
|
415
|
+
logical,
|
|
416
|
+
route,
|
|
417
|
+
limits?.members,
|
|
418
|
+
configuration.logging,
|
|
419
|
+
)
|
|
420
|
+
this.logging = configuration.logging
|
|
421
|
+
this.failures = failures
|
|
422
|
+
this.tasks = new ScheduledTasks(logical, failures, configuration.logging)
|
|
423
|
+
const reportCache = (error: unknown) => this.failures.report({ kind: "cache", error })
|
|
424
|
+
this.cache = configuration.cache
|
|
425
|
+
? new MessageCache(
|
|
426
|
+
configuration.cache,
|
|
427
|
+
(error, message) => this.failures.report({ kind: "cache", error, message: messageIds(message) }),
|
|
428
|
+
() => logical.now(),
|
|
429
|
+
logical,
|
|
430
|
+
this.cacheChanges,
|
|
431
|
+
)
|
|
432
|
+
: undefined
|
|
433
|
+
this.resources = Object.keys(configuration.resourceCache).length
|
|
434
|
+
? new GuildCache(configuration.resourceCache, () => logical.now(), logical, this.cacheChanges, reportCache)
|
|
435
|
+
: undefined
|
|
436
|
+
this.channelCache = configuration.channelCache
|
|
437
|
+
? new ChannelCache(configuration.channelCache, () => logical.now(), logical, this.cacheChanges, reportCache)
|
|
438
|
+
: undefined
|
|
439
|
+
this.userCache = new UserCache(
|
|
440
|
+
configuration.userCache,
|
|
441
|
+
() => logical.now(),
|
|
442
|
+
logical,
|
|
443
|
+
this.cacheChanges,
|
|
444
|
+
reportCache,
|
|
445
|
+
)
|
|
446
|
+
this.instance = new InstanceResolver(configuration.instance, scope, {
|
|
447
|
+
http: transport.http,
|
|
448
|
+
onDiscovered: (resolved) => {
|
|
449
|
+
this.#instanceLinks = resolved.links
|
|
450
|
+
const migration = resolved.domainMigration
|
|
451
|
+
if (!migration) return
|
|
452
|
+
const webapp = new URL(resolved.endpoints.webapp).host
|
|
453
|
+
this.logging.log({
|
|
454
|
+
level: migration.enabled ? "info" : "debug",
|
|
455
|
+
category: "lifecycle",
|
|
456
|
+
code: "lifecycle.domainMigration",
|
|
457
|
+
message: migration.enabled
|
|
458
|
+
? `The Fluxer instance announced a web domain migration, so links and OAuth URLs use the web app at ${webapp}`
|
|
459
|
+
: "The Fluxer instance announced a web domain migration that is switched off, so links and OAuth URLs are unchanged",
|
|
460
|
+
fields: {
|
|
461
|
+
enabled: migration.enabled,
|
|
462
|
+
webapp,
|
|
463
|
+
anonymousRolloutBasisPoints: migration.anonymousRolloutBasisPoints,
|
|
464
|
+
},
|
|
465
|
+
})
|
|
466
|
+
},
|
|
467
|
+
})
|
|
468
|
+
this.rest = new RestOwner({
|
|
469
|
+
cache: this.cache,
|
|
470
|
+
uploadMaxBytes: configuration.uploadMaxBytes,
|
|
471
|
+
resources: this.resources,
|
|
472
|
+
channels: this.channelCache,
|
|
473
|
+
users: this.userCache,
|
|
474
|
+
resolveInstance: () => this.instance.resolve(),
|
|
475
|
+
decodeMessage: configuration.decodeMessage,
|
|
476
|
+
logical,
|
|
477
|
+
logging: configuration.logging,
|
|
478
|
+
http: transport.http,
|
|
479
|
+
settings: configuration.rest,
|
|
480
|
+
shareGlobalPause: limits?.shareGlobalPause,
|
|
481
|
+
})
|
|
482
|
+
// Fluxer pauses every request of the account, so a pause another child learned holds this client's requests too
|
|
483
|
+
limits?.onGlobalPause((waitMs) => this.rest.pauseGlobal(waitMs))
|
|
484
|
+
if (configuration.sharding !== "auto") this.#adoptPlan(configuration.sharding, "Disconnected")
|
|
485
|
+
this.#shardHost = {
|
|
486
|
+
logging: this.logging,
|
|
487
|
+
instance: this.instance,
|
|
488
|
+
caches: this,
|
|
489
|
+
events: this.events,
|
|
490
|
+
presence: this.presence,
|
|
491
|
+
counts: this.counts,
|
|
492
|
+
memberChunks: this.memberChunks,
|
|
493
|
+
sockets: transport.sockets,
|
|
494
|
+
state: () => this.#state,
|
|
495
|
+
setShardState: (shard, state) => this.#setShardState(shard, state),
|
|
496
|
+
newSession: (shardId) => this.#clearShardGuilds(shardId),
|
|
497
|
+
identify: (shardId, send) => this.#identifyPermit(shardId, send),
|
|
498
|
+
identifyFields: (shardId) => this.#identifyFields(configuration, shardId),
|
|
499
|
+
sessions: configuration.sessions,
|
|
500
|
+
saveSessionOnClose: (shardId) =>
|
|
501
|
+
configuration.sessions !== undefined && this.#saveOnClose?.has(shardId) === true,
|
|
502
|
+
...(configuration.reshard ? { reshard: (shardId: number) => this.#acceptReshard(shardId) } : {}),
|
|
503
|
+
...(configuration.sessions && configuration.refillCaches
|
|
504
|
+
? { restored: (shardId: number) => void this.#restoredShards.add(shardId) }
|
|
505
|
+
: {}),
|
|
506
|
+
// Fluxer replays every missed dispatch on Resume except THREAD_LIST_SYNC, so cached threads may be stale
|
|
507
|
+
resumed: (shardId) => this.channelCache?.dropThreads(this.#shardGuilds(shardId)),
|
|
508
|
+
readyUser: (body, shardId) => this.#readyUser(body, shardId),
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Record the known plan, replace the shard runtimes and size REST concurrency for the local shard count. Called at
|
|
514
|
+
* creation, when an automatic plan is computed and when an automatic plan moves to a larger one
|
|
515
|
+
*/
|
|
516
|
+
#adoptPlan(plan: ShardPlan, state: ConnectionState) {
|
|
517
|
+
this.#plan = plan
|
|
518
|
+
this.#shards.clear()
|
|
519
|
+
this.rest.scaleConcurrency(plan.shardIds.length)
|
|
520
|
+
for (const shardId of plan.shardIds)
|
|
521
|
+
this.#shards.set(shardId, {
|
|
522
|
+
shardId,
|
|
523
|
+
state,
|
|
524
|
+
latency: null,
|
|
525
|
+
recovery: null,
|
|
526
|
+
session: { id: undefined, sequence: null },
|
|
527
|
+
submit: undefined,
|
|
528
|
+
gatewayUrl: undefined,
|
|
529
|
+
})
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Pace a fresh Identify across this client's shards, then wait for the supervisor's gate when one is configured.
|
|
534
|
+
* An application identify coordinator replaces the SDK's own spacing, and its permits are requested one at a time.
|
|
535
|
+
* A single unsharded, uncoordinated shard sends immediately
|
|
536
|
+
*/
|
|
537
|
+
#identifyPermit(shardId: number, send: () => void): Effect.Effect<void, AttemptFailure> {
|
|
538
|
+
const owner = this
|
|
539
|
+
const plan = owner.#plan!
|
|
540
|
+
const coordinator = owner.#configuration?.identifyCoordinator
|
|
541
|
+
const permit = coordinator
|
|
542
|
+
? // @effect-diagnostics-next-line unknownInEffectCatch:off
|
|
543
|
+
Effect.tryPromise({
|
|
544
|
+
try: (signal) => Promise.resolve(coordinator.permit(shardId, plan.totalShards, signal)),
|
|
545
|
+
catch: (error) => error,
|
|
546
|
+
}).pipe(
|
|
547
|
+
Effect.asVoid,
|
|
548
|
+
Effect.mapError((error) => {
|
|
549
|
+
owner.logging.log({
|
|
550
|
+
level: "error",
|
|
551
|
+
category: "lifecycle",
|
|
552
|
+
code: "lifecycle.identifyPermitFailed",
|
|
553
|
+
message: `The identify coordinator (sharding.identify) failed to grant shard ${shardId} permission to start a new session, so the SDK retries the connection as after a network failure`,
|
|
554
|
+
shardId,
|
|
555
|
+
error,
|
|
556
|
+
origin: "application",
|
|
557
|
+
})
|
|
558
|
+
return new AttemptFailure(
|
|
559
|
+
new ConnectionError("gateway", "network", null, {
|
|
560
|
+
cause: error,
|
|
561
|
+
details: {
|
|
562
|
+
detail: "the identify coordinator did not grant permission to start a new session",
|
|
563
|
+
},
|
|
564
|
+
hint: "Check the permit function of the sharding.identify coordinator",
|
|
565
|
+
}),
|
|
566
|
+
true,
|
|
567
|
+
)
|
|
568
|
+
}),
|
|
569
|
+
)
|
|
570
|
+
: Effect.void
|
|
571
|
+
const grant = () =>
|
|
572
|
+
owner.identifyGate
|
|
573
|
+
? owner.identifyGate
|
|
574
|
+
.permit(shardId, send)
|
|
575
|
+
.pipe(Effect.mapError((error) => new AttemptFailure(error, true)))
|
|
576
|
+
: Effect.sync(send)
|
|
577
|
+
if (!plan.identifyShards) return permit.pipe(Effect.andThen(Effect.suspend(grant)))
|
|
578
|
+
if (coordinator) return owner.#identify.withPermit(permit.pipe(Effect.andThen(Effect.suspend(grant))))
|
|
579
|
+
return owner.#identify.withPermit(
|
|
580
|
+
Effect.gen(function* () {
|
|
581
|
+
const clock = yield* Clock.Clock
|
|
582
|
+
const now = () => nowMs(clock)
|
|
583
|
+
// Pace actual Identify sends, including handshakes that finish out of order
|
|
584
|
+
while (owner.#nextIdentifyAt > now()) yield* Effect.sleep(owner.#nextIdentifyAt - now())
|
|
585
|
+
yield* grant()
|
|
586
|
+
owner.#nextIdentifyAt = now() + identifySpacingMs
|
|
587
|
+
}),
|
|
588
|
+
)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Identify fields for one new session: The current presence intent, the flags and the ignored dispatch names.
|
|
593
|
+
* Automatic filtering reads the event registrations present now, so later registrations take effect at the next
|
|
594
|
+
* new session only
|
|
595
|
+
*/
|
|
596
|
+
#identifyFields(configuration: Configuration<M>, shardId: number): IdentifyFields {
|
|
597
|
+
const gateway = configuration.gateway
|
|
598
|
+
const registered = this.events.registeredEvents()
|
|
599
|
+
let ignoredEvents: readonly string[]
|
|
600
|
+
if (gateway.ignoredEvents === "auto") {
|
|
601
|
+
ignoredEvents = automaticIgnoredEvents(registered, gateway.automaticNeeds)
|
|
602
|
+
} else {
|
|
603
|
+
ignoredEvents = gateway.ignoredEvents
|
|
604
|
+
const suppressed = suppressedRegistrations(registered, new Set(ignoredEvents))
|
|
605
|
+
if (suppressed.length)
|
|
606
|
+
this.logging.log({
|
|
607
|
+
level: "warn",
|
|
608
|
+
category: "gateway",
|
|
609
|
+
code: "gateway.ignoredEventRegistered",
|
|
610
|
+
message: `Handlers for ${suppressed.join(", ")} receive nothing on shard ${shardId}, because gateway.ignoredEvents suppresses every gateway event that delivers them`,
|
|
611
|
+
shardId,
|
|
612
|
+
fields: { events: suppressed.join(",") },
|
|
613
|
+
})
|
|
614
|
+
}
|
|
615
|
+
if (ignoredEvents.length && this.logging.enabled("debug", "gateway"))
|
|
616
|
+
this.logging.log({
|
|
617
|
+
level: "debug",
|
|
618
|
+
category: "gateway",
|
|
619
|
+
code: "gateway.identifyFilter",
|
|
620
|
+
message: `Shard ${shardId} asks Fluxer not to send ${ignoredEvents.length} gateway event type${ignoredEvents.length === 1 ? "" : "s"}`,
|
|
621
|
+
shardId,
|
|
622
|
+
fields: { ignoredEvents: ignoredEvents.join(","), automatic: gateway.ignoredEvents === "auto" },
|
|
623
|
+
})
|
|
624
|
+
return {
|
|
625
|
+
presence: this.presence.identifyPresence(),
|
|
626
|
+
ignoredEvents,
|
|
627
|
+
flags: gateway.flags,
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
get state(): ConnectionState {
|
|
631
|
+
return this.#state
|
|
632
|
+
}
|
|
633
|
+
/** Default deadline for multi-step REST workflows, from rest.defaultTimeoutMs */
|
|
634
|
+
get defaultTimeoutMs(): number {
|
|
635
|
+
return this.rest.defaultTimeoutMs
|
|
636
|
+
}
|
|
637
|
+
get gatewayLatencyMs(): number | null {
|
|
638
|
+
if (this.#state !== "Connected") return null
|
|
639
|
+
let maximum = 0
|
|
640
|
+
for (const shard of this.#shards.values()) {
|
|
641
|
+
if (shard.latency === null) return null
|
|
642
|
+
maximum = Math.max(maximum, shard.latency)
|
|
643
|
+
}
|
|
644
|
+
return maximum
|
|
645
|
+
}
|
|
646
|
+
get shards(): readonly ShardState[] {
|
|
647
|
+
return Object.freeze(
|
|
648
|
+
[...this.#shards.values()].map((shard) =>
|
|
649
|
+
Object.freeze({
|
|
650
|
+
shardId: shard.shardId,
|
|
651
|
+
state: shard.state,
|
|
652
|
+
gatewayLatencyMs: shard.latency,
|
|
653
|
+
recovery:
|
|
654
|
+
shard.recovery === null
|
|
655
|
+
? null
|
|
656
|
+
: Object.freeze({
|
|
657
|
+
phase: shard.recovery.phase,
|
|
658
|
+
attempt: shard.recovery.attempt,
|
|
659
|
+
retryDelayMs: shard.recovery.retryDelayMs,
|
|
660
|
+
}),
|
|
661
|
+
}),
|
|
662
|
+
),
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
diagnostics(): ClientDiagnostics {
|
|
666
|
+
const rest = this.rest.diagnostics()
|
|
667
|
+
const caches = Object.freeze({
|
|
668
|
+
messages: freezeCacheDiagnostic(this.cache?.diagnostics()),
|
|
669
|
+
guilds: freezeCacheDiagnostic(this.resources?.diagnostics("guilds")),
|
|
670
|
+
members: freezeCacheDiagnostic(this.resources?.diagnostics("members")),
|
|
671
|
+
roles: freezeCacheDiagnostic(this.resources?.diagnostics("roles")),
|
|
672
|
+
channels: freezeCacheDiagnostic(this.channelCache?.diagnostics()),
|
|
673
|
+
users: freezeCacheDiagnostic(this.userCache.diagnostics("users")),
|
|
674
|
+
directMessages: freezeCacheDiagnostic(this.userCache.diagnostics("directMessages")),
|
|
675
|
+
emojis: freezeCacheDiagnostic(this.resources?.diagnostics("emojis")),
|
|
676
|
+
stickers: freezeCacheDiagnostic(this.resources?.diagnostics("stickers")),
|
|
677
|
+
})
|
|
678
|
+
return Object.freeze({
|
|
679
|
+
state: this.state,
|
|
680
|
+
gatewayLatencyMs: this.gatewayLatencyMs,
|
|
681
|
+
shards: this.shards,
|
|
682
|
+
rest: Object.freeze({
|
|
683
|
+
activeRequests: rest.activeRequests,
|
|
684
|
+
activeCapacity: rest.activeCapacity,
|
|
685
|
+
queuedRequests: rest.queuedRequests,
|
|
686
|
+
queuedCapacity: rest.queuedCapacity,
|
|
687
|
+
queuedJsonBytes: rest.queuedJsonBytes,
|
|
688
|
+
queuedJsonByteCapacity: rest.queuedJsonByteCapacity,
|
|
689
|
+
}),
|
|
690
|
+
uploads: Object.freeze({ reservedBytes: rest.reservedUploadBytes, byteCapacity: rest.uploadByteCapacity }),
|
|
691
|
+
gatewayRequests: Object.freeze(this.#gatewayRequests.diagnostics()),
|
|
692
|
+
events: Object.freeze(this.events.diagnostics()),
|
|
693
|
+
caches,
|
|
694
|
+
counters: this.logging.counters(),
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
cacheEntries<K extends CacheKind>(
|
|
698
|
+
kind: K,
|
|
699
|
+
options?: CacheEntriesOptions,
|
|
700
|
+
): Effect.Effect<readonly CachedResources<M>[K][], ConfigurationError> {
|
|
701
|
+
return suspendInput(() => {
|
|
702
|
+
if (!cacheKinds.includes(kind))
|
|
703
|
+
return Effect.fail(
|
|
704
|
+
new ConfigurationError("kind", `The cache kind must be one of ${cacheKinds.join(", ")}`),
|
|
705
|
+
)
|
|
706
|
+
if (options !== undefined && (typeof options !== "object" || options === null || Array.isArray(options)))
|
|
707
|
+
return Effect.fail(new ConfigurationError("limit", "Cache entry options must contain only limit"))
|
|
708
|
+
const unsupported = options && Object.keys(options).find((key) => key !== "limit")
|
|
709
|
+
if (unsupported !== undefined)
|
|
710
|
+
return Effect.fail(
|
|
711
|
+
new ConfigurationError(
|
|
712
|
+
"limit",
|
|
713
|
+
`Unsupported option ${JSON.stringify(unsupported)} in the cache entry options`,
|
|
714
|
+
{ hint: unsupportedKeyHint(unsupported, ["limit"]) },
|
|
715
|
+
),
|
|
716
|
+
)
|
|
717
|
+
// Read limit once, so the validated limit is the one applied
|
|
718
|
+
const limitInput = options?.limit
|
|
719
|
+
const limit = limitInput === undefined ? 100 : limitInput
|
|
720
|
+
if (typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1 || limit > 1_000)
|
|
721
|
+
return Effect.fail(
|
|
722
|
+
new ConfigurationError("limit", "Cache entry limit must be a safe integer from 1 through 1,000"),
|
|
723
|
+
)
|
|
724
|
+
return Effect.sync(() => this.#cacheEntries(kind, limit))
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
/** The public cache.clear: Every cache, or one validated kind */
|
|
728
|
+
clearCache(kind?: CacheKind) {
|
|
729
|
+
if (kind === undefined) {
|
|
730
|
+
clearCaches(this)
|
|
731
|
+
return
|
|
732
|
+
}
|
|
733
|
+
const valid = validCacheKind(kind)
|
|
734
|
+
switch (valid) {
|
|
735
|
+
case "messages":
|
|
736
|
+
return this.cache?.clear()
|
|
737
|
+
case "guilds":
|
|
738
|
+
case "members":
|
|
739
|
+
case "roles":
|
|
740
|
+
case "emojis":
|
|
741
|
+
case "stickers":
|
|
742
|
+
return this.resources?.clearKind(valid)
|
|
743
|
+
case "channels":
|
|
744
|
+
return this.channelCache?.clear()
|
|
745
|
+
case "users":
|
|
746
|
+
case "directMessages":
|
|
747
|
+
return this.userCache.invalidate(valid)
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/** The public cache.delete: One entry by its validated CacheChange key */
|
|
751
|
+
deleteCacheEntry(kind: CacheKind, key: string) {
|
|
752
|
+
const valid = validCacheKind(kind)
|
|
753
|
+
const format = cachePairKeys[valid as keyof typeof cachePairKeys] as string | undefined
|
|
754
|
+
const parts = typeof key === "string" ? key.split(":") : []
|
|
755
|
+
if (parts.length !== (format === undefined ? 1 : 2) || !parts.every(identifier))
|
|
756
|
+
throw new ConfigurationError("key", `A ${valid} cache key must be ${format ?? "a decimal ID"}`)
|
|
757
|
+
const [first, second] = parts as [string, string | undefined]
|
|
758
|
+
switch (valid) {
|
|
759
|
+
case "messages":
|
|
760
|
+
return this.cache?.deleteMany(first, [second!])
|
|
761
|
+
case "guilds":
|
|
762
|
+
case "members":
|
|
763
|
+
case "roles":
|
|
764
|
+
case "emojis":
|
|
765
|
+
case "stickers":
|
|
766
|
+
// A community's own key is its ID, which the guild cache stores as both community and resource ID
|
|
767
|
+
return this.resources?.delete(valid, first, second ?? first)
|
|
768
|
+
case "channels":
|
|
769
|
+
return this.channelCache?.delete(first)
|
|
770
|
+
case "users":
|
|
771
|
+
case "directMessages":
|
|
772
|
+
return this.userCache.invalidate(valid, first)
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
#cacheEntries<K extends CacheKind>(kind: K, limit: number): readonly CachedResources<M>[K][] {
|
|
776
|
+
switch (kind) {
|
|
777
|
+
case "messages":
|
|
778
|
+
return (this.cache?.entries(limit) ?? emptyEntries) as readonly CachedResources<M>[K][]
|
|
779
|
+
case "guilds":
|
|
780
|
+
case "members":
|
|
781
|
+
case "roles":
|
|
782
|
+
case "emojis":
|
|
783
|
+
case "stickers":
|
|
784
|
+
return (this.resources?.entries(kind, limit) ?? emptyEntries) as readonly CachedResources<M>[K][]
|
|
785
|
+
case "channels":
|
|
786
|
+
return (this.channelCache?.entries(limit) ?? emptyEntries) as readonly CachedResources<M>[K][]
|
|
787
|
+
case "users":
|
|
788
|
+
case "directMessages":
|
|
789
|
+
return this.userCache.entries(kind, limit) as readonly CachedResources<M>[K][]
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
shardIdForGuild(guildId: string): number | undefined {
|
|
793
|
+
if (this.#plan === undefined) return undefined
|
|
794
|
+
const id = guildShardId(guildId, this.#plan.totalShards)
|
|
795
|
+
return this.#shards.has(id) ? id : undefined
|
|
796
|
+
}
|
|
797
|
+
/** The public client.shardIdForGuild: shardIdForGuild after checking the caller's ID */
|
|
798
|
+
ownedShardForGuild(guildId: unknown): number | undefined {
|
|
799
|
+
if (!identifier(guildId)) throw new ConfigurationError("guildId", "Guild IDs must be decimal strings")
|
|
800
|
+
return this.shardIdForGuild(guildId)
|
|
801
|
+
}
|
|
802
|
+
gatewayState(guildId?: string): ConnectionState {
|
|
803
|
+
if (guildId === undefined) return this.#state
|
|
804
|
+
if (this.#state === "Closing" || this.#state === "Closed") return this.#state
|
|
805
|
+
const id = this.shardIdForGuild(guildId)
|
|
806
|
+
return id === undefined ? "Disconnected" : this.#shards.get(id)!.state
|
|
807
|
+
}
|
|
808
|
+
subscribeGateway(guildId: string | undefined, listener: (state: ConnectionState) => void) {
|
|
809
|
+
if (guildId === undefined) return this.subscribe(listener)
|
|
810
|
+
const id = this.shardIdForGuild(guildId)
|
|
811
|
+
if (id === undefined) {
|
|
812
|
+
listener(this.gatewayState(guildId))
|
|
813
|
+
return () => {}
|
|
814
|
+
}
|
|
815
|
+
let listeners = this.#shardListeners.get(id)
|
|
816
|
+
if (!listeners) this.#shardListeners.set(id, (listeners = new Set()))
|
|
817
|
+
if (this.#state !== "Closed") listeners.add(listener)
|
|
818
|
+
listener(this.gatewayState(guildId))
|
|
819
|
+
return () => {
|
|
820
|
+
listeners.delete(listener)
|
|
821
|
+
// A move to a larger plan replaces the listener sets, and a new set for the same ID must stay
|
|
822
|
+
if (!listeners.size && this.#shardListeners.get(id) === listeners) this.#shardListeners.delete(id)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
#shardGuilds(shardId: number) {
|
|
826
|
+
const totalShards = this.#plan?.totalShards ?? 1
|
|
827
|
+
return (guildId: string | null | undefined) =>
|
|
828
|
+
guildId === undefined || (guildId === null ? shardId === 0 : guildShardId(guildId, totalShards) === shardId)
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Invalidate a shard's cached observations when its connection is lost. Guild-scoped entries stay while the shard
|
|
832
|
+
* can still resume, because Fluxer's Resume replays every missed dispatch or refuses the session, and
|
|
833
|
+
* #clearShardGuilds releases them once a new session is needed. Messages and users are cleared at once
|
|
834
|
+
*/
|
|
835
|
+
#gapShard(shardId: number, resumable: boolean) {
|
|
836
|
+
const affects = this.#shardGuilds(shardId)
|
|
837
|
+
this.cache?.gap(affects)
|
|
838
|
+
if (resumable) {
|
|
839
|
+
this.resources?.pause(affects)
|
|
840
|
+
this.channelCache?.pause(affects)
|
|
841
|
+
} else this.#clearShardGuilds(shardId)
|
|
842
|
+
this.userCache.gap(affects)
|
|
843
|
+
this.counts.detach(shardId)
|
|
844
|
+
this.memberChunks.detach(shardId)
|
|
845
|
+
this.presence.detach(shardId)
|
|
846
|
+
}
|
|
847
|
+
/** Release a shard's guild-scoped entries when its next connection starts a new session, which replays nothing */
|
|
848
|
+
#clearShardGuilds(shardId: number) {
|
|
849
|
+
const affects = this.#shardGuilds(shardId)
|
|
850
|
+
this.resources?.gap(affects)
|
|
851
|
+
this.channelCache?.gap(affects)
|
|
852
|
+
}
|
|
853
|
+
#setShardState(shard: ShardRuntime, state: ConnectionState) {
|
|
854
|
+
if (this.#state === "Closing" || this.#state === "Closed" || shard.state === state) return
|
|
855
|
+
if (state === "Recovering") this.#gapShard(shard.shardId, true)
|
|
856
|
+
shard.state = state
|
|
857
|
+
if (state !== "Connected") shard.latency = null
|
|
858
|
+
for (const listener of this.#shardListeners.get(shard.shardId) ?? []) listener(state)
|
|
859
|
+
if ([...this.#shards.values()].every((value) => value.state === "Connected")) {
|
|
860
|
+
this.#groupReady = true
|
|
861
|
+
this.#setState("Connected")
|
|
862
|
+
} else this.#setState(this.#groupReady ? "Recovering" : "Connecting")
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
#setState(state: ConnectionState) {
|
|
866
|
+
if (this.#state === state) return
|
|
867
|
+
if (state === "Disconnected") {
|
|
868
|
+
// A reusable startup can have delivered events from a ready sibling before the group failed
|
|
869
|
+
for (const shard of this.#shards.values())
|
|
870
|
+
if (shard.state === "Connected" || shard.state === "Recovering") this.#gapShard(shard.shardId, false)
|
|
871
|
+
}
|
|
872
|
+
if (state === "Closing" || state === "Closed") this.cache?.close()
|
|
873
|
+
if (state === "Closing" || state === "Closed") this.counts.close()
|
|
874
|
+
if (state === "Closing" || state === "Closed") this.memberChunks.close()
|
|
875
|
+
if (state === "Closing" || state === "Closed") this.resources?.close()
|
|
876
|
+
if (state === "Closing" || state === "Closed") this.userCache.close()
|
|
877
|
+
if (state === "Closing" || state === "Closed") this.presence.close()
|
|
878
|
+
if (state === "Closing" || state === "Closed") this.channelCache?.close()
|
|
879
|
+
this.#state = state
|
|
880
|
+
if (state === "Disconnected" || state === "Closing" || state === "Closed") {
|
|
881
|
+
for (const shard of this.#shards.values()) {
|
|
882
|
+
shard.state = state
|
|
883
|
+
shard.latency = null
|
|
884
|
+
if (state !== "Closing") shard.session = { id: undefined, sequence: null }
|
|
885
|
+
shard.recovery = null
|
|
886
|
+
for (const listener of this.#shardListeners.get(shard.shardId) ?? []) listener(state)
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
for (const listener of this.#listeners) listener(state)
|
|
890
|
+
if (state === "Closed") {
|
|
891
|
+
this.#listeners.clear()
|
|
892
|
+
this.#shardListeners.clear()
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
subscribe(listener: (state: ConnectionState) => void) {
|
|
897
|
+
if (this.#state !== "Closed") this.#listeners.add(listener)
|
|
898
|
+
listener(this.#state)
|
|
899
|
+
return () => {
|
|
900
|
+
this.#listeners.delete(listener)
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
observeState() {
|
|
905
|
+
const owner = this
|
|
906
|
+
return Stream.unwrap(
|
|
907
|
+
Effect.gen(function* () {
|
|
908
|
+
const queue = yield* Queue.sliding<ConnectionState, Cause.Done>(1)
|
|
909
|
+
let initial: ConnectionState | undefined
|
|
910
|
+
const unsubscribe = owner.subscribe((state) => {
|
|
911
|
+
if (initial === undefined) initial = state
|
|
912
|
+
else Queue.offerUnsafe(queue, state)
|
|
913
|
+
if (state === "Closed") Queue.endUnsafe(queue)
|
|
914
|
+
})
|
|
915
|
+
yield* Effect.addFinalizer(() => Effect.sync(unsubscribe).pipe(Effect.andThen(Queue.shutdown(queue))))
|
|
916
|
+
return Stream.concat(Stream.succeed(initial!), Stream.fromQueue(queue))
|
|
917
|
+
}),
|
|
918
|
+
)
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
#finish() {
|
|
922
|
+
Deferred.doneUnsafe(this.#typingClosed, Effect.void)
|
|
923
|
+
this.events.stop()
|
|
924
|
+
this.rest.stop()
|
|
925
|
+
if (this.#configuration) Redacted.wipeUnsafe(this.#configuration.token)
|
|
926
|
+
this.#configuration = undefined
|
|
927
|
+
this.#setState("Closed")
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
#selfUserId: string | undefined
|
|
931
|
+
/** The bot account ID once READY or a self read supplied it, without starting a read, for gateway cache intake */
|
|
932
|
+
get botUserId(): string | undefined {
|
|
933
|
+
return this.#selfUserId
|
|
934
|
+
}
|
|
935
|
+
/** The bot's own public account from the latest READY that carried a complete one, for users.getSelf */
|
|
936
|
+
#selfUser: User | undefined
|
|
937
|
+
/** The one users.fetchSelf read in flight, which concurrent mentions share */
|
|
938
|
+
#selfUserRead: Deferred.Deferred<string | undefined> | undefined
|
|
939
|
+
/** Consecutive failed reads, and the logical time before which no new read starts */
|
|
940
|
+
#selfUserFailures = 0
|
|
941
|
+
#selfUserRetryAtMs = 0
|
|
942
|
+
static readonly #selfUserBackoffMs = 5_000
|
|
943
|
+
static readonly #selfUserMaxBackoffMs = 300_000
|
|
944
|
+
|
|
945
|
+
/** The bot's username from the latest READY, for the connected record */
|
|
946
|
+
#readyName: string | undefined
|
|
947
|
+
/** Each shard's community count from its latest READY, for the connected record */
|
|
948
|
+
readonly #readyCommunities = new Map<number, number>()
|
|
949
|
+
/** Links for the discovered instance, so the connected record can offer an installation link */
|
|
950
|
+
#instanceLinks: LinkHelpers | undefined
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Keep the bot account ID from a READY body's user, so mention prefixes need no REST read, its public account for
|
|
954
|
+
* users.getSelf when the user is complete, so an incomplete one keeps the previous account, and the bot's username and the shard's community count for the connected record. For a bot, READY
|
|
955
|
+
* lists each community as unavailable
|
|
956
|
+
*/
|
|
957
|
+
#readyUser(body: unknown, shardId: number) {
|
|
958
|
+
const user = record(body) && record(body.user) ? body.user : undefined
|
|
959
|
+
if (identifier(user?.id) && user.id !== "0") this.#selfUserId = user.id
|
|
960
|
+
const self = decodeUser(user)
|
|
961
|
+
if (self !== undefined && self.id !== "0") this.#selfUser = self
|
|
962
|
+
if (typeof user?.username === "string" && user.username.length > 0) this.#readyName = user.username
|
|
963
|
+
if (record(body) && Array.isArray(body.guilds)) this.#readyCommunities.set(shardId, body.guilds.length)
|
|
964
|
+
else this.#readyCommunities.delete(shardId)
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Log once per start that every local shard is connected, naming the bot and its community count when READY
|
|
969
|
+
* supplied them. A bot that owns every shard and is in no community gets a Warn with an installation link instead
|
|
970
|
+
*/
|
|
971
|
+
#logConnected(context: Context.Context<never>) {
|
|
972
|
+
const plan = this.#plan
|
|
973
|
+
if (plan === undefined) return
|
|
974
|
+
const counts = plan.shardIds.map((shardId) => this.#readyCommunities.get(shardId))
|
|
975
|
+
const communities = counts.every((count) => count !== undefined)
|
|
976
|
+
? counts.reduce<number>((total, count) => total + count!, 0)
|
|
977
|
+
: undefined
|
|
978
|
+
const everyShard = plan.shardIds.length === plan.totalShards
|
|
979
|
+
const subject = `Connected to Fluxer${this.#readyName === undefined ? "" : ` as ${this.#readyName}`}`
|
|
980
|
+
const empty = communities === 0 && everyShard
|
|
981
|
+
const message = empty
|
|
982
|
+
? `${subject}, but the bot is not in any community yet. ${this.#inviteText()}`
|
|
983
|
+
: communities === undefined
|
|
984
|
+
? subject
|
|
985
|
+
: `${subject} in ${communities} ${communities === 1 ? "community" : "communities"}${everyShard ? "" : " on this client's shards"}`
|
|
986
|
+
this.logging.log(
|
|
987
|
+
{
|
|
988
|
+
level: empty ? "warn" : "info",
|
|
989
|
+
category: "lifecycle",
|
|
990
|
+
code: "lifecycle.connected",
|
|
991
|
+
message,
|
|
992
|
+
fields: { communities: communities ?? null, shards: plan.shardIds.length },
|
|
993
|
+
},
|
|
994
|
+
context,
|
|
995
|
+
)
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
/** Where to invite the bot: Its installation link when the bot account ID and instance are known */
|
|
999
|
+
#inviteText(): string {
|
|
1000
|
+
const id = this.#selfUserId
|
|
1001
|
+
if (id === undefined || this.#instanceLinks === undefined)
|
|
1002
|
+
return "Invite it with an installation link that uses the application ID"
|
|
1003
|
+
try {
|
|
1004
|
+
return `Invite it by opening ${this.#instanceLinks.installation(id)}`
|
|
1005
|
+
} catch {
|
|
1006
|
+
// allow-silent: An ID the link helper rejects still gets the general instruction
|
|
1007
|
+
return "Invite it with an installation link that uses the application ID"
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/** The bot's own public account from the latest READY, kept for the client lifetime. It never fails */
|
|
1012
|
+
getSelf(): Effect.Effect<User | undefined> {
|
|
1013
|
+
return Effect.sync(() => this.#selfUser)
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* The bot account ID for command mention prefixes, kept for the client lifetime.
|
|
1018
|
+
* READY normally supplies it. Until then it is read through users.fetchSelf, and concurrent callers share one read.
|
|
1019
|
+
* A failed read is logged at Warn and produces undefined, and callers get undefined without a new read for a backoff
|
|
1020
|
+
* that starts at 5 seconds and doubles per consecutive failure up to 5 minutes, unless READY supplies the ID first
|
|
1021
|
+
*/
|
|
1022
|
+
selfUserId(): Effect.Effect<string | undefined> {
|
|
1023
|
+
return Effect.suspend(() => {
|
|
1024
|
+
if (this.#selfUserId !== undefined) return Effect.succeed(this.#selfUserId)
|
|
1025
|
+
if (this.#selfUserRead !== undefined) return Deferred.await(this.#selfUserRead)
|
|
1026
|
+
if (this.logical.now() < this.#selfUserRetryAtMs) return Effect.succeed(undefined)
|
|
1027
|
+
const read = Deferred.makeUnsafe<string | undefined>()
|
|
1028
|
+
this.#selfUserRead = read
|
|
1029
|
+
return this.user("users.fetchSelf", () => userFetch("@me")).pipe(
|
|
1030
|
+
Effect.map((user) => {
|
|
1031
|
+
this.#selfUserFailures = 0
|
|
1032
|
+
return (this.#selfUserId = user.id)
|
|
1033
|
+
}),
|
|
1034
|
+
Effect.catch((error) =>
|
|
1035
|
+
Effect.sync(() => {
|
|
1036
|
+
this.#selfUserFailures += 1
|
|
1037
|
+
const retryInMs = Math.min(
|
|
1038
|
+
ClientOwner.#selfUserMaxBackoffMs,
|
|
1039
|
+
ClientOwner.#selfUserBackoffMs * 2 ** (this.#selfUserFailures - 1),
|
|
1040
|
+
)
|
|
1041
|
+
this.#selfUserRetryAtMs = this.logical.now() + retryInMs
|
|
1042
|
+
this.logging.log({
|
|
1043
|
+
level: "warn",
|
|
1044
|
+
category: "commands",
|
|
1045
|
+
code: "commands.mentionPrefixUnavailable",
|
|
1046
|
+
message: `The bot's user ID could not be read, so commands that start with a mention of the bot are ignored for ${retryInMs / 1_000} s. The next such command after that reads the ID again`,
|
|
1047
|
+
error,
|
|
1048
|
+
fields: { retryInMs, failures: this.#selfUserFailures },
|
|
1049
|
+
})
|
|
1050
|
+
return undefined
|
|
1051
|
+
}),
|
|
1052
|
+
),
|
|
1053
|
+
// Waiters get the reader's result. An interrupted or defective read releases them with undefined and
|
|
1054
|
+
// lets the next mention read again
|
|
1055
|
+
Effect.onExit((exit) =>
|
|
1056
|
+
Effect.sync(() => {
|
|
1057
|
+
this.#selfUserRead = undefined
|
|
1058
|
+
Deferred.doneUnsafe(read, Effect.succeed(Exit.isSuccess(exit) ? exit.value : undefined))
|
|
1059
|
+
}),
|
|
1060
|
+
),
|
|
1061
|
+
)
|
|
1062
|
+
})
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
user<A>(
|
|
1066
|
+
operation: UserOperation,
|
|
1067
|
+
build: () => UserRequest<A> | InputValidationFailure,
|
|
1068
|
+
options?: UserOperationOptions,
|
|
1069
|
+
) {
|
|
1070
|
+
return suspendInput(() => {
|
|
1071
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1072
|
+
return Effect.fail(new ClientClosedError())
|
|
1073
|
+
const request = build()
|
|
1074
|
+
if (request instanceof InputValidationFailure)
|
|
1075
|
+
return Effect.fail(
|
|
1076
|
+
new UserOperationError({
|
|
1077
|
+
operation,
|
|
1078
|
+
reason: "input",
|
|
1079
|
+
outcome: "notDispatched",
|
|
1080
|
+
inputValidation: request.detail,
|
|
1081
|
+
}),
|
|
1082
|
+
)
|
|
1083
|
+
const owner = this
|
|
1084
|
+
const token = this.#configuration.token
|
|
1085
|
+
// Read the caller's options here, where a throwing getter is reported as an application fault
|
|
1086
|
+
const timeout = options?.timeoutMs ?? owner.rest.defaultTimeoutMs
|
|
1087
|
+
const unsupportedOption = record(options)
|
|
1088
|
+
? unsupportedKeyFailure(options, ["timeoutMs", "signal"], "options", "the operation options")
|
|
1089
|
+
: undefined
|
|
1090
|
+
return Effect.gen(function* () {
|
|
1091
|
+
if (options !== undefined && !record(options))
|
|
1092
|
+
return yield* Effect.fail(
|
|
1093
|
+
new UserOperationError({
|
|
1094
|
+
operation,
|
|
1095
|
+
reason: "input",
|
|
1096
|
+
outcome: "notDispatched",
|
|
1097
|
+
inputValidation: inputValidationFailure(
|
|
1098
|
+
"options",
|
|
1099
|
+
"type",
|
|
1100
|
+
"Operation options must be an object",
|
|
1101
|
+
).detail,
|
|
1102
|
+
}),
|
|
1103
|
+
)
|
|
1104
|
+
if (unsupportedOption)
|
|
1105
|
+
return yield* Effect.fail(
|
|
1106
|
+
new UserOperationError({
|
|
1107
|
+
operation,
|
|
1108
|
+
reason: "input",
|
|
1109
|
+
outcome: "notDispatched",
|
|
1110
|
+
inputValidation: unsupportedOption.detail,
|
|
1111
|
+
}),
|
|
1112
|
+
)
|
|
1113
|
+
if (!Number.isSafeInteger(timeout) || timeout <= 0 || timeout > 2_147_483_647)
|
|
1114
|
+
return yield* Effect.fail(
|
|
1115
|
+
new UserOperationError({
|
|
1116
|
+
operation,
|
|
1117
|
+
reason: "input",
|
|
1118
|
+
outcome: "notDispatched",
|
|
1119
|
+
inputValidation: inputValidationFailure(
|
|
1120
|
+
"options.timeoutMs",
|
|
1121
|
+
"range",
|
|
1122
|
+
"Operation timeoutMs must be an integer from 1 through 2,147,483,647 milliseconds",
|
|
1123
|
+
).detail,
|
|
1124
|
+
}),
|
|
1125
|
+
)
|
|
1126
|
+
const deadline = owner.logical.now() + timeout
|
|
1127
|
+
if (request.verifyType) {
|
|
1128
|
+
const channel = yield* owner.rest.user(
|
|
1129
|
+
token,
|
|
1130
|
+
operation,
|
|
1131
|
+
() => directMessageFetch(request.id!),
|
|
1132
|
+
options,
|
|
1133
|
+
)
|
|
1134
|
+
if (request.verifyType === "group" && channel.type !== "group")
|
|
1135
|
+
return yield* Effect.fail(
|
|
1136
|
+
new UserOperationError({
|
|
1137
|
+
operation,
|
|
1138
|
+
reason: "input",
|
|
1139
|
+
outcome: "notDispatched",
|
|
1140
|
+
inputValidation: inputValidationFailure(
|
|
1141
|
+
"channelId",
|
|
1142
|
+
"relationship",
|
|
1143
|
+
"This operation requires a group DM channel",
|
|
1144
|
+
).detail,
|
|
1145
|
+
}),
|
|
1146
|
+
)
|
|
1147
|
+
}
|
|
1148
|
+
const remaining = Math.floor(deadline - owner.logical.now())
|
|
1149
|
+
if (remaining <= 0)
|
|
1150
|
+
return yield* Effect.fail(
|
|
1151
|
+
new UserOperationError({ operation, reason: "timeout", outcome: "notDispatched" }),
|
|
1152
|
+
)
|
|
1153
|
+
if (request.noCache)
|
|
1154
|
+
return yield* owner.rest.user(token, operation, () => request, { timeoutMs: remaining })
|
|
1155
|
+
const guard = owner.userCache.begin(request.resource, {
|
|
1156
|
+
...(request.id === undefined ? {} : { id: request.id }),
|
|
1157
|
+
mutation: request.method !== "GET",
|
|
1158
|
+
...(request.replace === undefined ? {} : { replace: request.replace }),
|
|
1159
|
+
})
|
|
1160
|
+
return yield* owner.rest
|
|
1161
|
+
.user(token, operation, () => request, { timeoutMs: remaining })
|
|
1162
|
+
.pipe(
|
|
1163
|
+
Effect.tap((value) =>
|
|
1164
|
+
Effect.sync(() =>
|
|
1165
|
+
owner.userCache.complete(
|
|
1166
|
+
guard,
|
|
1167
|
+
(value === undefined ? [] : Array.isArray(value) ? value : [value]) as readonly (
|
|
1168
|
+
User | DirectMessageChannel
|
|
1169
|
+
)[],
|
|
1170
|
+
),
|
|
1171
|
+
),
|
|
1172
|
+
),
|
|
1173
|
+
Effect.onExit((exit) =>
|
|
1174
|
+
Effect.sync(() => {
|
|
1175
|
+
if (exit._tag === "Failure") {
|
|
1176
|
+
owner.userCache.failed(guard)
|
|
1177
|
+
}
|
|
1178
|
+
if (request.method === "DELETE" && request.id) owner.cache?.deleteChannel(request.id)
|
|
1179
|
+
}),
|
|
1180
|
+
),
|
|
1181
|
+
)
|
|
1182
|
+
})
|
|
1183
|
+
})
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
application<A>(
|
|
1187
|
+
operation: BotApplicationOperation,
|
|
1188
|
+
build: () => BotApplicationRequest<A>,
|
|
1189
|
+
options?: BotApplicationOperationOptions,
|
|
1190
|
+
) {
|
|
1191
|
+
return Effect.suspend(() =>
|
|
1192
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1193
|
+
? this.rest.application(this.#configuration.token, operation, build, options)
|
|
1194
|
+
: Effect.fail(new ClientClosedError()),
|
|
1195
|
+
)
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
getUserResource<K extends "users" | "directMessages">(kind: K, id: string) {
|
|
1199
|
+
return Effect.suspend(
|
|
1200
|
+
(): Effect.Effect<UserResources[K] | undefined, ClientClosedError | UserOperationError> => {
|
|
1201
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1202
|
+
return Effect.fail(new ClientClosedError())
|
|
1203
|
+
if (!identifier(id))
|
|
1204
|
+
return Effect.fail(
|
|
1205
|
+
new UserOperationError({
|
|
1206
|
+
operation: `${kind}.get`,
|
|
1207
|
+
reason: "input",
|
|
1208
|
+
outcome: "notDispatched",
|
|
1209
|
+
inputValidation: inputValidationFailure(
|
|
1210
|
+
kind === "users" ? "userId" : "channelId",
|
|
1211
|
+
"format",
|
|
1212
|
+
kind === "users"
|
|
1213
|
+
? "User IDs must be decimal strings"
|
|
1214
|
+
: "Channel IDs must be decimal strings",
|
|
1215
|
+
).detail,
|
|
1216
|
+
}),
|
|
1217
|
+
)
|
|
1218
|
+
return Effect.succeed(this.userCache.get(kind, id))
|
|
1219
|
+
},
|
|
1220
|
+
)
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/** Send one caller-described request through this client's REST scheduler */
|
|
1224
|
+
request<T>(input: RestRequest | DefaultRestRequest): Effect.Effect<RestResponse<T>, RestRequestFailure> {
|
|
1225
|
+
return Effect.suspend(() =>
|
|
1226
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1227
|
+
? this.rest.request<T>(this.#configuration.token, input)
|
|
1228
|
+
: Effect.fail(new ClientClosedError()),
|
|
1229
|
+
)
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Send one application command on a ready local shard through that socket's paced path.
|
|
1234
|
+
* Validation happens before anything is queued. The data is copied through JSON so later caller changes cannot
|
|
1235
|
+
* alter the frame, and a frame above Fluxer's 4,096-byte limit is rejected rather than closing the connection
|
|
1236
|
+
*/
|
|
1237
|
+
gatewaySend(shardId: number, op: number, d: unknown, options?: unknown): Effect.Effect<void, GatewaySendFailure> {
|
|
1238
|
+
// Only encoding the caller data is marked, so a throw from its getters or toJSON is an application fault
|
|
1239
|
+
return suspendMarked((): Effect.Effect<void, GatewaySendFailure> => {
|
|
1240
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1241
|
+
return Effect.fail(new ClientClosedError())
|
|
1242
|
+
const validShard = typeof shardId === "number" && Number.isSafeInteger(shardId) && shardId >= 0
|
|
1243
|
+
const validOpcode = typeof op === "number" && Number.isSafeInteger(op) && op >= 0
|
|
1244
|
+
const input = (path: string, explanation: string, cause?: unknown) =>
|
|
1245
|
+
Effect.fail(
|
|
1246
|
+
new GatewaySendError({
|
|
1247
|
+
reason: "input",
|
|
1248
|
+
shardId: validShard ? shardId : null,
|
|
1249
|
+
opcode: validOpcode ? op : null,
|
|
1250
|
+
inputValidation: inputValidationFailure(path, path === "d" ? "format" : "type", explanation)
|
|
1251
|
+
.detail,
|
|
1252
|
+
cause,
|
|
1253
|
+
}),
|
|
1254
|
+
)
|
|
1255
|
+
if (!validShard) return input("shardId", "Shard IDs must be non-negative integers")
|
|
1256
|
+
if (!validOpcode) return input("op", "Opcodes must be non-negative integers")
|
|
1257
|
+
// Only the default API passes options, whose signal the operation executor reads
|
|
1258
|
+
const unsupported = record(options)
|
|
1259
|
+
? readCaller(() => unsupportedKeyFailure(options, ["signal"], "options", "the gateway send options"))
|
|
1260
|
+
: undefined
|
|
1261
|
+
if (unsupported)
|
|
1262
|
+
return Effect.fail(
|
|
1263
|
+
new GatewaySendError({ reason: "input", shardId, opcode: op, inputValidation: unsupported.detail }),
|
|
1264
|
+
)
|
|
1265
|
+
if (reservedClientOpcodes.has(op))
|
|
1266
|
+
return Effect.fail(new GatewaySendError({ reason: "reserved", shardId, opcode: op }))
|
|
1267
|
+
const result = readCaller(() => encodeCommandData(d))
|
|
1268
|
+
if ("failure" in result) return input("d", "Command data must be encodable as JSON", result.failure)
|
|
1269
|
+
const encoded = result.json
|
|
1270
|
+
if (encoded === undefined) return input("d", "Command data must be a JSON value; use null for no data")
|
|
1271
|
+
if (Buffer.byteLength(`{"op":${op},"d":${encoded}}`) > maxClientPayloadBytes)
|
|
1272
|
+
return input("d", "The encoded command must be at most 4,096 bytes")
|
|
1273
|
+
const shard = this.#shards.get(shardId)
|
|
1274
|
+
if (!shard) return Effect.fail(new GatewaySendError({ reason: "notOwned", shardId, opcode: op }))
|
|
1275
|
+
if (shard.state !== "Connected" || shard.submit === undefined)
|
|
1276
|
+
return Effect.fail(new GatewaySendError({ reason: "notReady", shardId, opcode: op }))
|
|
1277
|
+
// Fluxer limits raw member requests together with members.iterateChunks, so one counts there too, and in a
|
|
1278
|
+
// supervisor's account-wide window, from the moment the socket takes it, because a caller interrupted before it
|
|
1279
|
+
// resumes cannot withdraw a sent frame. It is counted, never refused
|
|
1280
|
+
const sent = op === Opcode.requestGuildMembers ? () => this.memberChunks.countSent() : undefined
|
|
1281
|
+
return shard.submit(op, JSON.parse(encoded), sent).pipe(
|
|
1282
|
+
Effect.mapError(
|
|
1283
|
+
(reason) =>
|
|
1284
|
+
new GatewaySendError({
|
|
1285
|
+
reason: reason === "busy" ? "busy" : "notReady",
|
|
1286
|
+
shardId,
|
|
1287
|
+
opcode: op,
|
|
1288
|
+
}),
|
|
1289
|
+
),
|
|
1290
|
+
)
|
|
1291
|
+
})
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
webhook<A>(
|
|
1295
|
+
operation: WebhookOperation,
|
|
1296
|
+
build: () => WebhookRequest<A> | InputValidationFailure,
|
|
1297
|
+
options?: WebhookOperationOptions,
|
|
1298
|
+
) {
|
|
1299
|
+
return Effect.suspend(() =>
|
|
1300
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1301
|
+
? this.rest.webhook(this.#configuration.token, operation, build, options)
|
|
1302
|
+
: Effect.fail(new ClientClosedError()),
|
|
1303
|
+
)
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
guild<A>(operation: GuildOperation, build: GuildBuild<A>, options?: GuildOperationOptions) {
|
|
1307
|
+
return Effect.suspend(() =>
|
|
1308
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1309
|
+
? this.rest.guild(this.#configuration.token, operation, build, options)
|
|
1310
|
+
: Effect.fail(new ClientClosedError()),
|
|
1311
|
+
)
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
leaveGuild(guildId: string, options?: GuildOperationOptions) {
|
|
1315
|
+
return this.guild("guilds.leave", () => guildLeave(guildId), options).pipe(
|
|
1316
|
+
Effect.tap(() => Effect.sync(() => this.presence.forgetMembers(guildId))),
|
|
1317
|
+
)
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
deleteOwnGuildMessages(guildId: string, options: unknown) {
|
|
1321
|
+
return suspendInput(() => {
|
|
1322
|
+
const checked = ownDeletionOptions<GuildOperationOptions>(options)
|
|
1323
|
+
return checked instanceof InputValidationFailure
|
|
1324
|
+
? this.guild<void>("guilds.deleteOwnMessages", () => checked)
|
|
1325
|
+
: this.guild("guilds.deleteOwnMessages", () => guildDeleteOwnMessages(guildId), checked)
|
|
1326
|
+
})
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
channel<A>(
|
|
1330
|
+
operation: ChannelOperation,
|
|
1331
|
+
build: () => ChannelRequest<A> | InputValidationFailure,
|
|
1332
|
+
options?: ChannelOperationOptions,
|
|
1333
|
+
) {
|
|
1334
|
+
return Effect.suspend(() =>
|
|
1335
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1336
|
+
? this.rest.channel(this.#configuration.token, operation, build, options)
|
|
1337
|
+
: Effect.fail(new ClientClosedError()),
|
|
1338
|
+
)
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
getChannel(id: string) {
|
|
1342
|
+
return Effect.suspend(
|
|
1343
|
+
(): Effect.Effect<GuildChannel | undefined, ClientClosedError | ChannelOperationError> => {
|
|
1344
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1345
|
+
return Effect.fail(new ClientClosedError())
|
|
1346
|
+
if (!identifier(id))
|
|
1347
|
+
return Effect.fail(
|
|
1348
|
+
new ChannelOperationError({
|
|
1349
|
+
operation: "channels.get",
|
|
1350
|
+
reason: "input",
|
|
1351
|
+
outcome: "notDispatched",
|
|
1352
|
+
inputValidation: inputValidationFailure(
|
|
1353
|
+
"channelId",
|
|
1354
|
+
"format",
|
|
1355
|
+
"Channel IDs must be decimal strings",
|
|
1356
|
+
).detail,
|
|
1357
|
+
}),
|
|
1358
|
+
)
|
|
1359
|
+
return Effect.succeed(this.channelCache?.get(id))
|
|
1360
|
+
},
|
|
1361
|
+
)
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
getResource<K extends ResourceKind>(kind: K, target: string | MemberReference | RoleReference) {
|
|
1365
|
+
return suspendInput((): Effect.Effect<Resources[K] | undefined, ClientClosedError | GuildOperationError> => {
|
|
1366
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1367
|
+
return Effect.fail(new ClientClosedError())
|
|
1368
|
+
const guildId = kind === "guilds" ? target : record(target) ? target.guildId : undefined
|
|
1369
|
+
const id =
|
|
1370
|
+
kind === "guilds"
|
|
1371
|
+
? guildId
|
|
1372
|
+
: record(target)
|
|
1373
|
+
? kind === "members"
|
|
1374
|
+
? target.userId
|
|
1375
|
+
: target.id
|
|
1376
|
+
: undefined
|
|
1377
|
+
if (!identifier(guildId) || !identifier(id))
|
|
1378
|
+
return Effect.fail(
|
|
1379
|
+
new GuildOperationError({
|
|
1380
|
+
operation: `${kind}.get`,
|
|
1381
|
+
reason: "input",
|
|
1382
|
+
outcome: "notDispatched",
|
|
1383
|
+
inputValidation: inputValidationFailure(
|
|
1384
|
+
kind === "guilds" ? "guildId" : "target",
|
|
1385
|
+
"format",
|
|
1386
|
+
kind === "guilds"
|
|
1387
|
+
? "Guild IDs must be decimal strings"
|
|
1388
|
+
: "Resource targets require decimal guild and resource IDs",
|
|
1389
|
+
).detail,
|
|
1390
|
+
}),
|
|
1391
|
+
)
|
|
1392
|
+
return Effect.sync(() => this.resources?.get(kind, guildId, id))
|
|
1393
|
+
})
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
reply(target: MessageReference, input: MessageInput | string, options?: SendOptions) {
|
|
1397
|
+
return suspendInput(() => {
|
|
1398
|
+
const request = replyInput(target, input)
|
|
1399
|
+
if (request instanceof MessageError) return Effect.fail(request)
|
|
1400
|
+
return this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1401
|
+
? this.rest.reply(this.#configuration.token, request.target, request.input, options)
|
|
1402
|
+
: Effect.fail(new ClientClosedError())
|
|
1403
|
+
})
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
send(channelId: string, input: MessageInput | string, options?: SendOptions) {
|
|
1407
|
+
return Effect.suspend(() =>
|
|
1408
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1409
|
+
? this.rest.send(this.#configuration.token, channelId, input, options)
|
|
1410
|
+
: Effect.fail(new ClientClosedError()),
|
|
1411
|
+
)
|
|
1412
|
+
}
|
|
1413
|
+
refreshAttachmentUrls(
|
|
1414
|
+
urls: readonly string[],
|
|
1415
|
+
options?: AttachmentRefreshOptions,
|
|
1416
|
+
): Effect.Effect<readonly RefreshedAttachmentUrl[], AttachmentRefreshFailure> {
|
|
1417
|
+
return Effect.suspend(() =>
|
|
1418
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1419
|
+
? this.rest.refreshAttachmentUrls(this.#configuration.token, urls, options)
|
|
1420
|
+
: Effect.fail(new ClientClosedError()),
|
|
1421
|
+
)
|
|
1422
|
+
}
|
|
1423
|
+
downloadAttachment(
|
|
1424
|
+
attachment: Attachment,
|
|
1425
|
+
options?: AttachmentDownloadOptions,
|
|
1426
|
+
): Effect.Effect<Uint8Array, AttachmentDownloadFailure> {
|
|
1427
|
+
return Effect.suspend(() =>
|
|
1428
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1429
|
+
? this.rest.download(attachment, options)
|
|
1430
|
+
: Effect.fail(new ClientClosedError()),
|
|
1431
|
+
)
|
|
1432
|
+
}
|
|
1433
|
+
streamAttachment(
|
|
1434
|
+
attachment: Attachment,
|
|
1435
|
+
options?: AttachmentDownloadOptions,
|
|
1436
|
+
): Effect.Effect<AttachmentDownloadSource, AttachmentDownloadFailure> {
|
|
1437
|
+
return Effect.suspend(() =>
|
|
1438
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1439
|
+
? this.rest.openDownload(attachment, options)
|
|
1440
|
+
: Effect.fail(new ClientClosedError()),
|
|
1441
|
+
)
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
forward(channelId: string, input: ForwardMessageInput, options?: SendOptions) {
|
|
1445
|
+
return Effect.suspend(() =>
|
|
1446
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1447
|
+
? this.rest.forward(this.#configuration.token, channelId, input, options)
|
|
1448
|
+
: Effect.fail(new ClientClosedError()),
|
|
1449
|
+
)
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
sendDirectMessage(userId: string, input: MessageInput | string, options?: SendOptions) {
|
|
1453
|
+
return suspendInput((): Effect.Effect<M, SendError> => {
|
|
1454
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1455
|
+
return Effect.fail(new ClientClosedError())
|
|
1456
|
+
if (!identifier(userId))
|
|
1457
|
+
return Effect.fail(
|
|
1458
|
+
new MessageError({
|
|
1459
|
+
reason: "input",
|
|
1460
|
+
outcome: "notDispatched",
|
|
1461
|
+
inputValidation: inputValidationFailure("userId", "format", "User IDs must be decimal strings")
|
|
1462
|
+
.detail,
|
|
1463
|
+
}),
|
|
1464
|
+
)
|
|
1465
|
+
if (typeof input !== "string" && !record(input))
|
|
1466
|
+
return Effect.fail(
|
|
1467
|
+
new MessageError({
|
|
1468
|
+
reason: "input",
|
|
1469
|
+
outcome: "notDispatched",
|
|
1470
|
+
inputValidation: inputValidationFailure(
|
|
1471
|
+
"input",
|
|
1472
|
+
"type",
|
|
1473
|
+
"Direct message input must be a string or an object",
|
|
1474
|
+
).detail,
|
|
1475
|
+
}),
|
|
1476
|
+
)
|
|
1477
|
+
// The send encoder rejects a messageReference first, reading it only once with the other fields
|
|
1478
|
+
return this.rest.send(this.#configuration.token, userId, input, options, userId)
|
|
1479
|
+
})
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
fetchReactionUsers(
|
|
1483
|
+
target: MessageReference,
|
|
1484
|
+
emoji: ReactionEmojiInput,
|
|
1485
|
+
query?: ReactionUsersQuery,
|
|
1486
|
+
options?: MessageOperationOptions,
|
|
1487
|
+
) {
|
|
1488
|
+
return Effect.suspend(() =>
|
|
1489
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1490
|
+
? this.rest.fetchReactionUsers(this.#configuration.token, target, emoji, query, options)
|
|
1491
|
+
: Effect.fail(new ClientClosedError()),
|
|
1492
|
+
)
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
reaction(
|
|
1496
|
+
operation: "addReaction" | "removeReaction" | "removeUserReaction" | "clearReaction" | "clearReactions",
|
|
1497
|
+
target: MessageReference,
|
|
1498
|
+
emoji: ReactionEmojiInput | undefined,
|
|
1499
|
+
options?: MessageOperationOptions,
|
|
1500
|
+
userId?: string,
|
|
1501
|
+
) {
|
|
1502
|
+
return Effect.suspend(() =>
|
|
1503
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1504
|
+
? this.rest.reaction(this.#configuration.token, operation, target, emoji, options, userId)
|
|
1505
|
+
: Effect.fail(new ClientClosedError()),
|
|
1506
|
+
)
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
fetch(target: MessageReference, options?: MessageOperationOptions) {
|
|
1510
|
+
return Effect.suspend(() =>
|
|
1511
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1512
|
+
? this.rest.fetch(this.#configuration.token, target, options)
|
|
1513
|
+
: Effect.fail(new ClientClosedError()),
|
|
1514
|
+
)
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
publish(target: MessageReference, options?: MessageOperationOptions) {
|
|
1518
|
+
return Effect.suspend(() =>
|
|
1519
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1520
|
+
? this.rest.publish(this.#configuration.token, target, options)
|
|
1521
|
+
: Effect.fail(new ClientClosedError()),
|
|
1522
|
+
)
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
fetchCrosspostSource(target: MessageReference, options?: MessageOperationOptions) {
|
|
1526
|
+
return Effect.suspend(() =>
|
|
1527
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1528
|
+
? this.rest.fetchCrosspostSource(this.#configuration.token, target, options)
|
|
1529
|
+
: Effect.fail(new ClientClosedError()),
|
|
1530
|
+
)
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
typing(channelId: string, options?: MessageOperationOptions) {
|
|
1534
|
+
return Effect.suspend(() =>
|
|
1535
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1536
|
+
? this.rest.typing(this.#configuration.token, channelId, options)
|
|
1537
|
+
: Effect.fail(new ClientClosedError()),
|
|
1538
|
+
)
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
keepTyping<A, E, R>(
|
|
1542
|
+
channelId: string,
|
|
1543
|
+
task: Effect.Effect<A, E, R>,
|
|
1544
|
+
options?: MessageOperationOptions,
|
|
1545
|
+
): Effect.Effect<A, E | MessageOperationFailure, R> {
|
|
1546
|
+
const owner = this
|
|
1547
|
+
if (!Effect.isEffect(task))
|
|
1548
|
+
return Effect.fail(
|
|
1549
|
+
new MessageOperationError({
|
|
1550
|
+
operation: "typing",
|
|
1551
|
+
reason: "input",
|
|
1552
|
+
outcome: "notDispatched",
|
|
1553
|
+
inputValidation: inputValidationFailure(
|
|
1554
|
+
"task",
|
|
1555
|
+
"type",
|
|
1556
|
+
"The task passed to keepTyping must be an Effect",
|
|
1557
|
+
).detail,
|
|
1558
|
+
}),
|
|
1559
|
+
)
|
|
1560
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
1561
|
+
restore(owner.typing(channelId, options)).pipe(
|
|
1562
|
+
Effect.flatMap(() =>
|
|
1563
|
+
Effect.gen(function* () {
|
|
1564
|
+
const id = Symbol("typing")
|
|
1565
|
+
const started = Deferred.makeUnsafe<void>()
|
|
1566
|
+
const refresh = yield* Effect.forkChild(
|
|
1567
|
+
Deferred.await(started).pipe(
|
|
1568
|
+
Effect.andThen(owner.#refreshTyping(channelId, options)),
|
|
1569
|
+
Effect.ensuring(Effect.sync(() => owner.#typing.delete(id))),
|
|
1570
|
+
),
|
|
1571
|
+
)
|
|
1572
|
+
owner.#typing.set(id, refresh)
|
|
1573
|
+
Deferred.doneUnsafe(started, Effect.void)
|
|
1574
|
+
return yield* restore(task).pipe(Effect.onExit(() => owner.#stopTyping(refresh)))
|
|
1575
|
+
}),
|
|
1576
|
+
),
|
|
1577
|
+
),
|
|
1578
|
+
)
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
#refreshTyping(
|
|
1582
|
+
channelId: string,
|
|
1583
|
+
options?: MessageOperationOptions,
|
|
1584
|
+
): Effect.Effect<never, MessageOperationFailure> {
|
|
1585
|
+
const owner = this
|
|
1586
|
+
return Effect.forever(
|
|
1587
|
+
Effect.raceFirst(
|
|
1588
|
+
Effect.sleep(typingRefreshMs),
|
|
1589
|
+
Deferred.await(owner.#typingClosed).pipe(Effect.andThen(Effect.fail(new ClientClosedError()))),
|
|
1590
|
+
).pipe(Effect.andThen(owner.typing(channelId, options))),
|
|
1591
|
+
)
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
#stopTyping(fiber: Fiber.Fiber<never, MessageOperationFailure>): Effect.Effect<void, MessageOperationFailure> {
|
|
1595
|
+
return Effect.uninterruptible(
|
|
1596
|
+
Fiber.interrupt(fiber).pipe(
|
|
1597
|
+
Effect.andThen(Fiber.await(fiber)),
|
|
1598
|
+
Effect.flatMap((exit) =>
|
|
1599
|
+
Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)
|
|
1600
|
+
? Effect.failCause(exit.cause)
|
|
1601
|
+
: Effect.void,
|
|
1602
|
+
),
|
|
1603
|
+
),
|
|
1604
|
+
)
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
pin(operation: "pin" | "unpin", target: MessageReference, options?: MessageOperationOptions) {
|
|
1608
|
+
return Effect.suspend(() =>
|
|
1609
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1610
|
+
? this.rest.pin(this.#configuration.token, operation, target, options)
|
|
1611
|
+
: Effect.fail(new ClientClosedError()),
|
|
1612
|
+
)
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
fetchPins(channel: string, query?: MessagePinsQuery, options?: MessageOperationOptions) {
|
|
1616
|
+
return Effect.suspend(() =>
|
|
1617
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1618
|
+
? this.rest.fetchPins(this.#configuration.token, channel, query, options)
|
|
1619
|
+
: Effect.fail(new ClientClosedError()),
|
|
1620
|
+
)
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
fetchHistory(channelId: string, query?: MessageHistoryQuery, options?: MessageOperationOptions) {
|
|
1624
|
+
return Effect.suspend(() =>
|
|
1625
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1626
|
+
? this.rest.fetchHistory(this.#configuration.token, channelId, query, options)
|
|
1627
|
+
: Effect.fail(new ClientClosedError()),
|
|
1628
|
+
)
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
searchMessages(context: MessageSearchContext, query?: MessageSearchQuery, options?: MessageOperationOptions) {
|
|
1632
|
+
return Effect.suspend(() =>
|
|
1633
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1634
|
+
? this.rest.search(this.#configuration.token, context, query, options)
|
|
1635
|
+
: Effect.fail(new ClientClosedError()),
|
|
1636
|
+
)
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
edit(target: MessageReference, input: EditMessageInput | string, options?: MessageOperationOptions) {
|
|
1640
|
+
return Effect.suspend(() =>
|
|
1641
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1642
|
+
? this.rest.edit(this.#configuration.token, target, input, options)
|
|
1643
|
+
: Effect.fail(new ClientClosedError()),
|
|
1644
|
+
)
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
delete(target: MessageReference, options?: MessageOperationOptions) {
|
|
1648
|
+
return Effect.suspend(() =>
|
|
1649
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1650
|
+
? this.rest.delete(this.#configuration.token, target, options)
|
|
1651
|
+
: Effect.fail(new ClientClosedError()),
|
|
1652
|
+
)
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
deleteAttachment(target: MessageReference, attachmentId: string, options?: MessageOperationOptions) {
|
|
1656
|
+
return Effect.suspend(() =>
|
|
1657
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1658
|
+
? this.rest.deleteAttachment(this.#configuration.token, target, attachmentId, options)
|
|
1659
|
+
: Effect.fail(new ClientClosedError()),
|
|
1660
|
+
)
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
deleteMany(channelId: string, ids: readonly string[], options?: MessageOperationOptions) {
|
|
1664
|
+
return Effect.suspend(() =>
|
|
1665
|
+
this.#configuration && this.#state !== "Closing" && this.#state !== "Closed"
|
|
1666
|
+
? this.rest.deleteMany(this.#configuration.token, channelId, ids, options)
|
|
1667
|
+
: Effect.fail(new ClientClosedError()),
|
|
1668
|
+
)
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
deleteOwnMessages(channelId: string, options: unknown) {
|
|
1672
|
+
return suspendInput(() => {
|
|
1673
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1674
|
+
return Effect.fail(new ClientClosedError())
|
|
1675
|
+
const checked = ownDeletionOptions<MessageOperationOptions>(options)
|
|
1676
|
+
if (checked instanceof InputValidationFailure)
|
|
1677
|
+
return Effect.fail(
|
|
1678
|
+
new MessageOperationError({
|
|
1679
|
+
operation: "deleteOwnMessages",
|
|
1680
|
+
reason: "input",
|
|
1681
|
+
outcome: "notDispatched",
|
|
1682
|
+
inputValidation: checked.detail,
|
|
1683
|
+
}),
|
|
1684
|
+
)
|
|
1685
|
+
return this.rest.deleteOwnMessages(this.#configuration.token, channelId, checked)
|
|
1686
|
+
})
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
get(target: MessageReference) {
|
|
1690
|
+
return suspendInput((): Effect.Effect<M | undefined, ClientClosedError | MessageOperationError> => {
|
|
1691
|
+
if (!this.#configuration || this.#state === "Closing" || this.#state === "Closed")
|
|
1692
|
+
return Effect.fail(new ClientClosedError())
|
|
1693
|
+
const reference = snapshotReference(target)
|
|
1694
|
+
if (reference === undefined)
|
|
1695
|
+
return Effect.fail(
|
|
1696
|
+
new MessageOperationError({
|
|
1697
|
+
operation: "messages.get",
|
|
1698
|
+
reason: "input",
|
|
1699
|
+
outcome: "notDispatched",
|
|
1700
|
+
inputValidation: inputValidationFailure(
|
|
1701
|
+
"target",
|
|
1702
|
+
"format",
|
|
1703
|
+
"Message targets require decimal id and channelId strings",
|
|
1704
|
+
).detail,
|
|
1705
|
+
}),
|
|
1706
|
+
)
|
|
1707
|
+
return Effect.sync(() => this.cache?.get(reference))
|
|
1708
|
+
})
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
#closeServices() {
|
|
1712
|
+
Deferred.doneUnsafe(this.#typingClosed, Effect.void)
|
|
1713
|
+
this.events.stop()
|
|
1714
|
+
this.rest.stop()
|
|
1715
|
+
return closeAll([
|
|
1716
|
+
Effect.exit(this.instance.shutdown()),
|
|
1717
|
+
Effect.exit(this.events.shutdown()),
|
|
1718
|
+
Effect.exit(this.tasks.shutdown()),
|
|
1719
|
+
Effect.exit(this.rest.shutdown()),
|
|
1720
|
+
Effect.exit(this.failures.shutdown()),
|
|
1721
|
+
Effect.all(
|
|
1722
|
+
[
|
|
1723
|
+
Effect.forEach(
|
|
1724
|
+
[...this.#messageCollectors],
|
|
1725
|
+
(collector) => Effect.exit(Deferred.await(collector.closed).pipe(Effect.asVoid)),
|
|
1726
|
+
{ concurrency: "unbounded" },
|
|
1727
|
+
),
|
|
1728
|
+
Effect.forEach(
|
|
1729
|
+
[...this.#reactionCollectors],
|
|
1730
|
+
(collector) => Effect.exit(Deferred.await(collector.closed).pipe(Effect.asVoid)),
|
|
1731
|
+
{ concurrency: "unbounded" },
|
|
1732
|
+
),
|
|
1733
|
+
],
|
|
1734
|
+
{ concurrency: "unbounded" },
|
|
1735
|
+
).pipe(
|
|
1736
|
+
Effect.map((exits) => defectsOnly(exits.flat())),
|
|
1737
|
+
Effect.ensuring(
|
|
1738
|
+
Effect.sync(() => {
|
|
1739
|
+
this.#messageCollectors.clear()
|
|
1740
|
+
this.#reactionCollectors.clear()
|
|
1741
|
+
}),
|
|
1742
|
+
),
|
|
1743
|
+
),
|
|
1744
|
+
]).pipe(Effect.onExit(() => this.#awaitTyping()))
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
#awaitTyping(): Effect.Effect<void> {
|
|
1748
|
+
return Effect.forEach([...this.#typing.values()], (fiber) => Fiber.await(fiber), {
|
|
1749
|
+
concurrency: "unbounded",
|
|
1750
|
+
}).pipe(
|
|
1751
|
+
Effect.flatMap((exits) => {
|
|
1752
|
+
const reasons = exits.flatMap((exit) =>
|
|
1753
|
+
Exit.isFailure(exit) ? exit.cause.reasons.filter((reason) => reason._tag === "Die") : [],
|
|
1754
|
+
)
|
|
1755
|
+
return reasons.length ? Effect.failCause(Cause.fromReasons<never>(reasons)) : Effect.void
|
|
1756
|
+
}),
|
|
1757
|
+
)
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
#supervise(configuration: Configuration<M>, startup: Deferred.Deferred<void, ConnectError>) {
|
|
1761
|
+
const owner = this
|
|
1762
|
+
return Effect.gen(function* () {
|
|
1763
|
+
// Automatic sizing counts guilds under its own deadline, so the shard group always gets its full budget
|
|
1764
|
+
let plan = owner.#plan ?? (yield* owner.#automaticPlan(configuration))
|
|
1765
|
+
// The refill waits for the whole group, and a failed startup ends this worker and the refill with it
|
|
1766
|
+
yield* Effect.forkChild(
|
|
1767
|
+
Effect.exit(Deferred.await(startup)).pipe(
|
|
1768
|
+
Effect.flatMap((ready) => (Exit.isSuccess(ready) ? owner.#refillRestored() : Effect.void)),
|
|
1769
|
+
),
|
|
1770
|
+
)
|
|
1771
|
+
for (let first = true; ; first = false) {
|
|
1772
|
+
// Capture and rethrow defects or interruption while masked, as REST attempts do. Otherwise an interruption that
|
|
1773
|
+
// skips this handler discards the plan's typed failure, and one pending at the mask's end discards the whole cause
|
|
1774
|
+
const exit = yield* Effect.uninterruptibleMask((restore) =>
|
|
1775
|
+
Effect.exit(restore(owner.#runPlan(configuration, startup, plan, first))).pipe(
|
|
1776
|
+
Effect.flatMap((exit) =>
|
|
1777
|
+
Exit.isFailure(exit) && (Cause.hasDies(exit.cause) || Cause.hasInterrupts(exit.cause))
|
|
1778
|
+
? Effect.failCause(exit.cause)
|
|
1779
|
+
: Effect.succeed(exit),
|
|
1780
|
+
),
|
|
1781
|
+
),
|
|
1782
|
+
)
|
|
1783
|
+
const shardId = owner.#reshardShard
|
|
1784
|
+
if (Exit.isSuccess(exit)) return
|
|
1785
|
+
if (shardId === undefined || Cause.hasDies(exit.cause)) return yield* Effect.failCause(exit.cause)
|
|
1786
|
+
plan = yield* owner.#reshard(configuration, plan, shardId)
|
|
1787
|
+
}
|
|
1788
|
+
})
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* Run every shard of one plan until one fails permanently. Only the first plan offers stored sessions, because a
|
|
1793
|
+
* snapshot belongs to the plan it was saved under
|
|
1794
|
+
*/
|
|
1795
|
+
#runPlan(
|
|
1796
|
+
configuration: Configuration<M>,
|
|
1797
|
+
startup: Deferred.Deferred<void, ConnectError>,
|
|
1798
|
+
plan: ShardPlan,
|
|
1799
|
+
loadSessions: boolean,
|
|
1800
|
+
) {
|
|
1801
|
+
const owner = this
|
|
1802
|
+
return Effect.gen(function* () {
|
|
1803
|
+
const spaced = plan.identifyShards && configuration.identifyCoordinator === undefined
|
|
1804
|
+
const startupTimeoutMs = startupBudgetMs(configuration.startupTimeoutMs, spaced ? plan.shardIds.length : 1)
|
|
1805
|
+
if (startupTimeoutMs > configuration.startupTimeoutMs)
|
|
1806
|
+
owner.logging.log({
|
|
1807
|
+
level: "info",
|
|
1808
|
+
category: "lifecycle",
|
|
1809
|
+
code: "lifecycle.startupDeadline",
|
|
1810
|
+
message: `Startup of ${plan.shardIds.length} shards may take up to ${formatDuration(startupTimeoutMs)}: The configured ${formatDuration(configuration.startupTimeoutMs)} (connection.startupTimeoutMs) plus ${identifySpacingMs / 1_000} s per additional shard, because the SDK starts shard sessions ${identifySpacingMs / 1_000} s apart`,
|
|
1811
|
+
fields: {
|
|
1812
|
+
startupTimeoutMs,
|
|
1813
|
+
configuredMs: configuration.startupTimeoutMs,
|
|
1814
|
+
shards: plan.shardIds.length,
|
|
1815
|
+
},
|
|
1816
|
+
})
|
|
1817
|
+
return yield* superviseShards({
|
|
1818
|
+
shards: [...owner.#shards.values()],
|
|
1819
|
+
startupTimeoutMs,
|
|
1820
|
+
attributeShards: plan.identifyShards,
|
|
1821
|
+
startup,
|
|
1822
|
+
runShard: (shard, deadline) =>
|
|
1823
|
+
runShardLoop({
|
|
1824
|
+
host: owner.#shardHost,
|
|
1825
|
+
configuration,
|
|
1826
|
+
startup,
|
|
1827
|
+
shard,
|
|
1828
|
+
plan,
|
|
1829
|
+
deadline,
|
|
1830
|
+
startupTimeoutMs,
|
|
1831
|
+
established: owner.#groupReady,
|
|
1832
|
+
loadSessions,
|
|
1833
|
+
}),
|
|
1834
|
+
})
|
|
1835
|
+
})
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* Decide whether a 4011 (sharding required) closure on this shard moves the automatic plan to a larger one: True when
|
|
1840
|
+
* it does, otherwise why not. Closures of sibling shards during the same move join it
|
|
1841
|
+
*/
|
|
1842
|
+
#acceptReshard(shardId: number): true | string {
|
|
1843
|
+
if (this.#reshardShard !== undefined) return true
|
|
1844
|
+
const refused = admitReshard(this.#reshards, this.logical.now(), this.#plan!.totalShards)
|
|
1845
|
+
if (refused !== undefined) return refused
|
|
1846
|
+
this.#reshardShard = shardId
|
|
1847
|
+
return true
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
/**
|
|
1851
|
+
* Move every local shard to a larger automatic plan after Fluxer closed shardId with 4011, from an automatic plan or
|
|
1852
|
+
* the one-shard plan of omitted settings. The old sessions are already closed. Their guild-scoped observations are
|
|
1853
|
+
* released under the old routing, the guilds are counted again and the new plan's shards start new sessions.
|
|
1854
|
+
* Guild-scoped state observers see the waiting state, because the shard that served their guild is gone
|
|
1855
|
+
*/
|
|
1856
|
+
#reshard(
|
|
1857
|
+
configuration: Configuration<M>,
|
|
1858
|
+
previous: ShardPlan,
|
|
1859
|
+
shardId: number,
|
|
1860
|
+
): Effect.Effect<ShardPlan, ConnectionFailure> {
|
|
1861
|
+
const owner = this
|
|
1862
|
+
return Effect.gen(function* () {
|
|
1863
|
+
const waiting = owner.#groupReady ? "Recovering" : "Connecting"
|
|
1864
|
+
for (const shard of owner.#shards.values()) {
|
|
1865
|
+
owner.#gapShard(shard.shardId, false)
|
|
1866
|
+
shard.state = waiting
|
|
1867
|
+
shard.latency = null
|
|
1868
|
+
shard.recovery = null
|
|
1869
|
+
shard.submit = undefined
|
|
1870
|
+
}
|
|
1871
|
+
const listeners = [...owner.#shardListeners.values()]
|
|
1872
|
+
owner.#shardListeners.clear()
|
|
1873
|
+
owner.#setState(waiting)
|
|
1874
|
+
for (const set of listeners) for (const listener of set) listener(waiting)
|
|
1875
|
+
const guilds = yield* owner.countGuilds(configuration.startupTimeoutMs)
|
|
1876
|
+
const plan = largerShardPlan(guilds, previous.totalShards)
|
|
1877
|
+
owner.#reshardShard = undefined
|
|
1878
|
+
owner.#adoptPlan(plan, "Connecting")
|
|
1879
|
+
owner.presence.reroute()
|
|
1880
|
+
// Omitted settings start with this fixed one-shard plan and switch to automatic sharding at the first move
|
|
1881
|
+
const switched = previous === configuration.sharding
|
|
1882
|
+
owner.logging.log({
|
|
1883
|
+
level: "warn",
|
|
1884
|
+
category: "lifecycle",
|
|
1885
|
+
code: "lifecycle.resharded",
|
|
1886
|
+
message: `Fluxer closed shard ${shardId} with close code 4011 (sharding required), so ${switched ? "the client without sharding settings switched to automatic sharding and moved" : "automatic sharding moved"} from ${previous.totalShards} to ${plan.totalShards} shards for ${guilds} communit${guilds === 1 ? "y" : "ies"}. Every shard starts a new session, so events sent during the move are missed${switched ? '. Set sharding to "auto" to start with enough shards next time' : ""}`,
|
|
1887
|
+
shardId,
|
|
1888
|
+
fields: { guilds, previousTotalShards: previous.totalShards, totalShards: plan.totalShards },
|
|
1889
|
+
})
|
|
1890
|
+
return plan
|
|
1891
|
+
})
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Refill the enabled guild, role and channel caches for the guilds of shards that resumed a stored session, since a
|
|
1896
|
+
* resumed session receives no guild data. Guilds are fetched one request at a time through the REST scheduler, so
|
|
1897
|
+
* the refill holds at most one REST slot and waits out rate limits. It stops after refillFailureLimit failed guilds,
|
|
1898
|
+
* when a guild-list page does not move past the previous one and when the client closes
|
|
1899
|
+
*/
|
|
1900
|
+
#refillRestored(): Effect.Effect<void> {
|
|
1901
|
+
const owner = this
|
|
1902
|
+
const shards = new Set(owner.#restoredShards)
|
|
1903
|
+
owner.#restoredShards.clear()
|
|
1904
|
+
const guildsEnabled = owner.resources?.diagnostics("guilds").configured === true
|
|
1905
|
+
const rolesEnabled = owner.resources?.diagnostics("roles").configured === true
|
|
1906
|
+
const channelsEnabled = owner.channelCache !== undefined
|
|
1907
|
+
if (shards.size === 0 || (!guildsEnabled && !rolesEnabled && !channelsEnabled)) return Effect.void
|
|
1908
|
+
return Effect.gen(function* () {
|
|
1909
|
+
const startedAt = owner.logical.now()
|
|
1910
|
+
const plan = owner.#plan!
|
|
1911
|
+
const guildIds: string[] = []
|
|
1912
|
+
let after: string | undefined
|
|
1913
|
+
let failed = 0
|
|
1914
|
+
let firstError: unknown
|
|
1915
|
+
let stopped: string | undefined
|
|
1916
|
+
while (true) {
|
|
1917
|
+
const page = yield* Effect.result(
|
|
1918
|
+
owner.guild("guilds.fetchPage", () =>
|
|
1919
|
+
guildList({ limit: guildCountPageSize, ...(after === undefined ? {} : { after }) }),
|
|
1920
|
+
),
|
|
1921
|
+
)
|
|
1922
|
+
if (page._tag === "Failure") {
|
|
1923
|
+
if (page.failure instanceof ClientClosedError) return
|
|
1924
|
+
firstError = page.failure
|
|
1925
|
+
stopped = "the bot's community list could not be read"
|
|
1926
|
+
break
|
|
1927
|
+
}
|
|
1928
|
+
// As in guilds.iterate, a page that repeats or goes back past the cursor would page forever
|
|
1929
|
+
const cursor = after
|
|
1930
|
+
if (cursor !== undefined && page.success.some((guild) => BigInt(guild.id) <= BigInt(cursor))) {
|
|
1931
|
+
stopped = "the bot's community list returned a page that did not move past the previous one"
|
|
1932
|
+
break
|
|
1933
|
+
}
|
|
1934
|
+
for (const guild of page.success)
|
|
1935
|
+
if (shards.has(guildShardId(guild.id, plan.totalShards))) guildIds.push(guild.id)
|
|
1936
|
+
if (page.success.length < guildCountPageSize) break
|
|
1937
|
+
after = page.success.at(-1)!.id
|
|
1938
|
+
}
|
|
1939
|
+
let refilled = 0
|
|
1940
|
+
for (const guildId of stopped === undefined ? guildIds : []) {
|
|
1941
|
+
const reads: Effect.Effect<unknown, unknown>[] = [
|
|
1942
|
+
...(guildsEnabled ? [owner.guild("guilds.fetch", () => guildFetch(guildId))] : []),
|
|
1943
|
+
...(rolesEnabled ? [owner.guild("roles.fetchAll", () => roleList(guildId))] : []),
|
|
1944
|
+
...(channelsEnabled ? [owner.channel("channels.fetchAll", () => channelList(guildId))] : []),
|
|
1945
|
+
]
|
|
1946
|
+
let failure: unknown
|
|
1947
|
+
for (const read of reads) {
|
|
1948
|
+
const result = yield* Effect.result(read)
|
|
1949
|
+
if (result._tag === "Success") continue
|
|
1950
|
+
if (result.failure instanceof ClientClosedError) return
|
|
1951
|
+
failure ??= result.failure
|
|
1952
|
+
}
|
|
1953
|
+
if (failure === undefined) {
|
|
1954
|
+
refilled += 1
|
|
1955
|
+
continue
|
|
1956
|
+
}
|
|
1957
|
+
failed += 1
|
|
1958
|
+
firstError ??= failure
|
|
1959
|
+
if (failed >= refillFailureLimit) {
|
|
1960
|
+
stopped = `${failed} communities failed`
|
|
1961
|
+
break
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
const durationMs = Math.round(owner.logical.now() - startedAt)
|
|
1965
|
+
const shardText = `${shards.size} shard${shards.size === 1 ? "" : "s"}`
|
|
1966
|
+
owner.logging.log({
|
|
1967
|
+
level: failed > 0 || stopped !== undefined ? "warn" : "info",
|
|
1968
|
+
category: "lifecycle",
|
|
1969
|
+
code: "lifecycle.cacheRefill",
|
|
1970
|
+
message:
|
|
1971
|
+
stopped !== undefined
|
|
1972
|
+
? `The cache refill after ${shardText} resumed saved sessions stopped after ${refilled} of ${guildIds.length} communities, because ${stopped}. The remaining cache entries fill as events and requests arrive`
|
|
1973
|
+
: failed > 0
|
|
1974
|
+
? `Refilled the caches of ${refilled} of ${guildIds.length} communities after ${shardText} resumed saved sessions. The others failed, so their cache entries fill as events and requests arrive`
|
|
1975
|
+
: `Refilled the caches of ${refilled} communit${refilled === 1 ? "y" : "ies"} after ${shardText} resumed saved sessions, in ${formatDuration(durationMs)}`,
|
|
1976
|
+
durationMs,
|
|
1977
|
+
...(firstError === undefined ? {} : { error: firstError }),
|
|
1978
|
+
fields: { guilds: guildIds.length, refilled, failed, shards: shards.size },
|
|
1979
|
+
})
|
|
1980
|
+
})
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* Count the bot's guilds by paging the current-user guild list within timeoutMs. Automatic sharding and the
|
|
1985
|
+
* supervisor's automatic plan both size their plans from this count. A page that does not move past the previous
|
|
1986
|
+
* one fails at once as a discovery ConnectionError, instead of paging until the deadline
|
|
1987
|
+
*/
|
|
1988
|
+
countGuilds(timeoutMs: number): Effect.Effect<number, ConnectionFailure> {
|
|
1989
|
+
const owner = this
|
|
1990
|
+
return Effect.gen(function* () {
|
|
1991
|
+
const clock = yield* Clock.Clock
|
|
1992
|
+
const deadline = nowMs(clock) + timeoutMs
|
|
1993
|
+
let guilds = 0
|
|
1994
|
+
let after: string | undefined
|
|
1995
|
+
while (true) {
|
|
1996
|
+
const remaining = Math.floor(deadline - nowMs(clock))
|
|
1997
|
+
if (remaining <= 0) return yield* Effect.fail(new ConnectionTimeoutError(timeoutMs))
|
|
1998
|
+
const page = yield* owner
|
|
1999
|
+
.guild(
|
|
2000
|
+
"guilds.fetchPage",
|
|
2001
|
+
() => guildList({ limit: guildCountPageSize, ...(after === undefined ? {} : { after }) }),
|
|
2002
|
+
{ timeoutMs: remaining },
|
|
2003
|
+
)
|
|
2004
|
+
.pipe(Effect.mapError((error) => automaticPlanFailure(error, timeoutMs)))
|
|
2005
|
+
// As in guilds.iterate, a page that repeats or goes back past the cursor would page until the deadline
|
|
2006
|
+
const cursor = after
|
|
2007
|
+
if (cursor !== undefined && page.some((guild) => BigInt(guild.id) <= BigInt(cursor)))
|
|
2008
|
+
return yield* Effect.fail(
|
|
2009
|
+
new ConnectionError("discovery", "protocol", null, {
|
|
2010
|
+
details: {
|
|
2011
|
+
detail: "the community list returned a page that did not move past the previous one, so the community count for automatic sharding could not be read",
|
|
2012
|
+
},
|
|
2013
|
+
hint: 'Set sharding.totalShards to a number instead of "auto"',
|
|
2014
|
+
}),
|
|
2015
|
+
)
|
|
2016
|
+
guilds += page.length
|
|
2017
|
+
if (page.length < guildCountPageSize) return guilds
|
|
2018
|
+
after = page.at(-1)!.id
|
|
2019
|
+
}
|
|
2020
|
+
})
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
/**
|
|
2024
|
+
* Size the plan from the bot's guild count, read by paging the current-user guild list under the startup deadline.
|
|
2025
|
+
* GET /gateway/bot is never used, because Fluxer returns a fixed compatibility shard count there
|
|
2026
|
+
*/
|
|
2027
|
+
#automaticPlan(configuration: Configuration<M>): Effect.Effect<ShardPlan, ConnectionFailure> {
|
|
2028
|
+
const owner = this
|
|
2029
|
+
return Effect.gen(function* () {
|
|
2030
|
+
const guilds = yield* owner.countGuilds(configuration.startupTimeoutMs)
|
|
2031
|
+
if (owner.#plan !== undefined) return owner.#plan
|
|
2032
|
+
const plan = automaticShardPlan(guilds)
|
|
2033
|
+
owner.#adoptPlan(plan, "Connecting")
|
|
2034
|
+
owner.logging.log({
|
|
2035
|
+
level: "info",
|
|
2036
|
+
category: "lifecycle",
|
|
2037
|
+
code: "lifecycle.automaticSharding",
|
|
2038
|
+
message: `Automatic sharding chose ${plan.totalShards} shard${plan.totalShards === 1 ? "" : "s"} for ${guilds} communit${guilds === 1 ? "y" : "ies"}, aiming for at most ${guildsPerShard} communities per shard on average`,
|
|
2039
|
+
fields: { guilds, totalShards: plan.totalShards, guildsPerShard },
|
|
2040
|
+
})
|
|
2041
|
+
return plan
|
|
2042
|
+
})
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Save the resumable session of each shard that was Connected when shutdown began, once, after its socket closed.
|
|
2047
|
+
* A shard that was recovering is skipped, because its session may already have expired. Failures are logged in
|
|
2048
|
+
* full and never block shutdown
|
|
2049
|
+
*/
|
|
2050
|
+
#saveSessions(): Effect.Effect<void> {
|
|
2051
|
+
const store = this.#configuration?.sessions
|
|
2052
|
+
const live = this.#saveOnClose
|
|
2053
|
+
this.#saveOnClose = undefined
|
|
2054
|
+
const plan = this.#plan
|
|
2055
|
+
if (!store || !live || !plan) return Effect.void
|
|
2056
|
+
const owner = this
|
|
2057
|
+
const shards = [...this.#shards.values()].filter(
|
|
2058
|
+
(shard) =>
|
|
2059
|
+
live.has(shard.shardId) &&
|
|
2060
|
+
shard.session.id !== undefined &&
|
|
2061
|
+
shard.session.sequence !== null &&
|
|
2062
|
+
shard.gatewayUrl !== undefined,
|
|
2063
|
+
)
|
|
2064
|
+
return Effect.forEach(
|
|
2065
|
+
shards,
|
|
2066
|
+
(shard) => {
|
|
2067
|
+
const snapshot: SessionSnapshot = Object.freeze({
|
|
2068
|
+
sessionId: shard.session.id!,
|
|
2069
|
+
sequence: shard.session.sequence!,
|
|
2070
|
+
resumeUrl: shard.gatewayUrl!,
|
|
2071
|
+
savedAt: Date.now(),
|
|
2072
|
+
totalShards: plan.totalShards,
|
|
2073
|
+
})
|
|
2074
|
+
// @effect-diagnostics-next-line unknownInEffectCatch:off
|
|
2075
|
+
return Effect.tryPromise({
|
|
2076
|
+
try: () => Promise.resolve(store.save(shard.shardId, snapshot)),
|
|
2077
|
+
catch: (error) => error,
|
|
2078
|
+
}).pipe(
|
|
2079
|
+
Effect.timeoutOrElse({
|
|
2080
|
+
duration: sessionSaveTimeoutMs,
|
|
2081
|
+
orElse: () =>
|
|
2082
|
+
Effect.fail(
|
|
2083
|
+
// @effect-diagnostics-next-line globalErrorInEffectFailure:off
|
|
2084
|
+
new Error(
|
|
2085
|
+
`The session store's save did not finish within ${formatDuration(sessionSaveTimeoutMs)}`,
|
|
2086
|
+
),
|
|
2087
|
+
),
|
|
2088
|
+
}),
|
|
2089
|
+
Effect.matchEffect({
|
|
2090
|
+
onSuccess: () =>
|
|
2091
|
+
Effect.sync(() =>
|
|
2092
|
+
owner.logging.log({
|
|
2093
|
+
level: "debug",
|
|
2094
|
+
category: "lifecycle",
|
|
2095
|
+
code: "lifecycle.sessionSaved",
|
|
2096
|
+
message: `Saved shard ${shard.shardId}'s session, so the next start can resume it`,
|
|
2097
|
+
shardId: shard.shardId,
|
|
2098
|
+
}),
|
|
2099
|
+
),
|
|
2100
|
+
onFailure: (error) =>
|
|
2101
|
+
Effect.sync(() =>
|
|
2102
|
+
owner.logging.log({
|
|
2103
|
+
level: "error",
|
|
2104
|
+
category: "lifecycle",
|
|
2105
|
+
code: "lifecycle.sessionSaveFailed",
|
|
2106
|
+
message: `Shard ${shard.shardId}'s session could not be saved, so the next start begins a new session`,
|
|
2107
|
+
shardId: shard.shardId,
|
|
2108
|
+
error,
|
|
2109
|
+
origin: "application",
|
|
2110
|
+
}),
|
|
2111
|
+
),
|
|
2112
|
+
}),
|
|
2113
|
+
)
|
|
2114
|
+
},
|
|
2115
|
+
{ concurrency: "unbounded", discard: true },
|
|
2116
|
+
)
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
#start(): Effect.Effect<void, ConnectError> {
|
|
2120
|
+
const owner = this
|
|
2121
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
2122
|
+
Effect.gen(function* () {
|
|
2123
|
+
if (owner.#state === "Closing" || owner.#state === "Closed")
|
|
2124
|
+
return yield* Effect.fail(new ClientClosedError())
|
|
2125
|
+
if (owner.#state === "Connected") return
|
|
2126
|
+
if (owner.#state !== "Disconnected") return yield* Effect.fail(new ClientBusyError())
|
|
2127
|
+
const configuration = owner.#configuration!
|
|
2128
|
+
owner.#everStarted = true
|
|
2129
|
+
const creator = yield* Effect.withFiber((fiber) => Effect.succeed(fiber))
|
|
2130
|
+
owner.logging.banners(creator.context)
|
|
2131
|
+
const plan = owner.#plan
|
|
2132
|
+
const host = new URL(configuration.instance.bootstrap).host
|
|
2133
|
+
owner.logging.log(
|
|
2134
|
+
{
|
|
2135
|
+
level: "info",
|
|
2136
|
+
category: "lifecycle",
|
|
2137
|
+
code: "lifecycle.starting",
|
|
2138
|
+
message:
|
|
2139
|
+
plan === undefined
|
|
2140
|
+
? `Starting Fluxerly ${sdkVersion()} for ${host} with an automatic shard count`
|
|
2141
|
+
: `Starting Fluxerly ${sdkVersion()} for ${host} with ${plan.shardIds.length} shard${plan.shardIds.length === 1 ? "" : "s"}`,
|
|
2142
|
+
fields: {
|
|
2143
|
+
version: sdkVersion(),
|
|
2144
|
+
instance: host,
|
|
2145
|
+
shards: plan?.shardIds.length ?? null,
|
|
2146
|
+
totalShards: plan?.totalShards ?? null,
|
|
2147
|
+
},
|
|
2148
|
+
},
|
|
2149
|
+
creator.context,
|
|
2150
|
+
)
|
|
2151
|
+
const startup = Deferred.makeUnsafe<void, ConnectError>()
|
|
2152
|
+
let becameReady = false
|
|
2153
|
+
owner.#workerExit = undefined
|
|
2154
|
+
owner.#groupReady = false
|
|
2155
|
+
for (const shard of owner.#shards.values()) {
|
|
2156
|
+
shard.state = "Connecting"
|
|
2157
|
+
shard.recovery = null
|
|
2158
|
+
}
|
|
2159
|
+
owner.#setState("Connecting")
|
|
2160
|
+
const trackReady = owner.subscribe((state) => {
|
|
2161
|
+
if (state === "Connected") becameReady = true
|
|
2162
|
+
})
|
|
2163
|
+
owner.#worker = yield* Effect.forkIn(
|
|
2164
|
+
owner.#supervise(configuration, startup).pipe(
|
|
2165
|
+
Effect.provideService(Clock.Clock, owner.logical.clock),
|
|
2166
|
+
Effect.onExit((exit) =>
|
|
2167
|
+
Effect.gen(function* () {
|
|
2168
|
+
trackReady()
|
|
2169
|
+
owner.#workerExit = exit
|
|
2170
|
+
const closing = owner.#state === "Closing" || owner.#state === "Closed"
|
|
2171
|
+
const interrupted = Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
|
|
2172
|
+
const defect = Exit.isFailure(exit) && Cause.hasDies(exit.cause)
|
|
2173
|
+
if (closing && !defect)
|
|
2174
|
+
Deferred.doneUnsafe(startup, Effect.fail(new ClientClosedError()))
|
|
2175
|
+
else Deferred.doneUnsafe(startup, exit)
|
|
2176
|
+
if (becameReady || defect) {
|
|
2177
|
+
const fiber = yield* Effect.withFiber((fiber) => Effect.succeed(fiber))
|
|
2178
|
+
const startedAt = Date.now()
|
|
2179
|
+
if (!closing)
|
|
2180
|
+
logShutdownStart(
|
|
2181
|
+
owner.logging,
|
|
2182
|
+
fiber.context,
|
|
2183
|
+
owner.#everStarted,
|
|
2184
|
+
"the connection ended",
|
|
2185
|
+
)
|
|
2186
|
+
owner.#setState("Closing")
|
|
2187
|
+
// An explicit shutdown lands here once the sockets are closed
|
|
2188
|
+
if (closing) yield* owner.#saveSessions()
|
|
2189
|
+
const services = yield* Effect.exit(owner.#closeServices())
|
|
2190
|
+
// The explicit shutdown that interrupted this worker logs the cleanup, including a socket
|
|
2191
|
+
// close that died. Otherwise this worker logs it below, even when a shutdown arrives during
|
|
2192
|
+
// the cleanup
|
|
2193
|
+
if (closing) {
|
|
2194
|
+
const dies = Exit.isFailure(exit)
|
|
2195
|
+
? exit.cause.reasons.filter((reason) => reason._tag === "Die")
|
|
2196
|
+
: []
|
|
2197
|
+
owner.#workerCleanup =
|
|
2198
|
+
dies.length > 0
|
|
2199
|
+
? Exit.failCause(
|
|
2200
|
+
Cause.combine(
|
|
2201
|
+
Cause.fromReasons(dies),
|
|
2202
|
+
Exit.isFailure(services) ? services.cause : Cause.empty,
|
|
2203
|
+
),
|
|
2204
|
+
)
|
|
2205
|
+
: services
|
|
2206
|
+
}
|
|
2207
|
+
const outcome = Exit.isFailure(services)
|
|
2208
|
+
? Exit.failCause(
|
|
2209
|
+
Cause.combine(
|
|
2210
|
+
Exit.isFailure(exit) ? exit.cause : Cause.empty,
|
|
2211
|
+
services.cause,
|
|
2212
|
+
),
|
|
2213
|
+
)
|
|
2214
|
+
: exit
|
|
2215
|
+
owner.#workerExit = outcome
|
|
2216
|
+
owner.#finish()
|
|
2217
|
+
if (!closing)
|
|
2218
|
+
logShutdownEnd(owner.logging, fiber.context, {
|
|
2219
|
+
everStarted: owner.#everStarted,
|
|
2220
|
+
startedAt,
|
|
2221
|
+
services,
|
|
2222
|
+
})
|
|
2223
|
+
Deferred.doneUnsafe(
|
|
2224
|
+
owner.#terminal,
|
|
2225
|
+
interrupted && !Exit.isFailure(services) ? Effect.void : outcome,
|
|
2226
|
+
)
|
|
2227
|
+
} else if (!closing) {
|
|
2228
|
+
owner.#setState("Disconnected")
|
|
2229
|
+
}
|
|
2230
|
+
}),
|
|
2231
|
+
),
|
|
2232
|
+
// Outcomes are retained above, not left as unobserved fiber failures
|
|
2233
|
+
// allow-silent: Outcomes are retained above, not left as unobserved fiber failures
|
|
2234
|
+
Effect.catchCause(() => Effect.void),
|
|
2235
|
+
),
|
|
2236
|
+
owner.scope,
|
|
2237
|
+
)
|
|
2238
|
+
const worker = owner.#worker
|
|
2239
|
+
// A shutdown can interrupt the worker before it runs, and then its exit handler above never runs.
|
|
2240
|
+
// Startup still ends, with the same ClientClosedError a closing client gives, instead of waiting forever
|
|
2241
|
+
worker.addObserver(() => Deferred.doneUnsafe(startup, Effect.fail(new ClientClosedError())))
|
|
2242
|
+
return yield* restore(Deferred.await(startup)).pipe(
|
|
2243
|
+
Effect.tap(() => Effect.sync(() => owner.#logConnected(creator.context))),
|
|
2244
|
+
Effect.onInterrupt(() =>
|
|
2245
|
+
Effect.gen(function* () {
|
|
2246
|
+
yield* Fiber.interrupt(worker)
|
|
2247
|
+
const exit = owner.#workerExit
|
|
2248
|
+
if (exit && Exit.isFailure(exit) && Cause.hasDies(exit.cause))
|
|
2249
|
+
return yield* Effect.failCause(exit.cause)
|
|
2250
|
+
}),
|
|
2251
|
+
),
|
|
2252
|
+
)
|
|
2253
|
+
}),
|
|
2254
|
+
)
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
connect(): Effect.Effect<void, ConnectError> {
|
|
2258
|
+
return Effect.suspend(() => {
|
|
2259
|
+
// A draining shutdown keeps the state until the drain ends, but the client never connects again
|
|
2260
|
+
if (this.#shutdownStarted || this.#state === "Closing" || this.#state === "Closed")
|
|
2261
|
+
return Effect.fail(new ClientClosedError())
|
|
2262
|
+
return this.#managed ? Effect.fail(new ClientBusyError()) : this.#start()
|
|
2263
|
+
})
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
waitForClose(): Effect.Effect<void, ConnectionFailure> {
|
|
2267
|
+
return Deferred.await(this.#terminal)
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
/**
|
|
2271
|
+
* Shut down once. A positive drainMs first stops event intake and lets running work finish until that deadline, while
|
|
2272
|
+
* the state and REST admission stay as they were
|
|
2273
|
+
*/
|
|
2274
|
+
shutdown(drainMs = 0): Effect.Effect<void> {
|
|
2275
|
+
return Effect.withFiber((fiber) =>
|
|
2276
|
+
this.events.ownsHandler(fiber.id) ||
|
|
2277
|
+
this.failures.owns(fiber.id) ||
|
|
2278
|
+
this.tasks.owns(fiber.id) ||
|
|
2279
|
+
[...this.#messageCollectors].some((collector) => collector.owns(fiber.id)) ||
|
|
2280
|
+
[...this.#reactionCollectors].some((collector) => collector.owns(fiber.id))
|
|
2281
|
+
? // A native handler cannot join its own cleanup: The client scope owns shutdown and interrupts this caller.
|
|
2282
|
+
// A drain would wait for this handler, so the handler ends at once instead of when the drain ends
|
|
2283
|
+
Effect.forkIn(this.#performShutdown(drainMs), this.scope).pipe(
|
|
2284
|
+
Effect.andThen(drainMs > 0 ? Effect.interrupt : Effect.never),
|
|
2285
|
+
)
|
|
2286
|
+
: this.#performShutdown(drainMs),
|
|
2287
|
+
)
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
#performShutdown(drainMs: number): Effect.Effect<void> {
|
|
2291
|
+
const owner = this
|
|
2292
|
+
return Effect.uninterruptible(
|
|
2293
|
+
Effect.suspend(() => {
|
|
2294
|
+
if (owner.#shutdownStarted) return Deferred.await(owner.#shutdown)
|
|
2295
|
+
if (owner.#state === "Closed") return Effect.void
|
|
2296
|
+
owner.#shutdownStarted = true
|
|
2297
|
+
const drain =
|
|
2298
|
+
drainMs > 0
|
|
2299
|
+
? Effect.withFiber((fiber) =>
|
|
2300
|
+
drainWork({
|
|
2301
|
+
events: owner.events,
|
|
2302
|
+
tasks: owner.tasks,
|
|
2303
|
+
requests: () => owner.rest.inFlight(),
|
|
2304
|
+
logging: owner.logging,
|
|
2305
|
+
drainMs,
|
|
2306
|
+
context: fiber.context,
|
|
2307
|
+
}).pipe(Effect.provideService(Clock.Clock, owner.logical.clock)),
|
|
2308
|
+
)
|
|
2309
|
+
: Effect.void
|
|
2310
|
+
return drain.pipe(
|
|
2311
|
+
Effect.andThen(owner.#closeAfterDrain()),
|
|
2312
|
+
Effect.onExit((exit) => Deferred.done(owner.#shutdown, exit)),
|
|
2313
|
+
)
|
|
2314
|
+
}),
|
|
2315
|
+
)
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
/** The shutdown steps after any drain. A connection that ended during the drain has already closed the client */
|
|
2319
|
+
#closeAfterDrain(): Effect.Effect<void> {
|
|
2320
|
+
const owner = this
|
|
2321
|
+
return Effect.suspend(() => {
|
|
2322
|
+
if (owner.#state === "Closed") return Effect.void
|
|
2323
|
+
// Only sessions that are live now are worth resuming later
|
|
2324
|
+
owner.#saveOnClose = new Set(
|
|
2325
|
+
[...owner.#shards.values()]
|
|
2326
|
+
.filter((shard) => shard.state === "Connected")
|
|
2327
|
+
.map((shard) => shard.shardId),
|
|
2328
|
+
)
|
|
2329
|
+
owner.#setState("Closing")
|
|
2330
|
+
owner.events.stop()
|
|
2331
|
+
owner.rest.stop()
|
|
2332
|
+
owner.tasks.stop()
|
|
2333
|
+
return Effect.gen(function* () {
|
|
2334
|
+
const fiber = yield* Effect.withFiber((fiber) => Effect.succeed(fiber))
|
|
2335
|
+
const startedAt = Date.now()
|
|
2336
|
+
logShutdownStart(owner.logging, fiber.context, owner.#everStarted, undefined)
|
|
2337
|
+
if (owner.#worker) yield* Fiber.interrupt(owner.#worker)
|
|
2338
|
+
// Every shard's socket is closed now, so a saved session cannot receive further dispatches
|
|
2339
|
+
yield* owner.#saveSessions()
|
|
2340
|
+
const services = yield* Effect.exit(owner.#closeServices())
|
|
2341
|
+
const exit = owner.#workerExit
|
|
2342
|
+
// An interrupted worker already closed its sockets and ran the service cleanup, so its failures are logged here
|
|
2343
|
+
const earlier = owner.#workerCleanup
|
|
2344
|
+
const cleaned =
|
|
2345
|
+
earlier && Exit.isFailure(earlier)
|
|
2346
|
+
? Exit.failCause(
|
|
2347
|
+
Cause.combine(earlier.cause, Exit.isFailure(services) ? services.cause : Cause.empty),
|
|
2348
|
+
)
|
|
2349
|
+
: services
|
|
2350
|
+
owner.#finish()
|
|
2351
|
+
logShutdownEnd(owner.logging, fiber.context, {
|
|
2352
|
+
everStarted: owner.#everStarted,
|
|
2353
|
+
startedAt,
|
|
2354
|
+
services: cleaned,
|
|
2355
|
+
})
|
|
2356
|
+
if (Exit.isFailure(services)) {
|
|
2357
|
+
Deferred.doneUnsafe(owner.#terminal, services)
|
|
2358
|
+
return yield* Effect.failCause(services.cause)
|
|
2359
|
+
}
|
|
2360
|
+
if (exit && Exit.isFailure(exit) && Cause.hasDies(exit.cause)) {
|
|
2361
|
+
Deferred.doneUnsafe(owner.#terminal, exit)
|
|
2362
|
+
return yield* Effect.failCause(
|
|
2363
|
+
Cause.fromReasons<never>(exit.cause.reasons.filter((reason) => reason._tag !== "Fail")),
|
|
2364
|
+
)
|
|
2365
|
+
}
|
|
2366
|
+
Deferred.doneUnsafe(owner.#terminal, Effect.void)
|
|
2367
|
+
})
|
|
2368
|
+
})
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
run(): Effect.Effect<void, ConnectError> {
|
|
2372
|
+
const owner = this
|
|
2373
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
2374
|
+
Effect.suspend(() => {
|
|
2375
|
+
if (owner.#shutdownStarted || owner.#state === "Closed" || owner.#state === "Closing")
|
|
2376
|
+
return Effect.fail(new ClientClosedError())
|
|
2377
|
+
if (owner.#managed || owner.#state !== "Disconnected") return Effect.fail(new ClientBusyError())
|
|
2378
|
+
owner.#managed = true
|
|
2379
|
+
return restore(owner.#start().pipe(Effect.andThen(owner.waitForClose()))).pipe(
|
|
2380
|
+
Effect.ensuring(owner.shutdown()),
|
|
2381
|
+
)
|
|
2382
|
+
}),
|
|
2383
|
+
)
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
export function makeClient<F extends MessageFields | undefined = undefined>(
|
|
2388
|
+
options: unknown,
|
|
2389
|
+
scope: Scope.Scope,
|
|
2390
|
+
native = false,
|
|
2391
|
+
): Effect.Effect<ClientOwner<SelectedMessage<F>>, ConfigurationError> {
|
|
2392
|
+
return Effect.gen(function* () {
|
|
2393
|
+
const identifyGate = typeof options === "object" && options !== null ? identifyGates.get(options) : undefined
|
|
2394
|
+
const limits = typeof options === "object" && options !== null ? accountLimits.get(options) : undefined
|
|
2395
|
+
if (typeof options === "object" && options !== null) {
|
|
2396
|
+
identifyGates.delete(options)
|
|
2397
|
+
accountLimits.delete(options)
|
|
2398
|
+
}
|
|
2399
|
+
const configuration = yield* validateConfiguration<F>(options, native)
|
|
2400
|
+
return yield* configuration.logging.provide(
|
|
2401
|
+
Effect.gen(function* () {
|
|
2402
|
+
const context = yield* Effect.context<never>()
|
|
2403
|
+
if (native) configuration.logging.context = context
|
|
2404
|
+
const callback = configuration.onError as ((report: unknown) => unknown) | undefined
|
|
2405
|
+
const hook = callback
|
|
2406
|
+
? (report: InternalReport): Effect.Effect<unknown, unknown> =>
|
|
2407
|
+
native
|
|
2408
|
+
? Effect.suspend(() => {
|
|
2409
|
+
const result = callback(report)
|
|
2410
|
+
// The report worker supplies the captured creation context, including the hook's services
|
|
2411
|
+
return Effect.isEffect(result)
|
|
2412
|
+
? (result as Effect.Effect<unknown, unknown>)
|
|
2413
|
+
: Effect.die(
|
|
2414
|
+
new TypeError(
|
|
2415
|
+
"An onError hook in the native Effect API must return an Effect",
|
|
2416
|
+
),
|
|
2417
|
+
)
|
|
2418
|
+
})
|
|
2419
|
+
: // @effect-diagnostics-next-line unknownInEffectCatch:off
|
|
2420
|
+
Effect.tryPromise({
|
|
2421
|
+
try: () => Promise.resolve(callback(publicReport(report))).then(throwIfErr),
|
|
2422
|
+
catch: (error) => error,
|
|
2423
|
+
})
|
|
2424
|
+
: undefined
|
|
2425
|
+
const failures = new FailureReporter(configuration.logging, hook, () => context)
|
|
2426
|
+
const logical = yield* makeLogicalScheduler(scope, (owner, cause) =>
|
|
2427
|
+
configuration.logging.log({
|
|
2428
|
+
level: "error",
|
|
2429
|
+
category: owner.includes("cache") ? "cache" : "sdk",
|
|
2430
|
+
code: "sdk.timerFailed",
|
|
2431
|
+
message: `The ${owner} timer callback failed`,
|
|
2432
|
+
error: primaryError(cause),
|
|
2433
|
+
cause,
|
|
2434
|
+
}),
|
|
2435
|
+
)
|
|
2436
|
+
return new ClientOwner(
|
|
2437
|
+
configuration,
|
|
2438
|
+
scope,
|
|
2439
|
+
failures,
|
|
2440
|
+
logical,
|
|
2441
|
+
identifyGate,
|
|
2442
|
+
limits,
|
|
2443
|
+
configuration.transport.transport,
|
|
2444
|
+
)
|
|
2445
|
+
}),
|
|
2446
|
+
)
|
|
2447
|
+
})
|
|
2448
|
+
}
|