meocord 4.1.2 → 4.2.0

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 (95) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-CRn9kWob.cjs} +295 -87
  3. package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-ChvMqnLd.cjs} +74 -40
  4. package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-BdAUTdYs.cjs} +1 -1
  5. package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CQo8mSP8.cjs} +158 -72
  6. package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-DhDVbT5M.cjs} +12 -3
  7. package/dist/cjs/common/index.cjs +4 -4
  8. package/dist/cjs/core/index.cjs +195 -157
  9. package/dist/cjs/decorator/index.cjs +66 -57
  10. package/dist/cjs/testing/index.cjs +733 -301
  11. package/dist/cli.json +1 -1
  12. package/dist/esm/bin/app-template/README.md.template +3 -1
  13. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
  14. package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
  15. package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
  16. package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
  17. package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
  18. package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
  19. package/dist/esm/build/load-config.pre-entry.js +4 -1
  20. package/dist/esm/common/decorator.js +6 -6
  21. package/dist/esm/common/deprecation.js +2 -2
  22. package/dist/esm/common/errors.js +2 -2
  23. package/dist/esm/common/execution-context.js +1 -1
  24. package/dist/esm/common/metadata.js +1 -1
  25. package/dist/esm/common/route.js +10 -1
  26. package/dist/esm/common/translate-error.js +1 -1
  27. package/dist/esm/core/command-conflicts.js +34 -34
  28. package/dist/esm/core/component-routes.js +74 -31
  29. package/dist/esm/core/dispatcher.js +51 -7
  30. package/dist/esm/core/filter-runner.js +2 -2
  31. package/dist/esm/core/handler-pipeline.js +27 -14
  32. package/dist/esm/core/handler-registry.js +1 -1
  33. package/dist/esm/core/interceptor-runner.js +2 -2
  34. package/dist/esm/core/meocord-factory.js +196 -157
  35. package/dist/esm/core/message-routes.js +4 -3
  36. package/dist/esm/core/observer-runner.js +4 -3
  37. package/dist/esm/core/providers.js +38 -21
  38. package/dist/esm/core/route-specificity.js +4 -4
  39. package/dist/esm/core/scalar-types.js +15 -3
  40. package/dist/esm/core/shard-context.js +1 -1
  41. package/dist/esm/core/startup-roots.js +2 -3
  42. package/dist/esm/core/theme-runtime.js +2 -2
  43. package/dist/esm/decorator/app.decorator.js +12 -7
  44. package/dist/esm/decorator/command-builder.decorator.js +4 -4
  45. package/dist/esm/decorator/controller-class.decorator.js +9 -4
  46. package/dist/esm/decorator/controller.decorator.js +110 -67
  47. package/dist/esm/decorator/cooldown.decorator.js +3 -3
  48. package/dist/esm/decorator/defer.decorator.js +4 -4
  49. package/dist/esm/decorator/event.decorator.js +5 -5
  50. package/dist/esm/decorator/filter.decorator.js +7 -7
  51. package/dist/esm/decorator/guard.decorator.js +8 -8
  52. package/dist/esm/decorator/inject.decorator.js +4 -4
  53. package/dist/esm/decorator/interceptor.decorator.js +7 -7
  54. package/dist/esm/decorator/observer.decorator.js +4 -4
  55. package/dist/esm/decorator/service.decorator.js +4 -4
  56. package/dist/esm/decorator/theme.decorator.js +3 -3
  57. package/dist/esm/decorator/validation.decorator.js +10 -10
  58. package/dist/esm/package.json.js +1 -1
  59. package/dist/esm/testing/catalog.js +1 -1
  60. package/dist/esm/testing/cooldown-store-suite.js +18 -10
  61. package/dist/esm/testing/discord-defaults.js +60 -7
  62. package/dist/esm/testing/index.js +4 -2
  63. package/dist/esm/testing/inspect-handler.js +20 -3
  64. package/dist/esm/testing/meocord-testing-module.js +229 -188
  65. package/dist/esm/testing/mock-fn.js +80 -10
  66. package/dist/esm/testing/mock-interaction.js +223 -72
  67. package/dist/esm/testing/response.js +1 -1
  68. package/dist/esm/testing/routing.js +39 -14
  69. package/dist/esm/testing/startup-errors.js +26 -0
  70. package/dist/esm/testing/strict-mocks.js +45 -0
  71. package/dist/esm/testing/theme.js +1 -1
  72. package/dist/esm/util/bundle-entry.util.js +2 -1
  73. package/dist/esm/util/interaction.util.js +1 -1
  74. package/dist/esm/util/meocord-config-validation.util.js +1 -0
  75. package/dist/esm/util/metadata-keys.js +1 -0
  76. package/dist/esm/util/refusal.util.js +148 -8
  77. package/dist/types/common/index.d.cts +8 -8
  78. package/dist/types/common/index.d.ts +8 -8
  79. package/dist/types/core/index.d.cts +8 -6
  80. package/dist/types/core/index.d.ts +8 -6
  81. package/dist/types/decorator/index.d.cts +30 -29
  82. package/dist/types/decorator/index.d.ts +30 -29
  83. package/dist/types/{index-D7n0FBMm.d.ts → index-CdXsFgjE.d.ts} +62 -15
  84. package/dist/types/{index-DEP0NYrU.d.cts → index-nn5p7OoX.d.cts} +62 -15
  85. package/dist/types/interface/index.d.cts +1 -1
  86. package/dist/types/interface/index.d.ts +1 -1
  87. package/dist/types/{route-C-aMI7Sl.d.cts → route-BZgFoNeB.d.cts} +5 -2
  88. package/dist/types/{route-C-aMI7Sl.d.ts → route-BZgFoNeB.d.ts} +5 -2
  89. package/dist/types/testing/index.d.cts +245 -31
  90. package/dist/types/testing/index.d.ts +245 -31
  91. package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-BcOJVzR9.d.ts} +1 -1
  92. package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-UKr3-6Ys.d.cts} +1 -1
  93. package/dist/types/{translator-J6tIUfdp.d.ts → translator-BViWKtLJ.d.ts} +1 -1
  94. package/dist/types/{translator-B4mc1lzJ.d.cts → translator-DHMkkCpg.d.cts} +1 -1
  95. package/package.json +3 -2
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var controller_decorator = require('./controller.decorator-BywwkU9O.cjs');
3
+ var controller_decorator = require('./controller.decorator-CRn9kWob.cjs');
4
4
  require('reflect-metadata');
