meocord 4.2.1 → 4.2.3

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 (137) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/cjs/_shared/{controller.decorator-XKMlcUZz.cjs → controller.decorator-hp5xdJU4.cjs} +550 -189
  3. package/dist/cjs/_shared/{startup-roots-CgfPQrNl.cjs → event-sources-DzqTODXg.cjs} +274 -88
  4. package/dist/cjs/_shared/{providers-BZgJyfZS.cjs → providers-BBvbnmLP.cjs} +837 -295
  5. package/dist/cjs/_shared/{sharded-cooldown-store-CsJJyCZd.cjs → sharded-cooldown-store-B62g5O88.cjs} +11 -9
  6. package/dist/cjs/_shared/{translate-error-BUAR1WYG.cjs → translate-error-wsgfsUET.cjs} +1 -1
  7. package/dist/cjs/common/index.cjs +4 -104
  8. package/dist/cjs/core/index.cjs +435 -182
  9. package/dist/cjs/decorator/index.cjs +116 -121
  10. package/dist/cjs/testing/index.cjs +1923 -808
  11. package/dist/cli.json +1 -1
  12. package/dist/esm/bin/app-template/package.json.template +9 -9
  13. package/dist/esm/bin/app-template/src/main.ts.template +3 -2
  14. package/dist/esm/bin/generator.js +6 -4
  15. package/dist/esm/bin/helper/filter-generator.helper.js +6 -4
  16. package/dist/esm/bin/helper/guard-generator.helper.js +6 -4
  17. package/dist/esm/bin/helper/interceptor-generator.helper.js +6 -4
  18. package/dist/esm/bin/helper/observer-generator.helper.js +6 -4
  19. package/dist/esm/bin/helper/pipe-generator.helper.js +6 -4
  20. package/dist/esm/bin/helper/service-generator.helper.js +6 -4
  21. package/dist/esm/bin/meocord.js +106 -48
  22. package/dist/esm/build/native-addons.js +75 -19
  23. package/dist/esm/build/rsbuild-config.js +11 -3
  24. package/dist/esm/build/typed-externals.js +15 -2
  25. package/dist/esm/common/cooldown-store.js +38 -14
  26. package/dist/esm/common/deprecation.js +4 -1
  27. package/dist/esm/common/execution-context.js +3 -1
  28. package/dist/esm/common/explained-error.js +7 -3
  29. package/dist/esm/common/log-level.js +13 -9
  30. package/dist/esm/common/logger.js +4 -2
  31. package/dist/esm/common/meocord-text.js +11 -6
  32. package/dist/esm/common/redis-cooldown-store.js +3 -7
  33. package/dist/esm/common/response/flags.js +17 -1
  34. package/dist/esm/common/response/install-context.js +27 -16
  35. package/dist/esm/common/response/presenter.js +7 -3
  36. package/dist/esm/common/response/response-state.js +32 -10
  37. package/dist/esm/common/sharded-cooldown-store.js +11 -9
  38. package/dist/esm/common/theme.js +3 -1
  39. package/dist/esm/common/translator.js +27 -8
  40. package/dist/esm/core/autocomplete-routes.js +26 -0
  41. package/dist/esm/core/command-conflicts.js +15 -7
  42. package/dist/esm/core/command-registration.js +15 -1
  43. package/dist/esm/core/cooldown-runner.js +7 -9
  44. package/dist/esm/core/defer.js +2 -5
  45. package/dist/esm/core/dispatcher.js +92 -66
  46. package/dist/esm/core/event-requirements.js +67 -19
  47. package/dist/esm/core/event-sources.js +100 -0
  48. package/dist/esm/core/fallback.js +3 -1
  49. package/dist/esm/core/filter-runner.js +6 -6
  50. package/dist/esm/core/guard-runner.js +173 -52
  51. package/dist/esm/core/handler-input.js +24 -3
  52. package/dist/esm/core/handler-pipeline.js +54 -32
  53. package/dist/esm/core/handler-registry.js +51 -21
  54. package/dist/esm/core/input-runner.js +7 -8
  55. package/dist/esm/core/interceptor-runner.js +4 -5
  56. package/dist/esm/core/lifecycle-hooks.js +3 -8
  57. package/dist/esm/core/lifecycle-order.js +1 -15
  58. package/dist/esm/core/meocord-classes.js +14 -1
  59. package/dist/esm/core/meocord-factory.js +83 -54
  60. package/dist/esm/core/meocord.app.js +174 -89
  61. package/dist/esm/core/message-help.js +37 -15
  62. package/dist/esm/core/message-params.js +126 -46
  63. package/dist/esm/core/message-routes.js +225 -45
  64. package/dist/esm/core/message-words.js +30 -2
  65. package/dist/esm/core/observer-runner.js +4 -2
  66. package/dist/esm/core/process-state.js +19 -0
  67. package/dist/esm/core/providers.js +39 -4
  68. package/dist/esm/core/scalar-types.js +57 -16
  69. package/dist/esm/core/shard-context.js +11 -10
  70. package/dist/esm/core/shard-exit.js +3 -3
  71. package/dist/esm/core/shard-manager.js +16 -6
  72. package/dist/esm/core/stage-scope.js +2 -1
  73. package/dist/esm/core/startup-roots.js +28 -8
  74. package/dist/esm/core/theme-resolvers.js +26 -11
  75. package/dist/esm/core/theme-runtime.js +23 -13
  76. package/dist/esm/core/theme-scope.js +31 -30
  77. package/dist/esm/core/theme-validation.js +1 -1
  78. package/dist/esm/decorator/app.decorator.js +37 -1
  79. package/dist/esm/decorator/command-builder.decorator.js +14 -0
  80. package/dist/esm/decorator/controller-class.decorator.js +1 -1
  81. package/dist/esm/decorator/controller.decorator.js +48 -19
  82. package/dist/esm/decorator/defer.decorator.js +2 -2
  83. package/dist/esm/decorator/event.decorator.js +4 -5
  84. package/dist/esm/decorator/filter.decorator.js +4 -4
  85. package/dist/esm/decorator/guard.decorator.js +4 -61
  86. package/dist/esm/decorator/inject.decorator.js +4 -1
  87. package/dist/esm/decorator/interceptor.decorator.js +2 -2
  88. package/dist/esm/decorator/theme.decorator.js +4 -3
  89. package/dist/esm/decorator/validation.decorator.js +5 -5
  90. package/dist/esm/package.json.js +1 -1
  91. package/dist/esm/testing/catalog.js +32 -25
  92. package/dist/esm/testing/cooldown-store-suite.js +3 -3
  93. package/dist/esm/testing/discord-defaults.js +449 -335
  94. package/dist/esm/testing/inspect-handler.js +91 -10
  95. package/dist/esm/testing/meocord-testing-module.js +217 -91
  96. package/dist/esm/testing/mock-fn.js +8 -2
  97. package/dist/esm/testing/mock-interaction.js +864 -124
  98. package/dist/esm/testing/response-log.js +8 -0
  99. package/dist/esm/testing/response.js +7 -5
  100. package/dist/esm/testing/routing.js +48 -6
  101. package/dist/esm/testing/strict-mocks.js +8 -4
  102. package/dist/esm/testing/theme.js +3 -2
  103. package/dist/esm/util/common.util.js +30 -9
  104. package/dist/esm/util/generator-cli.util.js +6 -4
  105. package/dist/esm/util/inherited-env.util.js +72 -13
  106. package/dist/esm/util/json.util.js +10 -3
  107. package/dist/esm/util/meocord-cli.util.js +3 -2
  108. package/dist/esm/util/meocord-config-loader.util.js +14 -1
  109. package/dist/esm/util/meocord-config-validation.util.js +13 -3
  110. package/dist/esm/util/meocord-source-config.util.js +13 -3
  111. package/dist/esm/util/platform.util.js +3 -2
  112. package/dist/esm/util/refusal.util.js +29 -11
  113. package/dist/esm/util/registration-mode.util.js +14 -1
  114. package/dist/esm/util/timer-limit.util.js +4 -1
  115. package/dist/esm/util/tsconfig-paths.util.js +38 -7
  116. package/dist/esm/util/tsconfig.util.js +16 -58
  117. package/dist/esm/util/user-text.util.js +8 -4
  118. package/dist/esm/util/value.util.js +33 -5
  119. package/dist/types/common/index.d.cts +17 -10
  120. package/dist/types/common/index.d.ts +17 -10
  121. package/dist/types/core/index.d.cts +21 -11
  122. package/dist/types/core/index.d.ts +21 -11
  123. package/dist/types/decorator/index.d.cts +63 -63
  124. package/dist/types/decorator/index.d.ts +63 -63
  125. package/dist/types/{index-DKJclIi-.d.ts → index-Brkd6QGL.d.ts} +81 -35
  126. package/dist/types/{index-Dlf0ZZtS.d.cts → index-CqXImwF_.d.cts} +81 -35
  127. package/dist/types/interface/index.d.cts +1 -1
  128. package/dist/types/interface/index.d.ts +1 -1
  129. package/dist/types/{route-Bh1U13yy.d.ts → route-IQrK-MN2.d.cts} +11 -3
  130. package/dist/types/{route-Bh1U13yy.d.cts → route-bI-okq7g.d.ts} +11 -3
  131. package/dist/types/testing/index.d.cts +158 -61
  132. package/dist/types/testing/index.d.ts +158 -61
  133. package/dist/types/{theme-resolvers-CyEz9pnv.d.cts → theme-resolvers-AtgeaYqF.d.cts} +1 -1
  134. package/dist/types/{theme-resolvers-DA204O-q.d.ts → theme-resolvers-BgqysV8d.d.ts} +1 -1
  135. package/dist/types/{translator-Bg7hwxDF.d.cts → translator-D8aydlCE.d.ts} +12 -11
  136. package/dist/types/{translator-CeP4bCzp.d.ts → translator-iJhDlgwL.d.cts} +12 -11
  137. package/package.json +5 -2
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var discord_js = require('discord.js');
4
- var controller_decorator = require('./controller.decorator-XKMlcUZz.cjs');
5
- var providers = require('./providers-BZgJyfZS.cjs');
4
+ var controller_decorator = require('./controller.decorator-hp5xdJU4.cjs');
5
+ var providers = require('./providers-BBvbnmLP.cjs');
6
6
  var enum_index = require('../enum/index.cjs');
