meocord 4.2.2 → 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 (136) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/dist/cjs/_shared/{controller.decorator-U2NBWcoU.cjs → controller.decorator-hp5xdJU4.cjs} +512 -183
  3. package/dist/cjs/_shared/{startup-roots-lA5xkBFq.cjs → event-sources-DzqTODXg.cjs} +265 -80
  4. package/dist/cjs/_shared/{providers-B4MOFBrW.cjs → providers-BBvbnmLP.cjs} +780 -281
  5. package/dist/cjs/_shared/{sharded-cooldown-store-HTUb3Ii_.cjs → sharded-cooldown-store-B62g5O88.cjs} +11 -9
  6. package/dist/cjs/_shared/{translate-error-CbBHUu5P.cjs → translate-error-wsgfsUET.cjs} +1 -1
  7. package/dist/cjs/common/index.cjs +4 -104
  8. package/dist/cjs/core/index.cjs +432 -181
  9. package/dist/cjs/decorator/index.cjs +85 -120
  10. package/dist/cjs/testing/index.cjs +1664 -784
  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 +27 -5
  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 +17 -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 +83 -58
  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 +4 -4
  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 +44 -28
  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 +80 -53
  60. package/dist/esm/core/meocord.app.js +174 -89
  61. package/dist/esm/core/message-help.js +5 -9
  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 +2 -1
  66. package/dist/esm/core/process-state.js +19 -0
  67. package/dist/esm/core/providers.js +28 -3
  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/startup-roots.js +28 -8
  73. package/dist/esm/core/theme-resolvers.js +26 -11
  74. package/dist/esm/core/theme-runtime.js +23 -13
  75. package/dist/esm/core/theme-scope.js +27 -29
  76. package/dist/esm/core/theme-validation.js +1 -1
  77. package/dist/esm/decorator/app.decorator.js +6 -1
  78. package/dist/esm/decorator/command-builder.decorator.js +14 -0
  79. package/dist/esm/decorator/controller-class.decorator.js +1 -1
  80. package/dist/esm/decorator/controller.decorator.js +48 -19
  81. package/dist/esm/decorator/defer.decorator.js +2 -2
  82. package/dist/esm/decorator/event.decorator.js +4 -5
  83. package/dist/esm/decorator/filter.decorator.js +2 -2
  84. package/dist/esm/decorator/guard.decorator.js +4 -61
  85. package/dist/esm/decorator/inject.decorator.js +4 -1
  86. package/dist/esm/decorator/interceptor.decorator.js +2 -2
  87. package/dist/esm/decorator/theme.decorator.js +4 -3
  88. package/dist/esm/decorator/validation.decorator.js +5 -5
  89. package/dist/esm/package.json.js +1 -1
  90. package/dist/esm/testing/catalog.js +29 -13
  91. package/dist/esm/testing/cooldown-store-suite.js +3 -3
  92. package/dist/esm/testing/discord-defaults.js +449 -355
  93. package/dist/esm/testing/inspect-handler.js +91 -10
  94. package/dist/esm/testing/meocord-testing-module.js +195 -77
  95. package/dist/esm/testing/mock-fn.js +8 -2
  96. package/dist/esm/testing/mock-interaction.js +639 -113
  97. package/dist/esm/testing/response-log.js +8 -0
  98. package/dist/esm/testing/response.js +7 -5
  99. package/dist/esm/testing/routing.js +48 -6
  100. package/dist/esm/testing/strict-mocks.js +1 -0
  101. package/dist/esm/testing/theme.js +3 -2
  102. package/dist/esm/util/common.util.js +30 -9
  103. package/dist/esm/util/generator-cli.util.js +6 -4
  104. package/dist/esm/util/inherited-env.util.js +72 -13
  105. package/dist/esm/util/json.util.js +10 -3
  106. package/dist/esm/util/meocord-cli.util.js +3 -2
  107. package/dist/esm/util/meocord-config-loader.util.js +14 -1
  108. package/dist/esm/util/meocord-config-validation.util.js +13 -3
  109. package/dist/esm/util/meocord-source-config.util.js +13 -3
  110. package/dist/esm/util/platform.util.js +3 -2
  111. package/dist/esm/util/refusal.util.js +29 -11
  112. package/dist/esm/util/registration-mode.util.js +14 -1
  113. package/dist/esm/util/timer-limit.util.js +4 -1
  114. package/dist/esm/util/tsconfig-paths.util.js +38 -7
  115. package/dist/esm/util/tsconfig.util.js +16 -58
  116. package/dist/esm/util/user-text.util.js +8 -4
  117. package/dist/esm/util/value.util.js +15 -4
  118. package/dist/types/common/index.d.cts +13 -9
  119. package/dist/types/common/index.d.ts +13 -9
  120. package/dist/types/core/index.d.cts +21 -11
  121. package/dist/types/core/index.d.ts +21 -11
  122. package/dist/types/decorator/index.d.cts +63 -63
  123. package/dist/types/decorator/index.d.ts +63 -63
  124. package/dist/types/{index-DKJclIi-.d.ts → index-Brkd6QGL.d.ts} +81 -35
  125. package/dist/types/{index-Dlf0ZZtS.d.cts → index-CqXImwF_.d.cts} +81 -35
  126. package/dist/types/interface/index.d.cts +1 -1
  127. package/dist/types/interface/index.d.ts +1 -1
  128. package/dist/types/{route-Bh1U13yy.d.ts → route-IQrK-MN2.d.cts} +11 -3
  129. package/dist/types/{route-Bh1U13yy.d.cts → route-bI-okq7g.d.ts} +11 -3
  130. package/dist/types/testing/index.d.cts +137 -54
  131. package/dist/types/testing/index.d.ts +137 -54
  132. package/dist/types/{theme-resolvers-CyEz9pnv.d.cts → theme-resolvers-AtgeaYqF.d.cts} +1 -1
  133. package/dist/types/{theme-resolvers-DA204O-q.d.ts → theme-resolvers-BgqysV8d.d.ts} +1 -1
  134. package/dist/types/{translator-Bg7hwxDF.d.cts → translator-D8aydlCE.d.ts} +12 -11
  135. package/dist/types/{translator-CeP4bCzp.d.ts → translator-iJhDlgwL.d.cts} +12 -11
  136. package/package.json +5 -2
