meocord 4.1.2 → 4.2.1

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.
Files changed (140) hide show
  1. package/CHANGELOG.md +362 -0
  2. package/THIRD_PARTY_NOTICES.md +14 -13
  3. package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-XKMlcUZz.cjs} +669 -185
  4. package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-BZgJyfZS.cjs} +218 -75
  5. package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-CsJJyCZd.cjs} +10 -7
  6. package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CgfPQrNl.cjs} +374 -132
  7. package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-BUAR1WYG.cjs} +21 -7
  8. package/dist/cjs/common/index.cjs +4 -4
  9. package/dist/cjs/core/index.cjs +365 -310
  10. package/dist/cjs/decorator/index.cjs +85 -61
  11. package/dist/cjs/testing/index.cjs +2236 -496
  12. package/dist/cli.json +1 -1
  13. package/dist/esm/bin/app-template/README.md.template +3 -1
  14. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
  15. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
  16. package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
  17. package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
  18. package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
  19. package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
  20. package/dist/esm/bin/generator.js +4 -5
  21. package/dist/esm/bin/helper/filter-generator.helper.js +4 -5
  22. package/dist/esm/bin/helper/guard-generator.helper.js +4 -5
  23. package/dist/esm/bin/helper/interceptor-generator.helper.js +4 -5
  24. package/dist/esm/bin/helper/observer-generator.helper.js +4 -5
  25. package/dist/esm/bin/helper/pipe-generator.helper.js +4 -5
  26. package/dist/esm/bin/helper/service-generator.helper.js +4 -5
  27. package/dist/esm/bin/meocord.js +84 -25
  28. package/dist/esm/build/load-config.pre-entry.js +17 -4
  29. package/dist/esm/build/rsbuild-config.js +16 -12
  30. package/dist/esm/build/typed-externals.js +36 -0
  31. package/dist/esm/common/cooldown-store.js +10 -2
  32. package/dist/esm/common/decorator.js +6 -6
  33. package/dist/esm/common/deprecation.js +2 -2
  34. package/dist/esm/common/errors.js +27 -2
  35. package/dist/esm/common/execution-context.js +1 -1
  36. package/dist/esm/common/meocord-text.js +11 -8
  37. package/dist/esm/common/metadata.js +1 -1
  38. package/dist/esm/common/response/call-order.js +9 -4
  39. package/dist/esm/common/response/presenter.js +33 -6
  40. package/dist/esm/common/response/response-state.js +84 -10
  41. package/dist/esm/common/route.js +21 -5
  42. package/dist/esm/common/sharded-cooldown-store.js +1 -1
  43. package/dist/esm/common/translate-error.js +1 -1
  44. package/dist/esm/common/translator.js +37 -6
  45. package/dist/esm/core/command-conflicts.js +34 -34
  46. package/dist/esm/core/command-registration.js +93 -24
  47. package/dist/esm/core/command-routes.js +2 -2
  48. package/dist/esm/core/component-routes.js +74 -31
  49. package/dist/esm/core/cooldown-runner.js +29 -8
  50. package/dist/esm/core/dispatcher.js +51 -7
  51. package/dist/esm/core/fallback.js +20 -11
  52. package/dist/esm/core/filter-runner.js +2 -2
  53. package/dist/esm/core/guard-runner.js +16 -3
  54. package/dist/esm/core/handler-input.js +4 -3
  55. package/dist/esm/core/handler-pipeline.js +27 -14
  56. package/dist/esm/core/handler-registry.js +1 -1
  57. package/dist/esm/core/interceptor-runner.js +2 -2
  58. package/dist/esm/core/lifecycle-hooks.js +134 -47
  59. package/dist/esm/core/meocord-factory.js +241 -179
  60. package/dist/esm/core/meocord.app.js +116 -75
  61. package/dist/esm/core/message-routes.js +4 -3
  62. package/dist/esm/core/observer-runner.js +4 -3
  63. package/dist/esm/core/providers.js +45 -23
  64. package/dist/esm/core/route-specificity.js +4 -4
  65. package/dist/esm/core/running-calls.js +43 -0
  66. package/dist/esm/core/scalar-types.js +15 -3
  67. package/dist/esm/core/shard-context.js +6 -7
  68. package/dist/esm/core/shard-manager.js +16 -10
  69. package/dist/esm/core/startup-roots.js +23 -4
  70. package/dist/esm/core/theme-resolvers.js +4 -1
  71. package/dist/esm/core/theme-runtime.js +2 -2
  72. package/dist/esm/core/theme-scope.js +3 -1
  73. package/dist/esm/core/theme-validation.js +17 -10
  74. package/dist/esm/decorator/app.decorator.js +12 -7
  75. package/dist/esm/decorator/command-builder.decorator.js +4 -4
  76. package/dist/esm/decorator/controller-class.decorator.js +9 -4
  77. package/dist/esm/decorator/controller.decorator.js +162 -83
  78. package/dist/esm/decorator/cooldown.decorator.js +20 -3
  79. package/dist/esm/decorator/defer.decorator.js +4 -4
  80. package/dist/esm/decorator/event.decorator.js +5 -5
  81. package/dist/esm/decorator/filter.decorator.js +7 -7
  82. package/dist/esm/decorator/guard.decorator.js +11 -10
  83. package/dist/esm/decorator/inject.decorator.js +4 -4
  84. package/dist/esm/decorator/interceptor.decorator.js +7 -7
  85. package/dist/esm/decorator/observer.decorator.js +4 -4
  86. package/dist/esm/decorator/service.decorator.js +4 -4
  87. package/dist/esm/decorator/theme.decorator.js +3 -3
  88. package/dist/esm/decorator/validation.decorator.js +10 -10
  89. package/dist/esm/package.json.js +1 -1
  90. package/dist/esm/testing/catalog.js +27 -11
  91. package/dist/esm/testing/cooldown-store-suite.js +85 -15
  92. package/dist/esm/testing/discord-defaults.js +249 -21
  93. package/dist/esm/testing/index.js +4 -2
  94. package/dist/esm/testing/inspect-handler.js +20 -3
  95. package/dist/esm/testing/meocord-testing-module.js +352 -205
  96. package/dist/esm/testing/mock-fn.js +173 -22
  97. package/dist/esm/testing/mock-interaction.js +1214 -209
  98. package/dist/esm/testing/response.js +1 -1
  99. package/dist/esm/testing/routing.js +39 -14
  100. package/dist/esm/testing/snowflake.js +9 -2
  101. package/dist/esm/testing/startup-errors.js +26 -0
  102. package/dist/esm/testing/strict-mocks.js +65 -0
  103. package/dist/esm/testing/theme.js +1 -1
  104. package/dist/esm/util/bundle-entry.util.js +2 -1
  105. package/dist/esm/util/common.util.js +47 -19
  106. package/dist/esm/util/dev-runner.util.js +8 -8
  107. package/dist/esm/util/generator-cli.util.js +4 -5
  108. package/dist/esm/util/interaction.util.js +3 -3
  109. package/dist/esm/util/meocord-config-loader.util.js +10 -2
  110. package/dist/esm/util/meocord-config-validation.util.js +4 -5
  111. package/dist/esm/util/meocord-source-config.util.js +65 -20
  112. package/dist/esm/util/metadata-keys.js +1 -0
  113. package/dist/esm/util/method-wrapper.util.js +16 -0
  114. package/dist/esm/util/platform.util.js +10 -10
  115. package/dist/esm/util/refusal.util.js +148 -8
  116. package/dist/esm/util/shard-process.util.js +4 -1
  117. package/dist/esm/util/sharding-mode.util.js +5 -7
  118. package/dist/esm/util/shared-state.util.js +21 -0
  119. package/dist/esm/util/tsconfig-paths.util.js +29 -0
  120. package/dist/esm/util/tsconfig.util.js +10 -10
  121. package/dist/esm/util/value.util.js +12 -1
  122. package/dist/types/common/index.d.cts +8 -8
  123. package/dist/types/common/index.d.ts +8 -8
  124. package/dist/types/core/index.d.cts +10 -7
  125. package/dist/types/core/index.d.ts +10 -7
  126. package/dist/types/decorator/index.d.cts +31 -29
  127. package/dist/types/decorator/index.d.ts +31 -29
  128. package/dist/types/{index-D7n0FBMm.d.ts → index-DKJclIi-.d.ts} +107 -36
  129. package/dist/types/{index-DEP0NYrU.d.cts → index-Dlf0ZZtS.d.cts} +107 -36
  130. package/dist/types/interface/index.d.cts +1 -1
  131. package/dist/types/interface/index.d.ts +1 -1
  132. package/dist/types/{route-C-aMI7Sl.d.cts → route-Bh1U13yy.d.cts} +8 -4
  133. package/dist/types/{route-C-aMI7Sl.d.ts → route-Bh1U13yy.d.ts} +8 -4
  134. package/dist/types/testing/index.d.cts +406 -70
  135. package/dist/types/testing/index.d.ts +406 -70
  136. package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-CyEz9pnv.d.cts} +1 -1
  137. package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-DA204O-q.d.ts} +1 -1
  138. package/dist/types/{translator-J6tIUfdp.d.ts → translator-Bg7hwxDF.d.cts} +21 -8
  139. package/dist/types/{translator-B4mc1lzJ.d.cts → translator-CeP4bCzp.d.ts} +21 -8
  140. package/package.json +4 -3
