meocord 4.1.2 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +362 -0
  2. package/THIRD_PARTY_NOTICES.md +14 -13
  3. package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-XKMlcUZz.cjs} +669 -185
  4. package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-BZgJyfZS.cjs} +218 -75
  5. package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-CsJJyCZd.cjs} +10 -7
  6. package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CgfPQrNl.cjs} +374 -132
  7. package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-BUAR1WYG.cjs} +21 -7
  8. package/dist/cjs/common/index.cjs +4 -4
  9. package/dist/cjs/core/index.cjs +365 -310
  10. package/dist/cjs/decorator/index.cjs +85 -61
  11. package/dist/cjs/testing/index.cjs +2236 -496
  12. package/dist/cli.json +1 -1
  13. package/dist/esm/bin/app-template/README.md.template +3 -1
  14. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
  15. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
  16. package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
  17. package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
  18. package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
  19. package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
  20. package/dist/esm/bin/generator.js +4 -5
  21. package/dist/esm/bin/helper/filter-generator.helper.js +4 -5
  22. package/dist/esm/bin/helper/guard-generator.helper.js +4 -5
  23. package/dist/esm/bin/helper/interceptor-generator.helper.js +4 -5
  24. package/dist/esm/bin/helper/observer-generator.helper.js +4 -5
  25. package/dist/esm/bin/helper/pipe-generator.helper.js +4 -5
  26. package/dist/esm/bin/helper/service-generator.helper.js +4 -5
  27. package/dist/esm/bin/meocord.js +84 -25
  28. package/dist/esm/build/load-config.pre-entry.js +17 -4
  29. package/dist/esm/build/rsbuild-config.js +16 -12
  30. package/dist/esm/build/typed-externals.js +36 -0
  31. package/dist/esm/common/cooldown-store.js +10 -2
  32. package/dist/esm/common/decorator.js +6 -6
  33. package/dist/esm/common/deprecation.js +2 -2
  34. package/dist/esm/common/errors.js +27 -2
  35. package/dist/esm/common/execution-context.js +1 -1
  36. package/dist/esm/common/meocord-text.js +11 -8
  37. package/dist/esm/common/metadata.js +1 -1
  38. package/dist/esm/common/response/call-order.js +9 -4
  39. package/dist/esm/common/response/presenter.js +33 -6
  40. package/dist/esm/common/response/response-state.js +84 -10
  41. package/dist/esm/common/route.js +21 -5
  42. package/dist/esm/common/sharded-cooldown-store.js +1 -1
  43. package/dist/esm/common/translate-error.js +1 -1
  44. package/dist/esm/common/translator.js +37 -6
  45. package/dist/esm/core/command-conflicts.js +34 -34
  46. package/dist/esm/core/command-registration.js +93 -24
  47. package/dist/esm/core/command-routes.js +2 -2
  48. package/dist/esm/core/component-routes.js +74 -31
  49. package/dist/esm/core/cooldown-runner.js +29 -8
  50. package/dist/esm/core/dispatcher.js +51 -7
  51. package/dist/esm/core/fallback.js +20 -11
  52. package/dist/esm/core/filter-runner.js +2 -2
  53. package/dist/esm/core/guard-runner.js +16 -3
  54. package/dist/esm/core/handler-input.js +4 -3
  55. package/dist/esm/core/handler-pipeline.js +27 -14
  56. package/dist/esm/core/handler-registry.js +1 -1
  57. package/dist/esm/core/interceptor-runner.js +2 -2
  58. package/dist/esm/core/lifecycle-hooks.js +134 -47
  59. package/dist/esm/core/meocord-factory.js +241 -179
  60. package/dist/esm/core/meocord.app.js +116 -75
  61. package/dist/esm/core/message-routes.js +4 -3
  62. package/dist/esm/core/observer-runner.js +4 -3
  63. package/dist/esm/core/providers.js +45 -23
  64. package/dist/esm/core/route-specificity.js +4 -4
  65. package/dist/esm/core/running-calls.js +43 -0
  66. package/dist/esm/core/scalar-types.js +15 -3
  67. package/dist/esm/core/shard-context.js +6 -7
  68. package/dist/esm/core/shard-manager.js +16 -10
  69. package/dist/esm/core/startup-roots.js +23 -4
  70. package/dist/esm/core/theme-resolvers.js +4 -1
  71. package/dist/esm/core/theme-runtime.js +2 -2
  72. package/dist/esm/core/theme-scope.js +3 -1
  73. package/dist/esm/core/theme-validation.js +17 -10
  74. package/dist/esm/decorator/app.decorator.js +12 -7
  75. package/dist/esm/decorator/command-builder.decorator.js +4 -4
  76. package/dist/esm/decorator/controller-class.decorator.js +9 -4
  77. package/dist/esm/decorator/controller.decorator.js +162 -83
  78. package/dist/esm/decorator/cooldown.decorator.js +20 -3
  79. package/dist/esm/decorator/defer.decorator.js +4 -4
  80. package/dist/esm/decorator/event.decorator.js +5 -5
  81. package/dist/esm/decorator/filter.decorator.js +7 -7
  82. package/dist/esm/decorator/guard.decorator.js +11 -10
  83. package/dist/esm/decorator/inject.decorator.js +4 -4
  84. package/dist/esm/decorator/interceptor.decorator.js +7 -7
  85. package/dist/esm/decorator/observer.decorator.js +4 -4
  86. package/dist/esm/decorator/service.decorator.js +4 -4
  87. package/dist/esm/decorator/theme.decorator.js +3 -3
  88. package/dist/esm/decorator/validation.decorator.js +10 -10
  89. package/dist/esm/package.json.js +1 -1
  90. package/dist/esm/testing/catalog.js +27 -11
  91. package/dist/esm/testing/cooldown-store-suite.js +85 -15
  92. package/dist/esm/testing/discord-defaults.js +249 -21
  93. package/dist/esm/testing/index.js +4 -2
  94. package/dist/esm/testing/inspect-handler.js +20 -3
  95. package/dist/esm/testing/meocord-testing-module.js +352 -205
  96. package/dist/esm/testing/mock-fn.js +173 -22
  97. package/dist/esm/testing/mock-interaction.js +1214 -209
  98. package/dist/esm/testing/response.js +1 -1
  99. package/dist/esm/testing/routing.js +39 -14
  100. package/dist/esm/testing/snowflake.js +9 -2
  101. package/dist/esm/testing/startup-errors.js +26 -0
  102. package/dist/esm/testing/strict-mocks.js +65 -0
  103. package/dist/esm/testing/theme.js +1 -1
  104. package/dist/esm/util/bundle-entry.util.js +2 -1
  105. package/dist/esm/util/common.util.js +47 -19
  106. package/dist/esm/util/dev-runner.util.js +8 -8
  107. package/dist/esm/util/generator-cli.util.js +4 -5
  108. package/dist/esm/util/interaction.util.js +3 -3
  109. package/dist/esm/util/meocord-config-loader.util.js +10 -2
  110. package/dist/esm/util/meocord-config-validation.util.js +4 -5
  111. package/dist/esm/util/meocord-source-config.util.js +65 -20
  112. package/dist/esm/util/metadata-keys.js +1 -0
  113. package/dist/esm/util/method-wrapper.util.js +16 -0
  114. package/dist/esm/util/platform.util.js +10 -10
  115. package/dist/esm/util/refusal.util.js +148 -8
  116. package/dist/esm/util/shard-process.util.js +4 -1
  117. package/dist/esm/util/sharding-mode.util.js +5 -7
  118. package/dist/esm/util/shared-state.util.js +21 -0
  119. package/dist/esm/util/tsconfig-paths.util.js +29 -0
  120. package/dist/esm/util/tsconfig.util.js +10 -10
  121. package/dist/esm/util/value.util.js +12 -1
  122. package/dist/types/common/index.d.cts +8 -8
  123. package/dist/types/common/index.d.ts +8 -8
  124. package/dist/types/core/index.d.cts +10 -7
  125. package/dist/types/core/index.d.ts +10 -7
  126. package/dist/types/decorator/index.d.cts +31 -29
  127. package/dist/types/decorator/index.d.ts +31 -29
  128. package/dist/types/{index-D7n0FBMm.d.ts → index-DKJclIi-.d.ts} +107 -36
  129. package/dist/types/{index-DEP0NYrU.d.cts → index-Dlf0ZZtS.d.cts} +107 -36
  130. package/dist/types/interface/index.d.cts +1 -1
  131. package/dist/types/interface/index.d.ts +1 -1
  132. package/dist/types/{route-C-aMI7Sl.d.cts → route-Bh1U13yy.d.cts} +8 -4
  133. package/dist/types/{route-C-aMI7Sl.d.ts → route-Bh1U13yy.d.ts} +8 -4
  134. package/dist/types/testing/index.d.cts +406 -70
  135. package/dist/types/testing/index.d.ts +406 -70
  136. package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-CyEz9pnv.d.cts} +1 -1
  137. package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-DA204O-q.d.ts} +1 -1
  138. package/dist/types/{translator-J6tIUfdp.d.ts → translator-Bg7hwxDF.d.cts} +21 -8
  139. package/dist/types/{translator-B4mc1lzJ.d.cts → translator-CeP4bCzp.d.ts} +21 -8
  140. package/package.json +4 -3