@@ -1,4 +1,7 @@
1
1
  /** The longest delay a timer keeps: 2^31 - 1 ms, about 24.8 days. Node fires a longer one at once. */ const MAX_TIMER_MS = 2147483647;
2
+ const MS_PER_SECOND = 1000;
3
+ /** The most whole seconds a timer keeps, as a message about an option given in seconds names the limit. */ const MAX_TIMER_SECONDS = Math.floor(MAX_TIMER_MS / MS_PER_SECOND);
4
+ /** Whether a timer keeps a delay given in seconds, a fraction of a second included. */ const timerKeepsSeconds = (seconds)=>seconds * MS_PER_SECOND <= MAX_TIMER_MS;
2
5
  /** A value as a refusal quotes it: a number as itself, `Infinity` and `NaN` included, a string in quotes. */ function quoted(value) {
3
6
  if (typeof value === 'string') return JSON.stringify(value);
4
7
  if (typeof value === 'number' || typeof value === 'boolean' || value === null) return String(value);
@@ -12,4 +15,4 @@
12
15
  return `must be a number of milliseconds ${allowZero ? '0 or more' : 'above 0'}, at most ${max} (got ${quoted(value)})`;
13
16
  }
14
17
 
15
- export { MAX_TIMER_MS, timeoutProblem };
18
+ export { MAX_TIMER_MS, MAX_TIMER_SECONDS, timeoutProblem, timerKeepsSeconds };
@@ -11,13 +11,15 @@ const STAR = '__meocord_paths_star__';
11
11
  */ function projectPaths(cwd = process.cwd()) {
12
12
  const configPath = path.join(cwd, 'tsconfig.json');
13
13
  if (!existsSync(configPath)) return undefined;
14
- const tsconfig = {
15
- path: configPath,
16
- config: parseTsconfig(configPath)
17
- };
18
- const paths = tsconfig.config.compilerOptions?.paths;
14
+ return absolutePaths(configPath, parseTsconfig(configPath));
15
+ }
16
+ /** A parsed tsconfig's `paths`, each target absolute, as {@link projectPaths} gives them. */ function absolutePaths(configPath, config) {
17
+ const paths = config.compilerOptions?.paths;
19
18
  // The matcher resolves each target from the file that declares it, or from baseUrl, as tsc does
20
- const match = createPathsMatcher(tsconfig);
19
+ const match = createPathsMatcher({
20
+ path: configPath,
21
+ config
22
+ });
21
23
  if (!paths || !match) return undefined;
22
24
  // Resolved, so each is the platform's own absolute path: get-tsconfig writes Windows paths with forward slashes
23
25
  return Object.fromEntries(Object.keys(paths).map((alias)=>[
@@ -25,5 +27,34 @@ const STAR = '__meocord_paths_star__';
25
27
  match(alias.replace('*', STAR)).map((target)=>path.resolve(target).replace(STAR, '*'))
26
28
  ]));
27
29
  }
30
+ /** The top-level lists of files and globs. */ const FILE_LISTS = [
31
+ 'include',
32
+ 'exclude',
33
+ 'files'
34
+ ];
35
+ /**
36
+ * The project's tsconfig as the bundler resolves modules with it, written as one file with no `extends`: `baseUrl`
37
+ * and `paths` as `tsc` reads them through `extends` (`paths` as {@link projectPaths} gives them), and `include`,
38
+ * `exclude` and `files`, each absolute, from the file that declares it and with `${configDir}` as the project. Rspack's
39
+ * resolver reads no other compiler option, so no other is written: one inferred by a TypeScript other than the
40
+ * project's can't reach the copy. Undefined when there is no tsconfig.json.
41
+ */ function resolutionTsconfig(cwd = process.cwd()) {
42
+ const configPath = path.join(cwd, 'tsconfig.json');
43
+ if (!existsSync(configPath)) return undefined;
44
+ const resolved = parseTsconfig(configPath);
45
+ const absolute = (entry)=>path.resolve(cwd, String(entry));
46
+ const tsconfig = {
47
+ compilerOptions: {}
48
+ };
49
+ const baseUrl = resolved.compilerOptions?.baseUrl;
50
+ if (typeof baseUrl === 'string') tsconfig.compilerOptions.baseUrl = absolute(baseUrl);
51
+ const paths = absolutePaths(configPath, resolved);
52
+ if (paths) tsconfig.compilerOptions.paths = paths;
53
+ for (const key of FILE_LISTS){
54
+ const value = resolved[key];
55
+ if (Array.isArray(value)) tsconfig[key] = value.map(absolute);
56
+ }
57
+ return tsconfig;
58
+ }
28
59
 
29
- export { projectPaths };
60
+ export { projectPaths, resolutionTsconfig };
@@ -1,25 +1,9 @@
1
1
  import path from 'path';
2
2
  import { existsSync, readFileSync, writeFileSync, mkdtempSync, rmSync, readlinkSync, readdirSync } from 'fs';
3
3
  import { tmpdir, hostname } from 'os';
4
- import { createRequire } from 'module';
5
4
  import { parseJsonc } from './json.util.js';
6
- import { projectPaths } from './tsconfig-paths.util.js';
5
+ import { resolutionTsconfig } from './tsconfig-paths.util.js';
7
6
 
8
- /**
9
- * An `extends` made to work from another directory: a relative path made absolute, and a package
10
- * resolved from the project's `node_modules`, or kept as it is when it cannot be found there.
11
- */ function resolveExtends(value, cwd) {
12
- const projectRequire = createRequire(path.join(cwd, 'tsconfig.json'));
13
- const resolve = (entry)=>{
14
- if (entry.startsWith('.') || path.isAbsolute(entry)) return path.resolve(cwd, entry);
15
- try {
16
- return projectRequire.resolve(entry);
17
- } catch {
18
- return entry;
19
- }
20
- };
21
- return Array.isArray(value) ? value.map(resolve) : resolve(value);
22
- }
23
7
  // The directory this process writes its copies in, and how many it has written there
24
8
  let copiesDir;
25
9
  let copies = 0;
@@ -89,12 +73,11 @@ function isRunning(pid) {
89
73
  return dir;
90
74
  }