@@ -1,4 +1,4 @@
1
- import { BaseInteraction, Message, APIEmbed, InteractionReplyOptions, BitFieldResolvable, MessageFlags, InteractionEditReplyOptions, JSONEncodable, APIModalInteractionResponseCallbackData, ModalComponentData, Interaction, ColorResolvable, ButtonStyle, MessageReaction, PartialMessageReaction, ContextMenuCommandType, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, ApplicationCommandType, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder, ContextMenuCommandBuilder, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody, ChatInputCommandInteraction, PrimaryEntryPointCommandInteraction, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, ModalSubmitInteraction, APIComponentInContainer, AttachmentBuilder, MessageReplyOptions, GuildMember, User, Role, GuildBasedChannel, PartialUser, Client } from 'discord.js';
1
+ import { BaseInteraction, Message, APIMessageTopLevelComponent, APIEmbed, InteractionReplyOptions, BitFieldResolvable, MessageFlags, InteractionEditReplyOptions, JSONEncodable, APIModalInteractionResponseCallbackData, ModalComponentData, Interaction, ColorResolvable, ButtonStyle, MessageReaction, PartialMessageReaction, ContextMenuCommandType, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, ApplicationCommandType, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder, ContextMenuCommandBuilder, RESTPostAPIPrimaryEntryPointApplicationCommandJSONBody, ChatInputCommandInteraction, PrimaryEntryPointCommandInteraction, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, ModalSubmitInteraction, APIComponentInContainer, AttachmentBuilder, MessageReplyOptions, GuildMember, User, Role, GuildBasedChannel, PartialUser, Client } from 'discord.js';
2
2
  import { RsbuildConfig as RsbuildConfig$1 } from '@rsbuild/core';
