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
@@ -4,8 +4,21 @@
4
4
  */ /** Set by `meocord register`: register the commands over REST and exit, without logging in. */ const REGISTER_ONLY_ENV = 'MEOCORD_REGISTER_ONLY';
5
5
  /** Set by `meocord register --guild <id>`: register every command to that guild only. */ const REGISTER_GUILD_ENV = 'MEOCORD_REGISTER_GUILD';
6
6
  /** Set by `meocord start --force-register` and by `meocord register`: send even an unchanged payload. */ const FORCE_REGISTER_ENV = 'MEOCORD_FORCE_REGISTER';
7
+ /**
8
+ * Set by the CLI for every bot it starts: `development` or `production`, the mode the command runs in. A bundle built
9
+ * with `bundleDependencies` has `process.env.NODE_ENV` written in as the mode it was built in, MeoCord's own reads
10
+ * included, so the command's mode reaches it through this variable instead.
11
+ */ const REGISTER_MODE_ENV = 'MEOCORD_REGISTER_MODE';
12
+ /**
13
+ * Whether commands register as development does, to `commands.developmentGuild`: as the CLI command that started the
14
+ * bot says, else as `NODE_ENV` says.
15
+ */ function registersAsDevelopment(env = process.env) {
16
+ const mode = env[REGISTER_MODE_ENV];
17
+ if (mode === 'development' || mode === 'production') return mode === 'development';
18
+ return process.env.NODE_ENV === 'development';
19
+ }
7
20
  /** Whether this process was started to register commands and exit. */ function isRegisterOnly(env = process.env) {
8
21
  return env[REGISTER_ONLY_ENV] === '1';
9
22
  }
10
23
 
11
- export { FORCE_REGISTER_ENV, REGISTER_GUILD_ENV, REGISTER_ONLY_ENV, isRegisterOnly };
24
+ export { FORCE_REGISTER_ENV, REGISTER_GUILD_ENV, REGISTER_MODE_ENV, REGISTER_ONLY_ENV, isRegisterOnly, registersAsDevelopment };
@@ -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
  }
@@ -1,6 +1,23 @@
1
1
  import { quoteForLog } from './user-text.util.js';
2
2
 
3
- /** Whether a value is a class, which a stage entry or a `@Catch` type must be: what `new` and `instanceof` take. */ const isConstructor = (value)=>typeof value === 'function';
3
+ /** Whether `new` takes a value, as `instanceof` needs of its right side, such as a `@Catch` type. */ function isConstructible(value) {
4
+ if (typeof value !== 'function') return false;
5
+ try {
6
+ // Fails unless value can be new.target, which only a constructor can; none of value's constructor code runs
7
+ Reflect.construct(String, [], value);
8
+ return true;
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ /**
14
+ * Whether a value is a class, which a stage entry or an app option must be: constructible, with an object prototype for
15
+ * the metadata MeoCord reads. An arrow function, a method, an async or a generator function is none.
16
+ */ function isConstructor(value) {
17
+ if (!isConstructible(value)) return false;
18
+ const prototype = value.prototype;
19
+ return typeof prototype === 'object' && prototype !== null;
20
+ }
4
21
  /** A value as a refusal or a warning names it: a string quoted, `null` and `undefined` as written, else its kind. */ function describeValue(value) {
5
22
  if (value === null || value === undefined) return String(value);
6
23
  if (typeof value === 'string') return quoteForLog(value);
@@ -9,15 +26,26 @@ import { quoteForLog } from './user-text.util.js';
9
26
  }
10
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}`;
11
28
  /**
12
- * Sets `key` on `target` as an own, enumerable data property: unlike `target[key] = value`, a key named `__proto__`
13
- * 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.
14
32
  */ function setOwn(target, key, value) {
15
- 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, {
16
36
  value,
17
37
  enumerable: true,
18
38
  writable: true,
19
39
  configurable: true
20
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);
21
49
  }
22
50
 
23
- export { describeValue, 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
  * ```
@@ -596,6 +600,9 @@ declare function useTheme(): ResolvedTheme;
596
600
  * Such a listener runs in its emitter's context, so without it a `useTheme()` inside reads the theme outside any call.
597
601
  * Timers and promises the handler starts keep its theme without it.
598
602
  *
603
+ * The theme is the binding call's whole theme, its server's and user's themes included, so every later call, whoever
604
+ * it is for, is answered in the starting member's theme.
605
+ *
599
606
  * @param fn - The function to run in the call's theme; `this` and its arguments are passed through.
600
607
  * @returns A function that runs `fn` in the theme `useTheme()` returns where `bindTheme` is called.
601
608
  *
@@ -605,7 +612,7 @@ declare function useTheme(): ResolvedTheme;
605
612
  * async poll(message: Message) {
606
613
  * const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 60_000 })
607
614
  * collector.on('collect', bindTheme(async (click: ButtonInteraction) => {
608
- * const { emojis } = useTheme() // the handler's theme, @UseTheme included
615
+ * const { emojis } = useTheme() // the starting member's theme, @UseTheme included
609
616
  * await click.reply(`${emojis.success} Picked`)
610
617
  * }))
611
618
  * }
@@ -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
  * ```
@@ -596,6 +600,9 @@ declare function useTheme(): ResolvedTheme;
596
600
  * Such a listener runs in its emitter's context, so without it a `useTheme()` inside reads the theme outside any call.
597
601
  * Timers and promises the handler starts keep its theme without it.
598
602
  *
603
+ * The theme is the binding call's whole theme, its server's and user's themes included, so every later call, whoever
604
+ * it is for, is answered in the starting member's theme.
605
+ *
599
606
  * @param fn - The function to run in the call's theme; `this` and its arguments are passed through.
600
607
  * @returns A function that runs `fn` in the theme `useTheme()` returns where `bindTheme` is called.
601
608
  *
@@ -605,7 +612,7 @@ declare function useTheme(): ResolvedTheme;
605
612
  * async poll(message: Message) {
606
613
  * const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 60_000 })
607
614
  * collector.on('collect', bindTheme(async (click: ButtonInteraction) => {
608
- * const { emojis } = useTheme() // the handler's theme, @UseTheme included
615
+ * const { emojis } = useTheme() // the starting member's theme, @UseTheme included
609
616
  * await click.reply(`${emojis.success} Picked`)
610
617
  * }))
611
618
  * }
@@ -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.