@@ -1,6 +1,5 @@
1
1
  import { writeFileSync, existsSync, readFileSync } from 'fs';
2
2
  import path from 'path';
3
- import { refuse } from './refusal.util.js';
4
3
  import { isBuiltApplication, bundleEntry } from './bundle-entry.util.js';
5
4
 
6
5
  /**
@@ -42,23 +41,24 @@ import { isBuiltApplication, bundleEntry } from './bundle-entry.util.js';
42
41
  writeFileSync(path.join(distDir, PLATFORM_MANIFEST), `${JSON.stringify(currentPlatform(), null, 2)}\n`);
43
42
  }
44
43
  /**
45
- * Stops a bundle from starting on a platform its native addons were not built for, with one clear
46
- * message instead of a linker error on the first command that loads an addon.
44
+ * Why a bundle cannot start on this platform, naming both, or `undefined` when it can: its manifest names the platform
45
+ * its native addons were built for. The pre-entry checks it before any package loads, where a native addon built for
46
+ * another platform would otherwise fail with an error of its own.
47
47
  * @param distDir - Directory holding the manifest. Defaults to the built bundle's directory, which a process manager's
48
48
  * wrapper in `argv[1]` would hide, and else to the entry script's.
49
- */ function assertBuiltForThisPlatform(distDir = defaultDistDir()) {
50
- if (!distDir) return;
49
+ */ function platformMismatch(distDir = defaultDistDir()) {
50
+ if (!distDir) return undefined;
51
51
  const manifest = path.join(distDir, PLATFORM_MANIFEST);
52
- if (!existsSync(manifest)) return;
52
+ if (!existsSync(manifest)) return undefined;
53
53
  let built;
54
54
  try {
55
55
  built = JSON.parse(readFileSync(manifest, 'utf8'));
56
56
  } catch {
57
- return;
57
+ return undefined;
58
58
  }
59
59
  const running = currentPlatform();
60
- if (isSamePlatform(built, running)) return;
61
- throw refuse(new Error(`${path.basename(distDir)}: this build carries native addons compiled for ${describePlatform(built)}, but is ` + `running on ${describePlatform(running)}. Compiled binaries only load on the platform they were built for. ` + 'Build on the same platform you deploy to -- for a container, run `meocord build` inside the image.'));
60
+ if (isSamePlatform(built, running)) return undefined;
61
+ return `${path.basename(distDir)}: this build carries native addons compiled for ${describePlatform(built)}, but is ` + `running on ${describePlatform(running)}. Compiled binaries only load on the platform they were built for. ` + 'Build on the same platform you deploy to -- for a container, run `meocord build` inside the image.';
62
62
  }
63
63
  /** The built bundle's directory, or the entry script's outside a built bot. */ function defaultDistDir() {
64
64
  const entry = isBuiltApplication() ? bundleEntry() : undefined;
@@ -66,4 +66,4 @@ import { isBuiltApplication, bundleEntry } from './bundle-entry.util.js';
66
66
  return process.argv[1] ? path.dirname(process.argv[1]) : undefined;
67
67
  }
68
68
 
69
- export { PLATFORM_MANIFEST, assertBuiltForThisPlatform, currentPlatform, describePlatform, isSamePlatform, writePlatformManifest };
69
+ export { PLATFORM_MANIFEST, currentPlatform, describePlatform, isSamePlatform, platformMismatch, writePlatformManifest };
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import { warnDeprecatedBehaviour } from '../common/deprecation.js';
3
3
  import { isExplainedError } from '../common/explained-error.js';
4
4
  import { Logger } from '../common/logger.js';
5
- import { isBuiltApplication } from './bundle-entry.util.js';
5
+ import { isBuiltApplication, STARTUP_ERRORS_KEY } from './bundle-entry.util.js';
6
6
  import { comparablePath, framePath } from './source-path.util.js';
7
7
 
8
8
  const REFUSAL = Symbol.for('meocord.refusal');
@@ -51,10 +51,10 @@ const REFUSAL = Symbol.for('meocord.refusal');
51
51
  // A stack frame's function and file: `at fn (file:line:col)` or `at file:line:col`, the file possibly a file:// URL
52
52
  const FRAME = /^\s*at (?:(.*?) \()?(.+?):\d+:\d+\)?$/;
53
53
  /**
54
- * A refusal as the application reports it: the message, and the first file of the application's own source the stack
55
- * passes through, other than its entry, which only loads the rest. The file alone: a line from a development build's
56
- * source map can be off, and a compiler's decorate helper can be placed in any file, so those frames are skipped.
57
- */ function describeRefusal(error, root, windows = process.platform === 'win32') {
54
+ * The first file of the application's own source an error's stack passes through, other than its entry, which only
55
+ * loads the rest, relative to `root`. The file alone: a line from a development build's source map can be off, and a
56
+ * compiler's decorate helper can be placed in any file, so those frames are skipped.
57
+ */ function sourceFileOf(error, root, windows = process.platform === 'win32') {
58
58
  const paths = windows ? path.win32 : path.posix;
59
59
  const source = comparablePath(paths.join(root, 'src') + paths.sep, windows);
60
60
  const entry = comparablePath(paths.join(root, 'src', 'main.ts'), windows);
@@ -65,9 +65,149 @@ const FRAME = /^\s*at (?:(.*?) \()?(.+?):\d+:\d+\)?$/;
65
65
  const file = paths.normalize(framePath(name, windows));
66
66
  const compared = comparablePath(file, windows);
67
67
  if (!compared.startsWith(source) || compared === entry || compared.includes(dependencies)) continue;
68
- return `${error.message}\n in ${paths.relative(root, file).split(paths.sep).join('/')}`;
68
+ return paths.relative(root, file).split(paths.sep).join('/');
69
+ }
70
+ return undefined;
71
+ }
72
+ /**
73
+ * A refusal as the application reports it: the message, led by the declaration it is about where the message does not
74
+ * name it, and the file of the application's source it comes from; see {@link sourceFileOf}.
75
+ */ function describeRefusal(error, root, windows = process.platform === 'win32') {
76
+ const declaration = declarationOf(error);
77
+ const message = declaration && !error.message.startsWith(declaration) ? `${declaration}: ${error.message}` : error.message;
78
+ const file = sourceFileOf(error, root, windows);
79
+ return file ? `${message}\n in ${file}` : message;
80
+ }
81
+ // ---------------------------------------------------------------------------
82
+ // Startup errors: those create() and a testing module's compile() find, and those decorators find at import
83
+ // ---------------------------------------------------------------------------
84
+ const STARTUP_ERRORS = Symbol.for('meocord.startupErrors');
85
+ const DECLARED = Symbol.for('meocord.declaredStartupErrors');
86
+ const ALL_DECLARED = Symbol.for('meocord.allDeclaredStartupErrors');
87
+ // The startup errors kept while checks run under collectStartupErrors; undefined outside them
88
+ let kept;
89
+ /**
90
+ * Reports an error a startup check finds. Outside {@link collectStartupErrors} it is thrown, as a refusal; inside, it
91
+ * is kept, and the check goes on to find the rest.
92
+ */ function startupError(error) {
93
+ refuse(error);
94
+ if (!kept) throw error;
95
+ // Two checks of one mistake, such as a class needing a translator, name it once
96
+ if (!kept.some((other)=>other.message === error.message)) kept.push(error);
97
+ }
98
+ /**
99
+ * Runs startup checks, and once they are done throws the first error they found, unchanged, with every one found on it
100
+ * for {@link startupErrorsOf}. An error that ends the checks after one was found is left out, as one the found error
101
+ * caused; one that ends them before any was found is thrown as it is.
102
+ */ function collectStartupErrors(checks) {
103
+ const outer = kept;
104
+ const found = [];
105
+ kept = found;
106
+ let result;
107
+ try {
108
+ result = checks();
109
+ } catch (error) {
110
+ if (found.length === 0) throw error;
111
+ if (isRefusal(error) && !found.includes(error)) found.push(error);
112
+ throw firstOf(found);
113
+ } finally{
114
+ kept = outer;
115
+ }
116
+ if (found.length > 0) throw firstOf(found);
117
+ return result;
118
+ }
119
+ /**
120
+ * Ends the checks under way with the errors found so far, before a step that is no check, such as binding or a warning:
121
+ * a run with one error stops where it stopped when the first error was thrown, and the errors of one pass of the
122
+ * checks are still reported together.
123
+ */ function stopOnStartupErrors() {
124
+ if (kept && kept.length > 0) throw kept[0];
125
+ }
126
+ function firstOf(errors) {
127
+ const [first] = errors;
128
+ Object.defineProperty(first, STARTUP_ERRORS, {
129
+ value: errors,
130
+ configurable: true
131
+ });
132
+ return first;
133
+ }
134
+ /** Every startup error found together with `error`, the first of them; `error` alone when it was the only one. */ function startupErrorsOf(error) {
135
+ return error[STARTUP_ERRORS] ?? [
136
+ error
137
+ ];
138
+ }
139
+ /**
140
+ * Logs each startup error found with `error` as the application reports a refusal, and how many there were, when there
141
+ * was more than one; a lone error is left to whoever reports it now.
142
+ */ function logStartupErrors(error, log, ending) {
143
+ const errors = startupErrorsOf(error);
144
+ if (errors.length < 2) return false;
145
+ for (const each of errors)log(describeRefusal(each, process.cwd()));
146
+ log(`MeoCord found ${errors.length} startup errors; ${ending}.`);
147
+ return true;
148
+ }
149
+ /** Sets how decorators report startup errors from here on, before the application's modules load. */ function setStartupErrorsMode(mode) {
150
+ globalThis[STARTUP_ERRORS_KEY] = mode;
151
+ }
152
+ /** Whether decorators keep their startup errors for create(), as `startupErrors: 'all'` has them. */ const keepsDeclaredErrors = ()=>globalThis[STARTUP_ERRORS_KEY] === 'all';
153
+ /** The declaration a decorator's startup error is about, `Class.method` or `Class`. */ function declarationOf(error) {
154
+ return error.declaration;
155
+ }
156
+ /**
157
+ * A decorator that names, on each startup error it throws, the declaration it was applied to, `Class.method` or
158
+ * `Class`, as `declaration`, and the source file it is declared in, as `file`: the same error, thrown at the same
159
+ * moment, with its message unchanged. With `startupErrors: 'all'` the error is kept on the class instead, for create()
160
+ * to report with the rest, and the decorator applies nothing more.
161
+ */ function declaring(decorate) {
162
+ return function(target, propertyKey, ...rest) {
163
+ try {
164
+ return decorate.call(this, target, propertyKey, ...rest);
165
+ } catch (error) {
166
+ if (!isRefusal(error)) throw error;
167
+ keepOrThrow(error, target, propertyKey);
168
+ return undefined;
169
+ }
170
+ };
171
+ }
172
+ function keepOrThrow(error, target, propertyKey) {
173
+ var _globalThis, _ALL_DECLARED;
174
+ // A decorator applied through another, such as applyDecorators, names the declaration once, where it was first thrown
175
+ if (declarationOf(error) === undefined) {
176
+ Object.defineProperty(error, 'declaration', {
177
+ value: decoratedName(target, propertyKey),
178
+ configurable: true
179
+ });
180
+ Object.defineProperty(error, 'file', {
181
+ get: ()=>sourceFileOf(error, process.cwd()),
182
+ configurable: true
183
+ });
69
184
  }
70
- return error.message;
185
+ if (!keepsDeclaredErrors()) throw error;
186
+ const owner = typeof target === 'function' ? target : target.constructor;
187
+ if (!Object.prototype.hasOwnProperty.call(owner, DECLARED)) Object.defineProperty(owner, DECLARED, {
188
+ value: []
189
+ });
190
+ const declared = owner[DECLARED];
191
+ if (!declared.includes(error)) declared.push(error);
192
+ const all = (_globalThis = globalThis)[_ALL_DECLARED = ALL_DECLARED] ?? (_globalThis[_ALL_DECLARED] = []);
193
+ if (!all.includes(error)) all.push(error);
194
+ }
195
+ /** The startup errors decorators kept on `classes` and the classes they extend, each once, in the order they were found. */ function declaredErrorsOf(classes) {
196
+ const found = new Set();
197
+ for (const cls of classes){
198
+ for(let current = cls; typeof current === 'function' && current !== Function.prototype; current = Object.getPrototypeOf(current)){
199
+ if (Object.prototype.hasOwnProperty.call(current, DECLARED)) for (const error of current[DECLARED])found.add(error);
200
+ }
201
+ }
202
+ const order = allDeclaredErrors();
203
+ return [
204
+ ...found
205
+ ].sort((a, b)=>order.indexOf(a) - order.indexOf(b));
206
+ }
207
+ /** Every startup error decorators kept in this process, in the order they were found. */ function allDeclaredErrors() {
208
+ return [
209
+ ...globalThis[ALL_DECLARED] ?? []
210
+ ];
71
211
  }
72
212
  /**
73
213
  * Reports a refusal nothing catches as one line, with its source file, and exits 1; other errors keep the runtime's
@@ -95,4 +235,4 @@ const FRAME = /^\s*at (?:(.*?) \()?(.+?):\d+:\d+\)?$/;
95
235
  process.on('unhandledRejection', onRejection);
96
236
  }
97
237
 
98
- export { decoratedName, deprecatedOnMethod, describeRefusal, handOffRefusal, isRefusal, refuse, refuseOnClass, refuseOnMethod, reportRefusals };
238
+ export { allDeclaredErrors, collectStartupErrors, declarationOf, declaredErrorsOf, declaring, decoratedName, deprecatedOnMethod, describeRefusal, handOffRefusal, isRefusal, keepsDeclaredErrors, logStartupErrors, refuse, refuseOnClass, refuseOnMethod, reportRefusals, setStartupErrorsMode, sourceFileOf, startupError, startupErrorsOf, stopOnStartupErrors };
@@ -1,5 +1,8 @@
1
1
  /** Set by discord.js in every process its ShardingManager spawns. */ const SHARDING_MANAGER_ENV = 'SHARDING_MANAGER';
2
- /** Whether this process was spawned as a shard by a sharding manager. */ function isShardProcess(env = process.env) {
2
+ /**
3
+ * Whether a process with `env` was spawned as a shard by a sharding manager. Read before anything builds a
4
+ * `ShardingManager`, whose constructor writes the variable into its own process too.
5
+ */ function isShardProcess(env) {
3
6
  return env[SHARDING_MANAGER_ENV] !== undefined;
4
7
  }
5
8
 
@@ -1,6 +1,4 @@
1
1
  import { refuse } from './refusal.util.js';
2
- import { isShardProcess } from './shard-process.util.js';
3
- export { SHARDING_MANAGER_ENV } from './shard-process.util.js';
4
2
 
5
3
  /** Whether a shard's manager is gone: the IPC channel it asks a shard to stop through is closed. */ function managerGone(proc = process) {
6
4
  return proc.connected === false;
@@ -10,10 +8,10 @@ export { SHARDING_MANAGER_ENV } from './shard-process.util.js';
10
8
  return env.NODE_ENV !== 'development' || config.sharding.development === true;
11
9
  }
12
10
  /**
13
- * The role of this process: a shard when a manager spawned it, the manager when process sharding is on,
14
- * and otherwise a single process running every shard it has.
15
- */ function shardingRole(config, env = process.env) {
16
- if (isShardProcess(env)) return 'shard';
11
+ * The role of this process: a shard when a manager spawned it, as `shard` says, the manager when process sharding is
12
+ * on, and otherwise a single process running every shard it has.
13
+ */ function shardingRole(config, shard, env = process.env) {
14
+ if (shard) return 'shard';
17
15
  return processShardingEnabled(config, env) ? 'manager' : 'single';
18
16
  }
19
17
  /**
@@ -52,4 +50,4 @@ export { SHARDING_MANAGER_ENV } from './shard-process.util.js';
52
50
  return clientOptions;
53
51
  }
54
52
 
55
- export { clientOptionsWithSharding, isShardProcess, managerGone, processShardingEnabled, shardingRole };
53
+ export { clientOptionsWithSharding, managerGone, processShardingEnabled, shardingRole };
@@ -0,0 +1,21 @@
1
+ import packageJson from '../package.json.js';
2
+
3
+ /**
4
+ * The name of a key the ES module and CommonJS builds of this meocord version share, and no other version has, for
5
+ * `Symbol.for`: a process that loads both builds of one install keeps one state under it, and two installed versions
6
+ * keep theirs apart. `Symbol.for(sharedName(...))` written out types as a `unique symbol`.
7
+ */ const sharedName = (name)=>`meocord@${packageJson.version}:${name}`;
8
+ /** The key named {@link sharedName}. */ function sharedKey(name) {
9
+ return Symbol.for(sharedName(name));
10
+ }
11
+ /** The state both builds of this version keep on `globalThis` under `name`, made by whichever asks first. */ function shared(name, make) {
12
+ const key = sharedKey(name);
13
+ const store = globalThis;
14
+ if (!Object.hasOwn(store, key)) Object.defineProperty(store, key, {
15
+ value: make(),
16
+ configurable: true
17
+ });
18
+ return store[key];
19
+ }
20
+
21
+ export { shared, sharedKey, sharedName };
@@ -0,0 +1,29 @@
1
+ import { existsSync } from 'fs';
2
+ import path from 'path';
3
+ import { parseTsconfig, createPathsMatcher } from 'get-tsconfig';
4
+
5
+ // Stands in for a pattern's `*` while its targets are worked out, then is put back
6
+ const STAR = '__meocord_paths_star__';
7
+ /**
8
+ * The project's tsconfig `paths`, each target an absolute path, as `tsc` reads them: through `extends`, from `baseUrl`,
9
+ * and with `${configDir}`. Read with get-tsconfig, whatever TypeScript the project installed. Undefined when there are
10
+ * none.
11
+ */ function projectPaths(cwd = process.cwd()) {
12
+ const configPath = path.join(cwd, 'tsconfig.json');
13
+ if (!existsSync(configPath)) return undefined;
14
+ const tsconfig = {
15
+ path: configPath,
16
+ config: parseTsconfig(configPath)
17
+ };
18
+ const paths = tsconfig.config.compilerOptions?.paths;
19
+ // The matcher resolves each target from the file that declares it, or from baseUrl, as tsc does
20
+ const match = createPathsMatcher(tsconfig);
21
+ if (!paths || !match) return undefined;
22
+ // Resolved, so each is the platform's own absolute path: get-tsconfig writes Windows paths with forward slashes
23
+ return Object.fromEntries(Object.keys(paths).map((alias)=>[
24
+ alias,
25
+ match(alias.replace('*', STAR)).map((target)=>path.resolve(target).replace(STAR, '*'))
26
+ ]));
27
+ }
28
+
29
+ export { projectPaths };
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync, mkdtempSync, rmSync, readlinkS
3
3
  import { tmpdir, hostname } from 'os';
4
4
  import { createRequire } from 'module';
5
5
  import { parseJsonc } from './json.util.js';
6
+ import { projectPaths } from './tsconfig-paths.util.js';
6
7
 
7
8
  /**
8
9
  * An `extends` made to work from another directory: a relative path made absolute, and a package
@@ -89,10 +90,11 @@ function isRunning(pid) {
89
90
  }
90
91
  /**
91
92
  * Writes a copy of the project's tsconfig.json for the bundler and returns its path: comments and trailing commas
92
- * removed, paths made absolute (a `paths` target from `baseUrl` when set), `noEmit` dropped. One file per call in this
93
+ * removed, paths made absolute (`paths` as {@link projectPaths} reads them), `noEmit` dropped. One file per call in this
93
94
  * process's directory, removed at exit; the project's file is never written.
94
95
  * @throws When `tsconfig.json` is missing or cannot be parsed.
95
96
  */ function prepareModifiedTsConfig() {
97
+ var _parsedConfig;
96
98
  const tsConfigPath = path.resolve(process.cwd(), 'tsconfig.json');
97
99
  // Ensure tsconfig.json exists
98
100
  if (!existsSync(tsConfigPath)) {
@@ -109,12 +111,15 @@ function isRunning(pid) {
109
111
  // The copy lives in the temp directory, so every path in it is made absolute from the project
110
112
  const cwd = process.cwd();
111
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
112
115
  for (const key of [
113
116
  'include',
114
117
  'exclude',
115
118
  'files'
116
119
  ]){
117
- if (Array.isArray(parsedConfig?.[key])) parsedConfig[key] = parsedConfig[key].map((p)=>path.resolve(cwd, p));
120
+ if (Array.isArray(parsedConfig?.[key])) {
121
+ parsedConfig[key] = parsedConfig[key].map((p)=>path.resolve(cwd, p.replaceAll('${configDir}', cwd)));
122
+ }
118
123
  }
119
124
  // Process compilerOptions
120
125
  if (parsedConfig?.compilerOptions) {
@@ -133,19 +138,14 @@ function isRunning(pid) {
133
138
  if (Array.isArray(parsedConfig.compilerOptions.typeRoots)) {
134
139
  parsedConfig.compilerOptions.typeRoots = parsedConfig.compilerOptions.typeRoots.map((p)=>path.resolve(cwd, p));
135
140
  }
136
- // A `paths` target is relative to baseUrl when there is one, made absolute above, and else to the project. Only
137
- // this file's own baseUrl counts: one inherited through `extends` isn't applied, so paths are the project's own.
138
- if (parsedConfig.compilerOptions.paths) {
139
- const base = parsedConfig.compilerOptions.baseUrl ?? cwd;
140
- Object.keys(parsedConfig.compilerOptions.paths).forEach((alias)=>{
141
- parsedConfig.compilerOptions.paths[alias] = parsedConfig.compilerOptions.paths[alias].map((p)=>path.resolve(base, p));
142
- });
143
- }
144
141
  // Remove `noEmit` option if it exists
145
142
  if ('noEmit' in parsedConfig.compilerOptions) {
146
143
  delete parsedConfig.compilerOptions.noEmit;
147
144
  }
148
145
  }
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
149
  const tempTsConfigPath = path.join(copiesDirectory(), `modified-tsconfig-${++copies}.json`);
150
150
  writeFileSync(tempTsConfigPath, JSON.stringify(parsedConfig, null, 2));
151
151
  return tempTsConfigPath;
@@ -8,5 +8,16 @@ import { quoteForLog } from './user-text.util.js';
8
8
  return typeof value === 'object' ? 'an object' : `a ${typeof value}`;
9
9
  }
10
10
  /** 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
+ /**
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.
14
+ */ function setOwn(target, key, value) {
15
+ Object.defineProperty(target, key, {
16
+ value,
17
+ enumerable: true,
18
+ writable: true,
19
+ configurable: true
20
+ });
21
+ }
11
22
 
12
- export { describeValue, isConstructor, withArticle };
23
+ export { describeValue, isConstructor, setOwn, 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-DEP0NYrU.cjs';
2
+ import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-Dlf0ZZtS.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-DEP0NYrU.cjs';
5
- export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-C-aMI7Sl.cjs';
6
- import { T as Translator } from '../translator-B4mc1lzJ.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-B4mc1lzJ.cjs';
8
- export { T as ThemeCache } from '../theme-resolvers-COHdX5g6.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';
9
9
  import '@rsbuild/core';
10
10
  import '../controller.enum-DTDn69N9.cjs';
11
11
 
@@ -169,7 +169,7 @@ declare class Theme {
169
169
  *
170
170
  * @group Utilities
171
171
  * @see {@link createMetadata}
172
- * @see {@link https://meocord.dev/docs/4.1/custom-decorators | Custom decorators}
172
+ * @see {@link https://meocord.dev/docs/4.2/custom-decorators | Custom decorators}
173
173
  */
174
174
  declare function applyDecorators(...decorators: (ClassDecorator | MethodDecorator)[]): ClassDecorator & MethodDecorator;
175
175
  /**
@@ -523,7 +523,7 @@ declare class ShardedCooldownStore extends CooldownStore {
523
523
  * @group Utilities
524
524
  * @category Localisation
525
525
  * @see {@link createTranslator}
526
- * @see {@link https://meocord.dev/docs/4.1/localisation | Localisation}
526
+ * @see {@link https://meocord.dev/docs/4.2/localisation | Localisation}
527
527
  */
528
528
  declare function translateError(error: unknown, t: Translator<any>, target: Interaction | Message | Locale | `${Locale}`): string;
529
529
 
@@ -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-D7n0FBMm.js';
2
+ import { C as CooldownStore, a as CooldownLimit, b as CooldownVerdict, c as CooldownEntry, d as CooldownBatchVerdict } from '../index-DKJclIi-.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-D7n0FBMm.js';
5
- export { R as Route, a as RouteParams, b as RouteValue, c as RouteValues, r as route } from '../route-C-aMI7Sl.js';
6
- import { T as Translator } from '../translator-J6tIUfdp.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-J6tIUfdp.js';
8
- export { T as ThemeCache } from '../theme-resolvers-DedxyscT.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';
9
9
  import '@rsbuild/core';
10
10
  import '../controller.enum-DTDn69N9.js';
11
11
 
@@ -169,7 +169,7 @@ declare class Theme {
169
169
  *
170
170
  * @group Utilities
171
171
  * @see {@link createMetadata}
172
- * @see {@link https://meocord.dev/docs/4.1/custom-decorators | Custom decorators}
172
+ * @see {@link https://meocord.dev/docs/4.2/custom-decorators | Custom decorators}
173
173
  */
174
174
  declare function applyDecorators(...decorators: (ClassDecorator | MethodDecorator)[]): ClassDecorator & MethodDecorator;
175
175
  /**
@@ -523,7 +523,7 @@ declare class ShardedCooldownStore extends CooldownStore {
523
523
  * @group Utilities
524
524
  * @category Localisation
525
525
  * @see {@link createTranslator}
526
- * @see {@link https://meocord.dev/docs/4.1/localisation | Localisation}
526
+ * @see {@link https://meocord.dev/docs/4.2/localisation | Localisation}
527
527
  */
528
528
  declare function translateError(error: unknown, t: Translator<any>, target: Interaction | Message | Locale | `${Locale}`): string;
529
529
 
@@ -1,5 +1,5 @@
1
1
  import { ServiceIdentifier } from 'inversify';
2
- import { J as MeoCordApplication } from '../index-DEP0NYrU.cjs';
2
+ import { J as MeoCordApplication } from '../index-Dlf0ZZtS.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';
@@ -44,11 +44,14 @@ declare class MeoCordFactory {
44
44
  * @returns The application, which `start()` logs in.
45
45
  * @throws Error when the class has no `@MeoCord`, when the compiled config is missing beside the bundle or fails to
46
46
  * load, naming the file and the reason, with what to do about it, when a provider cannot
47
- * be bound, such as one for a token MeoCord binds itself, when two handlers take one command, or two builder
47
+ * be bound, such as one for a token MeoCord binds itself (a provider for `ThemeCache` or `ExecutionContext` is warned
48
+ * about instead, and refused from the next major version, 5.0), when two handlers take one command, or two builder
48
49
  * classes build one, naming both, and for any other mistake it refuses as the app loads, such as two component
49
- * patterns that match the same customIds. In a built application it is logged first, with where in the source it
50
- * comes from, so `isExplainedError()` tells a caller not to log it again; in a shard of process sharding, the
51
- * manager logs it.
50
+ * patterns that match the same customIds. Each check reports every error it finds, and the first is thrown,
51
+ * unchanged. When there are several, each is logged first, with where in the source it comes from; a lone one is
52
+ * logged in a built application. Either way `isExplainedError()` then tells a caller not to log it again; in a
53
+ * shard of process sharding, the manager logs it. With `startupErrors: 'all'` in `meocord.config.ts`, the errors
54
+ * decorators kept are among them.
52
55
  */
53
56
  static create(target: ServiceIdentifier): MeoCordApplication;
54
57
  private static createApplication;
@@ -260,7 +263,7 @@ interface HandlerFilter<K extends HandlerKind = HandlerKind> {
260
263
  *
261
264
  * @group Controllers
262
265
  * @see {@link HandlerEntry}
263
- * @see {@link https://meocord.dev/docs/4.1/handler-discovery | Handler discovery}
266
+ * @see {@link https://meocord.dev/docs/4.2/handler-discovery | Handler discovery}
264
267
  */
265
268
  declare class HandlerRegistry {
266
269
  private readonly classes;
@@ -416,7 +419,7 @@ type MethodResult<T, M extends keyof T> = T[M] extends (...args: any[]) => infer
416
419
  *
417
420
  * @group Controllers
418
421
  * @see {@link ShardCallResult}
419
- * @see {@link https://meocord.dev/docs/4.1/sharding | Sharding}
422
+ * @see {@link https://meocord.dev/docs/4.2/sharding | Sharding}
420
423
  */
421
424
  declare class ShardContext {
422
425
  private readonly client;
@@ -1,5 +1,5 @@
1
1
  import { ServiceIdentifier } from 'inversify';
2
- import { J as MeoCordApplication } from '../index-D7n0FBMm.js';
2
+ import { J as MeoCordApplication } from '../index-DKJclIi-.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';
@@ -44,11 +44,14 @@ declare class MeoCordFactory {
44
44
  * @returns The application, which `start()` logs in.
45
45
  * @throws Error when the class has no `@MeoCord`, when the compiled config is missing beside the bundle or fails to
46
46
  * load, naming the file and the reason, with what to do about it, when a provider cannot
47
- * be bound, such as one for a token MeoCord binds itself, when two handlers take one command, or two builder
47
+ * be bound, such as one for a token MeoCord binds itself (a provider for `ThemeCache` or `ExecutionContext` is warned
48
+ * about instead, and refused from the next major version, 5.0), when two handlers take one command, or two builder
48
49
  * classes build one, naming both, and for any other mistake it refuses as the app loads, such as two component
49
- * patterns that match the same customIds. In a built application it is logged first, with where in the source it
50
- * comes from, so `isExplainedError()` tells a caller not to log it again; in a shard of process sharding, the
51
- * manager logs it.
50
+ * patterns that match the same customIds. Each check reports every error it finds, and the first is thrown,
51
+ * unchanged. When there are several, each is logged first, with where in the source it comes from; a lone one is
52
+ * logged in a built application. Either way `isExplainedError()` then tells a caller not to log it again; in a
53
+ * shard of process sharding, the manager logs it. With `startupErrors: 'all'` in `meocord.config.ts`, the errors
54
+ * decorators kept are among them.
52
55
  */
53
56
  static create(target: ServiceIdentifier): MeoCordApplication;
54
57
  private static createApplication;
@@ -260,7 +263,7 @@ interface HandlerFilter<K extends HandlerKind = HandlerKind> {
260
263
  *
261
264
  * @group Controllers
262
265
  * @see {@link HandlerEntry}
263
- * @see {@link https://meocord.dev/docs/4.1/handler-discovery | Handler discovery}
266
+ * @see {@link https://meocord.dev/docs/4.2/handler-discovery | Handler discovery}
264
267
  */
265
268
  declare class HandlerRegistry {
266
269
  private readonly classes;
@@ -416,7 +419,7 @@ type MethodResult<T, M extends keyof T> = T[M] extends (...args: any[]) => infer
416
419
  *
417
420
  * @group Controllers
418
421
  * @see {@link ShardCallResult}
419
- * @see {@link https://meocord.dev/docs/4.1/sharding | Sharding}
422
+ * @see {@link https://meocord.dev/docs/4.2/sharding | Sharding}
420
423
  */
421
424
  declare class ShardContext {
422
425
  private readonly client;