3
3
  import { C as CommandType, R as ReactionHandlerAction } from './controller.enum-DTDn69N9.js';
4
4
 
@@ -48,7 +48,7 @@ interface MetadataDecorator<T> {
48
48
  *
49
49
  * @group Utilities
50
50
  * @see {@link ExecutionContext}
51
- * @see {@link https://meocord.dev/docs/4.1/guards | Guards}
51
+ * @see {@link https://meocord.dev/docs/4.2/guards | Guards}
52
52
  */
53
53
  declare function createMetadata<T>(description?: string): MetadataDecorator<T>;
54
54
 
@@ -329,13 +329,14 @@ interface ResponseState {
329
329
  /** Where the answer stands, re-read from the interaction so answers made around this state count. */
330
330
  readonly state: ResponsePhase;
331
331
  /**
332
- * The message this state last replied with, updated or edited, never a follow-up, or the message a component is on.
333
- * Read-only: edit through the state.
332
+ * The answer's message: the one this state last replied with, updated or edited, or the message a component is on. A
333
+ * follow-up is it only once it is the answer, after replacing a public deferral or after `delete()`; after `delete()`
334
+ * it is `undefined` until something is sent. Read-only: edit through the state.
334
335
  */
335
336
  readonly message: Message | undefined;
336
337
  /** The components and embeds of the message before `@Defer` locked it; `undefined` until then. */
337
338
  readonly original: {
338
- readonly components: readonly unknown[];
339
+ readonly components: readonly APIMessageTopLevelComponent[];
339
340
  readonly embeds: readonly APIEmbed[];
340
341
  } | undefined;
341
342
  /**
@@ -366,6 +367,9 @@ interface ResponseState {
366
367
  * A reply Discord refuses as already acknowledged elsewhere still throws, and the next `send()` edits.
367
368
  * After `modal()` it throws: a modal has no message, and the modal's submit is answered instead.
368
369
  *
370
+ * After a private `followUp()` replaced a public deferral, it edits that follow-up. After `delete()` it sends a
371
+ * follow-up, private when the deleted answer was unless `flags` say otherwise, and that follow-up is the answer.
372
+ *
369
373
  * After `@Defer` locked a message, omitting `components` puts back its components as they were
370
374
  * before the lock, and omitting `embeds` drops the loading view; `components: []` clears them.
371
375
  *
@@ -375,7 +379,8 @@ interface ResponseState {
375
379
  */
376
380
  send(payload: ResponsePayload, options?: ResponseSendOptions): Promise<Message | undefined>;
377
381
  /**
378
- * Edits the answer, routed as `send()` is: an edit once the interaction is answered.
382
+ * Edits the answer, routed as `send()` is: an edit once the interaction is answered, of the follow-up that replaced
383
+ * a public deferral, or, after `delete()`, a follow-up that becomes the answer.
379
384
  *
380
385
  * @param payload - Text, or edit options.
381
386
  * @param options - `fill: false` leaves this edit's embeds and containers uncoloured.
@@ -386,8 +391,9 @@ interface ResponseState {
386
391
  * Sends another message after the answer. Before any answer it is the first reply. While a command's
387
392
  * reply is deferred and nothing is sent yet, Discord turns a follow-up into the deferred reply and
388
393
  * ignores its flags, so it is sent as that edit; a private follow-up on a public deferral deletes the
389
- * deferral first and is sent privately. After `modal()` it is sent as made, and Discord decides whether it
390
- * accepts a follow-up there; the modal's submit is the interaction to answer.
394
+ * deferral first, is sent privately, and is the answer `send()`, `edit()` and `delete()` act on. After `delete()`,
395
+ * it is the answer too. After `modal()` it is sent as made, and Discord decides whether it accepts a follow-up
396
+ * there; the modal's submit is the interaction to answer.
391
397
  *
392
398
  * @param payload - Text, or reply options; `Ephemeral` makes the follow-up private.
393
399
  * @param options - `fill: false` leaves this follow-up's embeds and containers uncoloured.
@@ -395,8 +401,9 @@ interface ResponseState {
395
401
  */
396
402
  followUp(payload: ResponsePayload, options?: ResponseSendOptions): Promise<Message | undefined>;
397
403
  /**
398
- * Deletes the answer: the reply, or for a component deferred without a reply of its own, its message.
399
- * Throws before any answer, and after `modal()`, which leaves no message.
404
+ * Deletes the answer: the reply, the follow-up that replaced a public deferral, or for a component deferred
405
+ * without a reply of its own, its message. `message` is then `undefined` until something is sent. Throws before any
406
+ * answer, after `modal()`, which leaves no message, and again with nothing sent since the last `delete()`.
400
407
  */
401
408
  delete(): Promise<void>;
402
409
  /**
@@ -781,7 +788,7 @@ type ExecutionContextType = 'interaction' | 'autocomplete' | 'message' | 'reacti
781
788
  * @group Utilities
782
789
  * @see {@link createMetadata}
783
790
  * @see {@link StageParams}
784
- * @see {@link https://meocord.dev/docs/4.1/guards | Guards}
791
+ * @see {@link https://meocord.dev/docs/4.2/guards | Guards}
785
792
  */
786
793
  declare abstract class ExecutionContext {
787
794
  /**
@@ -1243,7 +1250,10 @@ declare class MemoryCooldownStore extends CooldownStore {
1243
1250
  peekMany(entries: readonly CooldownEntry[]): Promise<CooldownBatchVerdict>;
1244
1251
  /** Each entry's call times, trimmed to its window, and whether it allows one more call now. */
1245
1252
  private check;
1246
- /** Drops the keys whose calls have all left their window, once a minute from the store's first call. */
1253
+ /**
1254
+ * Drops the keys whose calls have all left their window, once a minute from a call, until a sweep leaves none: an
1255
+ * idle store then holds no timer, so one nothing references is collected, and its next call starts the sweep again.
1256
+ */
1247
1257
  private startSweeping;
1248
1258
  }
1249
1259
 
@@ -1348,7 +1358,7 @@ declare class UserError extends Error {
1348
1358
  * @group Responses
1349
1359
  * @category Errors
1350
1360
  * @see {@link Catch}
1351
- * @see {@link https://meocord.dev/docs/4.1/exception-filters | Exception filters}
1361
+ * @see {@link https://meocord.dev/docs/4.2/exception-filters | Exception filters}
1352
1362
  */
1353
1363
  declare class CommandNotFoundError extends Error {
1354
1364
  /** @param message - What the error says, `No handler matched the interaction.` unless given. */
@@ -1439,7 +1449,7 @@ interface MessageUsageIssue {
1439
1449
  * @category Errors
1440
1450
  * @see {@link MessageHandler}
1441
1451
  * @see {@link translateError}
1442
- * @see {@link https://meocord.dev/docs/4.1/message-commands | Message commands}
1452
+ * @see {@link https://meocord.dev/docs/4.2/message-commands | Message commands}
1443
1453
  */
1444
1454
  declare class MessageUsageError extends Error {
1445
1455
  readonly usage: string;
@@ -1956,8 +1966,10 @@ interface CommandMeta<T extends string = string> {
1956
1966
  /** The params a component's customId pattern captures, in the order they appear. */
1957
1967
  dynamicParams?: T[];
1958
1968
  /**
1959
- * How specific this pattern is; the highest wins when several routes match one customId, so
1960
- * `gi-profile/summary/{ownerId}/{uid}` beats `gi-profile/{uuid}/{uid}` whatever the declaration order.
1969
+ * How specific this pattern is, as 4.1 ranked patterns: more literal text first.
1970
+ *
1971
+ * @deprecated Since 4.2, and removed in the next major version (5.0). Internal: nothing replaces it. Component routing
1972
+ * ranks patterns segment by segment and doesn't read it.
1961
1973
  */
1962
1974
  specificity?: number;
1963
1975
  /** The builder's own `guilds` option, when it has one. */
@@ -2103,10 +2115,14 @@ interface MeoCordApplication {
2103
2115
  */
2104
2116
  start(): Promise<void>;
2105
2117
  /**
2106
- * Stops the bot: runs the `onShutdown` hooks under the configured `shutdownTimeout` and closes the client, or with
2107
- * process sharding, asks every shard to shut down and waits for it. It ends no process of its own accord, except that
2108
- * called in a shard, it asks the manager to stop every shard, which then shuts that shard's process down too. A stop
2109
- * while the bot starts ends that start, a call after the first waits for it, and a stopped app does not start again.
2118
+ * Stops the bot: stops taking events, waits for the calls under way, runs the `onShutdown` hooks under the configured
2119
+ * `shutdownTimeout` and closes the client, or with process sharding, asks every shard to shut down and waits for it.
2120
+ * It ends no process of its own accord, except that called in a shard, it asks the manager to stop every shard, which
2121
+ * then shuts that shard's process down too. A call that awaits `stop()`, as an owner-only shutdown command does, is
2122
+ * not waited for.
2123
+ *
2124
+ * A stop while the bot starts ends that start: it waits for the providers being made, at most `shutdownTimeout`, and
2125
+ * nothing more is made after it. A call after the first waits for it, and a stopped app does not start again.
2110
2126
  *
2111
2127
  * @returns A promise that resolves once the bot is stopped. It never rejects: a failure to close is logged, and sets
2112
2128
  * `process.exitCode` to 1 unless another code is already set.
@@ -2164,7 +2180,7 @@ interface ReadyInfo {
2164
2180
  *
2165
2181
  * @group Types
2166
2182
  * @see {@link OnShutdown}
2167
- * @see {@link https://meocord.dev/docs/4.1/lifecycle-hooks | Lifecycle hooks}
2183
+ * @see {@link https://meocord.dev/docs/4.2/lifecycle-hooks | Lifecycle hooks}
2168
2184
  */
2169
2185
  interface OnReady {
2170
2186
  /**
@@ -2186,9 +2202,11 @@ interface OnReady {
2186
2202
  * it injects. The whole sequence is limited by `shutdownTimeout` in `meocord.config.ts`; shutdown then goes on
2187
2203
  * whether or not it finished. A hook that throws is logged and the next one still runs.
2188
2204
  *
2189
- * It runs only for a class whose `onReady` has finished, or that has none: a signal that arrives
2190
- * while the ready hooks are running skips the class still starting and those not reached yet, and no
2191
- * further `onReady` starts. An `onReady` that threw counts as finished, so its partial setup is cleaned up.
2205
+ * It runs only for a class whose `onReady` has finished, or that has none. A stop while the ready hooks
2206
+ * are running waits for the `onReady` in progress, within `shutdownTimeout`, then shuts that class down too;
2207
+ * those not reached yet are skipped, and no further `onReady` starts. An `onReady` still running once only the
2208
+ * hooks' share of the timeout is left is named in a warning and skipped. An `onReady` that threw counts as
2209
+ * finished, so its partial setup is cleaned up.
2192
2210
  *
2193
2211
  * @example
2194
2212
  * ```ts
@@ -2211,7 +2229,7 @@ interface OnReady {
2211
2229
  *
2212
2230
  * @group Types
2213
2231
  * @see {@link OnReady}
2214
- * @see {@link https://meocord.dev/docs/4.1/lifecycle-hooks | Lifecycle hooks}
2232
+ * @see {@link https://meocord.dev/docs/4.2/lifecycle-hooks | Lifecycle hooks}
2215
2233
  */
2216
2234
  interface OnShutdown {
2217
2235
  /** Runs before the client is destroyed. */
@@ -2353,7 +2371,11 @@ interface MessageResponseContext {
2353
2371
  * @category Presenters
2354
2372
  */
2355
2373
  interface PresentedError {
2356
- /** What the user is told. */
2374
+ /**
2375
+ * What the user is told, fitted to Discord's limit for the answer's `mode`: at most 4096 characters for an embed and
2376
+ * 4000 for a Components V2 Text Display, cut to end in `…`. An empty or whitespace-only message comes as MeoCord's generic error text.
2377
+ * So a presenter that writes it as its view's text renders, whatever the error's message.
2378
+ */
2357
2379
  message: string;
2358
2380
  /** The error being answered, so a presenter can style it by kind. */
2359
2381
  error: unknown;
@@ -2546,6 +2568,15 @@ interface ControllerOptions {
2546
2568
  * @defaultValue `true`
2547
2569
  */
2548
2570
  inheritStages?: boolean;
2571
+ /**
2572
+ * What a handler this class re-decorates answers of the routes its base classes declare for it. `'keep'` answers
2573
+ * them as well as its own; `'replace'` answers only the routes this class declares for it, of every kind: commands,
2574
+ * component patterns, message patterns, reactions and autocompletes. A command no handler answers any more is not
2575
+ * registered. A method overridden without decorators keeps every route it inherits either way.
2576
+ *
2577
+ * @defaultValue `'keep'`; the next major version (5.0) replaces them by default
2578
+ */
2579
+ inheritedRoutes?: 'keep' | 'replace';
2549
2580
  }
2550
2581
  /**
2551
2582
  * One prefix or several that start a message command, such as `'!'` or `['!', '?']`; `''` stands for none.
@@ -2583,7 +2614,7 @@ type MessagePrefix = string | readonly string[];
2583
2614
  * @group Configuration
2584
2615
  * @category App options
2585
2616
  * @see {@link MessageHandler}
2586
- * @see {@link https://meocord.dev/docs/4.1/message-commands | Message commands}
2617
+ * @see {@link https://meocord.dev/docs/4.2/message-commands | Message commands}
2587
2618
  */
2588
2619
  interface MessageCommandOptions {
2589
2620
  /**
@@ -2656,6 +2687,28 @@ interface MessageCommandOptions {
2656
2687
  * @defaultValue `false`
2657
2688
  */
2658
2689
  help?: boolean | MessageHelpOptions;
2690
+ /**
2691
+ * How a message's handlers run: the patterned handler it matched, then every `@MessageHandler()` listener.
2692
+ * `'sequential'` runs each once the one before it has settled, so a slow command delays the listeners after it.
2693
+ * `'concurrent'` starts them together and settles once all have: each keeps its own guards, interceptors, filters
2694
+ * and observers, and no order holds between them, so a listener can run before the command has written what it
2695
+ * reads. The built-in help and a command's usage are answered first either way.
2696
+ * @defaultValue `'sequential'`; the next major version (5.0) may run them concurrently by default
2697
+ *
2698
+ * @example
2699
+ * ```ts
2700
+ * @MeoCord({ controllers: [Commands, Moderation], messages: { prefix: '!', handlers: 'concurrent' } })
2701
+ * class App {}
2702
+ * ```
2703
+ */
2704
+ handlers?: 'sequential' | 'concurrent';
2705
+ /**
2706
+ * Warns, once per handler, when a message's handler takes 5 seconds or more with listeners waiting after it, under
2707
+ * `handlers: 'sequential'`, naming the handler and how many it held back. A testing module warns only when this is
2708
+ * `true`, since a test's fake clock can pass 5 seconds inside a handler.
2709
+ * @defaultValue `true` in a running bot, `false` in a `MeoCordTestingModule`
2710
+ */
2711
+ slowHandlerWarning?: boolean;
2659
2712
  }
2660
2713
  /**
2661
2714
  * The words the built-in help command answers to, as `@MeoCord({ messages: { help } })` takes them.
@@ -2775,7 +2828,7 @@ interface MessageHelpParam {
2775
2828
  * ```
2776
2829
  *
2777
2830
  * @group Types
2778
- * @see {@link https://meocord.dev/docs/4.1/localisation | Localisation}
2831
+ * @see {@link https://meocord.dev/docs/4.2/localisation | Localisation}
2779
2832
  */
2780
2833
  type MeoCordMessages = (typeof MEOCORD_MESSAGES)['meocord'];
2781
2834
  /**
@@ -2806,7 +2859,7 @@ type MeoCordMessages = (typeof MEOCORD_MESSAGES)['meocord'];
2806
2859
  *
2807
2860
  * @group Configuration
2808
2861
  * @category App options
2809
- * @see {@link https://meocord.dev/docs/4.1/message-params | Message params}
2862
+ * @see {@link https://meocord.dev/docs/4.2/message-params | Message params}
2810
2863
  */
2811
2864
  interface MessageParamType<T = unknown> {
2812
2865
  /** A noun such as `hex colour`, read in "is not a valid hex colour". Defaults to the type's key. */
@@ -3019,7 +3072,7 @@ type MessageScope = 'guild' | 'dm' | 'any';
3019
3072
  * @group Configuration
3020
3073
  * @category App options
3021
3074
  * @see {@link MessageHandler}
3022
- * @see {@link https://meocord.dev/docs/4.1/message-commands | Message commands}
3075
+ * @see {@link https://meocord.dev/docs/4.2/message-commands | Message commands}
3023
3076
  */
3024
3077
  interface MessageHandlerOptions {
3025
3078
  /**
@@ -3077,7 +3130,7 @@ interface MessageHandlerOptions {
3077
3130
  * @group Configuration
3078
3131
  * @category Config file
3079
3132
  * @see {@link MeoCord}
3080
- * @see {@link https://meocord.dev/docs/4.1/configuration | Configuration}
3133
+ * @see {@link https://meocord.dev/docs/4.2/configuration | Configuration}
3081
3134
  */
3082
3135
  interface MeoCordConfig {
3083
3136
  /** Shown as a prefix on every log line. Omitted when unset. */
@@ -3139,9 +3192,27 @@ interface MeoCordConfig {
3139
3192
  */
3140
3193
  sourceMappedStacks?: boolean;
3141
3194
  /**
3142
- * How long, in milliseconds, shutdown waits for the `onShutdown` hooks before destroying the client
3143
- * anyway: from 0 to 2147478647, the longest a timer keeps less the margin the shard manager waits on top. The limit covers the whole sequence, not each hook,
3144
- * including the calls under way that the cooldown store's shutdown waits for.
3195
+ * How a startup error a decorator finds, such as an invalid customId pattern, is reported.
3196
+ *
3197
+ * With `'first'`, a decorator throws its error as its class is defined, when its file is imported, so a bot with
3198
+ * three mistakes shows one per run. With `'all'`, decorators keep their errors, and `MeoCordFactory.create()` logs
3199
+ * every one, each with its handler and file, alongside the errors its own checks find, then throws the first. The
3200
+ * built bot reads it before your modules load. In a test, call `reportAllStartupErrors()` from `meocord/testing` in
3201
+ * the setup file instead.
3202
+ *
3203
+ * Whichever you choose, `create()` reports every error its own checks find, such as two handlers of one command.
3204
+ *
3205
+ * @defaultValue `'first'`; `'all'` in the next major version (5.0)
3206
+ */
3207
+ startupErrors?: 'first' | 'all';
3208
+ /**
3209
+ * How long, in milliseconds, shutdown waits for the calls under way and the `onShutdown` hooks before destroying the
3210
+ * client anyway: from 0 to 2147478647, the longest a timer keeps less the margin the shard manager waits on top. The
3211
+ * limit covers the whole sequence, not each hook.
3212
+ *
3213
+ * The calls are waited for first, but no longer than the timeout less a reserve kept for the hooks: a quarter of it,
3214
+ * at least 1 second and never more than half. A call still running then is named in a warning, and the hooks run in
3215
+ * the time left.
3145
3216
  *
3146
3217
  * @defaultValue `10_000`
3147
3218
  */
@@ -3196,7 +3267,7 @@ interface MeoCordConfig {
3196
3267
  * @group Configuration
3197
3268
  * @category Config file
3198
3269
  * @see {@link ShardContext}
3199
- * @see {@link https://meocord.dev/docs/4.1/sharding | Sharding}
3270
+ * @see {@link https://meocord.dev/docs/4.2/sharding | Sharding}
3200
3271
  */
3201
3272
  interface ShardingConfig {
3202
3273
  /**
@@ -3239,7 +3310,7 @@ interface ShardingConfig {
3239
3310
  *
3240
3311
  * @group Configuration
3241
3312
  * @category Config file
3242
- * @see {@link https://meocord.dev/docs/4.1/slash-commands | Slash commands}
3313
+ * @see {@link https://meocord.dev/docs/4.2/slash-commands | Slash commands}
3243
3314
  */
3244
3315
  interface CommandRegistrationConfig {
3245
3316
  /**