virello 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BREAKING_CHANGES.md +69 -0
- package/LICENSE +21 -0
- package/README.md +161 -2
- package/docs/console-logging.md +257 -0
- package/docs/engine.md +144 -0
- package/docs/sharding-recovery.md +163 -0
- package/docs/sharding-scaling.md +197 -0
- package/docs/sharding-stats.md +121 -0
- package/examples/README.md +288 -0
- package/examples/bot.ts +42 -0
- package/examples/client.ts +36 -0
- package/examples/commands/ping.ts +25 -0
- package/examples/commands/shard.ts +67 -0
- package/examples/commands/stats.ts +46 -0
- package/examples/config.ts +110 -0
- package/examples/contracts.ts +15 -0
- package/examples/coordinator.ts +50 -0
- package/examples/engine/MockDiscord.ts +159 -0
- package/examples/engine/live.ts +78 -0
- package/examples/engine/offline.ts +83 -0
- package/examples/host.ts +48 -0
- package/examples/listeners/ready.ts +20 -0
- package/examples/logging.ts +50 -0
- package/examples/offline.ts +93 -0
- package/examples/registration.ts +11 -0
- package/examples/shutdown.ts +50 -0
- package/examples/stats.ts +44 -0
- package/examples/unregisterCommands.ts +21 -0
- package/package.json +110 -5
- package/src/commands/Command.ts +424 -0
- package/src/commands/CommandStore.ts +71 -0
- package/src/commands/arguments/Args.ts +688 -0
- package/src/commands/arguments/Argument.ts +116 -0
- package/src/commands/arguments/ArgumentStore.ts +10 -0
- package/src/commands/arguments/builtins/Boolean.ts +27 -0
- package/src/commands/arguments/builtins/Channel.ts +24 -0
- package/src/commands/arguments/builtins/DMChannel.ts +24 -0
- package/src/commands/arguments/builtins/Date.ts +36 -0
- package/src/commands/arguments/builtins/Emoji.ts +23 -0
- package/src/commands/arguments/builtins/Enum.ts +27 -0
- package/src/commands/arguments/builtins/Float.ts +36 -0
- package/src/commands/arguments/builtins/Guild.ts +24 -0
- package/src/commands/arguments/builtins/GuildCategoryChannel.ts +36 -0
- package/src/commands/arguments/builtins/GuildChannel.ts +35 -0
- package/src/commands/arguments/builtins/GuildNewsChannel.ts +36 -0
- package/src/commands/arguments/builtins/GuildNewsThreadChannel.ts +36 -0
- package/src/commands/arguments/builtins/GuildPrivateThreadChannel.ts +36 -0
- package/src/commands/arguments/builtins/GuildPublicThreadChannel.ts +35 -0
- package/src/commands/arguments/builtins/GuildStageVoiceChannel.ts +36 -0
- package/src/commands/arguments/builtins/GuildTextChannel.ts +35 -0
- package/src/commands/arguments/builtins/GuildThreadChannel.ts +35 -0
- package/src/commands/arguments/builtins/GuildVoiceChannel.ts +35 -0
- package/src/commands/arguments/builtins/Hyperlink.ts +24 -0
- package/src/commands/arguments/builtins/Integer.ts +36 -0
- package/src/commands/arguments/builtins/Member.ts +41 -0
- package/src/commands/arguments/builtins/Message.ts +31 -0
- package/src/commands/arguments/builtins/Number.ts +36 -0
- package/src/commands/arguments/builtins/PartialDMChannel.ts +26 -0
- package/src/commands/arguments/builtins/Role.ts +35 -0
- package/src/commands/arguments/builtins/String.ts +34 -0
- package/src/commands/arguments/builtins/User.ts +24 -0
- package/src/commands/arguments/contexts.ts +32 -0
- package/src/commands/arguments/register.ts +166 -0
- package/src/commands/arguments/resolvers/boolean.ts +33 -0
- package/src/commands/arguments/resolvers/channel.ts +33 -0
- package/src/commands/arguments/resolvers/date.ts +41 -0
- package/src/commands/arguments/resolvers/dmChannel.ts +30 -0
- package/src/commands/arguments/resolvers/emoji.ts +51 -0
- package/src/commands/arguments/resolvers/enum.ts +38 -0
- package/src/commands/arguments/resolvers/float.ts +39 -0
- package/src/commands/arguments/resolvers/guild.ts +25 -0
- package/src/commands/arguments/resolvers/guildCategoryChannel.ts +27 -0
- package/src/commands/arguments/resolvers/guildChannel.ts +53 -0
- package/src/commands/arguments/resolvers/guildNewsChannel.ts +27 -0
- package/src/commands/arguments/resolvers/guildNewsThreadChannel.ts +28 -0
- package/src/commands/arguments/resolvers/guildPrivateThreadChannel.ts +28 -0
- package/src/commands/arguments/resolvers/guildPublicThreadChannel.ts +28 -0
- package/src/commands/arguments/resolvers/guildStageVoiceChannel.ts +27 -0
- package/src/commands/arguments/resolvers/guildTextChannel.ts +27 -0
- package/src/commands/arguments/resolvers/guildThreadChannel.ts +27 -0
- package/src/commands/arguments/resolvers/guildVoiceChannel.ts +27 -0
- package/src/commands/arguments/resolvers/hyperlink.ts +18 -0
- package/src/commands/arguments/resolvers/index.ts +27 -0
- package/src/commands/arguments/resolvers/integer.ts +39 -0
- package/src/commands/arguments/resolvers/internal/resolveGuildChannelPredicate.ts +24 -0
- package/src/commands/arguments/resolvers/member.ts +59 -0
- package/src/commands/arguments/resolvers/message.ts +179 -0
- package/src/commands/arguments/resolvers/number.ts +38 -0
- package/src/commands/arguments/resolvers/partialDMChannel.ts +30 -0
- package/src/commands/arguments/resolvers/role.ts +46 -0
- package/src/commands/arguments/resolvers/string.ts +37 -0
- package/src/commands/arguments/resolvers/user.ts +31 -0
- package/src/commands/checks/Precondition.ts +266 -0
- package/src/commands/checks/PreconditionStore.ts +153 -0
- package/src/commands/checks/builtins/ClientPermissions.ts +220 -0
- package/src/commands/checks/builtins/Cooldown.ts +173 -0
- package/src/commands/checks/builtins/DMOnly.ts +48 -0
- package/src/commands/checks/builtins/Enabled.ts +73 -0
- package/src/commands/checks/builtins/GuildNewsOnly.ts +65 -0
- package/src/commands/checks/builtins/GuildNewsThreadOnly.ts +59 -0
- package/src/commands/checks/builtins/GuildOnly.ts +48 -0
- package/src/commands/checks/builtins/GuildPrivateThreadOnly.ts +58 -0
- package/src/commands/checks/builtins/GuildPublicThreadOnly.ts +58 -0
- package/src/commands/checks/builtins/GuildTextOnly.ts +64 -0
- package/src/commands/checks/builtins/GuildThreadOnly.ts +50 -0
- package/src/commands/checks/builtins/GuildVoiceOnly.ts +51 -0
- package/src/commands/checks/builtins/NSFW.ts +64 -0
- package/src/commands/checks/builtins/RunIn.ts +111 -0
- package/src/commands/checks/builtins/UserPermissions.ts +109 -0
- package/src/commands/checks/containers/Container.ts +43 -0
- package/src/commands/checks/containers/Entry.ts +151 -0
- package/src/commands/checks/containers/Group.ts +236 -0
- package/src/commands/checks/containers/conditions/Condition.ts +66 -0
- package/src/commands/checks/containers/conditions/all.ts +71 -0
- package/src/commands/checks/containers/conditions/any.ts +102 -0
- package/src/commands/checks/containers/permissions/client.ts +24 -0
- package/src/commands/checks/containers/permissions/user.ts +24 -0
- package/src/commands/checks/core.ts +90 -0
- package/src/commands/checks/register.ts +94 -0
- package/src/commands/checks/resolvers/clientPermissions.ts +17 -0
- package/src/commands/checks/resolvers/cooldown.ts +37 -0
- package/src/commands/checks/resolvers/index.ts +5 -0
- package/src/commands/checks/resolvers/nsfw.ts +11 -0
- package/src/commands/checks/resolvers/runIn.ts +55 -0
- package/src/commands/checks/resolvers/userPermissions.ts +17 -0
- package/src/commands/enums.ts +93 -0
- package/src/commands/errors/ArgumentError.ts +44 -0
- package/src/commands/errors/Identifiers.ts +134 -0
- package/src/commands/errors/PreconditionError.ts +38 -0
- package/src/commands/errors/UserError.ts +39 -0
- package/src/commands/execution/autocomplete.ts +57 -0
- package/src/commands/execution/chatInput/accepted.ts +58 -0
- package/src/commands/execution/chatInput/beforeRun.ts +47 -0
- package/src/commands/execution/chatInput/resolve.ts +52 -0
- package/src/commands/execution/contextMenu/accepted.ts +58 -0
- package/src/commands/execution/contextMenu/beforeRun.ts +47 -0
- package/src/commands/execution/contextMenu/resolve.ts +52 -0
- package/src/commands/message/listeners/messageCommandAccepted.ts +55 -0
- package/src/commands/message/listeners/messageCommandTyping.ts +38 -0
- package/src/commands/message/listeners/messageCreate.ts +19 -0
- package/src/commands/message/listeners/preMessageCommandRun.ts +47 -0
- package/src/commands/message/listeners/preMessageParser.ts +117 -0
- package/src/commands/message/listeners/prefixedMessage.ts +78 -0
- package/src/commands/message/listeners/register.ts +41 -0
- package/src/commands/registration/Registry.ts +845 -0
- package/src/commands/registration/api.ts +33 -0
- package/src/commands/registration/computeDifferences/_shared.ts +84 -0
- package/src/commands/registration/computeDifferences/contexts.ts +57 -0
- package/src/commands/registration/computeDifferences/default_member_permissions.ts +14 -0
- package/src/commands/registration/computeDifferences/description.ts +19 -0
- package/src/commands/registration/computeDifferences/dm_permission.ts +14 -0
- package/src/commands/registration/computeDifferences/integration_types.ts +58 -0
- package/src/commands/registration/computeDifferences/localizations.ts +89 -0
- package/src/commands/registration/computeDifferences/name.ts +19 -0
- package/src/commands/registration/computeDifferences/option/autocomplete.ts +120 -0
- package/src/commands/registration/computeDifferences/option/channelTypes.ts +99 -0
- package/src/commands/registration/computeDifferences/option/minMaxLength.ts +76 -0
- package/src/commands/registration/computeDifferences/option/minMaxValue.ts +78 -0
- package/src/commands/registration/computeDifferences/option/required.ts +19 -0
- package/src/commands/registration/computeDifferences/option/type.ts +26 -0
- package/src/commands/registration/computeDifferences/options.ts +310 -0
- package/src/commands/registration/computeDifferences.ts +167 -0
- package/src/commands/registration/getNeededParameters.ts +62 -0
- package/src/commands/registration/listeners/bulkOverwrite.ts +28 -0
- package/src/commands/registration/listeners/initialising.ts +22 -0
- package/src/commands/registration/listeners/register.ts +23 -0
- package/src/commands/registration/listeners/registered.ts +27 -0
- package/src/commands/registration/normalizeInputs.ts +202 -0
- package/src/commands/registration/registries.ts +340 -0
- package/src/commands/registration/registriesErrors.ts +41 -0
- package/src/commands/registration/registriesLog.ts +41 -0
- package/src/commands/types.ts +256 -0
- package/src/core/Container.ts +41 -0
- package/src/core/client/RiloClient.ts +636 -0
- package/src/core/config/extensions.ts +87 -0
- package/src/core/config/options.ts +29 -0
- package/src/core/errors/MissingModuleError.ts +19 -0
- package/src/core/errors/ModuleCleanupError.ts +2 -0
- package/src/core/errors/ModuleLoadError.ts +32 -0
- package/src/core/index.ts +69 -0
- package/src/core/lifecycle/Extension.ts +25 -0
- package/src/core/loading/ModuleGlob.ts +159 -0
- package/src/core/loading/ModuleLoader.ts +116 -0
- package/src/core/loading/Shared.ts +2 -0
- package/src/core/loading/constants.ts +2 -0
- package/src/core/loading/contracts.ts +71 -0
- package/src/core/logging/LoggerLike.ts +27 -0
- package/src/core/logging/console.ts +325 -0
- package/src/core/logging/diagnostics.ts +60 -0
- package/src/core/logging/format.ts +406 -0
- package/src/core/logging/index.ts +15 -0
- package/src/core/logging/presentation.ts +343 -0
- package/src/core/logging/redact.ts +167 -0
- package/src/core/logging/text.ts +46 -0
- package/src/core/modules/AliasModule.ts +64 -0
- package/src/core/modules/AliasModuleStore.ts +48 -0
- package/src/core/modules/Module.ts +136 -0
- package/src/core/modules/ModuleLocation.ts +66 -0
- package/src/core/modules/ModuleRegistry.ts +372 -0
- package/src/core/modules/ModuleStore.ts +447 -0
- package/src/core/modules/index.ts +12 -0
- package/src/core/runtime.ts +2 -0
- package/src/engine/cache/CacheStore.ts +126 -0
- package/src/engine/cache/EngineCache.ts +374 -0
- package/src/engine/cache/ScopedCache.ts +126 -0
- package/src/engine/cache/activity.ts +78 -0
- package/src/engine/cache/merge.ts +26 -0
- package/src/engine/cache/types.ts +96 -0
- package/src/engine/client/EngineClient.ts +280 -0
- package/src/engine/client/EngineInteraction.ts +83 -0
- package/src/engine/client/EngineMessage.ts +34 -0
- package/src/engine/client/EventHub.ts +70 -0
- package/src/engine/client/types.ts +76 -0
- package/src/engine/common.ts +72 -0
- package/src/engine/gateway/GatewayShard.ts +392 -0
- package/src/engine/gateway/Heartbeat.ts +48 -0
- package/src/engine/gateway/IdentifyGate.ts +46 -0
- package/src/engine/gateway/protocol.ts +60 -0
- package/src/engine/gateway/types.ts +27 -0
- package/src/engine/rest/RateLimits.ts +164 -0
- package/src/engine/rest/RequestQueue.ts +100 -0
- package/src/engine/rest/RestClient.ts +402 -0
- package/src/engine/rest/RestError.ts +17 -0
- package/src/events/errors/autocomplete.ts +28 -0
- package/src/events/errors/chatInput.ts +28 -0
- package/src/events/errors/contextMenu.ts +28 -0
- package/src/events/errors/interaction.ts +28 -0
- package/src/events/errors/interactionParse.ts +31 -0
- package/src/events/errors/listener.ts +23 -0
- package/src/events/errors/message.ts +28 -0
- package/src/events/errors/register.ts +53 -0
- package/src/events/errors/registration.ts +24 -0
- package/src/events/events.ts +855 -0
- package/src/events/interactionCreate.ts +41 -0
- package/src/events/interactions/interactionHandler.ts +113 -0
- package/src/events/interactions/interactionHandlerStore.ts +158 -0
- package/src/events/listener.ts +120 -0
- package/src/events/listenerModuleLoader.ts +42 -0
- package/src/events/listenerStore.ts +14 -0
- package/src/events/ready.ts +27 -0
- package/src/events/register.ts +58 -0
- package/src/extensions/localisation/LocaleManager.ts +394 -0
- package/src/extensions/localisation/commands.ts +479 -0
- package/src/extensions/localisation/extension.ts +21 -0
- package/src/extensions/localisation/functions.ts +316 -0
- package/src/extensions/localisation/index.ts +4 -0
- package/src/extensions/localisation/resources.ts +143 -0
- package/src/extensions/localisation/types.ts +216 -0
- package/src/extensions/patternCommands/PatternCommand.ts +82 -0
- package/src/extensions/patternCommands/PatternCommandStore.ts +10 -0
- package/src/extensions/patternCommands/events.ts +19 -0
- package/src/extensions/patternCommands/extension.ts +14 -0
- package/src/extensions/patternCommands/index.ts +8 -0
- package/src/extensions/patternCommands/listeners/commandAccepted.ts +56 -0
- package/src/extensions/patternCommands/listeners/messageParse.ts +123 -0
- package/src/extensions/patternCommands/listeners/preCommandRun.ts +66 -0
- package/src/extensions/patternCommands/listeners/register.ts +23 -0
- package/src/extensions/patternCommands/types.ts +70 -0
- package/src/extensions/scheduledTasks/Task.ts +101 -0
- package/src/extensions/scheduledTasks/TaskScheduler.ts +523 -0
- package/src/extensions/scheduledTasks/TaskStore.ts +47 -0
- package/src/extensions/scheduledTasks/backends/MemoryBackend.ts +103 -0
- package/src/extensions/scheduledTasks/backends/RedisBackend.ts +163 -0
- package/src/extensions/scheduledTasks/backends/TaskBackend.ts +19 -0
- package/src/extensions/scheduledTasks/backends/scripts.ts +83 -0
- package/src/extensions/scheduledTasks/extension.ts +26 -0
- package/src/extensions/scheduledTasks/index.ts +6 -0
- package/src/extensions/scheduledTasks/listeners/error.ts +27 -0
- package/src/extensions/scheduledTasks/listeners/notFound.ts +24 -0
- package/src/extensions/scheduledTasks/listeners/register.ts +17 -0
- package/src/extensions/scheduledTasks/timing.ts +179 -0
- package/src/extensions/scheduledTasks/types/TaskEvents.ts +55 -0
- package/src/extensions/scheduledTasks/types/TaskTypes.ts +199 -0
- package/src/extensions/services/Service.ts +18 -0
- package/src/extensions/services/ServiceStore.ts +10 -0
- package/src/extensions/services/Services.ts +20 -0
- package/src/extensions/services/extension.ts +14 -0
- package/src/extensions/services/index.ts +14 -0
- package/src/extensions/subcommands/Subcommand.ts +715 -0
- package/src/extensions/subcommands/extension.ts +15 -0
- package/src/extensions/subcommands/index.ts +30 -0
- package/src/extensions/subcommands/listeners/chatInputError.ts +35 -0
- package/src/extensions/subcommands/listeners/chatInputNoMatch.ts +28 -0
- package/src/extensions/subcommands/listeners/messageError.ts +32 -0
- package/src/extensions/subcommands/listeners/messageNoMatch.ts +30 -0
- package/src/extensions/subcommands/listeners/missingChatInputHandler.ts +37 -0
- package/src/extensions/subcommands/listeners/missingMessageHandler.ts +37 -0
- package/src/extensions/subcommands/listeners/register.ts +42 -0
- package/src/extensions/subcommands/preconditionResolvers/subcommandCooldown.ts +83 -0
- package/src/extensions/subcommands/preconditions/SubcommandCooldown.ts +189 -0
- package/src/extensions/subcommands/preconditions/register.ts +12 -0
- package/src/extensions/subcommands/types/Enums.ts +11 -0
- package/src/extensions/subcommands/types/Events.ts +228 -0
- package/src/extensions/subcommands/types/mappings.ts +67 -0
- package/src/index.ts +19 -0
- package/src/public/client.ts +10 -0
- package/src/public/commands.ts +45 -0
- package/src/public/discord.ts +12 -0
- package/src/public/engine.ts +58 -0
- package/src/public/events.ts +12 -0
- package/src/public/extensions.ts +16 -0
- package/src/public/localisation.ts +7 -0
- package/src/public/modules.ts +32 -0
- package/src/public/scheduling.ts +11 -0
- package/src/public/services.ts +7 -0
- package/src/public/sharding.ts +7 -0
- package/src/public/time.ts +9 -0
- package/src/public/utilities.ts +22 -0
- package/src/public/validation.ts +14 -0
- package/src/sharding/LICENSE +191 -0
- package/src/sharding/client/Link.ts +735 -0
- package/src/sharding/client/gateway.ts +113 -0
- package/src/sharding/coordinator/Coordinator.ts +1696 -0
- package/src/sharding/coordinator/identify.ts +111 -0
- package/src/sharding/coordinator/scaling.ts +186 -0
- package/src/sharding/index.ts +4 -0
- package/src/sharding/manager/Manager.ts +1430 -0
- package/src/sharding/messaging.ts +185 -0
- package/src/sharding/stats.ts +91 -0
- package/src/sharding/transport/availability.ts +76 -0
- package/src/sharding/transport/pending.ts +79 -0
- package/src/sharding/transport/protocol.ts +317 -0
- package/src/sharding/transport/socket.ts +5 -0
- package/src/sharding/types.ts +292 -0
- package/src/utilities/array/arrayStrictEquals.ts +13 -0
- package/src/utilities/array/chunk.ts +11 -0
- package/src/utilities/array/filterNullAndUndefined.ts +11 -0
- package/src/utilities/array/filterNullAndUndefinedAndEmpty.ts +15 -0
- package/src/utilities/array/filterNullAndUndefinedAndZero.ts +15 -0
- package/src/utilities/array/partition.ts +26 -0
- package/src/utilities/array/pickRandom.ts +36 -0
- package/src/utilities/array/range.ts +6 -0
- package/src/utilities/async/events/index.ts +149 -0
- package/src/utilities/async/queue/AsyncQueue.ts +70 -0
- package/src/utilities/async/queue/_AsyncQueueEntry.ts +59 -0
- package/src/utilities/async/queue/index.ts +1 -0
- package/src/utilities/binary/buffer/DuplexBuffer.ts +209 -0
- package/src/utilities/binary/buffer/UnalignedUint16Array.ts +520 -0
- package/src/utilities/binary/index.ts +8 -0
- package/src/utilities/binary/schema/Schema.ts +376 -0
- package/src/utilities/binary/schema/SchemaStore.ts +157 -0
- package/src/utilities/binary/shared/Pointer.ts +57 -0
- package/src/utilities/binary/shared/_common.ts +24 -0
- package/src/utilities/binary/shared/codepoints.ts +21 -0
- package/src/utilities/binary/types/Array.ts +33 -0
- package/src/utilities/binary/types/BigInt32.ts +15 -0
- package/src/utilities/binary/types/BigInt64.ts +15 -0
- package/src/utilities/binary/types/BigUint32.ts +15 -0
- package/src/utilities/binary/types/BigUint64.ts +15 -0
- package/src/utilities/binary/types/Bit.ts +15 -0
- package/src/utilities/binary/types/Boolean.ts +15 -0
- package/src/utilities/binary/types/Constant.ts +18 -0
- package/src/utilities/binary/types/FixedLengthArray.ts +40 -0
- package/src/utilities/binary/types/Float32.ts +15 -0
- package/src/utilities/binary/types/Float64.ts +15 -0
- package/src/utilities/binary/types/Int16.ts +15 -0
- package/src/utilities/binary/types/Int2.ts +15 -0
- package/src/utilities/binary/types/Int32.ts +15 -0
- package/src/utilities/binary/types/Int4.ts +15 -0
- package/src/utilities/binary/types/Int64.ts +15 -0
- package/src/utilities/binary/types/Int8.ts +15 -0
- package/src/utilities/binary/types/Nullable.ts +25 -0
- package/src/utilities/binary/types/Snowflake.ts +15 -0
- package/src/utilities/binary/types/String.ts +29 -0
- package/src/utilities/binary/types/Uint16.ts +15 -0
- package/src/utilities/binary/types/Uint2.ts +15 -0
- package/src/utilities/binary/types/Uint32.ts +15 -0
- package/src/utilities/binary/types/Uint4.ts +15 -0
- package/src/utilities/binary/types/Uint64.ts +15 -0
- package/src/utilities/binary/types/Uint8.ts +15 -0
- package/src/utilities/binary/types/base/IType.ts +18 -0
- package/src/utilities/binary/types/index.ts +113 -0
- package/src/utilities/binary/utilities.ts +42 -0
- package/src/utilities/collections/bitfield/BitField.ts +232 -0
- package/src/utilities/collections/bitfield/index.ts +2 -0
- package/src/utilities/collections/bitfield/utilities.ts +12 -0
- package/src/utilities/decorators/base-decorators.ts +25 -0
- package/src/utilities/decorators/djs-decorators.ts +161 -0
- package/src/utilities/decorators/index.ts +4 -0
- package/src/utilities/decorators/module-decorators.ts +221 -0
- package/src/utilities/decorators/utils.ts +60 -0
- package/src/utilities/discord/builders/MessageBuilder.ts +119 -0
- package/src/utilities/discord/constants/TwemojiRegex.ts +8 -0
- package/src/utilities/discord/constants/index.ts +8 -0
- package/src/utilities/discord/constants/limits.ts +499 -0
- package/src/utilities/discord/constants/regexes.ts +59 -0
- package/src/utilities/discord/constants/resolvers/AutocompleteInteractionOptionResolver.ts +139 -0
- package/src/utilities/discord/constants/resolvers/ChatInputInteractionOptionResolver.ts +501 -0
- package/src/utilities/discord/constants/resolvers/ContextMenuInteractionOptionResolver.ts +96 -0
- package/src/utilities/discord/constants/resolvers/ModalInteractionOptionResolver.ts +135 -0
- package/src/utilities/discord/constants/resolvers/util.ts +72 -0
- package/src/utilities/discord/index.ts +7 -0
- package/src/utilities/discord/pagination/LazyPaginatedMessage.ts +121 -0
- package/src/utilities/discord/pagination/PaginatedFieldMessageEmbed.ts +118 -0
- package/src/utilities/discord/pagination/PaginatedMessage.ts +1345 -0
- package/src/utilities/discord/pagination/PaginatedMessageEmbedFields.ts +103 -0
- package/src/utilities/discord/pagination/PaginatedMessageTypes.ts +238 -0
- package/src/utilities/discord/pagination/Pagination.ts +536 -0
- package/src/utilities/discord/pagination/index.ts +7 -0
- package/src/utilities/discord/pagination/utils.ts +244 -0
- package/src/utilities/discord/prompts/ExplicitReturnTypes.ts +41 -0
- package/src/utilities/discord/prompts/MessagePrompter.ts +134 -0
- package/src/utilities/discord/prompts/constants.ts +17 -0
- package/src/utilities/discord/prompts/index.ts +12 -0
- package/src/utilities/discord/prompts/strategies/MessagePrompterBaseStrategy.ts +161 -0
- package/src/utilities/discord/prompts/strategies/MessagePrompterConfirmStrategy.ts +60 -0
- package/src/utilities/discord/prompts/strategies/MessagePrompterMessageStrategy.ts +82 -0
- package/src/utilities/discord/prompts/strategies/MessagePrompterNumberStrategy.ts +84 -0
- package/src/utilities/discord/prompts/strategies/MessagePrompterReactionStrategy.ts +50 -0
- package/src/utilities/discord/prompts/strategyOptions.ts +35 -0
- package/src/utilities/discord/snowflake/DiscordSnowflake.ts +4 -0
- package/src/utilities/discord/snowflake/Snowflake.ts +208 -0
- package/src/utilities/discord/snowflake/TwitterSnowflake.ts +4 -0
- package/src/utilities/discord/snowflake/index.ts +3 -0
- package/src/utilities/discord/type-guards.ts +262 -0
- package/src/utilities/discord/utilities.ts +127 -0
- package/src/utilities/discord/utility-types.ts +86 -0
- package/src/utilities/filesystem/findFilesRecursively.ts +78 -0
- package/src/utilities/filesystem/index.ts +1 -0
- package/src/utilities/function/cast.ts +4 -0
- package/src/utilities/function/debounce/LICENSE +47 -0
- package/src/utilities/function/debounce/debounce.ts +164 -0
- package/src/utilities/function/lazy.ts +6 -0
- package/src/utilities/function/noop.ts +2 -0
- package/src/utilities/function/poll.ts +82 -0
- package/src/utilities/function/pollSync.ts +81 -0
- package/src/utilities/function/retry.ts +27 -0
- package/src/utilities/function/retrySync.ts +22 -0
- package/src/utilities/function/sleep.ts +45 -0
- package/src/utilities/function/sleepSync.ts +6 -0
- package/src/utilities/function/throttle.ts +37 -0
- package/src/utilities/guards/classExtends.ts +15 -0
- package/src/utilities/guards/hasAtLeastOneKeyInMap.ts +7 -0
- package/src/utilities/guards/hasAtLeastOneKeyInObject.ts +9 -0
- package/src/utilities/guards/isClass.ts +6 -0
- package/src/utilities/guards/isFunction.ts +6 -0
- package/src/utilities/guards/isNullOrUndefined.ts +8 -0
- package/src/utilities/guards/isNullOrUndefinedOrEmpty.ts +11 -0
- package/src/utilities/guards/isNullOrUndefinedOrZero.ts +9 -0
- package/src/utilities/guards/isNumber.ts +7 -0
- package/src/utilities/guards/isObject.ts +36 -0
- package/src/utilities/guards/isPrimitive.ts +8 -0
- package/src/utilities/guards/isThenable.ts +31 -0
- package/src/utilities/http/HttpError.ts +28 -0
- package/src/utilities/http/index.ts +2 -0
- package/src/utilities/http/request.ts +135 -0
- package/src/utilities/index.ts +53 -0
- package/src/utilities/iterators/all.ts +1 -0
- package/src/utilities/iterators/any.ts +1 -0
- package/src/utilities/iterators/append.ts +12 -0
- package/src/utilities/iterators/at.ts +17 -0
- package/src/utilities/iterators/average.ts +23 -0
- package/src/utilities/iterators/chain.ts +11 -0
- package/src/utilities/iterators/chunk.ts +29 -0
- package/src/utilities/iterators/collect.ts +1 -0
- package/src/utilities/iterators/collectInto.ts +14 -0
- package/src/utilities/iterators/compact.ts +12 -0
- package/src/utilities/iterators/compare.ts +15 -0
- package/src/utilities/iterators/compareBy.ts +34 -0
- package/src/utilities/iterators/compress.ts +20 -0
- package/src/utilities/iterators/contains.ts +14 -0
- package/src/utilities/iterators/count.ts +14 -0
- package/src/utilities/iterators/cycle.ts +22 -0
- package/src/utilities/iterators/difference.ts +17 -0
- package/src/utilities/iterators/drop.ts +28 -0
- package/src/utilities/iterators/dropLast.ts +20 -0
- package/src/utilities/iterators/dropWhile.ts +40 -0
- package/src/utilities/iterators/empty.ts +11 -0
- package/src/utilities/iterators/enumerate.ts +12 -0
- package/src/utilities/iterators/equal.ts +12 -0
- package/src/utilities/iterators/equalBy.ts +26 -0
- package/src/utilities/iterators/every.ts +40 -0
- package/src/utilities/iterators/filter.ts +38 -0
- package/src/utilities/iterators/find.ts +30 -0
- package/src/utilities/iterators/findIndex.ts +22 -0
- package/src/utilities/iterators/first.ts +8 -0
- package/src/utilities/iterators/flat.ts +11 -0
- package/src/utilities/iterators/flatMap.ts +19 -0
- package/src/utilities/iterators/forEach.ts +16 -0
- package/src/utilities/iterators/from.ts +50 -0
- package/src/utilities/iterators/fuse.ts +23 -0
- package/src/utilities/iterators/greaterOrEqualThan.ts +14 -0
- package/src/utilities/iterators/greaterThan.ts +14 -0
- package/src/utilities/iterators/index.ts +91 -0
- package/src/utilities/iterators/indexOf.ts +19 -0
- package/src/utilities/iterators/inspect.ts +22 -0
- package/src/utilities/iterators/intersect.ts +12 -0
- package/src/utilities/iterators/intersperse.ts +34 -0
- package/src/utilities/iterators/isEmpty.ts +8 -0
- package/src/utilities/iterators/isSorted.ts +10 -0
- package/src/utilities/iterators/isSortedBy.ts +37 -0
- package/src/utilities/iterators/isSortedByKey.ts +41 -0
- package/src/utilities/iterators/last.ts +14 -0
- package/src/utilities/iterators/lessOrEqualThan.ts +14 -0
- package/src/utilities/iterators/lessThan.ts +14 -0
- package/src/utilities/iterators/map.ts +19 -0
- package/src/utilities/iterators/max.ts +13 -0
- package/src/utilities/iterators/maxBy.ts +34 -0
- package/src/utilities/iterators/maxByKey.ts +44 -0
- package/src/utilities/iterators/min.ts +10 -0
- package/src/utilities/iterators/minBy.ts +35 -0
- package/src/utilities/iterators/minByKey.ts +44 -0
- package/src/utilities/iterators/notEqual.ts +12 -0
- package/src/utilities/iterators/nth.ts +1 -0
- package/src/utilities/iterators/partition.ts +48 -0
- package/src/utilities/iterators/peekable.ts +32 -0
- package/src/utilities/iterators/position.ts +1 -0
- package/src/utilities/iterators/prepend.ts +14 -0
- package/src/utilities/iterators/product.ts +13 -0
- package/src/utilities/iterators/range.ts +44 -0
- package/src/utilities/iterators/reduce.ts +40 -0
- package/src/utilities/iterators/repeat.ts +21 -0
- package/src/utilities/iterators/reverse.ts +12 -0
- package/src/utilities/iterators/shared/_assertFunction.ts +9 -0
- package/src/utilities/iterators/shared/_assertNotNegative.ts +7 -0
- package/src/utilities/iterators/shared/_assertPositive.ts +7 -0
- package/src/utilities/iterators/shared/_compare.ts +47 -0
- package/src/utilities/iterators/shared/_makeIterableIterator.ts +10 -0
- package/src/utilities/iterators/shared/_toIntegerOrInfinityOrThrow.ts +10 -0
- package/src/utilities/iterators/shared/_toIntegerOrThrow.ts +12 -0
- package/src/utilities/iterators/shared/_toNumberOrThrow.ts +38 -0
- package/src/utilities/iterators/shared/comparators.ts +33 -0
- package/src/utilities/iterators/skip.ts +1 -0
- package/src/utilities/iterators/skipLast.ts +1 -0
- package/src/utilities/iterators/skipWhile.ts +1 -0
- package/src/utilities/iterators/slice.ts +70 -0
- package/src/utilities/iterators/some.ts +23 -0
- package/src/utilities/iterators/sorted.ts +10 -0
- package/src/utilities/iterators/starMap.ts +32 -0
- package/src/utilities/iterators/stepBy.ts +30 -0
- package/src/utilities/iterators/sum.ts +16 -0
- package/src/utilities/iterators/take.ts +25 -0
- package/src/utilities/iterators/takeLast.ts +30 -0
- package/src/utilities/iterators/takeWhile.ts +1 -0
- package/src/utilities/iterators/tee.ts +42 -0
- package/src/utilities/iterators/toArray.ts +9 -0
- package/src/utilities/iterators/toIterableIterator.ts +14 -0
- package/src/utilities/iterators/union.ts +20 -0
- package/src/utilities/iterators/unique.ts +10 -0
- package/src/utilities/iterators/unzip.ts +43 -0
- package/src/utilities/iterators/windows.ts +30 -0
- package/src/utilities/iterators/zip.ts +36 -0
- package/src/utilities/number/roundNumber.ts +11 -0
- package/src/utilities/object/deepClone.ts +86 -0
- package/src/utilities/object/getDeepObjectKeys.ts +92 -0
- package/src/utilities/object/makeObject.ts +32 -0
- package/src/utilities/object/mergeDefault.ts +37 -0
- package/src/utilities/object/mergeObjects.ts +33 -0
- package/src/utilities/object/objectEntries.ts +14 -0
- package/src/utilities/object/objectKeys.ts +12 -0
- package/src/utilities/object/objectToTuples.ts +19 -0
- package/src/utilities/object/objectValues.ts +12 -0
- package/src/utilities/object/omitKeysFromObject.ts +15 -0
- package/src/utilities/parsing/ArgumentStream.ts +440 -0
- package/src/utilities/parsing/index.ts +13 -0
- package/src/utilities/parsing/lexer/Lexer.ts +48 -0
- package/src/utilities/parsing/lexer/streams/ParameterStream.ts +37 -0
- package/src/utilities/parsing/lexer/streams/parameters/BaseParameter.ts +21 -0
- package/src/utilities/parsing/lexer/streams/parameters/QuotedParameter.ts +32 -0
- package/src/utilities/parsing/lexer/streams/parameters/WordParameter.ts +26 -0
- package/src/utilities/parsing/lexer/streams/raw/TokenStream.ts +110 -0
- package/src/utilities/parsing/parser/Parser.ts +37 -0
- package/src/utilities/parsing/parser/ParserResult.ts +60 -0
- package/src/utilities/parsing/parser/strategies/EmptyStrategy.ts +15 -0
- package/src/utilities/parsing/parser/strategies/IUnorderedStrategy.ts +10 -0
- package/src/utilities/parsing/parser/strategies/PrefixedStrategy.ts +71 -0
- package/src/utilities/parsing/strategies/FlagUnorderedStrategy.ts +76 -0
- package/src/utilities/parsing/util/util.ts +29 -0
- package/src/utilities/result/Option.ts +481 -0
- package/src/utilities/result/OptionError.ts +7 -0
- package/src/utilities/result/Result.ts +564 -0
- package/src/utilities/result/ResultError.ts +20 -0
- package/src/utilities/result/common/utils.ts +38 -0
- package/src/utilities/result/index.ts +4 -0
- package/src/utilities/text/capitalizeFirstLetter.ts +4 -0
- package/src/utilities/text/codeBlock.ts +15 -0
- package/src/utilities/text/cutText.ts +26 -0
- package/src/utilities/text/inlineCodeBlock.ts +13 -0
- package/src/utilities/text/regExpEsc.ts +6 -0
- package/src/utilities/text/snakeToCamelCase.ts +8 -0
- package/src/utilities/text/splitText.ts +13 -0
- package/src/utilities/text/toTitleCase.ts +48 -0
- package/src/utilities/text/tryParseJSON.ts +16 -0
- package/src/utilities/text/tryParseURL.ts +13 -0
- package/src/utilities/time/cron/Cron.ts +162 -0
- package/src/utilities/time/cron/constants.ts +88 -0
- package/src/utilities/time/cron/index.ts +2 -0
- package/src/utilities/time/duration/Duration.ts +157 -0
- package/src/utilities/time/duration/DurationFormatter.ts +113 -0
- package/src/utilities/time/duration/constants.ts +109 -0
- package/src/utilities/time/duration/index.ts +3 -0
- package/src/utilities/time/index.ts +29 -0
- package/src/utilities/time/ratelimits/RateLimit.ts +63 -0
- package/src/utilities/time/ratelimits/RateLimitManager.ts +85 -0
- package/src/utilities/time/ratelimits/index.ts +2 -0
- package/src/utilities/time/stopwatch/index.ts +68 -0
- package/src/utilities/time/timers/TimerManager.ts +53 -0
- package/src/utilities/time/timers/index.ts +1 -0
- package/src/utilities/time/timestamp/Timestamp.ts +264 -0
- package/src/utilities/time/timestamp/constants.ts +62 -0
- package/src/utilities/time/timestamp/index.ts +2 -0
- package/src/utilities/types.ts +191 -0
- package/src/version.ts +3 -0
package/docs/engine.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Original Discord engine milestone
|
|
2
|
+
|
|
3
|
+
`rilo/engine` is an experimental, independent Bun client implemented inside Rilo. It owns its gateway session, REST transport and synchronous cache. The existing RiloClient, module system, commands, extensions and root exports retain their current behavior. Importing the engine does not migrate framework features.
|
|
4
|
+
|
|
5
|
+
## Run it
|
|
6
|
+
|
|
7
|
+
Use Bun canary (`bun upgrade --canary`) and `bun install --frozen-lockfile`.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bun run engine:test
|
|
11
|
+
bun check
|
|
12
|
+
bun lint
|
|
13
|
+
bun fmt --check
|
|
14
|
+
bun examples/engine/offline.ts
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The offline example creates a loopback HTTP/WebSocket server, receives a message, replies using the engine's REST layer, then reconnects and resumes. It uses a literal fake token.
|
|
18
|
+
|
|
19
|
+
`bun examples/engine/live.ts` only logs instructions. To opt into Discord yourself, set `DISCORD_TOKEN` and add `--live`. The example requires Guilds, GuildMessages and MessageContent intents, plus permission to read and send in the channel. It replies to `!engine-ping`. Its interaction branch handles an already registered `engine-ping` command; it does not register commands. No live Discord validation was performed.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createConsoleLogger, EngineClient, logEvent } from "rilo/engine";
|
|
23
|
+
import {
|
|
24
|
+
GatewayIntentBits,
|
|
25
|
+
InteractionResponseType,
|
|
26
|
+
} from "discord-api-types/v10";
|
|
27
|
+
|
|
28
|
+
const logger = createConsoleLogger("MyBot");
|
|
29
|
+
const client = new EngineClient({
|
|
30
|
+
token: suppliedToken,
|
|
31
|
+
intents: GatewayIntentBits.Guilds | GatewayIntentBits.GuildMessages,
|
|
32
|
+
cache: { membersPerGuild: 1000, messagesPerChannel: 0 },
|
|
33
|
+
onError: ({ source, error, fatal }) =>
|
|
34
|
+
logEvent(logger, fatal ? "error" : "alert", {
|
|
35
|
+
component: `engine.${source}`,
|
|
36
|
+
event: "diagnostic",
|
|
37
|
+
message: fatal
|
|
38
|
+
? "Engine cannot continue; inspect the diagnostic cause before restarting."
|
|
39
|
+
: "Engine reported a recoverable failure; inspect the diagnostic cause.",
|
|
40
|
+
context: { fatal },
|
|
41
|
+
error,
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
client.on("messageCreate", async (message) => {
|
|
45
|
+
if (message.data.author.bot) return;
|
|
46
|
+
await message.reply({ content: "Hello", allowed_mentions: { parse: [] } });
|
|
47
|
+
});
|
|
48
|
+
client.on("interactionCreate", async (interaction) => {
|
|
49
|
+
if (interaction.data.type === 2)
|
|
50
|
+
await interaction.respond({
|
|
51
|
+
type: InteractionResponseType.ChannelMessageWithSource,
|
|
52
|
+
data: { content: "Hello", allowed_mentions: { parse: [] } },
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
await client.connect();
|
|
56
|
+
// client.close() aborts transport work and clears the session and caches.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Construction and imports open no connections. The engine never reads a token from the environment. Request bodies preserve snake_case fields and native discord-api-types/v10 types. `message.data` is an APIMessage; `interaction.data` is an APIInteraction. `message.reply` supplies a message_reference while preserving native reference options. `interaction.respond` accepts a complete native callback body.
|
|
60
|
+
|
|
61
|
+
The logging helpers share the framework's level, JSON/text, redaction and stack
|
|
62
|
+
settings; see [console logging](console-logging.md). The supplied `onError`
|
|
63
|
+
callback still receives the original error. Add fixed operation wording to the
|
|
64
|
+
record rather than printing arbitrary provider messages.
|
|
65
|
+
|
|
66
|
+
## Cache contract
|
|
67
|
+
|
|
68
|
+
| Cache | Default | Lookup | Retention |
|
|
69
|
+
| -------------------- | ------- | ------------------------------------ | ---------------------- |
|
|
70
|
+
| Guild metadata | On | `cache.guilds.get(id)` | Until removal/reset |
|
|
71
|
+
| Channels and threads | On | `cache.channels.get(id)` | Until removal/reset |
|
|
72
|
+
| Roles | On | `cache.roles.get(guildId, id)` | Until removal/reset |
|
|
73
|
+
| Members | On | `cache.members.get(guildId, userId)` | LRU, 1,000 per guild |
|
|
74
|
+
| Messages | Off | `cache.messages.get(channelId, id)` | Opt-in LRU per channel |
|
|
75
|
+
|
|
76
|
+
Members have **no combined client limit**. `membersPerGuild` accepts a nonnegative integer or a function of guild ID. Zero disables storage. `guilds`, `channels` and `roles` accept false. `messagesPerChannel` opts into a finite per-channel limit. A synchronous `policy({ kind, id, scope, data })` can reject admission; false also removes an existing entry. A thrown policy error stops the client and clears state instead of continuing after a partly applied transaction. Custom backends are deferred.
|
|
77
|
+
|
|
78
|
+
Results are undefined or a `$CacheEntry<T>` with complete and data. Patches create `complete: false`; accumulated partial fields never promote it. A full typed put, full gateway creation/chunk, or explicit REST fetch can promote it in place. Guild entries intentionally remain partial metadata: bulk members, channels, roles, presences and voice states live separately. This avoids retaining a second unbounded member snapshot inside the guild.
|
|
79
|
+
|
|
80
|
+
Resident entry and data identities remain stable during updates. Nested objects merge; omitted/undefined fields remain; explicit null replaces; arrays replace old contents, so `roles: []` removes membership. Identity ends at eviction, deletion, fresh identification or close. Treat cached objects as engine-owned mutable views; use structuredClone when you need a snapshot.
|
|
81
|
+
|
|
82
|
+
Targeted get, writes and gateway activity update LRU recency. Peek, has, values, entries and iteration do not. Activity discovery follows nested member payloads, guild-scoped user/author IDs, resolved member maps, mentioned users and member/user ID lists. Messages, commands, buttons, reactions, typing and voice payloads participate without an event-name whitelist. Future shapes without recognizable guild/member identity require an explicit cache call or adapter.
|
|
83
|
+
|
|
84
|
+
Deletion removes data before handlers. `cacheDelete` supplies the original dispatch in payload and previous when resident. Missing entries yield undefined; partial previous entries remain partial. Bulk message deletion finishes all removals before any deletion handler. Guild removal clears resident children. An unavailable guild remains marked unavailable and invalidates its member scope. Role deletion removes its ID from resident member role arrays without touching recency.
|
|
85
|
+
|
|
86
|
+
`fetchMember`, `fetchChannel` and `fetchMessage` are explicit HTTP operations; cache lookup never fetches. A fetch writes back only if no gateway dispatch or session reset occurred while pending. Otherwise it returns the HTTP snapshot without overwriting newer gateway state. This conservative rule avoids resurrecting deleted entries, but may skip insertion during unrelated activity.
|
|
87
|
+
|
|
88
|
+
## Ordering and failures
|
|
89
|
+
|
|
90
|
+
Each EngineClient owns one shard. Decoding, cache mutation and event invocation run synchronously in arrival order. Promise-returning handlers overlap; later cache processing does not await them. A live object's fields can change across await. Synchronous CPU-heavy user code still blocks Bun's event loop.
|
|
91
|
+
|
|
92
|
+
Events are ready, resumed, messageCreate, messageUpdate, interactionCreate, cacheDelete, dispatch and gatewayState. Raw dispatch follows specialized callbacks and sees updated cache. On returns an unsubscribe function. Handler exceptions/rejections go to onError without stopping later handlers. The default ceiling is 1,024 outstanding handler promises. Overflow reports a fatal error and closes the client instead of retaining unbounded work. Closing does not await or cancel developer promises; idle is available when they are known to settle.
|
|
93
|
+
|
|
94
|
+
Gateway ownership includes HELLO/session timeouts, jittered heartbeats, requested beats, ACK debt, reconnect backoff, session ID/sequence/resume URL, stale-generation rejection and cleanup. Fresh identification clears state; resume preserves it. Fatal close codes stop retries. Defaults: 10 reconnect attempts, 15-second handshake deadline, 8 MiB input frames, and capped exponential reconnect delays with jitter. Uncompressed v10 JSON needs no async decoding queue. Runtime validation covers the envelope/session fields, not every entity schema.
|
|
95
|
+
|
|
96
|
+
REST admits up to 8 concurrent attempts. Identical normalized routes and learned bucket hashes serialize within each channel/guild/webhook major partition; independent routes progress concurrently. Cooldowns apply before admitting more attempts. The bound is 1,024 logical requests, including retry delays. Each attempt has a 15-second timeout and an 8 MiB response limit. Rate-limit metadata is bounded independently; exhausting it while cooldowns remain active fails explicitly.
|
|
97
|
+
|
|
98
|
+
429 handling uses retry_after or Retry-After with a separate retry ceiling of 5. Transport and 5xx failures retry GET/PUT/DELETE at most twice. POST/PATCH are not replayed after ambiguous failure. Interaction initial responses cannot race; ambiguity leaves replyState unknown. JSON bodies are snapshotted on submission. Redirects are rejected and endpoints require TLS except loopback tests. Client/transport token fields are private and absent from ordinary JSON serialization.
|
|
99
|
+
|
|
100
|
+
## Validation results
|
|
101
|
+
|
|
102
|
+
Historical validation on 2026-10-11 used Bun 1.4.2 on Windows x64 and a disposable Redis 7.4.11 container:
|
|
103
|
+
|
|
104
|
+
- `bun install --frozen-lockfile --ignore-scripts`: succeeded without lock changes.
|
|
105
|
+
- `bun run engine:test`: 76 passing tests across four files, 279 assertions.
|
|
106
|
+
- `bun scripts/ci.ts` with `RILO_TEST_REDIS_URL`: 2,361 passing tests across 226 files, 4,943 assertions, zero failures and zero skipped tests.
|
|
107
|
+
- The same CI run passed type checking, the lint and formatting checks configured at the time, the existing offline framework example, the engine offline example and `bun run smoke`.
|
|
108
|
+
- The smoke test packed and installed the package in a temporary consumer, audited public exports/notices, loaded a framework command and ran the native engine connect/reply/resume example.
|
|
109
|
+
|
|
110
|
+
Engine coverage comprises 14 cache tests, 11 client/event/helper tests, 28 gateway/protocol/identify tests and 23 REST tests. They exercise partial/null updates, resident identity, LRU boundaries and recency, deletion ordering, concurrent handlers, fetch/delete races, heartbeat debt, disconnect/resume/session invalidation, malformed/premature frames, stale sockets, cleanup, rate limits, bounded retries, cancellation and request limits.
|
|
111
|
+
|
|
112
|
+
The live example was checked without `--live`. No real Discord token was retrieved and no live bot connection or Discord message was sent.
|
|
113
|
+
|
|
114
|
+
## Protocol and architectural references
|
|
115
|
+
|
|
116
|
+
Reviewed on 2026-10-11:
|
|
117
|
+
|
|
118
|
+
- [Gateway lifecycle](https://docs.discord.com/developers/events/gateway): discovery, HELLO, heartbeat/ACK, identify, resume URLs, sequences and session budgets.
|
|
119
|
+
- [Gateway events](https://docs.discord.com/developers/events/gateway-events) and [close codes](https://docs.discord.com/developers/topics/opcodes-and-status-codes): payloads and recoverable/fatal closures.
|
|
120
|
+
- [Rate limits](https://docs.discord.com/developers/topics/rate-limits): bucket hashes, major resources, fractional resets and global 429 handling.
|
|
121
|
+
- [Message resource](https://docs.discord.com/developers/resources/message) and [interaction responses](https://docs.discord.com/developers/interactions/receiving-and-responding): native body fields and callback endpoints.
|
|
122
|
+
- [discord-api-types 0.38.56](https://github.com/discordjs/discord-api-types/releases/tag/0.38.56): checked against public npm latest and pinned exactly in package.json and bun.lock.
|
|
123
|
+
|
|
124
|
+
`$MessageUpdateData` conservatively permits omitted native update fields with Partial plus required id/channel_id. Version 0.38.56 declares the create fields required for updates. This engine retains the agreed partial-update contract without renaming or reinterpreting fields.
|
|
125
|
+
|
|
126
|
+
The Ovencord checkout was read only. Package boundaries, WebSocketShard/identify strategies, REST SequentialHandler's bucket/major/global separation, and CachedManager's resident patching boundary informed the review. Its source, Collection abstraction, compatibility API and benchmark claims were not copied. Rilo uses its own Map/Set stores, scheduler, lifecycle and helpers.
|
|
127
|
+
|
|
128
|
+
## Production gaps
|
|
129
|
+
|
|
130
|
+
- A real bot session needs explicit approval. Extended reconnect soak tests and large real guild payloads remain untested; this milestone uses deterministic mocks and local servers.
|
|
131
|
+
- One shard per client is supported. The local identify budget cannot account for other processes or persist session starts across restarts. Supply waitForIdentify when sharing a token and integrate the existing Rilo coordinator.
|
|
132
|
+
- Add full payload validation and broader cache projections. Reaction counts, user updates across every guild, thread membership and ancillary guild collections still need reconciliation. This is not complete Discord cache coverage.
|
|
133
|
+
- Add compression, multipart uploads, additional typed endpoints, proactive global pacing, route-specific sublimits, retry telemetry and deadline-aware prioritization. Different routes can be coalesced only after headers reveal their shared bucket.
|
|
134
|
+
- Interaction callbacks share the bounded REST scheduler. Respond or defer promptly to meet Discord's initial-response deadline; automatic deferral, follow-up helpers and deadline reservation remain future work.
|
|
135
|
+
- Profile real traces before changing policy. Activity traversal, cache misses and JSON parsing need realistic workload evaluation.
|
|
136
|
+
|
|
137
|
+
## Future Rilo integration
|
|
138
|
+
|
|
139
|
+
1. Introduce engine-facing command/listener contexts using native payloads and thin helpers, then test adapters for current framework contracts.
|
|
140
|
+
2. Replace discord.js-specific permission, resolver, collector, pagination and registration dependencies at explicit boundaries without building a compatibility clone.
|
|
141
|
+
3. Wire ShardLink.waitForIdentify, shard readiness and cleanup into EngineClient's injected permits and state hooks.
|
|
142
|
+
4. Port extensions and example bots with contract tests before changing RiloClient's runtime and retiring discord.js dependencies.
|
|
143
|
+
|
|
144
|
+
The independent subpath is deliberate. The root API baseline stays intact, and a test verifies that this engine and the existing offline RiloClient coexist without changing the framework container.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Shard maintenance, leases and recovery
|
|
2
|
+
|
|
3
|
+
Each shard runs in its own Bun OS process. A `ShardManager` supervises its local
|
|
4
|
+
children and proxies their authenticated coordinator traffic. A child receives
|
|
5
|
+
only its own assignment and total count: no peer list, peer connections or
|
|
6
|
+
dependency on another shard's arrival. Coordination is entirely in memory.
|
|
7
|
+
There are no ownership files, state directories or persistent locks.
|
|
8
|
+
|
|
9
|
+
Fixed shard totals remain the default. Opt-in [automatic scaling](sharding-scaling.md)
|
|
10
|
+
retains its five-minute polling, three matching decisions and thirty-minute
|
|
11
|
+
cooldown defaults.
|
|
12
|
+
|
|
13
|
+
## Maintenance and bounded calls
|
|
14
|
+
|
|
15
|
+
A managed client starts with `client.maintenance === true`. A detected coordinator
|
|
16
|
+
connection loss immediately sets it back to `true` and keeps the existing Discord
|
|
17
|
+
session open. Socket closure is detected directly; a silent partition uses the
|
|
18
|
+
existing fifteen-second heartbeat deadline. Parent/child IPC heartbeats continue
|
|
19
|
+
through coordinator outages. Unmanaged clients default to `maintenance = false`.
|
|
20
|
+
|
|
21
|
+
Only authenticated control, an accepted lease, local assignment application and
|
|
22
|
+
coordinator registration clear maintenance. Opening a socket or receiving a
|
|
23
|
+
welcome is insufficient. Rilo does not install a command policy or send outage
|
|
24
|
+
replies: developers choose the response appropriate to each operation.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
if (client.maintenance) {
|
|
28
|
+
return interaction.reply(
|
|
29
|
+
"Coordination is temporarily unavailable. Try again shortly.",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Ordinary requests fail with `DISCONNECTED` while the local assignment is
|
|
35
|
+
unreconciled. Each request or broadcast can opt into bounded waiting:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const result = await client.sharding.request(0, "exampleStats", null, {
|
|
39
|
+
waitForCoordinator: true,
|
|
40
|
+
timeoutMs: 5000,
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The timeout includes waiting and execution; broadcasts share one overall deadline
|
|
46
|
+
across all targets. The default is `limits.timeoutMs` (15 seconds). Waits are
|
|
47
|
+
bounded by `limits.maxPending`, respect cancellation and return `TIMEOUT` or
|
|
48
|
+
`CAPACITY` instead of building an unlimited queue. Already dispatched calls fail
|
|
49
|
+
on disconnect and are never replayed. Waiting does not guarantee a destination
|
|
50
|
+
is available: routing can still return `UNAVAILABLE`, or `RESHARDING` during an
|
|
51
|
+
automatic scaling drain. Host manager calls support the same options.
|
|
52
|
+
|
|
53
|
+
Discord IDENTIFY still requires a coordinator permit. Control loss during a
|
|
54
|
+
permit wait waits for reconciliation within the shard's outage lifetime;
|
|
55
|
+
permit queue deadlines and exhausted allowance remain explicit failures.
|
|
56
|
+
An existing Discord session does not authorize new IDENTIFY calls offline.
|
|
57
|
+
|
|
58
|
+
## Leases and dormant processes
|
|
59
|
+
|
|
60
|
+
The coordinator maintains slots `0..shardCount - 1`. `workers[].shards` provide
|
|
61
|
+
initial placement preferences; leases determine actual ownership. `maxShards`
|
|
62
|
+
bounds new allocations per host, defaulting to its initial preference count
|
|
63
|
+
in fixed mode. Valid returning IDs are retained even if the host's configured
|
|
64
|
+
capacity was reduced during the outage. Allocation and lease installation are atomic, authenticated by
|
|
65
|
+
host credentials, supervisor instance and child generation. Duplicate sockets
|
|
66
|
+
for the same supervisor instance are refused. Different authenticated instances
|
|
67
|
+
can connect independently to reconcile their children or wait dormant.
|
|
68
|
+
|
|
69
|
+
A new/restarted process must obtain a valid assignment before opening Discord.
|
|
70
|
+
A returning process reports its previous ID. If valid and free, or already
|
|
71
|
+
reserved for that exact process, it keeps that ID. An invalid or taken ID is
|
|
72
|
+
refused: the child closes any old Discord session, forgets its ID
|
|
73
|
+
(`client.sharding.id === null`), warns clearly and becomes dormant. New processes
|
|
74
|
+
without capacity behave the same way.
|
|
75
|
+
|
|
76
|
+
Dormant children stay alive indefinitely with no Discord connection. They request
|
|
77
|
+
an assignment every `assignmentPollIntervalMs` (default 30,000), and open Discord
|
|
78
|
+
only after acceptance. Repeated full-capacity polls do not repeat the warning.
|
|
79
|
+
`client.login()` and transport-only `client.sharding.connect()` intentionally wait
|
|
80
|
+
for the first assignment. Ordinary coordinator RPC calls retain deadlines.
|
|
81
|
+
|
|
82
|
+
The host normally starts one child per initial placement. Optional `processCount`
|
|
83
|
+
sets a minimum local pool, including standby processes, within host capacity.
|
|
84
|
+
Scaling may require more children up to that capacity. Surviving processes are
|
|
85
|
+
reconciled independently, including IDs removed by a restarted coordinator.
|
|
86
|
+
|
|
87
|
+
`manager.processes` exposes a stable local `slot`, nullable leased `shard`, PID,
|
|
88
|
+
restart count and state. States include `maintenance` and `dormant`, alongside
|
|
89
|
+
`starting`, `running`, `backoff`, `failed` and `stopped`. Local slots are not
|
|
90
|
+
Discord shard IDs. Dormant clients remain in maintenance.
|
|
91
|
+
|
|
92
|
+
## Gateway changes and process lifetime
|
|
93
|
+
|
|
94
|
+
Recovery with the same ID and total preserves the Discord session. If the total
|
|
95
|
+
changed, the child closes its old gateway and reconnects with the accepted ID
|
|
96
|
+
and current total, even when the numerical ID is unchanged. RiloClient, modules
|
|
97
|
+
and application listeners stay alive. Assignment changes reset gateway resume
|
|
98
|
+
data and guild/channel/user caches. Extension login hooks run once per client;
|
|
99
|
+
Discord ready events may repeat. The gateway adapter targets discord.js 14.
|
|
100
|
+
|
|
101
|
+
A lost connection never releases a lease during the current coordinator lifetime.
|
|
102
|
+
Network loss is not proof of death. The owning supervisor must confirm the child
|
|
103
|
+
exited, including after reconnect, or release it after graceful shutdown/scaling.
|
|
104
|
+
Only then can an idle process acquire the slot. Another instance or generation
|
|
105
|
+
cannot release it. Lost first-lease replies and confirmed-exit acknowledgements
|
|
106
|
+
can be retried without stealing or releasing a replacement's assignment.
|
|
107
|
+
|
|
108
|
+
An assigned child has `maxDisconnectedLifetimeMs` (default 900,000: fifteen
|
|
109
|
+
minutes) to reconnect and reconcile. Failed connections, welcomes and failed
|
|
110
|
+
reconciliations never reset that timer. Success resets it for a future outage.
|
|
111
|
+
Expiry closes down and terminates the **entire shard process**; the existing host
|
|
112
|
+
restart/backoff policy applies. The host also bounds a stalled child's shutdown.
|
|
113
|
+
Dormant processes are exempt once their old Discord session is closed.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
new ShardManager({
|
|
117
|
+
id: "local",
|
|
118
|
+
url,
|
|
119
|
+
token,
|
|
120
|
+
script,
|
|
121
|
+
assignmentPollIntervalMs: 30_000,
|
|
122
|
+
maxDisconnectedLifetimeMs: 15 * 60_000,
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Polling accepts 50..300,000 ms; the outage lifetime accepts 1..2,147,483,647 ms.
|
|
127
|
+
`startupTimeoutMs` separately bounds boot, lease and local gateway application.
|
|
128
|
+
A gateway that cannot close/reconfigure is a process failure. `shutdownTimeoutMs`
|
|
129
|
+
bounds graceful exit. `reconnect: false` explicitly opts out of recovery and
|
|
130
|
+
stops children on loss. Hosts may start before the coordinator and retry with
|
|
131
|
+
capped backoff; closing the manager cancels the wait.
|
|
132
|
+
|
|
133
|
+
Parent IPC loss still terminates a responsive child, since it cannot reconnect
|
|
134
|
+
to a dead parent. Supervise each host's whole process tree. A new supervisor
|
|
135
|
+
instance cannot prove the old children died merely by presenting the same host
|
|
136
|
+
credentials. Their leases remain reserved for that coordinator lifetime; a
|
|
137
|
+
coordinator restart rebuilds the pool. No PID discovery or disk cleanup occurs.
|
|
138
|
+
|
|
139
|
+
## Coordinator restart tradeoff
|
|
140
|
+
|
|
141
|
+
Restart rebuilds zero-based slots from the currently required count (configured,
|
|
142
|
+
or the automatic scaling startup recommendation). Arrivals are admitted
|
|
143
|
+
independently, with **no global reconciliation grace period**.
|
|
144
|
+
|
|
145
|
+
An old partitioned Discord session may overlap a newly assigned replacement
|
|
146
|
+
until the old process reconnects or reaches its configured outage cutoff. If its
|
|
147
|
+
ID is taken on return, it closes Discord and becomes dormant. This policy cannot
|
|
148
|
+
promise zero duplicate processing or exactly-once delivery. A wedged process
|
|
149
|
+
needs host/service supervision to enforce termination. Run one coordinator per
|
|
150
|
+
bot; independent coordinators are not fenced from each other.
|
|
151
|
+
|
|
152
|
+
Use `botToken` in production. Each coordinator start waits 5.5 seconds before
|
|
153
|
+
fetching fresh `GET /gateway/bot` limits. This is an IDENTIFY cooldown, not a
|
|
154
|
+
reconciliation grace period. Permits are charged before sending and never refunded
|
|
155
|
+
on loss. An unexpired allowance cannot increase. `sessionStartLimit` is the
|
|
156
|
+
manual/offline alternative and needs a fresh snapshot on every start and expiry.
|
|
157
|
+
Restarting Rilo does not reset Discord's allowance.
|
|
158
|
+
|
|
159
|
+
Upgrade coordinator, hosts and children together: the internal protocol changed.
|
|
160
|
+
Restart running processes deliberately to load the new version. `statePath`,
|
|
161
|
+
`stateDirectory`, `releaseWorker`, `SHARD_STATE_PATH` and `SHARD_HOST_STATE_DIR`
|
|
162
|
+
remain removed. Legacy state files stay ignored and unused. See
|
|
163
|
+
[breaking changes](../BREAKING_CHANGES.md) and [examples](../examples/README.md).
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Automatic shard deployment
|
|
2
|
+
|
|
3
|
+
Automatic scaling is **off by default**. Without `scaling`, `shardCount` and
|
|
4
|
+
`workers[].shards` remain fixed and metadata is fetched only at startup or when
|
|
5
|
+
the identify allowance expires. Enable scaling on the coordinator; hosts run
|
|
6
|
+
the same `ShardManager` and bot entrypoint.
|
|
7
|
+
|
|
8
|
+
## Coordinator configuration
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { ShardCoordinator } from "rilo/sharding";
|
|
12
|
+
|
|
13
|
+
const coordinator = new ShardCoordinator({
|
|
14
|
+
shardCount: 2,
|
|
15
|
+
workers: [
|
|
16
|
+
{
|
|
17
|
+
id: "a",
|
|
18
|
+
token: process.env.HOST_A_SECRET!,
|
|
19
|
+
shards: [0],
|
|
20
|
+
maxShards: 4,
|
|
21
|
+
weight: 2,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "b",
|
|
25
|
+
token: process.env.HOST_B_SECRET!,
|
|
26
|
+
shards: [1],
|
|
27
|
+
maxShards: 2,
|
|
28
|
+
weight: 1,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
botToken: process.env.DISCORD_TOKEN!,
|
|
32
|
+
scaling: {
|
|
33
|
+
minShards: 2,
|
|
34
|
+
maxShards: 6,
|
|
35
|
+
targetGuildsPerShard: 1000,
|
|
36
|
+
pollIntervalMs: 300000,
|
|
37
|
+
cooldownMs: 1800000,
|
|
38
|
+
confirmations: 3,
|
|
39
|
+
allowDownscale: true,
|
|
40
|
+
rebalanceThreshold: 0.25,
|
|
41
|
+
sessionReserve: 10,
|
|
42
|
+
drainTimeoutMs: 30000,
|
|
43
|
+
readyTimeoutMs: 300000,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
await coordinator.start();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Keep credentials in your environment. Remote hosts need the normal TLS
|
|
50
|
+
configuration. This policy uses only configured hosts that are already running;
|
|
51
|
+
it never provisions machines, changes server settings or purchases capacity.
|
|
52
|
+
Every automatic host needs `maxShards`, including standby hosts with `shards: []`.
|
|
53
|
+
Initial assignments must cover `0..shardCount-1` exactly once and fit host capacity.
|
|
54
|
+
|
|
55
|
+
On startup, fresh Discord metadata, bounds and capacities determine the initial
|
|
56
|
+
assignment before hosts connect. `shardCount` is an initial configuration seed,
|
|
57
|
+
not a fixed count in automatic mode. No previous topology, guild counts,
|
|
58
|
+
confirmation history, cooldown or allowance is restored from disk. Set
|
|
59
|
+
`minShards` to retain an operational floor across coordinator restarts.
|
|
60
|
+
|
|
61
|
+
| Setting | Default | Meaning |
|
|
62
|
+
| ---------------------- | -------------- | ---------------------------------------------------------------------- |
|
|
63
|
+
| `maxShards` | Required | Global ceiling, 1–4096, also bounded by summed host capacity |
|
|
64
|
+
| `minShards` | `1` | Minimum total, at most `maxShards` |
|
|
65
|
+
| `targetGuildsPerShard` | Disabled (`0`) | Optional average target, 1–2500; requires accurate guild caches |
|
|
66
|
+
| `pollIntervalMs` | `300000` | Metadata polling interval, 60000–86400000 ms |
|
|
67
|
+
| `cooldownMs` | `1800000` | Minimum interval between transition attempts, 60000–604800000 ms |
|
|
68
|
+
| `confirmations` | `3` | Consecutive matching plans before a running deployment changes, 1–100 |
|
|
69
|
+
| `allowDownscale` | `true` | Allow fewer shards after confirmation and cooldown |
|
|
70
|
+
| `rebalanceThreshold` | `0.25` | Required improvement in peak weighted host guild load, 0.05–1 |
|
|
71
|
+
| `sessionReserve` | `0` | Unspent identify allowance to retain beyond the transition requirement |
|
|
72
|
+
| `drainTimeoutMs` | `30000` | Time for every host to confirm old process exits, 100–300000 ms |
|
|
73
|
+
| `readyTimeoutMs` | `300000` | Time for the new deployment to become gateway-ready, 100–3600000 ms |
|
|
74
|
+
| Host `maxShards` | Required | Maximum shard processes on this host, 1–4096 |
|
|
75
|
+
| Host `weight` | `1` | Relative resource capacity, 0.01–1000; set it from your host sizing |
|
|
76
|
+
|
|
77
|
+
## What Discord controls
|
|
78
|
+
|
|
79
|
+
Discord does not send a push event requesting a particular scale-up or
|
|
80
|
+
scale-down. The coordinator polls `GET /gateway/bot` for the recommended shard
|
|
81
|
+
count and fresh session-start metadata. Discord assigns guilds by
|
|
82
|
+
`(guild_id >> 22) % num_shards`; the framework cannot choose individual guilds or
|
|
83
|
+
move one guild to another shard independently. See the official
|
|
84
|
+
[Gateway sharding and session-start documentation](https://docs.discord.com/developers/events/gateway#sharding).
|
|
85
|
+
|
|
86
|
+
The desired count is the maximum of the recommendation, configured minimum and
|
|
87
|
+
`ceil(totalCachedGuilds / targetGuildsPerShard)` when that target is enabled.
|
|
88
|
+
Counts round up to multiples of the current recommendation, a conservative
|
|
89
|
+
choice compatible with Discord's large-bot shard-count multiple requirement.
|
|
90
|
+
This can overprovision compared with using arbitrary shard totals. The largest
|
|
91
|
+
legal count within configured capacity is used if the guild target cannot fit;
|
|
92
|
+
a recommendation or minimum that cannot fit blocks the change with
|
|
93
|
+
`SCALING_CAPACITY`. The recommendation is a floor, not an automatic instruction
|
|
94
|
+
to remove a particular existing shard.
|
|
95
|
+
|
|
96
|
+
Guild targets are averages, not a guarantee that every shard has that many
|
|
97
|
+
guilds. Discord's per-shard limit still applies. Uneven guild sizes, unavailable
|
|
98
|
+
guilds and custom caches affect the usefulness of guild counts. Keep the Guilds
|
|
99
|
+
intent and a complete guild cache if using guild targets. This is not CPU,
|
|
100
|
+
memory, member-count or event-volume monitoring.
|
|
101
|
+
|
|
102
|
+
For an unchanged total, allocation greedily places entire shards using observed
|
|
103
|
+
guild counts and host weights, while respecting per-host process limits.
|
|
104
|
+
Existing owners win equal-load ties. An allocation-only change requires the
|
|
105
|
+
configured improvement in peak weighted load. For a new total, individual shard
|
|
106
|
+
guild counts are unknown until login, so allocation uses equal estimated loads.
|
|
107
|
+
A later poll can rebalance hosts if the measured distribution warrants it.
|
|
108
|
+
|
|
109
|
+
## Transition and recovery
|
|
110
|
+
|
|
111
|
+
1. Fetch fresh metadata and apply the existing conservative identify accounting.
|
|
112
|
+
All configured hosts must be connected, including standby hosts. Shrinking,
|
|
113
|
+
guild-target changes and allocation-only balancing require every shard to
|
|
114
|
+
report gateway readiness and metrics no older than 15 seconds. A larger
|
|
115
|
+
Discord recommendation may grow an unready topology to repair insufficient
|
|
116
|
+
sharding. A degraded committed deployment must recover before another change.
|
|
117
|
+
2. Require repeated matching plans and the cooldown. Before disrupting children,
|
|
118
|
+
require unexpired remaining session starts of at least
|
|
119
|
+
`max(currentCount, proposedCount) + sessionReserve`. The larger count covers
|
|
120
|
+
either deployment or a pre-commit rollback. Insufficient budget defers the
|
|
121
|
+
change without stopping healthy children.
|
|
122
|
+
3. Reject pending/new shard RPC and identify work with `RESHARDING`. Ask all hosts
|
|
123
|
+
to drain. Each host cancels scheduled restarts, requests graceful shutdown,
|
|
124
|
+
escalates only its own children if necessary, and confirms actual process
|
|
125
|
+
exits. This interrupts bot availability and cancels in-flight work; application
|
|
126
|
+
jobs must tolerate restarts. Even allocation-only changes restart all shards.
|
|
127
|
+
4. If a host disconnects or draining times out before commit, retain the old
|
|
128
|
+
total and assignment. Still-connected hosts receive that assignment after
|
|
129
|
+
their drains finish; reconnecting hosts receive it in their welcome. An
|
|
130
|
+
unconfirmed child exit cannot authorize a replacement.
|
|
131
|
+
5. Once every old process is confirmed stopped, commit the new count and
|
|
132
|
+
assignment in memory and start fresh children. After this point recovery
|
|
133
|
+
moves forward: reconnecting hosts receive the committed topology. There is
|
|
134
|
+
no rollback to a different total while new children are running.
|
|
135
|
+
6. Wait for gateway readiness. Failed startup/readiness leaves status
|
|
136
|
+
`degraded`; ordinary configured process retries and host reconnection continue.
|
|
137
|
+
Once all shards recover, status becomes `idle` automatically. Exhausted
|
|
138
|
+
restart attempts, disabled restart, or application errors require correction
|
|
139
|
+
and replacement/restart of the affected supervisor. No state-file cleanup is
|
|
140
|
+
required.
|
|
141
|
+
|
|
142
|
+
Every IDENTIFY still uses the central `shardId % max_concurrency` bucket and
|
|
143
|
+
5.5-second cooldown. Permits are charged before delivery and never refunded.
|
|
144
|
+
Fresh metadata cannot increase an unexpired in-memory allowance. RESUME remains
|
|
145
|
+
discord.js behavior; this feature does not bypass Discord rate limits or alter
|
|
146
|
+
retry policy. Crashes during startup can consume additional session starts and
|
|
147
|
+
leave the deployment waiting for a fresh allowance. Choose host
|
|
148
|
+
`identifyTimeoutMs` and coordinator `readyTimeoutMs` for your total, concurrency,
|
|
149
|
+
startup time and possible retries; a large serial deployment can exceed defaults.
|
|
150
|
+
|
|
151
|
+
Automatic totals must also fit `limits.maxPending` (default 256) so the control
|
|
152
|
+
plane has room for startup identifies. Welcome and assignment frames must fit
|
|
153
|
+
`limits.maxBytes`; these checks happen before draining. Increase these bounded
|
|
154
|
+
limits deliberately when sizing a larger deployment. Host `identifyTimeoutMs`
|
|
155
|
+
is capped at 300000 ms. If the serial login queue cannot fit that deadline with
|
|
156
|
+
the bot's Discord concurrency, this deployment model cannot start the whole
|
|
157
|
+
topology reliably; more machines do not increase Discord concurrency.
|
|
158
|
+
|
|
159
|
+
Closing the coordinator aborts metadata requests and transition waits, closes
|
|
160
|
+
connections, and cancels polling. Surviving children keep Discord open in
|
|
161
|
+
maintenance while their hosts reconnect and reconcile individual leases. A
|
|
162
|
+
changed total closes the old gateway before a new session opens, even for the
|
|
163
|
+
same ID. Rejected IDs become dormant. Explicit scaling transitions still confirm
|
|
164
|
+
child exits and release leases before committing the new topology; reservations
|
|
165
|
+
belonging to disconnected instances block that transition.
|
|
166
|
+
|
|
167
|
+
The [process lifetime limitations](sharding-recovery.md) still apply: run one
|
|
168
|
+
coordinator per bot and supervise host process trees. There is no global grace
|
|
169
|
+
period on coordinator restart. A partitioned old Discord session can overlap a
|
|
170
|
+
replacement until it reconnects or reaches the configured outage cutoff.
|
|
171
|
+
|
|
172
|
+
## Observability and compatibility
|
|
173
|
+
|
|
174
|
+
`coordinator.shardCount` exposes the current committed total.
|
|
175
|
+
`coordinator.scalingStatus` is a copied snapshot with `phase`
|
|
176
|
+
(`disabled`, `idle`, `draining`, `starting` or `degraded`), `shardCount`, and optional
|
|
177
|
+
`reason`/`targetShards`. `await coordinator.checkScaling()` requests one immediate
|
|
178
|
+
evaluation; simultaneous calls share one promise and still respect confirmation,
|
|
179
|
+
cooldown, readiness and allowance checks. Periodic checks continue automatically.
|
|
180
|
+
|
|
181
|
+
Structured logs emit `scaling_status`, `scaling_failed` and host `reassigned`
|
|
182
|
+
events. Ordinary waiting/stable reasons are logged only when status changes.
|
|
183
|
+
Use `RILO_LOG_FORMAT=json` for full context. Logs contain counts and host IDs,
|
|
184
|
+
not guild IDs, tokens or application payloads.
|
|
185
|
+
|
|
186
|
+
Upgrade coordinator, hosts and children together. The internal transport adds
|
|
187
|
+
drain, assignment and bounded metrics frames; mixed framework versions are not
|
|
188
|
+
supported. Metrics distinguish IPC boot from actual gateway readiness.
|
|
189
|
+
Public logger customization, fixed totals and existing message APIs remain
|
|
190
|
+
available. Initial host placements are preferences; per-process leases control
|
|
191
|
+
ownership. In automatic mode, callers must tolerate `RESHARDING` and total-count
|
|
192
|
+
changes during requests/broadcasts. No ownership database, directory or lock is
|
|
193
|
+
introduced.
|
|
194
|
+
|
|
195
|
+
See [the runnable example configuration](../examples/README.md#automatic-scaling).
|
|
196
|
+
Regression tests use synthetic gateway metadata and real local Bun processes;
|
|
197
|
+
they never log in to Discord.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Live shard statistics
|
|
2
|
+
|
|
3
|
+
Use `this.container.sharding.stats()` inside a command, listener or service to ask
|
|
4
|
+
every configured shard for its current stats. `client.sharding.stats()` and
|
|
5
|
+
`manager.stats()` provide the same API. No application handler registration is needed.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const results = await this.container.sharding.stats();
|
|
9
|
+
for (const [shard, result] of results) {
|
|
10
|
+
if (result.ok) {
|
|
11
|
+
this.container.logger.notif(
|
|
12
|
+
`Shard ${shard}: ${result.value.guilds} cached guilds`,
|
|
13
|
+
);
|
|
14
|
+
} else {
|
|
15
|
+
this.container.logger.alert(`Shard ${shard}: ${result.error.code}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// A specific shard still returns a map, with exactly one entry.
|
|
20
|
+
const one = await this.container.sharding.stats(0, { timeoutMs: 5000 });
|
|
21
|
+
const shardZero = one.get(0);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The return type is `Promise<ReadonlyMap<number, ShardStatsResult>>`. Import
|
|
25
|
+
`ShardStats`, `ShardStatsResult` and `ShardCallOptions` from `rilo` or
|
|
26
|
+
`rilo/sharding`. Entries follow the existing request result shape: `{ shard,
|
|
27
|
+
ok: true, value }` or `{ shard, ok: false, error }`. Errors are frozen `ShardError`
|
|
28
|
+
copies with a `code` and `message`; a failed entry has no `value` property.
|
|
29
|
+
|
|
30
|
+
## Freshness and scope
|
|
31
|
+
|
|
32
|
+
Every call makes new requests through the coordinator, including a request to
|
|
33
|
+
the caller's own shard. The destination reads its client and process state only
|
|
34
|
+
when that request arrives. Stats never use the coordinator's periodic scaling
|
|
35
|
+
metrics, an earlier successful result, or a local shortcut as a fallback.
|
|
36
|
+
The private request name `rilo:stats` is reserved and cannot be registered with
|
|
37
|
+
`handle()`.
|
|
38
|
+
|
|
39
|
+
Without an ID, the target set is `0..shardCount - 1` from the caller's reconciled
|
|
40
|
+
assignment when collection begins. It includes unavailable IDs. Passing an ID
|
|
41
|
+
selects only that shard; an invalid or out-of-range ID rejects with `TARGET`.
|
|
42
|
+
Dormant process slots are not shard IDs. A topology change during collection can
|
|
43
|
+
produce failures; call again after reconciliation to sample the new topology.
|
|
44
|
+
|
|
45
|
+
Samples are fresh observations, not a simultaneous cross-process snapshot. State
|
|
46
|
+
can change after a shard replies. `sampledAt` is Unix milliseconds on that shard's
|
|
47
|
+
clock, so differences between hosts can include clock skew. Successful entries
|
|
48
|
+
remain useful even when other shards fail; totals computed from them are partial.
|
|
49
|
+
|
|
50
|
+
The returned map has the read methods and iteration behavior of `ReadonlyMap`,
|
|
51
|
+
with no `set`, `delete` or `clear` methods. The map, entries, samples, nested memory
|
|
52
|
+
objects and errors are frozen. They are detached from live internals and will
|
|
53
|
+
not change after you receive them. The read-only map is a view over private
|
|
54
|
+
storage, not a mutable JavaScript `Map` instance. It is iterable; use
|
|
55
|
+
`[...results]` when you need an array. JSON serialization of errors should copy
|
|
56
|
+
their `code` and `message` explicitly.
|
|
57
|
+
|
|
58
|
+
## Deadlines and failures
|
|
59
|
+
|
|
60
|
+
The optional second argument accepts the usual `ShardCallOptions`:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const results = await client.sharding.stats(undefined, {
|
|
64
|
+
timeoutMs: 5000,
|
|
65
|
+
waitForCoordinator: true,
|
|
66
|
+
signal: controller.signal,
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
One deadline covers coordinator waiting and the whole collection, including
|
|
71
|
+
queued targets. It defaults to the coordinator's `limits.timeoutMs` (15 seconds),
|
|
72
|
+
and accepts 1..300000 milliseconds. At most eight requests are in flight for one
|
|
73
|
+
collection, also subject to the existing client, host and coordinator pending
|
|
74
|
+
limits. A target still queued when the budget runs out receives `TIMEOUT`
|
|
75
|
+
without being dispatched. Late replies are ignored and never update a completed
|
|
76
|
+
map. A reply received after the budget expires is a timeout, even if it has metrics.
|
|
77
|
+
|
|
78
|
+
Before collection starts, invalid options or a locally unavailable coordinator
|
|
79
|
+
reject the promise. By default, maintenance and unmanaged clients reject with
|
|
80
|
+
`DISCONNECTED`; a closed client or host rejects with `CLOSED`. Opting into
|
|
81
|
+
`waitForCoordinator` waits within the same deadline and can reject with `TIMEOUT`,
|
|
82
|
+
`CAPACITY` or the supplied abort signal's reason. No local or cached stats are
|
|
83
|
+
returned while waiting. A connected host can still have children reconciling;
|
|
84
|
+
those destinations remain unavailable until their registration completes.
|
|
85
|
+
|
|
86
|
+
Once collection starts, failures become individual map entries:
|
|
87
|
+
|
|
88
|
+
| Code | What happened |
|
|
89
|
+
| -------------- | ------------------------------------------------------------------------ |
|
|
90
|
+
| `UNAVAILABLE` | The shard has no ready route, has exited, or has no stats provider. |
|
|
91
|
+
| `TIMEOUT` | The overall budget ran out before a usable reply arrived. |
|
|
92
|
+
| `DISCONNECTED` | A required coordinator or host connection was lost. |
|
|
93
|
+
| `CAPACITY` | A client, host, coordinator or destination handler limit was reached. |
|
|
94
|
+
| `RESHARDING` | The deployment is draining for a topology change. |
|
|
95
|
+
| `ABORTED` | The caller cancelled collection. Already completed entries are retained. |
|
|
96
|
+
| `PROTOCOL` | A responding shard returned a malformed stats sample. |
|
|
97
|
+
| `HANDLER` | The destination's sampler threw an error. |
|
|
98
|
+
|
|
99
|
+
Other transport error codes can also appear. Failed entries contain no metrics:
|
|
100
|
+
an unavailable shard does not mean zero guilds, zero memory or a disconnected
|
|
101
|
+
Discord session. Calls already sent are never replayed after reconnection.
|
|
102
|
+
Collection does not change leases, maintenance, gateway sessions or dormant
|
|
103
|
+
process behavior. See [maintenance and recovery](sharding-recovery.md).
|
|
104
|
+
|
|
105
|
+
## Sample fields
|
|
106
|
+
|
|
107
|
+
| Field | Meaning |
|
|
108
|
+
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `sampledAt` | Shard wall-clock sampling time, in Unix milliseconds. |
|
|
110
|
+
| `ready` | Whether the shard's Discord client is ready right now. |
|
|
111
|
+
| `guilds`, `users`, `channels` | Current local cache sizes. Users may appear on multiple shards. |
|
|
112
|
+
| `pingMs` | Most recent gateway heartbeat latency, or `null` before a measurement exists. This doesn't send a new heartbeat. |
|
|
113
|
+
| `uptimeMs` | Milliseconds since the client last became ready, or `null` while it isn't ready. |
|
|
114
|
+
| `pid` | Process ID on that shard's host. |
|
|
115
|
+
| `processUptimeMs` | Milliseconds since the Bun process started. |
|
|
116
|
+
| `memory` | Process memory usage in bytes: `rss`, `heapTotal`, `heapUsed`, `external`, `arrayBuffers`. These values overlap. |
|
|
117
|
+
|
|
118
|
+
Cache counts depend on intents, cache configuration and received events; they
|
|
119
|
+
aren't a fresh Discord REST census. A process can answer stats while its Discord
|
|
120
|
+
client is not ready. That is a successful sample with `ready: false`, distinct
|
|
121
|
+
from a shard that cannot answer at all.
|