7
7
  var node_async_hooks = require('node:async_hooks');
8
- var translateError = require('./translate-error-BUAR1WYG.cjs');
8
+ var translateError = require('./translate-error-wsgfsUET.cjs');
9
9
 
10
10
  /**
11
11
  * How a pattern ranks, one character per segment twice over: `L` for literal text or `P` for a param, then how narrow
@@ -392,9 +392,9 @@ const quoted = (names)=>names.length < 2 ? names.map((name)=>`"${name}"`).join('
392
392
  }
393
393
  /**
394
394
  * Warns about a handler a subclass re-declares on other routes while it still answers the ones it inherits, for
395
- * `@Command`, `@MessageHandler`, `@ReactionHandler` and `@Autocomplete` alike. The next major version (5.0) drops
396
- * the inherited routes. Every class a listed controller extends is checked, a base that is not listed included, for
397
- * the methods the controller still inherits through it.
395
+ * `@Command`, `@MessageHandler`, `@ReactionHandler`, `@Autocomplete`, `@On` and `@Once` alike. The next major version
396
+ * (5.0) drops the inherited routes. Every class a listed controller extends is checked, a base that is not listed
397
+ * included, for the methods the controller still inherits through it.
398
398
  */ function warnInheritedRoutes(controllerClasses) {
399
399
  // A set, so a class two listed controllers extend is named once, for the routes either of them still answers
400
400
  const problems = new Set();
@@ -403,11 +403,18 @@ const quoted = (names)=>names.length < 2 ? names.map((name)=>`"${name}"`).join('
403
403
  // nothing of them from above it, so a class above is not named for them
404
404
  const cut = new Set();
405
405
  for(let prototype = controllerClass.prototype; prototype && prototype !== Object.prototype; prototype = Object.getPrototypeOf(prototype)){
406
- const declared = controller_decorator.getDeclaredRoutes(prototype);
407
406
  const base = Object.getPrototypeOf(prototype);
408
- if (declared.length === 0 || !base) continue;
407
+ if (!base) continue;
408
+ const declared = [
409
+ ...controller_decorator.getDeclaredRoutes(prototype),
410
+ ...controller_decorator.ownEventHandlers(prototype).map(controller_decorator.eventRoute)
411
+ ];
412
+ if (declared.length === 0) continue;
409
413
  if (Reflect.getOwnMetadata(controller_decorator.META.replacesInheritedRoutes, prototype)) for (const route of declared)cut.add(route.method);
410
- const inherited = controller_decorator.getHandlerRoutes(base).filter((route)=>!cut.has(route.method));
414
+ const inherited = [
415
+ ...controller_decorator.getHandlerRoutes(base),
416
+ ...providers.getEventHandlers(base).map(controller_decorator.eventRoute)
417
+ ].filter((route)=>!cut.has(route.method));
411
418
  for (const method of new Set(declared.map((route)=>route.method))){
412
419
  const own = [
413
420
  ...new Set(declared.filter((route)=>route.method === method).map((route)=>route.label))
@@ -721,7 +728,8 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
721
728
  parse: []
722
729
  }
723
730
  }));
724
- if (seconds > 0) {
731
+ // Longer than a timer can wait keeps it, as 0 does; the timer would fire at once
732
+ if (seconds > 0 && providers.timerKeepsSeconds(seconds)) {
725
733
  setTimeout(()=>{
726
734
  reply.delete().catch((failure)=>controller_decorator.logFailedSend(logger, 'delete a reply to a command', failure));
727
735
  }, seconds * 1000).unref?.();
@@ -919,9 +927,9 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
919
927
  }
920
928
  /**
921
929
  * Resolves each unit and runs its `onReady` one at a time, in the units' dependency order. A failure is
922
- * reported and the next unit still runs. Each unit whose `onReady` settled, or that has none, is pushed
923
- * to `entries` as it finishes, so a shutdown that starts meanwhile closes exactly those.
924
- */ async function runReadyHooks(container, units, client, info, entries, report, { stopped = ()=>false, slowAfterMs, settled, runOnReady } = {}) {
930
+ * reported and the next unit still runs. `settled` hears of each unit as its `onReady` settles, or once it is found
931
+ * to have none, which is what a shutdown that starts meanwhile reads.
932
+ */ async function runReadyHooks(container, units, client, info, report, { stopped = ()=>false, slowAfterMs, settled, runOnReady } = {}) {
925
933
  const failed = new Set();
926
934
  // For each unit, the failed units it depends on, directly or through another dependency
927
935
  const failedUpstream = new Map();
@@ -958,11 +966,7 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
958
966
  continue;
959
967
  }
960
968
  firstToken.set(instance, unit.token);
961
- const entry = lifecycleEntry(unit, instance);
962
- // Only a unit whose onReady has settled, or that has none, is shut down: a stop mid-ready waits for the one still
963
- // starting, within its time, and skips those not reached yet
964
969
  if (typeof instance.onReady !== 'function') {
965
- entries.push(entry);
966
970
  settled?.(unit);
967
971
  continue;
968
972
  }
@@ -979,7 +983,6 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
979
983
  } finally{
980
984
  clearTimeout(slow);
981
985
  }
982
- entries.push(entry);
983
986
  settled?.(unit);
984
987
  }
985
988
  }
