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,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  require('reflect-metadata');
4
- var providers = require('../_shared/providers-BZgJyfZS.cjs');
5
- var controller_decorator = require('../_shared/controller.decorator-XKMlcUZz.cjs');
4
+ var providers = require('../_shared/providers-BBvbnmLP.cjs');
5
+ var controller_decorator = require('../_shared/controller.decorator-hp5xdJU4.cjs');
6
6
  var inversify = require('inversify');
7
7
  require('node:async_hooks');
8
8
  require('../enum/index.cjs');
@@ -12,14 +12,14 @@ require('fs');
12
12
  require('path');
13
13
  require('cli-table3');
14
14
  require('node:crypto');
15
- require('node:fs');
16
- require('node:url');
17
15
  require('node:util');
18
16
  require('dayjs');
19
17
  require('dayjs/plugin/utc.js');
20
18
  require('dayjs/plugin/timezone.js');
21
19
  require('module');
20
+ require('node:fs');
22
21
  require('chalk');
22
+ require('node:url');
23
23
 
24
24
  /**
25
25
  * Marks a class as a service, which controllers and other services inject by its type.
@@ -54,6 +54,13 @@ require('chalk');
54
54
  });
55
55
  }
56
56
 
57
+ const logger$3 = new controller_decorator.Logger('CommandBuilder');
58
+ /** The nearest base class of `target` that gives `guilds`, which `target` inherits when it gives none. */ function guildsBase(target) {
59
+ for(let base = Object.getPrototypeOf(target); base && base !== Function.prototype; base = Object.getPrototypeOf(base)){
60
+ if (Reflect.hasOwnMetadata(controller_decorator.META.builderGuilds, base)) return base;
61
+ }
62
+ return undefined;
63
+ }
57
64
  /**
58
65
  * Marks a class as a command's builder, which describes the command MeoCord registers with Discord.
59
66
  *
@@ -91,45 +98,61 @@ require('chalk');
91
98
  if (options.guilds) Reflect.defineMetadata(controller_decorator.META.builderGuilds, [
92
99
  ...options.guilds
93
100
  ], target);
101
+ else {
102
+ const base = guildsBase(target);
103
+ if (base) {
104
+ logger$3.warn(`${target.name} gives no guilds and inherits ${base.name}'s; declare guilds on ${target.name} to keep them. ` + 'The next major version (5.0) registers it in the configured scope.');
105
+ }
106
+ }
94
107
  });
95
108
  }
96
109
 
97
110
  /**
98
- * Adds guards to a method's class or method list, then republishes the effective list under
99
- * `META.guards`, in the order they run: the bases' before the class's own, and within one class or method the
100
- * latest decorator first, since it wraps outermost.
101
- */ function recordGuards(key, guards, prototype, methodName) {
102
- const existing = Reflect.getOwnMetadata(key, prototype, methodName) ?? [];
103
- const bases = key === controller_decorator.META.classGuards ? Reflect.getOwnMetadata(controller_decorator.META.baseClassGuards, prototype, methodName) ?? 0 : 0;
104
- Reflect.defineMetadata(key, [
105
- ...existing.slice(0, bases),
106
- ...guards,
107
- ...existing.slice(bases)
108
- ], prototype, methodName);
109
- const [classGuards, inheritedGuards, methodGuards] = [
110
- controller_decorator.META.classGuards,
111
- controller_decorator.META.inheritedGuards,
112
- controller_decorator.META.methodGuards
113
- ].map((list)=>Reflect.getOwnMetadata(list, prototype, methodName) ?? []);
114
- Reflect.defineMetadata(controller_decorator.META.guards, [
115
- ...inheritedGuards,
116
- ...classGuards,
117
- ...methodGuards
118
- ], prototype, methodName);
119
- }
120
- /**
121
- * Wraps a method so a direct call runs its guards first: the wrapper entered first runs the whole
122
- * chain dispatch resolves for the handler, and lets the ones inside it through. A call from dispatch,
123
- * which has already run the guards, passes through every wrapper.
124
- */ function applyGuards(descriptor, prototype, propertyKey) {
125
- const originalMethod = descriptor.value;
126
- descriptor.value = controller_decorator.wrapsMethod(async function(...args) {
127
- if (providers.consumeDispatchMark(args[0], this, propertyKey)) return originalMethod.apply(this, args);
128
- return providers.runDirectCall(this, propertyKey, args, ()=>originalMethod.apply(this, args));
129
- }, originalMethod);
130
- const wrappers = Reflect.getOwnMetadata(controller_decorator.META.guardWrappers, prototype, propertyKey) ?? 0;
131
- Reflect.defineMetadata(controller_decorator.META.guardWrappers, wrappers + 1, prototype, propertyKey);
111
+ * Marks a class as a controller, whose methods handle commands, components, messages, reactions or events.
112
+ *
113
+ * Use it on every class of handlers, and list the class in `@MeoCord({ controllers })`. Logic the handlers
114
+ * share, such as database access, belongs in a service it injects.
115
+ *
116
+ * @remarks
117
+ * A controller's class-level guards and filters apply to every handler it declares or inherits, its interceptors to
118
+ * all but autocomplete, and its cooldowns to its command, component and message handlers; each also applies to the
119
+ * handlers of a class that extends it. A base wraps what extends it: guards and interceptors
120
+ * run the top base's first, then each subclass's, then the method's, and filters are tried the other way, the
121
+ * method's first. `inheritStages: false` keeps the handlers a subclass declares to its own stages.
122
+ *
123
+ * @param options - Whether the handlers it declares take the stages of the classes it extends.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * @Controller()
128
+ * export class ProfileSlashController {
129
+ * constructor(private readonly profiles: ProfileService) {}
130
+ *
131
+ * @Command('profile', CommandType.SLASH)
132
+ * async profile(interaction: ChatInputCommandInteraction) {
133
+ * await respond(interaction).send({ embeds: [await this.profiles.render(interaction.user.id)] })
134
+ * }
135
+ * }
136
+ * ```
137
+ *
138
+ * @group Decorators
139
+ * @category Controllers
140
+ * @see {@link Command}
141
+ * @see {@link ControllerOptions}
142
+ * @see {@link https://meocord.dev/docs/4.2/how-a-call-runs | How a call runs}
143
+ */ function Controller(options = {}) {
144
+ return controller_decorator.declaring(function(target, propertyKey) {
145
+ if (controller_decorator.deprecatedOnMethod('@Controller', target, propertyKey)) return;
146
+ providers.makeInjectable(target);
147
+ if (options.inheritStages === false) Reflect.defineMetadata(controller_decorator.META.inheritStages, false, target);
148
+ if (options.inheritedRoutes === 'replace') {
149
+ controller_decorator.dropInheritedRoutes(target.prototype);
150
+ Reflect.defineMetadata(controller_decorator.META.replacesInheritedRoutes, true, target.prototype);
151
+ }
152
+ providers.guardOwnHandlersWithBaseGuards(target);
153
+ });
132
154
  }
155
+
133
156
  /**
134
157
  * The class's own descriptor for a handler. An inherited handler gets one that calls the inherited
135
158
  * method, starting from its guard lists and wrapper count, so class guards wrap it like an own method.
@@ -157,26 +180,6 @@ require('chalk');
157
180
  ...inherited
158
181
  };
159
182
  }
160
- /**
161
- * Gives each handler a controller declares itself the class-level guards of the classes it extends,
162
- * as its inherited handlers have: recorded under `META.guards`, and wrapped so a direct call
163
- * runs them when no other guard wraps the handler. `@Controller` calls this.
164
- */ function guardOwnHandlersWithBaseGuards(target) {
165
- const prototype = target.prototype;
166
- for (const methodName of providers.handlerMethods(prototype)){
167
- const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
168
- // Inherited handlers have the chain of the class that declares them
169
- if (typeof descriptor?.value !== 'function' || Reflect.getOwnMetadata(controller_decorator.META.inheritedFrom, prototype, methodName)) continue;
170
- // Base first, as dispatch runs them
171
- const guards = providers.classChain(prototype, prototype).slice(1).reverse().flatMap(providers.classLevelGuards);
172
- if (guards.length === 0) continue;
173
- if (!Reflect.getOwnMetadata(controller_decorator.META.guardWrappers, prototype, methodName)) {
174
- applyGuards(descriptor, prototype, methodName);
175
- Object.defineProperty(prototype, methodName, descriptor);
176
- }
177
- recordGuards(controller_decorator.META.inheritedGuards, guards, prototype, methodName);
178
- }
179
- }
180
183
  /**
181
184
  * Marks a class as a guard, which decides whether a handler runs.
182
185
  *
@@ -250,12 +253,12 @@ require('chalk');
250
253
  */ function UseGuard(...entries) {
251
254
  const guards = entries;
252
255
  return controller_decorator.declaring(function(target, propertyKey, descriptor) {
253
- const where = propertyKey === undefined ? String(target?.name) : `${target.constructor.name}.${String(propertyKey)}`;
256
+ const where = controller_decorator.decoratedName(target, propertyKey);
254
257
  providers.assertStageEntries('@UseGuard', 'guard', where, guards);
255
258
  if (descriptor && propertyKey) {
256
259
  // Method Decorator
257
- applyGuards(descriptor, target, String(propertyKey));
258
- recordGuards(controller_decorator.META.methodGuards, guards, target, String(propertyKey));
260
+ providers.applyGuards(descriptor, target, String(propertyKey));
261
+ providers.recordGuards(controller_decorator.META.methodGuards, guards, target, String(propertyKey));
259
262
  } else if (typeof target === 'function' && !propertyKey && !descriptor) {
260
263
  // Class Decorator
261
264
  const prototype = target.prototype;
@@ -268,61 +271,15 @@ require('chalk');
268
271
  for (const methodName of methods){
269
272
  const methodDescriptor = ownHandlerDescriptor(prototype, methodName);
270
273
  if (methodDescriptor) {
271
- applyGuards(methodDescriptor, prototype, methodName);
274
+ providers.applyGuards(methodDescriptor, prototype, methodName);
272
275
  Object.defineProperty(prototype, methodName, methodDescriptor);
273
- recordGuards(controller_decorator.META.classGuards, guards, prototype, methodName);
276
+ providers.recordGuards(controller_decorator.META.classGuards, guards, prototype, methodName);
274
277
  }
275
278
  }
276
279
  }
277
280
  });
278
281
  }
279
282
 
280
- /**
281
- * Marks a class as a controller, whose methods handle commands, components, messages, reactions or events.
282
- *
283
- * Use it on every class of handlers, and list the class in `@MeoCord({ controllers })`. Logic the handlers
284
- * share, such as database access, belongs in a service it injects.
285
- *
286
- * @remarks
287
- * A controller's class-level guards and filters apply to every handler it declares or inherits, its interceptors to
288
- * all but autocomplete, and its cooldowns to its command, component and message handlers; each also applies to the
289
- * handlers of a class that extends it. A base wraps what extends it: guards and interceptors
290
- * run the top base's first, then each subclass's, then the method's, and filters are tried the other way, the
291
- * method's first. `inheritStages: false` keeps the handlers a subclass declares to its own stages.
292
- *
293
- * @param options - Whether the handlers it declares take the stages of the classes it extends.
294
- *
295
- * @example
296
- * ```ts
297
- * @Controller()
298
- * export class ProfileSlashController {
299
- * constructor(private readonly profiles: ProfileService) {}
300
- *
301
- * @Command('profile', CommandType.SLASH)
302
- * async profile(interaction: ChatInputCommandInteraction) {
303
- * await respond(interaction).send({ embeds: [await this.profiles.render(interaction.user.id)] })
304
- * }
305
- * }
306
- * ```
307
- *
308
- * @group Decorators
309
- * @category Controllers
310
- * @see {@link Command}
311
- * @see {@link ControllerOptions}
312
- * @see {@link https://meocord.dev/docs/4.2/how-a-call-runs | How a call runs}
313
- */ function Controller(options = {}) {
314
- return controller_decorator.declaring(function(target, propertyKey) {
315
- if (controller_decorator.deprecatedOnMethod('@Controller', target, propertyKey)) return;
316
- providers.makeInjectable(target);
317
- if (options.inheritStages === false) Reflect.defineMetadata(controller_decorator.META.inheritStages, false, target);
318
- if (options.inheritedRoutes === 'replace') {
319
- controller_decorator.dropInheritedRoutes(target.prototype);
320
- Reflect.defineMetadata(controller_decorator.META.replacesInheritedRoutes, true, target.prototype);
321
- }
322
- guardOwnHandlersWithBaseGuards(target);
323
- });
324
- }
325
-
326
283
  /**
327
284
  * Marks a class as an interceptor, which wraps a handler to act before and after it.
328
285
  *
@@ -395,7 +352,7 @@ require('chalk');
395
352
  * @see {@link https://meocord.dev/docs/4.2/interceptors | Interceptors}
396
353
  */ function UseInterceptor(...interceptors) {
397
354
  return controller_decorator.declaring(function(target, propertyKey) {
398
- const where = propertyKey === undefined ? target.name : `${target.constructor.name}.${String(propertyKey)}`;
355
+ const where = controller_decorator.decoratedName(target, propertyKey);
399
356
  providers.assertStageEntries('@UseInterceptor', 'interceptor', where, interceptors);
400
357
  // Decorators apply bottom-up, so a higher decorator's interceptors go first, as with @UseGuard.
401
358
  if (propertyKey === undefined) {
@@ -447,14 +404,14 @@ require('chalk');
447
404
  controller_decorator.refuseOnMethod('@Catch', target, propertyKey);
448
405
  providers.makeInjectable(target);
449
406
  errorTypes.forEach((type, index)=>{
450
- if (controller_decorator.isConstructor(type)) return;
407
+ if (controller_decorator.isConstructible(type)) return;
451
408
  const entry = ORDINALS[index] ? `${ORDINALS[index]} entry` : `entry ${index + 1}`;
452
- controller_decorator.warnDeprecatedBehaviour(logger$1, `${target.name}: @Catch's ${entry}, ${controller_decorator.describeValue(type)}, which matches no error,`, 'is refused', 'an error class, such as @Catch(CooldownError),');
409
+ controller_decorator.warnDeprecatedBehaviour(logger$2, `${target.name}: @Catch's ${entry}, ${controller_decorator.describeValue(type)}, which matches no error,`, 'is refused', 'an error class, such as @Catch(CooldownError),');
453
410
  });