5
5
  var node_async_hooks = require('node:async_hooks');
6
6
  var inversify = require('inversify');
@@ -101,7 +101,7 @@ var Table__default = /*#__PURE__*/_interopDefaultCompat(Table);
101
101
 
102
102
  function eventDecorator(event, once) {
103
103
  // Generic over the method, so it is checked against the event's arguments rather than required to match exactly
104
- return function(target, propertyKey, _descriptor) {
104
+ return controller_decorator.declaring(function(target, propertyKey, _descriptor) {
105
105
  controller_decorator.refuseOnClass(once ? '@Once' : '@On', target, propertyKey);
106
106
  const handlers = controller_decorator.ownHandlerList(controller_decorator.META.eventHandlers, target);
107
107
  handlers.push({
@@ -110,7 +110,7 @@ function eventDecorator(event, once) {
110
110
  once
111
111
  });
112
112
  Reflect.defineMetadata(controller_decorator.META.eventHandlers, handlers, target);
113
- };
113
+ });
114
114
  }
115
115
  /**
116
116
  * Handles a discord.js client event every time it is emitted, on a controller or a service.
@@ -138,7 +138,7 @@ function eventDecorator(event, once) {
138
138
  * @group Decorators
139
139
  * @category Handlers
140
140
  * @see {@link Once}
141
- * @see {@link https://meocord.dev/docs/4.1/gateway-events | Gateway events}
141
+ * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
142
142
  */ function On(event) {
143
143
  return eventDecorator(event, false);
144
144
  }
@@ -165,7 +165,7 @@ function eventDecorator(event, once) {
165
165
  * @group Decorators
166
166
  * @category Handlers
167
167
  * @see {@link On}
168
- * @see {@link https://meocord.dev/docs/4.1/gateway-events | Gateway events}
168
+ * @see {@link https://meocord.dev/docs/4.2/gateway-events | Gateway events}
169
169
  */ function Once(event) {
170
170
  return eventDecorator(event, true);
171
171
  }
@@ -1376,7 +1376,7 @@ function isInterceptorWithParams(entry) {
1376
1376
  });
1377
1377
  /** Binds `cls` and its unbound dependencies as singletons, refusing any that injects `ExecutionContext`. */ function bindShared(container, cls) {
1378
1378
  if (container.isBound(cls)) return;
1379
- if (injectedTokens(cls).includes(controller_decorator.ExecutionContext)) throw controller_decorator.refuse(singletonContextError(cls));
1379
+ if (injectedTokens(cls).includes(controller_decorator.ExecutionContext)) return controller_decorator.startupError(singletonContextError(cls));
1380
1380
  assertStartupChecked(container, cls);
1381
1381
  makeInjectable(cls);
1382
1382
  container.bind(cls).toSelf().inSingletonScope();
@@ -1571,7 +1571,7 @@ function isInterceptorWithParams(entry) {
1571
1571
  /** Binds a filter as a singleton, after checking it is one. */ function prepareFilter(container, entry) {
1572
1572
  const cls = filterClass(entry);
1573
1573
  if (!Reflect.hasOwnMetadata(controller_decorator.META.catchTypes, cls)) {
1574
- throw controller_decorator.refuse(new Error(`${cls.name || 'A filter'}: used as an exception filter, but not decorated with @Catch().`));
1574
+ return controller_decorator.startupError(new Error(`${cls.name || 'A filter'}: used as an exception filter, but not decorated with @Catch().`));
1575
1575
  }
1576
1576
  bindShared(container, cls);
1577
1577
  }
@@ -1683,11 +1683,12 @@ const asList = (entries)=>Array.isArray(entries) ? entries : [
1683
1683
 
1684
1684
  /** Where a container keeps the observers its calls are reported to, in order. */ const OBSERVERS = Symbol('observers');
1685
1685
  const logger$1 = new controller_decorator.Logger('Observer');
1686
- /** Throws unless every entry is an `@Observer` class, naming where it was listed. */ function assertObservers(where, observers) {
1686
+ /** Reports each entry that is not an `@Observer` class, naming where it was listed. */ function assertObservers(where, observers) {
1687
1687
  for (const observer of observers){
1688
1688
  if (typeof observer !== 'function' || !Reflect.getMetadata(controller_decorator.META.observerClass, observer)) {
1689
1689
  const name = typeof observer === 'function' ? observer.name : String(observer);
1690
- throw controller_decorator.refuse(new Error(`${where} takes classes decorated with @Observer(), not ${name}.`));
1690
+ controller_decorator.startupError(new Error(`${where} takes classes decorated with @Observer(), not ${name}.`));
1691
+ continue;
1691
1692
  }
1692
1693
  }
1693
1694
  }
@@ -1942,12 +1943,16 @@ const NO_GLOBAL_STAGES = {
1942
1943
  */ function bindAppPresenter(container, app, client) {
1943
1944
  const options = Reflect.getMetadata(controller_decorator.META.appOptions, app);
1944
1945
  if (!options?.presenter) return undefined;
1945
- bindShared(container, options.presenter);
1946
+ prepareAppPresenter(container, app);
1946
1947
  const presenter = container.get(options.presenter);
1947
1948
  container.bind(APP_PRESENTER).toConstantValue(presenter);
1948
1949
  if (client) controller_decorator.setPresenter(client, presenter);
1949
1950
  return presenter;
1950
1951
  }
1952
+ /** Binds the app's presenter class as a shared stage, checking it as one, without making it. */ function prepareAppPresenter(container, app) {
1953
+ const options = Reflect.getMetadata(controller_decorator.META.appOptions, app);
1954
+ if (options?.presenter) bindShared(container, options.presenter);
1955
+ }
1951
1956
  /** Where a container keeps the application's presenter. */ const APP_PRESENTER = Symbol('app_presenter');
1952
1957
  /** The presenter `bindAppPresenter` resolved into `container`, if any. */ function appPresenterOf(container) {
1953
1958
  return container.isBound(APP_PRESENTER) ? container.get(APP_PRESENTER) : undefined;
@@ -2037,18 +2042,21 @@ const stagesByGlobals = new WeakMap();
2037
2042
  /**
2038
2043
  * Binds the interceptors, filters and pipes every handler of `controllers` uses, and the global ones, as singletons, so
2039
2044
  * one that cannot be shared, or a filter without `@Catch`, fails at startup; refuses there `@Defer`, `@Validate`,
2040
- * `@UsePipe` and `@Cooldown` where they cannot apply, and two classes that keep state under one name.
2045
+ * `@UsePipe` and `@Cooldown` where they cannot apply, and two classes that keep state under one name. Binds only; it
2046
+ * makes nothing, so it runs with the other startup checks.
2041
2047
  */ function prepareHandlerStages(container, controllers) {
2042
2048
  assertDistinctNamesWhereKeyed(controllers);
2043
2049
  const globals = globalStagesOf(container);
2044
- configureThemes(container, globals.theme, controllers, globals.themeFor);
2045
2050
  for (const entry of globals.interceptors)prepareInterceptor(container, entry);
2046
2051
  for (const entry of globals.filters)prepareFilter(container, entry);
2047
2052
  for (const controller of controllers){
2048
2053
  const prototype = controller.prototype;
2049
2054
  for (const method of handlerMethods(prototype)){
2050
2055
  const kind = handlerDefer(prototype, method) ? nonInteractionHandler(prototype, method) : undefined;
2051
- if (kind) throw controller_decorator.refuse(deferMisuseError(controller.name, method, kind));
2056
+ if (kind) {
2057
+ controller_decorator.startupError(deferMisuseError(controller.name, method, kind));
2058
+ continue;
2059
+ }
2052
2060
  for (const entry of handlerInterceptors(prototype, method))prepareInterceptor(container, entry);
2053
2061
  for (const entry of handlerFilterLevels(prototype, method, []).flat())prepareFilter(container, entry);
2054
2062
  for (const { entry } of handlerInputStages(prototype, method).pipes)preparePipe(container, entry);
@@ -2056,6 +2064,10 @@ const stagesByGlobals = new WeakMap();
2056
2064
  assertInputStagesOnInteractions(controller, prototype);
2057
2065
  }
2058
2066
  }
2067
+ /** Records the app's theme for the handlers of `controllers`, once the startup checks have passed: it makes the theme cache. */ function prepareThemes(container, controllers) {
2068
+ const globals = globalStagesOf(container);
2069
+ configureThemes(container, globals.theme, controllers, globals.themeFor);
2070
+ }
2059
2071
  /** Whether a class has state keyed by its name: a cooldown on a handler, or a `@Once` handler. */ function keyedByName(cls) {
2060
2072
  const prototype = cls.prototype;
2061
2073
  const handlers = [
@@ -2072,7 +2084,8 @@ const stagesByGlobals = new WeakMap();
2072
2084
  for (const cls of classes){
2073
2085
  const other = byName.get(cls.name);
2074
2086
  if (other && other !== cls && (keyedByName(cls) || keyedByName(other))) {
2075
- throw controller_decorator.refuse(new Error(`${cls.name}: two classes have this name; @Cooldown and @Once tell classes apart by name, so they would share ` + `their counts. Rename one of them.`));
2087
+ controller_decorator.startupError(new Error(`${cls.name}: two classes have this name; @Cooldown and @Once tell classes apart by name, so they would share ` + `their counts. Rename one of them.`));
2088
+ continue;
2076
2089
  }
2077
2090
  byName.set(cls.name, cls);
2078
2091
  }
@@ -2100,11 +2113,12 @@ const stagesByGlobals = new WeakMap();
2100
2113
  const { schema, pipes } = handlerInputStages(prototype, method);
2101
2114
  const handler = kind === 'message' ? 'a message handler without a pattern' : `${kind === 'autocomplete' || kind === 'event' ? 'an' : 'a'} ${kind} handler`;
2102
2115
  if (schema || pipes.length > 0) {
2103
- throw controller_decorator.refuse(new Error(`${controller.name}.${method}: @Validate and @UsePipe are for interaction and patterned message handlers, ` + `whose options, customId params, modal fields and pattern params they check, and this is ${handler}.`));
2116
+ controller_decorator.startupError(new Error(`${controller.name}.${method}: @Validate and @UsePipe are for interaction and patterned message handlers, ` + `whose options, customId params, modal fields and pattern params they check, and this is ${handler}.`));
2117
+ continue;
2104
2118
  }
2105
2119
  // A controller's own @Cooldown skips these handlers; one on the method itself is a mistake.
2106
2120
  if (kind !== 'message' && methodCooldowns(prototype, method).length > 0) {
2107
- throw controller_decorator.refuse(new Error(`${controller.name}.${method}: @Cooldown is for interaction and message handlers, and this is ${handler}.`));
2121
+ controller_decorator.startupError(new Error(`${controller.name}.${method}: @Cooldown is for interaction and message handlers, and this is ${handler}.`));
2108
2122
  }
2109
2123
  }
2110
2124
  }
@@ -3261,7 +3275,8 @@ const FLAG = /^\{--(\w+)(?::([\w-]+(?:\|[\w-]+)*))?(\?)?\}$/;
3261
3275
  if (own.hidden !== undefined && typeof own.hidden !== 'boolean') throw new Error(`hidden is true or false, not ${JSON.stringify(own.hidden)}.`);
3262
3276
  aliases = aliasPatterns(pattern, parsed.tokens, own.aliases);
3263
3277
  } catch (error) {
3264
- throw controller_decorator.refuse(new Error(`${controllerClass.name}.${method}: @MessageHandler('${pattern}'): ${error.message}`));
3278
+ controller_decorator.startupError(new Error(`${controllerClass.name}.${method}: @MessageHandler('${pattern}'): ${error.message}`));
3279
+ continue;
3265
3280
  }
3266
3281
  const shared = {
3267
3282
  controllerClass,
@@ -3300,7 +3315,7 @@ const FLAG = /^\{--(\w+)(?::([\w-]+(?:\|[\w-]+)*))?(\?)?\}$/;
3300
3315
  routes.splice(j--, 1);
3301
3316
  continue;
3302
3317
  }
3303
- throw controller_decorator.refuse(new Error(`${a.controllerClass.name}.${a.method}: ${describeRoute(a)} and ${describeRoute(b)} in ${b.controllerClass.name}.${b.method} ` + `match the same messages, so only one of them could ever answer those. Change one pattern, or give one its own prefix.`));
3318
+ controller_decorator.startupError(new Error(`${a.controllerClass.name}.${a.method}: ${describeRoute(a)} and ${describeRoute(b)} in ${b.controllerClass.name}.${b.method} ` + `match the same messages, so only one of them could ever answer those. Change one pattern, or give one its own prefix.`));
3304
3319
  }
3305
3320
  }
3306
3321
  return routes;
@@ -4179,7 +4194,7 @@ const COMMAND_TYPES = new Set([
4179
4194
  *
4180
4195
  * @group Controllers
4181
4196
  * @see {@link HandlerEntry}
4182
- * @see {@link https://meocord.dev/docs/4.1/handler-discovery | Handler discovery}
4197
+ * @see {@link https://meocord.dev/docs/4.2/handler-discovery | Handler discovery}
4183
4198
  */ class HandlerRegistry {
4184
4199
  /**
4185
4200
  * Lists the registered handlers.
@@ -4424,7 +4439,7 @@ async function withTimeout(promise, ms, what) {
4424
4439
  *
4425
4440
  * @group Controllers
4426
4441
  * @see {@link ShardCallResult}
4427
- * @see {@link https://meocord.dev/docs/4.1/sharding | Sharding}
4442
+ * @see {@link https://meocord.dev/docs/4.2/sharding | Sharding}
4428
4443
  */ class ShardContext {
4429
4444
  /** The ids of the shards this process runs. With process sharding, one id; otherwise every shard, once ready. */ get ids() {
4430
4445
  if (this.client?.shard) return [
@@ -4601,9 +4616,9 @@ const isFactoryProvider = (provider)=>'useFactory' in provider;
4601
4616
  }
4602
4617
  const isToken = (value)=>typeof value === 'function' || typeof value === 'string' || typeof value === 'symbol';
4603
4618
  /**
4604
- * Checks each provider's shape and indexes them by token, throwing on the first that cannot be bound, such as one with
4605
- * no token, not exactly one of `useValue`, `useClass` and `useFactory`, one that injects `ExecutionContext`, or a
4606
- * token provided twice.
4619
+ * Checks each provider's shape and indexes them by token, reporting each that cannot be bound, such as one with no
4620
+ * token, not exactly one of `useValue`, `useClass` and `useFactory`, one that injects `ExecutionContext`, or a token
4621
+ * provided twice, as a startup error; see `startupError`.
4607
4622
  */ function providerMap(providers, where) {
4608
4623
  const map = new Map();
4609
4624
  for (const provider of providers){
@@ -4612,10 +4627,12 @@ const isToken = (value)=>typeof value === 'function' || typeof value === 'string
4612
4627
  if (typeof entry === 'function') {
4613
4628
  const name = tokenName(entry);
4614
4629
  const listing = where.includes('@MeoCord') ? 'list a service in @MeoCord({ services })' : 'a class a controller or service injects is bound for you, so it needs no listing';
4615
- throw controller_decorator.refuse(new Error(`${where}: ${name} is a class, not a provider: ${listing}. To put something in its place, write ` + `{ provide: ${name}, useValue } or { provide: ${name}, useClass }.`));
4630
+ controller_decorator.startupError(new Error(`${where}: ${name} is a class, not a provider: ${listing}. To put something in its place, write ` + `{ provide: ${name}, useValue } or { provide: ${name}, useClass }.`));
4631
+ continue;
4616
4632
  }
4617
4633
  if (!entry || typeof entry !== 'object' || !isToken(entry.provide)) {
4618
- throw controller_decorator.refuse(new Error(`${where}: a provider has no token: set provide to a class, a string or a symbol.`));
4634
+ controller_decorator.startupError(new Error(`${where}: a provider has no token: set provide to a class, a string or a symbol.`));
4635
+ continue;
4619
4636
  }
4620
4637
  const name = tokenName(entry.provide);
4621
4638
  const kinds = [
@@ -4624,26 +4641,35 @@ const isToken = (value)=>typeof value === 'function' || typeof value === 'string
4624
4641
  'useFactory'
4625
4642
  ].filter((kind)=>kind in entry);
4626
4643
  if (kinds.length !== 1) {
4627
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} needs exactly one of useValue, useClass and useFactory.`));
4644
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} needs exactly one of useValue, useClass and useFactory.`));
4645
+ continue;
4628
4646
  }
4629
4647
  if ('useClass' in entry && typeof entry.useClass !== 'function') {
4630
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} has a useClass that is not a class.`));
4648
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} has a useClass that is not a class.`));
4649
+ continue;
4631
4650
  }
4632
4651
  if ('useClass' in entry && injectedTokens(entry.useClass).includes(controller_decorator.ExecutionContext)) {
4633
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} uses ${tokenName(entry.useClass)}, which injects ExecutionContext, but it is ` + "made once and shared, so it would keep the first call's context for every later call. Inject " + 'ExecutionContext only into guards.'));
4652
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} uses ${tokenName(entry.useClass)}, which injects ExecutionContext, but it is ` + "made once and shared, so it would keep the first call's context for every later call. Inject " + 'ExecutionContext only into guards.'));
4653
+ continue;
4634
4654
  }
4635
4655
  if ('useFactory' in entry) {
4636
4656
  if (typeof entry.useFactory !== 'function') {
4637
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} has a useFactory that is not a function.`));
4657
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} has a useFactory that is not a function.`));
4658
+ continue;
4638
4659
  }
4639
4660
  if (entry.inject !== undefined && !(Array.isArray(entry.inject) && entry.inject.every(isToken))) {
4640
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} has an inject that is not a list of tokens.`));
4661
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} has an inject that is not a list of tokens.`));
4662
+ continue;
4641
4663
  }
4642
4664
  if (entry.inject?.includes(controller_decorator.ExecutionContext)) {
4643
- throw controller_decorator.refuse(new Error(`${where}: the provider for ${name} injects ExecutionContext, but its factory runs once and its value is ` + "shared, so it would keep the first call's context for every later call. Inject ExecutionContext only into guards."));
4665
+ controller_decorator.startupError(new Error(`${where}: the provider for ${name} injects ExecutionContext, but its factory runs once and its value is ` + "shared, so it would keep the first call's context for every later call. Inject ExecutionContext only into guards."));
4666
+ continue;
4644
4667
  }
4645
4668
  }
4646
- if (map.has(entry.provide)) throw controller_decorator.refuse(new Error(`${where}: ${name} is provided twice.`));
4669
+ if (map.has(entry.provide)) {
4670
+ controller_decorator.startupError(new Error(`${where}: ${name} is provided twice.`));
4671
+ continue;
4672
+ }
4647
4673
  map.set(entry.provide, provider);
4648
4674
  }
