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
@@ -2,15 +2,22 @@ import 'reflect-metadata';
2
2
  import { HandlerExecutionContext } from '../common/execution-context.js';
3
3
  import { appStages, handlerStages } from '../core/handler-pipeline.js';
4
4
  import { handlerCooldowns } from '../core/cooldown-runner.js';
5
- import { getMessageHandlers, getHandlerRoutes, getDeclaredRoutes } from '../decorator/controller.decorator.js';
5
+ import { getMessageHandlers, getHandlerRoutes, eventRoute, getDeclaredRoutes, ownEventHandlers, getCommandMap, getAutocompleteHandlers, getReactionHandlers } from '../decorator/controller.decorator.js';
6
+ import { getEventHandlers } from '../decorator/event.decorator.js';
7
+ import { appliesTo } from '../core/stage-scope.js';
8
+ import { Logger } from '../common/logger.js';
9
+ import { warnOnce } from '../common/deprecation.js';
10
+ import { strictMocks } from './strict-mocks.js';
6
11
  import { appObservers } from '../core/observer-runner.js';
7
12
 
8
13
  /**
9
14
  * Reports what runs when a handler is dispatched, and the metadata declared on it, without running anything.
10
15
  *
11
16
  * Use it to check that decorators set a handler up as intended: its guards with their params, its interceptors,
12
- * filters and cooldowns, in the order dispatch applies them. To check what a handler does, run it with
13
- * {@link TestingModule.invoke}.
17
+ * filters and cooldowns, in the order dispatch applies them. Under `useStrictMocks()`, the guards, interceptors and
18
+ * observers are those dispatch runs for the handler's context types: a stage declared with `types` is listed only for
19
+ * a handler of one of them, and an autocomplete lists no interceptors. Default mode lists every one, with a warning
20
+ * once where dispatch runs fewer. To check what a handler does, run it with {@link TestingModule.invoke}.
14
21
  *
15
22
  * @param controller - The controller class declaring the handler.
16
23
  * @param methodName - The handler method's name.
@@ -46,14 +53,20 @@ import { appObservers } from '../core/observer-runner.js';
46
53
  });
47
54
  const globals = options.app ? appStages(options.app) : undefined;
48
55
  const { guards, interceptors, filters } = handlerStages(controller.prototype, methodName, globals);
56
+ const observers = options.app ? appObservers(options.app) : [];
57
+ const stages = runStages(controller.prototype, methodName, {
58
+ guards,
59
+ interceptors,
60
+ observers
61
+ });
49
62
  const inspection = {
50
63
  controller,
51
64
  methodName,
52
65
  guards: Object.freeze([
53
- ...guards
66
+ ...stages.guards
54
67
  ]),
55
68
  interceptors: Object.freeze([
56
- ...interceptors
69
+ ...stages.interceptors
57
70
  ]),
58
71
  filters: Object.freeze(filters.flat()),
59
72
  cooldowns: Object.freeze(handlerCooldowns(controller.prototype, methodName).map(({ seconds, uses, per, bypass, by })=>Object.freeze({
@@ -64,24 +77,92 @@ import { appObservers } from '../core/observer-runner.js';
64
77
  by: by !== undefined
65
78
  }))),
66
79
  pattern: getMessageHandlers(controller.prototype).find((handler)=>handler.method === methodName)?.pattern,
67
- observers: Object.freeze(options.app ? appObservers(options.app) : []),
80
+ observers: Object.freeze([
81
+ ...stages.observers
82
+ ]),
68
83
  get: (metadata)=>context.get(metadata),
69
84
  getAll: (metadata)=>context.getAll(metadata)
70
85
  };
71
86
  // Read by a test but not compared by toEqual, so an assertion on the result's other fields holds
87
+ const routes = Object.freeze(inheritedRoutesOf(controller.prototype, methodName, false));
88
+ const withEvents = Object.freeze(inheritedRoutesOf(controller.prototype, methodName, true));
72
89
  Object.defineProperty(inspection, 'inheritedRoutes', {
73
- value: Object.freeze(inheritedRoutesOf(controller.prototype, methodName)),
90
+ get: ()=>{
91
+ if (strictMocks()) return withEvents;
92
+ if (withEvents.length > routes.length) {
93
+ warnOnce(new Logger('inspectHandler'), `inspectHandler() here leaves the @On and @Once events ${controller.name}.${methodName} inherits out of ` + 'inheritedRoutes, though the bot answers them; under useStrictMocks() it lists them.');
94
+ }
95
+ return routes;
96
+ },
74
97
  enumerable: false
75
98
  });
76
99
  return inspection;
77
100
  }
78
- /** The routes `methodName` answers that the class declaring it last doesn't declare for it: those it inherits. */ function inheritedRoutesOf(prototype, methodName) {
101
+ /** The context types dispatch calls `methodName` with: one for each kind of route it answers. */ function handlerContextTypes(prototype, methodName) {
102
+ const answers = [
103
+ [
104
+ 'interaction',
105
+ Object.values(getCommandMap(prototype) ?? {}).some((metas)=>metas.some((meta)=>meta.methodName === methodName))
106
+ ],
107
+ [
108
+ 'autocomplete',
109
+ getAutocompleteHandlers(prototype).some((handler)=>handler.methodName === methodName)
110
+ ],
111
+ [
112
+ 'message',
113
+ getMessageHandlers(prototype).some((handler)=>handler.method === methodName)
114
+ ],
115
+ [
116
+ 'reaction',
117
+ getReactionHandlers(prototype).some((handler)=>handler.method === methodName)
118
+ ],
119
+ [
120
+ 'event',
121
+ getEventHandlers(prototype).some((handler)=>handler.method === methodName)
122
+ ]
123
+ ];
124
+ return answers.filter(([, answered])=>answered).map(([type])=>type);
125
+ }
126
+ /**
127
+ * The guards, interceptors and observers dispatch runs for the handler, by the same `appliesTo` it uses: those for one
128
+ * of its context types, and no interceptors for an autocomplete. Default mode lists every one, and warns once where
129
+ * that lists more; a method no route reaches lists every one in both modes.
130
+ */ function runStages(prototype, methodName, all) {
131
+ const types = handlerContextTypes(prototype, methodName);
132
+ if (types.length === 0) return all;
133
+ const forTypes = (entries, among)=>entries.filter((entry)=>among.some((type)=>appliesTo(entry, type)));
134
+ const run = {
135
+ ...all,
136
+ guards: forTypes(all.guards, types),
137
+ // Autocomplete answers within three seconds and has no reply to shape, so it runs no interceptors
138
+ interceptors: forTypes(all.interceptors, types.filter((type)=>type !== 'autocomplete')),
139
+ observers: forTypes(all.observers, types)
140
+ };
141
+ if (strictMocks()) return run;
142
+ const handlerName = `${prototype.constructor.name}.${methodName}`;
143
+ const count = (stages)=>stages.guards.length + stages.interceptors.length + stages.observers.length;
144
+ if (count(run) < count(all)) {
145
+ warnOnce(new Logger('inspectHandler'), `inspectHandler() here lists the guards, interceptors and observers of every context type for ${handlerName}, ` + "where dispatch runs only those of its own; under useStrictMocks() it lists the ones dispatch runs.");
146
+ }
147
+ return all;
148
+ }
149
+ /**
150
+ * The routes `methodName` answers that the class declaring it last doesn't declare for it: those it inherits, its
151
+ * `@On` and `@Once` events among them when `events` is set.
152
+ */ function inheritedRoutesOf(prototype, methodName, events) {
153
+ const labels = (routes)=>routes.filter((route)=>route.method === methodName).map((route)=>route.label);
79
154
  const answered = [
80
- ...new Set(getHandlerRoutes(prototype).filter((route)=>route.method === methodName).map((route)=>route.label))
155
+ ...new Set(labels([
156
+ ...getHandlerRoutes(prototype),
157
+ ...events ? getEventHandlers(prototype).map(eventRoute) : []
158
+ ]))
81
159
  ];
82
160
  // The nearest class with its own decorators on the method; a class that only overrides it declares none
83
161
  for(let at = prototype; at && at !== Object.prototype; at = Object.getPrototypeOf(at)){
84
- const own = getDeclaredRoutes(at).filter((route)=>route.method === methodName).map((route)=>route.label);
162
+ const own = labels([
163
+ ...getDeclaredRoutes(at),
164
+ ...events ? ownEventHandlers(at).map(eventRoute) : []
165
+ ]);
85
166
  if (own.length > 0) return at === prototype ? answered.filter((label)=>!own.includes(label)) : answered;
86
167
  }
87
168
  return [];