454
411
  Reflect.defineMetadata(controller_decorator.META.catchTypes, errorTypes, target);
455
412
  });
456
413
  }
457
- const logger$1 = new controller_decorator.Logger('Catch');
414
+ const logger$2 = new controller_decorator.Logger('Catch');
458
415
  const ORDINALS = [
459
416
  'first',
460
417
  'second',
@@ -495,7 +452,7 @@ const ORDINALS = [
495
452
  * @see {@link https://meocord.dev/docs/4.2/exception-filters | Exception filters}
496
453
  */ function UseFilter(...filters) {
497
454
  return controller_decorator.declaring(function(target, propertyKey) {
498
- const where = propertyKey === undefined ? target.name : `${target.constructor.name}.${String(propertyKey)}`;
455
+ const where = controller_decorator.decoratedName(target, propertyKey);
499
456
  providers.assertStageEntries('@UseFilter', 'filter', where, filters);
500
457
  // Decorators apply bottom-up, so a higher decorator's filters are tried first.
501
458
  if (propertyKey === undefined) {
@@ -514,6 +471,7 @@ const ORDINALS = [
514
471
  });
515
472
  }
516
473
 
474
+ const logger$1 = new controller_decorator.Logger('MeoCord');
517
475
  /** Refuses a `messages` option of the wrong type where the app is declared, rather than at the first message. */ function assertMessageOptions(appName, messages, i18n) {
518
476
  if (!messages) return;
519
477
  const { prefix, mention, caseSensitive } = messages;
@@ -541,6 +499,9 @@ const ORDINALS = [
541
499
  if (deleteUsageRepliesAfter !== undefined && !(typeof deleteUsageRepliesAfter === 'number' && deleteUsageRepliesAfter >= 0 && Number.isFinite(deleteUsageRepliesAfter))) {
542
500
  throw controller_decorator.refuse(new TypeError(`${appName}: @MeoCord({ messages: { deleteUsageRepliesAfter } }) takes a number of seconds, or 0 to keep usage replies.`));
543
501
  }
502
+ if (deleteUsageRepliesAfter !== undefined && !providers.timerKeepsSeconds(deleteUsageRepliesAfter)) {
503
+ logger$1.warn(`${appName}: @MeoCord({ messages: { deleteUsageRepliesAfter } }) is ${deleteUsageRepliesAfter} seconds, longer than a timer ` + `can wait (${providers.MAX_TIMER_SECONDS}), so usage replies are kept. The next major version (5.0) refuses it.`);
504
+ }
544
505
  const { help } = messages;
545
506
  // One word each, since help is asked for by its first word
546
507
  const isWord = (value)=>typeof value === 'string' && /^\S+$/.test(value);
@@ -555,6 +516,35 @@ const ORDINALS = [
555
516
  if (type.labelKey !== undefined) assertLabelKey(appName, name, type.labelKey, i18n);
556
517
  }
557
518
  }
519
+ /**
520
+ * Refuses an option that takes a class given anything else, such as an object or an arrow function, where the app is
521
+ * declared: the startup checks read each class's prototype, and would otherwise fail on it without naming the option.
522
+ */ function assertClassOptions(appName, { presenter, cooldownStore, controllers, services }) {
523
+ for (const [name, value, implementing] of [
524
+ [
525
+ 'presenter',
526
+ presenter,
527
+ 'ResponsePresenter'
528
+ ],
529
+ [
530
+ 'cooldownStore',
531
+ cooldownStore,
532
+ 'CooldownStore'
533
+ ]
534
+ ]){
535
+ if (value !== undefined && !controller_decorator.isConstructor(value)) {
536
+ throw controller_decorator.refuse(new TypeError(`${appName}: @MeoCord({ ${name} }) takes a class implementing ${implementing}, not ${controller_decorator.describeValue(value)}.`));
537
+ }
538
+ }
539
+ for (const controller of controllers ?? []){
540
+ if (!controller_decorator.isConstructor(controller)) throw controller_decorator.refuse(new TypeError(`${appName}: @MeoCord({ controllers }) takes classes, not ${controller_decorator.describeValue(controller)}.`));
541
+ }
542
+ // A string or symbol is a token, which a provider has to provide; that is checked as the app is created
543
+ for (const service of services ?? []){
544
+ if (controller_decorator.isConstructor(service) || typeof service === 'string' || typeof service === 'symbol') continue;
545
+ throw controller_decorator.refuse(new TypeError(`${appName}: @MeoCord({ services }) takes classes, or tokens that providers provide, not ${controller_decorator.describeValue(service)}.`));
546
+ }
547
+ }
558
548
  /** Refuses a type's `labelKey` that no message of the app's default catalog has, as it would be shown as the key. */ function assertLabelKey(appName, name, labelKey, i18n) {
559
549
  const where = `${appName}: @MeoCord({ messages: { types } }): "${name}" has labelKey`;
560
550
  if (typeof labelKey !== 'string') throw controller_decorator.refuse(new TypeError(`${where} ${String(labelKey)}; it takes a message key, such as 'types.${name}'.`));
@@ -599,6 +589,7 @@ const ORDINALS = [
599
589
  */ function MeoCord(options) {
600
590
  return controller_decorator.declaring((target, propertyKey)=>{
601
591
  if (controller_decorator.deprecatedOnMethod('@MeoCord', target, propertyKey)) return;
592
+ assertClassOptions(target.name, options);
602
593
  providers.assertStageEntries('@MeoCord({ guards })', 'guard', target.name, options.guards ?? []);
603
594
  providers.assertStageEntries('@MeoCord({ interceptors })', 'interceptor', target.name, options.interceptors ?? []);
604
595
  providers.assertStageEntries('@MeoCord({ filters })', 'filter', target.name, options.filters ?? []);
@@ -693,15 +684,15 @@ const ORDINALS = [
693
684
  controller_decorator.refuseOnClass('@Validate', target, propertyKey);
694
685
  // Checked where it applies, so the refusal names the handler
695
686
  if (typeof schema?.['~standard']?.validate !== 'function') {
696
- throw controller_decorator.refuse(new Error(`${target.constructor.name}.${propertyKey}: @Validate takes a Standard Schema, such as a zod, valibot or arktype schema.`));
687
+ throw controller_decorator.refuse(new Error(`${controller_decorator.decoratedName(target, propertyKey)}: @Validate takes a Standard Schema, such as a zod, valibot or arktype schema.`));
697
688
  }
698
689
  if (Reflect.hasOwnMetadata(controller_decorator.META.methodValidation, target, propertyKey)) {
699
- throw controller_decorator.refuse(new Error(`${target.constructor.name}.${propertyKey}: more than one @Validate; one @Validate per handler: combine the schemas into one.`));
690
+ throw controller_decorator.refuse(new Error(`${controller_decorator.decoratedName(target, propertyKey)}: more than one @Validate; one @Validate per handler: combine the schemas into one.`));
700
691
  }
701
692
  const inlinePipes = Object.values(options.pipes ?? {}).flatMap((entries)=>Array.isArray(entries) ? entries : [
702
693
  entries
703
694
  ]);
704
- providers.assertStageEntries('@Validate', 'pipe', `${target.constructor.name}.${propertyKey}`, inlinePipes);
695
+ providers.assertStageEntries('@Validate', 'pipe', controller_decorator.decoratedName(target, propertyKey), inlinePipes);
705
696
  const metadata = {
706
697
  schema,
707
698
  pipes: options.pipes ?? {}
@@ -743,7 +734,7 @@ const ORDINALS = [
743
734
  */ function UsePipe(key, ...pipes) {
744
735
  return controller_decorator.declaring(function(target, propertyKey, _descriptor) {
745
736
  controller_decorator.refuseOnClass('@UsePipe', target, propertyKey);
746
- providers.assertStageEntries('@UsePipe', 'pipe', `${target.constructor.name}.${propertyKey}`, pipes);
737
+ providers.assertStageEntries('@UsePipe', 'pipe', controller_decorator.decoratedName(target, propertyKey), pipes);
747
738
  // Decorators apply bottom-up, so a higher @UsePipe's pipes go first, in the order they read.
748
739
  const existing = Reflect.getOwnMetadata(controller_decorator.META.methodPipes, target, propertyKey) ?? [];
749
740
  Reflect.defineMetadata(controller_decorator.META.methodPipes, [
@@ -891,7 +882,7 @@ const logger = new controller_decorator.Logger('Cooldown');
891
882
  controller_decorator.refuseOnClass('@Defer', target, propertyKey);
892
883
  const methodName = String(propertyKey);
893
884
  const kind = providers.nonInteractionHandler(target, methodName);
894
- if (kind) throw controller_decorator.refuse(providers.deferMisuseError(target.constructor.name, methodName, kind));
885
+ if (kind) throw controller_decorator.refuse(providers.deferMisuseError(controller_decorator.ownerName(target), methodName, kind));
895
886
  Reflect.defineMetadata(controller_decorator.META.deferOptions, {
896
887
  ...options
897
888
  }, target, methodName);
@@ -927,6 +918,9 @@ const logger = new controller_decorator.Logger('Cooldown');
927
918
  return controller_decorator.declaring((target, propertyKey, parameterIndex)=>{
928
919
  // A constructor parameter has no property key either, but has an index
929
920
  if (typeof parameterIndex !== 'number') controller_decorator.refuseOnClass('@Inject', target, propertyKey, 'a constructor parameter or a property');
921
+ else if (propertyKey !== undefined) {
922
+ throw controller_decorator.refuse(new Error(`${controller_decorator.decoratedName(target, propertyKey)}: @Inject goes on a constructor parameter or a property, not a method parameter.`));
923
+ }
930
924
  decorate(target, propertyKey, parameterIndex);
931
925
  });
932
926
  }
@@ -1007,8 +1001,9 @@ const logger = new controller_decorator.Logger('Cooldown');
1007
1001
  */ function UseTheme(theme) {
1008
1002
  return controller_decorator.declaring(function(target, propertyKey) {
1009
1003
  const onClass = propertyKey === undefined;
1010
- const owner = onClass ? target : target.constructor;
1011
- const where = `${owner.name}${onClass ? '' : `.${String(propertyKey)}`}: @UseTheme`;
1004
+ // The class it themes: the target itself on a class or a static member, else the target's constructor
1005
+ const owner = onClass || typeof target === 'function' ? target : target.constructor;
1006
+ const where = `${controller_decorator.decoratedName(target, propertyKey)}: @UseTheme`;
1012
1007
  controller_decorator.assertValidTheme(theme, where);
1013
1008
  const key = onClass ? controller_decorator.META.classTheme : controller_decorator.META.methodTheme;
1014
1009
  const existing = onClass ? Reflect.getOwnMetadata(key, target) : Reflect.getOwnMetadata(key, target, propertyKey);