4649
4675
  return map;
@@ -4696,22 +4722,23 @@ const isToken = (value)=>typeof value === 'function' || typeof value === 'string
4696
4722
  for (const dependency of inject)if (isAppClassToken(dependency)) bindClass(dependency);
4697
4723
  }
4698
4724
  /**
4699
- * Throws when a class or a factory asks for a string or symbol token that nothing binds, naming both,
4700
- * rather than failing when the class is first resolved. A class token is bound on demand, so it is
4701
- * never missing.
4725
+ * Reports a class or a factory that asks for a string or symbol token nothing binds, naming both, rather than failing
4726
+ * when the class is first resolved. A class token is bound on demand, so it is never missing.
4702
4727
  */ function assertProvided(container, providers, classes, where) {
4703
4728
  const missing = (token)=>(typeof token === 'string' || typeof token === 'symbol') && !container.isBound(token);
4704
4729
  for (const cls of classes){
4705
4730
  const token = injectedTokens(cls).find(missing);
4706
4731
  if (token !== undefined) {
4707
- throw controller_decorator.refuse(new Error(`${cls.name}: it injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`));
4732
+ controller_decorator.startupError(new Error(`${cls.name}: it injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`));
4733
+ continue;
4708
4734
  }
4709
4735
  }
4710
4736
  for (const [provided, provider] of providers){
4711
4737
  const dependencies = isFactoryProvider(provider) ? provider.inject ?? [] : isClassProvider(provider) ? injectedTokens(provider.useClass) : [];
4712
4738
  const token = dependencies.find(missing);
4713
4739
  if (token !== undefined) {
4714
- throw controller_decorator.refuse(new Error(`${tokenName(provided)}: its provider injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`));
4740
+ controller_decorator.startupError(new Error(`${tokenName(provided)}: its provider injects ${tokenName(token)}, which nothing provides: add a provider for it to ${where}.`));
4741
+ continue;
4715
4742
  }
4716
4743
  }
4717
4744
  }