91
75
  /**
92
- * Writes a copy of the project's tsconfig.json for the bundler and returns its path: comments and trailing commas
93
- * removed, paths made absolute (`paths` as {@link projectPaths} reads them), `noEmit` dropped. One file per call in this
94
- * process's directory, removed at exit; the project's file is never written.
76
+ * Writes a copy of the project's tsconfig.json for the bundler and returns its path. The copy is for module resolution
77
+ * only: what {@link resolutionTsconfig} gives, with no `extends`, and the project's own references. One file per call
78
+ * in this process's directory, removed at exit; the project's file is never written.
95
79
  * @throws When `tsconfig.json` is missing or cannot be parsed.
96
80
  */ function prepareModifiedTsConfig() {
97
- var _parsedConfig;
98
81
  const tsConfigPath = path.resolve(process.cwd(), 'tsconfig.json');
99
82
  // Ensure tsconfig.json exists
100
83
  if (!existsSync(tsConfigPath)) {
@@ -108,46 +91,21 @@ function isRunning(pid) {
108
91
  } catch (error) {
109
92
  throw new Error(`Could not parse tsconfig.json in ${process.cwd()}: ${error instanceof Error ? error.message : String(error)}. ` + `Fix the JSON, then build again.`);
110
93
  }
111
- // The copy lives in the temp directory, so every path in it is made absolute from the project
94
+ // The copy lives in the temp directory and is for module resolution alone: what the bundler resolves with, every path
95
+ // absolute and no `extends` left, since the bundler's own lookup of one can't read a Windows absolute path
112
96
  const cwd = process.cwd();
113
- if (parsedConfig?.extends) parsedConfig.extends = resolveExtends(parsedConfig.extends, cwd);
114
- // `${configDir}` is the project, which the copy's own directory would otherwise stand for
115
- for (const key of [
116
- 'include',
117
- 'exclude',
118
- 'files'
119
- ]){
120
- if (Array.isArray(parsedConfig?.[key])) {
121
- parsedConfig[key] = parsedConfig[key].map((p)=>path.resolve(cwd, p.replaceAll('${configDir}', cwd)));
122
- }
123
- }
124
- // Process compilerOptions
125
- if (parsedConfig?.compilerOptions) {
126
- const pathOptions = [
127
- 'outDir',
128
- 'rootDir',
129
- 'baseUrl',
130
- 'tsBuildInfoFile'
131
- ];
132
- // Convert relative paths to absolute paths in `compilerOptions`
133
- pathOptions.forEach((option)=>{
134
- if (parsedConfig.compilerOptions[option]) {
135
- parsedConfig.compilerOptions[option] = path.resolve(process.cwd(), parsedConfig.compilerOptions[option]);
136
- }
137
- });
138
- if (Array.isArray(parsedConfig.compilerOptions.typeRoots)) {
139
- parsedConfig.compilerOptions.typeRoots = parsedConfig.compilerOptions.typeRoots.map((p)=>path.resolve(cwd, p));
140
- }
141
- // Remove `noEmit` option if it exists
142
- if ('noEmit' in parsedConfig.compilerOptions) {
143
- delete parsedConfig.compilerOptions.noEmit;
144
- }
97
+ const copy = {
98
+ ...resolutionTsconfig(cwd)
99
+ };
100
+ // Project references, which tsc reads from this file alone and resolves from the project, `${configDir}` as written
101
+ if (Array.isArray(parsedConfig?.references)) {
102
+ copy.references = parsedConfig.references.map((reference)=>typeof reference?.path === 'string' ? {
103
+ ...reference,
104
+ path: path.resolve(cwd, reference.path)
105
+ } : reference);
145
106
  }
146
- // The paths as tsc reads them, inherited ones included, written absolute so the copy in the temp directory keeps them
147
- const paths = projectPaths(cwd);
148
- if (paths) ((_parsedConfig = parsedConfig).compilerOptions ?? (_parsedConfig.compilerOptions = {})).paths = paths;
149
107
  const tempTsConfigPath = path.join(copiesDirectory(), `modified-tsconfig-${++copies}.json`);
150
- writeFileSync(tempTsConfigPath, JSON.stringify(parsedConfig, null, 2));
108
+ writeFileSync(tempTsConfigPath, JSON.stringify(copy, null, 2));
151
109
  return tempTsConfigPath;
152
110
  }
153
111
 
@@ -1,7 +1,7 @@
1
1
  import { escapeMarkdown } from 'discord.js';
2
2
 
3
- /** Characters a log line shows escaped: C0 and C1 controls, DEL, line and paragraph separators, and bidi controls. */ const UNSAFE_IN_LOG = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g;
4
- /** The same, with the quote and the backslash, for text shown between quotes. */ const UNSAFE_IN_QUOTES = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069"\\]/g;
3
+ /** Characters a log line shows escaped: C0 and C1 controls, DEL, line and paragraph separators, and bidi controls. */ const UNSAFE_IN_LOG = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\p{Bidi_Control}]/gu;
4
+ /** The same, with the quote and the backslash, for text shown between quotes. */ const UNSAFE_IN_QUOTES = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\p{Bidi_Control}"\\]/gu;
5
5
  const SHORT_ESCAPES = {
6
6
  '\n': '\\n',
7
7
  '\r': '\\r',
@@ -25,8 +25,12 @@ const escapeChar = (char)=>SHORT_ESCAPES[char] ?? `\\u${char.charCodeAt(0).toStr
25
25
  if (characters.length <= LOGGED_CHARACTERS) return quoted(text);
26
26
  return `${quoted(`${characters.slice(0, LOGGED_CHARACTERS).join('')}…`)} (${characters.length} characters)`;
27
27
  }
28
- /** Words a user wrote, as a reply quotes them: on one line, with their markdown, masked links included, shown as typed. */ function userWords(text) {
29
- return escapeMarkdown(text.replace(/\s+/g, ' ').trim(), {
28
+ /**
29
+ * Words a user wrote, as a reply quotes them: on one line, with their markdown, masked links included, shown as typed.
30
+ * Each run of whitespace reads as one space, and the spaces around the words are kept, since a word in quotes can
31
+ * carry them and be refused for them.
32
+ */ function userWords(text) {
33
+ return escapeMarkdown(text.replace(/\s+/g, ' '), {
30
34
  maskedLink: true
31
35
  });
32
36
  }
@@ -26,15 +26,26 @@ import { quoteForLog } from './user-text.util.js';
26
26
  }
27
27
  /** A class's name after its article, such as `an AutocompleteInteraction`; a `U` name reads as "you", so takes `a`. */ const withArticle = (name)=>`${/^[AEIO]/i.test(name) ? 'an' : 'a'} ${name}`;
28
28
  /**
29
- * Sets `key` on `target` as an own, enumerable data property: unlike `target[key] = value`, a key named `__proto__`
30
- * is a key like any other rather than the object's prototype.
29
+ * Sets `key` on a plain object `target` as an own, enumerable data property: unlike `target[key] = value`, a key
30
+ * named `__proto__` is a key like any other rather than the object's prototype, and a key such as `toString` is set
31
+ * even where `Object.prototype` is frozen.
31
32
  */ function setOwn(target, key, value) {
32
- Object.defineProperty(target, key, {
33
+ // An assignment would call __proto__'s setter, and throw for a member of a frozen Object.prototype. Any other key
34
+ // assigned is an own data property already; defining each would cost V8 its fast object shape
35
+ if (key in Object.prototype) Object.defineProperty(target, key, {
33
36
  value,
34
37
  enumerable: true,
35
38
  writable: true,
36
39
  configurable: true
37
40
  });
41
+ else target[key] = value;
42
+ }
43
+ /** `target`'s own value for `key`, never one it inherits, such as `Object.prototype.toString` for `toString`. */ const ownValue = (target, key)=>Object.hasOwn(target, key) ? target[key] : undefined;
44
+ /**
45
+ * Gives each of `keys`, names `Object.prototype` has such as `toString` or `constructor`, that `target` doesn't hold
46
+ * an own `undefined`, so reading one left out gives `undefined` rather than the inherited member.
47
+ */ function shadowInherited(target, keys) {
48
+ for (const key of keys)if (!Object.hasOwn(target, key)) setOwn(target, key, undefined);
38
49
  }
39
50
 
40
- export { describeValue, isConstructible, isConstructor, setOwn, withArticle };
51
+ export { describeValue, isConstructible, isConstructor, ownValue, setOwn, shadowInherited, withArticle };
@@ -1,11 +1,11 @@
1
1
  import { ColorResolvable, Interaction, Message, Locale } from 'discord.js';
2
- import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-Dlf0ZZtS.cjs';
2
+ import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-CqXImwF_.cjs';
3
3
  import { Token, ProviderToken, FactoryProvider, DeepReadonly, MeoCordTheme } from '../interface/index.cjs';
4
- export { e as CommandNotFoundError, f as CooldownError, g as CooldownScope, h as CooldownStoreError, E as ExecutionContext, i as ExecutionContextType, G as GuardDeniedError, I as InstallContext, j as MemoryCooldownStore, k as MessageUsageError, l as MessageUsageIssue, m as MetadataDecorator, R as ResponseCall, n as ResponseEditFlags, o as ResponseEditPayload, p as ResponseErrorOptions, q as ResponseFlags, r as ResponseLockOptions, s as ResponsePayload, t as ResponsePhase, u as ResponseSendOptions, v as ResponseState, U as UserError, w as UserErrorOptions, V as ValidationError, x as ValidationIssue, y as cooldownMessage, z as cooldownStoreMessage, A as createMetadata, B as getInstallContext, H as respond } from '../index-Dlf0ZZtS.cjs';
5
- export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-Bh1U13yy.cjs';
6
- import { T as Translator } from '../translator-Bg7hwxDF.cjs';
7
- export { C as CatalogDefinition, a as CatalogShape, L as LocaleCatalog, b as LocalizationKey, M as MessageKey, c as MessageParams, P as PluralCategory, d as PluralMessage, S as StringMessageKey, e as Translate, f as TranslatorOptions, g as createTranslator, h as defineCatalog } from '../translator-Bg7hwxDF.cjs';
8
- export { T as ThemeCache } from '../theme-resolvers-CyEz9pnv.cjs';
4
+ export { e as CommandNotFoundError, f as CooldownError, g as CooldownScope, h as CooldownStoreError, E as ExecutionContext, i as ExecutionContextType, G as GuardDeniedError, I as InstallContext, j as MemoryCooldownStore, k as MessageUsageError, l as MessageUsageIssue, m as MetadataDecorator, R as ResponseCall, n as ResponseEditFlags, o as ResponseEditPayload, p as ResponseErrorOptions, q as ResponseFlags, r as ResponseLockOptions, s as ResponsePayload, t as ResponsePhase, u as ResponseSendOptions, v as ResponseState, U as UserError, w as UserErrorOptions, V as ValidationError, x as ValidationIssue, y as cooldownMessage, z as cooldownStoreMessage, A as createMetadata, B as getInstallContext, H as respond } from '../index-CqXImwF_.cjs';
5
+ export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-IQrK-MN2.cjs';
6
+ import { T as Translator } from '../translator-iJhDlgwL.cjs';
7
+ export { C as CatalogDefinition, a as CatalogShape, L as LocaleCatalog, b as LocalizationKey, M as MessageKey, c as MessageParams, P as PluralCategory, d as PluralMessage, S as StringMessageKey, e as Translate, f as TranslatorOptions, g as createTranslator, h as defineCatalog } from '../translator-iJhDlgwL.cjs';
8
+ export { T as ThemeCache } from '../theme-resolvers-AtgeaYqF.cjs';
9
9
  import '@rsbuild/core';
10
10
  import '../controller.enum-DTDn69N9.cjs';
11
11
 
@@ -532,7 +532,10 @@ declare function translateError(error: unknown, t: Translator<any>, target: Inte
532
532
  *
533
533
  * Use it where a bot logs a startup failure, so an error MeoCord explained is not logged twice: an app
534
534
  * `MeoCordFactory.create()` refuses, such as two handlers for one command, or Discord refusing the bot token or a
535
- * privileged intent at login. The error is otherwise unchanged.
535
+ * privileged intent at login. A built bot has `process.exitCode` set for such an error already, and a start that a stop
536
+ * ended, which is explained too, leaves it as it was, so the process exits 0. Outside a build, as a script run by hand,
537
+ * `MeoCordFactory.create()` sets no code for an app it refuses, so a catch that returns early for an explained error,
538
+ * as below, sets `process.exitCode` itself there. The error is otherwise unchanged.
536
539
  *
537
540
  * @param error - An error `MeoCordFactory.create()` threw or `app.start()` rejected with.
538
541
  * @returns `true` when MeoCord explained the error.
@@ -548,9 +551,10 @@ declare function translateError(error: unknown, t: Translator<any>, target: Inte
548
551
  * await app.start()
549
552
  * }
550
553
  *
551
- * // An error MeoCord explained, such as an app it refuses or a token Discord refused, is already logged
554
+ * // An error MeoCord explained, such as an app it refuses or a token Discord refused, is already logged, with the exit code set
552
555
  * bootstrap().catch(error => {
553
- * if (!isExplainedError(error)) logger.error('Error during startup:', error)
556
+ * if (isExplainedError(error)) return
557
+ * logger.error('Error during startup:', error)
554
558
  * process.exitCode = 1
555
559
  * })
556
560
  * ```
@@ -1,11 +1,11 @@
1
1
  import { ColorResolvable, Interaction, Message, Locale } from 'discord.js';
2
- import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-DKJclIi-.js';
2
+ import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-Brkd6QGL.js';
3
3
  import { Token, ProviderToken, FactoryProvider, DeepReadonly, MeoCordTheme } from '../interface/index.js';
4
- export { e as CommandNotFoundError, f as CooldownError, g as CooldownScope, h as CooldownStoreError, E as ExecutionContext, i as ExecutionContextType, G as GuardDeniedError, I as InstallContext, j as MemoryCooldownStore, k as MessageUsageError, l as MessageUsageIssue, m as MetadataDecorator, R as ResponseCall, n as ResponseEditFlags, o as ResponseEditPayload, p as ResponseErrorOptions, q as ResponseFlags, r as ResponseLockOptions, s as ResponsePayload, t as ResponsePhase, u as ResponseSendOptions, v as ResponseState, U as UserError, w as UserErrorOptions, V as ValidationError, x as ValidationIssue, y as cooldownMessage, z as cooldownStoreMessage, A as createMetadata, B as getInstallContext, H as respond } from '../index-DKJclIi-.js';
5
- export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-Bh1U13yy.js';
6
- import { T as Translator } from '../translator-CeP4bCzp.js';
7
- export { C as CatalogDefinition, a as CatalogShape, L as LocaleCatalog, b as LocalizationKey, M as MessageKey, c as MessageParams, P as PluralCategory, d as PluralMessage, S as StringMessageKey, e as Translate, f as TranslatorOptions, g as createTranslator, h as defineCatalog } from '../translator-CeP4bCzp.js';
8
- export { T as ThemeCache } from '../theme-resolvers-DA204O-q.js';
4
+ export { e as CommandNotFoundError, f as CooldownError, g as CooldownScope, h as CooldownStoreError, E as ExecutionContext, i as ExecutionContextType, G as GuardDeniedError, I as InstallContext, j as MemoryCooldownStore, k as MessageUsageError, l as MessageUsageIssue, m as MetadataDecorator, R as ResponseCall, n as ResponseEditFlags, o as ResponseEditPayload, p as ResponseErrorOptions, q as ResponseFlags, r as ResponseLockOptions, s as ResponsePayload, t as ResponsePhase, u as ResponseSendOptions, v as ResponseState, U as UserError, w as UserErrorOptions, V as ValidationError, x as ValidationIssue, y as cooldownMessage, z as cooldownStoreMessage, A as createMetadata, B as getInstallContext, H as respond } from '../index-Brkd6QGL.js';
5
+ export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-bI-okq7g.js';
6
+ import { T as Translator } from '../translator-D8aydlCE.js';
7
+ export { C as CatalogDefinition, a as CatalogShape, L as LocaleCatalog, b as LocalizationKey, M as MessageKey, c as MessageParams, P as PluralCategory, d as PluralMessage, S as StringMessageKey, e as Translate, f as TranslatorOptions, g as createTranslator, h as defineCatalog } from '../translator-D8aydlCE.js';
8
+ export { T as ThemeCache } from '../theme-resolvers-BgqysV8d.js';
9
9
  import '@rsbuild/core';
10
10
  import '../controller.enum-DTDn69N9.js';
11
11
 
@@ -532,7 +532,10 @@ declare function translateError(error: unknown, t: Translator<any>, target: Inte
532
532
  *
533
533
  * Use it where a bot logs a startup failure, so an error MeoCord explained is not logged twice: an app
534
534
  * `MeoCordFactory.create()` refuses, such as two handlers for one command, or Discord refusing the bot token or a
535
- * privileged intent at login. The error is otherwise unchanged.
535
+ * privileged intent at login. A built bot has `process.exitCode` set for such an error already, and a start that a stop
536
+ * ended, which is explained too, leaves it as it was, so the process exits 0. Outside a build, as a script run by hand,
537
+ * `MeoCordFactory.create()` sets no code for an app it refuses, so a catch that returns early for an explained error,
538
+ * as below, sets `process.exitCode` itself there. The error is otherwise unchanged.
536
539
  *
537
540
  * @param error - An error `MeoCordFactory.create()` threw or `app.start()` rejected with.
538
541
  * @returns `true` when MeoCord explained the error.
@@ -548,9 +551,10 @@ declare function translateError(error: unknown, t: Translator<any>, target: Inte
548
551
  * await app.start()
549
552
  * }
550
553
  *
551
- * // An error MeoCord explained, such as an app it refuses or a token Discord refused, is already logged
554
+ * // An error MeoCord explained, such as an app it refuses or a token Discord refused, is already logged, with the exit code set
552
555
  * bootstrap().catch(error => {
553
- * if (!isExplainedError(error)) logger.error('Error during startup:', error)
556
+ * if (isExplainedError(error)) return
557
+ * logger.error('Error during startup:', error)
554
558
  * process.exitCode = 1
555
559
  * })
556
560
  * ```
@@ -1,5 +1,5 @@
1
1
  import { ServiceIdentifier } from 'inversify';
2
- import { J as MeoCordApplication } from '../index-Dlf0ZZtS.cjs';
2
+ import { J as MeoCordApplication } from '../index-CqXImwF_.cjs';
3
3
  import { MetadataDecorator } from '../common/index.cjs';
4
4
  import { MessageScope, MessageCommandOptions, MessageHelp } from '../interface/index.cjs';
5
5
  import { RESTPostAPIApplicationCommandsJSONBody, ClientEvents, Message, Client } from 'discord.js';
@@ -172,7 +172,10 @@ interface MessageHandlerEntry extends HandlerEntryBase {
172
172
  aliases: readonly string[];
173
173
  /** What the command does, from its `description` option. */
174
174
  description: string | undefined;
175
- /** Where the command works: its `scope` option, narrowed to servers by a `member`, `role` or `channel` param or flag. */
175
+ /**
176
+ * Where the command works: its `scope` option, narrowed to servers by a `member`, `role` or `channel` param, or
177
+ * required flag.
178
+ */
176
179
  scope: MessageScope;
177
180
  /** Whether its `hidden` option leaves it out of help's lists, including a parent's list of subcommands. */
178
181
  hidden: boolean;
@@ -269,16 +272,19 @@ declare class HandlerRegistry {
269
272
  private readonly classes;
270
273
  private readonly messages;
271
274
  private readonly translator;
275
+ private readonly controllers;
272
276
  private entries?;
273
- /** Built from `classes` for a registry no dispatcher shares its routes with. */
277
+ /** Built from the controllers for a registry no dispatcher shares its routes with yet. */
274
278
  private messageRoutes?;
275
279
  /**
276
- * @param classes - The app's classes to read handlers from. The factory fills the list
277
- * once the app is bound; entries are read on the first {@link list}.
280
+ * @param classes - The app's classes to read handlers from, controllers and services alike. The factory fills the
281
+ * list once the app is bound; entries are read on the first {@link list}.
278
282
  * @param messages - The app's `messages` options, whose `caseSensitive` message entries' `matches` follows.
279
283
  * @param translator - The app's translator, read when {@link messageHelp} words its labels; none for English.
284
+ * @param controllers - The app's controllers, the classes dispatch routes messages to, which {@link messageHelp}
285
+ * lists; `classes` when not given.
280
286
  */
281
- constructor(classes: readonly HandlerClass[], messages?: MessageCommandOptions, translator?: () => Translator<any> | undefined);
287
+ constructor(classes: readonly HandlerClass[], messages?: MessageCommandOptions, translator?: () => Translator<any> | undefined, controllers?: readonly HandlerClass[]);
282
288
  /**
283
289
  * Lists the registered handlers.
284
290
  *
@@ -364,6 +370,10 @@ type JsonObject<T> = {
364
370
  *
365
371
  * A `BigInt` cannot be written, so it is `never`.
366
372
  *
373
+ * An object arrives with its own enumerable fields only. A class's getters are left out, yet stay in this type, as
374
+ * TypeScript can't tell a getter from a field: return a plain object, or give the class a `toJSON`, for a value
375
+ * with getters.
376
+ *
367
377
  * @typeParam T - The value before it is sent.
368
378
  * @group Types
369
379
  * @see {@link ShardContext.call}
@@ -446,11 +456,11 @@ declare class ShardContext {
446
456
  *
447
457
  * With process sharding that is one result per shard; with one process, a single result listing every
448
458
  * shard. The arguments and the result pass as JSON in every mode, one process and tests included, so a
449
- * `Date` arrives as a string and a `Map` as `{}` wherever it runs, and a value JSON cannot carry, such as a
450
- * `BigInt`, fails the call. A method whose params JSON would change, such as one taking a `Date`, cannot be called:
451
- * the argument is refused, naming what to declare instead. An `undefined` argument arrives as `undefined`. A process
452
- * that throws, lacks the service, or does not answer within 10 seconds gives an error result instead of failing the
453
- * others.
459
+ * `Date` arrives as a string, a `Map` as `{}` and a class instance as its own fields, without its getters, wherever
460
+ * it runs, and a value JSON cannot carry, such as a `BigInt`, fails the call. A method whose params JSON would
461
+ * change, such as one taking a `Date`, cannot be called: the argument is refused, naming what to declare instead. An
462
+ * `undefined` argument arrives as `undefined`. A process that throws, lacks the service, or does not answer within
463
+ * 10 seconds gives an error result instead of failing the others.
454
464
  *
455
465
  * @param service - The controller, the service, or a class a provider stands in for; each process resolves its own
456
466
  * instance.
@@ -1,5 +1,5 @@
1
1
  import { ServiceIdentifier } from 'inversify';
2
- import { J as MeoCordApplication } from '../index-DKJclIi-.js';
2
+ import { J as MeoCordApplication } from '../index-Brkd6QGL.js';
3
3
  import { MetadataDecorator } from '../common/index.js';
4
4
  import { MessageScope, MessageCommandOptions, MessageHelp } from '../interface/index.js';
5
5
  import { RESTPostAPIApplicationCommandsJSONBody, ClientEvents, Message, Client } from 'discord.js';
@@ -172,7 +172,10 @@ interface MessageHandlerEntry extends HandlerEntryBase {
172
172
  aliases: readonly string[];
173
173
  /** What the command does, from its `description` option. */
174
174
  description: string | undefined;
175
- /** Where the command works: its `scope` option, narrowed to servers by a `member`, `role` or `channel` param or flag. */
175
+ /**
176
+ * Where the command works: its `scope` option, narrowed to servers by a `member`, `role` or `channel` param, or
177
+ * required flag.
178
+ */
176
179
  scope: MessageScope;
177
180
  /** Whether its `hidden` option leaves it out of help's lists, including a parent's list of subcommands. */
178
181
  hidden: boolean;
@@ -269,16 +272,19 @@ declare class HandlerRegistry {
269
272
  private readonly classes;
270
273
  private readonly messages;
271
274
  private readonly translator;
275
+ private readonly controllers;
272
276
  private entries?;
273
- /** Built from `classes` for a registry no dispatcher shares its routes with. */
277
+ /** Built from the controllers for a registry no dispatcher shares its routes with yet. */
274
278
  private messageRoutes?;
275
279
  /**
276
- * @param classes - The app's classes to read handlers from. The factory fills the list
277
- * once the app is bound; entries are read on the first {@link list}.
280
+ * @param classes - The app's classes to read handlers from, controllers and services alike. The factory fills the
281
+ * list once the app is bound; entries are read on the first {@link list}.
278
282
  * @param messages - The app's `messages` options, whose `caseSensitive` message entries' `matches` follows.
279
283
  * @param translator - The app's translator, read when {@link messageHelp} words its labels; none for English.
284
+ * @param controllers - The app's controllers, the classes dispatch routes messages to, which {@link messageHelp}
285
+ * lists; `classes` when not given.
280
286
  */
281
- constructor(classes: readonly HandlerClass[], messages?: MessageCommandOptions, translator?: () => Translator<any> | undefined);
287
+ constructor(classes: readonly HandlerClass[], messages?: MessageCommandOptions, translator?: () => Translator<any> | undefined, controllers?: readonly HandlerClass[]);
282
288
  /**
283
289
  * Lists the registered handlers.
284
290
  *
@@ -364,6 +370,10 @@ type JsonObject<T> = {
364
370
  *
365
371
  * A `BigInt` cannot be written, so it is `never`.
366
372
  *
373
+ * An object arrives with its own enumerable fields only. A class's getters are left out, yet stay in this type, as
374
+ * TypeScript can't tell a getter from a field: return a plain object, or give the class a `toJSON`, for a value
375
+ * with getters.
376
+ *
367
377
  * @typeParam T - The value before it is sent.
368
378
  * @group Types
369
379
  * @see {@link ShardContext.call}
@@ -446,11 +456,11 @@ declare class ShardContext {
446
456
  *
447
457
  * With process sharding that is one result per shard; with one process, a single result listing every
448
458
  * shard. The arguments and the result pass as JSON in every mode, one process and tests included, so a
449
- * `Date` arrives as a string and a `Map` as `{}` wherever it runs, and a value JSON cannot carry, such as a
450
- * `BigInt`, fails the call. A method whose params JSON would change, such as one taking a `Date`, cannot be called:
451
- * the argument is refused, naming what to declare instead. An `undefined` argument arrives as `undefined`. A process
452
- * that throws, lacks the service, or does not answer within 10 seconds gives an error result instead of failing the
453
- * others.
459
+ * `Date` arrives as a string, a `Map` as `{}` and a class instance as its own fields, without its getters, wherever
460
+ * it runs, and a value JSON cannot carry, such as a `BigInt`, fails the call. A method whose params JSON would
461
+ * change, such as one taking a `Date`, cannot be called: the argument is refused, naming what to declare instead. An
462
+ * `undefined` argument arrives as `undefined`. A process that throws, lacks the service, or does not answer within
463
+ * 10 seconds gives an error result instead of failing the others.
454
464
  *
455
465
  * @param service - The controller, the service, or a class a provider stands in for; each process resolves its own
456
466
  * instance.
@@ -1,12 +1,68 @@
1
1
  import { CooldownStore } from '../common/index.cjs';
2
2
  import { BuildableCommandType, CommandBuilderOptions, CommandBuilderBase, PIPED_BRAND, CommandBuilderConstructor, CommandInteractionType, MessageHandlerOptions, CheckedParams, ReactionHandlerSettings, ReactionEvent, ControllerOptions, StageParams, GuardOptions, GuardInterface, InterceptorOptions, InterceptorInterface, ExceptionFilter, Provider, CooldownStoreFailure, ResponsePresenter, MessageCommandOptions, DispatchObserver, RootTheme, ThemeResolvers, ThemeResolver, PipeInterface, StandardSchemaV1, ValidatePipes, ValidateOptions, InferSchemaOutput, CooldownOptions, ProviderToken, ObserverOptions, ThemeOverride } from '../interface/index.cjs';
3
- import { AutocompleteInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, ChannelSelectMenuInteraction, MentionableSelectMenuInteraction, OmitPartialGroupDMChannel, Message, MessageReaction, PartialMessageReaction, ClientEvents, ClientOptions, ActivityOptions } from 'discord.js';
3
+ import { ClientEvents, AutocompleteInteraction, UserContextMenuCommandInteraction, MessageContextMenuCommandInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, ChannelSelectMenuInteraction, MentionableSelectMenuInteraction, OmitPartialGroupDMChannel, Message, MessageReaction, PartialMessageReaction, ClientOptions, ActivityOptions } from 'discord.js';
4
4
  import { CommandType } from '../enum/index.cjs';
5
5
  import { Route, RouteParams, RouteValues, RouteValue } from '../common/index.cjs';
6
6
  import { ServiceIdentifier } from 'inversify';
7
7
  import { Translator } from '../common/index.cjs';
8
8
  import '@rsbuild/core';
9
9
 
10
+ /**
11
+ * Handles a discord.js client event every time it is emitted, on a controller or a service.
12
+ *
13
+ * Use it for gateway events MeoCord has no handler type for, such as a member joining or a channel being
14
+ * created. For commands, components, messages and reactions, use {@link Command}, `@MessageHandler` or
15
+ * `@ReactionHandler`, which route them.
16
+ *
17
+ * @remarks
18
+ * The handler's parameters are typed from discord.js's `ClientEvents`. It runs through the pipeline, so the app's
19
+ * global guards apply to it, and its class's and its own, and an error it throws is logged without stopping the bot. Handling `interactionCreate`
20
+ * or `messageCreate` here runs alongside MeoCord's own dispatch of them.
21
+ *
22
+ * @param event - The client event to handle, such as `'guildMemberAdd'`.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * @On('guildMemberAdd')
27
+ * async greet(member: GuildMember) {
28
+ * await member.send(`Welcome to ${member.guild.name}!`)
29
+ * }
30
+ * ```
31
+ *
32
+ * @pipeline handler with the guards, interceptors and filters that apply to events: the app's global ones, its class's and its own
33
+ * @group Decorators
34
+ * @category Handlers
35
+ * @see {@link Once}
36
+ * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
37
+ */
38
+ declare function On<E extends keyof ClientEvents>(event: E): <F extends (...args: ClientEvents[E]) => unknown>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<F>) => void;
39
+ /**
40
+ * Handles a discord.js client event the first time it is emitted only, on a controller or a service.
41
+ *
42
+ * Use it for one-off work on an event, such as warming a cache once the client is ready. For every emission,
43
+ * use {@link On}; for work at startup and shutdown, a service's `onReady` and `onShutdown` hooks.
44
+ *
45
+ * @param event - The client event to handle, such as `'clientReady'`.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * @Service()
50
+ * export class CacheWarmer {
51
+ * @Once('clientReady')
52
+ * async warm(client: Client<true>) {
53
+ * await client.guilds.fetch()
54
+ * }
55
+ * }
56
+ * ```
57
+ *
58
+ * @pipeline handler with the guards, interceptors and filters that apply to events: the app's global ones, its class's and its own
59
+ * @group Decorators
60
+ * @category Handlers
61
+ * @see {@link On}
62
+ * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
63
+ */
64
+ declare function Once<E extends keyof ClientEvents>(event: E): <F extends (...args: ClientEvents[E]) => unknown>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<F>) => void;
65
+
10
66
  /**
11
67
  * Marks a class as a service, which controllers and other services inject by its type.
12
68
  *
@@ -424,62 +480,6 @@ declare function Autocomplete<_R = unknown>(commandPath: string, optionName?: st
424
480
  */
425
481
  declare function Controller(options?: ControllerOptions): (target: abstract new (...args: any[]) => unknown, propertyKey?: string | symbol) => void;
426
482
 
427
- /**
428
- * Handles a discord.js client event every time it is emitted, on a controller or a service.
429
- *
430
- * Use it for gateway events MeoCord has no handler type for, such as a member joining or a channel being
431
- * created. For commands, components, messages and reactions, use {@link Command}, `@MessageHandler` or
432
- * `@ReactionHandler`, which route them.
433
- *
434
- * @remarks
435
- * The handler's parameters are typed from discord.js's `ClientEvents`. It runs through the pipeline, so the app's
436
- * global guards apply to it, and its class's and its own, and an error it throws is logged without stopping the bot. Handling `interactionCreate`
437
- * or `messageCreate` here runs alongside MeoCord's own dispatch of them.
438
- *
439
- * @param event - The client event to handle, such as `'guildMemberAdd'`.
440
- *
441
- * @example
442
- * ```ts
443
- * @On('guildMemberAdd')
444
- * async greet(member: GuildMember) {
445
- * await member.send(`Welcome to ${member.guild.name}!`)
446
- * }
447
- * ```
448
- *
449
- * @pipeline handler with the guards, interceptors and filters that apply to events: the app's global ones, its class's and its own
450
- * @group Decorators
451
- * @category Handlers
452
- * @see {@link Once}
453
- * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
454
- */
455
- declare function On<E extends keyof ClientEvents>(event: E): <F extends (...args: ClientEvents[E]) => unknown>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<F>) => void;
456
- /**
457
- * Handles a discord.js client event the first time it is emitted only, on a controller or a service.
458
- *
459
- * Use it for one-off work on an event, such as warming a cache once the client is ready. For every emission,
460
- * use {@link On}; for work at startup and shutdown, a service's `onReady` and `onShutdown` hooks.
461
- *
462
- * @param event - The client event to handle, such as `'clientReady'`.
463
- *
464
- * @example
465
- * ```ts
466
- * @Service()
467
- * export class CacheWarmer {
468
- * @Once('clientReady')
469
- * async warm(client: Client<true>) {
470
- * await client.guilds.fetch()
471
- * }
472
- * }
473
- * ```
474
- *
475
- * @pipeline handler with the guards, interceptors and filters that apply to events: the app's global ones, its class's and its own
476
- * @group Decorators
477
- * @category Handlers
478
- * @see {@link On}
479
- * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
480
- */
481
- declare function Once<E extends keyof ClientEvents>(event: E): <F extends (...args: ClientEvents[E]) => unknown>(target: object, propertyKey: string, _descriptor: TypedPropertyDescriptor<F>) => void;
482
-
483
483
  /**
484
484
  * One entry of `@UseGuard`, `@UseInterceptor`, `@UseFilter`, `@UsePipe` or `@MeoCord`'s lists, checked
485
485
  * against the class it provides: `params` against the params the class declares. An entry typed only as
@@ -735,8 +735,8 @@ declare function UseFilter<const T extends readonly unknown[]>(...filters: {
735
735
  * @see {@link MeoCordConfig}
736
736
  */