@@ -1093,6 +1096,28 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1093
1096
  ]);
1094
1097
  }
1095
1098
 
1099
+ /**
1100
+ * Every `@Autocomplete` handler of the given controllers, read from their metadata, with the handlers of one option
1101
+ * before those of a whole command, so an option's handler wins over its command's whatever order they are declared in.
1102
+ */ function buildAutocompleteRoutes(controllers) {
1103
+ const routes = controllers.flatMap((controllerClass)=>controller_decorator.getAutocompleteHandlers(controllerClass.prototype).map((meta)=>({
1104
+ controllerClass,
1105
+ meta
1106
+ })));
1107
+ return routes.sort((a, b)=>Number(Boolean(b.meta.optionName)) - Number(Boolean(a.meta.optionName)));
1108
+ }
1109
+ /**
1110
+ * The handler an autocomplete reaches, as dispatch routes it: its full command path, then its command's name, and at
1111
+ * each the first handler whose option is the focused one, or that names none.
1112
+ */ function matchAutocompleteRoute(routes, interaction) {
1113
+ const focused = controller_decorator.focusedOptionName(interaction);
1114
+ for (const path of controller_decorator.resolveCommandPaths(interaction)){
1115
+ const route = routes.find(({ meta })=>meta.commandPath === path && (meta.optionName === undefined || meta.optionName === focused));
1116
+ if (route) return route;
1117
+ }
1118
+ return undefined;
1119
+ }
1120
+
1096
1121
  /** The keys a command is routed by, most specific first: a chat command's full path, then its name; none for anything else. */ function commandRouteKeys(interaction) {
1097
1122
  if (interaction.isChatInputCommand()) return controller_decorator.resolveCommandPaths(interaction);
1098
1123
  if (interaction.isContextMenuCommand() || interaction.isPrimaryEntryPointCommand()) return [
@@ -1255,6 +1280,26 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1255
1280
  if (winner) return `command '${keys[0]}' does not reach ${handler}: dispatch runs ${winner.controllerClass.name}.${winner.meta.methodName}.`;
1256
1281
  return `command '${keys[0]}' does not match ${handler}'s route ${named.map(({ route })=>`'${route}'`).join(' or ')}.`;
1257
1282
  }
1283
+ /**
1284
+ * Why an autocomplete could not reach a handler, for a test calling the handler directly: the handler is checked
1285
+ * against the one dispatch routes the autocomplete to among `controllers`, by its command path and focused option.
1286
+ * `undefined` when it is that one, when the handler has no `@Autocomplete` route, or when the interaction is not an
1287
+ * autocomplete or has no command name.
1288
+ */ function autocompleteMismatch(controllers, controller, methodName, interaction) {
1289
+ if (!interaction.isAutocomplete() || typeof interaction.commandName !== 'string') return undefined;
1290
+ const own = controller_decorator.getAutocompleteHandlers(controller.prototype).filter((meta)=>meta.methodName === methodName);
1291
+ if (own.length === 0) return undefined;
1292
+ const winner = matchAutocompleteRoute(buildAutocompleteRoutes(controllers), interaction);
1293
+ if (winner?.controllerClass === controller && winner.meta.methodName === methodName) return undefined;
1294
+ const handler = `${controller.name}.${methodName}`;
1295
+ const received = controller_decorator.describeInteraction(interaction);
1296
+ // A mock without a focused option reaches no option's handler, the commonest way to miss one
1297
+ const option = controller_decorator.focusedOptionName(interaction) === undefined ? own.find((meta)=>meta.optionName !== undefined)?.optionName : undefined;
1298
+ const fix = option === undefined ? '' : ` Give the mock the focused option: options: createChatInputOptions({ focused: '${option}' }).`;
1299
+ if (winner) return `${received} does not reach ${handler}: dispatch runs ${winner.controllerClass.name}.${winner.meta.methodName}.${fix}`;
1300
+ const routes = own.map((meta)=>meta.optionName === undefined ? `'${meta.commandPath}'` : `'${meta.commandPath}' option '${meta.optionName}'`);
1301
+ return `${received} does not match ${handler}'s route ${routes.join(' or ')}.${fix}`;
1302
+ }
1258
1303
 
1259
1304
  /**
1260
1305
  * How long a component or modal submission no route takes is left to the client's other listeners, such as a
@@ -1322,13 +1367,19 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1322
1367
  this.logger.warn(`messages.help is on, but ${route.controllerClass.name}.${route.method} (${JSON.stringify(route.pattern)}) takes "${shown}", ` + `which runs it instead, so the built-in help never answers. Turn help off, or give the handler another word.`);
1323
1368
  }
1324
1369
  }
1325
- /** The call for one event: the fallback, told of each error that reaches it when there is a recorder. */ callFor(record, startedAt) {
1370
+ /**
1371
+ * The call for one event, stamped as it is made, so every handler it runs reports one start and a duration that
1372
+ * includes routing and any handler that ran before it; the fallback is told of each error that reaches it when there
1373
+ * is a recorder.
1374
+ */ callFor(record) {
1326
1375
  const fallback = record ? async (error, context)=>{
1327
1376
  record.unhandled(error);
1328
- return this.fallback(error, context);
1377
+ const failure = await this.fallback(error, context);
1378
+ if (failure !== undefined) record.unanswered(error, failure);
1379
+ return failure;
1329
1380
  } : this.fallback;
1330
1381
  return {
1331
- startedAt,
1382
+ startedAt: performance.now(),
1332
1383
  fallback,
1333
1384
  record
1334
1385
  };
@@ -1339,32 +1390,35 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1339
1390
  }
1340
1391
  return this.controllerInstancesCache.get(controllerClass);
1341
1392
  }
1393
+ /**
1394
+ * Runs `run` on the controller's instance, resolved as its handler starts. A controller that cannot be made fails
1395
+ * only its own handlers: the error goes to the global filters, then the fallback, and the call goes on without it.
1396
+ */ async withInstance(controllerClass, args, call, run, unresolved = new Set()) {
1397
+ // A controller that failed to resolve for this event is reported once, however many of its handlers it reaches
1398
+ if (unresolved.has(controllerClass)) return;
1399
+ let instance;
1400
+ try {
1401
+ instance = this.getInstance(controllerClass);
1402
+ } catch (error) {
1403
+ unresolved.add(controllerClass);
1404
+ await providers.handleUnroutedError(this.container, args, error, this.runOptions(call));
1405
+ return;
1406
+ }
1407
+ await run(instance);
1408
+ }
1342
1409
  /** The component routes, built on first use; the app calls it as it is created. */ getComponentRoutes() {
1343
1410
  return this.componentRoutes ?? (this.componentRoutes = buildComponentRoutes([
1344
1411
  ...this.controllerClasses
1345
1412
  ]));
1346
1413
  }
1347
1414
  getAutocompleteRoutes() {
1348
- if (this.autocompleteRoutes) return this.autocompleteRoutes;
1349
- const routes = [];
1350
- for (const controllerClass of this.controllerClasses){
1351
- for (const meta of controller_decorator.getAutocompleteHandlers(this.getInstance(controllerClass))){
1352
- routes.push({
1353
- controllerClass,
1354
- meta
1355
- });
1356
- }
1357
- }
1358
- routes.sort((a, b)=>Number(Boolean(b.meta.optionName)) - Number(Boolean(a.meta.optionName)));
1359
- this.autocompleteRoutes = routes;
1360
- return routes;
1415
+ return this.autocompleteRoutes ?? (this.autocompleteRoutes = buildAutocompleteRoutes(this.controllerClasses));
1361
1416
  }
1362
1417
  /**
1363
1418
  * Dispatches an interaction. A failure outside any handler, such as no route matching or resolving
1364
1419
  * the controller, goes to the global filters, then the fallback.
1365
1420
  */ async interaction(interaction, record) {
1366
- // From the moment it arrives, so an observer's duration includes routing
1367
- const call = this.callFor(record, performance.now());
1421
+ const call = this.callFor(record);
1368
1422
  try {
1369
1423
  await this.dispatchInteraction(interaction, call);
1370
1424
  } catch (error) {
@@ -1390,14 +1444,14 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1390
1444
  if (matched) {
1391
1445
  const { route, params } = matched;
1392
1446
  interaction.dynamicParams = params;
1393
- await this.executeCommand(this.getInstance(route.controllerClass), route.meta, interaction, call);
1447
+ await this.executeCommand(route.controllerClass, route.meta, interaction, call);
1394
1448
  return;
1395
1449
  }
1396
1450
  }
1397
1451
  // The same match a test's invoke checks a handler against, so the two cannot disagree
1398
1452
  const command = matchCommandRoute(this.controllerClasses, interaction);
1399
1453
  if (command) {
1400
- await this.executeCommand(this.getInstance(command.controllerClass), command.meta, interaction, call);
1454
+ await this.executeCommand(command.controllerClass, command.meta, interaction, call);
1401
1455
  return;
1402
1456
  }
1403
1457
  // A component or modal no route takes may be a collector's: with another listener on the client, it has the
@@ -1420,21 +1474,17 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1420
1474
  * Answers an autocomplete interaction from the `@Autocomplete` handler that claims it. An unclaimed
1421
1475
  * option gets an empty list and a warning, rather than a menu stuck loading until Discord times out.
1422
1476
  */ async handleAutocomplete(interaction, call) {
1423
- const focusedName = controller_decorator.focusedOptionName(interaction);
1424
- for (const path of controller_decorator.resolveCommandPaths(interaction)){
1425
- for (const { controllerClass, meta } of this.getAutocompleteRoutes()){
1426
- if (meta.commandPath !== path) continue;
1427
- if (meta.optionName !== undefined && meta.optionName !== focusedName) continue;
1428
- const controllerInstance = this.getInstance(controllerClass);
1429
- this.logger.log('[AUTOCOMPLETE]', `[${path}]`, `[${meta.methodName}]`);
1430
- const params = controller_decorator.resolveOptionParams(interaction);
1431
- const ran = await this.invokeHandler(controllerInstance, meta.methodName, [
1432
- interaction,
1433
- params
1434
- ], call);
1435
- if (!ran) await closeAutocomplete(interaction, this.logger);
1436
- return;
1437
- }
1477
+ const route = matchAutocompleteRoute(this.getAutocompleteRoutes(), interaction);
1478
+ if (route) {
1479
+ const { controllerClass, meta } = route;
1480
+ this.logger.log('[AUTOCOMPLETE]', `[${meta.commandPath}]`, `[${meta.methodName}]`);
1481
+ const params = controller_decorator.resolveOptionParams(interaction);
1482
+ const ran = await this.invokeHandler(this.getInstance(controllerClass), meta.methodName, [
1483
+ interaction,
1484
+ params
1485
+ ], call);
1486
+ if (!ran) await closeAutocomplete(interaction, this.logger);
1487
+ return;
1438
1488
  }
1439
1489
  this.logger.warn(`No handler matched ${controller_decorator.describeInteraction(interaction)}. Declare an @Autocomplete handler for it, ` + `or drop setAutocomplete(true) from the option.`);
1440
1490
  await closeAutocomplete(interaction, this.logger);
@@ -1442,19 +1492,22 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1442
1492
  interaction
1443
1493
  ], this.runOptions(call));
1444
1494
  }
1445
- /** Warns, in development, that a modal field or select menu choice is hidden by a customId param of the same name. */ warnCollisions(methodName, names) {
1495
+ /** Warns, in development, that a modal field or select menu choice is hidden by a customId param of the same name. */ warnCollisions(controllerClass, methodName, names) {
1446
1496
  if (process.env.NODE_ENV !== 'development') return;
1497
+ let warned = this.warnedCollisions.get(controllerClass);
1498
+ if (!warned) this.warnedCollisions.set(controllerClass, warned = new Set());
1447
1499
  for (const name of names){
1448
1500
  const key = `${methodName}:${name}`;
1449
- if (this.warnedCollisions.has(key)) continue;
1450
- this.warnedCollisions.add(key);
1451
- this.logger.warn(`"${name}" is both a customId param and a modal field or select menu choice of ${methodName}; the handler receives the customId ` + `param. Rename one to receive both.`);
1501
+ if (warned.has(key)) continue;
1502
+ warned.add(key);
1503
+ this.logger.warn(`"${name}" is both a customId param and a modal field or select menu choice of ${controllerClass.name}.${methodName}; the handler ` + `receives the customId param. Rename one to receive both.`);
1452
1504
  }
1453
1505
  }
1454
1506
  /**
1455
1507
  * Runs a resolved command, shared by both dispatch paths so a pattern-matched
1456
1508
  * component and a named slash command behave identically once the route is chosen.
1457
- */ async executeCommand(controllerInstance, commandMetadata, interaction, call) {
1509
+ */ async executeCommand(controllerClass, commandMetadata, interaction, call) {
1510
+ const controllerInstance = this.getInstance(controllerClass);
1458
1511
  const { methodName, type } = commandMetadata;
1459
1512
  // No interaction-type check here: both callers pick the route with
1460
1513
  // `matchesCommandType` before getting this far, and `@Command` re-checks the
@@ -1462,7 +1515,7 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1462
1515
  this.logger.log('[INTERACTION]', `[${type}]`, `[${methodName}]`);
1463
1516
  const routeParams = interaction.dynamicParams;
1464
1517
  const { params, collisions } = handlerInput(interaction, routeParams);
1465
- this.warnCollisions(methodName, collisions);
1518
+ this.warnCollisions(controllerClass, methodName, collisions);
1466
1519
  await this.invokeHandler(controllerInstance, methodName, [
1467
1520
  interaction,
1468
1521
  params
@@ -1480,10 +1533,7 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1480
1533
  * says whether the handler ran. Its guard wrappers let this call through, so each guard runs once.
1481
1534
  */ async invokeHandler(instance, methodName, args, call, hooks = {}) {
1482
1535
  const handler = `${instance.constructor.name}.${methodName}`;
1483
- const onUnanswered = this.options.warnUnanswered ? (phase, returnedBy)=>this.warnUnansweredOnce(handler, phase, returnedBy && {
1484
- name: returnedBy.interceptor.name,
1485
- handlerStarted: returnedBy.handlerStarted
1486
- }) : undefined;
1536
+ const onUnanswered = this.options.warnUnanswered ? (unanswered)=>this.warnUnansweredOnce(handler, unanswered) : undefined;
1487
1537
  const outcome = await providers.runHandler(this.container, instance, methodName, args, {
1488
1538
  ...this.runOptions(call),
1489
1539
  onUnanswered,
@@ -1493,13 +1543,17 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1493
1543
  return outcome.ran;
1494
1544
  }
1495
1545
  /**
1496
- * Warns, once per handler, that it left its interaction unanswered or deferred without a follow-up, or that
1497
- * `interceptor` returned before the handler ran or finished, and the call ended so.
1498
- */ warnUnansweredOnce(handler, phase, interceptor) {
1546
+ * Warns, once per handler, that it left its interaction or autocomplete unanswered or deferred without a follow-up,
1547
+ * or that an interceptor returned before the handler ran or finished, and the call ended so.
1548
+ */ warnUnansweredOnce(handler, { type, phase, returnedBy }) {
1499
1549
  if (this.warnedUnanswered.has(handler)) return;
1500
1550
  this.warnedUnanswered.add(handler);
1551
+ const interceptor = returnedBy && {
1552
+ name: returnedBy.interceptor.name,
1553
+ handlerStarted: returnedBy.handlerStarted
1554
+ };
1501
1555
  const returned = interceptor && `${handler}: its interceptor ${interceptor.name} returned before the handler ${interceptor.handlerStarted ? 'finished' : 'ran'}`;
1502
- const what = interceptor ? phase === 'unanswered' ? `${returned}, without answering the interaction, so the user saw "The application did not respond". Answer it ` + `in ${interceptor.name}, or await next.handle().` : interceptor.handlerStarted ? `${returned}, and the deferred interaction had no follow-up when the call ended, so @Defer's lock was ` + `released while the user still saw it thinking. Follow up in ${interceptor.name} with respond(interaction).send(), ` + 'or await next.handle().' : `${returned}, and the deferred interaction was never followed up, so the user saw it thinking until Discord ` + `gave up. Follow up in ${interceptor.name} with respond(interaction).send(), or await next.handle().` : phase === 'unanswered' ? `${handler} finished without answering its interaction, so the user saw "The application did not respond". ` + 'Answer it with respond(interaction).send(), or acknowledge it first with @Defer().' : `${handler} deferred its interaction and never followed up, so the user saw it thinking until Discord gave up. ` + 'Follow up with respond(interaction).send().';
1556
+ const what = type === 'autocomplete' ? `${handler} finished without answering its autocomplete, so the user saw its options fail to load. Answer it ` + 'with interaction.respond(choices) within 3 seconds.' : interceptor ? phase === 'unanswered' ? `${returned}, without answering the interaction, so the user saw "The application did not respond". Answer it ` + `in ${interceptor.name}, or await next.handle().` : interceptor.handlerStarted ? `${returned}, and the deferred interaction had no follow-up when the call ended, so @Defer's lock was ` + `released while the user still saw it thinking. Follow up in ${interceptor.name} with respond(interaction).send(), ` + 'or await next.handle().' : `${returned}, and the deferred interaction was never followed up, so the user saw it thinking until Discord ` + `gave up. Follow up in ${interceptor.name} with respond(interaction).send(), or await next.handle().` : phase === 'unanswered' ? `${handler} finished without answering its interaction, so the user saw "The application did not respond". ` + 'Answer it with respond(interaction).send(), or acknowledge it first with @Defer().' : `${handler} deferred its interaction and never followed up, so the user saw it thinking until Discord gave up. ` + 'Follow up with respond(interaction).send().';
1503
1557
  this.logger.warn(`${what} Shown once per handler; @MeoCord({ warnUnanswered: false }) turns it off.`);
1504
1558
  }
1505
1559
  /**
@@ -1508,7 +1562,7 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1508
1562
  * usage through that handler's filters. A bare parent's unguarded subcommands' usage, once the app's guards allow
1509
1563
  * it, and a failure to read the prefixes, go through the global filters, then the fallback; the listeners still run.
1510
1564
  */ async message(message, record) {
1511
- if (message.author.bot || !message.content?.trim()) return;
1565
+ if (message.author.bot || !providers.hasMessageText(message.content)) return;
1512
1566
  const call = this.callFor(record);
1513
1567
  let target;
1514
1568
  try {
@@ -1535,24 +1589,29 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1535
1589
  }
1536
1590
  // The matched handler, then every listener, each resolved from the container only as it starts
1537
1591
  const runs = [];
1592
+ const unresolved = new Set();
1538
1593
  if (target) {
1539
1594
  const { route, params, start, given } = target;
1540
1595
  noteInvocation(message, `${start}${providers.commandWordsOf(route.tokens).join(' ')}`);
1541
1596
  const hooks = providers.messageCommandHooks(route, params, message, start, given, this.messageOptions.types);
1542
1597
  runs.push({
1543
1598
  name: `${route.controllerClass.name}.${route.method}`,
1544
- run: (at)=>this.invokeHandler(this.getInstance(route.controllerClass), route.method, [
1545
- message,
1546
- params
1547
- ], at, hooks)
1599
+ run: (at)=>this.withInstance(route.controllerClass, [
1600
+ message
1601
+ ], at, (instance)=>this.invokeHandler(instance, route.method, [
1602
+ message,
1603
+ params
1604
+ ], at, hooks), unresolved)
1548
1605
  });
1549
1606
  }
1550
1607
  for (const { controllerClass, method } of this.messageListeners){
1551
1608
  runs.push({
1552
1609
  name: `${controllerClass.name}.${method}`,
1553
- run: (at)=>this.invokeHandler(this.getInstance(controllerClass), method, [
1610
+ run: (at)=>this.withInstance(controllerClass, [
1554
1611
  message
1555
- ], at)
1612
+ ], at, (instance)=>this.invokeHandler(instance, method, [
1613
+ message
1614
+ ], at), unresolved)
1556
1615
  });
1557
1616
  }
1558
1617
  if (this.messageOptions.handlers === 'concurrent') await this.runTogether(runs, call);
@@ -1577,7 +1636,8 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1577
1636
  ...call,
1578
1637
  record: {
1579
1638
  settled: (...report)=>reports[index].push(report),
1580
- unhandled: (error)=>record.unhandled(error)
1639
+ unhandled: (error)=>record.unhandled(error),
1640
+ unanswered: (error, failure)=>record.unanswered(error, failure)
1581
1641
  }
1582
1642
  } : call)));
1583
1643
  for (const report of reports.flat())record?.settled(...report);
@@ -1657,7 +1717,7 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1657
1717
  const matching = this.controllerClasses.map((controllerClass)=>({
1658
1718
  controllerClass,
1659
1719
  // Handlers for this emoji first, then those for every emoji
1660
- handlers: controller_decorator.getReactionHandlers(this.getInstance(controllerClass)).filter(forEmoji).sort((a, b)=>Number(!a.emoji) - Number(!b.emoji))
1720
+ handlers: controller_decorator.getReactionHandlers(controllerClass.prototype).filter(forEmoji).sort((a, b)=>Number(!a.emoji) - Number(!b.emoji))
1661
1721
  })).filter(({ handlers })=>handlers.length > 0);
1662
1722
  if (matching.length === 0) return;
1663
1723
  // Only asked when a matching handler leaves bots out, since a partial user costs a fetch
@@ -1681,16 +1741,21 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1681
1741
  return;
1682
1742
  }
1683
1743
  for (const { controllerClass, handlers } of runs){
1684
- const controllerInstance = this.getInstance(controllerClass);
1685
- for (const { method } of handlers){
1686
- await this.invokeHandler(controllerInstance, method, [
1744
+ await this.withInstance(controllerClass, [
1745
+ reaction,
1746
+ {
1747
+ user,
1748
+ action
1749
+ }
1750
+ ], call, async (instance)=>{
1751
+ for (const { method } of handlers)await this.invokeHandler(instance, method, [
1687
1752
  reaction,
1688
1753
  {
1689
1754
  user,
1690
1755
  action
1691
1756
  }
1692
1757
  ], call);
1693
- }
1758
+ });
1694
1759
  }
1695
1760
  }
1696
1761
  /**
@@ -1709,7 +1774,10 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1709
1774
  constructor(options){
1710
1775
  this.options = options;
1711
1776
  this.controllerInstancesCache = new Map();
1712
- /** Handler and name pairs already warned about, so a collision warns once, not on every submit or selection. */ this.warnedCollisions = new Set();
1777
+ /**
1778
+ * Method and name pairs already warned about, by controller class, so a collision warns once per handler, not on
1779
+ * every submit or selection. Keyed by the class itself, so two controllers of one name stay apart.
1780
+ */ this.warnedCollisions = new WeakMap();
1713
1781
  /** The handlers already warned about, so each is named once however often it runs. */ this.warnedUnanswered = new Set();
1714
1782
  /** The message handlers already warned about as slow, so each is named once. */ this.warnedSlow = new Set();
1715
1783
  this.container = options.container;
@@ -1724,6 +1792,8 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1724
1792
  // The app's own help command reads this table too, so it lists the commands dispatch reaches
1725
1793
  if (this.container.isBound(providers.HandlerRegistry)) providers.shareMessageRoutes(this.container.get(providers.HandlerRegistry), this.messageRoutes);
1726
1794
  this.warnUnreachableHelp();
1795
+ // Here, where the routes are built once, rather than in buildMessageRoutes, which startup runs twice
1796
+ for (const warning of providers.messageRouteWarnings(this.messageRoutes, this.messageOptions))this.logger.warn(warning);
1727
1797
  this.messageListeners = this.controllerClasses.flatMap((controllerClass)=>controller_decorator.getMessageHandlers(controllerClass.prototype).filter((handler)=>handler.pattern === undefined).map(({ method })=>({
1728
1798
  controllerClass,
1729
1799
  method
@@ -1807,19 +1877,38 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1807
1877
  seen.add(cls);
1808
1878
  return providers.injectedTokens(cls).some((token)=>token === controller_decorator.ExecutionContext || providers.isAppClassToken(token) && !providers$1.has(token) && !seen.has(token) && reachesContext(token, providers$1, seen));
1809
1879
  }
1810
- /**
1811
- * The classes the app's stages and presenter inject, which they would make only at their first call. Made with the
1812
- * app's own classes, a service among them gets its lifecycle hooks. One that reaches the call's `ExecutionContext` is
1813
- * made for each call, so it is left out, and a stage a test replaces is not among the stages, so what it would inject
1814
- * is not made.
1815
- */ function stageDependencies({ decorators }, providers$1, presenter) {
1880
+ /** The app's classes the stages and presenter inject. */ function stageInjected({ decorators }, presenter) {
1816
1881
  const stages = [
1817
1882
  ...decorators
1818
1883
  ].filter(([, decorator])=>decorator !== '@Controller()').map(([stage])=>stage);
1819
1884
  if (typeof presenter === 'function') stages.push(presenter);
1820
1885
  return [
1821
1886
  ...new Set(stages.flatMap((stage)=>providers.injectedTokens(stage)))
1822
- ].filter(providers.isAppClassToken).filter((cls)=>!reachesContext(cls, providers$1));
1887
+ ].filter(providers.isAppClassToken);
1888
+ }
1889
+ /**
1890
+ * The classes the app's stages and presenter inject, which they would make only at their first call. Made with the
1891
+ * app's own classes, a service among them gets its lifecycle hooks. One that reaches the call's `ExecutionContext` is
1892
+ * made for each call, so it is left out, and a stage a test replaces is not among the stages, so what it would inject
1893
+ * is not made.
1894
+ */ function stageDependencies(startup, providers, presenter) {
1895
+ return stageInjected(startup, presenter).filter((cls)=>!reachesContext(cls, providers));
1896
+ }
1897
+ /**
1898
+ * A warning for each `@On` and `@Once` handler on a class the stages and presenter reach that is made for each call,
1899
+ * as it reaches `ExecutionContext`: no one instance of it receives events.
1900
+ */ function perCallEventWarnings(startup, providers$1, presenter) {
1901
+ const perCall = new Set();
1902
+ // What a class made for each call injects is made for each call too only if it also reaches the context
1903
+ const visit = (cls)=>{
1904
+ if (perCall.has(cls) || providers$1.has(cls) || !reachesContext(cls, providers$1)) return;
1905
+ perCall.add(cls);
1906
+ providers.injectedTokens(cls).filter(providers.isAppClassToken).forEach(visit);
1907
+ };
1908
+ stageInjected(startup, presenter).forEach(visit);
1909
+ return [
1910
+ ...perCall
1911
+ ].flatMap((cls)=>providers.getEventHandlers(cls.prototype).map(({ event, method, once })=>`${cls.name}.${method}: @${once ? 'Once' : 'On'}('${event}') is on a class made for each call, as it injects ` + 'ExecutionContext or a class that does, so it receives no events. The next major version (5.0) refuses it; ' + 'move the handler to a service that does not.'));
1823
1912
  }
1824
1913
  /** How a container checks a guard first met at a call: the guard and what it injects, as the startup checks would. */ function lateGuardCheck(providers$1, translator) {
1825
1914
  return (guard)=>assertStartupClasses({
@@ -1842,6 +1931,100 @@ async function replyToMessage(message, error, logger, withEmoji, answering) {
1842
1931
  for (const needsOne of classes.filter((cls)=>providers.injectedTokens(cls).includes(controller_decorator.Translator)))controller_decorator.startupError(controller_decorator.missingTranslatorError(needsOne));
1843
1932
  }
1844
1933
 
1934
+ /**
1935
+ * One source for each instance whose lifecycle hooks run, the cooldown store aside, in lifecycle order; read once the
1936
+ * providers are made, as a value's or a factory's class is its instance's. A class `prepared` lacks has its handlers
1937
+ * prepared here, and one a controller's would be refused for is left out, with `warn` naming the refusal.
1938
+ */ function eventSources(container, providers$1, units, prepared, warn) {
1939
+ // The tokens of each class the container makes, bound as itself or given as a provider's `useClass`, the cooldown
1940
+ // store's included: no event reaches it, nor its instance through a factory
1941
+ const classTokens = new Map();
1942
+ for (const { token } of units){
1943
+ const provider = providers$1.get(token);
1944
+ if (provider && !providers.isClassProvider(provider) || !provider && !providers.isAppClassToken(token)) continue;
1945
+ const cls = provider ? provider.useClass : token;
1946
+ classTokens.set(cls, [
1947
+ ...classTokens.get(cls) ?? [],
1948
+ token
1949
+ ]);
1950
+ }
1951
+ const names = new Map(prepared.map((cls)=>[
1952
+ cls.name,
1953
+ cls
1954
+ ]));
1955
+ const handlers = new Map();
1956
+ const handlersOf = (cls)=>{
1957
+ let found = handlers.get(cls);
1958
+ if (!found) {
1959
+ found = prepared.includes(cls) ? providers.getEventHandlers(cls.prototype) : prepareClass(container, cls, names, warn);
1960
+ handlers.set(cls, found);
1961
+ }
1962
+ return found;
1963
+ };
1964
+ const sources = [];
1965
+ const given = new Set();
1966
+ for (const { token, cooldownStore } of units){
1967
+ // The cooldown store is the app's infrastructure, never one of its classes, so no event reaches it
1968
+ if (cooldownStore) continue;
1969
+ const provider = providers$1.get(token);
1970
+ if (!provider || providers.isClassProvider(provider)) {
1971
+ const cls = provider ? provider.useClass : token;
1972
+ const found = handlersOf(cls);
1973
+ if (found.length > 0) sources.push({
1974
+ cls,
1975
+ handlers: found,
1976
+ resolve: ()=>container.get(token)
1977
+ });
1978
+ continue;
1979
+ }
1980
+ // A value, or a factory's, made by now; one instance under several tokens is one source
1981
+ const instance = container.get(token);
1982
+ if (typeof instance !== 'object' || instance === null || given.has(instance)) continue;
1983
+ given.add(instance);
1984
+ const cls = instance.constructor;
1985
+ if (typeof cls !== 'function') continue;
1986
+ const found = handlersOf(cls);
1987
+ if (found.length === 0) continue;
1988
+ // A factory may return the instance the container made for a class unit, whose own source runs it; only units of
1989
+ // the instance's class are resolved, and each resolves on the event through its own source anyway
1990
+ const tokens = classTokens.get(cls) ?? [];
1991
+ const owned = ()=>tokens.some((token)=>container.get(token) === instance);
1992
+ sources.push({
1993
+ cls,
1994
+ handlers: found,
1995
+ resolve: ()=>owned() ? undefined : instance
1996
+ });
1997
+ }
1998
+ return sources;
1999
+ }
2000
+ /** The handlers of `cls` that pass a controller's checks, warning of each other one and why it does not run. */ function prepareClass(container, cls, names, warn) {
2001
+ const declared = providers.getEventHandlers(cls.prototype);
2002
+ if (declared.length === 0) return [];
2003
+ const other = names.get(cls.name);
2004
+ if (!other) names.set(cls.name, cls);
2005
+ // @Once tells classes apart by name, so this class's would share another's
2006
+ const sharesName = other !== undefined && other !== cls && declared.some((handler)=>handler.once);
2007
+ if (sharesName) {
2008
+ refused(warn, cls, `${cls.name}: two classes have this name; @Cooldown and @Once tell classes apart by name, so they would share their counts. Rename one of them.`, declared.filter((handler)=>handler.once));
2009
+ }
2010
+ const errors = new Map();
2011
+ return declared.filter((handler)=>{
2012
+ if (sharesName && handler.once) return false;
2013
+ let found = errors.get(handler.method);
2014
+ if (!found) {
2015
+ found = providers.prepareEventHandler(container, cls, handler.method);
2016
+ errors.set(handler.method, found);
2017
+ const onMethod = declared.filter((other)=>other.method === handler.method && !(sharesName && other.once));
2018
+ for (const error of found)refused(warn, cls, error.message, onMethod);
2019
+ }
2020
+ return found.length === 0;
2021
+ });
2022
+ }
2023
+ function refused(warn, cls, reason, handlers) {
2024
+ const labels = handlers.map(({ event, method, once })=>`@${once ? 'Once' : 'On'}('${event}') in ${cls.name}.${method}`);
2025
+ warn(`${reason} Until the next major version (5.0) refuses it at startup, ${labels.join(', ')} does not run.`);
2026
+ }
2027
+
1845
2028
  exports.DEFAULT_SHUTDOWN_TIMEOUT_MS = DEFAULT_SHUTDOWN_TIMEOUT_MS;
1846
2029
  exports.Dispatcher = Dispatcher;
1847
2030
  exports.ReadyPass = ReadyPass;
@@ -1849,11 +2032,13 @@ exports.RunningCalls = RunningCalls;
1849
2032
  exports.SHUTDOWN_MARGIN_MS = SHUTDOWN_MARGIN_MS;
1850
2033
  exports.assertDistinctCommands = assertDistinctCommands;
1851
2034
  exports.assertStartupClasses = assertStartupClasses;
2035
+ exports.autocompleteMismatch = autocompleteMismatch;
1852
2036
  exports.buildComponentRoutes = buildComponentRoutes;
1853
2037
  exports.callsSettled = callsSettled;
1854
2038
  exports.commandMismatch = commandMismatch;
1855
2039
  exports.componentRouteFor = componentRouteFor;
1856
2040
  exports.createFallback = createFallback;
2041
+ exports.eventSources = eventSources;
1857
2042
  exports.findComponentRouteConflicts = findComponentRouteConflicts;
1858
2043
  exports.handlerInput = handlerInput;
1859
2044
  exports.lateGuardCheck = lateGuardCheck;
@@ -1861,6 +2046,7 @@ exports.lifecycleEntry = lifecycleEntry;
1861
2046
  exports.matchComponentRoute = matchComponentRoute;
1862
2047
  exports.messageDecorator = messageDecorator;
1863
2048
  exports.ownInteractionListener = ownInteractionListener;
2049
+ exports.perCallEventWarnings = perCallEventWarnings;
1864
2050
  exports.reactionDecorator = reactionDecorator;
1865
2051
  exports.readCustomId = readCustomId;
1866
2052
  exports.replyWithUserError = replyWithUserError;