@@ -4744,16 +4771,21 @@ const isToken = (value)=>typeof value === 'function' || typeof value === 'string
4744
4771
  */ function assertTypedParameters(classes, decorators) {
4745
4772
  for (const cls of classes){
4746
4773
  const instead = meocordClassAdvice(cls);
4747
- if (instead && undecoratedConstructor(cls)) throw controller_decorator.refuse(new Error(`${cls.name}: MeoCord does not inject it; ${instead}.`));
4774
+ if (instead && undecoratedConstructor(cls)) {
4775
+ controller_decorator.startupError(new Error(`${cls.name}: MeoCord does not inject it; ${instead}.`));
4776
+ continue;
4777
+ }
4748
4778
  if (undecoratedConstructor(cls)) {
4749
4779
  const decorator = decorators.get(cls);
4750
- throw controller_decorator.refuse(new Error(`${cls.name}: its constructor takes parameters, but ${cls.name} has no decorator, so TypeScript recorded none of ` + `their types and it cannot be created. ${decorator ? `Decorate it with ${decorator}.` : 'Decorate it with @Service(), or give a class from a package a provider in @MeoCord({ providers }).'}`));
4780
+ controller_decorator.startupError(new Error(`${cls.name}: its constructor takes parameters, but ${cls.name} has no decorator, so TypeScript recorded none of ` + `their types and it cannot be created. ${decorator ? `Decorate it with ${decorator}.` : 'Decorate it with @Service(), or give a class from a package a provider in @MeoCord({ providers }).'}`));
4781
+ continue;
4751
4782
  }
4752
4783
  const index = untypedParameter(cls);
4753
4784
  if (index === -1) continue;
4754
4785
  const injectors = classes.filter((other)=>other !== cls && injectedTokens(other).includes(cls)).map((other)=>other.name);
4755
4786
  const injectedBy = injectors.length === 0 ? '' : ` (${injectors.length === 1 ? injectors[0] : `${injectors.slice(0, -1).join(', ')} and ${injectors.at(-1)}`} ` + `inject${injectors.length === 1 ? 's' : ''} ${cls.name})`;
4756
- throw controller_decorator.refuse(new Error(`${cls.name}: parameter ${index + 1} of its constructor has no runtime type, so it cannot be created. Usually ` + `${cls.name} and a class it injects import each other${injectedBy}, or the parameter is typed with an ` + 'interface or an `import type`. Move what they both need into a third service, or inject the parameter ' + 'with @Inject(token).'));
4787
+ controller_decorator.startupError(new Error(`${cls.name}: parameter ${index + 1} of its constructor has no runtime type, so it cannot be created. Usually ` + `${cls.name} and a class it injects import each other${injectedBy}, or the parameter is typed with an ` + 'interface or an `import type`. Move what they both need into a third service, or inject the parameter ' + 'with @Inject(token).'));
4788
+ continue;
4757
4789
  }
4758
4790
  }