737
737
  interface MeoCordOptions<G extends readonly unknown[] = readonly unknown[], I extends readonly unknown[] = readonly unknown[], F extends readonly unknown[] = readonly unknown[]> {
738
- /** Controllers to register. */
739
- controllers: ServiceIdentifier[];
738
+ /** Controllers to register: their classes. */
739
+ controllers: (abstract new (...args: any[]) => any)[];
740
740
  /**
741
741
  * Options for the discord.js `Client`, such as its intents and partials. The shards it runs as are set in
742
742
  * `meocord.config.ts`, under `sharding`.
@@ -835,10 +835,10 @@ interface MeoCordOptions<G extends readonly unknown[] = readonly unknown[], I ex
835
835
  /**
836
836
  * Themes by where a call comes from: `guild` for a server's, over the handler's, and `user` for a user's, over the
837
837
  * server's, in a server or a DM. Each returns part of a theme or `undefined`, at once or as a promise, and is looked
838
- * up while `@Defer` acknowledges, before the guards. A result that is not a valid theme is left out, with a warning
839
- * once per server or user; a resolver that fails or passes its timeout leaves its theme out of the call, logged once
840
- * until it answers again. A class implementing {@link ThemeResolver} does the same with the app's services, resolved
841
- * from its container.
838
+ * up while `@Defer` acknowledges, before the guards. A result that is not a valid theme, as is one that cannot be
839
+ * read, such as an object with a cycle or a getter that throws, is left out, with a warning once per server or user;
840
+ * a resolver that fails or passes its timeout leaves its theme out of the call, logged once until it answers again.
841
+ * A class implementing {@link ThemeResolver} does the same with the app's services, resolved from its container.
842
842
  */
843
843
  themeFor?: ThemeResolvers | (new (...args: any[]) => ThemeResolver);
844
844
  /**