4759
4791
  /** For each container, the tokens MeoCord binds itself, each to the app's class it stands for, if any. */ const ownTokens = new WeakMap();
@@ -4904,7 +4936,9 @@ exports.messageStartsFor = messageStartsFor;
4904
4936
  exports.needsGuild = needsGuild;
4905
4937
  exports.nonInteractionHandler = nonInteractionHandler;
4906
4938
  exports.observeUnclaimed = observeUnclaimed;
4939
+ exports.prepareAppPresenter = prepareAppPresenter;
4907
4940
  exports.prepareHandlerStages = prepareHandlerStages;
4941
+ exports.prepareThemes = prepareThemes;
4908
4942
  exports.providerMap = providerMap;
4909
4943
  exports.reachableClasses = reachableClasses;
4910
4944
  exports.registerClientTheme = registerClientTheme;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var controller_decorator = require('./controller.decorator-BywwkU9O.cjs');
4
+ var controller_decorator = require('./controller.decorator-CRn9kWob.cjs');
5
5
 
6
6
  /** The `fatal` code of a shard whose app MeoCord refused, which every shard would refuse alike. */ const REFUSED_CODE = 'Refused';
7
7
  /** Whether an IPC message is one of MeoCord's own, rather than one of discord.js's. */ function isShardMessage(message) {